JSim 2026.06.01-p(1)
Loading...
Searching...
No Matches
quaternion.hpp
Go to the documentation of this file.
1// Copyright (c) JSim contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the LGPLv3 license file in the root directory of this project.
4
5#pragma once
6#include <cmath>
7#include <algorithm>
8#include <cassert>
9#include "vector.hpp"
10
11namespace frcsim {
12
25struct Quaternion {
26 alignas(16) double w, x, y, z;
27
28 constexpr Quaternion() noexcept : w(1.0), x(0.0), y(0.0), z(0.0) {}
29 constexpr Quaternion(double w_, double x_, double y_, double z_) noexcept
30 : w(w_), x(x_), y(y_), z(z_) {}
31 constexpr Quaternion(double w_, const Vector3& v) noexcept
32 : w(w_), x(v.x), y(v.y), z(v.z) {}
33
34 [[nodiscard]]
35 constexpr double norm2() const noexcept {
36 return w * w + x * x + y * y + z * z;
37 }
38 [[nodiscard]]
39 double norm() const noexcept {
40 return std::sqrt(norm2());
41 }
42
43 [[nodiscard]]
44 bool isIdentity(double eps = 1e-12) const noexcept {
45 return std::abs(w - 1.0) < eps && std::abs(x) < eps && std::abs(y) < eps &&
46 std::abs(z) < eps;
47 }
48 [[nodiscard]]
49 bool hasNaN() const noexcept {
50 return std::isnan(w) || std::isnan(x) || std::isnan(y) || std::isnan(z);
51 }
52
53 [[nodiscard]]
54 Quaternion normalized() const noexcept {
55 double n = norm();
56 if (n < 1e-12)
57 return Quaternion();
58 return Quaternion(w / n, x / n, y / n, z / n);
59 }
60
61 // Normalize if needed (helper)
62 void normalizeIfNeeded(double eps = 1e-12) noexcept {
63 double n2 = norm2();
64 if (std::abs(n2 - 1.0) > eps)
65 normalize();
66 }
67
68 // Static: from axis-angle
69 static Quaternion fromAxisAngle(const Vector3& axis,
70 double angleRad) noexcept {
71 Vector3 nAxis = axis.normalized();
72 double half = 0.5 * angleRad;
73 double s = std::sin(half);
74 return Quaternion(std::cos(half), nAxis.x * s, nAxis.y * s, nAxis.z * s);
75 }
76
77 // From angular velocity (small angle approx)
79 double dt) noexcept {
80 double angle = omega.norm() * dt;
81 Vector3 axis = (angle > 1e-12) ? omega.normalized() : Vector3(1, 0, 0);
82 return fromAxisAngle(axis, angle);
83 }
84 // To axis-angle
85 void toAxisAngle(Vector3& axis, double& angleRad) const noexcept {
87 q.w = std::clamp(q.w, -1.0, 1.0);
88 angleRad = 2.0 * std::acos(q.w);
89 double s = std::sqrt(std::max(0.0, 1.0 - q.w * q.w));
90 if (s < 1e-12) {
91 axis = Vector3(1, 0, 0);
92 } else {
93 axis = Vector3(q.x / s, q.y / s, q.z / s);
94 }
95 }
96 // Slerp
97 static Quaternion slerp(const Quaternion& a, const Quaternion& b,
98 double t) noexcept {
99 double dot = a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
100 Quaternion b2 = b;
101 if (dot < 0.0) {
102 dot = -dot;
103 b2 = b * -1.0;
104 }
105 if (dot > 0.9995) {
106 // Linear
107 return (a * (1.0 - t) + b2 * t).normalized();
108 }
109 double theta = std::acos(dot);
110 double s1 = std::sin((1.0 - t) * theta);
111 double s2 = std::sin(t * theta);
112 double s = std::sin(theta);
113 return (a * s1 + b2 * s2) * (1.0 / s);
114 }
115 // To 3x3 rotation matrix (row-major)
116 void toMatrix(double m[3][3]) const noexcept {
117 double xx = x * x, yy = y * y, zz = z * z;
118 double xy = x * y, xz = x * z, yz = y * z;
119 double wx = w * x, wy = w * y, wz = w * z;
120 m[0][0] = 1.0 - 2.0 * (yy + zz);
121 m[0][1] = 2.0 * (xy - wz);
122 m[0][2] = 2.0 * (xz + wy);
123 m[1][0] = 2.0 * (xy + wz);
124 m[1][1] = 1.0 - 2.0 * (xx + zz);
125 m[1][2] = 2.0 * (yz - wx);
126 m[2][0] = 2.0 * (xz - wy);
127 m[2][1] = 2.0 * (yz + wx);
128 m[2][2] = 1.0 - 2.0 * (xx + yy);
129 }
130
131 void normalize() noexcept {
132 double n = norm();
133 if (n < 1e-12) {
134 w = 1.0;
135 x = y = z = 0.0;
136 return;
137 }
138 w /= n;
139 x /= n;
140 y /= n;
141 z /= n;
142 }
143
144 [[nodiscard]]
145 constexpr Quaternion conjugate() const noexcept {
146 return Quaternion(w, -x, -y, -z);
147 }
148
149 [[nodiscard]]
150 Quaternion inverse() const noexcept {
151 double n2 = norm2();
152 if (n2 < 1e-12)
153 return Quaternion();
154 Quaternion c = conjugate();
155 return Quaternion(c.w / n2, c.x / n2, c.y / n2, c.z / n2);
156 }
157
158 [[nodiscard]]
159 constexpr Quaternion operator*(const Quaternion& rhs) const noexcept {
160 return Quaternion(w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z,
161 w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y,
162 w * rhs.y - x * rhs.z + y * rhs.w + z * rhs.x,
163 w * rhs.z + x * rhs.y - y * rhs.x + z * rhs.w);
164 }
165
166 // Scalar multiply (right)
167 [[nodiscard]]
168 constexpr Quaternion operator*(double scalar) const noexcept {
169 return Quaternion(w * scalar, x * scalar, y * scalar, z * scalar);
170 }
171 // Scalar multiply (left)
172 friend constexpr Quaternion operator*(double scalar,
173 const Quaternion& q) noexcept {
174 return Quaternion(q.w * scalar, q.x * scalar, q.y * scalar, q.z * scalar);
175 }
176
177 // Rotate vector using conjugate (faster, more stable)
178 [[nodiscard]]
179 Vector3 rotate(const Vector3& v) const noexcept {
180 Quaternion qv(0.0, v);
181 Quaternion res = (*this * qv * conjugate());
182 return Vector3(res.x, res.y, res.z);
183 }
184
185 [[nodiscard]]
186 Vector3 forward() const noexcept {
187 return rotate(Vector3(0, 0, 1));
188 }
189 [[nodiscard]]
190 Vector3 up() const noexcept {
191 return rotate(Vector3(0, 1, 0));
192 }
193 [[nodiscard]]
194 Vector3 right() const noexcept {
195 return rotate(Vector3(1, 0, 0));
196 }
197
198 // No longer stores angular velocity; use Integrator for integration
199
200 [[nodiscard]]
201 constexpr Quaternion operator+(const Quaternion& rhs) const noexcept {
202 return Quaternion(w + rhs.w, x + rhs.x, y + rhs.y, z + rhs.z);
203 }
204 [[nodiscard]]
205 constexpr Quaternion operator-() const noexcept {
206 return Quaternion(-w, -x, -y, -z);
207 }
208 [[nodiscard]]
209 constexpr bool operator==(const Quaternion& rhs) const noexcept {
210 return w == rhs.w && x == rhs.x && y == rhs.y && z == rhs.z;
211 }
212 [[nodiscard]]
213 constexpr bool operator!=(const Quaternion& rhs) const noexcept {
214 return !(*this == rhs);
215 }
216
217 friend std::ostream& operator<<(std::ostream& os, const Quaternion& q) {
218 return os << "[" << q.w << ", (" << q.x << ", " << q.y << ", " << q.z
219 << ")]";
220 }
221};
222
223} // namespace frcsim
Definition vector.hpp:13
void toAxisAngle(Vector3 &axis, double &angleRad) const noexcept
Definition quaternion.hpp:85
constexpr Quaternion conjugate() const noexcept
Definition quaternion.hpp:145
friend std::ostream & operator<<(std::ostream &os, const Quaternion &q)
Definition quaternion.hpp:217
double x
Definition quaternion.hpp:26
double w
Definition quaternion.hpp:26
Vector3 right() const noexcept
Definition quaternion.hpp:194
constexpr bool operator!=(const Quaternion &rhs) const noexcept
Definition quaternion.hpp:213
constexpr double norm2() const noexcept
Definition quaternion.hpp:35
static Quaternion fromAngularVelocity(const Vector3 &omega, double dt) noexcept
Definition quaternion.hpp:78
Quaternion normalized() const noexcept
Definition quaternion.hpp:54
friend constexpr Quaternion operator*(double scalar, const Quaternion &q) noexcept
Definition quaternion.hpp:172
double norm() const noexcept
Definition quaternion.hpp:39
constexpr Quaternion operator-() const noexcept
Definition quaternion.hpp:205
Quaternion inverse() const noexcept
Definition quaternion.hpp:150
void normalize() noexcept
Definition quaternion.hpp:131
double z
Definition quaternion.hpp:26
constexpr bool operator==(const Quaternion &rhs) const noexcept
Definition quaternion.hpp:209
Vector3 up() const noexcept
Definition quaternion.hpp:190
bool hasNaN() const noexcept
Definition quaternion.hpp:49
constexpr Quaternion operator+(const Quaternion &rhs) const noexcept
Definition quaternion.hpp:201
bool isIdentity(double eps=1e-12) const noexcept
Definition quaternion.hpp:44
void toMatrix(double m[3][3]) const noexcept
Definition quaternion.hpp:116
constexpr Quaternion(double w_, const Vector3 &v) noexcept
Definition quaternion.hpp:31
void normalizeIfNeeded(double eps=1e-12) noexcept
Definition quaternion.hpp:62
Vector3 rotate(const Vector3 &v) const noexcept
Definition quaternion.hpp:179
static Quaternion fromAxisAngle(const Vector3 &axis, double angleRad) noexcept
Definition quaternion.hpp:69
constexpr Quaternion(double w_, double x_, double y_, double z_) noexcept
Definition quaternion.hpp:29
Vector3 forward() const noexcept
Definition quaternion.hpp:186
static Quaternion slerp(const Quaternion &a, const Quaternion &b, double t) noexcept
Definition quaternion.hpp:97
constexpr Quaternion operator*(double scalar) const noexcept
Definition quaternion.hpp:168
constexpr Quaternion() noexcept
Definition quaternion.hpp:28
constexpr Quaternion operator*(const Quaternion &rhs) const noexcept
Definition quaternion.hpp:159
double y
Definition quaternion.hpp:26
3D vector utility used throughout JSim physics.
Definition vector.hpp:22
Vector3 normalized() const noexcept
Returns the normalized direction vector.
Definition vector.hpp:119
double z
Z component.
Definition vector.hpp:28
double x
X component.
Definition vector.hpp:24
double y
Y component.
Definition vector.hpp:26