@voluma/vlam 0.2.3 → 0.3.0

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.
Files changed (91) hide show
  1. package/README.md +3 -2
  2. package/dist/budget-governor.d.ts +4 -4
  3. package/dist/camera-budget-governor.d.ts +11 -11
  4. package/dist/chunk-cache-budget.d.ts +5 -5
  5. package/dist/chunk-fetch-scheduler.d.ts +4 -4
  6. package/dist/chunk-loader-DlNwggfG.js +1859 -0
  7. package/dist/chunk-loader-DlNwggfG.js.map +1 -0
  8. package/dist/chunk-loader.d.ts +2 -2
  9. package/dist/compute-sorter.d.ts +1 -1
  10. package/dist/effects.d.ts +26 -16
  11. package/dist/effects.js +199 -165
  12. package/dist/effects.js.map +1 -1
  13. package/dist/formats/ksplat/index.d.ts +1 -1
  14. package/dist/formats/lcc/collision-mesh.d.ts +2 -2
  15. package/dist/formats/lcc.js +17 -16
  16. package/dist/formats/lcc.js.map +1 -1
  17. package/dist/formats/ply/index.d.ts +1 -1
  18. package/dist/formats/ply/parse-splat-ply.d.ts +2 -2
  19. package/dist/formats/ply.js +1 -1
  20. package/dist/formats/ply.js.map +1 -1
  21. package/dist/formats/rad/index.d.ts +1 -1
  22. package/dist/formats/rad/parse-rad.d.ts +1 -1
  23. package/dist/formats/rad/rad.d.ts +2 -2
  24. package/dist/formats/rad.js +3 -3
  25. package/dist/formats/rad.js.map +1 -1
  26. package/dist/formats/sog/index.d.ts +1 -1
  27. package/dist/formats/sog/parse-sog.d.ts +4 -4
  28. package/dist/formats/sog.js.map +1 -1
  29. package/dist/formats/splat/index.d.ts +1 -1
  30. package/dist/formats/spz/index.d.ts +1 -1
  31. package/dist/index.d.ts +16 -25
  32. package/dist/index.js +253 -9047
  33. package/dist/index.js.map +1 -1
  34. package/dist/{load-scene.d.ts → load-splat-data.d.ts} +15 -13
  35. package/dist/loaders.d.ts +15 -0
  36. package/dist/loaders.js +26 -0
  37. package/dist/loaders.js.map +1 -0
  38. package/dist/loading-wLo8vRbA.js.map +1 -1
  39. package/dist/loading.d.ts +8 -8
  40. package/dist/{splat-mesh-types-CTN_duw2.js → lod-scheduler-B0a_uBlv.js} +133 -140
  41. package/dist/lod-scheduler-B0a_uBlv.js.map +1 -0
  42. package/dist/lod-scheduler.d.ts +1 -1
  43. package/dist/lod-source.d.ts +5 -5
  44. package/dist/{splat-scene.d.ts → merged-splat-mesh.d.ts} +14 -15
  45. package/dist/orientation.d.ts +7 -7
  46. package/dist/{ply-header-BwgExCn-.js → ply-header-B9WllbPg.js} +3 -3
  47. package/dist/{ply-header-BwgExCn-.js.map → ply-header-B9WllbPg.js.map} +1 -1
  48. package/dist/radix-sorter-R0FCSqKu.js +178 -0
  49. package/dist/radix-sorter-R0FCSqKu.js.map +1 -0
  50. package/dist/relighting-CSQF3UcL.js +969 -0
  51. package/dist/relighting-CSQF3UcL.js.map +1 -0
  52. package/dist/relighting.d.ts +1 -1
  53. package/dist/selection.d.ts +13 -0
  54. package/dist/selection.js +137 -0
  55. package/dist/selection.js.map +1 -0
  56. package/dist/source-transform.d.ts +4 -4
  57. package/dist/{splat-budget-DXew_86c.js → splat-budget-DuVZspPQ.js} +7 -7
  58. package/dist/{splat-budget-DXew_86c.js.map → splat-budget-DuVZspPQ.js.map} +1 -1
  59. package/dist/splat-budget.d.ts +1 -1
  60. package/dist/splat-data-BZM5dhhx.js.map +1 -1
  61. package/dist/splat-data.d.ts +4 -4
  62. package/dist/splat-mesh-aQdcWA6v.js +1942 -0
  63. package/dist/splat-mesh-aQdcWA6v.js.map +1 -0
  64. package/dist/splat-mesh-material.d.ts +2 -2
  65. package/dist/splat-mesh-types-BZIko-_9.js +18 -0
  66. package/dist/splat-mesh-types-BZIko-_9.js.map +1 -0
  67. package/dist/splat-mesh-types.d.ts +14 -8
  68. package/dist/splat-mesh.d.ts +4 -4
  69. package/dist/splat-modifier.d.ts +5 -5
  70. package/dist/splat-partition.d.ts +1 -1
  71. package/dist/static-lod-entry.d.ts +12 -0
  72. package/dist/static-lod-splat-mesh.d.ts +2 -2
  73. package/dist/{static-lod-worker-D2fyqQZO.js → static-lod-worker-DktKnJkR.js} +46 -46
  74. package/dist/{static-lod-worker-D2fyqQZO.js.map → static-lod-worker-DktKnJkR.js.map} +1 -1
  75. package/dist/static-lod.js +148 -0
  76. package/dist/static-lod.js.map +1 -0
  77. package/dist/streamed-splat-mesh.d.ts +21 -27
  78. package/dist/streaming.d.ts +23 -0
  79. package/dist/streaming.js +2877 -0
  80. package/dist/streaming.js.map +1 -0
  81. package/dist/{unified-splat-renderer.d.ts → unified-splat-mesh.d.ts} +10 -10
  82. package/dist/unified-work-buffer.d.ts +66 -0
  83. package/dist/unified.d.ts +11 -0
  84. package/dist/unified.js +738 -0
  85. package/dist/unified.js.map +1 -0
  86. package/dist/webgpu-limits-Y3CI_6_6.js +43 -0
  87. package/dist/webgpu-limits-Y3CI_6_6.js.map +1 -0
  88. package/dist/webgpu-limits.d.ts +0 -59
  89. package/dist/work-buffer-gather.d.ts +1 -1
  90. package/package.json +24 -4
  91. package/dist/splat-mesh-types-CTN_duw2.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"rad.js","sources":["../../src/lib/formats/rad/rad-column-decoders.ts","../../src/lib/formats/rad/parse-rad.ts","../../src/lib/formats/rad/rad-parent-sizes.ts","../../src/lib/formats/rad/rad-foveated-source.ts","../../src/lib/formats/rad/rad.ts"],"sourcesContent":["/** Typed-array column decoders used by the RAD container parser. */\nimport { halfToFloat } from '../../half-float';\nimport type { RadChunkProperty } from './parse-rad';\n\nfunction shEncodingBytes(prop: RadChunkProperty): number {\n if (prop.encoding === 'f32') return 4;\n if (prop.encoding === 'f16') return 2;\n return 1;\n}\n\nfunction requireRange(prop: RadChunkProperty): { min: number; max: number } {\n if (prop.min === undefined || prop.max === undefined) {\n throw new Error(`RAD \"${prop.property}\" (${prop.encoding}) is missing its min/max range.`);\n }\n return { min: prop.min, max: prop.max };\n}\n\nfunction requireMax(prop: RadChunkProperty): number {\n if (prop.max === undefined) {\n throw new Error(`RAD \"${prop.property}\" (${prop.encoding}) is missing its max range.`);\n }\n return prop.max;\n}\n\n/**\n * Asserts a (possibly decompressed) column physically holds one value per\n * splat per dimension before its `count`-sized output is allocated - the\n * cross-check between the untrusted chunk `count` and the actual bytes.\n */\nexport function assertColumnHoldsCount(\n data: Uint8Array,\n prop: RadChunkProperty,\n dims: number,\n count: number,\n bytesPerValue: number,\n): void {\n if (data.byteLength < count * dims * bytesPerValue) {\n throw new Error(\n `RAD \"${prop.property}\" column holds ${data.byteLength} bytes, fewer than the ` +\n `${count * dims * bytesPerValue} its declared ${count} splats need.`,\n );\n }\n}\n\nexport function decodeFloatColumn(\n data: Uint8Array,\n prop: RadChunkProperty,\n dims: number,\n count: number,\n): Float32Array {\n assertColumnHoldsCount(data, prop, dims, count, shEncodingBytes(prop));\n switch (prop.encoding) {\n case 'f32':\n return decodeF32(data, dims, count);\n case 'f16':\n return decodeF16(data, dims, count);\n case 'f32_lebytes':\n return decodeF32LeBytes(data, dims, count);\n case 'f16_lebytes':\n return decodeF16LeBytes(data, dims, count);\n case 'r8': {\n const { min, max } = requireRange(prop);\n return decodeR8(data, dims, count, min, max);\n }\n case 'r8_delta': {\n const { min, max } = requireRange(prop);\n return decodeR8Delta(data, dims, count, min, max);\n }\n case 's8':\n return decodeS8(data, dims, count, requireMax(prop));\n case 's8_delta':\n return decodeS8Delta(data, dims, count, requireMax(prop));\n case 'ln_0r8': {\n const { min, max } = requireRange(prop);\n return decodeLn0R8(data, dims, count, min, max);\n }\n case 'ln_f16':\n return decodeLnF16(data, dims, count);\n default:\n throw new Error(`Unsupported RAD \"${prop.property}\" encoding \"${prop.encoding}\".`);\n }\n}\n\nexport function decodeOrientation(\n data: Uint8Array,\n prop: RadChunkProperty,\n count: number,\n): Float32Array {\n if (prop.encoding === 'oct88r8') {\n assertColumnHoldsCount(data, prop, 3, count, 1);\n return decodeQuatOct88R8(data, count);\n }\n if (prop.encoding === 'f32' || prop.encoding === 'f16') {\n assertColumnHoldsCount(data, prop, 3, count, prop.encoding === 'f32' ? 4 : 2);\n }\n const xyz =\n prop.encoding === 'f32'\n ? decodeF32(data, 3, count)\n : prop.encoding === 'f16'\n ? decodeF16(data, 3, count)\n : undefined;\n if (!xyz) throw new Error(`Unsupported RAD orientation encoding \"${prop.encoding}\".`);\n const out = new Float32Array(count * 4);\n for (let i = 0; i < count; i++) {\n const x = xyz[i * 3 + 0] as number;\n const y = xyz[i * 3 + 1] as number;\n const z = xyz[i * 3 + 2] as number;\n out[i * 4 + 0] = x;\n out[i * 4 + 1] = y;\n out[i * 4 + 2] = z;\n out[i * 4 + 3] = Math.sqrt(Math.max(0, 1 - x * x - y * y - z * z));\n }\n return out;\n}\n\nexport function decodeChildCount(\n data: Uint8Array,\n prop: RadChunkProperty,\n count: number,\n): Uint16Array {\n if (prop.encoding !== 'u16') {\n throw new Error(`Unsupported RAD child_count encoding \"${prop.encoding}\".`);\n }\n assertColumnHoldsCount(data, prop, 1, count, 2);\n return decodeU16(data, count);\n}\n\nexport function decodeChildStart(\n data: Uint8Array,\n prop: RadChunkProperty,\n count: number,\n): Uint32Array {\n if (prop.encoding !== 'u32') {\n throw new Error(`Unsupported RAD child_start encoding \"${prop.encoding}\".`);\n }\n assertColumnHoldsCount(data, prop, 1, count, 4);\n return decodeU32(data, count);\n}\n\nconst scratch = new DataView(new ArrayBuffer(4));\n\nfunction bitsToFloat32(bits: number): number {\n scratch.setUint32(0, bits, true);\n return scratch.getFloat32(0, true);\n}\n\n/** Planar (column-per-dimension) little-endian f32, splat-major output. */\nfunction decodeF32(data: Uint8Array, dims: number, count: number): Float32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i * 4;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = view.getFloat32(index, true);\n index += count * 4;\n }\n }\n return out;\n}\n\nfunction decodeF16(data: Uint8Array, dims: number, count: number): Float32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i * 2;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = halfToFloat(view.getUint16(index, true));\n index += count * 2;\n }\n }\n return out;\n}\n\n/** Byte-plane-transposed f32: byte `b` of every value's LE encoding is\n * grouped together (compresses better when values are similar). */\nfunction decodeF32LeBytes(data: Uint8Array, dims: number, count: number): Float32Array {\n const stride = count * dims;\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n for (let d = 0; d < dims; d++) {\n const index = count * d + i;\n const bits =\n ((data[index] as number) |\n ((data[index + stride] as number) << 8) |\n ((data[index + stride * 2] as number) << 16) |\n ((data[index + stride * 3] as number) << 24)) >>>\n 0;\n out[i * dims + d] = bitsToFloat32(bits);\n }\n }\n return out;\n}\n\nfunction decodeF16LeBytes(data: Uint8Array, dims: number, count: number): Float32Array {\n const stride = count * dims;\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n for (let d = 0; d < dims; d++) {\n const index = count * d + i;\n const bits = (data[index] as number) | ((data[index + stride] as number) << 8);\n out[i * dims + d] = halfToFloat(bits);\n }\n }\n return out;\n}\n\n/** Planar unsigned-byte, linearly dequantized across `[min, max]`. */\nfunction decodeR8(\n data: Uint8Array,\n dims: number,\n count: number,\n min: number,\n max: number,\n): Float32Array {\n const out = new Float32Array(count * dims);\n const range = max - min;\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = ((data[index] as number) / 255) * range + min;\n index += count;\n }\n }\n return out;\n}\n\n/** Like {@link decodeR8}, but each column is a running byte-wise delta. */\nfunction decodeR8Delta(\n data: Uint8Array,\n dims: number,\n count: number,\n min: number,\n max: number,\n): Float32Array {\n const out = new Float32Array(count * dims);\n const range = max - min;\n const last = new Uint8Array(dims);\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const value = ((last[d] as number) + (data[index] as number)) & 0xff;\n last[d] = value;\n out[i * dims + d] = (value / 255) * range + min;\n index += count;\n }\n }\n return out;\n}\n\n/** Planar signed-byte, scaled by `max / 127`. */\nfunction decodeS8(data: Uint8Array, dims: number, count: number, max: number): Float32Array {\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const byte = data[index] as number;\n const signed = byte >= 128 ? byte - 256 : byte;\n out[i * dims + d] = (signed / 127) * max;\n index += count;\n }\n }\n return out;\n}\n\nfunction decodeS8Delta(data: Uint8Array, dims: number, count: number, max: number): Float32Array {\n const out = new Float32Array(count * dims);\n const last = new Uint8Array(dims);\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const value = ((last[d] as number) + (data[index] as number)) & 0xff;\n last[d] = value;\n const signed = value >= 128 ? value - 256 : value;\n out[i * dims + d] = (signed / 127) * max;\n index += count;\n }\n }\n return out;\n}\n\n/** Log-space scale: byte 0 means \"zero scale\", bytes 1-255 map linearly\n * across `[min, max]` in natural-log space before exponentiating back. */\nfunction decodeLn0R8(\n data: Uint8Array,\n dims: number,\n count: number,\n min: number,\n max: number,\n): Float32Array {\n const out = new Float32Array(count * dims);\n const step = (max - min) / 254;\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const byte = data[index] as number;\n out[i * dims + d] = byte === 0 ? 0 : Math.exp(min + (byte - 1) * step);\n index += count;\n }\n }\n return out;\n}\n\nfunction decodeLnF16(data: Uint8Array, dims: number, count: number): Float32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i * 2;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = Math.exp(halfToFloat(view.getUint16(index, true)));\n index += count * 2;\n }\n }\n return out;\n}\n\n/** Octahedral-mapped quaternion: 2 bytes for the rotation axis, 1 for the\n * half-angle. Splat-major output, 4 floats (x, y, z, w) per splat. */\nfunction decodeQuatOct88R8(data: Uint8Array, count: number): Float32Array {\n const out = new Float32Array(count * 4);\n for (let i = 0; i < count; i++) {\n const index = i * 3;\n const u = data[index] as number;\n const v = data[index + 1] as number;\n const r = data[index + 2] as number;\n let x = (u / 255) * 2 - 1;\n let y = (v / 255) * 2 - 1;\n const z = 1 - Math.abs(x) - Math.abs(y);\n const t = Math.max(-z, 0);\n x = x >= 0 ? x - t : x + t;\n y = y >= 0 ? y - t : y + t;\n const length = Math.hypot(x, y, z) || 1;\n const halfTheta = (r / 255) * 0.5 * Math.PI;\n const s = Math.sin(halfTheta);\n const w = Math.cos(halfTheta);\n out[i * 4 + 0] = (x / length) * s;\n out[i * 4 + 1] = (y / length) * s;\n out[i * 4 + 2] = (z / length) * s;\n out[i * 4 + 3] = w;\n }\n return out;\n}\n\n/** Planar little-endian u16. */\nexport function decodeU16(data: Uint8Array, count: number): Uint16Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Uint16Array(count);\n for (let i = 0; i < count; i++) out[i] = view.getUint16(i * 2, true);\n return out;\n}\n\n/** Planar little-endian u32. */\nexport function decodeU32(data: Uint8Array, count: number): Uint32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Uint32Array(count);\n for (let i = 0; i < count; i++) out[i] = view.getUint32(i * 4, true);\n return out;\n}\n","import {\n assertColumnHoldsCount,\n decodeU16,\n decodeU32,\n decodeChildCount,\n decodeChildStart,\n decodeFloatColumn,\n decodeOrientation,\n} from './rad-column-decoders';\nimport {\n writeCovariance,\n type RadShCodebook,\n type SplatData,\n type SplatPackedShData,\n} from '../../splat-data';\nimport { neutralShWord, packShCoefficients, shCoefficientCount } from '../../sh-pack';\n\n/**\n * Parser for World Labs' Spark `.rad`/`.radc` format (a precomputed LOD splat\n * tree with per-column, gzip/deflate-compressed properties). Ported from the\n * MIT-licensed Rust reference (`sparkjsdev/spark`, `rust/spark-lib/src/rad.rs`\n * and `splat_encode.rs`) since this project's worker has no Rust/WASM\n * toolchain - Spark's own decoder runs as WASM and writes into Spark's own\n * GPU texture layout, not this package's `SplatData`.\n *\n * Two entry points:\n * - {@link parseRad} flattens a whole file into leaf splats only (the original\n * capture), for a small `.rad` loaded in one piece by `loadScene`.\n * - {@link parseRadChunkStreaming} decodes one chunk keeping *every* splat -\n * the merged coarse LOD-tree nodes included - plus the per-splat tree\n * columns, for the streamed reader (see `rad.ts`), which chooses the\n * rendered cut across chunks at runtime.\n *\n * SH columns are repacked into the renderer's per-splat packed representation.\n */\n\nexport const RAD_MAGIC = 0x30444152; // 'RAD0' (LE)\nexport const RAD_CHUNK_MAGIC = 0x43444152; // 'RADC' (LE)\n\n/**\n * Upper bound on an untrusted splat count that will size **one** buffer - a\n * whole-file decode or a single chunk. Mirrors `parse-sog.ts` MAX_SPLAT_COUNT:\n * the renderer cannot draw more than 2²⁴ splats from one pool texture anyway\n * (2048 wide × 8192 max height), so a hostile count is rejected rather than\n * ballooning into multi-gigabyte allocations.\n *\n * It deliberately does **not** bound a `.rad` header's `count`, which is the\n * whole LOD-tree node total of a capture meant to stream (53M nodes for a 37M-\n * leaf scene) and sizes nothing - see {@link parseRadHeaderMeta}.\n */\nconst MAX_RAD_SPLAT_COUNT = 1 << 24;\n\n/** Asserts an untrusted RAD splat count is a safe non-negative integer. */\nfunction assertRadCountInteger(count: number, what: string): void {\n if (!Number.isSafeInteger(count) || count < 0) {\n throw new Error(`Invalid RAD ${what} splat count: ${count}.`);\n }\n}\n\n/** Asserts a count that will size one buffer stays under the pool ceiling. */\nfunction assertSaneRadCount(count: number, what: string): void {\n assertRadCountInteger(count, what);\n if (count > MAX_RAD_SPLAT_COUNT) {\n throw new Error(\n `RAD ${what} declares ${count} splats, above the supported maximum of ${MAX_RAD_SPLAT_COUNT}.`,\n );\n }\n}\n\n/** One chunk's location within a `.rad` file (or its external `.radc` name). */\nexport interface RadChunkRange {\n readonly offset: number;\n readonly bytes: number;\n readonly filename?: string;\n}\n\n/** The file-level `RAD0` metadata (a subset - only the fields we consume). */\nexport interface RadMeta {\n readonly version: number;\n readonly type: string;\n /** Total splats in the LOD tree (merged nodes + leaves). */\n readonly count: number;\n readonly maxSh?: number;\n readonly lodTree?: boolean;\n readonly chunkSize?: number;\n readonly chunks: readonly RadChunkRange[];\n /** Spark's free-form build-info JSON; carries `input_splat_count` (the leaf\n * count) among other fields. Parsed best-effort by the reader. */\n readonly comment?: string;\n}\n\n/** Per-splat LOD-tree links: an internal node's children are the contiguous\n * global-index range `[childStart, childStart + childCount)`. Leaves have\n * `childCount === 0`. */\nexport interface RadTree {\n readonly childCount: Uint16Array;\n readonly childStart: Uint32Array;\n}\n\nexport interface RadChunkProperty {\n readonly offset: number;\n readonly bytes: number;\n readonly property: string;\n readonly encoding: string;\n readonly compression?: string;\n readonly min?: number;\n readonly max?: number;\n}\n\ninterface RadChunkMeta {\n readonly version: number;\n readonly count: number;\n readonly lodTree?: boolean;\n readonly properties: readonly RadChunkProperty[];\n}\n\ninterface DecodedChunk {\n readonly count: number;\n readonly lodTree: boolean;\n /** xyz, splat-major. */\n readonly center: Float32Array;\n readonly alpha: Float32Array;\n readonly rgb: Float32Array;\n readonly scale: Float32Array;\n /** xyzw, splat-major. */\n readonly quat: Float32Array;\n readonly childCount?: Uint16Array;\n readonly childStart?: Uint32Array;\n readonly shPacked?: SplatPackedShData;\n readonly shCodebook?: RadShCodebook;\n}\n\n/**\n * Decodes a `.rad` file, or a lone `.radc` chunk, into a flat `SplatData`\n * of leaf splats only (the original capture). Merged coarse LOD-tree nodes\n * are dropped - they exist for streamed reading, and are meaningless once\n * everything is already decoded into one buffer.\n *\n * @throws {Error} if the file is malformed, an unsupported version, carries\n * an external `.radc` chunk reference (one-shot loading needs every chunk in\n * the same buffer), or uses a property encoding this parser does not\n * recognize.\n */\nexport async function parseRad(buffer: ArrayBuffer): Promise<SplatData> {\n const bytes = new Uint8Array(buffer);\n if (bytes.byteLength < 8) throw new Error('Truncated RAD file.');\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const magic = view.getUint32(0, true);\n\n if (magic === RAD_CHUNK_MAGIC) {\n return mergeChunks([await decodeChunk(bytes, 0, true)]);\n }\n const { meta, chunksStart } = parseRadHeaderMeta(buffer);\n // One-shot folds the whole file into a single `SplatData` (one pool texture),\n // so the pool ceiling binds here - and only here. The streamed reader keeps a\n // budget-bounded resident set and has no such limit (see `buildRadScene`).\n if (meta.count > MAX_RAD_SPLAT_COUNT) {\n throw new Error(\n `RAD header declares ${meta.count} splats, above the supported maximum of ` +\n `${MAX_RAD_SPLAT_COUNT} for a one-shot decode; load this file as a streamed scene instead.`,\n );\n }\n const chunks: DecodedChunk[] = [];\n let shCodebook: RadShCodebook | undefined;\n let shExtent: number | undefined;\n for (const [index, range] of meta.chunks.entries()) {\n if (range.filename !== undefined) {\n throw new Error(\n `RAD chunk ${index} is an external .radc file (\"${range.filename}\"); one-shot ` +\n 'loading only supports a single self-contained .rad file.',\n );\n }\n const start = chunksStart + range.offset;\n if (start + range.bytes > bytes.byteLength) {\n throw new Error(`RAD chunk ${index} extends past the file.`);\n }\n const chunk = await decodeChunk(bytes, start, true, shCodebook, shExtent);\n shCodebook ??= chunk.shCodebook;\n // Later chunks pack against the first SH chunk's range, so every chunk's\n // packed words share one scene range (see `decodeRadSh`).\n shExtent ??= chunk.shPacked?.range.max[0];\n chunks.push(chunk);\n }\n return mergeChunks(chunks);\n}\n\n/**\n * Parses just the `RAD0` header: the typed metadata and the byte offset where\n * the chunk stream begins. `buffer` need only be long enough to hold the\n * header - a ranged prefix of a large single-file `.rad` is enough.\n */\nexport function parseRadHeaderMeta(buffer: ArrayBuffer): { meta: RadMeta; chunksStart: number } {\n const bytes = new Uint8Array(buffer);\n if (bytes.byteLength < 8) throw new Error('Truncated RAD file.');\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const magic = view.getUint32(0, true);\n if (magic !== RAD_MAGIC) {\n throw new Error(`Not a RAD file (magic 0x${magic.toString(16).padStart(8, '0')}).`);\n }\n const length = view.getUint32(4, true);\n if (bytes.byteLength < 8 + length) throw new Error('Truncated RAD header.');\n const meta = JSON.parse(decodeUtf8(bytes, 8, length)) as RadMeta;\n if (meta.version !== 1) throw new Error(`Unsupported RAD version ${meta.version}.`);\n if (meta.type !== 'gsplat') throw new Error(`Unsupported RAD type \"${meta.type}\".`);\n assertRadCountInteger(meta.count, 'header');\n if (!Array.isArray(meta.chunks)) throw new Error('RAD header is missing its chunk list.');\n // `Array.isArray` narrows a `readonly T[]` to `any[]`; restore the declared\n // element type before touching members.\n const chunkRanges: readonly RadChunkRange[] = meta.chunks;\n for (const [index, range] of chunkRanges.entries()) {\n if (\n !Number.isSafeInteger(range?.offset) ||\n range.offset < 0 ||\n !Number.isSafeInteger(range?.bytes) ||\n range.bytes < 0\n ) {\n throw new Error(`RAD chunk ${index} declares an invalid byte range.`);\n }\n }\n // `count` is the whole LOD-tree node total and sizes nothing here (every\n // buffer downstream is per-chunk), so the pool ceiling would wrongly reject a\n // large streamed capture. Bound it structurally instead: no more splats than\n // the declared chunk table can physically hold.\n if (meta.chunkSize !== undefined) {\n if (\n !Number.isSafeInteger(meta.chunkSize) ||\n meta.chunkSize <= 0 ||\n meta.chunkSize > MAX_RAD_SPLAT_COUNT\n ) {\n throw new Error(`Invalid RAD chunk size: ${meta.chunkSize}.`);\n }\n if (meta.count > meta.chunkSize * chunkRanges.length) {\n throw new Error(\n `RAD header declares ${meta.count} splats, more than its ${chunkRanges.length} chunks of ` +\n `${meta.chunkSize} can hold.`,\n );\n }\n }\n return { meta, chunksStart: 8 + roundup8(length) };\n}\n\n/**\n * Decodes one chunk (a `RADC`-magic byte range) into a `SplatData` holding\n * **every** splat in the chunk - internal LOD-tree nodes and leaves alike -\n * plus the per-splat tree columns when the file carries a LOD tree. The\n * streamed reader keeps all splats resident and chooses the rendered cut\n * itself from {@link SplatData.radTree}.\n *\n * @throws {Error} on a malformed chunk or an unrecognized property encoding.\n */\nexport async function parseRadChunkStreaming(\n buffer: ArrayBuffer,\n shCodebook?: RadShCodebook,\n shExtent?: number,\n reorder = true,\n): Promise<SplatData> {\n const chunk = await decodeChunk(new Uint8Array(buffer), 0, true, shCodebook, shExtent);\n return chunkToSplatData(chunk, reorder);\n}\n\n/**\n * Builds a `SplatData` for every splat of a decoded chunk.\n *\n * For a LOD chunk the splats are reordered by descending \"frontier key\" -\n * leaves first, then internal nodes by `child_start` descending. The streamed\n * reader draws a splat when it is a leaf or its children are beyond the\n * resident prefix (`child_start ≥ residentCount`); with this order the drawn\n * splats of any chunk are always a contiguous **prefix**, so the frontier\n * coalesces into one run per chunk instead of thousands of fragments. The\n * reorder is safe because it never moves a splat's children out of their\n * chunk, and the reader only threshold-tests `child_start`, never follows it.\n */\nfunction chunkToSplatData(chunk: DecodedChunk, reorder: boolean): SplatData {\n const { count } = chunk;\n const positions = new Float32Array(count * 3);\n const colors = new Uint8Array(count * 4);\n const covariances = new Float32Array(count * 6);\n\n if (!chunk.childCount || !chunk.childStart) {\n for (let i = 0; i < count; i++) writeSplat(chunk, i, i, positions, colors, covariances);\n return {\n count,\n positions,\n colors,\n covariances,\n ...(chunk.shPacked ? { shPacked: chunk.shPacked } : {}),\n ...(chunk.shCodebook ? { radShCodebook: chunk.shCodebook } : {}),\n };\n }\n\n const childCount = chunk.childCount;\n const childStart = chunk.childStart;\n // The prefix reader needs leaves-first ordering to coalesce its frontier into\n // one run per chunk; the foveation reader keeps file order so `child_start`\n // stays valid for parent-size propagation (it renders whole chunks and lets\n // the GPU cull pick the cut per splat).\n const order = reorder ? frontierOrder(childCount, childStart, count) : identityOrder(count);\n const reorderedSh = reorderPackedSh(chunk.shPacked, order);\n const outCount = new Uint16Array(count);\n const outStart = new Uint32Array(count);\n const outSize = new Float32Array(count);\n for (let j = 0; j < count; j++) {\n const src = order[j] as number;\n writeSplat(chunk, src, j, positions, colors, covariances);\n outCount[j] = childCount[src] as number;\n outStart[j] = childStart[src] as number;\n outSize[j] = radNodeSize(chunk.alpha[src] as number, chunk.scale, src);\n }\n return {\n count,\n positions,\n colors,\n covariances,\n radTree: { childCount: outCount, childStart: outStart, size: outSize },\n ...(reorderedSh ? { shPacked: reorderedSh } : {}),\n ...(chunk.shCodebook ? { radShCodebook: chunk.shCodebook } : {}),\n };\n}\n\n/**\n * Spark's merged-node size-expansion factor: 1 for leaves (`alpha ≤ 1`), up to\n * 3.8 at `alpha = 2`. A merged node's `alpha ∈ (1,2]` encodes coverage\n * expansion - how much to enlarge the node so it covers its subtree - not\n * opacity. Spark's renderer applies it to the rendered gaussian; so do we (in\n * `writeSplat`). The LOD cut uses a steeper authored size - see\n * {@link radNodeSize}.\n */\nfunction radExpansion(alpha: number): number {\n return alpha <= 1 ? 1 : 1 + 0.7 * (alpha * 4 - 4);\n}\n\n/**\n * A LOD node's world-space size, matching Spark's `encode_lod_tree`\n * (`splat_encode.rs`): `2 · (max(alpha,1)·4 − 3) · avg(scale)`. This is the\n * size Spark's traversal compares against `pixel_scale_limit`, and it is\n * **steeper** than the rendered covariance expansion ({@link radExpansion}'s\n * `1 + 0.7·(4α−4)`, ≤3.8): at `alpha = 2` the traversal size factor is 5.\n * Using the render curve here made every merged node look 24–32% smaller to\n * the cut than to Spark's, so the descent stopped ~one LOD level early -\n * large-scale scenes rendered visibly coarser (streaky merged nodes) than\n * Spark at the same limit and budget.\n */\nfunction radNodeSize(alpha: number, scale: Float32Array, i: number): number {\n const avg =\n ((scale[i * 3] as number) + (scale[i * 3 + 1] as number) + (scale[i * 3 + 2] as number)) / 3;\n return 2 * (Math.max(alpha, 1) * 4 - 3) * avg;\n}\n\n/** The trivial permutation - splats stay in file order (foveation path). */\nfunction identityOrder(count: number): Uint32Array {\n const order = new Uint32Array(count);\n for (let i = 0; i < count; i++) order[i] = i;\n return order;\n}\n\n/** Splat indices sorted by descending frontier key (leaves before internal\n * nodes; internal nodes by `child_start` descending). */\nfunction frontierOrder(\n childCount: Uint16Array,\n childStart: Uint32Array,\n count: number,\n): Uint32Array {\n const order = new Uint32Array(count);\n for (let i = 0; i < count; i++) order[i] = i;\n // Leaves sort ahead of every internal node; among internal nodes, the ones\n // whose children lie deepest (largest child_start) survive in the frontier\n // longest, so they come first.\n const key = (i: number): number =>\n childCount[i] === 0 ? Number.MAX_SAFE_INTEGER : (childStart[i] as number);\n return order.sort((a, b) => key(b) - key(a));\n}\n\n/** Decodes one `RADC`-magic chunk starting at `chunkStart` within `bytes`. */\nasync function decodeChunk(\n bytes: Uint8Array,\n chunkStart: number,\n withTree: boolean,\n knownShCodebook?: RadShCodebook,\n knownShExtent?: number,\n): Promise<DecodedChunk> {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const magic = view.getUint32(chunkStart, true);\n if (magic !== RAD_CHUNK_MAGIC) {\n throw new Error(`Not a RAD chunk (magic 0x${magic.toString(16).padStart(8, '0')}).`);\n }\n const length = view.getUint32(chunkStart + 4, true);\n const metaEnd = chunkStart + 8 + roundup8(length);\n const chunkMeta = JSON.parse(decodeUtf8(bytes, chunkStart + 8, length)) as RadChunkMeta;\n if (chunkMeta.version !== 1) {\n throw new Error(`Unsupported RAD chunk version ${chunkMeta.version}.`);\n }\n const payloadBytes = Number(view.getBigUint64(metaEnd, true));\n const payloadStart = metaEnd + 8;\n if (payloadStart + payloadBytes > bytes.byteLength) {\n throw new Error('RAD chunk payload extends past the file.');\n }\n const { count } = chunkMeta;\n assertSaneRadCount(count, 'chunk');\n const lodTree = chunkMeta.lodTree ?? false;\n\n if (!Array.isArray(chunkMeta.properties)) {\n throw new Error('RAD chunk metadata is missing its property list.');\n }\n // `Array.isArray` narrows a `readonly T[]` to `any[]`; restore the declared\n // element type before touching members.\n const properties: readonly RadChunkProperty[] = chunkMeta.properties;\n const byName = new Map(properties.map((prop) => [prop.property, prop]));\n const readProp = async (name: string): Promise<Uint8Array | undefined> => {\n const prop = byName.get(name);\n if (!prop) return undefined;\n if (\n !Number.isSafeInteger(prop.offset) ||\n prop.offset < 0 ||\n !Number.isSafeInteger(prop.bytes) ||\n prop.bytes < 0\n ) {\n throw new Error(`RAD property \"${name}\" declares an invalid byte range.`);\n }\n const start = payloadStart + prop.offset;\n if (prop.offset + prop.bytes > payloadBytes || start + prop.bytes > bytes.byteLength) {\n throw new Error(`RAD property \"${name}\" extends past its chunk.`);\n }\n const raw = bytes.subarray(start, start + prop.bytes);\n return prop.compression === undefined ? raw : decompressColumn(raw);\n };\n\n const wantTree = withTree && lodTree;\n const propertyNames: readonly (string | undefined)[] = [\n 'center',\n 'alpha',\n 'rgb',\n 'scales',\n 'orientation',\n ...(wantTree ? ['child_count', 'child_start'] : [undefined, undefined]),\n 'sh1',\n 'sh2',\n 'sh3',\n 'sh_label',\n 'sh1_code',\n 'sh2_code',\n 'sh3_code',\n ];\n const [\n centerRaw,\n alphaRaw,\n rgbRaw,\n scaleRaw,\n orientationRaw,\n childCountRaw,\n childStartRaw,\n sh1Raw,\n sh2Raw,\n sh3Raw,\n shLabelRaw,\n sh1CodeRaw,\n sh2CodeRaw,\n sh3CodeRaw,\n ] = await Promise.all(\n propertyNames.map((name) => (name === undefined ? Promise.resolve(undefined) : readProp(name))),\n );\n if (!centerRaw) throw new Error('RAD chunk is missing its \"center\" property.');\n if (!alphaRaw) throw new Error('RAD chunk is missing its \"alpha\" property.');\n if (!rgbRaw) throw new Error('RAD chunk is missing its \"rgb\" property.');\n if (!scaleRaw) throw new Error('RAD chunk is missing its \"scales\" property.');\n if (!orientationRaw) throw new Error('RAD chunk is missing its \"orientation\" property.');\n\n return {\n count,\n lodTree,\n center: decodeFloatColumn(centerRaw, byName.get('center')!, 3, count),\n alpha: decodeFloatColumn(alphaRaw, byName.get('alpha')!, 1, count),\n rgb: decodeFloatColumn(rgbRaw, byName.get('rgb')!, 3, count),\n scale: decodeFloatColumn(scaleRaw, byName.get('scales')!, 3, count),\n quat: decodeOrientation(orientationRaw, byName.get('orientation')!, count),\n ...(wantTree && childCountRaw\n ? { childCount: decodeChildCount(childCountRaw, byName.get('child_count')!, count) }\n : {}),\n ...(wantTree && childStartRaw\n ? { childStart: decodeChildStart(childStartRaw, byName.get('child_start')!, count) }\n : {}),\n ...decodeRadSh({\n count,\n byName,\n sh1Raw,\n sh2Raw,\n sh3Raw,\n shLabelRaw,\n sh1CodeRaw,\n sh2CodeRaw,\n sh3CodeRaw,\n knownShCodebook,\n knownShExtent,\n }),\n };\n}\n\n/**\n * Flattens decoded chunks into leaf splats only: internal LOD-tree nodes\n * (`childCount > 0`) are merged approximations of their children and are\n * dropped, same as loading only the finest level of any other LOD format.\n */\nfunction mergeChunks(chunks: readonly DecodedChunk[]): SplatData {\n let total = 0;\n for (const chunk of chunks) {\n total += chunk.childCount ? countLeaves(chunk.childCount) : chunk.count;\n }\n if (total === 0) throw new Error('RAD file has no leaf splats.');\n\n const positions = new Float32Array(total * 3);\n const colors = new Uint8Array(total * 4);\n const covariances = new Float32Array(total * 6);\n const sourceSh = chunks.find((chunk) => chunk.shPacked)?.shPacked;\n const packed = sourceSh ? new Uint32Array(total * shCoefficientCount(sourceSh.bands)) : undefined;\n // A splat from an SH-less chunk must encode \"0.0\", not word 0 (which\n // decodes to the range minimum). In practice Spark writes SH columns to\n // every chunk when the scene has SH, so this is belt-and-braces.\n if (packed && sourceSh) packed.fill(neutralShWord(sourceSh.range));\n\n let out = 0;\n for (const chunk of chunks) {\n for (let i = 0; i < chunk.count; i++) {\n if (chunk.childCount && chunk.childCount[i] !== 0) continue; // internal node\n writeSplat(chunk, i, out, positions, colors, covariances);\n if (packed && chunk.shPacked) {\n const words = shCoefficientCount(sourceSh!.bands);\n packed.set(chunk.shPacked.packed.subarray(i * words, (i + 1) * words), out * words);\n }\n out++;\n }\n }\n\n return {\n count: total,\n positions,\n colors,\n covariances,\n ...(sourceSh && packed ? { shPacked: { ...sourceSh, packed } } : {}),\n };\n}\n\nfunction reorderPackedSh(\n sh: SplatPackedShData | undefined,\n order: Uint32Array,\n): SplatPackedShData | undefined {\n if (!sh) return undefined;\n const words = shCoefficientCount(sh.bands);\n const packed = new Uint32Array(sh.packed.length);\n for (let dst = 0; dst < order.length; dst++) {\n const src = order[dst] as number;\n packed.set(sh.packed.subarray(src * words, (src + 1) * words), dst * words);\n }\n return { ...sh, packed };\n}\n\ninterface RadShInputs {\n readonly count: number;\n readonly byName: ReadonlyMap<string, RadChunkProperty>;\n readonly sh1Raw?: Uint8Array;\n readonly sh2Raw?: Uint8Array;\n readonly sh3Raw?: Uint8Array;\n readonly shLabelRaw?: Uint8Array;\n readonly sh1CodeRaw?: Uint8Array;\n readonly sh2CodeRaw?: Uint8Array;\n readonly sh3CodeRaw?: Uint8Array;\n readonly knownShCodebook?: RadShCodebook;\n readonly knownShExtent?: number;\n}\n\n/**\n * Decodes Spark's direct columns or its chunk-zero codebook and labels.\n *\n * The quantization range must be the same for every chunk of a file - the\n * shared pool decodes all chunks against one scene range - so the extent is\n * never measured from a lone chunk's values if a chunk-invariant source\n * exists: the column metadata range (Spark encodes every chunk against\n * scene-global per-band maxima), the codebook (stored once, in chunk zero),\n * or the extent already established by an earlier chunk (`knownShExtent`).\n */\nfunction decodeRadSh(input: RadShInputs): Pick<DecodedChunk, 'shPacked' | 'shCodebook'> {\n const direct = [input.sh1Raw, input.sh2Raw, input.sh3Raw];\n const code = [input.sh1CodeRaw, input.sh2CodeRaw, input.sh3CodeRaw];\n const directBands = direct.reduce((bands, raw, index) => (raw ? index + 1 : bands), 0);\n const codeBands = code.reduce((bands, raw, index) => (raw ? index + 1 : bands), 0);\n if (directBands === 0 && codeBands === 0 && !input.shLabelRaw) return {};\n if (directBands > 0 && codeBands > 0)\n throw new Error('RAD chunk mixes direct and codebook SH columns.');\n\n let coefficients: Float32Array;\n let bands: 1 | 2 | 3;\n let codebook: RadShCodebook | undefined;\n let extent: number | undefined;\n if (directBands > 0) {\n bands = directBands as 1 | 2 | 3;\n coefficients = combineShColumns(direct, input.byName, input.count, bands);\n extent = metadataShExtent(input.byName, bands) ?? input.knownShExtent;\n } else {\n codebook = input.knownShCodebook;\n if (!codebook && codeBands > 0) {\n bands = codeBands as 1 | 2 | 3;\n const codeCount = shColumnCount(\n code[codeBands - 1]!,\n input.byName.get(`sh${codeBands}_code`)!,\n );\n codebook = {\n bands,\n count: codeCount,\n coefficients: combineShColumns(code, input.byName, codeCount, bands, '_code'),\n };\n }\n if (!codebook) throw new Error('RAD SH labels require the codebook stored in chunk zero.');\n if (!input.shLabelRaw)\n throw new Error('RAD SH codebook chunk is missing its \"sh_label\" property.');\n bands = codebook.bands;\n const labels = decodeShLabels(input.shLabelRaw, input.byName.get('sh_label')!, input.count);\n coefficients = new Float32Array(input.count * shCoefficientCount(bands) * 3);\n const words = shCoefficientCount(bands) * 3;\n for (let i = 0; i < input.count; i++) {\n const label = labels[i] as number;\n if (label >= codebook.count)\n throw new Error(`RAD SH label ${label} exceeds its ${codebook.count}-entry codebook.`);\n coefficients.set(\n codebook.coefficients.subarray(label * words, (label + 1) * words),\n i * words,\n );\n }\n // The codebook is whole-scene, so its extent is the same whichever chunk\n // computes it - unlike the extent of one chunk's referenced entries.\n extent = 0;\n for (const value of codebook.coefficients) extent = Math.max(extent, Math.abs(value));\n }\n return {\n shPacked: packShCoefficients(coefficients, input.count, bands, extent),\n ...(codebook ? { shCodebook: codebook } : {}),\n };\n}\n\n/**\n * The scene-global SH extent recorded in the column metadata, when every band\n * is stored quantized. Spark computes one `shN_max` per band over the whole\n * scene and encodes every chunk against it (`rad.rs`), so these ranges are\n * chunk-invariant. Raw-float columns (`f32`/`f16`/`*_lebytes`) carry no range\n * - returns undefined and the caller falls back to a measured extent.\n */\nfunction metadataShExtent(\n byName: ReadonlyMap<string, RadChunkProperty>,\n bands: 1 | 2 | 3,\n suffix = '',\n): number | undefined {\n let extent = 0;\n for (let band = 1; band <= bands; band++) {\n const prop = byName.get(`sh${band}${suffix}`);\n if (!prop) return undefined;\n let bandExtent: number;\n switch (prop.encoding) {\n case 's8':\n case 's8_delta':\n if (prop.max === undefined) return undefined;\n bandExtent = Math.abs(prop.max);\n break;\n case 'r8':\n case 'r8_delta':\n if (prop.min === undefined || prop.max === undefined) return undefined;\n bandExtent = Math.max(Math.abs(prop.min), Math.abs(prop.max));\n break;\n default:\n return undefined;\n }\n extent = Math.max(extent, bandExtent);\n }\n return extent;\n}\n\n/** Interleaves the per-band columns into one splat-major array: splat `i`\n * holds its band-1, band-2, band-3 coefficient triples contiguously, the\n * order `packSh` and the codebook expansion consume. */\nfunction combineShColumns(\n raws: readonly (Uint8Array | undefined)[],\n byName: ReadonlyMap<string, RadChunkProperty>,\n count: number,\n bands: 1 | 2 | 3,\n suffix = '',\n): Float32Array {\n const totalCoefficients = shCoefficientCount(bands);\n const result = new Float32Array(count * totalCoefficients * 3);\n let coefficientOffset = 0;\n for (let band = 1; band <= bands; band++) {\n const raw = raws[band - 1];\n const prop = byName.get(`sh${band}${suffix}`);\n if (!raw || !prop) throw new Error(`RAD SH is missing its \"sh${band}${suffix}\" column.`);\n const dims = band === 1 ? 9 : band === 2 ? 15 : 21;\n const values = decodeFloatColumn(raw, prop, dims, count);\n for (let i = 0; i < count; i++) {\n const base = (i * totalCoefficients + coefficientOffset) * 3;\n for (let k = 0; k < dims; k++) result[base + k] = values[i * dims + k] as number;\n }\n coefficientOffset += dims / 3;\n }\n return result;\n}\n\nfunction shColumnCount(raw: Uint8Array, prop: RadChunkProperty): number {\n const dimensions = prop.property.startsWith('sh1')\n ? 9\n : prop.property.startsWith('sh2')\n ? 15\n : 21;\n const bytesPerValue = shEncodingBytes(prop);\n if (raw.byteLength % (dimensions * bytesPerValue) !== 0)\n throw new Error(`RAD \"${prop.property}\" has an invalid SH column length.`);\n return raw.byteLength / (dimensions * bytesPerValue);\n}\n\n/** Bytes per decoded value of an SH column's encoding. */\nfunction shEncodingBytes(prop: RadChunkProperty): number {\n switch (prop.encoding) {\n case 'f32':\n case 'f32_lebytes':\n return 4;\n case 'f16':\n case 'f16_lebytes':\n case 'ln_f16':\n return 2;\n case 'r8':\n case 'r8_delta':\n case 's8':\n case 's8_delta':\n case 'ln_0r8':\n return 1;\n default:\n throw new Error(`Unsupported RAD \"${prop.property}\" encoding \"${prop.encoding}\".`);\n }\n}\n\nfunction decodeShLabels(raw: Uint8Array, prop: RadChunkProperty, count: number): Uint32Array {\n if (prop.encoding === 'u16') {\n assertColumnHoldsCount(raw, prop, 1, count, 2);\n return Uint32Array.from(decodeU16(raw, count));\n }\n if (prop.encoding === 'u32') {\n assertColumnHoldsCount(raw, prop, 1, count, 4);\n return decodeU32(raw, count);\n }\n throw new Error(`Unsupported RAD sh_label encoding \"${prop.encoding}\".`);\n}\n\n/** Writes splat `src` of a decoded chunk into slot `dst` of the output arrays. */\nfunction writeSplat(\n chunk: DecodedChunk,\n src: number,\n dst: number,\n positions: Float32Array,\n colors: Uint8Array,\n covariances: Float32Array,\n): void {\n // Spark's LOD alpha encoding: store `alpha / 2` in the 8-bit channel so the\n // shader (`×2`) recovers `alpha ∈ [0, 2]`. A leaf's opacity is `≤ 1`; a merged\n // node's `alpha ∈ (1, 2]` is a size marker (not opacity) - the material grows\n // that splat's σ-cutoff and reshapes its falloff to cover its subtree, rather\n // than scaling the covariance (which over-inflates and blurs). The covariance\n // is therefore the raw fitted shape for every splat.\n const alpha = chunk.alpha[src] as number;\n positions[dst * 3 + 0] = chunk.center[src * 3 + 0] as number;\n positions[dst * 3 + 1] = chunk.center[src * 3 + 1] as number;\n positions[dst * 3 + 2] = chunk.center[src * 3 + 2] as number;\n colors[dst * 4 + 0] = clampByte((chunk.rgb[src * 3 + 0] as number) * 255);\n colors[dst * 4 + 1] = clampByte((chunk.rgb[src * 3 + 1] as number) * 255);\n colors[dst * 4 + 2] = clampByte((chunk.rgb[src * 3 + 2] as number) * 255);\n colors[dst * 4 + 3] = clampByte(alpha * 0.5 * 255);\n writeCovariance(\n covariances,\n dst,\n chunk.scale[src * 3 + 0] as number,\n chunk.scale[src * 3 + 1] as number,\n chunk.scale[src * 3 + 2] as number,\n chunk.quat[src * 4 + 3] as number, // w\n chunk.quat[src * 4 + 0] as number, // x\n chunk.quat[src * 4 + 1] as number, // y\n chunk.quat[src * 4 + 2] as number, // z\n );\n}\n\nfunction countLeaves(childCount: Uint16Array): number {\n let leaves = 0;\n for (const value of childCount) if (value === 0) leaves++;\n return leaves;\n}\n\nfunction clampByte(value: number): number {\n return Math.max(0, Math.min(255, Math.round(value)));\n}\n\nfunction roundup8(size: number): number {\n return (size + 7) & ~7;\n}\n\nfunction decodeUtf8(bytes: Uint8Array, offset: number, length: number): string {\n return new TextDecoder().decode(bytes.subarray(offset, offset + length));\n}\n\n/**\n * Spark compresses each property column with `miniz_oxide::deflate`, which\n * produces a raw DEFLATE stream (no zlib/gzip wrapper) despite the format\n * metadata calling it `\"gz\"`. Bytes are sniffed rather than trusted, so a\n * future Spark version that switches to a real gzip/zlib wrapper still decodes.\n */\nasync function decompressColumn(bytes: Uint8Array): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error('This runtime does not provide decompression required for RAD files.');\n }\n const format: CompressionFormat =\n bytes[0] === 0x1f && bytes[1] === 0x8b ? 'gzip' : bytes[0] === 0x78 ? 'deflate' : 'deflate-raw';\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n const stream = new Blob([copy.buffer]).stream().pipeThrough(new DecompressionStream(format));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n}\n","/**\n * Propagates each `.rad` splat's **parent LOD node size** as chunks stream in.\n *\n * The foveation cut (see `docs/formats/rad-notes.md` M14.6) draws a splat `i` when\n * `parentPixelScale(i) > limit ≥ ownPixelScale(i)` - exactly one node per\n * root→leaf path, since `pixel_scale = size / distance` decreases monotonically\n * down the tree. That test needs each splat's *parent's* `size`, but the file\n * only stores the forward links (`child_start`/`child_count`): a node lists its\n * children, not its parent.\n *\n * So we invert the links incrementally. When a chunk decodes, each internal\n * node's `size` is recorded against the global index range of its children\n * (`[child_start, child_start + child_count)`); a later chunk holding those\n * children reads its own parent sizes back out. Because chunks stream roughly\n * coarsest-first (a node's parent is always at a lower global index, and the\n * coarse base loads first), parents are normally decoded before their children.\n * A child whose parent has not decoded yet gets `Infinity` - treated as a root,\n * so it draws whenever it is small enough on screen; the worst case is a little\n * over-draw, never a hole or a crash.\n *\n * This decode order is the **original** chunk order (no frontier reorder), so\n * `child_start` still points at the right splats - the foveation path keeps\n * splats in file order and lets the GPU cull choose the cut per splat.\n */\nexport class RadParentSizes {\n private readonly chunkSize: number;\n /** Parent-size ranges awaiting the child chunk that will consume them,\n * keyed by that child chunk index. Each range is a global-index span. */\n private readonly pending = new Map<number, { start: number; end: number; size: number }[]>();\n /** Chunks already resolved - a parent decoded after its child can't fix it,\n * so we drop such stragglers rather than leak them. */\n private readonly resolved = new Set<number>();\n\n constructor(chunkSize: number) {\n this.chunkSize = chunkSize;\n }\n\n /**\n * Records `chunkIndex`'s internal→child links and returns that chunk's\n * per-splat parent sizes (in the chunk's own splat order). Call once per\n * chunk, as it decodes.\n *\n * @param childCount - Per-splat child count (0 = leaf).\n * @param childStart - Per-splat first-child global index.\n * @param size - Per-splat LOD node size.\n */\n resolve(\n chunkIndex: number,\n childCount: Uint16Array,\n childStart: Uint32Array,\n size: Float32Array,\n ): Float32Array {\n const count = childCount.length;\n const base = chunkIndex * this.chunkSize;\n // No known parent → Infinity (root-like: drawn when small enough on screen).\n const parentSize = new Float32Array(count).fill(Infinity);\n\n const ranges = this.pending.get(chunkIndex);\n if (ranges) {\n for (const range of ranges) {\n const from = Math.max(range.start - base, 0);\n const to = Math.min(range.end - base, count);\n for (let local = from; local < to; local++) parentSize[local] = range.size;\n }\n this.pending.delete(chunkIndex);\n }\n this.resolved.add(chunkIndex);\n\n // Record this chunk's children so they can read their parent size later.\n for (let i = 0; i < count; i++) {\n const cc = childCount[i] as number;\n if (cc === 0) continue;\n const parentNodeSize = size[i] as number;\n let g = childStart[i] as number;\n const end = g + cc;\n // Children are contiguous but may straddle a chunk boundary (rare).\n while (g < end) {\n const childChunk = Math.floor(g / this.chunkSize);\n const chunkEnd = (childChunk + 1) * this.chunkSize;\n const rangeEnd = Math.min(end, chunkEnd);\n if (!this.resolved.has(childChunk)) this.push(childChunk, g, rangeEnd, parentNodeSize);\n g = rangeEnd;\n }\n }\n\n return parentSize;\n }\n\n private push(childChunk: number, start: number, end: number, size: number): void {\n let ranges = this.pending.get(childChunk);\n if (!ranges) {\n ranges = [];\n this.pending.set(childChunk, ranges);\n }\n ranges.push({ start, end, size });\n }\n}\n","import * as THREE from 'three/webgpu';\nimport type { LodRun } from '../../lod-scheduler';\nimport type { LodSource, LodSourceOptions } from '../../lod-source';\nimport { RadParentSizes } from './rad-parent-sizes';\nimport type { SplatData } from '../../splat-data';\n\n/** Stand-in for an infinite parent size (root, or parent not yet decoded); the\n * frontier cut treats any node at this size as \"always big enough to keep\".\n * Matches `FRONTIER_ROOT_SIZE` in the material. */\nconst FRONTIER_ROOT_SIZE = 1e30;\n\n/** Spark's ~1.75× LOD merge ratio: a node's parent is roughly this much bigger.\n * Used as the `parent_size` fallback when the real parent has not decoded yet. */\nconst RAD_MERGE_RATIO = 1.75;\n\n/**\n * LOD source for a `.rad` too large to load whole (its leaf count exceeds the\n * budget-lift ceiling). Where {@link RadLodSource} renders the frontier of a\n * coarse→fine *prefix* - uniform detail, capped by the budget - this loads a\n * camera-directed **set** of chunks and renders them whole, letting the\n * material's screen-radius band cull pick the LOD cut per splat.\n *\n * The resident set is chunk 0's whole-scene coarse shell (a few shallow chunks,\n * always resident for far coverage) plus the nearest chunks by bounds distance,\n * up to the budget. There is **no ancestor-closedness requirement** - the\n * per-splat band cull is correct over any resident set (it draws exactly the\n * splats whose on-screen size lands in the band, one LOD level per view ray),\n * so it sidesteps the entangled-DAG wall that defeats a chunk cut. Loading\n * follows the camera: fetch the nearest undecoded refinements of the resident\n * set. See `docs/formats/rad-notes.md` M14.6.\n */\n\n/** Undecoded refinements fetched per reschedule to pull detail in. Ranked by\n * on-screen coarseness (Spark's descend-biggest-pixel-scale-first), so the near,\n * in-view, under-refined regions load ahead of distant ones. */\nconst PREFETCH_AHEAD = 16;\n/** Out-of-frustum regions are deprioritized by this factor (they are not drawn,\n * so refine what is on screen first). */\nconst FRUSTUM_PENALTY = 8;\n/** Shallowest chunks kept resident always, for whole-scene coarse coverage. */\nconst COARSE_BASE = 4;\n/** Floor on distance when scoring, so a splat at the camera can't divide by ~0. */\nconst MIN_SCORE_DISTANCE = 0.05;\n\nexport class RadFoveatedSource implements LodSource {\n budget: number;\n lodBaseDistance: number;\n lodMultiplier: number;\n\n private readonly chunkSize: number;\n private readonly chunkCounts: readonly number[];\n private readonly numChunks: number;\n private readonly decoded = new Set<number>();\n /** Real bounds of a decoded chunk (AABB of its splat centers). */\n private readonly bounds: (THREE.Box3 | null)[];\n /** Region estimated from decoded parents, to rank an undecoded chunk. */\n private readonly estBounds: (THREE.Box3 | null)[];\n /** Per chunk: the chunks its internal splats refine into (for prefetch). */\n private readonly childChunks: (Set<number> | null)[];\n /** Inverts the tree's forward links to give each splat its parent's size,\n * for the frontier cut. Fed the same `own_size` measure the material derives. */\n private readonly parentSizes: RadParentSizes;\n\n /** Splats chunk 0's coarse shell draws (the minimum-coverage floor). */\n coverageSplatCount = 1;\n\n /**\n * Whether per-splat `parent_size` is needed. Only the GPU cuts\n * (`foveationMode: 'band' | 'frontier'`) read `frontierParent`; the page-table\n * pager runs its own traversal in the worker and `gatherGlobals` never carries\n * it. Computing it anyway costs a full covariance pass per chunk *and* leaves\n * one pending-range object per internal node alive in {@link RadParentSizes}\n * until its child chunk decodes - tens of millions of live objects on an\n * 800-chunk capture, enough GC pressure to read as a frozen page.\n */\n needsParentSizes = true;\n\n private readonly scratchPoint = new THREE.Vector3();\n\n constructor(chunkSize: number, chunkCounts: readonly number[], options: LodSourceOptions) {\n this.chunkSize = chunkSize;\n this.chunkCounts = chunkCounts;\n this.numChunks = chunkCounts.length;\n this.budget = options.budget;\n this.lodBaseDistance = options.lodBaseDistance;\n this.lodMultiplier = options.lodMultiplier;\n this.bounds = Array.from({ length: this.numChunks }, () => null);\n this.estBounds = Array.from({ length: this.numChunks }, () => null);\n this.childChunks = Array.from({ length: this.numChunks }, () => null);\n this.parentSizes = new RadParentSizes(chunkSize);\n }\n\n onChunkDecoded(file: number, data: SplatData): void {\n if (this.decoded.has(file)) return;\n this.decoded.add(file);\n this.bounds[file] = boundsOf(data);\n\n const tree = data.radTree;\n if (tree) {\n const children = new Set<number>();\n const pos = data.positions;\n for (let i = 0; i < tree.childCount.length; i++) {\n if (tree.childCount[i] === 0) continue;\n const childChunk = Math.floor((tree.childStart[i] as number) / this.chunkSize);\n children.add(childChunk);\n let est = this.estBounds[childChunk];\n if (!est) {\n est = new THREE.Box3().makeEmpty();\n this.estBounds[childChunk] = est;\n }\n est.expandByPoint(\n this.scratchPoint.set(pos[i * 3] ?? 0, pos[i * 3 + 1] ?? 0, pos[i * 3 + 2] ?? 0),\n );\n }\n this.childChunks[file] = children;\n if (this.needsParentSizes) this.attachFrontierParent(file, data, tree, computeOwnSizes(data));\n }\n if (file === 0) this.coverageSplatCount = Math.max(1, this.chunkCounts[0] as number);\n }\n\n /**\n * Computes each splat's `parent_size` for the frontier cut and packs it, with\n * a leaf-sign bit, onto `data.frontierParent` (uploaded into `covarianceB.w`).\n *\n * `own_size` is derived from the covariance the same way the material does -\n * `2·√(trace(Σ)/3)`, which with the merged-node-expanded scales already carries\n * the expansion - so a node's own size and its children's `parent_size` are the\n * identical measure, giving clean level transitions.\n *\n * The catch: this source streams chunks in **camera** order, not tree order, so\n * a child frequently decodes before its parent and `RadParentSizes` returns\n * `Infinity` (unknown). Making every such splat a root would defeat the cut -\n * a root always passes the \"parent too big\" half, so it can never be culled and\n * the drawn count runs away. Instead an unknown parent falls back to Spark's\n * ~1.75× merge ratio (`parent ≈ own · 1.75`), which brackets the splat to one\n * level per ray (a per-splat band). Chunk 0 - the always-resident coarse shell -\n * keeps the true-root sentinel so far rays always have coverage.\n */\n private attachFrontierParent(\n file: number,\n data: SplatData,\n tree: NonNullable<SplatData['radTree']>,\n ownSize: Float32Array,\n ): void {\n const count = data.count;\n const parentSize = this.parentSizes.resolve(file, tree.childCount, tree.childStart, ownSize);\n const frontierParent = new Float32Array(count);\n for (let i = 0; i < count; i++) {\n let p = parentSize[i] as number;\n if (!Number.isFinite(p)) {\n p = file === 0 ? FRONTIER_ROOT_SIZE : (ownSize[i] as number) * RAD_MERGE_RATIO;\n }\n p = Math.min(p, FRONTIER_ROOT_SIZE);\n frontierParent[i] = tree.childCount[i] === 0 ? -p : p; // leaves negative\n }\n (data as { frontierParent?: Float32Array }).frontierParent = frontierParent;\n }\n\n computeDesiredRuns(cameraLocal: THREE.Vector3, frustum: THREE.Frustum, _now: number): LodRun[] {\n const resident = this.buildResident(cameraLocal, frustum);\n const runs: LodRun[] = [];\n let residentSplats = 0;\n let farthest = 0;\n for (const c of resident) {\n runs.push(this.wholeChunkRun(c));\n residentSplats += this.chunkCounts[c] as number;\n farthest = Math.max(farthest, this.chunkDistance(c, cameraLocal, frustum));\n }\n // Pull the nearest undecoded refinements of the resident set toward the\n // camera. Bound the fetch to chunks that could actually become resident -\n // nearer than the farthest resident chunk once the set is full - so a fixed\n // view does not eventually download the whole scene. A fetch-intent run only\n // needs a valid `file`; it becomes the real whole-chunk run once decoded.\n const limit = residentSplats >= this.budget ? farthest : Infinity;\n for (const c of this.nearestUndecodedRefinements(resident, cameraLocal, frustum, limit)) {\n runs.push(this.wholeChunkRun(c));\n }\n return runs;\n }\n\n /** The coarse shell covers far/loading regions via the band cull, so there\n * are no separate substitute runs. */\n coarsestRunsFor(): LodRun[] {\n return [];\n }\n\n /** Chunk 0's coarse shell plus the nearest decoded chunks, up to the budget. */\n private buildResident(cameraLocal: THREE.Vector3, frustum: THREE.Frustum): Set<number> {\n const resident = new Set<number>();\n let residentSplats = 0;\n for (let c = 0; c < Math.min(COARSE_BASE, this.numChunks); c++) {\n if (this.decoded.has(c)) {\n resident.add(c);\n residentSplats += this.chunkCounts[c] as number;\n }\n }\n const nearest = [...this.decoded]\n .filter((c) => !resident.has(c))\n .sort(\n (a, b) =>\n this.chunkDistance(a, cameraLocal, frustum) - this.chunkDistance(b, cameraLocal, frustum),\n );\n for (const c of nearest) {\n if (residentSplats >= this.budget) break;\n resident.add(c);\n residentSplats += this.chunkCounts[c] as number;\n }\n return resident;\n }\n\n private nearestUndecodedRefinements(\n resident: ReadonlySet<number>,\n cameraLocal: THREE.Vector3,\n frustum: THREE.Frustum,\n maxDistance: number,\n ): number[] {\n const candidates = new Set<number>();\n for (const c of resident) {\n const children = this.childChunks[c];\n if (!children) continue;\n for (const cc of children) {\n if (!this.decoded.has(cc) && this.chunkDistance(cc, cameraLocal, frustum) <= maxDistance) {\n candidates.add(cc);\n }\n }\n }\n // Refine the coarsest *on-screen* region first: score by the parent node's\n // `size / distance` (its projected coarseness), out-of-frustum penalized. This\n // is Spark's descend-biggest-pixel-scale-first order - the near surfaces the\n // camera is looking at refine ahead of distant ones, instead of by raw\n // whole-chunk bounds distance (which a scene-spanning coarse chunk defeats).\n return [...candidates]\n .sort(\n (a, b) =>\n this.refineScore(b, cameraLocal, frustum) - this.refineScore(a, cameraLocal, frustum),\n )\n .slice(0, PREFETCH_AHEAD);\n }\n\n /** Refine priority for an undecoded chunk (higher = sooner): nearest, in-view\n * region first, scored against its *localized* estimated bounds (not the\n * scene-spanning whole-chunk bounds). Off-screen regions - not drawn - are\n * strongly penalized so the surfaces the camera faces refine first. */\n private refineScore(c: number, cameraLocal: THREE.Vector3, frustum: THREE.Frustum): number {\n const box = this.estBounds[c] ?? this.bounds[c];\n if (!box) return 0;\n const d = Math.max(box.distanceToPoint(cameraLocal), MIN_SCORE_DISTANCE);\n return (frustum.intersectsBox(box) ? 1 : 1 / FRUSTUM_PENALTY) / d;\n }\n\n private chunkDistance(c: number, cameraLocal: THREE.Vector3, frustum: THREE.Frustum): number {\n const box = this.bounds[c] ?? this.estBounds[c];\n if (!box) return Infinity;\n const d = box.distanceToPoint(cameraLocal);\n return frustum.intersectsBox(box) ? d : d * FRUSTUM_PENALTY;\n }\n\n /** One run covering a whole chunk; the material cull selects splats within. */\n private wholeChunkRun(chunk: number): LodRun {\n return {\n file: chunk,\n level: this.numChunks - chunk,\n offset: 0,\n count: this.chunkCounts[chunk] as number,\n leafStart: chunk,\n leafEnd: chunk + 1,\n };\n }\n}\n\n/** Per-splat world `own_size` (`2·√(trace(Σ)/3)`) - the same measure the material\n * derives on the GPU and the frontier `parent_size` uses. */\nfunction computeOwnSizes(data: SplatData): Float32Array {\n const count = data.count;\n const cov = data.covariances;\n const ownSize = new Float32Array(count);\n for (let i = 0; i < count; i++) {\n const trace = (cov[i * 6] as number) + (cov[i * 6 + 3] as number) + (cov[i * 6 + 5] as number);\n ownSize[i] = 2 * Math.sqrt(Math.max(trace, 0) / 3);\n }\n return ownSize;\n}\n\n/** World-space AABB of a decoded chunk's splat centers. */\nfunction boundsOf(data: SplatData): THREE.Box3 {\n const box = new THREE.Box3();\n const p = new THREE.Vector3();\n const pos = data.positions;\n for (let i = 0; i < data.count; i++) {\n p.set(pos[i * 3] ?? 0, pos[i * 3 + 1] ?? 0, pos[i * 3 + 2] ?? 0);\n box.expandByPoint(p);\n }\n return box.isEmpty()\n ? new THREE.Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1))\n : box;\n}\n","import * as THREE from 'three/webgpu';\nimport type { LodRun } from '../../lod-scheduler';\nimport type {\n LodSource,\n LodSourceOptions,\n StreamedChunkOptions,\n StreamedScene,\n} from '../../lod-source';\nimport type { SplatData, RadTreeData } from '../../splat-data';\nimport type { SplatDatasetSource } from '../../dataset-source';\nimport { toRequestInit, type SplatRequestOptions } from '../../loading';\nimport { liftBudgetToFinestLevel } from '../../splat-budget';\nimport { parseRadChunkStreaming, parseRadHeaderMeta, type RadMeta } from './parse-rad';\nimport { RadFoveatedSource } from './rad-foveated-source';\n\n/**\n * Streamed reader for a single-file Spark `.rad` (see `docs/formats/rad-notes.md`).\n *\n * A `.rad` is a precomputed LOD splat tree stored as a sequence of 64K-splat\n * chunks in coarsest→finest order: chunk 0 is a whole-scene overview built\n * from the largest merged nodes, and each later chunk refines detail. The\n * tree itself lives *inside* the chunks - every splat carries `child_count`\n * and `child_start` columns - so unlike LCC2 (whose octree is fully described\n * by its manifest) the structure is discovered as chunks decode.\n *\n * {@link RadLodSource} renders the *frontier* of a resident **prefix** of the\n * chunk sequence: a splat is drawn when it is a leaf, or when its children have\n * not been loaded yet (their merged approximation stands in). The prefix grows\n * - coarsest chunk first - until the drawn count reaches the budget, so a huge\n * capture shows as a coherent, budget-sized scene that sharpens as it loads.\n *\n * {@link RadLodSource} is exported for unit testing only; it is not part of\n * the public API and is reached in practice through {@link buildRadScene}.\n */\n\n/** Chunks fetched ahead of the resident frontier to keep the pipeline full. */\nconst PREFETCH_AHEAD = 6;\n\n/**\n * Builds a streamed scene from a single-file `.rad`. Fetches the header and\n * chunk table, then decodes chunk 0 (the coarsest whole-scene overview) to\n * establish scene bounds - the header carries none - and to seed the LOD\n * source with the root of the tree.\n *\n * `maxShBands` lets a caller **decline** the spherical harmonics the file\n * carries. Unlike SOG's opt-*in* `shBands` (whose manifest never declares\n * whether tiles have shN), a `.rad` names its own `maxSh`, so the question here\n * is only whether to keep it. It matters: every band costs 16 B/splat of GPU\n * pool plus as much again of CPU backing, and a texture fetch and coefficient\n * evaluation per splat per frame - exactly what the `smooth` performance profile\n * exists to decline on mobile.\n *\n * The choice is deliberately **all-or-nothing**: `0` drops SH, anything else\n * keeps every band the file has. A *partial* cap cannot be honoured, because the\n * `.rad` decoder emits the file's full band count and `SplatMesh.writePackedSh`\n * only copies packed words when the counts match exactly - a mismatch falls\n * through to neutral words, which would pay for the reduced bands' textures and\n * still render flat, view-independent color. Truncating the packed SH per splat\n * at decode would lift this restriction.\n */\nexport async function buildRadScene(\n source: SplatDatasetSource,\n options: LodSourceOptions,\n request?: SplatRequestOptions,\n maxShBands: 0 | 1 | 2 | 3 = 3,\n budgetLifts = true,\n): Promise<StreamedScene> {\n const { meta, chunksStart } = await fetchRadHeader(source.manifestUrl, request);\n const chunkSize = meta.chunkSize ?? meta.count;\n const numChunks = meta.chunks.length;\n if (numChunks === 0) throw new Error('RAD file has no chunks.');\n\n // A `--rad-chunked` header names each chunk's own `.radc` file; a single-file\n // `.rad` gives byte ranges instead. An external chunk is fetched whole (its\n // URL resolves against the manifest); an inline one is a range of the `.rad`,\n // labelled with a debugging fragment the worker strips before fetching.\n const chunkUrls = meta.chunks.map((range, index) => {\n if (range.filename === undefined) return `${source.manifestUrl}#rad-chunk-${index}`;\n const url = source.resolve(range.filename);\n if (url === null) {\n throw new Error(`RAD chunk ${index} references a missing file \"${range.filename}\".`);\n }\n return url;\n });\n let chunkOptions: StreamedChunkOptions[] = meta.chunks.map((range) => ({\n format: 'rad-chunk',\n rad:\n range.filename === undefined\n ? { start: chunksStart + range.offset, length: range.bytes }\n : {},\n }));\n\n // The drawn frontier converges to the capture's leaf count (every merged node\n // hidden by its children). Spark records that as `input_splat_count` in the\n // header comment; it is the natural pool ceiling and lets a moderate scene\n // lift its budget to full resolution (see `fromSource`). Fall back to the\n // whole-tree count when the comment is absent or unparseable.\n const leafCount = parseInputSplatCount(meta.comment) ?? meta.count;\n // Prefix reader or foveated page table?\n //\n // The prefix reader is camera-*independent*: it grows a resident prefix of the\n // chunk sequence, coarsest first, until the drawn count reaches the budget. Its\n // one advantage is that at `budget >= leafCount` it reaches full leaf\n // resolution everywhere - which Spark's foveated cut never does - so a capture\n // that fits is better taken whole.\n //\n // When the budget *cannot* hold the leaves that advantage inverts, and badly:\n // the prefix spreads a fixed budget uniformly over the whole capture, so a 1M\n // budget on a 5.9M-leaf scene draws every surface at ~1/6 resolution and\n // walking up to something never sharpens it. Foveating spends the same budget\n // where the camera is looking and coarsens the distance instead.\n //\n // Hence the second test, against the budget *after* the finest-level lift\n // `fromSource` will apply - the lift is what carries a desktop over the line,\n // and mobile is deliberately exempt from it, which is exactly the case that was\n // landing on the wrong path.\n //\n // `budgetLifts` mirrors the caller's own precondition for that lift: it only\n // happens when neither `budget` nor `maxBudget` was pinned. Assuming it always\n // applies would misjudge every host that sizes its meshes explicitly - a\n // marker given `maxBudget: pool / N` keeps that number, so it needs the\n // foveated path for the same reason a phone does.\n const effectiveBudget = budgetLifts\n ? liftBudgetToFinestLevel(options.budget, leafCount)\n : options.budget;\n const foveate = leafCount > FOVEATION_LEAF_THRESHOLD || leafCount > effectiveBudget;\n\n // Decode chunk 0 for bounds (the header has none) and to seed the tree root.\n // It must use the same splat order the streamed chunks will, or its\n // `child_start` links index the wrong locals: the prefix reader wants the\n // leaves-first reorder, the foveated/page-table reader wants file order.\n const chunk0Range = meta.chunks[0]!;\n const chunk0Buffer =\n chunk0Range.filename === undefined\n ? await fetchRange(\n source.manifestUrl,\n chunksStart + chunk0Range.offset,\n chunk0Range.bytes,\n request,\n )\n : await fetchWhole(chunkUrls[0]!, request);\n const chunk0Data = await parseRadChunkStreaming(chunk0Buffer, undefined, undefined, !foveate);\n // Later chunks must pack their SH against the same scene range chunk 0\n // established - the pool adopts one range for the whole scene. The codebook\n // (clustered SH) lives only in chunk 0 and rides along the same way.\n const shExtent = chunk0Data.shPacked?.range.max[0];\n if (chunk0Data.radShCodebook || shExtent !== undefined) {\n chunkOptions = chunkOptions.map((options) => ({\n ...options,\n rad: {\n ...options.rad!,\n ...(chunk0Data.radShCodebook ? { shCodebook: chunk0Data.radShCodebook } : {}),\n ...(shExtent !== undefined ? { shExtent } : {}),\n },\n }));\n }\n const bounds = boundsOf(chunk0Data);\n // Chunk 0 is a whole-scene overview. Copy a subsample of its centers before\n // pagetable mode transfers the buffers to the worker, so the demo can place\n // the camera on the terrain instead of the AABB min (underground outliers).\n const overviewPositions = subsampleOverviewPositions(chunk0Data.positions, chunk0Data.count);\n // All-or-nothing, for the reason in the doc comment above: a partial cap\n // would silently degrade to neutral SH rather than to fewer bands.\n const shBands = maxShBands === 0 ? 0 : (chunk0Data.shPacked?.bands ?? 0);\n\n const chunkCounts = meta.chunks.map((_range, index) =>\n Math.min(chunkSize, meta.count - index * chunkSize),\n );\n\n // Foveated: load a camera-directed set of chunks and let the page-table\n // frontier pick the LOD cut per splat (see `docs/formats/rad-notes.md` M14.6\n // and `RadFoveatedSource`).\n if (foveate) {\n const foveated = new RadFoveatedSource(chunkSize, chunkCounts, options);\n if (chunk0Data.radTree) foveated.onChunkDecoded(0, chunk0Data);\n return {\n source: foveated,\n chunkUrls,\n chunkKind: 'file',\n // File order (no leaves-first reorder): the band cull is order-agnostic\n // and this keeps `child_start` valid for future exact-parent-size work.\n chunkOptions: chunkOptions.map((option) => ({\n ...option,\n rad: { ...option.rad!, reorder: false },\n })),\n shBands,\n bounds,\n pinnedFiles: new Set([0]),\n // Pool holds the resident set (near + coarse), not the whole tree.\n maxResidentSplats: options.budget,\n // The real capture size, which `maxResidentSplats` cannot carry here (it\n // echoes the caller's budget). A host splitting one budget across several\n // streamed meshes needs it: a mesh cannot spend more than it contains.\n contentSplatCount: leafCount,\n minimumCoverageSplats: foveated.coverageSplatCount,\n foveation: RAD_FOVEATION_BAND,\n chunkSize,\n overviewPositions,\n // Hand chunk 0 straight to the page-table worker: it holds the tree roots,\n // so until it lands every traversal returns an empty frontier. Its buffers\n // are transferred there, so nothing may read `chunk0Data` after this.\n bootstrapChunk: { file: 0, data: chunk0Data },\n };\n }\n\n const source_ = new RadLodSource(meta.count, chunkSize, chunkCounts, options);\n if (chunk0Data.radTree) source_.onChunkDecoded(0, chunk0Data);\n\n return {\n source: source_,\n chunkUrls,\n chunkKind: 'file',\n chunkOptions,\n shBands,\n bounds,\n pinnedFiles: new Set([0]),\n maxResidentSplats: leafCount,\n contentSplatCount: leafCount,\n minimumCoverageSplats: source_.coverageSplatCount,\n overviewPositions,\n };\n}\n\n/** Leaf count above which a `.rad` foveates instead of loading whole; matches\n * the budget-lift ceiling (`FINEST_LEVEL_BUDGET_MAX`). */\nconst FOVEATION_LEAF_THRESHOLD = 6_000_000;\n\n/** Screen-radius band (px) for foveated `.rad`: only splats projecting to a\n * radius in `(min, max]` draw. The ~2.5× width spans one LOD level (Spark's\n * merge ratio is ~1.75), so one level per view ray lands in the band. */\nconst RAD_FOVEATION_BAND = { minScreenRadiusPx: 1.6, maxScreenRadiusPx: 4 } as const;\n\n/** Reads `input_splat_count` (the leaf count) from Spark's build-info comment. */\nfunction parseInputSplatCount(comment: string | undefined): number | undefined {\n if (!comment) return undefined;\n try {\n const info = JSON.parse(comment) as { input_splat_count?: unknown };\n const value = info.input_splat_count;\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Source-local splat-center subsample for camera framing. */\nconst MAX_OVERVIEW_SAMPLES = 8192;\n\nfunction subsampleOverviewPositions(positions: Float32Array, count: number): Float32Array {\n if (count <= 0) return new Float32Array(0);\n const stride = Math.max(1, Math.floor(count / MAX_OVERVIEW_SAMPLES));\n let n = 0;\n for (let i = 0; i < count; i += stride) n++;\n const out = new Float32Array(n * 3);\n let o = 0;\n for (let i = 0; i < count; i += stride) {\n const b = i * 3;\n out[o++] = positions[b] as number;\n out[o++] = positions[b + 1] as number;\n out[o++] = positions[b + 2] as number;\n }\n return out;\n}\n\n/** World-space AABB of a decoded chunk's splat centers. */\nfunction boundsOf(data: SplatData): THREE.Box3 {\n const box = new THREE.Box3();\n const p = new THREE.Vector3();\n const pos = data.positions;\n for (let i = 0; i < data.count; i++) {\n p.set(pos[i * 3] ?? 0, pos[i * 3 + 1] ?? 0, pos[i * 3 + 2] ?? 0);\n box.expandByPoint(p);\n }\n return box.isEmpty()\n ? new THREE.Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1))\n : box;\n}\n\n/**\n * Selects a budget-bounded prefix of a `.rad` chunk sequence and renders its\n * frontier - the splats whose children are not yet resident, plus all leaves.\n *\n * The tree is discovered incrementally: {@link onChunkDecoded} records each\n * chunk's `child_count`/`child_start` columns, and `discoveredDepth` tracks the\n * longest contiguous decoded prefix. `computeDesiredRuns` emits the frontier of\n * the largest prefix whose drawn count fits the budget (cached - it does not\n * depend on the camera), plus fetch-intent runs that pull the next chunks in.\n *\n * Refinement is **uniform** over the scene, not camera-distance foveated. Chunk-\n * granular spatial foveation is impossible for this format: the chunk graph is a\n * deeply entangled DAG - every non-root chunk has ≥2 parent chunks - so no\n * ancestor-closed chunk subset larger than `{0}` exists, and a coarse ancestor\n * that is not resident would double-draw over any refined descendant. True near-\n * camera refinement therefore needs Spark-style per-splat GPU culling (see\n * `docs/formats/rad-notes.md`, ROADMAP M14.6). Two levers help meanwhile: a budget large\n * enough to load a moderate scene's full leaf set (blobs vanish once every chunk\n * is resident), and the `maxSplatScreenRadius` blob cull for what stays coarse.\n *\n * {@link RadLodSource} is exported for unit testing only; it is not part of the\n * public API and is reached in practice through {@link buildRadScene}.\n */\nexport class RadLodSource implements LodSource {\n budget: number;\n lodBaseDistance: number;\n lodMultiplier: number;\n\n private readonly total: number;\n private readonly chunkSize: number;\n private readonly chunkCounts: readonly number[];\n private readonly numChunks: number;\n /**\n * Decoded per-splat tree columns, by chunk index.\n *\n * Grows monotonically and is deliberately never evicted. That costs ~10 B per\n * splat of every chunk ever decoded (`childCount` u16 + `childStart` u32 +\n * `size` f32) and is invisible to the chunk cache's byte accounting, so it\n * reads like a leak - but it is not reclaimable. {@link discoveredDepth} is\n * the longest *contiguous* decoded prefix and every frontier walk at or below\n * it reads these columns, so dropping an entry would either corrupt the\n * traversal or force `discoveredDepth` back and visibly coarsen the scene.\n *\n * Bounded in practice by the mode that uses it: `.rad` defaults to\n * `foveationMode: 'pagetable'`, where the frontier worker owns the traversal\n * and this source is never constructed. Only the `'band'` and `'frontier'`\n * A/B modes pay it. Reducing it means changing the tree encoding, not adding\n * an eviction pass here.\n */\n private readonly decoded = new Map<number, RadTreeData>();\n /** Longest contiguous decoded prefix `{0 … discoveredDepth − 1}`. */\n private discoveredDepth = 0;\n\n /** Chunk-0-only frontier: the always-available coarse substitute coverage. */\n private coarseRuns: LodRun[] = [];\n /** Splats the coarse (chunk-0) coverage draws. */\n coverageSplatCount = 1;\n\n /** Cache of the last emitted frontier, keyed by the inputs that shape it. */\n private cachedRuns: LodRun[] = [];\n private cachedFrontierCount = 0;\n /** Frontier splat count by prefix depth; see {@link countAtDepth}. */\n private readonly depthCounts = new Map<number, number>();\n /** Budget range over which {@link cachedRuns} stays the correct frontier. */\n private cachedBudgetLo = Number.POSITIVE_INFINITY;\n private cachedBudgetHi = Number.NEGATIVE_INFINITY;\n private cachedForDiscovered = -1;\n\n constructor(\n total: number,\n chunkSize: number,\n chunkCounts: readonly number[],\n options: LodSourceOptions,\n ) {\n this.total = total;\n this.chunkSize = chunkSize;\n this.chunkCounts = chunkCounts;\n this.numChunks = chunkCounts.length;\n this.budget = options.budget;\n this.lodBaseDistance = options.lodBaseDistance;\n this.lodMultiplier = options.lodMultiplier;\n }\n\n onChunkDecoded(file: number, data: SplatData): void {\n if (!data.radTree || this.decoded.has(file)) return;\n this.decoded.set(file, data.radTree);\n if (file === 0) {\n // Chunk 0 alone is the coarsest whole-scene coverage (its splats' children\n // all live in later chunks), so its frontier at \"only chunk 0 resident\" is\n // the pinned substitute every deferred deeper region falls back to.\n this.coarseRuns = this.frontierRuns(1, this.chunkCounts[0] as number);\n this.coverageSplatCount = Math.max(1, countSplats(this.coarseRuns));\n }\n while (this.decoded.has(this.discoveredDepth)) this.discoveredDepth++;\n }\n\n computeDesiredRuns(_cameraLocal: THREE.Vector3, _frustum: THREE.Frustum, _now: number): LodRun[] {\n this.ensureFrontier();\n // Keep pulling chunks in until the drawn count reaches the budget. The\n // frontier can never exceed the capture's leaf count, so when the budget is\n // lifted to that count (a moderate scene) this loads every chunk and the\n // scene reaches full resolution - no coarse blobs left anywhere.\n if (this.cachedFrontierCount >= this.budget) return this.cachedRuns;\n\n // Pull the next window of undecoded chunks in. Fetch-intent runs\n // reference undecoded chunks so the mesh fetches them; they are always\n // deferred (their chunk is never cached at the moment it would append,\n // because decoding advances the frontier in the same tick), so their\n // geometry is immaterial - only their `file` matters.\n const runs = [...this.cachedRuns];\n const until = Math.min(this.discoveredDepth + PREFETCH_AHEAD, this.numChunks);\n for (let c = this.discoveredDepth; c < until; c++) {\n if (!this.decoded.has(c)) runs.push(this.fetchIntentRun(c));\n }\n return runs;\n }\n\n coarsestRunsFor(from: number, to: number): LodRun[] {\n const runs: LodRun[] = [];\n for (const run of this.coarseRuns) {\n if (run.leafStart < to && run.leafEnd > from) runs.push(run);\n }\n return runs;\n }\n\n /**\n * Recomputes the cached frontier when the budget or discovery has moved.\n *\n * The cache is keyed on the *range* of budgets that select the same prefix\n * depth, not on the budget itself. A camera-weighted governor rewrites the\n * budget every frame - it wanders by a few percent as the camera drifts - and\n * an exact-equality key therefore missed on roughly a third of all calls while\n * choosing the very same depth. Each of those misses re-walks `frontierRuns`\n * over the whole decoded prefix (millions of splats, several times over if it\n * steps down levels): measured at 33ms mean and 1.1s worst, it was 92% of all\n * frame CPU on a marker-heavy scene. Frontier count rises monotonically with\n * depth, so the selected depth is stable for any budget in\n * `[thisDepthCount, nextDepthCount)` and the walk can be skipped outright.\n */\n private ensureFrontier(): void {\n if (\n this.cachedForDiscovered === this.discoveredDepth &&\n this.budget >= this.cachedBudgetLo &&\n this.budget < this.cachedBudgetHi\n ) {\n return;\n }\n this.cachedForDiscovered = this.discoveredDepth;\n\n // Largest prefix whose frontier fits the budget. Frontier count rises\n // monotonically with depth, so walk down from the deepest decoded prefix.\n let depth = this.discoveredDepth;\n let count = depth > 0 ? this.countAtDepth(depth) : 0;\n // The shallowest depth already rejected as too large - the budget at which\n // this result would stop being the right one. Nothing is deeper than the\n // discovered prefix, so without a rejection the band has no upper bound.\n let rejectedCount = Number.POSITIVE_INFINITY;\n while (depth > 1 && count > this.budget) {\n rejectedCount = count;\n depth--;\n count = this.countAtDepth(depth);\n }\n // Only the depth that wins builds runs. The search itself reads memoized\n // counts, so a budget swing that skips several levels costs one walk rather\n // than one per level tried.\n const runs =\n depth > 0 ? this.frontierRuns(depth, Math.min(depth * this.chunkSize, this.total)) : [];\n this.cachedRuns = runs;\n this.cachedFrontierCount = count;\n // Depth 1 is the floor: it is returned however small the budget gets, so\n // that case stays valid all the way down to zero.\n this.cachedBudgetLo = depth > 1 ? count : 0;\n this.cachedBudgetHi = rejectedCount;\n }\n\n /**\n * Splats the frontier draws at `depth`, memoized.\n *\n * `discoveredDepth` is the longest *contiguous* decoded prefix, so for any\n * `depth <= discoveredDepth` every chunk the walk reads is already decoded and\n * the answer can never change afterwards - chunks are only ever added to\n * `decoded`. That makes this permanently cacheable, which is what lets the\n * depth search above run without rebuilding runs at each level it rejects.\n */\n private countAtDepth(depth: number): number {\n const memo = this.depthCounts.get(depth);\n if (memo !== undefined) return memo;\n const residentCount = Math.min(depth * this.chunkSize, this.total);\n let total = 0;\n for (let c = 0; c < depth; c++) {\n const tree = this.decoded.get(c);\n if (!tree) continue;\n const count = this.chunkCounts[c] as number;\n for (let i = 0; i < count; i++) {\n if (tree.childCount[i] === 0 || (tree.childStart[i] as number) >= residentCount) total++;\n }\n }\n this.depthCounts.set(depth, total);\n return total;\n }\n\n /**\n * The rendered runs for a resident prefix of `depth` chunks: within each\n * chunk, the contiguous ranges of splats that are leaves or whose children\n * fall beyond `residentCount` (the prefix's total splat count). The chunk's\n * splats are child-start-descending (leaves first), so each range coalesces\n * into one run.\n */\n private frontierRuns(depth: number, residentCount: number): LodRun[] {\n const runs: LodRun[] = [];\n for (let c = 0; c < depth; c++) {\n const tree = this.decoded.get(c);\n if (!tree) continue; // only reachable if a prefix chunk was evicted from discovery\n const count = this.chunkCounts[c] as number;\n const base = c * this.chunkSize;\n const level = this.numChunks - c;\n let start = -1;\n for (let i = 0; i < count; i++) {\n const draws = tree.childCount[i] === 0 || (tree.childStart[i] as number) >= residentCount;\n if (draws) {\n if (start < 0) start = i;\n } else if (start >= 0) {\n runs.push(makeRun(c, level, start, i, base));\n start = -1;\n }\n }\n if (start >= 0) runs.push(makeRun(c, level, start, count, base));\n }\n return runs;\n }\n\n private fetchIntentRun(chunk: number): LodRun {\n const count = this.chunkCounts[chunk] as number;\n return makeRun(chunk, this.numChunks - chunk, 0, count, chunk * this.chunkSize);\n }\n}\n\n/** A run for chunk `file`'s local splats `[lo, hi)`, keyed by global index. */\nfunction makeRun(file: number, level: number, lo: number, hi: number, base: number): LodRun {\n return { file, level, offset: lo, count: hi - lo, leafStart: base + lo, leafEnd: base + hi };\n}\n\nfunction countSplats(runs: readonly LodRun[]): number {\n let total = 0;\n for (const run of runs) total += run.count;\n return total;\n}\n\n/** Fetches and parses a `.rad`'s `RAD0` header and chunk table. */\nasync function fetchRadHeader(\n url: string,\n request?: SplatRequestOptions,\n): Promise<{ meta: RadMeta; chunksStart: number }> {\n // One speculative read covers the header for any realistic chunk count; a\n // pathologically long table (very large scene) triggers one exact refetch.\n const PROBE = 262_144;\n let buffer = await fetchRange(url, 0, PROBE, request, true);\n const view = new DataView(buffer);\n if (view.byteLength >= 8) {\n const length = view.getUint32(4, true);\n if (8 + length > buffer.byteLength) {\n buffer = await fetchRange(url, 0, 8 + length, request);\n }\n }\n return parseRadHeaderMeta(buffer);\n}\n\n/**\n * Fetches `[start, start + length)` of a URL, requiring a real 206 (like the\n * LCC path). `allowShort` permits a server to return fewer bytes than asked -\n * used for the header probe, where the file may be shorter than the probe.\n */\nasync function fetchRange(\n url: string,\n start: number,\n length: number,\n request?: SplatRequestOptions,\n allowShort = false,\n): Promise<ArrayBuffer> {\n const init = toRequestInit(request);\n const response = await fetch(url, {\n ...init,\n headers: { ...(request?.headers ?? {}), Range: `bytes=${start}-${start + length - 1}` },\n });\n if (!response.ok) {\n throw new Error(`Failed to load ${url}: HTTP ${response.status}`);\n }\n // The header probe (`allowShort`) tolerates a `200` - a `--rad-chunked` set's\n // `.rad` header is small enough to arrive whole from a server that ignores\n // Range. Chunk-*data* reads stay strict: a single-file `.rad` that answers\n // `200` would download the whole capture, so it is rejected.\n if (response.status !== 206 && !(allowShort && response.status === 200)) {\n throw new Error(\n `${url} ignored a Range request (HTTP ${response.status}); single-file .rad streaming ` +\n 'needs a server that answers 206 Partial Content.',\n );\n }\n const buffer = await response.arrayBuffer();\n if (!allowShort && buffer.byteLength !== length) {\n throw new Error(`${url} returned ${buffer.byteLength} bytes for a ${length}-byte range.`);\n }\n return buffer;\n}\n\n/** Fetches a whole file (an external `.radc` chunk, or a small chunked-set\n * `.rad` header served without range support). */\nasync function fetchWhole(url: string, request?: SplatRequestOptions): Promise<ArrayBuffer> {\n const response = await fetch(url, toRequestInit(request));\n if (!response.ok) throw new Error(`Failed to load ${url}: HTTP ${response.status}`);\n return response.arrayBuffer();\n}\n"],"names":["shEncodingBytes","prop","requireRange","requireMax","assertColumnHoldsCount","data","dims","count","bytesPerValue","decodeFloatColumn","decodeF32","decodeF16","decodeF32LeBytes","decodeF16LeBytes","min","max","decodeR8","decodeR8Delta","decodeS8","decodeS8Delta","decodeLn0R8","decodeLnF16","decodeOrientation","decodeQuatOct88R8","xyz","out","i","x","y","z","decodeChildCount","decodeU16","decodeChildStart","decodeU32","scratch","bitsToFloat32","bits","view","index","d","halfToFloat","stride","range","last","value","byte","signed","step","u","v","r","t","length","halfTheta","s","w","RAD_MAGIC","RAD_CHUNK_MAGIC","MAX_RAD_SPLAT_COUNT","assertRadCountInteger","what","assertSaneRadCount","parseRad","buffer","bytes","mergeChunks","decodeChunk","meta","chunksStart","parseRadHeaderMeta","chunks","shCodebook","shExtent","start","chunk","_a","magic","decodeUtf8","chunkRanges","roundup8","parseRadChunkStreaming","reorder","chunkToSplatData","positions","colors","covariances","writeSplat","childCount","childStart","order","frontierOrder","identityOrder","reorderedSh","reorderPackedSh","outCount","outStart","outSize","j","src","radNodeSize","alpha","scale","avg","key","a","b","chunkStart","withTree","knownShCodebook","knownShExtent","metaEnd","chunkMeta","payloadBytes","payloadStart","lodTree","properties","byName","readProp","name","raw","decompressColumn","wantTree","propertyNames","centerRaw","alphaRaw","rgbRaw","scaleRaw","orientationRaw","childCountRaw","childStartRaw","sh1Raw","sh2Raw","sh3Raw","shLabelRaw","sh1CodeRaw","sh2CodeRaw","sh3CodeRaw","decodeRadSh","total","countLeaves","sourceSh","packed","shCoefficientCount","neutralShWord","words","sh","dst","input","direct","code","directBands","bands","codeBands","coefficients","codebook","extent","combineShColumns","metadataShExtent","codeCount","shColumnCount","labels","decodeShLabels","label","packShCoefficients","suffix","band","bandExtent","raws","totalCoefficients","result","coefficientOffset","values","base","k","dimensions","clampByte","writeCovariance","leaves","size","offset","format","copy","stream","RadParentSizes","chunkSize","__publicField","chunkIndex","parentSize","ranges","from","to","local","cc","parentNodeSize","g","end","childChunk","chunkEnd","rangeEnd","FRONTIER_ROOT_SIZE","RAD_MERGE_RATIO","PREFETCH_AHEAD","FRUSTUM_PENALTY","COARSE_BASE","MIN_SCORE_DISTANCE","RadFoveatedSource","chunkCounts","options","THREE","file","boundsOf","tree","children","pos","est","computeOwnSizes","ownSize","frontierParent","p","cameraLocal","frustum","_now","resident","runs","residentSplats","farthest","c","limit","nearest","maxDistance","candidates","box","cov","trace","buildRadScene","source","request","maxShBands","budgetLifts","fetchRadHeader","chunkUrls","url","chunkOptions","leafCount","parseInputSplatCount","effectiveBudget","liftBudgetToFinestLevel","foveate","FOVEATION_LEAF_THRESHOLD","chunk0Range","chunk0Buffer","fetchRange","fetchWhole","chunk0Data","bounds","overviewPositions","subsampleOverviewPositions","shBands","_b","_range","foveated","option","RAD_FOVEATION_BAND","source_","RadLodSource","comment","MAX_OVERVIEW_SAMPLES","n","countSplats","_cameraLocal","_frustum","until","run","depth","rejectedCount","memo","residentCount","level","makeRun","lo","hi","allowShort","init","toRequestInit","response"],"mappings":";;;;;;;;;AAIA,SAASA,GAAgBC,GAAgC;AACvD,SAAIA,EAAK,aAAa,QAAc,IAChCA,EAAK,aAAa,QAAc,IAC7B;AACT;AAEA,SAASC,EAAaD,GAAsD;AAC1E,MAAIA,EAAK,QAAQ,UAAaA,EAAK,QAAQ;AACzC,UAAM,IAAI,MAAM,QAAQA,EAAK,QAAQ,MAAMA,EAAK,QAAQ,iCAAiC;AAE3F,SAAO,EAAE,KAAKA,EAAK,KAAK,KAAKA,EAAK,IAAA;AACpC;AAEA,SAASE,EAAWF,GAAgC;AAClD,MAAIA,EAAK,QAAQ;AACf,UAAM,IAAI,MAAM,QAAQA,EAAK,QAAQ,MAAMA,EAAK,QAAQ,6BAA6B;AAEvF,SAAOA,EAAK;AACd;AAOO,SAASG,EACdC,GACAJ,GACAK,GACAC,GACAC,GACM;AACN,MAAIH,EAAK,aAAaE,IAAQD,IAAOE;AACnC,UAAM,IAAI;AAAA,MACR,QAAQP,EAAK,QAAQ,kBAAkBI,EAAK,UAAU,0BACjDE,IAAQD,IAAOE,CAAa,iBAAiBD,CAAK;AAAA,IAAA;AAG7D;AAEO,SAASE,EACdJ,GACAJ,GACAK,GACAC,GACc;AAEd,UADAH,EAAuBC,GAAMJ,GAAMK,GAAMC,GAAOP,GAAgBC,CAAI,CAAC,GAC7DA,EAAK,UAAA;AAAA,IACX,KAAK;AACH,aAAOS,EAAUL,GAAMC,GAAMC,CAAK;AAAA,IACpC,KAAK;AACH,aAAOI,EAAUN,GAAMC,GAAMC,CAAK;AAAA,IACpC,KAAK;AACH,aAAOK,GAAiBP,GAAMC,GAAMC,CAAK;AAAA,IAC3C,KAAK;AACH,aAAOM,GAAiBR,GAAMC,GAAMC,CAAK;AAAA,IAC3C,KAAK,MAAM;AACT,YAAM,EAAE,KAAAO,GAAK,KAAAC,MAAQb,EAAaD,CAAI;AACtC,aAAOe,GAASX,GAAMC,GAAMC,GAAOO,GAAKC,CAAG;AAAA,IAC7C;AAAA,IACA,KAAK,YAAY;AACf,YAAM,EAAE,KAAAD,GAAK,KAAAC,MAAQb,EAAaD,CAAI;AACtC,aAAOgB,GAAcZ,GAAMC,GAAMC,GAAOO,GAAKC,CAAG;AAAA,IAClD;AAAA,IACA,KAAK;AACH,aAAOG,GAASb,GAAMC,GAAMC,GAAOJ,EAAWF,CAAI,CAAC;AAAA,IACrD,KAAK;AACH,aAAOkB,GAAcd,GAAMC,GAAMC,GAAOJ,EAAWF,CAAI,CAAC;AAAA,IAC1D,KAAK,UAAU;AACb,YAAM,EAAE,KAAAa,GAAK,KAAAC,MAAQb,EAAaD,CAAI;AACtC,aAAOmB,GAAYf,GAAMC,GAAMC,GAAOO,GAAKC,CAAG;AAAA,IAChD;AAAA,IACA,KAAK;AACH,aAAOM,GAAYhB,GAAMC,GAAMC,CAAK;AAAA,IACtC;AACE,YAAM,IAAI,MAAM,oBAAoBN,EAAK,QAAQ,eAAeA,EAAK,QAAQ,IAAI;AAAA,EAAA;AAEvF;AAEO,SAASqB,GACdjB,GACAJ,GACAM,GACc;AACd,MAAIN,EAAK,aAAa;AACpB,WAAAG,EAAuBC,GAAMJ,GAAM,GAAGM,GAAO,CAAC,GACvCgB,GAAkBlB,GAAME,CAAK;AAEtC,GAAIN,EAAK,aAAa,SAASA,EAAK,aAAa,UAC/CG,EAAuBC,GAAMJ,GAAM,GAAGM,GAAON,EAAK,aAAa,QAAQ,IAAI,CAAC;AAE9E,QAAMuB,IACJvB,EAAK,aAAa,QACdS,EAAUL,GAAM,GAAGE,CAAK,IACxBN,EAAK,aAAa,QAChBU,EAAUN,GAAM,GAAGE,CAAK,IACxB;AACR,MAAI,CAACiB,EAAK,OAAM,IAAI,MAAM,yCAAyCvB,EAAK,QAAQ,IAAI;AACpF,QAAMwB,IAAM,IAAI,aAAalB,IAAQ,CAAC;AACtC,WAASmB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,UAAMC,IAAIH,EAAIE,IAAI,IAAI,CAAC,GACjBE,IAAIJ,EAAIE,IAAI,IAAI,CAAC,GACjBG,IAAIL,EAAIE,IAAI,IAAI,CAAC;AACvB,IAAAD,EAAIC,IAAI,IAAI,CAAC,IAAIC,GACjBF,EAAIC,IAAI,IAAI,CAAC,IAAIE,GACjBH,EAAIC,IAAI,IAAI,CAAC,IAAIG,GACjBJ,EAAIC,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,KAAK,IAAI,GAAG,IAAIC,IAAIA,IAAIC,IAAIA,IAAIC,IAAIA,CAAC,CAAC;AAAA,EACnE;AACA,SAAOJ;AACT;AAEO,SAASK,GACdzB,GACAJ,GACAM,GACa;AACb,MAAIN,EAAK,aAAa;AACpB,UAAM,IAAI,MAAM,yCAAyCA,EAAK,QAAQ,IAAI;AAE5E,SAAAG,EAAuBC,GAAMJ,GAAM,GAAGM,GAAO,CAAC,GACvCwB,GAAU1B,GAAME,CAAK;AAC9B;AAEO,SAASyB,GACd3B,GACAJ,GACAM,GACa;AACb,MAAIN,EAAK,aAAa;AACpB,UAAM,IAAI,MAAM,yCAAyCA,EAAK,QAAQ,IAAI;AAE5E,SAAAG,EAAuBC,GAAMJ,GAAM,GAAGM,GAAO,CAAC,GACvC0B,GAAU5B,GAAME,CAAK;AAC9B;AAEA,MAAM2B,IAAU,IAAI,SAAS,IAAI,YAAY,CAAC,CAAC;AAE/C,SAASC,GAAcC,GAAsB;AAC3C,SAAAF,EAAQ,UAAU,GAAGE,GAAM,EAAI,GACxBF,EAAQ,WAAW,GAAG,EAAI;AACnC;AAGA,SAASxB,EAAUL,GAAkBC,GAAcC,GAA6B;AAC9E,QAAM8B,IAAO,IAAI,SAAShC,EAAK,QAAQA,EAAK,YAAYA,EAAK,UAAU,GACjEoB,IAAM,IAAI,aAAalB,IAAQD,CAAI;AACzC,WAASoB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,QAAIY,IAAQZ,IAAI;AAChB,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC;AACxB,MAAAd,EAAIC,IAAIpB,IAAOiC,CAAC,IAAIF,EAAK,WAAWC,GAAO,EAAI,GAC/CA,KAAS/B,IAAQ;AAAA,EAErB;AACA,SAAOkB;AACT;AAEA,SAASd,EAAUN,GAAkBC,GAAcC,GAA6B;AAC9E,QAAM8B,IAAO,IAAI,SAAShC,EAAK,QAAQA,EAAK,YAAYA,EAAK,UAAU,GACjEoB,IAAM,IAAI,aAAalB,IAAQD,CAAI;AACzC,WAASoB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,QAAIY,IAAQZ,IAAI;AAChB,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC;AACxB,MAAAd,EAAIC,IAAIpB,IAAOiC,CAAC,IAAIC,EAAYH,EAAK,UAAUC,GAAO,EAAI,CAAC,GAC3DA,KAAS/B,IAAQ;AAAA,EAErB;AACA,SAAOkB;AACT;AAIA,SAASb,GAAiBP,GAAkBC,GAAcC,GAA6B;AACrF,QAAMkC,IAASlC,IAAQD,GACjBmB,IAAM,IAAI,aAAalB,IAAQD,CAAI;AACzC,WAASoB,IAAI,GAAGA,IAAInB,GAAOmB;AACzB,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC,KAAK;AAC7B,YAAMD,IAAQ/B,IAAQgC,IAAIb,GACpBU,KACF/B,EAAKiC,CAAK,IACRjC,EAAKiC,IAAQG,CAAM,KAAgB,IACnCpC,EAAKiC,IAAQG,IAAS,CAAC,KAAgB,KACvCpC,EAAKiC,IAAQG,IAAS,CAAC,KAAgB,QAC3C;AACF,MAAAhB,EAAIC,IAAIpB,IAAOiC,CAAC,IAAIJ,GAAcC,CAAI;AAAA,IACxC;AAEF,SAAOX;AACT;AAEA,SAASZ,GAAiBR,GAAkBC,GAAcC,GAA6B;AACrF,QAAMkC,IAASlC,IAAQD,GACjBmB,IAAM,IAAI,aAAalB,IAAQD,CAAI;AACzC,WAASoB,IAAI,GAAGA,IAAInB,GAAOmB;AACzB,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC,KAAK;AAC7B,YAAMD,IAAQ/B,IAAQgC,IAAIb,GACpBU,IAAQ/B,EAAKiC,CAAK,IAAiBjC,EAAKiC,IAAQG,CAAM,KAAgB;AAC5E,MAAAhB,EAAIC,IAAIpB,IAAOiC,CAAC,IAAIC,EAAYJ,CAAI;AAAA,IACtC;AAEF,SAAOX;AACT;AAGA,SAAST,GACPX,GACAC,GACAC,GACAO,GACAC,GACc;AACd,QAAMU,IAAM,IAAI,aAAalB,IAAQD,CAAI,GACnCoC,IAAQ3B,IAAMD;AACpB,WAASY,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,QAAIY,IAAQZ;AACZ,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC;AACxB,MAAAd,EAAIC,IAAIpB,IAAOiC,CAAC,IAAMlC,EAAKiC,CAAK,IAAe,MAAOI,IAAQ5B,GAC9DwB,KAAS/B;AAAA,EAEb;AACA,SAAOkB;AACT;AAGA,SAASR,GACPZ,GACAC,GACAC,GACAO,GACAC,GACc;AACd,QAAMU,IAAM,IAAI,aAAalB,IAAQD,CAAI,GACnCoC,IAAQ3B,IAAMD,GACd6B,IAAO,IAAI,WAAWrC,CAAI;AAChC,WAASoB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,QAAIY,IAAQZ;AACZ,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC,KAAK;AAC7B,YAAMK,IAAUD,EAAKJ,CAAC,IAAgBlC,EAAKiC,CAAK,IAAgB;AAChE,MAAAK,EAAKJ,CAAC,IAAIK,GACVnB,EAAIC,IAAIpB,IAAOiC,CAAC,IAAKK,IAAQ,MAAOF,IAAQ5B,GAC5CwB,KAAS/B;AAAA,IACX;AAAA,EACF;AACA,SAAOkB;AACT;AAGA,SAASP,GAASb,GAAkBC,GAAcC,GAAeQ,GAA2B;AAC1F,QAAMU,IAAM,IAAI,aAAalB,IAAQD,CAAI;AACzC,WAASoB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,QAAIY,IAAQZ;AACZ,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC,KAAK;AAC7B,YAAMM,IAAOxC,EAAKiC,CAAK,GACjBQ,IAASD,KAAQ,MAAMA,IAAO,MAAMA;AAC1C,MAAApB,EAAIC,IAAIpB,IAAOiC,CAAC,IAAKO,IAAS,MAAO/B,GACrCuB,KAAS/B;AAAA,IACX;AAAA,EACF;AACA,SAAOkB;AACT;AAEA,SAASN,GAAcd,GAAkBC,GAAcC,GAAeQ,GAA2B;AAC/F,QAAMU,IAAM,IAAI,aAAalB,IAAQD,CAAI,GACnCqC,IAAO,IAAI,WAAWrC,CAAI;AAChC,WAAS,IAAI,GAAG,IAAIC,GAAO,KAAK;AAC9B,QAAI+B,IAAQ;AACZ,aAASC,IAAI,GAAGA,IAAIjC,GAAMiC,KAAK;AAC7B,YAAMK,IAAUD,EAAKJ,CAAC,IAAgBlC,EAAKiC,CAAK,IAAgB;AAChE,MAAAK,EAAKJ,CAAC,IAAIK;AACV,YAAME,IAASF,KAAS,MAAMA,IAAQ,MAAMA;AAC5C,MAAAnB,EAAI,IAAInB,IAAOiC,CAAC,IAAKO,IAAS,MAAO/B,GACrCuB,KAAS/B;AAAA,IACX;AAAA,EACF;AACA,SAAOkB;AACT;AAIA,SAASL,GACPf,GACAC,GACAC,GACAO,GACAC,GACc;AACd,QAAMU,IAAM,IAAI,aAAalB,IAAQD,CAAI,GACnCyC,KAAQhC,IAAMD,KAAO;AAC3B,WAASY,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,QAAIY,IAAQZ;AACZ,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC,KAAK;AAC7B,YAAMM,IAAOxC,EAAKiC,CAAK;AACvB,MAAAb,EAAIC,IAAIpB,IAAOiC,CAAC,IAAIM,MAAS,IAAI,IAAI,KAAK,IAAI/B,KAAO+B,IAAO,KAAKE,CAAI,GACrET,KAAS/B;AAAA,IACX;AAAA,EACF;AACA,SAAOkB;AACT;AAEA,SAASJ,GAAYhB,GAAkBC,GAAcC,GAA6B;AAChF,QAAM8B,IAAO,IAAI,SAAShC,EAAK,QAAQA,EAAK,YAAYA,EAAK,UAAU,GACjEoB,IAAM,IAAI,aAAalB,IAAQD,CAAI;AACzC,WAASoB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,QAAIY,IAAQZ,IAAI;AAChB,aAASa,IAAI,GAAGA,IAAIjC,GAAMiC;AACxB,MAAAd,EAAIC,IAAIpB,IAAOiC,CAAC,IAAI,KAAK,IAAIC,EAAYH,EAAK,UAAUC,GAAO,EAAI,CAAC,CAAC,GACrEA,KAAS/B,IAAQ;AAAA,EAErB;AACA,SAAOkB;AACT;AAIA,SAASF,GAAkBlB,GAAkBE,GAA6B;AACxE,QAAMkB,IAAM,IAAI,aAAalB,IAAQ,CAAC;AACtC,WAASmB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,UAAMY,IAAQZ,IAAI,GACZsB,IAAI3C,EAAKiC,CAAK,GACdW,IAAI5C,EAAKiC,IAAQ,CAAC,GAClBY,IAAI7C,EAAKiC,IAAQ,CAAC;AACxB,QAAIX,IAAKqB,IAAI,MAAO,IAAI,GACpBpB,IAAKqB,IAAI,MAAO,IAAI;AACxB,UAAMpB,IAAI,IAAI,KAAK,IAAIF,CAAC,IAAI,KAAK,IAAIC,CAAC,GAChCuB,IAAI,KAAK,IAAI,CAACtB,GAAG,CAAC;AACxB,IAAAF,IAAIA,KAAK,IAAIA,IAAIwB,IAAIxB,IAAIwB,GACzBvB,IAAIA,KAAK,IAAIA,IAAIuB,IAAIvB,IAAIuB;AACzB,UAAMC,IAAS,KAAK,MAAMzB,GAAGC,GAAGC,CAAC,KAAK,GAChCwB,IAAaH,IAAI,MAAO,MAAM,KAAK,IACnCI,IAAI,KAAK,IAAID,CAAS,GACtBE,IAAI,KAAK,IAAIF,CAAS;AAC5B,IAAA5B,EAAIC,IAAI,IAAI,CAAC,IAAKC,IAAIyB,IAAUE,GAChC7B,EAAIC,IAAI,IAAI,CAAC,IAAKE,IAAIwB,IAAUE,GAChC7B,EAAIC,IAAI,IAAI,CAAC,IAAKG,IAAIuB,IAAUE,GAChC7B,EAAIC,IAAI,IAAI,CAAC,IAAI6B;AAAA,EACnB;AACA,SAAO9B;AACT;AAGO,SAASM,GAAU1B,GAAkBE,GAA4B;AACtE,QAAM8B,IAAO,IAAI,SAAShC,EAAK,QAAQA,EAAK,YAAYA,EAAK,UAAU,GACjEoB,IAAM,IAAI,YAAYlB,CAAK;AACjC,WAASmB,IAAI,GAAGA,IAAInB,GAAOmB,IAAK,CAAAD,EAAIC,CAAC,IAAIW,EAAK,UAAUX,IAAI,GAAG,EAAI;AACnE,SAAOD;AACT;AAGO,SAASQ,GAAU5B,GAAkBE,GAA4B;AACtE,QAAM8B,IAAO,IAAI,SAAShC,EAAK,QAAQA,EAAK,YAAYA,EAAK,UAAU,GACjEoB,IAAM,IAAI,YAAYlB,CAAK;AACjC,WAASmB,IAAI,GAAGA,IAAInB,GAAOmB,IAAK,CAAAD,EAAIC,CAAC,IAAIW,EAAK,UAAUX,IAAI,GAAG,EAAI;AACnE,SAAOD;AACT;AChUO,MAAM+B,KAAY,WACZC,KAAkB,YAazBC,IAAsB,KAAK;AAGjC,SAASC,GAAsBpD,GAAeqD,GAAoB;AAChE,MAAI,CAAC,OAAO,cAAcrD,CAAK,KAAKA,IAAQ;AAC1C,UAAM,IAAI,MAAM,eAAeqD,CAAI,iBAAiBrD,CAAK,GAAG;AAEhE;AAGA,SAASsD,GAAmBtD,GAAeqD,GAAoB;AAE7D,MADAD,GAAsBpD,GAAOqD,CAAI,GAC7BrD,IAAQmD;AACV,UAAM,IAAI;AAAA,MACR,OAAOE,CAAI,aAAarD,CAAK,2CAA2CmD,CAAmB;AAAA,IAAA;AAGjG;AA4EA,eAAsBI,GAASC,GAAyC;;AACtE,QAAMC,IAAQ,IAAI,WAAWD,CAAM;AACnC,MAAIC,EAAM,aAAa,EAAG,OAAM,IAAI,MAAM,qBAAqB;AAI/D,MAHa,IAAI,SAASA,EAAM,QAAQA,EAAM,YAAYA,EAAM,UAAU,EACvD,UAAU,GAAG,EAAI,MAEtBP;AACZ,WAAOQ,EAAY,CAAC,MAAMC,EAAYF,GAAO,CAAO,CAAC,CAAC;AAExD,QAAM,EAAE,MAAAG,GAAM,aAAAC,MAAgBC,GAAmBN,CAAM;AAIvD,MAAII,EAAK,QAAQT;AACf,UAAM,IAAI;AAAA,MACR,uBAAuBS,EAAK,KAAK,2CAC5BT,CAAmB;AAAA,IAAA;AAG5B,QAAMY,IAAyB,CAAA;AAC/B,MAAIC,GACAC;AACJ,aAAW,CAAClC,GAAOI,CAAK,KAAKyB,EAAK,OAAO,WAAW;AAClD,QAAIzB,EAAM,aAAa;AACrB,YAAM,IAAI;AAAA,QACR,aAAaJ,CAAK,gCAAgCI,EAAM,QAAQ;AAAA,MAAA;AAIpE,UAAM+B,IAAQL,IAAc1B,EAAM;AAClC,QAAI+B,IAAQ/B,EAAM,QAAQsB,EAAM;AAC9B,YAAM,IAAI,MAAM,aAAa1B,CAAK,yBAAyB;AAE7D,UAAMoC,IAAQ,MAAMR,EAAYF,GAAOS,GAAO,IAAMF,GAAYC,CAAQ;AACxE,IAAAD,UAAeG,EAAM,aAGrBF,WAAaG,IAAAD,EAAM,aAAN,gBAAAC,EAAgB,MAAM,IAAI,KACvCL,EAAO,KAAKI,CAAK;AAAA,EACnB;AACA,SAAOT,EAAYK,CAAM;AAC3B;AAOO,SAASD,GAAmBN,GAA6D;AAC9F,QAAMC,IAAQ,IAAI,WAAWD,CAAM;AACnC,MAAIC,EAAM,aAAa,EAAG,OAAM,IAAI,MAAM,qBAAqB;AAC/D,QAAM3B,IAAO,IAAI,SAAS2B,EAAM,QAAQA,EAAM,YAAYA,EAAM,UAAU,GACpEY,IAAQvC,EAAK,UAAU,GAAG,EAAI;AACpC,MAAIuC,MAAUpB;AACZ,UAAM,IAAI,MAAM,2BAA2BoB,EAAM,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI;AAEpF,QAAMxB,IAASf,EAAK,UAAU,GAAG,EAAI;AACrC,MAAI2B,EAAM,aAAa,IAAIZ,EAAQ,OAAM,IAAI,MAAM,uBAAuB;AAC1E,QAAMe,IAAO,KAAK,MAAMU,GAAWb,GAAO,GAAGZ,CAAM,CAAC;AACpD,MAAIe,EAAK,YAAY,EAAG,OAAM,IAAI,MAAM,2BAA2BA,EAAK,OAAO,GAAG;AAClF,MAAIA,EAAK,SAAS,SAAU,OAAM,IAAI,MAAM,yBAAyBA,EAAK,IAAI,IAAI;AAElF,MADAR,GAAsBQ,EAAK,OAAO,QAAQ,GACtC,CAAC,MAAM,QAAQA,EAAK,MAAM,EAAG,OAAM,IAAI,MAAM,uCAAuC;AAGxF,QAAMW,IAAwCX,EAAK;AACnD,aAAW,CAAC7B,GAAOI,CAAK,KAAKoC,EAAY;AACvC,QACE,CAAC,OAAO,cAAcpC,KAAA,gBAAAA,EAAO,MAAM,KACnCA,EAAM,SAAS,KACf,CAAC,OAAO,cAAcA,KAAA,gBAAAA,EAAO,KAAK,KAClCA,EAAM,QAAQ;AAEd,YAAM,IAAI,MAAM,aAAaJ,CAAK,kCAAkC;AAOxE,MAAI6B,EAAK,cAAc,QAAW;AAChC,QACE,CAAC,OAAO,cAAcA,EAAK,SAAS,KACpCA,EAAK,aAAa,KAClBA,EAAK,YAAYT;AAEjB,YAAM,IAAI,MAAM,2BAA2BS,EAAK,SAAS,GAAG;AAE9D,QAAIA,EAAK,QAAQA,EAAK,YAAYW,EAAY;AAC5C,YAAM,IAAI;AAAA,QACR,uBAAuBX,EAAK,KAAK,0BAA0BW,EAAY,MAAM,cACxEX,EAAK,SAAS;AAAA,MAAA;AAAA,EAGzB;AACA,SAAO,EAAE,MAAAA,GAAM,aAAa,IAAIY,GAAS3B,CAAM,EAAA;AACjD;AAWA,eAAsB4B,GACpBjB,GACAQ,GACAC,GACAS,IAAU,IACU;AACpB,QAAMP,IAAQ,MAAMR,EAAY,IAAI,WAAWH,CAAM,GAAG,GAAG,IAAMQ,GAAYC,CAAQ;AACrF,SAAOU,GAAiBR,GAAOO,CAAO;AACxC;AAcA,SAASC,GAAiBR,GAAqBO,GAA6B;AAC1E,QAAM,EAAE,OAAA1E,MAAUmE,GACZS,IAAY,IAAI,aAAa5E,IAAQ,CAAC,GACtC6E,IAAS,IAAI,WAAW7E,IAAQ,CAAC,GACjC8E,IAAc,IAAI,aAAa9E,IAAQ,CAAC;AAE9C,MAAI,CAACmE,EAAM,cAAc,CAACA,EAAM,YAAY;AAC1C,aAAShD,IAAI,GAAGA,IAAInB,GAAOmB,IAAK,CAAA4D,EAAWZ,GAAOhD,GAAGA,GAAGyD,GAAWC,GAAQC,CAAW;AACtF,WAAO;AAAA,MACL,OAAA9E;AAAA,MACA,WAAA4E;AAAA,MACA,QAAAC;AAAA,MACA,aAAAC;AAAA,MACA,GAAIX,EAAM,WAAW,EAAE,UAAUA,EAAM,SAAA,IAAa,CAAA;AAAA,MACpD,GAAIA,EAAM,aAAa,EAAE,eAAeA,EAAM,WAAA,IAAe,CAAA;AAAA,IAAC;AAAA,EAElE;AAEA,QAAMa,IAAab,EAAM,YACnBc,IAAad,EAAM,YAKnBe,IAAQR,IAAUS,GAAcH,GAAYC,GAAYjF,CAAK,IAAIoF,GAAcpF,CAAK,GACpFqF,IAAcC,GAAgBnB,EAAM,UAAUe,CAAK,GACnDK,IAAW,IAAI,YAAYvF,CAAK,GAChCwF,IAAW,IAAI,YAAYxF,CAAK,GAChCyF,IAAU,IAAI,aAAazF,CAAK;AACtC,WAAS0F,IAAI,GAAGA,IAAI1F,GAAO0F,KAAK;AAC9B,UAAMC,IAAMT,EAAMQ,CAAC;AACnB,IAAAX,EAAWZ,GAAOwB,GAAKD,GAAGd,GAAWC,GAAQC,CAAW,GACxDS,EAASG,CAAC,IAAIV,EAAWW,CAAG,GAC5BH,EAASE,CAAC,IAAIT,EAAWU,CAAG,GAC5BF,EAAQC,CAAC,IAAIE,GAAYzB,EAAM,MAAMwB,CAAG,GAAaxB,EAAM,OAAOwB,CAAG;AAAA,EACvE;AACA,SAAO;AAAA,IACL,OAAA3F;AAAA,IACA,WAAA4E;AAAA,IACA,QAAAC;AAAA,IACA,aAAAC;AAAA,IACA,SAAS,EAAE,YAAYS,GAAU,YAAYC,GAAU,MAAMC,EAAA;AAAA,IAC7D,GAAIJ,IAAc,EAAE,UAAUA,EAAA,IAAgB,CAAA;AAAA,IAC9C,GAAIlB,EAAM,aAAa,EAAE,eAAeA,EAAM,WAAA,IAAe,CAAA;AAAA,EAAC;AAElE;AAyBA,SAASyB,GAAYC,GAAeC,GAAqB3E,GAAmB;AAC1E,QAAM4E,KACFD,EAAM3E,IAAI,CAAC,IAAgB2E,EAAM3E,IAAI,IAAI,CAAC,IAAgB2E,EAAM3E,IAAI,IAAI,CAAC,KAAgB;AAC7F,SAAO,KAAK,KAAK,IAAI0E,GAAO,CAAC,IAAI,IAAI,KAAKE;AAC5C;AAGA,SAASX,GAAcpF,GAA4B;AACjD,QAAMkF,IAAQ,IAAI,YAAYlF,CAAK;AACnC,WAASmB,IAAI,GAAGA,IAAInB,GAAOmB,IAAK,CAAA+D,EAAM/D,CAAC,IAAIA;AAC3C,SAAO+D;AACT;AAIA,SAASC,GACPH,GACAC,GACAjF,GACa;AACb,QAAMkF,IAAQ,IAAI,YAAYlF,CAAK;AACnC,WAASmB,IAAI,GAAGA,IAAInB,GAAOmB,IAAK,CAAA+D,EAAM/D,CAAC,IAAIA;AAI3C,QAAM6E,IAAM,CAAC7E,MACX6D,EAAW7D,CAAC,MAAM,IAAI,OAAO,mBAAoB8D,EAAW9D,CAAC;AAC/D,SAAO+D,EAAM,KAAK,CAACe,GAAGC,MAAMF,EAAIE,CAAC,IAAIF,EAAIC,CAAC,CAAC;AAC7C;AAGA,eAAetC,EACbF,GACA0C,GACAC,GACAC,GACAC,GACuB;AACvB,QAAMxE,IAAO,IAAI,SAAS2B,EAAM,QAAQA,EAAM,YAAYA,EAAM,UAAU,GACpEY,IAAQvC,EAAK,UAAUqE,GAAY,EAAI;AAC7C,MAAI9B,MAAUnB;AACZ,UAAM,IAAI,MAAM,4BAA4BmB,EAAM,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI;AAErF,QAAMxB,IAASf,EAAK,UAAUqE,IAAa,GAAG,EAAI,GAC5CI,IAAUJ,IAAa,IAAI3B,GAAS3B,CAAM,GAC1C2D,IAAY,KAAK,MAAMlC,GAAWb,GAAO0C,IAAa,GAAGtD,CAAM,CAAC;AACtE,MAAI2D,EAAU,YAAY;AACxB,UAAM,IAAI,MAAM,iCAAiCA,EAAU,OAAO,GAAG;AAEvE,QAAMC,IAAe,OAAO3E,EAAK,aAAayE,GAAS,EAAI,CAAC,GACtDG,IAAeH,IAAU;AAC/B,MAAIG,IAAeD,IAAehD,EAAM;AACtC,UAAM,IAAI,MAAM,0CAA0C;AAE5D,QAAM,EAAE,OAAAzD,MAAUwG;AAClB,EAAAlD,GAAmBtD,GAAO,OAAO;AACjC,QAAM2G,IAAUH,EAAU,WAAW;AAErC,MAAI,CAAC,MAAM,QAAQA,EAAU,UAAU;AACrC,UAAM,IAAI,MAAM,kDAAkD;AAIpE,QAAMI,IAA0CJ,EAAU,YACpDK,IAAS,IAAI,IAAID,EAAW,IAAI,CAAClH,MAAS,CAACA,EAAK,UAAUA,CAAI,CAAC,CAAC,GAChEoH,IAAW,OAAOC,MAAkD;AACxE,UAAMrH,IAAOmH,EAAO,IAAIE,CAAI;AAC5B,QAAI,CAACrH,EAAM;AACX,QACE,CAAC,OAAO,cAAcA,EAAK,MAAM,KACjCA,EAAK,SAAS,KACd,CAAC,OAAO,cAAcA,EAAK,KAAK,KAChCA,EAAK,QAAQ;AAEb,YAAM,IAAI,MAAM,iBAAiBqH,CAAI,mCAAmC;AAE1E,UAAM7C,IAAQwC,IAAehH,EAAK;AAClC,QAAIA,EAAK,SAASA,EAAK,QAAQ+G,KAAgBvC,IAAQxE,EAAK,QAAQ+D,EAAM;AACxE,YAAM,IAAI,MAAM,iBAAiBsD,CAAI,2BAA2B;AAElE,UAAMC,IAAMvD,EAAM,SAASS,GAAOA,IAAQxE,EAAK,KAAK;AACpD,WAAOA,EAAK,gBAAgB,SAAYsH,IAAMC,GAAiBD,CAAG;AAAA,EACpE,GAEME,IAAuBP,GACvBQ,IAAiD;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAID,IAAW,CAAC,eAAe,aAAa,IAAI,CAAC,QAAW,MAAS;AAAA,IACrE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,GAEI;AAAA,IACJE;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,EAAA,IACE,MAAM,QAAQ;AAAA,IAChBd,EAAc,IAAI,CAACJ,MAAUA,MAAS,SAAY,QAAQ,QAAQ,MAAS,IAAID,EAASC,CAAI,CAAE;AAAA,EAAA;AAEhG,MAAI,CAACK,EAAW,OAAM,IAAI,MAAM,6CAA6C;AAC7E,MAAI,CAACC,EAAU,OAAM,IAAI,MAAM,4CAA4C;AAC3E,MAAI,CAACC,EAAQ,OAAM,IAAI,MAAM,0CAA0C;AACvE,MAAI,CAACC,EAAU,OAAM,IAAI,MAAM,6CAA6C;AAC5E,MAAI,CAACC,EAAgB,OAAM,IAAI,MAAM,kDAAkD;AAEvF,SAAO;AAAA,IACL,OAAAxH;AAAA,IACA,SAAA2G;AAAA,IACA,QAAQzG,EAAkBkH,GAAWP,EAAO,IAAI,QAAQ,GAAI,GAAG7G,CAAK;AAAA,IACpE,OAAOE,EAAkBmH,GAAUR,EAAO,IAAI,OAAO,GAAI,GAAG7G,CAAK;AAAA,IACjE,KAAKE,EAAkBoH,GAAQT,EAAO,IAAI,KAAK,GAAI,GAAG7G,CAAK;AAAA,IAC3D,OAAOE,EAAkBqH,GAAUV,EAAO,IAAI,QAAQ,GAAI,GAAG7G,CAAK;AAAA,IAClE,MAAMe,GAAkByG,GAAgBX,EAAO,IAAI,aAAa,GAAI7G,CAAK;AAAA,IACzE,GAAIkH,KAAYO,IACZ,EAAE,YAAYlG,GAAiBkG,GAAeZ,EAAO,IAAI,aAAa,GAAI7G,CAAK,EAAA,IAC/E,CAAA;AAAA,IACJ,GAAIkH,KAAYQ,IACZ,EAAE,YAAYjG,GAAiBiG,GAAeb,EAAO,IAAI,aAAa,GAAI7G,CAAK,EAAA,IAC/E,CAAA;AAAA,IACJ,GAAGkI,GAAY;AAAA,MACb,OAAAlI;AAAA,MACA,QAAA6G;AAAA,MACA,QAAAc;AAAA,MACA,QAAAC;AAAA,MACA,QAAAC;AAAA,MACA,YAAAC;AAAA,MACA,YAAAC;AAAA,MACA,YAAAC;AAAA,MACA,YAAAC;AAAA,MACA,iBAAA5B;AAAA,MACA,eAAAC;AAAA,IAAA,CACD;AAAA,EAAA;AAEL;AAOA,SAAS5C,EAAYK,GAA4C;;AAC/D,MAAIoE,IAAQ;AACZ,aAAWhE,KAASJ;AAClB,IAAAoE,KAAShE,EAAM,aAAaiE,GAAYjE,EAAM,UAAU,IAAIA,EAAM;AAEpE,MAAIgE,MAAU,EAAG,OAAM,IAAI,MAAM,8BAA8B;AAE/D,QAAMvD,IAAY,IAAI,aAAauD,IAAQ,CAAC,GACtCtD,IAAS,IAAI,WAAWsD,IAAQ,CAAC,GACjCrD,IAAc,IAAI,aAAaqD,IAAQ,CAAC,GACxCE,KAAWjE,IAAAL,EAAO,KAAK,CAACI,MAAUA,EAAM,QAAQ,MAArC,gBAAAC,EAAwC,UACnDkE,IAASD,IAAW,IAAI,YAAYF,IAAQI,EAAmBF,EAAS,KAAK,CAAC,IAAI;AAIxF,EAAIC,KAAUD,KAAUC,EAAO,KAAKE,GAAcH,EAAS,KAAK,CAAC;AAEjE,MAAInH,IAAM;AACV,aAAWiD,KAASJ;AAClB,aAAS5C,IAAI,GAAGA,IAAIgD,EAAM,OAAOhD;AAC/B,UAAI,EAAAgD,EAAM,cAAcA,EAAM,WAAWhD,CAAC,MAAM,IAEhD;AAAA,YADA4D,EAAWZ,GAAOhD,GAAGD,GAAK0D,GAAWC,GAAQC,CAAW,GACpDwD,KAAUnE,EAAM,UAAU;AAC5B,gBAAMsE,IAAQF,EAAmBF,EAAU,KAAK;AAChD,UAAAC,EAAO,IAAInE,EAAM,SAAS,OAAO,SAAShD,IAAIsH,IAAQtH,IAAI,KAAKsH,CAAK,GAAGvH,IAAMuH,CAAK;AAAA,QACpF;AACA,QAAAvH;AAAA;AAIJ,SAAO;AAAA,IACL,OAAOiH;AAAA,IACP,WAAAvD;AAAA,IACA,QAAAC;AAAA,IACA,aAAAC;AAAA,IACA,GAAIuD,KAAYC,IAAS,EAAE,UAAU,EAAE,GAAGD,GAAU,QAAAC,EAAA,MAAa,CAAA;AAAA,EAAC;AAEtE;AAEA,SAAShD,GACPoD,GACAxD,GAC+B;AAC/B,MAAI,CAACwD,EAAI;AACT,QAAMD,IAAQF,EAAmBG,EAAG,KAAK,GACnCJ,IAAS,IAAI,YAAYI,EAAG,OAAO,MAAM;AAC/C,WAASC,IAAM,GAAGA,IAAMzD,EAAM,QAAQyD,KAAO;AAC3C,UAAMhD,IAAMT,EAAMyD,CAAG;AACrB,IAAAL,EAAO,IAAII,EAAG,OAAO,SAAS/C,IAAM8C,IAAQ9C,IAAM,KAAK8C,CAAK,GAAGE,IAAMF,CAAK;AAAA,EAC5E;AACA,SAAO,EAAE,GAAGC,GAAI,QAAAJ,EAAA;AAClB;AA0BA,SAASJ,GAAYU,GAAmE;AACtF,QAAMC,IAAS,CAACD,EAAM,QAAQA,EAAM,QAAQA,EAAM,MAAM,GAClDE,IAAO,CAACF,EAAM,YAAYA,EAAM,YAAYA,EAAM,UAAU,GAC5DG,IAAcF,EAAO,OAAO,CAACG,GAAOhC,GAAKjF,MAAWiF,IAAMjF,IAAQ,IAAIiH,GAAQ,CAAC,GAC/EC,IAAYH,EAAK,OAAO,CAACE,GAAOhC,GAAKjF,MAAWiF,IAAMjF,IAAQ,IAAIiH,GAAQ,CAAC;AACjF,MAAID,MAAgB,KAAKE,MAAc,KAAK,CAACL,EAAM,mBAAmB,CAAA;AACtE,MAAIG,IAAc,KAAKE,IAAY;AACjC,UAAM,IAAI,MAAM,iDAAiD;AAEnE,MAAIC,GACAF,GACAG,GACAC;AACJ,MAAIL,IAAc;AAChB,IAAAC,IAAQD,GACRG,IAAeG,EAAiBR,GAAQD,EAAM,QAAQA,EAAM,OAAOI,CAAK,GACxEI,IAASE,GAAiBV,EAAM,QAAQI,CAAK,KAAKJ,EAAM;AAAA,OACnD;AAEL,QADAO,IAAWP,EAAM,iBACb,CAACO,KAAYF,IAAY,GAAG;AAC9B,MAAAD,IAAQC;AACR,YAAMM,IAAYC;AAAA,QAChBV,EAAKG,IAAY,CAAC;AAAA,QAClBL,EAAM,OAAO,IAAI,KAAKK,CAAS,OAAO;AAAA,MAAA;AAExC,MAAAE,IAAW;AAAA,QACT,OAAAH;AAAA,QACA,OAAOO;AAAA,QACP,cAAcF,EAAiBP,GAAMF,EAAM,QAAQW,GAAWP,GAAO,OAAO;AAAA,MAAA;AAAA,IAEhF;AACA,QAAI,CAACG,EAAU,OAAM,IAAI,MAAM,0DAA0D;AACzF,QAAI,CAACP,EAAM;AACT,YAAM,IAAI,MAAM,2DAA2D;AAC7E,IAAAI,IAAQG,EAAS;AACjB,UAAMM,IAASC,GAAed,EAAM,YAAYA,EAAM,OAAO,IAAI,UAAU,GAAIA,EAAM,KAAK;AAC1F,IAAAM,IAAe,IAAI,aAAaN,EAAM,QAAQL,EAAmBS,CAAK,IAAI,CAAC;AAC3E,UAAMP,IAAQF,EAAmBS,CAAK,IAAI;AAC1C,aAAS7H,IAAI,GAAGA,IAAIyH,EAAM,OAAOzH,KAAK;AACpC,YAAMwI,IAAQF,EAAOtI,CAAC;AACtB,UAAIwI,KAASR,EAAS;AACpB,cAAM,IAAI,MAAM,gBAAgBQ,CAAK,gBAAgBR,EAAS,KAAK,kBAAkB;AACvF,MAAAD,EAAa;AAAA,QACXC,EAAS,aAAa,SAASQ,IAAQlB,IAAQkB,IAAQ,KAAKlB,CAAK;AAAA,QACjEtH,IAAIsH;AAAA,MAAA;AAAA,IAER;AAGA,IAAAW,IAAS;AACT,eAAW/G,KAAS8G,EAAS,aAAc,CAAAC,IAAS,KAAK,IAAIA,GAAQ,KAAK,IAAI/G,CAAK,CAAC;AAAA,EACtF;AACA,SAAO;AAAA,IACL,UAAUuH,GAAmBV,GAAcN,EAAM,OAAOI,GAAOI,CAAM;AAAA,IACrE,GAAID,IAAW,EAAE,YAAYA,MAAa,CAAA;AAAA,EAAC;AAE/C;AASA,SAASG,GACPzC,GACAmC,GACAa,IAAS,IACW;AACpB,MAAIT,IAAS;AACb,WAASU,IAAO,GAAGA,KAAQd,GAAOc,KAAQ;AACxC,UAAMpK,IAAOmH,EAAO,IAAI,KAAKiD,CAAI,GAAGD,CAAM,EAAE;AAC5C,QAAI,CAACnK,EAAM;AACX,QAAIqK;AACJ,YAAQrK,EAAK,UAAA;AAAA,MACX,KAAK;AAAA,MACL,KAAK;AACH,YAAIA,EAAK,QAAQ,OAAW;AAC5B,QAAAqK,IAAa,KAAK,IAAIrK,EAAK,GAAG;AAC9B;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,YAAIA,EAAK,QAAQ,UAAaA,EAAK,QAAQ,OAAW;AACtD,QAAAqK,IAAa,KAAK,IAAI,KAAK,IAAIrK,EAAK,GAAG,GAAG,KAAK,IAAIA,EAAK,GAAG,CAAC;AAC5D;AAAA,MACF;AACE;AAAA,IAAO;AAEX,IAAA0J,IAAS,KAAK,IAAIA,GAAQW,CAAU;AAAA,EACtC;AACA,SAAOX;AACT;AAKA,SAASC,EACPW,GACAnD,GACA7G,GACAgJ,GACAa,IAAS,IACK;AACd,QAAMI,IAAoB1B,EAAmBS,CAAK,GAC5CkB,IAAS,IAAI,aAAalK,IAAQiK,IAAoB,CAAC;AAC7D,MAAIE,IAAoB;AACxB,WAASL,IAAO,GAAGA,KAAQd,GAAOc,KAAQ;AACxC,UAAM9C,IAAMgD,EAAKF,IAAO,CAAC,GACnBpK,IAAOmH,EAAO,IAAI,KAAKiD,CAAI,GAAGD,CAAM,EAAE;AAC5C,QAAI,CAAC7C,KAAO,CAACtH,EAAM,OAAM,IAAI,MAAM,4BAA4BoK,CAAI,GAAGD,CAAM,WAAW;AACvF,UAAM9J,IAAO+J,MAAS,IAAI,IAAIA,MAAS,IAAI,KAAK,IAC1CM,IAASlK,EAAkB8G,GAAKtH,GAAMK,GAAMC,CAAK;AACvD,aAASmB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,YAAMkJ,KAAQlJ,IAAI8I,IAAoBE,KAAqB;AAC3D,eAASG,IAAI,GAAGA,IAAIvK,GAAMuK,IAAK,CAAAJ,EAAOG,IAAOC,CAAC,IAAIF,EAAOjJ,IAAIpB,IAAOuK,CAAC;AAAA,IACvE;AACA,IAAAH,KAAqBpK,IAAO;AAAA,EAC9B;AACA,SAAOmK;AACT;AAEA,SAASV,GAAcxC,GAAiBtH,GAAgC;AACtE,QAAM6K,IAAa7K,EAAK,SAAS,WAAW,KAAK,IAC7C,IACAA,EAAK,SAAS,WAAW,KAAK,IAC5B,KACA,IACAO,IAAgBR,GAAgBC,CAAI;AAC1C,MAAIsH,EAAI,cAAcuD,IAAatK,OAAmB;AACpD,UAAM,IAAI,MAAM,QAAQP,EAAK,QAAQ,oCAAoC;AAC3E,SAAOsH,EAAI,cAAcuD,IAAatK;AACxC;AAGA,SAASR,GAAgBC,GAAgC;AACvD,UAAQA,EAAK,UAAA;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,YAAM,IAAI,MAAM,oBAAoBA,EAAK,QAAQ,eAAeA,EAAK,QAAQ,IAAI;AAAA,EAAA;AAEvF;AAEA,SAASgK,GAAe1C,GAAiBtH,GAAwBM,GAA4B;AAC3F,MAAIN,EAAK,aAAa;AACpB,WAAAG,EAAuBmH,GAAKtH,GAAM,GAAGM,GAAO,CAAC,GACtC,YAAY,KAAKwB,GAAUwF,GAAKhH,CAAK,CAAC;AAE/C,MAAIN,EAAK,aAAa;AACpB,WAAAG,EAAuBmH,GAAKtH,GAAM,GAAGM,GAAO,CAAC,GACtC0B,GAAUsF,GAAKhH,CAAK;AAE7B,QAAM,IAAI,MAAM,sCAAsCN,EAAK,QAAQ,IAAI;AACzE;AAGA,SAASqF,EACPZ,GACAwB,GACAgD,GACA/D,GACAC,GACAC,GACM;AAON,QAAMe,IAAQ1B,EAAM,MAAMwB,CAAG;AAC7B,EAAAf,EAAU+D,IAAM,IAAI,CAAC,IAAIxE,EAAM,OAAOwB,IAAM,IAAI,CAAC,GACjDf,EAAU+D,IAAM,IAAI,CAAC,IAAIxE,EAAM,OAAOwB,IAAM,IAAI,CAAC,GACjDf,EAAU+D,IAAM,IAAI,CAAC,IAAIxE,EAAM,OAAOwB,IAAM,IAAI,CAAC,GACjDd,EAAO8D,IAAM,IAAI,CAAC,IAAI6B,EAAWrG,EAAM,IAAIwB,IAAM,IAAI,CAAC,IAAe,GAAG,GACxEd,EAAO8D,IAAM,IAAI,CAAC,IAAI6B,EAAWrG,EAAM,IAAIwB,IAAM,IAAI,CAAC,IAAe,GAAG,GACxEd,EAAO8D,IAAM,IAAI,CAAC,IAAI6B,EAAWrG,EAAM,IAAIwB,IAAM,IAAI,CAAC,IAAe,GAAG,GACxEd,EAAO8D,IAAM,IAAI,CAAC,IAAI6B,EAAU3E,IAAQ,MAAM,GAAG,GACjD4E;AAAA,IACE3F;AAAA,IACA6D;AAAA,IACAxE,EAAM,MAAMwB,IAAM,IAAI,CAAC;AAAA,IACvBxB,EAAM,MAAMwB,IAAM,IAAI,CAAC;AAAA,IACvBxB,EAAM,MAAMwB,IAAM,IAAI,CAAC;AAAA,IACvBxB,EAAM,KAAKwB,IAAM,IAAI,CAAC;AAAA;AAAA,IACtBxB,EAAM,KAAKwB,IAAM,IAAI,CAAC;AAAA;AAAA,IACtBxB,EAAM,KAAKwB,IAAM,IAAI,CAAC;AAAA;AAAA,IACtBxB,EAAM,KAAKwB,IAAM,IAAI,CAAC;AAAA;AAAA,EAAA;AAE1B;AAEA,SAASyC,GAAYpD,GAAiC;AACpD,MAAI0F,IAAS;AACb,aAAWrI,KAAS2C,EAAY,CAAI3C,MAAU,KAAGqI;AACjD,SAAOA;AACT;AAEA,SAASF,EAAUnI,GAAuB;AACxC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAMA,CAAK,CAAC,CAAC;AACrD;AAEA,SAASmC,GAASmG,GAAsB;AACtC,SAAQA,IAAO,IAAK;AACtB;AAEA,SAASrG,GAAWb,GAAmBmH,GAAgB/H,GAAwB;AAC7E,SAAO,IAAI,cAAc,OAAOY,EAAM,SAASmH,GAAQA,IAAS/H,CAAM,CAAC;AACzE;AAQA,eAAeoE,GAAiBxD,GAAwC;AACtE,MAAI,OAAO,sBAAwB;AACjC,UAAM,IAAI,MAAM,qEAAqE;AAEvF,QAAMoH,IACJpH,EAAM,CAAC,MAAM,MAAQA,EAAM,CAAC,MAAM,MAAO,SAASA,EAAM,CAAC,MAAM,MAAO,YAAY,eAC9EqH,IAAO,IAAI,WAAWrH,EAAM,UAAU;AAC5C,EAAAqH,EAAK,IAAIrH,CAAK;AACd,QAAMsH,IAAS,IAAI,KAAK,CAACD,EAAK,MAAM,CAAC,EAAE,OAAA,EAAS,YAAY,IAAI,oBAAoBD,CAAM,CAAC;AAC3F,SAAO,IAAI,WAAW,MAAM,IAAI,SAASE,CAAM,EAAE,aAAa;AAChE;ACvxBO,MAAMC,GAAe;AAAA,EAS1B,YAAYC,GAAmB;AARd,IAAAC,EAAA;AAGA;AAAA;AAAA,IAAAA,EAAA,qCAAc,IAAA;AAGd;AAAA;AAAA,IAAAA,EAAA,sCAAe,IAAA;AAG9B,SAAK,YAAYD;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,QACEE,GACAnG,GACAC,GACA0F,GACc;AACd,UAAM3K,IAAQgF,EAAW,QACnBqF,IAAOc,IAAa,KAAK,WAEzBC,IAAa,IAAI,aAAapL,CAAK,EAAE,KAAK,KAAQ,GAElDqL,IAAS,KAAK,QAAQ,IAAIF,CAAU;AAC1C,QAAIE,GAAQ;AACV,iBAAWlJ,KAASkJ,GAAQ;AAC1B,cAAMC,IAAO,KAAK,IAAInJ,EAAM,QAAQkI,GAAM,CAAC,GACrCkB,IAAK,KAAK,IAAIpJ,EAAM,MAAMkI,GAAMrK,CAAK;AAC3C,iBAASwL,IAAQF,GAAME,IAAQD,GAAIC,IAAS,CAAAJ,EAAWI,CAAK,IAAIrJ,EAAM;AAAA,MACxE;AACA,WAAK,QAAQ,OAAOgJ,CAAU;AAAA,IAChC;AACA,SAAK,SAAS,IAAIA,CAAU;AAG5B,aAAShK,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,YAAMsK,IAAKzG,EAAW7D,CAAC;AACvB,UAAIsK,MAAO,EAAG;AACd,YAAMC,IAAiBf,EAAKxJ,CAAC;AAC7B,UAAIwK,IAAI1G,EAAW9D,CAAC;AACpB,YAAMyK,IAAMD,IAAIF;AAEhB,aAAOE,IAAIC,KAAK;AACd,cAAMC,IAAa,KAAK,MAAMF,IAAI,KAAK,SAAS,GAC1CG,KAAYD,IAAa,KAAK,KAAK,WACnCE,IAAW,KAAK,IAAIH,GAAKE,CAAQ;AACvC,QAAK,KAAK,SAAS,IAAID,CAAU,KAAG,KAAK,KAAKA,GAAYF,GAAGI,GAAUL,CAAc,GACrFC,IAAII;AAAA,MACN;AAAA,IACF;AAEA,WAAOX;AAAA,EACT;AAAA,EAEQ,KAAKS,GAAoB3H,GAAe0H,GAAajB,GAAoB;AAC/E,QAAIU,IAAS,KAAK,QAAQ,IAAIQ,CAAU;AACxC,IAAKR,MACHA,IAAS,CAAA,GACT,KAAK,QAAQ,IAAIQ,GAAYR,CAAM,IAErCA,EAAO,KAAK,EAAE,OAAAnH,GAAO,KAAA0H,GAAK,MAAAjB,GAAM;AAAA,EAClC;AACF;ACvFA,MAAMqB,IAAqB,MAIrBC,KAAkB,MAsBlBC,KAAiB,IAGjBC,IAAkB,GAElBC,KAAc,GAEdC,KAAqB;AAEpB,MAAMC,GAAuC;AAAA,EAmClD,YAAYrB,GAAmBsB,GAAgCC,GAA2B;AAlC1F,IAAAtB,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAEiB,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA,qCAAc,IAAA;AAEd;AAAA,IAAAA,EAAA;AAEA;AAAA,IAAAA,EAAA;AAEA;AAAA,IAAAA,EAAA;AAGA;AAAA;AAAA,IAAAA,EAAA;AAGjB;AAAA,IAAAA,EAAA,4BAAqB;AAWrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,EAAA,0BAAmB;AAEF,IAAAA,EAAA,sBAAe,IAAIuB,EAAM,QAAA;AAGxC,SAAK,YAAYxB,GACjB,KAAK,cAAcsB,GACnB,KAAK,YAAYA,EAAY,QAC7B,KAAK,SAASC,EAAQ,QACtB,KAAK,kBAAkBA,EAAQ,iBAC/B,KAAK,gBAAgBA,EAAQ,eAC7B,KAAK,SAAS,MAAM,KAAK,EAAE,QAAQ,KAAK,aAAa,MAAM,IAAI,GAC/D,KAAK,YAAY,MAAM,KAAK,EAAE,QAAQ,KAAK,aAAa,MAAM,IAAI,GAClE,KAAK,cAAc,MAAM,KAAK,EAAE,QAAQ,KAAK,aAAa,MAAM,IAAI,GACpE,KAAK,cAAc,IAAIxB,GAAeC,CAAS;AAAA,EACjD;AAAA,EAEA,eAAeyB,GAAc5M,GAAuB;AAClD,QAAI,KAAK,QAAQ,IAAI4M,CAAI,EAAG;AAC5B,SAAK,QAAQ,IAAIA,CAAI,GACrB,KAAK,OAAOA,CAAI,IAAIC,GAAS7M,CAAI;AAEjC,UAAM8M,IAAO9M,EAAK;AAClB,QAAI8M,GAAM;AACR,YAAMC,wBAAe,IAAA,GACfC,IAAMhN,EAAK;AACjB,eAAS,IAAI,GAAG,IAAI8M,EAAK,WAAW,QAAQ,KAAK;AAC/C,YAAIA,EAAK,WAAW,CAAC,MAAM,EAAG;AAC9B,cAAMf,IAAa,KAAK,MAAOe,EAAK,WAAW,CAAC,IAAe,KAAK,SAAS;AAC7E,QAAAC,EAAS,IAAIhB,CAAU;AACvB,YAAIkB,IAAM,KAAK,UAAUlB,CAAU;AACnC,QAAKkB,MACHA,IAAM,IAAIN,EAAM,KAAA,EAAO,UAAA,GACvB,KAAK,UAAUZ,CAAU,IAAIkB,IAE/BA,EAAI;AAAA,UACF,KAAK,aAAa,IAAID,EAAI,IAAI,CAAC,KAAK,GAAGA,EAAI,IAAI,IAAI,CAAC,KAAK,GAAGA,EAAI,IAAI,IAAI,CAAC,KAAK,CAAC;AAAA,QAAA;AAAA,MAEnF;AACA,WAAK,YAAYJ,CAAI,IAAIG,GACrB,KAAK,oBAAkB,KAAK,qBAAqBH,GAAM5M,GAAM8M,GAAMI,GAAgBlN,CAAI,CAAC;AAAA,IAC9F;AACA,IAAI4M,MAAS,MAAG,KAAK,qBAAqB,KAAK,IAAI,GAAG,KAAK,YAAY,CAAC,CAAW;AAAA,EACrF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBQ,qBACNA,GACA5M,GACA8M,GACAK,GACM;AACN,UAAMjN,IAAQF,EAAK,OACbsL,IAAa,KAAK,YAAY,QAAQsB,GAAME,EAAK,YAAYA,EAAK,YAAYK,CAAO,GACrFC,IAAiB,IAAI,aAAalN,CAAK;AAC7C,aAASmB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,UAAIgM,IAAI/B,EAAWjK,CAAC;AACpB,MAAK,OAAO,SAASgM,CAAC,MACpBA,IAAIT,MAAS,IAAIV,IAAsBiB,EAAQ9L,CAAC,IAAe8K,KAEjEkB,IAAI,KAAK,IAAIA,GAAGnB,CAAkB,GAClCkB,EAAe/L,CAAC,IAAIyL,EAAK,WAAWzL,CAAC,MAAM,IAAI,CAACgM,IAAIA;AAAA,IACtD;AACC,IAAArN,EAA2C,iBAAiBoN;AAAA,EAC/D;AAAA,EAEA,mBAAmBE,GAA4BC,GAAwBC,GAAwB;AAC7F,UAAMC,IAAW,KAAK,cAAcH,GAAaC,CAAO,GAClDG,IAAiB,CAAA;AACvB,QAAIC,IAAiB,GACjBC,IAAW;AACf,eAAWC,KAAKJ;AACd,MAAAC,EAAK,KAAK,KAAK,cAAcG,CAAC,CAAC,GAC/BF,KAAkB,KAAK,YAAYE,CAAC,GACpCD,IAAW,KAAK,IAAIA,GAAU,KAAK,cAAcC,GAAGP,GAAaC,CAAO,CAAC;AAO3E,UAAMO,IAAQH,KAAkB,KAAK,SAASC,IAAW;AACzD,eAAWC,KAAK,KAAK,4BAA4BJ,GAAUH,GAAaC,GAASO,CAAK;AACpF,MAAAJ,EAAK,KAAK,KAAK,cAAcG,CAAC,CAAC;AAEjC,WAAOH;AAAA,EACT;AAAA;AAAA;AAAA,EAIA,kBAA4B;AAC1B,WAAO,CAAA;AAAA,EACT;AAAA;AAAA,EAGQ,cAAcJ,GAA4BC,GAAqC;AACrF,UAAME,wBAAe,IAAA;AACrB,QAAIE,IAAiB;AACrB,aAASE,IAAI,GAAGA,IAAI,KAAK,IAAIvB,IAAa,KAAK,SAAS,GAAGuB;AACzD,MAAI,KAAK,QAAQ,IAAIA,CAAC,MACpBJ,EAAS,IAAII,CAAC,GACdF,KAAkB,KAAK,YAAYE,CAAC;AAGxC,UAAME,IAAU,CAAC,GAAG,KAAK,OAAO,EAC7B,OAAO,CAACF,MAAM,CAACJ,EAAS,IAAII,CAAC,CAAC,EAC9B;AAAA,MACC,CAAC1H,GAAGC,MACF,KAAK,cAAcD,GAAGmH,GAAaC,CAAO,IAAI,KAAK,cAAcnH,GAAGkH,GAAaC,CAAO;AAAA,IAAA;AAE9F,eAAWM,KAAKE,GAAS;AACvB,UAAIJ,KAAkB,KAAK,OAAQ;AACnC,MAAAF,EAAS,IAAII,CAAC,GACdF,KAAkB,KAAK,YAAYE,CAAC;AAAA,IACtC;AACA,WAAOJ;AAAA,EACT;AAAA,EAEQ,4BACNA,GACAH,GACAC,GACAS,GACU;AACV,UAAMC,wBAAiB,IAAA;AACvB,eAAWJ,KAAKJ,GAAU;AACxB,YAAMV,IAAW,KAAK,YAAYc,CAAC;AACnC,UAAKd;AACL,mBAAWpB,KAAMoB;AACf,UAAI,CAAC,KAAK,QAAQ,IAAIpB,CAAE,KAAK,KAAK,cAAcA,GAAI2B,GAAaC,CAAO,KAAKS,KAC3EC,EAAW,IAAItC,CAAE;AAAA,IAGvB;AAMA,WAAO,CAAC,GAAGsC,CAAU,EAClB;AAAA,MACC,CAAC9H,GAAGC,MACF,KAAK,YAAYA,GAAGkH,GAAaC,CAAO,IAAI,KAAK,YAAYpH,GAAGmH,GAAaC,CAAO;AAAA,IAAA,EAEvF,MAAM,GAAGnB,EAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAYyB,GAAWP,GAA4BC,GAAgC;AACzF,UAAMW,IAAM,KAAK,UAAUL,CAAC,KAAK,KAAK,OAAOA,CAAC;AAC9C,QAAI,CAACK,EAAK,QAAO;AACjB,UAAMhM,IAAI,KAAK,IAAIgM,EAAI,gBAAgBZ,CAAW,GAAGf,EAAkB;AACvE,YAAQgB,EAAQ,cAAcW,CAAG,IAAI,IAAI,IAAI7B,KAAmBnK;AAAA,EAClE;AAAA,EAEQ,cAAc2L,GAAWP,GAA4BC,GAAgC;AAC3F,UAAMW,IAAM,KAAK,OAAOL,CAAC,KAAK,KAAK,UAAUA,CAAC;AAC9C,QAAI,CAACK,EAAK,QAAO;AACjB,UAAMhM,IAAIgM,EAAI,gBAAgBZ,CAAW;AACzC,WAAOC,EAAQ,cAAcW,CAAG,IAAIhM,IAAIA,IAAImK;AAAA,EAC9C;AAAA;AAAA,EAGQ,cAAchI,GAAuB;AAC3C,WAAO;AAAA,MACL,MAAMA;AAAA,MACN,OAAO,KAAK,YAAYA;AAAA,MACxB,QAAQ;AAAA,MACR,OAAO,KAAK,YAAYA,CAAK;AAAA,MAC7B,WAAWA;AAAA,MACX,SAASA,IAAQ;AAAA,IAAA;AAAA,EAErB;AACF;AAIA,SAAS6I,GAAgBlN,GAA+B;AACtD,QAAME,IAAQF,EAAK,OACbmO,IAAMnO,EAAK,aACXmN,IAAU,IAAI,aAAajN,CAAK;AACtC,WAASmB,IAAI,GAAGA,IAAInB,GAAOmB,KAAK;AAC9B,UAAM+M,IAASD,EAAI9M,IAAI,CAAC,IAAgB8M,EAAI9M,IAAI,IAAI,CAAC,IAAgB8M,EAAI9M,IAAI,IAAI,CAAC;AAClF,IAAA8L,EAAQ9L,CAAC,IAAI,IAAI,KAAK,KAAK,KAAK,IAAI+M,GAAO,CAAC,IAAI,CAAC;AAAA,EACnD;AACA,SAAOjB;AACT;AAGA,SAASN,GAAS7M,GAA6B;AAC7C,QAAMkO,IAAM,IAAIvB,EAAM,KAAA,GAChBU,IAAI,IAAIV,EAAM,QAAA,GACdK,IAAMhN,EAAK;AACjB,WAASqB,IAAI,GAAGA,IAAIrB,EAAK,OAAOqB;AAC9B,IAAAgM,EAAE,IAAIL,EAAI3L,IAAI,CAAC,KAAK,GAAG2L,EAAI3L,IAAI,IAAI,CAAC,KAAK,GAAG2L,EAAI3L,IAAI,IAAI,CAAC,KAAK,CAAC,GAC/D6M,EAAI,cAAcb,CAAC;AAErB,SAAOa,EAAI,YACP,IAAIvB,EAAM,KAAK,IAAIA,EAAM,QAAQ,IAAI,IAAI,EAAE,GAAG,IAAIA,EAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,IACxEuB;AACN;ACnQA,MAAM9B,KAAiB;AAwBvB,eAAsBiC,GACpBC,GACA5B,GACA6B,GACAC,IAA4B,GAC5BC,IAAc,IACU;;AACxB,QAAM,EAAE,MAAA3K,GAAM,aAAAC,EAAA,IAAgB,MAAM2K,GAAeJ,EAAO,aAAaC,CAAO,GACxEpD,IAAYrH,EAAK,aAAaA,EAAK;AAEzC,MADkBA,EAAK,OAAO,WACZ,EAAG,OAAM,IAAI,MAAM,yBAAyB;AAM9D,QAAM6K,IAAY7K,EAAK,OAAO,IAAI,CAACzB,GAAOJ,MAAU;AAClD,QAAII,EAAM,aAAa,OAAW,QAAO,GAAGiM,EAAO,WAAW,cAAcrM,CAAK;AACjF,UAAM2M,IAAMN,EAAO,QAAQjM,EAAM,QAAQ;AACzC,QAAIuM,MAAQ;AACV,YAAM,IAAI,MAAM,aAAa3M,CAAK,+BAA+BI,EAAM,QAAQ,IAAI;AAErF,WAAOuM;AAAA,EACT,CAAC;AACD,MAAIC,IAAuC/K,EAAK,OAAO,IAAI,CAACzB,OAAW;AAAA,IACrE,QAAQ;AAAA,IACR,KACEA,EAAM,aAAa,SACf,EAAE,OAAO0B,IAAc1B,EAAM,QAAQ,QAAQA,EAAM,MAAA,IACnD,CAAA;AAAA,EAAC,EACP;AAOF,QAAMyM,IAAYC,GAAqBjL,EAAK,OAAO,KAAKA,EAAK,OAyBvDkL,IAAkBP,IACpBQ,GAAwBvC,EAAQ,QAAQoC,CAAS,IACjDpC,EAAQ,QACNwC,IAAUJ,IAAYK,MAA4BL,IAAYE,GAM9DI,IAActL,EAAK,OAAO,CAAC,GAC3BuL,IACJD,EAAY,aAAa,SACrB,MAAME;AAAA,IACJhB,EAAO;AAAA,IACPvK,IAAcqL,EAAY;AAAA,IAC1BA,EAAY;AAAA,IACZb;AAAA,EAAA,IAEF,MAAMgB,GAAWZ,EAAU,CAAC,GAAIJ,CAAO,GACvCiB,IAAa,MAAM7K,GAAuB0K,GAAc,QAAW,QAAW,CAACH,CAAO,GAItF/K,KAAWG,IAAAkL,EAAW,aAAX,gBAAAlL,EAAqB,MAAM,IAAI;AAChD,GAAIkL,EAAW,iBAAiBrL,MAAa,YAC3C0K,IAAeA,EAAa,IAAI,CAACnC,OAAa;AAAA,IAC5C,GAAGA;AAAAA,IACH,KAAK;AAAA,MACH,GAAGA,EAAQ;AAAA,MACX,GAAI8C,EAAW,gBAAgB,EAAE,YAAYA,EAAW,cAAA,IAAkB,CAAA;AAAA,MAC1E,GAAIrL,MAAa,SAAY,EAAE,UAAAA,MAAa,CAAA;AAAA,IAAC;AAAA,EAC/C,EACA;AAEJ,QAAMsL,IAAS5C,GAAS2C,CAAU,GAI5BE,IAAoBC,GAA2BH,EAAW,WAAWA,EAAW,KAAK,GAGrFI,IAAUpB,MAAe,IAAI,MAAKqB,IAAAL,EAAW,aAAX,gBAAAK,EAAqB,UAAS,GAEhEpD,IAAc3I,EAAK,OAAO;AAAA,IAAI,CAACgM,GAAQ7N,MAC3C,KAAK,IAAIkJ,GAAWrH,EAAK,QAAQ7B,IAAQkJ,CAAS;AAAA,EAAA;AAMpD,MAAI+D,GAAS;AACX,UAAMa,IAAW,IAAIvD,GAAkBrB,GAAWsB,GAAaC,CAAO;AACtE,WAAI8C,EAAW,WAASO,EAAS,eAAe,GAAGP,CAAU,GACtD;AAAA,MACL,QAAQO;AAAA,MACR,WAAApB;AAAA,MACA,WAAW;AAAA;AAAA;AAAA,MAGX,cAAcE,EAAa,IAAI,CAACmB,OAAY;AAAA,QAC1C,GAAGA;AAAA,QACH,KAAK,EAAE,GAAGA,EAAO,KAAM,SAAS,GAAA;AAAA,MAAM,EACtC;AAAA,MACF,SAAAJ;AAAA,MACA,QAAAH;AAAA,MACA,aAAa,oBAAI,IAAI,CAAC,CAAC,CAAC;AAAA;AAAA,MAExB,mBAAmB/C,EAAQ;AAAA;AAAA;AAAA;AAAA,MAI3B,mBAAmBoC;AAAA,MACnB,uBAAuBiB,EAAS;AAAA,MAChC,WAAWE;AAAA,MACX,WAAA9E;AAAA,MACA,mBAAAuE;AAAA;AAAA;AAAA;AAAA,MAIA,gBAAgB,EAAE,MAAM,GAAG,MAAMF,EAAA;AAAA,IAAW;AAAA,EAEhD;AAEA,QAAMU,IAAU,IAAIC,GAAarM,EAAK,OAAOqH,GAAWsB,GAAaC,CAAO;AAC5E,SAAI8C,EAAW,WAASU,EAAQ,eAAe,GAAGV,CAAU,GAErD;AAAA,IACL,QAAQU;AAAA,IACR,WAAAvB;AAAA,IACA,WAAW;AAAA,IACX,cAAAE;AAAA,IACA,SAAAe;AAAA,IACA,QAAAH;AAAA,IACA,aAAa,oBAAI,IAAI,CAAC,CAAC,CAAC;AAAA,IACxB,mBAAmBX;AAAA,IACnB,mBAAmBA;AAAA,IACnB,uBAAuBoB,EAAQ;AAAA,IAC/B,mBAAAR;AAAA,EAAA;AAEJ;AAIA,MAAMP,KAA2B,KAK3Bc,KAAqB,EAAE,mBAAmB,KAAK,mBAAmB,EAAA;AAGxE,SAASlB,GAAqBqB,GAAiD;AAC7E,MAAKA;AACL,QAAI;AAEF,YAAM7N,IADO,KAAK,MAAM6N,CAAO,EACZ;AACnB,aAAO,OAAO7N,KAAU,YAAY,OAAO,SAASA,CAAK,KAAKA,IAAQ,IAAIA,IAAQ;AAAA,IACpF,QAAQ;AACN;AAAA,IACF;AACF;AAGA,MAAM8N,KAAuB;AAE7B,SAASV,GAA2B7K,GAAyB5E,GAA6B;AACxF,MAAIA,KAAS,EAAG,QAAO,IAAI,aAAa,CAAC;AACzC,QAAMkC,IAAS,KAAK,IAAI,GAAG,KAAK,MAAMlC,IAAQmQ,EAAoB,CAAC;AACnE,MAAIC,IAAI;AACR,WAAS,IAAI,GAAG,IAAIpQ,GAAO,KAAKkC,EAAQ,CAAAkO;AACxC,QAAMlP,IAAM,IAAI,aAAakP,IAAI,CAAC;AAClC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAIpQ,GAAO,KAAKkC,GAAQ;AACtC,UAAMgE,IAAI,IAAI;AACd,IAAAhF,EAAI,GAAG,IAAI0D,EAAUsB,CAAC,GACtBhF,EAAI,GAAG,IAAI0D,EAAUsB,IAAI,CAAC,GAC1BhF,EAAI,GAAG,IAAI0D,EAAUsB,IAAI,CAAC;AAAA,EAC5B;AACA,SAAOhF;AACT;AAGA,SAASyL,GAAS7M,GAA6B;AAC7C,QAAMkO,IAAM,IAAIvB,EAAM,KAAA,GAChBU,IAAI,IAAIV,EAAM,QAAA,GACdK,IAAMhN,EAAK;AACjB,WAASqB,IAAI,GAAGA,IAAIrB,EAAK,OAAOqB;AAC9B,IAAAgM,EAAE,IAAIL,EAAI3L,IAAI,CAAC,KAAK,GAAG2L,EAAI3L,IAAI,IAAI,CAAC,KAAK,GAAG2L,EAAI3L,IAAI,IAAI,CAAC,KAAK,CAAC,GAC/D6M,EAAI,cAAcb,CAAC;AAErB,SAAOa,EAAI,YACP,IAAIvB,EAAM,KAAK,IAAIA,EAAM,QAAQ,IAAI,IAAI,EAAE,GAAG,IAAIA,EAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,IACxEuB;AACN;AAyBO,MAAMiC,GAAkC;AAAA,EA6C7C,YACE9H,GACA8C,GACAsB,GACAC,GACA;AAjDF,IAAAtB,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAEiB,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAkBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,EAAA,qCAAc,IAAA;AAEvB;AAAA,IAAAA,EAAA,yBAAkB;AAGlB;AAAA,IAAAA,EAAA,oBAAuB,CAAA;AAE/B;AAAA,IAAAA,EAAA,4BAAqB;AAGb;AAAA,IAAAA,EAAA,oBAAuB,CAAA;AACvB,IAAAA,EAAA,6BAAsB;AAEb;AAAA,IAAAA,EAAA,yCAAkB,IAAA;AAE3B;AAAA,IAAAA,EAAA,wBAAiB,OAAO;AACxB,IAAAA,EAAA,wBAAiB,OAAO;AACxB,IAAAA,EAAA,6BAAsB;AAQ5B,SAAK,QAAQ/C,GACb,KAAK,YAAY8C,GACjB,KAAK,cAAcsB,GACnB,KAAK,YAAYA,EAAY,QAC7B,KAAK,SAASC,EAAQ,QACtB,KAAK,kBAAkBA,EAAQ,iBAC/B,KAAK,gBAAgBA,EAAQ;AAAA,EAC/B;AAAA,EAEA,eAAeE,GAAc5M,GAAuB;AAClD,QAAI,GAACA,EAAK,WAAW,KAAK,QAAQ,IAAI4M,CAAI;AAS1C,WARA,KAAK,QAAQ,IAAIA,GAAM5M,EAAK,OAAO,GAC/B4M,MAAS,MAIX,KAAK,aAAa,KAAK,aAAa,GAAG,KAAK,YAAY,CAAC,CAAW,GACpE,KAAK,qBAAqB,KAAK,IAAI,GAAG2D,GAAY,KAAK,UAAU,CAAC,IAE7D,KAAK,QAAQ,IAAI,KAAK,eAAe,IAAG,MAAK;AAAA,EACtD;AAAA,EAEA,mBAAmBC,GAA6BC,GAAyBjD,GAAwB;AAM/F,QALA,KAAK,eAAA,GAKD,KAAK,uBAAuB,KAAK,eAAe,KAAK;AAOzD,UAAME,IAAO,CAAC,GAAG,KAAK,UAAU,GAC1BgD,IAAQ,KAAK,IAAI,KAAK,kBAAkBtE,IAAgB,KAAK,SAAS;AAC5E,aAASyB,IAAI,KAAK,iBAAiBA,IAAI6C,GAAO7C;AAC5C,MAAK,KAAK,QAAQ,IAAIA,CAAC,KAAGH,EAAK,KAAK,KAAK,eAAeG,CAAC,CAAC;AAE5D,WAAOH;AAAA,EACT;AAAA,EAEA,gBAAgBlC,GAAcC,GAAsB;AAClD,UAAMiC,IAAiB,CAAA;AACvB,eAAWiD,KAAO,KAAK;AACrB,MAAIA,EAAI,YAAYlF,KAAMkF,EAAI,UAAUnF,KAAMkC,EAAK,KAAKiD,CAAG;AAE7D,WAAOjD;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,iBAAuB;AAC7B,QACE,KAAK,wBAAwB,KAAK,mBAClC,KAAK,UAAU,KAAK,kBACpB,KAAK,SAAS,KAAK;AAEnB;AAEF,SAAK,sBAAsB,KAAK;AAIhC,QAAIkD,IAAQ,KAAK,iBACb1Q,IAAQ0Q,IAAQ,IAAI,KAAK,aAAaA,CAAK,IAAI,GAI/CC,IAAgB,OAAO;AAC3B,WAAOD,IAAQ,KAAK1Q,IAAQ,KAAK;AAC/B,MAAA2Q,IAAgB3Q,GAChB0Q,KACA1Q,IAAQ,KAAK,aAAa0Q,CAAK;AAKjC,UAAMlD,IACJkD,IAAQ,IAAI,KAAK,aAAaA,GAAO,KAAK,IAAIA,IAAQ,KAAK,WAAW,KAAK,KAAK,CAAC,IAAI,CAAA;AACvF,SAAK,aAAalD,GAClB,KAAK,sBAAsBxN,GAG3B,KAAK,iBAAiB0Q,IAAQ,IAAI1Q,IAAQ,GAC1C,KAAK,iBAAiB2Q;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,aAAaD,GAAuB;AAC1C,UAAME,IAAO,KAAK,YAAY,IAAIF,CAAK;AACvC,QAAIE,MAAS,OAAW,QAAOA;AAC/B,UAAMC,IAAgB,KAAK,IAAIH,IAAQ,KAAK,WAAW,KAAK,KAAK;AACjE,QAAIvI,IAAQ;AACZ,aAASwF,IAAI,GAAGA,IAAI+C,GAAO/C,KAAK;AAC9B,YAAMf,IAAO,KAAK,QAAQ,IAAIe,CAAC;AAC/B,UAAI,CAACf,EAAM;AACX,YAAM5M,IAAQ,KAAK,YAAY2N,CAAC;AAChC,eAASxM,IAAI,GAAGA,IAAInB,GAAOmB;AACzB,SAAIyL,EAAK,WAAWzL,CAAC,MAAM,KAAMyL,EAAK,WAAWzL,CAAC,KAAgB0P,MAAe1I;AAAA,IAErF;AACA,gBAAK,YAAY,IAAIuI,GAAOvI,CAAK,GAC1BA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,aAAauI,GAAeG,GAAiC;AACnE,UAAMrD,IAAiB,CAAA;AACvB,aAASG,IAAI,GAAGA,IAAI+C,GAAO/C,KAAK;AAC9B,YAAMf,IAAO,KAAK,QAAQ,IAAIe,CAAC;AAC/B,UAAI,CAACf,EAAM;AACX,YAAM5M,IAAQ,KAAK,YAAY2N,CAAC,GAC1BtD,IAAOsD,IAAI,KAAK,WAChBmD,IAAQ,KAAK,YAAYnD;AAC/B,UAAIzJ,IAAQ;AACZ,eAAS/C,IAAI,GAAGA,IAAInB,GAAOmB;AAEzB,QADcyL,EAAK,WAAWzL,CAAC,MAAM,KAAMyL,EAAK,WAAWzL,CAAC,KAAgB0P,IAEtE3M,IAAQ,MAAGA,IAAQ/C,KACd+C,KAAS,MAClBsJ,EAAK,KAAKuD,EAAQpD,GAAGmD,GAAO5M,GAAO/C,GAAGkJ,CAAI,CAAC,GAC3CnG,IAAQ;AAGZ,MAAIA,KAAS,KAAGsJ,EAAK,KAAKuD,EAAQpD,GAAGmD,GAAO5M,GAAOlE,GAAOqK,CAAI,CAAC;AAAA,IACjE;AACA,WAAOmD;AAAA,EACT;AAAA,EAEQ,eAAerJ,GAAuB;AAC5C,UAAMnE,IAAQ,KAAK,YAAYmE,CAAK;AACpC,WAAO4M,EAAQ5M,GAAO,KAAK,YAAYA,GAAO,GAAGnE,GAAOmE,IAAQ,KAAK,SAAS;AAAA,EAChF;AACF;AAGA,SAAS4M,EAAQrE,GAAcoE,GAAeE,GAAYC,GAAY5G,GAAsB;AAC1F,SAAO,EAAE,MAAAqC,GAAM,OAAAoE,GAAO,QAAQE,GAAI,OAAOC,IAAKD,GAAI,WAAW3G,IAAO2G,GAAI,SAAS3G,IAAO4G,EAAA;AAC1F;AAEA,SAASZ,GAAY7C,GAAiC;AACpD,MAAIrF,IAAQ;AACZ,aAAWsI,KAAOjD,EAAM,CAAArF,KAASsI,EAAI;AACrC,SAAOtI;AACT;AAGA,eAAeqG,GACbE,GACAL,GACiD;AAIjD,MAAI7K,IAAS,MAAM4L,EAAWV,GAAK,GAAG,QAAOL,GAAS,EAAI;AAC1D,QAAMvM,IAAO,IAAI,SAAS0B,CAAM;AAChC,MAAI1B,EAAK,cAAc,GAAG;AACxB,UAAMe,IAASf,EAAK,UAAU,GAAG,EAAI;AACrC,IAAI,IAAIe,IAASW,EAAO,eACtBA,IAAS,MAAM4L,EAAWV,GAAK,GAAG,IAAI7L,GAAQwL,CAAO;AAAA,EAEzD;AACA,SAAOvK,GAAmBN,CAAM;AAClC;AAOA,eAAe4L,EACbV,GACAxK,GACArB,GACAwL,GACA6C,IAAa,IACS;AACtB,QAAMC,IAAOC,EAAc/C,CAAO,GAC5BgD,IAAW,MAAM,MAAM3C,GAAK;AAAA,IAChC,GAAGyC;AAAA,IACH,SAAS,EAAE,IAAI9C,KAAA,gBAAAA,EAAS,YAAW,CAAA,GAAK,OAAO,SAASnK,CAAK,IAAIA,IAAQrB,IAAS,CAAC,GAAA;AAAA,EAAG,CACvF;AACD,MAAI,CAACwO,EAAS;AACZ,UAAM,IAAI,MAAM,kBAAkB3C,CAAG,UAAU2C,EAAS,MAAM,EAAE;AAMlE,MAAIA,EAAS,WAAW,OAAO,EAAEH,KAAcG,EAAS,WAAW;AACjE,UAAM,IAAI;AAAA,MACR,GAAG3C,CAAG,kCAAkC2C,EAAS,MAAM;AAAA,IAAA;AAI3D,QAAM7N,IAAS,MAAM6N,EAAS,YAAA;AAC9B,MAAI,CAACH,KAAc1N,EAAO,eAAeX;AACvC,UAAM,IAAI,MAAM,GAAG6L,CAAG,aAAalL,EAAO,UAAU,gBAAgBX,CAAM,cAAc;AAE1F,SAAOW;AACT;AAIA,eAAe6L,GAAWX,GAAaL,GAAqD;AAC1F,QAAMgD,IAAW,MAAM,MAAM3C,GAAK0C,EAAc/C,CAAO,CAAC;AACxD,MAAI,CAACgD,EAAS,GAAI,OAAM,IAAI,MAAM,kBAAkB3C,CAAG,UAAU2C,EAAS,MAAM,EAAE;AAClF,SAAOA,EAAS,YAAA;AAClB;"}
1
+ {"version":3,"file":"rad.js","sources":["../../src/lib/formats/rad/rad-column-decoders.ts","../../src/lib/formats/rad/parse-rad.ts","../../src/lib/formats/rad/rad-parent-sizes.ts","../../src/lib/formats/rad/rad-foveated-source.ts","../../src/lib/formats/rad/rad.ts"],"sourcesContent":["/** Typed-array column decoders used by the RAD container parser. */\nimport { halfToFloat } from '../../half-float';\nimport type { RadChunkProperty } from './parse-rad';\n\nfunction shEncodingBytes(prop: RadChunkProperty): number {\n if (prop.encoding === 'f32') return 4;\n if (prop.encoding === 'f16') return 2;\n return 1;\n}\n\nfunction requireRange(prop: RadChunkProperty): { min: number; max: number } {\n if (prop.min === undefined || prop.max === undefined) {\n throw new Error(`RAD \"${prop.property}\" (${prop.encoding}) is missing its min/max range.`);\n }\n return { min: prop.min, max: prop.max };\n}\n\nfunction requireMax(prop: RadChunkProperty): number {\n if (prop.max === undefined) {\n throw new Error(`RAD \"${prop.property}\" (${prop.encoding}) is missing its max range.`);\n }\n return prop.max;\n}\n\n/**\n * Asserts a (possibly decompressed) column physically holds one value per\n * splat per dimension before its `count`-sized output is allocated - the\n * cross-check between the untrusted chunk `count` and the actual bytes.\n */\nexport function assertColumnHoldsCount(\n data: Uint8Array,\n prop: RadChunkProperty,\n dims: number,\n count: number,\n bytesPerValue: number,\n): void {\n if (data.byteLength < count * dims * bytesPerValue) {\n throw new Error(\n `RAD \"${prop.property}\" column holds ${data.byteLength} bytes, fewer than the ` +\n `${count * dims * bytesPerValue} its declared ${count} splats need.`,\n );\n }\n}\n\nexport function decodeFloatColumn(\n data: Uint8Array,\n prop: RadChunkProperty,\n dims: number,\n count: number,\n): Float32Array {\n assertColumnHoldsCount(data, prop, dims, count, shEncodingBytes(prop));\n switch (prop.encoding) {\n case 'f32':\n return decodeF32(data, dims, count);\n case 'f16':\n return decodeF16(data, dims, count);\n case 'f32_lebytes':\n return decodeF32LeBytes(data, dims, count);\n case 'f16_lebytes':\n return decodeF16LeBytes(data, dims, count);\n case 'r8': {\n const { min, max } = requireRange(prop);\n return decodeR8(data, dims, count, min, max);\n }\n case 'r8_delta': {\n const { min, max } = requireRange(prop);\n return decodeR8Delta(data, dims, count, min, max);\n }\n case 's8':\n return decodeS8(data, dims, count, requireMax(prop));\n case 's8_delta':\n return decodeS8Delta(data, dims, count, requireMax(prop));\n case 'ln_0r8': {\n const { min, max } = requireRange(prop);\n return decodeLn0R8(data, dims, count, min, max);\n }\n case 'ln_f16':\n return decodeLnF16(data, dims, count);\n default:\n throw new Error(`Unsupported RAD \"${prop.property}\" encoding \"${prop.encoding}\".`);\n }\n}\n\nexport function decodeOrientation(\n data: Uint8Array,\n prop: RadChunkProperty,\n count: number,\n): Float32Array {\n if (prop.encoding === 'oct88r8') {\n assertColumnHoldsCount(data, prop, 3, count, 1);\n return decodeQuatOct88R8(data, count);\n }\n if (prop.encoding === 'f32' || prop.encoding === 'f16') {\n assertColumnHoldsCount(data, prop, 3, count, prop.encoding === 'f32' ? 4 : 2);\n }\n const xyz =\n prop.encoding === 'f32'\n ? decodeF32(data, 3, count)\n : prop.encoding === 'f16'\n ? decodeF16(data, 3, count)\n : undefined;\n if (!xyz) throw new Error(`Unsupported RAD orientation encoding \"${prop.encoding}\".`);\n const out = new Float32Array(count * 4);\n for (let i = 0; i < count; i++) {\n const x = xyz[i * 3 + 0] as number;\n const y = xyz[i * 3 + 1] as number;\n const z = xyz[i * 3 + 2] as number;\n out[i * 4 + 0] = x;\n out[i * 4 + 1] = y;\n out[i * 4 + 2] = z;\n out[i * 4 + 3] = Math.sqrt(Math.max(0, 1 - x * x - y * y - z * z));\n }\n return out;\n}\n\nexport function decodeChildCount(\n data: Uint8Array,\n prop: RadChunkProperty,\n count: number,\n): Uint16Array {\n if (prop.encoding !== 'u16') {\n throw new Error(`Unsupported RAD child_count encoding \"${prop.encoding}\".`);\n }\n assertColumnHoldsCount(data, prop, 1, count, 2);\n return decodeU16(data, count);\n}\n\nexport function decodeChildStart(\n data: Uint8Array,\n prop: RadChunkProperty,\n count: number,\n): Uint32Array {\n if (prop.encoding !== 'u32') {\n throw new Error(`Unsupported RAD child_start encoding \"${prop.encoding}\".`);\n }\n assertColumnHoldsCount(data, prop, 1, count, 4);\n return decodeU32(data, count);\n}\n\nconst scratch = new DataView(new ArrayBuffer(4));\n\nfunction bitsToFloat32(bits: number): number {\n scratch.setUint32(0, bits, true);\n return scratch.getFloat32(0, true);\n}\n\n/** Planar (column-per-dimension) little-endian f32, splat-major output. */\nfunction decodeF32(data: Uint8Array, dims: number, count: number): Float32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i * 4;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = view.getFloat32(index, true);\n index += count * 4;\n }\n }\n return out;\n}\n\nfunction decodeF16(data: Uint8Array, dims: number, count: number): Float32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i * 2;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = halfToFloat(view.getUint16(index, true));\n index += count * 2;\n }\n }\n return out;\n}\n\n/** Byte-plane-transposed f32: byte `b` of every value's LE encoding is\n * grouped together (compresses better when values are similar). */\nfunction decodeF32LeBytes(data: Uint8Array, dims: number, count: number): Float32Array {\n const stride = count * dims;\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n for (let d = 0; d < dims; d++) {\n const index = count * d + i;\n const bits =\n ((data[index] as number) |\n ((data[index + stride] as number) << 8) |\n ((data[index + stride * 2] as number) << 16) |\n ((data[index + stride * 3] as number) << 24)) >>>\n 0;\n out[i * dims + d] = bitsToFloat32(bits);\n }\n }\n return out;\n}\n\nfunction decodeF16LeBytes(data: Uint8Array, dims: number, count: number): Float32Array {\n const stride = count * dims;\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n for (let d = 0; d < dims; d++) {\n const index = count * d + i;\n const bits = (data[index] as number) | ((data[index + stride] as number) << 8);\n out[i * dims + d] = halfToFloat(bits);\n }\n }\n return out;\n}\n\n/** Planar unsigned-byte, linearly dequantized across `[min, max]`. */\nfunction decodeR8(\n data: Uint8Array,\n dims: number,\n count: number,\n min: number,\n max: number,\n): Float32Array {\n const out = new Float32Array(count * dims);\n const range = max - min;\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = ((data[index] as number) / 255) * range + min;\n index += count;\n }\n }\n return out;\n}\n\n/** Like {@link decodeR8}, but each column is a running byte-wise delta. */\nfunction decodeR8Delta(\n data: Uint8Array,\n dims: number,\n count: number,\n min: number,\n max: number,\n): Float32Array {\n const out = new Float32Array(count * dims);\n const range = max - min;\n const last = new Uint8Array(dims);\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const value = ((last[d] as number) + (data[index] as number)) & 0xff;\n last[d] = value;\n out[i * dims + d] = (value / 255) * range + min;\n index += count;\n }\n }\n return out;\n}\n\n/** Planar signed-byte, scaled by `max / 127`. */\nfunction decodeS8(data: Uint8Array, dims: number, count: number, max: number): Float32Array {\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const byte = data[index] as number;\n const signed = byte >= 128 ? byte - 256 : byte;\n out[i * dims + d] = (signed / 127) * max;\n index += count;\n }\n }\n return out;\n}\n\nfunction decodeS8Delta(data: Uint8Array, dims: number, count: number, max: number): Float32Array {\n const out = new Float32Array(count * dims);\n const last = new Uint8Array(dims);\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const value = ((last[d] as number) + (data[index] as number)) & 0xff;\n last[d] = value;\n const signed = value >= 128 ? value - 256 : value;\n out[i * dims + d] = (signed / 127) * max;\n index += count;\n }\n }\n return out;\n}\n\n/** Log-space scale: byte 0 means \"zero scale\", bytes 1-255 map linearly\n * across `[min, max]` in natural-log space before exponentiating back. */\nfunction decodeLn0R8(\n data: Uint8Array,\n dims: number,\n count: number,\n min: number,\n max: number,\n): Float32Array {\n const out = new Float32Array(count * dims);\n const step = (max - min) / 254;\n for (let i = 0; i < count; i++) {\n let index = i;\n for (let d = 0; d < dims; d++) {\n const byte = data[index] as number;\n out[i * dims + d] = byte === 0 ? 0 : Math.exp(min + (byte - 1) * step);\n index += count;\n }\n }\n return out;\n}\n\nfunction decodeLnF16(data: Uint8Array, dims: number, count: number): Float32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Float32Array(count * dims);\n for (let i = 0; i < count; i++) {\n let index = i * 2;\n for (let d = 0; d < dims; d++) {\n out[i * dims + d] = Math.exp(halfToFloat(view.getUint16(index, true)));\n index += count * 2;\n }\n }\n return out;\n}\n\n/** Octahedral-mapped quaternion: 2 bytes for the rotation axis, 1 for the\n * half-angle. Splat-major output, 4 floats (x, y, z, w) per splat. */\nfunction decodeQuatOct88R8(data: Uint8Array, count: number): Float32Array {\n const out = new Float32Array(count * 4);\n for (let i = 0; i < count; i++) {\n const index = i * 3;\n const u = data[index] as number;\n const v = data[index + 1] as number;\n const r = data[index + 2] as number;\n let x = (u / 255) * 2 - 1;\n let y = (v / 255) * 2 - 1;\n const z = 1 - Math.abs(x) - Math.abs(y);\n const t = Math.max(-z, 0);\n x = x >= 0 ? x - t : x + t;\n y = y >= 0 ? y - t : y + t;\n const length = Math.hypot(x, y, z) || 1;\n const halfTheta = (r / 255) * 0.5 * Math.PI;\n const s = Math.sin(halfTheta);\n const w = Math.cos(halfTheta);\n out[i * 4 + 0] = (x / length) * s;\n out[i * 4 + 1] = (y / length) * s;\n out[i * 4 + 2] = (z / length) * s;\n out[i * 4 + 3] = w;\n }\n return out;\n}\n\n/** Planar little-endian u16. */\nexport function decodeU16(data: Uint8Array, count: number): Uint16Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Uint16Array(count);\n for (let i = 0; i < count; i++) out[i] = view.getUint16(i * 2, true);\n return out;\n}\n\n/** Planar little-endian u32. */\nexport function decodeU32(data: Uint8Array, count: number): Uint32Array {\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n const out = new Uint32Array(count);\n for (let i = 0; i < count; i++) out[i] = view.getUint32(i * 4, true);\n return out;\n}\n","import {\n assertColumnHoldsCount,\n decodeU16,\n decodeU32,\n decodeChildCount,\n decodeChildStart,\n decodeFloatColumn,\n decodeOrientation,\n} from './rad-column-decoders';\nimport {\n writeCovariance,\n type RadShCodebook,\n type SplatData,\n type SplatPackedShData,\n} from '../../splat-data';\nimport { neutralShWord, packShCoefficients, shCoefficientCount } from '../../sh-pack';\n\n/**\n * Parser for World Labs' Spark `.rad`/`.radc` format (a precomputed LOD splat\n * tree with per-column, gzip/deflate-compressed properties). Ported from the\n * MIT-licensed Rust reference (`sparkjsdev/spark`, `rust/spark-lib/src/rad.rs`\n * and `splat_encode.rs`) since this project's worker has no Rust/WASM\n * toolchain - Spark's own decoder runs as WASM and writes into Spark's own\n * GPU texture layout, not this package's `SplatData`.\n *\n * Two entry points:\n * - {@link parseRad} flattens a whole file into leaf splats only (the original\n * capture), for a small `.rad` loaded in one piece by `loadSplatData`.\n * - {@link parseRadChunkStreaming} decodes one chunk keeping *every* splat -\n * the merged coarse LOD-tree nodes included - plus the per-splat tree\n * columns, for the streamed reader (see `rad.ts`), which chooses the\n * rendered cut across chunks at runtime.\n *\n * SH columns are repacked into the renderer's per-splat packed representation.\n */\n\nexport const RAD_MAGIC = 0x30444152; // 'RAD0' (LE)\nexport const RAD_CHUNK_MAGIC = 0x43444152; // 'RADC' (LE)\n\n/**\n * Upper bound on an untrusted splat count that will size **one** buffer - a\n * whole-file decode or a single chunk. Mirrors `parse-sog.ts` MAX_SPLAT_COUNT:\n * the renderer cannot draw more than 2²⁴ splats from one pool texture anyway\n * (2048 wide × 8192 max height), so a hostile count is rejected rather than\n * ballooning into multi-gigabyte allocations.\n *\n * It deliberately does **not** bound a `.rad` header's `count`, which is the\n * whole LOD-tree node total of a capture meant to stream (53M nodes for a 37M-\n * leaf scene) and sizes nothing - see {@link parseRadHeaderMeta}.\n */\nconst MAX_RAD_SPLAT_COUNT = 1 << 24;\n\n/** Asserts an untrusted RAD splat count is a safe non-negative integer. */\nfunction assertRadCountInteger(count: number, what: string): void {\n if (!Number.isSafeInteger(count) || count < 0) {\n throw new Error(`Invalid RAD ${what} splat count: ${count}.`);\n }\n}\n\n/** Asserts a count that will size one buffer stays under the pool ceiling. */\nfunction assertSaneRadCount(count: number, what: string): void {\n assertRadCountInteger(count, what);\n if (count > MAX_RAD_SPLAT_COUNT) {\n throw new Error(\n `RAD ${what} declares ${count} splats, above the supported maximum of ${MAX_RAD_SPLAT_COUNT}.`,\n );\n }\n}\n\n/** One chunk's location within a `.rad` file (or its external `.radc` name). */\nexport interface RadChunkRange {\n readonly offset: number;\n readonly bytes: number;\n readonly filename?: string;\n}\n\n/** The file-level `RAD0` metadata (a subset - only the fields we consume). */\nexport interface RadMeta {\n readonly version: number;\n readonly type: string;\n /** Total splats in the LOD tree (merged nodes + leaves). */\n readonly count: number;\n readonly maxSh?: number;\n readonly lodTree?: boolean;\n readonly chunkSize?: number;\n readonly chunks: readonly RadChunkRange[];\n /** Spark's free-form build-info JSON; carries `input_splat_count` (the leaf\n * count) among other fields. Parsed best-effort by the reader. */\n readonly comment?: string;\n}\n\n/** Per-splat LOD-tree links: an internal node's children are the contiguous\n * global-index range `[childStart, childStart + childCount)`. Leaves have\n * `childCount === 0`. */\nexport interface RadTree {\n readonly childCount: Uint16Array;\n readonly childStart: Uint32Array;\n}\n\nexport interface RadChunkProperty {\n readonly offset: number;\n readonly bytes: number;\n readonly property: string;\n readonly encoding: string;\n readonly compression?: string;\n readonly min?: number;\n readonly max?: number;\n}\n\ninterface RadChunkMeta {\n readonly version: number;\n readonly count: number;\n readonly lodTree?: boolean;\n readonly properties: readonly RadChunkProperty[];\n}\n\ninterface DecodedChunk {\n readonly count: number;\n readonly lodTree: boolean;\n /** xyz, splat-major. */\n readonly center: Float32Array;\n readonly alpha: Float32Array;\n readonly rgb: Float32Array;\n readonly scale: Float32Array;\n /** xyzw, splat-major. */\n readonly quat: Float32Array;\n readonly childCount?: Uint16Array;\n readonly childStart?: Uint32Array;\n readonly shPacked?: SplatPackedShData;\n readonly shCodebook?: RadShCodebook;\n}\n\n/**\n * Decodes a `.rad` file, or a lone `.radc` chunk, into a flat `SplatData`\n * of leaf splats only (the original capture). Merged coarse LOD-tree nodes\n * are dropped - they exist for streamed reading, and are meaningless once\n * everything is already decoded into one buffer.\n *\n * @throws {Error} if the file is malformed, an unsupported version, carries\n * an external `.radc` chunk reference (one-shot loading needs every chunk in\n * the same buffer), or uses a property encoding this parser does not\n * recognize.\n */\nexport async function parseRad(buffer: ArrayBuffer): Promise<SplatData> {\n const bytes = new Uint8Array(buffer);\n if (bytes.byteLength < 8) throw new Error('Truncated RAD file.');\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const magic = view.getUint32(0, true);\n\n if (magic === RAD_CHUNK_MAGIC) {\n return mergeChunks([await decodeChunk(bytes, 0, true)]);\n }\n const { meta, chunksStart } = parseRadHeaderMeta(buffer);\n // One-shot folds the whole file into a single `SplatData` (one pool texture),\n // so the pool ceiling binds here - and only here. The streamed reader keeps a\n // budget-bounded resident set and has no such limit (see `buildRadScene`).\n if (meta.count > MAX_RAD_SPLAT_COUNT) {\n throw new Error(\n `RAD header declares ${meta.count} splats, above the supported maximum of ` +\n `${MAX_RAD_SPLAT_COUNT} for a one-shot decode; load this file as a streamed scene instead.`,\n );\n }\n const chunks: DecodedChunk[] = [];\n let shCodebook: RadShCodebook | undefined;\n let shExtent: number | undefined;\n for (const [index, range] of meta.chunks.entries()) {\n if (range.filename !== undefined) {\n throw new Error(\n `RAD chunk ${index} is an external .radc file (\"${range.filename}\"); one-shot ` +\n 'loading only supports a single self-contained .rad file.',\n );\n }\n const start = chunksStart + range.offset;\n if (start + range.bytes > bytes.byteLength) {\n throw new Error(`RAD chunk ${index} extends past the file.`);\n }\n const chunk = await decodeChunk(bytes, start, true, shCodebook, shExtent);\n shCodebook ??= chunk.shCodebook;\n // Later chunks pack against the first SH chunk's range, so every chunk's\n // packed words share one scene range (see `decodeRadSh`).\n shExtent ??= chunk.shPacked?.range.max[0];\n chunks.push(chunk);\n }\n return mergeChunks(chunks);\n}\n\n/**\n * Parses just the `RAD0` header: the typed metadata and the byte offset where\n * the chunk stream begins. `buffer` need only be long enough to hold the\n * header - a ranged prefix of a large single-file `.rad` is enough.\n */\nexport function parseRadHeaderMeta(buffer: ArrayBuffer): { meta: RadMeta; chunksStart: number } {\n const bytes = new Uint8Array(buffer);\n if (bytes.byteLength < 8) throw new Error('Truncated RAD file.');\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const magic = view.getUint32(0, true);\n if (magic !== RAD_MAGIC) {\n throw new Error(`Not a RAD file (magic 0x${magic.toString(16).padStart(8, '0')}).`);\n }\n const length = view.getUint32(4, true);\n if (bytes.byteLength < 8 + length) throw new Error('Truncated RAD header.');\n const meta = JSON.parse(decodeUtf8(bytes, 8, length)) as RadMeta;\n if (meta.version !== 1) throw new Error(`Unsupported RAD version ${meta.version}.`);\n if (meta.type !== 'gsplat') throw new Error(`Unsupported RAD type \"${meta.type}\".`);\n assertRadCountInteger(meta.count, 'header');\n if (!Array.isArray(meta.chunks)) throw new Error('RAD header is missing its chunk list.');\n // `Array.isArray` narrows a `readonly T[]` to `any[]`; restore the declared\n // element type before touching members.\n const chunkRanges: readonly RadChunkRange[] = meta.chunks;\n for (const [index, range] of chunkRanges.entries()) {\n if (\n !Number.isSafeInteger(range?.offset) ||\n range.offset < 0 ||\n !Number.isSafeInteger(range?.bytes) ||\n range.bytes < 0\n ) {\n throw new Error(`RAD chunk ${index} declares an invalid byte range.`);\n }\n }\n // `count` is the whole LOD-tree node total and sizes nothing here (every\n // buffer downstream is per-chunk), so the pool ceiling would wrongly reject a\n // large streamed capture. Bound it structurally instead: no more splats than\n // the declared chunk table can physically hold.\n if (meta.chunkSize !== undefined) {\n if (\n !Number.isSafeInteger(meta.chunkSize) ||\n meta.chunkSize <= 0 ||\n meta.chunkSize > MAX_RAD_SPLAT_COUNT\n ) {\n throw new Error(`Invalid RAD chunk size: ${meta.chunkSize}.`);\n }\n if (meta.count > meta.chunkSize * chunkRanges.length) {\n throw new Error(\n `RAD header declares ${meta.count} splats, more than its ${chunkRanges.length} chunks of ` +\n `${meta.chunkSize} can hold.`,\n );\n }\n }\n return { meta, chunksStart: 8 + roundup8(length) };\n}\n\n/**\n * Decodes one chunk (a `RADC`-magic byte range) into a `SplatData` holding\n * **every** splat in the chunk - internal LOD-tree nodes and leaves alike -\n * plus the per-splat tree columns when the file carries a LOD tree. The\n * streamed reader keeps all splats resident and chooses the rendered cut\n * itself from {@link SplatData.radTree}.\n *\n * @throws {Error} on a malformed chunk or an unrecognized property encoding.\n */\nexport async function parseRadChunkStreaming(\n buffer: ArrayBuffer,\n shCodebook?: RadShCodebook,\n shExtent?: number,\n reorder = true,\n): Promise<SplatData> {\n const chunk = await decodeChunk(new Uint8Array(buffer), 0, true, shCodebook, shExtent);\n return chunkToSplatData(chunk, reorder);\n}\n\n/**\n * Builds a `SplatData` for every splat of a decoded chunk.\n *\n * For a LOD chunk the splats are reordered by descending \"frontier key\" -\n * leaves first, then internal nodes by `child_start` descending. The streamed\n * reader draws a splat when it is a leaf or its children are beyond the\n * resident prefix (`child_start ≥ residentCount`); with this order the drawn\n * splats of any chunk are always a contiguous **prefix**, so the frontier\n * coalesces into one run per chunk instead of thousands of fragments. The\n * reorder is safe because it never moves a splat's children out of their\n * chunk, and the reader only threshold-tests `child_start`, never follows it.\n */\nfunction chunkToSplatData(chunk: DecodedChunk, reorder: boolean): SplatData {\n const { count } = chunk;\n const positions = new Float32Array(count * 3);\n const colors = new Uint8Array(count * 4);\n const covariances = new Float32Array(count * 6);\n\n if (!chunk.childCount || !chunk.childStart) {\n for (let i = 0; i < count; i++) writeSplat(chunk, i, i, positions, colors, covariances);\n return {\n count,\n positions,\n colors,\n covariances,\n ...(chunk.shPacked ? { shPacked: chunk.shPacked } : {}),\n ...(chunk.shCodebook ? { radShCodebook: chunk.shCodebook } : {}),\n };\n }\n\n const childCount = chunk.childCount;\n const childStart = chunk.childStart;\n // The prefix reader needs leaves-first ordering to coalesce its frontier into\n // one run per chunk; the foveation reader keeps file order so `child_start`\n // stays valid for parent-size propagation (it renders whole chunks and lets\n // the GPU cull pick the cut per splat).\n const order = reorder ? frontierOrder(childCount, childStart, count) : identityOrder(count);\n const reorderedSh = reorderPackedSh(chunk.shPacked, order);\n const outCount = new Uint16Array(count);\n const outStart = new Uint32Array(count);\n const outSize = new Float32Array(count);\n for (let j = 0; j < count; j++) {\n const src = order[j] as number;\n writeSplat(chunk, src, j, positions, colors, covariances);\n outCount[j] = childCount[src] as number;\n outStart[j] = childStart[src] as number;\n outSize[j] = radNodeSize(chunk.alpha[src] as number, chunk.scale, src);\n }\n return {\n count,\n positions,\n colors,\n covariances,\n radTree: { childCount: outCount, childStart: outStart, size: outSize },\n ...(reorderedSh ? { shPacked: reorderedSh } : {}),\n ...(chunk.shCodebook ? { radShCodebook: chunk.shCodebook } : {}),\n };\n}\n\n/**\n * Spark's merged-node size-expansion factor: 1 for leaves (`alpha ≤ 1`), up to\n * 3.8 at `alpha = 2`. A merged node's `alpha ∈ (1,2]` encodes coverage\n * expansion - how much to enlarge the node so it covers its subtree - not\n * opacity. Spark's renderer applies it to the rendered gaussian; so do we (in\n * `writeSplat`). The LOD cut uses a steeper authored size - see\n * {@link radNodeSize}.\n */\nfunction radExpansion(alpha: number): number {\n return alpha <= 1 ? 1 : 1 + 0.7 * (alpha * 4 - 4);\n}\n\n/**\n * A LOD node's world-space size, matching Spark's `encode_lod_tree`\n * (`splat_encode.rs`): `2 · (max(alpha,1)·4 − 3) · avg(scale)`. This is the\n * size Spark's traversal compares against `pixel_scale_limit`, and it is\n * **steeper** than the rendered covariance expansion ({@link radExpansion}'s\n * `1 + 0.7·(4α−4)`, ≤3.8): at `alpha = 2` the traversal size factor is 5.\n * Using the render curve here made every merged node look 24–32% smaller to\n * the cut than to Spark's, so the descent stopped ~one LOD level early -\n * large-scale scenes rendered visibly coarser (streaky merged nodes) than\n * Spark at the same limit and budget.\n */\nfunction radNodeSize(alpha: number, scale: Float32Array, i: number): number {\n const avg =\n ((scale[i * 3] as number) + (scale[i * 3 + 1] as number) + (scale[i * 3 + 2] as number)) / 3;\n return 2 * (Math.max(alpha, 1) * 4 - 3) * avg;\n}\n\n/** The trivial permutation - splats stay in file order (foveation path). */\nfunction identityOrder(count: number): Uint32Array {\n const order = new Uint32Array(count);\n for (let i = 0; i < count; i++) order[i] = i;\n return order;\n}\n\n/** Splat indices sorted by descending frontier key (leaves before internal\n * nodes; internal nodes by `child_start` descending). */\nfunction frontierOrder(\n childCount: Uint16Array,\n childStart: Uint32Array,\n count: number,\n): Uint32Array {\n const order = new Uint32Array(count);\n for (let i = 0; i < count; i++) order[i] = i;\n // Leaves sort ahead of every internal node; among internal nodes, the ones\n // whose children lie deepest (largest child_start) survive in the frontier\n // longest, so they come first.\n const key = (i: number): number =>\n childCount[i] === 0 ? Number.MAX_SAFE_INTEGER : (childStart[i] as number);\n return order.sort((a, b) => key(b) - key(a));\n}\n\n/** Decodes one `RADC`-magic chunk starting at `chunkStart` within `bytes`. */\nasync function decodeChunk(\n bytes: Uint8Array,\n chunkStart: number,\n withTree: boolean,\n knownShCodebook?: RadShCodebook,\n knownShExtent?: number,\n): Promise<DecodedChunk> {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const magic = view.getUint32(chunkStart, true);\n if (magic !== RAD_CHUNK_MAGIC) {\n throw new Error(`Not a RAD chunk (magic 0x${magic.toString(16).padStart(8, '0')}).`);\n }\n const length = view.getUint32(chunkStart + 4, true);\n const metaEnd = chunkStart + 8 + roundup8(length);\n const chunkMeta = JSON.parse(decodeUtf8(bytes, chunkStart + 8, length)) as RadChunkMeta;\n if (chunkMeta.version !== 1) {\n throw new Error(`Unsupported RAD chunk version ${chunkMeta.version}.`);\n }\n const payloadBytes = Number(view.getBigUint64(metaEnd, true));\n const payloadStart = metaEnd + 8;\n if (payloadStart + payloadBytes > bytes.byteLength) {\n throw new Error('RAD chunk payload extends past the file.');\n }\n const { count } = chunkMeta;\n assertSaneRadCount(count, 'chunk');\n const lodTree = chunkMeta.lodTree ?? false;\n\n if (!Array.isArray(chunkMeta.properties)) {\n throw new Error('RAD chunk metadata is missing its property list.');\n }\n // `Array.isArray` narrows a `readonly T[]` to `any[]`; restore the declared\n // element type before touching members.\n const properties: readonly RadChunkProperty[] = chunkMeta.properties;\n const byName = new Map(properties.map((prop) => [prop.property, prop]));\n const readProp = async (name: string): Promise<Uint8Array | undefined> => {\n const prop = byName.get(name);\n if (!prop) return undefined;\n if (\n !Number.isSafeInteger(prop.offset) ||\n prop.offset < 0 ||\n !Number.isSafeInteger(prop.bytes) ||\n prop.bytes < 0\n ) {\n throw new Error(`RAD property \"${name}\" declares an invalid byte range.`);\n }\n const start = payloadStart + prop.offset;\n if (prop.offset + prop.bytes > payloadBytes || start + prop.bytes > bytes.byteLength) {\n throw new Error(`RAD property \"${name}\" extends past its chunk.`);\n }\n const raw = bytes.subarray(start, start + prop.bytes);\n return prop.compression === undefined ? raw : decompressColumn(raw);\n };\n\n const wantTree = withTree && lodTree;\n const propertyNames: readonly (string | undefined)[] = [\n 'center',\n 'alpha',\n 'rgb',\n 'scales',\n 'orientation',\n ...(wantTree ? ['child_count', 'child_start'] : [undefined, undefined]),\n 'sh1',\n 'sh2',\n 'sh3',\n 'sh_label',\n 'sh1_code',\n 'sh2_code',\n 'sh3_code',\n ];\n const [\n centerRaw,\n alphaRaw,\n rgbRaw,\n scaleRaw,\n orientationRaw,\n childCountRaw,\n childStartRaw,\n sh1Raw,\n sh2Raw,\n sh3Raw,\n shLabelRaw,\n sh1CodeRaw,\n sh2CodeRaw,\n sh3CodeRaw,\n ] = await Promise.all(\n propertyNames.map((name) => (name === undefined ? Promise.resolve(undefined) : readProp(name))),\n );\n if (!centerRaw) throw new Error('RAD chunk is missing its \"center\" property.');\n if (!alphaRaw) throw new Error('RAD chunk is missing its \"alpha\" property.');\n if (!rgbRaw) throw new Error('RAD chunk is missing its \"rgb\" property.');\n if (!scaleRaw) throw new Error('RAD chunk is missing its \"scales\" property.');\n if (!orientationRaw) throw new Error('RAD chunk is missing its \"orientation\" property.');\n\n return {\n count,\n lodTree,\n center: decodeFloatColumn(centerRaw, byName.get('center')!, 3, count),\n alpha: decodeFloatColumn(alphaRaw, byName.get('alpha')!, 1, count),\n rgb: decodeFloatColumn(rgbRaw, byName.get('rgb')!, 3, count),\n scale: decodeFloatColumn(scaleRaw, byName.get('scales')!, 3, count),\n quat: decodeOrientation(orientationRaw, byName.get('orientation')!, count),\n ...(wantTree && childCountRaw\n ? { childCount: decodeChildCount(childCountRaw, byName.get('child_count')!, count) }\n : {}),\n ...(wantTree && childStartRaw\n ? { childStart: decodeChildStart(childStartRaw, byName.get('child_start')!, count) }\n : {}),\n ...decodeRadSh({\n count,\n byName,\n sh1Raw,\n sh2Raw,\n sh3Raw,\n shLabelRaw,\n sh1CodeRaw,\n sh2CodeRaw,\n sh3CodeRaw,\n knownShCodebook,\n knownShExtent,\n }),\n };\n}\n\n/**\n * Flattens decoded chunks into leaf splats only: internal LOD-tree nodes\n * (`childCount > 0`) are merged approximations of their children and are\n * dropped, same as loading only the finest level of any other LOD format.\n */\nfunction mergeChunks(chunks: readonly DecodedChunk[]): SplatData {\n let total = 0;\n for (const chunk of chunks) {\n total += chunk.childCount ? countLeaves(chunk.childCount) : chunk.count;\n }\n if (total === 0) throw new Error('RAD file has no leaf splats.');\n\n const positions = new Float32Array(total * 3);\n const colors = new Uint8Array(total * 4);\n const covariances = new Float32Array(total * 6);\n const sourceSh = chunks.find((chunk) => chunk.shPacked)?.shPacked;\n const packed = sourceSh ? new Uint32Array(total * shCoefficientCount(sourceSh.bands)) : undefined;\n // A splat from an SH-less chunk must encode \"0.0\", not word 0 (which\n // decodes to the range minimum). In practice Spark writes SH columns to\n // every chunk when the scene has SH, so this is belt-and-braces.\n if (packed && sourceSh) packed.fill(neutralShWord(sourceSh.range));\n\n let out = 0;\n for (const chunk of chunks) {\n for (let i = 0; i < chunk.count; i++) {\n if (chunk.childCount && chunk.childCount[i] !== 0) continue; // internal node\n writeSplat(chunk, i, out, positions, colors, covariances);\n if (packed && chunk.shPacked) {\n const words = shCoefficientCount(sourceSh!.bands);\n packed.set(chunk.shPacked.packed.subarray(i * words, (i + 1) * words), out * words);\n }\n out++;\n }\n }\n\n return {\n count: total,\n positions,\n colors,\n covariances,\n ...(sourceSh && packed ? { shPacked: { ...sourceSh, packed } } : {}),\n };\n}\n\nfunction reorderPackedSh(\n sh: SplatPackedShData | undefined,\n order: Uint32Array,\n): SplatPackedShData | undefined {\n if (!sh) return undefined;\n const words = shCoefficientCount(sh.bands);\n const packed = new Uint32Array(sh.packed.length);\n for (let dst = 0; dst < order.length; dst++) {\n const src = order[dst] as number;\n packed.set(sh.packed.subarray(src * words, (src + 1) * words), dst * words);\n }\n return { ...sh, packed };\n}\n\ninterface RadShInputs {\n readonly count: number;\n readonly byName: ReadonlyMap<string, RadChunkProperty>;\n readonly sh1Raw?: Uint8Array;\n readonly sh2Raw?: Uint8Array;\n readonly sh3Raw?: Uint8Array;\n readonly shLabelRaw?: Uint8Array;\n readonly sh1CodeRaw?: Uint8Array;\n readonly sh2CodeRaw?: Uint8Array;\n readonly sh3CodeRaw?: Uint8Array;\n readonly knownShCodebook?: RadShCodebook;\n readonly knownShExtent?: number;\n}\n\n/**\n * Decodes Spark's direct columns or its chunk-zero codebook and labels.\n *\n * The quantization range must be the same for every chunk of a file - the\n * shared pool decodes all chunks against one scene range - so the extent is\n * never measured from a lone chunk's values if a chunk-invariant source\n * exists: the column metadata range (Spark encodes every chunk against\n * scene-global per-band maxima), the codebook (stored once, in chunk zero),\n * or the extent already established by an earlier chunk (`knownShExtent`).\n */\nfunction decodeRadSh(input: RadShInputs): Pick<DecodedChunk, 'shPacked' | 'shCodebook'> {\n const direct = [input.sh1Raw, input.sh2Raw, input.sh3Raw];\n const code = [input.sh1CodeRaw, input.sh2CodeRaw, input.sh3CodeRaw];\n const directBands = direct.reduce((bands, raw, index) => (raw ? index + 1 : bands), 0);\n const codeBands = code.reduce((bands, raw, index) => (raw ? index + 1 : bands), 0);\n if (directBands === 0 && codeBands === 0 && !input.shLabelRaw) return {};\n if (directBands > 0 && codeBands > 0)\n throw new Error('RAD chunk mixes direct and codebook SH columns.');\n\n let coefficients: Float32Array;\n let bands: 1 | 2 | 3;\n let codebook: RadShCodebook | undefined;\n let extent: number | undefined;\n if (directBands > 0) {\n bands = directBands as 1 | 2 | 3;\n coefficients = combineShColumns(direct, input.byName, input.count, bands);\n extent = metadataShExtent(input.byName, bands) ?? input.knownShExtent;\n } else {\n codebook = input.knownShCodebook;\n if (!codebook && codeBands > 0) {\n bands = codeBands as 1 | 2 | 3;\n const codeCount = shColumnCount(\n code[codeBands - 1]!,\n input.byName.get(`sh${codeBands}_code`)!,\n );\n codebook = {\n bands,\n count: codeCount,\n coefficients: combineShColumns(code, input.byName, codeCount, bands, '_code'),\n };\n }\n if (!codebook) throw new Error('RAD SH labels require the codebook stored in chunk zero.');\n if (!input.shLabelRaw)\n throw new Error('RAD SH codebook chunk is missing its \"sh_label\" property.');\n bands = codebook.bands;\n const labels = decodeShLabels(input.shLabelRaw, input.byName.get('sh_label')!, input.count);\n coefficients = new Float32Array(input.count * shCoefficientCount(bands) * 3);\n const words = shCoefficientCount(bands) * 3;\n for (let i = 0; i < input.count; i++) {\n const label = labels[i] as number;\n if (label >= codebook.count)\n throw new Error(`RAD SH label ${label} exceeds its ${codebook.count}-entry codebook.`);\n coefficients.set(\n codebook.coefficients.subarray(label * words, (label + 1) * words),\n i * words,\n );\n }\n // The codebook is whole-scene, so its extent is the same whichever chunk\n // computes it - unlike the extent of one chunk's referenced entries.\n extent = 0;\n for (const value of codebook.coefficients) extent = Math.max(extent, Math.abs(value));\n }\n return {\n shPacked: packShCoefficients(coefficients, input.count, bands, extent),\n ...(codebook ? { shCodebook: codebook } : {}),\n };\n}\n\n/**\n * The scene-global SH extent recorded in the column metadata, when every band\n * is stored quantized. Spark computes one `shN_max` per band over the whole\n * scene and encodes every chunk against it (`rad.rs`), so these ranges are\n * chunk-invariant. Raw-float columns (`f32`/`f16`/`*_lebytes`) carry no range\n * - returns undefined and the caller falls back to a measured extent.\n */\nfunction metadataShExtent(\n byName: ReadonlyMap<string, RadChunkProperty>,\n bands: 1 | 2 | 3,\n suffix = '',\n): number | undefined {\n let extent = 0;\n for (let band = 1; band <= bands; band++) {\n const prop = byName.get(`sh${band}${suffix}`);\n if (!prop) return undefined;\n let bandExtent: number;\n switch (prop.encoding) {\n case 's8':\n case 's8_delta':\n if (prop.max === undefined) return undefined;\n bandExtent = Math.abs(prop.max);\n break;\n case 'r8':\n case 'r8_delta':\n if (prop.min === undefined || prop.max === undefined) return undefined;\n bandExtent = Math.max(Math.abs(prop.min), Math.abs(prop.max));\n break;\n default:\n return undefined;\n }\n extent = Math.max(extent, bandExtent);\n }\n return extent;\n}\n\n/** Interleaves the per-band columns into one splat-major array: splat `i`\n * holds its band-1, band-2, band-3 coefficient triples contiguously, the\n * order `packSh` and the codebook expansion consume. */\nfunction combineShColumns(\n raws: readonly (Uint8Array | undefined)[],\n byName: ReadonlyMap<string, RadChunkProperty>,\n count: number,\n bands: 1 | 2 | 3,\n suffix = '',\n): Float32Array {\n const totalCoefficients = shCoefficientCount(bands);\n const result = new Float32Array(count * totalCoefficients * 3);\n let coefficientOffset = 0;\n for (let band = 1; band <= bands; band++) {\n const raw = raws[band - 1];\n const prop = byName.get(`sh${band}${suffix}`);\n if (!raw || !prop) throw new Error(`RAD SH is missing its \"sh${band}${suffix}\" column.`);\n const dims = band === 1 ? 9 : band === 2 ? 15 : 21;\n const values = decodeFloatColumn(raw, prop, dims, count);\n for (let i = 0; i < count; i++) {\n const base = (i * totalCoefficients + coefficientOffset) * 3;\n for (let k = 0; k < dims; k++) result[base + k] = values[i * dims + k] as number;\n }\n coefficientOffset += dims / 3;\n }\n return result;\n}\n\nfunction shColumnCount(raw: Uint8Array, prop: RadChunkProperty): number {\n const dimensions = prop.property.startsWith('sh1')\n ? 9\n : prop.property.startsWith('sh2')\n ? 15\n : 21;\n const bytesPerValue = shEncodingBytes(prop);\n if (raw.byteLength % (dimensions * bytesPerValue) !== 0)\n throw new Error(`RAD \"${prop.property}\" has an invalid SH column length.`);\n return raw.byteLength / (dimensions * bytesPerValue);\n}\n\n/** Bytes per decoded value of an SH column's encoding. */\nfunction shEncodingBytes(prop: RadChunkProperty): number {\n switch (prop.encoding) {\n case 'f32':\n case 'f32_lebytes':\n return 4;\n case 'f16':\n case 'f16_lebytes':\n case 'ln_f16':\n return 2;\n case 'r8':\n case 'r8_delta':\n case 's8':\n case 's8_delta':\n case 'ln_0r8':\n return 1;\n default:\n throw new Error(`Unsupported RAD \"${prop.property}\" encoding \"${prop.encoding}\".`);\n }\n}\n\nfunction decodeShLabels(raw: Uint8Array, prop: RadChunkProperty, count: number): Uint32Array {\n if (prop.encoding === 'u16') {\n assertColumnHoldsCount(raw, prop, 1, count, 2);\n return Uint32Array.from(decodeU16(raw, count));\n }\n if (prop.encoding === 'u32') {\n assertColumnHoldsCount(raw, prop, 1, count, 4);\n return decodeU32(raw, count);\n }\n throw new Error(`Unsupported RAD sh_label encoding \"${prop.encoding}\".`);\n}\n\n/** Writes splat `src` of a decoded chunk into slot `dst` of the output arrays. */\nfunction writeSplat(\n chunk: DecodedChunk,\n src: number,\n dst: number,\n positions: Float32Array,\n colors: Uint8Array,\n covariances: Float32Array,\n): void {\n // Spark's LOD alpha encoding: store `alpha / 2` in the 8-bit channel so the\n // shader (`×2`) recovers `alpha ∈ [0, 2]`. A leaf's opacity is `≤ 1`; a merged\n // node's `alpha ∈ (1, 2]` is a size tag (not opacity) - the material grows\n // that splat's σ-cutoff and reshapes its falloff to cover its subtree, rather\n // than scaling the covariance (which over-inflates and blurs). The covariance\n // is therefore the raw fitted shape for every splat.\n const alpha = chunk.alpha[src] as number;\n positions[dst * 3 + 0] = chunk.center[src * 3 + 0] as number;\n positions[dst * 3 + 1] = chunk.center[src * 3 + 1] as number;\n positions[dst * 3 + 2] = chunk.center[src * 3 + 2] as number;\n colors[dst * 4 + 0] = clampByte((chunk.rgb[src * 3 + 0] as number) * 255);\n colors[dst * 4 + 1] = clampByte((chunk.rgb[src * 3 + 1] as number) * 255);\n colors[dst * 4 + 2] = clampByte((chunk.rgb[src * 3 + 2] as number) * 255);\n colors[dst * 4 + 3] = clampByte(alpha * 0.5 * 255);\n writeCovariance(\n covariances,\n dst,\n chunk.scale[src * 3 + 0] as number,\n chunk.scale[src * 3 + 1] as number,\n chunk.scale[src * 3 + 2] as number,\n chunk.quat[src * 4 + 3] as number, // w\n chunk.quat[src * 4 + 0] as number, // x\n chunk.quat[src * 4 + 1] as number, // y\n chunk.quat[src * 4 + 2] as number, // z\n );\n}\n\nfunction countLeaves(childCount: Uint16Array): number {\n let leaves = 0;\n for (const value of childCount) if (value === 0) leaves++;\n return leaves;\n}\n\nfunction clampByte(value: number): number {\n return Math.max(0, Math.min(255, Math.round(value)));\n}\n\nfunction roundup8(size: number): number {\n return (size + 7) & ~7;\n}\n\nfunction decodeUtf8(bytes: Uint8Array, offset: number, length: number): string {\n return new TextDecoder().decode(bytes.subarray(offset, offset + length));\n}\n\n/**\n * Spark compresses each property column with `miniz_oxide::deflate`, which\n * produces a raw DEFLATE stream (no zlib/gzip wrapper) despite the format\n * metadata calling it `\"gz\"`. Bytes are sniffed rather than trusted, so a\n * future Spark version that switches to a real gzip/zlib wrapper still decodes.\n */\nasync function decompressColumn(bytes: Uint8Array): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error('This runtime does not provide decompression required for RAD files.');\n }\n const format: CompressionFormat =\n bytes[0] === 0x1f && bytes[1] === 0x8b ? 'gzip' : bytes[0] === 0x78 ? 'deflate' : 'deflate-raw';\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n const stream = new Blob([copy.buffer]).stream().pipeThrough(new DecompressionStream(format));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n}\n","/**\n * Propagates each `.rad` splat's **parent LOD node size** as chunks stream in.\n *\n * The foveation cut (see `docs/formats/rad-notes.md` M14.6) draws a splat `i` when\n * `parentPixelScale(i) > limit ≥ ownPixelScale(i)` - exactly one node per\n * root→leaf path, since `pixel_scale = size / distance` decreases monotonically\n * down the tree. That test needs each splat's *parent's* `size`, but the file\n * only stores the forward links (`child_start`/`child_count`): a node lists its\n * children, not its parent.\n *\n * So we invert the links incrementally. When a chunk decodes, each internal\n * node's `size` is recorded against the global index range of its children\n * (`[child_start, child_start + child_count)`); a later chunk holding those\n * children reads its own parent sizes back out. Because chunks stream roughly\n * coarsest-first (a node's parent is always at a lower global index, and the\n * coarse base loads first), parents are normally decoded before their children.\n * A child whose parent has not decoded yet gets `Infinity` - treated as a root,\n * so it draws whenever it is small enough on screen; the worst case is a little\n * over-draw, never a hole or a crash.\n *\n * This decode order is the **original** chunk order (no frontier reorder), so\n * `child_start` still points at the right splats - the foveation path keeps\n * splats in file order and lets the GPU cull choose the cut per splat.\n */\nexport class RadParentSizes {\n private readonly chunkSize: number;\n /** Parent-size ranges awaiting the child chunk that will consume them,\n * keyed by that child chunk index. Each range is a global-index span. */\n private readonly pending = new Map<number, { start: number; end: number; size: number }[]>();\n /** Chunks already resolved - a parent decoded after its child can't fix it,\n * so we drop such stragglers rather than leak them. */\n private readonly resolved = new Set<number>();\n\n constructor(chunkSize: number) {\n this.chunkSize = chunkSize;\n }\n\n /**\n * Records `chunkIndex`'s internal→child links and returns that chunk's\n * per-splat parent sizes (in the chunk's own splat order). Call once per\n * chunk, as it decodes.\n *\n * @param childCount - Per-splat child count (0 = leaf).\n * @param childStart - Per-splat first-child global index.\n * @param size - Per-splat LOD node size.\n */\n resolve(\n chunkIndex: number,\n childCount: Uint16Array,\n childStart: Uint32Array,\n size: Float32Array,\n ): Float32Array {\n const count = childCount.length;\n const base = chunkIndex * this.chunkSize;\n // No known parent → Infinity (root-like: drawn when small enough on screen).\n const parentSize = new Float32Array(count).fill(Infinity);\n\n const ranges = this.pending.get(chunkIndex);\n if (ranges) {\n for (const range of ranges) {\n const from = Math.max(range.start - base, 0);\n const to = Math.min(range.end - base, count);\n for (let local = from; local < to; local++) parentSize[local] = range.size;\n }\n this.pending.delete(chunkIndex);\n }\n this.resolved.add(chunkIndex);\n\n // Record this chunk's children so they can read their parent size later.\n for (let i = 0; i < count; i++) {\n const cc = childCount[i] as number;\n if (cc === 0) continue;\n const parentNodeSize = size[i] as number;\n let g = childStart[i] as number;\n const end = g + cc;\n // Children are contiguous but may straddle a chunk boundary (rare).\n while (g < end) {\n const childChunk = Math.floor(g / this.chunkSize);\n const chunkEnd = (childChunk + 1) * this.chunkSize;\n const rangeEnd = Math.min(end, chunkEnd);\n if (!this.resolved.has(childChunk)) this.push(childChunk, g, rangeEnd, parentNodeSize);\n g = rangeEnd;\n }\n }\n\n return parentSize;\n }\n\n private push(childChunk: number, start: number, end: number, size: number): void {\n let ranges = this.pending.get(childChunk);\n if (!ranges) {\n ranges = [];\n this.pending.set(childChunk, ranges);\n }\n ranges.push({ start, end, size });\n }\n}\n","import * as THREE from 'three/webgpu';\nimport type { LodRun } from '../../lod-scheduler';\nimport type { LodSource, LodSourceOptions } from '../../lod-source';\nimport { RadParentSizes } from './rad-parent-sizes';\nimport type { SplatData } from '../../splat-data';\n\n/** Stand-in for an infinite parent size (root, or parent not yet decoded); the\n * frontier cut treats any node at this size as \"always big enough to keep\".\n * Matches `FRONTIER_ROOT_SIZE` in the material. */\nconst FRONTIER_ROOT_SIZE = 1e30;\n\n/** Spark's ~1.75× LOD merge ratio: a node's parent is roughly this much bigger.\n * Used as the `parent_size` fallback when the real parent has not decoded yet. */\nconst RAD_MERGE_RATIO = 1.75;\n\n/**\n * LOD source for a `.rad` too large to load whole (its leaf count exceeds the\n * budget-lift ceiling). Where {@link RadLodSource} renders the frontier of a\n * coarse→fine *prefix* - uniform detail, capped by the budget - this loads a\n * camera-directed **set** of chunks and renders them whole, letting the\n * material's screen-radius band cull pick the LOD cut per splat.\n *\n * The resident set is chunk 0's whole-scene coarse shell (a few shallow chunks,\n * always resident for far coverage) plus the nearest chunks by bounds distance,\n * up to the budget. There is **no ancestor-closedness requirement** - the\n * per-splat band cull is correct over any resident set (it draws exactly the\n * splats whose on-screen size lands in the band, one LOD level per view ray),\n * so it sidesteps the entangled-DAG wall that defeats a chunk cut. Loading\n * follows the camera: fetch the nearest undecoded refinements of the resident\n * set. See `docs/formats/rad-notes.md` M14.6.\n */\n\n/** Undecoded refinements fetched per reschedule to pull detail in. Ranked by\n * on-screen coarseness (Spark's descend-biggest-pixel-scale-first), so the near,\n * in-view, under-refined regions load ahead of distant ones. */\nconst PREFETCH_AHEAD = 16;\n/** Out-of-frustum regions are deprioritized by this factor (they are not drawn,\n * so refine what is on screen first). */\nconst FRUSTUM_PENALTY = 8;\n/** Shallowest chunks kept resident always, for whole-scene coarse coverage. */\nconst COARSE_BASE = 4;\n/** Floor on distance when scoring, so a splat at the camera can't divide by ~0. */\nconst MIN_SCORE_DISTANCE = 0.05;\n\nexport class RadFoveatedSource implements LodSource {\n budget: number;\n lodBaseDistance: number;\n lodMultiplier: number;\n\n private readonly chunkSize: number;\n private readonly chunkCounts: readonly number[];\n private readonly numChunks: number;\n private readonly decoded = new Set<number>();\n /** Real bounds of a decoded chunk (AABB of its splat centers). */\n private readonly bounds: (THREE.Box3 | null)[];\n /** Region estimated from decoded parents, to rank an undecoded chunk. */\n private readonly estBounds: (THREE.Box3 | null)[];\n /** Per chunk: the chunks its internal splats refine into (for prefetch). */\n private readonly childChunks: (Set<number> | null)[];\n /** Inverts the tree's forward links to give each splat its parent's size,\n * for the frontier cut. Fed the same `own_size` measure the material derives. */\n private readonly parentSizes: RadParentSizes;\n\n /** Splats chunk 0's coarse shell draws (the minimum-coverage floor). */\n coverageSplatCount = 1;\n\n /**\n * Whether per-splat `parent_size` is needed. Only the GPU cuts\n * (`foveationMode: 'band' | 'frontier'`) read `frontierParent`; the page-table\n * pager runs its own traversal in the worker and `gatherGlobals` never carries\n * it. Computing it anyway costs a full covariance pass per chunk *and* leaves\n * one pending-range object per internal node alive in {@link RadParentSizes}\n * until its child chunk decodes - tens of millions of live objects on an\n * 800-chunk capture, enough GC pressure to read as a frozen page.\n */\n needsParentSizes = true;\n\n private readonly scratchPoint = new THREE.Vector3();\n\n constructor(chunkSize: number, chunkCounts: readonly number[], options: LodSourceOptions) {\n this.chunkSize = chunkSize;\n this.chunkCounts = chunkCounts;\n this.numChunks = chunkCounts.length;\n this.budget = options.budget;\n this.lodBaseDistance = options.lodBaseDistance;\n this.lodMultiplier = options.lodMultiplier;\n this.bounds = Array.from({ length: this.numChunks }, () => null);\n this.estBounds = Array.from({ length: this.numChunks }, () => null);\n this.childChunks = Array.from({ length: this.numChunks }, () => null);\n this.parentSizes = new RadParentSizes(chunkSize);\n }\n\n onChunkDecoded(file: number, data: SplatData): void {\n if (this.decoded.has(file)) return;\n this.decoded.add(file);\n this.bounds[file] = boundsOf(data);\n\n const tree = data.radTree;\n if (tree) {\n const children = new Set<number>();\n const pos = data.positions;\n for (let i = 0; i < tree.childCount.length; i++) {\n if (tree.childCount[i] === 0) continue;\n const childChunk = Math.floor((tree.childStart[i] as number) / this.chunkSize);\n children.add(childChunk);\n let est = this.estBounds[childChunk];\n if (!est) {\n est = new THREE.Box3().makeEmpty();\n this.estBounds[childChunk] = est;\n }\n est.expandByPoint(\n this.scratchPoint.set(pos[i * 3] ?? 0, pos[i * 3 + 1] ?? 0, pos[i * 3 + 2] ?? 0),\n );\n }\n this.childChunks[file] = children;\n if (this.needsParentSizes) this.attachFrontierParent(file, data, tree, computeOwnSizes(data));\n }\n if (file === 0) this.coverageSplatCount = Math.max(1, this.chunkCounts[0] as number);\n }\n\n /**\n * Computes each splat's `parent_size` for the frontier cut and packs it, with\n * a leaf-sign bit, onto `data.frontierParent` (uploaded into `covarianceB.w`).\n *\n * `own_size` is derived from the covariance the same way the material does -\n * `2·√(trace(Σ)/3)`, which with the merged-node-expanded scales already carries\n * the expansion - so a node's own size and its children's `parent_size` are the\n * identical measure, giving clean level transitions.\n *\n * The catch: this source streams chunks in **camera** order, not tree order, so\n * a child frequently decodes before its parent and `RadParentSizes` returns\n * `Infinity` (unknown). Making every such splat a root would defeat the cut -\n * a root always passes the \"parent too big\" half, so it can never be culled and\n * the drawn count runs away. Instead an unknown parent falls back to Spark's\n * ~1.75× merge ratio (`parent ≈ own · 1.75`), which brackets the splat to one\n * level per ray (a per-splat band). Chunk 0 - the always-resident coarse shell -\n * keeps the true-root sentinel so far rays always have coverage.\n */\n private attachFrontierParent(\n file: number,\n data: SplatData,\n tree: NonNullable<SplatData['radTree']>,\n ownSize: Float32Array,\n ): void {\n const count = data.count;\n const parentSize = this.parentSizes.resolve(file, tree.childCount, tree.childStart, ownSize);\n const frontierParent = new Float32Array(count);\n for (let i = 0; i < count; i++) {\n let p = parentSize[i] as number;\n if (!Number.isFinite(p)) {\n p = file === 0 ? FRONTIER_ROOT_SIZE : (ownSize[i] as number) * RAD_MERGE_RATIO;\n }\n p = Math.min(p, FRONTIER_ROOT_SIZE);\n frontierParent[i] = tree.childCount[i] === 0 ? -p : p; // leaves negative\n }\n (data as { frontierParent?: Float32Array }).frontierParent = frontierParent;\n }\n\n computeDesiredRuns(cameraLocal: THREE.Vector3, frustum: THREE.Frustum, _now: number): LodRun[] {\n const resident = this.buildResident(cameraLocal, frustum);\n const runs: LodRun[] = [];\n let residentSplats = 0;\n let farthest = 0;\n for (const c of resident) {\n runs.push(this.wholeChunkRun(c));\n residentSplats += this.chunkCounts[c] as number;\n farthest = Math.max(farthest, this.chunkDistance(c, cameraLocal, frustum));\n }\n // Pull the nearest undecoded refinements of the resident set toward the\n // camera. Bound the fetch to chunks that could actually become resident -\n // nearer than the farthest resident chunk once the set is full - so a fixed\n // view does not eventually download the whole scene. A fetch-intent run only\n // needs a valid `file`; it becomes the real whole-chunk run once decoded.\n const limit = residentSplats >= this.budget ? farthest : Infinity;\n for (const c of this.nearestUndecodedRefinements(resident, cameraLocal, frustum, limit)) {\n runs.push(this.wholeChunkRun(c));\n }\n return runs;\n }\n\n /** The coarse shell covers far/loading regions via the band cull, so there\n * are no separate substitute runs. */\n coarsestRunsFor(): LodRun[] {\n return [];\n }\n\n /** Chunk 0's coarse shell plus the nearest decoded chunks, up to the budget. */\n private buildResident(cameraLocal: THREE.Vector3, frustum: THREE.Frustum): Set<number> {\n const resident = new Set<number>();\n let residentSplats = 0;\n for (let c = 0; c < Math.min(COARSE_BASE, this.numChunks); c++) {\n if (this.decoded.has(c)) {\n resident.add(c);\n residentSplats += this.chunkCounts[c] as number;\n }\n }\n const nearest = [...this.decoded]\n .filter((c) => !resident.has(c))\n .sort(\n (a, b) =>\n this.chunkDistance(a, cameraLocal, frustum) - this.chunkDistance(b, cameraLocal, frustum),\n );\n for (const c of nearest) {\n if (residentSplats >= this.budget) break;\n resident.add(c);\n residentSplats += this.chunkCounts[c] as number;\n }\n return resident;\n }\n\n private nearestUndecodedRefinements(\n resident: ReadonlySet<number>,\n cameraLocal: THREE.Vector3,\n frustum: THREE.Frustum,\n maxDistance: number,\n ): number[] {\n const candidates = new Set<number>();\n for (const c of resident) {\n const children = this.childChunks[c];\n if (!children) continue;\n for (const cc of children) {\n if (!this.decoded.has(cc) && this.chunkDistance(cc, cameraLocal, frustum) <= maxDistance) {\n candidates.add(cc);\n }\n }\n }\n // Refine the coarsest *on-screen* region first: score by the parent node's\n // `size / distance` (its projected coarseness), out-of-frustum penalized. This\n // is Spark's descend-biggest-pixel-scale-first order - the near surfaces the\n // camera is looking at refine ahead of distant ones, instead of by raw\n // whole-chunk bounds distance (which a scene-spanning coarse chunk defeats).\n return [...candidates]\n .sort(\n (a, b) =>\n this.refineScore(b, cameraLocal, frustum) - this.refineScore(a, cameraLocal, frustum),\n )\n .slice(0, PREFETCH_AHEAD);\n }\n\n /** Refine priority for an undecoded chunk (higher = sooner): nearest, in-view\n * region first, scored against its *localized* estimated bounds (not the\n * scene-spanning whole-chunk bounds). Off-screen regions - not drawn - are\n * strongly penalized so the surfaces the camera faces refine first. */\n private refineScore(c: number, cameraLocal: THREE.Vector3, frustum: THREE.Frustum): number {\n const box = this.estBounds[c] ?? this.bounds[c];\n if (!box) return 0;\n const d = Math.max(box.distanceToPoint(cameraLocal), MIN_SCORE_DISTANCE);\n return (frustum.intersectsBox(box) ? 1 : 1 / FRUSTUM_PENALTY) / d;\n }\n\n private chunkDistance(c: number, cameraLocal: THREE.Vector3, frustum: THREE.Frustum): number {\n const box = this.bounds[c] ?? this.estBounds[c];\n if (!box) return Infinity;\n const d = box.distanceToPoint(cameraLocal);\n return frustum.intersectsBox(box) ? d : d * FRUSTUM_PENALTY;\n }\n\n /** One run covering a whole chunk; the material cull selects splats within. */\n private wholeChunkRun(chunk: number): LodRun {\n return {\n file: chunk,\n level: this.numChunks - chunk,\n offset: 0,\n count: this.chunkCounts[chunk] as number,\n leafStart: chunk,\n leafEnd: chunk + 1,\n };\n }\n}\n\n/** Per-splat world `own_size` (`2·√(trace(Σ)/3)`) - the same measure the material\n * derives on the GPU and the frontier `parent_size` uses. */\nfunction computeOwnSizes(data: SplatData): Float32Array {\n const count = data.count;\n const cov = data.covariances;\n const ownSize = new Float32Array(count);\n for (let i = 0; i < count; i++) {\n const trace = (cov[i * 6] as number) + (cov[i * 6 + 3] as number) + (cov[i * 6 + 5] as number);\n ownSize[i] = 2 * Math.sqrt(Math.max(trace, 0) / 3);\n }\n return ownSize;\n}\n\n/** World-space AABB of a decoded chunk's splat centers. */\nfunction boundsOf(data: SplatData): THREE.Box3 {\n const box = new THREE.Box3();\n const p = new THREE.Vector3();\n const pos = data.positions;\n for (let i = 0; i < data.count; i++) {\n p.set(pos[i * 3] ?? 0, pos[i * 3 + 1] ?? 0, pos[i * 3 + 2] ?? 0);\n box.expandByPoint(p);\n }\n return box.isEmpty()\n ? new THREE.Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1))\n : box;\n}\n","import * as THREE from 'three/webgpu';\nimport type { LodRun } from '../../lod-scheduler';\nimport type {\n LodSource,\n LodSourceOptions,\n StreamedChunkOptions,\n StreamedScene,\n} from '../../lod-source';\nimport type { SplatData, RadTreeData } from '../../splat-data';\nimport type { SplatDatasetSource } from '../../dataset-source';\nimport { toRequestInit, type SplatRequestOptions } from '../../loading';\nimport { liftBudgetToFinestLevel } from '../../splat-budget';\nimport { parseRadChunkStreaming, parseRadHeaderMeta, type RadMeta } from './parse-rad';\nimport { RadFoveatedSource } from './rad-foveated-source';\n\n/**\n * Streamed reader for a single-file Spark `.rad` (see `docs/formats/rad-notes.md`).\n *\n * A `.rad` is a precomputed LOD splat tree stored as a sequence of 64K-splat\n * chunks in coarsest→finest order: chunk 0 is a whole-scene overview built\n * from the largest merged nodes, and each later chunk refines detail. The\n * tree itself lives *inside* the chunks - every splat carries `child_count`\n * and `child_start` columns - so unlike LCC2 (whose octree is fully described\n * by its manifest) the structure is discovered as chunks decode.\n *\n * {@link RadLodSource} renders the *frontier* of a resident **prefix** of the\n * chunk sequence: a splat is drawn when it is a leaf, or when its children have\n * not been loaded yet (their merged approximation stands in). The prefix grows\n * - coarsest chunk first - until the drawn count reaches the budget, so a huge\n * capture shows as a coherent, budget-sized scene that sharpens as it loads.\n *\n * {@link RadLodSource} is exported for unit testing only; it is not part of\n * the public API and is reached in practice through {@link buildRadScene}.\n */\n\n/** Chunks fetched ahead of the resident frontier to keep the pipeline full. */\nconst PREFETCH_AHEAD = 6;\n\n/**\n * Builds a streamed scene from a single-file `.rad`. Fetches the header and\n * chunk table, then decodes chunk 0 (the coarsest whole-scene overview) to\n * establish scene bounds - the header carries none - and to seed the LOD\n * source with the root of the tree.\n *\n * `maxShBands` lets a caller **decline** the spherical harmonics the file\n * carries. Unlike SOG's opt-*in* `shBands` (whose manifest never declares\n * whether tiles have shN), a `.rad` names its own `maxSh`, so the question here\n * is only whether to keep it. It matters: every band costs 16 B/splat of GPU\n * pool plus as much again of CPU backing, and a texture fetch and coefficient\n * evaluation per splat per frame - exactly what the `smooth` performance profile\n * exists to decline on mobile.\n *\n * The choice is deliberately **all-or-nothing**: `0` drops SH, anything else\n * keeps every band the file has. A *partial* cap cannot be honoured, because the\n * `.rad` decoder emits the file's full band count and `SplatMesh.writePackedSh`\n * only copies packed words when the counts match exactly - a mismatch falls\n * through to neutral words, which would pay for the reduced bands' textures and\n * still render flat, view-independent color. Truncating the packed SH per splat\n * at decode would lift this restriction.\n */\nexport async function buildRadScene(\n source: SplatDatasetSource,\n options: LodSourceOptions,\n request?: SplatRequestOptions,\n maxShBands: 0 | 1 | 2 | 3 = 3,\n budgetLifts = true,\n): Promise<StreamedScene> {\n const { meta, chunksStart } = await fetchRadHeader(source.manifestUrl, request);\n const chunkSize = meta.chunkSize ?? meta.count;\n const numChunks = meta.chunks.length;\n if (numChunks === 0) throw new Error('RAD file has no chunks.');\n\n // A `--rad-chunked` header names each chunk's own `.radc` file; a single-file\n // `.rad` gives byte ranges instead. An external chunk is fetched whole (its\n // URL resolves against the manifest); an inline one is a range of the `.rad`,\n // labelled with a debugging fragment the worker strips before fetching.\n const chunkUrls = meta.chunks.map((range, index) => {\n if (range.filename === undefined) return `${source.manifestUrl}#rad-chunk-${index}`;\n const url = source.resolve(range.filename);\n if (url === null) {\n throw new Error(`RAD chunk ${index} references a missing file \"${range.filename}\".`);\n }\n return url;\n });\n let chunkOptions: StreamedChunkOptions[] = meta.chunks.map((range) => ({\n format: 'rad-chunk',\n rad:\n range.filename === undefined\n ? { start: chunksStart + range.offset, length: range.bytes }\n : {},\n }));\n\n // The drawn frontier converges to the capture's leaf count (every merged node\n // hidden by its children). Spark records that as `input_splat_count` in the\n // header comment; it is the natural pool ceiling and lets a moderate scene\n // lift its budget to full resolution (see `fromSource`). Fall back to the\n // whole-tree count when the comment is absent or unparseable.\n const leafCount = parseInputSplatCount(meta.comment) ?? meta.count;\n // Prefix reader or foveated page table?\n //\n // The prefix reader is camera-*independent*: it grows a resident prefix of the\n // chunk sequence, coarsest first, until the drawn count reaches the budget. Its\n // one advantage is that at `budget >= leafCount` it reaches full leaf\n // resolution everywhere - which Spark's foveated cut never does - so a capture\n // that fits is better taken whole.\n //\n // When the budget *cannot* hold the leaves that advantage inverts, and badly:\n // the prefix spreads a fixed budget uniformly over the whole capture, so a 1M\n // budget on a 5.9M-leaf scene draws every surface at ~1/6 resolution and\n // walking up to something never sharpens it. Foveating spends the same budget\n // where the camera is looking and coarsens the distance instead.\n //\n // Hence the second test, against the budget *after* the finest-level lift\n // `fromSource` will apply - the lift is what carries a desktop over the line,\n // and mobile is deliberately exempt from it, which is exactly the case that was\n // landing on the wrong path.\n //\n // `budgetLifts` mirrors the caller's own precondition for that lift: it only\n // happens when neither `budget` nor `maxBudget` was pinned. Assuming it always\n // applies would misjudge every host that sizes its meshes explicitly - a\n // mesh given `maxBudget: pool / N` keeps that number, so it needs the\n // foveated path for the same reason a phone does.\n const effectiveBudget = budgetLifts\n ? liftBudgetToFinestLevel(options.budget, leafCount)\n : options.budget;\n const foveate = leafCount > FOVEATION_LEAF_THRESHOLD || leafCount > effectiveBudget;\n\n // Decode chunk 0 for bounds (the header has none) and to seed the tree root.\n // It must use the same splat order the streamed chunks will, or its\n // `child_start` links index the wrong locals: the prefix reader wants the\n // leaves-first reorder, the foveated/page-table reader wants file order.\n const chunk0Range = meta.chunks[0]!;\n const chunk0Buffer =\n chunk0Range.filename === undefined\n ? await fetchRange(\n source.manifestUrl,\n chunksStart + chunk0Range.offset,\n chunk0Range.bytes,\n request,\n )\n : await fetchWhole(chunkUrls[0]!, request);\n const chunk0Data = await parseRadChunkStreaming(chunk0Buffer, undefined, undefined, !foveate);\n // Later chunks must pack their SH against the same scene range chunk 0\n // established - the pool adopts one range for the whole scene. The codebook\n // (clustered SH) lives only in chunk 0 and rides along the same way.\n const shExtent = chunk0Data.shPacked?.range.max[0];\n if (chunk0Data.radShCodebook || shExtent !== undefined) {\n chunkOptions = chunkOptions.map((options) => ({\n ...options,\n rad: {\n ...options.rad!,\n ...(chunk0Data.radShCodebook ? { shCodebook: chunk0Data.radShCodebook } : {}),\n ...(shExtent !== undefined ? { shExtent } : {}),\n },\n }));\n }\n const bounds = boundsOf(chunk0Data);\n // Chunk 0 is a whole-scene overview. Copy a subsample of its centers before\n // page-table mode transfers the buffers to the worker, so the demo can place\n // the camera on the terrain instead of the AABB min (underground outliers).\n const overviewPositions = subsampleOverviewPositions(chunk0Data.positions, chunk0Data.count);\n // All-or-nothing, for the reason in the doc comment above: a partial cap\n // would silently degrade to neutral SH rather than to fewer bands.\n const shBands = maxShBands === 0 ? 0 : (chunk0Data.shPacked?.bands ?? 0);\n\n const chunkCounts = meta.chunks.map((_range, index) =>\n Math.min(chunkSize, meta.count - index * chunkSize),\n );\n\n // Foveated: load a camera-directed set of chunks and let the page-table\n // frontier pick the LOD cut per splat (see `docs/formats/rad-notes.md` M14.6\n // and `RadFoveatedSource`).\n if (foveate) {\n const foveated = new RadFoveatedSource(chunkSize, chunkCounts, options);\n if (chunk0Data.radTree) foveated.onChunkDecoded(0, chunk0Data);\n return {\n source: foveated,\n chunkUrls,\n chunkKind: 'file',\n // File order (no leaves-first reorder): the band cull is order-agnostic\n // and this keeps `child_start` valid for future exact-parent-size work.\n chunkOptions: chunkOptions.map((option) => ({\n ...option,\n rad: { ...option.rad!, reorder: false },\n })),\n shBands,\n bounds,\n pinnedFiles: new Set([0]),\n // Pool holds the resident set (near + coarse), not the whole tree.\n maxResidentSplats: options.budget,\n // The real capture size, which `maxResidentSplats` cannot carry here (it\n // echoes the caller's budget). A host splitting one budget across several\n // streamed meshes needs it: a mesh cannot spend more than it contains.\n contentSplatCount: leafCount,\n minimumCoverageSplats: foveated.coverageSplatCount,\n foveation: RAD_FOVEATION_BAND,\n chunkSize,\n overviewPositions,\n // Hand chunk 0 straight to the page-table worker: it holds the tree roots,\n // so until it lands every traversal returns an empty frontier. Its buffers\n // are transferred there, so nothing may read `chunk0Data` after this.\n bootstrapChunk: { file: 0, data: chunk0Data },\n };\n }\n\n const source_ = new RadLodSource(meta.count, chunkSize, chunkCounts, options);\n if (chunk0Data.radTree) source_.onChunkDecoded(0, chunk0Data);\n\n return {\n source: source_,\n chunkUrls,\n chunkKind: 'file',\n chunkOptions,\n shBands,\n bounds,\n pinnedFiles: new Set([0]),\n maxResidentSplats: leafCount,\n contentSplatCount: leafCount,\n minimumCoverageSplats: source_.coverageSplatCount,\n overviewPositions,\n };\n}\n\n/** Leaf count above which a `.rad` foveates instead of loading whole; matches\n * the budget-lift ceiling (`FINEST_LEVEL_BUDGET_MAX`). */\nconst FOVEATION_LEAF_THRESHOLD = 6_000_000;\n\n/** Screen-radius band (px) for foveated `.rad`: only splats projecting to a\n * radius in `(min, max]` draw. The ~2.5× width spans one LOD level (Spark's\n * merge ratio is ~1.75), so one level per view ray lands in the band. */\nconst RAD_FOVEATION_BAND = { minScreenRadiusPx: 1.6, maxScreenRadiusPx: 4 } as const;\n\n/** Reads `input_splat_count` (the leaf count) from Spark's build-info comment. */\nfunction parseInputSplatCount(comment: string | undefined): number | undefined {\n if (!comment) return undefined;\n try {\n const info = JSON.parse(comment) as { input_splat_count?: unknown };\n const value = info.input_splat_count;\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Source-local splat-center subsample for camera framing. */\nconst MAX_OVERVIEW_SAMPLES = 8192;\n\nfunction subsampleOverviewPositions(positions: Float32Array, count: number): Float32Array {\n if (count <= 0) return new Float32Array(0);\n const stride = Math.max(1, Math.floor(count / MAX_OVERVIEW_SAMPLES));\n let n = 0;\n for (let i = 0; i < count; i += stride) n++;\n const out = new Float32Array(n * 3);\n let o = 0;\n for (let i = 0; i < count; i += stride) {\n const b = i * 3;\n out[o++] = positions[b] as number;\n out[o++] = positions[b + 1] as number;\n out[o++] = positions[b + 2] as number;\n }\n return out;\n}\n\n/** World-space AABB of a decoded chunk's splat centers. */\nfunction boundsOf(data: SplatData): THREE.Box3 {\n const box = new THREE.Box3();\n const p = new THREE.Vector3();\n const pos = data.positions;\n for (let i = 0; i < data.count; i++) {\n p.set(pos[i * 3] ?? 0, pos[i * 3 + 1] ?? 0, pos[i * 3 + 2] ?? 0);\n box.expandByPoint(p);\n }\n return box.isEmpty()\n ? new THREE.Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1))\n : box;\n}\n\n/**\n * Selects a budget-bounded prefix of a `.rad` chunk sequence and renders its\n * frontier - the splats whose children are not yet resident, plus all leaves.\n *\n * The tree is discovered incrementally: {@link onChunkDecoded} records each\n * chunk's `child_count`/`child_start` columns, and `discoveredDepth` tracks the\n * longest contiguous decoded prefix. `computeDesiredRuns` emits the frontier of\n * the largest prefix whose drawn count fits the budget (cached - it does not\n * depend on the camera), plus fetch-intent runs that pull the next chunks in.\n *\n * Refinement is **uniform** over the scene, not camera-distance foveated. Chunk-\n * granular spatial foveation is impossible for this format: the chunk graph is a\n * deeply entangled DAG - every non-root chunk has ≥2 parent chunks - so no\n * ancestor-closed chunk subset larger than `{0}` exists, and a coarse ancestor\n * that is not resident would double-draw over any refined descendant. True near-\n * camera refinement therefore needs Spark-style per-splat GPU culling (see\n * `docs/formats/rad-notes.md`, ROADMAP M14.6). Two levers help meanwhile: a budget large\n * enough to load a moderate scene's full leaf set (blobs vanish once every chunk\n * is resident), and the `maxSplatScreenRadius` blob cull for what stays coarse.\n *\n * {@link RadLodSource} is exported for unit testing only; it is not part of the\n * public API and is reached in practice through {@link buildRadScene}.\n */\nexport class RadLodSource implements LodSource {\n budget: number;\n lodBaseDistance: number;\n lodMultiplier: number;\n\n private readonly total: number;\n private readonly chunkSize: number;\n private readonly chunkCounts: readonly number[];\n private readonly numChunks: number;\n /**\n * Decoded per-splat tree columns, by chunk index.\n *\n * Grows monotonically and is deliberately never evicted. That costs ~10 B per\n * splat of every chunk ever decoded (`childCount` u16 + `childStart` u32 +\n * `size` f32) and is invisible to the chunk cache's byte accounting, so it\n * reads like a leak - but it is not reclaimable. {@link discoveredDepth} is\n * the longest *contiguous* decoded prefix and every frontier walk at or below\n * it reads these columns, so dropping an entry would either corrupt the\n * traversal or force `discoveredDepth` back and visibly coarsen the scene.\n *\n * Bounded in practice by the mode that uses it: `.rad` defaults to\n * `foveationMode: 'page-table'`, where the frontier worker owns the traversal\n * and this source is never constructed. Only the `'band'` and `'frontier'`\n * A/B modes pay it. Reducing it means changing the tree encoding, not adding\n * an eviction pass here.\n */\n private readonly decoded = new Map<number, RadTreeData>();\n /** Longest contiguous decoded prefix `{0 … discoveredDepth − 1}`. */\n private discoveredDepth = 0;\n\n /** Chunk-0-only frontier: the always-available coarse substitute coverage. */\n private coarseRuns: LodRun[] = [];\n /** Splats the coarse (chunk-0) coverage draws. */\n coverageSplatCount = 1;\n\n /** Cache of the last emitted frontier, keyed by the inputs that shape it. */\n private cachedRuns: LodRun[] = [];\n private cachedFrontierCount = 0;\n /** Frontier splat count by prefix depth; see {@link countAtDepth}. */\n private readonly depthCounts = new Map<number, number>();\n /** Budget range over which {@link cachedRuns} stays the correct frontier. */\n private cachedBudgetLo = Number.POSITIVE_INFINITY;\n private cachedBudgetHi = Number.NEGATIVE_INFINITY;\n private cachedForDiscovered = -1;\n\n constructor(\n total: number,\n chunkSize: number,\n chunkCounts: readonly number[],\n options: LodSourceOptions,\n ) {\n this.total = total;\n this.chunkSize = chunkSize;\n this.chunkCounts = chunkCounts;\n this.numChunks = chunkCounts.length;\n this.budget = options.budget;\n this.lodBaseDistance = options.lodBaseDistance;\n this.lodMultiplier = options.lodMultiplier;\n }\n\n onChunkDecoded(file: number, data: SplatData): void {\n if (!data.radTree || this.decoded.has(file)) return;\n this.decoded.set(file, data.radTree);\n if (file === 0) {\n // Chunk 0 alone is the coarsest whole-scene coverage (its splats' children\n // all live in later chunks), so its frontier at \"only chunk 0 resident\" is\n // the pinned substitute every deferred deeper region falls back to.\n this.coarseRuns = this.frontierRuns(1, this.chunkCounts[0] as number);\n this.coverageSplatCount = Math.max(1, countSplats(this.coarseRuns));\n }\n while (this.decoded.has(this.discoveredDepth)) this.discoveredDepth++;\n }\n\n computeDesiredRuns(_cameraLocal: THREE.Vector3, _frustum: THREE.Frustum, _now: number): LodRun[] {\n this.ensureFrontier();\n // Keep pulling chunks in until the drawn count reaches the budget. The\n // frontier can never exceed the capture's leaf count, so when the budget is\n // lifted to that count (a moderate scene) this loads every chunk and the\n // scene reaches full resolution - no coarse blobs left anywhere.\n if (this.cachedFrontierCount >= this.budget) return this.cachedRuns;\n\n // Pull the next window of undecoded chunks in. Fetch-intent runs\n // reference undecoded chunks so the mesh fetches them; they are always\n // deferred (their chunk is never cached at the moment it would append,\n // because decoding advances the frontier in the same tick), so their\n // geometry is immaterial - only their `file` matters.\n const runs = [...this.cachedRuns];\n const until = Math.min(this.discoveredDepth + PREFETCH_AHEAD, this.numChunks);\n for (let c = this.discoveredDepth; c < until; c++) {\n if (!this.decoded.has(c)) runs.push(this.fetchIntentRun(c));\n }\n return runs;\n }\n\n coarsestRunsFor(from: number, to: number): LodRun[] {\n const runs: LodRun[] = [];\n for (const run of this.coarseRuns) {\n if (run.leafStart < to && run.leafEnd > from) runs.push(run);\n }\n return runs;\n }\n\n /**\n * Recomputes the cached frontier when the budget or discovery has moved.\n *\n * The cache is keyed on the *range* of budgets that select the same prefix\n * depth, not on the budget itself. A camera-weighted governor rewrites the\n * budget every frame - it wanders by a few percent as the camera drifts - and\n * an exact-equality key therefore missed on roughly a third of all calls while\n * choosing the very same depth. Each of those misses re-walks `frontierRuns`\n * over the whole decoded prefix (millions of splats, several times over if it\n * steps down levels): measured at 33ms mean and 1.1s worst, it was 92% of all\n * frame CPU on a multi-mesh scene. Frontier count rises monotonically with\n * depth, so the selected depth is stable for any budget in\n * `[thisDepthCount, nextDepthCount)` and the walk can be skipped outright.\n */\n private ensureFrontier(): void {\n if (\n this.cachedForDiscovered === this.discoveredDepth &&\n this.budget >= this.cachedBudgetLo &&\n this.budget < this.cachedBudgetHi\n ) {\n return;\n }\n this.cachedForDiscovered = this.discoveredDepth;\n\n // Largest prefix whose frontier fits the budget. Frontier count rises\n // monotonically with depth, so walk down from the deepest decoded prefix.\n let depth = this.discoveredDepth;\n let count = depth > 0 ? this.countAtDepth(depth) : 0;\n // The shallowest depth already rejected as too large - the budget at which\n // this result would stop being the right one. Nothing is deeper than the\n // discovered prefix, so without a rejection the band has no upper bound.\n let rejectedCount = Number.POSITIVE_INFINITY;\n while (depth > 1 && count > this.budget) {\n rejectedCount = count;\n depth--;\n count = this.countAtDepth(depth);\n }\n // Only the depth that wins builds runs. The search itself reads memoized\n // counts, so a budget swing that skips several levels costs one walk rather\n // than one per level tried.\n const runs =\n depth > 0 ? this.frontierRuns(depth, Math.min(depth * this.chunkSize, this.total)) : [];\n this.cachedRuns = runs;\n this.cachedFrontierCount = count;\n // Depth 1 is the floor: it is returned however small the budget gets, so\n // that case stays valid all the way down to zero.\n this.cachedBudgetLo = depth > 1 ? count : 0;\n this.cachedBudgetHi = rejectedCount;\n }\n\n /**\n * Splats the frontier draws at `depth`, memoized.\n *\n * `discoveredDepth` is the longest *contiguous* decoded prefix, so for any\n * `depth <= discoveredDepth` every chunk the walk reads is already decoded and\n * the answer can never change afterwards - chunks are only ever added to\n * `decoded`. That makes this permanently cacheable, which is what lets the\n * depth search above run without rebuilding runs at each level it rejects.\n */\n private countAtDepth(depth: number): number {\n const memo = this.depthCounts.get(depth);\n if (memo !== undefined) return memo;\n const residentCount = Math.min(depth * this.chunkSize, this.total);\n let total = 0;\n for (let c = 0; c < depth; c++) {\n const tree = this.decoded.get(c);\n if (!tree) continue;\n const count = this.chunkCounts[c] as number;\n for (let i = 0; i < count; i++) {\n if (tree.childCount[i] === 0 || (tree.childStart[i] as number) >= residentCount) total++;\n }\n }\n this.depthCounts.set(depth, total);\n return total;\n }\n\n /**\n * The rendered runs for a resident prefix of `depth` chunks: within each\n * chunk, the contiguous ranges of splats that are leaves or whose children\n * fall beyond `residentCount` (the prefix's total splat count). The chunk's\n * splats are child-start-descending (leaves first), so each range coalesces\n * into one run.\n */\n private frontierRuns(depth: number, residentCount: number): LodRun[] {\n const runs: LodRun[] = [];\n for (let c = 0; c < depth; c++) {\n const tree = this.decoded.get(c);\n if (!tree) continue; // only reachable if a prefix chunk was evicted from discovery\n const count = this.chunkCounts[c] as number;\n const base = c * this.chunkSize;\n const level = this.numChunks - c;\n let start = -1;\n for (let i = 0; i < count; i++) {\n const draws = tree.childCount[i] === 0 || (tree.childStart[i] as number) >= residentCount;\n if (draws) {\n if (start < 0) start = i;\n } else if (start >= 0) {\n runs.push(makeRun(c, level, start, i, base));\n start = -1;\n }\n }\n if (start >= 0) runs.push(makeRun(c, level, start, count, base));\n }\n return runs;\n }\n\n private fetchIntentRun(chunk: number): LodRun {\n const count = this.chunkCounts[chunk] as number;\n return makeRun(chunk, this.numChunks - chunk, 0, count, chunk * this.chunkSize);\n }\n}\n\n/** A run for chunk `file`'s local splats `[lo, hi)`, keyed by global index. */\nfunction makeRun(file: number, level: number, lo: number, hi: number, base: number): LodRun {\n return { file, level, offset: lo, count: hi - lo, leafStart: base + lo, leafEnd: base + hi };\n}\n\nfunction countSplats(runs: readonly LodRun[]): number {\n let total = 0;\n for (const run of runs) total += run.count;\n return total;\n}\n\n/** Fetches and parses a `.rad`'s `RAD0` header and chunk table. */\nasync function fetchRadHeader(\n url: string,\n request?: SplatRequestOptions,\n): Promise<{ meta: RadMeta; chunksStart: number }> {\n // One speculative read covers the header for any realistic chunk count; a\n // pathologically long table (very large scene) triggers one exact refetch.\n const PROBE = 262_144;\n let buffer = await fetchRange(url, 0, PROBE, request, true);\n const view = new DataView(buffer);\n if (view.byteLength >= 8) {\n const length = view.getUint32(4, true);\n if (8 + length > buffer.byteLength) {\n buffer = await fetchRange(url, 0, 8 + length, request);\n }\n }\n return parseRadHeaderMeta(buffer);\n}\n\n/**\n * Fetches `[start, start + length)` of a URL, requiring a real 206 (like the\n * LCC path). `allowShort` permits a server to return fewer bytes than asked -\n * used for the header probe, where the file may be shorter than the probe.\n */\nasync function fetchRange(\n url: string,\n start: number,\n length: number,\n request?: SplatRequestOptions,\n allowShort = false,\n): Promise<ArrayBuffer> {\n const init = toRequestInit(request);\n const response = await fetch(url, {\n ...init,\n headers: { ...(request?.headers ?? {}), Range: `bytes=${start}-${start + length - 1}` },\n });\n if (!response.ok) {\n throw new Error(`Failed to load ${url}: HTTP ${response.status}`);\n }\n // The header probe (`allowShort`) tolerates a `200` - a `--rad-chunked` set's\n // `.rad` header is small enough to arrive whole from a server that ignores\n // Range. Chunk-*data* reads stay strict: a single-file `.rad` that answers\n // `200` would download the whole capture, so it is rejected.\n if (response.status !== 206 && !(allowShort && response.status === 200)) {\n throw new Error(\n `${url} ignored a Range request (HTTP ${response.status}); single-file .rad streaming ` +\n 'needs a server that answers 206 Partial Content.',\n );\n }\n const buffer = await response.arrayBuffer();\n if (!allowShort && buffer.byteLength !== length) {\n throw new Error(`${url} returned ${buffer.byteLength} bytes for a ${length}-byte range.`);\n }\n return buffer;\n}\n\n/** Fetches a whole file (an external `.radc` chunk, or a small chunked-set\n * `.rad` header served without range support). */\nasync function fetchWhole(url: string, request?: SplatRequestOptions): Promise<ArrayBuffer> {\n const response = await fetch(url, toRequestInit(request));\n if (!response.ok) throw new Error(`Failed to load ${url}: HTTP ${response.status}`);\n return response.arrayBuffer();\n}\n"],"names":["shEncodingBytes","prop","requireRange","requireMax","assertColumnHoldsCount","data","dims","count","bytesPerValue","decodeFloatColumn","decodeF32","decodeF16","decodeF32LeBytes","decodeF16LeBytes","min","max","decodeR8","decodeR8Delta","decodeS8","decodeS8Delta","decodeLn0R8","decodeLnF16","decodeOrientation","decodeQuatOct88R8","xyz","out","i","x","y","z","decodeChildCount","decodeU16","decodeChildStart","decodeU32","scratch","bitsToFloat32","bits","view","index","d","halfToFloat","stride","range","last","value","byte","signed","step","u","v","r","t","length","halfTheta","s","w","RAD_MAGIC","RAD_CHUNK_MAGIC","MAX_RAD_SPLAT_COUNT","assertRadCountInteger","what","assertSaneRadCount","parseRad","buffer","bytes","mergeChunks","decodeChunk","meta","chunksStart","parseRadHeaderMeta","chunks","shCodebook","shExtent","start","chunk","_a","magic","decodeUtf8","chunkRanges","roundup8","parseRadChunkStreaming","reorder","chunkToSplatData","positions","colors","covariances","writeSplat","childCount","childStart","order","frontierOrder","identityOrder","reorderedSh","reorderPackedSh","outCount","outStart","outSize","j","src","radNodeSize","alpha","scale","avg","key","a","b","chunkStart","withTree","knownShCodebook","knownShExtent","metaEnd","chunkMeta","payloadBytes","payloadStart","lodTree","properties","byName","readProp","name","raw","decompressColumn","wantTree","propertyNames","centerRaw","alphaRaw","rgbRaw","scaleRaw","orientationRaw","childCountRaw","childStartRaw","sh1Raw","sh2Raw","sh3Raw","shLabelRaw","sh1CodeRaw","sh2CodeRaw","sh3CodeRaw","decodeRadSh","total","countLeaves","sourceSh","packed","shCoefficientCount","neutralShWord","words","sh","dst","input","direct","code","directBands","bands","codeBands","coefficients","codebook","extent","combineShColumns","metadataShExtent","codeCount","shColumnCount","labels","decodeShLabels","label","packShCoefficients","suffix","band","bandExtent","raws","totalCoefficients","result","coefficientOffset","values","base","k","dimensions","clampByte","writeCovariance","leaves","size","offset","format","copy","stream","RadParentSizes","chunkSize","__publicField","chunkIndex","parentSize","ranges","from","to","local","cc","parentNodeSize","g","end","childChunk","chunkEnd","rangeEnd","FRONTIER_ROOT_SIZE","RAD_MERGE_RATIO","PREFETCH_AHEAD","FRUSTUM_PENALTY","COARSE_BASE","MIN_SCORE_DISTANCE","RadFoveatedSource","chunkCounts","options","THREE","file","boundsOf","tree","children","pos","est","computeOwnSizes","ownSize","frontierParent","p","cameraLocal","frustum","_now","resident","runs","residentSplats","farthest","c","limit","nearest","maxDistance","candidates","box","cov","trace","buildRadScene","source","request","maxShBands","budgetLifts","fetchRadHeader","chunkUrls","url","chunkOptions","leafCount","parseInputSplatCount","effectiveBudget","liftBudgetToFinestLevel","foveate","FOVEATION_LEAF_THRESHOLD","chunk0Range","chunk0Buffer","fetchRange","fetchWhole","chunk0Data","bounds","overviewPositions","subsampleOverviewPositions","shBands","_b","_range","foveated","option","RAD_FOVEATION_BAND","source_","RadLodSource","comment","MAX_OVERVIEW_SAMPLES","n","countSplats","_cameraLocal","_frustum","until","run","depth","rejectedCount","memo","residentCount","level","makeRun","lo","hi","allowShort","init","toRequestInit","response"],"mappings":"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"}