// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using Avalonia.Utilities; using System; using System.Globalization; using System.Linq; namespace Avalonia { /// /// A 2x3 matrix. /// public struct Matrix { private readonly double _m11; private readonly double _m12; private readonly double _m21; private readonly double _m22; private readonly double _m31; private readonly double _m32; /// /// Initializes a new instance of the struct. /// /// The first element of the first row. /// The second element of the first row. /// The first element of the second row. /// The second element of the second row. /// The first element of the third row. /// The second element of the third row. public Matrix( double m11, double m12, double m21, double m22, double offsetX, double offsetY) { _m11 = m11; _m12 = m12; _m21 = m21; _m22 = m22; _m31 = offsetX; _m32 = offsetY; } /// /// Returns the multiplicative identity matrix. /// public static Matrix Identity => new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /// /// Returns whether the matrix is the identity matrix. /// public bool IsIdentity => Equals(Identity); /// /// HasInverse Property - returns true if this matrix is invertable, false otherwise. /// public bool HasInverse => GetDeterminant() != 0; /// /// The first element of the first row /// public double M11 => _m11; /// /// The second element of the first row /// public double M12 => _m12; /// /// The first element of the second row /// public double M21 => _m21; /// /// The second element of the second row /// public double M22 => _m22; /// /// The first element of the third row /// public double M31 => _m31; /// /// The second element of the third row /// public double M32 => _m32; /// /// Multiplies two matrices together and returns the resulting matrix. /// /// The first source matrix. /// The second source matrix. /// The product matrix. public static Matrix operator *(Matrix value1, Matrix value2) { return new Matrix( (value1.M11 * value2.M11) + (value1.M12 * value2.M21), (value1.M11 * value2.M12) + (value1.M12 * value2.M22), (value1.M21 * value2.M11) + (value1.M22 * value2.M21), (value1.M21 * value2.M12) + (value1.M22 * value2.M22), (value1._m31 * value2.M11) + (value1._m32 * value2.M21) + value2._m31, (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); } /// /// Negates the given matrix by multiplying all values by -1. /// /// The source matrix. /// The negated matrix. public static Matrix operator -(Matrix value) { return value.Invert(); } /// /// 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 ==(Matrix value1, Matrix value2) { return value1.Equals(value2); } /// /// 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 !=(Matrix value1, Matrix value2) { return !value1.Equals(value2); } /// /// Creates a rotation matrix using the given rotation in radians. /// /// The amount of rotation, in radians. /// A rotation matrix. public static Matrix CreateRotation(double radians) { double cos = Math.Cos(radians); double sin = Math.Sin(radians); return new Matrix(cos, sin, -sin, cos, 0, 0); } /// /// Creates a skew matrix from the given axis skew angles in radians. /// /// The amount of skew along the X-axis, in radians. /// The amount of skew along the Y-axis, in radians. /// A rotation matrix. public static Matrix CreateSkew(double xAngle, double yAngle) { double tanX = Math.Tan(xAngle); double tanY = Math.Tan(yAngle); return new Matrix(1.0, tanY, tanX, 1.0, 0.0, 0.0); } /// /// 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 Matrix CreateScale(double xScale, double yScale) { return CreateScale(new Vector(xScale, yScale)); } /// /// Creates a scale matrix from the given vector scale. /// /// The scale to use. /// A scaling matrix. public static Matrix CreateScale(Vector scales) { return new Matrix(scales.X, 0, 0, scales.Y, 0, 0); } /// /// Creates a translation matrix from the given vector. /// /// The translation position. /// A translation matrix. public static Matrix CreateTranslation(Vector position) { return CreateTranslation(position.X, position.Y); } /// /// Creates a translation matrix from the given X and Y components. /// /// The X position. /// The Y position. /// A translation matrix. public static Matrix CreateTranslation(double xPosition, double yPosition) { return new Matrix(1.0, 0.0, 0.0, 1.0, xPosition, yPosition); } /// /// Converts an ange in degrees to radians. /// /// The angle in degrees. /// The angle in radians. public static double ToRadians(double angle) { return angle * 0.0174532925; } /// /// Calculates the determinant for this matrix. /// /// The determinant. /// /// The determinant is calculated by expanding the matrix with a third column whose /// values are (0,0,1). /// public double GetDeterminant() { return (_m11 * _m22) - (_m12 * _m21); } /// /// 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(Matrix other) { return _m11 == other.M11 && _m12 == other.M12 && _m21 == other.M21 && _m22 == other.M22 && _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 Matrix)) { return false; } return Equals((Matrix)obj); } /// /// 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(); } /// /// 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)); } /// /// Inverts the Matrix. /// /// The inverted matrix. public Matrix Invert() { if (GetDeterminant() == 0) { throw new InvalidOperationException("Transform is not invertible."); } double d = GetDeterminant(); return new Matrix( _m22 / d, -_m12 / d, -_m21 / d, _m11 / d, ((_m21 * _m32) - (_m22 * _m31)) / d, ((_m12 * _m31) - (_m11 * _m32)) / d); } /// /// Parses a string. /// /// The string. /// The . public static Matrix Parse(string s) { using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Matrix")) { return new Matrix( tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble() ); } } } }