Skip to content

Commit 0b42626

Browse files
Migrate intersection code to internal API
1 parent a1b55d3 commit 0b42626

34 files changed

Lines changed: 489 additions & 430 deletions

src/ImageSharp.Drawing/Processing/PathGradientBrush.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public Edge(Path path, Color startColor, Color endColor)
166166
Span<PointF> intersections,
167167
Span<PointOrientation> orientations)
168168
{
169-
int pathIntersections = this.Path.FindIntersections(
169+
int pathIntersections = ((IPathInternals)this.Path).FindIntersections(
170170
start,
171171
end,
172172
intersections.Slice(0, this.Path.MaxIntersections),

src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs

Lines changed: 37 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
namespace SixLabors.ImageSharp.Drawing
1212
{
1313
/// <summary>
14-
/// Represents a complex polygon made up of one or more shapes overlayed on each other, where overlaps causes holes.
14+
/// Represents a complex polygon made up of one or more shapes overlayed on each other,
15+
/// where overlaps causes holes.
1516
/// </summary>
1617
/// <seealso cref="IPath" />
17-
public sealed class ComplexPolygon : IPath, IInternalPathOwner
18+
public sealed class ComplexPolygon : IPath, IPathInternals, IInternalPathOwner
1819
{
1920
private readonly IPath[] paths;
20-
private List<InternalPath> internalPaths = null;
21+
private readonly List<InternalPath> internalPaths;
22+
private readonly int maxIntersections;
2123

2224
/// <summary>
2325
/// Initializes a new instance of the <see cref="ComplexPolygon" /> class.
@@ -34,7 +36,10 @@ public ComplexPolygon(IEnumerable<IPath> paths)
3436
/// <param name="paths">The paths.</param>
3537
public ComplexPolygon(params IPath[] paths)
3638
{
37-
this.paths = paths ?? throw new ArgumentNullException(nameof(paths));
39+
Guard.NotNull(paths, nameof(paths));
40+
41+
this.paths = paths;
42+
this.internalPaths = new List<InternalPath>(this.paths.Length);
3843

3944
if (paths.Length > 0)
4045
{
@@ -45,39 +50,45 @@ public ComplexPolygon(params IPath[] paths)
4550
float length = 0;
4651
int intersections = 0;
4752

48-
foreach (IPath s in this.paths)
53+
foreach (IPath p in this.paths)
4954
{
50-
length += s.Length;
51-
if (s.Bounds.Left < minX)
55+
length += p.Length;
56+
if (p.Bounds.Left < minX)
5257
{
53-
minX = s.Bounds.Left;
58+
minX = p.Bounds.Left;
5459
}
5560

56-
if (s.Bounds.Right > maxX)
61+
if (p.Bounds.Right > maxX)
5762
{
58-
maxX = s.Bounds.Right;
63+
maxX = p.Bounds.Right;
5964
}
6065

61-
if (s.Bounds.Top < minY)
66+
if (p.Bounds.Top < minY)
6267
{
63-
minY = s.Bounds.Top;
68+
minY = p.Bounds.Top;
6469
}
6570

66-
if (s.Bounds.Bottom > maxY)
71+
if (p.Bounds.Bottom > maxY)
6772
{
68-
maxY = s.Bounds.Bottom;
73+
maxY = p.Bounds.Bottom;
6974
}
7075

71-
intersections += s.MaxIntersections;
76+
foreach (ISimplePath s in p.Flatten())
77+
{
78+
var ip = new InternalPath(s.Points, s.IsClosed);
79+
intersections += ip.PointCount;
80+
81+
this.internalPaths.Add(ip);
82+
}
7283
}
7384

74-
this.MaxIntersections = intersections;
85+
this.maxIntersections = intersections;
7586
this.Length = length;
7687
this.Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY);
7788
}
7889
else
7990
{
80-
this.MaxIntersections = 0;
91+
this.maxIntersections = 0;
8192
this.Length = 0;
8293
this.Bounds = RectangleF.Empty;
8394
}
@@ -111,13 +122,8 @@ public ComplexPolygon(params IPath[] paths)
111122
/// </value>
112123
public RectangleF Bounds { get; }
113124

114-
/// <summary>
115-
/// Gets the maximum number intersections that a shape can have when testing a line.
116-
/// </summary>
117-
/// <value>
118-
/// The maximum intersections.
119-
/// </value>
120-
public int MaxIntersections { get; }
125+
/// <inheritdoc/>
126+
int IPathInternals.MaxIntersections => this.maxIntersections;
121127

122128
/// <summary>
123129
/// The distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds
@@ -163,19 +169,17 @@ public PointInfo Distance(PointF point)
163169
}
164170

165171
/// <inheritdoc />
166-
public int FindIntersections(PointF start, PointF end, Span<PointF> intersections, Span<PointOrientation> orientations)
167-
=> this.FindIntersections(start, end, intersections, orientations, IntersectionRule.OddEven);
172+
int IPathInternals.FindIntersections(PointF start, PointF end, Span<PointF> intersections, Span<PointOrientation> orientations)
173+
=> ((IPathInternals)this).FindIntersections(start, end, intersections, orientations, IntersectionRule.OddEven);
168174

169175
/// <inheritdoc />
170-
public int FindIntersections(
176+
int IPathInternals.FindIntersections(
171177
PointF start,
172178
PointF end,
173179
Span<PointF> intersections,
174180
Span<PointOrientation> orientations,
175181
IntersectionRule intersectionRule)
176182
{
177-
this.EnsureInternalPathsInitalized();
178-
179183
int totalAdded = 0;
180184
foreach (InternalPath ip in this.internalPaths)
181185
{
@@ -204,29 +208,6 @@ public int FindIntersections(
204208
return totalAdded;
205209
}
206210

207-
private void EnsureInternalPathsInitalized()
208-
{
209-
if (this.internalPaths == null)
210-
{
211-
lock (this.paths)
212-
{
213-
if (this.internalPaths == null)
214-
{
215-
this.internalPaths = new List<InternalPath>(this.paths.Length);
216-
217-
foreach (IPath p in this.paths)
218-
{
219-
foreach (ISimplePath s in p.Flatten())
220-
{
221-
var ip = new InternalPath(s.Points, s.IsClosed);
222-
this.internalPaths.Add(ip);
223-
}
224-
}
225-
}
226-
}
227-
}
228-
}
229-
230211
/// <summary>
231212
/// Determines whether the <see cref="IPath" /> contains the specified point
232213
/// </summary>
@@ -267,7 +248,7 @@ public IPath Transform(Matrix3x2 matrix)
267248
int i = 0;
268249
foreach (IPath s in this.Paths)
269250
{
270-
shapes[i++] = s.Transform(matrix);
251+
shapes[i++] = (IPath)s.Transform(matrix);
271252
}
272253

273254
return new ComplexPolygon(shapes);
@@ -307,7 +288,7 @@ public IPath AsClosedPath()
307288
var paths = new IPath[this.paths.Length];
308289
for (int i = 0; i < this.paths.Length; i++)
309290
{
310-
paths[i] = this.paths[i].AsClosedPath();
291+
paths[i] = (IPath)this.paths[i].AsClosedPath();
311292
}
312293

313294
return new ComplexPolygon(paths);
@@ -336,14 +317,12 @@ public SegmentInfo PointAlongPath(float distanceAlongPath)
336317
distanceAlongPath -= p.Length;
337318
}
338319

320+
// TODO: Perf. Throwhelper
339321
throw new InvalidOperationException("Should not be possible to reach this line");
340322
}
341323

342324
/// <inheritdoc/>
343325
IReadOnlyList<InternalPath> IInternalPathOwner.GetRingsAsInternalPath()
344-
{
345-
this.EnsureInternalPathsInitalized();
346-
return this.internalPaths;
347-
}
326+
=> this.internalPaths;
348327
}
349328
}

src/ImageSharp.Drawing/Shapes/EllipsePolygon.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Drawing
1010
/// <summary>
1111
/// An elliptical shape made up of a single path made up of one of more <see cref="ILineSegment"/>s.
1212
/// </summary>
13-
public class EllipsePolygon : IPath, ISimplePath, IInternalPathOwner
13+
public class EllipsePolygon : IPath, ISimplePath, IPathInternals, IInternalPathOwner
1414
{
1515
private readonly InternalPath innerPath;
1616
private readonly CubicBezierLineSegment segment;
@@ -85,7 +85,7 @@ private EllipsePolygon(CubicBezierLineSegment segment)
8585
/// <summary>
8686
/// Gets the maximum number intersections that a shape can have when testing a line.
8787
/// </summary>
88-
int IPath.MaxIntersections => this.innerPath.PointCount;
88+
int IPathInternals.MaxIntersections => this.innerPath.PointCount;
8989

9090
/// <inheritdoc />
9191
public float Length => this.innerPath.Length;
@@ -141,11 +141,11 @@ public IEnumerable<ISimplePath> Flatten()
141141
}
142142

143143
/// <inheritdoc/>
144-
int IPath.FindIntersections(PointF start, PointF end, Span<PointF> intersections, Span<PointOrientation> orientations)
144+
int IPathInternals.FindIntersections(PointF start, PointF end, Span<PointF> intersections, Span<PointOrientation> orientations)
145145
=> this.innerPath.FindIntersections(start, end, intersections, orientations, IntersectionRule.OddEven);
146146

147147
/// <inheritdoc/>
148-
int IPath.FindIntersections(
148+
int IPathInternals.FindIntersections(
149149
PointF start,
150150
PointF end,
151151
Span<PointF> intersections,
@@ -211,6 +211,7 @@ private static CubicBezierLineSegment CreateSegment(Vector2 location, SizeF size
211211
}
212212

213213
/// <inheritdoc/>
214-
IReadOnlyList<InternalPath> IInternalPathOwner.GetRingsAsInternalPath() => new[] { this.innerPath };
214+
IReadOnlyList<InternalPath> IInternalPathOwner.GetRingsAsInternalPath()
215+
=> new[] { this.innerPath };
215216
}
216217
}

src/ImageSharp.Drawing/Shapes/IInternalPathOwner.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@ namespace SixLabors.ImageSharp.Drawing
1212
internal interface IInternalPathOwner
1313
{
1414
/// <summary>
15-
/// Returns the rings as a list of <see cref="InternalPath"/>-s.
15+
/// Returns the rings as a readonly collection of <see cref="InternalPath"/> elements.
1616
/// </summary>
17-
/// <returns>The list</returns>
17+
/// <returns>The <see cref="IReadOnlyList{T}"/>.</returns>
1818
IReadOnlyList<InternalPath> GetRingsAsInternalPath();
19-
20-
// TODO: We may want to reconfigure StyleCop rules for internals to avoid unnecessary redundant trivial code comments like in this file.
2119
}
2220
}

src/ImageSharp.Drawing/Shapes/IPath.cs

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4-
using System;
54
using System.Collections.Generic;
65
using System.Numerics;
76

@@ -18,15 +17,10 @@ public interface IPath
1817
PathTypes PathType { get; }
1918

2019
/// <summary>
21-
/// Gets the bounds enclosing the path
20+
/// Gets the bounds enclosing the path.
2221
/// </summary>
2322
RectangleF Bounds { get; }
2423

25-
/// <summary>
26-
/// Gets the maximum number intersections that a shape can have when testing a line.
27-
/// </summary>
28-
int MaxIntersections { get; }
29-
3024
/// <summary>
3125
/// Gets the length of the path.
3226
/// </summary>
@@ -56,44 +50,6 @@ public interface IPath
5650
/// <returns>Returns the current <see cref="IPath" /> as simple linear path.</returns>
5751
IEnumerable<ISimplePath> Flatten();
5852

59-
/// <summary>
60-
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
61-
/// populate a buffer for all points on the polygon that the line intersects.
62-
/// </summary>
63-
/// <param name="start">The start position.</param>
64-
/// <param name="end">The end position.</param>
65-
/// <param name="intersections">The buffer for storing each intersection.</param>
66-
/// <param name="orientations">
67-
/// The buffer for storing the orientation of each intersection.
68-
/// Must be the same length as <paramref name="intersections"/>.
69-
/// </param>
70-
/// <returns>
71-
/// The number of intersections found.
72-
/// </returns>
73-
int FindIntersections(PointF start, PointF end, Span<PointF> intersections, Span<PointOrientation> orientations);
74-
75-
/// <summary>
76-
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
77-
/// populate a buffer for all points on the polygon that the line intersects.
78-
/// </summary>
79-
/// <param name="start">The start position.</param>
80-
/// <param name="end">The end position.</param>
81-
/// <param name="intersections">The buffer for storing each intersection.</param>
82-
/// <param name="orientations">
83-
/// The buffer for storing the orientation of each intersection.
84-
/// Must be the same length as <paramref name="intersections"/>.
85-
/// </param>
86-
/// <param name="intersectionRule">How intersections should be handled.</param>
87-
/// <returns>
88-
/// The number of intersections found.
89-
/// </returns>
90-
int FindIntersections(
91-
PointF start,
92-
PointF end,
93-
Span<PointF> intersections,
94-
Span<PointOrientation> orientations,
95-
IntersectionRule intersectionRule);
96-
9753
/// <summary>
9854
/// Determines whether the <see cref="IPath"/> contains the specified point
9955
/// </summary>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
6+
namespace SixLabors.ImageSharp.Drawing
7+
{
8+
/// <summary>
9+
/// An interface for internal operations we don't want to expose on <see cref="IPath"/>.
10+
/// </summary>
11+
internal interface IPathInternals : IPath
12+
{
13+
/// <summary>
14+
/// Gets the maximum number intersections that a shape can have when testing a line.
15+
/// </summary>
16+
int MaxIntersections { get; }
17+
18+
/// <summary>
19+
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
20+
/// populate a buffer for all points on the polygon that the line intersects.
21+
/// </summary>
22+
/// <param name="start">The start position.</param>
23+
/// <param name="end">The end position.</param>
24+
/// <param name="intersections">The buffer for storing each intersection.</param>
25+
/// <param name="orientations">
26+
/// The buffer for storing the orientation of each intersection.
27+
/// Must be the same length as <paramref name="intersections"/>.
28+
/// </param>
29+
/// <returns>
30+
/// The number of intersections found.
31+
/// </returns>
32+
int FindIntersections(PointF start, PointF end, Span<PointF> intersections, Span<PointOrientation> orientations);
33+
34+
/// <summary>
35+
/// Based on a line described by <paramref name="start"/> and <paramref name="end"/>
36+
/// populate a buffer for all points on the polygon that the line intersects.
37+
/// </summary>
38+
/// <param name="start">The start position.</param>
39+
/// <param name="end">The end position.</param>
40+
/// <param name="intersections">The buffer for storing each intersection.</param>
41+
/// <param name="orientations">
42+
/// The buffer for storing the orientation of each intersection.
43+
/// Must be the same length as <paramref name="intersections"/>.
44+
/// </param>
45+
/// <param name="intersectionRule">How intersections should be handled.</param>
46+
/// <returns>
47+
/// The number of intersections found.
48+
/// </returns>
49+
int FindIntersections(
50+
PointF start,
51+
PointF end,
52+
Span<PointF> intersections,
53+
Span<PointOrientation> orientations,
54+
IntersectionRule intersectionRule);
55+
}
56+
}

0 commit comments

Comments
 (0)