ASCU_ALL/PrintPDF/PdfSharp/Drawing/XMatrix.cs
2021-05-25 17:00:45 +05:00

1558 lines
54 KiB
C#

#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Stefan Lange
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// 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.
#endregion
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
#if GDI
using System.Drawing;
using System.Drawing.Drawing2D;
#endif
#if WPF
using System.Windows;
using System.Windows.Media;
#endif
#if !EDF_CORE
using PdfSharp.Internal;
#else
using PdfSharp.Internal;
#endif
// ReSharper disable RedundantNameQualifier
#if !EDF_CORE
namespace PdfSharp.Drawing
#else
namespace Edf.Drawing
#endif
{
/// <summary>
/// Represents a 3-by-3 matrix that represents an affine 2D transformation.
/// </summary>
[DebuggerDisplay("{DebuggerDisplay}")]
[Serializable, StructLayout(LayoutKind.Sequential)] //, TypeConverter(typeof(MatrixConverter)), ValueSerializer(typeof(MatrixValueSerializer))]
public struct XMatrix : IFormattable
{
[Flags]
internal enum XMatrixTypes
{
Identity = 0,
Translation = 1,
Scaling = 2,
Unknown = 4
}
/// <summary>
/// Initializes a new instance of the XMatrix struct.
/// </summary>
public XMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY)
{
_m11 = m11;
_m12 = m12;
_m21 = m21;
_m22 = m22;
_offsetX = offsetX;
_offsetY = offsetY;
_type = XMatrixTypes.Unknown;
//_padding = 0;
DeriveMatrixType();
}
/// <summary>
/// Gets the identity matrix.
/// </summary>
public static XMatrix Identity
{
get { return s_identity; }
}
/// <summary>
/// Sets this matrix into an identity matrix.
/// </summary>
public void SetIdentity()
{
_type = XMatrixTypes.Identity;
}
/// <summary>
/// Gets a value indicating whether this matrix instance is the identity matrix.
/// </summary>
public bool IsIdentity
{
get
{
// ReSharper disable CompareOfFloatsByEqualityOperator
if (_type == XMatrixTypes.Identity)
return true;
if (_m11 == 1.0 && _m12 == 0 && _m21 == 0 && _m22 == 1.0 && _offsetX == 0 && _offsetY == 0)
{
_type = XMatrixTypes.Identity;
return true;
}
return false;
// ReSharper restore CompareOfFloatsByEqualityOperator
}
}
///// <summary>
///// Gets an array of double values that represents the elements of this matrix.
///// </summary>
//[Obsolete("Use GetElements().")]
//public double[] Elements
//{
// get { return GetElements(); }
//}
/// <summary>
/// Gets an array of double values that represents the elements of this matrix.
/// </summary>
public double[] GetElements()
{
if (_type == XMatrixTypes.Identity)
return new double[] { 1, 0, 0, 1, 0, 0 };
return new double[] { _m11, _m12, _m21, _m22, _offsetX, _offsetY };
}
/// <summary>
/// Multiplies two matrices.
/// </summary>
public static XMatrix operator *(XMatrix trans1, XMatrix trans2)
{
MatrixHelper.MultiplyMatrix(ref trans1, ref trans2);
return trans1;
}
/// <summary>
/// Multiplies two matrices.
/// </summary>
public static XMatrix Multiply(XMatrix trans1, XMatrix trans2)
{
MatrixHelper.MultiplyMatrix(ref trans1, ref trans2);
return trans1;
}
/// <summary>
/// Appends the specified matrix to this matrix.
/// </summary>
public void Append(XMatrix matrix)
{
this *= matrix;
}
/// <summary>
/// Prepends the specified matrix to this matrix.
/// </summary>
public void Prepend(XMatrix matrix)
{
this = matrix * this;
}
/// <summary>
/// Appends the specified matrix to this matrix.
/// </summary>
[Obsolete("Use Append.")]
public void Multiply(XMatrix matrix)
{
Append(matrix);
}
/// <summary>
/// Prepends the specified matrix to this matrix.
/// </summary>
[Obsolete("Use Prepend.")]
public void MultiplyPrepend(XMatrix matrix)
{
Prepend(matrix);
}
/// <summary>
/// Multiplies this matrix with the specified matrix.
/// </summary>
public void Multiply(XMatrix matrix, XMatrixOrder order)
{
if (_type == XMatrixTypes.Identity)
this = CreateIdentity();
// Must use properties, the fields can be invalid if the matrix is identity matrix.
double t11 = M11;
double t12 = M12;
double t21 = M21;
double t22 = M22;
double tdx = OffsetX;
double tdy = OffsetY;
if (order == XMatrixOrder.Append)
{
_m11 = t11 * matrix.M11 + t12 * matrix.M21;
_m12 = t11 * matrix.M12 + t12 * matrix.M22;
_m21 = t21 * matrix.M11 + t22 * matrix.M21;
_m22 = t21 * matrix.M12 + t22 * matrix.M22;
_offsetX = tdx * matrix.M11 + tdy * matrix.M21 + matrix.OffsetX;
_offsetY = tdx * matrix.M12 + tdy * matrix.M22 + matrix.OffsetY;
}
else
{
_m11 = t11 * matrix.M11 + t21 * matrix.M12;
_m12 = t12 * matrix.M11 + t22 * matrix.M12;
_m21 = t11 * matrix.M21 + t21 * matrix.M22;
_m22 = t12 * matrix.M21 + t22 * matrix.M22;
_offsetX = t11 * matrix.OffsetX + t21 * matrix.OffsetY + tdx;
_offsetY = t12 * matrix.OffsetX + t22 * matrix.OffsetY + tdy;
}
DeriveMatrixType();
}
/// <summary>
/// Appends a translation of the specified offsets to this matrix.
/// </summary>
[Obsolete("Use TranslateAppend or TranslatePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void Translate(double offsetX, double offsetY)
{
throw new InvalidOperationException("Temporarily out of order.");
//if (_type == XMatrixTypes.Identity)
//{
// SetMatrix(1.0, 0, 0, 1.0, offsetX, offsetY, XMatrixTypes.Translation);
//}
//else if (_type == XMatrixTypes.Unknown)
//{
// _offsetX += offsetX;
// _offsetY += offsetY;
//}
//else
//{
// _offsetX += offsetX;
// _offsetY += offsetY;
// _type |= XMatrixTypes.Translation;
//}
}
/// <summary>
/// Appends a translation of the specified offsets to this matrix.
/// </summary>
public void TranslateAppend(double offsetX, double offsetY) // TODO: will become default
{
if (_type == XMatrixTypes.Identity)
{
SetMatrix(1, 0, 0, 1, offsetX, offsetY, XMatrixTypes.Translation);
}
else if (_type == XMatrixTypes.Unknown)
{
_offsetX += offsetX;
_offsetY += offsetY;
}
else
{
_offsetX += offsetX;
_offsetY += offsetY;
_type |= XMatrixTypes.Translation;
}
}
/// <summary>
/// Prepends a translation of the specified offsets to this matrix.
/// </summary>
public void TranslatePrepend(double offsetX, double offsetY)
{
this = CreateTranslation(offsetX, offsetY) * this;
}
/// <summary>
/// Translates the matrix with the specified offsets.
/// </summary>
public void Translate(double offsetX, double offsetY, XMatrixOrder order)
{
if (_type == XMatrixTypes.Identity)
this = CreateIdentity();
if (order == XMatrixOrder.Append)
{
_offsetX += offsetX;
_offsetY += offsetY;
}
else
{
_offsetX += offsetX * _m11 + offsetY * _m21;
_offsetY += offsetX * _m12 + offsetY * _m22;
}
DeriveMatrixType();
}
/// <summary>
/// Appends the specified scale vector to this matrix.
/// </summary>
[Obsolete("Use ScaleAppend or ScalePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void Scale(double scaleX, double scaleY)
{
this = CreateScaling(scaleX, scaleY) * this;
}
/// <summary>
/// Appends the specified scale vector to this matrix.
/// </summary>
public void ScaleAppend(double scaleX, double scaleY) // TODO: will become default
{
this *= CreateScaling(scaleX, scaleY);
}
/// <summary>
/// Prepends the specified scale vector to this matrix.
/// </summary>
public void ScalePrepend(double scaleX, double scaleY)
{
this = CreateScaling(scaleX, scaleY) * this;
}
/// <summary>
/// Scales the matrix with the specified scalars.
/// </summary>
public void Scale(double scaleX, double scaleY, XMatrixOrder order)
{
if (_type == XMatrixTypes.Identity)
this = CreateIdentity();
if (order == XMatrixOrder.Append)
{
_m11 *= scaleX;
_m12 *= scaleY;
_m21 *= scaleX;
_m22 *= scaleY;
_offsetX *= scaleX;
_offsetY *= scaleY;
}
else
{
_m11 *= scaleX;
_m12 *= scaleX;
_m21 *= scaleY;
_m22 *= scaleY;
}
DeriveMatrixType();
}
/// <summary>
/// Scales the matrix with the specified scalar.
/// </summary>
[Obsolete("Use ScaleAppend or ScalePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
// ReSharper disable InconsistentNaming
public void Scale(double scaleXY)
// ReSharper restore InconsistentNaming
{
throw new InvalidOperationException("Temporarily out of order.");
//Scale(scaleXY, scaleXY, XMatrixOrder.Prepend);
}
/// <summary>
/// Appends the specified scale vector to this matrix.
/// </summary>
// ReSharper disable InconsistentNaming
public void ScaleAppend(double scaleXY)
// ReSharper restore InconsistentNaming
{
Scale(scaleXY, scaleXY, XMatrixOrder.Append);
}
/// <summary>
/// Prepends the specified scale vector to this matrix.
/// </summary>
// ReSharper disable InconsistentNaming
public void ScalePrepend(double scaleXY)
// ReSharper restore InconsistentNaming
{
Scale(scaleXY, scaleXY, XMatrixOrder.Prepend);
}
/// <summary>
/// Scales the matrix with the specified scalar.
/// </summary>
// ReSharper disable InconsistentNaming
public void Scale(double scaleXY, XMatrixOrder order)
// ReSharper restore InconsistentNaming
{
Scale(scaleXY, scaleXY, order);
}
/// <summary>
/// Function is obsolete.
/// </summary>
[Obsolete("Use ScaleAtAppend or ScaleAtPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void ScaleAt(double scaleX, double scaleY, double centerX, double centerY)
{
throw new InvalidOperationException("Temporarily out of order.");
//this *= CreateScaling(scaleX, scaleY, centerX, centerY);
}
/// <summary>
/// Apppends the specified scale about the specified point of this matrix.
/// </summary>
public void ScaleAtAppend(double scaleX, double scaleY, double centerX, double centerY) // TODO: will become default
{
this *= CreateScaling(scaleX, scaleY, centerX, centerY);
}
/// <summary>
/// Prepends the specified scale about the specified point of this matrix.
/// </summary>
public void ScaleAtPrepend(double scaleX, double scaleY, double centerX, double centerY)
{
this = CreateScaling(scaleX, scaleY, centerX, centerY) * this;
}
/// <summary>
/// Function is obsolete.
/// </summary>
[Obsolete("Use RotateAppend or RotatePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void Rotate(double angle)
{
throw new InvalidOperationException("Temporarily out of order.");
//angle = angle % 360.0;
//this *= CreateRotationRadians(angle * Const.Deg2Rad);
}
/// <summary>
/// Appends a rotation of the specified angle to this matrix.
/// </summary>
public void RotateAppend(double angle) // TODO: will become default Rotate
{
angle = angle % 360.0;
this *= CreateRotationRadians(angle * Const.Deg2Rad);
}
/// <summary>
/// Prepends a rotation of the specified angle to this matrix.
/// </summary>
public void RotatePrepend(double angle)
{
angle = angle % 360.0;
this = CreateRotationRadians(angle * Const.Deg2Rad) * this;
}
/// <summary>
/// Rotates the matrix with the specified angle.
/// </summary>
public void Rotate(double angle, XMatrixOrder order)
{
if (_type == XMatrixTypes.Identity)
this = CreateIdentity();
angle = angle * Const.Deg2Rad;
double cos = Math.Cos(angle);
double sin = Math.Sin(angle);
if (order == XMatrixOrder.Append)
{
double t11 = _m11;
double t12 = _m12;
double t21 = _m21;
double t22 = _m22;
double tdx = _offsetX;
double tdy = _offsetY;
_m11 = t11 * cos - t12 * sin;
_m12 = t11 * sin + t12 * cos;
_m21 = t21 * cos - t22 * sin;
_m22 = t21 * sin + t22 * cos;
_offsetX = tdx * cos - tdy * sin;
_offsetY = tdx * sin + tdy * cos;
}
else
{
double t11 = _m11;
double t12 = _m12;
double t21 = _m21;
double t22 = _m22;
_m11 = t11 * cos + t21 * sin;
_m12 = t12 * cos + t22 * sin;
_m21 = -t11 * sin + t21 * cos;
_m22 = -t12 * sin + t22 * cos;
}
DeriveMatrixType();
}
/// <summary>
/// Function is obsolete.
/// </summary>
[Obsolete("Use RotateAtAppend or RotateAtPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void RotateAt(double angle, double centerX, double centerY)
{
throw new InvalidOperationException("Temporarily out of order.");
//angle = angle % 360.0;
//this *= CreateRotationRadians(angle * Const.Deg2Rad, centerX, centerY);
}
/// <summary>
/// Appends a rotation of the specified angle at the specified point to this matrix.
/// </summary>
public void RotateAtAppend(double angle, double centerX, double centerY) // TODO: will become default
{
angle = angle % 360.0;
this *= CreateRotationRadians(angle * Const.Deg2Rad, centerX, centerY);
}
/// <summary>
/// Prepends a rotation of the specified angle at the specified point to this matrix.
/// </summary>
public void RotateAtPrepend(double angle, double centerX, double centerY)
{
angle = angle % 360.0;
this = CreateRotationRadians(angle * Const.Deg2Rad, centerX, centerY) * this;
}
/// <summary>
/// Rotates the matrix with the specified angle at the specified point.
/// </summary>
[Obsolete("Use RotateAtAppend or RotateAtPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void RotateAt(double angle, XPoint point)
{
throw new InvalidOperationException("Temporarily out of order.");
//RotateAt(angle, point, XMatrixOrder.Prepend);
}
/// <summary>
/// Appends a rotation of the specified angle at the specified point to this matrix.
/// </summary>
public void RotateAtAppend(double angle, XPoint point)
{
RotateAt(angle, point, XMatrixOrder.Append);
}
/// <summary>
/// Prepends a rotation of the specified angle at the specified point to this matrix.
/// </summary>
public void RotateAtPrepend(double angle, XPoint point)
{
RotateAt(angle, point, XMatrixOrder.Prepend);
}
/// <summary>
/// Rotates the matrix with the specified angle at the specified point.
/// </summary>
public void RotateAt(double angle, XPoint point, XMatrixOrder order)
{
if (order == XMatrixOrder.Append)
{
angle = angle % 360.0;
this *= CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y);
//Translate(point.X, point.Y, order);
//Rotate(angle, order);
//Translate(-point.X, -point.Y, order);
}
else
{
angle = angle % 360.0;
this = CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y) * this;
}
DeriveMatrixType();
}
/// <summary>
/// Function is obsolete.
/// </summary>
[Obsolete("Use ShearAppend or ShearPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void Shear(double shearX, double shearY)
{
throw new InvalidOperationException("Temporarily out of order.");
//Shear(shearX, shearY, XMatrixOrder.Prepend);
}
/// <summary>
/// Appends a skew of the specified degrees in the x and y dimensions to this matrix.
/// </summary>
public void ShearAppend(double shearX, double shearY) // TODO: will become default
{
Shear(shearX, shearY, XMatrixOrder.Append);
}
/// <summary>
/// Prepends a skew of the specified degrees in the x and y dimensions to this matrix.
/// </summary>
public void ShearPrepend(double shearX, double shearY)
{
Shear(shearX, shearY, XMatrixOrder.Prepend);
}
/// <summary>
/// Shears the matrix with the specified scalars.
/// </summary>
public void Shear(double shearX, double shearY, XMatrixOrder order)
{
if (_type == XMatrixTypes.Identity)
this = CreateIdentity();
double t11 = _m11;
double t12 = _m12;
double t21 = _m21;
double t22 = _m22;
double tdx = _offsetX;
double tdy = _offsetY;
if (order == XMatrixOrder.Append)
{
_m11 += shearX * t12;
_m12 += shearY * t11;
_m21 += shearX * t22;
_m22 += shearY * t21;
_offsetX += shearX * tdy;
_offsetY += shearY * tdx;
}
else
{
_m11 += shearY * t21;
_m12 += shearY * t22;
_m21 += shearX * t11;
_m22 += shearX * t12;
}
DeriveMatrixType();
}
/// <summary>
/// Function is obsolete.
/// </summary>
[Obsolete("Use SkewAppend or SkewPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)]
public void Skew(double skewX, double skewY)
{
throw new InvalidOperationException("Temporarily out of order.");
//skewX = skewX % 360.0;
//skewY = skewY % 360.0;
//this *= CreateSkewRadians(skewX * Const.Deg2Rad, skewY * Const.Deg2Rad);
}
/// <summary>
/// Appends a skew of the specified degrees in the x and y dimensions to this matrix.
/// </summary>
public void SkewAppend(double skewX, double skewY)
{
skewX = skewX % 360.0;
skewY = skewY % 360.0;
this *= CreateSkewRadians(skewX * Const.Deg2Rad, skewY * Const.Deg2Rad);
}
/// <summary>
/// Prepends a skew of the specified degrees in the x and y dimensions to this matrix.
/// </summary>
public void SkewPrepend(double skewX, double skewY)
{
skewX = skewX % 360.0;
skewY = skewY % 360.0;
this = CreateSkewRadians(skewX * Const.Deg2Rad, skewY * Const.Deg2Rad) * this;
}
/// <summary>
/// Transforms the specified point by this matrix and returns the result.
/// </summary>
public XPoint Transform(XPoint point)
{
double x = point.X;
double y = point.Y;
MultiplyPoint(ref x, ref y);
return new XPoint(x, y);
}
/// <summary>
/// Transforms the specified points by this matrix.
/// </summary>
public void Transform(XPoint[] points)
{
if (points != null)
{
int count = points.Length;
for (int idx = 0; idx < count; idx++)
{
double x = points[idx].X;
double y = points[idx].Y;
MultiplyPoint(ref x, ref y);
points[idx].X = x;
points[idx].Y = y;
}
}
}
/// <summary>
/// Multiplies all points of the specified array with the this matrix.
/// </summary>
public void TransformPoints(XPoint[] points)
{
if (points == null)
throw new ArgumentNullException("points");
if (IsIdentity)
return;
int count = points.Length;
for (int idx = 0; idx < count; idx++)
{
double x = points[idx].X;
double y = points[idx].Y;
points[idx].X = x * _m11 + y * _m21 + _offsetX;
points[idx].Y = x * _m12 + y * _m22 + _offsetY;
}
}
#if GDI
/// <summary>
/// Multiplies all points of the specified array with the this matrix.
/// </summary>
public void TransformPoints(System.Drawing.Point[] points)
{
if (points == null)
throw new ArgumentNullException("points");
if (IsIdentity)
return;
int count = points.Length;
for (int idx = 0; idx < count; idx++)
{
double x = points[idx].X;
double y = points[idx].Y;
points[idx].X = (int)(x * _m11 + y * _m21 + _offsetX);
points[idx].Y = (int)(x * _m12 + y * _m22 + _offsetY);
}
}
#endif
#if WPF
/// <summary>
/// Transforms an array of points.
/// </summary>
public void TransformPoints(System.Windows.Point[] points)
{
if (points == null)
throw new ArgumentNullException("points");
if (IsIdentity)
return;
int count = points.Length;
for (int idx = 0; idx < count; idx++)
{
double x = points[idx].X;
double y = points[idx].Y;
points[idx].X = (int)(x * _m11 + y * _m21 + _offsetX);
points[idx].Y = (int)(x * _m12 + y * _m22 + _offsetY);
}
}
#endif
/// <summary>
/// Transforms the specified vector by this Matrix and returns the result.
/// </summary>
public XVector Transform(XVector vector)
{
double x = vector.X;
double y = vector.Y;
MultiplyVector(ref x, ref y);
return new XVector(x, y);
}
/// <summary>
/// Transforms the specified vectors by this matrix.
/// </summary>
public void Transform(XVector[] vectors)
{
if (vectors != null)
{
int count = vectors.Length;
for (int idx = 0; idx < count; idx++)
{
double x = vectors[idx].X;
double y = vectors[idx].Y;
MultiplyVector(ref x, ref y);
vectors[idx].X = x;
vectors[idx].Y = y;
}
}
}
#if GDI
/// <summary>
/// Multiplies all vectors of the specified array with the this matrix. The translation elements
/// of this matrix (third row) are ignored.
/// </summary>
public void TransformVectors(PointF[] points)
{
if (points == null)
throw new ArgumentNullException("points");
if (IsIdentity)
return;
int count = points.Length;
for (int idx = 0; idx < count; idx++)
{
double x = points[idx].X;
double y = points[idx].Y;
points[idx].X = (float)(x * _m11 + y * _m21 + _offsetX);
points[idx].Y = (float)(x * _m12 + y * _m22 + _offsetY);
}
}
#endif
/// <summary>
/// Gets the determinant of this matrix.
/// </summary>
public double Determinant
{
get
{
switch (_type)
{
case XMatrixTypes.Identity:
case XMatrixTypes.Translation:
return 1.0;
case XMatrixTypes.Scaling:
case XMatrixTypes.Scaling | XMatrixTypes.Translation:
return _m11 * _m22;
}
return (_m11 * _m22) - (_m12 * _m21);
}
}
/// <summary>
/// Gets a value that indicates whether this matrix is invertible.
/// </summary>
public bool HasInverse
{
get { return !DoubleUtil.IsZero(Determinant); }
}
/// <summary>
/// Inverts the matrix.
/// </summary>
public void Invert()
{
double determinant = Determinant;
if (DoubleUtil.IsZero(determinant))
throw new InvalidOperationException("NotInvertible"); //SR.Get(SRID.Transform_NotInvertible, new object[0]));
switch (_type)
{
case XMatrixTypes.Identity:
break;
case XMatrixTypes.Translation:
_offsetX = -_offsetX;
_offsetY = -_offsetY;
return;
case XMatrixTypes.Scaling:
_m11 = 1.0 / _m11;
_m22 = 1.0 / _m22;
return;
case XMatrixTypes.Scaling | XMatrixTypes.Translation:
_m11 = 1.0 / _m11;
_m22 = 1.0 / _m22;
_offsetX = -_offsetX * _m11;
_offsetY = -_offsetY * _m22;
return;
default:
{
double detInvers = 1.0 / determinant;
SetMatrix(_m22 * detInvers, -_m12 * detInvers, -_m21 * detInvers, _m11 * detInvers, (_m21 * _offsetY - _offsetX * _m22) * detInvers, (_offsetX * _m12 - _m11 * _offsetY) * detInvers, XMatrixTypes.Unknown);
break;
}
}
}
/// <summary>
/// Gets or sets the value of the first row and first column of this matrix.
/// </summary>
public double M11
{
get
{
if (_type == XMatrixTypes.Identity)
return 1.0;
return _m11;
}
set
{
if (_type == XMatrixTypes.Identity)
SetMatrix(value, 0, 0, 1, 0, 0, XMatrixTypes.Scaling);
else
{
_m11 = value;
if (_type != XMatrixTypes.Unknown)
_type |= XMatrixTypes.Scaling;
}
}
}
/// <summary>
/// Gets or sets the value of the first row and second column of this matrix.
/// </summary>
public double M12
{
get
{
if (_type == XMatrixTypes.Identity)
return 0;
return _m12;
}
set
{
if (_type == XMatrixTypes.Identity)
SetMatrix(1, value, 0, 1, 0, 0, XMatrixTypes.Unknown);
else
{
_m12 = value;
_type = XMatrixTypes.Unknown;
}
}
}
/// <summary>
/// Gets or sets the value of the second row and first column of this matrix.
/// </summary>
public double M21
{
get
{
if (_type == XMatrixTypes.Identity)
return 0;
return _m21;
}
set
{
if (_type == XMatrixTypes.Identity)
SetMatrix(1, 0, value, 1, 0, 0, XMatrixTypes.Unknown);
else
{
_m21 = value;
_type = XMatrixTypes.Unknown;
}
}
}
/// <summary>
/// Gets or sets the value of the second row and second column of this matrix.
/// </summary>
public double M22
{
get
{
if (_type == XMatrixTypes.Identity)
return 1.0;
return _m22;
}
set
{
if (_type == XMatrixTypes.Identity)
SetMatrix(1, 0, 0, value, 0, 0, XMatrixTypes.Scaling);
else
{
_m22 = value;
if (_type != XMatrixTypes.Unknown)
_type |= XMatrixTypes.Scaling;
}
}
}
/// <summary>
/// Gets or sets the value of the third row and first column of this matrix.
/// </summary>
public double OffsetX
{
get
{
if (_type == XMatrixTypes.Identity)
return 0;
return _offsetX;
}
set
{
if (_type == XMatrixTypes.Identity)
SetMatrix(1, 0, 0, 1, value, 0, XMatrixTypes.Translation);
else
{
_offsetX = value;
if (_type != XMatrixTypes.Unknown)
_type |= XMatrixTypes.Translation;
}
}
}
/// <summary>
/// Gets or sets the value of the third row and second column of this matrix.
/// </summary>
public double OffsetY
{
get
{
if (_type == XMatrixTypes.Identity)
return 0;
return _offsetY;
}
set
{
if (_type == XMatrixTypes.Identity)
SetMatrix(1, 0, 0, 1, 0, value, XMatrixTypes.Translation);
else
{
_offsetY = value;
if (_type != XMatrixTypes.Unknown)
_type |= XMatrixTypes.Translation;
}
}
}
#if GDI
//#if UseGdiObjects
/// <summary>
/// Converts this matrix to a System.Drawing.Drawing2D.Matrix object.
/// </summary>
public System.Drawing.Drawing2D.Matrix ToGdiMatrix()
{
if (IsIdentity)
return new System.Drawing.Drawing2D.Matrix();
return new System.Drawing.Drawing2D.Matrix((float)_m11, (float)_m12, (float)_m21, (float)_m22,
(float)_offsetX, (float)_offsetY);
}
//#endif
#endif
#if WPF
/// Converts this matrix to a System.Windows.Media.Matrix object.
/// <summary>
/// </summary>
public System.Windows.Media.Matrix ToWpfMatrix()
{
return (System.Windows.Media.Matrix)this;
//return new System.Windows.Media.Matrix(_m11, _m12, _m21, _m22, _offsetX, _offsetY);
}
#endif
#if GDI
/// <summary>
/// Explicitly converts a XMatrix to a Matrix.
/// </summary>
public static explicit operator System.Drawing.Drawing2D.Matrix(XMatrix matrix)
{
if (matrix.IsIdentity)
return new System.Drawing.Drawing2D.Matrix();
return new System.Drawing.Drawing2D.Matrix(
(float)matrix._m11, (float)matrix._m12,
(float)matrix._m21, (float)matrix._m22,
(float)matrix._offsetX, (float)matrix._offsetY);
}
#endif
#if WPF
/// <summary>
/// Explicitly converts an XMatrix to a Matrix.
/// </summary>
public static explicit operator System.Windows.Media.Matrix(XMatrix matrix)
{
if (matrix.IsIdentity)
return new System.Windows.Media.Matrix();
return new System.Windows.Media.Matrix(
matrix._m11, matrix._m12,
matrix._m21, matrix._m22,
matrix._offsetX, matrix._offsetY);
}
#endif
#if GDI
/// <summary>
/// Implicitly converts a Matrix to an XMatrix.
/// </summary>
public static implicit operator XMatrix(System.Drawing.Drawing2D.Matrix matrix)
{
float[] elements = matrix.Elements;
return new XMatrix(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]);
}
#endif
#if WPF
/// <summary>
/// Implicitly converts a Matrix to an XMatrix.
/// </summary>
public static implicit operator XMatrix(System.Windows.Media.Matrix matrix)
{
return new XMatrix(matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.OffsetX, matrix.OffsetY);
}
#endif
/// <summary>
/// Determines whether the two matrices are equal.
/// </summary>
public static bool operator ==(XMatrix matrix1, XMatrix matrix2)
{
// ReSharper disable CompareOfFloatsByEqualityOperator
if (matrix1.IsDistinguishedIdentity || matrix2.IsDistinguishedIdentity)
return (matrix1.IsIdentity == matrix2.IsIdentity);
return matrix1.M11 == matrix2.M11 && matrix1.M12 == matrix2.M12 && matrix1.M21 == matrix2.M21 && matrix1.M22 == matrix2.M22 &&
matrix1.OffsetX == matrix2.OffsetX && matrix1.OffsetY == matrix2.OffsetY;
// ReSharper restore CompareOfFloatsByEqualityOperator
}
/// <summary>
/// Determines whether the two matrices are not equal.
/// </summary>
public static bool operator !=(XMatrix matrix1, XMatrix matrix2)
{
return !(matrix1 == matrix2);
}
/// <summary>
/// Determines whether the two matrices are equal.
/// </summary>
public static bool Equals(XMatrix matrix1, XMatrix matrix2)
{
if (matrix1.IsDistinguishedIdentity || matrix2.IsDistinguishedIdentity)
return matrix1.IsIdentity == matrix2.IsIdentity;
return matrix1.M11.Equals(matrix2.M11) && matrix1.M12.Equals(matrix2.M12) &&
matrix1.M21.Equals(matrix2.M21) && matrix1.M22.Equals(matrix2.M22) &&
matrix1.OffsetX.Equals(matrix2.OffsetX) && matrix1.OffsetY.Equals(matrix2.OffsetY);
}
/// <summary>
/// Determines whether this matrix is equal to the specified object.
/// </summary>
public override bool Equals(object o)
{
if (!(o is XMatrix))
return false;
return Equals(this, (XMatrix)o);
}
/// <summary>
/// Determines whether this matrix is equal to the specified matrix.
/// </summary>
public bool Equals(XMatrix value)
{
return Equals(this, value);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
public override int GetHashCode()
{
if (IsDistinguishedIdentity)
return 0;
return M11.GetHashCode() ^ M12.GetHashCode() ^ M21.GetHashCode() ^ M22.GetHashCode() ^ OffsetX.GetHashCode() ^ OffsetY.GetHashCode();
}
/// <summary>
/// Parses a matrix from a string.
/// </summary>
public static XMatrix Parse(string source)
{
IFormatProvider cultureInfo = CultureInfo.InvariantCulture; //.GetCultureInfo("en-us");
TokenizerHelper helper = new TokenizerHelper(source, cultureInfo);
string str = helper.NextTokenRequired();
XMatrix identity = str == "Identity" ? Identity : new XMatrix(
Convert.ToDouble(str, cultureInfo),
Convert.ToDouble(helper.NextTokenRequired(), cultureInfo),
Convert.ToDouble(helper.NextTokenRequired(), cultureInfo),
Convert.ToDouble(helper.NextTokenRequired(), cultureInfo),
Convert.ToDouble(helper.NextTokenRequired(), cultureInfo),
Convert.ToDouble(helper.NextTokenRequired(), cultureInfo));
helper.LastTokenRequired();
return identity;
}
/// <summary>
/// Converts this XMatrix to a human readable string.
/// </summary>
public override string ToString()
{
return ConvertToString(null, null);
}
/// <summary>
/// Converts this XMatrix to a human readable string.
/// </summary>
public string ToString(IFormatProvider provider)
{
return ConvertToString(null, provider);
}
/// <summary>
/// Converts this XMatrix to a human readable string.
/// </summary>
string IFormattable.ToString(string format, IFormatProvider provider)
{
return ConvertToString(format, provider);
}
internal string ConvertToString(string format, IFormatProvider provider)
{
if (IsIdentity)
return "Identity";
char numericListSeparator = TokenizerHelper.GetNumericListSeparator(provider);
provider = provider ?? CultureInfo.InvariantCulture;
// ReSharper disable FormatStringProblem
return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}",
new object[] { numericListSeparator, _m11, _m12, _m21, _m22, _offsetX, _offsetY });
// ReSharper restore FormatStringProblem
}
internal void MultiplyVector(ref double x, ref double y)
{
switch (_type)
{
case XMatrixTypes.Identity:
case XMatrixTypes.Translation:
return;
case XMatrixTypes.Scaling:
case XMatrixTypes.Scaling | XMatrixTypes.Translation:
x *= _m11;
y *= _m22;
return;
}
double d1 = y * _m21;
double d2 = x * _m12;
x *= _m11;
x += d1;
y *= _m22;
y += d2;
}
internal void MultiplyPoint(ref double x, ref double y)
{
switch (_type)
{
case XMatrixTypes.Identity:
return;
case XMatrixTypes.Translation:
x += _offsetX;
y += _offsetY;
return;
case XMatrixTypes.Scaling:
x *= _m11;
y *= _m22;
return;
case (XMatrixTypes.Scaling | XMatrixTypes.Translation):
x *= _m11;
x += _offsetX;
y *= _m22;
y += _offsetY;
return;
}
double d1 = (y * _m21) + _offsetX;
double d2 = (x * _m12) + _offsetY;
x *= _m11;
x += d1;
y *= _m22;
y += d2;
}
internal static XMatrix CreateTranslation(double offsetX, double offsetY)
{
XMatrix matrix = new XMatrix();
matrix.SetMatrix(1, 0, 0, 1, offsetX, offsetY, XMatrixTypes.Translation);
return matrix;
}
internal static XMatrix CreateRotationRadians(double angle)
{
return CreateRotationRadians(angle, 0, 0);
}
internal static XMatrix CreateRotationRadians(double angle, double centerX, double centerY)
{
XMatrix matrix = new XMatrix();
double sin = Math.Sin(angle);
double cos = Math.Cos(angle);
double offsetX = (centerX * (1.0 - cos)) + (centerY * sin);
double offsetY = (centerY * (1.0 - cos)) - (centerX * sin);
matrix.SetMatrix(cos, sin, -sin, cos, offsetX, offsetY, XMatrixTypes.Unknown);
return matrix;
}
internal static XMatrix CreateScaling(double scaleX, double scaleY)
{
XMatrix matrix = new XMatrix();
matrix.SetMatrix(scaleX, 0, 0, scaleY, 0, 0, XMatrixTypes.Scaling);
return matrix;
}
internal static XMatrix CreateScaling(double scaleX, double scaleY, double centerX, double centerY)
{
XMatrix matrix = new XMatrix();
matrix.SetMatrix(scaleX, 0, 0, scaleY, centerX - scaleX * centerX, centerY - scaleY * centerY, XMatrixTypes.Scaling | XMatrixTypes.Translation);
return matrix;
}
internal static XMatrix CreateSkewRadians(double skewX, double skewY, double centerX, double centerY)
{
XMatrix matrix = new XMatrix();
matrix.Append(CreateTranslation(-centerX, -centerY));
matrix.Append(new XMatrix(1, Math.Tan(skewY), Math.Tan(skewX), 1, 0, 0));
matrix.Append(CreateTranslation(centerX, centerY));
return matrix;
}
internal static XMatrix CreateSkewRadians(double skewX, double skewY)
{
XMatrix matrix = new XMatrix();
matrix.SetMatrix(1, Math.Tan(skewY), Math.Tan(skewX), 1, 0, 0, XMatrixTypes.Unknown);
return matrix;
}
static XMatrix CreateIdentity()
{
XMatrix matrix = new XMatrix();
matrix.SetMatrix(1, 0, 0, 1, 0, 0, XMatrixTypes.Identity);
return matrix;
}
/// <summary>
/// Sets the matrix.
/// </summary>
void SetMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY, XMatrixTypes type)
{
_m11 = m11;
_m12 = m12;
_m21 = m21;
_m22 = m22;
_offsetX = offsetX;
_offsetY = offsetY;
_type = type;
}
void DeriveMatrixType()
{
// ReSharper disable CompareOfFloatsByEqualityOperator
_type = XMatrixTypes.Identity;
if (_m12 != 0 || _m21 != 0)
{
_type = XMatrixTypes.Unknown;
}
else
{
if (_m11 != 1 || _m22 != 1)
_type = XMatrixTypes.Scaling;
if (_offsetX != 0 || _offsetY != 0)
_type |= XMatrixTypes.Translation;
if ((_type & (XMatrixTypes.Scaling | XMatrixTypes.Translation)) == XMatrixTypes.Identity)
_type = XMatrixTypes.Identity;
}
// ReSharper restore CompareOfFloatsByEqualityOperator
}
bool IsDistinguishedIdentity
{
get { return (_type == XMatrixTypes.Identity); }
}
// Keep the fields private and force using the properties.
// This prevents using m11 and m22 by mistake when the matrix is identity.
double _m11;
double _m12;
double _m21;
double _m22;
double _offsetX;
double _offsetY;
XMatrixTypes _type;
static readonly XMatrix s_identity = CreateIdentity();
/// <summary>
/// Internal matrix helper.
/// </summary>
internal static class MatrixHelper
{
// Fast mutiplication taking matrix type into account. Reflectored from WPF.
internal static void MultiplyMatrix(ref XMatrix matrix1, ref XMatrix matrix2)
{
XMatrixTypes type1 = matrix1._type;
XMatrixTypes type2 = matrix2._type;
if (type2 != XMatrixTypes.Identity)
{
if (type1 == XMatrixTypes.Identity)
matrix1 = matrix2;
else if (type2 == XMatrixTypes.Translation)
{
matrix1._offsetX += matrix2._offsetX;
matrix1._offsetY += matrix2._offsetY;
if (type1 != XMatrixTypes.Unknown)
matrix1._type |= XMatrixTypes.Translation;
}
else if (type1 == XMatrixTypes.Translation)
{
double num = matrix1._offsetX;
double num2 = matrix1._offsetY;
matrix1 = matrix2;
matrix1._offsetX = num * matrix2._m11 + num2 * matrix2._m21 + matrix2._offsetX;
matrix1._offsetY = num * matrix2._m12 + num2 * matrix2._m22 + matrix2._offsetY;
if (type2 == XMatrixTypes.Unknown)
matrix1._type = XMatrixTypes.Unknown;
else
matrix1._type = XMatrixTypes.Scaling | XMatrixTypes.Translation;
}
else
{
switch ((((int)type1) << 4) | (int)type2)
{
case 0x22:
matrix1._m11 *= matrix2._m11;
matrix1._m22 *= matrix2._m22;
return;
case 0x23:
matrix1._m11 *= matrix2._m11;
matrix1._m22 *= matrix2._m22;
matrix1._offsetX = matrix2._offsetX;
matrix1._offsetY = matrix2._offsetY;
matrix1._type = XMatrixTypes.Scaling | XMatrixTypes.Translation;
return;
case 0x24:
case 0x34:
case 0x42:
case 0x43:
case 0x44:
matrix1 = new XMatrix(
matrix1._m11 * matrix2._m11 + matrix1._m12 * matrix2._m21,
matrix1._m11 * matrix2._m12 + matrix1._m12 * matrix2._m22,
matrix1._m21 * matrix2._m11 + matrix1._m22 * matrix2._m21,
matrix1._m21 * matrix2._m12 + matrix1._m22 * matrix2._m22,
matrix1._offsetX * matrix2._m11 + matrix1._offsetY * matrix2._m21 + matrix2._offsetX,
matrix1._offsetX * matrix2._m12 + matrix1._offsetY * matrix2._m22 + matrix2._offsetY);
return;
case 50:
matrix1._m11 *= matrix2._m11;
matrix1._m22 *= matrix2._m22;
matrix1._offsetX *= matrix2._m11;
matrix1._offsetY *= matrix2._m22;
return;
case 0x33:
matrix1._m11 *= matrix2._m11;
matrix1._m22 *= matrix2._m22;
matrix1._offsetX = matrix2._m11 * matrix1._offsetX + matrix2._offsetX;
matrix1._offsetY = matrix2._m22 * matrix1._offsetY + matrix2._offsetY;
return;
}
}
}
}
internal static void PrependOffset(ref XMatrix matrix, double offsetX, double offsetY)
{
if (matrix._type == XMatrixTypes.Identity)
{
matrix = new XMatrix(1, 0, 0, 1, offsetX, offsetY);
matrix._type = XMatrixTypes.Translation;
}
else
{
matrix._offsetX += (matrix._m11 * offsetX) + (matrix._m21 * offsetY);
matrix._offsetY += (matrix._m12 * offsetX) + (matrix._m22 * offsetY);
if (matrix._type != XMatrixTypes.Unknown)
matrix._type |= XMatrixTypes.Translation;
}
}
internal static void TransformRect(ref XRect rect, ref XMatrix matrix)
{
if (!rect.IsEmpty)
{
XMatrixTypes type = matrix._type;
if (type != XMatrixTypes.Identity)
{
if ((type & XMatrixTypes.Scaling) != XMatrixTypes.Identity)
{
rect.X *= matrix._m11;
rect.Y *= matrix._m22;
rect.Width *= matrix._m11;
rect.Height *= matrix._m22;
if (rect.Width < 0)
{
rect.X += rect.Width;
rect.Width = -rect.Width;
}
if (rect.Height < 0)
{
rect.Y += rect.Height;
rect.Height = -rect.Height;
}
}
if ((type & XMatrixTypes.Translation) != XMatrixTypes.Identity)
{
rect.X += matrix._offsetX;
rect.Y += matrix._offsetY;
}
if (type == XMatrixTypes.Unknown)
{
XPoint point1 = matrix.Transform(rect.TopLeft);
XPoint point2 = matrix.Transform(rect.TopRight);
XPoint point3 = matrix.Transform(rect.BottomRight);
XPoint point4 = matrix.Transform(rect.BottomLeft);
rect.X = Math.Min(Math.Min(point1.X, point2.X), Math.Min(point3.X, point4.X));
rect.Y = Math.Min(Math.Min(point1.Y, point2.Y), Math.Min(point3.Y, point4.Y));
rect.Width = Math.Max(Math.Max(point1.X, point2.X), Math.Max(point3.X, point4.X)) - rect.X;
rect.Height = Math.Max(Math.Max(point1.Y, point2.Y), Math.Max(point3.Y, point4.Y)) - rect.Y;
}
}
}
}
}
/// <summary>
/// Gets the DebuggerDisplayAttribute text.
/// </summary>
/// <value>The debugger display.</value>
// ReSharper disable UnusedMember.Local
string DebuggerDisplay
// ReSharper restore UnusedMember.Local
{
get
{
if (IsIdentity)
return "matrix=(Identity)";
const string format = Config.SignificantFigures7;
// Calculate the angle in degrees.
XPoint point = new XMatrix(_m11, _m12, _m21, _m22, 0, 0).Transform(new XPoint(1, 0));
double φ = Math.Atan2(point.Y, point.X) / Const.Deg2Rad;
return String.Format(CultureInfo.InvariantCulture,
"matrix=({0:" + format + "}, {1:" + format + "}, {2:" + format + "}, {3:" + format + "}, {4:" + format + "}, {5:" + format + "}), φ={6:0.0#########}°",
_m11, _m12, _m21, _m22, _offsetX, _offsetY, φ);
}
}
}
}