Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Base / System / Windows / Media / Matrix.cs / 1 / 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=System.Windows.SR; using SRID=System.Windows.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: AdSmith, 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=System.Windows.SR; using SRID=System.Windows.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: AdSmith, 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
- SpeechSeg.cs
- ColorContextHelper.cs
- MarginsConverter.cs
- MonthCalendarDesigner.cs
- DATA_BLOB.cs
- SqlUserDefinedTypeAttribute.cs
- MsmqInputChannelBase.cs
- X509InitiatorCertificateClientElement.cs
- SerialReceived.cs
- XmlSerializerNamespaces.cs
- ServiceOperationParameter.cs
- InputProcessorProfiles.cs
- ArgumentException.cs
- Hyperlink.cs
- CompilationSection.cs
- LocationSectionRecord.cs
- ServiceHttpModule.cs
- IsolatedStorageSecurityState.cs
- SafeLibraryHandle.cs
- PersistChildrenAttribute.cs
- DataRecordInternal.cs
- WebPartDeleteVerb.cs
- ParallelTimeline.cs
- DesignerDataParameter.cs
- MissingFieldException.cs
- TypeUnloadedException.cs
- TableRow.cs
- IRCollection.cs
- BitmapEffectvisualstate.cs
- HttpProcessUtility.cs
- ConfigurationStrings.cs
- OleDbReferenceCollection.cs
- AutoResizedEvent.cs
- BitmapEffectState.cs
- ReachFixedPageSerializer.cs
- RelatedView.cs
- input.cs
- SQLByteStorage.cs
- WindowsListViewSubItem.cs
- DiscoveryClientChannelFactory.cs
- Soap.cs
- SecurityState.cs
- RemoteWebConfigurationHostStream.cs
- DbConnectionPoolIdentity.cs
- IndexerNameAttribute.cs
- QilInvokeLateBound.cs
- DataTableReader.cs
- Configuration.cs
- DBAsyncResult.cs
- CodeTypeReference.cs
- SocketConnection.cs
- OdbcParameterCollection.cs
- ComNativeDescriptor.cs
- XmlDesignerDataSourceView.cs
- Annotation.cs
- PageContent.cs
- ComplexBindingPropertiesAttribute.cs
- ProviderConnectionPoint.cs
- OptimalTextSource.cs
- ToolStripRenderer.cs
- AttributeTable.cs
- XslException.cs
- ServiceNameElementCollection.cs
- StrongNameMembershipCondition.cs
- HttpUnhandledOperationInvoker.cs
- IImplicitResourceProvider.cs
- DetailsViewInsertEventArgs.cs
- VScrollProperties.cs
- _CommandStream.cs
- KnownBoxes.cs
- PortCache.cs
- LoginName.cs
- DataGridViewRowPostPaintEventArgs.cs
- wgx_render.cs
- WindowsToolbarItemAsMenuItem.cs
- ConnectionModeReader.cs
- IPGlobalProperties.cs
- DurableOperationContext.cs
- StrokeDescriptor.cs
- SmiSettersStream.cs
- BindableTemplateBuilder.cs
- HttpApplication.cs
- DataGridToolTip.cs
- GetPageCompletedEventArgs.cs
- DbMetaDataColumnNames.cs
- JsonEnumDataContract.cs
- NativeRecognizer.cs
- Object.cs
- httpapplicationstate.cs
- login.cs
- ResourceCategoryAttribute.cs
- SimpleHandlerFactory.cs
- SafeMarshalContext.cs
- RouteValueDictionary.cs
- TreeNodeStyleCollection.cs
- wmiprovider.cs
- BrowserCapabilitiesCodeGenerator.cs
- NumberSubstitution.cs
- OdbcHandle.cs
- AsyncMethodInvoker.cs