Browse Source

Math: new CubicHermite class.

For spline interpolation.
pull/267/head
Vladimír Vondruš 8 years ago
parent
commit
e997344b8c
  1. 1
      doc/changelog.dox
  2. 4
      doc/types.dox
  3. 30
      src/Magnum/Magnum.h
  4. 1
      src/Magnum/Math/CMakeLists.txt
  5. 5
      src/Magnum/Math/Complex.h
  6. 422
      src/Magnum/Math/CubicHermite.h
  7. 8
      src/Magnum/Math/Functions.h
  8. 23
      src/Magnum/Math/Math.h
  9. 5
      src/Magnum/Math/Quaternion.h
  10. 2
      src/Magnum/Math/Test/CMakeLists.txt
  11. 915
      src/Magnum/Math/Test/CubicHermiteTest.cpp
  12. 12
      src/Magnum/Math/instantiation.cpp

1
doc/changelog.dox

@ -47,6 +47,7 @@ See also:
@subsubsection changelog-latest-new-math Math library @subsubsection changelog-latest-new-math Math library
- New @ref Math::CubicHermite class for cubic Hermite spline interpolation
- Added @ref Math::Intersection::rangeFrustum(), - Added @ref Math::Intersection::rangeFrustum(),
@ref Math::Intersection::aabbFrustum(), @ref Math::Intersection::aabbFrustum(),
@ref Math::Intersection::sphereFrustum(), @ref Math::Intersection::sphereFrustum(),

4
doc/types.dox

@ -170,10 +170,14 @@ Other types, which don't have their GLSL equivalent, are:
or @ref QuadraticBezier3Dd or @ref QuadraticBezier3Dd
- @ref CubicBezier2D or @ref CubicBezier2Dd, @ref CubicBezier3D - @ref CubicBezier2D or @ref CubicBezier2Dd, @ref CubicBezier3D
or @ref CubicBezier3Dd or @ref CubicBezier3Dd
- @ref CubicHermite1D or @ref CubicHermite1Dd, @ref CubicHermite2D or
@ref CubicHermite2Dd, @ref CubicHermite3D or @ref CubicHermite3Dd
- @ref Complex or @ref Complexd, @ref DualComplex or @ref DualComplexd - @ref Complex or @ref Complexd, @ref DualComplex or @ref DualComplexd
- @ref Frustum or @ref Frustumd - @ref Frustum or @ref Frustumd
- @ref Quaternion or @ref Quaterniond, @ref DualQuaternion or - @ref Quaternion or @ref Quaterniond, @ref DualQuaternion or
@ref DualQuaterniond @ref DualQuaterniond
- @ref CubicHermiteComplex or @ref CubicHermiteComplexd
- @ref CubicHermiteQuaternion or @ref CubicHermiteQuaterniond
- @ref Range1D / @ref Range2D / @ref Range3D, @ref Range1Di / @ref Range2Di / - @ref Range1D / @ref Range2D / @ref Range3D, @ref Range1Di / @ref Range2Di /
@ref Range3Di or @ref Range1Dd / @ref Range2Dd / @ref Range3Dd @ref Range3Di or @ref Range1Dd / @ref Range2Dd / @ref Range3Dd

30
src/Magnum/Magnum.h

@ -456,6 +456,21 @@ typedef Math::CubicBezier2D<Float> CubicBezier2D;
/** @brief Float three-dimensional cubic Bézier curve */ /** @brief Float three-dimensional cubic Bézier curve */
typedef Math::CubicBezier3D<Float> CubicBezier3D; typedef Math::CubicBezier3D<Float> CubicBezier3D;
/** @brief Float scalar cubic Hermite spline point */
typedef Math::CubicHermite1D<Float> CubicHermite1D;
/** @brief Float two-dimensional cubic Hermite spline point */
typedef Math::CubicHermite2D<Float> CubicHermite2D;
/** @brief Float three-dimensional cubic Hermite spline point */
typedef Math::CubicHermite3D<Float> CubicHermite3D;
/** @brief Float cubic Hermite spline complex number */
typedef Math::CubicHermiteComplex<Float> CubicHermiteComplex;
/** @brief Float cubic Hermite spline quaternion */
typedef Math::CubicHermiteQuaternion<Float> CubicHermiteQuaternion;
/** @brief Float complex number */ /** @brief Float complex number */
typedef Math::Complex<Float> Complex; typedef Math::Complex<Float> Complex;
@ -641,6 +656,21 @@ typedef Math::CubicBezier2D<Float> CubicBezier2Dd;
/** @brief Double three-dimensional cubic Bézier curve */ /** @brief Double three-dimensional cubic Bézier curve */
typedef Math::CubicBezier3D<Float> CubicBezier3Dd; typedef Math::CubicBezier3D<Float> CubicBezier3Dd;
/** @brief Double scalar cubic Hermite spline point */
typedef Math::CubicHermite1D<Double> CubicHermite1Dd;
/** @brief Double two-dimensional cubic Hermite spline point */
typedef Math::CubicHermite2D<Double> CubicHermite2Dd;
/** @brief Double three-dimensional cubic Hermite spline point */
typedef Math::CubicHermite3D<Double> CubicHermite3Dd;
/** @brief Double cubic Hermite spline complex number */
typedef Math::CubicHermiteComplex<Double> CubicHermiteComplexd;
/** @brief Double cubic Hermite spline quaternion */
typedef Math::CubicHermiteQuaternion<Double> CubicHermiteQuaterniond;
/** @brief Double complex number */ /** @brief Double complex number */
typedef Math::Complex<Double> Complexd; typedef Math::Complex<Double> Complexd;

1
src/Magnum/Math/CMakeLists.txt

@ -30,6 +30,7 @@ set(MagnumMath_HEADERS
Color.h Color.h
Complex.h Complex.h
Constants.h Constants.h
CubicHermite.h
Distance.h Distance.h
Dual.h Dual.h
DualComplex.h DualComplex.h

5
src/Magnum/Math/Complex.h

@ -469,7 +469,10 @@ Expects that both complex numbers are normalized. @f[
@f] @f]
@see @ref Complex::isNormalized(), @ref slerp(const Complex<T>&, const Complex<T>&, T), @see @ref Complex::isNormalized(), @ref slerp(const Complex<T>&, const Complex<T>&, T),
@ref lerp(const Quaternion<T>&, const Quaternion<T>&, T), @ref lerp(const Quaternion<T>&, const Quaternion<T>&, T),
@ref lerp(const T&, const T&, U) @ref lerp(const T&, const T&, U),
@ref lerp(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref lerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref lerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T)
*/ */
template<class T> inline Complex<T> lerp(const Complex<T>& normalizedA, const Complex<T>& normalizedB, T t) { template<class T> inline Complex<T> lerp(const Complex<T>& normalizedA, const Complex<T>& normalizedB, T t) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),

422
src/Magnum/Math/CubicHermite.h

@ -0,0 +1,422 @@
#ifndef Magnum_Math_CubicHermiteSpline_h
#define Magnum_Math_CubicHermiteSpline_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/** @file
* @brief Class @ref Magnum::Math::CubicHermite, alias @ref Magnum::Math::CubicHermite2D, @ref Magnum::Math::CubicHermite3D, function @ref Magnum::Math::select(), @ref Magnum::Math::lerp(), @ref Magnum::Math::splerp()
*/
#include "Magnum/Math/Complex.h"
#include "Magnum/Math/Quaternion.h"
namespace Magnum { namespace Math {
/**
@brief Cubic Hermite spline point
Represents a point on a [cubic Hermite spline](https://en.wikipedia.org/wiki/Cubic_Hermite_spline).
Unlike @ref Bezier, which describes a curve segment, this structure describes
a spline point @f$ \boldsymbol{p} @f$, with in-tangent @f$ \boldsymbol{m} @f$
and out-tangent @f$ \boldsymbol{n} @f$. This form is more suitable for
animation keyframe representation. The structure assumes the in/out tangents
to be in their final form, i.e. already normalized by length of their adjacent
segments.
@see @ref CubicHermite2D, @ref CubicHermite3D,
@ref Magnum::CubicHermite2D, @ref Magnum::CubicHermite2Dd,
@ref Magnum::CubicHermite3D, @ref Magnum::CubicHermite3Dd,
@ref CubicBezier
@experimental
*/
template<class T> class CubicHermite {
public:
typedef T Type; /**< @brief Underlying data type */
/**
* @brief Default constructor
*
* Equivalent to @ref CubicHermite(ZeroInitT) for vector types and to
* @ref CubicHermite(IdentityInitT) for complex and quaternion types.
*/
constexpr /*implicit*/ CubicHermite() noexcept: CubicHermite{typename std::conditional<std::is_constructible<T, IdentityInitT>::value, IdentityInitT, ZeroInitT>::type{typename std::conditional<std::is_constructible<T, IdentityInitT>::value, IdentityInitT, ZeroInitT>::type::Init{}}} {}
/**
* @brief Default constructor
*
* Construct cubic Hermite spline point with all control points being
* zero.
*/
constexpr explicit CubicHermite(ZeroInitT) noexcept: CubicHermite{ZeroInit, typename std::conditional<std::is_constructible<T, ZeroInitT>::value, ZeroInitT*, void*>::type{}} {}
/**
* @brief Identity constructor
*
* The @ref point() is constructed as identity in order to have
* interpolation working correctly; @ref inTangent() and
* @ref outTangent() is constructed as zero. Enabled only for complex
* and quaternion types.
*/
template<class U = T, class = typename std::enable_if<std::is_constructible<U, IdentityInitT>::value>::type> constexpr explicit CubicHermite(IdentityInitT) noexcept: _inTangent{ZeroInit}, _point{IdentityInit}, _outTangent{ZeroInit} {}
/** @brief Construct cubic Hermite spline point without initializing its contents */
explicit CubicHermite(NoInitT) noexcept: CubicHermite{NoInit, typename std::conditional<std::is_constructible<T, NoInitT>::value, NoInitT*, void*>::type{}} {}
/**
* @brief Construct cubic Hermite spline point with given control points
* @param inTangent In-tangent @f$ \boldsymbol{m} @f$
* @param point Point @f$ \boldsymbol{p} @f$
* @param outTangent Out-tangent @f$ \boldsymbol{n} @f$
*/
constexpr /*implicit*/ CubicHermite(const T& inTangent, const T& point, const T& outTangent) noexcept: _inTangent{inTangent}, _point{point}, _outTangent{outTangent} {}
/**
* @brief Construct subic Hermite spline point from another of different type
*
* Performs only default casting on the values, no rounding or
* anything else.
*/
template<class U> constexpr explicit CubicHermite(const CubicHermite<U>& other) noexcept: _inTangent{T(other._inTangent)}, _point{T(other._point)}, _outTangent{T(other._outTangent)} {}
/** @brief Equality comparison */
bool operator==(const CubicHermite<T>& other) const;
/** @brief Non-equality comparison */
bool operator!=(const CubicHermite<T>& other) const {
return !operator==(other);
}
/** @brief In-tangent @f$ \boldsymbol{m} @f$ */
T& inTangent() { return _inTangent; }
/* returns const& so [] operations are also constexpr */
constexpr const T& inTangent() const { return _inTangent; } /**< @overload */
/** @brief Point @f$ \boldsymbol{p} @f$ */
T& point() { return _point; }
/* returns const& so [] operations are also constexpr */
constexpr const T& point() const { return _point; } /**< @overload */
/** @brief Out-tangent @f$ \boldsymbol{n} @f$ */
T& outTangent() { return _outTangent; }
/* returns const& so [] operations are also constexpr */
constexpr const T& outTangent() const { return _outTangent; } /**< @overload */
private:
template<class> friend class CubicHermite;
/* Called from CubicHermite(ZeroInit), either using the ZeroInit
constructor (if available) or passing zero directly (for scalar
types) */
constexpr explicit CubicHermite(ZeroInitT, ZeroInitT*) noexcept: _inTangent{ZeroInit}, _point{ZeroInit}, _outTangent{ZeroInit} {}
constexpr explicit CubicHermite(ZeroInitT, void*) noexcept: _inTangent{T(0)}, _point{T(0)}, _outTangent{T(0)} {}
/* Called from CubicHermite(NoInit), either using the NoInit
constructor (if available) or not doing oanything */
explicit CubicHermite(NoInitT, NoInitT*) noexcept: _inTangent{NoInit}, _point{NoInit}, _outTangent{NoInit} {}
explicit CubicHermite(NoInitT, void*) noexcept {}
T _inTangent;
T _point;
T _outTangent;
};
/**
@brief Scalar cubic Hermite spline point
Convenience alternative to @cpp CubicHermite<T> @ce. See @ref CubicHermite for
more information.
@see @ref CubicHermite2D, @ref CubicHermite3D, @ref CubicHermiteComplex,
@ref CubicHermiteQuaternion, @ref Magnum::CubicHermite1D,
@ref Magnum::CubicHermite1Dd
*/
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */
template<class T> using CubicHermite1D = CubicHermite<T>;
#endif
/**
@brief Two-dimensional cubic Hermite spline point
Convenience alternative to @cpp CubicHermite<Vector2<T>> @ce. See
@ref CubicHermite for more information.
@see @ref CubicHermite1D, @ref CubicHermite3D, @ref CubicHermiteComplex,
@ref CubicHermiteQuaternion, @ref Magnum::CubicHermite2D,
@ref Magnum::CubicHermite2Dd
*/
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */
template<class T> using CubicHermite2D = CubicHermite<Vector2<T>>;
#endif
/**
@brief Three-dimensional cubic Hermite spline point
Convenience alternative to @cpp CubicHermite<Vector2<T>> @ce. See
@ref CubicHermite for more information.
@see @ref CubicHermite1D, @ref CubicHermite2D, @ref CubicHermiteComplex,
@ref CubicHermiteQuaternion, @ref Magnum::CubicHermite3D,
@ref Magnum::CubicHermite3Dd
*/
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */
template<class T> using CubicHermite3D = CubicHermite<Vector3<T>>;
#endif
/**
@brief Cubic Hermite spline complex number
Convenience alternative to @cpp CubicHermite<Complex<T>> @ce. See
@ref CubicHermite for more information.
@see @ref CubicHermite1D, @ref CubicHermite2D, @ref CubicHermite3D,
@ref CubicHermiteQuaternion, @ref Magnum::CubicHermiteComplex,
@ref Magnum::CubicHermiteComplexd
*/
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */
template<class T> using CubicHermiteComplex = CubicHermite<Complex<T>>;
#endif
/**
@brief Cubic Hermite spline quaternion
Convenience alternative to @cpp CubicHermite<Quaternion<T>> @ce. See
@ref CubicHermite for more information.
@see @ref CubicHermite1D, @ref CubicHermite2D, @ref CubicHermite3D,
@ref CubicHermiteComplex, @ref Magnum::CubicHermiteQuaternion,
@ref Magnum::CubicHermiteQuaterniond
*/
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */
template<class T> using CubicHermiteQuaternion = CubicHermite<Quaternion<T>>;
#endif
/** @debugoperator{CubicHermite} */
template<class T> Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const CubicHermite<T>& value) {
return debug << "CubicHermite(" << Corrade::Utility::Debug::nospace
<< value.inTangent() << Corrade::Utility::Debug::nospace << ","
<< value.point() << Corrade::Utility::Debug::nospace << ","
<< value.outTangent() << Corrade::Utility::Debug::nospace << ")";
}
/* Explicit instantiation for commonly used types */
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Float>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Double>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector2<Float>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector3<Float>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector4<Float>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector2<Double>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector3<Double>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector4<Double>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Complex<Float>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Complex<Double>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Quaternion<Float>>&);
extern template MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Quaternion<Double>>&);
#endif
/** @relatesalso CubicHermite
@brief Constant interpolation of two cubic Hermite spline points
@param a First value
@param b Second value
@param t Interpolation phase
Given segment points @f$ \boldsymbol{p}_i @f$, in-tangents @f$ \boldsymbol{m}_i @f$
and out-tangents @f$ \boldsymbol{n}_i @f$, the interpolated value @f$ \boldsymbol{p} @f$
at phase @f$ t @f$ is: @f[
\boldsymbol{p}(t) = \begin{cases}
\boldsymbol{p}_a, & t < 1 \\
\boldsymbol{p}_b, & t \ge 1
\end{cases}
@f]
Equivalent to calling @ref select(const T&, const T&, U) on
@ref CubicHermite::point() extracted from both @p a and @p b.
@see @ref lerp(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref splerp(const CubicHermite<T>&, const CubicHermite<T>&, U)
*/
template<class T, class U> T select(const CubicHermite<T>& a, const CubicHermite<T>& b, U t) {
/* Not using select() from Functions.h to avoid the header dependency */
return t < U(1) ? a.point() : b.point();
}
/** @relatesalso CubicHermite
@brief Linear interpolation of two cubic Hermite points
@param a First spline point
@param b Second spline point
@param t Interpolation phase
Given segment points @f$ \boldsymbol{p}_i @f$, in-tangents @f$ \boldsymbol{m}_i @f$
and out-tangents @f$ \boldsymbol{n}_i @f$, the interpolated value @f$ \boldsymbol{p} @f$
at phase @f$ t @f$ is: @f[
\boldsymbol{p}(t) = (1 - t) \boldsymbol{p}_a + t \boldsymbol{p}_b
@f]
Equivalent to calling @ref lerp(const T&, const T&, U) on
@ref CubicHermite::point() extracted from both @p a and @p b.
@see @ref lerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref lerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T),
@ref select(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref splerp(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref splerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref splerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T)
*/
template<class T, class U> T lerp(const CubicHermite<T>& a, const CubicHermite<T>& b, U t) {
/* To avoid dependency on Functions.h */
return Implementation::lerp(a.point(), b.point(), t);
}
/** @relatesalso CubicHermite
@brief Linear interpolation of two cubic Hermite complex numbers
Unlike @ref lerp(const CubicHermite<T>&, const CubicHermite<T>&, U) this adds
a normalization step after. Given segment points @f$ \boldsymbol{p}_i @f$,
in-tangents @f$ \boldsymbol{m}_i @f$ and out-tangents @f$ \boldsymbol{n}_i @f$,
the interpolated value @f$ \boldsymbol{p} @f$ at phase @f$ t @f$ is: @f[
\boldsymbol{p}(t) = \frac{(1 - t) \boldsymbol{p}_a + t \boldsymbol{p}_b}{|(1 - t) \boldsymbol{p}_a + t \boldsymbol{p}_b|}
@f]
Equivalent to calling @ref lerp(const Complex<T>&, const Complex<T>&, T) on
@ref CubicHermite::point() extracted from @p a and @p b. Expects that
@ref CubicHermite::point() is a normalized complex number in both @p a and @p b.
@see @ref Complex::isNormalized(),
@ref lerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T),
@ref select(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref splerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T)
*/
template<class T> Complex<T> lerp(const CubicHermiteComplex<T>& a, const CubicHermiteComplex<T>& b, T t) {
return lerp(a.point(), b.point(), t);
}
/** @relatesalso CubicHermite
@brief Linear interpolation of two cubic Hermite quaternions
Unlike @ref lerp(const CubicHermite<T>&, const CubicHermite<T>&, U) this adds a
normalization step after. Given segment points @f$ \boldsymbol{p}_i @f$,
in-tangents @f$ \boldsymbol{m}_i @f$ and out-tangents @f$ \boldsymbol{n}_i @f$,
the interpolated value @f$ \boldsymbol{p} @f$ at phase @f$ t @f$ is: @f[
\boldsymbol{p}(t) = \frac{(1 - t) \boldsymbol{p}_a + t \boldsymbol{p}_b}{|(1 - t) \boldsymbol{p}_a + t \boldsymbol{p}_b|}
@f]
Equivalent to calling @ref lerp(const Quaternion<T>&, const Quaternion<T>&, T)
on @ref CubicHermite::point() extracted from @p a and @p b. Expects that
@ref CubicHermite::point() is a normalized quaternion in both @p a and @p b.
@see @ref Quaternion::isNormalized(),
@ref lerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref select(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref splerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T)
*/
template<class T> Quaternion<T> lerp(const CubicHermiteQuaternion<T>& a, const CubicHermiteQuaternion<T>& b, T t) {
return lerp(a.point(), b.point(), t);
}
/** @relatesalso CubicHermite
@brief Spline interpolation of two cubic Hermite points
@param a First spline point
@param b Second spline point
@param t Interpolation phase
Given segment points @f$ \boldsymbol{p}_i @f$, in-tangents @f$ \boldsymbol{m}_i @f$
and out-tangents @f$ \boldsymbol{n}_i @f$, the interpolated value @f$ \boldsymbol{p} @f$
at phase @f$ t @f$ is: @f[
\boldsymbol{p}(t) = (2 t^3 - 3 t^2 + 1) \boldsymbol{p}_a + (t^3 - 2 t^2 + t) \boldsymbol{n}_a + (-2 t^3 + 3 t^2) \boldsymbol{p}_b + (t^3 - t^2)\boldsymbol{m}_b
@f]
@see @ref splerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref splerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T),
@ref select(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref lerp(const CubicHermite<T>&, const CubicHermite<T>&, U),
*/
template<class T, class U> T splerp(const CubicHermite<T>& a, const CubicHermite<T>& b, U t) {
return (U(2)*t*t*t - U(3)*t*t + U(1))*a.point() +
(t*t*t - U(2)*t*t + t)*a.outTangent() +
(U(-2)*t*t*t + U(3)*t*t)*b.point() +
(t*t*t - t*t)*b.inTangent();
}
/** @relatesalso CubicHermite
@brief Spline interpolation of two cubic Hermite complex numbers
Unlike @ref splerp(const CubicHermite<T>&, const CubicHermite<T>&, U) this adds
a normalization step after. Given segment points @f$ \boldsymbol{p}_i @f$,
in-tangents @f$ \boldsymbol{m}_i @f$ and out-tangents @f$ \boldsymbol{n}_i @f$,
the interpolated value @f$ \boldsymbol{p} @f$ at phase @f$ t @f$ is: @f[
\begin{array}{rcl}
\boldsymbol{p'}(t) & = & (2 t^3 - 3 t^2 + 1) \boldsymbol{p}_a + (t^3 - 2 t^2 + t) \boldsymbol{n}_a + (-2 t^3 + 3 t^2) \boldsymbol{p}_b + (t^3 - t^2)\boldsymbol{m}_b \\
\boldsymbol{p}(t) & = & \cfrac{\boldsymbol{p'}(t)}{|\boldsymbol{p'}(t)|}
\end{array}
@f]
Expects that @ref CubicHermite::point() is a normalized complex number in both
@p a and @p b.
@see @ref Complex::isNormalized(),
@ref splerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T),
@ref select(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref lerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T)
*/
template<class T> Complex<T> splerp(const CubicHermiteComplex<T>& a, const CubicHermiteComplex<T>& b, T t) {
CORRADE_ASSERT(a.point().isNormalized() && b.point().isNormalized(),
"Math::splerp(): complex spline points must be normalized", {});
return ((T(2)*t*t*t - T(3)*t*t + T(1))*a.point() +
(t*t*t - T(2)*t*t + t)*a.outTangent() +
(T(-2)*t*t*t + T(3)*t*t)*b.point() +
(t*t*t - t*t)*b.inTangent()).normalized();
}
/** @relatesalso CubicHermite
@brief Spline interpolation of two cubic Hermite quaternions
Unlike @ref splerp(const CubicHermite<T>&, const CubicHermite<T>&, U) this adds
a normalization step after. Given segment points @f$ \boldsymbol{p}_i @f$,
in-tangents @f$ \boldsymbol{m}_i @f$ and out-tangents @f$ \boldsymbol{n}_i @f$,
the interpolated value @f$ \boldsymbol{p} @f$ at phase @f$ t @f$ is: @f[
\begin{array}{rcl}
\boldsymbol{p'}(t) & = & (2 t^3 - 3 t^2 + 1) \boldsymbol{p}_a + (t^3 - 2 t^2 + t) \boldsymbol{n}_a + (-2 t^3 + 3 t^2) \boldsymbol{p}_b + (t^3 - t^2)\boldsymbol{m}_b \\
\boldsymbol{p}(t) & = & \cfrac{\boldsymbol{p'}(t)}{|\boldsymbol{p'}(t)|}
\end{array}
@f]
Expects that @ref CubicHermite::point() is a normalized quaternion in both @p a
and @p b.
@see @ref Quaternion::isNormalized(),
@ref splerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref select(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref lerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T)
*/
template<class T> Quaternion<T> splerp(const CubicHermiteQuaternion<T>& a, const CubicHermiteQuaternion<T>& b, T t) {
CORRADE_ASSERT(a.point().isNormalized() && b.point().isNormalized(),
"Math::splerp(): quaternion spline points must be normalized", {});
return ((T(2)*t*t*t - T(3)*t*t + T(1))*a.point() +
(t*t*t - T(2)*t*t + t)*a.outTangent() +
(T(-2)*t*t*t + T(3)*t*t)*b.point() +
(t*t*t - t*t)*b.inTangent()).normalized();
}
template<class T> inline bool CubicHermite<T>::operator==(const CubicHermite<T>& other) const {
/* For non-scalar types default implementation of TypeTraits would be used,
which is just operator== */
return TypeTraits<T>::equals(_inTangent, other._inTangent) &&
TypeTraits<T>::equals(_point, other._point) &&
TypeTraits<T>::equals(_outTangent, other._outTangent);
}
}}
#endif

8
src/Magnum/Math/Functions.h

@ -559,9 +559,13 @@ The interpolation for vectors is done as in following, similarly for scalars: @f
\boldsymbol{v_{LERP}} = (1 - t) \boldsymbol{v_A} + t \boldsymbol{v_B} \boldsymbol{v_{LERP}} = (1 - t) \boldsymbol{v_A} + t \boldsymbol{v_B}
@f] @f]
See @ref select() for constant interpolation using the same API. See @ref select() for constant interpolation using the same API and
@ref splerp() for spline interpolation.
@see @ref lerpInverted(), @ref lerp(const Complex<T>&, const Complex<T>&, T), @see @ref lerpInverted(), @ref lerp(const Complex<T>&, const Complex<T>&, T),
@ref lerp(const Quaternion<T>&, const Quaternion<T>&, T) @ref lerp(const Quaternion<T>&, const Quaternion<T>&, T),
@ref lerp(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref lerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref lerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T)
@m_keyword{mix(),GLSL mix(),} @m_keyword{mix(),GLSL mix(),}
*/ */
template<class T, class U> inline template<class T, class U> inline

23
src/Magnum/Math/Math.h

@ -43,14 +43,6 @@ template<std::size_t> class BoolVector;
/* Class Constants used only statically */ /* Class Constants used only statically */
template<UnsignedInt, UnsignedInt, class> class Bezier;
template<UnsignedInt dimensions, class T> using QuadraticBezier = Bezier<2, dimensions, T>;
template<UnsignedInt dimensions, class T> using CubicBezier = Bezier<3, dimensions, T>;
template<class T> using QuadraticBezier2D = QuadraticBezier<2, T>;
template<class T> using QuadraticBezier3D = QuadraticBezier<3, T>;
template<class T> using CubicBezier2D = CubicBezier<2, T>;
template<class T> using CubicBezier3D = CubicBezier<3, T>;
template<class> class Complex; template<class> class Complex;
template<class> class Dual; template<class> class Dual;
template<class> class DualComplex; template<class> class DualComplex;
@ -90,6 +82,21 @@ template<class> class Vector4;
template<class> class Color3; template<class> class Color3;
template<class> class Color4; template<class> class Color4;
template<UnsignedInt, UnsignedInt, class> class Bezier;
template<UnsignedInt dimensions, class T> using QuadraticBezier = Bezier<2, dimensions, T>;
template<UnsignedInt dimensions, class T> using CubicBezier = Bezier<3, dimensions, T>;
template<class T> using QuadraticBezier2D = QuadraticBezier<2, T>;
template<class T> using QuadraticBezier3D = QuadraticBezier<3, T>;
template<class T> using CubicBezier2D = CubicBezier<2, T>;
template<class T> using CubicBezier3D = CubicBezier<3, T>;
template<class> class CubicHermite;
template<class T> using CubicHermite1D = CubicHermite<T>;
template<class T> using CubicHermite2D = CubicHermite<Vector2<T>>;
template<class T> using CubicHermite3D = CubicHermite<Vector3<T>>;
template<class T> using CubicHermiteComplex = CubicHermite<Complex<T>>;
template<class T> using CubicHermiteQuaternion = CubicHermite<Quaternion<T>>;
template<UnsignedInt, class> class Range; template<UnsignedInt, class> class Range;
template<class T> using Range1D = Range<1, T>; template<class T> using Range1D = Range<1, T>;
template<class> class Range2D; template<class> class Range2D;

5
src/Magnum/Math/Quaternion.h

@ -90,7 +90,10 @@ Expects that both quaternions are normalized. @f[
@see @ref Quaternion::isNormalized(), @see @ref Quaternion::isNormalized(),
@ref slerp(const Quaternion<T>&, const Quaternion<T>&, T), @ref sclerp(), @ref slerp(const Quaternion<T>&, const Quaternion<T>&, T), @ref sclerp(),
@ref lerp(const T&, const T&, U), @ref lerp(const T&, const T&, U),
@ref lerp(const Complex<T>&, const Complex<T>&, T) @ref lerp(const Complex<T>&, const Complex<T>&, T),
@ref lerp(const CubicHermite<T>&, const CubicHermite<T>&, U),
@ref lerp(const CubicHermiteComplex<T>&, const CubicHermiteComplex<T>&, T),
@ref lerp(const CubicHermiteQuaternion<T>&, const CubicHermiteQuaternion<T>&, T)
*/ */
template<class T> inline Quaternion<T> lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) { template<class T> inline Quaternion<T> lerp(const Quaternion<T>& normalizedA, const Quaternion<T>& normalizedB, T t) {
CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(), CORRADE_ASSERT(normalizedA.isNormalized() && normalizedB.isNormalized(),

2
src/Magnum/Math/Test/CMakeLists.txt

@ -54,6 +54,7 @@ corrade_add_test(MathQuaternionTest QuaternionTest.cpp LIBRARIES MagnumMathTestL
corrade_add_test(MathDualQuaternionTest DualQuaternionTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathDualQuaternionTest DualQuaternionTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathBezierTest BezierTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathBezierTest BezierTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathCubicHermiteTest CubicHermiteTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathFrustumTest FrustumTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathFrustumTest FrustumTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathDistanceTest DistanceTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathDistanceTest DistanceTest.cpp LIBRARIES MagnumMathTestLib)
@ -66,6 +67,7 @@ set_property(TARGET
MathMatrix3Test MathMatrix3Test
MathMatrix4Test MathMatrix4Test
MathComplexTest MathComplexTest
MathCubicHermiteTest
MathDualComplexTest MathDualComplexTest
MathQuaternionTest MathQuaternionTest
MathDualQuaternionTest MathDualQuaternionTest

915
src/Magnum/Math/Test/CubicHermiteTest.cpp

@ -0,0 +1,915 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/TestSuite/Tester.h>
#include "Magnum/Math/CubicHermite.h"
#include "Magnum/Math/Functions.h"
#include "Magnum/Math/Vector2.h"
namespace Magnum { namespace Math { namespace Test {
struct CubicHermiteTest: Corrade::TestSuite::Tester {
explicit CubicHermiteTest();
void constructScalar();
void constructVector();
void constructComplex();
void constructQuaternion();
void constructDefaultScalar();
void constructDefaultVector();
void constructDefaultComplex();
void constructDefaultQuaternion();
void constructZeroScalar();
void constructZeroVector();
void constructZeroComplex();
void constructZeroQuaternion();
void constructIdentityScalar();
void constructIdentityVector();
void constructIdentityComplex();
void constructIdentityQuaternion();
void constructNoInitScalar();
void constructNoInitVector();
void constructNoInitComplex();
void constructNoInitQuaternion();
void constructConversionScalar();
void constructConversionVector();
void constructConversionComplex();
void constructConversionQuaternion();
void constructCopyScalar();
void constructCopyVector();
void constructCopyComplex();
void constructCopyQuaternion();
void dataScalar();
void dataVector();
void dataComplex();
void dataQuaternion();
void compareScalar();
void compareVector();
void compareComplex();
void compareQuaternion();
void selectScalar();
void selectVector();
void selectComplex();
void selectQuaternion();
void lerpScalar();
void lerpVector();
void lerpComplex();
void lerpComplexNotNormalized();
void lerpQuaternion();
void lerpQuaternionNotNormalized();
void splerpScalar();
void splerpVector();
void splerpComplex();
void splerpComplexNotNormalized();
void splerpQuaternion();
void splerpQuaternionNotNormalized();
void debugScalar();
void debugVector();
void debugComplex();
void debugQuaternion();
};
CubicHermiteTest::CubicHermiteTest() {
addTests({&CubicHermiteTest::constructScalar,
&CubicHermiteTest::constructVector,
&CubicHermiteTest::constructComplex,
&CubicHermiteTest::constructQuaternion,
&CubicHermiteTest::constructDefaultScalar,
&CubicHermiteTest::constructDefaultVector,
&CubicHermiteTest::constructDefaultComplex,
&CubicHermiteTest::constructDefaultQuaternion,
&CubicHermiteTest::constructZeroScalar,
&CubicHermiteTest::constructZeroVector,
&CubicHermiteTest::constructZeroComplex,
&CubicHermiteTest::constructZeroQuaternion,
&CubicHermiteTest::constructIdentityScalar,
&CubicHermiteTest::constructIdentityVector,
&CubicHermiteTest::constructIdentityComplex,
&CubicHermiteTest::constructIdentityQuaternion,
&CubicHermiteTest::constructNoInitScalar,
&CubicHermiteTest::constructNoInitVector,
&CubicHermiteTest::constructNoInitComplex,
&CubicHermiteTest::constructNoInitQuaternion,
&CubicHermiteTest::constructConversionScalar,
&CubicHermiteTest::constructConversionVector,
&CubicHermiteTest::constructConversionComplex,
&CubicHermiteTest::constructConversionQuaternion,
&CubicHermiteTest::constructCopyScalar,
&CubicHermiteTest::constructCopyVector,
&CubicHermiteTest::constructCopyComplex,
&CubicHermiteTest::constructCopyQuaternion,
&CubicHermiteTest::dataScalar,
&CubicHermiteTest::dataVector,
&CubicHermiteTest::dataComplex,
&CubicHermiteTest::dataQuaternion,
&CubicHermiteTest::compareScalar,
&CubicHermiteTest::compareVector,
&CubicHermiteTest::compareComplex,
&CubicHermiteTest::compareQuaternion,
&CubicHermiteTest::selectScalar,
&CubicHermiteTest::selectVector,
&CubicHermiteTest::selectComplex,
&CubicHermiteTest::selectQuaternion,
&CubicHermiteTest::lerpScalar,
&CubicHermiteTest::lerpVector,
&CubicHermiteTest::lerpComplex,
&CubicHermiteTest::lerpComplexNotNormalized,
&CubicHermiteTest::lerpQuaternion,
&CubicHermiteTest::lerpQuaternionNotNormalized,
&CubicHermiteTest::splerpScalar,
&CubicHermiteTest::splerpVector,
&CubicHermiteTest::splerpComplex,
&CubicHermiteTest::splerpComplexNotNormalized,
&CubicHermiteTest::splerpQuaternion,
&CubicHermiteTest::splerpQuaternionNotNormalized,
&CubicHermiteTest::debugScalar,
&CubicHermiteTest::debugVector,
&CubicHermiteTest::debugComplex,
&CubicHermiteTest::debugQuaternion});
}
typedef Math::Vector2<Float> Vector2;
typedef Math::Complex<Float> Complex;
typedef Math::Quaternion<Float> Quaternion;
typedef Math::CubicHermite1D<Float> CubicHermite1D;
typedef Math::CubicHermite2D<Float> CubicHermite2D;
typedef Math::CubicHermiteComplex<Float> CubicHermiteComplex;
typedef Math::CubicHermiteQuaternion<Float> CubicHermiteQuaternion;
void CubicHermiteTest::constructScalar() {
constexpr CubicHermite1D a{2.0f, -2.0f, -0.5f};
CubicHermite1D b{2.0f, -2.0f, -0.5f};
CORRADE_COMPARE(a.inTangent(), 2.0f);
CORRADE_COMPARE(b.inTangent(), 2.0f);
CORRADE_COMPARE(a.point(), -2.0f);
CORRADE_COMPARE(b.point(), -2.0f);
CORRADE_COMPARE(a.outTangent(), -0.5f);
CORRADE_COMPARE(b.outTangent(), -0.5f);
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite1D, Float, Float, Float>::value));
}
void CubicHermiteTest::constructVector() {
constexpr CubicHermite2D a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
CubicHermite2D b{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
CORRADE_COMPARE(a.inTangent(), (Vector2{1.0f, 2.0f}));
CORRADE_COMPARE(b.inTangent(), (Vector2{1.0f, 2.0f}));
CORRADE_COMPARE(a.point(), (Vector2{1.5f, -2.0f}));
CORRADE_COMPARE(b.point(), (Vector2{1.5f, -2.0f}));
CORRADE_COMPARE(a.outTangent(), (Vector2{3.0f, -0.5f}));
CORRADE_COMPARE(b.outTangent(), (Vector2{3.0f, -0.5f}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite2D, Vector2, Vector2, Vector2>::value));
}
void CubicHermiteTest::constructComplex() {
constexpr CubicHermiteComplex a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
CubicHermiteComplex b{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
CORRADE_COMPARE(a.inTangent(), (Complex{1.0f, 2.0f}));
CORRADE_COMPARE(b.inTangent(), (Complex{1.0f, 2.0f}));
CORRADE_COMPARE(a.point(), (Complex{1.5f, -2.0f}));
CORRADE_COMPARE(b.point(), (Complex{1.5f, -2.0f}));
CORRADE_COMPARE(a.outTangent(), (Complex{3.0f, -0.5f}));
CORRADE_COMPARE(b.outTangent(), (Complex{3.0f, -0.5f}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteComplex, Complex, Complex, Complex>::value));
}
void CubicHermiteTest::constructQuaternion() {
constexpr CubicHermiteQuaternion a{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}};
CubicHermiteQuaternion b{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}};
CORRADE_COMPARE(a.inTangent(), (Quaternion{{1.0f, 2.0f, -1.0f}, 3.0f}));
CORRADE_COMPARE(b.inTangent(), (Quaternion{{1.0f, 2.0f, -1.0f}, 3.0f}));
CORRADE_COMPARE(a.point(), (Quaternion{{1.5f, -2.0f, 0.1f}, 1.1f}));
CORRADE_COMPARE(b.point(), (Quaternion{{1.5f, -2.0f, 0.1f}, 1.1f}));
CORRADE_COMPARE(a.outTangent(), (Quaternion{{3.0f, -0.5f, 1.2f}, 0.3f}));
CORRADE_COMPARE(b.outTangent(), (Quaternion{{3.0f, -0.5f, 1.2f}, 0.3f}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteQuaternion, Quaternion, Quaternion, Quaternion>::value));
}
void CubicHermiteTest::constructDefaultScalar() {
constexpr CubicHermite1D a;
CubicHermite1D b;
/* Equivalent to ZeroInit constructor */
CORRADE_COMPARE(a, (CubicHermite1D{0.0f, 0.0f, 0.0f}));
CORRADE_COMPARE(b, (CubicHermite1D{0.0f, 0.0f, 0.0f}));
CORRADE_VERIFY((std::is_nothrow_default_constructible<CubicHermite1D>::value));
}
void CubicHermiteTest::constructDefaultVector() {
constexpr CubicHermite2D a;
CubicHermite2D b;
/* Equivalent to ZeroInit constructor */
CORRADE_COMPARE(a, (CubicHermite2D{{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermite2D{{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_default_constructible<CubicHermite2D>::value));
}
void CubicHermiteTest::constructDefaultComplex() {
constexpr CubicHermiteComplex a;
CubicHermiteComplex b;
/* Equivalent to IdentityInit constructor */
CORRADE_COMPARE(a, (CubicHermiteComplex{{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermiteComplex{{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_default_constructible<CubicHermiteComplex>::value));
}
void CubicHermiteTest::constructDefaultQuaternion() {
constexpr CubicHermiteQuaternion a;
CubicHermiteQuaternion b;
/* Equivalent to IdentityInit constructor */
CORRADE_COMPARE(a, (CubicHermiteQuaternion{
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 1.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermiteQuaternion{
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 1.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_default_constructible<CubicHermiteQuaternion>::value));
}
void CubicHermiteTest::constructZeroScalar() {
constexpr CubicHermite1D a{ZeroInit};
CubicHermite1D b{ZeroInit};
CORRADE_COMPARE(a, (CubicHermite1D{0.0f, 0.0f, 0.0f}));
CORRADE_COMPARE(b, (CubicHermite1D{0.0f, 0.0f, 0.0f}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite1D, ZeroInitT>::value));
}
void CubicHermiteTest::constructZeroVector() {
constexpr CubicHermite2D a{ZeroInit};
CubicHermite2D b{ZeroInit};
CORRADE_COMPARE(a, (CubicHermite2D{{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermite2D{{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite2D, ZeroInitT>::value));
}
void CubicHermiteTest::constructZeroComplex() {
constexpr CubicHermiteComplex a{ZeroInit};
CubicHermiteComplex b{ZeroInit};
CORRADE_COMPARE(a, (CubicHermiteComplex{{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermiteComplex{{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteComplex, ZeroInitT>::value));
}
void CubicHermiteTest::constructZeroQuaternion() {
constexpr CubicHermiteQuaternion a{ZeroInit};
CubicHermiteQuaternion b{ZeroInit};
CORRADE_COMPARE(a, (CubicHermiteQuaternion{
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermiteQuaternion{
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteQuaternion, ZeroInitT>::value));
}
void CubicHermiteTest::constructIdentityScalar() {
CORRADE_VERIFY(!(std::is_constructible<CubicHermite1D, IdentityInitT>::value));
}
void CubicHermiteTest::constructIdentityVector() {
CORRADE_VERIFY(!(std::is_constructible<CubicHermite2D, IdentityInitT>::value));
}
void CubicHermiteTest::constructIdentityComplex() {
constexpr CubicHermiteComplex a{IdentityInit};
CubicHermiteComplex b{IdentityInit};
CORRADE_COMPARE(a, (CubicHermiteComplex{{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermiteComplex{{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteComplex, IdentityInitT>::value));
}
void CubicHermiteTest::constructIdentityQuaternion() {
constexpr CubicHermiteQuaternion a{IdentityInit};
CubicHermiteQuaternion b{IdentityInit};
CORRADE_COMPARE(a, (CubicHermiteQuaternion{
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 1.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f}}));
CORRADE_COMPARE(b, (CubicHermiteQuaternion{
{{0.0f, 0.0f, 0.0f}, 0.0f},
{{0.0f, 0.0f, 0.0f}, 1.0f},
{{0.0f, 0.0f, 0.0f}, 0.0f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteComplex, IdentityInitT>::value));
}
void CubicHermiteTest::constructNoInitScalar() {
CubicHermite1D spline{2.0f, -2.0f, -0.5f};
new(&spline) CubicHermite1D{NoInit};
CORRADE_COMPARE(spline, (CubicHermite1D{2.0f, -2.0f, -0.5f}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite1D, NoInitT>::value));
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoInitT, CubicHermite1D>::value));
}
void CubicHermiteTest::constructNoInitVector() {
CubicHermite2D spline{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
new(&spline) CubicHermite2D{NoInit};
CORRADE_COMPARE(spline, (CubicHermite2D{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite2D, NoInitT>::value));
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoInitT, CubicHermite2D>::value));
}
void CubicHermiteTest::constructNoInitComplex() {
CubicHermiteComplex spline{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
new(&spline) CubicHermiteComplex{NoInit};
CORRADE_COMPARE(spline, (CubicHermiteComplex{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteComplex, NoInitT>::value));
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoInitT, CubicHermiteComplex>::value));
}
void CubicHermiteTest::constructNoInitQuaternion() {
CubicHermiteQuaternion spline{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}};
new(&spline) CubicHermiteQuaternion{NoInit};
CORRADE_COMPARE(spline, (CubicHermiteQuaternion{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}}));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteQuaternion, NoInitT>::value));
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoInitT, CubicHermiteQuaternion>::value));
}
void CubicHermiteTest::constructConversionScalar() {
typedef Math::CubicHermite1D<Int> CubicHermite1Di;
constexpr CubicHermite1D a{2.0f, -2.0f, -0.5f};
constexpr CubicHermite1Di b{a};
CubicHermite1Di c{a};
CORRADE_COMPARE(b, (CubicHermite1Di{2, -2, 0}));
CORRADE_COMPARE(c, (CubicHermite1Di{2, -2, 0}));
/* Implicit conversion is not allowed */
CORRADE_VERIFY(!(std::is_convertible<CubicHermite1D, CubicHermite1Di>::value));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite1D, CubicHermite1Di>::value));
}
void CubicHermiteTest::constructConversionVector() {
typedef Math::CubicHermite2D<Int> CubicHermite2Di;
constexpr CubicHermite2D a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
constexpr CubicHermite2Di b{a};
CubicHermite2Di c{a};
CORRADE_COMPARE(b, (CubicHermite2Di{{1, 2}, {1, -2}, {3, 0}}));
CORRADE_COMPARE(c, (CubicHermite2Di{{1, 2}, {1, -2}, {3, 0}}));
/* Implicit conversion is not allowed */
CORRADE_VERIFY(!(std::is_convertible<CubicHermite2D, CubicHermite2Di>::value));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermite2D, CubicHermite2Di>::value));
}
void CubicHermiteTest::constructConversionComplex() {
typedef Math::CubicHermiteComplex<Int> CubicHermiteComplexi;
constexpr CubicHermiteComplex a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
constexpr CubicHermiteComplexi b{a};
CubicHermiteComplexi c{a};
CORRADE_COMPARE(b, (CubicHermiteComplexi{{1, 2}, {1, -2}, {3, 0}}));
CORRADE_COMPARE(c, (CubicHermiteComplexi{{1, 2}, {1, -2}, {3, 0}}));
/* Implicit conversion is not allowed */
CORRADE_VERIFY(!(std::is_convertible<CubicHermiteComplex, CubicHermiteComplexi>::value));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteComplex, CubicHermiteComplexi>::value));
}
void CubicHermiteTest::constructConversionQuaternion() {
typedef Math::CubicHermiteQuaternion<Int> CubicHermiteQuaternioni;
constexpr CubicHermiteQuaternion a{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}};
constexpr CubicHermiteQuaternioni b{a};
CubicHermiteQuaternioni c{a};
CORRADE_COMPARE(b, (CubicHermiteQuaternioni{
{{1, 2, -1}, 3},
{{1, -2, 0}, 1},
{{3, 0, 1}, 0}}));
CORRADE_COMPARE(c, (CubicHermiteQuaternioni{
{{1, 2, -1}, 3},
{{1, -2, 0}, 1},
{{3, 0, 1}, 0}}));
/* Implicit conversion is not allowed */
CORRADE_VERIFY(!(std::is_convertible<CubicHermiteQuaternion, CubicHermiteQuaternioni>::value));
CORRADE_VERIFY((std::is_nothrow_constructible<CubicHermiteQuaternion, CubicHermiteQuaternioni>::value));
}
void CubicHermiteTest::constructCopyScalar() {
constexpr CubicHermite1D a{2.0f, -2.0f, -0.5f};
constexpr CubicHermite1D b{a};
CORRADE_COMPARE(b, (CubicHermite1D{2.0f, -2.0f, -0.5f}));
CORRADE_VERIFY(std::is_nothrow_copy_constructible<CubicHermite1D>::value);
CORRADE_VERIFY(std::is_nothrow_copy_assignable<CubicHermite1D>::value);
}
void CubicHermiteTest::constructCopyVector() {
constexpr CubicHermite2D a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
constexpr CubicHermite2D b{a};
CORRADE_COMPARE(b, (CubicHermite2D{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}}));
CORRADE_VERIFY(std::is_nothrow_copy_constructible<CubicHermite2D>::value);
CORRADE_VERIFY(std::is_nothrow_copy_assignable<CubicHermite2D>::value);
}
void CubicHermiteTest::constructCopyComplex() {
constexpr CubicHermiteComplex a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
constexpr CubicHermiteComplex b{a};
CORRADE_COMPARE(b, (CubicHermiteComplex{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}}));
CORRADE_VERIFY(std::is_nothrow_copy_constructible<CubicHermiteComplex>::value);
CORRADE_VERIFY(std::is_nothrow_copy_assignable<CubicHermiteComplex>::value);
}
void CubicHermiteTest::constructCopyQuaternion() {
constexpr CubicHermiteQuaternion a{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}};
constexpr CubicHermiteQuaternion b{a};
CORRADE_COMPARE(b, (CubicHermiteQuaternion{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}}));
CORRADE_VERIFY(std::is_nothrow_copy_constructible<CubicHermiteComplex>::value);
CORRADE_VERIFY(std::is_nothrow_copy_assignable<CubicHermiteComplex>::value);
}
void CubicHermiteTest::dataScalar() {
constexpr CubicHermite1D ca{2.0f, -2.0f, -0.5f};
constexpr Float inTangent = ca.inTangent();
constexpr Float point = ca.point();
constexpr Float outTangent = ca.outTangent();
CORRADE_COMPARE(inTangent, 2.0f);
CORRADE_COMPARE(point, -2.0f);
CORRADE_COMPARE(outTangent, -0.5f);
CubicHermite1D a{2.0f, -2.0f, -0.5f};
a.inTangent() = 3.0f;
a.point() = 1.0f;
a.outTangent() = 2.0f;
CORRADE_COMPARE(a, (CubicHermite1D{3.0f, 1.0f, 2.0f}));
}
void CubicHermiteTest::dataVector() {
constexpr CubicHermite2D ca{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
constexpr Vector2 inTangent = ca.inTangent();
constexpr Vector2 point = ca.point();
constexpr Vector2 outTangent = ca.outTangent();
CORRADE_COMPARE(inTangent, (Vector2{1.0f, 2.0f}));
CORRADE_COMPARE(point, (Vector2{1.5f, -2.0f}));
CORRADE_COMPARE(outTangent, (Vector2{3.0f, -0.5f}));
CubicHermite2D a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
a.inTangent().y() = 3.0f;
a.point().x() = 1.0f;
a.outTangent().y() = 2.0f;
CORRADE_COMPARE(a, (CubicHermite2D{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}));
}
void CubicHermiteTest::dataComplex() {
constexpr CubicHermiteComplex ca{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
constexpr Complex inTangent = ca.inTangent();
constexpr Complex point = ca.point();
constexpr Complex outTangent = ca.outTangent();
CORRADE_COMPARE(inTangent, (Complex{1.0f, 2.0f}));
CORRADE_COMPARE(point, (Complex{1.5f, -2.0f}));
CORRADE_COMPARE(outTangent, (Complex{3.0f, -0.5f}));
CubicHermiteComplex a{{1.0f, 2.0f}, {1.5f, -2.0f}, {3.0f, -0.5f}};
a.inTangent().imaginary() = 3.0f;
a.point().real() = 1.0f;
a.outTangent().imaginary() = 2.0f;
CORRADE_COMPARE(a, (CubicHermiteComplex{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}));
}
void CubicHermiteTest::dataQuaternion() {
constexpr CubicHermiteQuaternion ca{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}};
constexpr Quaternion inTangent = ca.inTangent();
constexpr Quaternion point = ca.point();
constexpr Quaternion outTangent = ca.outTangent();
CORRADE_COMPARE(inTangent, (Quaternion{{1.0f, 2.0f, -1.0f}, 3.0f}));
CORRADE_COMPARE(point, (Quaternion{{1.5f, -2.0f, 0.1f}, 1.1f}));
CORRADE_COMPARE(outTangent, (Quaternion{{3.0f, -0.5f, 1.2f}, 0.3f}));
CubicHermiteQuaternion a{
{{1.0f, 2.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.1f},
{{3.0f, -0.5f, 1.2f}, 0.3f}};
a.inTangent().vector().y() = 3.0f;
a.point().scalar() = 1.0f;
a.outTangent().vector().z() = 2.0f;
CORRADE_COMPARE(a, (CubicHermiteQuaternion{
{{1.0f, 3.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.0f},
{{3.0f, -0.5f, 2.0f}, 0.3f}}));
}
void CubicHermiteTest::compareScalar() {
CORRADE_VERIFY((CubicHermite1D{3.0f, 1.0f, 2.0f} == CubicHermite1D{3.0f, 1.0f + TypeTraits<Float>::epsilon()/2, 2.0f}));
CORRADE_VERIFY((CubicHermite1D{3.0f, 1.0f, 2.0f} != CubicHermite1D{3.0f + TypeTraits<Float>::epsilon()*6, 1.0f, 2.0f}));
}
void CubicHermiteTest::compareVector() {
CORRADE_VERIFY((CubicHermite2D{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}) == (CubicHermite2D{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f + TypeTraits<Float>::epsilon()/2}}));
CORRADE_VERIFY((CubicHermite2D{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}) != (CubicHermite2D{{1.0f + TypeTraits<Float>::epsilon()*2, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}));
}
void CubicHermiteTest::compareComplex() {
CORRADE_VERIFY((CubicHermiteComplex{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}) == (CubicHermiteComplex{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f + TypeTraits<Float>::epsilon()/2}}));
CORRADE_VERIFY((CubicHermiteComplex{{1.0f, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}) != (CubicHermiteComplex{{1.0f + TypeTraits<Float>::epsilon()*2, 3.0f}, {1.0f, -2.0f}, {3.0f, 2.0f}}));
}
void CubicHermiteTest::compareQuaternion() {
CORRADE_VERIFY((CubicHermiteQuaternion{
{{1.0f, 3.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.0f},
{{3.0f, -0.5f, 2.0f}, 0.3f}}) == (CubicHermiteQuaternion{
{{1.0f, 3.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.0f + TypeTraits<Float>::epsilon()/2},
{{3.0f, -0.5f, 2.0f}, 0.3f}}));
CORRADE_VERIFY((CubicHermiteQuaternion{
{{1.0f, 3.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.0f},
{{3.0f, -0.5f, 2.0f}, 0.3f}}) != (CubicHermiteQuaternion{
{{1.0f + TypeTraits<Float>::epsilon()*2, 3.0f, -1.0f}, 3.0f},
{{1.5f, -2.0f, 0.1f}, 1.0f},
{{3.0f, -0.5f, 2.0f}, 0.3f}}));
}
void CubicHermiteTest::selectScalar() {
CubicHermite1D a{2.0f, 3.0f, -1.0f};
CubicHermite1D b{5.0f, -2.0f, 1.5f};
CORRADE_COMPARE(Math::select(a, b, 0.0f), 3.0f);
CORRADE_COMPARE(Math::select(a, b, 0.8f), 3.0f);
CORRADE_COMPARE(Math::select(a, b, 1.0f), -2.0f);
}
void CubicHermiteTest::selectVector() {
CubicHermite2D a{{2.0f, 1.5f}, {3.0f, 0.1f}, {-1.0f, 0.0f}};
CubicHermite2D b{{5.0f, 0.3f}, {-2.0f, 1.1f}, {1.5f, 0.3f}};
CORRADE_COMPARE(Math::select(a, b, 0.0f), (Vector2{3.0f, 0.1f}));
CORRADE_COMPARE(Math::select(a, b, 0.8f), (Vector2{3.0f, 0.1f}));
CORRADE_COMPARE(Math::select(a, b, 1.0f), (Vector2{-2.0f, 1.1f}));
}
void CubicHermiteTest::selectComplex() {
CubicHermiteComplex a{{2.0f, 1.5f}, {3.0f, 0.1f}, {-1.0f, 0.0f}};
CubicHermiteComplex b{{5.0f, 0.3f}, {-2.0f, 1.1f}, {1.5f, 0.3f}};
CORRADE_COMPARE(Math::select(a, b, 0.0f), (Complex{3.0f, 0.1f}));
CORRADE_COMPARE(Math::select(a, b, 0.8f), (Complex{3.0f, 0.1f}));
CORRADE_COMPARE(Math::select(a, b, 1.0f), (Complex{-2.0f, 1.1f}));
}
void CubicHermiteTest::selectQuaternion() {
CubicHermiteQuaternion a{
{{2.0f, 1.5f, 0.3f}, 1.1f},
{{3.0f, 0.1f, 2.3f}, 0.7f},
{{-1.0f, 0.0f, 0.3f}, 0.4f}};
CubicHermiteQuaternion b{
{{5.0f, 0.3f, 1.1f}, 0.5f},
{{-2.0f, 1.1f, 1.0f}, 1.3f},
{{1.5f, 0.3f, 17.0f}, -7.0f}};
CORRADE_COMPARE(Math::select(a, b, 0.0f), (Quaternion{{3.0f, 0.1f, 2.3f}, 0.7f}));
CORRADE_COMPARE(Math::select(a, b, 0.8f), (Quaternion{{3.0f, 0.1f, 2.3f}, 0.7f}));
CORRADE_COMPARE(Math::select(a, b, 1.0f), (Quaternion{{-2.0f, 1.1f, 1.0f}, 1.3f}));
}
void CubicHermiteTest::lerpScalar() {
CubicHermite1D a{2.0f, 3.0f, -1.0f};
CubicHermite1D b{5.0f, -2.0f, 1.5f};
CORRADE_COMPARE(Math::lerp(a, b, 0.0f), 3.0f);
CORRADE_COMPARE(Math::lerp(a, b, 1.0f), -2.0f);
CORRADE_COMPARE(Math::lerp(a, b, 0.35f), 1.25f);
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.35f), 1.25f);
CORRADE_COMPARE(Math::lerp(a, b, 0.8f), -1.0f);
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.8f), -1.0f);
}
void CubicHermiteTest::lerpVector() {
CubicHermite2D a{{2.0f, 1.5f}, {3.0f, 0.1f}, {-1.0f, 0.0f}};
CubicHermite2D b{{5.0f, 0.3f}, {-2.0f, 1.1f}, {1.5f, 0.3f}};
CORRADE_COMPARE(Math::lerp(a, b, 0.0f), (Vector2{3.0f, 0.1f}));
CORRADE_COMPARE(Math::lerp(a, b, 1.0f), (Vector2{-2.0f, 1.1f}));
CORRADE_COMPARE(Math::lerp(a, b, 0.35f), (Vector2{1.25f, 0.45f}));
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.35f), (Vector2{1.25f, 0.45f}));
CORRADE_COMPARE(Math::lerp(a, b, 0.8f), (Vector2{-1.0f, 0.9f}));
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.8f), (Vector2{-1.0f, 0.9f}));
}
void CubicHermiteTest::lerpComplex() {
CubicHermiteComplex a{{2.0f, 1.5f}, {0.999445f, 0.0333148f}, {-1.0f, 0.0f}};
CubicHermiteComplex b{{5.0f, 0.3f}, {-0.876216f, 0.481919f}, {1.5f, 0.3f}};
CORRADE_COMPARE(Math::lerp(a, b, 0.0f), (Complex{0.999445f, 0.0333148f}));
CORRADE_COMPARE(Math::lerp(a, b, 1.0f), (Complex{-0.876216f, 0.481919f}));
CORRADE_COMPARE(Math::lerp(a, b, 0.35f), (Complex{0.874384f, 0.485235f}));
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.35f), (Complex{0.874384f, 0.485235f}));
CORRADE_VERIFY(Math::lerp(a, b, 0.35f).isNormalized());
CORRADE_COMPARE(Math::lerp(a, b, 0.8f), (Complex{-0.78747f, 0.616353f}));
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.8f), (Complex{-0.78747f, 0.616353f}));
CORRADE_VERIFY(Math::lerp(a, b, 0.8f).isNormalized());
}
void CubicHermiteTest::lerpComplexNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
/* This one should not assert as the default constructor should create
identity point */
CORRADE_COMPARE(Math::lerp(CubicHermiteComplex{}, {}, 0.3f), Complex{});
/* These will, tho */
CubicHermiteComplex a{{}, Complex{}*2.0f, {}};
Math::lerp({}, a, 0.3f);
Math::lerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::lerp(): complex numbers must be normalized\n"
"Math::lerp(): complex numbers must be normalized\n");
}
void CubicHermiteTest::lerpQuaternion() {
CubicHermiteQuaternion a{
{{2.0f, 1.5f, 0.3f}, 1.1f},
{{0.780076f, 0.0260025f, 0.598059f}, 0.182018f},
{{-1.0f, 0.0f, 0.3f}, 0.4f}};
CubicHermiteQuaternion b{
{{5.0f, 0.3f, 1.1f}, 0.5f},
{{-0.711568f, 0.391362f, 0.355784f}, 0.462519f},
{{1.5f, 0.3f, 17.0f}, -7.0f}};
CORRADE_COMPARE(Math::lerp(a, b, 0.0f), (Quaternion{{0.780076f, 0.0260025f, 0.598059f}, 0.182018f}));
CORRADE_COMPARE(Math::lerp(a, b, 1.0f), (Quaternion{{-0.711568f, 0.391362f, 0.355784f}, 0.462519f}));
CORRADE_COMPARE(Math::lerp(a, b, 0.35f), (Quaternion{{0.392449f, 0.234067f, 0.780733f}, 0.426207f}));
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.35f), (Quaternion{{0.392449f, 0.234067f, 0.780733f}, 0.426207f}));
CORRADE_VERIFY(Math::lerp(a, b, 0.35f).isNormalized());
CORRADE_COMPARE(Math::lerp(a, b, 0.8f), (Quaternion{{-0.533196f, 0.410685f, 0.521583f}, 0.524396f}));
CORRADE_COMPARE(Math::lerp(a.point(), b.point(), 0.8f), (Quaternion{{-0.533196f, 0.410685f, 0.521583f}, 0.524396f}));
CORRADE_VERIFY(Math::lerp(a, b, 0.8f).isNormalized());
}
void CubicHermiteTest::lerpQuaternionNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
/* This one should not assert as the default constructor should create
identity point */
Math::lerp(CubicHermiteQuaternion{}, {}, 0.3f);
/* These will, tho */
CubicHermiteQuaternion a{{}, Quaternion{}*2.0f, {}};
Math::lerp({}, a, 0.3f);
Math::lerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::lerp(): quaternions must be normalized\n"
"Math::lerp(): quaternions must be normalized\n");
}
void CubicHermiteTest::splerpScalar() {
CubicHermite1D a{2.0f, 3.0f, -1.0f};
CubicHermite1D b{5.0f, -2.0f, 1.5f};
CORRADE_COMPARE(Math::splerp(a, b, 0.0f), 3.0f);
CORRADE_COMPARE(Math::splerp(a, b, 1.0f), -2.0f);
CORRADE_COMPARE(Math::splerp(a, b, 0.35f), 1.04525f);
CORRADE_COMPARE(Math::splerp(a, b, 0.8f), -2.152f);
}
void CubicHermiteTest::splerpVector() {
CubicHermite2D a{{2.0f, 1.5f}, {3.0f, 0.1f}, {-1.0f, 0.0f}};
CubicHermite2D b{{5.0f, 0.3f}, {-2.0f, 1.1f}, {1.5f, 0.3f}};
CORRADE_COMPARE(Math::splerp(a, b, 0.0f), (Vector2{3.0f, 0.1f}));
CORRADE_COMPARE(Math::splerp(a, b, 1.0f), (Vector2{-2.0f, 1.1f}));
CORRADE_COMPARE(Math::splerp(a, b, 0.35f), (Vector2{1.04525f, 0.357862f}));
CORRADE_COMPARE(Math::splerp(a, b, 0.8f), (Vector2{-2.152f, 0.9576f}));
}
void CubicHermiteTest::splerpComplex() {
CubicHermiteComplex a{{2.0f, 1.5f}, {0.999445f, 0.0333148f}, {-1.0f, 0.0f}};
CubicHermiteComplex b{{5.0f, 0.3f}, {-0.876216f, 0.481919f}, {1.5f, 0.3f}};
CORRADE_COMPARE(Math::splerp(a, b, 0.0f), (Complex{0.999445f, 0.0333148f}));
CORRADE_COMPARE(Math::splerp(a, b, 1.0f), (Complex{-0.876216f, 0.481919f}));
CORRADE_COMPARE(Math::splerp(a, b, 0.35f), (Complex{-0.483504f, 0.875342f}));
CORRADE_VERIFY(Math::splerp(a, b, 0.35f).isNormalized());
CORRADE_COMPARE(Math::splerp(a, b, 0.8f), (Complex{-0.95958f, 0.281435f}));
CORRADE_VERIFY(Math::splerp(a, b, 0.8f).isNormalized());
}
void CubicHermiteTest::splerpComplexNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
/* This one should not assert as the default constructor should create
identity point */
CORRADE_COMPARE(Math::splerp(CubicHermiteComplex{}, {}, 0.3f), Complex{});
/* These will, tho */
CubicHermiteComplex a{{}, Complex{}*2.0f, {}};
Math::splerp({}, a, 0.3f);
Math::splerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::splerp(): complex spline points must be normalized\n"
"Math::splerp(): complex spline points must be normalized\n");
}
void CubicHermiteTest::splerpQuaternion() {
CubicHermiteQuaternion a{
{{2.0f, 1.5f, 0.3f}, 1.1f},
{{0.780076f, 0.0260025f, 0.598059f}, 0.182018f},
{{-1.0f, 0.0f, 0.3f}, 0.4f}};
CubicHermiteQuaternion b{
{{5.0f, 0.3f, 1.1f}, 0.5f},
{{-0.711568f, 0.391362f, 0.355784f}, 0.462519f},
{{1.5f, 0.3f, 17.0f}, -7.0f}};
CORRADE_COMPARE(Math::splerp(a, b, 0.0f), (Quaternion{{0.780076f, 0.0260025f, 0.598059f}, 0.182018f}));
CORRADE_COMPARE(Math::splerp(a, b, 1.0f), (Quaternion{{-0.711568f, 0.391362f, 0.355784f}, 0.462519f}));
CORRADE_COMPARE(Math::splerp(a, b, 0.35f), (Quaternion{{-0.309862f, 0.174831f, 0.809747f}, 0.466615f}));
CORRADE_VERIFY(Math::splerp(a, b, 0.35f).isNormalized());
CORRADE_COMPARE(Math::splerp(a, b, 0.8f), (Quaternion{{-0.911408f, 0.23368f, 0.185318f}, 0.283524f}));
CORRADE_VERIFY(Math::splerp(a, b, 0.8f).isNormalized());
}
void CubicHermiteTest::splerpQuaternionNotNormalized() {
std::ostringstream out;
Error redirectError{&out};
/* This one should not assert as the default constructor should create
identity point */
Math::splerp(CubicHermiteQuaternion{}, {}, 0.3f);
/* These will, tho */
CubicHermiteQuaternion a{{}, Quaternion{}*2.0f, {}};
Math::splerp({}, a, 0.3f);
Math::splerp(a, {}, 0.3f);
CORRADE_COMPARE(out.str(),
"Math::splerp(): quaternion spline points must be normalized\n"
"Math::splerp(): quaternion spline points must be normalized\n");
}
void CubicHermiteTest::debugScalar() {
std::ostringstream out;
Debug{&out} << CubicHermite1D{2.0f, 3.0f, -1.0f};
CORRADE_COMPARE(out.str(), "CubicHermite(2, 3, -1)\n");
}
void CubicHermiteTest::debugVector() {
std::ostringstream out;
Debug{&out} << CubicHermite2D{{2.0f, 1.5f}, {3.0f, 0.1f}, {-1.0f, 0.0f}};
CORRADE_COMPARE(out.str(), "CubicHermite(Vector(2, 1.5), Vector(3, 0.1), Vector(-1, 0))\n");
}
void CubicHermiteTest::debugComplex() {
std::ostringstream out;
Debug{&out} << CubicHermiteComplex{{2.0f, 1.5f}, {3.0f, 0.1f}, {-1.0f, 0.0f}};
CORRADE_COMPARE(out.str(), "CubicHermite(Complex(2, 1.5), Complex(3, 0.1), Complex(-1, 0))\n");
}
void CubicHermiteTest::debugQuaternion() {
std::ostringstream out;
Debug{&out} << CubicHermiteQuaternion{
{{2.0f, 1.5f, 0.3f}, 1.1f},
{{3.0f, 0.1f, 2.3f}, 0.7f},
{{-1.0f, 0.0f, 0.3f}, 0.4f}};
CORRADE_COMPARE(out.str(), "CubicHermite(Quaternion({2, 1.5, 0.3}, 1.1), Quaternion({3, 0.1, 2.3}, 0.7), Quaternion({-1, 0, 0.3}, 0.4))\n");
}
}}}
CORRADE_TEST_MAIN(Magnum::Math::Test::CubicHermiteTest)

12
src/Magnum/Math/instantiation.cpp

@ -24,6 +24,7 @@
*/ */
#include "Magnum/Math/Bezier.h" #include "Magnum/Math/Bezier.h"
#include "Magnum/Math/CubicHermite.h"
#include "Magnum/Math/DualComplex.h" #include "Magnum/Math/DualComplex.h"
#include "Magnum/Math/DualQuaternion.h" #include "Magnum/Math/DualQuaternion.h"
#include "Magnum/Math/Frustum.h" #include "Magnum/Math/Frustum.h"
@ -101,6 +102,17 @@ template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Bez
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Bezier<3, 2, Double>&); template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Bezier<3, 2, Double>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Bezier<3, 3, Double>&); template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Bezier<3, 3, Double>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Float>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Double>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector2<Float>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector3<Float>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector2<Double>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Vector3<Double>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Complex<Float>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Complex<Double>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Quaternion<Float>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const CubicHermite<Quaternion<Double>>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Complex<Float>&); template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Complex<Float>&);
template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Complex<Double>&); template Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug&, const Complex<Double>&);

Loading…
Cancel
Save