diff --git a/docs/guide/js/primer.md b/docs/guide/js/primer.md index db7ae5d..df35e3c 100644 --- a/docs/guide/js/primer.md +++ b/docs/guide/js/primer.md @@ -312,3 +312,64 @@ await renderer.resetCamera(); await interactor.interactorStyle.setCurrentStyleToTrackballCamera(); await interactor.start(); + +## WebGL2 rendering into a canvas without an `id` + +Normally VTK locates the canvas via a CSS selector, which requires the element to have a DOM `id`. If your canvas is framework-managed, dynamically created, or simply has no `id`, register it directly with `session.registerCanvas(key, canvas)`. The key must start with `!` (Emscripten convention) and is then used as the `canvasSelector`. + +When using the annotation auto-load (`id="vtk-wasm"`), the owning session is available as `vtkwasm.session` after `ready` resolves. When using [`loadAsync`](./loading.md) directly, the session is returned from `createStandaloneSession()`: + +```js +const runtime = await loadAsync({ url: "..." }); +const session = runtime.createStandaloneSession(); +const canvasSelector = session.registerCanvas("!vtk-canvas", canvas); +const vtk = session.vtk; +``` + + + +
+const vtk = await vtkwasm.ready;
+const canvas = document.querySelector("canvas");
+const canvasSelector = vtkwasm.session.registerCanvas("!vtk-canvas", canvas);
+const mesh = vtk.vtkPartitionedDataSetCollectionSource({
+    numberOfShapes: 1
+});
+const mapper = vtk.vtkCompositePolyDataMapper();
+await mapper.setInputConnection(await mesh.getOutputPort());
+const actor = vtk.vtkActor({mapper})
+const renderer = vtk.vtkRenderer({background: [0.2, 0.2, 0.2]});
+renderer.addViewProp(actor);
+const renderWindow = vtk.vtkRenderWindow({ canvasSelector });
+await renderWindow.addRenderer(renderer);
+const interactor = vtk.vtkRenderWindowInteractor({
+    canvasSelector,
+    renderWindow,
+});
+await renderer.resetCamera();
+await interactor.interactorStyle.setCurrentStyleToTrackballCamera();
+await interactor.start();
+
diff --git a/docs/public/demo/example.js b/docs/public/demo/example.js index 53a13aa..bc971a9 100644 --- a/docs/public/demo/example.js +++ b/docs/public/demo/example.js @@ -1,4 +1,4 @@ -async function buildWASMScene(vtk, titleText = "Sample VTK.wasm scene") { +async function buildWASMScene(vtk, canvasSelector = "#vtk-wasm-window", titleText = "Sample VTK.wasm scene") { function createSharedTextProperty() { const textProperty = vtk.vtkTextProperty({fontSize: 22}); @@ -54,7 +54,6 @@ async function buildWASMScene(vtk, titleText = "Sample VTK.wasm scene") { await renderer.resetCamera(); // Create a RenderWindow and bind it to a canvas in the DOM - const canvasSelector = "#vtk-wasm-window"; const renderWindow = vtk.vtkRenderWindow({ canvasSelector }); await renderWindow.addRenderer(renderer); const interactor = vtk.vtkRenderWindowInteractor({ diff --git a/docs/public/demo/plain-javascript-annotation-wasm-registry.html b/docs/public/demo/plain-javascript-annotation-wasm-registry.html index c8a35f8..f8e0080 100644 --- a/docs/public/demo/plain-javascript-annotation-wasm-registry.html +++ b/docs/public/demo/plain-javascript-annotation-wasm-registry.html @@ -11,7 +11,7 @@ diff --git a/docs/public/demo/plain-javascript.html b/docs/public/demo/plain-javascript.html index 4720196..6b6087f 100644 --- a/docs/public/demo/plain-javascript.html +++ b/docs/public/demo/plain-javascript.html @@ -9,7 +9,7 @@ vtkwasm.loadAsync({ url: "https://gitlab.kitware.com/api/v4/projects/13/packages/generic/vtk-wasm32-emscripten/9.5.20251215/vtk-9.5.20251215-wasm32-emscripten.tar.gz" }) .then((runtime) => { const session = runtime.createStandaloneSession(); - buildWASMScene(session.vtk, "This scene passes the VTK.wasm bundle from GitLab registry to loadAsync()"); + buildWASMScene(session.vtk, "#vtk-wasm-window", "This scene passes the VTK.wasm bundle from GitLab registry to loadAsync()"); }); diff --git a/src/index.js b/src/index.js index c670770..93cc835 100644 --- a/src/index.js +++ b/src/index.js @@ -24,13 +24,21 @@ export { loadAsync, VtkWasmRuntime, StandaloneSession, RemoteSession }; */ export let ready; +/** + * The {@link StandaloneSession} created by the annotation auto-load. Set after + * `ready` resolves; `undefined` before then and when auto-load is not active. + * Use it to call `session.registerCanvas()` and similar helpers. + */ +export let session; + if (typeof window !== "undefined") { const script = document.querySelector("#vtk-wasm"); if (script) { const url = script.dataset.url || "."; const config = JSON.parse(script.dataset.config || "{}"); - ready = loadAsync({ url, ...config }).then( - (runtime) => runtime.createStandaloneSession().vtk, - ); + ready = loadAsync({ url, ...config }).then((runtime) => { + session = runtime.createStandaloneSession(); + return session.vtk; + }); } } diff --git a/src/runtime.js b/src/runtime.js index 2df39e7..a6e017f 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -170,7 +170,7 @@ export class VtkWasmRuntime { */ createStandaloneSession() { this.#assertLive(); - return new StandaloneSession(new this.#module.vtkStandaloneSession()); + return new StandaloneSession(new this.#module.vtkStandaloneSession(), this.#module); } /** diff --git a/src/standaloneSession.js b/src/standaloneSession.js index 190e7d3..74fb8d2 100644 --- a/src/standaloneSession.js +++ b/src/standaloneSession.js @@ -8,15 +8,18 @@ import { createInstantiatorProxy } from "./core/proxy"; */ export class StandaloneSession { #native = null; + #module = null; #disposed = false; #vtkProxyCache = new WeakMap(); #idToRef = new Map(); /** * @param {object} native - the C++ vtkStandaloneSession instance. + * @param {object} module - the Emscripten module (for specialHTMLTargets access). */ - constructor(native) { + constructor(native, module) { this.#native = native; + this.#module = module; /** * The `vtk` namespace: call `vtk.vtkActor({ ... })` to create objects. * @type {object} @@ -29,6 +32,22 @@ export class StandaloneSession { return this.#native; } + /** + * Register `canvas` under `key` in `specialHTMLTargets` so it can be used as + * a `canvasSelector` without requiring a DOM `id`. The key must start with `!` + * (Emscripten convention). Returns `key` for convenience. + * + * @param {string} key - selector key, e.g. `"!my-canvas"`. + * @param {HTMLCanvasElement} canvas + * @returns {string} the key + */ + registerCanvas(key, canvas) { + if (this.#module?.specialHTMLTargets) { + this.#module.specialHTMLTargets[key] = canvas; + } + return key; + } + /** * Free the C++ session and all objects it owns, and drop proxy caches. * The session is unusable afterwards.