I have a question with regards to the pivot property of a DisplayObject. In particular, i want to rotate a DisplayObjectContainer around its center; so i set the pivot to its center point. However, i've noticed that this affects the position of the element.
For example, if i set the position to 0,0 (which is the default one) pixijs will try to position the object according to its center point and not the top left corner. So the children of the the DisplayObjectContainer (which in my case is an instance of the Graphics class) run out of the main viewport.
Is there any way to set the rotation point but still position the object in respect to its top left corner.
You need to draw the graphics container at the point at which you wish your object to rotate around and then draw the object so that its center is the graphic's x/y position. So, to draw a rectangle that is drawn at the precise coordinates you wish while pivoting around its center, you would use this function.
self.createRect = function (x1, y1, x2, y2, color) {
var graphics = new PIXI.Graphics();
graphics.beginFill(color || 0x000000);
//This is the point around which the object will rotate.
graphics.position.x = x1 + (x2/2);
graphics.position.y = y1 + (y2/2);
// draw a rectangle at -width/2 and -height/2. The rectangle's top-left corner will
// be at position x1/y1
graphics.drawRect(-(x2/2), -(y2/2), x2, y2);
self.stage.addChild(graphics);
return graphics;
};
Since it's a square or rectangle, we can calculate where its center should be on the screen by using x1 + (width/2) and y1 + (height/2) then we offset the rect to the left and to the top by half of its width and half of its height using drawRect(-(width/2), -(height/2), x2, y2);
The pivot property is a bit confusing. Imagine the following example:
Your pivot is a screw located somewhere on the object (it can be of course also located somewhere outside, but just for better understanding imagine that your object is a plank of wood with a screw sticking out). Your position (of the graphics/container) is a screw. The object is always rotating around the position, but you can change the pivot (the position of the screw on your plank of wood) on the object, so it will be the new point of the rotation for the object. Finally you can try to screw your plank of wood to the screw.
Basically, the default values of the position and pivot is 0. So if you have your object drawn for example here:
test.drawRoundedRect(100, 100, 200, 200,12);
you can now try to rotate it and you'll see, that it is rotating around the point (0,0).
The graphic is always rotating around the position, you can try to locate it somewhere else:
test.position.x = 200;
test.position.y = 200;
The object is now rotating around the point (200,200). But that's just a shift.
We can now try to change the pivot point (which is the screw) to any different position. So, on your plank of wood you just place the screw on (50,50), then (100,100), etc and you'll see that it affects your object position.
Now, for our example, we can set the pivot point to (200,200) to the same coordinates as the position of the object.
test.pivot.x = 200;
test.pivot.y = 200;
And finally it is rotating around the center point of the drawn object.
The solution provided by #Spencer is an alternative of the pivot property.
Related
I've already asked this same question months ago, but no one was able to answer me, even after making a fully functional example on Plunker, then, I am going to ask it again, and yes, I still have the same problem.
My problem: find the centre of an element who have some rotation in it, after resizing it, to use it as the new pivot of rotation.
In my practical example, it is possible to see the problem in action; I have created two circles to show the problem better. After rotating and resizing the element, it's possible to see how the red and blue circles are apart from each other.
Blue Circle: the "correct" position of the centre, achieved by setting the cx/cy coordinates as the calculated element centre, plus, applying the transform rotate in it. The transform translates the circle to the correct position.
Red Circle: same as the blue circle, minus the transform rotate, these values are the ones used as the rotation pivot for the transform rotate().
My assumptions until here: By applying the transform rotate() in the blue circle, I'm considering the rotation angle in the calculated centre, so all I have to do is replicate the matrix calculations made by the rotate() function. I'm already doing this with the four handles that the user can click to make a rotation, what could go wrong?
My goal: Resize an element with rotation keeping the pivot of rotation in the centre.
I think this answer gave me some info, the math here helped me with the rotation handles starting position, but still, I can't find the right way to calculate the new centre after the resize.
The example was made using D3js + AngularJS v1. I work actively with both, but I am new to the geometry math world.
Again, this is the project on Plunker.
To get the centre of the transformed and rotated element, the most accurate way would probably be to get the browser to calculate it for you.
First create an SVGPoint object to hold our original centre point.
var centre = svg.createSVGPoint();
Initialize this point with the centre of the original object. You can get that by calling getBBox() on the element, and performing a smiple calculation.
var bbox = obj.getBBox();
centre.x = bbox.x + bbox.width / 2;
centre.y = bbox.y + bbox.height / 2;
Next, get the transform matrix from the transform attribute of the transformed object
var matrix = transformedObj.transform.baseVal.consolidate().matrix
Now we can transform our SVGPoint object with this matrix.
var transformedCentre = centre.matrixTransform(matrix);
After this, the x and y properties of transformedCentre should be your transformed centre point.
This should work, but I haven't tested it.
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 :
I've searched for the answer to this and have tried many proposed solutions, but none seem to work. I've been struggling with this forever so any insight is greatly appreciated.
I have 3 shapes (vectors I suppose) on a JS canvas, each with an orientation represented as degrees off of 0 and a width. I need to drag one of these shapes "straight out" from its orientation. This is difficult to explain in words so please view the graphic I created:
The middle (diagonal) shape is at 45 degrees. It's origin is the red dot, (x1,y1). The user drags the shape and their mouse lies at the green dot, (x2,y2). Since the shape's origin is in the lower left, I need to position the shape at the position of the lighter blue shape as if the user has dragged straight outward from the shape's origin.
I don't think it matters, but the library I'm using to do this is KineticJS. Here's the code and some information I have available which may help solve the problem. This code positions the shape on top of the mouse, which isn't what I want:
var rotationDeg = this.model.get("DisplayOri"), // rotation in degrees
rotationRadians = rotationDeg * Math.PI / 180, // rotation in rads
unchanged = this.content.getAbsolutePosition(), // {x,y} of the shape before any dragging
dragBoundFunc = function (changed) {
// called on a mouseMove event, so changed is always different and is the x,y of mouse on stage
var delta = {
x: changed.x - unchanged.x,
y: changed.y - unchanged.y
};
return changed; // go to the mouse position
};
[edit] I should mention that the obvious of "return delta" doesn't work.
It sounds like you want to constrain the movement of the object.
Determine the vector representing the constraint axis : that is, we only want motion to occur along this line. It appears from your drawing that this is in the direction of the short line from the red dot out to the left. That vector has a direction of -1/m where m is the slope of the line we are moving.
Constrain the movement. The movement is represented by the mouse move delta - but we only want the portion of that movement in the direction of the constraint axis. This is done with a dot product of the two vectors.
So in pseudo code
m = (line.y2 - line.y1)/(line.x2 - line.x1)
constraintSlope = -1/m
contraintVector = {1, constraintSlope} //unit vector in that direction
userMove = {x2-x1, y2-y1} //vector of mouse move direction
projection = userMove.x * constraintVector.x + userMove.y * constraintVector.y
translation = projection * constraintVector //scaled vector
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)
I'm having serious trouble understanding how to compute the coordinates of rotation/scale pivots (e.g. rotation point) for SVG transformations, using Raphael.js. In short, if you apply a transformation such as image.transform("S1.5R45"), the transformations are applied in relation to the default rotation & scale pivot, which I'm not sure how to calculate.
To exemplify, I've put together a fiddle (jsfiddle.net/GVEqf/), where the aim is to have exactly the same output in both the viewports, for a couple of transformations on an image object. In the first viewport, I don't specify the rotation point, but in the second one I do. However, I can't get the same results. What I need is to input the rotation coordinates for the second viewport, so that the output is identical with the first case.
Please help.
When not specified the pivot is the center of the element.
Here you have to take care of the position you have applied to the images and the scaling that will be done. Since in this case your scaling is relative to the top left corner of the image, we can just multiply the center coordinate by it.
// Compute rotation pivot coordinates
var scaling = 1.5;
rx = (x + (img_width / 2)) * scaling;
ry = (y + (img_height / 2)) * scaling;
// Apply transformations
image1.transform("S1.5,1.5,0,0R45");
image2.transform("S1.5,1.5,0,0R45,"+rx+","+ry);
http://jsfiddle.net/TYCJ7/