From 5c969c40b8ca21bffbaca43c1e5a82132d9605d8 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 25 Jan 2016 01:06:36 +0300 Subject: [PATCH] Implemented Polygon, Polyline, PolylineGeometry. Added unit-tests with data and demo code in TestApplication. --- samples/TestApplicationShared/MainWindow.cs | 37 +++++++++++-- src/Perspex.Controls/Perspex.Controls.csproj | 2 + src/Perspex.Controls/Shapes/Polygon.cs | 28 ++++++++++ src/Perspex.Controls/Shapes/Polyline.cs | 33 +++++++++++ .../Media/PolylineGeometry.cs | 50 +++++++++++++++++ .../Perspex.SceneGraph.csproj | 2 + .../Perspex.Cairo.RenderTests.csproj | 2 + .../Perspex.Direct2D1.RenderTests.csproj | 2 + .../Perspex.Skia.RenderTests.csproj | 2 + .../Shapes/PolygonTests.cs | 24 ++++++++ .../Shapes/PolylineTests.cs | 52 ++++++++++++++++++ .../Polygon/Polygon_1px_Stroke.expected.png | Bin 0 -> 3969 bytes .../Polyline/Polyline_1px_Stroke.expected.png | Bin 0 -> 1604 bytes .../Polygon/Polygon_1px_Stroke.expected.png | Bin 0 -> 3969 bytes .../Polyline/Polyline_1px_Stroke.expected.png | Bin 0 -> 1604 bytes .../Polygon/Polygon_1px_Stroke.expected.png | Bin 0 -> 3969 bytes .../Polyline/Polyline_1px_Stroke.expected.png | Bin 0 -> 1604 bytes 17 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 src/Perspex.Controls/Shapes/Polygon.cs create mode 100644 src/Perspex.Controls/Shapes/Polyline.cs create mode 100644 src/Perspex.SceneGraph/Media/PolylineGeometry.cs create mode 100644 tests/Perspex.RenderTests/Shapes/PolygonTests.cs create mode 100644 tests/Perspex.RenderTests/Shapes/PolylineTests.cs create mode 100644 tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png create mode 100644 tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png create mode 100644 tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png create mode 100644 tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png create mode 100644 tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png create mode 100644 tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png 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 0000000000000000000000000000000000000000..2989e4bac47b430a77f1b29006510aa2e7155eb6 GIT binary patch literal 3969 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 Gi1;7bHjujj literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7cbe87fa6c7972e51836270cc2c0ed38736e2e78 GIT binary patch literal 1604 zcmZuxe>l^59RF@@+1XVRYVv#N$62>bN-VRR3FB&po6$`%`4JJqx}25HFwA+hO^@Wq zMt)SAQ@L=fFi{A{q*Dsfa8f!CH}{?Euj@X~^ZC3#ulMWqetp01_w#(e+#}v@8?+6z z0RY(GewaiB;{>=li1pw}-BkY(43INaHzH8?#ApfxurL=d7XY}Gi(CnY0|0#1o#aAG zIyUp%ImXDS zaM*%#f#Hr@6!^p{S6)s$o>?+3=P=10_R?f}v$;uV>LKHeLJ`+oz|ZGJ6;8U*kE*hM z{Nw0tK7xbVrBq~gX8EtbAW;dY))X~}$@wsua+FS2w-*ZyIH+o=(FUyx=q20k1yB^e zCQ@BWD!@D5472`;Nj|(^8kTzt%4Ve?SQnpcr>Em_c-R@Q#psbFpQd>4{nCX#BQ`4l zF=43UwV##oc+1mou0)3n#OB~}yI^PV%@~;~=)-W62fF%Jm^htz&{{mcV@!$>57sUj ze>5=E7-C?3O*crVoAm}~+y-y79BLVV5{G8A77I6Wrt(CEMS~3uiLGTSd8B`xBEPL_ zoSX<@vuqJ!MqI%8g&BOS$d~-*3oBB5dpSpCdi>lDx9)9J}e*9E*U2K1$R@l zR+I3a zOpQUdsXQutpuRuQfM5~zm{)VZ80E}S9(x65^a<9yD1A0-1i?az`>1igfY45g9Kw~6 zvCsC33iGR>ihBOQ5bhNji&0WMJA5G_BZ}QBVJup(m@TQ8CYExYirn{!2S<(C!3i(vtnY8sBQ)?- z(8aEun<=w%MU{wWWAm;wV$bOxvdrvFv^^Fx{`% z-H_UCS)ktBWx@^WRc|2}8!C7A;eS4IDUGQqw={8?aYi%RU(5+F+8V)RZVgv~II#2T zMG6sRz)|9gpp`RWA#d$wK;8)^3mY?SiG{o_<=5)Y{r$Aqedhxbmd)}3?w$()PI)o5wS?GO* zG`q}GailB0_g3*lls#uE%^mW(kw1{QaZ{>!!ArEjM4(3iKSfeHGlfY59HK)uZ{+ozWIaA6w2nmhkGE)|*noAlpr6KTNZLu%X%#eN()+dY5 zFg)gZ!d9@j2f+g3KP6t=14{YnMC`f}jnoYLt|jgA_tP1kIb0Niu5m#N((>SWcmCX# zg@fUAq#89DyYA@gB?!;Pj)TgGWH7Ysq@Vv(|3A}X)2Y@fkcfsiRZS>05_eZ`QXP@W F`3HG3#Bu-t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2989e4bac47b430a77f1b29006510aa2e7155eb6 GIT binary patch literal 3969 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 Gi1;7bHjujj literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7cbe87fa6c7972e51836270cc2c0ed38736e2e78 GIT binary patch literal 1604 zcmZuxe>l^59RF@@+1XVRYVv#N$62>bN-VRR3FB&po6$`%`4JJqx}25HFwA+hO^@Wq zMt)SAQ@L=fFi{A{q*Dsfa8f!CH}{?Euj@X~^ZC3#ulMWqetp01_w#(e+#}v@8?+6z z0RY(GewaiB;{>=li1pw}-BkY(43INaHzH8?#ApfxurL=d7XY}Gi(CnY0|0#1o#aAG zIyUp%ImXDS zaM*%#f#Hr@6!^p{S6)s$o>?+3=P=10_R?f}v$;uV>LKHeLJ`+oz|ZGJ6;8U*kE*hM z{Nw0tK7xbVrBq~gX8EtbAW;dY))X~}$@wsua+FS2w-*ZyIH+o=(FUyx=q20k1yB^e zCQ@BWD!@D5472`;Nj|(^8kTzt%4Ve?SQnpcr>Em_c-R@Q#psbFpQd>4{nCX#BQ`4l zF=43UwV##oc+1mou0)3n#OB~}yI^PV%@~;~=)-W62fF%Jm^htz&{{mcV@!$>57sUj ze>5=E7-C?3O*crVoAm}~+y-y79BLVV5{G8A77I6Wrt(CEMS~3uiLGTSd8B`xBEPL_ zoSX<@vuqJ!MqI%8g&BOS$d~-*3oBB5dpSpCdi>lDx9)9J}e*9E*U2K1$R@l zR+I3a zOpQUdsXQutpuRuQfM5~zm{)VZ80E}S9(x65^a<9yD1A0-1i?az`>1igfY45g9Kw~6 zvCsC33iGR>ihBOQ5bhNji&0WMJA5G_BZ}QBVJup(m@TQ8CYExYirn{!2S<(C!3i(vtnY8sBQ)?- z(8aEun<=w%MU{wWWAm;wV$bOxvdrvFv^^Fx{`% z-H_UCS)ktBWx@^WRc|2}8!C7A;eS4IDUGQqw={8?aYi%RU(5+F+8V)RZVgv~II#2T zMG6sRz)|9gpp`RWA#d$wK;8)^3mY?SiG{o_<=5)Y{r$Aqedhxbmd)}3?w$()PI)o5wS?GO* zG`q}GailB0_g3*lls#uE%^mW(kw1{QaZ{>!!ArEjM4(3iKSfeHGlfY59HK)uZ{+ozWIaA6w2nmhkGE)|*noAlpr6KTNZLu%X%#eN()+dY5 zFg)gZ!d9@j2f+g3KP6t=14{YnMC`f}jnoYLt|jgA_tP1kIb0Niu5m#N((>SWcmCX# zg@fUAq#89DyYA@gB?!;Pj)TgGWH7Ysq@Vv(|3A}X)2Y@fkcfsiRZS>05_eZ`QXP@W F`3HG3#Bu-t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2989e4bac47b430a77f1b29006510aa2e7155eb6 GIT binary patch literal 3969 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 Gi1;7bHjujj literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7cbe87fa6c7972e51836270cc2c0ed38736e2e78 GIT binary patch literal 1604 zcmZuxe>l^59RF@@+1XVRYVv#N$62>bN-VRR3FB&po6$`%`4JJqx}25HFwA+hO^@Wq zMt)SAQ@L=fFi{A{q*Dsfa8f!CH}{?Euj@X~^ZC3#ulMWqetp01_w#(e+#}v@8?+6z z0RY(GewaiB;{>=li1pw}-BkY(43INaHzH8?#ApfxurL=d7XY}Gi(CnY0|0#1o#aAG zIyUp%ImXDS zaM*%#f#Hr@6!^p{S6)s$o>?+3=P=10_R?f}v$;uV>LKHeLJ`+oz|ZGJ6;8U*kE*hM z{Nw0tK7xbVrBq~gX8EtbAW;dY))X~}$@wsua+FS2w-*ZyIH+o=(FUyx=q20k1yB^e zCQ@BWD!@D5472`;Nj|(^8kTzt%4Ve?SQnpcr>Em_c-R@Q#psbFpQd>4{nCX#BQ`4l zF=43UwV##oc+1mou0)3n#OB~}yI^PV%@~;~=)-W62fF%Jm^htz&{{mcV@!$>57sUj ze>5=E7-C?3O*crVoAm}~+y-y79BLVV5{G8A77I6Wrt(CEMS~3uiLGTSd8B`xBEPL_ zoSX<@vuqJ!MqI%8g&BOS$d~-*3oBB5dpSpCdi>lDx9)9J}e*9E*U2K1$R@l zR+I3a zOpQUdsXQutpuRuQfM5~zm{)VZ80E}S9(x65^a<9yD1A0-1i?az`>1igfY45g9Kw~6 zvCsC33iGR>ihBOQ5bhNji&0WMJA5G_BZ}QBVJup(m@TQ8CYExYirn{!2S<(C!3i(vtnY8sBQ)?- z(8aEun<=w%MU{wWWAm;wV$bOxvdrvFv^^Fx{`% z-H_UCS)ktBWx@^WRc|2}8!C7A;eS4IDUGQqw={8?aYi%RU(5+F+8V)RZVgv~II#2T zMG6sRz)|9gpp`RWA#d$wK;8)^3mY?SiG{o_<=5)Y{r$Aqedhxbmd)}3?w$()PI)o5wS?GO* zG`q}GailB0_g3*lls#uE%^mW(kw1{QaZ{>!!ArEjM4(3iKSfeHGlfY59HK)uZ{+ozWIaA6w2nmhkGE)|*noAlpr6KTNZLu%X%#eN()+dY5 zFg)gZ!d9@j2f+g3KP6t=14{YnMC`f}jnoYLt|jgA_tP1kIb0Niu5m#N((>SWcmCX# zg@fUAq#89DyYA@gB?!;Pj)TgGWH7Ysq@Vv(|3A}X)2Y@fkcfsiRZS>05_eZ`QXP@W F`3HG3#Bu-t literal 0 HcmV?d00001