I want to map cartesian coordinates (x,y,z) to bispherical coordinates (sigma,tau,phi) and back accourding to wikipedia.
Here are my functions:
public static Vector3 ProjectBisphericalToCartesian(Vector3 bispherical, double a)
{
var sigma = (double)bispherical.X;
var tau = (double)bispherical.Y;
var phi = (double)bispherical.Z;
var d = Math.Cosh(tau) - Math.Cos(sigma);
var s = a / d;
var x = s * Math.Sin(sigma) * Math.Cos(phi);
var y = s * Math.Sin(sigma) * Math.Sin(phi);
var z = s * Math.Sinh(tau);
return new Vector3(
(float)x,
(float)y,
(float)z
);
}
public static Vector3 ProjectCartesianToBispherical(Vector3 cartesian, double a)
{
var x = (double)cartesian.X;
var y = (double)cartesian.Y;
var z = (double)cartesian.Z;
var R = Math.Sqrt(x * x + y * y + z * z);
var s = R * R + a * a;
var t = 2.0 * a * z;
var Q = Math.Sqrt(s * s - t * t);
var sigma = Math.Acos((R * R - a * a) / Q);
var tau = Asinh(t / Q);
var phi = Math.Atan(y / x);
return new Vector3(
(float)sigma,
(float)tau,
(float)phi
);
}
// sinh^-1 ("areasinus hyperbolicus")
private static double Asinh(double x)
{
return Math.Log(x + Math.Sqrt(x * x + 1.0));
}
I test the function by generating random numbers for (x,y,z) and a, convert them to bispherical coordinates and back to cartesian and check whether or not they are equal to the original cartesian corrdinates (up to some small tolerance).
It turns out that some cartesian coordinates flip the sign of its x and y component (z is okay) while other's do not. For example {X:-5,3434 Y:2,569566 Z:-1,195607} flips the sign of the x and y component while {X:7,586471 Y:-6,154929 Z:1,494778} works fine.
I can not find an error in my code unfortunately.
The issue appears to be caused by negative x values in the original cartesian coordinates which will cause the sign of x and y to be flipped after conversion and back. I just don't see why that happens.
Does anybody have an idea what is going on here ?
Well, I don’t actually have the answer for you but maybe by putting our heads together we can do it.
First of all it looks to me like you did correctly transcribe the Wikipedia formulas. So what kind of a problem is this? Either the Math functions don’t do exactly the same thing as the functions referenced in Wikipedia, or else there is an error in Wikipedia.
At a guess I would not think that the methods in Math are differently defined. These functions have been around a long time and are very conventional. Maybe Wikipedia is wrong.
The type of error you have is a sign error. So you should look at what happens when the signs of one or two of the inputs changes. For example if we flip the sign of x then the sign of phi changes (it moves 180 degrees) in the conversion to bispherical. Correspondingly in the conversion to Cartesian, if we flip the sign of phi then the sign of x changes due to the cos(phi).
However if we flip the signs of both x and y then the sign of phi does not change. Neither do the other bispherical calculations change since they only use x and y within squaring. On the other hand if we change the sign of phi in the conversion to Cartesian, the signs of x and y do change. So there are two points in the Cartesian system that map to the same bispherical coordinates, while the two corresponding bisperical coordinates do map back to different Cartesian coordinates. It would seem that you need to introduce a sign in the conversion of x and y to phi. (Or, conceivably a sign in one of the other bispherical coordinates to make up for the lack of sign in phi.)
Okay, I got it. Wolfram was mentioning something about half-planes so it occurred to me that using Atan2(y, x) instead of Atan(y/x) could fix the issue and it did.
Related
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.
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 :)
I am trying to develop an algorithm that involves normalizing GPS coordinates (latitude/longitude). That means, that being given two points A (lat1,lon1) and B(lat2,lon2) I would like to insert a point C that is linear with AB (same arc) and is placed at a specific distance from A and B (eg: A to B distance is 0.5km and I want point C to be at 0.1 km from A, on the AB arc). How can I calculate the coordinates for point C?
For the purpose given, it is enough to approximate Earth as a perfect spherical object.
I have found this article, but it gives the formula for midpoint only (and I don't fully understand it, in order to adapt).
midpoint between two latitude and longitude
Thank you.
Edit: I tried this but it gives wrong answers
public static void normalizedPoint(double lat1, double lon1, double lat2, double lon2, double dist){
double constant=Math.PI/180;
double angular = dist/6371;
double a = Math.Sin( 0* angular )/Math.Sin(angular);
double b = Math.Sin(1*angular)/Math.Sin(angular);
double x = a * Math.Cos(lat1) * Math.Cos(lon1) + b * Math.Cos(lat2) * Math.Cos(lon2);
double y = a * Math.Cos(lat1) * Math.Sin(lon1) + b * Math.Cos(lat2) * Math.Sin(lon2);
double z = a * Math.Sin(lat1) + b * Math.Sin (lon2);
double lat3 = Math.Atan2(z, Math.Sqrt( x*x + y*y ));
double lon3 = Math.Atan2(y, x);
Console.WriteLine(lat3/constant + " " + lon3/constant );
}
As far as I understood the original formulas this should return one of the 2 original points, but it does not(because the fraction used is 1). Also the variable dist is the distance from the 2 points and is properly calculated (checked with the same website).
Edit 2: I am providing as inputs coordinates for 2 geographic points (lat1, lon1, lat2 lon2) and the distance between them. I'm trying to get an intermediary point (lat3,lon3).
As I point out in an answer on the linked to question, you need to change all of your inputs to use radians rather than degrees.
I believe you also had an error for z where you used lon2 rather than lat2.
With those corrections, I get the answer you're seeking:
public static void normalizedPoint(double lat1, double lon1,
double lat2, double lon2,
double dist)
{
double constant = Math.PI / 180;
double angular = dist / 6371;
double a = Math.Sin(0 * angular) / Math.Sin(angular);
double b = Math.Sin(1 * angular) / Math.Sin(angular);
double x = a * Math.Cos(lat1* constant) * Math.Cos(lon1* constant) +
b * Math.Cos(lat2* constant) * Math.Cos(lon2* constant);
double y = a * Math.Cos(lat1* constant) * Math.Sin(lon1* constant) +
b * Math.Cos(lat2* constant) * Math.Sin(lon2* constant);
double z = a * Math.Sin(lat1* constant) + b * Math.Sin(lat2* constant);
double lat3 = Math.Atan2(z, Math.Sqrt(x * x + y * y));
double lon3 = Math.Atan2(y, x);
Console.WriteLine(lat3 / constant + " " + lon3 / constant);
}
Of course, the above can be vastly simplified by only converting angles ones, avoiding repeated calculations of the same Sin/Cos values, etc.
Calling:
normalizedPoint(47.20761, 27.02185, 47.20754, 27.02177, 1);
I get the output:
47.20754 27.02177
Not sure if the original author found some answer, but since I had similar problem and developed working solution, I think it would be good to post it here.
The Problem
Having two geographical points, A and B, find intermediate point C which lies exactly on the direct way from A to B and is N kilometers far from A (where N is less than distance between A and B, otherwise C = B).
My Context
I was developing small pet project based on microservices architecture. The idea was to launch missile from given deployment platform (point A) to chosen target location (point B). I had to create some kind of simulator that sends some messages about current missile Geo location after it is launched, so I had to find those intermediate points between A and B somehow.
Solution Context
Eventually, I developed C# based solution based on this great web page - https://www.movable-type.co.uk/scripts/latlong.html.
That web page has all the explanations, formulas and JavaScript code at the bottom. If you are not familiar with the C#, you can use their JavaScript implementation.
My C# Implementation
Your input is Location A, Location B and the distance.
You need to find bearing from A to B (see 'Bearing' section on that site)
You need to find position C from A having bearing (see 'Destination point given distance and bearing from start point' on that site)
The Code
I have working solution as part of my pet project and it can be found here - https://github.com/kakarotto67/mlmc/blob/master/src/Services/MGCC.Api/ChipSimulation/CoordinatesHelper.cs.
(Since original class is subject to change in the future, you might need to refer to this gist - https://gist.github.com/kakarotto67/ef682bb5b3c8bd822c7f3cbce86ff372)
Usage
// 1. Find bearing between A and B
var bearing = CoordinatesHelper.FindInitialBearing(pointA, pointB);
// 2. Find intermediate point C having bearing (above) and any distance in km
var pointC = CoordinatesHelper.GetIntermediateLocation(pointA, bearing, distance);
I hope somebody will find this helpful.
def get_intermediate_point(lat1 , lon1 , lat2 , lon2 , d):
constant = np.pi / 180
R = 6371
φ1 = lat1 * constant
λ1 = lon1 * constant
φ2 = lat2 * constant
λ2 = lon2 * constant
y = np.sin(λ2-λ1) * np.cos(φ2);
x = np.cos(φ1)*np.sin(φ2) - np.sin(φ1)*np.cos(φ2)*np.cos(λ2-λ1)
θ = np.arctan2(y, x)
brng = (θ*180/np.pi + 360) % 360; #in degrees
brng = brng * constant
φ3 = np.arcsin( np.sin(φ1)*np.cos(d/R ) + np.cos(φ1)*np.sin(d/R )*np.cos(brng) )
λ3 = λ1 + np.arctan2(np.sin(brng)*np.sin(d/R )*np.cos(φ1), np.cos(d/R )-np.sin(φ1)*np.sin(φ2));
return φ3/constant , λ3/constant
I'm currently developing a 2-player Ping-Pong game (in 2D - real simple) from scratch, and it's going good. However Theres a problem I just can't seem to solve - I'm not sure if this should be located here or on MathExchange - anyway here goes.
Initially the ball should be located in the center of the canvas. When pressing a button the ball should be fired off in a completely random direction - but always with the same velocity.
The Ball object has (simplified) 4 fields - The position in X and Y, and the velocity in X and Y. This makes it simple to bounce the ball off the walls when it hits, simple by inverting the velocities.
public void Move()
{
if (X - Radius < 0 || X + Radius > GameWidth)
{
XVelocity = -XVelocity;
}
if (Y - Radius < 0 || Y + Radius > GameHeight)
{
YVelocity = -YVelocity;
}
X+= XVelocity;
Y+= YVelocity;
}
I figured the velocity should be the same in each game, so I figures I would use Pythagoras - the square of the two velocities should always be the same.
SO for the question:
Is there a way to randomly select two numbers (doubles) such that the sum of their squares is always a specific number - more formally:
double x = RandomDouble();
double y = RandomDouble();
if (x^2 + y^2 = 16) {/* should always be true */ }
Any help appreciated :)
Randomly pick an angle theta and multiply that by the magnitude of the distance d you want. Something like:
double theta = rand.NextDouble() * 2.0 * Math.PI;
double x = d * Math.Cos(theta);
double y = d * Math.Sin(theta);
If the constant is C, pick a number x between 0 and sqrt(C).
Solve for the other number y using simple algebra.
why not try this:
double x = RandomDouble();
double y = square(16-x^2);
as your application allow double type.
does this solve your problem?
if not, please let me know
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.