Calculate a 3D trajectory by start point, end point and height - c#

I've already figured out how to make a 3D trajectory using a start point and an angle.
However, I am trying to make a trajectory from a start point, an end point, and a height.
I tried taking the approach of a parabola on a 2D plane in a 3D space. I calculated the Prabola's A, B, and C values as well as the plane it's on given 3 points on the Parabola.
However, I've had a few complications with this sort of calculation, I assume it has to do with the inability to properly calculate a Z-axis without a plane but I cannot tell.
Other than a 2D parabola on a plane google did not provide another possible answer and a 3D trajectory yields a formula using a start point, an angle, and a power multiplier.
Is there any way to calculate a 3D trajectory given the start point, end point, and height?
Appreciating your help
Edit:
My code to calculate a parabola using 3 points (in case someone would like to know how I've done that and perhaps fix what I've done wrong)
public Parabola(Vector3 pa, Vector3 pb, Vector3 pc)
{
this.pa = pa;
this.pc = pc;
float a1 = -pa.x * pa.x + pb.x * pb.x, b1 = -pa.x + pb.x, c1 = -pa.y + pb.y;
float a2 = -pb.x * pb.x + pc.x * pc.x, b2 = -pb.x + pc.x, c2 = -pb.y + pc.y;
float bm = -(b2 / b1), a3 = bm * a1 + a2, c3 = bm * c1 + c2;
float a = c3 / a3, b = (c1 - a1 * a) / b1, c = pa.y - a * pa.x * pa.x - b * pa.x;
this.a = a; this.b = b; this.c = c;
plane = Vector3.Cross(pb - pa, pc - pa);
}
public Vector3 GetPoint(float x)
{
float angle = Mathf.Atan2(pc.z - pa.z, pc.x - pa.x) * Mathf.Rad2Deg;
float xs = Mathf.Cos(angle * Mathf.Deg2Rad) * x, zs = Mathf.Sin(angle * Mathf.Deg2Rad) * x;
return new Vector3(xs, a * x * x + b * x + c, zs);
}
public Vector3 ProjectOn(float x) => Vector3.ProjectOnPlane(GetPoint(x), plane);
The result looks ok when it's only on 2 Axis, but not 3.
here are 2 images for demonstration:

Looking at the second image, the parabola seems to be correct aside from being scaled incorrectly. Let's take a look at your code.
public Vector3 GetPoint(float x)
{
float angle = Mathf.Atan2(pc.z - pa.z, pc.x - pa.x) * Mathf.Rad2Deg;
float xs = Mathf.Cos(angle * Mathf.Deg2Rad) * x, zs = Mathf.Sin(angle * Mathf.Deg2Rad) * x;
return new Vector3(xs, a * x * x + b * x + c, zs);
}
I'm making a lot of assumptions here, but it seems like x is meant as a value that goes from 0.0 to 1.0, representing the start and end of the parabola, respectively. If so, you are determining the X and Z coordinates of this point based exclusively on the sine/cosine of the angle and x. This means that the values xs and zs should only ever be able to be between -1 and 1, limiting yourself to the confines of the unit circle.
The values xs and zs look like they need to be scaled by a factor s calculated by measuring the 2D distance of the start and end points when projected onto the XZ plane. This should stretch the parabola just enough to reach the end point.

I found an answer, but it's kinda a workaround.
Before messing around with Parabolas in 3D, I messed around with linear equations in 3D.
Unlike parabolas, lines have a defined equation even in 3D(Pn = P0 + t x V)(Pn vector containing XYZ, P0 initial point containing XYZ, t float, V Vector3)
In addition, there's only ONE line that goes through 2 points, even in 3D.
I used that to make a trajectory that's made out of 2 points and a height.
I make a new point in the center of those two points and add the height value to the highest Y value of the points, thus creating an Apex.
then I use the same calculations as before to calculate the A, B, and C values that
a parabola with those 3 points would have had.
I made a method that takes in an X value and returns a Vector3 containing the point this X is on a linear equation, but instead, changing the vector's Y value based on the parabola's equation.
Practically creating an elevated line, I made something that looks and behaves perfectly like a 3D parabola.
If you're in C#, here is the code(images):
FIX!!
in the Linear's GetX(float x) method.
it should be:
public Vector3 GetX(float x) => => r0 + (x - r0.x)/v.x * v;
I made a slight mistake in the calculations which I noticed immediately and changed.

Related

Coordinates of perpendicular points

I would like to draw a rectangle made of Mesh.
I enter the A starting point and B ending point.
The width of the rectangle is known in advance and is equal to H.
How to correctly determine the coordinates of corner points? (Everything happens in 3D)
There are a lot of theoretical entries on the net (but mostly for 2D) and trying something like this:
var vAB = B - A;
var P1 = new Vector3(-vAB.z, vAB.y, vAB.x) / Mathf.Sqrt(Mathf.Pow(vAB.x, 2) + Mathf.Pow(vAB.y, 2) + Mathf.Pow(vAB.z, 2)) * 0.5 * H;
But I can't find the correct solution
The simple way should be to use the cross product. The cross product of two vectors is perpendicular to both input vectors. You will need to define the normal of your rectangle, In this I use vector3.up. A-B cannot be parallel to the normal vector, or you will get an invalid result.
var l = B - A;
var s = Vector3.Normalize(Vector3.Cross(l, Vector3.up));
var p1 = A + s * h/2;
var p2 = A - s * h/2;
var p3 = B - s * h/2;
var p4 = B + s * h/2;
Here's a quick explanation of the trig involved. There are other tools which will reduce the boilerplate a bit, but this should give you an understanding of the underlying maths.
I've tweaked your problem statement slightly: I'm just showing the XY plane (there's no Z involved), and I've rotated it so that the line AB forms a positive angle with the horizontal (as it makes explaining the maths a bit easier). A is at (x1, y1), B is at (x2, y2).
The first step is to find the angle θ that the line AB makes with the horizontal. Draw a right-angled triangle, where AB is the hypotenuse, and the other two sides are parallel to the X and Y axes:
You can see that the horizontal side has length (x2 - x1), and the vertical side has length (y2 - y1). The angle between the base and the hypotenuse (the line AB) is given by trig, where tan θ = (y2 - y1) / (x2 - x1), so θ = arctan((y2 - y1) / (x2 - x1)).
(Make sure you use the Math.Atan2 method to calculate this angle, as it makes sure the sign of θ is correct).
Next we'll look at the corner P1, which is connected to A. As before, draw a triangle with the two shorter sides being parallel at the X and Y axes:
This again forms a right-angled triangle with hypotenuse H/2. To find the base of the triangle, which is the X-distance between P1 and A, again use trig: H/2 * sin θ. Similarly, the Y-distance between P1 and A is H/2 cos θ. Therefore P1 = (x1 + H/2 sin θ, y2 - H/2 cos θ).
Repeat the same trick for the other 3 corners, and you'll find the same result, but with differing signs.
My approach requires you to use a Transform.
public Transform ht; // assign in inspector or create new
void calculatePoints(Vector3 A, Vector3 B, float H)
{
Vector3 direction = B - A;
ht.position = A;
ht.rotation = Quaternion.LookRotation(direction, Vector3.Up);
Vector3 P1 = new Vector3(A + ht.right * H/2f);
Vector3 P2 = new Vector3(A + ht.left * H/2f);
Vector3 P3 = new Vector3(B + ht.right * H/2f);
Vector3 P4 = new Vector3(B + ht.left * H/2f);
}
I think it's intuitive to think with "left" and "right" which is why I used the Transform. If you wanted to define a point along the way, you'd be using ht.forward * value added to A, where value would be something between 0 and direction.magnitude.

How to let the program know if a point is at the right or the left of a line?

I wanted to let my program know whether a point X is at the right or the left of a line that crosses A and B
I found that solution in this forum (How to tell whether a point is to the right or left side of a line) and it was really helpful, but once I changed the angle of the line it just stopped working and started to give me the result of a line that has 0° as heading
I work with Lines that rarely have 0°
I tried also to rotate the point I have using an equation but still giving me the same result
I'm trying to achieve the results I want in Unity Engine so I can visualize what I'm doing before actually jumping to the original program
float Ax = Stop.transform.position.x;
float Ay = Stop.transform.position.y;
float Bx = Ref.transform.position.x;
float By = Ref.transform.position.y;
float X = Wheel.transform.position.x;
float Y = Wheel.transform.position.y;
float x = X * Cos(Gate.transform.rotation.eulerAngles.y) - Y * Sin(Gate.transform.rotation.eulerAngles.y);
float y = Y * Cos(Gate.transform.rotation.eulerAngles.y) + X * Sin(Gate.transform.rotation.eulerAngles.y);
float position = System.Math.Sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax));
if (position > 0)
{
lg.Log("Left");
}
else
{
lg.Log("Right");
}
I expect the program to return right or left whatever the heading of the line is and whatever is the distance between A & B & X but it just gives me results of straight 0° heading line which is weird as the B point is not aligned with the Y axes :|
You are calculating orientation of Wheel position (large X/Y) relative to Stop-Ref line. Coordinates of rotated point (small x/y) are ignored.
You can first find out the equation of the line with:
float m = (By - Ay) / (Bx - Ax);
float n = (Ay * Bx + By * Ax) / (Bx - Ax);
The equation of the line is:
y = mx + n
This is simply implementing the line equation from Wikipedia.
Now you can easily find out if a point is above or below the line. Just insert your x coordinate into the line, then you get the y coordinate of the line at that point. Now compare that y value with your y coordinate and you know if it's above or below. Depending on what you define as the direction of the line, you'll now also be able to tell if that's left or right of the line.
An exact vertical line won't work though since its slope would be approaching infinity.
so I found a solution
first of all, it was my mistake from the beginning
so after analyzing all of your answers and others from other questions on this website
I finally got it
First, my mistake is that I was using the Y value of a point in a left-handed 3D space plan
wich uses Y as altitude and not as Longitude
so I switched to Z and I started to see some results
this justifies the fact that the results I always get are vertical based
I think all of your answers may work but I finally used one of mine
so here is my logic
float Ax = Stop.transform.position.x;
float Ay = Stop.transform.position.z;
float Bx = Ref.transform.position.x;
float By = Ref.transform.position.y;
float X = Wheel.transform.position.x;
float Y = Wheel.transform.position.z;
// set the point coordinates in a new virtual plan that has the A point as origin
float x = X - Ax;
float y = Y - Ay;
// set a new B point and ignore the first one
// it seems not logical at all but in my next program i won't be able to set a virtual point so it is needed
Bx = 0;
By = 5;
//let the program know that Apoint now is the origin
Ax = 0;
Ay = 0;
//get the line heading using euler angles
float angle = Gate.transform.rotation.eulerAngles.y;
//small log to check the values
lg.Log(angle.ToString());
//rotate the point coordinates arround the orgin (0 ; 0)
var rotatedX = Cos(angle) * (x - Ax) - Sin(angle) * (y - Ay) + Ax;
var rotatedY = Sin(angle) * (x - Ax) + Cos(angle) * (y - Ay) + Ay;
//check the X value instead of the Y value, again my misatake
if(rotatedX > 0)
{
// i created big boxed to view the left/right sign instead of logs just to not get confused :)
left.SetActive(true);
right.SetActive(false);
}
else
{
left.SetActive(false);
right.SetActive(true);
}
I'm using unity for tests cus it has a render engine that helps visualize the results before jumping to the actual program
thank you all for the help :)

What are 2D quaternions called (what should I search for more information)?

I stumbled on a working concept for a fast rotation & orientation system today, based on a two-term quaternion that represents either a rotation about the X axis (1,0,0) in the form w + ix, a rotation about the Y axis (0,1,0) in the form w + jy, or a rotation about the Z axis (0,0,1) in the form w + kz.
They're similar to complex numbers, but a) are half-angled and double-sided like all quaternions (they're simply quaternions with two of three imaginary terms zeroed out), and b) represent rotations about one of three 3D axes specifically.
My problem and question is...I can't find any representation of such a system online and have no idea what to search for. What are these complex numbers called? Who else has done something like this before? Where can I find more information on the path I'm headed down? It seems too good to be true and I want to find the other shoe before it drops on me.
Practical example I worked out (an orientation quaternion from Tait-Bryan angles):
ZQuat Y, YQuat P, XQuat R; // yaw, pitch, roll
float w = Y.W * P.W;
float x = -Y.Z * P.Y;
float y = Y.W * P.Y;
float z = Y.Z * P.W;
Quaternion O; // orientation
O.W = x * R.W + w * R.X;
O.X = y * R.W + z * R.X;
O.Y = z * R.W - y * R.X;
O.Z = w * R.W - x * R.X;
Quaternions in 2D would degenerate to just being a single component being no diferrent than an rotation angle. That's propably why you do not find anything. With quaternions you do f.e. not have the problem of gimbal lock, appearing when two rotation axes align because of rotation order. In normal 2D space you do not have more than a single rotation axis, so it has neither order (how do you sort a single element) and there are no axes to align. The lack of rotation axes in 2D is because you get a rotation axis when being perpendicular to two other axes.
This gives 3 axes for 3D:
X&Y=>Z
X&Z=>Y
Y&Z=>X
But only one for 2D:
X&Y=>Z

How to do correct polygon rotation? ( in C# though it applies to anything )

Hi I'm using this C# code to rotate polygons in my app - they do rotate but also get skewed along the way which is not what i want to happen. All the polygons are rectangles with four corners defined as 2D Vectors,
public Polygon GetRotated(float radians)
{
Vector origin = this.Center;
Polygon ret = new Polygon();
for (int i = 0; i < points.Count; i++)
{
ret.Points.Add(RotatePoint(points[i], origin, radians));
}
return ret;
}
public Vector RotatePoint(Vector point, Vector origin, float angle)
{
Vector ret = new Vector();
ret.X = (float)(origin.X + ((point.X - origin.X) * Math.Cos((float)angle)) - ((point.Y - origin.Y) * Math.Sin((float)angle)));
ret.Y = (float)(origin.Y + ((point.X - origin.X) * Math.Sin((float)angle)) - ((point.Y - origin.Y) * Math.Cos((float)angle)));
return ret;
}
Looks like your rotation transformation is incorrect. You should use:
x' = x*Cos(angle) - y*Sin(angle)
y' = x*Sin(angle) + y*Cos(angle)
For more information, check various sources on the internet. :)
I don't have any answer to why it's skewing yet, but I do have a suggestion to make the code clearer:
public Vector RotatePoint(Vector point, Vector origin, float angle)
{
Vector translated = point - origin;
Vector rotated = new Vector
{
X = translated.X * Math.Cos(angle) - translated.Y * Math.Sin(angle),
Y = translated.X * Math.Sin(angle) + translated.Y * Math.Cos(angle)
};
return rotated + origin;
}
(That's assuming Vector has appropriate +/- operators defined.)
You may still need a couple of casts to float, but you'll still end up with fewer brackets obfuscating things. Oh, and you definitely don't need to cast angle to float, given that it's already declared as float.
EDIT: A note about the rotation matrices involved - it depends on whether you take the angle to be clockwise or anticlockwise. I wouldn't be at all surprised to find out that the matrix is indeed what's going wrong (I did try to check it, but apparently messed up)... but "different" doesn't necessarily mean "wrong". Hopefully the matrix is what's wrong, admittedly :)
I think your rotation matrix is incorrect. There should be a + instead of - in the second equation:
+cos -sin
+sin +cos
Assuming that origin is 0,0. From your formula I would get:
X' = (X + ((X - 0) * Cos(angle)) - ((Y - 0) * Sin(angle)));
X' = X + (X * Cos(angle)) - (Y * Sin(angle));
Which differs from the initial formula
x' = x * cos angle - y * cos angle
So I think Jon Skeet's answer is correct and clearer.
Just a wild guess - are you sure the aspect ratio of your desktop resolution is the same as of the physical screen? That is, are the pixels square? If not, then rotating your rectangles in an arbitrary angle can make them look skewed.

XNA: adding a force to a vector

i do have 2 points on a 2d plane. one has already an vector that does determine in which direction it will move.
now i want to add a vector to this existing vector. so he accelerates in the direction of the other point.
to be a bit more clear, it is about 2 asteroids flying in space (only 2d) and gravitation should move them a bit closer to each other.
what i did build till now is this:
c = body.position - body2.position;
dist = c.Length();
acc = (body.masse * body2.masse) / (dist * dist);
xDist = body2.position.X - body.position.X;
yDist = body2.position.Y - body.position.Y;
direction = MathHelper.ToDegrees((float)(Math.Atan2((double)yDist, (double)xDist)));
body.velocity.Y = body.velocity.Y + (float)(Math.Sin(direction) * acc);
body.velocity.X = body.velocity.X + (float)(Math.Cos(direction) * acc);
in the moment the direction calculated is completly off. surely i am making just a stupid mistake, but i have no idea.
You need to pass your direction angle in in radians to Math.sin and Math.Cos (rather then in degree as you do in your smaple code).
see also:
http://msdn.microsoft.com/en-us/library/system.math.sin.aspx
The angle, a, must be in radians. Multiply by Math.PI/180 to convert degrees to radians.
My mechanics and linear algebra are a bit rusty but I think you should be able to do it without resorting to trigonometry. These formulae probably need tweaking, I'm not sure if I got u and -u mixed up.
Here it is in pseudo code
T is whatever time period you're iterating over
G is the gravitational constant
body1 starts with a velocity of v1
body2 starts with a velocity of v2
c = body.position - body2.position
c1 is a vector
use the vector c to get a vector of length 1 in the direction of the force
u = c1 / c.Length()
body1 should have an acceleration vector of a1 = G * body2mass/c.Length()^2 * (-u)
body2 should have an acceleration vector of a2 = G * body1mass/c.Length()^2 * (u)
body1 has a new velocity vector of v1 + a1/T
body2 has a new velocity vector of v1 + a2/T
rinse and repeat
Not completely sure what you try to do. Why can't you just use Vector2.Add(v1, v2)?

Categories