// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.PolygonClipper;
using System;
using PCPolygon = SixLabors.PolygonClipper.Polygon;
using StrokeOptions = SixLabors.ImageSharp.Drawing.Processing.StrokeOptions;
namespace SixLabors.ImageSharp.Drawing.PolygonGeometry {
///
/// Generates stroked and merged shapes using polygon stroking and boolean clipping.
///
internal static class StrokedShapeGenerator
{
///
/// Strokes a path and returns a merged outline from its flattened segments.
///
/// The source path. It is flattened using the current flattening settings.
/// The stroke width in the caller's coordinate space.
/// The stroke geometry options.
///
/// A representing the stroked outline after boolean merge.
///
public static ComplexPolygon GenerateStrokedShapes(IPath path, float width, StrokeOptions options)
{
// 1) Stroke the input path as open or closed.
PCPolygon rings = [];
foreach (ISimplePath sp in path.Flatten())
{
ReadOnlySpan span = sp.Points.Span;
if (span.Length < 2)
{
continue;
}
Contour ring = new(span.Length);
for (int i = 0; i < span.Length; i++)
{
PointF p = span[i];
ring.Add(new Vertex(p.X, p.Y));
}
if (sp.IsClosed)
{
ring.Add(ring[0]);
}
rings.Add(ring);
}
int count = rings.Count;
if (count == 0)
{
return new([]);
}
PCPolygon result = PolygonStroker.Stroke(rings, width, CreateStrokeOptions(options));
IPath[] shapes = new IPath[result.Count];
int index = 0;
for (int i = 0; i < result.Count; i++)
{
Contour contour = result[i];
PointF[] points = new PointF[contour.Count];
for (int j = 0; j < contour.Count; j++)
{
Vertex vertex = contour[j];
points[j] = new PointF((float)vertex.X, (float)vertex.Y);
}
shapes[index++] = new Polygon(points);
}
return new(shapes);
}
private static PolygonClipper.StrokeOptions CreateStrokeOptions(StrokeOptions options)
{
PolygonClipper.StrokeOptions o = new()
{
ArcDetailScale = options.ArcDetailScale,
MiterLimit = options.MiterLimit,
LineJoin = options.LineJoin switch
{
LineJoin.MiterRound => PolygonClipper.LineJoin.MiterRound,
LineJoin.Bevel => PolygonClipper.LineJoin.Bevel,
LineJoin.Round => PolygonClipper.LineJoin.Round,
LineJoin.MiterRevert => PolygonClipper.LineJoin.MiterRevert,
_ => PolygonClipper.LineJoin.Miter,
},
LineCap = options.LineCap switch
{
LineCap.Round => PolygonClipper.LineCap.Round,
LineCap.Square => PolygonClipper.LineCap.Square,
_ => PolygonClipper.LineCap.Butt,
}
};
return o;
}
}
}