Add idle district lifecycle: decay then reap
The map only ever gained density: hubs were sized by lifetime-cumulative bytes (so they never shrank) and were never removed. Now each hub has a decaying activity level — quiet districts visibly shrink toward a dormant dot — and after a configurable idle timeout (~40s, NC_REAP_TICKS) the hub is demolished: faded out, removed with its road, and its per-org state pruned in the controller. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,7 +39,7 @@ final class BuildingNode: SKNode {
|
||||
private let core: SKShapeNode
|
||||
private let title: SKLabelNode
|
||||
private let subtitle: SKLabelNode
|
||||
private var cumulative: Double = 0
|
||||
private var activity: Double = 0
|
||||
|
||||
private let spokeLayer = SKNode()
|
||||
private let endpointLayer = SKNode()
|
||||
@@ -106,13 +106,11 @@ final class BuildingNode: SKNode {
|
||||
|
||||
func note(activeHosts: [String]) { knownHosts.formUnion(activeHosts) }
|
||||
|
||||
/// React to a tick of traffic: grow toward a size set by total volume, and
|
||||
/// pulse so the eye catches the activity.
|
||||
/// React to a tick of traffic: bump the (decaying) activity level, grow
|
||||
/// toward a size set by it, and pulse so the eye catches the activity.
|
||||
func receive(bytes: Double) {
|
||||
cumulative += bytes
|
||||
let target = (0.30 + CGFloat(log10(cumulative + 1)) * 0.09).clamped(0.30, 1.05)
|
||||
glow.removeAction(forKey: "resize")
|
||||
glow.run(.scale(to: target, duration: 0.6), withKey: "resize")
|
||||
activity += bytes
|
||||
updateGlowSize()
|
||||
|
||||
core.removeAction(forKey: "pulse")
|
||||
core.run(.sequence([
|
||||
@@ -121,6 +119,23 @@ final class BuildingNode: SKNode {
|
||||
]), withKey: "pulse")
|
||||
}
|
||||
|
||||
/// Called every tick. Activity decays so quiet hubs visibly shrink toward
|
||||
/// dormant instead of staying bright forever.
|
||||
func decay(factor: Double = 0.55) {
|
||||
guard activity > 0 else { return }
|
||||
activity *= factor
|
||||
updateGlowSize()
|
||||
}
|
||||
|
||||
/// Ticks since this hub last carried traffic — used to reap dead districts.
|
||||
var idleTicks = 0
|
||||
|
||||
private func updateGlowSize() {
|
||||
let target = (0.20 + CGFloat(log10(activity + 1)) * 0.085).clamped(0.20, 1.05)
|
||||
glow.removeAction(forKey: "resize")
|
||||
glow.run(.scale(to: target, duration: 0.6), withKey: "resize")
|
||||
}
|
||||
|
||||
// MARK: - Expansion
|
||||
|
||||
/// The capped, stably-ordered list of endpoints we actually draw.
|
||||
|
||||
Reference in New Issue
Block a user