first commit
[blok] / Box2D / Source / Collision / Shapes / b2PolygonShape.cpp
diff --git a/Box2D/Source/Collision/Shapes/b2PolygonShape.cpp b/Box2D/Source/Collision/Shapes/b2PolygonShape.cpp
new file mode 100644 (file)
index 0000000..936fb51
--- /dev/null
@@ -0,0 +1,449 @@
+
+/*
+* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
+*
+* This software is provided 'as-is', without any express or implied
+* warranty.  In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "b2PolygonShape.h"
+
+void b2PolygonDef::SetAsBox(float32 hx, float32 hy)
+{
+       vertexCount = 4;
+       vertices[0].Set(-hx, -hy);
+       vertices[1].Set( hx, -hy);
+       vertices[2].Set( hx,  hy);
+       vertices[3].Set(-hx,  hy);
+}
+
+void b2PolygonDef::SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle)
+{
+       SetAsBox(hx, hy);
+       b2XForm xf;
+       xf.position = center;
+       xf.R.Set(angle);
+
+       for (int32 i = 0; i < vertexCount; ++i)
+       {
+               vertices[i] = b2Mul(xf, vertices[i]);
+       }
+}
+
+static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count)
+{
+       b2Assert(count >= 3);
+
+       b2Vec2 c; c.Set(0.0f, 0.0f);
+       float32 area = 0.0f;
+
+       // pRef is the reference point for forming triangles.
+       // It's location doesn't change the result (except for rounding error).
+       b2Vec2 pRef(0.0f, 0.0f);
+#if 0
+       // This code would put the reference point inside the polygon.
+       for (int32 i = 0; i < count; ++i)
+       {
+               pRef += vs[i];
+       }
+       pRef *= 1.0f / count;
+#endif
+
+       const float32 inv3 = 1.0f / 3.0f;
+
+       for (int32 i = 0; i < count; ++i)
+       {
+               // Triangle vertices.
+               b2Vec2 p1 = pRef;
+               b2Vec2 p2 = vs[i];
+               b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0];
+
+               b2Vec2 e1 = p2 - p1;
+               b2Vec2 e2 = p3 - p1;
+
+               float32 D = b2Cross(e1, e2);
+
+               float32 triangleArea = 0.5f * D;
+               area += triangleArea;
+
+               // Area weighted centroid
+               c += triangleArea * inv3 * (p1 + p2 + p3);
+       }
+
+       // Centroid
+       b2Assert(area > B2_FLT_EPSILON);
+       c *= 1.0f / area;
+       return c;
+}
+
+// http://www.geometrictools.com/Documentation/MinimumAreaRectangle.pdf
+static void ComputeOBB(b2OBB* obb, const b2Vec2* vs, int32 count)
+{
+       b2Assert(count <= b2_maxPolygonVertices);
+       b2Vec2 p[b2_maxPolygonVertices + 1];
+       for (int32 i = 0; i < count; ++i)
+       {
+               p[i] = vs[i];
+       }
+       p[count] = p[0];
+
+       float32 minArea = B2_FLT_MAX;
+       
+       for (int32 i = 1; i <= count; ++i)
+       {
+               b2Vec2 root = p[i-1];
+               b2Vec2 ux = p[i] - root;
+               float32 length = ux.Normalize();
+               b2Assert(length > B2_FLT_EPSILON);
+               b2Vec2 uy(-ux.y, ux.x);
+               b2Vec2 lower(B2_FLT_MAX, B2_FLT_MAX);
+               b2Vec2 upper(-B2_FLT_MAX, -B2_FLT_MAX);
+
+               for (int32 j = 0; j < count; ++j)
+               {
+                       b2Vec2 d = p[j] - root;
+                       b2Vec2 r;
+                       r.x = b2Dot(ux, d);
+                       r.y = b2Dot(uy, d);
+                       lower = b2Min(lower, r);
+                       upper = b2Max(upper, r);
+               }
+
+               float32 area = (upper.x - lower.x) * (upper.y - lower.y);
+               if (area < 0.95f * minArea)
+               {
+                       minArea = area;
+                       obb->R.col1 = ux;
+                       obb->R.col2 = uy;
+                       b2Vec2 center = 0.5f * (lower + upper);
+                       obb->center = root + b2Mul(obb->R, center);
+                       obb->extents = 0.5f * (upper - lower);
+               }
+       }
+
+       b2Assert(minArea < B2_FLT_MAX);
+}
+
+b2PolygonShape::b2PolygonShape(const b2ShapeDef* def)
+        : b2Shape(def)
+{
+       b2Assert(def->type == e_polygonShape);
+       m_type = e_polygonShape;
+       const b2PolygonDef* poly = (const b2PolygonDef*)def;
+
+       // Get the vertices transformed into the body frame.
+       m_vertexCount = poly->vertexCount;
+       b2Assert(3 <= m_vertexCount && m_vertexCount <= b2_maxPolygonVertices);
+
+       // Copy vertices.
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               m_vertices[i] = poly->vertices[i];
+       }
+
+       // Compute normals. Ensure the edges have non-zero length.
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               int32 i1 = i;
+               int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0;
+               b2Vec2 edge = m_vertices[i2] - m_vertices[i1];
+               b2Assert(edge.LengthSquared() > B2_FLT_EPSILON * B2_FLT_EPSILON);
+               m_normals[i] = b2Cross(edge, 1.0f);
+               m_normals[i].Normalize();
+       }
+
+#ifdef _DEBUG
+       // Ensure the polygon is convex.
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               for (int32 j = 0; j < m_vertexCount; ++j)
+               {
+                       // Don't check vertices on the current edge.
+                       if (j == i || j == (i + 1) % m_vertexCount)
+                       {
+                               continue;
+                       }
+                       
+                       // Your polygon is non-convex (it has an indentation).
+                       // Or your polygon is too skinny.
+                       float32 s = b2Dot(m_normals[i], m_vertices[j] - m_vertices[i]);
+                       b2Assert(s < -b2_linearSlop);
+               }
+       }
+
+       // Ensure the polygon is counter-clockwise.
+       for (int32 i = 1; i < m_vertexCount; ++i)
+       {
+               float32 cross = b2Cross(m_normals[i-1], m_normals[i]);
+
+               // Keep asinf happy.
+               cross = b2Clamp(cross, -1.0f, 1.0f);
+
+               // You have consecutive edges that are almost parallel on your polygon.
+               float32 angle = asinf(cross);
+               b2Assert(angle > b2_angularSlop);
+       }
+#endif
+
+       // Compute the polygon centroid.
+       m_centroid = ComputeCentroid(poly->vertices, poly->vertexCount);
+
+       // Compute the oriented bounding box.
+       ComputeOBB(&m_obb, m_vertices, m_vertexCount);
+
+       // Create core polygon shape by shifting edges inward.
+       // Also compute the min/max radius for CCD.
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               int32 i1 = i - 1 >= 0 ? i - 1 : m_vertexCount - 1;
+               int32 i2 = i;
+
+               b2Vec2 n1 = m_normals[i1];
+               b2Vec2 n2 = m_normals[i2];
+               b2Vec2 v = m_vertices[i] - m_centroid;;
+
+               b2Vec2 d;
+               d.x = b2Dot(n1, v) - b2_toiSlop;
+               d.y = b2Dot(n2, v) - b2_toiSlop;
+
+               // Shifting the edge inward by b2_toiSlop should
+               // not cause the plane to pass the centroid.
+
+               // Your shape has a radius/extent less than b2_toiSlop.
+               b2Assert(d.x >= 0.0f);
+               b2Assert(d.y >= 0.0f);
+               b2Mat22 A;
+               A.col1.x = n1.x; A.col2.x = n1.y;
+               A.col1.y = n2.x; A.col2.y = n2.y;
+               m_coreVertices[i] = A.Solve(d) + m_centroid;
+       }
+}
+
+void b2PolygonShape::UpdateSweepRadius(const b2Vec2& center)
+{
+       // Update the sweep radius (maximum radius) as measured from
+       // a local center point.
+       m_sweepRadius = 0.0f;
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               b2Vec2 d = m_coreVertices[i] - center;
+               m_sweepRadius = b2Max(m_sweepRadius, d.Length());
+       }
+}
+
+bool b2PolygonShape::TestPoint(const b2XForm& xf, const b2Vec2& p) const
+{
+       b2Vec2 pLocal = b2MulT(xf.R, p - xf.position);
+
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               float32 dot = b2Dot(m_normals[i], pLocal - m_vertices[i]);
+               if (dot > 0.0f)
+               {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+bool b2PolygonShape::TestSegment(
+       const b2XForm& xf,
+       float32* lambda,
+       b2Vec2* normal,
+       const b2Segment& segment,
+       float32 maxLambda) const
+{
+       float32 lower = 0.0f, upper = maxLambda;
+
+       b2Vec2 p1 = b2MulT(xf.R, segment.p1 - xf.position);
+       b2Vec2 p2 = b2MulT(xf.R, segment.p2 - xf.position);
+       b2Vec2 d = p2 - p1;
+       int32 index = -1;
+
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               // p = p1 + a * d
+               // dot(normal, p - v) = 0
+               // dot(normal, p1 - v) + a * dot(normal, d) = 0
+               float32 numerator = b2Dot(m_normals[i], m_vertices[i] - p1);
+               float32 denominator = b2Dot(m_normals[i], d);
+
+               // Note: we want this predicate without division:
+               // lower < numerator / denominator, where denominator < 0
+               // Since denominator < 0, we have to flip the inequality:
+               // lower < numerator / denominator <==> denominator * lower > numerator.
+
+               if (denominator < 0.0f && numerator < lower * denominator)
+               {
+                       // Increase lower.
+                       // The segment enters this half-space.
+                       lower = numerator / denominator;
+                       index = i;
+               }
+               else if (denominator > 0.0f && numerator < upper * denominator)
+               {
+                       // Decrease upper.
+                       // The segment exits this half-space.
+                       upper = numerator / denominator;
+               }
+
+               if (upper < lower)
+               {
+                       return false;
+               }
+       }
+
+       b2Assert(0.0f <= lower && lower <= maxLambda);
+
+       if (index >= 0)
+       {
+               *lambda = lower;
+               *normal = b2Mul(xf.R, m_normals[index]);
+               return true;
+       }
+
+       return false;
+}
+
+void b2PolygonShape::ComputeAABB(b2AABB* aabb, const b2XForm& xf) const
+{
+       b2Mat22 R = b2Mul(xf.R, m_obb.R);
+       b2Mat22 absR = b2Abs(R);
+       b2Vec2 h = b2Mul(absR, m_obb.extents);
+       b2Vec2 position = xf.position + b2Mul(xf.R, m_obb.center);
+       aabb->lowerBound = position - h;
+       aabb->upperBound = position + h;
+}
+
+void b2PolygonShape::ComputeSweptAABB(b2AABB* aabb,
+                                         const b2XForm& transform1,
+                                         const b2XForm& transform2) const
+{
+       b2AABB aabb1, aabb2;
+       ComputeAABB(&aabb1, transform1);
+       ComputeAABB(&aabb2, transform2);
+       aabb->lowerBound = b2Min(aabb1.lowerBound, aabb2.lowerBound);
+       aabb->upperBound = b2Max(aabb1.upperBound, aabb2.upperBound);
+}
+
+void b2PolygonShape::ComputeMass(b2MassData* massData) const
+{
+       // Polygon mass, centroid, and inertia.
+       // Let rho be the polygon density in mass per unit area.
+       // Then:
+       // mass = rho * int(dA)
+       // centroid.x = (1/mass) * rho * int(x * dA)
+       // centroid.y = (1/mass) * rho * int(y * dA)
+       // I = rho * int((x*x + y*y) * dA)
+       //
+       // We can compute these integrals by summing all the integrals
+       // for each triangle of the polygon. To evaluate the integral
+       // for a single triangle, we make a change of variables to
+       // the (u,v) coordinates of the triangle:
+       // x = x0 + e1x * u + e2x * v
+       // y = y0 + e1y * u + e2y * v
+       // where 0 <= u && 0 <= v && u + v <= 1.
+       //
+       // We integrate u from [0,1-v] and then v from [0,1].
+       // We also need to use the Jacobian of the transformation:
+       // D = cross(e1, e2)
+       //
+       // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)
+       //
+       // The rest of the derivation is handled by computer algebra.
+
+       b2Assert(m_vertexCount >= 3);
+
+       b2Vec2 center; center.Set(0.0f, 0.0f);
+       float32 area = 0.0f;
+       float32 I = 0.0f;
+
+       // pRef is the reference point for forming triangles.
+       // It's location doesn't change the result (except for rounding error).
+       b2Vec2 pRef(0.0f, 0.0f);
+#if 0
+       // This code would put the reference point inside the polygon.
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               pRef += m_vertices[i];
+       }
+       pRef *= 1.0f / count;
+#endif
+
+       const float32 k_inv3 = 1.0f / 3.0f;
+
+       for (int32 i = 0; i < m_vertexCount; ++i)
+       {
+               // Triangle vertices.
+               b2Vec2 p1 = pRef;
+               b2Vec2 p2 = m_vertices[i];
+               b2Vec2 p3 = i + 1 < m_vertexCount ? m_vertices[i+1] : m_vertices[0];
+
+               b2Vec2 e1 = p2 - p1;
+               b2Vec2 e2 = p3 - p1;
+
+               float32 D = b2Cross(e1, e2);
+
+               float32 triangleArea = 0.5f * D;
+               area += triangleArea;
+
+               // Area weighted centroid
+               center += triangleArea * k_inv3 * (p1 + p2 + p3);
+
+               float32 px = p1.x, py = p1.y;
+               float32 ex1 = e1.x, ey1 = e1.y;
+               float32 ex2 = e2.x, ey2 = e2.y;
+
+               float32 intx2 = k_inv3 * (0.25f * (ex1*ex1 + ex2*ex1 + ex2*ex2) + (px*ex1 + px*ex2)) + 0.5f*px*px;
+               float32 inty2 = k_inv3 * (0.25f * (ey1*ey1 + ey2*ey1 + ey2*ey2) + (py*ey1 + py*ey2)) + 0.5f*py*py;
+
+               I += D * (intx2 + inty2);
+       }
+
+       // Total mass
+       massData->mass = m_density * area;
+
+       // Center of mass
+       b2Assert(area > B2_FLT_EPSILON);
+       center *= 1.0f / area;
+       massData->center = center;
+
+       // Inertia tensor relative to the local origin.
+       massData->I = m_density * I;
+}
+
+b2Vec2 b2PolygonShape::Centroid(const b2XForm& xf) const
+{
+       return b2Mul(xf, m_centroid);
+}
+
+b2Vec2 b2PolygonShape::Support(const b2XForm& xf, const b2Vec2& d) const
+{
+       b2Vec2 dLocal = b2MulT(xf.R, d);
+
+       int32 bestIndex = 0;
+       float32 bestValue = b2Dot(m_coreVertices[0], dLocal);
+       for (int32 i = 1; i < m_vertexCount; ++i)
+       {
+               float32 value = b2Dot(m_coreVertices[i], dLocal);
+               if (value > bestValue)
+               {
+                       bestIndex = i;
+                       bestValue = value;
+               }
+       }
+
+       return b2Mul(xf, m_coreVertices[bestIndex]);
+}