2012-12-17 159 views
2

我有一系列頂點(粉色),我想旋轉以便頂點圖案的一個邊緣與三角形的邊緣匹配(白色)。兩個3D矢量之間的角度

爲此,我首先創建兩個向量來表示邊:floretAB和triangleAB(綠色)。然後我找到兩者的叉積,得到一個我可以旋轉頂點的軸(紅色)。

然後我得到兩個向量之間的角度,並使用它與旋轉軸創建一個四元數。最後,我旋轉四元數週圍的所有頂點。

enter image description here

循環之前

_

enter image description here

什麼轉動應產生

_

然而,儘管頂點正確周圍的四元數旋轉,角度不出來正確的,說明這裏:

enter image description here

這是我使用來獲得兩者之間的角碼向量。我不明白我在做什麼錯:

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

_

public class Vector3D { 

public double mX; 
public double mY; 
public double mZ; 

public Vertex3D point; 

/** 
* Constructs a vector from two points. The new vector is normalised 
* 
* @param point1 
* @param point2 
*/ 
public Vector3D(Vertex3D point1, Vertex3D point2){ 
    mX = point2.getmX() - point1.getmX(); 
    mY = point2.getmY() - point1.getmY(); 
    mZ = point2.getmZ() - point1.getmZ(); 
    normalise(); 
    point = point1; 
} 

/** 
* Normalises the vector 
*/ 
public void normalise(){ 
    double magnitude = Math.sqrt(mX*mX + mY*mY + mZ*mZ); 
    if(magnitude!=0){ 
     mX /= magnitude; 
     mY /= magnitude; 
     mZ /= magnitude; 
    } 
} 

/** 
* 
* @return the magnitude of the vector 
*/ 
public double getMagnitude(){ 
    return Math.sqrt(mX*mX + mY*mY + mZ*mZ); 
} 

} 

_

public class Quaternion { 

private static final double TOLERANCE = 0.00001f; 

double w; 
double x; 
double y; 
double z; 

public Quaternion(double axisX, double axisY, double axisZ, double angleInRadians){ 
    setAxisAngle(axisX, axisY, axisZ, angleInRadians);  
} 

public void Normalise(){ 

    // Don't normalize if we don't have to 
    double mag2 = w * w + x * x + y * y + z * z; 
    if (Math.abs(mag2) > TOLERANCE && Math.abs(mag2 - 1.0f) > TOLERANCE) { 
     double mag = (double) Math.sqrt(mag2); 
     w /= mag; 
     x /= mag; 
     y /= mag; 
     z /= mag; 
    } 

} 

public void getConjugate(double[] outputArray){ 

    outputArray[0] = w; 
    outputArray[1] = -x; 
    outputArray[2] = -y; 
    outputArray[3] = -z; 

} 

public void Multiply(double[] aq, double[] rq, double[] outputArray){ 

    outputArray[0] = aq[0] * rq[0] - aq[1] * rq[1] - aq[2] * rq[2] - aq[3] * rq[3]; 
    outputArray[1] = aq[0] * rq[1] + aq[1] * rq[0] + aq[2] * rq[3] - aq[3] * rq[2]; 
    outputArray[2] = aq[0] * rq[2] + aq[2] * rq[0] + aq[3] * rq[1] - aq[1] * rq[3]; 
    outputArray[3] = aq[0] * rq[3] + aq[3] * rq[0] + aq[1] * rq[2] - aq[2] * rq[1]; 

} 

private double[] vecQuat = new double[4]; 
private double[] resQuat = new double[4]; 
private double[] thisQuat = new double[4]; 

private double[] conj = new double[4]; 

/** 
* Rotates a vector (or point) around this axis-angle 
* 
* @param vectorX the x component of the vector (or point) 
* @param vectorY the y component of the vector (or point) 
* @param vectorZ the z component of the vector (or point) 
* @param outputArray the array in which the results will be stored 
*/ 
public void RotateVector(double vectorX, double vectorY, double vectorZ, double[] outputArray){ 

    vecQuat[0] = 0.0f; 
    vecQuat[1] = vectorX; 
    vecQuat[2] = vectorY; 
    vecQuat[3] = vectorZ; 

    thisQuat[0] = w; 
    thisQuat[1] = x; 
    thisQuat[2] = y; 
    thisQuat[3] = z; 

    getConjugate(conj); 
    Multiply(vecQuat,conj,resQuat); 
    Multiply(thisQuat,resQuat,vecQuat); 

    outputArray[0] = vecQuat[1]; 
    outputArray[1] = vecQuat[2]; 
    outputArray[2] = vecQuat[3]; 

} 

/** 
* set Quaternion by providing axis-angle form 
*/ 
public void setAxisAngle(double axisX, double axisY, double axisZ, double angleInRadians){ 
    w = (double) Math.cos(angleInRadians/2); 
    x = (double) (axisX * Math.sin(angleInRadians/2)); 
    y = (double) (axisY * Math.sin(angleInRadians/2)); 
    z = (double) (axisZ * Math.sin(angleInRadians/2)); 

    Normalise(); 
} 
} 
+0

嘗試'Math.atan2(dot,crossMag);' –

+0

沒有區別/解決問題:( –

回答

1

我認爲你的數學過於複雜。給定兩個單位向量(你確實說他們被歸一化),那麼交叉乘積的大小等於sin(theta)。不應該有任何需要調用點積或atan2

您可能還需要在創建四元數之前對交叉乘積向量結果進行歸一化 - 這取決於您的new Quaternion(x, y, z, theta)的實現以及是否需要[x, y, z]進行歸一化。

+0

數學很好。我需要在將我的跨產品向量放入四元數之前進行標準化處理 –

+0

@JamesCoote這是一個幸運的猜測,然後:) – Alnitak

1

我認爲問題在於你以錯誤的方式評估角度。 如果我正確理解你想達到什麼目標,那麼你需要2條綠線之間的角度。你正確地評價使用的定義2條綠線之間的點積:

(a, b) = a1*b1 + a2*b2 + a3*b3. 

但積也可以這樣評價:

(a, b) = |a|*|b|*cos(theta) 

所以,你可以評估COS(THETA) - 的餘弦2綠線之間的角度 - 像這樣:

cos(theta) = (a1*b1 + a2*b2 + a3*b3)/(|a|*|b|) 

但我會用另一種方法。我首先將兩個向量歸一化(即將它們轉換爲unit-vectors)。您可以通過向量的長度除以每個矢量的分量做到這一點(的sqrt(X1 * X1 + Y1 * Y1 + Z1 * Z1)),然後,你將具備以下條件:

(aa, bb) = cos(theta) 

其中aa是歸一化的和BB被標準化b。

我希望這會有所幫助。

+0

實際上向量已經正常化。我嘗試過使用你的方法,但它給出了完全相同的結果,這表明我的代碼在其他地方有問題,但是如果我手動將角度設置爲Pi或Pi/2(即90或180度),它會旋轉正確的金額 –

+0

@詹姆斯Coote:你的意思是如果你硬編碼Pi或Pi/2,並使用完全相同的代碼,你現在除了它將正確旋轉? –

+0

是的,這就是爲什麼我推斷一定有什麼問題代碼生成的角度,我可能是錯的,所以我會發布其餘的代碼以及 –

1

所述的答案對於實數是正確的,但在用浮點數計算時可能會在某些角度附近的精度不準確。對於arcos(),當角度接近於零或PI時,以及對於pi/2和-pi/2附近的arcsin(),可能丟失多達一半的有效數字。是更健壯和唯一的方法遭受一些舍入誤差均勻地分佈在整個範圍內,並且包括零且包括PI,假設輸入矢量是單位長度是:

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

注意,這給出了未取向的角在兩個向量之間。對此的參考和歸因於Kahan可能被找到在:http://www.cs.berkeley.edu/~wkahan/MathH110/Cross.pdf

+0

什麼是2.0d? – user151496

+0

@ user151496代碼在c#中,「d」告訴編譯器常量2.0是一個double值。誇大其辭,但強調錶達要以雙精度進行。 – dmbaker