Skip to content

Commit

Permalink
Minor changes to quaternions docs and purepart implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jtlap authored Aug 10, 2023
1 parent 938ef12 commit babfefc
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 36 deletions.
2 changes: 0 additions & 2 deletions include/eve/module/core/regular/dot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ namespace eve
//!
//! @}
//================================================================================================
// namespace tag { struct dot_; }
// template<> struct supports_conditional<tag::dot_> : std::false_type {};

EVE_MAKE_CALLABLE(dot_, dot);

Expand Down
72 changes: 49 additions & 23 deletions include/eve/module/quaternion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<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<T>,
//! * from 2 complex using as_quaternion_t<T>,
//! * from 1 real and a 3 dimensuionnal span using as_quaternion_t<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<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
Expand All @@ -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`),
Expand All @@ -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 <eve/wide.hpp>
Expand Down
38 changes: 27 additions & 11 deletions include/eve/module/quaternion/quaternion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <eve/module/quaternion/detail/math.hpp>
#include <eve/module/quaternion/detail/predicates.hpp>
#include <ostream>
#include <array>

namespace eve
{
Expand Down Expand Up @@ -47,11 +48,13 @@ namespace eve
using underlying_type = underlying_type_t<Type>;

/// 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<Type, Type, Type> v) noexcept
: parent{r0, get<0>(v), get<1>(v), get<2>(v)}{}

/// Stream insertion operator
friend std::ostream& operator<<(std::ostream& os, like<quaternion> auto const& z)
Expand Down Expand Up @@ -97,6 +100,12 @@ namespace eve
return z1;
}

EVE_FORCEINLINE friend auto tagged_dispatch(eve::tag::purepart_, like<quaternion> auto q )
{
using a_t = std::array<std::decay_t<decltype(real(q))>, 3>;
return a_t{ipart(q), jpart(q), kpart(q)};
}

//==============================================================================================
// Operators
//==============================================================================================
Expand Down Expand Up @@ -300,22 +309,29 @@ namespace eve
}
}

template<ordered_value Q0, ordered_value Q1, ordered_value Q2, ordered_value Q3>
EVE_FORCEINLINE auto to_quaternion( Q0 const & q0, Q1 const & q1, Q2 const & q2, Q3 const & q3) noexcept
{
using e_t = std::decay_t<decltype(real(add(q0, q1, q2, q3)))>;
return as_quaternion_t<e_t>(q0, q1, q2, q3);
}

template<value Q1, value Q2>
EVE_FORCEINLINE auto to_quaternion( Q1 const & q1, Q2 const & q2) noexcept
requires(is_complex_v<Q1> && is_complex_v<Q2>)
{
using e_t = std::decay_t<decltype(real(q1+q2))>;
return as_quaternion_t<e_t>(real(q1), imag(q1), real(q2), imag(q2));
return to_quaternion(real(q1), imag(q1), real(q2), imag(q2));
}

template<ordered_value Q0, ordered_value Q1, ordered_value Q2, ordered_value Q3>
EVE_FORCEINLINE auto to_quaternion( Q0 const & q0, Q1 const & q1, Q2 const & q2, Q3 const & q3) noexcept

template<ordered_value Q0, kumi::sized_product_type<3> P>
EVE_FORCEINLINE auto to_quaternion( Q0 const & q0, P const & p) noexcept
{
using e_t = std::decay_t<decltype(real(q0+q1+q2+q3))>;
return as_quaternion_t<e_t>(q0, q1, q2, q3);
using e_t = std::decay_t<decltype(real(add(q0, get<0>(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<value Z1, value Z2>
template<value Z1, value Z2>
EVE_FORCEINLINE auto tagged_dispatch(eve::tag::if_else_,
auto const& cond,
Z1 const & z1,
Expand Down
1 change: 1 addition & 0 deletions include/eve/module/quaternion/regular/axis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//!
Expand Down
70 changes: 70 additions & 0 deletions include/eve/module/quaternion/regular/purepart.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//==================================================================================================
/*
EVE - Expressive Vector Engine
Copyright : EVE Project Contributors
SPDX-License-Identifier: BSL-1.0
*/
//==================================================================================================
#pragma once

#include <eve/detail/overload.hpp>

namespace eve
{
//================================================================================================
//! @addtogroup quaternion
//! @{
//! @var purepart
//!
//! @brief Callable object computing purepart part of values.
//!
//! **Defined in header** `#include <eve/module/quaternion.hpp>`
//!
//! #### 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<tag::purepart_> : std::false_type {};

EVE_MAKE_CALLABLE(purepart_, purepart);

namespace detail
{
template<value V>
EVE_FORCEINLINE auto purepart_( EVE_SUPPORTS(cpu_), V const& ) noexcept
{
return std::array<V, 3>{0, 0, 0};
}

template<value V>
EVE_FORCEINLINE auto purepart_( EVE_SUPPORTS(cpu_), eve::complex<V> const & z) noexcept
{
return std::array<V, 3>{imag(z), 0, 0};
}
}
}
1 change: 1 addition & 0 deletions include/eve/module/quaternion/regular/quaternion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <eve/module/quaternion/regular/ipart.hpp>
#include <eve/module/quaternion/regular/jpart.hpp>
#include <eve/module/quaternion/regular/kpart.hpp>
#include <eve/module/quaternion/regular/purepart.hpp>
#include <eve/module/quaternion/regular/is_not_pure.hpp>
#include <eve/module/quaternion/regular/is_pure.hpp>
#include <eve/module/quaternion/regular/to_euler.hpp>
Expand Down
7 changes: 7 additions & 0 deletions test/unit/module/quaternion/constructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ TTS_CASE_TPL( "Check quaternion constructor from constants", eve::test::scalar::
eve::wide<eve::quaternion<T>> z_vs{wo,spi,spi,spi};
eve::wide<eve::quaternion<T>> z_sv{spi,wo,wo,wo};
eve::wide<eve::quaternion<T>> z_vv{wo,wpi,wpi,wpi};
auto a = kumi::make_tuple(wpi,wpi,wpi);
eve::wide<eve::quaternion<T>> z_ra = eve::to_quaternion(wo,a);

TTS_EQUAL( eve::real(z_sd), T{0} );
TTS_EQUAL( eve::ipart(z_sd), T{0} );
Expand Down Expand Up @@ -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)
Expand Down
48 changes: 48 additions & 0 deletions test/unit/module/quaternion/purepart.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//==================================================================================================
/**
EVE - Expressive Vector Engine
Copyright : EVE Project Contributors
SPDX-License-Identifier: BSL-1.0
**/
//==================================================================================================
#include "test.hpp"
#include <eve/module/quaternion.hpp>

TTS_CASE_WITH( "Check behavior of purepart on scalar"
, tts::bunch<eve::test::scalar::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)
)
)
<typename T>(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<e_t>(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)
)
)
<typename T>(T const& a0, T const& a1, T const& a2, T const& a3 )
{
using e_t = typename T::value_type;
using z_t = eve::wide<eve::quaternion<e_t>, 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));
};

0 comments on commit babfefc

Please sign in to comment.