Browse Source

Plane,Box3,Sphere improvements: static constructors, code simplficiation, optimizations. Box3 made more robust via true empty (+MAX_VALUE,-MIN_VALUE).

Ben Houston 12 years ago
parent
commit
7ca6163266
3 changed files with 88 additions and 31 deletions
  1. 48 18
      src/core/Box3.js
  2. 33 8
      src/core/Plane.js
  3. 7 5
      src/core/Sphere.js

+ 48 - 18
src/core/Box3.js

@@ -6,9 +6,24 @@
 
 	THREE.Box3 = function ( min, max ) {
 
-		this.min = min || new THREE.Vector3();
-		this.max = max || this.min;		// This is done on purpose so you can make a box using a single point and then expand it.
+		// TODO: Is this valid JavaScript to check if the parameters are specified?
+		if( ! min && ! max ) {			
+			this.makeEmpty();
+		}
+		else {
+			this.min = min || new THREE.Vector3();
+			this.max = max || this.min;		// This is done on purpose so you can make a box using a single point and then expand it.
+		}
+	};
 
+	THREE.Box3.fromPoints = function ( points ) {
+
+		var boundingBox = new THREE.Box3();
+		for( var i = 0; i < points.length; i ++ ) {
+			boundingBox.extendByPoint( points[i] );
+		}
+
+		return boundingBox;
 	};
 
 	THREE.Box3.prototype.set = function ( min, max ) {
@@ -27,16 +42,26 @@
 		return this;
 	};
 
+	THREE.Box3.prototype.makeEmpty = function () {
+
+		this.min.x = this.min.y = this.min.z = Number.MAX_VALUE;
+		this.max.x = this.max.y = this.max.z = -Number.MAX_VALUE;
+
+		return this;
+	};
 
 	THREE.Box3.prototype.empty = 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 ) <= 0 ) ||
-			(( this.max.y - this.min.y ) <= 0 ) ||
-			(( this.max.z - this.min.z ) <= 0 );
+			( this.max.x < this.min.x ) ||
+			( this.max.y < this.min.y ) ||
+			( this.max.z < this.min.z );
 	};
 
 	THREE.Box3.prototype.volume = function () {
+
 		return 
 			( this.max.x - this.min.x ) *
 			( this.max.y - this.min.y ) *
@@ -44,10 +69,12 @@
 	};
 
 	THREE.Box3.prototype.center = function () {
+
 		return new THREE.Vector3().add( this.min, this.max ).multiplyScalar( 0.5 );
 	};
 
 	THREE.Box3.prototype.size = function () {
+
 		return new THREE.Vector3().sub( this.max, this.min );
 	};
 
@@ -57,20 +84,15 @@
 		return this;
 	};
 
-	THREE.Box3.prototype.extendByBox = function ( box ) {
-		// Assumption: for speed purposes, we assume that the box is not empty, e.g. box.min < box.max
-		this.min.minSelf( box.min );
-		this.max.maxSelf( box.max );
-		return this;
-	};
-
 	THREE.Box3.prototype.expandByVector = function ( vector ) {
+
 		this.min.subSelf( vector );
 		this.max.addSelf( vector );
 		return this;
 	};
 
 	THREE.Box3.prototype.expandByScalar = function ( scalar ) {
+
 		this.min.addScalar( -scalar );
 		this.max.addScalar( scalar );
 		return this;
@@ -101,6 +123,7 @@
 
 	THREE.Box3.prototype.isIntersection = function ( box ) {
 		// Assumption: for speed purposes, we assume that the box is not empty, e.g. box.min < box.max
+
 		// using 6 splitting planes to rule out intersections.
 		if( 
 			( this.max.x < box.min.x ) || ( box.min.x > this.max.x ) ||
@@ -114,6 +137,7 @@
 
 	THREE.Box3.prototype.getParameter = function ( point ) {
 		// Assumption: for speed purposes, we assume that the box is not empty, e.g. box.min < box.max
+
 		// This assumption can lead to a divide by zero if the box is actually empty.
 		// Suggestions?  I don't want to check for empty as it is a speed hit, but maybe
 		// it is necessary.
@@ -125,30 +149,36 @@
 	};
 
 	THREE.Box3.prototype.clampPoint = function ( point ) {
+
 		return point.maxSelf( this.min ).minSelf( this.max );
 	};
 
 	THREE.Box3.prototype.distanceToPoint = function ( point ) {
+
 		return this.clampPoint( point ).subSelf( point ).length();
 	};
 
 	THREE.Box3.prototype.intersect = function ( box ) {
-		// Assumption: for speed purposes, we assume that the box is not empty, e.g. box.min < box.max
-		this.min = this.clampPoint( box.min );
-		this.max = this.clampPoint( box.max );
+
+		this.min.maxSelf( box.min );
+		this.max.minSelf( box.max );
+		
 		return this;
 	};
 
 	THREE.Box3.prototype.union = function ( box ) {
-		// Assumption: for speed purposes, we assume that the box is not empty, e.g. box.min < box.max
-		this.extendByPoint( box.min );
-		this.extendByPoint( box.max );
+
+		this.min.minSelf( box.min );
+		this.max.maxSelf( box.max );
+
 		return this;
 	};
 
 	THREE.Box3.prototype.translate = function ( offset ) {
+
 		this.min.addSelf( offset );
 		this.max.addSelf( offset );
+
 		return this;
 	};
 

+ 33 - 8
src/core/Plane.js

@@ -4,16 +4,37 @@
 
 ( function ( THREE ) {
 
+	var zeroPoint = new THREE.Vector3();
+
 	THREE.Plane = function ( normal, constant ) {
-		// TODO: ensure that normal is of length 1 and if it isn't readjust both normal and constant?
+
 		this.normal = normal || new THREE.Vector3();
 		this.constant = constant || 0;
 
 	};
 
+	THREE.Plane.fromNormalAndCoplanarPoint = function ( normal, point ) {
+		// NOTE: This function doens't support optional parameters like the constructor.
+
+		return new THREE.Plane( 
+			normal,
+			- point.dot( normal )
+			);
+	};
+
+	THREE.Plane.fromCoplanarPoints = function ( a, b, c ) {
+		// NOTE: This function doens't support optional parameters like the constructor.
+
+		var normal = new THREE.Vector3().sub( b, a ).cross(
+			new THREE.Vector3().sub( c, a ) );
+
+		// Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
+
+		return THREE.Plane.fromNormalAndCoplanarPoint( normal, a );
+	};
+
 	THREE.Plane.prototype.set = function ( normal, constant ) {
 
-		// TODO: ensure that normal is of length 1 and if it isn't readjust both normal and constant?
 		this.normal = normal;
 		this.constant = constant;
 
@@ -22,10 +43,7 @@
 
 	THREE.Plane.prototype.setComponents = function ( x, y, z, w ) {
 
-		// TODO: ensure that normal is of length 1 and if it isn't readjust both normal and constant?
-		this.normal.x = x;
-		this.normal.y = y;
-		this.normal.z = z;
+		this.normal.set( x, y, z );
 		this.constant = w;
 
 		return this;
@@ -50,7 +68,7 @@
 	THREE.Plane.prototype.normalize = function () {
 
 		// Note: will lead to a divide by zero if the plane is invalid.
-		var inverseNormalLength = 1.0 / this.normal.length()
+		var inverseNormalLength = 1.0 / this.normal.length();
 		this.normal.multipleByScalar( inverseNormalLength );
 		this.constant *= inverseNormalLength;
 
@@ -62,8 +80,15 @@
 		return this.normal.dot( point ) + this.constant;
 	};
 
+	THREE.Sphere.prototype.distanceToSphere = function ( sphere ) {
+
+		return this.distanceToPoint( sphere.center ) - sphere.radius;
+	};
+
 	THREE.Plane.prototype.projectPoint = function ( point ) {		
 		
+		// TODO: optimize this by expanding and simplifying
+		
 		return new THREE.Vector3().copy( point ).sub( this.orthoPoint( point ) );
 	};
 
@@ -85,7 +110,7 @@
 
 	THREE.Plane.prototype.coplanarPoint = function () {		
 		
-		return this.projectPoint( new THREE.Vector3() );
+		return new THREE.Vector3().copy( this.normal ).multiplyByScalar( - this.constant );
 	};
 
 }( THREE ) );

+ 7 - 5
src/core/Sphere.js

@@ -4,6 +4,10 @@
 
 ( function ( THREE ) {
 
+	var sphereVolumeConstant = Math.PI * 4 / 3;
+	var square = function( x ) { return x*x; }
+	var cube = function( x ) { return x*x; }
+
 	THREE.Sphere = function ( center, radius ) {
 
 		this.center = center || new THREE.Vector3();
@@ -27,22 +31,20 @@
 		return this;
 	};
 
-
 	THREE.Sphere.prototype.empty = function () {
 
 		return ( this.radius <= 0 );
 
 	};
 
-
 	THREE.Sphere.prototype.volume = function () {
 
-		return Math.PI * 4 / 3 * this.radius * this.radius * this.radius;
+		return sphereVolumeConstant * cube( this.radius );
 	};
 
 	THREE.Sphere.prototype.containsPoint = function ( point ) {
 
-		return ( point.distanceToSquared( this.center ) <= this.radius * this.radius );
+		return ( point.distanceToSquared( this.center ) <= square( this.radius ) );
 	};
 
 	THREE.Sphere.prototype.distanceToPoint = function ( point ) {
@@ -54,7 +56,7 @@
 
 		var deltaLengthSq = this.center.distanceToSquared( point );
 
-		if( deltaLengthSq > ( this.radius*this.radius ) ) {
+		if( deltaLengthSq > square( this.radius ) ) {
 
 			var delta = new THREE.Vector3().sub( point, center ).normalize();
 			delta.multiplyByScalar( this.radius ).addSelf( this.center );