Renderer STEP conversion evidence #
Environment #
- Browser route tested:
http://127.0.0.1:49177/viewer/body-f-chest-v4 - Fixture path tested:
public/models/EInk_click_v101.stepserved as/models/EInk_click_v101.step - Local tools observed: Node
v24.15.0, npm11.12.1,wasm-bindgen 0.2.121,dx 0.7.9, noemccin PATH.
Commands run #
just fix
cargo check -p polymodel --target wasm32-unknown-unknown --features web
cargo check --workspace
just check
just build-renderer-worker
node tools/design-detector/detect.mjs --json assets/styling/viewer.css
node tools/renderer-step/prepare-full-npm-control.mjs
node tools/renderer-step/measure-assets.mjs
cargo tree -p polymodel --target wasm32-unknown-unknown --features web --edges normal
dx serve --addr 127.0.0.1 --port 49177 --interactive false --watch false --hot-reload false --open false
cargo check --workspace and just check emitted an unrelated jacquard-identity dead-code warning for invalidate_authority_chain; checks still passed.
Asset sizes measured #
Measured with node tools/renderer-step/measure-assets.mjs after staging the single-threaded custom opencascade.yml Docker build via just build-opencascade-step:
| asset | raw bytes | gzip bytes | note |
|---|---|---|---|
public/vendor/opencascade/opencascade.js |
134,503 | 34,346 | single-thread custom JS staged from build output |
public/vendor/opencascade/opencascade.wasm |
6,128,224 | 2,719,991 | single-thread custom WASM staged from build output |
public/vendor/opencascade/opencascade.worker.js |
missing | missing | expected for single-thread build |
public/step_worker_loader.js |
300 | 230 | committed loader |
public/step_worker.js |
7,449 | 2,222 | committed worker harness |
Earlier control measurement of published opencascade.js@1.1.1 was 65.9 MB raw / 13.7 MB gzip WASM and lacked the needed XDE/GLB bindings. The custom build is much smaller and exposes the required conversion path.
Browser observations #
Before clicking Convert STEP fixture, the browser fetched the Dioxus app and renderer worker assets. It did not fetch step_worker_loader.js, step_worker.js, /models/EInk_click_v101.step, or /vendor/opencascade/*.
After clicking Convert STEP fixture, Playwright network output showed lazy fetches:
GET /step_worker_loader.js 200
GET /step_worker.js 200
GET /models/EInk_click_v101.step 200
GET /vendor/opencascade/opencascade.js 200
GET /vendor/opencascade/opencascade.wasm 200
No browser-console error came from the STEP worker. The only console errors observed were Dioxus dev-server websocket messages caused by running with hot reload disabled:
WebSocket connection to 'ws://127.0.0.1:49177/_dioxus?build_id=0' failed: net::ERR_CONNECTION_REFUSED
The page reported crossOriginIsolated=false. The single-threaded custom build does not need COOP/COEP or a pthread worker sidecar.
Conversion result #
The single-threaded custom OpenCascade.js build initialized inside the browser STEP worker, fetched EInk_click_v101.step, converted it to GLB bytes, and posted a converted result back to the Dioxus app.
The app passes the converted GLB object URL into the retained renderer session as MeshFormat::Gltf; the renderer worker loads and renders it successfully. Small electronics/PCB STEP fixtures need camera near-plane and orbit-distance settings that scale with the model bounds.
Validation run after the handoff and camera-fit fixes:
just check
just check passed, including the app check and polymodel-renderer-worker wasm check.
Earlier full-npm-control failures #
The published full npm control initialized inside a browser Worker far enough to validate bindings, fetch the STEP fixture, and load the 65.9 MB OCCT WASM. It could not perform the target STEP→GLB pipeline because the published package lacks required XDE/GLB bindings for this workflow.
Observed worker failures while tightening the harness:
STEP worker blocked after 1325 ms: Message_ProgressRange_1 is missing from this OpenCascade.js build; crossOriginIsolated=false.
STEP worker blocked after 4350 ms: TDF_LabelSequence_1 is missing from this OpenCascade.js build; crossOriginIsolated=false.
The final worker harness fails fast with a required-binding inventory, so future runs report the missing custom-build surface directly rather than one symbol at a time.
Dependency separation #
The app crate does not depend on OCCT or the STEP worker through Cargo. The wasm app dependency-tree check showed only:
polymodel
├── polymodel-api
├── polymodel-renderer-protocol
and no matches for three-d, three-d-asset, stl_io, polymodel-mesh, or opencascade in the app tree. OCCT assets are runtime-loaded by public/step_worker.js only after a STEP source is converted.
What this evidence does and does not decide #
Decided:
- The worker split is the correct architecture boundary for client-side STEP: CAD conversion stays in a separate STEP worker, and rendering remains in the renderer worker.
- The published full npm
opencascade.js@1.1.1artifact is not a viable Polymodel STEP→GLB path: it is large and lacks required bindings for the target XDE/GLB workflow. - A single-threaded custom OpenCascade.js build can convert STEP fixtures to GLB in a browser Worker without cross-origin isolation.
- The existing renderer can load converted GLB output through the current
blob:object-URL handoff.
Not decided:
- A multithreaded pthread custom build was not measured.
- Whether the product path should keep the
blob:handoff or add direct transferred GLB bytes to the renderer protocol. Direct bytes would avoid object URL lifetime/fetch semantics, but the current handoff works.