/* MoonWorks - Game Development Framework * Copyright 2022 Evan Hemsley */ /* Derived from code by Microsoft. * Released under the MIT license. * See microsoft.LICENSE for details. */ using System; using System.Globalization; namespace MoonWorks.Math.Fixed { /// /// A structure encapsulating a 3x2 fixed point matrix. /// public struct Matrix3x2 : IEquatable { #region Public Fields /// /// The first element of the first row /// public Fix64 M11; /// /// The second element of the first row /// public Fix64 M12; /// /// The first element of the second row /// public Fix64 M21; /// /// The second element of the second row /// public Fix64 M22; /// /// The first element of the third row /// public Fix64 M31; /// /// The second element of the third row /// public Fix64 M32; #endregion Public Fields private static readonly Matrix3x2 _identity = new Matrix3x2 ( 1, 0, 0, 1, 0, 0 ); private static readonly Fix64 RotationEpsilon = Fix64.FromFraction(1, 1000) * (Fix64.Pi / new Fix64(180)); /// /// Returns the multiplicative identity matrix. /// public static Matrix3x2 Identity { get { return _identity; } } /// /// Returns whether the matrix is the identity matrix. /// public bool IsIdentity { get { return M11 == Fix64.One && M22 == Fix64.One && // Check diagonal element first for early out. M12 == Fix64.Zero && M21 == Fix64.Zero && M31 == Fix64.Zero && M32 == Fix64.Zero; } } /// /// Gets or sets the translation component of this matrix. /// public Vector2 Translation { get { return new Vector2(M31, M32); } set { M31 = value.X; M32 = value.Y; } } /// /// Constructs a FixMatrix3x2 from the given components. /// public Matrix3x2(Fix64 m11, Fix64 m12, Fix64 m21, Fix64 m22, Fix64 m31, Fix64 m32) { M11 = m11; M12 = m12; M21 = m21; M22 = m22; M31 = m31; M32 = m32; } public Matrix3x2(int m11, int m12, int m21, int m22, int m31, int m32) { M11 = new Fix64(m11); M12 = new Fix64(m12); M21 = new Fix64(m21); M22 = new Fix64(m22); M31 = new Fix64(m31); M32 = new Fix64(m32); } /// /// Creates a translation matrix from the given vector. /// /// The translation position. /// A translation matrix. public static Matrix3x2 CreateTranslation(Vector2 position) { Matrix3x2 result; result.M11 = Fix64.One; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = Fix64.One; result.M31 = position.X; result.M32 = position.Y; return result; } /// /// Creates a translation matrix from the given X and Y components. /// /// The X position. /// The Y position. /// A translation matrix. public static Matrix3x2 CreateTranslation(Fix64 xPosition, Fix64 yPosition) { Matrix3x2 result; result.M11 = Fix64.One; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = Fix64.One; result.M31 = xPosition; result.M32 = yPosition; return result; } /// /// Creates a scale matrix from the given X and Y components. /// /// Value to scale by on the X-axis. /// Value to scale by on the Y-axis. /// A scaling matrix. public static Matrix3x2 CreateScale(Fix64 xScale, Fix64 yScale) { Matrix3x2 result; result.M11 = xScale; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = yScale; result.M31 = Fix64.Zero; result.M32 = Fix64.Zero; return result; } /// /// Creates a scale matrix that is offset by a given center point. /// /// Value to scale by on the X-axis. /// Value to scale by on the Y-axis. /// The center point. /// A scaling matrix. public static Matrix3x2 CreateScale(Fix64 xScale, Fix64 yScale, Vector2 centerPoint) { Matrix3x2 result; Fix64 tx = centerPoint.X * (Fix64.One - xScale); Fix64 ty = centerPoint.Y * (Fix64.One - yScale); result.M11 = xScale; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = yScale; result.M31 = tx; result.M32 = ty; return result; } /// /// Creates a scale matrix from the given vector scale. /// /// The scale to use. /// A scaling matrix. public static Matrix3x2 CreateScale(Vector2 scales) { Matrix3x2 result; result.M11 = scales.X; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = scales.Y; result.M31 = Fix64.Zero; result.M32 = Fix64.Zero; return result; } /// /// Creates a scale matrix from the given vector scale with an offset from the given center point. /// /// The scale to use. /// The center offset. /// A scaling matrix. public static Matrix3x2 CreateScale(Vector2 scales, Vector2 centerPoint) { Matrix3x2 result; Fix64 tx = centerPoint.X * (Fix64.One - scales.X); Fix64 ty = centerPoint.Y * (Fix64.One - scales.Y); result.M11 = scales.X; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = scales.Y; result.M31 = tx; result.M32 = ty; return result; } /// /// Creates a scale matrix that scales uniformly with the given scale. /// /// The uniform scale to use. /// A scaling matrix. public static Matrix3x2 CreateScale(Fix64 scale) { Matrix3x2 result; result.M11 = scale; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = scale; result.M31 = Fix64.Zero; result.M32 = Fix64.Zero; return result; } /// /// Creates a scale matrix that scales uniformly with the given scale with an offset from the given center. /// /// The uniform scale to use. /// The center offset. /// A scaling matrix. public static Matrix3x2 CreateScale(Fix64 scale, Vector2 centerPoint) { Matrix3x2 result; Fix64 tx = centerPoint.X * (Fix64.One - scale); Fix64 ty = centerPoint.Y * (Fix64.One - scale); result.M11 = scale; result.M12 = Fix64.Zero; result.M21 = Fix64.Zero; result.M22 = scale; result.M31 = tx; result.M32 = ty; return result; } /// /// Creates a skew matrix from the given angles in radians. /// /// The X angle, in radians. /// The Y angle, in radians. /// A skew matrix. public static Matrix3x2 CreateSkew(Fix64 radiansX, Fix64 radiansY) { Matrix3x2 result; Fix64 xTan = (Fix64) Fix64.Tan(radiansX); Fix64 yTan = (Fix64) Fix64.Tan(radiansY); result.M11 = Fix64.One; result.M12 = yTan; result.M21 = xTan; result.M22 = Fix64.One; result.M31 = Fix64.Zero; result.M32 = Fix64.Zero; return result; } /// /// Creates a skew matrix from the given angles in radians and a center point. /// /// The X angle, in radians. /// The Y angle, in radians. /// The center point. /// A skew matrix. public static Matrix3x2 CreateSkew(Fix64 radiansX, Fix64 radiansY, Vector2 centerPoint) { Matrix3x2 result; Fix64 xTan = (Fix64) Fix64.Tan(radiansX); Fix64 yTan = (Fix64) Fix64.Tan(radiansY); Fix64 tx = -centerPoint.Y * xTan; Fix64 ty = -centerPoint.X * yTan; result.M11 = Fix64.One; result.M12 = yTan; result.M21 = xTan; result.M22 = Fix64.One; result.M31 = tx; result.M32 = ty; return result; } /// /// Creates a rotation matrix using the given rotation in radians. /// /// The amount of rotation, in radians. /// A rotation matrix. public static Matrix3x2 CreateRotation(Fix64 radians) { Matrix3x2 result; radians = Fix64.IEEERemainder(radians, Fix64.PiTimes2); Fix64 c, s; if (radians > -RotationEpsilon && radians < RotationEpsilon) { // Exact case for zero rotation. c = Fix64.One; s = Fix64.Zero; } else if (radians > Fix64.PiOver2 - RotationEpsilon && radians < Fix64.PiOver2 + RotationEpsilon) { // Exact case for 90 degree rotation. c = Fix64.Zero; s = Fix64.One; } else if (radians < -Fix64.Pi + RotationEpsilon || radians > Fix64.Pi - RotationEpsilon) { // Exact case for 180 degree rotation. c = -Fix64.One; s = Fix64.Zero; } else if (radians > -Fix64.PiOver2 - RotationEpsilon && radians < -Fix64.PiOver2 + RotationEpsilon) { // Exact case for 270 degree rotation. c = Fix64.Zero; s = -Fix64.One; } else { // Arbitrary rotation. c = Fix64.Cos(radians); s = Fix64.Sin(radians); } // [ c s ] // [ -s c ] // [ 0 0 ] result.M11 = c; result.M12 = s; result.M21 = -s; result.M22 = c; result.M31 = Fix64.Zero; result.M32 = Fix64.Zero; return result; } /// /// Creates a rotation matrix using the given rotation in radians and a center point. /// /// The amount of rotation, in radians. /// The center point. /// A rotation matrix. public static Matrix3x2 CreateRotation(Fix64 radians, Vector2 centerPoint) { Matrix3x2 result; radians = Fix64.IEEERemainder(radians, Fix64.PiTimes2); Fix64 c, s; if (radians > -RotationEpsilon && radians < RotationEpsilon) { // Exact case for zero rotation. c = Fix64.One; s = Fix64.Zero; } else if (radians > Fix64.PiOver2 - RotationEpsilon && radians < Fix64.PiOver2 + RotationEpsilon) { // Exact case for 90 degree rotation. c = Fix64.Zero; s = Fix64.One; } else if (radians < -Fix64.Pi + RotationEpsilon || radians > Fix64.Pi - RotationEpsilon) { // Exact case for 180 degree rotation. c = -Fix64.One; s = Fix64.Zero; } else if (radians > -Fix64.PiOver2 - RotationEpsilon && radians < -Fix64.PiOver2 + RotationEpsilon) { // Exact case for 270 degree rotation. c = Fix64.Zero; s = -Fix64.One; } else { // Arbitrary rotation. c = (Fix64) Fix64.Cos(radians); s = (Fix64) Fix64.Sin(radians); } Fix64 x = centerPoint.X * (Fix64.One - c) + centerPoint.Y * s; Fix64 y = centerPoint.Y * (Fix64.One - c) - centerPoint.X * s; // [ c s ] // [ -s c ] // [ x y ] result.M11 = c; result.M12 = s; result.M21 = -s; result.M22 = c; result.M31 = x; result.M32 = y; return result; } /// /// Calculates the determinant for this matrix. /// The determinant is calculated by expanding the matrix with a third column whose values are (0,0,1). /// /// The determinant. public Fix64 GetDeterminant() { // There isn't actually any such thing as a determinant for a non-square matrix, // but this 3x2 type is really just an optimization of a 3x3 where we happen to // know the rightmost column is always (0, 0, 1). So we expand to 3x3 format: // // [ M11, M12, 0 ] // [ M21, M22, 0 ] // [ M31, M32, 1 ] // // Sum the diagonal products: // (M11 * M22 * 1) + (M12 * 0 * M31) + (0 * M21 * M32) // // Subtract the opposite diagonal products: // (M31 * M22 * 0) + (M32 * 0 * M11) + (1 * M21 * M12) // // Collapse out the constants and oh look, this is just a 2x2 determinant! return (M11 * M22) - (M21 * M12); } /// /// Attempts to invert the given matrix. If the operation succeeds, the inverted matrix is stored in the result parameter. /// /// The source matrix. /// The output matrix. /// True if the operation succeeded, False otherwise. public static bool Invert(Matrix3x2 matrix, out Matrix3x2 result) { Fix64 det = (matrix.M11 * matrix.M22) - (matrix.M21 * matrix.M12); if (Fix64.Abs(det) == Fix64.Zero) { result = new Matrix3x2(Fix64.Zero, Fix64.Zero, Fix64.Zero, Fix64.Zero, Fix64.Zero, Fix64.Zero); return false; } Fix64 invDet = Fix64.One / det; result.M11 = matrix.M22 * invDet; result.M12 = -matrix.M12 * invDet; result.M21 = -matrix.M21 * invDet; result.M22 = matrix.M11 * invDet; result.M31 = (matrix.M21 * matrix.M32 - matrix.M31 * matrix.M22) * invDet; result.M32 = (matrix.M31 * matrix.M12 - matrix.M11 * matrix.M32) * invDet; return true; } /// /// Linearly interpolates from matrix1 to matrix2, based on the third parameter. /// /// The first source matrix. /// The second source matrix. /// The relative weighting of matrix2. /// The interpolated matrix. public static Matrix3x2 Lerp(Matrix3x2 matrix1, Matrix3x2 matrix2, Fix64 amount) { Matrix3x2 result; // First row result.M11 = matrix1.M11 + (matrix2.M11 - matrix1.M11) * amount; result.M12 = matrix1.M12 + (matrix2.M12 - matrix1.M12) * amount; // Second row result.M21 = matrix1.M21 + (matrix2.M21 - matrix1.M21) * amount; result.M22 = matrix1.M22 + (matrix2.M22 - matrix1.M22) * amount; // Third row result.M31 = matrix1.M31 + (matrix2.M31 - matrix1.M31) * amount; result.M32 = matrix1.M32 + (matrix2.M32 - matrix1.M32) * amount; return result; } /// /// Negates the given matrix by multiplying all values by -1. /// /// The source matrix. /// The negated matrix. public static Matrix3x2 Negate(Matrix3x2 value) { Matrix3x2 result; result.M11 = -value.M11; result.M12 = -value.M12; result.M21 = -value.M21; result.M22 = -value.M22; result.M31 = -value.M31; result.M32 = -value.M32; return result; } /// /// Adds each matrix element in value1 with its corresponding element in value2. /// /// The first source matrix. /// The second source matrix. /// The matrix containing the summed values. public static Matrix3x2 Add(Matrix3x2 value1, Matrix3x2 value2) { Matrix3x2 result; result.M11 = value1.M11 + value2.M11; result.M12 = value1.M12 + value2.M12; result.M21 = value1.M21 + value2.M21; result.M22 = value1.M22 + value2.M22; result.M31 = value1.M31 + value2.M31; result.M32 = value1.M32 + value2.M32; return result; } /// /// Subtracts each matrix element in value2 from its corresponding element in value1. /// /// The first source matrix. /// The second source matrix. /// The matrix containing the resulting values. public static Matrix3x2 Subtract(Matrix3x2 value1, Matrix3x2 value2) { Matrix3x2 result; result.M11 = value1.M11 - value2.M11; result.M12 = value1.M12 - value2.M12; result.M21 = value1.M21 - value2.M21; result.M22 = value1.M22 - value2.M22; result.M31 = value1.M31 - value2.M31; result.M32 = value1.M32 - value2.M32; return result; } /// /// Multiplies two matrices together and returns the resulting matrix. /// /// The first source matrix. /// The second source matrix. /// The product matrix. public static Matrix3x2 Multiply(Matrix3x2 value1, Matrix3x2 value2) { Matrix3x2 result; // First row result.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21; result.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22; // Second row result.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21; result.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22; // Third row result.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value2.M31; result.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value2.M32; return result; } public Matrix4x4 ToMatrix4x4() { return new Matrix4x4( M11, M12, Fix64.Zero, Fix64.Zero, M21, M22, Fix64.Zero, Fix64.Zero, Fix64.Zero, Fix64.Zero, Fix64.One, Fix64.Zero, M31, M32, Fix64.Zero, Fix64.One ); } /// /// Scales all elements in a matrix by the given scalar factor. /// /// The source matrix. /// The scaling value to use. /// The resulting matrix. public static Matrix3x2 Multiply(Matrix3x2 value1, Fix64 value2) { Matrix3x2 result; result.M11 = value1.M11 * value2; result.M12 = value1.M12 * value2; result.M21 = value1.M21 * value2; result.M22 = value1.M22 * value2; result.M31 = value1.M31 * value2; result.M32 = value1.M32 * value2; return result; } /// /// Negates the given matrix by multiplying all values by -1. /// /// The source matrix. /// The negated matrix. public static Matrix3x2 operator -(Matrix3x2 value) { Matrix3x2 m; m.M11 = -value.M11; m.M12 = -value.M12; m.M21 = -value.M21; m.M22 = -value.M22; m.M31 = -value.M31; m.M32 = -value.M32; return m; } /// /// Adds each matrix element in value1 with its corresponding element in value2. /// /// The first source matrix. /// The second source matrix. /// The matrix containing the summed values. public static Matrix3x2 operator +(Matrix3x2 value1, Matrix3x2 value2) { Matrix3x2 m; m.M11 = value1.M11 + value2.M11; m.M12 = value1.M12 + value2.M12; m.M21 = value1.M21 + value2.M21; m.M22 = value1.M22 + value2.M22; m.M31 = value1.M31 + value2.M31; m.M32 = value1.M32 + value2.M32; return m; } /// /// Subtracts each matrix element in value2 from its corresponding element in value1. /// /// The first source matrix. /// The second source matrix. /// The matrix containing the resulting values. public static Matrix3x2 operator -(Matrix3x2 value1, Matrix3x2 value2) { Matrix3x2 m; m.M11 = value1.M11 - value2.M11; m.M12 = value1.M12 - value2.M12; m.M21 = value1.M21 - value2.M21; m.M22 = value1.M22 - value2.M22; m.M31 = value1.M31 - value2.M31; m.M32 = value1.M32 - value2.M32; return m; } /// /// Multiplies two matrices together and returns the resulting matrix. /// /// The first source matrix. /// The second source matrix. /// The product matrix. public static Matrix3x2 operator *(Matrix3x2 value1, Matrix3x2 value2) { Matrix3x2 m; // First row m.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21; m.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22; // Second row m.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21; m.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22; // Third row m.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value2.M31; m.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value2.M32; return m; } /// /// Scales all elements in a matrix by the given scalar factor. /// /// The source matrix. /// The scaling value to use. /// The resulting matrix. public static Matrix3x2 operator *(Matrix3x2 value1, Fix64 value2) { Matrix3x2 m; m.M11 = value1.M11 * value2; m.M12 = value1.M12 * value2; m.M21 = value1.M21 * value2; m.M22 = value1.M22 * value2; m.M31 = value1.M31 * value2; m.M32 = value1.M32 * value2; return m; } /// /// Returns a boolean indicating whether the given matrices are equal. /// /// The first source matrix. /// The second source matrix. /// True if the matrices are equal; False otherwise. public static bool operator ==(Matrix3x2 value1, Matrix3x2 value2) { return (value1.M11 == value2.M11 && value1.M22 == value2.M22 && // Check diagonal element first for early out. value1.M12 == value2.M12 && value1.M21 == value2.M21 && value1.M31 == value2.M31 && value1.M32 == value2.M32); } /// /// Returns a boolean indicating whether the given matrices are not equal. /// /// The first source matrix. /// The second source matrix. /// True if the matrices are not equal; False if they are equal. public static bool operator !=(Matrix3x2 value1, Matrix3x2 value2) { return (value1.M11 != value2.M11 || value1.M12 != value2.M12 || value1.M21 != value2.M21 || value1.M22 != value2.M22 || value1.M31 != value2.M31 || value1.M32 != value2.M32); } /// /// Casts to floating point Matrix3x2. /// public static explicit operator Math.Float.Matrix3x2(Matrix3x2 matrix) { return new Math.Float.Matrix3x2( (float) matrix.M11, (float) matrix.M12, (float) matrix.M21, (float) matrix.M22, (float) matrix.M31, (float) matrix.M32 ); } /// /// Returns a boolean indicating whether the matrix is equal to the other given matrix. /// /// The other matrix to test equality against. /// True if this matrix is equal to other; False otherwise. public bool Equals(Matrix3x2 other) { return (M11 == other.M11 && M22 == other.M22 && // Check diagonal element first for early out. M12 == other.M12 && M21 == other.M21 && M31 == other.M31 && M32 == other.M32); } /// /// Returns a boolean indicating whether the given Object is equal to this matrix instance. /// /// The Object to compare against. /// True if the Object is equal to this matrix; False otherwise. public override bool Equals(object obj) { if (obj is Matrix3x2) { return Equals((Matrix3x2) obj); } return false; } /// /// Returns a String representing this matrix instance. /// /// The string representation. public override string ToString() { CultureInfo ci = CultureInfo.CurrentCulture; return String.Format(ci, "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}", M11.ToString(ci), M12.ToString(ci), M21.ToString(ci), M22.ToString(ci), M31.ToString(ci), M32.ToString(ci)); } /// /// Returns the hash code for this instance. /// /// The hash code. public override int GetHashCode() { return M11.GetHashCode() + M12.GetHashCode() + M21.GetHashCode() + M22.GetHashCode() + M31.GetHashCode() + M32.GetHashCode(); } } }