Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
Direction3.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <optional>
5#include <type_traits>
6
7#include <mv/math/Angles.hpp>
8#include <mv/math/detail/Normalization.hpp>
9
10namespace mv::math
11{
12 template <typename T>
13 requires std::is_floating_point_v<T>
14 class Rotation3;
15
16 template <typename T>
17 requires std::is_floating_point_v<T>
18 class Normal3;
19
20 template <typename T>
21 requires std::is_floating_point_v<T>
22 class Direction3;
23
24 template <typename T>
25 requires std::is_floating_point_v<T>
26 [[nodiscard]] Direction3<T> Rotate(const Rotation3<T>&,
27 const Direction3<T>&) noexcept;
28
29 template <typename T>
30 requires std::is_floating_point_v<T>
32 {
33 public:
34 using Component = T;
35
36 Direction3() noexcept : Value_(T(1), T(0), T(0))
37 {
38 }
39
40 [[nodiscard]] static std::optional<Direction3> TryFrom(
41 const Vec3<T>& value) noexcept
42 {
43 const auto normalized = detail::TryNormalizeVector(value);
44 if (!normalized)
45 {
46 return std::nullopt;
47 }
48 return Direction3(*normalized, UncheckedTag{});
49 }
50
52 Direction3 fallback) noexcept
53 {
54 const auto result = TryFrom(value);
55 return result ? *result : fallback;
56 }
57
59 {
60 return Direction3(Vec3<T>(T(1), T(0), T(0)), UncheckedTag{});
61 }
62
64 {
65 return Direction3(Vec3<T>(T(0), T(1), T(0)), UncheckedTag{});
66 }
67
69 {
70 return Direction3(Vec3<T>(T(0), T(0), T(1)), UncheckedTag{});
71 }
72
74 {
75 return AxisZ();
76 }
77
79 {
80 return Value_;
81 }
82
84 {
85 return Direction3(Value_ * T(-1), UncheckedTag{});
86 }
87
88 [[nodiscard]] friend bool operator==(const Direction3& left,
89 const Direction3& right) noexcept
90 {
91 return left.Value_ == right.Value_;
92 }
93
94 private:
95 struct UncheckedTag
96 {
97 };
98
99 explicit Direction3(const Vec3<T>& value, UncheckedTag) noexcept :
100 Value_(value)
101 {
102 }
103
104 Vec3<T> Value_;
105
106 friend class Normal3<T>;
107 friend class Rotation3<T>;
109 const Direction3<T>&) noexcept;
110 };
111
114
115 template <typename T>
116 requires std::is_floating_point_v<T>
117 [[nodiscard]] inline std::optional<Direction3<T>> TryNormalize(
118 const Vec3<T>& value) noexcept
119 {
121 }
122
123 template <typename T>
124 requires std::is_floating_point_v<T>
126 const Vec3<T>& value, const Direction3<T>& direction) noexcept
127 {
128 // Unit-direction projection follows Ericson, Real-Time Collision
129 // Detection (2005), section 5.1.2.
130 return direction.Vector() * Dot(value, direction.Vector());
131 }
132
133 template <typename T>
134 requires std::is_floating_point_v<T>
136 const Direction3<T>& from, const Direction3<T>& to) noexcept
137 {
138 // atan2(|cross|, dot) follows Godot 4.5-stable Vector3::angle_to
139 // (MIT, commit 876b290332ec6f2e6d173d08162a02aa7e6ca46d).
140 return Radians<T>(std::atan2(Length(Cross(from.Vector(), to.Vector())),
141 Dot(from.Vector(), to.Vector())));
142 }
143
144 template <typename T>
145 requires std::is_floating_point_v<T>
147 const Direction3<T>& from,
148 const Direction3<T>& to,
149 const Direction3<T>& positiveAxis) noexcept
150 {
151 // Sign selection follows Godot 4.5-stable
152 // Vector3::signed_angle_to (MIT, commit
153 // 876b290332ec6f2e6d173d08162a02aa7e6ca46d).
154 const Vec3<T> cross = Cross(from.Vector(), to.Vector());
155 const T unsignedAngle =
156 std::atan2(Length(cross), Dot(from.Vector(), to.Vector()));
157 return Radians<T>(Dot(cross, positiveAxis.Vector()) < T(0)
159 : unsignedAngle);
160 }
161} // namespace mv::math
162
163static_assert(sizeof(mv::math::Direction3f) == sizeof(mv::math::Vec3f));
164static_assert(alignof(mv::math::Direction3f) == alignof(mv::math::Vec3f));
165static_assert(std::is_trivially_copyable_v<mv::math::Direction3f>);
166static_assert(std::is_standard_layout_v<mv::math::Direction3f>);
167static_assert(sizeof(mv::math::Direction3d) == sizeof(mv::math::Vec3d));
168static_assert(alignof(mv::math::Direction3d) == alignof(mv::math::Vec3d));
169static_assert(std::is_trivially_copyable_v<mv::math::Direction3d>);
170static_assert(std::is_standard_layout_v<mv::math::Direction3d>);
static Direction3 AxisY() noexcept
static std::optional< Direction3 > TryFrom(const Vec3< T > &value) noexcept
friend bool operator==(const Direction3 &left, const Direction3 &right) noexcept
static Direction3 Forward() noexcept
const Vec3< T > & Vector() const noexcept
static Direction3 FromOr(const Vec3< T > &value, Direction3 fallback) noexcept
static Direction3 AxisZ() noexcept
Direction3 operator-() const noexcept
static Direction3 AxisX() noexcept
constexpr T Dot(const Quat< T > &left, const Quat< T > &right) noexcept
Definition Quat.hpp:102
Vec3< T > Cross(const Vec3< T > &left, const Vec3< T > &right) noexcept
Definition Vec3.hpp:367
Radians< T > AngleBetween(const Direction3< T > &from, const Direction3< T > &to) noexcept
Direction3< double > Direction3d
Vec3< T > ProjectOntoDirection(const Vec3< T > &value, const Direction3< T > &direction) noexcept
Direction3< float > Direction3f
T Length(const Quat< T > &value) noexcept
Definition Quat.hpp:116
std::optional< Quat< T > > TryNormalize(const Quat< T > &value) noexcept
Definition Quat.hpp:122
Radians< T > SignedAngle(const Direction3< T > &from, const Direction3< T > &to, const Direction3< T > &positiveAxis) noexcept
Vec3< T > Rotate(const Rotation3< T > &rotation, const Vec3< T > &vector) noexcept