UNPKG

12.9 kBJavaScriptView Raw
1import { Vector3 } from './Vector3.js';
2import { Sphere } from './Sphere.js';
3
4/**
5 * @author bhouston / http://clara.io
6 * @author WestLangley / http://github.com/WestLangley
7 */
8
9function Box3( min, max ) {
10
11 this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );
12 this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );
13
14}
15
16Object.assign( Box3.prototype, {
17
18 isBox3: true,
19
20 set: function ( min, max ) {
21
22 this.min.copy( min );
23 this.max.copy( max );
24
25 return this;
26
27 },
28
29 setFromArray: function ( array ) {
30
31 var minX = + Infinity;
32 var minY = + Infinity;
33 var minZ = + Infinity;
34
35 var maxX = - Infinity;
36 var maxY = - Infinity;
37 var maxZ = - Infinity;
38
39 for ( var i = 0, l = array.length; i < l; i += 3 ) {
40
41 var x = array[ i ];
42 var y = array[ i + 1 ];
43 var z = array[ i + 2 ];
44
45 if ( x < minX ) minX = x;
46 if ( y < minY ) minY = y;
47 if ( z < minZ ) minZ = z;
48
49 if ( x > maxX ) maxX = x;
50 if ( y > maxY ) maxY = y;
51 if ( z > maxZ ) maxZ = z;
52
53 }
54
55 this.min.set( minX, minY, minZ );
56 this.max.set( maxX, maxY, maxZ );
57
58 return this;
59
60 },
61
62 setFromBufferAttribute: function ( attribute ) {
63
64 var minX = + Infinity;
65 var minY = + Infinity;
66 var minZ = + Infinity;
67
68 var maxX = - Infinity;
69 var maxY = - Infinity;
70 var maxZ = - Infinity;
71
72 for ( var i = 0, l = attribute.count; i < l; i ++ ) {
73
74 var x = attribute.getX( i );
75 var y = attribute.getY( i );
76 var z = attribute.getZ( i );
77
78 if ( x < minX ) minX = x;
79 if ( y < minY ) minY = y;
80 if ( z < minZ ) minZ = z;
81
82 if ( x > maxX ) maxX = x;
83 if ( y > maxY ) maxY = y;
84 if ( z > maxZ ) maxZ = z;
85
86 }
87
88 this.min.set( minX, minY, minZ );
89 this.max.set( maxX, maxY, maxZ );
90
91 return this;
92
93 },
94
95 setFromPoints: function ( points ) {
96
97 this.makeEmpty();
98
99 for ( var i = 0, il = points.length; i < il; i ++ ) {
100
101 this.expandByPoint( points[ i ] );
102
103 }
104
105 return this;
106
107 },
108
109 setFromCenterAndSize: function () {
110
111 var v1 = new Vector3();
112
113 return function setFromCenterAndSize( center, size ) {
114
115 var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
116
117 this.min.copy( center ).sub( halfSize );
118 this.max.copy( center ).add( halfSize );
119
120 return this;
121
122 };
123
124 }(),
125
126 setFromObject: function ( object ) {
127
128 this.makeEmpty();
129
130 return this.expandByObject( object );
131
132 },
133
134 clone: function () {
135
136 return new this.constructor().copy( this );
137
138 },
139
140 copy: function ( box ) {
141
142 this.min.copy( box.min );
143 this.max.copy( box.max );
144
145 return this;
146
147 },
148
149 makeEmpty: function () {
150
151 this.min.x = this.min.y = this.min.z = + Infinity;
152 this.max.x = this.max.y = this.max.z = - Infinity;
153
154 return this;
155
156 },
157
158 isEmpty: function () {
159
160 // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
161
162 return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
163
164 },
165
166 getCenter: function ( target ) {
167
168 if ( target === undefined ) {
169
170 console.warn( 'THREE.Box3: .getCenter() target is now required' );
171 target = new Vector3();
172
173 }
174
175 return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
176
177 },
178
179 getSize: function ( target ) {
180
181 if ( target === undefined ) {
182
183 console.warn( 'THREE.Box3: .getSize() target is now required' );
184 target = new Vector3();
185
186 }
187
188 return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );
189
190 },
191
192 expandByPoint: function ( point ) {
193
194 this.min.min( point );
195 this.max.max( point );
196
197 return this;
198
199 },
200
201 expandByVector: function ( vector ) {
202
203 this.min.sub( vector );
204 this.max.add( vector );
205
206 return this;
207
208 },
209
210 expandByScalar: function ( scalar ) {
211
212 this.min.addScalar( - scalar );
213 this.max.addScalar( scalar );
214
215 return this;
216
217 },
218
219 expandByObject: function () {
220
221 // Computes the world-axis-aligned bounding box of an object (including its children),
222 // accounting for both the object's, and children's, world transforms
223
224 var scope, i, l;
225
226 var v1 = new Vector3();
227
228 function traverse( node ) {
229
230 var geometry = node.geometry;
231
232 if ( geometry !== undefined ) {
233
234 if ( geometry.isGeometry ) {
235
236 var vertices = geometry.vertices;
237
238 for ( i = 0, l = vertices.length; i < l; i ++ ) {
239
240 v1.copy( vertices[ i ] );
241 v1.applyMatrix4( node.matrixWorld );
242
243 scope.expandByPoint( v1 );
244
245 }
246
247 } else if ( geometry.isBufferGeometry ) {
248
249 var attribute = geometry.attributes.position;
250
251 if ( attribute !== undefined ) {
252
253 for ( i = 0, l = attribute.count; i < l; i ++ ) {
254
255 v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld );
256
257 scope.expandByPoint( v1 );
258
259 }
260
261 }
262
263 }
264
265 }
266
267 }
268
269 return function expandByObject( object ) {
270
271 scope = this;
272
273 object.updateMatrixWorld( true );
274
275 object.traverse( traverse );
276
277 return this;
278
279 };
280
281 }(),
282
283 containsPoint: function ( point ) {
284
285 return point.x < this.min.x || point.x > this.max.x ||
286 point.y < this.min.y || point.y > this.max.y ||
287 point.z < this.min.z || point.z > this.max.z ? false : true;
288
289 },
290
291 containsBox: function ( box ) {
292
293 return this.min.x <= box.min.x && box.max.x <= this.max.x &&
294 this.min.y <= box.min.y && box.max.y <= this.max.y &&
295 this.min.z <= box.min.z && box.max.z <= this.max.z;
296
297 },
298
299 getParameter: function ( point, target ) {
300
301 // This can potentially have a divide by zero if the box
302 // has a size dimension of 0.
303
304 if ( target === undefined ) {
305
306 console.warn( 'THREE.Box3: .getParameter() target is now required' );
307 target = new Vector3();
308
309 }
310
311 return target.set(
312 ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
313 ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
314 ( point.z - this.min.z ) / ( this.max.z - this.min.z )
315 );
316
317 },
318
319 intersectsBox: function ( box ) {
320
321 // using 6 splitting planes to rule out intersections.
322 return box.max.x < this.min.x || box.min.x > this.max.x ||
323 box.max.y < this.min.y || box.min.y > this.max.y ||
324 box.max.z < this.min.z || box.min.z > this.max.z ? false : true;
325
326 },
327
328 intersectsSphere: ( function () {
329
330 var closestPoint = new Vector3();
331
332 return function intersectsSphere( sphere ) {
333
334 // Find the point on the AABB closest to the sphere center.
335 this.clampPoint( sphere.center, closestPoint );
336
337 // If that point is inside the sphere, the AABB and sphere intersect.
338 return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
339
340 };
341
342 } )(),
343
344 intersectsPlane: function ( plane ) {
345
346 // We compute the minimum and maximum dot product values. If those values
347 // are on the same side (back or front) of the plane, then there is no intersection.
348
349 var min, max;
350
351 if ( plane.normal.x > 0 ) {
352
353 min = plane.normal.x * this.min.x;
354 max = plane.normal.x * this.max.x;
355
356 } else {
357
358 min = plane.normal.x * this.max.x;
359 max = plane.normal.x * this.min.x;
360
361 }
362
363 if ( plane.normal.y > 0 ) {
364
365 min += plane.normal.y * this.min.y;
366 max += plane.normal.y * this.max.y;
367
368 } else {
369
370 min += plane.normal.y * this.max.y;
371 max += plane.normal.y * this.min.y;
372
373 }
374
375 if ( plane.normal.z > 0 ) {
376
377 min += plane.normal.z * this.min.z;
378 max += plane.normal.z * this.max.z;
379
380 } else {
381
382 min += plane.normal.z * this.max.z;
383 max += plane.normal.z * this.min.z;
384
385 }
386
387 return ( min <= plane.constant && max >= plane.constant );
388
389 },
390
391 intersectsTriangle: ( function () {
392
393 // triangle centered vertices
394 var v0 = new Vector3();
395 var v1 = new Vector3();
396 var v2 = new Vector3();
397
398 // triangle edge vectors
399 var f0 = new Vector3();
400 var f1 = new Vector3();
401 var f2 = new Vector3();
402
403 var testAxis = new Vector3();
404
405 var center = new Vector3();
406 var extents = new Vector3();
407
408 var triangleNormal = new Vector3();
409
410 function satForAxes( axes ) {
411
412 var i, j;
413
414 for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) {
415
416 testAxis.fromArray( axes, i );
417 // project the aabb onto the seperating axis
418 var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z );
419 // project all 3 vertices of the triangle onto the seperating axis
420 var p0 = v0.dot( testAxis );
421 var p1 = v1.dot( testAxis );
422 var p2 = v2.dot( testAxis );
423 // actual test, basically see if either of the most extreme of the triangle points intersects r
424 if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
425
426 // points of the projected triangle are outside the projected half-length of the aabb
427 // the axis is seperating and we can exit
428 return false;
429
430 }
431
432 }
433
434 return true;
435
436 }
437
438 return function intersectsTriangle( triangle ) {
439
440 if ( this.isEmpty() ) {
441
442 return false;
443
444 }
445
446 // compute box center and extents
447 this.getCenter( center );
448 extents.subVectors( this.max, center );
449
450 // translate triangle to aabb origin
451 v0.subVectors( triangle.a, center );
452 v1.subVectors( triangle.b, center );
453 v2.subVectors( triangle.c, center );
454
455 // compute edge vectors for triangle
456 f0.subVectors( v1, v0 );
457 f1.subVectors( v2, v1 );
458 f2.subVectors( v0, v2 );
459
460 // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb
461 // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation
462 // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)
463 var axes = [
464 0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y,
465 f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x,
466 - f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0
467 ];
468 if ( ! satForAxes( axes ) ) {
469
470 return false;
471
472 }
473
474 // test 3 face normals from the aabb
475 axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];
476 if ( ! satForAxes( axes ) ) {
477
478 return false;
479
480 }
481
482 // finally testing the face normal of the triangle
483 // use already existing triangle edge vectors here
484 triangleNormal.crossVectors( f0, f1 );
485 axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ];
486 return satForAxes( axes );
487
488 };
489
490 } )(),
491
492 clampPoint: function ( point, target ) {
493
494 if ( target === undefined ) {
495
496 console.warn( 'THREE.Box3: .clampPoint() target is now required' );
497 target = new Vector3();
498
499 }
500
501 return target.copy( point ).clamp( this.min, this.max );
502
503 },
504
505 distanceToPoint: function () {
506
507 var v1 = new Vector3();
508
509 return function distanceToPoint( point ) {
510
511 var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
512 return clampedPoint.sub( point ).length();
513
514 };
515
516 }(),
517
518 getBoundingSphere: function () {
519
520 var v1 = new Vector3();
521
522 return function getBoundingSphere( target ) {
523
524 if ( target === undefined ) {
525
526 console.warn( 'THREE.Box3: .getBoundingSphere() target is now required' );
527 target = new Sphere();
528
529 }
530
531 this.getCenter( target.center );
532
533 target.radius = this.getSize( v1 ).length() * 0.5;
534
535 return target;
536
537 };
538
539 }(),
540
541 intersect: function ( box ) {
542
543 this.min.max( box.min );
544 this.max.min( box.max );
545
546 // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.
547 if ( this.isEmpty() ) this.makeEmpty();
548
549 return this;
550
551 },
552
553 union: function ( box ) {
554
555 this.min.min( box.min );
556 this.max.max( box.max );
557
558 return this;
559
560 },
561
562 applyMatrix4: function () {
563
564 var points = [
565 new Vector3(),
566 new Vector3(),
567 new Vector3(),
568 new Vector3(),
569 new Vector3(),
570 new Vector3(),
571 new Vector3(),
572 new Vector3()
573 ];
574
575 return function applyMatrix4( matrix ) {
576
577 // transform of empty box is an empty box.
578 if ( this.isEmpty() ) return this;
579
580 // NOTE: I am using a binary pattern to specify all 2^3 combinations below
581 points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
582 points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
583 points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
584 points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
585 points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
586 points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
587 points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
588 points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111
589
590 this.setFromPoints( points );
591
592 return this;
593
594 };
595
596 }(),
597
598 translate: function ( offset ) {
599
600 this.min.add( offset );
601 this.max.add( offset );
602
603 return this;
604
605 },
606
607 equals: function ( box ) {
608
609 return box.min.equals( this.min ) && box.max.equals( this.max );
610
611 }
612
613} );
614
615
616export { Box3 };