import vec3 from '../glmatrix/vec3';
/**
* @constructor
* @alias clay.Vector3
* @param {number} x
* @param {number} y
* @param {number} z
*/
var Vector3 = function(x, y, z) {
x = x || 0;
y = y || 0;
z = z || 0;
/**
* Storage of Vector3, read and write of x, y, z will change the values in array
* All methods also operate on the array instead of x, y, z components
* @name array
* @type {Float32Array}
* @memberOf clay.Vector3#
*/
this.array = vec3.fromValues(x, y, z);
/**
* Dirty flag is used by the Node to determine
* if the matrix is updated to latest
* @name _dirty
* @type {boolean}
* @memberOf clay.Vector3#
*/
this._dirty = true;
};
Vector3.prototype = {
constructor: Vector3,
/**
* Add b to self
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
add: function (b) {
vec3.add(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Set x, y and z components
* @param {number} x
* @param {number} y
* @param {number} z
* @return {clay.Vector3}
*/
set: function (x, y, z) {
this.array[0] = x;
this.array[1] = y;
this.array[2] = z;
this._dirty = true;
return this;
},
/**
* Set x, y and z components from array
* @param {Float32Array|number[]} arr
* @return {clay.Vector3}
*/
setArray: function (arr) {
this.array[0] = arr[0];
this.array[1] = arr[1];
this.array[2] = arr[2];
this._dirty = true;
return this;
},
/**
* Clone a new Vector3
* @return {clay.Vector3}
*/
clone: function () {
return new Vector3(this.x, this.y, this.z);
},
/**
* Copy from b
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
copy: function (b) {
vec3.copy(this.array, b.array);
this._dirty = true;
return this;
},
/**
* Cross product of self and b, written to a Vector3 out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
cross: function (a, b) {
vec3.cross(this.array, a.array, b.array);
this._dirty = true;
return this;
},
/**
* Alias for distance
* @param {clay.Vector3} b
* @return {number}
*/
dist: function (b) {
return vec3.dist(this.array, b.array);
},
/**
* Distance between self and b
* @param {clay.Vector3} b
* @return {number}
*/
distance: function (b) {
return vec3.distance(this.array, b.array);
},
/**
* Alias for divide
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
div: function (b) {
vec3.div(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Divide self by b
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
divide: function (b) {
vec3.divide(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Dot product of self and b
* @param {clay.Vector3} b
* @return {number}
*/
dot: function (b) {
return vec3.dot(this.array, b.array);
},
/**
* Alias of length
* @return {number}
*/
len: function () {
return vec3.len(this.array);
},
/**
* Calculate the length
* @return {number}
*/
length: function () {
return vec3.length(this.array);
},
/**
* Linear interpolation between a and b
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @param {number} t
* @return {clay.Vector3}
*/
lerp: function (a, b, t) {
vec3.lerp(this.array, a.array, b.array, t);
this._dirty = true;
return this;
},
/**
* Minimum of self and b
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
min: function (b) {
vec3.min(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Maximum of self and b
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
max: function (b) {
vec3.max(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Alias for multiply
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
mul: function (b) {
vec3.mul(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Mutiply self and b
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
multiply: function (b) {
vec3.multiply(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Negate self
* @return {clay.Vector3}
*/
negate: function () {
vec3.negate(this.array, this.array);
this._dirty = true;
return this;
},
/**
* Normalize self
* @return {clay.Vector3}
*/
normalize: function () {
vec3.normalize(this.array, this.array);
this._dirty = true;
return this;
},
/**
* Generate random x, y, z components with a given scale
* @param {number} scale
* @return {clay.Vector3}
*/
random: function (scale) {
vec3.random(this.array, scale);
this._dirty = true;
return this;
},
/**
* Scale self
* @param {number} scale
* @return {clay.Vector3}
*/
scale: function (s) {
vec3.scale(this.array, this.array, s);
this._dirty = true;
return this;
},
/**
* Scale b and add to self
* @param {clay.Vector3} b
* @param {number} scale
* @return {clay.Vector3}
*/
scaleAndAdd: function (b, s) {
vec3.scaleAndAdd(this.array, this.array, b.array, s);
this._dirty = true;
return this;
},
/**
* Alias for squaredDistance
* @param {clay.Vector3} b
* @return {number}
*/
sqrDist: function (b) {
return vec3.sqrDist(this.array, b.array);
},
/**
* Squared distance between self and b
* @param {clay.Vector3} b
* @return {number}
*/
squaredDistance: function (b) {
return vec3.squaredDistance(this.array, b.array);
},
/**
* Alias for squaredLength
* @return {number}
*/
sqrLen: function () {
return vec3.sqrLen(this.array);
},
/**
* Squared length of self
* @return {number}
*/
squaredLength: function () {
return vec3.squaredLength(this.array);
},
/**
* Alias for subtract
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
sub: function (b) {
vec3.sub(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Subtract b from self
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
subtract: function (b) {
vec3.subtract(this.array, this.array, b.array);
this._dirty = true;
return this;
},
/**
* Transform self with a Matrix3 m
* @param {clay.Matrix3} m
* @return {clay.Vector3}
*/
transformMat3: function (m) {
vec3.transformMat3(this.array, this.array, m.array);
this._dirty = true;
return this;
},
/**
* Transform self with a Matrix4 m
* @param {clay.Matrix4} m
* @return {clay.Vector3}
*/
transformMat4: function (m) {
vec3.transformMat4(this.array, this.array, m.array);
this._dirty = true;
return this;
},
/**
* Transform self with a Quaternion q
* @param {clay.Quaternion} q
* @return {clay.Vector3}
*/
transformQuat: function (q) {
vec3.transformQuat(this.array, this.array, q.array);
this._dirty = true;
return this;
},
/**
* Trasnform self into projection space with m
* @param {clay.Matrix4} m
* @return {clay.Vector3}
*/
applyProjection: function (m) {
var v = this.array;
m = m.array;
// Perspective projection
if (m[15] === 0) {
var w = -1 / v[2];
v[0] = m[0] * v[0] * w;
v[1] = m[5] * v[1] * w;
v[2] = (m[10] * v[2] + m[14]) * w;
}
else {
v[0] = m[0] * v[0] + m[12];
v[1] = m[5] * v[1] + m[13];
v[2] = m[10] * v[2] + m[14];
}
this._dirty = true;
return this;
},
eulerFromQuat: function(q, order) {
Vector3.eulerFromQuat(this, q, order);
},
eulerFromMat3: function (m, order) {
Vector3.eulerFromMat3(this, m, order);
},
toString: function() {
return '[' + Array.prototype.join.call(this.array, ',') + ']';
},
toArray: function () {
return Array.prototype.slice.call(this.array);
}
};
var defineProperty = Object.defineProperty;
// Getter and Setter
if (defineProperty) {
var proto = Vector3.prototype;
/**
* @name x
* @type {number}
* @memberOf clay.Vector3
* @instance
*/
defineProperty(proto, 'x', {
get: function () {
return this.array[0];
},
set: function (value) {
this.array[0] = value;
this._dirty = true;
}
});
/**
* @name y
* @type {number}
* @memberOf clay.Vector3
* @instance
*/
defineProperty(proto, 'y', {
get: function () {
return this.array[1];
},
set: function (value) {
this.array[1] = value;
this._dirty = true;
}
});
/**
* @name z
* @type {number}
* @memberOf clay.Vector3
* @instance
*/
defineProperty(proto, 'z', {
get: function () {
return this.array[2];
},
set: function (value) {
this.array[2] = value;
this._dirty = true;
}
});
}
// Supply methods that are not in place
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.add = function(out, a, b) {
vec3.add(out.array, a.array, b.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {number} x
* @param {number} y
* @param {number} z
* @return {clay.Vector3}
*/
Vector3.set = function(out, x, y, z) {
vec3.set(out.array, x, y, z);
out._dirty = true;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.copy = function(out, b) {
vec3.copy(out.array, b.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.cross = function(out, a, b) {
vec3.cross(out.array, a.array, b.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {number}
*/
Vector3.dist = function(a, b) {
return vec3.distance(a.array, b.array);
};
/**
* @function
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {number}
*/
Vector3.distance = Vector3.dist;
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.div = function(out, a, b) {
vec3.divide(out.array, a.array, b.array);
out._dirty = true;
return out;
};
/**
* @function
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.divide = Vector3.div;
/**
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {number}
*/
Vector3.dot = function(a, b) {
return vec3.dot(a.array, b.array);
};
/**
* @param {clay.Vector3} a
* @return {number}
*/
Vector3.len = function(b) {
return vec3.length(b.array);
};
// Vector3.length = Vector3.len;
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @param {number} t
* @return {clay.Vector3}
*/
Vector3.lerp = function(out, a, b, t) {
vec3.lerp(out.array, a.array, b.array, t);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.min = function(out, a, b) {
vec3.min(out.array, a.array, b.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.max = function(out, a, b) {
vec3.max(out.array, a.array, b.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.mul = function(out, a, b) {
vec3.multiply(out.array, a.array, b.array);
out._dirty = true;
return out;
};
/**
* @function
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.multiply = Vector3.mul;
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @return {clay.Vector3}
*/
Vector3.negate = function(out, a) {
vec3.negate(out.array, a.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @return {clay.Vector3}
*/
Vector3.normalize = function(out, a) {
vec3.normalize(out.array, a.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {number} scale
* @return {clay.Vector3}
*/
Vector3.random = function(out, scale) {
vec3.random(out.array, scale);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {number} scale
* @return {clay.Vector3}
*/
Vector3.scale = function(out, a, scale) {
vec3.scale(out.array, a.array, scale);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @param {number} scale
* @return {clay.Vector3}
*/
Vector3.scaleAndAdd = function(out, a, b, scale) {
vec3.scaleAndAdd(out.array, a.array, b.array, scale);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {number}
*/
Vector3.sqrDist = function(a, b) {
return vec3.sqrDist(a.array, b.array);
};
/**
* @function
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {number}
*/
Vector3.squaredDistance = Vector3.sqrDist;
/**
* @param {clay.Vector3} a
* @return {number}
*/
Vector3.sqrLen = function(a) {
return vec3.sqrLen(a.array);
};
/**
* @function
* @param {clay.Vector3} a
* @return {number}
*/
Vector3.squaredLength = Vector3.sqrLen;
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.sub = function(out, a, b) {
vec3.subtract(out.array, a.array, b.array);
out._dirty = true;
return out;
};
/**
* @function
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Vector3} b
* @return {clay.Vector3}
*/
Vector3.subtract = Vector3.sub;
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {Matrix3} m
* @return {clay.Vector3}
*/
Vector3.transformMat3 = function(out, a, m) {
vec3.transformMat3(out.array, a.array, m.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Matrix4} m
* @return {clay.Vector3}
*/
Vector3.transformMat4 = function(out, a, m) {
vec3.transformMat4(out.array, a.array, m.array);
out._dirty = true;
return out;
};
/**
* @param {clay.Vector3} out
* @param {clay.Vector3} a
* @param {clay.Quaternion} q
* @return {clay.Vector3}
*/
Vector3.transformQuat = function(out, a, q) {
vec3.transformQuat(out.array, a.array, q.array);
out._dirty = true;
return out;
};
function clamp(val, min, max) {
return val < min ? min : (val > max ? max : val);
}
var atan2 = Math.atan2;
var asin = Math.asin;
var abs = Math.abs;
/**
* Convert quaternion to euler angle
* Quaternion must be normalized
* From three.js
*/
Vector3.eulerFromQuat = function (out, q, order) {
out._dirty = true;
q = q.array;
var target = out.array;
var x = q[0], y = q[1], z = q[2], w = q[3];
var x2 = x * x;
var y2 = y * y;
var z2 = z * z;
var w2 = w * w;
var order = (order || 'XYZ').toUpperCase();
switch (order) {
case 'XYZ':
target[0] = atan2(2 * (x * w - y * z), (w2 - x2 - y2 + z2));
target[1] = asin(clamp(2 * (x * z + y * w), - 1, 1));
target[2] = atan2(2 * (z * w - x * y), (w2 + x2 - y2 - z2));
break;
case 'YXZ':
target[0] = asin(clamp(2 * (x * w - y * z), - 1, 1));
target[1] = atan2(2 * (x * z + y * w), (w2 - x2 - y2 + z2));
target[2] = atan2(2 * (x * y + z * w), (w2 - x2 + y2 - z2));
break;
case 'ZXY':
target[0] = asin(clamp(2 * (x * w + y * z), - 1, 1));
target[1] = atan2(2 * (y * w - z * x), (w2 - x2 - y2 + z2));
target[2] = atan2(2 * (z * w - x * y), (w2 - x2 + y2 - z2));
break;
case 'ZYX':
target[0] = atan2(2 * (x * w + z * y), (w2 - x2 - y2 + z2));
target[1] = asin(clamp(2 * (y * w - x * z), - 1, 1));
target[2] = atan2(2 * (x * y + z * w), (w2 + x2 - y2 - z2));
break;
case 'YZX':
target[0] = atan2(2 * (x * w - z * y), (w2 - x2 + y2 - z2));
target[1] = atan2(2 * (y * w - x * z), (w2 + x2 - y2 - z2));
target[2] = asin(clamp(2 * (x * y + z * w), - 1, 1));
break;
case 'XZY':
target[0] = atan2(2 * (x * w + y * z), (w2 - x2 + y2 - z2));
target[1] = atan2(2 * (x * z + y * w), (w2 + x2 - y2 - z2));
target[2] = asin(clamp(2 * (z * w - x * y), - 1, 1));
break;
default:
console.warn('Unkown order: ' + order);
}
return out;
};
/**
* Convert rotation matrix to euler angle
* from three.js
*/
Vector3.eulerFromMat3 = function (out, m, order) {
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
var te = m.array;
var m11 = te[0], m12 = te[3], m13 = te[6];
var m21 = te[1], m22 = te[4], m23 = te[7];
var m31 = te[2], m32 = te[5], m33 = te[8];
var target = out.array;
var order = (order || 'XYZ').toUpperCase();
switch (order) {
case 'XYZ':
target[1] = asin(clamp(m13, -1, 1));
if (abs(m13) < 0.99999) {
target[0] = atan2(-m23, m33);
target[2] = atan2(-m12, m11);
}
else {
target[0] = atan2(m32, m22);
target[2] = 0;
}
break;
case 'YXZ':
target[0] = asin(-clamp(m23, -1, 1));
if (abs(m23) < 0.99999) {
target[1] = atan2(m13, m33);
target[2] = atan2(m21, m22);
}
else {
target[1] = atan2(-m31, m11);
target[2] = 0;
}
break;
case 'ZXY':
target[0] = asin(clamp(m32, -1, 1));
if (abs(m32) < 0.99999) {
target[1] = atan2(-m31, m33);
target[2] = atan2(-m12, m22);
}
else {
target[1] = 0;
target[2] = atan2(m21, m11);
}
break;
case 'ZYX':
target[1] = asin(-clamp(m31, -1, 1));
if (abs(m31) < 0.99999) {
target[0] = atan2(m32, m33);
target[2] = atan2(m21, m11);
}
else {
target[0] = 0;
target[2] = atan2(-m12, m22);
}
break;
case 'YZX':
target[2] = asin(clamp(m21, -1, 1));
if (abs(m21) < 0.99999) {
target[0] = atan2(-m23, m22);
target[1] = atan2(-m31, m11);
}
else {
target[0] = 0;
target[1] = atan2(m13, m33);
}
break;
case 'XZY':
target[2] = asin(-clamp(m12, -1, 1));
if (abs(m12) < 0.99999) {
target[0] = atan2(m32, m22);
target[1] = atan2(m13, m11);
}
else {
target[0] = atan2(-m23, m33);
target[1] = 0;
}
break;
default:
console.warn('Unkown order: ' + order);
}
out._dirty = true;
return out;
};
Object.defineProperties(Vector3, {
/**
* @type {clay.Vector3}
* @readOnly
* @memberOf clay.Vector3
*/
POSITIVE_X: {
get: function () {
return new Vector3(1, 0, 0);
}
},
/**
* @type {clay.Vector3}
* @readOnly
* @memberOf clay.Vector3
*/
NEGATIVE_X: {
get: function () {
return new Vector3(-1, 0, 0);
}
},
/**
* @type {clay.Vector3}
* @readOnly
* @memberOf clay.Vector3
*/
POSITIVE_Y: {
get: function () {
return new Vector3(0, 1, 0);
}
},
/**
* @type {clay.Vector3}
* @readOnly
* @memberOf clay.Vector3
*/
NEGATIVE_Y: {
get: function () {
return new Vector3(0, -1, 0);
}
},
/**
* @type {clay.Vector3}
* @readOnly
* @memberOf clay.Vector3
*/
POSITIVE_Z: {
get: function () {
return new Vector3(0, 0, 1);
}
},
/**
* @type {clay.Vector3}
* @readOnly
*/
NEGATIVE_Z: {
get: function () {
return new Vector3(0, 0, -1);
}
},
/**
* @type {clay.Vector3}
* @readOnly
* @memberOf clay.Vector3
*/
UP: {
get: function () {
return new Vector3(0, 1, 0);
}
},
/**
* @type {clay.Vector3}
* @readOnly
* @memberOf clay.Vector3
*/
ZERO: {
get: function () {
return new Vector3();
}
}
});
export default Vector3;