Logo

index : binary-tree

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/things/binary-tree.git/html/src/vtree.jai blob: 12369c64e18d21a02fd4c66da82361bd044296e9 [raw] [clear marker]

        
0
1
2VTree_Node :: struct {
3 index: int;
4 value: int;
5 depth: int;
6
7 // Euclidian
8 x, y: float;
9}
10
11
12vtree_init :: (tree_copy: []int, stat: *Statistics) -> []VTree_Node {
13 tree = tree_copy;
14 assign_positions(0, -(SCREEN_WIDTH / 2), SCREEN_WIDTH - (SCREEN_WIDTH / 2), 0);
15 create_stats(stat);
16 copy := array_copy(nodes);
17 copy.count = node_count;
18 return copy;
19}
20
21vtree_fit_zoom :: () -> Camera2D {
22 min, max: Vector2;
23 min.x = nodes[0].x - NODE_RADIUS;
24 max.x = nodes[0].x + NODE_RADIUS;
25 min.y = nodes[0].y - NODE_RADIUS;
26 max.y = nodes[0].y + NODE_RADIUS;
27
28 for i: 1..node_count-1 {
29 if nodes[i].x - NODE_RADIUS < min.x then min.x = nodes[i].x - NODE_RADIUS;
30 if nodes[i].x + NODE_RADIUS > max.x then max.x = nodes[i].x + NODE_RADIUS;
31 if nodes[i].y - NODE_RADIUS < min.y then min.y = nodes[i].y - NODE_RADIUS;
32 if nodes[i].y + NODE_RADIUS > max.y then max.y = nodes[i].y + NODE_RADIUS;
33 }
34
35 tree_w := max.x - min.x;
36 tree_h := max.y - min.y;
37 zoom_x := (SCREEN_WIDTH * 0.9) / tree_w;
38 zoom_y := (SCREEN_HEIGHT * 0.9) / tree_h;
39 fit_zoom := ifx zoom_x < zoom_y then zoom_x else zoom_y;
40
41 cam := camera;
42 cam.zoom = fit_zoom;
43 cam.offset = { SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0 };
44 cam.target = { (min.x + max.x) / 2.0, (min.y + max.y) / 2.0 };
45
46 camera = cam;
47 return cam;
48}
49
50vtree_draw_2d :: () {
51 for i: 0..node_count-1 {
52 n := nodes[i];
53
54 idx_left := 2 * n.index + 1;
55 idx_right := 2 * n.index + 2;
56
57 found_l, lc := find_node(idx_left);
58 found_r, rc := find_node(idx_right);
59
60 if found_l {
61 DrawLineEx(
62 { n.x, n.y }, { lc.x, lc.y }, 2.0, { 100, 180, 255, 160 }
63 );
64 }
65
66 if found_r {
67 DrawLineEx(
68 { n.x, n.y }, { rc.x, rc.y }, 2.0, { 255, 140, 100, 160 }
69 );
70 }
71
72 node_scale := pow(DEPTH_SCALE_NODE, cast(float)n.depth);
73 font_scale := pow(DEPTH_SCALE_FONT, cast(float)n.depth);
74
75 node_r := NODE_RADIUS * node_scale;
76 font1_sz := max(6.0, FONT1_SIZE * font_scale);
77 font2_sz := max(4.0, FONT2_SIZE * font_scale);
78
79 tn_x := cast(s32, n.x);
80 tn_y := cast(s32, n.y);
81
82 is_root := n.index == 0;
83 is_internal := n.value == NODE_INTERNAL;
84
85 fill: Color;
86 glow: Color;
87 border: Color;
88
89 if is_root {
90 fill = COLOR_NODE_ROOT;
91 } else {
92 if is_internal
93 then fill = COLOR_NODE_INTERNAL;
94 else fill = COLOR_NODE_VALUE;
95 }
96
97 glow = fill + { 40, 40, 40, 0 };
98 glow -= { 0, 0, 0, 190 };
99
100 border = fill + { 80, 80, 80, 0 };
101
102 DrawCircle(tn_x, tn_y, node_r + 6.0, glow);
103
104 if CheckCollisionPointCircle(mouse_world, { n.x, n.y }, node_r) {
105 fill += { 30, 30, 30, 0 };
106 }
107
108 DrawCircle(tn_x, tn_y, xx node_r, fill);
109 DrawCircleLines(tn_x, tn_y, xx node_r, border);
110
111 label: string;
112
113 if is_root {
114 label = "R";
115 }
116 else if is_internal {
117 label = "N";
118 }
119 else {
120 label = tprint("%", n.value);
121 }
122
123 label_c := to_c_string(label);
124
125 tw := MeasureTextEx(GetFontDefault(), label_c, xx font1_sz, 1);
126 DrawText(
127 label_c,
128 xx (tn_x - tw.x / 2),
129 xx (tn_y - tw.y / 2),
130 xx font1_sz,
131 WHITE
132 );
133
134 idx_label := to_c_string(tprint("[%]", n.index));
135
136 sw := MeasureTextEx(GetFontDefault(), idx_label, xx font2_sz, 1);
137 DrawText(
138 idx_label,
139 xx (tn_x - sw.x / 2),
140 xx (tn_y - (node_r * 2)),
141 xx font2_sz,
142 { 180, 180, 180, 180 }
143 );
144 }
145}
146
147vtree_draw_screen :: (stat: *Statistics) {
148 gui_statistics(stat);
149
150 for i: 0..node_count-1 {
151 n := nodes[i];
152 node_scale := pow(DEPTH_SCALE_NODE, cast(float)n.depth);
153 node_r := NODE_RADIUS * node_scale;
154 n_screen := GetWorldToScreen2D({ n.x, n.y }, camera);
155
156 if CheckCollisionPointCircle(mouse_screen, n_screen, node_r * camera.zoom) {
157 text := tooltip_text(n.index, n.value);
158 tooltip(mouse_screen, text);
159 }
160 }
161}
162
163
164#scope_file
165
166
167camera_local: Camera2D;
168tree: []int;
169
170nodes: [NODES_MAX]VTree_Node;
171node_count: int;
172
173
174NODE_RADIUS :: 52;
175FONT1_SIZE :: 69;
176FONT2_SIZE :: 30;
177
178LEVEL_GAP_Y :: 110;
179LEVEL_GAP_X :: 2;
180PADDING :: 60;
181
182DEPTH_SCALE_NODE :: 0.8;
183DEPTH_SCALE_FONT :: 0.75;
184
185
186Range :: struct {
187 left: float;
188 right: float;
189}
190
191
192get_depth :: (idx: int) -> int {
193 d: int;
194 while idx > 0 {
195 idx = (idx - 1) / 2;
196 d += 1;
197 }
198 return d;
199}
200
201subtree_width :: (idx: int) -> int {
202 if idx >= tree.count || tree[idx] == NODE_EMPTY then return 0;
203 return 1 + subtree_width(2 * idx + 1) + subtree_width(2 * idx + 2);
204}
205
206assign_positions :: (idx: int, left: float, right: float, depth: int) {
207 if idx >= tree.count || tree[idx] == NODE_EMPTY return;
208
209 cx := (left + right) / 2.0;
210 cy := cast(float)depth;
211
212 if node_count < NODES_MAX {
213 nodes[node_count].index = idx;
214 nodes[node_count].value = tree[idx];
215 nodes[node_count].x = cx * LEVEL_GAP_X;
216 nodes[node_count].y = cy * LEVEL_GAP_Y;
217 nodes[node_count].depth = depth;
218 node_count += 1;
219 }
220
221 // TODO: Error handling
222 assert(node_count < NODES_MAX, "Too many nodes");
223
224 mid := (left + right) / 2.0;
225 assign_positions(2 * idx + 1, left, mid, depth + 1);
226 assign_positions(2 * idx + 2, mid, right, depth + 1);
227}
228
229find_node :: (idx: int) -> (found: bool, node: VTree_Node) {
230 for nodes {
231 if it.index == idx return true, it;
232 }
233 return false, {};
234}
235
236create_stats :: (stat: *Statistics) {
237 for i: 0..node_count-1 {
238 if nodes[i].value == {
239 case NODE_INTERNAL; stat.nodes += 1;
240 case NODE_EMPTY; stat.empty += 1;
241 case;
242 stat.children += 1;
243 }
244 }
245}
246
247
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit