Pārlūkot izejas kodu

Merge pull request #1446 from jmacato/master

SkewTransform Implementation
Steven Kirk 7 gadi atpakaļ
vecāks
revīzija
07fbb43544

+ 13 - 1
src/Avalonia.Visuals/Matrix.cs

@@ -150,6 +150,19 @@ namespace Avalonia
             return new Matrix(cos, sin, -sin, cos, 0, 0);
         }
 
+        /// <summary>
+        /// Creates a skew matrix from the given axis skew angles in radians.
+        /// </summary>
+        /// <param name="xAngle">The amount of skew along the X-axis, in radians.</param>
+        /// <param name="yAngle">The amount of skew along the Y-axis, in radians.</param>
+        /// <returns>A rotation matrix.</returns>
+        public static Matrix CreateSkew(double xAngle, double yAngle)
+        {
+            double tanX = Math.Tan(xAngle);
+            double tanY = Math.Tan(yAngle);
+            return new Matrix(1.0, tanY, tanX, 1.0, 0.0, 0.0);
+        }
+
         /// <summary>
         /// Creates a scale matrix from the given X and Y components.
         /// </summary>
@@ -215,7 +228,6 @@ namespace Avalonia
             return (_m11 * _m22) - (_m12 * _m21);
         }
 
-
         /// <summary>
         /// Returns a boolean indicating whether the matrix is equal to the other given matrix.
         /// </summary>

+ 69 - 0
src/Avalonia.Visuals/Media/SkewTransform.cs

@@ -0,0 +1,69 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// Skews an <see cref="IVisual"/>.
+    /// </summary>
+    public class SkewTransform : Transform
+    {
+        /// <summary>
+        /// Defines the <see cref="AngleX"/> property.
+        /// </summary>
+        public static readonly StyledProperty<double> AngleXProperty =
+                    AvaloniaProperty.Register<SkewTransform, double>(nameof(AngleX));
+
+        /// <summary>
+        /// Defines the <see cref="AngleY"/> property.
+        /// </summary>
+        public static readonly StyledProperty<double> AngleYProperty =
+                    AvaloniaProperty.Register<SkewTransform, double>(nameof(AngleY));
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SkewTransform"/> class.
+        /// </summary>
+        public SkewTransform()
+        {
+            this.GetObservable(AngleXProperty).Subscribe(_ => RaiseChanged());
+            this.GetObservable(AngleYProperty).Subscribe(_ => RaiseChanged());
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SkewTransform"/> class.
+        /// </summary>
+        /// <param name="angleX">The skew angle of X-axis, in degrees.</param>
+        /// <param name="angleY">The skew angle of Y-axis, in degrees.</param>
+        public SkewTransform(double angleX, double angleY) : this()
+        {
+            AngleX = angleX;
+            AngleY = angleY;
+        }
+
+        /// <summary>
+        /// Gets or sets the AngleX property.
+        /// </summary>
+        public double AngleX
+        {
+            get { return GetValue(AngleXProperty); }
+            set { SetValue(AngleXProperty, value); }
+        }
+
+        /// <summary>
+        /// Gets or sets the AngleY property.
+        /// </summary>
+        public double AngleY
+        {
+            get { return GetValue(AngleYProperty); }
+            set { SetValue(AngleYProperty, value); }
+        }
+
+        /// <summary>
+        /// Gets the tranform's <see cref="Matrix"/>.
+        /// </summary>
+        public override Matrix Value => Matrix.CreateSkew(Matrix.ToRadians(AngleX), Matrix.ToRadians(AngleY));
+    }
+}

+ 92 - 2
tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs

@@ -30,6 +30,52 @@ namespace Avalonia.Controls.UnitTests
                 new Size(50, 25));
         }
 
+        [Fact]
+        public void Measure_On_Skew_X_axis_45_degrees_Is_Correct()
+        {
+            TransformMeasureSizeTest(
+                new Size(100, 100),
+                new SkewTransform() { AngleX = 45 },
+                new Size(200, 100));
+
+        }
+
+        [Fact]
+        public void Measure_On_Skew_Y_axis_45_degrees_Is_Correct()
+        {
+            TransformMeasureSizeTest(
+                new Size(100, 100),
+                new SkewTransform() { AngleY = 45 },
+                new Size(100, 200));
+        }
+
+        [Fact]
+        public void Measure_On_Skew_X_axis_minus_45_degrees_Is_Correct()
+        {
+            TransformMeasureSizeTest(
+                new Size(100, 100),
+                new SkewTransform() { AngleX = -45 },
+                new Size(200, 100));
+        }
+
+        [Fact]
+        public void Measure_On_Skew_Y_axis_minus_45_degrees_Is_Correct()
+        {
+            TransformMeasureSizeTest(
+                new Size(100, 100),
+                new SkewTransform() { AngleY = -45 },
+                new Size(100, 200));
+        }
+
+        [Fact]
+        public void Measure_On_Skew_0_degrees_Is_Correct()
+        {
+            TransformMeasureSizeTest(
+                new Size(100, 100),
+                new SkewTransform() { AngleX = 0, AngleY = 0 },
+                new Size(100, 100));
+        }
+
         [Fact]
         public void Measure_On_Rotate_90_degrees_Is_Correct()
         {
@@ -125,7 +171,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Should_Generate_RenderTransform_90_degrees()
+        public void Should_Generate_RotateTransform_90_degrees()
         {
             LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
                                         100,
@@ -147,7 +193,7 @@ namespace Avalonia.Controls.UnitTests
         }
 
         [Fact]
-        public void Should_Generate_RenderTransform_minus_90_degrees()
+        public void Should_Generate_RotateTransform_minus_90_degrees()
         {
             LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
                                         100,
@@ -189,6 +235,50 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(m.M32, res.M32, 3);
         }
 
+        [Fact]
+        public void Should_Generate_SkewTransform_45_degrees()
+        {
+            LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
+                                        100,
+                                        100,
+                                        new SkewTransform() { AngleX = 45, AngleY = 45 });
+
+            Assert.NotNull(lt.TransformRoot.RenderTransform);
+
+            Matrix m = lt.TransformRoot.RenderTransform.Value;
+
+            Matrix res = Matrix.CreateSkew(Matrix.ToRadians(45), Matrix.ToRadians(45));
+
+            Assert.Equal(m.M11, res.M11, 3);
+            Assert.Equal(m.M12, res.M12, 3);
+            Assert.Equal(m.M21, res.M21, 3);
+            Assert.Equal(m.M22, res.M22, 3);
+            Assert.Equal(m.M31, res.M31, 3);
+            Assert.Equal(m.M32, res.M32, 3);
+        }
+
+        [Fact]
+        public void Should_Generate_SkewTransform_minus_45_degrees()
+        {
+            LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
+                                        100,
+                                        100,
+                                        new SkewTransform() { AngleX = -45, AngleY = -45 });
+
+            Assert.NotNull(lt.TransformRoot.RenderTransform);
+
+            Matrix m = lt.TransformRoot.RenderTransform.Value;
+
+            Matrix res = Matrix.CreateSkew(Matrix.ToRadians(-45), Matrix.ToRadians(-45));
+
+            Assert.Equal(m.M11, res.M11, 3);
+            Assert.Equal(m.M12, res.M12, 3);
+            Assert.Equal(m.M21, res.M21, 3);
+            Assert.Equal(m.M22, res.M22, 3);
+            Assert.Equal(m.M31, res.M31, 3);
+            Assert.Equal(m.M32, res.M32, 3);
+        }
+
         private static void TransformMeasureSizeTest(Size size, Transform transform, Size expectedSize)
         {
             LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(