There is method to analyze a 2D polygon using vector algebra that is in my opinion easier to implement programmatically than the methods relying on trigonometry.
Each Vector
quantity has two components .x
and .y
as well as methods vector algebra vectors including for dot and cross product
add(a,b) = [a.x+b.x, a.y+b.y] // a+b = add(a,b)
scale(f,x) = [f*a.x, f*a.y] // 2*a = scale(2,a), a/3 = scale(1/3,a)
dot(a,b) = a.x*b.x + a.y*b.y // a·b = dot(a,b)
cross(a,b) = a.x*b.y - a.y*b.x // a×b = cross(a,b)
The method below goes through all the sides of a polygon and sum up the area, the center and the mass moment of inertia about the coordinate origin of each triangle defined by the side and the origin. The final sum takes care of adding or subtracting areas near or far from the origin and yield the accurate results.
Finally, the mass moment is transferred from the origin to the center of mass.
polygon(Vector[] points, double depth, double density)
{
// Accumulate the following values
double area = 0.0;
double mass = 0.0;
Vector center = [0.0, 0.0];
double mmoi = 0.0;
// Take each vertex pair starting from the last-first vertex
// in order to consider all sides.
int count = points.Length;
int prev = count - 1;
for(int index=0; index<count; index++)
{
Vector a = points[prev];
Vector b = points[index];
double area_step = TriangleArea(a,b);
double mass_step = density * area_step * depth;
Vector center_step = TriangleCenter(a,b);
double mmoi_step = TriangleMmoi(a,b, mass_step);
area += area_step;
center = (mass*center + mass_step*center_step)/(mass+mass_step);
mass += mass_step;
mmoi += mmoi_step;
prev = index;
}
// Transfer mass moment of inertia from the origin to the center of mass
mmoi -= mass*dot(center,center);
// use area, mass, center and mmoi
}
double TriangleArea(Vector a, Vector b)
{
return cross(a,b)/2;
}
double TriangleCenter(Vector a, Vector b)
{
return (a+b)/3;
{
double TriangleMmoi(Vector a, Vector b, double triangleMass)
{
return triangleMass/6*(dot(a,a)+dot(b.b)+dot(a.b));
}
The above is a similar process as outlined in this answer. More details are included in the linked answer.
Below is a c#
implementation of the above, but with specifying the mass, instead of the density and thickness.
public static RigidBody2 FromShape(double mass, params Vector2[] polygon)
{
double area = 0;
Vector2 center = Vector2.Zero;
double mmoi = 0;
int prev = polygon.Length-1;
for (int index = 0; index < polygon.Length; index++)
{
var a = polygon[prev];
var b = polygon[index];
var area_step = Vector2.Cross(a, b)/2;
var center_step = (a+b)/3;
var mmoi_step = area_step*(Vector2.Dot(a, a)+Vector2.Dot(b, b)+Vector2.Dot(a, b))/6;
center = (center*area + center_step * area_step)/(area + area_step);
area += area_step;
mmoi += mmoi_step;
prev = index;
}
double density = mass/area;
mmoi *= density;
mmoi -= mass * Vector2.Dot(center, center);
return new RigidBody2(mass, mmoi, center);
}
In testing I used the following shape
and the results of center = [1.0, 0.75]
and mmoi = 687.5
match with analysis done in CAD package
Here is the unit test that checks against the CAD data:
[TestMethod, TestCategory("Linear Algebra, Planar")]
public void Geom_PlanarPolygonMass()
{
Vector2[] points = new Vector2[] {
Vector2.Cartesian(0.75, 0),
Vector2.Cartesian(2, 0),
Vector2.Cartesian(2, 0.5),
Vector2.Cartesian(1.25, 0.5),
Vector2.Cartesian(1.25, 1.5),
Vector2.Cartesian(0, 1.5),
Vector2.Cartesian(0, 1.0),
Vector2.Cartesian(0.75, 1),
};
var rg = RigidBody2.FromShape(1500, points);
Assert.AreEqual(1500, rg.Mass);
CollectionAssert.AreEqual(Vector2.Cartesian(1.0, 0.75), rg.LocalCg, AbsComparer(TinyNumber));
Assert.AreEqual(687.5, rg.LocalMmoi, DoubleEx.TinyNumber);
}