Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
Aabb3.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <cstdint>
7#include <limits>
8#include <optional>
9#include <span>
10#include <type_traits>
11
13
14namespace mv::math
15{
16 // Move-owned AABB contract; operations cross-checked against DirectXMath
17 // DirectXCollision.inl (MIT).
18 template <typename T>
19 requires std::is_arithmetic_v<T>
20 class Aabb3
21 {
22 public:
23 using Component = T;
24
26 Minimum_(EmptyMinimum(), EmptyMinimum(), EmptyMinimum()),
27 Maximum_(EmptyMaximum(), EmptyMaximum(), EmptyMaximum())
28 {
29 }
30
32 {
33 return Aabb3();
34 }
35
36 [[nodiscard]] static std::optional<Aabb3> TryFromMinMax(
37 const Point3<T>& minimum, const Point3<T>& maximum) noexcept
38 {
39 if (!IsFinitePoint(minimum) || !IsFinitePoint(maximum) ||
40 minimum.X() > maximum.X() || minimum.Y() > maximum.Y() ||
41 minimum.Z() > maximum.Z())
42 {
43 return std::nullopt;
44 }
45 return Aabb3(minimum, maximum, UncheckedTag{});
46 }
47
48 [[nodiscard]] static std::optional<Aabb3> TryFromCenterHalfExtents(
49 const Point3<T>& center, const Vec3<T>& halfExtents) noexcept
50 requires std::is_floating_point_v<T>
51 {
52 if (!IsFinitePoint(center) || !IsFiniteVector(halfExtents) ||
53 halfExtents.X() < T(0) || halfExtents.Y() < T(0) ||
54 halfExtents.Z() < T(0))
55 {
56 return std::nullopt;
57 }
59 }
60
61 [[nodiscard]] static std::optional<Aabb3> TryFromPoints(
62 std::span<const Point3<T>> points) noexcept
63 {
65 for (const Point3<T>& point : points)
66 {
67 const auto expanded = result.TryExpanded(point);
68 if (!expanded)
69 {
70 return std::nullopt;
71 }
73 }
74 return result;
75 }
76
78 {
79 return Minimum_;
80 }
81
83 {
84 return Maximum_;
85 }
86
88 {
89 return Minimum_.X() > Maximum_.X();
90 }
91
93 {
94 return IsEmpty() ||
95 (IsFinitePoint(Minimum_) && IsFinitePoint(Maximum_) &&
96 Minimum_.X() <= Maximum_.X() &&
97 Minimum_.Y() <= Maximum_.Y() &&
98 Minimum_.Z() <= Maximum_.Z());
99 }
100
101 [[nodiscard]] std::optional<Point3<T>> TryCenter() const noexcept
103 {
104 if (IsEmpty())
105 {
106 return std::nullopt;
107 }
108 using Calculation =
109 std::conditional_t<(sizeof(T) < sizeof(double)), double, T>;
110 return Point3<T>(
111 static_cast<T>((static_cast<Calculation>(Minimum_.X()) +
112 static_cast<Calculation>(Maximum_.X())) *
113 Calculation(0.5)),
114 static_cast<T>((static_cast<Calculation>(Minimum_.Y()) +
115 static_cast<Calculation>(Maximum_.Y())) *
116 Calculation(0.5)),
117 static_cast<T>((static_cast<Calculation>(Minimum_.Z()) +
118 static_cast<Calculation>(Maximum_.Z())) *
119 Calculation(0.5)));
120 }
121
122 [[nodiscard]] std::optional<Vec3<T>> TrySize() const noexcept
123 {
124 if (IsEmpty())
125 {
126 return std::nullopt;
127 }
128 const long double x = static_cast<long double>(Maximum_.X()) -
129 static_cast<long double>(Minimum_.X());
130 const long double y = static_cast<long double>(Maximum_.Y()) -
131 static_cast<long double>(Minimum_.Y());
132 const long double z = static_cast<long double>(Maximum_.Z()) -
133 static_cast<long double>(Minimum_.Z());
134 const long double maximum =
135 static_cast<long double>(std::numeric_limits<T>::max());
136 if (x > maximum || y > maximum || z > maximum)
137 {
138 return std::nullopt;
139 }
140 return Vec3<T>(static_cast<T>(x), static_cast<T>(y),
141 static_cast<T>(z));
142 }
143
144 [[nodiscard]] std::optional<Vec3<T>> TryHalfExtents() const noexcept
146 {
147 if (IsEmpty())
148 {
149 return std::nullopt;
150 }
151 using Calculation =
152 std::conditional_t<(sizeof(T) < sizeof(double)), double, T>;
153 return Vec3<T>(
154 static_cast<T>((static_cast<Calculation>(Maximum_.X()) -
155 static_cast<Calculation>(Minimum_.X())) *
156 Calculation(0.5)),
157 static_cast<T>((static_cast<Calculation>(Maximum_.Y()) -
158 static_cast<Calculation>(Minimum_.Y())) *
159 Calculation(0.5)),
160 static_cast<T>((static_cast<Calculation>(Maximum_.Z()) -
161 static_cast<Calculation>(Minimum_.Z())) *
162 Calculation(0.5)));
163 }
164
165 [[nodiscard]] bool Contains(const Point3<T>& point) const noexcept
166 {
167 return !IsEmpty() && point.X() >= Minimum_.X() &&
168 point.Y() >= Minimum_.Y() && point.Z() >= Minimum_.Z() &&
169 point.X() <= Maximum_.X() && point.Y() <= Maximum_.Y() &&
170 point.Z() <= Maximum_.Z();
171 }
172
173 [[nodiscard]] bool Contains(const Aabb3& other) const noexcept
174 {
175 return other.IsEmpty() || (!IsEmpty() && Contains(other.Minimum_) &&
176 Contains(other.Maximum_));
177 }
178
179 [[nodiscard]] bool Intersects(const Aabb3& other) const noexcept
180 {
181 return !IsEmpty() && !other.IsEmpty() &&
182 Maximum_.X() >= other.Minimum_.X() &&
183 Minimum_.X() <= other.Maximum_.X() &&
184 Maximum_.Y() >= other.Minimum_.Y() &&
185 Minimum_.Y() <= other.Maximum_.Y() &&
186 Maximum_.Z() >= other.Minimum_.Z() &&
187 Minimum_.Z() <= other.Maximum_.Z();
188 }
189
190 [[nodiscard]] Aabb3 Merged(const Aabb3& other) const noexcept
191 {
192 if (IsEmpty())
193 {
194 return other;
195 }
196 if (other.IsEmpty())
197 {
198 return *this;
199 }
200 return Aabb3(Point3<T>(std::min(Minimum_.X(), other.Minimum_.X()),
201 std::min(Minimum_.Y(), other.Minimum_.Y()),
202 std::min(Minimum_.Z(), other.Minimum_.Z())),
203 Point3<T>(std::max(Maximum_.X(), other.Maximum_.X()),
204 std::max(Maximum_.Y(), other.Maximum_.Y()),
205 std::max(Maximum_.Z(), other.Maximum_.Z())),
206 UncheckedTag{});
207 }
208
209 [[nodiscard]] Aabb3 Intersection(const Aabb3& other) const noexcept
210 {
211 if (!Intersects(other))
212 {
213 return Empty();
214 }
215 return Aabb3(Point3<T>(std::max(Minimum_.X(), other.Minimum_.X()),
216 std::max(Minimum_.Y(), other.Minimum_.Y()),
217 std::max(Minimum_.Z(), other.Minimum_.Z())),
218 Point3<T>(std::min(Maximum_.X(), other.Maximum_.X()),
219 std::min(Maximum_.Y(), other.Maximum_.Y()),
220 std::min(Maximum_.Z(), other.Maximum_.Z())),
221 UncheckedTag{});
222 }
223
224 [[nodiscard]] std::optional<Aabb3> TryExpanded(
225 const Point3<T>& point) const noexcept
226 {
227 if (!IsFinitePoint(point))
228 {
229 return std::nullopt;
230 }
231 if (IsEmpty())
232 {
233 return Aabb3(point, point, UncheckedTag{});
234 }
235 return Aabb3(Point3<T>(std::min(Minimum_.X(), point.X()),
236 std::min(Minimum_.Y(), point.Y()),
237 std::min(Minimum_.Z(), point.Z())),
238 Point3<T>(std::max(Maximum_.X(), point.X()),
239 std::max(Maximum_.Y(), point.Y()),
240 std::max(Maximum_.Z(), point.Z())),
241 UncheckedTag{});
242 }
243
244 [[nodiscard]] std::optional<Point3<T>> TryClosestPoint(
245 const Point3<T>& point) const noexcept
246 {
247 if (IsEmpty() || !IsFinitePoint(point))
248 {
249 return std::nullopt;
250 }
251 return Point3<T>(std::clamp(point.X(), Minimum_.X(), Maximum_.X()),
252 std::clamp(point.Y(), Minimum_.Y(), Maximum_.Y()),
253 std::clamp(point.Z(), Minimum_.Z(), Maximum_.Z()));
254 }
255
258 {
259 if (IsEmpty())
260 {
261 return T(0);
262 }
263 const double x = static_cast<double>(Maximum_.X()) -
264 static_cast<double>(Minimum_.X());
265 const double y = static_cast<double>(Maximum_.Y()) -
266 static_cast<double>(Minimum_.Y());
267 const double z = static_cast<double>(Maximum_.Z()) -
268 static_cast<double>(Minimum_.Z());
269 return static_cast<T>(x * y * z);
270 }
271
274 {
275 if (IsEmpty())
276 {
277 return T(0);
278 }
279 const double x = static_cast<double>(Maximum_.X()) -
280 static_cast<double>(Minimum_.X());
281 const double y = static_cast<double>(Maximum_.Y()) -
282 static_cast<double>(Minimum_.Y());
283 const double z = static_cast<double>(Maximum_.Z()) -
284 static_cast<double>(Minimum_.Z());
285 return static_cast<T>(2.0 * (x * y + y * z + z * x));
286 }
287
288 [[nodiscard]] std::optional<std::array<Point3<T>, 8>> TryCorners()
290 {
291 if (IsEmpty())
292 {
293 return std::nullopt;
294 }
295 return std::array<Point3<T>, 8>{
296 Point3<T>(Minimum_.X(), Minimum_.Y(), Minimum_.Z()),
297 Point3<T>(Maximum_.X(), Minimum_.Y(), Minimum_.Z()),
298 Point3<T>(Minimum_.X(), Maximum_.Y(), Minimum_.Z()),
299 Point3<T>(Maximum_.X(), Maximum_.Y(), Minimum_.Z()),
300 Point3<T>(Minimum_.X(), Minimum_.Y(), Maximum_.Z()),
301 Point3<T>(Maximum_.X(), Minimum_.Y(), Maximum_.Z()),
302 Point3<T>(Minimum_.X(), Maximum_.Y(), Maximum_.Z()),
303 Point3<T>(Maximum_.X(), Maximum_.Y(), Maximum_.Z())};
304 }
305
306 [[nodiscard]] friend bool operator==(const Aabb3& left,
307 const Aabb3& right) noexcept
308 {
309 return left.Minimum_ == right.Minimum_ &&
310 left.Maximum_ == right.Maximum_;
311 }
312
313 private:
314 struct UncheckedTag
315 {
316 };
317
318 Aabb3(const Point3<T>& minimum,
319 const Point3<T>& maximum,
320 UncheckedTag) noexcept :
321 Minimum_(minimum), Maximum_(maximum)
322 {
323 }
324
325 [[nodiscard]] static constexpr T EmptyMinimum() noexcept
326 {
327 if constexpr (std::is_floating_point_v<T>)
328 {
329 return std::numeric_limits<T>::infinity();
330 }
331 else
332 {
333 return std::numeric_limits<T>::max();
334 }
335 }
336
337 [[nodiscard]] static constexpr T EmptyMaximum() noexcept
338 {
339 if constexpr (std::is_floating_point_v<T>)
340 {
341 return -std::numeric_limits<T>::infinity();
342 }
343 else
344 {
345 return std::numeric_limits<T>::lowest();
346 }
347 }
348
349 [[nodiscard]] static bool IsFinitePoint(const Point3<T>& point) noexcept
350 {
351 if constexpr (std::is_floating_point_v<T>)
352 {
353 return std::isfinite(point.X()) && std::isfinite(point.Y()) &&
354 std::isfinite(point.Z());
355 }
356 return true;
357 }
358
359 [[nodiscard]] static bool IsFiniteVector(const Vec3<T>& vector) noexcept
360 {
361 if constexpr (std::is_floating_point_v<T>)
362 {
363 return std::isfinite(vector.X()) && std::isfinite(vector.Y()) &&
364 std::isfinite(vector.Z());
365 }
366 return true;
367 }
368
369 Point3<T> Minimum_;
370 Point3<T> Maximum_;
371 };
372
385} // namespace mv::math
386
387static_assert(sizeof(mv::math::Aabb3f) == 32);
388static_assert(alignof(mv::math::Aabb3f) == 16);
389static_assert(std::is_trivially_copyable_v<mv::math::Aabb3f>);
390static_assert(std::is_standard_layout_v<mv::math::Aabb3f>);
391static_assert(sizeof(mv::math::Aabb3d) == 64);
392static_assert(alignof(mv::math::Aabb3d) == 32);
393static_assert(std::is_trivially_copyable_v<mv::math::Aabb3d>);
394static_assert(std::is_standard_layout_v<mv::math::Aabb3d>);
bool IsEmpty() const noexcept
Definition Aabb3.hpp:87
static std::optional< Aabb3 > TryFromPoints(std::span< const Point3< T > > points) noexcept
Definition Aabb3.hpp:61
const Point3< T > & Minimum() const noexcept
Definition Aabb3.hpp:77
static std::optional< Aabb3 > TryFromMinMax(const Point3< T > &minimum, const Point3< T > &maximum) noexcept
Definition Aabb3.hpp:36
bool Contains(const Aabb3 &other) const noexcept
Definition Aabb3.hpp:173
std::optional< std::array< Point3< T >, 8 > > TryCorners() const noexcept
Definition Aabb3.hpp:288
static Aabb3 Empty() noexcept
Definition Aabb3.hpp:31
Aabb3 Merged(const Aabb3 &other) const noexcept
Definition Aabb3.hpp:190
friend bool operator==(const Aabb3 &left, const Aabb3 &right) noexcept
Definition Aabb3.hpp:306
bool IsValid() const noexcept
Definition Aabb3.hpp:92
Aabb3 Intersection(const Aabb3 &other) const noexcept
Definition Aabb3.hpp:209
std::optional< Aabb3 > TryExpanded(const Point3< T > &point) const noexcept
Definition Aabb3.hpp:224
bool Contains(const Point3< T > &point) const noexcept
Definition Aabb3.hpp:165
static std::optional< Aabb3 > TryFromCenterHalfExtents(const Point3< T > &center, const Vec3< T > &halfExtents) noexcept
Definition Aabb3.hpp:48
std::optional< Point3< T > > TryCenter() const noexcept
Definition Aabb3.hpp:101
bool Intersects(const Aabb3 &other) const noexcept
Definition Aabb3.hpp:179
const Point3< T > & Maximum() const noexcept
Definition Aabb3.hpp:82
std::optional< Vec3< T > > TryHalfExtents() const noexcept
Definition Aabb3.hpp:144
Aabb3() noexcept
Definition Aabb3.hpp:25
T Volume() const noexcept
Definition Aabb3.hpp:256
std::optional< Vec3< T > > TrySize() const noexcept
Definition Aabb3.hpp:122
std::optional< Point3< T > > TryClosestPoint(const Point3< T > &point) const noexcept
Definition Aabb3.hpp:244
T SurfaceArea() const noexcept
Definition Aabb3.hpp:272
Aabb3< double > Aabb3d
Definition Aabb3.hpp:374
Aabb3< std::int32_t > Aabb3i32
Definition Aabb3.hpp:379
Aabb3< std::uint32_t > Aabb3u32
Definition Aabb3.hpp:380
Aabb3< float > Aabb3f
Definition Aabb3.hpp:373