From babfefccffd84f22e3373a0ff0865755a651ec8e Mon Sep 17 00:00:00 2001 From: jtlap Date: Thu, 10 Aug 2023 16:07:33 +0200 Subject: [PATCH] Minor changes to quaternions docs and purepart implementation --- include/eve/module/core/regular/dot.hpp | 2 - include/eve/module/quaternion.hpp | 72 +++++++++++++------ include/eve/module/quaternion/quaternion.hpp | 38 +++++++--- .../eve/module/quaternion/regular/axis.hpp | 1 + .../module/quaternion/regular/purepart.hpp | 70 ++++++++++++++++++ .../module/quaternion/regular/quaternion.hpp | 1 + test/unit/module/quaternion/constructor.cpp | 7 ++ test/unit/module/quaternion/purepart.cpp | 48 +++++++++++++ 8 files changed, 203 insertions(+), 36 deletions(-) create mode 100644 include/eve/module/quaternion/regular/purepart.hpp create mode 100644 test/unit/module/quaternion/purepart.cpp diff --git a/include/eve/module/core/regular/dot.hpp b/include/eve/module/core/regular/dot.hpp index 6b9dfeab61..b9ee7293f3 100644 --- a/include/eve/module/core/regular/dot.hpp +++ b/include/eve/module/core/regular/dot.hpp @@ -54,8 +54,6 @@ namespace eve //! //! @} //================================================================================================ -// namespace tag { struct dot_; } -// template<> struct supports_conditional : std::false_type {}; EVE_MAKE_CALLABLE(dot_, dot); diff --git a/include/eve/module/quaternion.hpp b/include/eve/module/quaternion.hpp index 69330ce157..678db9390d 100644 --- a/include/eve/module/quaternion.hpp +++ b/include/eve/module/quaternion.hpp @@ -62,25 +62,50 @@ //! //! # Constructors //! -//! Quaternions can be created from quadruplet of floating point values or pair of complex values -//! using the call to eve::to_quaternions or eve::as_quaternion_t. We refer to the product tpe -//! documentation to see why it is generally advisable not to use the direct constructors. +//! a quaternion can be constructed : //! -//! the functions `semipolar`, `cylindrical`,`cylindrospherical` and `spherical`can also be used -//! to generate quaternions from various \f$\mathbb{R}^4\f$ element representations, quite as -//! creating complex numbers using polar representation. +//! * from 4 reals using as_quaternion_t, +//! * from 2 complex using as_quaternion_t, +//! * from 1 real and a 3 dimensuionnal span using as_quaternion_t, +//! * There are also a small family of functions that construct quaternions from various +//! \f$\mathbb{R}^4\f$ coordinates systems : from_cylindrical, from_semi_cylindrical, +//! from_spherical, from cylindrico_spherical and also from Euler angles : from_euler +//! or from \f$\mathbb{R}^3f\$ rotation_matrix :from_rotation_matrix //! -//! #arithmetic fonctions +//! @note `from_euler` takes three angles (in radian), three axes and a boolean (extrinsic/intrinsic), +//! `to compute a quaternion representation of the rotation. //! -//! Some but not all arithmetic function are at hand : +//! # Extractors //! -//! | | | | | | | | -//! | -------- | -------- | -------- | ------- | --------- | --------- | -------- | -//! | abs | add | average | ceil | conj | dec | dist | -//! | div | dot | floor | frac | inc | lerp | maxabs | -//! | minabs | minus | mul | nearest | negmaxabs | negminabs | oneminus | -//! | plus | pure | rec | reldist | sign | slerp | sqr | -//! | sqr_abs | sub | to_euler | trunc | | | | +//! As alredy stated, a quaternion is considered to be represented as +//! \f${\displaystyle a+b\ \mathbf {i} +c\ \mathbf {j} +d\ \mathbf {k} ,}\f$. +//! +//! * the a, b, c and d coefficients can be get/set using real, ipart, jpart, kpart. +//! * purepart return a 3 dimensional span containing only the three last quaternion coordinates. +//! * imag is reserved for complex numbers. +//! +//! @note the function pure does not extract anything but returns a copy the quaternion with zeroed real part. +//! If T is the type of a quaternion coordinate: a quaternion q always satisfies +//! +//! * q == as_quaternion_t(real(q), purepart(q)) +//! * q == real(q)+pure(q) +//! +//! # Arithmetic functions +//! +//! Some arithmetic function are at hand : +//! +//! | | | | | | | | +//! | ----------- | ---------- | ---------- | -------- | --------- | --------- | ------------ | +//! | abs | add | average | ceil | conj | dec | diff_of_prod | +//! | dist | div | dot | fam | fanm | floor | fma | +//! | fms | fnma | fnms | frac | fsm | fsnm | inc | +//! | if_else | lerp | manhattan | maxabs | maxmag | minabs | minmag | +//! | minus | mul | nearest | negmaxabs | negminabs | oneminus | plus | +//! | pure | rec | reldist | rot_vec | sign | slerp | sqr | +//! | sqr_abs | sub | sum_of_prod | to_euler | to_rotation_matrix | trunc | | +//! +//! * `rot_vec` takes a quaternion and a span of dimension 3 to rotate the vector of \f$\mathbb{R}^3\f$ +//! using the quaternion.(note that the quaternions and/or the vector can have simd components) //! //! * `sqr_abs` computes the so called Cayley norm of a quaternion, which is the square //! of the Euclidian norm (given by `abs`) and so is not a norm at all but a quadratic form @@ -91,26 +116,26 @@ //! considered as elements of the unit \$f\mathbb{R]^4\$f hypersphere and //! is a common operation in keyframe animation. //! -//! * `to_euler` takes a quaternion three axes and a boolean (extrinsic/intrinsic), to computes three angle +//! * `to_euler` takes a quaternion three axes and a boolean (extrinsic/intrinsic), to compute three angles //! in radian which are the Euler or Bryan-Taits angles according to the axes given. //! -//! #Mathematical fonctions +//! # Mathematical functions //! //! Some but not all math function are at hand : //! //! | | | | | | | | //! | -------- | ----------- | ------- | --------- | --------- | -------- | -------- | //! | cos | cosh | cot | coth | exp | expm1 | hypot | -//! | lpnorm | manhattan | pow | powm1 | sin | sincos | sinh | -//! | sinhcosh | sqrt | tan | tanh | | | | +//! | lpnorm | pow | powm1 | sin | sincos | sinh | sinhcosh | +//! | sqrt | tan | tanh | | | | | //! -//! @note `sqrt` on quaternions can have two square roots (general case) or infinitely many ones +//! @note `sqrt`: a quaternions can have two square roots (general case) or infinitely many ones //! is the quaternion "is real" and the real part is negative. We always return -//! the root with positive or null real part and the complex i*sqrt(abs(q)) in the second -//! case. +//! the root with positive or null real part in the first case and the complex +//! i*sqrt(abs(q)) in the second one. //! //! -//! #Predicate fonctions +//! # Predicate functions //! //! There is no order on the quaternions compatible with the algebra operators. //! What remains are equality or inequality, ieee relative properties and `is_pure` (resp. `is_not_pure`), @@ -122,6 +147,7 @@ //! | is_not_denormal | is_not_equal | is_not_finite | is_not_infinite | is_not_nan | is_not_pure | is_not_real | //! | is_ordered | is_pure | is_real | is_unordered | | | | //! +//! @note is_imag is reserved to complex numbers. However on complexes it coincide with is_pure. //! @} //================================================================================================== #include diff --git a/include/eve/module/quaternion/quaternion.hpp b/include/eve/module/quaternion/quaternion.hpp index 92808e261c..729f1f5e5c 100644 --- a/include/eve/module/quaternion/quaternion.hpp +++ b/include/eve/module/quaternion/quaternion.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace eve { @@ -47,11 +48,13 @@ namespace eve using underlying_type = underlying_type_t; /// Default constructor - quaternion( ) noexcept : parent{0,0,0,0} {} - explicit quaternion(Type r ) noexcept : parent{r,0,0,0} {} - quaternion(Type r, Type i, Type j, Type k) noexcept : parent{r,i,j,k} {} - quaternion(c_t r0, c_t r1 ) noexcept : parent{real(r0), imag(r0), real(r1), imag(r1)} {} + quaternion( ) noexcept : parent{0,0,0,0}{} + explicit quaternion(Type r ) noexcept : parent{r,0,0,0}{} + quaternion(Type r, Type i, Type j, Type k) noexcept : parent{r,i,j,k}{} + quaternion(c_t r0, c_t r1 ) noexcept : parent{real(r0), imag(r0), real(r1), imag(r1)}{} quaternion(c_t r0 ) noexcept : parent{real(r0), imag(r0), 0, 0} {} + quaternion(Type r0, kumi::tuple v) noexcept + : parent{r0, get<0>(v), get<1>(v), get<2>(v)}{} /// Stream insertion operator friend std::ostream& operator<<(std::ostream& os, like auto const& z) @@ -97,6 +100,12 @@ namespace eve return z1; } + EVE_FORCEINLINE friend auto tagged_dispatch(eve::tag::purepart_, like auto q ) + { + using a_t = std::array, 3>; + return a_t{ipart(q), jpart(q), kpart(q)}; + } + //============================================================================================== // Operators //============================================================================================== @@ -300,22 +309,29 @@ namespace eve } } + template + EVE_FORCEINLINE auto to_quaternion( Q0 const & q0, Q1 const & q1, Q2 const & q2, Q3 const & q3) noexcept + { + using e_t = std::decay_t; + return as_quaternion_t(q0, q1, q2, q3); + } + template EVE_FORCEINLINE auto to_quaternion( Q1 const & q1, Q2 const & q2) noexcept requires(is_complex_v && is_complex_v) { - using e_t = std::decay_t; - return as_quaternion_t(real(q1), imag(q1), real(q2), imag(q2)); + return to_quaternion(real(q1), imag(q1), real(q2), imag(q2)); } - template - EVE_FORCEINLINE auto to_quaternion( Q0 const & q0, Q1 const & q1, Q2 const & q2, Q3 const & q3) noexcept + + template P> + EVE_FORCEINLINE auto to_quaternion( Q0 const & q0, P const & p) noexcept { - using e_t = std::decay_t; - return as_quaternion_t(q0, q1, q2, q3); + using e_t = std::decay_t(p), get<1>(p), get<2>(p))))>; + return to_quaternion(e_t(q0), e_t(get<0>(p)), e_t(get<1>(p)), e_t(get<2>(p))); } - template + template EVE_FORCEINLINE auto tagged_dispatch(eve::tag::if_else_, auto const& cond, Z1 const & z1, diff --git a/include/eve/module/quaternion/regular/axis.hpp b/include/eve/module/quaternion/regular/axis.hpp index ca02a2cc4c..ebd9dfe284 100644 --- a/include/eve/module/quaternion/regular/axis.hpp +++ b/include/eve/module/quaternion/regular/axis.hpp @@ -39,6 +39,7 @@ namespace eve //! //! **Return value** //! (1, 0, 0) if `x` is real or the unreal part of `x/abs(x)` if x is an instance of eve::quaternion or eve::complex + //! in an std::array of 3 elements //! //! #### Example //! diff --git a/include/eve/module/quaternion/regular/purepart.hpp b/include/eve/module/quaternion/regular/purepart.hpp new file mode 100644 index 0000000000..3907f4b8b9 --- /dev/null +++ b/include/eve/module/quaternion/regular/purepart.hpp @@ -0,0 +1,70 @@ +//================================================================================================== +/* + EVE - Expressive Vector Engine + Copyright : EVE Project Contributors + SPDX-License-Identifier: BSL-1.0 +*/ +//================================================================================================== +#pragma once + +#include + +namespace eve +{ + //================================================================================================ + //! @addtogroup quaternion + //! @{ + //! @var purepart + //! + //! @brief Callable object computing purepart part of values. + //! + //! **Defined in header** `#include ` + //! + //! #### Members Functions + //! + //! | Member | Effect | + //! |:-------------|:-----------------------------------------------------------| + //! | `operator()` | the computation of purepart part | + //! + //! --- + //! + //! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + //! auto operator()(value auto x) const noexcept; + //! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + //! + //! **Parameters** + //! + //!`x`: [value](@ref eve::value). + //! + //! **Return value** + //! 0 if `x` is real, imag(x) if x is an instance of eve::complex, + //! or the purepart (second component) of `x` + //! if x is an instance of eve::quaternion. + //! + //! #### Example + //! + //! @godbolt{doc/quaternion/regular/purepart.cpp} + //! + //! @} + //================================================================================================ + + namespace tag { struct purepart_; } + template<> struct supports_conditional : std::false_type {}; + + EVE_MAKE_CALLABLE(purepart_, purepart); + + namespace detail + { + template + EVE_FORCEINLINE auto purepart_( EVE_SUPPORTS(cpu_), V const& ) noexcept + { + return std::array{0, 0, 0}; + } + + template + EVE_FORCEINLINE auto purepart_( EVE_SUPPORTS(cpu_), eve::complex const & z) noexcept + { + return std::array{imag(z), 0, 0}; + } + } +} diff --git a/include/eve/module/quaternion/regular/quaternion.hpp b/include/eve/module/quaternion/regular/quaternion.hpp index 558896c613..a0304e285c 100755 --- a/include/eve/module/quaternion/regular/quaternion.hpp +++ b/include/eve/module/quaternion/regular/quaternion.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/test/unit/module/quaternion/constructor.cpp b/test/unit/module/quaternion/constructor.cpp index ba2749cf19..154acae3d8 100644 --- a/test/unit/module/quaternion/constructor.cpp +++ b/test/unit/module/quaternion/constructor.cpp @@ -24,6 +24,8 @@ TTS_CASE_TPL( "Check quaternion constructor from constants", eve::test::scalar:: eve::wide> z_vs{wo,spi,spi,spi}; eve::wide> z_sv{spi,wo,wo,wo}; eve::wide> z_vv{wo,wpi,wpi,wpi}; + auto a = kumi::make_tuple(wpi,wpi,wpi); + eve::wide> z_ra = eve::to_quaternion(wo,a); TTS_EQUAL( eve::real(z_sd), T{0} ); TTS_EQUAL( eve::ipart(z_sd), T{0} ); @@ -59,6 +61,11 @@ TTS_CASE_TPL( "Check quaternion constructor from constants", eve::test::scalar:: TTS_EQUAL( eve::ipart(z_vv), wpi); TTS_EQUAL( eve::jpart(z_vv), wpi); TTS_EQUAL( eve::kpart(z_vv), wpi); + + TTS_EQUAL( eve::real(z_ra), wo ); + TTS_EQUAL( eve::ipart(z_ra), wpi); + TTS_EQUAL( eve::jpart(z_ra), wpi); + TTS_EQUAL( eve::kpart(z_ra), wpi); }; TTS_CASE_TPL("Check quaternion constructor from lambda", eve::test::scalar::ieee_reals) diff --git a/test/unit/module/quaternion/purepart.cpp b/test/unit/module/quaternion/purepart.cpp new file mode 100644 index 0000000000..ce2942c66c --- /dev/null +++ b/test/unit/module/quaternion/purepart.cpp @@ -0,0 +1,48 @@ +//================================================================================================== +/** + EVE - Expressive Vector Engine + Copyright : EVE Project Contributors + SPDX-License-Identifier: BSL-1.0 +**/ +//================================================================================================== +#include "test.hpp" +#include + +TTS_CASE_WITH( "Check behavior of purepart on scalar" + , tts::bunch + , tts::generate ( tts::randoms(eve::valmin, eve::valmax) + , tts::randoms(eve::valmin, eve::valmax) + , tts::randoms(eve::valmin, eve::valmax) + , tts::randoms(eve::valmin, eve::valmax) + ) + ) +(T const& a0, T const& a1, T const& a2, T const& a3) +{ + using e_t = typename T::value_type; + for(size_t i = 0; i < a0.size(); ++i) + { + auto a = eve::purepart(eve::quaternion(a0[i],a1[i],a2[i],a3[i])); + TTS_EQUAL( a1[i], get<0>(a) ); + TTS_EQUAL( a2[i], get<1>(a) ); + TTS_EQUAL( a3[i], get<2>(a) ); + } +}; + +TTS_CASE_WITH( "Check behavior of purepart on wide" + , eve::test::simd::ieee_reals + , tts::generate ( tts::randoms(eve::valmin, eve::valmax) + , tts::randoms(eve::valmin, eve::valmax) + , tts::randoms(eve::valmin, eve::valmax) + , tts::randoms(eve::valmin, eve::valmax) + ) + ) +(T const& a0, T const& a1, T const& a2, T const& a3 ) +{ + using e_t = typename T::value_type; + using z_t = eve::wide, typename T::cardinal_type>; + + auto a = eve::purepart(z_t(a0,a1,a2,a3)); + TTS_EQUAL( a1, get<0>(a)); + TTS_EQUAL( a2, get<1>(a)); + TTS_EQUAL( a3, get<2>(a)); +};