diff --git a/samples/TestApplicationShared/MainWindow.cs b/samples/TestApplicationShared/MainWindow.cs index 02edd23634..a0e0a2848e 100644 --- a/samples/TestApplicationShared/MainWindow.cs +++ b/samples/TestApplicationShared/MainWindow.cs @@ -542,6 +542,17 @@ namespace TestApplication private static TabItem LayoutTab() { + var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3), + new Point(9, 1), new Point(10, 0), new Point(15, 0) }; + var polygonPoints = new Point[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) }; + for (int i = 0; i < polylinePoints.Length; i++) + { + polylinePoints[i] = polylinePoints[i] * 13; + } + for (int i = 0; i < polygonPoints.Length; i++) + { + polygonPoints[i] = polygonPoints[i] * 15; + } return new TabItem { Header = "Layout", @@ -690,13 +701,31 @@ namespace TestApplication }, new Line { - Width = 90, - Height = 70, Stroke = Brushes.Red, StrokeThickness = 2, + PointPair = new PointPair(120, 185, 30, 115) + }, + new Perspex.Controls.Shapes.Path + { + Fill = Brushes.Orange, + Data = StreamGeometry.Parse("M 30,250 c 50,0 50,-50 c 50,0 50,50 h -50 v 50 l -50,-50 Z"), + }, + new Polygon + { + Stroke = Brushes.DarkBlue, + Fill = Brushes.Violet, + Points = polygonPoints, + StrokeThickness = 1, + [Canvas.LeftProperty] = 150, + [Canvas.TopProperty] = 180, + }, + new Polyline + { + Stroke = Brushes.Brown, + Points = polylinePoints, [Canvas.LeftProperty] = 30, - [Canvas.TopProperty] = 120 - } + [Canvas.TopProperty] = 350, + }, } }, } diff --git a/src/Perspex.Controls/Perspex.Controls.csproj b/src/Perspex.Controls/Perspex.Controls.csproj index 394fdccd2d..e02af26c3f 100644 --- a/src/Perspex.Controls/Perspex.Controls.csproj +++ b/src/Perspex.Controls/Perspex.Controls.csproj @@ -48,6 +48,8 @@ + + diff --git a/src/Perspex.Controls/Shapes/Polygon.cs b/src/Perspex.Controls/Shapes/Polygon.cs new file mode 100644 index 0000000000..0e6d846d90 --- /dev/null +++ b/src/Perspex.Controls/Shapes/Polygon.cs @@ -0,0 +1,28 @@ +using Perspex.Media; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Controls.Shapes +{ + public class Polygon : Shape + { + public static readonly PerspexProperty> PointsProperty = + PerspexProperty.Register>("Points"); + + public IList Points + { + get { return GetValue(PointsProperty); } + set { SetValue(PointsProperty, value); } + } + + public override Geometry DefiningGeometry => new PolylineGeometry(Points, true); + + protected override Size MeasureOverride(Size availableSize) + { + return new Size(StrokeThickness, StrokeThickness); + } + } +} diff --git a/src/Perspex.Controls/Shapes/Polyline.cs b/src/Perspex.Controls/Shapes/Polyline.cs new file mode 100644 index 0000000000..8e27535dc5 --- /dev/null +++ b/src/Perspex.Controls/Shapes/Polyline.cs @@ -0,0 +1,33 @@ +using Perspex.Media; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Controls.Shapes +{ + public class Polyline: Shape + { + public static readonly PerspexProperty> PointsProperty = + PerspexProperty.Register>("Points"); + + public Polyline() + { + StrokeThickness = 1; // Default Thickness + } + + public IList Points + { + get { return GetValue(PointsProperty); } + set { SetValue(PointsProperty, value); } + } + + public override Geometry DefiningGeometry => new PolylineGeometry(Points, false); + + protected override Size MeasureOverride(Size availableSize) + { + return new Size(StrokeThickness, StrokeThickness); + } + } +} diff --git a/src/Perspex.SceneGraph/Media/PolylineGeometry.cs b/src/Perspex.SceneGraph/Media/PolylineGeometry.cs new file mode 100644 index 0000000000..244f027339 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/PolylineGeometry.cs @@ -0,0 +1,50 @@ +using Perspex.Platform; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Media +{ + /// + /// Represents the geometry of an polyline or polygon. + /// + public class PolylineGeometry : Geometry + { + private IList _points; + private bool _isFilled; + + public PolylineGeometry(IList points, bool isFilled) + { + _points = points; + _isFilled = isFilled; + IPlatformRenderInterface factory = PerspexLocator.Current.GetService(); + IStreamGeometryImpl impl = factory.CreateStreamGeometry(); + + using (IStreamGeometryContextImpl context = impl.Open()) + { + if (points.Count > 0) + { + context.BeginFigure(points[0], isFilled); + for (int i = 1; i < points.Count; i++) + { + context.LineTo(points[i]); + } + context.EndFigure(isFilled); + } + } + + PlatformImpl = impl; + } + + /// + public override Rect Bounds => PlatformImpl.Bounds; + + /// + public override Geometry Clone() + { + return new PolylineGeometry(new List(_points), _isFilled); + } + } +} diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj index ac08406268..b1e42b4ea9 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -71,6 +71,7 @@ + @@ -107,6 +108,7 @@ + diff --git a/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj index dd6953d355..18d7aeffcf 100644 --- a/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj @@ -74,6 +74,8 @@ + + diff --git a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj index 2abf92fdb5..a922029365 100644 --- a/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj @@ -79,6 +79,8 @@ + + diff --git a/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj b/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj index 08bba41fce..e82de9eff2 100644 --- a/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj +++ b/tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj @@ -70,6 +70,8 @@ + + diff --git a/tests/Perspex.RenderTests/Shapes/PolygonTests.cs b/tests/Perspex.RenderTests/Shapes/PolygonTests.cs new file mode 100644 index 0000000000..8e9c2cbb6e --- /dev/null +++ b/tests/Perspex.RenderTests/Shapes/PolygonTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Perspex.Controls; +using Perspex.Controls.Shapes; +using Perspex.Media; +using Xunit; + +#if PERSPEX_CAIRO +namespace Perspex.Cairo.RenderTests.Shapes +#elif PERSPEX_SKIA +namespace Perspex.Skia.RenderTests +#else +namespace Perspex.Direct2D1.RenderTests.Shapes +#endif +{ + public class PolygonTests : TestBase + { + public PolygonTests() + : base(@"Shapes\Polygon") + { + } + } +} diff --git a/tests/Perspex.RenderTests/Shapes/PolylineTests.cs b/tests/Perspex.RenderTests/Shapes/PolylineTests.cs new file mode 100644 index 0000000000..79e45ace28 --- /dev/null +++ b/tests/Perspex.RenderTests/Shapes/PolylineTests.cs @@ -0,0 +1,52 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Perspex.Controls; +using Perspex.Controls.Shapes; +using Perspex.Media; +using Xunit; + +#if PERSPEX_CAIRO +namespace Perspex.Cairo.RenderTests.Shapes +#elif PERSPEX_SKIA +namespace Perspex.Skia.RenderTests +#else +namespace Perspex.Direct2D1.RenderTests.Shapes +#endif +{ + public class PolylineTests : TestBase + { + public PolylineTests() + : base(@"Shapes\Polyline") + { + } + + [Fact] + public void Polyline_1px_Stroke() + { + var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3), + new Point(9, 1), new Point(10, 0), new Point(15, 0) }; + var offsetPoint = new Point(0, 50); + for (int i = 0; i < polylinePoints.Length; i++) + { + polylinePoints[i] = polylinePoints[i] * 13 + offsetPoint; + } + + Decorator target = new Decorator + { + Padding = new Thickness(8), + Width = 230, + Height = 130, + Child = new Polyline + { + Stroke = Brushes.Brown, + Points = polylinePoints, + StrokeThickness = 1 + } + }; + + RenderToFile(target); + CompareImages(); + } + } +} diff --git a/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png b/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png new file mode 100644 index 0000000000..2989e4bac4 Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png b/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png new file mode 100644 index 0000000000..7cbe87fa6c Binary files /dev/null and b/tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png b/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png new file mode 100644 index 0000000000..2989e4bac4 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png b/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png new file mode 100644 index 0000000000..7cbe87fa6c Binary files /dev/null and b/tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png new file mode 100644 index 0000000000..2989e4bac4 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png new file mode 100644 index 0000000000..7cbe87fa6c Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png differ