jaulib v1.3.8
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
quaternion.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11
12#ifndef JAU_MATH_QUATERNION_HPP_
13#define JAU_MATH_QUATERNION_HPP_
14
15#include <cmath>
16#include <cstdarg>
17#include <limits>
18#include <string>
19#include <iostream>
20
21#include <jau/float_math.hpp>
22#include <jau/math/vec3f.hpp>
23#include <jau/math/mat4f.hpp>
24
25namespace jau::math {
26
27 /** \addtogroup Math
28 *
29 * @{
30 */
31
32/**
33 * Quaternion implementation supporting
34 * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a> free rotations.
35 * <p>
36 * All matrix operation provided are in column-major order,
37 * as specified in the OpenGL fixed function pipeline, i.e. compatibility profile.
38 * See {@link FloatUtil}.
39 * </p>
40 * <p>
41 * See <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html">Matrix-FAQ</a>
42 * </p>
43 * <p>
44 * See <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/index.htm">euclideanspace.com-Quaternion</a>
45 * </p>
46 */
47template<typename Value_type,
48 std::enable_if_t<std::is_floating_point_v<Value_type>, bool> = true>
49class alignas(Value_type) Quaternion {
50 public:
53 typedef const value_type* const_pointer;
57 typedef const value_type* const_iterator;
58
60
61 constexpr static const value_type zero = value_type(0);
62 constexpr static const value_type one = value_type(1);
63 constexpr static const value_type two = value_type(2);
64 constexpr static const value_type half = one/two;
65
66 /**
67 * Quaternion Epsilon, used with equals method to determine if two Quaternions are close enough to be considered equal.
68 * <p>
69 * Using {@value}, which is ~8.4 times `std::numeric_limits<value_type>::epsilon()`.
70 * </p>
71 */
72 constexpr static const value_type allowed_deviation = value_type(8.4) * std::numeric_limits<value_type>::epsilon(); // 8.4 * EPSILON(1.1920929E-7f) = 1.0E-6f; double deviation: 1.0E-8f
73
74 private:
75 value_type m_x, m_y, m_z, m_w;
76
77 public:
78
79 constexpr Quaternion() noexcept
80 : m_x(0), m_y(0), m_z(0), m_w(1) {}
81
82 constexpr Quaternion(const value_type x, const value_type y, const value_type z, const value_type w) noexcept
83 : m_x(x), m_y(y), m_z(z), m_w(w) {}
84
85 constexpr Quaternion(const Quaternion& o) noexcept = default;
86 constexpr Quaternion(Quaternion&& o) noexcept = default;
87 constexpr Quaternion& operator=(const Quaternion&) noexcept = default;
88 constexpr Quaternion& operator=(Quaternion&&) noexcept = default;
89
90 /**
91 * See {@link #magnitude()} for special handling of {@link FloatUtil#EPSILON epsilon},
92 * which is not applied here.
93 * @return the squared magnitude of this quaternion.
94 */
95 constexpr value_type magnitudeSquared() const noexcept {
96 return m_w*m_w + m_x*m_x + m_y*m_y + m_z*m_z;
97 }
98
99 /**
100 * Return the magnitude of this quaternion, i.e. sqrt({@link #magnitudeSquared()})
101 * <p>
102 * A magnitude of zero shall equal {@link #isIdentity() identity},
103 * as performed by {@link #normalize()}.
104 * </p>
105 * <p>
106 * Implementation Details:
107 * <ul>
108 * <li> returns 0f if {@link #magnitudeSquared()} is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
109 * <li> returns 1f if {@link #magnitudeSquared()} is {@link FloatUtil#isEqual(value_type, value_type, value_type) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li>
110 * </ul>
111 * </p>
112 */
114 const value_type magnitudeSQ = magnitudeSquared();
115 if ( jau::is_zero(magnitudeSQ) ) {
116 return zero;
117 }
118 if ( jau::equals(one, magnitudeSQ) ) {
119 return one;
120 }
121 return std::sqrt(magnitudeSQ);
122 }
123
124 constexpr value_type w() const noexcept { return m_w; }
125
126 constexpr void setW(value_type w) noexcept { m_w = w; }
127
128 constexpr value_type x() const noexcept { return m_x; }
129
130 constexpr void setX(value_type x) noexcept { m_x = x; }
131
132 constexpr value_type y() const noexcept { return m_y; }
133
134 constexpr void setY(value_type y) noexcept { m_y = y; }
135
136 constexpr value_type z() const noexcept { return m_z; }
137
138 constexpr void setZ(value_type z) noexcept { m_z = z; }
139
140 /**
141 * Returns the dot product of this quaternion with the given x,y,z and m_w components.
142 */
143 constexpr value_type dot(value_type x, value_type y, value_type z, value_type w) const noexcept {
144 return m_x * x + m_y * y + m_z * z + m_w * w;
145 }
146
147 /**
148 * Returns the dot product of this quaternion with the given quaternion
149 */
150 constexpr value_type dot(const Quaternion& quat) const noexcept {
151 return dot(quat.x(), quat.y(), quat.z(), quat.w());
152 }
153
154 /**
155 * Returns <code>true</code> if this quaternion has identity.
156 * <p>
157 * Implementation uses epsilon to compare
158 * {@link #w() W} {@link jau::equals() against 1f} and
159 * {@link #x() X}, {@link #y() Y} and {@link #z() Z}
160 * {@link jau::is_zero3f() against zero}.
161 * </p>
162 */
163 constexpr bool isIdentity() const noexcept {
164 return jau::equals(one, m_w) && jau::is_zero3f(m_x, m_y, m_z);
165 // return m_w == 1f && m_x == 0f && m_y == 0f && m_z == 0f;
166 }
167
168 /***
169 * Set this quaternion to identity (x=0,y=0,z=0,w=1)
170 * @return this quaternion for chaining.
171 */
172 constexpr Quaternion& setIdentity() noexcept {
173 m_x = m_y = m_z = zero; m_w = one;
174 return *this;
175 }
176
177 /**
178 * Normalize a quaternion required if to be used as a rotational quaternion.
179 * <p>
180 * Implementation Details:
181 * <ul>
182 * <li> {@link #setIdentity()} if {@link #magnitude()} is {@link jau::isZero() is zero} using epsilon</li>
183 * </ul>
184 * </p>
185 * @return this quaternion for chaining.
186 */
187 constexpr Quaternion& normalize() noexcept {
188 const value_type norm = magnitude();
189 if ( jau::is_zero(norm) ) {
190 setIdentity();
191 } else {
192 const value_type invNorm = one/norm;
193 m_w *= invNorm;
194 m_x *= invNorm;
195 m_y *= invNorm;
196 m_z *= invNorm;
197 }
198 return *this;
199 }
200
201 /**
202 * Conjugates this quaternion <code>[-x, -y, -z, w]</code>.
203 * @return this quaternion for chaining.
204 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q49">Matrix-FAQ Q49</a>
205 */
206 constexpr Quaternion& conjugate() noexcept {
207 m_x = -m_x;
208 m_y = -m_y;
209 m_z = -m_z;
210 return *this;
211 }
212
213 /**
214 * Invert the quaternion If rotational, will produce a the inverse rotation
215 * <p>
216 * Implementation Details:
217 * <ul>
218 * <li> {@link #conjugate() conjugates} if {@link #magnitudeSquared()} is is {@link FloatUtil#isEqual(value_type, value_type, value_type) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li>
219 * </ul>
220 * </p>
221 * @return this quaternion for chaining.
222 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q50">Matrix-FAQ Q50</a>
223 */
224 constexpr Quaternion& invert() noexcept {
225 const value_type magnitudeSQ = magnitudeSquared();
226 if ( jau::equals(one, magnitudeSQ) ) {
227 conjugate();
228 } else {
229 const value_type invmsq = one/magnitudeSQ;
230 m_w *= invmsq;
231 m_x = -m_x * invmsq;
232 m_y = -m_y * invmsq;
233 m_z = -m_z * invmsq;
234 }
235 return *this;
236 }
237
238 /**
239 * Set all values of this quaternion using the given components.
240 * @return this quaternion for chaining.
241 */
242 constexpr Quaternion& set(const value_type x, const value_type y, const value_type z, const value_type w) noexcept {
243 m_x = x;
244 m_y = y;
245 m_z = z;
246 m_w = w;
247 return *this;
248 }
249
250 /**
251 * Add a quaternion: this = this + rhs, returns this
252 *
253 * @param q quaternion
254 * @return this quaternion for chaining.
255 * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#add">euclideanspace.com-QuaternionAdd</a>
256 */
257 constexpr Quaternion& operator+=(const Quaternion& rhs ) noexcept {
258 m_x += rhs.m_x;
259 m_y += rhs.m_y;
260 m_z += rhs.m_z;
261 m_w += rhs.m_w;
262 return *this;
263 }
264
265 /**
266 * Subtract a quaternion: this = this - rhs, returns this
267 *
268 * @param q quaternion
269 * @return this quaternion for chaining.
270 * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#add">euclideanspace.com-QuaternionAdd</a>
271 */
272 constexpr Quaternion& operator-=(const Quaternion& rhs) noexcept {
273 m_x -= rhs.m_x;
274 m_y -= rhs.m_y;
275 m_z -= rhs.m_z;
276 m_w -= rhs.m_w;
277 return *this;
278 }
279
280 /**
281 * Multiply this quaternion: this = this * rhs, returns this
282 *
283 * @param q a quaternion to multiply with
284 * @return this quaternion for chaining.
285 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q53">Matrix-FAQ Q53</a>
286 * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#mul">euclideanspace.com-QuaternionMul</a>
287 */
288 constexpr Quaternion& operator*=(const Quaternion& rhs) noexcept {
289 return set( m_w * rhs.m_x + m_x * rhs.m_w + m_y * rhs.m_z - m_z * rhs.m_y,
290 m_w * rhs.m_y - m_x * rhs.m_z + m_y * rhs.m_w + m_z * rhs.m_x,
291 m_w * rhs.m_z + m_x * rhs.m_y - m_y * rhs.m_x + m_z * rhs.m_w,
292 m_w * rhs.m_w - m_x * rhs.m_x - m_y * rhs.m_y - m_z * rhs.m_z );
293 }
294
295 /**
296 * Scale this quaternion by a scalar: this = this * rhs, returns this
297 *
298 * @param n a value_type constant
299 * @return this quaternion for chaining.
300 * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#scale">euclideanspace.com-QuaternionScale</a>
301 */
302 constexpr Quaternion& operator*=(const value_type rhs) noexcept {
303 m_x *= rhs;
304 m_y *= rhs;
305 m_z *= rhs;
306 m_w *= rhs;
307 return *this;
308 }
309
310 /**
311 * Rotate this quaternion by the given angle and axis.
312 * <p>
313 * The axis must be a normalized vector.
314 * </p>
315 * <p>
316 * A rotational quaternion is made from the given angle and axis.
317 * </p>
318 *
319 * @param angle in radians
320 * @param axisX x-coord of rotation axis
321 * @param axisY y-coord of rotation axis
322 * @param axisZ m_z-coord of rotation axis
323 * @return this quaternion for chaining.
324 */
325 constexpr_cxx26 Quaternion& rotateByAngleNormalAxis(const value_type angle, const value_type axisX, const value_type axisY, const value_type axisZ) noexcept {
326 if( jau::is_zero3f(axisX, axisY, axisZ) ) {
327 // no change
328 return *this;
329 }
330 const value_type halfAngle = half * angle;
331 const value_type sin = std::sin(halfAngle);
332 const value_type qw = std::cos(halfAngle);
333 const value_type qx = sin * axisX;
334 const value_type qy = sin * axisY;
335 const value_type qz = sin * axisZ;
336 return set( m_x * qw + m_y * qz - m_z * qy + m_w * qx,
337 -m_x * qz + m_y * qw + m_z * qx + m_w * qy,
338 m_x * qy - m_y * qx + m_z * qw + m_w * qz,
339 -m_x * qx - m_y * qy - m_z * qz + m_w * qw);
340 }
341
342 /**
343 * Rotate this quaternion by the given angle and axis.
344 * <p>
345 * The axis must be a normalized vector.
346 * </p>
347 * <p>
348 * A rotational quaternion is made from the given angle and axis.
349 * </p>
350 *
351 * @param angle in radians
352 * @param axis Vec3 coord of rotation axis
353 * @return this quaternion for chaining.
354 */
356 return rotateByAngleNormalAxis(angle, axis.x, axis.y, axis.z);
357 }
358
359 /**
360 * Rotate this quaternion around X axis with the given angle in radians
361 *
362 * @param angle in radians
363 * @return this quaternion for chaining.
364 */
366 const value_type halfAngle = half * angle;
367 return rotateByAngleX(std::sin(halfAngle), std::cos(halfAngle));
368 }
369 /** Rotate this quaternion around X axis with the given angle's sin + cos values */
370 constexpr Quaternion& rotateByAngleX(const value_type sin, const value_type cos) noexcept {
371 return set( m_x * cos + m_w * sin,
372 m_y * cos + m_z * sin,
373 -m_y * sin + m_z * cos,
374 -m_x * sin + m_w * cos);
375 }
376
377 /**
378 * Rotate this quaternion around Y axis with the given angle in radians
379 *
380 * @param angle in radians
381 * @return this quaternion for chaining.
382 */
384 const value_type halfAngle = half * angle;
385 return rotateByAngleY(std::sin(halfAngle), std::cos(halfAngle));
386 }
387 /** Rotate this quaternion around Y axis with the given angle's sin + cos values */
388 constexpr Quaternion& rotateByAngleY(const value_type sin, const value_type cos) noexcept {
389 return set( m_x * cos - m_z * sin,
390 m_y * cos + m_w * sin,
391 m_x * sin + m_z * cos,
392 -m_y * sin + m_w * cos);
393 }
394
395 /**
396 * Rotate this quaternion around Z axis with the given angle in radians
397 *
398 * @param angle in radians
399 * @return this quaternion for chaining.
400 */
402 const value_type halfAngle = half * angle;
403 return rotateByAngleZ(std::sin(halfAngle), std::cos(halfAngle));
404 }
405 /** Rotate this quaternion around Y axis with the given angle's sin + cos values */
406 constexpr Quaternion& rotateByAngleZ(const value_type sin, const value_type cos) noexcept {
407 return set( m_x * cos + m_y * sin,
408 -m_x * sin + m_y * cos,
409 m_z * cos + m_w * sin,
410 -m_z * sin + m_w * cos);
411 }
412
413 /**
414 * Rotates this quaternion from the given Euler rotation array <code>angradXYZ</code> in radians.
415 * <p>
416 * The <code>angradXYZ</code> array is laid out in natural order:
417 * <ul>
418 * <li>x - bank</li>
419 * <li>y - heading</li>
420 * <li>z - attitude</li>
421 * </ul>
422 * </p>
423 * For details see {@link #rotateByEuler(value_type, value_type, value_type)}.
424 * @param angradXYZ euler angle array in radians
425 * @return this quaternion for chaining.
426 * @see #rotateByEuler(value_type, value_type, value_type)
427 */
428 Quaternion& rotateByEuler(const Vec3& angradXYZ) noexcept {
429 return rotateByEuler(angradXYZ.x, angradXYZ.y, angradXYZ.z);
430 }
431
432 /**
433 * Rotates this quaternion from the given Euler rotation angles in radians.
434 * <p>
435 * The rotations are applied in the given order and using chained rotation per axis:
436 * <ul>
437 * <li>y - heading - {@link #rotateByAngleY(value_type)}</li>
438 * <li>z - attitude - {@link #rotateByAngleZ(value_type)}</li>
439 * <li>x - bank - {@link #rotateByAngleX(value_type)}</li>
440 * </ul>
441 * </p>
442 * <p>
443 * Implementation Details:
444 * <ul>
445 * <li> NOP if all angles are {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
446 * <li> result is {@link #normalize()}ed</li>
447 * </ul>
448 * </p>
449 * @param bankX the Euler pitch angle in radians. (rotation about the X axis)
450 * @param headingY the Euler yaw angle in radians. (rotation about the Y axis)
451 * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis)
452 * @return this quaternion for chaining.
453 * @see #rotateByAngleY(value_type)
454 * @see #rotateByAngleZ(value_type)
455 * @see #rotateByAngleX(value_type)
456 * @see #setFromEuler(value_type, value_type, value_type)
457 */
458 constexpr_cxx26 Quaternion& rotateByEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept {
459 if ( jau::is_zero3f(bankX, headingY, attitudeZ) ) {
460 return *this;
461 } else {
462 // setFromEuler muls: ( 8 + 4 ) , + quat muls 24 = 36
463 // this: 8 + 8 + 8 + 4 = 28 muls
464 return rotateByAngleY(headingY).rotateByAngleZ(attitudeZ).rotateByAngleX(bankX).normalize();
465 }
466 }
467
468 /***
469 * Rotate the given vector by this quaternion
470 * @param in vector to be rotated
471 *
472 * @return new Vec3 result
473 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q63">Matrix-FAQ Q63</a>
474 */
475 Vec3 rotateVector(const Vec3& in) noexcept {
476 Vec3 out;
477 rotateVector(in, out);
478 return out;
479 }
480
481 /***
482 * Rotate the given vector by this quaternion
483 * @param in vector to be rotated
484 * @param vecOut result storage for rotated vector, maybe equal to vecIn for in-place rotation
485 * @return the given out store for chaining
486 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q63">Matrix-FAQ Q63</a>
487 */
488 Vec3& rotateVector(const Vec3& in, Vec3& out) noexcept {
489 if( in.is_zero() ) {
490 out.set(0, 0, 0);
491 } else {
492 const value_type vecX = in.x;
493 const value_type vecY = in.y;
494 const value_type vecZ = in.z;
495 const value_type x_x = m_x*m_x;
496 const value_type y_y = m_y*m_y;
497 const value_type z_z = m_z*m_z;
498 const value_type w_w = m_w*m_w;
499
500 out.x = w_w * vecX
501 + x_x * vecX
502 - z_z * vecX
503 - y_y * vecX
504 + two * ( m_y*m_w*vecZ - m_z*m_w*vecY + m_y*m_x*vecY + m_z*m_x*vecZ );
505 ;
506
507 out.y = y_y * vecY
508 - z_z * vecY
509 + w_w * vecY
510 - x_x * vecY
511 + two * ( m_x*m_y*vecX + m_z*m_y*vecZ + m_w*m_z*vecX - m_x*m_w*vecZ );
512
513 out.z = z_z * vecZ
514 - y_y * vecZ
515 - x_x * vecZ
516 + w_w * vecZ
517 + two * ( m_x*m_z*vecX + m_y*m_z*vecY - m_w*m_y*vecX + m_w*m_x*vecY );
518 }
519 return out;
520 }
521
522 /**
523 * Set this quaternion to a spherical linear interpolation
524 * between the given start and end quaternions by the given change amount.
525 * <p>
526 * Note: Method <i>does not</i> normalize this quaternion!
527 * </p>
528 *
529 * @param a start quaternion
530 * @param b end quaternion
531 * @param changeAmnt value_type between 0 and 1 representing interpolation.
532 * @return this quaternion for chaining.
533 * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/">euclideanspace.com-QuaternionSlerp</a>
534 */
535 constexpr_cxx26 Quaternion& setSlerp(const Quaternion& a, const Quaternion& b, const value_type changeAmnt) noexcept {
536 // std::cerr << "Slerp.0: A " << a << ", B " << b << ", t " << changeAmnt << std::endl;
537 if (changeAmnt == zero) {
538 *this = a;
539 } else if (changeAmnt == one) {
540 *this = b;
541 } else {
542 value_type bx = b.m_x;
543 value_type by = b.m_y;
544 value_type bz = b.m_z;
545 value_type bw = b.m_w;
546
547 // Calculate angle between them (quat dot product)
548 value_type cosHalfTheta = a.m_x * bx + a.m_y * by + a.m_z * bz + a.m_w * bw;
549
550 value_type scale0, scale1;
551
552 if( cosHalfTheta >= 0.95f ) {
553 // quaternions are close, just use linear interpolation
554 scale0 = one - changeAmnt;
555 scale1 = changeAmnt;
556 } else if ( cosHalfTheta <= -0.99f ) {
557 // the quaternions are nearly opposite,
558 // we can pick any axis normal to a,b to do the rotation
559 scale0 = half;
560 scale1 = half;
561 } else {
562 if( cosHalfTheta <= -std::numeric_limits<value_type>::epsilon() ) { // FIXME: .. or shall we use the upper bound 'cosHalfTheta < EPSILON' ?
563 // Negate the second quaternion and the result of the dot product (Inversion)
564 bx *= -one;
565 by *= -one;
566 bz *= -one;
567 bw *= -one;
568 cosHalfTheta *= -one;
569 }
570 const value_type halfTheta = std::acos(cosHalfTheta);
571 const value_type sinHalfTheta = std::sqrt(one - cosHalfTheta*cosHalfTheta);
572 // if theta = 180 degrees then result is not fully defined
573 // we could rotate around any axis normal to qa or qb
574 if ( std::abs(sinHalfTheta) < 0.001f ){ // fabs is floating point absolute
575 scale0 = half;
576 scale1 = half;
577 // throw new InternalError("XXX"); // FIXME should not be reached due to above inversion ?
578 } else {
579 // Calculate the scale for q1 and q2, according to the angle and
580 // it's sine value
581 scale0 = std::sin((one - changeAmnt) * halfTheta) / sinHalfTheta;
582 scale1 = std::sin(changeAmnt * halfTheta) / sinHalfTheta;
583 }
584 }
585
586 m_x = a.m_x * scale0 + bx * scale1;
587 m_y = a.m_y * scale0 + by * scale1;
588 m_z = a.m_z * scale0 + bz * scale1;
589 m_w = a.m_w * scale0 + bw * scale1;
590 }
591 return *this;
592 }
593
594 /**
595 * Set this quaternion to equal the rotation required
596 * to point the z-axis at <i>direction</i> and the y-axis to <i>up</i>.
597 * <p>
598 * Implementation generates a 3x3 matrix
599 * and is equal with ProjectFloat's lookAt(..).<br/>
600 * </p>
601 * Implementation Details:
602 * <ul>
603 * <li> result is {@link #normalize()}ed</li>
604 * </ul>
605 * </p>
606 * @param directionIn where to <i>look</i> at
607 * @param upIn a vector indicating the local <i>up</i> direction.
608 * @param xAxisOut vector storing the <i>orthogonal</i> x-axis of the coordinate system.
609 * @param yAxisOut vector storing the <i>orthogonal</i> y-axis of the coordinate system.
610 * @param zAxisOut vector storing the <i>orthogonal</i> m_z-axis of the coordinate system.
611 * @return this quaternion for chaining.
612 * @see <a href="http://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm">euclideanspace.com-LookUp</a>
613 */
614 Quaternion& setLookAt(const Vec3& directionIn, const Vec3& upIn,
615 Vec3& xAxisOut, Vec3& yAxisOut, Vec3& zAxisOut) noexcept {
616 // Z = norm(dir)
617 (zAxisOut = directionIn).normalize();
618
619 // X = upIn m_x Z
620 // (borrow yAxisOut for upNorm)
621 (yAxisOut = upIn).normalize();
622 xAxisOut.cross(yAxisOut, zAxisOut).normalize();
623
624 // Y = Z m_x X
625 //
626 yAxisOut.cross(zAxisOut, xAxisOut).normalize();
627
628 /**
629 const value_type m00 = xAxisOut[0];
630 const value_type m01 = yAxisOut[0];
631 const value_type m02 = zAxisOut[0];
632 const value_type m10 = xAxisOut[1];
633 const value_type m11 = yAxisOut[1];
634 const value_type m12 = zAxisOut[1];
635 const value_type m20 = xAxisOut[2];
636 const value_type m21 = yAxisOut[2];
637 const value_type m22 = zAxisOut[2];
638 */
639 return setFromAxes(xAxisOut, yAxisOut, zAxisOut).normalize();
640 }
641
642 //
643 // Conversions
644 //
645
646 /**
647 * Initialize this quaternion from two vectors
648 * <pre>
649 * q = (s,v) = (v1•v2 , v1 × v2),
650 * angle = angle(v1, v2) = v1•v2
651 * axis = normal(v1 x v2)
652 * </pre>
653 * <p>
654 * Implementation Details:
655 * <ul>
656 * <li> set_identity() if square vector-length is jau::is_zero2f(value_type, value_type) using epsilon</li>
657 * </ul>
658 * </p>
659 * @param v1 not normalized
660 * @param v2 not normalized
661 * @return this quaternion for chaining.
662 */
663 constexpr_cxx26 Quaternion& setFromVectors(const Vec3& v1, const Vec3& v2) noexcept {
664 const value_type factor = v1.length() * v2.length();
665 if ( jau::is_zero(factor) ) {
666 return setIdentity();
667 } else {
668 const value_type dot = v1.dot(v2) / factor; // normalize
669 const value_type theta = std::acos(std::max(-one, std::min(dot, one))); // clipping [-1..1]
670
671 Vec3 tmpPivotVec = v1.cross(v2);
672
673 if ( dot < zero && jau::is_zero( tmpPivotVec.length() ) ) {
674 // Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector
675 // perpendicular to this vector will rotate vector a onto vector b.
676 //
677 // The following guarantees the dot-product will be 0.0.
678 int dominantIndex;
679 if (std::abs(v1.x) > std::abs(v1.y)) {
680 if (std::abs(v1.x) > std::abs(v1.z)) {
681 dominantIndex = 0;
682 } else {
683 dominantIndex = 2;
684 }
685 } else {
686 if (std::abs(v1.y) > std::abs(v1.z)) {
687 dominantIndex = 1;
688 } else {
689 dominantIndex = 2;
690 }
691 }
692 tmpPivotVec[dominantIndex] = -v1[ (dominantIndex + 1) % 3 ];
693 tmpPivotVec[(dominantIndex + 1) % 3] = v1[ dominantIndex ];
694 tmpPivotVec[(dominantIndex + 2) % 3] = zero;
695 }
696 return setFromAngleAxis(theta, tmpPivotVec);
697 }
698 }
699
700 /**
701 * Initialize this quaternion from two normalized vectors
702 * <pre>
703 * q = (s,v) = (v1•v2 , v1 × v2),
704 * angle = angle(v1, v2) = v1•v2
705 * axis = v1 x v2
706 * </pre>
707 * <p>
708 * Implementation Details:
709 * <ul>
710 * <li> {@link #setIdentity()} if square vector-length is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
711 * </ul>
712 * </p>
713 * @param v1 normalized
714 * @param v2 normalized
715 * @return this quaternion for chaining.
716 */
717 constexpr_cxx26 Quaternion& setFromNormalVectors(const Vec3& v1, const Vec3& v2) noexcept {
718 const value_type factor = v1.length() * v2.length();
719 if ( jau::is_zero(factor) ) {
720 return setIdentity();
721 } else {
722 const value_type dot = v1.dot(v2) / factor; // normalize
723 const value_type theta = std::acos(std::max(-one, std::min(dot, one))); // clipping [-1..1]
724
725 Vec3 tmpPivotVec = v1.cross(v2);
726
727 if ( dot < zero && jau::is_zero( tmpPivotVec.length() ) ) {
728 // Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector
729 // perpendicular to this vector will rotate vector a onto vector b.
730 //
731 // The following guarantees the dot-product will be 0.0.
732 int dominantIndex;
733 if (std::abs(v1.x) > std::abs(v1.y)) {
734 if (std::abs(v1.x) > std::abs(v1.z)) {
735 dominantIndex = 0;
736 } else {
737 dominantIndex = 2;
738 }
739 } else {
740 if (std::abs(v1.y) > std::abs(v1.z)) {
741 dominantIndex = 1;
742 } else {
743 dominantIndex = 2;
744 }
745 }
746 tmpPivotVec[dominantIndex] = -v1[ (dominantIndex + 1) % 3 ];
747 tmpPivotVec[(dominantIndex + 1) % 3] = v1[ dominantIndex ];
748 tmpPivotVec[(dominantIndex + 2) % 3] = zero;
749 }
750 return setFromAngleNormalAxis(theta, tmpPivotVec);
751 }
752 }
753
754 /***
755 * Initialize this quaternion with given non-normalized axis vector and rotation angle
756 * <p>
757 * Implementation Details:
758 * <ul>
759 * <li> {@link #setIdentity()} if axis is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
760 * </ul>
761 * </p>
762 * @param angle rotation angle (rads)
763 * @param vector axis vector not normalized
764 * @return this quaternion for chaining.
765 *
766 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a>
767 * @see #toAngleAxis(Vec3)
768 */
769 Quaternion& setFromAngleAxis(const value_type angle, const Vec3& vector) noexcept {
770 return setFromAngleNormalAxis(angle, Vec3(vector).normalize());
771 }
772
773 /***
774 * Initialize this quaternion with given normalized axis vector and rotation angle
775 * <p>
776 * Implementation Details:
777 * <ul>
778 * <li> {@link #setIdentity()} if axis is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
779 * </ul>
780 * </p>
781 * @param angle rotation angle (rads)
782 * @param vector axis vector normalized
783 * @return this quaternion for chaining.
784 *
785 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a>
786 * @see #toAngleAxis(Vec3)
787 */
788 constexpr_cxx26 Quaternion& setFromAngleNormalAxis(const value_type angle, const Vec3& vector) noexcept {
789 if( vector.is_zero() ) {
790 setIdentity();
791 } else {
792 const value_type halfangle = angle * half;
793 const value_type sin = std::sin(halfangle);
794 m_x = vector.x * sin;
795 m_y = vector.y * sin;
796 m_z = vector.z * sin;
797 m_w = std::cos(halfangle);
798 }
799 return *this;
800 }
801
802 /**
803 * Transform the rotational quaternion to axis based rotation angles
804 *
805 * @param axis storage for computed axis
806 * @return the rotation angle in radians
807 * @see #setFromAngleAxis(value_type, Vec3, Vec3)
808 */
810 const value_type sqrLength = m_x*m_x + m_y*m_y + m_z*m_z;
811 value_type angle;
812 if ( jau::is_zero(sqrLength) ) { // length is ~0
813 angle = zero;
814 axis.set( one, zero, zero );
815 } else {
816 angle = std::acos(m_w) * two;
817 const value_type invLength = one / std::sqrt(sqrLength);
818 axis.set( m_x * invLength,
819 m_y * invLength,
820 m_z * invLength );
821 }
822 return angle;
823 }
824
825 /**
826 * Initializes this quaternion from the given Euler rotation array <code>angradXYZ</code> in radians.
827 * <p>
828 * The <code>angradXYZ</code> vector is laid out in natural order:
829 * <ul>
830 * <li>x - bank</li>
831 * <li>y - heading</li>
832 * <li>z - attitude</li>
833 * </ul>
834 * </p>
835 * For details see {@link #setFromEuler(value_type, value_type, value_type)}.
836 * @param angradXYZ euler angle vector in radians holding x-bank, m_y-heading and m_z-attitude
837 * @return this quaternion for chaining.
838 * @see #setFromEuler(value_type, value_type, value_type)
839 */
840 constexpr_cxx26 Quaternion& setFromEuler(const Vec3& angradXYZ) noexcept {
841 return setFromEuler(angradXYZ.x, angradXYZ.y, angradXYZ.z);
842 }
843
844 /**
845 * Initializes this quaternion from the given Euler rotation angles in radians.
846 * <p>
847 * The rotations are applied in the given order:
848 * <ul>
849 * <li>y - heading</li>
850 * <li>z - attitude</li>
851 * <li>x - bank</li>
852 * </ul>
853 * </p>
854 * <p>
855 * Implementation Details:
856 * <ul>
857 * <li> {@link #setIdentity()} if all angles are {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
858 * <li> result is {@link #normalize()}ed</li>
859 * </ul>
860 * </p>
861 * @param bankX the Euler pitch angle in radians. (rotation about the X axis)
862 * @param headingY the Euler yaw angle in radians. (rotation about the Y axis)
863 * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis)
864 * @return this quaternion for chaining.
865 *
866 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60">Matrix-FAQ Q60</a>
867 * @see <a href="http://vered.rose.utoronto.ca/people/david_dir/GEMS/GEMS.html">Gems</a>
868 * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">euclideanspace.com-eulerToQuaternion</a>
869 * @see #toEuler(Vec3)
870 */
871 constexpr_cxx26 Quaternion& setFromEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept {
872 if ( jau::is_zero3f(bankX, headingY, attitudeZ) ) {
873 return setIdentity();
874 } else {
875 value_type angle = headingY * half;
876 const value_type sinHeadingY = std::sin(angle);
877 const value_type cosHeadingY = std::cos(angle);
878 angle = attitudeZ * half;
879 const value_type sinAttitudeZ = std::sin(angle);
880 const value_type cosAttitudeZ = std::cos(angle);
881 angle = bankX * half;
882 const value_type sinBankX = std::sin(angle);
883 const value_type cosBankX = std::cos(angle);
884
885 // variables used to reduce multiplication calls.
886 const value_type cosHeadingXcosAttitude = cosHeadingY * cosAttitudeZ;
887 const value_type sinHeadingXsinAttitude = sinHeadingY * sinAttitudeZ;
888 const value_type cosHeadingXsinAttitude = cosHeadingY * sinAttitudeZ;
889 const value_type sinHeadingXcosAttitude = sinHeadingY * cosAttitudeZ;
890
891 m_w = cosHeadingXcosAttitude * cosBankX - sinHeadingXsinAttitude * sinBankX;
892 m_x = cosHeadingXcosAttitude * sinBankX + sinHeadingXsinAttitude * cosBankX;
893 m_y = sinHeadingXcosAttitude * cosBankX + cosHeadingXsinAttitude * sinBankX;
894 m_z = cosHeadingXsinAttitude * cosBankX - sinHeadingXcosAttitude * sinBankX;
895 return normalize();
896 }
897 }
898
899 /**
900 * Transform this quaternion to Euler rotation angles in radians (pitchX, yawY and rollZ).
901 * <p>
902 * The <code>result</code> array is laid out in natural order:
903 * <ul>
904 * <li>x - bank</li>
905 * <li>y - heading</li>
906 * <li>z - attitude</li>
907 * </ul>
908 * </p>
909 *
910 * @return new Vec3 euler angle result vector filled with x-bank, y-heading and m_z-attitude
911 * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm">euclideanspace.com-quaternionToEuler</a>
912 * @see #setFromEuler(value_type, value_type, value_type)
913 */
915 const value_type sqw = m_w*m_w;
916 const value_type sqx = m_x*m_x;
917 const value_type sqy = m_y*m_y;
918 const value_type sqz = m_z*m_z;
919 const value_type unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise, is correction factor
920 const value_type test = m_x*m_y + m_z*m_w;
921
922 if (test > 0.499f * unit) { // singularity at north pole
923 return Vec3( zero, // m_x-bank
924 two * std::atan2(m_x, m_w), // y-heading
925 M_PI_2 ); // z-attitude
926 } else if (test < -0.499f * unit) { // singularity at south pole
927 return Vec3( zero, // m_x-bank
928 -two * std::atan2(m_x, m_w), // m_y-heading
929 -M_PI_2 ); // m_z-attitude
930 } else {
931 return Vec3( std::atan2(two * m_x * m_w - two * m_y * m_z, -sqx + sqy - sqz + sqw), // m_x-bank
932 std::atan2(two * m_y * m_w - two * m_x * m_z, sqx - sqy - sqz + sqw), // m_y-heading
933 std::asin( two * test / unit) ); // z-attitude
934 }
935 }
936
937 /**
938 * Compute the quaternion from a 3x3 column rotation matrix
939 * <p>
940 * See <a href="ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z">Graphics Gems Code</a>,<br/>
941 * <a href="http://mathworld.wolfram.com/MatrixTrace.html">MatrixTrace</a>.
942 * </p>
943 * <p>
944 * Buggy <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55">Matrix-FAQ Q55</a>
945 * </p>
946 *
947 * @return this quaternion for chaining.
948 * @see #setFromMatrix(Matrix4f)
949 */
950 constexpr Quaternion& setFromMat(const value_type m00, const value_type m01, const value_type m02,
951 const value_type m10, const value_type m11, const value_type m12,
952 const value_type m20, const value_type m21, const value_type m22) noexcept {
953 // Note: Other implementations uses 'T' w/o '+1f' and compares 'T >= 0' while adding missing 1f in sqrt expr.
954 // However .. this causes setLookAt(..) to fail and actually violates the 'trace definition'.
955
956 // The trace T is the sum of the diagonal elements; see
957 // http://mathworld.wolfram.com/MatrixTrace.html
958 const value_type T = m00 + m11 + m22 + one;
959 if ( T > zero ) {
960 const value_type S = half / std::sqrt(T); // S = 1 / ( 2 t )
961 m_w = 0.25f / S; // m_w = 1 / ( 4 S ) = t / 2
962 m_x = ( m21 - m12 ) * S;
963 m_y = ( m02 - m20 ) * S;
964 m_z = ( m10 - m01 ) * S;
965 } else if ( m00 > m11 && m00 > m22) {
966 const value_type S = half / std::sqrt(one + m00 - m11 - m22); // S=4*qx
967 m_w = ( m21 - m12 ) * S;
968 m_x = 0.25f / S;
969 m_y = ( m10 + m01 ) * S;
970 m_z = ( m02 + m20 ) * S;
971 } else if ( m11 > m22 ) {
972 const value_type S = half / std::sqrt(one + m11 - m00 - m22); // S=4*qy
973 m_w = ( m02 - m20 ) * S;
974 m_x = ( m20 + m01 ) * S;
975 m_y = 0.25f / S;
976 m_z = ( m21 + m12 ) * S;
977 } else {
978 const value_type S = half / std::sqrt(one + m22 - m00 - m11); // S=4*qz
979 m_w = ( m10 - m01 ) * S;
980 m_x = ( m02 + m20 ) * S;
981 m_y = ( m21 + m12 ) * S;
982 m_z = 0.25f / S;
983 }
984 return *this;
985 }
986
987 /**
988 * Compute the quaternion from a 3x3 column rotation matrix from mat4f instance
989 * <p>
990 * See <a href="ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z">Graphics Gems Code</a>,<br/>
991 * <a href="http://mathworld.wolfram.com/MatrixTrace.html">MatrixTrace</a>.
992 * </p>
993 * <p>
994 * Buggy <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55">Matrix-FAQ Q55</a>
995 * </p>
996 *
997 * @return this quaternion for chaining.
998 * @see Matrix4f#getRotation(Quaternion)
999 * @see #setFromMatrix(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type)
1000 */
1001 constexpr Quaternion& setFromMat(const Mat4f& m) noexcept {
1002 return setFromMat(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22);
1003 }
1004
1005 /**
1006 * Initializes this quaternion to represent a rotation formed by the given three <i>orthogonal</i> axes.
1007 * <p>
1008 * No validation whether the axes are <i>orthogonal</i> is performed.
1009 * </p>
1010 *
1011 * @param xAxis vector representing the <i>orthogonal</i> x-axis of the coordinate system.
1012 * @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system.
1013 * @param zAxis vector representing the <i>orthogonal</i> m_z-axis of the coordinate system.
1014 * @return this quaternion for chaining.
1015 */
1016 constexpr Quaternion& setFromAxes(const Vec3& xAxis, const Vec3& yAxis, const Vec3& zAxis) noexcept {
1017 return setFromMat(xAxis.x, yAxis.x, zAxis.x,
1018 xAxis.y, yAxis.y, zAxis.y,
1019 xAxis.z, yAxis.z, zAxis.z);
1020 }
1021
1022 /**
1023 * Transform this quaternion to a normalized 4x4 column matrix representing the rotation.
1024 * <p>
1025 * Implementation Details:
1026 * <ul>
1027 * <li> makes identity matrix if {@link #magnitudeSquared()} is {@link jau::is_zero(value_type, value_type) is zero} using {@link FloatUtil#EPSILON epsilon}</li>
1028 * </ul>
1029 * </p>
1030 *
1031 * @return resulting normalized column matrix 4x4
1032 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54">Matrix-FAQ Q54</a>
1033 * @see #setFromMatrix(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type)
1034 * @see Matrix4f#setToRotation(Quaternion)
1035 */
1036 constexpr Mat4f toMatrix() const noexcept {
1037 Mat4f m; toMatrix(m); return m;
1038 }
1039
1040 /**
1041 * Transform this quaternion to a normalized 4x4 column matrix representing the rotation,
1042 * see toMatrix().
1043 *
1044 * @param out store for the resulting normalized column matrix 4x4
1045 * @return the given matrix store
1046 */
1047 constexpr Mat4f& toMatrix(Mat4f& m) const noexcept {
1048 // pre-multiply scaled-reciprocal-magnitude to reduce multiplications
1049 const value_type norm = magnitudeSquared();
1050 if ( jau::is_zero(norm) ) {
1051 // identity matrix -> srecip = 0f
1052 m.loadIdentity();
1053 return m;
1054 }
1055 value_type srecip;
1056 if ( jau::equals(one, norm) ) {
1057 srecip = two;
1058 } else {
1059 srecip = two / norm;
1060 }
1061 const value_type x = m_x;
1062 const value_type y = m_y;
1063 const value_type z = m_z;
1064 const value_type w = m_w;
1065
1066 const value_type xs = srecip * x;
1067 const value_type ys = srecip * y;
1068 const value_type zs = srecip * z;
1069
1070 const value_type xx = x * xs;
1071 const value_type xy = x * ys;
1072 const value_type xz = x * zs;
1073 const value_type xw = xs * w;
1074 const value_type yy = y * ys;
1075 const value_type yz = y * zs;
1076 const value_type yw = ys * w;
1077 const value_type zz = z * zs;
1078 const value_type zw = zs * w;
1079
1080 m.m00 = one - ( yy + zz );
1081 m.m01 = ( xy - zw );
1082 m.m02 = ( xz + yw );
1083 m.m03 = zero;
1084
1085 m.m10 = ( xy + zw );
1086 m.m11 = one - ( xx + zz );
1087 m.m12 = ( yz - xw );
1088 m.m13 = zero;
1089
1090 m.m20 = ( xz - yw );
1091 m.m21 = ( yz + xw );
1092 m.m22 = one - ( xx + yy );
1093 m.m23 = zero;
1094
1095 m.m30 = m.m31 = m.m32 = zero;
1096 m.m33 = one;
1097 return m;
1098 }
1099
1100 /**
1101 * Extracts this quaternion's <i>orthogonal</i> rotation axes.
1102 *
1103 * @param xAxis vector representing the <i>orthogonal</i> x-axis of the coordinate system.
1104 * @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system.
1105 * @param zAxis vector representing the <i>orthogonal</i> m_z-axis of the coordinate system.
1106 * @param tmp temporary Matrix4 used for toMatrix()
1107 */
1108 constexpr void toAxes(Vec3& xAxis, Vec3& yAxis, Vec3& zAxis, Matrix4<value_type>& tmp) const noexcept {
1109 toMatrix(tmp);
1110 tmp.getColumn(2, zAxis);
1111 tmp.getColumn(1, yAxis);
1112 tmp.getColumn(0, xAxis);
1113 }
1114
1115 /**
1116 * Extracts this quaternion's <i>orthogonal</i> rotation axes.
1117 *
1118 * @param xAxis vector representing the <i>orthogonal</i> x-axis of the coordinate system.
1119 * @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system.
1120 * @param zAxis vector representing the <i>orthogonal</i> m_z-axis of the coordinate system.
1121 */
1122 constexpr void toAxes(Vec3& xAxis, Vec3& yAxis, Vec3& zAxis) const noexcept {
1124 toAxes(xAxis, yAxis, zAxis, tmp);
1125 }
1126
1127 //
1128 // std overrides
1129 //
1130
1131 /**
1132 * @param o the object to compare for equality
1133 * @return true if this quaternion and the provided quaternion have roughly the same x, m_y, m_z and m_w values.
1134 */
1135 constexpr bool operator==(const Quaternion& o) const noexcept {
1136 if (this == &o) {
1137 return true;
1138 }
1139 return std::abs(m_x - o.m_x) <= allowed_deviation &&
1140 std::abs(m_y - o.m_y) <= allowed_deviation &&
1141 std::abs(m_z - o.m_z) <= allowed_deviation &&
1142 std::abs(m_w - o.m_w) <= allowed_deviation;
1143 }
1144
1145 std::string toString() const noexcept {
1146 return "Quat[x "+std::to_string(m_x)+", y "+std::to_string(m_y)+", z "+std::to_string(m_z)+", w "+std::to_string(m_w)+"]";
1147 }
1148};
1149
1150template<typename T,
1151 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1152constexpr Quaternion<T> operator+(const Quaternion<T>& lhs, const Quaternion<T>& rhs ) noexcept {
1153 Quaternion<T> r(lhs); r += rhs; return r;
1154}
1155
1156template<typename T,
1157 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1158constexpr Quaternion<T> operator-(const Quaternion<T>& lhs, const Quaternion<T>& rhs ) noexcept {
1159 Quaternion<T> r(lhs); r -= rhs; return r;
1160}
1161
1162template<typename T,
1163 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1164constexpr Quaternion<T> operator*(const Quaternion<T>& lhs, const Quaternion<T>& rhs ) noexcept {
1165 Quaternion<T> r(lhs); r *= rhs; return r;
1166}
1167
1168template<typename T,
1169 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1170constexpr Quaternion<T> operator*(const Quaternion<T>& lhs, const T s ) noexcept {
1171 Quaternion<T> r(lhs); r *= s; return r;
1172}
1173
1174template<typename T,
1175 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1176constexpr Quaternion<T> operator*(const T s, const Quaternion<T>& rhs) noexcept {
1177 Quaternion<T> r(rhs); r *= s; return r;
1178}
1179
1180template<typename T,
1181 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1182std::ostream& operator<<(std::ostream& out, const Quaternion<T>& v) noexcept {
1183 return out << v.toString();
1184}
1185
1187static_assert(alignof(float) == alignof(Quat4f));
1188
1189/**@}*/
1190
1191} // namespace jau::math
1192
1193#endif // JAU_MATH_QUATERNION_HPP_
Basic 4x4 value_type matrix implementation using fields for intensive use-cases (host operations).
Definition mat4f.hpp:101
Quaternion implementation supporting Gimbal-Lock free rotations.
constexpr_cxx26 Quaternion & rotateByAngleX(const value_type angle) noexcept
Rotate this quaternion around X axis with the given angle in radians.
constexpr Quaternion & operator+=(const Quaternion &rhs) noexcept
Add a quaternion: this = this + rhs, returns this.
constexpr value_type magnitudeSquared() const noexcept
const value_type * const_iterator
Vec3 rotateVector(const Vec3 &in) noexcept
constexpr Quaternion & normalize() noexcept
Normalize a quaternion required if to be used as a rotational quaternion.
value_type * iterator
constexpr Quaternion(const value_type x, const value_type y, const value_type z, const value_type w) noexcept
constexpr Quaternion & set(const value_type x, const value_type y, const value_type z, const value_type w) noexcept
Set all values of this quaternion using the given components.
constexpr Quaternion(Quaternion &&o) noexcept=default
static constexpr const value_type allowed_deviation
constexpr Quaternion & conjugate() noexcept
Conjugates this quaternion [-x, -y, -z, w].
static constexpr const value_type one
constexpr void toAxes(Vec3 &xAxis, Vec3 &yAxis, Vec3 &zAxis, Matrix4< value_type > &tmp) const noexcept
Extracts this quaternion's orthogonal rotation axes.
static constexpr const value_type zero
constexpr_cxx26 Quaternion & setFromEuler(const Vec3 &angradXYZ) noexcept
Initializes this quaternion from the given Euler rotation array angradXYZ in radians.
constexpr Quaternion & operator-=(const Quaternion &rhs) noexcept
Subtract a quaternion: this = this - rhs, returns this.
constexpr Quaternion & operator*=(const value_type rhs) noexcept
Scale this quaternion by a scalar: this = this * rhs, returns this.
constexpr_cxx26 Quaternion & setFromNormalVectors(const Vec3 &v1, const Vec3 &v2) noexcept
Initialize this quaternion from two normalized vectors.
constexpr_cxx26 Quaternion & rotateByAngleNormalAxis(const value_type angle, const value_type axisX, const value_type axisY, const value_type axisZ) noexcept
Rotate this quaternion by the given angle and axis.
constexpr bool isIdentity() const noexcept
Returns true if this quaternion has identity.
constexpr value_type dot(value_type x, value_type y, value_type z, value_type w) const noexcept
Returns the dot product of this quaternion with the given x,y,z and m_w components.
std::string toString() const noexcept
constexpr_cxx26 Quaternion & rotateByEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept
Rotates this quaternion from the given Euler rotation angles in radians.
constexpr Quaternion & invert() noexcept
Invert the quaternion If rotational, will produce a the inverse rotation.
constexpr_cxx26 Vec3 toEuler() noexcept
Transform this quaternion to Euler rotation angles in radians (pitchX, yawY and rollZ).
constexpr_cxx26 value_type magnitude() const noexcept
Return the magnitude of this quaternion, i.e.
constexpr Quaternion & setFromMat(const Mat4f &m) noexcept
Compute the quaternion from a 3x3 column rotation matrix from mat4f instance.
constexpr Mat4f toMatrix() const noexcept
Transform this quaternion to a normalized 4x4 column matrix representing the rotation.
constexpr void setY(value_type y) noexcept
Vec3 & rotateVector(const Vec3 &in, Vec3 &out) noexcept
constexpr Quaternion() noexcept
constexpr Quaternion & setIdentity() noexcept
Quaternion & setLookAt(const Vec3 &directionIn, const Vec3 &upIn, Vec3 &xAxisOut, Vec3 &yAxisOut, Vec3 &zAxisOut) noexcept
Set this quaternion to equal the rotation required to point the z-axis at direction and the y-axis to...
constexpr_cxx26 Quaternion & setFromAngleNormalAxis(const value_type angle, const Vec3 &vector) noexcept
static constexpr const value_type two
constexpr value_type dot(const Quaternion &quat) const noexcept
Returns the dot product of this quaternion with the given quaternion.
constexpr value_type x() const noexcept
Quaternion & setFromAngleAxis(const value_type angle, const Vec3 &vector) noexcept
constexpr Quaternion & rotateByAngleY(const value_type sin, const value_type cos) noexcept
Rotate this quaternion around Y axis with the given angle's sin + cos values.
constexpr Quaternion & operator=(Quaternion &&) noexcept=default
value_type & reference
constexpr Quaternion & operator*=(const Quaternion &rhs) noexcept
Multiply this quaternion: this = this * rhs, returns this.
Quaternion & rotateByEuler(const Vec3 &angradXYZ) noexcept
Rotates this quaternion from the given Euler rotation array angradXYZ in radians.
const value_type & const_reference
constexpr Quaternion & operator=(const Quaternion &) noexcept=default
constexpr value_type y() const noexcept
constexpr Quaternion & setFromAxes(const Vec3 &xAxis, const Vec3 &yAxis, const Vec3 &zAxis) noexcept
constexpr_cxx26 value_type toAngleAxis(Vec3 &axis) const noexcept
Transform the rotational quaternion to axis based rotation angles.
const value_type * const_pointer
constexpr bool operator==(const Quaternion &o) const noexcept
constexpr value_type w() const noexcept
constexpr void setX(value_type x) noexcept
constexpr_cxx26 Quaternion & setFromVectors(const Vec3 &v1, const Vec3 &v2) noexcept
Initialize this quaternion from two vectors.
constexpr Quaternion & setFromMat(const value_type m00, const value_type m01, const value_type m02, const value_type m10, const value_type m11, const value_type m12, const value_type m20, const value_type m21, const value_type m22) noexcept
Compute the quaternion from a 3x3 column rotation matrix.
constexpr_cxx26 Quaternion & setSlerp(const Quaternion &a, const Quaternion &b, const value_type changeAmnt) noexcept
Set this quaternion to a spherical linear interpolation between the given start and end quaternions b...
constexpr Quaternion & rotateByAngleZ(const value_type sin, const value_type cos) noexcept
Rotate this quaternion around Y axis with the given angle's sin + cos values.
constexpr void setZ(value_type z) noexcept
constexpr Quaternion(const Quaternion &o) noexcept=default
constexpr_cxx26 Quaternion & rotateByAngleNormalAxis(const value_type angle, const Vec3 &axis) noexcept
Rotate this quaternion by the given angle and axis.
constexpr_cxx26 Quaternion & rotateByAngleZ(value_type angle) noexcept
Rotate this quaternion around Z axis with the given angle in radians.
Vector3F< value_type, std::is_floating_point_v< Value_type > > Vec3
constexpr Quaternion & rotateByAngleX(const value_type sin, const value_type cos) noexcept
Rotate this quaternion around X axis with the given angle's sin + cos values.
constexpr Mat4f & toMatrix(Mat4f &m) const noexcept
Transform this quaternion to a normalized 4x4 column matrix representing the rotation,...
static constexpr const value_type half
constexpr void toAxes(Vec3 &xAxis, Vec3 &yAxis, Vec3 &zAxis) const noexcept
Extracts this quaternion's orthogonal rotation axes.
constexpr_cxx26 Quaternion & rotateByAngleY(value_type angle) noexcept
Rotate this quaternion around Y axis with the given angle in radians.
constexpr value_type z() const noexcept
constexpr void setW(value_type w) noexcept
constexpr_cxx26 Quaternion & setFromEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept
Initializes this quaternion from the given Euler rotation angles in radians.
3D vector using three value_type components.
Definition vec3f.hpp:45
constexpr value_type length() const noexcept
Return the length of a vector, a.k.a the norm or magnitude
Definition vec3f.hpp:252
#define constexpr_cxx26
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr equals(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if both values are equal, i.e.
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr is_zero3f(const T &a, const T &b, const T &c, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if all given values a, b and c are less than epsilon, w/ epsilon > 0.
constexpr Quaternion< T > operator-(const Quaternion< T > &lhs, const Quaternion< T > &rhs) noexcept
Matrix4< float > Mat4f
Definition mat4f.hpp:1928
Quaternion< float > Quat4f
constexpr Matrix4< T > operator*(const Matrix4< T > &lhs, const Matrix4< T > &rhs) noexcept
Definition mat4f.hpp:1906
constexpr Quaternion< T > operator+(const Quaternion< T > &lhs, const Quaternion< T > &rhs) noexcept
std::ostream & operator<<(std::ostream &out, const Addr48Bit &a)
uint8_t Value_type