From 7d253d81ed5d087b12df2f29ae2ea6ef4e98e151 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 29 Jan 2017 15:31:15 +0000 Subject: [PATCH] migrate to third party shapes library --- NuGet.config | 1 + src/ImageSharp.Drawing/Draw.cs | 3 +- src/ImageSharp.Drawing/DrawRectangle.cs | 6 +- src/ImageSharp.Drawing/Fill.cs | 4 +- src/ImageSharp.Drawing/FillRectangle.cs | 5 +- .../Paths/BezierLineSegment.cs | 119 - src/ImageSharp.Drawing/Paths/ILineSegment.cs | 21 - src/ImageSharp.Drawing/Paths/IPath.cs | 48 - src/ImageSharp.Drawing/Paths/InternalPath.cs | 516 --- .../Paths/LinearLineSegment.cs | 55 - src/ImageSharp.Drawing/Paths/Path.cs | 51 - src/ImageSharp.Drawing/Pens/Pen{TColor}.cs | 1 - .../Pens/Processors/PenApplicator.cs | 1 - .../{Paths => Pens/Processors}/PointInfo.cs | 18 +- .../Processors/DrawPathProcessor.cs | 30 +- .../Processors/FillShapeProcessor.cs | 4 +- .../Processors/PointInfoExtensions.cs | 37 + .../Processors/RectangleExtensions.cs | 32 + .../Shapes/BezierPolygon.cs | 93 - .../Shapes/ComplexPolygon.cs | 246 -- src/ImageSharp.Drawing/Shapes/IShape.cs | 56 - .../Shapes/LinearPolygon.cs | 93 - src/ImageSharp.Drawing/Shapes/Polygon.cs | 156 - .../Shapes/PolygonClipper/Clipper.cs | 3860 ----------------- .../Shapes/PolygonClipper/ClipperException.cs | 31 - .../Shapes/PolygonClipper/Direction.cs | 31 - .../Shapes/PolygonClipper/EdgeSide.cs | 31 - .../Shapes/PolygonClipper/IntersectNode.cs | 38 - .../PolygonClipper/IntersectNodeSort.cs | 48 - .../Shapes/PolygonClipper/Join.cs | 38 - .../Shapes/PolygonClipper/LocalMinima.cs | 44 - .../Shapes/PolygonClipper/Maxima.cs | 38 - .../Shapes/PolygonClipper/OutPt.cs | 43 - .../Shapes/PolygonClipper/OutRec.cs | 64 - .../Shapes/PolygonClipper/PolyNode.cs | 179 - .../Shapes/PolygonClipper/PolyTree.cs | 81 - .../Shapes/PolygonClipper/PolyType.cs | 31 - .../Shapes/PolygonClipper/README.md | 40 - .../Shapes/PolygonClipper/Scanbeam.cs | 33 - .../Shapes/PolygonClipper/TEdge.cs | 118 - .../Shapes/RectangularPolygon.cs | 281 -- src/ImageSharp.Drawing/project.json | 1 + .../Drawing/FillRectangle.cs | 10 +- 43 files changed, 108 insertions(+), 6528 deletions(-) delete mode 100644 src/ImageSharp.Drawing/Paths/BezierLineSegment.cs delete mode 100644 src/ImageSharp.Drawing/Paths/ILineSegment.cs delete mode 100644 src/ImageSharp.Drawing/Paths/IPath.cs delete mode 100644 src/ImageSharp.Drawing/Paths/InternalPath.cs delete mode 100644 src/ImageSharp.Drawing/Paths/LinearLineSegment.cs delete mode 100644 src/ImageSharp.Drawing/Paths/Path.cs rename src/ImageSharp.Drawing/{Paths => Pens/Processors}/PointInfo.cs (54%) create mode 100644 src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs create mode 100644 src/ImageSharp.Drawing/Processors/RectangleExtensions.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/BezierPolygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/IShape.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/LinearPolygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/Polygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs diff --git a/NuGet.config b/NuGet.config index b2c967cc9..88d71e7ba 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,6 +1,7 @@  + diff --git a/src/ImageSharp.Drawing/Draw.cs b/src/ImageSharp.Drawing/Draw.cs index c10665b83..8c3dbb1b5 100644 --- a/src/ImageSharp.Drawing/Draw.cs +++ b/src/ImageSharp.Drawing/Draw.cs @@ -9,10 +9,9 @@ namespace ImageSharp using System.Numerics; using Drawing; using Drawing.Brushes; - using Drawing.Paths; using Drawing.Pens; using Drawing.Processors; - using Drawing.Shapes; + using SixLabors.Shapes; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing/DrawRectangle.cs b/src/ImageSharp.Drawing/DrawRectangle.cs index 38ed578b6..17d6d5f36 100644 --- a/src/ImageSharp.Drawing/DrawRectangle.cs +++ b/src/ImageSharp.Drawing/DrawRectangle.cs @@ -9,10 +9,10 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; - using Drawing.Paths; using Drawing.Pens; using Drawing.Processors; - using Drawing.Shapes; + + using SixLabors.Shapes; /// /// Extension methods for the type. @@ -33,7 +33,7 @@ namespace ImageSharp public static Image DrawPolygon(this Image source, IPen pen, RectangleF shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new DrawPathProcessor(pen, (IPath)new RectangularPolygon(shape), options)); + return source.Apply(new DrawPathProcessor(pen, (IPath)new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options)); } /// diff --git a/src/ImageSharp.Drawing/Fill.cs b/src/ImageSharp.Drawing/Fill.cs index c0f43bdd1..637950e76 100644 --- a/src/ImageSharp.Drawing/Fill.cs +++ b/src/ImageSharp.Drawing/Fill.cs @@ -9,9 +9,9 @@ namespace ImageSharp using System.Numerics; using Drawing; using Drawing.Brushes; - using Drawing.Paths; using Drawing.Processors; - using Drawing.Shapes; + + using SixLabors.Shapes; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing/FillRectangle.cs b/src/ImageSharp.Drawing/FillRectangle.cs index d29b58e1b..7749e7c92 100644 --- a/src/ImageSharp.Drawing/FillRectangle.cs +++ b/src/ImageSharp.Drawing/FillRectangle.cs @@ -10,7 +10,6 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Processors; - using Drawing.Shapes; /// /// Extension methods for the type. @@ -31,7 +30,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, RectangleF shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, new RectangularPolygon(shape), options)); + return source.Apply(new FillShapeProcessor(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options)); } /// @@ -45,7 +44,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, RectangleF shape) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, new RectangularPolygon(shape), GraphicsOptions.Default)); + return source.Apply(new FillShapeProcessor(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), GraphicsOptions.Default)); } /// diff --git a/src/ImageSharp.Drawing/Paths/BezierLineSegment.cs b/src/ImageSharp.Drawing/Paths/BezierLineSegment.cs deleted file mode 100644 index 647f97f1e..000000000 --- a/src/ImageSharp.Drawing/Paths/BezierLineSegment.cs +++ /dev/null @@ -1,119 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// Represents a line segment that conistst of control points that will be rendered as a cubic bezier curve - /// - /// - public class BezierLineSegment : ILineSegment - { - /// - /// The segments per curve. - /// code for this taken from - /// - private const int SegmentsPerCurve = 50; - - /// - /// The line points. - /// - private readonly Vector2[] linePoints; - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public BezierLineSegment(params Vector2[] points) - { - Guard.NotNull(points, nameof(points)); - Guard.MustBeGreaterThanOrEqualTo(points.Length, 4, nameof(points)); - - this.linePoints = this.GetDrawingPoints(points); - } - - /// - /// Returns the current a simple linear path. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.linePoints; - } - - /// - /// Returns the drawing points along the line. - /// - /// The control points. - /// - /// The . - /// - private Vector2[] GetDrawingPoints(Vector2[] controlPoints) - { - // TODO we need to calculate an optimal SegmentsPerCurve value - // depending on the calcualted length of this curve - int curveCount = (controlPoints.Length - 1) / 3; - int finalPointCount = (SegmentsPerCurve * curveCount) + 1; // we have SegmentsPerCurve for each curve plus the origon point; - - Vector2[] drawingPoints = new Vector2[finalPointCount]; - - int position = 0; - int targetPoint = controlPoints.Length - 3; - for (int i = 0; i < targetPoint; i += 3) - { - Vector2 p0 = controlPoints[i]; - Vector2 p1 = controlPoints[i + 1]; - Vector2 p2 = controlPoints[i + 2]; - Vector2 p3 = controlPoints[i + 3]; - - // only do this for the first end point. When i != 0, this coincides with the end point of the previous segment, - if (i == 0) - { - drawingPoints[position++] = this.CalculateBezierPoint(0, p0, p1, p2, p3); - } - - for (int j = 1; j <= SegmentsPerCurve; j++) - { - float t = j / (float)SegmentsPerCurve; - drawingPoints[position++] = this.CalculateBezierPoint(t, p0, p1, p2, p3); - } - } - - return drawingPoints; - } - - /// - /// Calculates the bezier point along the line. - /// - /// The position within the line. - /// The p 0. - /// The p 1. - /// The p 2. - /// The p 3. - /// - /// The . - /// - private Vector2 CalculateBezierPoint(float t, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) - { - float u = 1 - t; - float tt = t * t; - float uu = u * u; - float uuu = uu * u; - float ttt = tt * t; - - Vector2 p = uuu * p0; // first term - - p += 3 * uu * t * p1; // second term - p += 3 * u * tt * p2; // third term - p += ttt * p3; // fourth term - - return p; - } - } -} diff --git a/src/ImageSharp.Drawing/Paths/ILineSegment.cs b/src/ImageSharp.Drawing/Paths/ILineSegment.cs deleted file mode 100644 index 380f0bf40..000000000 --- a/src/ImageSharp.Drawing/Paths/ILineSegment.cs +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// Represents a simple path segment - /// - public interface ILineSegment - { - /// - /// Converts the into a simple linear path.. - /// - /// Returns the current as simple linear path. - Vector2[] AsSimpleLinearPath(); // TODO move this over to ReadonlySpan once available - } -} diff --git a/src/ImageSharp.Drawing/Paths/IPath.cs b/src/ImageSharp.Drawing/Paths/IPath.cs deleted file mode 100644 index 278d97251..000000000 --- a/src/ImageSharp.Drawing/Paths/IPath.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// Represents a logic path that can be drawn - /// - public interface IPath : ILineSegment - { - /// - /// Gets the bounds enclosing the path - /// - /// - /// The bounds. - /// - RectangleF Bounds { get; } - - /// - /// Gets a value indicating whether this instance is closed. - /// - /// - /// true if this instance is closed; otherwise, false. - /// - bool IsClosed { get; } - - /// - /// Gets the length of the path - /// - /// - /// The length. - /// - float Length { get; } - - /// - /// Calculates the distance along and away from the path for a specified point. - /// - /// The point along the path. - /// - /// Returns details about the point and its distance away from the path. - /// - PointInfo Distance(Vector2 point); - } -} diff --git a/src/ImageSharp.Drawing/Paths/InternalPath.cs b/src/ImageSharp.Drawing/Paths/InternalPath.cs deleted file mode 100644 index 36a0704cb..000000000 --- a/src/ImageSharp.Drawing/Paths/InternalPath.cs +++ /dev/null @@ -1,516 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Paths -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - - /// - /// Internal logic for integrating linear paths. - /// - internal class InternalPath - { - /// - /// The maximum vector - /// - private static readonly Vector2 MaxVector = new Vector2(float.MaxValue); - - /// - /// The locker. - /// - private static readonly object Locker = new object(); - - /// - /// The points. - /// - private readonly Vector2[] points; - - /// - /// The closed path. - /// - private readonly bool closedPath; - - /// - /// The total distance. - /// - private readonly Lazy totalDistance; - - /// - /// The constant. - /// - private float[] constant; - - /// - /// The multiples. - /// - private float[] multiple; - - /// - /// The distances. - /// - private float[] distance; - - /// - /// The calculated. - /// - private bool calculated = false; - - /// - /// Initializes a new instance of the class. - /// - /// The segments. - /// if set to true [is closed path]. - internal InternalPath(ILineSegment[] segments, bool isClosedPath) - : this(Simplify(segments), isClosedPath) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The segment. - /// if set to true [is closed path]. - internal InternalPath(ILineSegment segment, bool isClosedPath) - : this(segment.AsSimpleLinearPath(), isClosedPath) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The points. - /// if set to true [is closed path]. - internal InternalPath(Vector2[] points, bool isClosedPath) - { - this.points = points; - this.closedPath = isClosedPath; - - float minX = this.points.Min(x => x.X); - float maxX = this.points.Max(x => x.X); - float minY = this.points.Min(x => x.Y); - float maxY = this.points.Max(x => x.Y); - - this.Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY); - this.totalDistance = new Lazy(this.CalculateLength); - } - - /// - /// Gets the bounds. - /// - /// - /// The bounds. - /// - public RectangleF Bounds - { - get; - } - - /// - /// Gets the length. - /// - /// - /// The length. - /// - public float Length => this.totalDistance.Value; - - /// - /// Gets the points. - /// - /// - /// The points. - /// - internal Vector2[] Points => this.points; - - /// - /// Calculates the distance from the path. - /// - /// The point. - /// Returns the distance from the path - public PointInfo DistanceFromPath(Vector2 point) - { - this.CalculateConstants(); - - PointInfoInternal internalInfo = default(PointInfoInternal); - internalInfo.DistanceSquared = float.MaxValue; // Set it to max so that CalculateShorterDistance can reduce it back down - - int polyCorners = this.points.Length; - - if (!this.closedPath) - { - polyCorners -= 1; - } - - int closestPoint = 0; - for (int i = 0; i < polyCorners; i++) - { - int next = i + 1; - if (this.closedPath && next == polyCorners) - { - next = 0; - } - - if (this.CalculateShorterDistance(this.points[i], this.points[next], point, ref internalInfo)) - { - closestPoint = i; - } - } - - return new PointInfo - { - DistanceAlongPath = this.distance[closestPoint] + Vector2.Distance(this.points[closestPoint], point), - DistanceFromPath = (float)Math.Sqrt(internalInfo.DistanceSquared), - SearchPoint = point, - ClosestPointOnPath = internalInfo.PointOnLine - }; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on the path that the line intersects. - /// - /// The start. - /// The end. - /// The buffer. - /// The count. - /// The offset. - /// number iof intersections hit - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - int polyCorners = this.points.Length; - - if (!this.closedPath) - { - polyCorners -= 1; - } - - int position = 0; - for (int i = 0; i < polyCorners && count > 0; i++) - { - int next = i + 1; - if (this.closedPath && next == polyCorners) - { - next = 0; - } - - Vector2 point = FindIntersection(this.points[i], this.points[next], start, end); - if (point != MaxVector) - { - buffer[position + offset] = point; - position++; - count--; - } - } - - return position; - } - - /// - /// Determines if the specified point is inside or outside the path. - /// - /// The point. - /// Returns true if the point is inside the closed path. - public bool PointInPolygon(Vector2 point) - { - // You can only be inside a path if its "closed" - if (!this.closedPath) - { - return false; - } - - if (!this.Bounds.Contains(point.X, point.Y)) - { - return false; - } - - this.CalculateConstants(); - - Vector2[] poly = this.points; - int polyCorners = poly.Length; - - int j = polyCorners - 1; - bool oddNodes = false; - - for (int i = 0; i < polyCorners; i++) - { - if ((poly[i].Y < point.Y && poly[j].Y >= point.Y) - || (poly[j].Y < point.Y && poly[i].Y >= point.Y)) - { - oddNodes ^= (point.Y * this.multiple[i]) + this.constant[i] < point.X; - } - - j = i; - } - - return oddNodes; - } - - /// - /// Determins if the bounding box for 2 lines - /// described by and - /// and and overlap. - /// - /// The line1 start. - /// The line1 end. - /// The line2 start. - /// The line2 end. - /// Returns true it the bounding box of the 2 lines intersect - private static bool BoundingBoxesIntersect(Vector2 line1Start, Vector2 line1End, Vector2 line2Start, Vector2 line2End) - { - Vector2 topLeft1 = Vector2.Min(line1Start, line1End); - Vector2 bottomRight1 = Vector2.Max(line1Start, line1End); - - Vector2 topLeft2 = Vector2.Min(line2Start, line2End); - Vector2 bottomRight2 = Vector2.Max(line2Start, line2End); - - float left1 = topLeft1.X; - float right1 = bottomRight1.X; - float top1 = topLeft1.Y; - float bottom1 = bottomRight1.Y; - - float left2 = topLeft2.X; - float right2 = bottomRight2.X; - float top2 = topLeft2.Y; - float bottom2 = bottomRight2.Y; - - return left1 <= right2 && right1 >= left2 - && - top1 <= bottom2 && bottom1 >= top2; - } - - /// - /// Finds the point on line described by and - /// that intersects with line described by and - /// - /// The line1 start. - /// The line1 end. - /// The line2 start. - /// The line2 end. - /// - /// A describing the point that the 2 lines cross or if they do not. - /// - private static Vector2 FindIntersection(Vector2 line1Start, Vector2 line1End, Vector2 line2Start, Vector2 line2End) - { - // do bounding boxes overlap, if not then the lines can't and return fast. - if (!BoundingBoxesIntersect(line1Start, line1End, line2Start, line2End)) - { - return MaxVector; - } - - Vector2 line1Diff = line1End - line1Start; - Vector2 line2Diff = line2End - line2Start; - - Vector2 point; - if (line1Diff.X == 0) - { - float slope = line2Diff.Y / line2Diff.X; - float yinter = line2Start.Y - (slope * line2Start.X); - float y = (line1Start.X * slope) + yinter; - point = new Vector2(line1Start.X, y); - - // horizontal and vertical lines - } - else if (line2Diff.X == 0) - { - float slope = line1Diff.Y / line1Diff.X; - float yinter = line1Start.Y - (slope * line1Start.X); - float y = (line2Start.X * slope) + yinter; - point = new Vector2(line2Start.X, y); - - // horizontal and vertical lines - } - else - { - float slope1 = line1Diff.Y / line1Diff.X; - float slope2 = line2Diff.Y / line2Diff.X; - - float yinter1 = line1Start.Y - (slope1 * line1Start.X); - float yinter2 = line2Start.Y - (slope2 * line2Start.X); - - if (slope1 == slope2 && yinter1 != yinter2) - { - return MaxVector; - } - - float x = (yinter2 - yinter1) / (slope1 - slope2); - float y = (slope1 * x) + yinter1; - - point = new Vector2(x, y); - } - - if (BoundingBoxesIntersect(line1Start, line1End, point, point)) - { - return point; - } - else if (BoundingBoxesIntersect(line2Start, line2End, point, point)) - { - return point; - } - - return MaxVector; - } - - /// - /// Simplifies the collection of segments. - /// - /// The segments. - /// - /// The . - /// - private static Vector2[] Simplify(ILineSegment[] segments) - { - List simplified = new List(); - foreach (ILineSegment seg in segments) - { - simplified.AddRange(seg.AsSimpleLinearPath()); - } - - return simplified.ToArray(); - } - - /// - /// Returns the length of the path. - /// - /// - /// The . - /// - private float CalculateLength() - { - float length = 0; - int polyCorners = this.points.Length; - - if (!this.closedPath) - { - polyCorners -= 1; - } - - for (int i = 0; i < polyCorners; i++) - { - int next = i + 1; - if (this.closedPath && next == polyCorners) - { - next = 0; - } - - length += Vector2.Distance(this.points[i], this.points[next]); - } - - return length; - } - - /// - /// Calculate the constants. - /// - private void CalculateConstants() - { - // http://alienryderflex.com/polygon/ source for point in polygon logic - if (this.calculated) - { - return; - } - - lock (Locker) - { - if (this.calculated) - { - return; - } - - Vector2[] poly = this.points; - int polyCorners = poly.Length; - this.constant = new float[polyCorners]; - this.multiple = new float[polyCorners]; - this.distance = new float[polyCorners]; - int i, j = polyCorners - 1; - - this.distance[0] = 0; - - for (i = 0; i < polyCorners; i++) - { - this.distance[j] = this.distance[i] + Vector2.Distance(poly[i], poly[j]); - if (poly[j].Y == poly[i].Y) - { - this.constant[i] = poly[i].X; - this.multiple[i] = 0; - } - else - { - Vector2 subtracted = poly[j] - poly[i]; - this.constant[i] = (poly[i].X - ((poly[i].Y * poly[j].X) / subtracted.Y)) + ((poly[i].Y * poly[i].X) / subtracted.Y); - this.multiple[i] = subtracted.X / subtracted.Y; - } - - j = i; - } - - this.calculated = true; - } - } - - /// - /// Calculate any shorter distances along the path. - /// - /// The start position. - /// The end position. - /// The current point. - /// The info. - /// - /// The . - /// - private bool CalculateShorterDistance(Vector2 start, Vector2 end, Vector2 point, ref PointInfoInternal info) - { - Vector2 diffEnds = end - start; - - float lengthSquared = diffEnds.LengthSquared(); - Vector2 diff = point - start; - - Vector2 multiplied = diff * diffEnds; - float u = (multiplied.X + multiplied.Y) / lengthSquared; - - if (u > 1) - { - u = 1; - } - else if (u < 0) - { - u = 0; - } - - Vector2 multipliedByU = diffEnds * u; - - Vector2 pointOnLine = start + multipliedByU; - - Vector2 d = pointOnLine - point; - - float dist = d.LengthSquared(); - - if (info.DistanceSquared > dist) - { - info.DistanceSquared = dist; - info.PointOnLine = pointOnLine; - return true; - } - - return false; - } - - /// - /// Contains information about the current point. - /// - private struct PointInfoInternal - { - /// - /// The distance squared. - /// - public float DistanceSquared; - - /// - /// The point on the current line. - /// - public Vector2 PointOnLine; - } - } -} diff --git a/src/ImageSharp.Drawing/Paths/LinearLineSegment.cs b/src/ImageSharp.Drawing/Paths/LinearLineSegment.cs deleted file mode 100644 index 6942ce5a5..000000000 --- a/src/ImageSharp.Drawing/Paths/LinearLineSegment.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Linq; - using System.Numerics; - - /// - /// Represents a series of control points that will be joined by straight lines - /// - /// - public class LinearLineSegment : ILineSegment - { - /// - /// The collection of points. - /// - private readonly Vector2[] points; - - /// - /// Initializes a new instance of the class. - /// - /// The start. - /// The end. - public LinearLineSegment(Vector2 start, Vector2 end) - : this(new[] { start, end }) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public LinearLineSegment(params Vector2[] points) - { - Guard.NotNull(points, nameof(points)); - Guard.MustBeGreaterThanOrEqualTo(points.Count(), 2, nameof(points)); - - this.points = points; - } - - /// - /// Converts the into a simple linear path.. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.points; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Paths/Path.cs b/src/ImageSharp.Drawing/Paths/Path.cs deleted file mode 100644 index eb2ab5e85..000000000 --- a/src/ImageSharp.Drawing/Paths/Path.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// A aggregate of s making a single logical path - /// - /// - public class Path : IPath - { - /// - /// The inner path. - /// - private readonly InternalPath innerPath; - - /// - /// Initializes a new instance of the class. - /// - /// The segment. - public Path(params ILineSegment[] segment) - { - this.innerPath = new InternalPath(segment, false); - } - - /// - public RectangleF Bounds => this.innerPath.Bounds; - - /// - public bool IsClosed => false; - - /// - public float Length => this.innerPath.Length; - - /// - public Vector2[] AsSimpleLinearPath() - { - return this.innerPath.Points; - } - - /// - public PointInfo Distance(Vector2 point) - { - return this.innerPath.DistanceFromPath(point); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs index a08c7a7fa..bbb3c2559 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs @@ -9,7 +9,6 @@ namespace ImageSharp.Drawing.Pens using System.Numerics; using ImageSharp.Drawing.Brushes; - using ImageSharp.Drawing.Paths; using Processors; /// diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs index e07b96949..222598d85 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Drawing.Processors { using System; - using Paths; /// /// primitive that converts a into a color and a distance away from the drawable part of the path. diff --git a/src/ImageSharp.Drawing/Paths/PointInfo.cs b/src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs similarity index 54% rename from src/ImageSharp.Drawing/Paths/PointInfo.cs rename to src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs index a2cbf1e73..6fc78b09b 100644 --- a/src/ImageSharp.Drawing/Paths/PointInfo.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs @@ -3,33 +3,29 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Drawing.Paths +namespace ImageSharp.Drawing.Processors { + using System; using System.Numerics; /// - /// Returns meta data about the nearest point on a path from a vector + /// Returns details about how far away from the inside of a shape and the color the pixel could be. /// public struct PointInfo { /// - /// The search point - /// - public Vector2 SearchPoint; - - /// - /// The distance along path is away from the start of the path + /// The distance along path /// public float DistanceAlongPath; /// - /// The distance is away from . + /// The distance from path /// public float DistanceFromPath; /// - /// The closest point to that lies on the path. + /// The search point /// - public Vector2 ClosestPointOnPath; + public Vector2 SearchPoint; } } diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index f7bdcb689..d6b2f3eb2 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -6,13 +6,13 @@ namespace ImageSharp.Drawing.Processors { using System; + using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Threading.Tasks; using ImageSharp.Processing; - using Paths; using Pens; - using Shapes; + using SixLabors.Shapes; using Rectangle = ImageSharp.Rectangle; /// @@ -38,7 +38,7 @@ namespace ImageSharp.Drawing.Processors /// The shape. /// The options. public DrawPathProcessor(IPen pen, IShape shape, GraphicsOptions options) - : this(pen, shape.ToArray(), options) + : this(pen, shape.Paths, options) { } @@ -59,24 +59,24 @@ namespace ImageSharp.Drawing.Processors /// The pen. /// The paths. /// The options. - public DrawPathProcessor(IPen pen, IPath[] paths, GraphicsOptions options) + public DrawPathProcessor(IPen pen, IEnumerable paths, GraphicsOptions options) { - this.paths = paths; + this.paths = paths.ToArray(); this.pen = pen; this.options = options; - if (paths.Length != 1) + if (this.paths.Length != 1) { - var maxX = paths.Max(x => x.Bounds.Right); - var minX = paths.Min(x => x.Bounds.Left); - var maxY = paths.Max(x => x.Bounds.Bottom); - var minY = paths.Min(x => x.Bounds.Top); + var maxX = this.paths.Max(x => x.Bounds.Right); + var minX = this.paths.Min(x => x.Bounds.Left); + var maxY = this.paths.Max(x => x.Bounds.Bottom); + var minY = this.paths.Min(x => x.Bounds.Top); this.region = new RectangleF(minX, minY, maxX - minX, maxY - minY); } else { - this.region = paths[0].Bounds; + this.region = this.paths[0].Bounds.Convert(); } } @@ -119,7 +119,7 @@ namespace ImageSharp.Drawing.Processors minY, maxY, this.ParallelOptions, - y => + (int y) => { int offsetY = y - polyStartY; var currentPoint = default(Vector2); @@ -131,7 +131,7 @@ namespace ImageSharp.Drawing.Processors var dist = this.Closest(currentPoint); - var color = applicator.GetColor(dist); + var color = applicator.GetColor(dist.Convert()); var opacity = this.Opacity(color.DistanceFromElement); @@ -154,9 +154,9 @@ namespace ImageSharp.Drawing.Processors } } - private PointInfo Closest(Vector2 point) + private SixLabors.Shapes.PointInfo Closest(Vector2 point) { - PointInfo result = default(PointInfo); + SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); float distance = float.MaxValue; for (int i = 0; i < this.paths.Length; i++) diff --git a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs b/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs index 4696b8613..14e99cba3 100644 --- a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs @@ -11,7 +11,7 @@ namespace ImageSharp.Drawing.Processors using System.Threading.Tasks; using Drawing; using ImageSharp.Processing; - using Shapes; + using SixLabors.Shapes; using Rectangle = ImageSharp.Rectangle; /// @@ -44,7 +44,7 @@ namespace ImageSharp.Drawing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - Rectangle rect = RectangleF.Ceiling(this.poly.Bounds); // rounds the points out away from the center + Rectangle rect = RectangleF.Ceiling(this.poly.Bounds.Convert()); // rounds the points out away from the center int polyStartY = rect.Y - DrawPadding; int polyEndY = rect.Bottom + DrawPadding; diff --git a/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs b/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs new file mode 100644 index 000000000..939a71845 --- /dev/null +++ b/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing.Processors +{ + using System; + using System.Buffers; + using System.Numerics; + using System.Threading.Tasks; + using Drawing; + using ImageSharp.Processing; + using SixLabors.Shapes; + using Rectangle = ImageSharp.Rectangle; + + /// + /// Extension methods for helping to bridge Shaper2D and ImageSharp primitives. + /// + internal static class PointInfoExtensions + { + /// + /// Converts a to an ImageSharp . + /// + /// The source. + /// A representation of this + public static PointInfo Convert(this SixLabors.Shapes.PointInfo source) + { + return new PointInfo + { + DistanceAlongPath = source.DistanceAlongPath, + DistanceFromPath = source.DistanceFromPath, + SearchPoint = source.SearchPoint + }; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs b/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs new file mode 100644 index 000000000..327b618fa --- /dev/null +++ b/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing.Processors +{ + using System; + using System.Buffers; + using System.Numerics; + using System.Threading.Tasks; + using Drawing; + using ImageSharp.Processing; + using SixLabors.Shapes; + using Rectangle = ImageSharp.Rectangle; + + /// + /// Extension methods for helping to bridge Shaper2D and ImageSharp primitives. + /// + internal static class RectangleExtensions + { + /// + /// Converts a Shaper2D to an ImageSharp . + /// + /// The source. + /// A representation of this + public static RectangleF Convert(this SixLabors.Shapes.Rectangle source) + { + return new RectangleF(source.Location.X, source.Location.Y, source.Size.Width, source.Size.Height); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/BezierPolygon.cs b/src/ImageSharp.Drawing/Shapes/BezierPolygon.cs deleted file mode 100644 index b69ded207..000000000 --- a/src/ImageSharp.Drawing/Shapes/BezierPolygon.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections; - using System.Collections.Generic; - using System.Numerics; - using Paths; - - /// - /// Represents a polygon made up exclusivly of a single close cubic Bezier curve. - /// - public sealed class BezierPolygon : IShape - { - private Polygon innerPolygon; - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public BezierPolygon(params Vector2[] points) - { - this.innerPolygon = new Polygon(new BezierLineSegment(points)); - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.innerPolygon.Bounds; - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections => this.innerPolygon.MaxIntersections; - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// The distance from the shape. - /// - public float Distance(Vector2 point) => this.innerPolygon.Distance(point); - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.innerPolygon.FindIntersections(start, end, buffer, count, offset); - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.innerPolygon.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.innerPolygon.GetEnumerator(); - } - } -} diff --git a/src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs b/src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs deleted file mode 100644 index fd709719c..000000000 --- a/src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs +++ /dev/null @@ -1,246 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - - using Paths; - using PolygonClipper; - - /// - /// Represents a complex polygon made up of one or more outline - /// polygons and one or more holes to punch out of them. - /// - /// - public sealed class ComplexPolygon : IShape - { - private const float ClipperScaleFactor = 100f; - private IShape[] shapes; - private IEnumerable paths; - - /// - /// Initializes a new instance of the class. - /// - /// The outline. - /// The holes. - public ComplexPolygon(IShape outline, params IShape[] holes) - : this(new[] { outline }, holes) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The outlines. - /// The holes. - public ComplexPolygon(IShape[] outlines, IShape[] holes) - { - Guard.NotNull(outlines, nameof(outlines)); - Guard.MustBeGreaterThanOrEqualTo(outlines.Length, 1, nameof(outlines)); - - this.MaxIntersections = this.FixAndSetShapes(outlines, holes); - - float minX = this.shapes.Min(x => x.Bounds.Left); - float maxX = this.shapes.Max(x => x.Bounds.Right); - float minY = this.shapes.Min(x => x.Bounds.Top); - float maxY = this.shapes.Max(x => x.Bounds.Bottom); - - this.Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY); - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds { get; } - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections { get; } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// 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) - { - float dist = float.MaxValue; - bool inside = false; - foreach (IShape shape in this.shapes) - { - float d = shape.Distance(point); - - if (d <= 0) - { - // we are inside a poly - d = -d; // flip the sign - inside ^= true; // flip the inside flag - } - - if (d < dist) - { - dist = d; - } - } - - if (inside) - { - return -dist; - } - - return dist; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on all the polygons, that make up this complex shape, - /// that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - int totalAdded = 0; - for (int i = 0; i < this.shapes.Length; i++) - { - int added = this.shapes[i].FindIntersections(start, end, buffer, count, offset); - count -= added; - offset += added; - totalAdded += added; - } - - return totalAdded; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.paths.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - - private void AddPoints(Clipper clipper, IShape shape, PolyType polyType) - { - // if the path is already the shape use it directly and skip the path loop. - if (shape is IPath) - { - clipper.AddPath( - (IPath)shape, - polyType); - } - else - { - foreach (IPath path in shape) - { - clipper.AddPath( - path, - polyType); - } - } - } - - private void AddPoints(Clipper clipper, IEnumerable shapes, PolyType polyType) - { - foreach (IShape shape in shapes) - { - this.AddPoints(clipper, shape, polyType); - } - } - - private void ExtractOutlines(PolyNode tree, List shapes, List paths) - { - if (tree.Contour.Any()) - { - // if the source path is set then we clipper retained the full path intact thus we can freely - // use it and get any shape optimisations that are availible. - if (tree.SourcePath != null) - { - shapes.Add((IShape)tree.SourcePath); - paths.Add(tree.SourcePath); - } - else - { - // convert the Clipper Contour from scaled ints back down to the origional size (this is going to be lossy but not significantly) - Polygon polygon = new Polygon(new Paths.LinearLineSegment(tree.Contour.ToArray())); - - shapes.Add(polygon); - paths.Add(polygon); - } - } - - foreach (PolyNode c in tree.Children) - { - this.ExtractOutlines(c, shapes, paths); - } - } - - private int FixAndSetShapes(IEnumerable outlines, IEnumerable holes) - { - Clipper clipper = new Clipper(); - - // add the outlines and the holes to clipper, scaling up from the float source to the int based system clipper uses - this.AddPoints(clipper, outlines, PolyType.Subject); - this.AddPoints(clipper, holes, PolyType.Clip); - - PolyTree tree = clipper.Execute(); - - List shapes = new List(); - List paths = new List(); - - // convert the 'tree' back to paths - this.ExtractOutlines(tree, shapes, paths); - this.shapes = shapes.ToArray(); - this.paths = paths.ToArray(); - - int intersections = 0; - foreach (IShape s in this.shapes) - { - intersections += s.MaxIntersections; - } - - return intersections; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/IShape.cs b/src/ImageSharp.Drawing/Shapes/IShape.cs deleted file mode 100644 index 242e3bd8e..000000000 --- a/src/ImageSharp.Drawing/Shapes/IShape.cs +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections.Generic; - using System.Numerics; - using Paths; - - /// - /// Represents a closed set of paths making up a single shape. - /// - public interface IShape : IEnumerable - { - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - RectangleF Bounds { get; } - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - int MaxIntersections { get; } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// Returns the distance from the shape to the point - /// - float Distance(Vector2 point); - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset); - } -} diff --git a/src/ImageSharp.Drawing/Shapes/LinearPolygon.cs b/src/ImageSharp.Drawing/Shapes/LinearPolygon.cs deleted file mode 100644 index 30a30c20f..000000000 --- a/src/ImageSharp.Drawing/Shapes/LinearPolygon.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections; - using System.Collections.Generic; - using System.Numerics; - using Paths; - - /// - /// Represents a polygon made up exclusivly of a single Linear path. - /// - public sealed class LinearPolygon : IShape - { - private Polygon innerPolygon; - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public LinearPolygon(params Vector2[] points) - { - this.innerPolygon = new Polygon(new LinearLineSegment(points)); - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.innerPolygon.Bounds; - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections - { - get - { - return this.innerPolygon.MaxIntersections; - } - } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// Returns the distance from the shape to the point - /// - public float Distance(Vector2 point) => this.innerPolygon.Distance(point); - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.innerPolygon.FindIntersections(start, end, buffer, count, offset); - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() => this.innerPolygon.GetEnumerator(); - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() => this.innerPolygon.GetEnumerator(); - } -} diff --git a/src/ImageSharp.Drawing/Shapes/Polygon.cs b/src/ImageSharp.Drawing/Shapes/Polygon.cs deleted file mode 100644 index 86c3c9ee4..000000000 --- a/src/ImageSharp.Drawing/Shapes/Polygon.cs +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections; - using System.Collections.Generic; - using System.Numerics; - - using Paths; - - /// - /// A shape made up of a single path made up of one of more s - /// - public sealed class Polygon : IShape, IPath - { - private readonly InternalPath innerPath; - private readonly IEnumerable pathCollection; - - /// - /// Initializes a new instance of the class. - /// - /// The segments. - public Polygon(params ILineSegment[] segments) - { - this.innerPath = new InternalPath(segments, true); - this.pathCollection = new[] { this }; - } - - /// - /// Initializes a new instance of the class. - /// - /// The segment. - public Polygon(ILineSegment segment) - { - this.innerPath = new InternalPath(segment, true); - this.pathCollection = new[] { this }; - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.innerPath.Bounds; - - /// - /// Gets the length of the path - /// - /// - /// The length. - /// - public float Length => this.innerPath.Length; - - /// - /// Gets a value indicating whether this instance is closed. - /// - /// - /// true if this instance is closed; otherwise, false. - /// - public bool IsClosed => true; - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections => this.innerPath.Points.Length; - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// The distance of the point away from the shape - /// - public float Distance(Vector2 point) - { - bool isInside = this.innerPath.PointInPolygon(point); - - float distance = this.innerPath.DistanceFromPath(point).DistanceFromPath; - if (isInside) - { - return -distance; - } - - return distance; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Calcualtes the distance along and away from the path for a specified point. - /// - /// The point along the path. - /// - /// distance metadata about the point. - /// - PointInfo IPath.Distance(Vector2 point) - { - return this.innerPath.DistanceFromPath(point); - } - - /// - /// Returns the current shape as a simple linear path. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.innerPath.Points; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.innerPath.FindIntersections(start, end, buffer, count, offset); - } - } -} diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs deleted file mode 100644 index c13acec8f..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs +++ /dev/null @@ -1,3860 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Library to clip polygons. - /// - internal class Clipper - { - private const double HorizontalDeltaLimit = -3.4E+38; - private const int Skip = -2; - private const int Unassigned = -1; // InitOptions that can be passed to the constructor ... - - private Maxima maxima; - private TEdge sortedEdges; - private List intersectList; - private IComparer intersectNodeComparer = new IntersectNodeSort(); - private bool executeLocked; - - private List joins; - private List ghostJoins; - private bool usingPolyTree; - private LocalMinima minimaList = null; - private LocalMinima currentLM = null; - private List> edges = new List>(); - private Scanbeam scanbeam; - private List polyOuts; - private TEdge activeEdges; - - /// - /// Initializes a new instance of the class. - /// - public Clipper() - { - this.scanbeam = null; - this.maxima = null; - this.activeEdges = null; - this.sortedEdges = null; - this.intersectList = new List(); - this.executeLocked = false; - this.usingPolyTree = false; - this.polyOuts = new List(); - this.joins = new List(); - this.ghostJoins = new List(); - } - - /// - /// Node types - /// - private enum NodeType - { - /// - /// Any - /// - Any, - - /// - /// The open - /// - Open, - - /// - /// The closed - /// - Closed - } - - /// - /// Adds the path. - /// - /// The path. - /// Type of the poly. - /// True if the path was added. - /// AddPath: Open paths have been disabled. - public bool AddPath(IPath path, PolyType polyType) - { - var pg = path.AsSimpleLinearPath(); - - int highI = pg.Length - 1; - while (highI > 0 && (pg[highI] == pg[0])) - { - --highI; - } - - while (highI > 0 && (pg[highI] == pg[highI - 1])) - { - --highI; - } - - if (highI < 2) - { - return false; - } - - // create a new edge array ... - List edges = new List(highI + 1); - for (int i = 0; i <= highI; i++) - { - edges.Add(new TEdge() { SourcePath = path }); - } - - bool isFlat = true; - - // 1. Basic (first) edge initialization ... - edges[1].Curr = pg[1]; - - InitEdge(edges[0], edges[1], edges[highI], pg[0]); - InitEdge(edges[highI], edges[0], edges[highI - 1], pg[highI]); - for (int i = highI - 1; i >= 1; --i) - { - InitEdge(edges[i], edges[i + 1], edges[i - 1], pg[i]); - } - - TEdge eStart = edges[0]; - - // 2. Remove duplicate vertices, and (when closed) collinear edges ... - TEdge edge = eStart, eLoopStop = eStart; - while (true) - { - // nb: allows matching start and end points when not Closed ... - if (edge.Curr == edge.Next.Curr) - { - if (edge == edge.Next) - { - break; - } - - if (edge == eStart) - { - eStart = edge.Next; - } - - edge = RemoveEdge(edge); - eLoopStop = edge; - continue; - } - - if (edge.Prev == edge.Next) - { - break; // only two vertices - } - else if (SlopesEqual(edge.Prev.Curr, edge.Curr, edge.Next.Curr)) - { - // Collinear edges are allowed for open paths but in closed paths - // the default is to merge adjacent collinear edges into a single edge. - // However, if the PreserveCollinear property is enabled, only overlapping - // collinear edges (ie spikes) will be removed from closed paths. - if (edge == eStart) - { - eStart = edge.Next; - } - - edge = RemoveEdge(edge); - edge = edge.Prev; - eLoopStop = edge; - continue; - } - - edge = edge.Next; - if (edge == eLoopStop) - { - break; - } - } - - if (edge.Prev == edge.Next) - { - return false; - } - - // 3. Do second stage of edge initialization ... - edge = eStart; - do - { - this.InitEdge2(edge, polyType); - edge = edge.Next; - if (isFlat && edge.Curr.Y != eStart.Curr.Y) - { - isFlat = false; - } - } - while (edge != eStart); - - // 4. Finally, add edge bounds to LocalMinima list ... - // Totally flat paths must be handled differently when adding them - // to LocalMinima list to avoid endless loops etc ... - if (isFlat) - { - return false; - } - - this.edges.Add(edges); - bool leftBoundIsForward; - TEdge emIn = null; - - // workaround to avoid an endless loop in the while loop below when - // open paths have matching start and end points ... - if (edge.Prev.Bot == edge.Prev.Top) - { - edge = edge.Next; - } - - while (true) - { - edge = FindNextLocMin(edge); - if (edge == emIn) - { - break; - } - else if (emIn == null) - { - emIn = edge; - } - - // E and E.Prev now share a local minima (left aligned if horizontal). - // Compare their slopes to find which starts which bound ... - LocalMinima locMin = new LocalMinima(); - locMin.Next = null; - locMin.Y = edge.Bot.Y; - if (edge.Dx < edge.Prev.Dx) - { - locMin.LeftBound = edge.Prev; - locMin.RightBound = edge; - leftBoundIsForward = false; // Q.nextInLML = Q.prev - } - else - { - locMin.LeftBound = edge; - locMin.RightBound = edge.Prev; - leftBoundIsForward = true; // Q.nextInLML = Q.next - } - - locMin.LeftBound.Side = EdgeSide.Left; - locMin.RightBound.Side = EdgeSide.Right; - - if (locMin.LeftBound.Next == locMin.RightBound) - { - locMin.LeftBound.WindDelta = -1; - } - else - { - locMin.LeftBound.WindDelta = 1; - } - - locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta; - - edge = this.ProcessBound(locMin.LeftBound, leftBoundIsForward); - if (edge.OutIdx == Skip) - { - edge = this.ProcessBound(edge, leftBoundIsForward); - } - - TEdge edge2 = this.ProcessBound(locMin.RightBound, !leftBoundIsForward); - if (edge2.OutIdx == Skip) - { - edge2 = this.ProcessBound(edge2, !leftBoundIsForward); - } - - if (locMin.LeftBound.OutIdx == Skip) - { - locMin.LeftBound = null; - } - else if (locMin.RightBound.OutIdx == Skip) - { - locMin.RightBound = null; - } - - this.InsertLocalMinima(locMin); - if (!leftBoundIsForward) - { - edge = edge2; - } - } - - return true; - } - - /// - /// Executes the specified clip type. - /// - /// - /// Returns the polytree containing the converted polygons. - /// - public PolyTree Execute() - { - PolyTree polytree = new PolyTree(); - - if (this.executeLocked) - { - return null; - } - - this.executeLocked = true; - this.usingPolyTree = true; - bool succeeded; - try - { - succeeded = this.ExecuteInternal(); - - // build the return polygons ... - if (succeeded) - { - this.BuildResult2(polytree); - } - } - finally - { - this.DisposeAllPolyPts(); - this.executeLocked = false; - } - - if (succeeded) - { - return polytree; - } - - return null; - } - - private static float Round(double value) - { - return value < 0 ? (float)(value - 0.5) : (float)(value + 0.5); - } - - private static float TopX(TEdge edge, float currentY) - { - if (currentY == edge.Top.Y) - { - return edge.Top.X; - } - - return edge.Bot.X + Round(edge.Dx * (currentY - edge.Bot.Y)); - } - - private static void AddPolyNodeToPaths(PolyNode polynode, NodeType nt, List> paths) - { - bool match = true; - switch (nt) - { - case NodeType.Open: return; - case NodeType.Closed: match = !polynode.IsOpen; break; - default: break; - } - - if (polynode.Polygon.Count > 0 && match) - { - paths.Add(polynode.Polygon); - } - - foreach (PolyNode pn in polynode.Children) - { - AddPolyNodeToPaths(pn, nt, paths); - } - } - - private static double DistanceFromLineSqrd(Vector2 pt, Vector2 ln1, Vector2 ln2) - { - // The equation of a line in general form (Ax + By + C = 0) - // given 2 points (x¹,y¹) & (x²,y²) is ... - // (y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 - // A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ - // perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) - // see http://en.wikipedia.org/wiki/Perpendicular_distance - double a = ln1.Y - ln2.Y; - double b = ln2.X - ln1.X; - double c = (a * ln1.X) + (b * ln1.Y); - c = (a * pt.X) + (b * pt.Y) - c; - return (c * c) / ((a * a) + (b * b)); - } - - private static bool SlopesNearCollinear(Vector2 pt1, Vector2 pt2, Vector2 pt3, double distSqrd) - { - // this function is more accurate when the point that's GEOMETRICALLY - // between the other 2 points is the one that's tested for distance. - // nb: with 'spikes', either pt1 or pt3 is geometrically between the other pts - if (Math.Abs(pt1.X - pt2.X) > Math.Abs(pt1.Y - pt2.Y)) - { - if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) - { - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - } - else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) - { - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - } - else - { - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - } - else - { - if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) - { - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - } - else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) - { - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - } - else - { - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - } - } - - private static bool PointsAreClose(Vector2 pt1, Vector2 pt2, double distSqrd) - { - return Vector2.DistanceSquared(pt1, pt2) <= distSqrd; - } - - private static OutPt ExcludeOp(OutPt op) - { - OutPt result = op.Prev; - result.Next = op.Next; - op.Next.Prev = result; - result.Idx = 0; - return result; - } - - private static void FixHoleLinkage(OutRec outRec) - { - // skip if an outermost polygon or - // already already points to the correct FirstLeft ... - if (outRec.FirstLeft == null || - (outRec.IsHole != outRec.FirstLeft.IsHole && - outRec.FirstLeft.Pts != null)) - { - return; - } - - OutRec orfl = outRec.FirstLeft; - while (orfl != null && ((orfl.IsHole == outRec.IsHole) || orfl.Pts == null)) - { - orfl = orfl.FirstLeft; - } - - outRec.FirstLeft = orfl; - } - - // See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos - // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf - private static int PointInPolygon(Vector2 pt, OutPt op) - { - // returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - OutPt startOp = op; - float ptx = pt.X, pty = pt.Y; - float poly0x = op.Pt.X, poly0y = op.Pt.Y; - do - { - op = op.Next; - float poly1x = op.Pt.X, poly1y = op.Pt.Y; - - if (poly1y == pty) - { - if ((poly1x == ptx) || (poly0y == pty && - ((poly1x > ptx) == (poly0x < ptx)))) - { - return -1; - } - } - - if ((poly0y < pty) != (poly1y < pty)) - { - if (poly0x >= ptx) - { - if (poly1x > ptx) - { - result = 1 - result; - } - else - { - double d = (double)((poly0x - ptx) * (poly1y - pty)) - - (double)((poly1x - ptx) * (poly0y - pty)); - if (d == 0) - { - return -1; - } - - if ((d > 0) == (poly1y > poly0y)) - { - result = 1 - result; - } - } - } - else - { - if (poly1x > ptx) - { - double d = (double)((poly0x - ptx) * (poly1y - pty)) - (double)((poly1x - ptx) * (poly0y - pty)); - if (d == 0) - { - return -1; - } - - if ((d > 0) == (poly1y > poly0y)) - { - result = 1 - result; - } - } - } - } - - poly0x = poly1x; - poly0y = poly1y; - } - while (startOp != op); - - return result; - } - - private static bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2) - { - OutPt op = outPt1; - do - { - // nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - int res = PointInPolygon(op.Pt, outPt2); - if (res >= 0) - { - return res > 0; - } - - op = op.Next; - } - while (op != outPt1); - return true; - } - - private static void SwapSides(TEdge edge1, TEdge edge2) - { - EdgeSide side = edge1.Side; - edge1.Side = edge2.Side; - edge2.Side = side; - } - - private static void SwapPolyIndexes(TEdge edge1, TEdge edge2) - { - int outIdx = edge1.OutIdx; - edge1.OutIdx = edge2.OutIdx; - edge2.OutIdx = outIdx; - } - - private static double GetDx(Vector2 pt1, Vector2 pt2) - { - if (pt1.Y == pt2.Y) - { - return HorizontalDeltaLimit; - } - else - { - return (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); - } - } - - private static bool HorzSegmentsOverlap(float seg1a, float seg1b, float seg2a, float seg2b) - { - if (seg1a > seg1b) - { - Swap(ref seg1a, ref seg1b); - } - - if (seg2a > seg2b) - { - Swap(ref seg2a, ref seg2b); - } - - return (seg1a < seg2b) && (seg2a < seg1b); - } - - private static TEdge FindNextLocMin(TEdge edge) - { - TEdge edge2; - while (true) - { - while (edge.Bot != edge.Prev.Bot || edge.Curr == edge.Top) - { - edge = edge.Next; - } - - if (edge.Dx != HorizontalDeltaLimit && edge.Prev.Dx != HorizontalDeltaLimit) - { - break; - } - - while (edge.Prev.Dx == HorizontalDeltaLimit) - { - edge = edge.Prev; - } - - edge2 = edge; - while (edge.Dx == HorizontalDeltaLimit) - { - edge = edge.Next; - } - - if (edge.Top.Y == edge.Prev.Bot.Y) - { - continue; // ie just an intermediate horz. - } - - if (edge2.Prev.Bot.X < edge.Bot.X) - { - edge = edge2; - } - - break; - } - - return edge; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Swap(ref float val1, ref float val2) - { - float tmp = val1; - val1 = val2; - val2 = tmp; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsHorizontal(TEdge e) - { - return e.Delta.Y == 0; - } - - private static bool SlopesEqual(TEdge e1, TEdge e2) - { - return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; - } - - private static bool SlopesEqual(Vector2 pt1, Vector2 pt2, Vector2 pt3) - { - var dif12 = pt1 - pt2; - var dif23 = pt2 - pt3; - return (dif12.Y * dif23.X) - (dif12.X * dif23.Y) == 0; - } - - private static bool SlopesEqual(Vector2 pt1, Vector2 pt2, Vector2 pt3, Vector2 pt4) - { - var dif12 = pt1 - pt2; - var dif34 = pt3 - pt4; - - return (dif12.Y * dif34.X) - (dif12.X * dif34.Y) == 0; - } - - private static void InitEdge(TEdge e, TEdge eNext, TEdge ePrev, Vector2 pt) - { - e.Next = eNext; - e.Prev = ePrev; - e.Curr = pt; - e.OutIdx = Unassigned; - } - - private static OutRec ParseFirstLeft(OutRec firstLeft) - { - while (firstLeft != null && firstLeft.Pts == null) - { - firstLeft = firstLeft.FirstLeft; - } - - return firstLeft; - } - - private static bool Pt2IsBetweenPt1AndPt3(Vector2 pt1, Vector2 pt2, Vector2 pt3) - { - if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) - { - return false; - } - else if (pt1.X != pt3.X) - { - return (pt2.X > pt1.X) == (pt2.X < pt3.X); - } - else - { - return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); - } - } - - private static TEdge RemoveEdge(TEdge e) - { - // removes e from double_linked_list (but without removing from memory) - e.Prev.Next = e.Next; - e.Next.Prev = e.Prev; - TEdge result = e.Next; - e.Prev = null; // flag as removed (see ClipperBase.Clear) - return result; - } - - private static void ReverseHorizontal(TEdge e) - { - // swap horizontal edges' top and bottom x's so they follow the natural - // progression of the bounds - ie so their xbots will align with the - // adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - Swap(ref e.Top.X, ref e.Bot.X); - } - - private void InsertMaxima(float x) - { - // double-linked list: sorted ascending, ignoring dups. - Maxima newMax = new Maxima(); - newMax.X = x; - if (this.maxima == null) - { - this.maxima = newMax; - this.maxima.Next = null; - this.maxima.Prev = null; - } - else if (x < this.maxima.X) - { - newMax.Next = this.maxima; - newMax.Prev = null; - this.maxima = newMax; - } - else - { - Maxima m = this.maxima; - while (m.Next != null && (x >= m.Next.X)) - { - m = m.Next; - } - - if (x == m.X) - { - return; // ie ignores duplicates (& CG to clean up newMax) - } - - // insert newMax between m and m.Next ... - newMax.Next = m.Next; - newMax.Prev = m; - if (m.Next != null) - { - m.Next.Prev = newMax; - } - - m.Next = newMax; - } - } - - private bool ExecuteInternal() - { - try - { - this.Reset(); - this.sortedEdges = null; - this.maxima = null; - - float botY, topY; - if (!this.PopScanbeam(out botY)) - { - return false; - } - - this.InsertLocalMinimaIntoAEL(botY); - while (this.PopScanbeam(out topY) || this.LocalMinimaPending()) - { - this.ProcessHorizontals(); - this.ghostJoins.Clear(); - if (!this.ProcessIntersections(topY)) - { - return false; - } - - this.ProcessEdgesAtTopOfScanbeam(topY); - botY = topY; - this.InsertLocalMinimaIntoAEL(botY); - } - - // fix orientations ... - foreach (OutRec outRec in this.polyOuts) - { - if (outRec.Pts == null || outRec.IsOpen) - { - continue; - } - } - - this.JoinCommonEdges(); - - foreach (OutRec outRec in this.polyOuts) - { - if (outRec.Pts == null) - { - continue; - } - else if (outRec.IsOpen) - { - this.FixupOutPolyline(outRec); - } - else - { - this.FixupOutPolygon(outRec); - } - } - - return true; - } - finally - { - this.joins.Clear(); - this.ghostJoins.Clear(); - } - } - - private void DisposeAllPolyPts() - { - for (int i = 0; i < this.polyOuts.Count; ++i) - { - this.DisposeOutRec(i); - } - - this.polyOuts.Clear(); - } - - private void AddJoin(OutPt op1, OutPt op2, Vector2 offPt) - { - Join j = new Join(); - j.OutPt1 = op1; - j.OutPt2 = op2; - j.OffPt = offPt; - this.joins.Add(j); - } - - private void AddGhostJoin(OutPt op, Vector2 offPt) - { - Join j = new Join(); - j.OutPt1 = op; - j.OffPt = offPt; - this.ghostJoins.Add(j); - } - - private void InsertLocalMinimaIntoAEL(float botY) - { - LocalMinima lm; - while (this.PopLocalMinima(botY, out lm)) - { - TEdge lb = lm.LeftBound; - TEdge rb = lm.RightBound; - - OutPt op1 = null; - if (lb == null) - { - this.InsertEdgeIntoAEL(rb, null); - this.SetWindingCount(rb); - if (this.IsContributing(rb)) - { - op1 = this.AddOutPt(rb, rb.Bot); - } - } - else if (rb == null) - { - this.InsertEdgeIntoAEL(lb, null); - this.SetWindingCount(lb); - if (this.IsContributing(lb)) - { - op1 = this.AddOutPt(lb, lb.Bot); - } - - this.InsertScanbeam(lb.Top.Y); - } - else - { - this.InsertEdgeIntoAEL(lb, null); - this.InsertEdgeIntoAEL(rb, lb); - this.SetWindingCount(lb); - rb.WindCnt = lb.WindCnt; - rb.WindCnt2 = lb.WindCnt2; - if (this.IsContributing(lb)) - { - op1 = this.AddLocalMinPoly(lb, rb, lb.Bot); - } - - this.InsertScanbeam(lb.Top.Y); - } - - if (rb != null) - { - if (IsHorizontal(rb)) - { - if (rb.NextInLML != null) - { - this.InsertScanbeam(rb.NextInLML.Top.Y); - } - - this.AddEdgeToSEL(rb); - } - else - { - this.InsertScanbeam(rb.Top.Y); - } - } - - if (lb == null || rb == null) - { - continue; - } - - // if output polygons share an Edge with a horizontal rb, they'll need joining later ... - if (op1 != null && IsHorizontal(rb) && - this.ghostJoins.Count > 0 && rb.WindDelta != 0) - { - for (int i = 0; i < this.ghostJoins.Count; i++) - { - // if the horizontal Rb and a 'ghost' horizontal overlap, then convert - // the 'ghost' join to a real join ready for later ... - Join j = this.ghostJoins[i]; - if (HorzSegmentsOverlap(j.OutPt1.Pt.X, j.OffPt.X, rb.Bot.X, rb.Top.X)) - { - this.AddJoin(j.OutPt1, op1, j.OffPt); - } - } - } - - if (lb.OutIdx >= 0 && lb.PrevInAEL != null && - lb.PrevInAEL.Curr.X == lb.Bot.X && - lb.PrevInAEL.OutIdx >= 0 && - SlopesEqual(lb.PrevInAEL.Curr, lb.PrevInAEL.Top, lb.Curr, lb.Top) && - lb.WindDelta != 0 && lb.PrevInAEL.WindDelta != 0) - { - OutPt op2 = this.AddOutPt(lb.PrevInAEL, lb.Bot); - this.AddJoin(op1, op2, lb.Top); - } - - if (lb.NextInAEL != rb) - { - if (rb.OutIdx >= 0 && rb.PrevInAEL.OutIdx >= 0 && - SlopesEqual(rb.PrevInAEL.Curr, rb.PrevInAEL.Top, rb.Curr, rb.Top) && - rb.WindDelta != 0 && rb.PrevInAEL.WindDelta != 0) - { - OutPt op2 = this.AddOutPt(rb.PrevInAEL, rb.Bot); - this.AddJoin(op1, op2, rb.Top); - } - - TEdge e = lb.NextInAEL; - if (e != null) - { - while (e != rb) - { - // nb: For calculating winding counts etc, IntersectEdges() assumes - // that param1 will be to the right of param2 ABOVE the intersection ... - this.IntersectEdges(rb, e, lb.Curr); // order important here - e = e.NextInAEL; - } - } - } - } - } - - private void InsertEdgeIntoAEL(TEdge edge, TEdge startEdge) - { - if (this.activeEdges == null) - { - edge.PrevInAEL = null; - edge.NextInAEL = null; - this.activeEdges = edge; - } - else if (startEdge == null && this.E2InsertsBeforeE1(this.activeEdges, edge)) - { - edge.PrevInAEL = null; - edge.NextInAEL = this.activeEdges; - this.activeEdges.PrevInAEL = edge; - this.activeEdges = edge; - } - else - { - if (startEdge == null) - { - startEdge = this.activeEdges; - } - - while (startEdge.NextInAEL != null && - !this.E2InsertsBeforeE1(startEdge.NextInAEL, edge)) - { - startEdge = startEdge.NextInAEL; - } - - edge.NextInAEL = startEdge.NextInAEL; - if (startEdge.NextInAEL != null) - { - startEdge.NextInAEL.PrevInAEL = edge; - } - - edge.PrevInAEL = startEdge; - startEdge.NextInAEL = edge; - } - } - - private bool E2InsertsBeforeE1(TEdge e1, TEdge e2) - { - if (e2.Curr.X == e1.Curr.X) - { - if (e2.Top.Y > e1.Top.Y) - { - return e2.Top.X < TopX(e1, e2.Top.Y); - } - else - { - return e1.Top.X > TopX(e2, e1.Top.Y); - } - } - else - { - return e2.Curr.X < e1.Curr.X; - } - } - - private bool IsContributing(TEdge edge) - { - // return false if a subj line has been flagged as inside a subj polygon - if (edge.WindDelta == 0 && edge.WindCnt != 1) - { - return false; - } - - if (edge.PolyTyp == PolyType.Subject) - { - return edge.WindCnt2 == 0; - } - else - { - return edge.WindCnt2 != 0; - } - } - - private void SetWindingCount(TEdge edge) - { - TEdge e = edge.PrevInAEL; - - // find the edge of the same polytype that immediately preceeds 'edge' in AEL - while (e != null && ((e.PolyTyp != edge.PolyTyp) || (e.WindDelta == 0))) - { - e = e.PrevInAEL; - } - - if (e == null) - { - if (edge.WindDelta == 0) - { - edge.WindCnt = 1; - } - else - { - edge.WindCnt = edge.WindDelta; - } - - edge.WindCnt2 = 0; - e = this.activeEdges; // ie get ready to calc WindCnt2 - } - else if (edge.WindDelta == 0) - { - edge.WindCnt = 1; - edge.WindCnt2 = e.WindCnt2; - e = e.NextInAEL; // ie get ready to calc WindCnt2 - } - else - { - // EvenOdd filling ... - if (edge.WindDelta == 0) - { - // are we inside a subj polygon ... - bool inside = true; - TEdge e2 = e.PrevInAEL; - while (e2 != null) - { - if (e2.PolyTyp == e.PolyTyp && e2.WindDelta != 0) - { - inside = !inside; - } - - e2 = e2.PrevInAEL; - } - - edge.WindCnt = inside ? 0 : 1; - } - else - { - edge.WindCnt = edge.WindDelta; - } - - edge.WindCnt2 = e.WindCnt2; - e = e.NextInAEL; // ie get ready to calc WindCnt2 - } - - // update WindCnt2 ... - // EvenOdd filling ... - while (e != edge) - { - if (e.WindDelta != 0) - { - edge.WindCnt2 = edge.WindCnt2 == 0 ? 1 : 0; - } - - e = e.NextInAEL; - } - } - - private void AddEdgeToSEL(TEdge edge) - { - // SEL pointers in PEdge are use to build transient lists of horizontal edges. - // However, since we don't need to worry about processing order, all additions - // are made to the front of the list ... - if (this.sortedEdges == null) - { - this.sortedEdges = edge; - edge.PrevInSEL = null; - edge.NextInSEL = null; - } - else - { - edge.NextInSEL = this.sortedEdges; - edge.PrevInSEL = null; - this.sortedEdges.PrevInSEL = edge; - this.sortedEdges = edge; - } - } - - private bool PopEdgeFromSEL(out TEdge e) - { - // Pop edge from front of SEL (ie SEL is a FILO list) - e = this.sortedEdges; - if (e == null) - { - return false; - } - - TEdge oldE = e; - this.sortedEdges = e.NextInSEL; - if (this.sortedEdges != null) - { - this.sortedEdges.PrevInSEL = null; - } - - oldE.NextInSEL = null; - oldE.PrevInSEL = null; - return true; - } - - private void CopyAELToSEL() - { - TEdge e = this.activeEdges; - this.sortedEdges = e; - while (e != null) - { - e.PrevInSEL = e.PrevInAEL; - e.NextInSEL = e.NextInAEL; - e = e.NextInAEL; - } - } - - private void SwapPositionsInSEL(TEdge edge1, TEdge edge2) - { - if (edge1.NextInSEL == null && edge1.PrevInSEL == null) - { - return; - } - - if (edge2.NextInSEL == null && edge2.PrevInSEL == null) - { - return; - } - - if (edge1.NextInSEL == edge2) - { - TEdge next = edge2.NextInSEL; - if (next != null) - { - next.PrevInSEL = edge1; - } - - TEdge prev = edge1.PrevInSEL; - if (prev != null) - { - prev.NextInSEL = edge2; - } - - edge2.PrevInSEL = prev; - edge2.NextInSEL = edge1; - edge1.PrevInSEL = edge2; - edge1.NextInSEL = next; - } - else if (edge2.NextInSEL == edge1) - { - TEdge next = edge1.NextInSEL; - if (next != null) - { - next.PrevInSEL = edge2; - } - - TEdge prev = edge2.PrevInSEL; - if (prev != null) - { - prev.NextInSEL = edge1; - } - - edge1.PrevInSEL = prev; - edge1.NextInSEL = edge2; - edge2.PrevInSEL = edge1; - edge2.NextInSEL = next; - } - else - { - TEdge next = edge1.NextInSEL; - TEdge prev = edge1.PrevInSEL; - edge1.NextInSEL = edge2.NextInSEL; - if (edge1.NextInSEL != null) - { - edge1.NextInSEL.PrevInSEL = edge1; - } - - edge1.PrevInSEL = edge2.PrevInSEL; - if (edge1.PrevInSEL != null) - { - edge1.PrevInSEL.NextInSEL = edge1; - } - - edge2.NextInSEL = next; - if (edge2.NextInSEL != null) - { - edge2.NextInSEL.PrevInSEL = edge2; - } - - edge2.PrevInSEL = prev; - if (edge2.PrevInSEL != null) - { - edge2.PrevInSEL.NextInSEL = edge2; - } - } - - if (edge1.PrevInSEL == null) - { - this.sortedEdges = edge1; - } - else if (edge2.PrevInSEL == null) - { - this.sortedEdges = edge2; - } - } - - private void AddLocalMaxPoly(TEdge e1, TEdge e2, Vector2 pt) - { - this.AddOutPt(e1, pt); - if (e2.WindDelta == 0) - { - this.AddOutPt(e2, pt); - } - - if (e1.OutIdx == e2.OutIdx) - { - e1.OutIdx = Unassigned; - e2.OutIdx = Unassigned; - } - else if (e1.OutIdx < e2.OutIdx) - { - this.AppendPolygon(e1, e2); - } - else - { - this.AppendPolygon(e2, e1); - } - } - - private OutPt AddLocalMinPoly(TEdge e1, TEdge e2, Vector2 pt) - { - OutPt result; - TEdge e, prevE; - if (IsHorizontal(e2) || (e1.Dx > e2.Dx)) - { - result = this.AddOutPt(e1, pt); - e2.OutIdx = e1.OutIdx; - e1.Side = EdgeSide.Left; - e2.Side = EdgeSide.Right; - e = e1; - if (e.PrevInAEL == e2) - { - prevE = e2.PrevInAEL; - } - else - { - prevE = e.PrevInAEL; - } - } - else - { - result = this.AddOutPt(e2, pt); - e1.OutIdx = e2.OutIdx; - e1.Side = EdgeSide.Right; - e2.Side = EdgeSide.Left; - e = e2; - if (e.PrevInAEL == e1) - { - prevE = e1.PrevInAEL; - } - else - { - prevE = e.PrevInAEL; - } - } - - if (prevE != null && prevE.OutIdx >= 0) - { - float xPrev = TopX(prevE, pt.Y); - float xE = TopX(e, pt.Y); - if ((xPrev == xE) && - (e.WindDelta != 0) && - (prevE.WindDelta != 0) && - SlopesEqual(new Vector2(xPrev, pt.Y), prevE.Top, new Vector2(xE, pt.Y), e.Top)) - { - OutPt outPt = this.AddOutPt(prevE, pt); - this.AddJoin(result, outPt, e.Top); - } - } - - return result; - } - - private OutPt AddOutPt(TEdge e, Vector2 pt) - { - if (e.OutIdx < 0) - { - OutRec outRec = this.CreateOutRec(); - outRec.SourcePath = e.SourcePath; // copy source from edge to outrec - outRec.IsOpen = e.WindDelta == 0; - OutPt newOp = new OutPt(); - outRec.Pts = newOp; - newOp.Idx = outRec.Idx; - newOp.Pt = pt; - newOp.Next = newOp; - newOp.Prev = newOp; - if (!outRec.IsOpen) - { - this.SetHoleState(e, outRec); - } - - e.OutIdx = outRec.Idx; // nb: do this after SetZ ! - return newOp; - } - else - { - OutRec outRec = this.polyOuts[e.OutIdx]; - - if (outRec.SourcePath != e.SourcePath) - { - // this edge was from a different/unknown source - outRec.SourcePath = null; // drop source form output - } - - // OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - OutPt op = outRec.Pts; - bool toFront = e.Side == EdgeSide.Left; - if (toFront && pt == op.Pt) - { - return op; - } - else if (!toFront && pt == op.Prev.Pt) - { - return op.Prev; - } - - // do we need to move the source to the point??? - OutPt newOp = new OutPt(); - newOp.Idx = outRec.Idx; - newOp.Pt = pt; - newOp.Next = op; - newOp.Prev = op.Prev; - newOp.Prev.Next = newOp; - op.Prev = newOp; - if (toFront) - { - outRec.Pts = newOp; - } - - return newOp; - } - } - - private OutPt GetLastOutPt(TEdge e) - { - OutRec outRec = this.polyOuts[e.OutIdx]; - if (e.Side == EdgeSide.Left) - { - return outRec.Pts; - } - else - { - return outRec.Pts.Prev; - } - } - - private void SetHoleState(TEdge e, OutRec outRec) - { - TEdge e2 = e.PrevInAEL; - TEdge eTmp = null; - while (e2 != null) - { - if (e2.OutIdx >= 0 && e2.WindDelta != 0) - { - if (eTmp == null) - { - eTmp = e2; - } - else if (eTmp.OutIdx == e2.OutIdx) - { - eTmp = null; // paired - } - } - - e2 = e2.PrevInAEL; - } - - if (eTmp == null) - { - outRec.FirstLeft = null; - outRec.IsHole = false; - } - else - { - outRec.FirstLeft = this.polyOuts[eTmp.OutIdx]; - outRec.IsHole = !outRec.FirstLeft.IsHole; - } - } - - private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2) - { - OutPt p = btmPt1.Prev; - while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) - { - p = p.Prev; - } - - double dx1p = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); - p = btmPt1.Next; - while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) - { - p = p.Next; - } - - double dx1n = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); - - p = btmPt2.Prev; - while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) - { - p = p.Prev; - } - - double dx2p = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); - p = btmPt2.Next; - while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) - { - p = p.Next; - } - - double dx2n = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); - - if (Math.Max(dx1p, dx1n) == Math.Max(dx2p, dx2n) && - Math.Min(dx1p, dx1n) == Math.Min(dx2p, dx2n)) - { - return this.Area(btmPt1) > 0; // if otherwise identical use orientation - } - else - { - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); - } - } - - private OutPt GetBottomPt(OutPt pp) - { - OutPt dups = null; - OutPt p = pp.Next; - while (p != pp) - { - if (p.Pt.Y > pp.Pt.Y) - { - pp = p; - dups = null; - } - else if (p.Pt.Y == pp.Pt.Y && p.Pt.X <= pp.Pt.X) - { - if (p.Pt.X < pp.Pt.X) - { - dups = null; - pp = p; - } - else - { - if (p.Next != pp && p.Prev != pp) - { - dups = p; - } - } - } - - p = p.Next; - } - - if (dups != null) - { - // there appears to be at least 2 vertices at bottomPt so ... - while (dups != p) - { - if (!this.FirstIsBottomPt(p, dups)) - { - pp = dups; - } - - dups = dups.Next; - while (dups.Pt != pp.Pt) - { - dups = dups.Next; - } - } - } - - return pp; - } - - private OutRec GetLowermostRec(OutRec outRec1, OutRec outRec2) - { - // work out which polygon fragment has the correct hole state ... - if (outRec1.BottomPt == null) - { - outRec1.BottomPt = this.GetBottomPt(outRec1.Pts); - } - - if (outRec2.BottomPt == null) - { - outRec2.BottomPt = this.GetBottomPt(outRec2.Pts); - } - - OutPt bPt1 = outRec1.BottomPt; - OutPt bPt2 = outRec2.BottomPt; - if (bPt1.Pt.Y > bPt2.Pt.Y) - { - return outRec1; - } - else if (bPt1.Pt.Y < bPt2.Pt.Y) - { - return outRec2; - } - else if (bPt1.Pt.X < bPt2.Pt.X) - { - return outRec1; - } - else if (bPt1.Pt.X > bPt2.Pt.X) - { - return outRec2; - } - else if (bPt1.Next == bPt1) - { - return outRec2; - } - else if (bPt2.Next == bPt2) - { - return outRec1; - } - else if (this.FirstIsBottomPt(bPt1, bPt2)) - { - return outRec1; - } - else - { - return outRec2; - } - } - - private bool OutRec1RightOfOutRec2(OutRec outRec1, OutRec outRec2) - { - do - { - outRec1 = outRec1.FirstLeft; - if (outRec1 == outRec2) - { - return true; - } - } - while (outRec1 != null); - - return false; - } - - private OutRec GetOutRec(int idx) - { - OutRec outrec = this.polyOuts[idx]; - while (outrec != this.polyOuts[outrec.Idx]) - { - outrec = this.polyOuts[outrec.Idx]; - } - - return outrec; - } - - private void AppendPolygon(TEdge e1, TEdge e2) - { - OutRec outRec1 = this.polyOuts[e1.OutIdx]; - OutRec outRec2 = this.polyOuts[e2.OutIdx]; - - OutRec holeStateRec; - if (this.OutRec1RightOfOutRec2(outRec1, outRec2)) - { - holeStateRec = outRec2; - } - else if (this.OutRec1RightOfOutRec2(outRec2, outRec1)) - { - holeStateRec = outRec1; - } - else - { - holeStateRec = this.GetLowermostRec(outRec1, outRec2); - } - - // get the start and ends of both output polygons and - // join E2 poly onto E1 poly and delete pointers to E2 ... - OutPt p1_lft = outRec1.Pts; - OutPt p1_rt = p1_lft.Prev; - OutPt p2_lft = outRec2.Pts; - OutPt p2_rt = p2_lft.Prev; - - // join e2 poly onto e1 poly and delete pointers to e2 ... - if (e1.Side == EdgeSide.Left) - { - if (e2.Side == EdgeSide.Left) - { - // z y x a b c - this.ReversePolyPtLinks(p2_lft); - p2_lft.Next = p1_lft; - p1_lft.Prev = p2_lft; - p1_rt.Next = p2_rt; - p2_rt.Prev = p1_rt; - outRec1.Pts = p2_rt; - } - else - { - // x y z a b c - p2_rt.Next = p1_lft; - p1_lft.Prev = p2_rt; - p2_lft.Prev = p1_rt; - p1_rt.Next = p2_lft; - outRec1.Pts = p2_lft; - } - } - else - { - if (e2.Side == EdgeSide.Right) - { - // a b c z y x - this.ReversePolyPtLinks(p2_lft); - p1_rt.Next = p2_rt; - p2_rt.Prev = p1_rt; - p2_lft.Next = p1_lft; - p1_lft.Prev = p2_lft; - } - else - { - // a b c x y z - p1_rt.Next = p2_lft; - p2_lft.Prev = p1_rt; - p1_lft.Prev = p2_rt; - p2_rt.Next = p1_lft; - } - } - - outRec1.BottomPt = null; - if (holeStateRec == outRec2) - { - if (outRec2.FirstLeft != outRec1) - { - outRec1.FirstLeft = outRec2.FirstLeft; - } - - outRec1.IsHole = outRec2.IsHole; - } - - outRec2.Pts = null; - outRec2.BottomPt = null; - - outRec2.FirstLeft = outRec1; - - int okIdx = e1.OutIdx; - int obsoleteIdx = e2.OutIdx; - - e1.OutIdx = Unassigned; // nb: safe because we only get here via AddLocalMaxPoly - e2.OutIdx = Unassigned; - - TEdge e = this.activeEdges; - while (e != null) - { - if (e.OutIdx == obsoleteIdx) - { - e.OutIdx = okIdx; - e.Side = e1.Side; - break; - } - - e = e.NextInAEL; - } - - outRec2.Idx = outRec1.Idx; - } - - private void ReversePolyPtLinks(OutPt pp) - { - if (pp == null) - { - return; - } - - OutPt pp1; - OutPt pp2; - pp1 = pp; - do - { - pp2 = pp1.Next; - pp1.Next = pp1.Prev; - pp1.Prev = pp2; - pp1 = pp2; - } - while (pp1 != pp); - } - - private void IntersectEdges(TEdge e1, TEdge e2, Vector2 pt) - { - // e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before - // e2 in AEL except when e1 is being inserted at the intersection point ... - bool e1Contributing = e1.OutIdx >= 0; - bool e2Contributing = e2.OutIdx >= 0; - - // update winding counts... - // assumes that e1 will be to the Right of e2 ABOVE the intersection - if (e1.PolyTyp == e2.PolyTyp) - { - int oldE1WindCnt = e1.WindCnt; - e1.WindCnt = e2.WindCnt; - e2.WindCnt = oldE1WindCnt; - } - else - { - e1.WindCnt2 = (e1.WindCnt2 == 0) ? 1 : 0; - e2.WindCnt2 = (e2.WindCnt2 == 0) ? 1 : 0; - } - - int e1Wc, e2Wc; - e1Wc = Math.Abs(e1.WindCnt); - e2Wc = Math.Abs(e2.WindCnt); - - if (e1Contributing && e2Contributing) - { - if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1.PolyTyp != e2.PolyTyp)) - { - this.AddLocalMaxPoly(e1, e2, pt); - } - else - { - this.AddOutPt(e1, pt); - this.AddOutPt(e2, pt); - SwapSides(e1, e2); - SwapPolyIndexes(e1, e2); - } - } - else if (e1Contributing) - { - if (e2Wc == 0 || e2Wc == 1) - { - this.AddOutPt(e1, pt); - SwapSides(e1, e2); - SwapPolyIndexes(e1, e2); - } - } - else if (e2Contributing) - { - if (e1Wc == 0 || e1Wc == 1) - { - this.AddOutPt(e2, pt); - SwapSides(e1, e2); - SwapPolyIndexes(e1, e2); - } - } - else if ((e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) - { - // neither edge is currently contributing ... - float e1Wc2, e2Wc2; - - e1Wc2 = Math.Abs(e1.WindCnt2); - e2Wc2 = Math.Abs(e2.WindCnt2); - - if (e1.PolyTyp != e2.PolyTyp) - { - this.AddLocalMinPoly(e1, e2, pt); - } - else if (e1Wc == 1 && e2Wc == 1) - { - if (((e1.PolyTyp == PolyType.Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((e1.PolyTyp == PolyType.Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - { - this.AddLocalMinPoly(e1, e2, pt); - } - } - else - { - SwapSides(e1, e2); - } - } - } - - private void ProcessHorizontals() - { - TEdge horzEdge; // m_SortedEdges; - while (this.PopEdgeFromSEL(out horzEdge)) - { - this.ProcessHorizontal(horzEdge); - } - } - - private void GetHorzDirection(TEdge horzEdge, out Direction dir, out float left, out float right) - { - if (horzEdge.Bot.X < horzEdge.Top.X) - { - left = horzEdge.Bot.X; - right = horzEdge.Top.X; - dir = Direction.LeftToRight; - } - else - { - left = horzEdge.Top.X; - right = horzEdge.Bot.X; - dir = Direction.RightToLeft; - } - } - - private void ProcessHorizontal(TEdge horzEdge) - { - Direction dir; - float horzLeft, horzRight; - bool isOpen = horzEdge.WindDelta == 0; - - this.GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); - - TEdge eLastHorz = horzEdge, eMaxPair = null; - while (eLastHorz.NextInLML != null && IsHorizontal(eLastHorz.NextInLML)) - { - eLastHorz = eLastHorz.NextInLML; - } - - if (eLastHorz.NextInLML == null) - { - eMaxPair = this.GetMaximaPair(eLastHorz); - } - - Maxima currMax = this.maxima; - if (currMax != null) - { - // get the first maxima in range (X) ... - if (dir == Direction.LeftToRight) - { - while (currMax != null && currMax.X <= horzEdge.Bot.X) - { - currMax = currMax.Next; - } - - if (currMax != null && currMax.X >= eLastHorz.Top.X) - { - currMax = null; - } - } - else - { - while (currMax.Next != null && currMax.Next.X < horzEdge.Bot.X) - { - currMax = currMax.Next; - } - - if (currMax.X <= eLastHorz.Top.X) - { - currMax = null; - } - } - } - - OutPt op1 = null; - - // loop through consec. horizontal edges - while (true) - { - bool isLastHorz = horzEdge == eLastHorz; - TEdge e = this.GetNextInAEL(horzEdge, dir); - while (e != null) - { - // this code block inserts extra coords into horizontal edges (in output - // polygons) whereever maxima touch these horizontal edges. This helps - // 'simplifying' polygons (ie if the Simplify property is set). - if (currMax != null) - { - if (dir == Direction.LeftToRight) - { - while (currMax != null && currMax.X < e.Curr.X) - { - if (horzEdge.OutIdx >= 0 && !isOpen) - { - this.AddOutPt(horzEdge, new Vector2(currMax.X, horzEdge.Bot.Y)); - } - - currMax = currMax.Next; - } - } - else - { - while (currMax != null && currMax.X > e.Curr.X) - { - if (horzEdge.OutIdx >= 0 && !isOpen) - { - this.AddOutPt(horzEdge, new Vector2(currMax.X, horzEdge.Bot.Y)); - } - - currMax = currMax.Prev; - } - } - } - - if ((dir == Direction.LeftToRight && e.Curr.X > horzRight) || - (dir == Direction.RightToLeft && e.Curr.X < horzLeft)) - { - break; - } - - // Also break if we've got to the end of an intermediate horizontal edge ... - // nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML != null && - e.Dx < horzEdge.NextInLML.Dx) - { - break; - } - - // note: may be done multiple times - if (horzEdge.OutIdx >= 0 && !isOpen) - { - op1 = this.AddOutPt(horzEdge, e.Curr); - TEdge eNextHorz = this.sortedEdges; - while (eNextHorz != null) - { - if (eNextHorz.OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge.Bot.X, horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X)) - { - OutPt op2 = this.GetLastOutPt(eNextHorz); - this.AddJoin(op2, op1, eNextHorz.Top); - } - - eNextHorz = eNextHorz.NextInSEL; - } - - this.AddGhostJoin(op1, horzEdge.Bot); - } - - // OK, so far we're still in range of the horizontal Edge but make sure - // we're at the last of consec. horizontals when matching with eMaxPair - if (e == eMaxPair && isLastHorz) - { - if (horzEdge.OutIdx >= 0) - { - this.AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge.Top); - } - - this.DeleteFromAEL(horzEdge); - this.DeleteFromAEL(eMaxPair); - return; - } - - if (dir == Direction.LeftToRight) - { - Vector2 pt = new Vector2(e.Curr.X, horzEdge.Curr.Y); - this.IntersectEdges(horzEdge, e, pt); - } - else - { - Vector2 pt = new Vector2(e.Curr.X, horzEdge.Curr.Y); - this.IntersectEdges(e, horzEdge, pt); - } - - TEdge eNext = this.GetNextInAEL(e, dir); - this.SwapPositionsInAEL(horzEdge, e); - e = eNext; - } // end while(e != null) - - // Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (horzEdge.NextInLML == null || !IsHorizontal(horzEdge.NextInLML)) - { - break; - } - - this.UpdateEdgeIntoAEL(ref horzEdge); - if (horzEdge.OutIdx >= 0) - { - this.AddOutPt(horzEdge, horzEdge.Bot); - } - - this.GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); - } - - if (horzEdge.OutIdx >= 0 && op1 == null) - { - op1 = this.GetLastOutPt(horzEdge); - TEdge eNextHorz = this.sortedEdges; - while (eNextHorz != null) - { - if (eNextHorz.OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge.Bot.X, horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X)) - { - OutPt op2 = this.GetLastOutPt(eNextHorz); - this.AddJoin(op2, op1, eNextHorz.Top); - } - - eNextHorz = eNextHorz.NextInSEL; - } - - this.AddGhostJoin(op1, horzEdge.Top); - } - - if (horzEdge.NextInLML != null) - { - if (horzEdge.OutIdx >= 0) - { - op1 = this.AddOutPt(horzEdge, horzEdge.Top); - - this.UpdateEdgeIntoAEL(ref horzEdge); - if (horzEdge.WindDelta == 0) - { - return; - } - - // nb: HorzEdge is no longer horizontal here - TEdge ePrev = horzEdge.PrevInAEL; - TEdge eNext = horzEdge.NextInAEL; - if (ePrev != null && ePrev.Curr.X == horzEdge.Bot.X && - ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta != 0 && - (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && - SlopesEqual(horzEdge, ePrev))) - { - OutPt op2 = this.AddOutPt(ePrev, horzEdge.Bot); - this.AddJoin(op1, op2, horzEdge.Top); - } - else if (eNext != null && eNext.Curr.X == horzEdge.Bot.X && - eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta != 0 && - eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && - SlopesEqual(horzEdge, eNext)) - { - OutPt op2 = this.AddOutPt(eNext, horzEdge.Bot); - this.AddJoin(op1, op2, horzEdge.Top); - } - } - else - { - this.UpdateEdgeIntoAEL(ref horzEdge); - } - } - else - { - if (horzEdge.OutIdx >= 0) - { - this.AddOutPt(horzEdge, horzEdge.Top); - } - - this.DeleteFromAEL(horzEdge); - } - } - - private TEdge GetNextInAEL(TEdge e, Direction direction) - { - return direction == Direction.LeftToRight ? e.NextInAEL : e.PrevInAEL; - } - - private bool IsMaxima(TEdge e, double y) - { - return e != null && e.Top.Y == y && e.NextInLML == null; - } - - private bool IsIntermediate(TEdge e, double y) - { - return e.Top.Y == y && e.NextInLML != null; - } - - private TEdge GetMaximaPair(TEdge e) - { - if ((e.Next.Top == e.Top) && e.Next.NextInLML == null) - { - return e.Next; - } - else if ((e.Prev.Top == e.Top) && e.Prev.NextInLML == null) - { - return e.Prev; - } - else - { - return null; - } - } - - private TEdge GetMaximaPairEx(TEdge e) - { - // as above but returns null if MaxPair isn't in AEL (unless it's horizontal) - TEdge result = this.GetMaximaPair(e); - if (result == null || result.OutIdx == Skip || - ((result.NextInAEL == result.PrevInAEL) && !IsHorizontal(result))) - { - return null; - } - - return result; - } - - private bool ProcessIntersections(float topY) - { - if (this.activeEdges == null) - { - return true; - } - - try - { - this.BuildIntersectList(topY); - if (this.intersectList.Count == 0) - { - return true; - } - - if (this.intersectList.Count == 1 || this.FixupIntersectionOrder()) - { - this.ProcessIntersectList(); - } - else - { - return false; - } - } - catch - { - this.sortedEdges = null; - this.intersectList.Clear(); - throw new ClipperException("ProcessIntersections error"); - } - - this.sortedEdges = null; - return true; - } - - private void BuildIntersectList(float topY) - { - if (this.activeEdges == null) - { - return; - } - - // prepare for sorting ... - TEdge e = this.activeEdges; - this.sortedEdges = e; - while (e != null) - { - e.PrevInSEL = e.PrevInAEL; - e.NextInSEL = e.NextInAEL; - e.Curr.X = TopX(e, topY); - e = e.NextInAEL; - } - - // bubblesort ... - bool isModified = true; - while (isModified && this.sortedEdges != null) - { - isModified = false; - e = this.sortedEdges; - while (e.NextInSEL != null) - { - TEdge eNext = e.NextInSEL; - Vector2 pt; - if (e.Curr.X > eNext.Curr.X) - { - this.IntersectPoint(e, eNext, out pt); - if (pt.Y < topY) - { - pt = new Vector2(TopX(e, topY), topY); - } - - IntersectNode newNode = new IntersectNode(); - newNode.Edge1 = e; - newNode.Edge2 = eNext; - newNode.Pt = pt; - this.intersectList.Add(newNode); - - this.SwapPositionsInSEL(e, eNext); - isModified = true; - } - else - { - e = eNext; - } - } - - if (e.PrevInSEL != null) - { - e.PrevInSEL.NextInSEL = null; - } - else - { - break; - } - } - - this.sortedEdges = null; - } - - private bool EdgesAdjacent(IntersectNode inode) - { - return (inode.Edge1.NextInSEL == inode.Edge2) || - (inode.Edge1.PrevInSEL == inode.Edge2); - } - - private bool FixupIntersectionOrder() - { - // pre-condition: intersections are sorted bottom-most first. - // Now it's crucial that intersections are made only between adjacent edges, - // so to ensure this the order of intersections may need adjusting ... - this.intersectList.Sort(this.intersectNodeComparer); - - this.CopyAELToSEL(); - int cnt = this.intersectList.Count; - for (int i = 0; i < cnt; i++) - { - if (!this.EdgesAdjacent(this.intersectList[i])) - { - int j = i + 1; - while (j < cnt && !this.EdgesAdjacent(this.intersectList[j])) - { - j++; - } - - if (j == cnt) - { - return false; - } - - IntersectNode tmp = this.intersectList[i]; - this.intersectList[i] = this.intersectList[j]; - this.intersectList[j] = tmp; - } - - this.SwapPositionsInSEL(this.intersectList[i].Edge1, this.intersectList[i].Edge2); - } - - return true; - } - - private void ProcessIntersectList() - { - for (int i = 0; i < this.intersectList.Count; i++) - { - IntersectNode iNode = this.intersectList[i]; - { - this.IntersectEdges(iNode.Edge1, iNode.Edge2, iNode.Pt); - this.SwapPositionsInAEL(iNode.Edge1, iNode.Edge2); - } - } - - this.intersectList.Clear(); - } - - private void IntersectPoint(TEdge edge1, TEdge edge2, out Vector2 ip) - { - ip = default(Vector2); - double b1, b2; - - // nb: with very large coordinate values, it's possible for SlopesEqual() to - // return false but for the edge.Dx value be equal due to double precision rounding. - if (edge1.Dx == edge2.Dx) - { - ip.Y = edge1.Curr.Y; - ip.X = TopX(edge1, ip.Y); - return; - } - - if (edge1.Delta.X == 0) - { - ip.X = edge1.Bot.X; - if (IsHorizontal(edge2)) - { - ip.Y = edge2.Bot.Y; - } - else - { - b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx); - ip.Y = Round((ip.X / edge2.Dx) + b2); - } - } - else if (edge2.Delta.X == 0) - { - ip.X = edge2.Bot.X; - if (IsHorizontal(edge1)) - { - ip.Y = edge1.Bot.Y; - } - else - { - b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx); - ip.Y = Round((ip.X / edge1.Dx) + b1); - } - } - else - { - b1 = edge1.Bot.X - (edge1.Bot.Y * edge1.Dx); - b2 = edge2.Bot.X - (edge2.Bot.Y * edge2.Dx); - double q = (b2 - b1) / (edge1.Dx - edge2.Dx); - ip.Y = Round(q); - if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx)) - { - ip.X = Round((edge1.Dx * q) + b1); - } - else - { - ip.X = Round((edge2.Dx * q) + b2); - } - } - - if (ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y) - { - if (edge1.Top.Y > edge2.Top.Y) - { - ip.Y = edge1.Top.Y; - } - else - { - ip.Y = edge2.Top.Y; - } - - if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx)) - { - ip.X = TopX(edge1, ip.Y); - } - else - { - ip.X = TopX(edge2, ip.Y); - } - } - - // finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... - if (ip.Y > edge1.Curr.Y) - { - ip.Y = edge1.Curr.Y; - - // better to use the more vertical edge to derive X ... - if (Math.Abs(edge1.Dx) > Math.Abs(edge2.Dx)) - { - ip.X = TopX(edge2, ip.Y); - } - else - { - ip.X = TopX(edge1, ip.Y); - } - } - } - - private void ProcessEdgesAtTopOfScanbeam(float topY) - { - TEdge e = this.activeEdges; - while (e != null) - { - // 1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - bool isMaximaEdge = this.IsMaxima(e, topY); - - if (isMaximaEdge) - { - TEdge eMaxPair = this.GetMaximaPairEx(e); - isMaximaEdge = eMaxPair == null || !IsHorizontal(eMaxPair); - } - - if (isMaximaEdge) - { - TEdge ePrev = e.PrevInAEL; - this.DoMaxima(e); - if (ePrev == null) - { - e = this.activeEdges; - } - else - { - e = ePrev.NextInAEL; - } - } - else - { - // 2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... - if (this.IsIntermediate(e, topY) && IsHorizontal(e.NextInLML)) - { - this.UpdateEdgeIntoAEL(ref e); - if (e.OutIdx >= 0) - { - this.AddOutPt(e, e.Bot); - } - - this.AddEdgeToSEL(e); - } - else - { - e.Curr.X = TopX(e, topY); - e.Curr.Y = topY; - } - - e = e.NextInAEL; - } - } - - // 3. Process horizontals at the Top of the scanbeam ... - this.ProcessHorizontals(); - this.maxima = null; - - // 4. Promote intermediate vertices ... - e = this.activeEdges; - while (e != null) - { - if (this.IsIntermediate(e, topY)) - { - OutPt op = null; - if (e.OutIdx >= 0) - { - op = this.AddOutPt(e, e.Top); - } - - this.UpdateEdgeIntoAEL(ref e); - - // if output polygons share an edge, they'll need joining later ... - TEdge ePrev = e.PrevInAEL; - TEdge eNext = e.NextInAEL; - if (ePrev != null && ePrev.Curr.X == e.Bot.X && - ePrev.Curr.Y == e.Bot.Y && op != null && - ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && - SlopesEqual(e.Curr, e.Top, ePrev.Curr, ePrev.Top) && - (e.WindDelta != 0) && (ePrev.WindDelta != 0)) - { - OutPt op2 = this.AddOutPt(ePrev, e.Bot); - this.AddJoin(op, op2, e.Top); - } - else if (eNext != null && eNext.Curr.X == e.Bot.X && - eNext.Curr.Y == e.Bot.Y && op != null && - eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && - SlopesEqual(e.Curr, e.Top, eNext.Curr, eNext.Top) && - (e.WindDelta != 0) && (eNext.WindDelta != 0)) - { - OutPt op2 = this.AddOutPt(eNext, e.Bot); - this.AddJoin(op, op2, e.Top); - } - } - - e = e.NextInAEL; - } - } - - private void DoMaxima(TEdge e) - { - TEdge eMaxPair = this.GetMaximaPairEx(e); - if (eMaxPair == null) - { - if (e.OutIdx >= 0) - { - this.AddOutPt(e, e.Top); - } - - this.DeleteFromAEL(e); - return; - } - - TEdge eNext = e.NextInAEL; - while (eNext != null && eNext != eMaxPair) - { - this.IntersectEdges(e, eNext, e.Top); - this.SwapPositionsInAEL(e, eNext); - eNext = e.NextInAEL; - } - - if (e.OutIdx == Unassigned && eMaxPair.OutIdx == Unassigned) - { - this.DeleteFromAEL(e); - this.DeleteFromAEL(eMaxPair); - } - else if (e.OutIdx >= 0 && eMaxPair.OutIdx >= 0) - { - if (e.OutIdx >= 0) - { - this.AddLocalMaxPoly(e, eMaxPair, e.Top); - } - - this.DeleteFromAEL(e); - this.DeleteFromAEL(eMaxPair); - } - else - { - throw new ClipperException("DoMaxima error"); - } - } - - private int PointCount(OutPt pts) - { - if (pts == null) - { - return 0; - } - - int result = 0; - OutPt p = pts; - do - { - result++; - p = p.Next; - } - while (p != pts); - return result; - } - - private void BuildResult2(PolyTree polytree) - { - polytree.Clear(); - - // add each output polygon/contour to polytree ... - polytree.AllPolys.Capacity = this.polyOuts.Count; - for (int i = 0; i < this.polyOuts.Count; i++) - { - OutRec outRec = this.polyOuts[i]; - int cnt = this.PointCount(outRec.Pts); - if ((outRec.IsOpen && cnt < 2) || - (!outRec.IsOpen && cnt < 3)) - { - continue; - } - - FixHoleLinkage(outRec); - PolyNode pn = new PolyNode(); - pn.SourcePath = outRec.SourcePath; - polytree.AllPolys.Add(pn); - outRec.PolyNode = pn; - pn.Polygon.Capacity = cnt; - OutPt op = outRec.Pts.Prev; - for (int j = 0; j < cnt; j++) - { - pn.Polygon.Add(op.Pt); - op = op.Prev; - } - } - - // fixup PolyNode links etc ... - polytree.Children.Capacity = this.polyOuts.Count; - for (int i = 0; i < this.polyOuts.Count; i++) - { - OutRec outRec = this.polyOuts[i]; - if (outRec.PolyNode == null) - { - continue; - } - else if (outRec.IsOpen) - { - outRec.PolyNode.IsOpen = true; - polytree.AddChild(outRec.PolyNode); - } - else if (outRec.FirstLeft != null && - outRec.FirstLeft.PolyNode != null) - { - outRec.FirstLeft.PolyNode.AddChild(outRec.PolyNode); - } - else - { - polytree.AddChild(outRec.PolyNode); - } - } - } - - private void FixupOutPolyline(OutRec outrec) - { - OutPt pp = outrec.Pts; - OutPt lastPP = pp.Prev; - while (pp != lastPP) - { - pp = pp.Next; - if (pp.Pt == pp.Prev.Pt) - { - if (pp == lastPP) - { - lastPP = pp.Prev; - } - - OutPt tmpPP = pp.Prev; - tmpPP.Next = pp.Next; - pp.Next.Prev = tmpPP; - pp = tmpPP; - } - } - - if (pp == pp.Prev) - { - outrec.Pts = null; - } - } - - private void FixupOutPolygon(OutRec outRec) - { - // FixupOutPolygon() - removes duplicate points and simplifies consecutive - // parallel edges by removing the middle vertex. - OutPt lastOK = null; - outRec.BottomPt = null; - OutPt pp = outRec.Pts; - while (true) - { - if (pp.Prev == pp || pp.Prev == pp.Next) - { - outRec.Pts = null; - return; - } - - // test for duplicate points and collinear edges ... - if ((pp.Pt == pp.Next.Pt) || (pp.Pt == pp.Prev.Pt) || - SlopesEqual(pp.Prev.Pt, pp.Pt, pp.Next.Pt)) - { - lastOK = null; - pp.Prev.Next = pp.Next; - pp.Next.Prev = pp.Prev; - pp = pp.Prev; - } - else if (pp == lastOK) - { - break; - } - else - { - if (lastOK == null) - { - lastOK = pp; - } - - pp = pp.Next; - } - } - - outRec.Pts = pp; - } - - private OutPt DupOutPt(OutPt outPt, bool insertAfter) - { - OutPt result = new OutPt(); - result.Pt = outPt.Pt; - result.Idx = outPt.Idx; - if (insertAfter) - { - result.Next = outPt.Next; - result.Prev = outPt; - outPt.Next.Prev = result; - outPt.Next = result; - } - else - { - result.Prev = outPt.Prev; - result.Next = outPt; - outPt.Prev.Next = result; - outPt.Prev = result; - } - - return result; - } - - private bool GetOverlap(float a1, float a2, float b1, float b2, out float left, out float right) - { - if (a1 < a2) - { - if (b1 < b2) - { - left = Math.Max(a1, b1); - right = Math.Min(a2, b2); - } - else - { - left = Math.Max(a1, b2); - right = Math.Min(a2, b1); - } - } - else - { - if (b1 < b2) - { - left = Math.Max(a2, b1); - right = Math.Min(a1, b2); - } - else - { - left = Math.Max(a2, b2); - right = Math.Min(a1, b1); - } - } - - return left < right; - } - - private bool JoinHorz(OutPt op1, OutPt op1b, OutPt op2, OutPt op2b, Vector2 pt, bool discardLeft) - { - Direction dir1 = op1.Pt.X > op1b.Pt.X ? Direction.RightToLeft : Direction.LeftToRight; - Direction dir2 = op2.Pt.X > op2b.Pt.X ? Direction.RightToLeft : Direction.LeftToRight; - if (dir1 == dir2) - { - return false; - } - - // When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - // want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - // So, to facilitate this while inserting Op1b and Op2b ... - // when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - // otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (dir1 == Direction.LeftToRight) - { - while (op1.Next.Pt.X <= pt.X && - op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == pt.Y) - { - op1 = op1.Next; - } - - if (discardLeft && (op1.Pt.X != pt.X)) - { - op1 = op1.Next; - } - - op1b = this.DupOutPt(op1, !discardLeft); - if (op1b.Pt != pt) - { - op1 = op1b; - op1.Pt = pt; - op1b = this.DupOutPt(op1, !discardLeft); - } - } - else - { - while (op1.Next.Pt.X >= pt.X && - op1.Next.Pt.X <= op1.Pt.X && - op1.Next.Pt.Y == pt.Y) - { - op1 = op1.Next; - } - - if (!discardLeft && (op1.Pt.X != pt.X)) - { - op1 = op1.Next; - } - - op1b = this.DupOutPt(op1, discardLeft); - if (op1b.Pt != pt) - { - op1 = op1b; - op1.Pt = pt; - op1b = this.DupOutPt(op1, discardLeft); - } - } - - if (dir2 == Direction.LeftToRight) - { - while (op2.Next.Pt.X <= pt.X && - op2.Next.Pt.X >= op2.Pt.X && - op2.Next.Pt.Y == pt.Y) - { - op2 = op2.Next; - } - - if (discardLeft && (op2.Pt.X != pt.X)) - { - op2 = op2.Next; - } - - op2b = this.DupOutPt(op2, !discardLeft); - if (op2b.Pt != pt) - { - op2 = op2b; - op2.Pt = pt; - op2b = this.DupOutPt(op2, !discardLeft); - } - } - else - { - while (op2.Next.Pt.X >= pt.X && - op2.Next.Pt.X <= op2.Pt.X && - op2.Next.Pt.Y == pt.Y) - { - op2 = op2.Next; - } - - if (!discardLeft && (op2.Pt.X != pt.X)) - { - op2 = op2.Next; - } - - op2b = this.DupOutPt(op2, discardLeft); - if (op2b.Pt != pt) - { - op2 = op2b; - op2.Pt = pt; - op2b = this.DupOutPt(op2, discardLeft); - } - } - - if ((dir1 == Direction.LeftToRight) == discardLeft) - { - op1.Prev = op2; - op2.Next = op1; - op1b.Next = op2b; - op2b.Prev = op1b; - } - else - { - op1.Next = op2; - op2.Prev = op1; - op1b.Prev = op2b; - op2b.Next = op1b; - } - - return true; - } - - private bool JoinPoints(Join j, OutRec outRec1, OutRec outRec2) - { - OutPt op1 = j.OutPt1, op1b; - OutPt op2 = j.OutPt2, op2b; - - // There are 3 kinds of joins for output polygons ... - // 1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - // along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - // 2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - // location at the Bottom of the overlapping segment (& Join.OffPt is above). - // 3. StrictlySimple joins where edges touch but are not collinear and where - // Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - bool isHorizontal = j.OutPt1.Pt.Y == j.OffPt.Y; - - if (isHorizontal && (j.OffPt == j.OutPt1.Pt) && (j.OffPt == j.OutPt2.Pt)) - { - // Strictly Simple join ... - if (outRec1 != outRec2) - { - return false; - } - - op1b = j.OutPt1.Next; - while (op1b != op1 && (op1b.Pt == j.OffPt)) - { - op1b = op1b.Next; - } - - bool reverse1 = op1b.Pt.Y > j.OffPt.Y; - op2b = j.OutPt2.Next; - while (op2b != op2 && (op2b.Pt == j.OffPt)) - { - op2b = op2b.Next; - } - - bool reverse2 = op2b.Pt.Y > j.OffPt.Y; - if (reverse1 == reverse2) - { - return false; - } - - if (reverse1) - { - op1b = this.DupOutPt(op1, false); - op2b = this.DupOutPt(op2, true); - op1.Prev = op2; - op2.Next = op1; - op1b.Next = op2b; - op2b.Prev = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - else - { - op1b = this.DupOutPt(op1, true); - op2b = this.DupOutPt(op2, false); - op1.Next = op2; - op2.Prev = op1; - op1b.Prev = op2b; - op2b.Next = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - } - else if (isHorizontal) - { - // treat horizontal joins differently to non-horizontal joins since with - // them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - // may be anywhere along the horizontal edge. - op1b = op1; - while (op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2) - { - op1 = op1.Prev; - } - - while (op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2) - { - op1b = op1b.Next; - } - - if (op1b.Next == op1 || op1b.Next == op2) - { - return false; // a flat 'polygon' - } - - op2b = op2; - while (op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b) - { - op2 = op2.Prev; - } - - while (op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1) - { - op2b = op2b.Next; - } - - if (op2b.Next == op2 || op2b.Next == op1) - { - return false; // a flat 'polygon' - } - - float left, right; - - // Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges - if (!this.GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, out left, out right)) - { - return false; - } - - // DiscardLeftSide: when overlapping edges are joined, a spike will created - // which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - // on the discard Side as either may still be needed for other joins ... - Vector2 pt; - bool discardLeftSide; - if (op1.Pt.X >= left && op1.Pt.X <= right) - { - pt = op1.Pt; - discardLeftSide = op1.Pt.X > op1b.Pt.X; - } - else if (op2.Pt.X >= left && op2.Pt.X <= right) - { - pt = op2.Pt; - discardLeftSide = op2.Pt.X > op2b.Pt.X; - } - else if (op1b.Pt.X >= left && op1b.Pt.X <= right) - { - pt = op1b.Pt; - discardLeftSide = op1b.Pt.X > op1.Pt.X; - } - else - { - pt = op2b.Pt; - discardLeftSide = op2b.Pt.X > op2.Pt.X; - } - - j.OutPt1 = op1; - j.OutPt2 = op2; - return this.JoinHorz(op1, op1b, op2, op2b, pt, discardLeftSide); - } - else - { - // nb: For non-horizontal joins ... - // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y - // 2. Jr.OutPt1.Pt > Jr.OffPt.Y - - // make sure the polygons are correctly oriented ... - op1b = op1.Next; - while ((op1b.Pt == op1.Pt) && (op1b != op1)) - { - op1b = op1b.Next; - } - - bool reverse1 = (op1b.Pt.Y > op1.Pt.Y) || !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt); - if (reverse1) - { - op1b = op1.Prev; - while ((op1b.Pt == op1.Pt) && (op1b != op1)) - { - op1b = op1b.Prev; - } - - if ((op1b.Pt.Y > op1.Pt.Y) || - !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt)) - { - return false; - } - } - - op2b = op2.Next; - while ((op2b.Pt == op2.Pt) && (op2b != op2)) - { - op2b = op2b.Next; - } - - bool reverse2 = (op2b.Pt.Y > op2.Pt.Y) || !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt); - if (reverse2) - { - op2b = op2.Prev; - while ((op2b.Pt == op2.Pt) && (op2b != op2)) - { - op2b = op2b.Prev; - } - - if ((op2b.Pt.Y > op2.Pt.Y) || - !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt)) - { - return false; - } - } - - if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || - ((outRec1 == outRec2) && (reverse1 == reverse2))) - { - return false; - } - - if (reverse1) - { - op1b = this.DupOutPt(op1, false); - op2b = this.DupOutPt(op2, true); - op1.Prev = op2; - op2.Next = op1; - op1b.Next = op2b; - op2b.Prev = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - else - { - op1b = this.DupOutPt(op1, true); - op2b = this.DupOutPt(op2, false); - op1.Next = op2; - op2.Prev = op1; - op1b.Prev = op2b; - op2b.Next = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - } - } - - private void FixupFirstLefts1(OutRec oldOutRec, OutRec newOutRec) - { - foreach (OutRec outRec in this.polyOuts) - { - OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); - if (outRec.Pts != null && firstLeft == oldOutRec) - { - if (Poly2ContainsPoly1(outRec.Pts, newOutRec.Pts)) - { - outRec.FirstLeft = newOutRec; - } - } - } - } - - private void FixupFirstLefts2(OutRec innerOutRec, OutRec outerOutRec) - { - // A polygon has split into two such that one is now the inner of the other. - // It's possible that these polygons now wrap around other polygons, so check - // every polygon that's also contained by OuterOutRec's FirstLeft container - // (including nil) to see if they've become inner to the new inner polygon ... - OutRec orfl = outerOutRec.FirstLeft; - foreach (OutRec outRec in this.polyOuts) - { - if (outRec.Pts == null || outRec == outerOutRec || outRec == innerOutRec) - { - continue; - } - - OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); - if (firstLeft != orfl && firstLeft != innerOutRec && firstLeft != outerOutRec) - { - continue; - } - - if (Poly2ContainsPoly1(outRec.Pts, innerOutRec.Pts)) - { - outRec.FirstLeft = innerOutRec; - } - else if (Poly2ContainsPoly1(outRec.Pts, outerOutRec.Pts)) - { - outRec.FirstLeft = outerOutRec; - } - else if (outRec.FirstLeft == innerOutRec || outRec.FirstLeft == outerOutRec) - { - outRec.FirstLeft = orfl; - } - } - } - - private void FixupFirstLefts3(OutRec oldOutRec, OutRec newOutRec) - { - // same as FixupFirstLefts1 but doesn't call Poly2ContainsPoly1() - foreach (OutRec outRec in this.polyOuts) - { - OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); - if (outRec.Pts != null && outRec.FirstLeft == oldOutRec) - { - outRec.FirstLeft = newOutRec; - } - } - } - - private void JoinCommonEdges() - { - for (int i = 0; i < this.joins.Count; i++) - { - Join join = this.joins[i]; - - OutRec outRec1 = this.GetOutRec(join.OutPt1.Idx); - OutRec outRec2 = this.GetOutRec(join.OutPt2.Idx); - - if (outRec1.Pts == null || outRec2.Pts == null) - { - continue; - } - - if (outRec1.IsOpen || outRec2.IsOpen) - { - continue; - } - - // get the polygon fragment with the correct hole state (FirstLeft) - // before calling JoinPoints() ... - OutRec holeStateRec; - if (outRec1 == outRec2) - { - holeStateRec = outRec1; - } - else if (this.OutRec1RightOfOutRec2(outRec1, outRec2)) - { - holeStateRec = outRec2; - } - else if (this.OutRec1RightOfOutRec2(outRec2, outRec1)) - { - holeStateRec = outRec1; - } - else - { - holeStateRec = this.GetLowermostRec(outRec1, outRec2); - } - - if (!this.JoinPoints(join, outRec1, outRec2)) - { - continue; - } - - if (outRec1 == outRec2) - { - // instead of joining two polygons, we've just created a new one by - // splitting one polygon into two. - outRec1.Pts = join.OutPt1; - outRec1.BottomPt = null; - outRec2 = this.CreateOutRec(); - outRec2.Pts = join.OutPt2; - - // update all OutRec2.Pts Idx's ... - this.UpdateOutPtIdxs(outRec2); - - if (Poly2ContainsPoly1(outRec2.Pts, outRec1.Pts)) - { - // outRec1 contains outRec2 ... - outRec2.IsHole = !outRec1.IsHole; - outRec2.FirstLeft = outRec1; - - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outRec2, outRec1); - } - } - else if (Poly2ContainsPoly1(outRec1.Pts, outRec2.Pts)) - { - // outRec2 contains outRec1 ... - outRec2.IsHole = outRec1.IsHole; - outRec1.IsHole = !outRec2.IsHole; - outRec2.FirstLeft = outRec1.FirstLeft; - outRec1.FirstLeft = outRec2; - - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outRec1, outRec2); - } - } - else - { - // the 2 polygons are completely separate ... - outRec2.IsHole = outRec1.IsHole; - outRec2.FirstLeft = outRec1.FirstLeft; - - // fixup FirstLeft pointers that may need reassigning to OutRec2 - if (this.usingPolyTree) - { - this.FixupFirstLefts1(outRec1, outRec2); - } - } - } - else - { - // joined 2 polygons together ... - outRec2.Pts = null; - outRec2.BottomPt = null; - outRec2.Idx = outRec1.Idx; - - outRec1.IsHole = holeStateRec.IsHole; - if (holeStateRec == outRec2) - { - outRec1.FirstLeft = outRec2.FirstLeft; - } - - outRec2.FirstLeft = outRec1; - - // fixup FirstLeft pointers that may need reassigning to OutRec1 - if (this.usingPolyTree) - { - this.FixupFirstLefts3(outRec2, outRec1); - } - } - } - } - - private void UpdateOutPtIdxs(OutRec outrec) - { - OutPt op = outrec.Pts; - do - { - op.Idx = outrec.Idx; - op = op.Prev; - } - while (op != outrec.Pts); - } - - private void DoSimplePolygons() - { - int i = 0; - while (i < this.polyOuts.Count) - { - OutRec outrec = this.polyOuts[i++]; - OutPt op = outrec.Pts; - if (op == null || outrec.IsOpen) - { - continue; - } - - do - { - // for each Pt in Polygon until duplicate found do ... - OutPt op2 = op.Next; - while (op2 != outrec.Pts) - { - if ((op.Pt == op2.Pt) && op2.Next != op && op2.Prev != op) - { - // split the polygon into two ... - OutPt op3 = op.Prev; - OutPt op4 = op2.Prev; - op.Prev = op4; - op4.Next = op; - op2.Prev = op3; - op3.Next = op2; - - outrec.Pts = op; - OutRec outrec2 = this.CreateOutRec(); - outrec2.Pts = op2; - this.UpdateOutPtIdxs(outrec2); - if (Poly2ContainsPoly1(outrec2.Pts, outrec.Pts)) - { - // OutRec2 is contained by OutRec1 ... - outrec2.IsHole = !outrec.IsHole; - outrec2.FirstLeft = outrec; - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outrec2, outrec); - } - } - else - if (Poly2ContainsPoly1(outrec.Pts, outrec2.Pts)) - { - // OutRec1 is contained by OutRec2 ... - outrec2.IsHole = outrec.IsHole; - outrec.IsHole = !outrec2.IsHole; - outrec2.FirstLeft = outrec.FirstLeft; - outrec.FirstLeft = outrec2; - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outrec, outrec2); - } - } - else - { - // the 2 polygons are separate ... - outrec2.IsHole = outrec.IsHole; - outrec2.FirstLeft = outrec.FirstLeft; - if (this.usingPolyTree) - { - this.FixupFirstLefts1(outrec, outrec2); - } - } - - op2 = op; // ie get ready for the next iteration - } - - op2 = op2.Next; - } - - op = op.Next; - } - while (op != outrec.Pts); - } - } - - private double Area(OutRec outRec) - { - return this.Area(outRec.Pts); - } - - private double Area(OutPt op) - { - OutPt opFirst = op; - if (op == null) - { - return 0; - } - - double a = 0; - do - { - a = a + ((op.Prev.Pt.X + op.Pt.X) * (op.Prev.Pt.Y - op.Pt.Y)); - op = op.Next; - } - while (op != opFirst); - - return a * 0.5; - } - - private void SetDx(TEdge e) - { - e.Delta.X = e.Top.X - e.Bot.X; - e.Delta.Y = e.Top.Y - e.Bot.Y; - if (e.Delta.Y == 0) - { - e.Dx = HorizontalDeltaLimit; - } - else - { - e.Dx = e.Delta.X / e.Delta.Y; - } - } - - private void InsertLocalMinima(LocalMinima newLm) - { - if (this.minimaList == null) - { - this.minimaList = newLm; - } - else if (newLm.Y >= this.minimaList.Y) - { - newLm.Next = this.minimaList; - this.minimaList = newLm; - } - else - { - LocalMinima tmpLm = this.minimaList; - while (tmpLm.Next != null && (newLm.Y < tmpLm.Next.Y)) - { - tmpLm = tmpLm.Next; - } - - newLm.Next = tmpLm.Next; - tmpLm.Next = newLm; - } - } - - private bool PopLocalMinima(float y, out LocalMinima current) - { - current = this.currentLM; - if (this.currentLM != null && this.currentLM.Y == y) - { - this.currentLM = this.currentLM.Next; - return true; - } - - return false; - } - - private void Reset() - { - this.currentLM = this.minimaList; - if (this.currentLM == null) - { - return; // ie nothing to process - } - - // reset all edges ... - this.scanbeam = null; - LocalMinima lm = this.minimaList; - while (lm != null) - { - this.InsertScanbeam(lm.Y); - TEdge e = lm.LeftBound; - if (e != null) - { - e.Curr = e.Bot; - e.OutIdx = Unassigned; - } - - e = lm.RightBound; - if (e != null) - { - e.Curr = e.Bot; - e.OutIdx = Unassigned; - } - - lm = lm.Next; - } - - this.activeEdges = null; - } - - private void InsertScanbeam(float y) - { - // single-linked list: sorted descending, ignoring dups. - if (this.scanbeam == null) - { - this.scanbeam = new Scanbeam(); - this.scanbeam.Next = null; - this.scanbeam.Y = y; - } - else if (y > this.scanbeam.Y) - { - Scanbeam newSb = new Scanbeam(); - newSb.Y = y; - newSb.Next = this.scanbeam; - this.scanbeam = newSb; - } - else - { - Scanbeam sb2 = this.scanbeam; - while (sb2.Next != null && (y <= sb2.Next.Y)) - { - sb2 = sb2.Next; - } - - if (y == sb2.Y) - { - return; // ie ignores duplicates - } - - Scanbeam newSb = new Scanbeam(); - newSb.Y = y; - newSb.Next = sb2.Next; - sb2.Next = newSb; - } - } - - private bool PopScanbeam(out float y) - { - if (this.scanbeam == null) - { - y = 0; - return false; - } - - y = this.scanbeam.Y; - this.scanbeam = this.scanbeam.Next; - return true; - } - - private bool LocalMinimaPending() - { - return this.currentLM != null; - } - - private OutRec CreateOutRec() - { - OutRec result = new OutRec(); - result.Idx = Unassigned; - result.IsHole = false; - result.IsOpen = false; - result.FirstLeft = null; - result.Pts = null; - result.BottomPt = null; - result.PolyNode = null; - this.polyOuts.Add(result); - result.Idx = this.polyOuts.Count - 1; - return result; - } - - private void DisposeOutRec(int index) - { - OutRec outRec = this.polyOuts[index]; - outRec.Pts = null; - outRec = null; - this.polyOuts[index] = null; - } - - private void UpdateEdgeIntoAEL(ref TEdge e) - { - if (e.NextInLML == null) - { - throw new ClipperException("UpdateEdgeIntoAEL: invalid call"); - } - - TEdge aelPrev = e.PrevInAEL; - TEdge aelNext = e.NextInAEL; - e.NextInLML.OutIdx = e.OutIdx; - if (aelPrev != null) - { - aelPrev.NextInAEL = e.NextInLML; - } - else - { - this.activeEdges = e.NextInLML; - } - - if (aelNext != null) - { - aelNext.PrevInAEL = e.NextInLML; - } - - e.NextInLML.Side = e.Side; - e.NextInLML.WindDelta = e.WindDelta; - e.NextInLML.WindCnt = e.WindCnt; - e.NextInLML.WindCnt2 = e.WindCnt2; - e = e.NextInLML; - e.Curr = e.Bot; - e.PrevInAEL = aelPrev; - e.NextInAEL = aelNext; - if (!IsHorizontal(e)) - { - this.InsertScanbeam(e.Top.Y); - } - } - - private void SwapPositionsInAEL(TEdge edge1, TEdge edge2) - { - // check that one or other edge hasn't already been removed from AEL ... - if (edge1.NextInAEL == edge1.PrevInAEL || - edge2.NextInAEL == edge2.PrevInAEL) - { - return; - } - - if (edge1.NextInAEL == edge2) - { - TEdge next = edge2.NextInAEL; - if (next != null) - { - next.PrevInAEL = edge1; - } - - TEdge prev = edge1.PrevInAEL; - if (prev != null) - { - prev.NextInAEL = edge2; - } - - edge2.PrevInAEL = prev; - edge2.NextInAEL = edge1; - edge1.PrevInAEL = edge2; - edge1.NextInAEL = next; - } - else if (edge2.NextInAEL == edge1) - { - TEdge next = edge1.NextInAEL; - if (next != null) - { - next.PrevInAEL = edge2; - } - - TEdge prev = edge2.PrevInAEL; - if (prev != null) - { - prev.NextInAEL = edge1; - } - - edge1.PrevInAEL = prev; - edge1.NextInAEL = edge2; - edge2.PrevInAEL = edge1; - edge2.NextInAEL = next; - } - else - { - TEdge next = edge1.NextInAEL; - TEdge prev = edge1.PrevInAEL; - edge1.NextInAEL = edge2.NextInAEL; - if (edge1.NextInAEL != null) - { - edge1.NextInAEL.PrevInAEL = edge1; - } - - edge1.PrevInAEL = edge2.PrevInAEL; - if (edge1.PrevInAEL != null) - { - edge1.PrevInAEL.NextInAEL = edge1; - } - - edge2.NextInAEL = next; - if (edge2.NextInAEL != null) - { - edge2.NextInAEL.PrevInAEL = edge2; - } - - edge2.PrevInAEL = prev; - if (edge2.PrevInAEL != null) - { - edge2.PrevInAEL.NextInAEL = edge2; - } - } - - if (edge1.PrevInAEL == null) - { - this.activeEdges = edge1; - } - else if (edge2.PrevInAEL == null) - { - this.activeEdges = edge2; - } - } - - private void DeleteFromAEL(TEdge e) - { - TEdge aelPrev = e.PrevInAEL; - TEdge aelNext = e.NextInAEL; - if (aelPrev == null && aelNext == null && (e != this.activeEdges)) - { - return; // already deleted - } - - if (aelPrev != null) - { - aelPrev.NextInAEL = aelNext; - } - else - { - this.activeEdges = aelNext; - } - - if (aelNext != null) - { - aelNext.PrevInAEL = aelPrev; - } - - e.NextInAEL = null; - e.PrevInAEL = null; - } - - private void InitEdge2(TEdge e, PolyType polyType) - { - if (e.Curr.Y >= e.Next.Curr.Y) - { - e.Bot = e.Curr; - e.Top = e.Next.Curr; - } - else - { - e.Top = e.Curr; - e.Bot = e.Next.Curr; - } - - this.SetDx(e); - e.PolyTyp = polyType; - } - - private TEdge ProcessBound(TEdge edge, bool leftBoundIsForward) - { - TEdge eStart, result = edge; - TEdge horz; - - if (result.OutIdx == Skip) - { - // check if there are edges beyond the skip edge in the bound and if so - // create another LocMin and calling ProcessBound once more ... - edge = result; - if (leftBoundIsForward) - { - while (edge.Top.Y == edge.Next.Bot.Y) - { - edge = edge.Next; - } - - while (edge != result && edge.Dx == HorizontalDeltaLimit) - { - edge = edge.Prev; - } - } - else - { - while (edge.Top.Y == edge.Prev.Bot.Y) - { - edge = edge.Prev; - } - - while (edge != result && edge.Dx == HorizontalDeltaLimit) - { - edge = edge.Next; - } - } - - if (edge == result) - { - if (leftBoundIsForward) - { - result = edge.Next; - } - else - { - result = edge.Prev; - } - } - else - { - // there are more edges in the bound beyond result starting with E - if (leftBoundIsForward) - { - edge = result.Next; - } - else - { - edge = result.Prev; - } - - LocalMinima locMin = new LocalMinima(); - locMin.Next = null; - locMin.Y = edge.Bot.Y; - locMin.LeftBound = null; - locMin.RightBound = edge; - edge.WindDelta = 0; - result = this.ProcessBound(edge, leftBoundIsForward); - this.InsertLocalMinima(locMin); - } - - return result; - } - - if (edge.Dx == HorizontalDeltaLimit) - { - // We need to be careful with open paths because this may not be a - // true local minima (ie E may be following a skip edge). - // Also, consecutive horz. edges may start heading left before going right. - if (leftBoundIsForward) - { - eStart = edge.Prev; - } - else - { - eStart = edge.Next; - } - - // ie an adjoining horizontal skip edge - if (eStart.Dx == HorizontalDeltaLimit) - { - if (eStart.Bot.X != edge.Bot.X && eStart.Top.X != edge.Bot.X) - { - ReverseHorizontal(edge); - } - } - else if (eStart.Bot.X != edge.Bot.X) - { - ReverseHorizontal(edge); - } - } - - eStart = edge; - if (leftBoundIsForward) - { - while (result.Top.Y == result.Next.Bot.Y && result.Next.OutIdx != Skip) - { - result = result.Next; - } - - if (result.Dx == HorizontalDeltaLimit && result.Next.OutIdx != Skip) - { - // nb: at the top of a bound, horizontals are added to the bound - // only when the preceding edge attaches to the horizontal's left vertex - // unless a Skip edge is encountered when that becomes the top divide - horz = result; - while (horz.Prev.Dx == HorizontalDeltaLimit) - { - horz = horz.Prev; - } - - if (horz.Prev.Top.X > result.Next.Top.X) - { - result = horz.Prev; - } - } - - while (edge != result) - { - edge.NextInLML = edge.Next; - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Prev.Top.X) - { - ReverseHorizontal(edge); - } - - edge = edge.Next; - } - - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Prev.Top.X) - { - ReverseHorizontal(edge); - } - - result = result.Next; // move to the edge just beyond current bound - } - else - { - while (result.Top.Y == result.Prev.Bot.Y && result.Prev.OutIdx != Skip) - { - result = result.Prev; - } - - if (result.Dx == HorizontalDeltaLimit && result.Prev.OutIdx != Skip) - { - horz = result; - while (horz.Next.Dx == HorizontalDeltaLimit) - { - horz = horz.Next; - } - - if (horz.Next.Top.X == result.Prev.Top.X || horz.Next.Top.X > result.Prev.Top.X) - { - result = horz.Next; - } - } - - while (edge != result) - { - edge.NextInLML = edge.Prev; - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Next.Top.X) - { - ReverseHorizontal(edge); - } - - edge = edge.Prev; - } - - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Next.Top.X) - { - ReverseHorizontal(edge); - } - - result = result.Prev; // move to the edge just beyond current bound - } - - return result; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs deleted file mode 100644 index cefd268af..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Clipper Exception - /// - /// - internal class ClipperException : Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The description. - public ClipperException(string description) - : base(description) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs deleted file mode 100644 index 5fa877fd4..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ??? - /// - internal enum Direction - { - /// - /// The right to left - /// - RightToLeft, - - /// - /// The left to right - /// - LeftToRight - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs deleted file mode 100644 index 5093958d1..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal enum EdgeSide - { - /// - /// The left - /// - Left, - - /// - /// The right - /// - Right - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs deleted file mode 100644 index 7cd0562b0..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class IntersectNode - { -#pragma warning disable SA1401 // Field must be private - /// - /// The edge1 - /// - internal TEdge Edge1; - - /// - /// The edge2 - /// - internal TEdge Edge2; - - /// - /// The pt - /// - internal System.Numerics.Vector2 Pt; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs deleted file mode 100644 index f4524fa9b..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Compares s - /// - internal class IntersectNodeSort : IComparer - { - /// - /// Compares the specified node1. - /// - /// The node1. - /// The node2. - /// - /// 1 if node2 %gt; node1 - /// -1 if node2 $lt; node1 - /// 0 if same - /// - public int Compare(IntersectNode node1, IntersectNode node2) - { - float i = node2.Pt.Y - node1.Pt.Y; - if (i > 0) - { - return 1; - } - else if (i < 0) - { - return -1; - } - else - { - return 0; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs deleted file mode 100644 index be948fbf7..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class Join - { -#pragma warning disable SA1401 // Field must be private - /// - /// The out PT1 - /// - internal OutPt OutPt1; - - /// - /// The out PT2 - /// - internal OutPt OutPt2; - - /// - /// The off pt - /// - internal System.Numerics.Vector2 OffPt; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs deleted file mode 100644 index b48a53cab..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class LocalMinima - { -#pragma warning disable SA1401 // Field must be private - /// - /// The y - /// - internal float Y; - - /// - /// The left bound - /// - internal TEdge LeftBound; - - /// - /// The right bound - /// - internal TEdge RightBound; - - /// - /// The next - /// - internal LocalMinima Next; - -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs deleted file mode 100644 index 85168e8e8..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class Maxima - { -#pragma warning disable SA1401 // Field must be private - /// - /// The x - /// - internal float X; - - /// - /// The next - /// - internal Maxima Next; - - /// - /// The previous - /// - internal Maxima Prev; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs deleted file mode 100644 index 8dae5780a..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class OutPt - { -#pragma warning disable SA1401 // Field must be private - /// - /// The index - /// - internal int Idx; - - /// - /// The pt - /// - internal System.Numerics.Vector2 Pt; - - /// - /// The next - /// - internal OutPt Next; - - /// - /// The previous - /// - internal OutPt Prev; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs deleted file mode 100644 index 7c2d41a72..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// OutRec: contains a path in the clipping solution. Edges in the AEL will - /// carry a pointer to an OutRec when they are part of the clipping solution. - /// - internal class OutRec - { -#pragma warning disable SA1401 // Field must be private - /// - /// The source path - /// - internal IPath SourcePath; - - /// - /// The index - /// - internal int Idx; - - /// - /// The is hole - /// - internal bool IsHole; - - /// - /// The is open - /// - internal bool IsOpen; - - /// - /// The first left - /// - internal OutRec FirstLeft; - - /// - /// The PTS - /// - internal OutPt Pts; - - /// - /// The bottom pt - /// - internal OutPt BottomPt; - - /// - /// The poly node - /// - internal PolyNode PolyNode; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs deleted file mode 100644 index 9d9c35504..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs +++ /dev/null @@ -1,179 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Poly Node - /// - internal class PolyNode - { -#pragma warning disable SA1401 // Field must be private - /// - /// The polygon - /// - internal List Polygon = new List(); - - /// - /// The index - /// - internal int Index; - - /// - /// The childs - /// - protected List children = new List(); - - private PolyNode parent; -#pragma warning restore SA1401 // Field must be private - - /// - /// Gets the child count. - /// - /// - /// The child count. - /// - public int ChildCount - { - get { return this.children.Count; } - } - - /// - /// Gets the contour. - /// - /// - /// The contour. - /// - public List Contour - { - get { return this.Polygon; } - } - - /// - /// Gets the childs. - /// - /// - /// The childs. - /// - public List Children - { - get { return this.children; } - } - - /// - /// Gets or sets the parent. - /// - /// - /// The parent. - /// - public PolyNode Parent - { - get { return this.parent; } - internal set { this.parent = value; } - } - - /// - /// Gets a value indicating whether this instance is hole. - /// - /// - /// true if this instance is hole; otherwise, false. - /// - public bool IsHole - { - get { return this.IsHoleNode(); } - } - - /// - /// Gets or sets a value indicating whether this instance is open. - /// - /// - /// true if this instance is open; otherwise, false. - /// - public bool IsOpen { get; set; } - - /// - /// Gets or sets the source path. - /// - /// - /// The source path. - /// - public IPath SourcePath { get; internal set; } - - /// - /// Gets the next. - /// - /// The next node - public PolyNode GetNext() - { - if (this.children.Count > 0) - { - return this.children[0]; - } - else - { - return this.GetNextSiblingUp(); - } - } - - /// - /// Adds the child. - /// - /// The child. - internal void AddChild(PolyNode child) - { - int cnt = this.children.Count; - this.children.Add(child); - child.parent = this; - child.Index = cnt; - } - - /// - /// Gets the next sibling up. - /// - /// The next sibling up - internal PolyNode GetNextSiblingUp() - { - if (this.parent == null) - { - return null; - } - else if (this.Index == this.parent.children.Count - 1) - { - return this.parent.GetNextSiblingUp(); - } - else - { - return this.parent.Children[this.Index + 1]; - } - } - - /// - /// Determines whether [is hole node]. - /// - /// - /// true if [is hole node]; otherwise, false. - /// - private bool IsHoleNode() - { - bool result = true; - PolyNode node = this.parent; - while (node != null) - { - result = !result; - node = node.parent; - } - - return result; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs deleted file mode 100644 index 3c35f389c..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs +++ /dev/null @@ -1,81 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Poly Tree - /// - /// - internal class PolyTree : PolyNode - { -#pragma warning disable SA1401 // Field must be private - /// - /// All polys - /// - internal List AllPolys = new List(); -#pragma warning restore SA1401 // Field must be private - - /// - /// Gets the total. - /// - /// - /// The total. - /// - public int Total - { - get - { - int result = this.AllPolys.Count; - - // with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && this.Children[0] != this.AllPolys[0]) - { - result--; - } - - return result; - } - } - - /// - /// Clears this instance. - /// - public void Clear() - { - for (int i = 0; i < this.AllPolys.Count; i++) - { - this.AllPolys[i] = null; - } - - this.AllPolys.Clear(); - this.Children.Clear(); - } - - /// - /// Gets the first. - /// - /// the first node - public PolyNode GetFirst() - { - if (this.Children.Count > 0) - { - return this.Children[0]; - } - else - { - return null; - } - } - } -} diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs deleted file mode 100644 index 2a130f509..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Poly Type - /// - internal enum PolyType - { - /// - /// The subject - /// - Subject, - - /// - /// The clip - /// - Clip - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md b/src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md deleted file mode 100644 index c0f2ff65f..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Clipper - -License details for code in this folder, this is code original written by **Angus Johnson** - -The license header onthe original file which has now be split across multiple files in this folder. - -``` -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ -``` \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs deleted file mode 100644 index 28b341004..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Scanbeam - /// - internal class Scanbeam // would this work as a struct? - { -#pragma warning disable SA1401 // Field must be private - /// - /// The y - /// - internal float Y; - - /// - /// The next - /// - internal Scanbeam Next; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs deleted file mode 100644 index 97f5b2ec7..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// TEdge - /// - internal class TEdge - { -#pragma warning disable SA1401 // Field must be private - /// - /// The source path, see if we can link this back later - /// - internal IPath SourcePath; - - /// - /// The bot - /// - internal System.Numerics.Vector2 Bot; - - /// - /// The current (updated for every new scanbeam) - /// - internal System.Numerics.Vector2 Curr; - - /// - /// The top - /// - internal System.Numerics.Vector2 Top; - - /// - /// The delta - /// - internal System.Numerics.Vector2 Delta; - - /// - /// The dx - /// - internal double Dx; - - /// - /// The poly type - /// - internal PolyType PolyTyp; - - /// - /// Side only refers to current side of solution poly - /// - internal EdgeSide Side; - - /// - /// 1 or -1 depending on winding direction - /// - internal int WindDelta; - - /// - /// The winding count - /// - internal int WindCnt; - - /// - /// The winding count of the opposite polytype - /// - internal int WindCnt2; - - /// - /// The out index - /// - internal int OutIdx; - - /// - /// The next - /// - internal TEdge Next; - - /// - /// The previous - /// - internal TEdge Prev; - - /// - /// The next in LML - /// - internal TEdge NextInLML; - - /// - /// The next in ael - /// - internal TEdge NextInAEL; - - /// - /// The previous in ael - /// - internal TEdge PrevInAEL; - - /// - /// The next in sel - /// - internal TEdge NextInSEL; - - /// - /// The previous in sel - /// - internal TEdge PrevInSEL; -#pragma warning restore SA1401 // Field must be - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs b/src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs deleted file mode 100644 index 5002bee40..000000000 --- a/src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs +++ /dev/null @@ -1,281 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Threading.Tasks; - using Paths; - - /// - /// A way of optermising drawing rectangles. - /// - /// - public class RectangularPolygon : IShape, IPath - { - private readonly RectangleF rectangle; - private readonly Vector2 topLeft; - private readonly Vector2 bottomRight; - private readonly Vector2[] points; - private readonly IEnumerable pathCollection; - private readonly float halfLength; - - /// - /// Initializes a new instance of the class. - /// - /// The rect. - public RectangularPolygon(ImageSharp.Rectangle rect) - : this((RectangleF)rect) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The rect. - public RectangularPolygon(ImageSharp.RectangleF rect) - { - this.rectangle = rect; - this.points = new Vector2[4] - { - this.topLeft = new Vector2(rect.Left, rect.Top), - new Vector2(rect.Right, rect.Top), - this.bottomRight = new Vector2(rect.Right, rect.Bottom), - new Vector2(rect.Left, rect.Bottom) - }; - - this.halfLength = this.rectangle.Width + this.rectangle.Height; - this.Length = this.halfLength * 2; - this.pathCollection = new[] { this }; - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.rectangle; - - /// - /// Gets a value indicating whether this instance is closed. - /// - /// - /// true if this instance is closed; otherwise, false. - /// - public bool IsClosed => true; - - /// - /// Gets the length of the path - /// - /// - /// The length. - /// - public float Length { get; } - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections => 4; - - /// - /// Calculates the distance along and away from the path for a specified point. - /// - /// The point along the path. - /// - /// Returns details about the point and its distance away from the path. - /// - PointInfo IPath.Distance(Vector2 point) - { - bool inside; // dont care about inside/outside for paths just distance - return this.Distance(point, false, out inside); - } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// Returns the distance from the shape to the point - /// - public float Distance(Vector2 point) - { - bool insidePoly; - PointInfo result = this.Distance(point, true, out insidePoly); - - // invert the distance from path when inside - return insidePoly ? -result.DistanceFromPath : result.DistanceFromPath; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Converts the into a simple linear path.. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.points; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on the edges of the - /// that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - int discovered = 0; - Vector2 startPoint = Vector2.Clamp(start, this.topLeft, this.bottomRight); - Vector2 endPoint = Vector2.Clamp(end, this.topLeft, this.bottomRight); - - if (startPoint == Vector2.Clamp(startPoint, start, end)) - { - // if start closest is within line then its a valid point - discovered++; - buffer[offset++] = startPoint; - } - - if (endPoint == Vector2.Clamp(endPoint, start, end)) - { - // if start closest is within line then its a valid point - discovered++; - buffer[offset++] = endPoint; - } - - return discovered; - } - - private PointInfo Distance(Vector2 point, bool getDistanceAwayOnly, out bool isInside) - { - // point in rectangle - // if after its clamped by the extreams its still the same then it must be inside :) - Vector2 clamped = Vector2.Clamp(point, this.topLeft, this.bottomRight); - isInside = clamped == point; - - float distanceFromEdge = float.MaxValue; - float distanceAlongEdge = 0f; - - if (isInside) - { - // get the absolute distances from the extreams - Vector2 topLeftDist = Vector2.Abs(point - this.topLeft); - Vector2 bottomRightDist = Vector2.Abs(point - this.bottomRight); - - // get the min components - Vector2 minDists = Vector2.Min(topLeftDist, bottomRightDist); - - // and then the single smallest (dont have to worry about direction) - distanceFromEdge = Math.Min(minDists.X, minDists.Y); - - if (!getDistanceAwayOnly) - { - // we need to make clamped the closest point - if (this.topLeft.X + distanceFromEdge == point.X) - { - // closer to lhf - clamped.X = this.topLeft.X; // y is already the same - - // distance along edge is length minus the amout down we are from the top of the rect - distanceAlongEdge = this.Length - (clamped.Y - this.topLeft.Y); - } - else if (this.topLeft.Y + distanceFromEdge == point.Y) - { - // closer to top - clamped.Y = this.topLeft.Y; // x is already the same - - distanceAlongEdge = clamped.X - this.topLeft.X; - } - else if (this.bottomRight.Y - distanceFromEdge == point.Y) - { - // closer to bottom - clamped.Y = this.bottomRight.Y; // x is already the same - - distanceAlongEdge = (this.bottomRight.X - clamped.X) + this.halfLength; - } - else if (this.bottomRight.X - distanceFromEdge == point.X) - { - // closer to rhs - clamped.X = this.bottomRight.X; // x is already the same - - distanceAlongEdge = (this.bottomRight.Y - clamped.Y) + this.rectangle.Width; - } - } - } - else - { - // clamped is the point on the path thats closest no matter what - distanceFromEdge = (clamped - point).Length(); - - if (!getDistanceAwayOnly) - { - // we need to figure out whats the cloests edge now and thus what distance/poitn is closest - if (this.topLeft.X == clamped.X) - { - // distance along edge is length minus the amout down we are from the top of the rect - distanceAlongEdge = this.Length - (clamped.Y - this.topLeft.Y); - } - else if (this.topLeft.Y == clamped.Y) - { - distanceAlongEdge = clamped.X - this.topLeft.X; - } - else if (this.bottomRight.Y == clamped.Y) - { - distanceAlongEdge = (this.bottomRight.X - clamped.X) + this.halfLength; - } - else if (this.bottomRight.X == clamped.X) - { - distanceAlongEdge = (this.bottomRight.Y - clamped.Y) + this.rectangle.Width; - } - } - } - - return new PointInfo - { - SearchPoint = point, - DistanceFromPath = distanceFromEdge, - ClosestPointOnPath = clamped, - DistanceAlongPath = distanceAlongEdge - }; - } - } -} diff --git a/src/ImageSharp.Drawing/project.json b/src/ImageSharp.Drawing/project.json index b974684a5..f0d4c6243 100644 --- a/src/ImageSharp.Drawing/project.json +++ b/src/ImageSharp.Drawing/project.json @@ -46,6 +46,7 @@ "target": "project", "version": "1.0.0-*" }, + "SixLabors.Shapes": "0.1.0-ci0043", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index 8e5c18d27..defb0e65e 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -39,7 +39,7 @@ namespace ImageSharp.Benchmarks { using (CoreImage image = new CoreImage(800, 800)) { - image.Fill(CoreColor.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new CoreRectangle(10, 10, 190, 140))); + image.Fill(CoreColor.HotPink, new SixLabors.Shapes.Rectangle(10, 10, 190, 140)); return new CoreSize(image.Width, image.Height); } @@ -53,10 +53,10 @@ namespace ImageSharp.Benchmarks image.FillPolygon( CoreColor.HotPink, new[] { - new Vector2(10, 10), - new Vector2(200, 10), - new Vector2(200, 150), - new Vector2(10, 150) }); + new Vector2(10, 10), + new Vector2(200, 10), + new Vector2(200, 150), + new Vector2(10, 150) }); return new CoreSize(image.Width, image.Height); }