#import "Basic"()( MEMORY_DEBUGGER = MEMORY_DEBUGGER_ENABLED ); #import "Math"; #import "Print_Vars"; #import "raylib"; #load "rect_cut.jai"; #load "gui.jai"; #load "input.jai"; #load "tooltip.jai"; #load "state.jai"; #load "vtree.jai"; #load "htree.jai"; #load "cells.jai"; /** Ideas / Todo - Different font - Hot reload a binary dump - Update the binary dump - Generic approach for other types of info data in the tooltip - Smooth zoom (maybe) - Second option for visualizing the tree -> https://en.wikipedia.org/wiki/Hyperbolic_tree */ rects := Rectangle.[ { -200.0, -160.0, 40.0, 45.0 }, { -140.0, -100.0, 25.0, 25.0 }, { -7.0, 60.0, 70.0, 50.0 }, { 55.0, 75.0, 25.0, 25.0 }, { -180.0, 40.0, 45.0, 25.0 }, { 120.0, 20.0, 9.0, 16.0 }, { 10.0, 35.0, 25.0, 15.0 }, { -80.0, 100.0, 21.0, 32.0 }, { 30.0, -70.0, 9.0, 16.0 }, ]; tree: [..]int; bvs: [..]Aabb; mode: Mode; gui_feature: Gui_Feature; camera: Camera2D; frame_time: float; mouse_screen: Vector2; mouse_world: Vector2; color_choice := Color.[ ORANGE, YELLOW, GREEN, BLUE, ]; NODES_MAX :: 256; NODE_ROOT :: -9; NODE_INTERNAL :: -7; NODE_EMPTY :: -1; COLOR_NODE_ROOT :: Color.{ 138, 62, 81, 255 }; COLOR_NODE_INTERNAL :: Color.{ 30, 60, 120, 255 }; COLOR_NODE_EMPTY :: #run COLOR_BACKGROUND + { 10, 10, 10, 0 }; COLOR_NODE_VALUE :: Color.{ 30, 110, 70, 255 }; COLOR_BACKGROUND :: Color.{ 42, 42, 60, 255 }; COLOR_HIGHLIGHT0 :: Color.{ 30, 30, 30, 0 }; COLOR_HIGHLIGHT1 :: Color.{ 70, 70, 70, 0 }; COLOR_HIGHLIGHT2 :: Color.{ 110, 110, 110, 0 }; COLOR_CELLS_BORDER :: #run COLOR_BACKGROUND + COLOR_HIGHLIGHT1; COLOR_CELLS_BORDER_HIGHLIGHT :: #run COLOR_CELLS_BORDER + COLOR_HIGHLIGHT1; DRAW_BV :: true; SCREEN_WIDTH :: 1920; SCREEN_HEIGHT :: 1080; TREE_VIEW :: true; Aabb :: struct { upper_bound: Vector2; lower_bound: Vector2; } Node :: struct { parent: int = NODE_EMPTY; left: int = NODE_EMPTY; right: int = NODE_EMPTY; } Statistics :: struct { nodes: int; children: int; empty: int; total: int; } #if true main :: () { #if MEMORY_DEBUGGER_ENABLED defer report_memory_leaks(); SetTraceLogLevel(.LOG_NONE); SetConfigFlags(.FLAG_MSAA_4X_HINT); tree_build(); tree_copy := array_copy(tree); log_vars(tree_copy); stats_vtree: Statistics; stats_htree: Statistics; stats_cells: Statistics; vtree_init(tree_copy, *stats_vtree); htree_init(tree_copy, *stats_htree); cells_init(tree_copy, *stats_cells); state_cells: State; { using state_cells; id = .CELLS; camera = cells_fit_zoom(); } state_htree: State; { using state_htree; id = .HYPER_TREE; camera = htree_fit_zoom(); } state_vtree: State; { using state_vtree; id = .TRAD_TREE; camera = vtree_fit_zoom(); } state_init(mode, state_vtree, state_htree, state_cells); gui_init(); InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Template"); defer CloseWindow(); SetTargetFPS(60); push_allocator(temp); while !WindowShouldClose() { frame_time = GetFrameTime(); mouse_screen = GetMousePosition(); mouse_world = GetScreenToWorld2D(mouse_screen, camera); input(); BeginDrawing(); defer EndDrawing(); ClearBackground(COLOR_BACKGROUND); gx: s32; while gx < SCREEN_WIDTH { DrawLine(gx, 0, gx, SCREEN_HEIGHT, { 255, 255, 255, 12 }); gx += 40; } gy: s32; while gy < SCREEN_HEIGHT { DrawLine(0, gy, SCREEN_WIDTH, gy, { 255, 255, 255, 12 }); gy += 40; } { BeginMode2D(camera); defer EndMode2D(); if mode == { case .TRAD_TREE; vtree_draw_2d(); case .HYPER_TREE; htree_draw_2d(); case .CELLS; cells_draw_2d(); } input_draw_selection(); } gui_draw_screen(); if mode == { case .TRAD_TREE; vtree_draw_screen(*stats_vtree); case .HYPER_TREE; htree_draw_screen(*stats_htree); case .CELLS; cells_draw_screen(*stats_cells); } DrawFPS(10, 10); DrawText(TextFormat("Frame Time: % s", frame_time), 10, 40, 20, WHITE); DrawText(TextFormat("Mouse (World): %", mouse_world), 10, 80, 20, WHITE); DrawText(TextFormat("Mouse (Screen): %", mouse_screen), 10, 120, 20, WHITE); reset_temporary_storage(); } } else main :: () { log("Offline"); tree_build(); log("-------------------"); log_vars(tree); //! tree_debug_view(); } tree_build :: () { for rects array_add(*bvs, make_aabb(it)); log_vars(bvs.count-1); array_add(*tree, NODE_INTERNAL, 0, 1); for i: 2..bvs.count-1 { // Find sibling id := find_best_sibling(i); // log_vars(id); // Insert tree_split_node(id, i); //! log_vars(tree); // Refit } } tree_split_node :: (index: int, left: int) { i_left, i_right := tree_get_ids(index); while tree.count <= i_right { array_add(*tree, NODE_EMPTY); } tree[i_left] = left; tree[i_right] = tree[index]; tree[index] = NODE_INTERNAL; //! tree.data[index] -> refit bv } find_best_sibling :: (id: int) -> id: int { candidate := bvs[id]; best_sa := FLOAT32_INFINITY; best_sibling := -1; for tree { if it == NODE_INTERNAL || it == NODE_EMPTY continue; sa := aabb_get_sa(bvs[it], candidate); if sa < best_sa { best_sa = sa; best_sibling = it_index; } } return best_sibling; } tree_access_node :: (index: int) -> Node { node: Node; i_left, i_right, i_parent := tree_get_ids(index); for int.[i_left, i_right] { if it > tree.count return node; } left := tree[i_left]; right := tree[i_right]; node.left = left; node.right = right; node.parent = ifx i_parent < 0 then NODE_INTERNAL else tree[i_parent]; return node; } tree_get_ids :: (index: int) -> (left: int, right: int, parent: int) { i_left := 2 * index + 1; i_right := 2 * index + 2; i_parent := cast(int)floor((index - 1.0) / 2.0); return i_left, i_right, i_parent; } tree_get_parent_from_id :: (id: int) -> int { //! even == right; odd == left lr := ifx (id % 2 == 0) then 2 else 1; index := (id - lr) / 2; return index; } /* TODO: check this! */ //! tree_refit :: (tree: *Tree, index: int) { //! while index > 0 { //! i_left := 2 * index + 1; //! i_right := 2 * index + 2; //! i_parent := cast(int)floor((index - 1.0) / 2.0); //! // Refit this node to enclose both children //! tree.data[index] = aabb_bv_union( //! tree.data[i_left], //! tree.data[i_right] //! ); //! index = i_parent; //! } //! // Refit the root (index 0) separately, since the while loop stops before it //! i_left := 1; //! i_right := 2; //! tree.data[0] = aabb_bv_union( //! tree.data[i_left], //! tree.data[i_right] //! ); //! } tree_debug_view :: () { for tree { node := tree_access_node(it_index); if (node.parent == NODE_EMPTY) then continue; log_vars(it_index, node.parent, node.left, node.right); //! log_vars(it, node.parent, bvs[node.left], bvs[node.right]); } } make_aabb :: (rect: Rectangle) -> Aabb { a: Aabb; a.upper_bound = { rect.x, rect.y }; a.lower_bound = { rect.x + rect.width , rect.y + rect.height }; return a; } aabb_draw :: (a: Aabb) { #if !DRAW_BV return; rect := Rectangle.{ a.upper_bound.x, a.upper_bound.y, a.lower_bound.x - a.upper_bound.x, a.lower_bound.y - a.upper_bound.y }; DrawRectangleLinesEx(rect, 1.0, RED); } aabb_bv_compute_surface_area :: (a: Aabb) -> float { d := a.upper_bound - a.lower_bound; return 2.0 * (d.x * d.y); } aabb_bv_union :: (a: Aabb, b: Aabb) -> Aabb { c: Aabb; c.upper_bound = min(a.upper_bound, b.upper_bound); c.lower_bound = max(a.lower_bound, b.lower_bound); return c; } aabb_get_sa :: (a: Aabb, b: Aabb) -> float { un := aabb_bv_union(a, b); sa := aabb_bv_compute_surface_area(un); return sa; } operator + :: (a: Color, b: Color) -> Color { c: Color = ---; c.r = clamp(a.r + b.r, 0, U8_MAX); c.g = clamp(a.g + b.g, 0, U8_MAX); c.b = clamp(a.b + b.b, 0, U8_MAX); c.a = clamp(a.a + b.a, 0, U8_MAX); return c; } operator - :: (a: Color, b: Color) -> Color { c: Color = ---; c.r = clamp(a.r - b.r, 0, U8_MAX); c.g = clamp(a.g - b.g, 0, U8_MAX); c.b = clamp(a.b - b.b, 0, U8_MAX); c.a = clamp(a.a - b.a, 0, U8_MAX); return c; }