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