2010-09-10 10 views
31

Non riesco a trovare alcuna informazione valida su questo argomento. Fondamentalmente voglio trovare il componente di una rotazione quaternion, cioè attorno ad un dato asse (non necessariamente X, Y o Z - qualsiasi vettore di unità arbitrarie). Un po 'come proiettare un quaternione su un vettore. Quindi, se dovessi chiedere la rotazione attorno ad un asse parallelo all'asse del quaternario, avrei recuperato lo stesso quaterno. Se dovessi chiedere la rotazione attorno a un asse ortogonale all'asse del quaternario, uscirò da un quaternario di identità. E tra ... beh, questo è quello che mi piacerebbe sapere come risolvere :)Componente di una rotazione quaternion attorno ad un asse

+1

ortogonale e perpendicolare è lo stesso per i vettori . Probabilmente intendevi un parallelo per il caso del quaternario dell'identità. – angularsen

+1

Wow; ben notato! Ho letto più volte (e ho avuto colleghi che cercavano in modo indipendente la stessa cosa e ho trovato questa domanda) e non l'ho mai notato prima. Intendevo parallelo per il caso di non cambiamento (ad esempio il componente di una rotazione attorno all'asse Y, attorno all'asse Y, rimarrà invariato) e ho aggiornato la domanda per riflettere questo. Grazie! –

risposta

15

Ho trascorso l'altro giorno cercando di trovare la stessa identica cosa per un editor di animazione; ecco come l'ho fatto:

  1. Prendi l'asse in cui vuoi trovare la rotazione e trova un vettore ortogonale.
  2. Ruota questo nuovo vettore utilizzando il quaternione.
  3. progetto questo ruotato vettore sul piano la cui normale è dell'asse
  4. l'Acos del prodotto scalare di questo vettore e proiettate ortogonale originale è l'angolo.

    public static float FindQuaternionTwist(Quaternion q, Vector3 axis) 
    { 
        axis.Normalize(); 
    
        // Get the plane the axis is a normal of 
        Vector3 orthonormal1, orthonormal2; 
        ExMath.FindOrthonormals(axis, out orthonormal1, out orthonormal2); 
    
        Vector3 transformed = Vector3.Transform(orthonormal1, q); 
    
        // Project transformed vector onto plane 
        Vector3 flattened = transformed - (Vector3.Dot(transformed, axis) * axis); 
        flattened.Normalize(); 
    
        // Get angle between original vector and projected transform to get angle around normal 
        float a = (float)Math.Acos((double)Vector3.Dot(orthonormal1, flattened)); 
    
        return a; 
    } 
    

Ecco il codice per trovare le orthonormals però probabilmente si può fare molto meglio se desideri solo quello per il metodo di cui sopra:

private static Matrix OrthoX = Matrix.CreateRotationX(MathHelper.ToRadians(90)); 
private static Matrix OrthoY = Matrix.CreateRotationY(MathHelper.ToRadians(90)); 

public static void FindOrthonormals(Vector3 normal, out Vector3 orthonormal1, out Vector3 orthonormal2) 
{ 
    Vector3 w = Vector3.Transform(normal, OrthoX); 
    float dot = Vector3.Dot(normal, w); 
    if (Math.Abs(dot) > 0.6) 
    { 
     w = Vector3.Transform(normal, OrthoY); 
    } 
    w.Normalize(); 

    orthonormal1 = Vector3.Cross(normal, w); 
    orthonormal1.Normalize(); 
    orthonormal2 = Vector3.Cross(normal, orthonormal1); 
    orthonormal2.Normalize(); 
} 

Anche se quanto sopra esposto opere si possono trovare non si comporta come ci si aspetterebbe. Ad esempio, se il quaternione ruota un vettore di 90 gradi. intorno a X e 90 gradi. intorno a Y troverai se decomponi la rotazione attorno a Z sarà 90 gradi. anche. Se si immagina un vettore che esegue queste rotazioni, ciò ha perfettamente senso, ma a seconda dell'applicazione potrebbe non essere il comportamento desiderato. Per la mia applicazione - vincolare i giunti di scheletro - ho finito con un sistema ibrido. Matrici/Quat usati ovunque, ma quando si trattava del metodo per vincolare le giunzioni, ho usato angoli euler internamente, scomponendo il quat di rotazione a rotazioni attorno a X, Y, Z ogni volta.

Buona fortuna, spero che abbia aiutato.

+0

Sei una specie di genio! –

+0

Sono al lavoro al momento, ci proverò più tardi e se funziona sarò un po 'chuffato :) (pensato per metterlo su una riga separata nel commento precedente ma apparentemente "invio" significa "invia" in questa particolare casella di testo) –

+0

Grazie (anche se controlla la prima domanda che ho chiesto e la sua risposta e vedi se la pensi ancora così!;)). Nel caso in cui, se hai bisogno di scomporre un quaternion su eulers (il che rende molto più facile da ricreare), ed022 ha una buona implementazione qui: http://forums.create.msdn.com/forums/p/4574/62520 .aspx (17 ° post). Buona fortuna con la tua app! – sebf

0

ho cercato di implementare la risposta di sebf, sembra buono, tranne che la scelta della scelta del vettore al punto 1:

  1. Prendere l'asse che si desidera trovare la rotazione intorno, e trovare un vettore ortogonale ad esso.

non è sufficiente per risultati ripetibili. L'ho sviluppato su carta e suggerisco la seguente linea di condotta per la scelta del vettore ortogonale all '"asse in cui si desidera trovare la rotazione attorno", cioè l'asse di osservazione. Esiste un piano ortogonale all'asse di osservazione. Devi proiettare l'asse di rotazione del tuo quaternione su questo piano. L'uso di questo vettore risultante come il vettore ortogonale all'asse di osservazione darà buoni risultati.

Grazie a sebf per avermi impostato sulla giusta rotta.

+1

Se l'asse di osservazione è uguale all'asse di rotazione allora questo produrrebbe un vettore di lunghezza zero, quindi questo caso dovrebbe essere controllato. – angularsen

20

C'è una soluzione elegante per questo problema, particolarmente adatto per quaternioni.E 'noto come il "swing torsione di decomposizione":

in pseudocodice

/** 
    Decompose the rotation on to 2 parts. 
    1. Twist - rotation around the "direction" vector 
    2. Swing - rotation around axis that is perpendicular to "direction" vector 
    The rotation can be composed back by 
    rotation = swing * twist 

    has singularity in case of swing_rotation close to 180 degrees rotation. 
    if the input quaternion is of non-unit length, the outputs are non-unit as well 
    otherwise, outputs are both unit 
*/ 
inline void swing_twist_decomposition(const xxquaternion& rotation, 
             const vector3&  direction, 
             xxquaternion&  swing, 
             xxquaternion&  twist) 
{ 
    vector3 ra(rotation.x, rotation.y, rotation.z); // rotation axis 
    vector3 p = projection(ra, direction); // return projection v1 on to v2 (parallel component) 
    twist.set(p.x, p.y, p.z, rotation.w); 
    twist.normalize(); 
    swing = rotation * twist.conjugated(); 
} 

E la risposta lunga e derivazione di questo codice può essere trovato qui http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/

+0

Questa sembra una soluzione molto migliore con un minor numero di edge case e molto più efficiente ... Mi piace! Quando avrò tempo, mi confronterò con la risposta che ho accettato. –

+1

Si prega di notare che la carta "DECOMPOSIZIONE DI ALTRO-TWIST IN CLIFFORD ALGEBRA" con derivazione generale – minorlogic

+1

@minorlogic sembra ottima, ma come affrontiamo la singolarità? Probabilmente fraintendo qualcosa, ma se questa soluzione si rompe una piccola percentuale del tempo, allora sicuramente non è molto utile? – JoeRocc

Problemi correlati