<script src="https://cdn.rawgit.com/google/closure-library/master/closure/goog/base.js"></script>
<script src="http://numericjs.com/lib/numeric-1.2.6.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.1/gl-matrix-min.js"></script>
const defaultCount = 100000000;
const floatArray = new Float64Array(defaultCount);
// Vector and Matrix mathematics modules for JavaScript
// Copyright (c) 2007 James Coglan
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// Returns element i of the vector
return (i < 1 || i > this.elements.length) ? null : this.elements[i-1];
// Returns the number of elements the vector has
return this.elements.length;
// Returns the modulus ('length') of the vector
return Math.sqrt(this.dot(this));
// Returns true iff the vector is equal to the argument
var n = this.elements.length;
var V = vector.elements || vector;
if (n != V.length) { return false; }
if (Math.abs(this.elements[n-1] - V[n-1]) > Sylvester.precision) { return false; }
// Returns a copy of the vector
return Vector.create(this.elements);
// Maps the vector to another vector according to the given function
this.each(function(x, i) {
return Vector.create(elements);
// Calls the iterator for each element of the vector in turn
var n = this.elements.length, k = n, i;
fn(this.elements[i], i+1);
// Returns a new vector created by normalizing the receiver
toUnitVector: function() {
if (r === 0) { return this.dup(); }
return this.map(function(x) { return x/r; });
// Returns the angle between the vector and the argument (also a vector)
angleFrom: function(vector) {
var V = vector.elements || vector;
var n = this.elements.length, k = n, i;
if (n != V.length) { return null; }
var dot = 0, mod1 = 0, mod2 = 0;
// Work things out in parallel to save time
this.each(function(x, i) {
mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2);
if (mod1*mod2 === 0) { return null; }
var theta = dot / (mod1*mod2);
if (theta < -1) { theta = -1; }
if (theta > 1) { theta = 1; }
// Returns true iff the vector is parallel to the argument
isParallelTo: function(vector) {
var angle = this.angleFrom(vector);
return (angle === null) ? null : (angle <= Sylvester.precision);
// Returns true iff the vector is antiparallel to the argument
isAntiparallelTo: function(vector) {
var angle = this.angleFrom(vector);
return (angle === null) ? null : (Math.abs(angle - Math.PI) <= Sylvester.precision);
// Returns true iff the vector is perpendicular to the argument
isPerpendicularTo: function(vector) {
var dot = this.dot(vector);
return (dot === null) ? null : (Math.abs(dot) <= Sylvester.precision);
// Returns the result of adding the argument to the vector
var V = vector.elements || vector;
if (this.elements.length != V.length) { return null; }
return this.map(function(x, i) { return x + V[i-1]; });
// Returns the result of subtracting the argument from the vector
subtract: function(vector) {
var V = vector.elements || vector;
if (this.elements.length != V.length) { return null; }
return this.map(function(x, i) { return x - V[i-1]; });
// Returns the result of multiplying the elements of the vector by the argument
return this.map(function(x) { return x*k; });
x: function(k) { return this.multiply(k); },
// Returns the scalar product of the vector with the argument
// Both vectors must have equal dimensionality
var V = vector.elements || vector;
var i, product = 0, n = this.elements.length;
if (n != V.length) { return null; }
do { product += this.elements[n-1] * V[n-1]; } while (--n);
// Returns the vector product of the vector with the argument
// Both vectors must have dimensionality 3
cross: function(vector) {
var B = vector.elements || vector;
if (this.elements.length != 3 || B.length != 3) { return null; }
(A[1] * B[2]) - (A[2] * B[1]),
(A[2] * B[0]) - (A[0] * B[2]),
(A[0] * B[1]) - (A[1] * B[0])
// Returns the (absolute) largest element of the vector
var m = 0, n = this.elements.length, k = n, i;
if (Math.abs(this.elements[i]) > Math.abs(m)) { m = this.elements[i]; }
// Returns the index of the first match found
var index = null, n = this.elements.length, k = n, i;
if (index === null && this.elements[i] == x) {
// Returns a diagonal matrix with the vector's elements as its diagonal elements
toDiagonalMatrix: function() {
return Matrix.Diagonal(this.elements);
// Returns the result of rounding the elements of the vector
return this.map(function(x) { return Math.round(x); });
// Returns a copy of the vector with elements set to the given value if they
// differ from it by less than Sylvester.precision
return this.map(function(y) {
return (Math.abs(y - x) <= Sylvester.precision) ? x : y;
// Returns the vector's distance from the argument, when considered as a point in space
distanceFrom: function(obj) {
if (obj.anchor) { return obj.distanceFrom(this); }
var V = obj.elements || obj;
if (V.length != this.elements.length) { return null; }
this.each(function(x, i) {
// Returns true if the vector is point on the given line
return line.contains(this);
// Return true iff the vector is a point in the given plane
liesIn: function(plane) {
return plane.contains(this);
// Rotates the vector about the given object. The object should be a
// point if the vector is 2D, and a line if it is 3D. Be careful with line directions!
rotate: function(t, obj) {
switch (this.elements.length) {
if (V.length != 2) { return null; }
R = Matrix.Rotation(t).elements;
x = this.elements[0] - V[0];
y = this.elements[1] - V[1];
V[0] + R[0][0] * x + R[0][1] * y,
V[1] + R[1][0] * x + R[1][1] * y
if (!obj.direction) { return null; }
var C = obj.pointClosestTo(this).elements;
R = Matrix.Rotation(t, obj.direction).elements;
x = this.elements[0] - C[0];
y = this.elements[1] - C[1];
z = this.elements[2] - C[2];
C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z,
C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z,
C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z
// Returns the result of reflecting the point in the given point, line or plane
reflectionIn: function(obj) {
// obj is a plane or line
var P = this.elements.slice();
var C = obj.pointClosestTo(P).elements;
return Vector.create([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);
var Q = obj.elements || obj;
if (this.elements.length != Q.length) { return null; }
return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); });
// Utility to make sure vectors are 3D. If they are 2D, a zero z-component is added
switch (V.elements.length) {
case 2: V.elements.push(0); break;
// Returns a string representation of the vector
return '[' + this.elements.join(', ') + ']';
// Set vector's elements from an array
setElements: function(els) {
this.elements = (els.elements || els).slice();
Vector.create = function(elements) {
return V.setElements(elements);
Vector.i = Vector.create([1,0,0]);
Vector.j = Vector.create([0,1,0]);
Vector.k = Vector.create([0,0,1]);
// Random vector of size n
Vector.Random = function(n) {
do { elements.push(Math.random());
return Vector.create(elements);
// Vector filled with zeros
Vector.Zero = function(n) {
return Vector.create(elements);
// Returns element (i,j) of the matrix
if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; }
return this.elements[i-1][j-1];
// Returns row k of the matrix as a vector
if (i > this.elements.length) { return null; }
return Vector.create(this.elements[i-1]);
// Returns column k of the matrix as a vector
if (j > this.elements[0].length) { return null; }
var col = [], n = this.elements.length, k = n, i;
col.push(this.elements[i][j-1]);
return Vector.create(col);
// Returns the number of rows/columns the matrix has
return {rows: this.elements.length, cols: this.elements[0].length};
// Returns the number of rows in the matrix
return this.elements.length;
// Returns the number of columns in the matrix
return this.elements[0].length;
// Returns true iff the matrix is equal to the argument. You can supply
// a vector as the argument, in which case the receiver must be a
// one-column matrix equal to the vector.
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (this.elements.length != M.length ||
this.elements[0].length != M[0].length) { return false; }
var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
if (Math.abs(this.elements[i][j] - M[i][j]) > Sylvester.precision) { return false; }
// Returns a copy of the matrix
return Matrix.create(this.elements);
// Maps the matrix to another matrix (of the same dimensions) according to the given function
var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
els[i][j] = fn(this.elements[i][j], i + 1, j + 1);
return Matrix.create(els);
// Returns true iff the argument has the same dimensions as the matrix
isSameSizeAs: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
return (this.elements.length == M.length &&
this.elements[0].length == M[0].length);
// Returns the result of adding the argument to the matrix
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (!this.isSameSizeAs(M)) { return null; }
return this.map(function(x, i, j) { return x + M[i-1][j-1]; });
// Returns the result of subtracting the argument from the matrix
subtract: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (!this.isSameSizeAs(M)) { return null; }
return this.map(function(x, i, j) { return x - M[i-1][j-1]; });
// Returns true iff the matrix can multiply the argument from the left
canMultiplyFromLeft: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
// this.columns should equal matrix.rows
return (this.elements[0].length == M.length);
// Returns the result of multiplying the matrix from the right by the argument.
// If the argument is a scalar then just multiply all the elements. If the argument is
// a vector, a vector is returned, which saves you having to remember calling
multiply: function(matrix) {
return this.map(function(x) { return x * matrix; });
var returnVector = matrix.modulus ? true : false;
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (!this.canMultiplyFromLeft(M)) { return null; }
var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;
var cols = this.elements[0].length, elements = [], sum, nc, c;
sum += this.elements[i][c] * M[c][j];
var M = Matrix.create(elements);
return returnVector ? M.col(1) : M;
x: function(matrix) { return this.multiply(matrix); },
// Returns a submatrix taken from the matrix
// Argument order is: start row, start col, nrows, ncols
// Element selection wraps if the required index is outside the matrix's bounds, so you could
// use this to perform row/column cycling or copy-augmenting.
minor: function(a, b, c, d) {
var elements = [], ni = c, i, nj, j;
var rows = this.elements.length, cols = this.elements[0].length;
elements[i][j] = this.elements[(a+i-1)%rows][(b+j-1)%cols];
return Matrix.create(elements);
// Returns the transpose of the matrix
var rows = this.elements.length, cols = this.elements[0].length;
var elements = [], ni = cols, i, nj, j;
elements[i][j] = this.elements[j][i];
return Matrix.create(elements);
// Returns true iff the matrix is square
return (this.elements.length == this.elements[0].length);
// Returns the (absolute) largest element of the matrix
var m = 0, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; }
// Returns the indeces of the first match found by reading row-by-row from left to right
var index = null, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
if (this.elements[i][j] == x) { return {i: i+1, j: j+1}; }
// If the matrix is square, returns the diagonal elements as a vector.
// Otherwise, returns null.
if (!this.isSquare) { return null; }
var els = [], n = this.elements.length, k = n, i;
els.push(this.elements[i][i]);
return Vector.create(els);
// Make the matrix upper (right) triangular by Gaussian elimination.
// This method only adds multiples of rows to other rows. No rows are
// scaled up or switched, and the determinant is preserved.
toRightTriangular: function() {
var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p;
if (M.elements[i][i] == 0) {
for (j = i + 1; j < k; j++) {
if (M.elements[j][i] != 0) {
els.push(M.elements[i][p] + M.elements[j][p]);
if (M.elements[i][i] != 0) {
for (j = i + 1; j < k; j++) {
var multiplier = M.elements[j][i] / M.elements[i][i];
// Elements with column numbers up to an including the number
// of the row that we're subtracting can safely be set straight to
// zero, since that's the point of this routine and it avoids having
// to loop over and correct rounding errors later
els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);
toUpperTriangular: function() { return this.toRightTriangular(); },
// Returns the determinant for square matrices
determinant: function() {
if (!this.isSquare()) { return null; }
var M = this.toRightTriangular();
var det = M.elements[0][0], n = M.elements.length - 1, k = n, i;
det = det * M.elements[i][i];
det: function() { return this.determinant(); },
// Returns true iff the matrix is singular
return (this.isSquare() && this.determinant() === 0);
// Returns the trace for square matrices
if (!this.isSquare()) { return null; }
var tr = this.elements[0][0], n = this.elements.length - 1, k = n, i;
tr += this.elements[i][i];
tr: function() { return this.trace(); },
// Returns the rank of the matrix
var M = this.toRightTriangular(), rank = 0;
var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
if (Math.abs(M.elements[i][j]) > Sylvester.precision) { rank++; break; }
rk: function() { return this.rank(); },
// Returns the result of attaching the given argument to the right-hand side of the matrix
augment: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
var T = this.dup(), cols = T.elements[0].length;
var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j;
if (ni != M.length) { return null; }
T.elements[i][cols + j] = M[i][j];
// Returns the inverse (if one exists) using Gauss-Jordan
if (!this.isSquare() || this.isSingular()) { return null; }
var ni = this.elements.length, ki = ni, i, j;
var M = this.augment(Matrix.I(ni)).toRightTriangular();
var np, kp = M.elements[0].length, p, els, divisor;
var inverse_elements = [], new_element;
// Matrix is non-singular so there will be no zeros on the diagonal
// Cycle through rows from last to first
// First, normalise diagonal elements to 1
inverse_elements[i] = [];
divisor = M.elements[i][i];
new_element = M.elements[i][p] / divisor;
// Shuffle of the current row of the right hand side into the results
// array as it will not be modified by later runs through this loop
if (p >= ki) { inverse_elements[i].push(new_element); }
// Then, subtract this row from those above it to
// give the identity matrix on the left hand side
for (j = 0; j < i; j++) {
els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);
return Matrix.create(inverse_elements);
inv: function() { return this.inverse(); },
// Returns the result of rounding all the elements
return this.map(function(x) { return Math.round(x); });
// Returns a copy of the matrix with elements set to the given value if they
// differ from it by less than Sylvester.precision
return this.map(function(p) {
return (Math.abs(p - x) <= Sylvester.precision) ? x : p;
// Returns a string representation of the matrix
var n = this.elements.length, k = n, i;
matrix_rows.push(Vector.create(this.elements[i]).inspect());
return matrix_rows.join('\n');
// Set the matrix's elements from an array. If the argument passed
// is a vector, the resulting matrix will be a single column.
setElements: function(els) {
var i, elements = els.elements || els;
if (typeof(elements[0][0]) != 'undefined') {
var ni = elements.length, ki = ni, nj, kj, j;
nj = elements[i].length; kj = nj;
this.elements[i][j] = elements[i][j];
var n = elements.length, k = n;
this.elements.push([elements[i]]);
Matrix.create = function(elements) {
return M.setElements(elements);
// Identity matrix of size n
var els = [], k = n, i, nj, j;
els[i][j] = (i == j) ? 1 : 0;
return Matrix.create(els);
// Diagonal matrix - all off-diagonal elements are zero
Matrix.Diagonal = function(elements) {
var n = elements.length, k = n, i;
M.elements[i][i] = elements[i];
// Rotation matrix about some axis. If no axis is
// supplied, assume we're after a 2D transform
Matrix.Rotation = function(theta, a) {
[Math.cos(theta), -Math.sin(theta)],
[Math.sin(theta), Math.cos(theta)]
if (axis.elements.length != 3) { return null; }
var mod = axis.modulus();
var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod;
var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c;
// Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp
// That proof rotates the co-ordinate system so theta
// becomes -theta and sin becomes -sin here.
[ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],
[ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],
[ t*x*z - s*y, t*y*z + s*x, t*z*z + c ]
// Special case rotations
Matrix.RotationX = function(t) {
var c = Math.cos(t), s = Math.sin(t);
Matrix.RotationY = function(t) {
var c = Math.cos(t), s = Math.sin(t);
Matrix.RotationZ = function(t) {
var c = Math.cos(t), s = Math.sin(t);
// Random matrix of n rows, m columns
Matrix.Random = function(n, m) {
return Matrix.Zero(n, m).map(
function() { return Math.random(); }
// Matrix filled with zeros
Matrix.Zero = function(n, m) {
var els = [], ni = n, i, nj, j;
return Matrix.create(els);
// Returns true if the argument occupies the same space as the line
return (this.isParallelTo(line) && this.contains(line.anchor));
// Returns a copy of the line
return Line.create(this.anchor, this.direction);
// Returns the result of translating the line by the given vector/array
translate: function(vector) {
var V = vector.elements || vector;
this.anchor.elements[0] + V[0],
this.anchor.elements[1] + V[1],
this.anchor.elements[2] + (V[2] || 0)
// Returns true if the line is parallel to the argument. Here, 'parallel to'
// means that the argument's direction is either parallel or antiparallel to
// the line's own direction. A line is parallel to a plane if the two do not
// have a unique intersection.
isParallelTo: function(obj) {
if (obj.normal) { return obj.isParallelTo(this); }
var theta = this.direction.angleFrom(obj.direction);
return (Math.abs(theta) <= Sylvester.precision || Math.abs(theta - Math.PI) <= Sylvester.precision);
// Returns the line's perpendicular distance from the argument,
// which can be a point, a line or a plane
distanceFrom: function(obj) {
if (obj.normal) { return obj.distanceFrom(this); }
if (this.isParallelTo(obj)) { return this.distanceFrom(obj.anchor); }
var N = this.direction.cross(obj.direction).toUnitVector().elements;
var A = this.anchor.elements, B = obj.anchor.elements;
return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
var P = obj.elements || obj;
var A = this.anchor.elements, D = this.direction.elements;
var PA1 = P[0] - A[0], PA2 = P[1] - A[1], PA3 = (P[2] || 0) - A[2];
var modPA = Math.sqrt(PA1*PA1 + PA2*PA2 + PA3*PA3);
if (modPA === 0) return 0;
// Assumes direction vector is normalized
var cosTheta = (PA1 * D[0] + PA2 * D[1] + PA3 * D[2]) / modPA;
var sin2 = 1 - cosTheta*cosTheta;
return Math.abs(modPA * Math.sqrt(sin2 < 0 ? 0 : sin2));
// Returns true iff the argument is a point on the line
contains: function(point) {
var dist = this.distanceFrom(point);
return (dist !== null && dist <= Sylvester.precision);
// Returns true iff the line lies in the given plane
liesIn: function(plane) {
return plane.contains(this);
// Returns true iff the line has a unique point of intersection with the argument
intersects: function(obj) {
if (obj.normal) { return obj.intersects(this); }
return (!this.isParallelTo(obj) && this.distanceFrom(obj) <= Sylvester.precision);
// Returns the unique intersection point with the argument, if one exists
intersectionWith: function(obj) {
if (obj.normal) { return obj.intersectionWith(this); }
if (!this.intersects(obj)) { return null; }
var P = this.anchor.elements, X = this.direction.elements,
Q = obj.anchor.elements, Y = obj.direction.elements;
var X1 = X[0], X2 = X[1], X3 = X[2], Y1 = Y[0], Y2 = Y[1], Y3 = Y[2];
var PsubQ1 = P[0] - Q[0], PsubQ2 = P[1] - Q[1], PsubQ3 = P[2] - Q[2];
var XdotQsubP = - X1*PsubQ1 - X2*PsubQ2 - X3*PsubQ3;
var YdotPsubQ = Y1*PsubQ1 + Y2*PsubQ2 + Y3*PsubQ3;
var XdotX = X1*X1 + X2*X2 + X3*X3;
var YdotY = Y1*Y1 + Y2*Y2 + Y3*Y3;
var XdotY = X1*Y1 + X2*Y2 + X3*Y3;
var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);
return Vector.create([P[0] + k*X1, P[1] + k*X2, P[2] + k*X3]);
// Returns the point on the line that is closest to the given point or line
pointClosestTo: function(obj) {
if (this.intersects(obj)) { return this.intersectionWith(obj); }
if (this.isParallelTo(obj)) { return null; }
var D = this.direction.elements, E = obj.direction.elements;
var D1 = D[0], D2 = D[1], D3 = D[2], E1 = E[0], E2 = E[1], E3 = E[2];
// Create plane containing obj and the shared normal and intersect this with it
// Thank you: http://www.cgafaq.info/wiki/Line-line_distance
var x = (D3 * E1 - D1 * E3), y = (D1 * E2 - D2 * E1), z = (D2 * E3 - D3 * E2);
var N = Vector.create([x * E3 - y * E2, y * E1 - z * E3, z * E2 - x * E1]);
var P = Plane.create(obj.anchor, N);
return P.intersectionWith(this);
var P = obj.elements || obj;
if (this.contains(P)) { return Vector.create(P); }
var A = this.anchor.elements, D = this.direction.elements;
var D1 = D[0], D2 = D[1], D3 = D[2], A1 = A[0], A2 = A[1], A3 = A[2];
var x = D1 * (P[1]-A2) - D2 * (P[0]-A1), y = D2 * ((P[2] || 0) - A3) - D3 * (P[1]-A2),
z = D3 * (P[0]-A1) - D1 * ((P[2] || 0) - A3);
var V = Vector.create([D2 * x - D3 * z, D3 * y - D1 * x, D1 * z - D2 * y]);
var k = this.distanceFrom(P) / V.modulus();
P[0] + V.elements[0] * k,
P[1] + V.elements[1] * k,
(P[2] || 0) + V.elements[2] * k
// Returns a copy of the line rotated by t radians about the given line. Works by
// finding the argument's closest point to this line's anchor point (call this C) and
// rotating the anchor about C. Also rotates the line's direction about the argument's.
// Be careful with this - the rotation axis' direction affects the outcome!
rotate: function(t, line) {
// If we're working in 2D
if (typeof(line.direction) == 'undefined') { line = Line.create(line.to3D(), Vector.k); }
var R = Matrix.Rotation(t, line.direction).elements;
var C = line.pointClosestTo(this.anchor).elements;
var A = this.anchor.elements, D = this.direction.elements;
var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
var x = A1 - C1, y = A2 - C2, z = A3 - C3;
C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
R[0][0] * D[0] + R[0][1] * D[1] + R[0][2] * D[2],
R[1][0] * D[0] + R[1][1] * D[1] + R[1][2] * D[2],
R[2][0] * D[0] + R[2][1] * D[1] + R[2][2] * D[2]
// Returns the line's reflection in the given point or line
reflectionIn: function(obj) {
var A = this.anchor.elements, D = this.direction.elements;
var A1 = A[0], A2 = A[1], A3 = A[2], D1 = D[0], D2 = D[1], D3 = D[2];
var newA = this.anchor.reflectionIn(obj).elements;
// Add the line's direction vector to its anchor, then mirror that in the plane
var AD1 = A1 + D1, AD2 = A2 + D2, AD3 = A3 + D3;
var Q = obj.pointClosestTo([AD1, AD2, AD3]).elements;
var newD = [Q[0] + (Q[0] - AD1) - newA[0], Q[1] + (Q[1] - AD2) - newA[1], Q[2] + (Q[2] - AD3) - newA[2]];
return Line.create(newA, newD);
} else if (obj.direction) {
// obj is a line - reflection obtained by rotating PI radians about obj
return this.rotate(Math.PI, obj);
// obj is a point - just reflect the line's anchor in it
var P = obj.elements || obj;
return Line.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.direction);
// Set the line's anchor point and direction.
setVectors: function(anchor, direction) {
// Need to do this so that line's properties are not
// references to the arguments passed in
anchor = Vector.create(anchor);
direction = Vector.create(direction);
if (anchor.elements.length == 2) {anchor.elements.push(0); }
if (direction.elements.length == 2) { direction.elements.push(0); }
if (anchor.elements.length > 3 || direction.elements.length > 3) { return null; }
var mod = direction.modulus();
if (mod === 0) { return null; }
this.direction = Vector.create([
direction.elements[0] / mod,
direction.elements[1] / mod,
direction.elements[2] / mod
Line.create = function(anchor, direction) {
return L.setVectors(anchor, direction);
Line.X = Line.create(Vector.Zero(3), Vector.i);
Line.Y = Line.create(Vector.Zero(3), Vector.j);
Line.Z = Line.create(Vector.Zero(3), Vector.k);
// Returns true iff the plane occupies the same space as the argument
return (this.contains(plane.anchor) && this.isParallelTo(plane));
// Returns a copy of the plane
return Plane.create(this.anchor, this.normal);
// Returns the result of translating the plane by the given vector
translate: function(vector) {
var V = vector.elements || vector;
this.anchor.elements[0] + V[0],
this.anchor.elements[1] + V[1],
this.anchor.elements[2] + (V[2] || 0)
// Returns true iff the plane is parallel to the argument. Will return true
// if the planes are equal, or if you give a line and it lies in the plane.
isParallelTo: function(obj) {
theta = this.normal.angleFrom(obj.normal);
return (Math.abs(theta) <= Sylvester.precision || Math.abs(Math.PI - theta) <= Sylvester.precision);
} else if (obj.direction) {
return this.normal.isPerpendicularTo(obj.direction);
// Returns true iff the receiver is perpendicular to the argument
isPerpendicularTo: function(plane) {
var theta = this.normal.angleFrom(plane.normal);
return (Math.abs(Math.PI/2 - theta) <= Sylvester.precision);
// Returns the plane's distance from the given object (point, line or plane)
distanceFrom: function(obj) {
if (this.intersects(obj) || this.contains(obj)) { return 0; }
// obj is a plane or line
var A = this.anchor.elements, B = obj.anchor.elements, N = this.normal.elements;
return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
var P = obj.elements || obj;
var A = this.anchor.elements, N = this.normal.elements;
return Math.abs((A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]);
// Returns true iff the plane contains the given point or line
contains: function(obj) {
if (obj.normal) { return null; }
return (this.contains(obj.anchor) && this.contains(obj.anchor.add(obj.direction)));
var P = obj.elements || obj;
var A = this.anchor.elements, N = this.normal.elements;
var diff = Math.abs(N[0]*(A[0] - P[0]) + N[1]*(A[1] - P[1]) + N[2]*(A[2] - (P[2] || 0)));
return (diff <= Sylvester.precision);
// Returns true iff the plane has a unique point/line of intersection with the argument
intersects: function(obj) {
if (typeof(obj.direction) == 'undefined' && typeof(obj.normal) == 'undefined') { return null; }
return !this.isParallelTo(obj);
// Returns the unique intersection with the argument, if one exists. The result
// will be a vector if a line is supplied, and a line if a plane is supplied.
intersectionWith: function(obj) {
if (!this.intersects(obj)) { return null; }
var A = obj.anchor.elements, D = obj.direction.elements,
P = this.anchor.elements, N = this.normal.elements;
var multiplier = (N[0]*(P[0]-A[0]) + N[1]*(P[1]-A[1]) + N[2]*(P[2]-A[2])) / (N[0]*D[0] + N[1]*D[1] + N[2]*D[2]);
return Vector.create([A[0] + D[0]*multiplier, A[1] + D[1]*multiplier, A[2] + D[2]*multiplier]);
var direction = this.normal.cross(obj.normal).toUnitVector();
// To find an anchor point, we find one co-ordinate that has a value
// of zero somewhere on the intersection, and remember which one we picked
var N = this.normal.elements, A = this.anchor.elements,
O = obj.normal.elements, B = obj.anchor.elements;
var solver = Matrix.Zero(2,2), i = 0;
while (solver.isSingular()) {
// Then we solve the simultaneous equations in the remaining dimensions
var inverse = solver.inverse().elements;
var x = N[0]*A[0] + N[1]*A[1] + N[2]*A[2];
var y = O[0]*B[0] + O[1]*B[1] + O[2]*B[2];
inverse[0][0] * x + inverse[0][1] * y,
inverse[1][0] * x + inverse[1][1] * y
for (var j = 1; j <= 3; j++) {
// This formula picks the right element from intersection by
// cycling depending on which element we set to zero above
anchor.push((i == j) ? 0 : intersection[(j + (5 - i)%3)%3]);
return Line.create(anchor, direction);
// Returns the point in the plane closest to the given point
pointClosestTo: function(point) {
var P = point.elements || point;
var A = this.anchor.elements, N = this.normal.elements;
var dot = (A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2];
return Vector.create([P[0] + N[0] * dot, P[1] + N[1] * dot, (P[2] || 0) + N[2] * dot]);
// Returns a copy of the plane, rotated by t radians about the given line
// See notes on Line#rotate.
rotate: function(t, line) {
var R = Matrix.Rotation(t, line.direction).elements;
var C = line.pointClosestTo(this.anchor).elements;
var A = this.anchor.elements, N = this.normal.elements;
var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
var x = A1 - C1, y = A2 - C2, z = A3 - C3;
C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
R[0][0] * N[0] + R[0][1] * N[1] + R[0][2] * N[2],
R[1][0] * N[0] + R[1][1] * N[1] + R[1][2] * N[2],
R[2][0] * N[0] + R[2][1] * N[1] + R[2][2] * N[2]
// Returns the reflection of the plane in the given point, line or plane.
reflectionIn: function(obj) {
var A = this.anchor.elements, N = this.normal.elements;
var A1 = A[0], A2 = A[1], A3 = A[2], N1 = N[0], N2 = N[1], N3 = N[2];
var newA = this.anchor.reflectionIn(obj).elements;
// Add the plane's normal to its anchor, then mirror that in the other plane
var AN1 = A1 + N1, AN2 = A2 + N2, AN3 = A3 + N3;
var Q = obj.pointClosestTo([AN1, AN2, AN3]).elements;
var newN = [Q[0] + (Q[0] - AN1) - newA[0], Q[1] + (Q[1] - AN2) - newA[1], Q[2] + (Q[2] - AN3) - newA[2]];
return Plane.create(newA, newN);
} else if (obj.direction) {
return this.rotate(Math.PI, obj);
var P = obj.elements || obj;
return Plane.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.normal);
// Sets the anchor point and normal to the plane. If three arguments are specified,
// the normal is calculated by assuming the three points should lie in the same plane.
// If only two are sepcified, the second is taken to be the normal. Normal vector is
// normalised before storage.
setVectors: function(anchor, v1, v2) {
anchor = Vector.create(anchor);
anchor = anchor.to3D(); if (anchor === null) { return null; }
v1 = v1.to3D(); if (v1 === null) { return null; }
if (typeof(v2) == 'undefined') {
v2 = v2.to3D(); if (v2 === null) { return null; }
var A1 = anchor.elements[0], A2 = anchor.elements[1], A3 = anchor.elements[2];
var v11 = v1.elements[0], v12 = v1.elements[1], v13 = v1.elements[2];
var v21 = v2.elements[0], v22 = v2.elements[1], v23 = v2.elements[2];
(v12 - A2) * (v23 - A3) - (v13 - A3) * (v22 - A2),
(v13 - A3) * (v21 - A1) - (v11 - A1) * (v23 - A3),
(v11 - A1) * (v22 - A2) - (v12 - A2) * (v21 - A1)
if (mod === 0) { return null; }
normal = Vector.create([normal.elements[0] / mod, normal.elements[1] / mod, normal.elements[2] / mod]);
mod = Math.sqrt(v11*v11 + v12*v12 + v13*v13);
if (mod === 0) { return null; }
normal = Vector.create([v1.elements[0] / mod, v1.elements[1] / mod, v1.elements[2] / mod]);
Plane.create = function(anchor, v1, v2) {
return P.setVectors(anchor, v1, v2);
Plane.XY = Plane.create(Vector.Zero(3), Vector.k);
Plane.YZ = Plane.create(Vector.Zero(3), Vector.i);
Plane.ZX = Plane.create(Vector.Zero(3), Vector.j);
Plane.YX = Plane.XY; Plane.ZY = Plane.YZ; Plane.XZ = Plane.ZX;
var d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0];
width:window.innerWidth || e.clientWidth || g.clientWidth,
height:window.innerHeight|| e.clientHeight|| g.clientHeight
N3D.Error = function(name,message) {
this.level = "Show Stopper";
this.htmlMessage = message;
N3D.Error.prototype.toString = function(){return this.name + ": " + this.message};
function extend(child,parent){
F.prototype = parent.prototype;
child.prototype = new F();
child.prototype.constructor = child;
var head = document.getElementsByTagName("head")[0];
var master_script = document.getElementsByTagName('script');
master_script = master_script[master_script.length-1];
var abs_path = master_script.src.match(/.*\//);
abs_path = abs_path ? abs_path[0] : '/';
/* >>>> Detect support render >>>> */
var supp = N3D.Support = {
return 'Context 2D: '+this.Canvas+'\n' +
'WebGL: '+this.WebGL+'\n' +
var canvas = document.createElement('canvas');
if(typeof WebGLRenderingContext !== 'undefined'){
var types = ['webgl','experimental-webgl'];
for(var i=0;i<length;i++){
var ctx = canvas.getContext(types[i]);
supp.SVG = document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Shape", "1.1")
var a = document.createElement('div');
a.innerHTML = '<v:shape adj="1" />';
b.style.behavior = "url(#default#VML)";
supp.VML = b ? typeof b.adj == "object": true;
/* <<<< Detect support render <<<< */
function load(urls,callbacks){
if(callbacks.unloaded.length == 0){
if(document.getElementById(url) != null){ loader(urls,callbacks); return false;}
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = abs_path+url+'.js';
head.appendChild(script);
script.onreadystatechange = function(){
if(script.readyState == 'loaded' || script.readyState == 'complete'){
this.onreadystatechange = null;
script.onerror = function(){
callbacks.unloaded.push(this.id+' missing library file,'+this.src+' not a file');
script.onload = function(){
N3D.require = function(){
var urls = Array.prototype.slice.call(arguments);
complete: function(f){ this.complete = f || this.complete; },
success: function(f){ this.success = f || this.success; },
error: function(f){ this.error = f || this.error; },
function getAttr(el, attr) {
if(result = el[attr]){ return result; }
if(el.getAttribute && (result = el.getAttribute(attr))){ return result;}
var attrs = el.attributes;
var length = attrs.length;
for(var i = 0; i < length; i++){
if(attrs[i].nodeName === attr){
return attrs[i].nodeValue;
var require = getAttr(master_script,'require');
var req_load = N3D.require.apply(null,require.split(','));
req_load.success(function(){
var script = document.createElement('script');
script.type = 'text/javascript';
script.text = master_script.innerHTML;
head.appendChild(script);
master_script.parentNode.removeChild(master_script);
req_load.error(function(){
N3D.SaveModel = function(name){
var model = N3D.Models[name];
var text = '{"vp":[1,2,3,4],"f":[0,1,2,0,2,3]}';
if(model instanceof N3D.Geometry.Shapes){
var a = document.createElement("a");
a.download = name+'.json';
a.href = 'data:text/javascript;charset=utf-8,'+text;
a.style.display = "none";
a.onclick = function(event){
var event = event || window.event;
var target = event.target || event.srcElement;
document.body.removeChild(event.target);
document.body.appendChild(a);
throw new N3D.Error('Model Exporter','export was successful');
throw new N3D.Error('Model Exporter','Model "'+name+'" not found');
/* >>>> Math.Main >>>> */
var PI = Math.PI, floor = Math.floor, random = Math.random;
var PI180 = PI/180, PI180_rev = 180/PI, sqrt5 = Math.sqrt(5);
obj.Log10E = Math.LOG10E || 0.4342945;
obj.Log2E = Math.LOG2E || 1.442695;
obj.Pow2 = function(n){ return n*n; };
obj.Pow3 = function(n){ return n*n*n; };
obj.Ceil2 = function(n){ return (~~n)+1; };
obj.Floor2 = function(n){ return ~~n; };
obj.RandomInt = function(min, max) {
return min + floor(random() * (max - min + 1));
obj.RandomFloat = function(min, max) {
return min + (random() * (max - min));
obj.AbsInt = function(n){
obj.ToDegrees = function(d){
obj.ToRadians = function(d){
obj.FromFibonacci = function(T){
var phi = (1 + root5) / 2;
var idx = floor( Math.log(T*sqrt5) / Math.log(phi) + 0.5 );
var u = floor( Math.pow(phi, idx)/sqrt5 + 0.5);
return (u == T) ? idx : false;
obj.ToFibonacci = function(d){
for(var a=0,b=1,c=0,f=1;f<d;f++){
obj.Barycentric = function(v1,v2,v3,a1,a2){
return v1 + (v2-v1) * a1 + (v3-v1) * a2;
obj.CatmullRom = function(v1, v2, v3, v4, a){
var aS = a * a, aC = aS * a;
return (0 * (2 * ve2 + (v3 - v1) * a + (2 * v1 - 5 * v2 + 4 * v3 - v4) * aS + (3 * v2 - v1 - 3 * v3 + v4) * aC));
obj.Clamp = function(v,min,max){
return v > max ? max : (v < min ? min : v);
obj.Lerp = function(v1,v2,a){
/* <<<< Math.Main <<<< */
/* >>>> Math.Matrix3 >>>> */
N3D.Math.Matrix3 = function(n0,n1,n2,n3,n4,n5,n6,n7,n8){
this.m = [n0,n1,n2,n3,n4,n5,n6,n7,n8];
N3D.Math.Matrix3.prototype = {
constructor:N3D.Math.Matrix3,
m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3],
m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7],
var det = 1/(m0*a0-m3*a1+m6*a2);
m[3] = -(m8*m3-m6*m5)*det;
m[4] = (m8*m0-m6*m2)*det;
m[5] = -(m5*m0-m3*m2)*det;
m[6] = (m7*m3-m6*m4)*det;
m[7] = -(m7*m0-m6*m1)*det;
m[8] = (m4*m0-m3*m1)*det;
m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3],
m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7],
n0 = nm[0], n1 = nm[1], n2 = nm[2], n3 = nm[3],
n4 = nm[4], n5 = nm[5], n6 = nm[6], n7 = nm[7],
m[0] = m0*n0 + m1*n3 + m2*n6;
m[1] = m0*n1 + m1*n4 + m2*n7;
m[2] = m0*n2 + m1*n5 + m2*n8;
m[3] = m3*n0 + m4*n3 + m5*n6;
m[4] = m3*n1 + m4*n4 + m5*n7;
m[5] = m3*n2 + m4*n5 + m5*n8;
m[6] = m6*n0 + m7*n3 + m8*n6;
m[7] = m6*n1 + m7*n4 + m8*n7;
m[8] = m6*n2 + m7*n5 + m8*n8;
multiplyVector3:function(v){
var x = v.x, y = v.y, z = v.z;
m[0] * x + m[3] * y + m[6] * z,
m[1] * x + m[4] * y + m[7] * z,
m[2] * x + m[5] * y + m[8] * z
var a1 = m[1], a2 = m[2], a5 = m[3];
m[0] *= x; m[1] *= y; m[2] *= z;
m[3] *= x; m[4] *= y; m[5] *= z;
m[6] *= x; m[7] *= y; m[8] *= z;
rotateX: function(angle){
var m1 = m[1], m4 = m[4], m7 = m[7],
m2 = m[2], m5 = m[5], m8= m[8];
rotateY: function(angle){
var m0 = m[0], m3 = m[3], m6 = m[6],
m2 = m[2], m5 = m[5], m8= m[8];
var m0 = m[0], m3 = m[3], m6 = m[6],
m1 = m[1], m4 = m[4], m7 = m[7];
return m[0].toFixed(4)+", "+m[1].toFixed(4)+", "+m[2].toFixed(4) + "\n" +
m[3].toFixed(4)+", "+m[4].toFixed(4)+", "+m[5].toFixed(4) + "\n" +
m[6].toFixed(4)+", "+m[7].toFixed(4)+", "+m[8].toFixed(4) + "\n";
N3D.Math.Matrix3.Identity = function(){
return new this(1,0,0,0,1,0,0,0,1);
N3D.Math.Matrix3.FromMatrix4 = function(m){
N3D.Math.Matrix3.Inverse = function(m){
m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3],
m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7],
var det = 1/(m0*a0-m3*a1+m6*a2);
-(m8*m3-m6*m5)*det, (m8*m0-m6*m2)*det, -(m5*m0-m3*m2)*det,
(m7*m3-m6*m4)*det, -(m7*m0-m6*m1)*det, (m4*m0-m3*m1)*det
/* <<<< Math.Matrix3 <<<< */
/* >>>> Math.Matrix4 >>>> */
N3D.Math.Matrix4 = function(a00,a04,a08,a12,
N3D.Math.Matrix4.prototype = {
constructor:N3D.Math.Matrix4,
return new $V4(m[12],m[13],m[14],m[15]);
return new this.constructor(
inverse:function(){ //zkontrolovat
m00 = m[0], m04 = m[4], m08 = m[8], m12 = m[12],
m01 = m[1], m05 = m[5], m09 = m[9], m13 = m[13],
m02 = m[2], m06 = m[6], m10 = m[10], m14 = m[14],
m03 = m[3], m07 = m[7], m11 = m[11], m15 = m[15];
var n0 = m05 * (m10*m15 - m11*m14) - m06 * (m09*m15 - m11*m13) + m07 * (m09*m14 - m10*m13),
n1 = m04 * (m10*m15 - m11*m14) - m06 * (m08*m15 - m11*m12) + m07 * (m08*m14 - m10*m12),
n2 = m04 * (m09*m15 - m11*m13) - m05 * (m08*m15 - m11*m12) + m07 * (m08*m13 - m09*m12),
n3 = m04 * (m09*m14 - m10*m13) - m05 * (m08*m14 - m10*m12) + m06 * (m08*m13 - m09*m12),
invDet = 1/(m00*n0 - m01*n1 + m02*n2 - m03*n3);
m[4] = -(m01 * (m10*m15 - m11*m14) - m02 * (m09*m15 - m11*m13) + m03 * (m09*m14 - m10*m13))*invDet;
m[5] = (m00 * (m10*m15 - m11*m14) - m02 * (m08*m15 - m11*m12) + m03 * (m08*m14 - m10*m12))*invDet;
m[6] = -(m00 * (m09*m15 - m11*m13) - m01 * (m08*m15 - m11*m12) + m03 * (m08*m13 - m09*m12))*invDet;
m[7] = (m00 * (m09*m14 - m10*m13) - m01 * (m08*m14 - m10*m12) + m02 * (m08*m13 - m09*m12))*invDet;
m[8] = (m01 * (m06*m15 - m07*m14) - m02 * (m05*m15 - m07*m13) + m03 * (m05*m14 - m06*m13))*invDet;
m[9] = -(m00 * (m06*m15 - m07*m14) - m02 * (m04*m15 - m07*m12) + m03 * (m04*m14 - m06*m12))*invDet;
m[10] = (m00 * (m05*m15 - m07*m13) - m01 * (m04*m15 - m07*m12) + m03 * (m04*m13 - m05*m12))*invDet;
m[11] = -(m00 * (m05*m14 - m06*m13) - m01 * (m04*m14 - m06*m12) + m02 * (m04*m13 - m05*m12))*invDet;
m[12] = -(m01 * (m06*m11 - m07*m10) - m02 * (m05*m11 - m07*m09) + m03 * (m05*m10 - m06*m09))*invDet;
m[13] = (m00 * (m06*m11 - m07*m10) - m02 * (m04*m11 - m07*m08) + m03 * (m04*m10 - m06*m08))*invDet;
m[14] = -(m00 * (m05*m11 - m07*m09) - m01 * (m04*m11 - m07*m08) + m03 * (m04*m09 - m05*m08))*invDet;
m[15] = (m00 * (m05*m10 - m06*m09) - m01 * (m04*m10 - m06*m08) + m02 * (m04*m09 - m05*m08))*invDet;
m00 = m[0], m04 = m[4], m08 = m[8], m12 = m[12],
m01 = m[1], m05 = m[5], m09 = m[9], m13 = m[13],
m02 = m[2], m06 = m[6], m10 = m[10], m14 = m[14],
m03 = m[3], m07 = m[7], m11 = m[11], m15 = m[15];
var a0813 = m08 * m13, a0814 = m08 * m14, a0815 = m08 * m15,
a0912 = m09 * m12, a0914 = m09 * m14, a0915 = m09 * m15,
a1012 = m10 * m12, a1013 = m10 * m13, a1015 = m10 * m15,
a1112 = m11 * m12, a1113 = m11 * m13, a1114 = m11 * m14;
var n0 = m05 * (a1015 - a1114) - m06 * (a0915 - a1113) + m07 * (a0914 - a1013),
n1 = m04 * (a1015 - a1114) - m06 * (a0815 - a1112) + m07 * (a0814 - a1012),
n2 = m04 * (a0915 - a1113) - m05 * (a0815 - a1112) + m07 * (a0813 - a0912),
n3 = m04 * (a0914 - a1013) - m05 * (a0814 - a1012) + m06 * (a0813 - a0912),
invDet = 1/(m00*n0 - m01*n1 + m02*n2 - m03*n3);
m[4] = -(m01 * (a1015 - a1114) - m02 * (a0915 - a1113) + m03 * (a0914 - a1013))*invDet;
m[5] = (m00 * (a1015 - a1114) - m02 * (a0815 - a1112) + m03 * (a0814 - a1012))*invDet;
m[6] = -(m00 * (a0915 - a1113) - m01 * (a0815 - a1112) + m03 * (a0813 - a0912))*invDet;
m[7] = (m00 * (a0914 - a1013) - m01 * (a0814 - a1012) + m02 * (a0813 - a0912))*invDet;
m[8] = (m01 * (m06*m15 - m07*m14) - m02 * (m05*m15 - m07*m13) + m03 * (m05*m14 - m06*m13))*invDet;
m[9] = -(m00 * (m06*m15 - m07*m14) - m02 * (m04*m15 - m07*m12) + m03 * (m04*m14 - m06*m12))*invDet;
m[10] = (m00 * (m05*m15 - m07*m13) - m01 * (m04*m15 - m07*m12) + m03 * (m04*m13 - m05*m12))*invDet;
m[11] = -(m00 * (m05*m14 - m06*m13) - m01 * (m04*m14 - m06*m12) + m02 * (m04*m13 - m05*m12))*invDet;
m[12] = -(m01 * (m06*m11 - m07*m10) - m02 * (m05*m11 - m07*m09) + m03 * (m05*m10 - m06*m09))*invDet;
m[13] = (m00 * (m06*m11 - m07*m10) - m02 * (m04*m11 - m07*m08) + m03 * (m04*m10 - m06*m08))*invDet;
m[14] = -(m00 * (m05*m11 - m07*m09) - m01 * (m04*m11 - m07*m08) + m03 * (m04*m09 - m05*m08))*invDet;
m[15] = (m00 * (m05*m10 - m06*m09) - m01 * (m04*m10 - m06*m08) + m02 * (m04*m09 - m05*m08))*invDet;
m1_00 = m1[0], m1_04 = m1[4], m1_08 = m1[8], m1_12 = m1[12],
m1_01 = m1[1], m1_05 = m1[5], m1_09 = m1[9], m1_13 = m1[13],
m1_02 = m1[2], m1_06 = m1[6], m1_10 = m1[10], m1_14 = m1[14],
m1_03 = m1[3], m1_07 = m1[7], m1_11 = m1[11], m1_15 = m1[15],
m2_00 = m2[0], m2_04 = m2[4], m2_08 = m2[8], m2_12 = m2[12],
m2_01 = m2[1], m2_05 = m2[5], m2_09 = m2[9], m2_13 = m2[13],
m2_02 = m2[2], m2_06 = m2[6], m2_10 = m2[10], m2_14 = m2[14],
m2_03 = m2[3], m2_07 = m2[7], m2_11 = m2[11], m2_15 = m2[15];
m1[0] = m1_00*m2_00 + m1_01*m2_04 + m1_02*m2_08 + m1_03*m2_12;
m1[4] = m1_04*m2_00 + m1_05*m2_04 + m1_06*m2_08 + m1_07*m2_12;
m1[8] = m1_08*m2_00 + m1_09*m2_04 + m1_10*m2_08 + m1_11*m2_12;
m1[12] = m1_12*m2_00 + m1_13*m2_04 + m1_14*m2_08 + m1_15*m2_12;
m1[1] = m1_00*m2_01 + m1_01*m2_05 + m1_02*m2_09 + m1_03*m2_13;
m1[5] = m1_04*m2_01 + m1_05*m2_05 + m1_06*m2_09 + m1_07*m2_13;
m1[9] = m1_08*m2_01 + m1_09*m2_05 + m1_10*m2_09 + m1_11*m2_13;
m1[13] = m1_12*m2_01 + m1_13*m2_05 + m1_14*m2_09 + m1_15*m2_13;
m1[2] = m1_00*m2_02 + m1_01*m2_06 + m1_02*m2_10 + m1_03*m2_14;
m1[6] = m1_04*m2_02 + m1_05*m2_06 + m1_06*m2_10 + m1_07*m2_14;
m1[10] = m1_08*m2_02 + m1_09*m2_06 + m1_10*m2_10 + m1_11*m2_14;
m1[14] = m1_12*m2_02 + m1_13*m2_06 + m1_14*m2_10 + m1_15*m2_14;
m1[3] = m1_00*m2_03 + m1_01*m2_07 + m1_02*m2_11 + m1_03*m2_15;
m1[7] = m1_04*m2_03 + m1_05*m2_07 + m1_06*m2_11 + m1_07*m2_15;
m1[11] = m1_08*m2_03 + m1_09*m2_07 + m1_10*m2_11 + m1_11*m2_15;
m1[15] = m1_12*m2_03 + m1_13*m2_07 + m1_14*m2_11 + m1_15*m2_15;
multiplyTranspose:function(m2){
m1_00 = m1[0], m1_04 = m1[4], m1_08 = m1[8], m1_12 = m1[12],
m1_01 = m1[1], m1_05 = m1[5], m1_09 = m1[9], m1_13 = m1[13],
m1_02 = m1[2], m1_06 = m1[6], m1_10 = m1[10], m1_14 = m1[14],
m1_03 = m1[3], m1_07 = m1[7], m1_11 = m1[11], m1_15 = m1[15],
m2_00 = m2[0], m2_04 = m2[4], m2_08 = m2[8], m2_12 = m2[12],
m2_01 = m2[1], m2_05 = m2[5], m2_09 = m2[9], m2_13 = m2[13],
m2_02 = m2[2], m2_06 = m2[6], m2_10 = m2[10], m2_14 = m2[14],
m2_03 = m2[3], m2_07 = m2[7], m2_11 = m2[11], m2_15 = m2[15];
m1[0] = m1_00*m2_00 + m1_04*m2_01 + m1_08*m2_02 + m1_12*m2_03;
m1[1] = m1_01*m2_00 + m1_05*m2_01 + m1_09*m2_02 + m1_13*m2_03;
m1[2] = m1_02*m2_00 + m1_06*m2_01 + m1_10*m2_02 + m1_14*m2_03;
m1[3] = m1_03*m2_00 + m1_07*m2_01 + m1_11*m2_02 + m1_15*m2_03;
m1[4] = m1_00*m2_04 + m1_04*m2_05 + m1_08*m2_06 + m1_12*m2_07;
m1[5] = m1_01*m2_04 + m1_05*m2_05 + m1_09*m2_06 + m1_13*m2_07;
m1[6] = m1_02*m2_04 + m1_06*m2_05 + m1_10*m2_06 + m1_14*m2_07;
m1[7] = m1_03*m2_04 + m1_07*m2_05 + m1_11*m2_06 + m1_15*m2_07;
m1[8] = m1_00*m2_08 + m1_04*m2_09 + m1_08*m2_10 + m1_12*m2_11;
m1[9] = m1_01*m2_08 + m1_05*m2_09 + m1_09*m2_10 + m1_13*m2_11;
m1[10] = m1_02*m2_08 + m1_06*m2_09 + m1_10*m2_10 + m1_14*m2_11;
m1[11] = m1_03*m2_08 + m1_07*m2_09 + m1_11*m2_10 + m1_15*m2_11;
m1[12] = m1_00*m2_12 + m1_04*m2_13 + m1_08*m2_14 + m1_12*m2_15;
m1[13] = m1_01*m2_12 + m1_05*m2_13 + m1_09*m2_14 + m1_13*m2_15;
m1[14] = m1_02*m2_12 + m1_06*m2_13 + m1_10*m2_14 + m1_14*m2_15;
m1[15] = m1_03*m2_12 + m1_07*m2_13 + m1_11*m2_14 + m1_15*m2_15;
multiplyVector4:function(v){
var x = v.x, y = v.y, z = v.z, w = v.w;
return new v.constructor(
m[0]*x + m[4]*y + m[8]*z + m[12]*w,
m[1]*x + m[5]*y + m[9]*z + m[13]*w,
m[2]*x + m[6]*y + m[10]*z + m[14]*w,
m[3]*x + m[7]*y + m[11]*z + m[15]*w
rotateX: function(radians){
var c = Math.cos(radians);
var s = Math.sin(radians);
var m04 = m[4], m05 = m[5], m06 = m[6], m07 = m[7],
m08 = m[8], m09 = m[9], m10 = m[10], m11= m[11];
m[4] = m04 * c + m08 * s; m[5] = m05 * c + m09 * s; m[6] = m06 * c + m10 * s; m[7] = m07 * c + m11 * s;
m[8] = m04 * -s + m08 * c; m[9] = m05 * -s + m09 * c; m[10] = m06 * -s + m10 * c; m[11] = m07 * -s + m11 * c;
rotateY: function(radians){
var c = Math.cos(radians);
var s = Math.sin(radians);
var m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3],
m08 = m[8], m09 = m[9], m10= m[10], m11= m[11];
m[0] = m00 * c + m02 * -s; m[1] = m01 * c + m09 * -s; m[2] = m02 * c + m10* -s; m[3] = m03 * c + m11* -s;
m[8] = m00 * s + m02 * c; m[9] = m01 * s + m09 * c; m[10]= m02 * s + m10* c; m[11] = m03 * s + m11* c;
rotateZ: function(radians){
var c = Math.cos(radians);
var s = Math.sin(radians);
var m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3],
m04 = m[4], m05 = m[5], m06 = m[6], m07 = m[7];
m[0] = m00 * c + m04 * s; m[1] = m01 * c + m05 * s; m[2] = m02 * c + m06* s; m[3] = m03 * c + m07* s;
m[4] = m00 * -s + m04 * c; m[5] = m01 * -s + m05 * c; m[6] = m02 * -s + m06* c; m[7] = m03 * -s + m07* c;
rotateAroundAxis:function(r,v){
var c = Math.cos(r), s = Math.sin(r);
var x = v.x,y = v.y, z = v.z, t = 1-c,
xyt = x*y*t, xzt = x*z*t, yzt = y*z*t,
xs = x*s, ys = y*s, zs = z*s;
m00 = m[0], m04 = m[4], m08 = m[8], m12 = m[12],
m01 = m[1], m05 = m[5], m09 = m[9], m13 = m[13],
m02 = m[2], m06 = m[6], m10 = m[10], m14 = m[14],
m03 = m[3], m07 = m[7], m11 = m[11], m15 = m[15];
var a00 = c+x*x*t, a04 = xyt-zs, a08 = xzt+ys,
a01 = xyt+zs, a05 = c+y*y*t,a09 = yzt-xs,
a02 = xzt-ys, a06 = yzt+xs, a10 = c+z*z*t;
m[0] = m00*a00 + m01*a04 + m02*a08;
m[4] = m04*a00 + m05*a04 + m06*a08;
m[8] = m08*a00 + m09*a04 + m10*a08;
m[12] = m12*a00 + m13*a04 + m14*a08;
m[1] = m00*a01 + m01*a05 + m02*a09;
m[5] = m04*a01 + m05*a05 + m06*a09;
m[9] = m08*a01 + m09*a05 + m10*a09;
m[13] = m12*a01 + m13*a05 + m14*a09;
m[2] = m00*a02 + m01*a06 + m02*a10;
m[6] = m04*a02 + m05*a06 + m06*a10;
m[10] = m08*a02 + m09*a06 + m10*a10;
m[14] = m12*a02 + m13*a06 + m14*a10;
m[0] = m[0]*x; m[4] = m[4]*y; m[8] = m[8]*z;
m[1] = m[1]*x; m[5] = m[5]*y; m[9] = m[9]*z;
m[2] = m[2]*x; m[6] = m[6]*y; m[10]= m[10]*z;
m[3] = m[3]*x; m[7] = m[7]*y; m[11]= m[11]*z;
translate:function(x,y,z){
m[12] = m[0]*x + m[4]*y + m[8]*z + m[12];
m[13] = m[1]*x + m[5]*y + m[9]*z + m[13];
m[14] = m[2]*x + m[6]*y + m[10]*z + m[14];
m[15] = m[3]*x + m[7]*y + m[11]*z + m[15];
var a01 = m[1], a02 = m[2], a03 = m[3],
m[1] = m[4]; m[2] = m[8]; m[3] = m[12];
m[4] = a01; m[6] = m[9]; m[7] = m[13];
m[8] = a02; m[9] = a12; m[11] = m[14];
m[12] = a03; m[13] = a13; m[14] = a23;
m00 = m[0], m04 = m[4], m08 = m[8],
m01 = m[1], m05 = m[5], m09 = m[9],
m02 = m[2], m06 = m[6], m10 = m[10];
var max = Math.max, sqrt = Math.sqrt;
return new N3D_M_Quaternion(
sqrt(max(0,1+m00-m05-m10))*0.5, //sqrt( max( 0, 1 + m00 - m11 - m22 ) ) / 2;
sqrt(max(0,1-m00+m05-m10))*0.5, //sqrt( max( 0, 1 - m00 + m11 - m22 ) ) / 2;
sqrt(max(0,1-m00-m05+m10))*0.5, //sqrt( max( 0, 1 - m00 - m11 + m22 ) ) / 2;
sqrt(max(0,1+m00+m05+m10))*0.5 //sqrt( max( 0, 1 + m00 + m11 + m22 ) ) / 2;
var trace = m[0] + m[5] + m[10];
s = 0.5/Math.sqrt(trace+1);
return new N3D_M_Vector4(
}else if((m00>m05) && (m00>m10)){
s = 0.5/Math.sqrt(1 + m00 - m05 - m10)
return new N3D_M_Vector4(
s = 0.5/Math.sqrt(1 + m05 - m00 - m10);
return new N3D_M_Vector4(
s = 0.5/Math.sqrt(1+m10 - m00 - m05);
return new N3D_M_Vector4(
return '01: '+e[0].toFixed(3)+', 04: '+e[4].toFixed(3)+', 08: '+e[8].toFixed(3)+', 12: '+e[12].toFixed(3) + '\n' +
'02: '+e[1].toFixed(3)+', 05: '+e[5].toFixed(3)+', 09: '+e[9].toFixed(3)+', 13: '+e[13].toFixed(3) + '\n' +
'03: '+e[2].toFixed(3)+', 06: '+e[6].toFixed(3)+', 10: '+e[10].toFixed(3)+', 14: '+e[14].toFixed(3) + '\n' +
'04: '+e[3].toFixed(3)+', 07: '+e[7].toFixed(3)+', 11: '+e[11].toFixed(3)+', 15: '+e[15].toFixed(3);
N3D.Math.Matrix4.Identity = function(){
return new N3D_M_Matrix4(
N3D.Math.Matrix4.Multiply = function(m1,m2){
m1_00 = m1[0], m1_04 = m1[4], m1_08 = m1[8], m1_12 = m1[12],
m1_01 = m1[1], m1_05 = m1[5], m1_09 = m1[9], m1_13 = m1[13],
m1_02 = m1[2], m1_06 = m1[6], m1_10 = m1[10], m1_14 = m1[14],
m1_03 = m1[3], m1_07 = m1[7], m1_11 = m1[11], m1_15 = m1[15],
m2_00 = m2[0], m2_04 = m2[4], m2_08 = m2[8], m2_12 = m2[12],
m2_01 = m2[1], m2_05 = m2[5], m2_09 = m2[9], m2_13 = m2[13],
m2_02 = m2[2], m2_06 = m2[6], m2_10 = m2[10], m2_14 = m2[14],
m2_03 = m2[3], m2_07 = m2[7], m2_11 = m2[11], m2_15 = m2[15];
return new N3D_M_Matrix4(
m1_00*m2_00 + m1_01*m2_04 + m1_02*m2_08 + m1_03*m2_12,
m1_04*m2_00 + m1_05*m2_04 + m1_06*m2_08 + m1_07*m2_12,
m1_08*m2_00 + m1_09*m2_04 + m1_10*m2_08 + m1_11*m2_12,
m1_12*m2_00 + m1_13*m2_04 + m1_14*m2_08 + m1_15*m2_12,
m1_00*m2_01 + m1_01*m2_05 + m1_02*m2_09 + m1_03*m2_13,
m1_04*m2_01 + m1_05*m2_05 + m1_06*m2_09 + m1_07*m2_13,
m1_08*m2_01 + m1_09*m2_05 + m1_10*m2_09 + m1_11*m2_13,
m1_12*m2_01 + m1_13*m2_05 + m1_14*m2_09 + m1_15*m2_13,
m1_00*m2_02 + m1_01*m2_06 + m1_02*m2_10 + m1_03*m2_14,
m1_04*m2_02 + m1_05*m2_06 + m1_06*m2_10 + m1_07*m2_14,
m1_08*m2_02 + m1_09*m2_06 + m1_10*m2_10 + m1_11*m2_14,
m1_12*m2_02 + m1_13*m2_06 + m1_14*m2_10 + m1_15*m2_14,
m1_00*m2_03 + m1_01*m2_07 + m1_02*m2_11 + m1_03*m2_15,
m1_04*m2_03 + m1_05*m2_07 + m1_06*m2_11 + m1_07*m2_15,
m1_08*m2_03 + m1_09*m2_07 + m1_10*m2_11 + m1_11*m2_15,
m1_12*m2_03 + m1_13*m2_07 + m1_14*m2_11 + m1_15*m2_15
N3D.Math.Matrix4.MultiplyTranspose = function(m1,m2){
m1_00 = m1[0], m1_04 = m1[4], m1_08 = m1[8], m1_12 = m1[12],
m1_01 = m1[1], m1_05 = m1[5], m1_09 = m1[9], m1_13 = m1[13],
m1_02 = m1[2], m1_06 = m1[6], m1_10 = m1[10], m1_14 = m1[14],
m1_03 = m1[3], m1_07 = m1[7], m1_11 = m1[11], m1_15 = m1[15],
m2_00 = m2[0], m2_04 = m2[4], m2_08 = m2[8], m2_12 = m2[12],
m2_01 = m2[1], m2_05 = m2[5], m2_09 = m2[9], m2_13 = m2[13],
m2_02 = m2[2], m2_06 = m2[6], m2_10 = m2[10], m2_14 = m2[14],
m2_03 = m2[3], m2_07 = m2[7], m2_11 = m2[11], m2_15 = m2[15];
return new N3D_M_Matrix4(
m1_00*m2_00 + m1_04*m2_01 + m1_08*m2_02 + m1_12*m2_03,
m1_00*m2_04 + m1_04*m2_05 + m1_08*m2_06 + m1_12*m2_07,
m1_00*m2_08 + m1_04*m2_09 + m1_08*m2_10 + m1_12*m2_11,
m1_00*m2_12 + m1_04*m2_13 + m1_08*m2_14 + m1_12*m2_15,
m1_01*m2_00 + m1_05*m2_01 + m1_09*m2_02 + m1_13*m2_03,
m1_01*m2_04 + m1_05*m2_05 + m1_09*m2_06 + m1_13*m2_07,
m1_01*m2_08 + m1_05*m2_09 + m1_09*m2_10 + m1_13*m2_11,
m1_01*m2_12 + m1_05*m2_13 + m1_09*m2_14 + m1_13*m2_15,
m1_02*m2_00 + m1_06*m2_01 + m1_10*m2_02 + m1_14*m2_03,
m1_02*m2_04 + m1_06*m2_05 + m1_10*m2_06 + m1_14*m2_07,
m1_02*m2_08 + m1_06*m2_09 + m1_10*m2_10 + m1_14*m2_11,
m1_02*m2_12 + m1_06*m2_13 + m1_10*m2_14 + m1_14*m2_15,
m1_03*m2_00 + m1_07*m2_01 + m1_11*m2_02 + m1_15*m2_03,
m1_03*m2_04 + m1_07*m2_05 + m1_11*m2_06 + m1_15*m2_07,
m1_03*m2_08 + m1_07*m2_09 + m1_11*m2_10 + m1_15*m2_11,
m1_03*m2_12 + m1_07*m2_13 + m1_11*m2_14 + m1_15*m2_15
N3D.Math.Matrix4.CreateLookAt = function(eye,target,up){
var f = N3D_M_Vector3.Sub(eye,target).normalize(),
s = N3D_M_Vector3.Cross(up,f).normalize(),
u = N3D_M_Vector3.Cross(f,s);
return new N3D_M_Matrix4(
N3D.Math.Matrix4.CreateRotationX = function(r){
var c = Math.cos(r), s = Math.sin(r);
return new N3D_M_Matrix4(
N3D.Math.Matrix4.CreateRotationY = function(r){
var c = Math.cos(r), s = Math.sin(r);
return new N3D_M_Matrix4(
N3D.Math.Matrix4.CreateRotationZ = function(r){
var c = Math.cos(r), s = Math.sin(r);
return new N3D_M_Matrix4(
N3D.Math.Matrix4.CreateRotationAroundAxis = function(r,v){
var c = Math.cos(r), s = Math.sin(r);
var x = v.x,y = v.y, z = v.z,
xyt = x*y*t, xzt = x*z*t, yzt = y*z*t,
xs = x*s, ys = y*s, zs = z*s;
return new N3D_M_Matrix4(
c+x*x*t, xyt-zs, xzt+ys, 0,
xyt+zs, c+y*y*t, yzt-xs, 0,
xzt-ys, yzt+xs, c+z*z*t, 0,
N3D.Math.Matrix4.CreateScale = function(x,y,z){
return new N3D_M_Matrix4(
N3D.Math.Matrix4.CreateTranslation = function(x,y,z){
return new N3D_M_Matrix4(
N3D.Math.Matrix4.CreateFrustum = function(left,right,bottom,top,near,far){
return new N3D_M_Matrix4(
(right+left)/rl, (top+bottom)/tb, -(far+near)/fn, -1,
N3D.Math.Matrix4.CreatePerspective = function(angle,aspectRatio,near,far){
var scale = Math.tan(angle * N3D_M.PiOver360) * near,
right = aspectRatio * scale,
return new N3D_M_Matrix4(
n2/rl, 0, (right-right)/rl, 0,
0, n2/tb, (scale-scale)/tb, 0,
0, 0, -(far+near)/fn, -1,
N3D.Math.Matrix4.CreatePerspective2 = function(angle,aspectRatio,near,far){
var scale = Math.tan(angle * N3D_M.PiOver360) * near,
right = aspectRatio * scale;
return N3D_M_Matrix4.CreateFrustum(-right,right,-scale,scale,near,far);
N3D.Math.Matrix4.CreateOrthographic = function(l,r,b,t,n,f){
return new N3D_M_Matrix4(
-(l+r)/rl, -(t+b)/tb, -(f+n)/fn, 1
N3D.Math.Matrix4.CreateFromQuaternion = function(q){
var x = q.x, y = q.y, z = q.z, w = 0;
var xx = x*x, xy = x*y, xz = x*z, xw = x*w,
yy = y*y, yz = y*z, yw = y*w,
return new N3D_M_Matrix4(
1 - 2*(yy+zz), 2*(xy-zw), 2*(xz+yw), 0,
2*(xy+zw), 1-2*(xx+zz), 2*(yz-xw), 0,
2*(xz-yw), 2*(yz+xw), 1-2*(xx+yy), 0,
/*var xx2 = 2*x*x, yy2 = 2*y*y, zz2 = 2*z*z;
return new N3D_M_Matrix4(
1-yy2 - zz2, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2*x*y + 2*z*w, 1-xx2 - zz2, 2*y*z - 2*x*w, 0,
2*x*z - 2*y*w, 2*y*z + 2*x*w, 1-xx2 - yy2, 0,
/* <<<< Math.Matrix4 <<<< */
/* >>>> Math.Vector2 >>>> */
N3D.Math.Vector2 = function(x,y){
N3D.Math.Vector2.prototype = {
constructor:N3D.Math.Vector2,
return new N3D_M_Vector2(this.x,this.y);
var x = this.x,y = this.y;
var length = Math.sqrt(x*x + y*y);
perpendicular:function(){
var x = this.x,y = this.y;
var scale_factor = 1 / Math.sqrt(x*x + y*y);
var x = this.x, y = this.y;
var cos = Math.cos(angle);
var sin = Math.sin(angle);
return (this.x * v.x + this.y * v.y);
return "N3D.Math.Vector2("+this.x+","+this.y+")";
return (this.x == v.x && this.y == v.y);
return Math.sqrt(x*x + y*y);
N3D.Math.Vector2.Equals = function(v1,v2){
return (v1.x == v2.x && v1.y == v2.y);
N3D.Math.Vector2.Identity = function(){
return new N3D_M_Vector2(0,0);
N3D.Math.Vector2.Add = function(v1,v2){
return new N3D_M_Vector2(v2.x+v1.x,v2.y+v1.y);
N3D.Math.Vector2.MultiplyScalar = function(v,n){
return new N3D_M_Vector2(v.x*n,v.y*n);
N3D.Math.Vector2.Dot = function(v1, v2){
return (v1.x*v2.x + v1.y*v2.y);
N3D.Math.Vector2.Sub = function(v1,v2){
return new N3D_M_Vector2(v1.x-v2.x,v1.y-v2.y);
N3D.Math.Vector2.Distance = function(v1,v2){
var x = v1.x-v2.x, y = v1.y-v2.y;
return Math.sqrt(x*x+y*y);
N3D.Math.Vector2.Cross = function(v1,v2){
return new N3D_M_Vector2(
N3D.Math.Vector2.Lerp = function(v1,v2,a){
return new N3D_M_Vector2(
/* <<<< Math.Vector2 <<<< */
/* >>>> Math.Vector3 >>>> */
N3D.Math.Vector3 = function(x,y,z){
N3D.Math.Vector3.prototype = {
constructor:N3D.Math.Vector3,
return new N3D_M_Vector3(this.x,this.y,this.z);
return [this.x,this.y,this.z];
var x = this.x,y = this.y,z = this.z;
var x = this.x,y = this.y,z = this.z;
return (x*x + y*y + z*z);
var x = this.x,y = this.y,z = this.z;
return Math.sqrt(x*x + y*y + z*z);
var x = this.x,y = this.y,z = this.z;
var length = 1/Math.sqrt(x*x + y*y + z*z);
var x = this.x, z = this.z;
toRotationMatrix:function(r){
var c = Math.cos(r), s = Math.sin(r);
var x = this.x, y = this.y, z = this.z, t = 1-c,
xyt = x*y*t, xzt = x*z*t, yzt = y*z*t,
xs = x*s, ys = y*s, zs = z*s;
return new N3D_M_Matrix4(
c+x*x*t, xyt-zs, xzt+ys, 0,
xyt+zs, c+y*y*t, yzt-xs, 0,
xzt-ys, yzt+xs, c+z*z*t, 0,
return new N3D_M_Vector4(this.x,this.y,this.z,n);
return "N3D.Math.Vector3("+this.x+","+this.y+","+this.z+")";
N3D.Math.Vector3.Perp = function(a,axis){
var x = a.x, y = a.y, z = a.z;
var par = N3D_M_Vector3.Parallel(a,axis);
return new N3D_M_Vector3(
N3D.Math.Vector3.Parallel = function(a,axis){
var dot = N3D_M_Vector3.Dot(a,axis);
return new N3D_M_Vector3(
axis.x*dot, axis.y*dot, axis.z*dot
N3D.Math.Vector3.Identity = function(){
return new N3D_M_Vector3(0,0,0);
N3D.Math.Vector3.Up = new N3D.Math.Vector3(0,1,0);
N3D.Math.Vector3.Right = new N3D.Math.Vector3(1,0,0);
N3D.Math.Vector3.Forward = new N3D.Math.Vector3(0,0,-1);
N3D.Math.Vector3.Lerp = function(v1,v2,a){
return new N3D_M_Vector3(
N3D.Math.Vector3.Max = function(v1,v2){
N3D.Math.Vector3.Min = function(v1,v2){
return new N3D_M_Vector3(
N3D.Math.Vector3.Herminte = function(v1,t1,v2,t2,a){
return new N3D_M_Vector3(
N3D_M.Hermite(v1.x, t1.x, v2.x, t2.x, a),
N3D_M.Hermite(v1.y, t1.y, v2.y, t2.y, a),
N3D_M.Hermite(v1.z, t1.z, v2.z, t2.z, a)
N3D.Math.Vector3.isZero = function(v){
return (v.x == 0 && v.y == 0 && v.z==0);
N3D.Math.Vector3.Equals = function(v){
return v instanceof N3D_M_Vector3;
N3D.Math.Vector3.DistanceSquared = function(v1,v2){
return (v1.x-v2.x) * (v1.x-v2.x) + (v1.y-v2.y) * (v1.y-v2.y) + (v1.z-v2.z) * (v1.z-v2.z);
N3D.Math.Vector3.Distance = function(v1,v2){
return Math.sqrt((v1.x-v2.x) * (v1.x-v2.x) + (v1.y-v2.y) * (v1.y-v2.y) + (v1.z-v2.z) * (v1.z-v2.z));
N3D.Math.Vector3.Cross = function(v1, v2){
return new N3D_M_Vector3(
v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x
N3D.Math.Vector3.BaryCentric = function(v1,v2,v3,a1,a2,r){
return new N3D_M_Vector3(
N3D_M.Barycentric(v1.x, v2.x, v3.x, a1, a2),
N3D_M.Barycentric(v1.y, v2.y, v3.y, a1, a2),
N3D_M.Barycentric(v1.z, v2.z, v3.z, a1, a2)
N3D.Math.Vector3.CatmullRom = function(v1,v2,v3,v4,a,r){
return new N3D_M_Vector3(
N3D_M.CatmullRom(v1.x, v2.x, v3.x, v4.x, a),
N3D_M.CatmullRom(v1.y, v2.y, v3.y, v4.y, a),
N3D_M.CatmullRom(v1.z, v2.z, v3.z, v4.z, a)
N3D.Math.Vector3.Clamp = function(v1, min, max){
return new N3D_M_Vector3(
N3D_M.Clamp(v1.x, min.x, max.x),
N3D_M.Clamp(v1.y, min.y, max.y),
N3D_M.Clamp(v1.z, min.z, max.z)
N3D.Math.Vector3.Dot = function(v1, v2){
return (v1.x*v2.x + v1.y*v2.y + v1.z * v2.z);
N3D.Math.Vector3.Reflect = function(v,n){
var dT = 2 * N3D_M_Vector3.Dot(v,n);
return new N3D_M_Vector3(
N3D.Math.Vector3.Add = function(v0,v1){
return new N3D_M_Vector3(
N3D.Math.Vector3.Sub = function(v0,v1){
return new N3D_M_Vector3(
N3D.Math.Vector3.MultiplyScalar = function(v0,n){
return new N3D_M_Vector3(
N3D.Math.Vector3.SmoothStep = function(v1,v2,a){
return new N3D_M_Vector3(
N3D_M.SmoothStep(v1.x, v2.x, a),
N3D_M.SmoothStep(v1.y, v2.y, a),
N3D_M.SmoothStep(v1.z, v2.z, a)
N3D_M_Vector3 = N3D.Math.Vector3;
/* <<<< Math.Vector3 <<<< */
/* >>>> Math.Vector4 >>>> */
N3D.Math.Vector4 = function(x,y,z,w){
N3D.Math.Vector4.prototype = {
constructor:N3D.Math.Vector4,
return new N3D.Math.Vector4(this.x,this.y,this.z,this.w);
return [this.x,this.y,this.z];
return [this.x,this.y,this.z,this.w];
divideScalar:function(n){
return this.multiplyScalar(1/n);
return (this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w);
var x = this.x, y = this.y, z = this.z, w = this.w;
var f = 1/Math.sqrt(x*x+y*y+z*z+w*w);
var x = this.x, y = this.y, z = this.z, w = this.w;
return Math.sqrt(x*x+y*y+z*z+w*w);
multiplyMatrix4:function(m){
var x = this.x, y = this.y, z = this.z,w = this.w;
this.x = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
this.y = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
this.z = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
this.w = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
copyFromVector4:function(o){
toHomogenous: function(width,height){
if(-1 < x && x < 1 && -1 < y && y < 1 && -1 < z && z < 1){
this.x = ~~((x+1)*(width*0.5));
this.y = ~~((y+1)*(height*0.5));
return "Vector4("+this.x+","+this.y+","+this.z+","+this.w+")";
N3D.Math.Vector4.Identity = function(){
return new this(0,0,0,1);
N3D.Math.Vector4.CreateFromVector3 = function(v,n){
return new this(v.x,v.y,v.z,n);
N3D.Math.Vector4.Lerp = function(v1,v2,a){
N3D.Math.Vector4.Multiply = function(v1,v2){
N3D.Math.Vector4.Equals = function(v){
return v instanceof this;
N3D.Math.Vector4.Add = function(v1,v2){
N3D.Math.Vector4.Sub = function(v1,v2){
N3D.Math.Vector4.Projection = function(p,viewport){
var viewport = viewport || $Game.viewport;
if(-w <= p.x <= w && -w <= p.y <= w && -w <= p.z <= w){
var x = (p.x+1)*(viewport.width*0.5);
var y = (p.y+1)*(viewport.height*0.5);
var v = new $V2(~~x,~~y);
/* <<<< Math.Vector4 <<<< */
/* >>>> Math.Quaternion >>>> */
N3D.Math.Quaternion = function(x,y,z,w){
N3D.Math.Quaternion.prototype = {
var q1x = this.x, q1y = this.y, q1z = this.z, q1w = this.w;
var q2x = q.x, q2y = q.y, q2z = q.z, q2w = q.w;
this.x = q1x * q2w + q1y * q2z - q1z * q2y + q1w * q2x;
this.y = -q1x * q2z + q1y * q2w + q1z * q2x + q1w * q2y;
this.z = q1x * q2y - q1y * q2x + q1z * q2w + q1w * q2z;
this.w = -q1x * q2x - q1y * q2y - q1z * q2z + q1w * q2w;
var x = this.x, y = this.y, z = this.z, w = this.w;
var xx = x*x, xy = x*y, xz = x*z, xw = x*w;
var yy = y*y, yz = y*z, yw = y*w;
return new N3D_M_Matrix4(
1 - 2*yy - 2*zz, 2*xy - 2*zw, 2*xz + 2*yw, 0,
2*xy + 2*zw, 1 - 2*xx - 2*zz, 2*yz - 2*xw, 0,
2*xz - 2*yw, 2*yz + 2*xw, 1 - 2*xx - 2*yy, 0,
toTransposedMatrix4:function(){
var x = this.x, y = this.y, z = this.z, w = this.w;
var xx = x*x, xy = x*y, xz = x*z, xw = x*w;
var yy = y*y, yz = y*z, yw = y*w;
return new N3D_M_Matrix4(
1 - 2*yy - 2*zz, 2*xy + 2*zw, 2*xz - 2*yw, 0,
2*xy - 2*zw, 1 - 2*xx - 2*zz, 2*yz + 2*xw, 0,
2*xz + 2*yw, 2*yz - 2*xw, 1 - 2*xx - 2*yy, 0,
var x = this.x, y = this.y, z = this.z, w = this.w;
var scale = Math.sqrt(x*x + y*y + z*z);
if (scale == 0 || w > 1 || w < -1){
var x = this.x, y = this.y, z = this.z;
return new N3D_M_Vector3(
Math.atan(2*(x*y + z*w)/(1-2*(y*y+z*z))),
Math.asin(2*(x*z - w*y)),
Math.atan(2*(x*w +y*z)/(1-2*(z*z + w*w)))
return "Quaternion("+this.x+","+this.y+","+this.z+","+this.w+")";
N3D.Math.Quaternion.prototype.dot = N3D.Math.Vector4.prototype.dot;
N3D.Math.Quaternion.Equals = N3D.Math.Vector4.Equals;
N3D.Math.Quaternion.prototype.inverse = N3D.Math.Quaternion.prototype.conjugate;
N3D.Math.Quaternion.prototype.normalize = N3D.Math.Vector4.prototype.normalize;
N3D.Math.Quaternion.CreateFromAngles = function(x,y,z){
x *= 0.5, y *= 0.5, z *= 0.5;
var cos_x_2 = Math.cos(x), sin_x_2 = Math.sin(x),
cos_y_2 = Math.cos(y), sin_y_2 = Math.sin(y),
cos_z_2 = Math.cos(z), sin_z_2 = Math.sin(z);
return new N3D_M_Quaternion(
cos_z_2*cos_y_2*sin_x_2 - sin_z_2*sin_y_2*cos_x_2,
cos_z_2*sin_y_2*cos_x_2 + sin_z_2*cos_y_2*sin_x_2,
sin_z_2*cos_y_2*cos_x_2 - cos_z_2*sin_y_2*sin_x_2,
cos_z_2*cos_y_2*cos_x_2 + sin_z_2*sin_y_2*sin_x_2
N3D.Math.Quaternion.Lerp = function(q1,q2,time){
return new N3D_M_Quaternion(
N3D.Math.Quaternion.Dot = function(q1,q2){
var x1 = q1.x, y1 = q1.y, z1 = q1.z, w1 = q1.w;
var x2 = q2.x, y2 = q2.y, z2 = q2.z, w2 = q2.w;
return x1*x2 + y1*y2 + z1*z2 + w1*w2;
N3D.Math.Quaternion.Slerp = function(q1,q2,time,threshold){
// make sure we use the short rotation
if (angle <= (1-threshold)){ // spherical interpolation
var theta = Math.acos(angle);
var invsintheta = 1/Math.sin(theta);
var scale = Math.sin(theta * (1-time)) * invsintheta;
var invscale = Math.sin(theta * time) * invsintheta;
return new N3D_M_Quaternion(
q1.x*scale + q2.x*invscale,
q1.y*scale + q2.y*invscale,
q1.z*scale + q2.z*invscale,
q1.w*scale + q2.w*invscale
return N3D_M_Quaternion.Lerp(q1,q2,time);
N3D.Math.Quaternion.CreateFromAngles2 = function(x,y,z){
x *= 0.5, y *= 0.5, z *= 0.5;
var sx = Math.sin(x), cx = Math.cos(x),
sy = Math.sin(y), cy = Math.cos(y),
sz = Math.sin(z), cz = Math.cos(z),
cycz = cy * cz, sycz = sy * cz,
cysz = cy * sz, sysz= sy * sz;
return new N3D_M_Quaternion(
N3D.Math.Quaternion.CreateFromAngles3 = function(x,y,z){
x *= 0.5, y *= 0.5, z *= 0.5;
var c1 = Math.cos(y), s1 = Math.sin(y),
c2 = Math.cos(z), s2 = Math.sin(z),
c3 = Math.cos(x), s3 = Math.sin(x),
c1c2 = c1*c2, s1s2 = s1*s2;
return new N3D_M_Quaternion(
N3D.Math.Quaternion.CreateFromAngles4 = function(x,y,z){
x *= 0.5, y *= 0.5, z *= 0.5, w = 0;
var cx = Math.cos(x), sx = Math.sin(x),
cy = Math.cos(y), sy = Math.sin(y),
cz = Math.cos(z), sz = Math.sin(z);
return new N3D_M_Quaternion(
$M4 = N3D_M_Matrix4 = N3D.Math.Matrix4;
$M3 = N3D_M_Matrix3 = N3D.Math.Matrix3;
$V2 = N3D_M_Vector2 = N3D.Math.Vector2;
$V3 = N3D_M_Vector3 = N3D.Math.Vector3;
$V4 = N3D_M_Vector4 = N3D.Math.Vector4;
N3D_M_Quaternion = N3D.Math.Quaternion;
goog.require('goog.math.Vec3');
goog.require('goog.math.Matrix');
//N3D.require("Math.Vector3");
let invnorm = 1.0/Math.sqrt(xx+yy+zz);
let ax = this.x, ay = this.y, az = this.z;
let bx = v2.x, by = v2.y, bz = v2.z;
this.x = ay * bz - az * by;
this.y = az * bx - ax * bz;
this.z = ax * by - ay * bx;