@selvajs/visualization 1.0.0-beta.4 → 1.0.1
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/dist/parse.cjs +1 -1
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +1 -1
- package/dist/parse.js.map +1 -1
- package/dist/scene.cjs +1 -1
- package/dist/scene.cjs.map +1 -1
- package/dist/scene.js +1 -1
- package/dist/scene.js.map +1 -1
- package/package.json +7 -7
package/dist/parse.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.js","names":["unhandled"],"sources":["../src/shared/encoding.ts","../src/parse/mesh-policy.ts","../src/parse/display-items/items/appearance.ts","../src/parse/display-items/items/curves.ts","../src/parse/display-items/items/points.ts","../src/parse/display-items/display-items-parser.ts","../src/parse/webdisplay/binary/header.ts","../src/parse/webdisplay/binary/geometry.ts","../src/parse/webdisplay/binary/textures.ts","../src/parse/webdisplay/binary-parser.ts","../src/parse/webdisplay/mesh-assembly.ts","../src/parse/webdisplay/batch/assembly-worker.ts","../src/parse/webdisplay/apply-texture.ts","../src/parse/webdisplay/batch/materials.ts","../src/parse/webdisplay/batch/metadata.ts","../src/parse/webdisplay/batch/merge.ts","../src/parse/webdisplay/batch-parser.ts","../src/parse/webdisplay/webdisplay-parser.ts"],"sourcesContent":["// 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","// Mesh ownership policy for `@selvajs/solve`'s result memo: `SolveResult<TMesh>` is opaque to the\n// memo, so clone/release are injected here instead. The viewer disposes whatever it last rendered\n// (`clearScene`), so a memo handing out live references would serve a disposed object on the next\n// hit — `clone` copies geometry explicitly (`Object3D.clone()` shares it by reference) but leaves\n// materials shared, since `clearScene` already spares `SHARED_MATERIALS` singletons and recompiling\n// per-mesh materials as shaders is expensive.\n\nimport * as THREE from 'three';\n\nimport { disposeObjectTree } from '../shared/index.js';\n\nexport function cloneSceneObjects(meshes: THREE.Object3D[]): THREE.Object3D[] {\n\treturn meshes.map((root) => {\n\t\tconst copy = root.clone(true);\n\t\tconst sources: THREE.Object3D[] = [];\n\t\troot.traverse((child) => sources.push(child));\n\t\tlet i = 0;\n\t\tcopy.traverse((child) => {\n\t\t\tconst source = sources[i++] as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tconst target = child as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tif (!source.geometry) return;\n\t\t\ttarget.geometry = source.geometry.clone();\n\t\t});\n\t\treturn copy;\n\t});\n}\n\n/** Skips materials — the memo never owns those; see the file header. */\nexport function releaseSceneObjects(meshes: THREE.Object3D[]): void {\n\tmeshes.forEach((root) => disposeObjectTree(root, { materials: false }));\n}\n\n/** Structurally, not nominally, typed as `@selvajs/solve/client`'s `MeshPolicy<THREE.Object3D>` — avoids a dependency on solve. */\nexport const meshPolicy: {\n\tclone(meshes: THREE.Object3D[]): THREE.Object3D[];\n\trelease(meshes: THREE.Object3D[]): void;\n} = {\n\tclone: cloneSceneObjects,\n\trelease: releaseSceneObjects\n};\n","import * as THREE from 'three';\n\nexport const DEFAULT_COLOR = '#ffffff';\n\n/** Opacity < 1 flips `transparent` on. */\nexport function materialParams(\n\tcolor: string | undefined,\n\topacity: number | undefined\n): { color: THREE.Color; transparent: boolean; opacity: number } {\n\tconst resolved = opacity ?? 1;\n\treturn {\n\t\tcolor: new THREE.Color(color ?? DEFAULT_COLOR),\n\t\ttransparent: resolved < 1,\n\t\topacity: resolved\n\t};\n}\n","import { Line2 } from 'three/addons/lines/Line2.js';\nimport { LineGeometry } from 'three/addons/lines/LineGeometry.js';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\n\nimport { ErrorCodes, VisualizationError } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayCurve } from '../types';\n\nconst DEFAULT_LINE_WIDTH = 2;\n\n/** Two vertices — the shortest renderable polyline. */\nconst MIN_POSITIONS = 6;\n\n/**\n * Curves arrive tessellated: the backend sends `points`, this builds the line. Nothing decodes\n * geometry in the browser.\n *\n * Uses `Line2`/`LineMaterial` instead of `THREE.Line`: plain `THREE.Line` is hard-capped at 1px on\n * every major GPU backend, so `item.width` would go unhonoured. `Line2.onBeforeRender` sets\n * `LineMaterial`'s required `resolution`, so no renderer reference is needed here.\n *\n * @throws VisualizationError when the item has no `points` — see {@link curvePositions}.\n */\nexport function buildCurveLine(item: DisplayCurve): Line2 | null {\n\tconst positions = curvePositions(item);\n\tif (!positions) return null;\n\n\tconst geometry = new LineGeometry();\n\tgeometry.setPositions(positions);\n\n\t// @types/three's LineMaterial omits `linewidth`/`transparent`/`opacity` though all exist at runtime.\n\tconst params = materialParams(item.color, item.opacity);\n\tconst material = new LineMaterial({ color: params.color });\n\tconst styled = material as LineMaterial & {\n\t\tlinewidth: number;\n\t\ttransparent: boolean;\n\t\topacity: number;\n\t};\n\tstyled.linewidth = item.width ?? DEFAULT_LINE_WIDTH; // CSS px (worldUnits defaults false)\n\tstyled.transparent = params.transparent;\n\tstyled.opacity = params.opacity;\n\n\tconst line = new Line2(geometry, material);\n\tline.computeLineDistances();\n\tline.name = item.name;\n\tline.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'curve',\n\t\tmetadata: item.metadata\n\t};\n\treturn line;\n}\n\n/**\n * Flat `[x,y,z, …]`, or null for a degenerate curve — one of those can't abort the batch.\n *\n * A curve with no `points` **throws** instead. It means the definition was solved by a Display\n * component predating backend tessellation, which is a stale definition rather than one bad curve:\n * skipping would render a scene silently missing geometry, indistinguishable from a definition that\n * has no curves, with the fix nowhere in sight.\n */\nfunction curvePositions(item: DisplayCurve): number[] | null {\n\tif (!item.points) {\n\t\tthrow new VisualizationError(\n\t\t\t`Curve display item '${item.id}' has no tessellated points. It was produced by an ` +\n\t\t\t\t'outdated Display component — upgrade it in Grasshopper (Solution → Upgrade obsolete ' +\n\t\t\t\t'components) and re-save the definition.',\n\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t{ context: { itemId: item.id, kind: item.kind } }\n\t\t);\n\t}\n\n\treturn item.points.length >= MIN_POSITIONS ? item.points : null;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayPoint } from '../types';\n\nexport function buildPoint(item: DisplayPoint): THREE.Points | null {\n\t// `position` comes off the wire — don't trust the declared type without validating.\n\tconst { position } = item as { position?: { X?: unknown; Y?: unknown; Z?: unknown } };\n\tif (\n\t\t!position ||\n\t\ttypeof position.X !== 'number' ||\n\t\t!Number.isFinite(position.X) ||\n\t\ttypeof position.Y !== 'number' ||\n\t\t!Number.isFinite(position.Y) ||\n\t\ttypeof position.Z !== 'number' ||\n\t\t!Number.isFinite(position.Z)\n\t) {\n\t\tgetLogger().warn(\n\t\t\t`Skipping point display item with missing or non-finite position (id: ${String(item.id)}).`\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute(\n\t\t'position',\n\t\tnew THREE.Float32BufferAttribute([position.X, position.Y, position.Z], 3)\n\t);\n\n\tconst material = new THREE.PointsMaterial({\n\t\t...materialParams(item.color, item.opacity),\n\t\tsize: 6,\n\t\tsizeAttenuation: false\n\t});\n\n\tconst points = new THREE.Points(geometry, material);\n\tpoints.name = item.name;\n\tpoints.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'point',\n\t\tmetadata: item.metadata\n\t};\n\treturn points;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { buildCurveLine } from './items/curves.js';\nimport { buildPoint } from './items/points.js';\n\nimport type { DisplayItem } from './types';\n\n/**\n * Builds THREE objects for the batch's non-mesh items.\n *\n * @throws VisualizationError when a curve predates backend tessellation, so a stale definition\n * surfaces as an actionable error instead of a scene quietly missing its curves. Every other\n * unrenderable item is logged and skipped.\n */\nexport function parseDisplayItems(items: DisplayItem[] | undefined): THREE.Object3D[] {\n\tif (!items || items.length === 0) return [];\n\n\tconst objects: THREE.Object3D[] = [];\n\n\tfor (const item of items) {\n\t\tswitch (item.kind) {\n\t\t\tcase 'curve': {\n\t\t\t\tconst line = buildCurveLine(item);\n\t\t\t\tif (line) objects.push(line);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'point': {\n\t\t\t\tconst point = buildPoint(item);\n\t\t\t\tif (point) objects.push(point);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// Forces a compile error if a new DisplayItem kind is added without a case above.\n\t\t\t\tconst unhandled: never = item;\n\t\t\t\tconst unknown = unhandled as { kind?: string };\n\t\t\t\tgetLogger().warn(`Skipping unknown display item kind: ${String(unknown.kind)}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn objects;\n}\n","import type { MaterialGroup, SerializableMaterial } from '../types.js';\n\n// ============================================================================\n// WIRE FORMAT CONSTANTS\n// ============================================================================\n\n/** \"SLVA\" little-endian — an uncompressed mesh blob. */\nexport const BINARY_MESH_MAGIC = 0x41564c53;\n/**\n * \"SLVZ\" little-endian — an optional raw-DEFLATE container around a SLVA blob (applied by the\n * plugin when it shrinks the payload). Layout: `[4] magic=SLVZ | [4] uncompressedLen(u32) |\n * [N] raw-deflate stream of the SLVA blob`.\n */\nexport const COMPRESSED_MESH_MAGIC = 0x5a564c53;\n/**\n * Current writer version. v2 added FLAG_UINT16_INDICES; v3 added FLAG_DELTA_ENCODED.\n */\nexport const BINARY_MESH_VERSION = 3;\n/**\n * Oldest wire version this parser still decodes. Each version only added a flag bit, so the\n * flag-driven read path handles every older blob unchanged — needed since persisted/cached blobs\n * (saved `.gh` files, `.slvm`/`.dmf` mesh files, cached compute results) must stay decodable after upgrade.\n */\nexport const MIN_SUPPORTED_VERSION = 1;\n/** Bit 0 of the geometry flags word: 0 = int16 quantized, 1 = float32 raw. */\nexport const FLAG_FLOAT32 = 0x1;\n/** Bit 1 of the geometry flags word: 0 = uint32 indices, 1 = uint16 indices. */\nexport const FLAG_UINT16_INDICES = 0x2;\n/**\n * Bit 2 of the geometry flags word: int16 vertex components and indices are stored as wrapped\n * per-component deltas from their predecessor, zigzag-mapped to unsigned (float32 vertices are\n * never filtered). Deltas of welded meshes concentrate near zero, so the SLVZ DEFLATE pass\n * compresses far better. Decoding reverses the filter with a running prefix sum.\n */\nexport const FLAG_DELTA_ENCODED = 0x4;\n/**\n * Bit 3: a UV chunk trails the index block. Layout: `uvFormat(u32: 0=uint16 quantized, 1=float32)\n * | uvOrigin(2×f64) | uvScale(2×f64) | data`, element count implied by vertexCount. Quantized UVs\n * reconstruct as `uv = origin + q * scale` (q unsigned in [0, 65535]), delta+zigzag filtered per\n * component (independent u/v predictors) iff FLAG_DELTA_ENCODED; float32 UVs are never filtered.\n * Absent flag = absent chunk, so untextured blobs are byte-identical to pre-chunk writers.\n */\nexport const FLAG_HAS_UVS = 0x8;\n/**\n * Bit 4: a vertex-color chunk trails the index block (after the UV chunk when both present).\n * Layout: `uint8 rgb[vertexCount*3]`, delta+zigzag filtered per channel (wrapped 8-bit, independent\n * r/g/b predictors) iff FLAG_DELTA_ENCODED.\n */\nexport const FLAG_HAS_VERTEX_COLORS = 0x10;\n\n/** uvFormat value inside the UV chunk: uint16 quantized. */\nexport const UV_FORMAT_UINT16 = 0;\n/** uvFormat value inside the UV chunk: raw float32. */\nexport const UV_FORMAT_FLOAT32 = 1;\n\nexport const HEADER_PREAMBLE_BYTES = 4 /* magic */ + 4 /* version */ + 4; /* metadataLen */\nexport const GEOMETRY_HEADER_BYTES =\n\t4 /* flags */ + 24 /* origin (3 x f64) */ + 24 /* scale (3 x f64) */ + 4; /* vertexCount */\n\n/**\n * Header fields use explicit-LE `DataView` reads, but the zero-copy geometry readers build\n * typed-array views in *host* byte order (every mainstream JS target is little-endian, and\n * per-element DataView reads would be far costlier on the hot geometry paths). This check makes\n * the assumption explicit: on a big-endian host the parser refuses to decode rather than return\n * byte-swapped garbage.\n */\nexport const HOST_IS_LITTLE_ENDIAN = new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;\n\n// ============================================================================\n// PARSED TYPES\n// ============================================================================\n\n/** Mesh-blob subset of `DisplayBatch` minus `compressedData` (circular — the blob can't embed itself). */\nexport interface BinaryMeshMetadata {\n\tmaterials: SerializableMaterial[];\n\tgroups: MaterialGroup[];\n\tsourceComponentId?: string;\n}\n\n/**\n * Result of parsing a binary mesh blob.\n *\n * `vertices`/`indices` hold absolute (unfiltered) values. For pre-v3 blobs they're zero-copy\n * typed-array views over the original `ArrayBuffer` — don't mutate the buffer, or call `.slice()`\n * to detach. Delta-encoded blobs (FLAG_DELTA_ENCODED) decode into freshly allocated arrays instead.\n *\n * `uvs`/`colors` are the optional trailing chunks (FLAG_HAS_UVS / FLAG_HAS_VERTEX_COLORS), null\n * when absent. UVs are dequantized to absolute Float32 (u,v per vertex) — ready for\n * `BufferAttribute(uvs, 2)`. Colors are raw r,g,b bytes per vertex, for a normalized\n * `BufferAttribute(colors, 3, true)`.\n */\nexport interface ParsedBinaryMeshBatch {\n\tmetadata: BinaryMeshMetadata;\n\tflags: number;\n\tvertices: Int16Array | Float32Array;\n\tindices: Uint16Array | Uint32Array;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\n// ============================================================================\n","import { inflateSync } from 'fflate';\n\nimport { decodeBase64ToBinary, VisualizationError, ErrorCodes } from '../../../shared/index.js';\n\nimport { COMPRESSED_MESH_MAGIC } from './header.js';\n\nexport function toUint8Array(input: ArrayBuffer | Uint8Array | string): Uint8Array {\n\tif (typeof input === 'string') {\n\t\treturn decodeBase64ToBinary(input);\n\t}\n\tif (input instanceof Uint8Array) {\n\t\treturn input;\n\t}\n\treturn new Uint8Array(input);\n}\n\n/**\n * If the blob is a SLVZ compressed container, inflate it back to the raw SLVA bytes; otherwise\n * return the input untouched. Detection is by the leading 4-byte magic, so an uncompressed SLVA\n * blob (or any pre-v3 payload) flows through unchanged.\n */\nexport function maybeDecompress(bytes: Uint8Array): Uint8Array {\n\tif (bytes.byteLength < 8) {\n\t\treturn bytes;\n\t}\n\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\tif (view.getUint32(0, true) !== COMPRESSED_MESH_MAGIC) {\n\t\treturn bytes;\n\t}\n\n\tconst uncompressedLen = view.getUint32(4, true);\n\tconst deflated = bytes.subarray(8);\n\n\t// Bound the wire-supplied length before allocating — a corrupt header could request ~4 GB.\n\t// DEFLATE won't expand past ~1000x.\n\tconst maxPlausibleLen = Math.max(deflated.byteLength * 1032 + 1024, 1 << 20);\n\tif (uncompressedLen > maxPlausibleLen) {\n\t\tthrow fail('SLVZ header declares an implausible uncompressed length', {\n\t\t\tuncompressedLen,\n\t\t\tdeflatedBytes: deflated.byteLength,\n\t\t\tmaxPlausibleLen\n\t\t});\n\t}\n\n\tlet out: Uint8Array;\n\ttry {\n\t\t// One byte of slack past the declared length: fflate trims its output to bytes actually\n\t\t// written, so a mismatched header lands off `uncompressedLen` either way — caught below\n\t\t// instead of silently decoding a zero-padded/truncated tail as geometry.\n\t\tout = inflateSync(deflated, { out: new Uint8Array(uncompressedLen + 1) });\n\t} catch (error) {\n\t\tthrow fail(\n\t\t\t`Failed to inflate SLVZ blob: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ uncompressedLen, deflatedBytes: deflated.byteLength }\n\t\t);\n\t}\n\n\tif (out.byteLength !== uncompressedLen) {\n\t\tthrow fail('SLVZ payload inflated to a different size than the header declares.', {\n\t\t\tdeclaredLen: uncompressedLen,\n\t\t\tactualLen: out.byteLength,\n\t\t\tdeflatedBytes: deflated.byteLength\n\t\t});\n\t}\n\n\treturn out;\n}\n\nexport function decodeUtf8(bytes: Uint8Array): string {\n\tif (typeof TextDecoder !== 'undefined') {\n\t\treturn new TextDecoder('utf-8').decode(bytes);\n\t}\n\t// Node fallback (Buffer is utf-8 by default).\n\tif (\n\t\ttypeof (globalThis as { Buffer?: { from(b: Uint8Array): { toString(enc: string): string } } })\n\t\t\t.Buffer !== 'undefined'\n\t) {\n\t\treturn (\n\t\t\tglobalThis as { Buffer: { from(b: Uint8Array): { toString(enc: string): string } } }\n\t\t).Buffer.from(bytes).toString('utf-8');\n\t}\n\tthrow new VisualizationError(\n\t\t'No UTF-8 decoder available in this environment.',\n\t\tErrorCodes.INVALID_STATE\n\t);\n}\n\nexport function readInt16Vertices(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Int16Array {\n\tif (count === 0) return new Int16Array(0);\n\tif (byteOffset % 2 === 0) {\n\t\treturn new Int16Array(buffer, byteOffset, count);\n\t}\n\t// Misaligned (rare — would require a wrapper Uint8Array with odd byteOffset).\n\tconst copy = new Uint8Array(count * 2);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 2));\n\treturn new Int16Array(copy.buffer);\n}\n\nexport function readFloat32Vertices(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Float32Array {\n\tif (count === 0) return new Float32Array(0);\n\tif (byteOffset % 4 === 0) {\n\t\treturn new Float32Array(buffer, byteOffset, count);\n\t}\n\tconst copy = new Uint8Array(count * 4);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 4));\n\treturn new Float32Array(copy.buffer);\n}\n\nexport function readUint16Array(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Uint16Array {\n\tif (count === 0) return new Uint16Array(0);\n\tif (byteOffset % 2 === 0) {\n\t\treturn new Uint16Array(buffer, byteOffset, count);\n\t}\n\tconst copy = new Uint8Array(count * 2);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 2));\n\treturn new Uint16Array(copy.buffer);\n}\n\nexport function readUint32Array(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Uint32Array {\n\tif (count === 0) return new Uint32Array(0);\n\tif (byteOffset % 4 === 0) {\n\t\treturn new Uint32Array(buffer, byteOffset, count);\n\t}\n\tconst copy = new Uint8Array(count * 4);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 4));\n\treturn new Uint32Array(copy.buffer);\n}\n\n/**\n * Rejects blobs whose index stream references vertices past `vertexCount`. Downstream mesh\n * assembly trusts indices arithmetically (rebasing, `subarray` slicing), so an out-of-range index\n * would otherwise corrupt geometry silently instead of failing the parse. A uint16 index stream\n * can't exceed a vertex count above 65535, so that case skips the scan.\n */\nexport function validateIndicesInRange(\n\tindices: Uint16Array | Uint32Array,\n\tvertexCount: number\n): void {\n\tif (indices.length === 0) return;\n\tif (indices instanceof Uint16Array && vertexCount > 0xffff) return;\n\tfor (let i = 0; i < indices.length; i++) {\n\t\tif (indices[i]! >= vertexCount) {\n\t\t\tthrow fail('Index out of range of vertexCount.', {\n\t\t\t\tindexPosition: i,\n\t\t\t\tindexValue: indices[i],\n\t\t\t\tvertexCount\n\t\t\t});\n\t\t}\n\t}\n}\n\n/** Inverse of the writer's zigzag map: 0,1,2,3 → 0,-1,1,-2. */\nexport function unzigzag(zz: number): number {\n\treturn (zz >>> 1) ^ -(zz & 1);\n}\n\n/**\n * Undoes the v3 delta filter on the quantized vertex stream: each component is a zigzag-mapped,\n * wrapped 16-bit difference from the previous vertex's same component (independent x/y/z running\n * sums). `(x << 16) >> 16` reproduces the writer's int16 wrapping.\n */\nexport function decodeDeltaVertices(zigzagged: Uint16Array): Int16Array {\n\tconst out = new Int16Array(zigzagged.length);\n\tlet px = 0;\n\tlet py = 0;\n\tlet pz = 0;\n\tfor (let i = 0; i < zigzagged.length; i += 3) {\n\t\tpx = ((px + unzigzag(zigzagged[i]!)) << 16) >> 16;\n\t\tpy = ((py + unzigzag(zigzagged[i + 1]!)) << 16) >> 16;\n\t\tpz = ((pz + unzigzag(zigzagged[i + 2]!)) << 16) >> 16;\n\t\tout[i] = px;\n\t\tout[i + 1] = py;\n\t\tout[i + 2] = pz;\n\t}\n\treturn out;\n}\n\nexport function decodeDeltaIndices16(zigzagged: Uint16Array): Uint16Array {\n\tconst out = new Uint16Array(zigzagged.length);\n\tlet prev = 0;\n\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\tprev = (prev + unzigzag(zigzagged[i]!)) & 0xffff;\n\t\tout[i] = prev;\n\t}\n\treturn out;\n}\n\nexport function decodeDeltaIndices32(zigzagged: Uint32Array): Uint32Array {\n\tconst out = new Uint32Array(zigzagged.length);\n\tlet prev = 0;\n\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\tprev = (prev + unzigzag(zigzagged[i]!)) >>> 0;\n\t\tout[i] = prev;\n\t}\n\treturn out;\n}\n\nexport function fail(message: string, context: Record<string, unknown>): VisualizationError {\n\treturn new VisualizationError(message, ErrorCodes.VALIDATION_ERROR, { context });\n}\n","import { UV_FORMAT_FLOAT32 } from './header.js';\nimport { fail, readFloat32Vertices, readUint16Array, unzigzag } from './geometry.js';\n\n/** Byte size of the UV chunk header: uvFormat(u32) + uvOrigin(2×f64) + uvScale(2×f64). */\nconst UV_CHUNK_HEADER_BYTES = 4 + 16 + 16;\n\n/**\n * Parses the trailing UV chunk into absolute Float32 u,v pairs. Quantized UVs reconstruct as\n * `origin + q * scale` (unsigned q), undoing the per-component delta+zigzag filter when set;\n * float32 UVs are copied out as-is (never filtered).\n */\nexport function parseUvChunk(\n\tbytes: Uint8Array,\n\tview: DataView,\n\toffset: number,\n\tvertexCount: number,\n\tdeltaEncoded: boolean\n): { uvs: Float32Array; offset: number } {\n\tif (offset + UV_CHUNK_HEADER_BYTES > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read UV chunk header.', {\n\t\t\texpectedBytes: UV_CHUNK_HEADER_BYTES,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\n\tconst uvFormat = view.getUint32(offset, true);\n\toffset += 4;\n\tconst originU = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst originV = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleU = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleV = view.getFloat64(offset, true);\n\toffset += 8;\n\n\tconst componentCount = vertexCount * 2;\n\tconst useFloat32 = uvFormat === UV_FORMAT_FLOAT32;\n\tconst dataByteLength = componentCount * (useFloat32 ? 4 : 2);\n\tif (offset + dataByteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read UV chunk.', {\n\t\t\texpectedBytes: dataByteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tuvFormat,\n\t\t\tvertexCount\n\t\t});\n\t}\n\n\tconst absoluteOffset = bytes.byteOffset + offset;\n\tlet uvs: Float32Array;\n\tif (useFloat32) {\n\t\t// Copy (not view) so the attribute owns its memory like the quantized path.\n\t\tuvs = readFloat32Vertices(bytes.buffer, absoluteOffset, componentCount).slice();\n\t} else {\n\t\tconst raw = readUint16Array(bytes.buffer, absoluteOffset, componentCount);\n\t\tuvs = new Float32Array(componentCount);\n\t\tlet qu = 0;\n\t\tlet qv = 0;\n\t\tfor (let i = 0; i < componentCount; i += 2) {\n\t\t\tif (deltaEncoded) {\n\t\t\t\tqu = (qu + unzigzag(raw[i]!)) & 0xffff;\n\t\t\t\tqv = (qv + unzigzag(raw[i + 1]!)) & 0xffff;\n\t\t\t} else {\n\t\t\t\tqu = raw[i]!;\n\t\t\t\tqv = raw[i + 1]!;\n\t\t\t}\n\t\t\tuvs[i] = originU + qu * scaleU;\n\t\t\tuvs[i + 1] = originV + qv * scaleV;\n\t\t}\n\t}\n\n\treturn { uvs, offset: offset + dataByteLength };\n}\n\n/**\n * Parses the trailing vertex-color chunk into raw r,g,b bytes, undoing the per-channel wrapped\n * 8-bit delta+zigzag filter when the blob-wide delta flag is set.\n */\nexport function parseColorChunk(\n\tbytes: Uint8Array,\n\toffset: number,\n\tvertexCount: number,\n\tdeltaEncoded: boolean\n): Uint8Array {\n\tconst byteLength = vertexCount * 3;\n\tif (offset + byteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read vertex-color chunk.', {\n\t\t\texpectedBytes: byteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tvertexCount\n\t\t});\n\t}\n\n\tconst raw = bytes.subarray(offset, offset + byteLength);\n\tif (!deltaEncoded) {\n\t\treturn raw.slice();\n\t}\n\n\tconst colors = new Uint8Array(byteLength);\n\tlet r = 0;\n\tlet g = 0;\n\tlet b = 0;\n\tfor (let i = 0; i < byteLength; i += 3) {\n\t\tr = (r + unzigzag(raw[i]!)) & 0xff;\n\t\tg = (g + unzigzag(raw[i + 1]!)) & 0xff;\n\t\tb = (b + unzigzag(raw[i + 2]!)) & 0xff;\n\t\tcolors[i] = r;\n\t\tcolors[i + 1] = g;\n\t\tcolors[i + 2] = b;\n\t}\n\treturn colors;\n}\n\n// ============================================================================\n","import { VisualizationError, ErrorCodes } from '../../shared/index.js';\n\nimport {\n\tBINARY_MESH_MAGIC,\n\tBINARY_MESH_VERSION,\n\tFLAG_DELTA_ENCODED,\n\tFLAG_FLOAT32,\n\tFLAG_HAS_UVS,\n\tFLAG_HAS_VERTEX_COLORS,\n\tFLAG_UINT16_INDICES,\n\tGEOMETRY_HEADER_BYTES,\n\tHEADER_PREAMBLE_BYTES,\n\tHOST_IS_LITTLE_ENDIAN,\n\tMIN_SUPPORTED_VERSION\n} from './binary/header.js';\nimport {\n\tdecodeDeltaIndices16,\n\tdecodeDeltaIndices32,\n\tdecodeDeltaVertices,\n\tdecodeUtf8,\n\tfail,\n\tmaybeDecompress,\n\treadFloat32Vertices,\n\treadInt16Vertices,\n\treadUint16Array,\n\treadUint32Array,\n\ttoUint8Array,\n\tvalidateIndicesInRange\n} from './binary/geometry.js';\nimport { parseColorChunk, parseUvChunk } from './binary/textures.js';\n\nimport type { BinaryMeshMetadata, ParsedBinaryMeshBatch } from './binary/header.js';\n\n// Re-exported so consumers keep importing from `binary-parser` rather than reaching into `binary/`.\nexport {\n\tBINARY_MESH_MAGIC,\n\tCOMPRESSED_MESH_MAGIC,\n\tBINARY_MESH_VERSION,\n\tMIN_SUPPORTED_VERSION,\n\tFLAG_FLOAT32,\n\tFLAG_UINT16_INDICES,\n\tFLAG_DELTA_ENCODED,\n\tFLAG_HAS_UVS,\n\tFLAG_HAS_VERTEX_COLORS,\n\tUV_FORMAT_UINT16,\n\tUV_FORMAT_FLOAT32\n} from './binary/header.js';\nexport type { BinaryMeshMetadata, ParsedBinaryMeshBatch } from './binary/header.js';\n\n// ============================================================================\n// PARSER\n// ============================================================================\n\n/**\n * Parses a binary mesh batch blob in the SLVA wire format.\n *\n * Blob layout:\n * ```\n * [4] magic = \"SLVA\" (0x53 0x4C 0x56 0x41)\n * [4] version = uint32 (currently 3)\n * [4] metadataLen = uint32 byte length of UTF-8 metadata JSON\n * [N] metadata = UTF-8 JSON (materials, groups, sourceComponentId, ...)\n * [4] flags = uint32 (bit 0: 0 = int16 quantized, 1 = float32 raw;\n * bit 1: 0 = uint32 indices, 1 = uint16 indices;\n * bit 2: 1 = delta+zigzag filtered)\n * [24] origin = 3 x float64\n * [24] scale = 3 x float64 (step per int16 unit; identity for float32)\n * [4] vertexCount = uint32 number of vertices (positions = vertexCount * 3 components)\n * [V] vertices = int16[vertexCount*3] OR float32[vertexCount*3]\n * [4] indexCount = uint32 number of indices\n * [I] indices = uint32[indexCount] OR uint16[indexCount]\n * ```\n *\n * For int16 vertices: world position = `origin + (q + 32767) * scale`. This matches Three.js\n * `BufferAttribute(arr, 3, true)` (`normalized: true`) semantics when the per-mesh transform\n * encodes `origin + scale`.\n *\n * For float32: `origin = (0, 0, 0)`, `scale = (1, 1, 1)`, vertices are raw world positions.\n *\n * With FLAG_DELTA_ENCODED (v3), stored int16 vertex components and indices are wrapped\n * differences from their predecessor, zigzag-mapped — see the flag's doc in `binary/header.ts`.\n * This parser returns reconstructed absolute values; consumers never see the filter.\n *\n * @param input - The blob, as either an `ArrayBuffer`/`Uint8Array` (binary transport) or a\n * base64-encoded string (JSON-envelope transport).\n * @throws {VisualizationError} On invalid magic, unknown version, or truncated input.\n */\nexport function parseBinaryMeshBatch(\n\tinput: ArrayBuffer | Uint8Array | string\n): ParsedBinaryMeshBatch {\n\tconst raw = parseBinaryMeshBatchRaw(input);\n\n\tlet vertices: Int16Array | Float32Array;\n\tif (raw.isFloat32) {\n\t\tvertices = raw.vertexData as Float32Array;\n\t} else if (raw.deltaEncoded) {\n\t\tvertices = decodeDeltaVertices(raw.vertexData as Uint16Array);\n\t} else {\n\t\tvertices = raw.vertexData as Int16Array;\n\t}\n\n\tlet indices = raw.indexData;\n\tif (raw.deltaEncoded) {\n\t\tindices =\n\t\t\tindices instanceof Uint16Array\n\t\t\t\t? decodeDeltaIndices16(indices)\n\t\t\t\t: decodeDeltaIndices32(indices);\n\t}\n\tvalidateIndicesInRange(indices, raw.vertexCount);\n\n\treturn {\n\t\tmetadata: raw.metadata,\n\t\tflags: raw.flags,\n\t\tvertices,\n\t\tindices,\n\t\torigin: raw.origin,\n\t\tscale: raw.scale,\n\t\tuvs: raw.uvs,\n\t\tcolors: raw.colors\n\t};\n}\n\n/**\n * Raw wire-value view of a blob: geometry arrays are exactly as stored — zigzag-mapped deltas when\n * the blob carries the delta filter — while metadata, UVs, and colors are fully decoded (they're\n * small). For consumers handing the heavy decoding to a worker (`mesh-assembly.ts`); everyone else\n * wants {@link parseBinaryMeshBatch}, which returns reconstructed absolute values.\n */\nexport interface RawBinaryMeshBatch {\n\tmetadata: BinaryMeshMetadata;\n\tflags: number;\n\t/** Wire vertex components: zigzag deltas (Uint16) when `deltaEncoded` and not float32. */\n\tvertexData: Uint16Array | Int16Array | Float32Array;\n\t/** Wire indices: zigzag deltas when `deltaEncoded`. NOT validated against vertexCount. */\n\tindexData: Uint16Array | Uint32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\tvertexCount: number;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\n/** See {@link RawBinaryMeshBatch}. Same validation/throw behavior as the decoding parser. */\nexport function parseBinaryMeshBatchRaw(\n\tinput: ArrayBuffer | Uint8Array | string\n): RawBinaryMeshBatch {\n\tif (!HOST_IS_LITTLE_ENDIAN) {\n\t\tthrow new VisualizationError(\n\t\t\t'SLVA parsing requires a little-endian host: the zero-copy geometry readers view the wire bytes in host byte order.',\n\t\t\tErrorCodes.ENVIRONMENT_ERROR\n\t\t);\n\t}\n\n\tconst bytes = maybeDecompress(toUint8Array(input));\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\n\tif (bytes.byteLength < HEADER_PREAMBLE_BYTES) {\n\t\tthrow fail('Blob too small to contain SLVA header.', {\n\t\t\texpectedBytes: HEADER_PREAMBLE_BYTES,\n\t\t\tavailableBytes: bytes.byteLength\n\t\t});\n\t}\n\n\tlet offset = 0;\n\n\tconst magic = view.getUint32(offset, true);\n\toffset += 4;\n\tif (magic !== BINARY_MESH_MAGIC) {\n\t\tthrow fail(`Invalid SLVA magic: 0x${magic.toString(16)}`, {\n\t\t\texpectedMagic: `0x${BINARY_MESH_MAGIC.toString(16)}`,\n\t\t\tactualMagic: `0x${magic.toString(16)}`\n\t\t});\n\t}\n\n\tconst version = view.getUint32(offset, true);\n\toffset += 4;\n\tif (version < MIN_SUPPORTED_VERSION || version > BINARY_MESH_VERSION) {\n\t\tthrow fail(`Unsupported SLVA version: ${version}`, {\n\t\t\tminSupportedVersion: MIN_SUPPORTED_VERSION,\n\t\t\tmaxSupportedVersion: BINARY_MESH_VERSION,\n\t\t\tactualVersion: version\n\t\t});\n\t}\n\n\tconst metadataLen = view.getUint32(offset, true);\n\toffset += 4;\n\tif (offset + metadataLen > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read metadata JSON.', {\n\t\t\texpectedBytes: metadataLen,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\n\tconst metadataBytes = bytes.subarray(offset, offset + metadataLen);\n\toffset += metadataLen;\n\n\tlet metadata: BinaryMeshMetadata;\n\ttry {\n\t\tmetadata = JSON.parse(decodeUtf8(metadataBytes)) as BinaryMeshMetadata;\n\t} catch (error) {\n\t\tthrow fail(\n\t\t\t`Failed to parse metadata JSON: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ metadataLen }\n\t\t);\n\t}\n\n\tif (offset + GEOMETRY_HEADER_BYTES > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read geometry header.', {\n\t\t\texpectedBytes: GEOMETRY_HEADER_BYTES,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\n\tconst flags = view.getUint32(offset, true);\n\toffset += 4;\n\n\tconst originX = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst originY = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst originZ = view.getFloat64(offset, true);\n\toffset += 8;\n\n\tconst scaleX = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleY = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleZ = view.getFloat64(offset, true);\n\toffset += 8;\n\n\tconst vertexCount = view.getUint32(offset, true);\n\toffset += 4;\n\n\tconst useFloat32 = (flags & FLAG_FLOAT32) !== 0;\n\tconst deltaEncoded = (flags & FLAG_DELTA_ENCODED) !== 0;\n\tconst componentCount = vertexCount * 3;\n\tconst bytesPerComponent = useFloat32 ? 4 : 2;\n\tconst verticesByteLength = componentCount * bytesPerComponent;\n\n\tif (offset + verticesByteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read vertices.', {\n\t\t\texpectedBytes: verticesByteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tuseFloat32,\n\t\t\tvertexCount\n\t\t});\n\t}\n\n\t// Typed-array views need alignment to the element size. The header lays out the geometry block\n\t// so the vertex byte offset is always 4-aligned (preamble 12 + metadataLen + 4 + 48 + 4) —\n\t// satisfies both float32 (4-byte) and int16 (2-byte). A zero-copy view is only valid if\n\t// `bytes.byteOffset + offset` respects that alignment in the underlying buffer, which a wrapper\n\t// Uint8Array could violate; the readers fall back to a copy when it does.\n\tconst absoluteOffset = bytes.byteOffset + offset;\n\tlet vertexData: Uint16Array | Int16Array | Float32Array;\n\tif (useFloat32) {\n\t\tvertexData = readFloat32Vertices(bytes.buffer, absoluteOffset, componentCount);\n\t} else if (deltaEncoded) {\n\t\t// Raw zigzag deltas — parseBinaryMeshBatch (or the assembly worker) prefix-sums them later.\n\t\tvertexData = readUint16Array(bytes.buffer, absoluteOffset, componentCount);\n\t} else {\n\t\tvertexData = readInt16Vertices(bytes.buffer, absoluteOffset, componentCount);\n\t}\n\toffset += verticesByteLength;\n\n\tif (offset + 4 > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read index count.', {\n\t\t\texpectedBytes: 4,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\tconst indexCount = view.getUint32(offset, true);\n\toffset += 4;\n\n\tconst useUint16Indices = (flags & FLAG_UINT16_INDICES) !== 0;\n\tconst bytesPerIndex = useUint16Indices ? 2 : 4;\n\tconst indicesByteLength = indexCount * bytesPerIndex;\n\tif (offset + indicesByteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read indices.', {\n\t\t\texpectedBytes: indicesByteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tindexCount,\n\t\t\tuseUint16Indices\n\t\t});\n\t}\n\n\tconst indexData = useUint16Indices\n\t\t? readUint16Array(bytes.buffer, bytes.byteOffset + offset, indexCount)\n\t\t: readUint32Array(bytes.buffer, bytes.byteOffset + offset, indexCount);\n\toffset += indicesByteLength;\n\n\t// Optional trailing chunks: UV first, then colors. Pre-chunk-writer blobs simply end here —\n\t// each read is gated by its flag, so nothing is consumed when a chunk is absent.\n\tlet uvs: Float32Array | null = null;\n\tif ((flags & FLAG_HAS_UVS) !== 0) {\n\t\tconst parsed = parseUvChunk(bytes, view, offset, vertexCount, deltaEncoded);\n\t\tuvs = parsed.uvs;\n\t\toffset = parsed.offset;\n\t}\n\n\tlet colors: Uint8Array | null = null;\n\tif ((flags & FLAG_HAS_VERTEX_COLORS) !== 0) {\n\t\tcolors = parseColorChunk(bytes, offset, vertexCount, deltaEncoded);\n\t}\n\n\treturn {\n\t\tmetadata,\n\t\tflags,\n\t\tvertexData,\n\t\tindexData,\n\t\tisFloat32: useFloat32,\n\t\tdeltaEncoded,\n\t\tvertexCount,\n\t\torigin: [originX, originY, originZ],\n\t\tscale: [scaleX, scaleY, scaleZ],\n\t\tuvs,\n\t\tcolors\n\t};\n}\n","/**\n * {@link assembleGeometries} is the hot, pure part of batch parsing: undoes the delta filter on\n * the raw wire arrays, dequantizes int16 positions to world floats, slices/rebases per-geometry\n * windows and computes vertex normals.\n * Everything it needs travels as typed arrays, so the whole stage runs in a Worker and the main\n * thread only wraps the returned buffers into `BufferGeometry` objects.\n *\n * Like `edge-extract.ts`, it's a single self-contained function with zero outer captures (only\n * `Math` and its arguments) so `Function.prototype.toString` yields code that runs unchanged\n * inside a blob-URL Worker ({@link meshAssemblyWorkerSource}) — bundler-agnostic by construction.\n * That forces duplicating small helpers from `binary-parser.ts` (unzigzag/delta decode);\n * equivalence with the synchronous path is pinned by tests.\n */\n\nexport interface AssemblyWindow {\n\tvertexStart: number;\n\tvertexCount: number;\n\tindexStart: number;\n\tindexCount: number;\n}\n\nexport interface AssemblyJob {\n\tkind: 'merged' | 'single';\n\twindows: AssemblyWindow[];\n}\n\nexport interface AssemblyInput {\n\t/** Raw wire vertex components: zigzag deltas (Uint16) when delta-encoded, else absolute. */\n\tvertexData: Uint16Array | Int16Array | Float32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\t/** Raw wire indices: zigzag deltas when delta-encoded, else absolute. */\n\tindexData: Uint16Array | Uint32Array;\n\t/** Already-decoded absolute UV pairs / RGB bytes (small, decoded on the main thread). */\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n\tjobs: AssemblyJob[];\n}\n\nexport interface AssembledGeometry {\n\tpositions: Float32Array;\n\tnormals: Float32Array;\n\tindices: Uint32Array;\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\nexport function assembleGeometries(input: AssemblyInput): AssembledGeometry[] {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tconst { isFloat32, deltaEncoded, origin, scale, uvs, colors, jobs } = input;\n\n\tconst unzigzag = (zz: number): number => (zz >>> 1) ^ -(zz & 1);\n\n\t// --- Undo the delta filter (whole-array: each value depends on its predecessor) ------------\n\tlet worldVertices: Float32Array;\n\tif (isFloat32) {\n\t\tworldVertices = input.vertexData as Float32Array;\n\t} else {\n\t\tlet quantized: Int16Array;\n\t\tif (deltaEncoded) {\n\t\t\tconst zigzagged = input.vertexData as Uint16Array;\n\t\t\tquantized = new Int16Array(zigzagged.length);\n\t\t\tlet px = 0;\n\t\t\tlet py = 0;\n\t\t\tlet pz = 0;\n\t\t\tfor (let i = 0; i < zigzagged.length; i += 3) {\n\t\t\t\tpx = ((px + unzigzag(zigzagged[i])) << 16) >> 16;\n\t\t\t\tpy = ((py + unzigzag(zigzagged[i + 1])) << 16) >> 16;\n\t\t\t\tpz = ((pz + unzigzag(zigzagged[i + 2])) << 16) >> 16;\n\t\t\t\tquantized[i] = px;\n\t\t\t\tquantized[i + 1] = py;\n\t\t\t\tquantized[i + 2] = pz;\n\t\t\t}\n\t\t} else {\n\t\t\tquantized = input.vertexData as Int16Array;\n\t\t}\n\t\t// Dequantize: world = origin + (q + 32767) * scale (matches the writer/binary-parser).\n\t\tworldVertices = new Float32Array(quantized.length);\n\t\tconst ox = origin[0];\n\t\tconst oy = origin[1];\n\t\tconst oz = origin[2];\n\t\tconst sx = scale[0];\n\t\tconst sy = scale[1];\n\t\tconst sz = scale[2];\n\t\tfor (let i = 0; i < quantized.length; i += 3) {\n\t\t\tworldVertices[i] = ox + (quantized[i] + 32767) * sx;\n\t\t\tworldVertices[i + 1] = oy + (quantized[i + 1] + 32767) * sy;\n\t\t\tworldVertices[i + 2] = oz + (quantized[i + 2] + 32767) * sz;\n\t\t}\n\t}\n\n\tlet indices: Uint16Array | Uint32Array;\n\tif (deltaEncoded) {\n\t\tconst zigzagged = input.indexData;\n\t\tif (zigzagged instanceof Uint16Array) {\n\t\t\tconst out = new Uint16Array(zigzagged.length);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\t\t\tprev = (prev + unzigzag(zigzagged[i])) & 0xffff;\n\t\t\t\tout[i] = prev;\n\t\t\t}\n\t\t\tindices = out;\n\t\t} else {\n\t\t\tconst out = new Uint32Array(zigzagged.length);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\t\t\tprev = (prev + unzigzag(zigzagged[i])) >>> 0;\n\t\t\t\tout[i] = prev;\n\t\t\t}\n\t\t\tindices = out;\n\t\t}\n\t} else {\n\t\tindices = input.indexData;\n\t}\n\n\tconst totalVertexCount = worldVertices.length / 3;\n\tfor (let i = 0; i < indices.length; i++) {\n\t\tif (indices[i] >= totalVertexCount) {\n\t\t\tthrow new Error(`Index ${indices[i]} out of range of vertexCount ${totalVertexCount}`);\n\t\t}\n\t}\n\n\t// --- Assemble each job: window copies, rebased indices, area-weighted vertex normals --------\n\tconst results: AssembledGeometry[] = [];\n\n\tfor (const job of jobs) {\n\t\tlet vertexTotal = 0;\n\t\tlet indexTotal = 0;\n\t\tfor (const window of job.windows) {\n\t\t\tvertexTotal += window.vertexCount;\n\t\t\tindexTotal += window.indexCount;\n\t\t}\n\n\t\tconst positions = new Float32Array(vertexTotal * 3);\n\t\tconst outIndices = new Uint32Array(indexTotal);\n\t\tconst outUvs = uvs ? new Float32Array(vertexTotal * 2) : null;\n\t\tconst outColors = colors ? new Uint8Array(vertexTotal * 3) : null;\n\n\t\tlet vertexCursor = 0;\n\t\tlet indexCursor = 0;\n\t\tfor (const window of job.windows) {\n\t\t\tconst componentStart = window.vertexStart * 3;\n\t\t\tpositions.set(\n\t\t\t\tworldVertices.subarray(componentStart, componentStart + window.vertexCount * 3),\n\t\t\t\tvertexCursor * 3\n\t\t\t);\n\t\t\tif (outUvs && uvs) {\n\t\t\t\toutUvs.set(\n\t\t\t\t\tuvs.subarray(window.vertexStart * 2, (window.vertexStart + window.vertexCount) * 2),\n\t\t\t\t\tvertexCursor * 2\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (outColors && colors) {\n\t\t\t\toutColors.set(\n\t\t\t\t\tcolors.subarray(componentStart, componentStart + window.vertexCount * 3),\n\t\t\t\t\tvertexCursor * 3\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst windowStart = window.vertexStart;\n\t\t\tconst windowEnd = window.vertexStart + window.vertexCount;\n\t\t\tconst shift = vertexCursor - window.vertexStart;\n\t\t\tfor (let i = 0; i < window.indexCount; i++) {\n\t\t\t\tconst indexValue = indices[window.indexStart + i];\n\t\t\t\tif (indexValue < windowStart || indexValue >= windowEnd) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Index ${indexValue} outside vertex window [${windowStart}, ${windowEnd})`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\toutIndices[indexCursor + i] = indexValue + shift;\n\t\t\t}\n\n\t\t\tvertexCursor += window.vertexCount;\n\t\t\tindexCursor += window.indexCount;\n\t\t}\n\n\t\t// Vertex normals, mirroring THREE.BufferGeometry.computeVertexNormals: accumulate the\n\t\t// non-normalized (area-weighted) face normal cross((c-b),(a-b)) onto each corner, then\n\t\t// normalize per vertex.\n\t\tconst normals = new Float32Array(vertexTotal * 3);\n\t\tfor (let i = 0; i < outIndices.length; i += 3) {\n\t\t\tconst a = outIndices[i] * 3;\n\t\t\tconst b = outIndices[i + 1] * 3;\n\t\t\tconst c = outIndices[i + 2] * 3;\n\n\t\t\tconst cbx = positions[c] - positions[b];\n\t\t\tconst cby = positions[c + 1] - positions[b + 1];\n\t\t\tconst cbz = positions[c + 2] - positions[b + 2];\n\t\t\tconst abx = positions[a] - positions[b];\n\t\t\tconst aby = positions[a + 1] - positions[b + 1];\n\t\t\tconst abz = positions[a + 2] - positions[b + 2];\n\n\t\t\tconst nx = cby * abz - cbz * aby;\n\t\t\tconst ny = cbz * abx - cbx * abz;\n\t\t\tconst nz = cbx * aby - cby * abx;\n\n\t\t\tnormals[a] += nx;\n\t\t\tnormals[a + 1] += ny;\n\t\t\tnormals[a + 2] += nz;\n\t\t\tnormals[b] += nx;\n\t\t\tnormals[b + 1] += ny;\n\t\t\tnormals[b + 2] += nz;\n\t\t\tnormals[c] += nx;\n\t\t\tnormals[c + 1] += ny;\n\t\t\tnormals[c + 2] += nz;\n\t\t}\n\t\tfor (let i = 0; i < normals.length; i += 3) {\n\t\t\tconst x = normals[i];\n\t\t\tconst y = normals[i + 1];\n\t\t\tconst z = normals[i + 2];\n\t\t\tconst length = Math.sqrt(x * x + y * y + z * z) || 1;\n\t\t\tnormals[i] = x / length;\n\t\t\tnormals[i + 1] = y / length;\n\t\t\tnormals[i + 2] = z / length;\n\t\t}\n\n\t\tresults.push({\n\t\t\tpositions,\n\t\t\tnormals,\n\t\t\tindices: outIndices,\n\t\t\tuvs: outUvs,\n\t\t\tcolors: outColors\n\t\t});\n\t}\n\n\treturn results;\n}\n\n/**\n * Worker script running {@link assembleGeometries} off the main thread. Protocol: receives\n * `{id, input}`, replies `{id, geometries}` with every output buffer transferred, or\n * `{id, error}`. Pinned by a test that evals this source against a stub `self`.\n */\nexport function meshAssemblyWorkerSource(): string {\n\treturn [\n\t\t`const assemble = ${assembleGeometries.toString()};`,\n\t\t`self.onmessage = (event) => {`,\n\t\t` const { id, input } = event.data;`,\n\t\t` try {`,\n\t\t` const geometries = assemble(input);`,\n\t\t` const transfer = [];`,\n\t\t` for (const g of geometries) {`,\n\t\t` transfer.push(g.positions.buffer, g.normals.buffer, g.indices.buffer);`,\n\t\t` if (g.uvs) transfer.push(g.uvs.buffer);`,\n\t\t` if (g.colors) transfer.push(g.colors.buffer);`,\n\t\t` }`,\n\t\t` self.postMessage({ id, geometries }, transfer);`,\n\t\t` } catch (error) {`,\n\t\t` self.postMessage({ id, error: String((error && error.message) || error) });`,\n\t\t` }`,\n\t\t`};`\n\t].join('\\n');\n}\n","import { meshAssemblyWorkerSource } from '../mesh-assembly.js';\n\nimport type { AssembledGeometry } from '../mesh-assembly.js';\n\n/**\n * Below this triangle count the synchronous path finishes in ~10 ms — a worker round-trip (two\n * buffer copies + wake) isn't worth it. Above it, delta-decode + dequantize + merge + normals run\n * in the worker and the main thread only wraps returned buffers (or reuses cached geometries).\n */\nexport const ASSEMBLY_WORKER_MIN_TRIANGLES = 50_000;\n\ninterface PendingAssembly {\n\tresolve: (geometries: AssembledGeometry[]) => void;\n\treject: (error: Error) => void;\n}\n\nlet assemblyWorker: Worker | null | undefined; // undefined = not yet tried, null = unavailable\nconst pendingAssemblies = new Map<number, PendingAssembly>();\nlet nextAssemblyRequestId = 1;\n\nexport function getAssemblyWorker(): Worker | null {\n\tif (assemblyWorker !== undefined) return assemblyWorker;\n\tif (\n\t\ttypeof Worker === 'undefined' ||\n\t\ttypeof Blob === 'undefined' ||\n\t\ttypeof URL === 'undefined' ||\n\t\ttypeof URL.createObjectURL !== 'function'\n\t) {\n\t\tassemblyWorker = null;\n\t\treturn null;\n\t}\n\ttry {\n\t\t// Blob URL keeps the library bundler-agnostic; deliberately never revoked (see render/edges/extraction.ts).\n\t\tconst url = URL.createObjectURL(\n\t\t\tnew Blob([meshAssemblyWorkerSource()], { type: 'text/javascript' })\n\t\t);\n\t\tconst worker = new Worker(url);\n\t\tworker.onmessage = (event: MessageEvent) => {\n\t\t\tconst { id, geometries, error } = event.data as {\n\t\t\t\tid: number;\n\t\t\t\tgeometries?: AssembledGeometry[];\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tconst pending = pendingAssemblies.get(id);\n\t\t\tif (!pending) return;\n\t\t\tpendingAssemblies.delete(id);\n\t\t\tif (geometries) pending.resolve(geometries);\n\t\t\telse pending.reject(new Error(error ?? 'mesh assembly failed in worker'));\n\t\t};\n\t\tworker.onerror = () => {\n\t\t\tfor (const pending of pendingAssemblies.values()) {\n\t\t\t\tpending.reject(new Error('mesh assembly worker crashed'));\n\t\t\t}\n\t\t\tpendingAssemblies.clear();\n\t\t\tworker.terminate();\n\t\t\tassemblyWorker = null; // don't retry this session — callers fall back to the sync path\n\t\t};\n\t\tassemblyWorker = worker;\n\t} catch {\n\t\tassemblyWorker = null;\n\t}\n\treturn assemblyWorker;\n}\n\nexport function requestAssembly(\n\tworker: Worker,\n\tinput: unknown,\n\ttransfer: Transferable[]\n): Promise<AssembledGeometry[]> {\n\treturn new Promise<AssembledGeometry[]>((resolve, reject) => {\n\t\tconst id = nextAssemblyRequestId++;\n\t\tpendingAssemblies.set(id, { resolve, reject });\n\t\tworker.postMessage({ id, input }, transfer);\n\t});\n}\n","import * as THREE from 'three';\n\nimport { getLogger, observeMaxAnisotropy } from '../../shared/index.js';\n\n/**\n * Anisotropic-filtering samples applied to color maps, keeping textures sharp at grazing angles\n * instead of blurring. Ceiling is hardware-defined (`renderer.capabilities.getMaxAnisotropy()`,\n * typically 16). Defaults to three's default (1 — no anisotropy) until a renderer reports in.\n */\nlet maxAnisotropy = 1;\n\n/**\n * Subscribed to the renderer's own report below, so no host wiring is needed; still exported for a\n * host embedding a foreign renderer that wants to set it directly. Applies to textures loaded from\n * here on — textures already decoded keep the value they were given.\n */\nexport function setTextureAnisotropy(value: number): void {\n\tmaxAnisotropy = Math.max(1, value);\n}\n\n// Take the value straight from whichever renderer initializes, rather than depending on the host to\n// forward it. `render/` publishes, this layer subscribes — neither imports the other.\nobserveMaxAnisotropy(setTextureAnisotropy);\n\n/**\n * Assigns a texture to `material.map` once fetched and decoded — the mesh renders untextured for\n * the first frames. Load failures log a warning and leave the material untextured rather than\n * breaking the batch.\n *\n * Each call loads independently: no caching, no cross-material sharing. The texture is owned by the\n * material it is assigned to, so the scene's normal dispose walk frees it like any other resource.\n */\nexport function applyTextureMap(material: THREE.MeshPhysicalMaterial, url: string): void {\n\t// No DOM (SSR / tests): textures can't decode without an image element; skip quietly.\n\tif (typeof document === 'undefined') {\n\t\treturn;\n\t}\n\n\tnew THREE.TextureLoader().load(\n\t\turl,\n\t\t(texture) => {\n\t\t\t// Color maps are sRGB; without this the render is washed out.\n\t\t\ttexture.colorSpace = THREE.SRGBColorSpace;\n\t\t\t// Keep textures crisp at grazing angles (see maxAnisotropy).\n\t\t\ttexture.anisotropy = maxAnisotropy;\n\t\t\tmaterial.map = texture;\n\t\t\tmaterial.needsUpdate = true;\n\t\t},\n\t\tundefined,\n\t\t(error) => {\n\t\t\tgetLogger().warn(`Failed to load material texture ${url}:`, error);\n\t\t}\n\t);\n}\n","import * as THREE from 'three';\n\nimport { parseColor } from '../../../shared/index.js';\n\nimport { applyTextureMap } from '../apply-texture.js';\n\nimport type { MaterialAppearanceOptions, SerializableMaterial } from '../types.js';\n\n// A near-pure metal has no diffuse response, so under the low-IBL 'technical' look it goes flat and\n// reads as painted card. Real architectural sheet metal is coated, not a bare mirror, so meaningfully\n// metallic materials get a thin satin clearcoat — a glossy dielectric layer independent of base\n// metalness/envMap, so folds catch light even when the IBL is dialed down.\nconst METAL_CLEARCOAT_THRESHOLD = 0.5;\nconst METAL_CLEARCOAT = 0.5;\nconst METAL_CLEARCOAT_ROUGHNESS = 0.3;\n\nexport function createMaterial(\n\tmatData: SerializableMaterial,\n\toptions?: { vertexColors?: boolean; appearance?: MaterialAppearanceOptions }\n): THREE.MeshPhysicalMaterial {\n\tconst color = parseColor(matData.color);\n\tconst vertexColors = options?.vertexColors ?? false;\n\tconst appearance = options?.appearance;\n\n\tconst material = new THREE.MeshPhysicalMaterial({\n\t\tcolor,\n\t\tmetalness: matData.metalness,\n\t\troughness: matData.roughness,\n\t\topacity: matData.opacity,\n\t\ttransparent: matData.transparent,\n\t\tvertexColors,\n\t\t// Cull back faces for closed solids (crisper silhouette, less overdraw); keep both sides for\n\t\t// open surfaces. Caller-controlled since Rhino emits both — default DoubleSide is the safe read.\n\t\tside: appearance?.cullBackfaces ? THREE.FrontSide : THREE.DoubleSide,\n\t\tpolygonOffset: true, // avoids z-fighting on coplanar faces\n\t\tpolygonOffsetFactor: 0.5,\n\t\tpolygonOffsetUnits: 0.5,\n\t\tdepthWrite: true,\n\t\tdepthTest: true\n\t});\n\n\t// HDR image-based-lighting reflection strength. Left at three's default (1) unless the caller\n\t// dials it: <1 flattens reflections toward a matte/technical read, >1 pushes a glossier look.\n\tif (appearance?.envMapIntensity != null) {\n\t\tmaterial.envMapIntensity = appearance.envMapIntensity;\n\t}\n\n\t// See the constants above. Plastics/matte fall below the threshold and stay bare.\n\tif (matData.metalness > METAL_CLEARCOAT_THRESHOLD) {\n\t\tmaterial.clearcoat = METAL_CLEARCOAT;\n\t\tmaterial.clearcoatRoughness = METAL_CLEARCOAT_ROUGHNESS;\n\t}\n\n\tif (vertexColors) {\n\t\tapplyVertexColorSRGBDecode(material);\n\t}\n\n\t// Async; the mesh renders untextured until the image decodes.\n\tif (matData.map) {\n\t\tapplyTextureMap(material, matData.map);\n\t}\n\n\treturn material;\n}\n\n/**\n * three.js uploads vertex colors verbatim and multiplies them straight into linear working space\n * (unlike textures, which carry a `colorSpace` and get decoded) — so sRGB-authored vertex colors\n * render too bright without this shader patch. Done on the GPU rather than a CPU pass over the\n * buffer, to keep the hot per-solve parse cheap.\n */\nexport function applyVertexColorSRGBDecode(material: THREE.Material): void {\n\tmaterial.onBeforeCompile = (shader) => {\n\t\tshader.vertexShader = shader.vertexShader.replace(\n\t\t\t'#include <color_vertex>',\n\t\t\t`#include <color_vertex>\n\t\t\t#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA )\n\t\t\t\tvColor.rgb = mix(\n\t\t\t\t\tvColor.rgb / 12.92,\n\t\t\t\t\tpow( ( vColor.rgb + 0.055 ) / 1.055, vec3( 2.4 ) ),\n\t\t\t\t\tstep( vec3( 0.04045 ), vColor.rgb )\n\t\t\t\t);\n\t\t\t#endif`\n\t\t);\n\t};\n}\n","import { VisualizationError, ErrorCodes } from '../../../shared/index.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\nexport function metadataFail(\n\tmessage: string,\n\tcontext: Record<string, unknown>\n): VisualizationError {\n\treturn new VisualizationError(message, ErrorCodes.VALIDATION_ERROR, { context });\n}\n\n/**\n * Validates the batch's group/mesh metadata against the decoded geometry buffers before any of it\n * is used arithmetically. Throws on the first inconsistency — out-of-range `materialId`,\n * non-integer or negative offsets/counts, or a vertex/index window that overruns the buffers — so\n * malformed or version-skewed metadata fails the parse loudly instead of corrupting the render.\n */\nexport function validateGroupMetadata(\n\tgroups: MaterialGroup[],\n\tmaterialCount: number,\n\ttotalVertexCount: number,\n\ttotalIndexCount: number\n): void {\n\tfor (const group of groups) {\n\t\tif (\n\t\t\t!Number.isInteger(group.materialId) ||\n\t\t\tgroup.materialId < 0 ||\n\t\t\tgroup.materialId >= materialCount\n\t\t) {\n\t\t\tthrow metadataFail('Group materialId out of range of the materials array.', {\n\t\t\t\tmaterialId: group.materialId,\n\t\t\t\tmaterialCount\n\t\t\t});\n\t\t}\n\n\t\tfor (const mesh of group.meshes) {\n\t\t\tconst fields = {\n\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\tindexCount: mesh.indexCount\n\t\t\t};\n\t\t\tfor (const [field, value] of Object.entries(fields)) {\n\t\t\t\tif (!Number.isInteger(value) || value < 0) {\n\t\t\t\t\tthrow metadataFail(`Mesh metadata field \"${field}\" must be a non-negative integer.`, {\n\t\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mesh.vertexStart + mesh.vertexCount > totalVertexCount) {\n\t\t\t\tthrow metadataFail('Mesh vertex window exceeds the batch vertex buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\t\ttotalVertexCount\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (mesh.indexStart + mesh.indexCount > totalIndexCount) {\n\t\t\t\tthrow metadataFail('Mesh index window exceeds the batch index buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\t\tindexCount: mesh.indexCount,\n\t\t\t\t\ttotalIndexCount\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Error for an index outside its mesh's declared vertex window\n * `[vertexStart, vertexStart + vertexCount)`. Rebasing (`index - vertexStart`) writes into an\n * unsigned array, so an out-of-window index would otherwise wrap to ~4 billion and corrupt the\n * geometry. Range checks live inline in the copy loops (a function call per index measured\n * noticeably slower at millions of indices) — this only builds the failure.\n */\nexport function indexOutOfWindow(indexValue: number, meshMeta: MeshMetadata): VisualizationError {\n\treturn metadataFail(\"Index references a vertex outside its mesh's vertex window.\", {\n\t\tmeshName: meshMeta.name,\n\t\tindexValue,\n\t\tvertexStart: meshMeta.vertexStart,\n\t\tvertexCount: meshMeta.vertexCount\n\t});\n}\n\n/**\n * Reconstructs world-unit float32 positions from int16 quantized values:\n * `world = origin + (q + 32767) * scale`. No rotation — the Three scene uses Rhino's Z-up frame,\n * so vertices pass through as they arrived.\n */\nexport function dequantizeInt16(\n\tq: Int16Array,\n\torigin: [number, number, number],\n\tscale: [number, number, number]\n): Float32Array {\n\tconst out = new Float32Array(q.length);\n\tconst ox = origin[0];\n\tconst oy = origin[1];\n\tconst oz = origin[2];\n\tconst sx = scale[0];\n\tconst sy = scale[1];\n\tconst sz = scale[2];\n\n\tfor (let i = 0; i < q.length; i += 3) {\n\t\tout[i] = ox + (q[i]! + 32767) * sx;\n\t\tout[i + 1] = oy + (q[i + 1]! + 32767) * sy;\n\t\tout[i + 2] = oz + (q[i + 2]! + 32767) * sz;\n\t}\n\n\treturn out;\n}\n","import * as THREE from 'three';\n\nimport { indexOutOfWindow } from './metadata.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\n/**\n * Merges a material group's meshes into one BufferGeometry. Parser indices already address the\n * combined vertex array (rebased by the C# pipeline during batch assembly), so this copies each\n * mesh's vertex/index slices into a fresh contiguous buffer and shifts indices to match.\n */\nexport function createMergedMesh(\n\tgroup: MaterialGroup,\n\tallVertices: Float32Array,\n\tallIndices: Uint16Array | Uint32Array,\n\tmaterials: THREE.Material[],\n\tallUvs: Float32Array | null = null,\n\tallColors: Uint8Array | null = null\n): THREE.Mesh {\n\tlet totalVertexCount = 0;\n\tlet totalIndexCount = 0;\n\tfor (const meshMeta of group.meshes) {\n\t\ttotalVertexCount += meshMeta.vertexCount;\n\t\ttotalIndexCount += meshMeta.indexCount;\n\t}\n\n\tconst mergedVertices = new Float32Array(totalVertexCount * 3);\n\tconst mergedIndices = new Uint32Array(totalIndexCount);\n\tconst mergedUvs = allUvs ? new Float32Array(totalVertexCount * 2) : null;\n\tconst mergedColors = allColors ? new Uint8Array(totalVertexCount * 3) : null;\n\n\tlet vertexWriteCursor = 0;\n\tlet indexWriteCursor = 0;\n\n\tfor (const meshMeta of group.meshes) {\n\t\tconst componentStart = meshMeta.vertexStart * 3;\n\t\tconst componentLen = meshMeta.vertexCount * 3;\n\t\tmergedVertices.set(\n\t\t\tallVertices.subarray(componentStart, componentStart + componentLen),\n\t\t\tvertexWriteCursor * 3\n\t\t);\n\n\t\tif (mergedUvs && allUvs) {\n\t\t\tconst uvStart = meshMeta.vertexStart * 2;\n\t\t\tmergedUvs.set(\n\t\t\t\tallUvs.subarray(uvStart, uvStart + meshMeta.vertexCount * 2),\n\t\t\t\tvertexWriteCursor * 2\n\t\t\t);\n\t\t}\n\n\t\tif (mergedColors && allColors) {\n\t\t\tmergedColors.set(\n\t\t\t\tallColors.subarray(componentStart, componentStart + componentLen),\n\t\t\t\tvertexWriteCursor * 3\n\t\t\t);\n\t\t}\n\n\t\tconst indicesSlice = allIndices.subarray(\n\t\t\tmeshMeta.indexStart,\n\t\t\tmeshMeta.indexStart + meshMeta.indexCount\n\t\t);\n\t\tconst indexShift = vertexWriteCursor - meshMeta.vertexStart;\n\t\tconst windowStart = meshMeta.vertexStart;\n\t\tconst windowEnd = meshMeta.vertexStart + meshMeta.vertexCount;\n\t\tfor (let i = 0; i < indicesSlice.length; i++) {\n\t\t\tconst indexValue = indicesSlice[i]!;\n\t\t\tif (indexValue < windowStart || indexValue >= windowEnd) {\n\t\t\t\tthrow indexOutOfWindow(indexValue, meshMeta);\n\t\t\t}\n\t\t\tmergedIndices[indexWriteCursor + i] = indexValue + indexShift;\n\t\t}\n\n\t\tvertexWriteCursor += meshMeta.vertexCount;\n\t\tindexWriteCursor += meshMeta.indexCount;\n\t}\n\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute('position', new THREE.BufferAttribute(mergedVertices, 3));\n\tgeometry.setIndex(new THREE.BufferAttribute(mergedIndices, 1));\n\tif (mergedUvs) {\n\t\tgeometry.setAttribute('uv', new THREE.BufferAttribute(mergedUvs, 2));\n\t}\n\tif (mergedColors) {\n\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(mergedColors, 3, true));\n\t}\n\tgeometry.computeVertexNormals();\n\n\treturn finalizeMergedMesh(geometry, group, materials);\n}\n\nexport function finalizeMergedMesh(\n\tgeometry: THREE.BufferGeometry,\n\tgroup: MaterialGroup,\n\tmaterials: THREE.Material[]\n): THREE.Mesh {\n\tconst threeMesh = new THREE.Mesh(geometry, materials[group.materialId]);\n\tconst firstMesh = group.meshes[0];\n\tconst meshNames = group.meshes.map((m) => m.name).filter((name) => name && name.length > 0);\n\tthreeMesh.name = meshNames.length > 0 ? meshNames[0]! : `merged_material_${group.materialId}`;\n\tthreeMesh.castShadow = true;\n\tthreeMesh.receiveShadow = true;\n\n\tthreeMesh.userData = {\n\t\tsource: 'compute',\n\t\tname: threeMesh.name,\n\t\tlayer: firstMesh?.layer ?? '',\n\t\toriginalIndex: firstMesh?.originalIndex ?? 0,\n\t\tmetadata: firstMesh?.metadata ?? {},\n\t\tmergedFrom: group.meshes.slice(1).map((m) => ({\n\t\t\tname: m.name,\n\t\t\tlayer: m.layer,\n\t\t\toriginalIndex: m.originalIndex\n\t\t}))\n\t};\n\n\treturn threeMesh;\n}\n\n/**\n * Creates individual meshes from a material group. Each mesh's indices are rebased so they\n * address its own local vertex slice starting from 0.\n */\nexport function createIndividualMeshes(\n\tgroup: MaterialGroup,\n\tallVertices: Float32Array,\n\tallIndices: Uint16Array | Uint32Array,\n\tmaterials: THREE.Material[],\n\tallUvs: Float32Array | null = null,\n\tallColors: Uint8Array | null = null\n): THREE.Mesh[] {\n\tconst meshes: THREE.Mesh[] = [];\n\n\tfor (const meshMeta of group.meshes) {\n\t\tconst componentStart = meshMeta.vertexStart * 3;\n\t\tconst componentLen = meshMeta.vertexCount * 3;\n\n\t\t// `subarray` returns a view; copy via `slice` so the BufferAttribute owns its memory and\n\t\t// downstream code (dispose/reuse) can't surprise us by sharing the parser's buffer.\n\t\tconst vertices = allVertices.slice(componentStart, componentStart + componentLen);\n\n\t\tconst indicesSlice = allIndices.subarray(\n\t\t\tmeshMeta.indexStart,\n\t\t\tmeshMeta.indexStart + meshMeta.indexCount\n\t\t);\n\t\tconst rebasedIndices = new Uint32Array(indicesSlice.length);\n\t\tconst baseIndex = meshMeta.vertexStart;\n\t\tconst windowEnd = meshMeta.vertexStart + meshMeta.vertexCount;\n\t\tfor (let i = 0; i < indicesSlice.length; i++) {\n\t\t\tconst indexValue = indicesSlice[i]!;\n\t\t\tif (indexValue < baseIndex || indexValue >= windowEnd) {\n\t\t\t\tthrow indexOutOfWindow(indexValue, meshMeta);\n\t\t\t}\n\t\t\trebasedIndices[i] = indexValue - baseIndex;\n\t\t}\n\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));\n\t\tgeometry.setIndex(new THREE.BufferAttribute(rebasedIndices, 1));\n\t\tif (allUvs) {\n\t\t\tconst uvStart = meshMeta.vertexStart * 2;\n\t\t\tconst uvs = allUvs.slice(uvStart, uvStart + meshMeta.vertexCount * 2);\n\t\t\tgeometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n\t\t}\n\t\tif (allColors) {\n\t\t\tconst colors = allColors.slice(componentStart, componentStart + componentLen);\n\t\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));\n\t\t}\n\t\tgeometry.computeVertexNormals();\n\n\t\tmeshes.push(finalizeSingleMesh(geometry, meshMeta, group, materials));\n\t}\n\n\treturn meshes;\n}\n\nexport function finalizeSingleMesh(\n\tgeometry: THREE.BufferGeometry,\n\tmeshMeta: MeshMetadata,\n\tgroup: MaterialGroup,\n\tmaterials: THREE.Material[]\n): THREE.Mesh {\n\tconst mesh = new THREE.Mesh(geometry, materials[group.materialId]);\n\tmesh.name = meshMeta.name;\n\tmesh.userData = {\n\t\tsource: 'compute',\n\t\tname: meshMeta.name,\n\t\tlayer: meshMeta.layer ?? '',\n\t\toriginalIndex: meshMeta.originalIndex,\n\t\tmetadata: meshMeta.metadata ?? {}\n\t};\n\tmesh.castShadow = true;\n\tmesh.receiveShadow = true;\n\treturn mesh;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { FLAG_FLOAT32, parseBinaryMeshBatch, parseBinaryMeshBatchRaw } from './binary-parser.js';\n\nimport {\n\tASSEMBLY_WORKER_MIN_TRIANGLES,\n\tgetAssemblyWorker,\n\trequestAssembly\n} from './batch/assembly-worker.js';\nimport { createMaterial } from './batch/materials.js';\nimport {\n\tcreateIndividualMeshes,\n\tcreateMergedMesh,\n\tfinalizeMergedMesh,\n\tfinalizeSingleMesh\n} from './batch/merge.js';\nimport { dequantizeInt16, validateGroupMetadata } from './batch/metadata.js';\n\nimport type { AssembledGeometry, AssemblyJob, AssemblyWindow } from './mesh-assembly.js';\nimport type { ParsedBinaryMeshBatch } from './binary-parser.js';\nimport type {\n\tDisplayBatch,\n\tMaterialAppearanceOptions,\n\tMaterialGroup,\n\tMeshBatchParsingOptions,\n\tMeshMetadata,\n\tSerializableMaterial\n} from './types.js';\ninterface ParseTelemetry {\n\tparseTime?: number;\n\tperfStart?: number;\n}\n\n/**\n * Parses a batched mesh JSON and creates Three.js meshes. The geometry payload is the binary\n * \"SLVA\" blob produced by the C# `BinaryGeometryWriter`, base64-encoded into the outer JSON\n * envelope — `JSON.parse`s the small envelope, then hands the blob to `parseBinaryMeshBatch`\n * without ever turning it into a string.\n *\n * An invalid JSON envelope logs and returns `[]` (genuinely absent data). A corrupt, truncated, or\n * unsupported *blob* throws instead of silently rendering an empty scene.\n *\n * @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.\n */\nexport async function parseMeshBatch(\n\tbatchJson: string,\n\toptions?: MeshBatchParsingOptions\n): Promise<THREE.Mesh[]> {\n\tconst { debug = false } = options ?? {};\n\n\tconst perfStart = debug ? performance.now() : 0;\n\n\t// Narrow catch: only the envelope JSON.parse is allowed to degrade to []. Blob parse errors\n\t// from parseMeshBatchObject propagate — see that entry point's contract.\n\tlet batch: DisplayBatch;\n\tconst parseStart = performance.now();\n\ttry {\n\t\tbatch = JSON.parse(batchJson);\n\t} catch (error) {\n\t\tgetLogger().error('Error parsing mesh batch envelope JSON:', error);\n\t\treturn [];\n\t}\n\tconst parseTime = performance.now() - parseStart;\n\n\treturn await parseMeshBatchObject(batch, options, { parseTime, perfStart });\n}\n\n/**\n * Parses a DisplayBatch object and creates Three.js meshes from its mesh blob.\n *\n * Synchronous internally — `parseBinaryMeshBatch` does no IO, just typed-array views over the\n * blob. Stays `async` so callers don't need to change shape if parsing moves into a worker later.\n *\n * @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.\n */\nexport async function parseMeshBatchObject(\n\tbatch: DisplayBatch,\n\toptions?: MeshBatchParsingOptions,\n\t/** @internal Timings threaded from an outer entry point — not a caller option. */\n\ttelemetry?: ParseTelemetry\n): Promise<THREE.Mesh[]> {\n\tconst { mergeByMaterial = true, debug = false, material } = options ?? {};\n\tconst { parseTime = 0, perfStart = debug ? performance.now() : 0 } = telemetry ?? {};\n\n\tif (!batch.compressedData) {\n\t\t// Items-only or empty batch — the one entry-point path that legitimately yields [] rather\n\t\t// than throwing.\n\t\treturn [];\n\t}\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch or no worker support).\n\tconst workerMeshes = await tryBuildViaWorker(batch.compressedData, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial,\n\t\tfallback: {\n\t\t\tmaterials: batch.materials,\n\t\t\tgroups: batch.groups,\n\t\t\tsourceComponentId: batch.sourceComponentId\n\t\t}\n\t});\n\tif (workerMeshes) return workerMeshes;\n\n\tconst decodeStart = performance.now();\n\tconst parsed = parseBinaryMeshBatch(batch.compressedData);\n\tconst decodeTime = performance.now() - decodeStart;\n\n\tconst blobBytes = debug ? approximateBase64DecodedBytes(batch.compressedData) : 0;\n\n\treturn buildMeshesFromParsed(parsed, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial,\n\t\tparseTime,\n\t\tdecodeTime,\n\t\tperfStart,\n\t\tblobBytes,\n\t\tfallback: {\n\t\t\tmaterials: batch.materials,\n\t\t\tgroups: batch.groups,\n\t\t\tsourceComponentId: batch.sourceComponentId\n\t\t}\n\t});\n}\n\n/**\n * Parses a raw binary mesh batch blob (SLVA wire format) and creates Three.js meshes.\n *\n * Use this entry point when the blob arrives as a binary WebSocket frame rather than inside a JSON\n * envelope — the blob is self-describing, with materials, groups, and `sourceComponentId` coming\n * from its embedded metadata header.\n *\n * @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.\n */\nexport async function parseMeshBatchBlob(\n\tblob: ArrayBuffer | Uint8Array,\n\toptions?: MeshBatchParsingOptions\n): Promise<THREE.Mesh[]> {\n\tconst { mergeByMaterial = true, debug = false, material } = options ?? {};\n\n\tconst perfStart = debug ? performance.now() : 0;\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch or no worker support).\n\tconst workerMeshes = await tryBuildViaWorker(blob, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial\n\t});\n\tif (workerMeshes) return workerMeshes;\n\n\tconst decodeStart = performance.now();\n\tconst parsed = parseBinaryMeshBatch(blob);\n\tconst decodeTime = performance.now() - decodeStart;\n\n\tconst blobBytes = blob.byteLength;\n\n\treturn buildMeshesFromParsed(parsed, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial,\n\t\tparseTime: 0,\n\t\tdecodeTime,\n\t\tperfStart,\n\t\tblobBytes\n\t});\n}\n\ninterface BuildOptions {\n\tmergeByMaterial: boolean;\n\tdebug: boolean;\n\tmaterial?: MaterialAppearanceOptions;\n\tparseTime: number;\n\tdecodeTime: number;\n\tperfStart: number;\n\tblobBytes: number;\n\t/** Outer-envelope fallback used when the blob's metadata is missing fields. */\n\tfallback?: {\n\t\tmaterials?: SerializableMaterial[];\n\t\tgroups?: MaterialGroup[];\n\t\tsourceComponentId?: string;\n\t};\n}\n\nfunction buildMeshesFromParsed(\n\tparsed: ParsedBinaryMeshBatch,\n\topts: BuildOptions\n): Promise<THREE.Mesh[]> {\n\tconst {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial: materialAppearance,\n\t\tparseTime,\n\t\tdecodeTime,\n\t\tperfStart,\n\t\tblobBytes,\n\t\tfallback\n\t} = opts;\n\n\tconst materialsSrc = parsed.metadata.materials ?? fallback?.materials ?? [];\n\tconst groups = parsed.metadata.groups ?? fallback?.groups ?? [];\n\t// Envelope sourceComponentId wins over the blob's embedded one: the blob bakes in the id at\n\t// encode time, but a reloaded part (e.g. a .slvm mesh file instanced many times) re-stamps a fresh id on\n\t// the envelope so web pick identity stays distinct per placement. The blob value only applies\n\t// to the raw-blob transport, which has no envelope.\n\tconst sourceComponentId = fallback?.sourceComponentId ?? parsed.metadata.sourceComponentId;\n\n\tconst isFloat32 = (parsed.flags & FLAG_FLOAT32) !== 0;\n\n\t// Group metadata is used arithmetically below — unchecked, a bad vertexStart/indexStart wraps\n\t// rebased indices into a Uint32Array, `subarray` silently clamps, and an out-of-range\n\t// materialId feeds `undefined` into `new THREE.Mesh`. Fail the parse instead of corrupting\n\t// the render silently.\n\tvalidateGroupMetadata(\n\t\tgroups,\n\t\tmaterialsSrc.length,\n\t\tparsed.vertices.length / 3,\n\t\tparsed.indices.length\n\t);\n\n\t// Dequantize once up front into a single Float32Array — downstream code (per-group merging,\n\t// computeVertexNormals, ground-offset) expects world-unit floats, and one linear pass over the\n\t// int16 buffer beats doing it per group.\n\tconst worldVertices = isFloat32\n\t\t? (parsed.vertices as Float32Array)\n\t\t: dequantizeInt16(parsed.vertices as Int16Array, parsed.origin, parsed.scale);\n\n\tif (debug) {\n\t\tconst wireBytes = parsed.vertices.byteLength + parsed.indices.byteLength;\n\t\tgetLogger().debug('Mesh Batch Stats:');\n\t\tgetLogger().debug(` Materials: ${materialsSrc.length} | Groups: ${groups.length}`);\n\t\tgetLogger().debug(\n\t\t\t` Vertices: ${parsed.vertices.length / 3} | Indices: ${parsed.indices.length}`\n\t\t);\n\t\tgetLogger().debug(` Format: ${isFloat32 ? 'float32' : 'int16 quantized'}`);\n\t\tgetLogger().debug(\n\t\t\t` Blob: ${(blobBytes / 1024 / 1024).toFixed(2)} MB | Geometry on wire: ${(wireBytes / 1024 / 1024).toFixed(2)} MB`\n\t\t);\n\t}\n\n\tconst meshCreateStart = performance.now();\n\t// Vertex colors are batch-wide when present — meshes without real colors carry a white fill,\n\t// which multiplies to identity — so the material enables vertexColors unconditionally.\n\tconst materials = materialsSrc.map((m) =>\n\t\tcreateMaterial(m, {\n\t\t\tvertexColors: parsed.colors != null,\n\t\t\tappearance: materialAppearance\n\t\t})\n\t);\n\n\tconst meshes: THREE.Mesh[] = [];\n\n\tfor (const group of groups) {\n\t\tif (mergeByMaterial && group.meshes.length > 1) {\n\t\t\tconst mergedMesh = createMergedMesh(\n\t\t\t\tgroup,\n\t\t\t\tworldVertices,\n\t\t\t\tparsed.indices,\n\t\t\t\tmaterials,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\n\t\t\tmergedMesh.userData.sourceComponentId = sourceComponentId ?? null;\n\t\t\tmeshes.push(mergedMesh);\n\t\t} else {\n\t\t\tconst individualMeshes = createIndividualMeshes(\n\t\t\t\tgroup,\n\t\t\t\tworldVertices,\n\t\t\t\tparsed.indices,\n\t\t\t\tmaterials,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\n\t\t\tfor (const mesh of individualMeshes) {\n\t\t\t\tmesh.userData.sourceComponentId = sourceComponentId ?? null;\n\t\t\t}\n\t\t\tmeshes.push(...individualMeshes);\n\t\t}\n\t}\n\n\tconst meshCreateTime = performance.now() - meshCreateStart;\n\n\tif (debug) {\n\t\tconst totalTime = performance.now() - perfStart;\n\t\tgetLogger().debug('Performance:');\n\t\tif (parseTime > 0) getLogger().debug(` Parse JSON: ${parseTime.toFixed(2)}ms`);\n\t\tgetLogger().debug(` Decode binary: ${decodeTime.toFixed(2)}ms`);\n\t\tgetLogger().debug(` Create Meshes: ${meshCreateTime.toFixed(2)}ms`);\n\t\tgetLogger().debug(` Total: ${totalTime.toFixed(2)}ms`);\n\t}\n\n\treturn Promise.resolve(meshes);\n}\n\n// ============================================================================\n// OFF-THREAD ASSEMBLY\n// ============================================================================\n\ninterface WorkerPathOptions {\n\tmergeByMaterial: boolean;\n\tdebug: boolean;\n\tmaterial?: MaterialAppearanceOptions;\n\tfallback?: BuildOptions['fallback'];\n}\n\n/**\n * Attempts the off-thread build. Returns the finished meshes, or `null` when the worker path\n * doesn't apply (no Worker, small batch, worker crashed) — the caller then runs the synchronous\n * path. Malformed-blob/metadata errors throw either way, matching the entry points' contract.\n *\n * The worker always assembles and fingerprints every geometry, even when the main thread ends up\n * preferring an existing cached geometry over the returned buffers. That's fine: cache hits skip\n * the GPU re-upload, so the wasted worker CPU is off the critical path by definition.\n */\nasync function tryBuildViaWorker(\n\tinput: ArrayBuffer | Uint8Array | string,\n\topts: WorkerPathOptions\n): Promise<THREE.Mesh[] | null> {\n\tif (typeof Worker === 'undefined') return null;\n\n\tconst raw = parseBinaryMeshBatchRaw(input);\n\tif (raw.indexData.length / 3 < ASSEMBLY_WORKER_MIN_TRIANGLES) return null;\n\tconst worker = getAssemblyWorker();\n\tif (!worker) return null;\n\n\tconst materialsSrc = raw.metadata.materials ?? opts.fallback?.materials ?? [];\n\tconst groups = raw.metadata.groups ?? opts.fallback?.groups ?? [];\n\tconst sourceComponentId = opts.fallback?.sourceComponentId ?? raw.metadata.sourceComponentId;\n\tvalidateGroupMetadata(groups, materialsSrc.length, raw.vertexCount, raw.indexData.length);\n\n\t// Same job branching as buildMeshesFromParsed, with a parallel ref list to unwrap results by index.\n\tinterface JobRef {\n\t\tkind: 'merged' | 'single';\n\t\tgroup: MaterialGroup;\n\t\tmeshMeta?: MeshMetadata;\n\t}\n\tconst windowOf = (m: MeshMetadata): AssemblyWindow => ({\n\t\tvertexStart: m.vertexStart,\n\t\tvertexCount: m.vertexCount,\n\t\tindexStart: m.indexStart,\n\t\tindexCount: m.indexCount\n\t});\n\tconst jobs: AssemblyJob[] = [];\n\tconst jobRefs: JobRef[] = [];\n\tfor (const group of groups) {\n\t\tif (opts.mergeByMaterial && group.meshes.length > 1) {\n\t\t\tjobs.push({ kind: 'merged', windows: group.meshes.map(windowOf) });\n\t\t\tjobRefs.push({ kind: 'merged', group });\n\t\t} else {\n\t\t\tfor (const meshMeta of group.meshes) {\n\t\t\t\tjobs.push({ kind: 'single', windows: [windowOf(meshMeta)] });\n\t\t\t\tjobRefs.push({ kind: 'single', group, meshMeta });\n\t\t\t}\n\t\t}\n\t}\n\n\t// vertexData/indexData alias the caller's blob buffer — copy before transferring so the\n\t// transfer can't detach it. UV/color arrays are already fresh copies and transfer directly.\n\tconst vertexData = raw.vertexData.slice();\n\tconst indexData = raw.indexData.slice();\n\tconst transfer: Transferable[] = [vertexData.buffer, indexData.buffer];\n\tif (raw.uvs) transfer.push(raw.uvs.buffer);\n\tif (raw.colors) transfer.push(raw.colors.buffer);\n\n\tlet assembled: AssembledGeometry[];\n\ttry {\n\t\tassembled = await requestAssembly(\n\t\t\tworker,\n\t\t\t{\n\t\t\t\tvertexData,\n\t\t\t\tisFloat32: raw.isFloat32,\n\t\t\t\tdeltaEncoded: raw.deltaEncoded,\n\t\t\t\torigin: raw.origin,\n\t\t\t\tscale: raw.scale,\n\t\t\t\tindexData,\n\t\t\t\tuvs: raw.uvs,\n\t\t\t\tcolors: raw.colors,\n\t\t\t\tjobs\n\t\t\t},\n\t\t\ttransfer\n\t\t);\n\t} catch (error) {\n\t\tgetLogger().warn('Mesh assembly worker failed; falling back to main-thread parse.', error);\n\t\treturn null;\n\t}\n\tif (assembled.length !== jobs.length) return null; // protocol mismatch → fall back to sync path\n\n\tconst materials = materialsSrc.map((m) =>\n\t\tcreateMaterial(m, { vertexColors: raw.colors != null, appearance: opts.material })\n\t);\n\n\tconst meshes: THREE.Mesh[] = [];\n\tfor (let i = 0; i < assembled.length; i++) {\n\t\tconst result = assembled[i]!;\n\t\tconst ref = jobRefs[i]!;\n\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(result.positions, 3));\n\t\tgeometry.setAttribute('normal', new THREE.BufferAttribute(result.normals, 3));\n\t\tgeometry.setIndex(new THREE.BufferAttribute(result.indices, 1));\n\t\tif (result.uvs) geometry.setAttribute('uv', new THREE.BufferAttribute(result.uvs, 2));\n\t\tif (result.colors) {\n\t\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(result.colors, 3, true));\n\t\t}\n\n\t\tconst mesh =\n\t\t\tref.kind === 'merged'\n\t\t\t\t? finalizeMergedMesh(geometry, ref.group, materials)\n\t\t\t\t: finalizeSingleMesh(geometry, ref.meshMeta!, ref.group, materials);\n\t\tmesh.userData.sourceComponentId = sourceComponentId ?? null;\n\t\tmeshes.push(mesh);\n\t}\n\n\tif (opts.debug) {\n\t\tgetLogger().debug(\n\t\t\t`Mesh batch assembled off-thread: ${meshes.length} meshes, ${raw.indexData.length / 3} triangles`\n\t\t);\n\t}\n\treturn meshes;\n}\n\n// ============================================================================\n// DEBUG HELPERS\n// ============================================================================\n\nfunction approximateBase64DecodedBytes(base64: string): number {\n\treturn Math.floor((base64.length * 3) / 4);\n}\n","import * as THREE from 'three';\n\nimport { applyOffset, computeCombinedBoundingBox, getLogger } from '../../shared/index.js';\n\nimport { parseDisplayItems } from '../display-items/display-items-parser.js';\n\nimport { parseMeshBatchObject } from './batch-parser.js';\n\nimport type { DisplayDataItem, DisplayComputeResponse } from './response-envelope.js';\nimport type { DisplayBatch, MeshExtractionOptions, MeshBatchParsingOptions } from './types.js';\n\n// Constants\n\n/**\n * Metres per model unit, keyed by Rhino `UnitSystem` name (the `modelunits` string on the compute\n * response). Imperial factors are the exact international definitions. Units missing from this\n * table scale by 1 and log a one-time warning — see {@link getScaleFactor}.\n */\nexport const SCALE_FACTORS: Record<string, number> = {\n\t// Metric\n\tAngstroms: 1e-10,\n\tNanometers: 1e-9,\n\tMicrons: 1e-6,\n\tMillimeters: 1e-3,\n\tCentimeters: 1e-2,\n\tDecimeters: 0.1,\n\tMeters: 1,\n\tDekameters: 10,\n\tHectometers: 100,\n\tKilometers: 1000,\n\tMegameters: 1e6,\n\tGigameters: 1e9,\n\t// Imperial (exact: 1 inch = 0.0254 m)\n\tMicroinches: 0.0254e-6,\n\tMils: 0.0254e-3,\n\tInches: 0.0254,\n\tFeet: 0.3048,\n\tYards: 0.9144,\n\tMiles: 1609.344,\n\tNauticalMiles: 1852\n};\n\nconst DISPLAY_COMPONENT_TYPE = 'Display';\nconst DISPLAY_BATCH_TYPE = 'DisplayBatch';\n\n/**\n * True when a wire `type` denotes a Display payload: one of its dot-separated tokens is exactly\n * `Display` or `DisplayBatch`. Matches the bare `Display` used by older servers and the namespaced\n * `Selva.GH.Features.Display.Services.DisplayBatch`, but not e.g. `System.DisplayText` — matching\n * on tokens rather than substring avoids misrouting an unrelated type that merely contains \"Display\".\n */\nfunction isDisplayItemType(type: string): boolean {\n\tconst tokens = type.split('.');\n\treturn tokens.includes(DISPLAY_COMPONENT_TYPE) || tokens.includes(DISPLAY_BATCH_TYPE);\n}\n\n/** Unknown-unit names already warned about, so a per-solve parse doesn't spam the log. */\nconst warnedUnknownUnits = new Set<string>();\n\n/**\n * Extracts display meshes and items from a Grasshopper WebDisplay compute response: decompresses,\n * scales to meters, and optionally grounds them. Requires the VektorNode Rhino.Compute fork.\n *\n * Synchronous internally (large batches block the UI for their duration); `async` only so the\n * shape can stay stable if parsing moves off-thread later.\n *\n * @throws Rethrows unexpected errors after attempting to dispose any created meshes.\n */\nexport async function getThreeMeshesFromComputeResponse(\n\tdata: DisplayComputeResponse,\n\toptions?: MeshExtractionOptions\n): Promise<THREE.Object3D[]> {\n\tconst startTime = performance.now();\n\tconst objects: THREE.Object3D[] = [];\n\n\tconst {\n\t\tallowScaling = true,\n\t\t// Defaults to false so picked/measured values match the GH definition's own coordinates\n\t\t// rather than shifting per transport.\n\t\tallowAutoPosition = false,\n\t\tgroundAxis = 'z',\n\t\tdebug = false,\n\t\tparsing: parsingOptions = {}\n\t} = options ?? {};\n\n\ttry {\n\t\tconst scaleFactor = allowScaling ? getScaleFactor(data.modelunits) : 1;\n\t\tawait extractDisplayFromData(data, objects, scaleFactor, parsingOptions, debug);\n\n\t\tif (allowAutoPosition) {\n\t\t\tapplyGroundOffset(objects, groundAxis);\n\t\t}\n\n\t\treturn objects;\n\t} catch (error) {\n\t\thandleError(error, objects);\n\t\tthrow error;\n\t} finally {\n\t\tif (debug) {\n\t\t\tlogProcessingTime(startTime);\n\t\t}\n\t}\n}\n\n/**\n * Gets the metres-per-unit scale factor for a Rhino unit name. Unknown units fall back to 1 (no\n * scaling) with a one-time warning — a kilometers model rendering 1000x off should at least say why.\n */\nfunction getScaleFactor(modelUnits: string): number {\n\tconst factor = SCALE_FACTORS[modelUnits];\n\tif (factor !== undefined) {\n\t\treturn factor;\n\t}\n\tif (!warnedUnknownUnits.has(modelUnits)) {\n\t\twarnedUnknownUnits.add(modelUnits);\n\t\tgetLogger().warn(\n\t\t\t`Unknown Rhino model unit \"${modelUnits}\" — geometry will not be scaled (factor 1). ` +\n\t\t\t\t`Known units: ${Object.keys(SCALE_FACTORS).join(', ')}.`\n\t\t);\n\t}\n\treturn 1;\n}\n\nasync function extractDisplayFromData(\n\tdata: DisplayComputeResponse,\n\tobjects: THREE.Object3D[],\n\tscaleFactor: number,\n\tparsingOptions: MeshBatchParsingOptions,\n\tdebug: boolean\n): Promise<void> {\n\tfor (const value of data.values) {\n\t\tconst innerTree = value.InnerTree;\n\n\t\tfor (const path in innerTree) {\n\t\t\tconst branch = innerTree[path];\n\t\t\tif (!branch) continue;\n\n\t\t\tawait processDataBranch(branch, objects, scaleFactor, parsingOptions, debug);\n\t\t}\n\t}\n}\n\n/** Extracts a DisplayBatch's meshes (binary blob) and items (curves/points JSON) from one data branch. */\nasync function processDataBranch(\n\tbranch: DisplayDataItem[],\n\tobjects: THREE.Object3D[],\n\tscaleFactor: number,\n\tparsingOptions: MeshBatchParsingOptions,\n\tdebug: boolean\n): Promise<void> {\n\tfor (const item of branch) {\n\t\tif (!isDisplayItemType(item.type)) continue;\n\n\t\tconst mergedParsingOptions = {\n\t\t\tmergeByMaterial: true,\n\t\t\tdebug: false,\n\t\t\t...parsingOptions\n\t\t};\n\n\t\t// Parsed once and shared: item.data is a multi-MB base64 SLVA blob, so parsing it twice (once\n\t\t// for the mesh parser, once for the item extractor) would double both CPU and string memory.\n\t\tconst batch = extractBatch(item.data);\n\t\tif (!batch) {\n\t\t\tgetLogger().error('Error parsing display batch envelope: invalid JSON');\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst batchMeshes = await parseMeshBatchObject(batch, mergedParsingOptions);\n\n\t\tconst batchItems = parseDisplayItems(batch.items);\n\n\t\tconst batchObjects: THREE.Object3D[] = [...batchMeshes, ...batchItems];\n\n\t\t// Meshes and items share one scale factor so they end up in the same frame.\n\t\tif (scaleFactor !== 1) {\n\t\t\tfor (const obj of batchObjects) {\n\t\t\t\tobj.scale.set(scaleFactor, scaleFactor, scaleFactor);\n\t\t\t}\n\t\t}\n\n\t\tobjects.push(...batchObjects);\n\n\t\tif (debug) {\n\t\t\tgetLogger().debug(\n\t\t\t\t`Extracted ${batchMeshes.length} meshes and ${batchItems.length} items from batch`\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** Resolves `item.data` to a parsed DisplayBatch, tolerating either an already-parsed object or a JSON string. */\nfunction extractBatch(data: unknown): DisplayBatch | undefined {\n\treturn typeof data === 'string' ? safeParse(data) : (data as DisplayBatch | undefined);\n}\n\nfunction safeParse(s: string): DisplayBatch | undefined {\n\ttry {\n\t\treturn JSON.parse(s) as DisplayBatch;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Drops objects so their lowest point sits on the ground plane. `axis` isn't hardcoded to `z`\n * because subtracting `min.z` on a host with a non-default `sceneUp` would shove content sideways\n * instead of down.\n */\nfunction applyGroundOffset(meshes: THREE.Object3D[], axis: 'x' | 'y' | 'z'): void {\n\tif (meshes.length === 0) return;\n\n\tconst combinedBoundingBox = computeCombinedBoundingBox(meshes);\n\tapplyOffset(meshes, combinedBoundingBox.min[axis], axis);\n}\n\nfunction handleError(error: unknown, meshes: THREE.Object3D[]): void {\n\tgetLogger().error('An unexpected error occurred:', error);\n\tdisposeMeshes(meshes);\n}\n\nfunction disposeMeshes(meshes: THREE.Object3D[]): void {\n\tfor (const obj of meshes) {\n\t\tconst mesh = obj as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tif (mesh.geometry) {\n\t\t\tmesh.geometry.dispose();\n\t\t}\n\n\t\tif (mesh.material) {\n\t\t\tif (Array.isArray(mesh.material)) {\n\t\t\t\tmesh.material.forEach((material) => material.dispose());\n\t\t\t} else {\n\t\t\t\tmesh.material.dispose();\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction logProcessingTime(startTime: number): void {\n\tconst elapsed = performance.now() - startTime;\n\tgetLogger().info('Time to process meshes:', `${elapsed.toFixed(2)}ms`);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parse.js","names":["unhandled"],"sources":["../src/shared/encoding.ts","../src/parse/mesh-policy.ts","../src/parse/display-items/items/appearance.ts","../src/parse/display-items/items/curves.ts","../src/parse/display-items/items/points.ts","../src/parse/display-items/display-items-parser.ts","../src/parse/webdisplay/binary/header.ts","../src/parse/webdisplay/binary/geometry.ts","../src/parse/webdisplay/binary/textures.ts","../src/parse/webdisplay/binary-parser.ts","../src/parse/webdisplay/mesh-assembly.ts","../src/parse/webdisplay/batch/assembly-worker.ts","../src/parse/webdisplay/apply-texture.ts","../src/parse/webdisplay/batch/materials.ts","../src/parse/webdisplay/batch/metadata.ts","../src/parse/webdisplay/batch/merge.ts","../src/parse/webdisplay/batch-parser.ts","../src/parse/webdisplay/webdisplay-parser.ts"],"sourcesContent":["// 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","// Mesh ownership policy for `@selvajs/solve`'s result memo: `SolveResult<TMesh>` is opaque to the\n// memo, so clone/release are injected here instead. The viewer disposes whatever it last rendered\n// (`clearScene`), so a memo handing out live references would serve a disposed object on the next\n// hit — `clone` copies geometry explicitly (`Object3D.clone()` shares it by reference) but leaves\n// materials shared, since `clearScene` already spares `SHARED_MATERIALS` singletons and recompiling\n// per-mesh materials as shaders is expensive.\n\nimport * as THREE from 'three';\n\nimport { disposeObjectTree } from '../shared/index.js';\n\nexport function cloneSceneObjects(meshes: THREE.Object3D[]): THREE.Object3D[] {\n\treturn meshes.map((root) => {\n\t\tconst copy = root.clone(true);\n\t\tconst sources: THREE.Object3D[] = [];\n\t\troot.traverse((child) => sources.push(child));\n\t\tlet i = 0;\n\t\tcopy.traverse((child) => {\n\t\t\tconst source = sources[i++] as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tconst target = child as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tif (!source.geometry) return;\n\t\t\ttarget.geometry = source.geometry.clone();\n\t\t});\n\t\treturn copy;\n\t});\n}\n\n/** Skips materials — the memo never owns those; see the file header. */\nexport function releaseSceneObjects(meshes: THREE.Object3D[]): void {\n\tmeshes.forEach((root) => disposeObjectTree(root, { materials: false }));\n}\n\n/** Structurally, not nominally, typed as `@selvajs/solve/client`'s `MeshPolicy<THREE.Object3D>` — avoids a dependency on solve. */\nexport const meshPolicy: {\n\tclone(meshes: THREE.Object3D[]): THREE.Object3D[];\n\trelease(meshes: THREE.Object3D[]): void;\n} = {\n\tclone: cloneSceneObjects,\n\trelease: releaseSceneObjects\n};\n","import * as THREE from 'three';\n\nexport const DEFAULT_COLOR = '#ffffff';\n\n/** Opacity < 1 flips `transparent` on. */\nexport function materialParams(\n\tcolor: string | undefined,\n\topacity: number | undefined\n): { color: THREE.Color; transparent: boolean; opacity: number } {\n\tconst resolved = opacity ?? 1;\n\treturn {\n\t\tcolor: new THREE.Color(color ?? DEFAULT_COLOR),\n\t\ttransparent: resolved < 1,\n\t\topacity: resolved\n\t};\n}\n","import { Line2 } from 'three/addons/lines/Line2.js';\nimport { LineGeometry } from 'three/addons/lines/LineGeometry.js';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\n\nimport { ErrorCodes, VisualizationError } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayCurve } from '../types';\n\nconst DEFAULT_LINE_WIDTH = 2;\n\n/** Two vertices — the shortest renderable polyline. */\nconst MIN_POSITIONS = 6;\n\n/**\n * Curves arrive tessellated: the backend sends `points`, this builds the line. Nothing decodes\n * geometry in the browser.\n *\n * Uses `Line2`/`LineMaterial` instead of `THREE.Line`: plain `THREE.Line` is hard-capped at 1px on\n * every major GPU backend, so `item.width` would go unhonoured. `Line2.onBeforeRender` sets\n * `LineMaterial`'s required `resolution`, so no renderer reference is needed here.\n *\n * @throws VisualizationError when the item has no `points` — see {@link curvePositions}.\n */\nexport function buildCurveLine(item: DisplayCurve): Line2 | null {\n\tconst positions = curvePositions(item);\n\tif (!positions) return null;\n\n\tconst geometry = new LineGeometry();\n\tgeometry.setPositions(positions);\n\n\t// @types/three's LineMaterial omits `linewidth`/`transparent`/`opacity` though all exist at runtime.\n\tconst params = materialParams(item.color, item.opacity);\n\tconst material = new LineMaterial({ color: params.color });\n\tconst styled = material as LineMaterial & {\n\t\tlinewidth: number;\n\t\ttransparent: boolean;\n\t\topacity: number;\n\t};\n\tstyled.linewidth = item.width ?? DEFAULT_LINE_WIDTH; // CSS px (worldUnits defaults false)\n\tstyled.transparent = params.transparent;\n\tstyled.opacity = params.opacity;\n\n\tconst line = new Line2(geometry, material);\n\tline.computeLineDistances();\n\tline.name = item.name;\n\tline.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'curve',\n\t\tmetadata: item.metadata\n\t};\n\treturn line;\n}\n\n/**\n * Flat `[x,y,z, …]`, or null for a degenerate curve — one of those can't abort the batch.\n *\n * A curve with no `points` **throws** instead. It means the definition was solved by a Display\n * component predating backend tessellation, which is a stale definition rather than one bad curve:\n * skipping would render a scene silently missing geometry, indistinguishable from a definition that\n * has no curves, with the fix nowhere in sight.\n */\nfunction curvePositions(item: DisplayCurve): number[] | null {\n\tif (!item.points) {\n\t\tthrow new VisualizationError(\n\t\t\t`Curve display item '${item.id}' has no tessellated points. It was produced by an ` +\n\t\t\t\t'outdated Display component — upgrade it in Grasshopper (Solution → Upgrade obsolete ' +\n\t\t\t\t'components) and re-save the definition.',\n\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t{ context: { itemId: item.id, kind: item.kind } }\n\t\t);\n\t}\n\n\treturn item.points.length >= MIN_POSITIONS ? item.points : null;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayPoint } from '../types';\n\nexport function buildPoint(item: DisplayPoint): THREE.Points | null {\n\t// `position` comes off the wire — don't trust the declared type without validating.\n\tconst { position } = item as { position?: { X?: unknown; Y?: unknown; Z?: unknown } };\n\tif (\n\t\t!position ||\n\t\ttypeof position.X !== 'number' ||\n\t\t!Number.isFinite(position.X) ||\n\t\ttypeof position.Y !== 'number' ||\n\t\t!Number.isFinite(position.Y) ||\n\t\ttypeof position.Z !== 'number' ||\n\t\t!Number.isFinite(position.Z)\n\t) {\n\t\tgetLogger().warn(\n\t\t\t`Skipping point display item with missing or non-finite position (id: ${String(item.id)}).`\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute(\n\t\t'position',\n\t\tnew THREE.Float32BufferAttribute([position.X, position.Y, position.Z], 3)\n\t);\n\n\tconst material = new THREE.PointsMaterial({\n\t\t...materialParams(item.color, item.opacity),\n\t\tsize: 6,\n\t\tsizeAttenuation: false\n\t});\n\n\tconst points = new THREE.Points(geometry, material);\n\tpoints.name = item.name;\n\tpoints.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'point',\n\t\tmetadata: item.metadata\n\t};\n\treturn points;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { buildCurveLine } from './items/curves.js';\nimport { buildPoint } from './items/points.js';\n\nimport type { DisplayItem } from './types';\n\n/**\n * Builds THREE objects for the batch's non-mesh items.\n *\n * @throws VisualizationError when a curve predates backend tessellation, so a stale definition\n * surfaces as an actionable error instead of a scene quietly missing its curves. Every other\n * unrenderable item is logged and skipped.\n */\nexport function parseDisplayItems(items: DisplayItem[] | undefined): THREE.Object3D[] {\n\tif (!items || items.length === 0) return [];\n\n\tconst objects: THREE.Object3D[] = [];\n\n\tfor (const item of items) {\n\t\tswitch (item.kind) {\n\t\t\tcase 'curve': {\n\t\t\t\tconst line = buildCurveLine(item);\n\t\t\t\tif (line) objects.push(line);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'point': {\n\t\t\t\tconst point = buildPoint(item);\n\t\t\t\tif (point) objects.push(point);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// Forces a compile error if a new DisplayItem kind is added without a case above.\n\t\t\t\tconst unhandled: never = item;\n\t\t\t\tconst unknown = unhandled as { kind?: string };\n\t\t\t\tgetLogger().warn(`Skipping unknown display item kind: ${String(unknown.kind)}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn objects;\n}\n","import type { MaterialGroup, SerializableMaterial } from '../types.js';\n\n// ============================================================================\n// WIRE FORMAT CONSTANTS\n// ============================================================================\n\n/** \"SLVA\" little-endian — an uncompressed mesh blob. */\nexport const BINARY_MESH_MAGIC = 0x41564c53;\n/**\n * \"SLVZ\" little-endian — an optional raw-DEFLATE container around a SLVA blob (applied by the\n * plugin when it shrinks the payload). Layout: `[4] magic=SLVZ | [4] uncompressedLen(u32) |\n * [N] raw-deflate stream of the SLVA blob`.\n */\nexport const COMPRESSED_MESH_MAGIC = 0x5a564c53;\n/**\n * Current writer version. v2 added FLAG_UINT16_INDICES; v3 added FLAG_DELTA_ENCODED.\n */\nexport const BINARY_MESH_VERSION = 3;\n/**\n * Oldest wire version this parser still decodes. Each version only added a flag bit, so the\n * flag-driven read path handles every older blob unchanged — needed since persisted/cached blobs\n * (saved `.gh` files, `.slvm`/`.dmf` mesh files, cached compute results) must stay decodable after upgrade.\n */\nexport const MIN_SUPPORTED_VERSION = 1;\n/** Bit 0 of the geometry flags word: 0 = int16 quantized, 1 = float32 raw. */\nexport const FLAG_FLOAT32 = 0x1;\n/** Bit 1 of the geometry flags word: 0 = uint32 indices, 1 = uint16 indices. */\nexport const FLAG_UINT16_INDICES = 0x2;\n/**\n * Bit 2 of the geometry flags word: int16 vertex components and indices are stored as wrapped\n * per-component deltas from their predecessor, zigzag-mapped to unsigned (float32 vertices are\n * never filtered). Deltas of welded meshes concentrate near zero, so the SLVZ DEFLATE pass\n * compresses far better. Decoding reverses the filter with a running prefix sum.\n */\nexport const FLAG_DELTA_ENCODED = 0x4;\n/**\n * Bit 3: a UV chunk trails the index block. Layout: `uvFormat(u32: 0=uint16 quantized, 1=float32)\n * | uvOrigin(2×f64) | uvScale(2×f64) | data`, element count implied by vertexCount. Quantized UVs\n * reconstruct as `uv = origin + q * scale` (q unsigned in [0, 65535]), delta+zigzag filtered per\n * component (independent u/v predictors) iff FLAG_DELTA_ENCODED; float32 UVs are never filtered.\n * Absent flag = absent chunk, so untextured blobs are byte-identical to pre-chunk writers.\n */\nexport const FLAG_HAS_UVS = 0x8;\n/**\n * Bit 4: a vertex-color chunk trails the index block (after the UV chunk when both present).\n * Layout: `uint8 rgb[vertexCount*3]`, delta+zigzag filtered per channel (wrapped 8-bit, independent\n * r/g/b predictors) iff FLAG_DELTA_ENCODED.\n */\nexport const FLAG_HAS_VERTEX_COLORS = 0x10;\n\n/** uvFormat value inside the UV chunk: uint16 quantized. */\nexport const UV_FORMAT_UINT16 = 0;\n/** uvFormat value inside the UV chunk: raw float32. */\nexport const UV_FORMAT_FLOAT32 = 1;\n\nexport const HEADER_PREAMBLE_BYTES = 4 /* magic */ + 4 /* version */ + 4; /* metadataLen */\nexport const GEOMETRY_HEADER_BYTES =\n\t4 /* flags */ + 24 /* origin (3 x f64) */ + 24 /* scale (3 x f64) */ + 4; /* vertexCount */\n\n/**\n * Header fields use explicit-LE `DataView` reads, but the zero-copy geometry readers build\n * typed-array views in *host* byte order (every mainstream JS target is little-endian, and\n * per-element DataView reads would be far costlier on the hot geometry paths). This check makes\n * the assumption explicit: on a big-endian host the parser refuses to decode rather than return\n * byte-swapped garbage.\n */\nexport const HOST_IS_LITTLE_ENDIAN = new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;\n\n// ============================================================================\n// PARSED TYPES\n// ============================================================================\n\n/** Mesh-blob subset of `DisplayBatch` minus `compressedData` (circular — the blob can't embed itself). */\nexport interface BinaryMeshMetadata {\n\tmaterials: SerializableMaterial[];\n\tgroups: MaterialGroup[];\n\tsourceComponentId?: string;\n}\n\n/**\n * Result of parsing a binary mesh blob.\n *\n * `vertices`/`indices` hold absolute (unfiltered) values. For pre-v3 blobs they're zero-copy\n * typed-array views over the original `ArrayBuffer` — don't mutate the buffer, or call `.slice()`\n * to detach. Delta-encoded blobs (FLAG_DELTA_ENCODED) decode into freshly allocated arrays instead.\n *\n * `uvs`/`colors` are the optional trailing chunks (FLAG_HAS_UVS / FLAG_HAS_VERTEX_COLORS), null\n * when absent. UVs are dequantized to absolute Float32 (u,v per vertex) — ready for\n * `BufferAttribute(uvs, 2)`. Colors are raw r,g,b bytes per vertex, for a normalized\n * `BufferAttribute(colors, 3, true)`.\n */\nexport interface ParsedBinaryMeshBatch {\n\tmetadata: BinaryMeshMetadata;\n\tflags: number;\n\tvertices: Int16Array | Float32Array;\n\tindices: Uint16Array | Uint32Array;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\n// ============================================================================\n","import { inflateSync } from 'fflate';\n\nimport { decodeBase64ToBinary, VisualizationError, ErrorCodes } from '../../../shared/index.js';\n\nimport { COMPRESSED_MESH_MAGIC } from './header.js';\n\nexport function toUint8Array(input: ArrayBuffer | Uint8Array | string): Uint8Array {\n\tif (typeof input === 'string') {\n\t\treturn decodeBase64ToBinary(input);\n\t}\n\tif (input instanceof Uint8Array) {\n\t\treturn input;\n\t}\n\treturn new Uint8Array(input);\n}\n\n/**\n * If the blob is a SLVZ compressed container, inflate it back to the raw SLVA bytes; otherwise\n * return the input untouched. Detection is by the leading 4-byte magic, so an uncompressed SLVA\n * blob (or any pre-v3 payload) flows through unchanged.\n */\nexport function maybeDecompress(bytes: Uint8Array): Uint8Array {\n\tif (bytes.byteLength < 8) {\n\t\treturn bytes;\n\t}\n\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\tif (view.getUint32(0, true) !== COMPRESSED_MESH_MAGIC) {\n\t\treturn bytes;\n\t}\n\n\tconst uncompressedLen = view.getUint32(4, true);\n\tconst deflated = bytes.subarray(8);\n\n\t// Bound the wire-supplied length before allocating — a corrupt header could request ~4 GB.\n\t// DEFLATE won't expand past ~1000x.\n\tconst maxPlausibleLen = Math.max(deflated.byteLength * 1032 + 1024, 1 << 20);\n\tif (uncompressedLen > maxPlausibleLen) {\n\t\tthrow fail('SLVZ header declares an implausible uncompressed length', {\n\t\t\tuncompressedLen,\n\t\t\tdeflatedBytes: deflated.byteLength,\n\t\t\tmaxPlausibleLen\n\t\t});\n\t}\n\n\tlet out: Uint8Array;\n\ttry {\n\t\t// One byte of slack past the declared length: fflate trims its output to bytes actually\n\t\t// written, so a mismatched header lands off `uncompressedLen` either way — caught below\n\t\t// instead of silently decoding a zero-padded/truncated tail as geometry.\n\t\tout = inflateSync(deflated, { out: new Uint8Array(uncompressedLen + 1) });\n\t} catch (error) {\n\t\tthrow fail(\n\t\t\t`Failed to inflate SLVZ blob: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ uncompressedLen, deflatedBytes: deflated.byteLength }\n\t\t);\n\t}\n\n\tif (out.byteLength !== uncompressedLen) {\n\t\tthrow fail('SLVZ payload inflated to a different size than the header declares.', {\n\t\t\tdeclaredLen: uncompressedLen,\n\t\t\tactualLen: out.byteLength,\n\t\t\tdeflatedBytes: deflated.byteLength\n\t\t});\n\t}\n\n\treturn out;\n}\n\nexport function decodeUtf8(bytes: Uint8Array): string {\n\tif (typeof TextDecoder !== 'undefined') {\n\t\treturn new TextDecoder('utf-8').decode(bytes);\n\t}\n\t// Node fallback (Buffer is utf-8 by default).\n\tif (\n\t\ttypeof (globalThis as { Buffer?: { from(b: Uint8Array): { toString(enc: string): string } } })\n\t\t\t.Buffer !== 'undefined'\n\t) {\n\t\treturn (\n\t\t\tglobalThis as { Buffer: { from(b: Uint8Array): { toString(enc: string): string } } }\n\t\t).Buffer.from(bytes).toString('utf-8');\n\t}\n\tthrow new VisualizationError(\n\t\t'No UTF-8 decoder available in this environment.',\n\t\tErrorCodes.INVALID_STATE\n\t);\n}\n\nexport function readInt16Vertices(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Int16Array {\n\tif (count === 0) return new Int16Array(0);\n\tif (byteOffset % 2 === 0) {\n\t\treturn new Int16Array(buffer, byteOffset, count);\n\t}\n\t// Misaligned (rare — would require a wrapper Uint8Array with odd byteOffset).\n\tconst copy = new Uint8Array(count * 2);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 2));\n\treturn new Int16Array(copy.buffer);\n}\n\nexport function readFloat32Vertices(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Float32Array {\n\tif (count === 0) return new Float32Array(0);\n\tif (byteOffset % 4 === 0) {\n\t\treturn new Float32Array(buffer, byteOffset, count);\n\t}\n\tconst copy = new Uint8Array(count * 4);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 4));\n\treturn new Float32Array(copy.buffer);\n}\n\nexport function readUint16Array(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Uint16Array {\n\tif (count === 0) return new Uint16Array(0);\n\tif (byteOffset % 2 === 0) {\n\t\treturn new Uint16Array(buffer, byteOffset, count);\n\t}\n\tconst copy = new Uint8Array(count * 2);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 2));\n\treturn new Uint16Array(copy.buffer);\n}\n\nexport function readUint32Array(\n\tbuffer: ArrayBufferLike,\n\tbyteOffset: number,\n\tcount: number\n): Uint32Array {\n\tif (count === 0) return new Uint32Array(0);\n\tif (byteOffset % 4 === 0) {\n\t\treturn new Uint32Array(buffer, byteOffset, count);\n\t}\n\tconst copy = new Uint8Array(count * 4);\n\tcopy.set(new Uint8Array(buffer, byteOffset, count * 4));\n\treturn new Uint32Array(copy.buffer);\n}\n\n/**\n * Rejects blobs whose index stream references vertices past `vertexCount`. Downstream mesh\n * assembly trusts indices arithmetically (rebasing, `subarray` slicing), so an out-of-range index\n * would otherwise corrupt geometry silently instead of failing the parse. A uint16 index stream\n * can't exceed a vertex count above 65535, so that case skips the scan.\n */\nexport function validateIndicesInRange(\n\tindices: Uint16Array | Uint32Array,\n\tvertexCount: number\n): void {\n\tif (indices.length === 0) return;\n\tif (indices instanceof Uint16Array && vertexCount > 0xffff) return;\n\tfor (let i = 0; i < indices.length; i++) {\n\t\tif (indices[i]! >= vertexCount) {\n\t\t\tthrow fail('Index out of range of vertexCount.', {\n\t\t\t\tindexPosition: i,\n\t\t\t\tindexValue: indices[i],\n\t\t\t\tvertexCount\n\t\t\t});\n\t\t}\n\t}\n}\n\n/** Inverse of the writer's zigzag map: 0,1,2,3 → 0,-1,1,-2. */\nexport function unzigzag(zz: number): number {\n\treturn (zz >>> 1) ^ -(zz & 1);\n}\n\n/**\n * Undoes the v3 delta filter on the quantized vertex stream: each component is a zigzag-mapped,\n * wrapped 16-bit difference from the previous vertex's same component (independent x/y/z running\n * sums). `(x << 16) >> 16` reproduces the writer's int16 wrapping.\n */\nexport function decodeDeltaVertices(zigzagged: Uint16Array): Int16Array {\n\tconst out = new Int16Array(zigzagged.length);\n\tlet px = 0;\n\tlet py = 0;\n\tlet pz = 0;\n\tfor (let i = 0; i < zigzagged.length; i += 3) {\n\t\tpx = ((px + unzigzag(zigzagged[i]!)) << 16) >> 16;\n\t\tpy = ((py + unzigzag(zigzagged[i + 1]!)) << 16) >> 16;\n\t\tpz = ((pz + unzigzag(zigzagged[i + 2]!)) << 16) >> 16;\n\t\tout[i] = px;\n\t\tout[i + 1] = py;\n\t\tout[i + 2] = pz;\n\t}\n\treturn out;\n}\n\nexport function decodeDeltaIndices16(zigzagged: Uint16Array): Uint16Array {\n\tconst out = new Uint16Array(zigzagged.length);\n\tlet prev = 0;\n\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\tprev = (prev + unzigzag(zigzagged[i]!)) & 0xffff;\n\t\tout[i] = prev;\n\t}\n\treturn out;\n}\n\nexport function decodeDeltaIndices32(zigzagged: Uint32Array): Uint32Array {\n\tconst out = new Uint32Array(zigzagged.length);\n\tlet prev = 0;\n\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\tprev = (prev + unzigzag(zigzagged[i]!)) >>> 0;\n\t\tout[i] = prev;\n\t}\n\treturn out;\n}\n\nexport function fail(message: string, context: Record<string, unknown>): VisualizationError {\n\treturn new VisualizationError(message, ErrorCodes.VALIDATION_ERROR, { context });\n}\n","import { UV_FORMAT_FLOAT32 } from './header.js';\nimport { fail, readFloat32Vertices, readUint16Array, unzigzag } from './geometry.js';\n\n/** Byte size of the UV chunk header: uvFormat(u32) + uvOrigin(2×f64) + uvScale(2×f64). */\nconst UV_CHUNK_HEADER_BYTES = 4 + 16 + 16;\n\n/**\n * Parses the trailing UV chunk into absolute Float32 u,v pairs. Quantized UVs reconstruct as\n * `origin + q * scale` (unsigned q), undoing the per-component delta+zigzag filter when set;\n * float32 UVs are copied out as-is (never filtered).\n */\nexport function parseUvChunk(\n\tbytes: Uint8Array,\n\tview: DataView,\n\toffset: number,\n\tvertexCount: number,\n\tdeltaEncoded: boolean\n): { uvs: Float32Array; offset: number } {\n\tif (offset + UV_CHUNK_HEADER_BYTES > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read UV chunk header.', {\n\t\t\texpectedBytes: UV_CHUNK_HEADER_BYTES,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\n\tconst uvFormat = view.getUint32(offset, true);\n\toffset += 4;\n\tconst originU = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst originV = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleU = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleV = view.getFloat64(offset, true);\n\toffset += 8;\n\n\tconst componentCount = vertexCount * 2;\n\tconst useFloat32 = uvFormat === UV_FORMAT_FLOAT32;\n\tconst dataByteLength = componentCount * (useFloat32 ? 4 : 2);\n\tif (offset + dataByteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read UV chunk.', {\n\t\t\texpectedBytes: dataByteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tuvFormat,\n\t\t\tvertexCount\n\t\t});\n\t}\n\n\tconst absoluteOffset = bytes.byteOffset + offset;\n\tlet uvs: Float32Array;\n\tif (useFloat32) {\n\t\t// Copy (not view) so the attribute owns its memory like the quantized path.\n\t\tuvs = readFloat32Vertices(bytes.buffer, absoluteOffset, componentCount).slice();\n\t} else {\n\t\tconst raw = readUint16Array(bytes.buffer, absoluteOffset, componentCount);\n\t\tuvs = new Float32Array(componentCount);\n\t\tlet qu = 0;\n\t\tlet qv = 0;\n\t\tfor (let i = 0; i < componentCount; i += 2) {\n\t\t\tif (deltaEncoded) {\n\t\t\t\tqu = (qu + unzigzag(raw[i]!)) & 0xffff;\n\t\t\t\tqv = (qv + unzigzag(raw[i + 1]!)) & 0xffff;\n\t\t\t} else {\n\t\t\t\tqu = raw[i]!;\n\t\t\t\tqv = raw[i + 1]!;\n\t\t\t}\n\t\t\tuvs[i] = originU + qu * scaleU;\n\t\t\tuvs[i + 1] = originV + qv * scaleV;\n\t\t}\n\t}\n\n\treturn { uvs, offset: offset + dataByteLength };\n}\n\n/**\n * Parses the trailing vertex-color chunk into raw r,g,b bytes, undoing the per-channel wrapped\n * 8-bit delta+zigzag filter when the blob-wide delta flag is set.\n */\nexport function parseColorChunk(\n\tbytes: Uint8Array,\n\toffset: number,\n\tvertexCount: number,\n\tdeltaEncoded: boolean\n): Uint8Array {\n\tconst byteLength = vertexCount * 3;\n\tif (offset + byteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read vertex-color chunk.', {\n\t\t\texpectedBytes: byteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tvertexCount\n\t\t});\n\t}\n\n\tconst raw = bytes.subarray(offset, offset + byteLength);\n\tif (!deltaEncoded) {\n\t\treturn raw.slice();\n\t}\n\n\tconst colors = new Uint8Array(byteLength);\n\tlet r = 0;\n\tlet g = 0;\n\tlet b = 0;\n\tfor (let i = 0; i < byteLength; i += 3) {\n\t\tr = (r + unzigzag(raw[i]!)) & 0xff;\n\t\tg = (g + unzigzag(raw[i + 1]!)) & 0xff;\n\t\tb = (b + unzigzag(raw[i + 2]!)) & 0xff;\n\t\tcolors[i] = r;\n\t\tcolors[i + 1] = g;\n\t\tcolors[i + 2] = b;\n\t}\n\treturn colors;\n}\n\n// ============================================================================\n","import { VisualizationError, ErrorCodes } from '../../shared/index.js';\n\nimport {\n\tBINARY_MESH_MAGIC,\n\tBINARY_MESH_VERSION,\n\tFLAG_DELTA_ENCODED,\n\tFLAG_FLOAT32,\n\tFLAG_HAS_UVS,\n\tFLAG_HAS_VERTEX_COLORS,\n\tFLAG_UINT16_INDICES,\n\tGEOMETRY_HEADER_BYTES,\n\tHEADER_PREAMBLE_BYTES,\n\tHOST_IS_LITTLE_ENDIAN,\n\tMIN_SUPPORTED_VERSION\n} from './binary/header.js';\nimport {\n\tdecodeDeltaIndices16,\n\tdecodeDeltaIndices32,\n\tdecodeDeltaVertices,\n\tdecodeUtf8,\n\tfail,\n\tmaybeDecompress,\n\treadFloat32Vertices,\n\treadInt16Vertices,\n\treadUint16Array,\n\treadUint32Array,\n\ttoUint8Array,\n\tvalidateIndicesInRange\n} from './binary/geometry.js';\nimport { parseColorChunk, parseUvChunk } from './binary/textures.js';\n\nimport type { BinaryMeshMetadata, ParsedBinaryMeshBatch } from './binary/header.js';\n\n// Re-exported so consumers keep importing from `binary-parser` rather than reaching into `binary/`.\nexport {\n\tBINARY_MESH_MAGIC,\n\tCOMPRESSED_MESH_MAGIC,\n\tBINARY_MESH_VERSION,\n\tMIN_SUPPORTED_VERSION,\n\tFLAG_FLOAT32,\n\tFLAG_UINT16_INDICES,\n\tFLAG_DELTA_ENCODED,\n\tFLAG_HAS_UVS,\n\tFLAG_HAS_VERTEX_COLORS,\n\tUV_FORMAT_UINT16,\n\tUV_FORMAT_FLOAT32\n} from './binary/header.js';\nexport type { BinaryMeshMetadata, ParsedBinaryMeshBatch } from './binary/header.js';\n\n// ============================================================================\n// PARSER\n// ============================================================================\n\n/**\n * Parses a binary mesh batch blob in the SLVA wire format.\n *\n * Blob layout:\n * ```\n * [4] magic = \"SLVA\" (0x53 0x4C 0x56 0x41)\n * [4] version = uint32 (currently 3)\n * [4] metadataLen = uint32 byte length of UTF-8 metadata JSON\n * [N] metadata = UTF-8 JSON (materials, groups, sourceComponentId, ...)\n * [4] flags = uint32 (bit 0: 0 = int16 quantized, 1 = float32 raw;\n * bit 1: 0 = uint32 indices, 1 = uint16 indices;\n * bit 2: 1 = delta+zigzag filtered)\n * [24] origin = 3 x float64\n * [24] scale = 3 x float64 (step per int16 unit; identity for float32)\n * [4] vertexCount = uint32 number of vertices (positions = vertexCount * 3 components)\n * [V] vertices = int16[vertexCount*3] OR float32[vertexCount*3]\n * [4] indexCount = uint32 number of indices\n * [I] indices = uint32[indexCount] OR uint16[indexCount]\n * ```\n *\n * For int16 vertices: world position = `origin + (q + 32767) * scale`. This matches Three.js\n * `BufferAttribute(arr, 3, true)` (`normalized: true`) semantics when the per-mesh transform\n * encodes `origin + scale`.\n *\n * For float32: `origin = (0, 0, 0)`, `scale = (1, 1, 1)`, vertices are raw world positions.\n *\n * With FLAG_DELTA_ENCODED (v3), stored int16 vertex components and indices are wrapped\n * differences from their predecessor, zigzag-mapped — see the flag's doc in `binary/header.ts`.\n * This parser returns reconstructed absolute values; consumers never see the filter.\n *\n * @param input - The blob, as either an `ArrayBuffer`/`Uint8Array` (binary transport) or a\n * base64-encoded string (JSON-envelope transport).\n * @throws {VisualizationError} On invalid magic, unknown version, or truncated input.\n */\nexport function parseBinaryMeshBatch(\n\tinput: ArrayBuffer | Uint8Array | string\n): ParsedBinaryMeshBatch {\n\tconst raw = parseBinaryMeshBatchRaw(input);\n\n\tlet vertices: Int16Array | Float32Array;\n\tif (raw.isFloat32) {\n\t\tvertices = raw.vertexData as Float32Array;\n\t} else if (raw.deltaEncoded) {\n\t\tvertices = decodeDeltaVertices(raw.vertexData as Uint16Array);\n\t} else {\n\t\tvertices = raw.vertexData as Int16Array;\n\t}\n\n\tlet indices = raw.indexData;\n\tif (raw.deltaEncoded) {\n\t\tindices =\n\t\t\tindices instanceof Uint16Array\n\t\t\t\t? decodeDeltaIndices16(indices)\n\t\t\t\t: decodeDeltaIndices32(indices);\n\t}\n\tvalidateIndicesInRange(indices, raw.vertexCount);\n\n\treturn {\n\t\tmetadata: raw.metadata,\n\t\tflags: raw.flags,\n\t\tvertices,\n\t\tindices,\n\t\torigin: raw.origin,\n\t\tscale: raw.scale,\n\t\tuvs: raw.uvs,\n\t\tcolors: raw.colors\n\t};\n}\n\n/**\n * Raw wire-value view of a blob: geometry arrays are exactly as stored — zigzag-mapped deltas when\n * the blob carries the delta filter — while metadata, UVs, and colors are fully decoded (they're\n * small). For consumers handing the heavy decoding to a worker (`mesh-assembly.ts`); everyone else\n * wants {@link parseBinaryMeshBatch}, which returns reconstructed absolute values.\n */\nexport interface RawBinaryMeshBatch {\n\tmetadata: BinaryMeshMetadata;\n\tflags: number;\n\t/** Wire vertex components: zigzag deltas (Uint16) when `deltaEncoded` and not float32. */\n\tvertexData: Uint16Array | Int16Array | Float32Array;\n\t/** Wire indices: zigzag deltas when `deltaEncoded`. NOT validated against vertexCount. */\n\tindexData: Uint16Array | Uint32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\tvertexCount: number;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\n/** See {@link RawBinaryMeshBatch}. Same validation/throw behavior as the decoding parser. */\nexport function parseBinaryMeshBatchRaw(\n\tinput: ArrayBuffer | Uint8Array | string\n): RawBinaryMeshBatch {\n\tif (!HOST_IS_LITTLE_ENDIAN) {\n\t\tthrow new VisualizationError(\n\t\t\t'SLVA parsing requires a little-endian host: the zero-copy geometry readers view the wire bytes in host byte order.',\n\t\t\tErrorCodes.ENVIRONMENT_ERROR\n\t\t);\n\t}\n\n\tconst bytes = maybeDecompress(toUint8Array(input));\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\n\tif (bytes.byteLength < HEADER_PREAMBLE_BYTES) {\n\t\tthrow fail('Blob too small to contain SLVA header.', {\n\t\t\texpectedBytes: HEADER_PREAMBLE_BYTES,\n\t\t\tavailableBytes: bytes.byteLength\n\t\t});\n\t}\n\n\tlet offset = 0;\n\n\tconst magic = view.getUint32(offset, true);\n\toffset += 4;\n\tif (magic !== BINARY_MESH_MAGIC) {\n\t\tthrow fail(`Invalid SLVA magic: 0x${magic.toString(16)}`, {\n\t\t\texpectedMagic: `0x${BINARY_MESH_MAGIC.toString(16)}`,\n\t\t\tactualMagic: `0x${magic.toString(16)}`\n\t\t});\n\t}\n\n\tconst version = view.getUint32(offset, true);\n\toffset += 4;\n\tif (version < MIN_SUPPORTED_VERSION || version > BINARY_MESH_VERSION) {\n\t\tthrow fail(`Unsupported SLVA version: ${version}`, {\n\t\t\tminSupportedVersion: MIN_SUPPORTED_VERSION,\n\t\t\tmaxSupportedVersion: BINARY_MESH_VERSION,\n\t\t\tactualVersion: version\n\t\t});\n\t}\n\n\tconst metadataLen = view.getUint32(offset, true);\n\toffset += 4;\n\tif (offset + metadataLen > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read metadata JSON.', {\n\t\t\texpectedBytes: metadataLen,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\n\tconst metadataBytes = bytes.subarray(offset, offset + metadataLen);\n\toffset += metadataLen;\n\n\tlet metadata: BinaryMeshMetadata;\n\ttry {\n\t\tmetadata = JSON.parse(decodeUtf8(metadataBytes)) as BinaryMeshMetadata;\n\t} catch (error) {\n\t\tthrow fail(\n\t\t\t`Failed to parse metadata JSON: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ metadataLen }\n\t\t);\n\t}\n\n\tif (offset + GEOMETRY_HEADER_BYTES > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read geometry header.', {\n\t\t\texpectedBytes: GEOMETRY_HEADER_BYTES,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\n\tconst flags = view.getUint32(offset, true);\n\toffset += 4;\n\n\tconst originX = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst originY = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst originZ = view.getFloat64(offset, true);\n\toffset += 8;\n\n\tconst scaleX = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleY = view.getFloat64(offset, true);\n\toffset += 8;\n\tconst scaleZ = view.getFloat64(offset, true);\n\toffset += 8;\n\n\tconst vertexCount = view.getUint32(offset, true);\n\toffset += 4;\n\n\tconst useFloat32 = (flags & FLAG_FLOAT32) !== 0;\n\tconst deltaEncoded = (flags & FLAG_DELTA_ENCODED) !== 0;\n\tconst componentCount = vertexCount * 3;\n\tconst bytesPerComponent = useFloat32 ? 4 : 2;\n\tconst verticesByteLength = componentCount * bytesPerComponent;\n\n\tif (offset + verticesByteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read vertices.', {\n\t\t\texpectedBytes: verticesByteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tuseFloat32,\n\t\t\tvertexCount\n\t\t});\n\t}\n\n\t// Typed-array views need alignment to the element size. The header lays out the geometry block\n\t// so the vertex byte offset is always 4-aligned (preamble 12 + metadataLen + 4 + 48 + 4) —\n\t// satisfies both float32 (4-byte) and int16 (2-byte). A zero-copy view is only valid if\n\t// `bytes.byteOffset + offset` respects that alignment in the underlying buffer, which a wrapper\n\t// Uint8Array could violate; the readers fall back to a copy when it does.\n\tconst absoluteOffset = bytes.byteOffset + offset;\n\tlet vertexData: Uint16Array | Int16Array | Float32Array;\n\tif (useFloat32) {\n\t\tvertexData = readFloat32Vertices(bytes.buffer, absoluteOffset, componentCount);\n\t} else if (deltaEncoded) {\n\t\t// Raw zigzag deltas — parseBinaryMeshBatch (or the assembly worker) prefix-sums them later.\n\t\tvertexData = readUint16Array(bytes.buffer, absoluteOffset, componentCount);\n\t} else {\n\t\tvertexData = readInt16Vertices(bytes.buffer, absoluteOffset, componentCount);\n\t}\n\toffset += verticesByteLength;\n\n\tif (offset + 4 > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read index count.', {\n\t\t\texpectedBytes: 4,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset\n\t\t});\n\t}\n\tconst indexCount = view.getUint32(offset, true);\n\toffset += 4;\n\n\tconst useUint16Indices = (flags & FLAG_UINT16_INDICES) !== 0;\n\tconst bytesPerIndex = useUint16Indices ? 2 : 4;\n\tconst indicesByteLength = indexCount * bytesPerIndex;\n\tif (offset + indicesByteLength > bytes.byteLength) {\n\t\tthrow fail('Insufficient data to read indices.', {\n\t\t\texpectedBytes: indicesByteLength,\n\t\t\tavailableBytes: bytes.byteLength - offset,\n\t\t\toffset,\n\t\t\tindexCount,\n\t\t\tuseUint16Indices\n\t\t});\n\t}\n\n\tconst indexData = useUint16Indices\n\t\t? readUint16Array(bytes.buffer, bytes.byteOffset + offset, indexCount)\n\t\t: readUint32Array(bytes.buffer, bytes.byteOffset + offset, indexCount);\n\toffset += indicesByteLength;\n\n\t// Optional trailing chunks: UV first, then colors. Pre-chunk-writer blobs simply end here —\n\t// each read is gated by its flag, so nothing is consumed when a chunk is absent.\n\tlet uvs: Float32Array | null = null;\n\tif ((flags & FLAG_HAS_UVS) !== 0) {\n\t\tconst parsed = parseUvChunk(bytes, view, offset, vertexCount, deltaEncoded);\n\t\tuvs = parsed.uvs;\n\t\toffset = parsed.offset;\n\t}\n\n\tlet colors: Uint8Array | null = null;\n\tif ((flags & FLAG_HAS_VERTEX_COLORS) !== 0) {\n\t\tcolors = parseColorChunk(bytes, offset, vertexCount, deltaEncoded);\n\t}\n\n\treturn {\n\t\tmetadata,\n\t\tflags,\n\t\tvertexData,\n\t\tindexData,\n\t\tisFloat32: useFloat32,\n\t\tdeltaEncoded,\n\t\tvertexCount,\n\t\torigin: [originX, originY, originZ],\n\t\tscale: [scaleX, scaleY, scaleZ],\n\t\tuvs,\n\t\tcolors\n\t};\n}\n","/**\n * {@link assembleGeometries} is the hot, pure part of batch parsing: undoes the delta filter on\n * the raw wire arrays, dequantizes int16 positions to world floats, slices/rebases per-geometry\n * windows and computes vertex normals.\n * Everything it needs travels as typed arrays, so the whole stage runs in a Worker and the main\n * thread only wraps the returned buffers into `BufferGeometry` objects.\n *\n * Like `edge-extract.ts`, it's a single self-contained function with zero outer captures (only\n * `Math` and its arguments) so `Function.prototype.toString` yields code that runs unchanged\n * inside a blob-URL Worker ({@link meshAssemblyWorkerSource}) — bundler-agnostic by construction.\n * That forces duplicating small helpers from `binary-parser.ts` (unzigzag/delta decode);\n * equivalence with the synchronous path is pinned by tests.\n */\n\nexport interface AssemblyWindow {\n\tvertexStart: number;\n\tvertexCount: number;\n\tindexStart: number;\n\tindexCount: number;\n}\n\nexport interface AssemblyJob {\n\tkind: 'merged' | 'single';\n\twindows: AssemblyWindow[];\n}\n\nexport interface AssemblyInput {\n\t/** Raw wire vertex components: zigzag deltas (Uint16) when delta-encoded, else absolute. */\n\tvertexData: Uint16Array | Int16Array | Float32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\t/** Raw wire indices: zigzag deltas when delta-encoded, else absolute. */\n\tindexData: Uint16Array | Uint32Array;\n\t/** Already-decoded absolute UV pairs / RGB bytes (small, decoded on the main thread). */\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n\tjobs: AssemblyJob[];\n}\n\nexport interface AssembledGeometry {\n\tpositions: Float32Array;\n\tnormals: Float32Array;\n\tindices: Uint32Array;\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\nexport function assembleGeometries(input: AssemblyInput): AssembledGeometry[] {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tconst { isFloat32, deltaEncoded, origin, scale, uvs, colors, jobs } = input;\n\n\tconst unzigzag = (zz: number): number => (zz >>> 1) ^ -(zz & 1);\n\n\t// --- Undo the delta filter (whole-array: each value depends on its predecessor) ------------\n\tlet worldVertices: Float32Array;\n\tif (isFloat32) {\n\t\tworldVertices = input.vertexData as Float32Array;\n\t} else {\n\t\tlet quantized: Int16Array;\n\t\tif (deltaEncoded) {\n\t\t\tconst zigzagged = input.vertexData as Uint16Array;\n\t\t\tquantized = new Int16Array(zigzagged.length);\n\t\t\tlet px = 0;\n\t\t\tlet py = 0;\n\t\t\tlet pz = 0;\n\t\t\tfor (let i = 0; i < zigzagged.length; i += 3) {\n\t\t\t\tpx = ((px + unzigzag(zigzagged[i])) << 16) >> 16;\n\t\t\t\tpy = ((py + unzigzag(zigzagged[i + 1])) << 16) >> 16;\n\t\t\t\tpz = ((pz + unzigzag(zigzagged[i + 2])) << 16) >> 16;\n\t\t\t\tquantized[i] = px;\n\t\t\t\tquantized[i + 1] = py;\n\t\t\t\tquantized[i + 2] = pz;\n\t\t\t}\n\t\t} else {\n\t\t\tquantized = input.vertexData as Int16Array;\n\t\t}\n\t\t// Dequantize: world = origin + (q + 32767) * scale (matches the writer/binary-parser).\n\t\tworldVertices = new Float32Array(quantized.length);\n\t\tconst ox = origin[0];\n\t\tconst oy = origin[1];\n\t\tconst oz = origin[2];\n\t\tconst sx = scale[0];\n\t\tconst sy = scale[1];\n\t\tconst sz = scale[2];\n\t\tfor (let i = 0; i < quantized.length; i += 3) {\n\t\t\tworldVertices[i] = ox + (quantized[i] + 32767) * sx;\n\t\t\tworldVertices[i + 1] = oy + (quantized[i + 1] + 32767) * sy;\n\t\t\tworldVertices[i + 2] = oz + (quantized[i + 2] + 32767) * sz;\n\t\t}\n\t}\n\n\tlet indices: Uint16Array | Uint32Array;\n\tif (deltaEncoded) {\n\t\tconst zigzagged = input.indexData;\n\t\tif (zigzagged instanceof Uint16Array) {\n\t\t\tconst out = new Uint16Array(zigzagged.length);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\t\t\tprev = (prev + unzigzag(zigzagged[i])) & 0xffff;\n\t\t\t\tout[i] = prev;\n\t\t\t}\n\t\t\tindices = out;\n\t\t} else {\n\t\t\tconst out = new Uint32Array(zigzagged.length);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < zigzagged.length; i++) {\n\t\t\t\tprev = (prev + unzigzag(zigzagged[i])) >>> 0;\n\t\t\t\tout[i] = prev;\n\t\t\t}\n\t\t\tindices = out;\n\t\t}\n\t} else {\n\t\tindices = input.indexData;\n\t}\n\n\tconst totalVertexCount = worldVertices.length / 3;\n\tfor (let i = 0; i < indices.length; i++) {\n\t\tif (indices[i] >= totalVertexCount) {\n\t\t\tthrow new Error(`Index ${indices[i]} out of range of vertexCount ${totalVertexCount}`);\n\t\t}\n\t}\n\n\t// --- Assemble each job: window copies, rebased indices, area-weighted vertex normals --------\n\tconst results: AssembledGeometry[] = [];\n\n\tfor (const job of jobs) {\n\t\tlet vertexTotal = 0;\n\t\tlet indexTotal = 0;\n\t\tfor (const window of job.windows) {\n\t\t\tvertexTotal += window.vertexCount;\n\t\t\tindexTotal += window.indexCount;\n\t\t}\n\n\t\tconst positions = new Float32Array(vertexTotal * 3);\n\t\tconst outIndices = new Uint32Array(indexTotal);\n\t\tconst outUvs = uvs ? new Float32Array(vertexTotal * 2) : null;\n\t\tconst outColors = colors ? new Uint8Array(vertexTotal * 3) : null;\n\n\t\tlet vertexCursor = 0;\n\t\tlet indexCursor = 0;\n\t\tfor (const window of job.windows) {\n\t\t\tconst componentStart = window.vertexStart * 3;\n\t\t\tpositions.set(\n\t\t\t\tworldVertices.subarray(componentStart, componentStart + window.vertexCount * 3),\n\t\t\t\tvertexCursor * 3\n\t\t\t);\n\t\t\tif (outUvs && uvs) {\n\t\t\t\toutUvs.set(\n\t\t\t\t\tuvs.subarray(window.vertexStart * 2, (window.vertexStart + window.vertexCount) * 2),\n\t\t\t\t\tvertexCursor * 2\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (outColors && colors) {\n\t\t\t\toutColors.set(\n\t\t\t\t\tcolors.subarray(componentStart, componentStart + window.vertexCount * 3),\n\t\t\t\t\tvertexCursor * 3\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst windowStart = window.vertexStart;\n\t\t\tconst windowEnd = window.vertexStart + window.vertexCount;\n\t\t\tconst shift = vertexCursor - window.vertexStart;\n\t\t\tfor (let i = 0; i < window.indexCount; i++) {\n\t\t\t\tconst indexValue = indices[window.indexStart + i];\n\t\t\t\tif (indexValue < windowStart || indexValue >= windowEnd) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Index ${indexValue} outside vertex window [${windowStart}, ${windowEnd})`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\toutIndices[indexCursor + i] = indexValue + shift;\n\t\t\t}\n\n\t\t\tvertexCursor += window.vertexCount;\n\t\t\tindexCursor += window.indexCount;\n\t\t}\n\n\t\t// Vertex normals, mirroring THREE.BufferGeometry.computeVertexNormals: accumulate the\n\t\t// non-normalized (area-weighted) face normal cross((c-b),(a-b)) onto each corner, then\n\t\t// normalize per vertex.\n\t\tconst normals = new Float32Array(vertexTotal * 3);\n\t\tfor (let i = 0; i < outIndices.length; i += 3) {\n\t\t\tconst a = outIndices[i] * 3;\n\t\t\tconst b = outIndices[i + 1] * 3;\n\t\t\tconst c = outIndices[i + 2] * 3;\n\n\t\t\tconst cbx = positions[c] - positions[b];\n\t\t\tconst cby = positions[c + 1] - positions[b + 1];\n\t\t\tconst cbz = positions[c + 2] - positions[b + 2];\n\t\t\tconst abx = positions[a] - positions[b];\n\t\t\tconst aby = positions[a + 1] - positions[b + 1];\n\t\t\tconst abz = positions[a + 2] - positions[b + 2];\n\n\t\t\tconst nx = cby * abz - cbz * aby;\n\t\t\tconst ny = cbz * abx - cbx * abz;\n\t\t\tconst nz = cbx * aby - cby * abx;\n\n\t\t\tnormals[a] += nx;\n\t\t\tnormals[a + 1] += ny;\n\t\t\tnormals[a + 2] += nz;\n\t\t\tnormals[b] += nx;\n\t\t\tnormals[b + 1] += ny;\n\t\t\tnormals[b + 2] += nz;\n\t\t\tnormals[c] += nx;\n\t\t\tnormals[c + 1] += ny;\n\t\t\tnormals[c + 2] += nz;\n\t\t}\n\t\tfor (let i = 0; i < normals.length; i += 3) {\n\t\t\tconst x = normals[i];\n\t\t\tconst y = normals[i + 1];\n\t\t\tconst z = normals[i + 2];\n\t\t\tconst length = Math.sqrt(x * x + y * y + z * z) || 1;\n\t\t\tnormals[i] = x / length;\n\t\t\tnormals[i + 1] = y / length;\n\t\t\tnormals[i + 2] = z / length;\n\t\t}\n\n\t\tresults.push({\n\t\t\tpositions,\n\t\t\tnormals,\n\t\t\tindices: outIndices,\n\t\t\tuvs: outUvs,\n\t\t\tcolors: outColors\n\t\t});\n\t}\n\n\treturn results;\n}\n\n/**\n * Worker script running {@link assembleGeometries} off the main thread. Protocol: receives\n * `{id, input}`, replies `{id, geometries}` with every output buffer transferred, or\n * `{id, error}`. Pinned by a test that evals this source against a stub `self`.\n */\nexport function meshAssemblyWorkerSource(): string {\n\treturn [\n\t\t`const assemble = ${assembleGeometries.toString()};`,\n\t\t`self.onmessage = (event) => {`,\n\t\t` const { id, input } = event.data;`,\n\t\t` try {`,\n\t\t` const geometries = assemble(input);`,\n\t\t` const transfer = [];`,\n\t\t` for (const g of geometries) {`,\n\t\t` transfer.push(g.positions.buffer, g.normals.buffer, g.indices.buffer);`,\n\t\t` if (g.uvs) transfer.push(g.uvs.buffer);`,\n\t\t` if (g.colors) transfer.push(g.colors.buffer);`,\n\t\t` }`,\n\t\t` self.postMessage({ id, geometries }, transfer);`,\n\t\t` } catch (error) {`,\n\t\t` self.postMessage({ id, error: String((error && error.message) || error) });`,\n\t\t` }`,\n\t\t`};`\n\t].join('\\n');\n}\n","import { meshAssemblyWorkerSource } from '../mesh-assembly.js';\n\nimport type { AssembledGeometry } from '../mesh-assembly.js';\n\n/**\n * Below this triangle count the synchronous path finishes in ~10 ms — a worker round-trip (two\n * buffer copies + wake) isn't worth it. Above it, delta-decode + dequantize + merge + normals run\n * in the worker and the main thread only wraps returned buffers (or reuses cached geometries).\n */\nexport const ASSEMBLY_WORKER_MIN_TRIANGLES = 50_000;\n\ninterface PendingAssembly {\n\tresolve: (geometries: AssembledGeometry[]) => void;\n\treject: (error: Error) => void;\n}\n\nlet assemblyWorker: Worker | null | undefined; // undefined = not yet tried, null = unavailable\nconst pendingAssemblies = new Map<number, PendingAssembly>();\nlet nextAssemblyRequestId = 1;\n\nexport function getAssemblyWorker(): Worker | null {\n\tif (assemblyWorker !== undefined) return assemblyWorker;\n\tif (\n\t\ttypeof Worker === 'undefined' ||\n\t\ttypeof Blob === 'undefined' ||\n\t\ttypeof URL === 'undefined' ||\n\t\ttypeof URL.createObjectURL !== 'function'\n\t) {\n\t\tassemblyWorker = null;\n\t\treturn null;\n\t}\n\ttry {\n\t\t// Blob URL keeps the library bundler-agnostic; deliberately never revoked (see render/edges/extraction.ts).\n\t\tconst url = URL.createObjectURL(\n\t\t\tnew Blob([meshAssemblyWorkerSource()], { type: 'text/javascript' })\n\t\t);\n\t\tconst worker = new Worker(url);\n\t\tworker.onmessage = (event: MessageEvent) => {\n\t\t\tconst { id, geometries, error } = event.data as {\n\t\t\t\tid: number;\n\t\t\t\tgeometries?: AssembledGeometry[];\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tconst pending = pendingAssemblies.get(id);\n\t\t\tif (!pending) return;\n\t\t\tpendingAssemblies.delete(id);\n\t\t\tif (geometries) pending.resolve(geometries);\n\t\t\telse pending.reject(new Error(error ?? 'mesh assembly failed in worker'));\n\t\t};\n\t\tworker.onerror = () => {\n\t\t\tfor (const pending of pendingAssemblies.values()) {\n\t\t\t\tpending.reject(new Error('mesh assembly worker crashed'));\n\t\t\t}\n\t\t\tpendingAssemblies.clear();\n\t\t\tworker.terminate();\n\t\t\tassemblyWorker = null; // don't retry this session — callers fall back to the sync path\n\t\t};\n\t\tassemblyWorker = worker;\n\t} catch {\n\t\tassemblyWorker = null;\n\t}\n\treturn assemblyWorker;\n}\n\nexport function requestAssembly(\n\tworker: Worker,\n\tinput: unknown,\n\ttransfer: Transferable[]\n): Promise<AssembledGeometry[]> {\n\treturn new Promise<AssembledGeometry[]>((resolve, reject) => {\n\t\tconst id = nextAssemblyRequestId++;\n\t\tpendingAssemblies.set(id, { resolve, reject });\n\t\tworker.postMessage({ id, input }, transfer);\n\t});\n}\n","import * as THREE from 'three';\n\nimport { getLogger, observeMaxAnisotropy } from '../../shared/index.js';\n\n/**\n * Anisotropic-filtering samples applied to color maps, keeping textures sharp at grazing angles\n * instead of blurring. Ceiling is hardware-defined (`renderer.capabilities.getMaxAnisotropy()`,\n * typically 16). Defaults to three's default (1 — no anisotropy) until a renderer reports in.\n */\nlet maxAnisotropy = 1;\n\n/**\n * Subscribed to the renderer's own report below, so no host wiring is needed; still exported for a\n * host embedding a foreign renderer that wants to set it directly. Applies to textures loaded from\n * here on — textures already decoded keep the value they were given.\n */\nexport function setTextureAnisotropy(value: number): void {\n\tmaxAnisotropy = Math.max(1, value);\n}\n\n// Take the value straight from whichever renderer initializes, rather than depending on the host to\n// forward it. `render/` publishes, this layer subscribes — neither imports the other.\nobserveMaxAnisotropy(setTextureAnisotropy);\n\n/**\n * Assigns a texture to `material.map` once fetched and decoded — the mesh renders untextured for\n * the first frames. Load failures log a warning and leave the material untextured rather than\n * breaking the batch.\n *\n * Each call loads independently: no caching, no cross-material sharing. The texture is owned by the\n * material it is assigned to, so the scene's normal dispose walk frees it like any other resource.\n */\nexport function applyTextureMap(material: THREE.MeshPhysicalMaterial, url: string): void {\n\t// No DOM (SSR / tests): textures can't decode without an image element; skip quietly.\n\tif (typeof document === 'undefined') {\n\t\treturn;\n\t}\n\n\tnew THREE.TextureLoader().load(\n\t\turl,\n\t\t(texture) => {\n\t\t\t// Color maps are sRGB; without this the render is washed out.\n\t\t\ttexture.colorSpace = THREE.SRGBColorSpace;\n\t\t\t// Keep textures crisp at grazing angles (see maxAnisotropy).\n\t\t\ttexture.anisotropy = maxAnisotropy;\n\t\t\tmaterial.map = texture;\n\t\t\tmaterial.needsUpdate = true;\n\t\t},\n\t\tundefined,\n\t\t(error) => {\n\t\t\tgetLogger().warn(`Failed to load material texture ${url}:`, error);\n\t\t}\n\t);\n}\n","import * as THREE from 'three';\n\nimport { parseColor } from '../../../shared/index.js';\n\nimport { applyTextureMap } from '../apply-texture.js';\n\nimport type { MaterialAppearanceOptions, SerializableMaterial } from '../types.js';\n\n// A near-pure metal has no diffuse response, so under the low-IBL 'technical' look it goes flat and\n// reads as painted card. Real architectural sheet metal is coated, not a bare mirror, so meaningfully\n// metallic materials get a thin satin clearcoat — a glossy dielectric layer independent of base\n// metalness/envMap, so folds catch light even when the IBL is dialed down.\nconst METAL_CLEARCOAT_THRESHOLD = 0.5;\nconst METAL_CLEARCOAT = 0.5;\nconst METAL_CLEARCOAT_ROUGHNESS = 0.3;\n\nexport function createMaterial(\n\tmatData: SerializableMaterial,\n\toptions?: { vertexColors?: boolean; appearance?: MaterialAppearanceOptions }\n): THREE.MeshPhysicalMaterial {\n\tconst color = parseColor(matData.color);\n\tconst vertexColors = options?.vertexColors ?? false;\n\tconst appearance = options?.appearance;\n\n\tconst material = new THREE.MeshPhysicalMaterial({\n\t\tcolor,\n\t\tmetalness: matData.metalness,\n\t\troughness: matData.roughness,\n\t\topacity: matData.opacity,\n\t\ttransparent: matData.transparent,\n\t\tvertexColors,\n\t\t// Cull back faces for closed solids (crisper silhouette, less overdraw); keep both sides for\n\t\t// open surfaces. Caller-controlled since Rhino emits both — default DoubleSide is the safe read.\n\t\tside: appearance?.cullBackfaces ? THREE.FrontSide : THREE.DoubleSide,\n\t\tpolygonOffset: true, // avoids z-fighting on coplanar faces\n\t\tpolygonOffsetFactor: 0.5,\n\t\tpolygonOffsetUnits: 0.5,\n\t\tdepthWrite: true,\n\t\tdepthTest: true\n\t});\n\n\t// HDR image-based-lighting reflection strength. Left at three's default (1) unless the caller\n\t// dials it: <1 flattens reflections toward a matte/technical read, >1 pushes a glossier look.\n\tif (appearance?.envMapIntensity != null) {\n\t\tmaterial.envMapIntensity = appearance.envMapIntensity;\n\t}\n\n\t// See the constants above. Plastics/matte fall below the threshold and stay bare.\n\tif (matData.metalness > METAL_CLEARCOAT_THRESHOLD) {\n\t\tmaterial.clearcoat = METAL_CLEARCOAT;\n\t\tmaterial.clearcoatRoughness = METAL_CLEARCOAT_ROUGHNESS;\n\t}\n\n\tif (vertexColors) {\n\t\tapplyVertexColorSRGBDecode(material);\n\t}\n\n\t// Async; the mesh renders untextured until the image decodes.\n\tif (matData.map) {\n\t\tapplyTextureMap(material, matData.map);\n\t}\n\n\treturn material;\n}\n\n/**\n * three.js uploads vertex colors verbatim and multiplies them straight into linear working space\n * (unlike textures, which carry a `colorSpace` and get decoded) — so sRGB-authored vertex colors\n * render too bright without this shader patch. Done on the GPU rather than a CPU pass over the\n * buffer, to keep the hot per-solve parse cheap.\n */\nexport function applyVertexColorSRGBDecode(material: THREE.Material): void {\n\tmaterial.onBeforeCompile = (shader) => {\n\t\tshader.vertexShader = shader.vertexShader.replace(\n\t\t\t'#include <color_vertex>',\n\t\t\t`#include <color_vertex>\n\t\t\t#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA )\n\t\t\t\tvColor.rgb = mix(\n\t\t\t\t\tvColor.rgb / 12.92,\n\t\t\t\t\tpow( ( vColor.rgb + 0.055 ) / 1.055, vec3( 2.4 ) ),\n\t\t\t\t\tstep( vec3( 0.04045 ), vColor.rgb )\n\t\t\t\t);\n\t\t\t#endif`\n\t\t);\n\t};\n}\n","import { VisualizationError, ErrorCodes } from '../../../shared/index.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\nexport function metadataFail(\n\tmessage: string,\n\tcontext: Record<string, unknown>\n): VisualizationError {\n\treturn new VisualizationError(message, ErrorCodes.VALIDATION_ERROR, { context });\n}\n\n/**\n * Validates the batch's group/mesh metadata against the decoded geometry buffers before any of it\n * is used arithmetically. Throws on the first inconsistency — out-of-range `materialId`,\n * non-integer or negative offsets/counts, or a vertex/index window that overruns the buffers — so\n * malformed or version-skewed metadata fails the parse loudly instead of corrupting the render.\n */\nexport function validateGroupMetadata(\n\tgroups: MaterialGroup[],\n\tmaterialCount: number,\n\ttotalVertexCount: number,\n\ttotalIndexCount: number\n): void {\n\tfor (const group of groups) {\n\t\tif (\n\t\t\t!Number.isInteger(group.materialId) ||\n\t\t\tgroup.materialId < 0 ||\n\t\t\tgroup.materialId >= materialCount\n\t\t) {\n\t\t\tthrow metadataFail('Group materialId out of range of the materials array.', {\n\t\t\t\tmaterialId: group.materialId,\n\t\t\t\tmaterialCount\n\t\t\t});\n\t\t}\n\n\t\tfor (const mesh of group.meshes) {\n\t\t\tconst fields = {\n\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\tindexCount: mesh.indexCount\n\t\t\t};\n\t\t\tfor (const [field, value] of Object.entries(fields)) {\n\t\t\t\tif (!Number.isInteger(value) || value < 0) {\n\t\t\t\t\tthrow metadataFail(`Mesh metadata field \"${field}\" must be a non-negative integer.`, {\n\t\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mesh.vertexStart + mesh.vertexCount > totalVertexCount) {\n\t\t\t\tthrow metadataFail('Mesh vertex window exceeds the batch vertex buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\t\ttotalVertexCount\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (mesh.indexStart + mesh.indexCount > totalIndexCount) {\n\t\t\t\tthrow metadataFail('Mesh index window exceeds the batch index buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\t\tindexCount: mesh.indexCount,\n\t\t\t\t\ttotalIndexCount\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Error for an index outside its mesh's declared vertex window\n * `[vertexStart, vertexStart + vertexCount)`. Rebasing (`index - vertexStart`) writes into an\n * unsigned array, so an out-of-window index would otherwise wrap to ~4 billion and corrupt the\n * geometry. Range checks live inline in the copy loops (a function call per index measured\n * noticeably slower at millions of indices) — this only builds the failure.\n */\nexport function indexOutOfWindow(indexValue: number, meshMeta: MeshMetadata): VisualizationError {\n\treturn metadataFail(\"Index references a vertex outside its mesh's vertex window.\", {\n\t\tmeshName: meshMeta.name,\n\t\tindexValue,\n\t\tvertexStart: meshMeta.vertexStart,\n\t\tvertexCount: meshMeta.vertexCount\n\t});\n}\n\n/**\n * Reconstructs world-unit float32 positions from int16 quantized values:\n * `world = origin + (q + 32767) * scale`. No rotation — the Three scene uses Rhino's Z-up frame,\n * so vertices pass through as they arrived.\n */\nexport function dequantizeInt16(\n\tq: Int16Array,\n\torigin: [number, number, number],\n\tscale: [number, number, number]\n): Float32Array {\n\tconst out = new Float32Array(q.length);\n\tconst ox = origin[0];\n\tconst oy = origin[1];\n\tconst oz = origin[2];\n\tconst sx = scale[0];\n\tconst sy = scale[1];\n\tconst sz = scale[2];\n\n\tfor (let i = 0; i < q.length; i += 3) {\n\t\tout[i] = ox + (q[i]! + 32767) * sx;\n\t\tout[i + 1] = oy + (q[i + 1]! + 32767) * sy;\n\t\tout[i + 2] = oz + (q[i + 2]! + 32767) * sz;\n\t}\n\n\treturn out;\n}\n","import * as THREE from 'three';\n\nimport { indexOutOfWindow } from './metadata.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\n/**\n * Merges a material group's meshes into one BufferGeometry. Parser indices already address the\n * combined vertex array (rebased by the C# pipeline during batch assembly), so this copies each\n * mesh's vertex/index slices into a fresh contiguous buffer and shifts indices to match.\n */\nexport function createMergedMesh(\n\tgroup: MaterialGroup,\n\tallVertices: Float32Array,\n\tallIndices: Uint16Array | Uint32Array,\n\tmaterials: THREE.Material[],\n\tallUvs: Float32Array | null = null,\n\tallColors: Uint8Array | null = null\n): THREE.Mesh {\n\tlet totalVertexCount = 0;\n\tlet totalIndexCount = 0;\n\tfor (const meshMeta of group.meshes) {\n\t\ttotalVertexCount += meshMeta.vertexCount;\n\t\ttotalIndexCount += meshMeta.indexCount;\n\t}\n\n\tconst mergedVertices = new Float32Array(totalVertexCount * 3);\n\tconst mergedIndices = new Uint32Array(totalIndexCount);\n\tconst mergedUvs = allUvs ? new Float32Array(totalVertexCount * 2) : null;\n\tconst mergedColors = allColors ? new Uint8Array(totalVertexCount * 3) : null;\n\n\tlet vertexWriteCursor = 0;\n\tlet indexWriteCursor = 0;\n\n\tfor (const meshMeta of group.meshes) {\n\t\tconst componentStart = meshMeta.vertexStart * 3;\n\t\tconst componentLen = meshMeta.vertexCount * 3;\n\t\tmergedVertices.set(\n\t\t\tallVertices.subarray(componentStart, componentStart + componentLen),\n\t\t\tvertexWriteCursor * 3\n\t\t);\n\n\t\tif (mergedUvs && allUvs) {\n\t\t\tconst uvStart = meshMeta.vertexStart * 2;\n\t\t\tmergedUvs.set(\n\t\t\t\tallUvs.subarray(uvStart, uvStart + meshMeta.vertexCount * 2),\n\t\t\t\tvertexWriteCursor * 2\n\t\t\t);\n\t\t}\n\n\t\tif (mergedColors && allColors) {\n\t\t\tmergedColors.set(\n\t\t\t\tallColors.subarray(componentStart, componentStart + componentLen),\n\t\t\t\tvertexWriteCursor * 3\n\t\t\t);\n\t\t}\n\n\t\tconst indicesSlice = allIndices.subarray(\n\t\t\tmeshMeta.indexStart,\n\t\t\tmeshMeta.indexStart + meshMeta.indexCount\n\t\t);\n\t\tconst indexShift = vertexWriteCursor - meshMeta.vertexStart;\n\t\tconst windowStart = meshMeta.vertexStart;\n\t\tconst windowEnd = meshMeta.vertexStart + meshMeta.vertexCount;\n\t\tfor (let i = 0; i < indicesSlice.length; i++) {\n\t\t\tconst indexValue = indicesSlice[i]!;\n\t\t\tif (indexValue < windowStart || indexValue >= windowEnd) {\n\t\t\t\tthrow indexOutOfWindow(indexValue, meshMeta);\n\t\t\t}\n\t\t\tmergedIndices[indexWriteCursor + i] = indexValue + indexShift;\n\t\t}\n\n\t\tvertexWriteCursor += meshMeta.vertexCount;\n\t\tindexWriteCursor += meshMeta.indexCount;\n\t}\n\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute('position', new THREE.BufferAttribute(mergedVertices, 3));\n\tgeometry.setIndex(new THREE.BufferAttribute(mergedIndices, 1));\n\tif (mergedUvs) {\n\t\tgeometry.setAttribute('uv', new THREE.BufferAttribute(mergedUvs, 2));\n\t}\n\tif (mergedColors) {\n\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(mergedColors, 3, true));\n\t}\n\tgeometry.computeVertexNormals();\n\n\treturn finalizeMergedMesh(geometry, group, materials);\n}\n\nexport function finalizeMergedMesh(\n\tgeometry: THREE.BufferGeometry,\n\tgroup: MaterialGroup,\n\tmaterials: THREE.Material[]\n): THREE.Mesh {\n\tconst threeMesh = new THREE.Mesh(geometry, materials[group.materialId]);\n\tconst firstMesh = group.meshes[0];\n\tconst meshNames = group.meshes.map((m) => m.name).filter((name) => name && name.length > 0);\n\tthreeMesh.name = meshNames.length > 0 ? meshNames[0]! : `merged_material_${group.materialId}`;\n\tthreeMesh.castShadow = true;\n\tthreeMesh.receiveShadow = true;\n\n\tthreeMesh.userData = {\n\t\tsource: 'compute',\n\t\tname: threeMesh.name,\n\t\tlayer: firstMesh?.layer ?? '',\n\t\toriginalIndex: firstMesh?.originalIndex ?? 0,\n\t\t// A merged mesh has no single source index, so `originalIndex` alone cannot identify it:\n\t\t// two merges of one component both report their first member's index and collide, which\n\t\t// makes hiding one hide the other. Identity keys on every member instead.\n\t\tmergedIndices: group.meshes.map((m) => m.originalIndex).sort((a, b) => a - b),\n\t\tmetadata: firstMesh?.metadata ?? {},\n\t\tmergedFrom: group.meshes.slice(1).map((m) => ({\n\t\t\tname: m.name,\n\t\t\tlayer: m.layer,\n\t\t\toriginalIndex: m.originalIndex\n\t\t}))\n\t};\n\n\treturn threeMesh;\n}\n\n/**\n * Creates individual meshes from a material group. Each mesh's indices are rebased so they\n * address its own local vertex slice starting from 0.\n */\nexport function createIndividualMeshes(\n\tgroup: MaterialGroup,\n\tallVertices: Float32Array,\n\tallIndices: Uint16Array | Uint32Array,\n\tmaterials: THREE.Material[],\n\tallUvs: Float32Array | null = null,\n\tallColors: Uint8Array | null = null\n): THREE.Mesh[] {\n\tconst meshes: THREE.Mesh[] = [];\n\n\tfor (const meshMeta of group.meshes) {\n\t\tconst componentStart = meshMeta.vertexStart * 3;\n\t\tconst componentLen = meshMeta.vertexCount * 3;\n\n\t\t// `subarray` returns a view; copy via `slice` so the BufferAttribute owns its memory and\n\t\t// downstream code (dispose/reuse) can't surprise us by sharing the parser's buffer.\n\t\tconst vertices = allVertices.slice(componentStart, componentStart + componentLen);\n\n\t\tconst indicesSlice = allIndices.subarray(\n\t\t\tmeshMeta.indexStart,\n\t\t\tmeshMeta.indexStart + meshMeta.indexCount\n\t\t);\n\t\tconst rebasedIndices = new Uint32Array(indicesSlice.length);\n\t\tconst baseIndex = meshMeta.vertexStart;\n\t\tconst windowEnd = meshMeta.vertexStart + meshMeta.vertexCount;\n\t\tfor (let i = 0; i < indicesSlice.length; i++) {\n\t\t\tconst indexValue = indicesSlice[i]!;\n\t\t\tif (indexValue < baseIndex || indexValue >= windowEnd) {\n\t\t\t\tthrow indexOutOfWindow(indexValue, meshMeta);\n\t\t\t}\n\t\t\trebasedIndices[i] = indexValue - baseIndex;\n\t\t}\n\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));\n\t\tgeometry.setIndex(new THREE.BufferAttribute(rebasedIndices, 1));\n\t\tif (allUvs) {\n\t\t\tconst uvStart = meshMeta.vertexStart * 2;\n\t\t\tconst uvs = allUvs.slice(uvStart, uvStart + meshMeta.vertexCount * 2);\n\t\t\tgeometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n\t\t}\n\t\tif (allColors) {\n\t\t\tconst colors = allColors.slice(componentStart, componentStart + componentLen);\n\t\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));\n\t\t}\n\t\tgeometry.computeVertexNormals();\n\n\t\tmeshes.push(finalizeSingleMesh(geometry, meshMeta, group, materials));\n\t}\n\n\treturn meshes;\n}\n\nexport function finalizeSingleMesh(\n\tgeometry: THREE.BufferGeometry,\n\tmeshMeta: MeshMetadata,\n\tgroup: MaterialGroup,\n\tmaterials: THREE.Material[]\n): THREE.Mesh {\n\tconst mesh = new THREE.Mesh(geometry, materials[group.materialId]);\n\tmesh.name = meshMeta.name;\n\tmesh.userData = {\n\t\tsource: 'compute',\n\t\tname: meshMeta.name,\n\t\tlayer: meshMeta.layer ?? '',\n\t\toriginalIndex: meshMeta.originalIndex,\n\t\tmetadata: meshMeta.metadata ?? {}\n\t};\n\tmesh.castShadow = true;\n\tmesh.receiveShadow = true;\n\treturn mesh;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { FLAG_FLOAT32, parseBinaryMeshBatch, parseBinaryMeshBatchRaw } from './binary-parser.js';\n\nimport {\n\tASSEMBLY_WORKER_MIN_TRIANGLES,\n\tgetAssemblyWorker,\n\trequestAssembly\n} from './batch/assembly-worker.js';\nimport { createMaterial } from './batch/materials.js';\nimport {\n\tcreateIndividualMeshes,\n\tcreateMergedMesh,\n\tfinalizeMergedMesh,\n\tfinalizeSingleMesh\n} from './batch/merge.js';\nimport { dequantizeInt16, validateGroupMetadata } from './batch/metadata.js';\n\nimport type { AssembledGeometry, AssemblyJob, AssemblyWindow } from './mesh-assembly.js';\nimport type { ParsedBinaryMeshBatch } from './binary-parser.js';\nimport type {\n\tDisplayBatch,\n\tMaterialAppearanceOptions,\n\tMaterialGroup,\n\tMeshBatchParsingOptions,\n\tMeshMetadata,\n\tSerializableMaterial\n} from './types.js';\ninterface ParseTelemetry {\n\tparseTime?: number;\n\tperfStart?: number;\n}\n\n/**\n * Parses a batched mesh JSON and creates Three.js meshes. The geometry payload is the binary\n * \"SLVA\" blob produced by the C# `BinaryGeometryWriter`, base64-encoded into the outer JSON\n * envelope — `JSON.parse`s the small envelope, then hands the blob to `parseBinaryMeshBatch`\n * without ever turning it into a string.\n *\n * An invalid JSON envelope logs and returns `[]` (genuinely absent data). A corrupt, truncated, or\n * unsupported *blob* throws instead of silently rendering an empty scene.\n *\n * @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.\n */\nexport async function parseMeshBatch(\n\tbatchJson: string,\n\toptions?: MeshBatchParsingOptions\n): Promise<THREE.Mesh[]> {\n\tconst { debug = false } = options ?? {};\n\n\tconst perfStart = debug ? performance.now() : 0;\n\n\t// Narrow catch: only the envelope JSON.parse is allowed to degrade to []. Blob parse errors\n\t// from parseMeshBatchObject propagate — see that entry point's contract.\n\tlet batch: DisplayBatch;\n\tconst parseStart = performance.now();\n\ttry {\n\t\tbatch = JSON.parse(batchJson);\n\t} catch (error) {\n\t\tgetLogger().error('Error parsing mesh batch envelope JSON:', error);\n\t\treturn [];\n\t}\n\tconst parseTime = performance.now() - parseStart;\n\n\treturn await parseMeshBatchObject(batch, options, { parseTime, perfStart });\n}\n\n/**\n * Parses a DisplayBatch object and creates Three.js meshes from its mesh blob.\n *\n * Synchronous internally — `parseBinaryMeshBatch` does no IO, just typed-array views over the\n * blob. Stays `async` so callers don't need to change shape if parsing moves into a worker later.\n *\n * @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.\n */\nexport async function parseMeshBatchObject(\n\tbatch: DisplayBatch,\n\toptions?: MeshBatchParsingOptions,\n\t/** @internal Timings threaded from an outer entry point — not a caller option. */\n\ttelemetry?: ParseTelemetry\n): Promise<THREE.Mesh[]> {\n\tconst { mergeByMaterial = true, debug = false, material } = options ?? {};\n\tconst { parseTime = 0, perfStart = debug ? performance.now() : 0 } = telemetry ?? {};\n\n\tif (!batch.compressedData) {\n\t\t// Items-only or empty batch — the one entry-point path that legitimately yields [] rather\n\t\t// than throwing.\n\t\treturn [];\n\t}\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch or no worker support).\n\tconst workerMeshes = await tryBuildViaWorker(batch.compressedData, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial,\n\t\tfallback: {\n\t\t\tmaterials: batch.materials,\n\t\t\tgroups: batch.groups,\n\t\t\tsourceComponentId: batch.sourceComponentId\n\t\t}\n\t});\n\tif (workerMeshes) return workerMeshes;\n\n\tconst decodeStart = performance.now();\n\tconst parsed = parseBinaryMeshBatch(batch.compressedData);\n\tconst decodeTime = performance.now() - decodeStart;\n\n\tconst blobBytes = debug ? approximateBase64DecodedBytes(batch.compressedData) : 0;\n\n\treturn buildMeshesFromParsed(parsed, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial,\n\t\tparseTime,\n\t\tdecodeTime,\n\t\tperfStart,\n\t\tblobBytes,\n\t\tfallback: {\n\t\t\tmaterials: batch.materials,\n\t\t\tgroups: batch.groups,\n\t\t\tsourceComponentId: batch.sourceComponentId\n\t\t}\n\t});\n}\n\n/**\n * Parses a raw binary mesh batch blob (SLVA wire format) and creates Three.js meshes.\n *\n * Use this entry point when the blob arrives as a binary WebSocket frame rather than inside a JSON\n * envelope — the blob is self-describing, with materials, groups, and `sourceComponentId` coming\n * from its embedded metadata header.\n *\n * @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.\n */\nexport async function parseMeshBatchBlob(\n\tblob: ArrayBuffer | Uint8Array,\n\toptions?: MeshBatchParsingOptions\n): Promise<THREE.Mesh[]> {\n\tconst { mergeByMaterial = true, debug = false, material } = options ?? {};\n\n\tconst perfStart = debug ? performance.now() : 0;\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch or no worker support).\n\tconst workerMeshes = await tryBuildViaWorker(blob, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial\n\t});\n\tif (workerMeshes) return workerMeshes;\n\n\tconst decodeStart = performance.now();\n\tconst parsed = parseBinaryMeshBatch(blob);\n\tconst decodeTime = performance.now() - decodeStart;\n\n\tconst blobBytes = blob.byteLength;\n\n\treturn buildMeshesFromParsed(parsed, {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial,\n\t\tparseTime: 0,\n\t\tdecodeTime,\n\t\tperfStart,\n\t\tblobBytes\n\t});\n}\n\ninterface BuildOptions {\n\tmergeByMaterial: boolean;\n\tdebug: boolean;\n\tmaterial?: MaterialAppearanceOptions;\n\tparseTime: number;\n\tdecodeTime: number;\n\tperfStart: number;\n\tblobBytes: number;\n\t/** Outer-envelope fallback used when the blob's metadata is missing fields. */\n\tfallback?: {\n\t\tmaterials?: SerializableMaterial[];\n\t\tgroups?: MaterialGroup[];\n\t\tsourceComponentId?: string;\n\t};\n}\n\nfunction buildMeshesFromParsed(\n\tparsed: ParsedBinaryMeshBatch,\n\topts: BuildOptions\n): Promise<THREE.Mesh[]> {\n\tconst {\n\t\tmergeByMaterial,\n\t\tdebug,\n\t\tmaterial: materialAppearance,\n\t\tparseTime,\n\t\tdecodeTime,\n\t\tperfStart,\n\t\tblobBytes,\n\t\tfallback\n\t} = opts;\n\n\tconst materialsSrc = parsed.metadata.materials ?? fallback?.materials ?? [];\n\tconst groups = parsed.metadata.groups ?? fallback?.groups ?? [];\n\t// Envelope sourceComponentId wins over the blob's embedded one: the blob bakes in the id at\n\t// encode time, but a reloaded part (e.g. a .slvm mesh file instanced many times) re-stamps a fresh id on\n\t// the envelope so web pick identity stays distinct per placement. The blob value only applies\n\t// to the raw-blob transport, which has no envelope.\n\tconst sourceComponentId = fallback?.sourceComponentId ?? parsed.metadata.sourceComponentId;\n\n\tconst isFloat32 = (parsed.flags & FLAG_FLOAT32) !== 0;\n\n\t// Group metadata is used arithmetically below — unchecked, a bad vertexStart/indexStart wraps\n\t// rebased indices into a Uint32Array, `subarray` silently clamps, and an out-of-range\n\t// materialId feeds `undefined` into `new THREE.Mesh`. Fail the parse instead of corrupting\n\t// the render silently.\n\tvalidateGroupMetadata(\n\t\tgroups,\n\t\tmaterialsSrc.length,\n\t\tparsed.vertices.length / 3,\n\t\tparsed.indices.length\n\t);\n\n\t// Dequantize once up front into a single Float32Array — downstream code (per-group merging,\n\t// computeVertexNormals, ground-offset) expects world-unit floats, and one linear pass over the\n\t// int16 buffer beats doing it per group.\n\tconst worldVertices = isFloat32\n\t\t? (parsed.vertices as Float32Array)\n\t\t: dequantizeInt16(parsed.vertices as Int16Array, parsed.origin, parsed.scale);\n\n\tif (debug) {\n\t\tconst wireBytes = parsed.vertices.byteLength + parsed.indices.byteLength;\n\t\tgetLogger().debug('Mesh Batch Stats:');\n\t\tgetLogger().debug(` Materials: ${materialsSrc.length} | Groups: ${groups.length}`);\n\t\tgetLogger().debug(\n\t\t\t` Vertices: ${parsed.vertices.length / 3} | Indices: ${parsed.indices.length}`\n\t\t);\n\t\tgetLogger().debug(` Format: ${isFloat32 ? 'float32' : 'int16 quantized'}`);\n\t\tgetLogger().debug(\n\t\t\t` Blob: ${(blobBytes / 1024 / 1024).toFixed(2)} MB | Geometry on wire: ${(wireBytes / 1024 / 1024).toFixed(2)} MB`\n\t\t);\n\t}\n\n\tconst meshCreateStart = performance.now();\n\t// Vertex colors are batch-wide when present — meshes without real colors carry a white fill,\n\t// which multiplies to identity — so the material enables vertexColors unconditionally.\n\tconst materials = materialsSrc.map((m) =>\n\t\tcreateMaterial(m, {\n\t\t\tvertexColors: parsed.colors != null,\n\t\t\tappearance: materialAppearance\n\t\t})\n\t);\n\n\tconst meshes: THREE.Mesh[] = [];\n\n\tfor (const group of groups) {\n\t\tif (mergeByMaterial && group.meshes.length > 1) {\n\t\t\tconst mergedMesh = createMergedMesh(\n\t\t\t\tgroup,\n\t\t\t\tworldVertices,\n\t\t\t\tparsed.indices,\n\t\t\t\tmaterials,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\n\t\t\t// Left absent when unknown, never null: stable identity tests the field's type to decide\n\t\t\t// whether it can key on the component, and a null would silently demote every mesh of\n\t\t\t// this batch to the weaker name+layer key.\n\t\t\tif (sourceComponentId) mergedMesh.userData.sourceComponentId = sourceComponentId;\n\t\t\tmeshes.push(mergedMesh);\n\t\t} else {\n\t\t\tconst individualMeshes = createIndividualMeshes(\n\t\t\t\tgroup,\n\t\t\t\tworldVertices,\n\t\t\t\tparsed.indices,\n\t\t\t\tmaterials,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\n\t\t\tif (sourceComponentId) {\n\t\t\t\tfor (const mesh of individualMeshes) {\n\t\t\t\t\tmesh.userData.sourceComponentId = sourceComponentId;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmeshes.push(...individualMeshes);\n\t\t}\n\t}\n\n\tconst meshCreateTime = performance.now() - meshCreateStart;\n\n\tif (debug) {\n\t\tconst totalTime = performance.now() - perfStart;\n\t\tgetLogger().debug('Performance:');\n\t\tif (parseTime > 0) getLogger().debug(` Parse JSON: ${parseTime.toFixed(2)}ms`);\n\t\tgetLogger().debug(` Decode binary: ${decodeTime.toFixed(2)}ms`);\n\t\tgetLogger().debug(` Create Meshes: ${meshCreateTime.toFixed(2)}ms`);\n\t\tgetLogger().debug(` Total: ${totalTime.toFixed(2)}ms`);\n\t}\n\n\treturn Promise.resolve(meshes);\n}\n\n// ============================================================================\n// OFF-THREAD ASSEMBLY\n// ============================================================================\n\ninterface WorkerPathOptions {\n\tmergeByMaterial: boolean;\n\tdebug: boolean;\n\tmaterial?: MaterialAppearanceOptions;\n\tfallback?: BuildOptions['fallback'];\n}\n\n/**\n * Attempts the off-thread build. Returns the finished meshes, or `null` when the worker path\n * doesn't apply (no Worker, small batch, worker crashed) — the caller then runs the synchronous\n * path. Malformed-blob/metadata errors throw either way, matching the entry points' contract.\n *\n * The worker always assembles and fingerprints every geometry, even when the main thread ends up\n * preferring an existing cached geometry over the returned buffers. That's fine: cache hits skip\n * the GPU re-upload, so the wasted worker CPU is off the critical path by definition.\n */\nasync function tryBuildViaWorker(\n\tinput: ArrayBuffer | Uint8Array | string,\n\topts: WorkerPathOptions\n): Promise<THREE.Mesh[] | null> {\n\tif (typeof Worker === 'undefined') return null;\n\n\tconst raw = parseBinaryMeshBatchRaw(input);\n\tif (raw.indexData.length / 3 < ASSEMBLY_WORKER_MIN_TRIANGLES) return null;\n\tconst worker = getAssemblyWorker();\n\tif (!worker) return null;\n\n\tconst materialsSrc = raw.metadata.materials ?? opts.fallback?.materials ?? [];\n\tconst groups = raw.metadata.groups ?? opts.fallback?.groups ?? [];\n\tconst sourceComponentId = opts.fallback?.sourceComponentId ?? raw.metadata.sourceComponentId;\n\tvalidateGroupMetadata(groups, materialsSrc.length, raw.vertexCount, raw.indexData.length);\n\n\t// Same job branching as buildMeshesFromParsed, with a parallel ref list to unwrap results by index.\n\tinterface JobRef {\n\t\tkind: 'merged' | 'single';\n\t\tgroup: MaterialGroup;\n\t\tmeshMeta?: MeshMetadata;\n\t}\n\tconst windowOf = (m: MeshMetadata): AssemblyWindow => ({\n\t\tvertexStart: m.vertexStart,\n\t\tvertexCount: m.vertexCount,\n\t\tindexStart: m.indexStart,\n\t\tindexCount: m.indexCount\n\t});\n\tconst jobs: AssemblyJob[] = [];\n\tconst jobRefs: JobRef[] = [];\n\tfor (const group of groups) {\n\t\tif (opts.mergeByMaterial && group.meshes.length > 1) {\n\t\t\tjobs.push({ kind: 'merged', windows: group.meshes.map(windowOf) });\n\t\t\tjobRefs.push({ kind: 'merged', group });\n\t\t} else {\n\t\t\tfor (const meshMeta of group.meshes) {\n\t\t\t\tjobs.push({ kind: 'single', windows: [windowOf(meshMeta)] });\n\t\t\t\tjobRefs.push({ kind: 'single', group, meshMeta });\n\t\t\t}\n\t\t}\n\t}\n\n\t// vertexData/indexData alias the caller's blob buffer — copy before transferring so the\n\t// transfer can't detach it. UV/color arrays are already fresh copies and transfer directly.\n\tconst vertexData = raw.vertexData.slice();\n\tconst indexData = raw.indexData.slice();\n\tconst transfer: Transferable[] = [vertexData.buffer, indexData.buffer];\n\tif (raw.uvs) transfer.push(raw.uvs.buffer);\n\tif (raw.colors) transfer.push(raw.colors.buffer);\n\n\tlet assembled: AssembledGeometry[];\n\ttry {\n\t\tassembled = await requestAssembly(\n\t\t\tworker,\n\t\t\t{\n\t\t\t\tvertexData,\n\t\t\t\tisFloat32: raw.isFloat32,\n\t\t\t\tdeltaEncoded: raw.deltaEncoded,\n\t\t\t\torigin: raw.origin,\n\t\t\t\tscale: raw.scale,\n\t\t\t\tindexData,\n\t\t\t\tuvs: raw.uvs,\n\t\t\t\tcolors: raw.colors,\n\t\t\t\tjobs\n\t\t\t},\n\t\t\ttransfer\n\t\t);\n\t} catch (error) {\n\t\tgetLogger().warn('Mesh assembly worker failed; falling back to main-thread parse.', error);\n\t\treturn null;\n\t}\n\tif (assembled.length !== jobs.length) return null; // protocol mismatch → fall back to sync path\n\n\tconst materials = materialsSrc.map((m) =>\n\t\tcreateMaterial(m, { vertexColors: raw.colors != null, appearance: opts.material })\n\t);\n\n\tconst meshes: THREE.Mesh[] = [];\n\tfor (let i = 0; i < assembled.length; i++) {\n\t\tconst result = assembled[i]!;\n\t\tconst ref = jobRefs[i]!;\n\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(result.positions, 3));\n\t\tgeometry.setAttribute('normal', new THREE.BufferAttribute(result.normals, 3));\n\t\tgeometry.setIndex(new THREE.BufferAttribute(result.indices, 1));\n\t\tif (result.uvs) geometry.setAttribute('uv', new THREE.BufferAttribute(result.uvs, 2));\n\t\tif (result.colors) {\n\t\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(result.colors, 3, true));\n\t\t}\n\n\t\tconst mesh =\n\t\t\tref.kind === 'merged'\n\t\t\t\t? finalizeMergedMesh(geometry, ref.group, materials)\n\t\t\t\t: finalizeSingleMesh(geometry, ref.meshMeta!, ref.group, materials);\n\t\tif (sourceComponentId) mesh.userData.sourceComponentId = sourceComponentId;\n\t\tmeshes.push(mesh);\n\t}\n\n\tif (opts.debug) {\n\t\tgetLogger().debug(\n\t\t\t`Mesh batch assembled off-thread: ${meshes.length} meshes, ${raw.indexData.length / 3} triangles`\n\t\t);\n\t}\n\treturn meshes;\n}\n\n// ============================================================================\n// DEBUG HELPERS\n// ============================================================================\n\nfunction approximateBase64DecodedBytes(base64: string): number {\n\treturn Math.floor((base64.length * 3) / 4);\n}\n","import * as THREE from 'three';\n\nimport { applyOffset, computeCombinedBoundingBox, getLogger } from '../../shared/index.js';\n\nimport { parseDisplayItems } from '../display-items/display-items-parser.js';\n\nimport { parseMeshBatchObject } from './batch-parser.js';\n\nimport type { DisplayDataItem, DisplayComputeResponse } from './response-envelope.js';\nimport type { DisplayBatch, MeshExtractionOptions, MeshBatchParsingOptions } from './types.js';\n\n// Constants\n\n/**\n * Metres per model unit, keyed by Rhino `UnitSystem` name (the `modelunits` string on the compute\n * response). Imperial factors are the exact international definitions. Units missing from this\n * table scale by 1 and log a one-time warning — see {@link getScaleFactor}.\n */\nexport const SCALE_FACTORS: Record<string, number> = {\n\t// Metric\n\tAngstroms: 1e-10,\n\tNanometers: 1e-9,\n\tMicrons: 1e-6,\n\tMillimeters: 1e-3,\n\tCentimeters: 1e-2,\n\tDecimeters: 0.1,\n\tMeters: 1,\n\tDekameters: 10,\n\tHectometers: 100,\n\tKilometers: 1000,\n\tMegameters: 1e6,\n\tGigameters: 1e9,\n\t// Imperial (exact: 1 inch = 0.0254 m)\n\tMicroinches: 0.0254e-6,\n\tMils: 0.0254e-3,\n\tInches: 0.0254,\n\tFeet: 0.3048,\n\tYards: 0.9144,\n\tMiles: 1609.344,\n\tNauticalMiles: 1852\n};\n\nconst DISPLAY_COMPONENT_TYPE = 'Display';\nconst DISPLAY_BATCH_TYPE = 'DisplayBatch';\n\n/**\n * True when a wire `type` denotes a Display payload: one of its dot-separated tokens is exactly\n * `Display` or `DisplayBatch`. Matches the bare `Display` used by older servers and the namespaced\n * `Selva.GH.Features.Display.Services.DisplayBatch`, but not e.g. `System.DisplayText` — matching\n * on tokens rather than substring avoids misrouting an unrelated type that merely contains \"Display\".\n */\nfunction isDisplayItemType(type: string): boolean {\n\tconst tokens = type.split('.');\n\treturn tokens.includes(DISPLAY_COMPONENT_TYPE) || tokens.includes(DISPLAY_BATCH_TYPE);\n}\n\n/** Unknown-unit names already warned about, so a per-solve parse doesn't spam the log. */\nconst warnedUnknownUnits = new Set<string>();\n\n/**\n * Extracts display meshes and items from a Grasshopper WebDisplay compute response: decompresses,\n * scales to meters, and optionally grounds them. Requires the VektorNode Rhino.Compute fork.\n *\n * Synchronous internally (large batches block the UI for their duration); `async` only so the\n * shape can stay stable if parsing moves off-thread later.\n *\n * @throws Rethrows unexpected errors after attempting to dispose any created meshes.\n */\nexport async function getThreeMeshesFromComputeResponse(\n\tdata: DisplayComputeResponse,\n\toptions?: MeshExtractionOptions\n): Promise<THREE.Object3D[]> {\n\tconst startTime = performance.now();\n\tconst objects: THREE.Object3D[] = [];\n\n\tconst {\n\t\tallowScaling = true,\n\t\t// Defaults to false so picked/measured values match the GH definition's own coordinates\n\t\t// rather than shifting per transport.\n\t\tallowAutoPosition = false,\n\t\tgroundAxis = 'z',\n\t\tdebug = false,\n\t\tparsing: parsingOptions = {}\n\t} = options ?? {};\n\n\ttry {\n\t\tconst scaleFactor = allowScaling ? getScaleFactor(data.modelunits) : 1;\n\t\tawait extractDisplayFromData(data, objects, scaleFactor, parsingOptions, debug);\n\n\t\tif (allowAutoPosition) {\n\t\t\tapplyGroundOffset(objects, groundAxis);\n\t\t}\n\n\t\treturn objects;\n\t} catch (error) {\n\t\thandleError(error, objects);\n\t\tthrow error;\n\t} finally {\n\t\tif (debug) {\n\t\t\tlogProcessingTime(startTime);\n\t\t}\n\t}\n}\n\n/**\n * Gets the metres-per-unit scale factor for a Rhino unit name. Unknown units fall back to 1 (no\n * scaling) with a one-time warning — a kilometers model rendering 1000x off should at least say why.\n */\nfunction getScaleFactor(modelUnits: string): number {\n\tconst factor = SCALE_FACTORS[modelUnits];\n\tif (factor !== undefined) {\n\t\treturn factor;\n\t}\n\tif (!warnedUnknownUnits.has(modelUnits)) {\n\t\twarnedUnknownUnits.add(modelUnits);\n\t\tgetLogger().warn(\n\t\t\t`Unknown Rhino model unit \"${modelUnits}\" — geometry will not be scaled (factor 1). ` +\n\t\t\t\t`Known units: ${Object.keys(SCALE_FACTORS).join(', ')}.`\n\t\t);\n\t}\n\treturn 1;\n}\n\nasync function extractDisplayFromData(\n\tdata: DisplayComputeResponse,\n\tobjects: THREE.Object3D[],\n\tscaleFactor: number,\n\tparsingOptions: MeshBatchParsingOptions,\n\tdebug: boolean\n): Promise<void> {\n\tfor (const value of data.values) {\n\t\tconst innerTree = value.InnerTree;\n\n\t\tfor (const path in innerTree) {\n\t\t\tconst branch = innerTree[path];\n\t\t\tif (!branch) continue;\n\n\t\t\tawait processDataBranch(branch, objects, scaleFactor, parsingOptions, debug);\n\t\t}\n\t}\n}\n\n/** Extracts a DisplayBatch's meshes (binary blob) and items (curves/points JSON) from one data branch. */\nasync function processDataBranch(\n\tbranch: DisplayDataItem[],\n\tobjects: THREE.Object3D[],\n\tscaleFactor: number,\n\tparsingOptions: MeshBatchParsingOptions,\n\tdebug: boolean\n): Promise<void> {\n\tfor (const item of branch) {\n\t\tif (!isDisplayItemType(item.type)) continue;\n\n\t\tconst mergedParsingOptions = {\n\t\t\tmergeByMaterial: true,\n\t\t\tdebug: false,\n\t\t\t...parsingOptions\n\t\t};\n\n\t\t// Parsed once and shared: item.data is a multi-MB base64 SLVA blob, so parsing it twice (once\n\t\t// for the mesh parser, once for the item extractor) would double both CPU and string memory.\n\t\tconst batch = extractBatch(item.data);\n\t\tif (!batch) {\n\t\t\tgetLogger().error('Error parsing display batch envelope: invalid JSON');\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst batchMeshes = await parseMeshBatchObject(batch, mergedParsingOptions);\n\n\t\tconst batchItems = parseDisplayItems(batch.items);\n\n\t\tconst batchObjects: THREE.Object3D[] = [...batchMeshes, ...batchItems];\n\n\t\t// Meshes and items share one scale factor so they end up in the same frame.\n\t\tif (scaleFactor !== 1) {\n\t\t\tfor (const obj of batchObjects) {\n\t\t\t\tobj.scale.set(scaleFactor, scaleFactor, scaleFactor);\n\t\t\t}\n\t\t}\n\n\t\tobjects.push(...batchObjects);\n\n\t\tif (debug) {\n\t\t\tgetLogger().debug(\n\t\t\t\t`Extracted ${batchMeshes.length} meshes and ${batchItems.length} items from batch`\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** Resolves `item.data` to a parsed DisplayBatch, tolerating either an already-parsed object or a JSON string. */\nfunction extractBatch(data: unknown): DisplayBatch | undefined {\n\treturn typeof data === 'string' ? safeParse(data) : (data as DisplayBatch | undefined);\n}\n\nfunction safeParse(s: string): DisplayBatch | undefined {\n\ttry {\n\t\treturn JSON.parse(s) as DisplayBatch;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Drops objects so their lowest point sits on the ground plane. `axis` isn't hardcoded to `z`\n * because subtracting `min.z` on a host with a non-default `sceneUp` would shove content sideways\n * instead of down.\n */\nfunction applyGroundOffset(meshes: THREE.Object3D[], axis: 'x' | 'y' | 'z'): void {\n\tif (meshes.length === 0) return;\n\n\tconst combinedBoundingBox = computeCombinedBoundingBox(meshes);\n\tapplyOffset(meshes, combinedBoundingBox.min[axis], axis);\n}\n\nfunction handleError(error: unknown, meshes: THREE.Object3D[]): void {\n\tgetLogger().error('An unexpected error occurred:', error);\n\tdisposeMeshes(meshes);\n}\n\nfunction disposeMeshes(meshes: THREE.Object3D[]): void {\n\tfor (const obj of meshes) {\n\t\tconst mesh = obj as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tif (mesh.geometry) {\n\t\t\tmesh.geometry.dispose();\n\t\t}\n\n\t\tif (mesh.material) {\n\t\t\tif (Array.isArray(mesh.material)) {\n\t\t\t\tmesh.material.forEach((material) => material.dispose());\n\t\t\t} else {\n\t\t\t\tmesh.material.dispose();\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction logProcessingTime(startTime: number): void {\n\tconst elapsed = performance.now() - startTime;\n\tgetLogger().info('Time to process meshes:', `${elapsed.toFixed(2)}ms`);\n}\n"],"mappings":"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"}
|
package/dist/scene.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./rolldown-runtime-BocRIvOZ.cjs");let t=require("three");t=e.t(t,1);const n=new Set([`grid`,`floor`,`label-layer`,`measure`]);function r(e){return!(e instanceof t.Camera)&&!(e instanceof t.Light)&&!n.has(e.userData?.id)}function i(e){return e.children.filter(r)}function a(e){return e.replace(/^Line(Segments)?2$/,`Curve`).replace(`Mesh`,``).replace(`Object3D`,`Obj`)||e}function o(e){return e.userData?.name||e.userData?.fileName||e.name||a(e.type)}function s(e){return a(e.type)}function c(e){let t=new Map;for(let n of e){let e=n.userData?.layer||n.userData?.category||`Default`,r=t.get(e);r||(r=[],t.set(e,r)),r.push(n)}return t}function l(e,t){if(!t.trim())return e;let n=t.toLowerCase(),r=new Map;for(let[t,i]of e){let e=t.toLowerCase().includes(n)?i:i.filter(e=>o(e).toLowerCase().includes(n));e.length>0&&r.set(t,e)}return r}function u(e){let t=e.userData;if(!t)return null;if(typeof t.id==`string`&&t.id)return t.id;if(typeof t.sourceComponentId==`string`&&t.sourceComponentId){let e=typeof t.originalIndex==`number`?t.originalIndex:0;return`gh${t.sourceComponentId}${e}`}let n=typeof t.name==`string`?t.name:e.name,r=typeof t.layer==`string`?t.layer:``;return n?`name${r}${n}`:null}function d(e){return u(e)??e.uuid}function f(e=new Set){let t={hidden:e,isHidden:t=>e.has(d(t)),setVisible(t,n){t.visible=n,t.traverse(e=>{e.visible=n});let r=d(t);n?e.delete(r):e.add(r)},isLayerHidden:e=>e.length>0&&e.every(e=>t.isHidden(e)),isLayerPartial(e){let n=e.filter(e=>t.isHidden(e)).length;return n>0&&n<e.length},toggleLayer(e){let n=t.isLayerHidden(e);for(let r of e)t.setVisible(r,n)},applyTo(t){for(let n of t)e.has(d(n))&&(n.visible=!1,n.traverse(e=>{e.visible=!1}))},reset:()=>e.clear()};return t}function p(e=new Set){let t=null,n=new Set,r=e=>{t=e;for(let t of n)t(e)};return{selected:e,get anchor(){return t},isSelected:t=>e.has(t),select(n,i,a){if(i.shiftKey&&t){let r=a(),o=r.indexOf(t),s=r.indexOf(n);if(o!==-1&&s!==-1){let[t,n]=o<s?[o,s]:[s,o];i.toggleKey||e.clear();for(let i=t;i<=n;i++)e.add(r[i])}return}i.toggleKey?e.has(n)?e.delete(n):e.add(n):(e.clear(),e.add(n)),r(n)},clear(){e.clear(),r(null)},onAnchorChange(e){return n.add(e),()=>n.delete(e)}}}function m(e,t={}){let n=t.sets?.collapsed??new Set,r=f(t.sets?.hidden),a=p(t.sets?.selected),o={visibility:r,selection:a,collapsed:n,searchQuery:``,objects:()=>i(e),layerGroups:()=>l(c(i(e)),o.searchQuery),isCollapsed:e=>n.has(e),toggleCollapsed(e){n.has(e)?n.delete(e):n.add(e)},toggleObject(e){if(a.isSelected(e.uuid)&&a.selected.size>1){let e=o.objects().filter(e=>a.isSelected(e.uuid)),t=e.every(e=>r.isHidden(e));for(let n of e)r.setVisible(n,t)}else r.setVisible(e,r.isHidden(e))},select(e,t){a.select(e,t,()=>o.flatVisibleUuids())},onAnchorChange:e=>a.onAnchorChange(e),flatVisibleUuids(){let e=[];for(let[t,r]of o.layerGroups())if(!n.has(t))for(let t of r)e.push(t.uuid);return e},applyTo(){r.applyTo(o.objects()),a.clear()},reset(){r.reset(),a.clear()}};return o}exports.createSceneOutliner=m,exports.getObjectLabel=o,exports.getTrackingKey=d,exports.getTypeLabel=s;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./rolldown-runtime-BocRIvOZ.cjs");let t=require("three");t=e.t(t,1);const n=new Set([`grid`,`floor`,`label-layer`,`measure`]);function r(e){return!(e instanceof t.Camera)&&!(e instanceof t.Light)&&!n.has(e.userData?.id)}function i(e){return e.children.filter(r)}function a(e){return e.replace(/^Line(Segments)?2$/,`Curve`).replace(`Mesh`,``).replace(`Object3D`,`Obj`)||e}function o(e){return e.userData?.name||e.userData?.fileName||e.name||a(e.type)}function s(e){return a(e.type)}function c(e){let t=new Map;for(let n of e){let e=n.userData?.layer||n.userData?.category||`Default`,r=t.get(e);r||(r=[],t.set(e,r)),r.push(n)}return t}function l(e,t){if(!t.trim())return e;let n=t.toLowerCase(),r=new Map;for(let[t,i]of e){let e=t.toLowerCase().includes(n)?i:i.filter(e=>o(e).toLowerCase().includes(n));e.length>0&&r.set(t,e)}return r}function u(e){let t=e.userData;if(!t)return null;if(typeof t.id==`string`&&t.id)return t.id;if(typeof t.sourceComponentId==`string`&&t.sourceComponentId){if(Array.isArray(t.mergedIndices)&&t.mergedIndices.length>0){let e=[...t.mergedIndices].sort((e,t)=>e-t).join(`,`);return`gh${t.sourceComponentId}m${e}`}let e=typeof t.originalIndex==`number`?t.originalIndex:0;return`gh${t.sourceComponentId}${e}`}let n=typeof t.name==`string`?t.name:e.name,r=typeof t.layer==`string`?t.layer:``;return n?`name${r}${n}`:null}function d(e){return u(e)??e.uuid}function f(e=new Set){let t={hidden:e,isHidden:t=>e.has(d(t)),setVisible(t,n){t.visible=n,t.traverse(e=>{e.visible=n});let r=d(t);n?e.delete(r):e.add(r)},isLayerHidden:e=>e.length>0&&e.every(e=>t.isHidden(e)),isLayerPartial(e){let n=e.filter(e=>t.isHidden(e)).length;return n>0&&n<e.length},toggleLayer(e){let n=t.isLayerHidden(e);for(let r of e)t.setVisible(r,n)},applyTo(t){for(let n of t)e.has(d(n))&&(n.visible=!1,n.traverse(e=>{e.visible=!1}))},reset:()=>e.clear()};return t}function p(e=new Set){let t=null,n=new Set,r=e=>{t=e;for(let t of n)t(e)};return{selected:e,get anchor(){return t},isSelected:t=>e.has(t),select(n,i,a){if(i.shiftKey&&t){let r=a(),o=r.indexOf(t),s=r.indexOf(n);if(o!==-1&&s!==-1){let[t,n]=o<s?[o,s]:[s,o];i.toggleKey||e.clear();for(let i=t;i<=n;i++)e.add(r[i])}return}i.toggleKey?e.has(n)?e.delete(n):e.add(n):(e.clear(),e.add(n)),r(n)},clear(){e.clear(),r(null)},onAnchorChange(e){return n.add(e),()=>n.delete(e)}}}function m(e,t={}){let n=t.sets?.collapsed??new Set,r=f(t.sets?.hidden),a=p(t.sets?.selected),o={visibility:r,selection:a,collapsed:n,searchQuery:``,objects:()=>i(e),layerGroups:()=>l(c(i(e)),o.searchQuery),isCollapsed:e=>n.has(e),toggleCollapsed(e){n.has(e)?n.delete(e):n.add(e)},toggleObject(e){if(a.isSelected(e.uuid)&&a.selected.size>1){let e=o.objects().filter(e=>a.isSelected(e.uuid)),t=e.every(e=>r.isHidden(e));for(let n of e)r.setVisible(n,t)}else r.setVisible(e,r.isHidden(e))},select(e,t){a.select(e,t,()=>o.flatVisibleUuids())},onAnchorChange:e=>a.onAnchorChange(e),flatVisibleUuids(){let e=[];for(let[t,r]of o.layerGroups())if(!n.has(t))for(let t of r)e.push(t.uuid);return e},applyTo(){r.applyTo(o.objects()),a.clear()},reset(){r.reset(),a.clear()}};return o}exports.createSceneOutliner=m,exports.getObjectLabel=o,exports.getTrackingKey=d,exports.getTypeLabel=s;
|
|
2
2
|
//# sourceMappingURL=scene.cjs.map
|