Skip to content

Commit af6b8ac

Browse files
committed
Add Bounds class for 3D axis-aligned bounding box (AABB) calculations
- Introduced the `Bounds` class to represent an axis-aligned bounding box, defined by minimum and maximum corners. - Implemented methods for checking point containment, expanding the box, encapsulating points, and calculating distances. - Added intersection methods for detecting ray and box intersections, as well as overlap between two bounding boxes. - Provided utility methods for getting and setting the minimum and maximum corners of the bounding box. This class will be useful for collision detection, frustum culling, and other spatial queries in 3D environments.
1 parent 63346a4 commit af6b8ac

File tree

1 file changed

+189
-0
lines changed

1 file changed

+189
-0
lines changed

src/main/java/math/Bounds.java

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package math;
2+
3+
/**
4+
* The {@code Bounds} class represents a 3D axis-aligned bounding box (AABB),
5+
* defined by two {@link Vector3f} points: the minimum and maximum corners. This
6+
* class provides various utility methods for manipulating and querying the
7+
* bounding box, such as checking if a point is contained within the bounds,
8+
* expanding the bounds, and testing for intersections with rays or other
9+
* bounds.
10+
*
11+
* <p>
12+
* A bounding box is often used in 3D graphics for collision detection, frustum
13+
* culling, and other spatial queries.
14+
* </p>
15+
*/
16+
public class Bounds {
17+
18+
/**
19+
* The minimum corner of the bounding box.
20+
*/
21+
private Vector3f min;
22+
23+
/**
24+
* The maximum corner of the bounding box.
25+
*/
26+
private Vector3f max;
27+
28+
/**
29+
* Constructs a new {@code Bounds} object with the specified minimum and
30+
* maximum corners.
31+
*
32+
* @param min the minimum corner of the bounding box
33+
* @param max the maximum corner of the bounding box
34+
* @throws IllegalArgumentException if either {@code min} or {@code max} is
35+
* {@code null}
36+
*/
37+
public Bounds(Vector3f min, Vector3f max) {
38+
if (min == null) {
39+
throw new IllegalArgumentException("Min cannot be null.");
40+
}
41+
if (max == null) {
42+
throw new IllegalArgumentException("Max cannot be null.");
43+
}
44+
this.min = new Vector3f(min);
45+
this.max = new Vector3f(max);
46+
}
47+
48+
/**
49+
* Returns the closest point on the bounding box to the given {@code point}.
50+
* The closest point is determined by clamping each coordinate of the point
51+
* between the minimum and maximum bounds of the box.
52+
*
53+
* @param point the point to clamp to the bounding box
54+
* @return a new {@code Vector3f} representing the closest point on the
55+
* bounding box
56+
*/
57+
public Vector3f closestPoint(Vector3f point) {
58+
float x = Math.max(min.x, Math.min(max.x, point.x));
59+
float y = Math.max(min.y, Math.min(max.y, point.y));
60+
float z = Math.max(min.z, Math.min(max.z, point.z));
61+
return new Vector3f(x, y, z);
62+
}
63+
64+
/**
65+
* Checks if the given {@code point} is inside the bounding box. The point is
66+
* considered inside if all of its coordinates are between the minimum and
67+
* maximum coordinates of the box.
68+
*
69+
* @param point the point to check
70+
* @return {@code true} if the point is inside the bounding box, {@code false}
71+
* otherwise
72+
*/
73+
public boolean contains(Vector3f point) {
74+
return point.x >= min.x && point.x <= max.x && point.y >= min.y
75+
&& point.y <= max.y && point.z >= min.z && point.z <= max.z;
76+
}
77+
78+
/**
79+
* Expands the bounding box to encompass the given {@code point}. If the point
80+
* is outside the current bounds, the box will be enlarged to include it.
81+
*
82+
* @param point the point to include in the bounding box
83+
*/
84+
public void encapsulate(Vector3f point) {
85+
min = new Vector3f(Math.min(min.x, point.x), Math.min(min.y, point.y),
86+
Math.min(min.z, point.z));
87+
max = new Vector3f(Math.max(max.x, point.x), Math.max(max.y, point.y),
88+
Math.max(max.z, point.z));
89+
}
90+
91+
/**
92+
* Expands the bounding box by the given {@code amount}. The expansion is done
93+
* uniformly along all axes, increasing the size of the bounding box by the
94+
* specified amount.
95+
*
96+
* @param amount the amount to expand the bounding box by
97+
*/
98+
public void expand(float amount) {
99+
Vector3f expansion = new Vector3f(amount / 2, amount / 2, amount / 2);
100+
min = min.subtract(expansion);
101+
max = max.add(expansion);
102+
}
103+
104+
/**
105+
* Tests if the given {@code ray} intersects the bounding box. The
106+
* intersection is checked using the slab method, which determines if the ray
107+
* intersects the box along each axis.
108+
*
109+
* @param ray the ray to test for intersection
110+
* @return {@code true} if the ray intersects the bounding box, {@code false}
111+
* otherwise
112+
*/
113+
public boolean intersectRay(Ray3f ray) {
114+
Vector3f invDir = ray.getDirection().reciprocal();
115+
Vector3f tMin = min.subtract(ray.getOrigin()).mult(invDir);
116+
Vector3f tMax = max.subtract(ray.getOrigin()).mult(invDir);
117+
118+
float t1 = Math.min(tMin.x, tMax.x);
119+
float t2 = Math.max(tMin.x, tMax.x);
120+
121+
for (int i = 1; i < 3; i++) { // For y and z axes
122+
t1 = Math.max(t1, Math.min(tMin.get(i), tMax.get(i)));
123+
t2 = Math.min(t2, Math.max(tMin.get(i), tMax.get(i)));
124+
}
125+
126+
return t1 <= t2 && t2 >= 0;
127+
}
128+
129+
/**
130+
* Tests if this bounding box intersects another {@code Bounds}. The
131+
* intersection is checked by comparing the min and max coordinates of both
132+
* boxes.
133+
*
134+
* @param other the other bounding box to check for intersection
135+
* @return {@code true} if the bounding boxes intersect, {@code false}
136+
* otherwise
137+
*/
138+
public boolean intersects(Bounds other) {
139+
return min.x <= other.max.x && max.x >= other.min.x && min.y <= other.max.y
140+
&& max.y >= other.min.y && min.z <= other.max.z && max.z >= other.min.z;
141+
}
142+
143+
/**
144+
* Calculates the squared distance from the given {@code point} to the closest
145+
* point on the bounding box. This method avoids calculating the square root
146+
* for performance reasons, returning the squared distance instead.
147+
*
148+
* @param point the point to calculate the squared distance from
149+
* @return the squared distance from the point to the closest point on the
150+
* bounding box
151+
*/
152+
public float sqrDistance(Vector3f point) {
153+
float dx = Math.max(0, Math.max(min.x - point.x, point.x - max.x));
154+
float dy = Math.max(0, Math.max(min.y - point.y, point.y - max.y));
155+
float dz = Math.max(0, Math.max(min.z - point.z, point.z - max.z));
156+
return dx * dx + dy * dy + dz * dz;
157+
}
158+
159+
/**
160+
* Sets the minimum and maximum corners of the bounding box to the specified
161+
* values.
162+
*
163+
* @param min the new minimum corner
164+
* @param max the new maximum corner
165+
*/
166+
public void setMinMax(Vector3f min, Vector3f max) {
167+
this.min = new Vector3f(min);
168+
this.max = new Vector3f(max);
169+
}
170+
171+
/**
172+
* Returns the minimum corner of the bounding box.
173+
*
174+
* @return the minimum corner of the bounding box
175+
*/
176+
public Vector3f getMin() {
177+
return min;
178+
}
179+
180+
/**
181+
* Returns the maximum corner of the bounding box.
182+
*
183+
* @return the maximum corner of the bounding box
184+
*/
185+
public Vector3f getMax() {
186+
return max;
187+
}
188+
189+
}

0 commit comments

Comments
 (0)