Reconcile the per-face cone animation timestamps with the calibrated
prop. starts maps each face to the performance.now() its grow+fade
animation began — null meaning "never calibrated, cone fully visible";
a timestamp at least CALIBRATED_ANIMATION_MS in the past meaning
"fade finished, cone hidden". Mutates starts in place (it lives in a ref
read by the R3F frame loop).
Rules:
On the FIRST set seen (prev === null), faces already calibrated have no
live pulse to fade them — mark their animation as already finished so
they hide silently instead of replaying the fade on every (re)open.
A face REMOVED from the set later (the connect sheet feeds an empty set
during a visual recalibration) gets its timestamp cleared so its cone
comes back full; the next live calibration pulse fades it again.
Faces ADDED after the first set are deliberately ignored: fades for live
calibration are driven by raw pulses in the frame loop, so a re-fired
event for an already-faded cone can't make it pop back.
Reconcile the per-face cone animation timestamps with the
calibratedprop.startsmaps each face to theperformance.now()its grow+fade animation began —nullmeaning "never calibrated, cone fully visible"; a timestamp at least CALIBRATED_ANIMATION_MS in the past meaning "fade finished, cone hidden". Mutatesstartsin place (it lives in a ref read by the R3F frame loop).Rules:
prev === null), faces already calibrated have no live pulse to fade them — mark their animation as already finished so they hide silently instead of replaying the fade on every (re)open.