Coordinates of perpendicular points - c#

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.

Related

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

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.

How to get the cursor position relative to ANY form of UIElement

I'm trying to find the cursor position related to the red line in the image below.
I tried these topics:
Using atan2 to find angle between two vectors
And How to calculate the angle between a line and the horizontal axis? but using
Math.Atan2
But there is a problem, P1 and P2 have not same values if I use these methods.
Is there any method to get any position of any points on a UIElement (e.g. Ellipse) using the red vector such as every point with the same angle (here P1 and P2) has the same value ?
Yes, and atan2 is perfectly the needed method. We can use cross- and dot-product to achieve result:
bx = redline_end.x - center.x
by = redline_end.y - center.y
// here bx=0 and by=75
px = p1.x - center.x
py = p1.y - center.y
angle = atan2(px * by - py * bx, px * bx + py * by) //and similar for P2

Find point C that forms perpendicular line BC to line AB

I have Line segment AB defined by two 2D points A,B.
What I am trying to do is find a point C, with distance d away from B.
The two constraints are that BC has to be perpendicular to AB and BC is always 90 degrees anti-clockwise relative to AB.
So far I have the following
double d = .01;
Coordinate C = new Coordinate(A.Lat - B.Lat, A.Long - B.Long);
C.Long = C.Long * A.Long * Math.Cos(90);
C.Lat = C.Lat * A.Lat * Math.Cos(90);
C.Long = A.Long + C.Long * d;
C.Lat = A.Lat + C.Lat * d;
Essentially what I am asking is, where am I going wrong with this? Is it the c# code? Is it the logic? What are the steps to solve for C using those two constraints.
Normalize AB vector and rotate it by 90 degrees:
ABY = B.Y - A.Y
ABX = B.X - A.X
Len = Sqrt(ABY*ABY + ABX*ABX)
C.X = B.X - d * ABY / Len
C.Y = B.Y + d * ABX / Len
Note that for geographic coordinates (Lat/Long) and large distances result is not exact.
Link for further reference (sections Bearing then Destination point given distance and bearing from start point)
MBo has the correct answer for your task (as you got 90 degrees turn) I just wanted to show you how to repair your own code (I deduced you wanted to do this) which is usable to any angular turn (but slower as it require goniometric):
d = .01;
a = atan2(B.y - A.y,B.x - A.x) (+/-) 90.0; // sign depends on your coordinate system
C.x = B.x + d*cos(a)
C.y = B.y + d*sin(a)
So you should obtain directional angle a of your AB and shift it by 90 deg. Then you just add d rotated by the a to the C which can be done by parametric circle equation.
Beware all the angles should be in units your goniometric functions accepts (so either degrees or radians) as I do not code in C# I have not a clue but in languages I code in it is usually in radians. In which case line:
a = atan2(B.y - A.y,B.x - A.x) (+/-) 90.0; // sign depends on your
Would change to:
a = atan2(B.y - A.y,B.x - A.x) (+/-) 90.0*Pi/180.0; // sign depends on your
Where Pi=3.1415926535897932384626433832795.

Trigonometry issue - Create a PointF using an angle

I have been scratching my head on this for quite a while now. Guess I should have paid more attention in the trigonometry math classes when I was younger but here we go:
I have an angle and a point. I then want to put a second point in the direction of the angle, 200 units away from the first point. I use Atan2 to get the angle, then cos and sin to get the third point. But... I think something goes wrong when calculating the Sin for p3.Y.
EDIT: To clarify, I removed p2 and used the angle directly:
PointF p1 = new PointF(20, 20);
double angle = 1.3034851559678624f;
//Create a new PointF in the same direction, 200 pixels away from p1
//{ X = 199,9482, Y = 4,549629 }
PointF p3 = new PointF
{
X = (float)(Math.Cos(Math.PI * angle / 180.0) * 200),
Y = (float)(Math.Sin(Math.PI * angle / 180.0) * 200)
};
//This is where I would expect 1.3034851559678624 as the first angle
//but I get -4.9073849244837184
double angle2 = Math.Atan2(p3.Y - p1.Y, p3.X - p1.X) * 180 / Math.PI;
Here is a visual representation of the values above. The green line is the first angle.
The problem with your current code is that you don't calculate p3 relative to p1. You need to add p1.X and p1.Y to the coordinates of p3:
PointF p3 = new PointF
{
X = p1.X + (float)(Math.Cos(Math.PI * angle / 180.0) * 200),
Y = p1.Y + (float)(Math.Sin(Math.PI * angle / 180.0) * 200)
};
Well if p3 is supposed to be on the line p1-p2 you don't need to use any trigonometry at all. You can find the coordinates of p3 directly from those of p1 and p2. Suppose the distance between p1 and p2 is 1000 units. Then the location of p3, 200 units from p1 is:
{p1.x+(p2.x-p1.x)*(200/1000),p1.y+(p2.y-p1.y)*(200/1000)}
If the distance between the two points is not 1000 (which of course it won't be), replace that value by the Euclidean Distance between them.
If you still want the slope of the line you calculate the ratio
(p2.y-p1.y)/(p2.x-p1.x)
which gives you the slope as a single number, where 1 represents the line which passes through the origin at a +45deg angle. A little fiddling around will turn the slope into the angle, be careful when the line is vertical, ie where the denominator of the ratio is 0.
You did not have to use atan to get the angle and then calc sin/cos.
You can get sin and cos direct from coordinates of points.
cos = (p2.x - p1.x)/length
sin = (p2.y - p1.y)/length
http://en.wikipedia.org/wiki/Line_%28geometry%29

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.

Categories