Skip to content
Open
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
61 changes: 61 additions & 0 deletions docs/guide/js/primer.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,64 @@ await renderer.resetCamera();
await interactor.interactorStyle.setCurrentStyleToTrackballCamera();
await interactor.start();</pre>
</Playground>

## 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;
```

<Playground display-i-frame>
<textarea data-lang="html" style="display:none"><!doctype html>
<html lang="en">
<head>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
<script
src="https://unpkg.com/@kitware/vtk-wasm/vtk-umd.js"
id="vtk-wasm"
data-url="https://gitlab.kitware.com/api/v4/projects/13/packages/generic/vtk-wasm32-emscripten/9.6.20260228/vtk-9.6.20260228-wasm32-emscripten.tar.gz"></script>
</head>
<body>
<div style="min-height:300px">
<canvas
tabindex="-1"
onclick="focus()"
oncontextmenu="event.preventDefault()"></canvas>
</div>
</body>
</html></textarea>
<pre data-lang="js" style="display:none">
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();</pre>
</Playground>
3 changes: 1 addition & 2 deletions docs/public/demo/example.js
Original file line number Diff line number Diff line change
@@ -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});
Expand Down Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<canvas id="vtk-wasm-window" tabindex="-1" onclick="focus()"></canvas>
<script>
vtkwasm.ready.then((vtk) => {
buildWASMScene(vtk,"This scene points the data-url in script tag to the VTK.wasm bundle from GitLab registry");
buildWASMScene(vtk, "#vtk-wasm-window", "This scene points the data-url in script tag to the VTK.wasm bundle from GitLab registry");
});
</script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion docs/public/demo/plain-javascript.html
Original file line number Diff line number Diff line change
Expand Up @@ -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()");
});
</script>
</body>
Expand Down
14 changes: 11 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
}
}
2 changes: 1 addition & 1 deletion src/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
21 changes: 20 additions & 1 deletion src/standaloneSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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.
Expand Down