166 lines
4.5 KiB
Odin
166 lines
4.5 KiB
Odin
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 {
|
|
pixel.already_moved = true
|
|
if pixel.pos.x >= DIVIDED - 1 do continue
|
|
pixel.pos = right
|
|
}
|
|
|
|
pixel.already_moved = true
|
|
}
|
|
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()
|
|
}
|
|
}
|
|
}
|
|
}
|