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