protected override void computeProperties()
{
// 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.
Debug.Assert( vertices.Count >= 3 );
//FPE optimization: Early exit as polygons with 0 density does not have any properties.
if( _density <= 0 )
return;
//FPE optimization: Consolidated the calculate centroid and mass code to a single method.
var center = Vector2.Zero;
float area = 0.0f;
float I = 0.0f;
// pRef is the reference point for forming triangles.
// It's location doesn't change the result (except for rounding error).
var s = Vector2.Zero;
// This code would put the reference point inside the polygon.
for( int i = 0; i < vertices.Count; ++i )
{
s += vertices[i];
}
s *= 1.0f / vertices.Count;
const float k_inv3 = 1.0f / 3.0f;
for( int i = 0; i < vertices.Count; ++i )
{
// Triangle vertices.
Vector2 e1 = vertices[i] - s;
Vector2 e2 = i + 1 < vertices.Count ? vertices[i + 1] - s : vertices[0] - s;
float D = MathUtils.cross( e1, e2 );
float triangleArea = 0.5f * D;
area += triangleArea;
// Area weighted centroid
center += triangleArea * k_inv3 * ( e1 + e2 );
float ex1 = e1.X, ey1 = e1.Y;
float ex2 = e2.X, ey2 = e2.Y;
float intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;
float inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;
I += ( 0.25f * k_inv3 * D ) * ( intx2 + inty2 );
}
//The area is too small for the engine to handle.
Debug.Assert( area > Settings.epsilon );
// We save the area
massData.area = area;
// Total mass
massData.mass = _density * area;
// Center of mass
center *= 1.0f / area;
massData.centroid = center + s;
// Inertia tensor relative to the local origin (point s).
massData.inertia = _density * I;
// Shift to center of mass then to original body origin.
massData.inertia += massData.mass * ( Vector2.Dot( massData.centroid, massData.centroid ) - Vector2.Dot( center, center ) );
}