import Foundation import CoreGraphics /// Places district labels so their boxes don't overlap. Each district starts at /// a hash-derived seed; if its (padded) label box collides with an already-placed /// one, we spiral outward — rotating and pushing away from downtown — until we /// find clear space. Existing placements never move, so the map stays stable as /// new districts appear. /// /// Pure and deterministic given its inputs; no SpriteKit, fully unit-testable. public enum LayoutSolver { /// - Parameters: /// - baseAngle/baseRadius: the hash-derived seed in polar coords. /// - localRect: the label box *relative to the node center* (labels sit /// below the dot, so this is usually offset downward). /// - existing: already-placed label boxes in world space. /// - pad: minimum gap to keep between boxes. /// - Returns: a world-space center for the new district. public static func placeNonOverlapping( baseAngle: Double, baseRadius: Double, localRect: CGRect, existing: [CGRect], pad: CGFloat = 16, maxAttempts: Int = 400 ) -> CGPoint { for attempt in 0..