// Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2007 Novell, Inc. (http://www.novell.com) // // Authors: // Chris Toshok (toshok@ximian.com) // using System; using System.ComponentModel; using System.Globalization; using System.Windows.Markup; using System.Windows.Media.Converters; using System.Windows.Threading; namespace System.Windows.Media { [Serializable] [TypeConverter (typeof(MatrixConverter))] [ValueSerializer (typeof (MatrixValueSerializer))] public struct Matrix : IFormattable { double _m11; double _m12; double _m21; double _m22; double _offsetX; double _offsetY; public Matrix (double m11, double m12, double m21, double m22, double offsetX, double offsetY) { this._m11 = m11; this._m12 = m12; this._m21 = m21; this._m22 = m22; this._offsetX = offsetX; this._offsetY = offsetY; } public void Append (Matrix matrix) { double _m11; double _m21; double _m12; double _m22; double _offsetX; double _offsetY; _m11 = this._m11 * matrix.M11 + this._m12 * matrix.M21; _m12 = this._m11 * matrix.M12 + this._m12 * matrix.M22; _m21 = this._m21 * matrix.M11 + this._m22 * matrix.M21; _m22 = this._m21 * matrix.M12 + this._m22 * matrix.M22; _offsetX = this._offsetX * matrix.M11 + this._offsetY * matrix.M21 + matrix.OffsetX; _offsetY = this._offsetX * matrix.M12 + this._offsetY * matrix.M22 + matrix.OffsetY; this._m11 = _m11; this._m12 = _m12; this._m21 = _m21; this._m22 = _m22; this._offsetX = _offsetX; this._offsetY = _offsetY; } public bool Equals (Matrix value) { return (_m11 == value.M11 && _m12 == value.M12 && _m21 == value.M21 && _m22 == value.M22 && _offsetX == value.OffsetX && _offsetY == value.OffsetY); } public override bool Equals (object o) { if (!(o is Matrix)) return false; return Equals ((Matrix)o); } public static bool Equals (Matrix matrix1, Matrix matrix2) { return matrix1.Equals (matrix2); } public override int GetHashCode () { unchecked { var hashCode = _m11.GetHashCode (); hashCode = (hashCode * 397) ^ _m12.GetHashCode (); hashCode = (hashCode * 397) ^ _m21.GetHashCode (); hashCode = (hashCode * 397) ^ _m22.GetHashCode (); hashCode = (hashCode * 397) ^ _offsetX.GetHashCode (); hashCode = (hashCode * 397) ^ _offsetY.GetHashCode (); return hashCode; } } public void Invert () { if (!HasInverse) throw new InvalidOperationException ("Transform is not invertible."); double d = Determinant; /* 1/(ad-bc)[d -b; -c a] */ double _m11 = this._m22; double _m12 = -this._m12; double _m21 = -this._m21; double _m22 = this._m11; double _offsetX = this._m21 * this._offsetY - this._m22 * this._offsetX; double _offsetY = this._m12 * this._offsetX - this._m11 * this._offsetY; this._m11 = _m11 / d; this._m12 = _m12 / d; this._m21 = _m21 / d; this._m22 = _m22 / d; this._offsetX = _offsetX / d; this._offsetY = _offsetY / d; } public static Matrix Multiply (Matrix trans1, Matrix trans2) { Matrix m = trans1; m.Append (trans2); return m; } public static bool operator == (Matrix matrix1, Matrix matrix2) { return matrix1.Equals (matrix2); } public static bool operator != (Matrix matrix1, Matrix matrix2) { return !matrix1.Equals (matrix2); } public static Matrix operator * (Matrix trans1, Matrix trans2) { Matrix result = trans1; result.Append (trans2); return result; } public static Matrix Parse (string source) { if (source == null) throw new ArgumentNullException ("source"); Matrix value; if (source.Trim () == "Identity") { value = Identity; } else { var parts = source.Split (','); if (parts.Length != 6) throw new FormatException (string.Format ("Invalid Matrix format: {0}", source)); double m11; double m12; double m21; double m22; double offsetX; double offsetY; if (double.TryParse (parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out m11) && double.TryParse (parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out m12) && double.TryParse (parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out m21) && double.TryParse (parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out m22) && double.TryParse (parts[4], NumberStyles.Float, CultureInfo.InvariantCulture, out offsetX) && double.TryParse (parts[5], NumberStyles.Float, CultureInfo.InvariantCulture, out offsetY)) { value = new Matrix (m11, m12, m21, m22, offsetX, offsetY); } else { throw new FormatException (string.Format ("Invalid Matrix format: {0}", source)); } } return value; } public void Prepend (Matrix matrix) { double _m11; double _m21; double _m12; double _m22; double _offsetX; double _offsetY; _m11 = matrix.M11 * this._m11 + matrix.M12 * this._m21; _m12 = matrix.M11 * this._m12 + matrix.M12 * this._m22; _m21 = matrix.M21 * this._m11 + matrix.M22 * this._m21; _m22 = matrix.M21 * this._m12 + matrix.M22 * this._m22; _offsetX = matrix.OffsetX * this._m11 + matrix.OffsetY * this._m21 + this._offsetX; _offsetY = matrix.OffsetX * this._m12 + matrix.OffsetY * this._m22 + this._offsetY; this._m11 = _m11; this._m12 = _m12; this._m21 = _m21; this._m22 = _m22; this._offsetX = _offsetX; this._offsetY = _offsetY; } public void Rotate (double angle) { // R_theta==[costheta -sintheta; sintheta costheta], double theta = angle * Math.PI / 180; Matrix r_theta = new Matrix (Math.Cos (theta), Math.Sin(theta), -Math.Sin (theta), Math.Cos(theta), 0, 0); Append (r_theta); } public void RotateAt (double angle, double centerX, double centerY) { Translate (-centerX, -centerY); Rotate (angle); Translate (centerX, centerY); } public void RotateAtPrepend (double angle, double centerX, double centerY) { Matrix m = Matrix.Identity; m.RotateAt (angle, centerX, centerY); Prepend (m); } public void RotatePrepend (double angle) { Matrix m = Matrix.Identity; m.Rotate (angle); Prepend (m); } public void Scale (double scaleX, double scaleY) { Matrix scale = new Matrix (scaleX, 0, 0, scaleY, 0, 0); Append (scale); } public void ScaleAt (double scaleX, double scaleY, double centerX, double centerY) { Translate (-centerX, -centerY); Scale (scaleX, scaleY); Translate (centerX, centerY); } public void ScaleAtPrepend (double scaleX, double scaleY, double centerX, double centerY) { Matrix m = Matrix.Identity; m.ScaleAt (scaleX, scaleY, centerX, centerY); Prepend (m); } public void ScalePrepend (double scaleX, double scaleY) { Matrix m = Matrix.Identity; m.Scale (scaleX, scaleY); Prepend (m); } public void SetIdentity () { _m11 = _m22 = 1.0; _m12 = _m21 = 0.0; _offsetX = _offsetY = 0.0; } public void Skew (double skewX, double skewY) { Matrix skew_m = new Matrix (1, Math.Tan (skewY * Math.PI / 180), Math.Tan (skewX * Math.PI / 180), 1, 0, 0); Append (skew_m); } public void SkewPrepend (double skewX, double skewY) { Matrix m = Matrix.Identity; m.Skew (skewX, skewY); Prepend (m); } string IFormattable.ToString (string format, IFormatProvider provider) { return ToString (provider); } public override string ToString () { return ToString (null); } public string ToString (IFormatProvider provider) { return IsIdentity ? "Identity" : string.Concat (_m11, ",", _m12, ",", _m21, ",", _m22, ",", _offsetX, ",", _offsetY); } public Point Transform (Point point) { return Point.Multiply (point, this); } public void Transform (Point[] points) { for (int i = 0; i < points.Length; i ++) points[i] = Transform (points[i]); } public Vector Transform (Vector vector) { return Vector.Multiply (vector, this); } public void Transform (Vector[] vectors) { for (int i = 0; i < vectors.Length; i ++) vectors[i] = Transform (vectors[i]); } public void Translate (double offsetX, double offsetY) { this._offsetX += offsetX; this._offsetY += offsetY; } public void TranslatePrepend (double offsetX, double offsetY) { Matrix m = Matrix.Identity; m.Translate (offsetX, offsetY); Prepend (m); } public double Determinant { get { return _m11 * _m22 - _m12 * _m21; } } public bool HasInverse { get { return Determinant != 0; } } public static Matrix Identity { get { return new Matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0); } } public bool IsIdentity { get { return Equals (Matrix.Identity); } } public double M11 { get { return _m11; } set { _m11 = value; } } public double M12 { get { return _m12; } set { _m12 = value; } } public double M21 { get { return _m21; } set { _m21 = value; } } public double M22 { get { return _m22; } set { _m22 = value; } } public double OffsetX { get { return _offsetX; } set { _offsetX = value; } } public double OffsetY { get { return _offsetY; } set { _offsetY = value; } } } }