updateOverlay() tears down and recreates every overlay item (spots,
predictions, hkl labels, rings, ROIs). It was called directly from every
scroll, wheel, pan and resize event. A single pan step emits valueChanged
on both scrollbars (each -> onScroll -> updateOverlay) plus a direct call,
so one visual change triggered ~3 full overlay rebuilds.
Route the navigation hot paths through scheduleOverlayUpdate(), which sets
a dirty flag and defers the rebuild to the next event-loop pass via
QTimer::singleShot(0). Bursts within one handler collapse to a single
rebuild. The synchronous one-offs (image load, ROI edit, viewport-lock
apply, setZoom) keep calling updateOverlay() directly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>