:last-update-label!: :icons: font :prewrap!: :docinfo: shared :stylesheet: zajo-dark.css :source-highlighter: rouge ifdef::backend-pdf[] = QVM endif::[] ifndef::backend-pdf[] = QVM pass:[
] endif::[] Quaternion / Vector / Matrix Library for {CPP}-11 | Emil Dotchevski ifndef::backend-pdf[] :toc: left :toclevels: 3 :toc-title: [.text-right] https://github.com/boostorg/qvm[GitHub] | link:./qvm.pdf[PDF] endif::[] [abstract] == Abstract QVM is a generic library for working with Quaternions, Vectors and Matrices of static size. Features: ==== * Emphasis on 2, 3 and 4-dimensional operations needed in graphics, video games and simulation applications. * Free function templates operate on any compatible user-defined quaternion, vector or matrix type. * Quaternion, vector and matrix types from different libraries or subsystems can be safely mixed in the same expression. * Type-safe mapping between compatible lvalue types with no temporary objects; e.g. transpose remaps the elements, rather than transforming the matrix. ==== ifndef::backend-pdf[] [.text-right] <{ typedef <> scalar_type; template static inline scalar_type read_element( Quaternion const & q ); template static inline scalar_type & write_element( Quaternion & q ); }; */ } } ---- The `quat_traits` template must be specialized for (user-defined) quaternion types in order to enable quaternion operations defined in QVM headers for objects of those types. NOTE: QVM quaternion operations do not require that quaternion types are copyable. The main `quat_traits` template members are not specified. Valid specializations are required to define the following members: - `scalar_type`: the expression `quat_traits ::scalar_type` must be a value type which satisfies the < >. In addition, valid specializations of the `quat_traits` template must define at least one of the following access functions as static members, where `q` is an object of type `Quaternion`, and `I` is compile-time integer constant: - `read_element`: the expression `quat_traits ::read_element(q)` returns either a copy of or a `const` reference to the `I`-th element of `q`. - `write_element`: the expression `quat_traits ::write_element(q)` returns mutable reference to the `I`-th element of `q`. NOTE: For the quaternion `a + bi + cj + dk`, the elements are assumed to be in the following order: `a`, `b`, `c`, `d`; that is, `I`=`0`/`1`/`2`/`3` would access `a`/`b`/`c`/`d`. It is illegal to call any of the above functions unless `is_quat ::value` is true. Even then, quaternion types are allowed to define only a subset of the access functions. Below is an example of a user-defined quaternion type, and its corresponding specialization of the quat_traits template: [source,c++] ---- #include struct fquat { float a[4]; }; namespace boost { namespace qvm { template <> struct quat_traits { typedef float scalar_type; template static inline scalar_type & write_element( fquat & q ) { return q.a[I]; } template static inline scalar_type read_element( fquat const & q ) { return q.a[I]; } }; } } ---- Equivalently, using the < > template the above can be shortened to: [source,c++] ---- namespace boost { namespace qvm { template <> struct quat_traits : quat_traits_defaults { template static inline scalar_type & write_element( fquat & q ) { return q.a[I]; } }; } } ---- ''' [[quat_traits_defaults]] ==== `quat_traits_defaults` .#include [source,c++] ---- namespace boost { namespace qvm { template struct quat_traits_defaults { typedef QuatType quat_type; typedef ScalarType scalar_type; template static BOOST_QVM_INLINE_CRITICAL scalar_type read_element( quat_type const & x ) { return quat_traits ::template write_element(const_cast (x)); } }; } } ---- The `quat_traits_defaults` template is designed to be used as a public base for user-defined specializations of the < > template, to easily define the required members. If it is used, the only member that must be defined by the user in a `quat_traits` specialization is `write_element`; the `quat_traits_defaults` base will define `read_element`, as well as `scalar_type` automatically. ''' [[deduce_quat]] ==== `deduce_quat` .#include [source,c++] ---- namespace boost { namespace qvm { template struct deduce_quat { typedef Q type; }; } } ---- Requires: :: - `< > ::value` is `true`; - `<> ::type>::value` must be `true`; - `deduce_quat ::type` must be copyable. This template is used by QVM whenever it needs to deduce a copyable quaternion type from a single user-supplied function parameter of quaternion type. Note that `Q` itself may be non-copyable. The main template definition returns `Q`, which means that it is suitable only for copyable quaternion types. QVM also defines (partial) specializations for the non-copyable quaternion types it produces. Users can define other (partial) specializations for their own types. A typical use of the `deduce_quat` template is for specifying the preferred quaternion type to be returned by the generic function template overloads in QVM depending on the type of their arguments. ''' [[deduce_quat2]] ==== `deduce_quat2` .#include[source,c++] ---- namespace boost { namespace qvm { template struct deduce_quat2 { typedef /*unspecified*/ type; }; } } ---- Requires: :: - Both `< >::type` and `scalar::type` are well defined; - `< >::value` || `is_quat::value` is `true`; - `is_quat ::type>::value` must be `true`; - `deduce_quat2::type` must be copyable. This template is used by QVM whenever it needs to deduce a quaternion type from the types of two user-supplied function parameters. The returned type must have accessible copy constructor (the `A` and `B` types themselves could be non-copyable, and either one of them may not be a quaternion type.) The main template definition returns an unspecified quaternion type with < > obtained by `< >::type`, except if `A` and `B` are the same quaternion type `Q`, in which case `Q` is returned, which is only suitable for copyable types. QVM also defines (partial) specializations for the non-copyable quaternion types it produces. Users can define other (partial) specializations for their own types. A typical use of the `deduce_quat2` template is for specifying the preferred quaternion type to be returned by the generic function template overloads in QVM depending on the type of their arguments. ''' [[is_vec]] ==== `is_vec` .#include [source,c++] ---- namespace boost { namespace qvm { template struct is_vec { static bool const value = false; }; } } ---- This type template defines a compile-time boolean constant value which can be used to determine whether a type `T` is a vector type. For vector types, the < > template can be used to access their elements generically, or to obtain their dimension and `scalar type`. ''' [[vec_traits]] ==== `vec_traits` .#include [source,c++] ---- namespace boost { namespace qvm { template struct vec_traits { /*main template members unspecified*/ }; /* User-defined (possibly partial) specializations: template <> struct vec_traits { static int const dim = < >; typedef < > scalar_type; template static inline scalar_type read_element( Vector const & v ); template static inline scalar_type & write_element( Vector & v ); static inline scalar_type read_element_idx( int i, Vector const & v ); static inline scalar_type & write_element_idx( int i, Vector & v ); }; */ } } ---- The `vec_traits` template must be specialized for (user-defined) vector types in order to enable vector and matrix operations defined in QVM headers for objects of those types. NOTE: QVM vector operations do not require that vector types are copyable. The main `vec_traits` template members are not specified. Valid specializations are required to define the following members: - `dim`: the expression `vec_traits ::dim` must evaluate to a compile-time integer constant greater than 0 that specifies the vector size. - `scalar_type`: the expression `vec_traits ::scalar_type` must be a value type which satisfies the < >. In addition, valid specializations of the `vec_traits` template may define the following access functions as static members, where `v` is an object of type `Vector`, `I` is a compile-time integer constant, and `i` is a variable of type `int`: - `read_element`: the expression `vec_traits ::read_element(v)` returns either a copy of or a const reference to the `I`-th element of `v`. - `write_element`: the expression `vec_traits ::write_element(v)` returns mutable reference to the `I`-th element of `v`. - `read_element_idx`: the expression `vec_traits ::read_element_idx(i,v)` returns either a copy of or a `const` reference to the `i`-th element of `v`. - `write_element_idx`: the expression `vec_traits ::write_element_idx(i,v)` returns mutable reference to the `i`-th element of `v`. It is illegal to call any of the above functions unless `is_vec ::value` is true. Even then, vector types are allowed to define only a subset of the access functions. The general requirements are: - At least one of `read_element` or `write_element` must be defined; - If `read_element_idx` is defined, `read_element` must also be defined; - If `write_element_idx` is defined, `write_element` must also be defined. Below is an example of a user-defined 3D vector type, and its corresponding specialization of the `vec_traits` template: [source,c++] ---- #include struct float3 { float a[3]; }; namespace boost { namespace qvm { template <> struct vec_traits { static int const dim=3; typedef float scalar_type; template static inline scalar_type & write_element( float3 & v ) { return v.a[I]; } template static inline scalar_type read_element( float3 const & v ) { return v.a[I]; } static inline scalar_type & write_element_idx( int i, float3 & v ) { return v.a[i]; } //optional static inline scalar_type read_element_idx( int i, float3 const & v ) { return v.a[i]; } //optional }; } } ---- Equivalently, using the < > template the above can be shortened to: [source,c++] ---- namespace boost { namespace qvm { template <> struct vec_traits : vec_traits_defaults { template static inline scalar_type & write_element( float3 & v ) { return v.a[I]; } static inline scalar_type & write_element_idx( int i, float3 & v ) { return v.a[i]; } //optional }; } } ---- ''' [[vec_traits_defaults]] ==== `vec_traits_defaults` .#include [source,c++] ---- namespace boost { namespace qvm { template struct vec_traits_defaults { typedef VecType vec_type; typedef ScalarType scalar_type; static int const dim=Dim; template static BOOST_QVM_INLINE_CRITICAL scalar_type write_element( vec_type const & x ) { return vec_traits ::template write_element(const_cast (x)); } static BOOST_QVM_INLINE_CRITICAL scalar_type read_element_idx( int i, vec_type const & x ) { return vec_traits ::write_element_idx(i,const_cast (x)); } protected: static BOOST_QVM_INLINE_TRIVIAL scalar_type & write_element_idx( int i, vec_type & m ) { /* unspecified */ } }; } } ---- The `vec_traits_defaults` template is designed to be used as a public base for user-defined specializations of the < > template, to easily define the required members. If it is used, the only member that must be defined by the user in a `vec_traits` specialization is `write_element`; the `vec_traits_defaults` base will define `read_element`, as well as `scalar_type` and `dim` automatically. Optionally, the user may also define `write_element_idx`, in which case the `vec_traits_defaults` base will provide a suitable `read_element_idx` definition automatically. If not, `vec_traits_defaults` defines a protected implementation of `write_element_idx` which may be made publicly available by the deriving `vec_traits` specialization in case the vector type for which it is being specialized can not be indexed efficiently. This `write_element_idx` function is less efficient (using meta-programming), implemented in terms of the required user-defined `write_element`. ''' [[deduce_vec]] ==== `deduce_vec` .#include [source,c++] ---- namespace boost { namespace qvm { template ::dim> struct deduce_vec { typedef /*unspecified*/ type; }; } } ---- Requires: :: - `< > ::value` is `true`; - `is_vec ::type>::value` must be `true`; - `deduce_vec ::type` must be copyable; - `vec_traits ::type>::dim==Dim`. This template is used by QVM whenever it needs to deduce a copyable vector type of certain dimension from a single user-supplied function parameter of vector type. The returned type must have accessible copy constructor. Note that `V` may be non-copyable. The main template definition returns an unspecified copyable vector type of size `Dim`, except if `< > ::dim==Dim`, in which case it returns `V`, which is suitable only if `V` is a copyable type. QVM also defines (partial) specializations for the non-copyable vector types it produces. Users can define other (partial) specializations for their own types. A typical use of the `deduce_vec` template is for specifying the preferred vector type to be returned by the generic function template overloads in QVM depending on the type of their arguments. ''' [[deduce_vec2]] ==== `deduce_vec2` .#include [source,c++] ---- namespace boost { namespace qvm { template struct deduce_vec2 { typedef /*unspecified*/ type; }; } } ---- Requires: :: - Both `< >::type` and `scalar::type` are well defined; - `< >::value || is_vec::value` is `true`; - `is_vec ::type>::value` must be `true`; - `deduce_vec2::type` must be copyable; - `vec_traits ::type>::dim==Dim`. This template is used by QVM whenever it needs to deduce a vector type of certain dimension from the types of two user-supplied function parameters. The returned type must have accessible copy constructor (the `A` and `B` types themselves could be non-copyable, and either one of them may not be a vector type.) The main template definition returns an unspecified vector type of the requested dimension with < > obtained by `< >::type`, except if `A` and `B` are the same vector type `V` of dimension `Dim`, in which case `V` is returned, which is only suitable for copyable types. QVM also defines (partial) specializations for the non-copyable vector types it produces. Users can define other (partial) specializations for their own types. A typical use of the `deduce_vec2` template is for specifying the preferred vector type to be returned by the generic function template overloads in QVM depending on the type of their arguments. ''' [[is_mat]] ==== `is_mat` .#include [source,c++] ---- namespace boost { namespace qvm { template struct is_mat { static bool const value = false; }; } } ---- This type template defines a compile-time boolean constant value which can be used to determine whether a type `T` is a matrix type. For matrix types, the < > template can be used to access their elements generically, or to obtain their dimensions and scalar type. ''' [[mat_traits]] ==== `mat_traits` .#include [source,c++] ---- namespace boost { namespace qvm { template struct mat_traits { /*main template members unspecified*/ }; /* User-defined (possibly partial) specializations: template <> struct mat_traits { static int const rows = < >; static int const cols = < >; typedef < > scalar_type; template static inline scalar_type read_element( Matrix const & m ); template static inline scalar_type & write_element( Matrix & m ); static inline scalar_typeread_element_idx( int r, int c, Matrix const & m ); static inline scalar_type & write_element_idx( int r, int c, Matrix & m ); }; */ } } ---- The `mat_traits` template must be specialized for (user-defined) matrix types in order to enable vector and matrix operations defined in QVM headers for objects of those types. NOTE: The matrix operations defined by QVM do not require matrix types to be copyable. The main `mat_traits` template members are not specified. Valid specializations are required to define the following members: - `rows`: the expression `mat_traits ::rows` must evaluate to a compile-time integer constant greater than 0 that specifies the number of rows in a matrix. - `cols` must evaluate to a compile-time integer constant greater than 0 that specifies the number of columns in a matrix. - `scalar_type`: the expression `mat_traits ::scalar_type` must be a value type which satisfies the scalar requirements. In addition, valid specializations of the `mat_traits` template may define the following access functions as static members, where `m` is an object of type `Matrix`, `R` and `C` are compile-time integer constants, and `r` and `c` are variables of type `int`: - `read_element`: the expression `mat_traits ::read_element (m)` returns either a copy of or a const reference to the element at row `R` and column `C` of `m`. - `write_element`: the expression `mat_traits ::write_element (m)` returns mutable reference to the element at row `R` and column `C` of `m`. - `read_element_idx`: the expression `mat_traits ::read_element_idx(r,c,m)` returns either a copy of or a const reference to the element at row `r` and column `c` of `m`. - `write_element_idx`: the expression `mat_traits ::write_element_idx(r,c,m)` returns mutable reference to the element at row `r` and column `c` of `m`. It is illegal to call any of the above functions unless `is_mat ::value` is true. Even then, matrix types are allowed to define only a subset of the access functions. The general requirements are: - At least one of `read_element` or `write_element` must be defined; - If `read_element_idx` is defined, `read_element` must also be defined; - If `write_element_idx` is defined, `write_element` must also be defined. Below is an example of a user-defined 3x3 matrix type, and its corresponding specialization of the `mat_traits` template: [source,c++] ---- #include struct float33 { float a[3][3]; }; namespace boost { namespace qvm { template <> struct mat_traits { static int const rows=3; static int const cols=3; typedef float scalar_type; template static inline scalar_type & write_element( float33 & m ) { return m.a[R][C]; } template