Kaynağa Gözat

Rename and fix the function that computes the ray-line intersection

The ray was considered as a line... Now it's a real ray-segment
intersection.
stephomi 12 yıl önce
ebeveyn
işleme
f58e144d9e
3 değiştirilmiş dosya ile 99 ekleme ve 48 silme
  1. 5 5
      src/core/Raycaster.js
  2. 68 37
      src/math/Ray.js
  3. 26 6
      test/unit/math/Ray.js

+ 5 - 5
src/core/Raycaster.js

@@ -348,18 +348,18 @@
 			var vertices = geometry.vertices;
 			var vertices = geometry.vertices;
 			var nbVertices = vertices.length;
 			var nbVertices = vertices.length;
 			var interSegment = new THREE.Vector3();
 			var interSegment = new THREE.Vector3();
-			var interLine = new THREE.Vector3();
+			var interRay = new THREE.Vector3();
 			var step = object.type === THREE.LineStrip ? 1 : 2;
 			var step = object.type === THREE.LineStrip ? 1 : 2;
 
 
 			for ( var i = 0; i < nbVertices - 1; i = i + step ) {
 			for ( var i = 0; i < nbVertices - 1; i = i + step ) {
 
 
-				localRay.distanceSqAndPointToSegment( vertices[ i ], vertices[ i + 1 ], interLine, interSegment );
+				localRay.distanceToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
 				interSegment.applyMatrix4( object.matrixWorld );
 				interSegment.applyMatrix4( object.matrixWorld );
-				interLine.applyMatrix4( object.matrixWorld );
+				interRay.applyMatrix4( object.matrixWorld );
 
 
-				if ( interLine.distanceToSquared( interSegment ) <= precisionSq ) {
+				if ( interRay.distanceToSquared( interSegment ) <= precisionSq ) {
 
 
-					var distance = raycaster.ray.origin.distanceTo( interLine );
+					var distance = raycaster.ray.origin.distanceTo( interRay );
 
 
 					if ( raycaster.near <= distance && distance <= raycaster.far ) {
 					if ( raycaster.near <= distance && distance <= raycaster.far ) {
 
 

+ 68 - 37
src/math/Ray.js

@@ -78,81 +78,112 @@ THREE.Ray.prototype = {
 
 
 	}(),
 	}(),
 
 
-	distanceSqAndPointToSegment: function ( v0, v1, optionalPointOnLine, optionalPointOnSegment ) {
+	distanceToSegment: function( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
 
 
-		// from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistLine3Segment3.cpp
-		// It returns the min distance between the ray (actually... the line) and the segment
+		// from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
+		// It returns the min distance between the ray and the segment
 		// defined by v0 and v1
 		// defined by v0 and v1
 		// It can also set two optional targets :
 		// It can also set two optional targets :
-		// - The closest point on the ray (...line)
+		// - The closest point on the ray
 		// - The closest point on the segment
 		// - The closest point on the segment
 
 
 		var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 );
 		var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 );
 		var segDir = v1.clone().sub( v0 ).normalize();
 		var segDir = v1.clone().sub( v0 ).normalize();
-		var segExtent = v0.distanceTo( v1 ) *0.5;
+		var segExtent = v0.distanceTo( v1 ) * 0.5;
 		var diff = this.origin.clone().sub( segCenter );
 		var diff = this.origin.clone().sub( segCenter );
-		var a01 = -this.direction.dot( segDir );
+		var a01 = - this.direction.dot( segDir );
 		var b0 = diff.dot( this.direction );
 		var b0 = diff.dot( this.direction );
+		var b1 = - diff.dot( segDir );
 		var c = diff.lengthSq();
 		var c = diff.lengthSq();
 		var det = Math.abs( 1 - a01 * a01 );
 		var det = Math.abs( 1 - a01 * a01 );
-		var b1, s0, s1, sqrDist, extDet;
+		var s0, s1, sqrDist, extDet;
+		if (det >= 0) {
 
 
-		if ( det >= 0 ) {
+			// The ray and segment are not parallel.
 
 
-			// The line and segment are not parallel.
-
-			b1 = -diff.dot( segDir );
+			s0 = a01 * b1 - b0;
 			s1 = a01 * b0 - b1;
 			s1 = a01 * b0 - b1;
 			extDet = segExtent * det;
 			extDet = segExtent * det;
 
 
-			if ( s1 >= -extDet ) {
+			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;
 
 
-				if ( s1 <= extDet ) {
+					} else {
 
 
-					// Two interior points are closest, one on the line and one
-					// on the segment.
+						// region 1
 
 
-					var invDet = 1 / det;
-					s0 = ( a01 * b1 - b0 ) * invDet;
-					s1 *= invDet;
-					sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
+						s1 = segExtent;
+						s0 = Math.max( 0, - ( a01 * s1 + b0) );
+						sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 
 
-				} else {
+					}
 
 
-					// The endpoint e1 of the segment and an interior point of
-					// the line are closest.
+				}
+
+				else {
+
+					// region 5
 
 
-					s1 = segExtent;
-					s0 = - ( a01 * s1 + b0 );
+					s1 = - segExtent;
+					s0 = Math.max( 0, - ( a01 * s1 + b0) );
 					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 					sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 
 
 				}
 				}
 
 
 			} else {
 			} else {
 
 
-				// The end point e0 of the segment and an interior point of the
-				// line are closest.
+				if ( s1 <= - extDet) {
 
 
-				s1 = - segExtent;
-				s0 = - ( a01 * s1 + b0 );
-				sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+					// 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 {
+		}	else {
 
 
-			// The line and segment are parallel.  Choose the closest pair so that
-			// one point is at segment center.
+			// Ray and segment are parallel.
 
 
-			s1 = 0;
-			s0 = - b0;
-			sqrDist = b0 * s0 + c;
+			s1 = ( a01 > 0 ) ? - segExtent : segExtent;
+			s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+			sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
 
 
 		}
 		}
 
 
-		if ( optionalPointOnLine ) {
+		if ( optionalPointOnRay ) {
 
 
-			optionalPointOnLine.copy( this.direction.clone().multiplyScalar( s0 ).add( this.origin ) );
+			optionalPointOnRay.copy( this.direction.clone().multiplyScalar( s0 ).add( this.origin ) );
 
 
 		}
 		}
 
 

+ 26 - 6
test/unit/math/Ray.js

@@ -174,19 +174,39 @@ test( "applyMatrix4", function() {
 });
 });
 
 
 
 
-test( "distanceSqAndPointToSegment4", function() {
+test( "distanceToSegment", function() {
 	var a = new THREE.Ray( one3.clone(), new THREE.Vector3( 0, 0, 1 ) );
 	var a = new THREE.Ray( one3.clone(), new THREE.Vector3( 0, 0, 1 ) );
-	var v0 = new THREE.Vector3( 3, 5, 50 );
-	var v1 = new THREE.Vector3( 50, 50, 50 ); // just a far away point
 	var ptOnLine = new THREE.Vector3();
 	var ptOnLine = new THREE.Vector3();
 	var ptOnSegment = new THREE.Vector3();
 	var ptOnSegment = new THREE.Vector3();
-	var distSqr = a.distanceSqAndPointToSegment( v0, v1, ptOnLine, ptOnSegment );
-	var m = new THREE.Matrix4();
+
+	//segment in front of the ray
+	var v0 = new THREE.Vector3( 3, 5, 50 );
+	var v1 = new THREE.Vector3( 50, 50, 50 ); // just a far away point
+	var distSqr = a.distanceToSegment( v0, v1, ptOnLine, ptOnSegment );
 
 
 	ok( ptOnSegment.distanceTo( v0 ) < 0.0001, "Passed!" );
 	ok( ptOnSegment.distanceTo( v0 ) < 0.0001, "Passed!" );
 	ok( ptOnLine.distanceTo( new THREE.Vector3(1, 1, 50) ) < 0.0001, "Passed!" );
 	ok( ptOnLine.distanceTo( new THREE.Vector3(1, 1, 50) ) < 0.0001, "Passed!" );
 	// ((3-1) * (3-1) + (5-1) * (5-1) = 4 + 16 = 20
 	// ((3-1) * (3-1) + (5-1) * (5-1) = 4 + 16 = 20
-	ok( distSqr === 20, "Passed!" );
+	ok( Math.abs( distSqr - 20 ) < 0.0001, "Passed!" );
+
+	//segment behind the ray
+	v0 = new THREE.Vector3( -50, -50, -50 ); // just a far away point
+	v1 = new THREE.Vector3( -3, -5, -4 );
+	distSqr = a.distanceToSegment( v0, v1, ptOnLine, ptOnSegment );
+
+	ok( ptOnSegment.distanceTo( v1 ) < 0.0001, "Passed!" );
+	ok( ptOnLine.distanceTo( one3 ) < 0.0001, "Passed!" );
+	// ((-3-1) * (-3-1) + (-5-1) * (-5-1) + (-4-1) + (-4-1) = 16 + 36 + 25 = 77
+	ok( Math.abs( distSqr - 77 ) < 0.0001, "Passed!" );
+
+	//exact intersection between the ray and the segment
+	v0 = new THREE.Vector3( -50, -50, -50 );
+	v1 = new THREE.Vector3( 50, 50, 50 );
+	distSqr = a.distanceToSegment( v0, v1, ptOnLine, ptOnSegment );
+
+	ok( ptOnSegment.distanceTo( one3 ) < 0.0001, "Passed!" );
+	ok( ptOnLine.distanceTo( one3 ) < 0.0001, "Passed!" );
+	ok( distSqr < 0.0001, "Passed!" );
 });
 });