зеркало из https://github.com/SixLabors/Shapes.git
prefilter and calculate data per point
This commit is contained in:
Родитель
c1810e8d9c
Коммит
172236263d
|
@ -257,3 +257,4 @@ paket-files/
|
|||
|
||||
/tests/CodeCoverage/OpenCover.*
|
||||
SixLabors.Shapes.Coverage.xml
|
||||
/tests/SixLabors.Shapes.Benchmarks/BenchmarkDotNet.Artifacts/results/
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.4
|
||||
VisualStudioVersion = 15.0.26228.10
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
@ -28,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SixLabors.Shapes.Tests", "t
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DrawShapesWithImageSharp", "samples\DrawShapesWithImageSharp\DrawShapesWithImageSharp.csproj", "{999EDFB3-9FE4-4E09-B669-CB02E597EC20}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SixLabors.Shapes.Benchmarks", "tests\SixLabors.Shapes.Benchmarks\SixLabors.Shapes.Benchmarks.csproj", "{87E262FA-57FE-4AA7-853C-9DD91E769D4B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -46,6 +48,10 @@ Global
|
|||
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{87E262FA-57FE-4AA7-853C-9DD91E769D4B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -55,5 +61,6 @@ Global
|
|||
{09E744EC-4852-4FC7-BE78-C1B399F17967} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
|
||||
{F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
|
||||
{999EDFB3-9FE4-4E09-B669-CB02E597EC20} = {9F33164A-9EA9-4CB4-A384-A8A0A6DCA35D}
|
||||
{87E262FA-57FE-4AA7-853C-9DD91E769D4B} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -35,3 +35,4 @@ using System.Runtime.CompilerServices;
|
|||
|
||||
// Ensure the internals can be tested.
|
||||
[assembly: InternalsVisibleTo("SixLabors.Shapes.Tests")]
|
||||
[assembly: InternalsVisibleTo("SixLabors.Shapes.Benchmarks")]
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace SixLabors.Shapes
|
|||
/// <summary>
|
||||
/// Gets the points that make up this simple linear path.
|
||||
/// </summary>
|
||||
ImmutableArray<Vector2> ISimplePath.Points => this.innerPath.Points;
|
||||
ImmutableArray<Vector2> ISimplePath.Points => this.innerPath.Points();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Rectangle Bounds => this.innerPath.Bounds;
|
||||
|
@ -92,7 +92,7 @@ namespace SixLabors.Shapes
|
|||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
int IPath.MaxIntersections => this.innerPath.Points.Length;
|
||||
int IPath.MaxIntersections => this.innerPath.PointCount;
|
||||
|
||||
/// <inheritdoc />
|
||||
public PointInfo Distance(Vector2 point)
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace SixLabors.Shapes
|
|||
/// <summary>
|
||||
/// The points.
|
||||
/// </summary>
|
||||
private readonly ImmutableArray<Vector2> points;
|
||||
private readonly PointData[] points;
|
||||
|
||||
/// <summary>
|
||||
/// The closed path.
|
||||
|
@ -62,18 +62,8 @@ namespace SixLabors.Shapes
|
|||
/// </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(ImmutableArray.Create(segments), isClosedPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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(ImmutableArray<ILineSegment> segments, bool isClosedPath)
|
||||
: this(Simplify(segments), isClosedPath)
|
||||
internal InternalPath(IEnumerable<ILineSegment> segments, bool isClosedPath)
|
||||
: this(Simplify(segments, isClosedPath), isClosedPath)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -83,7 +73,7 @@ namespace SixLabors.Shapes
|
|||
/// <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?.Flatten() ?? ImmutableArray<Vector2>.Empty, isClosedPath)
|
||||
: this(segment?.Flatten() ?? Enumerable.Empty<Vector2>(), isClosedPath)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -92,18 +82,27 @@ namespace SixLabors.Shapes
|
|||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
/// <param name="isClosedPath">if set to <c>true</c> [is closed path].</param>
|
||||
internal InternalPath(ImmutableArray<Vector2> points, bool isClosedPath)
|
||||
internal InternalPath(IEnumerable<Vector2> points, bool isClosedPath)
|
||||
: this(Simplify(points, isClosedPath), 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>
|
||||
private InternalPath(PointData[] 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);
|
||||
float minX = this.points.Min(x => x.Point.X);
|
||||
float maxX = this.points.Max(x => x.Point.X);
|
||||
float minY = this.points.Min(x => x.Point.Y);
|
||||
float maxY = this.points.Max(x => x.Point.Y);
|
||||
|
||||
this.Bounds = new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
this.totalDistance = new Lazy<float>(this.CalculateLength);
|
||||
this.Length = this.points.Sum(x => x.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -165,7 +164,15 @@ namespace SixLabors.Shapes
|
|||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
public float Length => this.totalDistance.Value;
|
||||
public float Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
public int PointCount => this.points.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the points.
|
||||
|
@ -173,7 +180,7 @@ namespace SixLabors.Shapes
|
|||
/// <value>
|
||||
/// The points.
|
||||
/// </value>
|
||||
internal ImmutableArray<Vector2> Points => this.points;
|
||||
internal ImmutableArray<Vector2> Points() => this.points.Select(X=>X.Point).ToImmutableArray();
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance from the path.
|
||||
|
@ -183,7 +190,6 @@ namespace SixLabors.Shapes
|
|||
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
|
||||
|
||||
|
@ -203,7 +209,7 @@ namespace SixLabors.Shapes
|
|||
next = 0;
|
||||
}
|
||||
|
||||
if (this.CalculateShorterDistance(this.points[i], this.points[next], point, ref internalInfo))
|
||||
if (this.CalculateShorterDistance(this.points[i].Point, this.points[next].Point, point, ref internalInfo))
|
||||
{
|
||||
closestPoint = i;
|
||||
}
|
||||
|
@ -211,7 +217,7 @@ namespace SixLabors.Shapes
|
|||
|
||||
return new PointInfo
|
||||
{
|
||||
DistanceAlongPath = this.distance[closestPoint] + Vector2.Distance(this.points[closestPoint], internalInfo.PointOnLine),
|
||||
DistanceAlongPath = this.distance[closestPoint] + Vector2.Distance(this.points[closestPoint].Point, internalInfo.PointOnLine),
|
||||
DistanceFromPath = (float)Math.Sqrt(internalInfo.DistanceSquared),
|
||||
SearchPoint = point,
|
||||
ClosestPointOnPath = internalInfo.PointOnLine
|
||||
|
@ -241,21 +247,9 @@ namespace SixLabors.Shapes
|
|||
Vector2 lastPoint = MaxVector;
|
||||
if (this.closedPath)
|
||||
{
|
||||
int prev = polyCorners;
|
||||
do
|
||||
{
|
||||
prev--;
|
||||
if (prev == 0)
|
||||
{
|
||||
// all points are common, shouldn't match anything
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
while (this.points[0].Equivelent(this.points[prev], Epsilon2)); // skip points too close together
|
||||
int prev = polyCorners-1;
|
||||
|
||||
lastPoint = FindIntersection(this.points[prev], this.points[0], start, end);
|
||||
|
||||
polyCorners = prev + 1;
|
||||
lastPoint = FindIntersection(this.points[prev].Point, this.points[0].Point, start, end);
|
||||
}
|
||||
|
||||
int inc = 0;
|
||||
|
@ -271,7 +265,7 @@ namespace SixLabors.Shapes
|
|||
inc = 1;
|
||||
}
|
||||
|
||||
if (closedPath && AreColliner(this.points[i], this.points[next], start, end))
|
||||
if (closedPath && AreColliner(this.points[i].Point, this.points[next].Point, start, end))
|
||||
{
|
||||
// lines are colinear and intersect
|
||||
// if this is the case we need to tell if this is an inflection or not
|
||||
|
@ -280,7 +274,7 @@ namespace SixLabors.Shapes
|
|||
while (nextSide == Side.Same)
|
||||
{
|
||||
var nextPlus1 = FindNextPoint(polyCorners, next);
|
||||
nextSide = SideOfLine(this.points[nextPlus1], this.points[i], this.points[next]);
|
||||
nextSide = SideOfLine(this.points[nextPlus1].Point, this.points[i].Point, this.points[next].Point);
|
||||
if (nextSide == Side.Same)
|
||||
{
|
||||
//skip a point
|
||||
|
@ -296,7 +290,7 @@ namespace SixLabors.Shapes
|
|||
}
|
||||
}
|
||||
|
||||
var prevSide = SideOfLine(this.points[lastCorner], this.points[i], this.points[next]);
|
||||
var prevSide = SideOfLine(this.points[lastCorner].Point, this.points[i].Point, this.points[next].Point);
|
||||
if (prevSide != nextSide)
|
||||
{
|
||||
position--;
|
||||
|
@ -305,7 +299,7 @@ namespace SixLabors.Shapes
|
|||
}
|
||||
}
|
||||
|
||||
Vector2 point = FindIntersection(this.points[i], this.points[next], start, end);
|
||||
Vector2 point = FindIntersection(this.points[i].Point, this.points[next].Point, start, end);
|
||||
if (point != MaxVector)
|
||||
{
|
||||
if (lastPoint.Equivelent(point, Epsilon2))
|
||||
|
@ -315,18 +309,18 @@ namespace SixLabors.Shapes
|
|||
int last = (i - 1 + polyCorners) % polyCorners;
|
||||
|
||||
// hit the same point a second time do we need to remove the old one if just clipping
|
||||
if (this.points[next].Equivelent(point, Epsilon))
|
||||
if (this.points[next].Point.Equivelent(point, Epsilon))
|
||||
{
|
||||
next = i;
|
||||
}
|
||||
|
||||
if (this.points[last].Equivelent(point, Epsilon))
|
||||
if (this.points[last].Point.Equivelent(point, Epsilon))
|
||||
{
|
||||
last = i;
|
||||
}
|
||||
|
||||
var side = SideOfLine(this.points[last], start, end);
|
||||
var side2 = SideOfLine(this.points[next], start, end);
|
||||
var side = SideOfLine(this.points[last].Point, start, end);
|
||||
var side2 = SideOfLine(this.points[next].Point, start, end);
|
||||
|
||||
if (side == Side.Same && side2 == Side.Same)
|
||||
{
|
||||
|
@ -346,8 +340,6 @@ namespace SixLabors.Shapes
|
|||
buffer[position + offset] = point;
|
||||
position++;
|
||||
count--;
|
||||
|
||||
|
||||
}
|
||||
|
||||
lastPoint = point;
|
||||
|
@ -361,16 +353,13 @@ namespace SixLabors.Shapes
|
|||
{
|
||||
int inc1 = 0;
|
||||
int nxt = i;
|
||||
do
|
||||
|
||||
nxt = i + 1;
|
||||
if (this.closedPath && nxt == polyCorners)
|
||||
{
|
||||
inc1++;
|
||||
nxt = i + inc1;
|
||||
if (this.closedPath && nxt == polyCorners)
|
||||
{
|
||||
nxt -= polyCorners;
|
||||
}
|
||||
nxt -= polyCorners;
|
||||
}
|
||||
while (this.points[i].Equivelent(this.points[nxt], Epsilon2) && inc1 < polyCorners); // skip points too close together
|
||||
|
||||
return nxt;
|
||||
}
|
||||
|
||||
|
@ -623,7 +612,7 @@ namespace SixLabors.Shapes
|
|||
/// <returns>
|
||||
/// The <see cref="T:Vector2[]"/>.
|
||||
/// </returns>
|
||||
private static ImmutableArray<Vector2> Simplify(ImmutableArray<ILineSegment> segments)
|
||||
private static PointData[] Simplify(IEnumerable<ILineSegment> segments, bool isClosed)
|
||||
{
|
||||
List<Vector2> simplified = new List<Vector2>();
|
||||
foreach (ILineSegment seg in segments)
|
||||
|
@ -631,7 +620,81 @@ namespace SixLabors.Shapes
|
|||
simplified.AddRange(seg.Flatten());
|
||||
}
|
||||
|
||||
return simplified.ToImmutableArray();
|
||||
return Simplify(simplified, isClosed);
|
||||
}
|
||||
|
||||
private static PointData[] Simplify(IEnumerable<Vector2> vectors, bool isClosed)
|
||||
{
|
||||
Vector2[] points = vectors.ToArray();
|
||||
List<PointData> results = new List<PointData>();
|
||||
|
||||
int polyCorners = points.Length;
|
||||
|
||||
Vector2 lastPoint = points[0];
|
||||
|
||||
if (!isClosed)
|
||||
{
|
||||
results.Add(new PointData
|
||||
{
|
||||
Point = points[0],
|
||||
Orientation = Orientation.Colinear,
|
||||
Length = 0
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isClosed)
|
||||
{
|
||||
int prev = polyCorners;
|
||||
do
|
||||
{
|
||||
prev--;
|
||||
if (prev == 0)
|
||||
{
|
||||
// all points are common, shouldn't match anything
|
||||
results.Add(new PointData
|
||||
{
|
||||
Point = points[0],
|
||||
Orientation = Orientation.Colinear,
|
||||
Length = 0
|
||||
});
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
while (points[0].Equivelent(points[prev], Epsilon2)); // skip points too close together
|
||||
polyCorners = prev + 1;
|
||||
lastPoint = points[prev];
|
||||
}
|
||||
|
||||
results.Add(new PointData
|
||||
{
|
||||
Point = points[0],
|
||||
Orientation = CalulateOrientation(lastPoint, points[0], points[1]),
|
||||
Length = Vector2.Distance(lastPoint, points[0])
|
||||
});
|
||||
|
||||
lastPoint = points[0];
|
||||
}
|
||||
|
||||
for (var i = 1; i < polyCorners; i++)
|
||||
{
|
||||
var next = (i + 1) % polyCorners;
|
||||
var or = CalulateOrientation(lastPoint, points[i], points[next]);
|
||||
if(or == Orientation.Colinear && next != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
results.Add(
|
||||
new PointData
|
||||
{
|
||||
Point = points[i],
|
||||
Orientation = or,
|
||||
Length = Vector2.Distance(lastPoint, points[i])
|
||||
});
|
||||
lastPoint = points[i];
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -658,7 +721,7 @@ namespace SixLabors.Shapes
|
|||
next = 0;
|
||||
}
|
||||
|
||||
length += Vector2.Distance(this.points[i], this.points[next]);
|
||||
length += this.points[i].Length;
|
||||
}
|
||||
|
||||
return length;
|
||||
|
@ -682,7 +745,7 @@ namespace SixLabors.Shapes
|
|||
return;
|
||||
}
|
||||
|
||||
ImmutableArray<Vector2> poly = this.points;
|
||||
PointData[] poly = this.points;
|
||||
int polyCorners = poly.Length;
|
||||
this.distance = new float[polyCorners];
|
||||
|
||||
|
@ -691,7 +754,7 @@ namespace SixLabors.Shapes
|
|||
for (int i = 1; i < polyCorners; i++)
|
||||
{
|
||||
int previousIndex = i - 1;
|
||||
this.distance[i] = this.distance[previousIndex] + Vector2.Distance(poly[i], poly[previousIndex]);
|
||||
this.distance[i] = this.distance[previousIndex] + Vector2.Distance(poly[i].Point, poly[previousIndex].Point);
|
||||
}
|
||||
|
||||
this.calculated = true;
|
||||
|
@ -760,5 +823,13 @@ namespace SixLabors.Shapes
|
|||
/// </summary>
|
||||
public Vector2 PointOnLine;
|
||||
}
|
||||
|
||||
private struct PointData
|
||||
{
|
||||
public Vector2 Point;
|
||||
public Orientation Orientation;
|
||||
|
||||
public float Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,764 @@
|
|||
// <copyright file="InternalPath.cs" company="Scott Williams">
|
||||
// Copyright (c) Scott Williams and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
||||
// </copyright>
|
||||
namespace SixLabors.Shapes
|
||||
{
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
/// <summary>
|
||||
/// Internal logic for integrating linear paths.
|
||||
/// </summary>
|
||||
internal class InternalPath_Old
|
||||
{
|
||||
/// <summary>
|
||||
/// The epsilon for float comparison
|
||||
/// </summary>
|
||||
private const float Epsilon = 0.003f;
|
||||
private const float Epsilon2 = 0.2f;
|
||||
|
||||
/// <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 ImmutableArray<Vector2> points;
|
||||
|
||||
/// <summary>
|
||||
/// The closed path.
|
||||
/// </summary>
|
||||
private readonly bool closedPath;
|
||||
|
||||
/// <summary>
|
||||
/// The total distance.
|
||||
/// </summary>
|
||||
private readonly Lazy<float> totalDistance;
|
||||
|
||||
/// <summary>
|
||||
/// The distances
|
||||
/// </summary>
|
||||
private float[] distance;
|
||||
|
||||
/// <summary>
|
||||
/// The calculated.
|
||||
/// </summary>
|
||||
private bool calculated;
|
||||
|
||||
/// <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_Old(ILineSegment[] segments, bool isClosedPath)
|
||||
: this(ImmutableArray.Create(segments), isClosedPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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_Old(ImmutableArray<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_Old(ILineSegment segment, bool isClosedPath)
|
||||
: this(segment?.Flatten() ?? ImmutableArray<Vector2>.Empty, 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_Old(IEnumerable<Vector2> points, bool isClosedPath)
|
||||
{
|
||||
this.points = points.ToArray().ToImmutableArray();
|
||||
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 Rectangle(minX, minY, maxX - minX, maxY - minY);
|
||||
this.totalDistance = new Lazy<float>(this.CalculateLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The sides a point can land on
|
||||
/// </summary>
|
||||
public enum Side
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the point falls on the left logical side of the line.
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the point falls on the right logical side of the line.
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// /// Indicates the point falls exactly on the line.
|
||||
/// </summary>
|
||||
Same
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the orrientateion of an point form a line
|
||||
/// </summary>
|
||||
private enum Orientation
|
||||
{
|
||||
/// <summary>
|
||||
/// POint is colienier
|
||||
/// </summary>
|
||||
Colinear = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Its clockwise
|
||||
/// </summary>
|
||||
Clockwise = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Its counter clockwise
|
||||
/// </summary>
|
||||
Counterclockwise = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounds.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The bounds.
|
||||
/// </value>
|
||||
public Rectangle 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 ImmutableArray<Vector2> Points => this.points;
|
||||
|
||||
public int PointCount => this.points.Length;
|
||||
|
||||
/// <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], internalInfo.PointOnLine),
|
||||
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" />
|
||||
/// populates 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 of 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;
|
||||
Vector2 lastPoint = MaxVector;
|
||||
if (this.closedPath)
|
||||
{
|
||||
int prev = polyCorners;
|
||||
do
|
||||
{
|
||||
prev--;
|
||||
if (prev == 0)
|
||||
{
|
||||
// all points are common, shouldn't match anything
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
while (this.points[0].Equivelent(this.points[prev], Epsilon2)); // skip points too close together
|
||||
|
||||
lastPoint = FindIntersection(this.points[prev], this.points[0], start, end);
|
||||
|
||||
polyCorners = prev + 1;
|
||||
}
|
||||
|
||||
int inc = 0;
|
||||
int lastCorner = polyCorners-1;
|
||||
for (int i = 0; i < polyCorners && count > 0; i += inc)
|
||||
{
|
||||
int next = FindNextPoint(polyCorners, i);
|
||||
if (next > i)
|
||||
{
|
||||
inc = next - i;
|
||||
}else
|
||||
{
|
||||
inc = 1;
|
||||
}
|
||||
|
||||
if (closedPath && AreColliner(this.points[i], this.points[next], start, end))
|
||||
{
|
||||
// lines are colinear and intersect
|
||||
// if this is the case we need to tell if this is an inflection or not
|
||||
var nextSide = Side.Same;
|
||||
// keep going next untill we are no longer on the line
|
||||
while (nextSide == Side.Same)
|
||||
{
|
||||
var nextPlus1 = FindNextPoint(polyCorners, next);
|
||||
nextSide = SideOfLine(this.points[nextPlus1], this.points[i], this.points[next]);
|
||||
if (nextSide == Side.Same)
|
||||
{
|
||||
//skip a point
|
||||
next = nextPlus1;
|
||||
if (nextPlus1 > next)
|
||||
{
|
||||
inc += nextPlus1 - next;
|
||||
}
|
||||
else
|
||||
{
|
||||
inc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var prevSide = SideOfLine(this.points[lastCorner], this.points[i], this.points[next]);
|
||||
if (prevSide != nextSide)
|
||||
{
|
||||
position--;
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 point = FindIntersection(this.points[i], this.points[next], start, end);
|
||||
if (point != MaxVector)
|
||||
{
|
||||
if (lastPoint.Equivelent(point, Epsilon2))
|
||||
{
|
||||
lastPoint = MaxVector;
|
||||
|
||||
int last = (i - 1 + polyCorners) % polyCorners;
|
||||
|
||||
// hit the same point a second time do we need to remove the old one if just clipping
|
||||
if (this.points[next].Equivelent(point, Epsilon))
|
||||
{
|
||||
next = i;
|
||||
}
|
||||
|
||||
if (this.points[last].Equivelent(point, Epsilon))
|
||||
{
|
||||
last = i;
|
||||
}
|
||||
|
||||
var side = SideOfLine(this.points[last], start, end);
|
||||
var side2 = SideOfLine(this.points[next], start, end);
|
||||
|
||||
if (side == Side.Same && side2 == Side.Same)
|
||||
{
|
||||
position--;
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (side != side2)
|
||||
{
|
||||
// differnet side we skip adding as we are passing through it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// we are not double crossing so just add it once
|
||||
buffer[position + offset] = point;
|
||||
position++;
|
||||
count--;
|
||||
}
|
||||
|
||||
lastPoint = point;
|
||||
lastCorner = i;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
private int FindNextPoint(int polyCorners, int i)
|
||||
{
|
||||
int inc1 = 0;
|
||||
int nxt = i;
|
||||
do
|
||||
{
|
||||
inc1++;
|
||||
nxt = i + inc1;
|
||||
if (this.closedPath && nxt == polyCorners)
|
||||
{
|
||||
nxt -= polyCorners;
|
||||
}
|
||||
}
|
||||
while (this.points[i].Equivelent(this.points[nxt], Epsilon2) && inc1 < polyCorners); // skip points too close together
|
||||
return nxt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ares the colliner.
|
||||
/// </summary>
|
||||
/// <param name="vector21">The vector21.</param>
|
||||
/// <param name="vector22">The vector22.</param>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <returns></returns>
|
||||
private bool AreColliner(Vector2 vector21, Vector2 vector22, Vector2 start, Vector2 end)
|
||||
{
|
||||
return SideOfLine(vector21, start, end) == Side.Same && SideOfLine(vector22, start, end) == Side.Same && DoIntersect(vector21, vector22, start, end);
|
||||
}
|
||||
|
||||
/// <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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if it hit any points then class it as inside
|
||||
var buffer = ArrayPool<Vector2>.Shared.Rent(this.points.Length);
|
||||
try
|
||||
{
|
||||
var intersection = this.FindIntersections(point, new Vector2(this.Bounds.Left - 1, this.Bounds.Top - 1), buffer, this.points.Length, 0);
|
||||
if (intersection % 2 == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if the point is on an intersection is it is then inside
|
||||
for (var i = 0; i < intersection; i++)
|
||||
{
|
||||
if (buffer[i].Equivelent(point, Epsilon))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Vector2>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Side SideOfLine(Vector2 test, Vector2 lineStart, Vector2 lineEnd)
|
||||
{
|
||||
var testDiff = test - lineStart;
|
||||
var lineDiff = lineEnd - lineStart;
|
||||
if (float.IsInfinity(lineDiff.X))
|
||||
{
|
||||
if (lineDiff.X > 0)
|
||||
{
|
||||
lineDiff.X = float.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineDiff.X = float.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (float.IsInfinity(lineDiff.Y))
|
||||
{
|
||||
if (lineDiff.Y > 0)
|
||||
{
|
||||
lineDiff.Y = float.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineDiff.Y = float.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
var crossProduct = (lineDiff.X * testDiff.Y) - (lineDiff.Y * testDiff.X);
|
||||
|
||||
if (crossProduct > -Epsilon && crossProduct < Epsilon)
|
||||
{
|
||||
return Side.Same;
|
||||
}
|
||||
|
||||
if (crossProduct > 0)
|
||||
{
|
||||
return Side.Left;
|
||||
}
|
||||
|
||||
return Side.Right;
|
||||
}
|
||||
|
||||
private static bool DoIntersect(Vector2 p1, Vector2 q1, Vector2 p2, Vector2 q2)
|
||||
{
|
||||
// Find the four orientations needed for general and
|
||||
// special cases
|
||||
var o1 = CalulateOrientation(p1, q1, p2);
|
||||
var o2 = CalulateOrientation(p1, q1, q2);
|
||||
var o3 = CalulateOrientation(p2, q2, p1);
|
||||
var o4 = CalulateOrientation(p2, q2, q1);
|
||||
|
||||
// General case
|
||||
if (o1 != o2 && o3 != o4)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special Cases
|
||||
// p1, q1 and p2 are colinear and p2 lies on segment p1q1
|
||||
if (o1 == Orientation.Colinear && IsOnSegment(p1, p2, q1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// p1, q1 and p2 are colinear and q2 lies on segment p1q1
|
||||
if (o2 == Orientation.Colinear && IsOnSegment(p1, q2, q1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// p2, q2 and p1 are colinear and p1 lies on segment p2q2
|
||||
if (o3 == Orientation.Colinear && IsOnSegment(p2, p1, q2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// p2, q2 and q1 are colinear and q1 lies on segment p2q2
|
||||
if (o4 == Orientation.Colinear && IsOnSegment(p2, q1, q2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Doesn't fall in any of the above cases
|
||||
}
|
||||
|
||||
private static bool IsOnSegment(Vector2 p, Vector2 q, Vector2 r)
|
||||
{
|
||||
return (q.X-Epsilon2) <= Math.Max(p.X, r.X) && (q.X + Epsilon2) >= Math.Min(p.X, r.X) &&
|
||||
(q.Y - Epsilon2) <= Math.Max(p.Y, r.Y) && (q.Y + Epsilon2) >= Math.Min(p.Y, r.Y);
|
||||
}
|
||||
|
||||
private static Orientation CalulateOrientation(Vector2 p, Vector2 q, Vector2 r)
|
||||
{
|
||||
// See http://www.geeksforgeeks.org/orientation-3-ordered-points/
|
||||
// for details of below formula.
|
||||
float val = ((q.Y - p.Y) * (r.X - q.X)) -
|
||||
((q.X - p.X) * (r.Y - q.Y));
|
||||
|
||||
if (val > -Epsilon && val < Epsilon)
|
||||
{
|
||||
return Orientation.Colinear; // colinear
|
||||
}
|
||||
|
||||
return (val > 0) ? Orientation.Clockwise : Orientation.Counterclockwise; // clock or counterclock wise
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines 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 - Epsilon;
|
||||
float right1 = bottomRight1.X + Epsilon;
|
||||
float top1 = topLeft1.Y - Epsilon;
|
||||
float bottom1 = bottomRight1.Y + Epsilon;
|
||||
|
||||
float left2 = topLeft2.X - Epsilon;
|
||||
float right2 = bottomRight2.X + Epsilon;
|
||||
float top2 = topLeft2.Y - Epsilon;
|
||||
float bottom2 = bottomRight2.Y + Epsilon;
|
||||
|
||||
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>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>
|
||||
/// the number of points on the line that it hit
|
||||
/// </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 (!DoIntersect(line1Start, line1End, line2Start, line2End))
|
||||
{
|
||||
return MaxVector;
|
||||
}
|
||||
|
||||
float x1, y1, x2, y2, x3, y3, x4, y4;
|
||||
x1 = line1Start.X;
|
||||
y1 = line1Start.Y;
|
||||
x2 = line1End.X;
|
||||
y2 = line1End.Y;
|
||||
|
||||
x3 = line2Start.X;
|
||||
y3 = line2Start.Y;
|
||||
x4 = line2End.X;
|
||||
y4 = line2End.Y;
|
||||
|
||||
float inter = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
|
||||
|
||||
if (inter > -Epsilon && inter < Epsilon)
|
||||
{
|
||||
return MaxVector;
|
||||
}
|
||||
|
||||
float x = (((x2 - x1) * ((x3 * y4) - (x4 * y3))) - ((x4 - x3) * ((x1 * y2) - (x2 * y1)))) / inter;
|
||||
float y = (((y3 - y4) * ((x1 * y2) - (x2 * y1))) - ((y1 - y2) * ((x3 * y4) - (x4 * y3)))) / inter;
|
||||
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simplifies the collection of segments.
|
||||
/// </summary>
|
||||
/// <param name="segments">The segments.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="T:Vector2[]"/>.
|
||||
/// </returns>
|
||||
private static ImmutableArray<Vector2> Simplify(ImmutableArray<ILineSegment> segments)
|
||||
{
|
||||
List<Vector2> simplified = new List<Vector2>();
|
||||
foreach (ILineSegment seg in segments)
|
||||
{
|
||||
simplified.AddRange(seg.Flatten());
|
||||
}
|
||||
|
||||
return simplified.ToImmutableArray();
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
ImmutableArray<Vector2> poly = this.points;
|
||||
int polyCorners = poly.Length;
|
||||
this.distance = new float[polyCorners];
|
||||
|
||||
this.distance[0] = 0;
|
||||
|
||||
for (int i = 1; i < polyCorners; i++)
|
||||
{
|
||||
int previousIndex = i - 1;
|
||||
this.distance[i] = this.distance[previousIndex] + Vector2.Distance(poly[i], poly[previousIndex]);
|
||||
}
|
||||
|
||||
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,7 +55,7 @@ namespace SixLabors.Shapes
|
|||
/// <summary>
|
||||
/// Gets the points that make up this simple linear path.
|
||||
/// </summary>
|
||||
ImmutableArray<Vector2> ISimplePath.Points => this.innerPath.Points;
|
||||
ImmutableArray<Vector2> ISimplePath.Points => this.innerPath.Points();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Rectangle Bounds => this.innerPath.Bounds;
|
||||
|
@ -68,7 +68,7 @@ namespace SixLabors.Shapes
|
|||
/// <summary>
|
||||
/// Gets the maximum number intersections that a shape can have when testing a line.
|
||||
/// </summary>
|
||||
public int MaxIntersections => this.innerPath.Points.Length;
|
||||
public int MaxIntersections => this.innerPath.PointCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line segments
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using SixLabors.Shapes;
|
||||
|
||||
namespace SixLabors.Shapes.Benchmarks
|
||||
{
|
||||
public class InteralPath_FindIntersections
|
||||
{
|
||||
[Benchmark]
|
||||
public Vector2[] InternalOld()
|
||||
{
|
||||
var vectors = new Ellipse(new System.Numerics.Vector2(0, 0), new Size(20, 10))
|
||||
.Flatten()
|
||||
.First().Points.ToArray();
|
||||
|
||||
var path = new InternalPath_Old(vectors, true);
|
||||
|
||||
Vector2[] buffer = new Vector2[path.PointCount*2];
|
||||
for (var y = path.Bounds.Top; y < path.Bounds.Bottom; y += (1f / 32f))
|
||||
{
|
||||
path.FindIntersections(new Vector2(path.Bounds.Left - 1, y), new Vector2(path.Bounds.Right + 1, y), buffer, path.PointCount, 0);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public Vector2[] InternalNew()
|
||||
{
|
||||
var vectors = new Ellipse(new System.Numerics.Vector2(0, 0), new Size(20, 10))
|
||||
.Flatten()
|
||||
.First().Points.ToArray();
|
||||
|
||||
var path = new InternalPath(vectors, true);
|
||||
|
||||
Vector2[] buffer = new Vector2[path.PointCount*2];
|
||||
for (var y = path.Bounds.Top; y < path.Bounds.Bottom; y += (1f / 32f))
|
||||
{
|
||||
path.FindIntersections(new Vector2(path.Bounds.Left - 1, y), new Vector2(path.Bounds.Right + 1, y), buffer, path.PointCount, 0);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace SixLabors.Shapes.Benchmarks
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
new BenchmarkSwitcher(typeof(Program).GetTypeInfo().Assembly).Run(args);
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.10.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\SixLabors.Shapes\SixLabors.Shapes.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -18,10 +18,10 @@ namespace SixLabors.Shapes.Tests
|
|||
|
||||
var path = new InternalPath(new ILineSegment[] { seg1, seg2 }, true);
|
||||
|
||||
Assert.Equal(new Vector2(0, 0), path.Points[0]);
|
||||
Assert.Equal(new Vector2(2, 2), path.Points[1]);
|
||||
Assert.Equal(new Vector2(4, 4), path.Points[2]);
|
||||
Assert.Equal(new Vector2(5, 5), path.Points[3]);
|
||||
Assert.Contains(new Vector2(0, 0), path.Points());
|
||||
Assert.DoesNotContain(new Vector2(2, 2), path.Points());
|
||||
Assert.DoesNotContain(new Vector2(4, 4), path.Points());
|
||||
Assert.Contains(new Vector2(5, 5), path.Points());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -157,7 +157,7 @@ namespace SixLabors.Shapes.Tests
|
|||
public void Intersections_buffer()
|
||||
{
|
||||
var shape = Create(new Vector2(0, 0), new Size(10, 10));
|
||||
var buffer = new Vector2[shape.Points.Length];
|
||||
var buffer = new Vector2[shape.PointCount];
|
||||
var hits = shape.FindIntersections(new Vector2(5, -10), new Vector2(5, 20), buffer, 4, 0);
|
||||
|
||||
Assert.Equal(2, hits);
|
||||
|
|
|
@ -23,10 +23,10 @@ namespace SixLabors.Shapes
|
|||
/// <returns>The points along the line the intersect with the boundaries of the polygon.</returns>
|
||||
internal static IEnumerable<Vector2> FindIntersections(this InternalPath path, Vector2 start, Vector2 end)
|
||||
{
|
||||
var buffer = ArrayPool<Vector2>.Shared.Rent(path.Points.Length);
|
||||
var buffer = ArrayPool<Vector2>.Shared.Rent(path.PointCount);
|
||||
try
|
||||
{
|
||||
var hits = path.FindIntersections(start, end, buffer, path.Points.Length, 0);
|
||||
var hits = path.FindIntersections(start, end, buffer, path.PointCount, 0);
|
||||
for (var i = 0; i < hits; i++)
|
||||
{
|
||||
yield return buffer[i];
|
||||
|
|
Загрузка…
Ссылка в новой задаче