Merge pull request #189 from vpenades/main
Refactored AffineTransform towards System.Numerics.Matrix3x2
This commit is contained in:
Коммит
1aeec2dd88
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
using Microsoft.Maui.Graphics.Blazor.Canvas2D;
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
|
||||
|
@ -232,10 +234,9 @@ namespace Microsoft.Maui.Graphics.Blazor
|
|||
_context.Clip("evenodd");
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
{
|
||||
transform.GetMatrix(_matrix);
|
||||
_context.SetTransform(_matrix[0], _matrix[1], _matrix[2], _matrix[3], _matrix[4], _matrix[5]);
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_context.SetTransform(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31,transform.M32);
|
||||
}
|
||||
|
||||
protected override void NativeDrawArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise, bool closed)
|
||||
|
|
|
@ -4,6 +4,7 @@ using Drawing = System.Drawing;
|
|||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Text;
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.GDI
|
||||
{
|
||||
|
@ -465,7 +466,7 @@ namespace Microsoft.Maui.Graphics.GDI
|
|||
CurrentState.NativeTranslate(tx, ty);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
CurrentState.NativeConcatenateTransform(transform);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Numerics;
|
||||
|
||||
using Drawing = System.Drawing;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.GDI
|
||||
|
@ -373,12 +375,10 @@ namespace Microsoft.Maui.Graphics.GDI
|
|||
_graphics.TranslateTransform(-x, -y);
|
||||
}
|
||||
|
||||
public void NativeConcatenateTransform(AffineTransform transform)
|
||||
{
|
||||
_scale *= transform.ScaleX;
|
||||
var values = new float[6];
|
||||
transform.GetMatrix(values);
|
||||
var transformMatrix = new Matrix(values[0], values[1], values[2], values[3], values[4], values[5]);
|
||||
public void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_scale *= GetLengthScale(transform);
|
||||
var transformMatrix = new Matrix(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
|
||||
_graphics.MultiplyTransform(transformMatrix);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Native.Gtk {
|
||||
|
||||
public partial class NativeCanvas : AbstractCanvas<NativeCanvasState> {
|
||||
|
@ -170,7 +172,7 @@ namespace Microsoft.Maui.Graphics.Native.Gtk {
|
|||
}
|
||||
|
||||
[GtkMissingImplementation]
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform) { }
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform) { }
|
||||
|
||||
public override void SetShadow(SizeF offset, float blur, Color color) {
|
||||
CurrentState.Shadow = (offset, blur, color);
|
||||
|
|
|
@ -16,13 +16,9 @@ namespace Microsoft.Maui.Graphics.Native.Gtk {
|
|||
LineCap = Cairo.LineCap.Butt;
|
||||
}
|
||||
|
||||
public NativeCanvasState(NativeCanvasState prototype) {
|
||||
|
||||
StrokeDashPattern = prototype.StrokeDashPattern;
|
||||
StrokeSize = prototype.StrokeSize;
|
||||
Scale = prototype.Scale;
|
||||
Transform = prototype.Transform;
|
||||
|
||||
public NativeCanvasState(NativeCanvasState prototype)
|
||||
: base(prototype)
|
||||
{
|
||||
Antialias = prototype.Antialias;
|
||||
MiterLimit = prototype.MiterLimit;
|
||||
StrokeColor = prototype.StrokeColor;
|
||||
|
|
|
@ -14,6 +14,8 @@ using AlphaMode = SharpDX.Direct2D1.AlphaMode;
|
|||
using TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode;
|
||||
using System;
|
||||
|
||||
using NumericsMatrix3x2 = System.Numerics.Matrix3x2;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.SharpDX
|
||||
{
|
||||
public class DXCanvas : AbstractCanvas<DXCanvasState>, IBlurrableCanvas
|
||||
|
@ -754,7 +756,7 @@ namespace Microsoft.Maui.Graphics.SharpDX
|
|||
_renderTarget.Transform = CurrentState.DxTranslate(tx, ty);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(NumericsMatrix3x2 transform)
|
||||
{
|
||||
_renderTarget.Transform = CurrentState.DxConcatenateTransform(transform);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ using SharpDX.Direct2D1;
|
|||
using SharpDX.DirectWrite;
|
||||
using System;
|
||||
|
||||
using NumericsMatrix3x2 = System.Numerics.Matrix3x2;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.SharpDX
|
||||
{
|
||||
public class DXCanvasState : CanvasState
|
||||
|
@ -84,7 +86,7 @@ namespace Microsoft.Maui.Graphics.SharpDX
|
|||
ShadowOffset = prototype.ShadowOffset;
|
||||
ShadowBlur = prototype.ShadowBlur;
|
||||
|
||||
Matrix = new Matrix3x2(prototype.Matrix.ToArray());
|
||||
Matrix = prototype.Matrix;
|
||||
|
||||
FontName = prototype.FontName;
|
||||
FontSize = prototype.FontSize;
|
||||
|
@ -613,12 +615,10 @@ namespace Microsoft.Maui.Graphics.SharpDX
|
|||
return Matrix;
|
||||
}
|
||||
|
||||
public Matrix3x2 DxConcatenateTransform(AffineTransform transform)
|
||||
public Matrix3x2 DxConcatenateTransform(NumericsMatrix3x2 transform)
|
||||
{
|
||||
var values = new float[6];
|
||||
transform.GetMatrix(values);
|
||||
Matrix = Matrix3x2.Multiply(Matrix, new Matrix3x2(values));
|
||||
return Matrix;
|
||||
var dxTransform = new Matrix3x2(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
|
||||
return Matrix = Matrix3x2.Multiply(Matrix, dxTransform);
|
||||
}
|
||||
|
||||
public Matrix3x2 DxScale(float tx, float ty)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Skia
|
||||
|
@ -74,16 +76,16 @@ namespace Microsoft.Maui.Graphics.Skia
|
|||
return new SKPoint(target.X, target.Y);
|
||||
}
|
||||
|
||||
public static SKMatrix AsMatrix(this AffineTransform transform)
|
||||
public static SKMatrix AsMatrix(this in Matrix3x2 transform)
|
||||
{
|
||||
var matrix = new SKMatrix
|
||||
{
|
||||
ScaleX = transform.ScaleX,
|
||||
SkewX = transform.ShearX,
|
||||
TransX = transform.TranslateX,
|
||||
SkewY = transform.ShearY,
|
||||
ScaleY = transform.ScaleY,
|
||||
TransY = transform.TranslateY,
|
||||
ScaleX = transform.M11,
|
||||
SkewX = transform.M21,
|
||||
TransX = transform.M31,
|
||||
SkewY = transform.M12,
|
||||
ScaleY = transform.M22,
|
||||
TransY = transform.M32,
|
||||
Persp0 = 0,
|
||||
Persp1 = 0,
|
||||
Persp2 = 1
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
using SkiaSharp;
|
||||
|
||||
|
@ -834,7 +836,7 @@ namespace Microsoft.Maui.Graphics.Skia
|
|||
_canvas.Translate(tx * CurrentState.ScaleX, ty * CurrentState.ScaleY);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
var matrix = new SKMatrix();
|
||||
|
||||
|
|
|
@ -701,7 +701,7 @@ namespace Microsoft.Maui.Graphics.Win2D
|
|||
_session.Transform = CurrentState.AppendTranslate(tx, ty);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_session.Transform = CurrentState.AppendConcatenateTransform(transform);
|
||||
}
|
||||
|
|
|
@ -436,12 +436,9 @@ namespace Microsoft.Maui.Graphics.Win2D
|
|||
return Matrix;
|
||||
}
|
||||
|
||||
public Matrix3x2 AppendConcatenateTransform(AffineTransform transform)
|
||||
public Matrix3x2 AppendConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
var values = new float[6];
|
||||
transform.GetMatrix(values);
|
||||
Matrix = Matrix3x2.Multiply(Matrix, new Matrix3x2(values[0],values[1],values[2],values[3],values[4],values[5]));
|
||||
return Matrix;
|
||||
return Matrix = Matrix3x2.Multiply(Matrix, transform);
|
||||
}
|
||||
|
||||
public Matrix3x2 AppendScale(float tx, float ty)
|
||||
|
|
|
@ -701,7 +701,7 @@ namespace Microsoft.Maui.Graphics.Win2D
|
|||
_session.Transform = CurrentState.AppendTranslate(tx, ty);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_session.Transform = CurrentState.AppendConcatenateTransform(transform);
|
||||
}
|
||||
|
|
|
@ -437,12 +437,9 @@ namespace Microsoft.Maui.Graphics.Win2D
|
|||
return Matrix;
|
||||
}
|
||||
|
||||
public Matrix3x2 AppendConcatenateTransform(AffineTransform transform)
|
||||
{
|
||||
var values = new float[6];
|
||||
transform.GetMatrix(values);
|
||||
Matrix = Matrix3x2.Multiply(Matrix, new Matrix3x2(values[0], values[1], values[2], values[3], values[4], values[5]));
|
||||
return Matrix;
|
||||
public Matrix3x2 AppendConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
return Matrix = Matrix3x2.Multiply(Matrix, transform);
|
||||
}
|
||||
|
||||
public Matrix3x2 AppendScale(float tx, float ty)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
@ -232,7 +234,7 @@ namespace Microsoft.Maui.Graphics.Xaml
|
|||
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
CurrentState.XamlConcatenateTransform(transform);
|
||||
}
|
||||
|
@ -427,8 +429,8 @@ namespace Microsoft.Maui.Graphics.Xaml
|
|||
var item = GetOrCreateItem(ItemType.DrawLine);
|
||||
var element = (Line)item.Element;
|
||||
|
||||
var p1 = CurrentState.Transform.Transform(x1, y1);
|
||||
var p2 = CurrentState.Transform.Transform(x2, y2);
|
||||
var p1 = Vector2.Transform(new Vector2(x1, y1), CurrentState.Transform);
|
||||
var p2 = Vector2.Transform(new Vector2(x2, y2), CurrentState.Transform);
|
||||
element.X1 = p1.X;
|
||||
element.Y1 = p1.Y;
|
||||
element.X2 = p2.X;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
|
@ -401,7 +403,7 @@ namespace Microsoft.Maui.Graphics.Xaml
|
|||
_transformGroup.Children.Add(transform);
|
||||
}
|
||||
|
||||
public void XamlConcatenateTransform(AffineTransform transform)
|
||||
public void XamlConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
InitGroup();
|
||||
var nativeTransform = transform.AsTransform();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
|
||||
using Windows.UI.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
|
@ -146,15 +148,14 @@ namespace Microsoft.Maui.Graphics.Xaml
|
|||
return geometry;
|
||||
}
|
||||
|
||||
public static Transform AsTransform(this AffineTransform transform)
|
||||
public static Transform AsTransform(this in Matrix3x2 transform)
|
||||
{
|
||||
if (transform.IsIdentity)
|
||||
{
|
||||
return new MatrixTransform();
|
||||
}
|
||||
var values = new float[6];
|
||||
transform.GetMatrix(values);
|
||||
return new MatrixTransform() { Matrix = new Matrix(values[0], values[1], values[2], values[3],values[4], values[5])};
|
||||
var xamlTransform = new MatrixTransform();
|
||||
xamlTransform.Matrix = transform.IsIdentity
|
||||
? Matrix.Identity
|
||||
: new Matrix(transform.M11,transform.M12,transform.M21,transform.M22,transform.M31,transform.M32);
|
||||
|
||||
return xamlTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Windows;
|
|||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Xaml
|
||||
{
|
||||
|
@ -229,7 +230,7 @@ namespace Microsoft.Maui.Graphics.Xaml
|
|||
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
CurrentState.XamlConcatenateTransform(transform);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Effects;
|
||||
|
@ -406,7 +407,7 @@ namespace Microsoft.Maui.Graphics.Xaml
|
|||
_transformGroup.Children.Add(transform);
|
||||
}
|
||||
|
||||
public void XamlConcatenateTransform(AffineTransform transform)
|
||||
public void XamlConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
InitGroup();
|
||||
var nativeTransform = transform.AsTransform();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Xaml
|
||||
|
@ -134,16 +135,14 @@ namespace Microsoft.Maui.Graphics.Xaml
|
|||
return geometry;
|
||||
}
|
||||
|
||||
public static Transform AsTransform(this AffineTransform transform)
|
||||
public static Transform AsTransform(this in Matrix3x2 transform)
|
||||
{
|
||||
if (transform.IsIdentity)
|
||||
{
|
||||
return Transform.Identity;
|
||||
}
|
||||
|
||||
var values = new float[6];
|
||||
transform.GetMatrix(values);
|
||||
return new MatrixTransform(values[0], values[1], values[2], values[3], values[4], values[5]);
|
||||
return new MatrixTransform(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
|
@ -27,7 +29,7 @@ namespace Microsoft.Maui.Graphics
|
|||
protected abstract void NativeRotate(float degrees, float radians);
|
||||
protected abstract void NativeScale(float fx, float fy);
|
||||
protected abstract void NativeTranslate(float tx, float ty);
|
||||
protected abstract void NativeConcatenateTransform(AffineTransform transform);
|
||||
protected abstract void NativeConcatenateTransform(Matrix3x2 transform);
|
||||
|
||||
protected AbstractCanvas(Func<object, TState> createNew, Func<TState, TState> createCopy)
|
||||
{
|
||||
|
@ -260,9 +262,10 @@ namespace Microsoft.Maui.Graphics
|
|||
var radians = Geometry.DegreesToRadians(degrees);
|
||||
|
||||
var transform = _currentState.Transform;
|
||||
transform.Translate(x, y);
|
||||
transform.Rotate(radians);
|
||||
transform.Translate(-x, -y);
|
||||
transform = Matrix3x2.CreateTranslation(x, y) * transform;
|
||||
transform = Matrix3x2.CreateRotation(radians) * transform;
|
||||
transform = Matrix3x2.CreateTranslation(-x, -y) * transform;
|
||||
_currentState.Transform = transform;
|
||||
|
||||
NativeRotate(degrees, radians, x, y);
|
||||
}
|
||||
|
@ -270,29 +273,36 @@ namespace Microsoft.Maui.Graphics
|
|||
public void Rotate(float degrees)
|
||||
{
|
||||
var radians = Geometry.DegreesToRadians(degrees);
|
||||
_currentState.Transform.Rotate(radians);
|
||||
|
||||
var transform = _currentState.Transform;
|
||||
transform = Matrix3x2.CreateRotation(radians) * transform;
|
||||
_currentState.Transform = transform;
|
||||
|
||||
NativeRotate(degrees, radians);
|
||||
}
|
||||
|
||||
public void Scale(float fx, float fy)
|
||||
{
|
||||
_currentState.Scale *= fx;
|
||||
_currentState.Transform.Scale(fx, fy);
|
||||
var transform = _currentState.Transform;
|
||||
transform = Matrix3x2.CreateScale(fx, fy) * transform;
|
||||
_currentState.Transform = transform;
|
||||
|
||||
NativeScale(fx, fy);
|
||||
}
|
||||
|
||||
public void Translate(float tx, float ty)
|
||||
{
|
||||
_currentState.Transform.Translate(tx, ty);
|
||||
var transform = _currentState.Transform;
|
||||
transform = Matrix3x2.CreateTranslation(tx, ty) * transform;
|
||||
_currentState.Transform = transform;
|
||||
|
||||
NativeTranslate(tx, ty);
|
||||
}
|
||||
|
||||
public void ConcatenateTransform(AffineTransform transform)
|
||||
public void ConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_currentState.Scale *= transform.ScaleX;
|
||||
_currentState.Transform.Concatenate(transform);
|
||||
_currentState.Transform = transform * _currentState.Transform;
|
||||
|
||||
NativeConcatenateTransform(transform);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,404 +0,0 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
{
|
||||
public class AffineTransform
|
||||
{
|
||||
private const float Epsilon = 1E-10f;
|
||||
|
||||
private float _m00;
|
||||
private float _m01;
|
||||
private float _m02;
|
||||
private float _m10;
|
||||
private float _m11;
|
||||
private float _m12;
|
||||
|
||||
public AffineTransform()
|
||||
{
|
||||
_m00 = _m11 = 1.0f;
|
||||
_m10 = _m01 = _m02 = _m12 = 0.0f;
|
||||
}
|
||||
|
||||
public AffineTransform(AffineTransform t)
|
||||
{
|
||||
_m00 = t._m00;
|
||||
_m10 = t._m10;
|
||||
_m01 = t._m01;
|
||||
_m11 = t._m11;
|
||||
_m02 = t._m02;
|
||||
_m12 = t._m12;
|
||||
}
|
||||
|
||||
public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12)
|
||||
{
|
||||
_m00 = m00;
|
||||
_m10 = m10;
|
||||
_m01 = m01;
|
||||
_m11 = m11;
|
||||
_m02 = m02;
|
||||
_m12 = m12;
|
||||
}
|
||||
|
||||
public AffineTransform(float[] matrix)
|
||||
{
|
||||
_m00 = matrix[0];
|
||||
_m10 = matrix[1];
|
||||
_m01 = matrix[2];
|
||||
_m11 = matrix[3];
|
||||
if (matrix.Length > 4)
|
||||
{
|
||||
_m02 = matrix[4];
|
||||
_m12 = matrix[5];
|
||||
}
|
||||
}
|
||||
|
||||
public AffineTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_m00 = matrix.M11;
|
||||
_m10 = matrix.M12;
|
||||
_m01 = matrix.M21;
|
||||
_m11 = matrix.M22;
|
||||
_m02 = matrix.M31;
|
||||
_m12 = matrix.M32;
|
||||
}
|
||||
|
||||
public void SetMatrix(float m00, float m10, float m01, float m11, float m02, float m12)
|
||||
{
|
||||
_m00 = m00;
|
||||
_m10 = m10;
|
||||
_m01 = m01;
|
||||
_m11 = m11;
|
||||
_m02 = m02;
|
||||
_m12 = m12;
|
||||
}
|
||||
|
||||
public void SetMatrix(in Matrix3x2 matrix)
|
||||
{
|
||||
_m00 = matrix.M11;
|
||||
_m10 = matrix.M12;
|
||||
_m01 = matrix.M21;
|
||||
_m11 = matrix.M22;
|
||||
_m02 = matrix.M31;
|
||||
_m12 = matrix.M32;
|
||||
}
|
||||
|
||||
public float ScaleX => _m00;
|
||||
|
||||
public float ScaleY => _m11;
|
||||
|
||||
public float ShearX => _m01;
|
||||
|
||||
public float ShearY => _m10;
|
||||
|
||||
public float TranslateX => _m02;
|
||||
|
||||
public float TranslateY => _m12;
|
||||
|
||||
public void GetMatrix(float[] matrix)
|
||||
{
|
||||
matrix[0] = _m00;
|
||||
matrix[1] = _m10;
|
||||
matrix[2] = _m01;
|
||||
matrix[3] = _m11;
|
||||
if (matrix.Length > 4)
|
||||
{
|
||||
matrix[4] = _m02;
|
||||
matrix[5] = _m12;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetDeterminant()
|
||||
{
|
||||
return _m00 * _m11 - _m01 * _m10;
|
||||
}
|
||||
|
||||
public void SetTransform(float m00, float m10, float m01, float m11, float m02, float m12)
|
||||
{
|
||||
_m00 = m00;
|
||||
_m10 = m10;
|
||||
_m01 = m01;
|
||||
_m11 = m11;
|
||||
_m02 = m02;
|
||||
_m12 = m12;
|
||||
}
|
||||
|
||||
public void SetTransform(in Matrix3x2 matrix)
|
||||
{
|
||||
_m00 = matrix.M11;
|
||||
_m10 = matrix.M12;
|
||||
_m01 = matrix.M21;
|
||||
_m11 = matrix.M22;
|
||||
_m02 = matrix.M31;
|
||||
_m12 = matrix.M32;
|
||||
}
|
||||
|
||||
public void SetTransform(AffineTransform t)
|
||||
{
|
||||
SetTransform(t._m00, t._m10, t._m01, t._m11, t._m02, t._m12);
|
||||
}
|
||||
|
||||
public void SetToIdentity()
|
||||
{
|
||||
_m00 = _m11 = 1.0f;
|
||||
_m10 = _m01 = _m02 = _m12 = 0.0f;
|
||||
}
|
||||
|
||||
public void SetToTranslation(float mx, float my)
|
||||
{
|
||||
_m00 = _m11 = 1.0f;
|
||||
_m01 = _m10 = 0.0f;
|
||||
_m02 = mx;
|
||||
_m12 = my;
|
||||
}
|
||||
|
||||
public void SetToScale(float scx, float scy)
|
||||
{
|
||||
_m00 = scx;
|
||||
_m11 = scy;
|
||||
_m10 = _m01 = _m02 = _m12 = 0.0f;
|
||||
}
|
||||
|
||||
public void SetToShear(float shx, float shy)
|
||||
{
|
||||
_m00 = _m11 = 1.0f;
|
||||
_m02 = _m12 = 0.0f;
|
||||
_m01 = shx;
|
||||
_m10 = shy;
|
||||
}
|
||||
|
||||
public void SetToRotation(float angle)
|
||||
{
|
||||
float sin = (float) Math.Sin(angle);
|
||||
float cos = (float) Math.Cos(angle);
|
||||
if (Math.Abs(cos) < Epsilon)
|
||||
{
|
||||
cos = 0.0f;
|
||||
sin = sin > 0.0f ? 1.0f : -1.0f;
|
||||
}
|
||||
else if (Math.Abs(sin) < Epsilon)
|
||||
{
|
||||
sin = 0.0f;
|
||||
cos = cos > 0.0f ? 1.0f : -1.0f;
|
||||
}
|
||||
|
||||
_m00 = _m11 = cos;
|
||||
_m01 = -sin;
|
||||
_m10 = sin;
|
||||
_m02 = _m12 = 0.0f;
|
||||
}
|
||||
|
||||
public void SetToRotation(float angle, float px, float py)
|
||||
{
|
||||
SetToRotation(angle);
|
||||
_m02 = px * (1.0f - _m00) + py * _m10;
|
||||
_m12 = py * (1.0f - _m00) - px * _m10;
|
||||
}
|
||||
|
||||
public static AffineTransform GetTranslateInstance(float mx, float my)
|
||||
{
|
||||
var t = new AffineTransform();
|
||||
t.SetToTranslation(mx, my);
|
||||
return t;
|
||||
}
|
||||
|
||||
public static AffineTransform GetScaleInstance(float scx, float scY)
|
||||
{
|
||||
var t = new AffineTransform();
|
||||
t.SetToScale(scx, scY);
|
||||
return t;
|
||||
}
|
||||
|
||||
public static AffineTransform GetShearInstance(float shx, float shy)
|
||||
{
|
||||
var m = new AffineTransform();
|
||||
m.SetToShear(shx, shy);
|
||||
return m;
|
||||
}
|
||||
|
||||
public static AffineTransform GetRotateInstance(float angle)
|
||||
{
|
||||
var t = new AffineTransform();
|
||||
t.SetToRotation(angle);
|
||||
return t;
|
||||
}
|
||||
|
||||
public static AffineTransform GetRotateInstance(float angle, float x, float y)
|
||||
{
|
||||
var t = new AffineTransform();
|
||||
t.SetToRotation(angle, x, y);
|
||||
return t;
|
||||
}
|
||||
|
||||
public void Translate(float mx, float my)
|
||||
{
|
||||
Concatenate(GetTranslateInstance(mx, my));
|
||||
}
|
||||
|
||||
public void Scale(float scx, float scy)
|
||||
{
|
||||
Concatenate(GetScaleInstance(scx, scy));
|
||||
}
|
||||
|
||||
public void Shear(float shx, float shy)
|
||||
{
|
||||
Concatenate(GetShearInstance(shx, shy));
|
||||
}
|
||||
|
||||
public void RotateInDegrees(float angle)
|
||||
{
|
||||
Rotate(Geometry.DegreesToRadians(angle));
|
||||
}
|
||||
|
||||
public void RotateInDegrees(float angle, float px, float py)
|
||||
{
|
||||
Rotate(Geometry.DegreesToRadians(angle), px, py);
|
||||
}
|
||||
|
||||
public void Rotate(float angle)
|
||||
{
|
||||
Concatenate(GetRotateInstance(angle));
|
||||
}
|
||||
|
||||
public void Rotate(float angle, float px, float py)
|
||||
{
|
||||
Concatenate(GetRotateInstance(angle, px, py));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply two AffineTransform objects
|
||||
/// </summary>
|
||||
/// <param name="t1">the multiplicand</param>
|
||||
/// <param name="t2">the multiplier</param>
|
||||
/// <returns>an AffineTransform object that is a result of t1 multiplied by t2</returns>
|
||||
private AffineTransform Multiply(AffineTransform t1, AffineTransform t2)
|
||||
{
|
||||
return new AffineTransform(
|
||||
t1._m00 * t2._m00 + t1._m10 * t2._m01, // m00
|
||||
t1._m00 * t2._m10 + t1._m10 * t2._m11, // m01
|
||||
t1._m01 * t2._m00 + t1._m11 * t2._m01, // m10
|
||||
t1._m01 * t2._m10 + t1._m11 * t2._m11, // m11
|
||||
t1._m02 * t2._m00 + t1._m12 * t2._m01 + t2._m02, // m02
|
||||
t1._m02 * t2._m10 + t1._m12 * t2._m11 + t2._m12); // m12
|
||||
}
|
||||
|
||||
public void Concatenate(AffineTransform t)
|
||||
{
|
||||
SetTransform(Multiply(t, this));
|
||||
}
|
||||
|
||||
public void PreConcatenate(AffineTransform t)
|
||||
{
|
||||
SetTransform(Multiply(this, t));
|
||||
}
|
||||
|
||||
public AffineTransform CreateInverse()
|
||||
{
|
||||
float det = GetDeterminant();
|
||||
if (Math.Abs(det) < Epsilon)
|
||||
throw new Exception("Determinant is zero");
|
||||
|
||||
return new AffineTransform(
|
||||
_m11 / det,
|
||||
-_m10 / det,
|
||||
-_m01 / det,
|
||||
_m00 / det,
|
||||
(_m01 * _m12 - _m11 * _m02) / det,
|
||||
(_m10 * _m02 - _m00 * _m12) / det
|
||||
);
|
||||
}
|
||||
|
||||
public PointF Transform(PointF src)
|
||||
{
|
||||
return Transform(src.X, src.Y);
|
||||
}
|
||||
|
||||
public PointF Transform(float x, float y)
|
||||
{
|
||||
return new PointF(x * _m00 + y * _m01 + _m02, x * _m10 + y * _m11 + _m12);
|
||||
}
|
||||
|
||||
public PointF InverseTransform(PointF src)
|
||||
{
|
||||
float det = GetDeterminant();
|
||||
if (Math.Abs(det) < Epsilon)
|
||||
throw new Exception("Unable to inverse this transform.");
|
||||
|
||||
float x = src.X - _m02;
|
||||
float y = src.Y - _m12;
|
||||
|
||||
return new PointF((x * _m11 - y * _m01) / det, (y * _m00 - x * _m10) / det);
|
||||
}
|
||||
|
||||
public void Transform(float[] src, int srcOff, float[] dst, int dstOff, int length)
|
||||
{
|
||||
int step = 2;
|
||||
if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2)
|
||||
{
|
||||
srcOff = srcOff + length * 2 - 2;
|
||||
dstOff = dstOff + length * 2 - 2;
|
||||
step = -2;
|
||||
}
|
||||
|
||||
while (--length >= 0)
|
||||
{
|
||||
float x = src[srcOff + 0];
|
||||
float y = src[srcOff + 1];
|
||||
dst[dstOff + 0] = x * _m00 + y * _m01 + _m02;
|
||||
dst[dstOff + 1] = x * _m10 + y * _m11 + _m12;
|
||||
srcOff += step;
|
||||
dstOff += step;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUnityTransform()
|
||||
{
|
||||
return !(HasScale() || HasRotate() || HasTranslate());
|
||||
}
|
||||
|
||||
private bool HasScale()
|
||||
{
|
||||
// ReSharper disable CompareOfFloatsByEqualityOperator
|
||||
return _m00 != 1.0 || _m11 != 1.0;
|
||||
// ReSharper restore CompareOfFloatsByEqualityOperator
|
||||
}
|
||||
|
||||
private bool HasRotate()
|
||||
{
|
||||
// ReSharper disable CompareOfFloatsByEqualityOperator
|
||||
return _m10 != 0.0 || _m01 != 0.0;
|
||||
// ReSharper restore CompareOfFloatsByEqualityOperator
|
||||
}
|
||||
|
||||
private bool HasTranslate()
|
||||
{
|
||||
// ReSharper disable CompareOfFloatsByEqualityOperator
|
||||
return _m02 != 0.0 || _m12 != 0.0;
|
||||
// ReSharper restore CompareOfFloatsByEqualityOperator
|
||||
}
|
||||
|
||||
public bool OnlyTranslate()
|
||||
{
|
||||
return !HasRotate() && !HasScale();
|
||||
}
|
||||
|
||||
public bool OnlyTranslateOrScale()
|
||||
{
|
||||
return !HasRotate();
|
||||
}
|
||||
|
||||
public bool OnlyScale()
|
||||
{
|
||||
return !HasRotate() && !HasTranslate();
|
||||
}
|
||||
|
||||
public static implicit operator AffineTransform(Matrix3x2 matrix) => new AffineTransform(matrix);
|
||||
|
||||
public static explicit operator Matrix3x2(AffineTransform matrix)
|
||||
{
|
||||
return new Matrix3x2(matrix._m00, matrix._m10, matrix._m01, matrix._m11, matrix._m02, matrix._m12);
|
||||
}
|
||||
|
||||
public bool IsIdentity => _m00 == 1.0f && _m11 == 1.0f && _m10 == 0.0f && _m01 == 0.0f && _m02 == 0.0f && _m12 == 0.0f;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using Android.Graphics;
|
||||
using Android.Text;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Native
|
||||
{
|
||||
|
@ -65,11 +66,16 @@ namespace Microsoft.Maui.Graphics.Native
|
|||
return new global::Android.Graphics.PointF(target.X, target.Y);
|
||||
}
|
||||
|
||||
public static Matrix AsMatrix(this AffineTransform transform)
|
||||
public static Matrix AsMatrix(this Matrix3x2 transform)
|
||||
{
|
||||
var values = new float[9];
|
||||
transform.GetMatrix(values);
|
||||
|
||||
values[Matrix.MscaleX] = transform.M11;
|
||||
values[Matrix.MskewX] = transform.M21;
|
||||
values[Matrix.MtransY] = transform.M31;
|
||||
values[Matrix.MskewY] = transform.M12;
|
||||
values[Matrix.MscaleY] = transform.M22;
|
||||
values[Matrix.MtransX] = transform.M32;
|
||||
values[Matrix.Mpersp0] = 0; // 6
|
||||
values[Matrix.Mpersp1] = 0; // 7
|
||||
values[Matrix.Mpersp2] = 1; // 8
|
||||
|
|
|
@ -4,6 +4,7 @@ using Android.Content;
|
|||
using Android.Graphics;
|
||||
using Android.Text;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Native
|
||||
{
|
||||
|
@ -780,7 +781,7 @@ namespace Microsoft.Maui.Graphics.Native
|
|||
_canvas.Translate(tx * CurrentState.ScaleX, ty * CurrentState.ScaleY);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
var matrix = new Matrix(_canvas.Matrix);
|
||||
matrix.PostConcat(transform.AsMatrix());
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
{
|
||||
|
@ -6,22 +7,51 @@ namespace Microsoft.Maui.Graphics
|
|||
{
|
||||
public float[] StrokeDashPattern { get; set; }
|
||||
public float StrokeSize { get; set; } = 1;
|
||||
public float Scale { get; set; } = 1;
|
||||
public AffineTransform Transform { get; set; }
|
||||
|
||||
private Matrix3x2 _transform = Matrix3x2.Identity;
|
||||
private float _scale = 1;
|
||||
private float _scaleX = 1;
|
||||
private float _scaleY = 1;
|
||||
|
||||
public Matrix3x2 Transform
|
||||
{
|
||||
get => this._transform;
|
||||
set
|
||||
{
|
||||
if (this._transform == value) return;
|
||||
|
||||
this._transform = value;
|
||||
value.DeconstructScales(out _scale, out _scaleX, out _scaleY);
|
||||
TransformChanged();
|
||||
}
|
||||
}
|
||||
public float Scale => this._scale;
|
||||
public float ScaleX => this._scaleX;
|
||||
public float ScaleY => this._scaleY;
|
||||
|
||||
protected CanvasState()
|
||||
{
|
||||
Transform = new AffineTransform();
|
||||
|
||||
}
|
||||
|
||||
protected CanvasState(CanvasState prototype)
|
||||
{
|
||||
StrokeDashPattern = prototype.StrokeDashPattern;
|
||||
StrokeSize = prototype.StrokeSize;
|
||||
Transform = new AffineTransform(prototype.Transform);
|
||||
Scale = prototype.Scale;
|
||||
|
||||
this._transform = prototype._transform;
|
||||
this._scale = prototype._scale;
|
||||
this._scaleX = prototype._scaleX;
|
||||
this._scaleY = prototype._scaleY;
|
||||
}
|
||||
|
||||
protected virtual void TransformChanged()
|
||||
{
|
||||
// let derived classes handle the transform change if needed.
|
||||
}
|
||||
|
||||
protected static float GetLengthScale(Matrix3x2 matrix) => matrix.GetLengthScale();
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
// Do nothing right now
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
|
@ -76,7 +78,7 @@ namespace Microsoft.Maui.Graphics
|
|||
|
||||
public void Translate(float tx, float ty);
|
||||
|
||||
public void ConcatenateTransform(AffineTransform transform);
|
||||
public void ConcatenateTransform(Matrix3x2 transform);
|
||||
|
||||
public void SaveState();
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using CoreGraphics;
|
|||
using CoreText;
|
||||
using Foundation;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Native
|
||||
{
|
||||
|
@ -1356,7 +1357,7 @@ namespace Microsoft.Maui.Graphics.Native
|
|||
_context.TranslateCTM(tx, ty);
|
||||
}
|
||||
|
||||
protected override void NativeConcatenateTransform(AffineTransform transform)
|
||||
protected override void NativeConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_context.ConcatCTM(transform.AsCGAffineTransform());
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
using CoreGraphics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Native
|
||||
|
@ -386,23 +388,9 @@ namespace Microsoft.Maui.Graphics.Native
|
|||
}
|
||||
}
|
||||
|
||||
public static CGAffineTransform AsCGAffineTransform(this AffineTransform transform)
|
||||
public static CGAffineTransform AsCGAffineTransform(this in Matrix3x2 transform)
|
||||
{
|
||||
if (transform != null)
|
||||
{
|
||||
var matrix = new float[6];
|
||||
transform.GetMatrix(matrix);
|
||||
float xx = matrix[0];
|
||||
float yx = matrix[1];
|
||||
float xy = matrix[2];
|
||||
float yy = matrix[3];
|
||||
float x0 = matrix[4];
|
||||
float y0 = matrix[5];
|
||||
|
||||
return new CGAffineTransform(xx, yx, xy, yy, x0, y0);
|
||||
}
|
||||
|
||||
return CGAffineTransform.MakeIdentity();
|
||||
return new CGAffineTransform(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
|
||||
}
|
||||
|
||||
public static CGColor AsCGColor(this Color color)
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.Maui.Graphics.Tests")]
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
{
|
||||
static class Matrix3x2Extensions
|
||||
{
|
||||
public static bool IsZero(this in Matrix3x2 matrix)
|
||||
{
|
||||
if (matrix.M11 != 0) return false;
|
||||
if (matrix.M12 != 0) return false;
|
||||
if (matrix.M21 != 0) return false;
|
||||
if (matrix.M22 != 0) return false;
|
||||
if (matrix.M31 != 0) return false;
|
||||
if (matrix.M32 != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsFinite(this in Matrix3x2 matrix)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
#if NETSTANDARD2_0 || TIZEN40
|
||||
result &= !(float.IsNaN(matrix.M11) || float.IsInfinity(matrix.M11));
|
||||
result &= !(float.IsNaN(matrix.M12) || float.IsInfinity(matrix.M12));
|
||||
result &= !(float.IsNaN(matrix.M21) || float.IsInfinity(matrix.M21));
|
||||
result &= !(float.IsNaN(matrix.M22) || float.IsInfinity(matrix.M22));
|
||||
result &= !(float.IsNaN(matrix.M31) || float.IsInfinity(matrix.M31));
|
||||
result &= !(float.IsNaN(matrix.M32) || float.IsInfinity(matrix.M32));
|
||||
#else
|
||||
result &= float.IsFinite(matrix.M11);
|
||||
result &= float.IsFinite(matrix.M12);
|
||||
result &= float.IsFinite(matrix.M21);
|
||||
result &= float.IsFinite(matrix.M22);
|
||||
result &= float.IsFinite(matrix.M31);
|
||||
result &= float.IsFinite(matrix.M32);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Vector2 GetScale(this in Matrix3x2 matrix)
|
||||
{
|
||||
var sx = matrix.M12 == 0 ? Math.Abs(matrix.M11) : new Vector2(matrix.M11, matrix.M12).Length();
|
||||
var sy = matrix.M21 == 0 ? Math.Abs(matrix.M22) : new Vector2(matrix.M21, matrix.M22).Length();
|
||||
if (matrix.GetDeterminant() < 0) sy = -sy;
|
||||
return new Vector2(sx, sy);
|
||||
}
|
||||
|
||||
public static float GetRotation(this in Matrix3x2 matrix)
|
||||
{
|
||||
return (float)Math.Atan2(matrix.M12, matrix.M11);
|
||||
}
|
||||
|
||||
public static Vector2 GetTranslation(this in Matrix3x2 matrix)
|
||||
{
|
||||
return matrix.Translation;
|
||||
}
|
||||
|
||||
public static Matrix3x2 WithScale(this Matrix3x2 matrix, Vector2 scale)
|
||||
{
|
||||
var sx = matrix.M12 == 0 ? Math.Abs(matrix.M11) : new Vector2(matrix.M11, matrix.M12).Length();
|
||||
var sy = matrix.M21 == 0 ? Math.Abs(matrix.M22) : new Vector2(matrix.M21, matrix.M22).Length();
|
||||
// if (matrix.GetDeterminant() < 0) sy = -sy;
|
||||
|
||||
scale /= new Vector2(sx, sy);
|
||||
|
||||
matrix.M11 *= scale.X;
|
||||
matrix.M12 *= scale.X;
|
||||
matrix.M21 *= scale.Y;
|
||||
matrix.M22 *= scale.Y;
|
||||
return matrix;
|
||||
|
||||
// var t = matrix.Translation;
|
||||
// var r = matrix.GetRotation();
|
||||
// return CreateMatrix3x2(scale, r, t);
|
||||
}
|
||||
|
||||
public static Matrix3x2 WithoutScale(this in Matrix3x2 matrix)
|
||||
{
|
||||
return matrix.WithScale(Vector2.One);
|
||||
}
|
||||
|
||||
public static Matrix3x2 WithRotation(this in Matrix3x2 matrix, float radians)
|
||||
{
|
||||
var t = matrix.Translation;
|
||||
var s = matrix.GetScale();
|
||||
return CreateMatrix3x2(s, radians, t);
|
||||
}
|
||||
|
||||
public static Matrix3x2 WithoutRotation(this in Matrix3x2 matrix)
|
||||
{
|
||||
var t = matrix.Translation;
|
||||
var s = matrix.GetScale();
|
||||
return CreateMatrix3x2(s, 0, t);
|
||||
}
|
||||
|
||||
public static Matrix3x2 WithTranslation(this Matrix3x2 matrix, Vector2 translation)
|
||||
{
|
||||
matrix.Translation = translation;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix from an SRT.
|
||||
/// </summary>
|
||||
/// <param name="scale">The scale</param>
|
||||
/// <param name="rotation">The rotation, in radians</param>
|
||||
/// <param name="translation">the translation</param>
|
||||
/// <returns>A Matrix3x2</returns>
|
||||
/// <remarks>
|
||||
/// This is equivalent to:<br/>
|
||||
/// <c>
|
||||
/// m = Matrix3x2.CreateScale(scale)<br/>
|
||||
/// m *= Matri3x2.CreateRotation(rotation)<br/>
|
||||
/// m *= Matri3x2.CreateTranslation(translation)<br/>
|
||||
/// </c>
|
||||
/// </remarks>
|
||||
internal static Matrix3x2 CreateMatrix3x2(Vector2 scale, float rotation, Vector2 translation)
|
||||
{
|
||||
var m = Matrix3x2.CreateRotation(rotation);
|
||||
m.M11 *= scale.X;
|
||||
m.M12 *= scale.X;
|
||||
m.M21 *= scale.Y;
|
||||
m.M22 *= scale.Y;
|
||||
m.M31 = translation.X;
|
||||
m.M32 = translation.Y;
|
||||
return m;
|
||||
}
|
||||
|
||||
public static float GetLengthScale(this in Matrix3x2 matrix)
|
||||
{
|
||||
var determinant = matrix.GetDeterminant();
|
||||
var areaScale = Math.Abs(determinant);
|
||||
return (float)Math.Sqrt(areaScale);
|
||||
}
|
||||
|
||||
public static void CopyTo(this in Matrix3x2 matrix, float[] dst, int offset = 0, int count = 6)
|
||||
{
|
||||
count = Math.Min(dst.Length, count);
|
||||
|
||||
dst[offset + 0] = matrix.M11;
|
||||
dst[offset + 1] = matrix.M12;
|
||||
dst[offset + 2] = matrix.M21;
|
||||
dst[offset + 3] = matrix.M22;
|
||||
if (count > 4)
|
||||
{
|
||||
dst[offset + 4] = matrix.M31;
|
||||
dst[offset + 5] = matrix.M32;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeconstructScales(this in Matrix3x2 value, out float scale, out float scalex, out float scaley)
|
||||
{
|
||||
var det = value.GetDeterminant();
|
||||
scale = (float)Math.Sqrt(Math.Abs(det));
|
||||
scalex = value.M12 == 0 ? Math.Abs(value.M11) : new Vector2(value.M11, value.M12).Length();
|
||||
scaley = value.M21 == 0 ? Math.Abs(value.M22) : new Vector2(value.M21, value.M22).Length();
|
||||
if (det < 0) scaley = -scaley;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
{
|
||||
|
@ -67,7 +68,7 @@ namespace Microsoft.Maui.Graphics
|
|||
float scale)
|
||||
{
|
||||
var scaledPath = new PathF(target);
|
||||
var transform = AffineTransform.GetScaleInstance(scale, scale);
|
||||
var transform = Matrix3x2.CreateScale(scale);
|
||||
scaledPath.Transform(transform);
|
||||
return scaledPath;
|
||||
}
|
||||
|
|
|
@ -920,10 +920,10 @@ namespace Microsoft.Maui.Graphics
|
|||
return Geometry.RotatePoint(pivotPoint, point, angle);
|
||||
}
|
||||
|
||||
public void Transform(AffineTransform transform)
|
||||
public void Transform(Matrix3x2 transform)
|
||||
{
|
||||
for (var i = 0; i < _points.Count; i++)
|
||||
_points[i] = transform.Transform(_points[i]);
|
||||
_points[i] = Vector2.Transform((Vector2)_points[i], transform);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
|
@ -220,7 +222,7 @@ namespace Microsoft.Maui.Graphics
|
|||
_commands.Add(canvas => canvas.Translate(tx, ty));
|
||||
}
|
||||
|
||||
public void ConcatenateTransform(AffineTransform transform)
|
||||
public void ConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_commands.Add(canvas => canvas.ConcatenateTransform(transform));
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Microsoft.Maui.Graphics
|
|||
|
||||
public float Y { get; set; }
|
||||
|
||||
public static PointF Zero = new PointF();
|
||||
public static readonly PointF Zero = new PointF();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
@ -69,6 +69,11 @@ namespace Microsoft.Maui.Graphics
|
|||
return p;
|
||||
}
|
||||
|
||||
public PointF TransformBy(in Matrix3x2 transform)
|
||||
{
|
||||
return Vector2.Transform((Vector2)this, transform);
|
||||
}
|
||||
|
||||
public PointF Round()
|
||||
{
|
||||
return new PointF((float)Math.Round(X), (float)Math.Round(Y));
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
using Microsoft.Maui.Graphics.Text;
|
||||
|
||||
namespace Microsoft.Maui.Graphics
|
||||
|
@ -221,10 +223,11 @@ namespace Microsoft.Maui.Graphics
|
|||
_canvas.Translate(tx, ty);
|
||||
}
|
||||
|
||||
public void ConcatenateTransform(AffineTransform transform)
|
||||
public void ConcatenateTransform(Matrix3x2 transform)
|
||||
{
|
||||
_scaleX *= transform.ScaleX;
|
||||
_scaleY *= transform.ScaleY;
|
||||
transform.DeconstructScales(out _, out var sx, out var sy);
|
||||
_scaleX *= sx;
|
||||
_scaleY *= sy;
|
||||
_canvas.ConcatenateTransform(transform);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,11 @@ namespace Microsoft.Maui.Graphics
|
|||
}
|
||||
}
|
||||
|
||||
public SizeF TransformNormalBy(in Matrix3x2 transform)
|
||||
{
|
||||
return (SizeF)Vector2.TransformNormal((Vector2)this, transform);
|
||||
}
|
||||
|
||||
public static SizeF operator +(SizeF s1, SizeF s2)
|
||||
{
|
||||
return new SizeF(s1._width + s2._width, s1._height + s2._height);
|
||||
|
@ -104,6 +109,11 @@ namespace Microsoft.Maui.Graphics
|
|||
return new Vector2(size.Width, size.Height);
|
||||
}
|
||||
|
||||
public static explicit operator SizeF(Vector2 size)
|
||||
{
|
||||
return new SizeF(size.X, size.Y);
|
||||
}
|
||||
|
||||
public bool Equals(SizeF other)
|
||||
{
|
||||
return _width.Equals(other._width) && _height.Equals(other._height);
|
||||
|
@ -134,6 +144,7 @@ namespace Microsoft.Maui.Graphics
|
|||
width = Width;
|
||||
height = Height;
|
||||
}
|
||||
|
||||
public static implicit operator Size(SizeF s) => new Size(s.Width, s.Height);
|
||||
|
||||
public static bool TryParse(string value, out SizeF sizeF)
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Maui.Graphics.Tests
|
||||
{
|
||||
public class Matrix3x2ExtensionsTests
|
||||
{
|
||||
public static IEnumerable<object[]> ScaleRotationData()
|
||||
{
|
||||
yield return new object[] { 0, 1, 1 };
|
||||
yield return new object[] { 0, -1, -1 };
|
||||
yield return new object[] { 1, 2, 3 };
|
||||
yield return new object[] { -2, 1, 1 };
|
||||
yield return new object[] { -1, 5, 2 };
|
||||
yield return new object[] { -3, 1, -2 };
|
||||
yield return new object[] { 0, -1, 2 };
|
||||
yield return new object[] { 0, 1, -2 };
|
||||
yield return new object[] { 3, -1, -2 };
|
||||
yield return new object[] { 1, -2, -2 };
|
||||
yield return new object[] { -1, -2, -2 };
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestEquivalence()
|
||||
{
|
||||
// the identity matrix should be equivalent to a matrix
|
||||
// with negative scale and rotated 180 degrees
|
||||
|
||||
var a = Matrix3x2.Identity;
|
||||
|
||||
var b = Matrix3x2.Identity;
|
||||
b *= Matrix3x2.CreateScale(-1, -1);
|
||||
b *= Matrix3x2.CreateRotation((float)Math.PI);
|
||||
|
||||
AssertEqual(a, b, 6);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ScaleRotationData))]
|
||||
public void TestDeconstruct(float rotation, float scaleX, float scaleY)
|
||||
{
|
||||
var translation = new Vector2(5, 9);
|
||||
|
||||
var reference = Matrix3x2.Identity;
|
||||
reference *= Matrix3x2.CreateScale(scaleX, scaleY);
|
||||
reference *= Matrix3x2.CreateRotation(rotation);
|
||||
reference *= Matrix3x2.CreateTranslation(translation);
|
||||
ReferenceDeconstruct(reference, out var refScale, out var refRot, out var refTrans);
|
||||
|
||||
// deconstruct
|
||||
|
||||
var s = reference.GetScale();
|
||||
var r = reference.GetRotation();
|
||||
var t = reference.GetTranslation();
|
||||
|
||||
AssertEqual(refScale, s, 5);
|
||||
Assert.Equal(refRot, r, 5);
|
||||
AssertEqual(refTrans, t, 5);
|
||||
|
||||
var m = Matrix3x2Extensions.CreateMatrix3x2(s, r, t);
|
||||
AssertEqual(reference, m, 5);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ScaleRotationData))]
|
||||
public void TestRotationAndScale(float rotation, float scaleX, float scaleY)
|
||||
{
|
||||
var translation = new Vector2(5, 9);
|
||||
|
||||
var reference = Matrix3x2.Identity;
|
||||
reference *= Matrix3x2.CreateScale(scaleX, scaleY);
|
||||
reference *= Matrix3x2.CreateRotation(rotation);
|
||||
reference *= Matrix3x2.CreateTranslation(translation);
|
||||
|
||||
ReferenceDeconstruct(reference, out var refScale, out var refRot, out var refTrans);
|
||||
|
||||
// concatenation
|
||||
// notice that AffineTransform concatenation order
|
||||
// is in reverse order compared to Matrix3x2
|
||||
|
||||
var m1 = Matrix3x2.Identity;
|
||||
m1 *= Matrix3x2.CreateScale(scaleX, scaleY);
|
||||
m1 *= Matrix3x2.CreateRotation(rotation);
|
||||
m1 *= Matrix3x2.CreateTranslation(translation);
|
||||
AssertOrthogonal(m1, 7);
|
||||
AssertEqual(reference, m1, 7);
|
||||
|
||||
// concatenation and rotation assign
|
||||
|
||||
m1 = Matrix3x2.Identity;
|
||||
m1 *= Matrix3x2.CreateScale(scaleX, scaleY);
|
||||
m1 = m1.WithRotation(scaleX >= 0 ? rotation : rotation + (float)Math.PI);
|
||||
m1 *= Matrix3x2.CreateTranslation(translation);
|
||||
AssertOrthogonal(m1, 7);
|
||||
AssertEqual(reference, m1, 4);
|
||||
|
||||
// concatenation and scale assign
|
||||
|
||||
m1 = Matrix3x2.Identity;
|
||||
m1 = m1.WithScale(new Vector2(scaleX, scaleY));
|
||||
m1 *= Matrix3x2.CreateRotation(rotation);
|
||||
m1 *= Matrix3x2.CreateTranslation(translation);
|
||||
AssertOrthogonal(m1, 7);
|
||||
AssertEqual(reference, m1, 7);
|
||||
|
||||
// deconstruct/reconstruct
|
||||
|
||||
m1 = Matrix3x2Extensions.CreateMatrix3x2(new Vector2(scaleX, scaleY), rotation, translation);
|
||||
var s = m1.GetScale();
|
||||
var r = m1.GetRotation();
|
||||
var t = m1.GetTranslation();
|
||||
m1 = Matrix3x2Extensions.CreateMatrix3x2(s, r, t);
|
||||
AssertOrthogonal(m1, 7);
|
||||
AssertEqual(reference, m1, 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestSettingScale()
|
||||
{
|
||||
var m = Matrix3x2.CreateRotation(2);
|
||||
|
||||
m = m.WithScale(new Vector2(3, 4));
|
||||
Assert.Equal(2, m.GetRotation(), 4);
|
||||
AssertEqual(new Vector2(3, 4), m.GetScale(), 5);
|
||||
|
||||
m = m.WithScale(new Vector2(2, 2));
|
||||
Assert.Equal(2, m.GetRotation(), 4);
|
||||
AssertEqual(new Vector2(2, 2), m.GetScale(), 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestAverageScale()
|
||||
{
|
||||
for (float r = 0; r < 3; r += 1f)
|
||||
{
|
||||
var rm = Matrix3x2.CreateRotation(r);
|
||||
|
||||
Assert.Equal(1, (Matrix3x2.CreateScale(1, 1) * rm).GetLengthScale(), 6);
|
||||
Assert.Equal(1, (Matrix3x2.CreateScale(-1, -1) * rm).GetLengthScale(), 6);
|
||||
Assert.Equal(2, (Matrix3x2.CreateScale(2, 2) * rm).GetLengthScale(), 6);
|
||||
Assert.Equal(1.414214f, (Matrix3x2.CreateScale(1, 2) * rm).GetLengthScale(), 5);
|
||||
Assert.Equal(1.414214f, (Matrix3x2.CreateScale(2, 1) * rm).GetLengthScale(), 5);
|
||||
Assert.Equal(1.414214f, (Matrix3x2.CreateScale(-2, 1) * rm).GetLengthScale(), 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void AssertEqual(Vector2 a, Vector2 b, int precision)
|
||||
{
|
||||
Assert.Equal(a.X, b.X, precision);
|
||||
Assert.Equal(a.Y, b.Y, precision);
|
||||
}
|
||||
|
||||
private static void AssertEqual(Matrix3x2 a, Matrix3x2 b, int precision)
|
||||
{
|
||||
Assert.Equal(a.M11, b.M11, precision);
|
||||
Assert.Equal(a.M21, b.M21, precision);
|
||||
Assert.Equal(a.M31, b.M31, precision);
|
||||
Assert.Equal(a.M12, b.M12, precision);
|
||||
Assert.Equal(a.M22, b.M22, precision);
|
||||
Assert.Equal(a.M32, b.M32, precision);
|
||||
}
|
||||
|
||||
private static void AssertOrthogonal(Matrix3x2 transform, int precission)
|
||||
{
|
||||
var x = Vector2.Normalize(new Vector2(transform.M11, transform.M12));
|
||||
var y = Vector2.Normalize(new Vector2(transform.M21, transform.M22));
|
||||
var d = Vector2.Dot(x, y);
|
||||
if (d < -1) d = -1;
|
||||
if (d > 1) d = 1;
|
||||
|
||||
var skewAngle = (float)((Math.PI / 2) - Math.Acos(d));
|
||||
|
||||
Assert.Equal(0, skewAngle, precission);
|
||||
}
|
||||
|
||||
private static void ReferenceDeconstruct(Matrix3x2 m, out Vector2 scale, out float rotation, out Vector2 translation)
|
||||
{
|
||||
// Matrix3x2 lacks decompose, but Matrix4x4 has it.
|
||||
|
||||
Matrix4x4.Decompose(new Matrix4x4(m), out var s, out Quaternion r, out var t);
|
||||
|
||||
scale = new Vector2(s.X, s.Y);
|
||||
translation = new Vector2(t.X, t.Y);
|
||||
|
||||
var hand = Vector3.Transform(Vector3.UnitX, r);
|
||||
|
||||
rotation = (float)Math.Atan2(hand.Y, hand.X);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче