1

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 Answer 1

2

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;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the suggestion. I believe this works for the most part. However, once I add the coordinate system transforms to apply this as a global transform, there is some strange behavior. I've updated my post with a fiddle to illustrate.
@Carpetfizz it looks like changing object.localToWorld(object.position) to object.parent.localToWorld(object.position) and object.worldToLocal(object.position) to object.parent.worldToLocal(object.position) fixes this issue.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.