Calculate Laser Reflection Angles in Matter.js
Simulating optical laser reflections off angled polygons in Matter.js requires identifying the exact edge of collision, extracting its surface normal vector, and applying the optical law of reflection. By combining Matter.js collision queries or custom segment intersection with vector mathematics, you can compute the reflected trajectory vector and its corresponding angle to recursively bounce laser rays off any rigid body.
1. Detect the Ray-Polygon Intersection
Matter.js provides Matter.Query.ray() to detect
collisions between a directed segment and a set of bodies. The function
takes a collection of bodies, a start point, and an end point:
const collisions = Matter.Query.ray(bodies, startPoint, endPoint, rayWidth);While Matter.Query.ray detects whether a body intersects
the ray path, calculating the exact hit point and the specific edge
struck usually requires testing the ray against each edge of the polygon
using 2D line segment intersection formulas.
Iterate through the vertices of the collided body
(body.vertices) in consecutive pairs \((V_i, V_{i+1})\) to find the intersecting
segment.
2. Determine the Surface Normal Vector
Once you identify the edge that the ray hit, find the outward-pointing unit normal vector of that edge.
Given an edge starting at vertex \(A = (x_1, y_1)\) and ending at vertex \(B = (x_2, y_2)\):
Compute the edge vector: \[\vec{E} = (x_2 - x_1, y_2 - y_1)\]
Obtain a perpendicular vector (normal): \[\vec{N}_{\text{raw}} = (-(y_2 - y_1), x_2 - x_1)\]
Normalize the vector to a unit length: \[\text{length} = \sqrt{N_x^2 + N_y^2}\] \[\vec{n} = \left(\frac{N_x}{\text{length}}, \frac{N_y}{\text{length}}\right)\]
Because vertices in Matter.js are arranged in clockwise order, rotating the edge vector 90 degrees counter-clockwise or clockwise will yield the correct outward normal depending on your coordinate system. Ensure \(\vec{n} \cdot \vec{d} < 0\), where \(\vec{d}\) is the incident laser direction; if the dot product is positive, negate the normal so it opposes the incoming ray.
3. Apply the Reflection Formula
The law of reflection states that the angle of incidence equals the angle of reflection. Using vector notation, the reflected direction vector \(\vec{r}\) is calculated from the normalized incident vector \(\vec{d}\) and the unit normal \(\vec{n}\):
\[\vec{r} = \vec{d} - 2(\vec{d} \cdot \vec{n})\vec{n}\]
In JavaScript:
function calculateReflection(incidentDir, normal) {
// Dot product of incident direction and surface normal
const dot = incidentDir.x * normal.x + incidentDir.y * normal.y;
// Reflection vector: R = D - 2 * (D . N) * N
return {
x: incidentDir.x - 2 * dot * normal.x,
y: incidentDir.y - 2 * dot * normal.y
};
}4. Convert the Reflected Vector to an Angle
To obtain the reflection angle in radians for rendering or projecting the next laser segment:
const reflectionAngle = Math.atan2(reflectedVector.y, reflectedVector.x);5. Cast Subsequent Reflections
To propagate the laser after the initial hit:
- Set the intersection point as the new origin.
- Advance the new origin slightly along the reflected vector \(\vec{r}\) (e.g., by
0.001units) to prevent the ray from immediately colliding with the same edge due to floating-point imprecision. - Cast a new ray in the direction of \(\vec{r}\) up to your maximum distance or bounce limit.