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