How Matter.js Calculates Compound Body Bounds

In Matter.js, the bounds of a compound body are determined by calculating an overall Axis-Aligned Bounding Box (AABB) that encapsulates all of its individual sub-parts. Rather than computing bounds as a single geometric shape, the physics engine updates the world coordinates of every vertex across all constituent parts and evaluates the absolute minimum and maximum X and Y values. This overview explores the internal mechanisms Matter.js uses to track sub-part hierarchies, transform vertices, and dynamically update compound body bounds during simulation steps.

The Anatomy of a Compound Body

A compound body in Matter.js is defined by its parts array. When you create a compound body using Body.create({ parts: [...] }), Matter.js sets up a parent-child relationship:

Each child body maintains its own set of local vertices, which are defined relative to the compound body's center of mass.

Step-by-Step Calculation of Bounds

Matter.js resolves the outer bounds of a compound body through three main steps:

1. World Coordinate Vertex Transformation

Before calculating bounds, the engine must know where each vertex is in world space. When a compound body rotates or translates, Matter.js iterates through each part in body.parts. It applies a 2D transformation matrix based on the compound body's position and angle to project the local vertices of each part into world coordinates.

2. Vertex Aggregation

Matter.js aggregates the transformed vertices of the sub-parts. The parent body's master vertex list is either assigned the combined set of all sub-part vertices or evaluated across every sub-part sequentially. For standard collision and broadphase detection, the engine considers the full collection of vertices belonging to all parts in body.parts.

3. Axis-Aligned Bounding Box (AABB) Computation

Once the world coordinates of all vertices are established, the engine utilizes its internal Bounds.update method. The bounds object consists of two coordinate pairs:

The algorithm loops through every vertex in every part:

// Conceptual representation of the Bounds.update logic
let minX = Infinity, minY = Infinity;
let maxX = -Infinity, maxY = -Infinity;

for (let i = 0; i < vertices.length; i++) {
    const vertex = vertices[i];
    if (vertex.x < minX) minX = vertex.x;
    if (vertex.x > maxX) maxX = vertex.x;
    if (vertex.y < minY) minY = vertex.y;
    if (vertex.y > maxY) maxY = vertex.y;
}

bounds.min.x = minX;
bounds.min.y = minY;
bounds.max.x = maxX;
bounds.max.y = maxY;

The resulting min and max values form the rectangular boundary aligned with the screen axes (X and Y), fully containing every part of the compound body regardless of its orientation.

Dynamic Updates During Simulation

Bounds are not static. During each step of the physics engine (Engine.update), the following lifecycle occurs for compound bodies:

  1. Velocity and Forces Applied: The parent body's position and angle are updated based on velocity, gravity, and external forces.
  2. Body.update Invocation: The engine updates the positions and vertices of all child parts relative to the parent.
  3. Compound Bounds Refresh: Matter.js re-runs the bounding box routine across the updated vertices, adjusting body.bounds to match the new physical footprint.

Purpose of Compound Bounds

Matter.js uses the calculated bounds primarily for the broadphase collision detection stage. Instead of performing expensive Narrowphase SAT (Separating Axis Theorem) checks against every sub-part of a compound body, the engine first checks if the compound body's AABB overlaps with other bodies' AABBs. Only when an overlap occurs does Matter.js proceed to check collisions for the specific sub-parts inside.