1 | import { Vector3 } from './Vector3.js';
|
2 |
|
3 | const _vector = new Vector3();
|
4 | const _segCenter = new Vector3();
|
5 | const _segDir = new Vector3();
|
6 | const _diff = new Vector3();
|
7 |
|
8 | const _edge1 = new Vector3();
|
9 | const _edge2 = new Vector3();
|
10 | const _normal = new Vector3();
|
11 |
|
12 | function Ray( origin, direction ) {
|
13 |
|
14 | this.origin = ( origin !== undefined ) ? origin : new Vector3();
|
15 | this.direction = ( direction !== undefined ) ? direction : new Vector3( 0, 0, - 1 );
|
16 |
|
17 | }
|
18 |
|
19 | Object.assign( Ray.prototype, {
|
20 |
|
21 | set: function ( origin, direction ) {
|
22 |
|
23 | this.origin.copy( origin );
|
24 | this.direction.copy( direction );
|
25 |
|
26 | return this;
|
27 |
|
28 | },
|
29 |
|
30 | clone: function () {
|
31 |
|
32 | return new this.constructor().copy( this );
|
33 |
|
34 | },
|
35 |
|
36 | copy: function ( ray ) {
|
37 |
|
38 | this.origin.copy( ray.origin );
|
39 | this.direction.copy( ray.direction );
|
40 |
|
41 | return this;
|
42 |
|
43 | },
|
44 |
|
45 | at: function ( t, target ) {
|
46 |
|
47 | if ( target === undefined ) {
|
48 |
|
49 | console.warn( 'THREE.Ray: .at() target is now required' );
|
50 | target = new Vector3();
|
51 |
|
52 | }
|
53 |
|
54 | return target.copy( this.direction ).multiplyScalar( t ).add( this.origin );
|
55 |
|
56 | },
|
57 |
|
58 | lookAt: function ( v ) {
|
59 |
|
60 | this.direction.copy( v ).sub( this.origin ).normalize();
|
61 |
|
62 | return this;
|
63 |
|
64 | },
|
65 |
|
66 | recast: function ( t ) {
|
67 |
|
68 | this.origin.copy( this.at( t, _vector ) );
|
69 |
|
70 | return this;
|
71 |
|
72 | },
|
73 |
|
74 | closestPointToPoint: function ( point, target ) {
|
75 |
|
76 | if ( target === undefined ) {
|
77 |
|
78 | console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' );
|
79 | target = new Vector3();
|
80 |
|
81 | }
|
82 |
|
83 | target.subVectors( point, this.origin );
|
84 |
|
85 | const directionDistance = target.dot( this.direction );
|
86 |
|
87 | if ( directionDistance < 0 ) {
|
88 |
|
89 | return target.copy( this.origin );
|
90 |
|
91 | }
|
92 |
|
93 | return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
|
94 |
|
95 | },
|
96 |
|
97 | distanceToPoint: function ( point ) {
|
98 |
|
99 | return Math.sqrt( this.distanceSqToPoint( point ) );
|
100 |
|
101 | },
|
102 |
|
103 | distanceSqToPoint: function ( point ) {
|
104 |
|
105 | const directionDistance = _vector.subVectors( point, this.origin ).dot( this.direction );
|
106 |
|
107 |
|
108 |
|
109 | if ( directionDistance < 0 ) {
|
110 |
|
111 | return this.origin.distanceToSquared( point );
|
112 |
|
113 | }
|
114 |
|
115 | _vector.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
|
116 |
|
117 | return _vector.distanceToSquared( point );
|
118 |
|
119 | },
|
120 |
|
121 | distanceSqToSegment: function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 | _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
|
131 | _segDir.copy( v1 ).sub( v0 ).normalize();
|
132 | _diff.copy( this.origin ).sub( _segCenter );
|
133 |
|
134 | const segExtent = v0.distanceTo( v1 ) * 0.5;
|
135 | const a01 = - this.direction.dot( _segDir );
|
136 | const b0 = _diff.dot( this.direction );
|
137 | const b1 = - _diff.dot( _segDir );
|
138 | const c = _diff.lengthSq();
|
139 | const det = Math.abs( 1 - a01 * a01 );
|
140 | let s0, s1, sqrDist, extDet;
|
141 |
|
142 | if ( det > 0 ) {
|
143 |
|
144 |
|
145 |
|
146 | s0 = a01 * b1 - b0;
|
147 | s1 = a01 * b0 - b1;
|
148 | extDet = segExtent * det;
|
149 |
|
150 | if ( s0 >= 0 ) {
|
151 |
|
152 | if ( s1 >= - extDet ) {
|
153 |
|
154 | if ( s1 <= extDet ) {
|
155 |
|
156 |
|
157 |
|
158 |
|
159 | const invDet = 1 / det;
|
160 | s0 *= invDet;
|
161 | s1 *= invDet;
|
162 | sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
|
163 |
|
164 | } else {
|
165 |
|
166 |
|
167 |
|
168 | s1 = segExtent;
|
169 | s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
|
170 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
171 |
|
172 | }
|
173 |
|
174 | } else {
|
175 |
|
176 |
|
177 |
|
178 | s1 = - segExtent;
|
179 | s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
|
180 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
181 |
|
182 | }
|
183 |
|
184 | } else {
|
185 |
|
186 | if ( s1 <= - extDet ) {
|
187 |
|
188 |
|
189 |
|
190 | s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
|
191 | s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
|
192 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
193 |
|
194 | } else if ( s1 <= extDet ) {
|
195 |
|
196 |
|
197 |
|
198 | s0 = 0;
|
199 | s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
|
200 | sqrDist = s1 * ( s1 + 2 * b1 ) + c;
|
201 |
|
202 | } else {
|
203 |
|
204 |
|
205 |
|
206 | s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
|
207 | s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
|
208 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
209 |
|
210 | }
|
211 |
|
212 | }
|
213 |
|
214 | } else {
|
215 |
|
216 |
|
217 |
|
218 | s1 = ( a01 > 0 ) ? - segExtent : segExtent;
|
219 | s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
|
220 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
221 |
|
222 | }
|
223 |
|
224 | if ( optionalPointOnRay ) {
|
225 |
|
226 | optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
|
227 |
|
228 | }
|
229 |
|
230 | if ( optionalPointOnSegment ) {
|
231 |
|
232 | optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter );
|
233 |
|
234 | }
|
235 |
|
236 | return sqrDist;
|
237 |
|
238 | },
|
239 |
|
240 | intersectSphere: function ( sphere, target ) {
|
241 |
|
242 | _vector.subVectors( sphere.center, this.origin );
|
243 | const tca = _vector.dot( this.direction );
|
244 | const d2 = _vector.dot( _vector ) - tca * tca;
|
245 | const radius2 = sphere.radius * sphere.radius;
|
246 |
|
247 | if ( d2 > radius2 ) return null;
|
248 |
|
249 | const thc = Math.sqrt( radius2 - d2 );
|
250 |
|
251 |
|
252 | const t0 = tca - thc;
|
253 |
|
254 |
|
255 | const t1 = tca + thc;
|
256 |
|
257 |
|
258 | if ( t0 < 0 && t1 < 0 ) return null;
|
259 |
|
260 |
|
261 |
|
262 |
|
263 | if ( t0 < 0 ) return this.at( t1, target );
|
264 |
|
265 |
|
266 | return this.at( t0, target );
|
267 |
|
268 | },
|
269 |
|
270 | intersectsSphere: function ( sphere ) {
|
271 |
|
272 | return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius );
|
273 |
|
274 | },
|
275 |
|
276 | distanceToPlane: function ( plane ) {
|
277 |
|
278 | const denominator = plane.normal.dot( this.direction );
|
279 |
|
280 | if ( denominator === 0 ) {
|
281 |
|
282 |
|
283 | if ( plane.distanceToPoint( this.origin ) === 0 ) {
|
284 |
|
285 | return 0;
|
286 |
|
287 | }
|
288 |
|
289 |
|
290 |
|
291 | return null;
|
292 |
|
293 | }
|
294 |
|
295 | const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
|
296 |
|
297 |
|
298 |
|
299 | return t >= 0 ? t : null;
|
300 |
|
301 | },
|
302 |
|
303 | intersectPlane: function ( plane, target ) {
|
304 |
|
305 | const t = this.distanceToPlane( plane );
|
306 |
|
307 | if ( t === null ) {
|
308 |
|
309 | return null;
|
310 |
|
311 | }
|
312 |
|
313 | return this.at( t, target );
|
314 |
|
315 | },
|
316 |
|
317 | intersectsPlane: function ( plane ) {
|
318 |
|
319 |
|
320 |
|
321 | const distToPoint = plane.distanceToPoint( this.origin );
|
322 |
|
323 | if ( distToPoint === 0 ) {
|
324 |
|
325 | return true;
|
326 |
|
327 | }
|
328 |
|
329 | const denominator = plane.normal.dot( this.direction );
|
330 |
|
331 | if ( denominator * distToPoint < 0 ) {
|
332 |
|
333 | return true;
|
334 |
|
335 | }
|
336 |
|
337 |
|
338 |
|
339 | return false;
|
340 |
|
341 | },
|
342 |
|
343 | intersectBox: function ( box, target ) {
|
344 |
|
345 | let tmin, tmax, tymin, tymax, tzmin, tzmax;
|
346 |
|
347 | const invdirx = 1 / this.direction.x,
|
348 | invdiry = 1 / this.direction.y,
|
349 | invdirz = 1 / this.direction.z;
|
350 |
|
351 | const origin = this.origin;
|
352 |
|
353 | if ( invdirx >= 0 ) {
|
354 |
|
355 | tmin = ( box.min.x - origin.x ) * invdirx;
|
356 | tmax = ( box.max.x - origin.x ) * invdirx;
|
357 |
|
358 | } else {
|
359 |
|
360 | tmin = ( box.max.x - origin.x ) * invdirx;
|
361 | tmax = ( box.min.x - origin.x ) * invdirx;
|
362 |
|
363 | }
|
364 |
|
365 | if ( invdiry >= 0 ) {
|
366 |
|
367 | tymin = ( box.min.y - origin.y ) * invdiry;
|
368 | tymax = ( box.max.y - origin.y ) * invdiry;
|
369 |
|
370 | } else {
|
371 |
|
372 | tymin = ( box.max.y - origin.y ) * invdiry;
|
373 | tymax = ( box.min.y - origin.y ) * invdiry;
|
374 |
|
375 | }
|
376 |
|
377 | if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
|
378 |
|
379 |
|
380 |
|
381 |
|
382 | if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
|
383 |
|
384 | if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
|
385 |
|
386 | if ( invdirz >= 0 ) {
|
387 |
|
388 | tzmin = ( box.min.z - origin.z ) * invdirz;
|
389 | tzmax = ( box.max.z - origin.z ) * invdirz;
|
390 |
|
391 | } else {
|
392 |
|
393 | tzmin = ( box.max.z - origin.z ) * invdirz;
|
394 | tzmax = ( box.min.z - origin.z ) * invdirz;
|
395 |
|
396 | }
|
397 |
|
398 | if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
|
399 |
|
400 | if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
|
401 |
|
402 | if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
|
403 |
|
404 |
|
405 |
|
406 | if ( tmax < 0 ) return null;
|
407 |
|
408 | return this.at( tmin >= 0 ? tmin : tmax, target );
|
409 |
|
410 | },
|
411 |
|
412 | intersectsBox: function ( box ) {
|
413 |
|
414 | return this.intersectBox( box, _vector ) !== null;
|
415 |
|
416 | },
|
417 |
|
418 | intersectTriangle: function ( a, b, c, backfaceCulling, target ) {
|
419 |
|
420 |
|
421 |
|
422 |
|
423 |
|
424 | _edge1.subVectors( b, a );
|
425 | _edge2.subVectors( c, a );
|
426 | _normal.crossVectors( _edge1, _edge2 );
|
427 |
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 | let DdN = this.direction.dot( _normal );
|
434 | let sign;
|
435 |
|
436 | if ( DdN > 0 ) {
|
437 |
|
438 | if ( backfaceCulling ) return null;
|
439 | sign = 1;
|
440 |
|
441 | } else if ( DdN < 0 ) {
|
442 |
|
443 | sign = - 1;
|
444 | DdN = - DdN;
|
445 |
|
446 | } else {
|
447 |
|
448 | return null;
|
449 |
|
450 | }
|
451 |
|
452 | _diff.subVectors( this.origin, a );
|
453 | const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) );
|
454 |
|
455 |
|
456 | if ( DdQxE2 < 0 ) {
|
457 |
|
458 | return null;
|
459 |
|
460 | }
|
461 |
|
462 | const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) );
|
463 |
|
464 |
|
465 | if ( DdE1xQ < 0 ) {
|
466 |
|
467 | return null;
|
468 |
|
469 | }
|
470 |
|
471 |
|
472 | if ( DdQxE2 + DdE1xQ > DdN ) {
|
473 |
|
474 | return null;
|
475 |
|
476 | }
|
477 |
|
478 |
|
479 | const QdN = - sign * _diff.dot( _normal );
|
480 |
|
481 |
|
482 | if ( QdN < 0 ) {
|
483 |
|
484 | return null;
|
485 |
|
486 | }
|
487 |
|
488 |
|
489 | return this.at( QdN / DdN, target );
|
490 |
|
491 | },
|
492 |
|
493 | applyMatrix4: function ( matrix4 ) {
|
494 |
|
495 | this.origin.applyMatrix4( matrix4 );
|
496 | this.direction.transformDirection( matrix4 );
|
497 |
|
498 | return this;
|
499 |
|
500 | },
|
501 |
|
502 | equals: function ( ray ) {
|
503 |
|
504 | return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
|
505 |
|
506 | }
|
507 |
|
508 | } );
|
509 |
|
510 |
|
511 | export { Ray };
|