// A four-second sweep for video capture, with no counters or on-page controls. const SWEEP_DURATION=4000,NOON=-Math.PI/2; const sweep={running:false,status:'ready',progress:0,angle:NOON,frame:0,startedAt:0}; let interactionPending=false; function prepareSweep(){ Object.assign(POC,{sweep,startSweep,stopSweep}); let handledSpace=false; document.addEventListener('keydown',e=>{ if(e.key==='Escape'&&sweep.running)stopSweep(); if((e.code!=='Space'&&e.key!==' ')||e.altKey||e.ctrlKey||e.metaKey)return; // A held key must neither toggle repeatedly nor activate a focused button. if(e.repeat){if(handledSpace){e.preventDefault();e.stopPropagation()}return} if(e.target.closest('input,textarea,select,[contenteditable]')||document.querySelector('dialog[open]'))return; // Stopping playback takes precedence over a button's normal Space action. if(!sweep.running&&e.target.closest('button,a'))return; handledSpace=true;e.preventDefault();e.stopPropagation(); sweep.running?stopSweep():startSweep(); },true); document.addEventListener('keyup',e=>{ if((e.code==='Space'||e.key===' ')&&handledSpace){handledSpace=false;e.preventDefault();e.stopPropagation()} },true); window.addEventListener('blur',()=>{handledSpace=false}); document.addEventListener('visibilitychange',()=>{if(document.hidden&&sweep.running)stopSweep()}); } function scheduleInteraction(){ if(interactionPending)return; interactionPending=true;requestAnimationFrame(()=>{interactionPending=false;drawInteraction()}); } function drawInteraction(){ const dpr=interaction.width/W; fx.setTransform(dpr,0,0,dpr,0,0);fx.clearRect(0,0,W,H); const angle=sweep.running?sweep.angle:selected?.angle??sweep.angle; // A fine hand joins the selected strand at its root, with no dial or numerals. const c=screen([0,0]),tip=screen(polar(92,angle)); fx.strokeStyle=palette.highlight;fx.lineWidth=.85;fx.lineCap='round'; fx.beginPath();fx.moveTo(...c);fx.lineTo(...tip);fx.stroke(); fx.fillStyle=palette.highlight;fx.beginPath();fx.arc(...c,1.5,0,TAU);fx.fill(); if(!selected)return; const a=selected;fx.lineCap='butt';fx.strokeStyle=palette.highlight;fx.lineWidth=1.8; fx.beginPath();fx.moveTo(...screen([hitData[a.geometryStart],hitData[a.geometryStart+1]])); for(let k=a.geometryStart+4;k