diff --git a/Makefile b/Makefile index 750ecc1..55f67f6 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ all: build test build: distclean build.deps build.game -# build.game: build.game.linux64 build.game.win64 build.game.mac -build.game: build.game.linux64 build.game.win64 +build.game: build.game.linux64 build.game.win64 build.game.mac +# build.game: build.game.linux64 build.game.win64 build.deps: GIT_SSL_NO_VERIFY=true $(GO) mod vendor @@ -45,11 +45,14 @@ build.game.win64: /usr/bin/x86_64-w64-mingw32-strip $(DISTFOLDER)/game.exe && \ cd $(DISTFOLDER) && zip -r ../$(PROJECT_NAME)-$(OS)-${PKG_VER}.zip . -x *.git* + + +# OSXCROSS_NO_INCLUDE_PATH_WARNINGS=1 MACOSX_DEPLOYMENT_TARGET=10.6 CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 $(GO) build -ldflags $(LDFLAGS) -o $(DISTFOLDER)/game $(CWD)/cmd/game/main.go && build.game.mac: cp $(CWD)/lib/mac/libBearLibTerminal.dylib $(DISTFOLDER) && \ cp $(CWD)/config.json $(DISTFOLDER) && \ cp -r $(CWD)/assets $(DISTFOLDER) && \ - OSXCROSS_NO_INCLUDE_PATH_WARNINGS=1 MACOSX_DEPLOYMENT_TARGET=10.6 CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 $(GO) build -ldflags $(LDFLAGS) -o $(DISTFOLDER)/game $(CWD)/cmd/game/main.go && \ + OSXCROSS_NO_INCLUDE_PATH_WARNINGS=1 MACOSX_DEPLOYMENT_TARGET=10.6 CC=clang CXX=clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 $(GO) build -ldflags $(LDFLAGS) -o $(DISTFOLDER)/game $(CWD)/cmd/game/main.go && \ strip $(DISTFOLDER)/game.exe && \ cd $(DISTFOLDER) && zip -r ../$(PROJECT_NAME)-$(OS)-${PKG_VER}.zip . -x *.git* diff --git a/assets/logo2.txt b/assets/logo2.txt new file mode 100644 index 0000000..d0b39c4 --- /dev/null +++ b/assets/logo2.txt @@ -0,0 +1,17 @@ + + ^^:^|+ !. ,| .':: + 'Uz 3F` |/ `cz RR + U| ye=-/l zy-^^ + gc ff ^v ik` : + l| :F c -!!: + + + .- ,` `, '' : '' -. `' `` `-'!! + '.f^ sw! !u|,,\%N; ipf tdD -|l!":aXr ',|| lf u%' tOc ru;.,C0q ,r` iXD! !t + `uqE' !w: -!w ul ,Uw !e# rye- ?ez ';3= !B? /d> :az !v0| ,a 'kw + `vv=ee i%! !w' ,o9i?zFsfU# !39z|?|// ^OWws` taq? -dgc `/f `t#%+ 'oe + `r3 ueo s%! 'qQ` ,pe >kW lgw |OXX|-ue/9> zgO! =e' ^DQw. 'Fe + :Cr!::|Ec cq! UQs 'Da >%W !QB` vR- cC' ^e> 3EW! -eQ%: 'qU + :a` `ON; /q| `#@e .yd ,ge |BQ sBp, ,y |D +k| :kd EB= !q: 'pq + ^w" ,qd :dE+!!!!!sw eQqs|zU/ #Q^ ,Q@z ,qU/::i| Fg zN/ !Qg E#pz>zw' R@C + `''''''` ` , , diff --git a/cmd/game/main.go b/cmd/game/main.go index 83bfd43..7356612 100644 --- a/cmd/game/main.go +++ b/cmd/game/main.go @@ -5,7 +5,7 @@ import ( "github.com/rs/zerolog/log" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" - "lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens" + "lab.zaar.be/thefish/alchemyst-go/engine/gamemap/mapgens/delaunaymst" "lab.zaar.be/thefish/alchemyst-go/engine/gamestate" "lab.zaar.be/thefish/alchemyst-go/engine/mob" "lab.zaar.be/thefish/alchemyst-go/engine/mob/movement" @@ -70,7 +70,8 @@ func main() { go decodeInput(mainCtx, mw.GetLayer("base")) //fixme set up (load / generate) level - move to game / enter or title / exit - level, rooms := mapgens.DefaultGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) + //level, rooms := _default.DefaultGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) + level, rooms := delaunaymst.DelaunayMstGen(mainCtx, gamemap.NewLevel(mainCtx, "test", 1)) State.Level = level sidebarWidth := 0 @@ -136,16 +137,12 @@ func main() { SetBgColor("#ef305c70"). SetFgColor("white"). SetItems([]interface{}{ - "hjklyubn, NumPad 12346789, arrow keys - move", - "s or . - pass turn", - "g or , - pick up item", - "i - inventory", - "? - this screen", - "Ctrl+q - exit", - "f or F - fire or throw weapon", - "z or Z - cast a spell", - "p - pray", - "Ctrl+p - message log", + `"Fisheye" crafty shaded glasses`, + "Xecutor's glowing visor", + "Kitschy goggles of many pathways", + "Ring of inexistence", + "Orb of omniscience", + "Wand of amnesia", }).MakeList(), ) diff --git a/config.json b/config.json index 5ec89b2..597bb95 100644 --- a/config.json +++ b/config.json @@ -1,10 +1,10 @@ { - "version": "v0.0.1.5-1-gbecf7ea", + "version": "v0.0.1.6", "title": "Alchemyst", "sizeX": 100, "sizeY": 47, "fpsLimit": 60, - "font": "./assets/fonts/bitmap/ibmnew8x12.png", - "fontSize": "8x12", + "font": "./assets/fonts/ttf/LiberationMono-Bold.ttf", + "fontSize": "8x14", "verbosity": "debug" } \ No newline at end of file diff --git a/engine/ecs/component.go b/engine/ecs/component.go index ca6f6bb..77c5ab3 100644 --- a/engine/ecs/component.go +++ b/engine/ecs/component.go @@ -8,6 +8,11 @@ const MobComponent = "mob" const MoveableComponent = "movable" const CarriedComponent = "carried" const UsableComponent = "usable" +const WearableComponent = "usable" +const ArmsComponent = "arms" +const RangedComponent = "ranged" +const AmmoComponent = "ammo" +const ArmorComponent = "armor" type Component interface { Type() string diff --git a/engine/fov/precomputed_shade/precomputed_shade.go b/engine/fov/precomputed_shade/precomputed_shade.go index f869df8..55ef27c 100644 --- a/engine/fov/precomputed_shade/precomputed_shade.go +++ b/engine/fov/precomputed_shade/precomputed_shade.go @@ -167,7 +167,7 @@ func (ps *precomputedShade) PrecomputeFovMap() { //Bresanham lines / Raycast var lineX, lineY float64 for i := 0; i < 720; i++ { // 1/2 of angles - dx := math.Sin(float64(i) / (float64(360) / math.Pi)) //1/2 of angles + dx := math.Sin(float64(i) / (float64(360) / math.Pi)) //1/2 of angle dy := math.Cos(float64(i) / (float64(360) / math.Pi)) lineX = 0 diff --git a/engine/gamemap/mapgens/default.go b/engine/gamemap/mapgens/default/default.go similarity index 99% rename from engine/gamemap/mapgens/default.go rename to engine/gamemap/mapgens/default/default.go index 09c49f1..dae2ad6 100644 --- a/engine/gamemap/mapgens/default.go +++ b/engine/gamemap/mapgens/default/default.go @@ -1,4 +1,4 @@ -package mapgens +package _default import ( "fmt" diff --git a/engine/gamemap/mapgens/delaunaymst/delaunay_mst.go b/engine/gamemap/mapgens/delaunaymst/delaunay_mst.go new file mode 100644 index 0000000..721ad8e --- /dev/null +++ b/engine/gamemap/mapgens/delaunaymst/delaunay_mst.go @@ -0,0 +1,196 @@ +package delaunaymst + +import ( + "fmt" + "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" + "lab.zaar.be/thefish/alchemyst-go/engine/types" + "lab.zaar.be/thefish/alchemyst-go/util" + "lab.zaar.be/thefish/alchemyst-go/util/appctx" + "lab.zaar.be/thefish/alchemyst-go/util/delaunay" +) + +//fixme move to config +var minRoomSize = 5 +var maxRoomSize = 15 +var maxrooms = 59 + +var fges = map[int]types.RectFill{ + 1: types.RectFill{ + Top: func() *gamemap.Tile { return gamemap.NewWall() }, + Bottom: func() *gamemap.Tile { return gamemap.NewWall() }, + Left: func() *gamemap.Tile { return gamemap.NewWall() }, + Right: func() *gamemap.Tile { return gamemap.NewWall() }, + BottomLeft: func() *gamemap.Tile { return gamemap.NewWall() }, + BottomRight: func() *gamemap.Tile { return gamemap.NewWall() }, + TopLeft: func() *gamemap.Tile { return gamemap.NewWall() }, + TopRight: func() *gamemap.Tile { return gamemap.NewWall() }, + Body: func() *gamemap.Tile { return gamemap.NewFloor() }, + }, + + 2: types.RectFill{ + Top: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + Bottom: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + Left: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + Right: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + BottomLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + BottomRight: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + TopLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + TopRight: func() *gamemap.Tile { return gamemap.NewWaterTile() }, + Body: func() *gamemap.Tile { return gamemap.NewDeepWaterTile() }, + }, +} + +func DelaunayMstGen(ctx appctx.ClientCtx, l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { + rng := util.NewRNG() + //load prefabs + pfLoader := gamemap.NewPrefabLoader(ctx) + pfRooms := pfLoader.PrefabRoomsList() + + //fill with walls + for i := 0; i < l.W; i ++ { + for j := 0; j < l.H; j++ { + l.SetTileByXY(i, j, gamemap.NewWall()) + } + } + rooms := make([]gamemap.Room, 0) + prefabUsed := false + for i := 0; i < maxrooms; i++ { + failed := false + var fillage types.RectFill + fillage = fges[rng.GetWeightedEntity(map[int]int{1: 10, 2: 1})] + var newRoom gamemap.Room + if !prefabUsed || rng.Range(0, 5) > 3 { + //prefab + prefabUsed = true + r := pfRooms[rng.Range(0, len(pfRooms))] //copy to local scope + newRoom = r + } else { + newRoom = gamemap.NewRandomRectRoom( + rng, + rng.Range(minRoomSize, maxRoomSize), + rng.Range(minRoomSize, maxRoomSize), + fillage, + ) + } + where := types.Coords{ + rng.Range(1, l.W-2-newRoom.W), + rng.Range(1, l.H-2-newRoom.H), + } + + newRoom.MoveToCoords(where) + + for _, otherRoom := range rooms { + if otherRoom.Intersects(newRoom.Rect) { + failed = true + break + } + } + if failed { + continue + } + if len(newRoom.Connectors) > 0 { + rooms = append(rooms, newRoom) + } + } + + for _, room := range rooms { + err := room.BlitToLevel(l) + if err != nil { + fmt.Printf("err: %v", err) + } + } + + centers := make([]types.Coords, 0) + for _, room := range rooms { + centers = append(centers, room.Center) + } + + for _, edge := range delaunay.GetMst(centers, l.W, l.H) { + MedianStraight(rng, l, rooms, centers, edge) + } + + return l, rooms +} + +func MedianStraight(rng *util.RNG, l *gamemap.Level, rooms []gamemap.Room, centers []types.Coords, edge types.Edge) { + //find connected rooms + var fromRoom, toRoom gamemap.Room + for _, room := range rooms { + if room.Center == edge.From { + fromRoom = room + continue + } + if room.Center == edge.To { + toRoom = room + continue + } + if len(fromRoom.Connectors) > 0 && len(toRoom.Connectors) > 0 { + break + } + } + + midpoint := edge.Midpoint() + fromConnector := findNearestCoonector(midpoint, fromRoom) + toConnector := findNearestCoonector(midpoint, toRoom) + connectStraight(rng, l, fromConnector, toConnector, midpoint) +} + +func findNearestCoonector(midpoint types.Coords, room gamemap.Room) types.Coords { + var nearest types.Coords + for _, con := range room.Connectors { + if nearest.X == 0 { + nearest = con + continue + } + if midpoint.DistanceTo(con) < midpoint.DistanceTo(nearest) { + nearest = con + } + } + return nearest +} + +func connectStraight(rng *util.RNG, l *gamemap.Level, from, to, midpoint types.Coords) { + toss := rng.Range(0, 1) + if toss == 0 { + digHTunnel(l, from.X, midpoint.X, from.Y) + digVTunnel(l, from.Y, to.Y, midpoint.X) + digHTunnel(l, midpoint.X, to.X, to.Y) + } else { + digVTunnel(l, from.Y, midpoint.Y, from.X) + digHTunnel(l, from.X, to.X, midpoint.Y) + digVTunnel(l, midpoint.Y, to.Y, to.X) + } +} + +func digHTunnel(l *gamemap.Level, x1, x2, y int) { + var start, finish int + if x1 < x2 { + start = x1 + finish = x2 + } else { + start = x2 + finish = x1 + } + for i := start; i <= finish; i++ { + if l.InBounds(types.Coords{i, y}) { + l.SetTileByXY(i, y, gamemap.NewFloor()) + //l.Tiles[i][y] = gamemap.NewFloor() + } + } +} + +func digVTunnel(l *gamemap.Level, y1, y2, x int) { + var start, finish int + if y1 < y2 { + start = y1 + finish = y2 + } else { + start = y2 + finish = y1 + } + for i := start; i <= finish; i++ { + if l.InBounds(types.Coords{x, i}) { + l.SetTileByXY(x, i, gamemap.NewFloor()) + } + } +} diff --git a/engine/gamemap/prefab.go b/engine/gamemap/prefab.go index 8b641bd..87224cb 100644 --- a/engine/gamemap/prefab.go +++ b/engine/gamemap/prefab.go @@ -57,11 +57,12 @@ func (pfbl PrefabLoader) PrefabRoomsList() []Room { panic(err) } + //prepare actual legends + currentTileLegend := file.DefaultTileLegend + currentMobsLegend := file.DefaultMobsLegend + currentItemLegend := file.DefaultItemLegend + for _, rawPrefab := range file.Prefabs { - //prepare actual legends - currentTileLegend := file.DefaultTileLegend - currentMobsLegend := file.DefaultMobsLegend - currentItemLegend := file.DefaultItemLegend for k,v := range rawPrefab.TileLegend { currentTileLegend[k] = v @@ -99,7 +100,7 @@ func (pfbl PrefabLoader) PrefabRoomsList() []Room { continue } if shortName == "connector" { - f = NewFloor + f = NewWall room.Connectors = append(room.Connectors, types.Coords{i,j}) } else { f, ok = TileTypeMap[shortName] @@ -122,4 +123,4 @@ var TileTypeMap = map[string]func()*Tile{ "decorated_wall": NewDecoratedWall, "water": NewWaterTile, "deep_water": NewDeepWaterTile, -} \ No newline at end of file +} diff --git a/engine/items/ammo.go b/engine/items/ammo.go new file mode 100644 index 0000000..d534853 --- /dev/null +++ b/engine/items/ammo.go @@ -0,0 +1,14 @@ +package items + +import ( + "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + "lab.zaar.be/thefish/alchemyst-go/engine/items/itemprops" +) + +type Ammo struct { + itemprops.DamageProfile +} + +func (a *Ammo) Type() string { + return ecs.AmmoComponent +} \ No newline at end of file diff --git a/engine/items/armor.go b/engine/items/armor.go new file mode 100644 index 0000000..9864d8e --- /dev/null +++ b/engine/items/armor.go @@ -0,0 +1,11 @@ +package items + +import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + +type Armor struct { + DefenceProfile struct{} +} + +func (a *Armor) Type() string { + return ecs.ArmorComponent +} diff --git a/engine/items/arms.go b/engine/items/arms.go new file mode 100644 index 0000000..6ec8e4a --- /dev/null +++ b/engine/items/arms.go @@ -0,0 +1,14 @@ +package items + +import ( + "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + "lab.zaar.be/thefish/alchemyst-go/engine/items/itemprops" +) + +type Arms struct { + itemprops.DamageProfile +} + +func (a Arms) Type() string { + return ecs.ArmsComponent +} diff --git a/engine/items/carried.go b/engine/items/carried.go index ef4ecad..95b21e1 100644 --- a/engine/items/carried.go +++ b/engine/items/carried.go @@ -2,8 +2,26 @@ package items import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" -type Carried struct {} +type CarriedFace interface { + Drop() + Pickup() +} + +type Carried struct { + Mass int //масса в граммах + Bulk int //внешний размер, см3 +} func (c *Carried) Type() string { return ecs.CarriedComponent } + +func (c *Carried) Pickup() {} +func (c *Carried) Drop() {} + +func (c *Carried) GetMass() int { + return c.Mass +} +func (c *Carried) GetBulk() int { + return c.Bulk +} \ No newline at end of file diff --git a/engine/items/itemprops/damage.go b/engine/items/itemprops/damage.go new file mode 100644 index 0000000..82d8cc1 --- /dev/null +++ b/engine/items/itemprops/damage.go @@ -0,0 +1,7 @@ +package itemprops + +type DamageProfile struct { + Pierce int + Bash int + Cleave int +} diff --git a/engine/items/ranged.go b/engine/items/ranged.go new file mode 100644 index 0000000..a462430 --- /dev/null +++ b/engine/items/ranged.go @@ -0,0 +1,11 @@ +package items + +import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + +type Ranged struct { + RangeProfile struct{} //это зависимость дальности-скорости от характеристик и атрибутов +} + +func (r *Ranged) Type() string { + return ecs.RangedComponent +} diff --git a/engine/items/useable.go b/engine/items/useable.go index dd8efaf..922d354 100644 --- a/engine/items/useable.go +++ b/engine/items/useable.go @@ -2,9 +2,16 @@ package items import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" -type Useable struct {} +type UsableFace interface { + Use() +} + +type Usable struct {} -func (u *Useable) Type() string { +func (u *Usable) Type() string { return ecs.UsableComponent +} + +func (u *Usable) Use() { } \ No newline at end of file diff --git a/engine/items/wearable.go b/engine/items/wearable.go new file mode 100644 index 0000000..0dd00f7 --- /dev/null +++ b/engine/items/wearable.go @@ -0,0 +1,16 @@ +package items + +import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + +type WearableFace interface { + Wear() + Takeoff() +} + +type Wearable struct { + Bodypart int //куда собстно одевать +} + +func (w *Wearable) Type() string { + return ecs.WearableComponent +} diff --git a/engine/matertals/properties.go b/engine/matertals/properties.go new file mode 100644 index 0000000..e8604bc --- /dev/null +++ b/engine/matertals/properties.go @@ -0,0 +1,82 @@ +package matertals + +import "github.com/rs/zerolog/log" + +type MaterialProperties struct { + Density float64 //Плотность (кг / м3) + Tougnness float64 //Ударная вязкость, мера скорости поглощения энергии без деформаций, джоули на квадратный метр в секунду + Brittleness float64 //Хрупкость - обратная пластичности (в основном за счет внешних эффектов), джоули на квадртаный метр, после которых раскол + MeltingPoint float64 //точка перехода из твердого в жидкое, градусы Цельсия при нормальном давлении + BoilingPoint float64 //точка кипения - из жидкого в газ, градусы Цельсия при нормальном давлении + Conductivity bool //проводимость эл. тока +} + +//агрегатное состояние +type MatterState int + +const ( + Solid MatterState = iota //кристаллическая решетка + Liquid + Gas + Plasma + + Glass //нет кристаллической решетки, аморфный +) + +var transitions = map[MatterState][]MatterState{ + Solid: {Liquid, Gas}, + Liquid: {Solid, Gas, Glass}, + Gas: {Solid, Plasma}, + Plasma: {Gas}, + + Glass: {Liquid}, + +} + +func (ms MatterState) Change(from MatterState, to MatterState) bool { + if from == to { + return true + } + + _, ok := transitions[from] + + if ok { + newStateFound := func(lst []MatterState, to MatterState) bool { + for _, b := range lst { + if b == to { + return true + } + } + return false + }(transitions[from], to) + if !newStateFound { + log.Warn().Msgf("Transition %s -> %s is impossible", from, to) + return false + } + // check temperatures/conditions, see template + /* + Solid -> Gas Sublimation + Solid -> Liquid Melting + + Liquid -> Gas Boiling + Liquid -> Solid Freezing + + Gas -> Solid Deposition + Gas -> Liquid Condensation + + Gas -> Plasma Ionization + Plasma -> Gas Recombination + */ + } + + //При фазовом переходе первого рода скачкообразно изменяются самые главные, первичные экстенсивные параметры: + // удельный объём, + // количество запасённой внутренней энергии, + // концентрация компонентов и т. п. + // + // Фазовые переходы второго рода происходят в тех случаях, когда меняется симметрия строения вещества + // (симметрия может полностью исчезнуть или понизиться). + + return true + +} \ No newline at end of file diff --git a/engine/screens/devmenu.go b/engine/screens/devmenu.go index fcb24f7..edbcca8 100644 --- a/engine/screens/devmenu.go +++ b/engine/screens/devmenu.go @@ -101,7 +101,7 @@ func (devm *DevmenuScreen) Render() { menuLayer.WithColor(devm.fgColor).PrintInside( devm.Rect, strings.Join([]string{ - "Dev Menu:", + "Действия для разработчика:", "[color=green]v[/color] - set all tiles visible", "[color=green]i[/color] - set all tiles invisible", fmt.Sprintf("[color=green]p[/color] - toggle passwall: %v", diff --git a/engine/types/egde.go b/engine/types/egde.go index 3a29e93..0e9c3b4 100644 --- a/engine/types/egde.go +++ b/engine/types/egde.go @@ -4,3 +4,23 @@ type Edge struct { From Coords To Coords } + +func (e *Edge) Midpoint() Coords { + var dx, dy, minX, minY int + if e.From.X > e.To.X { + minX = e.To.X + dx = e.From.X - e.To.X + } else { + minX = e.From.X + dx = e.To.X - e.From.X + } + + if e.From.Y > e.To.Y { + minY = e.To.Y + dy = e.From.Y - e.To.Y + } else { + minY = e.From.Y + dy = e.To.Y - e.From.Y + } + return Coords{minX + dx / 2, minY + dy / 2} +} \ No newline at end of file diff --git a/util/bresenham/bresenham.go b/util/bresenham/bresenham.go new file mode 100644 index 0000000..7090467 --- /dev/null +++ b/util/bresenham/bresenham.go @@ -0,0 +1,183 @@ +package bresenham + +import ( + "lab.zaar.be/thefish/alchemyst-go/engine/types" +) + +// dx > dy; x1 < x2; y1 < y2 +func BresenhamDxXRYD(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) { + dx, dy := To.X - From.X, 2*(To.Y - From.Y) + e, slope := dx, 2*dx + for ; dx != 0; dx-- { + canvas.Put(From.X, From.Y, symFunc()) + From.X++ + e -= dy + if e < 0 { + From.Y++ + e += slope + } + } +} + +// dy > dx; x1 < x2; y1 < y2 +func BresenhamDyXRYD(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) { + dx, dy := 2*(To.X - From.X), To.Y - From.Y + e, slope := dy, 2*dy + for ; dy != 0; dy-- { + canvas.Put(From.X, From.Y, symFunc()) + From.Y++ + e -= dx + if e < 0 { + From.X++ + e += slope + } + } +} + +// dx > dy; x1 < x2; y1 > y2 +func BresenhamDxXRYU(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) { + dx, dy := To.X - From.X, 2*(From.Y-To.Y) + e, slope := dx, 2*dx + for ; dx != 0; dx-- { + canvas.Put(From.X, From.Y, symFunc()) + From.X++ + e -= dy + if e < 0 { + From.Y-- + e += slope + } + } +} + +func BresenhamDyXRYU(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) { + dx, dy := 2*(To.X - From.X), From.Y-To.Y + e, slope := dy, 2*dy + for ; dy != 0; dy-- { + canvas.Put(From.X, From.Y, symFunc()) + From.Y-- + e -= dx + if e < 0 { + From.X++ + e += slope + } + } +} + +// Generalized with integer +func Bresenham(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) { + var dx, dy, e, slope int + + // Because drawing p1 -> p2 is equivalent to draw p2 -> p1, + // I sort points in x-axis order to handle only half of possible cases. + if From.X > To.X { + From.X, From.Y, To.X, To.Y = To.X, To.Y, From.X, From.Y + } + + dx, dy = To.X - From.X, To.Y - From.Y + // Because point is x-axis ordered, dx cannot be negative + if dy < 0 { + dy = -dy + } + + switch { + + // Is line a point ? + case From.X ==To.X && From.Y ==To.Y: + canvas.Put(From.X, From.Y, symFunc()) + + // Is line an horizontal ? + case From.Y ==To.Y: + for ; dx != 0; dx-- { + canvas.Put(From.X, From.Y, symFunc()) + From.X++ + } + canvas.Put(From.X, From.Y, symFunc()) + + // Is line a vertical ? + case From.X ==To.X: + if From.Y > To.Y { + From.Y, To.Y = To.Y, From.Y + } + for ; dy != 0; dy-- { + canvas.Put(From.X, From.Y, symFunc()) + From.Y++ + } + canvas.Put(From.X, From.Y, symFunc()) + + // Is line a diagonal ? + case dx == dy: + if From.Y < To.Y { + for ; dx != 0; dx-- { + canvas.Put(From.X, From.Y, symFunc()) + From.X++ + From.Y++ + } + } else { + for ; dx != 0; dx-- { + canvas.Put(From.X, From.Y, symFunc()) + From.X++ + From.Y-- + } + } + canvas.Put(From.X, From.Y, symFunc()) + + // wider than high ? + case dx > dy: + if From.Y < To.Y { + // BresenhamDxXRYD(img, x1, y1, x2, y2, col) + dy, e, slope = 2*dy, dx, 2*dx + for ; dx != 0; dx-- { + canvas.Put(From.X, From.Y, symFunc()) + From.X++ + e -= dy + if e < 0 { + From.Y++ + e += slope + } + } + } else { + // BresenhamDxXRYU(img, x1, y1, x2, y2, col) + dy, e, slope = 2*dy, dx, 2*dx + for ; dx != 0; dx-- { + canvas.Put(From.X, From.Y, symFunc()) + From.X++ + e -= dy + if e < 0 { + From.Y-- + e += slope + } + } + } + canvas.Put(To.X, To.Y, symFunc()) + + // higher than wide. + default: + if From.Y < To.Y { + // BresenhamDyXRYD(img, x1, y1, x2, y2, col) + dx, e, slope = 2*dx, dy, 2*dy + for ; dy != 0; dy-- { + canvas.Put(From.X, From.Y, symFunc()) + From.Y++ + e -= dx + if e < 0 { + From.X++ + e += slope + } + } + } else { + // BresenhamDyXRYU(img, x1, y1, x2, y2, col) + dx, e, slope = 2*dx, dy, 2*dy + for ; dy != 0; dy-- { + canvas.Put(From.X, From.Y, symFunc()) + From.Y-- + e -= dx + if e < 0 { + From.X++ + e += slope + } + } + } + canvas.Put(To.X, To.Y, symFunc()) + //img.Set(x2, y2, col) + } +} diff --git a/util/kruskals/algorithm.go b/util/kruskals/algorithm.go index caf3c3a..679d26d 100644 --- a/util/kruskals/algorithm.go +++ b/util/kruskals/algorithm.go @@ -12,7 +12,7 @@ type WeightedEdge interface { From() int // To returns the integer identifier of the second vertex. To() int - // Weight returns the integer identifier of the weight/cost. + // Mass returns the integer identifier of the weight/cost. Weight() int }