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

Java 假设北极的弧度为零,如何在极坐标和笛卡尔坐标之间转换?,java,math,game-physics,2d-games,Java,Math,Game Physics,2d Games,我一直在尝试为游戏制作一个简单的物理引擎。我很清楚,这是在重新发明轮子,但这更像是一种学习练习,而不是其他任何东西。例如,我从未期望它像box2d那样完整 我的2d向量实现有问题。这个问题与这样一个事实有关:在游戏世界中,我想将北方表示为零弧度,将东方表示为1/2π弧度,或者分别表示为0度和90度。然而在数学(或者更具体地说是Java的数学类)中,我相信像正弦和余弦这样的三角函数假设“东”是零弧度,我认为北是1/2π弧度 无论如何,我已经编写了向量类的一个小版本,只演示了错误代码 public

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

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

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

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

  • getTheta()
    应返回大于或等于零且小于2π的值。θ是它所调用的向量的角分量。例如,当调用
    getTheta()
    时,x:10和y:10的向量将返回1/4pi的值

  • 承载到(目标)
    应返回大于或等于零且小于2π的值。该值表示指向另一个向量的方向角

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

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

    我把代码弄得一团糟,在这里加上π的分数,在那里去掉π,切换正弦和余弦,但所有这些都只解决了部分问题,而不是整个问题


    最后,我制作了一个可以在线执行的代码版本,典型的极坐标
    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*π]
    范围内(即始终为非负),您可以使用此版本的
    轴承到
    (也固定了
    θ
    ):


    为什么你想在物理引擎中使用极坐标?有很多例子,但这里有一个简单的例子:假设你想得到一个实体的速度并显示在HUD上,简单的得到速度向量的大小。。。明确地说,物理引擎不在极坐标系下工作,但可以方便地访问它们。在三角坐标系中,角度随着x轴上的0点逆时针增加,指向+ve无穷大,在导航中,角度从北顺时针增加。你写的我相信像正弦和余弦这样的三角函数假设“东”是零弧度,我认为南是1/2π弧度?但我认为你理解的第二部分是错误的。这可能是你代码中错误的原因。它们以相反的方向增加这一事实肯定是问题的一部分,我知道这一点,但我不知道如何修复它。不过,我会修正我问题中的错误,那北方应该是1/2π弧度吗?我认为首先转换为正常极坐标可能更容易,然后只需更新角度以指向所需的位置。方位不能为负,而
    bearingTo
    的返回值可以。@meowgoethedog,不幸的是,OPs测试没有明确回答预期范围的问题。但是,如果在我添加了另一个版本的
    bearingTo
    感谢您的回答之后,OP真的是这样的话,那么我今天晚些时候将进行单元测试,以确认您的答案适用于所有情况:)@EstiaanJ,您的问题没有很好地说明(只是最简单的情况),因此我可能误解了您真正想要的。如果你需要进一步的帮助,请告诉我。是的,我没有具体说明。bearingTo()应该返回一个介于0(含0)和Tau极限之间的正值,这样,如果将其转换为度,它就像导航轴承一样。
    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;
        }
    }