Skip to content

Commit 48b4236

Browse files
Update ClippedShapeGenerator.cs
1 parent 38e9d2d commit 48b4236

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

src/ImageSharp.Drawing/Shapes/PolygonGeometry/ClippedShapeGenerator.cs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,60 @@ public static ComplexPolygon GenerateClippedShapes(
5454
int index = 0;
5555
for (int i = 0; i < result.Count; i++)
5656
{
57-
Contour contour = result[i];
58-
PointF[] points = new PointF[contour.Count];
57+
shapes[index++] = new Polygon(CreateContourPoints(result, i));
58+
}
59+
60+
return new(shapes);
61+
}
5962

60-
for (int j = 0; j < contour.Count; j++)
63+
/// <summary>
64+
/// Converts a PolygonClipper contour to ImageSharp points and normalizes winding for parent/child rings.
65+
/// </summary>
66+
/// <param name="polygon">The polygon containing the contour hierarchy.</param>
67+
/// <param name="contourIndex">The contour index to convert.</param>
68+
/// <returns>The converted point array.</returns>
69+
private static PointF[] CreateContourPoints(PCPolygon polygon, int contourIndex)
70+
{
71+
Contour contour = polygon[contourIndex];
72+
PointF[] points = new PointF[contour.Count];
73+
bool reverse = ShouldReverseForNonZeroWinding(polygon, contourIndex);
74+
75+
if (!reverse)
76+
{
77+
for (int i = 0; i < contour.Count; i++)
6178
{
62-
Vertex vertex = contour[j];
63-
points[j] = new PointF((float)vertex.X, (float)vertex.Y);
79+
Vertex vertex = contour[i];
80+
points[i] = new PointF((float)vertex.X, (float)vertex.Y);
6481
}
6582

66-
shapes[index++] = new Polygon(points);
83+
return points;
6784
}
6885

69-
return new(shapes);
86+
for (int sourceIndex = contour.Count - 1, targetIndex = 0; sourceIndex >= 0; sourceIndex--, targetIndex++)
87+
{
88+
Vertex vertex = contour[sourceIndex];
89+
points[targetIndex] = new PointF((float)vertex.X, (float)vertex.Y);
90+
}
91+
92+
return points;
93+
}
94+
95+
/// <summary>
96+
/// Ensures child contours (holes/islands) use opposite winding to their direct parent.
97+
/// This keeps clipped output deterministic when consumed with the NonZero fill rule.
98+
/// </summary>
99+
/// <param name="polygon">The polygon containing contour hierarchy information.</param>
100+
/// <param name="contourIndex">The contour index to inspect.</param>
101+
/// <returns><see langword="true"/> when the contour should be reversed.</returns>
102+
private static bool ShouldReverseForNonZeroWinding(PCPolygon polygon, int contourIndex)
103+
{
104+
Contour contour = polygon[contourIndex];
105+
if (contour.ParentIndex is not int parentIndex || (uint)parentIndex >= (uint)polygon.Count)
106+
{
107+
return false;
108+
}
109+
110+
Contour parentContour = polygon[parentIndex];
111+
return contour.IsCounterClockwise() == parentContour.IsCounterClockwise();
70112
}
71113
}

0 commit comments

Comments
 (0)