Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / System / Windows / Media / Matrix.cs / 1305600 / Matrix.cs
//------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, 2001, 2002 // // File: Matrix.cs //----------------------------------------------------------------------------- using System; using System.Diagnostics; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using MS.Internal; using System.Text; using System.Collections; using System.Globalization; using System.Windows; using System.Windows.Media; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using SR=MS.Internal.WindowsBase.SR; using SRID=MS.Internal.WindowsBase.SRID; // IMPORTANT // // Rules for using matrix types. // // internal enum MatrixTypes // { // TRANSFORM_IS_IDENTITY = 0, // TRANSFORM_IS_TRANSLATION = 1, // TRANSFORM_IS_SCALING = 2, // TRANSFORM_IS_UNKNOWN = 4 // } // // 1. Matrix type must be one of 0, 1, 2, 4, or 3 (for scale and translation) // 2. Matrix types are true but not exact! (E.G. A scale or identity transform could be marked as unknown or scale+translate.) // 3. Therefore read-only operations can ignore the type with one exception // EXCEPTION: A matrix tagged identity might have any coefficients instead of 1,0,0,1,0,0 // This is the (now) classic no default constructor for structs issue // 4. Matrix._type must be maintained by mutation operations // 5. MS.Internal.MatrixUtil uses unsafe code to access the private members of Matrix including _type. // // In Jan 2005 the matrix types were changed from being EXACT (i.e. a // scale matrix is always tagged as a scale and not something more // general.) This resulted in about a 2% speed up in matrix // multiplication. // // The special cases for matrix multiplication speed up scale*scale // and translation*translation by 30% compared to a single "no-branch" // multiplication algorithm. Matrix multiplication of two unknown // matrices is slowed by 20% compared to the no-branch algorithm. // // windows/wcp/DevTest/Drts/MediaApi/MediaPerf.cs includes the // simple test of matrix multiplication speed used for these results. namespace System.Windows.Media { ////// Matrix /// public partial struct Matrix: IFormattable { // the transform is identity by default // Actually fill in the fields - some (internal) code uses the fields directly for perf. private static Matrix s_identity = CreateIdentity(); #region Constructor ////// Creates a matrix of the form /// / m11, m12, 0 \ /// | m21, m22, 0 | /// \ offsetX, offsetY, 1 / /// 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; _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; _padding = 0; // We will detect EXACT identity, scale, translation or // scale+translation and use special case algorithms. DeriveMatrixType(); } #endregion Constructor #region Identity ////// Identity /// public static Matrix Identity { get { return s_identity; } } ////// Sets the matrix to identity. /// public void SetIdentity() { _type = MatrixTypes.TRANSFORM_IS_IDENTITY; } ////// Tests whether or not a given transform is an identity transform /// public bool IsIdentity { get { return (_type == MatrixTypes.TRANSFORM_IS_IDENTITY || (_m11 == 1 && _m12 == 0 && _m21 == 0 && _m22 == 1 && _offsetX == 0 && _offsetY == 0)); } } #endregion Identity #region Operators ////// Multiplies two transformations. /// public static Matrix operator *(Matrix trans1, Matrix trans2) { MatrixUtil.MultiplyMatrix(ref trans1, ref trans2); trans1.Debug_CheckType(); return trans1; } ////// Multiply /// public static Matrix Multiply(Matrix trans1, Matrix trans2) { MatrixUtil.MultiplyMatrix(ref trans1, ref trans2); trans1.Debug_CheckType(); return trans1; } #endregion Operators #region Combine Methods ////// Append - "this" becomes this * matrix, the same as this *= matrix. /// /// The Matrix to append to this Matrix public void Append(Matrix matrix) { this *= matrix; } ////// Prepend - "this" becomes matrix * this, the same as this = matrix * this. /// /// The Matrix to prepend to this Matrix public void Prepend(Matrix matrix) { this = matrix * this; } ////// Rotates this matrix about the origin /// /// The angle to rotate specifed in degrees public void Rotate(double angle) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this *= CreateRotationRadians(angle * (Math.PI/180.0)); } ////// Prepends a rotation about the origin to "this" /// /// The angle to rotate specifed in degrees public void RotatePrepend(double angle) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this = CreateRotationRadians(angle * (Math.PI/180.0)) * this; } ////// Rotates this matrix about the given point /// /// The angle to rotate specifed in degrees /// The centerX of rotation /// The centerY of rotation public void RotateAt(double angle, double centerX, double centerY) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this *= CreateRotationRadians(angle * (Math.PI/180.0), centerX, centerY); } ////// Prepends a rotation about the given point to "this" /// /// The angle to rotate specifed in degrees /// The centerX of rotation /// The centerY of rotation public void RotateAtPrepend(double angle, double centerX, double centerY) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this = CreateRotationRadians(angle * (Math.PI/180.0), centerX, centerY) * this; } ////// Scales this matrix around the origin /// /// The scale factor in the x dimension /// The scale factor in the y dimension public void Scale(double scaleX, double scaleY) { this *= CreateScaling(scaleX, scaleY); } ////// Prepends a scale around the origin to "this" /// /// The scale factor in the x dimension /// The scale factor in the y dimension public void ScalePrepend(double scaleX, double scaleY) { this = CreateScaling(scaleX, scaleY) * this; } ////// Scales this matrix around the center provided /// /// The scale factor in the x dimension /// The scale factor in the y dimension /// The centerX about which to scale /// The centerY about which to scale public void ScaleAt(double scaleX, double scaleY, double centerX, double centerY) { this *= CreateScaling(scaleX, scaleY, centerX, centerY); } ////// Prepends a scale around the center provided to "this" /// /// The scale factor in the x dimension /// The scale factor in the y dimension /// The centerX about which to scale /// The centerY about which to scale public void ScaleAtPrepend(double scaleX, double scaleY, double centerX, double centerY) { this = CreateScaling(scaleX, scaleY, centerX, centerY) * this; } ////// Skews this matrix /// /// The skew angle in the x dimension in degrees /// The skew angle in the y dimension in degrees public void Skew(double skewX, double skewY) { skewX %= 360; skewY %= 360; this *= CreateSkewRadians(skewX * (Math.PI/180.0), skewY * (Math.PI/180.0)); } ////// Prepends a skew to this matrix /// /// The skew angle in the x dimension in degrees /// The skew angle in the y dimension in degrees public void SkewPrepend(double skewX, double skewY) { skewX %= 360; skewY %= 360; this = CreateSkewRadians(skewX * (Math.PI/180.0), skewY * (Math.PI/180.0)) * this; } ////// Translates this matrix /// /// The offset in the x dimension /// The offset in the y dimension public void Translate(double offsetX, double offsetY) { // // / a b 0 \ / 1 0 0 \ / a b 0 \ // | c d 0 | * | 0 1 0 | = | c d 0 | // \ e f 1 / \ x y 1 / \ e+x f+y 1 / // // (where e = _offsetX and f == _offsetY) // if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { // Values would be incorrect if matrix was created using default constructor. // or if SetIdentity was called on a matrix which had values. // SetMatrix(1, 0, 0, 1, offsetX, offsetY, MatrixTypes.TRANSFORM_IS_TRANSLATION); } else if (_type == MatrixTypes.TRANSFORM_IS_UNKNOWN) { _offsetX += offsetX; _offsetY += offsetY; } else { _offsetX += offsetX; _offsetY += offsetY; // If matrix wasn't unknown we added a translation _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } Debug_CheckType(); } ////// Prepends a translation to this matrix /// /// The offset in the x dimension /// The offset in the y dimension public void TranslatePrepend(double offsetX, double offsetY) { this = CreateTranslation(offsetX, offsetY) * this; } #endregion Set Methods #region Transformation Services ////// Transform - returns the result of transforming the point by this matrix /// ////// The transformed point /// /// The Point to transform public Point Transform(Point point) { Point newPoint = point; MultiplyPoint(ref newPoint._x, ref newPoint._y); return newPoint; } ////// Transform - Transforms each point in the array by this matrix /// /// The Point array to transform public void Transform(Point[] points) { if (points != null) { for (int i = 0; i < points.Length; i++) { MultiplyPoint(ref points[i]._x, ref points[i]._y); } } } ////// Transform - returns the result of transforming the Vector by this matrix. /// ////// The transformed vector /// /// The Vector to transform public Vector Transform(Vector vector) { Vector newVector = vector; MultiplyVector(ref newVector._x, ref newVector._y); return newVector; } ////// Transform - Transforms each Vector in the array by this matrix. /// /// The Vector array to transform public void Transform(Vector[] vectors) { if (vectors != null) { for (int i = 0; i < vectors.Length; i++) { MultiplyVector(ref vectors[i]._x, ref vectors[i]._y); } } } #endregion Transformation Services #region Inversion ////// The determinant of this matrix /// public double Determinant { get { switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: case MatrixTypes.TRANSFORM_IS_TRANSLATION: return 1.0; case MatrixTypes.TRANSFORM_IS_SCALING: case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: return(_m11 * _m22); default: return(_m11 * _m22) - (_m12 * _m21); } } } ////// HasInverse Property - returns true if this matrix is invertable, false otherwise. /// public bool HasInverse { get { return !DoubleUtil.IsZero(Determinant); } } ////// Replaces matrix with the inverse of the transformation. This will throw an InvalidOperationException /// if !HasInverse /// ////// This will throw an InvalidOperationException if the matrix is non-invertable /// public void Invert() { double determinant = Determinant; if (DoubleUtil.IsZero(determinant)) { throw new System.InvalidOperationException(SR.Get(SRID.Transform_NotInvertible)); } // Inversion does not change the type of a matrix. switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: break; case MatrixTypes.TRANSFORM_IS_SCALING: { _m11 = 1.0 / _m11; _m22 = 1.0 / _m22; } break; case MatrixTypes.TRANSFORM_IS_TRANSLATION: _offsetX = -_offsetX; _offsetY = -_offsetY; break; case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: { _m11 = 1.0 / _m11; _m22 = 1.0 / _m22; _offsetX = -_offsetX * _m11; _offsetY = -_offsetY * _m22; } break; default: { double invdet = 1.0/determinant; SetMatrix(_m22 * invdet, -_m12 * invdet, -_m21 * invdet, _m11 * invdet, (_m21 * _offsetY - _offsetX * _m22) * invdet, (_offsetX * _m12 - _m11 * _offsetY) * invdet, MatrixTypes.TRANSFORM_IS_UNKNOWN); } break; } } #endregion Inversion #region Public Properties ////// M11 /// public double M11 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 1.0; } else { return _m11; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(value, 0, 0, 1, 0, 0, MatrixTypes.TRANSFORM_IS_SCALING); } else { _m11 = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_SCALING; } } } } ////// M12 /// public double M12 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _m12; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, value, 0, 1, 0, 0, MatrixTypes.TRANSFORM_IS_UNKNOWN); } else { _m12 = value; _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; } } } ////// M22 /// public double M21 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _m21; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, value, 1, 0, 0, MatrixTypes.TRANSFORM_IS_UNKNOWN); } else { _m21 = value; _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; } } } ////// M22 /// public double M22 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 1.0; } else { return _m22; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, 0, value, 0, 0, MatrixTypes.TRANSFORM_IS_SCALING); } else { _m22 = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_SCALING; } } } } ////// OffsetX /// public double OffsetX { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _offsetX; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, 0, 1, value, 0, MatrixTypes.TRANSFORM_IS_TRANSLATION); } else { _offsetX = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } } } } ////// OffsetY /// public double OffsetY { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _offsetY; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, 0, 1, 0, value, MatrixTypes.TRANSFORM_IS_TRANSLATION); } else { _offsetY = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } } } } #endregion Public Properties #region Internal Methods ////// MultiplyVector /// internal void MultiplyVector(ref double x, ref double y) { switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: case MatrixTypes.TRANSFORM_IS_TRANSLATION: return; case MatrixTypes.TRANSFORM_IS_SCALING: case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: x *= _m11; y *= _m22; break; default: double xadd = y * _m21; double yadd = x * _m12; x *= _m11; x += xadd; y *= _m22; y += yadd; break; } } ////// MultiplyPoint /// internal void MultiplyPoint(ref double x, ref double y) { switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: return; case MatrixTypes.TRANSFORM_IS_TRANSLATION: x += _offsetX; y += _offsetY; return; case MatrixTypes.TRANSFORM_IS_SCALING: x *= _m11; y *= _m22; return; case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: x *= _m11; x += _offsetX; y *= _m22; y += _offsetY; break; default: double xadd = y * _m21 + _offsetX; double yadd = x * _m12 + _offsetY; x *= _m11; x += xadd; y *= _m22; y += yadd; break; } } ////// Creates a rotation transformation about the given point /// /// The angle to rotate specifed in radians internal static Matrix CreateRotationRadians(double angle) { return CreateRotationRadians(angle, /* centerX = */ 0, /* centerY = */ 0); } ////// Creates a rotation transformation about the given point /// /// The angle to rotate specifed in radians /// The centerX of rotation /// The centerY of rotation internal static Matrix CreateRotationRadians(double angle, double centerX, double centerY) { Matrix matrix = new Matrix(); double sin = Math.Sin(angle); double cos = Math.Cos(angle); double dx = (centerX * (1.0 - cos)) + (centerY * sin); double dy = (centerY * (1.0 - cos)) - (centerX * sin); matrix.SetMatrix( cos, sin, -sin, cos, dx, dy, MatrixTypes.TRANSFORM_IS_UNKNOWN); return matrix; } ////// Creates a scaling transform around the given point /// /// The scale factor in the x dimension /// The scale factor in the y dimension /// The centerX of scaling /// The centerY of scaling internal static Matrix CreateScaling(double scaleX, double scaleY, double centerX, double centerY) { Matrix matrix = new Matrix(); matrix.SetMatrix(scaleX, 0, 0, scaleY, centerX - scaleX*centerX, centerY - scaleY*centerY, MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION); return matrix; } ////// Creates a scaling transform around the origin /// /// The scale factor in the x dimension /// The scale factor in the y dimension internal static Matrix CreateScaling(double scaleX, double scaleY) { Matrix matrix = new Matrix(); matrix.SetMatrix(scaleX, 0, 0, scaleY, 0, 0, MatrixTypes.TRANSFORM_IS_SCALING); return matrix; } ////// Creates a skew transform /// /// The skew angle in the x dimension in degrees /// The skew angle in the y dimension in degrees internal static Matrix CreateSkewRadians(double skewX, double skewY) { Matrix matrix = new Matrix(); matrix.SetMatrix(1.0, Math.Tan(skewY), Math.Tan(skewX), 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_UNKNOWN); return matrix; } ////// Sets the transformation to the given translation specified by the offset vector. /// /// The offset in X /// The offset in Y internal static Matrix CreateTranslation(double offsetX, double offsetY) { Matrix matrix = new Matrix(); matrix.SetMatrix(1, 0, 0, 1, offsetX, offsetY, MatrixTypes.TRANSFORM_IS_TRANSLATION); return matrix; } #endregion Internal Methods #region Private Methods ////// Sets the transformation to the identity. /// private static Matrix CreateIdentity() { Matrix matrix = new Matrix(); matrix.SetMatrix(1, 0, 0, 1, 0, 0, MatrixTypes.TRANSFORM_IS_IDENTITY); return matrix; } ////// Sets the transform to /// / m11, m12, 0 \ /// | m21, m22, 0 | /// \ offsetX, offsetY, 1 / /// where offsetX, offsetY is the translation. /// private void SetMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY, MatrixTypes type) { this._m11 = m11; this._m12 = m12; this._m21 = m21; this._m22 = m22; this._offsetX = offsetX; this._offsetY = offsetY; this._type = type; } ////// Set the type of the matrix based on its current contents /// private void DeriveMatrixType() { _type = 0; // Now classify our matrix. if (!(_m21 == 0 && _m12 == 0)) { _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; return; } if (!(_m11 == 1 && _m22 == 1)) { _type = MatrixTypes.TRANSFORM_IS_SCALING; } if (!(_offsetX == 0 && _offsetY == 0)) { _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } if (0 == (_type & (MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING))) { // We have an identity matrix. _type = MatrixTypes.TRANSFORM_IS_IDENTITY; } return; } ////// Asserts that the matrix tag is one of the valid options and /// that coefficients are correct. /// [Conditional("DEBUG")] private void Debug_CheckType() { switch(_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: return; case MatrixTypes.TRANSFORM_IS_UNKNOWN: return; case MatrixTypes.TRANSFORM_IS_SCALING: Debug.Assert(_m21 == 0); Debug.Assert(_m12 == 0); Debug.Assert(_offsetX == 0); Debug.Assert(_offsetY == 0); return; case MatrixTypes.TRANSFORM_IS_TRANSLATION: Debug.Assert(_m21 == 0); Debug.Assert(_m12 == 0); Debug.Assert(_m11 == 1); Debug.Assert(_m22 == 1); return; case MatrixTypes.TRANSFORM_IS_SCALING|MatrixTypes.TRANSFORM_IS_TRANSLATION: Debug.Assert(_m21 == 0); Debug.Assert(_m12 == 0); return; default: Debug.Assert(false); return; } } #endregion Private Methods #region Private Properties and Fields ////// Efficient but conservative test for identity. Returns /// true if the the matrix is identity. If it returns false /// the matrix may still be identity. /// private bool IsDistinguishedIdentity { get { return _type == MatrixTypes.TRANSFORM_IS_IDENTITY; } } // The hash code for a matrix is the xor of its element's hashes. // Since the identity matrix has 2 1's and 4 0's its hash is 0. private const int c_identityHashCode = 0; #endregion Private Properties and Fields internal double _m11; internal double _m12; internal double _m21; internal double _m22; internal double _offsetX; internal double _offsetY; internal MatrixTypes _type; // This field is only used by unmanaged code which isn't detected by the compiler. #pragma warning disable 0414 // Matrix in blt'd to unmanaged code, so this is padding // to align structure. // // ToDo: [....], Validate that this blt will work on 64-bit // internal Int32 _padding; #pragma warning restore 0414 } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, 2001, 2002 // // File: Matrix.cs //----------------------------------------------------------------------------- using System; using System.Diagnostics; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using MS.Internal; using System.Text; using System.Collections; using System.Globalization; using System.Windows; using System.Windows.Media; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using SR=MS.Internal.WindowsBase.SR; using SRID=MS.Internal.WindowsBase.SRID; // IMPORTANT // // Rules for using matrix types. // // internal enum MatrixTypes // { // TRANSFORM_IS_IDENTITY = 0, // TRANSFORM_IS_TRANSLATION = 1, // TRANSFORM_IS_SCALING = 2, // TRANSFORM_IS_UNKNOWN = 4 // } // // 1. Matrix type must be one of 0, 1, 2, 4, or 3 (for scale and translation) // 2. Matrix types are true but not exact! (E.G. A scale or identity transform could be marked as unknown or scale+translate.) // 3. Therefore read-only operations can ignore the type with one exception // EXCEPTION: A matrix tagged identity might have any coefficients instead of 1,0,0,1,0,0 // This is the (now) classic no default constructor for structs issue // 4. Matrix._type must be maintained by mutation operations // 5. MS.Internal.MatrixUtil uses unsafe code to access the private members of Matrix including _type. // // In Jan 2005 the matrix types were changed from being EXACT (i.e. a // scale matrix is always tagged as a scale and not something more // general.) This resulted in about a 2% speed up in matrix // multiplication. // // The special cases for matrix multiplication speed up scale*scale // and translation*translation by 30% compared to a single "no-branch" // multiplication algorithm. Matrix multiplication of two unknown // matrices is slowed by 20% compared to the no-branch algorithm. // // windows/wcp/DevTest/Drts/MediaApi/MediaPerf.cs includes the // simple test of matrix multiplication speed used for these results. namespace System.Windows.Media { ////// Matrix /// public partial struct Matrix: IFormattable { // the transform is identity by default // Actually fill in the fields - some (internal) code uses the fields directly for perf. private static Matrix s_identity = CreateIdentity(); #region Constructor ////// Creates a matrix of the form /// / m11, m12, 0 \ /// | m21, m22, 0 | /// \ offsetX, offsetY, 1 / /// 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; _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; _padding = 0; // We will detect EXACT identity, scale, translation or // scale+translation and use special case algorithms. DeriveMatrixType(); } #endregion Constructor #region Identity ////// Identity /// public static Matrix Identity { get { return s_identity; } } ////// Sets the matrix to identity. /// public void SetIdentity() { _type = MatrixTypes.TRANSFORM_IS_IDENTITY; } ////// Tests whether or not a given transform is an identity transform /// public bool IsIdentity { get { return (_type == MatrixTypes.TRANSFORM_IS_IDENTITY || (_m11 == 1 && _m12 == 0 && _m21 == 0 && _m22 == 1 && _offsetX == 0 && _offsetY == 0)); } } #endregion Identity #region Operators ////// Multiplies two transformations. /// public static Matrix operator *(Matrix trans1, Matrix trans2) { MatrixUtil.MultiplyMatrix(ref trans1, ref trans2); trans1.Debug_CheckType(); return trans1; } ////// Multiply /// public static Matrix Multiply(Matrix trans1, Matrix trans2) { MatrixUtil.MultiplyMatrix(ref trans1, ref trans2); trans1.Debug_CheckType(); return trans1; } #endregion Operators #region Combine Methods ////// Append - "this" becomes this * matrix, the same as this *= matrix. /// /// The Matrix to append to this Matrix public void Append(Matrix matrix) { this *= matrix; } ////// Prepend - "this" becomes matrix * this, the same as this = matrix * this. /// /// The Matrix to prepend to this Matrix public void Prepend(Matrix matrix) { this = matrix * this; } ////// Rotates this matrix about the origin /// /// The angle to rotate specifed in degrees public void Rotate(double angle) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this *= CreateRotationRadians(angle * (Math.PI/180.0)); } ////// Prepends a rotation about the origin to "this" /// /// The angle to rotate specifed in degrees public void RotatePrepend(double angle) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this = CreateRotationRadians(angle * (Math.PI/180.0)) * this; } ////// Rotates this matrix about the given point /// /// The angle to rotate specifed in degrees /// The centerX of rotation /// The centerY of rotation public void RotateAt(double angle, double centerX, double centerY) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this *= CreateRotationRadians(angle * (Math.PI/180.0), centerX, centerY); } ////// Prepends a rotation about the given point to "this" /// /// The angle to rotate specifed in degrees /// The centerX of rotation /// The centerY of rotation public void RotateAtPrepend(double angle, double centerX, double centerY) { angle %= 360.0; // Doing the modulo before converting to radians reduces total error this = CreateRotationRadians(angle * (Math.PI/180.0), centerX, centerY) * this; } ////// Scales this matrix around the origin /// /// The scale factor in the x dimension /// The scale factor in the y dimension public void Scale(double scaleX, double scaleY) { this *= CreateScaling(scaleX, scaleY); } ////// Prepends a scale around the origin to "this" /// /// The scale factor in the x dimension /// The scale factor in the y dimension public void ScalePrepend(double scaleX, double scaleY) { this = CreateScaling(scaleX, scaleY) * this; } ////// Scales this matrix around the center provided /// /// The scale factor in the x dimension /// The scale factor in the y dimension /// The centerX about which to scale /// The centerY about which to scale public void ScaleAt(double scaleX, double scaleY, double centerX, double centerY) { this *= CreateScaling(scaleX, scaleY, centerX, centerY); } ////// Prepends a scale around the center provided to "this" /// /// The scale factor in the x dimension /// The scale factor in the y dimension /// The centerX about which to scale /// The centerY about which to scale public void ScaleAtPrepend(double scaleX, double scaleY, double centerX, double centerY) { this = CreateScaling(scaleX, scaleY, centerX, centerY) * this; } ////// Skews this matrix /// /// The skew angle in the x dimension in degrees /// The skew angle in the y dimension in degrees public void Skew(double skewX, double skewY) { skewX %= 360; skewY %= 360; this *= CreateSkewRadians(skewX * (Math.PI/180.0), skewY * (Math.PI/180.0)); } ////// Prepends a skew to this matrix /// /// The skew angle in the x dimension in degrees /// The skew angle in the y dimension in degrees public void SkewPrepend(double skewX, double skewY) { skewX %= 360; skewY %= 360; this = CreateSkewRadians(skewX * (Math.PI/180.0), skewY * (Math.PI/180.0)) * this; } ////// Translates this matrix /// /// The offset in the x dimension /// The offset in the y dimension public void Translate(double offsetX, double offsetY) { // // / a b 0 \ / 1 0 0 \ / a b 0 \ // | c d 0 | * | 0 1 0 | = | c d 0 | // \ e f 1 / \ x y 1 / \ e+x f+y 1 / // // (where e = _offsetX and f == _offsetY) // if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { // Values would be incorrect if matrix was created using default constructor. // or if SetIdentity was called on a matrix which had values. // SetMatrix(1, 0, 0, 1, offsetX, offsetY, MatrixTypes.TRANSFORM_IS_TRANSLATION); } else if (_type == MatrixTypes.TRANSFORM_IS_UNKNOWN) { _offsetX += offsetX; _offsetY += offsetY; } else { _offsetX += offsetX; _offsetY += offsetY; // If matrix wasn't unknown we added a translation _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } Debug_CheckType(); } ////// Prepends a translation to this matrix /// /// The offset in the x dimension /// The offset in the y dimension public void TranslatePrepend(double offsetX, double offsetY) { this = CreateTranslation(offsetX, offsetY) * this; } #endregion Set Methods #region Transformation Services ////// Transform - returns the result of transforming the point by this matrix /// ////// The transformed point /// /// The Point to transform public Point Transform(Point point) { Point newPoint = point; MultiplyPoint(ref newPoint._x, ref newPoint._y); return newPoint; } ////// Transform - Transforms each point in the array by this matrix /// /// The Point array to transform public void Transform(Point[] points) { if (points != null) { for (int i = 0; i < points.Length; i++) { MultiplyPoint(ref points[i]._x, ref points[i]._y); } } } ////// Transform - returns the result of transforming the Vector by this matrix. /// ////// The transformed vector /// /// The Vector to transform public Vector Transform(Vector vector) { Vector newVector = vector; MultiplyVector(ref newVector._x, ref newVector._y); return newVector; } ////// Transform - Transforms each Vector in the array by this matrix. /// /// The Vector array to transform public void Transform(Vector[] vectors) { if (vectors != null) { for (int i = 0; i < vectors.Length; i++) { MultiplyVector(ref vectors[i]._x, ref vectors[i]._y); } } } #endregion Transformation Services #region Inversion ////// The determinant of this matrix /// public double Determinant { get { switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: case MatrixTypes.TRANSFORM_IS_TRANSLATION: return 1.0; case MatrixTypes.TRANSFORM_IS_SCALING: case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: return(_m11 * _m22); default: return(_m11 * _m22) - (_m12 * _m21); } } } ////// HasInverse Property - returns true if this matrix is invertable, false otherwise. /// public bool HasInverse { get { return !DoubleUtil.IsZero(Determinant); } } ////// Replaces matrix with the inverse of the transformation. This will throw an InvalidOperationException /// if !HasInverse /// ////// This will throw an InvalidOperationException if the matrix is non-invertable /// public void Invert() { double determinant = Determinant; if (DoubleUtil.IsZero(determinant)) { throw new System.InvalidOperationException(SR.Get(SRID.Transform_NotInvertible)); } // Inversion does not change the type of a matrix. switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: break; case MatrixTypes.TRANSFORM_IS_SCALING: { _m11 = 1.0 / _m11; _m22 = 1.0 / _m22; } break; case MatrixTypes.TRANSFORM_IS_TRANSLATION: _offsetX = -_offsetX; _offsetY = -_offsetY; break; case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: { _m11 = 1.0 / _m11; _m22 = 1.0 / _m22; _offsetX = -_offsetX * _m11; _offsetY = -_offsetY * _m22; } break; default: { double invdet = 1.0/determinant; SetMatrix(_m22 * invdet, -_m12 * invdet, -_m21 * invdet, _m11 * invdet, (_m21 * _offsetY - _offsetX * _m22) * invdet, (_offsetX * _m12 - _m11 * _offsetY) * invdet, MatrixTypes.TRANSFORM_IS_UNKNOWN); } break; } } #endregion Inversion #region Public Properties ////// M11 /// public double M11 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 1.0; } else { return _m11; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(value, 0, 0, 1, 0, 0, MatrixTypes.TRANSFORM_IS_SCALING); } else { _m11 = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_SCALING; } } } } ////// M12 /// public double M12 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _m12; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, value, 0, 1, 0, 0, MatrixTypes.TRANSFORM_IS_UNKNOWN); } else { _m12 = value; _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; } } } ////// M22 /// public double M21 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _m21; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, value, 1, 0, 0, MatrixTypes.TRANSFORM_IS_UNKNOWN); } else { _m21 = value; _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; } } } ////// M22 /// public double M22 { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 1.0; } else { return _m22; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, 0, value, 0, 0, MatrixTypes.TRANSFORM_IS_SCALING); } else { _m22 = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_SCALING; } } } } ////// OffsetX /// public double OffsetX { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _offsetX; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, 0, 1, value, 0, MatrixTypes.TRANSFORM_IS_TRANSLATION); } else { _offsetX = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } } } } ////// OffsetY /// public double OffsetY { get { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { return 0; } else { return _offsetY; } } set { if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY) { SetMatrix(1, 0, 0, 1, 0, value, MatrixTypes.TRANSFORM_IS_TRANSLATION); } else { _offsetY = value; if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN) { _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } } } } #endregion Public Properties #region Internal Methods ////// MultiplyVector /// internal void MultiplyVector(ref double x, ref double y) { switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: case MatrixTypes.TRANSFORM_IS_TRANSLATION: return; case MatrixTypes.TRANSFORM_IS_SCALING: case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: x *= _m11; y *= _m22; break; default: double xadd = y * _m21; double yadd = x * _m12; x *= _m11; x += xadd; y *= _m22; y += yadd; break; } } ////// MultiplyPoint /// internal void MultiplyPoint(ref double x, ref double y) { switch (_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: return; case MatrixTypes.TRANSFORM_IS_TRANSLATION: x += _offsetX; y += _offsetY; return; case MatrixTypes.TRANSFORM_IS_SCALING: x *= _m11; y *= _m22; return; case MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION: x *= _m11; x += _offsetX; y *= _m22; y += _offsetY; break; default: double xadd = y * _m21 + _offsetX; double yadd = x * _m12 + _offsetY; x *= _m11; x += xadd; y *= _m22; y += yadd; break; } } ////// Creates a rotation transformation about the given point /// /// The angle to rotate specifed in radians internal static Matrix CreateRotationRadians(double angle) { return CreateRotationRadians(angle, /* centerX = */ 0, /* centerY = */ 0); } ////// Creates a rotation transformation about the given point /// /// The angle to rotate specifed in radians /// The centerX of rotation /// The centerY of rotation internal static Matrix CreateRotationRadians(double angle, double centerX, double centerY) { Matrix matrix = new Matrix(); double sin = Math.Sin(angle); double cos = Math.Cos(angle); double dx = (centerX * (1.0 - cos)) + (centerY * sin); double dy = (centerY * (1.0 - cos)) - (centerX * sin); matrix.SetMatrix( cos, sin, -sin, cos, dx, dy, MatrixTypes.TRANSFORM_IS_UNKNOWN); return matrix; } ////// Creates a scaling transform around the given point /// /// The scale factor in the x dimension /// The scale factor in the y dimension /// The centerX of scaling /// The centerY of scaling internal static Matrix CreateScaling(double scaleX, double scaleY, double centerX, double centerY) { Matrix matrix = new Matrix(); matrix.SetMatrix(scaleX, 0, 0, scaleY, centerX - scaleX*centerX, centerY - scaleY*centerY, MatrixTypes.TRANSFORM_IS_SCALING | MatrixTypes.TRANSFORM_IS_TRANSLATION); return matrix; } ////// Creates a scaling transform around the origin /// /// The scale factor in the x dimension /// The scale factor in the y dimension internal static Matrix CreateScaling(double scaleX, double scaleY) { Matrix matrix = new Matrix(); matrix.SetMatrix(scaleX, 0, 0, scaleY, 0, 0, MatrixTypes.TRANSFORM_IS_SCALING); return matrix; } ////// Creates a skew transform /// /// The skew angle in the x dimension in degrees /// The skew angle in the y dimension in degrees internal static Matrix CreateSkewRadians(double skewX, double skewY) { Matrix matrix = new Matrix(); matrix.SetMatrix(1.0, Math.Tan(skewY), Math.Tan(skewX), 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_UNKNOWN); return matrix; } ////// Sets the transformation to the given translation specified by the offset vector. /// /// The offset in X /// The offset in Y internal static Matrix CreateTranslation(double offsetX, double offsetY) { Matrix matrix = new Matrix(); matrix.SetMatrix(1, 0, 0, 1, offsetX, offsetY, MatrixTypes.TRANSFORM_IS_TRANSLATION); return matrix; } #endregion Internal Methods #region Private Methods ////// Sets the transformation to the identity. /// private static Matrix CreateIdentity() { Matrix matrix = new Matrix(); matrix.SetMatrix(1, 0, 0, 1, 0, 0, MatrixTypes.TRANSFORM_IS_IDENTITY); return matrix; } ////// Sets the transform to /// / m11, m12, 0 \ /// | m21, m22, 0 | /// \ offsetX, offsetY, 1 / /// where offsetX, offsetY is the translation. /// private void SetMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY, MatrixTypes type) { this._m11 = m11; this._m12 = m12; this._m21 = m21; this._m22 = m22; this._offsetX = offsetX; this._offsetY = offsetY; this._type = type; } ////// Set the type of the matrix based on its current contents /// private void DeriveMatrixType() { _type = 0; // Now classify our matrix. if (!(_m21 == 0 && _m12 == 0)) { _type = MatrixTypes.TRANSFORM_IS_UNKNOWN; return; } if (!(_m11 == 1 && _m22 == 1)) { _type = MatrixTypes.TRANSFORM_IS_SCALING; } if (!(_offsetX == 0 && _offsetY == 0)) { _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION; } if (0 == (_type & (MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING))) { // We have an identity matrix. _type = MatrixTypes.TRANSFORM_IS_IDENTITY; } return; } ////// Asserts that the matrix tag is one of the valid options and /// that coefficients are correct. /// [Conditional("DEBUG")] private void Debug_CheckType() { switch(_type) { case MatrixTypes.TRANSFORM_IS_IDENTITY: return; case MatrixTypes.TRANSFORM_IS_UNKNOWN: return; case MatrixTypes.TRANSFORM_IS_SCALING: Debug.Assert(_m21 == 0); Debug.Assert(_m12 == 0); Debug.Assert(_offsetX == 0); Debug.Assert(_offsetY == 0); return; case MatrixTypes.TRANSFORM_IS_TRANSLATION: Debug.Assert(_m21 == 0); Debug.Assert(_m12 == 0); Debug.Assert(_m11 == 1); Debug.Assert(_m22 == 1); return; case MatrixTypes.TRANSFORM_IS_SCALING|MatrixTypes.TRANSFORM_IS_TRANSLATION: Debug.Assert(_m21 == 0); Debug.Assert(_m12 == 0); return; default: Debug.Assert(false); return; } } #endregion Private Methods #region Private Properties and Fields ////// Efficient but conservative test for identity. Returns /// true if the the matrix is identity. If it returns false /// the matrix may still be identity. /// private bool IsDistinguishedIdentity { get { return _type == MatrixTypes.TRANSFORM_IS_IDENTITY; } } // The hash code for a matrix is the xor of its element's hashes. // Since the identity matrix has 2 1's and 4 0's its hash is 0. private const int c_identityHashCode = 0; #endregion Private Properties and Fields internal double _m11; internal double _m12; internal double _m21; internal double _m22; internal double _offsetX; internal double _offsetY; internal MatrixTypes _type; // This field is only used by unmanaged code which isn't detected by the compiler. #pragma warning disable 0414 // Matrix in blt'd to unmanaged code, so this is padding // to align structure. // // ToDo: [....], Validate that this blt will work on 64-bit // internal Int32 _padding; #pragma warning restore 0414 } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- StylusDevice.cs
- RootContext.cs
- ElementUtil.cs
- CodeLabeledStatement.cs
- AspNetRouteServiceHttpHandler.cs
- designeractionbehavior.cs
- X509IssuerSerialKeyIdentifierClause.cs
- PanelStyle.cs
- RootBrowserWindowProxy.cs
- BehaviorEditorPart.cs
- QilTypeChecker.cs
- ITextView.cs
- ReadContentAsBinaryHelper.cs
- ToolStripItemDesigner.cs
- RegionData.cs
- Connection.cs
- ComplexTypeEmitter.cs
- WinFormsUtils.cs
- MachinePropertyVariants.cs
- iisPickupDirectory.cs
- ContainerVisual.cs
- DataGridViewTextBoxEditingControl.cs
- ResolveResponseInfo.cs
- IssuanceTokenProviderState.cs
- ExpandCollapseProviderWrapper.cs
- CacheVirtualItemsEvent.cs
- DecimalFormatter.cs
- HttpListenerException.cs
- WebServiceErrorEvent.cs
- ValidationErrorCollection.cs
- EntitySetBaseCollection.cs
- XhtmlConformanceSection.cs
- CatalogPartChrome.cs
- BindingNavigator.cs
- KeyedHashAlgorithm.cs
- IDictionary.cs
- diagnosticsswitches.cs
- ServiceAuthorizationBehavior.cs
- IndexedString.cs
- ListChunk.cs
- RuntimeCompatibilityAttribute.cs
- webclient.cs
- CoreSwitches.cs
- Cell.cs
- WizardSideBarListControlItem.cs
- DockPatternIdentifiers.cs
- BaseCodeDomTreeGenerator.cs
- XmlSignificantWhitespace.cs
- CodeIdentifiers.cs
- LexicalChunk.cs
- RoutedEventConverter.cs
- MediaElementAutomationPeer.cs
- ReflectTypeDescriptionProvider.cs
- ErrorView.xaml.cs
- SocketElement.cs
- ZoomPercentageConverter.cs
- SemanticResultKey.cs
- ReachDocumentReferenceCollectionSerializerAsync.cs
- StreamReader.cs
- Highlights.cs
- ElementUtil.cs
- BaseCollection.cs
- VariableQuery.cs
- ImageKeyConverter.cs
- AnimationLayer.cs
- EventRoute.cs
- HttpRawResponse.cs
- RightsManagementManager.cs
- BitmapEffectGeneralTransform.cs
- DBBindings.cs
- PersonalizablePropertyEntry.cs
- EventLogLink.cs
- Stylesheet.cs
- Latin1Encoding.cs
- TargetConverter.cs
- BinaryCommonClasses.cs
- ApplicationTrust.cs
- VersionValidator.cs
- GridViewPageEventArgs.cs
- RenderData.cs
- OutputWindow.cs
- IntSecurity.cs
- RemotingConfigParser.cs
- DataGridViewUtilities.cs
- IntellisenseTextBox.designer.cs
- QuotedPrintableStream.cs
- LogReserveAndAppendState.cs
- OracleRowUpdatedEventArgs.cs
- SettingsSection.cs
- FactorySettingsElement.cs
- RequestStatusBarUpdateEventArgs.cs
- ImageSource.cs
- oledbconnectionstring.cs
- Literal.cs
- RuntimeConfig.cs
- OuterGlowBitmapEffect.cs
- HandledEventArgs.cs
- XmlSerializationReader.cs
- VScrollBar.cs
- RequestTimeoutManager.cs