4

Okay, this is getting complicated...

Given situation:
I have a canvas with the dimensions of 800x600.
My mouse is at canvas position 100x200 (for example).

I save my canvas state.
Now I rotate and translate the canvas, I draw a square.
I restore my canvas state.

Is there any way to determine if my mouse is over the square?
I think I would have to translate/rotate my mouse position as well - in the opposite direction, but how would I do this?

Christian Engel
  • 3,738
  • 5
  • 28
  • 44
  • Do you have objects correlating to what you draw? Do you have a scenegraph? Do every object have a matrix? If not, how do you save position and rotation of what you draw? – bennedich Jan 13 '12 at 01:13
  • Yes, the square is also present as an object with the values x, y, w, h, r. The point where its getting complicated is that an object also can have children which inherit the parents position and rotation and are themselves positioned and rotated based on the parents origin. – Christian Engel Jan 13 '12 at 01:17

2 Answers2

4

You can get hold of an objects world position/rotation by recursively applying this formula:

worldX = parentX + x * Math.cos( parentR ) - y * Math.sin( parentR );
worldY = parentY + x * Math.sin( parentR ) + y * Math.cos( parentR );
worldR = parentR + r;

A javascript implementation would be:

var deg2rad, rad2deg, getXYR;

deg2rad = function ( d ) { return d * Math.PI / 180 };
rad2deg = function ( r ) { return r / Math.PI * 180 };

getXYR = function ( node ) {
  var x, y, r,
      parentXYR, pX, pY, pR,
      nX, nY;

  x = y = r = 0;

  if ( node ) {
    parentXYR = getXYR( node.parent );
    pX = parentXYR.x;
    pY = parentXYR.y;
    pR = deg2rad( parentXYR.r );
    nX = node.x;
    nY = node.y;

    x = pX + nX * Math.cos( pR ) - nY * Math.sin( pR );
    y = pY + nX * Math.sin( pR ) + nY * Math.cos( pR );
    r = rad2deg( pR + deg2rad( node.r ) );
  }

  return { x:x, y:y, r:r };
};

Try it out with these objects:

el1 = {x:3,y:0,r:45};
el2 = {x:0,y:0,r:45};
el1.parent = el2;
getXYR(el1);

It won't be long before you want to calculate the shortest angle between two objects, if you get the deltaX (x2-x1) and deltaY (y2-y1) between the two objects you can get the angle with this function:

var getAngle = function ( dx, dy ) {
  var r = Math.atan2( dy, dx ) * 180 / Math.PI;
  return ( r > 180 )  ? r-360 :
         ( r < -180 ) ? r+360 :
         r;
}

In the long run it's better to learn using matrices instead. The equivalence of getting the world pos/rot is a world matrix. Here's some good info about matrices (in the SVG doc, but it's not relevant): http://www.w3.org/TR/SVG/coords.html#NestedTransformations

This is how you would do it with matrices (and the gl-matrix lib https://github.com/toji/gl-matrix):

var getWorldMatrix = function ( node ) {
    var parentMatrix;

    if ( !node )
        return mat4.identity();

    parentMatrix = getWorldMatrix( node.parent );
    return mat4.multiply( parentMatrix, node.matrix );
};

Oh, i forgot, now to finally register a click you just get the screen coordinates of the mouse and compare them to the objects position + the canvas viewport offset.

bennedich
  • 12,150
  • 6
  • 33
  • 41
  • Your answer is awesome! Thank you for all the effort to help me :) I am directly going the matrix way - seems a bit complicated at the moment, but I hope it pays on the long run. Also, I didn't choose to use gl-matrix but Sylvester.js, since gl-matrix did some weird stuff to my matrices and is just too much away from the matrix format that ctx.transform() awaits. I hope my brain doesnt melt under the matrix power x) – Christian Engel Jan 13 '12 at 15:25
  • Good answer, another query! This question considers three factors, x, y and r; what if all transformation factors are considered. I mean what will the algorithm if scale, translate, skew and rotate transformations are applied. What is the source of your algorithm. – Software Enthusiastic Jun 05 '12 at 05:39
  • @bennedich Can you please look into this question? http://stackoverflow.com/questions/10892267/html5-canvas-transformation-algorithm-finding-object-coordinates-after-applyin – Software Enthusiastic Jun 05 '12 at 05:54
-1

Yes you have to either translate the mouse coordinates or retain a second set of coordinates for your shape. I recommend keeping a second set of coordinates as you will move the mouse more times than you transform the object. Try using an object like so

Box

function Box(x, y, w, h){
    this.x = x;
    this.y = y;
    this.tx = x;    //transformed x
    this.ty = y;    //transformed y
    this.w = w;
    this.h = h;

    this.mouseover = function(x, y){
        if (this.tx < x && this.tx + this.w > x && this.ty < y && this.ty + this.h > y){
            return true;
        }
        return false;
    }

    this.applyTransformation = function(transformation){
        switch(transformation){
        case 'rotation':
            //update tx/ty to match rotation
            break;
        case 'translation':
            //update tx/ty to match translation
            break;
       default:
            //do nothing or raise exception
    }
}
puk
  • 16,318
  • 29
  • 119
  • 199