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,1726 @@
1
+ // SPDX-FileCopyrightText: 2023 Erin Catto
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ #include "constants.h"
5
+ #include "core.h"
6
+
7
+ #include "box2d/collision.h"
8
+ #include "box2d/math_functions.h"
9
+
10
+ #include <float.h>
11
+ #include <stddef.h>
12
+ #include <stdlib.h>
13
+
14
+ #define B2_MAKE_ID( A, B ) ( (uint8_t)( A ) << 8 | (uint8_t)( B ) )
15
+
16
+ static b2Polygon b2MakeCapsule( b2Vec2 p1, b2Vec2 p2, float radius )
17
+ {
18
+ b2Polygon shape = { 0 };
19
+ shape.vertices[0] = p1;
20
+ shape.vertices[1] = p2;
21
+ shape.centroid = b2Lerp( p1, p2, 0.5f );
22
+
23
+ b2Vec2 d = b2Sub( p2, p1 );
24
+ B2_ASSERT( b2LengthSquared( d ) > FLT_EPSILON );
25
+ b2Vec2 axis = b2Normalize( d );
26
+ b2Vec2 normal = b2RightPerp( axis );
27
+
28
+ shape.normals[0] = normal;
29
+ shape.normals[1] = b2Neg( normal );
30
+ shape.count = 2;
31
+ shape.radius = radius;
32
+
33
+ return shape;
34
+ }
35
+
36
+ // point = qA * localAnchorA + pA
37
+ // localAnchorB = qBc * (point - pB)
38
+ // anchorB = point - pB = qA * localAnchorA + pA - pB
39
+ // = anchorA + (pA - pB)
40
+ b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
41
+ {
42
+ b2Manifold manifold = { 0 };
43
+
44
+ b2Transform xf = b2InvMulTransforms( xfA, xfB );
45
+
46
+ b2Vec2 pointA = circleA->center;
47
+ b2Vec2 pointB = b2TransformPoint( xf, circleB->center );
48
+
49
+ float distance;
50
+ b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pointB, pointA ) );
51
+
52
+ float radiusA = circleA->radius;
53
+ float radiusB = circleB->radius;
54
+
55
+ float separation = distance - radiusA - radiusB;
56
+ if ( separation > B2_SPECULATIVE_DISTANCE )
57
+ {
58
+ return manifold;
59
+ }
60
+
61
+ b2Vec2 cA = b2MulAdd( pointA, radiusA, normal );
62
+ b2Vec2 cB = b2MulAdd( pointB, -radiusB, normal );
63
+ b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
64
+
65
+ manifold.normal = b2RotateVector( xfA.q, normal );
66
+ b2ManifoldPoint* mp = manifold.points + 0;
67
+ mp->anchorA = b2RotateVector( xfA.q, contactPointA );
68
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
69
+ mp->point = b2Add( mp->anchorA, xfA.p );
70
+ mp->separation = separation;
71
+ mp->id = 0;
72
+ manifold.pointCount = 1;
73
+ return manifold;
74
+ }
75
+
76
+ /// Compute the collision manifold between a capsule and circle
77
+ b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
78
+ {
79
+ b2Manifold manifold = { 0 };
80
+
81
+ b2Transform xf = b2InvMulTransforms( xfA, xfB );
82
+
83
+ // Compute circle position in the frame of the capsule.
84
+ b2Vec2 pB = b2TransformPoint( xf, circleB->center );
85
+
86
+ // Compute closest point
87
+ b2Vec2 p1 = capsuleA->center1;
88
+ b2Vec2 p2 = capsuleA->center2;
89
+
90
+ b2Vec2 e = b2Sub( p2, p1 );
91
+
92
+ // dot(p - pA, e) = 0
93
+ // dot(p - (p1 + s1 * e), e) = 0
94
+ // s1 = dot(p - p1, e)
95
+ b2Vec2 pA;
96
+ float s1 = b2Dot( b2Sub( pB, p1 ), e );
97
+ float s2 = b2Dot( b2Sub( p2, pB ), e );
98
+ if ( s1 < 0.0f )
99
+ {
100
+ // p1 region
101
+ pA = p1;
102
+ }
103
+ else if ( s2 < 0.0f )
104
+ {
105
+ // p2 region
106
+ pA = p2;
107
+ }
108
+ else
109
+ {
110
+ // circle colliding with segment interior
111
+ float s = s1 / b2Dot( e, e );
112
+ pA = b2MulAdd( p1, s, e );
113
+ }
114
+
115
+ float distance;
116
+ b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) );
117
+
118
+ float radiusA = capsuleA->radius;
119
+ float radiusB = circleB->radius;
120
+ float separation = distance - radiusA - radiusB;
121
+ if ( separation > B2_SPECULATIVE_DISTANCE )
122
+ {
123
+ return manifold;
124
+ }
125
+
126
+ b2Vec2 cA = b2MulAdd( pA, radiusA, normal );
127
+ b2Vec2 cB = b2MulAdd( pB, -radiusB, normal );
128
+ b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
129
+
130
+ manifold.normal = b2RotateVector( xfA.q, normal );
131
+ b2ManifoldPoint* mp = manifold.points + 0;
132
+ mp->anchorA = b2RotateVector( xfA.q, contactPointA );
133
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
134
+ mp->point = b2Add( xfA.p, mp->anchorA );
135
+ mp->separation = separation;
136
+ mp->id = 0;
137
+ manifold.pointCount = 1;
138
+ return manifold;
139
+ }
140
+
141
+ b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
142
+ {
143
+ b2Manifold manifold = { 0 };
144
+ const float speculativeDistance = B2_SPECULATIVE_DISTANCE;
145
+
146
+ b2Transform xf = b2InvMulTransforms( xfA, xfB );
147
+
148
+ // Compute circle position in the frame of the polygon.
149
+ b2Vec2 center = b2TransformPoint( xf, circleB->center );
150
+ float radiusA = polygonA->radius;
151
+ float radiusB = circleB->radius;
152
+ float radius = radiusA + radiusB;
153
+
154
+ // Find the min separating edge.
155
+ int normalIndex = 0;
156
+ float separation = -FLT_MAX;
157
+ int vertexCount = polygonA->count;
158
+ const b2Vec2* vertices = polygonA->vertices;
159
+ const b2Vec2* normals = polygonA->normals;
160
+
161
+ for ( int i = 0; i < vertexCount; ++i )
162
+ {
163
+ float s = b2Dot( normals[i], b2Sub( center, vertices[i] ) );
164
+ if ( s > separation )
165
+ {
166
+ separation = s;
167
+ normalIndex = i;
168
+ }
169
+ }
170
+
171
+ if ( separation > radius + speculativeDistance )
172
+ {
173
+ return manifold;
174
+ }
175
+
176
+ // Vertices of the reference edge.
177
+ int vertIndex1 = normalIndex;
178
+ int vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
179
+ b2Vec2 v1 = vertices[vertIndex1];
180
+ b2Vec2 v2 = vertices[vertIndex2];
181
+
182
+ // Compute barycentric coordinates
183
+ float u1 = b2Dot( b2Sub( center, v1 ), b2Sub( v2, v1 ) );
184
+ float u2 = b2Dot( b2Sub( center, v2 ), b2Sub( v1, v2 ) );
185
+
186
+ if ( u1 < 0.0f && separation > FLT_EPSILON )
187
+ {
188
+ // Circle center is closest to v1 and safely outside the polygon
189
+ b2Vec2 normal = b2Normalize( b2Sub( center, v1 ) );
190
+ separation = b2Dot( b2Sub( center, v1 ), normal );
191
+ if ( separation > radius + speculativeDistance )
192
+ {
193
+ return manifold;
194
+ }
195
+
196
+ b2Vec2 cA = b2MulAdd( v1, radiusA, normal );
197
+ b2Vec2 cB = b2MulSub( center, radiusB, normal );
198
+ b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
199
+
200
+ manifold.normal = b2RotateVector( xfA.q, normal );
201
+ b2ManifoldPoint* mp = manifold.points + 0;
202
+ mp->anchorA = b2RotateVector( xfA.q, contactPointA );
203
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
204
+ mp->point = b2Add( xfA.p, mp->anchorA );
205
+ mp->separation = b2Dot( b2Sub( cB, cA ), normal );
206
+ mp->id = 0;
207
+ manifold.pointCount = 1;
208
+ }
209
+ else if ( u2 < 0.0f && separation > FLT_EPSILON )
210
+ {
211
+ // Circle center is closest to v2 and safely outside the polygon
212
+ b2Vec2 normal = b2Normalize( b2Sub( center, v2 ) );
213
+ separation = b2Dot( b2Sub( center, v2 ), normal );
214
+ if ( separation > radius + speculativeDistance )
215
+ {
216
+ return manifold;
217
+ }
218
+
219
+ b2Vec2 cA = b2MulAdd( v2, radiusA, normal );
220
+ b2Vec2 cB = b2MulSub( center, radiusB, normal );
221
+ b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
222
+
223
+ manifold.normal = b2RotateVector( xfA.q, normal );
224
+ b2ManifoldPoint* mp = manifold.points + 0;
225
+ mp->anchorA = b2RotateVector( xfA.q, contactPointA );
226
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
227
+ mp->point = b2Add( xfA.p, mp->anchorA );
228
+ mp->separation = b2Dot( b2Sub( cB, cA ), normal );
229
+ mp->id = 0;
230
+ manifold.pointCount = 1;
231
+ }
232
+ else
233
+ {
234
+ // Circle center is between v1 and v2. Center may be inside polygon
235
+ b2Vec2 normal = normals[normalIndex];
236
+ manifold.normal = b2RotateVector( xfA.q, normal );
237
+
238
+ // cA is the projection of the circle center onto to the reference edge
239
+ b2Vec2 cA = b2MulAdd( center, radiusA - b2Dot( b2Sub( center, v1 ), normal ), normal );
240
+
241
+ // cB is the deepest point on the circle with respect to the reference edge
242
+ b2Vec2 cB = b2MulSub( center, radiusB, normal );
243
+
244
+ b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
245
+
246
+ // The contact point is the midpoint in world space
247
+ b2ManifoldPoint* mp = manifold.points + 0;
248
+ mp->anchorA = b2RotateVector( xfA.q, contactPointA );
249
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
250
+ mp->point = b2Add( xfA.p, mp->anchorA );
251
+ mp->separation = separation - radius;
252
+ mp->id = 0;
253
+ manifold.pointCount = 1;
254
+ }
255
+
256
+ return manifold;
257
+ }
258
+
259
+ // Follows Ericson 5.1.9 Closest Points of Two Line Segments
260
+ // Adds some logic to support clipping to get two contact points
261
+ b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )
262
+ {
263
+ b2Vec2 origin = capsuleA->center1;
264
+
265
+ // Shift polyA to origin
266
+ // pw = q * pb + p
267
+ // pw = q * (pbs + origin) + p
268
+ // pw = q * pbs + (p + q * origin)
269
+ b2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q };
270
+ b2Transform xf = b2InvMulTransforms( sfA, xfB );
271
+
272
+ b2Vec2 p1 = b2Vec2_zero;
273
+ b2Vec2 q1 = b2Sub( capsuleA->center2, origin );
274
+
275
+ b2Vec2 p2 = b2TransformPoint( xf, capsuleB->center1 );
276
+ b2Vec2 q2 = b2TransformPoint( xf, capsuleB->center2 );
277
+
278
+ b2Vec2 d1 = b2Sub( q1, p1 );
279
+ b2Vec2 d2 = b2Sub( q2, p2 );
280
+
281
+ float dd1 = b2Dot( d1, d1 );
282
+ float dd2 = b2Dot( d2, d2 );
283
+
284
+ const float epsSqr = FLT_EPSILON * FLT_EPSILON;
285
+ B2_ASSERT( dd1 > epsSqr && dd2 > epsSqr );
286
+
287
+ b2Vec2 r = b2Sub( p1, p2 );
288
+ float rd1 = b2Dot( r, d1 );
289
+ float rd2 = b2Dot( r, d2 );
290
+
291
+ float d12 = b2Dot( d1, d2 );
292
+
293
+ float denom = dd1 * dd2 - d12 * d12;
294
+
295
+ // Fraction on segment 1
296
+ float f1 = 0.0f;
297
+ if ( denom != 0.0f )
298
+ {
299
+ // not parallel
300
+ f1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denom, 0.0f, 1.0f );
301
+ }
302
+
303
+ // Compute point on segment 2 closest to p1 + f1 * d1
304
+ float f2 = ( d12 * f1 + rd2 ) / dd2;
305
+
306
+ // Clamping of segment 2 requires a do over on segment 1
307
+ if ( f2 < 0.0f )
308
+ {
309
+ f2 = 0.0f;
310
+ f1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f );
311
+ }
312
+ else if ( f2 > 1.0f )
313
+ {
314
+ f2 = 1.0f;
315
+ f1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f );
316
+ }
317
+
318
+ b2Vec2 closest1 = b2MulAdd( p1, f1, d1 );
319
+ b2Vec2 closest2 = b2MulAdd( p2, f2, d2 );
320
+ float distanceSquared = b2DistanceSquared( closest1, closest2 );
321
+
322
+ b2Manifold manifold = { 0 };
323
+ float radiusA = capsuleA->radius;
324
+ float radiusB = capsuleB->radius;
325
+ float radius = radiusA + radiusB;
326
+ float maxDistance = radius + B2_SPECULATIVE_DISTANCE;
327
+
328
+ if ( distanceSquared > maxDistance * maxDistance )
329
+ {
330
+ return manifold;
331
+ }
332
+
333
+ float distance = sqrtf( distanceSquared );
334
+
335
+ float length1, length2;
336
+ b2Vec2 u1 = b2GetLengthAndNormalize( &length1, d1 );
337
+ b2Vec2 u2 = b2GetLengthAndNormalize( &length2, d2 );
338
+
339
+ // Does segment B project outside segment A?
340
+ float fp2 = b2Dot( b2Sub( p2, p1 ), u1 );
341
+ float fq2 = b2Dot( b2Sub( q2, p1 ), u1 );
342
+ bool outsideA = ( fp2 <= 0.0f && fq2 <= 0.0f ) || ( fp2 >= length1 && fq2 >= length1 );
343
+
344
+ // Does segment A project outside segment B?
345
+ float fp1 = b2Dot( b2Sub( p1, p2 ), u2 );
346
+ float fq1 = b2Dot( b2Sub( q1, p2 ), u2 );
347
+ bool outsideB = ( fp1 <= 0.0f && fq1 <= 0.0f ) || ( fp1 >= length2 && fq1 >= length2 );
348
+
349
+ if ( outsideA == false && outsideB == false )
350
+ {
351
+ // attempt to clip
352
+ // this may yield contact points with excessive separation
353
+ // in that case the algorithm falls back to single point collision
354
+
355
+ // find reference edge using SAT
356
+ b2Vec2 normalA;
357
+ float separationA;
358
+
359
+ {
360
+ normalA = b2LeftPerp( u1 );
361
+ float ss1 = b2Dot( b2Sub( p2, p1 ), normalA );
362
+ float ss2 = b2Dot( b2Sub( q2, p1 ), normalA );
363
+ float s1p = ss1 < ss2 ? ss1 : ss2;
364
+ float s1n = -ss1 < -ss2 ? -ss1 : -ss2;
365
+
366
+ if ( s1p > s1n )
367
+ {
368
+ separationA = s1p;
369
+ }
370
+ else
371
+ {
372
+ separationA = s1n;
373
+ normalA = b2Neg( normalA );
374
+ }
375
+ }
376
+
377
+ b2Vec2 normalB;
378
+ float separationB;
379
+ {
380
+ normalB = b2LeftPerp( u2 );
381
+ float ss1 = b2Dot( b2Sub( p1, p2 ), normalB );
382
+ float ss2 = b2Dot( b2Sub( q1, p2 ), normalB );
383
+ float s1p = ss1 < ss2 ? ss1 : ss2;
384
+ float s1n = -ss1 < -ss2 ? -ss1 : -ss2;
385
+
386
+ if ( s1p > s1n )
387
+ {
388
+ separationB = s1p;
389
+ }
390
+ else
391
+ {
392
+ separationB = s1n;
393
+ normalB = b2Neg( normalB );
394
+ }
395
+ }
396
+
397
+ // biased to avoid feature flip-flop
398
+ // todo more testing?
399
+ if ( separationA + 0.1f * B2_LINEAR_SLOP >= separationB )
400
+ {
401
+ manifold.normal = normalA;
402
+
403
+ b2Vec2 cp = p2;
404
+ b2Vec2 cq = q2;
405
+
406
+ // clip to p1
407
+ if ( fp2 < 0.0f && fq2 > 0.0f )
408
+ {
409
+ cp = b2Lerp( p2, q2, ( 0.0f - fp2 ) / ( fq2 - fp2 ) );
410
+ }
411
+ else if ( fq2 < 0.0f && fp2 > 0.0f )
412
+ {
413
+ cq = b2Lerp( q2, p2, ( 0.0f - fq2 ) / ( fp2 - fq2 ) );
414
+ }
415
+
416
+ // clip to q1
417
+ if ( fp2 > length1 && fq2 < length1 )
418
+ {
419
+ cp = b2Lerp( p2, q2, ( fp2 - length1 ) / ( fp2 - fq2 ) );
420
+ }
421
+ else if ( fq2 > length1 && fp2 < length1 )
422
+ {
423
+ cq = b2Lerp( q2, p2, ( fq2 - length1 ) / ( fq2 - fp2 ) );
424
+ }
425
+
426
+ float sp = b2Dot( b2Sub( cp, p1 ), normalA );
427
+ float sq = b2Dot( b2Sub( cq, p1 ), normalA );
428
+
429
+ if ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP )
430
+ {
431
+ b2ManifoldPoint* mp;
432
+ mp = manifold.points + 0;
433
+ mp->anchorA = b2MulAdd( cp, 0.5f * ( radiusA - radiusB - sp ), normalA );
434
+ mp->separation = sp - radius;
435
+ mp->id = B2_MAKE_ID( 0, 0 );
436
+
437
+ mp = manifold.points + 1;
438
+ mp->anchorA = b2MulAdd( cq, 0.5f * ( radiusA - radiusB - sq ), normalA );
439
+ mp->separation = sq - radius;
440
+ mp->id = B2_MAKE_ID( 0, 1 );
441
+ manifold.pointCount = 2;
442
+ }
443
+ }
444
+ else
445
+ {
446
+ // normal always points from A to B
447
+ manifold.normal = b2Neg( normalB );
448
+
449
+ b2Vec2 cp = p1;
450
+ b2Vec2 cq = q1;
451
+
452
+ // clip to p2
453
+ if ( fp1 < 0.0f && fq1 > 0.0f )
454
+ {
455
+ cp = b2Lerp( p1, q1, ( 0.0f - fp1 ) / ( fq1 - fp1 ) );
456
+ }
457
+ else if ( fq1 < 0.0f && fp1 > 0.0f )
458
+ {
459
+ cq = b2Lerp( q1, p1, ( 0.0f - fq1 ) / ( fp1 - fq1 ) );
460
+ }
461
+
462
+ // clip to q2
463
+ if ( fp1 > length2 && fq1 < length2 )
464
+ {
465
+ cp = b2Lerp( p1, q1, ( fp1 - length2 ) / ( fp1 - fq1 ) );
466
+ }
467
+ else if ( fq1 > length2 && fp1 < length2 )
468
+ {
469
+ cq = b2Lerp( q1, p1, ( fq1 - length2 ) / ( fq1 - fp1 ) );
470
+ }
471
+
472
+ float sp = b2Dot( b2Sub( cp, p2 ), normalB );
473
+ float sq = b2Dot( b2Sub( cq, p2 ), normalB );
474
+
475
+ if ( sp <= distance + B2_LINEAR_SLOP || sq <= distance + B2_LINEAR_SLOP )
476
+ {
477
+ b2ManifoldPoint* mp;
478
+ mp = manifold.points + 0;
479
+ mp->anchorA = b2MulAdd( cp, 0.5f * ( radiusB - radiusA - sp ), normalB );
480
+ mp->separation = sp - radius;
481
+ mp->id = B2_MAKE_ID( 0, 0 );
482
+ mp = manifold.points + 1;
483
+ mp->anchorA = b2MulAdd( cq, 0.5f * ( radiusB - radiusA - sq ), normalB );
484
+ mp->separation = sq - radius;
485
+ mp->id = B2_MAKE_ID( 1, 0 );
486
+ manifold.pointCount = 2;
487
+ }
488
+ }
489
+ }
490
+
491
+ if ( manifold.pointCount == 0 )
492
+ {
493
+ // single point collision
494
+ b2Vec2 normal = b2Sub( closest2, closest1 );
495
+ if ( b2Dot( normal, normal ) > epsSqr )
496
+ {
497
+ normal = b2Normalize( normal );
498
+ }
499
+ else
500
+ {
501
+ normal = b2LeftPerp( u1 );
502
+ }
503
+
504
+ b2Vec2 c1 = b2MulAdd( closest1, radiusA, normal );
505
+ b2Vec2 c2 = b2MulAdd( closest2, -radiusB, normal );
506
+
507
+ int i1 = f1 == 0.0f ? 0 : 1;
508
+ int i2 = f2 == 0.0f ? 0 : 1;
509
+
510
+ manifold.normal = normal;
511
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
512
+ manifold.points[0].separation = sqrtf( distanceSquared ) - radius;
513
+ manifold.points[0].id = B2_MAKE_ID( i1, i2 );
514
+ manifold.pointCount = 1;
515
+ }
516
+
517
+ // Convert manifold to world space
518
+ manifold.normal = b2RotateVector( xfA.q, manifold.normal );
519
+ for ( int i = 0; i < manifold.pointCount; ++i )
520
+ {
521
+ b2ManifoldPoint* mp = manifold.points + i;
522
+
523
+ // anchor points relative to shape origin in world space
524
+ mp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) );
525
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
526
+ mp->point = b2Add( xfA.p, mp->anchorA );
527
+ }
528
+
529
+ return manifold;
530
+ }
531
+
532
+ b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )
533
+ {
534
+ b2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f };
535
+ return b2CollideCapsules( &capsuleA, xfA, capsuleB, xfB );
536
+ }
537
+
538
+ b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB )
539
+ {
540
+ b2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius );
541
+ return b2CollidePolygons( polygonA, xfA, &polyB, xfB );
542
+ }
543
+
544
+ // Polygon clipper used to compute contact points when there are potentially two contact points.
545
+ static b2Manifold b2ClipPolygons( const b2Polygon* polyA, const b2Polygon* polyB, int edgeA, int edgeB, bool flip )
546
+ {
547
+ b2Manifold manifold = { 0 };
548
+
549
+ // reference polygon
550
+ const b2Polygon* poly1;
551
+ int i11, i12;
552
+
553
+ // incident polygon
554
+ const b2Polygon* poly2;
555
+ int i21, i22;
556
+
557
+ if ( flip )
558
+ {
559
+ poly1 = polyB;
560
+ poly2 = polyA;
561
+ i11 = edgeB;
562
+ i12 = edgeB + 1 < polyB->count ? edgeB + 1 : 0;
563
+ i21 = edgeA;
564
+ i22 = edgeA + 1 < polyA->count ? edgeA + 1 : 0;
565
+ }
566
+ else
567
+ {
568
+ poly1 = polyA;
569
+ poly2 = polyB;
570
+ i11 = edgeA;
571
+ i12 = edgeA + 1 < polyA->count ? edgeA + 1 : 0;
572
+ i21 = edgeB;
573
+ i22 = edgeB + 1 < polyB->count ? edgeB + 1 : 0;
574
+ }
575
+
576
+ b2Vec2 normal = poly1->normals[i11];
577
+
578
+ // Reference edge vertices
579
+ b2Vec2 v11 = poly1->vertices[i11];
580
+ b2Vec2 v12 = poly1->vertices[i12];
581
+
582
+ // Incident edge vertices
583
+ b2Vec2 v21 = poly2->vertices[i21];
584
+ b2Vec2 v22 = poly2->vertices[i22];
585
+
586
+ b2Vec2 tangent = b2CrossSV( 1.0f, normal );
587
+
588
+ float lower1 = 0.0f;
589
+ float upper1 = b2Dot( b2Sub( v12, v11 ), tangent );
590
+
591
+ // Incident edge points opposite of tangent due to CCW winding
592
+ float upper2 = b2Dot( b2Sub( v21, v11 ), tangent );
593
+ float lower2 = b2Dot( b2Sub( v22, v11 ), tangent );
594
+
595
+ // Are the segments disjoint?
596
+ if ( upper2 < lower1 || upper1 < lower2 )
597
+ {
598
+ return manifold;
599
+ }
600
+
601
+ b2Vec2 vLower;
602
+ if ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON )
603
+ {
604
+ vLower = b2Lerp( v22, v21, ( lower1 - lower2 ) / ( upper2 - lower2 ) );
605
+ }
606
+ else
607
+ {
608
+ vLower = v22;
609
+ }
610
+
611
+ b2Vec2 vUpper;
612
+ if ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON )
613
+ {
614
+ vUpper = b2Lerp( v22, v21, ( upper1 - lower2 ) / ( upper2 - lower2 ) );
615
+ }
616
+ else
617
+ {
618
+ vUpper = v21;
619
+ }
620
+
621
+ // todo vLower can be very close to vUpper, reduce to one point?
622
+
623
+ float separationLower = b2Dot( b2Sub( vLower, v11 ), normal );
624
+ float separationUpper = b2Dot( b2Sub( vUpper, v11 ), normal );
625
+
626
+ float r1 = poly1->radius;
627
+ float r2 = poly2->radius;
628
+
629
+ // Put contact points at midpoint, accounting for radii
630
+ vLower = b2MulAdd( vLower, 0.5f * ( r1 - r2 - separationLower ), normal );
631
+ vUpper = b2MulAdd( vUpper, 0.5f * ( r1 - r2 - separationUpper ), normal );
632
+
633
+ float radius = r1 + r2;
634
+
635
+ if ( flip == false )
636
+ {
637
+ manifold.normal = normal;
638
+ b2ManifoldPoint* cp = manifold.points + 0;
639
+
640
+ {
641
+ cp->anchorA = vLower;
642
+ cp->separation = separationLower - radius;
643
+ cp->id = B2_MAKE_ID( i11, i22 );
644
+ manifold.pointCount += 1;
645
+ cp += 1;
646
+ }
647
+
648
+ {
649
+ cp->anchorA = vUpper;
650
+ cp->separation = separationUpper - radius;
651
+ cp->id = B2_MAKE_ID( i12, i21 );
652
+ manifold.pointCount += 1;
653
+ }
654
+ }
655
+ else
656
+ {
657
+ manifold.normal = b2Neg( normal );
658
+ b2ManifoldPoint* cp = manifold.points + 0;
659
+
660
+ {
661
+ cp->anchorA = vUpper;
662
+ cp->separation = separationUpper - radius;
663
+ cp->id = B2_MAKE_ID( i21, i12 );
664
+ manifold.pointCount += 1;
665
+ cp += 1;
666
+ }
667
+
668
+ {
669
+ cp->anchorA = vLower;
670
+ cp->separation = separationLower - radius;
671
+ cp->id = B2_MAKE_ID( i22, i11 );
672
+ manifold.pointCount += 1;
673
+ }
674
+ }
675
+
676
+ return manifold;
677
+ }
678
+
679
+ // Find the max separation between poly1 and poly2 using edge normals from poly1.
680
+ static float b2FindMaxSeparation( int* edgeIndex, const b2Polygon* poly1, const b2Polygon* poly2 )
681
+ {
682
+ int count1 = poly1->count;
683
+ int count2 = poly2->count;
684
+ const b2Vec2* n1s = poly1->normals;
685
+ const b2Vec2* v1s = poly1->vertices;
686
+ const b2Vec2* v2s = poly2->vertices;
687
+
688
+ int bestIndex = 0;
689
+ float maxSeparation = -FLT_MAX;
690
+ for ( int i = 0; i < count1; ++i )
691
+ {
692
+ // Get poly1 normal in frame2.
693
+ b2Vec2 n = n1s[i];
694
+ b2Vec2 v1 = v1s[i];
695
+
696
+ // Find the deepest point for normal i.
697
+ float si = FLT_MAX;
698
+ for ( int j = 0; j < count2; ++j )
699
+ {
700
+ float sij = b2Dot( n, b2Sub( v2s[j], v1 ) );
701
+ if ( sij < si )
702
+ {
703
+ si = sij;
704
+ }
705
+ }
706
+
707
+ if ( si > maxSeparation )
708
+ {
709
+ maxSeparation = si;
710
+ bestIndex = i;
711
+ }
712
+ }
713
+
714
+ *edgeIndex = bestIndex;
715
+ return maxSeparation;
716
+ }
717
+
718
+ // Due to speculation, every polygon is rounded
719
+ // Algorithm:
720
+ //
721
+ // compute edge separation using the separating axis test (SAT)
722
+ // if (separation > speculation_distance)
723
+ // return
724
+ // find reference and incident edge
725
+ // if separation >= 0.1f * B2_LINEAR_SLOP
726
+ // compute closest points between reference and incident edge
727
+ // if vertices are closest
728
+ // single vertex-vertex contact
729
+ // else
730
+ // clip edges
731
+ // end
732
+ // else
733
+ // clip edges
734
+ // end
735
+
736
+ b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB )
737
+ {
738
+ b2Vec2 origin = polygonA->vertices[0];
739
+ float linearSlop = B2_LINEAR_SLOP;
740
+ float speculativeDistance = B2_SPECULATIVE_DISTANCE;
741
+
742
+ // Shift polyA to origin
743
+ // pw = q * pb + p
744
+ // pw = q * (pbs + origin) + p
745
+ // pw = q * pbs + (p + q * origin)
746
+ b2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q };
747
+ b2Transform xf = b2InvMulTransforms( sfA, xfB );
748
+
749
+ b2Polygon localPolyA;
750
+ localPolyA.count = polygonA->count;
751
+ localPolyA.radius = polygonA->radius;
752
+ localPolyA.vertices[0] = b2Vec2_zero;
753
+ localPolyA.normals[0] = polygonA->normals[0];
754
+ for ( int i = 1; i < localPolyA.count; ++i )
755
+ {
756
+ localPolyA.vertices[i] = b2Sub( polygonA->vertices[i], origin );
757
+ localPolyA.normals[i] = polygonA->normals[i];
758
+ }
759
+
760
+ // Put polyB in polyA's frame to reduce round-off error
761
+ b2Polygon localPolyB;
762
+ localPolyB.count = polygonB->count;
763
+ localPolyB.radius = polygonB->radius;
764
+ for ( int i = 0; i < localPolyB.count; ++i )
765
+ {
766
+ localPolyB.vertices[i] = b2TransformPoint( xf, polygonB->vertices[i] );
767
+ localPolyB.normals[i] = b2RotateVector( xf.q, polygonB->normals[i] );
768
+ }
769
+
770
+ int edgeA = 0;
771
+ float separationA = b2FindMaxSeparation( &edgeA, &localPolyA, &localPolyB );
772
+
773
+ int edgeB = 0;
774
+ float separationB = b2FindMaxSeparation( &edgeB, &localPolyB, &localPolyA );
775
+
776
+ float radius = localPolyA.radius + localPolyB.radius;
777
+
778
+ if ( separationA > speculativeDistance + radius || separationB > speculativeDistance + radius )
779
+ {
780
+ return (b2Manifold){ 0 };
781
+ }
782
+
783
+ // Find incident edge
784
+ bool flip;
785
+ if ( separationA >= separationB )
786
+ {
787
+ flip = false;
788
+
789
+ b2Vec2 searchDirection = localPolyA.normals[edgeA];
790
+
791
+ // Find the incident edge on polyB
792
+ int count = localPolyB.count;
793
+ const b2Vec2* normals = localPolyB.normals;
794
+ edgeB = 0;
795
+ float minDot = FLT_MAX;
796
+ for ( int i = 0; i < count; ++i )
797
+ {
798
+ float dot = b2Dot( searchDirection, normals[i] );
799
+ if ( dot < minDot )
800
+ {
801
+ minDot = dot;
802
+ edgeB = i;
803
+ }
804
+ }
805
+ }
806
+ else
807
+ {
808
+ flip = true;
809
+
810
+ b2Vec2 searchDirection = localPolyB.normals[edgeB];
811
+
812
+ // Find the incident edge on polyA
813
+ int count = localPolyA.count;
814
+ const b2Vec2* normals = localPolyA.normals;
815
+ edgeA = 0;
816
+ float minDot = FLT_MAX;
817
+ for ( int i = 0; i < count; ++i )
818
+ {
819
+ float dot = b2Dot( searchDirection, normals[i] );
820
+ if ( dot < minDot )
821
+ {
822
+ minDot = dot;
823
+ edgeA = i;
824
+ }
825
+ }
826
+ }
827
+
828
+ b2Manifold manifold = { 0 };
829
+
830
+ // Using slop here to ensure vertex-vertex normal vectors can be safely normalized
831
+ // todo this means edge clipping needs to handle slightly non-overlapping edges.
832
+ if ( separationA > 0.1f * linearSlop || separationB > 0.1f * linearSlop )
833
+ {
834
+ #if 1
835
+ // Edges are disjoint. Find closest points between reference edge and incident edge
836
+ // Reference edge on polygon A
837
+ int i11 = edgeA;
838
+ int i12 = edgeA + 1 < localPolyA.count ? edgeA + 1 : 0;
839
+ int i21 = edgeB;
840
+ int i22 = edgeB + 1 < localPolyB.count ? edgeB + 1 : 0;
841
+
842
+ b2Vec2 v11 = localPolyA.vertices[i11];
843
+ b2Vec2 v12 = localPolyA.vertices[i12];
844
+ b2Vec2 v21 = localPolyB.vertices[i21];
845
+ b2Vec2 v22 = localPolyB.vertices[i22];
846
+
847
+ b2SegmentDistanceResult result = b2SegmentDistance( v11, v12, v21, v22 );
848
+ B2_ASSERT( result.distanceSquared > 0.0f );
849
+ float distance = sqrtf( result.distanceSquared );
850
+ float separation = distance - radius;
851
+
852
+ if ( distance - radius > speculativeDistance )
853
+ {
854
+ // This can happen in the vertex-vertex case
855
+ return manifold;
856
+ }
857
+
858
+ // Attempt to clip edges
859
+ manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );
860
+
861
+ float minSeparation = FLT_MAX;
862
+ for ( int i = 0; i < manifold.pointCount; ++i )
863
+ {
864
+ minSeparation = b2MinFloat( minSeparation, manifold.points[i].separation );
865
+ }
866
+
867
+ // Does vertex-vertex have substantially larger separation?
868
+ if ( separation + 0.1f * linearSlop < minSeparation )
869
+ {
870
+ if ( result.fraction1 == 0.0f && result.fraction2 == 0.0f )
871
+ {
872
+ // v11 - v21
873
+ b2Vec2 normal = b2Sub( v21, v11 );
874
+ float invDistance = 1.0f / distance;
875
+ normal.x *= invDistance;
876
+ normal.y *= invDistance;
877
+
878
+ b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
879
+ b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
880
+
881
+ manifold.normal = normal;
882
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
883
+ manifold.points[0].separation = distance - radius;
884
+ manifold.points[0].id = B2_MAKE_ID( i11, i21 );
885
+ manifold.pointCount = 1;
886
+ }
887
+ else if ( result.fraction1 == 0.0f && result.fraction2 == 1.0f )
888
+ {
889
+ // v11 - v22
890
+ b2Vec2 normal = b2Sub( v22, v11 );
891
+ float invDistance = 1.0f / distance;
892
+ normal.x *= invDistance;
893
+ normal.y *= invDistance;
894
+
895
+ b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
896
+ b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
897
+
898
+ manifold.normal = normal;
899
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
900
+ manifold.points[0].separation = distance - radius;
901
+ manifold.points[0].id = B2_MAKE_ID( i11, i22 );
902
+ manifold.pointCount = 1;
903
+ }
904
+ else if ( result.fraction1 == 1.0f && result.fraction2 == 0.0f )
905
+ {
906
+ // v12 - v21
907
+ b2Vec2 normal = b2Sub( v21, v12 );
908
+ float invDistance = 1.0f / distance;
909
+ normal.x *= invDistance;
910
+ normal.y *= invDistance;
911
+
912
+ b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
913
+ b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
914
+
915
+ manifold.normal = normal;
916
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
917
+ manifold.points[0].separation = distance - radius;
918
+ manifold.points[0].id = B2_MAKE_ID( i12, i21 );
919
+ manifold.pointCount = 1;
920
+ }
921
+ else if ( result.fraction1 == 1.0f && result.fraction2 == 1.0f )
922
+ {
923
+ // v12 - v22
924
+ b2Vec2 normal = b2Sub( v22, v12 );
925
+ float invDistance = 1.0f / distance;
926
+ normal.x *= invDistance;
927
+ normal.y *= invDistance;
928
+
929
+ b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
930
+ b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
931
+
932
+ manifold.normal = normal;
933
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
934
+ manifold.points[0].separation = distance - radius;
935
+ manifold.points[0].id = B2_MAKE_ID( i12, i22 );
936
+ manifold.pointCount = 1;
937
+ }
938
+ }
939
+ #else
940
+ // Polygons are disjoint. Find closest points between reference edge and incident edge
941
+ // Reference edge on polygon A
942
+ int i11 = edgeA;
943
+ int i12 = edgeA + 1 < localPolyA.count ? edgeA + 1 : 0;
944
+ int i21 = edgeB;
945
+ int i22 = edgeB + 1 < localPolyB.count ? edgeB + 1 : 0;
946
+
947
+ b2Vec2 v11 = localPolyA.vertices[i11];
948
+ b2Vec2 v12 = localPolyA.vertices[i12];
949
+ b2Vec2 v21 = localPolyB.vertices[i21];
950
+ b2Vec2 v22 = localPolyB.vertices[i22];
951
+
952
+ b2SegmentDistanceResult result = b2SegmentDistance( v11, v12, v21, v22 );
953
+
954
+ if ( result.fraction1 == 0.0f && result.fraction2 == 0.0f )
955
+ {
956
+ // v11 - v21
957
+ b2Vec2 normal = b2Sub( v21, v11 );
958
+ B2_ASSERT( result.distanceSquared > 0.0f );
959
+ float distance = sqrtf( result.distanceSquared );
960
+ if ( distance > B2_SPECULATIVE_DISTANCE + radius )
961
+ {
962
+ return manifold;
963
+ }
964
+ float invDistance = 1.0f / distance;
965
+ normal.x *= invDistance;
966
+ normal.y *= invDistance;
967
+
968
+ b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
969
+ b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
970
+
971
+ manifold.normal = normal;
972
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
973
+ manifold.points[0].separation = distance - radius;
974
+ manifold.points[0].id = B2_MAKE_ID( i11, i21 );
975
+ manifold.pointCount = 1;
976
+ }
977
+ else if ( result.fraction1 == 0.0f && result.fraction2 == 1.0f )
978
+ {
979
+ // v11 - v22
980
+ b2Vec2 normal = b2Sub( v22, v11 );
981
+ B2_ASSERT( result.distanceSquared > 0.0f );
982
+ float distance = sqrtf( result.distanceSquared );
983
+ if ( distance > B2_SPECULATIVE_DISTANCE + radius )
984
+ {
985
+ return manifold;
986
+ }
987
+ float invDistance = 1.0f / distance;
988
+ normal.x *= invDistance;
989
+ normal.y *= invDistance;
990
+
991
+ b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal );
992
+ b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
993
+
994
+ manifold.normal = normal;
995
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
996
+ manifold.points[0].separation = distance - radius;
997
+ manifold.points[0].id = B2_MAKE_ID( i11, i22 );
998
+ manifold.pointCount = 1;
999
+ }
1000
+ else if ( result.fraction1 == 1.0f && result.fraction2 == 0.0f )
1001
+ {
1002
+ // v12 - v21
1003
+ b2Vec2 normal = b2Sub( v21, v12 );
1004
+ B2_ASSERT( result.distanceSquared > 0.0f );
1005
+ float distance = sqrtf( result.distanceSquared );
1006
+ if ( distance > B2_SPECULATIVE_DISTANCE + radius )
1007
+ {
1008
+ return manifold;
1009
+ }
1010
+ float invDistance = 1.0f / distance;
1011
+ normal.x *= invDistance;
1012
+ normal.y *= invDistance;
1013
+
1014
+ b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
1015
+ b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal );
1016
+
1017
+ manifold.normal = normal;
1018
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
1019
+ manifold.points[0].separation = distance - radius;
1020
+ manifold.points[0].id = B2_MAKE_ID( i12, i21 );
1021
+ manifold.pointCount = 1;
1022
+ }
1023
+ else if ( result.fraction1 == 1.0f && result.fraction2 == 1.0f )
1024
+ {
1025
+ // v12 - v22
1026
+ b2Vec2 normal = b2Sub( v22, v12 );
1027
+ B2_ASSERT( result.distanceSquared > 0.0f );
1028
+ float distance = sqrtf( result.distanceSquared );
1029
+ if ( distance > B2_SPECULATIVE_DISTANCE + radius )
1030
+ {
1031
+ return manifold;
1032
+ }
1033
+ float invDistance = 1.0f / distance;
1034
+ normal.x *= invDistance;
1035
+ normal.y *= invDistance;
1036
+
1037
+ b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal );
1038
+ b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal );
1039
+
1040
+ manifold.normal = normal;
1041
+ manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f );
1042
+ manifold.points[0].separation = distance - radius;
1043
+ manifold.points[0].id = B2_MAKE_ID( i12, i22 );
1044
+ manifold.pointCount = 1;
1045
+ }
1046
+ else
1047
+ {
1048
+ // Edge region
1049
+ manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );
1050
+ }
1051
+ #endif
1052
+ }
1053
+ else
1054
+ {
1055
+ // Polygons overlap
1056
+ manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip );
1057
+ }
1058
+
1059
+ // Convert manifold to world space
1060
+ if ( manifold.pointCount > 0 )
1061
+ {
1062
+ manifold.normal = b2RotateVector( xfA.q, manifold.normal );
1063
+ for ( int i = 0; i < manifold.pointCount; ++i )
1064
+ {
1065
+ b2ManifoldPoint* mp = manifold.points + i;
1066
+
1067
+ // anchor points relative to shape origin in world space
1068
+ mp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) );
1069
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
1070
+ mp->point = b2Add( xfA.p, mp->anchorA );
1071
+ }
1072
+ }
1073
+
1074
+ return manifold;
1075
+ }
1076
+
1077
+ b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB )
1078
+ {
1079
+ b2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f };
1080
+ return b2CollideCapsuleAndCircle( &capsuleA, xfA, circleB, xfB );
1081
+ }
1082
+
1083
+ b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB )
1084
+ {
1085
+ b2Polygon polygonA = b2MakeCapsule( segmentA->point1, segmentA->point2, 0.0f );
1086
+ return b2CollidePolygons( &polygonA, xfA, polygonB, xfB );
1087
+ }
1088
+
1089
+ b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB,
1090
+ b2Transform xfB )
1091
+ {
1092
+ b2Manifold manifold = { 0 };
1093
+
1094
+ b2Transform xf = b2InvMulTransforms( xfA, xfB );
1095
+
1096
+ // Compute circle in frame of segment
1097
+ b2Vec2 pB = b2TransformPoint( xf, circleB->center );
1098
+
1099
+ b2Vec2 p1 = segmentA->segment.point1;
1100
+ b2Vec2 p2 = segmentA->segment.point2;
1101
+ b2Vec2 e = b2Sub( p2, p1 );
1102
+
1103
+ // Normal points to the right
1104
+ float offset = b2Dot( b2RightPerp( e ), b2Sub( pB, p1 ) );
1105
+ if ( offset < 0.0f )
1106
+ {
1107
+ // collision is one-sided
1108
+ return manifold;
1109
+ }
1110
+
1111
+ // Barycentric coordinates
1112
+ float u = b2Dot( e, b2Sub( p2, pB ) );
1113
+ float v = b2Dot( e, b2Sub( pB, p1 ) );
1114
+
1115
+ b2Vec2 pA;
1116
+
1117
+ if ( v <= 0.0f )
1118
+ {
1119
+ // Behind point1?
1120
+ // Is pB in the Voronoi region of the previous edge?
1121
+ b2Vec2 prevEdge = b2Sub( p1, segmentA->ghost1 );
1122
+ float uPrev = b2Dot( prevEdge, b2Sub( pB, p1 ) );
1123
+ if ( uPrev <= 0.0f )
1124
+ {
1125
+ return manifold;
1126
+ }
1127
+
1128
+ pA = p1;
1129
+ }
1130
+ else if ( u <= 0.0f )
1131
+ {
1132
+ // Ahead of point2?
1133
+ b2Vec2 nextEdge = b2Sub( segmentA->ghost2, p2 );
1134
+ float vNext = b2Dot( nextEdge, b2Sub( pB, p2 ) );
1135
+
1136
+ // Is pB in the Voronoi region of the next edge?
1137
+ if ( vNext > 0.0f )
1138
+ {
1139
+ return manifold;
1140
+ }
1141
+
1142
+ pA = p2;
1143
+ }
1144
+ else
1145
+ {
1146
+ float ee = b2Dot( e, e );
1147
+ pA = (b2Vec2){ u * p1.x + v * p2.x, u * p1.y + v * p2.y };
1148
+ pA = ee > 0.0f ? b2MulSV( 1.0f / ee, pA ) : p1;
1149
+ }
1150
+
1151
+ float distance;
1152
+ b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) );
1153
+
1154
+ float radius = circleB->radius;
1155
+ float separation = distance - radius;
1156
+ if ( separation > B2_SPECULATIVE_DISTANCE )
1157
+ {
1158
+ return manifold;
1159
+ }
1160
+
1161
+ b2Vec2 cA = pA;
1162
+ b2Vec2 cB = b2MulAdd( pB, -radius, normal );
1163
+ b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f );
1164
+
1165
+ manifold.normal = b2RotateVector( xfA.q, normal );
1166
+
1167
+ b2ManifoldPoint* mp = manifold.points + 0;
1168
+ mp->anchorA = b2RotateVector( xfA.q, contactPointA );
1169
+ mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) );
1170
+ mp->point = b2Add( xfA.p, mp->anchorA );
1171
+ mp->separation = separation;
1172
+ mp->id = 0;
1173
+ manifold.pointCount = 1;
1174
+ return manifold;
1175
+ }
1176
+
1177
+ b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB,
1178
+ b2Transform xfB, b2SimplexCache* cache )
1179
+ {
1180
+ b2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius );
1181
+ return b2CollideChainSegmentAndPolygon( segmentA, xfA, &polyB, xfB, cache );
1182
+ }
1183
+
1184
+ static b2Manifold b2ClipSegments( b2Vec2 a1, b2Vec2 a2, b2Vec2 b1, b2Vec2 b2, b2Vec2 normal, float ra, float rb, uint16_t id1,
1185
+ uint16_t id2 )
1186
+ {
1187
+ b2Manifold manifold = { 0 };
1188
+
1189
+ b2Vec2 tangent = b2LeftPerp( normal );
1190
+
1191
+ // Barycentric coordinates of each point relative to a1 along tangent
1192
+ float lower1 = 0.0f;
1193
+ float upper1 = b2Dot( b2Sub( a2, a1 ), tangent );
1194
+
1195
+ // Incident edge points opposite of tangent due to CCW winding
1196
+ float upper2 = b2Dot( b2Sub( b1, a1 ), tangent );
1197
+ float lower2 = b2Dot( b2Sub( b2, a1 ), tangent );
1198
+
1199
+ // Do segments overlap?
1200
+ if ( upper2 < lower1 || upper1 < lower2 )
1201
+ {
1202
+ return manifold;
1203
+ }
1204
+
1205
+ b2Vec2 vLower;
1206
+ if ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON )
1207
+ {
1208
+ vLower = b2Lerp( b2, b1, ( lower1 - lower2 ) / ( upper2 - lower2 ) );
1209
+ }
1210
+ else
1211
+ {
1212
+ vLower = b2;
1213
+ }
1214
+
1215
+ b2Vec2 vUpper;
1216
+ if ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON )
1217
+ {
1218
+ vUpper = b2Lerp( b2, b1, ( upper1 - lower2 ) / ( upper2 - lower2 ) );
1219
+ }
1220
+ else
1221
+ {
1222
+ vUpper = b1;
1223
+ }
1224
+
1225
+ // todo vLower can be very close to vUpper, reduce to one point?
1226
+
1227
+ float separationLower = b2Dot( b2Sub( vLower, a1 ), normal );
1228
+ float separationUpper = b2Dot( b2Sub( vUpper, a1 ), normal );
1229
+
1230
+ // Put contact points at midpoint, accounting for radii
1231
+ vLower = b2MulAdd( vLower, 0.5f * ( ra - rb - separationLower ), normal );
1232
+ vUpper = b2MulAdd( vUpper, 0.5f * ( ra - rb - separationUpper ), normal );
1233
+
1234
+ float radius = ra + rb;
1235
+
1236
+ manifold.normal = normal;
1237
+ {
1238
+ b2ManifoldPoint* cp = manifold.points + 0;
1239
+ cp->anchorA = vLower;
1240
+ cp->separation = separationLower - radius;
1241
+ cp->id = id1;
1242
+ }
1243
+
1244
+ {
1245
+ b2ManifoldPoint* cp = manifold.points + 1;
1246
+ cp->anchorA = vUpper;
1247
+ cp->separation = separationUpper - radius;
1248
+ cp->id = id2;
1249
+ }
1250
+
1251
+ manifold.pointCount = 2;
1252
+
1253
+ return manifold;
1254
+ }
1255
+
1256
+ enum b2NormalType
1257
+ {
1258
+ // This means the normal points in a direction that is non-smooth relative to a convex vertex and should be skipped
1259
+ b2_normalSkip,
1260
+
1261
+ // This means the normal points in a direction that is smooth relative to a convex vertex and should be used for collision
1262
+ b2_normalAdmit,
1263
+
1264
+ // This means the normal is in a region of a concave vertex and should be snapped to the segment normal
1265
+ b2_normalSnap
1266
+ };
1267
+
1268
+ struct b2ChainSegmentParams
1269
+ {
1270
+ b2Vec2 edge1;
1271
+ b2Vec2 normal0;
1272
+ b2Vec2 normal2;
1273
+ bool convex1;
1274
+ bool convex2;
1275
+ };
1276
+
1277
+ // Evaluate Gauss map
1278
+ // See https://box2d.org/posts/2020/06/ghost-collisions/
1279
+ static enum b2NormalType b2ClassifyNormal( struct b2ChainSegmentParams params, b2Vec2 normal )
1280
+ {
1281
+ const float sinTol = 0.01f;
1282
+
1283
+ if ( b2Dot( normal, params.edge1 ) <= 0.0f )
1284
+ {
1285
+ // Normal points towards the segment tail
1286
+ if ( params.convex1 )
1287
+ {
1288
+ if ( b2Cross( normal, params.normal0 ) > sinTol )
1289
+ {
1290
+ return b2_normalSkip;
1291
+ }
1292
+
1293
+ return b2_normalAdmit;
1294
+ }
1295
+ else
1296
+ {
1297
+ return b2_normalSnap;
1298
+ }
1299
+ }
1300
+ else
1301
+ {
1302
+ // Normal points towards segment head
1303
+ if ( params.convex2 )
1304
+ {
1305
+ if ( b2Cross( params.normal2, normal ) > sinTol )
1306
+ {
1307
+ return b2_normalSkip;
1308
+ }
1309
+
1310
+ return b2_normalAdmit;
1311
+ }
1312
+ else
1313
+ {
1314
+ return b2_normalSnap;
1315
+ }
1316
+ }
1317
+ }
1318
+
1319
+ b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB,
1320
+ b2Transform xfB, b2SimplexCache* cache )
1321
+ {
1322
+ b2Manifold manifold = { 0 };
1323
+
1324
+ b2Transform xf = b2InvMulTransforms( xfA, xfB );
1325
+
1326
+ b2Vec2 centroidB = b2TransformPoint( xf, polygonB->centroid );
1327
+ float radiusB = polygonB->radius;
1328
+
1329
+ b2Vec2 p1 = segmentA->segment.point1;
1330
+ b2Vec2 p2 = segmentA->segment.point2;
1331
+
1332
+ b2Vec2 edge1 = b2Normalize( b2Sub( p2, p1 ) );
1333
+
1334
+ struct b2ChainSegmentParams smoothParams = { 0 };
1335
+ smoothParams.edge1 = edge1;
1336
+
1337
+ const float convexTol = 0.01f;
1338
+ b2Vec2 edge0 = b2Normalize( b2Sub( p1, segmentA->ghost1 ) );
1339
+ smoothParams.normal0 = b2RightPerp( edge0 );
1340
+ smoothParams.convex1 = b2Cross( edge0, edge1 ) >= convexTol;
1341
+
1342
+ b2Vec2 edge2 = b2Normalize( b2Sub( segmentA->ghost2, p2 ) );
1343
+ smoothParams.normal2 = b2RightPerp( edge2 );
1344
+ smoothParams.convex2 = b2Cross( edge1, edge2 ) >= convexTol;
1345
+
1346
+ // Normal points to the right
1347
+ b2Vec2 normal1 = b2RightPerp( edge1 );
1348
+ bool behind1 = b2Dot( normal1, b2Sub( centroidB, p1 ) ) < 0.0f;
1349
+ bool behind0 = true;
1350
+ bool behind2 = true;
1351
+ if ( smoothParams.convex1 )
1352
+ {
1353
+ behind0 = b2Dot( smoothParams.normal0, b2Sub( centroidB, p1 ) ) < 0.0f;
1354
+ }
1355
+
1356
+ if ( smoothParams.convex2 )
1357
+ {
1358
+ behind2 = b2Dot( smoothParams.normal2, b2Sub( centroidB, p2 ) ) < 0.0f;
1359
+ }
1360
+
1361
+ if ( behind1 && behind0 && behind2 )
1362
+ {
1363
+ // one-sided collision
1364
+ return manifold;
1365
+ }
1366
+
1367
+ // Get polygonB in frameA
1368
+ int count = polygonB->count;
1369
+ b2Vec2 vertices[B2_MAX_POLYGON_VERTICES];
1370
+ b2Vec2 normals[B2_MAX_POLYGON_VERTICES];
1371
+ for ( int i = 0; i < count; ++i )
1372
+ {
1373
+ vertices[i] = b2TransformPoint( xf, polygonB->vertices[i] );
1374
+ normals[i] = b2RotateVector( xf.q, polygonB->normals[i] );
1375
+ }
1376
+
1377
+ // Distance doesn't work correctly with partial polygons
1378
+ b2DistanceInput input;
1379
+ input.proxyA = b2MakeProxy( &segmentA->segment.point1, 2, 0.0f );
1380
+ input.proxyB = b2MakeProxy( vertices, count, 0.0f );
1381
+ input.transformA = b2Transform_identity;
1382
+ input.transformB = b2Transform_identity;
1383
+ input.useRadii = false;
1384
+
1385
+ b2DistanceOutput output = b2ShapeDistance( &input, cache, NULL, 0 );
1386
+
1387
+ if ( output.distance > radiusB + B2_SPECULATIVE_DISTANCE )
1388
+ {
1389
+ return manifold;
1390
+ }
1391
+
1392
+ // Snap concave normals for partial polygon
1393
+ b2Vec2 n0 = smoothParams.convex1 ? smoothParams.normal0 : normal1;
1394
+ b2Vec2 n2 = smoothParams.convex2 ? smoothParams.normal2 : normal1;
1395
+
1396
+ // Index of incident vertex on polygon
1397
+ int incidentIndex = -1;
1398
+ int incidentNormal = -1;
1399
+
1400
+ if ( behind1 == false && output.distance > 0.1f * B2_LINEAR_SLOP )
1401
+ {
1402
+ // The closest features may be two vertices or an edge and a vertex even when there should
1403
+ // be face contact
1404
+
1405
+ if ( cache->count == 1 )
1406
+ {
1407
+ // vertex-vertex collision
1408
+ b2Vec2 pA = output.pointA;
1409
+ b2Vec2 pB = output.pointB;
1410
+
1411
+ b2Vec2 normal = b2Normalize( b2Sub( pB, pA ) );
1412
+
1413
+ enum b2NormalType type = b2ClassifyNormal( smoothParams, normal );
1414
+ if ( type == b2_normalSkip )
1415
+ {
1416
+ return manifold;
1417
+ }
1418
+
1419
+ if ( type == b2_normalAdmit )
1420
+ {
1421
+ manifold.normal = b2RotateVector( xfA.q, normal );
1422
+ b2ManifoldPoint* cp = manifold.points + 0;
1423
+ cp->anchorA = b2RotateVector( xfA.q, pA );
1424
+ cp->anchorB = b2Add( cp->anchorA, b2Sub( xfA.p, xfB.p ) );
1425
+ cp->point = b2Add( xfA.p, cp->anchorA );
1426
+ cp->separation = output.distance - radiusB;
1427
+ cp->id = B2_MAKE_ID( cache->indexA[0], cache->indexB[0] );
1428
+ manifold.pointCount = 1;
1429
+ return manifold;
1430
+ }
1431
+
1432
+ // fall through b2_normalSnap
1433
+ incidentIndex = cache->indexB[0];
1434
+ }
1435
+ else
1436
+ {
1437
+ // vertex-edge collision
1438
+ B2_ASSERT( cache->count == 2 );
1439
+
1440
+ int ia1 = cache->indexA[0];
1441
+ int ia2 = cache->indexA[1];
1442
+ int ib1 = cache->indexB[0];
1443
+ int ib2 = cache->indexB[1];
1444
+
1445
+ if ( ia1 == ia2 )
1446
+ {
1447
+ // 1 point on A, expect 2 points on B
1448
+ B2_ASSERT( ib1 != ib2 );
1449
+
1450
+ // Find polygon normal most aligned with vector between closest points.
1451
+ // This effectively sorts ib1 and ib2
1452
+ b2Vec2 normalB = b2Sub( output.pointA, output.pointB );
1453
+ float dot1 = b2Dot( normalB, normals[ib1] );
1454
+ float dot2 = b2Dot( normalB, normals[ib2] );
1455
+ int ib = dot1 > dot2 ? ib1 : ib2;
1456
+
1457
+ // Use accurate normal
1458
+ normalB = normals[ib];
1459
+
1460
+ enum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( normalB ) );
1461
+ if ( type == b2_normalSkip )
1462
+ {
1463
+ return manifold;
1464
+ }
1465
+
1466
+ if ( type == b2_normalAdmit )
1467
+ {
1468
+ // Get polygon edge associated with normal
1469
+ ib1 = ib;
1470
+ ib2 = ib < count - 1 ? ib + 1 : 0;
1471
+
1472
+ b2Vec2 b1 = vertices[ib1];
1473
+ b2Vec2 b2 = vertices[ib2];
1474
+
1475
+ // Find incident segment vertex
1476
+ dot1 = b2Dot( normalB, b2Sub( p1, b1 ) );
1477
+ dot2 = b2Dot( normalB, b2Sub( p2, b1 ) );
1478
+
1479
+ if ( dot1 < dot2 )
1480
+ {
1481
+ if ( b2Dot( n0, normalB ) < b2Dot( normal1, normalB ) )
1482
+ {
1483
+ // Neighbor is incident
1484
+ return manifold;
1485
+ }
1486
+ }
1487
+ else
1488
+ {
1489
+ if ( b2Dot( n2, normalB ) < b2Dot( normal1, normalB ) )
1490
+ {
1491
+ // Neighbor is incident
1492
+ return manifold;
1493
+ }
1494
+ }
1495
+
1496
+ manifold =
1497
+ b2ClipSegments( b1, b2, p1, p2, normalB, radiusB, 0.0f, B2_MAKE_ID( ib1, 1 ), B2_MAKE_ID( ib2, 0 ) );
1498
+
1499
+ B2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );
1500
+ if ( manifold.pointCount == 2 )
1501
+ {
1502
+ manifold.normal = b2RotateVector( xfA.q, b2Neg( normalB ) );
1503
+ manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );
1504
+ manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );
1505
+ b2Vec2 pAB = b2Sub( xfA.p, xfB.p );
1506
+ manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );
1507
+ manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );
1508
+ manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );
1509
+ manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );
1510
+ }
1511
+ return manifold;
1512
+ }
1513
+
1514
+ // fall through b2_normalSnap
1515
+ incidentNormal = ib;
1516
+ }
1517
+ else
1518
+ {
1519
+ // Get index of incident polygonB vertex
1520
+ float dot1 = b2Dot( normal1, b2Sub( vertices[ib1], p1 ) );
1521
+ float dot2 = b2Dot( normal1, b2Sub( vertices[ib2], p2 ) );
1522
+ incidentIndex = dot1 < dot2 ? ib1 : ib2;
1523
+ }
1524
+ }
1525
+ }
1526
+ else
1527
+ {
1528
+ // SAT edge normal
1529
+ float edgeSeparation = FLT_MAX;
1530
+
1531
+ for ( int i = 0; i < count; ++i )
1532
+ {
1533
+ float s = b2Dot( normal1, b2Sub( vertices[i], p1 ) );
1534
+ if ( s < edgeSeparation )
1535
+ {
1536
+ edgeSeparation = s;
1537
+ incidentIndex = i;
1538
+ }
1539
+ }
1540
+
1541
+ // Check convex neighbor for edge separation
1542
+ if ( smoothParams.convex1 )
1543
+ {
1544
+ float s0 = FLT_MAX;
1545
+
1546
+ for ( int i = 0; i < count; ++i )
1547
+ {
1548
+ float s = b2Dot( smoothParams.normal0, b2Sub( vertices[i], p1 ) );
1549
+ if ( s < s0 )
1550
+ {
1551
+ s0 = s;
1552
+ }
1553
+ }
1554
+
1555
+ if ( s0 > edgeSeparation )
1556
+ {
1557
+ edgeSeparation = s0;
1558
+
1559
+ // Indicate neighbor owns edge separation
1560
+ incidentIndex = -1;
1561
+ }
1562
+ }
1563
+
1564
+ // Check convex neighbor for edge separation
1565
+ if ( smoothParams.convex2 )
1566
+ {
1567
+ float s2 = FLT_MAX;
1568
+
1569
+ for ( int i = 0; i < count; ++i )
1570
+ {
1571
+ float s = b2Dot( smoothParams.normal2, b2Sub( vertices[i], p2 ) );
1572
+ if ( s < s2 )
1573
+ {
1574
+ s2 = s;
1575
+ }
1576
+ }
1577
+
1578
+ if ( s2 > edgeSeparation )
1579
+ {
1580
+ edgeSeparation = s2;
1581
+
1582
+ // Indicate neighbor owns edge separation
1583
+ incidentIndex = -1;
1584
+ }
1585
+ }
1586
+
1587
+ // SAT polygon normals
1588
+ float polygonSeparation = -FLT_MAX;
1589
+ int referenceIndex = -1;
1590
+
1591
+ for ( int i = 0; i < count; ++i )
1592
+ {
1593
+ b2Vec2 n = normals[i];
1594
+
1595
+ enum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( n ) );
1596
+ if ( type != b2_normalAdmit )
1597
+ {
1598
+ continue;
1599
+ }
1600
+
1601
+ // Check the infinite sides of the partial polygon
1602
+ // if ((smoothParams.convex1 && b2Cross(n0, n) > 0.0f) || (smoothParams.convex2 && b2Cross(n, n2) > 0.0f))
1603
+ //{
1604
+ // continue;
1605
+ //}
1606
+
1607
+ b2Vec2 p = vertices[i];
1608
+ float s = b2MinFloat( b2Dot( n, b2Sub( p2, p ) ), b2Dot( n, b2Sub( p1, p ) ) );
1609
+
1610
+ if ( s > polygonSeparation )
1611
+ {
1612
+ polygonSeparation = s;
1613
+ referenceIndex = i;
1614
+ }
1615
+ }
1616
+
1617
+ if ( polygonSeparation > edgeSeparation )
1618
+ {
1619
+ int ia1 = referenceIndex;
1620
+ int ia2 = ia1 < count - 1 ? ia1 + 1 : 0;
1621
+ b2Vec2 a1 = vertices[ia1];
1622
+ b2Vec2 a2 = vertices[ia2];
1623
+
1624
+ b2Vec2 n = normals[ia1];
1625
+
1626
+ float dot1 = b2Dot( n, b2Sub( p1, a1 ) );
1627
+ float dot2 = b2Dot( n, b2Sub( p2, a1 ) );
1628
+
1629
+ if ( dot1 < dot2 )
1630
+ {
1631
+ if ( b2Dot( n0, n ) < b2Dot( normal1, n ) )
1632
+ {
1633
+ // Neighbor is incident
1634
+ return manifold;
1635
+ }
1636
+ }
1637
+ else
1638
+ {
1639
+ if ( b2Dot( n2, n ) < b2Dot( normal1, n ) )
1640
+ {
1641
+ // Neighbor is incident
1642
+ return manifold;
1643
+ }
1644
+ }
1645
+
1646
+ manifold = b2ClipSegments( a1, a2, p1, p2, normals[ia1], radiusB, 0.0f, B2_MAKE_ID( ia1, 1 ), B2_MAKE_ID( ia2, 0 ) );
1647
+
1648
+ B2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );
1649
+ if ( manifold.pointCount == 2 )
1650
+ {
1651
+
1652
+ manifold.normal = b2RotateVector( xfA.q, b2Neg( normals[ia1] ) );
1653
+ manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );
1654
+ manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );
1655
+ b2Vec2 pAB = b2Sub( xfA.p, xfB.p );
1656
+ manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );
1657
+ manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );
1658
+ manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );
1659
+ manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );
1660
+ }
1661
+
1662
+ return manifold;
1663
+ }
1664
+
1665
+ if ( incidentIndex == -1 )
1666
+ {
1667
+ // neighboring segment is the separating axis
1668
+ return manifold;
1669
+ }
1670
+
1671
+ // fall through segment normal axis
1672
+ }
1673
+
1674
+ B2_ASSERT( incidentNormal != -1 || incidentIndex != -1 );
1675
+
1676
+ // Segment normal
1677
+
1678
+ // Find incident polygon normal: normal adjacent to deepest vertex that is most anti-parallel to segment normal
1679
+ b2Vec2 b1, b2;
1680
+ int ib1, ib2;
1681
+
1682
+ if ( incidentNormal != -1 )
1683
+ {
1684
+ ib1 = incidentNormal;
1685
+ ib2 = ib1 < count - 1 ? ib1 + 1 : 0;
1686
+ b1 = vertices[ib1];
1687
+ b2 = vertices[ib2];
1688
+ }
1689
+ else
1690
+ {
1691
+ int i2 = incidentIndex;
1692
+ int i1 = i2 > 0 ? i2 - 1 : count - 1;
1693
+ float d1 = b2Dot( normal1, normals[i1] );
1694
+ float d2 = b2Dot( normal1, normals[i2] );
1695
+ if ( d1 < d2 )
1696
+ {
1697
+ ib1 = i1, ib2 = i2;
1698
+ b1 = vertices[ib1];
1699
+ b2 = vertices[ib2];
1700
+ }
1701
+ else
1702
+ {
1703
+ ib1 = i2, ib2 = i2 < count - 1 ? i2 + 1 : 0;
1704
+ b1 = vertices[ib1];
1705
+ b2 = vertices[ib2];
1706
+ }
1707
+ }
1708
+
1709
+ manifold = b2ClipSegments( p1, p2, b1, b2, normal1, 0.0f, radiusB, B2_MAKE_ID( 0, ib2 ), B2_MAKE_ID( 1, ib1 ) );
1710
+
1711
+ B2_ASSERT( manifold.pointCount == 0 || manifold.pointCount == 2 );
1712
+ if ( manifold.pointCount == 2 )
1713
+ {
1714
+ // There may be no points c
1715
+ manifold.normal = b2RotateVector( xfA.q, manifold.normal );
1716
+ manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA );
1717
+ manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA );
1718
+ b2Vec2 pAB = b2Sub( xfA.p, xfB.p );
1719
+ manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB );
1720
+ manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB );
1721
+ manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA );
1722
+ manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA );
1723
+ }
1724
+
1725
+ return manifold;
1726
+ }