Easiest way to rotate a rectangle - math

I'm using rectangles defined in terms of their x y coordinates and their width and height. I figured out how to rotate them in terms of coordinates (x = cos(deg) * x - sin(deg) * y y = sin(deg) * x + cos(deg) * y) but I'm stuck on the height and width. I'm sure there's an obvious solution that I'm missing. If it matters, I'm using Python.
edit Sorry for the confusing description. My intention is to get the width and height either reversed or negated due to whatever the angle is. For example, in a 90 degree rotation the values would switch. In a 180 degree rotation the width would be negative. Also, I only intend to use multiples of 90 in my script. I could just use if statements, but I assumed there would be a more "elegant" method.

Just calculate four corners of Your rectangle:
p1 = (x, y)
p2 = (x + w, y)
p3 = (x, y + h)
and rotate each by angle You want:
p1 = rotate(p1, angle)
# and so on...
and transform back to Your rectangle representation:
x, y = p1
w = dist(p1, p2) # the same as before rotation
h = dist(p1, p3)
where dist calculates distance between two points.
Edit: Why don't You try apply formula You have written to (width, height) pair?
x1 = cos(deg) * x - sin(deg) * y
y2 = sin(deg) * x + cos(deg) * y
It is easy to see that if deg == 90 the values will switch:
x1 = -y
y2 = x
and if deg == 180 they will be negated:
x1 = -x
y2 = -y
and so on... I think this is what You are looking for.
Edit2:
Here comes fast rotation function:
def rotate_left_by_90(times, x, y):
return [(x, y), (-y, x), (-x, -y), (y, -x)][times % 4]

The proper way would be to resort to transformation matrices. Also, judging from your question I suppose that you want to rotate with respect to (x=0,y=0), but if not you will need to take this into account and translate your rectangle to the center of the plan first (and then translate it back when the rotation is carried out).
M = Matrix to translate to the center
R = Rotation Matrix
Transformation Matrix = M^(-1) * R * M
But to give you an easy answer to your question, just take the two other corners of your rectangle and apply the same transformation on them.
To learn more about transformation matrices :
http://en.wikipedia.org/wiki/Transformation_matrix

From the way you describe only rotating by 90 degrees, and the way you seem to be defining width and height, perhaps you are looking for something like
direction = 1 // counter-clockwise degrees
// or
direction = -1 // clockwise 90 degrees
new_height = width * direction
new_width = -height * direction
width = new_width
height = new_height
Not sure why you want to have negative values for width and height, though .. because otherwise each 90 degree rotation effectively just swaps width and height, regardless which way you rotate.

Rotation should not change width and height. Your equation is correct if you want to rotate (x,y) about (0,0) by deg, but note that often cos and sin functions expect arguments in radians rather than degrees, so you may need to multiply deg by pi/180 (radians per degree).
If you need to find the locations of other rectangle vertices besides (x,y) after rotating, then you should either store and rotate them along with (x,y) or keep some information about the rectangle's orientation (such as deg) so you can recompute them as e.g. x+widthcos(deg), y+heightsin(deg).

Related

How to efficiently compute the future position of a point that will move in a box and bounce on its walls (2D)?

I have a simple maths/physics problem here: In a Cartesian coordinate system, I have a point that moves in time with a known velocity. The point is inside a box, and bounces orthognally on its walls.
Here is a quick example I did on paint:
What we know: The red point position, and its velocity which is defined by an angle θ and a speed. Of course we know the dimensions of the green box.
On the example, I've drawn in yellow its approximate trajectory, and let's say that after a determined period of time which is known, the red point is on the blue point. What would be the most efficient way to compute the blue point position?
I've tought about computing every "bounce point" with trigonometry and vector projection, but I feel like it's a waste of resources because trigonometric functions are usually very processor hungry. I'll have more than a thousand points to compute like that so I really need to find a more efficient way to do it.
If anyone has any idea, I'd be very grateful.
Apart from programming considerations, it has an interesting solution from geometric point of view. You can find the position of the point at a specific time T without considering its temporal trajectory during 0<t<T
For one minute, forget the size and the boundaries of the box; and assume that the point can move on a straight line for ever. Then the point has constant velocity components vx = v*cos(θ), vy = v*sin(θ) and at time T its virtual porition will be x' = x0 + vx * T, y' = y0 + vy * T
Now you need to map the virtual position (x',y') into the actual position (x,y). See image below
You can recursively reflect the virtual point w.r.t the borders until the point comes back into the reference (initial) box. And this is the actual point. Now the question is how to do these mathematics? and how to find (x,y) knowing (x',y')?
Denote by a and b the size of the box along x and y respectively. Then nx = floor(x'/a) and ny = floor(y'/b) indicates how far is the point from the reference box in terms of the number of boxes. Also dx = x'-nx*a and dy = y'-ny*b introduces the relative position of the virtual point inside its virtual box.
Now you can find the true position (x,y): if nx is even, then x = dx else x = a-dx; similarly if ny is even, then y = dy else y = b-dy. In other words, even number of reflections in each axis x and y, puts the true point and the virtual point in the same relative positions, while odd number of reflections make them different and complementary.
You don't need to use trigonometric function all the time. Instead get normalized direction vector as (dx, dy) = (cos(θ), sin(θ))
After bouncing from vertical wall x-component changes it's sign dx = -dx, after bouncing from horizontal wall y-component changes it's sign dy = -dy. You can see that calculations are blazingly simple.
If you (by strange reason) prefer to use angles, use angle transformations from here (for ball with non-zero radius)
if ((ball.x + ball.radius) >= window.width || (ball.x - ball.radius) <= 0)
ball.theta = M_PI - ball.theta;
else
if ((ball.y + ball.radius) >= window.height || (ball.y - ball.radius) <= 0)
ball.theta = - ball.theta;
To get point of bouncing:
Starting point (X0, Y0)
Ray angle Theta, c = Cos(Theta), s = Sin(Theta);
Rectangle coordinates: bottom left (X1,Y1), top right (X2,Y2)
if c >= 0 then //up
XX = X2
else
XX = X1
if s >= 0 then //right
YY = Y2
else
YY = Y1
if c = 0 then //vertical ray
return Intersection = (X0, YY)
if s = 0 then //horizontal ray
return Intersection = (XX, Y0)
tx = (XX - X0) / c //parameter when vertical edge is met
ty = (YY - Y0) / s //parameter when horizontal edge is met
if tx <= ty then //vertical first
return Intersection = (XX, Y0 + tx * s)
else //horizontal first
return Intersection = (X0 + ty * c, YY)

calculate angle from vector to coord

I am breaking my head trying to find an appropriate formula to calculate a what sounds to be an easy task but in practice is a big mathematical headache.
I want to find out the offset it needs to turn my vector's angle (X, Y, Angle) to face a coord ( X, Y )
My vector won't always be facing 360 degrees, so i need that as a variable as well..
Hoping an answer before i'm breaking my pc screen.
Thank you.
input
p1 = (x1,y1) point1 (vector origin)
p2 = (x2,y2) point2
a1 = 360 deg direction of vector
assuming your coodinate system is: X+ is right Y+ is up ang+ is CCW
your image suggest that you have X,Y mixed up (angle usually start from X axis not Y)
da=? change of a1 to match direction of p2-p1
solution 1:
da=a1-a2=a1-atanxy(x2-x1,y1-y1)
atanxy(dx,dy) is also called atan2 on some libs just make sure the order of operands is the right one
you can also use mine atanxy in C++
it is 4 quadrant arctangens
solution 2:
v1=(cos(a1),sin(a1))
v2=(x2-x1,y2-y1)
da=acos(dot(v1,v2)/(|v1|*|v2|))
or the same slightly different
v1=(cos(a1),sin(a1))
v2=(x2-x1,y2-y1)
v2/=|v2| // makes v2 unit vector, v1 is already unit
da=acos(dot(v1,v2))
so:
da=acos((cos(a1)*(x2-x1)+sin(a1)*(y2-y1)/sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)));
[notes]
just change it to match your coordinate system (which you did not specify)
use radians or degrees according to your sin,cos,atan dependencies ...
The difference between the vectors is also a vector.
Then calculate the tangens (y part / x part) and invert it to an angle.
Of course use the sign of y if x = 0.
if the coord to face is (x2 ,y2)
deltaY = y2 - y1
deltaX = x2 - x1
You have the angle in degrees between the two points using this formula...
angleInDegrees = arctan(deltaY / deltaX) * 180 / PI
subtract the original angle of your vector and you will get the correct offset!

Generating random point in a cylinder

What is best way or an algorithm for generating a random 3d point [x,y,z] inside the volume of the circular cylinder if radius r and height h of the cylinder are given?
How about -- in Python pseudocode, letting R be the radius and H be the height:
s = random.uniform(0, 1)
theta = random.uniform(0, 2*pi)
z = random.uniform(0, H)
r = sqrt(s)*R
x = r * cos(theta)
y = r * sin(theta)
z = z # .. for symmetry :-)
The problem with simply taking x = r * cos(angle) and y = r * sin(angle) is that then when r is small, i.e. at the centre of the circle, a tiny change in r doesn't change the x and y positions very much. IOW, it leads to a nonuniform distribution in Cartesian coordinates, and the points get concentrated toward the centre of the circle. Taking the square root corrects this, at least if I've done my arithmetic correctly.
[Ah, it looks like the sqrt was right.]
(Note that I assumed without thinking about it that the cylinder is aligned with the z-axis and the cylinder centre is located at (0,0,H/2). It'd be less arbitrary to set (0,0,0) at the cylinder centre, in which case z should be chosen to be between -H/2 and H/2, not 0,H.)
Generate a random point inside the rectangular solid circumscribing the cylinder; if it's inside the cylinder (probability pi/4), keep it, otherwise discard it and try again.
Generate a random angle (optionally less than 2π), a random r less than the radius, and a random z less than the height.
x = r * cos(angle)
y = r * sin(angle)
The z axis is easy: -0.5 * h <= z <= 0.5 * h
The x and y are equal to a circle will be:
x^2 + y^2 <= r^2
Buth math is long ago for me :-)

finding a dot on a circle by degree?

Let's say we have a 100x100 coordinate system, like the one below. 0,0 is its left-top corner, 50,50 is its center point, 100,100 is its bottom right corner, etc.
Now we need to draw a line from the center outwards. We know the angle of the line, but need to calculate the coordinates of its end point. What do you think would be the best way to do it?
For example, if the angle of the line is 45 degrees, its end point coordinates would be roughly 75,15.
You need to use the trigonometric functions sin and cos.
Something like this:
theta = 45
// theta = pi * theta / 180 // convert to radians.
radius = 50
centerX = 50
centerY = 50
p.x = centerX + radius * cos(theta)
p.y = centerY - radius * sin(theta)
Keep in mind that most implementations assume that you're working with radians and have positive y pointing upwards.
Use the unit circle to calculate X and Y, but because your radius is 50, multiply by 50
http://en.wikipedia.org/wiki/Unit_circle
Add the offset (50,50) and bob's your uncle
X = 50 + (cos(45) * 50) ~ 85,36
Y = 50 - (sin(45) * 50) ~ 14,65
The above happens to be 45 degrees.
EDIT: just saw the Y axis is inverted
First you would want to calculate the X and Y coordinates as if the circle were the unit circle (radius 1). The X coordinate of a given angle is given by cos(angle), and the Y coordinate is given by sin(angle). Most implementations of sin and cos take their inputs in radians, so a conversion is necessary (1 degree = 0.0174532925 radians). Now, since your coordinate system is not in fact the unit circle, you need to multiply the resultant values by the radius of your circle. In this given instance, you would multiply by 50, since your circle extends 50 units in each direction. Finally, using a unit circle coorindate system assumes your circle is centered at the origin (0,0). To account for this, add (or subtract) the offset of your center from your calculated X and Y coordinates. In your scenario, the offset from (0,0) is 50 in the positive X direction, and 50 in the negative Y direction.
For example:
cos(45) = x ~= .707
sin(45) = y ~= .707
.707*50 = 35.35
35.35+50 = 85.35
abs(35.35-50) = 14.65
Thus the coordinates of the ending segment would be (85.35, 14.65).
Note, there is probably a built-in degrees-to-radians function in your language of choice, I provided the unit conversion for reference.
edit: oops, used degrees at first

Knowing two points of a rectangle, how can I figure out the other two?

Hey there guys, I'm learning processing.js, and I've come across a mathematical problem, which I can't seem to solve with my limited geometry and trigonometry knowledge or by help of Wikipedia.
I need to draw a rectangle. To draw this rectangle, I need to know the coordinate points of each corner. All I know is x and y for the midpoints of the top and bottom of the box, and the length of all four sides.
There is no guarantee on the orientation of the box.
Any help? This seems like it should be easy, but it is really stumping me.
If this quadrilateral is a rectangle (all four angles are 90 degrees), then it can be solved. (if it could be any quadrilateral, then it is not solvable)
if the points are (x1,y1), and (x2, y2), and if the two points are not perfectly vertical (x1 = x2) or horizontal (y1 = y2), then the slope of one edge of the rectangle is
m1 = (y2-y1) / (x2-x1)
and the slope of the other edge is:
m2 = - 1 / m1
If you know the lengths of the sides, and the midpoints of two opposite sides, then the corrner points are easily determined by adding dx, dy to the midpoints: (if L is length of the sides that the midpoints are on)
dx = Sqrt( L^2 / (1 + m2^2) ) / 2
and
dy = m2 * dx
NOTE: if the points are vertically or horizontally aligned, this technique will not work, although the obvious solution for those degenerative cases is much simpler.
If you know your quadrilateral is a rectangle, then you can use some simple vector maths to find the coordinates of the corners. The knowns are:
(x1,y1) - the coordinate of the midpoint on the top line
(x2,y2) - the coordinate of the midpoint on the bottom line
l1 - the length of the top and bottom lines
l2 - the length of the other two lines
First, we find the vector between the two known points. This vector is parallel to the side lines:
(vx, vy) = (x2 - x1, y2 - y1)
We need to normalize this vector (i.e. make it length 1) so we can use it later as a basis to find our coordinates.
vlen = sqrt(vx*vx + vy*vy)
(v1x, v1y) = (vx / vlen, vy / vlen)
Next, we rotate this vector anticlockwise by 90 degrees. The rotated vector will be parallel to the top and bottom lines. 90 degree rotation turns out to just be swapping the coordinates and negating one of them. You can see this just by trying it out on paper. Or take at look at the equations for 2D rotations and substitute in 90 degrees.
(u1x, u1y) = (-v1y, v1x)
Now we have enough information to find the 'top-left' corner. We simply start at our point (x1, y1) and move back along that side by half the side length:
(p1x, p1y) = (x1 - u1x * l1 / 2, y1 - u1y * l1 / 2)
From here we can find the remaining points just by adding the appropriate multiples of our basis vectors. When implementing this you can obviously speed it up by only calculating each unique multiplication a single time:
(p2x, p2y) = (p1x + u1x * l1, p1y + u1y * l1)
(p3x, p3y) = (p1x + v1x * l2, p1y + v1y * l2)
(p4x, p4y) = (p3x + u1x * l1, p3y + u1y * l1)
function getFirstPoint(x1,y1,x2,y2,l1,l2)
distanceV = {x2 - x1, y2 - y1}
vlen = math.sqrt(distanceV[1]^2 + distanceV[2]^2)
normalized = {distanceV[1] / vlen, distanceV[2] / vlen}
rotated = {-normalized[2], normalized[1]}
p1 = {x1 - rotated[1] * l1 / 2, y1 - rotated[2] * l1 / 2}
p2 = {p1[1] + rotated[1] * l1, p1[2] + rotated[2] * l1}
p3 = {p1[1] + normalized[1] * l2, p1[2] + normalized[2] * l2}
p4 = {p3[1] + rotated[1] * l1, p3[2] + rotated[2] * l1}
points = { p1 , p2 , p3 , p4}
return p1
end
It's definitely a rectangle? Then you know the orientation of the short sides (they're parallel to the line between your points), and hence the orientation of the long sides.
You know the orientation and length of the long sides, and you know their midpoints, so it's straightforward to find the corners from there.
Implementation is left as an exercise to the reader.
This means that there will be two lines parallel to the line between the two points you have. Get the corners by translating the line you have 1/2 the length of the top side in each direction perpendicular to the line you have.
If you know the midpoint for the top, and the length of the top, then you know that the y will stay the same for both top corners, and the x will be the midpoint plus/minus the width of the rectangle. This will also be true for the bottom.
Once you have the four corners, there is no need to worry about the side lengths, as their points are the same as those used for the top and bottom.
midpoint
x,10 10,10 x,10
*--------------------------------------------*
width = 30
mx = midpoint x.
top left corner = (w/2) - mx or 15 - 10
top left corner coords = -5,10
mx = midpoint x.
top right corner = (w/2) + mx or 15 + 10
top left corner coords = 25,10
There's a difference between a "quadrilateral" and a "rectangle".
If you have the midpoint of the top and bottom, and the sides lengths, the rest is simple.
Given:
(x1, y1) -- (top_middle_x, top_middle_y) -- (x2, y1)
(x1, y2) -- (btm_middle_x, btm_middle_y) -- (x2, y2)
and top/bottom length along with right/left length.
x1 = top_middle_x - top/bottom_length / 2;
x2 = x1 + top/bottom_length;
y1 = top_middle_y
y2 = bottom_middle_y
Obviously, that's the simplest case and assuming that the line of (tmx, tmy) (bmx, bmy) is solely along the Y axis.
We'll call that line the "mid line".
The next trick is to take the mid line, and calculate it's rotational offset off the Y axis.
Now, my trig is super rusty.
dx = tmx - bmx, dy = tmy - bmy.
So, the tangent of the angle is dy / dx. The arctangent(dy / dx) is the angle of the line.
From that you can get your orientation.
(mind, there's some games with quadrants, and signs, and stuff to get this right -- but this is the gist of it.)
Once you have the orientation, you can "rotate" the line back to the Y axis. Look up 2D graphics for the math, it's straight forward.
That gets you your normal orientation. Then calculate the rectangles points, in this new normal form, and finally, rotate them back.
Viola. Rectangle.
Other things you can do is "rotate" a line that's half the length of the "top" line to where it's 90 deg of the mid line. So, say you have a mid line that's 45 degrees. You would start this line at tmx, tmy, and rotate this line 135 degrees (90 + 45). That point would be your "top left" corner. Rotate it -45 (45 - 90) to get the "top right" point. Then do something similar with the lower points.
Calculate the angle of the line joining the two midpoints using an arc-tangent function applied to the vector you get between them.
Subtract 90 degrees from that angle to get the direction of the top edge
Starting from the top-center point, move relative (1/2 top width x sin(angle), 1/2 top width x cos(angle)) - that gets the top right corner point.
Continue around the rectangle using the sin and cos of the angles and widths as appropriate
As a test: Check you made it back to the starting point
/* rcx = center x rectangle, rcy = center y rectangle, rw = width rectangle, rh = height rectangle, rr = rotation in radian from the rectangle (around it's center point) */
function toRectObjectFromCenter(rcx, rcy, rw, rh, rr){
var a = {
x: rcx+(Math.sin((rr-degToRad(90))+Math.asin(rh/(Math.sqrt(rh*rh+rw*rw)))) * (Math.sqrt(rh*rh+rw*rw)/2)),
y: rcy-(Math.cos((rr-degToRad(90))+Math.asin(rh/(Math.sqrt(rh*rh+rw*rw)))) * (Math.sqrt(rh*rh+rw*rw)/2))
};
var b = {
x: a.x+Math.cos(rr)*rw,
y: a.y+Math.sin(rr)*rw
};
var c = {
x: b.x+Math.cos(degToRad(radToDeg(rr)+90))*rh,
y: b.y+Math.sin(degToRad(radToDeg(rr)+90))*rh
};
var d = {
x: a.x+Math.cos(degToRad(radToDeg(rr)+90))*rh,
y: a.y+Math.sin(degToRad(radToDeg(rr)+90))*rh
};
return {a:a,b:b,c:c,d:d};
}

Resources