The API is primarily dictated by the OpenGL Shading Language 4.6 specification (pdf | html) for vectors and matrices. It is also dictated by the need to interact with the c++20
ecosystem.
- Library
cxcm
- Namespace
dsga
- Concepts
- Variable Templates
- Using Directives
- Utility Functions
- Class Templates
- Class Template Instantiations
- Vector
- Matrix
- Simple Conversion Functions
- Tuple Protocol
cxcm is its own stand-alone project for extending <cmath>
to have more functions be constexpr, targeted for c++20
. c++23
and hopefully c++26
extend the amount of functions in <cmath>
to be constexpr, but this project aims to support c++20
. cxcm
has been brought in under namespace dsga
as a nested namespace cxcm
, with all the functionality of the stand-alone cxcm
release. This allows us to have a single header library for dsga
.
dsga
needs cxcm
to implement some of the vector free functions as constexpr functions. Some of these functions are designed so that if they are invoked at runtime instead of compile time, then they invoke the corresponding functions in the standard library, giving us the fastest implementation. See the cxcm
API for how to use. Remember, cxcm
is a nested namespace under namespace dsga
for this project.
Unless otherwise stated, all of the API is defined in namespace dsga
.
bool_scalar
signed_scalar
unsigned_scalar
numeric_integral_scalar
floating_point_scalar
non_bool_scalar
dimensional_scalar
dimensional_size
dimensional_storage
promotes_to
implicitly_convertible_to
indexable
template <typename T>
concept bool_scalar = std::same_as<bool, T>;
Plain undecorated boolean type.
template <typename T>
concept signed_scalar = (std::same_as<int, T> || std::same_as<long long, T>);
Plain undecorated signed types.
template <typename T>
concept unsigned_scalar = (std::same_as<unsigned int, T> || std::same_as<unsigned long long, T> || std::same_as<std::size_t, T>);
Plain undecorated unsigned types.
template <typename T>
concept numeric_integral_scalar = (signed_scalar<T> || unsigned_scalar<T>);
Plain undecorated integral types.
template <typename T>
concept floating_point_scalar = (std::same_as<float, T> || std::same_as<double, T>);
Plain undecorated floating point types.
template <typename T>
concept non_bool_scalar = (numeric_integral_scalar<T> || floating_point_scalar<T>);
Plain undecorated integral and floating point types.
template <typename T>
concept dimensional_scalar = (non_bool_scalar<T> || bool_scalar<T>);
Plain undecorated arithmetic types.
template <std::size_t Size>
concept dimensional_size = ((Size >= 1) && (Size <= 4));
We want the size to be between 1 and 4, inclusive.
template <typename T, std::size_t Size>
concept dimensional_storage = dimensional_scalar<T> && dimensional_size<Size>;
Vector type and size requirements.
template <typename T, typename U>
concept promotes_to =
requires
{
typename std::common_type_t<std::remove_cvref_t<T>, std::remove_cvref_t<U>>;
requires std::same_as<std::common_type_t<std::remove_cvref_t<T>, std::remove_cvref_t<U>>, std::remove_cvref_t<U>>;
};
Is the second type U also the common type of the two types T and U?
template <typename T, typename U>
concept implicitly_convertible_to = non_bool_scalar<T> && non_bool_scalar<U> && promotes_to<T, U>;
Are implicit conversions allowed for non-boolean arithmetic purposes?
template <std::size_t Size, std::size_t Count, std::size_t ...Is>
concept indexable = detail::valid_index_count<Count, Is...>() && detail::valid_range_indexes<Size, Is...>();
Do the argument indexes and count/size make for valid indirect indexing, such as for indexed_vector
.
Size and Count are two different things, with Size being the physical number of elements, and Count being the logical number of elements. Count must be the sizeof...Is
, and for every one of the Is, it must be smaller than Size.
template <std::size_t Size, std::size_t Count, std::size_t ...Is>
requires indexable<Size, Count, Is...>
constexpr inline bool writable_swizzle = detail::unique_indexes(std::index_sequence<Is...>{});
Used for the Writable template parameter of a indexed_vector
. Can a particular swizzle be used as an lvalue reference. All of the swizzle indexes must have been used at most once, e.g., for xyz
, Writable is true, and for xyx
, Writable is false (assuming that all other requirements are met).
template <floating_point_scalar T>
inline constexpr T degrees_per_radian_v = std::numbers::inv_pi_v<T> * T(180);
template <floating_point_scalar T>
inline constexpr T radians_per_degree_v = std::numbers::pi_v<T> / T(180);
dimensional_storage_t
make_index_range
make_closed_index_range
make_array_sequence
- Using Directives for Creating
indexed_vector
s
template <dimensional_scalar T, std::size_t Size>
requires dimensional_storage<T, Size>
using dimensional_storage_t = std::array<T, Size>;
The underlying storage type for storage_wrappper
and indexed_vector
. It is contiguous, and it has contiguous iterators.
template<std::size_t Start, std::size_t End>
using make_index_range = decltype(detail::index_range<Start, End>());
This gives a half-open/half-closed interval in a std::index_sequence
-> [Start, End). If End is less than Start, the sequence is in descending order.
template<std::size_t Start, std::size_t End>
using make_closed_index_range = decltype(detail::closed_index_range<Start, End>());
This gives a closed interval in a std::index_sequence
-> [Start, End]. If End is less than Start, the sequence is in descending order.
template <detail::sequence_indexable auto vals>
using make_array_sequence = decltype(detail::indexable_to_sequence<vals>(std::make_index_sequence<vals.size()>{}));
This gives a std::index_sequence
that contains the elements of a constexpr std::array<T, N> vals
, where T
is convertible to a std::size_t
and none of the elements are negative. Constexpr non-type template parametervals
doesn't have to be of type std::array<T, N>
, but it must have constexpr member functions size
and operator []
, both of whose return values are convertible to std::size_t
, e.g., dsga::basic_vector<T, N>
, as long as all values in the vector are non-negative and std::convertible_to<T, std::size_t>
is true.
template <typename T, std::size_t Size, std::size_t I>
using dexvec1 = indexed_vector<std::remove_cvref_t<T>, Size, 1, I>;
Convenience using directive for creating a indexed_vector
for Count == 1
.
template <typename T, std::size_t Size, std::size_t ...Is>
requires (sizeof...(Is) == 2)
using dexvec2 = indexed_vector<std::remove_cvref_t<T>, Size, 2, Is...>;
Convenience using directive for creating a indexed_vector
for Count == 2
.
template <typename T, std::size_t Size, std::size_t ...Is>
requires (sizeof...(Is) == 3)
using dexvec3 = indexed_vector<std::remove_cvref_t<T>, Size, 3, Is...>;
Convenience using directive for creating a indexed_vector
for Count == 3
.
template <typename T, std::size_t Size, std::size_t ...Is>
requires (sizeof...(Is) == 4)
using dexvec4 = indexed_vector<std::remove_cvref_t<T>, Size, 4, Is...>;
Convenience using directive for creating a indexed_vector
for Count == 4
.
template <std::size_t... Is>
constexpr std::array<std::size_t, sizeof...(Is)> make_sequence_array(std::index_sequence<Is...>) noexcept;
Build an array from the indexes of a std::index_sequence
.
template<std::size_t ...Is>
constexpr auto make_reverse_sequence(std::index_sequence<Is...> seq) noexcept;
Convert a std::index_sequence<Is...>
to another std::index_sequence
with the Is...
in reverse order from input.
template <typename C, auto val = std::bool_constant<(C{}(), true)>{}>
consteval auto is_constexpr(C) noexcept;
Is a default-constructible callable constexpr? If not, then compile error due to trying to evaluate something that is not constexpr at compile time.
template <typename E>
requires std::is_enum_v<E>
[[nodiscard]] constexpr std::underlying_type_t<E> to_underlying(E e) noexcept;
Not a vector or matrix function. Not in GLSL. This functionality was added to c++23
, but since this is a c++20
library, we have to provide the underlying implementation ourselves. It is not really a good fit for dsga, but it is handy to have around anyway.
This diagram explores the relationships between the various dsga
template classes, which are actually all structs. The main two template structs are basic_vector
and basic_matrix
. The other structs are in support of those two primary components.
template <dimensional_scalar T, std::size_t Size>
requires dimensional_storage<T, Size>
struct storage_wrapper;
This struct is structurally equivalent to indexed_vector
. They both wrap a data member of type dimensional_storage_t
. When comparing an indexed_vector
and dimensional_storage_t
of the same type and size, as members in a union, they satisfy the type trait std::is_corresponding_member
. For a union of many members of indexed_vector
and a member of dimensional_storage_t
, these all share a common initial sequence. This allows us to read any of the union members without having to make a member active first (via writing). An anonymous union of a storage_wrapper
and multiple swizzles of indexed_vector
instantiations constitutes the data of a basic_vector
.
storage_wrapper
is not part of the GLSL specification, but it is used by basic_vector
for writing data to storage. It has been designed to have a similar API to basic_vector
.
-
Template Parameters
T
- type of thestorage_wrapper
elements. Must satisfydimensional_scalar
.Size
- number ofstorage_wrapper
elements. Must satisfydimensional_size
.
-
Non-Static Data Members
-
Static Data Members
-
Static Member Functions
-
Non-Static Member Functions
-
Using Directives
sequence_pack
value_type
iterator
const_iterator
reverse_iterator
const_reverse_iterator
dimensional_storage_t<T, Size> store;
The data member for the storage.
static constexpr std::size_t Count = Size;
The static variable that holds the number of items that we are handling. It is always the same value as the template parameter Size
. It is unnecessary for this class, but it is used here to be in solidarity with indexed_vector
, where Size
and Count
are often not the same value.
static constexpr bool Writable = true;
This static variable is always true for all storage_wrapper
s. It is unnecessary for this class, but it is used in here to be in solidarity with indexed_vector
, where Writable is not always true (mostly false actually).
static constexpr std::array<std::size_t, Count> offsets = make_sequence_array(sequence_pack{});
The static std::array
for how the physical representation is mapped to the logical representation. For storage_wrapper
, the physical and logical representations are the same, i.e., a contiguous representation, and the sequence ascends from 0.
static constexpr std::integral_constant<std::size_t, Count> size = {};
[[nodiscard]] static constexpr std::size_t size() const noexcept;
Return the number of components in the struct. The declaration for size()
is a fiction due to the fact that this function does not exist; however, the static std::integral_constant
size
has an operator()()
that operates exactly as the above declaration. This approach of using a std::integral_constant
for size
is supposed to be an up and coming idiom in the C++ standard for all new standard library components with constant sizes.
[[nodiscard]] constexpr int length() const noexcept;
The GLSL specification requires that the length method
behave as a member function, returning the number of components in the struct.
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr T &operator [](const U &index) noexcept requires Writable;
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr const T &operator [](const U &index) const noexcept;
Data access through the indexing operator. The indexing operator already takes the physical to logical data mapping into account. For storage_wrapper
, the elements are contiguous, so the physical and logical mapping is the same.
[[nodiscard]] constexpr T * data() noexcept requires Writable;
[[nodiscard]] constexpr const T * data() const noexcept;
Data access through pointers. Use with sequence
or offsets
for physical to logical mapping.
[[nodiscard]] static constexpr auto sequence() noexcept;
The std::index_sequence
for how the physical representation is mapped to the logical representation. For storage_wrapper
, the physical and logical representation are the same, i.e., a contiguous representation, and the sequence ascends from 0.
template <typename ...Args>
requires Writable && (sizeof...(Args) == Count) && (std::convertible_to<Args, T> &&...)
constexpr void set(Args ...args) noexcept;
Set all the values of a storage_wrapper
via parameter copies. This prevents aliasing issues with references.
constexpr void swap(storage_wrapper &sw) noexcept requires Writable;
Swap the data using the underlying dimensional_storage_t
's swap
function.
[[nodiscard]] constexpr auto begin() noexcept requires Writable;
[[nodiscard]] constexpr auto begin() const noexcept;
[[nodiscard]] constexpr auto cbegin() const noexcept;
[[nodiscard]] constexpr auto end() noexcept requires Writable;
[[nodiscard]] constexpr auto end() const noexcept;
[[nodiscard]] constexpr auto cend() const noexcept;
[[nodiscard]] constexpr auto rbegin() noexcept requires Writable;
[[nodiscard]] constexpr auto rbegin() const noexcept;
[[nodiscard]] constexpr auto crbegin() const noexcept;
[[nodiscard]] constexpr auto rend() noexcept requires Writable;
[[nodiscard]] constexpr auto rend() const noexcept;
[[nodiscard]] constexpr auto crend() const noexcept;
These contiguous iterators are supplied by the underlying dimensional_storage_t
.
using sequence_pack = std::make_index_sequence<Count>;
The instantiation of std::index_sequence
that represents the physical to logical mapping. sequence
returns an instance of this type.
template <dimensional_scalar T, std::size_t Size>
constexpr void swap(storage_wrapper<T, Size> &lhs, storage_wrapper<T, Size> &rhs) noexcept;
Free function swap
wraps the member function storage_wrapper::swap
.
template <dimensional_scalar T1, std::size_t C, dimensional_scalar T2>
requires implicitly_convertible_to<T2, T1>
constexpr bool operator ==(const storage_wrapper<T1, C> &first,
const storage_wrapper<T2, C> &second) noexcept;
Returns whether all the components are exactly equal.
template <dimensional_scalar T1, std::size_t C, dimensional_scalar T2>
requires implicitly_convertible_to<T2, T1>
constexpr bool operator !=(const storage_wrapper<T1, C> &first,
const storage_wrapper<T2, C> &second) noexcept;
Function automatically generated from operator ==
. Returns whether any of the components are not exactly equal.
template <dimensional_scalar T, dimensional_scalar ...U>
storage_wrapper(T, U...) -> storage_wrapper<T, 1 + sizeof...(U)>;
Class template argument deduction (CTAD) for storage_wrapper
.
template <bool Writable, dimensional_scalar T, std::size_t Count, typename Derived>
requires dimensional_storage<T, Count>
struct vector_base;
This CRTP base class is inherited by derived classes basic_vector
and indexed_vector
. It has no data members of its own. Most all of the vector operators and free functions operate on vector_base
instead of the derived vectors, as we want to treat the two derived vector types as similarly as possible.
- Template Parameters
Writable
- certain derived classes are not writable, namely most template instantiations ofindexed_vector
. IfWritable
is false, thenvector_base
is effectively const.T
- type of the vector elements. Must satisfydimensional_scalar
.Count
- number of logical vector elements. Must satisfydimensional_size
.Derived
- the type of this derived class for whichvector_base
is a CRTP base class.
- Non-Static Data Members
- None.
- Static Data Members
- Static Member Functions
- Non-Static Member Functions
- Vector Operators
- Vector Free Functions
static constexpr std::integral_constant<std::size_t, Count> size = {};
[[nodiscard]] static constexpr std::size_t size() const noexcept;
Return the number of components in the derived struct. The declaration for size()
is a fiction due to the fact that this function does not exist; however, the static std::integral_constant
size
has an operator()()
that operates exactly as the above declaration. This approach of using a std::integral_constant
for size
is supposed to be an up and coming idiom in the C++ standard for all new standard library components with constant sizes.
[[nodiscard]] constexpr int length() const noexcept;
The GLSL specification requires that the length method
behave as a member function, returning the number of components in the struct.
[[nodiscard]] constexpr Derived &as_derived() noexcept requires Writable;
[[nodiscard]] constexpr const Derived &as_derived() const noexcept;
Since this is a CRTP base class, one of the template type parameters is for the class/struct that derived from vector_base
. This function returns a reference to the derived class/struct version of the vector_base
.
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr T &operator [](const U &index) noexcept requires Writable;
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr const T &operator [](const U &index) const noexcept;
Data access through the indexing operator. This CRTP function calls the derived class/struct version of the function.
[[nodiscard]] constexpr T * data() noexcept requires Writable;
[[nodiscard]] constexpr const T * data() const noexcept;
Data access through pointers. Use with sequence
or offsets
for physical to logical mapping. This CRTP function calls the derived class/struct version of the function.
[[nodiscard]] static constexpr auto sequence() noexcept;
The std::index_sequence
for how the physical representation is mapped to the logical representation. This CRTP function calls the derived class/struct version of the function.
template <typename ...Args>
requires Writable && (sizeof...(Args) == Count) && (std::convertible_to<Args, T> &&...)
constexpr void set(Args ...args) noexcept;
Set all the values of the derived struct/class via parameter copies. This prevents aliasing issues with references. This CRTP function calls the derived class/struct version of the function.
[[nodiscard]] constexpr auto begin() noexcept requires Writable;
[[nodiscard]] constexpr auto begin() const noexcept;
[[nodiscard]] constexpr auto cbegin() const noexcept;
[[nodiscard]] constexpr auto end() noexcept requires Writable;
[[nodiscard]] constexpr auto end() const noexcept;
[[nodiscard]] constexpr auto cend() const noexcept;
[[nodiscard]] constexpr auto rbegin() noexcept requires Writable;
[[nodiscard]] constexpr auto rbegin() const noexcept;
[[nodiscard]] constexpr auto crbegin() const noexcept;
[[nodiscard]] constexpr auto rend() noexcept requires Writable;
[[nodiscard]] constexpr auto rend() const noexcept;
[[nodiscard]] constexpr auto crend() const noexcept;
These CRTP functions call the derived class/struct versions of the functions.
template <typename UnOp>
requires (std::same_as<T, std::invoke_result_t<UnOp, T>> || std::same_as<T, std::invoke_result_t<UnOp, const T &>>)
[[nodiscard]] constexpr basic_vector<T, Count> apply(UnOp op) const noexcept;
Applies a lambda/function/function object/callable to every element of a vector, in element order (order only matters if callable is side-effecting and/or has state). The callable must take either a T
or const T &
, and it must return a T
. Returns a vector of the results.
template <typename UnOp>
requires (std::same_as<bool, std::invoke_result_t<UnOp, T>> || std::same_as<bool, std::invoke_result_t<UnOp, const T &>>)
[[nodiscard]] constexpr basic_vector<bool, Count> query(UnOp op) const noexcept;
Applies a predicate lambda/function/function object/callable to every element of a vector, in element order (order only matters if callable is side-effecting and/or has state). The callable must take either a T
or const T &
, and it must return a bool
. Returns a vector of the results. Not in GLSL nor std::valarray
.
[[nodiscard]] constexpr basic_vector<T, Count> shift(int by) const noexcept;
Zero-filling shift the elements of the vector. Returns a vector of the results.
[[nodiscard]] constexpr basic_vector<T, Count> cshift(int by) const noexcept;
Circular shift of the elements of the vector. Returns a vector of the results.
[[nodiscard]] constexpr T min() const noexcept requires non_bool_scalar<T>;
Returns the smallest element.
[[nodiscard]] constexpr T max() const noexcept requires non_bool_scalar<T>;
Returns the largest element.
[[nodiscard]] constexpr T sum() const noexcept requires non_bool_scalar<T>;
Returns the sum of all elements.
template <dimensional_scalar T, std::size_t Size, std::size_t Count, std::size_t ...Is>
requires indexable<T, Size, Count, Is...>
struct indexed_vector<T, Size, Count, Is...>
: vector_base<writable_swizzle<Size, Count, Is...>, T, Count, indexed_vector<T, Size, Count, Is...>>;
One of the two vector types that inherit from vector_base
. This type represents a swizzle of a basic_vector
. It is a physically non-contiguous, logically contiguous vector. It is not expected to be used except as the swizzle members of the anonymous union in a basic_vector
. A basic_vector
is easily constructed from or assigned to from an indexed_vector
.
Like storage_wrapper
, this struct wraps a data member of type dimensional_storage_t
.
Since this vector type is physically non-contiguous, the iterator structs are not contiguous. The two iterator structs indexed_vector_iterator
and indexed_vector_const_iterator
are random-access iterators.
Size and Count are two different things, with Size being the physical number of elements, and Count being the logical number of elements. Count must be the sizeof...(Is)
, and for every one of the Is
, the value must be smaller than Size.
- Template Parameters
T
- type of theindexed_vector
elements. Must satisfydimensional_scalar
.Size
- physical number ofindexed_vector
elements. Must satisfydimensional_size
.Count
- logical number ofindexed_vector
elements. Must satisfydimensional_size
.Is...
- a parameter pack of the logical indexes for anindexed_vector
. Count must be thesizeof...(Is)
, and for every one of theIs
, the value must be smaller than Size.
- Non-Static Data Members
- Static Data Members
- Static Member Functions
- Non-Static Member Functions
- Using Directives
sequence_pack
value_type
iterator
const_iterator
reverse_iterator
const_reverse_iterator
- Vector Operators
- Vector Free Functions
dimensional_storage_t<T, Size> base;
The data member for the storage.
static constexpr bool Writable = writable_swizzle<Size, Count, Is...>;
Is this a writable vector. All of the Is...
must be unique for this vector to be writable. If not writable, then it is effectively const.
static constexpr std::array<std::size_t, Count> offsets;
The static std::array
for how the physical representation is mapped to the logical representation.
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr T &operator [](const U &index) noexcept requires Writable;
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr const T &operator [](const U &index) const noexcept;
Data access through the indexing operator. The indexing operator already takes the physical to logical data mapping into account, so no need to use sequence
or offsets
with this operator.
template <bool W, dimensional_scalar U, typename D>
requires Writable && implicitly_convertible_to<U, T>
constexpr indexed_vector &operator =(const vector_base<W, U, Count, D> &other) & noexcept;
The assignment operator. It can be assigned from objects that inherit from vector_base
. Can assign only to lvalues.
[[nodiscard]] constexpr T *data() noexcept requires Writable;
[[nodiscard]] constexpr const T *data() const noexcept;
Data access through pointers. Use with sequence
or offsets
for physical to logical mapping.
[[nodiscard]] static constexpr auto sequence() noexcept;
The std::index_sequence
for how the physical representation is mapped to the logical representation. This is best used with data
to manually access the elements for the logical representation.
template <typename ... Args>
requires Writable && (std::convertible_to<Args, T> && ...) && (sizeof...(Args) == Count)
constexpr void set(Args ...args) noexcept;
Set all the vector element values via parameter copies. This prevents aliasing issues with references.
[[nodiscard]] constexpr auto begin() noexcept requires Writable;
[[nodiscard]] constexpr auto begin() const noexcept;
[[nodiscard]] constexpr auto cbegin() const noexcept;
[[nodiscard]] constexpr auto end() noexcept requires Writable;
[[nodiscard]] constexpr auto end() const noexcept;
[[nodiscard]] constexpr auto cend() const noexcept;
[[nodiscard]] constexpr auto rbegin() noexcept requires Writable;
[[nodiscard]] constexpr auto rbegin() const noexcept;
[[nodiscard]] constexpr auto crbegin() const noexcept;
[[nodiscard]] constexpr auto rend() noexcept requires Writable;
[[nodiscard]] constexpr auto rend() const noexcept;
[[nodiscard]] constexpr auto crend() const noexcept;
Random-access iterators of types indexed_vector_iterator
and indexed_vector_const_iterator
.
using sequence_pack = std::index_sequence<Is...>;
The sequence pack is formed by the variadic Is...
from the template parameter pack.
template <dimensional_scalar T, std::size_t Size, std::size_t Count, std::size_t ... Is>
requires indexable<T, Size, Count, Is...>
struct indexed_vector_iterator : indexed_vector_const_iterator<T, Size, Count, Is...>;
Random-access iterator for indexed_vector
s. Inspired by std::array
's iterator, even though it is a contiguous iterator. It implements the normal API for a random-access iterator.
template <dimensional_scalar T, std::size_t Size, std::size_t Count, std::size_t ... Is>
requires indexable<T, Size, Count, Is...>
struct indexed_vector_const_iterator;
Const random-access iterator for indexed_vector
s. Inspired by std::array
's iterator, even though it is a contiguous iterator. It implements the normal API for a const random-access iterator.
template <dimensional_scalar T, std::size_t Size>
requires dimensional_storage<T, Size>
struct basic_vector : vector_base<true, T, Size, basic_vector<T, Size>>;
This is the primary struct for vectors. One of the two vector types that inherit from vector_base
. basic_vector
s can be swizzled, where a swizzle is one of its anonymous union members of type indexed_vector
. The anonymous union also has a storage_wrapper
member, and the basic_vector
member functions operate on its instances through this data member. All the anonymous union members share a common initial sequence, which means they can be read by any member, regardless of whether it is the active member.
The different sized versions of basic_vector
are individually partially specialized, with sizes from 1 to 4. Each partial specialization has different data members as part of its anonymous union, due to the fact that the swizzles are different depending on Size
.
- Template Parameters
T
- type of thebasic_vector
elements. Must satisfydimensional_scalar
.Size
- physical number ofbasic_vector
elements. Must satisfydimensional_size
.
basic_vector
Constructors- Non-Static Data Members
- Anonymous Union
- Static Data Members
- Static Member Functions
- Non-Static Member Functions
- Using Directives
sequence_pack
value_type
iterator
const_iterator
reverse_iterator
const_reverse_iterator
- Vector Operators
- Vector Free Functions
basic_vector
Free Functions- Class Template Argument Deduction (CTAD)
constexpr basic_vector() noexcept = default;
constexpr basic_vector(const basic_vector &) noexcept = default;
constexpr basic_vector(basic_vector &&) noexcept = default;
Default constructors.
template <typename U>
requires std::convertible_to<U, T>
explicit constexpr basic_vector(U value) noexcept;
All elements of the vector initialized to the same value.
template <typename U1, typename U2, typename U3, typename U4>
requires
std::convertible_to<U1, T> && std::convertible_to<U2, T> &&
std::convertible_to<U3, T> && std::convertible_to<U4, T>
explicit constexpr basic_vector(U1 xvalue,
U2 yvalue,
U3 zvalue,
U4 wvalue) noexcept;
Each of the elements have a scalar value passed in to initialize them. The above declaration is for a basic_vector
of Size == 4
. basic_vector
s where Size == 3
and Size == 2
have similar constructors based on the number of their elements.
template <bool W, dimensional_scalar U, typename D>
requires implicitly_convertible_to<U, T>
explicit(false) constexpr basic_vector(const vector_base<W, U, Count, D> &other) noexcept;
Initialize a basic_vector
from any of the vector types that derived from vector_base
.
template <typename U, typename ... Args>
requires (detail::valid_vector_component<U, T>::value) && (detail::valid_vector_component<Args, T>::value && ...) && detail::met_component_count<Count, U, Args...>
explicit constexpr basic_vector(const U &u, const Args & ...args) noexcept;
Variadic constructor. Can take a combination of vectors, scalars, and matrixes as arguments to initialize the basic_vector
.
constexpr basic_vector(const std::initializer_list<T> &init_list) noexcept;
An intializer list of values. If too few values for the vector, the rest of the elements will be set to 0. If too many values for the vector, the rest of the initialization list will be ignored.
storage_wrapper<T, Size> base;
The anonymous union data member through which basic_vector
accesses the vector elements.
dexvec1<T, Size, 0> x;
...
...
dexvec2<T, Size, 0, 0> xx;
...
...
dexvec3<T, Size, 0, 0, 0> xxx;
...
...
dexvec4<T, Size, 0, 0, 0, 0> xxxx;
...
...
The other anonymous union data members are all swizzles of the basic_vector
. They are of type indexed_vector
.
A swizzle data member is named with 1 to 4 element reference characters, e.g., xy
, wxwz
. Depending on the value of Count
, there are restrictions on the swizzle element access. x
maps to the first element, y
maps to the second element, z
maps to the third element, and w
maps to the fourth element.
Count == 1
Can use {x
} for swizzle names.
Examples: x
, xx
, xxx
, xxxx
.
Count == 2
Can use {x
, y
} for swizzle names.
Examples: y
, yx
, xyx
, yxxy
.
Count == 3
Can use {x
, y
, z
} for swizzle names.
Examples: z
, zx
, zyx
, yzxz
.
Count == 4
Can use {x
, y
, z
, w
} for swizzle names.
Examples: w
, yw
, zwx
, wyxz
.
static constexpr std::size_t Size;
Since we instantiate partial specializations of basic_vector
, we manually set this value for each of the partial specializations. The value must satisfy dimensional_size
. For the partial specializations, there is no template parameter Size available to use within the class/struct, and this static data member simulates such a template parameter.
static constexpr std::size_t Count = Size;
The static variable that holds the number of items that we are handling. It is always the same value as the template parameter Size
. It is unnecessary for this class, but it is used here to be in solidarity with indexed_vector
, where Size
and Count
are often not the same value.
static constexpr bool Writable = true;
This static variable is always true for all basic_vector
s. It is unnecessary for this class, but it is used in here to be in solidarity with indexed_vector
, where Writable is not always true (mostly false actually).
static constexpr std::array<std::size_t, Count> offsets = make_sequence_array(sequence_pack{});
The static std::array
for how the physical representation is mapped to the logical representation. For basic_vector
, the physical and logical representations are the same, i.e., a contiguous representation, and the sequence ascends from 0.
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr T &operator [](const U &index) noexcept requires Writable;
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr const T &operator [](const U &index) const noexcept;
Data access through the indexing operator. The indexing operator already takes the physical to logical data mapping into account. For basic_vector
, the elements are contiguous, so the physical and logical mapping is the same.
template <bool W, dimensional_scalar U, typename D>
requires Writable && implicitly_convertible_to<U, T>
constexpr basic_vector &operator =(const vector_base<W, U, Count, D> &other) & noexcept;
The assignment operator. It can be assigned from objects that inherit from vector_base
. Can assign only to lvalues.
[[nodiscard]] constexpr T *data() noexcept requires Writable;
[[nodiscard]] constexpr const T *data() const noexcept;
Data access through pointers. Use with sequence
or offsets
for physical to logical mapping. For basic_vector
, the elements are contiguous, so the physical and logical mapping is the same.
[[nodiscard]] static constexpr auto sequence() noexcept;
The std::index_sequence
for how the physical representation is mapped to the logical representation. For basic_vector
, the physical and logical representation are the same, i.e., a contiguous representation, and the sequence ascends from 0.
template <typename ...Args>
requires Writable && (sizeof...(Args) == Count) && (std::convertible_to<Args, T> && ...)
constexpr void set(Args ...args) noexcept;
Set all the vector element values via parameter copies. This prevents aliasing issues with references.
constexpr void swap(basic_vector &bv) noexcept requires Writable;
Swap the data using the underlying dimensional_storage_t
's swap
function.
[[nodiscard]] constexpr auto begin() noexcept requires Writable;
[[nodiscard]] constexpr auto begin() const noexcept;
[[nodiscard]] constexpr auto cbegin() const noexcept;
[[nodiscard]] constexpr auto end() noexcept requires Writable;
[[nodiscard]] constexpr auto end() const noexcept;
[[nodiscard]] constexpr auto cend() const noexcept;
[[nodiscard]] constexpr auto rbegin() noexcept requires Writable;
[[nodiscard]] constexpr auto rbegin() const noexcept;
[[nodiscard]] constexpr auto crbegin() const noexcept;
[[nodiscard]] constexpr auto rend() noexcept requires Writable;
[[nodiscard]] constexpr auto rend() const noexcept;
[[nodiscard]] constexpr auto crend() const noexcept;
These contiguous iterators are supplied indirectly by the underlying dimensional_storage_t
. Each data member of the anonymous union, whether a storage_wrapper
or an indexed_vector
, has a single data member of type dimensional_storage_t
. The iterators are accessed through base
.
using sequence_pack = std::make_index_sequence<Count>;
The instantiation of std::index_sequence
that represents the physical to logical mapping. sequence
returns an instance of this type.
template <dimensional_scalar T, std::size_t Size>
constexpr void swap(basic_vector<T, Size> &lhs, basic_vector<T, Size> &rhs) noexcept;
Free function swap
wraps the member function basic_vector::swap
.
template <dimensional_scalar T, dimensional_scalar ...U>
basic_vector(T, U...) -> basic_vector<T, 1 + sizeof...(U)>;
template <bool W, dimensional_scalar T, std::size_t C, typename D>
basic_vector(const vector_base<W, T, C, D> &) -> basic_vector<T, C>;
Class template argument deduction (CTAD) for basic_vector
.
template <floating_point_scalar T, std::size_t C, std::size_t R>
requires (((C >= 2) && (C <= 4)) && ((R >= 2) && (R <= 4)))
struct basic_matrix;
The struct that represents a matrix. The matrix elements are stored column order, as an array of column basic_vector
s. The terminology looks backwards, with number of columns coming before the number of rows, but it makes sense from a data storage perspective. It is also how GLSL does it.
- Template Parameters
T
- type of thebasic_matrix
elements. Must satisfyfloating_point_scalar
.C
- number of columns,where 2 <= C <= 4
.R
- number of rows,where 2 <= R <= 4
.
basic_matrix
Constructors- Non-Static Data Members
- Static Data Members
- Static Member Functions
- Non-Static Member Functions
- Matrix Operators
- Matrix Free Functions
basic_matrix
Free Functions
constexpr basic_matrix() noexcept = default;
constexpr basic_matrix(const basic_matrix &) noexcept = default;
constexpr basic_matrix(basic_matrix &&) noexcept = default;
Default constructors.
template <typename U, typename ... Args>
requires (detail::valid_matrix_component<U, T>::value) && (detail::valid_matrix_component<Args, T>::value && ...) && detail::met_component_count<ComponentCount, U, Args...>
explicit constexpr basic_matrix(const U &u, const Args & ...args) noexcept;
Variadic constructor. Can take a combination of vectors and scalars as arguments to initialize the basic_matrix
.
template <typename U>
requires std::convertible_to<U, T> && (C == R)
explicit constexpr basic_matrix(U arg) noexcept;
Diagonal constructor for square matrices.
template <floating_point_scalar U>
requires implicitly_convertible_to<U, T>
explicit(false) constexpr basic_matrix(const basic_matrix<U, C, R> &arg) noexcept;
template <floating_point_scalar U, std::size_t Cols, std::size_t Rows>
requires implicitly_convertible_to<U, T> && (Cols != C || Rows != R)
explicit(false) constexpr basic_matrix(const basic_matrix<U, Cols, Rows> &arg) noexcept;
template <floating_point_scalar U, std::size_t Cols, std::size_t Rows>
requires (!implicitly_convertible_to<U, T> && std::convertible_to<U, T>)
explicit constexpr basic_matrix(const basic_matrix<U, Cols, Rows> &arg) noexcept;
Constructors that take a matrix as an argument, but not a normal copy constructor (see default constructors).
constexpr basic_matrix(const std::initializer_list<T> &init_list) noexcept;
An intializer list of values. If too few values for the matrix, the rest of the elements will be set to 0. If too many values for the matrix, the rest of the initialization list will be ignored.
std::array<basic_vector<T, R>, C> columns;
The matrix elements' storage.
static constexpr std::size_t ComponentCount = C * R;
The number of matrix elements.
static constexpr std::integral_constant<std::size_t, C> size = {};
Holds the number of columns, which is the row size.
static constexpr std::integral_constant<std::size_t, R> column_size = {};
Holds the number of rows, which is the column size.
[[nodiscard]] static constexpr std::size_t size() const noexcept;
Return the number of columns, which is the row size. The declaration for size()
is a fiction due to the fact that this function does not exist; however, the static std::integral_constant
size
has an operator()()
that operates exactly as the above declaration. This approach of using a std::integral_constant
for size
is supposed to be an up and coming idiom in the C++ standard for all new standard library components with constant sizes.
[[nodiscard]] constexpr std::size_t column_size() const noexcept;
Return the number of rows, which is the column size. The declaration for column_size()
is a fiction due to the fact that this function does not exist; however, the static std::integral_constant
column_size
has an operator()()
that operates exactly as the above declaration. This approach of using a std::integral_constant
for size
(and here also column_size
) is supposed to be an up and coming idiom in the C++ standard for all new standard library components with constant sizes.
[[nodiscard]] constexpr int length() const noexcept;
Return the number of columns. This is also the row length.
The GLSL specification requires that the length method
behave as a member function.
[[nodiscard]] constexpr int column_length() const noexcept;
Return the number of rows.
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr basic_vector<T, R> &operator [](const U &index) noexcept;
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr const basic_vector<T, R> &operator [](const U &index) const noexcept;
Data access through the indexing operator.
template <floating_point_scalar U>
requires implicitly_convertible_to<U, T>
constexpr basic_matrix &operator =(const basic_matrix<U, C, R> &other) & noexcept;
The assignment operator. Can assign only to lvalues.
[[nodiscard]] constexpr basic_vector<T, R> * data() noexcept;
[[nodiscard]] constexpr const basic_vector<T, R> * data() const noexcept;
Data access through pointers.
template <typename U>
requires std::convertible_to<U, std::size_t>
[[nodiscard]] constexpr basic_vector<T, C> row(const U &row_index) const noexcept;
Return a row from the column-order matrix.
constexpr void swap(basic_matrix &bm) noexcept;
Swap the data using the underlying std::array
's swap
function.
[[nodiscard]] constexpr auto begin() noexcept;
[[nodiscard]] constexpr auto begin() const noexcept;
[[nodiscard]] constexpr auto cbegin() const noexcept;
[[nodiscard]] constexpr auto end() noexcept;
[[nodiscard]] constexpr auto end() const noexcept;
[[nodiscard]] constexpr auto cend() const noexcept;
[[nodiscard]] constexpr auto rbegin() noexcept;
[[nodiscard]] constexpr auto rbegin() const noexcept;
[[nodiscard]] constexpr auto crbegin() const noexcept;
[[nodiscard]] constexpr auto rend() noexcept;
[[nodiscard]] constexpr auto rend() const noexcept;
[[nodiscard]] constexpr auto crend() const noexcept;
These contiguous std::array
iterators are accessed through columns
.
template <floating_point_scalar T, std::size_t C, std::size_t R>
constexpr void swap(basic_matrix<T, C, R> &lhs, basic_matrix<T, C, R> &rhs) noexcept;
Free function swap
wraps the member function basic_matrix::swap
.
These instantiations represent the classes (structs) that are provided by GLSL, with a few others not in GLSL.
// boolean vectors
using bscal = basic_vector<bool, 1>;
using bvec2 = basic_vector<bool, 2>;
using bvec3 = basic_vector<bool, 3>;
using bvec4 = basic_vector<bool, 4>;
// int vectors
using iscal = basic_vector<int, 1>;
using ivec2 = basic_vector<int, 2>;
using ivec3 = basic_vector<int, 3>;
using ivec4 = basic_vector<int, 4>;
// unsigned int vectors
using uscal = basic_vector<unsigned, 1>;
using uvec2 = basic_vector<unsigned, 2>;
using uvec3 = basic_vector<unsigned, 3>;
using uvec4 = basic_vector<unsigned, 4>;
// long long vectors (not in GLSL)
using llscal = basic_vector<long long, 1>;
using llvec2 = basic_vector<long long, 2>;
using llvec3 = basic_vector<long long, 3>;
using llvec4 = basic_vector<long long, 4>;
// unsigned long long vectors (not in GLSL)
using ullscal = basic_vector<unsigned long long, 1>;
using ullvec2 = basic_vector<unsigned long long, 2>;
using ullvec3 = basic_vector<unsigned long long, 3>;
using ullvec4 = basic_vector<unsigned long long, 4>;
// float vectors with out an 'f' prefix -- this is from GLSL
using scal = basic_vector<float, 1>;
using vec2 = basic_vector<float, 2>;
using vec3 = basic_vector<float, 3>;
using vec4 = basic_vector<float, 4>;
// also float vectors, but using the common naming convention (not in GLSL)
using fscal = basic_vector<float, 1>;
using fvec2 = basic_vector<float, 2>;
using fvec3 = basic_vector<float, 3>;
using fvec4 = basic_vector<float, 4>;
// double vectors
using dscal = basic_vector<double, 1>;
using dvec2 = basic_vector<double, 2>;
using dvec3 = basic_vector<double, 3>;
using dvec4 = basic_vector<double, 4>;
// float matrices
using mat2x2 = basic_matrix<float, 2, 2>;
using mat2x3 = basic_matrix<float, 2, 3>;
using mat2x4 = basic_matrix<float, 2, 4>;
using mat3x2 = basic_matrix<float, 3, 2>;
using mat3x3 = basic_matrix<float, 3, 3>;
using mat3x4 = basic_matrix<float, 3, 4>;
using mat4x2 = basic_matrix<float, 4, 2>;
using mat4x3 = basic_matrix<float, 4, 3>;
using mat4x4 = basic_matrix<float, 4, 4>;
using mat2 = basic_matrix<float, 2, 2>;
using mat3 = basic_matrix<float, 3, 3>;
using mat4 = basic_matrix<float, 4, 4>;
// double matrices
using dmat2x2 = basic_matrix<double, 2, 2>;
using dmat2x3 = basic_matrix<double, 2, 3>;
using dmat2x4 = basic_matrix<double, 2, 4>;
using dmat3x2 = basic_matrix<double, 3, 2>;
using dmat3x3 = basic_matrix<double, 3, 3>;
using dmat3x4 = basic_matrix<double, 3, 4>;
using dmat4x2 = basic_matrix<double, 4, 2>;
using dmat4x3 = basic_matrix<double, 4, 3>;
using dmat4x4 = basic_matrix<double, 4, 4>;
using dmat2 = basic_matrix<double, 2, 2>;
using dmat3 = basic_matrix<double, 3, 3>;
using dmat4 = basic_matrix<double, 4, 4>;
- Vector Unary Operators
- Vector Binary Operators
- Vector Compound Assignment Operators
- Vector Comparison Operators
template <bool W, non_bool_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto operator +(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto operator -(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
requires W
constexpr auto &operator ++(vector_base<W, T, C, D> &arg) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
requires W
constexpr auto operator ++(vector_base<W, T, C, D> &arg, int) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
requires W
constexpr auto &operator --(vector_base<W, T, C, D> &arg) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
requires W
constexpr auto operator --(vector_base<W, T, C, D> &arg, int) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto operator ~(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C1, typename D1, bool W2, non_bool_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1)
[[nodiscard]] constexpr auto operator +(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator +(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator +(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C1, typename D1, bool W2, non_bool_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1)
[[nodiscard]] constexpr auto operator -(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator -(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator -(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C1, typename D1, bool W2, non_bool_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1)
[[nodiscard]] constexpr auto operator *(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator *(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator *(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C1, typename D1, bool W2, non_bool_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1)
[[nodiscard]] constexpr auto operator /(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator /(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator /(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C1, typename D1, bool W2, numeric_integral_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1)
[[nodiscard]] constexpr auto operator %(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator %(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator %(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C1, typename D1, bool W2, numeric_integral_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1)
[[nodiscard]] constexpr auto operator >>(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator >>(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator >>(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C1, typename D1, bool W2, numeric_integral_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1)
[[nodiscard]] constexpr auto operator <<(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator <<(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>
[[nodiscard]] constexpr auto operator <<(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C1, typename D1, bool W2, numeric_integral_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1) && detail::same_sizeof<T1, T2>
[[nodiscard]] constexpr auto operator &(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires (implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>) && detail::same_sizeof<T, U>
[[nodiscard]] constexpr auto operator &(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires (implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>) && detail::same_sizeof<T, U>
[[nodiscard]] constexpr auto operator &(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C1, typename D1, bool W2, numeric_integral_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1) && detail::same_sizeof<T1, T2>
[[nodiscard]] constexpr auto operator |(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires (implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>) && detail::same_sizeof<T, U>
[[nodiscard]] constexpr auto operator |(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires (implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>) && detail::same_sizeof<T, U>
[[nodiscard]] constexpr auto operator |(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C1, typename D1, bool W2, numeric_integral_scalar T2, std::size_t C2, typename D2>
requires (implicitly_convertible_to<T2, T1> || implicitly_convertible_to<T1, T2>) && (C1 == C2 || C1 == 1 || C2 == 1) && detail::same_sizeof<T1, T2>
[[nodiscard]] constexpr auto operator ^(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires (implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>) && detail::same_sizeof<T, U>
[[nodiscard]] constexpr auto operator ^(const vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires (implicitly_convertible_to<U, T> || implicitly_convertible_to<T, U>) && detail::same_sizeof<T, U>
[[nodiscard]] constexpr auto operator ^(U lhs,
const vector_base<W, T, C, D> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1>
constexpr auto &operator +=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1)
constexpr auto &operator +=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires W && implicitly_convertible_to<U, T>
constexpr auto &operator +=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1>
constexpr auto &operator -=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1)
constexpr auto &operator -=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires W && implicitly_convertible_to<U, T>
constexpr auto &operator -=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1>
constexpr auto &operator *=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1)
constexpr auto &operator *=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires W && implicitly_convertible_to<U, T>
constexpr auto &operator *=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1>
constexpr auto &operator /=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1)
constexpr auto &operator /=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D, non_bool_scalar U>
requires W && implicitly_convertible_to<U, T>
constexpr auto &operator /=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1>
constexpr auto &operator %=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1)
constexpr auto &operator %=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires W && implicitly_convertible_to<U, T>
constexpr auto &operator %=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1>
constexpr auto &operator >>=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1)
constexpr auto &operator >>=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires W && implicitly_convertible_to<U, T>
constexpr auto &operator >>=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1>
constexpr auto &operator <<=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1)
constexpr auto &operator <<=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires W && implicitly_convertible_to<U, T>
constexpr auto &operator <<=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && detail::same_sizeof<T1, T2>
constexpr auto &operator &=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1) && detail::same_sizeof<T1, T2>
constexpr auto &operator &=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires W && implicitly_convertible_to<U, T> && detail::same_sizeof<T, U>
constexpr auto &operator &=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && detail::same_sizeof<T1, T2>
constexpr auto &operator |=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1) && detail::same_sizeof<T1, T2>
constexpr auto &operator |=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires W && implicitly_convertible_to<U, T> && detail::same_sizeof<T, U>
constexpr auto &operator |=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && detail::same_sizeof<T1, T2>
constexpr auto &operator ^=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, C, D2> &rhs) noexcept;
template <bool W1, numeric_integral_scalar T1, std::size_t C, typename D1, bool W2, numeric_integral_scalar T2, typename D2>
requires W1 && implicitly_convertible_to<T2, T1> && (C > 1) && detail::same_sizeof<T1, T2>
constexpr auto &operator ^=(vector_base<W1, T1, C, D1> &lhs,
const vector_base<W2, T2, 1, D2> &rhs) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D, numeric_integral_scalar U>
requires W && implicitly_convertible_to<U, T> && detail::same_sizeof<T, U>
constexpr auto &operator ^=(vector_base<W, T, C, D> &lhs,
U rhs) noexcept;
template <bool W1, dimensional_scalar T1, std::size_t C, typename D1, bool W2, dimensional_scalar T2, typename D2>
requires implicitly_convertible_to<T2, T1>
constexpr bool operator ==(const vector_base<W1, T1, C, D1> &first,
const vector_base<W2, T2, C, D2> &second) noexcept;
template <bool W1, dimensional_scalar T, std::size_t C, typename D1, bool W2, typename D2>
constexpr bool operator ==(const vector_base<W1, T, C, D1> &first,
const vector_base<W2, T, C, D2> &second) noexcept;
If a signature of operator ==
is not matched, c++20
will swap the argument order to see if there is a match.
c++20
automatically creates operator !=
from operator ==
.
We have two different vector types, basic_vector
and indexed_vector
. We want the vector functions take work for both types, so most of the vector functions take instances of the vector types' common base class, vector_base
.
Most of the functions perform their operation component-wise. There are some functions that treat the vector geometrically and treat the components as part of a whole.
- Angle and Trigonometry Functions
- Exponential Functions
- Common Functions
- Geometric Functions
- Vector Relational Functions
- Other Vector Functions
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto radians(const vector_base<W, T, C, D> °) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto degrees(const vector_base<W, T, C, D> &rad) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto sin(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto cos(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto tan(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto asin(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto acos(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto atan(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] inline auto atan(const vector_base<W1, T, C, D1> &y,
const vector_base<W2, T, C, D2> &x) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto sinh(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto cosh(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto tanh(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto asinh(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto acosh(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto atanh(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] inline auto pow(const vector_base<W1, T, C, D1> &base,
const vector_base<W2, T, C, D2> &exp) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto exp(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto log(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto exp2(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] inline auto log2(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto sqrt(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto inversesqrt(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto fast_inversesqrt(const vector_base<W, T, C, D> &arg) noexcept;
Not in GLSL. May or may not actually be faster that rsqrt()
, for which this function is an approximation for. For float
, this function is 100% in agreement with rsqrt()
. For double
, the relationship to rsqrt()
is:
- 0 ulps: ~68.58%
- 1 ulps: ~31.00%
- 2 ulps: ~0.42%
template <bool W, non_bool_scalar T, std::size_t C, typename D>
requires (!unsigned_scalar<T>)
[[nodiscard]] constexpr auto abs(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
requires (!unsigned_scalar<T>)
[[nodiscard]] constexpr auto sign(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto floor(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto trunc(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto round(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto roundEven(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto ceil(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto fract(const vector_base<W, T, C, D> &arg) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto mod(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto mod(const vector_base<W, T, C, D> &x,
T y) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
requires W2
[[nodiscard]] constexpr auto modf(const vector_base<W1, T, C, D1> &arg,
vector_base<W2, T, C, D2> &i) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto min(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto min(const vector_base<W, T, C, D> &x,
T y) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto max(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto max(const vector_base<W, T, C, D> &x,
T y) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2, bool W3, typename D3>
[[nodiscard]] constexpr auto clamp(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &min_val,
const vector_base<W3, T, C, D3> &max_val) noexcept;
template <bool W, non_bool_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto clamp(const vector_base<W, T, C, D> &x,
T min_val,
T max_val) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2, bool W3, typename D3>
[[nodiscard]] constexpr auto mix(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y,
const vector_base<W3, T, C, D3> &a) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto mix(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y,
T a) noexcept;
template <bool W1, dimensional_scalar T, std::size_t C, typename D1, bool W2, typename D2, bool W3, bool_scalar B, typename D3>
[[nodiscard]] constexpr auto mix(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y,
const vector_base<W3, B, C, D3> &a) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto step(const vector_base<W1, T, C, D1> &edge,
const vector_base<W2, T, C, D2> &x) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto step(T edge,
const vector_base<W, T, C, D> &x) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2, bool W3, typename D3>
[[nodiscard]] constexpr auto smoothstep(const vector_base<W1, T, C, D1> &edge0,
const vector_base<W2, T, C, D2> &edge1,
const vector_base<W3, T, C, D3> &x) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto smoothstep(T edge0,
T edge1,
const vector_base<W, T, C, D> &x) noexcept;
template <floating_point_scalar T, std::size_t C>
[[nodiscard]] constexpr auto isnan(const basic_vector<T, C> &arg) noexcept;
template <floating_point_scalar T, std::size_t S, std::size_t C, std::size_t ...Is>
[[nodiscard]] constexpr auto isnan(const indexed_vector<T, S, C, Is...> &arg) noexcept;
These functions can not use vector_base
as the parameter type. They must be specialized for the actual types, not the base type. This is due to the C++ Standard Library implementation, at least for MSVC.
template <floating_point_scalar T, std::size_t C>
[[nodiscard]] constexpr auto isinf(const basic_vector<T, C> &arg) noexcept;
template <floating_point_scalar T, std::size_t S, std::size_t C, std::size_t ...Is>
[[nodiscard]] constexpr auto isinf(const indexed_vector<T, S, C, Is...> &arg) noexcept;
These functions can not use vector_base
as the parameter type. They must be specialized for the actual types, not the base type. This is due to the C++ Standard Library implementation, at least for MSVC.
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto floatBitsToInt(const vector_base<W, float, C, D> &arg) noexcept;
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto floatBitsToUint(const vector_base<W, float, C, D> &arg) noexcept;
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto doubleBitsToLongLong(const vector_base<W, double, C, D> &arg) noexcept;
Not in GLSL.
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto doubleBitsToUlongLong(const vector_base<W, double, C, D> &arg) noexcept;
Not in GLSL.
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto intBitsToFloat(const vector_base<W, int, C, D> &arg) noexcept;
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto uintBitsToFloat(const vector_base<W, unsigned int, C, D> &arg) noexcept;
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto longLongBitsToDouble(const vector_base<W, long long, C, D> &arg) noexcept;
Not in GLSL.
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto ulongLongBitsToDouble(const vector_base<W, unsigned long long, C, D> &arg) noexcept;
Not in GLSL.
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2, bool W3, typename D3>
[[nodiscard]] inline auto fma(const vector_base<W1, T, C, D1> &a,
const vector_base<W2, T, C, D2> &b,
const vector_base<W3, T, C, D3> &c) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
requires W2
[[nodiscard]] inline auto frexp(const vector_base<W1, T, C, D1> &x,
vector_base<W2, int, C, D2> &exp) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] inline auto ldexp(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, int, C, D2> &exp) noexcept;
template <bool W, numeric_integral_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto byteswap(const vector_base<W, T, C, D> &arg) noexcept;
Not in GLSL. This functionality was added to c++23
, but since this is a c++20
library, we have to provide the underlying implementation ourselves.
template <bool W, floating_point_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr auto length(const vector_base<W, T, C, D> &x) noexcept;
template <bool W1, floating_point_scalar T1, std::size_t C, typename D1, bool W2, floating_point_scalar T2, typename D2>
[[nodiscard]] constexpr auto distance(const vector_base<W1, T1, C, D1> &p0,
const vector_base<W2, T2, C, D2> &p1) noexcept;
template <bool W1, non_bool_scalar T1, std::size_t C, typename D1, bool W2, non_bool_scalar T2, typename D2>
[[nodiscard]] constexpr auto innerProduct(const vector_base<W1, T1, C, D1> &x,
const vector_base<W2, T2, C, D2> &y) noexcept;
Not in GLSL. This is just like dot
, except without the floating-point restriction.
template <bool W1, floating_point_scalar T1, std::size_t C, typename D1, bool W2, floating_point_scalar T2, typename D2>
[[nodiscard]] constexpr auto dot(const vector_base<W1, T1, C, D1> &x,
const vector_base<W2, T2, C, D2> &y) noexcept;
template <bool W1, floating_point_scalar T1, typename D1, bool W2, floating_point_scalar T2, typename D2>
[[nodiscard]] constexpr auto cross(const vector_base<W1, T1, 3, D1> &a,
const vector_base<W2, T2, 3, D2> &b) noexcept;
template <floating_point_scalar T1, floating_point_scalar T2>
[[nodiscard]] constexpr auto cross(const basic_vector<T1, 3> &a,
const basic_vector<T2, 3> &b) noexcept;
template <bool W, floating_point_scalar T, std::size_t C, typename D>
requires (C > 1)
[[nodiscard]] constexpr auto normalize(const vector_base<W, T, C, D> &x) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2, bool W3, typename D3>
requires (C > 1)
[[nodiscard]] constexpr auto faceforward(const vector_base<W1, T, C, D1> &n,
const vector_base<W2, T, C, D2> &i,
const vector_base<W3, T, C, D3> &nref) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
requires (C > 1)
[[nodiscard]] constexpr auto reflect(const vector_base<W1, T, C, D1> &i,
const vector_base<W2, T, C, D2> &n) noexcept;
template <bool W1, floating_point_scalar T, std::size_t C, typename D1, bool W2, typename D2>
requires (C > 1)
[[nodiscard]] constexpr auto refract(const vector_base<W1, T, C, D1> &i,
const vector_base<W2, T, C, D2> &n,
T eta) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto lessThan(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto lessThanEqual(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto greaterThan(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto greaterThanEqual(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto equal(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W1, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto equal(const vector_base<W1, bool, C, D1> &x,
const vector_base<W2, bool, C, D2> &y) noexcept;
template <bool W1, non_bool_scalar T, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto notEqual(const vector_base<W1, T, C, D1> &x,
const vector_base<W2, T, C, D2> &y) noexcept;
template <bool W1, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto notEqual(const vector_base<W1, bool, C, D1> &x,
const vector_base<W2, bool, C, D2> &y) noexcept;
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr bool any(const vector_base<W, bool, C, D> &x) noexcept;
Not a component-wise operation. Relies on all values.
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr bool all(const vector_base<W, bool, C, D> &x) noexcept;
Not a component-wise operation. Relies on all values.
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr bool none(const vector_base<W, bool, C, D> &x) noexcept;
Not in GLSL. Same effect as !any(vec)
.
Not a component-wise operation. Relies on all values.
template <bool W, std::size_t C, typename D>
[[nodiscard]] constexpr auto compNot(const vector_base<W, bool, C, D> &x) noexcept;
This function takes the place of GLSL function not
. We can't define a function named not
in C++ because it is a reserved keyword. This performs a component-wise ```not`` operation on the boolean inputs.
template <bool W1, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto compAnd(const vector_base<W1, bool, C, D1> &x,
const vector_base<W2, bool, C, D2> &y) noexcept;
Not in GLSL. The function returns a vector from performing component-wise and
operations of the boolean inputs.
template <bool W1, std::size_t C, typename D1, bool W2, typename D2>
[[nodiscard]] constexpr auto compOr(const vector_base<W1, bool, C, D1> &x,
const vector_base<W2, bool, C, D2> &y) noexcept;
Not in GLSL. The function returns a vector from performing component-wise or
operations of the boolean inputs.
template <bool W, dimensional_scalar T, std::size_t C, typename D, typename Arg>
requires std::convertible_to<Arg, std::size_t>
inline auto swizzle(const vector_base<W, T, C, D> &v, const Arg &index);
template <bool W, dimensional_scalar T, std::size_t C, typename D, typename ...Args>
requires (std::convertible_to<Args, std::size_t> && ...) && (sizeof...(Args) > 0) && (sizeof...(Args) <= 4)
inline basic_vector<T, sizeof...(Args)> swizzle(const vector_base<W, T, C, D> &v, const Args &...Is);
Not in GLSL. Runtime function for swizzling. Returns a stand-alone dsga::basic_vector
version of a swizzle, instead of a dsga::indexed_vector
data member. Will return a scalar value if only one index argument. If the index arguments are invalid (out of bounds), this function will throw a std::out_of_range()
exception. Inspired by the Odin Programming Language.
Scalar versions of most of the vector free functions exist. It is not recommended to use them if there is a function in the C++ Standard Library that does the same thing.
The matrix unary and binary operators (though not the linear algebraic operators) operate on the matrices component-wise.
The linear algebraic binary operators use linear algebraic concepts to multiply matrices and vectors.
- Matrix Unary Operators
- Matrix Binary Operators
operator +
operator -
operator *
operator /
- Matrix Linear Algebraic Operations
- Matrix Comparison Operators
template <floating_point_scalar T, std::size_t C, std::size_t R>
[[nodiscard]] constexpr auto operator +(const basic_matrix<T, C, R> &arg) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R>
[[nodiscard]] constexpr auto operator -(const basic_matrix<T, C, R> &arg) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R>
constexpr auto &operator ++(basic_matrix<T, C, R> &arg) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R>
constexpr auto operator ++(basic_matrix<T, C, R> &arg, int) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R>
constexpr auto &operator --(basic_matrix<T, C, R> &arg) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R>
constexpr auto operator --(basic_matrix<T, C, R> &arg, int) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator +(const basic_matrix<T, C, R> &lhs,
U rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator +(U lhs,
const basic_matrix<T, C, R> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, floating_point_scalar U>
[[nodiscard]] constexpr auto operator +(const basic_matrix<T, C, R> &lhs,
const basic_matrix<U, C, R> &rhs) noexcept
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator -(const basic_matrix<T, C, R> &lhs,
U rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator -(U lhs,
const basic_matrix<T, C, R> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, floating_point_scalar U>
[[nodiscard]] constexpr auto operator -(const basic_matrix<T, C, R> &lhs,
const basic_matrix<U, C, R> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator *(const basic_matrix<T, C, R> &lhs,
U rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator *(U lhs,
const basic_matrix<T, C, R> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator /(const basic_matrix<T, C, R> &lhs,
U rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, non_bool_scalar U>
[[nodiscard]] constexpr auto operator /(U lhs,
const basic_matrix<T, C, R> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, floating_point_scalar U>
[[nodiscard]] constexpr auto operator /(const basic_matrix<T, C, R> &lhs,
const basic_matrix<U, C, R> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, bool W, non_bool_scalar U, typename D>
[[nodiscard]] constexpr auto operator *(const vector_base<W, U, R, D> &lhs,
const basic_matrix<T, C, R> &rhs) noexcept;
For performing a vector * matrix
operation, the vector is treated as if it were transposed, i.e., a row vector, as is the result.
template <floating_point_scalar T, std::size_t C, std::size_t R, bool W, non_bool_scalar U, typename D>
[[nodiscard]] constexpr auto operator *(const basic_matrix<T, C, R> &lhs,
const vector_base<W, U, C, D> &rhs) noexcept;
For performing a matrix * vector
operation, the vector is treated as if it were a column vector, as is the result.
template <floating_point_scalar T, std::size_t C1, std::size_t R1, floating_point_scalar U, std::size_t C2, std::size_t R2>
requires (C1 == R2)
[[nodiscard]] constexpr auto operator *(const basic_matrix<T, C1, R1> &lhs,
const basic_matrix<U, C2, R2> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R, floating_point_scalar U>
requires implicitly_convertible_to<U, T>
constexpr bool operator ==(const basic_matrix<T, C, R> &lhs,
const basic_matrix<U, C, R> &rhs) noexcept;
If a signature of operator ==
is not matched, c++20
will swap the argument order to see if there is a match.
c++20
automatically creates operator !=
from operator ==
.
The matrix functions treat a matrix as an entity instead of as a collection of components, except for matrixCompMult
, which works component-wise.
template <floating_point_scalar T, std::size_t C, std::size_t R, floating_point_scalar U>
[[nodiscard]] constexpr auto matrixCompMult(const basic_matrix<T, C, R> &lhs,
const basic_matrix<U, C, R> &rhs) noexcept;
This function exists because operator *
is used for linear algebraic purposes instead of component-wise multiplication.
template <bool W1, non_bool_scalar T1, std::size_t C1, typename D1, bool W2, non_bool_scalar T2, std::size_t C2, typename D2>
requires (floating_point_scalar<T1> || floating_point_scalar<T2>) && ((C1 >= 2) && (C1 <= 4)) && ((C2 >= 2) && (C2 <= 4))
[[nodiscard]] constexpr auto outerProduct(const vector_base<W1, T1, C1, D1> &lhs,
const vector_base<W2, T2, C2, D2> &rhs) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R>
[[nodiscard]] constexpr basic_matrix<T, R, C> transpose(const basic_matrix<T, C, R> &arg) noexcept;
template <floating_point_scalar T, std::size_t C>
requires ((2 <= C) && (C <= 4))
[[nodiscard]] constexpr auto determinant(const basic_matrix<T, C, C> &arg) noexcept;
template <floating_point_scalar T>
requires ((2 <= C) && (C <= 4))
[[nodiscard]] constexpr auto inverse(const basic_matrix<T, C, C> &arg) noexcept;
template <bool W, floating_point_scalar T, typename D>
[[nodiscard]] constexpr basic_matrix<T, 3, 3> cross_matrix(const vector_base<W, T, 3, D> &vec) noexcept;
cross(u, v) == cross_matrix(u) * v == u * cross_matrix(v)
. This creates a matrix that can be used to compute the cross product when multiplied by a vector. This is not in GLSL.
template <bool W, floating_point_scalar T, std::size_t C, typename D>
requires (C > 1)
[[nodiscard]] constexpr basic_matrix<T, C, C> diagonal_matrix(const vector_base<W, T, C, D> &vec) noexcept;
This creates a symmetric diagonal matrix (square matrix) using the vector parameter for the diagonal values, with all other matrix elements having value 0. This is not in GLSL.
These functions allow the vector and matrix classes in namespace dsga
to interoperate with C++ array types, for both producing and consuming.
template <dimensional_scalar T, std::size_t S>
requires dimensional_storage<T, S>
[[nodiscard]] constexpr basic_vector<T, S> to_vector(const std::array<T, S> &arg) noexcept;
template <dimensional_scalar T, std::size_t S>
requires dimensional_storage<T, S>
[[nodiscard]] constexpr basic_vector<T, S> to_vector(const T(&arg)[S]) noexcept;
Convenience functions for converting a std::array
or C-style array
to a basic_vector
. The array types must satisfy the dimensional_storage
concept.
template <bool W, dimensional_scalar T, std::size_t C, typename D>
[[nodiscard]] constexpr std::array<T, C> to_array(const vector_base<W, T, C, D> &arg) noexcept;
template <floating_point_scalar T, std::size_t C, std::size_t R>
requires (((C >= 2) && (C <= 4)) && ((R >= 2) && (R <= 4)))
[[nodiscard]] constexpr std::array<T, C * R> to_array(const basic_matrix<T, C, R> &arg) noexcept;
Convenience functions for converting a vector or matrix to a std::array
. For the vector classes, the elements are written to the array in logical order (as opposed to physical order). For basic_matrix
, the elements are written to the array in column-order.
template <std::size_t C, std::size_t R, floating_point_scalar T, std::size_t S>
requires (((C >= 2) && (C <= 4)) && ((R >= 2) && (R <= 4))) && (C * R <= S)
[[nodiscard]] constexpr dsga::basic_matrix<T, C, R> to_matrix(const std::array<T, S> &arg) noexcept;
template <std::size_t C, std::size_t R, floating_point_scalar T, std::size_t S>
requires (((C >= 2) && (C <= 4)) && ((R >= 2) && (R <= 4))) && (C * R <= S)
[[nodiscard]] constexpr dsga::basic_matrix<T, C, R> to_matrix(const T(&arg)[S]) noexcept;
Convenience functions for converting a std::array
or C-style array
to a basic_matrix
. The array types must store the data in column-order.
The non-iterator classes in namespace dsga
support the tuple protocol. The most important use case is for structured bindings.
// storage_wrapper
template <int N, dimensional_scalar T, std::size_t S>
requires (N >= 0) && (N < S)
[[nodiscard]] constexpr auto & get(storage_wrapper<T, S> & arg) noexcept;
template <int N, dimensional_scalar T, std::size_t S>
requires (N >= 0) && (N < S)
[[nodiscard]] constexpr const auto & get(const storage_wrapper<T, S> & arg) noexcept;
template <int N, dimensional_scalar T, std::size_t S>
requires (N >= 0) && (N < S)
[[nodiscard]] constexpr auto && get(storage_wrapper<T, S> && arg) noexcept;
template <int N, dimensional_scalar T, std::size_t S>
requires (N >= 0) && (N < S)
[[nodiscard]] constexpr const auto && get(const storage_wrapper<T, S> && arg) noexcept;
// vector_base -- covers use for basic_vector and indexed_vector
template <int N, bool W, dimensional_scalar T, std::size_t C, typename D>
requires W && (N >= 0) && (N < C)
[[nodiscard]] constexpr auto & get(vector_base<W, T, C, D> & arg) noexcept;
template <int N, bool W, dimensional_scalar T, std::size_t C, typename D>
requires (N >= 0) && (N < C)
[[nodiscard]] constexpr const auto & get(const vector_base<W, T, C, D> & arg) noexcept;
template <int N, bool W, dimensional_scalar T, std::size_t C, typename D>
requires (N >= 0) && (N < C)
[[nodiscard]] constexpr auto && get(vector_base<W, T, C, D> && arg) noexcept;
template <int N, bool W, dimensional_scalar T, std::size_t C, typename D>
requires (N >= 0) && (N < C)
[[nodiscard]] constexpr const auto && get(const vector_base<W, T, C, D> && arg) noexcept;
// basic_matrix
template <int N, dimensional_scalar T, std::size_t C, std::size_t R>
requires (N >= 0) && (N < C)
[[nodiscard]] constexpr auto & get(dsga::basic_matrix<T, C, R> & arg) noexcept;
template <int N, dimensional_scalar T, std::size_t C, std::size_t R>
requires (N >= 0) && (N < C)
[[nodiscard]] constexpr const auto & get(const dsga::basic_matrix<T, C, R> & arg) noexcept;
template <int N, dimensional_scalar T, std::size_t C, std::size_t R>
requires (N >= 0) && (N < C)
[[nodiscard]] constexpr auto && get(dsga::basic_matrix<T, C, R> && arg) noexcept;
template <int N, dimensional_scalar T, std::size_t C, std::size_t R>
requires (N >= 0) && (N < C)
[[nodiscard]] constexpr const auto && get(const dsga::basic_matrix<T, C, R> && arg) noexcept;
The get
free functions for all ref-qualifier versions of arguments, defined in namespace dsga
. This allows ADL for finding the correct get
function.
template<dsga::dimensional_scalar T, std::size_t S>
struct std::tuple_size<dsga::storage_wrapper<T, S>> : std::integral_constant<std::size_t, S>;
template<dsga::dimensional_scalar T, std::size_t S>
struct std::tuple_size<dsga::basic_vector<T, S>> : std::integral_constant<std::size_t, S>;
template <dsga::dimensional_scalar T, std::size_t S, std::size_t C, std::size_t ...Is>
struct std::tuple_size<dsga::indexed_vector<T, S, C, Is...>> : std::integral_constant<std::size_t, C>;
template <bool W, dsga::dimensional_scalar T, std::size_t C, typename D>
struct std::tuple_size<dsga::vector_base<W, T, C, D>> : std::integral_constant<std::size_t, C>;
template <dsga::floating_point_scalar T, std::size_t C, std::size_t R>
struct std::tuple_size<dsga::basic_matrix<T, C, R>> : std::integral_constant<std::size_t, C>;
Specialized versions of std::tuple_size
for namespace dsga
classes/structs.
template <std::size_t I, dsga::dimensional_scalar T, std::size_t S>
struct std::tuple_element<I, dsga::storage_wrapper<T, S>>;
template <std::size_t I, dsga::dimensional_scalar T, std::size_t S>
struct std::tuple_element<I, dsga::basic_vector<T, S>>;
template <std::size_t I, dsga::dimensional_scalar T, std::size_t S, std::size_t C, std::size_t ...Is>
struct std::tuple_element<I, dsga::indexed_vector<T, S, C, Is...>>;
template <std::size_t I, bool W, dsga::dimensional_scalar T, std::size_t C, typename D>
struct std::tuple_element<I, dsga::vector_base<W, T, C, D>>;
template <std::size_t I, dsga::floating_point_scalar T, std::size_t C, std::size_t R>
struct std::tuple_element<I, dsga::basic_matrix<T, C, R>>;
Specialized versions of std::tuple_element
for namespace dsga
classes/structs.