Browse Source

Fix problems caused by missing transform data or invalid order of operations when interpolating matrices.

pull/6989/head
Dariusz Komosinski 5 years ago
parent
commit
7a44407666
  1. 22
      src/Avalonia.Visuals/Matrix.cs
  2. 10
      src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs
  3. 5
      src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs
  4. 72
      tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs

22
src/Avalonia.Visuals/Matrix.cs

@ -215,6 +215,28 @@ namespace Avalonia
return angle * 0.0174532925;
}
/// <summary>
/// Appends another matrix as post-multiplication operation.
/// Equivalent to this * value;
/// </summary>
/// <param name="value">A matrix.</param>
/// <returns>Post-multiplied matrix.</returns>
public Matrix Append(Matrix value)
{
return this * value;
}
/// <summary>
/// Prpends another matrix as pre-multiplication operation.
/// Equivalent to value * this;
/// </summary>
/// <param name="value">A matrix.</param>
/// <returns>Pre-multiplied matrix.</returns>
public Matrix Prepend(Matrix value)
{
return value * this;
}
/// <summary>
/// Calculates the determinant for this matrix.
/// </summary>

10
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)

5
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;
}
}

72
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);
}
}
}
}

Loading…
Cancel
Save