diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs
index 8136f843df..243dafe817 100644
--- a/src/Avalonia.Visuals/Matrix.cs
+++ b/src/Avalonia.Visuals/Matrix.cs
@@ -215,6 +215,28 @@ namespace Avalonia
return angle * 0.0174532925;
}
+ ///
+ /// Appends another matrix as post-multiplication operation.
+ /// Equivalent to this * value;
+ ///
+ /// A matrix.
+ /// Post-multiplied matrix.
+ public Matrix Append(Matrix value)
+ {
+ return this * value;
+ }
+
+ ///
+ /// Prpends another matrix as pre-multiplication operation.
+ /// Equivalent to value * this;
+ ///
+ /// A matrix.
+ /// Pre-multiplied matrix.
+ public Matrix Prepend(Matrix value)
+ {
+ return value * this;
+ }
+
///
/// Calculates the determinant for this matrix.
///
diff --git a/src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs b/src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs
index 742bb9c804..76c2deef3b 100644
--- a/src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs
+++ b/src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs
@@ -18,11 +18,11 @@ namespace Avalonia.Media.Transformation
public static Matrix ComposeTransform(Matrix.Decomposed decomposed)
{
// According to https://www.w3.org/TR/css-transforms-1/#recomposing-to-a-2d-matrix
-
- return Matrix.CreateTranslation(decomposed.Translate) *
- Matrix.CreateRotation(decomposed.Angle) *
- Matrix.CreateSkew(decomposed.Skew.X, decomposed.Skew.Y) *
- Matrix.CreateScale(decomposed.Scale);
+ return Matrix.Identity
+ .Prepend(Matrix.CreateTranslation(decomposed.Translate))
+ .Prepend(Matrix.CreateRotation(decomposed.Angle))
+ .Prepend(Matrix.CreateSkew(decomposed.Skew.X, decomposed.Skew.Y))
+ .Prepend(Matrix.CreateScale(decomposed.Scale));
}
public static Matrix.Decomposed InterpolateDecomposedTransforms(ref Matrix.Decomposed from, ref Matrix.Decomposed to, double progress)
diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs b/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs
index 36f5dd98f1..13a24cd523 100644
--- a/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs
+++ b/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs
@@ -86,6 +86,8 @@ namespace Avalonia.Media.Transformation
if (fromIdentity && toIdentity)
{
+ result.Matrix = Matrix.Identity;
+
return true;
}
@@ -179,7 +181,8 @@ namespace Avalonia.Media.Transformation
}
case OperationType.Identity:
{
- // Do nothing.
+ result.Matrix = Matrix.Identity;
+
break;
}
}
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs
index 856b4615a5..e4f88706d8 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs
@@ -129,7 +129,7 @@ namespace Avalonia.Visuals.UnitTests.Media
Assert.Single(operations);
Assert.Equal(TransformOperation.OperationType.Matrix, operations[0].Type);
-
+
var expectedMatrix = new Matrix(1, 2, 3, 4, 5, 6);
Assert.Equal(expectedMatrix, operations[0].Matrix);
@@ -195,7 +195,7 @@ namespace Avalonia.Visuals.UnitTests.Media
[Theory]
[InlineData(0d, 10d)]
[InlineData(0.5d, 15d)]
- [InlineData(1d,20d)]
+ [InlineData(1d, 20d)]
public void Can_Interpolate_Rotation(double progress, double angle)
{
var from = TransformOperations.Parse("rotate(10deg)");
@@ -225,5 +225,73 @@ namespace Avalonia.Visuals.UnitTests.Media
Assert.Single(operations);
Assert.Equal(TransformOperation.OperationType.Matrix, operations[0].Type);
}
+
+ [Fact]
+ public void Order_Of_Operations_Is_Preserved_No_Prefix()
+ {
+ var from = TransformOperations.Parse("scale(1)");
+ var to = TransformOperations.Parse("translate(50px,50px) scale(0.5,0.5)");
+
+ var interpolated_0 = TransformOperations.Interpolate(from, to, 0);
+
+ Assert.True(interpolated_0.IsIdentity);
+
+ var interpolated_50 = TransformOperations.Interpolate(from, to, 0.5);
+
+ AssertMatrix(interpolated_50.Value, scaleX: 0.75, scaleY: 0.75, translateX: 12.5, translateY: 12.5);
+
+ var interpolated_100 = TransformOperations.Interpolate(from, to, 1);
+
+ AssertMatrix(interpolated_100.Value, scaleX: 0.5, scaleY: 0.5, translateX: 25, translateY: 25);
+ }
+
+ [Fact]
+ public void Order_Of_Operations_Is_Preserved_One_Prefix()
+ {
+ var from = TransformOperations.Parse("scale(1)");
+ var to = TransformOperations.Parse("scale(0.5,0.5) translate(50px,50px)");
+
+ var interpolated_0 = TransformOperations.Interpolate(from, to, 0);
+
+ Assert.True(interpolated_0.IsIdentity);
+
+ var interpolated_50 = TransformOperations.Interpolate(from, to, 0.5);
+
+ AssertMatrix(interpolated_50.Value, scaleX: 0.75, scaleY: 0.75, translateX: 25.0, translateY: 25);
+
+ var interpolated_100 = TransformOperations.Interpolate(from, to, 1);
+
+ AssertMatrix(interpolated_100.Value, scaleX: 0.5, scaleY: 0.5, translateX: 50, translateY: 50);
+ }
+
+ private static void AssertMatrix(Matrix matrix, double? angle = null, double? scaleX = null, double? scaleY = null, double? translateX = null, double? translateY = null)
+ {
+ Assert.True(Matrix.TryDecomposeTransform(matrix, out var composed));
+
+ if (angle.HasValue)
+ {
+ Assert.Equal(angle.Value, composed.Angle);
+ }
+
+ if (scaleX.HasValue)
+ {
+ Assert.Equal(scaleX.Value, composed.Scale.X);
+ }
+
+ if (scaleY.HasValue)
+ {
+ Assert.Equal(scaleY.Value, composed.Scale.Y);
+ }
+
+ if (translateX.HasValue)
+ {
+ Assert.Equal(translateX.Value, composed.Translate.X);
+ }
+
+ if (translateY.HasValue)
+ {
+ Assert.Equal(translateY.Value, composed.Translate.Y);
+ }
+ }
}
}