// 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 {
///
/// Represents a complex polygon.
///
#pragma warning disable CA1710 // Identifiers should have correct suffix
public sealed class Polygon : IReadOnlyCollection
#pragma warning restore CA1710 // Identifiers should have correct suffix
{
///
/// The collection of contours that make up the polygon.
///
private readonly List contours;
///
/// Initializes a new instance of the class.
///
public Polygon()
=> this.contours = [];
///
/// Initializes a new instance of the class with a contour capacity.
///
/// The initial contour capacity.
public Polygon(int capacity)
=> this.contours = new List(capacity);
///
/// Gets the number of contours in the polygon.
///
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.contours.Count;
}
///
/// Gets the total number of vertices across all contours in the polygon.
///
/// The total vertex count.
public int VertexCount
{
get
{
int count = 0;
for (int i = 0; i < this.contours.Count; i++)
{
count += this.contours[i].Count;
}
return count;
}
}
///
/// Gets the contour at the specified index.
///
/// The index of the contour.
/// The at the given index.
public Contour this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.contours[index];
}
///
/// Joins another polygon to this instance.
///
/// The polygon to join.
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);
}
}
}
///
/// Gets the bounding box.
///
/// The .
[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;
}
///
/// Translates the polygon by the specified x and y values.
///
/// The x-coordinate offset.
/// The y-coordinate offset.
public void Translate(double x, double y)
{
for (int i = 0; i < this.contours.Count; i++)
{
this.contours[i].Translate(x, y);
}
}
///
/// Adds a contour to the end of the contour collection.
///
/// The contour to add.
public void Add(Contour contour) => this.contours.Add(contour);
///
/// Gets the last contour in the polygon.
///
/// The last in the collection.
public Contour GetLastContour() => this.contours[^1];
///
/// Clears all contours from the polygon.
///
public void Clear() => this.contours.Clear();
///
/// Creates a deep copy of this polygon and all of its contours.
///
/// A detached polygon copy.
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;
}
///
public IEnumerator GetEnumerator()
=> ((IEnumerable)this.contours).GetEnumerator();
///
IEnumerator IEnumerable.GetEnumerator()
=> ((IEnumerable)this.contours).GetEnumerator();
///
/// Creates a string useful for debugging.
///
/// The .
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();
}
}
}