import {Vec3} from './vec3.js';
let tmpvec = new Vec3();
let xvec = new Vec3(1,0,0);
let yvec = new Vec3(0,1,0);
/**
* Quaternion rotation
*/
export class Quat{
/**
* Creates a new identity quaternion.
*/
constructor(){
this.data = new Float32Array([0,0,0,1]);
}
/**
* sets this quaternion from axis and angle.
* @param {Vec3} axis axis of rotation
* @param {Number} angle angle in radians
*/
setAxisAngle(axis, angle){
angle = angle * 0.5;
let s = Math.sin(angle);
this.data[0] = s * axis.data[0];
this.data[1] = s * axis.data[1];
this.data[2] = s * axis.data[2];
this.data[3] = Math.cos(angle);
}
/**
* Calculates this quaternion from euler angles.
* @param {Number} x
* @param {Number} y
* @param {Number} z
*/
setEuler( x,y,z ){
let halfToRad = 0.5 * Math.PI / 180.0;
x *= halfToRad;
y *= halfToRad;
z *= halfToRad;
let sx = Math.sin(x);
let cx = Math.cos(x);
let sy = Math.sin(y);
let cy = Math.cos(y);
let sz = Math.sin(z);
let cz = Math.cos(z);
this.data[0] = sx * cy * cz - cx * sy * sz;
this.data[1] = cx * sy * cz + sx * cy * sz;
this.data[2] = cx * cy * sz - sx * sy * cz;
this.data[3] = cx * cy * cz + sx * sy * sz;
}
/**
* Multiplies this quaternion with another.
* @param {Quat} other quaternion.
*/
multiply(other){
let ax = this.data[0], ay = this.data[1], az = this.data[2], aw = this.data[3];
let bx = other.data[0], by = other.data[1], bz = other.data[2], bw = other.data[3];
this.data[0] = ax * bw + aw * bx + ay * bz - az * by;
this.data[1] = ay * bw + aw * by + az * bx - ax * bz;
this.data[2] = az * bw + aw * bz + ax * by - ay * bx;
this.data[3] = aw * bw - ax * bx - ay * by - az * bz;
}
/**
* Rotate this quaternion on the X axis
* @param {Number} rad radians to rotate by
*/
rotateX(rad) {
rad *= 0.5;
let ax = this.data[0], ay = this.data[1], az = this.data[2], aw = this.data[3];
let bx = Math.sin(rad), bw = Math.cos(rad);
this.data[0] = ax * bw + aw * bx;
this.data[1] = ay * bw + az * bx;
this.data[2] = az * bw - ay * bx;
this.data[3] = aw * bw - ax * bx;
}
/**
* Rotate this quaternion on the Y axis
* @param {Number} rad radians to rotate by
*/
rotateY(rad){
rad *= 0.5;
let ax = this.data[0], ay = this.data[1], az = this.data[2], aw = this.data[3];
let by = Math.sin(rad), bw = Math.cos(rad);
this.data[0] = ax * bw - az * by;
this.data[1] = ay * bw + aw * by;
this.data[2] = az * bw + ax * by;
this.data[3] = aw * bw - ay * by;
}
/**
* Rotate this quaternion on the Z axis
* @param {Number} rad radians to rotate by
*/
rotateZ(rad){
rad *= 0.5;
let ax = this.data[0], ay = this.data[1], az = this.data[2], aw = this.data[3];
let bz = Math.sin(rad), bw = Math.cos(rad);
this.data[0] = ax * bw + ay * bz;
this.data[1] = ay * bw - ax * bz;
this.data[2] = az * bw + aw * bz;
this.data[3] = aw * bw - az * bz;
}
rotationTo( v0, v1 ){
let dot = Vec3.dot(v0, v1);
if (dot < -0.999999) {
tmpvec.cross( xvec , v0);
if (tmpvec.length() < 0.000001)
tmpvec.cross( yvec, v0 );
tmpvec.normalize();
this.setAxisAngle(tmpvec, Math.PI);
} else if (dot > 0.999999) {
this.data[0] = 0;
this.data[1] = 0;
this.data[2] = 0;
this.data[3] = 1;
} else {
v0.cross(v1);
this.data[0] = v0.data[0];
this.data[1] = v0.data[1];
this.data[2] = v0.data[2];
this.data[3] = 1 + dot;
this.normalize();
}
}
length(){
let l = this.data[0]*this.data[0] + this.data[1]*this.data[1] + this.data[2]*this.data[2] + this.data[3]*this.data[3];
if(l > 0){
return Math.sqrt(l);
}else{
return 0;
}
}
normalize(){
let l = this.data[0]*this.data[0] + this.data[1]*this.data[1] + this.data[2]*this.data[2] + this.data[3]*this.data[3];
if(l > 0){
l = 1 / Math.sqrt(l);
this.data[0] *= l;
this.data[1] *= l;
this.data[2] *= l;
this.data[3] *= l;
}
}
}