Ensure the shared [min, max] covers every normalized
'percentage'/'indexed' series across the visible window. Recomputes
only when the visible window changed since the last call (so the N
primitives painted in one frame share a single O(points) scan). A no-op
for a window with no normalized series — the previous bounds are kept.
Force a recompute on the next ensure (call after mutating series).
Shared, self-scaling Y axis for every
'percentage'/'indexed'compare series on a chart. Compare lines only become comparable if they share one normalized scale — otherwise each would auto-fit independently and a flat series would look as volatile as a wild one. The controller owns one instance and hands it to every ComparePrimitive; the bounds are recomputed lazily, at most once per animation frame, the first time any primitive draws that frame.The scale is keyed on
(visible window, base, normalization-set)indirectly via a per-frame token the engine bumps — but since primitives don't get a frame token, we recompute whenever the viewport-derived visible window changes. To stay cheap and correct we just recompute on every freshbeginFrame()call (one per attached series per paint), guarded so only the first call in a paint batch does the O(points) scan.