Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b2f87f1
feat: add point to hex algorithms
theomund Jul 11, 2026
3265925
fix: run tests for all packages
theomund Jul 11, 2026
d6bbdf2
feat: start to rewrite hex grid logic
theomund Jul 12, 2026
45ec1e6
feat: create hex indexer
theomund Jul 12, 2026
3ebbc2c
feat: add language server config
theomund Jul 13, 2026
1b8659d
feat: add unit test for hex indexing
theomund Jul 13, 2026
06e58fe
feat: rework hex logic
theomund Jul 14, 2026
258a39f
fix: add missing license header
theomund Jul 14, 2026
9c09b25
fix: shorten procedure parameters
theomund Jul 14, 2026
ab9a63a
fix: shorten layout fields
theomund Jul 14, 2026
d094893
feat: various fixes and refactors
theomund Jul 15, 2026
080a771
feat: flatten project structure
theomund Jul 15, 2026
e9bdd26
feat: add strict style checking
theomund Jul 15, 2026
fbfd85e
feat: add basic error handler
theomund Jul 15, 2026
3069998
fix: address error conditional
theomund Jul 15, 2026
df1e1ee
fix: remove unused constructor
theomund Jul 15, 2026
ea95cde
feat: use vector types
theomund Jul 15, 2026
ffdc170
fix: use floor function
theomund Jul 15, 2026
4c0778a
feat: use matrix types
theomund Jul 15, 2026
b17135b
feat: add unit test for camera constructor
theomund Jul 15, 2026
ce16a16
feat: add unit test for point constructor
theomund Jul 15, 2026
8712e30
feat: condense camera logic
theomund Jul 15, 2026
6751aac
fix: address operator error
theomund Jul 15, 2026
89b282e
feat: add unit test for camera panning
theomund Jul 15, 2026
ff463b2
feat: add unit test for camera zooming
theomund Jul 15, 2026
c1ad7ef
fix: rename constants file
theomund Jul 15, 2026
ef9d947
feat: create separate file for types
theomund Jul 15, 2026
d279f50
feat: add unit test for flat orientations
theomund Jul 15, 2026
1c4b004
feat: add code coverage measuring
theomund Jul 15, 2026
a9581d5
fix: dump code coverage results to hidden folder
theomund Jul 16, 2026
54e6867
fix: remove redundant pattern inclusion
theomund Jul 16, 2026
05d0d8a
feat: add unit test for sectors
theomund Jul 16, 2026
a28083a
feat: add offset coordinate type
theomund Jul 16, 2026
8391acb
feat: add unit tests for offset coordinates
theomund Jul 16, 2026
e57e1d9
feat: add offset coordinate into hex indexer
theomund Jul 16, 2026
40e62b4
fix: remove unused import
theomund Jul 16, 2026
545a52f
fix: layout size needs to be equal to hex size
theomund Jul 16, 2026
b542bb9
feat: add hex hovering
theomund Jul 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@
"ols.server.path": "ols"
}
}
}
},
"securityOpt": [
"seccomp=unconfined"
]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

.env
.kcov/
spinward
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ ifneq (,$(wildcard ./.env))
export
endif

.PHONY: all build clean format lint run test
.PHONY: all build clean coverage format lint run test

all: lint test build

Expand All @@ -17,12 +17,16 @@ build:
clean:
git clean -fdxe ".env"

coverage:
odin build . -build-mode:test -debug
kcov --dump-summary --exclude-pattern=_test.odin --include-path=. .kcov spinward

format:
odinfmt . -w

lint:
hadolint .devcontainer/Dockerfile
odin check . -vet
odin check . -strict-style -vet
yamllint .github/workflows/linux.yml

run:
Expand Down
17 changes: 11 additions & 6 deletions viewer/renderer.odin → app.odin
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,35 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package viewer
package main

import rl "vendor:raylib"

run :: proc() {
run :: proc() -> Error {
rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
defer rl.CloseWindow()

if !rl.IsWindowReady() {
return .Initialization_Failed
}

camera := new_camera()
sector := new_sector("Spinward Marches", {38, 6})
sector := new_sector("Spinward Marches")

for !rl.WindowShouldClose() {
rl.BeginDrawing()

rl.ClearBackground(rl.BLACK)
rl.DrawFPS(16, WINDOW_HEIGHT - 32)

pan_camera(&camera)
zoom_camera(&camera)
poll_camera(&camera)

rl.BeginMode2D(camera)
draw_sector(sector)
draw_sector(sector, camera)
rl.EndMode2D()

rl.EndDrawing()
}

return nil
}
36 changes: 36 additions & 0 deletions camera.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

import "core:math"
import rl "vendor:raylib"

new_camera :: proc() -> rl.Camera2D {
return {zoom = 1.0}
}

poll_camera :: proc(camera: ^rl.Camera2D) {
if rl.IsMouseButtonDown(.RIGHT) {
pan_camera(camera, rl.GetMouseDelta())
}

if wheel := rl.GetMouseWheelMove(); wheel != 0.0 {
zoom_camera(camera, wheel, rl.GetMousePosition())
}
}

pan_camera :: proc(camera: ^rl.Camera2D, delta: rl.Vector2) {
camera.target += delta * -1.0 / camera.zoom
}

zoom_camera :: proc(camera: ^rl.Camera2D, wheel: f32, position: rl.Vector2) {
camera^ = {
offset = position,
target = rl.GetScreenToWorld2D(position, camera^),
zoom = clamp(math.exp(math.log(camera.zoom, math.E) + 0.2 * wheel), 0.125, 64.0),
}
}
35 changes: 35 additions & 0 deletions camera_test.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

import "core:testing"
import rl "vendor:raylib"

@(test)
test_new_camera :: proc(t: ^testing.T) {
testing.expect_value(t, new_camera(), rl.Camera2D{zoom = 1.0})
}

@(test)
test_pan_camera :: proc(t: ^testing.T) {
camera := new_camera()

pan_camera(&camera, {1.0, 2.0})

testing.expect_value(t, camera.target, rl.Vector2{-1.0, -2.0})
}

@(test)
test_zoom_camera :: proc(t: ^testing.T) {
camera := new_camera()

zoom_camera(&camera, 1.0, {1.0, 2.0})

testing.expect_value(t, camera.offset, rl.Vector2{1, 2})
testing.expect_value(t, camera.target, rl.Vector2{1, 2})
testing.expect_value(t, camera.zoom, 1.2214028)
}
6 changes: 4 additions & 2 deletions viewer/config.odin → constants.odin
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package viewer
package main

HEX_SIZE :: 12
HEX_SIZE :: 24

ODD_OFFSET :: -1

WINDOW_WIDTH :: 1920
WINDOW_HEIGHT :: 1080
Expand Down
57 changes: 57 additions & 0 deletions hex.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

import "core:fmt"
import "core:math"

new_hex :: proc(q, r, s: f32) -> Hex {
assert(math.round(q + r + s) == 0)

return {q, r, s}
}

hex_index :: proc(hex: Hex) -> string {
offset := qoffset_from_cube(hex)

return fmt.tprintf("%02d%02d", i32(offset.x + 1), i32(offset.y + 1))
}

hex_lerp :: proc(a, b: Hex, t: f32) -> Hex {
return math.lerp(a, b, t)
}

hex_to_pixel :: proc(layout: Layout, hex: Hex) -> Point {
M := layout.orientation
size := layout.size
origin := layout.origin

x := (M.f[0, 0] * hex.x + M.f[0, 1] * hex.y) * size.x
y := (M.f[1, 0] * hex.x + M.f[1, 1] * hex.y) * size.y

return new_point(x + origin.x, y + origin.y)
}

hex_round :: proc(hex: Hex) -> Hex {
q := math.round(hex.x)
r := math.round(hex.y)
s := math.round(hex.z)

q_diff := math.abs(q - hex.x)
r_diff := math.abs(r - hex.y)
s_diff := math.abs(s - hex.z)

if q_diff > r_diff && q_diff > s_diff {
q = -r - s
} else if r_diff > s_diff {
r = -q - s
} else {
s = -q - r
}

return new_hex(q, r, s)
}
48 changes: 48 additions & 0 deletions hex_test.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

import "core:testing"

@(test)
test_new_hex :: proc(t: ^testing.T) {
testing.expect_value(t, new_hex(1, 1, -2), Hex{1, 1, -2})
testing.expect_value(t, new_hex(1.0, 1.0, -2.0), Hex{1.0, 1.0, -2.0})
}

@(test)
test_hex_index :: proc(t: ^testing.T) {
testing.expect_value(t, hex_index(new_hex(0, 0, 0)), "0101")
testing.expect_value(t, hex_index(new_hex(1, 0, -1)), "0201")
testing.expect_value(t, hex_index(new_hex(0, 1, -1)), "0102")
testing.expect_value(t, hex_index(new_hex(1, 1, -2)), "0202")
}

@(test)
test_hex_round :: proc(t: ^testing.T) {
a := new_hex(0.0, 0.0, 0.0)
b := new_hex(1.0, -1.0, 0.0)
c := new_hex(0.0, -1.0, 1.0)
d := new_hex(10.0, -20.0, 10.0)
e := new_hex(5, -10, 5)
f := new_hex(
a.x * 0.4 + b.x * 0.3 + c.x * 0.3,
a.y * 0.4 + b.y * 0.3 + c.y * 0.3,
a.z * 0.4 + b.z * 0.3 + c.z * 0.3,
)
g := new_hex(
a.x * 0.3 + b.x * 0.3 + c.x * 0.4,
a.y * 0.3 + b.y * 0.3 + c.y * 0.4,
a.z * 0.3 + b.z * 0.3 + c.z * 0.4,
)

testing.expect_value(t, hex_round(hex_lerp(a, d, 0.5)), e)
testing.expect_value(t, hex_round(a), hex_round(hex_lerp(a, b, 0.499)))
testing.expect_value(t, hex_round(b), hex_round(hex_lerp(a, b, 0.501)))
testing.expect_value(t, hex_round(a), hex_round(f))
testing.expect_value(t, hex_round(c), hex_round(g))
}
11 changes: 11 additions & 0 deletions layout.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

new_layout :: proc(orientation: Orientation, origin, size: Point) -> Layout {
return {orientation, origin, size}
}
17 changes: 17 additions & 0 deletions layout_test.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

import "core:testing"

@(test)
test_new_layout :: proc(t: ^testing.T) {
hex := new_hex(3, 4, -7)
flat := new_layout(flat_orientation(), new_point(10.0, 15.0), new_point(35.0, 71.0))

testing.expect_value(t, pixel_to_hex_rounded(flat, hex_to_pixel(flat, hex)), hex)
}
6 changes: 4 additions & 2 deletions main.odin
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

package main

import "viewer"
import "core:os"

main :: proc() {
viewer.run()
if err := run(); err != nil {
os.exit(1)
}
}
26 changes: 26 additions & 0 deletions offset.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

new_offset :: proc(col, row: f32) -> Offset {
return {col, row}
}

qoffset_from_cube :: proc(hex: Hex) -> Offset {
col := hex.x
row := hex.y + (hex.x + ODD_OFFSET * f32(i32(hex.x) & 1)) / 2

return new_offset(col, row)
}

qoffset_to_cube :: proc(offset: Offset) -> Hex {
q := offset.x
r := offset.y - (offset.x + ODD_OFFSET * f32(i32(offset.x) & 1)) / 2
s := -q - r

return new_hex(q, r, s)
}
26 changes: 26 additions & 0 deletions offset_test.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package main

import "core:testing"

@(test)
test_new_offset :: proc(t: ^testing.T) {
testing.expect_value(t, new_offset(1, 2), Offset{1, 2})
}

@(test)
test_qoffset_from_cube :: proc(t: ^testing.T) {
testing.expect_value(t, qoffset_from_cube(new_hex(-2, 3, -1)), new_offset(-2, 2))
testing.expect_value(t, qoffset_from_cube(new_hex(-1, -1, 2)), new_offset(-1, -2))
}

@(test)
test_qoffset_to_cube :: proc(t: ^testing.T) {
testing.expect_value(t, qoffset_to_cube(new_offset(-2, 2)), new_hex(-2, 3, -1))
testing.expect_value(t, qoffset_to_cube(new_offset(-1, -2)), new_hex(-1, -1, 2))
}
6 changes: 6 additions & 0 deletions ols.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json",
"enable_document_symbols": true,
"enable_hover": true,
"enable_snippets": true
}
Loading