#pragma once #include #include #include #include "Vector2.h" namespace SwanCommon { // 3D transformation matrix. // All the operations assume that the last row remains {0, 0, 1}. // If the last row is modified, methods like .scale, .translate and .rotate won't work. template struct Matrix3 { using Vec = Vector2; static constexpr std::array identity = {1, 0, 0, 0, 1, 0, 0, 0, 1}; std::array vals; constexpr Matrix3(): vals(identity) {} constexpr Matrix3(const Matrix3 &mat): vals(mat.vals) {} constexpr Matrix3 &operator=(const Matrix3 &mat) { vals = mat.vals; } constexpr T *data() { return vals.data(); } constexpr const T *data() const { return vals.data(); } constexpr T &at(int x, int y) { return vals[y * 3 + x]; } constexpr const T &at(int x, int y) const { return vals[y * 3 + x]; } constexpr Matrix3 &set(std::initializer_list vals) { this->vals = vals; return *this; } constexpr Matrix3 &reset() { vals = identity; return *this; } constexpr Matrix3 &translate(Vec vec) { at(2, 0) += vec.x; at(2, 1) += vec.y; return *this; } constexpr Matrix3 &scale(Vec vec) { at(0, 0) *= vec.x; at(1, 0) *= vec.x; at(2, 0) *= vec.x; at(0, 1) *= vec.y; at(1, 1) *= vec.y; at(2, 1) *= vec.y; return *this; } constexpr Matrix3 &rotate(T rads) { T s = std::sin(rads); T c = std::cos(rads); T old00 = at(0, 0), old10 = at(1, 0), old20 = at(2, 0); at(0, 0) = c * old00 + -s * at(0, 1); at(1, 0) = c * old10 + -s * at(1, 1); at(2, 0) = c * old20 + -s * at(2, 1); at(0, 1) = s * old00 + c * at(0, 1); at(1, 1) = s * old10 + c * at(1, 1); at(2, 1) = s * old20 + c * at(2, 1); return *this; } static const Matrix3 IDENTITY; template friend std::ostream &operator<<(std::ostream &os, const Matrix3 &mat); }; template const Matrix3 Matrix3::IDENTITY = Matrix3(); template std::ostream &operator<<(std::ostream &os, const Matrix3 &mat) { os << '(' << '(' << mat.at(0, 0) << ", " << mat.at(1, 0) << ", " << mat.at(2, 0) << "), " << '(' << mat.at(0, 1) << ", " << mat.at(1, 1) << ", " << mat.at(2, 1) << "), " << '(' << mat.at(0, 2) << ", " << mat.at(1, 2) << ", " << mat.at(2, 2) << "))"; return os; } }