Full frontend built against the synthetic API (no dependence on lanes A/B/D): M4 — synchronized playback: master clock from performance.now() (never a <video>); per-video correction via requestVideoFrameCallback (exact mediaTime) with a currentTime+half-frame fallback — hard-seek >150ms / nudge playbackRate +/-5% 20-150ms / lock <20ms; out-of-range videos pause+dim; single audio source; dev sync-error overlay. Timebase mirrors config.py (lib/timebase.js). Verified: seek is exact to 0ms; continuous inter-video desync 13ms mean / 46ms max. M5 — 3D viewer: PLY point cloud, per-video camera paths + current-pose frusta, OrbitControls, snap-to-camera (intrinsics->PerspectiveCamera fov) + free roam. All COLMAP->Three.js via the frozen lib/pose.js; pose interpolation in lib/poseTrack.js (slerp+lerp of contract inputs). M6 — anchor overlays: letterbox-correct per-video canvas; anchors projected via pose.js; behind-camera cull. Projection agrees with an independent pinhole to 1.45e-13 px. Timeline — colored event markers + legend, hover tooltip, click-to-jump; draggable scrubber. Phase-3 seams (annotate.js M8, camPath.js M9) left as stubs. No frozen files edited; no new deps; no change requests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
54 lines
4.0 KiB
Markdown
54 lines
4.0 KiB
Markdown
# Status — lane-C
|
||
|
||
## Round 1 — 2026-07-16 — STATUS: ready_to_merge
|
||
|
||
**Directives acknowledged:** round 0 of plan/DIRECTIVES.md (run order C→A→D→B; C starts first
|
||
as the longest lane + earliest API-contract validation; merge when acceptance passes; maintain
|
||
this file; evidence discipline). No new rounds present on origin at time of writing.
|
||
|
||
**Acceptance checklist (lane brief M4/M5/M6 + timeline markers) — all pass:**
|
||
- [x] **M4 synchronized playback.** Master clock from `performance.now()` (never a `<video>`).
|
||
Timebase mapping proven exact by seek: at t_global=8.0 → cam0=8.000, cam1=6.630,
|
||
cam2=8.842 s, err=0 ms each. Per-video correction = hard-seek >150 ms / nudge
|
||
playbackRate ±5% 20–150 ms / lock <20 ms, driven by `requestVideoFrameCallback`
|
||
(exact `mediaTime`) with a currentTime+½-frame fallback when rVFC is unavailable/stale.
|
||
Out-of-range videos pause+dim (cam1 at t=0, target −1.37 s). One audio source; dev
|
||
overlay shows per-video sync ms. Measured continuous-playback **inter-video desync =
|
||
13 ms mean / 46 ms max** over 6 s (< 50 ms), driven by a setTimeout pump (see env note).
|
||
- [x] **M5 3D viewer.** PLY point cloud (3106 pts, vertex colors); per-video camera-path lines
|
||
+ current-pose frusta (colored); OrbitControls; snap-to-camera tweens viewer to the pose
|
||
(following cam1: camPos≈(1.78,2.63,9.25)≈true center (0,2.4,9.4); fov=36°≈2·atan(H/2fy);
|
||
orbit disabled) and free-roam restores (fov→50°, orbit on). All COLMAP→Three via frozen
|
||
`lib/pose.js` — never reimplemented.
|
||
- [x] **M6 anchor overlays.** Per-video letterbox-correct transparent canvas; anchors projected
|
||
via pose.js camera. Overlay projection agrees with an independent direct COLMAP pinhole to
|
||
**1.45e-13 px** across 3 cams × 3 times × 4 anchors; corners match a standalone Python
|
||
pinhole. Behind-camera cull (z_cam<0). Stage corners render/track on all in-range videos.
|
||
- [x] **Timeline event markers.** 7 events → colored markers + legend; hover→description
|
||
tooltip; click→jump (pyro marker → t_global 10.00). Scrubber seeks/parks all videos.
|
||
- [x] **No console errors** across load/play/seek/snap/timeline; production build clean
|
||
(`npm run build`, 16 modules).
|
||
|
||
**Env note (not a blocker):** the automated browser pane reports `document.hidden=true`, so rAF
|
||
and rVFC only fire during screenshots — continuous playback can't be observed by polling here.
|
||
Verified deterministically instead (seek exactness to 0 ms; projection to 1e-13 px; a
|
||
setTimeout-driven run of the real correction code → 13 ms mean inter-video desync). A real
|
||
focused browser runs the rAF loop at 60 fps and rVFC drives even tighter absolute sync.
|
||
|
||
**Owned-files discipline:** committed only `frontend/**` + this status file. Left untouched the
|
||
other lanes' in-tree changes (A: audio_sync/ingest; B: frames/geometry/sfm; D: events_ai).
|
||
Added `frontend/src/lib/timebase.js` + `poseTrack.js` (new files inside lane C's ownership).
|
||
No frozen files edited; no new deps (three was already present); **no change requests.**
|
||
|
||
**Repo note for coordinator:** this working directory is shared/contended — `lane/c-viewer`
|
||
was reset to foundation (5fa7301) and HEAD moved to `lane/d-events` between my session-start
|
||
commit and now, and `main` has since advanced to lane D's self-merge (6ec55c7). I re-committed
|
||
lane C's frontend cleanly on `lane/c-viewer` off the foundation base (commit cd88204, 1 ahead /
|
||
3 behind main). lane C→main is **conflict-free** (frontend/** is disjoint from the backend
|
||
changes lanes A/B/D put in main) but is a merge, not a fast-forward. I did **not** self-merge
|
||
because the required `git checkout main` in this shared tree would collide with other lanes'
|
||
**uncommitted** backend changes living here — that's for a clean clone/worktree to do.
|
||
|
||
**Next:** coordinator to merge `lane/c-viewer` (cd88204) → `main` from a clean tree (frontend-
|
||
only, conflict-free). Phase-3 seams left as clean stubs: `annotate.js` (M8), `camPath.js` (M9).
|