using System; using UnityEngine; namespace ToolBuddy.ThirdParty.VectorGraphics { /// A 2x3 transformation matrix used for 2D operations. public struct Matrix2D { // memory layout: // // row no (=vertical) // | 0 1 2 // ---+------------ // 0 | m00 m10 0 // column no 1 | m01 m11 0 // (=horiz) 2 | m02 m12 1 /// The matrix member at (0,0) public float m00; /// The matrix member at (1,0) public float m10; /// The matrix member at (0,1) public float m01; /// The matrix member at (1,1) public float m11; /// The matrix member at (0,2) public float m02; /// The matrix member at (1,2) public float m12; /// Initializes a Matrix2D with column vectors /// The first column /// The second column /// The third column public Matrix2D(Vector2 column0, Vector2 column1, Vector2 column2) { this.m00 = column0.x; this.m01 = column1.x; this.m02 = column2.x; this.m10 = column0.y; this.m11 = column1.y; this.m12 = column2.y; } /// Access element at [row, column]. /// The value at [row, column] public float this[int row, int column] { get { return this[row + column * 2]; } set { this[row + column * 2] = value; } } /// Access element at sequential index (0..5 inclusive). /// The value at [index] public float this[int index] { get { switch (index) { case 0: return m00; case 1: return m10; case 2: return m01; case 3: return m11; case 4: return m02; case 5: return m12; default: throw new IndexOutOfRangeException("Invalid matrix index!"); } } set { switch (index) { case 0: m00 = value; break; case 1: m10 = value; break; case 2: m01 = value; break; case 3: m11 = value; break; case 4: m02 = value; break; case 5: m12 = value; break; default: throw new IndexOutOfRangeException("Invalid matrix index!"); } } } /// Gets a hashcode of the matrix. /// Used to allow Matrix3x3s to be used as keys in hash tables. /// The hashcode of the matrix public override int GetHashCode() { return GetColumn(0).GetHashCode() ^ (GetColumn(1).GetHashCode() << 2) ^ (GetColumn(2).GetHashCode() >> 2); } /// Checks if two matrices are equal. /// The other matrix to compare with /// Used to allow Matrix3x3s to be used as keys in hash tables. /// True when the matrix is equal to "other" public override bool Equals(object other) { if (!(other is Matrix2D)) return false; Matrix2D rhs = (Matrix2D)other; return GetColumn(0).Equals(rhs.GetColumn(0)) && GetColumn(1).Equals(rhs.GetColumn(1)) && GetColumn(2).Equals(rhs.GetColumn(2)); } /// Multiplies two matrices. /// The left hand side matrix of the operation /// The right hand side matrix of the operation /// The multiplied matrix public static Matrix2D operator*(Matrix2D lhs, Matrix2D rhs) { Matrix2D res; res.m00 = lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10; res.m01 = lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11; res.m02 = lhs.m00 * rhs.m02 + lhs.m01 * rhs.m12 + lhs.m02; res.m10 = lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10; res.m11 = lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11; res.m12 = lhs.m10 * rhs.m02 + lhs.m11 * rhs.m12 + lhs.m12; return res; } /// Transforms a Vector2 by a matrix. /// The left hand side matrix of the operation /// The vector the matrix will be multiplied with /// The transformed vector public static Vector2 operator*(Matrix2D lhs, Vector2 vector) { Vector2 res; res.x = lhs.m00 * vector.x + lhs.m01 * vector.y + lhs.m02; res.y = lhs.m10 * vector.x + lhs.m11 * vector.y + lhs.m12; return res; } /// Checks if two matrices are equal. /// The left hand side matrix of the comparison /// The right hand side matrix of the comparison /// True if "lhs" and "rhs" are equal, or false otherwise. public static bool operator==(Matrix2D lhs, Matrix2D rhs) { // Returns false in the presence of NaN values. return lhs.GetColumn(0) == rhs.GetColumn(0) && lhs.GetColumn(1) == rhs.GetColumn(1) && lhs.GetColumn(2) == rhs.GetColumn(2); } /// Checks if two matrices are not equal. /// The left hand side matrix of the comparison /// The right hand side matrix of the comparison /// True if "lhs" and "rhs" not are equal, or false otherwise. public static bool operator!=(Matrix2D lhs, Matrix2D rhs) { // Returns true in the presence of NaN values. return !(lhs == rhs); } /// Gets a column of the matrix. /// The column index, between 0 and 2 inclusively /// The column at "index" public Vector2 GetColumn(int index) { switch (index) { case 0: return new Vector2(m00, m10); case 1: return new Vector2(m01, m11); case 2: return new Vector2(m02, m12); default: throw new IndexOutOfRangeException("Invalid column index!"); } } /// Gets a row of the matrix. /// The row index, between 0 and 1 inclusively /// The row at "index" public Vector3 GetRow(int index) { switch (index) { case 0: return new Vector3(m00, m01, m02); case 1: return new Vector3(m10, m11, m12); default: throw new IndexOutOfRangeException("Invalid row index!"); } } /// Sets a column of the matrix. /// The column index, between 0 and 2 inclusively /// The column public void SetColumn(int index, Vector2 column) { this[0, index] = column.x; this[1, index] = column.y; } /// Sets a row of the matrix. /// The column index, between 0 and 1 inclusively /// The row public void SetRow(int index, Vector3 row) { this[index, 0] = row.x; this[index, 1] = row.y; this[index, 2] = row.z; } /// Transforms a position by this matrix (effectively by 2x3). /// The point to multiply with this matrix /// The multiplied point public Vector2 MultiplyPoint(Vector2 point) { Vector2 res; res.x = this.m00 * point.x + this.m01 * point.y + this.m02; res.y = this.m10 * point.x + this.m11 * point.y + this.m12; return res; } /// Transforms a direction by this matrix. /// The direction to multiply with this matrix /// The multiplied direction public Vector2 MultiplyVector(Vector2 vector) { Vector2 res; res.x = this.m00 * vector.x + this.m01 * vector.y; res.y = this.m10 * vector.x + this.m11 * vector.y; return res; } /// Computes the inverse of the matrix. /// The inverse matrix public Matrix2D Inverse() { Matrix2D invMat = new Matrix2D(); float det = this[0, 0] * this[1, 1] - this[0, 1] * this[1, 0]; if (Mathf.Approximately(0.0f, det)) return zero; float invDet = 1.0F / det; invMat[0, 0] = this[1, 1] * invDet; invMat[0, 1] = -this[0, 1] * invDet; invMat[1, 0] = -this[1, 0] * invDet; invMat[1, 1] = this[0, 0] * invDet; // Do the translation part invMat[0, 2] = -(this[0, 2] * invMat[0, 0] + this[1, 2] * invMat[0, 1]); invMat[1, 2] = -(this[0, 2] * invMat[1, 0] + this[1, 2] * invMat[1, 1]); return invMat; } /// Creates a scaling matrix. /// The scaling vector /// The scaling matrix public static Matrix2D Scale(Vector2 vector) { Matrix2D m; m.m00 = vector.x; m.m01 = 0F; m.m02 = 0F; m.m10 = 0F; m.m11 = vector.y; m.m12 = 0F; return m; } /// Creates a translation matrix. /// The translation vector /// The translation matrix public static Matrix2D Translate(Vector2 vector) { Matrix2D m; m.m00 = 1F; m.m01 = 0F; m.m02 = vector.x; m.m10 = 0F; m.m11 = 1F; m.m12 = vector.y; return m; } /// Creates a right-hand side rotation matrix. /// The rotation angle, in radians /// The rotation matrix public static Matrix2D RotateRH(float angleRadians) { return RotateLH(-angleRadians); } /// Creates a left-hand side rotation matrix. /// The rotation angle, in radians /// The rotation matrix public static Matrix2D RotateLH(float angleRadians) { // No SinCos? I hope the compiler optimizes this float s = Mathf.Sin(angleRadians); float c = Mathf.Cos(angleRadians); Matrix2D m; m.m00 = c; m.m10 = -s; m.m01 = s; m.m11 = c; m.m02 = 0.0F; m.m12 = 0.0F; return m; } /// Creates a skew matrix on X. /// The skew angle, in radians /// The skew matrix public static Matrix2D SkewX(float angleRadians) { Matrix2D m; m.m00 = 1.0f; m.m01 = Mathf.Tan(angleRadians); m.m02 = 0F; m.m10 = 0F; m.m11 = 1.0f; m.m12 = 0F; return m; } /// Creates a skew matrix on U. /// The skew angle, in radians /// The skew matrix public static Matrix2D SkewY(float angleRadians) { Matrix2D m; m.m00 = 1.0f; m.m01 = 0F; m.m02 = 0F; m.m10 = Mathf.Tan(angleRadians); m.m11 = 1.0f; m.m12 = 0F; return m; } static readonly Matrix2D zeroMatrix = new Matrix2D(new Vector2(0, 0), new Vector2(0, 0), new Vector2(0, 0)); /// Returns a matrix with all elements set to zero (read-only). /// The zero matrix public static Matrix2D zero { get { return zeroMatrix; } } static readonly Matrix2D identityMatrix = new Matrix2D(new Vector2(1, 0), new Vector2(0, 1), new Vector2(0, 0)); /// Returns the identity matrix (read-only). /// The identity matrix public static Matrix2D identity { get { return identityMatrix; } } /// Returns a string representation of the matrix. /// The matrix string representation public override string ToString() { return string.Format("{0:F5}\t{1:F5}\t{2:F5}\n{3:F5}\t{4:F5}\t{5:F5}\n", m00, m01, m02, m10, m11, m12); } /// Returns a string representation of the matrix using a format. /// The format to be used for the matrix components /// The matrix string representation public string ToString(string format) { return string.Format("{0}\t{1}\t{2}\n{3}\t{4}\t{5}\n", m00.ToString(format), m01.ToString(format), m02.ToString(format), m10.ToString(format), m11.ToString(format), m12.ToString(format)); } } } //namespace