#ifndef STAR_MATRIX3_HPP #define STAR_MATRIX3_HPP #include "StarVector.hpp" namespace Star { template class Matrix3 { public: typedef Vector Vec3; typedef Vector Vec2; typedef Array Rows; // Only enable pointer access if we know that our internal rows are not // padded template using EnableIfContiguousStorage = typename std::enable_if::type; static Matrix3 identity(); // Construct an affine 2d transform static Matrix3 rotation(T angle, Vec2 const& point = Vec2()); static Matrix3 translation(Vec2 const& point); static Matrix3 scaling(T scale, Vec2 const& point = Vec2()); static Matrix3 scaling(Vec2 const& scale, Vec2 const& point = Vec2()); Matrix3(); Matrix3(T r1c1, T r1c2, T r1c3, T r2c1, T r2c2, T r2c3, T r3c1, T r3c2, T r3c3); Matrix3(Vec3 const& r1, Vec3 const& r2, Vec3 const& r3); Matrix3(T const* ptr); template Matrix3(Matrix3 const& m); template Matrix3& operator=(Matrix3 const& m); // Row-major indexing Vec3& operator[](size_t const i); Vec3 const& operator[](size_t const i) const; // Gives pointer to row major storage EnableIfContiguousStorage ptr(); EnableIfContiguousStorage ptr() const; // Copy to an existing array void copy(T* loc) const; Vec3 row(size_t i) const; template void setRow(size_t i, Vector const& v); Vec3 col(size_t i); template void setCol(size_t i, Vector const& v); T determinant() const; Vec3 trace() const; Matrix3 inverse() const; bool isOrthogonal(T tolerance) const; void transpose(); void orthogonalize(); void invert(); // Apply the given 2d affine transformation to this matrix in global // coordinates void rotate(T angle, Vec2 const& point = Vec2()); void translate(Vec2 const& point); void scale(Vec2 const& scale, Vec2 const& point = Vec2()); void scale(T scale, Vec2 const& point = Vec2()); // Do an affine transformation of the given 2d vector. template Vector transformVec2(Vector const& v2) const; // The resulting angle of a transformation on any ray with this angle. float transformAngle(float angle) const; bool operator==(Matrix3 const& m2) const; bool operator!=(Matrix3 const& m2) const; Matrix3& operator*=(T const& s); Matrix3& operator/=(T const& s); Matrix3 operator*(T const& s) const; Matrix3 operator/(T const& s) const; Matrix3 operator-() const; template Matrix3& operator+=(Matrix3 const& m2); template Matrix3& operator-=(Matrix3 const& m2); template Matrix3& operator*=(Matrix3 const& m2); template Matrix3 operator+(Matrix3 const& m2) const; template Matrix3 operator-(Matrix3 const& m2) const; template Matrix3 operator*(Matrix3 const& m2) const; template Vec3 operator*(Vector const& v) const; private: Rows m_rows; }; typedef Matrix3 Mat3F; typedef Matrix3 Mat3D; template Matrix3 Matrix3::identity() { return Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1); } template Matrix3 Matrix3::rotation(T angle, Vec2 const& point) { T s = sin(angle); T c = cos(angle); return Matrix3(c, -s, point[0] - c * point[0] + s * point[1], s, c, point[1] - s * point[0] - c * point[1], 0, 0, 1); } template Matrix3 Matrix3::translation(Vec2 const& point) { return Matrix3(1, 0, point[0], 0, 1, point[1], 0, 0, 1); } template Matrix3 Matrix3::scaling(T scale, Vec2 const& point) { return scaling(Vec2::filled(scale), point); } template Matrix3 Matrix3::scaling(Vec2 const& scale, Vec2 const& point) { return Matrix3(scale[0], 0, point[0] - point[0] * scale[0], 0, scale[1], point[1] - point[1] * scale[1], 0, 0, 1); } template Matrix3::Matrix3() {} template Matrix3::Matrix3(T r1c1, T r1c2, T r1c3, T r2c1, T r2c2, T r2c3, T r3c1, T r3c2, T r3c3) : m_rows(Vec3(r1c1, r1c2, r1c3), Vec3(r2c1, r2c2, r2c3), Vec3(r3c1, r3c2, r3c3)) {} template Matrix3::Matrix3(const Vec3& r1, const Vec3& r2, const Vec3& r3) : m_rows{r1, r2, r3} {} template Matrix3::Matrix3(T const* ptr) : m_rows{Vec3(ptr), Vec3(ptr + 3), Vec3(ptr + 6)} {} template template Matrix3::Matrix3(const Matrix3& m) { *this = m; } template template Matrix3& Matrix3::operator=(const Matrix3& m) { m_rows = m.m_rows; return *this; } template auto Matrix3::operator[](const size_t i) -> Vec3 & { return m_rows[i]; } template auto Matrix3::operator[](const size_t i) const -> Vec3 const & { return m_rows[i]; } template auto Matrix3::ptr() -> EnableIfContiguousStorage { return m_rows[0].ptr(); } template auto Matrix3::ptr() const -> EnableIfContiguousStorage { return m_rows[0].ptr(); } template void Matrix3::copy(T* loc) const { m_rows[0].copyFrom(loc); m_rows[1].copyFrom(loc + 3); m_rows[2].copyFrom(loc + 6); } template auto Matrix3::row(size_t i) const -> Vec3 { return operator[](i); } template template void Matrix3::setRow(size_t i, const Vector& v) { operator[](i) = Vec3(v); } template auto Matrix3::col(size_t i) -> Vec3 { return Vec3(m_rows[0][i], m_rows[1][i], m_rows[2][i]); } template template void Matrix3::setCol(size_t i, const Vector& v) { m_rows[0][i] = T(v[0]); m_rows[1][i] = T(v[1]); m_rows[2][i] = T(v[2]); } template T Matrix3::determinant() const { return m_rows[0][0] * m_rows[1][1] * m_rows[2][2] - m_rows[0][0] * m_rows[2][1] * m_rows[1][2] + m_rows[1][0] * m_rows[2][1] * m_rows[0][2] - m_rows[1][0] * m_rows[0][1] * m_rows[2][2] + m_rows[2][0] * m_rows[0][1] * m_rows[1][2] - m_rows[2][0] * m_rows[1][1] * m_rows[0][2]; } template void Matrix3::transpose() { std::swap(m_rows[1][0], m_rows[0][1]); std::swap(m_rows[2][0], m_rows[0][2]); std::swap(m_rows[2][1], m_rows[1][2]); } template void Matrix3::invert() { T d = determinant(); m_rows[0][0] = (m_rows[1][1] * m_rows[2][2] - m_rows[1][2] * m_rows[2][1]) / d; m_rows[0][1] = -(m_rows[0][1] * m_rows[2][2] - m_rows[0][2] * m_rows[2][1]) / d; m_rows[0][2] = (m_rows[0][1] * m_rows[1][2] - m_rows[0][2] * m_rows[1][1]) / d; m_rows[1][0] = -(m_rows[1][0] * m_rows[2][2] - m_rows[1][2] * m_rows[2][0]) / d; m_rows[1][1] = (m_rows[0][0] * m_rows[2][2] - m_rows[0][2] * m_rows[2][0]) / d; m_rows[1][2] = -(m_rows[0][0] * m_rows[1][2] - m_rows[0][2] * m_rows[1][0]) / d; m_rows[2][0] = (m_rows[1][0] * m_rows[2][1] - m_rows[1][1] * m_rows[2][0]) / d; m_rows[2][1] = -(m_rows[0][0] * m_rows[2][1] - m_rows[0][1] * m_rows[2][0]) / d; m_rows[2][2] = (m_rows[0][0] * m_rows[1][1] - m_rows[0][1] * m_rows[1][0]) / d; } template Matrix3 Matrix3::inverse() const { auto m = *this; m.invert(); return m; } template void Matrix3::orthogonalize() { m_rows[0].normalize(); T dot = m_rows[0] * m_rows[1]; m_rows[1][0] -= m_rows[0][0] * dot; m_rows[1][1] -= m_rows[0][1] * dot; m_rows[1][2] -= m_rows[0][2] * dot; m_rows[1].normalize(); dot = m_rows[1] * m_rows[2]; m_rows[2][0] -= m_rows[1][0] * dot; m_rows[2][1] -= m_rows[1][1] * dot; m_rows[2][2] -= m_rows[1][2] * dot; m_rows[2].normalize(); } template bool Matrix3::isOrthogonal(T tolerance) const { T det = determinant(); return std::fabs(det - 1) < tolerance || std::fabs(det + 1) < tolerance; } template void Matrix3::rotate(T angle, Vec2 const& point) { *this = rotation(angle, point) * *this; } template void Matrix3::translate(Vec2 const& point) { *this = translation(point) * *this; } template void Matrix3::scale(Vec2 const& scale, Vec2 const& point) { *this = scaling(scale, point) * *this; } template void Matrix3::scale(T scale, Vec2 const& point) { *this = scaling(scale, point) * *this; } template template Vector Matrix3::transformVec2(Vector const& point) const { Vector res = (*this) * Vector(point, 1); return res.vec2(); } template float Matrix3::transformAngle(float angle) const { Vec2 a = Vec2::withAngle(angle, 1.0f); Matrix3 m = *this; m[0][2] = 0; m[1][2] = 0; return m.transformVec2(a).angle(); } template bool Matrix3::operator==(Matrix3 const& m2) const { return tie(m_rows[0], m_rows[1], m_rows[2]) == tie(m2.m_rows[0], m2.m_rows[1], m2.m_rows[2]); } template bool Matrix3::operator!=(Matrix3 const& m2) const { return tie(m_rows[0], m_rows[1], m_rows[2]) != tie(m2.m_rows[0], m2.m_rows[1], m2.m_rows[2]); } template Matrix3& Matrix3::operator*=(const T& s) { m_rows[0] *= s; m_rows[1] *= s; m_rows[2] *= s; return *this; } template Matrix3& Matrix3::operator/=(const T& s) { m_rows[0] /= s; m_rows[1] /= s; m_rows[2] /= s; return *this; } template auto Matrix3::trace() const -> Vec3 { return Vec3(m_rows[0][0], m_rows[1][1], m_rows[2][2]); } template Matrix3 Matrix3::operator-() const { return Matrix3(-m_rows[0], -m_rows[1], -m_rows[2]); } template template Matrix3& Matrix3::operator+=(const Matrix3& m) { m_rows[0] += m[0]; m_rows[1] += m[1]; m_rows[2] += m[2]; return *this; } template template Matrix3& Matrix3::operator-=(const Matrix3& m) { m_rows[0] -= m[0]; m_rows[1] -= m[1]; m_rows[2] -= m[2]; return *this; } template template Matrix3& Matrix3::operator*=(Matrix3 const& m2) { *this = *this * m2; return *this; } template template Matrix3 Matrix3::operator+(const Matrix3& m2) const { return Matrix3(m_rows[0] + m2[0], m_rows[1] + m2[1], m_rows[2] + m2[2]); } template template Matrix3 Matrix3::operator-(const Matrix3& m2) const { return Matrix3(m_rows[0] - m2[0], m_rows[1] - m2[1], m_rows[2] - m2[2]); } template template Matrix3 Matrix3::operator*(const Matrix3& m2) const { return Matrix3(m_rows[0][0] * m2[0][0] + m_rows[0][1] * m2[1][0] + m_rows[0][2] * m2[2][0], m_rows[0][0] * m2[0][1] + m_rows[0][1] * m2[1][1] + m_rows[0][2] * m2[2][1], m_rows[0][0] * m2[0][2] + m_rows[0][1] * m2[1][2] + m_rows[0][2] * m2[2][2], m_rows[1][0] * m2[0][0] + m_rows[1][1] * m2[1][0] + m_rows[1][2] * m2[2][0], m_rows[1][0] * m2[0][1] + m_rows[1][1] * m2[1][1] + m_rows[1][2] * m2[2][1], m_rows[1][0] * m2[0][2] + m_rows[1][1] * m2[1][2] + m_rows[1][2] * m2[2][2], m_rows[2][0] * m2[0][0] + m_rows[2][1] * m2[1][0] + m_rows[2][2] * m2[2][0], m_rows[2][0] * m2[0][1] + m_rows[2][1] * m2[1][1] + m_rows[2][2] * m2[2][1], m_rows[2][0] * m2[0][2] + m_rows[2][1] * m2[1][2] + m_rows[2][2] * m2[2][2]); } template template auto Matrix3::operator*(const Vector& u) const -> Vec3 { return Vec3(m_rows[0][0] * u[0] + m_rows[0][1] * u[1] + m_rows[0][2] * u[2], m_rows[1][0] * u[0] + m_rows[1][1] * u[1] + m_rows[1][2] * u[2], m_rows[2][0] * u[0] + m_rows[2][1] * u[1] + m_rows[2][2] * u[2]); } template Matrix3 Matrix3::operator/(const T& s) const { return Matrix3(m_rows[0] / s, m_rows[1] / s, m_rows[2] / s); } template Matrix3 Matrix3::operator*(const T& s) const { return Matrix3(m_rows[0] * s, m_rows[1] * s, m_rows[2] * s); } template T determinant(const Matrix3& m) { return m.determinant(); } template Matrix3 transpose(Matrix3 m) { return m.transpose(); } template Matrix3 ortho(Matrix3 mat) { return mat.orthogonalize(); } template Matrix3 operator*(T s, const Matrix3& m) { return m * s; } template std::ostream& operator<<(std::ostream& os, Matrix3 m) { os << m[0][0] << ' ' << m[0][1] << ' ' << m[0][2] << std::endl; os << m[1][0] << ' ' << m[1][1] << ' ' << m[1][2] << std::endl; os << m[2][0] << ' ' << m[2][1] << ' ' << m[2][2]; return os; } } template struct fmt::formatter> : ostream_formatter {}; #endif