+c2Poly c2Dual(c2Poly poly, float skin_factor)
+{
+ c2Poly dual;
+ dual.count = poly.count;
+
+ // Each plane maps to a point by involution (the mapping is its own inverse) by dividing
+ // the plane normal by its offset factor.
+ // plane = a * x + b * y - d
+ // dual = { a / d, b / d }
+ for (int i = 0; i < poly.count; ++i) {
+ c2v n = poly.norms[i];
+ float d = c2Dot(n, poly.verts[i]) - skin_factor;
+ if (d == 0) dual.verts[i] = c2V(0, 0);
+ else dual.verts[i] = c2Div(n, d);
+ }
+
+ // Instead of canonically building the convex hull, can simply take advantage of how
+ // the vertices are still in proper CCW order, so only the normals must be recomputed.
+ c2Norms(dual.verts, dual.norms, dual.count);
+
+ return dual;
+}
+
+// Inflating a polytope, idea by Dirk Gregorius ~ 2015. Works in both 2D and 3D.
+// Reference: Halfspace intersection with Qhull by Brad Barber
+// http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/half.html
+//
+// Algorithm steps:
+// 1. Find a point within the input poly.
+// 2. Center this point onto the origin.
+// 3. Adjust the planes by a skin factor.
+// 4. Compute the dual vert of each plane. Each plane becomes a vertex.
+// c2v dual(c2h plane) { return c2V(plane.n.x / plane.d, plane.n.y / plane.d) }
+// 5. Compute the convex hull of the dual verts. This is called the dual.
+// 6. Compute the dual of the dual, this will be the poly to return.
+// 7. Translate the poly away from the origin by the center point from step 2.
+// 8. Return the inflated poly.
+c2Poly c2InflatePoly(c2Poly poly, float skin_factor)
+{
+ c2v average = poly.verts[0];
+ for (int i = 1; i < poly.count; ++i) {
+ average = c2Add(average, poly.verts[i]);
+ }
+ average = c2Div(average, (float)poly.count);
+
+ for (int i = 0; i < poly.count; ++i) {
+ poly.verts[i] = c2Sub(poly.verts[i], average);
+ }
+
+ c2Poly dual = c2Dual(poly, skin_factor);
+ poly = c2Dual(dual, 0);
+
+ for (int i = 0; i < poly.count; ++i) {
+ poly.verts[i] = c2Add(poly.verts[i], average);
+ }
+
+ return poly;
+}
+
+void c2Inflate(void* shape, C2_TYPE type, float skin_factor)
+{
+ switch (type)
+ {
+ case C2_TYPE_CIRCLE:
+ {
+ c2Circle* circle = (c2Circle*)shape;
+ circle->r += skin_factor;
+ } break;
+
+ case C2_TYPE_AABB:
+ {
+ c2AABB* bb = (c2AABB*)shape;
+ c2v factor = c2V(skin_factor, skin_factor);
+ bb->min = c2Sub(bb->min, factor);
+ bb->max = c2Add(bb->max, factor);
+ } break;
+
+ case C2_TYPE_CAPSULE:
+ {
+ c2Capsule* capsule = (c2Capsule*)shape;
+ capsule->r += skin_factor;
+ } break;
+
+ case C2_TYPE_POLY:
+ {
+ c2Poly* poly = (c2Poly*)shape;
+ *poly = c2InflatePoly(*poly, skin_factor);
+ } break;
+ }
+}
+