假设北方为零弧度,如何在极坐标和笛卡尔坐标之间进行转换?



我一直在尝试为游戏制作一个简单的物理引擎。我很清楚这是重新发明轮子,但它更像是一种学习练习,而不是其他任何事情。例如,我从没想过它会像box2d一样完整。

我在实现 2d 矢量时遇到问题。这个问题与以下事实有关:在游戏世界中,我想将北方表示为零弧度,将东部表示为 1/2 PI 弧度,或分别为 0 度和 90 度。然而,在数学中(或者更具体地说是Java的数学类),我相信像正弦和余弦这样的三角函数假设"东"是零弧度,我认为北是1/2 PI弧度?

无论如何,我已经编写了矢量类的一个小版本,只演示了错误的代码。

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
} }

要求是:

  1. fromPolar(magnitude,angle)应返回一个x的向量,并且y初始化为适当的值,假设北弧度为零,东弧度为 1/2 PI。 例如,fromPolar(10,PI)应该构造一个 x: 0 和 y: -1 的向量

  2. getTheta()应返回大于或等于零且小于 2 PI 的值。θ是它所调用的矢量的角度分量。例如,具有 x:10 和 y:10 的向量在调用getTheta()时将返回 1/4 PI 的值。

  3. bearingTo(target)应返回大于或等于零且小于 2 PI 的值。该值表示另一个矢量的方位角。

测试代码表明,当您尝试将 (0,0) 处的一个点的方位角变为 (10,0) 处的另一个点时,它不会产生预期的结果,它应该是 90 度或 1/2 PI 弧度。

同样,尝试从极坐标初始化矢量会将 x 和 y 坐标设置为意外值。我试图避免说"不正确的值",因为它不是不正确的,它只是不符合要求。

我弄乱了很多代码,在这里添加PI的分数或在那里将其拿走,切换正弦和余弦,但所有这些事情都只能解决部分问题,而不是整个问题。

最后,我制作了可以在线执行的代码版本 http://tpcg.io/OYVB5Q

典型的极坐标0指向东方,它们逆时针方向移动。您的坐标从北方开始,可能顺时针方向。修复代码的最简单方法是首先使用以下公式进行角度之间的转换:

flippedAngle = π/2 - originalAngle

此公式是对称的,因为它在"您的"和"标准"坐标之间进行双向转换。因此,如果您将代码更改为:

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));
}

它开始按照您的测试建议工作。您还可以应用一些三角学知识来不进行此操作Math.PI/2 - angle计算,但我不确定这是否真的使代码更清晰。

如果您希望您的"方位"在[0, 2*π]范围内(即始终为非负值),您可以使用此版本的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;
}
}

最新更新