// MIT License - Copyright (c) 2023 wallstop // Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE namespace WallstopStudios.UnityHelpers.Core.Extension { using System.Collections.Generic; using DataStructure; using DataStructure.Adapters; using UnityEngine; /// /// Provides extension methods for Circle data structure operations, particularly grid enumeration. /// /// /// Thread Safety: All methods are thread-safe as they operate on value types without shared state. /// Performance: EnumerateArea methods use bounding box optimization and avoid allocations where possible. /// public static class CircleExtensions { /// /// Enumerates all integer grid points within the circle's area using lazy evaluation. /// /// The circle whose area to enumerate. /// The z-coordinate to assign to all enumerated points (default: 0). /// /// A lazy-evaluated enumerable of FastVector3Int points where each point's (x,y) distance from circle center /// is less than or equal to circle radius, with the specified z coordinate. /// /// /// Null Handling: N/A - operates on value types. /// Thread Safety: Thread-safe - no shared state, operates on value type inputs. /// Performance: O(w*h) where w,h are bounding box dimensions. Uses lazy evaluation (yield return). /// Bounding box optimization reduces iterations from all grid points to only those in circle's AABB. /// Allocations: Minimal - uses yield return for lazy evaluation. Each returned FastVector3Int is a value type. /// No intermediate collections allocated. /// Edge Cases: Zero radius returns only center point (if center is on integer coordinates). /// Negative radius returns no points. Uses squared distance comparison to avoid sqrt. /// Points exactly on the boundary (distance == radius) are included. /// public static IEnumerable EnumerateArea(this Circle circle, int z = 0) { // Calculate integer bounds for the circle int radiusCeil = Mathf.CeilToInt(circle.radius); int minX = Mathf.FloorToInt(circle.center.x - circle.radius); int maxX = Mathf.CeilToInt(circle.center.x + circle.radius); int minY = Mathf.FloorToInt(circle.center.y - circle.radius); int maxY = Mathf.CeilToInt(circle.center.y + circle.radius); // Pre-cache radiusSquared for Contains check float radiusSquared = circle.radius * circle.radius; Vector2 center = circle.center; for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { // Calculate squared distance without allocating Vector2 float dx = x - center.x; float dy = y - center.y; float distanceSquared = dx * dx + dy * dy; if (distanceSquared <= radiusSquared) { yield return new FastVector3Int(x, y, z); } } } } /// /// Enumerates all integer grid points within the circle's area into a pre-existing buffer list. /// This is a non-lazy, eager evaluation version that populates a collection immediately. /// /// The circle whose area to enumerate. /// The list to populate with points. Will be cleared before populating. /// The z-coordinate to assign to all enumerated points (default: 0). /// The same buffer list passed in, now containing all points within the circle. /// /// Null Handling: Will throw NullReferenceException if buffer is null. /// Thread Safety: Thread-safe if buffer is not accessed by other threads during execution. /// Performance: O(w*h) where w,h are bounding box dimensions. Eagerly evaluates all points. /// Bounding box optimization reduces iterations. Same algorithm as lazy version but immediate execution. /// Allocations: No allocations except potential buffer growth if capacity is insufficient. /// Buffer is cleared first, then populated. Consider pre-sizing buffer if point count is known. /// Edge Cases: Zero radius adds only center point (if on integer coordinates). /// Negative radius clears buffer and returns empty. Uses squared distance to avoid sqrt. /// Points exactly on boundary (distance == radius) are included. /// Buffer capacity may grow if circle contains more points than current capacity. /// public static List EnumerateArea( this Circle circle, List buffer, int z = 0 ) { buffer.Clear(); // Calculate integer bounds for the circle int radiusCeil = Mathf.CeilToInt(circle.radius); int minX = Mathf.FloorToInt(circle.center.x - circle.radius); int maxX = Mathf.CeilToInt(circle.center.x + circle.radius); int minY = Mathf.FloorToInt(circle.center.y - circle.radius); int maxY = Mathf.CeilToInt(circle.center.y + circle.radius); // Pre-cache radiusSquared for Contains check float radiusSquared = circle.radius * circle.radius; Vector2 center = circle.center; for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { // Calculate squared distance without allocating Vector2 float dx = x - center.x; float dy = y - center.y; float distanceSquared = dx * dx + dy * dy; if (distanceSquared <= radiusSquared) { buffer.Add(new FastVector3Int(x, y, z)); } } } return buffer; } } }