@selvajs/visualization 1.0.0-beta.0 → 1.0.0-beta.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/README.md +4 -3
- package/dist/chunk-AZ4GBXXL.cjs +2 -0
- package/dist/chunk-AZ4GBXXL.cjs.map +1 -0
- package/dist/chunk-BYLIBOAU.cjs.map +1 -1
- package/dist/chunk-KRA5RVHM.js +2 -0
- package/dist/chunk-KRA5RVHM.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/parse.cjs +3 -3
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +31 -45
- package/dist/parse.d.ts +31 -45
- package/dist/parse.js +3 -3
- package/dist/parse.js.map +1 -1
- package/dist/render.cjs +9 -10
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +36 -44
- package/dist/render.d.ts +36 -44
- package/dist/render.js +9 -10
- package/dist/render.js.map +1 -1
- package/dist/scene.cjs.map +1 -1
- package/dist/scene.d.cts +4 -9
- package/dist/scene.d.ts +4 -9
- package/dist/scene.js.map +1 -1
- package/dist/{types-CdF9R3qA.d.cts → types-DCuos3gI.d.cts} +4 -12
- package/dist/{types-CdF9R3qA.d.ts → types-DCuos3gI.d.ts} +4 -12
- package/package.json +2 -3
- package/dist/chunk-AQJPVUH3.cjs +0 -2
- package/dist/chunk-AQJPVUH3.cjs.map +0 -1
- package/dist/chunk-EXAI6IC5.js +0 -2
- package/dist/chunk-EXAI6IC5.js.map +0 -1
package/dist/parse.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/parse/mesh-policy.ts","../src/parse/display-items/items/curves.ts","../src/parse/display-items/items/appearance.ts","../src/parse/display-items/items/points.ts","../src/parse/display-items/display-items-parser.ts","../src/parse/webdisplay/batch-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/geometry-cache.ts","../src/parse/webdisplay/mesh-assembly.ts","../src/parse/webdisplay/batch/assembly-worker.ts","../src/parse/webdisplay/batch/materials.ts","../src/parse/webdisplay/texture-cache.ts","../src/parse/webdisplay/batch/merge.ts","../src/parse/webdisplay/batch/metadata.ts","../src/parse/webdisplay/webdisplay-parser.ts","../src/parse/release-caches.ts"],"sourcesContent":["// 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 { CACHED_GEOMETRY_USERDATA_FLAG, disposeObjectTree } from '../shared/index.js';\n\n/**\n * A three.js object graph the caller owns outright: transforms cloned, geometry copied,\n * materials shared.\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\tconst geometry = source.geometry.clone();\n\t\t\t// `BufferGeometry.clone()` carries userData (including the cache-ownership flag) across\n\t\t\t// by reference, not by copy — deleting the flag in place would un-flag the source too,\n\t\t\t// and the geometry cache would then have its GPU buffers disposed out from under it.\n\t\t\t// Shallow-copy userData first, then drop the flag from the copy only.\n\t\t\tgeometry.userData = { ...geometry.userData };\n\t\t\tdelete geometry.userData[CACHED_GEOMETRY_USERDATA_FLAG];\n\t\t\ttarget.geometry = geometry;\n\t\t});\n\t\treturn copy;\n\t});\n}\n\n/**\n * Release the GPU buffers of objects the memo owns. Mirrors `clearScene`'s traversal, minus\n * materials — the memo never owns those (see {@link cloneSceneObjects}).\n */\nexport function releaseSceneObjects(meshes: THREE.Object3D[]): void {\n\tmeshes.forEach((root) => disposeObjectTree(root, { materials: false }));\n}\n\n/**\n * Structural, not nominal: satisfies `@selvajs/solve/client`'s `MeshPolicy<THREE.Object3D>`\n * without this package depending on solve.\n */\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';\nimport { 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 { getLogger } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayCurve } from '../types';\nimport type { RhinoModule } from 'rhino3dm';\n\n/** Initial uniform splits before adaptive refinement, so closed/looping curves aren't collapsed. */\nconst CURVE_INITIAL_SEGMENTS = 12;\n/** Chord-deviation tolerance as a fraction of the curve's bounding-box diagonal. */\nconst CURVE_CHORD_TOLERANCE_RATIO = 0.0004;\n/** Recursion-depth cap per initial span, so a pathological curve can't explode the vertex count. */\nconst CURVE_MAX_SUBDIVISION_DEPTH = 12;\n/** Max turn angle (radians) allowed across a span before it's split. */\nconst CURVE_MAX_TURN_RADIANS = 0.05;\n\nconst DEFAULT_LINE_WIDTH = 2;\n\n/**\n * Never throws — returns null so one bad curve can't abort the batch.\n *\n * Uses `Line2`/`LineMaterial` rather than `THREE.Line` because plain `THREE.Line` is hard-capped at\n * 1px on every major GPU backend, so `item.width` would otherwise be unhonoured. `Line2.onBeforeRender`\n * sets `LineMaterial`'s required `resolution`, so no renderer reference is needed here.\n */\nexport function buildCurveLine(item: DisplayCurve, rhino: RhinoModule | undefined): Line2 | null {\n\tif (!rhino) {\n\t\tgetLogger().warn('No rhino3dm instance provided; skipping curve display item.');\n\t\treturn null;\n\t}\n\n\tconst curve = decodeCurve(item.json, rhino);\n\tif (!curve) return null;\n\n\tlet points: THREE.Vector3[];\n\ttry {\n\t\tpoints = tessellate(curve);\n\t} catch (error) {\n\t\tgetLogger().warn('Failed to tessellate curve display item; skipping.', error);\n\t\treturn null;\n\t} finally {\n\t\tdeleteRhinoObject(curve);\n\t}\n\tif (points.length < 2) return null;\n\n\tconst positions: number[] = [];\n\tfor (const p of points) positions.push(p.x, p.y, p.z);\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/** emscripten bindings aren't reclaimed by JS GC — free them explicitly. */\nfunction deleteRhinoObject(obj: unknown): void {\n\t(obj as { delete?: () => void } | null | undefined)?.delete?.();\n}\n\nfunction decodeCurve(json: string, rhino: RhinoModule): InstanceType<RhinoModule['Curve']> | null {\n\ttry {\n\t\tconst parsed = JSON.parse(json);\n\t\tconst obj = rhino.CommonObject.decode(parsed);\n\t\t// decode() returns a CommonObject; only curves carry pointAt, so use it to detect a miss.\n\t\tif (obj && typeof (obj as { pointAt?: unknown }).pointAt === 'function') {\n\t\t\treturn obj as InstanceType<RhinoModule['Curve']>;\n\t\t}\n\t\tdeleteRhinoObject(obj);\n\t\tgetLogger().warn('Decoded display-item JSON is not a curve; skipping.');\n\t\treturn null;\n\t} catch (error) {\n\t\tgetLogger().warn('Failed to decode curve display item JSON:', error);\n\t\treturn null;\n\t}\n}\n\n/**\n * Most curves Grasshopper emits are linear, so uniform sampling would needlessly inflate them to\n * {@link CURVE_INITIAL_SEGMENTS}+1 points. Exact vertices for anything rhino3dm reports as a\n * polyline; only genuinely curved geometry falls through to {@link sampleUniform}.\n */\nfunction tessellate(curve: InstanceType<RhinoModule['Curve']>): THREE.Vector3[] {\n\tconst exact = tryPolylineVertices(curve);\n\tif (exact) return exact;\n\n\treturn sampleUniform(curve);\n}\n\ninterface PolylineLike {\n\tcount: number;\n\tget(index: number): number[];\n}\n\nfunction tryPolylineVertices(curve: InstanceType<RhinoModule['Curve']>): THREE.Vector3[] | null {\n\tif (!curve.isPolyline()) return null;\n\n\t// rhino3dm's WASM tryGetPolyline returns the Polyline directly, not the documented [ok, Polyline]\n\t// tuple — accept either shape.\n\tconst result = curve.tryGetPolyline() as unknown;\n\tconst polyline = (Array.isArray(result) ? result[1] : result) as PolylineLike | null;\n\tif (!polyline || typeof polyline.count !== 'number' || polyline.count < 2) {\n\t\tdeleteRhinoObject(polyline);\n\t\treturn null;\n\t}\n\n\tconst out: THREE.Vector3[] = [];\n\tfor (let i = 0; i < polyline.count; i++) {\n\t\tconst p = polyline.get(i);\n\t\tout.push(new THREE.Vector3(p[0], p[1], p[2]));\n\t}\n\n\tdeleteRhinoObject(polyline);\n\treturn out;\n}\n\n/**\n * Adaptively samples any curved type via `pointAt`: starts from {@link CURVE_INITIAL_SEGMENTS} uniform\n * spans, recursively subdividing only where the curve actually bends. Tolerance is a fraction of the\n * bounding-box diagonal, so a tiny fillet and a huge arc get the same *visual* smoothness.\n */\nfunction sampleUniform(curve: InstanceType<RhinoModule['Curve']>): THREE.Vector3[] {\n\tconst domain = curve.domain;\n\tconst t0 = domain[0];\n\tconst t1 = domain[1];\n\tconst span = t1 - t0;\n\n\tconst evalAt = (t: number): THREE.Vector3 => {\n\t\tconst p = curve.pointAt(t);\n\t\treturn new THREE.Vector3(p[0], p[1], p[2]);\n\t};\n\n\tconst tolerance = chordTolerance(curve);\n\n\tlet ta = t0;\n\tlet pa = evalAt(t0);\n\tconst out: THREE.Vector3[] = [pa];\n\tfor (let i = 0; i < CURVE_INITIAL_SEGMENTS; i++) {\n\t\tconst tb = t0 + (span * (i + 1)) / CURVE_INITIAL_SEGMENTS;\n\t\tconst pb = evalAt(tb);\n\t\tsubdivide(ta, pa, tb, pb, evalAt, tolerance, CURVE_MAX_SUBDIVISION_DEPTH, out);\n\t\tout.push(pb);\n\t\tta = tb;\n\t\tpa = pb;\n\t}\n\n\treturn out;\n}\n\nfunction subdivide(\n\tta: number,\n\tpa: THREE.Vector3,\n\ttb: number,\n\tpb: THREE.Vector3,\n\tevalAt: (t: number) => THREE.Vector3,\n\ttolerance: number,\n\tdepth: number,\n\tout: THREE.Vector3[]\n): void {\n\tif (depth <= 0) return;\n\n\tconst tm = (ta + tb) / 2;\n\tconst pm = evalAt(tm);\n\n\t// Subdivide on chord deviation OR on the turn angle at the midpoint. A pure deviation test can\n\t// pass a long, gently-curving span whose endpoints straddle the chord symmetrically; the angle\n\t// test catches the visible kink at span joints that deviation alone misses.\n\tconst deviation = distanceToSegment(pm, pa, pb);\n\tconst turn = turnAngle(pa, pm, pb);\n\tif (deviation <= tolerance && turn <= CURVE_MAX_TURN_RADIANS) return;\n\n\tsubdivide(ta, pa, tm, pm, evalAt, tolerance, depth - 1, out);\n\tout.push(pm);\n\tsubdivide(tm, pm, tb, pb, evalAt, tolerance, depth - 1, out);\n}\n\nfunction chordTolerance(curve: InstanceType<RhinoModule['Curve']>): number {\n\t// rhino3dm WASM's getBoundingBox takes no args at runtime despite the .d.ts signature.\n\tconst box = (\n\t\tcurve as unknown as { getBoundingBox(): InstanceType<RhinoModule['BoundingBox']> }\n\t).getBoundingBox();\n\tconst min = box.min;\n\tconst max = box.max;\n\tdeleteRhinoObject(box);\n\tconst diagonal = Math.hypot(max[0] - min[0], max[1] - min[1], max[2] - min[2]);\n\treturn Math.max(diagonal * CURVE_CHORD_TOLERANCE_RATIO, 1e-6);\n}\n\n/**\n * Turn angle (radians) at `b` along a→b→c; 0 = straight, π = reversal. Scalar math rather than\n * Vector3 temporaries — this recurses up to ~2^12 times per curve and clone() churn was measurable.\n */\nfunction turnAngle(a: THREE.Vector3, b: THREE.Vector3, c: THREE.Vector3): number {\n\tconst abx = b.x - a.x;\n\tconst aby = b.y - a.y;\n\tconst abz = b.z - a.z;\n\tconst bcx = c.x - b.x;\n\tconst bcy = c.y - b.y;\n\tconst bcz = c.z - b.z;\n\n\tconst lenAb = Math.sqrt(abx * abx + aby * aby + abz * abz);\n\tconst lenBc = Math.sqrt(bcx * bcx + bcy * bcy + bcz * bcz);\n\tif (lenAb === 0 || lenBc === 0) return 0;\n\n\tconst dot = abx * bcx + aby * bcy + abz * bcz;\n\tconst cos = Math.max(-1, Math.min(1, dot / (lenAb * lenBc)));\n\treturn Math.acos(cos);\n}\n\n/** Perpendicular distance from `p` to segment a→b, clamped to endpoints. */\nfunction distanceToSegment(p: THREE.Vector3, a: THREE.Vector3, b: THREE.Vector3): number {\n\tconst abx = b.x - a.x;\n\tconst aby = b.y - a.y;\n\tconst abz = b.z - a.z;\n\tconst lengthSq = abx * abx + aby * aby + abz * abz;\n\tif (lengthSq === 0) return p.distanceTo(a);\n\n\tconst apx = p.x - a.x;\n\tconst apy = p.y - a.y;\n\tconst apz = p.z - a.z;\n\tconst t = Math.max(0, Math.min(1, (apx * abx + apy * aby + apz * abz) / lengthSq));\n\n\tconst dx = apx - abx * t;\n\tconst dy = apy - aby * t;\n\tconst dz = apz - abz * t;\n\treturn Math.sqrt(dx * dx + dy * dy + dz * dz);\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 * 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';\nimport type { RhinoModule } from 'rhino3dm';\n\nexport interface DisplayItemParseOptions {\n\t/** Omit to skip curves; points still render. */\n\trhino?: RhinoModule;\n}\n\nexport function parseDisplayItems(\n\titems: DisplayItem[] | undefined,\n\toptions: DisplayItemParseOptions = {}\n): THREE.Object3D[] {\n\tif (!items || items.length === 0) return [];\n\n\tconst { rhino } = options;\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, rhino);\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 * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { FLAG_FLOAT32, parseBinaryMeshBatch, parseBinaryMeshBatchRaw } from './binary-parser.js';\nimport { geometryCacheGet, geometryCachePut } from './geometry-cache.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';\n/** Internal telemetry only (not exposed in public options). */\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`, which\n * decodes the geometry without ever turning it into a string.\n *\n * An invalid JSON envelope (not a batch at all) logs and returns `[]` — the \"genuinely absent\n * data\" case. A batch whose *blob* is corrupt, truncated, or unsupported throws instead of\n * 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 have 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// No blob at all — an items-only or empty batch. This is the one entry-point path that\n\t\t// legitimately yields [] rather 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 — materials, groups, and `sourceComponentId` come from its\n * 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 when the blob's metadata is missing fields (defensive). */\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// Prefer the outer envelope's sourceComponentId over the blob's embedded one. The blob bakes in\n\t// the id at encode time, but a reloaded part (e.g. from a .dmf instanced many times) re-stamps a\n\t// fresh id on the envelope to keep web pick identity distinct per placement. The blob value is\n\t// the fallback for 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 arrives as embedded (or envelope) JSON and is used arithmetically below —\n\t// unchecked, a bad vertexStart/indexStart wraps rebased indices into a Uint32Array, `subarray`\n\t// silently clamps, and an out-of-range materialId feeds `undefined` into `new THREE.Mesh`.\n\t// Fail the parse instead of silently corrupting the render.\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) all expect world-unit floats, and a single\n\t// linear pass over the int16 buffer is far cheaper than the legacy gunzip + base64 path.\n\t// No rotation happens here: the scene uses Rhino's Z-up frame, so vertices pass through in the\n\t// frame they arrived in.\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 can enable 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 * Attempt 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, matching the entry points' contract either way.\n *\n * Cache interplay: the worker always assembles and fingerprints every geometry (decode is\n * whole-array work anyway), and the main thread prefers an existing cached geometry over the\n * returned buffers — so cache hits skip the GPU re-upload, and the wasted worker CPU for them is\n * 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 wrap results.\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// The geometry views alias the caller's blob buffer — copy them so the transfer can't detach\n\t// it. UV/color arrays are parser-owned 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; // defensive: protocol mismatch → 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\tlet geometry = geometryCacheGet(result.key);\n\t\tif (!geometry) {\n\t\t\tgeometry = new THREE.BufferGeometry();\n\t\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(result.positions, 3));\n\t\t\tgeometry.setAttribute('normal', new THREE.BufferAttribute(result.normals, 3));\n\t\t\tgeometry.setIndex(new THREE.BufferAttribute(result.indices, 1));\n\t\t\tif (result.uvs) geometry.setAttribute('uv', new THREE.BufferAttribute(result.uvs, 2));\n\t\t\tif (result.colors) {\n\t\t\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(result.colors, 3, true));\n\t\t\t}\n\t\t\tgeometryCachePut(result.key, geometry);\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 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, DMF 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// PARSER\n// ============================================================================\n\n/**\n * Parses a binary mesh batch blob in the SLVA wire format.\n *\n * The blob layout is:\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), the stored int16 vertex components and indices are wrapped\n * differences from their predecessor, zigzag-mapped — see the flag's doc. The parser returns the\n * reconstructed absolute values, so 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 that hand the heavy decoding to a worker (`mesh-assembly.ts`); everyone\n * else 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 require alignment to the element size. The header lays out the geometry\n\t// block such that the vertex byte offset is always 4-aligned (preamble 12 + metadataLen + 4 +\n\t// 48 + 4). float32 needs 4-byte alignment (satisfied), int16 needs 2-byte alignment\n\t// (satisfied). We can take a zero-copy view as long as `bytes.byteOffset + offset` agrees with\n\t// that alignment in the underlying buffer — a wrapper Uint8Array could violate it. Fall back\n\t// to a fresh copy if so.\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// Left as raw zigzag deltas — parseBinaryMeshBatch (or the assembly worker) prefix-sums them.\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). Blobs from pre-chunk writers simply end\n\t// here; the flags gate every read, so nothing is consumed when the chunks are 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","import * as THREE from 'three';\n\nimport { CACHED_GEOMETRY_USERDATA_FLAG, registerCacheRelease } from '../../shared/index.js';\n\n/**\n * Cross-solve `BufferGeometry` cache, keyed by geometry *content*.\n *\n * The viewer rebuilds the whole scene every solve: `clearScene` disposes every geometry and the\n * parser re-decodes, re-copies, re-computes normals and re-uploads to the GPU — even for meshes the\n * solve didn't change. This closes that loop the same way the edge-segment cache does: fingerprint\n * the *raw wire windows* a geometry is built from, and on a hit hand back the same `BufferGeometry`\n * (normals computed, GPU buffers still resident) instead of rebuilding it.\n *\n * Safety model:\n * - Cached geometries are tagged with {@link CACHED_GEOMETRY_USERDATA_FLAG}; `clearScene` skips\n * disposing them, so the GPU buffers survive scene rebuilds. Everything else is disposed exactly\n * as before.\n * - Eviction (LRU by vertex+index bytes) *does* dispose. If an evicted geometry is still attached\n * to a live mesh, three transparently re-uploads its buffers on the next render — a one-frame\n * cost, never corruption.\n * - Sharing one geometry across several meshes (same content appearing twice in a solve, or across\n * solves) is safe: geometry is immutable after build, and materials live on the meshes.\n */\n\n/** CPU+GPU byte budget for cached geometry (positions+normals+indices+uv+color attributes). */\nconst GEOMETRY_CACHE_BYTE_BUDGET = 256 * 1024 * 1024;\n\n/** Words sampled from the head and tail of each buffer window when fingerprinting. */\nconst SAMPLE_WORDS = 1024;\n\ninterface CacheEntry {\n\tgeometry: THREE.BufferGeometry;\n\tbytes: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\nlet cacheBytes = 0;\n\n/**\n * FNV-1a over head+tail samples of each part plus every part's exact length, mixed with a caller\n * salt (quantization origin/scale, flags, window layout). Sampling keeps the hash ~free at millions\n * of vertices; a false hit would need identical lengths AND identical sampled regions across a real\n * geometry edit — the same accepted trade the edge-segment cache documents.\n */\nexport function fingerprintViews(parts: (ArrayBufferView | null)[], salt: string): string {\n\tlet hash = 0x811c9dc5;\n\tconst mix = (word: number): void => {\n\t\thash ^= word;\n\t\thash = Math.imul(hash, 0x01000193);\n\t};\n\n\tfor (let i = 0; i < salt.length; i++) mix(salt.charCodeAt(i));\n\n\tfor (const part of parts) {\n\t\tif (!part) {\n\t\t\tmix(0xdead);\n\t\t\tcontinue;\n\t\t}\n\t\t// Hash raw 32-bit words where alignment allows; fall back to a byte view otherwise.\n\t\tconst byteLength = part.byteLength;\n\t\tmix(byteLength);\n\t\tif ((part.byteOffset & 3) === 0 && (byteLength & 3) === 0) {\n\t\t\tconst words = new Uint32Array(part.buffer, part.byteOffset, byteLength >> 2);\n\t\t\tconst head = Math.min(SAMPLE_WORDS, words.length);\n\t\t\tfor (let i = 0; i < head; i++) mix(words[i]!);\n\t\t\tfor (let i = Math.max(head, words.length - SAMPLE_WORDS); i < words.length; i++) {\n\t\t\t\tmix(words[i]!);\n\t\t\t}\n\t\t} else {\n\t\t\tconst bytes = new Uint8Array(part.buffer, part.byteOffset, byteLength);\n\t\t\tconst head = Math.min(SAMPLE_WORDS * 4, bytes.length);\n\t\t\tfor (let i = 0; i < head; i++) mix(bytes[i]!);\n\t\t\tfor (let i = Math.max(head, bytes.length - SAMPLE_WORDS * 4); i < bytes.length; i++) {\n\t\t\t\tmix(bytes[i]!);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn `${(hash >>> 0).toString(36)}:${parts.length}`;\n}\n\nfunction bytesOf(geometry: THREE.BufferGeometry): number {\n\tlet total = geometry.index?.array.byteLength ?? 0;\n\tfor (const attribute of Object.values(geometry.attributes)) {\n\t\ttotal += (attribute as THREE.BufferAttribute).array.byteLength;\n\t}\n\treturn total;\n}\n\nexport function geometryCacheGet(key: string): THREE.BufferGeometry | undefined {\n\tconst entry = cache.get(key);\n\tif (!entry) return undefined;\n\tcache.delete(key);\n\tcache.set(key, entry);\n\treturn entry.geometry;\n}\n\n/** Insert a freshly built geometry, tagging it so `clearScene` won't dispose it. */\nexport function geometryCachePut(key: string, geometry: THREE.BufferGeometry): void {\n\tconst bytes = bytesOf(geometry);\n\tif (bytes > GEOMETRY_CACHE_BYTE_BUDGET) return; // absurd single geometry — don't cache\n\n\tif (cache.has(key)) {\n\t\t// Same content raced in twice — keep the incumbent. The newcomer stays untagged and\n\t\t// scene-owned, so the normal scene teardown disposes it; tagging it here would orphan it\n\t\t// (flagged, but never owned by the cache → skipped by every disposal path forever).\n\t\treturn;\n\t}\n\n\tgeometry.userData[CACHED_GEOMETRY_USERDATA_FLAG] = true;\n\tcache.set(key, { geometry, bytes });\n\tcacheBytes += bytes;\n\n\twhile (cacheBytes > GEOMETRY_CACHE_BYTE_BUDGET && cache.size > 1) {\n\t\tconst oldestKey = cache.keys().next().value as string;\n\t\tconst oldest = cache.get(oldestKey)!;\n\t\tcache.delete(oldestKey);\n\t\tcacheBytes -= oldest.bytes;\n\t\t// Disposing while still referenced by a scene mesh is safe: three re-uploads on next render.\n\t\t// Clear the tag so a later clearScene can dispose it for good.\n\t\tdelete oldest.geometry.userData[CACHED_GEOMETRY_USERDATA_FLAG];\n\t\toldest.geometry.dispose();\n\t}\n}\n\n// Declare this cache to the teardown registry, so the viewer's dispose() frees it without the\n// render layer importing this one and without any host wiring. See shared/gpu-ownership.ts.\nregisterCacheRelease(() => geometryCacheClear());\n\n/** Teardown hook registered above. */\nexport function geometryCacheClear(): void {\n\tfor (const entry of cache.values()) {\n\t\tdelete entry.geometry.userData[CACHED_GEOMETRY_USERDATA_FLAG];\n\t\tentry.geometry.dispose();\n\t}\n\tcache.clear();\n\tcacheBytes = 0;\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, computes vertex normals, and fingerprints each geometry for the cross-solve cache.\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) and\n * `geometry-cache.ts` (fingerprint); equivalence is pinned by tests asserting the worker path\n * shares cache entries with the synchronous path.\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\t/** Cache key — identical to the synchronous path's `geometryContentKey` output. */\n\tkey: string;\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// --- Fingerprint (must byte-match geometry-cache.ts fingerprintViews) ----------------------\n\tconst SAMPLE_WORDS = 1024;\n\tconst fingerprint = (parts: (ArrayBufferView | null)[], salt: string): string => {\n\t\tlet hash = 0x811c9dc5;\n\t\tconst mix = (word: number): void => {\n\t\t\thash ^= word;\n\t\t\thash = Math.imul(hash, 0x01000193);\n\t\t};\n\t\tfor (let i = 0; i < salt.length; i++) mix(salt.charCodeAt(i));\n\t\tfor (const part of parts) {\n\t\t\tif (!part) {\n\t\t\t\tmix(0xdead);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst byteLength = part.byteLength;\n\t\t\tmix(byteLength);\n\t\t\tif ((part.byteOffset & 3) === 0 && (byteLength & 3) === 0) {\n\t\t\t\tconst words = new Uint32Array(part.buffer, part.byteOffset, byteLength >> 2);\n\t\t\t\tconst head = Math.min(SAMPLE_WORDS, words.length);\n\t\t\t\tfor (let i = 0; i < head; i++) mix(words[i]);\n\t\t\t\tfor (let i = Math.max(head, words.length - SAMPLE_WORDS); i < words.length; i++) {\n\t\t\t\t\tmix(words[i]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst bytes = new Uint8Array(part.buffer, part.byteOffset, byteLength);\n\t\t\t\tconst head = Math.min(SAMPLE_WORDS * 4, bytes.length);\n\t\t\t\tfor (let i = 0; i < head; i++) mix(bytes[i]);\n\t\t\t\tfor (let i = Math.max(head, bytes.length - SAMPLE_WORDS * 4); i < bytes.length; i++) {\n\t\t\t\t\tmix(bytes[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn `${(hash >>> 0).toString(36)}:${parts.length}`;\n\t};\n\n\tconst keyFor = (kind: string, windows: AssemblyWindow[]): string => {\n\t\tconst parts: (ArrayBufferView | null)[] = [];\n\t\tlet salt = kind;\n\t\tfor (const window of windows) {\n\t\t\tsalt += `|${window.vertexStart},${window.vertexCount},${window.indexStart},${window.indexCount}`;\n\t\t\tconst componentStart = window.vertexStart * 3;\n\t\t\tconst componentEnd = componentStart + window.vertexCount * 3;\n\t\t\tparts.push(worldVertices.subarray(componentStart, componentEnd));\n\t\t\tparts.push(indices.subarray(window.indexStart, window.indexStart + window.indexCount));\n\t\t\tparts.push(\n\t\t\t\tuvs\n\t\t\t\t\t? uvs.subarray(window.vertexStart * 2, (window.vertexStart + window.vertexCount) * 2)\n\t\t\t\t\t: null\n\t\t\t);\n\t\t\tparts.push(colors ? colors.subarray(componentStart, componentEnd) : null);\n\t\t}\n\t\treturn fingerprint(parts, salt);\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\tkey: keyFor(job.kind, job.windows),\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 { parseColor } from '../../../shared/index.js';\n\nimport { applyTextureMap } from '../texture-cache.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 materials\n// meaningfully metallic get a thin satin clearcoat — a glossy dielectric layer independent of the\n// base 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\t// Minimal offset to avoid z-fighting on coplanar faces\n\t\tpolygonOffset: true,\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\t// See applyVertexColorSRGBDecode for why this is needed.\n\tif (vertexColors) {\n\t\tapplyVertexColorSRGBDecode(material);\n\t}\n\n\t// Async; see texture-cache.ts for load/cache behavior.\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 the linear working\n * space (unlike textures, which carry a `colorSpace` and get decoded) — so sRGB-authored vertex\n * colors render too bright without this shader patch. Done on the GPU, not 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 * as THREE from 'three';\n\nimport {\n\tCACHED_TEXTURE_USERDATA_FLAG,\n\tgetLogger,\n\tobserveMaxAnisotropy,\n\tregisterCacheRelease\n} from '../../shared/index.js';\n\n/**\n * Bound on the module-level texture cache; a fresh load past this evicts the least-recently-used\n * entry and disposes its GPU texture. Sized generously above any realistic per-scene texture count,\n * so eviction only ever trims textures no current scene references.\n */\nexport const TEXTURE_CACHE_MAX_ENTRIES = 64;\n\n/**\n * Cache keys longer than this are replaced by a compact hash — multi-KB/MB data URIs would\n * otherwise be retained as the Map key for as long as the texture lives.\n */\nconst MAX_KEY_LENGTH = 256;\n\n/**\n * Material texture references are immutable by construction (content-hashed asset URLs, self-\n * identifying http(s)/data URIs), so each URL is fetched and GPU-decoded exactly once per session\n * no matter how many solves or materials reference it — without this, every solve would re-decode\n * every texture on every slider nudge.\n *\n * The Map doubles as the LRU order: hits re-insert their entry, so iteration order is\n * least-recently-used first and eviction pops the first key.\n */\nconst textureCache = new Map<string, THREE.Texture>();\nconst inFlight = new Map<string, Promise<THREE.Texture>>();\n\n/**\n * Bumped by {@link clearTextureCache}. A load that started before a clear compares its captured\n * generation on resolve: if stale, the freshly decoded texture is disposed instead of repopulating\n * the just-emptied cache (which would leak it past viewer teardown).\n */\nlet cacheGeneration = 0;\n\n/** Expected during viewer teardown, so {@link applyTextureMap} swallows it silently instead of warning. */\nclass StaleTextureLoadError extends Error {\n\tconstructor(url: string) {\n\t\tsuper(`Texture load for ${url} resolved after clearTextureCache(); texture disposed.`);\n\t\tthis.name = 'StaleTextureLoadError';\n\t}\n}\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.\n */\nexport function setTextureAnisotropy(value: number): void {\n\tmaxAnisotropy = Math.max(1, value);\n\tfor (const texture of textureCache.values()) {\n\t\tif (texture.anisotropy !== maxAnisotropy) {\n\t\t\ttexture.anisotropy = maxAnisotropy;\n\t\t\ttexture.needsUpdate = true;\n\t\t}\n\t}\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`, synchronously when cached, otherwise asynchronously once\n * fetched and decoded — the mesh renders untextured for at most the first frames. Load failures\n * log a warning and leave the material untextured rather than breaking the batch.\n */\nexport function applyTextureMap(material: THREE.MeshPhysicalMaterial, url: string): void {\n\tconst key = cacheKeyFor(url);\n\tconst cached = textureCache.get(key);\n\tif (cached) {\n\t\t// Refresh LRU recency: re-insert so this entry moves to the back of the eviction order.\n\t\ttextureCache.delete(key);\n\t\ttextureCache.set(key, cached);\n\t\tmaterial.map = cached;\n\t\tmaterial.needsUpdate = true;\n\t\treturn;\n\t}\n\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\tloadTexture(url, key)\n\t\t.then((texture) => {\n\t\t\tmaterial.map = texture;\n\t\t\tmaterial.needsUpdate = true;\n\t\t})\n\t\t.catch((error) => {\n\t\t\tif (error instanceof StaleTextureLoadError) {\n\t\t\t\treturn; // Cache cleared mid-load (teardown/reset) — untextured is the intended state.\n\t\t\t}\n\t\t\tgetLogger().warn(`Failed to load material texture ${url}:`, error);\n\t\t});\n}\n\n// Declare this cache to the teardown registry, so the viewer's dispose() frees it without the\n// render layer importing this one and without any host wiring. See shared/gpu-ownership.ts.\nregisterCacheRelease(() => clearTextureCache());\n\n/**\n * Disposes all cached textures and empties the cache (e.g. on viewer teardown). Loads still in\n * flight are orphaned: when they resolve they see the bumped generation, dispose their texture,\n * and do not repopulate the cache.\n */\nexport function clearTextureCache(): void {\n\tcacheGeneration++;\n\tfor (const texture of textureCache.values()) {\n\t\tdelete texture.userData[CACHED_TEXTURE_USERDATA_FLAG];\n\t\ttexture.dispose();\n\t}\n\ttextureCache.clear();\n\tinFlight.clear();\n}\n\n/** Short URLs key by themselves; oversized ones hash (see MAX_KEY_LENGTH). The `data-uri:` prefix\n * keeps hashed keys disjoint from literal URL keys. */\nfunction cacheKeyFor(url: string): string {\n\tif (url.length <= MAX_KEY_LENGTH) {\n\t\treturn url;\n\t}\n\treturn `data-uri:${fnv1aString(url).toString(16)}:${url.length}`;\n}\n\n/** 32-bit FNV-1a over the string's UTF-16 code units. */\nfunction fnv1aString(s: string): number {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < s.length; i++) {\n\t\thash ^= s.charCodeAt(i);\n\t\thash = Math.imul(hash, 0x01000193);\n\t}\n\treturn hash >>> 0;\n}\n\nfunction storeTexture(key: string, texture: THREE.Texture): void {\n\t// Claim ownership before the texture can reach a material. A cached texture is assigned straight\n\t// onto `material.map` and shared by every material using that URL, so without this flag the first\n\t// material sweep to reach one would dispose a texture the cache still holds — and then serve the\n\t// disposed texture to the next mesh that asks for it. Cleared on eviction, below.\n\ttexture.userData[CACHED_TEXTURE_USERDATA_FLAG] = true;\n\n\ttextureCache.set(key, texture);\n\twhile (textureCache.size > TEXTURE_CACHE_MAX_ENTRIES) {\n\t\tconst oldestKey = textureCache.keys().next().value as string;\n\t\tconst evicted = textureCache.get(oldestKey)!;\n\t\ttextureCache.delete(oldestKey);\n\t\t// Release the claim first: an evicted texture may still be attached to a live material, and\n\t\t// the scene that owns that material should be free to dispose it from here on.\n\t\tdelete evicted.userData[CACHED_TEXTURE_USERDATA_FLAG];\n\t\tevicted.dispose();\n\t}\n}\n\nfunction loadTexture(url: string, key: string): Promise<THREE.Texture> {\n\tlet pending = inFlight.get(key);\n\tif (!pending) {\n\t\tconst generation = cacheGeneration;\n\t\tpending = new Promise<THREE.Texture>((resolve, reject) => {\n\t\t\tnew THREE.TextureLoader().load(\n\t\t\t\turl,\n\t\t\t\t(texture) => {\n\t\t\t\t\tif (generation !== cacheGeneration) {\n\t\t\t\t\t\t// clearTextureCache() ran while this load was in flight: the cache (and the\n\t\t\t\t\t\t// viewer that wanted this texture) is gone. Repopulating would leak a live GPU\n\t\t\t\t\t\t// texture past teardown, so dispose it and reject as stale.\n\t\t\t\t\t\ttexture.dispose();\n\t\t\t\t\t\treject(new StaleTextureLoadError(url));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// Color maps are sRGB; without this the render is washed out.\n\t\t\t\t\ttexture.colorSpace = THREE.SRGBColorSpace;\n\t\t\t\t\t// Keep textures crisp at grazing angles (see maxAnisotropy).\n\t\t\t\t\ttexture.anisotropy = maxAnisotropy;\n\t\t\t\t\tstoreTexture(key, texture);\n\t\t\t\t\tinFlight.delete(key);\n\t\t\t\t\tresolve(texture);\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t(error) => {\n\t\t\t\t\t// Guard: a failure resolving after a clear must not delete a *newer* in-flight entry.\n\t\t\t\t\tif (generation === cacheGeneration) {\n\t\t\t\t\t\tinFlight.delete(key);\n\t\t\t\t\t}\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t);\n\t\t});\n\t\tinFlight.set(key, pending);\n\t}\n\n\treturn pending;\n}\n","import * as THREE from 'three';\n\nimport { geometryCacheGet, geometryCachePut } from '../geometry-cache.js';\nimport { geometryContentKey, 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\t// Cross-solve reuse: identical content → same BufferGeometry object, skipping the merge copies,\n\t// computeVertexNormals, and the GPU re-upload entirely.\n\tconst cacheKey = geometryContentKey(\n\t\t'merged',\n\t\tgroup.meshes,\n\t\tallVertices,\n\t\tallIndices,\n\t\tallUvs,\n\t\tallColors\n\t);\n\tlet geometry = geometryCacheGet(cacheKey);\n\n\tif (!geometry) {\n\t\tlet totalVertexCount = 0;\n\t\tlet totalIndexCount = 0;\n\t\tfor (const meshMeta of group.meshes) {\n\t\t\ttotalVertexCount += meshMeta.vertexCount;\n\t\t\ttotalIndexCount += meshMeta.indexCount;\n\t\t}\n\n\t\tconst mergedVertices = new Float32Array(totalVertexCount * 3);\n\t\tconst mergedIndices = new Uint32Array(totalIndexCount);\n\t\tconst mergedUvs = allUvs ? new Float32Array(totalVertexCount * 2) : null;\n\t\tconst mergedColors = allColors ? new Uint8Array(totalVertexCount * 3) : null;\n\n\t\tlet vertexWriteCursor = 0;\n\t\tlet indexWriteCursor = 0;\n\n\t\tfor (const meshMeta of group.meshes) {\n\t\t\tconst componentStart = meshMeta.vertexStart * 3;\n\t\t\tconst componentLen = meshMeta.vertexCount * 3;\n\t\t\tmergedVertices.set(\n\t\t\t\tallVertices.subarray(componentStart, componentStart + componentLen),\n\t\t\t\tvertexWriteCursor * 3\n\t\t\t);\n\n\t\t\tif (mergedUvs && allUvs) {\n\t\t\t\tconst uvStart = meshMeta.vertexStart * 2;\n\t\t\t\tmergedUvs.set(\n\t\t\t\t\tallUvs.subarray(uvStart, uvStart + meshMeta.vertexCount * 2),\n\t\t\t\t\tvertexWriteCursor * 2\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (mergedColors && allColors) {\n\t\t\t\tmergedColors.set(\n\t\t\t\t\tallColors.subarray(componentStart, componentStart + componentLen),\n\t\t\t\t\tvertexWriteCursor * 3\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst indicesSlice = allIndices.subarray(\n\t\t\t\tmeshMeta.indexStart,\n\t\t\t\tmeshMeta.indexStart + meshMeta.indexCount\n\t\t\t);\n\t\t\tconst indexShift = vertexWriteCursor - meshMeta.vertexStart;\n\t\t\tconst windowStart = meshMeta.vertexStart;\n\t\t\tconst windowEnd = meshMeta.vertexStart + meshMeta.vertexCount;\n\t\t\tfor (let i = 0; i < indicesSlice.length; i++) {\n\t\t\t\tconst indexValue = indicesSlice[i]!;\n\t\t\t\tif (indexValue < windowStart || indexValue >= windowEnd) {\n\t\t\t\t\tthrow indexOutOfWindow(indexValue, meshMeta);\n\t\t\t\t}\n\t\t\t\tmergedIndices[indexWriteCursor + i] = indexValue + indexShift;\n\t\t\t}\n\n\t\t\tvertexWriteCursor += meshMeta.vertexCount;\n\t\t\tindexWriteCursor += meshMeta.indexCount;\n\t\t}\n\n\t\tgeometry = new THREE.BufferGeometry();\n\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(mergedVertices, 3));\n\t\tgeometry.setIndex(new THREE.BufferAttribute(mergedIndices, 1));\n\t\tif (mergedUvs) {\n\t\t\tgeometry.setAttribute('uv', new THREE.BufferAttribute(mergedUvs, 2));\n\t\t}\n\t\tif (mergedColors) {\n\t\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(mergedColors, 3, true));\n\t\t}\n\t\tgeometry.computeVertexNormals();\n\t\tgeometryCachePut(cacheKey, geometry);\n\t}\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// Cross-solve reuse — see createMergedMesh.\n\t\tconst cacheKey = geometryContentKey(\n\t\t\t'single',\n\t\t\t[meshMeta],\n\t\t\tallVertices,\n\t\t\tallIndices,\n\t\t\tallUvs,\n\t\t\tallColors\n\t\t);\n\t\tlet geometry = geometryCacheGet(cacheKey);\n\n\t\tif (!geometry) {\n\t\t\t// `subarray` returns a view; copy via `slice` so the BufferAttribute owns its memory and\n\t\t\t// downstream code (dispose/reuse) can't surprise us by sharing the parser's buffer.\n\t\t\tconst vertices = allVertices.slice(componentStart, componentStart + componentLen);\n\n\t\t\tconst indicesSlice = allIndices.subarray(\n\t\t\t\tmeshMeta.indexStart,\n\t\t\t\tmeshMeta.indexStart + meshMeta.indexCount\n\t\t\t);\n\t\t\tconst rebasedIndices = new Uint32Array(indicesSlice.length);\n\t\t\tconst baseIndex = meshMeta.vertexStart;\n\t\t\tconst windowEnd = meshMeta.vertexStart + meshMeta.vertexCount;\n\t\t\tfor (let i = 0; i < indicesSlice.length; i++) {\n\t\t\t\tconst indexValue = indicesSlice[i]!;\n\t\t\t\tif (indexValue < baseIndex || indexValue >= windowEnd) {\n\t\t\t\t\tthrow indexOutOfWindow(indexValue, meshMeta);\n\t\t\t\t}\n\t\t\t\trebasedIndices[i] = indexValue - baseIndex;\n\t\t\t}\n\n\t\t\tgeometry = new THREE.BufferGeometry();\n\t\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));\n\t\t\tgeometry.setIndex(new THREE.BufferAttribute(rebasedIndices, 1));\n\t\t\tif (allUvs) {\n\t\t\t\tconst uvStart = meshMeta.vertexStart * 2;\n\t\t\t\tconst uvs = allUvs.slice(uvStart, uvStart + meshMeta.vertexCount * 2);\n\t\t\t\tgeometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n\t\t\t}\n\t\t\tif (allColors) {\n\t\t\t\tconst colors = allColors.slice(componentStart, componentStart + componentLen);\n\t\t\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3, true));\n\t\t\t}\n\t\t\tgeometry.computeVertexNormals();\n\t\t\tgeometryCachePut(cacheKey, geometry);\n\t\t}\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 { VisualizationError, ErrorCodes } from '../../../shared/index.js';\n\nimport { fingerprintViews } from '../geometry-cache.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 a VALIDATION_ERROR on the first inconsistency (out-of-range\n * `materialId`, non-integer or negative offsets/counts, or vertex/index windows that overrun the\n * buffers) so malformed or version-skewed metadata fails the parse loudly.\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 a violation would otherwise wrap to ~4 billion and corrupt the geometry.\n * The range checks themselves are inlined in the copy loops (a function call per index was\n * measurable 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 * Content key for the cross-solve geometry cache: samples of every buffer window this geometry is\n * built from, plus the window layout as salt (identical bytes at a different offset rebase to\n * different geometry). See ../geometry-cache.ts for the safety model.\n */\nexport function geometryContentKey(\n\tkind: 'merged' | 'single',\n\tmeshes: MeshMetadata[],\n\tallVertices: Float32Array,\n\tallIndices: Uint16Array | Uint32Array,\n\tallUvs: Float32Array | null,\n\tallColors: Uint8Array | null\n): string {\n\tconst parts: (ArrayBufferView | null)[] = [];\n\tlet salt = kind;\n\tfor (const meshMeta of meshes) {\n\t\tsalt += `|${meshMeta.vertexStart},${meshMeta.vertexCount},${meshMeta.indexStart},${meshMeta.indexCount}`;\n\t\tconst componentStart = meshMeta.vertexStart * 3;\n\t\tconst componentEnd = componentStart + meshMeta.vertexCount * 3;\n\t\tparts.push(allVertices.subarray(componentStart, componentEnd));\n\t\tparts.push(allIndices.subarray(meshMeta.indexStart, meshMeta.indexStart + meshMeta.indexCount));\n\t\tparts.push(\n\t\t\tallUvs\n\t\t\t\t? allUvs.subarray(\n\t\t\t\t\t\tmeshMeta.vertexStart * 2,\n\t\t\t\t\t\t(meshMeta.vertexStart + meshMeta.vertexCount) * 2\n\t\t\t\t\t)\n\t\t\t\t: null\n\t\t);\n\t\tparts.push(allColors ? allColors.subarray(componentStart, componentEnd) : null);\n\t}\n\treturn fingerprintViews(parts, salt);\n}\n\n/**\n * Reconstructs world-unit float32 positions from int16 quantized values.\n *\n * Mirrors the encoder formula: `world = origin + (q + 32767) * scale`. Selva keeps one coordinate\n * frame end to end — the Three scene is Rhino's Z-up frame — so vertices pass through unrotated.\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 { 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';\nimport type { RhinoModule } from 'rhino3dm';\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 (1 in = 0.0254 m,\n * 1 ft = 0.3048 m, 1 mi = 1609.344 m). Units not in this table scale 1 and log a one-time warning\n * — 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\n/**\n * Wire `type` token identifying a Selva Display payload. The real wire value is namespaced (e.g.\n * `Selva.GH.Features.Display.Services.DisplayBatch`), so dispatch matches exact dot-separated\n * tokens — never substrings, which would misroute any unrelated type merely containing \"Display\".\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`.\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 (see\n * root CLAUDE.md).\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: grounding used to be on by default here but was never applied on the\n\t\t// WebSocket preview path, so the same definition sat at a different height depending on\n\t\t// transport. Rhino coordinates are the honest frame, so picked/measured values match the GH definition.\n\t\tallowAutoPosition = false,\n\t\tgroundAxis = 'z',\n\t\trhino,\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, rhino, 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 the given Rhino unit name. Unknown units fall back to\n * 1 (no scaling) with a one-time warning per unit name — a kilometers model rendering 1000× off\n * 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\trhino: RhinoModule | undefined,\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, rhino, debug);\n\t\t}\n\t}\n}\n\n/**\n * Processes a single data branch to extract a DisplayBatch's meshes (binary blob) and items\n * (curves/points JSON). Both get the same unit scale so they share one frame.\n */\nasync function processDataBranch(\n\tbranch: DisplayDataItem[],\n\tobjects: THREE.Object3D[],\n\tscaleFactor: number,\n\tparsingOptions: MeshBatchParsingOptions,\n\trhino: RhinoModule | undefined,\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// Parse the JSON envelope once — it contains the full multi-MB base64 SLVA blob as a string,\n\t\t// so letting the mesh parser and the display-item extractor each parse it doubles both the\n\t\t// synchronous main-thread CPU and the transient 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, { rhino });\n\n\t\tconst batchObjects: THREE.Object3D[] = [...batchMeshes, ...batchItems];\n\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/**\n * Resolves a raw DisplayBatch payload to a parsed object, tolerating either a parsed object or a\n * JSON string (the blob-bearing `item.data` is the same envelope the mesh parser reads).\n */\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` is taken as a parameter\n * (not hardcoded `z`) so grounding stays correct for a host configuring a non-default `sceneUp` —\n * subtracting `min.z` there would shove content sideways 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","import { releaseAllCaches } from '../shared/index.js';\n\n/**\n * Free every cross-solve GPU cache — cached geometries, textures and edge segments.\n *\n * **You do not normally need this.** These caches register themselves for teardown, so the viewer's\n * `dispose()` already frees them once the last live viewer goes away. It is exported for the cases\n * that sit outside a viewer lifecycle: reclaiming memory under pressure, or a test isolating\n * module-level state.\n *\n * Safe to call repeatedly and with a viewer running — the caches simply repopulate on the next solve.\n */\nexport function releaseParseCaches(): void {\n\treleaseAllCaches();\n}\n"],"mappings":"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","names":["cloneSceneObjects","meshes","root","copy","sources","child","i","source","target","geometry","CACHED_GEOMETRY_USERDATA_FLAG","releaseSceneObjects","disposeObjectTree","meshPolicy","THREE","Line2","LineGeometry","LineMaterial","THREE","DEFAULT_COLOR","materialParams","color","opacity","resolved","CURVE_INITIAL_SEGMENTS","CURVE_CHORD_TOLERANCE_RATIO","CURVE_MAX_SUBDIVISION_DEPTH","CURVE_MAX_TURN_RADIANS","DEFAULT_LINE_WIDTH","buildCurveLine","item","rhino","getLogger","curve","decodeCurve","points","tessellate","error","deleteRhinoObject","positions","p","geometry","LineGeometry","params","materialParams","material","LineMaterial","styled","line","Line2","obj","json","parsed","exact","tryPolylineVertices","sampleUniform","result","polyline","out","i","domain","t0","span","evalAt","t","tolerance","chordTolerance","ta","pa","tb","pb","subdivide","depth","tm","pm","deviation","distanceToSegment","turn","turnAngle","box","min","max","diagonal","a","b","c","abx","aby","abz","bcx","bcy","bcz","lenAb","lenBc","dot","cos","lengthSq","apx","apy","apz","dx","dy","dz","THREE","buildPoint","item","position","getLogger","geometry","material","materialParams","points","parseDisplayItems","items","options","rhino","objects","item","line","buildCurveLine","point","buildPoint","unknown","getLogger","THREE","HOST_IS_LITTLE_ENDIAN","inflateSync","toUint8Array","input","decodeBase64ToBinary","maybeDecompress","bytes","view","uncompressedLen","deflated","maxPlausibleLen","fail","out","inflateSync","error","decodeUtf8","VisualizationError","ErrorCodes","readInt16Vertices","buffer","byteOffset","count","copy","readFloat32Vertices","readUint16Array","readUint32Array","validateIndicesInRange","indices","vertexCount","i","unzigzag","zz","decodeDeltaVertices","zigzagged","px","py","pz","decodeDeltaIndices16","prev","decodeDeltaIndices32","message","context","UV_CHUNK_HEADER_BYTES","parseUvChunk","bytes","view","offset","vertexCount","deltaEncoded","fail","uvFormat","originU","originV","scaleU","scaleV","componentCount","useFloat32","dataByteLength","absoluteOffset","uvs","readFloat32Vertices","raw","readUint16Array","qu","qv","i","unzigzag","parseColorChunk","byteLength","colors","r","g","b","parseBinaryMeshBatch","input","raw","parseBinaryMeshBatchRaw","vertices","decodeDeltaVertices","indices","decodeDeltaIndices16","decodeDeltaIndices32","validateIndicesInRange","HOST_IS_LITTLE_ENDIAN","VisualizationError","ErrorCodes","bytes","maybeDecompress","toUint8Array","view","fail","offset","magic","version","metadataLen","metadataBytes","metadata","decodeUtf8","error","flags","originX","originY","originZ","scaleX","scaleY","scaleZ","vertexCount","useFloat32","deltaEncoded","componentCount","verticesByteLength","absoluteOffset","vertexData","readFloat32Vertices","readUint16Array","readInt16Vertices","indexCount","useUint16Indices","indicesByteLength","indexData","readUint32Array","uvs","parsed","parseUvChunk","colors","parseColorChunk","GEOMETRY_CACHE_BYTE_BUDGET","SAMPLE_WORDS","cache","cacheBytes","fingerprintViews","parts","salt","hash","mix","word","i","part","byteLength","words","head","bytes","bytesOf","geometry","total","attribute","geometryCacheGet","key","entry","geometryCachePut","CACHED_GEOMETRY_USERDATA_FLAG","oldestKey","oldest","registerCacheRelease","geometryCacheClear","assembleGeometries","input","isFloat32","deltaEncoded","origin","scale","uvs","colors","jobs","unzigzag","zz","worldVertices","quantized","zigzagged","px","py","pz","i","ox","oy","oz","sx","sy","sz","indices","out","prev","totalVertexCount","SAMPLE_WORDS","fingerprint","parts","salt","hash","mix","word","part","byteLength","words","head","bytes","keyFor","kind","windows","window","componentStart","componentEnd","results","job","vertexTotal","indexTotal","positions","outIndices","outUvs","outColors","vertexCursor","indexCursor","windowStart","windowEnd","shift","indexValue","normals","a","b","c","cbx","cby","cbz","abx","aby","abz","nx","ny","nz","x","y","z","length","meshAssemblyWorkerSource","ASSEMBLY_WORKER_MIN_TRIANGLES","assemblyWorker","pendingAssemblies","nextAssemblyRequestId","getAssemblyWorker","url","meshAssemblyWorkerSource","worker","event","id","geometries","error","pending","requestAssembly","input","transfer","resolve","reject","THREE","THREE","TEXTURE_CACHE_MAX_ENTRIES","MAX_KEY_LENGTH","textureCache","inFlight","cacheGeneration","StaleTextureLoadError","url","maxAnisotropy","setTextureAnisotropy","value","texture","observeMaxAnisotropy","applyTextureMap","material","key","cacheKeyFor","cached","loadTexture","error","getLogger","registerCacheRelease","clearTextureCache","CACHED_TEXTURE_USERDATA_FLAG","fnv1aString","s","hash","i","storeTexture","oldestKey","evicted","pending","generation","resolve","reject","METAL_CLEARCOAT_THRESHOLD","METAL_CLEARCOAT","METAL_CLEARCOAT_ROUGHNESS","createMaterial","matData","options","color","parseColor","vertexColors","appearance","material","applyVertexColorSRGBDecode","applyTextureMap","shader","THREE","metadataFail","message","context","VisualizationError","ErrorCodes","validateGroupMetadata","groups","materialCount","totalVertexCount","totalIndexCount","group","mesh","fields","field","value","indexOutOfWindow","indexValue","meshMeta","geometryContentKey","kind","meshes","allVertices","allIndices","allUvs","allColors","parts","salt","componentStart","componentEnd","fingerprintViews","dequantizeInt16","q","origin","scale","out","ox","oy","oz","sx","sy","sz","i","createMergedMesh","group","allVertices","allIndices","materials","allUvs","allColors","cacheKey","geometryContentKey","geometry","geometryCacheGet","totalVertexCount","totalIndexCount","meshMeta","mergedVertices","mergedIndices","mergedUvs","mergedColors","vertexWriteCursor","indexWriteCursor","componentStart","componentLen","uvStart","indicesSlice","indexShift","windowStart","windowEnd","i","indexValue","indexOutOfWindow","geometryCachePut","finalizeMergedMesh","threeMesh","firstMesh","meshNames","m","name","createIndividualMeshes","meshes","vertices","rebasedIndices","baseIndex","uvs","colors","finalizeSingleMesh","mesh","parseMeshBatchObject","batch","options","telemetry","mergeByMaterial","debug","material","parseTime","perfStart","workerMeshes","tryBuildViaWorker","decodeStart","parsed","parseBinaryMeshBatch","decodeTime","blobBytes","approximateBase64DecodedBytes","buildMeshesFromParsed","parseMeshBatchBlob","blob","opts","materialAppearance","fallback","materialsSrc","groups","sourceComponentId","isFloat32","validateGroupMetadata","worldVertices","dequantizeInt16","wireBytes","getLogger","meshCreateStart","materials","createMaterial","meshes","group","mergedMesh","createMergedMesh","individualMeshes","createIndividualMeshes","mesh","meshCreateTime","totalTime","input","raw","parseBinaryMeshBatchRaw","ASSEMBLY_WORKER_MIN_TRIANGLES","worker","getAssemblyWorker","windowOf","m","jobs","jobRefs","meshMeta","vertexData","indexData","transfer","assembled","requestAssembly","error","i","result","ref","geometry","geometryCacheGet","geometryCachePut","finalizeMergedMesh","finalizeSingleMesh","base64","SCALE_FACTORS","DISPLAY_COMPONENT_TYPE","DISPLAY_BATCH_TYPE","isDisplayItemType","type","tokens","warnedUnknownUnits","getThreeMeshesFromComputeResponse","data","options","startTime","objects","allowScaling","allowAutoPosition","groundAxis","rhino","debug","parsingOptions","scaleFactor","getScaleFactor","extractDisplayFromData","applyGroundOffset","error","handleError","logProcessingTime","modelUnits","factor","getLogger","value","innerTree","path","branch","processDataBranch","item","mergedParsingOptions","batch","extractBatch","batchMeshes","parseMeshBatchObject","batchItems","parseDisplayItems","batchObjects","obj","safeParse","s","meshes","axis","combinedBoundingBox","computeCombinedBoundingBox","applyOffset","disposeMeshes","mesh","material","elapsed","releaseParseCaches","releaseAllCaches"]}
|
|
1
|
+
{"version":3,"sources":["../src/parse/mesh-policy.ts","../src/parse/display-items/items/curves.ts","../src/parse/display-items/items/appearance.ts","../src/parse/display-items/items/points.ts","../src/parse/display-items/display-items-parser.ts","../src/parse/webdisplay/batch-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/batch/materials.ts","../src/parse/webdisplay/apply-texture.ts","../src/parse/webdisplay/batch/merge.ts","../src/parse/webdisplay/batch/metadata.ts","../src/parse/webdisplay/webdisplay-parser.ts"],"sourcesContent":["// 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 { 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\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 * 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 * 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 .dmf 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 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, DMF 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 { 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 * 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 { 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 { 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 { 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":"kIAWO,SAASA,GAAkBC,EAA4C,CAC7E,OAAOA,EAAO,IAAKC,GAAS,CAC3B,IAAMC,EAAOD,EAAK,MAAM,EAAI,EACtBE,EAA4B,CAAC,EACnCF,EAAK,SAAUG,GAAUD,EAAQ,KAAKC,CAAK,CAAC,EAC5C,IAAIC,EAAI,EACR,OAAAH,EAAK,SAAUE,GAAU,CACxB,IAAME,EAASH,EAAQE,GAAG,EACpBE,EAASH,EACVE,EAAO,WACZC,EAAO,SAAWD,EAAO,SAAS,MAAM,EACzC,CAAC,EACMJ,CACR,CAAC,CACF,CAGO,SAASM,GAAoBR,EAAgC,CACnEA,EAAO,QAASC,GAASQ,GAAkBR,EAAM,CAAE,UAAW,EAAM,CAAC,CAAC,CACvE,CAGO,IAAMS,GAGT,CACH,MAAOX,GACP,QAASS,EACV,ECvCA,OAAS,SAAAG,OAAa,8BACtB,OAAS,gBAAAC,OAAoB,qCAC7B,OAAS,gBAAAC,OAAoB,qCCF7B,UAAYC,OAAW,QAEhB,IAAMC,GAAgB,UAGtB,SAASC,EACfC,EACAC,EACgE,CAChE,IAAMC,EAAWD,GAAW,EAC5B,MAAO,CACN,MAAO,IAAU,SAAMD,GAASF,EAAa,EAC7C,YAAaI,EAAW,EACxB,QAASA,CACV,CACD,CDNA,IAAMC,GAAqB,EAGrBC,GAAgB,EAYf,SAASC,GAAeC,EAAkC,CAChE,IAAMC,EAAYC,GAAeF,CAAI,EACrC,GAAI,CAACC,EAAW,OAAO,KAEvB,IAAME,EAAW,IAAIC,GACrBD,EAAS,aAAaF,CAAS,EAG/B,IAAMI,EAASC,EAAeN,EAAK,MAAOA,EAAK,OAAO,EAChDO,EAAW,IAAIC,GAAa,CAAE,MAAOH,EAAO,KAAM,CAAC,EACnDI,EAASF,EAKfE,EAAO,UAAYT,EAAK,OAASH,GACjCY,EAAO,YAAcJ,EAAO,YAC5BI,EAAO,QAAUJ,EAAO,QAExB,IAAMK,EAAO,IAAIC,GAAMR,EAAUI,CAAQ,EACzC,OAAAG,EAAK,qBAAqB,EAC1BA,EAAK,KAAOV,EAAK,KACjBU,EAAK,SAAW,CACf,OAAQ,UACR,GAAIV,EAAK,GACT,MAAOA,EAAK,MACZ,KAAM,QACN,SAAUA,EAAK,QAChB,EACOU,CACR,CAUA,SAASR,GAAeF,EAAqC,CAC5D,GAAI,CAACA,EAAK,OACT,MAAM,IAAIY,EACT,uBAAuBZ,EAAK,EAAE,2LAG9Ba,EAAW,eACX,CAAE,QAAS,CAAE,OAAQb,EAAK,GAAI,KAAMA,EAAK,IAAK,CAAE,CACjD,EAGD,OAAOA,EAAK,OAAO,QAAUF,GAAgBE,EAAK,OAAS,IAC5D,CE5EA,UAAYc,MAAW,QAOhB,SAASC,GAAWC,EAAyC,CAEnE,GAAM,CAAE,SAAAC,CAAS,EAAID,EACrB,GACC,CAACC,GACD,OAAOA,EAAS,GAAM,UACtB,CAAC,OAAO,SAASA,EAAS,CAAC,GAC3B,OAAOA,EAAS,GAAM,UACtB,CAAC,OAAO,SAASA,EAAS,CAAC,GAC3B,OAAOA,EAAS,GAAM,UACtB,CAAC,OAAO,SAASA,EAAS,CAAC,EAE3B,OAAAC,EAAU,EAAE,KACX,wEAAwE,OAAOF,EAAK,EAAE,CAAC,IACxF,EACO,KAGR,IAAMG,EAAW,IAAU,iBAC3BA,EAAS,aACR,WACA,IAAU,yBAAuB,CAACF,EAAS,EAAGA,EAAS,EAAGA,EAAS,CAAC,EAAG,CAAC,CACzE,EAEA,IAAMG,EAAW,IAAU,iBAAe,CACzC,GAAGC,EAAeL,EAAK,MAAOA,EAAK,OAAO,EAC1C,KAAM,EACN,gBAAiB,EAClB,CAAC,EAEKM,EAAS,IAAU,SAAOH,EAAUC,CAAQ,EAClD,OAAAE,EAAO,KAAON,EAAK,KACnBM,EAAO,SAAW,CACjB,OAAQ,UACR,GAAIN,EAAK,GACT,MAAOA,EAAK,MACZ,KAAM,QACN,SAAUA,EAAK,QAChB,EACOM,CACR,CC/BO,SAASC,EAAkBC,EAAoD,CACrF,GAAI,CAACA,GAASA,EAAM,SAAW,EAAG,MAAO,CAAC,EAE1C,IAAMC,EAA4B,CAAC,EAEnC,QAAWC,KAAQF,EAClB,OAAQE,EAAK,KAAM,CAClB,IAAK,QAAS,CACb,IAAMC,EAAOC,GAAeF,CAAI,EAC5BC,GAAMF,EAAQ,KAAKE,CAAI,EAC3B,KACD,CACA,IAAK,QAAS,CACb,IAAME,EAAQC,GAAWJ,CAAI,EACzBG,GAAOJ,EAAQ,KAAKI,CAAK,EAC7B,KACD,CACA,QAAS,CAGR,IAAME,EADmBL,EAEzBM,EAAU,EAAE,KAAK,uCAAuC,OAAOD,EAAQ,IAAI,CAAC,EAAE,EAC9E,KACD,CACD,CAGD,OAAON,CACR,CC5CA,UAAYQ,MAAW,QCkEhB,IAAMC,GAAwB,IAAI,YAAY,IAAI,WAAW,CAAC,EAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,IAAM,EClE3F,OAAS,eAAAC,OAAmB,SAMrB,SAASC,GAAaC,EAAsD,CAClF,OAAI,OAAOA,GAAU,SACbC,GAAqBD,CAAK,EAE9BA,aAAiB,WACbA,EAED,IAAI,WAAWA,CAAK,CAC5B,CAOO,SAASE,GAAgBC,EAA+B,CAC9D,GAAIA,EAAM,WAAa,EACtB,OAAOA,EAGR,IAAMC,EAAO,IAAI,SAASD,EAAM,OAAQA,EAAM,WAAYA,EAAM,UAAU,EAC1E,GAAIC,EAAK,UAAU,EAAG,EAAI,IAAM,WAC/B,OAAOD,EAGR,IAAME,EAAkBD,EAAK,UAAU,EAAG,EAAI,EACxCE,EAAWH,EAAM,SAAS,CAAC,EAI3BI,EAAkB,KAAK,IAAID,EAAS,WAAa,KAAO,KAAM,GAAK,EAAE,EAC3E,GAAID,EAAkBE,EACrB,MAAMC,EAAK,0DAA2D,CACrE,gBAAAH,EACA,cAAeC,EAAS,WACxB,gBAAAC,CACD,CAAC,EAGF,IAAIE,EACJ,GAAI,CAIHA,EAAMC,GAAYJ,EAAU,CAAE,IAAK,IAAI,WAAWD,EAAkB,CAAC,CAAE,CAAC,CACzE,OAASM,EAAO,CACf,MAAMH,EACL,gCAAgCG,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,CAAC,GACtF,CAAE,gBAAAN,EAAiB,cAAeC,EAAS,UAAW,CACvD,CACD,CAEA,GAAIG,EAAI,aAAeJ,EACtB,MAAMG,EAAK,sEAAuE,CACjF,YAAaH,EACb,UAAWI,EAAI,WACf,cAAeH,EAAS,UACzB,CAAC,EAGF,OAAOG,CACR,CAEO,SAASG,GAAWT,EAA2B,CACrD,GAAI,OAAO,YAAgB,IAC1B,OAAO,IAAI,YAAY,OAAO,EAAE,OAAOA,CAAK,EAG7C,GACC,OAAQ,WACN,OAAW,IAEb,OACC,WACC,OAAO,KAAKA,CAAK,EAAE,SAAS,OAAO,EAEtC,MAAM,IAAIU,EACT,kDACAC,EAAW,aACZ,CACD,CAEO,SAASC,GACfC,EACAC,EACAC,EACa,CACb,GAAIA,IAAU,EAAG,OAAO,IAAI,WAAW,CAAC,EACxC,GAAID,EAAa,IAAM,EACtB,OAAO,IAAI,WAAWD,EAAQC,EAAYC,CAAK,EAGhD,IAAMC,EAAO,IAAI,WAAWD,EAAQ,CAAC,EACrC,OAAAC,EAAK,IAAI,IAAI,WAAWH,EAAQC,EAAYC,EAAQ,CAAC,CAAC,EAC/C,IAAI,WAAWC,EAAK,MAAM,CAClC,CAEO,SAASC,EACfJ,EACAC,EACAC,EACe,CACf,GAAIA,IAAU,EAAG,OAAO,IAAI,aAAa,CAAC,EAC1C,GAAID,EAAa,IAAM,EACtB,OAAO,IAAI,aAAaD,EAAQC,EAAYC,CAAK,EAElD,IAAMC,EAAO,IAAI,WAAWD,EAAQ,CAAC,EACrC,OAAAC,EAAK,IAAI,IAAI,WAAWH,EAAQC,EAAYC,EAAQ,CAAC,CAAC,EAC/C,IAAI,aAAaC,EAAK,MAAM,CACpC,CAEO,SAASE,EACfL,EACAC,EACAC,EACc,CACd,GAAIA,IAAU,EAAG,OAAO,IAAI,YAAY,CAAC,EACzC,GAAID,EAAa,IAAM,EACtB,OAAO,IAAI,YAAYD,EAAQC,EAAYC,CAAK,EAEjD,IAAMC,EAAO,IAAI,WAAWD,EAAQ,CAAC,EACrC,OAAAC,EAAK,IAAI,IAAI,WAAWH,EAAQC,EAAYC,EAAQ,CAAC,CAAC,EAC/C,IAAI,YAAYC,EAAK,MAAM,CACnC,CAEO,SAASG,GACfN,EACAC,EACAC,EACc,CACd,GAAIA,IAAU,EAAG,OAAO,IAAI,YAAY,CAAC,EACzC,GAAID,EAAa,IAAM,EACtB,OAAO,IAAI,YAAYD,EAAQC,EAAYC,CAAK,EAEjD,IAAMC,EAAO,IAAI,WAAWD,EAAQ,CAAC,EACrC,OAAAC,EAAK,IAAI,IAAI,WAAWH,EAAQC,EAAYC,EAAQ,CAAC,CAAC,EAC/C,IAAI,YAAYC,EAAK,MAAM,CACnC,CAQO,SAASI,GACfC,EACAC,EACO,CACP,GAAID,EAAQ,SAAW,GACnB,EAAAA,aAAmB,aAAeC,EAAc,QACpD,QAASC,EAAI,EAAGA,EAAIF,EAAQ,OAAQE,IACnC,GAAIF,EAAQE,CAAC,GAAMD,EAClB,MAAMjB,EAAK,qCAAsC,CAChD,cAAekB,EACf,WAAYF,EAAQE,CAAC,EACrB,YAAAD,CACD,CAAC,EAGJ,CAGO,SAASE,EAASC,EAAoB,CAC5C,OAAQA,IAAO,EAAK,EAAEA,EAAK,EAC5B,CAOO,SAASC,GAAoBC,EAAoC,CACvE,IAAMrB,EAAM,IAAI,WAAWqB,EAAU,MAAM,EACvCC,EAAK,EACLC,EAAK,EACLC,EAAK,EACT,QAASP,EAAI,EAAGA,EAAII,EAAU,OAAQJ,GAAK,EAC1CK,EAAOA,EAAKJ,EAASG,EAAUJ,CAAC,CAAE,GAAM,IAAO,GAC/CM,EAAOA,EAAKL,EAASG,EAAUJ,EAAI,CAAC,CAAE,GAAM,IAAO,GACnDO,EAAOA,EAAKN,EAASG,EAAUJ,EAAI,CAAC,CAAE,GAAM,IAAO,GACnDjB,EAAIiB,CAAC,EAAIK,EACTtB,EAAIiB,EAAI,CAAC,EAAIM,EACbvB,EAAIiB,EAAI,CAAC,EAAIO,EAEd,OAAOxB,CACR,CAEO,SAASyB,GAAqBJ,EAAqC,CACzE,IAAMrB,EAAM,IAAI,YAAYqB,EAAU,MAAM,EACxCK,EAAO,EACX,QAAST,EAAI,EAAGA,EAAII,EAAU,OAAQJ,IACrCS,EAAQA,EAAOR,EAASG,EAAUJ,CAAC,CAAE,EAAK,MAC1CjB,EAAIiB,CAAC,EAAIS,EAEV,OAAO1B,CACR,CAEO,SAAS2B,GAAqBN,EAAqC,CACzE,IAAMrB,EAAM,IAAI,YAAYqB,EAAU,MAAM,EACxCK,EAAO,EACX,QAAST,EAAI,EAAGA,EAAII,EAAU,OAAQJ,IACrCS,EAAQA,EAAOR,EAASG,EAAUJ,CAAC,CAAE,IAAO,EAC5CjB,EAAIiB,CAAC,EAAIS,EAEV,OAAO1B,CACR,CAEO,SAASD,EAAK6B,EAAiBC,EAAsD,CAC3F,OAAO,IAAIzB,EAAmBwB,EAASvB,EAAW,iBAAkB,CAAE,QAAAwB,CAAQ,CAAC,CAChF,CCpNA,IAAMC,GAAwB,GAOvB,SAASC,GACfC,EACAC,EACAC,EACAC,EACAC,EACwC,CACxC,GAAIF,EAASJ,GAAwBE,EAAM,WAC1C,MAAMK,EAAK,6CAA8C,CACxD,cAAeP,GACf,eAAgBE,EAAM,WAAaE,EACnC,OAAAA,CACD,CAAC,EAGF,IAAMI,EAAWL,EAAK,UAAUC,EAAQ,EAAI,EAC5CA,GAAU,EACV,IAAMK,EAAUN,EAAK,WAAWC,EAAQ,EAAI,EAC5CA,GAAU,EACV,IAAMM,EAAUP,EAAK,WAAWC,EAAQ,EAAI,EAC5CA,GAAU,EACV,IAAMO,EAASR,EAAK,WAAWC,EAAQ,EAAI,EAC3CA,GAAU,EACV,IAAMQ,EAAST,EAAK,WAAWC,EAAQ,EAAI,EAC3CA,GAAU,EAEV,IAAMS,EAAiBR,EAAc,EAC/BS,EAAaN,IAAa,EAC1BO,EAAiBF,GAAkBC,EAAa,EAAI,GAC1D,GAAIV,EAASW,EAAiBb,EAAM,WACnC,MAAMK,EAAK,sCAAuC,CACjD,cAAeQ,EACf,eAAgBb,EAAM,WAAaE,EACnC,OAAAA,EACA,SAAAI,EACA,YAAAH,CACD,CAAC,EAGF,IAAMW,EAAiBd,EAAM,WAAaE,EACtCa,EACJ,GAAIH,EAEHG,EAAMC,EAAoBhB,EAAM,OAAQc,EAAgBH,CAAc,EAAE,MAAM,MACxE,CACN,IAAMM,EAAMC,EAAgBlB,EAAM,OAAQc,EAAgBH,CAAc,EACxEI,EAAM,IAAI,aAAaJ,CAAc,EACrC,IAAIQ,EAAK,EACLC,EAAK,EACT,QAASC,EAAI,EAAGA,EAAIV,EAAgBU,GAAK,EACpCjB,GACHe,EAAMA,EAAKG,EAASL,EAAII,CAAC,CAAE,EAAK,MAChCD,EAAMA,EAAKE,EAASL,EAAII,EAAI,CAAC,CAAE,EAAK,QAEpCF,EAAKF,EAAII,CAAC,EACVD,EAAKH,EAAII,EAAI,CAAC,GAEfN,EAAIM,CAAC,EAAId,EAAUY,EAAKV,EACxBM,EAAIM,EAAI,CAAC,EAAIb,EAAUY,EAAKV,CAE9B,CAEA,MAAO,CAAE,IAAAK,EAAK,OAAQb,EAASW,CAAe,CAC/C,CAMO,SAASU,GACfvB,EACAE,EACAC,EACAC,EACa,CACb,IAAMoB,EAAarB,EAAc,EACjC,GAAID,EAASsB,EAAaxB,EAAM,WAC/B,MAAMK,EAAK,gDAAiD,CAC3D,cAAemB,EACf,eAAgBxB,EAAM,WAAaE,EACnC,OAAAA,EACA,YAAAC,CACD,CAAC,EAGF,IAAMc,EAAMjB,EAAM,SAASE,EAAQA,EAASsB,CAAU,EACtD,GAAI,CAACpB,EACJ,OAAOa,EAAI,MAAM,EAGlB,IAAMQ,EAAS,IAAI,WAAWD,CAAU,EACpCE,EAAI,EACJC,EAAI,EACJC,EAAI,EACR,QAASP,EAAI,EAAGA,EAAIG,EAAYH,GAAK,EACpCK,EAAKA,EAAIJ,EAASL,EAAII,CAAC,CAAE,EAAK,IAC9BM,EAAKA,EAAIL,EAASL,EAAII,EAAI,CAAC,CAAE,EAAK,IAClCO,EAAKA,EAAIN,EAASL,EAAII,EAAI,CAAC,CAAE,EAAK,IAClCI,EAAOJ,CAAC,EAAIK,EACZD,EAAOJ,EAAI,CAAC,EAAIM,EAChBF,EAAOJ,EAAI,CAAC,EAAIO,EAEjB,OAAOH,CACR,CC3BO,SAASI,EACfC,EACwB,CACxB,IAAMC,EAAMC,GAAwBF,CAAK,EAErCG,EACAF,EAAI,UACPE,EAAWF,EAAI,WACLA,EAAI,aACdE,EAAWC,GAAoBH,EAAI,UAAyB,EAE5DE,EAAWF,EAAI,WAGhB,IAAII,EAAUJ,EAAI,UAClB,OAAIA,EAAI,eACPI,EACCA,aAAmB,YAChBC,GAAqBD,CAAO,EAC5BE,GAAqBF,CAAO,GAEjCG,GAAuBH,EAASJ,EAAI,WAAW,EAExC,CACN,SAAUA,EAAI,SACd,MAAOA,EAAI,MACX,SAAAE,EACA,QAAAE,EACA,OAAQJ,EAAI,OACZ,MAAOA,EAAI,MACX,IAAKA,EAAI,IACT,OAAQA,EAAI,MACb,CACD,CAyBO,SAASC,GACfF,EACqB,CACrB,GAAI,CAACS,GACJ,MAAM,IAAIC,EACT,qHACAC,EAAW,iBACZ,EAGD,IAAMC,EAAQC,GAAgBC,GAAad,CAAK,CAAC,EAC3Ce,EAAO,IAAI,SAASH,EAAM,OAAQA,EAAM,WAAYA,EAAM,UAAU,EAE1E,GAAIA,EAAM,WAAa,GACtB,MAAMI,EAAK,yCAA0C,CACpD,cAAe,GACf,eAAgBJ,EAAM,UACvB,CAAC,EAGF,IAAIK,EAAS,EAEPC,EAAQH,EAAK,UAAUE,EAAQ,EAAI,EAEzC,GADAA,GAAU,EACNC,IAAU,WACb,MAAMF,EAAK,yBAAyBE,EAAM,SAAS,EAAE,CAAC,GAAI,CACzD,cAAe,KAAK,YAAkB,SAAS,EAAE,CAAC,GAClD,YAAa,KAAKA,EAAM,SAAS,EAAE,CAAC,EACrC,CAAC,EAGF,IAAMC,EAAUJ,EAAK,UAAUE,EAAQ,EAAI,EAE3C,GADAA,GAAU,EACNE,EAAU,GAAyBA,EAAU,EAChD,MAAMH,EAAK,6BAA6BG,CAAO,GAAI,CAClD,oBAAqB,EACrB,oBAAqB,EACrB,cAAeA,CAChB,CAAC,EAGF,IAAMC,EAAcL,EAAK,UAAUE,EAAQ,EAAI,EAE/C,GADAA,GAAU,EACNA,EAASG,EAAcR,EAAM,WAChC,MAAMI,EAAK,2CAA4C,CACtD,cAAeI,EACf,eAAgBR,EAAM,WAAaK,EACnC,OAAAA,CACD,CAAC,EAGF,IAAMI,EAAgBT,EAAM,SAASK,EAAQA,EAASG,CAAW,EACjEH,GAAUG,EAEV,IAAIE,EACJ,GAAI,CACHA,EAAW,KAAK,MAAMC,GAAWF,CAAa,CAAC,CAChD,OAASG,EAAO,CACf,MAAMR,EACL,kCAAkCQ,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,CAAC,GACxF,CAAE,YAAAJ,CAAY,CACf,CACD,CAEA,GAAIH,EAAS,GAAwBL,EAAM,WAC1C,MAAMI,EAAK,6CAA8C,CACxD,cAAe,GACf,eAAgBJ,EAAM,WAAaK,EACnC,OAAAA,CACD,CAAC,EAGF,IAAMQ,EAAQV,EAAK,UAAUE,EAAQ,EAAI,EACzCA,GAAU,EAEV,IAAMS,EAAUX,EAAK,WAAWE,EAAQ,EAAI,EAC5CA,GAAU,EACV,IAAMU,EAAUZ,EAAK,WAAWE,EAAQ,EAAI,EAC5CA,GAAU,EACV,IAAMW,EAAUb,EAAK,WAAWE,EAAQ,EAAI,EAC5CA,GAAU,EAEV,IAAMY,EAASd,EAAK,WAAWE,EAAQ,EAAI,EAC3CA,GAAU,EACV,IAAMa,EAASf,EAAK,WAAWE,EAAQ,EAAI,EAC3CA,GAAU,EACV,IAAMc,EAAShB,EAAK,WAAWE,EAAQ,EAAI,EAC3CA,GAAU,EAEV,IAAMe,EAAcjB,EAAK,UAAUE,EAAQ,EAAI,EAC/CA,GAAU,EAEV,IAAMgB,GAAcR,EAAQ,KAAkB,EACxCS,GAAgBT,EAAQ,KAAwB,EAChDU,EAAiBH,EAAc,EAE/BI,EAAqBD,GADDF,EAAa,EAAI,GAG3C,GAAIhB,EAASmB,EAAqBxB,EAAM,WACvC,MAAMI,EAAK,sCAAuC,CACjD,cAAeoB,EACf,eAAgBxB,EAAM,WAAaK,EACnC,OAAAA,EACA,WAAAgB,EACA,YAAAD,CACD,CAAC,EAQF,IAAMK,EAAiBzB,EAAM,WAAaK,EACtCqB,EAWJ,GAVIL,EACHK,EAAaC,EAAoB3B,EAAM,OAAQyB,EAAgBF,CAAc,EACnED,EAEVI,EAAaE,EAAgB5B,EAAM,OAAQyB,EAAgBF,CAAc,EAEzEG,EAAaG,GAAkB7B,EAAM,OAAQyB,EAAgBF,CAAc,EAE5ElB,GAAUmB,EAENnB,EAAS,EAAIL,EAAM,WACtB,MAAMI,EAAK,yCAA0C,CACpD,cAAe,EACf,eAAgBJ,EAAM,WAAaK,EACnC,OAAAA,CACD,CAAC,EAEF,IAAMyB,EAAa3B,EAAK,UAAUE,EAAQ,EAAI,EAC9CA,GAAU,EAEV,IAAM0B,GAAoBlB,EAAQ,KAAyB,EAErDmB,EAAoBF,GADJC,EAAmB,EAAI,GAE7C,GAAI1B,EAAS2B,EAAoBhC,EAAM,WACtC,MAAMI,EAAK,qCAAsC,CAChD,cAAe4B,EACf,eAAgBhC,EAAM,WAAaK,EACnC,OAAAA,EACA,WAAAyB,EACA,iBAAAC,CACD,CAAC,EAGF,IAAME,EAAYF,EACfH,EAAgB5B,EAAM,OAAQA,EAAM,WAAaK,EAAQyB,CAAU,EACnEI,GAAgBlC,EAAM,OAAQA,EAAM,WAAaK,EAAQyB,CAAU,EACtEzB,GAAU2B,EAIV,IAAIG,EAA2B,KAC/B,IAAKtB,EAAQ,KAAkB,EAAG,CACjC,IAAMuB,EAASC,GAAarC,EAAOG,EAAME,EAAQe,EAAaE,CAAY,EAC1Ea,EAAMC,EAAO,IACb/B,EAAS+B,EAAO,MACjB,CAEA,IAAIE,EAA4B,KAChC,OAAKzB,EAAQ,MAA4B,IACxCyB,EAASC,GAAgBvC,EAAOK,EAAQe,EAAaE,CAAY,GAG3D,CACN,SAAAZ,EACA,MAAAG,EACA,WAAAa,EACA,UAAAO,EACA,UAAWZ,EACX,aAAAC,EACA,YAAAF,EACA,OAAQ,CAACN,EAASC,EAASC,CAAO,EAClC,MAAO,CAACC,EAAQC,EAAQC,CAAM,EAC9B,IAAAgB,EACA,OAAAG,CACD,CACD,CCpRO,SAASE,GAAmBC,EAA2C,CAE7E,GAAM,CAAE,UAAAC,EAAW,aAAAC,EAAc,OAAAC,EAAQ,MAAAC,EAAO,IAAAC,EAAK,OAAAC,EAAQ,KAAAC,CAAK,EAAIP,EAEhEQ,EAAYC,GAAwBA,IAAO,EAAK,EAAEA,EAAK,GAGzDC,EACJ,GAAIT,EACHS,EAAgBV,EAAM,eAChB,CACN,IAAIW,EACJ,GAAIT,EAAc,CACjB,IAAMU,EAAYZ,EAAM,WACxBW,EAAY,IAAI,WAAWC,EAAU,MAAM,EAC3C,IAAIC,EAAK,EACLC,EAAK,EACLC,EAAK,EACT,QAASC,EAAI,EAAGA,EAAIJ,EAAU,OAAQI,GAAK,EAC1CH,EAAOA,EAAKL,EAASI,EAAUI,CAAC,CAAC,GAAM,IAAO,GAC9CF,EAAOA,EAAKN,EAASI,EAAUI,EAAI,CAAC,CAAC,GAAM,IAAO,GAClDD,EAAOA,EAAKP,EAASI,EAAUI,EAAI,CAAC,CAAC,GAAM,IAAO,GAClDL,EAAUK,CAAC,EAAIH,EACfF,EAAUK,EAAI,CAAC,EAAIF,EACnBH,EAAUK,EAAI,CAAC,EAAID,CAErB,MACCJ,EAAYX,EAAM,WAGnBU,EAAgB,IAAI,aAAaC,EAAU,MAAM,EACjD,IAAMM,EAAKd,EAAO,CAAC,EACbe,EAAKf,EAAO,CAAC,EACbgB,EAAKhB,EAAO,CAAC,EACbiB,EAAKhB,EAAM,CAAC,EACZiB,EAAKjB,EAAM,CAAC,EACZkB,EAAKlB,EAAM,CAAC,EAClB,QAASY,EAAI,EAAGA,EAAIL,EAAU,OAAQK,GAAK,EAC1CN,EAAcM,CAAC,EAAIC,GAAMN,EAAUK,CAAC,EAAI,OAASI,EACjDV,EAAcM,EAAI,CAAC,EAAIE,GAAMP,EAAUK,EAAI,CAAC,EAAI,OAASK,EACzDX,EAAcM,EAAI,CAAC,EAAIG,GAAMR,EAAUK,EAAI,CAAC,EAAI,OAASM,CAE3D,CAEA,IAAIC,EACJ,GAAIrB,EAAc,CACjB,IAAMU,EAAYZ,EAAM,UACxB,GAAIY,aAAqB,YAAa,CACrC,IAAMY,EAAM,IAAI,YAAYZ,EAAU,MAAM,EACxCa,EAAO,EACX,QAAST,EAAI,EAAGA,EAAIJ,EAAU,OAAQI,IACrCS,EAAQA,EAAOjB,EAASI,EAAUI,CAAC,CAAC,EAAK,MACzCQ,EAAIR,CAAC,EAAIS,EAEVF,EAAUC,CACX,KAAO,CACN,IAAMA,EAAM,IAAI,YAAYZ,EAAU,MAAM,EACxCa,EAAO,EACX,QAAST,EAAI,EAAGA,EAAIJ,EAAU,OAAQI,IACrCS,EAAQA,EAAOjB,EAASI,EAAUI,CAAC,CAAC,IAAO,EAC3CQ,EAAIR,CAAC,EAAIS,EAEVF,EAAUC,CACX,CACD,MACCD,EAAUvB,EAAM,UAGjB,IAAM0B,EAAmBhB,EAAc,OAAS,EAChD,QAASM,EAAI,EAAGA,EAAIO,EAAQ,OAAQP,IACnC,GAAIO,EAAQP,CAAC,GAAKU,EACjB,MAAM,IAAI,MAAM,SAASH,EAAQP,CAAC,CAAC,gCAAgCU,CAAgB,EAAE,EAKvF,IAAMC,EAA+B,CAAC,EAEtC,QAAWC,KAAOrB,EAAM,CACvB,IAAIsB,EAAc,EACdC,EAAa,EACjB,QAAWC,KAAUH,EAAI,QACxBC,GAAeE,EAAO,YACtBD,GAAcC,EAAO,WAGtB,IAAMC,EAAY,IAAI,aAAaH,EAAc,CAAC,EAC5CI,EAAa,IAAI,YAAYH,CAAU,EACvCI,EAAS7B,EAAM,IAAI,aAAawB,EAAc,CAAC,EAAI,KACnDM,EAAY7B,EAAS,IAAI,WAAWuB,EAAc,CAAC,EAAI,KAEzDO,EAAe,EACfC,EAAc,EAClB,QAAWN,KAAUH,EAAI,QAAS,CACjC,IAAMU,EAAiBP,EAAO,YAAc,EAC5CC,EAAU,IACTtB,EAAc,SAAS4B,EAAgBA,EAAiBP,EAAO,YAAc,CAAC,EAC9EK,EAAe,CAChB,EACIF,GAAU7B,GACb6B,EAAO,IACN7B,EAAI,SAAS0B,EAAO,YAAc,GAAIA,EAAO,YAAcA,EAAO,aAAe,CAAC,EAClFK,EAAe,CAChB,EAEGD,GAAa7B,GAChB6B,EAAU,IACT7B,EAAO,SAASgC,EAAgBA,EAAiBP,EAAO,YAAc,CAAC,EACvEK,EAAe,CAChB,EAGD,IAAMG,EAAcR,EAAO,YACrBS,EAAYT,EAAO,YAAcA,EAAO,YACxCU,EAAQL,EAAeL,EAAO,YACpC,QAASf,EAAI,EAAGA,EAAIe,EAAO,WAAYf,IAAK,CAC3C,IAAM0B,EAAanB,EAAQQ,EAAO,WAAaf,CAAC,EAChD,GAAI0B,EAAaH,GAAeG,GAAcF,EAC7C,MAAM,IAAI,MACT,SAASE,CAAU,2BAA2BH,CAAW,KAAKC,CAAS,GACxE,EAEDP,EAAWI,EAAcrB,CAAC,EAAI0B,EAAaD,CAC5C,CAEAL,GAAgBL,EAAO,YACvBM,GAAeN,EAAO,UACvB,CAKA,IAAMY,EAAU,IAAI,aAAad,EAAc,CAAC,EAChD,QAASb,EAAI,EAAGA,EAAIiB,EAAW,OAAQjB,GAAK,EAAG,CAC9C,IAAM4B,EAAIX,EAAWjB,CAAC,EAAI,EACpB6B,EAAIZ,EAAWjB,EAAI,CAAC,EAAI,EACxB8B,EAAIb,EAAWjB,EAAI,CAAC,EAAI,EAExB+B,EAAMf,EAAUc,CAAC,EAAId,EAAUa,CAAC,EAChCG,EAAMhB,EAAUc,EAAI,CAAC,EAAId,EAAUa,EAAI,CAAC,EACxCI,EAAMjB,EAAUc,EAAI,CAAC,EAAId,EAAUa,EAAI,CAAC,EACxCK,EAAMlB,EAAUY,CAAC,EAAIZ,EAAUa,CAAC,EAChCM,EAAMnB,EAAUY,EAAI,CAAC,EAAIZ,EAAUa,EAAI,CAAC,EACxCO,GAAMpB,EAAUY,EAAI,CAAC,EAAIZ,EAAUa,EAAI,CAAC,EAExCQ,EAAKL,EAAMI,GAAMH,EAAME,EACvBG,EAAKL,EAAMC,EAAMH,EAAMK,GACvBG,EAAKR,EAAMI,EAAMH,EAAME,EAE7BP,EAAQC,CAAC,GAAKS,EACdV,EAAQC,EAAI,CAAC,GAAKU,EAClBX,EAAQC,EAAI,CAAC,GAAKW,EAClBZ,EAAQE,CAAC,GAAKQ,EACdV,EAAQE,EAAI,CAAC,GAAKS,EAClBX,EAAQE,EAAI,CAAC,GAAKU,EAClBZ,EAAQG,CAAC,GAAKO,EACdV,EAAQG,EAAI,CAAC,GAAKQ,EAClBX,EAAQG,EAAI,CAAC,GAAKS,CACnB,CACA,QAASvC,EAAI,EAAGA,EAAI2B,EAAQ,OAAQ3B,GAAK,EAAG,CAC3C,IAAMwC,EAAIb,EAAQ3B,CAAC,EACbyC,EAAId,EAAQ3B,EAAI,CAAC,EACjB0C,EAAIf,EAAQ3B,EAAI,CAAC,EACjB2C,EAAS,KAAK,KAAKH,EAAIA,EAAIC,EAAIA,EAAIC,EAAIA,CAAC,GAAK,EACnDf,EAAQ3B,CAAC,EAAIwC,EAAIG,EACjBhB,EAAQ3B,EAAI,CAAC,EAAIyC,EAAIE,EACrBhB,EAAQ3B,EAAI,CAAC,EAAI0C,EAAIC,CACtB,CAEAhC,EAAQ,KAAK,CACZ,UAAAK,EACA,QAAAW,EACA,QAASV,EACT,IAAKC,EACL,OAAQC,CACT,CAAC,CACF,CAEA,OAAOR,CACR,CAOO,SAASiC,IAAmC,CAClD,MAAO,CACN,oBAAoB7D,GAAmB,SAAS,CAAC,IACjD,gCACA,sCACA,UACA,0CACA,2BACA,oCACA,+EACA,gDACA,sDACA,QACA,sDACA,sBACA,kFACA,MACA,IACD,EAAE,KAAK;AAAA,CAAI,CACZ,CCrPO,IAAM8D,GAAgC,IAOzCC,EACEC,EAAoB,IAAI,IAC1BC,GAAwB,EAErB,SAASC,IAAmC,CAClD,GAAIH,IAAmB,OAAW,OAAOA,EACzC,GACC,OAAO,OAAW,KAClB,OAAO,KAAS,KAChB,OAAO,IAAQ,KACf,OAAO,IAAI,iBAAoB,WAE/B,OAAAA,EAAiB,KACV,KAER,GAAI,CAEH,IAAMI,EAAM,IAAI,gBACf,IAAI,KAAK,CAACC,GAAyB,CAAC,EAAG,CAAE,KAAM,iBAAkB,CAAC,CACnE,EACMC,EAAS,IAAI,OAAOF,CAAG,EAC7BE,EAAO,UAAaC,GAAwB,CAC3C,GAAM,CAAE,GAAAC,EAAI,WAAAC,EAAY,MAAAC,CAAM,EAAIH,EAAM,KAKlCI,EAAUV,EAAkB,IAAIO,CAAE,EACnCG,IACLV,EAAkB,OAAOO,CAAE,EACvBC,EAAYE,EAAQ,QAAQF,CAAU,EACrCE,EAAQ,OAAO,IAAI,MAAMD,GAAS,gCAAgC,CAAC,EACzE,EACAJ,EAAO,QAAU,IAAM,CACtB,QAAWK,KAAWV,EAAkB,OAAO,EAC9CU,EAAQ,OAAO,IAAI,MAAM,8BAA8B,CAAC,EAEzDV,EAAkB,MAAM,EACxBK,EAAO,UAAU,EACjBN,EAAiB,IAClB,EACAA,EAAiBM,CAClB,MAAQ,CACPN,EAAiB,IAClB,CACA,OAAOA,CACR,CAEO,SAASY,GACfN,EACAO,EACAC,EAC+B,CAC/B,OAAO,IAAI,QAA6B,CAACC,EAASC,IAAW,CAC5D,IAAMR,EAAKN,KACXD,EAAkB,IAAIO,EAAI,CAAE,QAAAO,EAAS,OAAAC,CAAO,CAAC,EAC7CV,EAAO,YAAY,CAAE,GAAAE,EAAI,MAAAK,CAAM,EAAGC,CAAQ,CAC3C,CAAC,CACF,CC1EA,UAAYG,MAAW,QCAvB,UAAYC,MAAW,QASvB,IAAIC,GAAgB,EAOb,SAASC,GAAqBC,EAAqB,CACzDF,GAAgB,KAAK,IAAI,EAAGE,CAAK,CAClC,CAIAC,GAAqBF,EAAoB,EAUlC,SAASG,GAAgBC,EAAsCC,EAAmB,CAEpF,OAAO,SAAa,KAIxB,IAAU,gBAAc,EAAE,KACzBA,EACCC,GAAY,CAEZA,EAAQ,WAAmB,iBAE3BA,EAAQ,WAAaP,GACrBK,EAAS,IAAME,EACfF,EAAS,YAAc,EACxB,EACA,OACCG,GAAU,CACVC,EAAU,EAAE,KAAK,mCAAmCH,CAAG,IAAKE,CAAK,CAClE,CACD,CACD,CDzCA,IAAME,GAA4B,GAC5BC,GAAkB,GAClBC,GAA4B,GAE3B,SAASC,GACfC,EACAC,EAC6B,CAC7B,IAAMC,EAAQC,GAAWH,EAAQ,KAAK,EAChCI,EAAeH,GAAS,cAAgB,GACxCI,EAAaJ,GAAS,WAEtBK,EAAW,IAAU,uBAAqB,CAC/C,MAAAJ,EACA,UAAWF,EAAQ,UACnB,UAAWA,EAAQ,UACnB,QAASA,EAAQ,QACjB,YAAaA,EAAQ,YACrB,aAAAI,EAGA,KAAMC,GAAY,cAAsB,YAAkB,aAC1D,cAAe,GACf,oBAAqB,GACrB,mBAAoB,GACpB,WAAY,GACZ,UAAW,EACZ,CAAC,EAID,OAAIA,GAAY,iBAAmB,OAClCC,EAAS,gBAAkBD,EAAW,iBAInCL,EAAQ,UAAYJ,KACvBU,EAAS,UAAYT,GACrBS,EAAS,mBAAqBR,IAG3BM,GACHG,GAA2BD,CAAQ,EAIhCN,EAAQ,KACXQ,GAAgBF,EAAUN,EAAQ,GAAG,EAG/BM,CACR,CAQO,SAASC,GAA2BD,EAAgC,CAC1EA,EAAS,gBAAmBG,GAAW,CACtCA,EAAO,aAAeA,EAAO,aAAa,QACzC,0BACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQD,CACD,CACD,CErFA,UAAYC,MAAW,QCIhB,SAASC,EACfC,EACAC,EACqB,CACrB,OAAO,IAAIC,EAAmBF,EAASG,EAAW,iBAAkB,CAAE,QAAAF,CAAQ,CAAC,CAChF,CAQO,SAASG,GACfC,EACAC,EACAC,EACAC,EACO,CACP,QAAWC,KAASJ,EAAQ,CAC3B,GACC,CAAC,OAAO,UAAUI,EAAM,UAAU,GAClCA,EAAM,WAAa,GACnBA,EAAM,YAAcH,EAEpB,MAAMP,EAAa,wDAAyD,CAC3E,WAAYU,EAAM,WAClB,cAAAH,CACD,CAAC,EAGF,QAAWI,KAAQD,EAAM,OAAQ,CAChC,IAAME,EAAS,CACd,YAAaD,EAAK,YAClB,YAAaA,EAAK,YAClB,WAAYA,EAAK,WACjB,WAAYA,EAAK,UAClB,EACA,OAAW,CAACE,EAAOC,CAAK,IAAK,OAAO,QAAQF,CAAM,EACjD,GAAI,CAAC,OAAO,UAAUE,CAAK,GAAKA,EAAQ,EACvC,MAAMd,EAAa,wBAAwBa,CAAK,oCAAqC,CACpF,SAAUF,EAAK,KACf,MAAAE,EACA,MAAAC,CACD,CAAC,EAIH,GAAIH,EAAK,YAAcA,EAAK,YAAcH,EACzC,MAAMR,EAAa,sDAAuD,CACzE,SAAUW,EAAK,KACf,YAAaA,EAAK,YAClB,YAAaA,EAAK,YAClB,iBAAAH,CACD,CAAC,EAGF,GAAIG,EAAK,WAAaA,EAAK,WAAaF,EACvC,MAAMT,EAAa,oDAAqD,CACvE,SAAUW,EAAK,KACf,WAAYA,EAAK,WACjB,WAAYA,EAAK,WACjB,gBAAAF,CACD,CAAC,CAEH,CACD,CACD,CASO,SAASM,GAAiBC,EAAoBC,EAA4C,CAChG,OAAOjB,EAAa,8DAA+D,CAClF,SAAUiB,EAAS,KACnB,WAAAD,EACA,YAAaC,EAAS,YACtB,YAAaA,EAAS,WACvB,CAAC,CACF,CAOO,SAASC,GACfC,EACAC,EACAC,EACe,CACf,IAAMC,EAAM,IAAI,aAAaH,EAAE,MAAM,EAC/BI,EAAKH,EAAO,CAAC,EACbI,EAAKJ,EAAO,CAAC,EACbK,EAAKL,EAAO,CAAC,EACbM,EAAKL,EAAM,CAAC,EACZM,EAAKN,EAAM,CAAC,EACZO,EAAKP,EAAM,CAAC,EAElB,QAASQ,EAAI,EAAGA,EAAIV,EAAE,OAAQU,GAAK,EAClCP,EAAIO,CAAC,EAAIN,GAAMJ,EAAEU,CAAC,EAAK,OAASH,EAChCJ,EAAIO,EAAI,CAAC,EAAIL,GAAML,EAAEU,EAAI,CAAC,EAAK,OAASF,EACxCL,EAAIO,EAAI,CAAC,EAAIJ,GAAMN,EAAEU,EAAI,CAAC,EAAK,OAASD,EAGzC,OAAON,CACR,CDvGO,SAASQ,GACfC,EACAC,EACAC,EACAC,EACAC,EAA8B,KAC9BC,EAA+B,KAClB,CACb,IAAIC,EAAmB,EACnBC,EAAkB,EACtB,QAAWC,KAAYR,EAAM,OAC5BM,GAAoBE,EAAS,YAC7BD,GAAmBC,EAAS,WAG7B,IAAMC,EAAiB,IAAI,aAAaH,EAAmB,CAAC,EACtDI,EAAgB,IAAI,YAAYH,CAAe,EAC/CI,EAAYP,EAAS,IAAI,aAAaE,EAAmB,CAAC,EAAI,KAC9DM,EAAeP,EAAY,IAAI,WAAWC,EAAmB,CAAC,EAAI,KAEpEO,EAAoB,EACpBC,EAAmB,EAEvB,QAAWN,KAAYR,EAAM,OAAQ,CACpC,IAAMe,EAAiBP,EAAS,YAAc,EACxCQ,EAAeR,EAAS,YAAc,EAM5C,GALAC,EAAe,IACdR,EAAY,SAASc,EAAgBA,EAAiBC,CAAY,EAClEH,EAAoB,CACrB,EAEIF,GAAaP,EAAQ,CACxB,IAAMa,EAAUT,EAAS,YAAc,EACvCG,EAAU,IACTP,EAAO,SAASa,EAASA,EAAUT,EAAS,YAAc,CAAC,EAC3DK,EAAoB,CACrB,CACD,CAEID,GAAgBP,GACnBO,EAAa,IACZP,EAAU,SAASU,EAAgBA,EAAiBC,CAAY,EAChEH,EAAoB,CACrB,EAGD,IAAMK,EAAehB,EAAW,SAC/BM,EAAS,WACTA,EAAS,WAAaA,EAAS,UAChC,EACMW,EAAaN,EAAoBL,EAAS,YAC1CY,EAAcZ,EAAS,YACvBa,EAAYb,EAAS,YAAcA,EAAS,YAClD,QAASc,EAAI,EAAGA,EAAIJ,EAAa,OAAQI,IAAK,CAC7C,IAAMC,EAAaL,EAAaI,CAAC,EACjC,GAAIC,EAAaH,GAAeG,GAAcF,EAC7C,MAAMG,GAAiBD,EAAYf,CAAQ,EAE5CE,EAAcI,EAAmBQ,CAAC,EAAIC,EAAaJ,CACpD,CAEAN,GAAqBL,EAAS,YAC9BM,GAAoBN,EAAS,UAC9B,CAEA,IAAMiB,EAAW,IAAU,iBAC3B,OAAAA,EAAS,aAAa,WAAY,IAAU,kBAAgBhB,EAAgB,CAAC,CAAC,EAC9EgB,EAAS,SAAS,IAAU,kBAAgBf,EAAe,CAAC,CAAC,EACzDC,GACHc,EAAS,aAAa,KAAM,IAAU,kBAAgBd,EAAW,CAAC,CAAC,EAEhEC,GACHa,EAAS,aAAa,QAAS,IAAU,kBAAgBb,EAAc,EAAG,EAAI,CAAC,EAEhFa,EAAS,qBAAqB,EAEvBC,GAAmBD,EAAUzB,EAAOG,CAAS,CACrD,CAEO,SAASuB,GACfD,EACAzB,EACAG,EACa,CACb,IAAMwB,EAAY,IAAU,OAAKF,EAAUtB,EAAUH,EAAM,UAAU,CAAC,EAChE4B,EAAY5B,EAAM,OAAO,CAAC,EAC1B6B,EAAY7B,EAAM,OAAO,IAAK8B,GAAMA,EAAE,IAAI,EAAE,OAAQC,GAASA,GAAQA,EAAK,OAAS,CAAC,EAC1F,OAAAJ,EAAU,KAAOE,EAAU,OAAS,EAAIA,EAAU,CAAC,EAAK,mBAAmB7B,EAAM,UAAU,GAC3F2B,EAAU,WAAa,GACvBA,EAAU,cAAgB,GAE1BA,EAAU,SAAW,CACpB,OAAQ,UACR,KAAMA,EAAU,KAChB,MAAOC,GAAW,OAAS,GAC3B,cAAeA,GAAW,eAAiB,EAC3C,SAAUA,GAAW,UAAY,CAAC,EAClC,WAAY5B,EAAM,OAAO,MAAM,CAAC,EAAE,IAAK8B,IAAO,CAC7C,KAAMA,EAAE,KACR,MAAOA,EAAE,MACT,cAAeA,EAAE,aAClB,EAAE,CACH,EAEOH,CACR,CAMO,SAASK,GACfhC,EACAC,EACAC,EACAC,EACAC,EAA8B,KAC9BC,EAA+B,KAChB,CACf,IAAM4B,EAAuB,CAAC,EAE9B,QAAWzB,KAAYR,EAAM,OAAQ,CACpC,IAAMe,EAAiBP,EAAS,YAAc,EACxCQ,EAAeR,EAAS,YAAc,EAItC0B,EAAWjC,EAAY,MAAMc,EAAgBA,EAAiBC,CAAY,EAE1EE,EAAehB,EAAW,SAC/BM,EAAS,WACTA,EAAS,WAAaA,EAAS,UAChC,EACM2B,EAAiB,IAAI,YAAYjB,EAAa,MAAM,EACpDkB,EAAY5B,EAAS,YACrBa,EAAYb,EAAS,YAAcA,EAAS,YAClD,QAASc,EAAI,EAAGA,EAAIJ,EAAa,OAAQI,IAAK,CAC7C,IAAMC,EAAaL,EAAaI,CAAC,EACjC,GAAIC,EAAaa,GAAab,GAAcF,EAC3C,MAAMG,GAAiBD,EAAYf,CAAQ,EAE5C2B,EAAeb,CAAC,EAAIC,EAAaa,CAClC,CAEA,IAAMX,EAAW,IAAU,iBAG3B,GAFAA,EAAS,aAAa,WAAY,IAAU,kBAAgBS,EAAU,CAAC,CAAC,EACxET,EAAS,SAAS,IAAU,kBAAgBU,EAAgB,CAAC,CAAC,EAC1D/B,EAAQ,CACX,IAAMa,EAAUT,EAAS,YAAc,EACjC6B,EAAMjC,EAAO,MAAMa,EAASA,EAAUT,EAAS,YAAc,CAAC,EACpEiB,EAAS,aAAa,KAAM,IAAU,kBAAgBY,EAAK,CAAC,CAAC,CAC9D,CACA,GAAIhC,EAAW,CACd,IAAMiC,EAASjC,EAAU,MAAMU,EAAgBA,EAAiBC,CAAY,EAC5ES,EAAS,aAAa,QAAS,IAAU,kBAAgBa,EAAQ,EAAG,EAAI,CAAC,CAC1E,CACAb,EAAS,qBAAqB,EAE9BQ,EAAO,KAAKM,GAAmBd,EAAUjB,EAAUR,EAAOG,CAAS,CAAC,CACrE,CAEA,OAAO8B,CACR,CAEO,SAASM,GACfd,EACAjB,EACAR,EACAG,EACa,CACb,IAAMqC,EAAO,IAAU,OAAKf,EAAUtB,EAAUH,EAAM,UAAU,CAAC,EACjE,OAAAwC,EAAK,KAAOhC,EAAS,KACrBgC,EAAK,SAAW,CACf,OAAQ,UACR,KAAMhC,EAAS,KACf,MAAOA,EAAS,OAAS,GACzB,cAAeA,EAAS,cACxB,SAAUA,EAAS,UAAY,CAAC,CACjC,EACAgC,EAAK,WAAa,GAClBA,EAAK,cAAgB,GACdA,CACR,CTpHA,eAAsBC,GACrBC,EACAC,EAEAC,EACwB,CACxB,GAAM,CAAE,gBAAAC,EAAkB,GAAM,MAAAC,EAAQ,GAAO,SAAAC,CAAS,EAAIJ,GAAW,CAAC,EAClE,CAAE,UAAAK,EAAY,EAAG,UAAAC,EAAYH,EAAQ,YAAY,IAAI,EAAI,CAAE,EAAIF,GAAa,CAAC,EAEnF,GAAI,CAACF,EAAM,eAGV,MAAO,CAAC,EAIT,IAAMQ,EAAe,MAAMC,GAAkBT,EAAM,eAAgB,CAClE,gBAAAG,EACA,MAAAC,EACA,SAAAC,EACA,SAAU,CACT,UAAWL,EAAM,UACjB,OAAQA,EAAM,OACd,kBAAmBA,EAAM,iBAC1B,CACD,CAAC,EACD,GAAIQ,EAAc,OAAOA,EAEzB,IAAME,EAAc,YAAY,IAAI,EAC9BC,EAASC,EAAqBZ,EAAM,cAAc,EAClDa,EAAa,YAAY,IAAI,EAAIH,EAEjCI,EAAYV,EAAQW,GAA8Bf,EAAM,cAAc,EAAI,EAEhF,OAAOgB,GAAsBL,EAAQ,CACpC,gBAAAR,EACA,MAAAC,EACA,SAAAC,EACA,UAAAC,EACA,WAAAO,EACA,UAAAN,EACA,UAAAO,EACA,SAAU,CACT,UAAWd,EAAM,UACjB,OAAQA,EAAM,OACd,kBAAmBA,EAAM,iBAC1B,CACD,CAAC,CACF,CAWA,eAAsBiB,GACrBC,EACAjB,EACwB,CACxB,GAAM,CAAE,gBAAAE,EAAkB,GAAM,MAAAC,EAAQ,GAAO,SAAAC,CAAS,EAAIJ,GAAW,CAAC,EAElEM,EAAYH,EAAQ,YAAY,IAAI,EAAI,EAGxCI,EAAe,MAAMC,GAAkBS,EAAM,CAClD,gBAAAf,EACA,MAAAC,EACA,SAAAC,CACD,CAAC,EACD,GAAIG,EAAc,OAAOA,EAEzB,IAAME,EAAc,YAAY,IAAI,EAC9BC,EAASC,EAAqBM,CAAI,EAClCL,EAAa,YAAY,IAAI,EAAIH,EAEjCI,EAAYI,EAAK,WAEvB,OAAOF,GAAsBL,EAAQ,CACpC,gBAAAR,EACA,MAAAC,EACA,SAAAC,EACA,UAAW,EACX,WAAAQ,EACA,UAAAN,EACA,UAAAO,CACD,CAAC,CACF,CAkBA,SAASE,GACRL,EACAQ,EACwB,CACxB,GAAM,CACL,gBAAAhB,EACA,MAAAC,EACA,SAAUgB,EACV,UAAAd,EACA,WAAAO,EACA,UAAAN,EACA,UAAAO,EACA,SAAAO,CACD,EAAIF,EAEEG,EAAeX,EAAO,SAAS,WAAaU,GAAU,WAAa,CAAC,EACpEE,EAASZ,EAAO,SAAS,QAAUU,GAAU,QAAU,CAAC,EAKxDG,EAAoBH,GAAU,mBAAqBV,EAAO,SAAS,kBAEnEc,GAAad,EAAO,MAAQ,KAAkB,EAMpDe,GACCH,EACAD,EAAa,OACbX,EAAO,SAAS,OAAS,EACzBA,EAAO,QAAQ,MAChB,EAKA,IAAMgB,EAAgBF,EAClBd,EAAO,SACRiB,GAAgBjB,EAAO,SAAwBA,EAAO,OAAQA,EAAO,KAAK,EAE7E,GAAIP,EAAO,CACV,IAAMyB,EAAYlB,EAAO,SAAS,WAAaA,EAAO,QAAQ,WAC9DmB,EAAU,EAAE,MAAM,mBAAmB,EACrCA,EAAU,EAAE,MAAM,gBAAgBR,EAAa,MAAM,cAAcC,EAAO,MAAM,EAAE,EAClFO,EAAU,EAAE,MACX,eAAenB,EAAO,SAAS,OAAS,CAAC,eAAeA,EAAO,QAAQ,MAAM,EAC9E,EACAmB,EAAU,EAAE,MAAM,aAAaL,EAAY,UAAY,iBAAiB,EAAE,EAC1EK,EAAU,EAAE,MACX,YAAYhB,EAAY,KAAO,MAAM,QAAQ,CAAC,CAAC,4BAA4Be,EAAY,KAAO,MAAM,QAAQ,CAAC,CAAC,KAC/G,CACD,CAEA,IAAME,EAAkB,YAAY,IAAI,EAGlCC,EAAYV,EAAa,IAAKW,GACnCC,GAAeD,EAAG,CACjB,aAActB,EAAO,QAAU,KAC/B,WAAYS,CACb,CAAC,CACF,EAEMe,EAAuB,CAAC,EAE9B,QAAWC,KAASb,EACnB,GAAIpB,GAAmBiC,EAAM,OAAO,OAAS,EAAG,CAC/C,IAAMC,EAAaC,GAClBF,EACAT,EACAhB,EAAO,QACPqB,EACArB,EAAO,IACPA,EAAO,MACR,EACA0B,EAAW,SAAS,kBAAoBb,GAAqB,KAC7DW,EAAO,KAAKE,CAAU,CACvB,KAAO,CACN,IAAME,EAAmBC,GACxBJ,EACAT,EACAhB,EAAO,QACPqB,EACArB,EAAO,IACPA,EAAO,MACR,EACA,QAAW8B,KAAQF,EAClBE,EAAK,SAAS,kBAAoBjB,GAAqB,KAExDW,EAAO,KAAK,GAAGI,CAAgB,CAChC,CAGD,IAAMG,EAAiB,YAAY,IAAI,EAAIX,EAE3C,GAAI3B,EAAO,CACV,IAAMuC,EAAY,YAAY,IAAI,EAAIpC,EACtCuB,EAAU,EAAE,MAAM,cAAc,EAC5BxB,EAAY,GAAGwB,EAAU,EAAE,MAAM,iBAAiBxB,EAAU,QAAQ,CAAC,CAAC,IAAI,EAC9EwB,EAAU,EAAE,MAAM,oBAAoBjB,EAAW,QAAQ,CAAC,CAAC,IAAI,EAC/DiB,EAAU,EAAE,MAAM,oBAAoBY,EAAe,QAAQ,CAAC,CAAC,IAAI,EACnEZ,EAAU,EAAE,MAAM,YAAYa,EAAU,QAAQ,CAAC,CAAC,IAAI,CACvD,CAEA,OAAO,QAAQ,QAAQR,CAAM,CAC9B,CAsBA,eAAe1B,GACdmC,EACAzB,EAC+B,CAC/B,GAAI,OAAO,OAAW,IAAa,OAAO,KAE1C,IAAM0B,EAAMC,GAAwBF,CAAK,EACzC,GAAIC,EAAI,UAAU,OAAS,EAAIE,GAA+B,OAAO,KACrE,IAAMC,EAASC,GAAkB,EACjC,GAAI,CAACD,EAAQ,OAAO,KAEpB,IAAM1B,EAAeuB,EAAI,SAAS,WAAa1B,EAAK,UAAU,WAAa,CAAC,EACtEI,EAASsB,EAAI,SAAS,QAAU1B,EAAK,UAAU,QAAU,CAAC,EAC1DK,EAAoBL,EAAK,UAAU,mBAAqB0B,EAAI,SAAS,kBAC3EnB,GAAsBH,EAAQD,EAAa,OAAQuB,EAAI,YAAaA,EAAI,UAAU,MAAM,EAQxF,IAAMK,EAAYjB,IAAqC,CACtD,YAAaA,EAAE,YACf,YAAaA,EAAE,YACf,WAAYA,EAAE,WACd,WAAYA,EAAE,UACf,GACMkB,EAAsB,CAAC,EACvBC,EAAoB,CAAC,EAC3B,QAAWhB,KAASb,EACnB,GAAIJ,EAAK,iBAAmBiB,EAAM,OAAO,OAAS,EACjDe,EAAK,KAAK,CAAE,KAAM,SAAU,QAASf,EAAM,OAAO,IAAIc,CAAQ,CAAE,CAAC,EACjEE,EAAQ,KAAK,CAAE,KAAM,SAAU,MAAAhB,CAAM,CAAC,MAEtC,SAAWiB,KAAYjB,EAAM,OAC5Be,EAAK,KAAK,CAAE,KAAM,SAAU,QAAS,CAACD,EAASG,CAAQ,CAAC,CAAE,CAAC,EAC3DD,EAAQ,KAAK,CAAE,KAAM,SAAU,MAAAhB,EAAO,SAAAiB,CAAS,CAAC,EAOnD,IAAMC,EAAaT,EAAI,WAAW,MAAM,EAClCU,EAAYV,EAAI,UAAU,MAAM,EAChCW,EAA2B,CAACF,EAAW,OAAQC,EAAU,MAAM,EACjEV,EAAI,KAAKW,EAAS,KAAKX,EAAI,IAAI,MAAM,EACrCA,EAAI,QAAQW,EAAS,KAAKX,EAAI,OAAO,MAAM,EAE/C,IAAIY,EACJ,GAAI,CACHA,EAAY,MAAMC,GACjBV,EACA,CACC,WAAAM,EACA,UAAWT,EAAI,UACf,aAAcA,EAAI,aAClB,OAAQA,EAAI,OACZ,MAAOA,EAAI,MACX,UAAAU,EACA,IAAKV,EAAI,IACT,OAAQA,EAAI,OACZ,KAAAM,CACD,EACAK,CACD,CACD,OAASG,EAAO,CACf,OAAA7B,EAAU,EAAE,KAAK,kEAAmE6B,CAAK,EAClF,IACR,CACA,GAAIF,EAAU,SAAWN,EAAK,OAAQ,OAAO,KAE7C,IAAMnB,EAAYV,EAAa,IAAKW,GACnCC,GAAeD,EAAG,CAAE,aAAcY,EAAI,QAAU,KAAM,WAAY1B,EAAK,QAAS,CAAC,CAClF,EAEMgB,EAAuB,CAAC,EAC9B,QAASyB,EAAI,EAAGA,EAAIH,EAAU,OAAQG,IAAK,CAC1C,IAAMC,EAASJ,EAAUG,CAAC,EACpBE,EAAMV,EAAQQ,CAAC,EAEfG,EAAW,IAAU,iBAC3BA,EAAS,aAAa,WAAY,IAAU,kBAAgBF,EAAO,UAAW,CAAC,CAAC,EAChFE,EAAS,aAAa,SAAU,IAAU,kBAAgBF,EAAO,QAAS,CAAC,CAAC,EAC5EE,EAAS,SAAS,IAAU,kBAAgBF,EAAO,QAAS,CAAC,CAAC,EAC1DA,EAAO,KAAKE,EAAS,aAAa,KAAM,IAAU,kBAAgBF,EAAO,IAAK,CAAC,CAAC,EAChFA,EAAO,QACVE,EAAS,aAAa,QAAS,IAAU,kBAAgBF,EAAO,OAAQ,EAAG,EAAI,CAAC,EAGjF,IAAMpB,EACLqB,EAAI,OAAS,SACVE,GAAmBD,EAAUD,EAAI,MAAO9B,CAAS,EACjDiC,GAAmBF,EAAUD,EAAI,SAAWA,EAAI,MAAO9B,CAAS,EACpES,EAAK,SAAS,kBAAoBjB,GAAqB,KACvDW,EAAO,KAAKM,CAAI,CACjB,CAEA,OAAItB,EAAK,OACRW,EAAU,EAAE,MACX,oCAAoCK,EAAO,MAAM,YAAYU,EAAI,UAAU,OAAS,CAAC,YACtF,EAEMV,CACR,CAMA,SAASpB,GAA8BmD,EAAwB,CAC9D,OAAO,KAAK,MAAOA,EAAO,OAAS,EAAK,CAAC,CAC1C,CW1ZO,IAAMC,GAAwC,CAEpD,UAAW,MACX,WAAY,KACZ,QAAS,KACT,YAAa,KACb,YAAa,IACb,WAAY,GACZ,OAAQ,EACR,WAAY,GACZ,YAAa,IACb,WAAY,IACZ,WAAY,IACZ,WAAY,IAEZ,YAAa,QACb,KAAM,OACN,OAAQ,MACR,KAAM,MACN,MAAO,MACP,MAAO,SACP,cAAe,IAChB,EAEMC,GAAyB,UACzBC,GAAqB,eAQ3B,SAASC,GAAkBC,EAAuB,CACjD,IAAMC,EAASD,EAAK,MAAM,GAAG,EAC7B,OAAOC,EAAO,SAASJ,EAAsB,GAAKI,EAAO,SAASH,EAAkB,CACrF,CAGA,IAAMI,GAAqB,IAAI,IAW/B,eAAsBC,GACrBC,EACAC,EAC4B,CAC5B,IAAMC,EAAY,YAAY,IAAI,EAC5BC,EAA4B,CAAC,EAE7B,CACL,aAAAC,EAAe,GAGf,kBAAAC,EAAoB,GACpB,WAAAC,EAAa,IACb,MAAAC,EAAQ,GACR,QAASC,EAAiB,CAAC,CAC5B,EAAIP,GAAW,CAAC,EAEhB,GAAI,CACH,IAAMQ,EAAcL,EAAeM,GAAeV,EAAK,UAAU,EAAI,EACrE,aAAMW,GAAuBX,EAAMG,EAASM,EAAaD,EAAgBD,CAAK,EAE1EF,GACHO,GAAkBT,EAASG,CAAU,EAG/BH,CACR,OAASU,EAAO,CACf,MAAAC,GAAYD,EAAOV,CAAO,EACpBU,CACP,QAAE,CACGN,GACHQ,GAAkBb,CAAS,CAE7B,CACD,CAMA,SAASQ,GAAeM,EAA4B,CACnD,IAAMC,EAASzB,GAAcwB,CAAU,EACvC,OAAIC,IAAW,OACPA,GAEHnB,GAAmB,IAAIkB,CAAU,IACrClB,GAAmB,IAAIkB,CAAU,EACjCE,EAAU,EAAE,KACX,6BAA6BF,CAAU,iEACtB,OAAO,KAAKxB,EAAa,EAAE,KAAK,IAAI,CAAC,GACvD,GAEM,EACR,CAEA,eAAemB,GACdX,EACAG,EACAM,EACAD,EACAD,EACgB,CAChB,QAAWY,KAASnB,EAAK,OAAQ,CAChC,IAAMoB,EAAYD,EAAM,UAExB,QAAWE,KAAQD,EAAW,CAC7B,IAAME,EAASF,EAAUC,CAAI,EACxBC,GAEL,MAAMC,GAAkBD,EAAQnB,EAASM,EAAaD,EAAgBD,CAAK,CAC5E,CACD,CACD,CAGA,eAAegB,GACdD,EACAnB,EACAM,EACAD,EACAD,EACgB,CAChB,QAAWiB,KAAQF,EAAQ,CAC1B,GAAI,CAAC3B,GAAkB6B,EAAK,IAAI,EAAG,SAEnC,IAAMC,EAAuB,CAC5B,gBAAiB,GACjB,MAAO,GACP,GAAGjB,CACJ,EAIMkB,EAAQC,GAAaH,EAAK,IAAI,EACpC,GAAI,CAACE,EAAO,CACXR,EAAU,EAAE,MAAM,oDAAoD,EACtE,QACD,CAEA,IAAMU,EAAc,MAAMC,GAAqBH,EAAOD,CAAoB,EAEpEK,EAAaC,EAAkBL,EAAM,KAAK,EAE1CM,EAAiC,CAAC,GAAGJ,EAAa,GAAGE,CAAU,EAGrE,GAAIrB,IAAgB,EACnB,QAAWwB,KAAOD,EACjBC,EAAI,MAAM,IAAIxB,EAAaA,EAAaA,CAAW,EAIrDN,EAAQ,KAAK,GAAG6B,CAAY,EAExBzB,GACHW,EAAU,EAAE,MACX,aAAaU,EAAY,MAAM,eAAeE,EAAW,MAAM,mBAChE,CAEF,CACD,CAGA,SAASH,GAAa3B,EAAyC,CAC9D,OAAO,OAAOA,GAAS,SAAWkC,GAAUlC,CAAI,EAAKA,CACtD,CAEA,SAASkC,GAAUC,EAAqC,CACvD,GAAI,CACH,OAAO,KAAK,MAAMA,CAAC,CACpB,MAAQ,CACP,MACD,CACD,CAOA,SAASvB,GAAkBwB,EAA0BC,EAA6B,CACjF,GAAID,EAAO,SAAW,EAAG,OAEzB,IAAME,EAAsBC,GAA2BH,CAAM,EAC7DI,GAAYJ,EAAQE,EAAoB,IAAID,CAAI,EAAGA,CAAI,CACxD,CAEA,SAASvB,GAAYD,EAAgBuB,EAAgC,CACpElB,EAAU,EAAE,MAAM,gCAAiCL,CAAK,EACxD4B,GAAcL,CAAM,CACrB,CAEA,SAASK,GAAcL,EAAgC,CACtD,QAAWH,KAAOG,EAAQ,CACzB,IAAMM,EAAOT,EACTS,EAAK,UACRA,EAAK,SAAS,QAAQ,EAGnBA,EAAK,WACJ,MAAM,QAAQA,EAAK,QAAQ,EAC9BA,EAAK,SAAS,QAASC,GAAaA,EAAS,QAAQ,CAAC,EAEtDD,EAAK,SAAS,QAAQ,EAGzB,CACD,CAEA,SAAS3B,GAAkBb,EAAyB,CACnD,IAAM0C,EAAU,YAAY,IAAI,EAAI1C,EACpCgB,EAAU,EAAE,KAAK,0BAA2B,GAAG0B,EAAQ,QAAQ,CAAC,CAAC,IAAI,CACtE","names":["cloneSceneObjects","meshes","root","copy","sources","child","i","source","target","releaseSceneObjects","disposeObjectTree","meshPolicy","Line2","LineGeometry","LineMaterial","THREE","DEFAULT_COLOR","materialParams","color","opacity","resolved","DEFAULT_LINE_WIDTH","MIN_POSITIONS","buildCurveLine","item","positions","curvePositions","geometry","LineGeometry","params","materialParams","material","LineMaterial","styled","line","Line2","VisualizationError","ErrorCodes","THREE","buildPoint","item","position","getLogger","geometry","material","materialParams","points","parseDisplayItems","items","objects","item","line","buildCurveLine","point","buildPoint","unknown","getLogger","THREE","HOST_IS_LITTLE_ENDIAN","inflateSync","toUint8Array","input","decodeBase64ToBinary","maybeDecompress","bytes","view","uncompressedLen","deflated","maxPlausibleLen","fail","out","inflateSync","error","decodeUtf8","VisualizationError","ErrorCodes","readInt16Vertices","buffer","byteOffset","count","copy","readFloat32Vertices","readUint16Array","readUint32Array","validateIndicesInRange","indices","vertexCount","i","unzigzag","zz","decodeDeltaVertices","zigzagged","px","py","pz","decodeDeltaIndices16","prev","decodeDeltaIndices32","message","context","UV_CHUNK_HEADER_BYTES","parseUvChunk","bytes","view","offset","vertexCount","deltaEncoded","fail","uvFormat","originU","originV","scaleU","scaleV","componentCount","useFloat32","dataByteLength","absoluteOffset","uvs","readFloat32Vertices","raw","readUint16Array","qu","qv","i","unzigzag","parseColorChunk","byteLength","colors","r","g","b","parseBinaryMeshBatch","input","raw","parseBinaryMeshBatchRaw","vertices","decodeDeltaVertices","indices","decodeDeltaIndices16","decodeDeltaIndices32","validateIndicesInRange","HOST_IS_LITTLE_ENDIAN","VisualizationError","ErrorCodes","bytes","maybeDecompress","toUint8Array","view","fail","offset","magic","version","metadataLen","metadataBytes","metadata","decodeUtf8","error","flags","originX","originY","originZ","scaleX","scaleY","scaleZ","vertexCount","useFloat32","deltaEncoded","componentCount","verticesByteLength","absoluteOffset","vertexData","readFloat32Vertices","readUint16Array","readInt16Vertices","indexCount","useUint16Indices","indicesByteLength","indexData","readUint32Array","uvs","parsed","parseUvChunk","colors","parseColorChunk","assembleGeometries","input","isFloat32","deltaEncoded","origin","scale","uvs","colors","jobs","unzigzag","zz","worldVertices","quantized","zigzagged","px","py","pz","i","ox","oy","oz","sx","sy","sz","indices","out","prev","totalVertexCount","results","job","vertexTotal","indexTotal","window","positions","outIndices","outUvs","outColors","vertexCursor","indexCursor","componentStart","windowStart","windowEnd","shift","indexValue","normals","a","b","c","cbx","cby","cbz","abx","aby","abz","nx","ny","nz","x","y","z","length","meshAssemblyWorkerSource","ASSEMBLY_WORKER_MIN_TRIANGLES","assemblyWorker","pendingAssemblies","nextAssemblyRequestId","getAssemblyWorker","url","meshAssemblyWorkerSource","worker","event","id","geometries","error","pending","requestAssembly","input","transfer","resolve","reject","THREE","THREE","maxAnisotropy","setTextureAnisotropy","value","observeMaxAnisotropy","applyTextureMap","material","url","texture","error","getLogger","METAL_CLEARCOAT_THRESHOLD","METAL_CLEARCOAT","METAL_CLEARCOAT_ROUGHNESS","createMaterial","matData","options","color","parseColor","vertexColors","appearance","material","applyVertexColorSRGBDecode","applyTextureMap","shader","THREE","metadataFail","message","context","VisualizationError","ErrorCodes","validateGroupMetadata","groups","materialCount","totalVertexCount","totalIndexCount","group","mesh","fields","field","value","indexOutOfWindow","indexValue","meshMeta","dequantizeInt16","q","origin","scale","out","ox","oy","oz","sx","sy","sz","i","createMergedMesh","group","allVertices","allIndices","materials","allUvs","allColors","totalVertexCount","totalIndexCount","meshMeta","mergedVertices","mergedIndices","mergedUvs","mergedColors","vertexWriteCursor","indexWriteCursor","componentStart","componentLen","uvStart","indicesSlice","indexShift","windowStart","windowEnd","i","indexValue","indexOutOfWindow","geometry","finalizeMergedMesh","threeMesh","firstMesh","meshNames","m","name","createIndividualMeshes","meshes","vertices","rebasedIndices","baseIndex","uvs","colors","finalizeSingleMesh","mesh","parseMeshBatchObject","batch","options","telemetry","mergeByMaterial","debug","material","parseTime","perfStart","workerMeshes","tryBuildViaWorker","decodeStart","parsed","parseBinaryMeshBatch","decodeTime","blobBytes","approximateBase64DecodedBytes","buildMeshesFromParsed","parseMeshBatchBlob","blob","opts","materialAppearance","fallback","materialsSrc","groups","sourceComponentId","isFloat32","validateGroupMetadata","worldVertices","dequantizeInt16","wireBytes","getLogger","meshCreateStart","materials","m","createMaterial","meshes","group","mergedMesh","createMergedMesh","individualMeshes","createIndividualMeshes","mesh","meshCreateTime","totalTime","input","raw","parseBinaryMeshBatchRaw","ASSEMBLY_WORKER_MIN_TRIANGLES","worker","getAssemblyWorker","windowOf","jobs","jobRefs","meshMeta","vertexData","indexData","transfer","assembled","requestAssembly","error","i","result","ref","geometry","finalizeMergedMesh","finalizeSingleMesh","base64","SCALE_FACTORS","DISPLAY_COMPONENT_TYPE","DISPLAY_BATCH_TYPE","isDisplayItemType","type","tokens","warnedUnknownUnits","getThreeMeshesFromComputeResponse","data","options","startTime","objects","allowScaling","allowAutoPosition","groundAxis","debug","parsingOptions","scaleFactor","getScaleFactor","extractDisplayFromData","applyGroundOffset","error","handleError","logProcessingTime","modelUnits","factor","getLogger","value","innerTree","path","branch","processDataBranch","item","mergedParsingOptions","batch","extractBatch","batchMeshes","parseMeshBatchObject","batchItems","parseDisplayItems","batchObjects","obj","safeParse","s","meshes","axis","combinedBoundingBox","computeCombinedBoundingBox","applyOffset","disposeMeshes","mesh","material","elapsed"]}
|