Ray.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /**
  2. * @author bhouston / http://clara.io
  3. */
  4. THREE.Ray = function ( origin, direction ) {
  5. this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
  6. this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
  7. };
  8. THREE.Ray.prototype = {
  9. constructor: THREE.Ray,
  10. set: function ( origin, direction ) {
  11. this.origin.copy( origin );
  12. this.direction.copy( direction );
  13. return this;
  14. },
  15. clone: function () {
  16. return new this.constructor().copy( this );
  17. },
  18. copy: function ( ray ) {
  19. this.origin.copy( ray.origin );
  20. this.direction.copy( ray.direction );
  21. return this;
  22. },
  23. at: function ( t, optionalTarget ) {
  24. var result = optionalTarget || new THREE.Vector3();
  25. return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
  26. },
  27. recast: function () {
  28. var v1 = new THREE.Vector3();
  29. return function ( t ) {
  30. this.origin.copy( this.at( t, v1 ) );
  31. return this;
  32. };
  33. }(),
  34. closestPointToPoint: function ( point, optionalTarget ) {
  35. var result = optionalTarget || new THREE.Vector3();
  36. result.subVectors( point, this.origin );
  37. var directionDistance = result.dot( this.direction );
  38. if ( directionDistance < 0 ) {
  39. return result.copy( this.origin );
  40. }
  41. return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  42. },
  43. distanceToPoint: function ( point ) {
  44. return Math.sqrt( this.distanceSqToPoint( point ) );
  45. },
  46. distanceSqToPoint: function () {
  47. var v1 = new THREE.Vector3();
  48. return function ( point ) {
  49. var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
  50. // point behind the ray
  51. if ( directionDistance < 0 ) {
  52. return this.origin.distanceToSquared( point );
  53. }
  54. v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  55. return v1.distanceToSquared( point );
  56. };
  57. }(),
  58. distanceSqToSegment: function () {
  59. var segCenter = new THREE.Vector3();
  60. var segDir = new THREE.Vector3();
  61. var diff = new THREE.Vector3();
  62. return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
  63. // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
  64. // It returns the min distance between the ray and the segment
  65. // defined by v0 and v1
  66. // It can also set two optional targets :
  67. // - The closest point on the ray
  68. // - The closest point on the segment
  69. segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
  70. segDir.copy( v1 ).sub( v0 ).normalize();
  71. diff.copy( this.origin ).sub( segCenter );
  72. var segExtent = v0.distanceTo( v1 ) * 0.5;
  73. var a01 = - this.direction.dot( segDir );
  74. var b0 = diff.dot( this.direction );
  75. var b1 = - diff.dot( segDir );
  76. var c = diff.lengthSq();
  77. var det = Math.abs( 1 - a01 * a01 );
  78. var s0, s1, sqrDist, extDet;
  79. if ( det > 0 ) {
  80. // The ray and segment are not parallel.
  81. s0 = a01 * b1 - b0;
  82. s1 = a01 * b0 - b1;
  83. extDet = segExtent * det;
  84. if ( s0 >= 0 ) {
  85. if ( s1 >= - extDet ) {
  86. if ( s1 <= extDet ) {
  87. // region 0
  88. // Minimum at interior points of ray and segment.
  89. var invDet = 1 / det;
  90. s0 *= invDet;
  91. s1 *= invDet;
  92. sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
  93. } else {
  94. // region 1
  95. s1 = segExtent;
  96. s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
  97. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  98. }
  99. } else {
  100. // region 5
  101. s1 = - segExtent;
  102. s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
  103. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  104. }
  105. } else {
  106. if ( s1 <= - extDet ) {
  107. // region 4
  108. s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
  109. s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
  110. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  111. } else if ( s1 <= extDet ) {
  112. // region 3
  113. s0 = 0;
  114. s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
  115. sqrDist = s1 * ( s1 + 2 * b1 ) + c;
  116. } else {
  117. // region 2
  118. s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
  119. s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
  120. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  121. }
  122. }
  123. } else {
  124. // Ray and segment are parallel.
  125. s1 = ( a01 > 0 ) ? - segExtent : segExtent;
  126. s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
  127. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  128. }
  129. if ( optionalPointOnRay ) {
  130. optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
  131. }
  132. if ( optionalPointOnSegment ) {
  133. optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );
  134. }
  135. return sqrDist;
  136. };
  137. }(),
  138. intersectSphere: function () {
  139. // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
  140. var v1 = new THREE.Vector3();
  141. return function ( sphere, optionalTarget ) {
  142. v1.subVectors( sphere.center, this.origin );
  143. var tca = v1.dot( this.direction );
  144. var d2 = v1.dot( v1 ) - tca * tca;
  145. var radius2 = sphere.radius * sphere.radius;
  146. if ( d2 > radius2 ) return null;
  147. var thc = Math.sqrt( radius2 - d2 );
  148. // t0 = first intersect point - entrance on front of sphere
  149. var t0 = tca - thc;
  150. // t1 = second intersect point - exit point on back of sphere
  151. var t1 = tca + thc;
  152. // test to see if both t0 and t1 are behind the ray - if so, return null
  153. if ( t0 < 0 && t1 < 0 ) return null;
  154. // test to see if t0 is behind the ray:
  155. // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
  156. // in order to always return an intersect point that is in front of the ray.
  157. if ( t0 < 0 ) return this.at( t1, optionalTarget );
  158. // else t0 is in front of the ray, so return the first collision point scaled by t0
  159. return this.at( t0, optionalTarget );
  160. }
  161. }(),
  162. intersectsSphere: function ( sphere ) {
  163. return this.distanceToPoint( sphere.center ) <= sphere.radius;
  164. },
  165. isIntersectionSphere: function ( sphere ) {
  166. console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' );
  167. return this.intersectsSphere( sphere );
  168. },
  169. distanceToPlane: function ( plane ) {
  170. var denominator = plane.normal.dot( this.direction );
  171. if ( denominator === 0 ) {
  172. // line is coplanar, return origin
  173. if ( plane.distanceToPoint( this.origin ) === 0 ) {
  174. return 0;
  175. }
  176. // Null is preferable to undefined since undefined means.... it is undefined
  177. return null;
  178. }
  179. var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
  180. // Return if the ray never intersects the plane
  181. return t >= 0 ? t : null;
  182. },
  183. intersectPlane: function ( plane, optionalTarget ) {
  184. var t = this.distanceToPlane( plane );
  185. if ( t === null ) {
  186. return null;
  187. }
  188. return this.at( t, optionalTarget );
  189. },
  190. intersectsPlane: function ( plane ) {
  191. // check if the ray lies on the plane first
  192. var distToPoint = plane.distanceToPoint( this.origin );
  193. if ( distToPoint === 0 ) {
  194. return true;
  195. }
  196. var denominator = plane.normal.dot( this.direction );
  197. if ( denominator * distToPoint < 0 ) {
  198. return true;
  199. }
  200. // ray origin is behind the plane (and is pointing behind it)
  201. return false;
  202. },
  203. isIntersectionPlane: function ( plane ) {
  204. console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' );
  205. return this.intersectsPlane( plane );
  206. },
  207. intersectBox: function ( box, optionalTarget ) {
  208. // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
  209. var tmin, tmax, tymin, tymax, tzmin, tzmax;
  210. var invdirx = 1 / this.direction.x,
  211. invdiry = 1 / this.direction.y,
  212. invdirz = 1 / this.direction.z;
  213. var origin = this.origin;
  214. if ( invdirx >= 0 ) {
  215. tmin = ( box.min.x - origin.x ) * invdirx;
  216. tmax = ( box.max.x - origin.x ) * invdirx;
  217. } else {
  218. tmin = ( box.max.x - origin.x ) * invdirx;
  219. tmax = ( box.min.x - origin.x ) * invdirx;
  220. }
  221. if ( invdiry >= 0 ) {
  222. tymin = ( box.min.y - origin.y ) * invdiry;
  223. tymax = ( box.max.y - origin.y ) * invdiry;
  224. } else {
  225. tymin = ( box.max.y - origin.y ) * invdiry;
  226. tymax = ( box.min.y - origin.y ) * invdiry;
  227. }
  228. if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
  229. // These lines also handle the case where tmin or tmax is NaN
  230. // (result of 0 * Infinity). x !== x returns true if x is NaN
  231. if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
  232. if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
  233. if ( invdirz >= 0 ) {
  234. tzmin = ( box.min.z - origin.z ) * invdirz;
  235. tzmax = ( box.max.z - origin.z ) * invdirz;
  236. } else {
  237. tzmin = ( box.max.z - origin.z ) * invdirz;
  238. tzmax = ( box.min.z - origin.z ) * invdirz;
  239. }
  240. if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
  241. if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
  242. if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
  243. //return point closest to the ray (positive side)
  244. if ( tmax < 0 ) return null;
  245. return this.at( tmin >= 0 ? tmin : tmax, optionalTarget );
  246. },
  247. intersectsBox: ( function () {
  248. var v = new THREE.Vector3();
  249. return function ( box ) {
  250. return this.intersectBox( box, v ) !== null;
  251. };
  252. } )(),
  253. isIntersectionBox: function ( box ) {
  254. console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' );
  255. return this.intersectsBox( box );
  256. },
  257. intersectTriangle: function () {
  258. // Compute the offset origin, edges, and normal.
  259. var diff = new THREE.Vector3();
  260. var edge1 = new THREE.Vector3();
  261. var edge2 = new THREE.Vector3();
  262. var normal = new THREE.Vector3();
  263. return function ( a, b, c, backfaceCulling, optionalTarget ) {
  264. // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp
  265. edge1.subVectors( b, a );
  266. edge2.subVectors( c, a );
  267. normal.crossVectors( edge1, edge2 );
  268. // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
  269. // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
  270. // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
  271. // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
  272. // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
  273. var DdN = this.direction.dot( normal );
  274. var sign;
  275. if ( DdN > 0 ) {
  276. if ( backfaceCulling ) return null;
  277. sign = 1;
  278. } else if ( DdN < 0 ) {
  279. sign = - 1;
  280. DdN = - DdN;
  281. } else {
  282. return null;
  283. }
  284. diff.subVectors( this.origin, a );
  285. var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );
  286. // b1 < 0, no intersection
  287. if ( DdQxE2 < 0 ) {
  288. return null;
  289. }
  290. var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );
  291. // b2 < 0, no intersection
  292. if ( DdE1xQ < 0 ) {
  293. return null;
  294. }
  295. // b1+b2 > 1, no intersection
  296. if ( DdQxE2 + DdE1xQ > DdN ) {
  297. return null;
  298. }
  299. // Line intersects triangle, check if ray does.
  300. var QdN = - sign * diff.dot( normal );
  301. // t < 0, no intersection
  302. if ( QdN < 0 ) {
  303. return null;
  304. }
  305. // Ray intersects triangle.
  306. return this.at( QdN / DdN, optionalTarget );
  307. };
  308. }(),
  309. applyMatrix4: function ( matrix4 ) {
  310. this.direction.add( this.origin ).applyMatrix4( matrix4 );
  311. this.origin.applyMatrix4( matrix4 );
  312. this.direction.sub( this.origin );
  313. this.direction.normalize();
  314. return this;
  315. },
  316. equals: function ( ray ) {
  317. return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
  318. }
  319. };