-
Notifications
You must be signed in to change notification settings - Fork 0
helix: real-crate Fisher-Z rim bake + rim decode + Gouraud render #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dec9135
f4d10af
57c52f1
bda9ab9
f9f63e0
f3f25e2
3777522
3e32b5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +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.", | ||
| "verts": 4283525 | ||
| } |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -61,7 +61,11 @@ function conceptColor(layerId: number, matRgb: [number, number, number], row: nu | |||||||||||||
| return [Math.min(255, out[0]), Math.min(255, out[1]), Math.min(255, out[2])]; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| interface ConceptInfo { row: number; name: string; centroid: [number, number, number]; layer: number; material: string } | ||||||||||||||
| interface ConceptInfo { row: number; name: string; centroid: [number, number, number]; layer: number; material: string; verts: number } | ||||||||||||||
|
|
||||||||||||||
| // semantic x-ray opacity per compartment (id 1..8): skin/muscle fade, organ mid, | ||||||||||||||
| // skeleton mid, vessel + nervous opaque so they pop through the body. index 0 unused. | ||||||||||||||
| const LAYER_XRAY_ALPHA = new Float32Array([1, 0.10, 0.20, 0.45, 0.50, 1.0, 1.0, 0.22, 0.30]); | ||||||||||||||
|
|
||||||||||||||
| // 64K IEEE-half → f32 lookup (built once): ver-5 wire stores positions as F16 | ||||||||||||||
| // (10-bit mantissa, ~0.2 mm here — no BF16 staircase). LUT keeps decode O(1)/vertex. | ||||||||||||||
|
|
@@ -82,7 +86,7 @@ interface Decoded { | |||||||||||||
| colors: Uint8Array; alpha: Float32Array; layer: Float32Array; row: Float32Array; | ||||||||||||||
| materials: Material[]; labels: string[]; concepts: ConceptInfo[]; | ||||||||||||||
| } | ||||||||||||||
| interface RenderState { enabled: Float32Array; alpha: number; transparent: boolean; lodOn: boolean; focus: { t: [number, number, number]; d: number } | null } | ||||||||||||||
| interface RenderState { enabled: Float32Array; alpha: number; transparent: boolean; lodOn: boolean; selRow: number; focus: { t: [number, number, number]; d: number } | null } | ||||||||||||||
|
|
||||||||||||||
| function decodeBso2(buf: ArrayBuffer): Decoded { | ||||||||||||||
| const dv = new DataView(buf); | ||||||||||||||
|
|
@@ -97,7 +101,7 @@ function decodeBso2(buf: ArrayBuffer): Decoded { | |||||||||||||
| const layerOff = o; o += nC; // LAYER index u8 (1..8) | ||||||||||||||
| const labOff = o; o += 4 * nC; // label codebook index u32 | ||||||||||||||
| const cenOff = o; o += 12 * nC; // per-concept centroid 3×f32 (search zoom + server LOD) | ||||||||||||||
| o += 8 * nC; // (vstart,vcount) | ||||||||||||||
| const vrOff = o; o += 8 * nC; // (vstart,vcount) — vcount = mesh size for the popup | ||||||||||||||
| const posOff = o; o += posBytes * nV; | ||||||||||||||
| o += 6 * nV; // helix (server-side) | ||||||||||||||
| const rowOff = o; o += 4 * nV; | ||||||||||||||
|
|
@@ -112,6 +116,7 @@ function decodeBso2(buf: ArrayBuffer): Decoded { | |||||||||||||
| const cLayer = new Uint8Array(buf.slice(layerOff, layerOff + nC)); | ||||||||||||||
| const cNameIdx = new Uint32Array(buf.slice(labOff, labOff + 4 * nC)); | ||||||||||||||
| const cCen = new Float32Array(buf.slice(cenOff, cenOff + 12 * nC)); // bake-space; remap below | ||||||||||||||
| const cVR = new Uint32Array(buf.slice(vrOff, vrOff + 8 * nC)); // [vstart,vcount]×nC | ||||||||||||||
|
|
||||||||||||||
| // per-concept colour (precompute once) + searchable concept table (name + centroid). | ||||||||||||||
| const conceptRgb: [number, number, number][] = new Array(nC); | ||||||||||||||
|
|
@@ -123,6 +128,7 @@ function decodeBso2(buf: ArrayBuffer): Decoded { | |||||||||||||
| concepts[cI] = { | ||||||||||||||
| row: cI, name: labels[cNameIdx[cI]] ?? `concept ${cI}`, layer: li, material: mat.name, | ||||||||||||||
| centroid: [-cCen[cI * 3], cCen[cI * 3 + 2], cCen[cI * 3 + 1]], // (x,y,z)->(-x,z,y) display | ||||||||||||||
| verts: cVR[cI * 2 + 1], | ||||||||||||||
| }; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -186,6 +192,7 @@ const FRAG = ` | |||||||||||||
| precision mediump float; | ||||||||||||||
| uniform float uEnabled[9]; uniform float uGlobalAlpha; | ||||||||||||||
| uniform sampler2D uLod; uniform highp float uLodN; uniform float uLodOn; // server HHTL LOD gate | ||||||||||||||
| uniform float uXray; uniform float uLayerAlpha[9]; uniform highp float uSelRow; // semantic opacity | ||||||||||||||
| varying vec3 vNormal; varying vec3 vColor; varying float vAlpha; varying float vLayer; | ||||||||||||||
| varying highp float vRow; // highp: concept IDs up to ~1658 + the texel-center divide must | ||||||||||||||
| // resolve exactly; mediump's min precision aliases adjacent rows. | ||||||||||||||
|
|
@@ -201,7 +208,11 @@ void main(){ | |||||||||||||
| const vec3 L = vec3(-0.401,0.783,0.476); | ||||||||||||||
| float ndl = max(dot(n,L),0.0); | ||||||||||||||
| float shade = min(0.34 + 0.20*(n.y*0.5+0.5) + 0.12*(-n.x*0.5+0.5) + 0.92*ndl, 1.3); | ||||||||||||||
| gl_FragColor = vec4(vColor*shade, vAlpha * uGlobalAlpha); // #17 alpha × solid/transparent | ||||||||||||||
| // x-ray: per-compartment semantic opacity (skin faint → vessel/nerve opaque); | ||||||||||||||
| // solid mode keeps the legacy uniform alpha. Selected concept always pops to 1.0. | ||||||||||||||
| float a = uXray > 0.5 ? uLayerAlpha[li] : (vAlpha * uGlobalAlpha); | ||||||||||||||
| if(uSelRow >= 0.0 && abs(vRow - uSelRow) < 0.5) a = 1.0; | ||||||||||||||
| gl_FragColor = vec4(vColor*shade, a); | ||||||||||||||
| }`; | ||||||||||||||
|
|
||||||||||||||
| function mount(container: HTMLDivElement, d: Decoded, st: RenderState, onStats: (s: { fps: number }) => void): () => void { | ||||||||||||||
|
|
@@ -238,6 +249,7 @@ function mount(container: HTMLDivElement, d: Decoded, st: RenderState, onStats: | |||||||||||||
| const uniforms = { | ||||||||||||||
| uEnabled: { value: st.enabled }, uGlobalAlpha: { value: st.alpha }, | ||||||||||||||
| uLod: { value: lodTex }, uLodN: { value: d.nConcepts }, uLodOn: { value: 0 }, | ||||||||||||||
| uXray: { value: 0 }, uLayerAlpha: { value: LAYER_XRAY_ALPHA }, uSelRow: { value: -1 }, | ||||||||||||||
| }; | ||||||||||||||
| // solid mode: opaque solids draw fast (transparent:false), #17 vessels blend over | ||||||||||||||
| // them. transparent mode (port of /fma-body's uniform-uAlpha x-ray): BOTH groups go | ||||||||||||||
|
|
@@ -301,6 +313,8 @@ function mount(container: HTMLDivElement, d: Decoded, st: RenderState, onStats: | |||||||||||||
| if (renderer.getPixelRatio() !== pr) renderer.setPixelRatio(pr); | ||||||||||||||
| uniforms.uEnabled.value = st.enabled; // shared by both materials | ||||||||||||||
| uniforms.uGlobalAlpha.value = st.alpha; | ||||||||||||||
| uniforms.uXray.value = st.transparent ? 1 : 0; // x-ray ⇒ per-compartment semantic opacity | ||||||||||||||
| uniforms.uSelRow.value = st.selRow; // selected concept pops to full alpha | ||||||||||||||
| if (!st.lodOn) lodFail = false; // toggling LOD off clears a transient failure → re-enabling retries | ||||||||||||||
| uniforms.uLodOn.value = st.lodOn && !lodFail && lodReady ? 1 : 0; // only cull after a real response | ||||||||||||||
| if (st.focus) { // search-pick zoom: glide to the organ | ||||||||||||||
|
|
@@ -346,16 +360,17 @@ export function BodyV3() { | |||||||||||||
| const [lod, setLod] = useState(false); // server HHTL LOD — opt-in (off = today's full render) | ||||||||||||||
| const [query, setQuery] = useState(''); | ||||||||||||||
| const [selected, setSelected] = useState<ConceptInfo | null>(null); | ||||||||||||||
| const stRef = useRef<RenderState>({ enabled: new Float32Array([0, 0, 1, 1, 1, 1, 1, 1, 1]), alpha: 1, transparent: false, lodOn: false, focus: null }); | ||||||||||||||
| const stRef = useRef<RenderState>({ enabled: new Float32Array([0, 0, 1, 1, 1, 1, 1, 1, 1]), alpha: 1, transparent: false, lodOn: false, selRow: -1, focus: null }); | ||||||||||||||
|
|
||||||||||||||
| useEffect(() => { | ||||||||||||||
| const e = new Float32Array(9); | ||||||||||||||
| for (let i = 1; i <= 8; i++) e[i] = on[i] ? 1 : 0; | ||||||||||||||
| stRef.current.enabled = e; | ||||||||||||||
| stRef.current.transparent = transparent; | ||||||||||||||
| // /fma-body translucency model: one uniform alpha for the WHOLE body. transparent | ||||||||||||||
| // ⇒ 0.42 x-ray (see through skin/muscle to organs); solid ⇒ 1.0 (#17 vessels only). | ||||||||||||||
| stRef.current.alpha = transparent ? 0.42 : 1.0; | ||||||||||||||
| // x-ray opacity is now SEMANTIC (per-compartment uLayerAlpha in the shader), so the | ||||||||||||||
| // whole-body uGlobalAlpha stays at 1.0 in both modes — it only scales #17 vessel | ||||||||||||||
| // blending in solid mode; x-ray ignores it entirely. | ||||||||||||||
| stRef.current.alpha = 1.0; | ||||||||||||||
| stRef.current.lodOn = lod; | ||||||||||||||
| }, [on, transparent, lod]); | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -397,6 +412,7 @@ export function BodyV3() { | |||||||||||||
| function pick(c: ConceptInfo) { | ||||||||||||||
| setSelected(c); | ||||||||||||||
| stRef.current.focus = { t: c.centroid, d: 0.6 }; // glide the camera to the organ | ||||||||||||||
| stRef.current.selRow = c.row; // pop the selected concept to full alpha | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| const btn = (active: boolean): React.CSSProperties => ({ | ||||||||||||||
|
|
@@ -438,7 +454,9 @@ export function BodyV3() { | |||||||||||||
| <div style={{ marginTop: 4, background: '#0e1219', border: '1px solid #1c2530', borderRadius: 6, overflow: 'hidden', maxHeight: 320, overflowY: 'auto' }}> | ||||||||||||||
| {matches.map((c) => ( | ||||||||||||||
| <button type="button" key={c.row} onClick={() => { pick(c); setQuery(''); }} style={{ width: '100%', textAlign: 'left', padding: '6px 9px', cursor: 'pointer', display: 'flex', justifyContent: 'space-between', gap: 8, border: 'none', borderBottom: '1px solid #141b24', background: 'transparent', color: '#cdd9e5', font: '13px ui-monospace, monospace' }}> | ||||||||||||||
| <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.name}</span> | ||||||||||||||
| <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}> | ||||||||||||||
| <span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: 4, background: LAYERS[(c.layer - 1) % 8]?.color, marginRight: 6, verticalAlign: 'middle' }} />{c.name} | ||||||||||||||
| </span> | ||||||||||||||
|
Comment on lines
+457
to
+459
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Ellipsis likely won't trigger on this flex item. The name 🔧 Proposed fix- <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+ <span style={{ minWidth: 0, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
<span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: 4, background: LAYERS[(c.layer - 1) % 8]?.color, marginRight: 6, verticalAlign: 'middle' }} />{c.name}
</span>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| <span style={{ opacity: 0.5, flexShrink: 0 }}>{LAYERS[(c.layer - 1) % 8]?.name}</span> | ||||||||||||||
| </button> | ||||||||||||||
| ))} | ||||||||||||||
|
|
@@ -454,12 +472,14 @@ export function BodyV3() { | |||||||||||||
| <span>{selected.material.replace(/_/g, ' ')}</span> | ||||||||||||||
| <span style={{ opacity: 0.6 }}>row</span> | ||||||||||||||
| <span>#{selected.row}</span> | ||||||||||||||
| <span style={{ opacity: 0.6 }}>mesh</span> | ||||||||||||||
| <span>{selected.verts.toLocaleString()} verts</span> | ||||||||||||||
| <span style={{ opacity: 0.6 }}>centroid</span> | ||||||||||||||
| <span>{selected.centroid.map((v) => v.toFixed(2)).join(', ')}</span> | ||||||||||||||
| </div> | ||||||||||||||
| <div style={{ marginTop: 10, display: 'flex', gap: 6 }}> | ||||||||||||||
| <button style={btn(false)} onClick={() => { stRef.current.focus = { t: selected.centroid, d: 0.6 }; }}>re-center</button> | ||||||||||||||
| <button style={btn(false)} onClick={() => { setSelected(null); stRef.current.focus = null; }}>close</button> | ||||||||||||||
| <button style={btn(false)} onClick={() => { setSelected(null); stRef.current.focus = null; stRef.current.selRow = -1; }}>close</button> | ||||||||||||||
| </div> | ||||||||||||||
| </div> | ||||||||||||||
| )} | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ import { TorsoRender } from './TorsoRender'; | |
| import { TorsoMap } from './TorsoMap'; | ||
| import { FmaBody } from './FmaBody'; | ||
| import { BodyV3 } from './BodyV3'; | ||
| import BodyHelix from './BodyHelix'; | ||
| import { CpicCockpit } from './CpicCockpit'; | ||
| import { ReasoningPage } from './ReasoningPage'; | ||
| import { ErrorBoundary } from './components/ErrorBoundary'; | ||
|
|
@@ -105,6 +106,11 @@ createRoot(document.getElementById('root')!).render( | |
| cockpit/public/body.soa (BSO1 = V3 node table + SPM1 geometry). Polygons, | ||
| not surfels — the successor to /torso-live's decimated 2k-concept torso. */} | ||
| <Route path="/body" element={<BodyV3 />} /> | ||
| {/* /helix — EXPERIMENTAL sibling of /body. Same baked wire, but shades from the | ||
| per-vertex helix-normal bytes (Fisher-2z geodesic codes) via a 256×256 LUT | ||
| materialized once at load: one vertex-shader fetch/vert, no per-vertex decode, | ||
| no rebake. Standalone (BodyHelix.tsx) so it can never break /body (#64). */} | ||
|
Comment on lines
+109
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Update this route comment to match the current
🤖 Prompt for AI Agents |
||
| <Route path="/helix" element={<BodyHelix />} /> | ||
| {/* /cpic — CPIC pharmacogenomics cockpit (gene-first): {gene, diplotype, drug} | ||
| → phenotype → recommendation, 2-hop NARS deduction over the real CPIC tables | ||
| via POST /api/cpic/reason (the standalone cpic crate). Additive, gene-first | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # Body SoA bake artifacts — stamp, don't clobber | ||
|
|
||
| The `/body` (BodyV3) and `/helix` (BodyHelix) cockpit viewers both consume the | ||
| BSO2 SoA wire. **A new bake must never delete or overwrite a working artifact** — | ||
| experiments that turn out bad must not take down the deployed viewer. Stamp every | ||
| build; keep the old ones. | ||
|
|
||
| ## Naming | ||
|
|
||
| ``` | ||
| body.<YYYYMMDD>[-<n>].<fmt>.soa.gz # stamped, immutable once written | ||
| body.<YYYYMMDD>[-<n>].<fmt>.blocks # paired HHTL block bounds (cockpit-server /api/body/lod) | ||
| ``` | ||
|
|
||
| `<fmt>` records the wire format so two encodings on the same day stay distinct: | ||
|
|
||
| | `<fmt>` | meaning | | ||
| |---|---| | ||
| | `v5f16` | ver-5 wire, F16 (IEEE half) positions — current `/body` production | | ||
| | `v5f16h2` | same, helix-normal tuned (2-byte refinement validated) — `/helix` target | | ||
|
Comment on lines
+19
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Update the helix docs to the current v6, canonical-only flow. The naming table still frames Also applies to: 44-46 🤖 Prompt for AI Agents |
||
| | `v3f32` | ver-3 wire, raw f32 positions (legacy / debugging) | | ||
|
|
||
| `-<n>` is an optional same-day rebuild counter (`body.20260628-2.v5f16.soa.gz`). | ||
|
|
||
| ## The two stable names are pointers, not bakes | ||
|
|
||
| - `body.soa.gz` — the artifact `/body` serves. It is a **copy of the current | ||
| production stamp**, never a fresh bake written in place. Re-point it by copying | ||
| a stamped file over it *after* the stamp is validated. | ||
| - `body.manifest.json` — served same-origin; the viewers read it to find the | ||
| current stamps: | ||
|
|
||
| ```json | ||
| { | ||
| "body_latest": "body.20260628.v5f16.soa.gz", | ||
| "helix_latest": "body.20260628.v5f16h2.soa.gz", | ||
| "builds": [ | ||
| { "stamp": "body.20260628.v5f16.soa.gz", "ver": 5, "fmt": "v5f16", "verts": 4221000, "concepts": 1658, "note": "production" }, | ||
| { "stamp": "body.20260628.v5f16h2.soa.gz", "ver": 5, "fmt": "v5f16h2", "verts": 4221000, "concepts": 1658, "note": "helix experiment" } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| `BodyHelix` prefers `helix_latest` (then falls back to the shared `body.soa.gz`); | ||
| `BodyV3` reads `body.soa.gz` directly. A bad helix experiment is rolled back by | ||
| editing one line of the manifest — the production `/body` artifact is never touched. | ||
|
|
||
| ## Producing a stamped bake | ||
|
|
||
| The bake binaries take the output name as `argv[2]`, so the stamp is the caller's | ||
| responsibility (`{out}.blocks` is derived automatically): | ||
|
|
||
| ```sh | ||
| STAMP="body.$(date +%Y%m%d).v5f16" | ||
| ./soabake fma_concepts.json "$STAMP.soa" # writes $STAMP.soa + $STAMP.blocks | ||
| gzip -k "$STAMP.soa" # → $STAMP.soa.gz (keep the raw .soa too) | ||
| # validate, then (and only then) promote: | ||
| cp "$STAMP.soa.gz" body.soa.gz # re-point /body, old stamps retained | ||
| ``` | ||
|
|
||
| Never `rm` a prior stamp. Disk is cheap; a black-screen deploy from a clobbered | ||
| artifact is not. | ||
|
|
||
| ## `/helix` needs the canonical bake (`helixbake`), NOT the old artifact | ||
|
|
||
| The production `body.soa.gz` stores its per-vertex normal with the OLD ndarray | ||
| `helix_orient` codec (a place-blind 3-byte golden-spiral cascade). The canonical | ||
| `/helix` viewer decodes the **place-coupled `lance-graph::helix::Signed360`** | ||
| (6-byte: rim endpoint pair + signed polar lift + golden azimuth), so it CANNOT read | ||
| the old bytes — it would render garbage. `/helix` therefore reads only the stamped | ||
| canonical artifact named by `helix_latest`, and shows "no canonical helix bake yet" | ||
| until one is published. | ||
|
|
||
| Produce it with the **separate** bake crate `scratch-fma/helixbake` (soabake — the | ||
| `/body` bake — is left byte-identical; helixbake is its own crate so the old | ||
| pipeline never resolves helix): | ||
|
|
||
| ```sh | ||
| cd scratch-fma/helixbake | ||
| STAMP="body.$(date +%Y%m%d).v6helix" | ||
| cargo run --release -- /path/to/soa "$STAMP.soa" # writes $STAMP.soa (BSO2 ver 6) + .blocks | ||
| gzip -k "$STAMP.soa" # → $STAMP.soa.gz | ||
| # then add to body.manifest.json: "helix_latest": "$STAMP.soa.gz" | ||
| ``` | ||
|
|
||
| The normal is generated via `helix::ResidueEncoder::encode_signed(place, n, sign)` | ||
| — `place` = the concept's HHTL path, `n` = the nearest spherical-Fibonacci index of | ||
| the world normal, `sign` = its hemisphere. `cargo test` in that crate runs the | ||
| encode↔decode round-trip (the same decode BodyHelix.tsx uses) on synthetic normals, | ||
| no FMA data required. | ||
|
|
||
| (Build note: `helix` depends on `ndarray` via git; `helixbake/Cargo.toml` patches it | ||
| to the local `../../../ndarray` fork. A bake host that can't fetch the git source | ||
| relies on that patch; this sandbox's proxy blocks the fetch, so the crate is | ||
| validated by the round-trip test on a network-enabled host, not here.) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Stale x-ray label now that
uGlobalAlphais fixed at1.0.With whole-body alpha pinned to
1.0and x-ray driven by per-compartmentuLayerAlpha, the stats readout still advertises a flat 0.42 (Line 441:'x-ray (whole body 0.42)'), which no longer reflects the rendered opacity. Update the label to match the semantic per-compartment behavior to avoid misleading users.🤖 Prompt for AI Agents