mirror of https://github.com/SixLabors/ImageSharp
43 changed files with 108 additions and 6528 deletions
@ -1,119 +0,0 @@ |
|||||
// <copyright file="BezierLineSegment.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Paths |
|
||||
{ |
|
||||
using System.Numerics; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a line segment that conistst of control points that will be rendered as a cubic bezier curve
|
|
||||
/// </summary>
|
|
||||
/// <seealso cref="ImageSharp.Drawing.Paths.ILineSegment" />
|
|
||||
public class BezierLineSegment : ILineSegment |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The segments per curve.
|
|
||||
/// code for this taken from <see href="http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/"/>
|
|
||||
/// </summary>
|
|
||||
private const int SegmentsPerCurve = 50; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The line points.
|
|
||||
/// </summary>
|
|
||||
private readonly Vector2[] linePoints; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="BezierLineSegment"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">The points.</param>
|
|
||||
public BezierLineSegment(params Vector2[] points) |
|
||||
{ |
|
||||
Guard.NotNull(points, nameof(points)); |
|
||||
Guard.MustBeGreaterThanOrEqualTo(points.Length, 4, nameof(points)); |
|
||||
|
|
||||
this.linePoints = this.GetDrawingPoints(points); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the current <see cref="ILineSegment" /> a simple linear path.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
|
||||
/// </returns>
|
|
||||
public Vector2[] AsSimpleLinearPath() |
|
||||
{ |
|
||||
return this.linePoints; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the drawing points along the line.
|
|
||||
/// </summary>
|
|
||||
/// <param name="controlPoints">The control points.</param>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="T:Vector2[]"/>.
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculates the bezier point along the line.
|
|
||||
/// </summary>
|
|
||||
/// <param name="t">The position within the line.</param>
|
|
||||
/// <param name="p0">The p 0.</param>
|
|
||||
/// <param name="p1">The p 1.</param>
|
|
||||
/// <param name="p2">The p 2.</param>
|
|
||||
/// <param name="p3">The p 3.</param>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="Vector2"/>.
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,21 +0,0 @@ |
|||||
// <copyright file="ILineSegment.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Paths |
|
||||
{ |
|
||||
using System.Numerics; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a simple path segment
|
|
||||
/// </summary>
|
|
||||
public interface ILineSegment |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Converts the <see cref="ILineSegment" /> into a simple linear path..
|
|
||||
/// </summary>
|
|
||||
/// <returns>Returns the current <see cref="ILineSegment" /> as simple linear path.</returns>
|
|
||||
Vector2[] AsSimpleLinearPath(); // TODO move this over to ReadonlySpan<Vector2> once available
|
|
||||
} |
|
||||
} |
|
||||
@ -1,48 +0,0 @@ |
|||||
// <copyright file="IPath.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Paths |
|
||||
{ |
|
||||
using System.Numerics; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a logic path that can be drawn
|
|
||||
/// </summary>
|
|
||||
public interface IPath : ILineSegment |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the bounds enclosing the path
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
RectangleF Bounds { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether this instance is closed.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// <c>true</c> if this instance is closed; otherwise, <c>false</c>.
|
|
||||
/// </value>
|
|
||||
bool IsClosed { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the length of the path
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The length.
|
|
||||
/// </value>
|
|
||||
float Length { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculates the distance along and away from the path for a specified point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point along the path.</param>
|
|
||||
/// <returns>
|
|
||||
/// Returns details about the point and its distance away from the path.
|
|
||||
/// </returns>
|
|
||||
PointInfo Distance(Vector2 point); |
|
||||
} |
|
||||
} |
|
||||
@ -1,516 +0,0 @@ |
|||||
// <copyright file="InternalPath.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
namespace ImageSharp.Drawing.Paths |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Internal logic for integrating linear paths.
|
|
||||
/// </summary>
|
|
||||
internal class InternalPath |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The maximum vector
|
|
||||
/// </summary>
|
|
||||
private static readonly Vector2 MaxVector = new Vector2(float.MaxValue); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The locker.
|
|
||||
/// </summary>
|
|
||||
private static readonly object Locker = new object(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The points.
|
|
||||
/// </summary>
|
|
||||
private readonly Vector2[] points; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The closed path.
|
|
||||
/// </summary>
|
|
||||
private readonly bool closedPath; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The total distance.
|
|
||||
/// </summary>
|
|
||||
private readonly Lazy<float> totalDistance; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The constant.
|
|
||||
/// </summary>
|
|
||||
private float[] constant; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The multiples.
|
|
||||
/// </summary>
|
|
||||
private float[] multiple; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The distances.
|
|
||||
/// </summary>
|
|
||||
private float[] distance; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The calculated.
|
|
||||
/// </summary>
|
|
||||
private bool calculated = false; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="InternalPath"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="segments">The segments.</param>
|
|
||||
/// <param name="isClosedPath">if set to <c>true</c> [is closed path].</param>
|
|
||||
internal InternalPath(ILineSegment[] segments, bool isClosedPath) |
|
||||
: this(Simplify(segments), isClosedPath) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="InternalPath" /> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="segment">The segment.</param>
|
|
||||
/// <param name="isClosedPath">if set to <c>true</c> [is closed path].</param>
|
|
||||
internal InternalPath(ILineSegment segment, bool isClosedPath) |
|
||||
: this(segment.AsSimpleLinearPath(), isClosedPath) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="InternalPath" /> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">The points.</param>
|
|
||||
/// <param name="isClosedPath">if set to <c>true</c> [is closed path].</param>
|
|
||||
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<float>(this.CalculateLength); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the bounds.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
public RectangleF Bounds |
|
||||
{ |
|
||||
get; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the length.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The length.
|
|
||||
/// </value>
|
|
||||
public float Length => this.totalDistance.Value; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the points.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The points.
|
|
||||
/// </value>
|
|
||||
internal Vector2[] Points => this.points; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculates the distance from the path.
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>Returns the distance from the path</returns>
|
|
||||
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 |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Based on a line described by <paramref name="start" /> and <paramref name="end" />
|
|
||||
/// populate a buffer for all points on the path that the line intersects.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start.</param>
|
|
||||
/// <param name="end">The end.</param>
|
|
||||
/// <param name="buffer">The buffer.</param>
|
|
||||
/// <param name="count">The count.</param>
|
|
||||
/// <param name="offset">The offset.</param>
|
|
||||
/// <returns>number iof intersections hit</returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determines if the specified point is inside or outside the path.
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>Returns true if the point is inside the closed path.</returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determins if the bounding box for 2 lines
|
|
||||
/// described by <paramref name="line1Start" /> and <paramref name="line1End" />
|
|
||||
/// and <paramref name="line2Start" /> and <paramref name="line2End" /> overlap.
|
|
||||
/// </summary>
|
|
||||
/// <param name="line1Start">The line1 start.</param>
|
|
||||
/// <param name="line1End">The line1 end.</param>
|
|
||||
/// <param name="line2Start">The line2 start.</param>
|
|
||||
/// <param name="line2End">The line2 end.</param>
|
|
||||
/// <returns>Returns true it the bounding box of the 2 lines intersect</returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Finds the point on line described by <paramref name="line1Start" /> and <paramref name="line1End" />
|
|
||||
/// that intersects with line described by <paramref name="line2Start" /> and <paramref name="line2End" />
|
|
||||
/// </summary>
|
|
||||
/// <param name="line1Start">The line1 start.</param>
|
|
||||
/// <param name="line1End">The line1 end.</param>
|
|
||||
/// <param name="line2Start">The line2 start.</param>
|
|
||||
/// <param name="line2End">The line2 end.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Vector2"/> describing the point that the 2 lines cross or <see cref="MaxVector"/> if they do not.
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Simplifies the collection of segments.
|
|
||||
/// </summary>
|
|
||||
/// <param name="segments">The segments.</param>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="T:Vector2[]"/>.
|
|
||||
/// </returns>
|
|
||||
private static Vector2[] Simplify(ILineSegment[] segments) |
|
||||
{ |
|
||||
List<Vector2> simplified = new List<Vector2>(); |
|
||||
foreach (ILineSegment seg in segments) |
|
||||
{ |
|
||||
simplified.AddRange(seg.AsSimpleLinearPath()); |
|
||||
} |
|
||||
|
|
||||
return simplified.ToArray(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the length of the path.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="float"/>.
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculate the constants.
|
|
||||
/// </summary>
|
|
||||
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; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculate any shorter distances along the path.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start position.</param>
|
|
||||
/// <param name="end">The end position.</param>
|
|
||||
/// <param name="point">The current point.</param>
|
|
||||
/// <param name="info">The info.</param>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="bool"/>.
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Contains information about the current point.
|
|
||||
/// </summary>
|
|
||||
private struct PointInfoInternal |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The distance squared.
|
|
||||
/// </summary>
|
|
||||
public float DistanceSquared; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The point on the current line.
|
|
||||
/// </summary>
|
|
||||
public Vector2 PointOnLine; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,55 +0,0 @@ |
|||||
// <copyright file="LinearLineSegment.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Paths |
|
||||
{ |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a series of control points that will be joined by straight lines
|
|
||||
/// </summary>
|
|
||||
/// <seealso cref="ILineSegment" />
|
|
||||
public class LinearLineSegment : ILineSegment |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The collection of points.
|
|
||||
/// </summary>
|
|
||||
private readonly Vector2[] points; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="LinearLineSegment"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start.</param>
|
|
||||
/// <param name="end">The end.</param>
|
|
||||
public LinearLineSegment(Vector2 start, Vector2 end) |
|
||||
: this(new[] { start, end }) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="LinearLineSegment"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">The points.</param>
|
|
||||
public LinearLineSegment(params Vector2[] points) |
|
||||
{ |
|
||||
Guard.NotNull(points, nameof(points)); |
|
||||
Guard.MustBeGreaterThanOrEqualTo(points.Count(), 2, nameof(points)); |
|
||||
|
|
||||
this.points = points; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts the <see cref="ILineSegment" /> into a simple linear path..
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
|
||||
/// </returns>
|
|
||||
public Vector2[] AsSimpleLinearPath() |
|
||||
{ |
|
||||
return this.points; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,51 +0,0 @@ |
|||||
// <copyright file="Path.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Paths |
|
||||
{ |
|
||||
using System.Numerics; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A aggregate of <see cref="ILineSegment"/>s making a single logical path
|
|
||||
/// </summary>
|
|
||||
/// <seealso cref="IPath" />
|
|
||||
public class Path : IPath |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The inner path.
|
|
||||
/// </summary>
|
|
||||
private readonly InternalPath innerPath; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Path"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="segment">The segment.</param>
|
|
||||
public Path(params ILineSegment[] segment) |
|
||||
{ |
|
||||
this.innerPath = new InternalPath(segment, false); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public RectangleF Bounds => this.innerPath.Bounds; |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public bool IsClosed => false; |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public float Length => this.innerPath.Length; |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public Vector2[] AsSimpleLinearPath() |
|
||||
{ |
|
||||
return this.innerPath.Points; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public PointInfo Distance(Vector2 point) |
|
||||
{ |
|
||||
return this.innerPath.DistanceFromPath(point); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,37 @@ |
|||||
|
// <copyright file="PointInfoExtensions.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
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; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extension methods for helping to bridge Shaper2D and ImageSharp primitives.
|
||||
|
/// </summary>
|
||||
|
internal static class PointInfoExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts a <see cref="SixLabors.Shapes.PointInfo"/> to an ImageSharp <see cref="PointInfo"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The source.</param>
|
||||
|
/// <returns>A <see cref="PointInfo"/> representation of this <see cref="SixLabors.Shapes.PointInfo"/></returns>
|
||||
|
public static PointInfo Convert(this SixLabors.Shapes.PointInfo source) |
||||
|
{ |
||||
|
return new PointInfo |
||||
|
{ |
||||
|
DistanceAlongPath = source.DistanceAlongPath, |
||||
|
DistanceFromPath = source.DistanceFromPath, |
||||
|
SearchPoint = source.SearchPoint |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
// <copyright file="RectangleExtensions.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
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; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extension methods for helping to bridge Shaper2D and ImageSharp primitives.
|
||||
|
/// </summary>
|
||||
|
internal static class RectangleExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts a Shaper2D <see cref="SixLabors.Shapes.Rectangle"/> to an ImageSharp <see cref="RectangleF"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The source.</param>
|
||||
|
/// <returns>A <see cref="RectangleF"/> representation of this <see cref="SixLabors.Shapes.Rectangle"/></returns>
|
||||
|
public static RectangleF Convert(this SixLabors.Shapes.Rectangle source) |
||||
|
{ |
||||
|
return new RectangleF(source.Location.X, source.Location.Y, source.Size.Width, source.Size.Height); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,93 +0,0 @@ |
|||||
// <copyright file="BezierPolygon.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes |
|
||||
{ |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Numerics; |
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a polygon made up exclusivly of a single close cubic Bezier curve.
|
|
||||
/// </summary>
|
|
||||
public sealed class BezierPolygon : IShape |
|
||||
{ |
|
||||
private Polygon innerPolygon; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="BezierPolygon"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">The points.</param>
|
|
||||
public BezierPolygon(params Vector2[] points) |
|
||||
{ |
|
||||
this.innerPolygon = new Polygon(new BezierLineSegment(points)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the bounding box of this shape.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
public RectangleF Bounds => this.innerPolygon.Bounds; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The maximum intersections.
|
|
||||
/// </value>
|
|
||||
public int MaxIntersections => this.innerPolygon.MaxIntersections; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>
|
|
||||
/// The distance from the shape.
|
|
||||
/// </returns>
|
|
||||
public float Distance(Vector2 point) => this.innerPolygon.Distance(point); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start point of the line.</param>
|
|
||||
/// <param name="end">The end point of the line.</param>
|
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
|
||||
/// <param name="count">The count.</param>
|
|
||||
/// <param name="offset">The offset.</param>
|
|
||||
/// <returns>
|
|
||||
/// The number of intersections populated into the buffer.
|
|
||||
/// </returns>
|
|
||||
public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) |
|
||||
{ |
|
||||
return this.innerPolygon.FindIntersections(start, end, buffer, count, offset); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through the collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An enumerator that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
public IEnumerator<IPath> GetEnumerator() |
|
||||
{ |
|
||||
return this.innerPolygon.GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through a collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
IEnumerator IEnumerable.GetEnumerator() |
|
||||
{ |
|
||||
return this.innerPolygon.GetEnumerator(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,246 +0,0 @@ |
|||||
// <copyright file="ComplexPolygon.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
using Paths; |
|
||||
using PolygonClipper; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a complex polygon made up of one or more outline
|
|
||||
/// polygons and one or more holes to punch out of them.
|
|
||||
/// </summary>
|
|
||||
/// <seealso cref="ImageSharp.Drawing.Shapes.IShape" />
|
|
||||
public sealed class ComplexPolygon : IShape |
|
||||
{ |
|
||||
private const float ClipperScaleFactor = 100f; |
|
||||
private IShape[] shapes; |
|
||||
private IEnumerable<IPath> paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ComplexPolygon"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="outline">The outline.</param>
|
|
||||
/// <param name="holes">The holes.</param>
|
|
||||
public ComplexPolygon(IShape outline, params IShape[] holes) |
|
||||
: this(new[] { outline }, holes) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ComplexPolygon"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="outlines">The outlines.</param>
|
|
||||
/// <param name="holes">The holes.</param>
|
|
||||
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); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the bounding box of this shape.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
public RectangleF Bounds { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The maximum intersections.
|
|
||||
/// </value>
|
|
||||
public int MaxIntersections { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>
|
|
||||
/// Returns the distance from thr shape to the point
|
|
||||
/// </returns>
|
|
||||
/// <remarks>
|
|
||||
/// 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 :)
|
|
||||
/// </remarks>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
|
||||
/// populate a buffer for all points on all the polygons, that make up this complex shape,
|
|
||||
/// that the line intersects.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start point of the line.</param>
|
|
||||
/// <param name="end">The end point of the line.</param>
|
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
|
||||
/// <param name="count">The count.</param>
|
|
||||
/// <param name="offset">The offset.</param>
|
|
||||
/// <returns>
|
|
||||
/// The number of intersections populated into the buffer.
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through the collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An enumerator that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
public IEnumerator<IPath> GetEnumerator() |
|
||||
{ |
|
||||
return this.paths.GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through a collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
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<IShape> shapes, PolyType polyType) |
|
||||
{ |
|
||||
foreach (IShape shape in shapes) |
|
||||
{ |
|
||||
this.AddPoints(clipper, shape, polyType); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void ExtractOutlines(PolyNode tree, List<IShape> shapes, List<IPath> 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<IShape> outlines, IEnumerable<IShape> 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<IShape> shapes = new List<IShape>(); |
|
||||
List<IPath> paths = new List<IPath>(); |
|
||||
|
|
||||
// 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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,56 +0,0 @@ |
|||||
// <copyright file="IShape.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes |
|
||||
{ |
|
||||
using System.Collections.Generic; |
|
||||
using System.Numerics; |
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a closed set of paths making up a single shape.
|
|
||||
/// </summary>
|
|
||||
public interface IShape : IEnumerable<IPath> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the bounding box of this shape.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
RectangleF Bounds { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The maximum intersections.
|
|
||||
/// </value>
|
|
||||
int MaxIntersections { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>
|
|
||||
/// Returns the distance from the shape to the point
|
|
||||
/// </returns>
|
|
||||
float Distance(Vector2 point); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start point of the line.</param>
|
|
||||
/// <param name="end">The end point of the line.</param>
|
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
|
||||
/// <param name="count">The count.</param>
|
|
||||
/// <param name="offset">The offset.</param>
|
|
||||
/// <returns>
|
|
||||
/// The number of intersections populated into the buffer.
|
|
||||
/// </returns>
|
|
||||
int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset); |
|
||||
} |
|
||||
} |
|
||||
@ -1,93 +0,0 @@ |
|||||
// <copyright file="LinearPolygon.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes |
|
||||
{ |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Numerics; |
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Represents a polygon made up exclusivly of a single Linear path.
|
|
||||
/// </summary>
|
|
||||
public sealed class LinearPolygon : IShape |
|
||||
{ |
|
||||
private Polygon innerPolygon; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="LinearPolygon"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="points">The points.</param>
|
|
||||
public LinearPolygon(params Vector2[] points) |
|
||||
{ |
|
||||
this.innerPolygon = new Polygon(new LinearLineSegment(points)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the bounding box of this shape.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
public RectangleF Bounds => this.innerPolygon.Bounds; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The maximum intersections.
|
|
||||
/// </value>
|
|
||||
public int MaxIntersections |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return this.innerPolygon.MaxIntersections; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>
|
|
||||
/// Returns the distance from the shape to the point
|
|
||||
/// </returns>
|
|
||||
public float Distance(Vector2 point) => this.innerPolygon.Distance(point); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start point of the line.</param>
|
|
||||
/// <param name="end">The end point of the line.</param>
|
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
|
||||
/// <param name="count">The count.</param>
|
|
||||
/// <param name="offset">The offset.</param>
|
|
||||
/// <returns>
|
|
||||
/// The number of intersections populated into the buffer.
|
|
||||
/// </returns>
|
|
||||
public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) |
|
||||
{ |
|
||||
return this.innerPolygon.FindIntersections(start, end, buffer, count, offset); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through the collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An enumerator that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
public IEnumerator<IPath> GetEnumerator() => this.innerPolygon.GetEnumerator(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through a collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
IEnumerator IEnumerable.GetEnumerator() => this.innerPolygon.GetEnumerator(); |
|
||||
} |
|
||||
} |
|
||||
@ -1,156 +0,0 @@ |
|||||
// <copyright file="Polygon.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes |
|
||||
{ |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A shape made up of a single path made up of one of more <see cref="ILineSegment"/>s
|
|
||||
/// </summary>
|
|
||||
public sealed class Polygon : IShape, IPath |
|
||||
{ |
|
||||
private readonly InternalPath innerPath; |
|
||||
private readonly IEnumerable<IPath> pathCollection; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Polygon"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="segments">The segments.</param>
|
|
||||
public Polygon(params ILineSegment[] segments) |
|
||||
{ |
|
||||
this.innerPath = new InternalPath(segments, true); |
|
||||
this.pathCollection = new[] { this }; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Polygon" /> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="segment">The segment.</param>
|
|
||||
public Polygon(ILineSegment segment) |
|
||||
{ |
|
||||
this.innerPath = new InternalPath(segment, true); |
|
||||
this.pathCollection = new[] { this }; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the bounding box of this shape.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
public RectangleF Bounds => this.innerPath.Bounds; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the length of the path
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The length.
|
|
||||
/// </value>
|
|
||||
public float Length => this.innerPath.Length; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether this instance is closed.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// <c>true</c> if this instance is closed; otherwise, <c>false</c>.
|
|
||||
/// </value>
|
|
||||
public bool IsClosed => true; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The maximum intersections.
|
|
||||
/// </value>
|
|
||||
public int MaxIntersections => this.innerPath.Points.Length; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>
|
|
||||
/// The distance of the point away from the shape
|
|
||||
/// </returns>
|
|
||||
public float Distance(Vector2 point) |
|
||||
{ |
|
||||
bool isInside = this.innerPath.PointInPolygon(point); |
|
||||
|
|
||||
float distance = this.innerPath.DistanceFromPath(point).DistanceFromPath; |
|
||||
if (isInside) |
|
||||
{ |
|
||||
return -distance; |
|
||||
} |
|
||||
|
|
||||
return distance; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through the collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An enumerator that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
public IEnumerator<IPath> GetEnumerator() |
|
||||
{ |
|
||||
return this.pathCollection.GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through a collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
IEnumerator IEnumerable.GetEnumerator() |
|
||||
{ |
|
||||
return this.pathCollection.GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calcualtes the distance along and away from the path for a specified point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point along the path.</param>
|
|
||||
/// <returns>
|
|
||||
/// distance metadata about the point.
|
|
||||
/// </returns>
|
|
||||
PointInfo IPath.Distance(Vector2 point) |
|
||||
{ |
|
||||
return this.innerPath.DistanceFromPath(point); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the current shape as a simple linear path.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
|
||||
/// </returns>
|
|
||||
public Vector2[] AsSimpleLinearPath() |
|
||||
{ |
|
||||
return this.innerPath.Points; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Based on a line described by <paramref name="start" /> and <paramref name="end" />
|
|
||||
/// populate a buffer for all points on the polygon that the line intersects.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start point of the line.</param>
|
|
||||
/// <param name="end">The end point of the line.</param>
|
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
|
||||
/// <param name="count">The count.</param>
|
|
||||
/// <param name="offset">The offset.</param>
|
|
||||
/// <returns>
|
|
||||
/// The number of intersections populated into the buffer.
|
|
||||
/// </returns>
|
|
||||
public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) |
|
||||
{ |
|
||||
return this.innerPath.FindIntersections(start, end, buffer, count, offset); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
@ -1,31 +0,0 @@ |
|||||
// <copyright file="ClipperException.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clipper Exception
|
|
||||
/// </summary>
|
|
||||
/// <seealso cref="System.Exception" />
|
|
||||
internal class ClipperException : Exception |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ClipperException"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="description">The description.</param>
|
|
||||
public ClipperException(string description) |
|
||||
: base(description) |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
// <copyright file="Direction.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ???
|
|
||||
/// </summary>
|
|
||||
internal enum Direction |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The right to left
|
|
||||
/// </summary>
|
|
||||
RightToLeft, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The left to right
|
|
||||
/// </summary>
|
|
||||
LeftToRight |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
// <copyright file="EdgeSide.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ??
|
|
||||
/// </summary>
|
|
||||
internal enum EdgeSide |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The left
|
|
||||
/// </summary>
|
|
||||
Left, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The right
|
|
||||
/// </summary>
|
|
||||
Right |
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
// <copyright file="IntersectNode.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ??
|
|
||||
/// </summary>
|
|
||||
internal class IntersectNode |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The edge1
|
|
||||
/// </summary>
|
|
||||
internal TEdge Edge1; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The edge2
|
|
||||
/// </summary>
|
|
||||
internal TEdge Edge2; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The pt
|
|
||||
/// </summary>
|
|
||||
internal System.Numerics.Vector2 Pt; |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
} |
|
||||
} |
|
||||
@ -1,48 +0,0 @@ |
|||||
// <copyright file="IntersectNodeSort.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compares <see cref="IntersectNode"/>s
|
|
||||
/// </summary>
|
|
||||
internal class IntersectNodeSort : IComparer<IntersectNode> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Compares the specified node1.
|
|
||||
/// </summary>
|
|
||||
/// <param name="node1">The node1.</param>
|
|
||||
/// <param name="node2">The node2.</param>
|
|
||||
/// <returns>
|
|
||||
/// 1 if node2 %gt; node1
|
|
||||
/// -1 if node2 $lt; node1
|
|
||||
/// 0 if same
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
// <copyright file="Join.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ??
|
|
||||
/// </summary>
|
|
||||
internal class Join |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The out PT1
|
|
||||
/// </summary>
|
|
||||
internal OutPt OutPt1; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The out PT2
|
|
||||
/// </summary>
|
|
||||
internal OutPt OutPt2; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The off pt
|
|
||||
/// </summary>
|
|
||||
internal System.Numerics.Vector2 OffPt; |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
} |
|
||||
} |
|
||||
@ -1,44 +0,0 @@ |
|||||
// <copyright file="LocalMinima.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ??
|
|
||||
/// </summary>
|
|
||||
internal class LocalMinima |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The y
|
|
||||
/// </summary>
|
|
||||
internal float Y; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The left bound
|
|
||||
/// </summary>
|
|
||||
internal TEdge LeftBound; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The right bound
|
|
||||
/// </summary>
|
|
||||
internal TEdge RightBound; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next
|
|
||||
/// </summary>
|
|
||||
internal LocalMinima Next; |
|
||||
|
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
// <copyright file="Maxima.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ??
|
|
||||
/// </summary>
|
|
||||
internal class Maxima |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The x
|
|
||||
/// </summary>
|
|
||||
internal float X; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next
|
|
||||
/// </summary>
|
|
||||
internal Maxima Next; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The previous
|
|
||||
/// </summary>
|
|
||||
internal Maxima Prev; |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
} |
|
||||
} |
|
||||
@ -1,43 +0,0 @@ |
|||||
// <copyright file="OutPt.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ??
|
|
||||
/// </summary>
|
|
||||
internal class OutPt |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The index
|
|
||||
/// </summary>
|
|
||||
internal int Idx; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The pt
|
|
||||
/// </summary>
|
|
||||
internal System.Numerics.Vector2 Pt; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next
|
|
||||
/// </summary>
|
|
||||
internal OutPt Next; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The previous
|
|
||||
/// </summary>
|
|
||||
internal OutPt Prev; |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
} |
|
||||
} |
|
||||
@ -1,64 +0,0 @@ |
|||||
// <copyright file="OutRec.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 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.
|
|
||||
/// </summary>
|
|
||||
internal class OutRec |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The source path
|
|
||||
/// </summary>
|
|
||||
internal IPath SourcePath; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The index
|
|
||||
/// </summary>
|
|
||||
internal int Idx; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The is hole
|
|
||||
/// </summary>
|
|
||||
internal bool IsHole; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The is open
|
|
||||
/// </summary>
|
|
||||
internal bool IsOpen; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The first left
|
|
||||
/// </summary>
|
|
||||
internal OutRec FirstLeft; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The PTS
|
|
||||
/// </summary>
|
|
||||
internal OutPt Pts; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The bottom pt
|
|
||||
/// </summary>
|
|
||||
internal OutPt BottomPt; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The poly node
|
|
||||
/// </summary>
|
|
||||
internal PolyNode PolyNode; |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
} |
|
||||
} |
|
||||
@ -1,179 +0,0 @@ |
|||||
// <copyright file="PolyNode.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Poly Node
|
|
||||
/// </summary>
|
|
||||
internal class PolyNode |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The polygon
|
|
||||
/// </summary>
|
|
||||
internal List<Vector2> Polygon = new List<Vector2>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The index
|
|
||||
/// </summary>
|
|
||||
internal int Index; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The childs
|
|
||||
/// </summary>
|
|
||||
protected List<PolyNode> children = new List<PolyNode>(); |
|
||||
|
|
||||
private PolyNode parent; |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the child count.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The child count.
|
|
||||
/// </value>
|
|
||||
public int ChildCount |
|
||||
{ |
|
||||
get { return this.children.Count; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the contour.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The contour.
|
|
||||
/// </value>
|
|
||||
public List<Vector2> Contour |
|
||||
{ |
|
||||
get { return this.Polygon; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the childs.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The childs.
|
|
||||
/// </value>
|
|
||||
public List<PolyNode> Children |
|
||||
{ |
|
||||
get { return this.children; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the parent.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The parent.
|
|
||||
/// </value>
|
|
||||
public PolyNode Parent |
|
||||
{ |
|
||||
get { return this.parent; } |
|
||||
internal set { this.parent = value; } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether this instance is hole.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// <c>true</c> if this instance is hole; otherwise, <c>false</c>.
|
|
||||
/// </value>
|
|
||||
public bool IsHole |
|
||||
{ |
|
||||
get { return this.IsHoleNode(); } |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether this instance is open.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// <c>true</c> if this instance is open; otherwise, <c>false</c>.
|
|
||||
/// </value>
|
|
||||
public bool IsOpen { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the source path.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The source path.
|
|
||||
/// </value>
|
|
||||
public IPath SourcePath { get; internal set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the next.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The next node</returns>
|
|
||||
public PolyNode GetNext() |
|
||||
{ |
|
||||
if (this.children.Count > 0) |
|
||||
{ |
|
||||
return this.children[0]; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return this.GetNextSiblingUp(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Adds the child.
|
|
||||
/// </summary>
|
|
||||
/// <param name="child">The child.</param>
|
|
||||
internal void AddChild(PolyNode child) |
|
||||
{ |
|
||||
int cnt = this.children.Count; |
|
||||
this.children.Add(child); |
|
||||
child.parent = this; |
|
||||
child.Index = cnt; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the next sibling up.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The next sibling up</returns>
|
|
||||
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]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Determines whether [is hole node].
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// <c>true</c> if [is hole node]; otherwise, <c>false</c>.
|
|
||||
/// </returns>
|
|
||||
private bool IsHoleNode() |
|
||||
{ |
|
||||
bool result = true; |
|
||||
PolyNode node = this.parent; |
|
||||
while (node != null) |
|
||||
{ |
|
||||
result = !result; |
|
||||
node = node.parent; |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,81 +0,0 @@ |
|||||
// <copyright file="PolyTree.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Poly Tree
|
|
||||
/// </summary>
|
|
||||
/// <seealso cref="ImageSharp.Drawing.Shapes.PolygonClipper.PolyNode" />
|
|
||||
internal class PolyTree : PolyNode |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// All polys
|
|
||||
/// </summary>
|
|
||||
internal List<PolyNode> AllPolys = new List<PolyNode>(); |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the total.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The total.
|
|
||||
/// </value>
|
|
||||
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; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Clears this instance.
|
|
||||
/// </summary>
|
|
||||
public void Clear() |
|
||||
{ |
|
||||
for (int i = 0; i < this.AllPolys.Count; i++) |
|
||||
{ |
|
||||
this.AllPolys[i] = null; |
|
||||
} |
|
||||
|
|
||||
this.AllPolys.Clear(); |
|
||||
this.Children.Clear(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the first.
|
|
||||
/// </summary>
|
|
||||
/// <returns>the first node</returns>
|
|
||||
public PolyNode GetFirst() |
|
||||
{ |
|
||||
if (this.Children.Count > 0) |
|
||||
{ |
|
||||
return this.Children[0]; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
// <copyright file="PolyType.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Poly Type
|
|
||||
/// </summary>
|
|
||||
internal enum PolyType |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The subject
|
|
||||
/// </summary>
|
|
||||
Subject, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The clip
|
|
||||
/// </summary>
|
|
||||
Clip |
|
||||
} |
|
||||
} |
|
||||
@ -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 * |
|
||||
* * |
|
||||
*******************************************************************************/ |
|
||||
``` |
|
||||
@ -1,33 +0,0 @@ |
|||||
// <copyright file="Scanbeam.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Scanbeam
|
|
||||
/// </summary>
|
|
||||
internal class Scanbeam // would this work as a struct?
|
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The y
|
|
||||
/// </summary>
|
|
||||
internal float Y; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next
|
|
||||
/// </summary>
|
|
||||
internal Scanbeam Next; |
|
||||
#pragma warning restore SA1401 // Field must be private
|
|
||||
} |
|
||||
} |
|
||||
@ -1,118 +0,0 @@ |
|||||
// <copyright file="TEdge.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Drawing.Shapes.PolygonClipper |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
using Paths; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// TEdge
|
|
||||
/// </summary>
|
|
||||
internal class TEdge |
|
||||
{ |
|
||||
#pragma warning disable SA1401 // Field must be private
|
|
||||
/// <summary>
|
|
||||
/// The source path, see if we can link this back later
|
|
||||
/// </summary>
|
|
||||
internal IPath SourcePath; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The bot
|
|
||||
/// </summary>
|
|
||||
internal System.Numerics.Vector2 Bot; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The current (updated for every new scanbeam)
|
|
||||
/// </summary>
|
|
||||
internal System.Numerics.Vector2 Curr; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The top
|
|
||||
/// </summary>
|
|
||||
internal System.Numerics.Vector2 Top; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The delta
|
|
||||
/// </summary>
|
|
||||
internal System.Numerics.Vector2 Delta; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The dx
|
|
||||
/// </summary>
|
|
||||
internal double Dx; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The poly type
|
|
||||
/// </summary>
|
|
||||
internal PolyType PolyTyp; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Side only refers to current side of solution poly
|
|
||||
/// </summary>
|
|
||||
internal EdgeSide Side; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 1 or -1 depending on winding direction
|
|
||||
/// </summary>
|
|
||||
internal int WindDelta; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The winding count
|
|
||||
/// </summary>
|
|
||||
internal int WindCnt; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The winding count of the opposite polytype
|
|
||||
/// </summary>
|
|
||||
internal int WindCnt2; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The out index
|
|
||||
/// </summary>
|
|
||||
internal int OutIdx; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next
|
|
||||
/// </summary>
|
|
||||
internal TEdge Next; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The previous
|
|
||||
/// </summary>
|
|
||||
internal TEdge Prev; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next in LML
|
|
||||
/// </summary>
|
|
||||
internal TEdge NextInLML; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next in ael
|
|
||||
/// </summary>
|
|
||||
internal TEdge NextInAEL; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The previous in ael
|
|
||||
/// </summary>
|
|
||||
internal TEdge PrevInAEL; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The next in sel
|
|
||||
/// </summary>
|
|
||||
internal TEdge NextInSEL; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The previous in sel
|
|
||||
/// </summary>
|
|
||||
internal TEdge PrevInSEL; |
|
||||
#pragma warning restore SA1401 // Field must be
|
|
||||
} |
|
||||
} |
|
||||
@ -1,281 +0,0 @@ |
|||||
// <copyright file="RectangularPolygon.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
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; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A way of optermising drawing rectangles.
|
|
||||
/// </summary>
|
|
||||
/// <seealso cref="ImageSharp.Drawing.Shapes.IShape" />
|
|
||||
public class RectangularPolygon : IShape, IPath |
|
||||
{ |
|
||||
private readonly RectangleF rectangle; |
|
||||
private readonly Vector2 topLeft; |
|
||||
private readonly Vector2 bottomRight; |
|
||||
private readonly Vector2[] points; |
|
||||
private readonly IEnumerable<IPath> pathCollection; |
|
||||
private readonly float halfLength; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="RectangularPolygon"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rect">The rect.</param>
|
|
||||
public RectangularPolygon(ImageSharp.Rectangle rect) |
|
||||
: this((RectangleF)rect) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="RectangularPolygon"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rect">The rect.</param>
|
|
||||
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 }; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the bounding box of this shape.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The bounds.
|
|
||||
/// </value>
|
|
||||
public RectangleF Bounds => this.rectangle; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether this instance is closed.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// <c>true</c> if this instance is closed; otherwise, <c>false</c>.
|
|
||||
/// </value>
|
|
||||
public bool IsClosed => true; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the length of the path
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The length.
|
|
||||
/// </value>
|
|
||||
public float Length { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
|
||||
/// </summary>
|
|
||||
/// <value>
|
|
||||
/// The maximum intersections.
|
|
||||
/// </value>
|
|
||||
public int MaxIntersections => 4; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculates the distance along and away from the path for a specified point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point along the path.</param>
|
|
||||
/// <returns>
|
|
||||
/// Returns details about the point and its distance away from the path.
|
|
||||
/// </returns>
|
|
||||
PointInfo IPath.Distance(Vector2 point) |
|
||||
{ |
|
||||
bool inside; // dont care about inside/outside for paths just distance
|
|
||||
return this.Distance(point, false, out inside); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
|
|
||||
/// </summary>
|
|
||||
/// <param name="point">The point.</param>
|
|
||||
/// <returns>
|
|
||||
/// Returns the distance from the shape to the point
|
|
||||
/// </returns>
|
|
||||
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; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through the collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An enumerator that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
public IEnumerator<IPath> GetEnumerator() |
|
||||
{ |
|
||||
return this.pathCollection.GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns an enumerator that iterates through a collection.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
|
|
||||
/// </returns>
|
|
||||
IEnumerator IEnumerable.GetEnumerator() |
|
||||
{ |
|
||||
return this.pathCollection.GetEnumerator(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts the <see cref="ILineSegment" /> into a simple linear path..
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
|
|
||||
/// </returns>
|
|
||||
public Vector2[] AsSimpleLinearPath() |
|
||||
{ |
|
||||
return this.points; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
|
|
||||
/// populate a buffer for all points on the edges of the <see cref="RectangularPolygon"/>
|
|
||||
/// that the line intersects.
|
|
||||
/// </summary>
|
|
||||
/// <param name="start">The start point of the line.</param>
|
|
||||
/// <param name="end">The end point of the line.</param>
|
|
||||
/// <param name="buffer">The buffer that will be populated with intersections.</param>
|
|
||||
/// <param name="count">The count.</param>
|
|
||||
/// <param name="offset">The offset.</param>
|
|
||||
/// <returns>
|
|
||||
/// The number of intersections populated into the buffer.
|
|
||||
/// </returns>
|
|
||||
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 |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue