matrix3d
Version:
Decompose, interpolate, compose 3d matrices.
151 lines (119 loc) • 4.17 kB
JavaScript
/*
Vector4
This is mostly from three.js / euclideanspace.com / transform_util source code.
*/ 'use strict';
var prime = require('prime');
var degToRad = function(degrees) {
return degrees * Math.PI / 180;
};
var radToDeg = function(radians) {
return radians * 180 / Math.PI;
};
var Vector4 = prime({
constructor: function Vector4(x, y, z, w) {
this[0] = x || 0;
this[1] = y || 0;
this[2] = z || 0;
this[3] = w || 0;
},
clone: function() {
return new Vector4(this[0], this[1], this[2], this[3]);
},
get x() {
return this[0];
},
get y() {
return this[1];
},
get z() {
return this[2];
},
get w() {
return this[3];
},
equals: function(v3) {
return this[0] === v3[0] && this[1] === v3[1] && this[2] === v3[2] && this[3] === v3[3];
},
// three.js
length: function() {
return Math.sqrt(this[0] * this[0] + this[1] * this[1] + this[2] * this[2] + this[3] * this[3]);
},
// three.js
dot: function(v4) {
return this[0] * v4[0] + this[1] * v4[1] + this[2] * v4[2] + this[3] * v4[3];
},
// three.js
normalize: function() {
var length = this.length();
if (length === 0) return new Vector4(0, 0, 0, 1);
var inv = 1 / length;
return new Vector4(this[0] * inv, this[1] * inv, this[2] * inv, this[3] * inv);
},
// three.js
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
// deg indicates you want the angle in degrees in the resulting Vector4
quaternionToAngle: function(deg) {
var v4 = this;
// if w>1 acos and sqrt will produce errors, this cant happen if quaternion is normalised
if (v4[3] > 1) v4 = v4.normalize();
var w = 2 * Math.acos(v4[3]);
var s = Math.sqrt(1 - v4[3] * v4[3]); // assuming quaternion normalised then w is less than 1, so term always positive.
if (s < 1e-4) { // test to avoid divide by zero, s is always positive due to sqrt
// if s close to zero then direction of axis not important
return new Vector4(v4[0], v4[1], v4[2], w);
} else {
// normalise axis
return new Vector4(v4[0] / s, v4[1] / s, v4[2] / s, deg ? radToDeg(w) : w);
}
},
// three.js
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
// deg indicates the Vector4 contains the angle in degrees
angleToQuaternion: function(deg) {
var angle = deg ? degToRad(this[3]) : this[3];
var half = angle / 2, s = Math.sin(half);
return new Vector4(this[0] * s, this[1] * s, this[2] * s, Math.cos(half));
},
// transform_util
combine: function(v4, scale1, scale2) {
var result = new Vector4;
for (var i = 0; i < 4; i++) result[i] = this[i] * scale1 + v4[i] * scale2;
return result;
},
lerp: function (v4, delta) {
var scale1 = delta;
var scale2 = 1 - delta;
return v4.combine(this, scale1, scale2);
},
// transform_util
slerp: function(v4q, delta) {
var interpolated = new Vector4;
var product = this.dot(v4q);
// Clamp product to -1.0 <= product <= 1.0.
product = Math.min(Math.max(product, -1), 1);
// Interpolate angles along the shortest path. For example, to interpolate
// between a 175 degree angle and a 185 degree angle, interpolate along the
// 10 degree path from 175 to 185, rather than along the 350 degree path in
// the opposite direction. This matches WebKit's implementation but not
// the current W3C spec. Fixing the spec to match this approach is discussed
// at:
// http://lists.w3.org/Archives/Public/www-style/2013May/0131.html
var scale1 = 1;
if (product < 0) {
product = -product;
scale1 = -1.0;
}
var epsilon = 1e-5;
if (Math.abs(product - 1.0) < epsilon) {
for (var i = 0; i < 4; ++i) interpolated[i] = this[i];
return interpolated;
}
var denom = Math.sqrt(1 - product * product);
var theta = Math.acos(product);
var w = Math.sin(delta * theta) * (1 / denom);
scale1 *= Math.cos(delta * theta) - product * w;
var scale2 = w;
return this.combine(v4q, scale1, scale2);
}
});
module.exports = Vector4;