Chase Hendrick
Lower resolution while dragging (lib/dragres.js): renderer and composer pixel ratio drop on OrbitControls start and return 150 ms after the camera settles; Settings and panel control (off, light, strong), unrollView.dragRes API, ?dragRes= URL, glossary entry, docs/CT_VIEW.md; fix undefined prevClear in hover pick
1c31760 Download lib/dragres.js from SharpMeow/unroll: direct link, hf CLI and curl.
- Browser
- Download file 4.22 kB
-
https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/lib/dragres.js
- Command line
-
hf download hf://spaces/SharpMeow/unroll/lib/dragres.js
-
curl -L -o dragres.js https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/lib/dragres.js
4.22 kB
| // SPDX-License-Identifier: Apache-2.0 | |
| // Lower render resolution while the camera moves: on OrbitControls 'start' the renderer and the | |
| // effect composer drop to a fraction of their pixel ratio; once the camera has stopped changing | |
| // (pointer up and damping settled) the full ratio comes back. Setting: dragRes (off, light, strong). | |
| const SCALE = { off: 1, light: 0.75, strong: 0.5 }; | |
| const MIN_PR = 0.5; | |
| let saved = null, timer = 0, g = null, held = false; | |
| const stats = { drags: 0, lowered: 0, restored: 0, lastScale: 1 }; | |
| function mode() { | |
| try { return window.unrollView.settings.get('dragRes'); } catch { return matchMedia('(pointer: coarse)').matches ? 'strong' : 'light'; } | |
| } | |
| function setPR(pr) { | |
| g.renderer.setPixelRatio(pr); | |
| g.composer?.setPixelRatio?.(pr); | |
| g.composer?.setSize?.(innerWidth, innerHeight); | |
| } | |
| function lower() { | |
| stats.drags++; held = true; | |
| clearTimeout(timer); | |
| const s = SCALE[mode()] ?? 1; | |
| if (s >= 1 || saved !== null) return; | |
| saved = g.renderer.getPixelRatio(); | |
| const pr = Math.max(MIN_PR, saved * s); | |
| if (pr >= saved) { saved = null; return; } | |
| stats.lowered++; stats.lastScale = s; | |
| setPR(pr); | |
| } | |
| function restore() { | |
| if (saved === null) return; | |
| const pr = saved; saved = null; stats.restored++; | |
| setPR(pr); | |
| } | |
| // keep the low resolution while damping still moves the camera, restore 150 ms after the last change | |
| function changed() { if (saved !== null && !held) { clearTimeout(timer); timer = setTimeout(restore, 150); } } | |
| // panel control: a labeled select in a Performance group of the control panel | |
| function buildGroup(P) { | |
| if (document.getElementById('uiPerf')) return; | |
| const grp = P.group('Performance', 'ui-perf'); grp.id = 'uiPerf'; | |
| grp.insertAdjacentHTML('beforeend', `<label class="ui-row" for="dragResSel"><span>Resolution while dragging</span><select id="dragResSel" aria-label="Resolution while dragging the camera"><option value="off">Full (off)</option><option value="light">Light (0.75x)</option><option value="strong">Strong (0.5x)</option></select></label>`); | |
| P.body.append(grp); | |
| const sel = document.getElementById('dragResSel'); | |
| sel.value = mode(); | |
| sel.onchange = () => window.unrollView.settings?.set('dragRes', sel.value); | |
| addEventListener('unroll:settings', (e) => { sel.value = e.detail.dragRes; }); | |
| P.addAction?.('dragRes', { id: 'dragResSel', label: 'Resolution while dragging: off, light or strong', run: (m) => window.unrollView.settings?.set('dragRes', m) }); | |
| P.measure?.(); | |
| } | |
| function init() { | |
| const v = window.unrollView; | |
| if (!v?.gfx?.controls) return setTimeout(init, 100); | |
| g = v.gfx; | |
| g.controls.addEventListener('start', lower); | |
| g.controls.addEventListener('change', changed); | |
| g.controls.addEventListener('end', () => { held = false; clearTimeout(timer); timer = setTimeout(restore, 150); }); | |
| if (window.unrollPanel) buildGroup(window.unrollPanel); else addEventListener('unroll:panel', (e) => buildGroup(e.detail), { once: true }); | |
| v.dragRes = { | |
| state: () => ({ mode: mode(), active: saved !== null, pixelRatio: g.renderer.getPixelRatio(), fullPixelRatio: saved ?? g.renderer.getPixelRatio(), ...stats }), | |
| set: (m) => v.settings?.set('dragRes', m), | |
| // render cost at full and lowered pixel ratio: n synchronous composer renders each, gl.finish between | |
| bench: (n = 5, m) => { | |
| const s = SCALE[m || mode()] ?? 0.75, gl = g.renderer.getContext(), full = g.renderer.getPixelRatio(); | |
| const px = new Uint8Array(4), sync = () => gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, px); // readPixels waits for the GPU | |
| const run = (pr) => { setPR(pr); g.composer.render(); sync(); const t0 = performance.now(); for (let i = 0; i < n; i++) { g.composer.render(); sync(); } return +((performance.now() - t0) / n).toFixed(1); }; | |
| const out = { pixelRatio: full, scale: s, fullMs: run(full), lowMs: run(Math.max(MIN_PR, full * s)) }; | |
| setPR(full); out.speedup = +(out.fullMs / out.lowMs).toFixed(2); return out; | |
| }, | |
| // test hooks: simulate a drag without a pointer | |
| begin: () => { lower(); return g.renderer.getPixelRatio(); }, | |
| end: () => { held = false; clearTimeout(timer); restore(); return g.renderer.getPixelRatio(); }, | |
| }; | |
| } | |
| init(); | |