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) }