accepted

PassSonar wedge radius scales √-proportional to count, not linearly

Each wedge's outer radius is `√(count / maxCount)`, not `count / maxCount`. A bin with four times the passes has twice the radius — matching how human vision reads wedge area. Aligns with `mplsoccer` and Eliot McKinley's canonical reference.

PassSonar visual-encodingalgorithmfootball-semantics

Context

Three candidates for wedge-length encoding:

  • Linearr ∝ count. Tall bins overwhelm short bins visually.
  • √ (area-proportional)r ∝ √count. Area reads as proportional to count. Matches perceptual studies and the mplsoccer convention.
  • Logr ∝ log(count). Compresses further; good for highly skewed distributions but misreads in the sparse common case.

The same argument applies here as for ShotMap markers (see shotmap-marker-size-area-scaled): area-proportional scaling preserves the implicit “how many” read.

Decision

completedRadius = √(bucket.completed / resolvedMax) and attemptedRadius = √(bucket.attempted / resolvedMax). Under colorBy="distance" or "frequency", the same √-transform applies to the single-wedge radius. Log and linear are explicitly deferred — no radiusScaling prop in v0.3.

Consequences

  • Visual output matches McKinley’s canonical PassSonar and mplsoccer reference implementations; consumers switching from either library read the Campos rendering as expected.
  • High-volume directions dominate less; dispersed passing patterns remain legible at the same chart size.
  • Consumers wanting log or linear scaling override via the compute-layer maxCount prop to compress or expand the range, or request the feature as a follow-up decision if that becomes insufficient.
  • Legend radius samples use the same √-transform so readings stay consistent between legend and plot.
← All decisions