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,1028 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "constants.h"
5
+ #include "shape.h"
6
+
7
+ #include "box2d/collision.h"
8
+ #include "box2d/math_functions.h"
9
+
10
+ #include <float.h>
11
+ #include <stddef.h>
12
+
13
+ _Static_assert( B2_MAX_POLYGON_VERTICES > 2, "must be 3 or more" );
14
+
15
+ bool b2IsValidRay( const b2RayCastInput* input )
16
+ {
17
+ bool isValid = b2IsValidVec2( input->origin ) && b2IsValidVec2( input->translation ) &&
18
+ b2IsValidFloat( input->maxFraction ) && 0.0f <= input->maxFraction && input->maxFraction < B2_HUGE;
19
+ return isValid;
20
+ }
21
+
22
+ static b2Vec2 b2ComputePolygonCentroid( const b2Vec2* vertices, int count )
23
+ {
24
+ b2Vec2 center = { 0.0f, 0.0f };
25
+ float area = 0.0f;
26
+
27
+ // Get a reference point for forming triangles.
28
+ // Use the first vertex to reduce round-off errors.
29
+ b2Vec2 origin = vertices[0];
30
+
31
+ const float inv3 = 1.0f / 3.0f;
32
+
33
+ for ( int i = 1; i < count - 1; ++i )
34
+ {
35
+ // Triangle edges
36
+ b2Vec2 e1 = b2Sub( vertices[i], origin );
37
+ b2Vec2 e2 = b2Sub( vertices[i + 1], origin );
38
+ float a = 0.5f * b2Cross( e1, e2 );
39
+
40
+ // Area weighted centroid
41
+ center = b2MulAdd( center, a * inv3, b2Add( e1, e2 ) );
42
+ area += a;
43
+ }
44
+
45
+ B2_ASSERT( area > FLT_EPSILON );
46
+ float invArea = 1.0f / area;
47
+ center.x *= invArea;
48
+ center.y *= invArea;
49
+
50
+ // Restore offset
51
+ center = b2Add( origin, center );
52
+
53
+ return center;
54
+ }
55
+
56
+ b2Polygon b2MakePolygon( const b2Hull* hull, float radius )
57
+ {
58
+ B2_ASSERT( b2ValidateHull( hull ) );
59
+
60
+ if ( hull->count < 3 )
61
+ {
62
+ // Handle a bad hull when assertions are disabled
63
+ return b2MakeSquare( 0.5f );
64
+ }
65
+
66
+ b2Polygon shape = { 0 };
67
+ shape.count = hull->count;
68
+ shape.radius = radius;
69
+
70
+ // Copy vertices
71
+ for ( int i = 0; i < shape.count; ++i )
72
+ {
73
+ shape.vertices[i] = hull->points[i];
74
+ }
75
+
76
+ // Compute normals. Ensure the edges have non-zero length.
77
+ for ( int i = 0; i < shape.count; ++i )
78
+ {
79
+ int i1 = i;
80
+ int i2 = i + 1 < shape.count ? i + 1 : 0;
81
+ b2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] );
82
+ B2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON );
83
+ shape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) );
84
+ }
85
+
86
+ shape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count );
87
+
88
+ return shape;
89
+ }
90
+
91
+ b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation )
92
+ {
93
+ return b2MakeOffsetRoundedPolygon( hull, position, rotation, 0.0f );
94
+ }
95
+
96
+ b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius )
97
+ {
98
+ B2_ASSERT( b2ValidateHull( hull ) );
99
+
100
+ if ( hull->count < 3 )
101
+ {
102
+ // Handle a bad hull when assertions are disabled
103
+ return b2MakeSquare( 0.5f );
104
+ }
105
+
106
+ b2Transform transform = { position, rotation };
107
+
108
+ b2Polygon shape = { 0 };
109
+ shape.count = hull->count;
110
+ shape.radius = radius;
111
+
112
+ // Copy vertices
113
+ for ( int i = 0; i < shape.count; ++i )
114
+ {
115
+ shape.vertices[i] = b2TransformPoint( transform, hull->points[i] );
116
+ }
117
+
118
+ // Compute normals. Ensure the edges have non-zero length.
119
+ for ( int i = 0; i < shape.count; ++i )
120
+ {
121
+ int i1 = i;
122
+ int i2 = i + 1 < shape.count ? i + 1 : 0;
123
+ b2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] );
124
+ B2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON );
125
+ shape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) );
126
+ }
127
+
128
+ shape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count );
129
+
130
+ return shape;
131
+ }
132
+
133
+ b2Polygon b2MakeSquare( float halfWidth )
134
+ {
135
+ return b2MakeBox( halfWidth, halfWidth );
136
+ }
137
+
138
+ b2Polygon b2MakeBox( float halfWidth, float halfHeight )
139
+ {
140
+ B2_ASSERT( b2IsValidFloat( halfWidth ) && halfWidth > 0.0f );
141
+ B2_ASSERT( b2IsValidFloat( halfHeight ) && halfHeight > 0.0f );
142
+
143
+ b2Polygon shape = { 0 };
144
+ shape.count = 4;
145
+ shape.vertices[0] = (b2Vec2){ -halfWidth, -halfHeight };
146
+ shape.vertices[1] = (b2Vec2){ halfWidth, -halfHeight };
147
+ shape.vertices[2] = (b2Vec2){ halfWidth, halfHeight };
148
+ shape.vertices[3] = (b2Vec2){ -halfWidth, halfHeight };
149
+ shape.normals[0] = (b2Vec2){ 0.0f, -1.0f };
150
+ shape.normals[1] = (b2Vec2){ 1.0f, 0.0f };
151
+ shape.normals[2] = (b2Vec2){ 0.0f, 1.0f };
152
+ shape.normals[3] = (b2Vec2){ -1.0f, 0.0f };
153
+ shape.radius = 0.0f;
154
+ shape.centroid = b2Vec2_zero;
155
+ return shape;
156
+ }
157
+
158
+ b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius )
159
+ {
160
+ B2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );
161
+ b2Polygon shape = b2MakeBox( halfWidth, halfHeight );
162
+ shape.radius = radius;
163
+ return shape;
164
+ }
165
+
166
+ b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation )
167
+ {
168
+ b2Transform xf = { center, rotation };
169
+
170
+ b2Polygon shape = { 0 };
171
+ shape.count = 4;
172
+ shape.vertices[0] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, -halfHeight } );
173
+ shape.vertices[1] = b2TransformPoint( xf, (b2Vec2){ halfWidth, -halfHeight } );
174
+ shape.vertices[2] = b2TransformPoint( xf, (b2Vec2){ halfWidth, halfHeight } );
175
+ shape.vertices[3] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, halfHeight } );
176
+ shape.normals[0] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, -1.0f } );
177
+ shape.normals[1] = b2RotateVector( xf.q, (b2Vec2){ 1.0f, 0.0f } );
178
+ shape.normals[2] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, 1.0f } );
179
+ shape.normals[3] = b2RotateVector( xf.q, (b2Vec2){ -1.0f, 0.0f } );
180
+ shape.radius = 0.0f;
181
+ shape.centroid = xf.p;
182
+ return shape;
183
+ }
184
+
185
+ b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius )
186
+ {
187
+ B2_ASSERT( b2IsValidFloat( radius ) && radius >= 0.0f );
188
+ b2Transform xf = { center, rotation };
189
+
190
+ b2Polygon shape = { 0 };
191
+ shape.count = 4;
192
+ shape.vertices[0] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, -halfHeight } );
193
+ shape.vertices[1] = b2TransformPoint( xf, (b2Vec2){ halfWidth, -halfHeight } );
194
+ shape.vertices[2] = b2TransformPoint( xf, (b2Vec2){ halfWidth, halfHeight } );
195
+ shape.vertices[3] = b2TransformPoint( xf, (b2Vec2){ -halfWidth, halfHeight } );
196
+ shape.normals[0] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, -1.0f } );
197
+ shape.normals[1] = b2RotateVector( xf.q, (b2Vec2){ 1.0f, 0.0f } );
198
+ shape.normals[2] = b2RotateVector( xf.q, (b2Vec2){ 0.0f, 1.0f } );
199
+ shape.normals[3] = b2RotateVector( xf.q, (b2Vec2){ -1.0f, 0.0f } );
200
+ shape.radius = radius;
201
+ shape.centroid = xf.p;
202
+ return shape;
203
+ }
204
+
205
+ b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon )
206
+ {
207
+ b2Polygon p = *polygon;
208
+
209
+ for ( int i = 0; i < p.count; ++i )
210
+ {
211
+ p.vertices[i] = b2TransformPoint( transform, p.vertices[i] );
212
+ p.normals[i] = b2RotateVector( transform.q, p.normals[i] );
213
+ }
214
+
215
+ p.centroid = b2TransformPoint( transform, p.centroid );
216
+
217
+ return p;
218
+ }
219
+
220
+ b2MassData b2ComputeCircleMass( const b2Circle* shape, float density )
221
+ {
222
+ float rr = shape->radius * shape->radius;
223
+
224
+ b2MassData massData;
225
+ massData.mass = density * B2_PI * rr;
226
+ massData.center = shape->center;
227
+
228
+ // inertia about the local origin
229
+ massData.rotationalInertia = massData.mass * ( 0.5f * rr + b2Dot( shape->center, shape->center ) );
230
+
231
+ return massData;
232
+ }
233
+
234
+ b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density )
235
+ {
236
+ float radius = shape->radius;
237
+ float rr = radius * radius;
238
+ b2Vec2 p1 = shape->center1;
239
+ b2Vec2 p2 = shape->center2;
240
+ float length = b2Length( b2Sub( p2, p1 ) );
241
+ float ll = length * length;
242
+
243
+ float circleMass = density * ( B2_PI * radius * radius );
244
+ float boxMass = density * ( 2.0f * radius * length );
245
+
246
+ b2MassData massData;
247
+ massData.mass = circleMass + boxMass;
248
+ massData.center.x = 0.5f * ( p1.x + p2.x );
249
+ massData.center.y = 0.5f * ( p1.y + p2.y );
250
+
251
+ // two offset half circles, both halves add up to full circle and each half is offset by half length
252
+ // semi-circle centroid = 4 r / 3 pi
253
+ // Need to apply parallel-axis theorem twice:
254
+ // 1. shift semi-circle centroid to origin
255
+ // 2. shift semi-circle to box end
256
+ // m * ((h + lc)^2 - lc^2) = m * (h^2 + 2 * h * lc)
257
+ // See: https://en.wikipedia.org/wiki/Parallel_axis_theorem
258
+ // I verified this formula by computing the convex hull of a 128 vertex capsule
259
+
260
+ // half circle centroid
261
+ float lc = 4.0f * radius / ( 3.0f * B2_PI );
262
+
263
+ // half length of rectangular portion of capsule
264
+ float h = 0.5f * length;
265
+
266
+ float circleInertia = circleMass * ( 0.5f * rr + h * h + 2.0f * h * lc );
267
+ float boxInertia = boxMass * ( 4.0f * rr + ll ) / 12.0f;
268
+ massData.rotationalInertia = circleInertia + boxInertia;
269
+
270
+ // inertia about the local origin
271
+ massData.rotationalInertia += massData.mass * b2Dot( massData.center, massData.center );
272
+
273
+ return massData;
274
+ }
275
+
276
+ b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density )
277
+ {
278
+ // Polygon mass, centroid, and inertia.
279
+ // Let rho be the polygon density in mass per unit area.
280
+ // Then:
281
+ // mass = rho * int(dA)
282
+ // centroid.x = (1/mass) * rho * int(x * dA)
283
+ // centroid.y = (1/mass) * rho * int(y * dA)
284
+ // I = rho * int((x*x + y*y) * dA)
285
+ //
286
+ // We can compute these integrals by summing all the integrals
287
+ // for each triangle of the polygon. To evaluate the integral
288
+ // for a single triangle, we make a change of variables to
289
+ // the (u,v) coordinates of the triangle:
290
+ // x = x0 + e1x * u + e2x * v
291
+ // y = y0 + e1y * u + e2y * v
292
+ // where 0 <= u && 0 <= v && u + v <= 1.
293
+ //
294
+ // We integrate u from [0,1-v] and then v from [0,1].
295
+ // We also need to use the Jacobian of the transformation:
296
+ // D = cross(e1, e2)
297
+ //
298
+ // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)
299
+ //
300
+ // The rest of the derivation is handled by computer algebra.
301
+
302
+ B2_ASSERT( shape->count > 0 );
303
+
304
+ if ( shape->count == 1 )
305
+ {
306
+ b2Circle circle;
307
+ circle.center = shape->vertices[0];
308
+ circle.radius = shape->radius;
309
+ return b2ComputeCircleMass( &circle, density );
310
+ }
311
+
312
+ if ( shape->count == 2 )
313
+ {
314
+ b2Capsule capsule;
315
+ capsule.center1 = shape->vertices[0];
316
+ capsule.center2 = shape->vertices[1];
317
+ capsule.radius = shape->radius;
318
+ return b2ComputeCapsuleMass( &capsule, density );
319
+ }
320
+
321
+ b2Vec2 vertices[B2_MAX_POLYGON_VERTICES] = { 0 };
322
+ int count = shape->count;
323
+ float radius = shape->radius;
324
+
325
+ if ( radius > 0.0f )
326
+ {
327
+ // Approximate mass of rounded polygons by pushing out the vertices.
328
+ float sqrt2 = 1.412f;
329
+ for ( int i = 0; i < count; ++i )
330
+ {
331
+ int j = i == 0 ? count - 1 : i - 1;
332
+ b2Vec2 n1 = shape->normals[j];
333
+ b2Vec2 n2 = shape->normals[i];
334
+
335
+ b2Vec2 mid = b2Normalize( b2Add( n1, n2 ) );
336
+ vertices[i] = b2MulAdd( shape->vertices[i], sqrt2 * radius, mid );
337
+ }
338
+ }
339
+ else
340
+ {
341
+ for ( int i = 0; i < count; ++i )
342
+ {
343
+ vertices[i] = shape->vertices[i];
344
+ }
345
+ }
346
+
347
+ b2Vec2 center = { 0.0f, 0.0f };
348
+ float area = 0.0f;
349
+ float rotationalInertia = 0.0f;
350
+
351
+ // Get a reference point for forming triangles.
352
+ // Use the first vertex to reduce round-off errors.
353
+ b2Vec2 r = vertices[0];
354
+
355
+ const float inv3 = 1.0f / 3.0f;
356
+
357
+ for ( int i = 1; i < count - 1; ++i )
358
+ {
359
+ // Triangle edges
360
+ b2Vec2 e1 = b2Sub( vertices[i], r );
361
+ b2Vec2 e2 = b2Sub( vertices[i + 1], r );
362
+
363
+ float D = b2Cross( e1, e2 );
364
+
365
+ float triangleArea = 0.5f * D;
366
+ area += triangleArea;
367
+
368
+ // Area weighted centroid, r at origin
369
+ center = b2MulAdd( center, triangleArea * inv3, b2Add( e1, e2 ) );
370
+
371
+ float ex1 = e1.x, ey1 = e1.y;
372
+ float ex2 = e2.x, ey2 = e2.y;
373
+
374
+ float intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;
375
+ float inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;
376
+
377
+ rotationalInertia += ( 0.25f * inv3 * D ) * ( intx2 + inty2 );
378
+ }
379
+
380
+ b2MassData massData;
381
+
382
+ // Total mass
383
+ massData.mass = density * area;
384
+
385
+ // Center of mass, shift back from origin at r
386
+ B2_ASSERT( area > FLT_EPSILON );
387
+ float invArea = 1.0f / area;
388
+ center.x *= invArea;
389
+ center.y *= invArea;
390
+ massData.center = b2Add( r, center );
391
+
392
+ // Inertia tensor relative to the local origin (point s).
393
+ massData.rotationalInertia = density * rotationalInertia;
394
+
395
+ // Shift to center of mass then to original body origin.
396
+ massData.rotationalInertia += massData.mass * ( b2Dot( massData.center, massData.center ) - b2Dot( center, center ) );
397
+
398
+ return massData;
399
+ }
400
+
401
+ b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform xf )
402
+ {
403
+ b2Vec2 p = b2TransformPoint( xf, shape->center );
404
+ float r = shape->radius;
405
+
406
+ b2AABB aabb = { { p.x - r, p.y - r }, { p.x + r, p.y + r } };
407
+ return aabb;
408
+ }
409
+
410
+ b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform xf )
411
+ {
412
+ b2Vec2 v1 = b2TransformPoint( xf, shape->center1 );
413
+ b2Vec2 v2 = b2TransformPoint( xf, shape->center2 );
414
+
415
+ b2Vec2 r = { shape->radius, shape->radius };
416
+ b2Vec2 lower = b2Sub( b2Min( v1, v2 ), r );
417
+ b2Vec2 upper = b2Add( b2Max( v1, v2 ), r );
418
+
419
+ b2AABB aabb = { lower, upper };
420
+ return aabb;
421
+ }
422
+
423
+ b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform xf )
424
+ {
425
+ B2_ASSERT( shape->count > 0 );
426
+ b2Vec2 lower = b2TransformPoint( xf, shape->vertices[0] );
427
+ b2Vec2 upper = lower;
428
+
429
+ for ( int i = 1; i < shape->count; ++i )
430
+ {
431
+ b2Vec2 v = b2TransformPoint( xf, shape->vertices[i] );
432
+ lower = b2Min( lower, v );
433
+ upper = b2Max( upper, v );
434
+ }
435
+
436
+ b2Vec2 r = { shape->radius, shape->radius };
437
+ lower = b2Sub( lower, r );
438
+ upper = b2Add( upper, r );
439
+
440
+ b2AABB aabb = { lower, upper };
441
+ return aabb;
442
+ }
443
+
444
+ b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform xf )
445
+ {
446
+ b2Vec2 v1 = b2TransformPoint( xf, shape->point1 );
447
+ b2Vec2 v2 = b2TransformPoint( xf, shape->point2 );
448
+
449
+ b2Vec2 lower = b2Min( v1, v2 );
450
+ b2Vec2 upper = b2Max( v1, v2 );
451
+
452
+ b2AABB aabb = { lower, upper };
453
+ return aabb;
454
+ }
455
+
456
+ bool b2PointInCircle( b2Vec2 point, const b2Circle* shape )
457
+ {
458
+ b2Vec2 center = shape->center;
459
+ return b2DistanceSquared( point, center ) <= shape->radius * shape->radius;
460
+ }
461
+
462
+ bool b2PointInCapsule( b2Vec2 point, const b2Capsule* shape )
463
+ {
464
+ float rr = shape->radius * shape->radius;
465
+ b2Vec2 p1 = shape->center1;
466
+ b2Vec2 p2 = shape->center2;
467
+
468
+ b2Vec2 d = b2Sub( p2, p1 );
469
+ float dd = b2Dot( d, d );
470
+ if ( dd == 0.0f )
471
+ {
472
+ // Capsule is really a circle
473
+ return b2DistanceSquared( point, p1 ) <= rr;
474
+ }
475
+
476
+ // Get closest point on capsule segment
477
+ // c = p1 + t * d
478
+ // dot(point - c, d) = 0
479
+ // dot(point - p1 - t * d, d) = 0
480
+ // t = dot(point - p1, d) / dot(d, d)
481
+ float t = b2Dot( b2Sub( point, p1 ), d ) / dd;
482
+ t = b2ClampFloat( t, 0.0f, 1.0f );
483
+ b2Vec2 c = b2MulAdd( p1, t, d );
484
+
485
+ // Is query point within radius around closest point?
486
+ return b2DistanceSquared( point, c ) <= rr;
487
+ }
488
+
489
+ bool b2PointInPolygon( b2Vec2 point, const b2Polygon* shape )
490
+ {
491
+ b2DistanceInput input = { 0 };
492
+ input.proxyA = b2MakeProxy( shape->vertices, shape->count, 0.0f );
493
+ input.proxyB = b2MakeProxy( &point, 1, 0.0f );
494
+ input.transformA = b2Transform_identity;
495
+ input.transformB = b2Transform_identity;
496
+ input.useRadii = false;
497
+
498
+ b2SimplexCache cache = { 0 };
499
+ b2DistanceOutput output = b2ShapeDistance( &input, &cache, NULL, 0 );
500
+
501
+ return output.distance <= shape->radius;
502
+ }
503
+
504
+ // Precision Improvements for Ray / Sphere Intersection - Ray Tracing Gems 2019
505
+ // http://www.codercorner.com/blog/?p=321
506
+ b2CastOutput b2RayCastCircle( const b2RayCastInput* input, const b2Circle* shape )
507
+ {
508
+ B2_ASSERT( b2IsValidRay( input ) );
509
+
510
+ b2Vec2 p = shape->center;
511
+
512
+ b2CastOutput output = { 0 };
513
+
514
+ // Shift ray so circle center is the origin
515
+ b2Vec2 s = b2Sub( input->origin, p );
516
+ float length;
517
+ b2Vec2 d = b2GetLengthAndNormalize( &length, input->translation );
518
+ if ( length == 0.0f )
519
+ {
520
+ // zero length ray
521
+ return output;
522
+ }
523
+
524
+ // Find closest point on ray to origin
525
+
526
+ // solve: dot(s + t * d, d) = 0
527
+ float t = -b2Dot( s, d );
528
+
529
+ // c is the closest point on the line to the origin
530
+ b2Vec2 c = b2MulAdd( s, t, d );
531
+
532
+ float cc = b2Dot( c, c );
533
+ float r = shape->radius;
534
+ float rr = r * r;
535
+
536
+ if ( cc > rr )
537
+ {
538
+ // closest point is outside the circle
539
+ return output;
540
+ }
541
+
542
+ // Pythagoras
543
+ float h = sqrtf( rr - cc );
544
+
545
+ float fraction = t - h;
546
+
547
+ if ( fraction < 0.0f || input->maxFraction * length < fraction )
548
+ {
549
+ // outside the range of the ray segment
550
+ return output;
551
+ }
552
+
553
+ // hit point relative to center
554
+ b2Vec2 hitPoint = b2MulAdd( s, fraction, d );
555
+
556
+ output.fraction = fraction / length;
557
+ output.normal = b2Normalize( hitPoint );
558
+ output.point = b2MulAdd( p, shape->radius, output.normal );
559
+ output.hit = true;
560
+
561
+ return output;
562
+ }
563
+
564
+ b2CastOutput b2RayCastCapsule( const b2RayCastInput* input, const b2Capsule* shape )
565
+ {
566
+ B2_ASSERT( b2IsValidRay( input ) );
567
+
568
+ b2CastOutput output = { 0 };
569
+
570
+ b2Vec2 v1 = shape->center1;
571
+ b2Vec2 v2 = shape->center2;
572
+
573
+ b2Vec2 e = b2Sub( v2, v1 );
574
+
575
+ float capsuleLength;
576
+ b2Vec2 a = b2GetLengthAndNormalize( &capsuleLength, e );
577
+
578
+ if ( capsuleLength < FLT_EPSILON )
579
+ {
580
+ // Capsule is really a circle
581
+ b2Circle circle = { v1, shape->radius };
582
+ return b2RayCastCircle( input, &circle );
583
+ }
584
+
585
+ b2Vec2 p1 = input->origin;
586
+ b2Vec2 d = input->translation;
587
+
588
+ // Ray from capsule start to ray start
589
+ b2Vec2 q = b2Sub( p1, v1 );
590
+ float qa = b2Dot( q, a );
591
+
592
+ // Vector to ray start that is perpendicular to capsule axis
593
+ b2Vec2 qp = b2MulAdd( q, -qa, a );
594
+
595
+ float radius = shape->radius;
596
+
597
+ // Does the ray start within the infinite length capsule?
598
+ if ( b2Dot( qp, qp ) < radius * radius )
599
+ {
600
+ if ( qa < 0.0f )
601
+ {
602
+ // start point behind capsule segment
603
+ b2Circle circle = { v1, shape->radius };
604
+ return b2RayCastCircle( input, &circle );
605
+ }
606
+
607
+ if ( qa > 1.0f )
608
+ {
609
+ // start point ahead of capsule segment
610
+ b2Circle circle = { v2, shape->radius };
611
+ return b2RayCastCircle( input, &circle );
612
+ }
613
+
614
+ // ray starts inside capsule -> no hit
615
+ return output;
616
+ }
617
+
618
+ // Perpendicular to capsule axis, pointing right
619
+ b2Vec2 n = { a.y, -a.x };
620
+
621
+ float rayLength;
622
+ b2Vec2 u = b2GetLengthAndNormalize( &rayLength, d );
623
+
624
+ // Intersect ray with infinite length capsule
625
+ // v1 + radius * n + s1 * a = p1 + s2 * u
626
+ // v1 - radius * n + s1 * a = p1 + s2 * u
627
+
628
+ // s1 * a - s2 * u = b
629
+ // b = q - radius * ap
630
+ // or
631
+ // b = q + radius * ap
632
+
633
+ // Cramer's rule [a -u]
634
+ float den = -a.x * u.y + u.x * a.y;
635
+ if ( -FLT_EPSILON < den && den < FLT_EPSILON )
636
+ {
637
+ // Ray is parallel to capsule and outside infinite length capsule
638
+ return output;
639
+ }
640
+
641
+ b2Vec2 b1 = b2MulSub( q, radius, n );
642
+ b2Vec2 b2 = b2MulAdd( q, radius, n );
643
+
644
+ float invDen = 1.0f / den;
645
+
646
+ // Cramer's rule [a b1]
647
+ float s21 = ( a.x * b1.y - b1.x * a.y ) * invDen;
648
+
649
+ // Cramer's rule [a b2]
650
+ float s22 = ( a.x * b2.y - b2.x * a.y ) * invDen;
651
+
652
+ float s2;
653
+ b2Vec2 b;
654
+ if ( s21 < s22 )
655
+ {
656
+ s2 = s21;
657
+ b = b1;
658
+ }
659
+ else
660
+ {
661
+ s2 = s22;
662
+ b = b2;
663
+ n = b2Neg( n );
664
+ }
665
+
666
+ if ( s2 < 0.0f || input->maxFraction * rayLength < s2 )
667
+ {
668
+ return output;
669
+ }
670
+
671
+ // Cramer's rule [b -u]
672
+ float s1 = ( -b.x * u.y + u.x * b.y ) * invDen;
673
+
674
+ if ( s1 < 0.0f )
675
+ {
676
+ // ray passes behind capsule segment
677
+ b2Circle circle = { v1, shape->radius };
678
+ return b2RayCastCircle( input, &circle );
679
+ }
680
+ else if ( capsuleLength < s1 )
681
+ {
682
+ // ray passes ahead of capsule segment
683
+ b2Circle circle = { v2, shape->radius };
684
+ return b2RayCastCircle( input, &circle );
685
+ }
686
+ else
687
+ {
688
+ // ray hits capsule side
689
+ output.fraction = s2 / rayLength;
690
+ output.point = b2Add( b2Lerp( v1, v2, s1 / capsuleLength ), b2MulSV( shape->radius, n ) );
691
+ output.normal = n;
692
+ output.hit = true;
693
+ return output;
694
+ }
695
+ }
696
+
697
+ // Ray vs line segment
698
+ b2CastOutput b2RayCastSegment( const b2RayCastInput* input, const b2Segment* shape, bool oneSided )
699
+ {
700
+ if ( oneSided )
701
+ {
702
+ // Skip left-side collision
703
+ float offset = b2Cross( b2Sub( input->origin, shape->point1 ), b2Sub( shape->point2, shape->point1 ) );
704
+ if ( offset < 0.0f )
705
+ {
706
+ b2CastOutput output = { 0 };
707
+ return output;
708
+ }
709
+ }
710
+
711
+ // Put the ray into the edge's frame of reference.
712
+ b2Vec2 p1 = input->origin;
713
+ b2Vec2 d = input->translation;
714
+
715
+ b2Vec2 v1 = shape->point1;
716
+ b2Vec2 v2 = shape->point2;
717
+ b2Vec2 e = b2Sub( v2, v1 );
718
+
719
+ b2CastOutput output = { 0 };
720
+
721
+ float length;
722
+ b2Vec2 eUnit = b2GetLengthAndNormalize( &length, e );
723
+ if ( length == 0.0f )
724
+ {
725
+ return output;
726
+ }
727
+
728
+ // Normal points to the right, looking from v1 towards v2
729
+ b2Vec2 normal = b2RightPerp( eUnit );
730
+
731
+ // Intersect ray with infinite segment using normal
732
+ // Similar to intersecting a ray with an infinite plane
733
+ // p = p1 + t * d
734
+ // dot(normal, p - v1) = 0
735
+ // dot(normal, p1 - v1) + t * dot(normal, d) = 0
736
+ float numerator = b2Dot( normal, b2Sub( v1, p1 ) );
737
+ float denominator = b2Dot( normal, d );
738
+
739
+ if ( denominator == 0.0f )
740
+ {
741
+ // parallel
742
+ return output;
743
+ }
744
+
745
+ float t = numerator / denominator;
746
+ if ( t < 0.0f || input->maxFraction < t )
747
+ {
748
+ // out of ray range
749
+ return output;
750
+ }
751
+
752
+ // Intersection point on infinite segment
753
+ b2Vec2 p = b2MulAdd( p1, t, d );
754
+
755
+ // Compute position of p along segment
756
+ // p = v1 + s * e
757
+ // s = dot(p - v1, e) / dot(e, e)
758
+
759
+ float s = b2Dot( b2Sub( p, v1 ), eUnit );
760
+ if ( s < 0.0f || length < s )
761
+ {
762
+ // out of segment range
763
+ return output;
764
+ }
765
+
766
+ if ( numerator > 0.0f )
767
+ {
768
+ normal = b2Neg( normal );
769
+ }
770
+
771
+ output.fraction = t;
772
+ output.point = p;
773
+ output.normal = normal;
774
+ output.hit = true;
775
+
776
+ return output;
777
+ }
778
+
779
+ b2CastOutput b2RayCastPolygon( const b2RayCastInput* input, const b2Polygon* shape )
780
+ {
781
+ B2_ASSERT( b2IsValidRay( input ) );
782
+
783
+ if ( shape->radius == 0.0f )
784
+ {
785
+ // Put the ray into the polygon's frame of reference.
786
+ b2Vec2 p1 = input->origin;
787
+ b2Vec2 d = input->translation;
788
+
789
+ float lower = 0.0f, upper = input->maxFraction;
790
+
791
+ int index = -1;
792
+
793
+ b2CastOutput output = { 0 };
794
+
795
+ for ( int i = 0; i < shape->count; ++i )
796
+ {
797
+ // p = p1 + a * d
798
+ // dot(normal, p - v) = 0
799
+ // dot(normal, p1 - v) + a * dot(normal, d) = 0
800
+ float numerator = b2Dot( shape->normals[i], b2Sub( shape->vertices[i], p1 ) );
801
+ float denominator = b2Dot( shape->normals[i], d );
802
+
803
+ if ( denominator == 0.0f )
804
+ {
805
+ if ( numerator < 0.0f )
806
+ {
807
+ return output;
808
+ }
809
+ }
810
+ else
811
+ {
812
+ // Note: we want this predicate without division:
813
+ // lower < numerator / denominator, where denominator < 0
814
+ // Since denominator < 0, we have to flip the inequality:
815
+ // lower < numerator / denominator <==> denominator * lower > numerator.
816
+ if ( denominator < 0.0f && numerator < lower * denominator )
817
+ {
818
+ // Increase lower.
819
+ // The segment enters this half-space.
820
+ lower = numerator / denominator;
821
+ index = i;
822
+ }
823
+ else if ( denominator > 0.0f && numerator < upper * denominator )
824
+ {
825
+ // Decrease upper.
826
+ // The segment exits this half-space.
827
+ upper = numerator / denominator;
828
+ }
829
+ }
830
+
831
+ // The use of epsilon here causes the B2_ASSERT on lower to trip
832
+ // in some cases. Apparently the use of epsilon was to make edge
833
+ // shapes work, but now those are handled separately.
834
+ // if (upper < lower - b2_epsilon)
835
+ if ( upper < lower )
836
+ {
837
+ return output;
838
+ }
839
+ }
840
+
841
+ B2_ASSERT( 0.0f <= lower && lower <= input->maxFraction );
842
+
843
+ if ( index >= 0 )
844
+ {
845
+ output.fraction = lower;
846
+ output.normal = shape->normals[index];
847
+ output.point = b2MulAdd( p1, lower, d );
848
+ output.hit = true;
849
+ }
850
+
851
+ return output;
852
+ }
853
+
854
+ // TODO_ERIN this is not working for ray vs box (zero radii)
855
+ b2ShapeCastPairInput castInput;
856
+ castInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );
857
+ castInput.proxyB = b2MakeProxy( &input->origin, 1, 0.0f );
858
+ castInput.transformA = b2Transform_identity;
859
+ castInput.transformB = b2Transform_identity;
860
+ castInput.translationB = input->translation;
861
+ castInput.maxFraction = input->maxFraction;
862
+ castInput.canEncroach = false;
863
+ return b2ShapeCast( &castInput );
864
+ }
865
+
866
+ b2CastOutput b2ShapeCastCircle( const b2ShapeCastInput* input, const b2Circle* shape )
867
+ {
868
+ b2ShapeCastPairInput pairInput;
869
+ pairInput.proxyA = b2MakeProxy( &shape->center, 1, shape->radius );
870
+ pairInput.proxyB = input->proxy;
871
+ pairInput.transformA = b2Transform_identity;
872
+ pairInput.transformB = b2Transform_identity;
873
+ pairInput.translationB = input->translation;
874
+ pairInput.maxFraction = input->maxFraction;
875
+ pairInput.canEncroach = input->canEncroach;
876
+
877
+ b2CastOutput output = b2ShapeCast( &pairInput );
878
+ return output;
879
+ }
880
+
881
+ b2CastOutput b2ShapeCastCapsule( const b2ShapeCastInput* input, const b2Capsule* shape )
882
+ {
883
+ b2ShapeCastPairInput pairInput;
884
+ pairInput.proxyA = b2MakeProxy( &shape->center1, 2, shape->radius );
885
+ pairInput.proxyB = input->proxy;
886
+ pairInput.transformA = b2Transform_identity;
887
+ pairInput.transformB = b2Transform_identity;
888
+ pairInput.translationB = input->translation;
889
+ pairInput.maxFraction = input->maxFraction;
890
+ pairInput.canEncroach = input->canEncroach;
891
+
892
+ b2CastOutput output = b2ShapeCast( &pairInput );
893
+ return output;
894
+ }
895
+
896
+ b2CastOutput b2ShapeCastSegment( const b2ShapeCastInput* input, const b2Segment* shape )
897
+ {
898
+ b2ShapeCastPairInput pairInput;
899
+ pairInput.proxyA = b2MakeProxy( &shape->point1, 2, 0.0f );
900
+ pairInput.proxyB = input->proxy;
901
+ pairInput.transformA = b2Transform_identity;
902
+ pairInput.transformB = b2Transform_identity;
903
+ pairInput.translationB = input->translation;
904
+ pairInput.maxFraction = input->maxFraction;
905
+ pairInput.canEncroach = input->canEncroach;
906
+
907
+ b2CastOutput output = b2ShapeCast( &pairInput );
908
+ return output;
909
+ }
910
+
911
+ b2CastOutput b2ShapeCastPolygon( const b2ShapeCastInput* input, const b2Polygon* shape )
912
+ {
913
+ b2ShapeCastPairInput pairInput;
914
+ pairInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );
915
+ pairInput.proxyB = input->proxy;
916
+ pairInput.transformA = b2Transform_identity;
917
+ pairInput.transformB = b2Transform_identity;
918
+ pairInput.translationB = input->translation;
919
+ pairInput.maxFraction = input->maxFraction;
920
+ pairInput.canEncroach = input->canEncroach;
921
+
922
+ b2CastOutput output = b2ShapeCast( &pairInput );
923
+ return output;
924
+ }
925
+
926
+ b2PlaneResult b2CollideMoverAndCircle( const b2Circle* shape, const b2Capsule* mover )
927
+ {
928
+ b2DistanceInput distanceInput;
929
+ distanceInput.proxyA = b2MakeProxy( &shape->center, 1, 0.0f );
930
+ distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
931
+ distanceInput.transformA = b2Transform_identity;
932
+ distanceInput.transformB = b2Transform_identity;
933
+ distanceInput.useRadii = false;
934
+
935
+ float totalRadius = mover->radius + shape->radius;
936
+
937
+ b2SimplexCache cache = { 0 };
938
+ b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
939
+
940
+ if ( distanceOutput.distance <= totalRadius )
941
+ {
942
+ b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
943
+ return (b2PlaneResult){
944
+ .plane = plane,
945
+ .hit = true,
946
+ };
947
+ }
948
+
949
+ return (b2PlaneResult){ 0 };
950
+ }
951
+
952
+ b2PlaneResult b2CollideMoverAndCapsule( const b2Capsule* shape, const b2Capsule* mover )
953
+ {
954
+ b2DistanceInput distanceInput;
955
+ distanceInput.proxyA = b2MakeProxy( &shape->center1, 2, 0.0f );
956
+ distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
957
+ distanceInput.transformA = b2Transform_identity;
958
+ distanceInput.transformB = b2Transform_identity;
959
+ distanceInput.useRadii = false;
960
+
961
+ float totalRadius = mover->radius + shape->radius;
962
+
963
+ b2SimplexCache cache = { 0 };
964
+ b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
965
+
966
+ if ( distanceOutput.distance <= totalRadius )
967
+ {
968
+ b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
969
+ return (b2PlaneResult){
970
+ .plane = plane,
971
+ .hit = true,
972
+ };
973
+ }
974
+
975
+ return (b2PlaneResult){ 0 };
976
+ }
977
+
978
+ b2PlaneResult b2CollideMoverAndPolygon( const b2Polygon* shape, const b2Capsule* mover )
979
+ {
980
+ b2DistanceInput distanceInput;
981
+ distanceInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius );
982
+ distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
983
+ distanceInput.transformA = b2Transform_identity;
984
+ distanceInput.transformB = b2Transform_identity;
985
+ distanceInput.useRadii = false;
986
+
987
+ float totalRadius = mover->radius + shape->radius;
988
+
989
+ b2SimplexCache cache = { 0 };
990
+ b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
991
+
992
+ if ( distanceOutput.distance <= totalRadius )
993
+ {
994
+ b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
995
+ return (b2PlaneResult){
996
+ .plane = plane,
997
+ .hit = true,
998
+ };
999
+ }
1000
+
1001
+ return (b2PlaneResult){ 0 };
1002
+ }
1003
+
1004
+ b2PlaneResult b2CollideMoverAndSegment( const b2Segment* shape, const b2Capsule* mover )
1005
+ {
1006
+ b2DistanceInput distanceInput;
1007
+ distanceInput.proxyA = b2MakeProxy( &shape->point1, 2, 0.0f );
1008
+ distanceInput.proxyB = b2MakeProxy( &mover->center1, 2, mover->radius );
1009
+ distanceInput.transformA = b2Transform_identity;
1010
+ distanceInput.transformB = b2Transform_identity;
1011
+ distanceInput.useRadii = false;
1012
+
1013
+ float totalRadius = mover->radius;
1014
+
1015
+ b2SimplexCache cache = { 0 };
1016
+ b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceInput, &cache, NULL, 0 );
1017
+
1018
+ if ( distanceOutput.distance <= totalRadius )
1019
+ {
1020
+ b2Plane plane = { distanceOutput.normal, totalRadius - distanceOutput.distance };
1021
+ return (b2PlaneResult){
1022
+ .plane = plane,
1023
+ .hit = true,
1024
+ };
1025
+ }
1026
+
1027
+ return (b2PlaneResult){ 0 };
1028
+ }