#pragma once #include #include #include namespace hpr { // forward declarations template requires (Rows >= 0 && Cols >= 0) class Matrix; template using SubMatrix = typename std::conditional<(Rows >= 2 && Cols >= 2), Matrix, Matrix>::type; // type traits template struct is_matrix : public std::false_type {}; template struct is_matrix> : public std::true_type {}; // concepts template concept IsMatrix = is_matrix::value; // aliases template using mat = Matrix; using mat2 = Matrix; using mat3 = Matrix; using mat4 = Matrix; template requires (Rows >= 0 && Cols >= 0) class Matrix : public StaticArray { using base = StaticArray; public: using value_type = Type; using size_type = Size; using pointer = Type*; using reference = Type&; using iterator = Iterator; using const_reference = Type const&; using const_iterator = Iterator; protected: size_type p_rows; size_type p_cols; public: friend constexpr void swap(Matrix& main, Matrix& other) { using std::swap; swap(static_cast(main), static_cast(other)); swap(main.p_rows, other.p_rows); swap(main.p_cols, other.p_cols); } inline Matrix() : base {}, p_rows {Rows}, p_cols {Cols} {} inline Matrix(const Matrix& ms) : base {static_cast(ms)}, p_rows {Rows}, p_cols {Cols} {} inline Matrix(Matrix&& ms) noexcept: base {std::forward(static_cast(ms))}, p_rows {Rows}, p_cols {Cols} {} inline Matrix& operator=(const Matrix& ms) { //base::operator=(ms); swap(*this, ms); return *this; } inline explicit Matrix(const base& vs) : base {vs}, p_rows {Rows}, p_cols {Cols} {} inline explicit Matrix(base&& vs) noexcept: base {std::forward(vs)}, p_rows {Rows}, p_cols {Cols} {} inline Matrix(typename base::iterator start, typename base::iterator end) : base {start, end}, p_rows {Rows}, p_cols {Cols} {} inline Matrix(typename base::const_iterator start, typename base::const_iterator end) : base {start, end}, p_rows {Rows}, p_cols {Cols} {} inline Matrix(std::initializer_list list) : base {list}, p_rows {Rows}, p_cols {Cols} {} template inline Matrix(value_type&& v, Args&& ...args) requires (1 + sizeof...(args) == Rows * Cols): base {v, static_cast(std::forward(args))...}, p_rows {Rows}, p_cols {Cols} {} inline Matrix(const value_type& v) : base {}, p_rows {Rows}, p_cols {Cols} { for (Size n = 0; n < Rows * Cols; ++n) (*this)[n] = v; } inline Matrix& operator=(const value_type& v) { for (Size n = 0; n < Rows * Cols; ++n) (*this)[n] = v; return *this; } inline explicit Matrix(const Quaternion& q) requires (Rows == 3 && Cols == 3 || Rows == 4 && Cols == 4) : base {}, p_rows {Rows}, p_cols {Cols} { const scalar s = pow(norm(q), -2); (*this)(0, 0) = 1 - 2 * s * (q[2] * q[2] + q[3] * q[3]); (*this)(1, 0) = 2 * s * (q[1] * q[2] - q[3] * q[0]); (*this)(2, 0) = 2 * s * (q[1] * q[3] - q[2] * q[0]); (*this)(0, 1) = 2 * s * (q[1] * q[2] + q[3] * q[0]); (*this)(1, 1) = 1 - 2 * s * (q[1] * q[1] + q[3] * q[3]); (*this)(2, 1) = 2 * s * (q[2] * q[3] + q[1] * q[0]); (*this)(0, 2) = 2 * s * (q[1] * q[3] + q[2] * q[0]); (*this)(1, 2) = 2 * s * (q[2] * q[3] + q[1] * q[0]); (*this)(2, 2) = 1 - 2 * s * (q[1] * q[1] + q[2] * q[2]); if constexpr (Rows == 4) (*this)(3, 3) = 1; } // access inline reference operator()(size_type row, size_type col) { if (row >= p_rows || std::numeric_limits::max() - p_rows < row) throw std::out_of_range("Row index is out of range"); if (col >= p_cols || std::numeric_limits::max() - p_cols < col) throw std::out_of_range("Column index is out of range"); return (*this)[col + p_rows * row]; } inline const_reference operator()(size_type row, size_type col) const { if (row >= p_rows || std::numeric_limits::max() - p_rows < row) throw std::out_of_range("Row index is out of range"); if (col >= p_cols || std::numeric_limits::max() - p_cols < col) throw std::out_of_range("Column index is out of range"); return (*this)[col + p_rows * row]; } Vector row(size_type row) const { Vector vs; for (auto n = 0; n < Cols; ++n) vs[n] = (*this)(row, n); return vs; } void row(size_type row, const Vector& vs) { for (auto n = 0; n < Cols; ++n) (*this)(row, n) = vs[n]; } Vector col(size_type col) const { Vector vs; for (auto n = 0; n < Rows; ++n) vs[n] = (*this)(n, col); return vs; } void col(size_type col, const Vector& vs) { for (auto n = 0; n < Rows; ++n) (*this)(n, col) = vs[n]; } [[nodiscard]] constexpr size_type rows() const { return p_rows; } [[nodiscard]] constexpr size_type cols() const { return p_cols; } // member functions [[nodiscard]] constexpr bool is_square() const { return p_rows == p_cols; } inline Matrix& fill(value_type value) { for (auto n = 0; n < this->size(); ++n) (*this)[n] = value; return *this; } // Global functions static inline Matrix identity() { Matrix ms; for (auto n = 0; n < Rows; ++n) //for (auto k = 0; k < Cols; ++k) ms(n, n) = 1; return ms; } }; // global operators template inline Matrix operator+(const Matrix& lhs) { Matrix ms; for (Size n = 0; n < lhs.size(); ++n) ms[n] = lhs[n]; return ms; } template inline Matrix operator-(const Matrix& lhs) { Matrix ms; for (Size n = 0; n < lhs.size(); ++n) ms[n] = -lhs[n]; return ms; } template inline Matrix& operator+=(Matrix& lhs, const Matrix& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] += rhs[n]; return lhs; } template inline Matrix& operator-=(Matrix& lhs, const Matrix& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] -= rhs[n]; return lhs; } template inline Matrix& operator*=(Matrix& lhs, const Matrix& rhs) { Matrix temp {lhs}; for (Size n = 0; n < R; ++n) for (Size k = 0; k < C; ++k) lhs(n, k) = sum(temp.col(k) * rhs.row(n)); return lhs; } template inline Matrix operator+(const Matrix& lhs, const Matrix& rhs) { Matrix ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] += rhs[n]; return ms; } template inline Matrix operator-(const Matrix& lhs, const Matrix& rhs) { Matrix ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] -= rhs[n]; return ms; } template inline Matrix operator*(const Matrix& lhs, const Matrix& rhs) { Matrix ms; for (Size n = 0; n < R; ++n) for (Size k = 0; k < C; ++k) ms(n, k) = sum(lhs.col(k) * rhs.row(n)); return ms; } template inline bool operator==(const Matrix& lhs, const Matrix& rhs) { for (Size n = 0; n < lhs.size(); ++n) if (lhs[n] != rhs[n]) return false; return true; } template inline bool operator!=(const Matrix& lhs, const Matrix& rhs) { for (Size n = 0; n < lhs.size(); ++n) if (lhs[n] == rhs[n]) return false; return true; } template inline Matrix& operator+=(Matrix& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] += rhs; return lhs; } template inline Matrix& operator-=(Matrix& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] -= rhs; return lhs; } template inline Matrix& operator*=(Matrix& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] *= rhs; return lhs; } template inline Matrix& operator/=(Matrix& lhs, const T& rhs) { for (Size n = 0; n < lhs.size(); ++n) lhs[n] /= rhs; return lhs; } template inline Matrix operator+(const Matrix& lhs, const T& rhs) { Matrix ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] += rhs; return ms; } template inline Matrix operator-(const Matrix& lhs, const T& rhs) { Matrix ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] -= rhs; return ms; } template inline Matrix operator*(const Matrix& lhs, const T& rhs) { Matrix ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] *= rhs; return ms; } template inline Matrix operator/(const Matrix& lhs, const T& rhs) { Matrix ms {lhs}; for (Size n = 0; n < lhs.size(); ++n) ms[n] /= rhs; return ms; } template inline Vector operator*(const Matrix& ms, const Vector& vs) { Vector res; for (Size n = 0; n < R; ++n) res[n] = sum(ms.row(n) * vs); return res; } template inline Vector operator*(const Vector& vs, const Matrix& ms) { Vector res; for (Size n = 0; n < C; ++n) res[n] = sum(ms.col(n) * vs); return res; } template inline bool operator==(const Matrix& lhs, const Vector& rhs) { return false; } template inline bool operator!=(const Matrix& lhs, const Vector& rhs) { return true; } // matrix operations //! Transpose matrix template inline Matrix transpose(const Matrix& ms) { Matrix res; for (Size n = 0; n < R; ++n) for (Size k = 0; k < C; ++k) res(k, n) = ms(n, k); return res; } //! Trace of a matrix template inline T trace(const Matrix& ms) requires (R == C) { T res; for (auto n = 0; n < R; ++n) res += ms(n, n); return res; } //! Minor of a matrix template inline SubMatrix minor(const Matrix& ms, Size row, Size col) { if (ms.size() < 4) throw std::runtime_error("Matrix should be greater 2x2"); SubMatrix minor; auto minor_iter = minor.begin(); for (auto n = 0; n < R; ++n) for (auto k = 0; k < C; ++k) if (k != col && n != row) *(minor_iter++) = ms[k + ms.rows() * n]; return minor; } //! Determinant of a matrix template inline scalar det(const Matrix& ms) requires (R == C) { if (ms.size() == 1) return ms[0]; else if (ms.size() == 4) return ms(0, 0) * ms(1, 1) - ms(0, 1) * ms(1, 0); else { scalar res = 0; for (auto n = 0; n < ms.cols(); ++n) res += pow(-1, n) * ms(0, n) * det(minor(ms, 0, n)); return res; } } //! Adjoint matrix template inline Matrix adj(const Matrix& ms) { Matrix res; for (auto n = 0; n < R; ++n) for (auto k = 0; k < C; ++k) res(n, k) = pow(-1, n + k) * det(minor(ms, n, k)); return transpose(res); } //! Inverse matrix template inline Matrix inv(const Matrix& ms) { return adj(ms) / det(ms); } // Transforms template inline Matrix translate(const Matrix& ms, const Vector& vs) { Matrix res {ms}; res.col(3, ms.row(0) * vs[0] + ms.row(1) * vs[1] + ms.row(2) * vs[2] + ms.row(3)); return res; } template inline Matrix rotate(const Matrix& ms, const Vector& vs, T angle) { const T cosv = cos(angle); const T sinv = sin(angle); Vector axis {normalize(vs)}; Vector temp {(static_cast(1) - cosv) * axis}; Matrix rot; rot(0, 0) = cosv + temp[0] * axis[0]; rot(0, 1) = temp[0] * axis[1] + sinv * axis[2]; rot(0, 2) = temp[0] * axis[2] - sinv * axis[1]; rot(1, 0) = temp[1] * axis[0] - sinv * axis[2]; rot(1, 1) = cosv + temp[1] * axis[1]; rot(1, 2) = temp[1] * axis[2] + sinv * axis[0]; rot(2, 0) = temp[2] * axis[0] + sinv * axis[1]; rot(2, 1) = temp[2] * axis[1] - sinv * axis[0]; rot(2, 2) = cosv + temp[2] * axis[2]; Matrix res {ms}; res.row(0, ms.row(0) * rot(0, 0) + ms.row(1) * rot(0, 1) + ms.row(2) * rot(0, 2)); res.row(1, ms.row(0) * rot(1, 0) + ms.row(1) * rot(1, 1) + ms.row(2) * rot(1, 2)); res.row(2, ms.row(0) * rot(2, 0) + ms.row(1) * rot(2, 1) + ms.row(2) * rot(2, 2)); res.row(3, ms.row(3)); return res; } template inline Matrix rotate(const Matrix& ms, const Quaternion& q) { return ms * Matrix(q); } template inline Matrix scale(const Matrix& ms, const Vector& vs) { Matrix res; res.row(0, ms.row(0) * vs[0]); res.row(1, ms.row(1) * vs[1]); res.row(2, ms.row(2) * vs[2]); res.row(3, ms.row(3)); return res; } template inline Matrix lookAt(const Vector& eye, const Vector& center, const Vector& up) { const Vector forward {normalize(center - eye)}; const Vector right {normalize(cross(forward, up))}; const Vector nup {cross(right, forward)}; const Vector translation {dot(right, eye), dot(nup, eye), -dot(forward, eye)}; Matrix res = Matrix::identity(); res.row(0, Vector(right, 0)); res.row(1, Vector(nup, 0)); res.row(2, Vector(-forward, 0)); res.col(3, Vector(-translation, static_cast(1))); return res; } // Clip space template inline Matrix ortho(T left, T right, T bottom, T top) { Matrix ms = Matrix::identity(); ms(0, 0) = static_cast(2) / (right - left); ms(1, 1) = static_cast(2) / (top - bottom); ms(2, 2) = -static_cast(1); ms(3, 0) = -(right + left) / (right - left); ms(3, 1) = -(top + bottom) / (top - bottom); return ms; } template inline Matrix ortho(T left, T right, T bottom, T top, T zNear, T zFar) { Matrix ms = Matrix::identity(); ms(0, 0) = static_cast(2) / (right - left); ms(1, 1) = static_cast(2) / (top - bottom); ms(2, 2) = -static_cast(2) / (zFar - zNear); ms(0, 3) = -(right + left) / (right - left); ms(1, 3) = -(top + bottom) / (top - bottom); ms(2, 3) = -(zFar + zNear) / (zFar - zNear); return ms; } template inline Matrix perspective(T fovy, T aspect, T zNear, T zFar) { assert(abs(aspect - std::numeric_limits::epsilon()) > 0); Matrix ms; const T halfFovyTan = tan(fovy / 2); ms(0, 0) = static_cast(1) / (aspect * halfFovyTan); ms(1, 1) = static_cast(1) / halfFovyTan; ms(2, 2) = -(zFar + zNear) / (zFar - zNear); ms(3, 2) = -static_cast(1); ms(2, 3) = -(static_cast(2) * zFar * zNear) / (zFar - zNear); return ms; } } // end namespace hpr