@selvajs/visualization 1.6.0-beta.7 → 1.6.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gpu-dispose-Cl3RXm-v.js +355 -0
- package/dist/gpu-dispose-Cl3RXm-v.js.map +1 -0
- package/dist/gpu-dispose-fRI12OCL.cjs +469 -0
- package/dist/gpu-dispose-fRI12OCL.cjs.map +1 -0
- package/dist/gpu-ownership-ADRZBxxs.cjs +102 -0
- package/dist/gpu-ownership-ADRZBxxs.cjs.map +1 -0
- package/dist/gpu-ownership-Bs9l7hrU.js +40 -0
- package/dist/gpu-ownership-Bs9l7hrU.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/merged-draw-CO4rr2Co.js +129 -0
- package/dist/merged-draw-CO4rr2Co.js.map +1 -0
- package/dist/merged-draw-ma_2r4N8.cjs +171 -0
- package/dist/merged-draw-ma_2r4N8.cjs.map +1 -0
- package/dist/parse.cjs +2789 -11
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +8 -2
- package/dist/parse.d.ts +8 -2
- package/dist/parse.js +2780 -11
- package/dist/parse.js.map +1 -1
- package/dist/render.cjs +3706 -28
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +25 -5
- package/dist/render.d.ts +25 -5
- package/dist/render.js +3685 -28
- package/dist/render.js.map +1 -1
- package/dist/scene.cjs +343 -1
- package/dist/scene.cjs.map +1 -1
- package/dist/scene.js +336 -1
- package/dist/scene.js.map +1 -1
- package/dist/{types-P5PyfYYE.d.ts → types-DW_qU4-Y.d.cts} +22 -3
- package/dist/{types-P5PyfYYE.d.cts → types-DW_qU4-Y.d.ts} +22 -3
- package/package.json +2 -2
- package/dist/gpu-dispose-Cestx__L.cjs +0 -2
- package/dist/gpu-dispose-Cestx__L.cjs.map +0 -1
- package/dist/gpu-dispose-gTGxGTL4.js +0 -2
- package/dist/gpu-dispose-gTGxGTL4.js.map +0 -1
- package/dist/gpu-ownership-DcHWbATk.js +0 -2
- package/dist/gpu-ownership-DcHWbATk.js.map +0 -1
- package/dist/gpu-ownership-ilj3JEdn.cjs +0 -2
- package/dist/gpu-ownership-ilj3JEdn.cjs.map +0 -1
- package/dist/merged-draw-CLtLo5Bh.cjs +0 -2
- package/dist/merged-draw-CLtLo5Bh.cjs.map +0 -1
- package/dist/merged-draw-U_8oQ5q9.js +0 -2
- package/dist/merged-draw-U_8oQ5q9.js.map +0 -1
package/dist/parse.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.cjs","names":["VisualizationError","ErrorCodes","disposeObjectTree","THREE","LineGeometry","LineMaterial","Line2","VisualizationError","ErrorCodes","THREE","unhandled","inflateSync","VisualizationError","ErrorCodes","VisualizationError","ErrorCodes","observeMaxAnisotropy","THREE","parseColor","THREE","OWN_ENV_MAP_INTENSITY","lookEnvMapIntensity","VisualizationError","ErrorCodes","THREE","shareGeometry","computeCombinedBoundingBox","require_gpu_ownership","require_gpu_dispose","three","three_addons_lines_Line2_js","three_addons_lines_LineGeometry_js","three_addons_lines_LineMaterial_js","fflate","getNodeBuffer","buf","stripBase64","data","str","decodeBase64ToBinary","base64File","bytes","decodeStripped","Buffer","decoded","binary","i","cloneSceneObjects","meshes","copy","root","sources","child","source","target","releaseSceneObjects","meshPolicy","materialParams","resolved","opacity","color","buildCurveLine","positions","curvePositions","item","geometry","params","material","styled","line","buildPoint","position","points","parseDisplayItems","items","objects","point","unknown","HOST_IS_LITTLE_ENDIAN","toUint8Array","input","isSlvz","slvzInflatedBytes","slvzPayload","uncompressedLen","deflated","maxPlausibleLen","fail","inflateFailure","error","payload","checkInflatedLength","out","maybeDecompress","inflateSlvzAsync","reject","resolve","mismatch","decodeUtf8","readInt16Vertices","count","byteOffset","buffer","readFloat32Vertices","readUint16Array","readUint32Array","validateIndicesInRange","indices","vertexCount","unzigzag","zz","decodeDeltaVertices","zigzagged","px","py","pz","decodePlanarVertices","n","planes","decodePlanarIndices16","prev","decodePlanarIndices32","decodeDeltaIndices16","decodeDeltaIndices32","message","parseUvChunk","offset","uvFormat","view","originU","originV","scaleU","scaleV","componentCount","useFloat32","dataByteLength","absoluteOffset","uvs","planarByteSplit","qu","qv","raw","deltaEncoded","parseColorChunk","byteLength","colors","r","g","b","isSlvmContainer","parseSlvmContainer","chunks","readChunks","geometryBlob","tableBytes","materialsJson","closedFlags","textures","type","parseTable","materials","parseMaterials","version","len","readVarint","value","shift","readStringColumn","mode","result","objectCount","pool","meshCount","curveCount","pointCount","poolCount","vertexCounts","triCounts","runCount","runs","names","layers","attrs","attrCount","a","key","idx","objIndex","effectiveRuns","groups","meshIndex","vertexStart","indexStart","run","meshAttrs","id","ID_ATTR","mesh","verdict","json","slot","TEXTURE_SLOTS","reference","tex","textureToDataUri","mimeLen","mime","step","decodeRawMeshBatch","vertices","parseBinaryMeshBatchRaw","rawInput","container","magic","metadataLen","metadataBytes","metadata","flags","originX","originY","originZ","scaleX","scaleY","scaleZ","verticesByteLength","vertexData","indexCount","useUint16Indices","indicesByteLength","indexData","parsed","isClosedSolid","memberIndexCounts","tableSizeFor","size","tag","canonicalOf","weldVertex","cellX","cellY","cellZ","welded","vertexOf","outStart","outTo","cursor","grown","array","length","memberIsClosed","seen","member","done","magnitude","uniqueVertices","v","start","inverseCell","weldSize","weldMask","cellSlot","h","x","y","z","neighbour","scaled","cell","canonicals","sx","sy","sz","nx","ny","nz","canonical","ownSlot","combo","triangles","c","k","first","end","j","reversed","bEnd","o","ox","oy","oz","volume","ax","ay","az","bx","by","bz","cx","cy","cz","splitCreases","CREASE_COS","cornerCount","triangleCount","orphans","posBits","uvBits","word","bits","mask","slots","merged","other","orphanCount","faceNormals","faceAreas","t","abx","aby","abz","acx","acy","acz","cornerStart","cornersOf","fill","maxDegree","groupOf","groupCount","founders","corner","f","group","extraStart","extra","total","copySlot","outPositions","outUvs","outColors","normals","w","grainTangents","gradU","gradV","e1x","e1y","e1z","e2x","e2y","e2z","qx","qy","qz","du1","du2","dv1","dv2","ux","uy","uz","vx","vy","vz","tangents","along","tx","ty","tz","handedness","finishGeometry","closed","members","m","creased","assembleGeometries","worldVertices","isFloat32","quantized","origin","scale","uint16Indices","totalVertexCount","results","job","jobs","vertexTotal","indexTotal","window","outIndices","vertexCursor","indexCursor","componentStart","windowStart","windowEnd","indexValue","meshAssemblyWorkerSource","assemblyWorker","pendingAssemblies","nextAssemblyRequestId","getAssemblyWorker","url","worker","event","pending","geometries","requestAssembly","transfer","maxAnisotropy","setTextureAnisotropy","applyTexture","mapSize","image","texture","aspect","RECIPES","FIBRE_FADE","finishPatch","recipe","finish","strength","fibres","fb","bands","shader","cullsBackFaces","options","matData","createMaterialPicker","built","materialsSrc","materialId","culled","createMaterial","vertexColors","appearance","applyVertexColorSRGBDecode","addShaderPatch","previous","previousKey","renderer","patch","metadataFail","validateGroupMetadata","materialCount","fields","field","totalIndexCount","indexOutOfWindow","meshMeta","dequantizeInt16","q","splitGroupByLayer","byLayer","layer","bucket","mergeGroupsOf","byMaterial","createMergedMesh","mergedVertices","mergedIndices","mergedUvs","allUvs","mergedColors","allColors","vertexWriteCursor","indexWriteCursor","componentLen","allVertices","uvStart","indicesSlice","allIndices","indexShift","finished","finalizeMergedMesh","geometryFrom","pickMaterial","mergedMembers","threeMesh","firstMesh","meshNames","name","createIndividualMeshes","rebasedIndices","baseIndex","finalizeSingleMesh","MAX_BYTES","entries","totalBytes","getFinishedBatch","jobShape","shapeOf","cacheKey","geometryBytes","entry","sameInts","sameBytes","buildBatch","rest","batch","storeFinishedBatch","existing","release","oldest","clearGeometryCache","shape","stride","hash","shapeHash","words","wa","wb","parseMeshBatchObject","debug","telemetry","decodeStart","assemblyOptions","opened","openBatch","tryBuildViaAssembly","buildMeshesFromParsed","approximateBase64DecodedBytes","parseMeshBatchBlob","perfStart","blob","assembledMeshes","decodeTime","blobBytes","warnedUnmerged","warnIfUnmergedAtScale","mergeByMaterial","materialAppearance","opts","fallback","wireBytes","meshCreateStart","mergeGroups","mergedMesh","individualMeshes","meshCreateTime","totalTime","parseTime","cached","buildFromFinishedCache","reparse","inflated","inflateInWorker","planJobs","meshesFromBuilt","jobRefs","windowOf","assembled","assemble","ownsBuffer","ref","base64","SCALE_FACTORS","isDisplayItemType","tokens","warnedUnknownUnits","getThreeObjectsFromComputeResponse","startTime","parsingOptions","extractDisplayFromData","allowScaling","getScaleFactor","allowAutoPosition","applyGroundOffset","groundAxis","handleError","logProcessingTime","factor","modelUnits","innerTree","path","branch","processDataBranch","scaleFactor","mergedParsingOptions","extractBatch","batchMeshes","batchItems","batchObjects","obj","object","safeParse","s","combinedBoundingBox","axis","disposeMeshes","elapsed"],"sources":["../src/shared/encoding.ts","../src/parse/mesh-policy.ts","../src/parse/display-items/items/appearance.ts","../src/parse/display-items/items/curves.ts","../src/parse/display-items/items/points.ts","../src/parse/display-items/display-items-parser.ts","../src/parse/webdisplay/binary/header.ts","../src/parse/webdisplay/binary/geometry.ts","../src/parse/webdisplay/binary/textures.ts","../src/parse/webdisplay/binary/slvm.ts","../src/parse/webdisplay/binary-parser.ts","../src/parse/webdisplay/closed-solid.ts","../src/parse/webdisplay/crease-normals.ts","../src/parse/webdisplay/grain-tangents.ts","../src/parse/webdisplay/mesh-assembly.ts","../src/parse/webdisplay/batch/assembly-worker.ts","../src/parse/webdisplay/apply-texture.ts","../src/parse/webdisplay/batch/finish.ts","../src/parse/webdisplay/batch/materials.ts","../src/parse/webdisplay/batch/metadata.ts","../src/parse/webdisplay/batch/merge.ts","../src/parse/webdisplay/batch/finished-cache.ts","../src/parse/webdisplay/batch-parser.ts","../src/parse/webdisplay/webdisplay-parser.ts"],"sourcesContent":["// Copied (not imported) from `@selvajs/compute`'s `decodeBase64ToBinary`: a few stable lines don't\n// justify depending on the Rhino.Compute client. Keep the two in sync by hand.\n\nimport { VisualizationError, ErrorCodes } from './errors.js';\n\nfunction getNodeBuffer(): typeof Buffer | undefined {\n\tconst buf = (globalThis as { Buffer?: typeof Buffer }).Buffer;\n\treturn typeof buf === 'function' ? buf : undefined;\n}\n\n/**\n * WHATWG forgiving-base64 normalization without the alphabet check: strip ASCII whitespace, then\n * drop trailing padding where the spec allows it (total length a multiple of 4). `null` when the\n * length can't be base64. The decoders check the alphabet themselves, faster than a regex.\n */\nfunction stripBase64(str: string): string | null {\n\tlet data = str.replace(/[\\t\\n\\f\\r ]/g, '');\n\tif (data.length % 4 === 0) data = data.replace(/={1,2}$/, '');\n\treturn data.length % 4 === 1 ? null : data;\n}\n\n/**\n * @throws {VisualizationError} `ENCODING_ERROR` if invalid, or `INVALID_STATE` if no decoder is\n * available in this environment.\n */\nexport function decodeBase64ToBinary(base64File: string): Uint8Array {\n\tconst data = stripBase64(base64File);\n\tconst bytes = data === null ? null : decodeStripped(data);\n\tif (bytes === null) {\n\t\tthrow new VisualizationError('Invalid base64 input.', ErrorCodes.ENCODING_ERROR, {\n\t\t\tcontext: { inputLength: base64File.length }\n\t\t});\n\t}\n\treturn bytes;\n}\n\n/** Decode {@link stripBase64} output; `null` when a character is outside the alphabet. */\nfunction decodeStripped(data: string): Uint8Array | null {\n\t// Prefer Buffer in Node: faster, and avoids the atob + charCodeAt latin-1 detour.\n\tconst Buffer = getNodeBuffer();\n\tif (Buffer) {\n\t\t// Buffer decodes leniently: `-` and `_` as base64url, a non-ASCII char as its low byte, and\n\t\t// any other stray char is skipped. Rule out the first two; a skip leaves the result short.\n\t\tif (Buffer.byteLength(data, 'utf8') !== data.length) return null;\n\t\tif (data.includes('-') || data.includes('_')) return null;\n\t\tconst decoded = Buffer.from(data, 'base64');\n\t\tif (decoded.length !== Math.floor((data.length * 3) / 4)) return null;\n\t\t// Small Buffer.from results are views over Node's shared 8 KiB pool slab: copy one out, or\n\t\t// it retains the whole slab and leaks unrelated pooled bytes to any consumer touching\n\t\t// `.buffer` (structuredClone, postMessage transfer, etc). A large one owns its memory.\n\t\tconst ownsBuffer = decoded.byteOffset === 0 && decoded.buffer.byteLength === decoded.length;\n\t\treturn ownsBuffer ? new Uint8Array(decoded.buffer, 0, decoded.length) : new Uint8Array(decoded);\n\t}\n\tif (typeof globalThis.atob === 'function') {\n\t\tlet binary: string;\n\t\ttry {\n\t\t\tbinary = globalThis.atob(data);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tconst bytes = new Uint8Array(binary.length);\n\t\tfor (let i = 0; i < binary.length; i++) {\n\t\t\tbytes[i] = binary.charCodeAt(i) & 0xff;\n\t\t}\n\t\treturn bytes;\n\t}\n\n\tthrow new VisualizationError(\n\t\t'Base64 decoding not supported in this environment.',\n\t\tErrorCodes.INVALID_STATE,\n\t\t{ context: { environmentInfo: 'atob or Buffer not available' } }\n\t);\n}\n","// Mesh ownership policy for `@selvajs/solve`'s result memo: `SolveResult<TMesh>` is opaque to the\n// memo, so clone/release are injected here instead. The viewer disposes whatever it last rendered\n// (`clearScene`), so a memo handing out live references would serve a disposed object on the next\n// hit — `clone` copies geometry explicitly (`Object3D.clone()` shares it by reference) but leaves\n// materials shared, since `clearScene` already spares `SHARED_MATERIALS` singletons and recompiling\n// per-mesh materials as shaders is expensive.\n\nimport * as THREE from 'three';\n\nimport { disposeObjectTree } from '../shared/index.js';\n\nexport function cloneSceneObjects(meshes: THREE.Object3D[]): THREE.Object3D[] {\n\treturn meshes.map((root) => {\n\t\tconst copy = root.clone(true);\n\t\tconst sources: THREE.Object3D[] = [];\n\t\troot.traverse((child) => sources.push(child));\n\t\tlet i = 0;\n\t\tcopy.traverse((child) => {\n\t\t\tconst source = sources[i++] as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tconst target = child as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tif (!source.geometry) return;\n\t\t\ttarget.geometry = source.geometry.clone();\n\t\t});\n\t\treturn copy;\n\t});\n}\n\n/** Skips materials — the memo never owns those; see the file header. */\nexport function releaseSceneObjects(meshes: THREE.Object3D[]): void {\n\tmeshes.forEach((root) => disposeObjectTree(root, { materials: false }));\n}\n\n/** Structurally, not nominally, typed as `@selvajs/solve/client`'s `MeshPolicy<THREE.Object3D>` — avoids a dependency on solve. */\nexport const meshPolicy: {\n\tclone(meshes: THREE.Object3D[]): THREE.Object3D[];\n\trelease(meshes: THREE.Object3D[]): void;\n} = {\n\tclone: cloneSceneObjects,\n\trelease: releaseSceneObjects\n};\n","import * as THREE from 'three';\n\nexport const DEFAULT_COLOR = '#ffffff';\n\n/** Opacity < 1 flips `transparent` on. */\nexport function materialParams(\n\tcolor: string | undefined,\n\topacity: number | undefined\n): { color: THREE.Color; transparent: boolean; opacity: number } {\n\tconst resolved = opacity ?? 1;\n\treturn {\n\t\tcolor: new THREE.Color(color ?? DEFAULT_COLOR),\n\t\ttransparent: resolved < 1,\n\t\topacity: resolved\n\t};\n}\n","import { Line2 } from 'three/addons/lines/Line2.js';\nimport { LineGeometry } from 'three/addons/lines/LineGeometry.js';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\n\nimport { ErrorCodes, VisualizationError } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayCurve } from '../types';\n\nconst DEFAULT_LINE_WIDTH = 2;\n\n/** Two vertices — the shortest renderable polyline. */\nconst MIN_POSITIONS = 6;\n\n/**\n * Curves arrive tessellated: the backend sends `points`, this builds the line. Nothing decodes\n * geometry in the browser.\n *\n * Uses `Line2`/`LineMaterial` instead of `THREE.Line`: plain `THREE.Line` is hard-capped at 1px on\n * every major GPU backend, so `item.width` would go unhonoured. `Line2.onBeforeRender` sets\n * `LineMaterial`'s required `resolution`, so no renderer reference is needed here.\n *\n * @throws VisualizationError when the item has no `points` — see {@link curvePositions}.\n */\nexport function buildCurveLine(item: DisplayCurve): Line2 | null {\n\tconst positions = curvePositions(item);\n\tif (!positions) return null;\n\n\tconst geometry = new LineGeometry();\n\tgeometry.setPositions(positions);\n\n\t// @types/three's LineMaterial omits `linewidth`/`transparent`/`opacity` though all exist at runtime.\n\tconst params = materialParams(item.color, item.opacity);\n\tconst material = new LineMaterial({ color: params.color });\n\tconst styled = material as LineMaterial & {\n\t\tlinewidth: number;\n\t\ttransparent: boolean;\n\t\topacity: number;\n\t};\n\tstyled.linewidth = item.width ?? DEFAULT_LINE_WIDTH; // CSS px (worldUnits defaults false)\n\tstyled.transparent = params.transparent;\n\tstyled.opacity = params.opacity;\n\n\tconst line = new Line2(geometry, material);\n\tline.computeLineDistances();\n\tline.name = item.name;\n\tline.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'curve',\n\t\tmetadata: item.metadata\n\t};\n\treturn line;\n}\n\n/**\n * Flat `[x,y,z, …]`, or null for a degenerate curve — one of those can't abort the batch.\n *\n * A curve with no `points` **throws** instead. It means the definition was solved by a Display\n * component predating backend tessellation, which is a stale definition rather than one bad curve:\n * skipping would render a scene silently missing geometry, indistinguishable from a definition that\n * has no curves, with the fix nowhere in sight.\n */\nfunction curvePositions(item: DisplayCurve): number[] | null {\n\tif (!item.points) {\n\t\tthrow new VisualizationError(\n\t\t\t`Curve display item '${item.id}' has no tessellated points. It was produced by an ` +\n\t\t\t\t'outdated Display component — upgrade it in Grasshopper (Solution → Upgrade obsolete ' +\n\t\t\t\t'components) and re-save the definition.',\n\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t{ context: { itemId: item.id, kind: item.kind } }\n\t\t);\n\t}\n\n\treturn item.points.length >= MIN_POSITIONS ? item.points : null;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayPoint } from '../types';\n\nexport function buildPoint(item: DisplayPoint): THREE.Points | null {\n\t// `position` comes off the wire — don't trust the declared type without validating.\n\tconst { position } = item as { position?: { X?: unknown; Y?: unknown; Z?: unknown } };\n\tif (\n\t\t!position ||\n\t\ttypeof position.X !== 'number' ||\n\t\t!Number.isFinite(position.X) ||\n\t\ttypeof position.Y !== 'number' ||\n\t\t!Number.isFinite(position.Y) ||\n\t\ttypeof position.Z !== 'number' ||\n\t\t!Number.isFinite(position.Z)\n\t) {\n\t\tgetLogger().warn(\n\t\t\t`Skipping point display item with missing or non-finite position (id: ${String(item.id)}).`\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute(\n\t\t'position',\n\t\tnew THREE.Float32BufferAttribute([position.X, position.Y, position.Z], 3)\n\t);\n\n\tconst material = new THREE.PointsMaterial({\n\t\t...materialParams(item.color, item.opacity),\n\t\tsize: 6,\n\t\tsizeAttenuation: false\n\t});\n\n\tconst points = new THREE.Points(geometry, material);\n\tpoints.name = item.name;\n\tpoints.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'point',\n\t\tmetadata: item.metadata\n\t};\n\treturn points;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { buildCurveLine } from './items/curves.js';\nimport { buildPoint } from './items/points.js';\n\nimport type { DisplayItem } from './types';\n\n/**\n * Builds THREE objects for the batch's non-mesh items.\n *\n * @throws VisualizationError when a curve predates backend tessellation, so a stale definition\n * surfaces as an actionable error instead of a scene quietly missing its curves. Every other\n * unrenderable item is logged and skipped.\n */\nexport function parseDisplayItems(items: DisplayItem[] | undefined): THREE.Object3D[] {\n\tif (!items || items.length === 0) return [];\n\n\tconst objects: THREE.Object3D[] = [];\n\n\tfor (const item of items) {\n\t\tswitch (item.kind) {\n\t\t\tcase 'curve': {\n\t\t\t\tconst line = buildCurveLine(item);\n\t\t\t\tif (line) objects.push(line);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'point': {\n\t\t\t\tconst point = buildPoint(item);\n\t\t\t\tif (point) objects.push(point);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// Forces a compile error if a new DisplayItem kind is added without a case above.\n\t\t\t\tconst unhandled: never = item;\n\t\t\t\tconst unknown = unhandled as { kind?: string };\n\t\t\t\tgetLogger().warn(`Skipping unknown display item kind: ${String(unknown.kind)}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn objects;\n}\n","import type { MaterialGroup, SerializableMaterial } from '../types.js';\n\n// ============================================================================\n// WIRE FORMAT CONSTANTS\n// ============================================================================\n\n/** \"SLVA\" little-endian — an uncompressed mesh blob. */\nexport const BINARY_MESH_MAGIC = 0x41564c53;\n/**\n * \"SLVZ\" little-endian — an optional raw-DEFLATE container around a SLVA blob (applied by the\n * plugin when it shrinks the payload). Layout: `[4] magic=SLVZ | [4] uncompressedLen(u32) |\n * [N] raw-deflate stream of the SLVA blob`.\n */\nexport const COMPRESSED_MESH_MAGIC = 0x5a564c53;\n/**\n * Current writer version. v2 added FLAG_UINT16_INDICES; v3 added FLAG_DELTA_ENCODED; v4 added\n * FLAG_PLANAR_BYTESPLIT.\n */\nexport const BINARY_MESH_VERSION = 4;\n/**\n * Oldest wire version this parser still decodes. Each version only added a flag bit, so the\n * flag-driven read path handles every older blob unchanged — needed since persisted/cached blobs\n * (saved `.gh` files, `.slvm`/`.dmf` mesh files, cached compute results) must stay decodable after upgrade.\n */\nexport const MIN_SUPPORTED_VERSION = 1;\n/** Bit 0 of the geometry flags word: 0 = int16 quantized, 1 = float32 raw. */\nexport const FLAG_FLOAT32 = 0x1;\n/** Bit 1 of the geometry flags word: 0 = uint32 indices, 1 = uint16 indices. */\nexport const FLAG_UINT16_INDICES = 0x2;\n/**\n * Bit 2 of the geometry flags word: int16 vertex components and indices are stored as wrapped\n * per-component deltas from their predecessor, zigzag-mapped to unsigned (float32 vertices are\n * never filtered). Deltas of welded meshes concentrate near zero, so the SLVZ DEFLATE pass\n * compresses far better. Decoding reverses the filter with a running prefix sum.\n */\nexport const FLAG_DELTA_ENCODED = 0x4;\n/**\n * Bit 3: a UV chunk trails the index block. Layout: `uvFormat(u32: 0=uint16 quantized, 1=float32)\n * | uvOrigin(2×f64) | uvScale(2×f64) | data`, element count implied by vertexCount. Quantized UVs\n * reconstruct as `uv = origin + q * scale` (q unsigned in [0, 65535]), delta+zigzag filtered per\n * component (independent u/v predictors) iff FLAG_DELTA_ENCODED; float32 UVs are never filtered.\n * Absent flag = absent chunk, so untextured blobs are byte-identical to pre-chunk writers.\n */\nexport const FLAG_HAS_UVS = 0x8;\n/**\n * Bit 4: a vertex-color chunk trails the index block (after the UV chunk when both present).\n * Layout: `uint8 rgb[vertexCount*3]`, delta+zigzag filtered per channel (wrapped 8-bit, independent\n * r/g/b predictors) iff FLAG_DELTA_ENCODED.\n */\nexport const FLAG_HAS_VERTEX_COLORS = 0x10;\n\n/**\n * Bit 5 (v4): every delta+zigzag-filtered stream (quantized vertices, indices, quantized UVs —\n * never float32 data, never colors) is stored as byte planes over its element count N instead of\n * interleaved LE values:\n *\n * ```\n * vertices [X-lo × N][Y-lo × N][Z-lo × N][X-hi × N][Y-hi × N][Z-hi × N] N = vertexCount\n * uint16 indices [lo × N][hi × N] N = indexCount\n * uint32 indices [b0 × N][b1 × N][b2 × N][b3 × N] N = indexCount\n * uvs [U-lo × N][V-lo × N][U-hi × N][V-hi × N] N = vertexCount\n * ```\n *\n * Byte lengths and delta/zigzag semantics are identical to the interleaved v3 layout — only byte\n * order within each block changes. Near-zero deltas make the hi planes runs of zeros, so the SLVZ\n * DEFLATE pass compresses 28–50% smaller than interleaved. Colors keep the v3 interleaved layout\n * (planar loses on noisy per-channel data, measured).\n */\nexport const FLAG_PLANAR_BYTESPLIT = 0x20;\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}\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","import { inflate, 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\ninterface SlvzPayload {\n\tdeflated: Uint8Array;\n\tuncompressedLen: number;\n}\n\nfunction isSlvz(bytes: Uint8Array): boolean {\n\treturn (\n\t\tbytes.byteLength >= 8 &&\n\t\tnew DataView(bytes.buffer, bytes.byteOffset, 8).getUint32(0, true) === COMPRESSED_MESH_MAGIC\n\t);\n}\n\n/** Size a SLVZ blob inflates to, as its header declares; 0 for anything else. Unvalidated. */\nexport function slvzInflatedBytes(bytes: Uint8Array): number {\n\treturn isSlvz(bytes) ? new DataView(bytes.buffer, bytes.byteOffset, 8).getUint32(4, true) : 0;\n}\n\nfunction slvzPayload(bytes: Uint8Array): SlvzPayload {\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\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\treturn { deflated, uncompressedLen };\n}\n\nfunction inflateFailure(error: unknown, payload: SlvzPayload): VisualizationError {\n\treturn fail(\n\t\t`Failed to inflate SLVZ blob: ${error instanceof Error ? error.message : String(error)}`,\n\t\t{ uncompressedLen: payload.uncompressedLen, deflatedBytes: payload.deflated.byteLength }\n\t);\n}\n\nfunction checkInflatedLength(out: Uint8Array, payload: SlvzPayload): Uint8Array {\n\tif (out.byteLength !== payload.uncompressedLen) {\n\t\tthrow fail('SLVZ payload inflated to a different size than the header declares.', {\n\t\t\tdeclaredLen: payload.uncompressedLen,\n\t\t\tactualLen: out.byteLength,\n\t\t\tdeflatedBytes: payload.deflated.byteLength\n\t\t});\n\t}\n\treturn out;\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 (!isSlvz(bytes)) return bytes;\n\n\tconst payload = slvzPayload(bytes);\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(payload.deflated, { out: new Uint8Array(payload.uncompressedLen + 1) });\n\t} catch (error) {\n\t\tthrow inflateFailure(error, payload);\n\t}\n\treturn checkInflatedLength(out, payload);\n}\n\n/**\n * {@link maybeDecompress} for a SLVZ blob, inflated in fflate's own worker so the calling thread\n * stays free. The result owns its buffer. Rejects on any failure, including a worker that could not\n * start: the caller falls back to {@link maybeDecompress}, which reports a corrupt blob properly.\n */\nexport function inflateSlvzAsync(bytes: Uint8Array): Promise<Uint8Array> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst payload = slvzPayload(bytes);\n\t\ttry {\n\t\t\t// One byte of slack, as in `maybeDecompress`: fflate truncates output that overruns `size`,\n\t\t\t// which would hide an oversized payload instead of tripping the length check.\n\t\t\tinflate(payload.deflated, { size: payload.uncompressedLen + 1 }, (error, out) => {\n\t\t\t\tif (error) {\n\t\t\t\t\treject(inflateFailure(error, payload));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tresolve(checkInflatedLength(out, payload));\n\t\t\t\t} catch (mismatch) {\n\t\t\t\t\treject(mismatch);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treject(inflateFailure(error, payload));\n\t\t}\n\t});\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\n/**\n * Undoes the v4 planar byte-split vertex layout: six byte planes\n * `[Xlo][Ylo][Zlo][Xhi][Yhi][Zhi]` (each `vertexCount` bytes) of the zigzag deltas — merge the\n * planes, unzigzag, prefix-sum per component. Same delta semantics as {@link decodeDeltaVertices}.\n */\nexport function decodePlanarVertices(planes: Uint8Array, vertexCount: number): Int16Array {\n\tconst out = new Int16Array(vertexCount * 3);\n\tconst n = vertexCount;\n\tlet px = 0;\n\tlet py = 0;\n\tlet pz = 0;\n\tfor (let i = 0; i < n; i++) {\n\t\tpx = ((px + unzigzag(planes[i]! | (planes[n * 3 + i]! << 8))) << 16) >> 16;\n\t\tpy = ((py + unzigzag(planes[n + i]! | (planes[n * 4 + i]! << 8))) << 16) >> 16;\n\t\tpz = ((pz + unzigzag(planes[n * 2 + i]! | (planes[n * 5 + i]! << 8))) << 16) >> 16;\n\t\tout[i * 3] = px;\n\t\tout[i * 3 + 1] = py;\n\t\tout[i * 3 + 2] = pz;\n\t}\n\treturn out;\n}\n\n/** Undoes the v4 planar byte-split uint16 index layout `[lo × N][hi × N]`. */\nexport function decodePlanarIndices16(planes: Uint8Array, count: number): Uint16Array {\n\tconst out = new Uint16Array(count);\n\tlet prev = 0;\n\tfor (let i = 0; i < count; i++) {\n\t\tprev = (prev + unzigzag(planes[i]! | (planes[count + i]! << 8))) & 0xffff;\n\t\tout[i] = prev;\n\t}\n\treturn out;\n}\n\n/** Undoes the v4 planar byte-split uint32 index layout `[b0 × N][b1 × N][b2 × N][b3 × N]`. */\nexport function decodePlanarIndices32(planes: Uint8Array, count: number): Uint32Array {\n\tconst out = new Uint32Array(count);\n\tlet prev = 0;\n\tfor (let i = 0; i < count; i++) {\n\t\tconst zz =\n\t\t\t(planes[i]! |\n\t\t\t\t(planes[count + i]! << 8) |\n\t\t\t\t(planes[count * 2 + i]! << 16) |\n\t\t\t\t(planes[count * 3 + i]! << 24)) >>>\n\t\t\t0;\n\t\tprev = (prev + unzigzag(zz)) >>> 0;\n\t\tout[i] = prev;\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\tplanarByteSplit = false\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 if (planarByteSplit) {\n\t\t// v4 layout: [Ulo][Vlo][Uhi][Vhi], each vertexCount bytes.\n\t\tconst planes = bytes.subarray(offset, offset + dataByteLength);\n\t\tconst n = vertexCount;\n\t\tuvs = new Float32Array(componentCount);\n\t\tlet qu = 0;\n\t\tlet qv = 0;\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tqu = (qu + unzigzag(planes[i]! | (planes[n * 2 + i]! << 8))) & 0xffff;\n\t\t\tqv = (qv + unzigzag(planes[n + i]! | (planes[n * 3 + i]! << 8))) & 0xffff;\n\t\t\tuvs[i * 2] = originU + qu * scaleU;\n\t\t\tuvs[i * 2 + 1] = originV + qv * scaleV;\n\t\t}\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 { decodeUtf8, fail, maybeDecompress } from './geometry.js';\n\nimport type { BinaryMeshMetadata } from './header.js';\nimport type { MaterialGroup, MeshMetadata, SerializableMaterial } from '../types.js';\n\n// ============================================================================\n// SLVM v3 CONTAINER\n// ============================================================================\n// The C# mirror and normative spec is `SlvmDocument.cs`. Layout:\n//\n// [4] magic \"SLVM\" | [4] version = 3 | [4] chunkCount\n// per chunk: [4] fourcc | [4] byteLen | payload | zero pad to 4\n//\n// Unknown chunk types are skipped by length — that's the format's extension mechanism. This\n// decoder consumes GEOM (a nested bare SLVA/SLVZ blob), TABL (the columnar object table), MATL\n// (materials JSON), TEXR (texture bytes) and CLSD (the writer's closed-solid verdict per mesh); CRVS/PNTS (file-only item geometry) and EXTN\n// (host extensions — the selva.gh one carries only Rhino curve JSON) are skipped because\n// items reach the web as JSON alongside the blob.\n\n/** \"SLVM\" little-endian. */\nexport const SLVM_MAGIC = 0x4d564c53;\nexport const SLVM_VERSION = 3;\n\nexport const CHUNK_GEOM = 0x4d4f4547; // \"GEOM\"\nexport const CHUNK_TABL = 0x4c424154; // \"TABL\"\nexport const CHUNK_MATL = 0x4c54414d; // \"MATL\"\nconst CHUNK_TEXR = 0x52584554; // \"TEXR\"\n/** One byte per mesh row: 0 = unknown, 1 = open, 2 = closed solid. */\nconst CHUNK_CLSD = 0x44534c43; // \"CLSD\"\n\n/** Reserved attr key carrying each object's identity — split out of the attr dict on read. */\nconst ID_ATTR = 'id';\n\n/** Material `map` prefix that references a TEXR chunk by index. */\nconst TEX_REF_PREFIX = 'slvm:tex:';\n\n// String-column modes (see SlvmDocument.WriteStringColumn).\nconst NAMES_NONE = 0;\nconst NAMES_SEQUENTIAL = 1;\nconst NAMES_POOL = 2;\n\nexport function isSlvmContainer(bytes: Uint8Array): boolean {\n\treturn (\n\t\tbytes.byteLength >= 4 &&\n\t\tnew DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0, true) === SLVM_MAGIC\n\t);\n}\n\nexport interface SlvmContainer {\n\t/** The GEOM payload: a bare SLVA/SLVZ blob whose embedded metadata is empty. */\n\tgeometryBlob: Uint8Array;\n\t/** Batch metadata reconstructed from TABL + MATL + EXTN, in the legacy envelope shape. */\n\tmetadata: BinaryMeshMetadata;\n}\n\n/**\n * Parses an SLVM v3 container down to its mesh geometry blob and the metadata the rest of the\n * pipeline expects. Group vertex/index windows are rebuilt as prefix sums over the table — the\n * format mandates geometry is concatenated in table order, so starts are never stored.\n *\n * @throws {VisualizationError} On bad magic, unknown version, or a truncated/malformed container.\n */\nexport function parseSlvmContainer(bytes: Uint8Array): SlvmContainer {\n\tconst chunks = readChunks(bytes);\n\n\tlet geometryBlob: Uint8Array | null = null;\n\tlet tableBytes: Uint8Array | null = null;\n\tlet materialsJson: string | null = null;\n\tlet closedFlags: Uint8Array | null = null;\n\tconst textures: Uint8Array[] = [];\n\n\tfor (const { type, payload } of chunks) {\n\t\tswitch (type) {\n\t\t\tcase CHUNK_GEOM:\n\t\t\t\tgeometryBlob = payload;\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_TABL:\n\t\t\t\ttableBytes = maybeDecompress(payload);\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_MATL:\n\t\t\t\tmaterialsJson = decodeUtf8(payload);\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_TEXR:\n\t\t\t\ttextures.push(payload);\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_CLSD:\n\t\t\t\tclosedFlags = maybeDecompress(payload);\n\t\t\t\tbreak;\n\t\t\t// CRVS/PNTS, EXTN and unknown chunks: skipped.\n\t\t}\n\t}\n\n\tif (geometryBlob === null) {\n\t\tthrow fail('SLVM container has no GEOM chunk.', { chunkCount: chunks.length });\n\t}\n\n\tif (tableBytes === null) {\n\t\tthrow fail('SLVM container has no TABL chunk.', { chunkCount: chunks.length });\n\t}\n\n\tconst { groups } = parseTable(tableBytes, closedFlags);\n\tconst materials = parseMaterials(materialsJson, textures);\n\n\treturn {\n\t\tgeometryBlob,\n\t\tmetadata: { materials, groups }\n\t};\n}\n\n// ============================================================================\n// CHUNK STREAM\n// ============================================================================\n\nfunction readChunks(bytes: Uint8Array): { type: number; payload: Uint8Array }[] {\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\tif (bytes.byteLength < 12 || view.getUint32(0, true) !== SLVM_MAGIC) {\n\t\tthrow fail('Not an SLVM container (bad magic).', { byteLength: bytes.byteLength });\n\t}\n\n\tconst version = view.getUint32(4, true);\n\tif (version !== SLVM_VERSION) {\n\t\tthrow fail(`Unsupported SLVM version: ${version}`, { expectedVersion: SLVM_VERSION });\n\t}\n\n\tconst count = view.getUint32(8, true);\n\tconst chunks: { type: number; payload: Uint8Array }[] = [];\n\tlet offset = 12;\n\tfor (let i = 0; i < count; i++) {\n\t\tif (offset + 8 > bytes.byteLength) {\n\t\t\tthrow fail('Truncated SLVM chunk header.', { offset, chunkIndex: i });\n\t\t}\n\n\t\tconst type = view.getUint32(offset, true);\n\t\tconst len = view.getUint32(offset + 4, true);\n\t\toffset += 8;\n\t\tif (offset + len > bytes.byteLength) {\n\t\t\tthrow fail('Truncated SLVM chunk payload.', { offset, chunkIndex: i, byteLen: len });\n\t\t}\n\n\t\tchunks.push({ type, payload: bytes.subarray(offset, offset + len) });\n\t\toffset += len + ((4 - (len % 4)) % 4);\n\t}\n\n\treturn chunks;\n}\n\n// ============================================================================\n// TABL\n// ============================================================================\n\ninterface TableReader {\n\tbytes: Uint8Array;\n\tpos: number;\n}\n\nfunction readVarint(r: TableReader): number {\n\tlet value = 0;\n\tlet shift = 0;\n\tfor (;;) {\n\t\tif (r.pos >= r.bytes.byteLength || shift > 28) {\n\t\t\tthrow fail('Malformed varint in SLVM table.', { pos: r.pos });\n\t\t}\n\n\t\tconst b = r.bytes[r.pos++]!;\n\t\tvalue |= (b & 0x7f) << shift;\n\t\tif ((b & 0x80) === 0) {\n\t\t\treturn value >>> 0;\n\t\t}\n\n\t\tshift += 7;\n\t}\n}\n\nfunction readStringColumn(r: TableReader, objectCount: number, pool: string[]): string[] {\n\tconst mode = r.bytes[r.pos++];\n\tconst result = new Array<string>(objectCount);\n\tswitch (mode) {\n\t\tcase NAMES_NONE:\n\t\t\tresult.fill('');\n\t\t\tbreak;\n\t\tcase NAMES_SEQUENTIAL:\n\t\t\tfor (let i = 0; i < objectCount; i++) {\n\t\t\t\tresult[i] = String(i + 1);\n\t\t\t}\n\n\t\t\tbreak;\n\t\tcase NAMES_POOL:\n\t\t\tfor (let i = 0; i < objectCount; i++) {\n\t\t\t\tresult[i] = pool[readVarint(r)] ?? '';\n\t\t\t}\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow fail(`Unknown SLVM string column mode: ${mode}`, { pos: r.pos });\n\t}\n\n\treturn result;\n}\n\nfunction parseTable(\n\tbytes: Uint8Array,\n\tclosedFlags: Uint8Array | null\n): { groups: MaterialGroup[] } {\n\tconst r: TableReader = { bytes, pos: 0 };\n\n\tconst meshCount = readVarint(r);\n\tconst curveCount = readVarint(r);\n\tconst pointCount = readVarint(r);\n\tconst objectCount = meshCount + curveCount + pointCount;\n\n\tconst poolCount = readVarint(r);\n\tconst pool = new Array<string>(poolCount);\n\tfor (let i = 0; i < poolCount; i++) {\n\t\tconst len = readVarint(r);\n\t\tpool[i] = decodeUtf8(bytes.subarray(r.pos, r.pos + len));\n\t\tr.pos += len;\n\t}\n\n\tconst vertexCounts = new Array<number>(meshCount);\n\tconst triCounts = new Array<number>(meshCount);\n\tfor (let i = 0; i < meshCount; i++) {\n\t\tvertexCounts[i] = readVarint(r);\n\t\ttriCounts[i] = readVarint(r);\n\t}\n\n\tfor (let i = 0; i < curveCount; i++) {\n\t\treadVarint(r); // curve point counts — items don't reach this decoder\n\t}\n\n\tconst runCount = readVarint(r);\n\tconst runs: { materialId: number; meshCount: number }[] = [];\n\tfor (let i = 0; i < runCount; i++) {\n\t\truns.push({ materialId: readVarint(r), meshCount: readVarint(r) });\n\t}\n\n\tconst names = readStringColumn(r, objectCount, pool);\n\tconst layers = readStringColumn(r, objectCount, pool);\n\n\tconst attrs = new Array<Record<string, string> | undefined>(objectCount);\n\tconst attrCount = readVarint(r);\n\tfor (let a = 0; a < attrCount; a++) {\n\t\tconst key = pool[readVarint(r)] ?? '';\n\t\tconst n = readVarint(r);\n\t\tconst indices = new Array<number>(n);\n\t\tlet idx = 0;\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tidx += readVarint(r);\n\t\t\tindices[i] = idx;\n\t\t}\n\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tconst value = pool[readVarint(r)] ?? '';\n\t\t\tconst objIndex = indices[i]!;\n\t\t\t(attrs[objIndex] ??= {})[key] = value;\n\t\t}\n\t}\n\n\t// A table with meshes but no material runs (a foreign writer) gets one implicit group.\n\tconst effectiveRuns = runs.length > 0 || meshCount === 0 ? runs : [{ materialId: 0, meshCount }];\n\n\tconst groups: MaterialGroup[] = [];\n\tlet meshIndex = 0;\n\tlet vertexStart = 0;\n\tlet indexStart = 0;\n\tfor (const run of effectiveRuns) {\n\t\tconst meshes: MeshMetadata[] = [];\n\t\tfor (let i = 0; i < run.meshCount && meshIndex < meshCount; i++, meshIndex++) {\n\t\t\t// The identity rides the attr mechanism but is a first-class field to consumers.\n\t\t\tconst meshAttrs = attrs[meshIndex];\n\t\t\tconst id = meshAttrs?.[ID_ATTR];\n\t\t\tif (meshAttrs !== undefined) {\n\t\t\t\tdelete meshAttrs[ID_ATTR];\n\t\t\t}\n\n\t\t\tconst mesh: MeshMetadata = {\n\t\t\t\tid,\n\t\t\t\tname: names[meshIndex]!,\n\t\t\t\tlayer: layers[meshIndex]!,\n\t\t\t\tvertexCount: vertexCounts[meshIndex]!,\n\t\t\t\tindexCount: triCounts[meshIndex]! * 3,\n\t\t\t\tvertexStart,\n\t\t\t\tindexStart,\n\t\t\t\tmetadata: meshAttrs ?? {}\n\t\t\t};\n\t\t\tconst verdict = closedFlags?.[meshIndex];\n\t\t\tif (verdict === 1 || verdict === 2) mesh.closed = verdict === 2;\n\t\t\tmeshes.push(mesh);\n\t\t\tvertexStart += vertexCounts[meshIndex]!;\n\t\t\tindexStart += triCounts[meshIndex]! * 3;\n\t\t}\n\n\t\tgroups.push({ materialId: run.materialId, meshes });\n\t}\n\n\treturn { groups };\n}\n\n// ============================================================================\n// MATL + TEXR\n// ============================================================================\n\nfunction parseMaterials(json: string | null, textures: Uint8Array[]): SerializableMaterial[] {\n\tif (json === null) {\n\t\treturn [];\n\t}\n\n\tlet materials: SerializableMaterial[];\n\ttry {\n\t\tmaterials = (JSON.parse(json) as { materials?: SerializableMaterial[] }).materials ?? [];\n\t} catch (error) {\n\t\tthrow fail(\n\t\t\t`Failed to parse SLVM materials JSON: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ byteLength: json.length }\n\t\t);\n\t}\n\n\tfor (const material of materials) {\n\t\tfor (const slot of TEXTURE_SLOTS) {\n\t\t\tconst reference = material[slot];\n\t\t\tif (reference?.startsWith(TEX_REF_PREFIX)) {\n\t\t\t\tconst tex = textures[Number(reference.slice(TEX_REF_PREFIX.length))];\n\t\t\t\tif (tex !== undefined) {\n\t\t\t\t\tmaterial[slot] = textureToDataUri(tex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn materials;\n}\n\nconst TEXTURE_SLOTS = ['map', 'roughnessMap', 'normalMap'] as const;\n\n/** TEXR payload is [varint mimeLen][mime utf8][image bytes]; downstream wants a data URI. */\nfunction textureToDataUri(payload: Uint8Array): string {\n\tconst r: TableReader = { bytes: payload, pos: 0 };\n\tconst mimeLen = readVarint(r);\n\tconst mime = decodeUtf8(payload.subarray(r.pos, r.pos + mimeLen));\n\tconst data = payload.subarray(r.pos + mimeLen);\n\n\t// btoa takes a binary string; build it in chunks to stay clear of argument-count limits.\n\tlet binary = '';\n\tconst step = 0x8000;\n\tfor (let i = 0; i < data.byteLength; i += step) {\n\t\tbinary += String.fromCharCode(...data.subarray(i, i + step));\n\t}\n\n\treturn `data:${mime};base64,${btoa(binary)}`;\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_PLANAR_BYTESPLIT,\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\tdecodePlanarIndices16,\n\tdecodePlanarIndices32,\n\tdecodePlanarVertices,\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';\nimport { isSlvmContainer, parseSlvmContainer } from './binary/slvm.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\tFLAG_PLANAR_BYTESPLIT,\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. The field layout and every flag bit are\n * specified in `binary/header.ts`, next to the constants this reads.\n *\n * Returns absolute values: quantized vertices stay int16 (dequantize with `origin + (q + 32767) *\n * scale`, which is what Three.js `BufferAttribute(arr, 3, true)` expects when the per-mesh\n * transform encodes origin and scale), but the delta filter and planar byte-split are both undone\n * here — consumers never see either.\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\treturn decodeRawMeshBatch(parseBinaryMeshBatchRaw(input));\n}\n\n/** Second half of {@link parseBinaryMeshBatch}, for a caller that already holds the raw view. */\nexport function decodeRawMeshBatch(raw: RawBinaryMeshBatch): ParsedBinaryMeshBatch {\n\tlet vertices: Int16Array | Float32Array;\n\tif (raw.isFloat32) {\n\t\tvertices = raw.vertexData as Float32Array;\n\t} else if (raw.planarByteSplit) {\n\t\tvertices = decodePlanarVertices(raw.vertexData as Uint8Array, raw.vertexCount);\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: Uint16Array | Uint32Array;\n\tif (raw.planarByteSplit) {\n\t\tconst planes = raw.indexData as Uint8Array;\n\t\tindices = raw.uint16Indices\n\t\t\t? decodePlanarIndices16(planes, planes.length / 2)\n\t\t\t: decodePlanarIndices32(planes, planes.length / 4);\n\t} else if (raw.deltaEncoded) {\n\t\tindices =\n\t\t\traw.indexData instanceof Uint16Array\n\t\t\t\t? decodeDeltaIndices16(raw.indexData)\n\t\t\t\t: decodeDeltaIndices32(raw.indexData as Uint32Array);\n\t} else {\n\t\tindices = raw.indexData as Uint16Array | Uint32Array;\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/**\n\t * Wire vertex components: byte planes (Uint8) when `planarByteSplit`, zigzag deltas (Uint16)\n\t * when `deltaEncoded` and not float32.\n\t */\n\tvertexData: Uint8Array | Uint16Array | Int16Array | Float32Array;\n\t/**\n\t * Wire indices: byte planes (Uint8) when `planarByteSplit`, else zigzag deltas when\n\t * `deltaEncoded`. NOT validated against vertexCount.\n\t */\n\tindexData: Uint8Array | Uint16Array | Uint32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\t/** v4 byte-plane layout on the delta-filtered streams — see FLAG_PLANAR_BYTESPLIT. */\n\tplanarByteSplit: boolean;\n\t/** Width of the wire indices (needed since planar `indexData` is a bare byte stream). */\n\tuint16Indices: 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 rawInput = toUint8Array(input);\n\n\t// An SLVM v3 container nests a bare SLVA/SLVZ blob as its GEOM chunk and carries the object\n\t// table/materials as binary chunks — unwrap, decode the inner blob through the path below,\n\t// and overlay the container's metadata (the inner blob's own metadata is empty).\n\tif (isSlvmContainer(rawInput)) {\n\t\tconst container = parseSlvmContainer(rawInput);\n\t\tconst raw = parseBinaryMeshBatchRaw(container.geometryBlob);\n\t\traw.metadata = container.metadata;\n\t\treturn raw;\n\t}\n\n\tconst bytes = maybeDecompress(rawInput);\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\t// A blob nested inside an SLVM container carries no metadata of its own (metadataLen = 0);\n\t\t// the container's TABL/MATL/EXTN chunks supply it after this parse returns.\n\t\tmetadata =\n\t\t\tmetadataLen === 0\n\t\t\t\t? ({} as BinaryMeshMetadata)\n\t\t\t\t: (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 planarByteSplit = (flags & FLAG_PLANAR_BYTESPLIT) !== 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: Uint8Array | Uint16Array | Int16Array | Float32Array;\n\tif (useFloat32) {\n\t\tvertexData = readFloat32Vertices(bytes.buffer, absoluteOffset, componentCount);\n\t} else if (planarByteSplit) {\n\t\t// v4 byte planes — no alignment requirement, view the bytes directly.\n\t\tvertexData = bytes.subarray(offset, offset + verticesByteLength);\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 = planarByteSplit\n\t\t? bytes.subarray(offset, offset + indicesByteLength)\n\t\t: useUint16Indices\n\t\t\t? readUint16Array(bytes.buffer, bytes.byteOffset + offset, indexCount)\n\t\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, planarByteSplit);\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\tplanarByteSplit,\n\t\tuint16Indices: useUint16Indices,\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 * True when every member of a geometry is a closed, consistently wound, outward-facing triangle\n * surface, so its back faces can never be seen and the material can cull them.\n *\n * Drawing them anyway is not just overdraw. A thin closed part (a 0.5 mm sheet) puts its inside\n * faces within one depth-buffer step of its outside ones, and once precision drops below the\n * thickness they punch through as dark jagged triangles. Culled, they never compete.\n *\n * Members are tested apart, because two closed parts touching along an edge share it four ways and\n * would read as open together. Positions are welded first: Rhino meshes each Brep face separately,\n * so a solid's seams carry duplicate vertices that only position identifies. Any\n * doubt (an unwelded seam, a non-manifold edge, inward winding) answers false, which keeps the\n * double-sided default.\n *\n * Self-contained (no outer captures besides `Math`) so the mesh-assembly worker can embed it via\n * `Function.prototype.toString`.\n *\n * @param memberIndexCounts Index count of each member, in order; members are concatenated in\n * `indices`.\n */\nexport function isClosedSolid(\n\tpositions: Float32Array,\n\tindices: Uint32Array,\n\tmemberIndexCounts: number[]\n): boolean {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tif (memberIndexCounts.length === 0) return false;\n\n\tconst tableSizeFor = (count: number): number => {\n\t\tlet size = 16;\n\t\twhile (size < count * 2) size *= 2;\n\t\treturn size;\n\t};\n\n\t// Welding works per vertex, not per corner: a vertex is shared by ~6 corners, and welding each\n\t// corner on its own could also map one vertex to two different canonicals. Member m tags a\n\t// vertex 2m+1 once seen and 2m+2 once welded, so `canonicalOf` needs no reset between members.\n\t// Canonicals are numbered per member (0, 1, …), which keeps the edge arrays member-sized.\n\tconst vertexCount = positions.length / 3;\n\tconst tag = new Int32Array(vertexCount);\n\tconst canonicalOf = new Int32Array(vertexCount);\n\n\t// Per-member tables, grown to the largest member and reused: a fresh set per member is about\n\t// 1 MB of zeroed memory each.\n\tlet weldVertex: Int32Array = new Int32Array(0);\n\tlet cellX: Int32Array = new Int32Array(0);\n\tlet cellY: Int32Array = new Int32Array(0);\n\tlet cellZ: Int32Array = new Int32Array(0);\n\tlet welded: Int32Array = new Int32Array(0);\n\tlet vertexOf: Int32Array = new Int32Array(0);\n\tlet outStart: Int32Array = new Int32Array(0);\n\tlet outTo: Int32Array = new Int32Array(0);\n\tlet cursor: Int32Array = new Int32Array(0);\n\tconst grown = (array: Int32Array, length: number): Int32Array =>\n\t\tarray.length >= length ? array : new Int32Array(length);\n\n\tconst memberIsClosed = (member: number, start: number, count: number): boolean => {\n\t\t// A tetrahedron is the smallest closed surface.\n\t\tif (count < 12 || count % 3 !== 0) return false;\n\n\t\tconst seen = 2 * member + 1;\n\t\tconst done = seen + 1;\n\n\t\t// --- Weld: map each vertex to the first vertex at (nearly) its position ------------------\n\t\t// Rhino meshes each Brep face on its own and stores float32 vertices, so the two copies of a\n\t\t// seam point can round one or two float32 steps apart. Snap to a grid two steps wide at the\n\t\t// part's largest coordinate: wide enough for that, far below its real features (sliver\n\t\t// triangles along a fillet run down to ~1 µm, which a wider weld collapses).\n\t\tlet magnitude = 0;\n\t\tlet uniqueVertices = 0;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst v = indices[start + i]!;\n\t\t\tif (tag[v] === seen) continue;\n\t\t\ttag[v] = seen;\n\t\t\tuniqueVertices++;\n\t\t\tmagnitude = Math.max(\n\t\t\t\tmagnitude,\n\t\t\t\tMath.abs(positions[v * 3]!),\n\t\t\t\tMath.abs(positions[v * 3 + 1]!),\n\t\t\t\tMath.abs(positions[v * 3 + 2]!)\n\t\t\t);\n\t\t}\n\t\tif (!(magnitude > 0) || !Number.isFinite(magnitude)) return false;\n\t\tconst step = 2 * Math.pow(2, Math.floor(Math.log2(magnitude)) - 23);\n\t\tconst inverseCell = 1 / step;\n\n\t\tconst weldSize = tableSizeFor(uniqueVertices);\n\t\tconst weldMask = weldSize - 1;\n\t\t// Only `weldVertex` is read before it is written: a cell's coordinates count once its slot is taken.\n\t\tweldVertex = grown(weldVertex, weldSize);\n\t\tweldVertex.fill(-1, 0, weldSize);\n\t\tcellX = grown(cellX, weldSize);\n\t\tcellY = grown(cellY, weldSize);\n\t\tcellZ = grown(cellZ, weldSize);\n\t\tconst cellSlot = (x: number, y: number, z: number): number => {\n\t\t\tlet h = Math.imul(x, 0x9e3779b1) ^ Math.imul(y, 0x85ebca77) ^ Math.imul(z, 0xc2b2ae3d);\n\t\t\th = (h ^ (h >>> 15)) & weldMask;\n\t\t\twhile (weldVertex[h] !== -1 && (cellX[h] !== x || cellY[h] !== y || cellZ[h] !== z)) {\n\t\t\t\th = (h + 1) & weldMask;\n\t\t\t}\n\t\t\treturn h;\n\t\t};\n\t\t// The cell across the nearer boundary, for a point close enough to it that its near-copy could\n\t\t// have rounded into the other cell.\n\t\tconst neighbour = (scaled: number, cell: number): number => {\n\t\t\tconst offset = scaled - cell;\n\t\t\treturn offset > 0.25 ? cell + 1 : offset < -0.25 ? cell - 1 : cell;\n\t\t};\n\n\t\twelded = grown(welded, count);\n\t\t// The vertex each canonical stands for, to read its position.\n\t\tvertexOf = grown(vertexOf, uniqueVertices);\n\t\tlet canonicals = 0;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst v = indices[start + i]!;\n\t\t\tif (tag[v] === done) {\n\t\t\t\twelded[i] = canonicalOf[v]!;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst sx = positions[v * 3]! * inverseCell;\n\t\t\tconst sy = positions[v * 3 + 1]! * inverseCell;\n\t\t\tconst sz = positions[v * 3 + 2]! * inverseCell;\n\t\t\tconst x = Math.round(sx);\n\t\t\tconst y = Math.round(sy);\n\t\t\tconst z = Math.round(sz);\n\t\t\tconst nx = neighbour(sx, x);\n\t\t\tconst ny = neighbour(sy, y);\n\t\t\tconst nz = neighbour(sz, z);\n\n\t\t\tlet canonical = -1;\n\t\t\tlet ownSlot = -1;\n\t\t\tfor (let combo = 0; combo < 8 && canonical === -1; combo++) {\n\t\t\t\tif ((combo & 1 && nx === x) || (combo & 2 && ny === y) || (combo & 4 && nz === z)) continue;\n\t\t\t\tconst h = cellSlot(combo & 1 ? nx : x, combo & 2 ? ny : y, combo & 4 ? nz : z);\n\t\t\t\tif (combo === 0) ownSlot = h;\n\t\t\t\tif (weldVertex[h] !== -1) canonical = weldVertex[h]!;\n\t\t\t}\n\t\t\tif (canonical === -1) {\n\t\t\t\t// Nothing matched, so the table is as the first probe left it: that slot is still free.\n\t\t\t\tconst h = ownSlot;\n\t\t\t\tcanonical = canonicals++;\n\t\t\t\tweldVertex[h] = canonical;\n\t\t\t\tcellX[h] = x;\n\t\t\t\tcellY[h] = y;\n\t\t\t\tcellZ[h] = z;\n\t\t\t\tvertexOf[canonical] = v;\n\t\t\t}\n\t\t\ttag[v] = done;\n\t\t\tcanonicalOf[v] = canonical;\n\t\t\twelded[i] = canonical;\n\t\t}\n\n\t\t// --- Directed edges: each must occur once, and its reverse once -------------------------\n\t\t// That is exactly \"every edge joins two triangles that agree on winding\", i.e. closed and\n\t\t// consistently oriented. Each canonical's outgoing edges sit together, so both checks scan\n\t\t// its handful of neighbours rather than hashing every edge.\n\t\toutStart = grown(outStart, canonicals + 1);\n\t\toutStart.fill(0, 0, canonicals + 1);\n\t\tlet triangles = 0;\n\t\tfor (let i = 0; i < count; i += 3) {\n\t\t\tconst a = welded[i]!;\n\t\t\tconst b = welded[i + 1]!;\n\t\t\tconst c = welded[i + 2]!;\n\t\t\t// Collapsed by welding (or authored that way): no area, nothing to see from either side.\n\t\t\tif (a === b || b === c || c === a) continue;\n\t\t\toutStart[a + 1] += 1;\n\t\t\toutStart[b + 1] += 1;\n\t\t\toutStart[c + 1] += 1;\n\t\t\ttriangles++;\n\t\t}\n\t\tif (triangles < 4) return false;\n\t\tfor (let k = 0; k < canonicals; k++) outStart[k + 1] += outStart[k]!;\n\t\toutTo = grown(outTo, outStart[canonicals]!);\n\t\tcursor = grown(cursor, canonicals);\n\t\tcursor.set(outStart.subarray(0, canonicals));\n\t\tfor (let i = 0; i < count; i += 3) {\n\t\t\tconst a = welded[i]!;\n\t\t\tconst b = welded[i + 1]!;\n\t\t\tconst c = welded[i + 2]!;\n\t\t\tif (a === b || b === c || c === a) continue;\n\t\t\toutTo[cursor[a]!] = b;\n\t\t\tcursor[a] += 1;\n\t\t\toutTo[cursor[b]!] = c;\n\t\t\tcursor[b] += 1;\n\t\t\toutTo[cursor[c]!] = a;\n\t\t\tcursor[c] += 1;\n\t\t}\n\t\tfor (let a = 0; a < canonicals; a++) {\n\t\t\tconst first = outStart[a]!;\n\t\t\tconst end = outStart[a + 1]!;\n\t\t\tfor (let k = first; k < end; k++) {\n\t\t\t\tconst b = outTo[k]!;\n\t\t\t\tfor (let j = first; j < k; j++) {\n\t\t\t\t\tif (outTo[j] === b) return false; // second use of one directed edge\n\t\t\t\t}\n\t\t\t\tlet reversed = false;\n\t\t\t\tfor (let j = outStart[b]!, bEnd = outStart[b + 1]!; j < bEnd && !reversed; j++) {\n\t\t\t\t\treversed = outTo[j] === a;\n\t\t\t\t}\n\t\t\t\tif (!reversed) return false;\n\t\t\t}\n\t\t}\n\n\t\t// --- Outward: counter-clockwise winding seen from outside encloses positive volume -------\n\t\tconst o = vertexOf[welded[0]!]! * 3;\n\t\tconst ox = positions[o]!;\n\t\tconst oy = positions[o + 1]!;\n\t\tconst oz = positions[o + 2]!;\n\t\tlet volume = 0;\n\t\tfor (let i = 0; i < count; i += 3) {\n\t\t\tconst a = vertexOf[welded[i]!]! * 3;\n\t\t\tconst b = vertexOf[welded[i + 1]!]! * 3;\n\t\t\tconst c = vertexOf[welded[i + 2]!]! * 3;\n\t\t\tconst ax = positions[a]! - ox;\n\t\t\tconst ay = positions[a + 1]! - oy;\n\t\t\tconst az = positions[a + 2]! - oz;\n\t\t\tconst bx = positions[b]! - ox;\n\t\t\tconst by = positions[b + 1]! - oy;\n\t\t\tconst bz = positions[b + 2]! - oz;\n\t\t\tconst cx = positions[c]! - ox;\n\t\t\tconst cy = positions[c + 1]! - oy;\n\t\t\tconst cz = positions[c + 2]! - oz;\n\t\t\tvolume += ax * (by * cz - bz * cy) + ay * (bz * cx - bx * cz) + az * (bx * cy - by * cx);\n\t\t}\n\t\treturn volume > 0;\n\t};\n\n\tlet start = 0;\n\tfor (let member = 0; member < memberIndexCounts.length; member++) {\n\t\tconst count = memberIndexCounts[member]!;\n\t\tif (!memberIsClosed(member, start, count)) return false;\n\t\tstart += count;\n\t}\n\treturn true;\n}\n","export interface CreasedGeometry {\n\tpositions: Float32Array;\n\tnormals: Float32Array;\n\tindices: Uint32Array;\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\n/**\n * Vertex normals that stay smooth across gentle curvature and break at creases, splitting a vertex\n * wherever the faces sharing it disagree by more than the crease angle.\n *\n * The writer welds coincident vertices, and seams between Brep faces often come out welded\n * across a 90° fold. A plain average there tilts every normal along the fold halfway round it,\n * and the long thin triangles of a sheet-metal strip carry that tilt metres across a flat face:\n * streaks, an X around every hole, a whole strip shaded darker than its neighbour.\n *\n * Face normals are area-weighted: across a tangent bend the bend's facets are narrow, so they barely\n * tilt the wide flat face beside them. Weighting by corner angle instead let a bend facet's corner\n * tilt a flat band by up to 23°, enough to flip a metal's reflection from sky to ground.\n *\n * `indices` is rewritten in place to point split corners at their new vertices, which take the\n * slots of welded-away duplicates before any are appended; the index count never changes, so index\n * windows stay valid.\n *\n * Self-contained (no outer captures besides `Math`) so the mesh-assembly worker can embed it via\n * `Function.prototype.toString`.\n */\nexport function splitCreases(\n\tpositions: Float32Array,\n\tindices: Uint32Array,\n\tuvs: Float32Array | null,\n\tcolors: Uint8Array | null\n): CreasedGeometry {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\t// Faces further apart than this split their shared vertex. Above a coarse render mesh's step\n\t// around a fillet (Rhino's defaults stay under 20°), below any real fold.\n\tconst CREASE_COS = Math.cos((30 * Math.PI) / 180);\n\n\tconst vertexCount = positions.length / 3;\n\tconst cornerCount = indices.length;\n\tconst triangleCount = cornerCount / 3;\n\n\t// --- Weld identical vertices first ------------------------------------------------------------\n\t// Which seams arrive welded is the writer's call, and it isn't consistent: it compares normals\n\t// at the seam within a tolerance, so two identical strips can come out one welded, one split\n\t// along the same tangent bend, and then reflect visibly differently. Welding every identical\n\t// vertex (position, UV and colour alike) here makes the crease angle the only thing that\n\t// decides. The orphaned duplicates are reused below as slots for split copies.\n\tlet orphans = new Uint32Array(0);\n\t{\n\t\tconst posBits = new Uint32Array(positions.buffer, positions.byteOffset, positions.length);\n\t\tconst uvBits = uvs ? new Uint32Array(uvs.buffer, uvs.byteOffset, uvs.length) : null;\n\t\t// -0 and +0 are one position with two bit patterns.\n\t\tconst word = (bits: Uint32Array, i: number) => (bits[i] === 0x80000000 ? 0 : bits[i]!);\n\t\tlet size = 16;\n\t\twhile (size < vertexCount * 2) size *= 2;\n\t\tconst mask = size - 1;\n\t\tconst slots = new Int32Array(size).fill(-1);\n\t\tconst canonical = new Uint32Array(vertexCount);\n\t\tlet merged = false;\n\t\tfor (let v = 0; v < vertexCount; v++) {\n\t\t\tconst x = word(posBits, v * 3);\n\t\t\tconst y = word(posBits, v * 3 + 1);\n\t\t\tconst z = word(posBits, v * 3 + 2);\n\t\t\tlet h = Math.imul(x, 0x9e3779b1) ^ Math.imul(y, 0x85ebca77) ^ Math.imul(z, 0xc2b2ae3d);\n\t\t\th = (h ^ (h >>> 15)) & mask;\n\t\t\tfor (;;) {\n\t\t\t\tconst other = slots[h]!;\n\t\t\t\tif (other === -1) {\n\t\t\t\t\tslots[h] = v;\n\t\t\t\t\tcanonical[v] = v;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tword(posBits, other * 3) === x &&\n\t\t\t\t\tword(posBits, other * 3 + 1) === y &&\n\t\t\t\t\tword(posBits, other * 3 + 2) === z &&\n\t\t\t\t\t(!colors ||\n\t\t\t\t\t\t(colors[other * 3] === colors[v * 3] &&\n\t\t\t\t\t\t\tcolors[other * 3 + 1] === colors[v * 3 + 1] &&\n\t\t\t\t\t\t\tcolors[other * 3 + 2] === colors[v * 3 + 2])) &&\n\t\t\t\t\t(!uvBits ||\n\t\t\t\t\t\t(uvBits[other * 2] === uvBits[v * 2] && uvBits[other * 2 + 1] === uvBits[v * 2 + 1]))\n\t\t\t\t) {\n\t\t\t\t\tcanonical[v] = other;\n\t\t\t\t\tmerged = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\th = (h + 1) & mask;\n\t\t\t}\n\t\t}\n\t\tif (merged) {\n\t\t\tfor (let i = 0; i < cornerCount; i++) indices[i] = canonical[indices[i]!]!;\n\t\t\tlet orphanCount = 0;\n\t\t\tfor (let v = 0; v < vertexCount; v++) if (canonical[v] !== v) orphanCount++;\n\t\t\torphans = new Uint32Array(orphanCount);\n\t\t\tfor (let v = 0, k = 0; v < vertexCount; v++) if (canonical[v] !== v) orphans[k++] = v;\n\t\t}\n\t}\n\n\t// --- Unit face normals and areas ---------------------------------------------------------------\n\tconst faceNormals = new Float32Array(triangleCount * 3);\n\tconst faceAreas = new Float32Array(triangleCount);\n\tfor (let t = 0; t < triangleCount; t++) {\n\t\tconst a = indices[t * 3]! * 3;\n\t\tconst b = indices[t * 3 + 1]! * 3;\n\t\tconst c = indices[t * 3 + 2]! * 3;\n\t\tconst abx = positions[b]! - positions[a]!;\n\t\tconst aby = positions[b + 1]! - positions[a + 1]!;\n\t\tconst abz = positions[b + 2]! - positions[a + 2]!;\n\t\tconst acx = positions[c]! - positions[a]!;\n\t\tconst acy = positions[c + 1]! - positions[a + 1]!;\n\t\tconst acz = positions[c + 2]! - positions[a + 2]!;\n\t\tconst nx = aby * acz - abz * acy;\n\t\tconst ny = abz * acx - abx * acz;\n\t\tconst nz = abx * acy - aby * acx;\n\t\tconst length = Math.sqrt(nx * nx + ny * ny + nz * nz);\n\t\tif (length === 0) continue; // degenerate: no normal, no weight\n\t\tfaceNormals[t * 3] = nx / length;\n\t\tfaceNormals[t * 3 + 1] = ny / length;\n\t\tfaceNormals[t * 3 + 2] = nz / length;\n\t\tfaceAreas[t] = length;\n\t}\n\n\t// --- Corners grouped by vertex (CSR) ----------------------------------------------------------\n\tconst cornerStart = new Uint32Array(vertexCount + 1);\n\tfor (let i = 0; i < cornerCount; i++) cornerStart[indices[i]! + 1] += 1;\n\tfor (let v = 0; v < vertexCount; v++) cornerStart[v + 1] += cornerStart[v]!;\n\tconst cornersOf = new Uint32Array(cornerCount);\n\tconst fill = cornerStart.slice(0, vertexCount);\n\tfor (let i = 0; i < cornerCount; i++) {\n\t\tconst v = indices[i]!;\n\t\tcornersOf[fill[v]!] = i;\n\t\tfill[v] += 1;\n\t}\n\n\t// --- Group each vertex's corners by face normal -----------------------------------------------\n\t// A corner joins the first group whose founding normal is within the crease angle. Degenerate\n\t// faces (zero normal) join group 0.\n\tlet maxDegree = 0;\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tmaxDegree = Math.max(maxDegree, cornerStart[v + 1]! - cornerStart[v]!);\n\t}\n\tconst groupOf = new Uint32Array(cornerCount);\n\tconst groupCount = new Uint32Array(vertexCount);\n\tconst founders = new Float32Array(Math.max(1, maxDegree) * 3);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tlet groups = 0;\n\t\tfor (let k = cornerStart[v]!; k < cornerStart[v + 1]!; k++) {\n\t\t\tconst corner = cornersOf[k]!;\n\t\t\tconst f = Math.floor(corner / 3) * 3;\n\t\t\tconst nx = faceNormals[f]!;\n\t\t\tconst ny = faceNormals[f + 1]!;\n\t\t\tconst nz = faceNormals[f + 2]!;\n\t\t\tlet group = -1;\n\t\t\tif (nx === 0 && ny === 0 && nz === 0) {\n\t\t\t\tgroup = 0;\n\t\t\t} else {\n\t\t\t\tfor (let g = 0; g < groups; g++) {\n\t\t\t\t\tconst dot = founders[g * 3]! * nx + founders[g * 3 + 1]! * ny + founders[g * 3 + 2]! * nz;\n\t\t\t\t\tif (dot >= CREASE_COS) {\n\t\t\t\t\t\tgroup = g;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (group === -1) {\n\t\t\t\tgroup = groups;\n\t\t\t\tfounders[groups * 3] = nx;\n\t\t\t\tfounders[groups * 3 + 1] = ny;\n\t\t\t\tfounders[groups * 3 + 2] = nz;\n\t\t\t\tgroups++;\n\t\t\t}\n\t\t\tgroupOf[corner] = group;\n\t\t}\n\t\tgroupCount[v] = Math.max(1, groups);\n\t}\n\n\t// --- Split: group 0 keeps the vertex, later groups get copies -------------------------------\n\t// Copies fill the weld's orphaned slots first and only then append. Appending every copy left\n\t// a box scatter at 400k vertices for 240k used, a third of every attribute unreferenced.\n\tconst extraStart = new Uint32Array(vertexCount + 1);\n\tfor (let v = 0; v < vertexCount; v++) extraStart[v + 1] = extraStart[v]! + groupCount[v]! - 1;\n\tconst extra = extraStart[vertexCount]!;\n\tconst total = vertexCount + Math.max(0, extra - orphans.length);\n\tconst copySlot = new Uint32Array(extra);\n\tfor (let k = 0; k < extra; k++) {\n\t\tcopySlot[k] = k < orphans.length ? orphans[k]! : vertexCount + k - orphans.length;\n\t}\n\n\tlet outPositions = positions;\n\tlet outUvs = uvs;\n\tlet outColors = colors;\n\tif (extra > 0) {\n\t\toutPositions = new Float32Array(total * 3);\n\t\toutPositions.set(positions);\n\t\toutUvs = uvs ? new Float32Array(total * 2) : null;\n\t\tif (outUvs && uvs) outUvs.set(uvs);\n\t\toutColors = colors ? new Uint8Array(total * 3) : null;\n\t\tif (outColors && colors) outColors.set(colors);\n\t\tfor (let v = 0; v < vertexCount; v++) {\n\t\t\tfor (let g = 1; g < groupCount[v]!; g++) {\n\t\t\t\tconst copy = copySlot[extraStart[v]! + g - 1]!;\n\t\t\t\toutPositions[copy * 3] = positions[v * 3]!;\n\t\t\t\toutPositions[copy * 3 + 1] = positions[v * 3 + 1]!;\n\t\t\t\toutPositions[copy * 3 + 2] = positions[v * 3 + 2]!;\n\t\t\t\tif (outUvs && uvs) {\n\t\t\t\t\toutUvs[copy * 2] = uvs[v * 2]!;\n\t\t\t\t\toutUvs[copy * 2 + 1] = uvs[v * 2 + 1]!;\n\t\t\t\t}\n\t\t\t\tif (outColors && colors) {\n\t\t\t\t\toutColors[copy * 3] = colors[v * 3]!;\n\t\t\t\t\toutColors[copy * 3 + 1] = colors[v * 3 + 1]!;\n\t\t\t\t\toutColors[copy * 3 + 2] = colors[v * 3 + 2]!;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst normals = new Float32Array(total * 3);\n\tfor (let t = 0; t < triangleCount; t++) {\n\t\tconst w = faceAreas[t]!;\n\t\tconst nx = faceNormals[t * 3]! * w;\n\t\tconst ny = faceNormals[t * 3 + 1]! * w;\n\t\tconst nz = faceNormals[t * 3 + 2]! * w;\n\t\tfor (let corner = t * 3; corner < t * 3 + 3; corner++) {\n\t\t\tlet target = indices[corner]!;\n\t\t\tconst g = groupOf[corner]!;\n\t\t\tif (g !== 0) {\n\t\t\t\ttarget = copySlot[extraStart[target]! + g - 1]!;\n\t\t\t\tindices[corner] = target;\n\t\t\t}\n\t\t\tnormals[target * 3] += nx;\n\t\t\tnormals[target * 3 + 1] += ny;\n\t\t\tnormals[target * 3 + 2] += nz;\n\t\t}\n\t}\n\tfor (let i = 0; i < normals.length; i += 3) {\n\t\tconst x = normals[i]!;\n\t\tconst y = normals[i + 1]!;\n\t\tconst z = normals[i + 2]!;\n\t\tconst length = Math.sqrt(x * x + y * y + z * z) || 1;\n\t\tnormals[i] = x / length;\n\t\tnormals[i + 1] = y / length;\n\t\tnormals[i + 2] = z / length;\n\t}\n\n\treturn { positions: outPositions, normals, indices, uvs: outUvs, colors: outColors };\n}\n","/**\n * Per-vertex tangents for three's `tangent` attribute: xyz along increasing U, w the handedness that\n * points `cross(normal, tangent)` along increasing V.\n *\n * Without the attribute three rebuilds the frame per pixel from screen-space UV derivatives, which\n * is noisy on long thin triangles. three's own `computeTangents()` needs V to vary on every\n * triangle and leaves a zero tangent where it doesn't (a tube centred on its grain axis has constant\n * V), and a zero tangent renders NaN-black. Only U's gradient is needed here, so V can be anything.\n *\n * Self-contained (no outer captures besides `Math`) so the mesh-assembly worker can embed it via\n * `Function.prototype.toString`.\n */\nexport function grainTangents(\n\tpositions: Float32Array,\n\tnormals: Float32Array,\n\tindices: Uint32Array,\n\tuvs: Float32Array\n): Float32Array {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tconst vertexCount = positions.length / 3;\n\tconst gradU = new Float32Array(vertexCount * 3);\n\tconst gradV = new Float32Array(vertexCount * 3);\n\n\t// Area-weighted gradients of U and V over each triangle. With e1, e2 the edges from corner a and\n\t// n = e1 × e2, a linear f has ∇f = (df1 (e2 × n) + df2 (n × e1)) / |n|²; times the area |n| / 2.\n\tfor (let t = 0; t < indices.length; t += 3) {\n\t\tconst a = indices[t]!;\n\t\tconst b = indices[t + 1]!;\n\t\tconst c = indices[t + 2]!;\n\t\tconst e1x = positions[b * 3]! - positions[a * 3]!;\n\t\tconst e1y = positions[b * 3 + 1]! - positions[a * 3 + 1]!;\n\t\tconst e1z = positions[b * 3 + 2]! - positions[a * 3 + 2]!;\n\t\tconst e2x = positions[c * 3]! - positions[a * 3]!;\n\t\tconst e2y = positions[c * 3 + 1]! - positions[a * 3 + 1]!;\n\t\tconst e2z = positions[c * 3 + 2]! - positions[a * 3 + 2]!;\n\t\tconst nx = e1y * e2z - e1z * e2y;\n\t\tconst ny = e1z * e2x - e1x * e2z;\n\t\tconst nz = e1x * e2y - e1y * e2x;\n\t\tconst length = Math.sqrt(nx * nx + ny * ny + nz * nz);\n\t\tif (length === 0) continue;\n\t\t// e2 × n and n × e1, pre-divided by |n| for the area weighting.\n\t\tconst px = (e2y * nz - e2z * ny) / length;\n\t\tconst py = (e2z * nx - e2x * nz) / length;\n\t\tconst pz = (e2x * ny - e2y * nx) / length;\n\t\tconst qx = (ny * e1z - nz * e1y) / length;\n\t\tconst qy = (nz * e1x - nx * e1z) / length;\n\t\tconst qz = (nx * e1y - ny * e1x) / length;\n\t\tconst du1 = uvs[b * 2]! - uvs[a * 2]!;\n\t\tconst du2 = uvs[c * 2]! - uvs[a * 2]!;\n\t\tconst dv1 = uvs[b * 2 + 1]! - uvs[a * 2 + 1]!;\n\t\tconst dv2 = uvs[c * 2 + 1]! - uvs[a * 2 + 1]!;\n\t\tconst ux = du1 * px + du2 * qx;\n\t\tconst uy = du1 * py + du2 * qy;\n\t\tconst uz = du1 * pz + du2 * qz;\n\t\tconst vx = dv1 * px + dv2 * qx;\n\t\tconst vy = dv1 * py + dv2 * qy;\n\t\tconst vz = dv1 * pz + dv2 * qz;\n\t\tfor (let k = 0; k < 3; k++) {\n\t\t\tconst v = indices[t + k]!;\n\t\t\tgradU[v * 3] += ux;\n\t\t\tgradU[v * 3 + 1] += uy;\n\t\t\tgradU[v * 3 + 2] += uz;\n\t\t\tgradV[v * 3] += vx;\n\t\t\tgradV[v * 3 + 1] += vy;\n\t\t\tgradV[v * 3 + 2] += vz;\n\t\t}\n\t}\n\n\tconst tangents = new Float32Array(vertexCount * 4);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tconst nx = normals[v * 3]!;\n\t\tconst ny = normals[v * 3 + 1]!;\n\t\tconst nz = normals[v * 3 + 2]!;\n\t\t// Into the vertex's tangent plane: the averaged gradient leans off it on curved surfaces.\n\t\tconst along = gradU[v * 3]! * nx + gradU[v * 3 + 1]! * ny + gradU[v * 3 + 2]! * nz;\n\t\tlet tx = gradU[v * 3]! - along * nx;\n\t\tlet ty = gradU[v * 3 + 1]! - along * ny;\n\t\tlet tz = gradU[v * 3 + 2]! - along * nz;\n\t\tlet length = Math.sqrt(tx * tx + ty * ty + tz * tz);\n\t\tif (!(length > 1e-12)) {\n\t\t\t// U constant here: any direction in the plane beats a zero tangent.\n\t\t\tconst ax = Math.abs(nx) < 0.9 ? 1 : 0;\n\t\t\tconst ay = 1 - ax;\n\t\t\ttx = ay * nz;\n\t\t\tty = -ax * nz;\n\t\t\ttz = ax * ny - ay * nx;\n\t\t\tlength = Math.sqrt(tx * tx + ty * ty + tz * tz) || 1;\n\t\t}\n\t\ttx /= length;\n\t\tty /= length;\n\t\ttz /= length;\n\t\tconst bx = ny * tz - nz * ty;\n\t\tconst by = nz * tx - nx * tz;\n\t\tconst bz = nx * ty - ny * tx;\n\t\tconst handedness = bx * gradV[v * 3]! + by * gradV[v * 3 + 1]! + bz * gradV[v * 3 + 2]!;\n\t\ttangents[v * 4] = tx;\n\t\ttangents[v * 4 + 1] = ty;\n\t\ttangents[v * 4 + 2] = tz;\n\t\ttangents[v * 4 + 3] = handedness < 0 ? -1 : 1;\n\t}\n\treturn tangents;\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, and slices/rebases per-geometry\n * windows. {@link finishGeometry} then adds normals and the closed-solid flag.\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\nimport { isClosedSolid } from './closed-solid.js';\nimport { splitCreases, type CreasedGeometry } from './crease-normals.js';\nimport { grainTangents } from './grain-tangents.js';\n\nexport interface AssemblyWindow {\n\tvertexStart: number;\n\tvertexCount: number;\n\tindexStart: number;\n\tindexCount: number;\n\t/** The writer's closed-solid verdict for this member, when it sent one. */\n\tclosed?: boolean;\n}\n\nexport interface AssemblyJob {\n\tkind: 'merged' | 'single';\n\twindows: AssemblyWindow[];\n}\n\nexport interface AssemblyInput {\n\t/**\n\t * Raw wire vertex components: byte planes (Uint8) when planar-byte-split, zigzag deltas\n\t * (Uint16) when delta-encoded, else absolute.\n\t */\n\tvertexData: Uint8Array | Uint16Array | Int16Array | Float32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\t/** v4 byte-plane layout on the delta-filtered streams — see FLAG_PLANAR_BYTESPLIT. */\n\tplanarByteSplit: boolean;\n\t/** Wire index width; required because planar `indexData` is a bare byte stream. */\n\tuint16Indices: boolean;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\t/** Raw wire indices: byte planes when planar-byte-split, zigzag deltas when delta-encoded. */\n\tindexData: Uint8Array | 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\tindices: Uint32Array;\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\nexport interface FinishedGeometry extends CreasedGeometry {\n\t/** See {@link isClosedSolid}. */\n\tclosed: boolean;\n\t/** See {@link grainTangents}. Present exactly when `uvs` is. */\n\ttangents: Float32Array | null;\n}\n\n/**\n * The shared tail of both parse paths, so the worker and the main-thread fallback can't drift.\n * Closedness is judged before the crease split, which rewires corners to new vertex slots.\n * Tangents come after it, so each split copy takes only its own side's faces.\n */\nexport function finishGeometry(\n\tgeometry: AssembledGeometry,\n\tmembers: { indexCount: number; closed?: boolean }[]\n): FinishedGeometry {\n\t// The writer's verdicts, when every member has one: computing it was the costliest finish step.\n\tconst closed = members.every((m) => typeof m.closed === 'boolean')\n\t\t? members.length > 0 && members.every((m) => m.closed)\n\t\t: isClosedSolid(\n\t\t\t\tgeometry.positions,\n\t\t\t\tgeometry.indices,\n\t\t\t\tmembers.map((m) => m.indexCount)\n\t\t\t);\n\tconst creased = splitCreases(geometry.positions, geometry.indices, geometry.uvs, geometry.colors);\n\tconst tangents = creased.uvs\n\t\t? grainTangents(creased.positions, creased.normals, creased.indices, creased.uvs)\n\t\t: null;\n\treturn { ...creased, closed, tangents };\n}\n\nexport function assembleGeometries(input: AssemblyInput): AssembledGeometry[] {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tconst {\n\t\tisFloat32,\n\t\tdeltaEncoded,\n\t\tplanarByteSplit,\n\t\tuint16Indices,\n\t\torigin,\n\t\tscale,\n\t\tuvs,\n\t\tcolors,\n\t\tjobs\n\t} = 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 (planarByteSplit) {\n\t\t\t// v4 layout: [Xlo][Ylo][Zlo][Xhi][Yhi][Zhi] byte planes of the zigzag deltas.\n\t\t\tconst planes = input.vertexData as Uint8Array;\n\t\t\tconst n = planes.length / 6;\n\t\t\tquantized = new Int16Array(n * 3);\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 < n; i++) {\n\t\t\t\tpx = ((px + unzigzag(planes[i] | (planes[n * 3 + i] << 8))) << 16) >> 16;\n\t\t\t\tpy = ((py + unzigzag(planes[n + i] | (planes[n * 4 + i] << 8))) << 16) >> 16;\n\t\t\t\tpz = ((pz + unzigzag(planes[n * 2 + i] | (planes[n * 5 + i] << 8))) << 16) >> 16;\n\t\t\t\tquantized[i * 3] = px;\n\t\t\t\tquantized[i * 3 + 1] = py;\n\t\t\t\tquantized[i * 3 + 2] = pz;\n\t\t\t}\n\t\t} else if (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 (planarByteSplit) {\n\t\tconst planes = input.indexData as Uint8Array;\n\t\tif (uint16Indices) {\n\t\t\tconst count = planes.length / 2;\n\t\t\tconst out = new Uint16Array(count);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tprev = (prev + unzigzag(planes[i] | (planes[count + i] << 8))) & 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 count = planes.length / 4;\n\t\t\tconst out = new Uint32Array(count);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tconst zz =\n\t\t\t\t\t(planes[i] |\n\t\t\t\t\t\t(planes[count + i] << 8) |\n\t\t\t\t\t\t(planes[count * 2 + i] << 16) |\n\t\t\t\t\t\t(planes[count * 3 + i] << 24)) >>>\n\t\t\t\t\t0;\n\t\t\t\tprev = (prev + unzigzag(zz)) >>> 0;\n\t\t\t\tout[i] = prev;\n\t\t\t}\n\t\t\tindices = out;\n\t\t}\n\t} else if (deltaEncoded) {\n\t\tconst zigzagged = input.indexData as Uint16Array | Uint32Array;\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 as Uint16Array | Uint32Array;\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\tresults.push({\n\t\t\tpositions,\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} and {@link finishGeometry} off the main thread.\n * Protocol: receives `{id, input}`, replies `{id, geometries}` (each a {@link FinishedGeometry})\n * with every output buffer transferred, or `{id, error}`. Pinned by a test that evals this source\n * against a stub `self`.\n */\nexport function meshAssemblyWorkerSource(): string {\n\treturn [\n\t\t`const assemble = ${assembleGeometries.toString()};`,\n\t\t`const isClosedSolid = ${isClosedSolid.toString()};`,\n\t\t`const splitCreases = ${splitCreases.toString()};`,\n\t\t`const grainTangents = ${grainTangents.toString()};`,\n\t\t`self.onmessage = (event) => {`,\n\t\t` const { id, input } = event.data;`,\n\t\t` try {`,\n\t\t// Mirrors finishGeometry, which can't be stringified: it captures the functions above.\n\t\t` const geometries = assemble(input).map((g, i) => {`,\n\t\t` const windows = input.jobs[i].windows;`,\n\t\t` const known = windows.every((w) => typeof w.closed === 'boolean');`,\n\t\t` const closed = known`,\n\t\t` ? windows.length > 0 && windows.every((w) => w.closed)`,\n\t\t` : isClosedSolid(g.positions, g.indices, windows.map((w) => w.indexCount));`,\n\t\t` const c = splitCreases(g.positions, g.indices, g.uvs, g.colors);`,\n\t\t` const tangents = c.uvs ? grainTangents(c.positions, c.normals, c.indices, c.uvs) : null;`,\n\t\t` return { ...c, closed, tangents };`,\n\t\t` });`,\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.tangents) transfer.push(g.tangents.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 { FinishedGeometry } from '../mesh-assembly.js';\n\n/**\n * Below this triangle count the synchronous path finishes in ~10 ms — a worker round-trip isn't\n * 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: FinishedGeometry[]) => 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?: FinishedGeometry[];\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<FinishedGeometry[]> {\n\treturn new Promise<FinishedGeometry[]>((resolve, reject) => {\n\t\tconst id = nextAssemblyRequestId++;\n\t\tpendingAssemblies.set(id, { resolve, reject });\n\t\tworker.postMessage({ id, input }, transfer);\n\t});\n}\n","import * as THREE from 'three';\n\nimport { getLogger, observeMaxAnisotropy } from '../../shared/index.js';\n\n/**\n * Anisotropic-filtering samples applied to material textures, keeping them 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\nexport type TextureSlot = 'map' | 'roughnessMap' | 'normalMap';\n\n/**\n * Assigns a texture to `material[slot]` once fetched and decoded — the mesh renders without it for\n * the first frames. Load failures log a warning and leave the slot empty rather than breaking the\n * 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 applyTexture(\n\tmaterial: THREE.MeshPhysicalMaterial,\n\turl: string,\n\tslot: TextureSlot,\n\tmapSize?: number\n): 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); roughness and normal maps\n\t\t\t// are data and must stay linear.\n\t\t\ttexture.colorSpace = slot === 'map' ? THREE.SRGBColorSpace : THREE.NoColorSpace;\n\t\t\ttexture.anisotropy = maxAnisotropy;\n\t\t\tif (mapSize) {\n\t\t\t\t// UVs are in mm: one repeat per mapSize across, the image's aspect down.\n\t\t\t\tconst image = texture.image as { width?: number; height?: number } | undefined;\n\t\t\t\tconst aspect = image?.width && image.height ? image.height / image.width : 1;\n\t\t\t\ttexture.wrapS = THREE.RepeatWrapping;\n\t\t\t\ttexture.wrapT = THREE.RepeatWrapping;\n\t\t\t\ttexture.repeat.set(1 / mapSize, 1 / (mapSize * aspect));\n\t\t\t}\n\t\t\tmaterial[slot] = 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 ${slot} ${url}:`, error);\n\t\t}\n\t);\n}\n","import type * as THREE from 'three';\n\nimport type { SerializableMaterial } from '../types.js';\n\nexport type Finish = NonNullable<SerializableMaterial['finish']>;\n\nexport type ShaderPatch = (shader: THREE.WebGLProgramParametersWithUniforms) => void;\n\n/**\n * Scratch fibres across the grain: `pitch` mm apart, each segment `length` mm long (a range: real\n * brushing is many short fibres, not lines running the whole part), `depth` how far one scales\n * roughness, brightness by half, and tilts the normal across the grain so single fibres glint.\n */\ninterface Fibres {\n\tpitch: number;\n\tlength: [min: number, max: number];\n\tdepth: number;\n}\n\n/** Soft streaks along the grain, as on rolled sheet. Not on brushed: any band longer than its fibres read as full-height streaks on a profile wall. */\ninterface Bands {\n\twidth: number;\n\tlength: number;\n\tdepth: number;\n}\n\n// Starting points, set by eye against photos of brushed stainless and mill-finish aluminium: tune\n// against photos of real sheet, never against another render.\nconst RECIPES: Record<Finish, { fibres: Fibres[]; bands: Bands[] }> = {\n\tbrushed: {\n\t\tfibres: [\n\t\t\t{ pitch: 0.04, length: [2, 12], depth: 0.3 },\n\t\t\t{ pitch: 0.25, length: [5, 40], depth: 0.18 }\n\t\t],\n\t\tbands: []\n\t},\n\trolled: {\n\t\tfibres: [{ pitch: 0.3, length: [80, 600], depth: 0.07 }],\n\t\tbands: [{ width: 20, length: 1500, depth: 0.05 }]\n\t}\n};\n\n// Radians of normal tilt per unit fibre depth.\nconst TILT = 0.6;\n\n// Per doubling of cell width, unresolved fibres keep 2^-FIBRE_FADE of their spread. A true average\n// is 0.5; slower keeps the few deep scratches that still read at wall distance in photos.\nconst FIBRE_FADE = 0.35;\n\nconst f = (n: number) => n.toFixed(4);\n\n/** The shader patch for a finish, or null when it would draw nothing. `key` extends the program cache key. */\nexport function finishPatch(\n\tfinish: Finish,\n\tstrength: number\n): { key: string; patch: ShaderPatch } | null {\n\tconst recipe = RECIPES[finish];\n\tif (!recipe || !(strength > 0)) return null;\n\n\tconst fibres =\n\t\trecipe.fibres\n\t\t\t.map(\n\t\t\t\t(fb, i) =>\n\t\t\t\t\t`selvaFibres( vUv, footprint, ${f(fb.pitch)}, vec2( ${f(fb.length[0])}, ${f(fb.length[1])} ), ${f(fb.depth)}, ${f(i * 31 + 3)} )`\n\t\t\t)\n\t\t\t.join(' + ') || '0.0';\n\tconst bands =\n\t\trecipe.bands\n\t\t\t.map(\n\t\t\t\t(b) => `selvaBand( vUv, footprint, vec2( ${f(b.length)}, ${f(b.width)} ), ${f(b.depth)} )`\n\t\t\t)\n\t\t\t.join(' + ') || '0.0';\n\n\tconst patch: ShaderPatch = (shader) => {\n\t\tshader.fragmentShader = shader.fragmentShader\n\t\t\t.replace(\n\t\t\t\t'#include <common>',\n\t\t\t\t`#include <common>\n\t\t\t\t// Hash without sine: sin() loses precision on the large arguments mm UVs produce.\n\t\t\t\tfloat selvaHash( vec2 p ) {\n\t\t\t\t\tvec3 p3 = fract( vec3( p.xyx ) * 0.1031 );\n\t\t\t\t\tp3 += dot( p3, p3.yzx + 33.33 );\n\t\t\t\t\treturn fract( ( p3.x + p3.y ) * p3.z );\n\t\t\t\t}\n\t\t\t\tfloat selvaNoise( vec2 p ) {\n\t\t\t\t\tvec2 i = floor( p );\n\t\t\t\t\tvec2 f = fract( p );\n\t\t\t\t\tvec2 s = f * f * ( 3.0 - 2.0 * f );\n\t\t\t\t\treturn mix(\n\t\t\t\t\t\tmix( selvaHash( i ), selvaHash( i + vec2( 1.0, 0.0 ) ), s.x ),\n\t\t\t\t\t\tmix( selvaHash( i + vec2( 0.0, 1.0 ) ), selvaHash( i + vec2( 1.0, 1.0 ) ), s.x ),\n\t\t\t\t\t\ts.y\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// One row of fibres per pitch, hard-edged across the grain, each fibre its own depth and\n\t\t\t\t// tapered at both ends. A depth per row instead drew every row as one streak the height\n\t\t\t\t// of the part. depth * |depth| keeps most fibres faint and a few deep.\n\t\t\t\tfloat selvaFibreRow( vec2 uv, float pitch, vec2 len, float seed ) {\n\t\t\t\t\tfloat row = floor( uv.y / pitch );\n\t\t\t\t\tfloat l = mix( len.x, len.y, selvaHash( vec2( row, seed + 1.0 ) ) );\n\t\t\t\t\tfloat along = uv.x / l + selvaHash( vec2( row, seed + 2.0 ) );\n\t\t\t\t\tfloat seg = floor( along );\n\t\t\t\t\tfloat depth = selvaHash( vec2( row, seed + 3.0 + 1.618 * seg ) ) * 2.0 - 1.0;\n\t\t\t\t\tfloat t = along - seg;\n\t\t\t\t\treturn depth * abs( depth ) * smoothstep( 0.0, 0.2, t ) * smoothstep( 1.0, 0.8, t );\n\t\t\t\t}\n\t\t\t\t// Below pixel size a cell 2^n rows wide stands for the rows it covers, at a reduced\n\t\t\t\t// spread: distance keeps the grain's texture without the shimmer of sampling single\n\t\t\t\t// fibres. Two levels blend so nothing pops while zooming.\n\t\t\t\tfloat selvaFibres( vec2 uv, float footprint, float pitch, vec2 len, float depth, float seed ) {\n\t\t\t\t\tfloat level = clamp( log2( footprint / pitch ) + 0.5, 0.0, 14.0 );\n\t\t\t\t\tfloat l0 = floor( level );\n\t\t\t\t\tfloat a = selvaFibreRow( uv, pitch * exp2( l0 ), len, seed + 7.0 * l0 ) * exp2( -${f(FIBRE_FADE)} * l0 );\n\t\t\t\t\tfloat b = selvaFibreRow( uv, pitch * exp2( l0 + 1.0 ), len, seed + 7.0 * l0 + 7.0 )\n\t\t\t\t\t\t* exp2( -${f(FIBRE_FADE)} * ( l0 + 1.0 ) );\n\t\t\t\t\treturn depth * mix( a, b, level - l0 );\n\t\t\t\t}\n\t\t\t\tfloat selvaBand( vec2 uv, float footprint, vec2 size, float depth ) {\n\t\t\t\t\tfloat fade = 1.0 - smoothstep( 0.5, 1.0, footprint / size.y );\n\t\t\t\t\treturn depth * fade * ( selvaNoise( uv / size ) * 2.0 - 1.0 );\n\t\t\t\t}`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <color_fragment>',\n\t\t\t\t`#include <color_fragment>\n\t\t\t\tfloat footprint = max( fwidth( vUv.y ), 1e-6 );\n\t\t\t\tfloat selvaFibre = ( ${fibres} ) * ${f(strength)};\n\t\t\t\tfloat selvaStreak = selvaFibre + ( ${bands} ) * ${f(strength)};\n\t\t\t\tdiffuseColor.rgb *= 1.0 + 0.5 * selvaStreak;`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <roughnessmap_fragment>',\n\t\t\t\t`#include <roughnessmap_fragment>\n\t\t\t\troughnessFactor = clamp( roughnessFactor * ( 1.0 + selvaStreak ), 0.0, 1.0 );`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <normal_fragment_maps>',\n\t\t\t\t`#include <normal_fragment_maps>\n\t\t\t\t#ifdef USE_ANISOTROPY\n\t\t\t\t\tvec3 selvaAcross = tbn[ 1 ];\n\t\t\t\t#else\n\t\t\t\t\t// V's direction from screen derivatives, as three's getTangentFrame.\n\t\t\t\t\tvec3 selvaQ0 = dFdx( - vViewPosition );\n\t\t\t\t\tvec3 selvaQ1 = dFdy( - vViewPosition );\n\t\t\t\t\tvec3 selvaAcross = cross( selvaQ1, normal ) * dFdx( vUv ).y + cross( normal, selvaQ0 ) * dFdy( vUv ).y;\n\t\t\t\t\tselvaAcross *= inversesqrt( max( dot( selvaAcross, selvaAcross ), 1e-12 ) );\n\t\t\t\t#endif\n\t\t\t\tnormal = normalize( normal + ${f(TILT)} * selvaFibre * selvaAcross );`\n\t\t\t);\n\t};\n\n\treturn { key: `finish:${finish}:${f(strength)}`, patch };\n}\n","import * as THREE from 'three';\n\nimport { OWN_ENV_MAP_INTENSITY, lookEnvMapIntensity, parseColor } from '../../../shared/index.js';\n\nimport { applyTexture } from '../apply-texture.js';\nimport { finishPatch, type ShaderPatch } from './finish.js';\n\nimport type { MaterialAppearanceOptions, SerializableMaterial } from '../types.js';\n\n// Pushes surfaces back by a fraction of their own depth slope so crease edges keep their full width:\n// an edge line is a screen-space quad at the edge's depth, and without this the face on its near\n// side covers half of it. The same push lets anything just behind a grazing surface show through,\n// and that gap grows with distance. Measured on a 1 mm sheet with parts 1 mm behind it: 0.5 bled\n// from ~2 m, 0.25 from ~5 m with crease lines barely thinner, 0 only past ~10 m but halved lines.\nconst SURFACE_OFFSET_FACTOR = 0.25;\n\ninterface MaterialOptions {\n\tvertexColors?: boolean;\n\thasUvs?: boolean;\n\tappearance?: MaterialAppearanceOptions;\n\t/** Every mesh drawn with this material is a closed solid: see `isClosedSolid`. */\n\tclosed?: boolean;\n}\n\n// Transparent solids keep both sides: their far wall is meant to show through the near one.\nfunction cullsBackFaces(matData: SerializableMaterial, options?: MaterialOptions): boolean {\n\tif (options?.appearance?.cullBackfaces) return true;\n\treturn (options?.closed ?? false) && !matData.transparent && matData.opacity >= 1;\n}\n\n/**\n * Builds each wire material on first use, once per side it's needed on. `side` is per material, so\n * one material shared by closed solids and open surfaces becomes two instances.\n */\nexport type MaterialPicker = (materialId: number, closed: boolean) => THREE.Material;\n\nexport function createMaterialPicker(\n\tmaterialsSrc: SerializableMaterial[],\n\toptions: Omit<MaterialOptions, 'closed'>\n): MaterialPicker {\n\tconst built = new Map<string, THREE.Material>();\n\treturn (materialId, closed) => {\n\t\tconst matData = materialsSrc[materialId]!;\n\t\tconst culled = cullsBackFaces(matData, { ...options, closed });\n\t\tconst key = `${materialId}:${culled}`;\n\t\tlet material = built.get(key);\n\t\tif (!material) {\n\t\t\tmaterial = createMaterial(matData, { ...options, closed: culled });\n\t\t\tbuilt.set(key, material);\n\t\t}\n\t\treturn material;\n\t};\n}\n\nexport function createMaterial(\n\tmatData: SerializableMaterial,\n\toptions?: MaterialOptions\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// Open surfaces need both sides; a closed solid's back faces are never visible and only\n\t\t// z-fight with its front ones.\n\t\tside: cullsBackFaces(matData, options) ? THREE.FrontSide : THREE.DoubleSide,\n\t\tpolygonOffset: true,\n\t\tpolygonOffsetFactor: SURFACE_OFFSET_FACTOR,\n\t\tpolygonOffsetUnits: 0,\n\t\tdepthWrite: true,\n\t\tdepthTest: true\n\t});\n\n\t// The material's own value beats the look's; otherwise three's default (1) unless the look dials it.\n\tif (matData.envMapIntensity != null) {\n\t\tmaterial.envMapIntensity = matData.envMapIntensity;\n\t\tmaterial.userData[OWN_ENV_MAP_INTENSITY] = matData.envMapIntensity;\n\t} else if (appearance?.envMapIntensity != null) {\n\t\tmaterial.envMapIntensity = lookEnvMapIntensity(appearance.envMapIntensity, matData.metalness);\n\t}\n\n\tif (matData.clearcoat != null) {\n\t\tmaterial.clearcoat = matData.clearcoat;\n\t\tmaterial.clearcoatRoughness = matData.clearcoatRoughness ?? 0;\n\t}\n\n\t// The anisotropy tangent comes from the UVs; without them three normalizes a zero vector and the\n\t// mesh renders NaN-black.\n\tif (matData.anisotropy != null && matData.anisotropy > 0 && options?.hasUvs) {\n\t\tmaterial.anisotropy = matData.anisotropy;\n\t\tmaterial.anisotropyRotation = matData.anisotropyRotation ?? 0;\n\t}\n\n\tif (matData.transmission != null) {\n\t\tmaterial.transmission = matData.transmission;\n\t\t// A pane, not a lens: no thickness, so nothing behind it refracts sideways.\n\t\tmaterial.thickness = 0;\n\t}\n\tif (matData.ior != null) material.ior = matData.ior;\n\n\tif (vertexColors) {\n\t\tapplyVertexColorSRGBDecode(material);\n\t}\n\n\tconst finish =\n\t\tmatData.finish && options?.hasUvs\n\t\t\t? finishPatch(matData.finish, matData.finishStrength ?? 1)\n\t\t\t: null;\n\tif (finish) {\n\t\t// `vUv` exists only with a map or anisotropy; a finish needs it either way.\n\t\tmaterial.defines = { ...material.defines, USE_UV: '' };\n\t\taddShaderPatch(material, finish.key, finish.patch);\n\t}\n\n\t// Async; the mesh renders without them until each image decodes.\n\tconst { mapSize } = matData;\n\tif (matData.map) applyTexture(material, matData.map, 'map', mapSize);\n\tif (matData.roughnessMap) applyTexture(material, matData.roughnessMap, 'roughnessMap', mapSize);\n\tif (matData.normalMap) applyTexture(material, matData.normalMap, 'normalMap', mapSize);\n\n\treturn material;\n}\n\n/**\n * Chains `patch` after any earlier one. three caches compiled programs by `customProgramCacheKey`,\n * which defaults to the `onBeforeCompile` source; a chained wrapper's source is the same whatever\n * it wraps, so every patch must extend the key or two materials would share one program.\n */\nfunction addShaderPatch(material: THREE.Material, key: string, patch: ShaderPatch): void {\n\tconst previous = material.onBeforeCompile;\n\tconst previousKey = material.customProgramCacheKey.bind(material);\n\tmaterial.onBeforeCompile = (shader, renderer) => {\n\t\tprevious.call(material, shader, renderer);\n\t\tpatch(shader);\n\t};\n\tmaterial.customProgramCacheKey = () => `${previousKey()}|${key}`;\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\taddShaderPatch(material, 'srgb-vertex-colors', (shader) => {\n\t\tshader.vertexShader = shader.vertexShader.replace(\n\t\t\t'#include <color_vertex>',\n\t\t\t`#include <color_vertex>\n\t\t\t#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA )\n\t\t\t\tvColor.rgb = mix(\n\t\t\t\t\tvColor.rgb / 12.92,\n\t\t\t\t\tpow( ( vColor.rgb + 0.055 ) / 1.055, vec3( 2.4 ) ),\n\t\t\t\t\tstep( vec3( 0.04045 ), vColor.rgb )\n\t\t\t\t);\n\t\t\t#endif`\n\t\t);\n\t});\n}\n","import { VisualizationError, ErrorCodes } from '../../../shared/index.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\nexport function metadataFail(\n\tmessage: string,\n\tcontext: Record<string, unknown>\n): VisualizationError {\n\treturn new VisualizationError(message, ErrorCodes.VALIDATION_ERROR, { context });\n}\n\n/**\n * Validates the batch's group/mesh metadata against the decoded geometry buffers before any of it\n * is used arithmetically. Throws on the first inconsistency — out-of-range `materialId`,\n * non-integer or negative offsets/counts, or a vertex/index window that overruns the buffers — so\n * malformed or version-skewed metadata fails the parse loudly instead of corrupting the render.\n */\nexport function validateGroupMetadata(\n\tgroups: MaterialGroup[],\n\tmaterialCount: number,\n\ttotalVertexCount: number,\n\ttotalIndexCount: number\n): void {\n\tfor (const group of groups) {\n\t\tif (\n\t\t\t!Number.isInteger(group.materialId) ||\n\t\t\tgroup.materialId < 0 ||\n\t\t\tgroup.materialId >= materialCount\n\t\t) {\n\t\t\tthrow metadataFail('Group materialId out of range of the materials array.', {\n\t\t\t\tmaterialId: group.materialId,\n\t\t\t\tmaterialCount\n\t\t\t});\n\t\t}\n\n\t\tfor (const mesh of group.meshes) {\n\t\t\tconst fields = {\n\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\tindexCount: mesh.indexCount\n\t\t\t};\n\t\t\tfor (const [field, value] of Object.entries(fields)) {\n\t\t\t\tif (!Number.isInteger(value) || value < 0) {\n\t\t\t\t\tthrow metadataFail(`Mesh metadata field \"${field}\" must be a non-negative integer.`, {\n\t\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mesh.vertexStart + mesh.vertexCount > totalVertexCount) {\n\t\t\t\tthrow metadataFail('Mesh vertex window exceeds the batch vertex buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\t\ttotalVertexCount\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (mesh.indexStart + mesh.indexCount > totalIndexCount) {\n\t\t\t\tthrow metadataFail('Mesh index window exceeds the batch index buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\t\tindexCount: mesh.indexCount,\n\t\t\t\t\ttotalIndexCount\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Error for an index outside its mesh's declared vertex window\n * `[vertexStart, vertexStart + vertexCount)`. Rebasing (`index - vertexStart`) writes into an\n * unsigned array, so an out-of-window index would otherwise wrap to ~4 billion and corrupt the\n * geometry. Range checks live inline in the copy loops (a function call per index measured\n * noticeably slower at millions of indices) — this only builds the failure.\n */\nexport function indexOutOfWindow(indexValue: number, meshMeta: MeshMetadata): VisualizationError {\n\treturn metadataFail(\"Index references a vertex outside its mesh's vertex window.\", {\n\t\tmeshName: meshMeta.name,\n\t\tindexValue,\n\t\tvertexStart: meshMeta.vertexStart,\n\t\tvertexCount: meshMeta.vertexCount\n\t});\n}\n\n/**\n * Reconstructs world-unit float32 positions from int16 quantized values:\n * `world = origin + (q + 32767) * scale`. No rotation — the Three scene uses Rhino's Z-up frame,\n * so vertices pass through as they arrived.\n */\nexport function dequantizeInt16(\n\tq: Int16Array,\n\torigin: [number, number, number],\n\tscale: [number, number, number]\n): Float32Array {\n\tconst out = new Float32Array(q.length);\n\tconst ox = origin[0];\n\tconst oy = origin[1];\n\tconst oz = origin[2];\n\tconst sx = scale[0];\n\tconst sy = scale[1];\n\tconst sz = scale[2];\n\n\tfor (let i = 0; i < q.length; i += 3) {\n\t\tout[i] = ox + (q[i]! + 32767) * sx;\n\t\tout[i + 1] = oy + (q[i + 1]! + 32767) * sy;\n\t\tout[i + 2] = oz + (q[i + 2]! + 32767) * sz;\n\t}\n\n\treturn out;\n}\n","import * as THREE from 'three';\n\nimport { finishGeometry, type FinishedGeometry } from '../mesh-assembly.js';\n\nimport { indexOutOfWindow } from './metadata.js';\n\nimport type { MaterialPicker } from './materials.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\n/**\n * Split a material group into one sub-group per layer, preserving mesh order.\n *\n * Merging is by material alone, but a merged mesh is one THREE object and so can sit on exactly\n * one layer in the outliner. Without this split, two layers sharing a material collapse into a\n * single object filed under the first one, and hiding either layer hides both.\n */\nexport function splitGroupByLayer(group: MaterialGroup): MaterialGroup[] {\n\tconst byLayer = new Map<string, MeshMetadata[]>();\n\tfor (const meshMeta of group.meshes) {\n\t\tconst layer = meshMeta.layer ?? '';\n\t\tlet bucket = byLayer.get(layer);\n\t\tif (!bucket) {\n\t\t\tbucket = [];\n\t\t\tbyLayer.set(layer, bucket);\n\t\t}\n\t\tbucket.push(meshMeta);\n\t}\n\treturn [...byLayer.values()].map((meshes) => ({ materialId: group.materialId, meshes }));\n}\n\n/**\n * The groups a merge-by-material draws: every group of one material gathered into one, in order of\n * first appearance, then split by layer. The writer keeps meshes in input order, so one material\n * can head several groups.\n */\nexport function mergeGroupsOf(groups: MaterialGroup[]): MaterialGroup[] {\n\tconst byMaterial = new Map<number, MeshMetadata[]>();\n\tfor (const group of groups) {\n\t\tconst meshes = byMaterial.get(group.materialId);\n\t\t// A loop, not push(...spread): spreading passes every mesh as an argument and overflows the\n\t\t// stack past ~100k meshes.\n\t\tif (meshes) for (const mesh of group.meshes) meshes.push(mesh);\n\t\telse byMaterial.set(group.materialId, group.meshes.slice());\n\t}\n\treturn [...byMaterial].flatMap(([materialId, meshes]) =>\n\t\tsplitGroupByLayer({ materialId, meshes })\n\t);\n}\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\tpickMaterial: MaterialPicker,\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 finished = finishGeometry(\n\t\t{ positions: mergedVertices, indices: mergedIndices, uvs: mergedUvs, colors: mergedColors },\n\t\tgroup.meshes\n\t);\n\treturn finalizeMergedMesh(\n\t\tgeometryFrom(finished),\n\t\tgroup,\n\t\tpickMaterial(group.materialId, finished.closed)\n\t);\n}\n\nexport function geometryFrom(finished: FinishedGeometry): THREE.BufferGeometry {\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute('position', new THREE.BufferAttribute(finished.positions, 3));\n\tgeometry.setAttribute('normal', new THREE.BufferAttribute(finished.normals, 3));\n\tgeometry.setIndex(new THREE.BufferAttribute(finished.indices, 1));\n\tif (finished.uvs) geometry.setAttribute('uv', new THREE.BufferAttribute(finished.uvs, 2));\n\tif (finished.tangents) {\n\t\tgeometry.setAttribute('tangent', new THREE.BufferAttribute(finished.tangents, 4));\n\t}\n\tif (finished.colors) {\n\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(finished.colors, 3, true));\n\t}\n\treturn geometry;\n}\n\n/**\n * One source object inside a merged mesh, as stamped into `userData.members`.\n *\n * Deliberately declared here rather than imported from `scene/`: the two layers are siblings, so\n * `parse` writing this shape and `scene` reading it back off `userData` is the only contract\n * between them. `scene/identity.ts` has the mirror of this interface.\n */\ninterface MergedMember {\n\ttrackingKey?: string;\n\tname: string;\n\tlayer: string;\n\tmetadata: Record<string, string>;\n\tindexStart: number;\n\tindexCount: number;\n}\n\n/**\n * Per-member records for a merged mesh, each carrying its window into the merged index buffer.\n * The windows are a prefix sum over `indexCount` because the merge concatenates members in this\n * order — see the copy loop in {@link createMergedMesh}.\n */\nfunction mergedMembers(group: MaterialGroup): MergedMember[] {\n\tconst members: MergedMember[] = [];\n\tlet indexStart = 0;\n\tfor (const mesh of group.meshes) {\n\t\tmembers.push({\n\t\t\ttrackingKey: mesh.id,\n\t\t\tname: mesh.name,\n\t\t\tlayer: mesh.layer,\n\t\t\tmetadata: mesh.metadata ?? {},\n\t\t\tindexStart,\n\t\t\tindexCount: mesh.indexCount\n\t\t});\n\t\tindexStart += mesh.indexCount;\n\t}\n\treturn members;\n}\n\nexport function finalizeMergedMesh(\n\tgeometry: THREE.BufferGeometry,\n\tgroup: MaterialGroup,\n\tmaterial: THREE.Material\n): THREE.Mesh {\n\tconst threeMesh = new THREE.Mesh(geometry, material);\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\tmetadata: firstMesh?.metadata ?? {},\n\t\t// A merged mesh is several source objects in one THREE object, so identity lives per\n\t\t// member: the scene layer keys hidden state on every member's key, and regrouping can\n\t\t// never lose it.\n\t\t//\n\t\t// `indexStart`/`indexCount` are the member's window into the merged index buffer, which is\n\t\t// what maps a raycast hit back to the one source object under the cursor — without them a\n\t\t// click could only ever identify the whole merged mesh. The merge writes members in this\n\t\t// exact order (see the concatenation loop above), so the windows are a prefix sum over\n\t\t// `indexCount` and are never stored on the wire.\n\t\tmembers: mergedMembers(group)\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\tpickMaterial: MaterialPicker,\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 uvStart = meshMeta.vertexStart * 2;\n\t\tconst finished = finishGeometry(\n\t\t\t{\n\t\t\t\tpositions: vertices,\n\t\t\t\tindices: rebasedIndices,\n\t\t\t\tuvs: allUvs ? allUvs.slice(uvStart, uvStart + meshMeta.vertexCount * 2) : null,\n\t\t\t\tcolors: allColors ? allColors.slice(componentStart, componentStart + componentLen) : null\n\t\t\t},\n\t\t\t[meshMeta]\n\t\t);\n\t\tmeshes.push(\n\t\t\tfinalizeSingleMesh(\n\t\t\t\tgeometryFrom(finished),\n\t\t\t\tmeshMeta,\n\t\t\t\tpickMaterial(group.materialId, finished.closed)\n\t\t\t)\n\t\t);\n\t}\n\n\treturn meshes;\n}\n\nexport function finalizeSingleMesh(\n\tgeometry: THREE.BufferGeometry,\n\tmeshMeta: MeshMetadata,\n\tmaterial: THREE.Material\n): THREE.Mesh {\n\tconst mesh = new THREE.Mesh(geometry, material);\n\tmesh.name = meshMeta.name;\n\tmesh.userData = {\n\t\tsource: 'compute',\n\t\tname: meshMeta.name,\n\t\tlayer: meshMeta.layer ?? '',\n\t\t// The writer-minted identity; absent for foreign writers, and the scene layer then\n\t\t// falls back to name + layer.\n\t\ttrackingKey: meshMeta.id,\n\t\tmetadata: meshMeta.metadata ?? {}\n\t};\n\tmesh.castShadow = true;\n\tmesh.receiveShadow = true;\n\treturn mesh;\n}\n","import type * as THREE from 'three';\n\nimport { shareGeometry, unshareGeometry } from '../../../shared/index.js';\nimport { geometryFrom } from './merge.js';\n\nimport type { AssemblyJob, FinishedGeometry } from '../mesh-assembly.js';\n\n/**\n * Finished geometries from earlier batches, keyed by the exact geometry chunk bytes and the jobs cut\n * from them. A re-solve that only changed materials, names or layers sends the same geometry bytes,\n * and a hit skips inflate, decode, the closed-solid test and the crease split: most of a parse.\n *\n * A hit is decided by comparing every byte, never by a fingerprint alone: a sampled fingerprint can\n * match two different payloads and serve stale geometry without an error.\n *\n * A hit hands out the same `BufferGeometry` objects again, registered with `shareGeometry`. Who frees\n * what is the cache rule in `shared/gpu-ownership.ts`.\n */\n\nexport interface FinishedBatch {\n\tgeometries: FinishedGeometry[];\n\thasUvs: boolean;\n\thasColors: boolean;\n\tvertexCount: number;\n\tindexCount: number;\n}\n\n/** One `BufferGeometry` per job, in job order, with each job's closed-solid flag. */\nexport interface BuiltBatch extends Omit<FinishedBatch, 'geometries'> {\n\tbuffers: THREE.BufferGeometry[];\n\tclosed: boolean[];\n}\n\ninterface Entry extends BuiltBatch {\n\tgeometryBytes: Uint8Array;\n\tjobShape: Int32Array;\n\tbytes: number;\n}\n\n// A colour change on a 2M-vertex scene keeps ~110 MB of finished arrays; this holds two variants.\nconst MAX_BYTES = 256 * 1024 * 1024;\n\nconst entries = new Map<string, Entry>();\nlet totalBytes = 0;\n\nexport function getFinishedBatch(\n\tgeometryBytes: Uint8Array,\n\tjobs: AssemblyJob[]\n): BuiltBatch | null {\n\tconst jobShape = shapeOf(jobs);\n\tconst key = cacheKey(geometryBytes, jobShape);\n\tconst entry = entries.get(key);\n\tif (\n\t\t!entry ||\n\t\t!sameInts(entry.jobShape, jobShape) ||\n\t\t!sameBytes(entry.geometryBytes, geometryBytes)\n\t)\n\t\treturn null;\n\t// Re-insert: Map iteration order is the eviction order.\n\tentries.delete(key);\n\tentries.set(key, entry);\n\treturn entry;\n}\n\n/** Wraps each finished geometry in a `BufferGeometry`, owned by the scene. */\nexport function buildBatch(batch: FinishedBatch): BuiltBatch {\n\tconst { geometries, ...rest } = batch;\n\treturn {\n\t\t...rest,\n\t\tbuffers: geometries.map(geometryFrom),\n\t\tclosed: geometries.map((g) => g.closed)\n\t};\n}\n\n/** Same as {@link buildBatch}, but the cache keeps the geometries for the next identical payload. */\nexport function storeFinishedBatch(\n\tgeometryBytes: Uint8Array,\n\tjobs: AssemblyJob[],\n\tbatch: FinishedBatch\n): BuiltBatch {\n\tconst built = buildBatch(batch);\n\tlet bytes = geometryBytes.byteLength;\n\tfor (const g of batch.geometries) {\n\t\tbytes += g.positions.byteLength + g.normals.byteLength + g.indices.byteLength;\n\t\tbytes += (g.uvs?.byteLength ?? 0) + (g.colors?.byteLength ?? 0) + (g.tangents?.byteLength ?? 0);\n\t}\n\tif (bytes > MAX_BYTES) return built;\n\n\tconst jobShape = shapeOf(jobs);\n\tconst key = cacheKey(geometryBytes, jobShape);\n\tconst existing = entries.get(key);\n\tif (existing) {\n\t\tentries.delete(key);\n\t\trelease(existing);\n\t}\n\tbuilt.buffers.forEach(shareGeometry);\n\t// A copy, so the entry neither pins the whole WebSocket frame nor sees it reused.\n\tentries.set(key, { ...built, geometryBytes: geometryBytes.slice(), jobShape, bytes });\n\ttotalBytes += bytes;\n\n\tfor (const [oldest, entry] of entries) {\n\t\tif (totalBytes <= MAX_BYTES) break;\n\t\tentries.delete(oldest);\n\t\trelease(entry);\n\t}\n\treturn built;\n}\n\n/**\n * Empties the cache and frees its geometries. The cache lives as long as the page, so a host calls\n * this when its viewer goes away; a mesh still drawing one of them uploads it again.\n */\nexport function clearGeometryCache(): void {\n\tentries.forEach(release);\n\tentries.clear();\n}\n\n// A mesh still drawing an evicted geometry is fine: three uploads it again on the next frame.\nfunction release(entry: Entry): void {\n\ttotalBytes -= entry.bytes;\n\tfor (const buffer of entry.buffers) {\n\t\tunshareGeometry(buffer);\n\t\tbuffer.dispose();\n\t}\n}\n\n// Per job: kind, window count, then each window's four numbers. The count prefix keeps two\n// different job lists from flattening to the same ints.\nfunction shapeOf(jobs: AssemblyJob[]): Int32Array {\n\tlet length = 0;\n\tfor (const job of jobs) length += 2 + 4 * job.windows.length;\n\tconst shape = new Int32Array(length);\n\tlet i = 0;\n\tfor (const job of jobs) {\n\t\tshape[i++] = job.kind === 'merged' ? 1 : 0;\n\t\tshape[i++] = job.windows.length;\n\t\tfor (const w of job.windows) {\n\t\t\tshape[i++] = w.vertexStart;\n\t\t\tshape[i++] = w.vertexCount;\n\t\t\tshape[i++] = w.indexStart;\n\t\t\tshape[i++] = w.indexCount;\n\t\t}\n\t}\n\treturn shape;\n}\n\n// Buckets entries; `sameInts` and `sameBytes` decide a hit. 1024 sampled bytes keep this cheap on a\n// multi-MB chunk.\nfunction cacheKey(geometryBytes: Uint8Array, jobShape: Int32Array): string {\n\tconst length = geometryBytes.length;\n\tconst stride = Math.max(1, Math.floor(length / 1024));\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < length; i += stride) {\n\t\thash = Math.imul(hash ^ geometryBytes[i]!, 0x01000193);\n\t}\n\tlet shapeHash = 0x811c9dc5;\n\tfor (let i = 0; i < jobShape.length; i++) {\n\t\tshapeHash = Math.imul(shapeHash ^ jobShape[i]!, 0x01000193);\n\t}\n\treturn `${length}:${hash >>> 0}:${jobShape.length}:${shapeHash >>> 0}`;\n}\n\nfunction sameInts(a: Int32Array, b: Int32Array): boolean {\n\tif (a.length !== b.length) return false;\n\tfor (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n\treturn true;\n}\n\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n\tif (a.length !== b.length) return false;\n\tlet i = 0;\n\tif (a.byteOffset % 4 === 0 && b.byteOffset % 4 === 0) {\n\t\tconst words = a.length >>> 2;\n\t\tconst wa = new Uint32Array(a.buffer, a.byteOffset, words);\n\t\tconst wb = new Uint32Array(b.buffer, b.byteOffset, words);\n\t\tfor (; i < words; i++) if (wa[i] !== wb[i]) return false;\n\t\ti = words << 2;\n\t}\n\tfor (; i < a.length; i++) if (a[i] !== b[i]) return false;\n\treturn true;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { decodeRawMeshBatch, FLAG_FLOAT32, parseBinaryMeshBatchRaw } from './binary-parser.js';\nimport { inflateSlvzAsync, slvzInflatedBytes, toUint8Array } from './binary/geometry.js';\nimport { isSlvmContainer, parseSlvmContainer, type SlvmContainer } from './binary/slvm.js';\nimport { assembleGeometries, finishGeometry } from './mesh-assembly.js';\n\nimport {\n\tASSEMBLY_WORKER_MIN_TRIANGLES,\n\tgetAssemblyWorker,\n\trequestAssembly\n} from './batch/assembly-worker.js';\nimport { createMaterialPicker } from './batch/materials.js';\nimport {\n\tcreateIndividualMeshes,\n\tcreateMergedMesh,\n\tfinalizeMergedMesh,\n\tfinalizeSingleMesh,\n\tmergeGroupsOf\n} from './batch/merge.js';\nimport {\n\tbuildBatch,\n\tgetFinishedBatch,\n\tstoreFinishedBatch,\n\ttype BuiltBatch,\n\ttype FinishedBatch\n} from './batch/finished-cache.js';\nimport { dequantizeInt16, validateGroupMetadata } from './batch/metadata.js';\n\nimport type { AssemblyJob, AssemblyWindow, FinishedGeometry } from './mesh-assembly.js';\nimport type { ParsedBinaryMeshBatch, RawBinaryMeshBatch } 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# `SlvaWriter`, 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\tconst decodeStart = performance.now();\n\tconst assemblyOptions: AssemblyPathOptions = {\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}\n\t};\n\tconst opened = await openBatch(batch.compressedData, assemblyOptions);\n\tif (opened.meshes) return opened.meshes;\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch).\n\tconst assembledMeshes = await tryBuildViaAssembly(opened, assemblyOptions);\n\tif (assembledMeshes) return assembledMeshes;\n\n\tconst parsed = decodeRawMeshBatch(opened.raw);\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}\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 per-object ids coming\n * from the container's table.\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\tconst decodeStart = performance.now();\n\tconst assemblyOptions: AssemblyPathOptions = { mergeByMaterial, debug, material };\n\tconst opened = await openBatch(blob, assemblyOptions);\n\tif (opened.meshes) return opened.meshes;\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch).\n\tconst assembledMeshes = await tryBuildViaAssembly(opened, assemblyOptions);\n\tif (assembledMeshes) return assembledMeshes;\n\n\tconst parsed = decodeRawMeshBatch(opened.raw);\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};\n}\n\n// Past this many source meshes, one THREE object each is the difference between a viewer that\n// holds 60fps and one that does not: render cost tracks object count, not triangles, and every\n// object also pays for its own edge overlay. A batch this size is an imported building model, where\n// merging is the only thing that keeps it interactive.\nconst UNMERGED_MESH_WARN_THRESHOLD = 1_000;\n\nlet warnedUnmerged = false;\n\n/** @internal Test seam: the warn-once flag is module state and would leak between cases. */\nexport function resetUnmergedWarning(): void {\n\twarnedUnmerged = false;\n}\n\n/**\n * Warns once when a caller opts out of merging on a batch large enough for it to hurt.\n *\n * Opting out is legitimate — it is what gives every source object its own THREE object — so this\n * does not override the caller. It exists because the failure is silent: nothing errors, the model\n * renders correctly, and the only symptom is a frame rate nobody traces back to a parse option.\n */\nfunction warnIfUnmergedAtScale(mergeByMaterial: boolean, groups: MaterialGroup[]): void {\n\tif (mergeByMaterial || warnedUnmerged) return;\n\n\tlet meshCount = 0;\n\tfor (const group of groups) meshCount += group.meshes.length;\n\tif (meshCount < UNMERGED_MESH_WARN_THRESHOLD) return;\n\n\twarnedUnmerged = true;\n\tgetLogger().warn(\n\t\t`Parsing ${meshCount} meshes with mergeByMaterial: false — each becomes its own THREE ` +\n\t\t\t`object, and render cost scales with object count. Drop the option to merge by material ` +\n\t\t\t`(the default); per-object identity survives it via userData.members.`\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\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\twarnIfUnmergedAtScale(mergeByMaterial, groups);\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 pickMaterial = createMaterialPicker(materialsSrc, {\n\t\tvertexColors: parsed.colors != null,\n\t\thasUvs: parsed.uvs != null,\n\t\tappearance: materialAppearance\n\t});\n\n\tconst meshes: THREE.Mesh[] = [];\n\n\tconst mergeGroups = mergeByMaterial ? mergeGroupsOf(groups) : groups;\n\n\tfor (const group of mergeGroups) {\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\tpickMaterial,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\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\tpickMaterial,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\n\t\t\tfor (const mesh of individualMeshes) meshes.push(mesh);\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// ASSEMBLY PATH: worker or inline, cached by geometry bytes\n// ============================================================================\n\ninterface AssemblyPathOptions {\n\tmergeByMaterial: boolean;\n\tdebug: boolean;\n\tmaterial?: MaterialAppearanceOptions;\n\tfallback?: BuildOptions['fallback'];\n}\n\ninterface JobRef {\n\tkind: 'merged' | 'single';\n\tgroup: MaterialGroup;\n\tmeshMeta?: MeshMetadata;\n}\n\ninterface OpenedGeometry {\n\tmeshes?: never;\n\traw: RawBinaryMeshBatch;\n\t/** The geometry chunk of an SLVM container: the finished-cache key. Null for a bare blob. */\n\tgeometryBytes: Uint8Array | null;\n\t/** The bytes `raw` was parsed from, whose buffer `raw`'s arrays may still alias. */\n\tsource: Uint8Array;\n\t/** Parses `source` again, for when `raw`'s arrays were transferred away. */\n\treparse: () => RawBinaryMeshBatch;\n}\n\ntype OpenedBatch = { meshes: THREE.Mesh[]; raw?: never } | OpenedGeometry;\n\n/**\n * Unwraps an SLVM container only as far as its table and materials, and answers from the\n * finished-geometry cache before paying for the inflate. A bare SLVA/SLVZ blob (old `.gh` files)\n * keeps its metadata inside the compressed geometry, so it always decodes.\n */\nasync function openBatch(\n\tinput: ArrayBuffer | Uint8Array | string,\n\topts: AssemblyPathOptions\n): Promise<OpenedBatch> {\n\tconst bytes = toUint8Array(input);\n\tif (!isSlvmContainer(bytes)) {\n\t\treturn {\n\t\t\traw: parseBinaryMeshBatchRaw(bytes),\n\t\t\tgeometryBytes: null,\n\t\t\tsource: bytes,\n\t\t\treparse: () => parseBinaryMeshBatchRaw(bytes)\n\t\t};\n\t}\n\n\tconst container = parseSlvmContainer(bytes);\n\tconst cached = buildFromFinishedCache(container, opts);\n\tif (cached) return { meshes: cached };\n\n\tconst geometryBlob = container.geometryBlob;\n\tconst reparse = (): RawBinaryMeshBatch => {\n\t\tconst raw = parseBinaryMeshBatchRaw(geometryBlob);\n\t\traw.metadata = container.metadata;\n\t\treturn raw;\n\t};\n\tconst inflated = await inflateInWorker(container);\n\tconst raw = inflated ? parseBinaryMeshBatchRaw(inflated) : reparse();\n\traw.metadata = container.metadata;\n\treturn { raw, geometryBytes: geometryBlob, source: geometryBlob, reparse };\n}\n\n// Below this the inflate takes under ~6 ms, less than the hop to a second worker costs.\nconst INFLATE_IN_WORKER_MIN_BYTES = 4 * 1024 * 1024;\n\n/**\n * Inflates a large SLVZ geometry chunk off the calling thread. Null means inflate it here: a small\n * or uncompressed chunk, no worker support, or a worker that failed.\n */\nasync function inflateInWorker(container: SlvmContainer): Promise<Uint8Array | null> {\n\tif (\n\t\tslvzInflatedBytes(container.geometryBlob) < INFLATE_IN_WORKER_MIN_BYTES ||\n\t\t!getAssemblyWorker()\n\t) {\n\t\treturn null;\n\t}\n\ttry {\n\t\treturn await inflateSlvzAsync(container.geometryBlob);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction buildFromFinishedCache(\n\tcontainer: SlvmContainer,\n\topts: AssemblyPathOptions\n): THREE.Mesh[] | null {\n\tconst materialsSrc = container.metadata.materials ?? opts.fallback?.materials ?? [];\n\tconst groups = container.metadata.groups ?? opts.fallback?.groups ?? [];\n\tconst { jobs, jobRefs } = planJobs(groups, opts.mergeByMaterial);\n\tconst cached = getFinishedBatch(container.geometryBlob, jobs);\n\tif (!cached) return null;\n\n\t// Equal jobs mean equal windows, but the material ids are this batch's own.\n\tvalidateGroupMetadata(groups, materialsSrc.length, cached.vertexCount, cached.indexCount);\n\treturn meshesFromBuilt(cached, jobRefs, materialsSrc, opts.material);\n}\n\n/** One job per merged group or per unmerged mesh, with a parallel ref list to unwrap results by index. */\nfunction planJobs(\n\tgroups: MaterialGroup[],\n\tmergeByMaterial: boolean\n): { jobs: AssemblyJob[]; jobRefs: JobRef[] } {\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\tclosed: m.closed\n\t});\n\tconst jobs: AssemblyJob[] = [];\n\tconst jobRefs: JobRef[] = [];\n\t// Same layer-aware merge grouping as the synchronous path.\n\tconst mergeGroups = mergeByMaterial ? mergeGroupsOf(groups) : groups;\n\tfor (const group of mergeGroups) {\n\t\tif (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\treturn { jobs, jobRefs };\n}\n\n/**\n * Builds a heavy batch through the assembly functions, in the worker when there is one and inline\n * otherwise, and caches the result under `geometryBytes`. Returns `null` for a small batch or a\n * crashed worker, and the caller then runs the synchronous path. Malformed-blob/metadata errors\n * throw either way, matching the entry points' contract.\n */\nasync function tryBuildViaAssembly(\n\topened: OpenedGeometry,\n\topts: AssemblyPathOptions\n): Promise<THREE.Mesh[] | null> {\n\tconst { raw, geometryBytes } = opened;\n\t// Planar (v4) indexData is a bare byte stream — divide by the element width for a count.\n\tconst indexCount = raw.planarByteSplit\n\t\t? raw.indexData.length / (raw.uint16Indices ? 2 : 4)\n\t\t: raw.indexData.length;\n\tif (indexCount / 3 < ASSEMBLY_WORKER_MIN_TRIANGLES) return null;\n\n\tconst materialsSrc = raw.metadata.materials ?? opts.fallback?.materials ?? [];\n\tconst groups = raw.metadata.groups ?? opts.fallback?.groups ?? [];\n\tvalidateGroupMetadata(groups, materialsSrc.length, raw.vertexCount, indexCount);\n\n\tconst { jobs, jobRefs } = planJobs(groups, opts.mergeByMaterial);\n\tconst assembled = await assemble(raw, jobs, opened.source);\n\tif (!assembled) {\n\t\t// A worker that failed already took the arrays: `decodeRawMeshBatch` needs fresh ones.\n\t\tif (ownsBuffer(raw, opened.source)) opened.raw = opened.reparse();\n\t\treturn null;\n\t}\n\n\tconst batch: FinishedBatch = {\n\t\tgeometries: assembled,\n\t\thasUvs: raw.uvs != null,\n\t\thasColors: raw.colors != null,\n\t\tvertexCount: raw.vertexCount,\n\t\tindexCount\n\t};\n\tconst built = geometryBytes ? storeFinishedBatch(geometryBytes, jobs, batch) : buildBatch(batch);\n\n\tif (opts.debug) {\n\t\tgetLogger().debug(\n\t\t\t`Mesh batch assembled: ${assembled.length} geometries, ${indexCount / 3} triangles`\n\t\t);\n\t}\n\treturn meshesFromBuilt(built, jobRefs, materialsSrc, opts.material);\n}\n\n/**\n * True when `raw`'s geometry arrays live in a buffer nobody else reads: the buffer a SLVZ chunk\n * was inflated into, not the WebSocket frame an uncompressed chunk still points into.\n */\nfunction ownsBuffer(raw: RawBinaryMeshBatch, source: Uint8Array): boolean {\n\treturn raw.vertexData.buffer !== source.buffer && raw.indexData.buffer !== source.buffer;\n}\n\nasync function assemble(\n\traw: RawBinaryMeshBatch,\n\tjobs: AssemblyJob[],\n\tsource: Uint8Array\n): Promise<FinishedGeometry[] | null> {\n\tconst worker = getAssemblyWorker();\n\tconst input = {\n\t\tvertexData: raw.vertexData,\n\t\tisFloat32: raw.isFloat32,\n\t\tdeltaEncoded: raw.deltaEncoded,\n\t\tplanarByteSplit: raw.planarByteSplit,\n\t\tuint16Indices: raw.uint16Indices,\n\t\torigin: raw.origin,\n\t\tscale: raw.scale,\n\t\tindexData: raw.indexData,\n\t\tuvs: raw.uvs,\n\t\tcolors: raw.colors,\n\t\tjobs\n\t};\n\n\tif (!worker) {\n\t\treturn assembleGeometries(input).map((geometry, i) =>\n\t\t\tfinishGeometry(geometry, jobs[i]!.windows)\n\t\t);\n\t}\n\n\t// Transferring detaches. An inflated buffer is ours to give away; an uncompressed chunk's arrays\n\t// alias the WebSocket frame, which the finished cache still reads afterwards, so those are copied.\n\tif (!ownsBuffer(raw, source)) {\n\t\tinput.vertexData = raw.vertexData.slice();\n\t\tinput.indexData = raw.indexData.slice();\n\t\tinput.uvs = raw.uvs?.slice() ?? null;\n\t\tinput.colors = raw.colors?.slice() ?? null;\n\t}\n\n\tconst transfer = new Set<ArrayBufferLike>([input.vertexData.buffer, input.indexData.buffer]);\n\tif (input.uvs) transfer.add(input.uvs.buffer);\n\tif (input.colors) transfer.add(input.colors.buffer);\n\ttry {\n\t\tconst assembled = await requestAssembly(worker, input, [...transfer] as Transferable[]);\n\t\treturn assembled.length === jobs.length ? assembled : null; // protocol mismatch → sync path\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}\n\nfunction meshesFromBuilt(\n\tbuilt: BuiltBatch,\n\tjobRefs: JobRef[],\n\tmaterialsSrc: SerializableMaterial[],\n\tappearance: MaterialAppearanceOptions | undefined\n): THREE.Mesh[] {\n\tconst pickMaterial = createMaterialPicker(materialsSrc, {\n\t\tvertexColors: built.hasColors,\n\t\thasUvs: built.hasUvs,\n\t\tappearance\n\t});\n\n\tconst meshes: THREE.Mesh[] = [];\n\tfor (let i = 0; i < built.buffers.length; i++) {\n\t\tconst geometry = built.buffers[i]!;\n\t\tconst ref = jobRefs[i]!;\n\t\tconst material = pickMaterial(ref.group.materialId, built.closed[i]!);\n\t\tconst mesh =\n\t\t\tref.kind === 'merged'\n\t\t\t\t? finalizeMergedMesh(geometry, ref.group, material)\n\t\t\t\t: finalizeSingleMesh(geometry, ref.meshMeta!, material);\n\t\tmeshes.push(mesh);\n\t}\n\treturn meshes;\n}\n\n// ============================================================================\n// DEBUG HELPERS\n// ============================================================================\n\nfunction approximateBase64DecodedBytes(base64: string): number {\n\treturn Math.floor((base64.length * 3) / 4);\n}\n","import * as THREE from 'three';\n\nimport {\n\tapplyOffset,\n\tcomputeCombinedBoundingBox,\n\tdisposeObjectTree,\n\tgetLogger\n} 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, GrasshopperComputeResponse } 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 getThreeObjectsFromComputeResponse(\n\tdata: GrasshopperComputeResponse,\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: GrasshopperComputeResponse,\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\t// Spread first, then re-apply the default for anything the caller left undefined: a caller\n\t\t// passing `{ material }` would otherwise carry `mergeByMaterial: undefined` over the default\n\t\t// and silently un-merge the batch.\n\t\tconst mergedParsingOptions: MeshBatchParsingOptions = {\n\t\t\tdebug: false,\n\t\t\t...parsingOptions,\n\t\t\tmergeByMaterial: parsingOptions.mergeByMaterial ?? true\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\tfor (const object of batchObjects) objects.push(object);\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 mesh of meshes) disposeObjectTree(mesh);\n}\n\nfunction logProcessingTime(startTime: number): void {\n\tconst elapsed = performance.now() - startTime;\n\tgetLogger().info('Time to process meshes:', `${elapsed.toFixed(2)}ms`);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parse.cjs","names":["VisualizationError","ErrorCodes","disposeObjectTree","THREE","LineGeometry","LineMaterial","Line2","VisualizationError","ErrorCodes","THREE","unhandled","inflateSync","VisualizationError","ErrorCodes","VisualizationError","ErrorCodes","observeMaxAnisotropy","THREE","RECIPES","f","parseColor","THREE","OWN_ENV_MAP_INTENSITY","lookEnvMapIntensity","OWN_EMISSIVE","EMISSIVE_LIGHT","VisualizationError","ErrorCodes","THREE","shareGeometry","computeCombinedBoundingBox"],"sources":["../src/shared/encoding.ts","../src/parse/mesh-policy.ts","../src/parse/display-items/items/appearance.ts","../src/parse/display-items/items/curves.ts","../src/parse/display-items/items/points.ts","../src/parse/display-items/display-items-parser.ts","../src/parse/webdisplay/binary/header.ts","../src/parse/webdisplay/binary/geometry.ts","../src/parse/webdisplay/binary/textures.ts","../src/parse/webdisplay/binary/slvm.ts","../src/parse/webdisplay/binary-parser.ts","../src/parse/webdisplay/closed-solid.ts","../src/parse/webdisplay/crease-normals.ts","../src/parse/webdisplay/grain-tangents.ts","../src/parse/webdisplay/mesh-assembly.ts","../src/parse/webdisplay/batch/assembly-worker.ts","../src/parse/webdisplay/apply-texture.ts","../src/parse/webdisplay/batch/surface-finish.ts","../src/parse/webdisplay/batch/finish.ts","../src/parse/webdisplay/batch/materials.ts","../src/parse/webdisplay/batch/metadata.ts","../src/parse/webdisplay/batch/merge.ts","../src/parse/webdisplay/batch/finished-cache.ts","../src/parse/webdisplay/batch-parser.ts","../src/parse/webdisplay/webdisplay-parser.ts"],"sourcesContent":["// Copied (not imported) from `@selvajs/compute`'s `decodeBase64ToBinary`: a few stable lines don't\n// justify depending on the Rhino.Compute client. Keep the two in sync by hand.\n\nimport { VisualizationError, ErrorCodes } from './errors.js';\n\nfunction getNodeBuffer(): typeof Buffer | undefined {\n\tconst buf = (globalThis as { Buffer?: typeof Buffer }).Buffer;\n\treturn typeof buf === 'function' ? buf : undefined;\n}\n\n/**\n * WHATWG forgiving-base64 normalization without the alphabet check: strip ASCII whitespace, then\n * drop trailing padding where the spec allows it (total length a multiple of 4). `null` when the\n * length can't be base64. The decoders check the alphabet themselves, faster than a regex.\n */\nfunction stripBase64(str: string): string | null {\n\tlet data = str.replace(/[\\t\\n\\f\\r ]/g, '');\n\tif (data.length % 4 === 0) data = data.replace(/={1,2}$/, '');\n\treturn data.length % 4 === 1 ? null : data;\n}\n\n/**\n * @throws {VisualizationError} `ENCODING_ERROR` if invalid, or `INVALID_STATE` if no decoder is\n * available in this environment.\n */\nexport function decodeBase64ToBinary(base64File: string): Uint8Array {\n\tconst data = stripBase64(base64File);\n\tconst bytes = data === null ? null : decodeStripped(data);\n\tif (bytes === null) {\n\t\tthrow new VisualizationError('Invalid base64 input.', ErrorCodes.ENCODING_ERROR, {\n\t\t\tcontext: { inputLength: base64File.length }\n\t\t});\n\t}\n\treturn bytes;\n}\n\n/** Decode {@link stripBase64} output; `null` when a character is outside the alphabet. */\nfunction decodeStripped(data: string): Uint8Array | null {\n\t// Prefer Buffer in Node: faster, and avoids the atob + charCodeAt latin-1 detour.\n\tconst Buffer = getNodeBuffer();\n\tif (Buffer) {\n\t\t// Buffer decodes leniently: `-` and `_` as base64url, a non-ASCII char as its low byte, and\n\t\t// any other stray char is skipped. Rule out the first two; a skip leaves the result short.\n\t\tif (Buffer.byteLength(data, 'utf8') !== data.length) return null;\n\t\tif (data.includes('-') || data.includes('_')) return null;\n\t\tconst decoded = Buffer.from(data, 'base64');\n\t\tif (decoded.length !== Math.floor((data.length * 3) / 4)) return null;\n\t\t// Small Buffer.from results are views over Node's shared 8 KiB pool slab: copy one out, or\n\t\t// it retains the whole slab and leaks unrelated pooled bytes to any consumer touching\n\t\t// `.buffer` (structuredClone, postMessage transfer, etc). A large one owns its memory.\n\t\tconst ownsBuffer = decoded.byteOffset === 0 && decoded.buffer.byteLength === decoded.length;\n\t\treturn ownsBuffer ? new Uint8Array(decoded.buffer, 0, decoded.length) : new Uint8Array(decoded);\n\t}\n\tif (typeof globalThis.atob === 'function') {\n\t\tlet binary: string;\n\t\ttry {\n\t\t\tbinary = globalThis.atob(data);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tconst bytes = new Uint8Array(binary.length);\n\t\tfor (let i = 0; i < binary.length; i++) {\n\t\t\tbytes[i] = binary.charCodeAt(i) & 0xff;\n\t\t}\n\t\treturn bytes;\n\t}\n\n\tthrow new VisualizationError(\n\t\t'Base64 decoding not supported in this environment.',\n\t\tErrorCodes.INVALID_STATE,\n\t\t{ context: { environmentInfo: 'atob or Buffer not available' } }\n\t);\n}\n","// Mesh ownership policy for `@selvajs/solve`'s result memo: `SolveResult<TMesh>` is opaque to the\n// memo, so clone/release are injected here instead. The viewer disposes whatever it last rendered\n// (`clearScene`), so a memo handing out live references would serve a disposed object on the next\n// hit — `clone` copies geometry explicitly (`Object3D.clone()` shares it by reference) but leaves\n// materials shared, since `clearScene` already spares `SHARED_MATERIALS` singletons and recompiling\n// per-mesh materials as shaders is expensive.\n\nimport * as THREE from 'three';\n\nimport { disposeObjectTree } from '../shared/index.js';\n\nexport function cloneSceneObjects(meshes: THREE.Object3D[]): THREE.Object3D[] {\n\treturn meshes.map((root) => {\n\t\tconst copy = root.clone(true);\n\t\tconst sources: THREE.Object3D[] = [];\n\t\troot.traverse((child) => sources.push(child));\n\t\tlet i = 0;\n\t\tcopy.traverse((child) => {\n\t\t\tconst source = sources[i++] as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tconst target = child as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tif (!source.geometry) return;\n\t\t\ttarget.geometry = source.geometry.clone();\n\t\t});\n\t\treturn copy;\n\t});\n}\n\n/** Skips materials — the memo never owns those; see the file header. */\nexport function releaseSceneObjects(meshes: THREE.Object3D[]): void {\n\tmeshes.forEach((root) => disposeObjectTree(root, { materials: false }));\n}\n\n/** Structurally, not nominally, typed as `@selvajs/solve/client`'s `MeshPolicy<THREE.Object3D>` — avoids a dependency on solve. */\nexport const meshPolicy: {\n\tclone(meshes: THREE.Object3D[]): THREE.Object3D[];\n\trelease(meshes: THREE.Object3D[]): void;\n} = {\n\tclone: cloneSceneObjects,\n\trelease: releaseSceneObjects\n};\n","import * as THREE from 'three';\n\nexport const DEFAULT_COLOR = '#ffffff';\n\n/** Opacity < 1 flips `transparent` on. */\nexport function materialParams(\n\tcolor: string | undefined,\n\topacity: number | undefined\n): { color: THREE.Color; transparent: boolean; opacity: number } {\n\tconst resolved = opacity ?? 1;\n\treturn {\n\t\tcolor: new THREE.Color(color ?? DEFAULT_COLOR),\n\t\ttransparent: resolved < 1,\n\t\topacity: resolved\n\t};\n}\n","import { Line2 } from 'three/addons/lines/Line2.js';\nimport { LineGeometry } from 'three/addons/lines/LineGeometry.js';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\n\nimport { ErrorCodes, VisualizationError } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayCurve } from '../types';\n\nconst DEFAULT_LINE_WIDTH = 2;\n\n/** Two vertices — the shortest renderable polyline. */\nconst MIN_POSITIONS = 6;\n\n/**\n * Curves arrive tessellated: the backend sends `points`, this builds the line. Nothing decodes\n * geometry in the browser.\n *\n * Uses `Line2`/`LineMaterial` instead of `THREE.Line`: plain `THREE.Line` is hard-capped at 1px on\n * every major GPU backend, so `item.width` would go unhonoured. `Line2.onBeforeRender` sets\n * `LineMaterial`'s required `resolution`, so no renderer reference is needed here.\n *\n * @throws VisualizationError when the item has no `points` — see {@link curvePositions}.\n */\nexport function buildCurveLine(item: DisplayCurve): Line2 | null {\n\tconst positions = curvePositions(item);\n\tif (!positions) return null;\n\n\tconst geometry = new LineGeometry();\n\tgeometry.setPositions(positions);\n\n\t// @types/three's LineMaterial omits `linewidth`/`transparent`/`opacity` though all exist at runtime.\n\tconst params = materialParams(item.color, item.opacity);\n\tconst material = new LineMaterial({ color: params.color });\n\tconst styled = material as LineMaterial & {\n\t\tlinewidth: number;\n\t\ttransparent: boolean;\n\t\topacity: number;\n\t};\n\tstyled.linewidth = item.width ?? DEFAULT_LINE_WIDTH; // CSS px (worldUnits defaults false)\n\tstyled.transparent = params.transparent;\n\tstyled.opacity = params.opacity;\n\n\tconst line = new Line2(geometry, material);\n\tline.computeLineDistances();\n\tline.name = item.name;\n\tline.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'curve',\n\t\tmetadata: item.metadata\n\t};\n\treturn line;\n}\n\n/**\n * Flat `[x,y,z, …]`, or null for a degenerate curve — one of those can't abort the batch.\n *\n * A curve with no `points` **throws** instead. It means the definition was solved by a Display\n * component predating backend tessellation, which is a stale definition rather than one bad curve:\n * skipping would render a scene silently missing geometry, indistinguishable from a definition that\n * has no curves, with the fix nowhere in sight.\n */\nfunction curvePositions(item: DisplayCurve): number[] | null {\n\tif (!item.points) {\n\t\tthrow new VisualizationError(\n\t\t\t`Curve display item '${item.id}' has no tessellated points. It was produced by an ` +\n\t\t\t\t'outdated Display component — upgrade it in Grasshopper (Solution → Upgrade obsolete ' +\n\t\t\t\t'components) and re-save the definition.',\n\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t{ context: { itemId: item.id, kind: item.kind } }\n\t\t);\n\t}\n\n\treturn item.points.length >= MIN_POSITIONS ? item.points : null;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../../shared/index.js';\nimport { materialParams } from './appearance.js';\n\nimport type { DisplayPoint } from '../types';\n\nexport function buildPoint(item: DisplayPoint): THREE.Points | null {\n\t// `position` comes off the wire — don't trust the declared type without validating.\n\tconst { position } = item as { position?: { X?: unknown; Y?: unknown; Z?: unknown } };\n\tif (\n\t\t!position ||\n\t\ttypeof position.X !== 'number' ||\n\t\t!Number.isFinite(position.X) ||\n\t\ttypeof position.Y !== 'number' ||\n\t\t!Number.isFinite(position.Y) ||\n\t\ttypeof position.Z !== 'number' ||\n\t\t!Number.isFinite(position.Z)\n\t) {\n\t\tgetLogger().warn(\n\t\t\t`Skipping point display item with missing or non-finite position (id: ${String(item.id)}).`\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute(\n\t\t'position',\n\t\tnew THREE.Float32BufferAttribute([position.X, position.Y, position.Z], 3)\n\t);\n\n\tconst material = new THREE.PointsMaterial({\n\t\t...materialParams(item.color, item.opacity),\n\t\tsize: 6,\n\t\tsizeAttenuation: false\n\t});\n\n\tconst points = new THREE.Points(geometry, material);\n\tpoints.name = item.name;\n\tpoints.userData = {\n\t\tsource: 'compute',\n\t\tid: item.id,\n\t\tlayer: item.layer,\n\t\tkind: 'point',\n\t\tmetadata: item.metadata\n\t};\n\treturn points;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { buildCurveLine } from './items/curves.js';\nimport { buildPoint } from './items/points.js';\n\nimport type { DisplayItem } from './types';\n\n/**\n * Builds THREE objects for the batch's non-mesh items.\n *\n * @throws VisualizationError when a curve predates backend tessellation, so a stale definition\n * surfaces as an actionable error instead of a scene quietly missing its curves. Every other\n * unrenderable item is logged and skipped.\n */\nexport function parseDisplayItems(items: DisplayItem[] | undefined): THREE.Object3D[] {\n\tif (!items || items.length === 0) return [];\n\n\tconst objects: THREE.Object3D[] = [];\n\n\tfor (const item of items) {\n\t\tswitch (item.kind) {\n\t\t\tcase 'curve': {\n\t\t\t\tconst line = buildCurveLine(item);\n\t\t\t\tif (line) objects.push(line);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'point': {\n\t\t\t\tconst point = buildPoint(item);\n\t\t\t\tif (point) objects.push(point);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// Forces a compile error if a new DisplayItem kind is added without a case above.\n\t\t\t\tconst unhandled: never = item;\n\t\t\t\tconst unknown = unhandled as { kind?: string };\n\t\t\t\tgetLogger().warn(`Skipping unknown display item kind: ${String(unknown.kind)}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn objects;\n}\n","import type { MaterialGroup, SerializableMaterial } from '../types.js';\n\n// ============================================================================\n// WIRE FORMAT CONSTANTS\n// ============================================================================\n\n/** \"SLVA\" little-endian — an uncompressed mesh blob. */\nexport const BINARY_MESH_MAGIC = 0x41564c53;\n/**\n * \"SLVZ\" little-endian — an optional raw-DEFLATE container around a SLVA blob (applied by the\n * plugin when it shrinks the payload). Layout: `[4] magic=SLVZ | [4] uncompressedLen(u32) |\n * [N] raw-deflate stream of the SLVA blob`.\n */\nexport const COMPRESSED_MESH_MAGIC = 0x5a564c53;\n/**\n * Current writer version. v2 added FLAG_UINT16_INDICES; v3 added FLAG_DELTA_ENCODED; v4 added\n * FLAG_PLANAR_BYTESPLIT.\n */\nexport const BINARY_MESH_VERSION = 4;\n/**\n * Oldest wire version this parser still decodes. Each version only added a flag bit, so the\n * flag-driven read path handles every older blob unchanged — needed since persisted/cached blobs\n * (saved `.gh` files, `.slvm`/`.dmf` mesh files, cached compute results) must stay decodable after upgrade.\n */\nexport const MIN_SUPPORTED_VERSION = 1;\n/** Bit 0 of the geometry flags word: 0 = int16 quantized, 1 = float32 raw. */\nexport const FLAG_FLOAT32 = 0x1;\n/** Bit 1 of the geometry flags word: 0 = uint32 indices, 1 = uint16 indices. */\nexport const FLAG_UINT16_INDICES = 0x2;\n/**\n * Bit 2 of the geometry flags word: int16 vertex components and indices are stored as wrapped\n * per-component deltas from their predecessor, zigzag-mapped to unsigned (float32 vertices are\n * never filtered). Deltas of welded meshes concentrate near zero, so the SLVZ DEFLATE pass\n * compresses far better. Decoding reverses the filter with a running prefix sum.\n */\nexport const FLAG_DELTA_ENCODED = 0x4;\n/**\n * Bit 3: a UV chunk trails the index block. Layout: `uvFormat(u32: 0=uint16 quantized, 1=float32)\n * | uvOrigin(2×f64) | uvScale(2×f64) | data`, element count implied by vertexCount. Quantized UVs\n * reconstruct as `uv = origin + q * scale` (q unsigned in [0, 65535]), delta+zigzag filtered per\n * component (independent u/v predictors) iff FLAG_DELTA_ENCODED; float32 UVs are never filtered.\n * Absent flag = absent chunk, so untextured blobs are byte-identical to pre-chunk writers.\n */\nexport const FLAG_HAS_UVS = 0x8;\n/**\n * Bit 4: a vertex-color chunk trails the index block (after the UV chunk when both present).\n * Layout: `uint8 rgb[vertexCount*3]`, delta+zigzag filtered per channel (wrapped 8-bit, independent\n * r/g/b predictors) iff FLAG_DELTA_ENCODED.\n */\nexport const FLAG_HAS_VERTEX_COLORS = 0x10;\n\n/**\n * Bit 5 (v4): every delta+zigzag-filtered stream (quantized vertices, indices, quantized UVs —\n * never float32 data, never colors) is stored as byte planes over its element count N instead of\n * interleaved LE values:\n *\n * ```\n * vertices [X-lo × N][Y-lo × N][Z-lo × N][X-hi × N][Y-hi × N][Z-hi × N] N = vertexCount\n * uint16 indices [lo × N][hi × N] N = indexCount\n * uint32 indices [b0 × N][b1 × N][b2 × N][b3 × N] N = indexCount\n * uvs [U-lo × N][V-lo × N][U-hi × N][V-hi × N] N = vertexCount\n * ```\n *\n * Byte lengths and delta/zigzag semantics are identical to the interleaved v3 layout — only byte\n * order within each block changes. Near-zero deltas make the hi planes runs of zeros, so the SLVZ\n * DEFLATE pass compresses 28–50% smaller than interleaved. Colors keep the v3 interleaved layout\n * (planar loses on noisy per-channel data, measured).\n */\nexport const FLAG_PLANAR_BYTESPLIT = 0x20;\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}\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","import { inflate, 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\ninterface SlvzPayload {\n\tdeflated: Uint8Array;\n\tuncompressedLen: number;\n}\n\nfunction isSlvz(bytes: Uint8Array): boolean {\n\treturn (\n\t\tbytes.byteLength >= 8 &&\n\t\tnew DataView(bytes.buffer, bytes.byteOffset, 8).getUint32(0, true) === COMPRESSED_MESH_MAGIC\n\t);\n}\n\n/** Size a SLVZ blob inflates to, as its header declares; 0 for anything else. Unvalidated. */\nexport function slvzInflatedBytes(bytes: Uint8Array): number {\n\treturn isSlvz(bytes) ? new DataView(bytes.buffer, bytes.byteOffset, 8).getUint32(4, true) : 0;\n}\n\nfunction slvzPayload(bytes: Uint8Array): SlvzPayload {\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\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\treturn { deflated, uncompressedLen };\n}\n\nfunction inflateFailure(error: unknown, payload: SlvzPayload): VisualizationError {\n\treturn fail(\n\t\t`Failed to inflate SLVZ blob: ${error instanceof Error ? error.message : String(error)}`,\n\t\t{ uncompressedLen: payload.uncompressedLen, deflatedBytes: payload.deflated.byteLength }\n\t);\n}\n\nfunction checkInflatedLength(out: Uint8Array, payload: SlvzPayload): Uint8Array {\n\tif (out.byteLength !== payload.uncompressedLen) {\n\t\tthrow fail('SLVZ payload inflated to a different size than the header declares.', {\n\t\t\tdeclaredLen: payload.uncompressedLen,\n\t\t\tactualLen: out.byteLength,\n\t\t\tdeflatedBytes: payload.deflated.byteLength\n\t\t});\n\t}\n\treturn out;\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 (!isSlvz(bytes)) return bytes;\n\n\tconst payload = slvzPayload(bytes);\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(payload.deflated, { out: new Uint8Array(payload.uncompressedLen + 1) });\n\t} catch (error) {\n\t\tthrow inflateFailure(error, payload);\n\t}\n\treturn checkInflatedLength(out, payload);\n}\n\n/**\n * {@link maybeDecompress} for a SLVZ blob, inflated in fflate's own worker so the calling thread\n * stays free. The result owns its buffer. Rejects on any failure, including a worker that could not\n * start: the caller falls back to {@link maybeDecompress}, which reports a corrupt blob properly.\n */\nexport function inflateSlvzAsync(bytes: Uint8Array): Promise<Uint8Array> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst payload = slvzPayload(bytes);\n\t\ttry {\n\t\t\t// One byte of slack, as in `maybeDecompress`: fflate truncates output that overruns `size`,\n\t\t\t// which would hide an oversized payload instead of tripping the length check.\n\t\t\tinflate(payload.deflated, { size: payload.uncompressedLen + 1 }, (error, out) => {\n\t\t\t\tif (error) {\n\t\t\t\t\treject(inflateFailure(error, payload));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tresolve(checkInflatedLength(out, payload));\n\t\t\t\t} catch (mismatch) {\n\t\t\t\t\treject(mismatch);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treject(inflateFailure(error, payload));\n\t\t}\n\t});\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\n/**\n * Undoes the v4 planar byte-split vertex layout: six byte planes\n * `[Xlo][Ylo][Zlo][Xhi][Yhi][Zhi]` (each `vertexCount` bytes) of the zigzag deltas — merge the\n * planes, unzigzag, prefix-sum per component. Same delta semantics as {@link decodeDeltaVertices}.\n */\nexport function decodePlanarVertices(planes: Uint8Array, vertexCount: number): Int16Array {\n\tconst out = new Int16Array(vertexCount * 3);\n\tconst n = vertexCount;\n\tlet px = 0;\n\tlet py = 0;\n\tlet pz = 0;\n\tfor (let i = 0; i < n; i++) {\n\t\tpx = ((px + unzigzag(planes[i]! | (planes[n * 3 + i]! << 8))) << 16) >> 16;\n\t\tpy = ((py + unzigzag(planes[n + i]! | (planes[n * 4 + i]! << 8))) << 16) >> 16;\n\t\tpz = ((pz + unzigzag(planes[n * 2 + i]! | (planes[n * 5 + i]! << 8))) << 16) >> 16;\n\t\tout[i * 3] = px;\n\t\tout[i * 3 + 1] = py;\n\t\tout[i * 3 + 2] = pz;\n\t}\n\treturn out;\n}\n\n/** Undoes the v4 planar byte-split uint16 index layout `[lo × N][hi × N]`. */\nexport function decodePlanarIndices16(planes: Uint8Array, count: number): Uint16Array {\n\tconst out = new Uint16Array(count);\n\tlet prev = 0;\n\tfor (let i = 0; i < count; i++) {\n\t\tprev = (prev + unzigzag(planes[i]! | (planes[count + i]! << 8))) & 0xffff;\n\t\tout[i] = prev;\n\t}\n\treturn out;\n}\n\n/** Undoes the v4 planar byte-split uint32 index layout `[b0 × N][b1 × N][b2 × N][b3 × N]`. */\nexport function decodePlanarIndices32(planes: Uint8Array, count: number): Uint32Array {\n\tconst out = new Uint32Array(count);\n\tlet prev = 0;\n\tfor (let i = 0; i < count; i++) {\n\t\tconst zz =\n\t\t\t(planes[i]! |\n\t\t\t\t(planes[count + i]! << 8) |\n\t\t\t\t(planes[count * 2 + i]! << 16) |\n\t\t\t\t(planes[count * 3 + i]! << 24)) >>>\n\t\t\t0;\n\t\tprev = (prev + unzigzag(zz)) >>> 0;\n\t\tout[i] = prev;\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\tplanarByteSplit = false\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 if (planarByteSplit) {\n\t\t// v4 layout: [Ulo][Vlo][Uhi][Vhi], each vertexCount bytes.\n\t\tconst planes = bytes.subarray(offset, offset + dataByteLength);\n\t\tconst n = vertexCount;\n\t\tuvs = new Float32Array(componentCount);\n\t\tlet qu = 0;\n\t\tlet qv = 0;\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tqu = (qu + unzigzag(planes[i]! | (planes[n * 2 + i]! << 8))) & 0xffff;\n\t\t\tqv = (qv + unzigzag(planes[n + i]! | (planes[n * 3 + i]! << 8))) & 0xffff;\n\t\t\tuvs[i * 2] = originU + qu * scaleU;\n\t\t\tuvs[i * 2 + 1] = originV + qv * scaleV;\n\t\t}\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 { decodeUtf8, fail, maybeDecompress } from './geometry.js';\n\nimport type { BinaryMeshMetadata } from './header.js';\nimport type { MaterialGroup, MeshMetadata, SerializableMaterial } from '../types.js';\n\n// ============================================================================\n// SLVM v3 CONTAINER\n// ============================================================================\n// The C# mirror and normative spec is `SlvmDocument.cs`. Layout:\n//\n// [4] magic \"SLVM\" | [4] version = 3 | [4] chunkCount\n// per chunk: [4] fourcc | [4] byteLen | payload | zero pad to 4\n//\n// Unknown chunk types are skipped by length — that's the format's extension mechanism. This\n// decoder consumes GEOM (a nested bare SLVA/SLVZ blob), TABL (the columnar object table), MATL\n// (materials JSON), TEXR (texture bytes) and CLSD (the writer's closed-solid verdict per mesh); CRVS/PNTS (file-only item geometry) and EXTN\n// (host extensions — the selva.gh one carries only Rhino curve JSON) are skipped because\n// items reach the web as JSON alongside the blob.\n\n/** \"SLVM\" little-endian. */\nexport const SLVM_MAGIC = 0x4d564c53;\nexport const SLVM_VERSION = 3;\n\nexport const CHUNK_GEOM = 0x4d4f4547; // \"GEOM\"\nexport const CHUNK_TABL = 0x4c424154; // \"TABL\"\nexport const CHUNK_MATL = 0x4c54414d; // \"MATL\"\nconst CHUNK_TEXR = 0x52584554; // \"TEXR\"\n/** One byte per mesh row: 0 = unknown, 1 = open, 2 = closed solid. */\nconst CHUNK_CLSD = 0x44534c43; // \"CLSD\"\n\n/** Reserved attr key carrying each object's identity — split out of the attr dict on read. */\nconst ID_ATTR = 'id';\n\n/** Material `map` prefix that references a TEXR chunk by index. */\nconst TEX_REF_PREFIX = 'slvm:tex:';\n\n// String-column modes (see SlvmDocument.WriteStringColumn).\nconst NAMES_NONE = 0;\nconst NAMES_SEQUENTIAL = 1;\nconst NAMES_POOL = 2;\n\nexport function isSlvmContainer(bytes: Uint8Array): boolean {\n\treturn (\n\t\tbytes.byteLength >= 4 &&\n\t\tnew DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0, true) === SLVM_MAGIC\n\t);\n}\n\nexport interface SlvmContainer {\n\t/** The GEOM payload: a bare SLVA/SLVZ blob whose embedded metadata is empty. */\n\tgeometryBlob: Uint8Array;\n\t/** Batch metadata reconstructed from TABL + MATL + EXTN, in the legacy envelope shape. */\n\tmetadata: BinaryMeshMetadata;\n}\n\n/**\n * Parses an SLVM v3 container down to its mesh geometry blob and the metadata the rest of the\n * pipeline expects. Group vertex/index windows are rebuilt as prefix sums over the table — the\n * format mandates geometry is concatenated in table order, so starts are never stored.\n *\n * @throws {VisualizationError} On bad magic, unknown version, or a truncated/malformed container.\n */\nexport function parseSlvmContainer(bytes: Uint8Array): SlvmContainer {\n\tconst chunks = readChunks(bytes);\n\n\tlet geometryBlob: Uint8Array | null = null;\n\tlet tableBytes: Uint8Array | null = null;\n\tlet materialsJson: string | null = null;\n\tlet closedFlags: Uint8Array | null = null;\n\tconst textures: Uint8Array[] = [];\n\n\tfor (const { type, payload } of chunks) {\n\t\tswitch (type) {\n\t\t\tcase CHUNK_GEOM:\n\t\t\t\tgeometryBlob = payload;\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_TABL:\n\t\t\t\ttableBytes = maybeDecompress(payload);\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_MATL:\n\t\t\t\tmaterialsJson = decodeUtf8(payload);\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_TEXR:\n\t\t\t\ttextures.push(payload);\n\t\t\t\tbreak;\n\t\t\tcase CHUNK_CLSD:\n\t\t\t\tclosedFlags = maybeDecompress(payload);\n\t\t\t\tbreak;\n\t\t\t// CRVS/PNTS, EXTN and unknown chunks: skipped.\n\t\t}\n\t}\n\n\tif (geometryBlob === null) {\n\t\tthrow fail('SLVM container has no GEOM chunk.', { chunkCount: chunks.length });\n\t}\n\n\tif (tableBytes === null) {\n\t\tthrow fail('SLVM container has no TABL chunk.', { chunkCount: chunks.length });\n\t}\n\n\tconst { groups } = parseTable(tableBytes, closedFlags);\n\tconst materials = parseMaterials(materialsJson, textures);\n\n\treturn {\n\t\tgeometryBlob,\n\t\tmetadata: { materials, groups }\n\t};\n}\n\n// ============================================================================\n// CHUNK STREAM\n// ============================================================================\n\nfunction readChunks(bytes: Uint8Array): { type: number; payload: Uint8Array }[] {\n\tconst view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\tif (bytes.byteLength < 12 || view.getUint32(0, true) !== SLVM_MAGIC) {\n\t\tthrow fail('Not an SLVM container (bad magic).', { byteLength: bytes.byteLength });\n\t}\n\n\tconst version = view.getUint32(4, true);\n\tif (version !== SLVM_VERSION) {\n\t\tthrow fail(`Unsupported SLVM version: ${version}`, { expectedVersion: SLVM_VERSION });\n\t}\n\n\tconst count = view.getUint32(8, true);\n\tconst chunks: { type: number; payload: Uint8Array }[] = [];\n\tlet offset = 12;\n\tfor (let i = 0; i < count; i++) {\n\t\tif (offset + 8 > bytes.byteLength) {\n\t\t\tthrow fail('Truncated SLVM chunk header.', { offset, chunkIndex: i });\n\t\t}\n\n\t\tconst type = view.getUint32(offset, true);\n\t\tconst len = view.getUint32(offset + 4, true);\n\t\toffset += 8;\n\t\tif (offset + len > bytes.byteLength) {\n\t\t\tthrow fail('Truncated SLVM chunk payload.', { offset, chunkIndex: i, byteLen: len });\n\t\t}\n\n\t\tchunks.push({ type, payload: bytes.subarray(offset, offset + len) });\n\t\toffset += len + ((4 - (len % 4)) % 4);\n\t}\n\n\treturn chunks;\n}\n\n// ============================================================================\n// TABL\n// ============================================================================\n\ninterface TableReader {\n\tbytes: Uint8Array;\n\tpos: number;\n}\n\nfunction readVarint(r: TableReader): number {\n\tlet value = 0;\n\tlet shift = 0;\n\tfor (;;) {\n\t\tif (r.pos >= r.bytes.byteLength || shift > 28) {\n\t\t\tthrow fail('Malformed varint in SLVM table.', { pos: r.pos });\n\t\t}\n\n\t\tconst b = r.bytes[r.pos++]!;\n\t\tvalue |= (b & 0x7f) << shift;\n\t\tif ((b & 0x80) === 0) {\n\t\t\treturn value >>> 0;\n\t\t}\n\n\t\tshift += 7;\n\t}\n}\n\nfunction readStringColumn(r: TableReader, objectCount: number, pool: string[]): string[] {\n\tconst mode = r.bytes[r.pos++];\n\tconst result = new Array<string>(objectCount);\n\tswitch (mode) {\n\t\tcase NAMES_NONE:\n\t\t\tresult.fill('');\n\t\t\tbreak;\n\t\tcase NAMES_SEQUENTIAL:\n\t\t\tfor (let i = 0; i < objectCount; i++) {\n\t\t\t\tresult[i] = String(i + 1);\n\t\t\t}\n\n\t\t\tbreak;\n\t\tcase NAMES_POOL:\n\t\t\tfor (let i = 0; i < objectCount; i++) {\n\t\t\t\tresult[i] = pool[readVarint(r)] ?? '';\n\t\t\t}\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow fail(`Unknown SLVM string column mode: ${mode}`, { pos: r.pos });\n\t}\n\n\treturn result;\n}\n\nfunction parseTable(\n\tbytes: Uint8Array,\n\tclosedFlags: Uint8Array | null\n): { groups: MaterialGroup[] } {\n\tconst r: TableReader = { bytes, pos: 0 };\n\n\tconst meshCount = readVarint(r);\n\tconst curveCount = readVarint(r);\n\tconst pointCount = readVarint(r);\n\tconst objectCount = meshCount + curveCount + pointCount;\n\n\tconst poolCount = readVarint(r);\n\tconst pool = new Array<string>(poolCount);\n\tfor (let i = 0; i < poolCount; i++) {\n\t\tconst len = readVarint(r);\n\t\tpool[i] = decodeUtf8(bytes.subarray(r.pos, r.pos + len));\n\t\tr.pos += len;\n\t}\n\n\tconst vertexCounts = new Array<number>(meshCount);\n\tconst triCounts = new Array<number>(meshCount);\n\tfor (let i = 0; i < meshCount; i++) {\n\t\tvertexCounts[i] = readVarint(r);\n\t\ttriCounts[i] = readVarint(r);\n\t}\n\n\tfor (let i = 0; i < curveCount; i++) {\n\t\treadVarint(r); // curve point counts — items don't reach this decoder\n\t}\n\n\tconst runCount = readVarint(r);\n\tconst runs: { materialId: number; meshCount: number }[] = [];\n\tfor (let i = 0; i < runCount; i++) {\n\t\truns.push({ materialId: readVarint(r), meshCount: readVarint(r) });\n\t}\n\n\tconst names = readStringColumn(r, objectCount, pool);\n\tconst layers = readStringColumn(r, objectCount, pool);\n\n\tconst attrs = new Array<Record<string, string> | undefined>(objectCount);\n\tconst attrCount = readVarint(r);\n\tfor (let a = 0; a < attrCount; a++) {\n\t\tconst key = pool[readVarint(r)] ?? '';\n\t\tconst n = readVarint(r);\n\t\tconst indices = new Array<number>(n);\n\t\tlet idx = 0;\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tidx += readVarint(r);\n\t\t\tindices[i] = idx;\n\t\t}\n\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tconst value = pool[readVarint(r)] ?? '';\n\t\t\tconst objIndex = indices[i]!;\n\t\t\t(attrs[objIndex] ??= {})[key] = value;\n\t\t}\n\t}\n\n\t// A table with meshes but no material runs (a foreign writer) gets one implicit group.\n\tconst effectiveRuns = runs.length > 0 || meshCount === 0 ? runs : [{ materialId: 0, meshCount }];\n\n\tconst groups: MaterialGroup[] = [];\n\tlet meshIndex = 0;\n\tlet vertexStart = 0;\n\tlet indexStart = 0;\n\tfor (const run of effectiveRuns) {\n\t\tconst meshes: MeshMetadata[] = [];\n\t\tfor (let i = 0; i < run.meshCount && meshIndex < meshCount; i++, meshIndex++) {\n\t\t\t// The identity rides the attr mechanism but is a first-class field to consumers.\n\t\t\tconst meshAttrs = attrs[meshIndex];\n\t\t\tconst id = meshAttrs?.[ID_ATTR];\n\t\t\tif (meshAttrs !== undefined) {\n\t\t\t\tdelete meshAttrs[ID_ATTR];\n\t\t\t}\n\n\t\t\tconst mesh: MeshMetadata = {\n\t\t\t\tid,\n\t\t\t\tname: names[meshIndex]!,\n\t\t\t\tlayer: layers[meshIndex]!,\n\t\t\t\tvertexCount: vertexCounts[meshIndex]!,\n\t\t\t\tindexCount: triCounts[meshIndex]! * 3,\n\t\t\t\tvertexStart,\n\t\t\t\tindexStart,\n\t\t\t\tmetadata: meshAttrs ?? {}\n\t\t\t};\n\t\t\tconst verdict = closedFlags?.[meshIndex];\n\t\t\tif (verdict === 1 || verdict === 2) mesh.closed = verdict === 2;\n\t\t\tmeshes.push(mesh);\n\t\t\tvertexStart += vertexCounts[meshIndex]!;\n\t\t\tindexStart += triCounts[meshIndex]! * 3;\n\t\t}\n\n\t\tgroups.push({ materialId: run.materialId, meshes });\n\t}\n\n\treturn { groups };\n}\n\n// ============================================================================\n// MATL + TEXR\n// ============================================================================\n\nfunction parseMaterials(json: string | null, textures: Uint8Array[]): SerializableMaterial[] {\n\tif (json === null) {\n\t\treturn [];\n\t}\n\n\tlet materials: SerializableMaterial[];\n\ttry {\n\t\tmaterials = (JSON.parse(json) as { materials?: SerializableMaterial[] }).materials ?? [];\n\t} catch (error) {\n\t\tthrow fail(\n\t\t\t`Failed to parse SLVM materials JSON: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t{ byteLength: json.length }\n\t\t);\n\t}\n\n\tfor (const material of materials) {\n\t\tfor (const slot of TEXTURE_SLOTS) {\n\t\t\tconst reference = material[slot];\n\t\t\tif (reference?.startsWith(TEX_REF_PREFIX)) {\n\t\t\t\tconst tex = textures[Number(reference.slice(TEX_REF_PREFIX.length))];\n\t\t\t\tif (tex !== undefined) {\n\t\t\t\t\tmaterial[slot] = textureToDataUri(tex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn materials;\n}\n\nconst TEXTURE_SLOTS = ['map', 'roughnessMap', 'normalMap'] as const;\n\n/** TEXR payload is [varint mimeLen][mime utf8][image bytes]; downstream wants a data URI. */\nfunction textureToDataUri(payload: Uint8Array): string {\n\tconst r: TableReader = { bytes: payload, pos: 0 };\n\tconst mimeLen = readVarint(r);\n\tconst mime = decodeUtf8(payload.subarray(r.pos, r.pos + mimeLen));\n\tconst data = payload.subarray(r.pos + mimeLen);\n\n\t// btoa takes a binary string; build it in chunks to stay clear of argument-count limits.\n\tlet binary = '';\n\tconst step = 0x8000;\n\tfor (let i = 0; i < data.byteLength; i += step) {\n\t\tbinary += String.fromCharCode(...data.subarray(i, i + step));\n\t}\n\n\treturn `data:${mime};base64,${btoa(binary)}`;\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_PLANAR_BYTESPLIT,\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\tdecodePlanarIndices16,\n\tdecodePlanarIndices32,\n\tdecodePlanarVertices,\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';\nimport { isSlvmContainer, parseSlvmContainer } from './binary/slvm.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\tFLAG_PLANAR_BYTESPLIT,\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. The field layout and every flag bit are\n * specified in `binary/header.ts`, next to the constants this reads.\n *\n * Returns absolute values: quantized vertices stay int16 (dequantize with `origin + (q + 32767) *\n * scale`, which is what Three.js `BufferAttribute(arr, 3, true)` expects when the per-mesh\n * transform encodes origin and scale), but the delta filter and planar byte-split are both undone\n * here — consumers never see either.\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\treturn decodeRawMeshBatch(parseBinaryMeshBatchRaw(input));\n}\n\n/** Second half of {@link parseBinaryMeshBatch}, for a caller that already holds the raw view. */\nexport function decodeRawMeshBatch(raw: RawBinaryMeshBatch): ParsedBinaryMeshBatch {\n\tlet vertices: Int16Array | Float32Array;\n\tif (raw.isFloat32) {\n\t\tvertices = raw.vertexData as Float32Array;\n\t} else if (raw.planarByteSplit) {\n\t\tvertices = decodePlanarVertices(raw.vertexData as Uint8Array, raw.vertexCount);\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: Uint16Array | Uint32Array;\n\tif (raw.planarByteSplit) {\n\t\tconst planes = raw.indexData as Uint8Array;\n\t\tindices = raw.uint16Indices\n\t\t\t? decodePlanarIndices16(planes, planes.length / 2)\n\t\t\t: decodePlanarIndices32(planes, planes.length / 4);\n\t} else if (raw.deltaEncoded) {\n\t\tindices =\n\t\t\traw.indexData instanceof Uint16Array\n\t\t\t\t? decodeDeltaIndices16(raw.indexData)\n\t\t\t\t: decodeDeltaIndices32(raw.indexData as Uint32Array);\n\t} else {\n\t\tindices = raw.indexData as Uint16Array | Uint32Array;\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/**\n\t * Wire vertex components: byte planes (Uint8) when `planarByteSplit`, zigzag deltas (Uint16)\n\t * when `deltaEncoded` and not float32.\n\t */\n\tvertexData: Uint8Array | Uint16Array | Int16Array | Float32Array;\n\t/**\n\t * Wire indices: byte planes (Uint8) when `planarByteSplit`, else zigzag deltas when\n\t * `deltaEncoded`. NOT validated against vertexCount.\n\t */\n\tindexData: Uint8Array | Uint16Array | Uint32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\t/** v4 byte-plane layout on the delta-filtered streams — see FLAG_PLANAR_BYTESPLIT. */\n\tplanarByteSplit: boolean;\n\t/** Width of the wire indices (needed since planar `indexData` is a bare byte stream). */\n\tuint16Indices: 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 rawInput = toUint8Array(input);\n\n\t// An SLVM v3 container nests a bare SLVA/SLVZ blob as its GEOM chunk and carries the object\n\t// table/materials as binary chunks — unwrap, decode the inner blob through the path below,\n\t// and overlay the container's metadata (the inner blob's own metadata is empty).\n\tif (isSlvmContainer(rawInput)) {\n\t\tconst container = parseSlvmContainer(rawInput);\n\t\tconst raw = parseBinaryMeshBatchRaw(container.geometryBlob);\n\t\traw.metadata = container.metadata;\n\t\treturn raw;\n\t}\n\n\tconst bytes = maybeDecompress(rawInput);\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\t// A blob nested inside an SLVM container carries no metadata of its own (metadataLen = 0);\n\t\t// the container's TABL/MATL/EXTN chunks supply it after this parse returns.\n\t\tmetadata =\n\t\t\tmetadataLen === 0\n\t\t\t\t? ({} as BinaryMeshMetadata)\n\t\t\t\t: (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 planarByteSplit = (flags & FLAG_PLANAR_BYTESPLIT) !== 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: Uint8Array | Uint16Array | Int16Array | Float32Array;\n\tif (useFloat32) {\n\t\tvertexData = readFloat32Vertices(bytes.buffer, absoluteOffset, componentCount);\n\t} else if (planarByteSplit) {\n\t\t// v4 byte planes — no alignment requirement, view the bytes directly.\n\t\tvertexData = bytes.subarray(offset, offset + verticesByteLength);\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 = planarByteSplit\n\t\t? bytes.subarray(offset, offset + indicesByteLength)\n\t\t: useUint16Indices\n\t\t\t? readUint16Array(bytes.buffer, bytes.byteOffset + offset, indexCount)\n\t\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, planarByteSplit);\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\tplanarByteSplit,\n\t\tuint16Indices: useUint16Indices,\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 * True when every member of a geometry is a closed, consistently wound, outward-facing triangle\n * surface, so its back faces can never be seen and the material can cull them.\n *\n * Drawing them anyway is not just overdraw. A thin closed part (a 0.5 mm sheet) puts its inside\n * faces within one depth-buffer step of its outside ones, and once precision drops below the\n * thickness they punch through as dark jagged triangles. Culled, they never compete.\n *\n * Members are tested apart, because two closed parts touching along an edge share it four ways and\n * would read as open together. Positions are welded first: Rhino meshes each Brep face separately,\n * so a solid's seams carry duplicate vertices that only position identifies. Any\n * doubt (an unwelded seam, a non-manifold edge, inward winding) answers false, which keeps the\n * double-sided default.\n *\n * Self-contained (no outer captures besides `Math`) so the mesh-assembly worker can embed it via\n * `Function.prototype.toString`.\n *\n * @param memberIndexCounts Index count of each member, in order; members are concatenated in\n * `indices`.\n */\nexport function isClosedSolid(\n\tpositions: Float32Array,\n\tindices: Uint32Array,\n\tmemberIndexCounts: number[]\n): boolean {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tif (memberIndexCounts.length === 0) return false;\n\n\tconst tableSizeFor = (count: number): number => {\n\t\tlet size = 16;\n\t\twhile (size < count * 2) size *= 2;\n\t\treturn size;\n\t};\n\n\t// Welding works per vertex, not per corner: a vertex is shared by ~6 corners, and welding each\n\t// corner on its own could also map one vertex to two different canonicals. Member m tags a\n\t// vertex 2m+1 once seen and 2m+2 once welded, so `canonicalOf` needs no reset between members.\n\t// Canonicals are numbered per member (0, 1, …), which keeps the edge arrays member-sized.\n\tconst vertexCount = positions.length / 3;\n\tconst tag = new Int32Array(vertexCount);\n\tconst canonicalOf = new Int32Array(vertexCount);\n\n\t// Per-member tables, grown to the largest member and reused: a fresh set per member is about\n\t// 1 MB of zeroed memory each.\n\tlet weldVertex: Int32Array = new Int32Array(0);\n\tlet cellX: Int32Array = new Int32Array(0);\n\tlet cellY: Int32Array = new Int32Array(0);\n\tlet cellZ: Int32Array = new Int32Array(0);\n\tlet welded: Int32Array = new Int32Array(0);\n\tlet vertexOf: Int32Array = new Int32Array(0);\n\tlet outStart: Int32Array = new Int32Array(0);\n\tlet outTo: Int32Array = new Int32Array(0);\n\tlet cursor: Int32Array = new Int32Array(0);\n\tconst grown = (array: Int32Array, length: number): Int32Array =>\n\t\tarray.length >= length ? array : new Int32Array(length);\n\n\tconst memberIsClosed = (member: number, start: number, count: number): boolean => {\n\t\t// A tetrahedron is the smallest closed surface.\n\t\tif (count < 12 || count % 3 !== 0) return false;\n\n\t\tconst seen = 2 * member + 1;\n\t\tconst done = seen + 1;\n\n\t\t// --- Weld: map each vertex to the first vertex at (nearly) its position ------------------\n\t\t// Rhino meshes each Brep face on its own and stores float32 vertices, so the two copies of a\n\t\t// seam point can round one or two float32 steps apart. Snap to a grid two steps wide at the\n\t\t// part's largest coordinate: wide enough for that, far below its real features (sliver\n\t\t// triangles along a fillet run down to ~1 µm, which a wider weld collapses).\n\t\tlet magnitude = 0;\n\t\tlet uniqueVertices = 0;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst v = indices[start + i]!;\n\t\t\tif (tag[v] === seen) continue;\n\t\t\ttag[v] = seen;\n\t\t\tuniqueVertices++;\n\t\t\tmagnitude = Math.max(\n\t\t\t\tmagnitude,\n\t\t\t\tMath.abs(positions[v * 3]!),\n\t\t\t\tMath.abs(positions[v * 3 + 1]!),\n\t\t\t\tMath.abs(positions[v * 3 + 2]!)\n\t\t\t);\n\t\t}\n\t\tif (!(magnitude > 0) || !Number.isFinite(magnitude)) return false;\n\t\tconst step = 2 * Math.pow(2, Math.floor(Math.log2(magnitude)) - 23);\n\t\tconst inverseCell = 1 / step;\n\n\t\tconst weldSize = tableSizeFor(uniqueVertices);\n\t\tconst weldMask = weldSize - 1;\n\t\t// Only `weldVertex` is read before it is written: a cell's coordinates count once its slot is taken.\n\t\tweldVertex = grown(weldVertex, weldSize);\n\t\tweldVertex.fill(-1, 0, weldSize);\n\t\tcellX = grown(cellX, weldSize);\n\t\tcellY = grown(cellY, weldSize);\n\t\tcellZ = grown(cellZ, weldSize);\n\t\tconst cellSlot = (x: number, y: number, z: number): number => {\n\t\t\tlet h = Math.imul(x, 0x9e3779b1) ^ Math.imul(y, 0x85ebca77) ^ Math.imul(z, 0xc2b2ae3d);\n\t\t\th = (h ^ (h >>> 15)) & weldMask;\n\t\t\twhile (weldVertex[h] !== -1 && (cellX[h] !== x || cellY[h] !== y || cellZ[h] !== z)) {\n\t\t\t\th = (h + 1) & weldMask;\n\t\t\t}\n\t\t\treturn h;\n\t\t};\n\t\t// The cell across the nearer boundary, for a point close enough to it that its near-copy could\n\t\t// have rounded into the other cell.\n\t\tconst neighbour = (scaled: number, cell: number): number => {\n\t\t\tconst offset = scaled - cell;\n\t\t\treturn offset > 0.25 ? cell + 1 : offset < -0.25 ? cell - 1 : cell;\n\t\t};\n\n\t\twelded = grown(welded, count);\n\t\t// The vertex each canonical stands for, to read its position.\n\t\tvertexOf = grown(vertexOf, uniqueVertices);\n\t\tlet canonicals = 0;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst v = indices[start + i]!;\n\t\t\tif (tag[v] === done) {\n\t\t\t\twelded[i] = canonicalOf[v]!;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst sx = positions[v * 3]! * inverseCell;\n\t\t\tconst sy = positions[v * 3 + 1]! * inverseCell;\n\t\t\tconst sz = positions[v * 3 + 2]! * inverseCell;\n\t\t\tconst x = Math.round(sx);\n\t\t\tconst y = Math.round(sy);\n\t\t\tconst z = Math.round(sz);\n\t\t\tconst nx = neighbour(sx, x);\n\t\t\tconst ny = neighbour(sy, y);\n\t\t\tconst nz = neighbour(sz, z);\n\n\t\t\tlet canonical = -1;\n\t\t\tlet ownSlot = -1;\n\t\t\tfor (let combo = 0; combo < 8 && canonical === -1; combo++) {\n\t\t\t\tif ((combo & 1 && nx === x) || (combo & 2 && ny === y) || (combo & 4 && nz === z)) continue;\n\t\t\t\tconst h = cellSlot(combo & 1 ? nx : x, combo & 2 ? ny : y, combo & 4 ? nz : z);\n\t\t\t\tif (combo === 0) ownSlot = h;\n\t\t\t\tif (weldVertex[h] !== -1) canonical = weldVertex[h]!;\n\t\t\t}\n\t\t\tif (canonical === -1) {\n\t\t\t\t// Nothing matched, so the table is as the first probe left it: that slot is still free.\n\t\t\t\tconst h = ownSlot;\n\t\t\t\tcanonical = canonicals++;\n\t\t\t\tweldVertex[h] = canonical;\n\t\t\t\tcellX[h] = x;\n\t\t\t\tcellY[h] = y;\n\t\t\t\tcellZ[h] = z;\n\t\t\t\tvertexOf[canonical] = v;\n\t\t\t}\n\t\t\ttag[v] = done;\n\t\t\tcanonicalOf[v] = canonical;\n\t\t\twelded[i] = canonical;\n\t\t}\n\n\t\t// --- Directed edges: each must occur once, and its reverse once -------------------------\n\t\t// That is exactly \"every edge joins two triangles that agree on winding\", i.e. closed and\n\t\t// consistently oriented. Each canonical's outgoing edges sit together, so both checks scan\n\t\t// its handful of neighbours rather than hashing every edge.\n\t\toutStart = grown(outStart, canonicals + 1);\n\t\toutStart.fill(0, 0, canonicals + 1);\n\t\tlet triangles = 0;\n\t\tfor (let i = 0; i < count; i += 3) {\n\t\t\tconst a = welded[i]!;\n\t\t\tconst b = welded[i + 1]!;\n\t\t\tconst c = welded[i + 2]!;\n\t\t\t// Collapsed by welding (or authored that way): no area, nothing to see from either side.\n\t\t\tif (a === b || b === c || c === a) continue;\n\t\t\toutStart[a + 1] += 1;\n\t\t\toutStart[b + 1] += 1;\n\t\t\toutStart[c + 1] += 1;\n\t\t\ttriangles++;\n\t\t}\n\t\tif (triangles < 4) return false;\n\t\tfor (let k = 0; k < canonicals; k++) outStart[k + 1] += outStart[k]!;\n\t\toutTo = grown(outTo, outStart[canonicals]!);\n\t\tcursor = grown(cursor, canonicals);\n\t\tcursor.set(outStart.subarray(0, canonicals));\n\t\tfor (let i = 0; i < count; i += 3) {\n\t\t\tconst a = welded[i]!;\n\t\t\tconst b = welded[i + 1]!;\n\t\t\tconst c = welded[i + 2]!;\n\t\t\tif (a === b || b === c || c === a) continue;\n\t\t\toutTo[cursor[a]!] = b;\n\t\t\tcursor[a] += 1;\n\t\t\toutTo[cursor[b]!] = c;\n\t\t\tcursor[b] += 1;\n\t\t\toutTo[cursor[c]!] = a;\n\t\t\tcursor[c] += 1;\n\t\t}\n\t\tfor (let a = 0; a < canonicals; a++) {\n\t\t\tconst first = outStart[a]!;\n\t\t\tconst end = outStart[a + 1]!;\n\t\t\tfor (let k = first; k < end; k++) {\n\t\t\t\tconst b = outTo[k]!;\n\t\t\t\tfor (let j = first; j < k; j++) {\n\t\t\t\t\tif (outTo[j] === b) return false; // second use of one directed edge\n\t\t\t\t}\n\t\t\t\tlet reversed = false;\n\t\t\t\tfor (let j = outStart[b]!, bEnd = outStart[b + 1]!; j < bEnd && !reversed; j++) {\n\t\t\t\t\treversed = outTo[j] === a;\n\t\t\t\t}\n\t\t\t\tif (!reversed) return false;\n\t\t\t}\n\t\t}\n\n\t\t// --- Outward: counter-clockwise winding seen from outside encloses positive volume -------\n\t\tconst o = vertexOf[welded[0]!]! * 3;\n\t\tconst ox = positions[o]!;\n\t\tconst oy = positions[o + 1]!;\n\t\tconst oz = positions[o + 2]!;\n\t\tlet volume = 0;\n\t\tfor (let i = 0; i < count; i += 3) {\n\t\t\tconst a = vertexOf[welded[i]!]! * 3;\n\t\t\tconst b = vertexOf[welded[i + 1]!]! * 3;\n\t\t\tconst c = vertexOf[welded[i + 2]!]! * 3;\n\t\t\tconst ax = positions[a]! - ox;\n\t\t\tconst ay = positions[a + 1]! - oy;\n\t\t\tconst az = positions[a + 2]! - oz;\n\t\t\tconst bx = positions[b]! - ox;\n\t\t\tconst by = positions[b + 1]! - oy;\n\t\t\tconst bz = positions[b + 2]! - oz;\n\t\t\tconst cx = positions[c]! - ox;\n\t\t\tconst cy = positions[c + 1]! - oy;\n\t\t\tconst cz = positions[c + 2]! - oz;\n\t\t\tvolume += ax * (by * cz - bz * cy) + ay * (bz * cx - bx * cz) + az * (bx * cy - by * cx);\n\t\t}\n\t\treturn volume > 0;\n\t};\n\n\tlet start = 0;\n\tfor (let member = 0; member < memberIndexCounts.length; member++) {\n\t\tconst count = memberIndexCounts[member]!;\n\t\tif (!memberIsClosed(member, start, count)) return false;\n\t\tstart += count;\n\t}\n\treturn true;\n}\n","export interface CreasedGeometry {\n\tpositions: Float32Array;\n\tnormals: Float32Array;\n\tindices: Uint32Array;\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\n/**\n * Vertex normals that stay smooth across gentle curvature and break at creases, splitting a vertex\n * wherever the faces sharing it disagree by more than the crease angle.\n *\n * The writer welds coincident vertices, and seams between Brep faces often come out welded\n * across a 90° fold. A plain average there tilts every normal along the fold halfway round it,\n * and the long thin triangles of a sheet-metal strip carry that tilt metres across a flat face:\n * streaks, an X around every hole, a whole strip shaded darker than its neighbour.\n *\n * Face normals are area-weighted: across a tangent bend the bend's facets are narrow, so they barely\n * tilt the wide flat face beside them. Weighting by corner angle instead let a bend facet's corner\n * tilt a flat band by up to 23°, enough to flip a metal's reflection from sky to ground.\n *\n * `indices` is rewritten in place to point split corners at their new vertices, which take the\n * slots of welded-away duplicates before any are appended; the index count never changes, so index\n * windows stay valid.\n *\n * Self-contained (no outer captures besides `Math`) so the mesh-assembly worker can embed it via\n * `Function.prototype.toString`.\n */\nexport function splitCreases(\n\tpositions: Float32Array,\n\tindices: Uint32Array,\n\tuvs: Float32Array | null,\n\tcolors: Uint8Array | null\n): CreasedGeometry {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\t// Faces further apart than this split their shared vertex. Above a coarse render mesh's step\n\t// around a fillet (Rhino's defaults stay under 20°), below any real fold.\n\tconst CREASE_COS = Math.cos((30 * Math.PI) / 180);\n\n\tconst vertexCount = positions.length / 3;\n\tconst cornerCount = indices.length;\n\tconst triangleCount = cornerCount / 3;\n\n\t// --- Weld identical vertices first ------------------------------------------------------------\n\t// Which seams arrive welded is the writer's call, and it isn't consistent: it compares normals\n\t// at the seam within a tolerance, so two identical strips can come out one welded, one split\n\t// along the same tangent bend, and then reflect visibly differently. Welding every identical\n\t// vertex (position, UV and colour alike) here makes the crease angle the only thing that\n\t// decides. The orphaned duplicates are reused below as slots for split copies.\n\tlet orphans = new Uint32Array(0);\n\t{\n\t\tconst posBits = new Uint32Array(positions.buffer, positions.byteOffset, positions.length);\n\t\tconst uvBits = uvs ? new Uint32Array(uvs.buffer, uvs.byteOffset, uvs.length) : null;\n\t\t// -0 and +0 are one position with two bit patterns.\n\t\tconst word = (bits: Uint32Array, i: number) => (bits[i] === 0x80000000 ? 0 : bits[i]!);\n\t\tlet size = 16;\n\t\twhile (size < vertexCount * 2) size *= 2;\n\t\tconst mask = size - 1;\n\t\tconst slots = new Int32Array(size).fill(-1);\n\t\tconst canonical = new Uint32Array(vertexCount);\n\t\tlet merged = false;\n\t\tfor (let v = 0; v < vertexCount; v++) {\n\t\t\tconst x = word(posBits, v * 3);\n\t\t\tconst y = word(posBits, v * 3 + 1);\n\t\t\tconst z = word(posBits, v * 3 + 2);\n\t\t\tlet h = Math.imul(x, 0x9e3779b1) ^ Math.imul(y, 0x85ebca77) ^ Math.imul(z, 0xc2b2ae3d);\n\t\t\th = (h ^ (h >>> 15)) & mask;\n\t\t\tfor (;;) {\n\t\t\t\tconst other = slots[h]!;\n\t\t\t\tif (other === -1) {\n\t\t\t\t\tslots[h] = v;\n\t\t\t\t\tcanonical[v] = v;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tword(posBits, other * 3) === x &&\n\t\t\t\t\tword(posBits, other * 3 + 1) === y &&\n\t\t\t\t\tword(posBits, other * 3 + 2) === z &&\n\t\t\t\t\t(!colors ||\n\t\t\t\t\t\t(colors[other * 3] === colors[v * 3] &&\n\t\t\t\t\t\t\tcolors[other * 3 + 1] === colors[v * 3 + 1] &&\n\t\t\t\t\t\t\tcolors[other * 3 + 2] === colors[v * 3 + 2])) &&\n\t\t\t\t\t(!uvBits ||\n\t\t\t\t\t\t(uvBits[other * 2] === uvBits[v * 2] && uvBits[other * 2 + 1] === uvBits[v * 2 + 1]))\n\t\t\t\t) {\n\t\t\t\t\tcanonical[v] = other;\n\t\t\t\t\tmerged = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\th = (h + 1) & mask;\n\t\t\t}\n\t\t}\n\t\tif (merged) {\n\t\t\tfor (let i = 0; i < cornerCount; i++) indices[i] = canonical[indices[i]!]!;\n\t\t\tlet orphanCount = 0;\n\t\t\tfor (let v = 0; v < vertexCount; v++) if (canonical[v] !== v) orphanCount++;\n\t\t\torphans = new Uint32Array(orphanCount);\n\t\t\tfor (let v = 0, k = 0; v < vertexCount; v++) if (canonical[v] !== v) orphans[k++] = v;\n\t\t}\n\t}\n\n\t// --- Unit face normals and areas ---------------------------------------------------------------\n\tconst faceNormals = new Float32Array(triangleCount * 3);\n\tconst faceAreas = new Float32Array(triangleCount);\n\tfor (let t = 0; t < triangleCount; t++) {\n\t\tconst a = indices[t * 3]! * 3;\n\t\tconst b = indices[t * 3 + 1]! * 3;\n\t\tconst c = indices[t * 3 + 2]! * 3;\n\t\tconst abx = positions[b]! - positions[a]!;\n\t\tconst aby = positions[b + 1]! - positions[a + 1]!;\n\t\tconst abz = positions[b + 2]! - positions[a + 2]!;\n\t\tconst acx = positions[c]! - positions[a]!;\n\t\tconst acy = positions[c + 1]! - positions[a + 1]!;\n\t\tconst acz = positions[c + 2]! - positions[a + 2]!;\n\t\tconst nx = aby * acz - abz * acy;\n\t\tconst ny = abz * acx - abx * acz;\n\t\tconst nz = abx * acy - aby * acx;\n\t\tconst length = Math.sqrt(nx * nx + ny * ny + nz * nz);\n\t\tif (length === 0) continue; // degenerate: no normal, no weight\n\t\tfaceNormals[t * 3] = nx / length;\n\t\tfaceNormals[t * 3 + 1] = ny / length;\n\t\tfaceNormals[t * 3 + 2] = nz / length;\n\t\tfaceAreas[t] = length;\n\t}\n\n\t// --- Corners grouped by vertex (CSR) ----------------------------------------------------------\n\tconst cornerStart = new Uint32Array(vertexCount + 1);\n\tfor (let i = 0; i < cornerCount; i++) cornerStart[indices[i]! + 1] += 1;\n\tfor (let v = 0; v < vertexCount; v++) cornerStart[v + 1] += cornerStart[v]!;\n\tconst cornersOf = new Uint32Array(cornerCount);\n\tconst fill = cornerStart.slice(0, vertexCount);\n\tfor (let i = 0; i < cornerCount; i++) {\n\t\tconst v = indices[i]!;\n\t\tcornersOf[fill[v]!] = i;\n\t\tfill[v] += 1;\n\t}\n\n\t// --- Group each vertex's corners by face normal -----------------------------------------------\n\t// A corner joins the first group whose founding normal is within the crease angle. Degenerate\n\t// faces (zero normal) join group 0.\n\tlet maxDegree = 0;\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tmaxDegree = Math.max(maxDegree, cornerStart[v + 1]! - cornerStart[v]!);\n\t}\n\tconst groupOf = new Uint32Array(cornerCount);\n\tconst groupCount = new Uint32Array(vertexCount);\n\tconst founders = new Float32Array(Math.max(1, maxDegree) * 3);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tlet groups = 0;\n\t\tfor (let k = cornerStart[v]!; k < cornerStart[v + 1]!; k++) {\n\t\t\tconst corner = cornersOf[k]!;\n\t\t\tconst f = Math.floor(corner / 3) * 3;\n\t\t\tconst nx = faceNormals[f]!;\n\t\t\tconst ny = faceNormals[f + 1]!;\n\t\t\tconst nz = faceNormals[f + 2]!;\n\t\t\tlet group = -1;\n\t\t\tif (nx === 0 && ny === 0 && nz === 0) {\n\t\t\t\tgroup = 0;\n\t\t\t} else {\n\t\t\t\tfor (let g = 0; g < groups; g++) {\n\t\t\t\t\tconst dot = founders[g * 3]! * nx + founders[g * 3 + 1]! * ny + founders[g * 3 + 2]! * nz;\n\t\t\t\t\tif (dot >= CREASE_COS) {\n\t\t\t\t\t\tgroup = g;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (group === -1) {\n\t\t\t\tgroup = groups;\n\t\t\t\tfounders[groups * 3] = nx;\n\t\t\t\tfounders[groups * 3 + 1] = ny;\n\t\t\t\tfounders[groups * 3 + 2] = nz;\n\t\t\t\tgroups++;\n\t\t\t}\n\t\t\tgroupOf[corner] = group;\n\t\t}\n\t\tgroupCount[v] = Math.max(1, groups);\n\t}\n\n\t// --- Split: group 0 keeps the vertex, later groups get copies -------------------------------\n\t// Copies fill the weld's orphaned slots first and only then append. Appending every copy left\n\t// a box scatter at 400k vertices for 240k used, a third of every attribute unreferenced.\n\tconst extraStart = new Uint32Array(vertexCount + 1);\n\tfor (let v = 0; v < vertexCount; v++) extraStart[v + 1] = extraStart[v]! + groupCount[v]! - 1;\n\tconst extra = extraStart[vertexCount]!;\n\tconst total = vertexCount + Math.max(0, extra - orphans.length);\n\tconst copySlot = new Uint32Array(extra);\n\tfor (let k = 0; k < extra; k++) {\n\t\tcopySlot[k] = k < orphans.length ? orphans[k]! : vertexCount + k - orphans.length;\n\t}\n\n\tlet outPositions = positions;\n\tlet outUvs = uvs;\n\tlet outColors = colors;\n\tif (extra > 0) {\n\t\toutPositions = new Float32Array(total * 3);\n\t\toutPositions.set(positions);\n\t\toutUvs = uvs ? new Float32Array(total * 2) : null;\n\t\tif (outUvs && uvs) outUvs.set(uvs);\n\t\toutColors = colors ? new Uint8Array(total * 3) : null;\n\t\tif (outColors && colors) outColors.set(colors);\n\t\tfor (let v = 0; v < vertexCount; v++) {\n\t\t\tfor (let g = 1; g < groupCount[v]!; g++) {\n\t\t\t\tconst copy = copySlot[extraStart[v]! + g - 1]!;\n\t\t\t\toutPositions[copy * 3] = positions[v * 3]!;\n\t\t\t\toutPositions[copy * 3 + 1] = positions[v * 3 + 1]!;\n\t\t\t\toutPositions[copy * 3 + 2] = positions[v * 3 + 2]!;\n\t\t\t\tif (outUvs && uvs) {\n\t\t\t\t\toutUvs[copy * 2] = uvs[v * 2]!;\n\t\t\t\t\toutUvs[copy * 2 + 1] = uvs[v * 2 + 1]!;\n\t\t\t\t}\n\t\t\t\tif (outColors && colors) {\n\t\t\t\t\toutColors[copy * 3] = colors[v * 3]!;\n\t\t\t\t\toutColors[copy * 3 + 1] = colors[v * 3 + 1]!;\n\t\t\t\t\toutColors[copy * 3 + 2] = colors[v * 3 + 2]!;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst normals = new Float32Array(total * 3);\n\tfor (let t = 0; t < triangleCount; t++) {\n\t\tconst w = faceAreas[t]!;\n\t\tconst nx = faceNormals[t * 3]! * w;\n\t\tconst ny = faceNormals[t * 3 + 1]! * w;\n\t\tconst nz = faceNormals[t * 3 + 2]! * w;\n\t\tfor (let corner = t * 3; corner < t * 3 + 3; corner++) {\n\t\t\tlet target = indices[corner]!;\n\t\t\tconst g = groupOf[corner]!;\n\t\t\tif (g !== 0) {\n\t\t\t\ttarget = copySlot[extraStart[target]! + g - 1]!;\n\t\t\t\tindices[corner] = target;\n\t\t\t}\n\t\t\tnormals[target * 3] += nx;\n\t\t\tnormals[target * 3 + 1] += ny;\n\t\t\tnormals[target * 3 + 2] += nz;\n\t\t}\n\t}\n\tfor (let i = 0; i < normals.length; i += 3) {\n\t\tconst x = normals[i]!;\n\t\tconst y = normals[i + 1]!;\n\t\tconst z = normals[i + 2]!;\n\t\tconst length = Math.sqrt(x * x + y * y + z * z) || 1;\n\t\tnormals[i] = x / length;\n\t\tnormals[i + 1] = y / length;\n\t\tnormals[i + 2] = z / length;\n\t}\n\n\treturn { positions: outPositions, normals, indices, uvs: outUvs, colors: outColors };\n}\n","/**\n * Per-vertex tangents for three's `tangent` attribute: xyz along increasing U, w the handedness that\n * points `cross(normal, tangent)` along increasing V.\n *\n * Without the attribute three rebuilds the frame per pixel from screen-space UV derivatives, which\n * is noisy on long thin triangles. three's own `computeTangents()` needs V to vary on every\n * triangle and leaves a zero tangent where it doesn't (a tube centred on its grain axis has constant\n * V), and a zero tangent renders NaN-black. Only U's gradient is needed here, so V can be anything.\n *\n * Self-contained (no outer captures besides `Math`) so the mesh-assembly worker can embed it via\n * `Function.prototype.toString`.\n */\nexport function grainTangents(\n\tpositions: Float32Array,\n\tnormals: Float32Array,\n\tindices: Uint32Array,\n\tuvs: Float32Array\n): Float32Array {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tconst vertexCount = positions.length / 3;\n\tconst gradU = new Float32Array(vertexCount * 3);\n\tconst gradV = new Float32Array(vertexCount * 3);\n\n\t// Area-weighted gradients of U and V over each triangle. With e1, e2 the edges from corner a and\n\t// n = e1 × e2, a linear f has ∇f = (df1 (e2 × n) + df2 (n × e1)) / |n|²; times the area |n| / 2.\n\tfor (let t = 0; t < indices.length; t += 3) {\n\t\tconst a = indices[t]!;\n\t\tconst b = indices[t + 1]!;\n\t\tconst c = indices[t + 2]!;\n\t\tconst e1x = positions[b * 3]! - positions[a * 3]!;\n\t\tconst e1y = positions[b * 3 + 1]! - positions[a * 3 + 1]!;\n\t\tconst e1z = positions[b * 3 + 2]! - positions[a * 3 + 2]!;\n\t\tconst e2x = positions[c * 3]! - positions[a * 3]!;\n\t\tconst e2y = positions[c * 3 + 1]! - positions[a * 3 + 1]!;\n\t\tconst e2z = positions[c * 3 + 2]! - positions[a * 3 + 2]!;\n\t\tconst nx = e1y * e2z - e1z * e2y;\n\t\tconst ny = e1z * e2x - e1x * e2z;\n\t\tconst nz = e1x * e2y - e1y * e2x;\n\t\tconst length = Math.sqrt(nx * nx + ny * ny + nz * nz);\n\t\tif (length === 0) continue;\n\t\t// e2 × n and n × e1, pre-divided by |n| for the area weighting.\n\t\tconst px = (e2y * nz - e2z * ny) / length;\n\t\tconst py = (e2z * nx - e2x * nz) / length;\n\t\tconst pz = (e2x * ny - e2y * nx) / length;\n\t\tconst qx = (ny * e1z - nz * e1y) / length;\n\t\tconst qy = (nz * e1x - nx * e1z) / length;\n\t\tconst qz = (nx * e1y - ny * e1x) / length;\n\t\tconst du1 = uvs[b * 2]! - uvs[a * 2]!;\n\t\tconst du2 = uvs[c * 2]! - uvs[a * 2]!;\n\t\tconst dv1 = uvs[b * 2 + 1]! - uvs[a * 2 + 1]!;\n\t\tconst dv2 = uvs[c * 2 + 1]! - uvs[a * 2 + 1]!;\n\t\tconst ux = du1 * px + du2 * qx;\n\t\tconst uy = du1 * py + du2 * qy;\n\t\tconst uz = du1 * pz + du2 * qz;\n\t\tconst vx = dv1 * px + dv2 * qx;\n\t\tconst vy = dv1 * py + dv2 * qy;\n\t\tconst vz = dv1 * pz + dv2 * qz;\n\t\tfor (let k = 0; k < 3; k++) {\n\t\t\tconst v = indices[t + k]!;\n\t\t\tgradU[v * 3] += ux;\n\t\t\tgradU[v * 3 + 1] += uy;\n\t\t\tgradU[v * 3 + 2] += uz;\n\t\t\tgradV[v * 3] += vx;\n\t\t\tgradV[v * 3 + 1] += vy;\n\t\t\tgradV[v * 3 + 2] += vz;\n\t\t}\n\t}\n\n\tconst tangents = new Float32Array(vertexCount * 4);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tconst nx = normals[v * 3]!;\n\t\tconst ny = normals[v * 3 + 1]!;\n\t\tconst nz = normals[v * 3 + 2]!;\n\t\t// Into the vertex's tangent plane: the averaged gradient leans off it on curved surfaces.\n\t\tconst along = gradU[v * 3]! * nx + gradU[v * 3 + 1]! * ny + gradU[v * 3 + 2]! * nz;\n\t\tlet tx = gradU[v * 3]! - along * nx;\n\t\tlet ty = gradU[v * 3 + 1]! - along * ny;\n\t\tlet tz = gradU[v * 3 + 2]! - along * nz;\n\t\tlet length = Math.sqrt(tx * tx + ty * ty + tz * tz);\n\t\tif (!(length > 1e-12)) {\n\t\t\t// U constant here: any direction in the plane beats a zero tangent.\n\t\t\tconst ax = Math.abs(nx) < 0.9 ? 1 : 0;\n\t\t\tconst ay = 1 - ax;\n\t\t\ttx = ay * nz;\n\t\t\tty = -ax * nz;\n\t\t\ttz = ax * ny - ay * nx;\n\t\t\tlength = Math.sqrt(tx * tx + ty * ty + tz * tz) || 1;\n\t\t}\n\t\ttx /= length;\n\t\tty /= length;\n\t\ttz /= length;\n\t\tconst bx = ny * tz - nz * ty;\n\t\tconst by = nz * tx - nx * tz;\n\t\tconst bz = nx * ty - ny * tx;\n\t\tconst handedness = bx * gradV[v * 3]! + by * gradV[v * 3 + 1]! + bz * gradV[v * 3 + 2]!;\n\t\ttangents[v * 4] = tx;\n\t\ttangents[v * 4 + 1] = ty;\n\t\ttangents[v * 4 + 2] = tz;\n\t\ttangents[v * 4 + 3] = handedness < 0 ? -1 : 1;\n\t}\n\treturn tangents;\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, and slices/rebases per-geometry\n * windows. {@link finishGeometry} then adds normals and the closed-solid flag.\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\nimport { isClosedSolid } from './closed-solid.js';\nimport { splitCreases, type CreasedGeometry } from './crease-normals.js';\nimport { grainTangents } from './grain-tangents.js';\n\nexport interface AssemblyWindow {\n\tvertexStart: number;\n\tvertexCount: number;\n\tindexStart: number;\n\tindexCount: number;\n\t/** The writer's closed-solid verdict for this member, when it sent one. */\n\tclosed?: boolean;\n}\n\nexport interface AssemblyJob {\n\tkind: 'merged' | 'single';\n\twindows: AssemblyWindow[];\n}\n\nexport interface AssemblyInput {\n\t/**\n\t * Raw wire vertex components: byte planes (Uint8) when planar-byte-split, zigzag deltas\n\t * (Uint16) when delta-encoded, else absolute.\n\t */\n\tvertexData: Uint8Array | Uint16Array | Int16Array | Float32Array;\n\tisFloat32: boolean;\n\tdeltaEncoded: boolean;\n\t/** v4 byte-plane layout on the delta-filtered streams — see FLAG_PLANAR_BYTESPLIT. */\n\tplanarByteSplit: boolean;\n\t/** Wire index width; required because planar `indexData` is a bare byte stream. */\n\tuint16Indices: boolean;\n\torigin: [number, number, number];\n\tscale: [number, number, number];\n\t/** Raw wire indices: byte planes when planar-byte-split, zigzag deltas when delta-encoded. */\n\tindexData: Uint8Array | 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\tindices: Uint32Array;\n\tuvs: Float32Array | null;\n\tcolors: Uint8Array | null;\n}\n\nexport interface FinishedGeometry extends CreasedGeometry {\n\t/** See {@link isClosedSolid}. */\n\tclosed: boolean;\n\t/** See {@link grainTangents}. Present exactly when `uvs` is. */\n\ttangents: Float32Array | null;\n}\n\n/**\n * The shared tail of both parse paths, so the worker and the main-thread fallback can't drift.\n * Closedness is judged before the crease split, which rewires corners to new vertex slots.\n * Tangents come after it, so each split copy takes only its own side's faces.\n */\nexport function finishGeometry(\n\tgeometry: AssembledGeometry,\n\tmembers: { indexCount: number; closed?: boolean }[]\n): FinishedGeometry {\n\t// The writer's verdicts, when every member has one: computing it was the costliest finish step.\n\tconst closed = members.every((m) => typeof m.closed === 'boolean')\n\t\t? members.length > 0 && members.every((m) => m.closed)\n\t\t: isClosedSolid(\n\t\t\t\tgeometry.positions,\n\t\t\t\tgeometry.indices,\n\t\t\t\tmembers.map((m) => m.indexCount)\n\t\t\t);\n\tconst creased = splitCreases(geometry.positions, geometry.indices, geometry.uvs, geometry.colors);\n\tconst tangents = creased.uvs\n\t\t? grainTangents(creased.positions, creased.normals, creased.indices, creased.uvs)\n\t\t: null;\n\treturn { ...creased, closed, tangents };\n}\n\nexport function assembleGeometries(input: AssemblyInput): AssembledGeometry[] {\n\t// NOTE: self-contained by design (worker stringification) — no outer references besides Math.\n\tconst {\n\t\tisFloat32,\n\t\tdeltaEncoded,\n\t\tplanarByteSplit,\n\t\tuint16Indices,\n\t\torigin,\n\t\tscale,\n\t\tuvs,\n\t\tcolors,\n\t\tjobs\n\t} = 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 (planarByteSplit) {\n\t\t\t// v4 layout: [Xlo][Ylo][Zlo][Xhi][Yhi][Zhi] byte planes of the zigzag deltas.\n\t\t\tconst planes = input.vertexData as Uint8Array;\n\t\t\tconst n = planes.length / 6;\n\t\t\tquantized = new Int16Array(n * 3);\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 < n; i++) {\n\t\t\t\tpx = ((px + unzigzag(planes[i] | (planes[n * 3 + i] << 8))) << 16) >> 16;\n\t\t\t\tpy = ((py + unzigzag(planes[n + i] | (planes[n * 4 + i] << 8))) << 16) >> 16;\n\t\t\t\tpz = ((pz + unzigzag(planes[n * 2 + i] | (planes[n * 5 + i] << 8))) << 16) >> 16;\n\t\t\t\tquantized[i * 3] = px;\n\t\t\t\tquantized[i * 3 + 1] = py;\n\t\t\t\tquantized[i * 3 + 2] = pz;\n\t\t\t}\n\t\t} else if (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 (planarByteSplit) {\n\t\tconst planes = input.indexData as Uint8Array;\n\t\tif (uint16Indices) {\n\t\t\tconst count = planes.length / 2;\n\t\t\tconst out = new Uint16Array(count);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tprev = (prev + unzigzag(planes[i] | (planes[count + i] << 8))) & 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 count = planes.length / 4;\n\t\t\tconst out = new Uint32Array(count);\n\t\t\tlet prev = 0;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tconst zz =\n\t\t\t\t\t(planes[i] |\n\t\t\t\t\t\t(planes[count + i] << 8) |\n\t\t\t\t\t\t(planes[count * 2 + i] << 16) |\n\t\t\t\t\t\t(planes[count * 3 + i] << 24)) >>>\n\t\t\t\t\t0;\n\t\t\t\tprev = (prev + unzigzag(zz)) >>> 0;\n\t\t\t\tout[i] = prev;\n\t\t\t}\n\t\t\tindices = out;\n\t\t}\n\t} else if (deltaEncoded) {\n\t\tconst zigzagged = input.indexData as Uint16Array | Uint32Array;\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 as Uint16Array | Uint32Array;\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\tresults.push({\n\t\t\tpositions,\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} and {@link finishGeometry} off the main thread.\n * Protocol: receives `{id, input}`, replies `{id, geometries}` (each a {@link FinishedGeometry})\n * with every output buffer transferred, or `{id, error}`. Pinned by a test that evals this source\n * against a stub `self`.\n */\nexport function meshAssemblyWorkerSource(): string {\n\treturn [\n\t\t`const assemble = ${assembleGeometries.toString()};`,\n\t\t`const isClosedSolid = ${isClosedSolid.toString()};`,\n\t\t`const splitCreases = ${splitCreases.toString()};`,\n\t\t`const grainTangents = ${grainTangents.toString()};`,\n\t\t`self.onmessage = (event) => {`,\n\t\t` const { id, input } = event.data;`,\n\t\t` try {`,\n\t\t// Mirrors finishGeometry, which can't be stringified: it captures the functions above.\n\t\t` const geometries = assemble(input).map((g, i) => {`,\n\t\t` const windows = input.jobs[i].windows;`,\n\t\t` const known = windows.every((w) => typeof w.closed === 'boolean');`,\n\t\t` const closed = known`,\n\t\t` ? windows.length > 0 && windows.every((w) => w.closed)`,\n\t\t` : isClosedSolid(g.positions, g.indices, windows.map((w) => w.indexCount));`,\n\t\t` const c = splitCreases(g.positions, g.indices, g.uvs, g.colors);`,\n\t\t` const tangents = c.uvs ? grainTangents(c.positions, c.normals, c.indices, c.uvs) : null;`,\n\t\t` return { ...c, closed, tangents };`,\n\t\t` });`,\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.tangents) transfer.push(g.tangents.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 { FinishedGeometry } from '../mesh-assembly.js';\n\n/**\n * Below this triangle count the synchronous path finishes in ~10 ms — a worker round-trip isn't\n * 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: FinishedGeometry[]) => 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?: FinishedGeometry[];\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<FinishedGeometry[]> {\n\treturn new Promise<FinishedGeometry[]>((resolve, reject) => {\n\t\tconst id = nextAssemblyRequestId++;\n\t\tpendingAssemblies.set(id, { resolve, reject });\n\t\tworker.postMessage({ id, input }, transfer);\n\t});\n}\n","import * as THREE from 'three';\n\nimport { getLogger, observeMaxAnisotropy } from '../../shared/index.js';\n\n/**\n * Anisotropic-filtering samples applied to material textures, keeping them 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\nexport type TextureSlot = 'map' | 'roughnessMap' | 'normalMap';\n\n/**\n * Assigns a texture to `material[slot]` once fetched and decoded — the mesh renders without it for\n * the first frames. Load failures log a warning and leave the slot empty rather than breaking the\n * 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 applyTexture(\n\tmaterial: THREE.MeshPhysicalMaterial,\n\turl: string,\n\tslot: TextureSlot,\n\tmapSize?: number\n): 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); roughness and normal maps\n\t\t\t// are data and must stay linear.\n\t\t\ttexture.colorSpace = slot === 'map' ? THREE.SRGBColorSpace : THREE.NoColorSpace;\n\t\t\ttexture.anisotropy = maxAnisotropy;\n\t\t\tif (mapSize) {\n\t\t\t\t// UVs are in mm: one repeat per mapSize across, the image's aspect down.\n\t\t\t\tconst image = texture.image as { width?: number; height?: number } | undefined;\n\t\t\t\tconst aspect = image?.width && image.height ? image.height / image.width : 1;\n\t\t\t\ttexture.wrapS = THREE.RepeatWrapping;\n\t\t\t\ttexture.wrapT = THREE.RepeatWrapping;\n\t\t\t\ttexture.repeat.set(1 / mapSize, 1 / (mapSize * aspect));\n\t\t\t}\n\t\t\tmaterial[slot] = 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 ${slot} ${url}:`, error);\n\t\t}\n\t);\n}\n","import type { Finish, ShaderPatch } from './finish.js';\n\n/**\n * Procedural surfaces, so a preset looks like its material with no texture files to ship or upload.\n * UVs are millimetres, so a brick is brick-sized at any model scale. Each recipe sets `tone` (albedo\n * multiplier), `gloss` (roughness multiplier) and `height` (relief in metres, to match the viewer).\n * Every pattern goes through `selvaDetail`, colour included: a noise cell or a brick under a pixel\n * samples to static, so a whole building has to settle to its flat colour.\n */\nconst RECIPES: Partial<Record<Finish, string>> = {\n\t// Rings warped by slow noise, fibres along U.\n\twood: /* glsl */ `\n\t\tfloat warp = selvaFbm( uv * vec2( 0.003, 0.02 ) );\n\t\tfloat rings = 0.5 + 0.5 * sin( ( uv.y + warp * 90.0 ) * 0.28 );\n\t\tfloat fibre = selvaNoise( uv * vec2( 0.03, 1.4 ) );\n\t\tfloat grain = ( rings * 0.65 + fibre * 0.35 ) - 0.5;\n\t\tgrain *= selvaDetail( 4.0 );\n\t\ttone = vec3( 1.0 + grain * 0.42 );\n\t\tgloss = 1.0 + grain * 0.35;\n\t\theight = fibre * 0.00012 * selvaDetail( 1.0 );`,\n\t// Slow blotches from the pour, plus bug holes.\n\tconcrete: /* glsl */ `\n\t\tfloat blotch = ( selvaFbm( uv * 0.0035 ) - 0.5 ) * selvaDetail( 40.0 );\n\t\tfloat holes = smoothstep( 0.8, 0.88, selvaNoise( uv * 0.22 ) ) * selvaDetail( 5.0 );\n\t\tfloat speck = selvaNoise( uv * 0.9 ) - 0.5;\n\t\ttone = vec3( 1.0 + blotch * 0.26 + speck * 0.05 * selvaDetail( 1.0 ) - holes * 0.2 );\n\t\tgloss = 1.0 + blotch * 0.3;\n\t\theight = -holes * 0.0005;`,\n\t// Hand-trowelled: all relief, barely any colour.\n\tplaster: /* glsl */ `\n\t\tfloat trowel = 0.5 + ( selvaFbm( uv * 0.05 ) - 0.5 ) * selvaDetail( 6.0 );\n\t\ttone = vec3( 1.0 + ( trowel - 0.5 ) * 0.07 );\n\t\tgloss = 1.0 + ( trowel - 0.5 ) * 0.2;\n\t\theight = trowel * 0.0007;`,\n\t// Running bond at the standard 215 x 65 mm with 10 mm joints. Every brick its own tone.\n\tbrick: /* glsl */ `\n\t\tvec2 course = vec2( 225.0, 75.0 );\n\t\tfloat row = floor( uv.y / course.y );\n\t\tvec2 laid = vec2( uv.x + mod( row, 2.0 ) * course.x * 0.5, uv.y );\n\t\tvec2 id = floor( laid / course );\n\t\tvec2 edge = abs( fract( laid / course ) - 0.5 ) * course;\n\t\tfloat joint = 1.0 - smoothstep( 0.0, 1.5, min( course.x * 0.5 - 5.0 - edge.x, course.y * 0.5 - 5.0 - edge.y ) );\n\t\tjoint *= selvaDetail( 10.0 );\n\t\tfloat face = 1.0 + ( ( selvaHash( id ) - 0.5 ) * 0.48 + ( selvaFbm( uv * 0.05 ) - 0.5 ) * 0.25 ) * selvaDetail( 65.0 );\n\t\ttone = mix( vec3( face ), vec3( 1.5, 1.7, 1.8 ), joint );\n\t\tgloss = 1.0 + joint * 0.15;\n\t\theight = -joint * 0.005;`,\n\t// Weathering steel: orange bloom over dark scale, streaking down the sheet.\n\tcorten: /* glsl */ `\n\t\tfloat bloom = 0.5 + ( selvaFbm( uv * vec2( 0.006, 0.0025 ) ) - 0.5 ) * selvaDetail( 25.0 );\n\t\tfloat scale = 0.5 + ( selvaFbm( uv * 0.06 ) - 0.5 ) * selvaDetail( 8.0 );\n\t\ttone = mix( vec3( 0.62, 0.52, 0.46 ), vec3( 1.28, 1.08, 0.9 ), smoothstep( 0.32, 0.68, bloom ) );\n\t\ttone *= 1.0 + ( scale - 0.5 ) * 0.3;\n\t\tgloss = 1.0 + ( scale - 0.5 ) * 0.25;\n\t\theight = scale * 0.0004;`\n};\n\nconst f = (n: number) => n.toFixed(4);\n\n/** Null for the finishes `finish.ts` draws itself. */\nexport function surfacePatch(\n\tfinish: Finish,\n\tstrength: number\n): { key: string; patch: ShaderPatch } | null {\n\tconst recipe = RECIPES[finish];\n\tif (!recipe || !(strength > 0)) return null;\n\n\tconst patch: ShaderPatch = (shader) => {\n\t\tshader.fragmentShader = shader.fragmentShader\n\t\t\t.replace(\n\t\t\t\t'#include <common>',\n\t\t\t\t`#include <common>\n\t\t\t\tfloat selvaFootprint;\n\t\t\t\t// Hash without sine: sin() loses precision on the large arguments mm UVs produce.\n\t\t\t\tfloat selvaHash( vec2 p ) {\n\t\t\t\t\tvec3 p3 = fract( vec3( p.xyx ) * 0.1031 );\n\t\t\t\t\tp3 += dot( p3, p3.yzx + 33.33 );\n\t\t\t\t\treturn fract( ( p3.x + p3.y ) * p3.z );\n\t\t\t\t}\n\t\t\t\tfloat selvaNoise( vec2 p ) {\n\t\t\t\t\tvec2 i = floor( p );\n\t\t\t\t\tvec2 f = fract( p );\n\t\t\t\t\tvec2 s = f * f * ( 3.0 - 2.0 * f );\n\t\t\t\t\treturn mix(\n\t\t\t\t\t\tmix( selvaHash( i ), selvaHash( i + vec2( 1.0, 0.0 ) ), s.x ),\n\t\t\t\t\t\tmix( selvaHash( i + vec2( 0.0, 1.0 ) ), selvaHash( i + vec2( 1.0, 1.0 ) ), s.x ),\n\t\t\t\t\t\ts.y\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfloat selvaFbm( vec2 p ) {\n\t\t\t\t\tfloat sum = 0.0;\n\t\t\t\t\tfloat amplitude = 0.5;\n\t\t\t\t\tfor ( int i = 0; i < 4; i ++ ) {\n\t\t\t\t\t\tsum += amplitude * selvaNoise( p );\n\t\t\t\t\t\tp = p * 2.03 + vec2( 17.0, 9.0 );\n\t\t\t\t\t\tamplitude *= 0.5;\n\t\t\t\t\t}\n\t\t\t\t\treturn sum / 0.9375;\n\t\t\t\t}\n\t\t\t\tfloat selvaDetail( float featureMm ) {\n\t\t\t\t\treturn 1.0 - smoothstep( 0.5, 1.5, selvaFootprint / featureMm );\n\t\t\t\t}`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <color_fragment>',\n\t\t\t\t`#include <color_fragment>\n\t\t\t\tselvaFootprint = max( max( fwidth( vUv.x ), fwidth( vUv.y ) ), 1e-6 );\n\t\t\t\tvec2 uv = vUv;\n\t\t\t\tvec3 tone = vec3( 1.0 );\n\t\t\t\tfloat gloss = 1.0;\n\t\t\t\tfloat height = 0.0;\n\t\t\t\t${recipe}\n\t\t\t\tdiffuseColor.rgb *= mix( vec3( 1.0 ), tone, ${f(strength)} );`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <roughnessmap_fragment>',\n\t\t\t\t`#include <roughnessmap_fragment>\n\t\t\t\troughnessFactor = clamp( roughnessFactor * mix( 1.0, gloss, ${f(strength)} ), 0.0, 1.0 );`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <normal_fragment_maps>',\n\t\t\t\t`#include <normal_fragment_maps>\n\t\t\t\t{\n\t\t\t\t\t// Bump from screen derivatives, as three's perturbNormalArb.\n\t\t\t\t\tvec3 dx = dFdx( - vViewPosition );\n\t\t\t\t\tvec3 dy = dFdy( - vViewPosition );\n\t\t\t\t\tvec3 r1 = cross( dy, normal );\n\t\t\t\t\tvec3 r2 = cross( normal, dx );\n\t\t\t\t\tfloat det = dot( dx, r1 ) * faceDirection;\n\t\t\t\t\tvec3 grad = sign( det ) * ( dFdx( height ) * r1 + dFdy( height ) * r2 ) * ${f(strength)};\n\t\t\t\t\tnormal = normalize( abs( det ) * normal - grad );\n\t\t\t\t}`\n\t\t\t);\n\t};\n\n\treturn { key: `surface:${finish}:${f(strength)}`, patch };\n}\n","import type * as THREE from 'three';\n\nimport type { SerializableMaterial } from '../types.js';\nimport { surfacePatch } from './surface-finish.js';\n\nexport type Finish = NonNullable<SerializableMaterial['finish']>;\n\nexport type ShaderPatch = (shader: THREE.WebGLProgramParametersWithUniforms) => void;\n\n/**\n * Scratch fibres across the grain: `pitch` mm apart, each segment `length` mm long (a range: real\n * brushing is many short fibres, not lines running the whole part), `depth` how far one scales\n * roughness, brightness by half, and tilts the normal across the grain so single fibres glint.\n */\ninterface Fibres {\n\tpitch: number;\n\tlength: [min: number, max: number];\n\tdepth: number;\n}\n\n/** Soft streaks along the grain, as on rolled sheet. Not on brushed: any band longer than its fibres read as full-height streaks on a profile wall. */\ninterface Bands {\n\twidth: number;\n\tlength: number;\n\tdepth: number;\n}\n\n// Starting points, set by eye against photos of brushed stainless and mill-finish aluminium: tune\n// against photos of real sheet, never against another render.\nconst RECIPES: Partial<Record<Finish, { fibres: Fibres[]; bands: Bands[] }>> = {\n\tbrushed: {\n\t\tfibres: [\n\t\t\t{ pitch: 0.04, length: [2, 12], depth: 0.3 },\n\t\t\t{ pitch: 0.25, length: [5, 40], depth: 0.18 }\n\t\t],\n\t\tbands: []\n\t},\n\trolled: {\n\t\tfibres: [{ pitch: 0.3, length: [80, 600], depth: 0.07 }],\n\t\tbands: [{ width: 20, length: 1500, depth: 0.05 }]\n\t}\n};\n\n// Radians of normal tilt per unit fibre depth.\nconst TILT = 0.6;\n\n// Per doubling of cell width, unresolved fibres keep 2^-FIBRE_FADE of their spread. A true average\n// is 0.5; slower keeps the few deep scratches that still read at wall distance in photos.\nconst FIBRE_FADE = 0.35;\n\nconst f = (n: number) => n.toFixed(4);\n\n/** The shader patch for a finish, or null when it would draw nothing. `key` extends the program cache key. */\nexport function finishPatch(\n\tfinish: Finish,\n\tstrength: number\n): { key: string; patch: ShaderPatch } | null {\n\tconst recipe = RECIPES[finish];\n\tif (!recipe) return surfacePatch(finish, strength);\n\tif (!(strength > 0)) return null;\n\n\tconst fibres =\n\t\trecipe.fibres\n\t\t\t.map(\n\t\t\t\t(fb, i) =>\n\t\t\t\t\t`selvaFibres( vUv, footprint, ${f(fb.pitch)}, vec2( ${f(fb.length[0])}, ${f(fb.length[1])} ), ${f(fb.depth)}, ${f(i * 31 + 3)} )`\n\t\t\t)\n\t\t\t.join(' + ') || '0.0';\n\tconst bands =\n\t\trecipe.bands\n\t\t\t.map(\n\t\t\t\t(b) => `selvaBand( vUv, footprint, vec2( ${f(b.length)}, ${f(b.width)} ), ${f(b.depth)} )`\n\t\t\t)\n\t\t\t.join(' + ') || '0.0';\n\n\tconst patch: ShaderPatch = (shader) => {\n\t\tshader.fragmentShader = shader.fragmentShader\n\t\t\t.replace(\n\t\t\t\t'#include <common>',\n\t\t\t\t`#include <common>\n\t\t\t\t// Hash without sine: sin() loses precision on the large arguments mm UVs produce.\n\t\t\t\tfloat selvaHash( vec2 p ) {\n\t\t\t\t\tvec3 p3 = fract( vec3( p.xyx ) * 0.1031 );\n\t\t\t\t\tp3 += dot( p3, p3.yzx + 33.33 );\n\t\t\t\t\treturn fract( ( p3.x + p3.y ) * p3.z );\n\t\t\t\t}\n\t\t\t\tfloat selvaNoise( vec2 p ) {\n\t\t\t\t\tvec2 i = floor( p );\n\t\t\t\t\tvec2 f = fract( p );\n\t\t\t\t\tvec2 s = f * f * ( 3.0 - 2.0 * f );\n\t\t\t\t\treturn mix(\n\t\t\t\t\t\tmix( selvaHash( i ), selvaHash( i + vec2( 1.0, 0.0 ) ), s.x ),\n\t\t\t\t\t\tmix( selvaHash( i + vec2( 0.0, 1.0 ) ), selvaHash( i + vec2( 1.0, 1.0 ) ), s.x ),\n\t\t\t\t\t\ts.y\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// One row of fibres per pitch, hard-edged across the grain, each fibre its own depth and\n\t\t\t\t// tapered at both ends. A depth per row instead drew every row as one streak the height\n\t\t\t\t// of the part. depth * |depth| keeps most fibres faint and a few deep.\n\t\t\t\tfloat selvaFibreRow( vec2 uv, float pitch, vec2 len, float seed ) {\n\t\t\t\t\tfloat row = floor( uv.y / pitch );\n\t\t\t\t\tfloat l = mix( len.x, len.y, selvaHash( vec2( row, seed + 1.0 ) ) );\n\t\t\t\t\tfloat along = uv.x / l + selvaHash( vec2( row, seed + 2.0 ) );\n\t\t\t\t\tfloat seg = floor( along );\n\t\t\t\t\tfloat depth = selvaHash( vec2( row, seed + 3.0 + 1.618 * seg ) ) * 2.0 - 1.0;\n\t\t\t\t\tfloat t = along - seg;\n\t\t\t\t\treturn depth * abs( depth ) * smoothstep( 0.0, 0.2, t ) * smoothstep( 1.0, 0.8, t );\n\t\t\t\t}\n\t\t\t\t// Below pixel size a cell 2^n rows wide stands for the rows it covers, at a reduced\n\t\t\t\t// spread: distance keeps the grain's texture without the shimmer of sampling single\n\t\t\t\t// fibres. Two levels blend so nothing pops while zooming.\n\t\t\t\tfloat selvaFibres( vec2 uv, float footprint, float pitch, vec2 len, float depth, float seed ) {\n\t\t\t\t\tfloat level = clamp( log2( footprint / pitch ) + 0.5, 0.0, 14.0 );\n\t\t\t\t\tfloat l0 = floor( level );\n\t\t\t\t\tfloat a = selvaFibreRow( uv, pitch * exp2( l0 ), len, seed + 7.0 * l0 ) * exp2( -${f(FIBRE_FADE)} * l0 );\n\t\t\t\t\tfloat b = selvaFibreRow( uv, pitch * exp2( l0 + 1.0 ), len, seed + 7.0 * l0 + 7.0 )\n\t\t\t\t\t\t* exp2( -${f(FIBRE_FADE)} * ( l0 + 1.0 ) );\n\t\t\t\t\treturn depth * mix( a, b, level - l0 );\n\t\t\t\t}\n\t\t\t\tfloat selvaBand( vec2 uv, float footprint, vec2 size, float depth ) {\n\t\t\t\t\tfloat fade = 1.0 - smoothstep( 0.5, 1.0, footprint / size.y );\n\t\t\t\t\treturn depth * fade * ( selvaNoise( uv / size ) * 2.0 - 1.0 );\n\t\t\t\t}`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <color_fragment>',\n\t\t\t\t`#include <color_fragment>\n\t\t\t\tfloat footprint = max( fwidth( vUv.y ), 1e-6 );\n\t\t\t\tfloat selvaFibre = ( ${fibres} ) * ${f(strength)};\n\t\t\t\tfloat selvaStreak = selvaFibre + ( ${bands} ) * ${f(strength)};\n\t\t\t\tdiffuseColor.rgb *= 1.0 + 0.5 * selvaStreak;`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <roughnessmap_fragment>',\n\t\t\t\t`#include <roughnessmap_fragment>\n\t\t\t\troughnessFactor = clamp( roughnessFactor * ( 1.0 + selvaStreak ), 0.0, 1.0 );`\n\t\t\t)\n\t\t\t.replace(\n\t\t\t\t'#include <normal_fragment_maps>',\n\t\t\t\t`#include <normal_fragment_maps>\n\t\t\t\t#ifdef USE_ANISOTROPY\n\t\t\t\t\tvec3 selvaAcross = tbn[ 1 ];\n\t\t\t\t#else\n\t\t\t\t\t// V's direction from screen derivatives, as three's getTangentFrame.\n\t\t\t\t\tvec3 selvaQ0 = dFdx( - vViewPosition );\n\t\t\t\t\tvec3 selvaQ1 = dFdy( - vViewPosition );\n\t\t\t\t\tvec3 selvaAcross = cross( selvaQ1, normal ) * dFdx( vUv ).y + cross( normal, selvaQ0 ) * dFdy( vUv ).y;\n\t\t\t\t\tselvaAcross *= inversesqrt( max( dot( selvaAcross, selvaAcross ), 1e-12 ) );\n\t\t\t\t#endif\n\t\t\t\tnormal = normalize( normal + ${f(TILT)} * selvaFibre * selvaAcross );`\n\t\t\t);\n\t};\n\n\treturn { key: `finish:${finish}:${f(strength)}`, patch };\n}\n","import * as THREE from 'three';\n\nimport {\n\tEMISSIVE_LIGHT,\n\tOWN_EMISSIVE,\n\tOWN_ENV_MAP_INTENSITY,\n\tlookEnvMapIntensity,\n\tparseColor\n} from '../../../shared/index.js';\n\nimport { applyTexture } from '../apply-texture.js';\nimport { finishPatch, type ShaderPatch } from './finish.js';\n\nimport type { MaterialAppearanceOptions, SerializableMaterial } from '../types.js';\n\n// Pushes surfaces back by a fraction of their own depth slope so crease edges keep their full width:\n// an edge line is a screen-space quad at the edge's depth, and without this the face on its near\n// side covers half of it. The same push lets anything just behind a grazing surface show through,\n// and that gap grows with distance. Measured on a 1 mm sheet with parts 1 mm behind it: 0.5 bled\n// from ~2 m, 0.25 from ~5 m with crease lines barely thinner, 0 only past ~10 m but halved lines.\nconst SURFACE_OFFSET_FACTOR = 0.25;\n\ninterface MaterialOptions {\n\tvertexColors?: boolean;\n\thasUvs?: boolean;\n\tappearance?: MaterialAppearanceOptions;\n\t/** Every mesh drawn with this material is a closed solid: see `isClosedSolid`. */\n\tclosed?: boolean;\n}\n\n// Transparent solids keep both sides: their far wall is meant to show through the near one.\nfunction cullsBackFaces(matData: SerializableMaterial, options?: MaterialOptions): boolean {\n\tif (options?.appearance?.cullBackfaces) return true;\n\treturn (options?.closed ?? false) && !matData.transparent && matData.opacity >= 1;\n}\n\n/**\n * Builds each wire material on first use, once per side it's needed on. `side` is per material, so\n * one material shared by closed solids and open surfaces becomes two instances.\n */\nexport type MaterialPicker = (materialId: number, closed: boolean) => THREE.Material;\n\nexport function createMaterialPicker(\n\tmaterialsSrc: SerializableMaterial[],\n\toptions: Omit<MaterialOptions, 'closed'>\n): MaterialPicker {\n\tconst built = new Map<string, THREE.Material>();\n\treturn (materialId, closed) => {\n\t\tconst matData = materialsSrc[materialId]!;\n\t\tconst culled = cullsBackFaces(matData, { ...options, closed });\n\t\tconst key = `${materialId}:${culled}`;\n\t\tlet material = built.get(key);\n\t\tif (!material) {\n\t\t\tmaterial = createMaterial(matData, { ...options, closed: culled });\n\t\t\tbuilt.set(key, material);\n\t\t}\n\t\treturn material;\n\t};\n}\n\nexport function createMaterial(\n\tmatData: SerializableMaterial,\n\toptions?: MaterialOptions\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// Open surfaces need both sides; a closed solid's back faces are never visible and only\n\t\t// z-fight with its front ones.\n\t\tside: cullsBackFaces(matData, options) ? THREE.FrontSide : THREE.DoubleSide,\n\t\tpolygonOffset: true,\n\t\tpolygonOffsetFactor: SURFACE_OFFSET_FACTOR,\n\t\tpolygonOffsetUnits: 0,\n\t\tdepthWrite: true,\n\t\tdepthTest: true\n\t});\n\n\t// The material's own value beats the look's; otherwise three's default (1) unless the look dials it.\n\tif (matData.envMapIntensity != null) {\n\t\tmaterial.envMapIntensity = matData.envMapIntensity;\n\t\tmaterial.userData[OWN_ENV_MAP_INTENSITY] = matData.envMapIntensity;\n\t} else if (appearance?.envMapIntensity != null) {\n\t\tmaterial.envMapIntensity = lookEnvMapIntensity(appearance.envMapIntensity, matData.metalness);\n\t}\n\n\tif (matData.emissive != null) {\n\t\tmaterial.emissive.set(matData.emissive);\n\t\tmaterial.emissiveIntensity = matData.emissiveIntensity ?? 1;\n\t\tmaterial.userData[OWN_EMISSIVE] = true;\n\t\tif (matData.emissiveLight != null) material.userData[EMISSIVE_LIGHT] = matData.emissiveLight;\n\t}\n\n\tif (matData.clearcoat != null) {\n\t\tmaterial.clearcoat = matData.clearcoat;\n\t\tmaterial.clearcoatRoughness = matData.clearcoatRoughness ?? 0;\n\t}\n\n\t// The anisotropy tangent comes from the UVs; without them three normalizes a zero vector and the\n\t// mesh renders NaN-black.\n\tif (matData.anisotropy != null && matData.anisotropy > 0 && options?.hasUvs) {\n\t\tmaterial.anisotropy = matData.anisotropy;\n\t\tmaterial.anisotropyRotation = matData.anisotropyRotation ?? 0;\n\t}\n\n\tif (matData.transmission != null) {\n\t\tmaterial.transmission = matData.transmission;\n\t\t// A pane, not a lens: no thickness, so nothing behind it refracts sideways.\n\t\tmaterial.thickness = 0;\n\t}\n\tif (matData.ior != null) material.ior = matData.ior;\n\n\tif (vertexColors) {\n\t\tapplyVertexColorSRGBDecode(material);\n\t}\n\n\tconst finish =\n\t\tmatData.finish && options?.hasUvs\n\t\t\t? finishPatch(matData.finish, matData.finishStrength ?? 1)\n\t\t\t: null;\n\tif (finish) {\n\t\t// `vUv` exists only with a map or anisotropy; a finish needs it either way.\n\t\tmaterial.defines = { ...material.defines, USE_UV: '' };\n\t\taddShaderPatch(material, finish.key, finish.patch);\n\t}\n\n\t// Async; the mesh renders without them until each image decodes.\n\tconst { mapSize } = matData;\n\tif (matData.map) applyTexture(material, matData.map, 'map', mapSize);\n\tif (matData.roughnessMap) applyTexture(material, matData.roughnessMap, 'roughnessMap', mapSize);\n\tif (matData.normalMap) applyTexture(material, matData.normalMap, 'normalMap', mapSize);\n\n\treturn material;\n}\n\n/**\n * Chains `patch` after any earlier one. three caches compiled programs by `customProgramCacheKey`,\n * which defaults to the `onBeforeCompile` source; a chained wrapper's source is the same whatever\n * it wraps, so every patch must extend the key or two materials would share one program.\n */\nfunction addShaderPatch(material: THREE.Material, key: string, patch: ShaderPatch): void {\n\tconst previous = material.onBeforeCompile;\n\tconst previousKey = material.customProgramCacheKey.bind(material);\n\tmaterial.onBeforeCompile = (shader, renderer) => {\n\t\tprevious.call(material, shader, renderer);\n\t\tpatch(shader);\n\t};\n\tmaterial.customProgramCacheKey = () => `${previousKey()}|${key}`;\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\taddShaderPatch(material, 'srgb-vertex-colors', (shader) => {\n\t\tshader.vertexShader = shader.vertexShader.replace(\n\t\t\t'#include <color_vertex>',\n\t\t\t`#include <color_vertex>\n\t\t\t#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA )\n\t\t\t\tvColor.rgb = mix(\n\t\t\t\t\tvColor.rgb / 12.92,\n\t\t\t\t\tpow( ( vColor.rgb + 0.055 ) / 1.055, vec3( 2.4 ) ),\n\t\t\t\t\tstep( vec3( 0.04045 ), vColor.rgb )\n\t\t\t\t);\n\t\t\t#endif`\n\t\t);\n\t});\n}\n","import { VisualizationError, ErrorCodes } from '../../../shared/index.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\nexport function metadataFail(\n\tmessage: string,\n\tcontext: Record<string, unknown>\n): VisualizationError {\n\treturn new VisualizationError(message, ErrorCodes.VALIDATION_ERROR, { context });\n}\n\n/**\n * Validates the batch's group/mesh metadata against the decoded geometry buffers before any of it\n * is used arithmetically. Throws on the first inconsistency — out-of-range `materialId`,\n * non-integer or negative offsets/counts, or a vertex/index window that overruns the buffers — so\n * malformed or version-skewed metadata fails the parse loudly instead of corrupting the render.\n */\nexport function validateGroupMetadata(\n\tgroups: MaterialGroup[],\n\tmaterialCount: number,\n\ttotalVertexCount: number,\n\ttotalIndexCount: number\n): void {\n\tfor (const group of groups) {\n\t\tif (\n\t\t\t!Number.isInteger(group.materialId) ||\n\t\t\tgroup.materialId < 0 ||\n\t\t\tgroup.materialId >= materialCount\n\t\t) {\n\t\t\tthrow metadataFail('Group materialId out of range of the materials array.', {\n\t\t\t\tmaterialId: group.materialId,\n\t\t\t\tmaterialCount\n\t\t\t});\n\t\t}\n\n\t\tfor (const mesh of group.meshes) {\n\t\t\tconst fields = {\n\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\tindexCount: mesh.indexCount\n\t\t\t};\n\t\t\tfor (const [field, value] of Object.entries(fields)) {\n\t\t\t\tif (!Number.isInteger(value) || value < 0) {\n\t\t\t\t\tthrow metadataFail(`Mesh metadata field \"${field}\" must be a non-negative integer.`, {\n\t\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (mesh.vertexStart + mesh.vertexCount > totalVertexCount) {\n\t\t\t\tthrow metadataFail('Mesh vertex window exceeds the batch vertex buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tvertexStart: mesh.vertexStart,\n\t\t\t\t\tvertexCount: mesh.vertexCount,\n\t\t\t\t\ttotalVertexCount\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (mesh.indexStart + mesh.indexCount > totalIndexCount) {\n\t\t\t\tthrow metadataFail('Mesh index window exceeds the batch index buffer.', {\n\t\t\t\t\tmeshName: mesh.name,\n\t\t\t\t\tindexStart: mesh.indexStart,\n\t\t\t\t\tindexCount: mesh.indexCount,\n\t\t\t\t\ttotalIndexCount\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Error for an index outside its mesh's declared vertex window\n * `[vertexStart, vertexStart + vertexCount)`. Rebasing (`index - vertexStart`) writes into an\n * unsigned array, so an out-of-window index would otherwise wrap to ~4 billion and corrupt the\n * geometry. Range checks live inline in the copy loops (a function call per index measured\n * noticeably slower at millions of indices) — this only builds the failure.\n */\nexport function indexOutOfWindow(indexValue: number, meshMeta: MeshMetadata): VisualizationError {\n\treturn metadataFail(\"Index references a vertex outside its mesh's vertex window.\", {\n\t\tmeshName: meshMeta.name,\n\t\tindexValue,\n\t\tvertexStart: meshMeta.vertexStart,\n\t\tvertexCount: meshMeta.vertexCount\n\t});\n}\n\n/**\n * Reconstructs world-unit float32 positions from int16 quantized values:\n * `world = origin + (q + 32767) * scale`. No rotation — the Three scene uses Rhino's Z-up frame,\n * so vertices pass through as they arrived.\n */\nexport function dequantizeInt16(\n\tq: Int16Array,\n\torigin: [number, number, number],\n\tscale: [number, number, number]\n): Float32Array {\n\tconst out = new Float32Array(q.length);\n\tconst ox = origin[0];\n\tconst oy = origin[1];\n\tconst oz = origin[2];\n\tconst sx = scale[0];\n\tconst sy = scale[1];\n\tconst sz = scale[2];\n\n\tfor (let i = 0; i < q.length; i += 3) {\n\t\tout[i] = ox + (q[i]! + 32767) * sx;\n\t\tout[i + 1] = oy + (q[i + 1]! + 32767) * sy;\n\t\tout[i + 2] = oz + (q[i + 2]! + 32767) * sz;\n\t}\n\n\treturn out;\n}\n","import * as THREE from 'three';\n\nimport { finishGeometry, type FinishedGeometry } from '../mesh-assembly.js';\n\nimport { indexOutOfWindow } from './metadata.js';\n\nimport type { MaterialPicker } from './materials.js';\n\nimport type { MaterialGroup, MeshMetadata } from '../types.js';\n\n/**\n * Split a material group into one sub-group per layer, preserving mesh order.\n *\n * Merging is by material alone, but a merged mesh is one THREE object and so can sit on exactly\n * one layer in the outliner. Without this split, two layers sharing a material collapse into a\n * single object filed under the first one, and hiding either layer hides both.\n */\nexport function splitGroupByLayer(group: MaterialGroup): MaterialGroup[] {\n\tconst byLayer = new Map<string, MeshMetadata[]>();\n\tfor (const meshMeta of group.meshes) {\n\t\tconst layer = meshMeta.layer ?? '';\n\t\tlet bucket = byLayer.get(layer);\n\t\tif (!bucket) {\n\t\t\tbucket = [];\n\t\t\tbyLayer.set(layer, bucket);\n\t\t}\n\t\tbucket.push(meshMeta);\n\t}\n\treturn [...byLayer.values()].map((meshes) => ({ materialId: group.materialId, meshes }));\n}\n\n/**\n * The groups a merge-by-material draws: every group of one material gathered into one, in order of\n * first appearance, then split by layer. The writer keeps meshes in input order, so one material\n * can head several groups.\n */\nexport function mergeGroupsOf(groups: MaterialGroup[]): MaterialGroup[] {\n\tconst byMaterial = new Map<number, MeshMetadata[]>();\n\tfor (const group of groups) {\n\t\tconst meshes = byMaterial.get(group.materialId);\n\t\t// A loop, not push(...spread): spreading passes every mesh as an argument and overflows the\n\t\t// stack past ~100k meshes.\n\t\tif (meshes) for (const mesh of group.meshes) meshes.push(mesh);\n\t\telse byMaterial.set(group.materialId, group.meshes.slice());\n\t}\n\treturn [...byMaterial].flatMap(([materialId, meshes]) =>\n\t\tsplitGroupByLayer({ materialId, meshes })\n\t);\n}\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\tpickMaterial: MaterialPicker,\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 finished = finishGeometry(\n\t\t{ positions: mergedVertices, indices: mergedIndices, uvs: mergedUvs, colors: mergedColors },\n\t\tgroup.meshes\n\t);\n\treturn finalizeMergedMesh(\n\t\tgeometryFrom(finished),\n\t\tgroup,\n\t\tpickMaterial(group.materialId, finished.closed)\n\t);\n}\n\nexport function geometryFrom(finished: FinishedGeometry): THREE.BufferGeometry {\n\tconst geometry = new THREE.BufferGeometry();\n\tgeometry.setAttribute('position', new THREE.BufferAttribute(finished.positions, 3));\n\tgeometry.setAttribute('normal', new THREE.BufferAttribute(finished.normals, 3));\n\tgeometry.setIndex(new THREE.BufferAttribute(finished.indices, 1));\n\tif (finished.uvs) geometry.setAttribute('uv', new THREE.BufferAttribute(finished.uvs, 2));\n\tif (finished.tangents) {\n\t\tgeometry.setAttribute('tangent', new THREE.BufferAttribute(finished.tangents, 4));\n\t}\n\tif (finished.colors) {\n\t\tgeometry.setAttribute('color', new THREE.BufferAttribute(finished.colors, 3, true));\n\t}\n\treturn geometry;\n}\n\n/**\n * One source object inside a merged mesh, as stamped into `userData.members`.\n *\n * Deliberately declared here rather than imported from `scene/`: the two layers are siblings, so\n * `parse` writing this shape and `scene` reading it back off `userData` is the only contract\n * between them. `scene/identity.ts` has the mirror of this interface.\n */\ninterface MergedMember {\n\ttrackingKey?: string;\n\tname: string;\n\tlayer: string;\n\tmetadata: Record<string, string>;\n\tindexStart: number;\n\tindexCount: number;\n}\n\n/**\n * Per-member records for a merged mesh, each carrying its window into the merged index buffer.\n * The windows are a prefix sum over `indexCount` because the merge concatenates members in this\n * order — see the copy loop in {@link createMergedMesh}.\n */\nfunction mergedMembers(group: MaterialGroup): MergedMember[] {\n\tconst members: MergedMember[] = [];\n\tlet indexStart = 0;\n\tfor (const mesh of group.meshes) {\n\t\tmembers.push({\n\t\t\ttrackingKey: mesh.id,\n\t\t\tname: mesh.name,\n\t\t\tlayer: mesh.layer,\n\t\t\tmetadata: mesh.metadata ?? {},\n\t\t\tindexStart,\n\t\t\tindexCount: mesh.indexCount\n\t\t});\n\t\tindexStart += mesh.indexCount;\n\t}\n\treturn members;\n}\n\nexport function finalizeMergedMesh(\n\tgeometry: THREE.BufferGeometry,\n\tgroup: MaterialGroup,\n\tmaterial: THREE.Material\n): THREE.Mesh {\n\tconst threeMesh = new THREE.Mesh(geometry, material);\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\tmetadata: firstMesh?.metadata ?? {},\n\t\t// A merged mesh is several source objects in one THREE object, so identity lives per\n\t\t// member: the scene layer keys hidden state on every member's key, and regrouping can\n\t\t// never lose it.\n\t\t//\n\t\t// `indexStart`/`indexCount` are the member's window into the merged index buffer, which is\n\t\t// what maps a raycast hit back to the one source object under the cursor — without them a\n\t\t// click could only ever identify the whole merged mesh. The merge writes members in this\n\t\t// exact order (see the concatenation loop above), so the windows are a prefix sum over\n\t\t// `indexCount` and are never stored on the wire.\n\t\tmembers: mergedMembers(group)\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\tpickMaterial: MaterialPicker,\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 uvStart = meshMeta.vertexStart * 2;\n\t\tconst finished = finishGeometry(\n\t\t\t{\n\t\t\t\tpositions: vertices,\n\t\t\t\tindices: rebasedIndices,\n\t\t\t\tuvs: allUvs ? allUvs.slice(uvStart, uvStart + meshMeta.vertexCount * 2) : null,\n\t\t\t\tcolors: allColors ? allColors.slice(componentStart, componentStart + componentLen) : null\n\t\t\t},\n\t\t\t[meshMeta]\n\t\t);\n\t\tmeshes.push(\n\t\t\tfinalizeSingleMesh(\n\t\t\t\tgeometryFrom(finished),\n\t\t\t\tmeshMeta,\n\t\t\t\tpickMaterial(group.materialId, finished.closed)\n\t\t\t)\n\t\t);\n\t}\n\n\treturn meshes;\n}\n\nexport function finalizeSingleMesh(\n\tgeometry: THREE.BufferGeometry,\n\tmeshMeta: MeshMetadata,\n\tmaterial: THREE.Material\n): THREE.Mesh {\n\tconst mesh = new THREE.Mesh(geometry, material);\n\tmesh.name = meshMeta.name;\n\tmesh.userData = {\n\t\tsource: 'compute',\n\t\tname: meshMeta.name,\n\t\tlayer: meshMeta.layer ?? '',\n\t\t// The writer-minted identity; absent for foreign writers, and the scene layer then\n\t\t// falls back to name + layer.\n\t\ttrackingKey: meshMeta.id,\n\t\tmetadata: meshMeta.metadata ?? {}\n\t};\n\tmesh.castShadow = true;\n\tmesh.receiveShadow = true;\n\treturn mesh;\n}\n","import type * as THREE from 'three';\n\nimport { shareGeometry, unshareGeometry } from '../../../shared/index.js';\nimport { geometryFrom } from './merge.js';\n\nimport type { AssemblyJob, FinishedGeometry } from '../mesh-assembly.js';\n\n/**\n * Finished geometries from earlier batches, keyed by the exact geometry chunk bytes and the jobs cut\n * from them. A re-solve that only changed materials, names or layers sends the same geometry bytes,\n * and a hit skips inflate, decode, the closed-solid test and the crease split: most of a parse.\n *\n * A hit is decided by comparing every byte, never by a fingerprint alone: a sampled fingerprint can\n * match two different payloads and serve stale geometry without an error.\n *\n * A hit hands out the same `BufferGeometry` objects again, registered with `shareGeometry`. Who frees\n * what is the cache rule in `shared/gpu-ownership.ts`.\n */\n\nexport interface FinishedBatch {\n\tgeometries: FinishedGeometry[];\n\thasUvs: boolean;\n\thasColors: boolean;\n\tvertexCount: number;\n\tindexCount: number;\n}\n\n/** One `BufferGeometry` per job, in job order, with each job's closed-solid flag. */\nexport interface BuiltBatch extends Omit<FinishedBatch, 'geometries'> {\n\tbuffers: THREE.BufferGeometry[];\n\tclosed: boolean[];\n}\n\ninterface Entry extends BuiltBatch {\n\tgeometryBytes: Uint8Array;\n\tjobShape: Int32Array;\n\tbytes: number;\n}\n\n// A colour change on a 2M-vertex scene keeps ~110 MB of finished arrays; this holds two variants.\nconst MAX_BYTES = 256 * 1024 * 1024;\n\nconst entries = new Map<string, Entry>();\nlet totalBytes = 0;\n\nexport function getFinishedBatch(\n\tgeometryBytes: Uint8Array,\n\tjobs: AssemblyJob[]\n): BuiltBatch | null {\n\tconst jobShape = shapeOf(jobs);\n\tconst key = cacheKey(geometryBytes, jobShape);\n\tconst entry = entries.get(key);\n\tif (\n\t\t!entry ||\n\t\t!sameInts(entry.jobShape, jobShape) ||\n\t\t!sameBytes(entry.geometryBytes, geometryBytes)\n\t)\n\t\treturn null;\n\t// Re-insert: Map iteration order is the eviction order.\n\tentries.delete(key);\n\tentries.set(key, entry);\n\treturn entry;\n}\n\n/** Wraps each finished geometry in a `BufferGeometry`, owned by the scene. */\nexport function buildBatch(batch: FinishedBatch): BuiltBatch {\n\tconst { geometries, ...rest } = batch;\n\treturn {\n\t\t...rest,\n\t\tbuffers: geometries.map(geometryFrom),\n\t\tclosed: geometries.map((g) => g.closed)\n\t};\n}\n\n/** Same as {@link buildBatch}, but the cache keeps the geometries for the next identical payload. */\nexport function storeFinishedBatch(\n\tgeometryBytes: Uint8Array,\n\tjobs: AssemblyJob[],\n\tbatch: FinishedBatch\n): BuiltBatch {\n\tconst built = buildBatch(batch);\n\tlet bytes = geometryBytes.byteLength;\n\tfor (const g of batch.geometries) {\n\t\tbytes += g.positions.byteLength + g.normals.byteLength + g.indices.byteLength;\n\t\tbytes += (g.uvs?.byteLength ?? 0) + (g.colors?.byteLength ?? 0) + (g.tangents?.byteLength ?? 0);\n\t}\n\tif (bytes > MAX_BYTES) return built;\n\n\tconst jobShape = shapeOf(jobs);\n\tconst key = cacheKey(geometryBytes, jobShape);\n\tconst existing = entries.get(key);\n\tif (existing) {\n\t\tentries.delete(key);\n\t\trelease(existing);\n\t}\n\tbuilt.buffers.forEach(shareGeometry);\n\t// A copy, so the entry neither pins the whole WebSocket frame nor sees it reused.\n\tentries.set(key, { ...built, geometryBytes: geometryBytes.slice(), jobShape, bytes });\n\ttotalBytes += bytes;\n\n\tfor (const [oldest, entry] of entries) {\n\t\tif (totalBytes <= MAX_BYTES) break;\n\t\tentries.delete(oldest);\n\t\trelease(entry);\n\t}\n\treturn built;\n}\n\n/**\n * Empties the cache and frees its geometries. The cache lives as long as the page, so a host calls\n * this when its viewer goes away; a mesh still drawing one of them uploads it again.\n */\nexport function clearGeometryCache(): void {\n\tentries.forEach(release);\n\tentries.clear();\n}\n\n// A mesh still drawing an evicted geometry is fine: three uploads it again on the next frame.\nfunction release(entry: Entry): void {\n\ttotalBytes -= entry.bytes;\n\tfor (const buffer of entry.buffers) {\n\t\tunshareGeometry(buffer);\n\t\tbuffer.dispose();\n\t}\n}\n\n// Per job: kind, window count, then each window's four numbers. The count prefix keeps two\n// different job lists from flattening to the same ints.\nfunction shapeOf(jobs: AssemblyJob[]): Int32Array {\n\tlet length = 0;\n\tfor (const job of jobs) length += 2 + 4 * job.windows.length;\n\tconst shape = new Int32Array(length);\n\tlet i = 0;\n\tfor (const job of jobs) {\n\t\tshape[i++] = job.kind === 'merged' ? 1 : 0;\n\t\tshape[i++] = job.windows.length;\n\t\tfor (const w of job.windows) {\n\t\t\tshape[i++] = w.vertexStart;\n\t\t\tshape[i++] = w.vertexCount;\n\t\t\tshape[i++] = w.indexStart;\n\t\t\tshape[i++] = w.indexCount;\n\t\t}\n\t}\n\treturn shape;\n}\n\n// Buckets entries; `sameInts` and `sameBytes` decide a hit. 1024 sampled bytes keep this cheap on a\n// multi-MB chunk.\nfunction cacheKey(geometryBytes: Uint8Array, jobShape: Int32Array): string {\n\tconst length = geometryBytes.length;\n\tconst stride = Math.max(1, Math.floor(length / 1024));\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < length; i += stride) {\n\t\thash = Math.imul(hash ^ geometryBytes[i]!, 0x01000193);\n\t}\n\tlet shapeHash = 0x811c9dc5;\n\tfor (let i = 0; i < jobShape.length; i++) {\n\t\tshapeHash = Math.imul(shapeHash ^ jobShape[i]!, 0x01000193);\n\t}\n\treturn `${length}:${hash >>> 0}:${jobShape.length}:${shapeHash >>> 0}`;\n}\n\nfunction sameInts(a: Int32Array, b: Int32Array): boolean {\n\tif (a.length !== b.length) return false;\n\tfor (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n\treturn true;\n}\n\nfunction sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n\tif (a.length !== b.length) return false;\n\tlet i = 0;\n\tif (a.byteOffset % 4 === 0 && b.byteOffset % 4 === 0) {\n\t\tconst words = a.length >>> 2;\n\t\tconst wa = new Uint32Array(a.buffer, a.byteOffset, words);\n\t\tconst wb = new Uint32Array(b.buffer, b.byteOffset, words);\n\t\tfor (; i < words; i++) if (wa[i] !== wb[i]) return false;\n\t\ti = words << 2;\n\t}\n\tfor (; i < a.length; i++) if (a[i] !== b[i]) return false;\n\treturn true;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\n\nimport { decodeRawMeshBatch, FLAG_FLOAT32, parseBinaryMeshBatchRaw } from './binary-parser.js';\nimport { inflateSlvzAsync, slvzInflatedBytes, toUint8Array } from './binary/geometry.js';\nimport { isSlvmContainer, parseSlvmContainer, type SlvmContainer } from './binary/slvm.js';\nimport { assembleGeometries, finishGeometry } from './mesh-assembly.js';\n\nimport {\n\tASSEMBLY_WORKER_MIN_TRIANGLES,\n\tgetAssemblyWorker,\n\trequestAssembly\n} from './batch/assembly-worker.js';\nimport { createMaterialPicker } from './batch/materials.js';\nimport {\n\tcreateIndividualMeshes,\n\tcreateMergedMesh,\n\tfinalizeMergedMesh,\n\tfinalizeSingleMesh,\n\tmergeGroupsOf\n} from './batch/merge.js';\nimport {\n\tbuildBatch,\n\tgetFinishedBatch,\n\tstoreFinishedBatch,\n\ttype BuiltBatch,\n\ttype FinishedBatch\n} from './batch/finished-cache.js';\nimport { dequantizeInt16, validateGroupMetadata } from './batch/metadata.js';\n\nimport type { AssemblyJob, AssemblyWindow, FinishedGeometry } from './mesh-assembly.js';\nimport type { ParsedBinaryMeshBatch, RawBinaryMeshBatch } 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# `SlvaWriter`, 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\tconst decodeStart = performance.now();\n\tconst assemblyOptions: AssemblyPathOptions = {\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}\n\t};\n\tconst opened = await openBatch(batch.compressedData, assemblyOptions);\n\tif (opened.meshes) return opened.meshes;\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch).\n\tconst assembledMeshes = await tryBuildViaAssembly(opened, assemblyOptions);\n\tif (assembledMeshes) return assembledMeshes;\n\n\tconst parsed = decodeRawMeshBatch(opened.raw);\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}\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 per-object ids coming\n * from the container's table.\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\tconst decodeStart = performance.now();\n\tconst assemblyOptions: AssemblyPathOptions = { mergeByMaterial, debug, material };\n\tconst opened = await openBatch(blob, assemblyOptions);\n\tif (opened.meshes) return opened.meshes;\n\n\t// Heavy batches decode+assemble in a worker; null → do it here (small batch).\n\tconst assembledMeshes = await tryBuildViaAssembly(opened, assemblyOptions);\n\tif (assembledMeshes) return assembledMeshes;\n\n\tconst parsed = decodeRawMeshBatch(opened.raw);\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};\n}\n\n// Past this many source meshes, one THREE object each is the difference between a viewer that\n// holds 60fps and one that does not: render cost tracks object count, not triangles, and every\n// object also pays for its own edge overlay. A batch this size is an imported building model, where\n// merging is the only thing that keeps it interactive.\nconst UNMERGED_MESH_WARN_THRESHOLD = 1_000;\n\nlet warnedUnmerged = false;\n\n/** @internal Test seam: the warn-once flag is module state and would leak between cases. */\nexport function resetUnmergedWarning(): void {\n\twarnedUnmerged = false;\n}\n\n/**\n * Warns once when a caller opts out of merging on a batch large enough for it to hurt.\n *\n * Opting out is legitimate — it is what gives every source object its own THREE object — so this\n * does not override the caller. It exists because the failure is silent: nothing errors, the model\n * renders correctly, and the only symptom is a frame rate nobody traces back to a parse option.\n */\nfunction warnIfUnmergedAtScale(mergeByMaterial: boolean, groups: MaterialGroup[]): void {\n\tif (mergeByMaterial || warnedUnmerged) return;\n\n\tlet meshCount = 0;\n\tfor (const group of groups) meshCount += group.meshes.length;\n\tif (meshCount < UNMERGED_MESH_WARN_THRESHOLD) return;\n\n\twarnedUnmerged = true;\n\tgetLogger().warn(\n\t\t`Parsing ${meshCount} meshes with mergeByMaterial: false — each becomes its own THREE ` +\n\t\t\t`object, and render cost scales with object count. Drop the option to merge by material ` +\n\t\t\t`(the default); per-object identity survives it via userData.members.`\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\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\twarnIfUnmergedAtScale(mergeByMaterial, groups);\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 pickMaterial = createMaterialPicker(materialsSrc, {\n\t\tvertexColors: parsed.colors != null,\n\t\thasUvs: parsed.uvs != null,\n\t\tappearance: materialAppearance\n\t});\n\n\tconst meshes: THREE.Mesh[] = [];\n\n\tconst mergeGroups = mergeByMaterial ? mergeGroupsOf(groups) : groups;\n\n\tfor (const group of mergeGroups) {\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\tpickMaterial,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\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\tpickMaterial,\n\t\t\t\tparsed.uvs,\n\t\t\t\tparsed.colors\n\t\t\t);\n\t\t\tfor (const mesh of individualMeshes) meshes.push(mesh);\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// ASSEMBLY PATH: worker or inline, cached by geometry bytes\n// ============================================================================\n\ninterface AssemblyPathOptions {\n\tmergeByMaterial: boolean;\n\tdebug: boolean;\n\tmaterial?: MaterialAppearanceOptions;\n\tfallback?: BuildOptions['fallback'];\n}\n\ninterface JobRef {\n\tkind: 'merged' | 'single';\n\tgroup: MaterialGroup;\n\tmeshMeta?: MeshMetadata;\n}\n\ninterface OpenedGeometry {\n\tmeshes?: never;\n\traw: RawBinaryMeshBatch;\n\t/** The geometry chunk of an SLVM container: the finished-cache key. Null for a bare blob. */\n\tgeometryBytes: Uint8Array | null;\n\t/** The bytes `raw` was parsed from, whose buffer `raw`'s arrays may still alias. */\n\tsource: Uint8Array;\n\t/** Parses `source` again, for when `raw`'s arrays were transferred away. */\n\treparse: () => RawBinaryMeshBatch;\n}\n\ntype OpenedBatch = { meshes: THREE.Mesh[]; raw?: never } | OpenedGeometry;\n\n/**\n * Unwraps an SLVM container only as far as its table and materials, and answers from the\n * finished-geometry cache before paying for the inflate. A bare SLVA/SLVZ blob (old `.gh` files)\n * keeps its metadata inside the compressed geometry, so it always decodes.\n */\nasync function openBatch(\n\tinput: ArrayBuffer | Uint8Array | string,\n\topts: AssemblyPathOptions\n): Promise<OpenedBatch> {\n\tconst bytes = toUint8Array(input);\n\tif (!isSlvmContainer(bytes)) {\n\t\treturn {\n\t\t\traw: parseBinaryMeshBatchRaw(bytes),\n\t\t\tgeometryBytes: null,\n\t\t\tsource: bytes,\n\t\t\treparse: () => parseBinaryMeshBatchRaw(bytes)\n\t\t};\n\t}\n\n\tconst container = parseSlvmContainer(bytes);\n\tconst cached = buildFromFinishedCache(container, opts);\n\tif (cached) return { meshes: cached };\n\n\tconst geometryBlob = container.geometryBlob;\n\tconst reparse = (): RawBinaryMeshBatch => {\n\t\tconst raw = parseBinaryMeshBatchRaw(geometryBlob);\n\t\traw.metadata = container.metadata;\n\t\treturn raw;\n\t};\n\tconst inflated = await inflateInWorker(container);\n\tconst raw = inflated ? parseBinaryMeshBatchRaw(inflated) : reparse();\n\traw.metadata = container.metadata;\n\treturn { raw, geometryBytes: geometryBlob, source: geometryBlob, reparse };\n}\n\n// Below this the inflate takes under ~6 ms, less than the hop to a second worker costs.\nconst INFLATE_IN_WORKER_MIN_BYTES = 4 * 1024 * 1024;\n\n/**\n * Inflates a large SLVZ geometry chunk off the calling thread. Null means inflate it here: a small\n * or uncompressed chunk, no worker support, or a worker that failed.\n */\nasync function inflateInWorker(container: SlvmContainer): Promise<Uint8Array | null> {\n\tif (\n\t\tslvzInflatedBytes(container.geometryBlob) < INFLATE_IN_WORKER_MIN_BYTES ||\n\t\t!getAssemblyWorker()\n\t) {\n\t\treturn null;\n\t}\n\ttry {\n\t\treturn await inflateSlvzAsync(container.geometryBlob);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction buildFromFinishedCache(\n\tcontainer: SlvmContainer,\n\topts: AssemblyPathOptions\n): THREE.Mesh[] | null {\n\tconst materialsSrc = container.metadata.materials ?? opts.fallback?.materials ?? [];\n\tconst groups = container.metadata.groups ?? opts.fallback?.groups ?? [];\n\tconst { jobs, jobRefs } = planJobs(groups, opts.mergeByMaterial);\n\tconst cached = getFinishedBatch(container.geometryBlob, jobs);\n\tif (!cached) return null;\n\n\t// Equal jobs mean equal windows, but the material ids are this batch's own.\n\tvalidateGroupMetadata(groups, materialsSrc.length, cached.vertexCount, cached.indexCount);\n\treturn meshesFromBuilt(cached, jobRefs, materialsSrc, opts.material);\n}\n\n/** One job per merged group or per unmerged mesh, with a parallel ref list to unwrap results by index. */\nfunction planJobs(\n\tgroups: MaterialGroup[],\n\tmergeByMaterial: boolean\n): { jobs: AssemblyJob[]; jobRefs: JobRef[] } {\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\tclosed: m.closed\n\t});\n\tconst jobs: AssemblyJob[] = [];\n\tconst jobRefs: JobRef[] = [];\n\t// Same layer-aware merge grouping as the synchronous path.\n\tconst mergeGroups = mergeByMaterial ? mergeGroupsOf(groups) : groups;\n\tfor (const group of mergeGroups) {\n\t\tif (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\treturn { jobs, jobRefs };\n}\n\n/**\n * Builds a heavy batch through the assembly functions, in the worker when there is one and inline\n * otherwise, and caches the result under `geometryBytes`. Returns `null` for a small batch or a\n * crashed worker, and the caller then runs the synchronous path. Malformed-blob/metadata errors\n * throw either way, matching the entry points' contract.\n */\nasync function tryBuildViaAssembly(\n\topened: OpenedGeometry,\n\topts: AssemblyPathOptions\n): Promise<THREE.Mesh[] | null> {\n\tconst { raw, geometryBytes } = opened;\n\t// Planar (v4) indexData is a bare byte stream — divide by the element width for a count.\n\tconst indexCount = raw.planarByteSplit\n\t\t? raw.indexData.length / (raw.uint16Indices ? 2 : 4)\n\t\t: raw.indexData.length;\n\tif (indexCount / 3 < ASSEMBLY_WORKER_MIN_TRIANGLES) return null;\n\n\tconst materialsSrc = raw.metadata.materials ?? opts.fallback?.materials ?? [];\n\tconst groups = raw.metadata.groups ?? opts.fallback?.groups ?? [];\n\tvalidateGroupMetadata(groups, materialsSrc.length, raw.vertexCount, indexCount);\n\n\tconst { jobs, jobRefs } = planJobs(groups, opts.mergeByMaterial);\n\tconst assembled = await assemble(raw, jobs, opened.source);\n\tif (!assembled) {\n\t\t// A worker that failed already took the arrays: `decodeRawMeshBatch` needs fresh ones.\n\t\tif (ownsBuffer(raw, opened.source)) opened.raw = opened.reparse();\n\t\treturn null;\n\t}\n\n\tconst batch: FinishedBatch = {\n\t\tgeometries: assembled,\n\t\thasUvs: raw.uvs != null,\n\t\thasColors: raw.colors != null,\n\t\tvertexCount: raw.vertexCount,\n\t\tindexCount\n\t};\n\tconst built = geometryBytes ? storeFinishedBatch(geometryBytes, jobs, batch) : buildBatch(batch);\n\n\tif (opts.debug) {\n\t\tgetLogger().debug(\n\t\t\t`Mesh batch assembled: ${assembled.length} geometries, ${indexCount / 3} triangles`\n\t\t);\n\t}\n\treturn meshesFromBuilt(built, jobRefs, materialsSrc, opts.material);\n}\n\n/**\n * True when `raw`'s geometry arrays live in a buffer nobody else reads: the buffer a SLVZ chunk\n * was inflated into, not the WebSocket frame an uncompressed chunk still points into.\n */\nfunction ownsBuffer(raw: RawBinaryMeshBatch, source: Uint8Array): boolean {\n\treturn raw.vertexData.buffer !== source.buffer && raw.indexData.buffer !== source.buffer;\n}\n\nasync function assemble(\n\traw: RawBinaryMeshBatch,\n\tjobs: AssemblyJob[],\n\tsource: Uint8Array\n): Promise<FinishedGeometry[] | null> {\n\tconst worker = getAssemblyWorker();\n\tconst input = {\n\t\tvertexData: raw.vertexData,\n\t\tisFloat32: raw.isFloat32,\n\t\tdeltaEncoded: raw.deltaEncoded,\n\t\tplanarByteSplit: raw.planarByteSplit,\n\t\tuint16Indices: raw.uint16Indices,\n\t\torigin: raw.origin,\n\t\tscale: raw.scale,\n\t\tindexData: raw.indexData,\n\t\tuvs: raw.uvs,\n\t\tcolors: raw.colors,\n\t\tjobs\n\t};\n\n\tif (!worker) {\n\t\treturn assembleGeometries(input).map((geometry, i) =>\n\t\t\tfinishGeometry(geometry, jobs[i]!.windows)\n\t\t);\n\t}\n\n\t// Transferring detaches. An inflated buffer is ours to give away; an uncompressed chunk's arrays\n\t// alias the WebSocket frame, which the finished cache still reads afterwards, so those are copied.\n\tif (!ownsBuffer(raw, source)) {\n\t\tinput.vertexData = raw.vertexData.slice();\n\t\tinput.indexData = raw.indexData.slice();\n\t\tinput.uvs = raw.uvs?.slice() ?? null;\n\t\tinput.colors = raw.colors?.slice() ?? null;\n\t}\n\n\tconst transfer = new Set<ArrayBufferLike>([input.vertexData.buffer, input.indexData.buffer]);\n\tif (input.uvs) transfer.add(input.uvs.buffer);\n\tif (input.colors) transfer.add(input.colors.buffer);\n\ttry {\n\t\tconst assembled = await requestAssembly(worker, input, [...transfer] as Transferable[]);\n\t\treturn assembled.length === jobs.length ? assembled : null; // protocol mismatch → sync path\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}\n\nfunction meshesFromBuilt(\n\tbuilt: BuiltBatch,\n\tjobRefs: JobRef[],\n\tmaterialsSrc: SerializableMaterial[],\n\tappearance: MaterialAppearanceOptions | undefined\n): THREE.Mesh[] {\n\tconst pickMaterial = createMaterialPicker(materialsSrc, {\n\t\tvertexColors: built.hasColors,\n\t\thasUvs: built.hasUvs,\n\t\tappearance\n\t});\n\n\tconst meshes: THREE.Mesh[] = [];\n\tfor (let i = 0; i < built.buffers.length; i++) {\n\t\tconst geometry = built.buffers[i]!;\n\t\tconst ref = jobRefs[i]!;\n\t\tconst material = pickMaterial(ref.group.materialId, built.closed[i]!);\n\t\tconst mesh =\n\t\t\tref.kind === 'merged'\n\t\t\t\t? finalizeMergedMesh(geometry, ref.group, material)\n\t\t\t\t: finalizeSingleMesh(geometry, ref.meshMeta!, material);\n\t\tmeshes.push(mesh);\n\t}\n\treturn meshes;\n}\n\n// ============================================================================\n// DEBUG HELPERS\n// ============================================================================\n\nfunction approximateBase64DecodedBytes(base64: string): number {\n\treturn Math.floor((base64.length * 3) / 4);\n}\n","import * as THREE from 'three';\n\nimport {\n\tapplyOffset,\n\tcomputeCombinedBoundingBox,\n\tdisposeObjectTree,\n\tgetLogger\n} 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, GrasshopperComputeResponse } 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 getThreeObjectsFromComputeResponse(\n\tdata: GrasshopperComputeResponse,\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: GrasshopperComputeResponse,\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\t// Spread first, then re-apply the default for anything the caller left undefined: a caller\n\t\t// passing `{ material }` would otherwise carry `mergeByMaterial: undefined` over the default\n\t\t// and silently un-merge the batch.\n\t\tconst mergedParsingOptions: MeshBatchParsingOptions = {\n\t\t\tdebug: false,\n\t\t\t...parsingOptions,\n\t\t\tmergeByMaterial: parsingOptions.mergeByMaterial ?? true\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\tfor (const object of batchObjects) objects.push(object);\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 mesh of meshes) disposeObjectTree(mesh);\n}\n\nfunction logProcessingTime(startTime: number): void {\n\tconst elapsed = performance.now() - startTime;\n\tgetLogger().info('Time to process meshes:', `${elapsed.toFixed(2)}ms`);\n}\n"],"mappings":"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"}
|