computeMass method

  1. @override
void computeMass(
  1. MassData massData,
  2. double density
)
override

Compute the mass properties of this shape using its dimensions and density. The inertia tensor is computed about the local origin. density should be in kilograms per meter squared.

Implementation

@override
void computeMass(MassData massData, double density) {
  // 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.

  assert(vertices.length >= 3);

  final center = Vector2.zero();
  var area = 0.0;
  var I = 0.0;

  // pRef is the reference point for forming triangles.
  // It's location doesn't change the result (except for rounding error).
  final s = Vector2.zero();
  // This code would put the reference point inside the polygon.
  for (var i = 0; i < vertices.length; ++i) {
    s.add(vertices[i]);
  }
  s.scale(1.0 / vertices.length.toDouble());

  const kInv3 = 1.0 / 3.0;

  final e1 = Vector2.zero();
  final e2 = Vector2.zero();

  for (var i = 0; i < vertices.length; ++i) {
    // Triangle vertices.
    e1
      ..setFrom(vertices[i])
      ..sub(s);
    e2
      ..setFrom(s)
      ..negate()
      ..add(i + 1 < vertices.length ? vertices[i + 1] : vertices[0]);

    final D = e1.cross(e2);

    final triangleArea = 0.5 * D;
    area += triangleArea.abs();

    // Area weighted centroid
    center.x += triangleArea * kInv3 * (e1.x + e2.x);
    center.y += triangleArea * kInv3 * (e1.y + e2.y);

    final ex1 = e1.x;
    final ey1 = e1.y;
    final ex2 = e2.x;
    final ey2 = e2.y;

    final intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;
    final inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;

    I += (0.25 * kInv3 * D) * (intx2 + inty2);
  }

  // Total mass
  massData.mass = density * area;

  // Center of mass
  assert(area > settings.epsilon);
  center.scale(1.0 / area);
  massData.center
    ..setFrom(center)
    ..add(s);

  // Inertia tensor relative to the local origin (point s)
  massData.I = I * density;

  // Shift to center of mass then to original body origin.
  massData.I += massData.mass * (massData.center.dot(massData.center));
}