зеркало из https://github.com/SixLabors/Shapes.git
minimise calls to CalculateOrientation
This commit is contained in:
Родитель
4c9fdd76bc
Коммит
972b98dac7
|
@ -31,6 +31,9 @@ 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(Performance) = preSolution
|
||||
HasPerformanceSessions = true
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
|
|
|
@ -185,7 +185,7 @@ namespace SixLabors.Shapes.DrawShapesWithImageSharp
|
|||
img.Fill(Color.DarkBlue);
|
||||
|
||||
// In ImageSharp.Drawing.Paths there is an extension method that takes in an IShape directly.
|
||||
img.Fill(Color.HotPink, shape, new ImageSharp.Drawing.GraphicsOptions(true) { AntialiasSubpixelDepth = 0, Antialias = false });
|
||||
img.Fill(Color.HotPink, shape, new ImageSharp.Drawing.GraphicsOptions(true) { AntialiasSubpixelDepth = 0, Antialias = true });
|
||||
// img.Draw(Color.LawnGreen, 1, new ShapePath(shape));
|
||||
|
||||
// Ensure directory exists
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace SixLabors.Shapes
|
|||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// Internal logic for integrating linear paths.
|
||||
|
@ -199,6 +200,8 @@ namespace SixLabors.Shapes
|
|||
/// <returns>number of intersections hit</returns>
|
||||
public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset)
|
||||
{
|
||||
ClampPoints(ref start, ref end);
|
||||
|
||||
int polyCorners = this.points.Length;
|
||||
|
||||
if (!this.closedPath)
|
||||
|
@ -208,116 +211,181 @@ namespace SixLabors.Shapes
|
|||
|
||||
int position = 0;
|
||||
Vector2 lastPoint = MaxVector;
|
||||
if (this.closedPath)
|
||||
|
||||
PassPointData[] precaclulate = ArrayPool<PassPointData>.Shared.Rent(this.points.Length);
|
||||
|
||||
try
|
||||
{
|
||||
int prev = polyCorners - 1;
|
||||
float targetMinX = Math.Min(start.X, end.X);
|
||||
float targetMaxX = Math.Max(start.X, end.X);
|
||||
float targetMinY = Math.Min(start.Y, end.Y);
|
||||
float targetMaxY = Math.Max(start.Y, end.Y);
|
||||
|
||||
lastPoint = FindIntersection(this.points[prev].Point, this.points[0].Point, start, end);
|
||||
}
|
||||
int lastCorner = polyCorners - 1;
|
||||
Orientation prevOrientation = CalulateOrientation(start, end, this.points[lastCorner].Point);
|
||||
Orientation nextOrientation = CalulateOrientation(start, end, this.points[0].Point);
|
||||
Orientation nextPlus1Orientation = CalulateOrientation(start, end, this.points[1].Point);
|
||||
|
||||
int inc = 0;
|
||||
int lastCorner = polyCorners - 1;
|
||||
for (int i = 0; i < polyCorners && count > 0; i++)
|
||||
{
|
||||
int next = (i + 1) % this.points.Length;
|
||||
|
||||
if (closedPath && AreColliner(this.points[i].Point, this.points[next].Point, start, end))
|
||||
for (int i = 0; i < polyCorners && count > 0; i++)
|
||||
{
|
||||
// lines are colinear and intersect
|
||||
// if this is the case we need to tell if this is an inflection or not
|
||||
Orientation nextSide = Orientation.Colinear;
|
||||
// keep going next untill we are no longer on the line
|
||||
while (nextSide == Orientation.Colinear)
|
||||
int next = (i + 1) % this.points.Length;
|
||||
int nextPlus1 = (next + 1) % this.points.Length;
|
||||
Vector2 edgeStart = this.points[i].Point;
|
||||
Vector2 edgeEnd = this.points[next].Point;
|
||||
|
||||
var pointOrientation = nextOrientation;
|
||||
nextOrientation = nextPlus1Orientation;
|
||||
nextPlus1Orientation = CalulateOrientation(start, end, this.points[nextPlus1].Point);
|
||||
|
||||
bool removeLastIntersection = nextOrientation == Orientation.Colinear &&
|
||||
pointOrientation == Orientation.Colinear &&
|
||||
nextPlus1Orientation != prevOrientation &&
|
||||
(this.closedPath || i > 0) &&
|
||||
(IsOnSegment(start, edgeStart, end) || IsOnSegment(start, edgeEnd, end));
|
||||
|
||||
bool doIntersect = false;
|
||||
if (pointOrientation == Orientation.Colinear || pointOrientation != nextOrientation)
|
||||
{
|
||||
int nextPlus1 = (next + 1) % this.points.Length;
|
||||
nextSide = CalulateOrientation(this.points[nextPlus1].Point, this.points[i].Point, this.points[next].Point);
|
||||
if (nextSide == Orientation.Colinear)
|
||||
{
|
||||
//skip a point
|
||||
next = nextPlus1;
|
||||
if (nextPlus1 > next)
|
||||
{
|
||||
inc += nextPlus1 - next;
|
||||
}
|
||||
else
|
||||
{
|
||||
inc++;
|
||||
}
|
||||
}
|
||||
//intersect
|
||||
float edgeMinX = Math.Min(edgeStart.X, edgeEnd.X);
|
||||
float edgeMaxX = Math.Max(edgeStart.X, edgeEnd.X);
|
||||
float edgeMinY = Math.Min(edgeStart.Y, edgeEnd.Y);
|
||||
float edgeMaxY = Math.Max(edgeStart.Y, edgeEnd.Y);
|
||||
|
||||
doIntersect = edgeMinX - Epsilon <= targetMaxX &&
|
||||
edgeMaxX + Epsilon >= targetMinX &&
|
||||
edgeMinY - Epsilon <= targetMaxY &&
|
||||
edgeMaxY + Epsilon >= targetMinY;
|
||||
}
|
||||
|
||||
Orientation prevSide = CalulateOrientation(this.points[lastCorner].Point, this.points[i].Point, this.points[next].Point);
|
||||
if (prevSide != nextSide)
|
||||
precaclulate[i] = new PassPointData
|
||||
{
|
||||
RemoveLastIntersectionAndSkip = removeLastIntersection,
|
||||
RelativeOrientation = pointOrientation,
|
||||
DoIntersect = doIntersect // DoIntersect(edgeStart, edgeEnd, start, end)
|
||||
};
|
||||
lastCorner = i;
|
||||
prevOrientation = pointOrientation;
|
||||
}
|
||||
|
||||
if (this.closedPath)
|
||||
{
|
||||
int prev = polyCorners - 1;
|
||||
|
||||
if (precaclulate[prev].DoIntersect)
|
||||
{
|
||||
lastPoint = FindIntersection(this.points[prev].Point, this.points[0].Point, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < polyCorners && count > 0; i++)
|
||||
{
|
||||
int next = (i + 1) % this.points.Length;
|
||||
|
||||
if (precaclulate[i].RemoveLastIntersectionAndSkip)
|
||||
{
|
||||
position--;
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (precaclulate[i].DoIntersect)
|
||||
{
|
||||
Vector2 point = FindIntersection(this.points[i].Point, this.points[next].Point, start, end);
|
||||
if (point != MaxVector)
|
||||
{
|
||||
if (lastPoint.Equivelent(point, Epsilon2))
|
||||
{
|
||||
lastPoint = MaxVector;
|
||||
|
||||
Vector2 point = FindIntersection(this.points[i].Point, this.points[next].Point, start, end);
|
||||
if (point != MaxVector)
|
||||
{
|
||||
if (lastPoint.Equivelent(point, Epsilon2))
|
||||
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].Point.Equivelent(point, Epsilon))
|
||||
{
|
||||
next = i;
|
||||
}
|
||||
|
||||
if (this.points[last].Point.Equivelent(point, Epsilon))
|
||||
{
|
||||
last = i;
|
||||
}
|
||||
|
||||
Orientation side = precaclulate[next].RelativeOrientation;
|
||||
Orientation side2 = precaclulate[last].RelativeOrientation;
|
||||
|
||||
//if (side == Orientation.Colinear && side2 == Orientation.Colinear)
|
||||
//{
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
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].Point.Equivelent(point, Epsilon))
|
||||
{
|
||||
next = i;
|
||||
}
|
||||
|
||||
if (this.points[last].Point.Equivelent(point, Epsilon))
|
||||
{
|
||||
last = i;
|
||||
}
|
||||
|
||||
Orientation side = CalulateOrientation(this.points[last].Point, start, end);
|
||||
Orientation side2 = CalulateOrientation(this.points[next].Point, start, end);
|
||||
|
||||
if (side == Orientation.Colinear && side2 == Orientation.Colinear)
|
||||
{
|
||||
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--;
|
||||
lastCorner = i;
|
||||
}
|
||||
|
||||
lastPoint = point;
|
||||
lastCorner = i;
|
||||
return position;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<PassPointData>.Shared.Return(precaclulate);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
private void ClampPoints(ref Vector2 start, ref Vector2 end)
|
||||
{
|
||||
return CalulateOrientation(vector21, start, end) == Orientation.Colinear &&
|
||||
CalulateOrientation(vector22, start, end) == Orientation.Colinear &&
|
||||
DoIntersect(vector21, vector22, start, end);
|
||||
// clean up start and end points
|
||||
if (start.X == float.MaxValue)
|
||||
{
|
||||
start.X = this.Bounds.Right + 1;
|
||||
}
|
||||
if (start.X == float.MinValue)
|
||||
{
|
||||
start.X = this.Bounds.Left - 1;
|
||||
}
|
||||
if (end.X == float.MaxValue)
|
||||
{
|
||||
end.X = this.Bounds.Right + 1;
|
||||
}
|
||||
if (end.X == float.MinValue)
|
||||
{
|
||||
end.X = this.Bounds.Left - 1;
|
||||
}
|
||||
|
||||
if (start.Y == float.MaxValue)
|
||||
{
|
||||
start.Y = this.Bounds.Bottom + 1;
|
||||
}
|
||||
if (start.Y == float.MinValue)
|
||||
{
|
||||
start.Y = this.Bounds.Top - 1;
|
||||
}
|
||||
if (end.Y == float.MaxValue)
|
||||
{
|
||||
end.Y = this.Bounds.Bottom + 1;
|
||||
}
|
||||
if (end.Y == float.MinValue)
|
||||
{
|
||||
end.Y = this.Bounds.Top - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -365,49 +433,6 @@ namespace SixLabors.Shapes
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool DoIntersect(Vector2 p1, Vector2 q1, Vector2 p2, Vector2 q2)
|
||||
{
|
||||
// Find the four orientations needed for general and
|
||||
// special cases
|
||||
Orientation o1 = CalulateOrientation(p1, q1, p2);
|
||||
Orientation o2 = CalulateOrientation(p1, q1, q2);
|
||||
Orientation o3 = CalulateOrientation(p2, q2, p1);
|
||||
Orientation 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) &&
|
||||
|
@ -416,6 +441,7 @@ namespace SixLabors.Shapes
|
|||
(q.Y + Epsilon2) >= Math.Min(p.Y, r.Y);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Orientation CalulateOrientation(Vector2 p, Vector2 q, Vector2 r)
|
||||
{
|
||||
// See http://www.geeksforgeeks.org/orientation-3-ordered-points/
|
||||
|
@ -441,17 +467,16 @@ namespace SixLabors.Shapes
|
|||
/// <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;
|
||||
}
|
||||
//if (!DoIntersect(line1Start, line1End, line2Start, line2End))
|
||||
// {
|
||||
// return MaxVector;
|
||||
// }
|
||||
|
||||
float x1, y1, x2, y2, x3, y3, x4, y4;
|
||||
x1 = line1Start.X;
|
||||
|
@ -474,7 +499,14 @@ namespace SixLabors.Shapes
|
|||
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);
|
||||
var point = new Vector2(x, y);
|
||||
|
||||
if(IsOnSegment(line1Start, point, line1End) && IsOnSegment(line2Start, point, line2End))
|
||||
{
|
||||
return point;
|
||||
}
|
||||
|
||||
return MaxVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -680,5 +712,12 @@ namespace SixLabors.Shapes
|
|||
public float Length;
|
||||
public float TotalLength;
|
||||
}
|
||||
|
||||
private struct PassPointData
|
||||
{
|
||||
public bool RemoveLastIntersectionAndSkip;
|
||||
public Orientation RelativeOrientation;
|
||||
public bool DoIntersect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace SixLabors.Shapes.Benchmarks
|
|||
|
||||
public InteralPath_FindIntersections()
|
||||
{
|
||||
this.vectors = new Ellipse(new System.Numerics.Vector2(0, 0), new Size(20, 10))
|
||||
this.vectors = new Ellipse(new System.Numerics.Vector2(0, 0), new Size(2000, 1000))
|
||||
.Flatten()
|
||||
.First().Points.ToArray();
|
||||
}
|
||||
|
@ -23,14 +23,11 @@ namespace SixLabors.Shapes.Benchmarks
|
|||
public Vector2[] InternalOld()
|
||||
{
|
||||
Vector2[] buffer = new Vector2[vectors.Length];
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
var path = new InternalPath_Old(vectors, true);
|
||||
var path = new InternalPath_Old(vectors, true);
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -39,14 +36,11 @@ namespace SixLabors.Shapes.Benchmarks
|
|||
public Vector2[] InternalNew()
|
||||
{
|
||||
Vector2[] buffer = new Vector2[vectors.Length];
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
var path = new InternalPath(vectors, true);
|
||||
var path = new InternalPath(vectors, true);
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,13 @@ namespace SixLabors.Shapes.Benchmarks
|
|||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
//var p = new InteralPath_FindIntersections();
|
||||
//for (var i = 0; i < 10; i++)
|
||||
//{
|
||||
// p.InternalNew();
|
||||
// Console.WriteLine(i);
|
||||
//}
|
||||
|
||||
new BenchmarkSwitcher(typeof(Program).GetTypeInfo().Assembly).Run(args);
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче