Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Quaternion from two vector pairs

Tags:

quaternions

I have two vector pairs (before and after rotation).

before rotation: [x1,y1,z1] [x2,y2,z2]

after rotation: [x1',y1',z1'] [x2',y2',z2']

How to create a quaternion representing this rotation?

like image 605
Dorian Avatar asked Dec 30 '25 01:12

Dorian


2 Answers

In most cases there is no rotation which transforms 2 vectors into 2 other vectors. Here is a simple way to visualize why: a rotation does not change the angle between vectors. If the angle between the 2 vectors before the rotation is different from the angle between the 2 vectors after the rotation, then there is no rotation which meets your criteria.

This said there may be an optimal quaternion with an acceptable error which "almost" rotates your 2 vector pairs. There are a number of algorithms which vary in speed and precision to find such a quaternion. I wrote a fast C++ algorithm for an Arduino application where the speed is critical but the precision is less important.

http://robokitchen.tumblr.com/post/67060392720/finding-a-rotation-quaternion-from-two-pairs-of-vectors

Before rotation: u0, v0. After rotation: u2, v2.

Quaternion q2 = Quaternion::fromTwoVectors(u0, u2);
Vector v1 = v2.rotate(q2.conjugate());
Vector v0_proj = v0.projectPlane(u0);
Vector v1_proj = v1.projectPlane(u0);
Quaternion q1 = Quaternion::fromTwoVectors(v0_proj, v1_proj);
return (q2 * q1).normalized();

If this does not meet the requirements of your own application try to google Wabha's problem.

like image 61
marcv81 Avatar answered Jan 01 '26 21:01

marcv81


I translated marcv81's very helpful blog post into Three.js:

const rotateVectorsSimultaneously = (u0, v0, u2, v2) => {
    const q2 = new THREE.Quaternion().setFromUnitVectors(u0, u2);

    const v1 = v2.clone().applyQuaternion(q2.clone().conjugate());

    const v0_proj = v0.projectOnPlane(u0);
    const v1_proj = v1.projectOnPlane(u0);

    let angleInPlane = v0_proj.angleTo(v1_proj);
    if (v1_proj.dot(new THREE.Vector3().crossVectors(u0, v0)) < 0) {
        angleInPlane *= -1;
    }
    const q1 = new THREE.Quaternion().setFromAxisAngle(u0, angleInPlane);

    const q = new THREE.Quaternion().multiplyQuaternions(q2, q1);
    return q;
};

Because angleTo always returns a positive value, I manually flip the sign of the angle depending on which side of the u0-v0 plane v1 is on.

like image 33
chrmcg Avatar answered Jan 01 '26 22:01

chrmcg