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

标签 java math game-physics 2d-games

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

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

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

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) 应返回一个 vector ,其中 xy 初始化为适当的值(假设北在)零弧度,东为 1/2 PI 弧度。例如 fromPolar(10,PI) 应该构造一个 x: 0 和 y: -1 的 vector

  2. getTheta() 应返回一个大于或等于 0 且小于 2 PI 的值。 Theta 是它所调用的 vector 的角度分量。例如,当调用 getTheta() 时,具有 x:10 和 y:10 的 vector 将返回 1/4 PI 的值。

  3. bearingTo(target) 应返回一个大于或等于 0 且小于 2 PI 的值。该值表示相对于另一个 vector 的方位。

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

同样,尝试从极坐标初始化 vector 会将 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));
    }

它开始像您的 tests suggest 一样工作。 。您还可以应用一些三角学知识来不执行此 Math.PI/2 - 角度 计算,但我不确定这是否真的使代码更清晰。

如果您希望“方位角”在 [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;
    }
}

关于java - 假设北角为零弧度,如何在极坐标和笛卡尔坐标之间进行转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49576502/

相关文章:

Java - 查找类名和该类中的方法还可以查找该方法可用的参数

c++ - 如何将对象作为另一个对象的父对象并通过旋转影响其位置(使对象围绕其他对象旋转)

ios - 如何附加碰撞的 Sprite ?

python - 给定每条线上的一个点和一个平行向量,如何找到两条线的交点

math - 根据玩家的选择分配团队的算法

java - 安卓TCG游戏

java - 如何将 JAVA Swing 中的时间转换为货币

java - 为什么自定义系统类加载器不工作?

java - 什么是 "non-static method"错误以及 "this"是如何工作的?

c - 不使用除法或乘法运算符除以 9