@selvajs/visualization 1.0.0-beta.0 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -10
- package/dist/gpu-dispose-Dkj-BKt4.js +2 -0
- package/dist/gpu-dispose-Dkj-BKt4.js.map +1 -0
- package/dist/gpu-dispose-DrN-ryj5.cjs +2 -0
- package/dist/gpu-dispose-DrN-ryj5.cjs.map +1 -0
- package/dist/index.cjs +0 -1
- package/dist/index.d.cts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -1
- package/dist/parse.cjs +3 -3
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +135 -142
- package/dist/parse.d.ts +135 -142
- package/dist/parse.js +3 -3
- package/dist/parse.js.map +1 -1
- package/dist/render.cjs +8 -10
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +335 -333
- package/dist/render.d.ts +335 -333
- package/dist/render.js +8 -10
- package/dist/render.js.map +1 -1
- package/dist/rolldown-runtime-BocRIvOZ.cjs +1 -0
- package/dist/scene.cjs +1 -1
- package/dist/scene.cjs.map +1 -1
- package/dist/scene.d.cts +95 -95
- package/dist/scene.d.ts +95 -95
- package/dist/scene.js +1 -1
- package/dist/scene.js.map +1 -1
- package/dist/types-Di80Y609.d.cts +34 -0
- package/dist/types-Di80Y609.d.ts +34 -0
- package/package.json +14 -10
- package/dist/chunk-5XGN7UAV.js +0 -2
- package/dist/chunk-5XGN7UAV.js.map +0 -1
- package/dist/chunk-AQJPVUH3.cjs +0 -2
- package/dist/chunk-AQJPVUH3.cjs.map +0 -1
- package/dist/chunk-BYLIBOAU.cjs +0 -2
- package/dist/chunk-BYLIBOAU.cjs.map +0 -1
- package/dist/chunk-EXAI6IC5.js +0 -2
- package/dist/chunk-EXAI6IC5.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types-CdF9R3qA.d.cts +0 -41
- package/dist/types-CdF9R3qA.d.ts +0 -41
package/dist/scene.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scene/objects.ts","../src/scene/layers.ts","../src/scene/identity.ts","../src/scene/visibility.ts","../src/scene/selection.ts","../src/scene/outliner.ts"],"sourcesContent":["// ============================================================================\n// Scene content: which objects are user content, and how they are labelled\n// ============================================================================\n//\n// A live THREE.Scene holds more than the solve's output — the renderer also adds a camera, lights,\n// and viewer aids (grid, floor, measurement overlay, CSS2D label layer). Anything presenting the\n// scene to a user has to filter those out the same way.\n\nimport * as THREE from 'three';\n\n// Viewer aids tagged by `userData.id` in `render/`; live in the scene graph but are not solve output.\nexport const HELPER_IDS: ReadonlySet<string> = new Set(['grid', 'floor', 'label-layer', 'measure']);\n\nexport function isSceneContent(object: THREE.Object3D): boolean {\n\treturn (\n\t\t!(object instanceof THREE.Camera) &&\n\t\t!(object instanceof THREE.Light) &&\n\t\t!HELPER_IDS.has(object.userData?.id)\n\t);\n}\n\n// Only top-level children: a mesh's own sub-objects (edge overlays, labels) are governed by it,\n// not listed alongside it.\nexport function getSceneObjects(scene: THREE.Scene): THREE.Object3D[] {\n\treturn scene.children.filter(isSceneContent);\n}\n\n// `Line2`/`LineSegments2` are how curves are rendered — an implementation detail no user should\n// have to decode.\nexport function prettyType(type: string): string {\n\treturn (\n\t\ttype\n\t\t\t.replace(/^Line(Segments)?2$/, 'Curve')\n\t\t\t.replace('Mesh', '')\n\t\t\t.replace('Object3D', 'Obj') || type\n\t);\n}\n\nexport function getObjectLabel(object: THREE.Object3D): string {\n\treturn (\n\t\tobject.userData?.name || object.userData?.fileName || object.name || prettyType(object.type)\n\t);\n}\n\nexport function getTypeLabel(object: THREE.Object3D): string {\n\treturn prettyType(object.type);\n}\n","// ============================================================================\n// Layer grouping and search filtering\n// ============================================================================\n\nimport type * as THREE from 'three';\nimport { getObjectLabel } from './objects.js';\n\n/** Objects that carry no layer or category fall under this name. */\nexport const DEFAULT_LAYER = 'Default';\n\n/**\n * Group content objects by their Grasshopper layer.\n *\n * `userData.layer` wins over `userData.category`; objects with neither land in {@link DEFAULT_LAYER}.\n * Insertion order is preserved, so the grouping follows scene-graph order rather than sorting\n * alphabetically — the order geometry was baked in is meaningful to the author.\n */\nexport function groupByLayer(objects: THREE.Object3D[]): Map<string, THREE.Object3D[]> {\n\tconst groups = new Map<string, THREE.Object3D[]>();\n\tfor (const obj of objects) {\n\t\tconst layer: string = obj.userData?.layer || obj.userData?.category || DEFAULT_LAYER;\n\t\tlet bucket = groups.get(layer);\n\t\tif (!bucket) {\n\t\t\tbucket = [];\n\t\t\tgroups.set(layer, bucket);\n\t\t}\n\t\tbucket.push(obj);\n\t}\n\treturn groups;\n}\n\n/**\n * Filter grouped layers by a free-text query.\n *\n * A layer whose *name* matches keeps all its objects — searching for a layer means wanting to see\n * what's on it. Otherwise the layer keeps only the objects whose labels match, and drops out\n * entirely when none do. An empty query returns the input untouched.\n */\nexport function filterLayerGroups(\n\tgroups: Map<string, THREE.Object3D[]>,\n\tquery: string\n): Map<string, THREE.Object3D[]> {\n\tif (!query.trim()) return groups;\n\tconst q = query.toLowerCase();\n\tconst filtered = new Map<string, THREE.Object3D[]>();\n\tfor (const [layerName, objects] of groups) {\n\t\tconst matching = layerName.toLowerCase().includes(q)\n\t\t\t? objects\n\t\t\t: objects.filter((obj) => getObjectLabel(obj).toLowerCase().includes(q));\n\t\tif (matching.length > 0) filtered.set(layerName, matching);\n\t}\n\treturn filtered;\n}\n","// ============================================================================\n// Stable object identity across solves\n// ============================================================================\n//\n// A solve discards all content and rebuilds it, so `THREE.Object3D.uuid` (assigned per instance)\n// cannot answer \"is this the same wall I hid a minute ago\". Anything that must outlive a solve —\n// hidden state, selection, per-object overrides — has to key on what the geometry *is*.\n//\n// Grasshopper gives no object GUIDs, so identity is synthesized from `userData`, in descending\n// order of trustworthiness.\n\nimport type * as THREE from 'three';\n\n// Unit separator, not a printable character: layer/object names may contain anything a user can\n// type, and 'a' + ':' + 'b:c' must not collide with 'a:b' + ':' + 'c'.\nconst SEP = String.fromCharCode(31);\n\n/**\n * Resolution order:\n * 1. `userData.id` — display items (curves, points) arrive with a pre-built pick key.\n * 2. `sourceComponentId` + `originalIndex` — meshes. Component GUID is stable across solves.\n * 3. `name` + `layer` — fallback for content predating `sourceComponentId`. Weaker: two unnamed\n * meshes on one layer collide, but it lets hiding survive a solve on older definitions.\n */\nexport function getStableKey(object: THREE.Object3D): string | null {\n\tconst data = object.userData;\n\tif (!data) return null;\n\n\tif (typeof data.id === 'string' && data.id) return data.id;\n\n\tif (typeof data.sourceComponentId === 'string' && data.sourceComponentId) {\n\t\t// `originalIndex` is 0 for the first mesh of a component, so check presence, not truthiness.\n\t\tconst index = typeof data.originalIndex === 'number' ? data.originalIndex : 0;\n\t\treturn `gh${SEP}${data.sourceComponentId}${SEP}${index}`;\n\t}\n\n\tconst name = typeof data.name === 'string' ? data.name : object.name;\n\tconst layer = typeof data.layer === 'string' ? data.layer : '';\n\tif (name) return `name${SEP}${layer}${SEP}${name}`;\n\n\treturn null;\n}\n\n/**\n * The key an object is tracked under: its stable identity, or its instance uuid when it has none.\n * Use this, not `getStableKey`, when reading the backing set directly (e.g. for a reactive lookup\n * that bypasses `VisibilityState.isHidden`) — `getStableKey` alone misses unidentified objects.\n */\nexport function getTrackingKey(object: THREE.Object3D): string {\n\treturn getStableKey(object) ?? object.uuid;\n}\n","// ============================================================================\n// Visibility: hidden-set bookkeeping over live THREE objects\n// ============================================================================\n//\n// Hiding an object flips `.visible` on the object *and its whole subtree* (edge overlays and CSS2D\n// labels are children, and three does not propagate `.visible` down for the label renderer), and\n// records the fact in a hidden-set so the UI can render an eye-off state and compute per-layer\n// tri-state.\n//\n// The set is keyed by stable identity (see `identity.ts`), not `uuid`, so hiding survives a solve —\n// a solve regenerates uuids but not the Grasshopper source the key is derived from. Objects with no\n// identifying userData fall back to their uuid, so their hidden state lasts only until the next solve.\n\nimport type * as THREE from 'three';\nimport { getTrackingKey as hiddenKey } from './identity.js';\n\n// Backed by a caller-supplied `Set` so a reactive host can pass a framework-observable set\n// (Svelte's `SvelteSet`) and get re-renders for free.\nexport interface VisibilityState {\n\treadonly hidden: Set<string>;\n\tisHidden(object: THREE.Object3D): boolean;\n\tsetVisible(object: THREE.Object3D, visible: boolean): void;\n\tisLayerHidden(objects: THREE.Object3D[]): boolean;\n\t/** Some — but not all — of the layer is hidden. Drives the tri-state eye icon. */\n\tisLayerPartial(objects: THREE.Object3D[]): boolean;\n\ttoggleLayer(objects: THREE.Object3D[]): void;\n\t/** Restores `.visible` on everything the user had hidden before the solve. Call after each solve. */\n\tapplyTo(objects: THREE.Object3D[]): void;\n\treset(): void;\n}\n\nexport function createVisibilityState(hidden: Set<string> = new Set()): VisibilityState {\n\tconst state: VisibilityState = {\n\t\thidden,\n\n\t\tisHidden: (object) => hidden.has(hiddenKey(object)),\n\n\t\tsetVisible(object, visible) {\n\t\t\tobject.visible = visible;\n\t\t\tobject.traverse((child) => {\n\t\t\t\tchild.visible = visible;\n\t\t\t});\n\t\t\tconst key = hiddenKey(object);\n\t\t\tif (visible) hidden.delete(key);\n\t\t\telse hidden.add(key);\n\t\t},\n\n\t\tisLayerHidden: (objects) => objects.length > 0 && objects.every((obj) => state.isHidden(obj)),\n\n\t\tisLayerPartial(objects) {\n\t\t\tconst count = objects.filter((obj) => state.isHidden(obj)).length;\n\t\t\treturn count > 0 && count < objects.length;\n\t\t},\n\n\t\ttoggleLayer(objects) {\n\t\t\t// A partially hidden layer hides the rest — only a fully hidden layer comes back.\n\t\t\tconst show = state.isLayerHidden(objects);\n\t\t\tfor (const obj of objects) state.setVisible(obj, show);\n\t\t},\n\n\t\tapplyTo(objects) {\n\t\t\tfor (const object of objects) {\n\t\t\t\t// Only push objects down. Anything not in the set keeps whatever visibility the render\n\t\t\t\t// layer gave it, so this never fights another feature that hid something for its own\n\t\t\t\t// reasons (isolate mode, section-box culling).\n\t\t\t\tif (!hidden.has(hiddenKey(object))) continue;\n\t\t\t\tobject.visible = false;\n\t\t\t\tobject.traverse((child) => {\n\t\t\t\t\tchild.visible = false;\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\treset: () => hidden.clear()\n\t};\n\treturn state;\n}\n","// ============================================================================\n// Selection: click / ctrl-click / shift-range over a flat list\n// ============================================================================\n//\n// Standard file-explorer selection semantics. Range selection needs the *flattened, currently\n// visible* order — what the user actually sees after search filtering and layer collapse — which\n// only the caller knows, so it is passed in per click rather than derived here.\n\n/** Modifier state of the click that drove the selection. */\nexport interface SelectionModifiers {\n\tshiftKey: boolean;\n\t/** Ctrl (Windows/Linux) or Meta (macOS) — the caller normalizes the platform difference. */\n\ttoggleKey: boolean;\n}\n\nexport interface SelectionState {\n\treadonly selected: Set<string>;\n\t/** The click that anchors the next shift-range. */\n\treadonly anchor: string | null;\n\tisSelected(uuid: string): boolean;\n\t/**\n\t * Apply a click.\n\t *\n\t * @param flatOrder The uuids currently visible, in display order — the span a shift-range walks.\n\t */\n\tselect(uuid: string, modifiers: SelectionModifiers, flatOrder: () => string[]): void;\n\tclear(): void;\n\t/**\n\t * Observe anchor moves. The anchor is a scalar, so unlike `selected` it cannot be watched by\n\t * handing in an observable set — a host that renders it mirrors it through this instead.\n\t *\n\t * @returns An unsubscribe function.\n\t */\n\tonAnchorChange(listener: (anchor: string | null) => void): () => void;\n}\n\nexport function createSelectionState(selected: Set<string> = new Set()): SelectionState {\n\tlet anchor: string | null = null;\n\tconst anchorListeners = new Set<(anchor: string | null) => void>();\n\n\tconst setAnchor = (next: string | null) => {\n\t\tanchor = next;\n\t\tfor (const listener of anchorListeners) listener(next);\n\t};\n\n\treturn {\n\t\tselected,\n\t\tget anchor() {\n\t\t\treturn anchor;\n\t\t},\n\n\t\tisSelected: (uuid) => selected.has(uuid),\n\n\t\tselect(uuid, modifiers, flatOrder) {\n\t\t\tif (modifiers.shiftKey && anchor) {\n\t\t\t\tconst flat = flatOrder();\n\t\t\t\tconst a = flat.indexOf(anchor);\n\t\t\t\tconst b = flat.indexOf(uuid);\n\t\t\t\tif (a !== -1 && b !== -1) {\n\t\t\t\t\tconst [lo, hi] = a < b ? [a, b] : [b, a];\n\t\t\t\t\t// Ctrl+Shift extends the selection; plain Shift replaces it.\n\t\t\t\t\tif (!modifiers.toggleKey) selected.clear();\n\t\t\t\t\tfor (let i = lo; i <= hi; i++) selected.add(flat[i]);\n\t\t\t\t}\n\t\t\t\t// The anchor stays put, so dragging the shift-click around pivots on the same origin.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (modifiers.toggleKey) {\n\t\t\t\tif (selected.has(uuid)) selected.delete(uuid);\n\t\t\t\telse selected.add(uuid);\n\t\t\t} else {\n\t\t\t\tselected.clear();\n\t\t\t\tselected.add(uuid);\n\t\t\t}\n\t\t\tsetAnchor(uuid);\n\t\t},\n\n\t\tclear() {\n\t\t\tselected.clear();\n\t\t\tsetAnchor(null);\n\t\t},\n\n\t\tonAnchorChange(listener) {\n\t\t\tanchorListeners.add(listener);\n\t\t\treturn () => anchorListeners.delete(listener);\n\t\t}\n\t};\n}\n","// ============================================================================\n// SceneOutliner: the object-list state machine over a live THREE.Scene\n// ============================================================================\n//\n// Everything an outliner panel needs — content filtering, layer grouping, search, collapse,\n// visibility and selection — with no DOM and no framework. A host renders `layerGroups()` and\n// forwards clicks; see `SceneManager.svelte` in `@selvajs/ui` for the Svelte binding.\n//\n// The outliner *reads* the scene and toggles `.visible`. It does not add, remove, or dispose\n// anything — the render layer owns scene content (`updateScene`), and having two owners of the\n// scene graph is how double-dispose bugs start.\n\nimport type * as THREE from 'three';\nimport { getSceneObjects } from './objects.js';\nimport { filterLayerGroups, groupByLayer } from './layers.js';\nimport { createVisibilityState, type VisibilityState } from './visibility.js';\nimport { createSelectionState, type SelectionModifiers, type SelectionState } from './selection.js';\n\nexport interface SceneOutlinerOptions {\n\t/**\n\t * Backing sets for the mutable state. Supply framework-observable sets (e.g. Svelte's\n\t * `SvelteSet`) to make a host re-render on mutation; omit for plain `Set`s.\n\t */\n\tsets?: {\n\t\thidden?: Set<string>;\n\t\tselected?: Set<string>;\n\t\tcollapsed?: Set<string>;\n\t};\n}\n\nexport interface SceneOutliner {\n\treadonly visibility: VisibilityState;\n\treadonly selection: SelectionState;\n\treadonly collapsed: Set<string>;\n\n\t/** Free-text search over layer and object names. */\n\tsearchQuery: string;\n\n\t/** Content objects of the scene, in scene-graph order. Recomputed on every call. */\n\tobjects(): THREE.Object3D[];\n\t/** Content grouped by layer, after the search filter. */\n\tlayerGroups(): Map<string, THREE.Object3D[]>;\n\n\tisCollapsed(layerName: string): boolean;\n\ttoggleCollapsed(layerName: string): void;\n\n\t/**\n\t * Toggle one object's visibility. When the object is part of a multi-selection, the whole\n\t * selection follows it — hiding one of five selected meshes hides all five.\n\t */\n\ttoggleObject(object: THREE.Object3D): void;\n\n\t/** Apply a click to the selection, resolving shift-ranges against the visible flat order. */\n\tselect(uuid: string, modifiers: SelectionModifiers): void;\n\n\t/**\n\t * Observe shift-range anchor moves, for hosts that mirror it into their own state.\n\t * Convenience passthrough to `selection.onAnchorChange`.\n\t *\n\t * @returns An unsubscribe function.\n\t */\n\tonAnchorChange(listener: (anchor: string | null) => void): () => void;\n\n\t/**\n\t * The uuids currently visible in the panel, in display order: every object of every\n\t * non-collapsed layer that survived the search filter. This is the span a shift-range walks.\n\t */\n\tflatVisibleUuids(): string[];\n\n\t/**\n\t * Re-apply hidden state to freshly built scene content. **Call after every solve.**\n\t *\n\t * A solve discards all content and rebuilds it, so whatever the user had hidden comes back\n\t * visible unless it is hidden again. Hidden state is keyed by stable identity rather than\n\t * instance uuid, so this restores it (see `identity.ts`). Selection is dropped: it refers to\n\t * object instances that no longer exist, and a persistent selection across solves is not a\n\t * behaviour anyone asked for.\n\t */\n\tapplyTo(): void;\n\n\t/** Drop all hidden and selected state, showing everything. Objects are left as they are. */\n\treset(): void;\n}\n\nexport function createSceneOutliner(\n\tscene: THREE.Scene,\n\toptions: SceneOutlinerOptions = {}\n): SceneOutliner {\n\tconst collapsed = options.sets?.collapsed ?? new Set<string>();\n\tconst visibility = createVisibilityState(options.sets?.hidden);\n\tconst selection = createSelectionState(options.sets?.selected);\n\n\tconst outliner: SceneOutliner = {\n\t\tvisibility,\n\t\tselection,\n\t\tcollapsed,\n\t\tsearchQuery: '',\n\n\t\tobjects: () => getSceneObjects(scene),\n\n\t\tlayerGroups: () =>\n\t\t\tfilterLayerGroups(groupByLayer(getSceneObjects(scene)), outliner.searchQuery),\n\n\t\tisCollapsed: (layerName) => collapsed.has(layerName),\n\n\t\ttoggleCollapsed(layerName) {\n\t\t\tif (collapsed.has(layerName)) collapsed.delete(layerName);\n\t\t\telse collapsed.add(layerName);\n\t\t},\n\n\t\ttoggleObject(object) {\n\t\t\tif (selection.isSelected(object.uuid) && selection.selected.size > 1) {\n\t\t\t\tconst chosen = outliner.objects().filter((o) => selection.isSelected(o.uuid));\n\t\t\t\t// Show the group only once every member is hidden, matching the per-layer rule.\n\t\t\t\tconst allHidden = chosen.every((o) => visibility.isHidden(o));\n\t\t\t\tfor (const o of chosen) visibility.setVisible(o, allHidden);\n\t\t\t} else {\n\t\t\t\tvisibility.setVisible(object, visibility.isHidden(object));\n\t\t\t}\n\t\t},\n\n\t\tselect(uuid, modifiers) {\n\t\t\tselection.select(uuid, modifiers, () => outliner.flatVisibleUuids());\n\t\t},\n\n\t\tonAnchorChange: (listener) => selection.onAnchorChange(listener),\n\n\t\tflatVisibleUuids() {\n\t\t\tconst result: string[] = [];\n\t\t\tfor (const [layerName, objects] of outliner.layerGroups()) {\n\t\t\t\tif (collapsed.has(layerName)) continue;\n\t\t\t\tfor (const obj of objects) result.push(obj.uuid);\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\n\t\tapplyTo() {\n\t\t\tvisibility.applyTo(outliner.objects());\n\t\t\tselection.clear();\n\t\t},\n\n\t\treset() {\n\t\t\tvisibility.reset();\n\t\t\tselection.clear();\n\t\t}\n\t};\n\n\treturn outliner;\n}\n"],"mappings":"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","names":["THREE","HELPER_IDS","isSceneContent","object","getSceneObjects","scene","prettyType","type","getObjectLabel","getTypeLabel","DEFAULT_LAYER","groupByLayer","objects","groups","obj","layer","bucket","filterLayerGroups","query","q","filtered","layerName","matching","getObjectLabel","getStableKey","object","data","index","name","layer","getTrackingKey","createVisibilityState","hidden","state","object","getTrackingKey","visible","child","key","objects","obj","count","show","createSelectionState","selected","anchor","anchorListeners","setAnchor","next","listener","uuid","modifiers","flatOrder","flat","a","b","lo","hi","i","createSceneOutliner","scene","options","collapsed","visibility","createVisibilityState","selection","createSelectionState","outliner","getSceneObjects","filterLayerGroups","groupByLayer","layerName","object","chosen","o","allHidden","uuid","modifiers","listener","result","objects","obj"]}
|
|
1
|
+
{"version":3,"file":"scene.js","names":["hiddenKey"],"sources":["../src/scene/objects.ts","../src/scene/layers.ts","../src/scene/identity.ts","../src/scene/visibility.ts","../src/scene/selection.ts","../src/scene/outliner.ts"],"sourcesContent":["// ============================================================================\n// Scene content: which objects are user content, and how they are labelled\n// ============================================================================\n//\n// A live THREE.Scene holds more than the solve's output — the renderer also adds a camera, lights,\n// and viewer aids (grid, floor, measurement overlay, CSS2D label layer). Anything presenting the\n// scene to a user has to filter those out the same way.\n\nimport * as THREE from 'three';\n\n// Viewer aids tagged by `userData.id` in `render/`; live in the scene graph but are not solve output.\nexport const HELPER_IDS: ReadonlySet<string> = new Set(['grid', 'floor', 'label-layer', 'measure']);\n\nexport function isSceneContent(object: THREE.Object3D): boolean {\n\treturn (\n\t\t!(object instanceof THREE.Camera) &&\n\t\t!(object instanceof THREE.Light) &&\n\t\t!HELPER_IDS.has(object.userData?.id)\n\t);\n}\n\n// Only top-level children: a mesh's own sub-objects (edge overlays, labels) are governed by it,\n// not listed alongside it.\nexport function getSceneObjects(scene: THREE.Scene): THREE.Object3D[] {\n\treturn scene.children.filter(isSceneContent);\n}\n\n// `Line2`/`LineSegments2` are how curves are rendered — an implementation detail no user should\n// have to decode.\nexport function prettyType(type: string): string {\n\treturn (\n\t\ttype\n\t\t\t.replace(/^Line(Segments)?2$/, 'Curve')\n\t\t\t.replace('Mesh', '')\n\t\t\t.replace('Object3D', 'Obj') || type\n\t);\n}\n\nexport function getObjectLabel(object: THREE.Object3D): string {\n\treturn (\n\t\tobject.userData?.name || object.userData?.fileName || object.name || prettyType(object.type)\n\t);\n}\n\nexport function getTypeLabel(object: THREE.Object3D): string {\n\treturn prettyType(object.type);\n}\n","// ============================================================================\n// Layer grouping and search filtering\n// ============================================================================\n\nimport type * as THREE from 'three';\nimport { getObjectLabel } from './objects.js';\n\nexport const DEFAULT_LAYER = 'Default';\n\n/**\n * Group content objects by their Grasshopper layer.\n *\n * `userData.layer` wins over `userData.category`; objects with neither land in {@link DEFAULT_LAYER}.\n * Insertion order is preserved, so the grouping follows scene-graph order rather than sorting\n * alphabetically — the order geometry was baked in is meaningful to the author.\n */\nexport function groupByLayer(objects: THREE.Object3D[]): Map<string, THREE.Object3D[]> {\n\tconst groups = new Map<string, THREE.Object3D[]>();\n\tfor (const obj of objects) {\n\t\tconst layer: string = obj.userData?.layer || obj.userData?.category || DEFAULT_LAYER;\n\t\tlet bucket = groups.get(layer);\n\t\tif (!bucket) {\n\t\t\tbucket = [];\n\t\t\tgroups.set(layer, bucket);\n\t\t}\n\t\tbucket.push(obj);\n\t}\n\treturn groups;\n}\n\n/**\n * Filter grouped layers by a free-text query.\n *\n * A layer whose *name* matches keeps all its objects — searching for a layer means wanting to see\n * what's on it. Otherwise the layer keeps only the objects whose labels match, and drops out\n * entirely when none do. An empty query returns the input untouched.\n */\nexport function filterLayerGroups(\n\tgroups: Map<string, THREE.Object3D[]>,\n\tquery: string\n): Map<string, THREE.Object3D[]> {\n\tif (!query.trim()) return groups;\n\tconst q = query.toLowerCase();\n\tconst filtered = new Map<string, THREE.Object3D[]>();\n\tfor (const [layerName, objects] of groups) {\n\t\tconst matching = layerName.toLowerCase().includes(q)\n\t\t\t? objects\n\t\t\t: objects.filter((obj) => getObjectLabel(obj).toLowerCase().includes(q));\n\t\tif (matching.length > 0) filtered.set(layerName, matching);\n\t}\n\treturn filtered;\n}\n","// ============================================================================\n// Stable object identity across solves\n// ============================================================================\n//\n// A solve discards all content and rebuilds it, so `THREE.Object3D.uuid` (assigned per instance)\n// cannot answer \"is this the same wall I hid a minute ago\". Anything that must outlive a solve —\n// hidden state, selection, per-object overrides — has to key on what the geometry *is*.\n//\n// Grasshopper gives no object GUIDs, so identity is synthesized from `userData`, in descending\n// order of trustworthiness.\n\nimport type * as THREE from 'three';\n\n// Unit separator, not a printable character: layer/object names may contain anything a user can\n// type, and 'a' + ':' + 'b:c' must not collide with 'a:b' + ':' + 'c'.\nconst SEP = String.fromCharCode(31);\n\n/**\n * Tries, in order: `userData.id` (display items arrive with a pre-built pick key), then\n * `sourceComponentId` + `originalIndex` (component GUID is stable across solves), then\n * `name` + `layer` as a weaker fallback — two unnamed meshes on one layer collide under it.\n */\nexport function getStableKey(object: THREE.Object3D): string | null {\n\tconst data = object.userData;\n\tif (!data) return null;\n\n\tif (typeof data.id === 'string' && data.id) return data.id;\n\n\tif (typeof data.sourceComponentId === 'string' && data.sourceComponentId) {\n\t\t// `originalIndex` is 0 for the first mesh of a component, so check presence, not truthiness.\n\t\tconst index = typeof data.originalIndex === 'number' ? data.originalIndex : 0;\n\t\treturn `gh${SEP}${data.sourceComponentId}${SEP}${index}`;\n\t}\n\n\tconst name = typeof data.name === 'string' ? data.name : object.name;\n\tconst layer = typeof data.layer === 'string' ? data.layer : '';\n\tif (name) return `name${SEP}${layer}${SEP}${name}`;\n\n\treturn null;\n}\n\n/** Falls back to the instance uuid when the object has no stable identity, unlike `getStableKey`. */\nexport function getTrackingKey(object: THREE.Object3D): string {\n\treturn getStableKey(object) ?? object.uuid;\n}\n","// ============================================================================\n// Visibility: hidden-set bookkeeping over live THREE objects\n// ============================================================================\n//\n// Hiding an object flips `.visible` on the object *and its whole subtree* (edge overlays and CSS2D\n// labels are children, and three does not propagate `.visible` down for the label renderer), and\n// records the fact in a hidden-set so the UI can render an eye-off state and compute per-layer\n// tri-state.\n//\n// The set is keyed by stable identity (see `identity.ts`), not `uuid`, so hiding survives a solve —\n// a solve regenerates uuids but not the Grasshopper source the key is derived from. Objects with no\n// identifying userData fall back to their uuid, so their hidden state lasts only until the next solve.\n\nimport type * as THREE from 'three';\nimport { getTrackingKey as hiddenKey } from './identity.js';\n\n// Backed by a caller-supplied `Set` so a reactive host can pass a framework-observable set\n// (Svelte's `SvelteSet`) and get re-renders for free.\nexport interface VisibilityState {\n\treadonly hidden: Set<string>;\n\tisHidden(object: THREE.Object3D): boolean;\n\tsetVisible(object: THREE.Object3D, visible: boolean): void;\n\tisLayerHidden(objects: THREE.Object3D[]): boolean;\n\t/** Drives the tri-state eye icon. */\n\tisLayerPartial(objects: THREE.Object3D[]): boolean;\n\ttoggleLayer(objects: THREE.Object3D[]): void;\n\t/** Restores `.visible` on everything the user had hidden before the solve. Call after each solve. */\n\tapplyTo(objects: THREE.Object3D[]): void;\n\treset(): void;\n}\n\nexport function createVisibilityState(hidden: Set<string> = new Set()): VisibilityState {\n\tconst state: VisibilityState = {\n\t\thidden,\n\n\t\tisHidden: (object) => hidden.has(hiddenKey(object)),\n\n\t\tsetVisible(object, visible) {\n\t\t\tobject.visible = visible;\n\t\t\tobject.traverse((child) => {\n\t\t\t\tchild.visible = visible;\n\t\t\t});\n\t\t\tconst key = hiddenKey(object);\n\t\t\tif (visible) hidden.delete(key);\n\t\t\telse hidden.add(key);\n\t\t},\n\n\t\tisLayerHidden: (objects) => objects.length > 0 && objects.every((obj) => state.isHidden(obj)),\n\n\t\tisLayerPartial(objects) {\n\t\t\tconst count = objects.filter((obj) => state.isHidden(obj)).length;\n\t\t\treturn count > 0 && count < objects.length;\n\t\t},\n\n\t\ttoggleLayer(objects) {\n\t\t\t// A partially hidden layer hides the rest — only a fully hidden layer comes back.\n\t\t\tconst show = state.isLayerHidden(objects);\n\t\t\tfor (const obj of objects) state.setVisible(obj, show);\n\t\t},\n\n\t\tapplyTo(objects) {\n\t\t\tfor (const object of objects) {\n\t\t\t\t// Only push objects down. Anything not in the set keeps whatever visibility the render\n\t\t\t\t// layer gave it, so this never fights another feature that hid something for its own\n\t\t\t\t// reasons (isolate mode, section-box culling).\n\t\t\t\tif (!hidden.has(hiddenKey(object))) continue;\n\t\t\t\tobject.visible = false;\n\t\t\t\tobject.traverse((child) => {\n\t\t\t\t\tchild.visible = false;\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\treset: () => hidden.clear()\n\t};\n\treturn state;\n}\n","// ============================================================================\n// Selection: click / ctrl-click / shift-range over a flat list\n// ============================================================================\n//\n// Standard file-explorer selection semantics. Range selection needs the *flattened, currently\n// visible* order — what the user actually sees after search filtering and layer collapse — which\n// only the caller knows, so it is passed in per click rather than derived here.\n\nexport interface SelectionModifiers {\n\tshiftKey: boolean;\n\t/** Ctrl (Windows/Linux) or Meta (macOS) — the caller normalizes the platform difference. */\n\ttoggleKey: boolean;\n}\n\nexport interface SelectionState {\n\treadonly selected: Set<string>;\n\t/** The click that anchors the next shift-range. */\n\treadonly anchor: string | null;\n\tisSelected(uuid: string): boolean;\n\t/**\n\t * Apply a click.\n\t *\n\t * @param flatOrder The uuids currently visible, in display order — the span a shift-range walks.\n\t */\n\tselect(uuid: string, modifiers: SelectionModifiers, flatOrder: () => string[]): void;\n\tclear(): void;\n\t/**\n\t * Observe anchor moves. The anchor is a scalar, so unlike `selected` it cannot be watched by\n\t * handing in an observable set — a host that renders it mirrors it through this instead.\n\t *\n\t * @returns An unsubscribe function.\n\t */\n\tonAnchorChange(listener: (anchor: string | null) => void): () => void;\n}\n\nexport function createSelectionState(selected: Set<string> = new Set()): SelectionState {\n\tlet anchor: string | null = null;\n\tconst anchorListeners = new Set<(anchor: string | null) => void>();\n\n\tconst setAnchor = (next: string | null) => {\n\t\tanchor = next;\n\t\tfor (const listener of anchorListeners) listener(next);\n\t};\n\n\treturn {\n\t\tselected,\n\t\tget anchor() {\n\t\t\treturn anchor;\n\t\t},\n\n\t\tisSelected: (uuid) => selected.has(uuid),\n\n\t\tselect(uuid, modifiers, flatOrder) {\n\t\t\tif (modifiers.shiftKey && anchor) {\n\t\t\t\tconst flat = flatOrder();\n\t\t\t\tconst a = flat.indexOf(anchor);\n\t\t\t\tconst b = flat.indexOf(uuid);\n\t\t\t\tif (a !== -1 && b !== -1) {\n\t\t\t\t\tconst [lo, hi] = a < b ? [a, b] : [b, a];\n\t\t\t\t\t// Ctrl+Shift extends the selection; plain Shift replaces it.\n\t\t\t\t\tif (!modifiers.toggleKey) selected.clear();\n\t\t\t\t\tfor (let i = lo; i <= hi; i++) selected.add(flat[i]);\n\t\t\t\t}\n\t\t\t\t// The anchor stays put, so dragging the shift-click around pivots on the same origin.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (modifiers.toggleKey) {\n\t\t\t\tif (selected.has(uuid)) selected.delete(uuid);\n\t\t\t\telse selected.add(uuid);\n\t\t\t} else {\n\t\t\t\tselected.clear();\n\t\t\t\tselected.add(uuid);\n\t\t\t}\n\t\t\tsetAnchor(uuid);\n\t\t},\n\n\t\tclear() {\n\t\t\tselected.clear();\n\t\t\tsetAnchor(null);\n\t\t},\n\n\t\tonAnchorChange(listener) {\n\t\t\tanchorListeners.add(listener);\n\t\t\treturn () => anchorListeners.delete(listener);\n\t\t}\n\t};\n}\n","// ============================================================================\n// SceneOutliner: the object-list state machine over a live THREE.Scene\n// ============================================================================\n//\n// Everything an outliner panel needs — content filtering, layer grouping, search, collapse,\n// visibility and selection — with no DOM and no framework. A host renders `layerGroups()` and\n// forwards clicks; see `SceneManager.svelte` in `@selvajs/ui` for the Svelte binding.\n\nimport type * as THREE from 'three';\nimport { getSceneObjects } from './objects.js';\nimport { filterLayerGroups, groupByLayer } from './layers.js';\nimport { createVisibilityState, type VisibilityState } from './visibility.js';\nimport { createSelectionState, type SelectionModifiers, type SelectionState } from './selection.js';\n\nexport interface SceneOutlinerOptions {\n\t/**\n\t * Backing sets for the mutable state. Supply framework-observable sets (e.g. Svelte's\n\t * `SvelteSet`) to make a host re-render on mutation; omit for plain `Set`s.\n\t */\n\tsets?: {\n\t\thidden?: Set<string>;\n\t\tselected?: Set<string>;\n\t\tcollapsed?: Set<string>;\n\t};\n}\n\nexport interface SceneOutliner {\n\treadonly visibility: VisibilityState;\n\treadonly selection: SelectionState;\n\treadonly collapsed: Set<string>;\n\n\t/** Free-text search over layer and object names. */\n\tsearchQuery: string;\n\n\t/** Not memoized — recomputes from the scene on every call. */\n\tobjects(): THREE.Object3D[];\n\t/** Content grouped by layer, after the search filter. */\n\tlayerGroups(): Map<string, THREE.Object3D[]>;\n\n\tisCollapsed(layerName: string): boolean;\n\ttoggleCollapsed(layerName: string): void;\n\n\t/**\n\t * Toggle one object's visibility. When the object is part of a multi-selection, the whole\n\t * selection follows it — hiding one of five selected meshes hides all five.\n\t */\n\ttoggleObject(object: THREE.Object3D): void;\n\n\t/** Shift-ranges resolve against `flatVisibleUuids()`, not scene-graph order. */\n\tselect(uuid: string, modifiers: SelectionModifiers): void;\n\n\t/**\n\t * Observe shift-range anchor moves, for hosts that mirror it into their own state.\n\t * Convenience passthrough to `selection.onAnchorChange`.\n\t *\n\t * @returns An unsubscribe function.\n\t */\n\tonAnchorChange(listener: (anchor: string | null) => void): () => void;\n\n\t/**\n\t * The uuids currently visible in the panel, in display order: every object of every\n\t * non-collapsed layer that survived the search filter. This is the span a shift-range walks.\n\t */\n\tflatVisibleUuids(): string[];\n\n\t/**\n\t * Re-apply hidden state to freshly built scene content. **Call after every solve.**\n\t *\n\t * A solve discards all content and rebuilds it, so whatever the user had hidden comes back\n\t * visible unless it is hidden again. Hidden state is keyed by stable identity rather than\n\t * instance uuid, so this restores it (see `identity.ts`). Selection is dropped: it refers to\n\t * object instances that no longer exist, and a persistent selection across solves is not a\n\t * behaviour anyone asked for.\n\t */\n\tapplyTo(): void;\n\n\t/** Drop all hidden and selected state, showing everything. Objects are left as they are. */\n\treset(): void;\n}\n\nexport function createSceneOutliner(\n\tscene: THREE.Scene,\n\toptions: SceneOutlinerOptions = {}\n): SceneOutliner {\n\tconst collapsed = options.sets?.collapsed ?? new Set<string>();\n\tconst visibility = createVisibilityState(options.sets?.hidden);\n\tconst selection = createSelectionState(options.sets?.selected);\n\n\tconst outliner: SceneOutliner = {\n\t\tvisibility,\n\t\tselection,\n\t\tcollapsed,\n\t\tsearchQuery: '',\n\n\t\tobjects: () => getSceneObjects(scene),\n\n\t\tlayerGroups: () =>\n\t\t\tfilterLayerGroups(groupByLayer(getSceneObjects(scene)), outliner.searchQuery),\n\n\t\tisCollapsed: (layerName) => collapsed.has(layerName),\n\n\t\ttoggleCollapsed(layerName) {\n\t\t\tif (collapsed.has(layerName)) collapsed.delete(layerName);\n\t\t\telse collapsed.add(layerName);\n\t\t},\n\n\t\ttoggleObject(object) {\n\t\t\tif (selection.isSelected(object.uuid) && selection.selected.size > 1) {\n\t\t\t\tconst chosen = outliner.objects().filter((o) => selection.isSelected(o.uuid));\n\t\t\t\t// Show the group only once every member is hidden, matching the per-layer rule.\n\t\t\t\tconst allHidden = chosen.every((o) => visibility.isHidden(o));\n\t\t\t\tfor (const o of chosen) visibility.setVisible(o, allHidden);\n\t\t\t} else {\n\t\t\t\tvisibility.setVisible(object, visibility.isHidden(object));\n\t\t\t}\n\t\t},\n\n\t\tselect(uuid, modifiers) {\n\t\t\tselection.select(uuid, modifiers, () => outliner.flatVisibleUuids());\n\t\t},\n\n\t\tonAnchorChange: (listener) => selection.onAnchorChange(listener),\n\n\t\tflatVisibleUuids() {\n\t\t\tconst result: string[] = [];\n\t\t\tfor (const [layerName, objects] of outliner.layerGroups()) {\n\t\t\t\tif (collapsed.has(layerName)) continue;\n\t\t\t\tfor (const obj of objects) result.push(obj.uuid);\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\n\t\tapplyTo() {\n\t\t\tvisibility.applyTo(outliner.objects());\n\t\t\tselection.clear();\n\t\t},\n\n\t\treset() {\n\t\t\tvisibility.reset();\n\t\t\tselection.clear();\n\t\t}\n\t};\n\n\treturn outliner;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
//#region src/shared/types.d.ts
|
|
3
|
+
/** Source of truth for {@link Look} — lets consumers (e.g. a style picker) iterate instead of hardcoding names. */
|
|
4
|
+
declare const LOOKS: readonly ["technical", "studio", "showcase"];
|
|
5
|
+
type Look = (typeof LOOKS)[number];
|
|
6
|
+
/**
|
|
7
|
+
* The lighting/material dials a {@link Look} sets. Never carries edges or grid — those are
|
|
8
|
+
* independent overlays.
|
|
9
|
+
*/
|
|
10
|
+
type LookPreset = {
|
|
11
|
+
toneMapping: THREE.ToneMapping;
|
|
12
|
+
toneMappingExposure: number;
|
|
13
|
+
envMapIntensity: number;
|
|
14
|
+
/** Multiplier on the HDR's IBL (`scene.environmentIntensity`). */
|
|
15
|
+
environmentIntensity: number;
|
|
16
|
+
hemisphereIntensity: number;
|
|
17
|
+
ambientIntensity: number;
|
|
18
|
+
cullBackfaces: boolean;
|
|
19
|
+
ambientOcclusion: boolean;
|
|
20
|
+
};
|
|
21
|
+
/** How compute meshes read visually — the parse-time material choices baked from a {@link Look}. */
|
|
22
|
+
interface MaterialAppearanceOptions {
|
|
23
|
+
/** Default 1 (three.js's own material default) when omitted. */
|
|
24
|
+
envMapIntensity?: number;
|
|
25
|
+
/**
|
|
26
|
+
* `THREE.FrontSide` instead of `THREE.DoubleSide` — crisper silhouette on closed solids, but open
|
|
27
|
+
* surfaces (which Rhino also emits) vanish when viewed from behind. Default false (DoubleSide) to
|
|
28
|
+
* stay safe for surface geometry.
|
|
29
|
+
*/
|
|
30
|
+
cullBackfaces?: boolean;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { MaterialAppearanceOptions as i, Look as n, LookPreset as r, LOOKS as t };
|
|
34
|
+
//# sourceMappingURL=types-Di80Y609.d.cts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
//#region src/shared/types.d.ts
|
|
3
|
+
/** Source of truth for {@link Look} — lets consumers (e.g. a style picker) iterate instead of hardcoding names. */
|
|
4
|
+
declare const LOOKS: readonly ["technical", "studio", "showcase"];
|
|
5
|
+
type Look = (typeof LOOKS)[number];
|
|
6
|
+
/**
|
|
7
|
+
* The lighting/material dials a {@link Look} sets. Never carries edges or grid — those are
|
|
8
|
+
* independent overlays.
|
|
9
|
+
*/
|
|
10
|
+
type LookPreset = {
|
|
11
|
+
toneMapping: THREE.ToneMapping;
|
|
12
|
+
toneMappingExposure: number;
|
|
13
|
+
envMapIntensity: number;
|
|
14
|
+
/** Multiplier on the HDR's IBL (`scene.environmentIntensity`). */
|
|
15
|
+
environmentIntensity: number;
|
|
16
|
+
hemisphereIntensity: number;
|
|
17
|
+
ambientIntensity: number;
|
|
18
|
+
cullBackfaces: boolean;
|
|
19
|
+
ambientOcclusion: boolean;
|
|
20
|
+
};
|
|
21
|
+
/** How compute meshes read visually — the parse-time material choices baked from a {@link Look}. */
|
|
22
|
+
interface MaterialAppearanceOptions {
|
|
23
|
+
/** Default 1 (three.js's own material default) when omitted. */
|
|
24
|
+
envMapIntensity?: number;
|
|
25
|
+
/**
|
|
26
|
+
* `THREE.FrontSide` instead of `THREE.DoubleSide` — crisper silhouette on closed solids, but open
|
|
27
|
+
* surfaces (which Rhino also emits) vanish when viewed from behind. Default false (DoubleSide) to
|
|
28
|
+
* stay safe for surface geometry.
|
|
29
|
+
*/
|
|
30
|
+
cullBackfaces?: boolean;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { MaterialAppearanceOptions as i, Look as n, LookPreset as r, LOOKS as t };
|
|
34
|
+
//# sourceMappingURL=types-Di80Y609.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/visualization",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"description": "Headless, extensible viewer core for Selva — parse, render and scene layers over Three.js",
|
|
5
5
|
"author": "VektorNode",
|
|
6
6
|
"license": "MIT",
|
|
@@ -70,12 +70,14 @@
|
|
|
70
70
|
},
|
|
71
71
|
"files": [
|
|
72
72
|
"dist",
|
|
73
|
-
"README.md"
|
|
73
|
+
"README.md",
|
|
74
|
+
"!**/__tests__/**",
|
|
75
|
+
"!**/*.test.*",
|
|
76
|
+
"!**/*.spec.*"
|
|
74
77
|
],
|
|
75
78
|
"sideEffects": false,
|
|
76
79
|
"dependencies": {
|
|
77
|
-
"fflate": "^0.8.2"
|
|
78
|
-
"rhino3dm": "8.17.0"
|
|
80
|
+
"fflate": "^0.8.2"
|
|
79
81
|
},
|
|
80
82
|
"peerDependencies": {
|
|
81
83
|
"three": ">=0.179.0"
|
|
@@ -84,25 +86,27 @@
|
|
|
84
86
|
"@eslint/js": "^10.0.1",
|
|
85
87
|
"@types/node": "^26.1.2",
|
|
86
88
|
"@types/three": "^0.185.1",
|
|
87
|
-
"@vitest/coverage-v8": "^4.1.10",
|
|
88
89
|
"eslint": "^10.8.0",
|
|
89
90
|
"eslint-config-prettier": "^10.1.8",
|
|
90
91
|
"globals": "^17.5.0",
|
|
91
92
|
"prettier": "^3.9.6",
|
|
92
93
|
"three": "^0.185.1",
|
|
93
|
-
"
|
|
94
|
+
"tsdown": "^0.22.14",
|
|
94
95
|
"typescript": "~6.0.3",
|
|
95
96
|
"vite": "^8.1.5",
|
|
96
|
-
"vitest": "^4.1.10"
|
|
97
|
+
"vitest": "^4.1.10",
|
|
98
|
+
"@selvajs/config": "0.0.3"
|
|
97
99
|
},
|
|
98
100
|
"engines": {
|
|
99
101
|
"node": ">=22.0.0"
|
|
100
102
|
},
|
|
103
|
+
"publishConfig": {
|
|
104
|
+
"access": "public"
|
|
105
|
+
},
|
|
101
106
|
"scripts": {
|
|
102
|
-
"build": "pnpm type-check &&
|
|
103
|
-
"dev": "
|
|
107
|
+
"build": "pnpm type-check && tsdown",
|
|
108
|
+
"dev": "tsdown --watch",
|
|
104
109
|
"example": "vite --config vite.config.ts",
|
|
105
|
-
"clean": "node --input-type=module -e \"import fs from 'fs'; fs.rmSync('dist', { recursive: true, force: true })\"",
|
|
106
110
|
"lint": "eslint .",
|
|
107
111
|
"lint:fix": "eslint . --fix",
|
|
108
112
|
"format": "prettier --write .",
|
package/dist/chunk-5XGN7UAV.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/chunk-AQJPVUH3.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkBYLIBOAUcjs = require('./chunk-BYLIBOAU.cjs');var p={VALIDATION_ERROR:"VALIDATION_ERROR",INVALID_STATE:"INVALID_STATE",ENVIRONMENT_ERROR:"ENVIRONMENT_ERROR",INVALID_CONFIG:"INVALID_CONFIG",ENCODING_ERROR:"ENCODING_ERROR",UNKNOWN_ERROR:"UNKNOWN_ERROR"},a= exports.b =class extends Error{constructor(r,n=p.UNKNOWN_ERROR,t){super(r);_chunkBYLIBOAUcjs.a.call(void 0, this,"code");_chunkBYLIBOAUcjs.a.call(void 0, this,"context");_chunkBYLIBOAUcjs.a.call(void 0, this,"originalError");this.name="VisualizationError",this.code=n,this.context=_optionalChain([t, 'optionalAccess', _2 => _2.context]),this.originalError=_optionalChain([t, 'optionalAccess', _3 => _3.originalError]),_optionalChain([t, 'optionalAccess', _4 => _4.originalError])&&(this.cause=t.originalError)}};var l=class{debug(){}info(){}warn(){}error(){}},d=class{debug(o,...r){console.debug(o,...r)}info(o,...r){console.info(o,...r)}warn(o,...r){console.warn(o,...r)}error(o,...r){console.error(o,...r)}},g=new l;function f(){return g}function h(e){if(e===null){g=new l;return}let o=["debug","info","warn","error"].filter(r=>typeof e[r]!="function");if(o.length>0)throw new a(`Logger is missing required method(s): ${o.join(", ")}. A logger must implement debug, info, warn and error.`,p.INVALID_CONFIG,{context:{missingMethods:o}});g=e}function I(){h(new d)}var L=["technical","studio","showcase"];var _three = require('three'); var c = _interopRequireWildcard(_three); var s = _interopRequireWildcard(_three); var w = _interopRequireWildcard(_three);var D="technical",O= exports.i ={studio:{toneMapping:c.ACESFilmicToneMapping,toneMappingExposure:1,envMapIntensity:1,environmentIntensity:1,hemisphereIntensity:.75,ambientIntensity:.4,cullBackfaces:!1,ambientOcclusion:!1},technical:{toneMapping:c.NeutralToneMapping,toneMappingExposure:1,envMapIntensity:.9,environmentIntensity:1,hemisphereIntensity:.35,ambientIntensity:.25,cullBackfaces:!1,ambientOcclusion:!1},showcase:{toneMapping:c.ACESFilmicToneMapping,toneMappingExposure:1.15,envMapIntensity:1.4,environmentIntensity:1.25,hemisphereIntensity:.35,ambientIntensity:.15,cullBackfaces:!1,ambientOcclusion:!1}};function H(e){let o=O[e];return{envMapIntensity:o.envMapIntensity,cullBackfaces:o.cullBackfaces}}function N(){let e=globalThis.Buffer;return typeof e=="function"?e:void 0}function k(e){let o=e.replace(/[\t\n\f\r ]/g,"");if(o.length%4===0&&(o=o.replace(/={1,2}$/,"")),o.length%4===1||!/^[A-Za-z0-9+/]*$/.test(o))throw new a("Invalid base64 input.",p.ENCODING_ERROR,{context:{inputLength:e.length}});let r=N();if(r)return new Uint8Array(r.from(o,"base64"));if(typeof globalThis.atob=="function"){let n=globalThis.atob(o),t=new Uint8Array(n.length);for(let i=0;i<n.length;i++)t[i]=n.charCodeAt(i)&255;return t}throw new a("Base64 decoding not supported in this environment.",p.INVALID_STATE,{context:{environmentInfo:"atob or Buffer not available"}})}function _(e){if(!e||typeof e!="string")return f().warn(`Invalid color input: ${e}, using white`),new s.Color(16777215);let o=e.trim();if(/^#?[0-9A-Fa-f]{6}$/.test(o))try{let n=o.startsWith("#")?o:`#${o}`;return new s.Color(n)}catch (e2){return f().warn(`Invalid hex color: ${e}, using white`),new s.Color(16777215)}if(o.includes(",")){let n=o.split(",").map(t=>parseInt(t.trim(),10));if(n.length===3&&n.every(t=>!isNaN(t)&&t>=0&&t<=255))return new s.Color(n[0]/255,n[1]/255,n[2]/255)}let r=o.toLowerCase();return r in s.Color.NAMES?new s.Color(s.Color.NAMES[r]):(f().warn(`Invalid color string: ${e}, using white`),new s.Color(16777215))}function B(e,o,r="z"){e.forEach(n=>{n.position[r]-=o})}function G(e){let o=new s.Box3;return e.length===0||e.forEach(r=>{r.updateMatrixWorld(!0);let n=new s.Box3().setFromObject(r);o.union(n)}),o}var A="selvaGeometryCache",v= exports.o ="selvaTextureCache",S=new Set;function m(e){return e?!_optionalChain([e, 'access', _5 => _5.userData, 'optionalAccess', _6 => _6[A]]):!1}function R(e){return e?!_optionalChain([e, 'access', _7 => _7.userData, 'optionalAccess', _8 => _8[v]]):!1}function x(e){return e?!S.has(e):!1}var M=new Set;function j(e){M.add(e)}function C(){for(let e of M)try{e()}catch (e3){}}var E=0;function F(){E++;let e=!1;return()=>{e||(e=!0,E--,E<=0&&(E=0,C()))}}var y=new Set,T=1;function U(e){let o=Math.max(1,e);if(o!==T){T=o;for(let r of y)r(o)}}function V(e){return y.add(e),e(T),()=>y.delete(e)}function b(e){if(x(e)){for(let o of Object.values(e))o instanceof w.Texture&&R(o)&&o.dispose();e.dispose()}}function K(e){e&&(Array.isArray(e)?e.forEach(b):b(e))}function $(e,o={}){let{materials:r=!0,onGeometry:n}=o;e.traverse(t=>{let i=t;!i.geometry&&!i.material||(i.geometry&&(_optionalChain([n, 'optionalCall', _9 => _9(i.geometry)]),m(i.geometry)&&i.geometry.dispose()),r&&K(i.material))})}exports.a = p; exports.b = a; exports.c = f; exports.d = h; exports.e = I; exports.f = k; exports.g = L; exports.h = D; exports.i = O; exports.j = H; exports.k = _; exports.l = B; exports.m = G; exports.n = A; exports.o = v; exports.p = R; exports.q = j; exports.r = C; exports.s = F; exports.t = U; exports.u = V; exports.v = $;
|
|
2
|
-
//# sourceMappingURL=chunk-AQJPVUH3.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["c:\\Users\\felix\\coding\\selva\\packages\\visualization\\dist\\chunk-AQJPVUH3.cjs"],"names":[],"mappings":"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","file":"C:\\Users\\felix\\coding\\selva\\packages\\visualization\\dist\\chunk-AQJPVUH3.cjs","sourcesContent":[null]}
|
package/dist/chunk-BYLIBOAU.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var d=Object.defineProperty;var e=(b,a,c)=>a in b?d(b,a,{enumerable:!0,configurable:!0,writable:!0,value:c}):b[a]=c;var f=(b,a,c)=>e(b,typeof a!="symbol"?a+"":a,c);exports.a = f;
|
|
2
|
-
//# sourceMappingURL=chunk-BYLIBOAU.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["c:\\Users\\felix\\coding\\selva\\packages\\visualization\\dist\\chunk-BYLIBOAU.cjs"],"names":[],"mappings":"AAAA,6EAAI,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc","file":"C:\\Users\\felix\\coding\\selva\\packages\\visualization\\dist\\chunk-BYLIBOAU.cjs"}
|
package/dist/chunk-EXAI6IC5.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as u}from"./chunk-5XGN7UAV.js";var p={VALIDATION_ERROR:"VALIDATION_ERROR",INVALID_STATE:"INVALID_STATE",ENVIRONMENT_ERROR:"ENVIRONMENT_ERROR",INVALID_CONFIG:"INVALID_CONFIG",ENCODING_ERROR:"ENCODING_ERROR",UNKNOWN_ERROR:"UNKNOWN_ERROR"},a=class extends Error{constructor(r,n=p.UNKNOWN_ERROR,t){super(r);u(this,"code");u(this,"context");u(this,"originalError");this.name="VisualizationError",this.code=n,this.context=t?.context,this.originalError=t?.originalError,t?.originalError&&(this.cause=t.originalError)}};var l=class{debug(){}info(){}warn(){}error(){}},d=class{debug(o,...r){console.debug(o,...r)}info(o,...r){console.info(o,...r)}warn(o,...r){console.warn(o,...r)}error(o,...r){console.error(o,...r)}},g=new l;function f(){return g}function h(e){if(e===null){g=new l;return}let o=["debug","info","warn","error"].filter(r=>typeof e[r]!="function");if(o.length>0)throw new a(`Logger is missing required method(s): ${o.join(", ")}. A logger must implement debug, info, warn and error.`,p.INVALID_CONFIG,{context:{missingMethods:o}});g=e}function I(){h(new d)}var L=["technical","studio","showcase"];import*as c from"three";var D="technical",O={studio:{toneMapping:c.ACESFilmicToneMapping,toneMappingExposure:1,envMapIntensity:1,environmentIntensity:1,hemisphereIntensity:.75,ambientIntensity:.4,cullBackfaces:!1,ambientOcclusion:!1},technical:{toneMapping:c.NeutralToneMapping,toneMappingExposure:1,envMapIntensity:.9,environmentIntensity:1,hemisphereIntensity:.35,ambientIntensity:.25,cullBackfaces:!1,ambientOcclusion:!1},showcase:{toneMapping:c.ACESFilmicToneMapping,toneMappingExposure:1.15,envMapIntensity:1.4,environmentIntensity:1.25,hemisphereIntensity:.35,ambientIntensity:.15,cullBackfaces:!1,ambientOcclusion:!1}};function H(e){let o=O[e];return{envMapIntensity:o.envMapIntensity,cullBackfaces:o.cullBackfaces}}function N(){let e=globalThis.Buffer;return typeof e=="function"?e:void 0}function k(e){let o=e.replace(/[\t\n\f\r ]/g,"");if(o.length%4===0&&(o=o.replace(/={1,2}$/,"")),o.length%4===1||!/^[A-Za-z0-9+/]*$/.test(o))throw new a("Invalid base64 input.",p.ENCODING_ERROR,{context:{inputLength:e.length}});let r=N();if(r)return new Uint8Array(r.from(o,"base64"));if(typeof globalThis.atob=="function"){let n=globalThis.atob(o),t=new Uint8Array(n.length);for(let i=0;i<n.length;i++)t[i]=n.charCodeAt(i)&255;return t}throw new a("Base64 decoding not supported in this environment.",p.INVALID_STATE,{context:{environmentInfo:"atob or Buffer not available"}})}import*as s from"three";function _(e){if(!e||typeof e!="string")return f().warn(`Invalid color input: ${e}, using white`),new s.Color(16777215);let o=e.trim();if(/^#?[0-9A-Fa-f]{6}$/.test(o))try{let n=o.startsWith("#")?o:`#${o}`;return new s.Color(n)}catch{return f().warn(`Invalid hex color: ${e}, using white`),new s.Color(16777215)}if(o.includes(",")){let n=o.split(",").map(t=>parseInt(t.trim(),10));if(n.length===3&&n.every(t=>!isNaN(t)&&t>=0&&t<=255))return new s.Color(n[0]/255,n[1]/255,n[2]/255)}let r=o.toLowerCase();return r in s.Color.NAMES?new s.Color(s.Color.NAMES[r]):(f().warn(`Invalid color string: ${e}, using white`),new s.Color(16777215))}function B(e,o,r="z"){e.forEach(n=>{n.position[r]-=o})}function G(e){let o=new s.Box3;return e.length===0||e.forEach(r=>{r.updateMatrixWorld(!0);let n=new s.Box3().setFromObject(r);o.union(n)}),o}var A="selvaGeometryCache",v="selvaTextureCache",S=new Set;function m(e){return e?!e.userData?.[A]:!1}function R(e){return e?!e.userData?.[v]:!1}function x(e){return e?!S.has(e):!1}var M=new Set;function j(e){M.add(e)}function C(){for(let e of M)try{e()}catch{}}var E=0;function F(){E++;let e=!1;return()=>{e||(e=!0,E--,E<=0&&(E=0,C()))}}var y=new Set,T=1;function U(e){let o=Math.max(1,e);if(o!==T){T=o;for(let r of y)r(o)}}function V(e){return y.add(e),e(T),()=>y.delete(e)}import*as w from"three";function b(e){if(x(e)){for(let o of Object.values(e))o instanceof w.Texture&&R(o)&&o.dispose();e.dispose()}}function K(e){e&&(Array.isArray(e)?e.forEach(b):b(e))}function $(e,o={}){let{materials:r=!0,onGeometry:n}=o;e.traverse(t=>{let i=t;!i.geometry&&!i.material||(i.geometry&&(n?.(i.geometry),m(i.geometry)&&i.geometry.dispose()),r&&K(i.material))})}export{p as a,a as b,f as c,h as d,I as e,k as f,L as g,D as h,O as i,H as j,_ as k,B as l,G as m,A as n,v as o,R as p,j as q,C as r,F as s,U as t,V as u,$ as v};
|
|
2
|
-
//# sourceMappingURL=chunk-EXAI6IC5.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/errors.ts","../src/shared/logger.ts","../src/shared/types.ts","../src/shared/looks.ts","../src/shared/encoding.ts","../src/shared/geometry.ts","../src/shared/gpu-ownership.ts","../src/shared/gpu-capabilities.ts","../src/shared/gpu-dispose.ts"],"sourcesContent":["/**\n * Errors for the visualization package. Replaces `@selvajs/compute`'s `RhinoComputeError`, which\n * mis-named failures on paths (e.g. the plugin WebSocket) that never touch Rhino.Compute. `code`\n * values match compute's so existing catch-sites keep working.\n */\n\nexport const ErrorCodes = {\n\t/** Structural check failed: bad magic bytes, out-of-window index, malformed metadata. */\n\tVALIDATION_ERROR: 'VALIDATION_ERROR',\n\tINVALID_STATE: 'INVALID_STATE',\n\t/** No `DecompressionStream`, no WebGL context, etc. */\n\tENVIRONMENT_ERROR: 'ENVIRONMENT_ERROR',\n\tINVALID_CONFIG: 'INVALID_CONFIG',\n\t/** Base64 input could not be decoded. */\n\tENCODING_ERROR: 'ENCODING_ERROR',\n\tUNKNOWN_ERROR: 'UNKNOWN_ERROR'\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\nexport class VisualizationError extends Error {\n\tpublic readonly code: ErrorCode;\n\tpublic readonly context?: Record<string, unknown>;\n\tpublic readonly originalError?: Error;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tcode: ErrorCode = ErrorCodes.UNKNOWN_ERROR,\n\t\toptions?: { context?: Record<string, unknown>; originalError?: Error }\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'VisualizationError';\n\t\tthis.code = code;\n\t\tthis.context = options?.context;\n\t\tthis.originalError = options?.originalError;\n\t\tif (options?.originalError) {\n\t\t\t(this as { cause?: unknown }).cause = options.originalError;\n\t\t}\n\t}\n}\n","/**\n * Logging facility for the visualization package. Deliberately local rather than imported from\n * `@selvajs/compute` (logging isn't a compute concern). Mirrors compute's logger shape so a host\n * wanting one sink for both can call `setLogger(computeLogger.getLogger())`.\n */\n\nimport { VisualizationError, ErrorCodes } from './errors.js';\n\nexport interface Logger {\n\tdebug(message: string, ...args: unknown[]): void;\n\tinfo(message: string, ...args: unknown[]): void;\n\twarn(message: string, ...args: unknown[]): void;\n\terror(message: string, ...args: unknown[]): void;\n}\n\nclass NoOpLogger implements Logger {\n\tdebug(): void {}\n\tinfo(): void {}\n\twarn(): void {}\n\terror(): void {}\n}\n\nclass ConsoleLogger implements Logger {\n\tdebug(message: string, ...args: unknown[]): void {\n\t\t// eslint-disable-next-line no-console -- this class exists to be the console sink\n\t\tconsole.debug(message, ...args);\n\t}\n\n\tinfo(message: string, ...args: unknown[]): void {\n\t\tconsole.info(message, ...args);\n\t}\n\n\twarn(message: string, ...args: unknown[]): void {\n\t\tconsole.warn(message, ...args);\n\t}\n\n\terror(message: string, ...args: unknown[]): void {\n\t\tconsole.error(message, ...args);\n\t}\n}\n\n// Defaults to no-op: a library that writes to the console uninvited is a nuisance in a host with\n// its own logging. Opt in with setLogger or enableDebugLogging.\nlet internalLogger: Logger = new NoOpLogger();\n\n// Call per-use rather than caching — the sink is settable at any time.\nexport function getLogger(): Logger {\n\treturn internalLogger;\n}\n\n// Validates logger shape up front: failing here beats a confusing \"getLogger().debug is not a\n// function\" at some later, unrelated call site.\nexport function setLogger(logger: Logger | Console | null): void {\n\tif (logger === null) {\n\t\tinternalLogger = new NoOpLogger();\n\t\treturn;\n\t}\n\n\tconst missing = (['debug', 'info', 'warn', 'error'] as const).filter(\n\t\t(method) => typeof (logger as unknown as Record<string, unknown>)[method] !== 'function'\n\t);\n\tif (missing.length > 0) {\n\t\tthrow new VisualizationError(\n\t\t\t`Logger is missing required method(s): ${missing.join(', ')}. A logger must implement debug, info, warn and error.`,\n\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t{ context: { missingMethods: missing } }\n\t\t);\n\t}\n\n\tinternalLogger = logger as Logger;\n}\n\nexport function enableDebugLogging(): void {\n\tsetLogger(new ConsoleLogger());\n}\n","import type * as THREE from 'three';\n\n// ============================================================================\n// LOOKS\n// ============================================================================\n\n/**\n * `LOOKS` is the source of truth ({@link Look} derives from it) so consumers (e.g. a style picker)\n * can iterate instead of hardcoding names. 'technical' is first because it's the default.\n */\nexport const LOOKS = ['technical', 'studio', 'showcase'] as const;\n\nexport type Look = (typeof LOOKS)[number];\n\n/**\n * The lighting/material dials a {@link Look} sets, shared by construction-time defaults and runtime\n * `setLook`. `envMapIntensity`/`cullBackfaces` are parse-time material choices (see\n * `materialAppearanceForLook`); the rest apply to the live scene. Never carries edges or grid —\n * those are independent overlays.\n */\nexport type LookPreset = {\n\ttoneMapping: THREE.ToneMapping;\n\ttoneMappingExposure: number;\n\tenvMapIntensity: number;\n\t/** Multiplier on the HDR's IBL (`scene.environmentIntensity`). */\n\tenvironmentIntensity: number;\n\themisphereIntensity: number;\n\tambientIntensity: number;\n\tcullBackfaces: boolean;\n\tambientOcclusion: boolean;\n};\n\n/**\n * How compute meshes read visually. Lives in `shared/` (not with the parser) so the render layer can\n * read a look's material dials via `materialAppearanceForLook` without importing upward into `parse/`.\n */\nexport interface MaterialAppearanceOptions {\n\t/** Default 1 (three.js's own material default) when omitted. */\n\tenvMapIntensity?: number;\n\t/**\n\t * `THREE.FrontSide` instead of `THREE.DoubleSide` — crisper silhouette on closed solids, but open\n\t * surfaces (which Rhino also emits) vanish when viewed from behind. Default false (DoubleSide) to\n\t * stay safe for surface geometry.\n\t */\n\tcullBackfaces?: boolean;\n}\n","import * as THREE from 'three';\n\nimport type { Look, LookPreset, MaterialAppearanceOptions } from './types.js';\n\n/** The look applied when the caller passes no `look` option. */\nexport const DEFAULT_LOOK: Look = 'technical';\n\n/**\n * Single source of truth for both `applyDefaults` (construction) and `setLook` (runtime), so the two\n * can't drift.\n *\n * `ambientOcclusion: false` on every look: GTAO is a heavy full-screen pass, so it stays opt-in\n * (`render.ambientOcclusion` or `setAmbientOcclusion(true)`) rather than costing every viewer 60fps.\n */\nexport const LOOK_PRESETS: Record<Look, LookPreset> = {\n\t// Fills shadows in (vs. showcase, which lets them fall off). IBL/fill kept modest so\n\t// env+hemisphere+ambient don't triple-stack into an ACES-washed look.\n\tstudio: {\n\t\ttoneMapping: THREE.ACESFilmicToneMapping,\n\t\ttoneMappingExposure: 1,\n\t\tenvMapIntensity: 1.0,\n\t\tenvironmentIntensity: 1.0,\n\t\themisphereIntensity: 0.75,\n\t\tambientIntensity: 0.4,\n\t\tcullBackfaces: false,\n\t\tambientOcclusion: false\n\t},\n\t// Flat ambient kept low — a full flat ambient flattens shading toward milky grey regardless of\n\t// face orientation — with IBL near full and a little hemisphere fill for under-facing surfaces.\n\ttechnical: {\n\t\ttoneMapping: THREE.NeutralToneMapping,\n\t\ttoneMappingExposure: 1,\n\t\tenvMapIntensity: 0.9,\n\t\tenvironmentIntensity: 1,\n\t\themisphereIntensity: 0.35,\n\t\tambientIntensity: 0.25,\n\t\tcullBackfaces: false,\n\t\tambientOcclusion: false\n\t},\n\t// Fill pulled DOWN (low ambient/hemisphere) so light-to-shadow falloff stays strong for a\n\t// dramatic hero shot, unlike `studio` which fills shadows in.\n\tshowcase: {\n\t\ttoneMapping: THREE.ACESFilmicToneMapping,\n\t\ttoneMappingExposure: 1.15,\n\t\tenvMapIntensity: 1.4,\n\t\tenvironmentIntensity: 1.25,\n\t\themisphereIntensity: 0.35,\n\t\tambientIntensity: 0.15,\n\t\tcullBackfaces: false,\n\t\tambientOcclusion: false\n\t}\n};\n\n/** Baked at parse time (not toggleable at runtime). */\nexport function materialAppearanceForLook(look: Look): MaterialAppearanceOptions {\n\tconst preset = LOOK_PRESETS[look];\n\treturn {\n\t\tenvMapIntensity: preset.envMapIntensity,\n\t\tcullBackfaces: preset.cullBackfaces\n\t};\n}\n","// Copied (not imported) from `@selvajs/compute`'s `decodeBase64ToBinary` to avoid depending on the\n// Rhino.Compute client for ~20 stable lines; keep the two in sync by hand.\n\nimport { VisualizationError, ErrorCodes } from './errors.js';\n\nfunction getNodeBuffer(): typeof Buffer | undefined {\n\tconst buf = (globalThis as { Buffer?: typeof Buffer }).Buffer;\n\treturn typeof buf === 'function' ? buf : undefined;\n}\n\n/**\n * @throws {VisualizationError} `ENCODING_ERROR` if invalid, or `INVALID_STATE` if no decoder is\n * available in this environment.\n */\nexport function decodeBase64ToBinary(base64File: string): Uint8Array {\n\t// Forgiving-base64: strip whitespace, then drop trailing padding only where length % 4 allows it.\n\tlet data = base64File.replace(/[\\t\\n\\f\\r ]/g, '');\n\tif (data.length % 4 === 0) data = data.replace(/={1,2}$/, '');\n\tif (data.length % 4 === 1 || !/^[A-Za-z0-9+/]*$/.test(data)) {\n\t\tthrow new VisualizationError('Invalid base64 input.', ErrorCodes.ENCODING_ERROR, {\n\t\t\tcontext: { inputLength: base64File.length }\n\t\t});\n\t}\n\n\t// Prefer Buffer in Node: faster, and avoids the atob + charCodeAt latin-1 detour.\n\tconst Buffer = getNodeBuffer();\n\tif (Buffer) {\n\t\t// Copy out of the Buffer — small Buffer.from results are views over Node's shared 8 KiB pool\n\t\t// slab, so returning one would retain the whole slab and leak unrelated pooled bytes to any\n\t\t// consumer touching `.buffer` (structuredClone, postMessage transfer, etc).\n\t\treturn new Uint8Array(Buffer.from(data, 'base64'));\n\t}\n\tif (typeof globalThis.atob === 'function') {\n\t\tconst binary = globalThis.atob(data);\n\t\tconst bytes = new Uint8Array(binary.length);\n\t\tfor (let i = 0; i < binary.length; i++) {\n\t\t\tbytes[i] = binary.charCodeAt(i) & 0xff;\n\t\t}\n\t\treturn bytes;\n\t}\n\n\tthrow new VisualizationError(\n\t\t'Base64 decoding not supported in this environment.',\n\t\tErrorCodes.INVALID_STATE,\n\t\t{ context: { environmentInfo: 'atob or Buffer not available' } }\n\t);\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from './logger.js';\n\n// Object/color utilities shared by `parse/` and `render/`. Live here (not in render's\n// three-helpers) because `parse/` needs them and must never import upward from `render/`.\n\nexport function parseColor(colorString: string): THREE.Color {\n\tif (!colorString || typeof colorString !== 'string') {\n\t\tgetLogger().warn(`Invalid color input: ${colorString}, using white`);\n\t\treturn new THREE.Color(0xffffff);\n\t}\n\n\tconst trimmed = colorString.trim();\n\n\tif (/^#?[0-9A-Fa-f]{6}$/.test(trimmed)) {\n\t\ttry {\n\t\t\tconst hex = trimmed.startsWith('#') ? trimmed : `#${trimmed}`;\n\t\t\treturn new THREE.Color(hex);\n\t\t} catch {\n\t\t\tgetLogger().warn(`Invalid hex color: ${colorString}, using white`);\n\t\t\treturn new THREE.Color(0xffffff);\n\t\t}\n\t}\n\n\tif (trimmed.includes(',')) {\n\t\tconst rgb = trimmed.split(',').map((c) => parseInt(c.trim(), 10));\n\t\tif (rgb.length === 3 && rgb.every((n) => !isNaN(n) && n >= 0 && n <= 255)) {\n\t\t\treturn new THREE.Color(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255);\n\t\t}\n\t}\n\n\t// `new THREE.Color(name)` never throws on unknown names (it logs its own warning and leaves the\n\t// color white), so validate against three's CSS name table instead.\n\tconst named = trimmed.toLowerCase();\n\tif (named in THREE.Color.NAMES) {\n\t\treturn new THREE.Color(THREE.Color.NAMES[named as keyof typeof THREE.Color.NAMES]);\n\t}\n\n\tgetLogger().warn(`Invalid color string: ${colorString}, using white`);\n\treturn new THREE.Color(0xffffff);\n}\n\n/** Defaults to `z` (Rhino's up axis); pass an explicit axis when the scene uses a different `sceneUp`. */\nexport function applyOffset(\n\tmeshes: THREE.Object3D[],\n\toffset: number,\n\taxis: 'x' | 'y' | 'z' = 'z'\n): void {\n\tmeshes.forEach((mesh) => {\n\t\tmesh.position[axis] -= offset;\n\t});\n}\n\nexport function computeCombinedBoundingBox(meshes: THREE.Object3D[]): THREE.Box3 {\n\tconst combinedBoundingBox = new THREE.Box3();\n\tif (meshes.length === 0) return combinedBoundingBox;\n\tmeshes.forEach((mesh) => {\n\t\tmesh.updateMatrixWorld(true);\n\t\tconst bbox = new THREE.Box3().setFromObject(mesh);\n\t\tcombinedBoundingBox.union(bbox);\n\t});\n\treturn combinedBoundingBox;\n}\n","import type * as THREE from 'three';\n\n// ============================================================================\n// GPU resource ownership — the single rule every disposal path obeys\n// ============================================================================\n\n/**\n * Who may free a GPU resource, in one place. A resource has exactly one owner: a **cache** (frees\n * on eviction, nobody else ever), a **module singleton** (nobody ever frees it), or else the\n * **scene** (freed on teardown). Every disposal path asks {@link canDisposeGeometry} /\n * {@link canDisposeMaterial} / {@link canDisposeTexture} and disposes only if true — a new cache\n * adds a claim here, not an audit of every walker.\n */\n\n/**\n * `geometry.userData` tag set by the cross-solve geometry cache (`parse/webdisplay/geometry-cache.ts`).\n * Exported only so the cache can set/clear it; disposal paths should use {@link canDisposeGeometry}.\n */\nexport const CACHED_GEOMETRY_USERDATA_FLAG = 'selvaGeometryCache';\n\n/**\n * `texture.userData` tag for a texture owned by the cross-solve texture cache\n * (`parse/webdisplay/texture-cache.ts`). A cached texture is assigned directly onto materials\n * (`material.map = cached`) and shared across every material using that URL — without this flag the\n * first material sweep to reach one would free a texture the cache still holds and serves elsewhere.\n */\nexport const CACHED_TEXTURE_USERDATA_FLAG = 'selvaTextureCache';\n\n// Module-scope singletons from `render/three-materials.ts`, shared across meshes and solves —\n// disposing one would free textures still in use and force a recompile. Held here (not imported\n// there) to avoid an import cycle.\nconst protectedMaterials = new Set<THREE.Material>();\n\n/** Register materials as never-disposable. Called once by `render/three-materials.ts`. */\nexport function protectMaterials(materials: Iterable<THREE.Material>): void {\n\tfor (const material of materials) protectedMaterials.add(material);\n}\n\n/** True when `geometry` is scene-owned (not held by the geometry cache). */\nexport function canDisposeGeometry(geometry: THREE.BufferGeometry | undefined): boolean {\n\tif (!geometry) return false;\n\treturn !geometry.userData?.[CACHED_GEOMETRY_USERDATA_FLAG];\n}\n\n/** True when `texture` is scene-owned (not held by the texture cache). */\nexport function canDisposeTexture(texture: THREE.Texture | undefined): boolean {\n\tif (!texture) return false;\n\treturn !texture.userData?.[CACHED_TEXTURE_USERDATA_FLAG];\n}\n\n/** True when `material` is scene-owned (not one of the protected singletons). */\nexport function canDisposeMaterial(material: THREE.Material | undefined): boolean {\n\tif (!material) return false;\n\treturn !protectedMaterials.has(material);\n}\n\n// ============================================================================\n// Cache registry — so teardown frees every cache without knowing they exist\n// ============================================================================\n\nconst cacheReleases = new Set<() => void>();\n\n/**\n * Register a cache's \"free everything you hold\" function, called once at the cache's module init.\n * This is how `render/`'s teardown frees `parse/`'s caches without importing `parse/`.\n */\nexport function registerCacheRelease(release: () => void): void {\n\tcacheReleases.add(release);\n}\n\n/**\n * Free every registered cache. One throwing cache does not stop the rest.\n *\n * Prefer {@link retainCaches} in a viewer — caches are shared across viewers, so calling this while\n * another viewer is live discards entries it's still using (they repopulate, but it's wasteful).\n */\nexport function releaseAllCaches(): void {\n\tfor (const release of cacheReleases) {\n\t\ttry {\n\t\t\trelease();\n\t\t} catch {\n\t\t\t// Teardown has no sink to report to — the context is already gone.\n\t\t}\n\t}\n}\n\nlet cacheRetainCount = 0;\n\n/**\n * Claim the shared caches for the lifetime of one viewer. Returns a release function; refcounted\n * because two viewers on one page (e.g. main view + thumbnail) share the same cache entries, and\n * the first to unmount must not wipe the second's working set — caches only actually free once the\n * last viewer releases them.\n */\nexport function retainCaches(): () => void {\n\tcacheRetainCount++;\n\tlet released = false;\n\n\treturn () => {\n\t\t// Idempotent: hosts double-invoke teardown (React StrictMode, double unmount); a second call\n\t\t// must not decrement the count on another viewer's behalf.\n\t\tif (released) return;\n\t\treleased = true;\n\t\tcacheRetainCount--;\n\t\tif (cacheRetainCount <= 0) {\n\t\t\tcacheRetainCount = 0;\n\t\t\treleaseAllCaches();\n\t\t}\n\t};\n}\n","// ============================================================================\n// GPU capabilities — published by the renderer, read by whoever needs them\n// ============================================================================\n\n/**\n * Lets a renderer publish GPU capabilities (max anisotropy) without `render/` and `parse/` importing\n * each other: the renderer publishes on init, interested parties subscribe.\n */\n\ntype AnisotropyObserver = (value: number) => void;\n\nconst observers = new Set<AnisotropyObserver>();\n\n// Module load order between layers isn't guaranteed, so a subscriber arriving after init still\n// needs the current value rather than waiting for a second renderer to publish.\nlet maxAnisotropy = 1;\n\nexport function publishMaxAnisotropy(value: number): void {\n\tconst next = Math.max(1, value);\n\tif (next === maxAnisotropy) return;\n\tmaxAnisotropy = next;\n\tfor (const observe of observers) observe(next);\n}\n\n/** Fires immediately with the current value (1 until a renderer has reported), then on every change. */\nexport function observeMaxAnisotropy(observe: AnisotropyObserver): () => void {\n\tobservers.add(observe);\n\tobserve(maxAnisotropy);\n\treturn () => observers.delete(observe);\n}\n","import * as THREE from 'three';\n\nimport { canDisposeGeometry, canDisposeMaterial, canDisposeTexture } from './gpu-ownership.js';\n\n// ============================================================================\n// The disposal walkers — every teardown path in the package goes through these\n// ============================================================================\n\n/**\n * Free a material and its textures, respecting ownership (`./gpu-ownership.ts`). Walks own\n * enumerable properties only (`for...in` would also walk the prototype chain).\n */\nexport function disposeMaterial(material: THREE.Material): void {\n\tif (!canDisposeMaterial(material)) return;\n\n\tfor (const value of Object.values(material)) {\n\t\tif (value instanceof THREE.Texture && canDisposeTexture(value)) {\n\t\t\tvalue.dispose();\n\t\t}\n\t}\n\tmaterial.dispose();\n}\n\nfunction disposeMaterials(material: THREE.Material | THREE.Material[] | undefined): void {\n\tif (!material) return;\n\tif (Array.isArray(material)) material.forEach(disposeMaterial);\n\telse disposeMaterial(material);\n}\n\nexport interface DisposeOptions {\n\t/** Free materials and their textures too. Default true; the solve memo passes false. */\n\tmaterials?: boolean;\n\t/** Called for each geometry before disposal, so a subsystem can free derived resources keyed on it (e.g. edge caches). */\n\tonGeometry?: (geometry: THREE.BufferGeometry) => void;\n}\n\n/**\n * Free the GPU resources of an object subtree, respecting ownership. **The only traversal that\n * should dispose scene content** — a second disposing `traverse` risks missing an ownership guard.\n */\nexport function disposeObjectTree(root: THREE.Object3D, options: DisposeOptions = {}): void {\n\tconst { materials = true, onGeometry } = options;\n\n\troot.traverse((object) => {\n\t\tconst renderable = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tif (!renderable.geometry && !renderable.material) return;\n\n\t\tif (renderable.geometry) {\n\t\t\tonGeometry?.(renderable.geometry);\n\t\t\tif (canDisposeGeometry(renderable.geometry)) renderable.geometry.dispose();\n\t\t}\n\n\t\tif (materials) disposeMaterials(renderable.material);\n\t});\n}\n"],"mappings":"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","names":["ErrorCodes","VisualizationError","message","code","options","__publicField","NoOpLogger","ConsoleLogger","message","args","internalLogger","getLogger","setLogger","logger","missing","method","VisualizationError","ErrorCodes","enableDebugLogging","LOOKS","THREE","DEFAULT_LOOK","LOOK_PRESETS","materialAppearanceForLook","look","preset","getNodeBuffer","buf","decodeBase64ToBinary","base64File","data","VisualizationError","ErrorCodes","Buffer","binary","bytes","THREE","parseColor","colorString","getLogger","trimmed","hex","rgb","c","n","named","applyOffset","meshes","offset","axis","mesh","computeCombinedBoundingBox","combinedBoundingBox","bbox","CACHED_GEOMETRY_USERDATA_FLAG","CACHED_TEXTURE_USERDATA_FLAG","protectedMaterials","canDisposeGeometry","geometry","CACHED_GEOMETRY_USERDATA_FLAG","canDisposeTexture","texture","CACHED_TEXTURE_USERDATA_FLAG","canDisposeMaterial","material","protectedMaterials","cacheReleases","registerCacheRelease","release","releaseAllCaches","cacheRetainCount","retainCaches","released","observers","maxAnisotropy","publishMaxAnisotropy","value","next","observe","observeMaxAnisotropy","THREE","disposeMaterial","material","canDisposeMaterial","value","canDisposeTexture","disposeMaterials","disposeObjectTree","root","options","materials","onGeometry","object","renderable","canDisposeGeometry"]}
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["c:\\Users\\felix\\coding\\selva\\packages\\visualization\\dist\\index.cjs"],"names":[],"mappings":"AAAA","file":"C:\\Users\\felix\\coding\\selva\\packages\\visualization\\dist\\index.cjs"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* `LOOKS` is the source of truth ({@link Look} derives from it) so consumers (e.g. a style picker)
|
|
5
|
-
* can iterate instead of hardcoding names. 'technical' is first because it's the default.
|
|
6
|
-
*/
|
|
7
|
-
declare const LOOKS: readonly ["technical", "studio", "showcase"];
|
|
8
|
-
type Look = (typeof LOOKS)[number];
|
|
9
|
-
/**
|
|
10
|
-
* The lighting/material dials a {@link Look} sets, shared by construction-time defaults and runtime
|
|
11
|
-
* `setLook`. `envMapIntensity`/`cullBackfaces` are parse-time material choices (see
|
|
12
|
-
* `materialAppearanceForLook`); the rest apply to the live scene. Never carries edges or grid —
|
|
13
|
-
* those are independent overlays.
|
|
14
|
-
*/
|
|
15
|
-
type LookPreset = {
|
|
16
|
-
toneMapping: THREE.ToneMapping;
|
|
17
|
-
toneMappingExposure: number;
|
|
18
|
-
envMapIntensity: number;
|
|
19
|
-
/** Multiplier on the HDR's IBL (`scene.environmentIntensity`). */
|
|
20
|
-
environmentIntensity: number;
|
|
21
|
-
hemisphereIntensity: number;
|
|
22
|
-
ambientIntensity: number;
|
|
23
|
-
cullBackfaces: boolean;
|
|
24
|
-
ambientOcclusion: boolean;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* How compute meshes read visually. Lives in `shared/` (not with the parser) so the render layer can
|
|
28
|
-
* read a look's material dials via `materialAppearanceForLook` without importing upward into `parse/`.
|
|
29
|
-
*/
|
|
30
|
-
interface MaterialAppearanceOptions {
|
|
31
|
-
/** Default 1 (three.js's own material default) when omitted. */
|
|
32
|
-
envMapIntensity?: number;
|
|
33
|
-
/**
|
|
34
|
-
* `THREE.FrontSide` instead of `THREE.DoubleSide` — crisper silhouette on closed solids, but open
|
|
35
|
-
* surfaces (which Rhino also emits) vanish when viewed from behind. Default false (DoubleSide) to
|
|
36
|
-
* stay safe for surface geometry.
|
|
37
|
-
*/
|
|
38
|
-
cullBackfaces?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export { type Look as L, type MaterialAppearanceOptions as M, type LookPreset as a, LOOKS as b };
|
package/dist/types-CdF9R3qA.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* `LOOKS` is the source of truth ({@link Look} derives from it) so consumers (e.g. a style picker)
|
|
5
|
-
* can iterate instead of hardcoding names. 'technical' is first because it's the default.
|
|
6
|
-
*/
|
|
7
|
-
declare const LOOKS: readonly ["technical", "studio", "showcase"];
|
|
8
|
-
type Look = (typeof LOOKS)[number];
|
|
9
|
-
/**
|
|
10
|
-
* The lighting/material dials a {@link Look} sets, shared by construction-time defaults and runtime
|
|
11
|
-
* `setLook`. `envMapIntensity`/`cullBackfaces` are parse-time material choices (see
|
|
12
|
-
* `materialAppearanceForLook`); the rest apply to the live scene. Never carries edges or grid —
|
|
13
|
-
* those are independent overlays.
|
|
14
|
-
*/
|
|
15
|
-
type LookPreset = {
|
|
16
|
-
toneMapping: THREE.ToneMapping;
|
|
17
|
-
toneMappingExposure: number;
|
|
18
|
-
envMapIntensity: number;
|
|
19
|
-
/** Multiplier on the HDR's IBL (`scene.environmentIntensity`). */
|
|
20
|
-
environmentIntensity: number;
|
|
21
|
-
hemisphereIntensity: number;
|
|
22
|
-
ambientIntensity: number;
|
|
23
|
-
cullBackfaces: boolean;
|
|
24
|
-
ambientOcclusion: boolean;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* How compute meshes read visually. Lives in `shared/` (not with the parser) so the render layer can
|
|
28
|
-
* read a look's material dials via `materialAppearanceForLook` without importing upward into `parse/`.
|
|
29
|
-
*/
|
|
30
|
-
interface MaterialAppearanceOptions {
|
|
31
|
-
/** Default 1 (three.js's own material default) when omitted. */
|
|
32
|
-
envMapIntensity?: number;
|
|
33
|
-
/**
|
|
34
|
-
* `THREE.FrontSide` instead of `THREE.DoubleSide` — crisper silhouette on closed solids, but open
|
|
35
|
-
* surfaces (which Rhino also emits) vanish when viewed from behind. Default false (DoubleSide) to
|
|
36
|
-
* stay safe for surface geometry.
|
|
37
|
-
*/
|
|
38
|
-
cullBackfaces?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export { type Look as L, type MaterialAppearanceOptions as M, type LookPreset as a, LOOKS as b };
|