#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) const
{
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) const
{
return TransformVector(v);
}
///
/// Return a copy of the object with all values A-F scaled by the specified amount.
///
/// The amount to scale by
/// A new Affine2D which a scaled copy of this instance
template
Affine2D Affine2D:: operator * (T t) const
{
return Affine2D(A() * t,
D() * t,
B() * t,
E() * t,
C() * t,
F() * t);
}
///
/// 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 (A() == 1) &&
(B() == 0) &&
(C() == 0) &&
(D() == 0) &&
(E() == 1) &&
(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 (A() == 0) &&
(B() == 0) &&
(C() == 0) &&
(D() == 0) &&
(E() == 0) &&
(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));
}
///
/// Scales all values A-F by the specified amount.
///
/// The amount to scale by
template
void Affine2D::Scale(T amount)
{
A(A() * amount);
B(B() * amount);
C(C() * amount);
D(D() * amount);
E(E() * amount);
F(F() * amount);
}
///
/// Scales all values A,B,D,E by the specified amount.
///
/// The amount to scale by
template
void Affine2D::ScaleXY(T amount)
{
A(A() * amount);
B(B() * amount);
D(D() * amount);
E(E() * amount);
}
///
/// Rotate this affine transform around its origin by the specified angle in degrees.
///
/// The angle to rotate by
template
void Affine2D::Rotate(T rad)
{
m4T origMat4 = ToMat4ColMajor(true);//Must center and use column major for glm to work.
m4T newMat4 = glm::rotate(origMat4, rad, 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]);
}
template
void Affine2D::RotateTrans(T rad)
{
m4T origMat4 = TransToMat4ColMajor();//Only put translation in this matrix.
m4T newMat4 = glm::rotate(origMat4, rad, v3T(0, 0, 1));//Assuming only rotating around z.
C(newMat4[0][3]);//Use direct assignments instead of constructor to skip assigning A, B, D, E.
F(newMat4[1][3]);
}
///
/// 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;
}
template
typename m4T Affine2D::TransToMat4ColMajor() const
{
m4T mat(1, 0, 0, C(), //Col0...
0, 1, 0, F(), //1
0, 0, 1, 0, //2
0, 0, 0, 1);//3
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.
template
string Affine2D::ToString() const
{
ostringstream ss;
ss << "A: " << A() << " "
<< "B: " << B() << " "
<< "C: " << C()
<< "\nD: " << D() << " "
<< "E: " << E() << " "
<< "F: " << F();
return ss.str();
}
///
/// 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
}