// 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(); } } }