|
18 | 18 |
|
19 | 19 | #pragma once |
20 | 20 | #include <array> |
| 21 | +#include <cassert> |
21 | 22 | #include <cmath> |
22 | 23 | #include <stdexcept> |
23 | 24 | #include <type_traits> |
|
26 | 27 | using nullptr_t = std::nullptr_t; |
27 | 28 |
|
28 | 29 | struct Vector3 { |
| 30 | + static constexpr float kDivisionEpsilon = 1.0e-6f; |
| 31 | + |
29 | 32 | std::array<float, 3> components{ 0.0f, 0.0f, 0.0f }; |
30 | 33 | float& x; |
31 | 34 | float& y; |
32 | 35 | float& z; |
33 | 36 |
|
| 37 | + /* |
| 38 | + ============= |
| 39 | + SafeDivisor |
| 40 | +
|
| 41 | + Clamps divisors away from zero while asserting in debug builds. |
| 42 | + ============= |
| 43 | + */ |
| 44 | + [[nodiscard]] static inline float SafeDivisor(const float divisor) { |
| 45 | + const bool near_zero = divisor > -kDivisionEpsilon && divisor < kDivisionEpsilon; |
| 46 | + |
| 47 | + #ifndef NDEBUG |
| 48 | + assert(!near_zero && "Vector3 division by zero or near-zero divisor"); |
| 49 | + #endif |
| 50 | + |
| 51 | + if (near_zero) |
| 52 | + return divisor >= 0.0f ? kDivisionEpsilon : -kDivisionEpsilon; |
| 53 | + |
| 54 | + return divisor; |
| 55 | + } |
| 56 | + |
34 | 57 | /* |
35 | 58 | ============= |
36 | 59 | Vector3 |
@@ -159,12 +182,27 @@ struct Vector3 { |
159 | 182 | [[nodiscard]] constexpr Vector3 operator+(const Vector3& v) const { |
160 | 183 | return { x + v.x, y + v.y, z + v.z }; |
161 | 184 | } |
162 | | - [[nodiscard]] constexpr Vector3 operator/(const Vector3& v) const { |
163 | | - return { x / v.x, y / v.y, z / v.z }; |
| 185 | + /* |
| 186 | + ============= |
| 187 | + operator/ |
| 188 | +
|
| 189 | + Divides component-wise by another vector using guarded divisors. |
| 190 | + ============= |
| 191 | + */ |
| 192 | + [[nodiscard]] inline Vector3 operator/(const Vector3& v) const { |
| 193 | + return { x / SafeDivisor(v.x), y / SafeDivisor(v.y), z / SafeDivisor(v.z) }; |
164 | 194 | } |
165 | 195 | template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>> |
166 | | - [[nodiscard]] constexpr Vector3 operator/(const T& v) const { |
167 | | - return { static_cast<float>(x / v), static_cast<float>(y / v), static_cast<float>(z / v) }; |
| 196 | + /* |
| 197 | + ============= |
| 198 | + operator/ |
| 199 | +
|
| 200 | + Divides each component by a scalar using a guarded divisor. |
| 201 | + ============= |
| 202 | + */ |
| 203 | + [[nodiscard]] inline Vector3 operator/(const T& v) const { |
| 204 | + const float divisor = SafeDivisor(static_cast<float>(v)); |
| 205 | + return { static_cast<float>(x / divisor), static_cast<float>(y / divisor), static_cast<float>(z / divisor) }; |
168 | 206 | } |
169 | 207 | template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>> |
170 | 208 | [[nodiscard]] constexpr Vector3 operator*(const T& v) const { |
|
0 commit comments