#include "EmberPch.h" #include "Affine2D.h" namespace EmberNs { /// /// Default constructor which sets the matrix to the identity. /// template Affine2D::Affine2D() { MakeID(); } /// /// Default copy constructor. /// /// The Affine2D object to copy template Affine2D::Affine2D(const Affine2D& affine) { Affine2D::operator=(affine); } /// /// Constructor which takes each column of the affine as a separate parameter. /// /// A and D /// B and E /// C and F template Affine2D::Affine2D(v2T& x, v2T& y, v2T& t) { X(x); Y(y); O(t); } /// /// Constructor which takes all six of the affine values as parameters. /// /// A /// D /// B /// E /// C /// F template Affine2D::Affine2D(T xx, T xy, T yx, T yy, T tx, T ty) { A(xx); D(xy); B(yx); E(yy); C(tx); F(ty); } /// /// Constructor which takes a 4x4 matrix and assigns the /// corresponding values in the 2x3 affine matrix. /// /// The 4x4 affine matrix to read from template Affine2D::Affine2D(m4T& mat) { A(mat[0][0]); B(mat[0][1]); C(mat[0][3]); D(mat[1][0]); E(mat[1][1]); F(mat[1][3]); } /// /// Default assignment operator. /// /// The Affine2D object to copy template Affine2D& Affine2D::operator = (const Affine2D& affine) { if (this != &affine) Affine2D::operator=(affine); return *this; } /// /// == operator which tests if all fields are equal with another Affine2D. /// /// The Affine2D to compare to /// True if all fields are equal, else false template bool Affine2D::operator == (const Affine2D& affine) { return IsClose(A(), affine.A()) && IsClose(B(), affine.B()) && IsClose(C(), affine.C()) && IsClose(D(), affine.D()) && IsClose(E(), affine.E()) && IsClose(F(), affine.F()); } /// /// * operator to multiply this affine transform by a vec2 and return the result as a vec2. /// /// The vec2 to multiply by /// A new vec2 which is the product of the multiplication template typename v2T Affine2D::operator * (const v2T& v) { return TransformVector(v); } /// /// Make this affine transform the identity matrix. /// A and E = 1, all else 0. /// template void Affine2D::MakeID() { A(1); B(0); C(0); D(0); E(1); F(0); } /// /// Determine whether this affine transform is the identity matrix. /// /// True if A and E are equal to 1 and all others are 0, else false. template bool Affine2D::IsID() const { return (IsClose(A(), 1)) && (IsClose(B(), 0)) && (IsClose(C(), 0)) && (IsClose(D(), 0)) && (IsClose(E(), 1)) && (IsClose(F(), 0)); } /// /// Determine whether this affine transform is all zeroes. /// /// True if all 6 elements equal zero, else false. template bool Affine2D::IsZero() const { return (IsClose(A(), 0)) && (IsClose(B(), 0)) && (IsClose(C(), 0)) && (IsClose(D(), 0)) && (IsClose(E(), 0)) && (IsClose(F(), 0)); } /// /// Determine whether this affine transform was deliberately set to all empty values. /// /// True if all 6 elements equal zero, else false. template bool Affine2D::IsEmpty() const { return (IsClose(A(), EMPTYFIELD)) && (IsClose(B(), EMPTYFIELD)) && (IsClose(C(), EMPTYFIELD)) && (IsClose(D(), EMPTYFIELD)) && (IsClose(E(), EMPTYFIELD)) && (IsClose(F(), EMPTYFIELD)); } /// /// Rotate this affine transform around its origin by the specified angle in degrees. /// /// The angle to rotate by template void Affine2D::Rotate(T angle) { m4T origMat4 = ToMat4ColMajor(true);//Must center and use column major for glm to work. m4T newMat4 = glm::rotate(origMat4, angle * DEG_2_RAD_T, v3T(0, 0, 1));//Assuming only rotating around z. A(newMat4[0][0]);//Use direct assignments instead of constructor to skip assigning C and F. B(newMat4[0][1]); D(newMat4[1][0]); E(newMat4[1][1]); } /// /// Move by v. /// /// The vec2 describing how far to move in the x and y directions template void Affine2D::Translate(const v2T& v) { O(O() + v); } /// /// Rotate and scale the X and Y components by a certain amount based on X. /// /// The vec2 describing how much to rotate and scale the X and Y components template void Affine2D::RotateScaleXTo(const v2T& v) { Affine2D rs = CalcRotateScale(X(), v); X(rs.TransformNormal(X())); Y(rs.TransformNormal(Y())); } /// /// Rotate and scale the X and Y components by a certain amount based on Y. /// /// The vec2 describing how much to rotate and scale the X and Y components template void Affine2D::RotateScaleYTo(const v2T& v) { Affine2D rs = CalcRotateScale(Y(), v); X(rs.TransformNormal(X())); Y(rs.TransformNormal(Y())); } /// /// Return the inverse of the 2x3 affine matrix. /// /// The inverse of this affine transform template Affine2D Affine2D::Inverse() const { T det = A() * E() - D() * B(); return Affine2D(E() / det, -D() / det, -B() / det, A() / det, (F() * B() - C() * E()) / det, (C() * D() - F() * A()) / det); } /// /// Return a vec2 gotten from transforming this affine transform /// by the vec2 passed in, but with a T component of 0, 0. /// /// The vec2 describing how much to transform by /// The centered, transformed vec2 template typename v2T Affine2D::TransformNormal(const v2T& v) const { return v2T(A() * v.x + B() * v.y, D() * v.x + E() * v.y); } /// /// Return a vec2 gotten from transforming this affine transform /// by the vec2 passed in, and applying T translation. /// /// The vec2 describing how much to transform by /// The translated, transformed vec2 template typename v2T Affine2D::TransformVector(const v2T& v) const { return v2T(A() * v.x + B() * v.y + C(), D() * v.x + E() * v.y + F()); } /// /// Return the X and Y components as a 2x2 matrix in column major order. /// /// The 2x2 matrix template typename m2T Affine2D::ToMat2ColMajor() const { return m2T(A(), B(),//Col0... D(), E());//1 } /// /// Return the X and Y components as a 2x2 matrix in row major order. /// /// The 2x2 matrix template typename m2T Affine2D::ToMat2RowMajor() const { return m2T(A(), D(),//Col0... B(), E());//1 } /// /// Return the 2x3 affine transform matrix as a 4x4 matrix in column major order. /// /// Whether to use T translation value or just 0 for center /// The 4x4 matrix template typename m4T Affine2D::ToMat4ColMajor(bool center) const { m4T mat(A(), B(), 0, center ? 0 : C(), //Col0... D(), E(), 0, center ? 0 : F(), //1 0, 0, 1, 0, //2 0, 0, 0, 1);//3 return mat; } /// /// Return the 2x3 affine transform matrix as a 4x4 matrix in row major order. /// /// Whether to use T translation value or just 0 for center /// The 4x4 matrix template typename m4T Affine2D::ToMat4RowMajor(bool center) const { m4T mat(A(), D(), 0, 0, B(), E(), 0, 0, 0, 0, 1, 0, center ? 0 : C(), center ? 0 : F(), 0, 1); return mat; } /// /// Accessors. /// template T Affine2D::A() const { return m_Mat[0][0]; }//[0][0]//flam3 template T Affine2D::B() const { return m_Mat[0][1]; }//[1][0] template T Affine2D::C() const { return m_Mat[0][2]; }//[2][0] template T Affine2D::D() const { return m_Mat[1][0]; }//[0][1] template T Affine2D::E() const { return m_Mat[1][1]; }//[1][1] template T Affine2D::F() const { return m_Mat[1][2]; }//[2][1] template void Affine2D::A(T a) { m_Mat[0][0] = a; } template void Affine2D::B(T b) { m_Mat[0][1] = b; } template void Affine2D::C(T c) { m_Mat[0][2] = c; } template void Affine2D::D(T d) { m_Mat[1][0] = d; } template void Affine2D::E(T e) { m_Mat[1][1] = e; } template void Affine2D::F(T f) { m_Mat[1][2] = f; } template typename v2T Affine2D::X() const { return v2T(A(), D()); }//X Axis. template typename v2T Affine2D::Y() const { return v2T(B(), E()); }//Y Axis. template typename v2T Affine2D::O() const { return v2T(C(), F()); }//Translation. template void Affine2D::X(const v2T& x) { A(x.x); D(x.y); }//X Axis. template void Affine2D::Y(const v2T& y) { B(y.x); E(y.y); }//Y Axis. template void Affine2D::O(const v2T& t) { C(t.x); F(t.y); }//Translation. /// /// Rotate and scale this affine transform and return as a copy. Orginal is unchanged. /// /// The starting point to rotate and scale from /// The ending point to rotate and scale to /// The newly rotated and scalled Affine2D template Affine2D Affine2D::CalcRotateScale(const v2T& from, const v2T& to) { T a, c; CalcRSAC(from, to, a, c); return Affine2D(a, c, -c, a, 0, 0); } /// /// Never fully understood what this did or why it's named what it is. /// But it seems to handle some rotating and scaling. /// /// The starting point to rotate and scale from /// The ending point to rotate and scale to /// a /// c template void Affine2D::CalcRSAC(const v2T& from, const v2T& to, T& a, T& c) { T lsq = from.x * from.x + from.y * from.y; a = (from.y * to.y + from.x * to.x) / lsq; c = (from.x * to.y - from.y * to.x) / lsq; } //This class had to be implemented in a cpp file because the compiler was breaking. //So the explicit instantiation must be declared here rather than in Ember.cpp where //all of the other classes are done. template EMBER_API class Affine2D; #ifdef DO_DOUBLE template EMBER_API class Affine2D; #endif }