@somakine/viewer
v0.2.0
Published
Framework-neutral Three.js viewer for Somakine.
Maintainers
Readme
@somakine/viewer
npm install @somakine/viewer threethree is a peer dependency and must be installed by the host application.
const viewer = await createSomakine(container, {
dataset,
accessibleLabel: "Interactive body model",
onSelection(selection) {},
onSelectionGroup(selections) {},
});
viewer.setLocale("zh-CN");
viewer.setInteractionMode("pan");
viewer.setExplode(0.5);
viewer.setExplode(0.5, { types: ["muscle"] }); // move muscles around fixed visible anatomy
viewer.setInteractionMode("move"); // drag a picked structure; blank space pans
// Demand-driven startup for hosts that know an initial semantic subset.
const lazyViewer = await createSomakine(container, {
dataset,
accessibleLabel: "Interactive body model",
initialVisibleStructureIds: initialStructureIds,
loading: { mode: "visible", concurrency: 3 },
onAssetProgress(progress) {},
});
await lazyViewer.preloadStructures(nextStructureIds);The host owns controls, navigation, labels, panels, and educational content. The viewer owns the canvas, scene, semantic events, verified loading, and resource disposal.
The default resolver supports self-contained binary GLB assets only. Hosts may provide a custom resolver, but data-pack hashes and byte counts are still verified before parsing.
To render developer-loaded data, compose extensions before creating the Viewer:
const { dataset } = composeDataPacks(basePack, [kneeExtension]);
const viewer = await createSomakine(container, {
dataset,
assetResolver: resolvePackAsset,
accessibleLabel: "Interactive body model",
});resolvePackAsset remains host-owned so local files, package assets, and
authenticated storage can use the same verified rendering contract.
Loading is asset-level and de-duplicated across structures. loading.mode is
"eager" by default; "visible" loads the initial visible structures and
automatically loads missing geometry for visibility/focus operations. The
concurrency limit bounds resolver, verification, and GLB parse jobs. Three.js
scene construction remains on the main thread in P0; worker/WASM decoding needs
a separate transferable scene representation.
selectStructure keeps the current visibility set and applies a distinct
selection style; pass { side } to highlight only one side of a paired
structure. selectStructures(entries) does the same for any arbitrary set of
IDs (or { id, side } entries for mixed sides); the IDs do not need to share a
Region, and onSelectionGroup receives the resulting selection records. Entries
are deduplicated and unknown IDs are omitted; if none resolve, the current
highlight is cleared and an empty group is emitted. focusStructure is the
explicit isolation operation for callers that want only the selected
direct/compound structure (or one side of it, or its declared context
structures) visible. setStructureStyle(id, style, options?) sets or clears a
per-structure material style — colour, emissive treatment, opacity, and surface
parameters; a { side } styles one side, overriding the structure's base.
setLayerVisibility(type, visible) hides or restores a type without loading
assets, changing the camera, clearing selection, or changing explode/offset
state. Hidden groups are excluded from click, pickAt, and move-mode picking;
restoring only returns the structures in the current semantic visible set.
showBody is the explicit whole-body operation and may load missing geometry.
reset restores the complete creation-time scene state: its initial semantic
subset and camera, authored transforms, default styles and visible layers, and
no selection. It never loads or evicts assets, so geometry loaded later remains
cached but hidden unless it belonged to the initial subset. Use resetView to
restore the camera and authored transforms without changing the current
semantic-visible set, side filters, styles, or layer visibility.
setExplode(amount, options?) applies a bounded holographic separation only to the
structures currently visible. A bounds-only planner chooses among canonical
three-dimensional directions, accounts for nearby blocking structures, and
minimizes unnecessary travel. It also preserves clear original up/down,
left/right, and front/back order, so structures do not cross sides or overtake
their anatomical neighbours. The plan is rebuilt only when loaded visibility
changes; changing amount interpolates cached offsets. Each renderable instance
receives its own explode transform even when several instances belong to one
compound semantic structure. Host drag offsets remain structure-level, and
hidden geometry is never loaded. Pass { types: ["muscle"] } (or another
semantic type list) to move only those types; other visible types remain in
their authored positions and anchor the layout. An empty list moves nothing.
The plan is rebuilt when the visible set or type scope changes; changing only
amount stays on the cached O(n) interpolation path.
setStructureOffset(id, offset, { side })
applies a session-level host translation without mutating the data pack, and
resetStructureOffsets() clears those translations. onStructureTransformChange
can persist user edits. setInteractionMode("rotate" | "pan" | "move") chooses
whether primary mouse drag rotates, pans, or moves a picked structure on a
camera-facing plane; in move mode blank space pans and the secondary drag keeps
camera rotation available. Camera framing and orbit limits are calculated from the visible
bounds. Packs with a non-default up/front axis can pass
initialViewDirection and initialViewUp; hosts can expose the canvas
guidance from the active interaction mode, for example “drag to rotate,
right-drag to pan” or “drag a structure to move it”.
Canvas picking is side-aware even when a source node contains bilateral
children: a normal click highlights only the raycast mesh, while Ctrl-click
or Cmd-click toggles additional picked meshes. Programmatic selection, focus,
visibility, and styling also accept an optional { side } to target one side
of a paired structure; without it they operate on the whole semantic Structure,
which may intentionally contain bilateral geometry. The resolved side is
reported on ViewerSelection.side.
API reference: docs/api/viewer.md — every option, viewer method, callback, camera helper, and asset helper with examples.
