Skip to content

Commit f63789e

Browse files
Fix remaining issues and cleanup
1 parent a0ad69c commit f63789e

16 files changed

Lines changed: 209 additions & 202 deletions

src/ImageSharp.Drawing/Shapes/ClipPathExtensions.cs

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,74 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System.Collections.Generic;
5-
using SixLabors.ImageSharp.Drawing.PolygonClipper;
5+
using SixLabors.ImageSharp.Drawing.Shapes.PolygonClipper;
66

77
namespace SixLabors.ImageSharp.Drawing
88
{
99
/// <summary>
10-
/// Path extensions to clip paths.
10+
/// Provides extension methods to <see cref="IPath"/> that allow the clipping of shapes.
1111
/// </summary>
1212
public static class ClipPathExtensions
1313
{
1414
/// <summary>
15-
/// Clips the specified holes.
15+
/// Clips the specified subject path with the provided clipping paths.
1616
/// </summary>
17-
/// <param name="shape">The shape.</param>
18-
/// <param name="holes">The holes.</param>
19-
/// <returns>Returns a new shape with the holes clipped out of the shape.</returns>
20-
/// <exception cref="ClipperException">Open paths have been disabled.</exception>
21-
public static IPath Clip(this IPath shape, IEnumerable<IPath> holes)
17+
/// <param name="subjectPath">The subject path.</param>
18+
/// <param name="clipPaths">The clipping paths.</param>
19+
/// <returns>The clipped <see cref="IPath"/>.</returns>
20+
/// <exception cref="ClipperException">Thrown when an error occured while attempting to clip the polygon.</exception>
21+
public static IPath Clip(this IPath subjectPath, params IPath[] clipPaths)
22+
=> subjectPath.Clip((IEnumerable<IPath>)clipPaths);
23+
24+
/// <summary>
25+
/// Clips the specified subject path with the provided clipping paths.
26+
/// </summary>
27+
/// <param name="subjectPath">The subject path.</param>
28+
/// <param name="operation">The clipping operation.</param>
29+
/// <param name="rule">The intersection rule.</param>
30+
/// <param name="clipPaths">The clipping paths.</param>
31+
/// <returns>The clipped <see cref="IPath"/>.</returns>
32+
/// <exception cref="ClipperException">Thrown when an error occured while attempting to clip the polygon.</exception>
33+
public static IPath Clip(
34+
this IPath subjectPath,
35+
ClippingOperation operation,
36+
IntersectionRule rule,
37+
params IPath[] clipPaths)
38+
=> subjectPath.Clip(operation, rule, (IEnumerable<IPath>)clipPaths);
39+
40+
/// <summary>
41+
/// Clips the specified subject path with the provided clipping paths.
42+
/// </summary>
43+
/// <param name="subjectPath">The subject path.</param>
44+
/// <param name="clipPaths">The clipping paths.</param>
45+
/// <returns>The clipped <see cref="IPath"/>.</returns>
46+
/// <exception cref="ClipperException">Thrown when an error occured while attempting to clip the polygon.</exception>
47+
public static IPath Clip(this IPath subjectPath, IEnumerable<IPath> clipPaths)
48+
=> subjectPath.Clip(ClippingOperation.Difference, IntersectionRule.EvenOdd, clipPaths);
49+
50+
/// <summary>
51+
/// Clips the specified subject path with the provided clipping paths.
52+
/// </summary>
53+
/// <param name="subjectPath">The subject path.</param>
54+
/// <param name="operation">The clipping operation.</param>
55+
/// <param name="rule">The intersection rule.</param>
56+
/// <param name="clipPaths">The clipping paths.</param>
57+
/// <returns>The clipped <see cref="IPath"/>.</returns>
58+
/// <exception cref="ClipperException">Thrown when an error occured while attempting to clip the polygon.</exception>
59+
public static IPath Clip(
60+
this IPath subjectPath,
61+
ClippingOperation operation,
62+
IntersectionRule rule,
63+
IEnumerable<IPath> clipPaths)
2264
{
23-
var clipper = new Clipper();
65+
Clipper clipper = new();
2466

25-
clipper.AddPath(shape, ClippingType.Subject);
26-
clipper.AddPaths(holes, ClippingType.Clip);
67+
clipper.AddPath(subjectPath, ClippingType.Subject);
68+
clipper.AddPaths(clipPaths, ClippingType.Clip);
2769

28-
IPath[] result = clipper.GenerateClippedShapes();
70+
IPath[] result = clipper.GenerateClippedShapes(operation, rule);
2971

3072
return new ComplexPolygon(result);
3173
}
32-
33-
/// <summary>
34-
/// Clips the specified holes.
35-
/// </summary>
36-
/// <param name="shape">The shape.</param>
37-
/// <param name="holes">The holes.</param>
38-
/// <returns>Returns a new shape with the holes clipped out of the shape.</returns>
39-
/// <exception cref="ClipperException">Open paths have been disabled.</exception>
40-
public static IPath Clip(this IPath shape, params IPath[] holes)
41-
=> shape.Clip((IEnumerable<IPath>)holes);
4274
}
4375
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
namespace SixLabors.ImageSharp.Drawing
5+
{
6+
/// <summary>
7+
/// Provides options for boolean clipping operations.
8+
/// </summary>
9+
/// <remarks>
10+
/// All clipping operations except for Difference are commutative.
11+
/// </remarks>
12+
public enum ClippingOperation
13+
{
14+
/// <summary>
15+
/// No clipping is performed.
16+
/// </summary>
17+
None,
18+
19+
/// <summary>
20+
/// Clips regions covered by both subject and clip polygons.
21+
/// </summary>
22+
Intersection,
23+
24+
/// <summary>
25+
/// Clips regions covered by subject or clip polygons, or both polygons.
26+
/// </summary>
27+
Union,
28+
29+
/// <summary>
30+
/// Clips regions covered by subject, but not clip polygons.
31+
/// </summary>
32+
Difference,
33+
34+
/// <summary>
35+
/// Clips regions covered by subject or clip polygons, but not both.
36+
/// </summary>
37+
Xor
38+
}
39+
}

src/ImageSharp.Drawing/Shapes/IntersectionRule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace SixLabors.ImageSharp.Drawing
55
{
66
/// <summary>
7-
/// Rule for calulating intersection points.
7+
/// Provides options for calculating intersection points.
88
/// </summary>
99
public enum IntersectionRule
1010
{

src/ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Numerics;
7-
using SixLabors.ImageSharp.Drawing.PolygonClipper;
7+
using SixLabors.ImageSharp.Drawing.Shapes.PolygonClipper;
88

99
namespace SixLabors.ImageSharp.Drawing
1010
{
@@ -24,7 +24,8 @@ public static class OutlinePathExtensions
2424
/// <param name="width">The outline width.</param>
2525
/// <returns>A new <see cref="IPath"/> representing the outline.</returns>
2626
/// <exception cref="ClipperException">Thrown when an offset cannot be calculated.</exception>
27-
public static IPath GenerateOutline(this IPath path, float width) => GenerateOutline(path, width, DefaultJointStyle, DefaultEndCapStyle);
27+
public static IPath GenerateOutline(this IPath path, float width)
28+
=> GenerateOutline(path, width, DefaultJointStyle, DefaultEndCapStyle);
2829

2930
/// <summary>
3031
/// Generates an outline of the path.
@@ -183,11 +184,7 @@ public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<f
183184
offset.AddPath(new ReadOnlySpan<PointF>(buffer.ToArray()), jointStyle, endCapStyle);
184185
}
185186

186-
online = !online;
187-
188187
buffer.Clear();
189-
patternPos = (patternPos + 1) % pattern.Length;
190-
targetLength = pattern[patternPos] * width;
191188
}
192189
}
193190

src/ImageSharp.Drawing/Shapes/PolygonClipper/ClippablePath.cs

Lines changed: 0 additions & 35 deletions
This file was deleted.

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

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Numerics;
7-
using SixLabors.ImageSharp.Drawing.Shapes.PolygonClipper;
87

9-
namespace SixLabors.ImageSharp.Drawing.PolygonClipper
8+
namespace SixLabors.ImageSharp.Drawing.Shapes.PolygonClipper
109
{
1110
/// <summary>
1211
/// Library to clip polygons.
@@ -15,90 +14,65 @@ internal class Clipper
1514
{
1615
// To make the floating point polygons compatable with clipper we have to scale them.
1716
private const float ScalingFactor = 1000F;
18-
private readonly Shapes.PolygonClipper.PolygonClipper polygonClipper;
17+
private readonly PolygonClipper polygonClipper;
1918

2019
/// <summary>
2120
/// Initializes a new instance of the <see cref="Clipper"/> class.
2221
/// </summary>
2322
public Clipper()
24-
=> this.polygonClipper = new Shapes.PolygonClipper.PolygonClipper();
23+
=> this.polygonClipper = new PolygonClipper();
2524

2625
/// <summary>
27-
/// Initializes a new instance of the <see cref="Clipper" /> class.
26+
/// Generates the clipped shapes from the previously provided paths.
2827
/// </summary>
29-
/// <param name="shapes">The shapes.</param>
30-
public Clipper(params ClippablePath[] shapes)
31-
: this() => this.AddPaths(shapes);
32-
33-
/// <summary>
34-
/// Executes the specified clip type.
35-
/// </summary>
36-
/// <returns>
37-
/// Returns the <see cref="IPath" /> array containing the converted polygons.
38-
/// </returns>
39-
/// <exception cref="ClipperException">GenerateClippedShapes: Open paths have been disabled.</exception>
40-
public IPath[] GenerateClippedShapes()
28+
/// <param name="operation">The clipping operation.</param>
29+
/// <param name="rule">The intersection rule.</param>
30+
/// <returns>The <see cref="T:IPath[]"/>.</returns>
31+
public IPath[] GenerateClippedShapes(ClippingOperation operation, IntersectionRule rule)
4132
{
4233
PathsF closedPaths = new();
4334
PathsF openPaths = new();
4435

45-
this.polygonClipper.Execute(ClipType.Difference, FillRule.EvenOdd, closedPaths, openPaths);
36+
FillRule fillRule = rule == IntersectionRule.EvenOdd ? FillRule.EvenOdd : FillRule.NonZero;
37+
this.polygonClipper.Execute(operation, fillRule, closedPaths, openPaths);
4638

4739
var shapes = new IPath[closedPaths.Count + openPaths.Count];
48-
const float scale = 1F / ScalingFactor;
4940

41+
int index = 0;
5042
for (int i = 0; i < closedPaths.Count; i++)
5143
{
52-
var points = new PointF[closedPaths[i].Count];
44+
PathF path = closedPaths[i];
45+
var points = new PointF[path.Count];
5346

54-
for (int j = 0; j < closedPaths[i].Count; j++)
47+
for (int j = 0; j < path.Count; j++)
5548
{
56-
Vector2 v = closedPaths[i][j];
57-
points[j] = v * scale;
49+
points[j] = path[j] / ScalingFactor;
5850
}
5951

60-
shapes[i] = new Polygon(new LinearLineSegment(points));
52+
shapes[index++] = new Polygon(new LinearLineSegment(points));
6153
}
6254

6355
for (int i = 0; i < openPaths.Count; i++)
6456
{
65-
var points = new PointF[closedPaths[i].Count];
57+
PathF path = openPaths[i];
58+
var points = new PointF[path.Count];
6659

67-
for (int j = 0; j < closedPaths[i].Count; j++)
60+
for (int j = 0; j < path.Count; j++)
6861
{
69-
Vector2 v = closedPaths[i][j];
70-
points[j] = v * scale;
62+
points[j] = path[j] / ScalingFactor;
7163
}
7264

73-
shapes[i] = new Path(new LinearLineSegment(points));
65+
shapes[index++] = new Polygon(new LinearLineSegment(points));
7466
}
7567

7668
return shapes;
7769
}
7870

79-
/// <summary>
80-
/// Adds the paths.
81-
/// </summary>
82-
/// <param name="paths">The paths.</param>
83-
/// <exception cref="ClipperException">Open paths have been disabled.</exception>
84-
public void AddPaths(ClippablePath[] paths)
85-
{
86-
Guard.NotNull(paths, nameof(paths));
87-
88-
for (int i = 0; i < paths.Length; i++)
89-
{
90-
ref ClippablePath p = ref paths[i];
91-
92-
this.AddPath(p.Path, p.Type);
93-
}
94-
}
95-
9671
/// <summary>
9772
/// Adds the shapes.
9873
/// </summary>
9974
/// <param name="paths">The paths.</param>
10075
/// <param name="clippingType">The clipping type.</param>
101-
/// <exception cref="ClipperException">Open paths have been disabled.</exception>
10276
public void AddPaths(IEnumerable<IPath> paths, ClippingType clippingType)
10377
{
10478
Guard.NotNull(paths, nameof(paths));
@@ -114,7 +88,6 @@ public void AddPaths(IEnumerable<IPath> paths, ClippingType clippingType)
11488
/// </summary>
11589
/// <param name="path">The path.</param>
11690
/// <param name="clippingType">The clipping type.</param>
117-
/// <exception cref="ClipperException">Open paths have been disabled.</exception>
11891
public void AddPath(IPath path, ClippingType clippingType)
11992
{
12093
Guard.NotNull(path, nameof(path));
@@ -130,7 +103,6 @@ public void AddPath(IPath path, ClippingType clippingType)
130103
/// </summary>
131104
/// <param name="path">The path.</param>
132105
/// <param name="clippingType">Type of the poly.</param>
133-
/// <exception cref="ClipperException">Open paths have been disabled.</exception>
134106
internal void AddPath(ISimplePath path, ClippingType clippingType)
135107
{
136108
ReadOnlySpan<PointF> vectors = path.Points.Span;

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@
33

44
using System;
55

6-
namespace SixLabors.ImageSharp.Drawing.PolygonClipper
6+
namespace SixLabors.ImageSharp.Drawing.Shapes.PolygonClipper
77
{
88
/// <summary>
99
/// The exception that is thrown when an error occurs clipping a polygon.
1010
/// </summary>
1111
public class ClipperException : Exception
1212
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="ClipperException"/> class.
15+
/// </summary>
16+
public ClipperException()
17+
{
18+
}
19+
1320
/// <summary>
1421
/// Initializes a new instance of the <see cref="ClipperException"/> class.
1522
/// </summary>
@@ -18,5 +25,16 @@ public ClipperException(string message)
1825
: base(message)
1926
{
2027
}
28+
29+
/// <summary>
30+
/// Initializes a new instance of the <see cref="ClipperException" /> class with a specified error message and a
31+
/// reference to the inner exception that is the cause of this exception.</summary>
32+
/// <param name="message">The error message that explains the reason for the exception. </param>
33+
/// <param name="innerException">The exception that is the cause of the current exception, or a <see langword="null"/>
34+
/// reference if no inner exception is specified. </param>
35+
public ClipperException(string message, Exception innerException)
36+
: base(message, innerException)
37+
{
38+
}
2139
}
2240
}

0 commit comments

Comments
 (0)