Files
fennec/include/fennec/math/matrix.h

702 lines
23 KiB
C++

// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright (C) 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \file matrix.h
/// \brief the \ref fennec_math_matrix
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_MATH_MATRIX_H
#define FENNEC_MATH_MATRIX_H
///
///
/// \page fennec_math_matrix Matrices
///
/// \brief The fennec Matrix Math Module
///
/// \code #include <fennec/math/matrix.h> \endcode
///
///
///
///
#include <fennec/math/detail/__fwd.h>
#include <fennec/math/detail/__matrix.h>
#include <fennec/containers/array.h>
#include <fennec/math/geometric.h>
#include <fennec/math/vector_traits.h>
namespace fennec
{
///
/// \brief returns a **copy** of the column \f$i\f$ of matrix \f$m\f$
/// \param m the matrix
/// \param i the index of the row
/// \returns a **copy** of the column at index \f$i\f$
template<typename scalar, size_t rows, size_t...cols>
constexpr vec<scalar, rows> column(const matrix<scalar, rows, cols...>& m, size_t i) noexcept
{ return m[i]; }
///
/// \brief returns a **copy** of the row \f$i\f$ of matrix \f$m\f$
/// \param m the matrix
/// \param i the index of the row
/// \returns a **copy** of the row at index \f$i\f$
template<typename scalar, size_t rows, size_t...cols>
constexpr vec<scalar, sizeof...(cols)> row(const matrix<scalar, rows, cols...>& m, size_t i) noexcept
{ return vec<scalar, sizeof...(cols)>(m[cols][i]...); }
template<typename ScalarT>
using tmat2x2 = mat<ScalarT, 2, 2>; ///< helper for creating 2x2 matrices of the specified type.
template<typename ScalarT> using tmat2x3 = mat<ScalarT, 2, 3>; ///< helper for creating 2x3 matrices of the specified type.
template<typename ScalarT> using tmat2x4 = mat<ScalarT, 2, 4>; ///< helper for creating 2x4 matrices of the specified type.
template<typename ScalarT> using tmat3x2 = mat<ScalarT, 3, 2>; ///< helper for creating 3x2 matrices of the specified type.
template<typename ScalarT> using tmat3x3 = mat<ScalarT, 3, 3>; ///< helper for creating 3x3 matrices of the specified type.
template<typename ScalarT> using tmat3x4 = mat<ScalarT, 3, 4>; ///< helper for creating 3x4 matrices of the specified type.
template<typename ScalarT> using tmat4x2 = mat<ScalarT, 4, 2>; ///< helper for creating 4x2 matrices of the specified type.
template<typename ScalarT> using tmat4x3 = mat<ScalarT, 4, 3>; ///< helper for creating 4x3 matrices of the specified type.
template<typename ScalarT> using tmat4x4 = mat<ScalarT, 4, 4>; ///< helper for creating 4x4 matrices of the specified type.
using mat2 = tmat2x2<float_t>; ///< Specification for glsl float matrices
using mat3 = tmat3x3<float_t>; ///< Specification for glsl float matrices
using mat4 = tmat4x4<float_t>; ///< Specification for glsl float matrices
using mat2x2 = tmat2x2<float_t>; ///< Specification for sized glsl float matrices
using mat2x3 = tmat2x3<float_t>; ///< Specification for sized glsl float matrices
using mat2x4 = tmat2x4<float_t>; ///< Specification for sized glsl float matrices
using mat3x2 = tmat3x2<float_t>; ///< Specification for sized glsl float matrices
using mat3x3 = tmat3x3<float_t>; ///< Specification for sized glsl float matrices
using mat3x4 = tmat3x4<float_t>; ///< Specification for sized glsl float matrices
using mat4x2 = tmat4x2<float_t>; ///< Specification for sized glsl float matrices
using mat4x3 = tmat4x3<float_t>; ///< Specification for sized glsl float matrices
using mat4x4 = tmat4x4<float_t>; ///< Specification for sized glsl float matrices
using dmat2 = tmat2x2<double_t>; ///< Specification for glsl double matrices
using dmat3 = tmat3x3<double_t>; ///< Specification for glsl double matrices
using dmat4 = tmat3x3<double_t>; ///< Specification for glsl double matrices
using dmat2x2 = tmat2x2<double_t>; ///< Specification for size glsl double matrices
using dmat2x3 = tmat2x3<double_t>; ///< Specification for size glsl double matrices
using dmat2x4 = tmat2x4<double_t>; ///< Specification for size glsl double matrices
using dmat3x2 = tmat3x2<double_t>; ///< Specification for size glsl double matrices
using dmat3x3 = tmat3x3<double_t>; ///< Specification for size glsl double matrices
using dmat3x4 = tmat3x4<double_t>; ///< Specification for size glsl double matrices
using dmat4x2 = tmat4x2<double_t>; ///< Specification for size glsl double matrices
using dmat4x3 = tmat4x3<double_t>; ///< Specification for size glsl double matrices
using dmat4x4 = tmat4x4<double_t>; ///< Specification for size glsl double matrices
///
/// \brief Multiply matrix \$x\f$ by matrix \f$y\f$ component-wise.
/// \details Multiply matrix x by matrix y component-wise, i.e., result[i][j] is the scalar product of x[i][j] and y[i][j].<br><br>
/// Note: to get linear algebraic matrix multiplication, use
/// the multiply operator (*)
/// \param x the first matrix
/// \param y the second matrix
/// \returns the resulting product in a matrix of the same size and base type
template<typename scalar, size_t rows, size_t...cols>
constexpr matrix<scalar, rows, cols...> matrixCompMult(const matrix<scalar, rows, cols...>& x, const matrix<scalar, rows, cols...>& y) noexcept
{ return matrix<scalar, rows, cols...>(x[cols] * y[cols] ...); }
///
/// \brief Performs a linear algebraic multiply, multiplying \f$c\f$ by the components of \f$r\f$, producing a matrix.
///
/// \details Treats the first parameter \f$c\f$ as a column vector (matrix
/// with one column) and the second parameter \f$r\f$ as a row
/// vector (matrix with one row) and does a linear algebraic
/// matrix multiply \f$c \cross r\f$, yielding a matrix whose number of
/// rows is the number of components in \f$c\f$ and whose
/// number of columns is the number of components in \f$r\f$.
/// \param c the column vector
/// \param r the row vector
/// \returns the resulting matrix produced by the linear algebraic product
template<typename scalar, size_t...s0, size_t...s1>
constexpr matrix<scalar, sizeof...(s0), s1...> outerProduct(const vector<scalar, s0...>& c, const vector<scalar, s1...>& r) noexcept
{
return matrix<scalar, sizeof...(s0), s1...>(
c * r[s1]...
);
}
///
/// \brief get the transpose of \f$m\f$
/// \param m the matrix to transpose
/// \returns a matrix that is the transpose of \f$m\f$
/// \details The input matrix m is not modified.
template<typename scalar, size_t rows, size_t...cols>
constexpr mat<scalar, rows, sizeof...(cols)> transpose(const matrix<scalar, rows, cols...>& m) noexcept
{
return [m]<size_t...i>(index_sequence<i...>) -> mat<scalar, rows, sizeof...(cols)> {
return mat<scalar, rows, sizeof...(cols)>(fennec::row(m, i) ...);
}(make_index_sequence<rows>{});
}
///
/// \brief Returns the determinant of m.
/// \param m the matrix
/// \returns the determinant of m.
template<typename scalar, size_t rows, size_t...cols>
constexpr scalar determinant(const matrix<scalar, rows, cols...>&) noexcept
{
// ReSharper disable once CppStaticAssertFailure
static_assert(false, "implementation undefined");
return 0;
}
template<typename scalar, size_t rows, size_t...cols>
constexpr matrix<scalar, rows, cols...> inverse(const matrix<scalar, rows, cols...>&) noexcept
{
// ReSharper disable once CppStaticAssertFailure
static_assert(false, "implementation undefined");
return 1;
}
///
/// \brief
/// \tparam ScalarT
/// \tparam RowsV
/// \tparam ColIndicesV
template<typename ScalarT, size_t RowsV, size_t...ColIndicesV>
struct matrix
{
// Assertions ==========================================================================================================
static_assert(is_arithmetic_v<ScalarT> or is_bool_v<ScalarT>);
// Typedefs & Constants ================================================================================================
///
/// \brief number of rows in the matrix
static constexpr size_t rows = RowsV;
///
/// \brief number of columns in the matrix
static constexpr size_t columns = sizeof...(ColIndicesV);
///
/// \brief number of total components
static constexpr size_t num_components = rows * columns;
///
/// \brief vector class correspondant to the rows
using row_t = vec<ScalarT, columns>;
///
/// \brief vector class correspondant to the columns
using column_t = vec<ScalarT, rows>;
///
/// \brief base scalar type
using scalar_t = ScalarT;
///
/// \brief alias for this matrix type
using matrix_t = matrix;
///
/// \brief alias for the transpose matrix type
using transpose_t = mat<ScalarT, columns, rows>;
///
/// \brief matrix array data
array<column_t, columns> data;
// Constructors ========================================================================================================
/// \name Constructors
/// @{
///
/// \brief default constructor, initializes elements with 0
///
/// \details
constexpr matrix()
: data{ scalar_t(0) }{}
///
/// \brief copy constructor
///
/// \details
/// \param mat matrix to copy
constexpr matrix(const matrix_t& mat)
: data{ mat.data } {}
///
/// \brief move constructor
///
/// \details
/// \param mat matrix to move
constexpr matrix(matrix_t&& mat) noexcept
: data{ mat.data } {}
///
/// \brief scalar constructor, initializes a diagonal matrix with a scale of \p s
///
/// \details
/// This function creates a diagonal matrix such that ```vec3(2.0f)``` would result in a matrix
/// <table>
/// <caption id="fennec_table_matrix_diagonal"></caption>
/// <tr><th> <th> 0 <th> 1 <th> 2
/// <tr><th> 0 <td>2.0<td>0.0<td>0.0
/// <tr><th> 1 <td>0.0<td>2.0<td>0.0
/// <tr><th> 2 <td>0.0<td>0.0<td>2.0
/// </table><br><br>
///
/// \param s scalar value
constexpr matrix(scalar_t s)
{ (((ColIndicesV < rows) ? data[ColIndicesV][ColIndicesV] = s : 0), ...); }
///
/// \brief Piece-wise constructor
///
/// \details
/// \tparam ArgsT
/// \param args
template<typename...ArgsT> requires(total_component_count_v<ArgsT...> == num_components)
constexpr matrix(ArgsT&&...args)
{ matrix::__construct(fennec::forward<ArgsT>(args)...); }
/// @}
// Assignment Operators ================================================================================================
/// \name Assignment Operators
/// @{
///
/// \brief copy assignment operator
///
/// \details
/// \returns a reference to **this** after copying the contents of \p rhs
/// \param rhs the matrix to copy
constexpr matrix_t& operator=(const matrix_t& rhs)
{ data = rhs.data; return *this; }
///
/// \brief move assignment operator
///
/// \details
/// \returns a reference to **this** after moving the contents of \p rhs
/// \param rhs the matrix to move
constexpr matrix_t& operator=(matrix_t&& rhs) noexcept
{ data = rhs.data; return *this; }
/// @}
// Access Operators ====================================================================================================
/// \name Access Operator
/// @{
///
/// \copydetails matrix::operator[](size_t) const
constexpr column_t& operator[](size_t i)
{ assert(i < rows); return data[i]; }
///
/// \brief returns the column at index \f$i\f$
///
/// \details
/// \param i the index
/// \returns the column at index \f$i\f$
constexpr const column_t& operator[](size_t i) const
{ assert(i < columns); return data[i]; }
///
/// \copydetails matrix::operator()(size_t, size_t) const
constexpr scalar_t& operator()(size_t i, size_t j)
{ assert(i < columns && j < rows); return data[i][j]; }
///
/// \brief returns the cell in row \p j column \p i
/// \param i the column
/// \param j the row
/// \returns the cell at the specified index.
constexpr scalar_t operator()(size_t i, size_t j) const
{ assert(i < columns && j < rows); return data[i][j]; }
/// @}
// Scalar Operators ====================================================================================================
/// \name Scalar Operators
/// @{
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar addition operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns a copy of the matrix with each component having been added by the scalar
constexpr friend matrix_t operator+(const matrix_t& lhs, scalar_t rhs)
{
matrix_t retval = lhs;
((retval[ColIndicesV] += rhs), ...);
return retval;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" addition operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns a copy of the matrix with each component having been added by the scalar
constexpr friend matrix_t operator+(scalar_t lhs, const matrix_t& rhs)
{
matrix_t retval = rhs;
((retval[ColIndicesV] += lhs), ...);
return retval;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar subtraction operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns a copy of the matrix with each component having been subtracted by the scalar
constexpr friend matrix_t operator-(const matrix_t& lhs, scalar_t rhs)
{
matrix_t retval = lhs;
((retval[ColIndicesV] -= rhs), ...);
return retval;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" subtraction operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns a copy of the matrix with each component having been subtracted by the scalar
constexpr friend matrix_t operator-(scalar_t lhs, const matrix_t& rhs)
{
matrix_t retval = rhs;
((retval[ColIndicesV] -= lhs), ...);
return retval;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar multiplication operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns a copy of the matrix with each component having been multiplied by the scalar
constexpr friend matrix_t operator*(const matrix_t& lhs, scalar_t rhs)
{
matrix_t retval = lhs;
((retval[ColIndicesV] *= rhs), ...);
return retval;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" multiplication operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns a copy of the matrix with each component having been multiplied by the scalar
constexpr friend matrix_t operator*(scalar_t lhs, const matrix_t& rhs)
{
matrix_t retval = rhs;
((retval[ColIndicesV] *= lhs), ...);
return retval;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar division operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns a copy of the matrix with each component having been divided by the scalar
constexpr friend matrix_t operator/(const matrix_t& lhs, scalar_t rhs)
{
matrix_t retval = lhs;
((retval[ColIndicesV] /= rhs), ...);
return retval;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" division operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns a copy of the matrix with each component having been divided by the scalar
constexpr friend matrix_t operator/(scalar_t lhs, const matrix_t& rhs)
{
matrix_t retval = rhs;
((retval[ColIndicesV] /= lhs), ...);
return retval;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar addition assignment operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns the matrix after having each component added by the scalar
constexpr friend matrix_t& operator+=(matrix_t& lhs, scalar_t rhs)
{
((lhs[ColIndicesV] += rhs), ...);
return lhs;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" addition assignment operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns the matrix after having each component added by the scalar
constexpr friend matrix_t& operator+=(scalar_t lhs, matrix_t& rhs)
{
((rhs[ColIndicesV] += lhs), ...);
return rhs;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar subtraction assignment operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns the matrix after having each component subtracted by the scalar
constexpr friend matrix_t& operator-=(matrix_t& lhs, scalar_t rhs)
{
((lhs[ColIndicesV] -= rhs), ...);
return lhs;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" addition assignment operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns the matrix after having each component added by the scalar
constexpr friend matrix_t& operator-=(scalar_t lhs, matrix_t& rhs)
{
((rhs[ColIndicesV] -= lhs), ...);
return rhs;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar multiplication assignment operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns the matrix after having each component multiplied by the scalar
constexpr friend matrix_t& operator*=(matrix_t& lhs, scalar_t rhs)
{
((lhs[ColIndicesV] *= rhs), ...);
return lhs;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" multiplication assignment operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns the matrix after having each component multiplied by the scalar
constexpr friend matrix_t& operator*=(scalar_t lhs, matrix_t& rhs)
{
((rhs[ColIndicesV] *= lhs), ...);
return rhs;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar division assignment operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns the matrix after having each component divided by the scalar
constexpr friend matrix_t& operator/=(matrix_t& lhs, scalar_t rhs)
{
((lhs[ColIndicesV] /= rhs), ...);
return lhs;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" division assignment operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns the matrix after having each component divided by the scalar
constexpr friend matrix_t& operator/=(scalar_t lhs, matrix_t& rhs)
{
((rhs[ColIndicesV] /= lhs), ...);
return rhs;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar modulo operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns a copy of the matrix with each component having been reduced modulo by the scalar
constexpr friend matrix_t operator%(const matrix_t& lhs, scalar_t rhs) requires is_integral_v<scalar_t>
{
matrix_t retval = lhs;
((retval[ColIndicesV] %= rhs), ...);
return retval;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" modulo operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns a copy of the matrix with each component having been reduced modulo by the scalar
constexpr friend matrix_t operator%(scalar_t lhs, const matrix_t& rhs) requires is_integral_v<scalar_t>
{
matrix_t retval = rhs;
((retval[ColIndicesV] %= lhs), ...);
return retval;
}
///
/// \brief \ref fennec::matrix "matrix" - \ref scalar modulo assignment operator.
/// \param lhs the matrix
/// \param rhs the scalar
/// \returns the matrix after having each component reduced modulo by the scalar
constexpr friend matrix_t& operator%=(matrix_t& lhs, scalar_t rhs) requires is_integral_v<scalar_t>
{
((lhs[ColIndicesV] %= rhs), ...);
return lhs;
}
///
/// \brief \ref scalar - \ref fennec::matrix "matrix" modulo assignment operator.
/// \param lhs the scalar
/// \param rhs the matrix
/// \returns the matrix after having each component reduced modulo by the scalar
constexpr friend matrix_t& operator%=(scalar_t lhs, matrix_t& rhs) requires is_integral_v<scalar_t>
{
((rhs[ColIndicesV] %= lhs), ...);
return rhs;
}
/// @}
// Vector Operators ====================================================================================================
/// \name \ref fennec::matrix "matrix" - \ref fennec::vector "vector" Operators
/// @{
///
/// \brief performs a linear algebraic multiply
/// \param lhs the matrix
/// \param rhs the vector
/// \returns a vector containing the dot products of \f$rhs\f$ with each row of \f$lhs\f$
constexpr friend column_t operator*(const matrix_t& lhs, const row_t& rhs)
{ return [lhs, rhs]<size_t...i>(index_sequence<i...>) -> column_t
{ return column_t(fennec::dot(fennec::row(lhs, i), rhs) ...); }(make_index_sequence<rows>{}); }
///
/// \brief performs a linear algebraic multiply
/// \param lhs the vector
/// \param rhs the matrix
/// \returns a vector containing the dot products of \f$lhs\f$ with each column of \f$rhs\f$
constexpr friend row_t operator*(const column_t& lhs, const matrix_t& rhs)
{ return row_t(fennec::dot(fennec::column(rhs, ColIndicesV), lhs) ...); }
/// @}
// Matrix Operators ====================================================================================================
/// \name \ref fennec::matrix "matrix" - \ref fennec::matrix "matrix" Operators
/// @{
///
/// \brief matrix comparison operator
/// \param lhs the first matrix
/// \param rhs the second matrix
/// \returns a boolean value that contains \f$true\f$ when all components of `lhs` and `rhs` are equal and \f$false\f$ otherwise
constexpr friend bool operator==(const matrix_t& lhs, const matrix_t& rhs)
{ return lhs.data == rhs.data; }
///
/// \brief matrix comparison operator
/// \param lhs the first matrix
/// \param rhs the second matrix
/// \returns a boolean value that contains \f$true\f$ when all components of `lhs` and `rhs` are not equal and \f$false\f$ otherwise
constexpr friend bool operator!=(const matrix_t& lhs, const matrix_t& rhs)
{ return lhs.data != rhs.data; }
///
/// \brief performs a linear algebraic matrix multiplication
/// \param lhs the rows to multiply with
/// \param rhs the columns to multiply with
constexpr friend matrix_t operator*(const matrix_t& lhs, const transpose_t& rhs)
{
return [lhs, rhs]<size_t...i>(index_sequence<i...>) -> matrix_t {
return matrix_t(rhs * fennec::row(lhs, i)...);
}(make_index_sequence<rows>{});
}
/// @}
// Helpers =============================================================================================================
private:
template<size_t i0 = 0>
constexpr void __construct() { }
template<size_t i0 = 0, typename HeadT, typename...ArgsT>
constexpr void __construct(HeadT&& head, ArgsT&&...args)
{
matrix::__insert<i0>(head);
matrix::__construct<i0 + component_count_v<HeadT>>(std::forward<ArgsT>(args)...);
}
template<size_t i0 = 0>
constexpr void __insert(scalar_t s)
{ data[i0 / rows][i0 % rows] = s; }
template<size_t i0 = 0, typename OScalarT> requires(is_arithmetic_v<OScalarT>)
constexpr void __insert(OScalarT s)
{ data[i0 / rows][i0 % rows] = scalar_t(s); }
template<size_t i0 = 0, size_t...i>
constexpr void __insert(const vector<scalar_t, i...>& v)
{ (matrix::__insert<i0 + i>(v[i]), ...); }
template<size_t i0 = 0, typename OScalarT, size_t...i>
constexpr void __insert(const vector<OScalarT, i...>& v)
{ (matrix::__insert<i0 + i>(v[i]), ...); }
};
// Internal ============================================================================================================
}
#endif // FENNEC_MATH_MATRIX_H