I have a rounded rectangle at a specific x, y, w, h on a canvas. I first do a context.translate to get the object where I want it, then when it comes to rotating it, this is where I'm having issues working out the math needed.
I can do a simple context.rotate(Math.PI/180 * 25) to rotate it 25degs but it rotates from the x,y. I really want to shift the rotating point to like x + (w/2) and y + (w/2).
I'm not sure how to tell the rotate method to rotate it around a different point. I think I have to rotate it like normal but recalculate x,y perhaps based on the rotation maybe?
The canvas always rotates about the origin (0,0). The ctx.translate command can be thought of as shifting the origin, so you must translate by (x+w/2, y+h/2) before you rotate if you wish to rotate about the center of the rectangle.
(and of course, translate back after, or use save and restore)
Related
I am working on this "simple" problem for hours with no success, although I tried many ways to solve it using all kind of solutions suggested in SO.
My problem is as follows:
I have a point on a canvas, which when I click on it my app does something, after identifying the point by comparing the mouse click coordinates to the stored position of the point.
After zooming into the point, using the mouse wheel, I click on the point again but the mouse coordinates no longer fits the stored position of the point.
I need to either transform the mouse coordinates to it's coordinates before the zoom, so I will be able to compare to the stored position, or to transform the stored position to the new canvas so it can be compare to the coordinates of the mouse. Any of the solution is fine by me.
I know the following data:
The "scale" value,
The size of the canvas (top, left, width, height),
The new origin of the canvas (top, left)
I would like a solution using java script.
Finally figured it out and it is quite simple, once I understood the concept.
Save the new canvas origin after doing the zoom (in JS it is calling ctx.translate() and ctx.scale(), where ctx is the canvas context.
When need to calculate the mouse position in the old coordinate system, one has to add back the moved origin of the canvas, and multiply by the scale factor.
seat.x = (-new_org.x + pos.x) / scale;
seat.y = (-new_org.y + pos.y) / scale;
where pos is the calculated mouse pointer
pos.x = event.clientX - .getBoundingClientRect().left;
pos.y = event.clientY - .getBoundingClientRect().top;
I am trying to build a small app where my users can straighten up a tilted face with just 2 clicks
I ask my users to click on the middle of the nose and the middle of the eyebrows of the face within the image.
From there I get 2 points eyebrowMiddle(x1,y1) and noseMiddle (x2,y2).
Is it possible via these 2 points to calculate how much Canvas
rotation I need to have to rotate the image and make the face straight
in relation to the canvas rectangle?
Also, how can I detect and adjust accordingly if the image is tilted
to the left or right?
Here is a more descriptive image to show you what I mean now.
PS:
x1,y1 and x2,y2 are in relation to the canvas perimeter of
course, not the browser window or anything else.
We have tried the line equation such as m = (x2-x1) / (y2-y1) but the
result is always near 1 so I don't think we are following the right
course at the moment.
We don't care if the image looks wrong in the canvas as long as the
face features are parallel in relation to the bottom of the canvas
(they should be looking straight).
To perform such a rotation, you need to decide of the pivot point. Here i choose the eyebrow.
Then you have to choose a point in the target canvas where this pivot point will be hooked. I decided to choose the point at middle x coordinates, and at fourth of the screen in y.
To compute the rotation angle, you have to use atan2, which will nicely give you the angle for a given deltaY / deltaX in between two points ( angle = Math.atan2 ( delta y , delta x ) ) .
Then to draw :
- Translate to the target point.
- rotate by right angle.
- draw the image centering on its pivot.
ET VOILA, it works :-)
function rotate() {
ctx.save();
// go to default center position
ctx.translate(eyeBrowTargetPosition.x, eyeBrowTargetPosition.y);
// compute angle
var yDelta = noseMiddle.y - eyebrowMiddle.y;
var xDelta = noseMiddle.x - eyebrowMiddle.x ;
var angle = Math.atan2 (yDelta ,xDelta);
// compensate for angle
ctx.rotate(angle);
//draw image centering input on eyebrow
ctx.drawImage(face, -eyebrowMiddle.x, -eyebrowMiddle.y);
ctx.restore();
};
jsbin is here :
http://jsbin.com/wavokaku/2/edit?js,output
result with an approximation of the existing green dots :
Y coordinate of rectangle after rotation on canvas. As shown in image the rectangle will be rotated on its center point axis. After rotation and canvas is restored I want to find the new X,Y coordinates like one shown in second image, before rotation points were 50,50 and after rotation they could be 62,40.
I found similar question so I took the images from there but that question is for some WPF and my requirement is JS. How to find coordinates of all corners of rectangle after rotation?
I made a simple JavaScript transformation class for this exact purpose.
Using it you can transform arbitrary points by a transform of your making.
When you transform the canvas, transform the Transform object in the same way and then call transformPoint(x, y) to get back the appropriate coordinates.
So in your case calling transformPoint(50, 50) would return about [62, 40], etc.
https://github.com/simonsarris/Canvas-tutorials/blob/master/transform.js
Here's an example: http://jsfiddle.net/b2fEX/
For a game project, I'm drawing images using their properties such as fileName, position, scale, rotation.
Here is the part that does the drawing:
this.context.save();
this.context.translate(item.position.x, item.position.y);
if (item.rotation > 0) {
this.context.rotate(item.rotation * (Math.PI / 180));
}
if (item.scale.x !== 1 || item.scale.y !== 1) {
this.context.scale(item.scale.x, item.scale.y);
}
var width = item.imageSize.width * item.scale.x;
var height = item.imageSize.height * item.scale.y;
this.context.drawImage(this.assets.image[item.fileName], -(width / 2), -(height / 2), width, height);
this.context.restore();
(don't mind the strange positioning, it's not important)
This works fine, but there is one thing that I don't understand:
Rotation and scaling can be done in two different ways: first scale and then rotate, or other way around. Logically, one would think that first scale then rotation is correct, but for some reason, it only works correctly if I first rotate then scale.
What is the correct way of doing this?
Where is your point of origin for your objects? Are the x/y the top left? If so that could be causing the issue.
Demo Scaling Then Rotating
ctx.translate(box.x, box.y);
ctx.scale(2,2);
ctx.rotate(box.angle);
Demo Rotating Then Scaling
ctx.translate(box.x, box.y);
ctx.rotate(box.angle);
ctx.scale(2,2);
If you notice both of those demos work fine regardless of when I perform the scaling and rotating. My point of origins however (where I translate to) are in the center of the boxes.
To answer your question (or attempt to) there is no right or wrong way, you can scale first or rotate first, its really just a matter of preference.
The "correct way" really depends on what you're trying to do. For you, it seems like rotating and then scaling results in what you expect.
Here is a fiddle that shows the difference between rotating then scaling and scaling then rotating: http://jsfiddle.net/HYbC7/3/
What's unexpected for me is when you scale then rotate. If you scale(x, y) where x and y are not equal, then rotate, then the rotation along the x-axis will be different than the rotation along the y-axis and the resulting grid will be skewed.
I'm drawing a symbol with simple lines, but want the user to be able to specify the rotation (in 90degrees only).
Which also means that the dimensions of my canvas change.
No I calculate the dimensions, and set the canvas size. Then I set the center of the rotation to the center of my canvas (via ctx.translate) and rotate to the arbitrary degrees.
Now my problem is: How do I set the translation back to the upper left corner, so I can draw my symbol normally from that position? Do I really have to calculate the values with the rotation?
Thanks.
Great question! translate rotate and scale are all functions that operate upon the current matrix. This gives you lots of options. Probably the simplest thing is to do a save restore on the context matrix
ctx.save();
ctx.translate ( to the center );
ctx.rotate ( do rotation );
//Draw rotated stuff
ctx.restore();
//Draw non-rotated stuff
Now after you call restore, the matrix reverts to how it was before the last save - In opengl, this is actually a stack, and you can push many contexts, but I'm not sure if webgl supports that.
This link may also be helpful: https://developer.mozilla.org/en/drawing_graphics_with_canvas
Hope this helps.
Update:
Yes. Okay, so you are misunderstanding something a little bit. The translations and rotations are applied before the drawing. This is because of a lot of complex math, and is really beyond the scope of this question. So if you want to draw part of your canvas rotated one way, and the other part of it rotated a different way, you first save, then apply the transformations, do the first part of the drawing, then restore to get back to the pre-transformed state. At which point, you can repeat.
So, for example, you can do this:
ctx.save();
ctx.translate ( x_center, y_center );
ctx.rotate ( 90 );
//Draw your rotated stuff starting at the center
ctx.translate ( -x_center, -y_center );
//Draw your main frame stuff that is all rotated around the center
ctx.restore();
ctx.save();
ctx.rotate ( 90 );
//Draw your text which is rotated around the top-left corner
ctx.restore();
In this way, you have 1 drawing function, and you simply setup a context before you draw the different components.