Browse Source

Merge pull request #4817 from erichlof/dev

Added method Ray.intersectSphere(sphere) and 3 semicolon insertions to Ray.js
Mr.doob 11 years ago
parent
commit
ff15ec8ac5
2 changed files with 103 additions and 3 deletions
  1. 44 3
      src/math/Ray.js
  2. 59 0
      test/unit/math/Ray.js

+ 44 - 3
src/math/Ray.js

@@ -216,6 +216,47 @@ THREE.Ray.prototype = {
 		return this.distanceToPoint( sphere.center ) <= sphere.radius;
 
 	},
+	
+	intersectSphere: function () {
+
+		// from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
+		
+		var v1 = new THREE.Vector3();
+		
+		return function ( sphere, optionalTarget ) {
+
+			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, optionalTarget );
+
+			// else t0 is in front of the ray, so return the first collision point scaled by t0 
+			return this.at( t0, optionalTarget );
+			
+		}
+	
+	}(),
 
 	isIntersectionPlane: function ( plane ) {
 
@@ -233,7 +274,7 @@ THREE.Ray.prototype = {
 
 		if ( denominator * distToPoint < 0 ) {
 
-			return true
+			return true;
 
 		}
 
@@ -290,7 +331,7 @@ THREE.Ray.prototype = {
 
 			return this.intersectBox( box, v ) !== null;
 
-		}
+		};
 
 	}(),
 
@@ -441,7 +482,7 @@ THREE.Ray.prototype = {
 			// Ray intersects triangle.
 			return this.at( QdN / DdN, optionalTarget );
 	
-		}
+		};
 	
 	}(),
 

+ 59 - 0
test/unit/math/Ray.js

@@ -108,6 +108,65 @@ test( "isIntersectionSphere", function() {
 	ok( ! a.isIntersectionSphere( f ), "Passed!" );
 });
 
+test( "intersectSphere", function() {
+	
+	var TOL = 0.0001;
+	
+	// ray a0 origin located at ( 0, 0, 0 ) and points outward in negative-z direction
+	var a0 = new THREE.Ray( zero3.clone(), new THREE.Vector3( 0, 0, -1 ) );
+	// ray a1 origin located at ( 1, 1, 1 ) and points left in negative-x direction
+	var a1 = new THREE.Ray( one3.clone(), new THREE.Vector3( -1, 0, 0 ) );
+
+	// sphere (radius of 2) located behind ray a0, should result in null
+	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, 3 ), 2 );
+	ok( a0.intersectSphere( b ) === null, "Passed!" );
+	
+	// sphere (radius of 2) located in front of, but too far right of ray a0, should result in null
+	var b = new THREE.Sphere( new THREE.Vector3( 3, 0, -1 ), 2 );
+	ok( a0.intersectSphere( b ) === null, "Passed!" );
+	
+	// sphere (radius of 2) located below ray a1, should result in null
+	var b = new THREE.Sphere( new THREE.Vector3( 1, -2, 1 ), 2 );
+	ok( a1.intersectSphere( b ) === null, "Passed!" );
+	
+	// sphere (radius of 1) located to the left of ray a1, should result in intersection at 0, 1, 1 
+	var b = new THREE.Sphere( new THREE.Vector3( -1, 1, 1 ), 1 );
+	ok( a1.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 1, 1 ) ) < TOL, "Passed!" );
+	
+	// sphere (radius of 1) located in front of ray a0, should result in intersection at 0, 0, -1 
+	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, -2 ), 1 );
+	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -1 ) ) < TOL, "Passed!" );
+	
+	// sphere (radius of 2) located in front & right of ray a0, should result in intersection at 0, 0, -1, or left-most edge of sphere 
+	var b = new THREE.Sphere( new THREE.Vector3( 2, 0, -1 ), 2 );
+	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -1 ) ) < TOL, "Passed!" );
+	
+	// same situation as above, but move the sphere a fraction more to the right, and ray a0 should now just miss 
+	var b = new THREE.Sphere( new THREE.Vector3( 2.01, 0, -1 ), 2 );
+	ok( a0.intersectSphere( b ) === null, "Passed!" );
+	
+	// following tests are for situations where the ray origin is inside the sphere
+	
+	// sphere (radius of 1) center located at ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 1, 
+	// is behind ray a0.  Therefore, second exit point on back of sphere will be returned: 0, 0, -1
+	// thus keeping the intersection point always in front of the ray.
+	var b = new THREE.Sphere( zero3.clone(), 1 );
+	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -1 ) ) < TOL, "Passed!" );
+	
+	// sphere (radius of 4) center located behind ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 5, 
+	// is behind ray a0.  Therefore, second exit point on back of sphere will be returned: 0, 0, -3
+	// thus keeping the intersection point always in front of the ray.
+	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, 1 ), 4 );
+	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -3 ) ) < TOL, "Passed!" );
+	
+	// sphere (radius of 4) center located in front of ray a0 origin / sphere surrounds the ray origin, so the first intersect point 0, 0, 3, 
+	// is behind ray a0.  Therefore, second exit point on back of sphere will be returned: 0, 0, -5
+	// thus keeping the intersection point always in front of the ray.
+	var b = new THREE.Sphere( new THREE.Vector3( 0, 0, -1 ), 4 );
+	ok( a0.intersectSphere( b ).distanceTo( new THREE.Vector3( 0, 0, -5 ) ) < TOL, "Passed!" );
+	
+});
+
 test( "isIntersectionPlane", function() {
 	var a = new THREE.Ray( one3.clone(), new THREE.Vector3( 0, 0, 1 ) );