Java 两个三维矢量之间的角度

Java 两个三维矢量之间的角度,java,math,vector,geometry,dot-product,Java,Math,Vector,Geometry,Dot Product,我有一系列要旋转的顶点(粉红色),以便顶点图案的一条边与三角形的边(白色)匹配 为此,我首先创建两个向量来表示边:floretAB和triangelab(绿色)。然后我找到两个顶点的叉积,得到一个轴,我可以围绕它旋转顶点(红色) 然后,我得到两个向量之间的角度,并用它与旋转轴一起创建一个四元数。最后,我围绕四元数旋转所有顶点 轮换前 _ 轮换应该产生什么结果 _ 但是,尽管顶点正确地围绕四元数旋转,但角度并不正确,如下图所示: 这是我用来得到两个向量之间的角度的代码。我不明白我做错了什么:

我有一系列要旋转的顶点(粉红色),以便顶点图案的一条边与三角形的边(白色)匹配

为此,我首先创建两个向量来表示边:floretAB和triangelab(绿色)。然后我找到两个顶点的叉积,得到一个轴,我可以围绕它旋转顶点(红色)

然后,我得到两个向量之间的角度,并用它与旋转轴一起创建一个四元数。最后,我围绕四元数旋转所有顶点

轮换前

_

轮换应该产生什么结果

_

但是,尽管顶点正确地围绕四元数旋转,但角度并不正确,如下图所示:

这是我用来得到两个向量之间的角度的代码。我不明白我做错了什么:

double[] cross = new double[3];
crossProduct(floretAB.mX, floretAB.mY, floretAB.mZ, triangleAB.mX, triangleAB.mY, triangleAB.mZ, cross);
double dot = dotProduct(floretAB.mX, floretAB.mY, floretAB.mZ, triangleAB.mX, triangleAB.mY, triangleAB.mZ);
double crossMag = Math.sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]);
double angle = Math.atan2(crossMag, dot);

public static double dotProduct(double vector1X,double vector1Y,double vector1Z,double vector2X,double vector2Y,double vector2Z){

    return vector1X*vector2X + vector1Y*vector2Y + vector1Z*vector2Z;

}

public static void crossProduct(double vector1X,double vector1Y,double vector1Z,double vector2X,double vector2Y,double vector2Z, double[] outputArray){

    outputArray[0] = vector1Y*vector2Z - vector1Z*vector2Y;     
    outputArray[1] = vector1Z*vector2X - vector1X*vector2Z;
    outputArray[2] = vector1X*vector2Y - vector1Y*vector2X;

}
如果您能帮上忙,我将不胜感激,因为这真的让我心烦

谢谢,詹姆斯

编辑:以下是代码的其余部分:

        // get floret p1,p2 vector
    // get triangle p1,p2 vector
    Vector3D floretAB = new Vector3D(florets3D[0], florets3D[7]);
    // get triangle p1,p2 vector
    Vector3D triangleAB = new Vector3D(triangle[0], triangle[1]);

    // get rotation axis (cross) and angle (dot)

    /*
    double[] cross = new double[3];
    crossProduct(floretAB.mX, floretAB.mY, floretAB.mZ, triangleAB.mX, triangleAB.mY, triangleAB.mZ, cross);
    double dotMag = floretAB.getMagnitude() * triangleAB.getMagnitude();
    double dot = dotProduct(floretAB.mX, floretAB.mY, floretAB.mZ, triangleAB.mX, triangleAB.mY, triangleAB.mZ) / dotMag;
    double angle = Math.acos(dot);
    */

    double[] cross = new double[3];
    crossProduct(floretAB.mX, floretAB.mY, floretAB.mZ, triangleAB.mX, triangleAB.mY, triangleAB.mZ, cross);
    double dot = dotProduct(floretAB.mX, floretAB.mY, floretAB.mZ, triangleAB.mX, triangleAB.mY, triangleAB.mZ);
    double crossMag = Math.sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]);
    double angle = Math.atan2(crossMag, dot);

    // rotate floret so p1,p2 vector matches with triangle p1,p2 vector     
    double[] newVerts = new double[3];
    Quaternion quat = new Quaternion(cross[0], cross[1], cross[2], angle);
    for(int i = 0;i<numfloretVerts;i++){
        Vertex3D vert = florets3D[i];
        quat.RotateVector(vert.getmX(), vert.getmY(), vert.getmZ(), newVerts);
        vert.setmX(newVerts[0]);
        vert.setmY(newVerts[1]);
        vert.setmZ(newVerts[2]);
    }
_


我认为问题在于你用错误的方式评估角度。 如果我正确理解了你想要实现的目标,那么你需要两条绿线之间的角度。使用以下定义正确计算两条绿线之间的点积:

(a, b) = a1*b1 + a2*b2 + a3*b3.
但dot产品也可以这样评估:

(a, b) = |a|*|b|*cos(theta)
cos(theta) = (a1*b1 + a2*b2 + a3*b3) / (|a|*|b|)
所以你可以计算cos(θ)-两条绿线之间夹角的余弦-如下所示:

(a, b) = |a|*|b|*cos(theta)
cos(theta) = (a1*b1 + a2*b2 + a3*b3) / (|a|*|b|)
但我会使用另一种方法。首先,我将规范化这两个向量(即,将它们转换为)。您可以通过将每个向量的分量除以向量的长度(sqrt(x1*x1+y1*y1+z1*z1))来完成此操作,然后您将得到以下结果:

(aa, bb) = cos(theta)
其中aa为标准化a,bb为标准化b


我希望这有帮助。

我想你的数学太复杂了

给定两个单位向量(你确实说过它们是归一化的),那么叉积的大小等于
sin(θ)
。不需要调用点积或atan2


在创建四元数之前,您可能还需要对叉积向量结果进行归一化-这取决于
新四元数(x,y,z,θ)的实现
以及是否需要
[x,y,z]
是否标准化。

所述答案对于实数是正确的,但在使用浮点数计算时,可能会在某些角度附近失去准确性。对于角度接近零或PI的arcos(),以及接近PI/2和–PI/2的arcsin(),可能会丢失一半的有效数字。假设输入向量为单位长度,则在从零到π的整个范围内(包括零到π的整个范围内)更稳健且仅承受少量均匀舍入误差的方法为:

public double AngleBetween(Vector3D a, Vector3D b)
{
    return 2.0d * Math.atan((a-b).Length/(a+b).Length);
}

注意,这给出了两个向量之间的无定向角度。关于这一点以及Kahan的参考资料,请访问:

Try
Math.atan2(dot,crossMag)没有改变/解决问题:(实际上向量已经归一化。我尝试使用你的方法,但结果完全相同。这表明我的代码在其他地方有问题,但如果我手动将角度设置为Pi或Pi/2(即90或180度),它会正确地旋转amounts@JamesCoote:你的意思是如果你硬编码Pi或Pi/2,并且使用完全相同的代码,它将正确旋转?是的,这就是为什么我推断生成角度的代码一定有问题。我可能错了,所以我将把剩下的代码发布为well@James库特:是的,如果您发布代码的其余部分,它可能会很有用。我现在可以建议的另一件事是检查角度的测量单位。如果您的代码适用于硬编码值,这可能是一个问题。顺便说一句,您是以弧度还是以度进行硬编码?有时可能会出现这样的情况:在一个变量中,您有一个以度为单位的值,但在另一个变量中,您可能有一个以度为单位的值用弧度表示。如果在计算中同时使用它们,而不事先转换为相同的测量单位,则会得到错误的结果。数学很好。我需要在将其放入quaternion@JamesCoote这是一个幸运的猜测:)@user151496代码是c#和“d”告诉编译器常量2.0是双精度值。过度杀戮可能会加强表达式的执行精度。