The Harlinn.Math library contains a set of optimized linear algebra classes and functions for games and other graphic intensive apps. The operations are implemented using a blend of SIMD and regular C/C++, performing as well as, and sometimes outperforming, the DirectXMath library.

This is a raytraced image generated by pbrto.

kroken

pbrto is the main example/demonstration of how to use the library to improve the performance of a complex, computationally intensive, app.

The classes and functions are designed to make it easier to implement, understand and maintain efficient solutions, and the Benchmarks shows that this can be done without loss of performance.

Harlinn/Math/VectorMath.h is header only.

This document and related pages are under construction.

Concepts

Classes and Templates

Functions

Introduction

The code for the mathematical classes and functions lives inside the Harlinn::Math namespace, and all that is needed to use this part of the library is to include the Harlinn/Math/VectorMath.h header file:

#include <Harlinn/Math/VectorMath.h>

and you are ready to code.

In Game Programming Gems, Sam Melax describes a function for calculating the shortest arc quaternion between to vectors that can be implemented like this:


using namespace Harlinn
using namespace Harlinn::Math;

namespace Basics
{
    using Quaternion = Math::Quaternion<float>;

    Quaternion ShortestArc1( const Vector3f& fromDir, 
                                    const Vector3f& toDir ) noexcept
    {
        using Constants = Vector3f::Traits::Constants::Base;

        Vector3f fromDirNormalized = Normalize( fromDir );
        Vector3f toDirNormalized = Normalize( toDir );

        const float dot = ScalarDot( fromDirNormalized, toDirNormalized );
        if ( dot >= 1.f )
        {
            return Quaternion::Identity( );
        }
        else if ( dot <= -1.f )
        {
            Vector3f axis = Cross( fromDirNormalized, Vector3f::Right( ) );
            if ( AreNearlyEqual( ScalarLengthSquared( axis ), 0.f, 
                                                Constants::EpsilonValue ) )
            {
                axis = Cross( fromDirNormalized, Vector3f::Up() );
            }

            return Quaternion::FromAxisAndAngle( axis, Constants::Pi );
        }
        else
        {
            const float s = Sqrt( ( 1.f + dot ) * 2.f );
            Vector3f cp = Cross( fromDirNormalized, toDirNormalized ) / s;
            Quaternion result( cp, s * 0.5f );
            return result;
        }
    }
}

No surprises here, the above looks like something that could easily be done using most 3D math libraries.

Currently, this implementation is as fast as Quaternion::FromToRotation from DirectXTK12, which is cool:

BenchmarkShortestArc1                                29.8 ns         29.3 ns     22400000
BenchmarkDirectXTK12QuaternionFromToRotation         30.5 ns         30.0 ns     21333333

A better performing version would look like:

    Quaternion ShortestArc2( const Vector3f::Simd& fromDir, 
                                    const Vector3f::Simd& toDir ) noexcept
    {
        using Constants = Vector3f::Traits::Constants::Base;

        auto fromDirNormalized = Normalize( fromDir );
        auto toDirNormalized = Normalize( toDir );

        const auto dot = Dot( fromDirNormalized, toDirNormalized );
        const auto dotf = dot[ 0 ];
        if ( dotf >= 1.f )
        {
            return Quaternion::Identity( );
        }
        else if ( dotf <= -1.f )
        {
            auto axis = Cross( fromDirNormalized, Vector3f::Right( ) );
            if ( AreNearlyEqual( ScalarLengthSquared( axis ), 0.f, 
                                                Constants::EpsilonValue ) )
            {
                axis = Cross( fromDirNormalized, Vector3f::Up( ) );
            }

            return Quaternion::FromAxisAndAngle( axis, Constants::Pi );
        }
        else
        {
            const auto s = Sqrt( ( 1.f + dot ) * 2.f );
            auto cp = Cross( fromDirNormalized, toDirNormalized ) / s;
            Quaternion result( cp, s[0] * 0.5f );
            return result;
        }
    }

which looks almost identical to ShortestArc1, but the benchmark:

BenchmarkShortestArc2                                14.6 ns         14.6 ns     44800000

shows that ShortestArc2 is more than twice as fast as ShortestArc1. Like ShortestArc2, ShortestArc1 also does most of its calculations using functions that are implemented using SIMD, but ShortestArc2 is smarter about how it does that.

The short explanation is that ShortestArc2 avoids a bunch of calls to _mm_load_ps and _mm_store_ps, and their sibling intrinsic functions, and here is why:

    Quaternion ShortestArc2( const Vector3f::Simd& fromDir, 
                                    const Vector3f::Simd& toDir ) noexcept
    {
        using Constants = Vector3f::Traits::Constants::Base;

Since Normalize returns a Vector3f::Simd object, and using this for the calculations eliminates calls to _mm_load_ps and _mm_store_ps, we replace Vector3f with auto in the code.

        auto fromDirNormalized = Normalize( fromDir );
        auto toDirNormalized = Normalize( toDir );

Next we replace the call to ScalarDot( fromDirNormalized, toDirNormalized ) that returns the dot product as a scalar, with Dot( fromDirNormalized, toDirNormalized ) which returns a Vector3f::Simd object.

        const auto dot = Dot( fromDirNormalized, toDirNormalized );

The Vector3f::Simd type doesn’t have member variables for x, y and z, as it uses a member variable simd of type __m128 to store its data. x, y and z occupies the first, second and third position of the __m128, respectively. Dot sets the first, second and third position of the __m128 to the value of the dot product between the argument vectors. Vector3f::Simd provides access to the individual values through its float x( ) const noexcept, float y( ) const noexcept and float z( ) const noexcept member functions, and we need this value to be able to handle possible singularities.

        const auto dotf = dot.x( );

With that out of the way, we replace the remaining Vector3f declarations with auto:

        if ( dotf >= 1.f )
        {
            return Quaternion::Identity( );
        }
        else if ( dotf <= -1.f )
        {
            auto axis = Cross( fromDirNormalized, Vector3f::Right( ) );
            if ( AreNearlyEqual( ScalarLengthSquared( axis ), 0.f, 
                                                Constants::EpsilonValue ) )
            {
                axis = Cross( fromDirNormalized, Vector3f::Up( ) );
            }

            return Quaternion::FromAxisAndAngle( axis, Constants::Pi );
        }
        else
        {

The next line of code is interesting, as diving into the details about what’s happening explains a lot about how the library works.

The expression 1.f + dot is handled by an operator + overload that widens 1.f to a __m128 value with 1.f in the three first positions before doing vector addition with the simd variable of dot, returning the result as a Vector3f::Simd object. The result then gets passed to an operator + overload that widens 2.f into __m128 value with 2.f in the three first positions before doing vector multiplication with the result of 1.f + dot, returning the result as Vector3f::Simd object. This is then passed to an overload of Sqrt that calculates the square root for each of the elements in the result of ( 1.f + dot ) * 2.f, returning a Vector3f::Simd object which is stored in s.

            const auto s = Sqrt( ( 1.f + dot ) * 2.f );

So s is now a Vector3f::Simd object, with relevant values in its first three positions, and naturally Cross also returns a Vector3f::Simd object. The result of the call to Cross and s gets handled by an operator / overload dividing each of the elements of the first by its corresponding element in s, again returning a Vector3f::Simd object which is assigned to cp.

            auto cp = Cross( fromDirNormalized, toDirNormalized ) / s;
            Quaternion result( cp, s[0] * 0.5f );
            return result;
        }
    }

ShortestArc2 is as readable as ShortestArc1, and most of the increased performance was achieved by replacing Vector3f with auto, which is good C++ practice, anyway.

Could it be even faster? Sure:

BenchmarkShortestArc                                 11.1 ns         11.2 ns     56000000

This benchmark, BenchmarkShortestArc, is for the ShortestArc implementation found in Harlinn/Math/VectorMath.h.

Implementation Details

The following operations are currently supported for Tuple2, Tuple3 and Tuple4:

+, -, unary -, *, /, +=, -=, *=, /=, Abs, Min, Max, Sqr, Ceil, Floor, Round, Trunc, Lerp, Saturate, Sqrt, FMA, FMSub, Sin, Cos, Tan, ASin, ACos, ATan, ATan2, SinH, CosH, TanH, ASinH, ACosH, ATanH, Log, Log1P, Log10, Log2, Exp, Exp10, Exp2, ExpM1, Pow, Dot, Hypot, Permute, Cross, LengthSquared, Length Normalize, ReciprocalLength, DistanceSquared, Distance, HProd HSum, DifferenceOfProducts, SumOfProducts

Tuple2, Tuple3, Tuple4 and TupleSimd Functions

The functions that work with the Tuple2, Tuple3, Tuple4 and TupleSimd types generally returns a TupleSimd holding the result of the operation.

There are usually several overloads for each function, allowing the values held by the Tuple2, Tuple3 and Tuple4 derived types to be automatically loaded into a SIMD type/register.

Functions that calculate a single scalar value, returns a TupleSimd where every element holds the calculated value. These functions also have an implementation that returns the scalar as single value. For instance, Dot returns a TupleSimd, while ScalarDot returns a floating point value of the same type as a single element from the TupleSimd type.

HSum and ScalarHSum

Calculates the horizontal sum of the elements in the vector.

template<Internal::SimdType T>
inline T HSum( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
inline ResultT ScalarHSum( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT HSum( const T& t ) noexcept;

HProd and ScalarHProd

Calculates the horizontal product of the elements in the vector.

template<Internal::SimdType T>
inline T HProd( const T& t ) noexcept;

template<Internal::SimdType T>
inline auto ScalarHProd( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
inline ResultT ScalarHProd( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT HProd( const T& t ) noexcept;

Abs

Computes the absolute value of each element held by the argument.

template<Internal::SimdType T>
inline T Abs( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Abs( const T& t ) noexcept;

Min

Makes a comparison between the elements held by the two arguments, and returns a TupleSimd containing the smallest elements.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Min( const T& lhs, const U& rhs ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Min( const T& lhs, const U& rhs ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Min( const U& lhs, const T& rhs ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, 
                typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Min( const T& lhs, const U& rhs ) noexcept;

Max

Makes a comparison between the elements held by the two arguments, and returns a TupleSimd containing the largest elements.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Max( const T& lhs, const U& rhs ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Max( const T& lhs, const U& rhs ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Max( const U& lhs, const T& rhs ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, 
                typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Max( const T& lhs, const U& rhs ) noexcept;

Sqr

Computes the square value of each element held by the argument.

template<Internal::SimdType T>
inline T Sqr( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Sqr( const T& t ) noexcept;

Ceil

Computes the ceiling of each element held by the argument.

template<Internal::SimdType T>
inline T Ceil( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Ceil( const T& t ) noexcept;

Floor

Computes the floor of each element held by the argument.

template<Internal::SimdType T>
inline T Floor( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Floor( const T& t ) noexcept;

Round

Rounds each element held by the argument towards the nearest even integer.

template<Internal::SimdType T>
inline T Round( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Round( const T& t ) noexcept;

Trunc

Rounds each element held by the argument to the nearest integer in the direction of zero.

template<Internal::SimdType T>
inline T Trunc( const T& t ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Trunc( const T& t ) noexcept;

Lerp

Calculates the linear interpolation between the the elements of a and the elements of b, for elements of c is inside [0,1), or the linear extrapolation for elements in c outside [0,1).

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT> && Internal::IsCompatible<T, U> 
inline T Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT Lerp( NumberT a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Lerp( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT Lerp( const S& a, const T& b, const U& c ) noexcept;

Clamp

Returns the elements of v, if the elements are between their respective boundaries specified the elements of lowerBounds and the elements of upperBounds, otherwise the value of nearest boundary is returned.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, 
                                        typename ResultT = typename S::Simd>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT Clamp( const S& v, const T& lowerBounds, const U& upperBounds ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                        typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT> && Internal::IsCompatible<T, U>
inline ResultT Clamp( NumberT v, const T& lowerBounds, const U& upperBounds ) noexcept;

Saturate

Saturates the elements of v to the range 0.0 to 1.0.

template<Internal::SimdType T>
inline T Saturate( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Saturate( const T& v ) noexcept;

Sqrt

Calculates the square root of each element in the argument.

template<Internal::SimdType T>
inline T Sqrt( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Sqrt( const T& v ) noexcept;

ReciprocalSqrt

Calculates the reciprocal square root of each element in the argument.

template<Internal::SimdType T>
inline T ReciprocalSqrt( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ReciprocalSqrt( const T& v ) noexcept;

Reciprocal

Calculates the reciprocal of each element in the argument.

template<Internal::SimdType T>
inline T Reciprocal( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Reciprocal( const T& v ) noexcept;

FMA

Multiplies the corresponding elements of a and b, adding the result to the corresponding element of c.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMA( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMA( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMA( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, 
                                    typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMA( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMA( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, 
                                        typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMA( const S& a, const T& b, const U& c ) noexcept;

FMSub

Performs a set of multiply-subtract computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and the infinite precision intermediate results are obtained. From the infinite precision intermediate results, the values in the third operand, c, are subtracted. The final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSub( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMSub( const S& a, const T& b, const U& c ) noexcept;

FMAddSub

Performs a set of multiply-add-subtract computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and infinite precision intermediate results are obtained. The odd values in the third operand, c, are added to the intermediate results while the even values are subtracted from them. The final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMAddSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMAddSub( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMAddSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMAddSub( const S& a, const T& b, const U& c ) noexcept;

FMSubAdd

Performs a set of multiply-subtract-add computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and infinite precision intermediate results are obtained. The odd values in the third operand, c, are subtracted from the intermediate results while the even values are added to them. The final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSubAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FMSubAdd( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FMSubAdd( const S& a, const T& b, const U& c ) noexcept;

FNMAdd

Performs a set of negated multiply-add computation on a, b, and c. Corresponding values in two operands, a and b, are multiplied and the negated infinite precision intermediate results are added to the values in the third operand, c, after which the final results are rounded to the nearest floating point values.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMAdd( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMAdd( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMAdd( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FNMAdd( const S& a, const T& b, const U& c ) noexcept;

FNMSub

Performs a set of negated multiply-subtract computation on a, b, and c. The values in two operands, a and b, are multiplied and the negated infinite precision intermediate result is obtained. From this negated intermediate result, the value in the third operand, c, is subtracted. The final result is rounded to the nearest floating point value.

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMSub( NumberT a, const T& b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::SimdType T, Internal::TupleType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline T FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::SimdType U>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline U FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<typename NumberT, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires std::is_arithmetic_v<NumberT>&& Internal::IsCompatible<T, U>
inline ResultT FNMSub( const T& a, NumberT b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline S FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline T FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline U FNMSub( const S& a, const T& b, const U& c ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd >
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<T, U>
inline ResultT FNMSub( const S& a, const T& b, const U& c ) noexcept;

Sin

Calculates the sine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T Sin( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Sin( const T& v ) noexcept;

Cos

Calculates the cosine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T Cos( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Cos( const T& v ) noexcept;

Tan

Calculates the tangent of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T Tan( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Tan( const T& v ) noexcept;

ASin

Calculates the inverse sine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ASin( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ASin( const T& v ) noexcept;

ACos

Calculates the inverse cosine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ACos( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ACos( const T& v ) noexcept;

ATan

Calculates the inverse tangent of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ATan( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ATan( const T& v ) noexcept;

ATan2

Calculates the inverse tangent of each element in x divided by the corresponding element in y, in radians.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T ATan2( const T& x, const U& y ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T ATan2( const T& x, const U& y ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T ATan2( const U& x, const T& y ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT ATan2( const T& x, const U& y ) noexcept;

ModAngles

Calculates the angle modulo \(2\pi\) of each element in the argument.

template<Internal::SimdType T>
inline T ModAngles( const T& angles );

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ModAngles( const T& v ) noexcept;

AddAngles

Adds the angles in the corresponding elements of v1 and v2. The argument angles must be in the range \([-\pi,\pi)\), and the computed angles will be in the range \([-\pi,\pi)\).

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T AddAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T AddAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T AddAngles( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT AddAngles( const T& v1, const U& v2 ) noexcept;

SubtractAngles

Subtracts the angles in v2 from the corresponding elements of v1. The argument angles must be in the range \([-\pi,\pi)\), and the computed angles will be in the range \([-\pi,\pi)\)

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T SubtractAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T SubtractAngles( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T SubtractAngles( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT SubtractAngles( const T& v1, const U& v2 ) noexcept;

SinH

Calculates the hyperbolic sine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T SinH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT SinH( const T& v ) noexcept;

CosH

Calculates the hyperbolic cosine of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T CosH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT CosH( const T& v ) noexcept;

TanH

Calculates the hyperbolic tangent of each element in the argument expressed in radians.

template<Internal::SimdType T>
inline T TanH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT TanH( const T& v ) noexcept;

ASinH

Calculates the inverse hyperbolic sine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ASinH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ASinH( const T& v ) noexcept;

ACosH

Calculates the inverse hyperbolic cosine of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ACosH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ACosH( const T& v ) noexcept;

ATanH

Calculates the inverse hyperbolic tangent of each element in the argument, in radians.

template<Internal::SimdType T>
inline T ATanH( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ATanH( const T& v ) noexcept;

Log

Calculates the natural logarithm of each element in the argument.

template<Internal::SimdType T>
inline T Log( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log( const T& v ) noexcept;

Log1P

Calculates the natural logarithm of \(1 +\) each element in the argument.

template<Internal::SimdType T>
inline T Log1P( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log1P( const T& v ) noexcept;

Log10

Calculates the base-10 logarithm, \(log_{10}\), of each element in the argument.

template<Internal::SimdType T>
inline T Log10( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log10( const T& v ) noexcept;

Log2

Calculates the base-2 logarithm, \(log_{2}\), of each element in the argument.

template<Internal::SimdType T>
inline T Log2( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Log2( const T& v ) noexcept;

Exp

Calculates \(e\) (Euler’s number, 2.7182818…), raised to the power of each element in the argument.

template<Internal::SimdType T>
inline T Exp( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Exp( const T& v ) noexcept;

Exp10

Calculates the base-10 exponential of each element in the argument.

template<Internal::SimdType T>
inline T Exp10( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Exp10( const T& v ) noexcept;

Exp2

Calculates the base-2 exponential of each element in the argument.

template<Internal::SimdType T>
inline T Exp2( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Exp2( const T& v ) noexcept;

ExpM1

Calculates \(e\) (Euler’s number, 2.7182818…), raised to the power of each element in the argument, \(-1.0\).

template<Internal::SimdType T>
inline T ExpM1( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ExpM1( const T& v ) noexcept;

Pow

Calculates the elements in base raised to the corresponding elements in exponent.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Pow( const T& base, const U& exponent ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Pow( const T& base, const U& exponent ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Pow( const U& base, const T& exponent ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Pow( const T& base, const U& exponent ) noexcept;

Hypot

Calculates the square root of the sum of the squares of each corresponding element in x and y, without undue overflow or underflow at intermediate stages of the computation.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Hypot( const T& x, const U& y ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Hypot( const T& x, const U& y ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Hypot( const U& x, const T& y ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Hypot( const T& x, const U& y ) noexcept;

Hermite

Calculates the Hermite spline interpolation, using the specified arguments.


template<Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<T, U> && 
            Internal::IsCompatible<T, V> && 
            Internal::IsCompatible<T, W>
inline T Hermite( const T& firstPosition, const U& firstTangent, 
                    const V& secondPosition, const W& secondTangent, 
                    typename T::value_type t ) noexcept;

Dot and ScalarDot

Calculates the dot product between v1 and v2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Dot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Dot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Dot( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Dot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
constexpr inline typename T::value_type ScalarDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
constexpr inline typename T::value_type ScalarDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
constexpr inline typename T::value_type ScalarDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::value_type>
    requires Internal::IsCompatible<T, U>
constexpr inline ResultT ScalarDot( const T& v1, const U& v2 ) noexcept;

AbsDot and ScalarAbsDot

Calculates the absolute value of the dot product between v1 and v2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T AbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T AbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T AbsDot( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT AbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarAbsDot( const T& v1, const U& v2 ) noexcept;

Cross

Calculates the cross product between v1 and v2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Cross( const T& v1, const U& v2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Cross( const T& v1, const U& v2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Cross( const U& v1, const T& v2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Cross( const T& v1, const U& v2 ) noexcept;

LengthSquared and ScalarLengthSquared

Calculates the squared length of v.

template<Internal::SimdType T>
inline T LengthSquared( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT LengthSquared( const T& v ) noexcept;

template<Internal::SimdType T>
inline auto ScalarLengthSquared( const T& v ) noexcept;

template<Internal::TupleType T>
inline auto ScalarLengthSquared( const T& v ) noexcept;

Length and ScalarLength

Calculates the length of v.

template<Internal::SimdType T>
inline T Length( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Length( const T& v ) noexcept;

template<Internal::SimdType T>
inline auto ScalarLength( const T& v ) noexcept;

template<Internal::TupleType T>
inline auto ScalarLength( const T& v ) noexcept;

Normalize

Normalizes v.

template<Internal::SimdType T>
inline T Normalize( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT Normalize( const T& v ) noexcept;

ReciprocalLength and ScalarReciprocalLength

Calculates the reciprocal length of v.

template<Internal::SimdType T>
inline T ReciprocalLength( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::Simd>
inline ResultT ReciprocalLength( const T& v ) noexcept;

template<Internal::SimdType T>
inline auto ScalarReciprocalLength( const T& v ) noexcept;

template<Internal::TupleType T>
inline auto ScalarReciprocalLength( const T& v ) noexcept;

DistanceSquared and ScalarDistanceSquared

Calculates the squared distance between p1 and p2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T DistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T DistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T DistanceSquared( const U& p1, const T& p2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT DistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const U& p1, const T& p2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistanceSquared( const T& p1, const U& p2 ) noexcept;

Distance and ScalarDistance

Calculates the distance between p1 and p2.

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline T Distance( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline T Distance( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline T Distance( const U& p1, const T& p2 ) noexcept;

template<Internal::TupleType T, Internal::TupleType U, typename ResultT = typename T::Simd>
    requires Internal::IsCompatible<T, U>
inline ResultT Distance( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistance( const T& p1, const U& p2 ) noexcept;

template<Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistance( const T& p1, const U& p2 ) noexcept;

template<Internal::TupleType U, Internal::SimdType T>
    requires Internal::IsCompatible<T, U>
inline auto ScalarDistance( const U& p1, const T& p2 ) noexcept;

InBounds

Detects if the elements of a vector are within bounds.

template<Internal::SimdType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline S InBounds( const S& v, const T& bounds ) noexcept;

template<Internal::SimdType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline S InBounds( const S& v, const T& bounds ) noexcept;

template<Internal::TupleType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline T InBounds( const S& v, const T& bounds ) noexcept;

template<Internal::TupleType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline typename S::Simd InBounds( const S& v, const T& bounds ) noexcept;

ClampLength

Clamps the length of a vector to a given range.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd ClampLength( const S& v, const T& lengthMin, const U& lengthMax ) noexcept;

template<Internal::SimdType S, typename T, typename U>
    requires IsFloatingPoint<T> && IsFloatingPoint<U>
inline S ClampLength( const S& v, const T lengthMin, const U lengthMax ) noexcept;

template<Internal::TupleType S, typename T, typename U>
    requires IsFloatingPoint<T>&& IsFloatingPoint<U>
inline S ClampLength( const S& v, const T lengthMin, const U lengthMax ) noexcept;

Reflect

Reflects an incident vector across a normal vector.

template<Internal::SimdType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline S Reflect( const S& incident, const T& normal ) noexcept;

template<Internal::SimdType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline S Reflect( const S& incident, const T& normal ) noexcept;

template<Internal::TupleType S, Internal::SimdType T>
    requires Internal::IsCompatible<S, T>
inline T Reflect( const S& incident, const T& normal ) noexcept;

template<Internal::TupleType S, Internal::TupleType T>
    requires Internal::IsCompatible<S, T>
inline typename S::Simd Reflect( const S& incident, const T& normal ) noexcept;

Refract

Refracts an incident vector across a normal vector.


template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline S Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U& refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline S Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline S Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, typename U>
    requires Internal::IsCompatible<S, T>&& IsFloatingPoint<U>
inline typename S::Simd Refract( const S& incident, const T& normal, const U refractionIndex ) noexcept;

Orthogonal

Computes a vector perpendicular to the argument vector.

template<Internal::SimdType S>
inline S Orthogonal( const S& v ) noexcept;

template<Internal::TupleType S>
inline typename S::Simd Orthogonal( const S& v ) noexcept;

DifferenceOfProducts

Calculates the difference between the product of the first and the second argument, and the product of the third and fourth argument.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U> && Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline V DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline typename S::Simd DifferenceOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

SumOfProducts

Calculates the sum of the product of the first and the second argument, and the product of the third and fourth argument.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U> && Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline S SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline T SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline U SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline V SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>
inline typename S::Simd SumOfProducts( const S& v1, const T& v2, const U& v3, const V& v4 ) noexcept;

BaryCentric

Calculates a point in Barycentric coordinates, using the specified triangle.

see https://en.wikipedia.org/wiki/Barycentric_coordinate_system

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U> && Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V& f, const W& g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T> && Internal::IsCompatible<S, U> && IsFloatingPoint<V> && IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, typename V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& IsFloatingPoint<V>&& IsFloatingPoint<W>
inline typename S::Simd BaryCentric( const S& p1, const T& p2, const U& p3, const V f, const W g ) noexcept;

CatmullRom

Calculates the Catmull-Rom interpolation, using the specified positions.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::SimdType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, Internal::TupleType W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& Internal::IsCompatible<S, W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W& t ) noexcept;


template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::SimdType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline S CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::SimdType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::SimdType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::SimdType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

template<Internal::TupleType S, Internal::TupleType T, Internal::TupleType U, Internal::TupleType V, typename W>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U>&& Internal::IsCompatible<S, V>&& IsFloatingPoint<W>
inline typename S::Simd CatmullRom( const S& p1, const T& p2, const U& p3, const V& p4, const W t ) noexcept;

MinComponentValue

Retrieves the lowest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MinComponentValue( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MinComponentValue( const T& v ) noexcept;

MaxComponentValue

Retrieves the highest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MaxComponentValue( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MaxComponentValue( const T& v ) noexcept;

MinComponentIndex

Retrieves the offset of the lowest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MinComponentIndex( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MinComponentIndex( const T& v ) noexcept;

MaxComponentIndex

Retrieves the offset of the highest value held by the argument.

template<Internal::SimdType T, typename ResultT = typename T::value_type >
inline ResultT MaxComponentIndex( const T& v ) noexcept;

template<Internal::TupleType T, typename ResultT = typename T::value_type>
constexpr inline ResultT MaxComponentIndex( const T& v ) noexcept;

Vector2f, Vector3f, Vector4f, Vector2i, Vector3i, Vector4i

Vector<float,2>, Vector<float,3> and Vector<float,4> are specializations of Vector<T,N> that supports a wider repertoire of operations than the general Vector<T,N>.

Vector<float,2> is derived from the Tuple2 template, Vector<float,3> is derived from the Tuple3, and Vector<float,4> is derived from Tuple4.

Just a few lines of code are required to create the Vector<float, 3> specialization of Vector<T,N> with the full repertoire of features available for Tuple3:

template<>
class Vector<float, 3> : public Tuple3<Vector<float, 3>,float>
{
public:
    using Base = Tuple3<Vector<float, 3>, float>;
    using Traits = Base::Traits;

    Vector( ) noexcept = default;
    explicit Vector( float v ) noexcept
        : Base( v, v, v )
    { }
    Vector( float xv, float yv, float zv ) noexcept
        : Base( xv, yv, zv )
    { }

    template<typename T>
        requires std::is_same_v<typename T::SIMDType, typename Traits::SIMDType >
    Vector( const T& other ) noexcept
        : Base( other )
    { }
};

Below are two benchmarks, the first using Math::Vector<float, 3>, while the second uses pbrt::Vector3f.

static void BenchmarkVector3( benchmark::State& state )
{
    using namespace Harlinn::Math;
    using Vector = Math::Vector<float, 3>;
    DoubleGenerator.Reset( );

    for ( auto _ : state )
    {
        Vector v1( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        Vector v2( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        benchmark::DoNotOptimize( Dot( Abs( Max( Ceil( -v1 ), Floor( v2 ) ) ), v2 ) );
    }
}
BENCHMARK( BenchmarkVector3 );

static void BenchmarkPBRTVector3f( benchmark::State& state )
{
    using namespace pbrt;
    using Vector = pbrt::Vector3f;
    DoubleGenerator.Reset( );


    for ( auto _ : state )
    {
        Vector v1( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        Vector v2( FloatGenerator( ), FloatGenerator( ), FloatGenerator( ) );
        benchmark::DoNotOptimize( Dot( Abs( Max( Ceil( -v1 ), Floor( v2 ) ) ), v2 ) );
    }
}
BENCHMARK( BenchmarkPBRTVector3f );

BenchmarkVector2 runs 20 % faster than BenchmarkPBRTVector3f which is optimized by the compiler for the AVX2 instruction set:

-------------------------------------------------------------------
Benchmark                         Time             CPU   Iterations
-------------------------------------------------------------------
BenchmarkVector3               5.63 ns         4.85 ns    186666667
BenchmarkPBRTVector3f          7.08 ns         5.86 ns    112000000

Vector functions

AngleBetween

Calculates the angle in radians between two vectors.

AngleBetweenNormals

Calculates the angle in radians between two normalized vectors.

Transform

Transforms a 3D vector by a matrix.

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>& matrix );

Normal3f and Normal3i

The surface normal is a vector perpendicular to a surface at a specific position. It can be defined as the cross product of any two nonparallel vectors that are tangent to the surface at a point. Normals are similar to vectors, but it’s important to distinguish between the two of them: since normals are defined in terms of their relationship to a surface, they behave differently than vectors in some situations, particularly when applying transformations.

Normal3f functions

Transform

Transforms the Normal3f object by the given matrix.

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 3>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 3>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 3>& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& v, 
                                const SquareMatrix<float, 4>& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 3>& matrix );

inline Normal3f::Simd Transform( const Normal3f& v, 
                                const SquareMatrix<float, 4>& matrix );

Point3f and Point3i

A point is a zero-dimensional location in 2D or 3D space. The Point2i/f and Point3i/f classes represent points in the obvious way: using \(x, y, z\) (in 3D) coordinates with respect to a coordinate system. Although the same representation is used for vectors, the fact that a point represents a position whereas a vector represents a direction leads to a number of important differences in how they are treated.

Point3f

LinePointDistance

Calculates the minimum distance between a line and a point.

Transform

Transforms the Point3f object by the provided transformation matrix.


inline Point3f::Simd Transform( const Point3f::Simd& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f& v, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f::Simd& v, 
                                const SquareMatrix<float, 4>& matrix );

inline Point3f::Simd Transform( const Point3f& v, 
                                const SquareMatrix<float, 4>& matrix );

SquareMatrix

The SquareMatrix template class is used to represent transformations that can be applied to vectors, points and surface normals.

The template is designed as a row oriented matrix, that can be instantiated for single and double precision floating point types. The template can be instantiated as a \(2 x 2\), \(3 x 3\) or \(4 x 4\) matrix.

Internally, computations on \(2 x 2\) matrices are performed using a single SIMD type/register, while operations on \(3 x 3\) and \(4 x 4\) matrices are performed using one SIMD type/register for each row.

SquareMatrix support matrix addition, scalar addition, matrix multiplication, scalar multiplication, the matrices can be transposed using Transpose and inverted using Inverse, and Determinant calculates the determinant. The default constructor creates an identity matrix, so SquareMatrix handles the usual operations associated with small square matrices.

SquareMatrix<float,4> functions

Transpose

Calculates the transpose of the matrix.

inline SquareMatrix<float, 4>::Simd Transpose( const SquareMatrix<float, 4>::Simd& matrix );

inline SquareMatrix<float, 4>::Simd Transpose( const SquareMatrix<float, 4>& matrix );

Inverse

Calculates the inverse of the matrix.

inline SquareMatrix<float, 4>::Simd Inverse( const SquareMatrix<float, 4>::Simd& matrix, 
                                            typename Vector<float, 4>::Simd* determinant = nullptr );

inline typename SquareMatrix<float, 4>::Simd Inverse( const SquareMatrix<float, 4>& matrix, 
                                            typename Vector<float, 4>::Simd* determinant = nullptr );

Determinant and ScalarDeterminant

Calculates the determinant of a matrix.

inline typename Vector<float,4>::Simd Determinant( const typename SquareMatrix<float, 4>::Simd& matrix );

inline typename Vector<float, 4>::Simd Determinant( const SquareMatrix<float, 4>& matrix );

inline float ScalarDeterminant( const SquareMatrix<float, 4>::Simd& matrix );

inline float ScalarDeterminant( const SquareMatrix<float, 4>& matrix );

Translation

Creates a translation matrix using the provided offsets.

inline SquareMatrix<float, 4>::Simd Translation( float offsetX, float offsetY, float offsetZ );

template<Internal::SimdType S>
    requires (S::Size > 2) && std::is_same_v<typename S::value_type, float>
inline SquareMatrix<float, 4>::Simd Translation( const S& offsets );

template<Internal::TupleType S>
    requires ( S::Size > 2 ) && std::is_same_v<typename S::value_type, float>
inline SquareMatrix<float, 4>::Simd Translation( const S& offsets );

Scaling

Creates a transformation matrix for scaling along the x-axis, y-axis, and z-axis.

inline SquareMatrix<float, 4>::Simd Scaling( float scaleX, float scaleY, float scaleZ );

template<Internal::SimdType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd Scaling( const S& v ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd Scaling( const S& v ) noexcept;

Rotation

Creates a transformation matrix that rotates about the y-axis, then the x-axis, and finally the z-axis.

template<Internal::SimdType S>
    requires (S::Size > 2)
inline SquareMatrix<float, 4>::Simd Rotation( const S& v ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd Rotation( const S& v ) noexcept;

inline SquareMatrix<float, 4>::Simd Rotation( float xAxisRotation, 
                        float yAxisRotation, float zAxisRotation ) noexcept;

RotationNormal

Creates a matrix that rotates around a normalized vector.

template<Internal::SimdType S>
    requires ( S::Size > 2 ) 
inline SquareMatrix<float, 4>::Simd RotationNormal( const S& normalizedAxis, float angle ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd RotationNormal( const S& normalizedAxis, float angle ) noexcept;

RotationAxis

Creates a matrix that rotates around an arbitrary axis.

template<Internal::SimdType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd RotationAxis( const S& axis, float angle ) noexcept;

template<Internal::TupleType S>
    requires ( S::Size > 2 )
inline SquareMatrix<float, 4>::Simd RotationAxis( const S& axis, float angle ) noexcept;

RotationQuaternion

Creates a rotation matrix from a quaternion.

inline SquareMatrix<float, 4>::Simd RotationQuaternion( const QuaternionSimd<Quaternion<float>>& q );

inline SquareMatrix<float, 4>::Simd RotationQuaternion( const Quaternion<float>& q );

TransformationMatrix

Creates a transformation matrix.

inline SquareMatrix<float, 4>::Simd TransformationMatrix( const Point3f::Simd& scalingOrigin, 
                            const QuaternionSimd<Quaternion<float>>& scalingOrientationQuaternion, 
                            const Vector<float,3>::Simd& scaling,
                            const Point3f::Simd& rotationOrigin, 
                            const QuaternionSimd<Quaternion<float>>& rotationQuaternion, 
                            const Vector<float, 3>::Simd& translation ) noexcept;


AffineTransformationMatrix

Creates an affine transformation matrix.

template<Internal::SimdType S, Internal::SimdType T, typename U, Internal::SimdType W>
        requires (S::Size > 2) && (T::Size > 2) && (W::Size > 2) && IsFloatingPoint<U> &&
            std::is_same_v<typename S::value_type,U> && 
                std::is_same_v<typename T::value_type, U> && 
                std::is_same_v<typename W::value_type, U>
    inline SquareMatrix<U, 4>::Simd AffineTransformationMatrix( const S& scaling,
                                        const T& rotationOrigin,
                                        const QuaternionSimd<Quaternion<U>>& rotationQuaternion,
                                        const W& translation ) noexcept;

LookTo

Creates a view matrix using the left-handed coordinate system for the provided camera position, camera direction, and up direction.

template<Internal::SimdType S, Internal::SimdType T, Internal::SimdType U>
    requires Internal::IsCompatible<S,T> && Internal::IsCompatible<S, U> && (S::Size == 3)
inline SquareMatrix<typename S::value_type, 4>::Simd LookTo( const S& cameraPosition, 
                                                            const T& cameraDirection, 
                                                            const U& upDirection ) noexcept;

LookAt

Creates a view matrix using the left-handed coordinate system for the provided camera position, focal point, and up direction.

template<Internal::SimdType S, Internal::SimdType T, Internal::TupleType U>
    requires Internal::IsCompatible<S, T>&& Internal::IsCompatible<S, U> && ( S::Size == 3 )
inline SquareMatrix<typename S::value_type, 4>::Simd LookAt( const S& cameraPosition, 
                                                            const T& focusPosition, 
                                                            const U& upDirection ) noexcept;

PerspectiveProjection

Creates a left-handed perspective projection matrix.

template<typename T>
    requires IsFloatingPoint<T>
inline SquareMatrix<T, 4>::Simd PerspectiveProjection( T viewWidth, 
                                                    T viewHeight, 
                                                    T nearZ, 
                                                    T farZ ) noexcept;

PerspectiveFovProjection

Creates a left-handed perspective projection matrix based on a field of view.

template<typename T>
    requires IsFloatingPoint<T>
inline SquareMatrix<T, 4>::Simd PerspectiveFovProjection( T fovAngleY, 
                                                        T aspectRatio, 
                                                        T nearZ, 
                                                        T farZ ) noexcept;

Transform

Applies a transformation matrix to a 3D vector.

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>::Simd& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>::Simd& v, 
                                        const SquareMatrix<float, 4>& matrix );

inline Vector<float, 3>::Simd Transform( const Vector<float, 3>& v, 
                                        const SquareMatrix<float, 4>& matrix );

Applies a transformation matrix to a 3D coordinate.

inline Point3f::Simd Transform( const Point3f::Simd& p, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f& p, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Point3f::Simd Transform( const Point3f::Simd& p, 
                                const SquareMatrix<float, 4>& matrix );

inline Point3f::Simd Transform( const Point3f& p, 
                                const SquareMatrix<float, 4>& matrix );

Applies a transformation matrix to a normal.

inline Normal3f::Simd Transform( const Normal3f::Simd& n, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f& n, 
                                const SquareMatrix<float, 4>::Simd& matrix );

inline Normal3f::Simd Transform( const Normal3f::Simd& n, 
                                const SquareMatrix<float, 4>& matrix );

inline Normal3f::Simd Transform( const Normal3f& n, 
                                const SquareMatrix<float, 4>& matrix );

Quaternion

Quaternions provides a compact representation of rotations.

The Quaternion template can be used with both single and double precision floating point types.

Like the tuple templates, there is a QuaternionSimd template that represents quaternions loaded into a SIMD register.

Quaternion and QuaternionSimd supports addition, subtraction, multiplication, scalar multiplication and scalar division.

Quaternion member functions

Constructors

constexpr Quaternion( ) noexcept;

The default constructor initializes v.x, v.y, v.z and w to 0.0.

constexpr Quaternion( ValueType xv, ValueType yv, ValueType zv, ValueType wv ) noexcept;

Initializes v.x to xv, v.y to yv, v.z to zv and w to wv.

constexpr Quaternion( const Vector<ValueType,3>& vv, ValueType wv ) noexcept;

Initializes v to xv and w to wv.

Quaternion( const Simd& qsimd ) noexcept

Initializes the quaternion using the values held by qsimd.simd.

Quaternion( ValueType pitch, ValueType yaw, ValueType roll ) noexcept

Creates a quaternion based on the pitch, yaw, and roll (Euler angles), where:

  • pitch is the angle of rotation around the x-axis, in radians.
  • yaw is the angle of rotation around the y-axis, in radians.
  • roll is the angle of rotation around the z-axis, in radians.

Quaternion functions

Dot

Calculates the dot product of two quaternions.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T,4>::Simd Dot( const QuaternionSimd<Quaternion<T>>& q1, 
                                const QuaternionSimd<Quaternion<T>>& q2 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Dot( const Quaternion<T>& q1, 
                                const QuaternionSimd<Quaternion<T>>& q2 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Dot( const QuaternionSimd<Quaternion<T>>& q1, 
                                const Quaternion<T>& q2 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Dot( const Quaternion<T>& q1, 
                                const Quaternion<T>& q2 ) noexcept;

Length and ScalarLength

Calculates the magnitude of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Length( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd Length( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLength( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLength( const Quaternion<T>& q1 ) noexcept;

LengthSquared and ScalarLengthSquared

Calculates the square of the magnitude of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd LengthSquared( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd LengthSquared( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLengthSquared( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarLengthSquared( const Quaternion<T>& q1 ) noexcept;

ReciprocalLength and ScalarReciprocalLength

Calculates the reciprocal of the magnitude of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd ReciprocalLength( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename Vector<T, 4>::Simd ReciprocalLength( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarReciprocalLength( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
typename T ScalarReciprocalLength( const Quaternion<T>& q1 ) noexcept;

Conjugate

Calculates the conjugate of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Conjugate( const Quaternion<T>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Normalize( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

Normalize

Normalizes a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Normalize( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Normalize( const Quaternion<T>& q1 ) noexcept;

Inverse

Calculates the inverse of a quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Inverse( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Inverse( const Quaternion<T>& q1 ) noexcept;

Log

Calculates the natural logarithm of a unit quaternion.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Log( const QuaternionSimd<Quaternion<T>>& q1 ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Log( const Quaternion<T>& q1 ) noexcept;

Slerp

Spherical linear interpolation between two unit quaternions.

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const Quaternion<T>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const QuaternionSimd<Quaternion<T>>& q1, 
                                    const Quaternion<T>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const QuaternionSimd<Quaternion<T>>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const Quaternion<T>& q2, 
                                    const typename Vector<T, 4>::Simd& t ) noexcept;

template<typename T>
    requires IsFloatingPoint<T>
QuaternionSimd<Quaternion<T>> Slerp( const Quaternion<T>& q1, 
                                    const Quaternion<T>& q2, 
                                    const Vector<T, 4>& t ) noexcept;

Transforms

A transform is a mapping from vectors to vectors or points to points.

Taking a point, vector, or normal defined with respect to one coordinate frame and finding its coordinate values with respect to another coordinate frame is a useful capability.

AffineTransformation

Affine transformations are transformations that preserves collinearity; where all points lying on a line initially, still lie on a line after transformation; and ratios of distances, like the midpoint of a line segment, which remains the midpoint after transformation. In this sense, affine indicates a special class of projective transformations that do not move any objects from the affine space \(R^3\) to the plane at infinity or conversely.

Geometric contraction, expansion, dilation, reflection, rotation, shear, similarity transformations, spiral similarities, and translation are all affine transformations, as are their combinations. In general, an affine transformation is a composition of rotations, translations, dilations, and shears.

While an affine transformation preserves proportions on lines, it does not necessarily preserve angles or lengths. Any triangle can be transformed into any other by an affine transformation, so all triangles are affine and, in this sense, affine is a generalization of congruent and similar.

Use the AffineTransformation class to efficiently transform Vector3f, Point3f, Normal3f, and AffineTransformation objects from one coordinate space to another.

The transformation of a AffineTransformation object, results in an AffineTransformation object combining the transformations of both into a single transformation.