package main
import "core:log"
import "core:math"
import "core:math/rand"
import "core:slice"
pallette := []u32 {
0x29009e,
0x0029b4,
0x003ec5,
0x004ed1,
0x005cd7,
0x0067d7,
0x0072d2,
0x007cc9,
0x0085bc,
0x008ead,
0x00969b,
0x009f8a,
0x00a678,
0x00ad67,
0x00b457,
0x4cb94a,
}
steps := f32(len(pallette) - 1) / 255
App :: struct {
balls: [dynamic][4]int,
cell_size: int,
grid_width: int,
cells: []u8,
next_cells: []u8,
}
app_init :: proc() -> ^App {
log.debugf(
"pallette test: %d %02x%02x%02x==29009e\n",
len(pallette),
pallette[0] >> 16 & 0xff,
pallette[0] >> 8 & 0xff,
pallette[0] & 0xff,
)
app := new(App)
app.cell_size = 8
app.grid_width = 70
app.cells = make([]u8, app.grid_width * app.grid_width)
app.next_cells = make([]u8, app.grid_width * app.grid_width)
app.balls = make([dynamic][4]int)
append(&app.balls, [4]int{10, 10, 1, 1})
append(&app.balls, [4]int{60, 60, 1, -1})
append(&app.balls, [4]int{60, 10, 1, 1})
for _, i in app.cells {
//app.cells[i] = u8(rand.int_max(len(pallette))) if u8(rand.int_max(2)) == 1 else 0
//app.cells[i] = u8(rand.int_max(2))
pos_x := i % app.grid_width
pos_y := i / app.grid_width
cell: int
for ball in app.balls {
dist := 1 / math.sqrt(math.pow(f32(ball.x - pos_x), 2) + math.pow(f32(ball.y - pos_y), 2))
cell += int(dist * 255)
}
app.cells[i] = u8(cell / len(app.balls))
}
return app
}
draw_cells :: proc(app: ^App, bitmap: ^Bitmap) {
left_x := (bitmap.width / 2) - ((app.grid_width / 2) * app.cell_size)
right_x := (bitmap.width / 2) + ((app.grid_width / 2) * app.cell_size)
top_y := (bitmap.height / 2) - ((app.grid_width / 2) * app.cell_size)
bottom_y := (bitmap.height / 2) + ((app.grid_width / 2) * app.cell_size)
/*for i in 0 ..= app.grid_width {
offset := i * app.cell_size
draw_line(bitmap, left_x, top_y + offset, right_x, top_y + offset, 0xffffffff)
draw_line(bitmap, left_x + offset, top_y, left_x + offset, bottom_y, 0xffffffff)
}*/
for cell, i in app.cells {
if cell > 0 {
pos_x := i % app.grid_width
pos_y := i / app.grid_width
draw_rect_filled(
bitmap,
left_x + (pos_x * app.cell_size),
top_y + (pos_y * app.cell_size),
left_x + (pos_x * app.cell_size) + app.cell_size,
top_y + (pos_y * app.cell_size) + app.cell_size,
pallette[cell],
pallette[cell],
)
}
}
draw_rect(bitmap, left_x, top_y, right_x, bottom_y, 0xffffffff)
}
threshold :: proc(val, thresh: u8) -> u8 {
return 1 if val >= thresh else 0
}
get_cell_state :: proc(app: ^App, x, y: int, thresh: u8) -> u8 {
state := threshold(app.cells[(y * app.grid_width) + x], thresh) << 3
state += threshold(app.cells[(y * app.grid_width) + x + 1], thresh) << 2
state += threshold(app.cells[((y + 1) * app.grid_width) + x + 1], thresh) << 1
state += threshold(app.cells[((y + 1) * app.grid_width) + x], thresh)
return state
}
draw_marching_squares :: proc(app: ^App, bitmap: ^Bitmap) {
left_x := (bitmap.width / 2) - ((app.grid_width / 2) * app.cell_size)
right_x := (bitmap.width / 2) + ((app.grid_width / 2) * app.cell_size)
top_y := (bitmap.height / 2) - ((app.grid_width / 2) * app.cell_size)
bottom_y := (bitmap.height / 2) + ((app.grid_width / 2) * app.cell_size)
half_size := app.cell_size / 2
for y in 0 ..< app.grid_width {
for x in 0 ..< app.grid_width {
pos_x := x * app.cell_size
pos_y := y * app.cell_size
if x < app.grid_width - 1 && y < app.grid_width - 1 {
a := [2]int{left_x + pos_x + half_size, top_y + pos_y}
b := [2]int{left_x + pos_x + app.cell_size, top_y + pos_y + half_size}
c := [2]int{left_x + pos_x + half_size, top_y + pos_y + app.cell_size}
d := [2]int{left_x + pos_x, top_y + pos_y + half_size}
state := get_cell_state(app, x, y, 18)
switch state {
case 1:
draw_line(bitmap, c.x, c.y, d.x, d.y, 0xffffffff)
case 2:
draw_line(bitmap, b.x, b.y, c.x, c.y, 0xffffffff)
case 3:
draw_line(bitmap, b.x, b.y, d.x, d.y, 0xffffffff)
case 4:
draw_line(bitmap, a.x, a.y, b.x, b.y, 0xffffffff)
case 5:
draw_line(bitmap, a.x, a.y, d.x, d.y, 0xffffffff)
draw_line(bitmap, b.x, b.y, c.x, c.y, 0xffffffff)
case 6:
draw_line(bitmap, a.x, a.y, c.x, c.y, 0xffffffff)
case 7:
draw_line(bitmap, a.x, a.y, d.x, d.y, 0xffffffff)
case 8:
draw_line(bitmap, a.x, a.y, d.x, d.y, 0xffffffff)
case 9:
draw_line(bitmap, a.x, a.y, c.x, c.y, 0xffffffff)
case 10:
draw_line(bitmap, a.x, a.y, b.x, b.y, 0xffffffff)
draw_line(bitmap, c.x, c.y, d.x, d.y, 0xffffffff)
case 11:
draw_line(bitmap, a.x, a.y, b.x, b.y, 0xffffffff)
case 12:
draw_line(bitmap, b.x, b.y, d.x, d.y, 0xffffffff)
case 13:
draw_line(bitmap, b.x, b.y, c.x, c.y, 0xffffffff)
case 14:
draw_line(bitmap, c.x, c.y, d.x, d.y, 0xffffffff)
}
}
/*cell := threshold(app.cells[(y * app.grid_width) + x], 24)
if cell > 0 {
draw_rect_filled(
bitmap,
left_x + pos_x - 1,
top_y + pos_y - 1,
left_x + pos_x + 1,
top_y + pos_y + 1,
0xff00ff00,
0xff00ff00,
)
} else {
draw_rect_filled(
bitmap,
left_x + pos_x - 1,
top_y + pos_y - 1,
left_x + pos_x + 1,
top_y + pos_y + 1,
0xffff0000,
0xffff0000,
)
}*/
}
}
draw_rect(bitmap, left_x - half_size, top_y - half_size, right_x - half_size, bottom_y - half_size, 0xffffffff)
}
do_xor :: proc(app: ^App, bitmap: ^Bitmap) {
@(static)
offset: int
i: int
for y in 0 ..< bitmap.height {
for x in 0 ..< bitmap.width {
c := int(f32(u8((x + offset) ~ (y + offset))) * steps)
bitmap.buffer[i + 0] = u8(pallette[c] & 0xff) / 10
bitmap.buffer[i + 1] = u8(pallette[c] >> 8 & 0xff) / 10
bitmap.buffer[i + 2] = u8(pallette[c] >> 16 & 0xff) / 10
bitmap.buffer[i + 3] = 255
i += 4
}
}
offset += 1
}
do_sand :: proc(app: ^App, bitmap: ^Bitmap) {
@(static)
count: int
@(static)
pallette_color: u8 = 1
if count % 20 == 0 {
pallette_color = (1 + (pallette_color + 1)) % u8(len(pallette))
}
app.cells[rand.int_max(40) - 20 + (app.grid_width / 2)] = pallette_color
app.cells[rand.int_max(40) - 20 + (app.grid_width / 2)] = pallette_color
app.cells[rand.int_max(40) - 20 + (app.grid_width / 2)] = pallette_color
app.cells[rand.int_max(40) - 20 + (app.grid_width / 2)] = pallette_color
count += 1
slice.fill(app.next_cells, 0)
for cell, i in app.cells {
if cell > 0 {
if i >= len(app.cells) - app.grid_width {
app.next_cells[i] = cell
continue
}
next_cell := i + app.grid_width
if (app.cells[next_cell] == 0) {
app.next_cells[next_cell] = cell
} else {
left_cell := next_cell - 1
right_cell := next_cell + 1
cell_x := next_cell % app.grid_width
if cell_x == 0 {
left_cell += app.grid_width
}
if cell_x == app.grid_width - 1 {
right_cell -= app.grid_width
}
check_cells := [2]int{left_cell, right_cell}
if rand.int_max(2) == 1 {
check_cells[0] = right_cell
check_cells[1] = left_cell
}
if app.cells[check_cells[0]] == 0 {
app.next_cells[check_cells[0]] = cell
} else if app.cells[check_cells[1]] == 0 {
app.next_cells[check_cells[1]] = cell
} else {
app.next_cells[i] = cell
}
}
}
}
app.cells, app.next_cells = app.next_cells, app.cells
}
/** Conway's Game of Life
1. Any live cell with fewer than two live neighbours dies, as if by underpopulation.
2. Any live cell with two or three live neighbours lives on to the next generation.
3. Any live cell with more than three live neighbours dies, as if by overpopulation.
4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
*/
do_conway :: proc(app: ^App, bitmap: ^Bitmap) {
slice.fill(app.next_cells, 0)
for cell, i in app.cells {
num_neighbours: int
pos_x := i % app.grid_width
pos_y := i / app.grid_width
neighbours := [][2]int {
{pos_x - 1, pos_y - 1},
{pos_x, pos_y - 1},
{pos_x + 1, pos_y - 1},
{pos_x - 1, pos_y},
{pos_x + 1, pos_y},
{pos_x - 1, pos_y + 1},
{pos_x, pos_y + 1},
{pos_x + 1, pos_y + 1},
}
for ni in neighbours {
x := ni.x
y := ni.y
if x < 0 {
x += app.grid_width
}
if x >= app.grid_width {
x -= app.grid_width
}
if y < 0 {
y += app.grid_width
}
if y >= app.grid_width {
y -= app.grid_width
}
ci := (y * app.grid_width) + x
if app.cells[ci] > 0 {
num_neighbours += 1
}
}
if cell > 0 && (num_neighbours == 2 || num_neighbours == 3) {
app.next_cells[i] = cell
}
if cell == 0 && num_neighbours == 3 {
app.next_cells[i] = u8(rand.int_max(len(pallette) - 1) + 1)
}
}
app.cells, app.next_cells = app.next_cells, app.cells
}
app_update_and_draw :: proc(app: ^App, bitmap: ^Bitmap) {
// test
//do_xor(app, bitmap)
// cells
//do_sand(app, bitmap)
//do_conway(app, bitmap)
//draw_cells(app, bitmap)
// marching squares
for _, i in app.balls {
if app.balls[i].x >= app.grid_width || app.balls[i].x <= 0 {
app.balls[i].z = -app.balls[i].z
}
if app.balls[i].y >= app.grid_width || app.balls[i].y <= 0 {
app.balls[i].w = -app.balls[i].w
}
app.balls[i].x += app.balls[i].z
app.balls[i].y += app.balls[i].w
}
for _, i in app.cells {
pos_x := i % app.grid_width
pos_y := i / app.grid_width
cell: int
for ball in app.balls {
dist := 1 / math.sqrt(math.pow(f32(ball.x - pos_x), 2) + math.pow(f32(ball.y - pos_y), 2))
cell += int(dist * 255)
}
app.cells[i] = u8(cell / len(app.balls))
}
/*left_x := (bitmap.width / 2) - ((app.grid_width / 2) * app.cell_size)
top_y := (bitmap.height / 2) - ((app.grid_width / 2) * app.cell_size)
for cell, i in app.cells {
if cell > 0 {
pos_x := i % app.grid_width
pos_y := i / app.grid_width
draw_rect_filled(
bitmap,
left_x + (pos_x * app.cell_size),
top_y + (pos_y * app.cell_size),
left_x + (pos_x * app.cell_size) + app.cell_size,
top_y + (pos_y * app.cell_size) + app.cell_size,
0xff000000 + u32(cell) << 16 + u32(cell) << 8 + u32(cell),
0xff000000 + u32(cell) << 16 + u32(cell) << 8 + u32(cell),
)
}
}*/
draw_marching_squares(app, bitmap)
}