diff --git a/src/ImageSharp/Drawing/Shapes/ComplexPolygon.cs b/src/ImageSharp/Drawing/Shapes/ComplexPolygon.cs index 7bd459f6a..32f5678a7 100644 --- a/src/ImageSharp/Drawing/Shapes/ComplexPolygon.cs +++ b/src/ImageSharp/Drawing/Shapes/ComplexPolygon.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Drawing.Shapes { using System.Collections; using System.Collections.Generic; + using System.Linq; using System.Numerics; using Paths; @@ -19,8 +20,7 @@ namespace ImageSharp.Drawing.Shapes public sealed class ComplexPolygon : IShape { private const float ClipperScaleFactor = 100f; - private IShape[] holes; - private IShape[] outlines; + private IShape[] shapes; private IEnumerable paths; /// @@ -41,14 +41,14 @@ namespace ImageSharp.Drawing.Shapes public ComplexPolygon(IShape[] outlines, IShape[] holes) { Guard.NotNull(outlines, nameof(outlines)); - Guard.MustBeGreaterThanOrEqualTo(outlines.Count(), 1, nameof(outlines)); + Guard.MustBeGreaterThanOrEqualTo(outlines.Length, 1, nameof(outlines)); this.FixAndSetShapes(outlines, holes); - var minX = outlines.Min(x => x.Bounds.Left); - var maxX = outlines.Max(x => x.Bounds.Right); - var minY = outlines.Min(x => x.Bounds.Top); - var maxY = outlines.Max(x => x.Bounds.Bottom); + var minX = this.shapes.Min(x => x.Bounds.Left); + var maxX = this.shapes.Max(x => x.Bounds.Right); + var minY = this.shapes.Min(x => x.Bounds.Top); + var maxY = this.shapes.Max(x => x.Bounds.Bottom); this.Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY); } @@ -68,30 +68,36 @@ namespace ImageSharp.Drawing.Shapes /// /// Returns the distance from thr shape to the point /// + /// + /// Due to the clipping we did during construction we know that out shapes do not overlap at there edges + /// therefore for apoint to be in more that one we must be in a hole of another, theoretically this could + /// then flip again to be in a outlin inside a hole inside an outline :) + /// float IShape.Distance(Vector2 point) { - // get the outline we are closest to the center of - // by rights we should only be inside 1 outline - // othersie we will start returning the distanct to the nearest shape - var dist = this.outlines.Select(o => o.Distance(point)).OrderBy(p => p).First(); - - if (dist <= 0 && this.holes != null) + float dist = float.MaxValue; + bool inside = false; + foreach (IShape shape in this.shapes) { - // inside poly - foreach (var hole in this.holes) + var d = shape.Distance(point); + + if(d <= 0) { - var distFromHole = hole.Distance(point); + // we are inside a poly + d = -d; // flip the sign + inside ^= true; // flip the inside flag + } - // less than zero we are inside shape - if (distFromHole <= 0) - { - // invert distance - dist = distFromHole * -1; - break; - } + if(d < dist) + { + dist = d; } } + if (inside) { + return -dist; + } + return dist; } @@ -149,7 +155,7 @@ namespace ImageSharp.Drawing.Shapes } - private void ExtractOutlines(ClipperLib.PolyNode tree, List outlines, List holes) + private void ExtractOutlines(ClipperLib.PolyNode tree, List shapes) { if (tree.Contour.Any()) { @@ -164,19 +170,12 @@ namespace ImageSharp.Drawing.Shapes var polygon = new Polygon(new LinearLineSegment(vectors)); - if (tree.IsHole) - { - holes.Add(polygon); - } - else - { - outlines.Add(polygon); - } + shapes.Add(polygon); } foreach (var c in tree.Childs) { - this.ExtractOutlines(c, outlines, holes); + this.ExtractOutlines(c, shapes); } } @@ -198,7 +197,7 @@ namespace ImageSharp.Drawing.Shapes // as sending then though clipper will turn them into generic polygons and loose thier shape specific optimisations int outlineLength = outlines.Length; - int holesLength = holes.Length; + int holesLength = holes?.Length ?? 0; bool[] overlappingOutlines = new bool[outlineLength]; bool[] overlappingHoles = new bool[holesLength]; bool anyOutlinesOverlapping = false; @@ -249,43 +248,32 @@ namespace ImageSharp.Drawing.Shapes var tree = new ClipperLib.PolyTree(); clipper.Execute(ClipperLib.ClipType.ctDifference, tree); - List newOutlines = new List(); - List newHoles = new List(); + List newShapes = new List(); // convert the 'tree' back to shapes - this.ExtractOutlines(tree, newOutlines, newHoles); + this.ExtractOutlines(tree, newShapes); // add the origional outlines that where not overlapping for (int i = 0; i < outlineLength - 1; i++) { if (!overlappingOutlines[i]) { - newOutlines.Add(outlines[i]); + newShapes.Add(outlines[i]); } } - this.outlines = newOutlines.ToArray(); - if (newHoles.Count > 0) - { - this.holes = newHoles.ToArray(); - } + this.shapes = newShapes.ToArray(); }else { - this.outlines = outlines; + this.shapes = outlines; } var paths = new List(); - foreach (var o in this.outlines) + foreach (var o in this.shapes) { paths.AddRange(o); } - if (this.holes != null) - { - foreach (var o in this.holes) - { - paths.AddRange(o); - } - } + this.paths = paths; } }