I'm trying to determine which nodes in my scene graph contain a given point in global coordinate space, using the inverse of the node's global transformation matrices.
Here's a simplified version of the Node (real version does dirty checks and caches matrix etc).
class Node {
parent: Node;
width: number;
height: number;
translation: Vec2;
rotation: number;
scale: number;
// Get the transformation matrix required to draw this node onscreen
get matrix() {
let mat = mat2d.clone(this.parent.matrix);
mat2d.translate(mat, mat, this.translation);
mat2d.rotate(mat, mat, this.rotation);
mat2d.scale(mat, mat, [this.scale, this.scale]);
this.matrix = mat;
}
// Check if point is inside this node's rect
contains(x: number, y: number) {
let point = this.toLocalPoint(x, y);
return (
point.x >= this.translation.x &&
point.y >= this.translation.y &&
point.x <= this.translation.x + this.width &&
point.y <= this.translation.y + this.height
);
}
// Convert a point in global space to local space
toLocalPoint(x: number, y: number) {
let inverse = mat2d.create();
mat2d.invert(inverse, this.matrix);
let point = vec2.fromValues([x, y]);
vec2.transformMat2d(point, point, inverse);
return point;
}
}
This is working fine for the renderer. Here's an example of how that looks for reference.
for (let node of nodes) {
renderer.push(node.matrix);
renderer.draw(node.texture);
renderer.pop();
}
The Node#contains
method has some problems. It seems to adjust correctly to detect rotation and scale changes, but as soon as there's a translation applied to a node, the inverse matrix gives the wrong results.
Here's an example that shows a test of whether the mouse cursor is inside a node.
- The cyan node is at the origin, given a scale, and rotation.
- The magenta node has the same scale and rotation, but it has also been translated along the X axis.
There are points off to the right that are registering as inside the node though.
I'm fairly sure that this means that the separate steps of the matrix transform are being applied in the wrong order, but I'm not sure how to apply the inverse matrix in any other way (without decomposing it into the 3 individual components of the transform and applying them in the reverse order that the Node#matrix
getter does).
Have I misunderstood how the inverse matrix works here?