如何最小化围绕任意轴旋转 3D space 中的点时的浮点误差?

How to minimize floating point inaccuracy in rotating a point in 3D space about an arbitrary axis?

我有一个程序可以在 3D 上可视化平移和旋转 space。我对主要代码没有问题,但经过数小时的测试后,我发现导致不准确的函数是任意轴上的旋转。

我的对象包含点数组和三个作为局部轴的向量。我对这些轴使用旋转功能。当我的位置在原点时,该功能运行良好,但是一旦该点被平移到原点以外的地方,它就会显示轻微的错误旋转,直到该点不在适当的位置。

我已将此 website as my guide, please jump to the bottom of the page at 6.2 and see the function with 10 parameters. On the same page, there's a link 用于该过程的示例用法。我已经确认问题在于此应用程序的浮点不准确。

我要旋转的样本点:( 0, -9, 0) 与旋转轴相交的点:( 0, -10, 0) 旋转轴的方向向量:<1,0,0> 旋转角度:2度

每次循环,当前点旋转2度,重复20次。第一次迭代的正确 Y 值是 -9.0006,而在我的例子中它给出 -8.9945 并持续波动直到第 5 次或第 6 次迭代,然后在这些迭代之后显示正确的值。

这里是旋转函数,注意Point是要旋转的点,AxisPoint是与轴相交的点旋转,AxisDirection是平行于旋转轴的方向向量,Degrees是旋转的角度量:

private static Vector3d RotateArbitrary(Vector3d Point, Vector3d AxisPoint, Vector3d AxisDirection, double Degrees)
    {
        return new Vector3d(
            ((AxisPoint.X * (Math.Pow(AxisDirection.Y, 2) + Math.Pow(AxisDirection.Z, 2)) - AxisDirection.X * (AxisPoint.Y * AxisDirection.Y + AxisPoint.Z * AxisDirection.Z - AxisDirection.X * Point.X - AxisDirection.Y * Point.Y - AxisDirection.Z * Point.Z)) * (1 - Math.Cos(MathHelper.DegreesToRadians(Degrees))) + Point.X * Math.Cos(MathHelper.DegreesToRadians(Degrees)) + (-AxisPoint.Z * AxisDirection.Y + AxisPoint.Y * AxisDirection.Z - AxisDirection.Z * Point.Y + AxisDirection.Y * Point.Z) * Math.Sin(MathHelper.DegreesToRadians(Degrees))),
            ((AxisPoint.X * (Math.Pow(AxisDirection.X, 2) + Math.Pow(AxisDirection.Z, 2)) - AxisDirection.Y * (AxisPoint.X * AxisDirection.X + AxisPoint.Z * AxisDirection.Z - AxisDirection.X * Point.X - AxisDirection.Y * Point.Y - AxisDirection.Z * Point.Z)) * (1 - Math.Cos(MathHelper.DegreesToRadians(Degrees))) + Point.Y * Math.Cos(MathHelper.DegreesToRadians(Degrees)) + (AxisPoint.Z * AxisDirection.X - AxisPoint.X * AxisDirection.Z + AxisDirection.Z * Point.X - AxisDirection.X * Point.Z) * Math.Sin(MathHelper.DegreesToRadians(Degrees))),
            ((AxisPoint.X * (Math.Pow(AxisDirection.X, 2) + Math.Pow(AxisDirection.Y, 2)) - AxisDirection.Z * (AxisPoint.X * AxisDirection.X + AxisPoint.Y * AxisDirection.Y - AxisDirection.X * Point.X - AxisDirection.Y * Point.Y - AxisDirection.Z * Point.Z)) * (1 - Math.Cos(MathHelper.DegreesToRadians(Degrees))) + Point.Z * Math.Cos(MathHelper.DegreesToRadians(Degrees)) + (-AxisPoint.Y * AxisDirection.X + AxisPoint.X * AxisDirection.Y - AxisDirection.Y * Point.X + AxisDirection.X * Point.Y) * Math.Sin(MathHelper.DegreesToRadians(Degrees)))
            );
    }

float和double两种数据类型我都试过了,结果还是一样,你能提供什么方案,哪怕是轮换方式的改变,我都愿意。请帮忙。

由于我长时间的网络搜索,我终于得出了我应该使用四元数的结论。使用当前的方法,我发现对浮点变量的过度操作会增加舍入误差。使用四元数更简单、更干净。

下面是代码,如果有人感兴趣的话:

private static Vector3 RotateArbitrary(Vector3 Point, Vector3 AxisPoint, Vector3 AxisDirection, float Radians)
        {
            return Vector3.Add(Vector3.Transform(Vector3.Subtract(Point, AxisPoint), Quaternion.FromAxisAngle(AxisDirection, Radians)), AxisPoint);
        }

请注意,首先平移Point,使AxisPoint 位于原点,这样就可以进行旋转。然后将结果转换到其原始位置。