Fix label overlap when districts glide to their latency distance
The latency reposition moved hubs to their RTT radius without checking label collisions, so a gliding district could land on top of a neighbour. Make both placement paths overlap-aware via the existing LayoutSolver: keep distance = latency (the meaningful axis) but rotate the arbitrary angle to clear other labels. Track each hub's target position so same-tick moves avoid where others are heading, not just where they are. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,7 @@ final class CityScene: SKScene {
|
||||
private var buildings: [String: BuildingNode] = [:]
|
||||
private var roads: [String: SKShapeNode] = [:]
|
||||
private var latencyRadius: [String: CGFloat] = [:]
|
||||
private var targetPos: [String: CGPoint] = [:] // where each hub is heading
|
||||
private var ringKey: String?
|
||||
private let roadLayer = SKNode()
|
||||
private let carLayer = SKNode()
|
||||
@@ -234,6 +235,7 @@ final class CityScene: SKScene {
|
||||
|
||||
let node = BuildingNode(title: title, subtitle: subtitle)
|
||||
node.position = placement(for: key, footprint: node.labelFootprint)
|
||||
targetPos[key] = node.position
|
||||
node.alpha = 0
|
||||
node.run(.fadeIn(withDuration: 0.6))
|
||||
buildingLayer.addChild(node)
|
||||
@@ -266,8 +268,19 @@ final class CityScene: SKScene {
|
||||
if let current = latencyRadius[key], abs(current - target) < 18 { return }
|
||||
latencyRadius[key] = target
|
||||
|
||||
// Keep distance = latency, but rotate/nudge the (arbitrary) angle to
|
||||
// clear other labels — otherwise gliding hubs land on top of each other.
|
||||
let angle = atan2(building.position.y, building.position.x)
|
||||
let dest = CGPoint(x: cos(angle) * Double(target), y: sin(angle) * Double(target))
|
||||
let others = buildings.compactMap { (k, b) -> CGRect? in
|
||||
guard k != key else { return nil }
|
||||
let p = targetPos[k] ?? b.position
|
||||
return b.labelFootprint.offsetBy(dx: p.x, dy: p.y)
|
||||
}
|
||||
let dest = LayoutSolver.placeNonOverlapping(
|
||||
baseAngle: angle, baseRadius: Double(target),
|
||||
localRect: building.labelFootprint, existing: others, pad: 18
|
||||
)
|
||||
targetPos[key] = dest
|
||||
building.run(.move(to: dest, duration: 0.8))
|
||||
|
||||
if let road = roads[key] {
|
||||
@@ -287,8 +300,9 @@ final class CityScene: SKScene {
|
||||
let ring = Double((h >> 16) % 1000) / 1000
|
||||
let baseRadius = 165 + Double(ring) * Double(worldRadius - 165)
|
||||
|
||||
let existing = buildings.values.map {
|
||||
$0.labelFootprint.offsetBy(dx: $0.position.x, dy: $0.position.y)
|
||||
let existing = buildings.map { (k, b) in
|
||||
let p = targetPos[k] ?? b.position
|
||||
return b.labelFootprint.offsetBy(dx: p.x, dy: p.y)
|
||||
}
|
||||
return LayoutSolver.placeNonOverlapping(
|
||||
baseAngle: baseAngle, baseRadius: baseRadius,
|
||||
|
||||
Reference in New Issue
Block a user