Convert Cartesian point to point on rotated plane (pic) - math

I have a cartesian point that I am being given (blue line), and I need to convert it to a point relative to a rotated plane (green box). The plane is rotated at 28.227º as shown below.
Sadly, my lack of math education has me completely baffled as to how to solve this. I need to be able to take any x,y point and convert it for the rotated plane.
Any help at all on this would be greatly appreciated as I am at a total loss.
Best I can figure out, I will need several different calculations depending on where the input point is.
(source: adam-meyer.com)

I love friends who know math. Thanks KJ!
Here is the answer.
function convertPoint(x,y){
var degree = -28.227;
var offset = 0; //change if your corner is not 0,0
x2 = x *Math.cos(radians(degree)) + (y - offset) *Math.sin(radians(degree));
y2 = x *Math.sin(radians(degree)) - (y - offset) *Math.cos(radians(degree));
return {x: x2, y: y2}
}
function radians(degrees){
return degrees * (Math.PI / 180);
}

Related

Perspective projection - help me duplicate blender's default scene

I'm currently attempting to teach myself perspective projection, my reference is the wikipedia page on the subject here: http://en.wikipedia.org/wiki/3D_projection#cite_note-3
My understanding is that you take your object to be project it and rotate and translate it in to "camera space", such that your camera is now assumed to be origin looking directly down the z axis. (This matrix op from the wikipedia page: http://upload.wikimedia.org/math/5/1/c/51c6a530c7bdd83ed129f7c3f0ff6637.png)
You then project your new points in to 2D space using this equation: http://upload.wikimedia.org/math/6/8/c/68cb8ee3a483cc4e7ee6553ce58b18ac.png
The first step I can do flawlessly. Granted I wrote my own matrix library to do it, but I verified it was spitting out the right answer by typing the results in to blender and moving the camera to 0,0,0 and checking it renders the same as the default scene.
However, the projection part is where it all goes wrong.
From what I can see, I ought to be taking the field of view, which by default in blender is 28.842 degrees, and using it to calculate the value wikipedia calls ez, by doing
ez = 1 / tan(fov / 2);
which is approximately 3.88 in this case.
I should then for every point be doing:
x = (ez / dz) * dx;
y = (ez / dz) * dy;
to get x and y coordinates in the range of -1 to 1 which I can then scale appropriately for the screen width.
However, when I do that my projected image is mirrored in the x axis and in any case doesn't match with the cube blender renders. What am I doing wrong, and what should I be doing to get the right projected coordinates?
I'm aware that you can do this whole thing with one matrix op, but for the moment I'm just trying to understand the equations, so please just stick to the question asked.
From what you say in your question it's unclear whether you're having trouble with the Projection matrix or the Model matrix.
Like I said in my comments, you can Google glFrustum and gluLookAt to see exactly what these matrices look like. If you're familiar with matrix math (and it looks like you are), you will then understand how the coordinates are transformed into a 2D perspective.
Here is some sample OpenGL code to make the View and Projection Matrices and Model matrix for a simple 30 degree rotation about the Y axis so you can see how the components that go into these matrices are calculated.
// The Projection Matrix
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
near = -camera.viewPos.z - shapeSize * 0.5;
if (near < 0.00001)
near = 0.00001;
far = -camera.viewPos.z + shapeSize * 0.5;
if (far < 1.0)
far = 1.0;
radians = 0.0174532925 * camera.aperture / 2; // half aperture degrees to radians
wd2 = near * tan(radians);
ratio = camera.viewWidth / (float) camera.viewHeight;
if (ratio >= 1.0) {
left = -ratio * wd2;
right = ratio * wd2;
top = wd2;
bottom = -wd2;
} else {
left = -wd2;
right = wd2;
top = wd2 / ratio;
bottom = -wd2 / ratio;
}
glFrustum (left, right, bottom, top, near, far);
// The View Matrix
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
gluLookAt (camera.viewPos.x, camera.viewPos.y, camera.viewPos.z,
camera.viewPos.x + camera.viewDir.x,
camera.viewPos.y + camera.viewDir.y,
camera.viewPos.z + camera.viewDir.z,
camera.viewUp.x, camera.viewUp.y ,camera.viewUp.z);
// The Model Matrix
glRotatef (30.0, 0.0, 1.0, 0.0);
You'll see that glRotate actually does a quaternion rotation (angle of rotation plus a vector about which to do the rotation).
You could also do separate rotations about the X, Y and Z axis.
There's lot's of information on the web about how to form 4X4 matrices for rotations, translations and scales. If you do each of these separately, you'll need to multiply them to get the Model matrix. e.g:
If you have 4X4 matrices rotateX, rotateY, rotateZ, translate, scale, you might form your Model matrix by:
Model = scale * rotateX * rotateZ * rotateY * translate.
Order matters when you form the Model matrix. You'll get different results if you do the multiplication in a different order.
If your object is at the origin, I doubt you want to also put the camera at the origin.

How to find points on the circumference of a arc knowing a start point, an end point and the radius?

Please see the image below for a visual clue to my problem:
I have the coordinates for points 1 and 2. They were derived by a formula that uses the other information available (see question: How to calculate a point on a circle knowing the radius and center point).
What I need to do now (separately from the track construction) is plot the points in green between point 1 and 2.
What is the best way of doing so? My Maths skills are not the best I have to admit and I'm sure there's a really simple formula I just can't work out (from my research) which to use or how to implement.
In the notation of my answer to your linked question (i.e. x,y is the current location, fx,fy is the current 'forward vector', and lx,ly is the current 'left vector')
for (i=0; i<=10; i++)
{
sub_angle=(i/10)*deg2rad(22.5);
xi=x+285.206*(sin(sub_angle)*fx + (1-cos(sub_angle))*(-lx))
yi=y+285.206*(sin(sub_angle)*fy + (1-cos(sub_angle))*(-ly))
// now plot green point at (xi, yi)
}
would generate eleven green points equally spaced along the arc.
The equation of a circle with center (h,k) and radius r is
(x - h)² + (y - k)² = r² if that helps
check out this link for points http://www.analyzemath.com/Calculators/CircleInterCalc.html
The parametric equation for a circle is
x = cx + r * cos(a)
y = cy + r * sin(a)
Where r is the radius, cx,cy the origin, and a the angle from 0..2PI radians or 0..360 degrees.

How can I calculate the distance between two points in Cartesian space while respecting Asteroids style wrap around?

I have two points (x1, y1) and (x2,y2) which represent the location of two entities in my space. I calculate the Euclidian distance between them using Pythagoras' theorem and everything is wonderful. However, if my space becomes finite, I want to define a new shortest distance between the points that "wraps around" the seams of the map. For example, if I have point A as (10, 10) and point B as (90,10), and my map is 100 units wide, I'd like to calculate the distance between A and B as 20 (out the right edge of the map and back into the left edge), instead of 80, which is the normal Euclidian distance.
I think my issue is that I'm using a coordinate system that isn't quite right for what I'm trying to do, and that really my flat square map is more of a seamless doughnut shape. Any suggestions for how to implement a system of this nature and convert back and forth from Cartesian coordinates would be appreciated too!
Toroidal plane? Okay, I'll bite.
var raw_dx = Math.abs(x2 - x1);
var raw_dy = Math.abs(y2 - y1);
var dx = (raw_dx < (xmax / 2)) ? raw_dx : xmax - raw_dx;
var dy = (raw_dy < (ymax / 2)) ? raw_dy : ymax - raw_dy;
var l2dist = Math.sqrt((dx * dx) + (dy * dy));
There's a correspondence here between the rollover behavior of your x and y coordinates and the rollover behavior of signed integers represented using the base's complement representation in the method of complements.
If your coordinate bounds map exactly to the bounds of a binary integer type supported by your language, you can take advantage of the two's complement representation used by nearly all current machines by simply performing the subtraction directly, ignoring overflow and reinterpreting the result as a signed value of the same size as the original coordinate. In the general case, you're not going to be that lucky, so the above dance with abs, compare and subtract is required.

I've got my 2D/3D conversion working perfectly, how to do perspective

Although the context of this question is about making a 2d/3d game, the problem i have boils down to some math.
Although its a 2.5D world, lets pretend its just 2d for this question.
// xa: x-accent, the x coordinate of the projection
// mapP: a coordinate on a map which need to be projected
// _Dist_ values are constants for the projection, choosing them correctly will result in i.e. an isometric projection
xa = mapP.x * xDistX + mapP.y * xDistY;
ya = mapP.x * yDistX + mapP.y * yDistY;
xDistX and yDistX determine the angle of the x-axis, and xDistY and yDistY determine the angle of the y-axis on the projection (and also the size of the grid, but lets assume this is 1-pixel for simplicity).
x-axis-angle = atan(yDistX/xDistX)
y-axis-angle = atan(yDistY/yDistY)
a "normal" coordinate system like this
--------------- x
|
|
|
|
|
y
has values like this:
xDistX = 1;
yDistX = 0;
xDistY = 0;
YDistY = 1;
So every step in x direction will result on the projection to 1 pixel to the right end 0 pixels down. Every step in the y direction of the projection will result in 0 steps to the right and 1 pixel down.
When choosing the correct xDistX, yDistX, xDistY, yDistY, you can project any trimetric or dimetric system (which is why i chose this).
So far so good, when this is drawn everything turns out okay. If "my system" and mindset are clear, lets move on to perspective.
I wanted to add some perspective to this grid so i added some extra's like this:
camera = new MapPoint(60, 60);
dx = mapP.x - camera.x; // delta x
dy = mapP.y - camera.y; // delta y
dist = Math.sqrt(dx * dx + dy * dy); // dist is the distance to the camera, Pythagoras etc.. all objects must be in front of the camera
fac = 1 - dist / 100; // this formula determines the amount of perspective
xa = fac * (mapP.x * xDistX + mapP.y * xDistY) ;
ya = fac * (mapP.x * yDistX + mapP.y * yDistY );
Now the real hard part... what if you got a (xa,ya) point on the projection and want to calculate the original point (x,y).
For the first case (without perspective) i did find the inverse function, but how can this be done for the formula with the perspective. May math skills are not quite up to the challenge to solve this.
( I vaguely remember from a long time ago mathematica could create inverse function for some special cases... could it solve this problem? Could someone maybe try?)
The function you've defined doesn't have an inverse. Just as an example, as user207422 already pointed out anything that's 100 units away from the camera will get mapped to (xa,ya)=(0,0), so the inverse isn't uniquely defined.
More importantly, that's not how you calculate perspective. Generally the perspective scaling factor is defined to be viewdist/zdist where zdist is the perpendicular distance from the camera to the object and viewdist is a constant which is the distance from the camera to the hypothetical screen onto which everything is being projected. (See the diagram here, but feel free to ignore everything else on that page.) The scaling factor you're using in your example doesn't have the same behaviour.
Here's a stab at trying to convert your code into a correct perspective calculation (note I'm not simplifying to 2D; perspective is about projecting three dimensions to two, trying to simplify the problem to 2D is kind of pointless):
camera = new MapPoint(60, 60, 10);
camera_z = camera.x*zDistX + camera.y*zDistY + camera.z*zDistz;
// viewdist is the distance from the viewer's eye to the screen in
// "world units". You'll have to fiddle with this, probably.
viewdist = 10.0;
xa = mapP.x*xDistX + mapP.y*xDistY + mapP.z*xDistZ;
ya = mapP.x*yDistX + mapP.y*yDistY + mapP.z*yDistZ;
za = mapP.x*zDistX + mapP.y*zDistY + mapP.z*zDistZ;
zdist = camera_z - za;
scaling_factor = viewdist / zdist;
xa *= scaling_factor;
ya *= scaling_factor;
You're only going to return xa and ya from this function; za is just for the perspective calculation. I'm assuming the the "za-direction" points out of the screen, so if the pre-projection x-axis points towards the viewer then zDistX should be positive and vice-versa, and similarly for zDistY. For a trimetric projection you would probably have xDistZ==0, yDistZ<0, and zDistZ==0. This would make the pre-projection z-axis point straight up post-projection.
Now the bad news: this function doesn't have an inverse either. Any point (xa,ya) is the image of an infinite number of points (x,y,z). But! If you assume that z=0, then you can solve for x and y, which is possibly good enough.
To do that you'll have to do some linear algebra. Compute camera_x and camera_y similar to camera_z. That's the post-transformation coordinates of the camera. The point on the screen has post-tranformation coordinates (xa,ya,camera_z-viewdist). Draw a line through those two points, and calculate where in intersects the plane spanned by the vectors (xDistX, yDistX, zDistX) and (xDistY, yDistY, zDistY). In other words, you need to solve the equations:
x*xDistX + y*xDistY == s*camera_x + (1-s)*xa
x*yDistX + y*yDistY == s*camera_y + (1-s)*ya
x*zDistX + y*zDistY == s*camera_z + (1-s)*(camera_z - viewdist)
It's not pretty, but it will work.
I think that with your post i can solve the problem. Still, to clarify some questions:
Solving the problem in 2d is useless indeed, but this was only done to make the problem easier to grasp (for me and for the readers here). My program actually give's a perfect 3d projection (i checked it with 3d images rendered with blender). I did left something out about the inverse function though. The inverse function is only for coordinates between 0..camera.x * 0.5 and 0.. camera.y*0.5. So in my example between 0 and 30. But even then i have doubt's about my function.
In my projection the z-axis is always straight up, so to calculate the height of an object i only used the vieuwingangle. But since you cant actually fly or jumpt into the sky everything has only a 2d point. This also means that when you try to solve the x and y, the z really is 0.
I know not every funcion has an inverse, and some functions do, but only for a particular domain. My basic thought in this all was... if i can draw a grid using a function... every point on that grid maps to exactly one map-point. I can read the x and y coordinate so if i just had the correct function i would be able to calculate the inverse.
But there is no better replacement then some good solid math, and im very glad you took the time to give a very helpfull responce :).

Given an angle and dimensions, find a coordinate along the perimeter of a rectangle

I'm writing a script where icons rotate around a given pivot (or origin). I've been able to make this work for rotating the icons around an ellipse but I also want to have them move around the perimeter of a rectangle of a certain width, height and origin.
I'm doing it this way because my current code stores all the coords in an array with each angle integer as the key, and reusing this code would be much easier to work with.
If someone could give me an example of a 100x150 rectangle, that would be great.
EDIT: to clarify, by rotating around I mean moving around the perimeter (or orbiting) of a shape.
You know the size of the rectangle and you need to split up the whole angle interval into four different, so you know if a ray from the center of the rectangle intersects right, top, left or bottom of the rectangle.
If the angle is: -atan(d/w) < alfa < atan(d/w) the ray intersects the right side of the rectangle. Then since you know that the x-displacement from the center of the rectangle to the right side is d/2, the displacement dy divided by d/2 is tan(alfa), so
dy = d/2 * tan(alfa)
You would handle this similarily with the other three angle intervals.
Ok, here goes. You have a rect with width w and depth d. In the middle you have the center point, cp. I assume you want to calculate P, for different values of the angle alfa.
I divided the rectangle in four different areas, or angle intervals (1 to 4). The interval I mentioned above is the first one to the right. I hope this makes sense to you.
First you need to calculate the angle intervals, these are determined completely by w and d. Depending on what value alfa has, calculate P accordingly, i.e. if the "ray" from CP to P intersects the upper, lower, right or left sides of the rectangle.
Cheers
This was made for and verified to work on the Pebble smartwatch, but modified to be pseudocode:
struct GPoint {
int x;
int y;
}
// Return point on rectangle edge. Rectangle is centered on (0,0) and has a width of w and height of h
GPoint getPointOnRect(int angle, int w, int h) {
var sine = sin(angle), cosine = cos(angle); // Calculate once and store, to make quicker and cleaner
var dy = sin>0 ? h/2 : h/-2; // Distance to top or bottom edge (from center)
var dx = cos>0 ? w/2 : w/-2; // Distance to left or right edge (from center)
if(abs(dx*sine) < abs(dy*cosine)) { // if (distance to vertical line) < (distance to horizontal line)
dy = (dx * sine) / cosine; // calculate distance to vertical line
} else { // else: (distance to top or bottom edge) < (distance to left or right edge)
dx = (dy * cosine) / sine; // move to top or bottom line
}
return GPoint(dx, dy); // Return point on rectangle edge
}
Use:
rectangle_width = 100;
rectangle_height = 150;
rectangle_center_x = 300;
rectangle_center_y = 300;
draw_rect(rectangle_center_x - (rectangle_width/2), rectangle_center_y - (rectangle_center_h/2), rectangle_width, rectangle_height);
GPoint point = getPointOnRect(angle, rectangle_width, rectangle_height);
point.x += rectangle_center_x;
point.y += rectangle_center_y;
draw_line(rectangle_center_x, rectangle_center_y, point.x, point.y);
One simple way to do this using an angle as a parameter is to simply clip the X and Y values using the bounds of the rectangle. In other words, calculate position as though the icon will rotate around a circular or elliptical path, then apply this:
(Assuming axis-aligned rectangle centered at (0,0), with X-axis length of XAxis and Y-axis length of YAxis):
if (X > XAxis/2)
X = XAxis/2;
if (X < 0 - XAxis/2)
X = 0 - XAxis/2;
if (Y > YAxis/2)
Y = YAxis/2;
if (Y < 0 - YAxis/2)
Y = 0 - YAxis/2;
The problem with this approach is that the angle will not be entirely accurate and the speed along the perimeter of the rectangle will not be constant. Modelling an ellipse that osculates the rectangle at its corners can minimize the effect, but if you are looking for a smooth, constant-speed "orbit," this method will not be adequate.
If think you mean rotate like the earth rotates around the sun (not the self-rotation... so your question is about how to slide along the edges of a rectangle?)
If so, you can give this a try:
# pseudo coode
for i = 0 to 499
if i < 100: x++
else if i < 250: y--
else if i < 350: x--
else y++
drawTheIcon(x, y)
Update: (please see comment below)
to use an angle, one line will be
y / x = tan(th) # th is the angle
the other lines are simple since they are just horizontal or vertical. so for example, it is x = 50 and you can put that into the line above to get the y. do that for the intersection of the horizontal line and vertical line (for example, angle is 60 degree and it shoot "NorthEast"... now you have two points. Then the point that is closest to the origin is the one that hits the rectangle first).
Use a 2D transformation matrix. Many languages (e.g. Java) support this natively (look up AffineTransformation); otherwise, write out a routine to do rotation yourself, once, debug it well, and use it forever. I must have five of them written in different languages.
Once you can do the rotation simply, find the location on the rectangle by doing line-line intersection. Find the center of the orbited icon by intersecting two lines:
A ray from your center of rotation at the angle you desire
One of the four sides, bounded by what angle you want (the four quadrants).
Draw yourself a sketch on a piece of paper with a rectangle and a centre of rotation. First translate the rectangle to centre at the origin of your coordinate system (remember the translation parameters, you'll need to reverse the translation later). Rotate the rectangle so that its sides are parallel to the coordinate axes (same reason).
Now you have a triangle with known angle at the origin, the opposite side is of known length (half of the length of one side of the rectangle), and you can now:
-- solve the triangle
-- undo the rotation
-- undo the translation

Resources