Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,19 @@ COPY --from=frontend /build/dist/ /build/q2/cockpit/dist/
# fetch the release URL directly (github.com/.../releases/download sends no CORS
# header on its redirect → "TypeError: Failed to fetch"), so /body fetches the
# same-origin copy. The asset stays in the release (downloaded at build), never git.
RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.soa.gz \
# 20260629b re-bake: teeth → skeleton + per-vessel diameter boundary (no stray fat
# branches). Pulled under its stamped name, served same-origin AS body.soa.gz so /body
# picks it up; the old body.soa.gz stays in the release untouched.
RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.20260629b.soa.gz \
-o /build/q2/cockpit/dist/body.soa.gz \
&& ls -lh /build/q2/cockpit/dist/body.soa.gz

# Same for the /helix wire: one SoA (BSO2 ver 6) = F16 pos + a canonical Signed360
# NORMAL column in the same struct-of-arrays. Same-origin for the same CORS reason;
# named by cockpit/public/body.manifest.json (helix_latest). Stays in the release.
RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.20260629.v6helix.soa.gz \
-o /build/q2/cockpit/dist/body.20260629.v6helix.soa.gz \
&& ls -lh /build/q2/cockpit/dist/body.20260629.v6helix.soa.gz
RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.20260629b.v6helix.soa.gz \
-o /build/q2/cockpit/dist/body.20260629b.v6helix.soa.gz \
&& ls -lh /build/q2/cockpit/dist/body.20260629b.v6helix.soa.gz

# Sibling deps — clone from GitHub
# graph-flow stub is local (crates/stubs/graph-flow), no rs-graph-llm needed
Expand Down
104 changes: 104 additions & 0 deletions claude-notes/plans/2026-06-29-cesium-hhtl-splat-viewer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# /cesium — HHTL-tiled 3D-Gaussian-splat viewer (activation scope)

> Scoping doc, 2026-06-29. The third viewer alongside `/body` (BodyV3 mesh)
> and `/helix` (Signed360-normal mesh). `/cesium` is the **splat** path:
> 3D-Gaussian splatting, HHTL-tiled, with the **helix arc as the place-relative
> orientation (`quat_wxyz`) carrier**. No code uncommented yet — this is the
> ordered plan; cesium modules stay review-gated (Opus + CodeRabbit) per the
> crate's own rule before any `//`-scaffold goes live.

## The insight that drives it (corrected 2026-06-29)

helix `Signed360`'s rim is the **`(start, end)` endpoint pair**, each a Fisher-Z
point on the φ-spiral. Two sphere points = a great-circle **arc** = a **rotation**
(3-DOF, a 4D unit quaternion's worth). The crate frames it exactly so: `start` =
"where the arc begins" (the HHTL place anchor, `CurveRuler::from_hhtl(path,depth)`),
`end` = the residue. So the 2 Fisher-Z values do **3D/4D work** — a rotation
**relative to the HHTL tile frame**.

That is precisely a 3DGS gaussian's orientation: `GaussianBatch.quat_wxyz`,
consumed by `Spd3::from_scale_quat` → Σ = R·diag(s²)·Rᵀ. So **helix is the splat
orientation carrier** (place-relative), not sidelined by the quaternion
requirement — it satisfies it. The shading normal `/helix` decodes is just one
2-DOF projection of this fuller frame.
Comment on lines +12 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clarify the Signed360 encoding dimensionality claim.

The document states "2 Fisher-Z values do 3D/4D work" and "3-DOF, a 4D unit quaternion's worth." However, the downstream BodyHelix.tsx decode (lines 140-180) shows Signed360 as a 6-byte encoding: end (rim endpoint), polar (signed polar lift), and az16 (golden azimuth). Two Fisher-Z sphere points define a great-circle arc (2-DOF), but the full normal decode requires the additional polar+azimuth bytes to resolve hemisphere and direction. For a full 3-DOF quaternion, the encoding must preserve all three degrees of freedom — the J1 gate will validate this, but the prose understates the actual 6-byte payload and risks confusing readers about whether the "2 Fisher-Z" rim alone suffices. Consider rephrasing to "the 6-byte Signed360 (rim pair + polar/azimuth) carries 3-DOF orientation" to match the real codec.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@claude-notes/plans/2026-06-29-cesium-hhtl-splat-viewer.md` around lines 12 -
23, The dimensionality description for Signed360 is overstated and should match
the actual codec used by BodyHelix.tsx. Update the prose in this note so it
clearly says the full 6-byte Signed360 payload (the rim endpoint pair plus polar
and azimuth fields) carries the 3-DOF orientation, and avoid implying that the
two Fisher-Z rim values alone provide the full quaternion-equivalent
information. Refer to Signed360, BodyHelix.tsx, and the “rimu/end, polar, az16”
decode terms to keep the wording aligned with the implementation.


## What already exists (don't rebuild)

`ndarray/crates/cesium` — optional parity **oracle** (not in `default-members`,
deps commented, modules `//`-scaffold). Module map already covers the spine:
- `tileset` / `implicit_tiling` — OGC 3D Tiles 1.1 implicit tiling: subtree
availability bitstreams + **Morton/Z-order** = the HHTL nibble-interleave
("Morton in centroid space"). The HHTL↔Cesium tile bridge is *already designed*.
- `khr_gs` / `spz` / `arcgis_pbf` / `esri_crs` — cold-boundary ingest (reverse-
engineer → CAM SoA; never depend on / emit source; no JSON in hotpath).
- `to_cam_soa` — parsed splats → `splat3d::GaussianBatch` + `cam_pq` `[u8;6]`.
- `sse` (screen-space-error LOD) / `hlod` (refine ADD·REPLACE) — LOD machinery.
- `point_fallback` — explicitly "coarse-tier **HHTL** preview" (points before splats).
- `oracle` / `fixtures` — SSIM/PSNR diff vs a reference render; golden gates.

`ndarray/.../splat3d` — `GaussianBatch` (SoA: mean/scale/quat/opacity + 48 SH),
`covariance_x16` (SIMD Σ), `project`, `ply` reader. The renderer compute.

## Phases (each gated by its falsification joint)

### P0 — green skeleton + deps
Uncomment `ndarray = { workspace = true }` in cesium; wire `to_cam_soa` to the
real `GaussianBatch` + `cam_pq` types (drop the `UNVERIFIED` placeholders).
Build stays green; nothing rendered yet. **Respect the review gate** — propose
the uncomment, don't ship it unilaterally.

### P1 — one real scene → GaussianBatch (cold ingest)
Start with the simplest reference: Inria binary `.ply` via `splat3d::ply` →
`GaussianBatch` (ground-truth gaussians). `khr_gs` (glTF KHR_gaussian_splatting)
second. This gives the oracle something to diff against.

### P2 — HHTL tiling (implicit_tiling ↔ HHTL)
Quantize each gaussian's `mean_xyz` → Morton/Z-order → HEEL/HIP/TWIG place;
build the LOD tree from `implicit_tiling` subtree availability. `point_fallback`
renders the coarse HHTL tier (points) before splats stream in.
- **J2 (gate):** the HHTL Morton tile address == the `implicit_tiling` subtree-
local index for the same `(level,x,y[,z])`. Same address, both directions.

### P3 — helix arc → `quat_wxyz` (the insight, made falsifiable)
In `to_cam_soa`, carry each gaussian's orientation as a **place-relative helix
`Signed360`**: `start = CurveRuler::from_hhtl(tile_path, depth)`, `end` = the
residue orientation; decode arc→quaternion (axis = P_start × P_end, angle = arc
length; absolute frame pinned by polar+azimuth). Store the 6-byte Signed360, not
a raw `[f32;4]` quat — the place-relative, HHTL-coupled form.
- **J1 (gate — proves "2-Z does 3D/4D"):** round-trip `quat → Signed360 → quat`
must preserve the **covariance Σ** (`from_scale_quat`) within tol, and the
splat render SSIM/PSNR (vs raw-quat) ≥ threshold. If Σ drifts, the arc↔quat
bijection is wrong and this is a KILL for the place-relative-quat claim
(fall back: store raw quat, keep helix for the normal/metric only).

### P4 — render path (this is "WebGL ndarray")
Compile `splat3d` (project + depth-sort + `covariance_x16` + SH eval) to wasm32;
`/cesium` cockpit viewer streams HHTL tiles and rasterizes gaussians via
WebGL2/WebGPU. `sse` selects tile LOD per frame. (Contrast `/helix`: a mesh path
that needs *no* ndarray in the browser. `/cesium` is where ndarray→wasm pays off
because 3DGS rasterization is heavy SoA compute.)
- **J3 (gate):** wasm `splat3d` render == native `splat3d` within precision tol
(no drift crossing the wasm boundary).

### P5 — oracle + parity
`oracle` diffs our render vs an external reference (Inria/Cesium); `fixtures`
holds golden scenes + thresholds. Keeps "parity / better" honest by measurement,
which is the crate's whole charter.

## Cross-repo & rules
- **cesium crate** lives in `ndarray` (activate there, review-gated; relocates to
`lance-graph/crates/cesium` once flawless). RULE 3: no serde/JSON in hotpath,
ArcGIS **PBF** over `f=json`. Reverse-engineer only — never depend on / emit a
source format; output is always CAM SoA.
- **helix** in lance-graph: the arc→quat decode mirrors `/helix`'s rim→normal
decode. Ideal convergence — ship the helix codec as **one wasm** used by both
`/helix` (normal) and `/cesium` (quat), killing TS reimplementation drift.
- **/cesium viewer** in `q2/cockpit` (like `/helix`).

## First concrete step (smallest provable slice)
Not the whole pipeline — **J1 only, native, no viewer**: a Rust probe that takes
a real `.ply` GaussianBatch, encodes each quat as a place-relative `Signed360`,
decodes back, and reports Σ-error + per-gaussian rotation error (deg). Green J1 =
"the 2 Fisher-Z carry the 3D/4D orientation" is proven on real data before any
tiling, wasm, or cockpit work. Same shape as the `helixbake` round-trip that
proved the normal path (mean 0.26°). If J1 fails, we learn it cheaply.
4 changes: 2 additions & 2 deletions cockpit/public/body.manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"helix_latest": "body.20260629.v6helix.soa.gz",
"note": "Single SoA wire (BSO2 ver 6): F16 positions + a Signed360 NORMAL column in the same struct-of-arrays, plus an HXFL floor trailer (the RollingFloor lo,hi). Baked by helixbake using the REAL lance-graph::helix::ResidueEncoder::encode_signed against the local ndarray fork — the Fisher-Z rim is populated (not zeroed). Published to the fma-body-soa-v3-v1 release; the Dockerfile pulls it same-origin. Decode: rim r=sinθ -> int8 normal at load, Gouraud per-vertex shading (no per-fragment lighting). Sampled round-trip err: mean 0.21 deg, p99 0.85 deg, grid-worst ~1.83 deg.",
"helix_latest": "body.20260629b.v6helix.soa.gz",
"note": "20260629b re-bake from soa_v2: teeth reclassified into the skeleton (layer 4) + per-vessel slicer-fill diameter boundary (no stray fat branches at bends). BSO2 ver 6 = F16 positions + Signed360 NORMAL column + HXFL floor trailer; helixbake (real lance-graph::helix::encode_signed). Decode: rim r=sinθ -> int8 normal at load, Gouraud per-vertex shading. Published to fma-body-soa-v3-v1; Dockerfile pulls same-origin.",
"verts": 4283525
}
Loading