0
\$\begingroup\$

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?

\$\endgroup\$
0
\$\begingroup\$

The atypical order of your matrix composition is scale then rotation then translation. As Matrices are not communitive, the order obviously matters. I'm going to assume (seeing that I have no experience with the library you are using that) is in the correct order, if it isn't, then I would guess swapping your scale and translation will make it work. The inversion of the matrix is basically going to return you to where your object origin was, so your understanding of the concepts is correct. You do not need to decompose the matrix into it's seperate steps for inverting

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.