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>