I have two rectangles with pivots,
I need to attach the position of the green rectangle based on the rotation of the red rectangle
The result should be like in the picture:
I tried different formulas and nothing succeeded
red rectangle:
x=500, y=100, width=200, height=500, pivotX=100, pivotY=400
green rectangle:
x=450, y=150, width=100, height=400, pivotX=50, pivotY=50
I tried something like this:
var radians = (Math.PI / 180) * red.degree;
green.x += red.pivotX * Math.cos(radians) - red.pivotY * Math.sin(radians);
green.y += red.pivotX * Math.sin(radians) + red.pivotY * Math.cos(radians);
Big thanks to everyone that helped!
The pivot of a rectangle is originally at position (x + pivotX, y + pivotY). Take the vector pointing from the red pivot point towards the green pivot point, namely
vx = green.x + green.pivotX - red.x - red.pivotX;
vy = green.y + green.pivotY - red.y - red.pivotY;
That's the vector you rotate:
wx = Math.cos(radians)*vx - Math.sin(radians)*vy;
wy = Math.sin(radians)*vx + Math.cos(radians)*vy;
Then you can use that rotated w instead of the original v to determine the position of the green rectangle:
green.x += wx - vx;
green.y += wy - vy;
Related
I have the xy coordinates from before and during a drag event, this.x and this.y```` are the current coordinates,this.lastXandthis.lastY``` are the origin.
What I need to do is given a radian of the source element, determine which mouse coordinate to use, IE if the angle is 0 then the x coordinates is used to give a "distance" if the degrees are 90 then the y coordinates are used
if the radian is 0.785398 then both x and y would need to be used.
I have the following code for one axis, but this only flips the y coordinates
let leftPosition;
if (this.walls[this.dragItem.wall].angle < Math.PI / 2) {
leftPosition = Math.round((-(this.y - this.lastY) / this.scale + this.dragItem.origin.left));
} else {
leftPosition = Math.round(((this.y - this.lastY) / this.scale + this.dragItem.origin.left));
}
I have an example here https://engine.owuk.co.uk
what I need to do is have the radian dictate what x or y coordinate is used to control the drag of the item by calculating the leftPosition, I have been loosing my mind trying to get this to work :(
The Math.sin and Math.cos is what you need, here is an example
<canvas id="c" width=300 height=150></canvas>
<script>
const ctx = document.getElementById('c').getContext('2d');
function drawShape(size, angle, numPoints, color) {
ctx.beginPath();
for (j = 0; j < numPoints; j++) {
a = angle * Math.PI / 180
x = size * Math.sin(a)
y = size * Math.cos(a)
ctx.lineTo(x, y);
angle += 360 / numPoints
}
ctx.fillStyle = color;
ctx.fill();
}
ctx.translate(80, 80);
drawShape(55, 0, 7, "green");
drawShape(45, 0, 5, "red");
drawShape(35, 0, 3, "blue");
ctx.translate(160, 0);
drawShape(55, 15, 7, "green");
drawShape(45, 35, 5, "red");
drawShape(35, 25, 3, "blue");
</script>
Here is a theoretical answer to your problem.
In the simplest way, you have an object within a segment that has to move relative to the position of the mouse, but constrained by the segment's vector.
Here is a visual representation:
So with the mouse at the red arrow, the blue circle needs to move to the light blue.
(the shortest distance between a line and a point)
How do we do that?
Let's add everything we can to that image:
The segment and the mouse form a triangle and we can calculate the length of all sides of that triangle.
The distance between two points is an easy Pythagorean calculation:
https://ncalculators.com/geometry/length-between-two-points-calculator.htm
Then we need the height of the triangle where the base is our segment:
https://tutors.com/math-tutors/geometry-help/how-to-find-the-height-of-a-triangle
That will give us the distance from our mouse to the segment, and we do know the angle by adding the angle of the segment + 90 degrees (or PI/2 in radians) that is all that we need to calculate the position of our light blue circle.
Of course, we will need to also add some min/max math to not exceed the boundaries of the segment, but if you made it this far that should be easy pickings.
I was able to make the solution to my issue
let position;
const sin = Math.sin(this.walls[this.dragItem.wall].angle);
const cos = Math.cos(this.walls[this.dragItem.wall].angle);
position = Math.round(((this.x - this.lastX) / this.scale * cos + (this.y - this.lastY) / this.scale * sin) + this.dragItem.origin.left);
I'm trying to find intersecting point on circle. Can anybody solve this?. please find my problem's explanation image click here to see image
I'm going to assume you have the radius of the circle, as well as the center point of the circle. If you do, then the coordinates of the respective points are as follows:
// assume r is set to the radius of the circle
// and (x, y) is the center of the circle
p1.x = x - r * Math.sin(Math.acos(150 / r));
p1.y = y + 150;
p2.x = x + r * Math.sin(Math.acos(150 / r));
p2.y = y + 150;
I have a function which gets the mouse position in world space, then checks to see if the mouse is over or near to the circle's line.
The added complication how ever is the circle is transformed at an angle so it's more of an ellipse. I can't see to get the code to detect that the mouse is near the border of circle and am unsure where I am going wrong.
This is my code:
function check(evt){
var x = (evt.offsetX - element.width/2) + camera.x; // world space
var y = (evt.offsetY - element.height/2) + camera.y; // world space
var threshold = 20/scale; //margin to edge of circle
for(var i = 0; i < obj.length;i++){
// var mainAngle is related to the transform
var x1 = Math.pow((x - obj[i].originX), 2) / Math.pow((obj[i].radius + threshold) * 1,2);
var y1 = Math.pow((y - obj[i].originY),2) / Math.pow((obj[i].radius + threshold) * mainAngle,2);
var x0 = Math.pow((x - obj[i].originX),2) / Math.pow((obj[i].radius - threshold) * 1, 2);
var y0 = Math.pow((y - obj[i].originY),2) / Math.pow((obj[i].radius - threshold) * mainAngle, 2);
if(x1 + y1 <= 1 && x0 + y0 >= 1){
output.innerHTML += '<br/>Over';
return false;
}
}
output.innerHTML += '<br/>out';
}
To understand it better, I have a fiddle here: http://jsfiddle.net/nczbmbxm/ you can move the mouse over the circle, it should say "Over" when you are within the threshold of being near the circle's perimeter. Currently it does not seem to work. And I can't work out what the maths needs to be check for this.
There is a typo on line 34 with orignX
var x1 = Math.pow((x - obj[i].orignX), 2) / Math.pow((obj[i].radius + threshold) * 1,2);
should be
var x1 = Math.pow((x - obj[i].originX), 2) / Math.pow((obj[i].radius + threshold) * 1,2);
now you're good to go!
EDIT: In regards to the scaling of the image and further rotation of the circle, I would set up variables for rotation about the x-axis and y-axis, such as
var xAngle;
var yAngle;
then as an ellipse can be written in the form
x^2 / a^2 + y^2 / b^2 = 1
such as in Euclidean Geometry,
then the semi-major and semi-minor axes would be determined by the rotation angles. If radius is the circles actual radius. then
var semiMajor = radius * cos( xAngle );
var semiMinor = radius;
or
var semiMajor = radius;
var semiMinor = radius * cos( yAngle );
you would still need to do some more transformations if you wanted an x and y angle.
so if (xMouseC, yMouseC) are the mouse coordinates relative to the circles centre, all you must do is check if that point satisfies the equation of the ellipse to within a certain tolerance, i.e. plug in
a = semiMajor;
b = semiMinor;
x = xMouseC;
y = yMouseC;
and see if it is sufficiently close to 1.
Hope that helps!
I am writing a multitouch jigsaw puzzle using html5 canvas in which you can rotate the pieces around a point. Each piece has their own canvas the size of their bounding box. When the rotation occurs, the canvas size must change, which I am able to calculate and is working. What I can't figure out, is how to find the new x,y offsets if I am to have this appear to be rotating around the pivot (first touch point).
Here is an image to better explain what I'm trying to achieve. Note the pivot point is not always the center, otherwise I could just halve the difference between the new bounds and the old.
So I know the original x, y, width, height, rotation angle, new bounds(rotatedWidth, rotatedHeight), and the pivot X,Y relating to original object. What I can't figure out how to get is the x/y offset for the new bounds (to make it appear that the object rotated around the pivot point)
Thanks in advance!
First we need to find the distance from pivot point to the corner.
Then calculate the angle between pivot and corner
Then calculate the absolute angle based on previous angle + new angle.
And finally calculate the new corner.
Snapshot from demo below showing a line from pivot to corner.
The red dot is calculated while the rectangle is rotated using
translations.
Here is an example using an absolute angle, but you can easily convert this into converting the difference between old and new angle for example. I kept the angles as degrees rather than radians for simplicity.
The demo first uses canvas' internal translation and rotation to rotate the rectangle. Then we use pure math to get to the same point as evidence that we have calculated the correct new x and y point for corner.
/// find distance from pivot to corner
diffX = rect[0] - mx; /// mx/my = center of rectangle (in demo of canvas)
diffY = rect[1] - my;
dist = Math.sqrt(diffX * diffX + diffY * diffY); /// Pythagoras
/// find angle from pivot to corner
ca = Math.atan2(diffY, diffX) * 180 / Math.PI; /// convert to degrees for demo
/// get new angle based on old + current delta angle
na = ((ca + angle) % 360) * Math.PI / 180; /// convert to radians for function
/// get new x and y and round it off to integer
x = (mx + dist * Math.cos(na) + 0.5)|0;
y = (my + dist * Math.sin(na) + 0.5)|0;
Initially you can verify the printed x and y by seeing that the they are exact the same value as the initial corner defined for the rectangle (50, 100).
UPDATE
It seems as I missed the word in: offset for the new bounds... sorry about that, but what you can do instead is to calculate the distance to each corner instead.
That will give you the outer limits of the bound and you just "mix and match" the corner base on those distance values using min and max.
New Live demo here
The new parts consist of a function that will give you the x and y of a corner:
///mx, my = pivot, cx, cy = corner, angle in degrees
function getPoint(mx, my, cx, cy, angle) {
var x, y, dist, diffX, diffY, ca, na;
/// get distance from center to point
diffX = cx - mx;
diffY = cy - my;
dist = Math.sqrt(diffX * diffX + diffY * diffY);
/// find angle from pivot to corner
ca = Math.atan2(diffY, diffX) * 180 / Math.PI;
/// get new angle based on old + current delta angle
na = ((ca + angle) % 360) * Math.PI / 180;
/// get new x and y and round it off to integer
x = (mx + dist * Math.cos(na) + 0.5)|0;
y = (my + dist * Math.sin(na) + 0.5)|0;
return {x:x, y:y};
}
Now it's just to run the function for each corner and then do a min/max to find the bounds:
/// offsets
c2 = getPoint(mx, my, rect[0], rect[1], angle);
c2 = getPoint(mx, my, rect[0] + rect[2], rect[1], angle);
c3 = getPoint(mx, my, rect[0] + rect[2], rect[1] + rect[3], angle);
c4 = getPoint(mx, my, rect[0], rect[1] + rect[3], angle);
/// bounds
bx1 = Math.min(c1.x, c2.x, c3.x, c4.x);
by1 = Math.min(c1.y, c2.y, c3.y, c4.y);
bx2 = Math.max(c1.x, c2.x, c3.x, c4.x);
by2 = Math.max(c1.y, c2.y, c3.y, c4.y);
to rotate around the centre point of the canvas you can use this function:
function rotate(context, rotation, canvasWidth, canvasHeight) {
// Move registration point to the center of the canvas
context.translate(canvasWidth / 2, canvasHeight/ 2);
// Rotate 1 degree
context.rotate((rotation * Math.PI) / 180);
// Move registration point back to the top left corner of canvas
context.translate(-canvasWidth / 2, -canvasHeight/ 2);
}
Here is the way that worked best for me. First I calculate what is the new width and height of that image, then I translate it by half of that amount, then I apply the rotation and finally I go back by the original width and height amount to re center the image.
var canvas = document.getElementById("canvas")
const ctx = canvas.getContext('2d')
drawRectangle(30,30,40,40,30,"black")
function drawRectangle(x,y,width,height, angle,color) {
drawRotated(x,y,width,height,angle,ctx =>{
ctx.fillStyle = color
ctx.fillRect(0,0,width,height)
})
}
function drawRotated(x,y,width,height, angle,callback)
{
angle = angle * Math.PI / 180
const newWidth = Math.abs(width * Math.cos(angle)) + Math.abs(height * Math.sin(angle));
const newHeight = Math.abs(width * Math.sin(angle)) + Math.abs(height * Math.cos(angle));
var surface = document.createElement('canvas')
var sctx = surface.getContext('2d')
surface.width = newWidth
surface.height = newHeight
// START debug magenta square
sctx.fillStyle = "magenta"
sctx.fillRect(0,0,newWidth,newHeight)
// END
sctx.translate(newWidth/2,newHeight/2)
sctx.rotate(angle)
sctx.translate(-width/2,-height/2)
callback(sctx)
ctx.drawImage(surface,x-newWidth/2,y-newHeight/2)
}
#canvas{
border:1px solid black
}
<canvas id="canvas">
</canvas>
I found this excellent question and answer which starts with x/y (plus the center x/y and degrees/radians) and calculates the rotated-to x'/y'. This calculation works perfectly, but I would like to run it in the opposite direction; starting with x'/y' and degrees/radians, I would like to calculate the originating x/y and the center x/y.
(x', y') = new position
(xc, yc) = center point things rotate around
(x, y) = initial point
theta = counterclockwise rotation in radians (radians = degrees * Pi / 180)
dx = x - xc
dy = y - yc
x' = xc + dx cos(theta) - dy sin(theta)
y' = yc + dx sin(theta) + dy cos(theta)
Or, in JavaScript/jQuery:
XYRotatesTo = function($element, iDegrees, iX, iY, iCenterXPercent, iCenterYPercent) {
var oPos = $element.position(),
iCenterX = ($element.outerWidth() * iCenterXPercent / 100),
iCenterY = ($element.outerHeight() * iCenterYPercent / 100),
iRadians = (iDegrees * Math.PI / 180),
iDX = (oPos.left - iCenterX),
iDY = (oPos.top - iCenterY)
;
return {
x: iCenterX + (iDX * Math.cos(iRadians)) - (iDY * Math.sin(iRadians)),
y: iCenterY + (iDX * Math.sin(iRadians)) + (iDY * Math.cos(iRadians))
};
};
The math/code above solves for the situation in Figure A; it calculates the position of the destination x'/y' (green circle) based on the known values for x/y (red circle), the center x/y (blue star) and the degrees/radians.
But I need math/code to solve for Figure B; where I can find not only the destination x/y (green circle), but also the destination center x/y (green star) from the known values of the starting x/y (grey circle, though probably not needed), the destination x'/y' (red circle) and the degrees/radians.
The code above will solve for the destination x/y (green circle) via iDegrees * -1 (thanks to #andrew cooke's answer which has since been removed by him), but in order to do that I need to feed into it the location of the destination center x/y (green star), and that is the calculations I'm currently missing, as you can see in Diagram C, below:
So... how do I find the coordinates ?/? (green star) given n, A (angle) and x'/y' (red circle)?
You're trying to find an inverse transformation. You start with the composition of two linear transformations, a translation T and a rotation R. You apply R first to a vector x and T second, so the expression is y = TRx. To solve the inverse problem you need the inverse of TR, written (TR)-1, which is equal to R-1T-1. The inverse of the rotation R is just the rotation by the negative of the angle (which you mention). The inverse of the translation is, similarly, the original translation multiplied by -1. So your answer is x = R-1T-1y.
In your present situation, you're given the rotation by means of its angle, but you'll need to compute the translation. You'll need the grey circle, which you didn't think you would need. Apply the rotation R (not its inverse) to the gray circle. Subtract this point from the red circle. This is the original translation T. Reverse the sign to get T-1.