box2d-ruby 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +336 -0
  4. data/examples/contact_events.rb +22 -0
  5. data/examples/debug_draw.rb +15 -0
  6. data/examples/falling_ball.rb +19 -0
  7. data/examples/rugl_debug_draw.rb +68 -0
  8. data/examples/stagecraft_debug_draw.rb +64 -0
  9. data/examples/support/box2d_debug_lines.rb +125 -0
  10. data/ext/box2d/CMakeLists.txt +35 -0
  11. data/ext/box2d/extconf.rb +42 -0
  12. data/ext/box2d/native.c +9 -0
  13. data/ext/box2d/vendor/box2d/LICENSE +21 -0
  14. data/ext/box2d/vendor/box2d/VERSION +1 -0
  15. data/ext/box2d/vendor/box2d/include/box2d/base.h +131 -0
  16. data/ext/box2d/vendor/box2d/include/box2d/box2d.h +1222 -0
  17. data/ext/box2d/vendor/box2d/include/box2d/collision.h +830 -0
  18. data/ext/box2d/vendor/box2d/include/box2d/id.h +144 -0
  19. data/ext/box2d/vendor/box2d/include/box2d/math_functions.h +761 -0
  20. data/ext/box2d/vendor/box2d/include/box2d/types.h +1457 -0
  21. data/ext/box2d/vendor/box2d/src/CMakeLists.txt +223 -0
  22. data/ext/box2d/vendor/box2d/src/aabb.c +132 -0
  23. data/ext/box2d/vendor/box2d/src/aabb.h +56 -0
  24. data/ext/box2d/vendor/box2d/src/arena_allocator.c +112 -0
  25. data/ext/box2d/vendor/box2d/src/arena_allocator.h +48 -0
  26. data/ext/box2d/vendor/box2d/src/array.c +8 -0
  27. data/ext/box2d/vendor/box2d/src/array.h +179 -0
  28. data/ext/box2d/vendor/box2d/src/atomic.h +79 -0
  29. data/ext/box2d/vendor/box2d/src/bitset.c +67 -0
  30. data/ext/box2d/vendor/box2d/src/bitset.h +65 -0
  31. data/ext/box2d/vendor/box2d/src/body.c +1884 -0
  32. data/ext/box2d/vendor/box2d/src/body.h +194 -0
  33. data/ext/box2d/vendor/box2d/src/box2d.natvis +41 -0
  34. data/ext/box2d/vendor/box2d/src/broad_phase.c +524 -0
  35. data/ext/box2d/vendor/box2d/src/broad_phase.h +83 -0
  36. data/ext/box2d/vendor/box2d/src/constants.h +54 -0
  37. data/ext/box2d/vendor/box2d/src/constraint_graph.c +322 -0
  38. data/ext/box2d/vendor/box2d/src/constraint_graph.h +58 -0
  39. data/ext/box2d/vendor/box2d/src/contact.c +650 -0
  40. data/ext/box2d/vendor/box2d/src/contact.h +148 -0
  41. data/ext/box2d/vendor/box2d/src/contact_solver.c +2120 -0
  42. data/ext/box2d/vendor/box2d/src/contact_solver.h +54 -0
  43. data/ext/box2d/vendor/box2d/src/core.c +178 -0
  44. data/ext/box2d/vendor/box2d/src/core.h +149 -0
  45. data/ext/box2d/vendor/box2d/src/ctz.h +112 -0
  46. data/ext/box2d/vendor/box2d/src/distance.c +1415 -0
  47. data/ext/box2d/vendor/box2d/src/distance_joint.c +556 -0
  48. data/ext/box2d/vendor/box2d/src/dynamic_tree.c +1989 -0
  49. data/ext/box2d/vendor/box2d/src/geometry.c +1028 -0
  50. data/ext/box2d/vendor/box2d/src/hull.c +328 -0
  51. data/ext/box2d/vendor/box2d/src/id_pool.c +79 -0
  52. data/ext/box2d/vendor/box2d/src/id_pool.h +35 -0
  53. data/ext/box2d/vendor/box2d/src/island.c +977 -0
  54. data/ext/box2d/vendor/box2d/src/island.h +89 -0
  55. data/ext/box2d/vendor/box2d/src/joint.c +1272 -0
  56. data/ext/box2d/vendor/box2d/src/joint.h +335 -0
  57. data/ext/box2d/vendor/box2d/src/manifold.c +1726 -0
  58. data/ext/box2d/vendor/box2d/src/math_functions.c +159 -0
  59. data/ext/box2d/vendor/box2d/src/motor_joint.c +283 -0
  60. data/ext/box2d/vendor/box2d/src/mouse_joint.c +214 -0
  61. data/ext/box2d/vendor/box2d/src/mover.c +73 -0
  62. data/ext/box2d/vendor/box2d/src/prismatic_joint.c +656 -0
  63. data/ext/box2d/vendor/box2d/src/revolute_joint.c +534 -0
  64. data/ext/box2d/vendor/box2d/src/sensor.c +389 -0
  65. data/ext/box2d/vendor/box2d/src/sensor.h +36 -0
  66. data/ext/box2d/vendor/box2d/src/shape.c +1714 -0
  67. data/ext/box2d/vendor/box2d/src/shape.h +123 -0
  68. data/ext/box2d/vendor/box2d/src/solver.c +2038 -0
  69. data/ext/box2d/vendor/box2d/src/solver.h +155 -0
  70. data/ext/box2d/vendor/box2d/src/solver_set.c +613 -0
  71. data/ext/box2d/vendor/box2d/src/solver_set.h +57 -0
  72. data/ext/box2d/vendor/box2d/src/table.c +238 -0
  73. data/ext/box2d/vendor/box2d/src/table.h +37 -0
  74. data/ext/box2d/vendor/box2d/src/timer.c +185 -0
  75. data/ext/box2d/vendor/box2d/src/types.c +151 -0
  76. data/ext/box2d/vendor/box2d/src/weld_joint.c +310 -0
  77. data/ext/box2d/vendor/box2d/src/wheel_joint.c +551 -0
  78. data/ext/box2d/vendor/box2d/src/world.c +3301 -0
  79. data/ext/box2d/vendor/box2d/src/world.h +192 -0
  80. data/generator/generate.rb +316 -0
  81. data/generator/layout_probe.c +507 -0
  82. data/generator/verify_layouts.rb +32 -0
  83. data/lib/box2d/body.rb +172 -0
  84. data/lib/box2d/body_definition.rb +38 -0
  85. data/lib/box2d/body_shapes.rb +135 -0
  86. data/lib/box2d/chain.rb +61 -0
  87. data/lib/box2d/debug_draw.rb +118 -0
  88. data/lib/box2d/events.rb +103 -0
  89. data/lib/box2d/fixed_stepper.rb +39 -0
  90. data/lib/box2d/handle.rb +46 -0
  91. data/lib/box2d/hit.rb +5 -0
  92. data/lib/box2d/joint.rb +197 -0
  93. data/lib/box2d/native.rb +1462 -0
  94. data/lib/box2d/native_loader.rb +45 -0
  95. data/lib/box2d/pixel_scale.rb +29 -0
  96. data/lib/box2d/shape.rb +109 -0
  97. data/lib/box2d/shape_definition.rb +44 -0
  98. data/lib/box2d/value_conversion.rb +80 -0
  99. data/lib/box2d/version.rb +7 -0
  100. data/lib/box2d/world.rb +135 -0
  101. data/lib/box2d/world_joints.rb +156 -0
  102. data/lib/box2d/world_queries.rb +122 -0
  103. data/lib/box2d/world_registry.rb +95 -0
  104. data/lib/box2d.rb +31 -0
  105. data/script/build_platform_gem.rb +24 -0
  106. data/script/deterministic_scene.rb +56 -0
  107. data/script/update_deterministic_snapshot.rb +11 -0
  108. data/script/verify_platform_gem.rb +24 -0
  109. metadata +164 -0
@@ -0,0 +1,1989 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "aabb.h"
5
+ #include "constants.h"
6
+ #include "core.h"
7
+
8
+ #include "box2d/collision.h"
9
+ #include "box2d/math_functions.h"
10
+
11
+ #include <float.h>
12
+ #include <string.h>
13
+
14
+ #define B2_TREE_STACK_SIZE 1024
15
+
16
+ // todo externalize this to visualize internal nodes and speed up FindPairs
17
+
18
+ // A node in the dynamic tree.
19
+ typedef struct b2TreeNode
20
+ {
21
+ // The node bounding box
22
+ b2AABB aabb; // 16
23
+
24
+ // Category bits for collision filtering
25
+ uint64_t categoryBits; // 8
26
+
27
+ union
28
+ {
29
+ // Children (internal node)
30
+ struct
31
+ {
32
+ int32_t child1, child2;
33
+ } children;
34
+
35
+ /// User data (leaf node)
36
+ uint64_t userData;
37
+ }; // 8
38
+
39
+ union
40
+ {
41
+ /// The node parent index (allocated node)
42
+ int32_t parent;
43
+
44
+ /// The node freelist next index (free node)
45
+ int32_t next;
46
+ }; // 4
47
+
48
+ uint16_t height; // 2
49
+ uint16_t flags; // 2
50
+ } b2TreeNode;
51
+
52
+ static b2TreeNode b2_defaultTreeNode = {
53
+ .aabb = { { 0.0f, 0.0f }, { 0.0f, 0.0f } },
54
+ .categoryBits = B2_DEFAULT_CATEGORY_BITS,
55
+ .children =
56
+ {
57
+ .child1 = B2_NULL_INDEX,
58
+ .child2 = B2_NULL_INDEX,
59
+ },
60
+ .parent = B2_NULL_INDEX,
61
+ .height = 0,
62
+ .flags = b2_allocatedNode,
63
+ };
64
+
65
+ static bool b2IsLeaf( const b2TreeNode* node )
66
+ {
67
+ return node->flags & b2_leafNode;
68
+ }
69
+
70
+ static bool b2IsAllocated( const b2TreeNode* node )
71
+ {
72
+ return node->flags & b2_allocatedNode;
73
+ }
74
+
75
+ static uint16_t b2MaxUInt16( uint16_t a, uint16_t b )
76
+ {
77
+ return a > b ? a : b;
78
+ }
79
+
80
+ b2DynamicTree b2DynamicTree_Create( void )
81
+ {
82
+ b2DynamicTree tree;
83
+ tree.root = B2_NULL_INDEX;
84
+
85
+ tree.nodeCapacity = 16;
86
+ tree.nodeCount = 0;
87
+ tree.nodes = (b2TreeNode*)b2Alloc( tree.nodeCapacity * sizeof( b2TreeNode ) );
88
+ memset( tree.nodes, 0, tree.nodeCapacity * sizeof( b2TreeNode ) );
89
+
90
+ // Build a linked list for the free list.
91
+ for ( int i = 0; i < tree.nodeCapacity - 1; ++i )
92
+ {
93
+ tree.nodes[i].next = i + 1;
94
+ }
95
+
96
+ tree.nodes[tree.nodeCapacity - 1].next = B2_NULL_INDEX;
97
+ tree.freeList = 0;
98
+
99
+ tree.proxyCount = 0;
100
+
101
+ tree.leafIndices = NULL;
102
+ tree.leafBoxes = NULL;
103
+ tree.leafCenters = NULL;
104
+ tree.binIndices = NULL;
105
+ tree.rebuildCapacity = 0;
106
+
107
+ return tree;
108
+ }
109
+
110
+ void b2DynamicTree_Destroy( b2DynamicTree* tree )
111
+ {
112
+ b2Free( tree->nodes, tree->nodeCapacity * sizeof( b2TreeNode ) );
113
+ b2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int32_t ) );
114
+ b2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) );
115
+ b2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) );
116
+ b2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int32_t ) );
117
+
118
+ memset( tree, 0, sizeof( b2DynamicTree ) );
119
+ }
120
+
121
+ // Allocate a node from the pool. Grow the pool if necessary.
122
+ static int b2AllocateNode( b2DynamicTree* tree )
123
+ {
124
+ // Expand the node pool as needed.
125
+ if ( tree->freeList == B2_NULL_INDEX )
126
+ {
127
+ B2_ASSERT( tree->nodeCount == tree->nodeCapacity );
128
+
129
+ // The free list is empty. Rebuild a bigger pool.
130
+ b2TreeNode* oldNodes = tree->nodes;
131
+ int oldCapacity = tree->nodeCapacity;
132
+ tree->nodeCapacity += oldCapacity >> 1;
133
+ tree->nodes = (b2TreeNode*)b2Alloc( tree->nodeCapacity * sizeof( b2TreeNode ) );
134
+ B2_ASSERT( oldNodes != NULL );
135
+ memcpy( tree->nodes, oldNodes, tree->nodeCount * sizeof( b2TreeNode ) );
136
+ memset( tree->nodes + tree->nodeCount, 0, ( tree->nodeCapacity - tree->nodeCount ) * sizeof( b2TreeNode ) );
137
+ b2Free( oldNodes, oldCapacity * sizeof( b2TreeNode ) );
138
+
139
+ // Build a linked list for the free list. The parent pointer becomes the "next" pointer.
140
+ // todo avoid building freelist?
141
+ for ( int i = tree->nodeCount; i < tree->nodeCapacity - 1; ++i )
142
+ {
143
+ tree->nodes[i].next = i + 1;
144
+ }
145
+
146
+ tree->nodes[tree->nodeCapacity - 1].next = B2_NULL_INDEX;
147
+ tree->freeList = tree->nodeCount;
148
+ }
149
+
150
+ // Peel a node off the free list.
151
+ int nodeIndex = tree->freeList;
152
+ b2TreeNode* node = tree->nodes + nodeIndex;
153
+ tree->freeList = node->next;
154
+ *node = b2_defaultTreeNode;
155
+ ++tree->nodeCount;
156
+ return nodeIndex;
157
+ }
158
+
159
+ // Return a node to the pool.
160
+ static void b2FreeNode( b2DynamicTree* tree, int nodeId )
161
+ {
162
+ B2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );
163
+ B2_ASSERT( 0 < tree->nodeCount );
164
+ tree->nodes[nodeId].next = tree->freeList;
165
+ tree->nodes[nodeId].flags = 0;
166
+ tree->freeList = nodeId;
167
+ --tree->nodeCount;
168
+ }
169
+
170
+ // Greedy algorithm for sibling selection using the SAH
171
+ // We have three nodes A-(B,C) and want to add a leaf D, there are three choices.
172
+ // 1: make a new parent for A and D : E-(A-(B,C), D)
173
+ // 2: associate D with B
174
+ // a: B is a leaf : A-(E-(B,D), C)
175
+ // b: B is an internal node: A-(B{D},C)
176
+ // 3: associate D with C
177
+ // a: C is a leaf : A-(B, E-(C,D))
178
+ // b: C is an internal node: A-(B, C{D})
179
+ // All of these have a clear cost except when B or C is an internal node. Hence we need to be greedy.
180
+
181
+ // The cost for cases 1, 2a, and 3a can be computed using the sibling cost formula.
182
+ // cost of sibling H = area(union(H, D)) + increased area of ancestors
183
+
184
+ // Suppose B (or C) is an internal node, then the lowest cost would be one of two cases:
185
+ // case1: D becomes a sibling of B
186
+ // case2: D becomes a descendant of B along with a new internal node of area(D).
187
+ static int b2FindBestSibling( const b2DynamicTree* tree, b2AABB boxD )
188
+ {
189
+ b2Vec2 centerD = b2AABB_Center( boxD );
190
+ float areaD = b2Perimeter( boxD );
191
+
192
+ const b2TreeNode* nodes = tree->nodes;
193
+ int rootIndex = tree->root;
194
+
195
+ b2AABB rootBox = nodes[rootIndex].aabb;
196
+
197
+ // Area of current node
198
+ float areaBase = b2Perimeter( rootBox );
199
+
200
+ // Area of inflated node
201
+ float directCost = b2Perimeter( b2AABB_Union( rootBox, boxD ) );
202
+ float inheritedCost = 0.0f;
203
+
204
+ int bestSibling = rootIndex;
205
+ float bestCost = directCost;
206
+
207
+ // Descend the tree from root, following a single greedy path.
208
+ int index = rootIndex;
209
+ while ( nodes[index].height > 0 )
210
+ {
211
+ int child1 = nodes[index].children.child1;
212
+ int child2 = nodes[index].children.child2;
213
+
214
+ // Cost of creating a new parent for this node and the new leaf
215
+ float cost = directCost + inheritedCost;
216
+
217
+ // Sometimes there are multiple identical costs within tolerance.
218
+ // This breaks the ties using the centroid distance.
219
+ if ( cost < bestCost )
220
+ {
221
+ bestSibling = index;
222
+ bestCost = cost;
223
+ }
224
+
225
+ // Inheritance cost seen by children
226
+ inheritedCost += directCost - areaBase;
227
+
228
+ bool leaf1 = nodes[child1].height == 0;
229
+ bool leaf2 = nodes[child2].height == 0;
230
+
231
+ // Cost of descending into child 1
232
+ float lowerCost1 = FLT_MAX;
233
+ b2AABB box1 = nodes[child1].aabb;
234
+ float directCost1 = b2Perimeter( b2AABB_Union( box1, boxD ) );
235
+ float area1 = 0.0f;
236
+ if ( leaf1 )
237
+ {
238
+ // Child 1 is a leaf
239
+ // Cost of creating new node and increasing area of node P
240
+ float cost1 = directCost1 + inheritedCost;
241
+
242
+ // Need this here due to while condition above
243
+ if ( cost1 < bestCost )
244
+ {
245
+ bestSibling = child1;
246
+ bestCost = cost1;
247
+ }
248
+ }
249
+ else
250
+ {
251
+ // Child 1 is an internal node
252
+ area1 = b2Perimeter( box1 );
253
+
254
+ // Lower bound cost of inserting under child 1. The minimum accounts for two possibilities:
255
+ // 1. Child1 could be the sibling with cost1 = inheritedCost + directCost1
256
+ // 2. A descendent of child1 could be the sibling with the lower bound cost of
257
+ // cost1 = inheritedCost + (directCost1 - area1) + areaD
258
+ // This minimum here leads to the minimum of these two costs.
259
+ lowerCost1 = inheritedCost + directCost1 + b2MinFloat( areaD - area1, 0.0f );
260
+ }
261
+
262
+ // Cost of descending into child 2
263
+ float lowerCost2 = FLT_MAX;
264
+ b2AABB box2 = nodes[child2].aabb;
265
+ float directCost2 = b2Perimeter( b2AABB_Union( box2, boxD ) );
266
+ float area2 = 0.0f;
267
+ if ( leaf2 )
268
+ {
269
+ float cost2 = directCost2 + inheritedCost;
270
+
271
+ if ( cost2 < bestCost )
272
+ {
273
+ bestSibling = child2;
274
+ bestCost = cost2;
275
+ }
276
+ }
277
+ else
278
+ {
279
+ area2 = b2Perimeter( box2 );
280
+ lowerCost2 = inheritedCost + directCost2 + b2MinFloat( areaD - area2, 0.0f );
281
+ }
282
+
283
+ if ( leaf1 && leaf2 )
284
+ {
285
+ break;
286
+ }
287
+
288
+ // Can the cost possibly be decreased?
289
+ if ( bestCost <= lowerCost1 && bestCost <= lowerCost2 )
290
+ {
291
+ break;
292
+ }
293
+
294
+ if ( lowerCost1 == lowerCost2 && leaf1 == false )
295
+ {
296
+ B2_ASSERT( lowerCost1 < FLT_MAX );
297
+ B2_ASSERT( lowerCost2 < FLT_MAX );
298
+
299
+ // No clear choice based on lower bound surface area. This can happen when both
300
+ // children fully contain D. Fall back to node distance.
301
+ b2Vec2 d1 = b2Sub( b2AABB_Center( box1 ), centerD );
302
+ b2Vec2 d2 = b2Sub( b2AABB_Center( box2 ), centerD );
303
+ lowerCost1 = b2LengthSquared( d1 );
304
+ lowerCost2 = b2LengthSquared( d2 );
305
+ }
306
+
307
+ // Descend
308
+ if ( lowerCost1 < lowerCost2 && leaf1 == false )
309
+ {
310
+ index = child1;
311
+ areaBase = area1;
312
+ directCost = directCost1;
313
+ }
314
+ else
315
+ {
316
+ index = child2;
317
+ areaBase = area2;
318
+ directCost = directCost2;
319
+ }
320
+
321
+ B2_ASSERT( nodes[index].height > 0 );
322
+ }
323
+
324
+ return bestSibling;
325
+ }
326
+
327
+ enum b2RotateType
328
+ {
329
+ b2_rotateNone,
330
+ b2_rotateBF,
331
+ b2_rotateBG,
332
+ b2_rotateCD,
333
+ b2_rotateCE
334
+ };
335
+
336
+ // Perform a left or right rotation if node A is imbalanced.
337
+ // Returns the new root index.
338
+ static void b2RotateNodes( b2DynamicTree* tree, int iA )
339
+ {
340
+ B2_ASSERT( iA != B2_NULL_INDEX );
341
+
342
+ b2TreeNode* nodes = tree->nodes;
343
+
344
+ b2TreeNode* A = nodes + iA;
345
+ if ( A->height < 2 )
346
+ {
347
+ return;
348
+ }
349
+
350
+ int iB = A->children.child1;
351
+ int iC = A->children.child2;
352
+ B2_ASSERT( 0 <= iB && iB < tree->nodeCapacity );
353
+ B2_ASSERT( 0 <= iC && iC < tree->nodeCapacity );
354
+
355
+ b2TreeNode* B = nodes + iB;
356
+ b2TreeNode* C = nodes + iC;
357
+
358
+ if ( B->height == 0 )
359
+ {
360
+ // B is a leaf and C is internal
361
+ B2_ASSERT( C->height > 0 );
362
+
363
+ int iF = C->children.child1;
364
+ int iG = C->children.child2;
365
+ b2TreeNode* F = nodes + iF;
366
+ b2TreeNode* G = nodes + iG;
367
+ B2_ASSERT( 0 <= iF && iF < tree->nodeCapacity );
368
+ B2_ASSERT( 0 <= iG && iG < tree->nodeCapacity );
369
+
370
+ // Base cost
371
+ float costBase = b2Perimeter( C->aabb );
372
+
373
+ // Cost of swapping B and F
374
+ b2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb );
375
+ float costBF = b2Perimeter( aabbBG );
376
+
377
+ // Cost of swapping B and G
378
+ b2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb );
379
+ float costBG = b2Perimeter( aabbBF );
380
+
381
+ if ( costBase < costBF && costBase < costBG )
382
+ {
383
+ // Rotation does not improve cost
384
+ return;
385
+ }
386
+
387
+ if ( costBF < costBG )
388
+ {
389
+ // Swap B and F
390
+ A->children.child1 = iF;
391
+ C->children.child1 = iB;
392
+
393
+ B->parent = iC;
394
+ F->parent = iA;
395
+
396
+ C->aabb = aabbBG;
397
+
398
+ C->height = 1 + b2MaxUInt16( B->height, G->height );
399
+ A->height = 1 + b2MaxUInt16( C->height, F->height );
400
+ C->categoryBits = B->categoryBits | G->categoryBits;
401
+ A->categoryBits = C->categoryBits | F->categoryBits;
402
+ C->flags |= ( B->flags | G->flags ) & b2_enlargedNode;
403
+ A->flags |= ( C->flags | F->flags ) & b2_enlargedNode;
404
+ }
405
+ else
406
+ {
407
+ // Swap B and G
408
+ A->children.child1 = iG;
409
+ C->children.child2 = iB;
410
+
411
+ B->parent = iC;
412
+ G->parent = iA;
413
+
414
+ C->aabb = aabbBF;
415
+
416
+ C->height = 1 + b2MaxUInt16( B->height, F->height );
417
+ A->height = 1 + b2MaxUInt16( C->height, G->height );
418
+ C->categoryBits = B->categoryBits | F->categoryBits;
419
+ A->categoryBits = C->categoryBits | G->categoryBits;
420
+ C->flags |= ( B->flags | F->flags ) & b2_enlargedNode;
421
+ A->flags |= ( C->flags | G->flags ) & b2_enlargedNode;
422
+ }
423
+ }
424
+ else if ( C->height == 0 )
425
+ {
426
+ // C is a leaf and B is internal
427
+ B2_ASSERT( B->height > 0 );
428
+
429
+ int iD = B->children.child1;
430
+ int iE = B->children.child2;
431
+ b2TreeNode* D = nodes + iD;
432
+ b2TreeNode* E = nodes + iE;
433
+ B2_ASSERT( 0 <= iD && iD < tree->nodeCapacity );
434
+ B2_ASSERT( 0 <= iE && iE < tree->nodeCapacity );
435
+
436
+ // Base cost
437
+ float costBase = b2Perimeter( B->aabb );
438
+
439
+ // Cost of swapping C and D
440
+ b2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb );
441
+ float costCD = b2Perimeter( aabbCE );
442
+
443
+ // Cost of swapping C and E
444
+ b2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb );
445
+ float costCE = b2Perimeter( aabbCD );
446
+
447
+ if ( costBase < costCD && costBase < costCE )
448
+ {
449
+ // Rotation does not improve cost
450
+ return;
451
+ }
452
+
453
+ if ( costCD < costCE )
454
+ {
455
+ // Swap C and D
456
+ A->children.child2 = iD;
457
+ B->children.child1 = iC;
458
+
459
+ C->parent = iB;
460
+ D->parent = iA;
461
+
462
+ B->aabb = aabbCE;
463
+
464
+ B->height = 1 + b2MaxUInt16( C->height, E->height );
465
+ A->height = 1 + b2MaxUInt16( B->height, D->height );
466
+ B->categoryBits = C->categoryBits | E->categoryBits;
467
+ A->categoryBits = B->categoryBits | D->categoryBits;
468
+ B->flags |= ( C->flags | E->flags ) & b2_enlargedNode;
469
+ A->flags |= ( B->flags | D->flags ) & b2_enlargedNode;
470
+ }
471
+ else
472
+ {
473
+ // Swap C and E
474
+ A->children.child2 = iE;
475
+ B->children.child2 = iC;
476
+
477
+ C->parent = iB;
478
+ E->parent = iA;
479
+
480
+ B->aabb = aabbCD;
481
+ B->height = 1 + b2MaxUInt16( C->height, D->height );
482
+ A->height = 1 + b2MaxUInt16( B->height, E->height );
483
+ B->categoryBits = C->categoryBits | D->categoryBits;
484
+ A->categoryBits = B->categoryBits | E->categoryBits;
485
+ B->flags |= ( C->flags | D->flags ) & b2_enlargedNode;
486
+ A->flags |= ( B->flags | E->flags ) & b2_enlargedNode;
487
+ }
488
+ }
489
+ else
490
+ {
491
+ int iD = B->children.child1;
492
+ int iE = B->children.child2;
493
+ int iF = C->children.child1;
494
+ int iG = C->children.child2;
495
+
496
+ b2TreeNode* D = nodes + iD;
497
+ b2TreeNode* E = nodes + iE;
498
+ b2TreeNode* F = nodes + iF;
499
+ b2TreeNode* G = nodes + iG;
500
+
501
+ B2_ASSERT( 0 <= iD && iD < tree->nodeCapacity );
502
+ B2_ASSERT( 0 <= iE && iE < tree->nodeCapacity );
503
+ B2_ASSERT( 0 <= iF && iF < tree->nodeCapacity );
504
+ B2_ASSERT( 0 <= iG && iG < tree->nodeCapacity );
505
+
506
+ // Base cost
507
+ float areaB = b2Perimeter( B->aabb );
508
+ float areaC = b2Perimeter( C->aabb );
509
+ float costBase = areaB + areaC;
510
+ enum b2RotateType bestRotation = b2_rotateNone;
511
+ float bestCost = costBase;
512
+
513
+ // Cost of swapping B and F
514
+ b2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb );
515
+ float costBF = areaB + b2Perimeter( aabbBG );
516
+ if ( costBF < bestCost )
517
+ {
518
+ bestRotation = b2_rotateBF;
519
+ bestCost = costBF;
520
+ }
521
+
522
+ // Cost of swapping B and G
523
+ b2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb );
524
+ float costBG = areaB + b2Perimeter( aabbBF );
525
+ if ( costBG < bestCost )
526
+ {
527
+ bestRotation = b2_rotateBG;
528
+ bestCost = costBG;
529
+ }
530
+
531
+ // Cost of swapping C and D
532
+ b2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb );
533
+ float costCD = areaC + b2Perimeter( aabbCE );
534
+ if ( costCD < bestCost )
535
+ {
536
+ bestRotation = b2_rotateCD;
537
+ bestCost = costCD;
538
+ }
539
+
540
+ // Cost of swapping C and E
541
+ b2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb );
542
+ float costCE = areaC + b2Perimeter( aabbCD );
543
+ if ( costCE < bestCost )
544
+ {
545
+ bestRotation = b2_rotateCE;
546
+ // bestCost = costCE;
547
+ }
548
+
549
+ switch ( bestRotation )
550
+ {
551
+ case b2_rotateNone:
552
+ break;
553
+
554
+ case b2_rotateBF:
555
+ A->children.child1 = iF;
556
+ C->children.child1 = iB;
557
+
558
+ B->parent = iC;
559
+ F->parent = iA;
560
+
561
+ C->aabb = aabbBG;
562
+ C->height = 1 + b2MaxUInt16( B->height, G->height );
563
+ A->height = 1 + b2MaxUInt16( C->height, F->height );
564
+ C->categoryBits = B->categoryBits | G->categoryBits;
565
+ A->categoryBits = C->categoryBits | F->categoryBits;
566
+ C->flags |= ( B->flags | G->flags ) & b2_enlargedNode;
567
+ A->flags |= ( C->flags | F->flags ) & b2_enlargedNode;
568
+ break;
569
+
570
+ case b2_rotateBG:
571
+ A->children.child1 = iG;
572
+ C->children.child2 = iB;
573
+
574
+ B->parent = iC;
575
+ G->parent = iA;
576
+
577
+ C->aabb = aabbBF;
578
+ C->height = 1 + b2MaxUInt16( B->height, F->height );
579
+ A->height = 1 + b2MaxUInt16( C->height, G->height );
580
+ C->categoryBits = B->categoryBits | F->categoryBits;
581
+ A->categoryBits = C->categoryBits | G->categoryBits;
582
+ C->flags |= ( B->flags | F->flags ) & b2_enlargedNode;
583
+ A->flags |= ( C->flags | G->flags ) & b2_enlargedNode;
584
+ break;
585
+
586
+ case b2_rotateCD:
587
+ A->children.child2 = iD;
588
+ B->children.child1 = iC;
589
+
590
+ C->parent = iB;
591
+ D->parent = iA;
592
+
593
+ B->aabb = aabbCE;
594
+ B->height = 1 + b2MaxUInt16( C->height, E->height );
595
+ A->height = 1 + b2MaxUInt16( B->height, D->height );
596
+ B->categoryBits = C->categoryBits | E->categoryBits;
597
+ A->categoryBits = B->categoryBits | D->categoryBits;
598
+ B->flags |= ( C->flags | E->flags ) & b2_enlargedNode;
599
+ A->flags |= ( B->flags | D->flags ) & b2_enlargedNode;
600
+ break;
601
+
602
+ case b2_rotateCE:
603
+ A->children.child2 = iE;
604
+ B->children.child2 = iC;
605
+
606
+ C->parent = iB;
607
+ E->parent = iA;
608
+
609
+ B->aabb = aabbCD;
610
+ B->height = 1 + b2MaxUInt16( C->height, D->height );
611
+ A->height = 1 + b2MaxUInt16( B->height, E->height );
612
+ B->categoryBits = C->categoryBits | D->categoryBits;
613
+ A->categoryBits = B->categoryBits | E->categoryBits;
614
+ B->flags |= ( C->flags | D->flags ) & b2_enlargedNode;
615
+ A->flags |= ( B->flags | E->flags ) & b2_enlargedNode;
616
+ break;
617
+
618
+ default:
619
+ B2_ASSERT( false );
620
+ break;
621
+ }
622
+ }
623
+ }
624
+
625
+ static void b2InsertLeaf( b2DynamicTree* tree, int leaf, bool shouldRotate )
626
+ {
627
+ if ( tree->root == B2_NULL_INDEX )
628
+ {
629
+ tree->root = leaf;
630
+ tree->nodes[tree->root].parent = B2_NULL_INDEX;
631
+ return;
632
+ }
633
+
634
+ // Stage 1: find the best sibling for this node
635
+ b2AABB leafAABB = tree->nodes[leaf].aabb;
636
+ int sibling = b2FindBestSibling( tree, leafAABB );
637
+
638
+ // Stage 2: create a new parent for the leaf and sibling
639
+ int oldParent = tree->nodes[sibling].parent;
640
+ int newParent = b2AllocateNode( tree );
641
+
642
+ // warning: node pointer can change after allocation
643
+ b2TreeNode* nodes = tree->nodes;
644
+ nodes[newParent].parent = oldParent;
645
+ nodes[newParent].userData = UINT64_MAX;
646
+ nodes[newParent].aabb = b2AABB_Union( leafAABB, nodes[sibling].aabb );
647
+ nodes[newParent].categoryBits = nodes[leaf].categoryBits | nodes[sibling].categoryBits;
648
+ nodes[newParent].height = nodes[sibling].height + 1;
649
+
650
+ if ( oldParent != B2_NULL_INDEX )
651
+ {
652
+ // The sibling was not the root.
653
+ if ( nodes[oldParent].children.child1 == sibling )
654
+ {
655
+ nodes[oldParent].children.child1 = newParent;
656
+ }
657
+ else
658
+ {
659
+ nodes[oldParent].children.child2 = newParent;
660
+ }
661
+
662
+ nodes[newParent].children.child1 = sibling;
663
+ nodes[newParent].children.child2 = leaf;
664
+ nodes[sibling].parent = newParent;
665
+ nodes[leaf].parent = newParent;
666
+ }
667
+ else
668
+ {
669
+ // The sibling was the root.
670
+ nodes[newParent].children.child1 = sibling;
671
+ nodes[newParent].children.child2 = leaf;
672
+ nodes[sibling].parent = newParent;
673
+ nodes[leaf].parent = newParent;
674
+ tree->root = newParent;
675
+ }
676
+
677
+ // Stage 3: walk back up the tree fixing heights and AABBs
678
+ int index = nodes[leaf].parent;
679
+ while ( index != B2_NULL_INDEX )
680
+ {
681
+ int child1 = nodes[index].children.child1;
682
+ int child2 = nodes[index].children.child2;
683
+
684
+ B2_ASSERT( child1 != B2_NULL_INDEX );
685
+ B2_ASSERT( child2 != B2_NULL_INDEX );
686
+
687
+ nodes[index].aabb = b2AABB_Union( nodes[child1].aabb, nodes[child2].aabb );
688
+ nodes[index].categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;
689
+ nodes[index].height = 1 + b2MaxUInt16( nodes[child1].height, nodes[child2].height );
690
+ nodes[index].flags |= ( nodes[child1].flags | nodes[child2].flags ) & b2_enlargedNode;
691
+
692
+ if ( shouldRotate )
693
+ {
694
+ b2RotateNodes( tree, index );
695
+ }
696
+
697
+ index = nodes[index].parent;
698
+ }
699
+ }
700
+
701
+ static void b2RemoveLeaf( b2DynamicTree* tree, int leaf )
702
+ {
703
+ if ( leaf == tree->root )
704
+ {
705
+ tree->root = B2_NULL_INDEX;
706
+ return;
707
+ }
708
+
709
+ b2TreeNode* nodes = tree->nodes;
710
+
711
+ int parent = nodes[leaf].parent;
712
+ int grandParent = nodes[parent].parent;
713
+ int sibling;
714
+ if ( nodes[parent].children.child1 == leaf )
715
+ {
716
+ sibling = nodes[parent].children.child2;
717
+ }
718
+ else
719
+ {
720
+ sibling = nodes[parent].children.child1;
721
+ }
722
+
723
+ if ( grandParent != B2_NULL_INDEX )
724
+ {
725
+ // Destroy parent and connect sibling to grandParent.
726
+ if ( nodes[grandParent].children.child1 == parent )
727
+ {
728
+ nodes[grandParent].children.child1 = sibling;
729
+ }
730
+ else
731
+ {
732
+ nodes[grandParent].children.child2 = sibling;
733
+ }
734
+ nodes[sibling].parent = grandParent;
735
+ b2FreeNode( tree, parent );
736
+
737
+ // Adjust ancestor bounds.
738
+ int index = grandParent;
739
+ while ( index != B2_NULL_INDEX )
740
+ {
741
+ b2TreeNode* node = nodes + index;
742
+ b2TreeNode* child1 = nodes + node->children.child1;
743
+ b2TreeNode* child2 = nodes + node->children.child2;
744
+
745
+ // Fast union using SSE
746
+ //__m128 aabb1 = _mm_load_ps(&child1->aabb.lowerBound.x);
747
+ //__m128 aabb2 = _mm_load_ps(&child2->aabb.lowerBound.x);
748
+ //__m128 lower = _mm_min_ps(aabb1, aabb2);
749
+ //__m128 upper = _mm_max_ps(aabb1, aabb2);
750
+ //__m128 aabb = _mm_shuffle_ps(lower, upper, _MM_SHUFFLE(3, 2, 1, 0));
751
+ //_mm_store_ps(&node->aabb.lowerBound.x, aabb);
752
+
753
+ node->aabb = b2AABB_Union( child1->aabb, child2->aabb );
754
+ node->categoryBits = child1->categoryBits | child2->categoryBits;
755
+ node->height = 1 + b2MaxUInt16( child1->height, child2->height );
756
+
757
+ index = node->parent;
758
+ }
759
+ }
760
+ else
761
+ {
762
+ tree->root = sibling;
763
+ tree->nodes[sibling].parent = B2_NULL_INDEX;
764
+ b2FreeNode( tree, parent );
765
+ }
766
+ }
767
+
768
+ // Create a proxy in the tree as a leaf node. We return the index of the node instead of a pointer so that we can grow
769
+ // the node pool.
770
+ int b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, uint64_t userData )
771
+ {
772
+ B2_ASSERT( -B2_HUGE < aabb.lowerBound.x && aabb.lowerBound.x < B2_HUGE );
773
+ B2_ASSERT( -B2_HUGE < aabb.lowerBound.y && aabb.lowerBound.y < B2_HUGE );
774
+ B2_ASSERT( -B2_HUGE < aabb.upperBound.x && aabb.upperBound.x < B2_HUGE );
775
+ B2_ASSERT( -B2_HUGE < aabb.upperBound.y && aabb.upperBound.y < B2_HUGE );
776
+
777
+ int proxyId = b2AllocateNode( tree );
778
+ b2TreeNode* node = tree->nodes + proxyId;
779
+
780
+ node->aabb = aabb;
781
+ node->userData = userData;
782
+ node->categoryBits = categoryBits;
783
+ node->height = 0;
784
+ node->flags = b2_allocatedNode | b2_leafNode;
785
+
786
+ bool shouldRotate = true;
787
+ b2InsertLeaf( tree, proxyId, shouldRotate );
788
+
789
+ tree->proxyCount += 1;
790
+
791
+ return proxyId;
792
+ }
793
+
794
+ void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int proxyId )
795
+ {
796
+ B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
797
+ B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );
798
+
799
+ b2RemoveLeaf( tree, proxyId );
800
+ b2FreeNode( tree, proxyId );
801
+
802
+ B2_ASSERT( tree->proxyCount > 0 );
803
+ tree->proxyCount -= 1;
804
+ }
805
+
806
+ int b2DynamicTree_GetProxyCount( const b2DynamicTree* tree )
807
+ {
808
+ return tree->proxyCount;
809
+ }
810
+
811
+ void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb )
812
+ {
813
+ B2_ASSERT( b2IsValidAABB( aabb ) );
814
+ B2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < B2_HUGE );
815
+ B2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < B2_HUGE );
816
+ B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
817
+ B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );
818
+
819
+ b2RemoveLeaf( tree, proxyId );
820
+
821
+ tree->nodes[proxyId].aabb = aabb;
822
+
823
+ bool shouldRotate = false;
824
+ b2InsertLeaf( tree, proxyId, shouldRotate );
825
+ }
826
+
827
+ void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb )
828
+ {
829
+ b2TreeNode* nodes = tree->nodes;
830
+
831
+ B2_ASSERT( b2IsValidAABB( aabb ) );
832
+ B2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < B2_HUGE );
833
+ B2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < B2_HUGE );
834
+ B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
835
+ B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) );
836
+
837
+ // Caller must ensure this
838
+ B2_ASSERT( b2AABB_Contains( nodes[proxyId].aabb, aabb ) == false );
839
+
840
+ nodes[proxyId].aabb = aabb;
841
+
842
+ int parentIndex = nodes[proxyId].parent;
843
+ while (parentIndex != B2_NULL_INDEX)
844
+ {
845
+ bool changed = b2EnlargeAABB( &nodes[parentIndex].aabb, aabb );
846
+ nodes[parentIndex].flags |= b2_enlargedNode;
847
+ parentIndex = nodes[parentIndex].parent;
848
+
849
+ if (changed == false)
850
+ {
851
+ break;
852
+ }
853
+ }
854
+
855
+ while (parentIndex != B2_NULL_INDEX)
856
+ {
857
+ if (nodes[parentIndex].flags & b2_enlargedNode)
858
+ {
859
+ // early out because this ancestor was previously ascended and marked as enlarged
860
+ break;
861
+ }
862
+
863
+ nodes[parentIndex].flags |= b2_enlargedNode;
864
+ parentIndex = nodes[parentIndex].parent;
865
+ }
866
+ }
867
+
868
+ void b2DynamicTree_SetCategoryBits( b2DynamicTree* tree, int proxyId, uint64_t categoryBits )
869
+ {
870
+ b2TreeNode* nodes = tree->nodes;
871
+
872
+ B2_ASSERT( nodes[proxyId].children.child1 == B2_NULL_INDEX );
873
+ B2_ASSERT( nodes[proxyId].children.child2 == B2_NULL_INDEX );
874
+ B2_ASSERT( (nodes[proxyId].flags & b2_leafNode) == b2_leafNode );
875
+
876
+ nodes[proxyId].categoryBits = categoryBits;
877
+
878
+ // Fix up category bits in ancestor internal nodes
879
+ int nodeIndex = nodes[proxyId].parent;
880
+ while ( nodeIndex != B2_NULL_INDEX )
881
+ {
882
+ b2TreeNode* node = nodes + nodeIndex;
883
+ int child1 = node->children.child1;
884
+ B2_ASSERT( child1 != B2_NULL_INDEX );
885
+ int child2 = node->children.child2;
886
+ B2_ASSERT( child2 != B2_NULL_INDEX );
887
+ node->categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits;
888
+
889
+ nodeIndex = node->parent;
890
+ }
891
+ }
892
+
893
+ uint64_t b2DynamicTree_GetCategoryBits( b2DynamicTree* tree, int proxyId )
894
+ {
895
+ B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
896
+ return tree->nodes[proxyId].categoryBits;
897
+ }
898
+
899
+ int b2DynamicTree_GetHeight( const b2DynamicTree* tree )
900
+ {
901
+ if ( tree->root == B2_NULL_INDEX )
902
+ {
903
+ return 0;
904
+ }
905
+
906
+ return tree->nodes[tree->root].height;
907
+ }
908
+
909
+ float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree )
910
+ {
911
+ if ( tree->root == B2_NULL_INDEX )
912
+ {
913
+ return 0.0f;
914
+ }
915
+
916
+ const b2TreeNode* root = tree->nodes + tree->root;
917
+ float rootArea = b2Perimeter( root->aabb );
918
+
919
+ float totalArea = 0.0f;
920
+ for ( int i = 0; i < tree->nodeCapacity; ++i )
921
+ {
922
+ const b2TreeNode* node = tree->nodes + i;
923
+ if ( b2IsAllocated(node) == false || b2IsLeaf( node ) || i == tree->root )
924
+ {
925
+ continue;
926
+ }
927
+
928
+ totalArea += b2Perimeter( node->aabb );
929
+ }
930
+
931
+ return totalArea / rootArea;
932
+ }
933
+
934
+ b2AABB b2DynamicTree_GetRootBounds( const b2DynamicTree* tree )
935
+ {
936
+ if (tree->root != B2_NULL_INDEX)
937
+ {
938
+ return tree->nodes[tree->root].aabb;
939
+ }
940
+
941
+ b2AABB empty = { b2Vec2_zero, b2Vec2_zero };
942
+ return empty;
943
+ }
944
+
945
+ #if B2_VALIDATE
946
+ // Compute the height of a sub-tree.
947
+ static int b2ComputeHeight( const b2DynamicTree* tree, int nodeId )
948
+ {
949
+ B2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity );
950
+ b2TreeNode* node = tree->nodes + nodeId;
951
+
952
+ if ( b2IsLeaf( node ) )
953
+ {
954
+ return 0;
955
+ }
956
+
957
+ int height1 = b2ComputeHeight( tree, node->children.child1 );
958
+ int height2 = b2ComputeHeight( tree, node->children.child2 );
959
+ return 1 + b2MaxInt( height1, height2 );
960
+ }
961
+
962
+ static void b2ValidateStructure( const b2DynamicTree* tree, int index )
963
+ {
964
+ if ( index == B2_NULL_INDEX )
965
+ {
966
+ return;
967
+ }
968
+
969
+ if ( index == tree->root )
970
+ {
971
+ B2_ASSERT( tree->nodes[index].parent == B2_NULL_INDEX );
972
+ }
973
+
974
+ const b2TreeNode* node = tree->nodes + index;
975
+
976
+ B2_ASSERT( node->flags == 0 || ( node->flags & b2_allocatedNode ) != 0 );
977
+
978
+ if ( b2IsLeaf( node ) )
979
+ {
980
+ B2_ASSERT( node->height == 0 );
981
+ return;
982
+ }
983
+
984
+ int child1 = node->children.child1;
985
+ int child2 = node->children.child2;
986
+
987
+ B2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );
988
+ B2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );
989
+
990
+ B2_ASSERT( tree->nodes[child1].parent == index );
991
+ B2_ASSERT( tree->nodes[child2].parent == index );
992
+
993
+ if ( ( tree->nodes[child1].flags | tree->nodes[child2].flags ) & b2_enlargedNode )
994
+ {
995
+ B2_ASSERT( node->flags & b2_enlargedNode );
996
+ }
997
+
998
+ b2ValidateStructure( tree, child1 );
999
+ b2ValidateStructure( tree, child2 );
1000
+ }
1001
+
1002
+ static void b2ValidateMetrics( const b2DynamicTree* tree, int index )
1003
+ {
1004
+ if ( index == B2_NULL_INDEX )
1005
+ {
1006
+ return;
1007
+ }
1008
+
1009
+ const b2TreeNode* node = tree->nodes + index;
1010
+
1011
+ if ( b2IsLeaf( node ) )
1012
+ {
1013
+ B2_ASSERT( node->height == 0 );
1014
+ return;
1015
+ }
1016
+
1017
+ int child1 = node->children.child1;
1018
+ int child2 = node->children.child2;
1019
+
1020
+ B2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity );
1021
+ B2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity );
1022
+
1023
+ int height1 = tree->nodes[child1].height;
1024
+ int height2 = tree->nodes[child2].height;
1025
+ int height = 1 + b2MaxInt( height1, height2 );
1026
+ B2_ASSERT( node->height == height );
1027
+
1028
+ // b2AABB aabb = b2AABB_Union(tree->nodes[child1].aabb, tree->nodes[child2].aabb);
1029
+
1030
+ B2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child1].aabb ) );
1031
+ B2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child2].aabb ) );
1032
+
1033
+ // B2_ASSERT(aabb.lowerBound.x == node->aabb.lowerBound.x);
1034
+ // B2_ASSERT(aabb.lowerBound.y == node->aabb.lowerBound.y);
1035
+ // B2_ASSERT(aabb.upperBound.x == node->aabb.upperBound.x);
1036
+ // B2_ASSERT(aabb.upperBound.y == node->aabb.upperBound.y);
1037
+
1038
+ uint64_t categoryBits = tree->nodes[child1].categoryBits | tree->nodes[child2].categoryBits;
1039
+ B2_ASSERT( node->categoryBits == categoryBits );
1040
+
1041
+ b2ValidateMetrics( tree, child1 );
1042
+ b2ValidateMetrics( tree, child2 );
1043
+ }
1044
+ #endif
1045
+
1046
+ void b2DynamicTree_Validate( const b2DynamicTree* tree )
1047
+ {
1048
+ #if B2_VALIDATE
1049
+ if ( tree->root == B2_NULL_INDEX )
1050
+ {
1051
+ return;
1052
+ }
1053
+
1054
+ b2ValidateStructure( tree, tree->root );
1055
+ b2ValidateMetrics( tree, tree->root );
1056
+
1057
+ int freeCount = 0;
1058
+ int freeIndex = tree->freeList;
1059
+ while ( freeIndex != B2_NULL_INDEX )
1060
+ {
1061
+ B2_ASSERT( 0 <= freeIndex && freeIndex < tree->nodeCapacity );
1062
+ freeIndex = tree->nodes[freeIndex].next;
1063
+ ++freeCount;
1064
+ }
1065
+
1066
+ int height = b2DynamicTree_GetHeight( tree );
1067
+ int computedHeight = b2ComputeHeight( tree, tree->root );
1068
+ B2_ASSERT( height == computedHeight );
1069
+
1070
+ B2_ASSERT( tree->nodeCount + freeCount == tree->nodeCapacity );
1071
+ #else
1072
+ B2_UNUSED( tree );
1073
+ #endif
1074
+ }
1075
+
1076
+ void b2DynamicTree_ValidateNoEnlarged(const b2DynamicTree* tree)
1077
+ {
1078
+ #if B2_VALIDATE == 1
1079
+ int capacity = tree->nodeCapacity;
1080
+ const b2TreeNode* nodes = tree->nodes;
1081
+ for ( int i = 0; i < capacity; ++i )
1082
+ {
1083
+ const b2TreeNode* node = nodes + i;
1084
+ if ( node->flags & b2_allocatedNode )
1085
+ {
1086
+ B2_ASSERT( ( node->flags & b2_enlargedNode ) == 0 );
1087
+ }
1088
+ }
1089
+ #else
1090
+ B2_UNUSED( tree );
1091
+ #endif
1092
+ }
1093
+
1094
+ int b2DynamicTree_GetByteCount( const b2DynamicTree* tree )
1095
+ {
1096
+ size_t size = sizeof( b2DynamicTree ) + sizeof( b2TreeNode ) * tree->nodeCapacity +
1097
+ tree->rebuildCapacity * ( sizeof( int ) + sizeof( b2AABB ) + sizeof( b2Vec2 ) + sizeof( int ) );
1098
+
1099
+ return (int)size;
1100
+ }
1101
+
1102
+ uint64_t b2DynamicTree_GetUserData( const b2DynamicTree* tree, int proxyId )
1103
+ {
1104
+ B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
1105
+ return tree->nodes[proxyId].userData;
1106
+ }
1107
+
1108
+ b2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int proxyId )
1109
+ {
1110
+ B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity );
1111
+ return tree->nodes[proxyId].aabb;
1112
+ }
1113
+
1114
+ b2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, b2TreeQueryCallbackFcn* callback,
1115
+ void* context )
1116
+ {
1117
+ b2TreeStats result = { 0 };
1118
+
1119
+ if ( tree->nodeCount == 0 )
1120
+ {
1121
+ return result;
1122
+ }
1123
+
1124
+ int stack[B2_TREE_STACK_SIZE];
1125
+ int stackCount = 0;
1126
+ stack[stackCount++] = tree->root;
1127
+
1128
+ while ( stackCount > 0 )
1129
+ {
1130
+ int nodeId = stack[--stackCount];
1131
+ if ( nodeId == B2_NULL_INDEX )
1132
+ {
1133
+ // todo huh?
1134
+ B2_ASSERT( false );
1135
+ continue;
1136
+ }
1137
+
1138
+ const b2TreeNode* node = tree->nodes + nodeId;
1139
+ result.nodeVisits += 1;
1140
+
1141
+ if ( b2AABB_Overlaps( node->aabb, aabb ) && ( node->categoryBits & maskBits ) != 0 )
1142
+ {
1143
+ if ( b2IsLeaf( node ) )
1144
+ {
1145
+ // callback to user code with proxy id
1146
+ bool proceed = callback( nodeId, node->userData, context );
1147
+ result.leafVisits += 1;
1148
+
1149
+ if ( proceed == false )
1150
+ {
1151
+ return result;
1152
+ }
1153
+ }
1154
+ else
1155
+ {
1156
+ if ( stackCount < B2_TREE_STACK_SIZE - 1 )
1157
+ {
1158
+ stack[stackCount++] = node->children.child1;
1159
+ stack[stackCount++] = node->children.child2;
1160
+ }
1161
+ else
1162
+ {
1163
+ B2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ return result;
1170
+ }
1171
+
1172
+ b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits,
1173
+ b2TreeRayCastCallbackFcn* callback, void* context )
1174
+ {
1175
+ b2TreeStats result = { 0 };
1176
+
1177
+ if ( tree->nodeCount == 0 )
1178
+ {
1179
+ return result;
1180
+ }
1181
+
1182
+ b2Vec2 p1 = input->origin;
1183
+ b2Vec2 d = input->translation;
1184
+
1185
+ b2Vec2 r = b2Normalize( d );
1186
+
1187
+ // v is perpendicular to the segment.
1188
+ b2Vec2 v = b2CrossSV( 1.0f, r );
1189
+ b2Vec2 abs_v = b2Abs( v );
1190
+
1191
+ // Separating axis for segment (Gino, p80).
1192
+ // |dot(v, p1 - c)| > dot(|v|, h)
1193
+
1194
+ float maxFraction = input->maxFraction;
1195
+
1196
+ b2Vec2 p2 = b2MulAdd( p1, maxFraction, d );
1197
+
1198
+ // Build a bounding box for the segment.
1199
+ b2AABB segmentAABB = { b2Min( p1, p2 ), b2Max( p1, p2 ) };
1200
+
1201
+ int stack[B2_TREE_STACK_SIZE];
1202
+ int stackCount = 0;
1203
+ stack[stackCount++] = tree->root;
1204
+
1205
+ const b2TreeNode* nodes = tree->nodes;
1206
+
1207
+ b2RayCastInput subInput = *input;
1208
+
1209
+ while ( stackCount > 0 )
1210
+ {
1211
+ int nodeId = stack[--stackCount];
1212
+ if ( nodeId == B2_NULL_INDEX )
1213
+ {
1214
+ // todo is this possible?
1215
+ B2_ASSERT( false );
1216
+ continue;
1217
+ }
1218
+
1219
+ const b2TreeNode* node = nodes + nodeId;
1220
+ result.nodeVisits += 1;
1221
+
1222
+ b2AABB nodeAABB = node->aabb;
1223
+
1224
+ if ( ( node->categoryBits & maskBits ) == 0 || b2AABB_Overlaps( nodeAABB, segmentAABB ) == false )
1225
+ {
1226
+ continue;
1227
+ }
1228
+
1229
+ // Separating axis for segment (Gino, p80).
1230
+ // |dot(v, p1 - c)| > dot(|v|, h)
1231
+ // radius extension is added to the node in this case
1232
+ b2Vec2 c = b2AABB_Center( nodeAABB );
1233
+ b2Vec2 h = b2AABB_Extents( nodeAABB );
1234
+ float term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) );
1235
+ float term2 = b2Dot( abs_v, h );
1236
+ if ( term2 < term1 )
1237
+ {
1238
+ continue;
1239
+ }
1240
+
1241
+ if ( b2IsLeaf( node ) )
1242
+ {
1243
+ subInput.maxFraction = maxFraction;
1244
+
1245
+ float value = callback( &subInput, nodeId, node->userData, context );
1246
+ result.leafVisits += 1;
1247
+
1248
+ // The user may return -1 to indicate this shape should be skipped
1249
+
1250
+ if ( value == 0.0f )
1251
+ {
1252
+ // The client has terminated the ray cast.
1253
+ return result;
1254
+ }
1255
+
1256
+ if ( 0.0f < value && value <= maxFraction )
1257
+ {
1258
+ // Update segment bounding box.
1259
+ maxFraction = value;
1260
+ p2 = b2MulAdd( p1, maxFraction, d );
1261
+ segmentAABB.lowerBound = b2Min( p1, p2 );
1262
+ segmentAABB.upperBound = b2Max( p1, p2 );
1263
+ }
1264
+ }
1265
+ else
1266
+ {
1267
+ if ( stackCount < B2_TREE_STACK_SIZE - 1 )
1268
+ {
1269
+ b2Vec2 c1 = b2AABB_Center( nodes[node->children.child1].aabb );
1270
+ b2Vec2 c2 = b2AABB_Center( nodes[node->children.child2].aabb );
1271
+ if ( b2DistanceSquared( c1, p1 ) < b2DistanceSquared( c2, p1 ) )
1272
+ {
1273
+ stack[stackCount++] = node->children.child2;
1274
+ stack[stackCount++] = node->children.child1;
1275
+ }
1276
+ else
1277
+ {
1278
+ stack[stackCount++] = node->children.child1;
1279
+ stack[stackCount++] = node->children.child2;
1280
+ }
1281
+ }
1282
+ else
1283
+ {
1284
+ B2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );
1285
+ }
1286
+ }
1287
+ }
1288
+
1289
+ return result;
1290
+ }
1291
+
1292
+ b2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits,
1293
+ b2TreeShapeCastCallbackFcn* callback, void* context )
1294
+ {
1295
+ b2TreeStats stats = { 0 };
1296
+
1297
+ if ( tree->nodeCount == 0 || input->proxy.count == 0 )
1298
+ {
1299
+ return stats;
1300
+ }
1301
+
1302
+ b2AABB originAABB = { input->proxy.points[0], input->proxy.points[0] };
1303
+ for ( int i = 1; i < input->proxy.count; ++i )
1304
+ {
1305
+ originAABB.lowerBound = b2Min( originAABB.lowerBound, input->proxy.points[i] );
1306
+ originAABB.upperBound = b2Max( originAABB.upperBound, input->proxy.points[i] );
1307
+ }
1308
+
1309
+ b2Vec2 radius = { input->proxy.radius, input->proxy.radius };
1310
+
1311
+ originAABB.lowerBound = b2Sub( originAABB.lowerBound, radius );
1312
+ originAABB.upperBound = b2Add( originAABB.upperBound, radius );
1313
+
1314
+ b2Vec2 p1 = b2AABB_Center( originAABB );
1315
+ b2Vec2 extension = b2AABB_Extents( originAABB );
1316
+
1317
+ // v is perpendicular to the segment.
1318
+ b2Vec2 r = input->translation;
1319
+ b2Vec2 v = b2CrossSV( 1.0f, r );
1320
+ b2Vec2 abs_v = b2Abs( v );
1321
+
1322
+ // Separating axis for segment (Gino, p80).
1323
+ // |dot(v, p1 - c)| > dot(|v|, h)
1324
+
1325
+ float maxFraction = input->maxFraction;
1326
+
1327
+ // Build total box for the shape cast
1328
+ b2Vec2 t = b2MulSV( maxFraction, input->translation );
1329
+ b2AABB totalAABB = {
1330
+ b2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) ),
1331
+ b2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) ),
1332
+ };
1333
+
1334
+ b2ShapeCastInput subInput = *input;
1335
+ const b2TreeNode* nodes = tree->nodes;
1336
+
1337
+ int stack[B2_TREE_STACK_SIZE];
1338
+ int stackCount = 0;
1339
+ stack[stackCount++] = tree->root;
1340
+
1341
+ while ( stackCount > 0 )
1342
+ {
1343
+ int nodeId = stack[--stackCount];
1344
+ if ( nodeId == B2_NULL_INDEX )
1345
+ {
1346
+ // todo is this possible?
1347
+ B2_ASSERT( false );
1348
+ continue;
1349
+ }
1350
+
1351
+ const b2TreeNode* node = nodes + nodeId;
1352
+ stats.nodeVisits += 1;
1353
+
1354
+ if ( ( node->categoryBits & maskBits ) == 0 || b2AABB_Overlaps( node->aabb, totalAABB ) == false )
1355
+ {
1356
+ continue;
1357
+ }
1358
+
1359
+ // Separating axis for segment (Gino, p80).
1360
+ // |dot(v, p1 - c)| > dot(|v|, h)
1361
+ // radius extension is added to the node in this case
1362
+ b2Vec2 c = b2AABB_Center( node->aabb );
1363
+ b2Vec2 h = b2Add( b2AABB_Extents( node->aabb ), extension );
1364
+ float term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) );
1365
+ float term2 = b2Dot( abs_v, h );
1366
+ if ( term2 < term1 )
1367
+ {
1368
+ continue;
1369
+ }
1370
+
1371
+ if ( b2IsLeaf( node ) )
1372
+ {
1373
+ subInput.maxFraction = maxFraction;
1374
+
1375
+ float value = callback( &subInput, nodeId, node->userData, context );
1376
+ stats.leafVisits += 1;
1377
+
1378
+ if ( value == 0.0f )
1379
+ {
1380
+ // The client has terminated the ray cast.
1381
+ return stats;
1382
+ }
1383
+
1384
+ if ( 0.0f < value && value < maxFraction )
1385
+ {
1386
+ // Update segment bounding box.
1387
+ maxFraction = value;
1388
+ t = b2MulSV( maxFraction, input->translation );
1389
+ totalAABB.lowerBound = b2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) );
1390
+ totalAABB.upperBound = b2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) );
1391
+ }
1392
+ }
1393
+ else
1394
+ {
1395
+ if ( stackCount < B2_TREE_STACK_SIZE - 1 )
1396
+ {
1397
+ b2Vec2 c1 = b2AABB_Center( nodes[node->children.child1].aabb );
1398
+ b2Vec2 c2 = b2AABB_Center( nodes[node->children.child2].aabb );
1399
+ if ( b2DistanceSquared( c1, p1 ) < b2DistanceSquared( c2, p1 ) )
1400
+ {
1401
+ stack[stackCount++] = node->children.child2;
1402
+ stack[stackCount++] = node->children.child1;
1403
+ }
1404
+ else
1405
+ {
1406
+ stack[stackCount++] = node->children.child1;
1407
+ stack[stackCount++] = node->children.child2;
1408
+ }
1409
+ }
1410
+ else
1411
+ {
1412
+ B2_ASSERT( stackCount < B2_TREE_STACK_SIZE - 1 );
1413
+ }
1414
+ }
1415
+ }
1416
+
1417
+ return stats;
1418
+ }
1419
+
1420
+ // Median split == 0, Surface area heuristic == 1
1421
+ #define B2_TREE_HEURISTIC 0
1422
+
1423
+ #if B2_TREE_HEURISTIC == 0
1424
+
1425
+ // Median split heuristic
1426
+ static int b2PartitionMid( int* indices, b2Vec2* centers, int count )
1427
+ {
1428
+ // Handle trivial case
1429
+ if ( count <= 2 )
1430
+ {
1431
+ return count / 2;
1432
+ }
1433
+
1434
+ b2Vec2 lowerBound = centers[0];
1435
+ b2Vec2 upperBound = centers[0];
1436
+
1437
+ for ( int i = 1; i < count; ++i )
1438
+ {
1439
+ lowerBound = b2Min( lowerBound, centers[i] );
1440
+ upperBound = b2Max( upperBound, centers[i] );
1441
+ }
1442
+
1443
+ b2Vec2 d = b2Sub( upperBound, lowerBound );
1444
+ b2Vec2 c = { 0.5f * ( lowerBound.x + upperBound.x ), 0.5f * ( lowerBound.y + upperBound.y ) };
1445
+
1446
+ // Partition longest axis using the Hoare partition scheme
1447
+ // https://en.wikipedia.org/wiki/Quicksort
1448
+ // https://nicholasvadivelu.com/2021/01/11/array-partition/
1449
+ int i1 = 0, i2 = count;
1450
+ if ( d.x > d.y )
1451
+ {
1452
+ float pivot = c.x;
1453
+
1454
+ while ( i1 < i2 )
1455
+ {
1456
+ while ( i1 < i2 && centers[i1].x < pivot )
1457
+ {
1458
+ i1 += 1;
1459
+ };
1460
+
1461
+ while ( i1 < i2 && centers[i2 - 1].x >= pivot )
1462
+ {
1463
+ i2 -= 1;
1464
+ };
1465
+
1466
+ if ( i1 < i2 )
1467
+ {
1468
+ // Swap indices
1469
+ {
1470
+ int temp = indices[i1];
1471
+ indices[i1] = indices[i2 - 1];
1472
+ indices[i2 - 1] = temp;
1473
+ }
1474
+
1475
+ // Swap centers
1476
+ {
1477
+ b2Vec2 temp = centers[i1];
1478
+ centers[i1] = centers[i2 - 1];
1479
+ centers[i2 - 1] = temp;
1480
+ }
1481
+
1482
+ i1 += 1;
1483
+ i2 -= 1;
1484
+ }
1485
+ }
1486
+ }
1487
+ else
1488
+ {
1489
+ float pivot = c.y;
1490
+
1491
+ while ( i1 < i2 )
1492
+ {
1493
+ while ( i1 < i2 && centers[i1].y < pivot )
1494
+ {
1495
+ i1 += 1;
1496
+ };
1497
+
1498
+ while ( i1 < i2 && centers[i2 - 1].y >= pivot )
1499
+ {
1500
+ i2 -= 1;
1501
+ };
1502
+
1503
+ if ( i1 < i2 )
1504
+ {
1505
+ // Swap indices
1506
+ {
1507
+ int temp = indices[i1];
1508
+ indices[i1] = indices[i2 - 1];
1509
+ indices[i2 - 1] = temp;
1510
+ }
1511
+
1512
+ // Swap centers
1513
+ {
1514
+ b2Vec2 temp = centers[i1];
1515
+ centers[i1] = centers[i2 - 1];
1516
+ centers[i2 - 1] = temp;
1517
+ }
1518
+
1519
+ i1 += 1;
1520
+ i2 -= 1;
1521
+ }
1522
+ }
1523
+ }
1524
+ B2_ASSERT( i1 == i2 );
1525
+
1526
+ if ( i1 > 0 && i1 < count )
1527
+ {
1528
+ return i1;
1529
+ }
1530
+
1531
+ return count / 2;
1532
+ }
1533
+
1534
+ #else
1535
+
1536
+ #define B2_BIN_COUNT 64
1537
+
1538
+ typedef struct b2TreeBin
1539
+ {
1540
+ b2AABB aabb;
1541
+ int count;
1542
+ } b2TreeBin;
1543
+
1544
+ typedef struct b2TreePlane
1545
+ {
1546
+ b2AABB leftAABB;
1547
+ b2AABB rightAABB;
1548
+ int leftCount;
1549
+ int rightCount;
1550
+ } b2TreePlane;
1551
+
1552
+ // "On Fast Construction of SAH-based Bounding Volume Hierarchies" by Ingo Wald
1553
+ // Returns the left child count
1554
+ static int b2PartitionSAH( int* indices, int* binIndices, b2AABB* boxes, int count )
1555
+ {
1556
+ B2_ASSERT( count > 0 );
1557
+
1558
+ b2TreeBin bins[B2_BIN_COUNT];
1559
+ b2TreePlane planes[B2_BIN_COUNT - 1];
1560
+
1561
+ b2Vec2 center = b2AABB_Center( boxes[0] );
1562
+ b2AABB centroidAABB;
1563
+ centroidAABB.lowerBound = center;
1564
+ centroidAABB.upperBound = center;
1565
+
1566
+ for ( int i = 1; i < count; ++i )
1567
+ {
1568
+ center = b2AABB_Center( boxes[i] );
1569
+ centroidAABB.lowerBound = b2Min( centroidAABB.lowerBound, center );
1570
+ centroidAABB.upperBound = b2Max( centroidAABB.upperBound, center );
1571
+ }
1572
+
1573
+ b2Vec2 d = b2Sub( centroidAABB.upperBound, centroidAABB.lowerBound );
1574
+
1575
+ // Find longest axis
1576
+ int axisIndex;
1577
+ float invD;
1578
+ if ( d.x > d.y )
1579
+ {
1580
+ axisIndex = 0;
1581
+ invD = d.x;
1582
+ }
1583
+ else
1584
+ {
1585
+ axisIndex = 1;
1586
+ invD = d.y;
1587
+ }
1588
+
1589
+ invD = invD > 0.0f ? 1.0f / invD : 0.0f;
1590
+
1591
+ // Initialize bin bounds and count
1592
+ for ( int i = 0; i < B2_BIN_COUNT; ++i )
1593
+ {
1594
+ bins[i].aabb.lowerBound = ( b2Vec2 ){ FLT_MAX, FLT_MAX };
1595
+ bins[i].aabb.upperBound = ( b2Vec2 ){ -FLT_MAX, -FLT_MAX };
1596
+ bins[i].count = 0;
1597
+ }
1598
+
1599
+ // Assign boxes to bins and compute bin boxes
1600
+ // TODO_ERIN optimize
1601
+ float binCount = B2_BIN_COUNT;
1602
+ float lowerBoundArray[2] = { centroidAABB.lowerBound.x, centroidAABB.lowerBound.y };
1603
+ float minC = lowerBoundArray[axisIndex];
1604
+ for ( int i = 0; i < count; ++i )
1605
+ {
1606
+ b2Vec2 c = b2AABB_Center( boxes[i] );
1607
+ float cArray[2] = { c.x, c.y };
1608
+ int binIndex = (int)( binCount * ( cArray[axisIndex] - minC ) * invD );
1609
+ binIndex = b2ClampInt( binIndex, 0, B2_BIN_COUNT - 1 );
1610
+ binIndices[i] = binIndex;
1611
+ bins[binIndex].count += 1;
1612
+ bins[binIndex].aabb = b2AABB_Union( bins[binIndex].aabb, boxes[i] );
1613
+ }
1614
+
1615
+ int planeCount = B2_BIN_COUNT - 1;
1616
+
1617
+ // Prepare all the left planes, candidates for left child
1618
+ planes[0].leftCount = bins[0].count;
1619
+ planes[0].leftAABB = bins[0].aabb;
1620
+ for ( int i = 1; i < planeCount; ++i )
1621
+ {
1622
+ planes[i].leftCount = planes[i - 1].leftCount + bins[i].count;
1623
+ planes[i].leftAABB = b2AABB_Union( planes[i - 1].leftAABB, bins[i].aabb );
1624
+ }
1625
+
1626
+ // Prepare all the right planes, candidates for right child
1627
+ planes[planeCount - 1].rightCount = bins[planeCount].count;
1628
+ planes[planeCount - 1].rightAABB = bins[planeCount].aabb;
1629
+ for ( int i = planeCount - 2; i >= 0; --i )
1630
+ {
1631
+ planes[i].rightCount = planes[i + 1].rightCount + bins[i + 1].count;
1632
+ planes[i].rightAABB = b2AABB_Union( planes[i + 1].rightAABB, bins[i + 1].aabb );
1633
+ }
1634
+
1635
+ // Find best split to minimize SAH
1636
+ float minCost = FLT_MAX;
1637
+ int bestPlane = 0;
1638
+ for ( int i = 0; i < planeCount; ++i )
1639
+ {
1640
+ float leftArea = b2Perimeter( planes[i].leftAABB );
1641
+ float rightArea = b2Perimeter( planes[i].rightAABB );
1642
+ int leftCount = planes[i].leftCount;
1643
+ int rightCount = planes[i].rightCount;
1644
+
1645
+ float cost = leftCount * leftArea + rightCount * rightArea;
1646
+ if ( cost < minCost )
1647
+ {
1648
+ bestPlane = i;
1649
+ minCost = cost;
1650
+ }
1651
+ }
1652
+
1653
+ // Partition node indices and boxes using the Hoare partition scheme
1654
+ // https://en.wikipedia.org/wiki/Quicksort
1655
+ // https://nicholasvadivelu.com/2021/01/11/array-partition/
1656
+ int i1 = 0, i2 = count;
1657
+ while ( i1 < i2 )
1658
+ {
1659
+ while ( i1 < i2 && binIndices[i1] < bestPlane )
1660
+ {
1661
+ i1 += 1;
1662
+ };
1663
+
1664
+ while ( i1 < i2 && binIndices[i2 - 1] >= bestPlane )
1665
+ {
1666
+ i2 -= 1;
1667
+ };
1668
+
1669
+ if ( i1 < i2 )
1670
+ {
1671
+ // Swap indices
1672
+ {
1673
+ int temp = indices[i1];
1674
+ indices[i1] = indices[i2 - 1];
1675
+ indices[i2 - 1] = temp;
1676
+ }
1677
+
1678
+ // Swap boxes
1679
+ {
1680
+ b2AABB temp = boxes[i1];
1681
+ boxes[i1] = boxes[i2 - 1];
1682
+ boxes[i2 - 1] = temp;
1683
+ }
1684
+
1685
+ i1 += 1;
1686
+ i2 -= 1;
1687
+ }
1688
+ }
1689
+ B2_ASSERT( i1 == i2 );
1690
+
1691
+ if ( i1 > 0 && i1 < count )
1692
+ {
1693
+ return i1;
1694
+ }
1695
+ else
1696
+ {
1697
+ return count / 2;
1698
+ }
1699
+ }
1700
+
1701
+ #endif
1702
+
1703
+ // Temporary data used to track the rebuild of a tree node
1704
+ struct b2RebuildItem
1705
+ {
1706
+ int nodeIndex;
1707
+ int childCount;
1708
+
1709
+ // Leaf indices
1710
+ int startIndex;
1711
+ int splitIndex;
1712
+ int endIndex;
1713
+ };
1714
+
1715
+ // Returns root node index
1716
+ static int b2BuildTree( b2DynamicTree* tree, int leafCount )
1717
+ {
1718
+ b2TreeNode* nodes = tree->nodes;
1719
+ int* leafIndices = tree->leafIndices;
1720
+
1721
+ if ( leafCount == 1 )
1722
+ {
1723
+ nodes[leafIndices[0]].parent = B2_NULL_INDEX;
1724
+ return leafIndices[0];
1725
+ }
1726
+
1727
+ #if B2_TREE_HEURISTIC == 0
1728
+ b2Vec2* leafCenters = tree->leafCenters;
1729
+ #else
1730
+ b2AABB* leafBoxes = tree->leafBoxes;
1731
+ int* binIndices = tree->binIndices;
1732
+ #endif
1733
+
1734
+ // todo large stack item
1735
+ struct b2RebuildItem stack[B2_TREE_STACK_SIZE];
1736
+ int top = 0;
1737
+
1738
+ stack[0].nodeIndex = b2AllocateNode( tree );
1739
+ stack[0].childCount = -1;
1740
+ stack[0].startIndex = 0;
1741
+ stack[0].endIndex = leafCount;
1742
+ #if B2_TREE_HEURISTIC == 0
1743
+ stack[0].splitIndex = b2PartitionMid( leafIndices, leafCenters, leafCount );
1744
+ #else
1745
+ stack[0].splitIndex = b2PartitionSAH( leafIndices, binIndices, leafBoxes, leafCount );
1746
+ #endif
1747
+
1748
+ while ( true )
1749
+ {
1750
+ struct b2RebuildItem* item = stack + top;
1751
+
1752
+ item->childCount += 1;
1753
+
1754
+ if ( item->childCount == 2 )
1755
+ {
1756
+ // This internal node has both children established
1757
+
1758
+ if ( top == 0 )
1759
+ {
1760
+ // all done
1761
+ break;
1762
+ }
1763
+
1764
+ struct b2RebuildItem* parentItem = stack + ( top - 1 );
1765
+ b2TreeNode* parentNode = nodes + parentItem->nodeIndex;
1766
+
1767
+ if ( parentItem->childCount == 0 )
1768
+ {
1769
+ B2_ASSERT( parentNode->children.child1 == B2_NULL_INDEX );
1770
+ parentNode->children.child1 = item->nodeIndex;
1771
+ }
1772
+ else
1773
+ {
1774
+ B2_ASSERT( parentItem->childCount == 1 );
1775
+ B2_ASSERT( parentNode->children.child2 == B2_NULL_INDEX );
1776
+ parentNode->children.child2 = item->nodeIndex;
1777
+ }
1778
+
1779
+ b2TreeNode* node = nodes + item->nodeIndex;
1780
+
1781
+ B2_ASSERT( node->parent == B2_NULL_INDEX );
1782
+ node->parent = parentItem->nodeIndex;
1783
+
1784
+ B2_ASSERT( node->children.child1 != B2_NULL_INDEX );
1785
+ B2_ASSERT( node->children.child2 != B2_NULL_INDEX );
1786
+ b2TreeNode* child1 = nodes + node->children.child1;
1787
+ b2TreeNode* child2 = nodes + node->children.child2;
1788
+
1789
+ node->aabb = b2AABB_Union( child1->aabb, child2->aabb );
1790
+ node->height = 1 + b2MaxUInt16( child1->height, child2->height );
1791
+ node->categoryBits = child1->categoryBits | child2->categoryBits;
1792
+
1793
+ // Pop stack
1794
+ top -= 1;
1795
+ }
1796
+ else
1797
+ {
1798
+ int startIndex, endIndex;
1799
+ if ( item->childCount == 0 )
1800
+ {
1801
+ startIndex = item->startIndex;
1802
+ endIndex = item->splitIndex;
1803
+ }
1804
+ else
1805
+ {
1806
+ B2_ASSERT( item->childCount == 1 );
1807
+ startIndex = item->splitIndex;
1808
+ endIndex = item->endIndex;
1809
+ }
1810
+
1811
+ int count = endIndex - startIndex;
1812
+
1813
+ if ( count == 1 )
1814
+ {
1815
+ int childIndex = leafIndices[startIndex];
1816
+ b2TreeNode* node = nodes + item->nodeIndex;
1817
+
1818
+ if ( item->childCount == 0 )
1819
+ {
1820
+ B2_ASSERT( node->children.child1 == B2_NULL_INDEX );
1821
+ node->children.child1 = childIndex;
1822
+ }
1823
+ else
1824
+ {
1825
+ B2_ASSERT( item->childCount == 1 );
1826
+ B2_ASSERT( node->children.child2 == B2_NULL_INDEX );
1827
+ node->children.child2 = childIndex;
1828
+ }
1829
+
1830
+ b2TreeNode* childNode = nodes + childIndex;
1831
+ B2_ASSERT( childNode->parent == B2_NULL_INDEX );
1832
+ childNode->parent = item->nodeIndex;
1833
+ }
1834
+ else
1835
+ {
1836
+ B2_ASSERT( count > 0 );
1837
+ B2_ASSERT( top < B2_TREE_STACK_SIZE );
1838
+
1839
+ top += 1;
1840
+ struct b2RebuildItem* newItem = stack + top;
1841
+ newItem->nodeIndex = b2AllocateNode( tree );
1842
+ newItem->childCount = -1;
1843
+ newItem->startIndex = startIndex;
1844
+ newItem->endIndex = endIndex;
1845
+ #if B2_TREE_HEURISTIC == 0
1846
+ newItem->splitIndex = b2PartitionMid( leafIndices + startIndex, leafCenters + startIndex, count );
1847
+ #else
1848
+ newItem->splitIndex =
1849
+ b2PartitionSAH( leafIndices + startIndex, binIndices + startIndex, leafBoxes + startIndex, count );
1850
+ #endif
1851
+ newItem->splitIndex += startIndex;
1852
+ }
1853
+ }
1854
+ }
1855
+
1856
+ b2TreeNode* rootNode = nodes + stack[0].nodeIndex;
1857
+ B2_ASSERT( rootNode->parent == B2_NULL_INDEX );
1858
+ B2_ASSERT( rootNode->children.child1 != B2_NULL_INDEX );
1859
+ B2_ASSERT( rootNode->children.child2 != B2_NULL_INDEX );
1860
+
1861
+ b2TreeNode* child1 = nodes + rootNode->children.child1;
1862
+ b2TreeNode* child2 = nodes + rootNode->children.child2;
1863
+
1864
+ rootNode->aabb = b2AABB_Union( child1->aabb, child2->aabb );
1865
+ rootNode->height = 1 + b2MaxUInt16( child1->height, child2->height );
1866
+ rootNode->categoryBits = child1->categoryBits | child2->categoryBits;
1867
+
1868
+ return stack[0].nodeIndex;
1869
+ }
1870
+
1871
+ // Not safe to access tree during this operation because it may grow
1872
+ int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild )
1873
+ {
1874
+ int proxyCount = tree->proxyCount;
1875
+ if ( proxyCount == 0 )
1876
+ {
1877
+ return 0;
1878
+ }
1879
+
1880
+ // Ensure capacity for rebuild space
1881
+ if ( proxyCount > tree->rebuildCapacity )
1882
+ {
1883
+ int newCapacity = proxyCount + proxyCount / 2;
1884
+
1885
+ b2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int ) );
1886
+ tree->leafIndices = b2Alloc( newCapacity * sizeof( int ) );
1887
+
1888
+ #if B2_TREE_HEURISTIC == 0
1889
+ b2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) );
1890
+ tree->leafCenters = b2Alloc( newCapacity * sizeof( b2Vec2 ) );
1891
+ #else
1892
+ b2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) );
1893
+ tree->leafBoxes = b2Alloc( newCapacity * sizeof( b2AABB ) );
1894
+ b2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int ) );
1895
+ tree->binIndices = b2Alloc( newCapacity * sizeof( int ) );
1896
+ #endif
1897
+ tree->rebuildCapacity = newCapacity;
1898
+ }
1899
+
1900
+ int leafCount = 0;
1901
+ int stack[B2_TREE_STACK_SIZE];
1902
+ int stackCount = 0;
1903
+
1904
+ int nodeIndex = tree->root;
1905
+ b2TreeNode* nodes = tree->nodes;
1906
+ b2TreeNode* node = nodes + nodeIndex;
1907
+
1908
+ // These are the nodes that get sorted to rebuild the tree.
1909
+ // I'm using indices because the node pool may grow during the build.
1910
+ int* leafIndices = tree->leafIndices;
1911
+
1912
+ #if B2_TREE_HEURISTIC == 0
1913
+ b2Vec2* leafCenters = tree->leafCenters;
1914
+ #else
1915
+ b2AABB* leafBoxes = tree->leafBoxes;
1916
+ #endif
1917
+
1918
+ // Gather all proxy nodes that have grown and all internal nodes that haven't grown. Both are
1919
+ // considered leaves in the tree rebuild.
1920
+ // Free all internal nodes that have grown.
1921
+ // todo use a node growth metric instead of simply enlarged to reduce rebuild size and frequency
1922
+ // this should be weighed against B2_AABB_MARGIN
1923
+ while ( true )
1924
+ {
1925
+ if ( node->height == 0 || ( ( node->flags & b2_enlargedNode ) == 0 && fullBuild == false ) )
1926
+ {
1927
+ leafIndices[leafCount] = nodeIndex;
1928
+ #if B2_TREE_HEURISTIC == 0
1929
+ leafCenters[leafCount] = b2AABB_Center( node->aabb );
1930
+ #else
1931
+ leafBoxes[leafCount] = node->aabb;
1932
+ #endif
1933
+ leafCount += 1;
1934
+
1935
+ // Detach
1936
+ node->parent = B2_NULL_INDEX;
1937
+ }
1938
+ else
1939
+ {
1940
+ int doomedNodeIndex = nodeIndex;
1941
+
1942
+ // Handle children
1943
+ nodeIndex = node->children.child1;
1944
+
1945
+ if ( stackCount < B2_TREE_STACK_SIZE )
1946
+ {
1947
+ stack[stackCount++] = node->children.child2;
1948
+ }
1949
+ else
1950
+ {
1951
+ B2_ASSERT( stackCount < B2_TREE_STACK_SIZE );
1952
+ }
1953
+
1954
+ node = nodes + nodeIndex;
1955
+
1956
+ // Remove doomed node
1957
+ b2FreeNode( tree, doomedNodeIndex );
1958
+
1959
+ continue;
1960
+ }
1961
+
1962
+ if ( stackCount == 0 )
1963
+ {
1964
+ break;
1965
+ }
1966
+
1967
+ nodeIndex = stack[--stackCount];
1968
+ node = nodes + nodeIndex;
1969
+ }
1970
+
1971
+ #if B2_VALIDATE == 1
1972
+ int capacity = tree->nodeCapacity;
1973
+ for ( int i = 0; i < capacity; ++i )
1974
+ {
1975
+ if ( nodes[i].flags & b2_allocatedNode )
1976
+ {
1977
+ B2_ASSERT( ( nodes[i].flags & b2_enlargedNode ) == 0 );
1978
+ }
1979
+ }
1980
+ #endif
1981
+
1982
+ B2_ASSERT( leafCount <= proxyCount );
1983
+
1984
+ tree->root = b2BuildTree( tree, leafCount );
1985
+
1986
+ b2DynamicTree_Validate( tree );
1987
+
1988
+ return leafCount;
1989
+ }