function Maths3D() {} Maths3D.Vector2 = function(x, y) { this.x = 0; this.y = 0; if (x) { this.x = x; } if (y) { this.y = y; } }; Maths3D.Vector3 = function(x, y, z) { this.x = 0; this.y = 0; this.z = 0; if (x) { this.x = x; } if (y) { this.y = y; } if (z) { this.z = z; } }; Maths3D.Vector4 = function(x, y, z, w) { this.x = 0; this.y = 0; this.z = 0; this.w = 0; if (x) { this.x = x; } if (y) { this.y = y; } if (z) { this.z = z; } if (w) { this.w = w; } }; Maths3D.Matrix3 = function( row0, row1, row2 ) { this.identity(); if (row0) { this.rows[0] = row0; } if (row1) { this.rows[1] = row1; } if (row2) { this.rows[2] = row2; } }; Maths3D.Matrix4 = function( row0, row1, row2, row3 ) { this.identity(); if (row0) { this.rows[0] = row0; } if (row1) { this.rows[1] = row1; } if (row2) { this.rows[2] = row2; } if (row3) { this.rows[3] = row3; } }; Maths3D.Color = function(r, g, b, a) { this.r = r; this.g = g; this.b = b; this.a = a; }; Maths3D.Vector2.prototype.copy = function() { return new Maths3D.Vector2( this.x, this.y ); }; Maths3D.Vector2.prototype.equals = function(v) { return !!(((this.x == v.x) && (this.y == v.y)) || ((this.y == v.x) && (this.x == v.y))); }; Maths3D.Matrix4.prototype.rotationMatrixX = function (radians) { this.identity(); this.rows[1] = new Maths3D.Vector4(0, Math.cos(radians), -1 * Math.sin(radians), 0); this.rows[2] = new Maths3D.Vector4(0, Math.sin(radians), Math.cos(radians), 0); return this; }; Maths3D.Matrix4.prototype.rotationMatrixY = function (radians) { this.identity(); this.rows[0] = new Maths3D.Vector4( Math.cos(radians), 0, Math.sin(radians), 0 ); this.rows[2] = new Maths3D.Vector4( -1 * Math.sin(radians), 0, Math.cos(radians), 0 ); return this; }; Maths3D.Matrix4.prototype.rotationMatrixZ = function (radians) { this.identity(); this.rows[0] = new Maths3D.Vector4(Math.cos(radians), -1 * Math.sin(radians), 0, 0); this.rows[1] = new Maths3D.Vector4(Math.sin(radians), Math.cos(radians), 0, 0); return this; }; Maths3D.Matrix4.prototype.rotateX = function (radians, point) { this.identity(); this.rotationMatrixX(radians); return this.multiply(point); }; Maths3D.Matrix4.prototype.rotateY = function (radians, point) { this.identity(); this.rotationMatrixY(radians); return this.multiply(point); }; Maths3D.Matrix4.prototype.rotateZ = function (radians, point) { this.identity(); this.rotationMatrixZ(radians); return this.multiply(point); }; Maths3D.Matrix4.prototype.multiply = function (mvp) { if (mvp instanceof Maths3D.Vector4) { return new Maths3D.Vector4( this.rows[0].x * mvp.x + this.rows[0].y * mvp.y + this.rows[0].z * mvp.z + this.rows[0].w * mvp.w, this.rows[1].x * mvp.x + this.rows[1].y * mvp.y + this.rows[1].z * mvp.z + this.rows[1].w * mvp.w, this.rows[2].x * mvp.x + this.rows[2].y * mvp.y + this.rows[2].z * mvp.z + this.rows[2].w * mvp.w, this.rows[3].x * mvp.x + this.rows[3].y * mvp.y + this.rows[3].z * mvp.z + this.rows[3].w * mvp.w ); } else if (mvp instanceof Maths3D.Vector3) { return new Maths3D.Vector3( this.rows[0].x * mvp.x + this.rows[0].y * mvp.y + this.rows[0].z * mvp.z, this.rows[1].x * mvp.x + this.rows[1].y * mvp.y + this.rows[1].z * mvp.z, this.rows[2].x * mvp.x + this.rows[2].y * mvp.y + this.rows[2].z * mvp.z ); } }; Maths3D.Vector4.Points = function () { this.vectors = []; }; Maths3D.Vector4.Points.prototype.add = function (vector) { if (vector instanceof Maths3D.Vector3) { vector = new Maths3D.Vector4( vector.x, vector.y, vector.z, 1 ) } if (!vector instanceof Maths3D.Vector4) { console.warn("Vector needs to be of type Vector4"); throw new Error("Vector needs to be of type Vector4"); } this.vectors.push(vector); return this; }; Maths3D.Vector4.Points.prototype.copy = function () { var vectors = []; for (var i = 0; i < this.vectors.length; i++) { vectors.push(this.vectors[i].copy()); } return vectors; }; Maths3D.Vector4.Points.prototype.maximizeXDistance = function (grain) { // console.log("vectors (before): " + JSON.stringify(this.vectors, null, 2)); var multiplier = 0; var rotationMatrixY = new Maths3D.Matrix4().rotationMatrixY(grain); var totalRadians = 0; var backupVectors = this.copy(); var maxXDistance = 0; for (var i = 0; i < Math.PI * 2; i += grain) { multiplier++; for (var j = 0; j < this.vectors.length; j++) { this.vectors[j] = rotationMatrixY.multiply(this.vectors[j]); } var distances = this.distances(); if (distances.x > maxXDistance) { maxXDistance = distances.x; totalRadians = multiplier * grain; } } this.vectors = backupVectors; // console.log("distance: " + maxXDistance + " radians : " + totalRadians); var maxRotationMatrix = new Maths3D.Matrix4().rotationMatrixY(totalRadians); for (var k = 0; k < this.vectors.length; k++) { this.vectors[k] = maxRotationMatrix.multiply(this.vectors[k]); } // console.log("vectors (after): " + JSON.stringify(this.vectors, null, 2)); }; Maths3D.Vector4.Points.prototype.maximizeYDistance = function (grain) { // console.log("vectors (before): " + JSON.stringify(this.vectors, null, 2)); var multiplier = 0; var rotationMatrixX = new Maths3D.Matrix4().rotationMatrixX(grain); var totalRadians = 0; var backupVectors = this.copy(); var maxYDistance = 0; for (var i = 0; i < Math.PI * 2; i += grain) { multiplier++; for (var j = 0; j < this.vectors.length; j++) { this.vectors[j] = rotationMatrixX.multiply(this.vectors[j]); } var distances = this.distances(); if (distances.y > maxYDistance) { maxYDistance = distances.y; totalRadians = multiplier * grain; } } this.vectors = backupVectors; // console.log("distance: " + maxYDistance + " radians : " + totalRadians); var maxRotationMatrix = new Maths3D.Matrix4().rotationMatrixX(totalRadians); for (var k = 0; k < this.vectors.length; k++) { this.vectors[k] = maxRotationMatrix.multiply(this.vectors[k]); } // console.log("vectors (after): " + JSON.stringify(this.vectors, null, 2)); }; Maths3D.Vector4.Points.prototype.lookAt = function (at, up) { var polyCenter = this.average(); console.log("poly center : " + JSON.stringify(polyCenter)); var lookAtMatrix = new Maths3D.Matrix4().lookAt(polyCenter, at, up); lookAtMatrix.rows[0] = new Maths3D.Vector4(1, 0, 0, 0); lookAtMatrix.rows[1] = new Maths3D.Vector4(0, 0, 1, 0); lookAtMatrix.rows[2] = new Maths3D.Vector4(0, 1, 0, 0); console.log("look at matrix : " + JSON.stringify(lookAtMatrix, null, 2)); for (var i = 0; i < this.vectors.length; i++) { console.log("vector " + i + " (before): " + JSON.stringify(this.vectors[i])); this.vectors[i] = lookAtMatrix.multiply(this.vectors[i]); console.log("vector " + i + " (after) : " + JSON.stringify(this.vectors[i])); } }; Maths3D.Vector4.Points.prototype.distances = function () { var minX = this.vectors[0].x; var minY = this.vectors[0].y; var minZ = this.vectors[0].z; var maxX = this.vectors[0].x; var maxY = this.vectors[0].y; var maxZ = this.vectors[0].z; for (var i = 0; i < this.vectors.length; i++) { if (this.vectors[i].x < minX) { minX = this.vectors[i].x; } if (this.vectors[i].y < minY) { minY = this.vectors[i].y; } if (this.vectors[i].z < minZ) { minZ = this.vectors[i].z; } if (this.vectors[i].x > maxX) { maxX = this.vectors[i].x; } if (this.vectors[i].y > maxY) { maxY = this.vectors[i].y; } if (this.vectors[i].z > maxZ) { maxZ = this.vectors[i].z; } } return new Maths3D.Vector3( Math.abs(maxX - minX), Math.abs(maxY - minY), Math.abs(maxY - minZ) ) }; Maths3D.Vector4.Points.prototype.average = function () { var averageX = 0; var averageY = 0; var averageZ = 0; for (var i = 0; i < this.vectors.length; i++) { averageX += this.vectors[i].x; averageY += this.vectors[i].y; averageZ += this.vectors[i].z; } return new Maths3D.Vector3( averageX / this.vectors.length, averageY / this.vectors.length, averageZ / this.vectors.length ); }; Maths3D.Vector4.Points.prototype.negative = function () { for (var i = 0; i < this.vectors.length; i++) { this.vectors[i].x *= -1; this.vectors[i].y *= -1; this.vectors[i].z *= -1; } return this; }; Maths3D.Vector4.Points.prototype.toOrigin = function () { var distanceFromOrigin = this.average().negative(); for (var i = 0; i < this.vectors.length; i++) { this.vectors[i].translate(distanceFromOrigin); } }; Maths3D.Vector3.clockwise = function (u, v, w, viewPoint) { var normal = Maths3D.Vector3.normal(u, v, w); var uv = u.copy(); var winding = normal.dot(uv.subtract(viewPoint)); return (winding > 0); }; Maths3D.Vector3.normal = function (u, v, w) { var vv = v.copy(); var wv = w.copy(); return vv.subtract(u).cross(wv.subtract(u)); }; Maths3D.Vector3.prototype.lookAt = function (at, up) { var lookAtMatrix = Maths3D.Matrix4.lookAt(this, at, up); this.multiply(lookAtMatrix); }; Maths3D.Vector3.prototype.translate = function (v) { this.x += v.x; this.y += v.y; this.z += v.z; return this; }; Maths3D.Vector4.prototype.translate = function (v) { this.x += v.x; this.y += v.y; this.z += v.z; return this; }; Maths3D.Vector3.prototype.squared = function () { return this.x * this.x + this.y * this.y + this.z * this.z; }; Maths3D.Vector3.prototype.copy = function () { return new Maths3D.Vector3( this.x, this.y, this.z ); }; Maths3D.Vector4.prototype.copy = function () { return new Maths3D.Vector4( this.x, this.y, this.z, this.w ); }; Maths3D.Vector3.prototype.multiply = function (s) { if (s instanceof Maths3D.Vector3) { this.x *= s.x; this.y *= s.y; this.z *= s.z; } else if (s instanceof Maths3D.Matrix4) { var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z; var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z; var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z; this.x = x; this.y = y; this.z = z; } else { console.log("functionality not implemented - please do this"); throw new Error("not implemented"); } return this; }; Maths3D.Vector4.prototype.multiply = function (s) { if (s instanceof Maths3D.Vector3) { this.x *= s.x; this.y *= s.y; this.z *= s.z; } else if (s instanceof Maths3D.Matrix4) { var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z + s.rows[0].w * this.w; var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z + s.rows[1].w * this.w; var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z + s.rows[2].w * this.w; var w = s.rows[3].x * this.x + s.rows[3].y * this.y + s.rows[3].z * this.z + s.rows[3].w * this.w; this.x = x; this.y = y; this.z = z; this.w = w; } else { console.log("functionality not implemented - please do this"); throw new Error("not implemented"); } }; Maths3D.Vector3.prototype.dot = function (v) { return (this.x * v.x) + (this.y * v.y) + (this.z * v.z); }; Maths3D.Vector3.prototype.normalize = function () { var EPSILON = 0.000001; var v2 = this.squared(); if (v2 < EPSILON) { return this; //do nothing for zero vector } var invLength = 1 / Math.sqrt(v2); this.x *= invLength; this.y *= invLength; this.z *= invLength; return this; }; Maths3D.Vector4.prototype.normalize = function () { // note - leave w untouched var EPSILON = 0.000001; var v2 = this.x * this.x + this.y * this.y + this.z * this.z; if (v2 < EPSILON) { return this; //do nothing for zero vector } var invLength = 1 / Math.sqrt(v2); this.x *= invLength; this.y *= invLength; this.z *= invLength; return this; }; Maths3D.Matrix3.prototype.identity = function () { this.rows = [ new Maths3D.Vector4(1, 0, 0), new Maths3D.Vector4(0, 1, 0), new Maths3D.Vector4(0, 0, 1) ]; }; Maths3D.Matrix4.prototype.identity = function () { this.rows = [ new Maths3D.Vector4(1, 0, 0, 0), new Maths3D.Vector4(0, 1, 0, 0), new Maths3D.Vector4(0, 0, 1, 0), new Maths3D.Vector4(0, 0, 0, 1) ]; }; Maths3D.Matrix4.prototype.lookAt = function (position, target, up) { var pv = new Maths3D.Vector3(position.x, position.y, position.z); var z = pv.subtract(target).normalize(); if (z.squared() === 0) { z.z = 1; } var x = up.cross(z).normalize(); if (x.squared() === 0) { z.x += 0.0001; x = up.cross(z).normalize(); } var y = z.cross(x); this.rows[0].x = x.x; this.rows[0].y = x.y; this.rows[0].z = x.z; this.rows[1].x = y.x; this.rows[1].y = y.y; this.rows[1].z = y.z; this.rows[2].x = z.x; this.rows[2].y = z.y; this.rows[2].z = z.z; return this; // te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; // te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; // te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; // var matrix4 = new Matrix4(); // // matrix4.rows[0] = side.negative(); // matrix4.rows[1] = _up; // matrix4.rows[2] = forward; // // matrix4.setColumn(0, side.negative()); // matrix4.setColumn(1, _up); // matrix4.setColumn(2, forward); //return matrix4; // return new Matrix4( // new Vector4( // side.x, // side.y, // side.z, // side.negative().dot(position) // ), // new Vector4( // _up.x, // _up.y, // _up.z, // _up.negative().dot(position) // ), // new Vector4( // forward.negative().x, // forward.negative().y, // forward.negative().z, // forward.dot(position) // ) // ) }; Maths3D.Vector3.prototype.negative = function () { this.x *= -1; this.y *= -1; this.z *= -1; return this; }; Maths3D.Vector3.prototype.magnitude = function () { return this.x + this.y + this.z; }; Maths3D.Vector4.prototype.subtract = function (v) { if (v instanceof Maths3D.Vector3) { this.x -= v.x; this.y -= v.y; this.z -= v.z; } if (v instanceof Maths3D.Vector4) { this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; } return this; }; Maths3D.Vector3.prototype.subtract = function (v) { if (v instanceof Maths3D.Vector3) { this.x -= v.x; this.y -= v.y; this.z -= v.z; } if (v instanceof Maths3D.Vector4) { console.warn("trying to subtract vector of bigger length (4 vs 3))"); } return this; }; Maths3D.Vector3.prototype.cross = function (v) { return new Maths3D.Vector3( this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x ); }; if (typeof module !== 'undefined') { module.exports = Maths3D; }