0
votes

I would like to set the absolute global rotation of a vector using a quaternion.

Let's say I have a function f(v, q) which rotates vector v to quaternion q.

THREE.Vector3 has a .applyQuaternion function which applies a rotation to a vector, but this is a relative rotation. For example, the function call f(f(v, q), q) will apply q twice. Instead, I would expect f(f(v, q), q) = f(v, q) since q is the absolute rotation of the vector.

I know that I can compute some deltaQ = currentQ.premultiply(q.inverse()) and then call .applyQuaternion, but I don't know how to query the currentQ, or the current rotation of the vector, so that I can "undo" it and apply the new quaternion.

Thanks for any help!

EDIT:

I would ideally like to rotate about a point defined in world coordinate space, which is why I have the object.worldToLocal and object.localToWorld calls. However, the orbiting object flies off the map when I make these conversions.

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
/* const orbitControls = new THREE.OrbitControls(camera, renderer.domElement); */

// CONSTRUCT ORBITER
const orbitGeometry = new THREE.SphereGeometry(0.1, 32, 32);
const orbitMaterial = new THREE.MeshBasicMaterial({colors: 0xff0000});
const orbiter = new THREE.Mesh(orbitGeometry, orbitMaterial);

// CONSTRUCT GLOBAL AXES
const xDir = new THREE.Vector3(1, 0, 0);
const yDir = new THREE.Vector3(0, 1, 0);
const zDir = new THREE.Vector3(0, 0, 1);
const origin = new THREE.Vector3(0, 0, 0);
const xArrowWorld = new THREE.ArrowHelper(xDir, origin, 3, 0xFF00FF);
const yArrowWorld = new THREE.ArrowHelper(yDir, origin, 3, 0xffff00);
const zArrowWorld = new THREE.ArrowHelper(zDir, origin, 3, 0x00b7eb);
const worldAxes = new THREE.Group();
worldAxes.add(xArrowWorld);
worldAxes.add(yArrowWorld);
worldAxes.add(zArrowWorld);


// CONSTRUCT MARKER
const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32);
const sphereMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});
const marker = new THREE.Mesh(sphereGeometry, sphereMaterial);

// scene.add(axes);
scene.add(marker);
scene.add(orbiter);
scene.add(worldAxes);

camera.position.z = 5;
camera.position.y = 3;
camera.position.x = 3;
camera.lookAt(worldAxes.position);

marker.position.set(1, 0, 0);

function rotateEuler(object, point, eulerX, eulerY, eulerZ, isExtrinsic = false) {
    if (isExtrinsic) {
        object.rotation.order = 'ZYX';
    } else {
        object.rotation.order = 'XYZ';
    }
    const q = new THREE.Quaternion();
    const eul = new THREE.Euler(eulerX, eulerY, eulerZ, object.rotation.order);
    q.setFromEuler(eul);
    rotateQuaternionAroundPoint(object, point, q);
}

function rotateQuaternionAroundPoint(object, point, q) {

    object.rotation.set(0, 0, 0)
    object.position.set(0, 0, 0);

    object.localToWorld(object.position);
    object.position.sub(point);
    object.position.applyQuaternion(q);
    object.position.add(point);
    object.worldToLocal(object.position);

}

let rotY = 0;
let rotationPoint = marker.localToWorld(marker.position.clone());

function animate() {
    requestAnimationFrame( animate );

    rotY += 0.01;

    rotateEuler(orbiter, rotationPoint, 0, rotY, 0, true);

    renderer.render( scene, camera );
}

animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
1

1 Answers

1
votes

The easiest way to do this is to reset the Vector3 to its initial state, then apply the quaternion to this reset vector. This erases any previous rotation from the equation. For instance, assuming the default state of the vector is pointing 'UP':

function f(v, q) {
    v.set(0, 1, 0);
    v.applyQuaternion(q);
    return v;
}