ImageSharp/PolygonClipper/Polygon.cs
2026-08-03 22:31:27 +02:00

193 lines
5.9 KiB
C#

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace SixLabors.PolygonClipper {
/// <summary>
/// Represents a complex polygon.
/// </summary>
#pragma warning disable CA1710 // Identifiers should have correct suffix
public sealed class Polygon : IReadOnlyCollection<Contour>
#pragma warning restore CA1710 // Identifiers should have correct suffix
{
/// <summary>
/// The collection of contours that make up the polygon.
/// </summary>
private readonly List<Contour> contours;
/// <summary>
/// Initializes a new instance of the <see cref="Polygon"/> class.
/// </summary>
public Polygon()
=> this.contours = [];
/// <summary>
/// Initializes a new instance of the <see cref="Polygon"/> class with a contour capacity.
/// </summary>
/// <param name="capacity">The initial contour capacity.</param>
public Polygon(int capacity)
=> this.contours = new List<Contour>(capacity);
/// <summary>
/// Gets the number of contours in the polygon.
/// </summary>
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.contours.Count;
}
/// <summary>
/// Gets the total number of vertices across all contours in the polygon.
/// </summary>
/// <returns>The total vertex count.</returns>
public int VertexCount
{
get
{
int count = 0;
for (int i = 0; i < this.contours.Count; i++)
{
count += this.contours[i].Count;
}
return count;
}
}
/// <summary>
/// Gets the contour at the specified index.
/// </summary>
/// <param name="index">The index of the contour.</param>
/// <returns>The <see cref="Contour"/> at the given index.</returns>
public Contour this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.contours[index];
}
/// <summary>
/// Joins another polygon to this instance.
/// </summary>
/// <param name="polygon">The polygon to join.</param>
public void Join(Polygon polygon)
{
int size = this.Count;
for (int i = 0; i < polygon.contours.Count; ++i)
{
Contour contour = polygon.contours[i];
this.Add(contour);
this.GetLastContour().ClearHoles();
for (int j = 0; j < contour.HoleCount; ++j)
{
this.GetLastContour().AddHoleIndex(contour.GetHoleIndex(j) + size);
}
}
}
/// <summary>
/// Gets the bounding box.
/// </summary>
/// <returns>The <see cref="Box2"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Box2 GetBoundingBox()
{
if (this.Count == 0)
{
return default;
}
Box2 b = this.contours[0].GetBoundingBox();
for (int i = 1; i < this.Count; i++)
{
b = b.Add(this.contours[i].GetBoundingBox());
}
return b;
}
/// <summary>
/// Translates the polygon by the specified x and y values.
/// </summary>
/// <param name="x">The x-coordinate offset.</param>
/// <param name="y">The y-coordinate offset.</param>
public void Translate(double x, double y)
{
for (int i = 0; i < this.contours.Count; i++)
{
this.contours[i].Translate(x, y);
}
}
/// <summary>
/// Adds a contour to the end of the contour collection.
/// </summary>
/// <param name="contour">The contour to add.</param>
public void Add(Contour contour) => this.contours.Add(contour);
/// <summary>
/// Gets the last contour in the polygon.
/// </summary>
/// <returns>The last <see cref="Contour"/> in the collection.</returns>
public Contour GetLastContour() => this.contours[^1];
/// <summary>
/// Clears all contours from the polygon.
/// </summary>
public void Clear() => this.contours.Clear();
/// <summary>
/// Creates a deep copy of this polygon and all of its contours.
/// </summary>
/// <returns>A detached polygon copy.</returns>
public Polygon DeepClone()
{
Polygon clone = new(this.contours.Count);
for (int i = 0; i < this.contours.Count; i++)
{
clone.contours.Add(this.contours[i].DeepClone());
}
return clone;
}
/// <inheritdoc/>
public IEnumerator<Contour> GetEnumerator()
=> ((IEnumerable<Contour>)this.contours).GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
=> ((IEnumerable)this.contours).GetEnumerator();
/// <summary>
/// Creates a string useful for debugging.
/// </summary>
/// <returns>The <see cref="string"/>.</returns>
public string ToDebugString()
{
StringBuilder stringBuilder = new();
stringBuilder.AppendLine("[");
foreach (Contour contour in this.contours)
{
stringBuilder.AppendLine(" [");
foreach (Vertex vertex in contour)
{
stringBuilder.AppendLine(" new Vertex(" + vertex.X + ", " + vertex.Y + "),");
}
stringBuilder.AppendLine(" ],");
}
stringBuilder.AppendLine("];");
return stringBuilder.ToString();
}
}
}