Browse Source

migrate to third party shapes library

af/merge-core
Scott Williams 9 years ago
parent
commit
7d253d81ed
  1. 1
      NuGet.config
  2. 3
      src/ImageSharp.Drawing/Draw.cs
  3. 6
      src/ImageSharp.Drawing/DrawRectangle.cs
  4. 4
      src/ImageSharp.Drawing/Fill.cs
  5. 5
      src/ImageSharp.Drawing/FillRectangle.cs
  6. 119
      src/ImageSharp.Drawing/Paths/BezierLineSegment.cs
  7. 21
      src/ImageSharp.Drawing/Paths/ILineSegment.cs
  8. 48
      src/ImageSharp.Drawing/Paths/IPath.cs
  9. 516
      src/ImageSharp.Drawing/Paths/InternalPath.cs
  10. 55
      src/ImageSharp.Drawing/Paths/LinearLineSegment.cs
  11. 51
      src/ImageSharp.Drawing/Paths/Path.cs
  12. 1
      src/ImageSharp.Drawing/Pens/Pen{TColor}.cs
  13. 1
      src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs
  14. 18
      src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs
  15. 30
      src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
  16. 4
      src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs
  17. 37
      src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs
  18. 32
      src/ImageSharp.Drawing/Processors/RectangleExtensions.cs
  19. 93
      src/ImageSharp.Drawing/Shapes/BezierPolygon.cs
  20. 246
      src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs
  21. 56
      src/ImageSharp.Drawing/Shapes/IShape.cs
  22. 93
      src/ImageSharp.Drawing/Shapes/LinearPolygon.cs
  23. 156
      src/ImageSharp.Drawing/Shapes/Polygon.cs
  24. 3860
      src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs
  25. 31
      src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs
  26. 31
      src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs
  27. 31
      src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs
  28. 38
      src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs
  29. 48
      src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs
  30. 38
      src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs
  31. 44
      src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs
  32. 38
      src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs
  33. 43
      src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs
  34. 64
      src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs
  35. 179
      src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs
  36. 81
      src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs
  37. 31
      src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs
  38. 40
      src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md
  39. 33
      src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs
  40. 118
      src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs
  41. 281
      src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs
  42. 1
      src/ImageSharp.Drawing/project.json
  43. 10
      tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs

1
NuGet.config

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="myget.org Six Labors" value="https://www.myget.org/F/sixlabors/api/v3/index.json" />
<add key="myget.org dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />

3
src/ImageSharp.Drawing/Draw.cs

@ -9,10 +9,9 @@ namespace ImageSharp
using System.Numerics;
using Drawing;
using Drawing.Brushes;
using Drawing.Paths;
using Drawing.Pens;
using Drawing.Processors;
using Drawing.Shapes;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TColor}"/> type.

6
src/ImageSharp.Drawing/DrawRectangle.cs

@ -9,10 +9,10 @@ namespace ImageSharp
using Drawing;
using Drawing.Brushes;
using Drawing.Paths;
using Drawing.Pens;
using Drawing.Processors;
using Drawing.Shapes;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TColor}"/> type.
@ -33,7 +33,7 @@ namespace ImageSharp
public static Image<TColor> DrawPolygon<TColor>(this Image<TColor> source, IPen<TColor> pen, RectangleF shape, GraphicsOptions options)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
return source.Apply(new DrawPathProcessor<TColor>(pen, (IPath)new RectangularPolygon(shape), options));
return source.Apply(new DrawPathProcessor<TColor>(pen, (IPath)new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options));
}
/// <summary>

4
src/ImageSharp.Drawing/Fill.cs

@ -9,9 +9,9 @@ namespace ImageSharp
using System.Numerics;
using Drawing;
using Drawing.Brushes;
using Drawing.Paths;
using Drawing.Processors;
using Drawing.Shapes;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TColor}"/> type.

5
src/ImageSharp.Drawing/FillRectangle.cs

@ -10,7 +10,6 @@ namespace ImageSharp
using Drawing;
using Drawing.Brushes;
using Drawing.Processors;
using Drawing.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TColor}"/> type.
@ -31,7 +30,7 @@ namespace ImageSharp
public static Image<TColor> Fill<TColor>(this Image<TColor> source, IBrush<TColor> brush, RectangleF shape, GraphicsOptions options)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
return source.Apply(new FillShapeProcessor<TColor>(brush, new RectangularPolygon(shape), options));
return source.Apply(new FillShapeProcessor<TColor>(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options));
}
/// <summary>
@ -45,7 +44,7 @@ namespace ImageSharp
public static Image<TColor> Fill<TColor>(this Image<TColor> source, IBrush<TColor> brush, RectangleF shape)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
return source.Apply(new FillShapeProcessor<TColor>(brush, new RectangularPolygon(shape), GraphicsOptions.Default));
return source.Apply(new FillShapeProcessor<TColor>(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), GraphicsOptions.Default));
}
/// <summary>

119
src/ImageSharp.Drawing/Paths/BezierLineSegment.cs

@ -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;
}
}
}

21
src/ImageSharp.Drawing/Paths/ILineSegment.cs

@ -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
}
}

48
src/ImageSharp.Drawing/Paths/IPath.cs

@ -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);
}
}

516
src/ImageSharp.Drawing/Paths/InternalPath.cs

@ -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;
}
}
}

55
src/ImageSharp.Drawing/Paths/LinearLineSegment.cs

@ -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;
}
}
}

51
src/ImageSharp.Drawing/Paths/Path.cs

@ -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);
}
}
}

1
src/ImageSharp.Drawing/Pens/Pen{TColor}.cs

@ -9,7 +9,6 @@ namespace ImageSharp.Drawing.Pens
using System.Numerics;
using ImageSharp.Drawing.Brushes;
using ImageSharp.Drawing.Paths;
using Processors;
/// <summary>

1
src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs

@ -6,7 +6,6 @@
namespace ImageSharp.Drawing.Processors
{
using System;
using Paths;
/// <summary>
/// primitive that converts a <see cref="PointInfo"/> into a color and a distance away from the drawable part of the path.

18
src/ImageSharp.Drawing/Paths/PointInfo.cs → src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs

@ -3,33 +3,29 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing.Paths
namespace ImageSharp.Drawing.Processors
{
using System;
using System.Numerics;
/// <summary>
/// Returns meta data about the nearest point on a path from a vector
/// Returns details about how far away from the inside of a shape and the color the pixel could be.
/// </summary>
public struct PointInfo
{
/// <summary>
/// The search point
/// </summary>
public Vector2 SearchPoint;
/// <summary>
/// The distance along path <see cref="ClosestPointOnPath"/> is away from the start of the path
/// The distance along path
/// </summary>
public float DistanceAlongPath;
/// <summary>
/// The distance <see cref="SearchPoint"/> is away from <see cref="ClosestPointOnPath"/>.
/// The distance from path
/// </summary>
public float DistanceFromPath;
/// <summary>
/// The closest point to <see cref="SearchPoint"/> that lies on the path.
/// The search point
/// </summary>
public Vector2 ClosestPointOnPath;
public Vector2 SearchPoint;
}
}

30
src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs

@ -6,13 +6,13 @@
namespace ImageSharp.Drawing.Processors
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using ImageSharp.Processing;
using Paths;
using Pens;
using Shapes;
using SixLabors.Shapes;
using Rectangle = ImageSharp.Rectangle;
/// <summary>
@ -38,7 +38,7 @@ namespace ImageSharp.Drawing.Processors
/// <param name="shape">The shape.</param>
/// <param name="options">The options.</param>
public DrawPathProcessor(IPen<TColor> pen, IShape shape, GraphicsOptions options)
: this(pen, shape.ToArray(), options)
: this(pen, shape.Paths, options)
{
}
@ -59,24 +59,24 @@ namespace ImageSharp.Drawing.Processors
/// <param name="pen">The pen.</param>
/// <param name="paths">The paths.</param>
/// <param name="options">The options.</param>
public DrawPathProcessor(IPen<TColor> pen, IPath[] paths, GraphicsOptions options)
public DrawPathProcessor(IPen<TColor> pen, IEnumerable<IPath> paths, GraphicsOptions options)
{
this.paths = paths;
this.paths = paths.ToArray();
this.pen = pen;
this.options = options;
if (paths.Length != 1)
if (this.paths.Length != 1)
{
var maxX = paths.Max(x => x.Bounds.Right);
var minX = paths.Min(x => x.Bounds.Left);
var maxY = paths.Max(x => x.Bounds.Bottom);
var minY = paths.Min(x => x.Bounds.Top);
var maxX = this.paths.Max(x => x.Bounds.Right);
var minX = this.paths.Min(x => x.Bounds.Left);
var maxY = this.paths.Max(x => x.Bounds.Bottom);
var minY = this.paths.Min(x => x.Bounds.Top);
this.region = new RectangleF(minX, minY, maxX - minX, maxY - minY);
}
else
{
this.region = paths[0].Bounds;
this.region = this.paths[0].Bounds.Convert();
}
}
@ -119,7 +119,7 @@ namespace ImageSharp.Drawing.Processors
minY,
maxY,
this.ParallelOptions,
y =>
(int y) =>
{
int offsetY = y - polyStartY;
var currentPoint = default(Vector2);
@ -131,7 +131,7 @@ namespace ImageSharp.Drawing.Processors
var dist = this.Closest(currentPoint);
var color = applicator.GetColor(dist);
var color = applicator.GetColor(dist.Convert());
var opacity = this.Opacity(color.DistanceFromElement);
@ -154,9 +154,9 @@ namespace ImageSharp.Drawing.Processors
}
}
private PointInfo Closest(Vector2 point)
private SixLabors.Shapes.PointInfo Closest(Vector2 point)
{
PointInfo result = default(PointInfo);
SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo);
float distance = float.MaxValue;
for (int i = 0; i < this.paths.Length; i++)

4
src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs

@ -11,7 +11,7 @@ namespace ImageSharp.Drawing.Processors
using System.Threading.Tasks;
using Drawing;
using ImageSharp.Processing;
using Shapes;
using SixLabors.Shapes;
using Rectangle = ImageSharp.Rectangle;
/// <summary>
@ -44,7 +44,7 @@ namespace ImageSharp.Drawing.Processors
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
Rectangle rect = RectangleF.Ceiling(this.poly.Bounds); // rounds the points out away from the center
Rectangle rect = RectangleF.Ceiling(this.poly.Bounds.Convert()); // rounds the points out away from the center
int polyStartY = rect.Y - DrawPadding;
int polyEndY = rect.Bottom + DrawPadding;

37
src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs

@ -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
};
}
}
}

32
src/ImageSharp.Drawing/Processors/RectangleExtensions.cs

@ -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);
}
}
}

93
src/ImageSharp.Drawing/Shapes/BezierPolygon.cs

@ -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();
}
}
}

246
src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs

@ -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;
}
}
}

56
src/ImageSharp.Drawing/Shapes/IShape.cs

@ -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);
}
}

93
src/ImageSharp.Drawing/Shapes/LinearPolygon.cs

@ -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();
}
}

156
src/ImageSharp.Drawing/Shapes/Polygon.cs

@ -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);
}
}
}

3860
src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs

File diff suppressed because it is too large

31
src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs

@ -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)
{
}
}
}

31
src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs

@ -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
}
}

31
src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs

@ -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
}
}

38
src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs

@ -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
}
}

48
src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs

@ -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;
}
}
}
}

38
src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs

@ -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
}
}

44
src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs

@ -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
}
}

38
src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs

@ -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
}
}

43
src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs

@ -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
}
}

64
src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs

@ -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
}
}

179
src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs

@ -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;
}
}
}

81
src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs

@ -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;
}
}
}
}

31
src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs

@ -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
}
}

40
src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md

@ -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 *
* *
*******************************************************************************/
```

33
src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs

@ -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
}
}

118
src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs

@ -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
}
}

281
src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs

@ -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
};
}
}
}

1
src/ImageSharp.Drawing/project.json

@ -46,6 +46,7 @@
"target": "project",
"version": "1.0.0-*"
},
"SixLabors.Shapes": "0.1.0-ci0043",
"StyleCop.Analyzers": {
"version": "1.0.0",
"type": "build"

10
tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs

@ -39,7 +39,7 @@ namespace ImageSharp.Benchmarks
{
using (CoreImage image = new CoreImage(800, 800))
{
image.Fill(CoreColor.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new CoreRectangle(10, 10, 190, 140)));
image.Fill(CoreColor.HotPink, new SixLabors.Shapes.Rectangle(10, 10, 190, 140));
return new CoreSize(image.Width, image.Height);
}
@ -53,10 +53,10 @@ namespace ImageSharp.Benchmarks
image.FillPolygon(
CoreColor.HotPink,
new[] {
new Vector2(10, 10),
new Vector2(200, 10),
new Vector2(200, 150),
new Vector2(10, 150) });
new Vector2(10, 10),
new Vector2(200, 10),
new Vector2(200, 150),
new Vector2(10, 150) });
return new CoreSize(image.Width, image.Height);
}

Loading…
Cancel
Save