JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
AABBox.java
Go to the documentation of this file.
1/**
2 * Copyright 2010-2024 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.math.geom;
29
30import com.jogamp.math.FloatUtil;
31import com.jogamp.math.Matrix4f;
32import com.jogamp.math.Quaternion;
33import com.jogamp.math.Ray;
34import com.jogamp.math.Recti;
35import com.jogamp.math.Vec3f;
36import com.jogamp.math.geom.plane.AffineTransform;
37
38
39/**
40 * Axis Aligned Bounding Box. Defined by two 3D coordinates (low and high)
41 * The low being the the lower left corner of the box, and the high being the upper
42 * right corner of the box.
43 * <p>
44 * A few references for collision detection, intersections:
45 * <pre>
46 * http://www.realtimerendering.com/intersections.html
47 * http://www.codercorner.com/RayAABB.cpp
48 * http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter0.htm
49 * http://realtimecollisiondetection.net/files/levine_swept_sat.txt
50 * </pre>
51 * </p>
52 *
53 */
54public final class AABBox {
55 private static final boolean DEBUG = FloatUtil.DEBUG;
56 /** Low left-bottom-far (xyz) coordinate */
57 private final Vec3f lo = new Vec3f();
58 /** High right-top-near (xyz) coordinate */
59 private final Vec3f hi = new Vec3f();
60 /** Computed center of {@link #lo} and {@link #hi}. */
61 private final Vec3f center = new Vec3f();
62
63 /**
64 * Create an Axis Aligned bounding box (AABBox) with the
65 * inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit.
66 * <p>
67 * The dimension, i.e. {@link #getWidth()} abd {@link #getHeight()} is {@link Float#isInfinite()} thereafter.
68 * </p>
69 * @see #reset()
70 */
71 public AABBox() {
72 reset();
73 }
74
75 /**
76 * Create an AABBox copying all values from the given one
77 * @param src the box value to be used for the new instance
78 */
79 public AABBox(final AABBox src) {
80 copy(src);
81 }
82
83 /**
84 * Create an AABBox specifying the coordinates
85 * of the low and high
86 * @param lx min x-coordinate
87 * @param ly min y-coordnate
88 * @param lz min z-coordinate
89 * @param hx max x-coordinate
90 * @param hy max y-coordinate
91 * @param hz max z-coordinate
92 */
93 public AABBox(final float lx, final float ly, final float lz,
94 final float hx, final float hy, final float hz) {
95 setSize(lx, ly, lz, hx, hy, hz);
96 }
97
98 /**
99 * Create a AABBox defining the low and high
100 * @param low min xyz-coordinates
101 * @param high max xyz-coordinates
102 */
103 public AABBox(final float[] low, final float[] high) {
104 setSize(low, high);
105 }
106
107 /**
108 * Create a AABBox defining the low and high
109 * @param low min xyz-coordinates
110 * @param high max xyz-coordinates
111 */
112 public AABBox(final Vec3f low, final Vec3f high) {
113 setSize(low, high);
114 }
115
116 /**
117 * Resets this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit.
118 * <p>
119 * The dimension, i.e. {@link #getWidth()} abd {@link #getHeight()} is {@link Float#isInfinite()} thereafter.
120 * </p>
121 * @return this AABBox for chaining
122 */
123 public final AABBox reset() {
124 setLow(Float.MAX_VALUE,Float.MAX_VALUE,Float.MAX_VALUE);
125 setHigh(-1*Float.MAX_VALUE,-1*Float.MAX_VALUE,-1*Float.MAX_VALUE);
126 center.set( 0f, 0f, 0f);
127 return this;
128 }
129
130 /** Returns the maximum right-top-near (xyz) coordinate */
131 public final Vec3f getHigh() {
132 return hi;
133 }
134
135 private final void setHigh(final float hx, final float hy, final float hz) {
136 this.hi.set(hx, hy, hz);
137 }
138
139 /** Returns the minimum left-bottom-far (xyz) coordinate */
140 public final Vec3f getLow() {
141 return lo;
142 }
143
144 private final void setLow(final float lx, final float ly, final float lz) {
145 this.lo.set(lx, ly, lz);
146 }
147
148 private final void computeCenter() {
149 center.set(hi).add(lo).scale(1f/2f);
150 }
151
152 /**
153 * Copy given AABBox 'src' values to this AABBox.
154 *
155 * @param src source AABBox
156 * @return this AABBox for chaining
157 */
158 public final AABBox copy(final AABBox src) {
159 lo.set(src.lo);
160 hi.set(src.hi);
161 center.set(src.center);
162 return this;
163 }
164
165 /**
166 * Set size of the AABBox specifying the coordinates
167 * of the low and high.
168 *
169 * @param low min xyz-coordinates
170 * @param high max xyz-coordinates
171 * @return this AABBox for chaining
172 */
173 public final AABBox setSize(final float[] low, final float[] high) {
174 return setSize(low[0],low[1],low[2], high[0],high[1],high[2]);
175 }
176
177 /**
178 * Set size of the AABBox specifying the coordinates
179 * of the low and high.
180 *
181 * @param lx min x-coordinate
182 * @param ly min y-coordnate
183 * @param lz min z-coordinate
184 * @param hx max x-coordinate
185 * @param hy max y-coordinate
186 * @param hz max z-coordinate
187 * @return this AABBox for chaining
188 */
189 public final AABBox setSize(final float lx, final float ly, final float lz,
190 final float hx, final float hy, final float hz) {
191 this.lo.set(lx, ly, lz);
192 this.hi.set(hx, hy, hz);
193 computeCenter();
194 return this;
195 }
196
197 /**
198 * Set size of the AABBox specifying the coordinates
199 * of the low and high.
200 *
201 * @param low min xyz-coordinates
202 * @param high max xyz-coordinates
203 * @return this AABBox for chaining
204 */
205 public final AABBox setSize(final Vec3f low, final Vec3f high) {
206 this.lo.set(low);
207 this.hi.set(high);
208 computeCenter();
209 return this;
210 }
211
212 /**
213 * Resize width of this AABBox with explicit left- and right delta values
214 * @param deltaLeft positive value will expand width, otherwise shrink width
215 * @param deltaRight positive value will expand width, otherwise shrink width
216 * @return this AABBox for chaining
217 */
218 public final AABBox resizeWidth(final float deltaLeft, final float deltaRight) {
219 boolean mod = false;
220 if( !FloatUtil.isZero(deltaLeft) ) {
221 lo.setX( lo.x() - deltaLeft );
222 mod = true;
223 }
224 if( !FloatUtil.isZero(deltaRight) ) {
225 hi.setX( hi.x() + deltaRight );
226 mod = true;
227 }
228 if( mod ) {
229 computeCenter();
230 }
231 return this;
232 }
233
234 /**
235 * Resize height of this AABBox with explicit bottom- and top delta values
236 * @param deltaBottom positive value will expand height, otherwise shrink height
237 * @param deltaTop positive value will expand height, otherwise shrink height
238 * @return this AABBox for chaining
239 */
240 public final AABBox resizeHeight(final float deltaBottom, final float deltaTop) {
241 boolean mod = false;
242 if( !FloatUtil.isZero(deltaBottom) ) {
243 lo.setY( lo.y() - deltaBottom );
244 mod = true;
245 }
246 if( !FloatUtil.isZero(deltaTop) ) {
247 hi.setY( hi.y() + deltaTop );
248 mod = true;
249 }
250 if( mod ) {
251 computeCenter();
252 }
253 return this;
254 }
255
256 /**
257 * Assign values of given AABBox to this instance.
258 *
259 * @param o source AABBox
260 * @return this AABBox for chaining
261 */
262 public final AABBox set(final AABBox o) {
263 this.lo.set(o.lo);
264 this.hi.set(o.hi);
265 this.center.set(o.center);
266 return this;
267 }
268
269 /**
270 * Resize the AABBox to encapsulate another AABox
271 * @param newBox AABBox to be encapsulated in
272 * @return this AABBox for chaining
273 */
274 public final AABBox resize(final AABBox newBox) {
275 final Vec3f newBL = newBox.getLow();
276 final Vec3f newTR = newBox.getHigh();
277
278 /** test low */
279 if (newBL.x() < lo.x()) {
280 lo.setX( newBL.x() );
281 }
282 if (newBL.y() < lo.y()) {
283 lo.setY( newBL.y() );
284 }
285 if (newBL.z() < lo.z()) {
286 lo.setZ( newBL.z() );
287 }
288
289 /** test high */
290 if (newTR.x() > hi.x()) {
291 hi.setX( newTR.x() );
292 }
293 if (newTR.y() > hi.y()) {
294 hi.setY( newTR.y() );
295 }
296 if (newTR.z() > hi.z()) {
297 hi.setZ( newTR.z() );
298 }
299 computeCenter();
300 return this;
301 }
302
303 /**
304 * Resize the AABBox to encapsulate another AABox, which will be <i>transformed</i> on the fly first.
305 * @param newBox AABBox to be encapsulated in
306 * @param t the {@link AffineTransform} applied on <i>newBox</i> on the fly
307 * @param tmpV3 temporary storage
308 * @return this AABBox for chaining
309 */
310 public final AABBox resize(final AABBox newBox, final AffineTransform t, final Vec3f tmpV3) {
311 /** test low */
312 {
313 final Vec3f newBL = t.transform(newBox.getLow(), tmpV3);
314 if (newBL.x() < lo.x())
315 lo.setX( newBL.x() );
316 if (newBL.y() < lo.y())
317 lo.setY( newBL.y() );
318 if (newBL.z() < lo.z())
319 lo.setZ( newBL.z() );
320 }
321
322 /** test high */
323 {
324 final Vec3f newTR = t.transform(newBox.getHigh(), tmpV3);
325 if (newTR.x() > hi.x())
326 hi.setX( newTR.x() );
327 if (newTR.y() > hi.y())
328 hi.setY( newTR.y() );
329 if (newTR.z() > hi.z())
330 hi.setZ( newTR.z() );
331 }
332
333 computeCenter();
334 return this;
335 }
336
337 /**
338 * Resize the AABBox to encapsulate the passed
339 * xyz-coordinates.
340 * @param x x-axis coordinate value
341 * @param y y-axis coordinate value
342 * @param z z-axis coordinate value
343 * @return this AABBox for chaining
344 */
345 public final AABBox resize(final float x, final float y, final float z) {
346 /** test low */
347 if (x < lo.x()) {
348 lo.setX( x );
349 }
350 if (y < lo.y()) {
351 lo.setY( y );
352 }
353 if (z < lo.z()) {
354 lo.setZ( z );
355 }
356
357 /** test high */
358 if (x > hi.x()) {
359 hi.setX( x );
360 }
361 if (y > hi.y()) {
362 hi.setY( y );
363 }
364 if (z > hi.z()) {
365 hi.setZ( z );
366 }
367
368 computeCenter();
369 return this;
370 }
371
372 /**
373 * Resize the AABBox to encapsulate the passed
374 * xyz-coordinates.
375 * @param xyz xyz-axis coordinate values
376 * @param offset of the array
377 * @return this AABBox for chaining
378 */
379 public final AABBox resize(final float[] xyz, final int offset) {
380 return resize(xyz[0+offset], xyz[1+offset], xyz[2+offset]);
381 }
382
383 /**
384 * Resize the AABBox to encapsulate the passed
385 * xyz-coordinates.
386 * @param xyz xyz-axis coordinate values
387 * @return this AABBox for chaining
388 */
389 public final AABBox resize(final float[] xyz) {
390 return resize(xyz[0], xyz[1], xyz[2]);
391 }
392
393 /**
394 * Resize the AABBox to encapsulate the passed
395 * xyz-coordinates.
396 * @param xyz xyz-axis coordinate values
397 * @return this AABBox for chaining
398 */
399 public final AABBox resize(final Vec3f xyz) {
400 return resize(xyz.x(), xyz.y(), xyz.z());
401 }
402
403 /**
404 * Returns whether this AABBox contains given 2D point.
405 * @param x x-axis coordinate value
406 * @param y y-axis coordinate value
407 */
408 public final boolean contains(final float x, final float y) {
409 return lo.x()<=x && x<=hi.x() &&
410 lo.y()<=y && y<=hi.y();
411 }
412
413 /**
414 * Returns whether this AABBox contains given 3D point.
415 * @param x x-axis coordinate value
416 * @param y y-axis coordinate value
417 * @param z z-axis coordinate value
418 */
419 public final boolean contains(final float x, final float y, final float z) {
420 return lo.x()<=x && x<=hi.x() &&
421 lo.y()<=y && y<=hi.y() &&
422 lo.z()<=z && z<=hi.z();
423 }
424
425 /** Returns whether this AABBox intersects (partially contains) given AABBox. */
426 public final boolean intersects(final AABBox o) {
427 /**
428 * Traditional boolean equation leads to multiple branches,
429 * using max/min approach allowing for branch-less optimizations.
430 *
431 return !( hi.x() < o.lo.x() ||
432 hi.y() < o.lo.y() ||
433 hi.z() < o.lo.z() ||
434 lo.x() > o.hi.x() ||
435 lo.y() > o.hi.y() ||
436 lo.z() > o.hi.z());
437 */
438 return Math.max(lo.x(), o.lo.x()) <= Math.min(hi.x(), o.hi.x()) &&
439 Math.max(lo.y(), o.lo.y()) <= Math.min(hi.y(), o.hi.y()) &&
440 Math.max(lo.z(), o.lo.z()) <= Math.min(hi.z(), o.hi.z());
441 }
442
443 /** Returns whether this AABBox fully contains given AABBox. */
444 public final boolean contains(final AABBox o) {
445 return hi.x() >= o.hi.x() &&
446 hi.y() >= o.hi.y() &&
447 hi.z() >= o.hi.z() &&
448 lo.x() <= o.lo.x() &&
449 lo.y() <= o.lo.y() &&
450 lo.z() <= o.lo.z();
451 }
452
453 /**
454 * Check if there is a common region between this AABBox and the passed
455 * 2D region irrespective of z range
456 * @param x lower left x-coord
457 * @param y lower left y-coord
458 * @param w width
459 * @param h hight
460 * @return true if this AABBox might have a common region with this 2D region
461 */
462 public final boolean intersects2DRegion(final float x, final float y, final float w, final float h) {
463 if (w <= 0 || h <= 0) {
464 return false;
465 }
466
467 final float _w = getWidth();
468 final float _h = getHeight();
469 if (_w <= 0 || _h <= 0) {
470 return false;
471 }
472
473 final float x0 = getMinX();
474 final float y0 = getMinY();
475 return (x >= x0 &&
476 y >= y0 &&
477 x + w <= x0 + _w &&
478 y + h <= y0 + _h);
479 }
480
481 /**
482 * Check if {@link Ray} intersects this bounding box.
483 * <p>
484 * Versions uses the SAT[1], testing 6 axes.
485 * Original code for OBBs from MAGIC.
486 * Rewritten for AABBs and reorganized for early exits[2].
487 * </p>
488 * <pre>
489 * [1] SAT = Separating Axis Theorem
490 * [2] http://www.codercorner.com/RayAABB.cpp
491 * </pre>
492 * @param ray
493 * @return
494 */
495 public final boolean intersectsRay(final Ray ray) {
496 // diff[XYZ] -> VectorUtil.subVec3(diff, ray.orig, center);
497 // ext[XYZ] -> extend VectorUtil.subVec3(ext, high, center);
498
499 final float dirX = ray.dir.x();
500 final float diffX = ray.orig.x() - center.x();
501 final float extX = hi.x() - center.x();
502 if( Math.abs(diffX) > extX && diffX*dirX >= 0f ) return false;
503
504 final float dirY = ray.dir.y();
505 final float diffY = ray.orig.y() - center.y();
506 final float extY = hi.y() - center.y();
507 if( Math.abs(diffY) > extY && diffY*dirY >= 0f ) return false;
508
509 final float dirZ = ray.dir.z();
510 final float diffZ = ray.orig.z() - center.z();
511 final float extZ = hi.z() - center.z();
512 if( Math.abs(diffZ) > extZ && diffZ*dirZ >= 0f ) return false;
513
514 final float absDirY = Math.abs(dirY);
515 final float absDirZ = Math.abs(dirZ);
516
517 float f = dirY * diffZ - dirZ * diffY;
518 if( Math.abs(f) > extY*absDirZ + extZ*absDirY ) return false;
519
520 final float absDirX = Math.abs(dirX);
521
522 f = dirZ * diffX - dirX * diffZ;
523 if( Math.abs(f) > extX*absDirZ + extZ*absDirX ) return false;
524
525 f = dirX * diffY - dirY * diffX;
526 if( Math.abs(f) > extX*absDirY + extY*absDirX ) return false;
527
528 return true;
529 }
530
531 /**
532 * Return intersection of a {@link Ray} with this bounding box,
533 * or null if none exist.
534 * <p>
535 * <ul>
536 * <li>Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 [2]</li>
537 * <li>Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)</li>
538 * <li>Epsilon value added by Klaus Hartmann.</li>
539 * </ul>
540 * </p>
541 * <p>
542 * Method is based on the requirements:
543 * <ul>
544 * <li>the integer representation of 0.0f is 0x00000000</li>
545 * <li>the sign bit of the float is the most significant one</li>
546 * </ul>
547 * </p>
548 * <p>
549 * Report bugs: p.terdiman@codercorner.com (original author)
550 * </p>
551 * <pre>
552 * [1] http://www.codercorner.com/RayAABB.cpp
553 * [2] http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c
554 * </pre>
555 * @param result vec3
556 * @param ray
557 * @param epsilon
558 * @param assumeIntersection if true, method assumes an intersection, i.e. by pre-checking via {@link #intersectsRay(Ray)}.
559 * In this case method will not validate a possible non-intersection and just computes
560 * coordinates.
561 * @return float[3] result of intersection coordinates, or null if none exists
562 */
563 public final Vec3f getRayIntersection(final Vec3f result, final Ray ray, final float epsilon,
564 final boolean assumeIntersection) {
565 final float[] maxT = { -1f, -1f, -1f };
566
567 final Vec3f origin = ray.orig;
568 final Vec3f dir = ray.dir;
569
570 boolean inside = true;
571
572 /**
573 * Use unrolled version below...
574 *
575 * Find candidate planes.
576 for(int i=0; i<3; i++) {
577 final float origin_i = origin.get(i);
578 final float dir_i = dir.get(i);
579 final float bl_i = bl.get(i);
580 final float tr_i = tr.get(i);
581 if(origin_i < bl_i) {
582 result.set(i, bl_i);
583 inside = false;
584
585 // Calculate T distances to candidate planes
586 if( 0 != Float.floatToIntBits(dir_i) ) {
587 maxT[i] = (bl_i - origin_i) / dir_i;
588 }
589 } else if(origin_i > tr_i) {
590 result.set(i, tr_i);
591 inside = false;
592
593 // Calculate T distances to candidate planes
594 if( 0 != Float.floatToIntBits(dir_i) ) {
595 maxT[i] = (tr_i - origin_i) / dir_i;
596 }
597 }
598 }
599 */
600 // Find candidate planes, unrolled
601 {
602 if(origin.x() < lo.x()) {
603 result.setX(lo.x());
604 inside = false;
605
606 // Calculate T distances to candidate planes
607 if( 0 != Float.floatToIntBits(dir.x()) ) {
608 maxT[0] = (lo.x() - origin.x()) / dir.x();
609 }
610 } else if(origin.x() > hi.x()) {
611 result.setX(hi.x());
612 inside = false;
613
614 // Calculate T distances to candidate planes
615 if( 0 != Float.floatToIntBits(dir.x()) ) {
616 maxT[0] = (hi.x() - origin.x()) / dir.x();
617 }
618 }
619 }
620 {
621 if(origin.y() < lo.y()) {
622 result.setY(lo.y());
623 inside = false;
624
625 // Calculate T distances to candidate planes
626 if( 0 != Float.floatToIntBits(dir.y()) ) {
627 maxT[1] = (lo.y() - origin.y()) / dir.y();
628 }
629 } else if(origin.y() > hi.y()) {
630 result.setY(hi.y());
631 inside = false;
632
633 // Calculate T distances to candidate planes
634 if( 0 != Float.floatToIntBits(dir.y()) ) {
635 maxT[1] = (hi.y() - origin.y()) / dir.y();
636 }
637 }
638 }
639 {
640 if(origin.z() < lo.z()) {
641 result.setZ(lo.z());
642 inside = false;
643
644 // Calculate T distances to candidate planes
645 if( 0 != Float.floatToIntBits(dir.z()) ) {
646 maxT[2] = (lo.z() - origin.z()) / dir.z();
647 }
648 } else if(origin.z() > hi.z()) {
649 result.setZ(hi.z());
650 inside = false;
651
652 // Calculate T distances to candidate planes
653 if( 0 != Float.floatToIntBits(dir.z()) ) {
654 maxT[2] = (hi.z() - origin.z()) / dir.z();
655 }
656 }
657 }
658
659 // Ray origin inside bounding box
660 if(inside) {
661 result.set(origin);
662 return result;
663 }
664
665 // Get largest of the maxT's for final choice of intersection
666 int whichPlane = 0;
667 if(maxT[1] > maxT[whichPlane]) { whichPlane = 1; }
668 if(maxT[2] > maxT[whichPlane]) { whichPlane = 2; }
669
670 if( !assumeIntersection ) {
671 // Check final candidate actually inside box
672 if( 0 != ( Float.floatToIntBits(maxT[whichPlane]) & FloatUtil.IEC559_SIGN_BIT ) ) {
673 return null;
674 }
675
676 /** Use unrolled version below ..
677 for(int i=0; i<3; i++) {
678 if( i!=whichPlane ) {
679 result[i] = origin[i] + maxT[whichPlane] * dir[i];
680 if(result[i] < minB[i] - epsilon || result[i] > maxB[i] + epsilon) { return null; }
681 // if(result[i] < minB[i] || result[i] > maxB[i] ) { return null; }
682 }
683 } */
684 switch( whichPlane ) {
685 case 0:
686 result.setY( origin.y() + maxT[whichPlane] * dir.y() );
687 if(result.y() < lo.y() - epsilon || result.y() > hi.y() + epsilon) { return null; }
688 result.setZ( origin.z() + maxT[whichPlane] * dir.z() );
689 if(result.z() < lo.z() - epsilon || result.z() > hi.z() + epsilon) { return null; }
690 break;
691 case 1:
692 result.setX( origin.x() + maxT[whichPlane] * dir.x() );
693 if(result.x() < lo.x() - epsilon || result.x() > hi.x() + epsilon) { return null; }
694 result.setZ( origin.z() + maxT[whichPlane] * dir.z() );
695 if(result.z() < lo.z() - epsilon || result.z() > hi.z() + epsilon) { return null; }
696 break;
697 case 2:
698 result.setX( origin.x() + maxT[whichPlane] * dir.x() );
699 if(result.x() < lo.x() - epsilon || result.x() > hi.x() + epsilon) { return null; }
700 result.setY( origin.y() + maxT[whichPlane] * dir.y() );
701 if(result.y() < lo.y() - epsilon || result.y() > hi.y() + epsilon) { return null; }
702 break;
703 default:
704 throw new InternalError("XXX");
705 }
706 } else {
707 switch( whichPlane ) {
708 case 0:
709 result.setY( origin.y() + maxT[whichPlane] * dir.y() );
710 result.setZ( origin.z() + maxT[whichPlane] * dir.z() );
711 break;
712 case 1:
713 result.setX( origin.x() + maxT[whichPlane] * dir.x() );
714 result.setZ( origin.z() + maxT[whichPlane] * dir.z() );
715 break;
716 case 2:
717 result.setX( origin.x() + maxT[whichPlane] * dir.x() );
718 result.setY( origin.y() + maxT[whichPlane] * dir.y() );
719 break;
720 default:
721 throw new InternalError("XXX");
722 }
723 }
724 return result; // ray hits box
725 }
726
727 /**
728 * Get the size of this AABBox where the size is represented by the
729 * length of the vector between low and high.
730 * @return a float representing the size of the AABBox
731 */
732 public final float getSize() {
733 return lo.dist(hi);
734 }
735
736 /** Returns computed center of this AABBox of {@link #getLow()} and {@link #getHigh()}. */
737 public final Vec3f getCenter() {
738 return center;
739 }
740
741 /**
742 * Scale this AABBox by a constant around fixed center
743 * <p>
744 * high and low is recomputed by scaling its distance to fixed center.
745 * </p>
746 * @param s scale factor
747 * @return this AABBox for chaining
748 * @see #scale2(float, float[])
749 */
750 public final AABBox scale(final float s) {
751 final Vec3f tmp = new Vec3f();
752 tmp.set(hi).sub(center).scale(s);
753 hi.set(center).add(tmp);
754
755 tmp.set(lo).sub(center).scale(s);
756 lo.set(center).add(tmp);
757
758 return this;
759 }
760 /**
761 * Scale this AABBox by constants around fixed center
762 * <p>
763 * high and low is recomputed by scaling its distance to fixed center.
764 * </p>
765 * @param sX horizontal scale factor
766 * @param sY vertical scale factor
767 * @param sZ Z-axis scale factor
768 * @return this AABBox for chaining
769 * @see #scale2(float, float[])
770 */
771 public final AABBox scale(final float sX, final float sY, final float sZ) {
772 final Vec3f tmp = new Vec3f();
773 tmp.set(hi).sub(center).mul(sX, sY, sZ);
774 hi.set(center).add(tmp);
775
776 tmp.set(lo).sub(center).mul(sX, sY, sZ);
777 lo.set(center).add(tmp);
778
779 return this;
780 }
781
782 /**
783 * Scale this AABBox by a constant, recomputing center
784 * <p>
785 * high and low is scaled and center recomputed.
786 * </p>
787 * @param s scale factor
788 * @return this AABBox for chaining
789 * @see #scale(float, float[])
790 */
791 public final AABBox scale2(final float s) {
792 hi.scale(s);
793 lo.scale(s);
794 computeCenter();
795 return this;
796 }
797
798 /**
799 * Scale this AABBox by constants, recomputing center
800 * <p>
801 * high and low is scaled and center recomputed.
802 * </p>
803 * @param sX horizontal scale factor
804 * @param sY vertical scale factor
805 * @param sZ Z-axis scale factor
806 * @return this AABBox for chaining
807 * @see #scale(float, float[])
808 */
809 public final AABBox scale2(final float sX, final float sY, final float sZ) {
810 hi.mul(sX, sY, sZ);
811 lo.mul(sX, sY, sZ);
812 computeCenter();
813 return this;
814 }
815
816 /**
817 * Translate this AABBox by a float[3] vector
818 * @param dx the translation x-component
819 * @param dy the translation y-component
820 * @param dz the translation z-component
821 * @param t the float[3] translation vector
822 * @return this AABBox for chaining
823 */
824 public final AABBox translate(final float dx, final float dy, final float dz) {
825 lo.add(dx, dy, dz);
826 hi.add(dx, dy, dz);
827 computeCenter();
828 return this;
829 }
830
831 /**
832 * Translate this AABBox by a float[3] vector
833 * @param t the float[3] translation vector
834 * @return this AABBox for chaining
835 */
836 public final AABBox translate(final Vec3f t) {
837 lo.add(t);
838 hi.add(t);
839 computeCenter();
840 return this;
841 }
842
843 /**
844 * Rotate this AABBox by a float[3] vector
845 * @param quat the {@link Quaternion} used for rotation
846 * @return this AABBox for chaining
847 */
848 public final AABBox rotate(final Quaternion quat) {
849 quat.rotateVector(lo, lo);
850 quat.rotateVector(hi, hi);
851 computeCenter();
852 return this;
853 }
854
855 public final float getMinX() {
856 return lo.x();
857 }
858
859 public final float getMinY() {
860 return lo.y();
861 }
862
863 public final float getMinZ() {
864 return lo.z();
865 }
866
867 public final float getMaxX() {
868 return hi.x();
869 }
870
871 public final float getMaxY() {
872 return hi.y();
873 }
874
875 public final float getMaxZ() {
876 return hi.z();
877 }
878
879 public final float getWidth(){
880 return hi.x() - lo.x();
881 }
882
883 public final float getHeight() {
884 return hi.y() - lo.y();
885 }
886
887 public final float getDepth() {
888 return hi.z() - lo.z();
889 }
890
891 /** Returns the volume, i.e. width * height * depth */
892 public final float getVolume() {
893 return getWidth() * getHeight() * getDepth();
894 }
895
896 /** Return true if {@link #getVolume()} is {@link FloatUtil#isZero(float)}, considering epsilon. */
897 public final boolean hasZeroVolume() {
898 return FloatUtil.isZero(getVolume());
899 }
900
901 /** Returns the assumed 2D area, i.e. width * height while assuming low and high lies on same plane. */
902 public final float get2DArea() {
903 return getWidth() * getHeight();
904 }
905
906 /** Return true if {@link #get2DArea()} is {@link FloatUtil#isZero(float)}, considering epsilon. */
907 public final boolean hasZero2DArea() {
908 return FloatUtil.isZero(get2DArea());
909 }
910
911 @Override
912 public final boolean equals(final Object obj) {
913 if( obj == this ) {
914 return true;
915 }
916 if( null == obj || !(obj instanceof AABBox) ) {
917 return false;
918 }
919 final AABBox other = (AABBox) obj;
920 return lo.isEqual(other.lo) && hi.isEqual(other.hi);
921 }
922 @Override
923 public final int hashCode() {
924 throw new InternalError("hashCode not designed");
925 }
926
927 /**
928 * Transform this box using the given {@link Matrix4f} into {@code out}
929 * @param mat transformation {@link Matrix4f}
930 * @param out the resulting {@link AABBox}
931 * @return the resulting {@link AABBox} for chaining
932 */
933 public AABBox transform(final Matrix4f mat, final AABBox out) {
934 final Vec3f tmp = new Vec3f();
935 out.reset();
936 out.resize( mat.mulVec3f(lo, tmp) );
937 out.resize( mat.mulVec3f(hi, tmp) );
938 out.computeCenter();
939 return out;
940 }
941
942 /**
943 * Assume this bounding box as being in object space and
944 * compute the window bounding box.
945 * <p>
946 * If <code>useCenterZ</code> is <code>true</code>,
947 * only 4 {@link FloatUtil#mapObjToWin(float, float, float, float[], int[], float[], float[], float[]) mapObjToWinCoords}
948 * operations are made on points [1..4] using {@link #getCenter()}'s z-value.
949 * Otherwise 8 {@link FloatUtil#mapObjToWin(float, float, float, float[], int[], float[], float[], float[]) mapObjToWinCoords}
950 * operation on all 8 points are performed.
951 * </p>
952 * <pre>
953 * .z() ------ [4]
954 * | |
955 * | |
956 * .y() ------ [3]
957 * </pre>
958 * @param mat4PMv [projection] x [modelview] matrix, i.e. P x Mv
959 * @param viewport viewport rectangle
960 * @param useCenterZ
961 * @param vec3Tmp0 3 component vector for temp storage
962 * @param vec4Tmp1 4 component vector for temp storage
963 * @param vec4Tmp2 4 component vector for temp storage
964 * @return
965 */
966 public AABBox mapToWindow(final AABBox result, final Matrix4f mat4PMv, final Recti viewport, final boolean useCenterZ) {
967 final Vec3f tmp = new Vec3f();
968 final Vec3f winPos = new Vec3f();
969 {
970 final float objZ = useCenterZ ? center.z() : getMinZ();
971 result.reset();
972
973 Matrix4f.mapObjToWin(tmp.set(getMinX(), getMinY(), objZ), mat4PMv, viewport, winPos);
974 result.resize(winPos);
975
976 Matrix4f.mapObjToWin(tmp.set(getMinX(), getMaxY(), objZ), mat4PMv, viewport, winPos);
977 result.resize(winPos);
978
979 Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMaxY(), objZ), mat4PMv, viewport, winPos);
980 result.resize(winPos);
981
982 Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMinY(), objZ), mat4PMv, viewport, winPos);
983 result.resize(winPos);
984 }
985
986 if( !useCenterZ ) {
987 final float objZ = getMaxZ();
988
989 Matrix4f.mapObjToWin(tmp.set(getMinX(), getMinY(), objZ), mat4PMv, viewport, winPos);
990 result.resize(winPos);
991
992 Matrix4f.mapObjToWin(tmp.set(getMinX(), getMaxY(), objZ), mat4PMv, viewport, winPos);
993 result.resize(winPos);
994
995 Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMaxY(), objZ), mat4PMv, viewport, winPos);
996 result.resize(winPos);
997
998 Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMinY(), objZ), mat4PMv, viewport, winPos);
999 result.resize(winPos);
1000 }
1001 if( DEBUG ) {
1002 System.err.printf("AABBox.mapToWindow: view[%s], this %s -> %s%n", viewport, toString(), result.toString());
1003 }
1004 return result;
1005 }
1006
1007 @Override
1008 public final String toString() {
1009 return "[dim "+getWidth()+" x "+getHeight()+" x "+getDepth()+
1010 ", box "+lo+" .. "+hi+", ctr "+center+"]";
1011 }
1012}
Basic Float math utility functions.
Definition: FloatUtil.java:83
static boolean isZero(final float a, final float epsilon)
Returns true if value is zero, i.e.
static final boolean DEBUG
Definition: FloatUtil.java:84
Basic 4x4 float matrix implementation using fields for intensive use-cases (host operations).
Definition: Matrix4f.java:89
static boolean mapObjToWin(final Vec3f obj, final Matrix4f mMv, final Matrix4f mP, final Recti viewport, final Vec3f winPos)
Map object coordinates to window coordinates.
Definition: Matrix4f.java:1696
final Vec3f mulVec3f(final Vec3f v_in, final Vec3f v_out)
Affine 3f-vector transformation by 4x4 matrix.
Definition: Matrix4f.java:841
Quaternion implementation supporting Gimbal-Lock free rotations.
Definition: Quaternion.java:45
final Vec3f rotateVector(final Vec3f vecIn, final Vec3f vecOut)
Simple compound denoting a ray.
Definition: Ray.java:49
final Vec3f dir
Normalized direction vector of ray.
Definition: Ray.java:54
final Vec3f orig
Origin of Ray.
Definition: Ray.java:51
Rectangle with x, y, width and height integer components.
Definition: Recti.java:34
3D Vector based upon three float components.
Definition: Vec3f.java:37
Vec3f mul(final float val)
Returns this * val; creates new vector.
Definition: Vec3f.java:178
void setX(final float x)
Definition: Vec3f.java:158
Vec3f scale(final float s)
this = this * s, returns this.
Definition: Vec3f.java:218
Vec3f sub(final Vec3f b)
this = this - b, returns this.
Definition: Vec3f.java:268
boolean isEqual(final Vec3f o, final float epsilon)
Equals check using a given FloatUtil#EPSILON value and FloatUtil#isEqual(float, float,...
Definition: Vec3f.java:383
void setZ(final float z)
Definition: Vec3f.java:160
float dist(final Vec3f o)
Return the distance between this vector and the given one.
Definition: Vec3f.java:329
void setY(final float y)
Definition: Vec3f.java:159
Vec3f set(final Vec3f o)
this = o, returns this.
Definition: Vec3f.java:79
Vec3f add(final float dx, final float dy, final float dz)
this = this + { dx, dy, dz }, returns this.
Definition: Vec3f.java:239
Axis Aligned Bounding Box.
Definition: AABBox.java:54
final boolean contains(final float x, final float y, final float z)
Returns whether this AABBox contains given 3D point.
Definition: AABBox.java:419
final AABBox resize(final float x, final float y, final float z)
Resize the AABBox to encapsulate the passed xyz-coordinates.
Definition: AABBox.java:345
final AABBox scale(final float sX, final float sY, final float sZ)
Scale this AABBox by constants around fixed center.
Definition: AABBox.java:771
final String toString()
Definition: AABBox.java:1008
final AABBox resize(final AABBox newBox, final AffineTransform t, final Vec3f tmpV3)
Resize the AABBox to encapsulate another AABox, which will be transformed on the fly first.
Definition: AABBox.java:310
AABBox(final float lx, final float ly, final float lz, final float hx, final float hy, final float hz)
Create an AABBox specifying the coordinates of the low and high.
Definition: AABBox.java:93
AABBox(final AABBox src)
Create an AABBox copying all values from the given one.
Definition: AABBox.java:79
final AABBox resizeHeight(final float deltaBottom, final float deltaTop)
Resize height of this AABBox with explicit bottom- and top delta values.
Definition: AABBox.java:240
final AABBox scale2(final float s)
Scale this AABBox by a constant, recomputing center.
Definition: AABBox.java:791
final Vec3f getHigh()
Returns the maximum right-top-near (xyz) coordinate.
Definition: AABBox.java:131
final float getWidth()
Definition: AABBox.java:879
final Vec3f getLow()
Returns the minimum left-bottom-far (xyz) coordinate.
Definition: AABBox.java:140
final AABBox scale2(final float sX, final float sY, final float sZ)
Scale this AABBox by constants, recomputing center.
Definition: AABBox.java:809
final float get2DArea()
Returns the assumed 2D area, i.e.
Definition: AABBox.java:902
final boolean contains(final float x, final float y)
Returns whether this AABBox contains given 2D point.
Definition: AABBox.java:408
final float getHeight()
Definition: AABBox.java:883
final boolean contains(final AABBox o)
Returns whether this AABBox fully contains given AABBox.
Definition: AABBox.java:444
AABBox mapToWindow(final AABBox result, final Matrix4f mat4PMv, final Recti viewport, final boolean useCenterZ)
Assume this bounding box as being in object space and compute the window bounding box.
Definition: AABBox.java:966
final AABBox translate(final float dx, final float dy, final float dz)
Translate this AABBox by a float[3] vector.
Definition: AABBox.java:824
final AABBox setSize(final float lx, final float ly, final float lz, final float hx, final float hy, final float hz)
Set size of the AABBox specifying the coordinates of the low and high.
Definition: AABBox.java:189
AABBox()
Create an Axis Aligned bounding box (AABBox) with the inverse low/high, allowing the next resize(floa...
Definition: AABBox.java:71
final AABBox resize(final float[] xyz)
Resize the AABBox to encapsulate the passed xyz-coordinates.
Definition: AABBox.java:389
AABBox(final float[] low, final float[] high)
Create a AABBox defining the low and high.
Definition: AABBox.java:103
final boolean equals(final Object obj)
Definition: AABBox.java:912
final AABBox reset()
Resets this box to the inverse low/high, allowing the next resize(float, float, float) command to hit...
Definition: AABBox.java:123
final boolean hasZero2DArea()
Return true if get2DArea() is FloatUtil#isZero(float), considering epsilon.
Definition: AABBox.java:907
final boolean intersects(final AABBox o)
Returns whether this AABBox intersects (partially contains) given AABBox.
Definition: AABBox.java:426
final AABBox setSize(final float[] low, final float[] high)
Set size of the AABBox specifying the coordinates of the low and high.
Definition: AABBox.java:173
final float getSize()
Get the size of this AABBox where the size is represented by the length of the vector between low and...
Definition: AABBox.java:732
final float getVolume()
Returns the volume, i.e.
Definition: AABBox.java:892
AABBox(final Vec3f low, final Vec3f high)
Create a AABBox defining the low and high.
Definition: AABBox.java:112
final boolean intersectsRay(final Ray ray)
Check if Ray intersects this bounding box.
Definition: AABBox.java:495
final AABBox setSize(final Vec3f low, final Vec3f high)
Set size of the AABBox specifying the coordinates of the low and high.
Definition: AABBox.java:205
AABBox transform(final Matrix4f mat, final AABBox out)
Transform this box using the given Matrix4f into out @endiliteral.
Definition: AABBox.java:933
final AABBox scale(final float s)
Scale this AABBox by a constant around fixed center.
Definition: AABBox.java:750
final AABBox copy(final AABBox src)
Copy given AABBox 'src' values to this AABBox.
Definition: AABBox.java:158
final AABBox resize(final AABBox newBox)
Resize the AABBox to encapsulate another AABox.
Definition: AABBox.java:274
final boolean hasZeroVolume()
Return true if getVolume() is FloatUtil#isZero(float), considering epsilon.
Definition: AABBox.java:897
final AABBox translate(final Vec3f t)
Translate this AABBox by a float[3] vector.
Definition: AABBox.java:836
final AABBox resize(final Vec3f xyz)
Resize the AABBox to encapsulate the passed xyz-coordinates.
Definition: AABBox.java:399
final AABBox resize(final float[] xyz, final int offset)
Resize the AABBox to encapsulate the passed xyz-coordinates.
Definition: AABBox.java:379
final Vec3f getRayIntersection(final Vec3f result, final Ray ray, final float epsilon, final boolean assumeIntersection)
Return intersection of a Ray with this bounding box, or null if none exist.
Definition: AABBox.java:563
final AABBox rotate(final Quaternion quat)
Rotate this AABBox by a float[3] vector.
Definition: AABBox.java:848
final AABBox resizeWidth(final float deltaLeft, final float deltaRight)
Resize width of this AABBox with explicit left- and right delta values.
Definition: AABBox.java:218
final boolean intersects2DRegion(final float x, final float y, final float w, final float h)
Check if there is a common region between this AABBox and the passed 2D region irrespective of z rang...
Definition: AABBox.java:462
final Vec3f getCenter()
Returns computed center of this AABBox of getLow() and getHigh().
Definition: AABBox.java:737
final float getDepth()
Definition: AABBox.java:887
final AABBox transform(final AABBox src, final AABBox dst)