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,1415 @@
1
+
2
+ // SPDX-FileCopyrightText: 2023 Erin Catto
3
+ // SPDX-License-Identifier: MIT
4
+
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 <stddef.h>
13
+
14
+ b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time )
15
+ {
16
+ // https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
17
+ b2Transform xf;
18
+ xf.p = b2Add( b2MulSV( 1.0f - time, sweep->c1 ), b2MulSV( time, sweep->c2 ) );
19
+
20
+ b2Rot q = {
21
+ ( 1.0f - time ) * sweep->q1.c + time * sweep->q2.c,
22
+ ( 1.0f - time ) * sweep->q1.s + time * sweep->q2.s,
23
+ };
24
+
25
+ xf.q = b2NormalizeRot( q );
26
+
27
+ // Shift to origin
28
+ xf.p = b2Sub( xf.p, b2RotateVector( xf.q, sweep->localCenter ) );
29
+ return xf;
30
+ }
31
+
32
+ /// Follows Ericson 5.1.9 Closest Points of Two Line Segments
33
+ b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 )
34
+ {
35
+ b2SegmentDistanceResult result = { 0 };
36
+
37
+ b2Vec2 d1 = b2Sub( q1, p1 );
38
+ b2Vec2 d2 = b2Sub( q2, p2 );
39
+ b2Vec2 r = b2Sub( p1, p2 );
40
+ float dd1 = b2Dot( d1, d1 );
41
+ float dd2 = b2Dot( d2, d2 );
42
+ float rd1 = b2Dot( r, d1 );
43
+ float rd2 = b2Dot( r, d2 );
44
+
45
+ const float epsSqr = FLT_EPSILON * FLT_EPSILON;
46
+
47
+ if ( dd1 < epsSqr || dd2 < epsSqr )
48
+ {
49
+ // Handle all degeneracies
50
+ if ( dd1 >= epsSqr )
51
+ {
52
+ // Segment 2 is degenerate
53
+ result.fraction1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );
54
+ result.fraction2 = 0.0f;
55
+ }
56
+ else if ( dd2 >= epsSqr )
57
+ {
58
+ // Segment 1 is degenerate
59
+ result.fraction1 = 0.0f;
60
+ result.fraction2 = b2ClampFloat( rd2 / dd2, 0.0f, 1.0f );
61
+ }
62
+ else
63
+ {
64
+ result.fraction1 = 0.0f;
65
+ result.fraction2 = 0.0f;
66
+ }
67
+ }
68
+ else
69
+ {
70
+ // Non-degenerate segments
71
+ float d12 = b2Dot( d1, d2 );
72
+
73
+ float denominator = dd1 * dd2 - d12 * d12;
74
+
75
+ // Fraction on segment 1
76
+ float f1 = 0.0f;
77
+ if ( denominator != 0.0f )
78
+ {
79
+ // not parallel
80
+ f1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denominator, 0.0f, 1.0f );
81
+ }
82
+
83
+ // Compute point on segment 2 closest to p1 + f1 * d1
84
+ float f2 = ( d12 * f1 + rd2 ) / dd2;
85
+
86
+ // Clamping of segment 2 requires a do over on segment 1
87
+ if ( f2 < 0.0f )
88
+ {
89
+ f2 = 0.0f;
90
+ f1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );
91
+ }
92
+ else if ( f2 > 1.0f )
93
+ {
94
+ f2 = 1.0f;
95
+ f1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f );
96
+ }
97
+
98
+ result.fraction1 = f1;
99
+ result.fraction2 = f2;
100
+ }
101
+
102
+ result.closest1 = b2MulAdd( p1, result.fraction1, d1 );
103
+ result.closest2 = b2MulAdd( p2, result.fraction2, d2 );
104
+ result.distanceSquared = b2DistanceSquared( result.closest1, result.closest2 );
105
+ return result;
106
+ }
107
+
108
+ b2ShapeProxy b2MakeProxy( const b2Vec2* points, int count, float radius )
109
+ {
110
+ count = b2MinInt( count, B2_MAX_POLYGON_VERTICES );
111
+ b2ShapeProxy proxy;
112
+ for ( int i = 0; i < count; ++i )
113
+ {
114
+ proxy.points[i] = points[i];
115
+ }
116
+ proxy.count = count;
117
+ proxy.radius = radius;
118
+ return proxy;
119
+ }
120
+
121
+ b2ShapeProxy b2MakeOffsetProxy( const b2Vec2* points, int count, float radius, b2Vec2 position, b2Rot rotation )
122
+ {
123
+ count = b2MinInt( count, B2_MAX_POLYGON_VERTICES );
124
+ b2Transform transform = {
125
+ .p = position,
126
+ .q = rotation,
127
+ };
128
+ b2ShapeProxy proxy;
129
+ for ( int i = 0; i < count; ++i )
130
+ {
131
+ proxy.points[i] = b2TransformPoint( transform, points[i] );
132
+ }
133
+ proxy.count = count;
134
+ proxy.radius = radius;
135
+ return proxy;
136
+ }
137
+
138
+ static inline b2Vec2 b2Weight2( float a1, b2Vec2 w1, float a2, b2Vec2 w2 )
139
+ {
140
+ return (b2Vec2){ a1 * w1.x + a2 * w2.x, a1 * w1.y + a2 * w2.y };
141
+ }
142
+
143
+ static inline b2Vec2 b2Weight3( float a1, b2Vec2 w1, float a2, b2Vec2 w2, float a3, b2Vec2 w3 )
144
+ {
145
+ return (b2Vec2){ a1 * w1.x + a2 * w2.x + a3 * w3.x, a1 * w1.y + a2 * w2.y + a3 * w3.y };
146
+ }
147
+
148
+ static inline int b2FindSupport( const b2ShapeProxy* proxy, b2Vec2 direction )
149
+ {
150
+ const b2Vec2* points = proxy->points;
151
+ int count = proxy->count;
152
+
153
+ int bestIndex = 0;
154
+ float bestValue = b2Dot( points[0], direction );
155
+ for ( int i = 1; i < count; ++i )
156
+ {
157
+ float value = b2Dot( points[i], direction );
158
+ if ( value > bestValue )
159
+ {
160
+ bestIndex = i;
161
+ bestValue = value;
162
+ }
163
+ }
164
+
165
+ return bestIndex;
166
+ }
167
+
168
+ static b2Simplex b2MakeSimplexFromCache( const b2SimplexCache* cache, const b2ShapeProxy* proxyA, const b2ShapeProxy* proxyB )
169
+ {
170
+ B2_ASSERT( cache->count <= 3 );
171
+ b2Simplex s;
172
+
173
+ // Copy data from cache.
174
+ s.count = cache->count;
175
+
176
+ b2SimplexVertex* vertices[] = { &s.v1, &s.v2, &s.v3 };
177
+ for ( int i = 0; i < s.count; ++i )
178
+ {
179
+ b2SimplexVertex* v = vertices[i];
180
+ v->indexA = cache->indexA[i];
181
+ v->indexB = cache->indexB[i];
182
+ v->wA = proxyA->points[v->indexA];
183
+ v->wB = proxyB->points[v->indexB];
184
+ v->w = b2Sub( v->wA, v->wB );
185
+
186
+ // invalid
187
+ v->a = -1.0f;
188
+ }
189
+
190
+ // If the cache is empty or invalid ...
191
+ if ( s.count == 0 )
192
+ {
193
+ b2SimplexVertex* v = vertices[0];
194
+ v->indexA = 0;
195
+ v->indexB = 0;
196
+ v->wA = proxyA->points[0];
197
+ v->wB = proxyB->points[0];
198
+ v->w = b2Sub( v->wA, v->wB );
199
+ v->a = 1.0f;
200
+ s.count = 1;
201
+ }
202
+
203
+ return s;
204
+ }
205
+
206
+ static void b2MakeSimplexCache( b2SimplexCache* cache, const b2Simplex* simplex )
207
+ {
208
+ cache->count = (uint16_t)simplex->count;
209
+ const b2SimplexVertex* vertices[] = { &simplex->v1, &simplex->v2, &simplex->v3 };
210
+ for ( int i = 0; i < simplex->count; ++i )
211
+ {
212
+ cache->indexA[i] = (uint8_t)vertices[i]->indexA;
213
+ cache->indexB[i] = (uint8_t)vertices[i]->indexB;
214
+ }
215
+ }
216
+
217
+ static void b2ComputeSimplexWitnessPoints( b2Vec2* a, b2Vec2* b, const b2Simplex* s )
218
+ {
219
+ switch ( s->count )
220
+ {
221
+ case 0:
222
+ B2_ASSERT( false );
223
+ break;
224
+
225
+ case 1:
226
+ *a = s->v1.wA;
227
+ *b = s->v1.wB;
228
+ break;
229
+
230
+ case 2:
231
+ *a = b2Weight2( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA );
232
+ *b = b2Weight2( s->v1.a, s->v1.wB, s->v2.a, s->v2.wB );
233
+ break;
234
+
235
+ case 3:
236
+ *a = b2Weight3( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA, s->v3.a, s->v3.wA );
237
+ // todo why are these not equal?
238
+ //*b = b2Weight3(s->v1.a, s->v1.wB, s->v2.a, s->v2.wB, s->v3.a, s->v3.wB);
239
+ *b = *a;
240
+ break;
241
+
242
+ default:
243
+ B2_ASSERT( false );
244
+ break;
245
+ }
246
+ }
247
+
248
+ // Solve a line segment using barycentric coordinates.
249
+ //
250
+ // p = a1 * w1 + a2 * w2
251
+ // a1 + a2 = 1
252
+ //
253
+ // The vector from the origin to the closest point on the line is
254
+ // perpendicular to the line.
255
+ // e12 = w2 - w1
256
+ // dot(p, e) = 0
257
+ // a1 * dot(w1, e) + a2 * dot(w2, e) = 0
258
+ //
259
+ // 2-by-2 linear system
260
+ // [1 1 ][a1] = [1]
261
+ // [w1.e12 w2.e12][a2] = [0]
262
+ //
263
+ // Define
264
+ // d12_1 = dot(w2, e12)
265
+ // d12_2 = -dot(w1, e12)
266
+ // d12 = d12_1 + d12_2
267
+ //
268
+ // Solution
269
+ // a1 = d12_1 / d12
270
+ // a2 = d12_2 / d12
271
+ //
272
+ // returns a vector that points towards the origin
273
+ static b2Vec2 b2SolveSimplex2( b2Simplex* s )
274
+ {
275
+ b2Vec2 w1 = s->v1.w;
276
+ b2Vec2 w2 = s->v2.w;
277
+ b2Vec2 e12 = b2Sub( w2, w1 );
278
+
279
+ // w1 region
280
+ float d12_2 = -b2Dot( w1, e12 );
281
+ if ( d12_2 <= 0.0f )
282
+ {
283
+ // a2 <= 0, so we clamp it to 0
284
+ s->v1.a = 1.0f;
285
+ s->count = 1;
286
+ return b2Neg( w1 );
287
+ }
288
+
289
+ // w2 region
290
+ float d12_1 = b2Dot( w2, e12 );
291
+ if ( d12_1 <= 0.0f )
292
+ {
293
+ // a1 <= 0, so we clamp it to 0
294
+ s->v2.a = 1.0f;
295
+ s->count = 1;
296
+ s->v1 = s->v2;
297
+ return b2Neg( w2 );
298
+ }
299
+
300
+ // Must be in e12 region.
301
+ float inv_d12 = 1.0f / ( d12_1 + d12_2 );
302
+ s->v1.a = d12_1 * inv_d12;
303
+ s->v2.a = d12_2 * inv_d12;
304
+ s->count = 2;
305
+ return b2CrossSV( b2Cross( b2Add( w1, w2 ), e12 ), e12 );
306
+ }
307
+
308
+ static b2Vec2 b2SolveSimplex3( b2Simplex* s )
309
+ {
310
+ b2Vec2 w1 = s->v1.w;
311
+ b2Vec2 w2 = s->v2.w;
312
+ b2Vec2 w3 = s->v3.w;
313
+
314
+ // Edge12
315
+ // [1 1 ][a1] = [1]
316
+ // [w1.e12 w2.e12][a2] = [0]
317
+ // a3 = 0
318
+ b2Vec2 e12 = b2Sub( w2, w1 );
319
+ float w1e12 = b2Dot( w1, e12 );
320
+ float w2e12 = b2Dot( w2, e12 );
321
+ float d12_1 = w2e12;
322
+ float d12_2 = -w1e12;
323
+
324
+ // Edge13
325
+ // [1 1 ][a1] = [1]
326
+ // [w1.e13 w3.e13][a3] = [0]
327
+ // a2 = 0
328
+ b2Vec2 e13 = b2Sub( w3, w1 );
329
+ float w1e13 = b2Dot( w1, e13 );
330
+ float w3e13 = b2Dot( w3, e13 );
331
+ float d13_1 = w3e13;
332
+ float d13_2 = -w1e13;
333
+
334
+ // Edge23
335
+ // [1 1 ][a2] = [1]
336
+ // [w2.e23 w3.e23][a3] = [0]
337
+ // a1 = 0
338
+ b2Vec2 e23 = b2Sub( w3, w2 );
339
+ float w2e23 = b2Dot( w2, e23 );
340
+ float w3e23 = b2Dot( w3, e23 );
341
+ float d23_1 = w3e23;
342
+ float d23_2 = -w2e23;
343
+
344
+ // Triangle123
345
+ float n123 = b2Cross( e12, e13 );
346
+
347
+ float d123_1 = n123 * b2Cross( w2, w3 );
348
+ float d123_2 = n123 * b2Cross( w3, w1 );
349
+ float d123_3 = n123 * b2Cross( w1, w2 );
350
+
351
+ // w1 region
352
+ if ( d12_2 <= 0.0f && d13_2 <= 0.0f )
353
+ {
354
+ s->v1.a = 1.0f;
355
+ s->count = 1;
356
+ return b2Neg( w1 );
357
+ }
358
+
359
+ // e12
360
+ if ( d12_1 > 0.0f && d12_2 > 0.0f && d123_3 <= 0.0f )
361
+ {
362
+ float inv_d12 = 1.0f / ( d12_1 + d12_2 );
363
+ s->v1.a = d12_1 * inv_d12;
364
+ s->v2.a = d12_2 * inv_d12;
365
+ s->count = 2;
366
+ return b2CrossSV( b2Cross( b2Add( w1, w2 ), e12 ), e12 );
367
+ }
368
+
369
+ // e13
370
+ if ( d13_1 > 0.0f && d13_2 > 0.0f && d123_2 <= 0.0f )
371
+ {
372
+ float inv_d13 = 1.0f / ( d13_1 + d13_2 );
373
+ s->v1.a = d13_1 * inv_d13;
374
+ s->v3.a = d13_2 * inv_d13;
375
+ s->count = 2;
376
+ s->v2 = s->v3;
377
+ return b2CrossSV( b2Cross( b2Add( w1, w3 ), e13 ), e13 );
378
+ }
379
+
380
+ // w2 region
381
+ if ( d12_1 <= 0.0f && d23_2 <= 0.0f )
382
+ {
383
+ s->v2.a = 1.0f;
384
+ s->count = 1;
385
+ s->v1 = s->v2;
386
+ return b2Neg( w2 );
387
+ }
388
+
389
+ // w3 region
390
+ if ( d13_1 <= 0.0f && d23_1 <= 0.0f )
391
+ {
392
+ s->v3.a = 1.0f;
393
+ s->count = 1;
394
+ s->v1 = s->v3;
395
+ return b2Neg( w3 );
396
+ }
397
+
398
+ // e23
399
+ if ( d23_1 > 0.0f && d23_2 > 0.0f && d123_1 <= 0.0f )
400
+ {
401
+ float inv_d23 = 1.0f / ( d23_1 + d23_2 );
402
+ s->v2.a = d23_1 * inv_d23;
403
+ s->v3.a = d23_2 * inv_d23;
404
+ s->count = 2;
405
+ s->v1 = s->v3;
406
+ return b2CrossSV( b2Cross( b2Add( w2, w3 ), e23 ), e23 );
407
+ }
408
+
409
+ // Must be in triangle123
410
+ float inv_d123 = 1.0f / ( d123_1 + d123_2 + d123_3 );
411
+ s->v1.a = d123_1 * inv_d123;
412
+ s->v2.a = d123_2 * inv_d123;
413
+ s->v3.a = d123_3 * inv_d123;
414
+ s->count = 3;
415
+
416
+ // No search direction
417
+ return b2Vec2_zero;
418
+ }
419
+
420
+ // Uses GJK for computing the distance between convex shapes.
421
+ // https://box2d.org/files/ErinCatto_GJK_GDC2010.pdf
422
+ // I spent time optimizing this and could find no further significant gains 3/30/2025
423
+ b2DistanceOutput b2ShapeDistance( const b2DistanceInput* input, b2SimplexCache* cache, b2Simplex* simplexes, int simplexCapacity )
424
+ {
425
+ B2_UNUSED( simplexes, simplexCapacity );
426
+ B2_ASSERT( input->proxyA.count > 0 && input->proxyB.count > 0 );
427
+ B2_ASSERT( input->proxyA.radius >= 0.0f );
428
+ B2_ASSERT( input->proxyB.radius >= 0.0f );
429
+
430
+ b2DistanceOutput output = { 0 };
431
+
432
+ const b2ShapeProxy* proxyA = &input->proxyA;
433
+
434
+ // Get proxyB in frame A to avoid further transforms in the main loop.
435
+ // This is still a performance gain at 8 points.
436
+ b2ShapeProxy localProxyB;
437
+ {
438
+ b2Transform transform = b2InvMulTransforms( input->transformA, input->transformB );
439
+ localProxyB.count = input->proxyB.count;
440
+ localProxyB.radius = input->proxyB.radius;
441
+ for ( int i = 0; i < localProxyB.count; ++i )
442
+ {
443
+ localProxyB.points[i] = b2TransformPoint( transform, input->proxyB.points[i] );
444
+ }
445
+ }
446
+
447
+ // Initialize the simplex.
448
+ b2Simplex simplex = b2MakeSimplexFromCache( cache, proxyA, &localProxyB );
449
+
450
+ int simplexIndex = 0;
451
+ if ( simplexes != NULL && simplexIndex < simplexCapacity )
452
+ {
453
+ simplexes[simplexIndex] = simplex;
454
+ simplexIndex += 1;
455
+ }
456
+
457
+ // Get simplex vertices as an array.
458
+ b2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 };
459
+
460
+ b2Vec2 nonUnitNormal = b2Vec2_zero;
461
+
462
+ // These store the vertices of the last simplex so that we can check for duplicates and prevent cycling.
463
+ int saveA[3], saveB[3];
464
+
465
+ // Main iteration loop. All computations are done in frame A.
466
+ const int maxIterations = 20;
467
+ int iteration = 0;
468
+ while ( iteration < maxIterations )
469
+ {
470
+ // Copy simplex so we can identify duplicates.
471
+ int saveCount = simplex.count;
472
+ for ( int i = 0; i < saveCount; ++i )
473
+ {
474
+ saveA[i] = vertices[i]->indexA;
475
+ saveB[i] = vertices[i]->indexB;
476
+ }
477
+
478
+ b2Vec2 d = { 0 };
479
+ switch ( simplex.count )
480
+ {
481
+ case 1:
482
+ d = b2Neg( simplex.v1.w );
483
+ break;
484
+
485
+ case 2:
486
+ d = b2SolveSimplex2( &simplex );
487
+ break;
488
+
489
+ case 3:
490
+ d = b2SolveSimplex3( &simplex );
491
+ break;
492
+
493
+ default:
494
+ B2_ASSERT( false );
495
+ }
496
+
497
+ // If we have 3 points, then the origin is in the corresponding triangle.
498
+ if ( simplex.count == 3 )
499
+ {
500
+ break;
501
+ }
502
+
503
+ #ifndef NDEBUG
504
+ if ( simplexes != NULL && simplexIndex < simplexCapacity )
505
+ {
506
+ simplexes[simplexIndex] = simplex;
507
+ simplexIndex += 1;
508
+ }
509
+ #endif
510
+
511
+ // Save the normal
512
+ nonUnitNormal = d;
513
+
514
+ // Ensure the search direction is numerically fit.
515
+ if ( b2Dot( d, d ) < FLT_EPSILON * FLT_EPSILON )
516
+ {
517
+ // This is unlikely but could lead to bad cycling.
518
+ // The branch predictor seems to make this check have low cost.
519
+
520
+ // The origin is probably contained by a line segment
521
+ // or triangle. Thus the shapes are overlapped.
522
+
523
+ // We can't return zero here even though there may be overlap.
524
+ // In case the simplex is a point, segment, or triangle it is difficult
525
+ // to determine if the origin is contained in the CSO or very close to it.
526
+ break;
527
+ }
528
+
529
+ // Compute a tentative new simplex vertex using support points.
530
+ // support = support(a, d) - support(b, -d)
531
+ b2SimplexVertex* vertex = vertices[simplex.count];
532
+ vertex->indexA = b2FindSupport( proxyA, d );
533
+ vertex->wA = proxyA->points[vertex->indexA];
534
+ vertex->indexB = b2FindSupport( &localProxyB, b2Neg( d ) );
535
+ vertex->wB = localProxyB.points[vertex->indexB];
536
+ vertex->w = b2Sub( vertex->wA, vertex->wB );
537
+
538
+ // Iteration count is equated to the number of support point calls.
539
+ ++iteration;
540
+
541
+ // Check for duplicate support points. This is the main termination criteria.
542
+ bool duplicate = false;
543
+ for ( int i = 0; i < saveCount; ++i )
544
+ {
545
+ if ( vertex->indexA == saveA[i] && vertex->indexB == saveB[i] )
546
+ {
547
+ duplicate = true;
548
+ break;
549
+ }
550
+ }
551
+
552
+ // If we found a duplicate support point we must exit to avoid cycling.
553
+ if ( duplicate )
554
+ {
555
+ break;
556
+ }
557
+
558
+ // New vertex is valid and needed.
559
+ simplex.count += 1;
560
+ }
561
+
562
+ #ifndef NDEBUG
563
+ if ( simplexes != NULL && simplexIndex < simplexCapacity )
564
+ {
565
+ simplexes[simplexIndex] = simplex;
566
+ simplexIndex += 1;
567
+ }
568
+ #endif
569
+
570
+ // Prepare output
571
+ b2Vec2 normal = b2Normalize( nonUnitNormal );
572
+ normal = b2RotateVector( input->transformA.q, normal );
573
+
574
+ b2Vec2 localPointA, localPointB;
575
+ b2ComputeSimplexWitnessPoints( &localPointA, &localPointB, &simplex );
576
+ output.normal = normal;
577
+ output.distance = b2Distance( localPointA, localPointB );
578
+ output.pointA = b2TransformPoint( input->transformA, localPointA );
579
+ output.pointB = b2TransformPoint( input->transformA, localPointB );
580
+ output.iterations = iteration;
581
+ output.simplexCount = simplexIndex;
582
+
583
+ // Cache the simplex
584
+ b2MakeSimplexCache( cache, &simplex );
585
+
586
+ // Apply radii if requested
587
+ if ( input->useRadii && output.distance > 0.1f * B2_LINEAR_SLOP )
588
+ {
589
+ float radiusA = input->proxyA.radius;
590
+ float radiusB = input->proxyB.radius;
591
+ output.distance = b2MaxFloat( 0.0f, output.distance - radiusA - radiusB );
592
+
593
+ // Keep closest points on perimeter even if overlapped, this way the points move smoothly.
594
+ output.pointA = b2MulAdd( output.pointA, radiusA, normal );
595
+ output.pointB = b2MulSub( output.pointB, radiusB, normal );
596
+ }
597
+
598
+ return output;
599
+ }
600
+
601
+ // Shape cast using conservative advancement
602
+ b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input )
603
+ {
604
+ // Compute tolerance
605
+ float linearSlop = B2_LINEAR_SLOP;
606
+ float totalRadius = input->proxyA.radius + input->proxyB.radius;
607
+ float target = b2MaxFloat( linearSlop, totalRadius - linearSlop );
608
+ float tolerance = 0.25f * linearSlop;
609
+
610
+ B2_ASSERT( target > tolerance );
611
+
612
+ // Prepare input for distance query
613
+ b2SimplexCache cache = { 0 };
614
+
615
+ float alpha = 0.0f;
616
+
617
+ b2DistanceInput distanceInput = { 0 };
618
+ distanceInput.proxyA = input->proxyA;
619
+ distanceInput.proxyB = input->proxyB;
620
+ distanceInput.transformA = input->transformA;
621
+ distanceInput.transformB = input->transformB;
622
+ distanceInput.useRadii = false;
623
+
624
+ b2Vec2 delta2 = input->translationB;
625
+ b2CastOutput output = { 0 };
626
+
627
+ int iteration = 0;
628
+ int maxIterations = 20;
629
+ for ( ; iteration < maxIterations; ++iteration )
630
+ {
631
+ output.iterations += 1;
632
+
633
+ b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
634
+
635
+ if ( distanceOutput.distance < target + tolerance )
636
+ {
637
+ if ( iteration == 0 )
638
+ {
639
+ if ( input->canEncroach && distanceOutput.distance > 2.0f * linearSlop )
640
+ {
641
+ target = distanceOutput.distance - linearSlop;
642
+ }
643
+ else
644
+ {
645
+ if ( distanceOutput.distance == 0.0f )
646
+ {
647
+ // Normal may be invalid
648
+ return output;
649
+ }
650
+
651
+ // Initial overlap but distance is non-zero due to radius
652
+ B2_ASSERT( b2IsNormalized( distanceOutput.normal ) );
653
+ output.fraction = alpha;
654
+ output.point = b2MulAdd( distanceOutput.pointA, input->proxyA.radius, distanceOutput.normal );
655
+ output.normal = distanceOutput.normal;
656
+ output.hit = true;
657
+ return output;
658
+ }
659
+ }
660
+ else
661
+ {
662
+ // Regular hit
663
+ B2_ASSERT( distanceOutput.distance > 0.0f && b2IsNormalized( distanceOutput.normal ) );
664
+ output.fraction = alpha;
665
+ output.point = b2MulAdd( distanceOutput.pointA, input->proxyA.radius, distanceOutput.normal );
666
+ output.normal = distanceOutput.normal;
667
+ output.hit = true;
668
+ return output;
669
+ }
670
+ }
671
+
672
+ B2_ASSERT( distanceOutput.distance > 0.0f );
673
+ B2_ASSERT( b2IsNormalized( distanceOutput.normal ) );
674
+
675
+ // Check if shapes are approaching each other
676
+ float denominator = b2Dot( delta2, distanceOutput.normal );
677
+ if ( denominator >= 0.0f )
678
+ {
679
+ // Miss
680
+ output.fraction = 1.0f;
681
+ return output;
682
+ }
683
+
684
+ // Advance sweep
685
+ alpha += ( target - distanceOutput.distance ) / denominator;
686
+ if ( alpha >= input->maxFraction )
687
+ {
688
+ // Miss
689
+ output.fraction = 1.0f;
690
+ return output;
691
+ }
692
+
693
+ distanceInput.transformB.p = b2MulAdd( input->transformB.p, alpha, delta2 );
694
+ }
695
+
696
+ // Failure!
697
+ return output;
698
+ }
699
+
700
+ #if 0
701
+ static inline b2Vec2 b2ComputeSimplexClosestPoint( const b2Simplex* s )
702
+ {
703
+ if ( s->count == 1 )
704
+ {
705
+ return s->v1.w;
706
+ }
707
+
708
+ if ( s->count == 2 )
709
+ {
710
+ return b2Weight2( s->v1.a, s->v1.w, s->v2.a, s->v2.w );
711
+ }
712
+
713
+ return b2Vec2_zero;
714
+ }
715
+
716
+ typedef struct b2ShapeCastData
717
+ {
718
+ b2Simplex simplex;
719
+ b2Vec2 closestA, closestB;
720
+ b2Vec2 normal;
721
+ b2Vec2 p0;
722
+ float fraction;
723
+ } b2ShapeCastData;
724
+
725
+ // GJK-raycast
726
+ // Algorithm by Gino van den Bergen.
727
+ // "Smooth Mesh Contacts with GJK" in Game Physics Pearls. 2010
728
+ // This needs the simplex of A - B because the translation is for B and this
729
+ // is how the relative motion works out when both shapes are translating.
730
+ // This is similar to ray vs polygon and involves plane clipping. See b2RayCastPolygon.
731
+ // In this case the polygon is just points and there are no planes. This uses a modified
732
+ // version of GJK to generate planes for clipping.
733
+ // The algorithm works by incrementally building clipping planes using GJK. Once a valid
734
+ // clip plane is found the simplex origin is moved to the current fraction on the ray.
735
+ // This resets the simplex after every clip. Later I should compare performance.
736
+ // However, adapting this to work with encroachment is tricky and confusing because encroachment
737
+ // needs distance.
738
+ // Note: this algorithm is difficult to debug and not worth the effort in my opinion 4/1/2025
739
+ b2CastOutput b2ShapeCastMerged( const b2ShapeCastPairInput* input, b2ShapeCastData* debugData, int debugCapacity )
740
+ {
741
+ B2_UNUSED( debugData, debugCapacity );
742
+
743
+ b2CastOutput output = { 0 };
744
+ output.fraction = input->maxFraction;
745
+
746
+ b2ShapeProxy proxyA = input->proxyA;
747
+
748
+ b2Transform xf = b2InvMulTransforms( input->transformA, input->transformB );
749
+
750
+ // Put proxyB in proxyA's frame to reduce round-off error
751
+ b2ShapeProxy proxyB;
752
+ proxyB.count = input->proxyB.count;
753
+ proxyB.radius = input->proxyB.radius;
754
+ B2_ASSERT( proxyB.count <= B2_MAX_POLYGON_VERTICES );
755
+
756
+ for ( int i = 0; i < proxyB.count; ++i )
757
+ {
758
+ proxyB.points[i] = b2TransformPoint( xf, input->proxyB.points[i] );
759
+ }
760
+
761
+ float radius = proxyA.radius + proxyB.radius;
762
+
763
+ b2Vec2 r = b2RotateVector( xf.q, input->translationB );
764
+ float lambda = 0.0f;
765
+ float maxFraction = input->maxFraction;
766
+
767
+ // Initial simplex
768
+ b2Simplex simplex;
769
+ simplex.count = 0;
770
+
771
+ // Get simplex vertices as an array.
772
+ b2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 };
773
+
774
+ // Get an initial point in A - B
775
+ b2Vec2 wA = proxyA.points[0];
776
+ b2Vec2 wB = proxyB.points[0];
777
+ b2Vec2 v = b2Sub( wA, wB );
778
+ b2Vec2 d = b2Neg( v );
779
+
780
+ // Sigma is the target distance between proxies
781
+ const float linearSlop = B2_LINEAR_SLOP;
782
+ const float sigma = b2MaxFloat( linearSlop, radius - linearSlop );
783
+ float tolerance = 0.5f * linearSlop;
784
+ float stolSquared = ( sigma + tolerance ) * ( sigma + tolerance );
785
+
786
+ // Main iteration loop.
787
+ const int maxIterations = 20;
788
+ int iteration = 0;
789
+ while ( iteration < maxIterations && b2LengthSquared( v ) > stolSquared )
790
+ {
791
+ B2_ASSERT( simplex.count < 3 );
792
+
793
+ // Support in direction d (A - B)
794
+ int indexA = b2FindSupport( &proxyA, d );
795
+ wA = proxyA.points[indexA];
796
+ int indexB = b2FindSupport( &proxyB, b2Neg( d ) );
797
+ wB = proxyB.points[indexB];
798
+ b2Vec2 p0 = b2Sub( wA, wB );
799
+
800
+ // d is a normal at p, normalize to work with sigma
801
+ b2Vec2 normal = b2Normalize( d );
802
+
803
+ // Intersect ray with plane
804
+ // p = origin + t * r
805
+ // dot(n, p - p0) = sigma
806
+ // dot(n, origin - p0) + t * dot(n, r) = sigma
807
+ // t = ( dot(n, p0) + sigma) / dot(n, r)
808
+ // if t < (dot(n, p0) + sigma) / dot(n, r) then t can be increased
809
+ // or (flipping sign because dot(n,r) < 0)
810
+ // dot(n, p0) + sigma < t * dot(n, r) && dot(n, r) < 0
811
+ float np0 = b2Dot( normal, p0 );
812
+ float nr = b2Dot( normal, r );
813
+ if ( np0 + sigma < lambda * nr )
814
+ {
815
+ if ( nr >= 0.0f )
816
+ {
817
+ // miss
818
+ return output;
819
+ }
820
+
821
+ lambda = ( np0 + sigma ) / nr;
822
+ if ( lambda > maxFraction )
823
+ {
824
+ // too far
825
+ return output;
826
+ }
827
+
828
+ // reset the simplex
829
+ simplex.count = 0;
830
+ }
831
+
832
+ // Shift by lambda * r because we want the closest point to the current clip point.
833
+ // Note that the support point p is not shifted because we want the plane equation
834
+ // to be formed in un-shifted space.
835
+ b2SimplexVertex* vertex = vertices[simplex.count];
836
+ vertex->indexA = indexB;
837
+ vertex->wA = wA;
838
+ vertex->indexB = indexA;
839
+ vertex->wB = (b2Vec2){ wB.x + lambda * r.x, wB.y + lambda * r.y };
840
+ vertex->w = b2Sub( vertex->wA, vertex->wB );
841
+ vertex->a = 1.0f;
842
+ simplex.count += 1;
843
+
844
+ switch ( simplex.count )
845
+ {
846
+ case 1:
847
+ d = b2Neg( simplex.v1.w );
848
+ break;
849
+
850
+ case 2:
851
+ d = b2SolveSimplex2( &simplex );
852
+ break;
853
+
854
+ case 3:
855
+ d = b2SolveSimplex3( &simplex );
856
+ break;
857
+
858
+ default:
859
+ B2_ASSERT( false );
860
+ }
861
+
862
+ #ifndef NDEBUG
863
+ if ( debugData != NULL && output.iterations < debugCapacity )
864
+ {
865
+ debugData[output.iterations].simplex = simplex;
866
+ debugData[output.iterations].normal = normal;
867
+ debugData[output.iterations].p0 = p0;
868
+ b2Vec2 cA, cB;
869
+ b2ComputeSimplexWitnessPoints( &cA, &cB, &simplex );
870
+ debugData[output.iterations].closestA = cA;
871
+ debugData[output.iterations].closestB = cB;
872
+ debugData[output.iterations].fraction = lambda;
873
+ }
874
+ #endif
875
+
876
+ output.iterations += 1;
877
+
878
+ // If we have 3 points, then the origin is in the corresponding triangle.
879
+ if ( simplex.count == 3 )
880
+ {
881
+ // Overlap
882
+ return output;
883
+ }
884
+
885
+ // Get distance vector
886
+ v = b2ComputeSimplexClosestPoint( &simplex );
887
+
888
+ // Iteration count is equated to the number of support point calls.
889
+ ++iteration;
890
+ }
891
+
892
+ if ( iteration == 0 || lambda == 0.0f )
893
+ {
894
+ // Initial overlap
895
+ return output;
896
+ }
897
+
898
+ // Prepare output.
899
+ b2Vec2 pointA, pointB;
900
+ b2ComputeSimplexWitnessPoints( &pointB, &pointA, &simplex );
901
+
902
+ b2Vec2 n = b2Normalize( b2Neg( v ) );
903
+ b2Vec2 point = { pointA.x + proxyA.radius * n.x, pointA.y + proxyA.radius * n.y };
904
+
905
+ output.point = b2TransformPoint( input->transformA, point );
906
+ output.normal = b2RotateVector( input->transformA.q, n );
907
+ output.fraction = lambda;
908
+ output.iterations = iteration;
909
+ output.hit = true;
910
+ return output;
911
+ }
912
+ #endif
913
+
914
+ // Warning: writing to these globals significantly slows multithreading performance
915
+ #if B2_SNOOP_TOI_COUNTERS
916
+ float b2_toiTime, b2_toiMaxTime;
917
+ int b2_toiCalls, b2_toiDistanceIterations, b2_toiMaxDistanceIterations;
918
+ int b2_toiRootIterations, b2_toiMaxRootIterations;
919
+ int b2_toiFailedCount;
920
+ int b2_toiOverlappedCount;
921
+ int b2_toiHitCount;
922
+ int b2_toiSeparatedCount;
923
+ #endif
924
+
925
+ typedef enum b2SeparationType
926
+ {
927
+ b2_pointsType,
928
+ b2_faceAType,
929
+ b2_faceBType
930
+ } b2SeparationType;
931
+
932
+ typedef struct b2SeparationFunction
933
+ {
934
+ const b2ShapeProxy* proxyA;
935
+ const b2ShapeProxy* proxyB;
936
+ b2Sweep sweepA, sweepB;
937
+ b2Vec2 localPoint;
938
+ b2Vec2 axis;
939
+ b2SeparationType type;
940
+ } b2SeparationFunction;
941
+
942
+ static b2SeparationFunction b2MakeSeparationFunction( const b2SimplexCache* cache, const b2ShapeProxy* proxyA,
943
+ const b2Sweep* sweepA, const b2ShapeProxy* proxyB, const b2Sweep* sweepB,
944
+ float t1 )
945
+ {
946
+ b2SeparationFunction f;
947
+
948
+ f.proxyA = proxyA;
949
+ f.proxyB = proxyB;
950
+ int count = cache->count;
951
+ B2_ASSERT( 0 < count && count < 3 );
952
+
953
+ f.sweepA = *sweepA;
954
+ f.sweepB = *sweepB;
955
+
956
+ b2Transform xfA = b2GetSweepTransform( sweepA, t1 );
957
+ b2Transform xfB = b2GetSweepTransform( sweepB, t1 );
958
+
959
+ if ( count == 1 )
960
+ {
961
+ f.type = b2_pointsType;
962
+ b2Vec2 localPointA = proxyA->points[cache->indexA[0]];
963
+ b2Vec2 localPointB = proxyB->points[cache->indexB[0]];
964
+ b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
965
+ b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
966
+ f.axis = b2Normalize( b2Sub( pointB, pointA ) );
967
+ f.localPoint = b2Vec2_zero;
968
+ return f;
969
+ }
970
+
971
+ if ( cache->indexA[0] == cache->indexA[1] )
972
+ {
973
+ // Two points on B and one on A.
974
+ f.type = b2_faceBType;
975
+ b2Vec2 localPointB1 = proxyB->points[cache->indexB[0]];
976
+ b2Vec2 localPointB2 = proxyB->points[cache->indexB[1]];
977
+
978
+ f.axis = b2CrossVS( b2Sub( localPointB2, localPointB1 ), 1.0f );
979
+ f.axis = b2Normalize( f.axis );
980
+ b2Vec2 normal = b2RotateVector( xfB.q, f.axis );
981
+
982
+ f.localPoint = (b2Vec2){ 0.5f * ( localPointB1.x + localPointB2.x ), 0.5f * ( localPointB1.y + localPointB2.y ) };
983
+ b2Vec2 pointB = b2TransformPoint( xfB, f.localPoint );
984
+
985
+ b2Vec2 localPointA = proxyA->points[cache->indexA[0]];
986
+ b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
987
+
988
+ float s = b2Dot( b2Sub( pointA, pointB ), normal );
989
+ if ( s < 0.0f )
990
+ {
991
+ f.axis = b2Neg( f.axis );
992
+ }
993
+ return f;
994
+ }
995
+
996
+ // Two points on A and one or two points on B.
997
+ f.type = b2_faceAType;
998
+ b2Vec2 localPointA1 = proxyA->points[cache->indexA[0]];
999
+ b2Vec2 localPointA2 = proxyA->points[cache->indexA[1]];
1000
+
1001
+ f.axis = b2CrossVS( b2Sub( localPointA2, localPointA1 ), 1.0f );
1002
+ f.axis = b2Normalize( f.axis );
1003
+ b2Vec2 normal = b2RotateVector( xfA.q, f.axis );
1004
+
1005
+ f.localPoint = (b2Vec2){ 0.5f * ( localPointA1.x + localPointA2.x ), 0.5f * ( localPointA1.y + localPointA2.y ) };
1006
+ b2Vec2 pointA = b2TransformPoint( xfA, f.localPoint );
1007
+
1008
+ b2Vec2 localPointB = proxyB->points[cache->indexB[0]];
1009
+ b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
1010
+
1011
+ float s = b2Dot( b2Sub( pointB, pointA ), normal );
1012
+ if ( s < 0.0f )
1013
+ {
1014
+ f.axis = b2Neg( f.axis );
1015
+ }
1016
+ return f;
1017
+ }
1018
+
1019
+ static float b2FindMinSeparation( const b2SeparationFunction* f, int* indexA, int* indexB, float t )
1020
+ {
1021
+ b2Transform xfA = b2GetSweepTransform( &f->sweepA, t );
1022
+ b2Transform xfB = b2GetSweepTransform( &f->sweepB, t );
1023
+
1024
+ switch ( f->type )
1025
+ {
1026
+ case b2_pointsType:
1027
+ {
1028
+ b2Vec2 axisA = b2InvRotateVector( xfA.q, f->axis );
1029
+ b2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( f->axis ) );
1030
+
1031
+ *indexA = b2FindSupport( f->proxyA, axisA );
1032
+ *indexB = b2FindSupport( f->proxyB, axisB );
1033
+
1034
+ b2Vec2 localPointA = f->proxyA->points[*indexA];
1035
+ b2Vec2 localPointB = f->proxyB->points[*indexB];
1036
+
1037
+ b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
1038
+ b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
1039
+
1040
+ float separation = b2Dot( b2Sub( pointB, pointA ), f->axis );
1041
+ return separation;
1042
+ }
1043
+
1044
+ case b2_faceAType:
1045
+ {
1046
+ b2Vec2 normal = b2RotateVector( xfA.q, f->axis );
1047
+ b2Vec2 pointA = b2TransformPoint( xfA, f->localPoint );
1048
+
1049
+ b2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( normal ) );
1050
+
1051
+ *indexA = -1;
1052
+ *indexB = b2FindSupport( f->proxyB, axisB );
1053
+
1054
+ b2Vec2 localPointB = f->proxyB->points[*indexB];
1055
+ b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
1056
+
1057
+ float separation = b2Dot( b2Sub( pointB, pointA ), normal );
1058
+ return separation;
1059
+ }
1060
+
1061
+ case b2_faceBType:
1062
+ {
1063
+ b2Vec2 normal = b2RotateVector( xfB.q, f->axis );
1064
+ b2Vec2 pointB = b2TransformPoint( xfB, f->localPoint );
1065
+
1066
+ b2Vec2 axisA = b2InvRotateVector( xfA.q, b2Neg( normal ) );
1067
+
1068
+ *indexB = -1;
1069
+ *indexA = b2FindSupport( f->proxyA, axisA );
1070
+
1071
+ b2Vec2 localPointA = f->proxyA->points[*indexA];
1072
+ b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
1073
+
1074
+ float separation = b2Dot( b2Sub( pointA, pointB ), normal );
1075
+ return separation;
1076
+ }
1077
+
1078
+ default:
1079
+ B2_ASSERT( false );
1080
+ *indexA = -1;
1081
+ *indexB = -1;
1082
+ return 0.0f;
1083
+ }
1084
+ }
1085
+
1086
+ //
1087
+ static float b2EvaluateSeparation( const b2SeparationFunction* f, int indexA, int indexB, float t )
1088
+ {
1089
+ b2Transform xfA = b2GetSweepTransform( &f->sweepA, t );
1090
+ b2Transform xfB = b2GetSweepTransform( &f->sweepB, t );
1091
+
1092
+ switch ( f->type )
1093
+ {
1094
+ case b2_pointsType:
1095
+ {
1096
+ b2Vec2 localPointA = f->proxyA->points[indexA];
1097
+ b2Vec2 localPointB = f->proxyB->points[indexB];
1098
+
1099
+ b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
1100
+ b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
1101
+
1102
+ float separation = b2Dot( b2Sub( pointB, pointA ), f->axis );
1103
+ return separation;
1104
+ }
1105
+
1106
+ case b2_faceAType:
1107
+ {
1108
+ b2Vec2 normal = b2RotateVector( xfA.q, f->axis );
1109
+ b2Vec2 pointA = b2TransformPoint( xfA, f->localPoint );
1110
+
1111
+ b2Vec2 localPointB = f->proxyB->points[indexB];
1112
+ b2Vec2 pointB = b2TransformPoint( xfB, localPointB );
1113
+
1114
+ float separation = b2Dot( b2Sub( pointB, pointA ), normal );
1115
+ return separation;
1116
+ }
1117
+
1118
+ case b2_faceBType:
1119
+ {
1120
+ b2Vec2 normal = b2RotateVector( xfB.q, f->axis );
1121
+ b2Vec2 pointB = b2TransformPoint( xfB, f->localPoint );
1122
+
1123
+ b2Vec2 localPointA = f->proxyA->points[indexA];
1124
+ b2Vec2 pointA = b2TransformPoint( xfA, localPointA );
1125
+
1126
+ float separation = b2Dot( b2Sub( pointA, pointB ), normal );
1127
+ return separation;
1128
+ }
1129
+
1130
+ default:
1131
+ B2_ASSERT( false );
1132
+ return 0.0f;
1133
+ }
1134
+ }
1135
+
1136
+ // CCD via the local separating axis method. This seeks progression
1137
+ // by computing the largest time at which separation is maintained.
1138
+ b2TOIOutput b2TimeOfImpact( const b2TOIInput* input )
1139
+ {
1140
+ #if B2_SNOOP_TOI_COUNTERS
1141
+ uint64_t ticks = b2GetTicks();
1142
+ ++b2_toiCalls;
1143
+ #endif
1144
+
1145
+ b2TOIOutput output;
1146
+ output.state = b2_toiStateUnknown;
1147
+ output.fraction = input->maxFraction;
1148
+
1149
+ b2Sweep sweepA = input->sweepA;
1150
+ b2Sweep sweepB = input->sweepB;
1151
+ B2_ASSERT( b2IsNormalizedRot( sweepA.q1 ) && b2IsNormalizedRot( sweepA.q2 ) );
1152
+ B2_ASSERT( b2IsNormalizedRot( sweepB.q1 ) && b2IsNormalizedRot( sweepB.q2 ) );
1153
+
1154
+ // todo_erin
1155
+ // c1 can be at the origin yet the points are far away
1156
+ // b2Vec2 origin = b2Add(sweepA.c1, input->proxyA.points[0]);
1157
+
1158
+ const b2ShapeProxy* proxyA = &input->proxyA;
1159
+ const b2ShapeProxy* proxyB = &input->proxyB;
1160
+
1161
+ float tMax = input->maxFraction;
1162
+
1163
+ float totalRadius = proxyA->radius + proxyB->radius;
1164
+ // todo_erin consider different target
1165
+ // float target = b2MaxFloat( B2_LINEAR_SLOP, totalRadius );
1166
+ float target = b2MaxFloat( B2_LINEAR_SLOP, totalRadius - B2_LINEAR_SLOP );
1167
+ float tolerance = 0.25f * B2_LINEAR_SLOP;
1168
+ B2_ASSERT( target > tolerance );
1169
+
1170
+ float t1 = 0.0f;
1171
+ const int k_maxIterations = 20;
1172
+ int distanceIterations = 0;
1173
+
1174
+ // Prepare input for distance query.
1175
+ b2SimplexCache cache = { 0 };
1176
+ b2DistanceInput distanceInput;
1177
+ distanceInput.proxyA = input->proxyA;
1178
+ distanceInput.proxyB = input->proxyB;
1179
+ distanceInput.useRadii = false;
1180
+
1181
+ // The outer loop progressively attempts to compute new separating axes.
1182
+ // This loop terminates when an axis is repeated (no progress is made).
1183
+ for ( ;; )
1184
+ {
1185
+ b2Transform xfA = b2GetSweepTransform( &sweepA, t1 );
1186
+ b2Transform xfB = b2GetSweepTransform( &sweepB, t1 );
1187
+
1188
+ // Get the distance between shapes. We can also use the results
1189
+ // to get a separating axis.
1190
+ distanceInput.transformA = xfA;
1191
+ distanceInput.transformB = xfB;
1192
+ b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
1193
+
1194
+ // Progressive time of impact. This handles slender geometry well but introduces
1195
+ // significant time loss.
1196
+ // if (distanceIterations == 0)
1197
+ //{
1198
+ // if ( distanceOutput.distance > totalRadius + B2_SPECULATIVE_DISTANCE )
1199
+ // {
1200
+ // target = totalRadius + B2_SPECULATIVE_DISTANCE - tolerance;
1201
+ // }
1202
+ // else
1203
+ // {
1204
+ // target = distanceOutput.distance - 1.5f * tolerance;
1205
+ // target = b2MaxFloat( target, 2.0f * tolerance );
1206
+ // }
1207
+ //}
1208
+
1209
+ distanceIterations += 1;
1210
+ #if B2_SNOOP_TOI_COUNTERS
1211
+ b2_toiDistanceIterations += 1;
1212
+ #endif
1213
+
1214
+ // If the shapes are overlapped, we give up on continuous collision.
1215
+ if ( distanceOutput.distance <= 0.0f )
1216
+ {
1217
+ // Failure!
1218
+ output.state = b2_toiStateOverlapped;
1219
+ #if B2_SNOOP_TOI_COUNTERS
1220
+ b2_toiOverlappedCount += 1;
1221
+ #endif
1222
+ output.fraction = 0.0f;
1223
+ break;
1224
+ }
1225
+
1226
+ if ( distanceOutput.distance <= target + tolerance )
1227
+ {
1228
+ // Victory!
1229
+ output.state = b2_toiStateHit;
1230
+ #if B2_SNOOP_TOI_COUNTERS
1231
+ b2_toiHitCount += 1;
1232
+ #endif
1233
+ output.fraction = t1;
1234
+ break;
1235
+ }
1236
+
1237
+ // Initialize the separating axis.
1238
+ b2SeparationFunction fcn = b2MakeSeparationFunction( &cache, proxyA, &sweepA, proxyB, &sweepB, t1 );
1239
+ #if 0
1240
+ // Dump the curve seen by the root finder
1241
+ {
1242
+ const int N = 100;
1243
+ float dx = 1.0f / N;
1244
+ float xs[N + 1];
1245
+ float fs[N + 1];
1246
+
1247
+ float x = 0.0f;
1248
+
1249
+ for (int i = 0; i <= N; ++i)
1250
+ {
1251
+ sweepA.GetTransform(&xfA, x);
1252
+ sweepB.GetTransform(&xfB, x);
1253
+ float f = fcn.Evaluate(xfA, xfB) - target;
1254
+
1255
+ printf("%g %g\n", x, f);
1256
+
1257
+ xs[i] = x;
1258
+ fs[i] = f;
1259
+
1260
+ x += dx;
1261
+ }
1262
+ }
1263
+ #endif
1264
+
1265
+ // Compute the TOI on the separating axis. We do this by successively
1266
+ // resolving the deepest point. This loop is bounded by the number of vertices.
1267
+ bool done = false;
1268
+ float t2 = tMax;
1269
+ int pushBackIterations = 0;
1270
+ for ( ;; )
1271
+ {
1272
+ // Find the deepest point at t2. Store the witness point indices.
1273
+ int indexA, indexB;
1274
+ float s2 = b2FindMinSeparation( &fcn, &indexA, &indexB, t2 );
1275
+
1276
+ // Is the final configuration separated?
1277
+ if ( s2 > target + tolerance )
1278
+ {
1279
+ // Victory!
1280
+ output.state = b2_toiStateSeparated;
1281
+ #if B2_SNOOP_TOI_COUNTERS
1282
+ b2_toiSeparatedCount += 1;
1283
+ #endif
1284
+ output.fraction = tMax;
1285
+ done = true;
1286
+ break;
1287
+ }
1288
+
1289
+ // Has the separation reached tolerance?
1290
+ if ( s2 > target - tolerance )
1291
+ {
1292
+ // Advance the sweeps
1293
+ t1 = t2;
1294
+ break;
1295
+ }
1296
+
1297
+ // Compute the initial separation of the witness points.
1298
+ float s1 = b2EvaluateSeparation( &fcn, indexA, indexB, t1 );
1299
+
1300
+ // Check for initial overlap. This might happen if the root finder
1301
+ // runs out of iterations.
1302
+ if ( s1 < target - tolerance )
1303
+ {
1304
+ output.state = b2_toiStateFailed;
1305
+ #if B2_SNOOP_TOI_COUNTERS
1306
+ b2_toiFailedCount += 1;
1307
+ #endif
1308
+ output.fraction = t1;
1309
+ done = true;
1310
+ break;
1311
+ }
1312
+
1313
+ // Check for touching
1314
+ if ( s1 <= target + tolerance )
1315
+ {
1316
+ // Victory! t1 should hold the TOI (could be 0.0).
1317
+ output.state = b2_toiStateHit;
1318
+ #if B2_SNOOP_TOI_COUNTERS
1319
+ b2_toiHitCount += 1;
1320
+ #endif
1321
+ output.fraction = t1;
1322
+ done = true;
1323
+ break;
1324
+ }
1325
+
1326
+ // Compute 1D root of: f(x) - target = 0
1327
+ int rootIterationCount = 0;
1328
+ float a1 = t1, a2 = t2;
1329
+ for ( ;; )
1330
+ {
1331
+ // Use a mix of the secant rule and bisection.
1332
+ float t;
1333
+ if ( rootIterationCount & 1 )
1334
+ {
1335
+ // Secant rule to improve convergence.
1336
+ t = a1 + ( target - s1 ) * ( a2 - a1 ) / ( s2 - s1 );
1337
+ }
1338
+ else
1339
+ {
1340
+ // Bisection to guarantee progress.
1341
+ t = 0.5f * ( a1 + a2 );
1342
+ }
1343
+
1344
+ rootIterationCount += 1;
1345
+
1346
+ #if B2_SNOOP_TOI_COUNTERS
1347
+ ++b2_toiRootIterations;
1348
+ #endif
1349
+
1350
+ float s = b2EvaluateSeparation( &fcn, indexA, indexB, t );
1351
+
1352
+ if ( b2AbsFloat( s - target ) < tolerance )
1353
+ {
1354
+ // t2 holds a tentative value for t1
1355
+ t2 = t;
1356
+ break;
1357
+ }
1358
+
1359
+ // Ensure we continue to bracket the root.
1360
+ if ( s > target )
1361
+ {
1362
+ a1 = t;
1363
+ s1 = s;
1364
+ }
1365
+ else
1366
+ {
1367
+ a2 = t;
1368
+ s2 = s;
1369
+ }
1370
+
1371
+ if ( rootIterationCount == 50 )
1372
+ {
1373
+ break;
1374
+ }
1375
+ }
1376
+
1377
+ #if B2_SNOOP_TOI_COUNTERS
1378
+ b2_toiMaxRootIterations = b2MaxInt( b2_toiMaxRootIterations, rootIterationCount );
1379
+ #endif
1380
+
1381
+ pushBackIterations += 1;
1382
+
1383
+ if ( pushBackIterations == B2_MAX_POLYGON_VERTICES )
1384
+ {
1385
+ break;
1386
+ }
1387
+ }
1388
+
1389
+ if ( done )
1390
+ {
1391
+ break;
1392
+ }
1393
+
1394
+ if ( distanceIterations == k_maxIterations )
1395
+ {
1396
+ // Root finder got stuck. Semi-victory.
1397
+ output.state = b2_toiStateFailed;
1398
+ #if B2_SNOOP_TOI_COUNTERS
1399
+ b2_toiFailedCount += 1;
1400
+ #endif
1401
+ output.fraction = t1;
1402
+ break;
1403
+ }
1404
+ }
1405
+
1406
+ #if B2_SNOOP_TOI_COUNTERS
1407
+ b2_toiMaxDistanceIterations = b2MaxInt( b2_toiMaxDistanceIterations, distanceIterations );
1408
+
1409
+ float time = b2GetMilliseconds( ticks );
1410
+ b2_toiMaxTime = b2MaxFloat( b2_toiMaxTime, time );
1411
+ b2_toiTime += time;
1412
+ #endif
1413
+
1414
+ return output;
1415
+ }