multimodalart HF Staff commited on
Commit
79ba03d
·
verified ·
1 Parent(s): f1f9071

Viewer: wall-clock playback that holds while waiting for frames

Browse files
Files changed (1) hide show
  1. viewer.py +23 -9
viewer.py CHANGED
@@ -122,7 +122,8 @@ CSS = """
122
  JS = """
123
  const view = element.querySelector('.f3a-view');
124
  const labelEl = element.querySelector('.f3a-label');
125
- const st = { ready: false, run: null, frames: [], labels: [], cursor: 0, done: false, lastT: 0, bodyGroups: {}, meshCache: {}, meshUrl: null };
 
126
  let THREE = null;
127
  let renderer, scene, camera, controls, root, dynamicRoot;
128
 
@@ -186,25 +187,38 @@ async function buildScene(msg) {
186
  function handle(msg) {
187
  if (!msg || !st.ready) return;
188
  if (msg.kind === 'reset') {
189
- st.run = msg.run; st.frames = []; st.labels = []; st.cursor = 0; st.done = false;
190
  st.building = true;
191
  buildScene(msg).then(() => { st.building = false; labelEl.textContent = msg.label || 'ready'; });
192
  return;
193
  }
 
194
  if (msg.kind === 'frames' && msg.run === st.run) {
195
  msg.poses.forEach((p, i) => { st.frames[msg.start + i] = p; st.labels[msg.start + i] = msg.labels[i]; });
196
  if (msg.done) st.done = true;
197
  }
198
  }
199
 
200
- function tick(t) {
 
 
 
 
 
 
201
  requestAnimationFrame(tick);
202
  if (!renderer) return;
203
- if (!st.building && t - st.lastT >= 1000 / 30 - 1 && st.cursor < st.frames.length && st.frames[st.cursor]) {
204
- applyPose(st.frames[st.cursor]);
205
- labelEl.textContent = st.labels[st.cursor] || '';
206
- st.cursor += 1;
207
- st.lastT = t;
 
 
 
 
 
 
208
  }
209
  controls.update();
210
  renderer.render(scene, camera);
@@ -218,7 +232,7 @@ function resize() {
218
  camera.updateProjectionMatrix();
219
  }
220
 
221
- element.querySelector('.f3a-replay').addEventListener('click', () => { st.cursor = 0; });
222
 
223
  (async () => {
224
  THREE = await import('https://esm.sh/three@0.160.0');
 
122
  JS = """
123
  const view = element.querySelector('.f3a-view');
124
  const labelEl = element.querySelector('.f3a-label');
125
+ const st = { ready: false, run: null, frames: [], labels: [], shown: -1, playT0: 0, done: false, msgs: 0, bodyGroups: {}, meshCache: {}, meshUrl: null };
126
+ element.f3aState = st;
127
  let THREE = null;
128
  let renderer, scene, camera, controls, root, dynamicRoot;
129
 
 
187
  function handle(msg) {
188
  if (!msg || !st.ready) return;
189
  if (msg.kind === 'reset') {
190
+ st.run = msg.run; st.frames = []; st.labels = []; st.shown = -1; st.playT0 = performance.now(); st.done = false;
191
  st.building = true;
192
  buildScene(msg).then(() => { st.building = false; labelEl.textContent = msg.label || 'ready'; });
193
  return;
194
  }
195
+ st.msgs += 1;
196
  if (msg.kind === 'frames' && msg.run === st.run) {
197
  msg.poses.forEach((p, i) => { st.frames[msg.start + i] = p; st.labels[msg.start + i] = msg.labels[i]; });
198
  if (msg.done) st.done = true;
199
  }
200
  }
201
 
202
+ function available() {
203
+ let n = st.shown;
204
+ while (st.frames[n + 1]) n += 1;
205
+ return n;
206
+ }
207
+
208
+ function tick() {
209
  requestAnimationFrame(tick);
210
  if (!renderer) return;
211
+ const now = performance.now();
212
+ if (!st.building) {
213
+ const avail = available();
214
+ const due = Math.floor((now - st.playT0) * 0.03);
215
+ if (due > avail) st.playT0 = now - (avail + 1) / 0.03;
216
+ const target = Math.min(due, avail);
217
+ if (target > st.shown) {
218
+ applyPose(st.frames[target]);
219
+ labelEl.textContent = st.labels[target] || '';
220
+ st.shown = target;
221
+ }
222
  }
223
  controls.update();
224
  renderer.render(scene, camera);
 
232
  camera.updateProjectionMatrix();
233
  }
234
 
235
+ element.querySelector('.f3a-replay').addEventListener('click', () => { st.shown = -1; st.playT0 = performance.now(); });
236
 
237
  (async () => {
238
  THREE = await import('https://esm.sh/three@0.160.0');