JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
MinimalQuat.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPLSpatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright 2024 Jaroslav Pevno, JPLSpatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
22#include "JPLSpatial/Core.h"
23
28
29#include <cmath>
30#include <ostream>
31
32namespace JPL
33{
34 //======================================================================
36 template<CVec3 Vec3>
37 struct Quat
38 {
40 Vec3 V; // v = {x,y,z}
42
44 inline bool operator == (const Quat& rhs) const noexcept { return V == rhs.V && W == rhs.W; }
45 inline bool operator != (const Quat& rhs) const noexcept { return V != rhs.V || W != rhs.W; }
46
47 [[nodiscard]] JPL_INLINE static Quat Identity() noexcept { return Quat({ 0, 0, 0, }, 1); }
48
49 [[nodiscard]] JPL_INLINE Quat Conjugated() const noexcept { return Quat{ -V, W }; }
50
51 [[nodiscard]] JPL_INLINE Quat Inversed() const noexcept
52 {
53 const Float n2Inv = 1.0f / (DotProduct(V, V) + W * W);
54 return Quat{ -V * n2Inv, W * n2Inv };
55 }
56
60 [[nodiscard]] static JPL_INLINE Quat Rotation(const Vec3& axis, Float angleRad) noexcept
61 {
62 const Float halfAngle = Float(0.5) * angleRad;
63 const auto [s, c] = Math::SinCos(halfAngle);
64 return Quat(axis * s, c);
65 }
66
69 [[nodiscard]] inline static Quat FromTo(const Vec3& from, const Vec3& to) noexcept
70 {
71 const Float dot = DotProduct(from, to);
72
73 if (dot > Float(0.999999)) // directions already equal
74 return Identity(); // no rotation
75
76 if (dot < Float(-0.999999)) // opposite directions
78
79 // General case
80 return Quat(CrossProduct(from, to), Float(1) + dot).Normalized();
81 }
82
84 [[nodiscard]] inline static Quat FromBasis(const Basis<Vec3>& basis) noexcept
85 {
86 // axes-as-columns rotation matrix:
87 const Float r00 = GetX(basis.X), r01 = GetX(basis.Y), r02 = GetX(basis.Z);
88 const Float r10 = GetY(basis.X), r11 = GetY(basis.Y), r12 = GetY(basis.Z);
89 const Float r20 = GetZ(basis.X), r21 = GetZ(basis.Y), r22 = GetZ(basis.Z);
90
91 Float tr = r00 + r11 + r22;
92 Float qw, qx, qy, qz;
93
94 if (tr > 0.0f)
95 {
96 const Float s = Math::Sqrt(tr + Float(1.0)) * Float(2.0);
97 const Float invS = Float(1.0) / s;
98 qw = Float(0.25) * s;
99 qx = (r21 - r12) * invS;
100 qy = (r02 - r20) * invS;
101 qz = (r10 - r01) * invS;
102 }
103 else if (r00 > r11 && r00 > r22)
104 {
105 const Float s = Math::Sqrt(Float(1.0) + r00 - r11 - r22) * Float(2.0);
106 const Float invS = Float(1.0) / s;
107 qw = (r21 - r12) * invS;
108 qx = Float(0.25) * s;
109 qy = (r01 + r10) * invS;
110 qz = (r02 + r20) * invS;
111 }
112 else if (r11 > r22)
113 {
114 const Float s = Math::Sqrt(Float(1.0) + r11 - r00 - r22) * Float(2.0);
115 const Float invS = Float(1.0) / s;
116 qw = (r02 - r20) * invS;
117 qx = (r01 + r10) * invS;
118 qy = Float(0.25) * s;
119 qz = (r12 + r21) * invS;
120 }
121 else
122 {
123 const Float s = Math::Sqrt(Float(1.0) + r22 - r00 - r11) * Float(2.0);
124 const Float invS = Float(1.0) / s;
125 qw = (r10 - r01) * invS;
126 qx = (r02 + r20) * invS;
127 qy = (r12 + r21) * invS;
128 qz = Float(0.25) * s;
129 }
130
131 return Quat{ Vec3{ qx, qy, qz }, qw };
132 }
133
136 [[nodiscard]] static JPL_INLINE Quat FromUpAndForward(const Vec3& up, const Vec3& forward) noexcept
137 {
138 return FromBasis(Basis<Vec3>::FromUpAndForward(up, forward));
139 }
140
141 [[nodiscard]] static JPL_INLINE Quat LookAt(const Vec3& direction, const Vec3& up) noexcept
142 {
143 return FromUpAndForward(up, direction);
144 }
145
147 [[nodiscard]] JPL_INLINE friend Quat operator*(const Quat& a, const Quat& b) noexcept
148 {
149#if 1
150 return Quat{
151 .V = a.W * b.V + b.W * a.V + CrossProduct(a.V, b.V),
152 .W = a.W * b.W - DotProduct(a.V, b.V)
153 };
154#else
155 // Let the compiler figure out optimization
156 Float lx = a.V.X;
157 Float ly = a.V.Y;
158 Float lz = a.V.Z;
159 Float lw = a.W;
160
161 Float rx = b.V.X;
162 Float ry = b.V.Y;
163 Float rz = b.V.Z;
164 Float rw = b.W;
165
166 Float x = lw * rx + lx * rw + ly * rz - lz * ry;
167 Float y = lw * ry - lx * rz + ly * rw + lz * rx;
168 Float z = lw * rz + lx * ry - ly * rx + lz * rw;
169 Float w = lw * rw - lx * rx - ly * ry - lz * rz;
170
171 return Quat({ x, y, z }, w);
172#endif
173 }
174
176 [[nodiscard]] JPL_INLINE static Quat MakeSlerp(const Vec3& from, const Vec3& to, Float t) noexcept
177 {
178 const Quat qFromTo = FromTo(from, to);
179 return Slerp(Identity(), qFromTo, t); // id -> qFromTo
180 }
181
183 [[nodiscard]] JPL_INLINE Vec3 Rotate(const Vec3& vector) const noexcept
184 {
185 // q p q* (H. Hamilton trick, 18 mul + 12 add)
186 const Vec3 t = Float(2) * CrossProduct(V, vector);
187 return vector + W * t + CrossProduct(V, t);
188 }
189
190 [[nodiscard]] JPL_INLINE Float LengthSquared() const noexcept { return DotProduct(V, V) + W * W; }
191
192 [[nodiscard]] JPL_INLINE Float Length() const noexcept { return Math::Sqrt(LengthSquared()); }
193
194 [[nodiscard]] JPL_INLINE Quat Normalized() const noexcept
195 {
196 const Float invLen = Float(1) / Length();
197 return Quat{ V * invLen, W * invLen };
198 }
199
200 [[nodiscard]] JPL_INLINE bool IsNormalized(Float tolerance = Float(1.0e-5)) const noexcept
201 {
202 return Math::Abs(LengthSquared() - Float(1.0)) <= tolerance;
203 }
204
206 [[nodiscard]] JPL_INLINE Float GetRotationAngle(const Vec3& axis) const noexcept
207 {
208 return W == Float(0.0) ? JPL_PI : Float(2.0) * std::atan(DotProduct(V, axis) / W);
209 }
210
212 [[nodiscard]] inline Basis<Vec3> ToBasis() const noexcept
213 {
214 const Float xx = GetX(V) * GetX(V), yy = GetY(V) * GetY(V), zz = GetZ(V) * GetZ(V);
215 const Float xy = GetX(V) * GetY(V), xz = GetX(V) * GetZ(V), yz = GetY(V) * GetZ(V);
216 const Float wx = W * GetX(V), wy = W * GetY(V), wz = W * GetZ(V);
217 return Basis<Vec3>{
218 .X = Vec3{ Float(1) - Float(2) * (yy + zz), Float(2) * (xy + wz), Float(2) * (xz - wy) },
219 .Y = Vec3{ Float(2) * (xy - wz), Float(1) - Float(2) * (xx + zz), Float(2) * (yz + wx) },
220 .Z = Vec3{ Float(2) * (xz + wy), Float(2) * (yz - wx), Float(1) - Float(2) * (xx + yy) }
221 };
222 }
223
224 };
225
226 template<CVec3 Vec3>
227 std::ostream& operator<<(std::ostream& os, const Quat<Vec3>& quat) { os << "V ={ " << GetX(quat.V) << ", " << GetY(quat.V) << ", " << GetZ(quat.V) << " }, W = " << quat.W; return os; }
228
230 template<CVec3 Vec3>
231 [[nodiscard]] static inline Quat<Vec3> Slerp(const Quat<Vec3>& a, const Quat<Vec3>& b, typename Quat<Vec3>::Float t) noexcept
232 {
233 using F = typename Quat<Vec3>::Float;
234 F dot = a.W * b.W + DotProduct(a.V, b.V);
235 Quat<Vec3> b1 = (dot < 0) ? Quat<Vec3>{-b.W, -b.V} : b; // enforce short arc
236
237 if (Math::Abs(dot) > F(0.9995))
238 {
239 // near 0 degrees -> nlerp
240 Vec3 v = Math::Lerp(a.V, b1.V, t);
241 F w = Math::Lerp(a.W, b1.W, t);
242 return Quat<Vec3>{ v, w }.Normalized();
243 }
244
245 F phi = std::acos(dot);
246 F invSinPhi = F(1.0) / std::sin(phi);
247 F wA = std::sin((1 - t) * phi) * invSinPhi;
248 F wB = std::sin(t * phi) * invSinPhi;
249 return Quat<Vec3>{
250 .V = wA * a.V + wB * b1.V,
251 .W = wA * a.W + wB * b1.W
252 };
253 }
254
255 namespace Math
256 {
257 // Mainly just some helpers for template argument deduction
258
259 template<CVec3 Vec3>
260 [[nodiscard]] static JPL_INLINE Quat<Vec3> GetDeltaQuat(const Quat<Vec3>& a, const Quat<Vec3> b) noexcept
261 {
262 return a * b.Conjugated();
263 }
264
265 template<CVec3 Vec3>
266 [[nodiscard]] static JPL_INLINE Quat<Vec3> QuatRotation(const Vec3& axis, Internal::FloatOf<Vec3> angleRad) noexcept
267 {
268 return Quat<Vec3>::Rotation(axis, angleRad);
269 }
270
271 template<CVec3 Vec3>
272 [[nodiscard]] static JPL_INLINE Quat<Vec3> QuatFromTo(const Vec3& from, const Vec3& to) noexcept
273 {
274 return Quat<Vec3>::FromTo(from, to);
275 }
276
277 template<CVec3 Vec3>
278 [[nodiscard]] static JPL_INLINE Quat<Vec3> QuatFromBasis(const Basis<Vec3>& basis) noexcept
279 {
280 return Quat<Vec3>::FromBasis(basis);
281 }
282
283 template<CVec3 Vec3>
284 [[nodiscard]] static JPL_INLINE Quat<Vec3> QuatFromUpAndForward(const Vec3& up, const Vec3& forward) noexcept
285 {
286 return Quat<Vec3>::FromUpAndForward(up, forward);
287 }
288
289 template<CVec3 Vec3>
290 [[nodiscard]] static JPL_INLINE Quat<Vec3> QuatLookAt(const Vec3& direction, const Vec3& up) noexcept
291 {
292 return Quat<Vec3>::LookAt(direction, up);
293 }
294
295 } // namespace Math
296} // namespace JPL
std::remove_cvref_t< decltype(GetX(std::declval< Vec3 >()))> FloatOf
Definition Vec3Math.h:36
JPL_INLINE constexpr T Lerp(const T &v0, const T &v1, T t) noexcept
Linearly interpolate v0 towards v1.
Definition Math.h:172
JPL_INLINE constexpr T Sqrt(T x) noexcept
Definition Math.h:269
JPL_INLINE constexpr auto Abs(const T &value) noexcept
Standard abs is not constexpr in C++20.
Definition Math.h:87
JPL_INLINE std::pair< T, T > SinCos(T value) noexcept
Definition Math.h:164
Vec3 GetNormalizedPerpendicular(const Vec3 &vec) noexcept
Definition Vec3Math.h:67
Definition AcousticMaterial.h:36
JPL_INLINE auto GetX(const Vec3Type &v) noexcept
Definition Vec3Traits.h:35
JPL_INLINE auto GetZ(const Vec3Type &v) noexcept
Definition Vec3Traits.h:37
JPL_INLINE auto GetY(const Vec3Type &v) noexcept
Definition Vec3Traits.h:36
std::ostream & operator<<(std::ostream &os, const AcousticMaterial &v)
Definition AcousticMaterial.h:104
Orthonormal basis (column-major)
Definition MinimalBasis.h:35
Minimal quaternion (w + xi + yj + zk)
Definition MinimalQuat.h:38
JPL_INLINE Float Length() const noexcept
Definition MinimalQuat.h:192
Float W
Definition MinimalQuat.h:41
JPL_INLINE Float GetRotationAngle(const Vec3 &axis) const noexcept
Get rotation angle around axis.
Definition MinimalQuat.h:206
JPL_INLINE Quat Conjugated() const noexcept
Definition MinimalQuat.h:49
static JPL_INLINE Quat LookAt(const Vec3 &direction, const Vec3 &up) noexcept
Definition MinimalQuat.h:141
bool operator!=(const Quat &rhs) const noexcept
Definition MinimalQuat.h:45
JPL_INLINE Quat Inversed() const noexcept
Definition MinimalQuat.h:51
bool operator==(const Quat &rhs) const noexcept
Check if two quaternions are exactly equal.
Definition MinimalQuat.h:44
JPL_INLINE friend Quat operator*(const Quat &a, const Quat &b) noexcept
Quaternion multiplication (rotation composition)
Definition MinimalQuat.h:147
static JPL_INLINE Quat FromUpAndForward(const Vec3 &up, const Vec3 &forward) noexcept
Definition MinimalQuat.h:136
JPL_INLINE Vec3 Rotate(const Vec3 &vector) const noexcept
Rotate a vector by this quaternion.
Definition MinimalQuat.h:183
static Quat FromTo(const Vec3 &from, const Vec3 &to) noexcept
Definition MinimalQuat.h:69
static Quat FromBasis(const Basis< Vec3 > &basis) noexcept
Construct quaternian from basis columns X, Y, Z.
Definition MinimalQuat.h:84
Vec3 V
Definition MinimalQuat.h:40
Basis< Vec3 > ToBasis() const noexcept
Convert to an orthonormal Basis3 (column-major)
Definition MinimalQuat.h:212
static JPL_INLINE Quat Rotation(const Vec3 &axis, Float angleRad) noexcept
Definition MinimalQuat.h:60
JPL_INLINE Float LengthSquared() const noexcept
Definition MinimalQuat.h:190
Internal::FloatOf< Vec3 > Float
Definition MinimalQuat.h:39
JPL_INLINE bool IsNormalized(Float tolerance=Float(1.0e-5)) const noexcept
Definition MinimalQuat.h:200
static JPL_INLINE Quat MakeSlerp(const Vec3 &from, const Vec3 &to, Float t) noexcept
Create quaternian to slerp direction vector based on 'from' and 'to'.
Definition MinimalQuat.h:176
JPL_INLINE Quat Normalized() const noexcept
Definition MinimalQuat.h:194
static JPL_INLINE Quat Identity() noexcept
Definition MinimalQuat.h:47