Okay, this is getting plicated...
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?
Okay, this is getting plicated...
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?
- 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 Commented Jan 13, 2012 at 1:13
- Yes, the square is also present as an object with the values x, y, w, h, r. The point where its getting plicated 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 Commented Jan 13, 2012 at 1:17
2 Answers
Reset to default 4You 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/TR/SVG/coords.html#NestedTransformations
This is how you would do it with matrices (and the gl-matrix lib https://github./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 pare them to the objects position + the canvas viewport offset.
Yes you have to either translate the mouse coordinates or retain a second set of coordinates for your shape. I remend 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
}
}