假设北为零弧度,如何在极坐标和笛卡尔坐标之间进行转换?
How do you convert between polar coordinates and cartesian coordinates assuming north is zero radians?
我一直在尝试为游戏制作一个简单的物理引擎。我很清楚这是在重新发明轮子,但它更像是一种学习练习,而不是其他任何东西。例如,我从没想过它会像 box2d 一样完整。
我在执行二维向量时遇到问题。这个问题与以下事实有关:在游戏世界中,我想将北表示为零弧度,将东表示为 1/2 PI 弧度,或分别为 0 度和 90 度。然而在数学中(或者更具体地说 Java 的数学 class),我相信像正弦和余弦这样的三角函数假设 "east" 是零弧度,我 认为 北是 1/2 PI 弧度?
无论如何,我已经编写了我的向量的一个小版本 class,它只演示了错误的代码。
public class Vector {
private final double x;
private final double y;
public Vector(double xIn, double yIn) {
x = xIn;
y = yIn;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getR() {
return Math.sqrt((x * x) + (y * y));
}
public double getTheta() {
return Math.atan(y / x);
}
public double bearingTo(Vector target) {
return (Math.atan2(target.getY() - y, target.getX() - x));
}
public static Vector fromPolar(double magnitude, double angle) {
return new Vector(magnitude * Math.cos(angle),
magnitude * Math.sin(angle));
}
}
这里是演示问题的测试代码:
public class SOQuestion {
public static void main(String[] args) {
//This works just fine
Vector origin = new Vector(0, 0);
Vector target = new Vector(10, 10);
double expected = Math.PI * 0.25;
double actual = origin.bearingTo(target);
System.out.println("Expected: " + expected);
System.out.println("Actual: " + actual);
//This doesn't work
origin = new Vector(0, 0);
target = new Vector(10, 0);
expected = Math.PI * 0.5; //90 degrees, or east.
actual = origin.bearingTo(target); //Of course this is going to be zero, because in mathematics right is zero
System.out.println("Expected: " + expected);
System.out.println("Actual: " + actual);
//This doesn't work either
Vector secondTest = Vector.fromPolar(100, Math.PI * 0.5); // set the vector to the cartesian coordinates of (100,0)
System.out.println("X: " + secondTest.getX()); //X ends up being basically zero
System.out.println("Y: " + secondTest.getY()); //Y ends up being 100
} }
要求是:
fromPolar(magnitude,angle)
应 return 具有 x
和 y
的矢量初始化为适当的值,假设北为零弧度且东为1/2 PI 弧度。例如 fromPolar(10,PI)
应该构造一个 x: 0 和 y: -1
的向量
getTheta()
应该 return 一个大于或等于零且小于 2 PI 的值。 Theta 是它所调用的向量的 angular 分量。例如,当调用 getTheta()
时,具有 x:10 和 y:10 的向量将 return 的值为 1/4 PI。
bearingTo(target)
应该 return 一个大于或等于零且小于 2 PI 的值。该值表示与另一个向量的方位角。
测试代码表明,当您尝试将 (0,0) 处的一个点的方位角设置为 (10,0) 处的另一点时,它不会产生预期的结果,应该是 90 度或 1/2 PI 弧度。
同样,尝试从极坐标初始化向量会将 x 和 y 坐标设置为意外值。我尽量避免说 "incorrect values" 因为它不是不正确的,它只是不符合要求。
我把代码弄乱了很多,在这里添加 PI 的分数或在那里去掉,切换正弦和余弦,但所有这些事情只能解决部分问题,而不能解决整个问题。
最后我做了一个可以在线执行的代码版本http://tpcg.io/OYVB5Q
典型的极坐标 0
指向东方,它们指向 counter-clockwise。您的坐标从北方开始,可能顺时针方向。修复代码的最简单方法是首先使用以下公式在角度之间进行转换:
flippedAngle = π/2 - originalAngle
这个公式是对称的,因为它在 "your" 和 "standard" 坐标之间进行双向转换。因此,如果您将代码更改为:
public double bearingTo(Vector target) {
return Math.PI/2 - (Math.atan2(target.getY() - y, target.getX() - x));
}
public static Vector fromPolar(double magnitude, double angle) {
double flippedAngle = Math.PI/2 - angle;
return new Vector(magnitude * Math.cos(flippedAngle),
magnitude * Math.sin(flippedAngle));
}
它开始作为您的 tests suggest 工作。您也可以应用一些三角学知识来不进行此 Math.PI/2 - angle
计算,但我不确定这是否真的使代码更清晰。
如果你想让你的 "bearing" 在 [0, 2*π]
范围内(即总是 non-negative),你可以使用这个版本的 bearingTo
(也修复了 theta
):
public class Vector {
private final double x;
private final double y;
public Vector(double xIn, double yIn) {
x = xIn;
y = yIn;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getR() {
return Math.sqrt((x * x) + (y * y));
}
public double getTheta() {
return flippedAtan2(y, x);
}
public double bearingTo(Vector target) {
return flippedAtan2(target.getY() - y, target.getX() - x);
}
public static Vector fromPolar(double magnitude, double angle) {
double flippedAngle = flipAngle(angle);
return new Vector(magnitude * Math.cos(flippedAngle),
magnitude * Math.sin(flippedAngle));
}
// flip the angle between 0 is the East + counter-clockwise and 0 is the North + clockwise
// and vice versa
private static double flipAngle(double angle) {
return Math.PI / 2 - angle;
}
private static double flippedAtan2(double y, double x) {
double angle = Math.atan2(y, x);
double flippedAngle = flipAngle(angle);
// additionally put the angle into [0; 2*Pi) range from its [-pi; +pi] range
return (flippedAngle >= 0) ? flippedAngle : flippedAngle + 2 * Math.PI;
}
}
我一直在尝试为游戏制作一个简单的物理引擎。我很清楚这是在重新发明轮子,但它更像是一种学习练习,而不是其他任何东西。例如,我从没想过它会像 box2d 一样完整。
我在执行二维向量时遇到问题。这个问题与以下事实有关:在游戏世界中,我想将北表示为零弧度,将东表示为 1/2 PI 弧度,或分别为 0 度和 90 度。然而在数学中(或者更具体地说 Java 的数学 class),我相信像正弦和余弦这样的三角函数假设 "east" 是零弧度,我 认为 北是 1/2 PI 弧度?
无论如何,我已经编写了我的向量的一个小版本 class,它只演示了错误的代码。
public class Vector {
private final double x;
private final double y;
public Vector(double xIn, double yIn) {
x = xIn;
y = yIn;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getR() {
return Math.sqrt((x * x) + (y * y));
}
public double getTheta() {
return Math.atan(y / x);
}
public double bearingTo(Vector target) {
return (Math.atan2(target.getY() - y, target.getX() - x));
}
public static Vector fromPolar(double magnitude, double angle) {
return new Vector(magnitude * Math.cos(angle),
magnitude * Math.sin(angle));
}
}
这里是演示问题的测试代码:
public class SOQuestion {
public static void main(String[] args) {
//This works just fine
Vector origin = new Vector(0, 0);
Vector target = new Vector(10, 10);
double expected = Math.PI * 0.25;
double actual = origin.bearingTo(target);
System.out.println("Expected: " + expected);
System.out.println("Actual: " + actual);
//This doesn't work
origin = new Vector(0, 0);
target = new Vector(10, 0);
expected = Math.PI * 0.5; //90 degrees, or east.
actual = origin.bearingTo(target); //Of course this is going to be zero, because in mathematics right is zero
System.out.println("Expected: " + expected);
System.out.println("Actual: " + actual);
//This doesn't work either
Vector secondTest = Vector.fromPolar(100, Math.PI * 0.5); // set the vector to the cartesian coordinates of (100,0)
System.out.println("X: " + secondTest.getX()); //X ends up being basically zero
System.out.println("Y: " + secondTest.getY()); //Y ends up being 100
} }
要求是:
fromPolar(magnitude,angle)
应 return 具有x
和y
的矢量初始化为适当的值,假设北为零弧度且东为1/2 PI 弧度。例如fromPolar(10,PI)
应该构造一个 x: 0 和 y: -1 的向量
getTheta()
应该 return 一个大于或等于零且小于 2 PI 的值。 Theta 是它所调用的向量的 angular 分量。例如,当调用getTheta()
时,具有 x:10 和 y:10 的向量将 return 的值为 1/4 PI。bearingTo(target)
应该 return 一个大于或等于零且小于 2 PI 的值。该值表示与另一个向量的方位角。
测试代码表明,当您尝试将 (0,0) 处的一个点的方位角设置为 (10,0) 处的另一点时,它不会产生预期的结果,应该是 90 度或 1/2 PI 弧度。
同样,尝试从极坐标初始化向量会将 x 和 y 坐标设置为意外值。我尽量避免说 "incorrect values" 因为它不是不正确的,它只是不符合要求。
我把代码弄乱了很多,在这里添加 PI 的分数或在那里去掉,切换正弦和余弦,但所有这些事情只能解决部分问题,而不能解决整个问题。
最后我做了一个可以在线执行的代码版本http://tpcg.io/OYVB5Q
典型的极坐标 0
指向东方,它们指向 counter-clockwise。您的坐标从北方开始,可能顺时针方向。修复代码的最简单方法是首先使用以下公式在角度之间进行转换:
flippedAngle = π/2 - originalAngle
这个公式是对称的,因为它在 "your" 和 "standard" 坐标之间进行双向转换。因此,如果您将代码更改为:
public double bearingTo(Vector target) {
return Math.PI/2 - (Math.atan2(target.getY() - y, target.getX() - x));
}
public static Vector fromPolar(double magnitude, double angle) {
double flippedAngle = Math.PI/2 - angle;
return new Vector(magnitude * Math.cos(flippedAngle),
magnitude * Math.sin(flippedAngle));
}
它开始作为您的 tests suggest 工作。您也可以应用一些三角学知识来不进行此 Math.PI/2 - angle
计算,但我不确定这是否真的使代码更清晰。
如果你想让你的 "bearing" 在 [0, 2*π]
范围内(即总是 non-negative),你可以使用这个版本的 bearingTo
(也修复了 theta
):
public class Vector {
private final double x;
private final double y;
public Vector(double xIn, double yIn) {
x = xIn;
y = yIn;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getR() {
return Math.sqrt((x * x) + (y * y));
}
public double getTheta() {
return flippedAtan2(y, x);
}
public double bearingTo(Vector target) {
return flippedAtan2(target.getY() - y, target.getX() - x);
}
public static Vector fromPolar(double magnitude, double angle) {
double flippedAngle = flipAngle(angle);
return new Vector(magnitude * Math.cos(flippedAngle),
magnitude * Math.sin(flippedAngle));
}
// flip the angle between 0 is the East + counter-clockwise and 0 is the North + clockwise
// and vice versa
private static double flipAngle(double angle) {
return Math.PI / 2 - angle;
}
private static double flippedAtan2(double y, double x) {
double angle = Math.atan2(y, x);
double flippedAngle = flipAngle(angle);
// additionally put the angle into [0; 2*Pi) range from its [-pi; +pi] range
return (flippedAngle >= 0) ? flippedAngle : flippedAngle + 2 * Math.PI;
}
}