1 /*
2  * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 module dchip.cpCollision;
23 
24 import core.stdc.stdlib : alloca;
25 
26 import std..string;
27 
28 import dchip.cpArbiter;
29 import dchip.cpBB;
30 import dchip.cpBody;
31 import dchip.chipmunk;
32 import dchip.chipmunk_private;
33 import dchip.chipmunk_types;
34 import dchip.cpPolyShape;
35 import dchip.cpShape;
36 import dchip.cpSpace;
37 import dchip.cpVect;
38 import dchip.util;
39 
40 /+ #if DEBUG && 0
41 #include "ChipmunkDemo.h"
42 #define DRAW_ALL 0
43 #define DRAW_GJK (0 || DRAW_ALL)
44 #define DRAW_EPA (0 || DRAW_ALL)
45 #define DRAW_CLOSEST (0 || DRAW_ALL)
46 #define DRAW_CLIP (0 || DRAW_ALL)
47 
48 #define PRINT_LOG 0
49 #endif +/
50 
51 enum ENABLE_CACHING = 1;
52 
53 enum MAX_GJK_ITERATIONS = 30;
54 enum MAX_EPA_ITERATIONS = 30;
55 enum WARN_GJK_ITERATIONS = 20;
56 enum WARN_EPA_ITERATIONS = 20;
57 
58 // Add contact points for circle to circle collisions.
59 // Used by several collision tests.
60 // TODO should accept hash parameter
61 int CircleToCircleQuery(const cpVect p1, const cpVect p2, const cpFloat r1, const cpFloat r2, cpHashValue hash, cpContact* con)
62 {
63     cpFloat mindist = r1 + r2;
64     cpVect  delta   = cpvsub(p2, p1);
65     cpFloat distsq  = cpvlengthsq(delta);
66 
67     if (distsq < mindist * mindist)
68     {
69         cpFloat dist = cpfsqrt(distsq);
70         cpVect  n    = (dist ? cpvmult(delta, 1.0f / dist) : cpv(1.0f, 0.0f));
71         cpContactInit(con, cpvlerp(p1, p2, r1 / (r1 + r2)), n, dist - mindist, hash);
72 
73         return 1;
74     }
75     else
76     {
77         return 0;
78     }
79 }
80 
81 //MARK: Support Points and Edges:
82 
83 int PolySupportPointIndex(const int count, const cpVect* verts, const cpVect n)
84 {
85     cpFloat max = -INFINITY;
86     int index   = 0;
87 
88     for (int i = 0; i < count; i++)
89     {
90         cpVect  v = verts[i];
91         cpFloat d = cpvdot(v, n);
92 
93         if (d > max)
94         {
95             max   = d;
96             index = i;
97         }
98     }
99 
100     return index;
101 }
102 
103 struct SupportPoint
104 {
105     cpVect p;
106     cpCollisionID id;
107 }
108 
109 SupportPoint SupportPointNew(cpVect p, cpCollisionID id)
110 {
111     SupportPoint point = { p, id };
112     return point;
113 }
114 
115 alias SupportPointFunc = SupportPoint function(const cpShape* shape, const cpVect n);
116 
117 SupportPoint CircleSupportPoint(const cpCircleShape* circle, const cpVect n)
118 {
119     return SupportPointNew(circle.tc, 0);
120 }
121 
122 SupportPoint SegmentSupportPoint(const cpSegmentShape* seg, const cpVect n)
123 {
124     if (cpvdot(seg.ta, n) > cpvdot(seg.tb, n))
125     {
126         return SupportPointNew(seg.ta, 0);
127     }
128     else
129     {
130         return SupportPointNew(seg.tb, 1);
131     }
132 }
133 
134 SupportPoint PolySupportPoint(const cpPolyShape* poly, const cpVect n)
135 {
136     const cpVect* verts = poly.tVerts;
137     int i = PolySupportPointIndex(poly.numVerts, verts, n);
138     return SupportPointNew(verts[i], i);
139 }
140 
141 struct MinkowskiPoint
142 {
143     cpVect a, b;
144     cpVect ab;
145     cpCollisionID id;
146 }
147 
148 MinkowskiPoint MinkowskiPointNew(const SupportPoint a, const SupportPoint b)
149 {
150     MinkowskiPoint point = { a.p, b.p, cpvsub(b.p, a.p), (a.id & 0xFF) << 8 | (b.id & 0xFF) };
151     return point;
152 }
153 
154 struct SupportContext
155 {
156     const cpShape* shape1;
157     const cpShape* shape2;
158     SupportPointFunc func1, func2;
159 }
160 
161 MinkowskiPoint Support(const SupportContext* ctx, const cpVect n)
162 {
163     SupportPoint a = ctx.func1(ctx.shape1, cpvneg(n));
164     SupportPoint b = ctx.func2(ctx.shape2, n);
165     return MinkowskiPointNew(a, b);
166 }
167 
168 struct EdgePoint
169 {
170     cpVect p;
171     cpHashValue hash;
172 }
173 
174 struct Edge
175 {
176     EdgePoint a, b;
177     cpFloat r = 0;
178     cpVect n;
179 }
180 
181 Edge EdgeNew(cpVect va, cpVect vb, cpHashValue ha, cpHashValue hb, cpFloat r)
182 {
183     Edge edge = { { va, ha }, { vb, hb }, r, cpvnormalize(cpvperp(cpvsub(vb, va))) };
184     return edge;
185 }
186 
187 Edge SupportEdgeForPoly(const cpPolyShape* poly, const cpVect n)
188 {
189     int numVerts = poly.numVerts;
190     int i1       = PolySupportPointIndex(poly.numVerts, poly.tVerts, n);
191 
192     // TODO get rid of mod eventually, very expensive on ARM
193     int i0 = (i1 - 1 + numVerts) % numVerts;
194     int i2 = (i1 + 1) % numVerts;
195 
196     cpVect* verts = cast(cpVect*)poly.tVerts;
197 
198     if (cpvdot(n, poly.tPlanes[i1].n) > cpvdot(n, poly.tPlanes[i2].n))
199     {
200         Edge edge = { { verts[i0], CP_HASH_PAIR(poly, i0) }, { verts[i1], CP_HASH_PAIR(poly, i1) }, poly.r, poly.tPlanes[i1].n };
201         return edge;
202     }
203     else
204     {
205         Edge edge = { { verts[i1], CP_HASH_PAIR(poly, i1) }, { verts[i2], CP_HASH_PAIR(poly, i2) }, poly.r, poly.tPlanes[i2].n };
206         return edge;
207     }
208 }
209 
210 Edge SupportEdgeForSegment(const cpSegmentShape* seg, const cpVect n)
211 {
212     if (cpvdot(seg.tn, n) > 0.0)
213     {
214         Edge edge = { { seg.ta, CP_HASH_PAIR(seg, 0) }, { seg.tb, CP_HASH_PAIR(seg, 1) }, seg.r, seg.tn };
215         return edge;
216     }
217     else
218     {
219         Edge edge = { { seg.tb, CP_HASH_PAIR(seg, 1) }, { seg.ta, CP_HASH_PAIR(seg, 0) }, seg.r, cpvneg(seg.tn) };
220         return edge;
221     }
222 }
223 
224 cpFloat ClosestT(const cpVect a, const cpVect b)
225 {
226     cpVect delta = cpvsub(b, a);
227     return -cpfclamp(cpvdot(delta, cpvadd(a, b)) / cpvlengthsq(delta), -1.0f, 1.0f);
228 }
229 
230 cpVect LerpT(const cpVect a, const cpVect b, const cpFloat t)
231 {
232     cpFloat ht = 0.5f * t;
233     return cpvadd(cpvmult(a, 0.5f - ht), cpvmult(b, 0.5f + ht));
234 }
235 
236 struct ClosestPoints
237 {
238     cpVect a, b;
239     cpVect n;
240     cpFloat d = 0;
241     cpCollisionID id;
242 }
243 
244 ClosestPoints ClosestPointsNew(const MinkowskiPoint v0, const MinkowskiPoint v1)
245 {
246     cpFloat t = ClosestT(v0.ab, v1.ab);
247     cpVect  p = LerpT(v0.ab, v1.ab, t);
248 
249     cpVect pa        = LerpT(v0.a, v1.a, t);
250     cpVect pb        = LerpT(v0.b, v1.b, t);
251     cpCollisionID id = (v0.id & 0xFFFF) << 16 | (v1.id & 0xFFFF);
252 
253     cpVect  delta = cpvsub(v1.ab, v0.ab);
254     cpVect  n     = cpvnormalize(cpvperp(delta));
255     cpFloat d     = -cpvdot(n, p);
256 
257     if (d <= 0.0f || (0.0f < t && t < 1.0f))
258     {
259         ClosestPoints points = { pa, pb, cpvneg(n), d, id };
260         return points;
261     }
262     else
263     {
264         cpFloat d2 = cpvlength(p);
265         cpVect  n_  = cpvmult(p, 1.0f / (d2 + CPFLOAT_MIN));
266 
267         ClosestPoints points = { pa, pb, n_, d2, id };
268         return points;
269     }
270 }
271 
272 //MARK: EPA Functions
273 
274 cpFloat ClosestDist(const cpVect v0, const cpVect v1)
275 {
276     return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1)));
277 }
278 
279 ClosestPoints EPARecurse(const SupportContext* ctx, const int count, const MinkowskiPoint* hull, const int iteration)
280 {
281     int mini        = 0;
282     cpFloat minDist = INFINITY;
283 
284     // TODO: precalculate this when building the hull and save a step.
285     for (int j = 0, i = count - 1; j < count; i = j, j++)
286     {
287         cpFloat d = ClosestDist(hull[i].ab, hull[j].ab);
288 
289         if (d < minDist)
290         {
291             minDist = d;
292             mini    = i;
293         }
294     }
295 
296     MinkowskiPoint v0 = hull[mini];
297     MinkowskiPoint v1 = hull[(mini + 1) % count];
298     cpAssertSoft(!cpveql(v0.ab, v1.ab), "!cpveql(v0.ab, v1.ab)", "Internal Error: EPA vertexes are the same (%d and %d)", mini, (mini + 1) % count);
299 
300     MinkowskiPoint p = Support(ctx, cpvperp(cpvsub(v1.ab, v0.ab)));
301 
302 /+ #if DRAW_EPA
303     cpVect verts[count];
304 
305     for (int i = 0; i < count; i++)
306         verts[i] = hull[i].ab;
307 
308     ChipmunkDebugDrawPolygon(count, verts, RGBAColor(1, 1, 0, 1), RGBAColor(1, 1, 0, 0.25));
309     ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 0, 0, 1));
310 
311     ChipmunkDebugDrawPoints(5, 1, (cpVect[]){ p.ab }, RGBAColor(1, 1, 1, 1));
312 #endif +/
313 
314     cpFloat area2x = cpvcross(cpvsub(v1.ab, v0.ab), cpvadd(cpvsub(p.ab, v0.ab), cpvsub(p.ab, v1.ab)));
315 
316     if (area2x > 0.0f && iteration < MAX_EPA_ITERATIONS)
317     {
318         int count2 = 1;
319         MinkowskiPoint* hull2 = cast(MinkowskiPoint*)alloca((count + 1) * MinkowskiPoint.sizeof);
320         hull2[0] = p;
321 
322         for (int i = 0; i < count; i++)
323         {
324             int index = (mini + 1 + i) % count;
325 
326             cpVect h0 = hull2[count2 - 1].ab;
327             cpVect h1 = hull[index].ab;
328             cpVect h2 = (i + 1 < count ? hull[(index + 1) % count] : p).ab;
329 
330             // TODO: Should this be changed to an area2x check?
331             if (cpvcross(cpvsub(h2, h0), cpvsub(h1, h0)) > 0.0f)
332             {
333                 hull2[count2] = hull[index];
334                 count2++;
335             }
336         }
337 
338         return EPARecurse(ctx, count2, hull2, iteration + 1);
339     }
340     else
341     {
342         cpAssertWarn(iteration < WARN_EPA_ITERATIONS, "High EPA iterations: %d", iteration);
343         return ClosestPointsNew(v0, v1);
344     }
345 }
346 
347 ClosestPoints EPA(const SupportContext* ctx, const MinkowskiPoint v0, const MinkowskiPoint v1, const MinkowskiPoint v2)
348 {
349     // TODO: allocate a NxM array here and do an in place convex hull reduction in EPARecurse
350     MinkowskiPoint[3] hull;
351     hull[0] = v0;
352     hull[1] = v1;
353     hull[2] = v2;
354     return EPARecurse(ctx, 3, hull.ptr, 1);
355 }
356 
357 //MARK: GJK Functions.
358 
359 ClosestPoints GJKRecurse(const SupportContext* ctx, const MinkowskiPoint v0, const MinkowskiPoint v1, const int iteration)
360 {
361     if (iteration > MAX_GJK_ITERATIONS)
362     {
363         cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);
364         return ClosestPointsNew(v0, v1);
365     }
366 
367     cpVect delta = cpvsub(v1.ab, v0.ab);
368 
369     if (cpvcross(delta, cpvadd(v0.ab, v1.ab)) > 0.0f)
370     {
371         // Origin is behind axis. Flip and try again.
372         return GJKRecurse(ctx, v1, v0, iteration + 1);
373     }
374     else
375     {
376         cpFloat t = ClosestT(v0.ab, v1.ab);
377         cpVect  n = (-1.0f < t && t < 1.0f ? cpvperp(delta) : cpvneg(LerpT(v0.ab, v1.ab, t)));
378         MinkowskiPoint p = Support(ctx, n);
379 
380 /+ #if DRAW_GJK
381         ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1));
382         cpVect c = cpvlerp(v0.ab, v1.ab, 0.5);
383         ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1));
384 
385         ChipmunkDebugDrawPoints(5.0, 1, &p.ab, RGBAColor(1, 1, 1, 1));
386 #endif +/
387 
388         if (
389             cpvcross(cpvsub(v1.ab, p.ab), cpvadd(v1.ab, p.ab)) > 0.0f &&
390             cpvcross(cpvsub(v0.ab, p.ab), cpvadd(v0.ab, p.ab)) < 0.0f
391             )
392         {
393             cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK.EPA iterations: %d", iteration);
394 
395             // The triangle v0, p, v1 contains the origin. Use EPA to find the MSA.
396             return EPA(ctx, v0, p, v1);
397         }
398         else
399         {
400             // The new point must be farther along the normal than the existing points.
401             if (cpvdot(p.ab, n) <= cpfmax(cpvdot(v0.ab, n), cpvdot(v1.ab, n)))
402             {
403                 cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);
404                 return ClosestPointsNew(v0, v1);
405             }
406             else
407             {
408                 if (ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab))
409                 {
410                     return GJKRecurse(ctx, v0, p, iteration + 1);
411                 }
412                 else
413                 {
414                     return GJKRecurse(ctx, p, v1, iteration + 1);
415                 }
416             }
417         }
418     }
419 }
420 
421 SupportPoint ShapePoint(const cpShape* shape, const int i)
422 {
423     switch (shape.klass.type)
424     {
425         case CP_CIRCLE_SHAPE:
426         {
427             return SupportPointNew((cast(cpCircleShape*)shape).tc, 0);
428         }
429 
430         case CP_SEGMENT_SHAPE:
431         {
432             cpSegmentShape* seg = cast(cpSegmentShape*)shape;
433             return SupportPointNew(i == 0 ? seg.ta : seg.tb, i);
434         }
435 
436         case CP_POLY_SHAPE:
437         {
438             cpPolyShape* poly = cast(cpPolyShape*)shape;
439 
440             // Poly shapes may change vertex count.
441             int index = (i < poly.numVerts ? i : 0);
442             return SupportPointNew(poly.tVerts[index], index);
443         }
444 
445         default:
446         {
447             return SupportPointNew(cpvzero, 0);
448         }
449     }
450 }
451 
452 ClosestPoints GJK(const SupportContext* ctx, cpCollisionID* id)
453 {
454 /+ #if DRAW_GJK || DRAW_EPA
455 
456     // draw the minkowski difference origin
457     cpVect origin = cpvzero;
458     ChipmunkDebugDrawPoints(5.0, 1, &origin, RGBAColor(1, 0, 0, 1));
459 
460     int mdiffCount     = ctx.count1 * ctx.count2;
461     cpVect* mdiffVerts = alloca(mdiffCount * sizeof(cpVect));
462 
463     for (int i = 0; i < ctx.count1; i++)
464     {
465         for (int j = 0; j < ctx.count2; j++)
466         {
467             cpVect v1 = ShapePoint(ctx.count1, ctx.verts1, i).p;
468             cpVect v2 = ShapePoint(ctx.count2, ctx.verts2, j).p;
469             mdiffVerts[i * ctx.count2 + j] = cpvsub(v2, v1);
470         }
471     }
472 
473     cpVect* hullVerts = alloca(mdiffCount * sizeof(cpVect));
474     int hullCount     = cpConvexHull(mdiffCount, mdiffVerts, hullVerts, null, 0.0);
475 
476     ChipmunkDebugDrawPolygon(hullCount, hullVerts, RGBAColor(1, 0, 0, 1), RGBAColor(1, 0, 0, 0.25));
477     ChipmunkDebugDrawPoints(2.0, mdiffCount, mdiffVerts, RGBAColor(1, 0, 0, 1));
478 #endif +/
479 
480     MinkowskiPoint v0, v1;
481 
482     if (*id && ENABLE_CACHING)
483     {
484         v0 = MinkowskiPointNew(ShapePoint(ctx.shape1, (*id >> 24) & 0xFF), ShapePoint(ctx.shape2, (*id >> 16) & 0xFF));
485         v1 = MinkowskiPointNew(ShapePoint(ctx.shape1, (*id >> 8) & 0xFF), ShapePoint(ctx.shape2, (*id    ) & 0xFF));
486     }
487     else
488     {
489         cpVect axis = cpvperp(cpvsub(cpBBCenter(ctx.shape1.bb), cpBBCenter(ctx.shape2.bb)));
490         v0 = Support(ctx, axis);
491         v1 = Support(ctx, cpvneg(axis));
492     }
493 
494     ClosestPoints points = GJKRecurse(ctx, v0, v1, 1);
495     *id = points.id;
496     return points;
497 }
498 
499 //MARK: Contact Clipping
500 
501 void Contact1(cpFloat dist, cpVect a, cpVect b, cpFloat refr, cpFloat incr, cpVect n, cpHashValue hash, cpContact* arr)
502 {
503     cpFloat rsum  = refr + incr;
504     cpFloat alpha = (rsum > 0.0f ? refr / rsum : 0.5f);
505     cpVect  point = cpvlerp(a, b, alpha);
506 
507     cpContactInit(arr, point, n, dist - rsum, hash);
508 }
509 
510 int Contact2(cpVect refp, cpVect inca, cpVect incb, cpFloat refr, cpFloat incr, cpVect refn, cpVect n, cpHashValue hash, cpContact* arr)
511 {
512     cpFloat cian = cpvcross(inca, refn);
513     cpFloat cibn = cpvcross(incb, refn);
514     cpFloat crpn = cpvcross(refp, refn);
515     cpFloat t    = 1.0f - cpfclamp01((cibn - crpn) / (cibn - cian));
516 
517     cpVect  point = cpvlerp(inca, incb, t);
518     cpFloat pd    = cpvdot(cpvsub(point, refp), refn);
519 
520     if (t > 0.0f && pd <= 0.0f)
521     {
522         cpFloat rsum  = refr + incr;
523         cpFloat alpha = (rsum > 0.0f ? incr * (1.0f - (rsum + pd) / rsum) : -0.5f * pd);
524 
525         cpContactInit(arr, cpvadd(point, cpvmult(refn, alpha)), n, pd, hash);
526         return 1;
527     }
528     else
529     {
530         return 0;
531     }
532 }
533 
534 int ClipContacts(const Edge ref_, const Edge inc, const ClosestPoints points, const cpFloat nflip, cpContact* arr)
535 {
536     cpVect inc_offs = cpvmult(inc.n, inc.r);
537     cpVect ref_offs = cpvmult(ref_.n, ref_.r);
538 
539     cpVect inca = cpvadd(inc.a.p, inc_offs);
540     cpVect incb = cpvadd(inc.b.p, inc_offs);
541 
542     cpVect closest_inca = cpClosetPointOnSegment(inc.a.p, ref_.a.p, ref_.b.p);
543     cpVect closest_incb = cpClosetPointOnSegment(inc.b.p, ref_.a.p, ref_.b.p);
544 
545     cpVect  msa    = cpvmult(points.n, nflip * points.d);
546     cpFloat cost_a = cpvdistsq(cpvsub(inc.a.p, closest_inca), msa);
547     cpFloat cost_b = cpvdistsq(cpvsub(inc.b.p, closest_incb), msa);
548 
549 /+ #if DRAW_CLIP
550     ChipmunkDebugDrawSegment(ref_.a.p, ref_.b.p, RGBAColor(1, 0, 0, 1));
551     ChipmunkDebugDrawSegment(inc.a.p, inc.b.p, RGBAColor(0, 1, 0, 1));
552     ChipmunkDebugDrawSegment(inca, incb, RGBAColor(0, 1, 0, 1));
553 
554     cpVect cref = cpvlerp(ref_.a.p, ref_.b.p, 0.5);
555     ChipmunkDebugDrawSegment(cref, cpvadd(cref, cpvmult(ref_.n, 5.0)), RGBAColor(1, 0, 0, 1));
556 
557     cpVect cinc = cpvlerp(inc.a.p, inc.b.p, 0.5);
558     ChipmunkDebugDrawSegment(cinc, cpvadd(cinc, cpvmult(inc.n, 5.0)), RGBAColor(1, 0, 0, 1));
559 
560     ChipmunkDebugDrawPoints(5.0, 2, (cpVect[]){ ref_.a.p, inc.a.p }, RGBAColor(1, 1, 0, 1));
561     ChipmunkDebugDrawPoints(5.0, 2, (cpVect[]){ ref_.b.p, inc.b.p }, RGBAColor(0, 1, 1, 1));
562 
563     if (cost_a < cost_b)
564     {
565         ChipmunkDebugDrawSegment(closest_inca, inc.a.p, RGBAColor(1, 0, 1, 1));
566     }
567     else
568     {
569         ChipmunkDebugDrawSegment(closest_incb, inc.b.p, RGBAColor(1, 0, 1, 1));
570     }
571 #endif +/
572 
573     cpHashValue hash_iarb = CP_HASH_PAIR(inc.a.hash, ref_.b.hash);
574     cpHashValue hash_ibra = CP_HASH_PAIR(inc.b.hash, ref_.a.hash);
575 
576     if (cost_a < cost_b)
577     {
578         cpVect refp = cpvadd(ref_.a.p, ref_offs);
579         Contact1(points.d, closest_inca, inc.a.p, ref_.r, inc.r, points.n, hash_iarb, arr);
580         return Contact2(refp, inca, incb, ref_.r, inc.r, ref_.n, points.n, hash_ibra, arr + 1) + 1;
581     }
582     else
583     {
584         cpVect refp = cpvadd(ref_.b.p, ref_offs);
585         Contact1(points.d, closest_incb, inc.b.p, ref_.r, inc.r, points.n, hash_ibra, arr);
586         return Contact2(refp, incb, inca, ref_.r, inc.r, ref_.n, points.n, hash_iarb, arr + 1) + 1;
587     }
588 }
589 
590 int ContactPoints(const Edge e1, const Edge e2, const ClosestPoints points, cpContact* arr)
591 {
592     cpFloat mindist = e1.r + e2.r;
593 
594     if (points.d <= mindist)
595     {
596         cpFloat pick = cpvdot(e1.n, points.n) + cpvdot(e2.n, points.n);
597 
598         if (
599             (pick != 0.0f && pick > 0.0f) ||
600 
601             // If the edges are both perfectly aligned weird things happen.
602             // This is *very* common at the start of a simulation.
603             // Pick the longest edge as the reference to break the tie.
604             (pick == 0.0f && (cpvdistsq(e1.a.p, e1.b.p) > cpvdistsq(e2.a.p, e2.b.p)))
605             )
606         {
607             return ClipContacts(e1, e2, points, 1.0f, arr);
608         }
609         else
610         {
611             return ClipContacts(e2, e1, points, -1.0f, arr);
612         }
613     }
614     else
615     {
616         return 0;
617     }
618 }
619 
620 //MARK: Collision Functions
621 
622 alias CollisionFunc = int function(const cpShape* a, const cpShape* b, cpCollisionID* id, cpContact* arr);
623 
624 // Collide circle shapes.
625 int CircleToCircle(const cpCircleShape* c1, const cpCircleShape* c2, cpCollisionID* id, cpContact* arr)
626 {
627     return CircleToCircleQuery(c1.tc, c2.tc, c1.r, c2.r, 0, arr);
628 }
629 
630 int CircleToSegment(const cpCircleShape* circleShape, const cpSegmentShape* segmentShape, cpCollisionID* id, cpContact* con)
631 {
632     cpVect seg_a  = segmentShape.ta;
633     cpVect seg_b  = segmentShape.tb;
634     cpVect center = circleShape.tc;
635 
636     cpVect  seg_delta = cpvsub(seg_b, seg_a);
637     cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a)) / cpvlengthsq(seg_delta));
638     cpVect  closest   = cpvadd(seg_a, cpvmult(seg_delta, closest_t));
639 
640     if (CircleToCircleQuery(center, closest, circleShape.r, segmentShape.r, 0, con))
641     {
642         cpVect n = con[0].n;
643 
644         // Reject endcap collisions if tangents are provided.
645         if (
646             (closest_t != 0.0f || cpvdot(n, cpvrotate(segmentShape.a_tangent, segmentShape.shape.body_.rot)) >= 0.0) &&
647             (closest_t != 1.0f || cpvdot(n, cpvrotate(segmentShape.b_tangent, segmentShape.shape.body_.rot)) >= 0.0)
648             )
649         {
650             return 1;
651         }
652     }
653 
654     return 0;
655 }
656 
657 int SegmentToSegment(const cpSegmentShape* seg1, const cpSegmentShape* seg2, cpCollisionID* id, cpContact* arr)
658 {
659     SupportContext context = { cast(cpShape*)seg1, cast(cpShape*)seg2, safeCast!SupportPointFunc(&SegmentSupportPoint), safeCast!SupportPointFunc(&SegmentSupportPoint) };
660     ClosestPoints  points  = GJK(&context, id);
661 
662 /+ #if DRAW_CLOSEST
663 #if PRINT_LOG
664 
665     //	ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
666 #endif
667 
668     ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1));
669     ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1));
670     ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
671     ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
672 #endif +/
673 
674     cpVect n    = points.n;
675     cpVect rot1 = seg1.shape.body_.rot;
676     cpVect rot2 = seg2.shape.body_.rot;
677 
678     if (
679         points.d <= (seg1.r + seg2.r) &&
680         (
681             (!cpveql(points.a, seg1.ta) || cpvdot(n, cpvrotate(seg1.a_tangent, rot1)) <= 0.0) &&
682             (!cpveql(points.a, seg1.tb) || cpvdot(n, cpvrotate(seg1.b_tangent, rot1)) <= 0.0) &&
683             (!cpveql(points.b, seg2.ta) || cpvdot(n, cpvrotate(seg2.a_tangent, rot2)) >= 0.0) &&
684             (!cpveql(points.b, seg2.tb) || cpvdot(n, cpvrotate(seg2.b_tangent, rot2)) >= 0.0)
685         )
686         )
687     {
688         return ContactPoints(SupportEdgeForSegment(seg1, n), SupportEdgeForSegment(seg2, cpvneg(n)), points, arr);
689     }
690     else
691     {
692         return 0;
693     }
694 }
695 
696 int PolyToPoly(const cpPolyShape* poly1, const cpPolyShape* poly2, cpCollisionID* id, cpContact* arr)
697 {
698     SupportContext context = { cast(cpShape*)poly1, cast(cpShape*)poly2, safeCast!SupportPointFunc(&PolySupportPoint), safeCast!SupportPointFunc(&PolySupportPoint) };
699     ClosestPoints  points  = GJK(&context, id);
700 
701 /+ #if DRAW_CLOSEST
702 #if PRINT_LOG
703 
704     //	ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
705 #endif
706 
707     ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
708     ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
709     ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
710     ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
711 #endif +/
712 
713     if (points.d - poly1.r - poly2.r <= 0.0)
714     {
715         return ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, arr);
716     }
717     else
718     {
719         return 0;
720     }
721 }
722 
723 int SegmentToPoly(const cpSegmentShape* seg, const cpPolyShape* poly, cpCollisionID* id, cpContact* arr)
724 {
725     SupportContext context = { cast(cpShape*)seg, cast(cpShape*)poly, safeCast!SupportPointFunc(&SegmentSupportPoint), safeCast!SupportPointFunc(&PolySupportPoint) };
726     ClosestPoints  points  = GJK(&context, id);
727 
728 /+ #if DRAW_CLOSEST
729 #if PRINT_LOG
730 
731     //	ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
732 #endif
733 
734     ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
735     ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
736     ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
737     ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
738 #endif +/
739 
740     // Reject endcap collisions if tangents are provided.
741     cpVect n   = points.n;
742     cpVect rot = seg.shape.body_.rot;
743 
744     if (
745         points.d - seg.r - poly.r <= 0.0 &&
746         (
747             (!cpveql(points.a, seg.ta) || cpvdot(n, cpvrotate(seg.a_tangent, rot)) <= 0.0) &&
748             (!cpveql(points.a, seg.tb) || cpvdot(n, cpvrotate(seg.b_tangent, rot)) <= 0.0)
749         )
750         )
751     {
752         return ContactPoints(SupportEdgeForSegment(seg, n), SupportEdgeForPoly(poly, cpvneg(n)), points, arr);
753     }
754     else
755     {
756         return 0;
757     }
758 }
759 
760 // This one is less gross, but still gross.
761 // TODO: Comment me!
762 int CircleToPoly(const cpCircleShape* circle, const cpPolyShape* poly, cpCollisionID* id, cpContact* con)
763 {
764     SupportContext context = { cast(cpShape*)circle, cast(cpShape*)poly, safeCast!SupportPointFunc(&CircleSupportPoint), safeCast!SupportPointFunc(&PolySupportPoint) };
765     ClosestPoints  points  = GJK(&context, id);
766 
767 /+ #if DRAW_CLOSEST
768     ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
769     ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
770     ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
771     ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
772 #endif +/
773 
774     cpFloat mindist = circle.r + poly.r;
775 
776     if (points.d - mindist <= 0.0)
777     {
778         cpVect p = cpvlerp(points.a, points.b, circle.r / (mindist));
779         cpContactInit(con, p, points.n, points.d - mindist, 0);
780         return 1;
781     }
782     else
783     {
784         return 0;
785     }
786 }
787 
788 /* const */ __gshared CollisionFunc[9] builtinCollisionFuncs;
789 /* const */ __gshared CollisionFunc* colfuncs;
790 /* const */ __gshared CollisionFunc[9] segmentCollisions;
791 
792 void _initModuleCtor_cpCollision()
793 {
794     builtinCollisionFuncs = [
795         safeCast!CollisionFunc(&CircleToCircle),
796         null,
797         null,
798         safeCast!CollisionFunc(&CircleToSegment),
799         null,
800         null,
801         safeCast!CollisionFunc(&CircleToPoly),
802         safeCast!CollisionFunc(&SegmentToPoly),
803         safeCast!CollisionFunc(&PolyToPoly),
804     ];
805 
806     colfuncs = builtinCollisionFuncs.ptr;
807 
808     segmentCollisions = [
809         safeCast!CollisionFunc(&CircleToCircle),
810         null,
811         null,
812         safeCast!CollisionFunc(&CircleToSegment),
813         safeCast!CollisionFunc(&SegmentToSegment),
814         null,
815         safeCast!CollisionFunc(&CircleToPoly),
816         safeCast!CollisionFunc(&SegmentToPoly),
817         safeCast!CollisionFunc(&PolyToPoly),
818     ];
819 }
820 
821 void cpEnableSegmentToSegmentCollisions()
822 {
823     colfuncs = segmentCollisions.ptr;
824 }
825 
826 int cpCollideShapes(const cpShape* a, const cpShape* b, cpCollisionID* id, cpContact* arr)
827 {
828     // Their shape types must be in order.
829     cpAssertSoft(a.klass.type <= b.klass.type, "Internal Error: Collision shapes passed to cpCollideShapes() are not sorted.");
830 
831     CollisionFunc cfunc = colfuncs[a.klass.type + b.klass.type * CP_NUM_SHAPES];
832 
833     int numContacts = (cfunc ? cfunc(a, b, id, arr) : 0);
834     cpAssertSoft(numContacts <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Too many contact points returned.");
835 
836     return numContacts;
837 }