1558 lines
54 KiB
C#
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, φ);
|
|
}
|
|
}
|
|
}
|
|
}
|