2424 */
2525package de .bluecolored .bluemap .core .util ;
2626
27- import static com .google .common .base .Preconditions .checkArgument ;
2827import static com .google .common .base .Preconditions .checkNotNull ;
2928
30- import java .util .Optional ;
31-
29+ import com .flowpowered .math .GenericMath ;
3230import com .flowpowered .math .vector .Vector3d ;
3331import com .flowpowered .math .vector .Vector3i ;
3432
3533/**
3634 * An axis aligned bounding box. That is, an un-rotated cuboid.
3735 * It is represented by its minimum and maximum corners.
38- *
39- * <p>The box will never be degenerate: the corners are always not equal and
40- * respect the minimum and maximum properties.</p>
36+ *
37+ * Using integers, the box has a minimum size of 1 in each direction.
4138 *
4239 * <p>This class is immutable, all objects returned are either new instances or
4340 * itself.</p>
4441 */
4542public class AABB {
4643
47- private final Vector3d min ;
48- private final Vector3d max ;
49- private Vector3d size = null ;
44+ private final Vector3i min ;
45+ private final Vector3i max ;
46+ private Vector3i size = null ;
5047 private Vector3d center = null ;
51-
52- /**
53- * Constructs a new bounding box from two opposite corners.
54- * Fails the resulting box would be degenerate (a dimension is 0).
55- *
56- * @param firstCorner The first corner
57- * @param secondCorner The second corner
58- */
59- public AABB (Vector3i firstCorner , Vector3i secondCorner ) {
60- this (checkNotNull (firstCorner , "firstCorner" ).toDouble (), checkNotNull (secondCorner , "secondCorner" ).toDouble ());
61- }
62-
48+
6349 /**
6450 * Constructs a new bounding box from two opposite corners.
6551 * Fails the resulting box would be degenerate (a dimension is 0).
@@ -71,8 +57,8 @@ public AABB(Vector3i firstCorner, Vector3i secondCorner) {
7157 * @param y2 The second corner y coordinate
7258 * @param z2 The second corner z coordinate
7359 */
74- public AABB (double x1 , double y1 , double z1 , double x2 , double y2 , double z2 ) {
75- this (new Vector3d (x1 , y1 , z1 ), new Vector3d (x2 , y2 , z2 ));
60+ public AABB (int x1 , int y1 , int z1 , int x2 , int y2 , int z2 ) {
61+ this (new Vector3i (x1 , y1 , z1 ), new Vector3i (x2 , y2 , z2 ));
7662 }
7763
7864 /**
@@ -82,22 +68,19 @@ public AABB(double x1, double y1, double z1, double x2, double y2, double z2) {
8268 * @param firstCorner The first corner
8369 * @param secondCorner The second corner
8470 */
85- public AABB (Vector3d firstCorner , Vector3d secondCorner ) {
71+ public AABB (Vector3i firstCorner , Vector3i secondCorner ) {
8672 checkNotNull (firstCorner , "firstCorner" );
8773 checkNotNull (secondCorner , "secondCorner" );
8874 this .min = firstCorner .min (secondCorner );
8975 this .max = firstCorner .max (secondCorner );
90- checkArgument (this .min .getX () != this .max .getX (), "The box is degenerate on x" );
91- checkArgument (this .min .getY () != this .max .getY (), "The box is degenerate on y" );
92- checkArgument (this .min .getZ () != this .max .getZ (), "The box is degenerate on z" );
9376 }
9477
9578 /**
9679 * The minimum corner of the box.
9780 *
9881 * @return The minimum corner
9982 */
100- public Vector3d getMin () {
83+ public Vector3i getMin () {
10184 return this .min ;
10285 }
10386
@@ -106,7 +89,7 @@ public Vector3d getMin() {
10689 *
10790 * @return The maximum corner
10891 */
109- public Vector3d getMax () {
92+ public Vector3i getMax () {
11093 return this .max ;
11194 }
11295
@@ -117,7 +100,7 @@ public Vector3d getMax() {
117100 */
118101 public Vector3d getCenter () {
119102 if (this .center == null ) {
120- this .center = this .min .add (getSize ().div (2 ));
103+ this .center = this .min .toDouble (). add (getSize (). toDouble ().div (2 ));
121104 }
122105 return this .center ;
123106 }
@@ -127,9 +110,9 @@ public Vector3d getCenter() {
127110 *
128111 * @return The size
129112 */
130- public Vector3d getSize () {
113+ public Vector3i getSize () {
131114 if (this .size == null ) {
132- this .size = this .max .sub (this .min );
115+ this .size = this .max .sub (this .min ). add ( Vector3i . ONE ) ;
133116 }
134117 return this .size ;
135118 }
@@ -165,6 +148,18 @@ public boolean contains(Vector3d point) {
165148 * @return Whether or not the box contains the point
166149 */
167150 public boolean contains (double x , double y , double z ) {
151+ return contains (GenericMath .floor (x ), GenericMath .floor (y ), GenericMath .floor (z ));
152+ }
153+
154+ /**
155+ * Checks if the bounding box contains a point.
156+ *
157+ * @param x The x coordinate of the point
158+ * @param y The y coordinate of the point
159+ * @param z The z coordinate of the point
160+ * @return Whether or not the box contains the point
161+ */
162+ public boolean contains (int x , int y , int z ) {
168163 return this .min .getX () <= x && this .max .getX () >= x
169164 && this .min .getY () <= y && this .max .getY () >= y
170165 && this .min .getZ () <= z && this .max .getZ () >= z ;
@@ -183,156 +178,6 @@ public boolean intersects(AABB other) {
183178 && this .max .getZ () >= other .getMin ().getZ () && other .getMax ().getZ () >= this .min .getZ ();
184179 }
185180
186- /**
187- * Tests for intersection between the box and a ray defined by a starting
188- * point and a direction.
189- *
190- * @param start The starting point of the ray
191- * @param direction The direction of the ray
192- * @return An intersection point, if any
193- */
194- public Optional <IntersectionPoint > intersects (Vector3d start , Vector3d direction ) {
195- checkNotNull (start , "start" );
196- checkNotNull (direction , "direction" );
197- // Adapted from: https://github.com/flow/react/blob/develop/src/main/java/com/flowpowered/react/collision/RayCaster.java#L156
198- // The box is interpreted as 6 infinite perpendicular places, one for each face (being expanded infinitely)
199- // "t" variables are multipliers: start + direction * t gives the intersection point
200- // Find the intersections on the -x and +x planes, oriented by direction
201- final double txMin ;
202- final double txMax ;
203- final Vector3d xNormal ;
204- if (Math .copySign (1 , direction .getX ()) > 0 ) {
205- txMin = (this .min .getX () - start .getX ()) / direction .getX ();
206- txMax = (this .max .getX () - start .getX ()) / direction .getX ();
207- xNormal = Vector3d .UNIT_X ;
208- } else {
209- txMin = (this .max .getX () - start .getX ()) / direction .getX ();
210- txMax = (this .min .getX () - start .getX ()) / direction .getX ();
211- xNormal = Vector3d .UNIT_X .negate ();
212- }
213- // Find the intersections on the -y and +y planes, oriented by direction
214- final double tyMin ;
215- final double tyMax ;
216- final Vector3d yNormal ;
217- if (Math .copySign (1 , direction .getY ()) > 0 ) {
218- tyMin = (this .min .getY () - start .getY ()) / direction .getY ();
219- tyMax = (this .max .getY () - start .getY ()) / direction .getY ();
220- yNormal = Vector3d .UNIT_Y ;
221- } else {
222- tyMin = (this .max .getY () - start .getY ()) / direction .getY ();
223- tyMax = (this .min .getY () - start .getY ()) / direction .getY ();
224- yNormal = Vector3d .UNIT_Y .negate ();
225- }
226- // The ray should intersect the -x plane before the +y plane and intersect
227- // the -y plane before the +x plane, else it is outside the box
228- if (txMin > tyMax || txMax < tyMin ) {
229- return Optional .empty ();
230- }
231- // Keep track of the intersection normal which also helps with floating point errors
232- Vector3d normalMax ;
233- Vector3d normalMin ;
234- // The ray intersects only the furthest min plane on the box and only the closest
235- // max plane on the box
236- double tMin ;
237- if (tyMin == txMin ) {
238- tMin = tyMin ;
239- normalMin = xNormal .negate ().sub (yNormal );
240- } else if (tyMin > txMin ) {
241- tMin = tyMin ;
242- normalMin = yNormal .negate ();
243- } else {
244- tMin = txMin ;
245- normalMin = xNormal .negate ();
246- }
247- double tMax ;
248- if (tyMax == txMax ) {
249- tMax = tyMax ;
250- normalMax = xNormal .add (yNormal );
251- } else if (tyMax < txMax ) {
252- tMax = tyMax ;
253- normalMax = yNormal ;
254- } else {
255- tMax = txMax ;
256- normalMax = xNormal ;
257- }
258- // Find the intersections on the -z and +z planes, oriented by direction
259- final double tzMin ;
260- final double tzMax ;
261- final Vector3d zNormal ;
262- if (Math .copySign (1 , direction .getZ ()) > 0 ) {
263- tzMin = (this .min .getZ () - start .getZ ()) / direction .getZ ();
264- tzMax = (this .max .getZ () - start .getZ ()) / direction .getZ ();
265- zNormal = Vector3d .UNIT_Z ;
266- } else {
267- tzMin = (this .max .getZ () - start .getZ ()) / direction .getZ ();
268- tzMax = (this .min .getZ () - start .getZ ()) / direction .getZ ();
269- zNormal = Vector3d .UNIT_Z .negate ();
270- }
271- // The ray intersects only the furthest min plane on the box and only the closest
272- // max plane on the box
273- if (tMin > tzMax || tMax < tzMin ) {
274- return Optional .empty ();
275- }
276- // The ray should intersect the closest plane outside first and the furthest
277- // plane outside last
278- if (tzMin == tMin ) {
279- normalMin = normalMin .sub (zNormal );
280- } else if (tzMin > tMin ) {
281- tMin = tzMin ;
282- normalMin = zNormal .negate ();
283- }
284- if (tzMax == tMax ) {
285- normalMax = normalMax .add (zNormal );
286- } else if (tzMax < tMax ) {
287- tMax = tzMax ;
288- normalMax = zNormal ;
289- }
290- // Both intersection points are behind the start, there are no intersections
291- if (tMax < 0 ) {
292- return Optional .empty ();
293- }
294- // Find the final intersection multiplier and normal
295- final double t ;
296- Vector3d normal ;
297- if (tMin < 0 ) {
298- // Only the furthest intersection is after the start, so use it
299- t = tMax ;
300- normal = normalMax ;
301- } else {
302- // Both are after the start, use the closest one
303- t = tMin ;
304- normal = normalMin ;
305- }
306- normal = normal .normalize ();
307- // To avoid rounding point errors leaving the intersection point just off the plane
308- // we check the normal to use the actual plane value from the box coordinates
309- final double x ;
310- final double y ;
311- final double z ;
312- if (normal .getX () > 0 ) {
313- x = this .max .getX ();
314- } else if (normal .getX () < 0 ) {
315- x = this .min .getX ();
316- } else {
317- x = direction .getX () * t + start .getX ();
318- }
319- if (normal .getY () > 0 ) {
320- y = this .max .getY ();
321- } else if (normal .getY () < 0 ) {
322- y = this .min .getY ();
323- } else {
324- y = direction .getY () * t + start .getY ();
325- }
326- if (normal .getZ () > 0 ) {
327- z = this .max .getZ ();
328- } else if (normal .getZ () < 0 ) {
329- z = this .min .getZ ();
330- } else {
331- z = direction .getZ () * t + start .getZ ();
332- }
333- return Optional .of (new IntersectionPoint (new Vector3d (x , y , z ), normal ));
334- }
335-
336181 /**
337182 * Offsets this bounding box by a given amount and returns a new box.
338183 *
@@ -344,17 +189,6 @@ public AABB offset(Vector3i offset) {
344189 return offset (offset .getX (), offset .getY (), offset .getZ ());
345190 }
346191
347- /**
348- * Offsets this bounding box by a given amount and returns a new box.
349- *
350- * @param offset The offset to apply
351- * @return The new offset box
352- */
353- public AABB offset (Vector3d offset ) {
354- checkNotNull (offset , "offset" );
355- return offset (offset .getX (), offset .getY (), offset .getZ ());
356- }
357-
358192 /**
359193 * Offsets this bounding box by a given amount and returns a new box.
360194 *
@@ -363,53 +197,10 @@ public AABB offset(Vector3d offset) {
363197 * @param z The amount of offset for the z coordinate
364198 * @return The new offset box
365199 */
366- public AABB offset (double x , double y , double z ) {
200+ public AABB offset (int x , int y , int z ) {
367201 return new AABB (this .min .add (x , y , z ), this .max .add (x , y , z ));
368202 }
369-
370- /**
371- * Expands this bounding box by a given amount in both directions and
372- * returns a new box. The expansion is applied half and half to the
373- * minimum and maximum corners.
374- *
375- * @param amount The amount of expansion to apply
376- * @return The new expanded box
377- */
378- public AABB expand (Vector3i amount ) {
379- checkNotNull (amount , "amount" );
380- return expand (amount .getX (), amount .getY (), amount .getZ ());
381- }
382-
383- /**
384- * Expands this bounding box by a given amount in both directions and
385- * returns a new box. The expansion is applied half and half to the
386- * minimum and maximum corners.
387- *
388- * @param amount The amount of expansion to apply
389- * @return The new expanded box
390- */
391- public AABB expand (Vector3d amount ) {
392- checkNotNull (amount , "amount" );
393- return expand (amount .getX (), amount .getY (), amount .getZ ());
394- }
395-
396- /**
397- * Expands this bounding box by a given amount in both directions and
398- * returns a new box. The expansion is applied half and half to the
399- * minimum and maximum corners.
400- *
401- * @param x The amount of expansion for the x coordinate
402- * @param y The amount of expansion for the y coordinate
403- * @param z The amount of expansion for the z coordinate
404- * @return The new expanded box
405- */
406- public AABB expand (double x , double y , double z ) {
407- x /= 2 ;
408- y /= 2 ;
409- z /= 2 ;
410- return new AABB (this .min .sub (x , y , z ), this .max .add (x , y , z ));
411- }
412-
203+
413204 @ Override
414205 public boolean equals (Object other ) {
415206 if (this == other ) {
0 commit comments