From 17fbac49cfde7221c842e88551727bd9dbdd53ee Mon Sep 17 00:00:00 2001 From: liteng <930372551@qq.com> Date: Sat, 8 Dec 2018 07:44:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=B7=E8=B4=9Dthree.js=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- THREE/Math/Box3.cs | 668 ++++++++++++++++++++++++++ THREE/Math/Color.cs | 664 ++++++++++++++++++++++++++ THREE/Math/Cylindrical.cs | 56 +++ THREE/Math/Euler.cs | 390 +++++++++++++++ THREE/Math/Frustum.cs | 196 ++++++++ THREE/Math/Interpolant.cs | 249 ++++++++++ THREE/Math/Line3.cs | 156 ++++++ THREE/Math/Matrix3.cs | 386 +++++++++++++++ THREE/Math/Matrix4.cs | 965 ++++++++++++++++++++++++++++++++++++++ THREE/Math/Plane.cs | 259 ++++++++++ THREE/Math/Quaternion.cs | 689 +++++++++++++++++++++++++++ THREE/Math/Ray.cs | 604 ++++++++++++++++++++++++ THREE/Math/Sphere.cs | 188 ++++++++ THREE/Math/Spherical.cs | 80 ++++ THREE/Math/Triangle.cs | 372 +++++++++++++++ THREE/Math/Vector4.cs | 684 +++++++++++++++++++++++++++ 16 files changed, 6606 insertions(+) diff --git a/THREE/Math/Box3.cs b/THREE/Math/Box3.cs index c289c958..9b42ba6d 100644 --- a/THREE/Math/Box3.cs +++ b/THREE/Math/Box3.cs @@ -8,6 +8,674 @@ namespace THREE { public class Box3 { + public Box3(min, max ) + { + + this.min = (min !== undefined) ? min : new Vector3(+Infinity, +Infinity, +Infinity); + this.max = (max !== undefined) ? max : new Vector3(-Infinity, -Infinity, -Infinity); + + } + + isBox3: true, + + set: function(min, max ) + { + + this.min.copy(min); + this.max.copy(max); + + return this; + + }, + + setFromArray: function(array ) + { + + var minX = +Infinity; + var minY = +Infinity; + var minZ = +Infinity; + + var maxX = -Infinity; + var maxY = -Infinity; + var maxZ = -Infinity; + + for (var i = 0, l = array.length; i < l; i += 3) + { + + var x = array[i]; + var y = array[i + 1]; + var z = array[i + 2]; + + if (x < minX) minX = x; + if (y < minY) minY = y; + if (z < minZ) minZ = z; + + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + if (z > maxZ) maxZ = z; + + } + + this.min.set(minX, minY, minZ); + this.max.set(maxX, maxY, maxZ); + + return this; + + }, + + setFromBufferAttribute: function(attribute ) + { + + var minX = +Infinity; + var minY = +Infinity; + var minZ = +Infinity; + + var maxX = -Infinity; + var maxY = -Infinity; + var maxZ = -Infinity; + + for (var i = 0, l = attribute.count; i < l; i++) + { + + var x = attribute.getX(i); + var y = attribute.getY(i); + var z = attribute.getZ(i); + + if (x < minX) minX = x; + if (y < minY) minY = y; + if (z < minZ) minZ = z; + + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + if (z > maxZ) maxZ = z; + + } + + this.min.set(minX, minY, minZ); + this.max.set(maxX, maxY, maxZ); + + return this; + + }, + + setFromPoints: function(points ) + { + + this.makeEmpty(); + + for (var i = 0, il = points.length; i < il; i++) + { + + this.expandByPoint(points[i]); + + } + + return this; + + }, + + setFromCenterAndSize: function() + { + + var v1 = new Vector3(); + + return function setFromCenterAndSize(center, size) { + + var halfSize = v1.copy(size).multiplyScalar(0.5); + + this.min.copy(center).sub(halfSize); + this.max.copy(center).add(halfSize); + + return this; + + }; + + } + (), + + setFromObject: function(object ) + { + + this.makeEmpty(); + + return this.expandByObject(object); + + }, + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(box ) + { + + this.min.copy(box.min); + this.max.copy(box.max); + + return this; + + }, + + makeEmpty: function() + { + + this.min.x = this.min.y = this.min.z = +Infinity; + this.max.x = this.max.y = this.max.z = -Infinity; + + return this; + + }, + + isEmpty: function() + { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return (this.max.x < this.min.x) || (this.max.y < this.min.y) || (this.max.z < this.min.z); + + }, + + getCenter: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Box3: .getCenter() target is now required'); + target = new Vector3(); + + } + + return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); + + }, + + getSize: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Box3: .getSize() target is now required'); + target = new Vector3(); + + } + + return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); + + }, + + expandByPoint: function(point ) + { + + this.min.min(point); + this.max.max(point); + + return this; + + }, + + expandByVector: function(vector ) + { + + this.min.sub(vector); + this.max.add(vector); + + return this; + + }, + + expandByScalar: function(scalar ) + { + + this.min.addScalar(-scalar); + this.max.addScalar(scalar); + + return this; + + }, + + expandByObject: function() + { + + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms + + var scope, i, l; + + var v1 = new Vector3(); + + function traverse(node ) + { + + var geometry = node.geometry; + + if (geometry !== undefined) + { + + if (geometry.isGeometry) + { + + var vertices = geometry.vertices; + + for (i = 0, l = vertices.length; i < l; i++) + { + + v1.copy(vertices[i]); + v1.applyMatrix4(node.matrixWorld); + + scope.expandByPoint(v1); + + } + + } + else if (geometry.isBufferGeometry) + { + + var attribute = geometry.attributes.position; + + if (attribute !== undefined) + { + + for (i = 0, l = attribute.count; i < l; i++) + { + + v1.fromBufferAttribute(attribute, i).applyMatrix4(node.matrixWorld); + + scope.expandByPoint(v1); + + } + + } + + } + + } + + } + + return function expandByObject(object) { + + scope = this; + + object.updateMatrixWorld(true); + + object.traverse(traverse); + + return this; + + }; + + } + (), + + containsPoint: function(point ) + { + + return point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ? false : true; + + }, + + containsBox: function(box ) + { + + return this.min.x <= box.min.x && box.max.x <= this.max.x && + this.min.y <= box.min.y && box.max.y <= this.max.y && + this.min.z <= box.min.z && box.max.z <= this.max.z; + + }, + + getParameter: function(point, target ) + { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + if (target === undefined) + { + + console.warn('THREE.Box3: .getParameter() target is now required'); + target = new Vector3(); + + } + + return target.set( + (point.x - this.min.x) / (this.max.x - this.min.x), + (point.y - this.min.y) / (this.max.y - this.min.y), + (point.z - this.min.z) / (this.max.z - this.min.z) + ); + + }, + + intersectsBox: function(box ) + { + + // using 6 splitting planes to rule out intersections. + return box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ? false : true; + + }, + + intersectsSphere: (function () { + + var closestPoint = new Vector3(); + + return function intersectsSphere(sphere ) + { + + // Find the point on the AABB closest to the sphere center. + this.clampPoint(sphere.center, closestPoint); + + // If that point is inside the sphere, the AABB and sphere intersect. + return closestPoint.distanceToSquared(sphere.center) <= (sphere.radius * sphere.radius); + + }; + + } )(), + + intersectsPlane: function(plane ) + { + + // We compute the minimum and maximum dot product values. If those values + // are on the same side (back or front) of the plane, then there is no intersection. + + var min, max; + + if (plane.normal.x > 0) + { + + min = plane.normal.x * this.min.x; + max = plane.normal.x * this.max.x; + + } + else + { + + min = plane.normal.x * this.max.x; + max = plane.normal.x * this.min.x; + + } + + if (plane.normal.y > 0) + { + + min += plane.normal.y * this.min.y; + max += plane.normal.y * this.max.y; + + } + else + { + + min += plane.normal.y * this.max.y; + max += plane.normal.y * this.min.y; + + } + + if (plane.normal.z > 0) + { + + min += plane.normal.z * this.min.z; + max += plane.normal.z * this.max.z; + + } + else + { + + min += plane.normal.z * this.max.z; + max += plane.normal.z * this.min.z; + + } + + return (min <= plane.constant && max >= plane.constant); + + }, + + intersectsTriangle: (function () { + + // triangle centered vertices + var v0 = new Vector3(); + var v1 = new Vector3(); + var v2 = new Vector3(); + + // triangle edge vectors + var f0 = new Vector3(); + var f1 = new Vector3(); + var f2 = new Vector3(); + + var testAxis = new Vector3(); + + var center = new Vector3(); + var extents = new Vector3(); + + var triangleNormal = new Vector3(); + + function satForAxes(axes ) + { + + var i, j; + + for (i = 0, j = axes.length - 3; i <= j; i += 3) + { + + testAxis.fromArray(axes, i); + // project the aabb onto the seperating axis + var r = extents.x * Math.abs(testAxis.x) + extents.y * Math.abs(testAxis.y) + extents.z * Math.abs(testAxis.z); + // project all 3 vertices of the triangle onto the seperating axis + var p0 = v0.dot(testAxis); + var p1 = v1.dot(testAxis); + var p2 = v2.dot(testAxis); + // actual test, basically see if either of the most extreme of the triangle points intersects r + if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) + { + + // points of the projected triangle are outside the projected half-length of the aabb + // the axis is seperating and we can exit + return false; + + } + + } + + return true; + } + + return function intersectsTriangle(triangle ) + { + + if (this.isEmpty()) + { + + return false; + + } + + // compute box center and extents + this.getCenter(center); + extents.subVectors(this.max, center); + + // translate triangle to aabb origin + v0.subVectors(triangle.a, center); + v1.subVectors(triangle.b, center); + v2.subVectors(triangle.c, center); + + // compute edge vectors for triangle + f0.subVectors(v1, v0); + f1.subVectors(v2, v1); + f2.subVectors(v0, v2); + + // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb + // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation + // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) + var axes = [ + 0, -f0.z, f0.y, 0, -f1.z, f1.y, 0, -f2.z, f2.y, + f0.z, 0, -f0.x, f1.z, 0, -f1.x, f2.z, 0, -f2.x, + -f0.y, f0.x, 0, -f1.y, f1.x, 0, -f2.y, f2.x, 0 + ]; + if (!satForAxes(axes)) + { + + return false; + + } + + // test 3 face normals from the aabb + axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; + if (!satForAxes(axes)) + { + + return false; + + } + + // finally testing the face normal of the triangle + // use already existing triangle edge vectors here + triangleNormal.crossVectors(f0, f1); + axes = [triangleNormal.x, triangleNormal.y, triangleNormal.z]; + return satForAxes(axes); + + }; + +} )(), + + clampPoint: function(point, target ) +{ + + if (target === undefined) + { + + console.warn('THREE.Box3: .clampPoint() target is now required'); + target = new Vector3(); + + } + + return target.copy(point).clamp(this.min, this.max); + +}, + + distanceToPoint: function() +{ + + var v1 = new Vector3(); + + return function distanceToPoint(point) { + + var clampedPoint = v1.copy(point).clamp(this.min, this.max); + return clampedPoint.sub(point).length(); + + }; + +} +(), + + getBoundingSphere: function() +{ + + var v1 = new Vector3(); + + return function getBoundingSphere(target) { + + if (target === undefined) + { + + console.warn('THREE.Box3: .getBoundingSphere() target is now required'); + target = new Sphere(); + + } + + this.getCenter(target.center); + + target.radius = this.getSize(v1).length() * 0.5; + + return target; + + }; + +} +(), + + intersect: function(box ) +{ + + this.min.max(box.min); + this.max.min(box.max); + + // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. + if (this.isEmpty()) this.makeEmpty(); + + return this; + +}, + + union: function(box ) +{ + + this.min.min(box.min); + this.max.max(box.max); + + return this; + +}, + + applyMatrix4: function() +{ + + var points = [ + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3() + ]; + + return function applyMatrix4(matrix) { + + // transform of empty box is an empty box. + if (this.isEmpty()) return this; + + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 + points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 + points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 + points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 + points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 + points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 + points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 + points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 + + this.setFromPoints(points); + + return this; + + }; + +} +(), + + translate: function(offset ) +{ + + this.min.add(offset); + this.max.add(offset); + + return this; + +}, + + equals: function(box ) +{ + + return box.min.equals(this.min) && box.max.equals(this.max); + +} } } diff --git a/THREE/Math/Color.cs b/THREE/Math/Color.cs index 49806afb..0685df0b 100644 --- a/THREE/Math/Color.cs +++ b/THREE/Math/Color.cs @@ -8,6 +8,670 @@ namespace THREE { public class Color { + var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, + 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, + 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, + 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, + 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, + 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, + 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, + 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, + 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, + 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, + 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, + 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, + 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, + 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, + 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, + 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, + 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, + 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, + 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, + 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, + 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, + 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, + 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, + 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; + + public Color(r, g, b ) + { + + if (g === undefined && b === undefined) + { + + // r is THREE.Color, hex or string + return this.set(r); + + } + + return this.setRGB(r, g, b); + + } + + isColor: true, + + r: 1, g: 1, b: 1, + + set: function(value ) + { + + if (value && value.isColor) + { + + this.copy(value); + + } + else if (typeof value === 'number') + { + + this.setHex(value); + + } + else if (typeof value === 'string') + { + + this.setStyle(value); + + } + + return this; + + }, + + setScalar: function(scalar ) + { + + this.r = scalar; + this.g = scalar; + this.b = scalar; + + return this; + + }, + + setHex: function(hex ) + { + + hex = Math.floor(hex); + + this.r = (hex >> 16 & 255) / 255; + this.g = (hex >> 8 & 255) / 255; + this.b = (hex & 255) / 255; + + return this; + + }, + + setRGB: function(r, g, b ) + { + + this.r = r; + this.g = g; + this.b = b; + + return this; + + }, + + setHSL: function() + { + + function hue2rgb(p, q, t ) + { + + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); + return p; + + } + + return function setHSL(h, s, l) { + + // h,s,l ranges are in 0.0 - 1.0 + h = _Math.euclideanModulo(h, 1); + s = _Math.clamp(s, 0, 1); + l = _Math.clamp(l, 0, 1); + + if (s === 0) + { + + this.r = this.g = this.b = l; + + } + else + { + + var p = l <= 0.5 ? l * (1 + s) : l + s - (l * s); + var q = (2 * l) - p; + + this.r = hue2rgb(q, p, h + 1 / 3); + this.g = hue2rgb(q, p, h); + this.b = hue2rgb(q, p, h - 1 / 3); + + } + + return this; + + }; + + } + (), + + setStyle: function(style ) + { + + function handleAlpha(string ) + { + + if (string === undefined) return; + + if (parseFloat(string) < 1) + { + + console.warn('THREE.Color: Alpha component of ' + style + ' will be ignored.'); + + } + + } + + + var m; + + if (m = /^ ((?: rgb | hsl)a ?)\(\s * ([^\)]*)\)/.exec(style) ) { + + // rgb / hsl + + var color; + var name = m[1]; + var components = m[2]; + + switch (name) + { + + case 'rgb': + case 'rgba': + + if (color = /^ (\d +)\s *,\s * (\d +)\s *,\s * (\d +)\s * (,\s * ([0 - 9] *\.?[0-9]+)\s*)?$/.exec(components ) ) { + + // rgb(255,0,0) rgba(255,0,0,0.5) + this.r = Math.min( 255, parseInt(color[1], 10 ) ) / 255; + this.g = Math.min( 255, parseInt(color[2], 10 ) ) / 255; + this.b = Math.min( 255, parseInt(color[3], 10 ) ) / 255; + + handleAlpha(color[5] ); + + return this; + + } + + if (color = /^(\d+)\%\s*,\s* (\d+)\%\s*,\s* (\d+)\%\s* (,\s* ([0 - 9]*\.?[0 - 9]+)\s*)?$/.exec(components ) ) { + + // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) + this.r = Math.min(100, parseInt(color[1], 10)) / 100; + + this.g = Math.min(100, parseInt(color[2], 10)) / 100; + + this.b = Math.min(100, parseInt(color[3], 10)) / 100; + + + handleAlpha(color[5] ); + + return this; + + + } + + break; + + case 'hsl': + case 'hsla': + + if (color = /^([0 - 9]*\.?[0 - 9]+)\s*,\s* (\d+)\%\s*,\s* (\d+)\%\s* (,\s* ([0 - 9]*\.?[0 - 9]+)\s*)?$/.exec(components ) ) { + + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) + var h = parseFloat(color[1]) / 360; + var s = parseInt(color[2], 10) / 100; + var l = parseInt(color[3], 10) / 100; + + handleAlpha(color[5] ); + + return this.setHSL(h, s, l); + + + } + + break; + + } + + } else if (m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { + + // hex color + + var hex = m[1]; +var size = hex.length; + + if (size === 3 ) { + + // #ff0 + this.r = parseInt(hex.charAt(0) + hex.charAt(0), 16) / 255; + + this.g = parseInt(hex.charAt(1) + hex.charAt(1), 16) / 255; + + this.b = parseInt(hex.charAt(2) + hex.charAt(2), 16) / 255; + + return this; + + + } else if (size === 6 ) { + + // #ff0000 + this.r = parseInt(hex.charAt(0) + hex.charAt(1), 16) / 255; + + this.g = parseInt(hex.charAt(2) + hex.charAt(3), 16) / 255; + + this.b = parseInt(hex.charAt(4) + hex.charAt(5), 16) / 255; + + return this; + + + } + + } + + if (style && style.length > 0 ) { + + // color keywords + var hex = ColorKeywords[style]; + + if (hex !== undefined ) { + + // red + this.setHex(hex ); + + + } else { + + // unknown color + console.warn( 'THREE.Color: Unknown color ' + style ); + + } + + } + + return this; + + + }, + + clone: function() +{ + + return new this.constructor(this.r, this.g, this.b); + +}, + + copy: function(color ) +{ + + this.r = color.r; + this.g = color.g; + this.b = color.b; + + return this; + +}, + + copyGammaToLinear: function(color, gammaFactor ) +{ + + if (gammaFactor === undefined) gammaFactor = 2.0; + + this.r = Math.pow(color.r, gammaFactor); + this.g = Math.pow(color.g, gammaFactor); + this.b = Math.pow(color.b, gammaFactor); + + return this; + +}, + + copyLinearToGamma: function(color, gammaFactor ) +{ + + if (gammaFactor === undefined) gammaFactor = 2.0; + + var safeInverse = (gammaFactor > 0) ? (1.0 / gammaFactor) : 1.0; + + this.r = Math.pow(color.r, safeInverse); + this.g = Math.pow(color.g, safeInverse); + this.b = Math.pow(color.b, safeInverse); + + return this; + +}, + + convertGammaToLinear: function(gammaFactor ) +{ + + this.copyGammaToLinear(this, gammaFactor); + + return this; + +}, + + convertLinearToGamma: function(gammaFactor ) +{ + + this.copyLinearToGamma(this, gammaFactor); + + return this; + +}, + + copySRGBToLinear: function() +{ + + function SRGBToLinear(c ) + { + + return (c < 0.04045) ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } + + return function copySRGBToLinear(color) { + + this.r = SRGBToLinear(color.r); + this.g = SRGBToLinear(color.g); + this.b = SRGBToLinear(color.b); + + return this; + + }; + +} +(), + + copyLinearToSRGB: function() +{ + + function LinearToSRGB(c ) + { + + return (c < 0.0031308) ? c * 12.92 : 1.055 * (Math.pow(c, 0.41666)) - 0.055; + + } + + return function copyLinearToSRGB(color) { + + this.r = LinearToSRGB(color.r); + this.g = LinearToSRGB(color.g); + this.b = LinearToSRGB(color.b); + + return this; + + }; + +} +(), + + convertSRGBToLinear: function() +{ + + this.copySRGBToLinear(this); + + return this; + +}, + + convertLinearToSRGB: function() +{ + + this.copyLinearToSRGB(this); + + return this; + +}, + + getHex: function() +{ + + return (this.r * 255) << 16 ^ (this.g * 255) << 8 ^ (this.b * 255) << 0; + +}, + + getHexString: function() +{ + + return ('000000' + this.getHex().toString(16)).slice(-6); + +}, + + getHSL: function(target ) +{ + + // h,s,l ranges are in 0.0 - 1.0 + + if (target === undefined) + { + + console.warn('THREE.Color: .getHSL() target is now required'); + target = { h: 0, s: 0, l: 0 }; + + } + + var r = this.r, g = this.g, b = this.b; + + var max = Math.max(r, g, b); + var min = Math.min(r, g, b); + + var hue, saturation; + var lightness = (min + max) / 2.0; + + if (min === max) + { + + hue = 0; + saturation = 0; + + } + else + { + + var delta = max - min; + + saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); + + switch (max) + { + + case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; + case g: hue = (b - r) / delta + 2; break; + case b: hue = (r - g) / delta + 4; break; + + } + + hue /= 6; + + } + + target.h = hue; + target.s = saturation; + target.l = lightness; + + return target; + +}, + + getStyle: function() +{ + + return 'rgb(' + ((this.r * 255) | 0) + ',' + ((this.g * 255) | 0) + ',' + ((this.b * 255) | 0) + ')'; + +}, + + offsetHSL: function() +{ + + var hsl = { }; + + return function(h, s, l) { + + this.getHSL(hsl); + + hsl.h += h; hsl.s += s; hsl.l += l; + + this.setHSL(hsl.h, hsl.s, hsl.l); + + return this; + + }; + +} +(), + + add: function(color ) +{ + + this.r += color.r; + this.g += color.g; + this.b += color.b; + + return this; + +}, + + addColors: function(color1, color2 ) +{ + + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; + + return this; + +}, + + addScalar: function(s ) +{ + + this.r += s; + this.g += s; + this.b += s; + + return this; + +}, + + sub: function(color ) +{ + + this.r = Math.max(0, this.r - color.r); + this.g = Math.max(0, this.g - color.g); + this.b = Math.max(0, this.b - color.b); + + return this; + +}, + + multiply: function(color ) +{ + + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; + + return this; + +}, + + multiplyScalar: function(s ) +{ + + this.r *= s; + this.g *= s; + this.b *= s; + + return this; + +}, + + lerp: function(color, alpha ) +{ + + this.r += (color.r - this.r) * alpha; + this.g += (color.g - this.g) * alpha; + this.b += (color.b - this.b) * alpha; + + return this; + +}, + + lerpHSL: function() +{ + + var hslA = { h: 0, s: 0, l: 0 }; +var hslB = { h: 0, s: 0, l: 0 }; + + return function lerpHSL(color, alpha ) +{ + + this.getHSL(hslA); + color.getHSL(hslB); + + var h = _Math.lerp(hslA.h, hslB.h, alpha); + var s = _Math.lerp(hslA.s, hslB.s, alpha); + var l = _Math.lerp(hslA.l, hslB.l, alpha); + + this.setHSL(h, s, l); + + return this; + +}; + + }(), + + equals: function(c ) +{ + + return (c.r === this.r) && (c.g === this.g) && (c.b === this.b); + +}, + + fromArray: function(array, offset ) +{ + + if (offset === undefined) offset = 0; + + this.r = array[offset]; + this.g = array[offset + 1]; + this.b = array[offset + 2]; + + return this; + +}, + + toArray: function(array, offset ) +{ + + if (array === undefined) array = []; + if (offset === undefined) offset = 0; + + array[offset] = this.r; + array[offset + 1] = this.g; + array[offset + 2] = this.b; + + return array; + +}, + + toJSON: function() +{ + + return this.getHex(); + +} + } } diff --git a/THREE/Math/Cylindrical.cs b/THREE/Math/Cylindrical.cs index 84e39408..c4ccc213 100644 --- a/THREE/Math/Cylindrical.cs +++ b/THREE/Math/Cylindrical.cs @@ -8,6 +8,62 @@ namespace THREE { public class Cylindrical { + function Cylindrical(radius, theta, y ) + { + this.radius = (radius !== undefined) ? radius : 1.0; // distance from the origin to a point in the x-z plane + this.theta = (theta !== undefined) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis + this.y = (y !== undefined) ? y : 0; // height above the x-z plane + + return this; + + } + + set: function(radius, theta, y ) + { + + this.radius = radius; + this.theta = theta; + this.y = y; + + return this; + + }, + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(other ) + { + + this.radius = other.radius; + this.theta = other.theta; + this.y = other.y; + + return this; + + }, + + setFromVector3: function(v ) + { + + return this.setFromCartesianCoords(v.x, v.y, v.z); + + }, + + setFromCartesianCoords: function(x, y, z ) + { + + this.radius = Math.sqrt(x * x + z * z); + this.theta = Math.atan2(x, z); + this.y = y; + + return this; + + } } } diff --git a/THREE/Math/Euler.cs b/THREE/Math/Euler.cs index 4dd59944..0a90b63e 100644 --- a/THREE/Math/Euler.cs +++ b/THREE/Math/Euler.cs @@ -8,6 +8,396 @@ namespace THREE { public class Euler { + Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +Euler.DefaultOrder = 'XYZ'; + + function Euler(x, y, z, order ) + { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || Euler.DefaultOrder; + + } + + x: { + + get: function() + { + + return this._x; + + }, + + set: function(value ) + { + + this._x = value; + this.onChangeCallback(); + + } + + }, + + y: { + + get: function() + { + + return this._y; + + }, + + set: function(value ) + { + + this._y = value; + this.onChangeCallback(); } + +}, + + z: { + + get: function() +{ + + return this._z; + +}, + + set: function(value ) +{ + + this._z = value; + this.onChangeCallback(); + +} + + }, + + order: { + + get: function() +{ + + return this._order; + +}, + + set: function(value ) +{ + + this._order = value; + this.onChangeCallback(); + +} + + } + +} ); + +Object.assign(Euler.prototype, { + + isEuler: true, + + set: function(x, y, z, order ) +{ + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + +}, + + clone: function() +{ + + return new this.constructor(this._x, this._y, this._z, this._order); + +}, + + copy: function(euler ) +{ + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + +}, + + setFromRotationMatrix: function(m, order, update ) +{ + + var clamp = _Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[0], m12 = te[4], m13 = te[8]; + var m21 = te[1], m22 = te[5], m23 = te[9]; + var m31 = te[2], m32 = te[6], m33 = te[10]; + + order = order || this._order; + + if (order === 'XYZ') + { + + this._y = Math.asin(clamp(m13, -1, 1)); + + if (Math.abs(m13) < 0.99999) + { + + this._x = Math.atan2(-m23, m33); + this._z = Math.atan2(-m12, m11); + + } + else + { + + this._x = Math.atan2(m32, m22); + this._z = 0; + + } + + } + else if (order === 'YXZ') + { + + this._x = Math.asin(-clamp(m23, -1, 1)); + + if (Math.abs(m23) < 0.99999) + { + + this._y = Math.atan2(m13, m33); + this._z = Math.atan2(m21, m22); + + } + else + { + + this._y = Math.atan2(-m31, m11); + this._z = 0; + + } + + } + else if (order === 'ZXY') + { + + this._x = Math.asin(clamp(m32, -1, 1)); + + if (Math.abs(m32) < 0.99999) + { + + this._y = Math.atan2(-m31, m33); + this._z = Math.atan2(-m12, m22); + + } + else + { + + this._y = 0; + this._z = Math.atan2(m21, m11); + + } + + } + else if (order === 'ZYX') + { + + this._y = Math.asin(-clamp(m31, -1, 1)); + + if (Math.abs(m31) < 0.99999) + { + + this._x = Math.atan2(m32, m33); + this._z = Math.atan2(m21, m11); + + } + else + { + + this._x = 0; + this._z = Math.atan2(-m12, m22); + + } + + } + else if (order === 'YZX') + { + + this._z = Math.asin(clamp(m21, -1, 1)); + + if (Math.abs(m21) < 0.99999) + { + + this._x = Math.atan2(-m23, m22); + this._y = Math.atan2(-m31, m11); + + } + else + { + + this._x = 0; + this._y = Math.atan2(m13, m33); + + } + + } + else if (order === 'XZY') + { + + this._z = Math.asin(-clamp(m12, -1, 1)); + + if (Math.abs(m12) < 0.99999) + { + + this._x = Math.atan2(m32, m22); + this._y = Math.atan2(m13, m11); + + } + else + { + + this._x = Math.atan2(-m23, m33); + this._y = 0; + + } + + } + else + { + + console.warn('THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order); + + } + + this._order = order; + + if (update !== false) this.onChangeCallback(); + + return this; + +}, + + setFromQuaternion: function() +{ + + var matrix = new Matrix4(); + + return function setFromQuaternion(q, order, update) { + + matrix.makeRotationFromQuaternion(q); + + return this.setFromRotationMatrix(matrix, order, update); + + }; + +} +(), + + setFromVector3: function(v, order ) +{ + + return this.set(v.x, v.y, v.z, order || this._order); + +}, + + reorder: function() +{ + + // WARNING: this discards revolution information -bhouston + + var q = new Quaternion(); + + return function reorder(newOrder) { + + q.setFromEuler(this); + + return this.setFromQuaternion(q, newOrder); + + }; + +} +(), + + equals: function(euler ) +{ + + return (euler._x === this._x) && (euler._y === this._y) && (euler._z === this._z) && (euler._order === this._order); + +}, + + fromArray: function(array ) +{ + + this._x = array[0]; + this._y = array[1]; + this._z = array[2]; + if (array[3] !== undefined) this._order = array[3]; + + this.onChangeCallback(); + + return this; + +}, + + toArray: function(array, offset ) +{ + + if (array === undefined) array = []; + if (offset === undefined) offset = 0; + + array[offset] = this._x; + array[offset + 1] = this._y; + array[offset + 2] = this._z; + array[offset + 3] = this._order; + + return array; + +}, + + toVector3: function(optionalResult ) +{ + + if (optionalResult) + { + + return optionalResult.set(this._x, this._y, this._z); + + } + else + { + + return new Vector3(this._x, this._y, this._z); + + } + +}, + + onChange: function(callback ) +{ + + this.onChangeCallback = callback; + + return this; + +}, + + onChangeCallback: function() { } + } } diff --git a/THREE/Math/Frustum.cs b/THREE/Math/Frustum.cs index 5400b9b9..e9420f96 100644 --- a/THREE/Math/Frustum.cs +++ b/THREE/Math/Frustum.cs @@ -8,6 +8,202 @@ namespace THREE { public class Frustum { + function Frustum(p0, p1, p2, p3, p4, p5 ) + { + this.planes = [ + + (p0 !== undefined) ? p0 : new Plane(), + (p1 !== undefined) ? p1 : new Plane(), + (p2 !== undefined) ? p2 : new Plane(), + (p3 !== undefined) ? p3 : new Plane(), + (p4 !== undefined) ? p4 : new Plane(), + (p5 !== undefined) ? p5 : new Plane() + + ]; + + } + + set: function(p0, p1, p2, p3, p4, p5 ) + { + + var planes = this.planes; + + planes[0].copy(p0); + planes[1].copy(p1); + planes[2].copy(p2); + planes[3].copy(p3); + planes[4].copy(p4); + planes[5].copy(p5); + + return this; + + }, + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(frustum ) + { + + var planes = this.planes; + + for (var i = 0; i < 6; i++) + { + + planes[i].copy(frustum.planes[i]); + + } + + return this; + + }, + + setFromMatrix: function(m ) + { + + var planes = this.planes; + var me = m.elements; + var me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; + var me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; + var me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; + var me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; + + planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); + planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); + planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); + planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); + planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); + planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); + + return this; + + }, + + intersectsObject: function() + { + + var sphere = new Sphere(); + + return function intersectsObject(object) { + + var geometry = object.geometry; + + if (geometry.boundingSphere === null) + geometry.computeBoundingSphere(); + + sphere.copy(geometry.boundingSphere) + .applyMatrix4(object.matrixWorld); + + return this.intersectsSphere(sphere); + + }; + + } + (), + + intersectsSprite: function() + { + + var sphere = new Sphere(); + + return function intersectsSprite(sprite) { + + sphere.center.set(0, 0, 0); + sphere.radius = 0.7071067811865476; + sphere.applyMatrix4(sprite.matrixWorld); + + return this.intersectsSphere(sphere); + + }; + + } + (), + + intersectsSphere: function(sphere ) + { + + var planes = this.planes; + var center = sphere.center; + var negRadius = -sphere.radius; + + for (var i = 0; i < 6; i++) + { + + var distance = planes[i].distanceToPoint(center); + + if (distance < negRadius) + { + + return false; + + } + + } + + return true; + + }, + + intersectsBox: function() + { + + var p = new Vector3(); + + return function intersectsBox(box) { + + var planes = this.planes; + + for (var i = 0; i < 6; i++) + { + + var plane = planes[i]; + + // corner at max distance + + p.x = plane.normal.x > 0 ? box.max.x : box.min.x; + p.y = plane.normal.y > 0 ? box.max.y : box.min.y; + p.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + if (plane.distanceToPoint(p) < 0) + { + + return false; + + } + + } + + return true; + + }; + + } + (), + + containsPoint: function(point ) + { + + var planes = this.planes; + + for (var i = 0; i < 6; i++) + { + + if (planes[i].distanceToPoint(point) < 0) + { + + return false; + + } + + } + + return true; + + } } } diff --git a/THREE/Math/Interpolant.cs b/THREE/Math/Interpolant.cs index c82d3335..24bb5d60 100644 --- a/THREE/Math/Interpolant.cs +++ b/THREE/Math/Interpolant.cs @@ -8,6 +8,255 @@ namespace THREE { public class Interpolant { + function Interpolant(parameterPositions, sampleValues, sampleSize, resultBuffer ) + { + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; + + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor(sampleSize); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; + + } + + evaluate: function(t ) + { + + var pp = this.parameterPositions, + i1 = this._cachedIndex, + + t1 = pp[i1], + t0 = pp[i1 - 1]; + + validate_interval: + { + + seek: + { + + var right; + + linear_scan: + { + + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if (!(t < t1)) + { + + for (var giveUpAt = i1 + 2; ;) + { + + if (t1 === undefined) + { + + if (t < t0) break forward_scan; + + // after end + + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_(i1 - 1, t, t0); + + } + + if (i1 === giveUpAt) break; // this loop + + t0 = t1; + t1 = pp[++i1]; + + if (t < t1) + { + + // we have arrived at the sought interval + break seek; + + } + + } + + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; + + } + + //- slower code: + //- if ( t < t0 || t0 === undefined ) { + if (!(t >= t0)) + { + + // looping? + + var t1global = pp[1]; + + if (t < t1global) + { + + i1 = 2; // + 1, using the scan for the details + t0 = t1global; + + } + + // linear reverse scan + + for (var giveUpAt = i1 - 2; ;) + { + + if (t0 === undefined) + { + + // before start + + this._cachedIndex = 0; + return this.beforeStart_(0, t, t1); + + } + + if (i1 === giveUpAt) break; // this loop + + t1 = t0; + t0 = pp[--i1 - 1]; + + if (t >= t0) + { + + // we have arrived at the sought interval + break seek; + + } + + } + + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; + + } + + // the interval is valid + + break validate_interval; + + } // linear scan + + // binary search + + while (i1 < right) + { + + var mid = (i1 + right) >>> 1; + + if (t < pp[mid]) + { + + right = mid; + + } + else + { + + i1 = mid + 1; + + } + + } + + t1 = pp[i1]; + t0 = pp[i1 - 1]; + + // check boundary cases, again + + if (t0 === undefined) + { + + this._cachedIndex = 0; + return this.beforeStart_(0, t, t1); + + } + + if (t1 === undefined) + { + + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_(i1 - 1, t0, t); + + } + + } // seek + + this._cachedIndex = i1; + + this.intervalChanged_(i1, t0, t1); + + } // validate_interval + + return this.interpolate_(i1, t0, t, t1); + + }, + + settings: null, // optional, subclass-specific settings structure + // Note: The indirection allows central control of many interpolants. + + // --- Protected interface + + DefaultSettings_: {}, + + getSettings_: function() + { + + return this.settings || this.DefaultSettings_; + + }, + + copySampleValue_: function(index ) + { + + // copies a sample value to the result buffer + + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; + + for (var i = 0; i !== stride; ++i) + { + + result[i] = values[offset + i]; + + } + + return result; + + }, + + // Template methods for derived classes: + + interpolate_: function( /* i1, t0, t, t1 */ ) + { + + throw new Error('call to abstract method'); + // implementations shall return this.resultBuffer + + }, + + intervalChanged_: function( /* i1, t0, t1 */ ) + { + + // empty + + } + + //( 0, t, t0 ), returns this.resultBuffer + beforeStart_: Interpolant.prototype.copySampleValue_, + + //( N-1, tN-1, t ), returns this.resultBuffer + afterEnd_: Interpolant.prototype.copySampleValue_, } } diff --git a/THREE/Math/Line3.cs b/THREE/Math/Line3.cs index 9102fd35..dffe58e6 100644 --- a/THREE/Math/Line3.cs +++ b/THREE/Math/Line3.cs @@ -8,6 +8,162 @@ namespace THREE { public class Line3 { + function Line3(start, end ) + { + this.start = (start !== undefined) ? start : new Vector3(); + this.end = (end !== undefined) ? end : new Vector3(); + + } + + set: function(start, end ) + { + + this.start.copy(start); + this.end.copy(end); + + return this; + + }, + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(line ) + { + + this.start.copy(line.start); + this.end.copy(line.end); + + return this; + + }, + + getCenter: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Line3: .getCenter() target is now required'); + target = new Vector3(); + + } + + return target.addVectors(this.start, this.end).multiplyScalar(0.5); + + }, + + delta: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Line3: .delta() target is now required'); + target = new Vector3(); + + } + + return target.subVectors(this.end, this.start); + + }, + + distanceSq: function() + { + + return this.start.distanceToSquared(this.end); + + }, + + distance: function() + { + + return this.start.distanceTo(this.end); + + }, + + at: function(t, target ) + { + + if (target === undefined) + { + + console.warn('THREE.Line3: .at() target is now required'); + target = new Vector3(); + + } + + return this.delta(target).multiplyScalar(t).add(this.start); + + }, + + closestPointToPointParameter: function() + { + + var startP = new Vector3(); + var startEnd = new Vector3(); + + return function closestPointToPointParameter(point, clampToLine) { + + startP.subVectors(point, this.start); + startEnd.subVectors(this.end, this.start); + + var startEnd2 = startEnd.dot(startEnd); + var startEnd_startP = startEnd.dot(startP); + + var t = startEnd_startP / startEnd2; + + if (clampToLine) + { + + t = _Math.clamp(t, 0, 1); + + } + + return t; + + }; + + } + (), + + closestPointToPoint: function(point, clampToLine, target ) + { + + var t = this.closestPointToPointParameter(point, clampToLine); + + if (target === undefined) + { + + console.warn('THREE.Line3: .closestPointToPoint() target is now required'); + target = new Vector3(); + + } + + return this.delta(target).multiplyScalar(t).add(this.start); + + }, + + applyMatrix4: function(matrix ) + { + + this.start.applyMatrix4(matrix); + this.end.applyMatrix4(matrix); + + return this; + + }, + + equals: function(line ) + { + + return line.start.equals(this.start) && line.end.equals(this.end); + + } } } diff --git a/THREE/Math/Matrix3.cs b/THREE/Math/Matrix3.cs index 4221baf3..153371a7 100644 --- a/THREE/Math/Matrix3.cs +++ b/THREE/Math/Matrix3.cs @@ -18,5 +18,391 @@ namespace THREE 0, 0, 1 }; } + + isMatrix3: true, + + set: function(n11, n12, n13, n21, n22, n23, n31, n32, n33 ) + { + + var te = this.elements; + + te[0] = n11; te[1] = n21; te[2] = n31; + te[3] = n12; te[4] = n22; te[5] = n32; + te[6] = n13; te[7] = n23; te[8] = n33; + + return this; + + }, + + identity: function() + { + + this.set( + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ); + + return this; + + }, + + clone: function() + { + + return new this.constructor().fromArray(this.elements); + + }, + + copy: function(m ) + { + + var te = this.elements; + var me = m.elements; + + te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; + te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; + te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; + + return this; + + }, + + setFromMatrix4: function(m ) + { + + var me = m.elements; + + this.set( + + me[0], me[4], me[8], + me[1], me[5], me[9], + me[2], me[6], me[10] + + ); + + return this; + + }, + + applyToBufferAttribute: function() + { + + var v1 = new Vector3(); + + return function applyToBufferAttribute(attribute) { + + for (var i = 0, l = attribute.count; i < l; i++) + { + + v1.x = attribute.getX(i); + v1.y = attribute.getY(i); + v1.z = attribute.getZ(i); + + v1.applyMatrix3(this); + + attribute.setXYZ(i, v1.x, v1.y, v1.z); + + } + + return attribute; + + }; + + } + (), + + multiply: function(m ) + { + + return this.multiplyMatrices(this, m); + + }, + + premultiply: function(m ) + { + + return this.multiplyMatrices(m, this); + + }, + + multiplyMatrices: function(a, b ) + { + + var ae = a.elements; + var be = b.elements; + var te = this.elements; + + var a11 = ae[0], a12 = ae[3], a13 = ae[6]; + var a21 = ae[1], a22 = ae[4], a23 = ae[7]; + var a31 = ae[2], a32 = ae[5], a33 = ae[8]; + + var b11 = be[0], b12 = be[3], b13 = be[6]; + var b21 = be[1], b22 = be[4], b23 = be[7]; + var b31 = be[2], b32 = be[5], b33 = be[8]; + + te[0] = a11 * b11 + a12 * b21 + a13 * b31; + te[3] = a11 * b12 + a12 * b22 + a13 * b32; + te[6] = a11 * b13 + a12 * b23 + a13 * b33; + + te[1] = a21 * b11 + a22 * b21 + a23 * b31; + te[4] = a21 * b12 + a22 * b22 + a23 * b32; + te[7] = a21 * b13 + a22 * b23 + a23 * b33; + + te[2] = a31 * b11 + a32 * b21 + a33 * b31; + te[5] = a31 * b12 + a32 * b22 + a33 * b32; + te[8] = a31 * b13 + a32 * b23 + a33 * b33; + + return this; + + }, + + multiplyScalar: function(s ) + { + + var te = this.elements; + + te[0] *= s; te[3] *= s; te[6] *= s; + te[1] *= s; te[4] *= s; te[7] *= s; + te[2] *= s; te[5] *= s; te[8] *= s; + + return this; + + }, + + determinant: function() + { + + var te = this.elements; + + var a = te[0], b = te[1], c = te[2], + d = te[3], e = te[4], f = te[5], + g = te[6], h = te[7], i = te[8]; + + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + + }, + + getInverse: function(matrix, throwOnDegenerate ) + { + + if (matrix && matrix.isMatrix4) + { + + console.error("THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument."); + + } + + var me = matrix.elements, + te = this.elements, + + n11 = me[0], n21 = me[1], n31 = me[2], + n12 = me[3], n22 = me[4], n32 = me[5], + n13 = me[6], n23 = me[7], n33 = me[8], + + t11 = n33 * n22 - n32 * n23, + t12 = n32 * n13 - n33 * n12, + t13 = n23 * n12 - n22 * n13, + + det = n11 * t11 + n21 * t12 + n31 * t13; + + if (det === 0) + { + + var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0"; + + if (throwOnDegenerate === true) + { + + throw new Error(msg); + + } + else + { + + console.warn(msg); + + } + + return this.identity(); + + } + + var detInv = 1 / det; + + te[0] = t11 * detInv; + te[1] = (n31 * n23 - n33 * n21) * detInv; + te[2] = (n32 * n21 - n31 * n22) * detInv; + + te[3] = t12 * detInv; + te[4] = (n33 * n11 - n31 * n13) * detInv; + te[5] = (n31 * n12 - n32 * n11) * detInv; + + te[6] = t13 * detInv; + te[7] = (n21 * n13 - n23 * n11) * detInv; + te[8] = (n22 * n11 - n21 * n12) * detInv; + + return this; + + }, + + transpose: function() + { + + var tmp, m = this.elements; + + tmp = m[1]; m[1] = m[3]; m[3] = tmp; + tmp = m[2]; m[2] = m[6]; m[6] = tmp; + tmp = m[5]; m[5] = m[7]; m[7] = tmp; + + return this; + + }, + + getNormalMatrix: function(matrix4 ) + { + + return this.setFromMatrix4(matrix4).getInverse(this).transpose(); + + }, + + transposeIntoArray: function(r ) + { + + var m = this.elements; + + r[0] = m[0]; + r[1] = m[3]; + r[2] = m[6]; + r[3] = m[1]; + r[4] = m[4]; + r[5] = m[7]; + r[6] = m[2]; + r[7] = m[5]; + r[8] = m[8]; + + return this; + + }, + + setUvTransform: function(tx, ty, sx, sy, rotation, cx, cy ) + { + + var c = Math.cos(rotation); + var s = Math.sin(rotation); + + this.set( + sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, + -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, + 0, 0, 1 + ); + + }, + + scale: function(sx, sy ) + { + + var te = this.elements; + + te[0] *= sx; te[3] *= sx; te[6] *= sx; + te[1] *= sy; te[4] *= sy; te[7] *= sy; + + return this; + + }, + + rotate: function(theta ) + { + + var c = Math.cos(theta); + var s = Math.sin(theta); + + var te = this.elements; + + var a11 = te[0], a12 = te[3], a13 = te[6]; + var a21 = te[1], a22 = te[4], a23 = te[7]; + + te[0] = c * a11 + s * a21; + te[3] = c * a12 + s * a22; + te[6] = c * a13 + s * a23; + + te[1] = -s * a11 + c * a21; + te[4] = -s * a12 + c * a22; + te[7] = -s * a13 + c * a23; + + return this; + + }, + + translate: function(tx, ty ) + { + + var te = this.elements; + + te[0] += tx * te[2]; te[3] += tx * te[5]; te[6] += tx * te[8]; + te[1] += ty * te[2]; te[4] += ty * te[5]; te[7] += ty * te[8]; + + return this; + + }, + + equals: function(matrix ) + { + + var te = this.elements; + var me = matrix.elements; + + for (var i = 0; i < 9; i++) + { + + if (te[i] !== me[i]) return false; + + } + + return true; + + }, + + fromArray: function(array, offset ) + { + + if (offset === undefined) offset = 0; + + for (var i = 0; i < 9; i++) + { + + this.elements[i] = array[i + offset]; + + } + + return this; + + }, + + toArray: function(array, offset ) + { + + if (array === undefined) array = []; + if (offset === undefined) offset = 0; + + var te = this.elements; + + array[offset] = te[0]; + array[offset + 1] = te[1]; + array[offset + 2] = te[2]; + + array[offset + 3] = te[3]; + array[offset + 4] = te[4]; + array[offset + 5] = te[5]; + + array[offset + 6] = te[6]; + array[offset + 7] = te[7]; + array[offset + 8] = te[8]; + + return array; + + } } } diff --git a/THREE/Math/Matrix4.cs b/THREE/Math/Matrix4.cs index 0534a6de..e3092e71 100644 --- a/THREE/Math/Matrix4.cs +++ b/THREE/Math/Matrix4.cs @@ -19,5 +19,970 @@ namespace THREE 0, 0, 0, 1 }; } + + isMatrix4: true, + + set: function(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) + { + + var te = this.elements; + + te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; + te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; + te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; + te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; + + return this; + + }, + + identity: function() + { + + this.set( + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + clone: function() + { + + return new Matrix4().fromArray(this.elements); + + }, + + copy: function(m ) + { + + var te = this.elements; + var me = m.elements; + + te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; + te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; + te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; + te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; + + return this; + + }, + + copyPosition: function(m ) + { + + var te = this.elements, me = m.elements; + + te[12] = me[12]; + te[13] = me[13]; + te[14] = me[14]; + + return this; + + }, + + extractBasis: function(xAxis, yAxis, zAxis ) + { + + xAxis.setFromMatrixColumn(this, 0); + yAxis.setFromMatrixColumn(this, 1); + zAxis.setFromMatrixColumn(this, 2); + + return this; + + }, + + makeBasis: function(xAxis, yAxis, zAxis ) + { + + this.set( + xAxis.x, yAxis.x, zAxis.x, 0, + xAxis.y, yAxis.y, zAxis.y, 0, + xAxis.z, yAxis.z, zAxis.z, 0, + 0, 0, 0, 1 + ); + + return this; + + }, + + extractRotation: function() + { + + var v1 = new Vector3(); + + return function extractRotation(m) { + + // this method does not support reflection matrices + + var te = this.elements; + var me = m.elements; + + var scaleX = 1 / v1.setFromMatrixColumn(m, 0).length(); + var scaleY = 1 / v1.setFromMatrixColumn(m, 1).length(); + var scaleZ = 1 / v1.setFromMatrixColumn(m, 2).length(); + + te[0] = me[0] * scaleX; + te[1] = me[1] * scaleX; + te[2] = me[2] * scaleX; + te[3] = 0; + + te[4] = me[4] * scaleY; + te[5] = me[5] * scaleY; + te[6] = me[6] * scaleY; + te[7] = 0; + + te[8] = me[8] * scaleZ; + te[9] = me[9] * scaleZ; + te[10] = me[10] * scaleZ; + te[11] = 0; + + te[12] = 0; + te[13] = 0; + te[14] = 0; + te[15] = 1; + + return this; + + }; + + } + (), + + makeRotationFromEuler: function(euler ) + { + + if (!(euler && euler.isEuler)) + { + + console.error('THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.'); + + } + + var te = this.elements; + + var x = euler.x, y = euler.y, z = euler.z; + var a = Math.cos(x), b = Math.sin(x); + var c = Math.cos(y), d = Math.sin(y); + var e = Math.cos(z), f = Math.sin(z); + + if (euler.order === 'XYZ') + { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[0] = c * e; + te[4] = -c * f; + te[8] = d; + + te[1] = af + be * d; + te[5] = ae - bf * d; + te[9] = -b * c; + + te[2] = bf - ae * d; + te[6] = be + af * d; + te[10] = a * c; + + } + else if (euler.order === 'YXZ') + { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[0] = ce + df * b; + te[4] = de * b - cf; + te[8] = a * d; + + te[1] = a * f; + te[5] = a * e; + te[9] = -b; + + te[2] = cf * b - de; + te[6] = df + ce * b; + te[10] = a * c; + + } + else if (euler.order === 'ZXY') + { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[0] = ce - df * b; + te[4] = -a * f; + te[8] = de + cf * b; + + te[1] = cf + de * b; + te[5] = a * e; + te[9] = df - ce * b; + + te[2] = -a * d; + te[6] = b; + te[10] = a * c; + + } + else if (euler.order === 'ZYX') + { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[0] = c * e; + te[4] = be * d - af; + te[8] = ae * d + bf; + + te[1] = c * f; + te[5] = bf * d + ae; + te[9] = af * d - be; + + te[2] = -d; + te[6] = b * c; + te[10] = a * c; + + } + else if (euler.order === 'YZX') + { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[0] = c * e; + te[4] = bd - ac * f; + te[8] = bc * f + ad; + + te[1] = f; + te[5] = a * e; + te[9] = -b * e; + + te[2] = -d * e; + te[6] = ad * f + bc; + te[10] = ac - bd * f; + + } + else if (euler.order === 'XZY') + { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[0] = c * e; + te[4] = -f; + te[8] = d * e; + + te[1] = ac * f + bd; + te[5] = a * e; + te[9] = ad * f - bc; + + te[2] = bc * f - ad; + te[6] = b * e; + te[10] = bd * f + ac; + + } + + // bottom row + te[3] = 0; + te[7] = 0; + te[11] = 0; + + // last column + te[12] = 0; + te[13] = 0; + te[14] = 0; + te[15] = 1; + + return this; + + }, + + makeRotationFromQuaternion: function() + { + + var zero = new Vector3(0, 0, 0); + var one = new Vector3(1, 1, 1); + + return function makeRotationFromQuaternion(q) { + + return this.compose(zero, q, one); + + }; + + } + (), + + lookAt: function() + { + + var x = new Vector3(); + var y = new Vector3(); + var z = new Vector3(); + + return function lookAt(eye, target, up) { + + var te = this.elements; + + z.subVectors(eye, target); + + if (z.lengthSq() === 0) + { + + // eye and target are in the same position + + z.z = 1; + + } + + z.normalize(); + x.crossVectors(up, z); + + if (x.lengthSq() === 0) + { + + // up and z are parallel + + if (Math.abs(up.z) === 1) + { + + z.x += 0.0001; + + } + else + { + + z.z += 0.0001; + + } + + z.normalize(); + x.crossVectors(up, z); + + } + + x.normalize(); + y.crossVectors(z, x); + + 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; + + return this; + + }; + + } + (), + + multiply: function(m, n ) + { + + if (n !== undefined) + { + + console.warn('THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.'); + return this.multiplyMatrices(m, n); + + } + + return this.multiplyMatrices(this, m); + + }, + + premultiply: function(m ) + { + + return this.multiplyMatrices(m, this); + + }, + + multiplyMatrices: function(a, b ) + { + + var ae = a.elements; + var be = b.elements; + var te = this.elements; + + var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; + var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; + var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; + var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; + + var b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; + var b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; + var b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; + var b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; + + te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + + }, + + multiplyScalar: function(s ) + { + + var te = this.elements; + + te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; + te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; + te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; + te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; + + return this; + + }, + + applyToBufferAttribute: function() + { + + var v1 = new Vector3(); + + return function applyToBufferAttribute(attribute) { + + for (var i = 0, l = attribute.count; i < l; i++) + { + + v1.x = attribute.getX(i); + v1.y = attribute.getY(i); + v1.z = attribute.getZ(i); + + v1.applyMatrix4(this); + + attribute.setXYZ(i, v1.x, v1.y, v1.z); + + } + + return attribute; + + }; + + } + (), + + determinant: function() + { + + var te = this.elements; + + var n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; + var n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; + var n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; + var n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return ( + n41 * ( + +n14 * n23 * n32 + - n13 * n24 * n32 + - n14 * n22 * n33 + + n12 * n24 * n33 + + n13 * n22 * n34 + - n12 * n23 * n34 + ) + + n42 * ( + +n11 * n23 * n34 + - n11 * n24 * n33 + + n14 * n21 * n33 + - n13 * n21 * n34 + + n13 * n24 * n31 + - n14 * n23 * n31 + ) + + n43 * ( + +n11 * n24 * n32 + - n11 * n22 * n34 + - n14 * n21 * n32 + + n12 * n21 * n34 + + n14 * n22 * n31 + - n12 * n24 * n31 + ) + + n44 * ( + -n13 * n22 * n31 + - n11 * n23 * n32 + + n11 * n22 * n33 + + n13 * n21 * n32 + - n12 * n21 * n33 + + n12 * n23 * n31 + ) + + ); + + }, + + transpose: function() + { + + var te = this.elements; + var tmp; + + tmp = te[1]; te[1] = te[4]; te[4] = tmp; + tmp = te[2]; te[2] = te[8]; te[8] = tmp; + tmp = te[6]; te[6] = te[9]; te[9] = tmp; + + tmp = te[3]; te[3] = te[12]; te[12] = tmp; + tmp = te[7]; te[7] = te[13]; te[13] = tmp; + tmp = te[11]; te[11] = te[14]; te[14] = tmp; + + return this; + + }, + + setPosition: function(v ) + { + + var te = this.elements; + + te[12] = v.x; + te[13] = v.y; + te[14] = v.z; + + return this; + + }, + + getInverse: function(m, throwOnDegenerate ) + { + + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements, + me = m.elements, + + n11 = me[0], n21 = me[1], n31 = me[2], n41 = me[3], + n12 = me[4], n22 = me[5], n32 = me[6], n42 = me[7], + n13 = me[8], n23 = me[9], n33 = me[10], n43 = me[11], + n14 = me[12], n24 = me[13], n34 = me[14], n44 = me[15], + + t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, + t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, + t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, + t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + + var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + + if (det === 0) + { + + var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0"; + + if (throwOnDegenerate === true) + { + + throw new Error(msg); + + } + else + { + + console.warn(msg); + + } + + return this.identity(); + + } + + var detInv = 1 / det; + + te[0] = t11 * detInv; + te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; + te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; + te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; + + te[4] = t12 * detInv; + te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; + te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; + te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; + + te[8] = t13 * detInv; + te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; + te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; + te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; + + te[12] = t14 * detInv; + te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; + te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; + te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; + + return this; + + }, + + scale: function(v ) + { + + var te = this.elements; + var x = v.x, y = v.y, z = v.z; + + te[0] *= x; te[4] *= y; te[8] *= z; + te[1] *= x; te[5] *= y; te[9] *= z; + te[2] *= x; te[6] *= y; te[10] *= z; + te[3] *= x; te[7] *= y; te[11] *= z; + + return this; + + }, + + getMaxScaleOnAxis: function() + { + + var te = this.elements; + + var scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; + var scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; + var scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; + + return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); + + }, + + makeTranslation: function(x, y, z ) + { + + this.set( + + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationX: function(theta ) + { + + var c = Math.cos(theta), s = Math.sin(theta); + + this.set( + + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationY: function(theta ) + { + + var c = Math.cos(theta), s = Math.sin(theta); + + this.set( + + c, 0, s, 0, + 0, 1, 0, 0, + -s, 0, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationZ: function(theta ) + { + + var c = Math.cos(theta), s = Math.sin(theta); + + this.set( + + c, -s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationAxis: function(axis, angle ) + { + + // Based on http://www.gamedev.net/reference/articles/article1199.asp + + var c = Math.cos(angle); + var s = Math.sin(angle); + var t = 1 - c; + var x = axis.x, y = axis.y, z = axis.z; + var tx = t * x, ty = t * y; + + this.set( + + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeScale: function(x, y, z ) + { + + this.set( + + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeShear: function(x, y, z ) + { + + this.set( + + 1, y, z, 0, + x, 1, z, 0, + x, y, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + compose: function(position, quaternion, scale ) + { + + var te = this.elements; + + var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; + var x2 = x + x, y2 = y + y, z2 = z + z; + var xx = x * x2, xy = x * y2, xz = x * z2; + var yy = y * y2, yz = y * z2, zz = z * z2; + var wx = w * x2, wy = w * y2, wz = w * z2; + + var sx = scale.x, sy = scale.y, sz = scale.z; + + te[0] = (1 - (yy + zz)) * sx; + te[1] = (xy + wz) * sx; + te[2] = (xz - wy) * sx; + te[3] = 0; + + te[4] = (xy - wz) * sy; + te[5] = (1 - (xx + zz)) * sy; + te[6] = (yz + wx) * sy; + te[7] = 0; + + te[8] = (xz + wy) * sz; + te[9] = (yz - wx) * sz; + te[10] = (1 - (xx + yy)) * sz; + te[11] = 0; + + te[12] = position.x; + te[13] = position.y; + te[14] = position.z; + te[15] = 1; + + return this; + + }, + + decompose: function() + { + + var vector = new Vector3(); + var matrix = new Matrix4(); + + return function decompose(position, quaternion, scale) { + + var te = this.elements; + + var sx = vector.set(te[0], te[1], te[2]).length(); + var sy = vector.set(te[4], te[5], te[6]).length(); + var sz = vector.set(te[8], te[9], te[10]).length(); + + // if determine is negative, we need to invert one scale + var det = this.determinant(); + if (det < 0) sx = -sx; + + position.x = te[12]; + position.y = te[13]; + position.z = te[14]; + + // scale the rotation part + matrix.copy(this); + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + + matrix.elements[0] *= invSX; + matrix.elements[1] *= invSX; + matrix.elements[2] *= invSX; + + matrix.elements[4] *= invSY; + matrix.elements[5] *= invSY; + matrix.elements[6] *= invSY; + + matrix.elements[8] *= invSZ; + matrix.elements[9] *= invSZ; + matrix.elements[10] *= invSZ; + + quaternion.setFromRotationMatrix(matrix); + + scale.x = sx; + scale.y = sy; + scale.z = sz; + + return this; + + }; + + } + (), + + makePerspective: function(left, right, top, bottom, near, far ) + { + + if (far === undefined) + { + + console.warn('THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.'); + + } + + var te = this.elements; + var x = 2 * near / (right - left); + var y = 2 * near / (top - bottom); + + var a = (right + left) / (right - left); + var b = (top + bottom) / (top - bottom); + var c = -(far + near) / (far - near); + var d = -2 * far * near / (far - near); + + te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; + te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; + te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; + te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; + + return this; + + }, + + makeOrthographic: function(left, right, top, bottom, near, far ) + { + + var te = this.elements; + var w = 1.0 / (right - left); + var h = 1.0 / (top - bottom); + var p = 1.0 / (far - near); + + var x = (right + left) * w; + var y = (top + bottom) * h; + var z = (far + near) * p; + + te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; + te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; + te[2] = 0; te[6] = 0; te[10] = -2 * p; te[14] = -z; + te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; + + return this; + + }, + + equals: function(matrix ) + { + + var te = this.elements; + var me = matrix.elements; + + for (var i = 0; i < 16; i++) + { + + if (te[i] !== me[i]) return false; + + } + + return true; + + }, + + fromArray: function(array, offset ) + { + + if (offset === undefined) offset = 0; + + for (var i = 0; i < 16; i++) + { + + this.elements[i] = array[i + offset]; + + } + + return this; + + }, + + toArray: function(array, offset ) + { + + if (array === undefined) array = []; + if (offset === undefined) offset = 0; + + var te = this.elements; + + array[offset] = te[0]; + array[offset + 1] = te[1]; + array[offset + 2] = te[2]; + array[offset + 3] = te[3]; + + array[offset + 4] = te[4]; + array[offset + 5] = te[5]; + array[offset + 6] = te[6]; + array[offset + 7] = te[7]; + + array[offset + 8] = te[8]; + array[offset + 9] = te[9]; + array[offset + 10] = te[10]; + array[offset + 11] = te[11]; + + array[offset + 12] = te[12]; + array[offset + 13] = te[13]; + array[offset + 14] = te[14]; + array[offset + 15] = te[15]; + + return array; + + } } } diff --git a/THREE/Math/Plane.cs b/THREE/Math/Plane.cs index e8dc6b75..da82df40 100644 --- a/THREE/Math/Plane.cs +++ b/THREE/Math/Plane.cs @@ -8,6 +8,265 @@ namespace THREE { public class Plane { + function Plane(normal, constant ) + { + // normal is assumed to be normalized + + this.normal = (normal !== undefined) ? normal : new Vector3(1, 0, 0); + this.constant = (constant !== undefined) ? constant : 0; + + } + + set: function(normal, constant ) + { + + this.normal.copy(normal); + this.constant = constant; + + return this; + + }, + + setComponents: function(x, y, z, w ) + { + + this.normal.set(x, y, z); + this.constant = w; + + return this; + + }, + + setFromNormalAndCoplanarPoint: function(normal, point ) + { + + this.normal.copy(normal); + this.constant = -point.dot(this.normal); + + return this; + + }, + + setFromCoplanarPoints: function() + { + + var v1 = new Vector3(); + var v2 = new Vector3(); + + return function setFromCoplanarPoints(a, b, c) { + + var normal = v1.subVectors(c, b).cross(v2.subVectors(a, b)).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint(normal, a); + + return this; + + }; + + } + (), + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(plane ) + { + + this.normal.copy(plane.normal); + this.constant = plane.constant; + + return this; + + }, + + normalize: function() + { + + // Note: will lead to a divide by zero if the plane is invalid. + + var inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar(inverseNormalLength); + this.constant *= inverseNormalLength; + + return this; + + }, + + negate: function() + { + + this.constant *= -1; + this.normal.negate(); + + return this; + + }, + + distanceToPoint: function(point ) + { + + return this.normal.dot(point) + this.constant; + + }, + + distanceToSphere: function(sphere ) + { + + return this.distanceToPoint(sphere.center) - sphere.radius; + + }, + + projectPoint: function(point, target ) + { + + if (target === undefined) + { + + console.warn('THREE.Plane: .projectPoint() target is now required'); + target = new Vector3(); + + } + + return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point); + + }, + + intersectLine: function() + { + + var v1 = new Vector3(); + + return function intersectLine(line, target) { + + if (target === undefined) + { + + console.warn('THREE.Plane: .intersectLine() target is now required'); + target = new Vector3(); + + } + + var direction = line.delta(v1); + + var denominator = this.normal.dot(direction); + + if (denominator === 0) + { + + // line is coplanar, return origin + if (this.distanceToPoint(line.start) === 0) + { + + return target.copy(line.start); + + } + + // Unsure if this is the correct method to handle this case. + return undefined; + + } + + var t = -(line.start.dot(this.normal) + this.constant) / denominator; + + if (t < 0 || t > 1) + { + + return undefined; + + } + + return target.copy(direction).multiplyScalar(t).add(line.start); + + }; + + } + (), + + intersectsLine: function(line ) + { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + var startSign = this.distanceToPoint(line.start); + var endSign = this.distanceToPoint(line.end); + + return (startSign < 0 && endSign > 0) || (endSign < 0 && startSign > 0); + + }, + + intersectsBox: function(box ) + { + + return box.intersectsPlane(this); + + }, + + intersectsSphere: function(sphere ) + { + + return sphere.intersectsPlane(this); + + }, + + coplanarPoint: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Plane: .coplanarPoint() target is now required'); + target = new Vector3(); + + } + + return target.copy(this.normal).multiplyScalar(-this.constant); + + }, + + applyMatrix4: function() + { + + var v1 = new Vector3(); + var m1 = new Matrix3(); + + return function applyMatrix4(matrix, optionalNormalMatrix) { + + var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix(matrix); + + var referencePoint = this.coplanarPoint(v1).applyMatrix4(matrix); + + var normal = this.normal.applyMatrix3(normalMatrix).normalize(); + + this.constant = -referencePoint.dot(normal); + + return this; + + }; + + } + (), + + translate: function(offset ) + { + + this.constant -= offset.dot(this.normal); + + return this; + + }, + + equals: function(plane ) + { + + return plane.normal.equals(this.normal) && (plane.constant === this.constant); + + } } } diff --git a/THREE/Math/Quaternion.cs b/THREE/Math/Quaternion.cs index ee1ad5f6..92f0857b 100644 --- a/THREE/Math/Quaternion.cs +++ b/THREE/Math/Quaternion.cs @@ -8,6 +8,695 @@ namespace THREE { public class Quaternion { + function Quaternion(x, y, z, w ) + { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = (w !== undefined) ? w : 1; + + } + + slerp: function(qa, qb, qm, t ) + { + + return qm.copy(qa).slerp(qb, t); + + }, + + slerpFlat: function(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) + { + + // fuzz-free, array-based Quaternion SLERP operation + + var x0 = src0[srcOffset0 + 0], + y0 = src0[srcOffset0 + 1], + z0 = src0[srcOffset0 + 2], + w0 = src0[srcOffset0 + 3], + + x1 = src1[srcOffset1 + 0], + y1 = src1[srcOffset1 + 1], + z1 = src1[srcOffset1 + 2], + w1 = src1[srcOffset1 + 3]; + + if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) + { + + var s = 1 - t, + + cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + + dir = (cos >= 0 ? 1 : -1), + sqrSin = 1 - cos * cos; + + // Skip the Slerp for tiny steps to avoid numeric problems: + if (sqrSin > Number.EPSILON) + { + + var sin = Math.sqrt(sqrSin), + len = Math.atan2(sin, cos * dir); + + s = Math.sin(s * len) / sin; + t = Math.sin(t * len) / sin; + + } + + var tDir = t * dir; + + x0 = x0 * s + x1 * tDir; + y0 = y0 * s + y1 * tDir; + z0 = z0 * s + z1 * tDir; + w0 = w0 * s + w1 * tDir; + + // Normalize in case we just did a lerp: + if (s === 1 - t) + { + + var f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); + + x0 *= f; + y0 *= f; + z0 *= f; + w0 *= f; + + } + + } + + dst[dstOffset] = x0; + dst[dstOffset + 1] = y0; + dst[dstOffset + 2] = z0; + dst[dstOffset + 3] = w0; + + } + + } ); + +Object.defineProperties(Quaternion.prototype, { + + x: { + + get: function() + { + + return this._x; + + }, + + set: function(value ) + { + + this._x = value; + this.onChangeCallback(); } + +}, + + y: { + + get: function() +{ + + return this._y; + +}, + + set: function(value ) +{ + + this._y = value; + this.onChangeCallback(); + +} + + }, + + z: { + + get: function() +{ + + return this._z; + +}, + + set: function(value ) +{ + + this._z = value; + this.onChangeCallback(); + +} + + }, + + w: { + + get: function() +{ + + return this._w; + +}, + + set: function(value ) +{ + + this._w = value; + this.onChangeCallback(); + +} + + } + +} ); + +Object.assign(Quaternion.prototype, { + + set: function(x, y, z, w ) +{ + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + +}, + + clone: function() +{ + + return new this.constructor(this._x, this._y, this._z, this._w); + +}, + + copy: function(quaternion ) +{ + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + +}, + + setFromEuler: function(euler, update ) +{ + + if (!(euler && euler.isEuler)) + { + + throw new Error('THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.'); + + } + + var x = euler._x, y = euler._y, z = euler._z, order = euler.order; + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var cos = Math.cos; + var sin = Math.sin; + + var c1 = cos(x / 2); + var c2 = cos(y / 2); + var c3 = cos(z / 2); + + var s1 = sin(x / 2); + var s2 = sin(y / 2); + var s3 = sin(z / 2); + + if (order === 'XYZ') + { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } + else if (order === 'YXZ') + { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + else if (order === 'ZXY') + { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } + else if (order === 'ZYX') + { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + else if (order === 'YZX') + { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } + else if (order === 'XZY') + { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if (update !== false) this.onChangeCallback(); + + return this; + +}, + + setFromAxisAngle: function(axis, angle ) +{ + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin(halfAngle); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos(halfAngle); + + this.onChangeCallback(); + + return this; + +}, + + setFromRotationMatrix: function(m ) +{ + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[0], m12 = te[4], m13 = te[8], + m21 = te[1], m22 = te[5], m23 = te[9], + m31 = te[2], m32 = te[6], m33 = te[10], + + trace = m11 + m22 + m33, + s; + + if (trace > 0) + { + + s = 0.5 / Math.sqrt(trace + 1.0); + + this._w = 0.25 / s; + this._x = (m32 - m23) * s; + this._y = (m13 - m31) * s; + this._z = (m21 - m12) * s; + + } + else if (m11 > m22 && m11 > m33) + { + + s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); + + this._w = (m32 - m23) / s; + this._x = 0.25 * s; + this._y = (m12 + m21) / s; + this._z = (m13 + m31) / s; + + } + else if (m22 > m33) + { + + s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); + + this._w = (m13 - m31) / s; + this._x = (m12 + m21) / s; + this._y = 0.25 * s; + this._z = (m23 + m32) / s; + + } + else + { + + s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); + + this._w = (m21 - m12) / s; + this._x = (m13 + m31) / s; + this._y = (m23 + m32) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + +}, + + setFromUnitVectors: function() +{ + + // assumes direction vectors vFrom and vTo are normalized + + var v1 = new Vector3(); + var r; + + var EPS = 0.000001; + + return function setFromUnitVectors(vFrom, vTo) { + + if (v1 === undefined) v1 = new Vector3(); + + r = vFrom.dot(vTo) + 1; + + if (r < EPS) + { + + r = 0; + + if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) + { + + v1.set(-vFrom.y, vFrom.x, 0); + + } + else + { + + v1.set(0, -vFrom.z, vFrom.y); + + } + + } + else + { + + v1.crossVectors(vFrom, vTo); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + return this.normalize(); + + }; + +} +(), + + angleTo: function(q ) +{ + + return 2 * Math.acos(Math.abs(_Math.clamp(this.dot(q), -1, 1))); + +}, + + rotateTowards: function(q, step ) +{ + + var angle = this.angleTo(q); + + if (angle === 0) return this; + + var t = Math.min(1, step / angle); + + this.slerp(q, t); + + return this; + +}, + + inverse: function() +{ + + // quaternion is assumed to have unit length + + return this.conjugate(); + +}, + + conjugate: function() +{ + + this._x *= -1; + this._y *= -1; + this._z *= -1; + + this.onChangeCallback(); + + return this; + +}, + + dot: function(v ) +{ + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + +}, + + lengthSq: function() +{ + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + +}, + + length: function() +{ + + return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); + +}, + + normalize: function() +{ + + var l = this.length(); + + if (l === 0) + { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } + else + { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + +}, + + multiply: function(q, p ) +{ + + if (p !== undefined) + { + + console.warn('THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.'); + return this.multiplyQuaternions(q, p); + + } + + return this.multiplyQuaternions(this, q); + +}, + + premultiply: function(q ) +{ + + return this.multiplyQuaternions(q, this); + +}, + + multiplyQuaternions: function(a, b ) +{ + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + +}, + + slerp: function(qb, t ) +{ + + if (t === 0) return this; + if (t === 1) return this.copy(qb); + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if (cosHalfTheta < 0) + { + + this._w = -qb._w; + this._x = -qb._x; + this._y = -qb._y; + this._z = -qb._z; + + cosHalfTheta = -cosHalfTheta; + + } + else + { + + this.copy(qb); + + } + + if (cosHalfTheta >= 1.0) + { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; + + if (sqrSinHalfTheta <= Number.EPSILON) + { + + var s = 1 - t; + this._w = s * w + t * this._w; + this._x = s * x + t * this._x; + this._y = s * y + t * this._y; + this._z = s * z + t * this._z; + + return this.normalize(); + + } + + var sinHalfTheta = Math.sqrt(sqrSinHalfTheta); + var halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); + var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, + ratioB = Math.sin(t * halfTheta) / sinHalfTheta; + + this._w = (w * ratioA + this._w * ratioB); + this._x = (x * ratioA + this._x * ratioB); + this._y = (y * ratioA + this._y * ratioB); + this._z = (z * ratioA + this._z * ratioB); + + this.onChangeCallback(); + + return this; + +}, + + equals: function(quaternion ) +{ + + return (quaternion._x === this._x) && (quaternion._y === this._y) && (quaternion._z === this._z) && (quaternion._w === this._w); + +}, + + fromArray: function(array, offset ) +{ + + if (offset === undefined) offset = 0; + + this._x = array[offset]; + this._y = array[offset + 1]; + this._z = array[offset + 2]; + this._w = array[offset + 3]; + + this.onChangeCallback(); + + return this; + +}, + + toArray: function(array, offset ) +{ + + if (array === undefined) array = []; + if (offset === undefined) offset = 0; + + array[offset] = this._x; + array[offset + 1] = this._y; + array[offset + 2] = this._z; + array[offset + 3] = this._w; + + return array; + +}, + + onChange: function(callback ) +{ + + this.onChangeCallback = callback; + + return this; + +}, + + onChangeCallback: function() { } + } } diff --git a/THREE/Math/Ray.cs b/THREE/Math/Ray.cs index 6e7fc108..825b94f4 100644 --- a/THREE/Math/Ray.cs +++ b/THREE/Math/Ray.cs @@ -8,6 +8,610 @@ namespace THREE { public class Ray { + function Ray(origin, direction ) + { + + this.origin = (origin !== undefined) ? origin : new Vector3(); + this.direction = (direction !== undefined) ? direction : new Vector3(); + + } + + set: function(origin, direction ) + { + + this.origin.copy(origin); + this.direction.copy(direction); + + return this; + + }, + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(ray ) + { + + this.origin.copy(ray.origin); + this.direction.copy(ray.direction); + + return this; + + }, + + at: function(t, target ) + { + + if (target === undefined) + { + + console.warn('THREE.Ray: .at() target is now required'); + target = new Vector3(); + + } + + return target.copy(this.direction).multiplyScalar(t).add(this.origin); + + }, + + lookAt: function(v ) + { + + this.direction.copy(v).sub(this.origin).normalize(); + + return this; + + }, + + recast: function() + { + + var v1 = new Vector3(); + + return function recast(t) { + + this.origin.copy(this.at(t, v1)); + + return this; + + }; + + } + (), + + closestPointToPoint: function(point, target ) + { + + if (target === undefined) + { + + console.warn('THREE.Ray: .closestPointToPoint() target is now required'); + target = new Vector3(); + + } + + target.subVectors(point, this.origin); + + var directionDistance = target.dot(this.direction); + + if (directionDistance < 0) + { + + return target.copy(this.origin); + + } + + return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); + + }, + + distanceToPoint: function(point ) + { + + return Math.sqrt(this.distanceSqToPoint(point)); + + }, + + distanceSqToPoint: function() + { + + var v1 = new Vector3(); + + return function distanceSqToPoint(point) { + + var directionDistance = v1.subVectors(point, this.origin).dot(this.direction); + + // point behind the ray + + if (directionDistance < 0) + { + + return this.origin.distanceToSquared(point); + + } + + v1.copy(this.direction).multiplyScalar(directionDistance).add(this.origin); + + return v1.distanceToSquared(point); + + }; + + } + (), + + distanceSqToSegment: function() + { + + var segCenter = new Vector3(); + var segDir = new Vector3(); + var diff = new Vector3(); + + return function distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { + + // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment + + segCenter.copy(v0).add(v1).multiplyScalar(0.5); + segDir.copy(v1).sub(v0).normalize(); + diff.copy(this.origin).sub(segCenter); + + var segExtent = v0.distanceTo(v1) * 0.5; + var a01 = -this.direction.dot(segDir); + var b0 = diff.dot(this.direction); + var b1 = -diff.dot(segDir); + var c = diff.lengthSq(); + var det = Math.abs(1 - a01 * a01); + var s0, s1, sqrDist, extDet; + + if (det > 0) + { + + // The ray and segment are not parallel. + + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; + + if (s0 >= 0) + { + + if (s1 >= -extDet) + { + + if (s1 <= extDet) + { + + // region 0 + // Minimum at interior points of ray and segment. + + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; + + } + else + { + + // region 1 + + s1 = segExtent; + s0 = Math.max(0, -(a01 * s1 + b0)); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + + } + + } + else + { + + // region 5 + + s1 = -segExtent; + s0 = Math.max(0, -(a01 * s1 + b0)); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + + } + + } + else + { + + if (s1 <= -extDet) + { + + // region 4 + + s0 = Math.max(0, -(-a01 * segExtent + b0)); + s1 = (s0 > 0) ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + + } + else if (s1 <= extDet) + { + + // region 3 + + s0 = 0; + s1 = Math.min(Math.max(-segExtent, -b1), segExtent); + sqrDist = s1 * (s1 + 2 * b1) + c; + + } + else + { + + // region 2 + + s0 = Math.max(0, -(a01 * segExtent + b0)); + s1 = (s0 > 0) ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + + } + + } + + } + else + { + + // Ray and segment are parallel. + + s1 = (a01 > 0) ? -segExtent : segExtent; + s0 = Math.max(0, -(a01 * s1 + b0)); + sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; + + } + + if (optionalPointOnRay) + { + + optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin); + + } + + if (optionalPointOnSegment) + { + + optionalPointOnSegment.copy(segDir).multiplyScalar(s1).add(segCenter); + + } + + return sqrDist; + + }; + + } + (), + + intersectSphere: function() + { + + var v1 = new Vector3(); + + return function intersectSphere(sphere, target) { + + v1.subVectors(sphere.center, this.origin); + var tca = v1.dot(this.direction); + var d2 = v1.dot(v1) - tca * tca; + var radius2 = sphere.radius * sphere.radius; + + if (d2 > radius2) return null; + + var thc = Math.sqrt(radius2 - d2); + + // t0 = first intersect point - entrance on front of sphere + var t0 = tca - thc; + + // t1 = second intersect point - exit point on back of sphere + var t1 = tca + thc; + + // test to see if both t0 and t1 are behind the ray - if so, return null + if (t0 < 0 && t1 < 0) return null; + + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if (t0 < 0) return this.at(t1, target); + + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at(t0, target); + + }; + + } + (), + + intersectsSphere: function(sphere ) + { + + return this.distanceSqToPoint(sphere.center) <= (sphere.radius * sphere.radius); + + }, + + distanceToPlane: function(plane ) + { + + var denominator = plane.normal.dot(this.direction); + + if (denominator === 0) + { + + // line is coplanar, return origin + if (plane.distanceToPoint(this.origin) === 0) + { + + return 0; + + } + + // Null is preferable to undefined since undefined means.... it is undefined + + return null; + + } + + var t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; + + // Return if the ray never intersects the plane + + return t >= 0 ? t : null; + + }, + + intersectPlane: function(plane, target ) + { + + var t = this.distanceToPlane(plane); + + if (t === null) + { + + return null; + + } + + return this.at(t, target); + + }, + + intersectsPlane: function(plane ) + { + + // check if the ray lies on the plane first + + var distToPoint = plane.distanceToPoint(this.origin); + + if (distToPoint === 0) + { + + return true; + + } + + var denominator = plane.normal.dot(this.direction); + + if (denominator * distToPoint < 0) + { + + return true; + + } + + // ray origin is behind the plane (and is pointing behind it) + + return false; + + }, + + intersectBox: function(box, target ) + { + + var tmin, tmax, tymin, tymax, tzmin, tzmax; + + var invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; + + var origin = this.origin; + + if (invdirx >= 0) + { + + tmin = (box.min.x - origin.x) * invdirx; + tmax = (box.max.x - origin.x) * invdirx; + + } + else + { + + tmin = (box.max.x - origin.x) * invdirx; + tmax = (box.min.x - origin.x) * invdirx; + + } + + if (invdiry >= 0) + { + + tymin = (box.min.y - origin.y) * invdiry; + tymax = (box.max.y - origin.y) * invdiry; + + } + else + { + + tymin = (box.max.y - origin.y) * invdiry; + tymax = (box.min.y - origin.y) * invdiry; + + } + + if ((tmin > tymax) || (tymin > tmax)) return null; + + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN + + if (tymin > tmin || tmin !== tmin) tmin = tymin; + + if (tymax < tmax || tmax !== tmax) tmax = tymax; + + if (invdirz >= 0) + { + + tzmin = (box.min.z - origin.z) * invdirz; + tzmax = (box.max.z - origin.z) * invdirz; + + } + else + { + + tzmin = (box.max.z - origin.z) * invdirz; + tzmax = (box.min.z - origin.z) * invdirz; + + } + + if ((tmin > tzmax) || (tzmin > tmax)) return null; + + if (tzmin > tmin || tmin !== tmin) tmin = tzmin; + + if (tzmax < tmax || tmax !== tmax) tmax = tzmax; + + //return point closest to the ray (positive side) + + if (tmax < 0) return null; + + return this.at(tmin >= 0 ? tmin : tmax, target); + + }, + + intersectsBox: (function () { + + var v = new Vector3(); + + return function intersectsBox(box ) + { + + return this.intersectBox(box, v) !== null; + + }; + + } )(), + + intersectTriangle: function() + { + + // Compute the offset origin, edges, and normal. + var diff = new Vector3(); + var edge1 = new Vector3(); + var edge2 = new Vector3(); + var normal = new Vector3(); + + return function intersectTriangle(a, b, c, backfaceCulling, target) { + + // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + + edge1.subVectors(b, a); + edge2.subVectors(c, a); + normal.crossVectors(edge1, edge2); + + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + var DdN = this.direction.dot(normal); + var sign; + + if (DdN > 0) + { + + if (backfaceCulling) return null; + sign = 1; + + } + else if (DdN < 0) + { + + sign = -1; + DdN = -DdN; + + } + else + { + + return null; + + } + + diff.subVectors(this.origin, a); + var DdQxE2 = sign * this.direction.dot(edge2.crossVectors(diff, edge2)); + + // b1 < 0, no intersection + if (DdQxE2 < 0) + { + + return null; + + } + + var DdE1xQ = sign * this.direction.dot(edge1.cross(diff)); + + // b2 < 0, no intersection + if (DdE1xQ < 0) + { + + return null; + + } + + // b1+b2 > 1, no intersection + if (DdQxE2 + DdE1xQ > DdN) + { + + return null; + + } + + // Line intersects triangle, check if ray does. + var QdN = -sign * diff.dot(normal); + + // t < 0, no intersection + if (QdN < 0) + { + + return null; + + } + + // Ray intersects triangle. + return this.at(QdN / DdN, target); + + }; + + } + (), + + applyMatrix4: function(matrix4 ) + { + + this.origin.applyMatrix4(matrix4); + this.direction.transformDirection(matrix4); + + return this; + + }, + + equals: function(ray ) + { + + return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } } +} diff --git a/THREE/Math/Sphere.cs b/THREE/Math/Sphere.cs index f2c7e4f9..2a1f9179 100644 --- a/THREE/Math/Sphere.cs +++ b/THREE/Math/Sphere.cs @@ -8,6 +8,194 @@ namespace THREE { public class Sphere { + function Sphere(center, radius ) + { + this.center = (center !== undefined) ? center : new Vector3(); + this.radius = (radius !== undefined) ? radius : 0; + + } + + set: function(center, radius ) + { + + this.center.copy(center); + this.radius = radius; + + return this; + + }, + + setFromPoints: function() + { + + var box = new Box3(); + + return function setFromPoints(points, optionalCenter) { + + var center = this.center; + + if (optionalCenter !== undefined) + { + + center.copy(optionalCenter); + + } + else + { + + box.setFromPoints(points).getCenter(center); + + } + + var maxRadiusSq = 0; + + for (var i = 0, il = points.length; i < il; i++) + { + + maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); + + } + + this.radius = Math.sqrt(maxRadiusSq); + + return this; + + }; + + } + (), + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(sphere ) + { + + this.center.copy(sphere.center); + this.radius = sphere.radius; + + return this; + + }, + + empty: function() + { + + return (this.radius <= 0); + + }, + + containsPoint: function(point ) + { + + return (point.distanceToSquared(this.center) <= (this.radius * this.radius)); + + }, + + distanceToPoint: function(point ) + { + + return (point.distanceTo(this.center) - this.radius); + + }, + + intersectsSphere: function(sphere ) + { + + var radiusSum = this.radius + sphere.radius; + + return sphere.center.distanceToSquared(this.center) <= (radiusSum * radiusSum); + + }, + + intersectsBox: function(box ) + { + + return box.intersectsSphere(this); + + }, + + intersectsPlane: function(plane ) + { + + return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; + + }, + + clampPoint: function(point, target ) + { + + var deltaLengthSq = this.center.distanceToSquared(point); + + if (target === undefined) + { + + console.warn('THREE.Sphere: .clampPoint() target is now required'); + target = new Vector3(); + + } + + target.copy(point); + + if (deltaLengthSq > (this.radius * this.radius)) + { + + target.sub(this.center).normalize(); + target.multiplyScalar(this.radius).add(this.center); + + } + + return target; + + }, + + getBoundingBox: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Sphere: .getBoundingBox() target is now required'); + target = new Box3(); + + } + + target.set(this.center, this.center); + target.expandByScalar(this.radius); + + return target; + + }, + + applyMatrix4: function(matrix ) + { + + this.center.applyMatrix4(matrix); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); + + return this; + + }, + + translate: function(offset ) + { + + this.center.add(offset); + + return this; + + }, + + equals: function(sphere ) + { + + return sphere.center.equals(this.center) && (sphere.radius === this.radius); + + } } } diff --git a/THREE/Math/Spherical.cs b/THREE/Math/Spherical.cs index 6292fa63..9c4c112a 100644 --- a/THREE/Math/Spherical.cs +++ b/THREE/Math/Spherical.cs @@ -8,6 +8,86 @@ namespace THREE { public class Spherical { + function Spherical(radius, phi, theta ) + { + this.radius = (radius !== undefined) ? radius : 1.0; + this.phi = (phi !== undefined) ? phi : 0; // polar angle + this.theta = (theta !== undefined) ? theta : 0; // azimuthal angle + + return this; + + } + + set: function(radius, phi, theta ) + { + + this.radius = radius; + this.phi = phi; + this.theta = theta; + + return this; + + }, + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(other ) + { + + this.radius = other.radius; + this.phi = other.phi; + this.theta = other.theta; + + return this; + + }, + + // restrict phi to be betwee EPS and PI-EPS + makeSafe: function() + { + + var EPS = 0.000001; + this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); + + return this; + + }, + + setFromVector3: function(v ) + { + + return this.setFromCartesianCoords(v.x, v.y, v.z); + + }, + + setFromCartesianCoords: function(x, y, z ) + { + + this.radius = Math.sqrt(x * x + y * y + z * z); + + if (this.radius === 0) + { + + this.theta = 0; + this.phi = 0; + + } + else + { + + this.theta = Math.atan2(x, z); + this.phi = Math.acos(_Math.clamp(y / this.radius, -1, 1)); + + } + + return this; + + } } } diff --git a/THREE/Math/Triangle.cs b/THREE/Math/Triangle.cs index 3ccc9a92..45749f59 100644 --- a/THREE/Math/Triangle.cs +++ b/THREE/Math/Triangle.cs @@ -8,6 +8,378 @@ namespace THREE { public class Triangle { + function Triangle(a, b, c ) + { + + this.a = (a !== undefined) ? a : new Vector3(); + this.b = (b !== undefined) ? b : new Vector3(); + this.c = (c !== undefined) ? c : new Vector3(); + + } + + getNormal: function() + { + + var v0 = new Vector3(); + + return function getNormal(a, b, c, target) { + + if (target === undefined) + { + + console.warn('THREE.Triangle: .getNormal() target is now required'); + target = new Vector3(); + + } + + target.subVectors(c, b); + v0.subVectors(a, b); + target.cross(v0); + + var targetLengthSq = target.lengthSq(); + if (targetLengthSq > 0) + { + + return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); + + } + + return target.set(0, 0, 0); + + }; + + } + (), + + // static/instance method to calculate barycentric coordinates + // based on: http://www.blackpawn.com/texts/pointinpoly/default.html + getBarycoord: function() + { + + var v0 = new Vector3(); + var v1 = new Vector3(); + var v2 = new Vector3(); + + return function getBarycoord(point, a, b, c, target) { + + v0.subVectors(c, a); + v1.subVectors(b, a); + v2.subVectors(point, a); + + var dot00 = v0.dot(v0); + var dot01 = v0.dot(v1); + var dot02 = v0.dot(v2); + var dot11 = v1.dot(v1); + var dot12 = v1.dot(v2); + + var denom = (dot00 * dot11 - dot01 * dot01); + + if (target === undefined) + { + + console.warn('THREE.Triangle: .getBarycoord() target is now required'); + target = new Vector3(); + + } + + // collinear or singular triangle + if (denom === 0) + { + + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return target.set(-2, -1, -1); + + } + + var invDenom = 1 / denom; + var u = (dot11 * dot02 - dot01 * dot12) * invDenom; + var v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + // barycentric coordinates must always sum to 1 + return target.set(1 - u - v, v, u); + + }; + + } + (), + + containsPoint: function() + { + + var v1 = new Vector3(); + + return function containsPoint(point, a, b, c) { + + Triangle.getBarycoord(point, a, b, c, v1); + + return (v1.x >= 0) && (v1.y >= 0) && ((v1.x + v1.y) <= 1); + + }; + + } + (), + + getUV: function() + { + + var barycoord = new Vector3(); + + return function getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { + + this.getBarycoord(point, p1, p2, p3, barycoord); + + target.set(0, 0); + target.addScaledVector(uv1, barycoord.x); + target.addScaledVector(uv2, barycoord.y); + target.addScaledVector(uv3, barycoord.z); + + return target; + + }; + + } + () + + } ); + +Object.assign(Triangle.prototype, { + + set: function(a, b, c ) + { + + this.a.copy(a); + this.b.copy(b); + this.c.copy(c); + + return this; + + }, + + setFromPointsAndIndices: function(points, i0, i1, i2 ) + { + + this.a.copy(points[i0]); + this.b.copy(points[i1]); + this.c.copy(points[i2]); + + return this; + + }, + + clone: function() + { + + return new this.constructor().copy(this); + + }, + + copy: function(triangle ) + { + + this.a.copy(triangle.a); + this.b.copy(triangle.b); + this.c.copy(triangle.c); + + return this; + + }, + + getArea: function() + { + + var v0 = new Vector3(); + var v1 = new Vector3(); + + return function getArea() { + + v0.subVectors(this.c, this.b); + v1.subVectors(this.a, this.b); + + return v0.cross(v1).length() * 0.5; + + }; + + } + (), + + getMidpoint: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Triangle: .getMidpoint() target is now required'); + target = new Vector3(); + + } + + return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); + + }, + + getNormal: function(target ) + { + + return Triangle.getNormal(this.a, this.b, this.c, target); + + }, + + getPlane: function(target ) + { + + if (target === undefined) + { + + console.warn('THREE.Triangle: .getPlane() target is now required'); + target = new Vector3(); + + } + + return target.setFromCoplanarPoints(this.a, this.b, this.c); + + }, + + getBarycoord: function(point, target ) + { + + return Triangle.getBarycoord(point, this.a, this.b, this.c, target); + + }, + + containsPoint: function(point ) + { + + return Triangle.containsPoint(point, this.a, this.b, this.c); + + }, + + getUV: function(point, uv1, uv2, uv3, result ) + { + + return Triangle.getUV(point, this.a, this.b, this.c, uv1, uv2, uv3, result); + + }, + + intersectsBox: function(box ) + { + + return box.intersectsTriangle(this); + + }, + + closestPointToPoint: function() + { + + var vab = new Vector3(); + var vac = new Vector3(); + var vbc = new Vector3(); + var vap = new Vector3(); + var vbp = new Vector3(); + var vcp = new Vector3(); + + return function closestPointToPoint(p, target) { + + if (target === undefined) + { + + console.warn('THREE.Triangle: .closestPointToPoint() target is now required'); + target = new Vector3(); + + } + + var a = this.a, b = this.b, c = this.c; + var v, w; + + // algorithm thanks to Real-Time Collision Detection by Christer Ericson, + // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., + // under the accompanying license; see chapter 5.1.5 for detailed explanation. + // basically, we're distinguishing which of the voronoi regions of the triangle + // the point lies in with the minimum amount of redundant computation. + + vab.subVectors(b, a); + vac.subVectors(c, a); + vap.subVectors(p, a); + var d1 = vab.dot(vap); + var d2 = vac.dot(vap); + if (d1 <= 0 && d2 <= 0) + { + + // vertex region of A; barycentric coords (1, 0, 0) + return target.copy(a); + + } + + vbp.subVectors(p, b); + var d3 = vab.dot(vbp); + var d4 = vac.dot(vbp); + if (d3 >= 0 && d4 <= d3) + { + + // vertex region of B; barycentric coords (0, 1, 0) + return target.copy(b); + + } + + var vc = d1 * d4 - d3 * d2; + if (vc <= 0 && d1 >= 0 && d3 <= 0) + { + + v = d1 / (d1 - d3); + // edge region of AB; barycentric coords (1-v, v, 0) + return target.copy(a).addScaledVector(vab, v); + + } + + vcp.subVectors(p, c); + var d5 = vab.dot(vcp); + var d6 = vac.dot(vcp); + if (d6 >= 0 && d5 <= d6) + { + + // vertex region of C; barycentric coords (0, 0, 1) + return target.copy(c); + + } + + var vb = d5 * d2 - d1 * d6; + if (vb <= 0 && d2 >= 0 && d6 <= 0) + { + + w = d2 / (d2 - d6); + // edge region of AC; barycentric coords (1-w, 0, w) + return target.copy(a).addScaledVector(vac, w); + + } + + var va = d3 * d6 - d5 * d4; + if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) + { + + vbc.subVectors(c, b); + w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + // edge region of BC; barycentric coords (0, 1-w, w) + return target.copy(b).addScaledVector(vbc, w); // edge region of BC + + } + + // face region + var denom = 1 / (va + vb + vc); + // u = va * denom + v = vb * denom; + w = vc * denom; + return target.copy(a).addScaledVector(vab, v).addScaledVector(vac, w); + + }; + + } + (), + + equals: function(triangle ) + { + + return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } +} diff --git a/THREE/Math/Vector4.cs b/THREE/Math/Vector4.cs index e5193dd8..c48e042c 100644 --- a/THREE/Math/Vector4.cs +++ b/THREE/Math/Vector4.cs @@ -8,6 +8,690 @@ namespace THREE { public class Vector4 { + function Vector4(x, y, z, w ) + { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = (w !== undefined) ? w : 1; + + } + + isVector4: true, + + set: function(x, y, z, w ) + { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + setScalar: function(scalar ) + { + + this.x = scalar; + this.y = scalar; + this.z = scalar; + this.w = scalar; + + return this; + + }, + + setX: function(x ) + { + + this.x = x; + + return this; + + }, + + setY: function(y ) + { + + this.y = y; + + return this; + + }, + + setZ: function(z ) + { + + this.z = z; + + return this; + + }, + + setW: function(w ) + { + + this.w = w; + + return this; + + }, + + setComponent: function(index, value ) + { + + switch (index) + { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error('index is out of range: ' + index); + + } + + return this; + + }, + + getComponent: function(index ) + { + + switch (index) + { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error('index is out of range: ' + index); + + } + + }, + + clone: function() + { + + return new this.constructor(this.x, this.y, this.z, this.w); + + }, + + copy: function(v ) + { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = (v.w !== undefined) ? v.w : 1; + + return this; + + }, + + add: function(v, w ) + { + + if (w !== undefined) + { + + console.warn('THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.'); + return this.addVectors(v, w); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + + return this; + + }, + + addScalar: function(s ) + { + + this.x += s; + this.y += s; + this.z += s; + this.w += s; + + return this; + + }, + + addVectors: function(a, b ) + { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + + return this; + + }, + + addScaledVector: function(v, s ) + { + + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; + + return this; + + }, + + sub: function(v, w ) + { + + if (w !== undefined) + { + + console.warn('THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.'); + return this.subVectors(v, w); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + + return this; + + }, + + subScalar: function(s ) + { + + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; + + return this; + + }, + + subVectors: function(a, b ) + { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; + + }, + + multiplyScalar: function(scalar ) + { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + + return this; + + }, + + applyMatrix4: function(m ) + { + + var x = this.x, y = this.y, z = this.z, w = this.w; + var e = m.elements; + + this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; + this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; + this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; + this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; + + return this; + + }, + + divideScalar: function(scalar ) + { + + return this.multiplyScalar(1 / scalar); + + }, + + setAxisAngleFromQuaternion: function(q ) + { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + + // q is assumed to be normalized + + this.w = 2 * Math.acos(q.w); + + var s = Math.sqrt(1 - q.w * q.w); + + if (s < 0.0001) + { + + this.x = 1; + this.y = 0; + this.z = 0; + + } + else + { + + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; + + } + + return this; + + }, + + setAxisAngleFromRotationMatrix: function(m ) + { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var angle, x, y, z, // variables for result + epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + + te = m.elements, + + m11 = te[0], m12 = te[4], m13 = te[8], + m21 = te[1], m22 = te[5], m23 = te[9], + m31 = te[2], m32 = te[6], m33 = te[10]; + + if ((Math.abs(m12 - m21) < epsilon) && + (Math.abs(m13 - m31) < epsilon) && + (Math.abs(m23 - m32) < epsilon)) + { + + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms + + if ((Math.abs(m12 + m21) < epsilon2) && + (Math.abs(m13 + m31) < epsilon2) && + (Math.abs(m23 + m32) < epsilon2) && + (Math.abs(m11 + m22 + m33 - 3) < epsilon2)) + { + + // this singularity is identity matrix so angle = 0 + + this.set(1, 0, 0, 0); + + return this; // zero angle, arbitrary axis + + } + + // otherwise this singularity is angle = 180 + + angle = Math.PI; + + var xx = (m11 + 1) / 2; + var yy = (m22 + 1) / 2; + var zz = (m33 + 1) / 2; + var xy = (m12 + m21) / 4; + var xz = (m13 + m31) / 4; + var yz = (m23 + m32) / 4; + + if ((xx > yy) && (xx > zz)) + { + + // m11 is the largest diagonal term + + if (xx < epsilon) + { + + x = 0; + y = 0.707106781; + z = 0.707106781; + + } + else + { + + x = Math.sqrt(xx); + y = xy / x; + z = xz / x; + + } + + } + else if (yy > zz) + { + + // m22 is the largest diagonal term + + if (yy < epsilon) + { + + x = 0.707106781; + y = 0; + z = 0.707106781; + + } + else + { + + y = Math.sqrt(yy); + x = xy / y; + z = yz / y; + + } + + } + else + { + + // m33 is the largest diagonal term so base result on this + + if (zz < epsilon) + { + + x = 0.707106781; + y = 0.707106781; + z = 0; + + } + else + { + + z = Math.sqrt(zz); + x = xz / z; + y = yz / z; + + } + + } + + this.set(x, y, z, angle); + + return this; // return 180 deg rotation + + } + + // as we have reached here there are no singularities so we can handle normally + + var s = Math.sqrt((m32 - m23) * (m32 - m23) + + (m13 - m31) * (m13 - m31) + + (m21 - m12) * (m21 - m12)); // used to normalize + + if (Math.abs(s) < 0.001) s = 1; + + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + + this.x = (m32 - m23) / s; + this.y = (m13 - m31) / s; + this.z = (m21 - m12) / s; + this.w = Math.acos((m11 + m22 + m33 - 1) / 2); + + return this; + + }, + + min: function(v ) + { + + this.x = Math.min(this.x, v.x); + this.y = Math.min(this.y, v.y); + this.z = Math.min(this.z, v.z); + this.w = Math.min(this.w, v.w); + + return this; + + }, + + max: function(v ) + { + + this.x = Math.max(this.x, v.x); + this.y = Math.max(this.y, v.y); + this.z = Math.max(this.z, v.z); + this.w = Math.max(this.w, v.w); + + return this; + + }, + + clamp: function(min, max ) + { + + // assumes min < max, componentwise + + this.x = Math.max(min.x, Math.min(max.x, this.x)); + this.y = Math.max(min.y, Math.min(max.y, this.y)); + this.z = Math.max(min.z, Math.min(max.z, this.z)); + this.w = Math.max(min.w, Math.min(max.w, this.w)); + + return this; + + }, + + clampScalar: function() + { + + var min, max; + + return function clampScalar(minVal, maxVal) { + + if (min === undefined) + { + + min = new Vector4(); + max = new Vector4(); + + } + + min.set(minVal, minVal, minVal, minVal); + max.set(maxVal, maxVal, maxVal, maxVal); + + return this.clamp(min, max); + + }; + + } + (), + + clampLength: function(min, max ) + { + + var length = this.length(); + + return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); + + }, + + floor: function() + { + + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + this.z = Math.floor(this.z); + this.w = Math.floor(this.w); + + return this; + + }, + + ceil: function() + { + + this.x = Math.ceil(this.x); + this.y = Math.ceil(this.y); + this.z = Math.ceil(this.z); + this.w = Math.ceil(this.w); + + return this; + + }, + + round: function() + { + + this.x = Math.round(this.x); + this.y = Math.round(this.y); + this.z = Math.round(this.z); + this.w = Math.round(this.w); + + return this; + + }, + + roundToZero: function() + { + + this.x = (this.x < 0) ? Math.ceil(this.x) : Math.floor(this.x); + this.y = (this.y < 0) ? Math.ceil(this.y) : Math.floor(this.y); + this.z = (this.z < 0) ? Math.ceil(this.z) : Math.floor(this.z); + this.w = (this.w < 0) ? Math.ceil(this.w) : Math.floor(this.w); + + return this; + + }, + + negate: function() + { + + this.x = -this.x; + this.y = -this.y; + this.z = -this.z; + this.w = -this.w; + + return this; + + }, + + dot: function(v ) + { + + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + + }, + + lengthSq: function() + { + + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + + }, + + length: function() + { + + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); + + }, + + manhattanLength: function() + { + + return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); + + }, + + normalize: function() + { + + return this.divideScalar(this.length() || 1); + + }, + + setLength: function(length ) + { + + return this.normalize().multiplyScalar(length); + + }, + + lerp: function(v, alpha ) + { + + this.x += (v.x - this.x) * alpha; + this.y += (v.y - this.y) * alpha; + this.z += (v.z - this.z) * alpha; + this.w += (v.w - this.w) * alpha; + + return this; + + }, + + lerpVectors: function(v1, v2, alpha ) + { + + return this.subVectors(v2, v1).multiplyScalar(alpha).add(v1); + + }, + + equals: function(v ) + { + + return ((v.x === this.x) && (v.y === this.y) && (v.z === this.z) && (v.w === this.w)); + + }, + + fromArray: function(array, offset ) + { + + if (offset === undefined) offset = 0; + + this.x = array[offset]; + this.y = array[offset + 1]; + this.z = array[offset + 2]; + this.w = array[offset + 3]; + + return this; + + }, + + toArray: function(array, offset ) + { + + if (array === undefined) array = []; + if (offset === undefined) offset = 0; + + array[offset] = this.x; + array[offset + 1] = this.y; + array[offset + 2] = this.z; + array[offset + 3] = this.w; + + return array; + + }, + + fromBufferAttribute: function(attribute, index, offset ) + { + + if (offset !== undefined) + { + + console.warn('THREE.Vector4: offset has been removed from .fromBufferAttribute().'); + + } + + this.x = attribute.getX(index); + this.y = attribute.getY(index); + this.z = attribute.getZ(index); + this.w = attribute.getW(index); + + return this; + + } } }