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,322 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "constraint_graph.h"
5
+
6
+ #include "array.h"
7
+ #include "bitset.h"
8
+ #include "body.h"
9
+ #include "contact.h"
10
+ #include "joint.h"
11
+ #include "solver_set.h"
12
+ #include "world.h"
13
+
14
+ #include <string.h>
15
+
16
+ // Solver using graph coloring. Islands are only used for sleep.
17
+ // High-Performance Physical Simulations on Next-Generation Architecture with Many Cores
18
+ // http://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf
19
+
20
+ // Kinematic bodies have to be treated like dynamic bodies in graph coloring. Unlike static bodies, we cannot use a dummy solver
21
+ // body for kinematic bodies. We cannot access a kinematic body from multiple threads efficiently because the SIMD solver body
22
+ // scatter would write to the same kinematic body from multiple threads. Even if these writes don't modify the body, they will
23
+ // cause horrible cache stalls. To make this feasible I would need a way to block these writes.
24
+
25
+ // This is used for debugging by making all constraints be assigned to overflow.
26
+ #define B2_FORCE_OVERFLOW 0
27
+
28
+ _Static_assert( B2_GRAPH_COLOR_COUNT == 12, "graph color count assumed to be 12" );
29
+
30
+ void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity )
31
+ {
32
+ _Static_assert( B2_GRAPH_COLOR_COUNT >= 2, "must have at least two constraint graph colors" );
33
+ _Static_assert( B2_OVERFLOW_INDEX == B2_GRAPH_COLOR_COUNT - 1, "bad over flow index" );
34
+
35
+ *graph = ( b2ConstraintGraph ){ 0 };
36
+
37
+ bodyCapacity = b2MaxInt( bodyCapacity, 8 );
38
+
39
+ // Initialize graph color bit set.
40
+ // No bitset for overflow color.
41
+ for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i )
42
+ {
43
+ b2GraphColor* color = graph->colors + i;
44
+ color->bodySet = b2CreateBitSet( bodyCapacity );
45
+ b2SetBitCountAndClear( &color->bodySet, bodyCapacity );
46
+ }
47
+ }
48
+
49
+ void b2DestroyGraph( b2ConstraintGraph* graph )
50
+ {
51
+ for ( int i = 0; i < B2_GRAPH_COLOR_COUNT; ++i )
52
+ {
53
+ b2GraphColor* color = graph->colors + i;
54
+
55
+ // The bit set should never be used on the overflow color
56
+ B2_ASSERT( i != B2_OVERFLOW_INDEX || color->bodySet.bits == NULL );
57
+
58
+ b2DestroyBitSet( &color->bodySet );
59
+
60
+ b2ContactSimArray_Destroy( &color->contactSims );
61
+ b2JointSimArray_Destroy( &color->jointSims );
62
+ }
63
+ }
64
+
65
+ // Contacts are always created as non-touching. They get cloned into the constraint
66
+ // graph once they are found to be touching.
67
+ // todo maybe kinematic bodies should not go into graph
68
+ void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* contact )
69
+ {
70
+ B2_ASSERT( contactSim->manifold.pointCount > 0 );
71
+ B2_ASSERT( contactSim->simFlags & b2_simTouchingFlag );
72
+ B2_ASSERT( contact->flags & b2_contactTouchingFlag );
73
+
74
+ b2ConstraintGraph* graph = &world->constraintGraph;
75
+ int colorIndex = B2_OVERFLOW_INDEX;
76
+
77
+ int bodyIdA = contact->edges[0].bodyId;
78
+ int bodyIdB = contact->edges[1].bodyId;
79
+ b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
80
+ b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
81
+ bool staticA = bodyA->setIndex == b2_staticSet;
82
+ bool staticB = bodyB->setIndex == b2_staticSet;
83
+ B2_ASSERT( staticA == false || staticB == false );
84
+
85
+ #if B2_FORCE_OVERFLOW == 0
86
+ if ( staticA == false && staticB == false )
87
+ {
88
+ for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i )
89
+ {
90
+ b2GraphColor* color = graph->colors + i;
91
+ if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) )
92
+ {
93
+ continue;
94
+ }
95
+
96
+ b2SetBitGrow( &color->bodySet, bodyIdA );
97
+ b2SetBitGrow( &color->bodySet, bodyIdB );
98
+ colorIndex = i;
99
+ break;
100
+ }
101
+ }
102
+ else if ( staticA == false )
103
+ {
104
+ // No static contacts in color 0
105
+ for ( int i = 1; i < B2_OVERFLOW_INDEX; ++i )
106
+ {
107
+ b2GraphColor* color = graph->colors + i;
108
+ if ( b2GetBit( &color->bodySet, bodyIdA ) )
109
+ {
110
+ continue;
111
+ }
112
+
113
+ b2SetBitGrow( &color->bodySet, bodyIdA );
114
+ colorIndex = i;
115
+ break;
116
+ }
117
+ }
118
+ else if ( staticB == false )
119
+ {
120
+ // No static contacts in color 0
121
+ for ( int i = 1; i < B2_OVERFLOW_INDEX; ++i )
122
+ {
123
+ b2GraphColor* color = graph->colors + i;
124
+ if ( b2GetBit( &color->bodySet, bodyIdB ) )
125
+ {
126
+ continue;
127
+ }
128
+
129
+ b2SetBitGrow( &color->bodySet, bodyIdB );
130
+ colorIndex = i;
131
+ break;
132
+ }
133
+ }
134
+ #endif
135
+
136
+ b2GraphColor* color = graph->colors + colorIndex;
137
+ contact->colorIndex = colorIndex;
138
+ contact->localIndex = color->contactSims.count;
139
+
140
+ b2ContactSim* newContact = b2ContactSimArray_Add( &color->contactSims );
141
+ memcpy( newContact, contactSim, sizeof( b2ContactSim ) );
142
+
143
+ // todo perhaps skip this if the contact is already awake
144
+
145
+ if ( staticA )
146
+ {
147
+ newContact->bodySimIndexA = B2_NULL_INDEX;
148
+ newContact->invMassA = 0.0f;
149
+ newContact->invIA = 0.0f;
150
+ }
151
+ else
152
+ {
153
+ B2_ASSERT( bodyA->setIndex == b2_awakeSet );
154
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
155
+
156
+ int localIndex = bodyA->localIndex;
157
+ newContact->bodySimIndexA = localIndex;
158
+
159
+ b2BodySim* bodySimA = b2BodySimArray_Get( &awakeSet->bodySims, localIndex );
160
+ newContact->invMassA = bodySimA->invMass;
161
+ newContact->invIA = bodySimA->invInertia;
162
+ }
163
+
164
+ if ( staticB )
165
+ {
166
+ newContact->bodySimIndexB = B2_NULL_INDEX;
167
+ newContact->invMassB = 0.0f;
168
+ newContact->invIB = 0.0f;
169
+ }
170
+ else
171
+ {
172
+ B2_ASSERT( bodyB->setIndex == b2_awakeSet );
173
+ b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet );
174
+
175
+ int localIndex = bodyB->localIndex;
176
+ newContact->bodySimIndexB = localIndex;
177
+
178
+ b2BodySim* bodySimB = b2BodySimArray_Get( &awakeSet->bodySims, localIndex );
179
+ newContact->invMassB = bodySimB->invMass;
180
+ newContact->invIB = bodySimB->invInertia;
181
+ }
182
+ }
183
+
184
+ void b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex )
185
+ {
186
+ b2ConstraintGraph* graph = &world->constraintGraph;
187
+
188
+ B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );
189
+ b2GraphColor* color = graph->colors + colorIndex;
190
+
191
+ if ( colorIndex != B2_OVERFLOW_INDEX )
192
+ {
193
+ // might clear a bit for a static body, but this has no effect
194
+ b2ClearBit( &color->bodySet, bodyIdA );
195
+ b2ClearBit( &color->bodySet, bodyIdB );
196
+ }
197
+
198
+ int movedIndex = b2ContactSimArray_RemoveSwap( &color->contactSims, localIndex );
199
+ if ( movedIndex != B2_NULL_INDEX )
200
+ {
201
+ // Fix index on swapped contact
202
+ b2ContactSim* movedContactSim = color->contactSims.data + localIndex;
203
+
204
+ // Fix moved contact
205
+ int movedId = movedContactSim->contactId;
206
+ b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedId );
207
+ B2_ASSERT( movedContact->setIndex == b2_awakeSet );
208
+ B2_ASSERT( movedContact->colorIndex == colorIndex );
209
+ B2_ASSERT( movedContact->localIndex == movedIndex );
210
+ movedContact->localIndex = localIndex;
211
+ }
212
+ }
213
+
214
+ static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyIdB, bool staticA, bool staticB )
215
+ {
216
+ B2_ASSERT( staticA == false || staticB == false );
217
+
218
+ #if B2_FORCE_OVERFLOW == 0
219
+ if ( staticA == false && staticB == false )
220
+ {
221
+ for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i )
222
+ {
223
+ b2GraphColor* color = graph->colors + i;
224
+ if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) )
225
+ {
226
+ continue;
227
+ }
228
+
229
+ b2SetBitGrow( &color->bodySet, bodyIdA );
230
+ b2SetBitGrow( &color->bodySet, bodyIdB );
231
+ return i;
232
+ }
233
+ }
234
+ else if ( staticA == false )
235
+ {
236
+ for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i )
237
+ {
238
+ b2GraphColor* color = graph->colors + i;
239
+ if ( b2GetBit( &color->bodySet, bodyIdA ) )
240
+ {
241
+ continue;
242
+ }
243
+
244
+ b2SetBitGrow( &color->bodySet, bodyIdA );
245
+ return i;
246
+ }
247
+ }
248
+ else if ( staticB == false )
249
+ {
250
+ for ( int i = 0; i < B2_OVERFLOW_INDEX; ++i )
251
+ {
252
+ b2GraphColor* color = graph->colors + i;
253
+ if ( b2GetBit( &color->bodySet, bodyIdB ) )
254
+ {
255
+ continue;
256
+ }
257
+
258
+ b2SetBitGrow( &color->bodySet, bodyIdB );
259
+ return i;
260
+ }
261
+ }
262
+ #else
263
+ B2_UNUSED( graph, bodyIdA, bodyIdB, staticA, staticB );
264
+ #endif
265
+
266
+ return B2_OVERFLOW_INDEX;
267
+ }
268
+
269
+ b2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint )
270
+ {
271
+ b2ConstraintGraph* graph = &world->constraintGraph;
272
+
273
+ int bodyIdA = joint->edges[0].bodyId;
274
+ int bodyIdB = joint->edges[1].bodyId;
275
+ b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA );
276
+ b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB );
277
+ bool staticA = bodyA->setIndex == b2_staticSet;
278
+ bool staticB = bodyB->setIndex == b2_staticSet;
279
+
280
+ int colorIndex = b2AssignJointColor( graph, bodyIdA, bodyIdB, staticA, staticB );
281
+
282
+ b2JointSim* jointSim = b2JointSimArray_Add( &graph->colors[colorIndex].jointSims );
283
+ memset( jointSim, 0, sizeof( b2JointSim ) );
284
+
285
+ joint->colorIndex = colorIndex;
286
+ joint->localIndex = graph->colors[colorIndex].jointSims.count - 1;
287
+ return jointSim;
288
+ }
289
+
290
+ void b2AddJointToGraph( b2World* world, b2JointSim* jointSim, b2Joint* joint )
291
+ {
292
+ b2JointSim* jointDst = b2CreateJointInGraph( world, joint );
293
+ memcpy( jointDst, jointSim, sizeof( b2JointSim ) );
294
+ }
295
+
296
+ void b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex )
297
+ {
298
+ b2ConstraintGraph* graph = &world->constraintGraph;
299
+
300
+ B2_ASSERT( 0 <= colorIndex && colorIndex < B2_GRAPH_COLOR_COUNT );
301
+ b2GraphColor* color = graph->colors + colorIndex;
302
+
303
+ if ( colorIndex != B2_OVERFLOW_INDEX )
304
+ {
305
+ // May clear static bodies, no effect
306
+ b2ClearBit( &color->bodySet, bodyIdA );
307
+ b2ClearBit( &color->bodySet, bodyIdB );
308
+ }
309
+
310
+ int movedIndex = b2JointSimArray_RemoveSwap( &color->jointSims, localIndex );
311
+ if ( movedIndex != B2_NULL_INDEX )
312
+ {
313
+ // Fix moved joint
314
+ b2JointSim* movedJointSim = color->jointSims.data + localIndex;
315
+ int movedId = movedJointSim->jointId;
316
+ b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId );
317
+ B2_ASSERT( movedJoint->setIndex == b2_awakeSet );
318
+ B2_ASSERT( movedJoint->colorIndex == colorIndex );
319
+ B2_ASSERT( movedJoint->localIndex == movedIndex );
320
+ movedJoint->localIndex = localIndex;
321
+ }
322
+ }
@@ -0,0 +1,58 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #pragma once
5
+
6
+ #include "array.h"
7
+ #include "bitset.h"
8
+ #include "constants.h"
9
+
10
+ typedef struct b2Body b2Body;
11
+ typedef struct b2ContactSim b2ContactSim;
12
+ typedef struct b2Contact b2Contact;
13
+ typedef struct b2ContactConstraint b2ContactConstraint;
14
+ typedef struct b2ContactConstraintSIMD b2ContactConstraintSIMD;
15
+ typedef struct b2JointSim b2JointSim;
16
+ typedef struct b2Joint b2Joint;
17
+ typedef struct b2StepContext b2StepContext;
18
+ typedef struct b2World b2World;
19
+
20
+ // This holds constraints that cannot fit the graph color limit. This happens when a single dynamic body
21
+ // is touching many other bodies.
22
+ #define B2_OVERFLOW_INDEX (B2_GRAPH_COLOR_COUNT - 1)
23
+
24
+ typedef struct b2GraphColor
25
+ {
26
+ // This bitset is indexed by bodyId so this is over-sized to encompass static bodies
27
+ // however I never traverse these bits or use the bit count for anything
28
+ // This bitset is unused on the overflow color.
29
+ // todo consider having a uint_16 per body that tracks the graph color membership
30
+ b2BitSet bodySet;
31
+
32
+ // cache friendly arrays
33
+ b2ContactSimArray contactSims;
34
+ b2JointSimArray jointSims;
35
+
36
+ // transient
37
+ union
38
+ {
39
+ b2ContactConstraintSIMD* simdConstraints;
40
+ b2ContactConstraint* overflowConstraints;
41
+ };
42
+ } b2GraphColor;
43
+
44
+ typedef struct b2ConstraintGraph
45
+ {
46
+ // including overflow at the end
47
+ b2GraphColor colors[B2_GRAPH_COLOR_COUNT];
48
+ } b2ConstraintGraph;
49
+
50
+ void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity );
51
+ void b2DestroyGraph( b2ConstraintGraph* graph );
52
+
53
+ void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* contact );
54
+ void b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex );
55
+
56
+ b2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint );
57
+ void b2AddJointToGraph( b2World* world, b2JointSim* jointSim, b2Joint* joint );
58
+ void b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex );