commit c2142436b958606e6f0fe0da160ae10d2b8ea321 Author: sam <multisniperism@gmail.com> Date: Fri Mar 7 18:58:47 2025 +1300 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0fa0f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +converter* +viewer* diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..dfc00ef --- /dev/null +++ b/build.bat @@ -0,0 +1,2 @@ +odin build . -out:converter +odin build . -out:viewer -define:MAKE_CONVERTER=false diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..dfc00ef --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +odin build . -out:converter +odin build . -out:viewer -define:MAKE_CONVERTER=false diff --git a/kiva.sand b/kiva.sand new file mode 100644 index 0000000..990ce8a Binary files /dev/null and b/kiva.sand differ diff --git a/main.odin b/main.odin new file mode 100644 index 0000000..bf7fd26 --- /dev/null +++ b/main.odin @@ -0,0 +1,163 @@ +package main + +import "core:encoding/cbor" +import "core:fmt" +import "core:image/png" +import "core:math/rand" +import "core:os" +import "core:strings" +import "core:time" +import rl "vendor:raylib" + +MAKE_CONVERTER :: #config(MAKE_CONVERTER, true) + +Pixel :: struct { + id: int, + pos: rl.Vector2, + already_moved: bool, + color: rl.Color, +} + +State :: struct { + seed: u64, + colors: map[int]rl.Color, +} +state: State + +SIZE :: 800 +SCALE :: 20 +DIVIDED :: SIZE / SCALE +pixels: [dynamic]Pixel + +get_pixel :: proc(pos: rl.Vector2) -> Maybe(^Pixel) { + for &pixel in pixels { + if pixel.pos == pos { + return &pixel + } + } + + return nil +} + +is_finished :: proc() -> bool { + return len(pixels) >= DIVIDED * DIVIDED +} + +main :: proc() { + rl.SetTraceLogLevel(.ERROR) + + when MAKE_CONVERTER { + if len(os.args) < 3 { + fmt.eprintln("Please provide input and output filenames!") + return + } + + state.seed = u64(time.time_to_unix(time.now())) + rand.reset(state.seed) + rl.SetConfigFlags({.WINDOW_HIDDEN}) + fmt.println("Beginning conversion...") + } else { + if len(os.args) < 2 { + fmt.eprintln("Please provide a sand image!") + return + } + + bytes, err := os.read_entire_file_or_err(os.args[1]) + if err != nil { + fmt.eprintln("Failed to read sand image:", err) + return + } + unmarshal_err := cbor.unmarshal(string(bytes), &state) + if unmarshal_err != nil { + fmt.eprintln("Failed to unmarshal sand image:", unmarshal_err) + } + rand.reset(state.seed) + } + + rl.InitWindow(SIZE, SIZE, strings.clone_to_cstring(os.args[1])) + when MAKE_CONVERTER { + img := rl.LoadImage(strings.clone_to_cstring(os.args[1])) + rl.ImageResize(&img, DIVIDED, DIVIDED) + } else { + rl.SetTargetFPS(120) + } + + for !rl.WindowShouldClose() { + for &pixel in pixels { + pixel.already_moved = false + } + + if !is_finished() { + pos := rl.Vector2{f32(rand.int_max(DIVIDED)), 0} + if _, found := get_pixel(pos).?; !found { + color: rl.Color + id := len(pixels) + when !MAKE_CONVERTER { + color = state.colors[id] + } + append(&pixels, Pixel{id = id, pos = pos, color = color}) + } + } + + when !MAKE_CONVERTER { + rl.BeginDrawing() + rl.ClearBackground(rl.BLACK) + } + for &pixel in pixels { + when !MAKE_CONVERTER { + rl.DrawRectangleV(pixel.pos * SCALE, {SCALE, SCALE}, pixel.color) + } + + if pixel.already_moved do continue + if pixel.pos.y >= DIVIDED - 1 do continue + + down := pixel.pos + {0, 1} + left := pixel.pos + {-1, 1} + right := pixel.pos + {1, 1} + if _, found := get_pixel(down).?; !found { + pixel.pos = down + pixel.already_moved = true + } else if _, found := get_pixel(left).?; !found { + if pixel.pos.x > 0 { + pixel.pos = left + pixel.already_moved = true + } + } + + if _, found := get_pixel(right).?; !found && !pixel.already_moved { + if pixel.pos.x >= DIVIDED - 1 do continue + pixel.pos = right + } + } + when !MAKE_CONVERTER { + rl.EndDrawing() + } + + when MAKE_CONVERTER { + if is_finished() { + for x in 0 ..< DIVIDED { + for y in 0 ..< DIVIDED { + pixel := get_pixel(rl.Vector2{f32(x), f32(y)}).? + color := rl.GetImageColor(img, i32(x), i32(y)) + pixel.color = color + state.colors[pixel.id] = color + } + } + + bytes, err := cbor.marshal(state) + if err != nil { + fmt.eprintln("Failed to marshal sand image:", err) + return + } + write_err := os.write_entire_file_or_err(os.args[2], bytes) + if write_err != nil { + fmt.eprintln("Failed to write sand image:", write_err) + return + } + + fmt.println("Conversion complete") + rl.CloseWindow() + } + } + } +} diff --git a/mathew.sand b/mathew.sand new file mode 100644 index 0000000..5a755f5 Binary files /dev/null and b/mathew.sand differ diff --git a/odinfmt.json b/odinfmt.json new file mode 100644 index 0000000..4137bac --- /dev/null +++ b/odinfmt.json @@ -0,0 +1,5 @@ +{ + "character_width": 110, + "tabs": false, + "tabs_width": 4 +} diff --git a/out.sand b/out.sand new file mode 100644 index 0000000..5026824 Binary files /dev/null and b/out.sand differ diff --git a/rose.sand b/rose.sand new file mode 100644 index 0000000..7a72907 Binary files /dev/null and b/rose.sand differ diff --git a/sam.sand b/sam.sand new file mode 100644 index 0000000..d38e103 Binary files /dev/null and b/sam.sand differ