I'm trying to rotate a quaternion and convert it to Euler angle(for CSS) in javascript. Here is my general idea:
①Get quaternion in the current state.
②Calculate the rotate axis vector in local coordinate(e.g. for z-axis in local coordinate, just multiply [0, 0, 1] by ①).
③Substitute vector v
from ② and rotate angle θ
into = [cos(θ), sin(θ)v]
④Multiply ① by ③ and that should be the new rotated quaternion.
function rotate( axisVector, rotateDegree ) {
axisVector = getRotatedVectorQuaternion( ...axisVector );
const roamRotateQuaternion = [
Math.cos( rotateDegree / 2 ),
Math.sin( rotateDegree / 2 ) * axisVector[ 1 ],
Math.sin( rotateDegree / 2 ) * axisVector[ 2 ],
Math.sin( rotateDegree / 2 ) * axisVector[ 3 ]
];
const roamedEuler = eulerFromQuaternion( multiplyQuaternions( getPresentQuaternion(), roamRotateQuaternion ), ( presentStep.dataset.rotateOrder || "xyz" ).toUpperCase() );
presentStep.dataset.rotateX = roamedEuler[0] * 180 / Math.PI;
presentStep.dataset.rotateY = roamedEuler[1] * 180 / Math.PI;
presentStep.dataset.rotateZ = roamedEuler[2] * 180 / Math.PI;
}
function multiplyQuaternions( qa, qb ) {
const qaw = qa[ 0 ], qax = qa[ 1 ], qay = qa[ 2 ], qaz = qa[ 3 ];
const qbw = qb[ 0 ], qbx = qb[ 1 ], qby = qb[ 2 ], qbz = qb[ 3 ];
return [
qaw * qbw - qax * qbx - qay * qby - qaz * qbz,
qax * qbw + qaw * qbx + qay * qbz - qaz * qby,
qay * qbw + qaw * qby + qaz * qbx - qax * qbz,
qaz * qbw + qaw * qbz + qax * qby - qay * qbx
];
}
function getRotatedVectorQuaternion( ...dirVec ) {
var vectorQuaternion = [
0,
...dirVec
];
const rotateQuaternion = getPresentQuaternion();
const rotateConjugate = [
rotateQuaternion[ 0 ],
rotateQuaternion[ 1 ] * -1,
rotateQuaternion[ 2 ] * -1,
rotateQuaternion[ 3 ] * -1
];
return multiplyQuaternions( multiplyQuaternions( rotateQuaternion, vectorQuaternion ), rotateConjugate );
}
function eulerFromQuaternion( quaternion, order ) {
// Quaternion to matrix.
const w = quaternion[0], x = quaternion[1], y = quaternion[2], z = quaternion[3];
const x2 = x + x, y2 = y + y, z2 = z + z;
const xx = x * x2, xy = x * y2, xz = x * z2;
const yy = y * y2, yz = y * z2, zz = z * z2;
const wx = w * x2, wy = w * y2, wz = w * z2;
const matrix = [
1 - ( yy + zz ),
xy + wz,
xz - wy,
0,
xy - wz,
1 - ( xx + zz ),
yz + wx,
0,
xz + wy,
yz - wx,
1 - ( xx + yy ),
0,
0,
0,
0,
1
];
// Matrix to euler
function clamp( value, min, max ) {
return Math.max( min, Math.min( max, value ) );
}
const m11 = matrix[ 0 ], m12 = matrix[ 4 ], m13 = matrix[ 8 ];
const m21 = matrix[ 1 ], m22 = matrix[ 5 ], m23 = matrix[ 9 ];
const m31 = matrix[ 2 ], m32 = matrix[ 6 ], m33 = matrix[ 10 ];
var euler = [ 0, 0, 0 ];
switch ( order ) {
case "XYZ":
euler[1] = Math.asin( clamp( m13, - 1, 1 ) );
if ( Math.abs( m13 ) < 0.9999999 ) {
euler[0] = Math.atan2( - m23, m33 );
euler[2] = Math.atan2( - m12, m11 );
} else {
euler[0] = Math.atan2( m32, m22 );
euler[2] = 0;
}
break;
case "YXZ":
euler[0] = Math.asin( - clamp( m23, - 1, 1 ) );
if ( Math.abs( m23 ) < 0.9999999 ) {
euler[1] = Math.atan2( m13, m33 );
euler[2] = Math.atan2( m21, m22 );
} else {
euler[1] = Math.atan2( - m31, m11 );
euler[2] = 0;
}
break;
case "ZXY":
euler[0] = Math.asin( clamp( m32, - 1, 1 ) );
if ( Math.abs( m32 ) < 0.9999999 ) {
euler[1] = Math.atan2( - m31, m33 );
euler[2] = Math.atan2( - m12, m22 );
} else {
euler[1] = 0;
euler[2] = Math.atan2( m21, m11 );
}
break;
case "ZYX":
euler[1] = Math.asin( - clamp( m31, - 1, 1 ) );
if ( Math.abs( m31 ) < 0.9999999 ) {
euler[0] = Math.atan2( m32, m33 );
euler[2] = Math.atan2( m21, m11 );
} else {
euler[0] = 0;
euler[2] = Math.atan2( - m12, m22 );
}
break;
case "YZX":
euler[2] = Math.asin( clamp( m21, - 1, 1 ) );
if ( Math.abs( m21 ) < 0.9999999 ) {
euler[0] = Math.atan2( - m23, m22 );
euler[1] = Math.atan2( - m31, m11 );
} else {
euler[0] = 0;
euler[1] = Math.atan2( m13, m33 );
}
break;
case "XZY":
euler[2] = Math.asin( - clamp( m12, - 1, 1 ) );
if ( Math.abs( m12 ) < 0.9999999 ) {
euler[0] = Math.atan2( m32, m22 );
euler[1] = Math.atan2( m13, m11 );
} else {
euler[0] = Math.atan2( - m23, m33 );
euler[1] = 0;
}
break;
}
return euler;
}
But it rotates weirdly. I am searching for a long time on the net and also tried the slerp
function. But no use. Please help or try to give some ideas on how to achieve this.