Skip to content

webapp-ng: vtk.wasm field viewer — client-side FV pipeline (/vtk-wasm) - #1829

Open
jcschaff wants to merge 4 commits into
masterfrom
vtk/field-viewer
Open

webapp-ng: vtk.wasm field viewer — client-side FV pipeline (/vtk-wasm)#1829
jcschaff wants to merge 4 commits into
masterfrom
vtk/field-viewer

Conversation

@jcschaff

@jcschaff jcschaff commented Aug 4, 2026

Copy link
Copy Markdown
Member

What

A /vtk-wasm route that renders VCell's finite-volume field geometry entirely client-side through the custom VTK-compiled-to-WebAssembly bundle — the payoff of the §8B design (docs/salad-3d-renderer-design.md). It proves the whole chain end-to-end in webapp-ng.

A real VCell cell membrane (cytosol domain, 16306 pts / 12210 voxels) renders as a shaded 3-D surface in ~1.3 s, computed in the browser — no server-side VTK, no readers.

The pipeline (all in-browser, via the wasm standalone session)

  1. Build the raw whole-voxel unstructured grid in-memory from arrays (vtkPoints + vtkCellArray + vtkUnstructuredGrid.setCells) — a fixture stands in for server-sent arrays.
  2. vtkGeometryFilter → boundary surface.
  3. vtkWindowedSincPolyDataFilter (iters 12, feature angle 120°, pass-band 0.05) → the faithful FV membrane smoothing.
  4. vtkPolyDataNormals → smooth shading; render via WebGL2.

All filters are constructable in the session thanks to the marshalling-coverage patch shipped in the bundle.

How the bundle is consumed

  • Build-time fetch (scripts/fetch-vtk-wasm.mjs, wired into serve/build): downloads the pinned virtualcell/vcell-vtk-wasm v1.0.0 release and stages the .tar.gz under src/assets/vtk-wasm/ (gitignored, ~12 MB). Served same-origin — GitHub release assets have no CORS header, so the browser can't fetch them cross-origin.
  • UMD loader, not ESM: the ESM entry's runtime does a dynamic import(blobUrl) of the in-browser-untar'd glue, which webpack rewrites and breaks. The UMD build (loaded via a <script>, copied to assets by angular.json) isn't webpack-processed, so its native import() works.
  • No COOP/COEP needed — the bundle is built threads-off.

Notes

Follow-ups

Variable/time/slice/isovalue controls; vtkThreshold for multi-region grids; unstructured volume rendering (vtkProjectedTetrahedra); replace the fixture with real server-sent arrays.

🤖 Generated with Claude Code

jcschaff and others added 4 commits August 3, 2026 08:02
…r foundation)

GitHub release assets can't be fetched cross-origin from the browser (no CORS
header), so the field viewer must load the bundle SAME-ORIGIN. Add a build-time
step that downloads the pinned virtualcell/vcell-vtk-wasm release and unpacks
vtkWebAssembly.{mjs,wasm} into src/assets/vtk-wasm/ (served at /assets/vtk-wasm/).

- scripts/fetch-vtk-wasm.mjs: idempotent fetch + unpack (VTK_WASM_TAG default
  v1.0.0; VTK_WASM_BUNDLE=<local .tar.gz> overrides for offline/dev). Verified
  locally against the built bundle.
- package.json: pin @kitware/vtk-wasm 2.1.8 (the loader); prepend
  `npm run fetch:vtk-wasm` to the serve + build scripts so the bundle is present.
- .gitignore the 78 MB fetched bundle.

Foundation for the field-viewer component (loads /assets/vtk-wasm via the loader),
which follows once the v1.0.0 release is published. Until then, builds on this
branch need the v1.0.0 release reachable (or VTK_WASM_BUNDLE set).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMxhE4gyPPRncpMPrRxnBW
Renders the custom VTK-compiled-to-WebAssembly bundle in the browser end-to-end:
build-time fetch of the vcell-vtk-wasm release -> same-origin /assets/vtk-wasm ->
UMD loader untars the .tar.gz in-browser -> standalone session renders via WebGL2.
Verified headless (a cone renders in ~3.9 s in the app shell).

- fetch-vtk-wasm.mjs: serve the .tar.gz as-is (the loader's loadAsync untars it
  in-browser via DecompressionStream), instead of unpacking .mjs/.wasm.
- angular.json: copy @kitware/vtk-wasm's UMD loader (vtk.umd.js) into
  assets/vtk-wasm at build time.
- VtkWasmViewerComponent + /vtk-wasm route + type shim. Uses the UMD loader via a
  <script> tag: the ESM entry's runtime does a dynamic import(blobUrl) of the
  untar'd glue that webpack rewrites and breaks ("Critical dependency ... is an
  expression"); the UMD build isn't webpack-processed, so its native import works.

A cone is the smoke test; the FV pipeline (in-memory unstructured grid ->
vtkThreshold -> vtkGeometryFilter -> vtkWindowedSincPolyDataFilter, all
constructable via the marshal-coverage patch) is the next increment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMxhE4gyPPRncpMPrRxnBW
…brane)

The vtk.wasm viewer now runs VCell's finite-volume surface-smoothing pipeline
entirely in the browser (design doc §8B): build the raw whole-voxel unstructured
grid in-memory from arrays → vtkGeometryFilter (boundary surface) →
vtkWindowedSincPolyDataFilter (iters 12, feature angle 120°, pass-band 0.05) →
render via WebGL2. All filters constructable in the standalone session via the
marshal-coverage patch.

Verified headless: a real VCell cell membrane (cytosol domain of SimID_946368938,
16306 pts / 12210 voxels) renders in ~1.85 s. The grid built in-session via
vtkPoints + vtkCellArray + vtkUnstructuredGrid.setCells (single VTK_VOXEL type),
proving the in-memory grid path (server sends raw arrays, client smooths + renders
— no readers needed).

- fv-sample-grid.json: sample FV grid fixture (exported from pyvcell) standing in
  for server-sent arrays.
- vtk-wasm-viewer.component.ts: grid build + pipeline + render.

Polish next: actor color/shading (property API), variable/time/slice/isovalue
controls, and threshold for multi-region grids.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMxhE4gyPPRncpMPrRxnBW
Assigning actor.property.color = [...] is a no-op in the standalone session; set
color via actor.getProperty().setColor(...). Also add vtkPolyDataNormals after the
smoothing filter so the mapper shades smoothly (Gouraud) instead of flat/faceted.
The membrane now renders as a shaded green 3-D surface. Verified headless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMxhE4gyPPRncpMPrRxnBW
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant