@selvajs/visualization 1.6.0-beta.9 → 1.6.1-beta.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.
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","names":["renderable"],"sources":["../src/shared/types.ts","../src/render/scene-ownership.ts","../src/render/scene-setup/reflection-environment.ts","../src/render/up-axis.ts","../src/render/scene-setup/defaults.ts","../src/render/scene-setup/setup-lighting.ts","../src/render/three-helpers.ts","../src/render/camera-controller.ts","../src/render/edges/line-geometry.ts","../src/render/edge-extract.ts","../src/render/edges/options.ts","../src/render/edges/extraction.ts","../src/render/scene-setup/merged-picking.ts","../src/render/edges/member-filter.ts","../src/render/edges/overlay.ts","../src/render/edges.ts","../src/render/grid.ts","../src/render/label-layer.ts","../src/render/measure.ts","../src/render/near-plane.ts","../src/render/tool-registry.ts","../src/render/view-gizmo.ts","../src/render/scene-setup/animation-loop.ts","../src/render/scene-setup/emissive-lights.ts","../src/render/scene-setup/appearance.ts","../src/render/scene-setup/create-camera.ts","../src/render/scene-setup/create-scene.ts","../src/render/scene-setup/dispose.ts","../src/render/edge-detection-pass.ts","../src/render/render-pipeline.ts","../src/render/scene-setup/pipeline-controller.ts","../src/render/scene-setup/setup-controls.ts","../src/render/scene-setup/setup-environment.ts","../src/render/scene-setup/setup-events.ts","../src/render/scene-setup/setup-renderer.ts","../src/render/scene-setup/init-three.ts"],"sourcesContent":["import type * as THREE from 'three';\n\n// ============================================================================\n// LOOKS\n// ============================================================================\n\n/** Source of truth for {@link Look} — lets consumers (e.g. a style picker) iterate instead of hardcoding names. */\nexport const LOOKS = [\n\t'technical',\n\t'studio',\n\t'showcase',\n\t'arctic',\n\t'xray',\n\t'lineart',\n\t'wireframe',\n\t'night'\n] as const;\n\nexport type Look = (typeof LOOKS)[number];\n\n/**\n * How a {@link Look} overrides the per-mesh material a solve produced. Omitted fields leave the\n * mesh's own value alone, so a look that sets none of them is purely a lighting change.\n *\n * These fight the model's real colours on purpose: `arctic` reads shape over material, `xray` reads\n * what is inside, `wireframe` reads topology. `setLook` snapshots the parsed values before the first\n * override and restores them when switching back, so the model's own colours survive a round trip.\n */\nexport type LookMaterialOverride = {\n\t/** Replaces the mesh's own colour. Hex, e.g. 0xf2f4f7. */\n\tcolor?: number;\n\tmetalness?: number;\n\troughness?: number;\n\t/** Replaces the glow of a material that emits from the wire. A selection tint keeps its own. */\n\temissiveIntensity?: number;\n\t/** Forces `transparent: true` when below 1. */\n\topacity?: number;\n\t/**\n\t * Skip the depth buffer so far faces aren't hidden by near ones — what makes an x-ray read\n\t * through the model instead of just looking like tinted glass. Costs correct sort order, which\n\t * is the intended trade.\n\t */\n\tdepthWrite?: boolean;\n\t/**\n\t * Draw each triangle as its three edges instead of a filled face. Shows the tessellation, not the\n\t * design edges the `edges` overlay extracts — a curved surface reads as a dense triangle mesh.\n\t * Lighting still applies but has almost nothing to shade, so a wireframe look wants flat fill\n\t * rather than a key light.\n\t */\n\twireframe?: boolean;\n\t/**\n\t * Replaces the parsed side. Closed solids parse as `FrontSide`; a look that shows what's behind\n\t * a surface (x-ray, wireframe) needs their far side back.\n\t */\n\tside?: THREE.Side;\n};\n\nexport type BloomOptions = { strength: number; radius: number; threshold: number };\n\n/**\n * The lighting/material dials a {@link Look} sets. It still never *drives* the edge overlay or the\n * grid; `requiresEdges` only states that a look is incomplete without edges, and the host decides\n * whether to honour it.\n */\nexport type LookPreset = {\n\ttoneMapping: THREE.ToneMapping;\n\ttoneMappingExposure: number;\n\tenvMapIntensity: number;\n\t/** Multiplier on the HDR's IBL (`scene.environmentIntensity`). */\n\tenvironmentIntensity: number;\n\themisphereIntensity: number;\n\tambientIntensity: number;\n\tcullBackfaces: boolean;\n\tambientOcclusion: boolean;\n\t/**\n\t * A key light casting a shadow. IBL alone lights every face of a box almost equally, so without\n\t * this a model reads as a flat white silhouette — the directional falloff is what separates the\n\t * three faces meeting at a corner.\n\t */\n\tsunlightIntensity: number;\n\t/** Absent on the looks that only retune lighting. */\n\tmaterialOverride?: LookMaterialOverride;\n\t/** Replaces the scene's background while the look is active. */\n\tbackground?: number;\n\t/** Glow around anything brighter than `threshold` in linear light, so only emissive parts bloom. */\n\tbloom?: BloomOptions;\n\t/** Emissive meshes light the scene, through a shadow-casting point light each. */\n\temissiveLights?: boolean;\n\t/** How many emissive sources get a light. Default 3. */\n\tmaxEmissiveLights?: number;\n\t/** Caps the render resolution while this look is active. */\n\tmaxPixelRatio?: number;\n\t/** Replaces these fields on a phone (see `RenderConfig.lowPower`). */\n\tlowPower?: Partial<\n\t\tPick<LookPreset, 'ambientOcclusion' | 'bloom' | 'maxEmissiveLights' | 'maxPixelRatio'>\n\t>;\n\t/**\n\t * This look is a line drawing: without the edge overlay it renders as blank white shapes. Set\n\t * only by `lineart`. A declaration, not an action — `setLook` still touches no overlay, so a\n\t * host that ignores this gets a look that doesn't work rather than a broken invariant.\n\t *\n\t * A host honouring this must also turn `edges.distanceFade` off. The fade sets opacity per\n\t * overlay from its 15th-percentile segment length, so one finely-detailed mesh fades whole:\n\t * in a shaded look that softens some outlines, but here it erases parts of the only thing being\n\t * drawn, and the opacity is recomputed per frame so edges pop in and out while orbiting.\n\t */\n\trequiresEdges?: boolean;\n};\n\n/** How compute meshes read visually — the parse-time material choices baked from a {@link Look}. */\nexport interface MaterialAppearanceOptions {\n\t/** Default 1 (three.js's own material default) when omitted. */\n\tenvMapIntensity?: number;\n\t/**\n\t * `THREE.FrontSide` on every mesh, open surfaces included, which then vanish when viewed from\n\t * behind. Default false: only meshes detected as closed solids are culled.\n\t */\n\tcullBackfaces?: boolean;\n}\n","// ============================================================================\n// Scene ownership: who put an object in the scene, and what may remove it\n// ============================================================================\n//\n// A live scene mixes content from three owners: the solve (replaced wholesale every solve),\n// viewer aids (grid/floor/labels, never replaced), and host apps drawing their own geometry\n// alongside the solve. `userData.source` records which, and `clearScene` consults it to decide\n// what a solve is allowed to destroy.\n//\n// App content carries an owner id (`app:<id>`) rather than a flat `'user'` so a host running\n// more than one app can clear its own geometry without touching another's, and so a scoped\n// solve can replace one app's results while leaving the rest standing.\n\nimport type * as THREE from 'three';\n\n/** Geometry produced by a solve. Replaced wholesale on the next one. */\nexport const SOURCE_COMPUTE = 'compute';\n\n/**\n * Host-added geometry with no owner id. Predates scoped ownership and still honoured everywhere\n * an `app:` scope is — new code should prefer {@link appSource}.\n */\nexport const SOURCE_USER = 'user';\n\nconst APP_PREFIX = 'app:';\n\n/** The `userData.source` tag for geometry owned by app `id` (`'pointcloud'` → `'app:pointcloud'`). */\nexport function appSource(id: string): string {\n\treturn `${APP_PREFIX}${id}`;\n}\n\n/** The app id from a source tag, or null if the tag isn't app-owned. */\nexport function appIdFromSource(source: unknown): string | null {\n\tif (typeof source !== 'string' || !source.startsWith(APP_PREFIX)) return null;\n\treturn source.slice(APP_PREFIX.length) || null;\n}\n\n/**\n * True for anything a host added rather than the solve — `'user'` or any `app:` scope. This is\n * the predicate `clearScene` uses, so it decides what survives a solve.\n */\nexport function isHostOwned(object: THREE.Object3D): boolean {\n\tconst source = object.userData?.source;\n\treturn source === SOURCE_USER || appIdFromSource(source) !== null;\n}\n\n/**\n * True for objects owned by `id`. Passing no id matches every host-owned object, which is what\n * `clearUserGeometry()` does.\n */\nexport function isOwnedBy(object: THREE.Object3D, id?: string): boolean {\n\tif (id === undefined) return isHostOwned(object);\n\treturn object.userData?.source === appSource(id);\n}\n","import * as THREE from 'three';\nimport { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';\n\nimport { SOURCE_COMPUTE } from '../scene-ownership.js';\n\n// A metal shows nothing but its reflection. The HDR that lights the scene is an outdoor sky with a\n// hard horizon over a near-black ground, so a metal mirrors a white band over a black one and the\n// split lands mid-face, jagged wherever roughness varies. Metals reflect a soft studio room instead;\n// everything else keeps the HDR, so the looks tuned on it don't move.\nconst METAL = 0.5;\n\nconst KEY = 'selvaReflectionEnvironment';\n\ninterface ReflectionEnvironment {\n\ttexture: THREE.Texture;\n\trotation: THREE.Euler;\n}\n\n/**\n * Builds the studio probe once per scene. `rotation` turns its Y-up room to the scene's up, the same\n * rotation the HDR gets.\n */\nexport function createReflectionEnvironment(\n\tscene: THREE.Scene,\n\trenderer: THREE.WebGLRenderer,\n\trotation: THREE.Euler\n): void {\n\tconst pmrem = new THREE.PMREMGenerator(renderer);\n\tconst room = new RoomEnvironment();\n\tconst texture = pmrem.fromScene(room, 0.04).texture;\n\troom.dispose();\n\tpmrem.dispose();\n\tinstallReflectionEnvironment(scene, texture, rotation);\n}\n\n/** The GPU-free half of {@link createReflectionEnvironment}; tests call it with a plain texture. */\nexport function installReflectionEnvironment(\n\tscene: THREE.Scene,\n\ttexture: THREE.Texture,\n\trotation: THREE.Euler\n): void {\n\tconst environment: ReflectionEnvironment = { texture, rotation: rotation.clone() };\n\tscene.userData[KEY] = environment;\n\tapplyReflectionEnvironment(scene, scene);\n}\n\nexport function disposeReflectionEnvironment(scene: THREE.Scene): void {\n\t(scene.userData[KEY] as ReflectionEnvironment | undefined)?.texture.dispose();\n\tdelete scene.userData[KEY];\n}\n\n/**\n * Points every metallic solve material under `root` at the studio probe, and every other one back\n * at `scene.environment`. Run after meshes are added and after a look changes metalness.\n *\n * A material's own `envMap` also makes three honour its `envMapIntensity`: with only\n * `scene.environment`, three overwrites it with `scene.environmentIntensity` on every draw.\n */\nexport function applyReflectionEnvironment(root: THREE.Object3D, scene: THREE.Scene): void {\n\tconst environment = scene.userData[KEY] as ReflectionEnvironment | undefined;\n\tif (!environment) return;\n\troot.traverse((object) => {\n\t\tif (object.userData.source !== SOURCE_COMPUTE) return;\n\t\tconst mesh = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tconst materials = Array.isArray(mesh.material)\n\t\t\t? mesh.material\n\t\t\t: mesh.material\n\t\t\t\t? [mesh.material]\n\t\t\t\t: [];\n\t\tfor (const material of materials) {\n\t\t\tif (!(material instanceof THREE.MeshStandardMaterial)) continue;\n\t\t\t// Leave a host's own envMap alone.\n\t\t\tif (material.envMap && material.envMap !== environment.texture) continue;\n\t\t\tconst wanted = material.metalness > METAL ? environment.texture : null;\n\t\t\tif (material.envMap === wanted) continue;\n\t\t\tmaterial.envMap = wanted;\n\t\t\tmaterial.envMapRotation.copy(environment.rotation);\n\t\t\tmaterial.needsUpdate = true;\n\t\t}\n\t});\n}\n","import * as THREE from 'three';\n\n/**\n * Single source of truth for \"which way is up, and what do front/right mean\" — camera framing, sun\n * position, ground offset, and view presets all derive from {@link buildUpBasis} rather than\n * hardcoding an axis, so a Y-up scene gets a correct horizon and sun instead of the below-horizon\n * result a hardcoded Z-up vector would give.\n *\n * `forward` is the camera's look direction (camera → model); a view preset's camera position is the\n * reverse (target → camera), see `camera-controller.ts`. `right` is `seed x up`, not `up x seed`, to\n * match Rhino's handedness: for Z-up this makes the Front-view camera look along +Y and the\n * Right-view camera look along -X (it sits at `right` = +X, facing back toward the origin).\n */\n\n/** Orthonormal frame derived from a scene up axis. All vectors are unit length. */\nexport interface UpBasis {\n\tup: THREE.Vector3;\n\tforward: THREE.Vector3;\n\tright: THREE.Vector3;\n}\n\n/** Ground-plane axes for a given up vector: Z-up yields forward = +Y, right = +X (Rhino's Front/Right). */\nexport function buildUpBasis(up: THREE.Vector3): UpBasis {\n\tconst u = up.clone().normalize();\n\n\t// Seed must not be (nearly) parallel to up, or the cross product is unstable.\n\tconst worldZ = new THREE.Vector3(0, 0, 1);\n\tconst worldY = new THREE.Vector3(0, 1, 0);\n\tconst seed = Math.abs(u.dot(worldZ)) > 0.9 ? worldY : worldZ;\n\n\tconst right = new THREE.Vector3().crossVectors(seed, u).normalize();\n\tconst forward = new THREE.Vector3().crossVectors(u, right).normalize();\n\n\treturn { up: u, forward, right };\n}\n\n/** Default 3/4 iso camera offset from the target (behind-left, above), scaled to `distance`. */\nexport function isoOffset(up: THREE.Vector3, distance: number): THREE.Vector3 {\n\tconst { forward, right, up: u } = buildUpBasis(up);\n\t// Normalize before scaling so `distance` is the true radius, not the diagonal of the raw sum.\n\treturn forward\n\t\t.clone()\n\t\t.multiplyScalar(-1)\n\t\t.add(right.clone().multiplyScalar(-1))\n\t\t.add(u)\n\t\t.normalize()\n\t\t.multiplyScalar(distance);\n}\n\n/** Default sun position: high above the model, offset to one side for a directional gradient. */\nexport function sunOffset(up: THREE.Vector3, sideDistance: number, height: number): THREE.Vector3 {\n\tconst { forward, right, up: u } = buildUpBasis(up);\n\treturn right\n\t\t.clone()\n\t\t.multiplyScalar(sideDistance)\n\t\t.add(forward.clone().multiplyScalar(sideDistance))\n\t\t.add(u.clone().multiplyScalar(height));\n}\n\n/**\n * Rotates an equirectangular environment map's horizon onto the scene's ground plane.\n *\n * Three's equirect mapping is hardcoded to Y-up: the HDR's horizon is assumed to lie in the XZ\n * plane with zenith along +Y. In a Z-up scene that leaves the environment on its side — horizon\n * vertical, lighting arriving from +Y instead of overhead. A neutral studio HDR hides this; any\n * sky/ground HDR makes it obvious.\n *\n * Returns the Euler rotating the map's native +Y zenith onto `up` (identity for Y-up). Apply to\n * BOTH `scene.environmentRotation` and `scene.backgroundRotation` — they're independent, and\n * setting only one desyncs background from lighting.\n */\nexport function environmentRotationFor(up: THREE.Vector3): THREE.Euler {\n\tconst u = up.clone().normalize();\n\tconst mapZenith = new THREE.Vector3(0, 1, 0);\n\n\tif (u.dot(mapZenith) > 0.9999) return new THREE.Euler();\n\n\t// Upside-down (-Y): setFromUnitVectors picks an arbitrary perpendicular axis for a 180° flip,\n\t// spinning the horizon. Roll about X instead so the horizon stays put.\n\tif (u.dot(mapZenith) < -0.9999) return new THREE.Euler(Math.PI, 0, 0);\n\n\tconst quaternion = new THREE.Quaternion().setFromUnitVectors(mapZenith, u);\n\treturn new THREE.Euler().setFromQuaternion(quaternion);\n}\n\n/** Which world axis the up vector most closely aligns with. */\nexport function upToAxis(up: THREE.Vector3): 'x' | 'y' | 'z' {\n\tconst ax = Math.abs(up.x);\n\tconst ay = Math.abs(up.y);\n\tconst az = Math.abs(up.z);\n\tif (ax >= ay && ax >= az) return 'x';\n\tif (ay >= az) return 'y';\n\treturn 'z';\n}\n","import * as THREE from 'three';\n\nimport { DEFAULT_LOOK, lookPreset } from '../../shared/index.js';\nimport type { ThreeInitializerOptions } from '../types.js';\nimport { isoOffset, sunOffset, upToAxis } from '../up-axis.js';\n\n/** Rhino's convention, and the frame all geometry arrives in — Selva is Z-up end to end. */\nexport const defaultUp = new THREE.Vector3(0, 0, 1);\n\n// onMaxAnisotropy stays optional — a caller-supplied hook, not a config value with a default.\nexport type ResolvedOptions = Required<Omit<ThreeInitializerOptions, 'onMaxAnisotropy'>> &\n\tPick<ThreeInitializerOptions, 'onMaxAnisotropy'>;\n\n/**\n * Decided once, from the device: reacting to slow frames would change the light count, which\n * recompiles every shader on the device least able to. A coarse pointer alone would catch tablets;\n * 600px is the narrowest tablet.\n */\nexport function isPhone(): boolean {\n\tconst coarse = window.matchMedia?.('(pointer: coarse)').matches ?? false;\n\treturn coarse && Math.min(window.screen.width, window.screen.height) < 600;\n}\n\nexport function applyDefaults(options: ThreeInitializerOptions): ResolvedOptions {\n\tconst scale = options.sceneScale || 'm';\n\n\t// Geometry is always in meters; sceneScale only changes camera/light/grid magnitudes.\n\tconst scaleDefaults = {\n\t\tmm: {\n\t\t\tcameraDistance: 20,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 100,\n\t\t\tlightDistance: 10,\n\t\t\tlightHeight: 20,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 100,\n\t\t\tscaleFactor: 1000\n\t\t},\n\t\tcm: {\n\t\t\tcameraDistance: 20,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 100,\n\t\t\tlightDistance: 25,\n\t\t\tlightHeight: 50,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 100,\n\t\t\tscaleFactor: 100\n\t\t},\n\t\tm: {\n\t\t\tcameraDistance: 10,\n\t\t\tnear: 0.01,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 50,\n\t\t\tlightDistance: 25,\n\t\t\tlightHeight: 50,\n\t\t\tminDistance: 0.001,\n\t\t\tshadowSize: 100,\n\t\t\tscaleFactor: 1\n\t\t},\n\t\tinches: {\n\t\t\tcameraDistance: 15,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 80,\n\t\t\tlightDistance: 20,\n\t\t\tlightHeight: 40,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 80,\n\t\t\tscaleFactor: 39.37\n\t\t},\n\t\tfeet: {\n\t\t\tcameraDistance: 8,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 40,\n\t\t\tlightDistance: 15,\n\t\t\tlightHeight: 30,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 60,\n\t\t\tscaleFactor: 3.28084\n\t\t}\n\t};\n\n\tconst defaults = scaleDefaults[scale];\n\n\t// The look seeds lighting/material defaults (tone mapping, AO, IBL, fill), ranked below explicit\n\t// per-field options but above the plain defaults; it never touches edges/grid.\n\tconst look = options.look ?? DEFAULT_LOOK;\n\tconst lowPower = options.render?.lowPower ?? isPhone();\n\tconst preset = lookPreset(look, lowPower);\n\n\treturn {\n\t\tsceneScale: scale,\n\t\tlook,\n\t\tcamera: {\n\t\t\t// Default 3/4 iso (behind-left, above), derived from the scene up axis so a Y-up scene still\n\t\t\t// gets an overhead iso rather than a below-horizon view. cameraDistance*sqrt(3) preserves the\n\t\t\t// orbit radius of the old per-component (-d,-d,d) vector so this doesn't rezoom every scene.\n\t\t\tposition:\n\t\t\t\toptions.camera?.position ||\n\t\t\t\tisoOffset(\n\t\t\t\t\toptions.environment?.sceneUp ?? defaultUp,\n\t\t\t\t\tdefaults.cameraDistance * Math.sqrt(3)\n\t\t\t\t),\n\t\t\tfov: options.camera?.fov || 20,\n\t\t\tnear: options.camera?.near || defaults.near,\n\t\t\tfar: options.camera?.far || defaults.far,\n\t\t\ttarget: options.camera?.target || new THREE.Vector3(0, 0, 0),\n\t\t\tdynamicNear: options.camera?.dynamicNear ?? true\n\t\t},\n\t\tlighting: {\n\t\t\tenableSunlight: options.lighting?.enableSunlight ?? true,\n\t\t\tsunlightIntensity: options.lighting?.sunlightIntensity ?? preset.sunlightIntensity,\n\t\t\t// Expressed in the scene basis so the sun stays overhead in any up convention.\n\t\t\tsunlightPosition:\n\t\t\t\toptions.lighting?.sunlightPosition ||\n\t\t\t\tsunOffset(\n\t\t\t\t\toptions.environment?.sceneUp ?? defaultUp,\n\t\t\t\t\tdefaults.lightDistance,\n\t\t\t\t\tdefaults.lightHeight\n\t\t\t\t),\n\t\t\tambientLightColor: options.lighting?.ambientLightColor || new THREE.Color(0x404040),\n\t\t\tambientLightIntensity: options.lighting?.ambientLightIntensity ?? preset.ambientIntensity,\n\t\t\tsunlightColor: options.lighting?.sunlightColor || 0xffffff,\n\t\t\t// A positive hemisphereIntensity is what actually creates the light in setupLighting.\n\t\t\tenableHemisphereLight:\n\t\t\t\toptions.lighting?.enableHemisphereLight ?? preset.hemisphereIntensity > 0,\n\t\t\themisphereSkyColor: options.lighting?.hemisphereSkyColor ?? 0xdfe6ff,\n\t\t\themisphereGroundColor: options.lighting?.hemisphereGroundColor ?? 0x6b5f52,\n\t\t\themisphereIntensity: options.lighting?.hemisphereIntensity ?? preset.hemisphereIntensity\n\t\t},\n\t\tenvironment: {\n\t\t\thdrPath: options.environment?.hdrPath || '/baseHDR.hdr',\n\t\t\tbackgroundColor: options.environment?.backgroundColor || new THREE.Color(0xf0f0f0),\n\t\t\tenableEnvironmentLighting: options.environment?.enableEnvironmentLighting ?? true,\n\t\t\tsceneUp: options.environment?.sceneUp || defaultUp,\n\t\t\tshowEnvironment: options.environment?.showEnvironment ?? false,\n\t\t\tenvironmentIntensity: options.environment?.environmentIntensity ?? preset.environmentIntensity\n\t\t},\n\t\tfloor: {\n\t\t\tenabled: options.floor?.enabled ?? false,\n\t\t\tsize: options.floor?.size || defaults.floorSize,\n\t\t\tcolor: options.floor?.color || new THREE.Color(0x808080),\n\t\t\troughness: options.floor?.roughness ?? 0.7,\n\t\t\tmetalness: options.floor?.metalness ?? 0.0,\n\t\t\treceiveShadow: options.floor?.receiveShadow ?? true\n\t\t},\n\t\trender: {\n\t\t\tenableShadows: options.render?.enableShadows ?? true,\n\t\t\tshadowMapSize: options.render?.shadowMapSize || 1024,\n\t\t\tantialias: options.render?.antialias ?? true,\n\t\t\tpixelRatio: options.render?.pixelRatio || Math.min(window.devicePixelRatio, 2),\n\t\t\t// ?? not ||: an explicit NoToneMapping (0) must be honoured, not fall through as falsy.\n\t\t\ttoneMapping: options.render?.toneMapping ?? preset.toneMapping,\n\t\t\ttoneMappingExposure: options.render?.toneMappingExposure ?? preset.toneMappingExposure,\n\t\t\tpreserveDrawingBuffer: options.render?.preserveDrawingBuffer ?? false,\n\t\t\tlowPower,\n\t\t\tambientOcclusion: options.render?.ambientOcclusion ?? preset.ambientOcclusion,\n\t\t\taoIntensity: options.render?.aoIntensity ?? 1,\n\t\t\tbloom: options.render?.bloom ?? preset.bloom,\n\t\t\t// Default caps AO buffers at 1x — biggest lever on GTAO cost at high DPI.\n\t\t\taoPixelRatio: options.render?.aoPixelRatio ?? 1,\n\t\t\tonDemand: options.render?.onDemand ?? true\n\t\t},\n\t\tcontrols: {\n\t\t\tenableDamping: options.controls?.enableDamping ?? false,\n\t\t\tdampingFactor: options.controls?.dampingFactor || 0.05,\n\t\t\tautoRotate: options.controls?.autoRotate ?? false,\n\t\t\tautoRotateSpeed: options.controls?.autoRotateSpeed || 0.5,\n\t\t\tenableZoom: options.controls?.enableZoom ?? true,\n\t\t\tenablePan: options.controls?.enablePan ?? true,\n\t\t\tminDistance: options.controls?.minDistance || defaults.minDistance,\n\t\t\tmaxDistance: options.controls?.maxDistance || Infinity\n\t\t},\n\t\tgrid: {\n\t\t\t// Mirrors createGrid's own defaults so the two never drift.\n\t\t\tenabled: options.grid?.enabled ?? false,\n\t\t\tcellSize: options.grid?.cellSize ?? 1,\n\t\t\tmajorEvery: options.grid?.majorEvery ?? 10,\n\t\t\tcellColor: options.grid?.cellColor ?? 0x888888,\n\t\t\tmajorColor: options.grid?.majorColor ?? 0x444444,\n\t\t\tfadeDistance: options.grid?.fadeDistance ?? 100,\n\t\t\t// Orthogonal to the scene up axis: Z-up Rhino -> 'z', Y-up -> 'y'.\n\t\t\tplane: options.grid?.plane ?? upToAxis(options.environment?.sceneUp ?? defaultUp)\n\t\t},\n\t\tgizmo: {\n\t\t\tenabled: options.gizmo?.enabled ?? false\n\t\t},\n\t\tedges: {\n\t\t\t// Mirrors addEdges' own defaults so the two never drift.\n\t\t\tenabled: options.edges?.enabled ?? false,\n\t\t\t// Undefined lets addEdges derive each mesh's edge color from its own surface material.\n\t\t\tcolor: options.edges?.color,\n\t\t\tdarken: options.edges?.darken,\n\t\t\twidth: options.edges?.width ?? 1.5,\n\t\t\tthresholdAngle: options.edges?.thresholdAngle ?? 44,\n\t\t\tdistanceFade: options.edges?.distanceFade ?? true,\n\t\t\t// Passed through undefined on purpose: the caps' canonical defaults live in\n\t\t\t// `edges/options.ts` (resolveOptions), and applyEdges forwards these straight to it.\n\t\t\t// Restating them here would be a second copy free to drift from the real one.\n\t\t\tmaxTriangles: options.edges?.maxTriangles,\n\t\t\tmaxSegments: options.edges?.maxSegments,\n\t\t\tmaxOverlays: options.edges?.maxOverlays,\n\t\t\t// Read by init-three's updateEdgeFallback, which only checks for an explicit `false`.\n\t\t\tscreenSpaceFallback: options.edges?.screenSpaceFallback\n\t\t},\n\t\tmeasure: {\n\t\t\t// Visual defaults live in createMeasureTool; these pass through undefined to it.\n\t\t\tenabled: options.measure?.enabled ?? false,\n\t\t\tsnapPixels: options.measure?.snapPixels,\n\t\t\tcolor: options.measure?.color,\n\t\t\tlabelClassName: options.measure?.labelClassName,\n\t\t\tdisplayUnit: options.measure?.displayUnit,\n\t\t\tformat: options.measure?.format\n\t\t},\n\t\tevents: {\n\t\t\tonBackgroundClicked: options.events?.onBackgroundClicked,\n\t\t\tonObjectSelected: options.events?.onObjectSelected,\n\t\t\tonMeshMetadataClicked: options.events?.onMeshMetadataClicked,\n\t\t\tonMeshDoubleClicked: options.events?.onMeshDoubleClicked,\n\t\t\tselectionColor: options.events?.selectionColor || '#ff0000',\n\t\t\tenableEventHandlers: options.events?.enableEventHandlers ?? true,\n\t\t\tenableKeyboardControls: options.events?.enableKeyboardControls ?? true,\n\t\t\tenableClickToFocus: options.events?.enableClickToFocus ?? true,\n\t\t\tenableDoubleClickZoom: options.events?.enableDoubleClickZoom ?? true,\n\t\t\tonReady: options.events?.onReady,\n\t\t\tonFrame: options.events?.onFrame\n\t\t},\n\t\tonMaxAnisotropy: options.onMaxAnisotropy\n\t};\n}\n","import * as THREE from 'three';\n\nimport { defaultUp, type ResolvedOptions } from './defaults.js';\n\nexport type SceneLights = {\n\tambient: THREE.AmbientLight;\n\t/** Null unless `lighting.enableHemisphereLight`. */\n\themisphere: THREE.HemisphereLight | null;\n\t/** Null when sunlight or shadows are disabled. */\n\tsun: THREE.DirectionalLight | null;\n};\n\nconst SHADOW_NORMAL_BIAS = 0.02;\n\n/**\n * In shadow-map texels, and the map always spans the model, so the penumbra stays about 1.5% of the\n * model at any scale. A sharp edge reads as a sticker; a real sky never gives one.\n */\nconst SUN_SHADOW_BLUR = 6;\n\n/** Sky and bounce always fill a shadow in, so a fully dark one looks painted on. */\nconst SUN_SHADOW_DENSITY = 0.62;\n\n/** Survives a solve with the sun, which would otherwise aim at a target outside the scene. */\nexport const SUN_TARGET_ID = 'sun-target';\n\nexport function setupLighting(scene: THREE.Scene, config: ResolvedOptions): SceneLights {\n\tconst ambient = new THREE.AmbientLight(\n\t\tconfig.lighting.ambientLightColor,\n\t\tconfig.lighting.ambientLightIntensity\n\t);\n\tscene.add(ambient);\n\n\t// HemisphereLight defaults to +Y up, which is wrong for a Z-up scene — align to scene up instead.\n\tlet hemisphere: THREE.HemisphereLight | null = null;\n\tif (config.lighting.enableHemisphereLight) {\n\t\themisphere = new THREE.HemisphereLight(\n\t\t\tconfig.lighting.hemisphereSkyColor,\n\t\t\tconfig.lighting.hemisphereGroundColor,\n\t\t\tconfig.lighting.hemisphereIntensity\n\t\t);\n\t\tconst up = config.environment.sceneUp ?? defaultUp;\n\t\themisphere.position.copy(up);\n\t\tscene.add(hemisphere);\n\t}\n\n\tif (!config.lighting.enableSunlight) return { ambient, hemisphere, sun: null };\n\n\tconst sunlight = new THREE.DirectionalLight(\n\t\tconfig.lighting.sunlightColor ?? 0xffffff,\n\t\tconfig.lighting.sunlightIntensity\n\t);\n\tconst pos = config.lighting.sunlightPosition;\n\tif (pos) {\n\t\tsunlight.position.set(pos.x, pos.y, pos.z);\n\t}\n\n\tif (!config.render.enableShadows) {\n\t\tscene.add(sunlight);\n\t\treturn { ambient, hemisphere, sun: null };\n\t}\n\n\tsunlight.castShadow = true;\n\n\t// Frustum bounds are not set here — fitShadowToContent sizes them to scene content instead.\n\tsunlight.shadow.mapSize.width = config.render.shadowMapSize!;\n\tsunlight.shadow.mapSize.height = config.render.shadowMapSize!;\n\n\tsunlight.shadow.bias = -0.0001;\n\tsunlight.shadow.normalBias = SHADOW_NORMAL_BIAS;\n\tsunlight.shadow.radius = SUN_SHADOW_BLUR;\n\tsunlight.shadow.blurSamples = 12;\n\tsunlight.shadow.intensity = SUN_SHADOW_DENSITY;\n\n\tscene.add(sunlight);\n\t// A DirectionalLight aims at its target's world position, so the target must be in the scene\n\t// graph for its matrix to update.\n\tsunlight.target.userData.id = SUN_TARGET_ID;\n\tscene.add(sunlight.target);\n\treturn { ambient, hemisphere, sun: sunlight };\n}\n\n/**\n * Sizes a directional light's orthographic shadow frustum to the scene content's bounding sphere —\n * the dominant lever on shadow crispness. No-op on an empty or zero-size box: the frustum would\n * collapse to a point, and the sun would land on its target and lose its direction for good.\n */\nexport function fitShadowToContent(light: THREE.DirectionalLight, bounds: THREE.Box3): void {\n\tif (bounds.isEmpty()) return;\n\n\tconst center = bounds.getCenter(new THREE.Vector3());\n\t// Bounding-sphere radius keeps the frustum rotation-invariant; padded so grazing-angle casters\n\t// and VSM blur near the edges don't clip.\n\tconst radius = bounds.getSize(new THREE.Vector3()).length() * 0.5 * 1.2;\n\tif (radius === 0) return;\n\n\t// Scenes are in metres, so a fixed offset that suits a building erases a desk lamp's whole shadow.\n\tlight.shadow.normalBias = Math.min(SHADOW_NORMAL_BIAS, radius * 0.0015);\n\n\tconst cam = light.shadow.camera;\n\tcam.left = -radius;\n\tcam.right = radius;\n\tcam.top = radius;\n\tcam.bottom = -radius;\n\n\t// The configured position is a direction, not a place: left where it is, the sun ends up inside a\n\t// building-sized scene and everything behind it stops casting. Sits outside the sphere instead.\n\tconst direction = light.position.clone().sub(light.target.position).normalize();\n\tlight.target.position.copy(center);\n\tlight.target.updateMatrixWorld();\n\tconst lightDistance = radius * 2;\n\tlight.position.copy(center).addScaledVector(direction, lightDistance);\n\tlight.updateMatrixWorld();\n\n\tcam.near = lightDistance - radius;\n\tcam.far = lightDistance + radius;\n\tcam.updateProjectionMatrix();\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport { cachedSourceOf, computeCombinedBoundingBox, disposeObjectTree } from '../shared/index.js';\nimport { isHostOwned } from './scene-ownership.js';\nimport { applyReflectionEnvironment } from './scene-setup/reflection-environment.js';\nimport { SUN_TARGET_ID } from './scene-setup/setup-lighting.js';\nimport { isoOffset } from './up-axis';\n\nconst CAMERA_CONFIG = {\n\tHUGE_THRESHOLD: 10000,\n\tLARGE_THRESHOLD: 1000,\n\tSCALE_RATIO_THRESHOLD: 100,\n\tNEAR_PLANE_FACTOR: {\n\t\tTINY: 0.0001,\n\t\tSMALL: 0.001,\n\t\tNORMAL: 0.01\n\t},\n\tFAR_PLANE_FACTOR: {\n\t\tHUGE: 100,\n\t\tLARGE: 50,\n\t\tNORMAL: 20\n\t},\n\tINITIAL_DISTANCE_MULTIPLIER: 4\n};\n\n/** Replaces scene content with `meshes`, rescales the camera frustum to fit, and (first call only) positions the camera/controls. */\nexport function updateScene(\n\tscene: THREE.Scene,\n\tmeshes: THREE.Object3D[],\n\tcamera: THREE.PerspectiveCamera,\n\tcontrols: OrbitControls,\n\tinitialPositionSet: boolean\n) {\n\tclearScene(scene, cachedGeometriesOf(meshes));\n\n\tif (meshes.length === 0) return;\n\n\tmeshes.forEach((mesh) => {\n\t\tscene.add(mesh);\n\t\tapplyReflectionEnvironment(mesh, scene);\n\t});\n\n\tconst unionBoundingBox = computeCombinedBoundingBox(meshes);\n\tconst center = unionBoundingBox.getCenter(new THREE.Vector3());\n\tconst size = unionBoundingBox.getSize(new THREE.Vector3());\n\tconst maxDim = Math.max(size.x, size.y, size.z);\n\n\t// Rescaled every call, not just the first, so near/far stay well-conditioned when geometry\n\t// size changes drastically between solves.\n\tconst scaleRatio = maxDim / Math.min(size.x || 1, size.y || 1, size.z || 1);\n\n\tif (scaleRatio > CAMERA_CONFIG.SCALE_RATIO_THRESHOLD || maxDim > CAMERA_CONFIG.HUGE_THRESHOLD) {\n\t\tcamera.near = maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.TINY;\n\t\tcamera.far = maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.HUGE;\n\t} else if (maxDim > CAMERA_CONFIG.LARGE_THRESHOLD) {\n\t\tcamera.near = maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.SMALL;\n\t\tcamera.far = maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.LARGE;\n\t} else {\n\t\tcamera.near = Math.max(0.01, maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.NORMAL);\n\t\tcamera.far = Math.max(2000, maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.NORMAL);\n\t}\n\n\tcamera.updateProjectionMatrix();\n\n\t// Camera/controls are repositioned on first frame only. Zoom limits (min/maxDistance) are\n\t// deliberately NOT touched here: they're owned by the host via setupControls, and overwriting\n\t// them per solve would silently discard user-supplied configuration after the first update.\n\tif (!initialPositionSet) {\n\t\tconst distance = maxDim * CAMERA_CONFIG.INITIAL_DISTANCE_MULTIPLIER;\n\n\t\t// camera.up is already the configured sceneUp (initThree sets it before this runs), so the\n\t\t// iso offset stays consistent with whatever up-axis the viewer opened at.\n\t\tcamera.position.copy(center).add(isoOffset(camera.up, distance));\n\t\tcontrols.target.copy(center);\n\n\t\tcontrols.update();\n\t}\n}\n\n// Excluded from every content-bounds query: the grid is a huge plane that re-centers on the\n// camera each frame, so including it would make fit-to-view frame the camera's position instead\n// of the geometry.\nconst VIEWER_AID_IDS = new Set(['grid', 'floor', 'label-layer', 'measure', SUN_TARGET_ID]);\n\n/** True if `object` itself roots a viewer aid; {@link isViewerAid} also checks ancestors. */\nexport function isViewerAidRoot(object: THREE.Object3D): boolean {\n\treturn typeof object.userData.id === 'string' && VIEWER_AID_IDS.has(object.userData.id);\n}\n\n/** True if `object` or any ancestor is a viewer aid (grid/floor/labels/measure). */\nexport function isViewerAid(object: THREE.Object3D): boolean {\n\tlet current: THREE.Object3D | null = object;\n\twhile (current) {\n\t\tif (isViewerAidRoot(current)) return true;\n\t\tcurrent = current.parent;\n\t}\n\treturn false;\n}\n\n/**\n * Bounds of the scene's renderable content, excluding viewer aids (grid/floor/labels/measure).\n * Shared by fit-to-view, pick-threshold scaling, camera framing (`setView`), and shadow-frustum\n * fitting so they all measure exactly the same box.\n */\nexport function computeContentBounds(scene: THREE.Scene): THREE.Box3 {\n\t// Refresh world matrices once up front so expandByObject reads current transforms, regardless\n\t// of when the caller invokes this.\n\tscene.updateMatrixWorld(true);\n\tconst box = new THREE.Box3();\n\tscene.traverse((object) => {\n\t\tconst renderable = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tif (object.visible && !isViewerAid(object) && renderable.geometry) {\n\t\t\tbox.expandByObject(object);\n\t\t}\n\t});\n\treturn box;\n}\n\nfunction cachedGeometriesOf(objects: THREE.Object3D[]): Set<THREE.BufferGeometry> {\n\tconst cached = new Set<THREE.BufferGeometry>();\n\tfor (const root of objects) {\n\t\troot.traverse((object) => {\n\t\t\tconst geometry = (object as Partial<THREE.Mesh>).geometry;\n\t\t\tconst source = geometry && cachedSourceOf(geometry);\n\t\t\tif (source) cached.add(source);\n\t\t});\n\t}\n\treturn cached;\n}\n\nconst PERSISTENT_SCENE_IDS = new Set(['floor', 'grid', 'label-layer', SUN_TARGET_ID]);\n\n/** Frees the solve content. `keepUploaded` names the cached geometries the next content draws. */\nexport function clearScene(\n\tscene: THREE.Scene,\n\tkeepUploaded: ReadonlySet<THREE.BufferGeometry> = new Set()\n): void {\n\tconst kept: THREE.Object3D[] = [];\n\tconst removed: THREE.Object3D[] = [];\n\tfor (const object of scene.children) {\n\t\t// Removing the label-layer group here would orphan it: the CSS2D renderer only finds labels\n\t\t// by walking the live scene, so labels added afterwards would never render.\n\t\t// Host-added geometry (tagged by addUserGeometry, either plain 'user' or an app: scope)\n\t\t// persists across solves so it isn't lost when compute content is replaced.\n\t\t// Lights belong to the viewer, not the solve: losing them leaves only the environment map, with\n\t\t// no sun, no shadows and flat upward faces, and nothing errors.\n\t\tconst isLight = (object as Partial<THREE.Light>).isLight === true;\n\t\tif (isLight || PERSISTENT_SCENE_IDS.has(object.userData.id) || isHostOwned(object)) {\n\t\t\tkept.push(object);\n\t\t} else removed.push(object);\n\t}\n\n\t// One rebuild of the child list, not removeFromParent per object: that is indexOf + splice\n\t// each, O(n²), 3.5 s at 100k meshes. The events are the ones Object3D.remove dispatches.\n\tscene.children.length = 0;\n\tfor (const object of kept) scene.children.push(object);\n\tfor (const object of removed) {\n\t\t// Edge overlays are children of the meshes they outline, so this traversal disposes their\n\t\t// line geometries too — each overlay owns its geometry outright.\n\t\tdisposeObjectTree(object, { keepUploaded });\n\t\tobject.parent = null;\n\t\tobject.dispatchEvent({ type: 'removed' });\n\t\tscene.dispatchEvent({ type: 'childremoved', child: object });\n\t}\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport { computeContentBounds } from './three-helpers';\nimport { buildUpBasis } from './up-axis';\n\n/**\n * Runtime camera control: preset views, perspective⇄orthographic toggle, rotate lock.\n *\n * Centralized because projection switching swaps the camera object that OrbitControls drives, the\n * render loop renders, resize reshapes, and the raycaster picks with — {@link getActiveCamera} is\n * the one source of truth for all four call sites.\n *\n * Orthographic mirrors perspective's position/target with a frustum derived from perspective's FOV\n * and distance, so switching projections doesn't visually jump.\n */\n\nexport type ViewPreset = 'top' | 'bottom' | 'front' | 'back' | 'left' | 'right' | 'iso';\n\nexport type CameraProjection = 'perspective' | 'orthographic';\n\nexport interface CameraController {\n\t/** Swaps identity on {@link setProjection}. */\n\tgetActiveCamera(): THREE.Camera;\n\tgetProjection(): CameraProjection;\n\tsetProjection(projection: CameraProjection): void;\n\ttoggleProjection(): CameraProjection;\n\tsetView(preset: ViewPreset, animate?: boolean): void;\n\t/**\n\t * Frame current content from an explicit world-space direction (target → camera) instead of a\n\t * named preset — used by the nav-cube, whose clicked axis is a world axis.\n\t */\n\tsetViewDirection(direction: THREE.Vector3, animate?: boolean): void;\n\t/** Frame a world-space box from the current view direction. No-op on an empty box. */\n\tframeBounds(box: THREE.Box3, animate?: boolean): void;\n\tsetRotateEnabled(enabled: boolean): void;\n\tisRotateEnabled(): boolean;\n\tupdateAspect(width: number, height: number): void;\n\t/** Cancel any in-flight camera tween. Call on viewer teardown so ticks can't touch disposed controls. */\n\tdispose(): void;\n}\n\ninterface CameraControllerDeps {\n\tscene: THREE.Scene;\n\tperspective: THREE.PerspectiveCamera;\n\tcontrols: OrbitControls;\n\tonActiveCameraChange: (camera: THREE.Camera) => void;\n\t/** Drives presets, ortho camera up, and iso direction. Falls back to `perspective.up`. */\n\tup?: THREE.Vector3;\n}\n\n/**\n * Seven preset view directions (target → camera, unit vectors), derived from `up` rather than a\n * fixed Y-up table so Top/Front/… stay meaningful for Z-up Rhino scenes.\n *\n * `buildUpBasis`'s `forward` is camera→model; these are camera positions relative to target, so\n * \"front\" is `-forward`. Flipping this puts the camera behind the model and swaps left/right.\n */\nfunction buildViewDirections(up: THREE.Vector3): Record<ViewPreset, THREE.Vector3> {\n\tconst { up: u, forward, right } = buildUpBasis(up);\n\n\t// Camera positions are opposite the look direction: Rhino's Front looks along +Y from -Y.\n\tconst frontPosition = forward.clone().negate();\n\tconst rightPosition = right.clone();\n\n\treturn {\n\t\ttop: u.clone(),\n\t\tbottom: u.clone().negate(),\n\t\tfront: frontPosition.clone(),\n\t\tback: frontPosition.clone().negate(),\n\t\tright: rightPosition.clone(),\n\t\tleft: rightPosition.clone().negate(),\n\t\tiso: frontPosition\n\t\t\t.clone()\n\t\t\t.multiplyScalar(1.2)\n\t\t\t.add(rightPosition.clone())\n\t\t\t.add(u.clone())\n\t\t\t.normalize()\n\t};\n}\n\nexport function createCameraController(deps: CameraControllerDeps): CameraController {\n\tconst { scene, perspective, controls, onActiveCameraChange } = deps;\n\n\tconst up = (deps.up ?? perspective.up).clone().normalize();\n\tconst VIEW_DIRECTIONS = buildViewDirections(up);\n\n\tconst ortho = new THREE.OrthographicCamera(-1, 1, 1, -1, perspective.near, perspective.far);\n\tortho.up.copy(up);\n\n\tlet projection: CameraProjection = 'perspective';\n\tlet aspect = perspective.aspect;\n\n\tconst active = (): THREE.Camera => (projection === 'perspective' ? perspective : ortho);\n\n\t// Starting a new tween cancels any prior one — two loops would otherwise fight over the camera.\n\tlet activeTween: TweenHandle | null = null;\n\tconst cancelTween = () => {\n\t\tactiveTween?.cancel();\n\t\tactiveTween = null;\n\t};\n\n\t// Sizes the ortho frustum to match perspective's apparent size at the current distance.\n\tconst syncOrthoFrustum = () => {\n\t\t// Measure whichever camera is live: while ortho is active, OrbitControls moves ortho's\n\t\t// position (only its zoom changes), leaving perspective's distance stale.\n\t\tconst reference = projection === 'orthographic' ? ortho : perspective;\n\t\tconst distance = reference.position.distanceTo(controls.target);\n\t\tconst halfH = distance * Math.tan((perspective.fov * Math.PI) / 360);\n\t\tconst halfW = halfH * aspect;\n\t\tortho.left = -halfW;\n\t\tortho.right = halfW;\n\t\tortho.top = halfH;\n\t\tortho.bottom = -halfH;\n\t\tortho.near = perspective.near;\n\t\tortho.far = perspective.far;\n\t\tortho.updateProjectionMatrix();\n\t};\n\n\tconst setProjection = (next: CameraProjection) => {\n\t\tif (next === projection) return;\n\t\t// A tween mid-flight would keep lerping the OLD active camera after the swap.\n\t\tcancelTween();\n\n\t\tif (next === 'orthographic') {\n\t\t\tortho.position.copy(perspective.position);\n\t\t\tortho.up.copy(perspective.up);\n\t\t\tortho.lookAt(controls.target);\n\t\t\t// OrbitControls dollies ortho via `zoom`, not position — reset to 1 so a leftover zoom\n\t\t\t// from a prior 2D session doesn't double up with the freshly-derived frustum.\n\t\t\tortho.zoom = 1;\n\t\t\tsyncOrthoFrustum();\n\t\t} else {\n\t\t\t// Convert ortho zoom back to perspective DISTANCE (halfH / tan(fov/2)) — copying position\n\t\t\t// alone would discard any zooming done in 2D.\n\t\t\tconst halfH = (ortho.top - ortho.bottom) / (2 * ortho.zoom);\n\t\t\tconst distance = halfH / Math.tan((perspective.fov * Math.PI) / 360);\n\t\t\tconst direction = ortho.position.clone().sub(controls.target);\n\t\t\tif (direction.lengthSq() < 1e-12) direction.copy(up);\n\t\t\tdirection.normalize();\n\t\t\tperspective.position.copy(controls.target).add(direction.multiplyScalar(distance));\n\t\t}\n\n\t\tprojection = next;\n\t\tcontrols.object = active();\n\t\tcontrols.update();\n\t\tonActiveCameraChange(active());\n\t};\n\n\t// Positions the active camera along `direction` at the distance fitting `maxDim`, retargeting\n\t// controls at `center`. Ortho zoom resets and the frustum re-derives via syncOrthoFrustum —\n\t// position alone wouldn't change an orthographic view's apparent size.\n\tconst frame = (\n\t\tcenter: THREE.Vector3,\n\t\tmaxDim: number,\n\t\tdirection: THREE.Vector3,\n\t\tanimate: boolean\n\t) => {\n\t\tconst fov = perspective.fov * (Math.PI / 180);\n\t\tconst distance = (maxDim / (2 * Math.tan(fov / 2))) * 1.5;\n\n\t\tconst dir = nudgeOffPole(direction, up);\n\t\tconst toPosition = center.clone().add(dir.clone().multiplyScalar(distance));\n\n\t\tconst cam = active();\n\t\t// Reset zoom before re-deriving the frustum, else it multiplies in and defeats the fit.\n\t\tif (projection === 'orthographic') ortho.zoom = 1;\n\n\t\tcancelTween();\n\t\tif (animate) {\n\t\t\tactiveTween = animateMove(cam, controls, toPosition, center, () => {\n\t\t\t\tif (projection === 'orthographic') syncOrthoFrustum();\n\t\t\t});\n\t\t} else {\n\t\t\tcam.position.copy(toPosition);\n\t\t\tcontrols.target.copy(center);\n\t\t\tif (projection === 'orthographic') syncOrthoFrustum();\n\t\t\tcontrols.update();\n\t\t}\n\t};\n\n\tconst setViewDirection = (direction: THREE.Vector3, animate = true) => {\n\t\tconst box = computeContentBounds(scene);\n\t\tconst center = box.isEmpty() ? controls.target.clone() : box.getCenter(new THREE.Vector3());\n\t\tconst size = box.isEmpty() ? new THREE.Vector3(1, 1, 1) : box.getSize(new THREE.Vector3());\n\t\tconst maxDim = Math.max(size.x, size.y, size.z) || 1;\n\t\tframe(center, maxDim, direction, animate);\n\t};\n\n\tconst frameBounds = (box: THREE.Box3, animate = true) => {\n\t\tif (box.isEmpty()) return;\n\t\tconst center = box.getCenter(new THREE.Vector3());\n\t\tconst size = box.getSize(new THREE.Vector3());\n\t\tconst maxDim = Math.max(size.x, size.y, size.z) || 1;\n\t\t// Keep the user's current viewing direction; only the distance/target change.\n\t\tconst direction = active().position.clone().sub(controls.target);\n\t\tif (direction.lengthSq() < 1e-12) direction.copy(VIEW_DIRECTIONS.iso);\n\t\tframe(center, maxDim, direction.normalize(), animate);\n\t};\n\n\tconst setView = (preset: ViewPreset, animate = true) => {\n\t\tsetViewDirection(VIEW_DIRECTIONS[preset], animate);\n\t};\n\n\tconst setRotateEnabled = (enabled: boolean) => {\n\t\tcontrols.enableRotate = enabled;\n\t};\n\n\tconst updateAspect = (width: number, height: number) => {\n\t\taspect = height === 0 ? aspect : width / height;\n\t\tif (projection === 'orthographic') syncOrthoFrustum();\n\t};\n\n\treturn {\n\t\tgetActiveCamera: active,\n\t\tgetProjection: () => projection,\n\t\tsetProjection,\n\t\ttoggleProjection: () => {\n\t\t\tsetProjection(projection === 'perspective' ? 'orthographic' : 'perspective');\n\t\t\treturn projection;\n\t\t},\n\t\tsetView,\n\t\tsetViewDirection,\n\t\tframeBounds,\n\t\tsetRotateEnabled,\n\t\tisRotateEnabled: () => controls.enableRotate,\n\t\tupdateAspect,\n\t\tdispose: cancelTween\n\t};\n}\n\n/**\n * Nudges a top/bottom view direction a ~0.5° tilt off the up axis; other presets pass through\n * unchanged. Looking exactly down `up` is an OrbitControls singularity: camera direction coincides\n * with `camera.up`, azimuth is undefined, and the first drag snaps the view.\n *\n * At the pole, `camera.up` can't define roll, so the tilt direction does instead. Both poles lean\n * toward `-forward` to reproduce Rhino's convention (Top has +forward at screen-top; Bottom mirrors\n * about the horizontal axis, matching Rhino where the far side reads backwards too) — leaning the\n * poles opposite ways also mirrors correctly, but rolled 180° from Rhino.\n */\nfunction nudgeOffPole(dir: THREE.Vector3, up: THREE.Vector3): THREE.Vector3 {\n\tconst { up: u, forward } = buildUpBasis(up);\n\tconst d = dir.clone().normalize();\n\tif (Math.abs(d.dot(u)) < 0.9999) return dir;\n\n\tconst inPlane = forward.clone().negate();\n\n\tconst tilt = (0.5 * Math.PI) / 180;\n\treturn d\n\t\t.multiplyScalar(Math.cos(tilt))\n\t\t.add(inPlane.multiplyScalar(Math.sin(tilt)))\n\t\t.normalize();\n}\n\nconst easeOut = (t: number) => 1 - Math.pow(1 - t, 3);\n\n/** Handle to a running camera tween, so callers can stop it (new move, projection swap, teardown). */\ninterface TweenHandle {\n\tcancel(): void;\n}\n\n/** Tweens camera position + controls target; returns a cancel handle for teardown/preemption. */\nfunction animateMove(\n\tcamera: THREE.Camera,\n\tcontrols: OrbitControls,\n\ttoPosition: THREE.Vector3,\n\ttoTarget: THREE.Vector3,\n\tonTick: () => void,\n\tdurationMs = 250\n): TweenHandle {\n\tconst fromPosition = camera.position.clone();\n\tconst fromTarget = controls.target.clone();\n\tconst startTime = performance.now();\n\tlet rafId: number | null = null;\n\n\tconst tick = () => {\n\t\trafId = null;\n\t\tconst t = easeOut(Math.min((performance.now() - startTime) / durationMs, 1));\n\t\tcamera.position.lerpVectors(fromPosition, toPosition, t);\n\t\tcontrols.target.lerpVectors(fromTarget, toTarget, t);\n\t\tonTick();\n\t\tcontrols.update();\n\t\tif (t < 1) rafId = requestAnimationFrame(tick);\n\t};\n\n\trafId = requestAnimationFrame(tick);\n\n\treturn {\n\t\tcancel: () => {\n\t\t\tif (rafId !== null) {\n\t\t\t\tcancelAnimationFrame(rafId);\n\t\t\t\trafId = null;\n\t\t\t}\n\t\t}\n\t};\n}\n","import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js';\n\n// ============================================================================\n// Line geometry construction\n// ============================================================================\n\n// No cache here. An earlier WeakMap keyed per source BufferGeometry leaked ~400 live GPU entries\n// where 8 were expected — entries never vanished with their source — and measured 0/80 hits in a\n// real scrubbing loop. Every overlay builds and owns its own line geometry.\n\nexport interface EdgeGeometryEntry {\n\tgeometry: LineSegmentsGeometry;\n\tsegmentCount: number;\n\t/** {@link SPACING_PERCENTILE} quantile of segment length; drives the density fade in overlay.ts. */\n\tedgeSpacing: number;\n}\n\n// A low quantile (not mean) tracks the fine detail: real parts mix a few long silhouette edges\n// with many short ones at wildly different scale (e.g. 1mm laminations on a 10m part) — an\n// average would sit between the two and never trigger the fade for either.\nconst SPACING_PERCENTILE = 0.15;\n\n// Stride sampling keeps this O(1) on millions of segments.\nconst SPACING_SAMPLE_LIMIT = 4096;\n\nfunction edgeSpacingOf(segments: Float32Array): number {\n\tconst segmentCount = Math.floor(segments.length / 6);\n\tif (segmentCount === 0) return Infinity;\n\n\tconst stride = Math.max(1, Math.ceil(segmentCount / SPACING_SAMPLE_LIMIT));\n\tconst lengths: number[] = [];\n\tfor (let s = 0; s < segmentCount; s += stride) {\n\t\tconst i = s * 6;\n\t\tconst length = Math.hypot(\n\t\t\tsegments[i + 3] - segments[i],\n\t\t\tsegments[i + 4] - segments[i + 1],\n\t\t\tsegments[i + 5] - segments[i + 2]\n\t\t);\n\t\tif (length > 0) lengths.push(length);\n\t}\n\tif (lengths.length === 0) return Infinity;\n\n\tlengths.sort((a, b) => a - b);\n\treturn lengths[Math.min(lengths.length - 1, Math.floor(lengths.length * SPACING_PERCENTILE))]!;\n}\n\n// LineSegmentsGeometry adopts `segments` as its backing store without copying — treat it as\n// read-only from here on.\nexport function buildLineGeometry(segments: Float32Array): EdgeGeometryEntry {\n\tconst geometry = new LineSegmentsGeometry();\n\tgeometry.setPositions(segments);\n\treturn {\n\t\tgeometry,\n\t\tsegmentCount: segments.length / 6,\n\t\tedgeSpacing: edgeSpacingOf(segments)\n\t};\n}\n","/**\n * Dependency-free crease/boundary edge extraction — the hot core behind `addEdges`. Semantically\n * a drop-in for `THREE.EdgesGeometry(geometry, angle)` (same welding, crease test, boundary\n * handling, 3+-face quirks), but operates on raw typed arrays with numeric hashing instead of\n * three's per-vertex string keys, for speed and Worker portability — see the no-outer-captures\n * constraint on {@link extractEdgeSegments} itself.\n */\n\n/** Largest vertex count the fast path takes; callers fall back to `THREE.EdgesGeometry` above it. */\nexport const MAX_EXTRACT_VERTICES = 0x4000000; // 2^26\n\n/**\n * @param index - Triangle indices, or null for non-indexed soup.\n * @returns `segments`: endpoint pairs, same layout as `EdgesGeometry.attributes.position.array`.\n * `faces`: per segment, the triangle it came from, so a merged mesh can drop a hidden member's\n * edges without extracting again.\n * @throws If `positions` holds ≥ 2^26 vertices ({@link MAX_EXTRACT_VERTICES}) — callers fall\n * back to `THREE.EdgesGeometry`.\n */\nexport function extractEdgeSegments(\n\tpositions: Float32Array,\n\tindex: Uint32Array | Uint16Array | null,\n\tthresholdAngleDeg: number\n): { segments: Float32Array; faces: Uint32Array } {\n\t// No outer captures besides Math — this function is stringified via toString() to run inside\n\t// a Worker ({@link edgeExtractWorkerSource}). Don't reference anything outside this body.\n\tconst PRECISION = 1e4; // same quantization grid as THREE.EdgesGeometry\n\tconst ID_BITS = 0x4000000; // MAX_EXTRACT_VERTICES, restated: this body may not capture it\n\tconst thresholdDot = Math.cos((Math.PI / 180) * thresholdAngleDeg);\n\n\tconst vertexCount = positions.length / 3;\n\tif (vertexCount >= ID_BITS) {\n\t\tthrow new Error(`extractEdgeSegments: ${vertexCount} vertices exceeds 2^26 limit`);\n\t}\n\n\t// --- Weld vertices on the quantization grid → canonical id per vertex -------------------\n\t// Rounded coords stay float64 (huge coordinates stay exact where int32 would overflow); only\n\t// the hash truncates to int32 — equality always compares the exact float64 values.\n\tconst quantX = new Float64Array(vertexCount);\n\tconst quantY = new Float64Array(vertexCount);\n\tconst quantZ = new Float64Array(vertexCount);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tquantX[v] = Math.round(positions[3 * v] * PRECISION);\n\t\tquantY[v] = Math.round(positions[3 * v + 1] * PRECISION);\n\t\tquantZ[v] = Math.round(positions[3 * v + 2] * PRECISION);\n\t}\n\n\t// Open-addressed table (linear probing): slot → first vertex id seen at that grid point.\n\tlet capacity = 16;\n\twhile (capacity < vertexCount * 2) capacity <<= 1;\n\tconst mask = capacity - 1;\n\tconst table = new Int32Array(capacity).fill(-1);\n\tconst canonical = new Int32Array(vertexCount);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tlet slot =\n\t\t\t(Math.imul(quantX[v] | 0, 73856093) ^\n\t\t\t\tMath.imul(quantY[v] | 0, 19349663) ^\n\t\t\t\tMath.imul(quantZ[v] | 0, 83492791)) &\n\t\t\tmask;\n\t\tfor (;;) {\n\t\t\tconst existing = table[slot];\n\t\t\tif (existing === -1) {\n\t\t\t\ttable[slot] = v;\n\t\t\t\tcanonical[v] = v;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tquantX[existing] === quantX[v] &&\n\t\t\t\tquantY[existing] === quantY[v] &&\n\t\t\t\tquantZ[existing] === quantZ[v]\n\t\t\t) {\n\t\t\t\tcanonical[v] = existing;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tslot = (slot + 1) & mask;\n\t\t}\n\t}\n\n\t// --- Growable segment output ------------------------------------------------------------\n\tlet out = new Float32Array(6 * 1024);\n\tlet outLength = 0;\n\tlet outFaces = new Uint32Array(1024);\n\tconst emit = (i0: number, i1: number, face: number): void => {\n\t\tif (outLength + 6 > out.length) {\n\t\t\tconst grown = new Float32Array(out.length * 2);\n\t\t\tgrown.set(out);\n\t\t\tout = grown;\n\t\t\tconst grownFaces = new Uint32Array(out.length / 6);\n\t\t\tgrownFaces.set(outFaces);\n\t\t\toutFaces = grownFaces;\n\t\t}\n\t\toutFaces[outLength / 6] = face;\n\t\tout[outLength++] = positions[3 * i0];\n\t\tout[outLength++] = positions[3 * i0 + 1];\n\t\tout[outLength++] = positions[3 * i0 + 2];\n\t\tout[outLength++] = positions[3 * i1];\n\t\tout[outLength++] = positions[3 * i1 + 1];\n\t\tout[outLength++] = positions[3 * i1 + 2];\n\t};\n\n\t// --- Directed edges grouped by their canonical start vertex (CSR) -----------------------\n\t// Each row holds one cell per directed-edge occurrence leaving that vertex (about six), so a\n\t// lookup scans a handful of ints. A Map keyed per directed edge cost ~12M entries on a 4M-tri\n\t// mesh and dominated extraction (6.5 s against 0.4 s).\n\tconst triCount = (index ? index.length : vertexCount) / 3;\n\tconst rowStart = new Int32Array(vertexCount + 1);\n\tfor (let t = 0; t < triCount; t++) {\n\t\tconst a = canonical[index ? index[3 * t] : 3 * t];\n\t\tconst b = canonical[index ? index[3 * t + 1] : 3 * t + 1];\n\t\tconst c = canonical[index ? index[3 * t + 2] : 3 * t + 2];\n\t\tif (a === b || b === c || c === a) continue;\n\t\trowStart[a + 1]++;\n\t\trowStart[b + 1]++;\n\t\trowStart[c + 1]++;\n\t}\n\tfor (let v = 0; v < vertexCount; v++) rowStart[v + 1] += rowStart[v];\n\tconst cellTo = new Int32Array(rowStart[vertexCount]);\n\t{\n\t\tconst cursor = rowStart.slice(0, vertexCount);\n\t\tfor (let t = 0; t < triCount; t++) {\n\t\t\tconst a = canonical[index ? index[3 * t] : 3 * t];\n\t\t\tconst b = canonical[index ? index[3 * t + 1] : 3 * t + 1];\n\t\t\tconst c = canonical[index ? index[3 * t + 2] : 3 * t + 2];\n\t\t\tif (a === b || b === c || c === a) continue;\n\t\t\tcellTo[cursor[a]++] = b;\n\t\t\tcellTo[cursor[b]++] = c;\n\t\t\tcellTo[cursor[c]++] = a;\n\t\t}\n\t}\n\t// A directed edge's state lives in the first cell of its row that names its end vertex:\n\t// UNSEEN, MATCHED, or the pending slot it registered.\n\tconst UNSEEN = -2;\n\tconst MATCHED = -1;\n\tconst cellState = new Int32Array(cellTo.length).fill(UNSEEN);\n\tconst findCell = (from: number, to: number): number => {\n\t\tfor (let k = rowStart[from], end = rowStart[from + 1]; k < end; k++) {\n\t\t\tif (cellTo[k] === to) return k;\n\t\t}\n\t\treturn -1;\n\t};\n\n\tconst cornerOf = (t: number, k: number): number => (index ? index[3 * t + k] : 3 * t + k);\n\n\t// Face normal, computed exactly as Triangle.getNormal: normalize((c-b) × (a-b)). Pending edges\n\t// store only their corner and recompute this on a match: identical values, and 8 bytes per entry\n\t// instead of 40, which was ~480 MB at 8M triangles.\n\tconst normal = new Float64Array(3);\n\tconst faceNormal = (t: number): void => {\n\t\tconst i0 = cornerOf(t, 0);\n\t\tconst i1 = cornerOf(t, 1);\n\t\tconst i2 = cornerOf(t, 2);\n\t\tconst e0x = positions[3 * i2] - positions[3 * i1];\n\t\tconst e0y = positions[3 * i2 + 1] - positions[3 * i1 + 1];\n\t\tconst e0z = positions[3 * i2 + 2] - positions[3 * i1 + 2];\n\t\tconst e1x = positions[3 * i0] - positions[3 * i1];\n\t\tconst e1y = positions[3 * i0 + 1] - positions[3 * i1 + 1];\n\t\tconst e1z = positions[3 * i0 + 2] - positions[3 * i1 + 2];\n\t\tlet nx = e0y * e1z - e0z * e1y;\n\t\tlet ny = e0z * e1x - e0x * e1z;\n\t\tlet nz = e0x * e1y - e0y * e1x;\n\t\tconst lengthSq = nx * nx + ny * ny + nz * nz;\n\t\tif (lengthSq > 0) {\n\t\t\tconst inverseLength = 1 / Math.sqrt(lengthSq);\n\t\t\tnx *= inverseLength;\n\t\t\tny *= inverseLength;\n\t\t\tnz *= inverseLength;\n\t\t} else {\n\t\t\tnx = 0;\n\t\t\tny = 0;\n\t\t\tnz = 0;\n\t\t}\n\t\tnormal[0] = nx;\n\t\tnormal[1] = ny;\n\t\tnormal[2] = nz;\n\t};\n\n\t// A pending edge is the corner it starts at (3t + j; it runs to corner j+1 of face t) and the\n\t// cell holding its state. Most edges pair up, so about half the cells ever register; sized to\n\t// the mesh because a fixed start dominated small meshes, which come thousands to a batch.\n\tlet pendingCount = 0;\n\tlet pendingCorners = new Uint32Array(Math.max(16, cellTo.length >> 1));\n\tlet pendingCells = new Int32Array(pendingCorners.length);\n\tconst growPending = (): void => {\n\t\tconst corners = new Uint32Array(pendingCorners.length * 2);\n\t\tcorners.set(pendingCorners);\n\t\tpendingCorners = corners;\n\t\tconst cells = new Int32Array(corners.length);\n\t\tcells.set(pendingCells);\n\t\tpendingCells = cells;\n\t};\n\n\t// --- Walk triangles, pairing opposite-winding edges -------------------------------------\n\t// Mirrors EdgesGeometry: a directed edge a→b matches a pending b→a; on match the segment is\n\t// kept iff the face normals differ beyond the threshold, and the pending entry is marked\n\t// MATCHED (never re-registered as a→b) so a third face b→a registers anew. Pending entries\n\t// left at the end are boundary edges and always emitted.\n\tfor (let t = 0; t < triCount; t++) {\n\t\tconst i0 = cornerOf(t, 0);\n\t\tconst i1 = cornerOf(t, 1);\n\t\tconst i2 = cornerOf(t, 2);\n\t\tconst a = canonical[i0];\n\t\tconst b = canonical[i1];\n\t\tconst c = canonical[i2];\n\n\t\t// Degenerate on the quantization grid — skip, as EdgesGeometry does.\n\t\tif (a === b || b === c || c === a) continue;\n\n\t\tlet haveNormal = false;\n\t\tlet nx = 0;\n\t\tlet ny = 0;\n\t\tlet nz = 0;\n\t\tfor (let j = 0; j < 3; j++) {\n\t\t\tconst fromCanonical = j === 0 ? a : j === 1 ? b : c;\n\t\t\tconst toCanonical = j === 0 ? b : j === 1 ? c : a;\n\n\t\t\tconst reverseCell = findCell(toCanonical, fromCanonical);\n\t\t\tconst reverseSlot = reverseCell === -1 ? UNSEEN : cellState[reverseCell];\n\t\t\tif (reverseSlot >= 0) {\n\t\t\t\tif (!haveNormal) {\n\t\t\t\t\tfaceNormal(t);\n\t\t\t\t\tnx = normal[0];\n\t\t\t\t\tny = normal[1];\n\t\t\t\t\tnz = normal[2];\n\t\t\t\t\thaveNormal = true;\n\t\t\t\t}\n\t\t\t\tfaceNormal(Math.floor(pendingCorners[reverseSlot] / 3));\n\t\t\t\tconst dot = nx * normal[0] + ny * normal[1] + nz * normal[2];\n\t\t\t\tif (dot <= thresholdDot) {\n\t\t\t\t\temit(j === 0 ? i0 : j === 1 ? i1 : i2, j === 0 ? i1 : j === 1 ? i2 : i0, t);\n\t\t\t\t}\n\t\t\t\tcellState[reverseCell] = MATCHED;\n\t\t\t} else {\n\t\t\t\tconst forwardCell = findCell(fromCanonical, toCanonical);\n\t\t\t\tif (cellState[forwardCell] === UNSEEN) {\n\t\t\t\t\tif (pendingCount === pendingCorners.length) growPending();\n\t\t\t\t\tconst slot = pendingCount++;\n\t\t\t\t\tcellState[forwardCell] = slot;\n\t\t\t\t\tpendingCorners[slot] = 3 * t + j;\n\t\t\t\t\tpendingCells[slot] = forwardCell;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Unmatched edges are boundaries — always kept, in registration order ----------------\n\tfor (let slot = 0; slot < pendingCount; slot++) {\n\t\tif (cellState[pendingCells[slot]] !== slot) continue;\n\t\tconst corner = pendingCorners[slot];\n\t\tconst t = Math.floor(corner / 3);\n\t\tconst j = corner - 3 * t;\n\t\temit(cornerOf(t, j), cornerOf(t, j === 2 ? 0 : j + 1), t);\n\t}\n\n\treturn { segments: out.slice(0, outLength), faces: outFaces.slice(0, outLength / 6) };\n}\n\n/**\n * Worker source running {@link extractEdgeSegments} off the main thread. Protocol: receives\n * `{id, positions, index, thresholdAngle}`, replies `{id, segments, faces}` (buffers transferred) or\n * `{id, error}`.\n *\n * Relies on `extractEdgeSegments` stringifying to standalone code — guarded by a unit test that\n * evals this source in isolation.\n */\nexport function edgeExtractWorkerSource(): string {\n\treturn [\n\t\t`const extract = ${extractEdgeSegments.toString()};`,\n\t\t`self.onmessage = (event) => {`,\n\t\t` const { id, positions, index, thresholdAngle } = event.data;`,\n\t\t` try {`,\n\t\t` const { segments, faces } = extract(positions, index, thresholdAngle);`,\n\t\t` self.postMessage({ id, segments, faces }, [segments.buffer, faces.buffer]);`,\n\t\t` } catch (error) {`,\n\t\t` self.postMessage({ id, error: String((error && error.message) || error) });`,\n\t\t` }`,\n\t\t`};`\n\t].join('\\n');\n}\n","import * as THREE from 'three';\n\n/** Crisp boundary/crease edges overlaid on meshes. See the layer README for depth/perf strategy. */\nexport interface EdgeOptions {\n\t/** Default: each overlay derives its color from its own mesh's material (see {@link DEFAULT_EDGE_COLOR}). */\n\tcolor?: THREE.ColorRepresentation;\n\t/** How far to darken the derived edge color toward black, 0-1 (default 0.75). No-op when `color` is set. */\n\tdarken?: number;\n\t/** Edge thickness in CSS px. Default 1.5. */\n\twidth?: number;\n\t/** Crease angle in degrees; an edge survives only where its two faces differ by more. Default 44. */\n\tthresholdAngle?: number;\n\t/**\n\t * Fade an overlay out as its own edges crowd together on screen (default true). Edges draw at\n\t * constant pixel width, so dense edges (e.g. millimetre-pitch laminations on sheet goods) merge\n\t * into a dark smear at normal zoom; fading by edge density rather than mesh size catches that\n\t * while leaving sparsely-edged geometry fully drawn.\n\t */\n\tdistanceFade?: boolean;\n\t/**\n\t * Skip meshes above this triangle count entirely (default 8M). Extraction time and memory are\n\t * linear in triangles: 8M is about 0.7 s in the worker and a few hundred MB of scratch, and past it\n\t * the screen-space fallback approximates the look at constant cost. Skipped meshes are tagged\n\t * `userData.edgesSkipped = 'triangle-cap'`.\n\t */\n\tmaxTriangles?: number;\n\t/**\n\t * Above this many extracted segments (default 2M), an overlay drops the distance fade and renders\n\t * opaque instead — millions of blended fat-line quads are a fill-rate cliff; opaque ones aren't.\n\t */\n\tmaxSegments?: number;\n\t/**\n\t * Attach at most this many overlays per apply (default 1500); past it every remaining mesh is\n\t * skipped and tagged `userData.edgesSkipped = 'overlay-budget'`, which switches on the\n\t * screen-space fallback.\n\t *\n\t * This is the count cap, and it catches what {@link maxTriangles} structurally cannot. That one\n\t * is per mesh, so it only ever sees one mesh at a time and a 20-triangle mesh always clears it —\n\t * yet an IFC model arrives as thousands of such meshes, and each overlay is a separate\n\t * `LineSegments2` with its own draw call and per-frame resolution uniform. Cost tracks the number\n\t * of overlays, not the triangles inside any one of them: measured on a 6043-mesh IFC house, the\n\t * overlays alone were 60% of a 99ms frame.\n\t */\n\tmaxOverlays?: number;\n}\n\n/** Tag on edge overlays so pick/fit/clear logic can recognize and skip or dispose them. */\nexport const EDGE_USERDATA_KIND = 'edge-overlay';\n\n/** `userData.edgesSkipped` value; see {@link EdgeOptions.maxTriangles}. */\nexport const EDGES_SKIPPED_TRIANGLE_CAP = 'triangle-cap';\n\n/** `userData.edgesSkipped` value; see {@link EdgeOptions.maxOverlays}. */\nexport const EDGES_SKIPPED_OVERLAY_BUDGET = 'overlay-budget';\n\nexport const DEFAULT_EDGE_COLOR = 0x222222;\nconst DEFAULT_EDGE_WIDTH = 1.5;\nconst DEFAULT_THRESHOLD_ANGLE = 44;\nconst DEFAULT_DARKEN = 0.75;\nconst DEFAULT_MAX_TRIANGLES = 8_000_000;\nconst DEFAULT_MAX_SEGMENTS = 2_000_000;\n// Sits above the few hundred objects a Grasshopper solve produces (which must keep real overlays)\n// and below the thousands an imported building model does. At the measured ~6µs per object per\n// frame under orbit, 1500 overlays is ~9ms — already most of a 60fps frame, and the point where\n// the constant-cost screen-space pass wins outright.\nconst DEFAULT_MAX_OVERLAYS = 1_500;\n\n// Below this triangle count a worker round-trip would cost more than the extraction itself, so it\n// runs inline even on the async path.\nexport const INLINE_TRIANGLE_BUDGET = 25_000;\n\n// Fade band as mean on-screen gap between neighbouring edges, in px: opaque at/above\n// FADE_START_PX, gone at/below FADE_END_PX, linear between. Density-based rather than\n// mesh/bounding-sphere-based, because a big part with sub-pixel edge spacing still draws every\n// line at full width — the old bounding-sphere rule missed that (the sphere still covers most of\n// the viewport). Band sits just above 1px, where constant-width lines start visibly overlapping.\nexport const FADE_START_PX = 4;\nexport const FADE_END_PX = 1;\n\n// Units-only pull-forward, deliberately no slope (factor) term: a slope term scales with the\n// polygon's dZ/dpixel, huge at grazing angles, and applied to surfaces (the old strategy) it\n// pushed grazing faces back further than the mm-scale gaps between stacked parts — geometry\n// behind a wall then won the depth test and bled through the wall's own edges. A fixed\n// quantization-step bias on the lines instead lifts an edge off its own surface without reaching\n// across a gap to a neighbouring part.\n//\n// This bias is only safe as long as a depth ULP stays small, which is what near-plane.ts's dynamic\n// near-plane fitter guarantees. Weakening that fit makes this bias start to bleed.\nexport const EDGE_OFFSET_FACTOR = 0;\nexport const EDGE_OFFSET_UNITS = -1; // negative = toward the camera\n\nexport interface ResolvedOptions {\n\tforcedColor: THREE.Color | null;\n\tdarken: number;\n\twidth: number;\n\tthresholdAngle: number;\n\tdistanceFade: boolean;\n\tmaxTriangles: number;\n\tmaxSegments: number;\n\tmaxOverlays: number;\n}\n\nexport function resolveOptions(options: EdgeOptions): ResolvedOptions {\n\treturn {\n\t\tforcedColor: options.color != null ? new THREE.Color(options.color) : null,\n\t\tdarken: THREE.MathUtils.clamp(options.darken ?? DEFAULT_DARKEN, 0, 1),\n\t\twidth: options.width ?? DEFAULT_EDGE_WIDTH,\n\t\tthresholdAngle: options.thresholdAngle ?? DEFAULT_THRESHOLD_ANGLE,\n\t\tdistanceFade: options.distanceFade ?? true,\n\t\tmaxTriangles: options.maxTriangles ?? DEFAULT_MAX_TRIANGLES,\n\t\tmaxSegments: options.maxSegments ?? DEFAULT_MAX_SEGMENTS,\n\t\tmaxOverlays: options.maxOverlays ?? DEFAULT_MAX_OVERLAYS\n\t};\n}\n","import * as THREE from 'three';\n\nimport {\n\tMAX_EXTRACT_VERTICES,\n\tedgeExtractWorkerSource,\n\textractEdgeSegments\n} from '../edge-extract.js';\nimport { INLINE_TRIANGLE_BUDGET } from './options.js';\n\n// ============================================================================\n// Segment extraction — fast path, worker offload\n// ============================================================================\n\nexport function triangleCountOf(geometry: THREE.BufferGeometry): number {\n\tconst position = geometry.getAttribute('position');\n\tif (!position) return 0;\n\treturn (geometry.index ? geometry.index.count : position.count) / 3;\n}\n\n/** `faces` maps each segment to its source triangle; null from the `EdgesGeometry` fallback. */\nexport interface ExtractedEdges {\n\tsegments: Float32Array;\n\tfaces: Uint32Array | null;\n}\n\ninterface FastPathData {\n\tpositions: Float32Array;\n\tindex: Uint32Array | Uint16Array | null;\n\t/** Bumped by `needsUpdate`: an array edited in place keeps its identity but not its edges. */\n\tversion: string;\n}\n\n// Fast extractor needs plain non-interleaved float32 xyz + typed index arrays. Anything exotic\n// (interleaved, float64, morphed) falls back to THREE.EdgesGeometry instead.\nfunction fastPathData(geometry: THREE.BufferGeometry): FastPathData | null {\n\tconst position = geometry.getAttribute('position');\n\tif (\n\t\t!position ||\n\t\t(position as THREE.InterleavedBufferAttribute).isInterleavedBufferAttribute ||\n\t\tposition.itemSize !== 3 ||\n\t\t!(position.array instanceof Float32Array) ||\n\t\tposition.count >= MAX_EXTRACT_VERTICES\n\t) {\n\t\treturn null;\n\t}\n\tconst index = geometry.index;\n\tif (index && !(index.array instanceof Uint32Array) && !(index.array instanceof Uint16Array)) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tpositions: position.array,\n\t\tindex: index ? (index.array as Uint32Array | Uint16Array) : null,\n\t\tversion: `${(position as THREE.BufferAttribute).version}:${index?.version ?? 0}`\n\t};\n}\n\n// FNV-1a over sampled head+tail words of position/index plus lengths and crease angle. Sampling\n// keeps this ~free at millions of vertices; a collision needs identical lengths AND sampled regions.\nfunction contentKey(data: FastPathData, thresholdAngle: number): string {\n\tconst SAMPLE_WORDS = 4096;\n\tlet hash = 0x811c9dc5;\n\tconst mix = (word: number): void => {\n\t\thash ^= word;\n\t\thash = Math.imul(hash, 0x01000193);\n\t};\n\n\tconst words = new Uint32Array(\n\t\tdata.positions.buffer,\n\t\tdata.positions.byteOffset,\n\t\tdata.positions.length\n\t);\n\tconst head = Math.min(SAMPLE_WORDS, words.length);\n\tfor (let i = 0; i < head; i++) mix(words[i]);\n\tfor (let i = Math.max(head, words.length - SAMPLE_WORDS); i < words.length; i++) mix(words[i]);\n\n\tlet indexLength = 0;\n\tif (data.index) {\n\t\tindexLength = data.index.length;\n\t\tconst headIndex = Math.min(SAMPLE_WORDS, indexLength);\n\t\tfor (let i = 0; i < headIndex; i++) mix(data.index[i]);\n\t\tfor (let i = Math.max(headIndex, indexLength - SAMPLE_WORDS); i < indexLength; i++) {\n\t\t\tmix(data.index[i]);\n\t\t}\n\t}\n\n\treturn `${thresholdAngle}:${data.positions.length}:${indexLength}:${hash >>> 0}`;\n}\n\nfunction extractViaThree(geometry: THREE.BufferGeometry, thresholdAngle: number): ExtractedEdges {\n\tconst edges = new THREE.EdgesGeometry(geometry, thresholdAngle);\n\tconst positions = edges.attributes.position\n\t\t? (edges.attributes.position.array as Float32Array)\n\t\t: new Float32Array(0);\n\tedges.dispose(); // frees only GPU-side state; the CPU array is the return value\n\treturn { segments: positions, faces: null };\n}\n\n// A re-solve whose geometry the parser served from its cache hands back the very same position and\n// index arrays, so identity is an exact key that needs no hashing, and entries go with the arrays.\n// Results are shared by every overlay built from them: read-only, like the arrays they came from.\n// Only the latest version per angle is kept: an array edited in place never returns to an old one.\nconst extracted = new WeakMap<\n\tFloat32Array,\n\tWeakMap<object, Map<number, { version: string; edges: ExtractedEdges }>>\n>();\nconst NO_INDEX = {};\n\nfunction recall(data: FastPathData, thresholdAngle: number): ExtractedEdges | undefined {\n\tconst known = extracted\n\t\t.get(data.positions)\n\t\t?.get(data.index ?? NO_INDEX)\n\t\t?.get(thresholdAngle);\n\treturn known?.version === data.version ? known.edges : undefined;\n}\n\nfunction remember(\n\tdata: FastPathData,\n\tthresholdAngle: number,\n\tedges: ExtractedEdges\n): ExtractedEdges {\n\tlet byIndex = extracted.get(data.positions);\n\tif (!byIndex) extracted.set(data.positions, (byIndex = new WeakMap()));\n\tlet byAngle = byIndex.get(data.index ?? NO_INDEX);\n\tif (!byAngle) byIndex.set(data.index ?? NO_INDEX, (byAngle = new Map()));\n\tbyAngle.set(thresholdAngle, { version: data.version, edges });\n\treturn edges;\n}\n\nexport function extractSegmentsSync(\n\tgeometry: THREE.BufferGeometry,\n\tthresholdAngle: number\n): ExtractedEdges {\n\tconst data = fastPathData(geometry);\n\tif (!data) return extractViaThree(geometry, thresholdAngle);\n\n\treturn (\n\t\trecall(data, thresholdAngle) ??\n\t\tremember(data, thresholdAngle, extractEdgeSegments(data.positions, data.index, thresholdAngle))\n\t);\n}\n\n// --- Worker offload -----------------------------------------------------------------------------\n\ninterface PendingRequest {\n\tresolve: (edges: ExtractedEdges) => void;\n\treject: (error: Error) => void;\n}\n\nlet extractionWorker: Worker | null | undefined; // undefined = not yet tried, null = unavailable\nconst pendingRequests = new Map<number, PendingRequest>();\nlet nextRequestId = 1;\n\nfunction getExtractionWorker(): Worker | null {\n\tif (extractionWorker !== undefined) return extractionWorker;\n\tif (\n\t\ttypeof Worker === 'undefined' ||\n\t\ttypeof Blob === 'undefined' ||\n\t\ttypeof URL === 'undefined' ||\n\t\ttypeof URL.createObjectURL !== 'function'\n\t) {\n\t\textractionWorker = null;\n\t\treturn null;\n\t}\n\ttry {\n\t\t// Blob URL keeps this bundler-agnostic (no `new Worker(new URL(...))`). Never revoked:\n\t\t// revoking before the worker finishes fetching is unspecified behavior, and this is a\n\t\t// process-lifetime singleton.\n\t\tconst url = URL.createObjectURL(\n\t\t\tnew Blob([edgeExtractWorkerSource()], { type: 'text/javascript' })\n\t\t);\n\t\tconst worker = new Worker(url);\n\t\tworker.onmessage = (event: MessageEvent) => {\n\t\t\tconst { id, segments, faces, error } = event.data as {\n\t\t\t\tid: number;\n\t\t\t\tsegments?: Float32Array;\n\t\t\t\tfaces?: Uint32Array;\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tconst pending = pendingRequests.get(id);\n\t\t\tif (!pending) return;\n\t\t\tpendingRequests.delete(id);\n\t\t\tif (segments) pending.resolve({ segments, faces: faces ?? null });\n\t\t\telse pending.reject(new Error(error ?? 'edge extraction failed in worker'));\n\t\t};\n\t\tworker.onerror = () => {\n\t\t\t// Worker died (CSP, OOM, script error): fail everything in flight and never retry the\n\t\t\t// worker this session — callers fall back to inline extraction.\n\t\t\tfor (const pending of pendingRequests.values()) {\n\t\t\t\tpending.reject(new Error('edge extraction worker crashed'));\n\t\t\t}\n\t\t\tpendingRequests.clear();\n\t\t\tworker.terminate();\n\t\t\textractionWorker = null;\n\t\t};\n\t\textractionWorker = worker;\n\t} catch {\n\t\textractionWorker = null;\n\t}\n\treturn extractionWorker;\n}\n\nfunction extractInWorker(\n\tworker: Worker,\n\tdata: FastPathData,\n\tthresholdAngle: number\n): Promise<ExtractedEdges> {\n\treturn new Promise<ExtractedEdges>((resolve, reject) => {\n\t\tconst id = nextRequestId++;\n\t\tpendingRequests.set(id, { resolve, reject });\n\t\t// Copy before transfer — the originals back the render geometry.\n\t\tconst positions = data.positions.slice();\n\t\tconst index = data.index ? data.index.slice() : null;\n\t\tconst transfer: Transferable[] = [positions.buffer];\n\t\tif (index) transfer.push(index.buffer);\n\t\tworker.postMessage({ id, positions, index, thresholdAngle }, transfer);\n\t});\n}\n\n// In-flight dedupe: meshes with identical content share one worker round-trip.\nconst inFlightExtractions = new Map<string, Promise<ExtractedEdges>>();\n\nexport function extractSegmentsAsync(\n\tgeometry: THREE.BufferGeometry,\n\tthresholdAngle: number\n): Promise<ExtractedEdges> {\n\tconst data = fastPathData(geometry);\n\tif (!data || triangleCountOf(geometry) < INLINE_TRIANGLE_BUDGET) {\n\t\treturn Promise.resolve(extractSegmentsSync(geometry, thresholdAngle));\n\t}\n\n\tconst known = recall(data, thresholdAngle);\n\tif (known) return Promise.resolve(known);\n\n\tconst key = contentKey(data, thresholdAngle);\n\tconst inFlight = inFlightExtractions.get(key);\n\tif (inFlight) return inFlight;\n\n\tconst worker = getExtractionWorker();\n\tif (!worker) return Promise.resolve(extractSegmentsSync(geometry, thresholdAngle));\n\n\tconst request = extractInWorker(worker, data, thresholdAngle)\n\t\t.catch(() => extractEdgeSegments(data.positions, data.index, thresholdAngle))\n\t\t.then((edges) => remember(data, thresholdAngle, edges))\n\t\t.finally(() => {\n\t\t\tinFlightExtractions.delete(key);\n\t\t});\n\tinFlightExtractions.set(key, request);\n\treturn request;\n}\n","import * as THREE from 'three';\n\nimport {\n\tclearMemberHighlight,\n\tsetMemberHighlight,\n\ttype HighlightKind\n} from '../../shared/index.js';\n\n// ============================================================================\n// Picking inside a merged mesh\n// ============================================================================\n//\n// Merging by material collapses thousands of source objects into a handful of THREE.Mesh objects,\n// which is what keeps an IFC-scale model renderable. The cost is that a raycast can only name the\n// merged mesh, not the wall the user actually clicked — so selection and the metadata panel would\n// both address the whole group.\n//\n// `userData.members` carries each source object's window into the merged index buffer\n// (`finalizeMergedMesh`), and the raycast reports which face it hit. Face → index range → member\n// recovers the original object, so highlight and metadata stay per-object even though rendering is\n// per-group.\n\n/** Mirror of the record `parse/webdisplay/batch/merge.ts` stamps into `userData.members`. */\nexport interface PickableMember {\n\ttrackingKey?: string;\n\tname: string;\n\tlayer: string;\n\tmetadata: Record<string, string>;\n\tindexStart?: number;\n\tindexCount?: number;\n}\n\nexport interface ResolvedMember {\n\tmember: PickableMember;\n\tindex: number;\n}\n\n/** Members of a merged mesh, or null for an ordinary one. */\nexport function membersOf(object: THREE.Object3D): PickableMember[] | null {\n\tconst members = object.userData?.members as PickableMember[] | undefined;\n\treturn Array.isArray(members) && members.length > 0 ? members : null;\n}\n\n/**\n * The source object a raycast hit landed on, or null when this isn't a merged mesh or the hit\n * carries no face (points/lines, or a member written without an index window).\n *\n * Members are laid out in ascending, contiguous index order, so this is a binary search rather\n * than a scan — a merged group can hold thousands of members and picking runs per click.\n */\nexport function resolveHitMember(hit: THREE.Intersection): ResolvedMember | null {\n\tconst members = membersOf(hit.object);\n\tif (!members || hit.faceIndex == null) return null;\n\n\t// Three reports the face ordinal; each face is 3 indices into the merged index buffer.\n\tconst hitIndex = hit.faceIndex * 3;\n\n\tlet low = 0;\n\tlet high = members.length - 1;\n\twhile (low <= high) {\n\t\tconst mid = (low + high) >> 1;\n\t\tconst member = members[mid]!;\n\t\tif (member.indexStart == null || member.indexCount == null) return null;\n\t\tif (hitIndex < member.indexStart) high = mid - 1;\n\t\telse if (hitIndex >= member.indexStart + member.indexCount) low = mid + 1;\n\t\telse return { member, index: mid };\n\t}\n\treturn null;\n}\n\n/**\n * World-space bounds of one member inside a merged mesh, walking only that member's index window.\n * `Box3.setFromObject` would return the whole merged group's bounds.\n */\nexport function memberBounds(mesh: THREE.Mesh, member: PickableMember): THREE.Box3 {\n\tconst box = new THREE.Box3();\n\tconst index = mesh.geometry.getIndex();\n\tconst position = mesh.geometry.getAttribute('position');\n\tif (!index || !position || member.indexStart == null || member.indexCount == null) return box;\n\n\tmesh.updateMatrixWorld();\n\tconst vertex = new THREE.Vector3();\n\tconst end = Math.min(member.indexStart + member.indexCount, index.count);\n\tfor (let i = member.indexStart; i < end; i++) {\n\t\tvertex.fromBufferAttribute(position, index.getX(i)).applyMatrix4(mesh.matrixWorld);\n\t\tbox.expandByPoint(vertex);\n\t}\n\treturn box;\n}\n\n/**\n * Draws members of a merged mesh with `highlight`. Returns a restore function; call it before\n * applying a different highlight. Members hidden by the outliner stay hidden either way.\n */\nexport function highlightMembers(\n\tmesh: THREE.Mesh,\n\tmembers: ReadonlySet<number>,\n\thighlight: THREE.Material,\n\tkind?: HighlightKind\n): () => void {\n\tsetMemberHighlight(mesh, members, highlight, kind);\n\treturn () => clearMemberHighlight(mesh, kind);\n}\n","import type * as THREE from 'three';\nimport type { LineSegments2 } from 'three/addons/lines/LineSegments2.js';\n\nimport { getHiddenMembers, onMemberDrawChange } from '../../shared/index.js';\nimport { membersOf } from '../scene-setup/merged-picking.js';\nimport type { ExtractedEdges } from './extraction.js';\nimport { buildLineGeometry } from './line-geometry.js';\n\n// ============================================================================\n// Edge overlay of a merged mesh follows its hidden members\n// ============================================================================\n//\n// Hiding a member stops its triangles drawing, but the overlay is one line soup for the whole\n// merged mesh, so its edges would stay behind as a floating outline. Each segment carries its\n// source triangle, so the overlay rebuilds its line geometry from the segments of drawn members.\n\n/** Per segment, the index of the member whose index window holds its source triangle. */\nfunction memberOfSegments(mesh: THREE.Mesh, faces: Uint32Array): Uint32Array | null {\n\tconst members = membersOf(mesh);\n\tif (!members) return null;\n\tconst starts: number[] = [];\n\tfor (const member of members) {\n\t\tif (member.indexStart == null) return null;\n\t\tstarts.push(member.indexStart);\n\t}\n\n\tconst result = new Uint32Array(faces.length);\n\tfor (let s = 0; s < faces.length; s++) {\n\t\tconst indexPosition = faces[s]! * 3;\n\t\t// Last window starting at or before this triangle; windows are ascending and contiguous.\n\t\tlet low = 0;\n\t\tlet high = starts.length - 1;\n\t\twhile (low < high) {\n\t\t\tconst mid = (low + high + 1) >> 1;\n\t\t\tif (starts[mid]! <= indexPosition) low = mid;\n\t\t\telse high = mid - 1;\n\t\t}\n\t\tresult[s] = low;\n\t}\n\treturn result;\n}\n\nfunction visibleSegments(\n\tsegments: Float32Array,\n\tmemberOf: Uint32Array,\n\thidden: ReadonlySet<number>\n): Float32Array {\n\tlet count = 0;\n\tfor (let s = 0; s < memberOf.length; s++) if (!hidden.has(memberOf[s]!)) count++;\n\n\tconst out = new Float32Array(count * 6);\n\tlet offset = 0;\n\tfor (let s = 0; s < memberOf.length; s++) {\n\t\tif (hidden.has(memberOf[s]!)) continue;\n\t\tout.set(segments.subarray(s * 6, s * 6 + 6), offset);\n\t\toffset += 6;\n\t}\n\treturn out;\n}\n\n/** Keeps `overlay` in step with the hidden members of `mesh`, its parent. */\nexport function followHiddenMembers(\n\toverlay: LineSegments2,\n\tmesh: THREE.Mesh,\n\tedges: ExtractedEdges\n): void {\n\tconst { segments, faces } = edges;\n\tif (!faces || !membersOf(mesh)) return;\n\n\tlet memberOf: Uint32Array | null | undefined;\n\tlet filtered = false;\n\n\tconst apply = () => {\n\t\tconst hidden = getHiddenMembers(mesh);\n\t\tif (hidden.size === 0 && !filtered) return;\n\n\t\tmemberOf ??= memberOfSegments(mesh, faces);\n\t\tif (!memberOf) return;\n\n\t\tconst next = hidden.size === 0 ? segments : visibleSegments(segments, memberOf, hidden);\n\t\toverlay.geometry.dispose();\n\t\toverlay.geometry = buildLineGeometry(next).geometry;\n\t\tfiltered = hidden.size > 0;\n\t};\n\n\tapply();\n\tconst unsubscribe = onMemberDrawChange(mesh, apply);\n\toverlay.addEventListener('removed', unsubscribe);\n}\n","import * as THREE from 'three';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\nimport { LineSegments2 } from 'three/addons/lines/LineSegments2.js';\n\nimport type { EdgeGeometryEntry } from './line-geometry.js';\nimport {\n\tDEFAULT_EDGE_COLOR,\n\tEDGE_OFFSET_FACTOR,\n\tEDGE_OFFSET_UNITS,\n\tEDGE_USERDATA_KIND,\n\tFADE_END_PX,\n\tFADE_START_PX,\n\ttype ResolvedOptions\n} from './options.js';\n\n// ============================================================================\n// Overlay construction\n// ============================================================================\n\n// Multiplicative darkening (not lerp-to-black) preserves hue and desaturates gently; a near-black\n// surface just yields near-black edges.\nfunction deriveEdgeColor(mesh: THREE.Mesh, darken: number): THREE.Color {\n\tconst material = Array.isArray(mesh.material) ? mesh.material[0] : mesh.material;\n\tconst source = (material as { color?: THREE.Color } | null)?.color;\n\tif (!source) return new THREE.Color(DEFAULT_EDGE_COLOR);\n\treturn source.clone().multiplyScalar(1 - darken);\n}\n\n/** Pools materials by color+fade so overlays sharing both share one `LineMaterial` instance. */\nexport class MaterialPool {\n\tprivate readonly byKey = new Map<number, LineMaterial>();\n\tconstructor(private readonly options: ResolvedOptions) {}\n\n\tfor(mesh: THREE.Mesh, fade: boolean): LineMaterial {\n\t\tconst color = this.options.forcedColor ?? deriveEdgeColor(mesh, this.options.darken);\n\t\tconst key = color.getHex() * 2 + (fade ? 1 : 0);\n\t\tlet material = this.byKey.get(key);\n\t\tif (!material) {\n\t\t\tmaterial = createEdgeMaterial(color, this.options.width, fade);\n\t\t\tthis.byKey.set(key, material);\n\t\t}\n\t\treturn material;\n\t}\n\n\t/** Dispose any material no overlay adopted (e.g. every mesh was skipped or cancelled). */\n\tdisposeUnused(created: LineSegments2[]): void {\n\t\tconst used = new Set(created.map((overlay) => overlay.material));\n\t\tfor (const material of this.byKey.values()) {\n\t\t\tif (!used.has(material)) material.dispose();\n\t\t}\n\t}\n}\n\nfunction createEdgeMaterial(\n\tcolor: THREE.Color,\n\twidth: number,\n\tdistanceFade: boolean\n): LineMaterial {\n\t// LineMaterialParameters omits linewidth/opacity from its type though both exist at runtime.\n\tconst material = new LineMaterial({ color });\n\t(material as LineMaterial & { linewidth: number }).linewidth = width;\n\t// Lifts lines toward the camera by a couple of depth-quantization steps so they win z-fighting\n\t// against the surface they were extracted from, without moving the surface itself.\n\tmaterial.polygonOffset = true;\n\tmaterial.polygonOffsetFactor = EDGE_OFFSET_FACTOR;\n\tmaterial.polygonOffsetUnits = EDGE_OFFSET_UNITS;\n\t// Set once here, not per draw: flipping `transparent` after the render list is built wouldn't\n\t// re-sort the object into the transparent pass.\n\tif (distanceFade) material.transparent = true;\n\treturn material;\n}\n\nexport function buildEdgeOverlay(\n\tentry: EdgeGeometryEntry,\n\tmaterial: LineMaterial,\n\tdistanceFade: boolean\n): LineSegments2 {\n\tconst overlay = new LineSegments2(entry.geometry, material);\n\toverlay.userData.kind = EDGE_USERDATA_KIND;\n\toverlay.raycast = () => {}; // never pickable; clicks should hit the mesh, not its outline\n\tif (distanceFade) enableDistanceFade(overlay, entry.edgeSpacing);\n\treturn overlay;\n}\n\nconst _fadeCenter = new THREE.Vector3();\nconst _fadeCameraPos = new THREE.Vector3();\n\n// Returns Infinity (\"don't fade\") for an unknown/degenerate projection, or a camera inside the mesh.\nfunction pixelsPerWorldUnit(\n\toverlay: LineSegments2,\n\tcamera: THREE.Camera,\n\tviewportHeightPx: number\n): number {\n\tif (!overlay.geometry.boundingSphere) overlay.geometry.computeBoundingSphere();\n\tconst sphere = overlay.geometry.boundingSphere;\n\tif (!sphere) return Infinity;\n\n\tif ((camera as THREE.PerspectiveCamera).isPerspectiveCamera) {\n\t\tconst perspective = camera as THREE.PerspectiveCamera;\n\t\t_fadeCenter.copy(sphere.center).applyMatrix4(overlay.matrixWorld);\n\t\tconst distance = _fadeCameraPos\n\t\t\t.setFromMatrixPosition(camera.matrixWorld)\n\t\t\t.distanceTo(_fadeCenter);\n\t\tconst radius = sphere.radius * overlay.matrixWorld.getMaxScaleOnAxis();\n\t\tif (distance <= radius) return Infinity; // camera inside the mesh — no fade\n\t\tconst tanHalfFov = Math.tan(THREE.MathUtils.degToRad(perspective.fov) * 0.5);\n\t\tconst worldHeightAtCentre = 2 * distance * tanHalfFov;\n\t\treturn worldHeightAtCentre > 0 ? viewportHeightPx / worldHeightAtCentre : Infinity;\n\t}\n\tif ((camera as THREE.OrthographicCamera).isOrthographicCamera) {\n\t\tconst ortho = camera as THREE.OrthographicCamera;\n\t\tconst worldHeight = (ortho.top - ortho.bottom) / ortho.zoom;\n\t\treturn worldHeight > 0 ? viewportHeightPx / worldHeight : Infinity;\n\t}\n\treturn Infinity;\n}\n\n// Runs in onBeforeRender (not computed once) so opacity is written right before this overlay's\n// draw call — uniforms upload per draw, so overlays sharing one material still fade independently.\n// Chains LineSegments2's own onBeforeRender to keep its resolution uniform in sync.\nfunction enableDistanceFade(overlay: LineSegments2, edgeSpacing: number): void {\n\t// Assign via the Object3D base type: LineSegments2's typings narrow onBeforeRender to\n\t// (renderer) only, but the renderer actually calls it with (renderer, scene, camera, …).\n\t(overlay as THREE.Object3D).onBeforeRender = (renderer, _scene, camera) => {\n\t\tLineSegments2.prototype.onBeforeRender.call(overlay, renderer);\n\t\tconst material = overlay.material as LineMaterial;\n\t\tconst scale = pixelsPerWorldUnit(overlay, camera, material.resolution.y);\n\t\t// Screen-space gap between neighbouring edges. Infinity in, Infinity out — clamps to fully\n\t\t// opaque rather than fading on a guess.\n\t\tconst gapPx = edgeSpacing * scale;\n\t\tmaterial.opacity = THREE.MathUtils.clamp(\n\t\t\t(gapPx - FADE_END_PX) / (FADE_START_PX - FADE_END_PX),\n\t\t\t0,\n\t\t\t1\n\t\t);\n\t};\n}\n","import * as THREE from 'three';\nimport type { LineMaterial } from 'three/addons/lines/LineMaterial.js';\nimport { LineSegments2 } from 'three/addons/lines/LineSegments2.js';\n\nimport { buildLineGeometry } from './edges/line-geometry.js';\nimport {\n\textractSegmentsAsync,\n\textractSegmentsSync,\n\ttriangleCountOf,\n\ttype ExtractedEdges\n} from './edges/extraction.js';\nimport { followHiddenMembers } from './edges/member-filter.js';\nimport {\n\tEDGES_SKIPPED_OVERLAY_BUDGET,\n\tEDGES_SKIPPED_TRIANGLE_CAP,\n\tEDGE_USERDATA_KIND,\n\tresolveOptions,\n\ttype EdgeOptions,\n\ttype ResolvedOptions\n} from './edges/options.js';\nimport { MaterialPool, buildEdgeOverlay } from './edges/overlay.js';\n\n/**\n * Crisp boundary/crease edges overlaid on meshes, rendered as fat `LineSegments2` (controllable\n * thickness, unlike the 1px cap of `THREE.LineSegments`). Depth-offset rationale for the overlay\n * lines: see `EDGE_OFFSET_FACTOR`/`EDGE_OFFSET_UNITS` in `edges/options.ts`.\n */\nexport type { EdgeOptions };\nexport { EDGE_USERDATA_KIND, EDGES_SKIPPED_TRIANGLE_CAP, EDGES_SKIPPED_OVERLAY_BUDGET };\n\n// ============================================================================\n// Public API — add / remove / query\n// ============================================================================\n\n/** For pick/fit filters elsewhere to exclude overlays from hit-testing. */\nexport function isEdgeOverlay(object: THREE.Object3D): boolean {\n\treturn object.userData?.kind === EDGE_USERDATA_KIND;\n}\n\n/**\n * Meshes under `root` that should get an overlay: content meshes without one, caps applied.\n *\n * Two caps, and they fail in opposite directions. `maxTriangles` is per mesh and rejects the rare\n * enormous one; `maxOverlays` is a budget over the whole set and rejects the far more common case\n * of thousands of small ones, which no per-mesh test can see. Overlays already attached from an\n * earlier apply are counted against the budget, so a re-apply can't creep past it.\n */\nfunction collectTargets(root: THREE.Object3D, resolved: ResolvedOptions): THREE.Mesh[] {\n\tconst candidates: THREE.Mesh[] = [];\n\tlet existingOverlays = 0;\n\n\troot.traverse((object) => {\n\t\tif (!(object instanceof THREE.Mesh)) return;\n\t\tif (object.userData.id === 'floor' || object.userData.id === 'grid') return;\n\t\tif (object.userData.kind === EDGE_USERDATA_KIND) return;\n\t\tif (object.children.some((c) => c.userData?.kind === EDGE_USERDATA_KIND)) {\n\t\t\texistingOverlays++;\n\t\t\treturn; // already done\n\t\t}\n\t\tif (!object.geometry) return;\n\n\t\tif (triangleCountOf(object.geometry) > resolved.maxTriangles) {\n\t\t\tobject.userData.edgesSkipped = EDGES_SKIPPED_TRIANGLE_CAP;\n\t\t\treturn;\n\t\t}\n\t\tcandidates.push(object);\n\t});\n\n\tconst budget = Math.max(0, resolved.maxOverlays - existingOverlays);\n\tconst targets = candidates.slice(0, budget);\n\tfor (const mesh of targets) delete mesh.userData.edgesSkipped;\n\tfor (const mesh of candidates.slice(budget)) {\n\t\tmesh.userData.edgesSkipped = EDGES_SKIPPED_OVERLAY_BUDGET;\n\t}\n\n\tif (candidates.length > budget) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.debug(\n\t\t\t`[edges] overlay budget reached: ${candidates.length} meshes want overlays, ` +\n\t\t\t\t`${budget} allowed — the rest fall back to the screen-space edge pass.`\n\t\t);\n\t}\n\n\treturn targets;\n}\n\nfunction attachOverlay(\n\tmesh: THREE.Mesh,\n\tedges: ExtractedEdges,\n\tmaterials: MaterialPool,\n\tresolved: ResolvedOptions\n): LineSegments2 {\n\tconst entry = buildLineGeometry(edges.segments);\n\t// Distance fade needs the transparent pass; overlays over the segment cap stay opaque instead\n\t// of skipping outright — see EdgeOptions.maxSegments.\n\tconst fade = resolved.distanceFade && entry.segmentCount <= resolved.maxSegments;\n\tconst overlay = buildEdgeOverlay(entry, materials.for(mesh, fade), fade);\n\tmesh.add(overlay); // child → inherits transform, disposed with the parent subtree\n\tfollowHiddenMembers(overlay, mesh, edges);\n\treturn overlay;\n}\n\n/**\n * Attach an edge overlay to every `Mesh` in `root`'s subtree, returning the created overlays.\n * Idempotent — meshes that already carry an overlay are skipped. Skips the floor/grid aids.\n *\n * Fully synchronous, extraction included. Prefer {@link addEdgesAsync} for interactive hosts with\n * potentially-large meshes.\n */\nexport function addEdges(root: THREE.Object3D, options: EdgeOptions = {}): LineSegments2[] {\n\tconst resolved = resolveOptions(options);\n\tconst materials = new MaterialPool(resolved);\n\tconst created: LineSegments2[] = [];\n\n\tfor (const mesh of collectTargets(root, resolved)) {\n\t\t// Extraction is cached by source array (`extraction.ts`), which is where the savings are;\n\t\t// the line geometry is per-overlay and owned by it.\n\t\tconst edges = extractSegmentsSync(mesh.geometry, resolved.thresholdAngle);\n\t\tcreated.push(attachOverlay(mesh, edges, materials, resolved));\n\t}\n\n\tmaterials.disposeUnused(created);\n\treturn created;\n}\n\n/**\n * {@link removeEdges} bumps this per root; async attaches landing after a bump are dropped, so\n * \"toggle off while extracting\" can't resurrect overlays.\n */\nconst rootGenerations = new WeakMap<THREE.Object3D, number>();\n\nfunction generationOf(root: THREE.Object3D): number {\n\treturn rootGenerations.get(root) ?? 0;\n}\n\n/** Is `mesh` still reachable from `root`? Guards attaches racing scene clears. */\nfunction isConnected(mesh: THREE.Object3D, root: THREE.Object3D): boolean {\n\tfor (let node: THREE.Object3D | null = mesh; node; node = node.parent) {\n\t\tif (node === root) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Like {@link addEdges}, but large-mesh extraction runs in a Worker so the main thread never\n * stalls; small meshes still attach synchronously before this resolves. Resolves with every\n * overlay actually attached — late results are dropped if the mesh left the subtree,\n * {@link removeEdges} ran for this root meanwhile, or another apply already attached one to that\n * mesh.\n */\nexport async function addEdgesAsync(\n\troot: THREE.Object3D,\n\toptions: EdgeOptions = {}\n): Promise<LineSegments2[]> {\n\tconst resolved = resolveOptions(options);\n\tconst materials = new MaterialPool(resolved);\n\tconst generation = generationOf(root);\n\tconst created: LineSegments2[] = [];\n\n\tconst attaches = collectTargets(root, resolved).map(async (mesh) => {\n\t\tconst edges = await extractSegmentsAsync(mesh.geometry, resolved.thresholdAngle);\n\t\t// Things may have moved on while extracting — attach only if this apply is still wanted.\n\t\tif (generationOf(root) !== generation) return;\n\t\tif (!isConnected(mesh, root)) return;\n\t\tif (mesh.children.some((c) => c.userData?.kind === EDGE_USERDATA_KIND)) return;\n\t\tcreated.push(attachOverlay(mesh, edges, materials, resolved));\n\t});\n\n\tawait Promise.all(attaches);\n\tmaterials.disposeUnused(created);\n\treturn created;\n}\n\n/**\n * Remove every edge overlay under `root`, disposing geometry and material, and cancels any\n * in-flight async attaches for `root`. Inverse of {@link addEdges}/{@link addEdgesAsync}. Returns\n * the count removed.\n */\nexport function removeEdges(root: THREE.Object3D): number {\n\trootGenerations.set(root, generationOf(root) + 1);\n\n\tconst overlays: LineSegments2[] = [];\n\troot.traverse((object) => {\n\t\tif (object instanceof LineSegments2 && isEdgeOverlay(object)) overlays.push(object);\n\t});\n\n\t// One addEdges call shares one material across its overlays — dispose each distinct one once.\n\t// (If `root` covers only part of a call's overlays, survivors self-heal: three recompiles a\n\t// disposed-but-still-referenced material on its next use.)\n\tconst materials = new Set<LineMaterial>();\n\tfor (const overlay of overlays) {\n\t\toverlay.geometry.dispose(); // each overlay owns its line geometry outright\n\t\tmaterials.add(overlay.material as LineMaterial);\n\t\t// Nothing to undo on the parent mesh: the depth bias lives entirely on the overlay's own\n\t\t// material, so surfaces keep whatever polygonOffset their look preset configured.\n\t\toverlay.removeFromParent();\n\t}\n\tmaterials.forEach((material) => material.dispose());\n\treturn overlays.length;\n}\n","import * as THREE from 'three';\n\n/**\n * An \"infinite\", distance-fading reference grid.\n *\n * `GridHelper` is a fixed-size square that visibly ends once you pan/zoom past it. This draws one\n * large plane and computes the grid in the fragment shader from world coordinates, fading with\n * distance so the edge is never a hard cutoff.\n *\n * Spacing is in world units (meters) — `cellSize` of 1 = 1m cells.\n */\n\nexport interface GridOptions {\n\t/** Minor cell size in world units (meters). Default 1. */\n\tcellSize?: number;\n\t/** How many minor cells per major line. Default 10. */\n\tmajorEvery?: number;\n\t/** Minor line color. Default 0x888888. */\n\tcellColor?: THREE.ColorRepresentation;\n\t/** Major line color. Default 0x444444. */\n\tmajorColor?: THREE.ColorRepresentation;\n\t/** World-space radius at which the grid has fully faded out. Default 100. */\n\tfadeDistance?: number;\n\t/**\n\t * Axis the grid is laid perpendicular to, i.e. the scene's up axis. Standalone default is `'y'`,\n\t * but `initThree` always passes a plane derived from the configured `sceneUp`, so a\n\t * viewer-created grid defaults to `'z'` (Rhino's ground plane).\n\t */\n\tplane?: 'x' | 'y' | 'z';\n}\n\nexport interface Grid {\n\t/** Tagged `userData.id = 'grid'` so pick/fit code skips it. */\n\treadonly object: THREE.Mesh;\n\t/** Re-centers the fade on the camera so the grid feels infinite as you move. Call per frame. */\n\tupdate(cameraPosition: THREE.Vector3): void;\n\t/**\n\t * Rescales cell spacing and fade radius to the content's extent, so a 3-unit or 3000-unit part\n\t * both get sensible cells. No-op for empty/degenerate bounds.\n\t */\n\tfitToContent(bounds: THREE.Box3): void;\n\tsetVisible(visible: boolean): void;\n\tdispose(): void;\n}\n\n/** Rounds to a \"nice\" 1/2/5 × 10ⁿ step (CAD ruler convention) so cells never come out finer than the target and alias into a solid sheet. */\nfunction niceStep(value: number): number {\n\tif (!(value > 0) || !Number.isFinite(value)) return 1;\n\tconst exponent = Math.floor(Math.log10(value));\n\tconst power = Math.pow(10, exponent);\n\tconst mantissa = value / power; // in [1, 10)\n\tconst niceMantissa = mantissa >= 5 ? 5 : mantissa >= 2 ? 2 : 1;\n\treturn niceMantissa * power;\n}\n\nconst GRID_VERTEX = /* glsl */ `\n\tvarying vec3 vWorldPos;\n\tvoid main() {\n\t\tvec4 world = modelMatrix * vec4(position, 1.0);\n\t\tvWorldPos = world.xyz;\n\t\tgl_Position = projectionMatrix * viewMatrix * world;\n\t}\n`;\n\nconst GRID_FRAGMENT = /* glsl */ `\n\tprecision highp float;\n\tvarying vec3 vWorldPos;\n\n\tuniform vec2 uAxes; // indices (0=x,1=y,2=z) of the two in-plane world axes\n\tuniform float uCell;\n\tuniform float uMajor;\n\tuniform vec3 uCellColor;\n\tuniform vec3 uMajorColor;\n\tuniform vec3 uCenter; // fade center (camera position projected onto the plane)\n\tuniform float uFade;\n\n\t// Screen-space derivatives keep grid lines ~1px regardless of zoom (\"pristine grid\" technique).\n\tfloat gridLine(vec2 coord, float spacing) {\n\t\tvec2 c = coord / spacing;\n\t\tvec2 d = fwidth(c);\n\t\tvec2 g = abs(fract(c - 0.5) - 0.5) / max(d, 1e-6);\n\t\tfloat line = min(g.x, g.y);\n\t\treturn 1.0 - clamp(line, 0.0, 1.0);\n\t}\n\n\t// Index a vec3 by a float axis id (0/1/2) without dynamic indexing (WebGL1-safe).\n\tfloat axis(vec3 v, float i) {\n\t\treturn i < 0.5 ? v.x : (i < 1.5 ? v.y : v.z);\n\t}\n\n\tvoid main() {\n\t\t// Pick the two in-plane world coordinates.\n\t\tvec2 coord = vec2(axis(vWorldPos, uAxes.x), axis(vWorldPos, uAxes.y));\n\n\t\tfloat minor = gridLine(coord, uCell);\n\t\tfloat major = gridLine(coord, uCell * uMajor);\n\n\t\tvec3 color = mix(uCellColor, uMajorColor, major);\n\t\tfloat alpha = max(minor, major);\n\n\t\t// Radial fade from the camera-projected center.\n\t\tfloat dist = distance(vWorldPos, uCenter);\n\t\tfloat fade = 1.0 - clamp(dist / uFade, 0.0, 1.0);\n\t\talpha *= fade * fade;\n\n\t\tif (alpha < 0.001) discard;\n\t\tgl_FragColor = vec4(color, alpha);\n\t}\n`;\n\nexport function createGrid(options: GridOptions = {}): Grid {\n\tconst {\n\t\tcellSize = 1,\n\t\tmajorEvery = 10,\n\t\tcellColor = 0x888888,\n\t\tmajorColor = 0x444444,\n\t\tfadeDistance = 100,\n\t\tplane = 'y'\n\t} = options;\n\n\t// In-plane world axes: ground 'y' grids over x,z; 'z' over x,y; 'x' over y,z.\n\tconst axes =\n\t\tplane === 'y'\n\t\t\t? new THREE.Vector2(0, 2) // x, z\n\t\t\t: plane === 'z'\n\t\t\t\t? new THREE.Vector2(0, 1) // x, y\n\t\t\t\t: new THREE.Vector2(1, 2); // y, z\n\n\t// Must comfortably outreach the fade radius, else the grid's rectangular edge shows before the\n\t// fade completes. Plane is unit-sized and grown purely via scale so fitToContent never\n\t// recreates geometry.\n\tconst PLANE_TO_FADE_RATIO = 2.5;\n\tconst geometry = new THREE.PlaneGeometry(1, 1);\n\n\t// PlaneGeometry is in the XY plane by default; rotate it onto the requested world plane.\n\tif (plane === 'y') geometry.rotateX(-Math.PI / 2);\n\telse if (plane === 'x') geometry.rotateY(Math.PI / 2);\n\n\tconst material = new THREE.ShaderMaterial({\n\t\tvertexShader: GRID_VERTEX,\n\t\tfragmentShader: GRID_FRAGMENT,\n\t\ttransparent: true,\n\t\tdepthWrite: false,\n\t\tside: THREE.DoubleSide,\n\t\tuniforms: {\n\t\t\tuAxes: { value: axes },\n\t\t\tuCell: { value: cellSize },\n\t\t\tuMajor: { value: majorEvery },\n\t\t\tuCellColor: { value: new THREE.Color(cellColor) },\n\t\t\tuMajorColor: { value: new THREE.Color(majorColor) },\n\t\t\tuCenter: { value: new THREE.Vector3() },\n\t\t\tuFade: { value: fadeDistance }\n\t\t}\n\t});\n\n\tconst mesh = new THREE.Mesh(geometry, material);\n\tmesh.name = 'grid';\n\tmesh.userData.id = 'grid';\n\tmesh.renderOrder = -1; // draw before content so transparent geometry blends over it\n\n\t// fitToContent mutates both; seeded from fadeDistance so an un-fitted grid still covers its fade.\n\tlet fadeRadius = fadeDistance;\n\tlet planeScale = fadeDistance * PLANE_TO_FADE_RATIO;\n\n\tconst center = new THREE.Vector3();\n\n\treturn {\n\t\tobject: mesh,\n\t\tupdate: (cameraPosition) => {\n\t\t\t// Re-center on the camera so the grid tracks the view; the plane's own axis stays fixed\n\t\t\t// (a ground grid shouldn't lift to the camera's height).\n\t\t\tif (plane === 'y') {\n\t\t\t\tmesh.position.set(cameraPosition.x, 0, cameraPosition.z);\n\t\t\t\tcenter.set(cameraPosition.x, 0, cameraPosition.z);\n\t\t\t} else if (plane === 'z') {\n\t\t\t\tmesh.position.set(cameraPosition.x, cameraPosition.y, 0);\n\t\t\t\tcenter.set(cameraPosition.x, cameraPosition.y, 0);\n\t\t\t} else {\n\t\t\t\tmesh.position.set(0, cameraPosition.y, cameraPosition.z);\n\t\t\t\tcenter.set(0, cameraPosition.y, cameraPosition.z);\n\t\t\t}\n\t\t\tmaterial.uniforms.uCenter.value.copy(center);\n\t\t\t// Rotation is baked into the geometry, so uniform scale works on any plane orientation.\n\t\t\tmesh.scale.setScalar(planeScale);\n\t\t},\n\t\tfitToContent: (bounds) => {\n\t\t\tif (bounds.isEmpty()) return;\n\t\t\t// In-plane extent only — a tall thin part shouldn't blow up the cell size by its height.\n\t\t\tconst sizeVec = bounds.getSize(new THREE.Vector3());\n\t\t\tconst axisComponent = (v: THREE.Vector3, i: number) => (i === 0 ? v.x : i === 1 ? v.y : v.z);\n\t\t\tconst inPlaneExtent = Math.max(\n\t\t\t\taxisComponent(sizeVec, axes.x),\n\t\t\t\taxisComponent(sizeVec, axes.y)\n\t\t\t);\n\t\t\tif (!(inPlaneExtent > 0) || !Number.isFinite(inPlaneExtent)) return;\n\n\t\t\t// ~20 minor cells across the part; fade reaches ~2x past it so the edge stays out of view.\n\t\t\tconst TARGET_CELLS_ACROSS = 20;\n\t\t\tmaterial.uniforms.uCell.value = niceStep(inPlaneExtent / TARGET_CELLS_ACROSS);\n\t\t\tfadeRadius = inPlaneExtent * 2;\n\t\t\tmaterial.uniforms.uFade.value = fadeRadius;\n\t\t\tplaneScale = fadeRadius * PLANE_TO_FADE_RATIO;\n\t\t},\n\t\tsetVisible: (visible) => {\n\t\t\tmesh.visible = visible;\n\t\t},\n\t\tdispose: () => {\n\t\t\tmesh.removeFromParent();\n\t\t\tgeometry.dispose();\n\t\t\tmaterial.dispose();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\nimport { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\n\nexport interface LabelHandle {\n\treadonly object: CSS2DObject;\n\tsetPosition(position: THREE.Vector3): void;\n\tsetText(text: string): void;\n\tremove(): void;\n}\n\nexport interface LabelLayer {\n\taddLabel(text: string, position: THREE.Vector3, className?: string): LabelHandle;\n\t/** Call each frame after the WebGL render, with the active camera. */\n\trender(scene: THREE.Scene, camera: THREE.Camera): void;\n\tsetSize(width: number, height: number): void;\n\tdispose(): void;\n}\n\n// `container` is normally the canvas's parent, so both share a positioning context for the\n// absolutely-positioned label overlay.\nexport function createLabelLayer(container: HTMLElement, scene: THREE.Scene): LabelLayer {\n\tconst renderer = new CSS2DRenderer();\n\tconst dom = renderer.domElement;\n\tdom.style.position = 'absolute';\n\tdom.style.top = '0';\n\tdom.style.left = '0';\n\t// CSS2DRenderer sets width/height in pixels, so host must call `setSize` on every resize, same\n\t// as the WebGL renderer. overflow:hidden + pointerEvents:none: without both, the overlay can\n\t// cover the canvas and swallow orbit/clicks.\n\tdom.style.overflow = 'hidden';\n\tdom.style.pointerEvents = 'none';\n\tdom.style.zIndex = '30'; // above canvas/host overlays, below menus/popovers\n\tif (getComputedStyle(container).position === 'static') {\n\t\tcontainer.style.position = 'relative';\n\t}\n\tcontainer.appendChild(dom);\n\n\tconst size = { width: container.clientWidth || 1, height: container.clientHeight || 1 };\n\trenderer.setSize(size.width, size.height);\n\n\tconst group = new THREE.Group(); // pick/fit logic skips objects tagged 'label-layer'\n\tgroup.name = 'label-layer';\n\tgroup.userData.id = 'label-layer';\n\tscene.add(group);\n\n\tconst labels = new Set<CSS2DObject>();\n\n\tconst addLabel = (text: string, position: THREE.Vector3, className?: string): LabelHandle => {\n\t\tconst el = document.createElement('div');\n\t\tel.textContent = text;\n\t\tif (className) {\n\t\t\tel.className = className;\n\t\t} else {\n\t\t\t// Inline default so the layer needs no external stylesheet; pass className to opt out.\n\t\t\tObject.assign(el.style, {\n\t\t\t\tpadding: '2px 6px',\n\t\t\t\tborderRadius: '4px',\n\t\t\t\tbackground: 'rgba(20, 20, 20, 0.78)',\n\t\t\t\tcolor: '#fff',\n\t\t\t\tfont: '12px/1.3 system-ui, sans-serif',\n\t\t\t\t// `pre` preserves line breaks for multi-line readouts (e.g. total + per-axis deltas).\n\t\t\t\twhiteSpace: 'pre',\n\t\t\t\ttextAlign: 'center',\n\t\t\t\tuserSelect: 'none'\n\t\t\t} satisfies Partial<CSSStyleDeclaration>);\n\t\t}\n\t\tel.style.pointerEvents = 'none';\n\n\t\tconst object = new CSS2DObject(el);\n\t\tobject.position.copy(position);\n\t\tgroup.add(object);\n\t\tlabels.add(object);\n\n\t\treturn {\n\t\t\tobject,\n\t\t\tsetPosition: (p) => object.position.copy(p),\n\t\t\tsetText: (t) => {\n\t\t\t\tel.textContent = t;\n\t\t\t},\n\t\t\tremove: () => {\n\t\t\t\tobject.removeFromParent();\n\t\t\t\tel.remove();\n\t\t\t\tlabels.delete(object);\n\t\t\t}\n\t\t};\n\t};\n\n\treturn {\n\t\taddLabel,\n\t\trender: (scene, camera) => renderer.render(scene, camera),\n\t\tsetSize: (width, height) => renderer.setSize(width, height),\n\t\tdispose: () => {\n\t\t\tlabels.forEach((object) => {\n\t\t\t\tobject.removeFromParent();\n\t\t\t\t(object.element as HTMLElement).remove();\n\t\t\t});\n\t\t\tlabels.clear();\n\t\t\tgroup.removeFromParent();\n\t\t\tdom.remove();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\nimport { Line2 } from 'three/addons/lines/Line2.js';\nimport { LineGeometry } from 'three/addons/lines/LineGeometry.js';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\n\nimport type { LabelLayer, LabelHandle } from './label-layer';\n\n/**\n * Two-click distance measurement. Click a point, click a second, read the distance off a label on\n * the connecting line; a third click starts fresh.\n *\n * Picking snaps to the nearest vertex within {@link MeasureOptions.snapPixels} so measurements\n * land exactly on vertices rather than wherever the ray happened to hit — a cheap local snap\n * against the struck primitive's own vertices, no spatial index.\n *\n * Dormant until {@link MeasureTool.setEnabled}(true). While enabled it intercepts clicks (caller\n * forwards them and swallows the event when {@link MeasureTool.handleClick} returns true) so\n * measuring doesn't also select objects.\n */\n\nexport interface MeasureTool {\n\tsetEnabled(enabled: boolean): void;\n\tisEnabled(): boolean;\n\t/** Returns true if the tool consumed the click (caller should not also select). */\n\thandleClick(event: MouseEvent): boolean;\n\t/** Preview the next snap point via a ghost marker. No-op when disabled; never consumes the event. */\n\thandleMove(event: MouseEvent): void;\n\tclear(): void;\n\tdispose(): void;\n}\n\nexport interface MeasureOptions {\n\t/** Snap to a vertex when the cursor is within this many screen pixels of it. Default 12. */\n\tsnapPixels?: number;\n\t/** Marker + line color. Default yellow. */\n\tcolor?: THREE.ColorRepresentation;\n\tlabelClassName?: string;\n\t/**\n\t * Pass `data.modelunits`. Scene is in meters; default formatter converts and labels in this unit\n\t * (e.g. \"25.0 mm\" not \"0.025 m\"). Defaults to meters. Ignored if `format` is given.\n\t */\n\tdisplayUnit?: string;\n\t/**\n\t * Format the measurement → label text. Receives `distance` and per-axis `delta` (|b − a|), both\n\t * in meters. May return multi-line text/HTML; default renders total + Δx/Δy/Δz in `displayUnit`.\n\t */\n\tformat?: (distance: number, delta: THREE.Vector3) => string;\n}\n\ninterface MeasureDeps {\n\tcanvas: HTMLCanvasElement;\n\tscene: THREE.Scene;\n\tgetActiveCamera: () => THREE.Camera;\n\t/**\n\t * The current orbit target (e.g. `controls.target`). Scales the line/point pick threshold as a\n\t * fraction of camera→target distance so it stays constant on screen regardless of framing.\n\t * Without it, the fallback is distance-to-origin, which misjudges off-origin content.\n\t */\n\tgetViewTarget?: () => THREE.Vector3;\n\tlabelLayer: LabelLayer;\n\toptions?: MeasureOptions;\n}\n\nconst DEFAULT_SNAP_PIXELS = 12;\nconst DEFAULT_COLOR = 0xffcc00;\n// Fraction of view distance used as the line/point raycast threshold: ~1.5% gives a comfortable\n// grab band at typical framing without snapping to far-off geometry.\nconst LINE_PICK_FRACTION = 0.015;\n\n// Scene geometry loads in meters (webdisplay parser scales when `allowScaling` is on). Keep in\n// sync with the webdisplay parser's SCALE_FACTORS.\nconst UNIT_DISPLAY: Record<string, { metersPerUnit: number; suffix: string }> = {\n\tMillimeters: { metersPerUnit: 1 / 1000, suffix: 'mm' },\n\tCentimeters: { metersPerUnit: 1 / 100, suffix: 'cm' },\n\tMeters: { metersPerUnit: 1, suffix: 'm' },\n\tInches: { metersPerUnit: 1 / 39.37, suffix: 'in' },\n\tFeet: { metersPerUnit: 1 / 3.28084, suffix: 'ft' }\n};\n\n/** @internal exported for tests */\nexport function makeFormatter(displayUnit?: string): (n: number) => string {\n\tconst unit = (displayUnit && UNIT_DISPLAY[displayUnit]) || UNIT_DISPLAY.Meters;\n\treturn (meters: number) => `${(meters / unit.metersPerUnit).toPrecision(3)} ${unit.suffix}`;\n}\n\n/**\n * Raycast threshold for picking lines/points, as a fixed fraction of view size so the grab band\n * stays constant on screen while zooming. Perspective uses camera→target distance (see\n * `MeasureDeps.getViewTarget`); orthographic uses frustum height `(top − bottom) / zoom`, since\n * ortho zoom changes `camera.zoom` rather than position.\n *\n * Shared with any tool doing its own picking, so grab bands stay consistent across tools.\n */\nexport function pickThreshold(camera: THREE.Camera, viewTarget?: THREE.Vector3): number {\n\tif ((camera as THREE.OrthographicCamera).isOrthographicCamera) {\n\t\tconst ortho = camera as THREE.OrthographicCamera;\n\t\tconst visibleHeight = Math.abs(ortho.top - ortho.bottom) / (ortho.zoom || 1);\n\t\treturn visibleHeight * LINE_PICK_FRACTION;\n\t}\n\tconst viewScale = viewTarget ? camera.position.distanceTo(viewTarget) : camera.position.length();\n\treturn (viewScale || 1) * LINE_PICK_FRACTION;\n}\n\n/**\n * Vertex indices to consider snapping to, by object type: Mesh → struck triangle's 3 vertices;\n * Line/LineSegments → struck segment's 2 endpoints; Points → the struck vertex. Null when the hit\n * carries no usable index (e.g. a fat `Line2`), so the caller keeps the raw hit point.\n */\nfunction snapCandidateIndices(hit: THREE.Intersection): number[] | null {\n\tconst obj = hit.object;\n\tif (obj instanceof THREE.Mesh) {\n\t\treturn hit.face ? [hit.face.a, hit.face.b, hit.face.c] : null;\n\t}\n\tif (obj instanceof THREE.Points) {\n\t\t// Points.raycast resolves indexed geometry itself: `hit.index` is always a position index.\n\t\treturn hit.index != null ? [hit.index] : null;\n\t}\n\t// THREE.Line / LineSegments / LineLoop. For non-indexed geometry `hit.index` is the first\n\t// vertex of the struck segment. For indexed geometry it's a cursor into the index buffer, not\n\t// the resolved vertex (three r184 Line.raycast reports the loop counter) — endpoints must be\n\t// looked up through the index before reading the position attribute.\n\tif (obj instanceof THREE.Line) {\n\t\tif (hit.index == null) return null;\n\t\tconst index = obj.geometry.index;\n\t\tif (index) {\n\t\t\tif (hit.index + 1 >= index.count) return null; // stale/inconsistent hit; keep raw point\n\t\t\treturn [index.getX(hit.index), index.getX(hit.index + 1)];\n\t\t}\n\t\treturn [hit.index, hit.index + 1];\n\t}\n\treturn null;\n}\n\n/** Snap a raycast hit to the nearest geometry vertex within `snapPixels` on screen, else the raw hit point. */\nexport function snapToVertex(\n\thit: THREE.Intersection,\n\tcamera: THREE.Camera,\n\tscreenSize: { width: number; height: number },\n\tsnapPixels: number\n): THREE.Vector3 {\n\tconst raw = hit.point.clone();\n\tconst obj = hit.object as THREE.Object3D & { geometry?: THREE.BufferGeometry };\n\tconst indices = snapCandidateIndices(hit);\n\tif (!indices || !obj.geometry) return raw;\n\n\tconst pos = obj.geometry.attributes.position as THREE.BufferAttribute | undefined;\n\tif (!pos) return raw;\n\n\tconst toScreen = (worldP: THREE.Vector3): THREE.Vector2 => {\n\t\tconst ndc = worldP.clone().project(camera);\n\t\treturn new THREE.Vector2(\n\t\t\t((ndc.x + 1) / 2) * screenSize.width,\n\t\t\t((1 - ndc.y) / 2) * screenSize.height\n\t\t);\n\t};\n\tconst rawScreen = toScreen(raw);\n\n\tlet best = raw;\n\tlet bestPx = snapPixels;\n\tfor (const idx of indices) {\n\t\tif (idx >= pos.count) continue; // guard the line `index + 1` against the geometry end\n\t\tconst local = new THREE.Vector3().fromBufferAttribute(pos, idx);\n\t\tconst world = local.applyMatrix4(obj.matrixWorld);\n\t\tconst px = toScreen(world).distanceTo(rawScreen);\n\t\tif (px < bestPx) {\n\t\t\tbestPx = px;\n\t\t\tbest = world;\n\t\t}\n\t}\n\treturn best;\n}\n\nexport function createMeasureTool(deps: MeasureDeps): MeasureTool {\n\tconst { canvas, scene, getActiveCamera, getViewTarget, labelLayer, options = {} } = deps;\n\tconst snapPixels = options.snapPixels ?? DEFAULT_SNAP_PIXELS;\n\tconst color = new THREE.Color(options.color ?? DEFAULT_COLOR);\n\tconst fmt = makeFormatter(options.displayUnit);\n\tconst defaultFormat = (d: number, delta: THREE.Vector3) =>\n\t\t`${fmt(d)}\\nΔx ${fmt(delta.x)} Δy ${fmt(delta.y)} Δz ${fmt(delta.z)}`;\n\tconst format = options.format ?? defaultFormat;\n\n\tconst raycaster = new THREE.Raycaster();\n\tconst pointer = new THREE.Vector2();\n\n\tlet enabled = false;\n\tconst points: THREE.Vector3[] = [];\n\n\tconst markers: THREE.Points[] = [];\n\tlet line: Line2 | null = null;\n\tlet label: LabelHandle | null = null;\n\n\tconst markerMaterial = new THREE.PointsMaterial({\n\t\tcolor,\n\t\tsize: 8,\n\t\tsizeAttenuation: false,\n\t\tdepthTest: false // markers stay visible through geometry, like CAD snap dots\n\t});\n\n\t// Dimmer + bigger than a committed marker so the next click's snap target is obvious before clicking.\n\tconst hoverMaterial = new THREE.PointsMaterial({\n\t\tcolor,\n\t\tsize: 11,\n\t\tsizeAttenuation: false,\n\t\tdepthTest: false,\n\t\ttransparent: true,\n\t\topacity: 0.5\n\t});\n\tlet hoverMarker: THREE.Points | null = null;\n\n\tconst showHover = (p: THREE.Vector3 | null) => {\n\t\tif (!p) {\n\t\t\tif (hoverMarker) hoverMarker.visible = false;\n\t\t\treturn;\n\t\t}\n\t\tif (!hoverMarker) {\n\t\t\tconst geometry = new THREE.BufferGeometry();\n\t\t\tgeometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0], 3));\n\t\t\thoverMarker = new THREE.Points(geometry, hoverMaterial);\n\t\t\thoverMarker.renderOrder = 1000;\n\t\t\thoverMarker.userData.id = 'measure';\n\t\t\thoverMarker.raycast = () => {};\n\t\t\tscene.add(hoverMarker);\n\t\t}\n\t\thoverMarker.position.copy(p);\n\t\thoverMarker.visible = true;\n\t};\n\n\tconst makeMarker = (p: THREE.Vector3): THREE.Points => {\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tgeometry.setAttribute('position', new THREE.Float32BufferAttribute([p.x, p.y, p.z], 3));\n\t\tconst marker = new THREE.Points(geometry, markerMaterial);\n\t\tmarker.renderOrder = 999;\n\t\tmarker.userData.id = 'measure';\n\t\tmarker.raycast = () => {}; // don't let markers be measure targets themselves\n\t\tscene.add(marker);\n\t\treturn marker;\n\t};\n\n\tconst clear = () => {\n\t\tpoints.length = 0;\n\t\tmarkers.forEach((m) => {\n\t\t\tm.geometry.dispose();\n\t\t\tm.removeFromParent();\n\t\t});\n\t\tmarkers.length = 0;\n\t\tif (line) {\n\t\t\tline.geometry.dispose();\n\t\t\t(line.material as LineMaterial).dispose();\n\t\t\tline.removeFromParent();\n\t\t\tline = null;\n\t\t}\n\t\tlabel?.remove();\n\t\tlabel = null;\n\t};\n\n\tconst drawMeasurement = () => {\n\t\tif (points.length !== 2) return;\n\t\tconst [a, b] = points;\n\n\t\tconst geometry = new LineGeometry();\n\t\tgeometry.setPositions([a.x, a.y, a.z, b.x, b.y, b.z]);\n\t\tconst material = new LineMaterial({ color });\n\t\t(material as LineMaterial & { linewidth: number; depthTest: boolean }).linewidth = 2;\n\t\tmaterial.depthTest = false;\n\n\t\tline = new Line2(geometry, material);\n\t\tline.renderOrder = 998;\n\t\tline.userData.id = 'measure';\n\t\tline.raycast = () => {};\n\t\tscene.add(line);\n\n\t\tconst mid = a.clone().add(b).multiplyScalar(0.5);\n\t\tconst delta = new THREE.Vector3(Math.abs(b.x - a.x), Math.abs(b.y - a.y), Math.abs(b.z - a.z));\n\t\tlabel = labelLayer.addLabel(format(a.distanceTo(b), delta), mid, options.labelClassName);\n\t};\n\n\tconst pickPoint = (event: MouseEvent): THREE.Vector3 | null => {\n\t\tconst rect = canvas.getBoundingClientRect();\n\t\tpointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n\t\tpointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n\n\t\tconst camera = getActiveCamera();\n\t\traycaster.setFromCamera(pointer, camera);\n\n\t\t// Lines/points have no surface area, so the raycast threshold matters — three's default is\n\t\t// nearly unclickable. pickThreshold scales it with the view so it stays constant on screen.\n\t\tconst threshold = pickThreshold(camera, getViewTarget?.());\n\t\traycaster.params.Line!.threshold = threshold;\n\t\traycaster.params.Points!.threshold = threshold;\n\n\t\tconst hits = raycaster\n\t\t\t.intersectObjects(scene.children, true)\n\t\t\t.filter((i) => i.object.userData.id !== 'measure' && i.object.userData.id !== 'grid');\n\n\t\tif (hits.length === 0) return null;\n\t\treturn snapToVertex(hits[0], camera, { width: rect.width, height: rect.height }, snapPixels);\n\t};\n\n\t// One raycast per animation frame, not per mousemove: a full-scene recursive raycast per event\n\t// hitches on large models, and only the latest event matters for the preview.\n\tlet pendingMove: MouseEvent | null = null;\n\tlet moveRaf = 0;\n\n\tconst cancelPendingMove = () => {\n\t\tif (moveRaf) {\n\t\t\tcancelAnimationFrame(moveRaf);\n\t\t\tmoveRaf = 0;\n\t\t}\n\t\tpendingMove = null;\n\t};\n\n\tconst handleMove = (event: MouseEvent): void => {\n\t\tif (!enabled) return;\n\t\tpendingMove = event;\n\t\tif (moveRaf) return;\n\t\tmoveRaf = requestAnimationFrame(() => {\n\t\t\tmoveRaf = 0;\n\t\t\tconst latest = pendingMove;\n\t\t\tpendingMove = null;\n\t\t\tif (!enabled || !latest) return;\n\t\t\tshowHover(pickPoint(latest));\n\t\t});\n\t};\n\n\tconst handleClick = (event: MouseEvent): boolean => {\n\t\tif (!enabled) return false;\n\n\t\t// A third click after a completed measurement starts fresh.\n\t\tif (points.length === 2) clear();\n\n\t\tconst point = pickPoint(event);\n\t\tif (point === null) return true; // consumed: a measuring click that missed still isn't a select\n\n\t\tpoints.push(point);\n\t\tmarkers.push(makeMarker(point));\n\n\t\tif (points.length === 2) drawMeasurement();\n\t\treturn true;\n\t};\n\n\treturn {\n\t\tsetEnabled: (value) => {\n\t\t\tenabled = value;\n\t\t\tif (!value) {\n\t\t\t\tcancelPendingMove();\n\t\t\t\tclear();\n\t\t\t\tshowHover(null);\n\t\t\t}\n\t\t},\n\t\tisEnabled: () => enabled,\n\t\thandleClick,\n\t\thandleMove,\n\t\tclear,\n\t\tdispose: () => {\n\t\t\tcancelPendingMove();\n\t\t\tclear();\n\t\t\tif (hoverMarker) {\n\t\t\t\thoverMarker.geometry.dispose();\n\t\t\t\thoverMarker.removeFromParent();\n\t\t\t\thoverMarker = null;\n\t\t\t}\n\t\t\tmarkerMaterial.dispose();\n\t\t\thoverMaterial.dispose();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\n\nimport { EDGE_USERDATA_KIND } from './edges/options.js';\nimport { membersOf } from './scene-setup/merged-picking.js';\nimport { isViewerAidRoot } from './three-helpers.js';\n\n/**\n * Depth-buffer precision is ∝ near/z²: at the 0.01 m floor a 24-bit buffer resolves ~0.15 mm at\n * 5 m and ~0.25 m at 200 m, so a sheet's two faces fight up close and coplanar surfaces fight far\n * away. Pushing `near` up to a fraction of the view depth of the nearest geometry in view recovers\n * that precision without clipping anything.\n *\n * The nearest geometry is measured on per-chunk triangle bounds, not the scene's bounding sphere:\n * zoomed in on a detail, the camera sits inside the whole model's sphere, which pinned near to the\n * floor exactly when precision mattered most. Chunks outside the frustum's side planes are skipped,\n * since geometry nobody sees can't be clipped.\n *\n * `far` stays owned by config/`updateScene` (must keep covering grid fade, floor). External writes\n * to `camera.near` are adopted as the new lower bound rather than fought — the fitter only ever\n * *raises* near above that floor.\n *\n * Ortho camera needs no fitting (linear depth) and is left untouched.\n */\n\n/** Safety margin: a host's `onFrame` can still move the camera after the fit. */\nconst NEAR_GAP_FRACTION = 0.5;\n/** Caps near so the frustum stays sane if the camera flies out. */\nconst MAX_NEAR_TO_FAR = 0.01;\n/** Skip sub-5% changes so the projection matrix isn't rebuilt every frame while orbiting. */\nconst APPLY_THRESHOLD = 0.05;\n/**\n * Triangles per bounding box. Smaller fits tighter up close but costs more box tests per frame:\n * at 256, a 2M-triangle model is ~8k tests, well under a millisecond.\n */\nconst TRIANGLES_PER_CHUNK = 256;\n\nexport interface NearPlaneFitterOptions {\n\tcamera: THREE.PerspectiveCamera;\n\tscene: THREE.Scene;\n\t/**\n\t * Unit normals of ground planes through the origin carrying ground aids (grid, floor) — their\n\t * perpendicular distance to the camera also bounds near, since the aid re-centers under the\n\t * camera and content distance alone would clip it at grazing views.\n\t *\n\t * Pass a callback returning only planes whose aid is visible this frame: an aid that's hidden\n\t * (not merely absent) must not collapse `near` to protect geometry nobody can see.\n\t */\n\tgroundNormals?: () => THREE.Vector3[];\n}\n\nexport interface NearPlaneFitter {\n\tupdate: () => void;\n}\n\nconst NO_GROUND_NORMALS: THREE.Vector3[] = [];\n\nexport function createNearPlaneFitter({\n\tcamera,\n\tscene,\n\tgroundNormals = () => NO_GROUND_NORMALS\n}: NearPlaneFitterOptions): NearPlaneFitter {\n\tlet baseNear = camera.near;\n\tlet appliedNear = camera.near;\n\n\tconst nearest = createNearestDepthQuery();\n\n\tconst update = () => {\n\t\tif (camera.near !== appliedNear) baseNear = camera.near; // external write → new floor\n\n\t\tconst depth = nearest.measure(scene, camera);\n\t\tlet near = baseNear;\n\t\tif (depth !== null) {\n\t\t\tlet gap = depth;\n\t\t\tfor (const normal of groundNormals()) {\n\t\t\t\tgap = Math.min(gap, Math.abs(camera.position.dot(normal)));\n\t\t\t}\n\t\t\tnear = THREE.MathUtils.clamp(gap * NEAR_GAP_FRACTION, baseNear, camera.far * MAX_NEAR_TO_FAR);\n\t\t}\n\n\t\tif (Math.abs(near - appliedNear) > appliedNear * APPLY_THRESHOLD) {\n\t\t\tcamera.near = near;\n\t\t\tcamera.updateProjectionMatrix();\n\t\t\tappliedNear = near;\n\t\t}\n\t};\n\n\treturn { update };\n}\n\n// ============================================================================\n// Nearest view depth\n// ============================================================================\n\ninterface Chunks {\n\t/** min xyz, max xyz per chunk, in the geometry's local space. */\n\tboxes: Float32Array;\n\t/** First and past-the-end triangle per chunk. */\n\tranges: Uint32Array;\n}\n\ninterface ChunkBounds extends Chunks {\n\tposition: THREE.BufferAttribute | THREE.InterleavedBufferAttribute;\n\tpositionVersion: number;\n\tindex: THREE.BufferAttribute | null;\n\tindexVersion: number;\n}\n\n/**\n * Smallest view depth (distance along the camera's forward axis) of any content inside the\n * frustum's side planes: `null` when the scene has no content, `Infinity` when none of it is in\n * view.\n *\n * A chunk's box only bounds that depth from below, and loosely: a big box reaches beside and behind\n * the camera even when its triangles are all well ahead. One 60-triangle wall around a building is\n * one box the size of the building, which used to pin near to the floor from anywhere in it. So a\n * box that could still be the nearest has its triangles clipped to the frustum and measured\n * exactly; boxes already further than the best so far are skipped.\n */\nfunction createNearestDepthQuery() {\n\tconst chunkCache = new WeakMap<THREE.BufferGeometry, ChunkBounds>();\n\tconst frustum = new THREE.Frustum();\n\tconst viewProjection = new THREE.Matrix4();\n\tconst forward = new THREE.Vector3();\n\tconst box = new THREE.Box3();\n\t// Four side planes then the depth plane, each (a, b, c, d) with a·x + b·y + c·z + d.\n\tconst worldPlanes = new Float64Array(20);\n\tconst localPlanes = new Float64Array(20);\n\tconst singleBox = new Float32Array(6);\n\t// Sutherland–Hodgman ping-pong buffers: a triangle clipped by four planes keeps at most 7 corners.\n\tlet polygon = new Float64Array(21);\n\tlet clipped = new Float64Array(21);\n\n\tlet nearest = Infinity;\n\tlet sawContent = false;\n\n\tconst chunksOf = (object: THREE.Object3D, geometry: THREE.BufferGeometry): ChunkBounds => {\n\t\tconst position = geometry.getAttribute('position');\n\t\tconst index = geometry.getIndex();\n\t\tconst cached = chunkCache.get(geometry);\n\t\tif (\n\t\t\tcached &&\n\t\t\tcached.position === position &&\n\t\t\tcached.positionVersion === versionOf(position) &&\n\t\t\tcached.index === index &&\n\t\t\tcached.indexVersion === (index?.version ?? -1)\n\t\t) {\n\t\t\treturn cached;\n\t\t}\n\t\tconst built: ChunkBounds = {\n\t\t\t...buildChunkBoxes(position, index, memberStarts(object)),\n\t\t\tposition,\n\t\t\tpositionVersion: versionOf(position),\n\t\t\tindex,\n\t\t\tindexVersion: index?.version ?? -1\n\t\t};\n\t\tchunkCache.set(geometry, built);\n\t\treturn built;\n\t};\n\n\t/** Exact nearest depth of a chunk's triangles inside the frustum's side planes. */\n\tconst chunkDepth = (chunks: ChunkBounds, chunk: number, planes: Float64Array): number => {\n\t\tconst { position, index } = chunks;\n\t\tconst indices = index?.array as ArrayLike<number> | undefined;\n\t\tlet best = Infinity;\n\t\tfor (let t = chunks.ranges[chunk * 2]!; t < chunks.ranges[chunk * 2 + 1]!; t++) {\n\t\t\tfor (let corner = 0; corner < 3; corner++) {\n\t\t\t\tconst v = indices ? indices[t * 3 + corner]! : t * 3 + corner;\n\t\t\t\tpolygon[corner * 3] = position.getX(v);\n\t\t\t\tpolygon[corner * 3 + 1] = position.getY(v);\n\t\t\t\tpolygon[corner * 3 + 2] = position.getZ(v);\n\t\t\t}\n\t\t\tlet count = 3;\n\t\t\tfor (let p = 0; p < 16 && count > 0; p += 4) {\n\t\t\t\tconst a = planes[p]!;\n\t\t\t\tconst b = planes[p + 1]!;\n\t\t\t\tconst c = planes[p + 2]!;\n\t\t\t\tconst d = planes[p + 3]!;\n\t\t\t\tlet kept = 0;\n\t\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\t\tconst j = (i + 1) % count;\n\t\t\t\t\tconst ix = polygon[i * 3]!;\n\t\t\t\t\tconst iy = polygon[i * 3 + 1]!;\n\t\t\t\t\tconst iz = polygon[i * 3 + 2]!;\n\t\t\t\t\tconst jx = polygon[j * 3]!;\n\t\t\t\t\tconst jy = polygon[j * 3 + 1]!;\n\t\t\t\t\tconst jz = polygon[j * 3 + 2]!;\n\t\t\t\t\tconst di = a * ix + b * iy + c * iz + d;\n\t\t\t\t\tconst dj = a * jx + b * jy + c * jz + d;\n\t\t\t\t\tif (di >= 0) {\n\t\t\t\t\t\tclipped[kept * 3] = ix;\n\t\t\t\t\t\tclipped[kept * 3 + 1] = iy;\n\t\t\t\t\t\tclipped[kept * 3 + 2] = iz;\n\t\t\t\t\t\tkept++;\n\t\t\t\t\t}\n\t\t\t\t\tif (di >= 0 !== dj >= 0) {\n\t\t\t\t\t\tconst f = di / (di - dj);\n\t\t\t\t\t\tclipped[kept * 3] = ix + (jx - ix) * f;\n\t\t\t\t\t\tclipped[kept * 3 + 1] = iy + (jy - iy) * f;\n\t\t\t\t\t\tclipped[kept * 3 + 2] = iz + (jz - iz) * f;\n\t\t\t\t\t\tkept++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t[polygon, clipped] = [clipped, polygon];\n\t\t\t\tcount = kept;\n\t\t\t}\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tconst depth =\n\t\t\t\t\tplanes[16]! * polygon[i * 3]! +\n\t\t\t\t\tplanes[17]! * polygon[i * 3 + 1]! +\n\t\t\t\t\tplanes[18]! * polygon[i * 3 + 2]! +\n\t\t\t\t\tplanes[19]!;\n\t\t\t\tif (depth < best) best = depth;\n\t\t\t}\n\t\t}\n\t\treturn best;\n\t};\n\n\t// Moves the world planes into the object's local space instead of moving every box into world\n\t// space: p_world = L·p + t, so n·p_world + d = (Lᵀn)·p + (n·t + d).\n\tconst toLocalPlanes = (matrix: THREE.Matrix4): Float64Array => {\n\t\tconst e = matrix.elements;\n\t\tfor (let i = 0; i < 20; i += 4) {\n\t\t\tconst nx = worldPlanes[i]!;\n\t\t\tconst ny = worldPlanes[i + 1]!;\n\t\t\tconst nz = worldPlanes[i + 2]!;\n\t\t\tlocalPlanes[i] = e[0]! * nx + e[1]! * ny + e[2]! * nz;\n\t\t\tlocalPlanes[i + 1] = e[4]! * nx + e[5]! * ny + e[6]! * nz;\n\t\t\tlocalPlanes[i + 2] = e[8]! * nx + e[9]! * ny + e[10]! * nz;\n\t\t\tlocalPlanes[i + 3] = nx * e[12]! + ny * e[13]! + nz * e[14]! + worldPlanes[i + 3]!;\n\t\t}\n\t\treturn localPlanes;\n\t};\n\n\tconst testBoxes = (boxes: Float32Array, planes: Float64Array, chunks?: ChunkBounds) => {\n\t\touter: for (let b = 0; b < boxes.length; b += 6) {\n\t\t\tconst minX = boxes[b]!;\n\t\t\tconst minY = boxes[b + 1]!;\n\t\t\tconst minZ = boxes[b + 2]!;\n\t\t\tconst maxX = boxes[b + 3]!;\n\t\t\tconst maxY = boxes[b + 4]!;\n\t\t\tconst maxZ = boxes[b + 5]!;\n\t\t\tfor (let p = 0; p < 16; p += 4) {\n\t\t\t\tconst a = planes[p]!;\n\t\t\t\tconst bb = planes[p + 1]!;\n\t\t\t\tconst c = planes[p + 2]!;\n\t\t\t\tconst farthest =\n\t\t\t\t\ta * (a > 0 ? maxX : minX) +\n\t\t\t\t\tbb * (bb > 0 ? maxY : minY) +\n\t\t\t\t\tc * (c > 0 ? maxZ : minZ) +\n\t\t\t\t\tplanes[p + 3]!;\n\t\t\t\tif (farthest < 0) continue outer;\n\t\t\t}\n\t\t\tconst a = planes[16]!;\n\t\t\tconst bb = planes[17]!;\n\t\t\tconst c = planes[18]!;\n\t\t\tconst depth =\n\t\t\t\ta * (a > 0 ? minX : maxX) +\n\t\t\t\tbb * (bb > 0 ? minY : maxY) +\n\t\t\t\tc * (c > 0 ? minZ : maxZ) +\n\t\t\t\tplanes[19]!;\n\t\t\tif (depth >= nearest) continue;\n\t\t\tnearest = chunks ? Math.min(nearest, chunkDepth(chunks, b / 6, planes)) : depth;\n\t\t}\n\t};\n\n\tconst measureObject = (object: THREE.Object3D) => {\n\t\tconst renderable = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tconst geometry = renderable.geometry;\n\t\tif (!geometry?.getAttribute?.('position')) return;\n\t\tsawContent = true;\n\n\t\tif (isPlainTriangleMesh(object)) {\n\t\t\tconst chunks = chunksOf(object, geometry);\n\t\t\ttestBoxes(chunks.boxes, toLocalPlanes(object.matrixWorld), chunks);\n\t\t\treturn;\n\t\t}\n\n\t\t// Lines, points, instanced/skinned meshes: one box for the whole object.\n\t\tconst instanced = object as Partial<THREE.InstancedMesh>;\n\t\tif (instanced.isInstancedMesh) {\n\t\t\tif (!instanced.boundingBox) instanced.computeBoundingBox!();\n\t\t\tbox.copy(instanced.boundingBox!);\n\t\t} else {\n\t\t\tif (!geometry.boundingBox) geometry.computeBoundingBox();\n\t\t\tbox.copy(geometry.boundingBox!);\n\t\t}\n\t\tif (box.isEmpty()) return;\n\t\tsingleBox[0] = box.min.x;\n\t\tsingleBox[1] = box.min.y;\n\t\tsingleBox[2] = box.min.z;\n\t\tsingleBox[3] = box.max.x;\n\t\tsingleBox[4] = box.max.y;\n\t\tsingleBox[5] = box.max.z;\n\t\ttestBoxes(singleBox, toLocalPlanes(object.matrixWorld));\n\t};\n\n\t// Prunes whole subtrees: hidden objects, viewer aids, and edge overlays (they trace their parent\n\t// mesh, and their one big box would defeat the parent's chunking).\n\tconst visit = (object: THREE.Object3D) => {\n\t\tif (!object.visible || isViewerAidRoot(object)) return;\n\t\tif (object.userData.kind === EDGE_USERDATA_KIND) return;\n\t\tmeasureObject(object);\n\t\tfor (const child of object.children) visit(child);\n\t};\n\n\tconst measure = (scene: THREE.Scene, camera: THREE.PerspectiveCamera): number | null => {\n\t\tscene.updateMatrixWorld();\n\t\tcamera.updateMatrixWorld();\n\n\t\tviewProjection.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);\n\t\tfrustum.setFromProjectionMatrix(viewProjection);\n\t\t// planes[0..3] are the side planes; [4]/[5] are far/near, which this must ignore — near is\n\t\t// what's being solved for, and content past far isn't drawn regardless of near.\n\t\tfor (let i = 0; i < 4; i++) {\n\t\t\tconst plane = frustum.planes[i]!;\n\t\t\tworldPlanes[i * 4] = plane.normal.x;\n\t\t\tworldPlanes[i * 4 + 1] = plane.normal.y;\n\t\t\tworldPlanes[i * 4 + 2] = plane.normal.z;\n\t\t\tworldPlanes[i * 4 + 3] = plane.constant;\n\t\t}\n\t\tcamera.getWorldDirection(forward);\n\t\tworldPlanes[16] = forward.x;\n\t\tworldPlanes[17] = forward.y;\n\t\tworldPlanes[18] = forward.z;\n\t\tworldPlanes[19] = -forward.dot(camera.position);\n\n\t\tnearest = Infinity;\n\t\tsawContent = false;\n\t\tfor (const child of scene.children) visit(child);\n\t\treturn sawContent ? nearest : null;\n\t};\n\n\treturn { measure };\n}\n\nfunction isPlainTriangleMesh(object: THREE.Object3D): boolean {\n\tconst mesh = object as Partial<THREE.Mesh & THREE.InstancedMesh & THREE.SkinnedMesh>;\n\tif (!mesh.isMesh || mesh.isInstancedMesh || mesh.isSkinnedMesh) return false;\n\tconst geometry = mesh.geometry as THREE.BufferGeometry & { isInstancedBufferGeometry?: boolean };\n\t// Line2's `position` is a template quad; its points live in instance attributes, which only\n\t// the geometry's own computeBoundingBox reads.\n\tif (geometry.isInstancedBufferGeometry) return false;\n\treturn Object.keys(geometry.morphAttributes).length === 0;\n}\n\nfunction versionOf(attribute: THREE.BufferAttribute | THREE.InterleavedBufferAttribute): number {\n\treturn (attribute as THREE.InterleavedBufferAttribute).isInterleavedBufferAttribute\n\t\t? (attribute as THREE.InterleavedBufferAttribute).data.version\n\t\t: (attribute as THREE.BufferAttribute).version;\n}\n\n/**\n * Triangle offsets where a merged mesh's source objects begin. A chunk spanning two of them would\n * box the empty space between, and a camera parked in that space would read as touching geometry.\n */\nfunction memberStarts(object: THREE.Object3D): number[] {\n\tconst starts: number[] = [];\n\tfor (const member of membersOf(object) ?? []) {\n\t\tif (member.indexStart !== undefined) starts.push(member.indexStart / 3);\n\t}\n\treturn starts.sort((a, b) => a - b);\n}\n\n/**\n * Bounds of consecutive runs of triangles, never crossing a `breaks` offset. Grouped by triangle,\n * not by vertex, so each box holds whole triangles: a big face whose corners all sit off-screen\n * still overlaps the frustum.\n */\nfunction buildChunkBoxes(\n\tposition: THREE.BufferAttribute | THREE.InterleavedBufferAttribute,\n\tindex: THREE.BufferAttribute | null,\n\tbreaks: number[]\n): Chunks {\n\tconst triangleCount = Math.floor((index ? index.count : position.count) / 3);\n\tconst ranges: number[] = [];\n\tlet nextBreak = 0;\n\tfor (let start = 0; start < triangleCount;) {\n\t\twhile (nextBreak < breaks.length && breaks[nextBreak]! <= start) nextBreak++;\n\t\tconst end = Math.min(\n\t\t\ttriangleCount,\n\t\t\tstart + TRIANGLES_PER_CHUNK,\n\t\t\tbreaks[nextBreak] ?? triangleCount\n\t\t);\n\t\tranges.push(start, end);\n\t\tstart = end;\n\t}\n\tconst chunkCount = ranges.length / 2;\n\tconst boxes = new Float32Array(chunkCount * 6);\n\tconst plain = !(position as THREE.InterleavedBufferAttribute).isInterleavedBufferAttribute;\n\tconst array = position.array as ArrayLike<number>;\n\tconst fast = plain && position.itemSize === 3 && !position.normalized;\n\tconst indices = index?.array as ArrayLike<number> | undefined;\n\n\tfor (let chunk = 0; chunk < chunkCount; chunk++) {\n\t\tlet minX = Infinity;\n\t\tlet minY = Infinity;\n\t\tlet minZ = Infinity;\n\t\tlet maxX = -Infinity;\n\t\tlet maxY = -Infinity;\n\t\tlet maxZ = -Infinity;\n\t\tconst end = ranges[chunk * 2 + 1]! * 3;\n\t\tfor (let corner = ranges[chunk * 2]! * 3; corner < end; corner++) {\n\t\t\tconst v = indices ? indices[corner]! : corner;\n\t\t\tconst x = fast ? array[v * 3]! : position.getX(v);\n\t\t\tconst y = fast ? array[v * 3 + 1]! : position.getY(v);\n\t\t\tconst z = fast ? array[v * 3 + 2]! : position.getZ(v);\n\t\t\tif (x < minX) minX = x;\n\t\t\tif (y < minY) minY = y;\n\t\t\tif (z < minZ) minZ = z;\n\t\t\tif (x > maxX) maxX = x;\n\t\t\tif (y > maxY) maxY = y;\n\t\t\tif (z > maxZ) maxZ = z;\n\t\t}\n\t\tconst o = chunk * 6;\n\t\tboxes[o] = minX;\n\t\tboxes[o + 1] = minY;\n\t\tboxes[o + 2] = minZ;\n\t\tboxes[o + 3] = maxX;\n\t\tboxes[o + 4] = maxY;\n\t\tboxes[o + 5] = maxZ;\n\t}\n\treturn { boxes, ranges: Uint32Array.from(ranges) };\n}\n","// ============================================================================\n// Pointer tools: who gets the click first\n// ============================================================================\n//\n// A pointer tool claims canvas input ahead of object selection — measuring a distance or placing\n// a vertex must not also select the mesh under the cursor. The registry owns the ordering and the\n// single-active rule; `initThree` owns the DOM listeners and forwards to it.\n\n/**\n * A tool that can claim canvas pointer input.\n *\n * `handleClick` returning true means the tool consumed the event and the host stops dispatching —\n * no further tool sees it, and object selection doesn't run. `handleMove` never consumes: it runs\n * on every move regardless of which tool is active, so previews can't block orbit or pan.\n */\nexport interface PointerTool {\n\tsetEnabled?(enabled: boolean): void;\n\tisEnabled?(): boolean;\n\t/** Returns true if this tool consumed the click. */\n\thandleClick(event: MouseEvent): boolean;\n\t/** Preview only — must not consume. */\n\thandleMove?(event: MouseEvent): void;\n\tclear?(): void;\n\tdispose?(): void;\n}\n\nexport interface ToolRegistration {\n\t/** Unique within the registry; registering the same id twice replaces the earlier tool. */\n\tid: string;\n\ttool: PointerTool;\n\t/**\n\t * Higher runs first. The built-ins sit at 0 (measure) and -100 (gizmo); register above 0 to\n\t * claim clicks before measuring, below -100 to act only as a fallback.\n\t */\n\tpriority?: number;\n}\n\nexport interface ToolRegistry {\n\t/** Returns an unregister function. Does not dispose the tool — the registrant still owns it. */\n\tregister(registration: ToolRegistration): () => void;\n\tunregister(id: string): void;\n\tget(id: string): PointerTool | null;\n\t/**\n\t * Enables one tool and disables every other registered one. Pass null to disable all.\n\t * Tools without `setEnabled` are always live and unaffected.\n\t */\n\tsetActive(id: string | null): void;\n\t/** The id passed to the last `setActive`, or null. */\n\tgetActive(): string | null;\n\t/** @internal — `initThree` forwards DOM events here. */\n\thandleClick(event: MouseEvent): boolean;\n\t/** @internal */\n\thandleMove(event: MouseEvent): void;\n}\n\nexport function createToolRegistry(): ToolRegistry {\n\tconst entries: Required<ToolRegistration>[] = [];\n\tlet activeId: string | null = null;\n\n\t// Descending priority, and registration order breaks ties — sort() is stable, so a tool\n\t// registered later never jumps ahead of an equal-priority one already there.\n\tconst sort = () => entries.sort((a, b) => b.priority - a.priority);\n\n\tconst indexOf = (id: string) => entries.findIndex((entry) => entry.id === id);\n\n\tconst unregister = (id: string) => {\n\t\tconst index = indexOf(id);\n\t\tif (index === -1) return;\n\t\tentries.splice(index, 1);\n\t\tif (activeId === id) activeId = null;\n\t};\n\n\tconst register = ({ id, tool, priority = 0 }: ToolRegistration) => {\n\t\tunregister(id);\n\t\tentries.push({ id, tool, priority });\n\t\tsort();\n\t\treturn () => unregister(id);\n\t};\n\n\tconst setActive = (id: string | null) => {\n\t\tactiveId = id;\n\t\tfor (const entry of entries) {\n\t\t\tentry.tool.setEnabled?.(entry.id === id);\n\t\t}\n\t};\n\n\treturn {\n\t\tregister,\n\t\tunregister,\n\t\tget: (id) => entries[indexOf(id)]?.tool ?? null,\n\t\tsetActive,\n\t\tgetActive: () => activeId,\n\t\thandleClick: (event) => {\n\t\t\t// Snapshot: a tool's handler may register or unregister during dispatch.\n\t\t\tfor (const entry of [...entries]) {\n\t\t\t\tif (entry.tool.handleClick(event)) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\thandleMove: (event) => {\n\t\t\tfor (const entry of [...entries]) {\n\t\t\t\tentry.tool.handleMove?.(event);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Screen-space ray from a canvas mouse event, for tools doing their own picking. Handles the\n * canvas's position and size, so it stays correct under CSS scaling and in fullscreen.\n */\nexport function pointerToNdc(\n\tevent: MouseEvent,\n\tcanvas: HTMLCanvasElement\n): { x: number; y: number } {\n\tconst rect = canvas.getBoundingClientRect();\n\treturn {\n\t\tx: ((event.clientX - rect.left) / rect.width) * 2 - 1,\n\t\ty: -((event.clientY - rect.top) / rect.height) * 2 + 1\n\t};\n}\n","import * as THREE from 'three';\nimport { ViewHelper } from 'three/addons/helpers/ViewHelper.js';\n\nimport type { CameraController } from './camera-controller';\n\n/**\n * Corner nav-cube/axis gizmo. Uses three's {@link ViewHelper} only as the rendered widget, not its\n * click→animate behavior: ViewHelper's snap assumes Y-up and animates straight onto the up axis,\n * which rolls the view and jitters the gizmo at the pole in a Z-up scene. Instead this hit-tests\n * the axis sprites directly and drives the viewer's up-aware camera controller, which snaps\n * instantly with a pole nudge so the orbit basis never degenerates.\n *\n * A click frames the current orbit target (not the world origin) and switches back to perspective\n * first if orthographic — the cube is a 3D-orientation tool.\n *\n * Caller contract (mirrors ViewHelper's own): call {@link ViewGizmo.render} after the main scene\n * render each frame, and forward pointer clicks to {@link ViewGizmo.handleClick}.\n */\nexport interface ViewGizmo {\n\trender(renderer: THREE.WebGLRenderer): void;\n\t/** Returns true if it hit the gizmo (and a view change started). */\n\thandleClick(event: MouseEvent): boolean;\n\tsetVisible(visible: boolean): void;\n\tisVisible(): boolean;\n\tdispose(): void;\n}\n\ninterface ViewGizmoDeps {\n\tcamera: THREE.PerspectiveCamera;\n\tdomElement: HTMLElement;\n\tcontroller: CameraController;\n}\n\nexport function createViewGizmo(deps: ViewGizmoDeps): ViewGizmo {\n\tconst { camera, domElement, controller } = deps;\n\n\tconst helper = new ViewHelper(camera, domElement);\n\thelper.setLabels('X', 'Y', 'Z');\n\n\tlet visible = true;\n\n\t// Mirrors ViewHelper's internal `dim`×`dim` corner-viewport math.\n\tconst DIM = 128;\n\tconst raycaster = new THREE.Raycaster();\n\tconst gizmoCamera = new THREE.OrthographicCamera(-2, 2, 2, -2, 0, 4);\n\tgizmoCamera.position.set(0, 0, 2);\n\t// This camera is never rendered, so nothing else computes its matrixWorld — and\n\t// Raycaster.setFromCamera doesn't either. Without this the ray originates at the identity\n\t// position (z = 0, the cube's mid-plane) and the camera-facing axis sprites sit behind it.\n\tgizmoCamera.updateMatrixWorld();\n\n\t// target → camera, matching CameraController.setViewDirection.\n\tconst AXIS_DIRECTIONS: Record<string, THREE.Vector3> = {\n\t\tposX: new THREE.Vector3(1, 0, 0),\n\t\tnegX: new THREE.Vector3(-1, 0, 0),\n\t\tposY: new THREE.Vector3(0, 1, 0),\n\t\tnegY: new THREE.Vector3(0, -1, 0),\n\t\tposZ: new THREE.Vector3(0, 0, 1),\n\t\tnegZ: new THREE.Vector3(0, 0, -1)\n\t};\n\n\t// Returns the hit sprite's `userData.type`, or null if the click missed the gizmo.\n\tconst pickAxis = (event: MouseEvent): string | null => {\n\t\tconst rect = domElement.getBoundingClientRect();\n\t\t// Gizmo viewport sits in the bottom-right corner (helper.location defaults: right/bottom 0).\n\t\tconst offsetX = rect.left + domElement.offsetWidth - DIM - helper.location.right;\n\t\tconst offsetY = rect.top + domElement.offsetHeight - DIM - helper.location.bottom;\n\n\t\tconst mouse = new THREE.Vector2(\n\t\t\t((event.clientX - offsetX) / DIM) * 2 - 1,\n\t\t\t-((event.clientY - offsetY) / DIM) * 2 + 1\n\t\t);\n\t\tif (Math.abs(mouse.x) > 1 || Math.abs(mouse.y) > 1) return null;\n\n\t\t// Orient the helper as rendered (inverse of the camera) so sprites match what's on screen.\n\t\thelper.quaternion.copy(camera.quaternion).invert();\n\t\thelper.updateMatrixWorld();\n\n\t\traycaster.setFromCamera(mouse, gizmoCamera);\n\t\tconst hits = raycaster.intersectObjects(helper.children, false);\n\t\tfor (const hit of hits) {\n\t\t\tconst type = hit.object.userData?.type;\n\t\t\tif (typeof type === 'string' && type in AXIS_DIRECTIONS) return type;\n\t\t}\n\t\treturn null;\n\t};\n\n\tconst handleClick = (event: MouseEvent): boolean => {\n\t\tif (!visible) return false;\n\n\t\tconst axis = pickAxis(event);\n\t\tif (!axis) return false;\n\n\t\tif (controller.getProjection() === 'orthographic') {\n\t\t\tcontroller.setProjection('perspective');\n\t\t}\n\n\t\tcontroller.setViewDirection(AXIS_DIRECTIONS[axis]!, false);\n\t\treturn true;\n\t};\n\n\treturn {\n\t\trender: (renderer) => {\n\t\t\tif (!visible) return;\n\t\t\t// ViewHelper.render() calls renderer.render() with autoClear=true by default, which wipes\n\t\t\t// the FULL framebuffer before drawing the cube in its corner. Suppress it — ViewHelper\n\t\t\t// does its own depth clear internally.\n\t\t\tconst prevAutoClear = renderer.autoClear;\n\t\t\trenderer.autoClear = false;\n\t\t\thelper.render(renderer);\n\t\t\trenderer.autoClear = prevAutoClear;\n\t\t},\n\t\thandleClick,\n\t\tsetVisible: (value) => {\n\t\t\tvisible = value;\n\t\t},\n\t\tisVisible: () => visible,\n\t\tdispose: () => helper.dispose()\n\t};\n}\n","import * as THREE from 'three';\nimport type { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport type { CameraController } from '../camera-controller.js';\nimport type { Grid } from '../grid.js';\nimport type { LabelLayer } from '../label-layer.js';\nimport type { NearPlaneFitter } from '../near-plane.js';\nimport type { RenderPipeline } from '../render-pipeline.js';\nimport type { ViewGizmo } from '../view-gizmo.js';\n\n// Resize applied before render so buffer clear and draw happen in the same frame — avoids a\n// visible blank frame on resize.\nexport function createAnimationLoop(\n\trenderer: THREE.WebGLRenderer,\n\tscene: THREE.Scene,\n\tcamera: THREE.PerspectiveCamera,\n\tgetActiveCamera: () => THREE.Camera,\n\tcameraController: CameraController,\n\tcontrols: OrbitControls,\n\tgetCanvasSize: () => { width: number; height: number },\n\tgetPixelRatio: () => number,\n\tonFrame?: (delta: number) => void,\n\tgrid?: Grid | null,\n\tgizmo?: ViewGizmo | null,\n\tgetRenderPipeline?: () => RenderPipeline | null,\n\tlabelLayer?: LabelLayer | null,\n\tnearFitter?: NearPlaneFitter | null,\n\t// false = render every frame regardless of invalidate()/camera movement.\n\tonDemand: boolean = true\n): { animate: () => void; dispose: () => void; invalidate: () => void; renderNow: () => void } {\n\tlet animationId: number | null = null;\n\tlet lastTime = performance.now();\n\n\t// The loop always *ticks* (cheap); it only *renders* when invalidate() was called, the active\n\t// camera moved (matrix compare catches damping/presets/gizmo/near-plane), or the idle-repaint\n\t// interval elapsed as a safety net for any mutation that forgot to invalidate.\n\tlet renderRequested = true; // first frame always renders\n\tlet lastRenderTime = 0;\n\tconst IDLE_REPAINT_INTERVAL_MS = 500;\n\tconst lastWorldMatrix = new THREE.Matrix4();\n\tconst lastProjectionMatrix = new THREE.Matrix4();\n\tlet lastCamera: THREE.Camera | null = null;\n\tconst invalidate = () => {\n\t\trenderRequested = true;\n\t};\n\n\tconst cameraMoved = (activeCamera: THREE.Camera): boolean => {\n\t\t// renderer.render normally refreshes matrixWorld, but we're deciding whether to call it — so\n\t\t// refresh here first (cheap: a camera has no deep subtree).\n\t\tactiveCamera.updateMatrixWorld();\n\t\tconst moved =\n\t\t\tlastCamera !== activeCamera ||\n\t\t\t!lastWorldMatrix.equals(activeCamera.matrixWorld) ||\n\t\t\t!lastProjectionMatrix.equals(activeCamera.projectionMatrix);\n\t\tif (moved) {\n\t\t\tlastCamera = activeCamera;\n\t\t\tlastWorldMatrix.copy(activeCamera.matrixWorld);\n\t\t\tlastProjectionMatrix.copy(activeCamera.projectionMatrix);\n\t\t}\n\t\treturn moved;\n\t};\n\n\t// Click-driven mutations (measure points, selection highlights) don't call invalidate() themselves.\n\tconst canvas = renderer.domElement;\n\tconst pointerEvents = ['pointerdown', 'pointerup', 'wheel'] as const;\n\tif (onDemand) {\n\t\tfor (const type of pointerEvents) {\n\t\t\tcanvas.addEventListener(type, invalidate, { passive: true });\n\t\t}\n\t}\n\n\tconst checkResize = () => {\n\t\tconst { width, height } = getCanvasSize();\n\t\tif (width === 0 || height === 0) return;\n\t\tconst pixelRatio = getPixelRatio();\n\n\t\t// Must floor (not round) to match renderer.setSize's own flooring — otherwise the size\n\t\t// comparison below never settles and the resize branch runs every frame.\n\t\tconst newW = Math.floor(width * pixelRatio);\n\t\tconst newH = Math.floor(height * pixelRatio);\n\n\t\tif (renderer.domElement.width !== newW || renderer.domElement.height !== newH) {\n\t\t\trenderer.setPixelRatio(pixelRatio);\n\t\t\trenderer.setSize(width, height, false);\n\t\t\tcamera.aspect = width / height;\n\t\t\tcamera.updateProjectionMatrix();\n\t\t\tcameraController.updateAspect(width, height);\n\t\t\tgetRenderPipeline?.()?.setSize(width, height, pixelRatio);\n\t\t\tlabelLayer?.setSize(width, height); // CSS2D overlay uses CSS size, not the pixel-ratio buffer\n\t\t\tinvalidate();\n\t\t}\n\t};\n\n\tconst drawFrame = (activeCamera: THREE.Camera, delta: number) => {\n\t\tconst renderPipeline = getRenderPipeline?.();\n\t\tif (renderPipeline) {\n\t\t\trenderPipeline.setCamera(activeCamera); // retarget in case 2D/3D swapped\n\t\t\trenderPipeline.render(delta);\n\t\t} else {\n\t\t\trenderer.render(scene, activeCamera);\n\t\t}\n\n\t\tif (labelLayer) labelLayer.render(scene, activeCamera);\n\n\t\t// Corner-viewport overlay with its own clear; must render last to sit on top.\n\t\tif (gizmo) gizmo.render(renderer);\n\t};\n\n\t// Without preserveDrawingBuffer the colour buffer is cleared once the browser composites, so a\n\t// canvas read (toBlob/toDataURL) only sees pixels if it happens in the same task as a draw.\n\tconst renderNow = () => {\n\t\tdrawFrame(getActiveCamera(), 0);\n\t};\n\n\tconst animate = function () {\n\t\tanimationId = requestAnimationFrame(animate);\n\n\t\tconst now = performance.now();\n\t\tconst delta = (now - lastTime) / 1000;\n\t\tlastTime = now;\n\n\t\tcheckResize();\n\n\t\tif (controls.enableDamping || controls.autoRotate) {\n\t\t\tcontrols.update();\n\t\t}\n\n\t\tif (grid) grid.update(getActiveCamera().position); // recenter on camera so it reads as infinite\n\n\t\t// Before render, so depth precision tracks the camera's current distance from content.\n\t\tif (nearFitter) nearFitter.update();\n\n\t\tonFrame?.(delta);\n\n\t\tconst activeCamera = getActiveCamera();\n\n\t\tif (onDemand) {\n\t\t\tconst shouldRender =\n\t\t\t\trenderRequested ||\n\t\t\t\tcameraMoved(activeCamera) ||\n\t\t\t\tnow - lastRenderTime >= IDLE_REPAINT_INTERVAL_MS;\n\t\t\tif (!shouldRender) return;\n\t\t\trenderRequested = false;\n\t\t\tlastRenderTime = now;\n\t\t}\n\n\t\tdrawFrame(activeCamera, delta);\n\t};\n\n\tconst dispose = () => {\n\t\tif (animationId !== null) {\n\t\t\tcancelAnimationFrame(animationId);\n\t\t\tanimationId = null;\n\t\t}\n\t\tif (onDemand) {\n\t\t\tfor (const type of pointerEvents) {\n\t\t\t\tcanvas.removeEventListener(type, invalidate);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn { animate, dispose, invalidate, renderNow };\n}\n","import * as THREE from 'three';\n\nimport {\n\tEMISSIVE_LIGHT,\n\tOWN_EMISSIVE,\n\tgetBaseMaterial,\n\tgetHiddenMembers\n} from '../../shared/index.js';\nimport { SOURCE_COMPUTE } from '../scene-ownership.js';\nimport { memberBounds, membersOf } from './merged-picking.js';\n\n/** Point lights are expensive: every lit material pays for each one in its shader. */\nconst DEFAULT_MAX_LIGHTS = 3;\n/** Six shadow-map faces per light, each drawing the whole scene. */\nconst MAX_SHADOWED_LIGHTS = 2;\nconst SHADOW_MAP_SIZE = 1024;\n/** A glow this far below the strongest is a lit surface, not a source: a diffuser, not the bulb. */\nconst MIN_RELATIVE_STRENGTH = 1 / 3;\n/** Parts this close, as a share of the larger one's diagonal, are one lamp: a bulb and its reflector. */\nconst SAME_LAMP_GAP = 0.05;\n/**\n * three's point lights are in candela, so brightness falls off with the scene's units squared.\n * Scaling by the scene's radius squared makes one strength read the same in millimetres and metres.\n */\nconst INTENSITY_PER_STRENGTH = 0.17;\n/** Share of a source's light that comes back off the room, with no shadow to stop it. */\nconst BOUNCE_SHARE = 0.07;\n/** A lamp's source has a size, so its shadow has a penumbra; three's default edge is a hard line. */\nconst SHADOW_SOFTNESS = 7;\n/** A source wider than this share of the scene isn't a point. */\nconst MAX_POINT_SOURCE_SPREAD = 0.15;\n\n/** One object that glows: a whole mesh, or one member of a merged mesh. */\ntype GlowingPart = {\n\tmesh: THREE.Mesh;\n\tmaterial: THREE.MeshStandardMaterial;\n\tbounds: THREE.Box3;\n};\n\ntype Lamp = {\n\tbounds: THREE.Box3;\n\tmeshes: Set<THREE.Mesh>;\n};\n\ntype Source = Lamp & {\n\tcolor: THREE.Color;\n\tstrength: number;\n\t/** Absolute intensity from real lumens. Without one, scaled to the model. */\n\tlight?: number;\n};\n\n// The base, not `mesh.material`: a selection clone copies the emissive flag beside its red tint.\nfunction emissiveMaterialOf(mesh: THREE.Mesh): THREE.MeshStandardMaterial | null {\n\tconst material = getBaseMaterial(mesh);\n\treturn material?.userData[OWN_EMISSIVE] ? (material as THREE.MeshStandardMaterial) : null;\n}\n\nfunction drawnBounds(mesh: THREE.Mesh): THREE.Box3[] {\n\tconst members = membersOf(mesh);\n\tif (!members) return [new THREE.Box3().setFromObject(mesh)];\n\tconst hidden = getHiddenMembers(mesh);\n\treturn members\n\t\t.filter((_, index) => !hidden.has(index))\n\t\t.map((member) => memberBounds(mesh, member))\n\t\t.filter((bounds) => !bounds.isEmpty());\n}\n\nfunction touches(a: THREE.Box3, b: THREE.Box3): boolean {\n\tconst diagonal = (box: THREE.Box3) => box.getSize(new THREE.Vector3()).length();\n\tconst gap = Math.max(diagonal(a), diagonal(b)) * SAME_LAMP_GAP;\n\treturn a.clone().expandByScalar(gap).intersectsBox(b);\n}\n\n/**\n * Groups parts of one glow into lamps by contact. More lamps than lights get one light in the\n * middle: lighting three of a street's lamps would pick them by mesh order.\n */\nfunction lampsOf(parts: GlowingPart[], maxLights: number): Lamp[] {\n\tlet lamps: Lamp[] = [];\n\tfor (const part of parts) {\n\t\tconst lamp: Lamp = { bounds: part.bounds.clone(), meshes: new Set([part.mesh]) };\n\t\tconst separate: Lamp[] = [];\n\t\tfor (const other of lamps) {\n\t\t\tif (touches(other.bounds, part.bounds)) {\n\t\t\t\tlamp.bounds.union(other.bounds);\n\t\t\t\tfor (const mesh of other.meshes) lamp.meshes.add(mesh);\n\t\t\t} else separate.push(other);\n\t\t}\n\t\tlamps = [...separate, lamp];\n\t\tif (lamps.length > maxLights) {\n\t\t\tconst bounds = new THREE.Box3();\n\t\t\tfor (const each of parts) bounds.union(each.bounds);\n\t\t\treturn [{ bounds, meshes: new Set(parts.map((each) => each.mesh)) }];\n\t\t}\n\t}\n\treturn lamps;\n}\n\n// By glow, not material instance: the parser splits one material between closed and open meshes.\nfunction sourcesFrom(parts: GlowingPart[], maxLights: number): Source[] {\n\tconst byGlow = new Map<string, GlowingPart[]>();\n\tfor (const part of parts) {\n\t\tconst { emissive, emissiveIntensity, userData } = part.material;\n\t\tconst glow = `${emissive.getHexString()}|${emissiveIntensity}|${userData[EMISSIVE_LIGHT]}`;\n\t\tconst same = byGlow.get(glow);\n\t\tif (same) same.push(part);\n\t\telse byGlow.set(glow, [part]);\n\t}\n\treturn [...byGlow.values()].flatMap((same) => {\n\t\tconst { emissive, emissiveIntensity, userData } = same[0]!.material;\n\t\treturn lampsOf(same, maxLights).map((lamp) => ({\n\t\t\t...lamp,\n\t\t\tcolor: emissive,\n\t\t\tstrength: emissiveIntensity,\n\t\t\tlight: userData[EMISSIVE_LIGHT]\n\t\t}));\n\t});\n}\n\n/**\n * Picks the strongest emissive materials to light the scene. Exported for tests: which surface\n * counts as a source decides whether a lamp's desk gets one pool of light or a washed-out pair.\n */\nexport function pickLightSources<S extends { strength: number }>(\n\tsources: S[],\n\tmaxLights = DEFAULT_MAX_LIGHTS\n): S[] {\n\tconst strongest = Math.max(0, ...sources.map((s) => s.strength));\n\treturn sources\n\t\t.filter((s) => s.strength >= strongest * MIN_RELATIVE_STRENGTH)\n\t\t.sort((a, b) => b.strength - a.strength)\n\t\t.slice(0, maxLights);\n}\n\nexport interface EmissiveLights {\n\t/**\n\t * Rebuilds from what the scene draws now, so call it after anything is shown or hidden.\n\t * `enabled` false removes every light.\n\t */\n\tsync(enabled: boolean, maxLights?: number): void;\n\tdispose(): void;\n}\n\n/**\n * An emissive surface only looks bright, it lights nothing, so a look that wants real light from\n * glowing parts gets a point light at each lamp. A picked lamp stops casting shadows meanwhile, and\n * so does a dimmer glow around its light: a bulb or its diffuser would otherwise block the light.\n */\nexport function createEmissiveLights(scene: THREE.Scene): EmissiveLights {\n\tconst lights: { key: THREE.PointLight; bounce: THREE.PointLight }[] = [];\n\tconst silenced = new Set<THREE.Mesh>();\n\n\tconst addLight = (shadowed: boolean) => {\n\t\tconst key = new THREE.PointLight(0xffffff, 1, 0, 2);\n\t\t// No real bounce here, so anything outside the shade's cone would go flat. This stands in for\n\t\t// the room: light from the same place that the shade doesn't stop. Decay 1 falls off slower\n\t\t// than the key light, so it fills the room.\n\t\tconst bounce = new THREE.PointLight(0xffffff, 1, 0, 1);\n\t\tif (shadowed) {\n\t\t\tkey.castShadow = true;\n\t\t\tkey.shadow.mapSize.setScalar(SHADOW_MAP_SIZE);\n\t\t\tkey.shadow.radius = SHADOW_SOFTNESS;\n\t\t\t// Solve output only changes between syncs, and six faces every frame would sink orbiting.\n\t\t\tkey.shadow.autoUpdate = false;\n\t\t}\n\t\tconst pair = { key, bounce };\n\t\tlights.push(pair);\n\t\tscene.add(key, bounce);\n\t\treturn pair;\n\t};\n\n\tconst collect = () => {\n\t\tconst content = new THREE.Box3();\n\t\tconst parts: GlowingPart[] = [];\n\t\t// A hidden group hides its meshes without touching their own `.visible`.\n\t\tscene.traverseVisible((object) => {\n\t\t\tconst mesh = object as THREE.Mesh;\n\t\t\tif (!mesh.isMesh || object.userData.source !== SOURCE_COMPUTE) return;\n\t\t\tcontent.union(new THREE.Box3().setFromObject(mesh));\n\t\t\tconst material = emissiveMaterialOf(mesh);\n\t\t\tif (!material) return;\n\t\t\tfor (const bounds of drawnBounds(mesh)) parts.push({ mesh, material, bounds });\n\t\t});\n\t\treturn { parts, radius: content.getSize(new THREE.Vector3()).length() / 2 };\n\t};\n\n\tconst silence = (parts: GlowingPart[], sources: Source[]) => {\n\t\tfor (const { mesh, bounds } of parts) {\n\t\t\tconst blocksALight = sources.some(\n\t\t\t\t(source, index) =>\n\t\t\t\t\tsource.meshes.has(mesh) || bounds.containsPoint(lights[index]!.key.position)\n\t\t\t);\n\t\t\tif (blocksALight && mesh.castShadow) {\n\t\t\t\tmesh.castShadow = false;\n\t\t\t\tsilenced.add(mesh);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst sync = (enabled: boolean, maxLights = DEFAULT_MAX_LIGHTS) => {\n\t\tfor (const mesh of silenced) mesh.castShadow = true;\n\t\tsilenced.clear();\n\n\t\tconst { parts, radius } = enabled ? collect() : { parts: [], radius: 0 };\n\t\tconst sources = pickLightSources(sourcesFrom(parts, maxLights), maxLights);\n\n\t\t// Reused across solves: a new light reallocates its shadow map and recompiles every shader.\n\t\tsources.forEach((source, index) => {\n\t\t\tconst { key, bounce } = lights[index] ?? addLight(index < MAX_SHADOWED_LIGHTS);\n\t\t\tconst intensity = source.light ?? source.strength * INTENSITY_PER_STRENGTH * radius * radius;\n\t\t\tkey.color.copy(source.color);\n\t\t\t// Lit windows across a facade end up as one light in its middle, which burns a hole in the\n\t\t\t// wall. Only the bounce's even wash suits a glow that wide. Zeroed, not removed: removing a\n\t\t\t// light recompiles every shader.\n\t\t\tconst spread = source.bounds.getSize(new THREE.Vector3()).length() / 2 / radius;\n\t\t\tkey.intensity = !source.light && spread > MAX_POINT_SOURCE_SPREAD ? 0 : intensity;\n\t\t\tsource.bounds.getCenter(key.position);\n\t\t\tbounce.color.copy(source.color);\n\t\t\t// Over the radius because decay 1 scales with distance once, not squared like the key.\n\t\t\tbounce.intensity = (intensity * BOUNCE_SHARE) / radius;\n\t\t\tbounce.position.copy(key.position);\n\t\t\tif (key.castShadow) {\n\t\t\t\tkey.shadow.camera.near = radius * 0.005;\n\t\t\t\tkey.shadow.camera.far = radius * 4;\n\t\t\t\t// three only refreshes this on a change of `light.distance`, and ours stays 0.\n\t\t\t\tkey.shadow.camera.updateProjectionMatrix();\n\t\t\t\tkey.shadow.bias = -0.002;\n\t\t\t\tkey.shadow.normalBias = radius * 0.002;\n\t\t\t\tkey.shadow.needsUpdate = true;\n\t\t\t}\n\t\t});\n\t\twhile (lights.length > sources.length) {\n\t\t\tconst { key, bounce } = lights.pop()!;\n\t\t\tscene.remove(key, bounce);\n\t\t\tkey.dispose();\n\t\t\tbounce.dispose();\n\t\t}\n\t\tsilence(parts, sources);\n\t};\n\n\treturn { sync, dispose: () => sync(false) };\n}\n","import * as THREE from 'three';\n\nimport {\n\tOWN_EMISSIVE,\n\tOWN_ENV_MAP_INTENSITY,\n\tlookEnvMapIntensity,\n\tlookPreset,\n\tmaterialAppearanceForLook\n} from '../../shared/index.js';\nimport { SOURCE_COMPUTE } from '../scene-ownership.js';\nimport { createEmissiveLights } from './emissive-lights.js';\nimport { applyReflectionEnvironment } from './reflection-environment.js';\nimport type {\n\tLook,\n\tLookMaterialOverride,\n\tLookPreset,\n\tMaterialAppearanceOptions\n} from '../types.js';\nimport { defaultUp, type ResolvedOptions } from './defaults.js';\nimport type { PipelineController } from './pipeline-controller.js';\nimport type { SceneLights } from './setup-lighting.js';\n\n/**\n * What a look overwrote, stashed on the material itself so it survives a look switch without a\n * side table keyed on materials the scene may dispose. Written once, on the first override.\n */\ntype MaterialBaseline = {\n\tcolor: number;\n\tmetalness: number;\n\troughness: number;\n\temissiveIntensity: number;\n\topacity: number;\n\ttransparent: boolean;\n\tdepthWrite: boolean;\n\twireframe: boolean;\n\tside: THREE.Side;\n};\n\nconst BASELINE_KEY = '__selvaLookBaseline';\n\ntype OverridableMaterial = THREE.Material & {\n\tcolor?: THREE.Color;\n\tmetalness?: number;\n\troughness?: number;\n\temissiveIntensity?: number;\n\twireframe?: boolean;\n\t[BASELINE_KEY]?: MaterialBaseline;\n};\n\nfunction applyBaseline(target: OverridableMaterial, baseline: MaterialBaseline): void {\n\ttarget.color?.setHex(baseline.color);\n\tif (target.metalness !== undefined) target.metalness = baseline.metalness;\n\tif (target.roughness !== undefined) target.roughness = baseline.roughness;\n\tif (target.emissiveIntensity !== undefined) target.emissiveIntensity = baseline.emissiveIntensity;\n\ttarget.opacity = baseline.opacity;\n\ttarget.transparent = baseline.transparent;\n\ttarget.depthWrite = baseline.depthWrite;\n\tif (target.wireframe !== undefined) target.wireframe = baseline.wireframe;\n\ttarget.side = baseline.side;\n}\n\n/** Restores the parsed values and forgets the baseline. No-op when nothing was overridden. */\nfunction restoreBaseline(target: OverridableMaterial): void {\n\tconst baseline = target[BASELINE_KEY];\n\tif (!baseline) return;\n\tapplyBaseline(target, baseline);\n\tdelete target[BASELINE_KEY];\n}\n\n/**\n * Applies a look's material overrides, or restores the parsed values when the look has none.\n * `needsUpdate` is set because switching `transparent` changes the shader program, not just a\n * uniform.\n *\n * Exported for tests: the leak this guards against is invisible until two overriding looks are\n * applied in sequence, which no single-look check catches.\n *\n * Every apply restores the baseline first, so an override only has to describe what *it* wants.\n * Without that reset, switching between two looks that both override leaves behind whatever the\n * previous one set and the new one is silent about — `wireframe` surviving out of the wireframe\n * look, or `opacity`/`depthWrite` surviving out of xray, since no other look mentions them.\n */\nexport function applyMaterialOverride(\n\tmaterial: THREE.Material,\n\toverride: LookMaterialOverride | undefined\n): void {\n\tconst target = material as OverridableMaterial;\n\n\tif (!override) {\n\t\trestoreBaseline(target);\n\t\ttarget.needsUpdate = true;\n\t\treturn;\n\t}\n\n\tif (!target[BASELINE_KEY]) {\n\t\ttarget[BASELINE_KEY] = {\n\t\t\tcolor: target.color?.getHex() ?? 0xffffff,\n\t\t\tmetalness: target.metalness ?? 0,\n\t\t\troughness: target.roughness ?? 1,\n\t\t\temissiveIntensity: target.emissiveIntensity ?? 0,\n\t\t\topacity: target.opacity,\n\t\t\ttransparent: target.transparent,\n\t\t\tdepthWrite: target.depthWrite,\n\t\t\twireframe: target.wireframe ?? false,\n\t\t\tside: target.side\n\t\t};\n\t} else {\n\t\t// Baseline captured by an earlier look — reset onto it so this override starts clean.\n\t\tapplyBaseline(target, target[BASELINE_KEY]);\n\t}\n\n\tif (override.color !== undefined) target.color?.setHex(override.color);\n\tif (override.metalness !== undefined && target.metalness !== undefined) {\n\t\ttarget.metalness = override.metalness;\n\t}\n\tif (override.roughness !== undefined && target.roughness !== undefined) {\n\t\ttarget.roughness = override.roughness;\n\t}\n\t// Only the wire's glow: a selection tint uses the same channel and has to stay readable.\n\tif (override.emissiveIntensity !== undefined && target.userData[OWN_EMISSIVE]) {\n\t\ttarget.emissiveIntensity = override.emissiveIntensity;\n\t}\n\tif (override.opacity !== undefined) {\n\t\ttarget.opacity = override.opacity;\n\t\ttarget.transparent = override.opacity < 1;\n\t}\n\tif (override.depthWrite !== undefined) target.depthWrite = override.depthWrite;\n\tif (override.wireframe !== undefined && target.wireframe !== undefined) {\n\t\ttarget.wireframe = override.wireframe;\n\t}\n\tif (override.side !== undefined) target.side = override.side;\n\ttarget.needsUpdate = true;\n}\n\n/** A material's own value (see `OWN_ENV_MAP_INTENSITY`) unless the look restyles every material. */\nexport function envMapIntensityForLook(material: THREE.Material, preset: LookPreset): number {\n\tif (preset.materialOverride) return preset.envMapIntensity;\n\tconst own = material.userData[OWN_ENV_MAP_INTENSITY] as number | undefined;\n\tif (own != null) return own;\n\t// Runs before the previous look's override is lifted, so read the parsed metalness.\n\tconst target = material as OverridableMaterial;\n\tconst metalness = target[BASELINE_KEY]?.metalness ?? target.metalness ?? 0;\n\treturn lookEnvMapIntensity(preset.envMapIntensity, metalness);\n}\n\nexport type SetLookOptions = {\n\t/**\n\t * Skips the sync, which walks every glowing part. The caller runs `syncEmissiveLights()` itself\n\t * once visibility settles.\n\t */\n\tdeferEmissiveLights?: boolean;\n};\n\nexport function lookPixelRatio(fullPixelRatio: number, preset: LookPreset): number {\n\treturn Math.min(fullPixelRatio, preset.maxPixelRatio ?? fullPixelRatio);\n}\n\n/** The runtime lighting/material dials — everything a host can retune without rebuilding the scene. */\nexport interface AppearanceController {\n\tsetFillLights(opts: {\n\t\themisphereIntensity?: number;\n\t\themisphereSkyColor?: THREE.Color | number;\n\t\themisphereGroundColor?: THREE.Color | number;\n\t\tambientIntensity?: number;\n\t}): void;\n\tsetEnvironmentIntensity(intensity: number): void;\n\tsetToneMappingExposure(exposure: number): void;\n\tsetAoIntensity(intensity: number): void;\n\tsetLook(look: Look, options?: SetLookOptions): void;\n\tsyncEmissiveLights(): void;\n\tgetMaterialAppearance(): MaterialAppearanceOptions;\n}\n\n// setLook is built from the same setters a host would call directly, so construction-time defaults\n// (applyDefaults seeding from LOOK_PRESETS) can't drift from the runtime path.\nexport function createAppearanceController(params: {\n\tscene: THREE.Scene;\n\trenderer: THREE.WebGLRenderer;\n\tlights: SceneLights;\n\tconfig: ResolvedOptions;\n\tpipeline: PipelineController;\n\t/** Before any look's cap. */\n\tfullPixelRatio: number;\n\trequestRender: () => void;\n}): AppearanceController {\n\tconst { scene, renderer, lights, config, pipeline, fullPixelRatio, requestRender } = params;\n\n\tconst presetOf = (look: Look) => lookPreset(look, config.render.lowPower);\n\tlet activeLook: Look = config.look;\n\tconst emissiveLights = createEmissiveLights(scene);\n\t// VSM can't shadow a point light, and three then drops the shadow with only a console warning.\n\tconst ownShadowType = renderer.shadowMap.type;\n\tconst applyShadowType = (preset: LookPreset) => {\n\t\tconst type = preset.emissiveLights ? THREE.PCFShadowMap : ownShadowType;\n\t\tif (renderer.shadowMap.type === type) return;\n\t\trenderer.shadowMap.type = type;\n\t\trenderer.shadowMap.needsUpdate = true;\n\t};\n\n\t// The scene's own background, held while a look replaces it.\n\tlet ownBackground: THREE.Scene['background'] | undefined;\n\n\tconst applyBackground = (preset: LookPreset) => {\n\t\tif (preset.background === undefined) {\n\t\t\tif (ownBackground !== undefined) scene.background = ownBackground;\n\t\t\townBackground = undefined;\n\t\t\treturn;\n\t\t}\n\t\townBackground ??= scene.background;\n\t\tscene.background = new THREE.Color(preset.background);\n\t};\n\tapplyBackground(presetOf(activeLook));\n\tapplyShadowType(presetOf(activeLook));\n\n\tconst setFillLights: AppearanceController['setFillLights'] = (opts) => {\n\t\tif (opts.ambientIntensity !== undefined) {\n\t\t\tlights.ambient.intensity = opts.ambientIntensity;\n\t\t}\n\t\tif (\n\t\t\topts.hemisphereIntensity !== undefined &&\n\t\t\t!lights.hemisphere &&\n\t\t\topts.hemisphereIntensity > 0\n\t\t) {\n\t\t\t// Lazily created so hosts can enable fill at runtime even if the scene was built without one.\n\t\t\tlights.hemisphere = new THREE.HemisphereLight(\n\t\t\t\topts.hemisphereSkyColor ?? config.lighting.hemisphereSkyColor,\n\t\t\t\topts.hemisphereGroundColor ?? config.lighting.hemisphereGroundColor,\n\t\t\t\topts.hemisphereIntensity\n\t\t\t);\n\t\t\tlights.hemisphere.position.copy(config.environment.sceneUp ?? defaultUp);\n\t\t\tscene.add(lights.hemisphere);\n\t\t}\n\t\tif (lights.hemisphere) {\n\t\t\tif (opts.hemisphereIntensity !== undefined)\n\t\t\t\tlights.hemisphere.intensity = opts.hemisphereIntensity;\n\t\t\tif (opts.hemisphereSkyColor !== undefined)\n\t\t\t\tlights.hemisphere.color.set(opts.hemisphereSkyColor);\n\t\t\tif (opts.hemisphereGroundColor !== undefined)\n\t\t\t\tlights.hemisphere.groundColor.set(opts.hemisphereGroundColor);\n\t\t}\n\t\trequestRender();\n\t};\n\n\tconst setEnvironmentIntensity = (intensity: number) => {\n\t\tconfig.environment.environmentIntensity = intensity;\n\t\tscene.environmentIntensity = intensity;\n\t\trequestRender();\n\t};\n\n\tconst setToneMappingExposure = (exposure: number) => {\n\t\tconfig.render.toneMappingExposure = exposure;\n\t\trenderer.toneMappingExposure = exposure;\n\t\t// Composer applies tone mapping via its own OutputPass, so it must rebuild to pick this up.\n\t\tif (pipeline.get()) pipeline.rebuild();\n\t};\n\n\tconst setAoIntensity = (intensity: number) => {\n\t\tconfig.render.aoIntensity = intensity;\n\t\tif (pipeline.get()) pipeline.rebuild();\n\t};\n\n\tconst syncEmissiveLights = () => {\n\t\tconst { emissiveLights: enabled, maxEmissiveLights } = presetOf(activeLook);\n\t\temissiveLights.sync(!!enabled, maxEmissiveLights);\n\t\trequestRender();\n\t};\n\n\tconst setLook: AppearanceController['setLook'] = (look, options) => {\n\t\tconst preset = presetOf(look);\n\t\tactiveLook = look;\n\n\t\trenderer.toneMapping = preset.toneMapping;\n\t\trenderer.toneMappingExposure = preset.toneMappingExposure;\n\t\tconfig.render.toneMapping = preset.toneMapping;\n\t\tconfig.render.toneMappingExposure = preset.toneMappingExposure;\n\n\t\tsetFillLights({\n\t\t\themisphereIntensity: preset.hemisphereIntensity,\n\t\t\tambientIntensity: preset.ambientIntensity\n\t\t});\n\t\tsetEnvironmentIntensity(preset.environmentIntensity);\n\n\t\t// Null when the viewer was built with sunlight off; such a host has opted out of key lighting\n\t\t// entirely, and a look switch is not the place to opt it back in.\n\t\tif (lights.sun) {\n\t\t\tlights.sun.intensity = preset.sunlightIntensity;\n\t\t\tconfig.lighting.sunlightIntensity = preset.sunlightIntensity;\n\t\t}\n\n\t\tapplyBackground(preset);\n\t\tapplyShadowType(preset);\n\t\tconfig.render.bloom = preset.bloom;\n\t\t// The render loop's resize check applies it; set before the rebuild so the composer starts right.\n\t\tconfig.render.pixelRatio = lookPixelRatio(fullPixelRatio, preset);\n\n\t\t// Also rebuilds a live composer whose tone mapping or bloom just changed.\n\t\tpipeline.setAmbientOcclusion(preset.ambientOcclusion);\n\n\t\t// Solve output only. Host-added geometry (`user`/`app:` scopes) owns its own materials —\n\t\t// a point cloud or draft line has a deliberate look that a render-style switch must not\n\t\t// overwrite. Hosts that do want to follow the look read `getMaterialAppearance()`.\n\t\tscene.traverse((object) => {\n\t\t\tif (object.userData.source !== SOURCE_COMPUTE) return;\n\t\t\tconst mesh = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tconst materials = Array.isArray(mesh.material)\n\t\t\t\t? mesh.material\n\t\t\t\t: mesh.material\n\t\t\t\t\t? [mesh.material]\n\t\t\t\t\t: [];\n\t\t\tfor (const material of materials) {\n\t\t\t\tif ('envMapIntensity' in material) {\n\t\t\t\t\t(material as THREE.MeshStandardMaterial).envMapIntensity = envMapIntensityForLook(\n\t\t\t\t\t\tmaterial,\n\t\t\t\t\t\tpreset\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tapplyMaterialOverride(material as THREE.Material, preset.materialOverride);\n\t\t\t}\n\t\t});\n\t\t// An override can zero metalness, and lifting it restores it.\n\t\tapplyReflectionEnvironment(scene, scene);\n\t\tif (!options?.deferEmissiveLights) syncEmissiveLights();\n\t};\n\n\treturn {\n\t\tsetFillLights,\n\t\tsetEnvironmentIntensity,\n\t\tsetToneMappingExposure,\n\t\tsetAoIntensity,\n\t\tsetLook,\n\t\tsyncEmissiveLights,\n\t\tgetMaterialAppearance: () => materialAppearanceForLook(activeLook)\n\t};\n}\n","import * as THREE from 'three';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function createCamera(\n\tconfig: ResolvedOptions,\n\tcanvas: HTMLCanvasElement\n): THREE.PerspectiveCamera {\n\tconst parent = canvas.parentElement;\n\tconst width = parent ? parent.clientWidth : window.innerWidth;\n\tconst height = parent ? parent.clientHeight : window.innerHeight;\n\n\tconst camera = new THREE.PerspectiveCamera(\n\t\tconfig.camera.fov,\n\t\twidth / height,\n\t\tconfig.camera.near,\n\t\tconfig.camera.far\n\t);\n\n\tconst pos = config.camera.position;\n\tif (pos) {\n\t\tcamera.position.set(pos.x, pos.y, pos.z);\n\t}\n\n\treturn camera;\n}\n","import * as THREE from 'three';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function createScene(config: ResolvedOptions): THREE.Scene {\n\tconst scene = new THREE.Scene();\n\n\tconst bgColor =\n\t\ttypeof config.environment.backgroundColor === 'string'\n\t\t\t? new THREE.Color(config.environment.backgroundColor)\n\t\t\t: config.environment.backgroundColor;\n\tscene.background = bgColor || null;\n\n\treturn scene;\n}\n","import * as THREE from 'three';\n\nimport { disposeObjectTree, type DisposeOptions } from '../../shared/index.js';\nimport { disposeReflectionEnvironment } from './reflection-environment.js';\n\nexport { disposeObjectTree };\nexport type { DisposeOptions };\n\n/** Sweeps every renderable plus the scene-level textures the object traversal can't reach. */\nexport function disposeSceneResources(scene: THREE.Scene, options?: DisposeOptions): void {\n\tdisposeObjectTree(scene, options);\n\n\tscene.environment?.dispose();\n\tdisposeReflectionEnvironment(scene);\n\tif (scene.background instanceof THREE.Texture) {\n\t\tscene.background.dispose();\n\t}\n}\n","import * as THREE from 'three';\nimport { Pass, FullScreenQuad } from 'three/addons/postprocessing/Pass.js';\n\n/**\n * Screen-space edge detection (Roberts cross on depth + normal discontinuities), O(pixels)\n * regardless of triangle count. Fallback for meshes too heavy for geometry edge overlays\n * (over `EdgeOptions.maxTriangles`): uniform pixel width, one global color, view-dependent,\n * gentle creases below the normal threshold don't register.\n */\nexport interface EdgeDetectionOptions {\n\tcolor?: THREE.ColorRepresentation;\n\topacity?: number;\n\t/** Summed `1 - dot(n₁, n₂)` across the two diagonal pairs. Lower catches gentler creases. */\n\tnormalThreshold?: number;\n\t/** Relative view-depth discontinuity, fraction of center depth. */\n\tdepthThreshold?: number;\n\t/** Sample offset in device px — line thickness. */\n\tthickness?: number;\n}\n\nconst EDGE_SHADER = {\n\tuniforms: {\n\t\ttDiffuse: { value: null as THREE.Texture | null },\n\t\ttNormal: { value: null as THREE.Texture | null },\n\t\ttDepth: { value: null as THREE.Texture | null },\n\t\tuResolution: { value: new THREE.Vector2(1, 1) },\n\t\tuColor: { value: new THREE.Color(0x222222) },\n\t\tuOpacity: { value: 1 },\n\t\tuNormalThreshold: { value: 0.4 },\n\t\tuDepthThreshold: { value: 0.02 },\n\t\tuThickness: { value: 1 },\n\t\tuNear: { value: 0.1 },\n\t\tuFar: { value: 1000 },\n\t\tuPerspective: { value: 1 }\n\t},\n\tvertexShader: /* glsl */ `\n\t\tvarying vec2 vUv;\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n\t\t}\n\t`,\n\tfragmentShader: /* glsl */ `\n\t\tuniform sampler2D tDiffuse;\n\t\tuniform sampler2D tNormal;\n\t\tuniform sampler2D tDepth;\n\t\tuniform vec2 uResolution;\n\t\tuniform vec3 uColor;\n\t\tuniform float uOpacity;\n\t\tuniform float uNormalThreshold;\n\t\tuniform float uDepthThreshold;\n\t\tuniform float uThickness;\n\t\tuniform float uNear;\n\t\tuniform float uFar;\n\t\tuniform float uPerspective;\n\t\tvarying vec2 vUv;\n\n\t\tfloat viewZOf(const in float depth) {\n\t\t\tfloat perspective = (uNear * uFar) / ((uFar - uNear) * depth - uFar);\n\t\t\tfloat orthographic = -(depth * (uFar - uNear) + uNear);\n\t\t\treturn mix(orthographic, perspective, uPerspective);\n\t\t}\n\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D(tDiffuse, vUv);\n\t\t\tvec2 texel = uThickness / uResolution;\n\n\t\t\tvec2 offsetA = vec2(texel.x, texel.y);\n\t\t\tvec2 offsetB = vec2(texel.x, -texel.y);\n\n\t\t\tfloat z0 = viewZOf(texture2D(tDepth, vUv + offsetA).x);\n\t\t\tfloat z1 = viewZOf(texture2D(tDepth, vUv - offsetA).x);\n\t\t\tfloat z2 = viewZOf(texture2D(tDepth, vUv + offsetB).x);\n\t\t\tfloat z3 = viewZOf(texture2D(tDepth, vUv - offsetB).x);\n\t\t\tfloat zCenter = viewZOf(texture2D(tDepth, vUv).x);\n\t\t\t// Normalized by center depth, not absolute Z: keeps the response scale-invariant (an\n\t\t\t// absolute threshold would be noise far away, blind up close).\n\t\t\tfloat depthDelta = (abs(z0 - z1) + abs(z2 - z3)) / max(abs(zCenter), 1e-6);\n\t\t\tfloat depthEdge = step(uDepthThreshold, depthDelta);\n\n\t\t\tvec3 n0 = texture2D(tNormal, vUv + offsetA).rgb * 2.0 - 1.0;\n\t\t\tvec3 n1 = texture2D(tNormal, vUv - offsetA).rgb * 2.0 - 1.0;\n\t\t\tvec3 n2 = texture2D(tNormal, vUv + offsetB).rgb * 2.0 - 1.0;\n\t\t\tvec3 n3 = texture2D(tNormal, vUv - offsetB).rgb * 2.0 - 1.0;\n\t\t\tfloat normalDelta = (1.0 - dot(n0, n1)) + (1.0 - dot(n2, n3));\n\t\t\tfloat normalEdge = step(uNormalThreshold, normalDelta);\n\n\t\t\tfloat edge = max(depthEdge, normalEdge) * uOpacity;\n\t\t\tgl_FragColor = vec4(mix(color.rgb, uColor, edge), color.a);\n\t\t}\n\t`\n};\n\nexport class EdgeDetectionPass extends Pass {\n\tcamera: THREE.Camera;\n\n\tprivate readonly scene: THREE.Scene;\n\tprivate readonly normalMaterial: THREE.MeshNormalMaterial;\n\tprivate readonly edgeMaterial: THREE.ShaderMaterial;\n\tprivate readonly fsQuad: FullScreenQuad;\n\tprivate normalTarget: THREE.WebGLRenderTarget | null = null;\n\tprivate width: number;\n\tprivate height: number;\n\n\tconstructor(\n\t\tscene: THREE.Scene,\n\t\tcamera: THREE.Camera,\n\t\twidth: number,\n\t\theight: number,\n\t\toptions: EdgeDetectionOptions = {}\n\t) {\n\t\tsuper();\n\t\tthis.scene = scene;\n\t\tthis.camera = camera;\n\t\tthis.width = Math.max(1, width);\n\t\tthis.height = Math.max(1, height);\n\n\t\tthis.normalMaterial = new THREE.MeshNormalMaterial();\n\t\tthis.normalMaterial.blending = THREE.NoBlending;\n\n\t\tthis.edgeMaterial = new THREE.ShaderMaterial({\n\t\t\tuniforms: THREE.UniformsUtils.clone(EDGE_SHADER.uniforms),\n\t\t\tvertexShader: EDGE_SHADER.vertexShader,\n\t\t\tfragmentShader: EDGE_SHADER.fragmentShader\n\t\t});\n\t\tconst uniforms = this.edgeMaterial.uniforms;\n\t\tuniforms.uColor.value = new THREE.Color(options.color ?? 0x222222);\n\t\tuniforms.uOpacity.value = options.opacity ?? 1;\n\t\tuniforms.uNormalThreshold.value = options.normalThreshold ?? 0.4;\n\t\tuniforms.uDepthThreshold.value = options.depthThreshold ?? 0.02;\n\t\tuniforms.uThickness.value = options.thickness ?? 1;\n\n\t\tthis.fsQuad = new FullScreenQuad(this.edgeMaterial);\n\t\tthis.needsSwap = true;\n\t}\n\n\tprivate acquireNormalTarget(): THREE.WebGLRenderTarget {\n\t\tif (!this.normalTarget) {\n\t\t\tconst depthTexture = new THREE.DepthTexture(this.width, this.height);\n\t\t\tthis.normalTarget = new THREE.WebGLRenderTarget(this.width, this.height, {\n\t\t\t\tminFilter: THREE.NearestFilter,\n\t\t\t\tmagFilter: THREE.NearestFilter,\n\t\t\t\tdepthTexture\n\t\t\t});\n\t\t}\n\t\treturn this.normalTarget;\n\t}\n\n\toverride setSize(width: number, height: number): void {\n\t\tthis.width = Math.max(1, width);\n\t\tthis.height = Math.max(1, height);\n\t\tthis.normalTarget?.setSize(this.width, this.height);\n\t}\n\n\toverride render(\n\t\trenderer: THREE.WebGLRenderer,\n\t\twriteBuffer: THREE.WebGLRenderTarget,\n\t\treadBuffer: THREE.WebGLRenderTarget\n\t): void {\n\t\tconst normalTarget = this.acquireNormalTarget();\n\n\t\t// --- Normals + depth prepass (override material) ---\n\t\tconst previousTarget = renderer.getRenderTarget();\n\t\tconst previousAutoClear = renderer.autoClear;\n\t\tconst previousClearColor = renderer.getClearColor(new THREE.Color());\n\t\tconst previousClearAlpha = renderer.getClearAlpha();\n\t\tconst previousOverride = this.scene.overrideMaterial;\n\n\t\trenderer.setRenderTarget(normalTarget);\n\t\t// 0x7777ff is packed +Z: background pixels get a uniform normal, so only depth silhouettes\n\t\t// (not normal noise) separate objects from empty space.\n\t\trenderer.setClearColor(0x7777ff, 1);\n\t\trenderer.autoClear = true;\n\t\tthis.scene.overrideMaterial = this.normalMaterial;\n\t\trenderer.render(this.scene, this.camera);\n\t\tthis.scene.overrideMaterial = previousOverride;\n\t\trenderer.setClearColor(previousClearColor, previousClearAlpha);\n\t\trenderer.autoClear = previousAutoClear;\n\n\t\t// --- Edge composite ---\n\t\tconst uniforms = this.edgeMaterial.uniforms;\n\t\tuniforms.tDiffuse.value = readBuffer.texture;\n\t\tuniforms.tNormal.value = normalTarget.texture;\n\t\tuniforms.tDepth.value = normalTarget.depthTexture;\n\t\tuniforms.uResolution.value.set(this.width, this.height);\n\t\tconst perspective = this.camera as Partial<THREE.PerspectiveCamera>;\n\t\tuniforms.uPerspective.value = perspective.isPerspectiveCamera ? 1 : 0;\n\t\tuniforms.uNear.value = (this.camera as THREE.PerspectiveCamera).near ?? 0.1;\n\t\tuniforms.uFar.value = (this.camera as THREE.PerspectiveCamera).far ?? 1000;\n\n\t\trenderer.setRenderTarget(this.renderToScreen ? null : writeBuffer);\n\t\tthis.fsQuad.render(renderer);\n\t\trenderer.setRenderTarget(previousTarget);\n\t}\n\n\toverride dispose(): void {\n\t\tthis.normalTarget?.dispose();\n\t\tthis.normalMaterial.dispose();\n\t\tthis.edgeMaterial.dispose();\n\t\tthis.fsQuad.dispose();\n\t}\n}\n","import * as THREE from 'three';\nimport { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';\nimport { RenderPass } from 'three/addons/postprocessing/RenderPass.js';\nimport { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';\nimport { GTAOPass } from 'three/addons/postprocessing/GTAOPass.js';\nimport { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js';\nimport { OutputPass } from 'three/addons/postprocessing/OutputPass.js';\nimport { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';\n\nimport type { BloomOptions } from '../shared/types.js';\n\nimport { EdgeDetectionPass, type EdgeDetectionOptions } from './edge-detection-pass';\n\n/**\n * Pipeline: RenderPass → GTAOPass? → BloomPass? → EdgeDetectionPass? → SMAAPass → OutputPass. Bloom\n * sits before OutputPass because it needs linear HDR values: tone mapping would clamp the emissive\n * highlights it keys on. EdgeDetectionPass sits before SMAA so its 1px lines get antialiased. EffectComposer renders offscreen, where the\n * canvas's own MSAA never applies, so the scene target carries its own (see {@link msaaSamplesFor});\n * SMAA then smooths what MSAA leaves, the edge pass's lines included. SMAA over TAA because TAA's\n * temporal jitter smears during OrbitControls drags. OutputPass applies tone mapping and color\n * space last, so SMAA operates on the pre-tonemapped image.\n */\n\nexport interface RenderPipeline {\n\trender(deltaTime: number): void;\n\tsetSize(width: number, height: number, pixelRatio: number): void;\n\t/** Call when the camera's projection changes (e.g. perspective↔ortho). */\n\tsetCamera(camera: THREE.Camera): void;\n\tsetEdgeDetection(enabled: boolean): void;\n\tedgeDetectionEnabled(): boolean;\n\tdispose(): void;\n}\n\nexport interface RenderPipelineOptions {\n\t/** Must mirror the renderer's own tone mapping — OutputPass applies it once composited, not the renderer. */\n\ttoneMapping: THREE.ToneMapping;\n\ttoneMappingExposure: number;\n\t/** Default true. */\n\tambientOcclusion?: boolean;\n\t/** AO strength 0–1. Default 1. */\n\taoIntensity?: number;\n\t/** DPR cap for the AO buffers only; the scene itself renders at the full pixel ratio. Default 1. */\n\taoPixelRatio?: number;\n\t/** Omitted: no bloom pass at all. */\n\tbloom?: BloomOptions;\n\t/** Start with the screen-space edge pass enabled; pass an object to tune it. */\n\tedgeDetection?: boolean | EdgeDetectionOptions;\n}\n\n/**\n * A highlight beside a point light overflows the half-float target to infinity, and bloom's blur\n * turns that one pixel into NaN across the whole frame: the viewer goes blank. Clamping first keeps\n * a hot pixel hot without letting it reach the blur.\n */\nconst HDR_CLAMP_SHADER = {\n\tname: 'HdrClamp',\n\tuniforms: { tDiffuse: { value: null } },\n\tvertexShader: /* glsl */ `\n\t\tvarying vec2 vUv;\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n\t\t}`,\n\tfragmentShader: /* glsl */ `\n\t\tuniform sampler2D tDiffuse;\n\t\tvarying vec2 vUv;\n\t\tconst float MAX_RADIANCE = 64.0;\n\t\tfloat safe(float v) {\n\t\t\treturn isnan(v) ? 0.0 : clamp(v, 0.0, MAX_RADIANCE);\n\t\t}\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D(tDiffuse, vUv);\n\t\t\tgl_FragColor = vec4(safe(color.r), safe(color.g), safe(color.b), color.a);\n\t\t}`\n};\n\n/**\n * Without MSAA a sub-pixel feature (a sheet's bright hem edge) comes out as a dashed line, which\n * SMAA can't rebuild. At 2× DPR each CSS pixel already holds four samples, and 4× MSAA on top\n * would cost ~250 MB on a 4K target for no visible gain.\n */\nfunction msaaSamplesFor(pixelRatio: number): number {\n\treturn pixelRatio >= 2 ? 0 : 4;\n}\n\nexport function createRenderPipeline(\n\trenderer: THREE.WebGLRenderer,\n\tscene: THREE.Scene,\n\tcamera: THREE.Camera,\n\twidth: number,\n\theight: number,\n\toptions: RenderPipelineOptions\n): RenderPipeline {\n\t// Only the scene target needs samples: RenderPass draws into it, every later pass is a\n\t// full-screen quad. EffectComposer clones the second from the first, so reset it.\n\tconst sceneTarget = new THREE.WebGLRenderTarget(width, height, { type: THREE.HalfFloatType });\n\tconst composer = new EffectComposer(renderer, sceneTarget);\n\tcomposer.renderTarget2.samples = 0;\n\n\t// RenderPass draws into `readBuffer`, and the composer's buffer swaps carry over between\n\t// frames. With an odd number of swapping passes the scene would land in the unsampled target\n\t// every other frame, so point `readBuffer` at the scene target before each frame.\n\tconst renderFrame = (deltaTime: number) => {\n\t\tcomposer.readBuffer = sceneTarget;\n\t\tcomposer.writeBuffer = composer.renderTarget2;\n\t\tcomposer.render(deltaTime);\n\t};\n\n\tconst renderPass = new RenderPass(scene, camera);\n\tcomposer.addPass(renderPass);\n\n\tlet gtaoPass: GTAOPass | null = null;\n\tif (options.ambientOcclusion ?? true) {\n\t\tgtaoPass = new GTAOPass(scene, camera, width, height);\n\t\tgtaoPass.blendIntensity = options.aoIntensity ?? 1;\n\t\tgtaoPass.updateGtaoMaterial({ screenSpaceRadius: true });\n\t\tcomposer.addPass(gtaoPass);\n\t}\n\n\tlet clampPass: ShaderPass | null = null;\n\tlet bloomPass: UnrealBloomPass | null = null;\n\tif (options.bloom) {\n\t\tconst { strength, radius, threshold } = options.bloom;\n\t\tclampPass = new ShaderPass(HDR_CLAMP_SHADER);\n\t\tcomposer.addPass(clampPass);\n\t\tbloomPass = new UnrealBloomPass(new THREE.Vector2(width, height), strength, radius, threshold);\n\t\tcomposer.addPass(bloomPass);\n\t}\n\n\tconst edgeOptions = typeof options.edgeDetection === 'object' ? options.edgeDetection : {};\n\tconst edgePass = new EdgeDetectionPass(scene, camera, width, height, edgeOptions);\n\tedgePass.enabled = !!options.edgeDetection;\n\tcomposer.addPass(edgePass);\n\n\tconst smaaPass = new SMAAPass();\n\tcomposer.addPass(smaaPass);\n\n\tconst outputPass = new OutputPass();\n\tcomposer.addPass(outputPass);\n\n\trenderer.toneMapping = options.toneMapping;\n\trenderer.toneMappingExposure = options.toneMappingExposure;\n\n\tconst aoPixelRatioCap = options.aoPixelRatio ?? 1;\n\t// The composer sizes every pass at the scene's resolution; AO alone is cheap enough to run lower,\n\t// and its blend samples by UV so it upscales onto the full-resolution frame.\n\tlet aoScale = 1;\n\tif (gtaoPass) {\n\t\tconst setAoSize = gtaoPass.setSize.bind(gtaoPass);\n\t\tgtaoPass.setSize = (w: number, h: number) =>\n\t\t\tsetAoSize(Math.max(1, Math.round(w * aoScale)), Math.max(1, Math.round(h * aoScale)));\n\t}\n\tconst applyPixelRatio = (pixelRatio: number) => {\n\t\taoScale = Math.min(pixelRatio, aoPixelRatioCap) / pixelRatio;\n\t\tconst samples = msaaSamplesFor(pixelRatio);\n\t\tif (sceneTarget.samples !== samples) {\n\t\t\tsceneTarget.samples = samples;\n\t\t\tsceneTarget.dispose(); // three reallocates on next use; samples alone doesn't trigger it\n\t\t}\n\t\tcomposer.setPixelRatio(pixelRatio);\n\t};\n\tapplyPixelRatio(renderer.getPixelRatio());\n\tcomposer.setSize(width, height);\n\n\treturn {\n\t\trender: renderFrame,\n\t\t// composer.setSize only — calling individual pass.setSize would reset AO/AA targets back to\n\t\t// logical CSS size, undoing the pixel-ratio scaling.\n\t\tsetSize: (w, h, pixelRatio) => {\n\t\t\tapplyPixelRatio(pixelRatio);\n\t\t\tcomposer.setSize(w, h);\n\t\t},\n\t\tsetCamera: (cam) => {\n\t\t\trenderPass.camera = cam;\n\t\t\tedgePass.camera = cam;\n\t\t\tif (!gtaoPass) return;\n\t\t\tgtaoPass.camera = cam;\n\t\t\t// GTAOPass bakes camera type into its AO shader as a construction-time define; reassigning\n\t\t\t// `camera` alone leaves the old projection's depth reconstruction active — garbage AO after\n\t\t\t// a perspective⇄ortho toggle. Force a recompile when the type actually changes.\n\t\t\tconst isPerspective = (cam as Partial<THREE.PerspectiveCamera>).isPerspectiveCamera ? 1 : 0;\n\t\t\tif (gtaoPass.gtaoMaterial.defines.PERSPECTIVE_CAMERA !== isPerspective) {\n\t\t\t\tgtaoPass.gtaoMaterial.defines.PERSPECTIVE_CAMERA = isPerspective;\n\t\t\t\tgtaoPass.gtaoMaterial.needsUpdate = true;\n\t\t\t}\n\t\t},\n\t\tsetEdgeDetection: (enabled) => {\n\t\t\tedgePass.enabled = enabled;\n\t\t},\n\t\tedgeDetectionEnabled: () => edgePass.enabled,\n\t\t// composer.dispose() doesn't free passes.\n\t\tdispose: () => {\n\t\t\tcomposer.dispose();\n\t\t\tgtaoPass?.dispose();\n\t\t\tclampPass?.dispose();\n\t\t\tbloomPass?.dispose();\n\t\t\tedgePass.dispose();\n\t\t\tsmaaPass.dispose();\n\t\t\toutputPass.dispose();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\n\nimport { createRenderPipeline, type RenderPipeline } from '../render-pipeline.js';\nimport type { ResolvedOptions } from './defaults.js';\n\n/**\n * Owns the optional postprocessing composer and the independent reasons to want one: ambient\n * occlusion (a user/look choice), bloom (a look choice) and the screen-space edge fallback (forced on\n * while meshes over the triangle cap are in the scene). None knows about the others, so \"is a\n * pipeline wanted, and does it need rebuilding\" is reconciled here instead of at each caller.\n */\nexport interface PipelineController {\n\tget(): RenderPipeline | null;\n\tsync(): void;\n\t/** Dispose and rebuild (if one is wanted) so construction-time options re-apply. */\n\trebuild(): void;\n\tsetAmbientOcclusion(enabled: boolean): void;\n\tsetEdgeFallback(active: boolean): void;\n\tisEdgeFallbackActive(): boolean;\n\tdispose(): void;\n}\n\nexport function createPipelineController(params: {\n\trenderer: THREE.WebGLRenderer;\n\tscene: THREE.Scene;\n\tgetActiveCamera: () => THREE.Camera;\n\tgetCanvasSize: () => { width: number; height: number };\n\tconfig: ResolvedOptions;\n\trequestRender: () => void;\n}): PipelineController {\n\tconst { renderer, scene, getActiveCamera, getCanvasSize, config, requestRender } = params;\n\n\tlet pipeline: RenderPipeline | null = null;\n\tlet aoEnabled = !!config.render.ambientOcclusion;\n\tlet edgeFallbackActive = false;\n\n\t// Baked in at construction. A look re-applies on every solve, and rebuilding reallocates bloom's\n\t// whole render-target chain, so only a real change may rebuild.\n\tconst bakedOptions = () => ({\n\t\tambientOcclusion: aoEnabled,\n\t\tbloom: config.render.bloom,\n\t\ttoneMapping: config.render.toneMapping,\n\t\ttoneMappingExposure: config.render.toneMappingExposure\n\t});\n\tlet baked = bakedOptions();\n\tconst isStale = () => {\n\t\tconst wanted = bakedOptions();\n\t\treturn (\n\t\t\twanted.ambientOcclusion !== baked.ambientOcclusion ||\n\t\t\twanted.bloom !== baked.bloom ||\n\t\t\twanted.toneMapping !== baked.toneMapping ||\n\t\t\twanted.toneMappingExposure !== baked.toneMappingExposure\n\t\t);\n\t};\n\n\tconst build = (): RenderPipeline => {\n\t\tconst { width, height } = getCanvasSize();\n\t\tconst built = createRenderPipeline(\n\t\t\trenderer,\n\t\t\tscene,\n\t\t\tgetActiveCamera(),\n\t\t\tMath.max(1, width),\n\t\t\tMath.max(1, height),\n\t\t\t{\n\t\t\t\ttoneMapping: config.render.toneMapping ?? THREE.NeutralToneMapping,\n\t\t\t\ttoneMappingExposure: config.render.toneMappingExposure ?? 1,\n\t\t\t\tambientOcclusion: aoEnabled,\n\t\t\t\taoIntensity: config.render.aoIntensity,\n\t\t\t\taoPixelRatio: config.render.aoPixelRatio,\n\t\t\t\tbloom: config.render.bloom,\n\t\t\t\t// Always built disabled; sync() flips it live via setEdgeDetection.\n\t\t\t\tedgeDetection: false\n\t\t\t}\n\t\t);\n\t\tbuilt.setSize(Math.max(1, width), Math.max(1, height), config.render.pixelRatio!);\n\t\treturn built;\n\t};\n\n\tconst sync = () => {\n\t\tconst wantPipeline = aoEnabled || edgeFallbackActive || !!config.render.bloom;\n\t\tif (!wantPipeline) {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = null;\n\t\t\trequestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (!pipeline || isStale()) {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = build();\n\t\t\tbaked = bakedOptions();\n\t\t}\n\t\tpipeline.setEdgeDetection(edgeFallbackActive);\n\t\trequestRender();\n\t};\n\n\treturn {\n\t\tget: () => pipeline,\n\t\tsync,\n\t\trebuild: () => {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = null;\n\t\t\tsync();\n\t\t},\n\t\tsetAmbientOcclusion: (enabled: boolean) => {\n\t\t\taoEnabled = enabled;\n\t\t\tsync();\n\t\t},\n\t\tsetEdgeFallback: (active: boolean) => {\n\t\t\tif (active === edgeFallbackActive) return;\n\t\t\tedgeFallbackActive = active;\n\t\t\tsync();\n\t\t},\n\t\tisEdgeFallbackActive: () => edgeFallbackActive,\n\t\tdispose: () => {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = null;\n\t\t}\n\t};\n}\n","import type * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function setupControls(\n\tcamera: THREE.PerspectiveCamera,\n\tcanvas: HTMLCanvasElement,\n\tconfig: ResolvedOptions\n): OrbitControls {\n\tconst controls = new OrbitControls(camera, canvas);\n\n\tconst target = config.camera.target;\n\tif (target) {\n\t\tcontrols.target.set(target.x, target.y, target.z);\n\t}\n\n\tcontrols.enableDamping = config.controls.enableDamping || false;\n\tcontrols.dampingFactor = config.controls.dampingFactor || 0.05;\n\n\tcontrols.autoRotate = config.controls.autoRotate || false;\n\tcontrols.autoRotateSpeed = config.controls.autoRotateSpeed || 0.5;\n\n\tcontrols.enableZoom = config.controls.enableZoom ?? true;\n\tcontrols.enablePan = config.controls.enablePan ?? true;\n\tcontrols.minDistance = config.controls.minDistance || 0.001;\n\tcontrols.maxDistance = config.controls.maxDistance || Infinity;\n\n\tcontrols.screenSpacePanning = false;\n\tcontrols.maxPolarAngle = Math.PI;\n\n\tcontrols.update();\n\treturn controls;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\nimport { HDRLoader } from 'three/addons/loaders/HDRLoader.js';\n\nimport { environmentRotationFor } from '../up-axis.js';\nimport { createReflectionEnvironment } from './reflection-environment.js';\nimport { defaultUp, type ResolvedOptions } from './defaults.js';\n\nexport function setupEnvironment(\n\tscene: THREE.Scene,\n\trenderer: THREE.WebGLRenderer,\n\tconfig: ResolvedOptions,\n\tisDisposed: () => boolean\n) {\n\tif (config.environment.enableEnvironmentLighting) {\n\t\tcreateReflectionEnvironment(\n\t\t\tscene,\n\t\t\trenderer,\n\t\t\tenvironmentRotationFor(config.environment.sceneUp ?? defaultUp)\n\t\t);\n\t\tnew HDRLoader().load(\n\t\t\tconfig.environment.hdrPath || '/baseHDR.hdr',\n\t\t\tfunction (envMap) {\n\t\t\t\t// Viewer may be torn down mid-fetch (fast mount/unmount); dispose() already swept the\n\t\t\t\t// scene, so adopting the texture now would leak it, and onReady must not fire.\n\t\t\t\tif (isDisposed()) {\n\t\t\t\t\tenvMap.dispose();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!envMap?.image) {\n\t\t\t\t\tgetLogger().warn('HDR loaded without image data; skipping environment map.');\n\t\t\t\t\tenvMap?.dispose();\n\t\t\t\t\tconfig.events.onReady?.();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tenvMap.mapping = THREE.EquirectangularReflectionMapping;\n\n\t\t\t\t// PMREM builds the roughness-aware mip chain MeshStandardMaterial samples for IBL;\n\t\t\t\t// without it, rough surfaces read a near-mirror level (sharp reflections, sparkly highlights).\n\t\t\t\tconst pmrem = new THREE.PMREMGenerator(renderer);\n\t\t\t\tpmrem.compileEquirectangularShader();\n\t\t\t\tconst prefiltered = pmrem.fromEquirectangular(envMap).texture;\n\t\t\t\tpmrem.dispose();\n\n\t\t\t\tscene.environment = prefiltered;\n\t\t\t\t// Normalizes IBL contribution so brightness is consistent across HDRs of differing exposure.\n\t\t\t\tscene.environmentIntensity = config.environment.environmentIntensity ?? 1;\n\t\t\t\t// Equirect mapping assumes the horizon lies in the XZ plane (Y-up); without rotating for\n\t\t\t\t// a Z-up scene the sky lights the model from +Y instead of from above.\n\t\t\t\tconst envRotation = environmentRotationFor(config.environment.sceneUp ?? defaultUp);\n\t\t\t\tscene.environmentRotation.copy(envRotation);\n\t\t\t\tif (config.environment.showEnvironment) {\n\t\t\t\t\t// Background wants the full-res equirect, not the low-res prefiltered probe.\n\t\t\t\t\tscene.background = envMap;\n\t\t\t\t\t// Separate property from environmentRotation — drifts apart if only one is set.\n\t\t\t\t\tscene.backgroundRotation.copy(envRotation);\n\t\t\t\t} else {\n\t\t\t\t\t// Raw equirect was only PMREM input; the prefiltered probe has superseded it.\n\t\t\t\t\tenvMap.dispose();\n\t\t\t\t}\n\t\t\t\tconfig.events.onReady?.();\n\t\t\t},\n\t\t\tundefined,\n\t\t\tfunction (error) {\n\t\t\t\tif (isDisposed()) return;\n\t\t\t\tgetLogger().warn('HDR texture could not be loaded, falling back to basic lighting:', error);\n\t\t\t\tconfig.events.onReady?.();\n\t\t\t}\n\t\t);\n\t} else {\n\t\tconfig.events.onReady?.();\n\t}\n}\n\nexport function addFloor(scene: THREE.Scene, config: ResolvedOptions) {\n\tconst floorSize = config.floor.size;\n\tconst floorGeometry = new THREE.PlaneGeometry(floorSize, floorSize);\n\n\tconst floorColor =\n\t\ttypeof config.floor.color === 'string'\n\t\t\t? new THREE.Color(config.floor.color)\n\t\t\t: config.floor.color;\n\n\tconst floorMaterial = new THREE.MeshStandardMaterial({\n\t\tcolor: floorColor,\n\t\troughness: config.floor.roughness,\n\t\tmetalness: config.floor.metalness,\n\t\tside: THREE.DoubleSide\n\t});\n\n\tconst floor = new THREE.Mesh(floorGeometry, floorMaterial);\n\tfloor.userData.id = 'floor';\n\tfloor.name = 'floor';\n\t// PlaneGeometry's default +Z normal is already correct for Z-up; orient to scene up for any other.\n\tconst up = (config.environment?.sceneUp || defaultUp).clone().normalize();\n\tfloor.quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), up);\n\tfloor.position.set(0, 0, 0);\n\n\tif (config.floor.receiveShadow && config.render.enableShadows) {\n\t\tfloor.receiveShadow = true;\n\t}\n\n\tscene.add(floor);\n}\n","import * as THREE from 'three';\n\nimport { cloneForTint, getBaseMaterial, getLogger } from '../../shared/index.js';\n\nimport type { CameraController } from '../camera-controller.js';\nimport { computeContentBounds, isViewerAid } from '../three-helpers.js';\nimport { highlightMembers, memberBounds, resolveHitMember } from './merged-picking.js';\nimport type { ResolvedOptions } from './defaults.js';\nimport type { SelectionTarget } from './viewer.js';\n\n// Tuned against the brightest look (`showcase`, exposure 1.15 + sun 2.6): the highlight has to stay\n// obvious there without blowing out to a flat silhouette under the dimmest one.\n/** How far the base color moves toward the selection color. 1 would discard the texture underneath. */\nconst SELECTION_COLOR_MIX = 0.75;\n/** Above ~2 the tint clips to pure red and the geometry's shading disappears with it. */\nconst SELECTION_EMISSIVE_INTENSITY = 0.6;\n/** A metallic surface reflects the environment rather than showing its albedo tint. */\nconst SELECTION_MAX_METALNESS = 0.2;\n/** Below this a highlight reads as a ghost, so the selection is forced opaque. */\nconst SELECTION_MIN_OPACITY = 1;\n/**\n * Lighter than the default selection red on purpose: amber and red are close hues under red-green\n * color blindness, so what tells hover from selected there is brightness.\n */\nconst HOVER_COLOR = '#ffb020';\n/** Linear RGB distance. Closer than this, a tint barely changes the surface. */\nconst MIN_TINT_DISTANCE = 0.35;\n/** Taken by a surface already close to the tint. Blue stays distinct under every common deficiency. */\nconst FALLBACK_TINT = new THREE.Color('#2f7bff');\n\nfunction readableTint(material: THREE.Material, tint: THREE.Color): THREE.Color {\n\tconst base = (material as THREE.MeshStandardMaterial).color as THREE.Color | undefined;\n\tif (!base) return tint;\n\tconst distance = Math.hypot(base.r - tint.r, base.g - tint.g, base.b - tint.b);\n\treturn distance < MIN_TINT_DISTANCE ? FALLBACK_TINT : tint;\n}\n\n/**\n * Recolors a material clone to mark it selected. Exported for tests: the highlight reads against\n * whatever the active look does to lighting, and that coupling has broken it silently before.\n *\n * Meshes tint via `emissive` AND base color. Emissive alone is additive on top of what the surface\n * already reflects, so on a bright white model under a key light it only shifts the surface slightly\n * pink — the brighter the look, the weaker the highlight. Moving the albedo toward the selection\n * color is what makes it hold at any exposure; the emissive on top keeps it reading as lit rather\n * than as a flat sticker in shadowed areas. Lines/points have no emissive channel, so their `color`\n * is replaced outright.\n *\n * Mutates `material` in place — pass a clone, never a material the scene still shares.\n */\nexport function tintForSelection(\n\tmaterial: THREE.Material,\n\tcolor: THREE.Color,\n\tisMesh: boolean\n): void {\n\tconst selectionColor = readableTint(material, color);\n\tif (isMesh && 'emissive' in material) {\n\t\tconst mesh = material as THREE.MeshStandardMaterial;\n\t\tmesh.emissive = selectionColor.clone();\n\t\tmesh.emissiveIntensity = SELECTION_EMISSIVE_INTENSITY;\n\t\t// A textured surface keeps its map; tinting the base color under it still reads.\n\t\tmesh.color.lerp(selectionColor, SELECTION_COLOR_MIX);\n\t\t// Chrome-like input would reflect the environment instead of showing the tint.\n\t\tmesh.metalness = Math.min(mesh.metalness, SELECTION_MAX_METALNESS);\n\t\t// Under the x-ray look every mesh is near-transparent with depth writes off, which would\n\t\t// leave the selection a faint ghost sorted behind its own neighbours. Selection is a UI\n\t\t// affordance, so it opts out of the look and renders solid.\n\t\tif (mesh.opacity < SELECTION_MIN_OPACITY) {\n\t\t\tmesh.opacity = SELECTION_MIN_OPACITY;\n\t\t\tmesh.transparent = false;\n\t\t\tmesh.depthWrite = true;\n\t\t}\n\t\treturn;\n\t}\n\tif ('color' in material) {\n\t\t(material as THREE.LineBasicMaterial).color = selectionColor.clone();\n\t}\n}\n\nexport function setupEventHandlers(\n\tcanvas: HTMLCanvasElement,\n\tscene: THREE.Scene,\n\tcameraController: CameraController,\n\tconfig: ResolvedOptions\n): {\n\tdispose: () => void;\n\tfitToView: () => void;\n\tclearSelection: () => void;\n\tselectEntries: (targets: SelectionTarget[]) => void;\n\tsetHover: (object: THREE.Object3D | null, memberIndex?: number | null) => void;\n} {\n\tconst selectedObjects = new Set<THREE.Object3D>();\n\tconst originalMaterials = new Map<THREE.Object3D, THREE.Material | THREE.Material[]>();\n\t// One per merged mesh with selected members: restores the geometry's groups and material array,\n\t// which a plain material swap can't undo. Owns its highlight clone, which never becomes the\n\t// mesh's `material` and so isn't reached by the clone disposal below.\n\tconst memberHighlights: { restore: () => void; material: THREE.Material }[] = [];\n\t/** Which member of a merged mesh is selected, so a click on a sibling isn't a no-op. */\n\tlet selectedMemberIndex: number | null = null;\n\t// True while `selectEntries` drew the selection: a click on one of those objects is a fresh pick,\n\t// not a re-click of what it already selected.\n\tlet selectedByEntries = false;\n\tconst raycaster = new THREE.Raycaster();\n\tconst mouse = new THREE.Vector2();\n\tconst mouseDownPosition = new THREE.Vector2();\n\tconst getActiveCamera = () => cameraController.getActiveCamera();\n\n\t// Three.js's recursive intersect hits a visible Mesh inside a hidden Group; this enforces that\n\t// every ancestor must also be visible.\n\tconst isFullyVisible = (object: THREE.Object3D): boolean => {\n\t\tlet current: THREE.Object3D | null = object;\n\t\twhile (current) {\n\t\t\tif (!current.visible) return false;\n\t\t\tcurrent = current.parent;\n\t\t}\n\t\treturn true;\n\t};\n\n\tconst fitToView = () => {\n\t\tconst box = computeContentBounds(scene);\n\n\t\tif (box.isEmpty()) {\n\t\t\tgetLogger().warn('No objects to fit to view');\n\t\t\treturn;\n\t\t}\n\n\t\t// Via the controller, not the perspective camera directly: it repositions whichever camera is\n\t\t// live and re-derives the ortho frustum in 2D mode.\n\t\tcameraController.frameBounds(box, false);\n\t};\n\n\tconst selectionColorObj =\n\t\ttypeof config.events.selectionColor === 'string'\n\t\t\t? new THREE.Color(config.events.selectionColor)\n\t\t\t: config.events.selectionColor instanceof THREE.Color\n\t\t\t\t? config.events.selectionColor\n\t\t\t\t: new THREE.Color('#ff0000');\n\n\tconst hoverColorObj = new THREE.Color(HOVER_COLOR);\n\tlet hoverObject: (THREE.Object3D & { material?: THREE.Material | THREE.Material[] }) | null =\n\t\tnull;\n\tlet hoverOriginal: THREE.Material | THREE.Material[] | null = null;\n\tlet hoverMember: { restore: () => void; material: THREE.Material } | null = null;\n\n\t// If the object left the scene, no traversal can reach the original material just restored to\n\t// it, so dispose it here. Compute content is cleared wholesale per solve, so a detached object's\n\t// material has no surviving sharers.\n\tconst disposeIfDetached = (\n\t\tobject: THREE.Object3D,\n\t\toriginal: THREE.Material | THREE.Material[]\n\t) => {\n\t\tlet root = object;\n\t\twhile (root.parent) root = root.parent;\n\t\tif (root === scene) return;\n\t\tif (original instanceof THREE.Material) original.dispose();\n\t\telse original.forEach((m) => m.dispose());\n\t};\n\n\tconst clearHover = () => {\n\t\thoverMember?.restore();\n\t\thoverMember?.material.dispose();\n\t\thoverMember = null;\n\t\tif (hoverObject && hoverOriginal) {\n\t\t\tconst clone = hoverObject.material;\n\t\t\tif (clone instanceof THREE.Material) clone.dispose();\n\t\t\thoverObject.material = hoverOriginal;\n\t\t\tdisposeIfDetached(hoverObject, hoverOriginal);\n\t\t}\n\t\thoverObject = null;\n\t\thoverOriginal = null;\n\t};\n\n\t/**\n\t * Previews an outliner row in the viewport. Fires no events, and yields to a selection already\n\t * tinting the same thing.\n\t */\n\tconst setHover = (object: THREE.Object3D | null, memberIndex: number | null = null) => {\n\t\tclearHover();\n\t\tif (!object) return;\n\n\t\tif (memberIndex !== null && object instanceof THREE.Mesh) {\n\t\t\tconst material = getBaseMaterial(object).clone();\n\t\t\ttintForSelection(material, hoverColorObj, true);\n\t\t\tconst members = new Set([memberIndex]);\n\t\t\thoverMember = { restore: highlightMembers(object, members, material, 'hover'), material };\n\t\t\treturn;\n\t\t}\n\n\t\tif (selectedObjects.has(object)) return;\n\t\tconst target = object as THREE.Object3D & { material?: THREE.Material | THREE.Material[] };\n\t\tif (!(target.material instanceof THREE.Material)) return;\n\t\tconst clone = cloneForTint(target.material);\n\t\ttintForSelection(clone, hoverColorObj, object instanceof THREE.Mesh);\n\t\thoverObject = target;\n\t\thoverOriginal = target.material;\n\t\ttarget.material = clone;\n\t};\n\n\tconst clearSelection = () => {\n\t\tfor (const { restore, material } of memberHighlights) {\n\t\t\trestore();\n\t\t\tmaterial.dispose();\n\t\t}\n\t\tmemberHighlights.length = 0;\n\t\tselectedObjects.forEach((obj) => {\n\t\t\tconst restorable = obj as THREE.Object3D & {\n\t\t\t\tmaterial?: THREE.Material | THREE.Material[];\n\t\t\t};\n\t\t\tif (originalMaterials.has(obj)) {\n\t\t\t\tconst original = originalMaterials.get(obj)!;\n\t\t\t\tconst clone = restorable.material; // dispose the highlight clone before restoring\n\t\t\t\tif (clone instanceof THREE.Material) clone.dispose();\n\t\t\t\telse if (Array.isArray(clone)) clone.forEach((m) => m.dispose());\n\t\t\t\trestorable.material = original;\n\t\t\t\toriginalMaterials.delete(obj);\n\n\t\t\t\tdisposeIfDetached(obj, original);\n\t\t\t}\n\t\t});\n\t\tselectedObjects.clear();\n\t\tselectedMemberIndex = null;\n\t\tselectedByEntries = false;\n\t};\n\n\t/**\n\t * Tints the picked object. On a merged mesh only the given members are tinted, so clicking one\n\t * wall doesn't light up every wall sharing its material — see `merged-picking.ts`.\n\t */\n\tconst applyHighlight = (object: THREE.Object3D, members: ReadonlySet<number> | null): boolean => {\n\t\tif (members && object instanceof THREE.Mesh) {\n\t\t\t// The material is an array while some members are hidden, so read the base through\n\t\t\t// the draw-state owner rather than off the mesh.\n\t\t\tconst clone = getBaseMaterial(object).clone();\n\t\t\ttintForSelection(clone, selectionColorObj, true);\n\t\t\tmemberHighlights.push({\n\t\t\t\trestore: highlightMembers(object, members, clone),\n\t\t\t\tmaterial: clone\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\n\t\tconst target = object as THREE.Object3D & { material?: THREE.Material | THREE.Material[] };\n\t\tif (!(target.material instanceof THREE.Material)) return false;\n\n\t\tconst clone = cloneForTint(target.material);\n\t\ttintForSelection(clone, selectionColorObj, object instanceof THREE.Mesh);\n\n\t\toriginalMaterials.set(object, target.material);\n\t\ttarget.material = clone;\n\t\treturn true;\n\t};\n\n\t/**\n\t * Selection made outside the viewport. An empty list only drops such a selection, so a host\n\t * syncing its own empty state can't wipe what a click on the canvas just selected.\n\t */\n\tconst selectEntries = (targets: SelectionTarget[]) => {\n\t\tif (targets.length === 0 && !selectedByEntries) return;\n\n\t\tclearHover();\n\t\tclearSelection();\n\n\t\tconst membersByMesh = new Map<THREE.Object3D, Set<number>>();\n\t\tfor (const { object, memberIndex } of targets) {\n\t\t\tif (memberIndex === null) {\n\t\t\t\tselectedObjects.add(object);\n\t\t\t\tapplyHighlight(object, null);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet members = membersByMesh.get(object);\n\t\t\tif (!members) membersByMesh.set(object, (members = new Set()));\n\t\t\tmembers.add(memberIndex);\n\t\t}\n\t\tfor (const [object, members] of membersByMesh) {\n\t\t\tselectedObjects.add(object);\n\t\t\tapplyHighlight(object, members);\n\t\t}\n\t\tselectedByEntries = targets.length > 0;\n\t};\n\n\t// Points picking tolerance, scaled to scene size so it holds at any zoom. Fat Line2 uses its own\n\t// material linewidth instead, so no separate threshold is needed for lines.\n\t//\n\t// `computeContentBounds` traverses the whole scene, which is far too slow to redo on every\n\t// click. The scene only changes wholesale between solves, so the diagonal is cached and\n\t// recomputed when the child count changes.\n\tlet cachedDiagonal: number | null = null;\n\tlet cachedChildCount = -1;\n\tconst updatePickThresholds = () => {\n\t\tif (cachedDiagonal === null || scene.children.length !== cachedChildCount) {\n\t\t\tconst box = computeContentBounds(scene);\n\t\t\tcachedDiagonal = box.isEmpty() ? 1 : box.getSize(new THREE.Vector3()).length();\n\t\t\tcachedChildCount = scene.children.length;\n\t\t}\n\t\traycaster.params.Points.threshold = cachedDiagonal * 0.01;\n\t};\n\n\t/**\n\t * Nearest visible, pickable object under the pointer, or null. Viewer aids are excluded: the grid\n\t * is a huge camera-following plane, so double-clicking through a thin curve onto it would frame a\n\t * box the size of the fade radius and fling the camera out.\n\t */\n\tconst pickAt = (clientX: number, clientY: number): THREE.Intersection | null => {\n\t\tconst rect = canvas.getBoundingClientRect();\n\t\tmouse.x = ((clientX - rect.left) / rect.width) * 2 - 1;\n\t\tmouse.y = -((clientY - rect.top) / rect.height) * 2 + 1;\n\n\t\tupdatePickThresholds();\n\t\traycaster.setFromCamera(mouse, getActiveCamera());\n\t\t// Sorted near-to-far, so the first visible hit is the nearest one.\n\t\tfor (const hit of raycaster.intersectObjects(scene.children, true)) {\n\t\t\tif (isFullyVisible(hit.object) && !isViewerAid(hit.object)) return hit;\n\t\t}\n\t\treturn null;\n\t};\n\n\tconst handleMouseDown = (event: MouseEvent) => {\n\t\tmouseDownPosition.set(event.clientX, event.clientY);\n\t};\n\n\tconst handleCanvasClick = (event: MouseEvent) => {\n\t\tconst currentMousePosition = new THREE.Vector2(event.clientX, event.clientY);\n\t\tif (mouseDownPosition.distanceTo(currentMousePosition) > 5) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Acted on immediately, including the first press of a double-click: selecting and reporting\n\t\t// metadata are idempotent, so the second press re-reports the same object. Deferring this to\n\t\t// wait out a possible `dblclick` made every selection feel a quarter-second late.\n\t\tconst hit = pickAt(event.clientX, event.clientY);\n\n\t\tif (!hit) {\n\t\t\tclearSelection();\n\t\t\tconfig.events?.onBackgroundClicked?.({ x: mouse.x, y: mouse.y });\n\t\t\treturn;\n\t\t}\n\n\t\tconst clickedObject = hit.object;\n\t\tconst resolved = resolveHitMember(hit);\n\t\tconst hitMemberIndex = resolved?.index ?? null;\n\n\t\t// A merged mesh stays one object across clicks, so object identity alone would treat a click\n\t\t// on a different member as re-clicking the current selection and do nothing.\n\t\tif (\n\t\t\t!selectedByEntries &&\n\t\t\tselectedObjects.has(clickedObject) &&\n\t\t\thitMemberIndex === selectedMemberIndex\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tclearHover();\n\t\tclearSelection();\n\t\tselectedObjects.add(clickedObject);\n\t\tselectedMemberIndex = hitMemberIndex;\n\t\tapplyHighlight(clickedObject, resolved ? new Set([resolved.index]) : null);\n\n\t\tconfig.events?.onObjectSelected?.(clickedObject);\n\n\t\t// The member's own name/layer/metadata, so the panel describes the object under the cursor\n\t\t// rather than the merged group's first member.\n\t\tif (resolved) {\n\t\t\tconfig.events?.onMeshMetadataClicked?.({\n\t\t\t\tsource: clickedObject.userData?.source,\n\t\t\t\tname: resolved.member.name,\n\t\t\t\tlayer: resolved.member.layer,\n\t\t\t\ttrackingKey: resolved.member.trackingKey,\n\t\t\t\tmetadata: resolved.member.metadata\n\t\t\t});\n\t\t} else if (\n\t\t\tclickedObject instanceof THREE.Mesh &&\n\t\t\tObject.keys(clickedObject.userData).length > 0\n\t\t) {\n\t\t\tconfig.events?.onMeshMetadataClicked?.(clickedObject.userData);\n\t\t}\n\t};\n\n\tconst handleDoubleClick = (event: MouseEvent) => {\n\t\tconst hit = pickAt(event.clientX, event.clientY);\n\t\tif (!hit) {\n\t\t\t// The way back out after framing a part. On touch there is no F key, and a pinch-out\n\t\t\t// from a small part back to the whole model takes many gestures.\n\t\t\tif (config.events?.enableDoubleClickZoom) {\n\t\t\t\tcameraController.frameBounds(computeContentBounds(scene), true);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst target = hit.object;\n\n\t\tconfig.events?.onMeshDoubleClicked?.(target);\n\n\t\tif (!config.events?.enableDoubleClickZoom) return;\n\n\t\t// On a merged mesh, framing the object would frame the whole material group; frame just the\n\t\t// member under the cursor instead.\n\t\tconst resolved = resolveHitMember(hit);\n\t\tconst box =\n\t\t\tresolved && target instanceof THREE.Mesh\n\t\t\t\t? memberBounds(target, resolved.member)\n\t\t\t\t: new THREE.Box3().setFromObject(target);\n\t\tif (box.isEmpty()) return;\n\t\t// Via the controller so the active camera moves (translating an ortho camera alone zooms\n\t\t// nothing). The resulting tween is cancellable — a rapid second double-click replaces it\n\t\t// rather than racing it.\n\t\tcameraController.frameBounds(box, true);\n\t};\n\n\tconst handleKeydown = (event: KeyboardEvent) => {\n\t\tif (!config.events?.enableKeyboardControls) return;\n\n\t\tswitch (event.key.toLowerCase()) {\n\t\t\tcase 'f':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfitToView();\n\t\t\t\tbreak;\n\t\t\tcase 'escape':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tclearSelection();\n\t\t\t\tbreak;\n\t\t\tcase ' ':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfitToView();\n\t\t\t\tbreak;\n\t\t}\n\t};\n\n\tif (config.events?.enableClickToFocus) {\n\t\tcanvas.addEventListener('mousedown', handleMouseDown);\n\t\tcanvas.addEventListener('click', handleCanvasClick);\n\t\tcanvas.addEventListener('dblclick', handleDoubleClick);\n\t}\n\n\tif (config.events?.enableKeyboardControls) {\n\t\tcanvas.setAttribute('tabindex', '0');\n\t\tcanvas.addEventListener('keydown', handleKeydown);\n\t}\n\n\tconst dispose = () => {\n\t\tcanvas.removeEventListener('mousedown', handleMouseDown);\n\t\tcanvas.removeEventListener('click', handleCanvasClick);\n\t\tcanvas.removeEventListener('dblclick', handleDoubleClick);\n\t\tcanvas.removeEventListener('keydown', handleKeydown);\n\t\tclearHover();\n\t\tclearSelection();\n\t};\n\n\treturn { dispose, fitToView, clearSelection, selectEntries, setHover };\n}\n","import * as THREE from 'three';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function setupRenderer(\n\tcanvas: HTMLCanvasElement,\n\tconfig: ResolvedOptions,\n\tpixelRatio: number\n): THREE.WebGLRenderer {\n\tconst renderer = new THREE.WebGLRenderer({\n\t\tantialias: config.render.antialias,\n\t\tcanvas,\n\t\talpha: true,\n\t\tpowerPreference: 'high-performance',\n\t\tpreserveDrawingBuffer: config.render.preserveDrawingBuffer,\n\t\t// Deliberately NOT logarithmic: the GTAO pipeline reconstructs view-space positions assuming\n\t\t// standard perspective depth and doesn't support log-encoded depth (haloing, wrong-scale\n\t\t// occlusion if enabled). If log depth is ever needed, AO must be disabled alongside it.\n\t\tlogarithmicDepthBuffer: false\n\t});\n\n\tconst parent = canvas.parentElement;\n\tconst width = parent ? parent.clientWidth : window.innerWidth;\n\tconst height = parent ? parent.clientHeight : window.innerHeight;\n\n\tif (parent) {\n\t\tcanvas.style.width = '100%';\n\t\tcanvas.style.height = '100%';\n\t\tcanvas.style.display = 'block';\n\t}\n\n\trenderer.setSize(width, height, false);\n\trenderer.setPixelRatio(pixelRatio);\n\n\tif (config.render.enableShadows) {\n\t\trenderer.shadowMap.enabled = true;\n\t\trenderer.shadowMap.type = THREE.VSMShadowMap;\n\t}\n\n\trenderer.toneMapping = config.render.toneMapping!;\n\trenderer.toneMappingExposure = config.render.toneMappingExposure ?? 1.0;\n\trenderer.outputColorSpace = THREE.SRGBColorSpace;\n\n\trenderer.sortObjects = true;\n\n\treturn renderer;\n}\n","import * as THREE from 'three';\n\nimport { lookPreset, publishMaxAnisotropy } from '../../shared/index.js';\nimport { createCameraController } from '../camera-controller.js';\nimport {\n\tEDGES_SKIPPED_OVERLAY_BUDGET,\n\tEDGES_SKIPPED_TRIANGLE_CAP,\n\taddEdgesAsync,\n\ttype EdgeOptions,\n\tremoveEdges\n} from '../edges.js';\nimport { createGrid } from '../grid.js';\nimport { createLabelLayer, type LabelLayer } from '../label-layer.js';\nimport { createMeasureTool, type MeasureTool } from '../measure.js';\nimport { createNearPlaneFitter, type NearPlaneFitter } from '../near-plane.js';\nimport { SOURCE_USER, appSource, isOwnedBy } from '../scene-ownership.js';\nimport { computeContentBounds } from '../three-helpers.js';\nimport { createToolRegistry } from '../tool-registry.js';\nimport type { ThreeInitializerOptions } from '../types.js';\nimport { upToAxis } from '../up-axis.js';\nimport { createViewGizmo } from '../view-gizmo.js';\nimport { createAnimationLoop } from './animation-loop.js';\nimport { createAppearanceController, lookPixelRatio } from './appearance.js';\nimport { createCamera } from './create-camera.js';\nimport { createScene } from './create-scene.js';\nimport { applyDefaults, defaultUp } from './defaults.js';\nimport { disposeObjectTree, disposeSceneResources } from './dispose.js';\nimport { createPipelineController } from './pipeline-controller.js';\nimport { setupControls } from './setup-controls.js';\nimport { addFloor, setupEnvironment } from './setup-environment.js';\nimport { setupEventHandlers } from './setup-events.js';\nimport { fitShadowToContent, setupLighting } from './setup-lighting.js';\nimport { setupRenderer } from './setup-renderer.js';\nimport type { ThreeViewer } from './viewer.js';\n\nexport const initThree = function (\n\tcanvas: HTMLCanvasElement,\n\toptions?: ThreeInitializerOptions\n): ThreeViewer {\n\tconst config = applyDefaults(options || {});\n\n\tconst sceneUp = config.environment?.sceneUp || defaultUp;\n\n\t// Single source of truth for DPR (renderer, resize check, AO pipeline), kept in config because a\n\t// look can cap it; applyDefaults always sets it, the fallback here is just type narrowing.\n\tconst fullPixelRatio = config.render.pixelRatio ?? Math.min(window.devicePixelRatio, 2);\n\tconst pixelRatio = lookPixelRatio(\n\t\tfullPixelRatio,\n\t\tlookPreset(config.look!, config.render.lowPower)\n\t);\n\tconfig.render.pixelRatio = pixelRatio;\n\n\tconst scene = createScene(config);\n\tconst camera = createCamera(config, canvas);\n\t// Must happen before OrbitControls/the controller read camera.up (captured at construction), or\n\t// a Z-up scene orbits and frames as if Y-up.\n\tcamera.up.copy(sceneUp);\n\tconst renderer = setupRenderer(canvas, config, pixelRatio);\n\t// Published to a shared sink rather than imported, so render/ stays independent of parse/.\n\tpublishMaxAnisotropy(renderer.capabilities.getMaxAnisotropy());\n\toptions?.onMaxAnisotropy?.(renderer.capabilities.getMaxAnisotropy());\n\n\tconst controls = setupControls(camera, canvas, config);\n\n\t// Render loop, resize, and raycasting all read through getActiveCamera so 2D/3D stays coherent.\n\tconst cameraController = createCameraController({\n\t\tscene,\n\t\tperspective: camera,\n\t\tcontrols,\n\t\tonActiveCameraChange: () => {},\n\t\tup: sceneUp\n\t});\n\tconst getActiveCamera = () => cameraController.getActiveCamera();\n\n\t// HDR decodes asynchronously; setupEnvironment's load callback checks this to drop (and dispose)\n\t// the texture instead of attaching it to a scene torn down mid-fetch.\n\tlet disposed = false;\n\tsetupEnvironment(scene, renderer, config, () => disposed);\n\tconst lights = setupLighting(scene, config);\n\tconst sunlight = lights.sun;\n\n\tconst updateShadowBounds = () => {\n\t\tif (sunlight) fitShadowToContent(sunlight, computeContentBounds(scene));\n\t};\n\n\tif (config.floor?.enabled) {\n\t\taddFloor(scene, config);\n\t}\n\t// So the near-plane fitter below can consult the floor's live visibility.\n\tconst floorMesh = config.floor?.enabled\n\t\t? (scene.children.find((child) => child.userData.id === 'floor') ?? null)\n\t\t: null;\n\n\tconst grid = config.grid.enabled\n\t\t? createGrid({\n\t\t\t\tcellSize: config.grid.cellSize,\n\t\t\t\tmajorEvery: config.grid.majorEvery,\n\t\t\t\tcellColor: config.grid.cellColor,\n\t\t\t\tmajorColor: config.grid.majorColor,\n\t\t\t\tfadeDistance: config.grid.fadeDistance,\n\t\t\t\tplane: config.grid.plane\n\t\t\t})\n\t\t: null;\n\tif (grid) scene.add(grid.object);\n\n\tconst updateGridScale = () => {\n\t\tif (grid) grid.fitToContent(computeContentBounds(scene));\n\t};\n\n\tconst gizmo = config.gizmo.enabled\n\t\t? createViewGizmo({ camera, domElement: canvas, controller: cameraController })\n\t\t: null;\n\n\t// Only VISIBLE ground aids feed the near-plane fitter's clamp: the grid is commonly built hidden\n\t// so hosts can toggle it, and the clamp is the camera's height above the plane — a hidden grid\n\t// would still drive near→0 at grazing views and crater depth precision, punching hidden edges\n\t// through geometry.\n\tconst gridPlane = config.grid.plane ?? upToAxis(sceneUp);\n\tconst gridNormal = new THREE.Vector3(\n\t\tgridPlane === 'x' ? 1 : 0,\n\t\tgridPlane === 'y' ? 1 : 0,\n\t\tgridPlane === 'z' ? 1 : 0\n\t);\n\tconst floorNormal = sceneUp.clone().normalize();\n\tconst groundNormals = (): THREE.Vector3[] => {\n\t\tconst normals: THREE.Vector3[] = [];\n\t\tif (grid?.object.visible) normals.push(gridNormal);\n\t\tif (config.floor.enabled && floorMesh?.visible) normals.push(floorNormal);\n\t\treturn normals;\n\t};\n\tconst nearFitter: NearPlaneFitter | null = config.camera.dynamicNear\n\t\t? createNearPlaneFitter({ camera, scene, groundNormals })\n\t\t: null;\n\n\t// Built unconditionally: measure was the first consumer, but any tool or app annotating the\n\t// scene needs it, and gating it behind measure.enabled left them with no way to get one.\n\tconst labelContainer = canvas.parentElement ?? canvas;\n\tconst labelLayer: LabelLayer = createLabelLayer(labelContainer, scene);\n\tconst measureTool: MeasureTool | null = config.measure.enabled\n\t\t? createMeasureTool({\n\t\t\t\tcanvas,\n\t\t\t\tscene,\n\t\t\t\tgetActiveCamera,\n\t\t\t\tlabelLayer,\n\t\t\t\toptions: {\n\t\t\t\t\tsnapPixels: config.measure.snapPixels,\n\t\t\t\t\tcolor: config.measure.color,\n\t\t\t\t\tlabelClassName: config.measure.labelClassName,\n\t\t\t\t\tdisplayUnit: config.measure.displayUnit,\n\t\t\t\t\tformat: config.measure.format\n\t\t\t\t}\n\t\t\t})\n\t\t: null;\n\n\tconst eventHandlers =\n\t\tconfig.events.enableEventHandlers !== false\n\t\t\t? setupEventHandlers(canvas, scene, cameraController, config)\n\t\t\t: {\n\t\t\t\t\tdispose: () => {},\n\t\t\t\t\tfitToView: () => {},\n\t\t\t\t\tclearSelection: () => {},\n\t\t\t\t\tselectEntries: () => {},\n\t\t\t\t\tsetHover: () => {}\n\t\t\t\t};\n\n\t// Built-ins register at the priorities documented on ToolRegistration, so a host tool can slot\n\t// above or below them. Listeners are attached unconditionally — a tool can register at any time.\n\tconst tools = createToolRegistry();\n\tif (measureTool) tools.register({ id: 'measure', tool: measureTool, priority: 0 });\n\tif (gizmo) tools.register({ id: 'gizmo', tool: gizmo, priority: -100 });\n\n\t// A drag to orbit/pan ends with a `click` on mouseup; without this guard that release would be\n\t// mistaken for a measurement point.\n\tconst DRAG_SLOP_PX = 5;\n\tlet pressX = 0;\n\tlet pressY = 0;\n\tconst handlePointerDown = (event: MouseEvent) => {\n\t\tpressX = event.clientX;\n\t\tpressY = event.clientY;\n\t};\n\tconst wasDrag = (event: MouseEvent) =>\n\t\tMath.hypot(event.clientX - pressX, event.clientY - pressY) > DRAG_SLOP_PX;\n\n\t// Capture-phase so tools see the click before bubble-phase selection; the first to claim it\n\t// wins, and stopImmediatePropagation keeps selection from also firing.\n\tconst handleToolClick = (event: MouseEvent) => {\n\t\tif (wasDrag(event)) return;\n\t\tif (tools.handleClick(event)) event.stopImmediatePropagation();\n\t};\n\tcanvas.addEventListener('mousedown', handlePointerDown, { capture: true });\n\tcanvas.addEventListener('click', handleToolClick, { capture: true });\n\n\t// Passive: moves only drive previews, never consume, so they can't interfere with orbit/pan.\n\tconst handleToolMove = (event: MouseEvent) => tools.handleMove(event);\n\tcanvas.addEventListener('mousemove', handleToolMove, { passive: true });\n\n\t// Rebound to the animation loop's real invalidate once it's created below.\n\tlet requestRender: () => void = () => {};\n\n\t// Applies regardless of edges.enabled — an explicit call should never be silently ignored.\n\t// Meshes the overlay path skipped — too large, or past the scene-wide overlay budget — switch the\n\t// screen-space edge fallback on; a later solve without such meshes switches it back off.\n\tconst applyEdges = (root: THREE.Object3D, overrides?: Partial<EdgeOptions>) => {\n\t\tvoid addEdgesAsync(root, {\n\t\t\tcolor: config.edges.color,\n\t\t\tdarken: config.edges.darken,\n\t\t\twidth: config.edges.width,\n\t\t\tthresholdAngle: config.edges.thresholdAngle,\n\t\t\tdistanceFade: config.edges.distanceFade,\n\t\t\tmaxTriangles: config.edges.maxTriangles,\n\t\t\tmaxSegments: config.edges.maxSegments,\n\t\t\tmaxOverlays: config.edges.maxOverlays,\n\t\t\t// Last, so a caller's override actually wins over the configured value.\n\t\t\t...overrides\n\t\t}).then(() => {\n\t\t\tupdateEdgeFallback(root);\n\t\t\trequestRender(); // async attach may land after the solve's own repaint\n\t\t});\n\t};\n\n\tconst updateEdgeFallback = (root: THREE.Object3D) => {\n\t\tif (config.edges.screenSpaceFallback === false) return;\n\t\tlet hasSkippedMeshes = false;\n\t\troot.traverse((object) => {\n\t\t\tconst skipped = object.userData?.edgesSkipped;\n\t\t\tif (skipped === EDGES_SKIPPED_TRIANGLE_CAP || skipped === EDGES_SKIPPED_OVERLAY_BUDGET) {\n\t\t\t\thasSkippedMeshes = true;\n\t\t\t}\n\t\t});\n\t\tpipeline.setEdgeFallback(hasSkippedMeshes);\n\t};\n\n\t// Also stands down the screen-space fallback — bare removeEdges alone would keep drawing lines\n\t// for capped meshes.\n\tconst clearEdges = (root: THREE.Object3D) => {\n\t\tremoveEdges(root);\n\t\tpipeline.setEdgeFallback(false);\n\t\trequestRender();\n\t};\n\n\tconst parent = canvas.parentElement;\n\tconst getCanvasSize = () =>\n\t\tparent\n\t\t\t? { width: parent.clientWidth, height: parent.clientHeight }\n\t\t\t: { width: window.innerWidth, height: window.innerHeight };\n\n\tconst pipeline = createPipelineController({\n\t\trenderer,\n\t\tscene,\n\t\tgetActiveCamera,\n\t\tgetCanvasSize,\n\t\tconfig,\n\t\trequestRender: () => requestRender()\n\t});\n\tpipeline.sync();\n\n\tconst appearance = createAppearanceController({\n\t\tscene,\n\t\trenderer,\n\t\tlights,\n\t\tconfig,\n\t\tpipeline,\n\t\tfullPixelRatio,\n\t\trequestRender: () => requestRender()\n\t});\n\n\tconst {\n\t\tanimate,\n\t\tdispose: disposeAnimation,\n\t\tinvalidate,\n\t\trenderNow\n\t} = createAnimationLoop(\n\t\trenderer,\n\t\tscene,\n\t\tcamera,\n\t\tgetActiveCamera,\n\t\tcameraController,\n\t\tcontrols,\n\t\tgetCanvasSize,\n\t\t() => config.render.pixelRatio!,\n\t\tconfig.events.onFrame,\n\t\tgrid,\n\t\tgizmo,\n\t\t() => pipeline.get(),\n\t\tlabelLayer,\n\t\tnearFitter,\n\t\tconfig.render.onDemand ?? true\n\t);\n\trequestRender = invalidate;\n\tanimate();\n\n\tscene.up.set(sceneUp.x, sceneUp.y, sceneUp.z);\n\n\t// Initial fit for geometry already present; hosts loading more later via updateScene should\n\t// call these again.\n\tupdateShadowBounds();\n\tupdateGridScale();\n\n\tconst captureImage = (type = 'image/png', quality?: number): Promise<Blob | null> => {\n\t\t// Draw and read in the same task: without preserveDrawingBuffer the colour buffer is gone\n\t\t// once the browser composites, and a deferred toBlob returns a blank image.\n\t\trenderNow();\n\t\treturn new Promise((resolve) => renderer.domElement.toBlob(resolve, type, quality));\n\t};\n\n\tconst addUserGeometry = (object: THREE.Object3D, appId?: string) => {\n\t\tobject.userData.source = appId === undefined ? SOURCE_USER : appSource(appId);\n\t\tscene.add(object);\n\t\trequestRender();\n\t};\n\n\tconst removeUserGeometry = (object: THREE.Object3D) => {\n\t\tobject.removeFromParent();\n\t\tdisposeObjectTree(object);\n\t\trequestRender();\n\t};\n\n\tconst clearUserGeometry = (appId?: string) => {\n\t\t// Snapshot first: removeFromParent would mutate scene.children mid-iteration otherwise.\n\t\tconst owned = scene.children.filter((child) => isOwnedBy(child, appId));\n\t\towned.forEach((object) => {\n\t\t\tobject.removeFromParent();\n\t\t\tdisposeObjectTree(object);\n\t\t});\n\t\trequestRender();\n\t};\n\n\tconst dispose = () => {\n\t\t// Idempotent: a second call would re-run forceContextLoss() on an already-lost context and\n\t\t// throw (double-dispose happens naturally under React StrictMode).\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tdisposeAnimation();\n\t\teventHandlers.dispose();\n\t\tcanvas.removeEventListener('mousedown', handlePointerDown, { capture: true });\n\t\tcanvas.removeEventListener('click', handleToolClick, { capture: true });\n\t\tcanvas.removeEventListener('mousemove', handleToolMove);\n\t\tmeasureTool?.dispose();\n\t\tlabelLayer?.dispose();\n\t\tgizmo?.dispose();\n\t\tgrid?.dispose();\n\t\tpipeline.dispose();\n\t\t// Stops any in-flight camera tween — its rAF ticks would otherwise keep touching the\n\t\t// disposed controls after teardown.\n\t\tcameraController.dispose();\n\t\tcontrols.dispose();\n\t\trenderer.dispose();\n\t\t// Frees the GL context itself: browsers cap live WebGL contexts (~16), and otherwise it won't\n\t\t// be reclaimed until GC, which can lag across rapid mount/unmount cycles.\n\t\trenderer.forceContextLoss();\n\n\t\tdisposeSceneResources(scene);\n\n\t\t// Cross-solve caches (parse/'s, reached via a registry rather than an import — layer rule)\n\t\t// outlive any single scene but not the GL context just destroyed. Refcounted: only the last\n\t\t// live viewer actually frees, and this must run after the scene sweep above.\n\t};\n\n\treturn {\n\t\tscene,\n\t\tcamera,\n\t\tcontrols,\n\t\trenderer,\n\t\tcameraController,\n\t\tgrid,\n\t\tgizmo,\n\t\tmeasureTool,\n\t\tlabelLayer,\n\t\ttools,\n\t\tapplyEdges,\n\t\tclearEdges,\n\t\tinvalidate,\n\t\tcaptureImage,\n\t\tsetAmbientOcclusion: pipeline.setAmbientOcclusion,\n\t\tsetLook: appearance.setLook,\n\t\tsyncEmissiveLights: appearance.syncEmissiveLights,\n\t\tsetFillLights: appearance.setFillLights,\n\t\tsetEnvironmentIntensity: appearance.setEnvironmentIntensity,\n\t\tsetToneMappingExposure: appearance.setToneMappingExposure,\n\t\tsetAoIntensity: appearance.setAoIntensity,\n\t\tgetMaterialAppearance: appearance.getMaterialAppearance,\n\t\tupdateShadowBounds,\n\t\tupdateGridScale,\n\t\tdispose,\n\t\tfitToView: eventHandlers.fitToView,\n\t\tclearSelection: eventHandlers.clearSelection,\n\t\tselectEntries: (targets) => {\n\t\t\teventHandlers.selectEntries(targets);\n\t\t\tinvalidate();\n\t\t},\n\t\tsetHover: (object, memberIndex) => {\n\t\t\teventHandlers.setHover(object, memberIndex);\n\t\t\tinvalidate();\n\t\t},\n\t\taddUserGeometry,\n\t\tremoveUserGeometry,\n\t\tclearUserGeometry\n\t};\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"render.js","names":["versionOf","renderable"],"sources":["../src/shared/types.ts","../src/render/scene-ownership.ts","../src/render/scene-setup/studio-environment.ts","../src/render/scene-setup/reflection-environment.ts","../src/render/up-axis.ts","../src/render/scene-setup/defaults.ts","../src/render/scene-setup/idle-refine.ts","../src/render/scene-setup/setup-lighting.ts","../src/render/three-helpers.ts","../src/render/camera-controller.ts","../src/render/edges/line-geometry.ts","../src/render/edge-extract.ts","../src/render/edges/options.ts","../src/render/edges/extraction.ts","../src/render/scene-setup/merged-picking.ts","../src/render/edges/member-filter.ts","../src/render/edges/overlay.ts","../src/render/edges.ts","../src/render/grid.ts","../src/render/label-layer.ts","../src/render/scene-pick.ts","../src/render/measure.ts","../src/render/near-plane.ts","../src/render/tool-registry.ts","../src/render/view-gizmo.ts","../src/render/scene-setup/animation-loop.ts","../src/render/scene-setup/emissive-lights.ts","../src/render/scene-setup/appearance.ts","../src/render/scene-setup/create-camera.ts","../src/render/scene-setup/create-scene.ts","../src/render/scene-setup/dispose.ts","../src/render/edge-detection-pass.ts","../src/render/render-pipeline.ts","../src/render/scene-setup/pipeline-controller.ts","../src/render/scene-setup/setup-controls.ts","../src/render/scene-setup/setup-environment.ts","../src/render/scene-setup/setup-events.ts","../src/render/scene-setup/tap-gesture.ts","../src/render/scene-setup/setup-renderer.ts","../src/render/scene-setup/init-three.ts"],"sourcesContent":["import type * as THREE from 'three';\n\n// ============================================================================\n// LOOKS\n// ============================================================================\n\n/** Source of truth for {@link Look} — lets consumers (e.g. a style picker) iterate instead of hardcoding names. */\nexport const LOOKS = [\n\t'technical',\n\t'studio',\n\t'showcase',\n\t'arctic',\n\t'xray',\n\t'lineart',\n\t'wireframe',\n\t'night'\n] as const;\n\nexport type Look = (typeof LOOKS)[number];\n\n/**\n * How a {@link Look} overrides the per-mesh material a solve produced. Omitted fields leave the\n * mesh's own value alone, so a look that sets none of them is purely a lighting change.\n *\n * These fight the model's real colours on purpose: `arctic` reads shape over material, `xray` reads\n * what is inside, `wireframe` reads topology. `setLook` snapshots the parsed values before the first\n * override and restores them when switching back, so the model's own colours survive a round trip.\n */\nexport type LookMaterialOverride = {\n\t/** Replaces the mesh's own colour. Hex, e.g. 0xf2f4f7. */\n\tcolor?: number;\n\tmetalness?: number;\n\troughness?: number;\n\t/** Replaces the glow of a material that emits from the wire. A selection tint keeps its own. */\n\temissiveIntensity?: number;\n\t/** Forces `transparent: true` when below 1. */\n\topacity?: number;\n\t/**\n\t * Skip the depth buffer so far faces aren't hidden by near ones — what makes an x-ray read\n\t * through the model instead of just looking like tinted glass. Costs correct sort order, which\n\t * is the intended trade.\n\t */\n\tdepthWrite?: boolean;\n\t/**\n\t * Draw each triangle as its three edges instead of a filled face. Shows the tessellation, not the\n\t * design edges the `edges` overlay extracts — a curved surface reads as a dense triangle mesh.\n\t * Lighting still applies but has almost nothing to shade, so a wireframe look wants flat fill\n\t * rather than a key light.\n\t */\n\twireframe?: boolean;\n\t/**\n\t * Replaces the parsed side. Closed solids parse as `FrontSide`; a look that shows what's behind\n\t * a surface (x-ray, wireframe) needs their far side back.\n\t */\n\tside?: THREE.Side;\n};\n\nexport type BloomOptions = { strength: number; radius: number; threshold: number };\n\n/**\n * The lighting/material dials a {@link Look} sets. It still never *drives* the edge overlay or the\n * grid; `requiresEdges` only states that a look is incomplete without edges, and the host decides\n * whether to honour it.\n */\nexport type LookPreset = {\n\ttoneMapping: THREE.ToneMapping;\n\ttoneMappingExposure: number;\n\tenvMapIntensity: number;\n\t/** Multiplier on the HDR's IBL (`scene.environmentIntensity`). */\n\tenvironmentIntensity: number;\n\themisphereIntensity: number;\n\tambientIntensity: number;\n\tcullBackfaces: boolean;\n\tambientOcclusion: boolean;\n\t/**\n\t * A key light casting a shadow. IBL alone lights every face of a box almost equally, so without\n\t * this a model reads as a flat white silhouette — the directional falloff is what separates the\n\t * three faces meeting at a corner.\n\t */\n\tsunlightIntensity: number;\n\t/** Absent on the looks that only retune lighting. */\n\tmaterialOverride?: LookMaterialOverride;\n\t/** Replaces the scene's background while the look is active. */\n\tbackground?: number;\n\t/** Glow around anything brighter than `threshold` in linear light, so only emissive parts bloom. */\n\tbloom?: BloomOptions;\n\t/** Emissive meshes light the scene, through a shadow-casting point light each. */\n\temissiveLights?: boolean;\n\t/** How many emissive sources get a light. Default 3. */\n\tmaxEmissiveLights?: number;\n\t/** Caps the render resolution while this look is active. */\n\tmaxPixelRatio?: number;\n\t/** Replaces these fields on a phone (see `RenderConfig.lowPower`). */\n\tlowPower?: Partial<\n\t\tPick<LookPreset, 'ambientOcclusion' | 'bloom' | 'maxEmissiveLights' | 'maxPixelRatio'>\n\t>;\n\t/**\n\t * This look is a line drawing: without the edge overlay it renders as blank white shapes. Set\n\t * only by `lineart`. A declaration, not an action — `setLook` still touches no overlay, so a\n\t * host that ignores this gets a look that doesn't work rather than a broken invariant.\n\t *\n\t * A host honouring this must also turn `edges.distanceFade` off. The fade sets opacity per\n\t * overlay from its 15th-percentile segment length, so one finely-detailed mesh fades whole:\n\t * in a shaded look that softens some outlines, but here it erases parts of the only thing being\n\t * drawn, and the opacity is recomputed per frame so edges pop in and out while orbiting.\n\t */\n\trequiresEdges?: boolean;\n};\n\n/** How compute meshes read visually — the parse-time material choices baked from a {@link Look}. */\nexport interface MaterialAppearanceOptions {\n\t/** Default 1 (three.js's own material default) when omitted. */\n\tenvMapIntensity?: number;\n\t/**\n\t * `THREE.FrontSide` on every mesh, open surfaces included, which then vanish when viewed from\n\t * behind. Default false: only meshes detected as closed solids are culled.\n\t */\n\tcullBackfaces?: boolean;\n}\n","// ============================================================================\n// Scene ownership: who put an object in the scene, and what may remove it\n// ============================================================================\n//\n// A live scene mixes content from three owners: the solve (replaced wholesale every solve),\n// viewer aids (grid/floor/labels, never replaced), and host apps drawing their own geometry\n// alongside the solve. `userData.source` records which, and `clearScene` consults it to decide\n// what a solve is allowed to destroy.\n//\n// App content carries an owner id (`app:<id>`) rather than a flat `'user'` so a host running\n// more than one app can clear its own geometry without touching another's, and so a scoped\n// solve can replace one app's results while leaving the rest standing.\n\nimport type * as THREE from 'three';\n\n/** Geometry produced by a solve. Replaced wholesale on the next one. */\nexport const SOURCE_COMPUTE = 'compute';\n\n/**\n * Host-added geometry with no owner id. Predates scoped ownership and still honoured everywhere\n * an `app:` scope is — new code should prefer {@link appSource}.\n */\nexport const SOURCE_USER = 'user';\n\nconst APP_PREFIX = 'app:';\n\n/** The `userData.source` tag for geometry owned by app `id` (`'pointcloud'` → `'app:pointcloud'`). */\nexport function appSource(id: string): string {\n\treturn `${APP_PREFIX}${id}`;\n}\n\n/** The app id from a source tag, or null if the tag isn't app-owned. */\nexport function appIdFromSource(source: unknown): string | null {\n\tif (typeof source !== 'string' || !source.startsWith(APP_PREFIX)) return null;\n\treturn source.slice(APP_PREFIX.length) || null;\n}\n\n/**\n * True for anything a host added rather than the solve — `'user'` or any `app:` scope. This is\n * the predicate `clearScene` uses, so it decides what survives a solve.\n */\nexport function isHostOwned(object: THREE.Object3D): boolean {\n\tconst source = object.userData?.source;\n\treturn source === SOURCE_USER || appIdFromSource(source) !== null;\n}\n\n/**\n * True for objects owned by `id`. Passing no id matches every host-owned object, which is what\n * `clearUserGeometry()` does.\n */\nexport function isOwnedBy(object: THREE.Object3D, id?: string): boolean {\n\tif (id === undefined) return isHostOwned(object);\n\treturn object.userData?.source === appSource(id);\n}\n","import * as THREE from 'three';\n\n/** What a softbox costs to set up, in radiance: only their ratio to the room matters to a reflection. */\nconst OVERHEAD = 2.5;\nconst KEY_STRIP = 11;\nconst FILL_STRIP = 4.5;\nconst RIM = 3.5;\nconst FLOOR = 0.05;\n\n// The walls grade from dark at the floor to light overhead. A flat sheet mirrors one direction of\n// the room, and on a dark wall a standing panel turned brass olive and copper brown. Graded, every\n// direction returns some light, and down still reads as turned away from it.\nconst WALL_LOW = 0.06;\nconst WALL_HORIZON = 0.65;\nconst WALL_HIGH = 0.8;\n\nconst ROOM_SIZE = 10;\n\nfunction softbox(\n\tradiance: number,\n\twidth: number,\n\theight: number,\n\tposition: [number, number, number]\n): THREE.Mesh {\n\tconst panel = new THREE.Mesh(\n\t\tnew THREE.PlaneGeometry(width, height),\n\t\tnew THREE.MeshBasicMaterial({\n\t\t\tcolor: new THREE.Color().setScalar(radiance),\n\t\t\tside: THREE.DoubleSide\n\t\t})\n\t);\n\tpanel.position.set(...position);\n\tpanel.lookAt(0, 0, 0);\n\treturn panel;\n}\n\n/**\n * A photographer's table, for PMREM to bake into a reflection probe: softboxes on a dark room.\n * Shape comes from the gaps between lights. A surface that reflects light everywhere has no contour,\n * which is why three's `RoomEnvironment`, bright on all sides, leaves metal looking like flat paint.\n * The floor stays dark so an underside reads as turned away from the light.\n */\nexport function createStudioScene(): THREE.Scene {\n\tconst scene = new THREE.Scene();\n\n\tconst half = ROOM_SIZE / 2;\n\tconst room = new THREE.Mesh(\n\t\tgradedRoom(),\n\t\tnew THREE.MeshBasicMaterial({ vertexColors: true, side: THREE.BackSide })\n\t);\n\tconst floor = new THREE.Mesh(\n\t\tnew THREE.PlaneGeometry(ROOM_SIZE, ROOM_SIZE),\n\t\tnew THREE.MeshBasicMaterial({ color: new THREE.Color().setScalar(FLOOR) })\n\t);\n\tfloor.rotation.x = -Math.PI / 2;\n\tfloor.position.y = 1 - half;\n\n\tscene.add(\n\t\troom,\n\t\tfloor,\n\t\tsoftbox(OVERHEAD, 6.5, 4.5, [0, half - 0.2, 0.6]),\n\t\tsoftbox(KEY_STRIP, 1.4, 5.5, [-(half - 0.3), 0.8, 1.6]),\n\t\tsoftbox(FILL_STRIP, 0.9, 5, [half - 0.3, 0.5, 2.4]),\n\t\tsoftbox(RIM, 5, 0.8, [0.6, 2.2, -(half - 0.3)])\n\t);\n\treturn scene;\n}\n\nfunction gradedRoom(): THREE.BoxGeometry {\n\tconst geometry = new THREE.BoxGeometry(ROOM_SIZE, ROOM_SIZE, ROOM_SIZE, 1, 16, 1);\n\tconst position = geometry.attributes.position!;\n\tconst colors = new Float32Array(position.count * 3);\n\tfor (let i = 0; i < position.count; i++) {\n\t\tconst t = position.getY(i) / (ROOM_SIZE / 2);\n\t\tconst radiance =\n\t\t\tt < 0\n\t\t\t\t? THREE.MathUtils.lerp(WALL_HORIZON, WALL_LOW, -t)\n\t\t\t\t: THREE.MathUtils.lerp(WALL_HORIZON, WALL_HIGH, t);\n\t\tcolors.fill(radiance, i * 3, i * 3 + 3);\n\t}\n\tgeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));\n\treturn geometry;\n}\n\n/** Frees what {@link createStudioScene} built, once PMREM has baked it. */\nexport function disposeStudioScene(scene: THREE.Scene): void {\n\tscene.traverse((object) => {\n\t\tconst mesh = object as Partial<THREE.Mesh>;\n\t\tmesh.geometry?.dispose();\n\t\t(mesh.material as THREE.Material | undefined)?.dispose();\n\t});\n}\n","import * as THREE from 'three';\n\nimport { SOURCE_COMPUTE } from '../scene-ownership.js';\nimport { createStudioScene, disposeStudioScene } from './studio-environment.js';\n\n// A metal shows nothing but its reflection. The HDR that lights the scene is an outdoor sky with a\n// hard horizon over a near-black ground, so a metal mirrors a white band over a black one and the\n// split lands mid-face, jagged wherever roughness varies. Metals reflect a soft studio room instead.\n// Everything else keeps the HDR, clear coats included: a material's envMap replaces its diffuse\n// lighting too, and the studio's softboxes wash a dark paint out to pastel.\nconst METAL = 0.5;\n\n/** Above PMREM's blur for a mirror, so a softbox has an edge in chrome without showing as a shape. */\nconst STUDIO_BLUR = 0.03;\n\nconst KEY = 'selvaReflectionEnvironment';\n\ninterface ReflectionEnvironment {\n\ttexture: THREE.Texture;\n\trotation: THREE.Euler;\n}\n\n/**\n * Builds the studio probe once per scene. `rotation` turns its Y-up room to the scene's up, the same\n * rotation the HDR gets.\n */\nexport function createReflectionEnvironment(\n\tscene: THREE.Scene,\n\trenderer: THREE.WebGLRenderer,\n\trotation: THREE.Euler\n): void {\n\tconst pmrem = new THREE.PMREMGenerator(renderer);\n\tconst studio = createStudioScene();\n\tconst texture = pmrem.fromScene(studio, STUDIO_BLUR).texture;\n\tdisposeStudioScene(studio);\n\tpmrem.dispose();\n\tinstallReflectionEnvironment(scene, texture, rotation);\n}\n\n/** The GPU-free half of {@link createReflectionEnvironment}; tests call it with a plain texture. */\nexport function installReflectionEnvironment(\n\tscene: THREE.Scene,\n\ttexture: THREE.Texture,\n\trotation: THREE.Euler\n): void {\n\tconst environment: ReflectionEnvironment = { texture, rotation: rotation.clone() };\n\tscene.userData[KEY] = environment;\n\tapplyReflectionEnvironment(scene, scene);\n}\n\nexport function disposeReflectionEnvironment(scene: THREE.Scene): void {\n\t(scene.userData[KEY] as ReflectionEnvironment | undefined)?.texture.dispose();\n\tdelete scene.userData[KEY];\n}\n\n/**\n * Points every metallic solve material under `root` at the studio probe, and every other one back\n * at `scene.environment`. Run after meshes are added and after a look changes metalness.\n *\n * A material's own `envMap` also makes three honour its `envMapIntensity`: with only\n * `scene.environment`, three overwrites it with `scene.environmentIntensity` on every draw.\n */\nexport function applyReflectionEnvironment(root: THREE.Object3D, scene: THREE.Scene): void {\n\tconst environment = scene.userData[KEY] as ReflectionEnvironment | undefined;\n\tif (!environment) return;\n\troot.traverse((object) => {\n\t\tif (object.userData.source !== SOURCE_COMPUTE) return;\n\t\tconst mesh = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tconst materials = Array.isArray(mesh.material)\n\t\t\t? mesh.material\n\t\t\t: mesh.material\n\t\t\t\t? [mesh.material]\n\t\t\t\t: [];\n\t\tfor (const material of materials) {\n\t\t\tif (!(material instanceof THREE.MeshStandardMaterial)) continue;\n\t\t\t// Leave a host's own envMap alone.\n\t\t\tif (material.envMap && material.envMap !== environment.texture) continue;\n\t\t\tconst wanted = material.metalness > METAL ? environment.texture : null;\n\t\t\tif (material.envMap === wanted) continue;\n\t\t\tmaterial.envMap = wanted;\n\t\t\tmaterial.envMapRotation.copy(environment.rotation);\n\t\t\tmaterial.needsUpdate = true;\n\t\t}\n\t});\n}\n","import * as THREE from 'three';\n\n/**\n * Single source of truth for \"which way is up, and what do front/right mean\" — camera framing, sun\n * position, ground offset, and view presets all derive from {@link buildUpBasis} rather than\n * hardcoding an axis, so a Y-up scene gets a correct horizon and sun instead of the below-horizon\n * result a hardcoded Z-up vector would give.\n *\n * `forward` is the camera's look direction (camera → model); a view preset's camera position is the\n * reverse (target → camera), see `camera-controller.ts`. `right` is `seed x up`, not `up x seed`, to\n * match Rhino's handedness: for Z-up this makes the Front-view camera look along +Y and the\n * Right-view camera look along -X (it sits at `right` = +X, facing back toward the origin).\n */\n\n/** Orthonormal frame derived from a scene up axis. All vectors are unit length. */\nexport interface UpBasis {\n\tup: THREE.Vector3;\n\tforward: THREE.Vector3;\n\tright: THREE.Vector3;\n}\n\n/** Ground-plane axes for a given up vector: Z-up yields forward = +Y, right = +X (Rhino's Front/Right). */\nexport function buildUpBasis(up: THREE.Vector3): UpBasis {\n\tconst u = up.clone().normalize();\n\n\t// Seed must not be (nearly) parallel to up, or the cross product is unstable.\n\tconst worldZ = new THREE.Vector3(0, 0, 1);\n\tconst worldY = new THREE.Vector3(0, 1, 0);\n\tconst seed = Math.abs(u.dot(worldZ)) > 0.9 ? worldY : worldZ;\n\n\tconst right = new THREE.Vector3().crossVectors(seed, u).normalize();\n\tconst forward = new THREE.Vector3().crossVectors(u, right).normalize();\n\n\treturn { up: u, forward, right };\n}\n\n/** Default 3/4 iso camera offset from the target (behind-left, above), scaled to `distance`. */\nexport function isoOffset(up: THREE.Vector3, distance: number): THREE.Vector3 {\n\tconst { forward, right, up: u } = buildUpBasis(up);\n\t// Normalize before scaling so `distance` is the true radius, not the diagonal of the raw sum.\n\treturn forward\n\t\t.clone()\n\t\t.multiplyScalar(-1)\n\t\t.add(right.clone().multiplyScalar(-1))\n\t\t.add(u)\n\t\t.normalize()\n\t\t.multiplyScalar(distance);\n}\n\n/** Default sun position: high above the model, offset to one side for a directional gradient. */\nexport function sunOffset(up: THREE.Vector3, sideDistance: number, height: number): THREE.Vector3 {\n\tconst { forward, right, up: u } = buildUpBasis(up);\n\treturn right\n\t\t.clone()\n\t\t.multiplyScalar(sideDistance)\n\t\t.add(forward.clone().multiplyScalar(sideDistance))\n\t\t.add(u.clone().multiplyScalar(height));\n}\n\n/**\n * Rotates an equirectangular environment map's horizon onto the scene's ground plane.\n *\n * Three's equirect mapping is hardcoded to Y-up: the HDR's horizon is assumed to lie in the XZ\n * plane with zenith along +Y. In a Z-up scene that leaves the environment on its side — horizon\n * vertical, lighting arriving from +Y instead of overhead. A neutral studio HDR hides this; any\n * sky/ground HDR makes it obvious.\n *\n * Returns the Euler rotating the map's native +Y zenith onto `up` (identity for Y-up). Apply to\n * BOTH `scene.environmentRotation` and `scene.backgroundRotation` — they're independent, and\n * setting only one desyncs background from lighting.\n */\nexport function environmentRotationFor(up: THREE.Vector3): THREE.Euler {\n\tconst u = up.clone().normalize();\n\tconst mapZenith = new THREE.Vector3(0, 1, 0);\n\n\tif (u.dot(mapZenith) > 0.9999) return new THREE.Euler();\n\n\t// Upside-down (-Y): setFromUnitVectors picks an arbitrary perpendicular axis for a 180° flip,\n\t// spinning the horizon. Roll about X instead so the horizon stays put.\n\tif (u.dot(mapZenith) < -0.9999) return new THREE.Euler(Math.PI, 0, 0);\n\n\tconst quaternion = new THREE.Quaternion().setFromUnitVectors(mapZenith, u);\n\treturn new THREE.Euler().setFromQuaternion(quaternion);\n}\n\n/** Which world axis the up vector most closely aligns with. */\nexport function upToAxis(up: THREE.Vector3): 'x' | 'y' | 'z' {\n\tconst ax = Math.abs(up.x);\n\tconst ay = Math.abs(up.y);\n\tconst az = Math.abs(up.z);\n\tif (ax >= ay && ax >= az) return 'x';\n\tif (ay >= az) return 'y';\n\treturn 'z';\n}\n","import * as THREE from 'three';\n\nimport { DEFAULT_LOOK, lookPreset } from '../../shared/index.js';\nimport type { ThreeInitializerOptions } from '../types.js';\nimport { isoOffset, sunOffset, upToAxis } from '../up-axis.js';\n\n/** Rhino's convention, and the frame all geometry arrives in — Selva is Z-up end to end. */\nexport const defaultUp = new THREE.Vector3(0, 0, 1);\n\n// onMaxAnisotropy stays optional — a caller-supplied hook, not a config value with a default.\nexport type ResolvedOptions = Required<Omit<ThreeInitializerOptions, 'onMaxAnisotropy'>> &\n\tPick<ThreeInitializerOptions, 'onMaxAnisotropy'>;\n\n/**\n * Decided once, from the device: reacting to slow frames would change the light count, which\n * recompiles every shader on the device least able to. A coarse pointer alone would catch tablets;\n * 600px is the narrowest tablet.\n */\nexport function isPhone(): boolean {\n\tconst coarse = window.matchMedia?.('(pointer: coarse)').matches ?? false;\n\treturn coarse && Math.min(window.screen.width, window.screen.height) < 600;\n}\n\nexport function applyDefaults(options: ThreeInitializerOptions): ResolvedOptions {\n\tconst scale = options.sceneScale || 'm';\n\n\t// Geometry is always in meters; sceneScale only changes camera/light/grid magnitudes.\n\tconst scaleDefaults = {\n\t\tmm: {\n\t\t\tcameraDistance: 20,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 100,\n\t\t\tlightDistance: 10,\n\t\t\tlightHeight: 20,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 100,\n\t\t\tscaleFactor: 1000\n\t\t},\n\t\tcm: {\n\t\t\tcameraDistance: 20,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 100,\n\t\t\tlightDistance: 25,\n\t\t\tlightHeight: 50,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 100,\n\t\t\tscaleFactor: 100\n\t\t},\n\t\tm: {\n\t\t\tcameraDistance: 10,\n\t\t\tnear: 0.01,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 50,\n\t\t\tlightDistance: 25,\n\t\t\tlightHeight: 50,\n\t\t\tminDistance: 0.001,\n\t\t\tshadowSize: 100,\n\t\t\tscaleFactor: 1\n\t\t},\n\t\tinches: {\n\t\t\tcameraDistance: 15,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 80,\n\t\t\tlightDistance: 20,\n\t\t\tlightHeight: 40,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 80,\n\t\t\tscaleFactor: 39.37\n\t\t},\n\t\tfeet: {\n\t\t\tcameraDistance: 8,\n\t\t\tnear: 0.1,\n\t\t\tfar: 2000,\n\t\t\tfloorSize: 40,\n\t\t\tlightDistance: 15,\n\t\t\tlightHeight: 30,\n\t\t\tminDistance: 0.1,\n\t\t\tshadowSize: 60,\n\t\t\tscaleFactor: 3.28084\n\t\t}\n\t};\n\n\tconst defaults = scaleDefaults[scale];\n\n\t// The look seeds lighting/material defaults (tone mapping, AO, IBL, fill), ranked below explicit\n\t// per-field options but above the plain defaults; it never touches edges/grid.\n\tconst look = options.look ?? DEFAULT_LOOK;\n\tconst lowPower = options.render?.lowPower ?? isPhone();\n\tconst preset = lookPreset(look, lowPower);\n\n\treturn {\n\t\tsceneScale: scale,\n\t\tlook,\n\t\tcamera: {\n\t\t\t// Default 3/4 iso (behind-left, above), derived from the scene up axis so a Y-up scene still\n\t\t\t// gets an overhead iso rather than a below-horizon view. cameraDistance*sqrt(3) preserves the\n\t\t\t// orbit radius of the old per-component (-d,-d,d) vector so this doesn't rezoom every scene.\n\t\t\tposition:\n\t\t\t\toptions.camera?.position ||\n\t\t\t\tisoOffset(\n\t\t\t\t\toptions.environment?.sceneUp ?? defaultUp,\n\t\t\t\t\tdefaults.cameraDistance * Math.sqrt(3)\n\t\t\t\t),\n\t\t\tfov: options.camera?.fov || 20,\n\t\t\tnear: options.camera?.near || defaults.near,\n\t\t\tfar: options.camera?.far || defaults.far,\n\t\t\ttarget: options.camera?.target || new THREE.Vector3(0, 0, 0),\n\t\t\tdynamicNear: options.camera?.dynamicNear ?? true\n\t\t},\n\t\tlighting: {\n\t\t\tenableSunlight: options.lighting?.enableSunlight ?? true,\n\t\t\tsunlightIntensity: options.lighting?.sunlightIntensity ?? preset.sunlightIntensity,\n\t\t\t// Expressed in the scene basis so the sun stays overhead in any up convention.\n\t\t\tsunlightPosition:\n\t\t\t\toptions.lighting?.sunlightPosition ||\n\t\t\t\tsunOffset(\n\t\t\t\t\toptions.environment?.sceneUp ?? defaultUp,\n\t\t\t\t\tdefaults.lightDistance,\n\t\t\t\t\tdefaults.lightHeight\n\t\t\t\t),\n\t\t\tambientLightColor: options.lighting?.ambientLightColor || new THREE.Color(0x404040),\n\t\t\tambientLightIntensity: options.lighting?.ambientLightIntensity ?? preset.ambientIntensity,\n\t\t\tsunlightColor: options.lighting?.sunlightColor || 0xffffff,\n\t\t\t// A positive hemisphereIntensity is what actually creates the light in setupLighting.\n\t\t\tenableHemisphereLight:\n\t\t\t\toptions.lighting?.enableHemisphereLight ?? preset.hemisphereIntensity > 0,\n\t\t\themisphereSkyColor: options.lighting?.hemisphereSkyColor ?? 0xdfe6ff,\n\t\t\themisphereGroundColor: options.lighting?.hemisphereGroundColor ?? 0x6b5f52,\n\t\t\themisphereIntensity: options.lighting?.hemisphereIntensity ?? preset.hemisphereIntensity\n\t\t},\n\t\tenvironment: {\n\t\t\thdrPath: options.environment?.hdrPath || '/baseHDR.hdr',\n\t\t\tbackgroundColor: options.environment?.backgroundColor || new THREE.Color(0xf0f0f0),\n\t\t\tenableEnvironmentLighting: options.environment?.enableEnvironmentLighting ?? true,\n\t\t\tsceneUp: options.environment?.sceneUp || defaultUp,\n\t\t\tshowEnvironment: options.environment?.showEnvironment ?? false,\n\t\t\tenvironmentIntensity: options.environment?.environmentIntensity ?? preset.environmentIntensity\n\t\t},\n\t\tfloor: {\n\t\t\tenabled: options.floor?.enabled ?? false,\n\t\t\tsize: options.floor?.size || defaults.floorSize,\n\t\t\tcolor: options.floor?.color || new THREE.Color(0x808080),\n\t\t\troughness: options.floor?.roughness ?? 0.7,\n\t\t\tmetalness: options.floor?.metalness ?? 0.0,\n\t\t\treceiveShadow: options.floor?.receiveShadow ?? true\n\t\t},\n\t\trender: {\n\t\t\tenableShadows: options.render?.enableShadows ?? true,\n\t\t\tshadowMapSize: options.render?.shadowMapSize || 1024,\n\t\t\tantialias: options.render?.antialias ?? true,\n\t\t\tpixelRatio: options.render?.pixelRatio || Math.min(window.devicePixelRatio, 2),\n\t\t\t// ?? not ||: an explicit NoToneMapping (0) must be honoured, not fall through as falsy.\n\t\t\ttoneMapping: options.render?.toneMapping ?? preset.toneMapping,\n\t\t\ttoneMappingExposure: options.render?.toneMappingExposure ?? preset.toneMappingExposure,\n\t\t\tpreserveDrawingBuffer: options.render?.preserveDrawingBuffer ?? false,\n\t\t\tlowPower,\n\t\t\tidleRefineFrames: options.render?.idleRefineFrames ?? (lowPower ? 0 : 32),\n\t\t\tlightOrbitFrameMs: options.render?.lightOrbitFrameMs ?? 22,\n\t\t\tambientOcclusion: options.render?.ambientOcclusion ?? preset.ambientOcclusion,\n\t\t\taoIntensity: options.render?.aoIntensity ?? 1,\n\t\t\tbloom: options.render?.bloom ?? preset.bloom,\n\t\t\t// Default caps AO buffers at 1x — biggest lever on GTAO cost at high DPI.\n\t\t\taoPixelRatio: options.render?.aoPixelRatio ?? 1,\n\t\t\tonDemand: options.render?.onDemand ?? true\n\t\t},\n\t\tcontrols: {\n\t\t\tenableDamping: options.controls?.enableDamping ?? false,\n\t\t\tdampingFactor: options.controls?.dampingFactor || 0.05,\n\t\t\tautoRotate: options.controls?.autoRotate ?? false,\n\t\t\tautoRotateSpeed: options.controls?.autoRotateSpeed || 0.5,\n\t\t\tenableZoom: options.controls?.enableZoom ?? true,\n\t\t\tenablePan: options.controls?.enablePan ?? true,\n\t\t\tminDistance: options.controls?.minDistance || defaults.minDistance,\n\t\t\tmaxDistance: options.controls?.maxDistance || Infinity\n\t\t},\n\t\tgrid: {\n\t\t\t// Mirrors createGrid's own defaults so the two never drift.\n\t\t\tenabled: options.grid?.enabled ?? false,\n\t\t\tcellSize: options.grid?.cellSize ?? 1,\n\t\t\tmajorEvery: options.grid?.majorEvery ?? 10,\n\t\t\tcellColor: options.grid?.cellColor ?? 0x888888,\n\t\t\tmajorColor: options.grid?.majorColor ?? 0x444444,\n\t\t\tfadeDistance: options.grid?.fadeDistance ?? 100,\n\t\t\t// Orthogonal to the scene up axis: Z-up Rhino -> 'z', Y-up -> 'y'.\n\t\t\tplane: options.grid?.plane ?? upToAxis(options.environment?.sceneUp ?? defaultUp)\n\t\t},\n\t\tgizmo: {\n\t\t\tenabled: options.gizmo?.enabled ?? false\n\t\t},\n\t\tedges: {\n\t\t\t// Mirrors addEdges' own defaults so the two never drift.\n\t\t\tenabled: options.edges?.enabled ?? false,\n\t\t\t// Undefined lets addEdges derive each mesh's edge color from its own surface material.\n\t\t\tcolor: options.edges?.color,\n\t\t\tdarken: options.edges?.darken,\n\t\t\twidth: options.edges?.width ?? 1.5,\n\t\t\tthresholdAngle: options.edges?.thresholdAngle ?? 44,\n\t\t\tdistanceFade: options.edges?.distanceFade ?? true,\n\t\t\t// Passed through undefined on purpose: the caps' canonical defaults live in\n\t\t\t// `edges/options.ts` (resolveOptions), and applyEdges forwards these straight to it.\n\t\t\t// Restating them here would be a second copy free to drift from the real one.\n\t\t\tmaxTriangles: options.edges?.maxTriangles,\n\t\t\tmaxSegments: options.edges?.maxSegments,\n\t\t\tmaxOverlays: options.edges?.maxOverlays,\n\t\t\t// Read by init-three's updateEdgeFallback, which only checks for an explicit `false`.\n\t\t\tscreenSpaceFallback: options.edges?.screenSpaceFallback\n\t\t},\n\t\tmeasure: {\n\t\t\t// Visual defaults live in createMeasureTool; these pass through undefined to it.\n\t\t\tenabled: options.measure?.enabled ?? false,\n\t\t\tsnapPixels: options.measure?.snapPixels,\n\t\t\tcolor: options.measure?.color,\n\t\t\tlabelClassName: options.measure?.labelClassName,\n\t\t\tdisplayUnit: options.measure?.displayUnit,\n\t\t\tformat: options.measure?.format\n\t\t},\n\t\tevents: {\n\t\t\tonBackgroundClicked: options.events?.onBackgroundClicked,\n\t\t\tonObjectSelected: options.events?.onObjectSelected,\n\t\t\tonMeshMetadataClicked: options.events?.onMeshMetadataClicked,\n\t\t\tonMeshDoubleClicked: options.events?.onMeshDoubleClicked,\n\t\t\tselectionColor: options.events?.selectionColor || '#ff0000',\n\t\t\tenableEventHandlers: options.events?.enableEventHandlers ?? true,\n\t\t\tenableKeyboardControls: options.events?.enableKeyboardControls ?? true,\n\t\t\tenableClickToFocus: options.events?.enableClickToFocus ?? true,\n\t\t\tenableDoubleClickZoom: options.events?.enableDoubleClickZoom ?? true,\n\t\t\tonReady: options.events?.onReady,\n\t\t\tonFrame: options.events?.onFrame\n\t\t},\n\t\tonMaxAnisotropy: options.onMaxAnisotropy\n\t};\n}\n","import * as THREE from 'three';\n\nimport type { RenderPipeline } from '../render-pipeline.js';\n\n/** Long enough to skip the pauses inside an orbit, or a drag would keep restarting the average. */\nconst SETTLE_MS = 150;\n\n/** Ambient occlusion is the largest cost of a frame, and the only one that can fade without a pop. */\nconst AO_FADE_FRAMES = 8;\n\n/** Enough moving frames that one hitch can't decide it. */\nconst ORBIT_SAMPLES = 12;\n\n/** Low-discrepancy sequence in [0, 1): N samples cover the range far more evenly than random ones. */\nexport function halton(index: number, base: number): number {\n\tlet result = 0;\n\tlet fraction = 1 / base;\n\tfor (let i = index; i > 0; i = Math.floor(i / base)) {\n\t\tresult += (i % base) * fraction;\n\t\tfraction /= base;\n\t}\n\treturn result;\n}\n\n/** Counts what a forgotten `invalidate()` most likely changed: visibility and materials. */\nfunction sceneStamp(scene: THREE.Scene): number {\n\tlet stamp = 0;\n\tscene.traverseVisible((object) => {\n\t\tstamp += 1;\n\t\tconst { material } = object as Partial<THREE.Mesh>;\n\t\tfor (const each of Array.isArray(material) ? material : material ? [material] : []) {\n\t\t\tstamp += each.version * 31;\n\t\t}\n\t});\n\treturn stamp;\n}\n\nexport interface IdleRefiner {\n\t/** Something changed: drop the average, and wait for the camera to settle again. */\n\treset(now: number): void;\n\t/** The camera moved this frame, after `deltaMs`. */\n\tmoving(deltaMs: number): void;\n\t/** Whether the next frame should be a refinement instead of nothing. */\n\twants(now: number): boolean;\n\t/** Draws one refinement and shows the average so far. */\n\tstep(camera: THREE.Camera): void;\n\t/** Puts the finished average back on the canvas. False if there is none. */\n\tpresent(): boolean;\n\t/** True once the average is finished and the scene hasn't changed under it. */\n\tisHolding(): boolean;\n}\n\n/**\n * Softens the picture while nothing moves. Each frame moves the camera by under a pixel and every\n * light within its real size, and the average of those frames has true penumbras and clean edges at\n * no cost to orbiting. The averaged image would be lost to the render loop's safety repaint, so\n * while it holds, that repaint only checks whether the scene changed.\n *\n * It also keeps orbiting smooth on a slow GPU. Once frames run over `lightOrbitFrameMs`, ambient\n * occlusion fades out while the camera moves and back in as it rests. Decided once, so a machine\n * near the limit doesn't flip between the two, and a fast one never sees it.\n */\nexport function createIdleRefiner(params: {\n\tscene: THREE.Scene;\n\trenderer: THREE.WebGLRenderer;\n\tgetPipeline: () => RenderPipeline | null;\n\tframes: number;\n\tlightOrbitFrameMs: number;\n\tjitterLights: (index: number) => void;\n\trestoreLights: () => void;\n}): IdleRefiner {\n\tconst { scene, renderer, getPipeline, frames, lightOrbitFrameMs, jitterLights, restoreLights } =\n\t\tparams;\n\n\tlet index = 0;\n\tlet changedAt = 0;\n\tlet stamp = 0;\n\tconst size = new THREE.Vector2();\n\n\t// Fading back in needs the refine frames, so without them AO stays on.\n\tlet lightOrbit = frames > 0 && lightOrbitFrameMs <= 0;\n\tlet aoWeight = 1;\n\tconst orbitSamples: number[] = [];\n\tconst setAoWeight = (weight: number) => {\n\t\taoWeight = Math.min(1, Math.max(0, weight));\n\t\tgetPipeline()?.setAoWeight(aoWeight);\n\t};\n\tconst measureOrbit = (deltaMs: number) => {\n\t\torbitSamples.push(deltaMs);\n\t\tif (orbitSamples.length < ORBIT_SAMPLES) return;\n\t\torbitSamples.sort((a, b) => a - b);\n\t\tlightOrbit = orbitSamples[ORBIT_SAMPLES >> 1]! > lightOrbitFrameMs;\n\t\torbitSamples.length = 0;\n\t};\n\n\tconst jitterCamera = (camera: THREE.Camera, sample: number) => {\n\t\tconst lens = camera as THREE.PerspectiveCamera | THREE.OrthographicCamera;\n\t\tif (!lens.setViewOffset) return;\n\t\trenderer.getDrawingBufferSize(size);\n\t\tconst x = sample ? halton(sample, 2) - 0.5 : 0;\n\t\tconst y = sample ? halton(sample, 3) - 0.5 : 0;\n\t\tlens.setViewOffset(size.x, size.y, x, y, size.x, size.y);\n\t};\n\n\treturn {\n\t\treset: (now) => {\n\t\t\tindex = 0;\n\t\t\tchangedAt = now;\n\t\t},\n\t\tmoving: (deltaMs) => {\n\t\t\tif (frames === 0) return;\n\t\t\tif (lightOrbit) setAoWeight(aoWeight - 1 / AO_FADE_FRAMES);\n\t\t\telse measureOrbit(deltaMs);\n\t\t},\n\t\twants: (now) =>\n\t\t\t(index < frames || aoWeight < 1) && now - changedAt >= SETTLE_MS && !!getPipeline(),\n\t\tstep: (camera) => {\n\t\t\tconst pipeline = getPipeline();\n\t\t\tif (!pipeline) return;\n\t\t\tif (aoWeight < 1) {\n\t\t\t\t// Shown as it fades, and left out of the average, which should only hold the full picture.\n\t\t\t\tsetAoWeight(aoWeight + 1 / AO_FADE_FRAMES);\n\t\t\t\tpipeline.setCamera(camera);\n\t\t\t\tpipeline.accumulate(0);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Frame 0 is the picture already on screen, so the average starts without a jump.\n\t\t\tif (index > 0) jitterLights(index);\n\t\t\tjitterCamera(camera, index);\n\t\t\tpipeline.setCamera(camera);\n\t\t\tpipeline.accumulate(index);\n\t\t\t(camera as THREE.PerspectiveCamera).clearViewOffset?.();\n\t\t\tif (index > 0) restoreLights();\n\t\t\tindex += 1;\n\t\t\tif (index === frames) stamp = sceneStamp(scene);\n\t\t},\n\t\tpresent: () => {\n\t\t\tif (frames === 0 || index < frames) return false;\n\t\t\tgetPipeline()?.presentAccumulated();\n\t\t\treturn !!getPipeline();\n\t\t},\n\t\tisHolding: () => frames > 0 && index >= frames && !!getPipeline() && stamp === sceneStamp(scene)\n\t};\n}\n","import * as THREE from 'three';\n\nimport { defaultUp, type ResolvedOptions } from './defaults.js';\nimport { halton } from './idle-refine.js';\n\nexport type SceneLights = {\n\tambient: THREE.AmbientLight;\n\t/** Null unless `lighting.enableHemisphereLight`. */\n\themisphere: THREE.HemisphereLight | null;\n\t/** Null when sunlight or shadows are disabled. */\n\tsun: THREE.DirectionalLight | null;\n};\n\nconst SHADOW_NORMAL_BIAS = 0.02;\n\n/**\n * In shadow-map texels, and the map always spans the model, so the penumbra stays about 1.5% of the\n * model at any scale. A sharp edge reads as a sticker; a real sky never gives one.\n */\nconst SUN_SHADOW_BLUR = 6;\n\n/** Sky and bounce always fill a shadow in, so a fully dark one looks painted on. */\nconst SUN_SHADOW_DENSITY = 0.62;\n\n/**\n * How far the sun moves while the view refines, as a tangent: about 1 degree. The real sun's 0.27\n * looks hard on a model this size, and much past 1 a small part's shadow dissolves completely.\n */\nconst SUN_DISC_RADIUS = 0.018;\n\n/** Survives a solve with the sun, which would otherwise aim at a target outside the scene. */\nexport const SUN_TARGET_ID = 'sun-target';\n\nexport function setupLighting(scene: THREE.Scene, config: ResolvedOptions): SceneLights {\n\tconst ambient = new THREE.AmbientLight(\n\t\tconfig.lighting.ambientLightColor,\n\t\tconfig.lighting.ambientLightIntensity\n\t);\n\tscene.add(ambient);\n\n\t// HemisphereLight defaults to +Y up, which is wrong for a Z-up scene — align to scene up instead.\n\tlet hemisphere: THREE.HemisphereLight | null = null;\n\tif (config.lighting.enableHemisphereLight) {\n\t\themisphere = new THREE.HemisphereLight(\n\t\t\tconfig.lighting.hemisphereSkyColor,\n\t\t\tconfig.lighting.hemisphereGroundColor,\n\t\t\tconfig.lighting.hemisphereIntensity\n\t\t);\n\t\tconst up = config.environment.sceneUp ?? defaultUp;\n\t\themisphere.position.copy(up);\n\t\tscene.add(hemisphere);\n\t}\n\n\tif (!config.lighting.enableSunlight) return { ambient, hemisphere, sun: null };\n\n\tconst sunlight = new THREE.DirectionalLight(\n\t\tconfig.lighting.sunlightColor ?? 0xffffff,\n\t\tconfig.lighting.sunlightIntensity\n\t);\n\tconst pos = config.lighting.sunlightPosition;\n\tif (pos) {\n\t\tsunlight.position.set(pos.x, pos.y, pos.z);\n\t}\n\n\tif (!config.render.enableShadows) {\n\t\tscene.add(sunlight);\n\t\treturn { ambient, hemisphere, sun: null };\n\t}\n\n\tsunlight.castShadow = true;\n\n\t// Frustum bounds are not set here — fitShadowToContent sizes them to scene content instead.\n\tsunlight.shadow.mapSize.width = config.render.shadowMapSize!;\n\tsunlight.shadow.mapSize.height = config.render.shadowMapSize!;\n\n\tsunlight.shadow.bias = -0.0001;\n\tsunlight.shadow.normalBias = SHADOW_NORMAL_BIAS;\n\tsunlight.shadow.radius = SUN_SHADOW_BLUR;\n\tsunlight.shadow.blurSamples = 12;\n\tsunlight.shadow.intensity = SUN_SHADOW_DENSITY;\n\n\tscene.add(sunlight);\n\t// A DirectionalLight aims at its target's world position, so the target must be in the scene\n\t// graph for its matrix to update.\n\tsunlight.target.userData.id = SUN_TARGET_ID;\n\tscene.add(sunlight.target);\n\treturn { ambient, hemisphere, sun: sunlight };\n}\n\n/** Moves the sun within its disc and returns how to put it back. */\nexport function jitterSun(light: THREE.DirectionalLight, index: number): () => void {\n\tconst home = light.position.clone();\n\tconst toSun = home.clone().sub(light.target.position);\n\tconst side = new THREE.Vector3(0, 0, 1).cross(toSun);\n\tif (side.lengthSq() === 0) side.set(1, 0, 0);\n\tside.setLength(toSun.length() * SUN_DISC_RADIUS);\n\tconst across = toSun.clone().cross(side).setLength(side.length());\n\n\tconst angle = halton(index, 2) * Math.PI * 2;\n\tconst reach = Math.sqrt(halton(index, 3));\n\tlight.position\n\t\t.addScaledVector(side, Math.cos(angle) * reach)\n\t\t.addScaledVector(across, Math.sin(angle) * reach);\n\treturn () => light.position.copy(home);\n}\n\n/**\n * Sizes a directional light's orthographic shadow frustum to the scene content's bounding sphere —\n * the dominant lever on shadow crispness. No-op on an empty or zero-size box: the frustum would\n * collapse to a point, and the sun would land on its target and lose its direction for good.\n */\nexport function fitShadowToContent(light: THREE.DirectionalLight, bounds: THREE.Box3): void {\n\tif (bounds.isEmpty()) return;\n\n\tconst center = bounds.getCenter(new THREE.Vector3());\n\t// Bounding-sphere radius keeps the frustum rotation-invariant; padded so grazing-angle casters\n\t// and VSM blur near the edges don't clip.\n\tconst radius = bounds.getSize(new THREE.Vector3()).length() * 0.5 * 1.2;\n\tif (radius === 0) return;\n\n\t// Scenes are in metres, so a fixed offset that suits a building erases a desk lamp's whole shadow.\n\tlight.shadow.normalBias = Math.min(SHADOW_NORMAL_BIAS, radius * 0.0015);\n\n\tconst cam = light.shadow.camera;\n\tcam.left = -radius;\n\tcam.right = radius;\n\tcam.top = radius;\n\tcam.bottom = -radius;\n\n\t// The configured position is a direction, not a place: left where it is, the sun ends up inside a\n\t// building-sized scene and everything behind it stops casting. Sits outside the sphere instead.\n\tconst direction = light.position.clone().sub(light.target.position).normalize();\n\tlight.target.position.copy(center);\n\tlight.target.updateMatrixWorld();\n\tconst lightDistance = radius * 2;\n\tlight.position.copy(center).addScaledVector(direction, lightDistance);\n\tlight.updateMatrixWorld();\n\n\tcam.near = lightDistance - radius;\n\tcam.far = lightDistance + radius;\n\tcam.updateProjectionMatrix();\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport { cachedSourceOf, computeCombinedBoundingBox, disposeObjectTree } from '../shared/index.js';\nimport { isHostOwned } from './scene-ownership.js';\nimport { applyReflectionEnvironment } from './scene-setup/reflection-environment.js';\nimport { SUN_TARGET_ID } from './scene-setup/setup-lighting.js';\nimport { isoOffset } from './up-axis';\n\nconst CAMERA_CONFIG = {\n\tHUGE_THRESHOLD: 10000,\n\tLARGE_THRESHOLD: 1000,\n\tSCALE_RATIO_THRESHOLD: 100,\n\tNEAR_PLANE_FACTOR: {\n\t\tTINY: 0.0001,\n\t\tSMALL: 0.001,\n\t\tNORMAL: 0.01\n\t},\n\tFAR_PLANE_FACTOR: {\n\t\tHUGE: 100,\n\t\tLARGE: 50,\n\t\tNORMAL: 20\n\t},\n\tINITIAL_DISTANCE_MULTIPLIER: 4\n};\n\n/** Replaces scene content with `meshes`, rescales the camera frustum to fit, and (first call only) positions the camera/controls. */\nexport function updateScene(\n\tscene: THREE.Scene,\n\tmeshes: THREE.Object3D[],\n\tcamera: THREE.PerspectiveCamera,\n\tcontrols: OrbitControls,\n\tinitialPositionSet: boolean\n) {\n\tclearScene(scene, cachedGeometriesOf(meshes));\n\n\tif (meshes.length === 0) return;\n\n\tmeshes.forEach((mesh) => {\n\t\tscene.add(mesh);\n\t\tapplyReflectionEnvironment(mesh, scene);\n\t});\n\n\tconst unionBoundingBox = computeCombinedBoundingBox(meshes);\n\tconst center = unionBoundingBox.getCenter(new THREE.Vector3());\n\tconst size = unionBoundingBox.getSize(new THREE.Vector3());\n\tconst maxDim = Math.max(size.x, size.y, size.z);\n\n\t// Rescaled every call, not just the first, so near/far stay well-conditioned when geometry\n\t// size changes drastically between solves.\n\tconst scaleRatio = maxDim / Math.min(size.x || 1, size.y || 1, size.z || 1);\n\n\tif (scaleRatio > CAMERA_CONFIG.SCALE_RATIO_THRESHOLD || maxDim > CAMERA_CONFIG.HUGE_THRESHOLD) {\n\t\tcamera.near = maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.TINY;\n\t\tcamera.far = maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.HUGE;\n\t} else if (maxDim > CAMERA_CONFIG.LARGE_THRESHOLD) {\n\t\tcamera.near = maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.SMALL;\n\t\tcamera.far = maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.LARGE;\n\t} else {\n\t\tcamera.near = Math.max(0.01, maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.NORMAL);\n\t\tcamera.far = Math.max(2000, maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.NORMAL);\n\t}\n\n\tcamera.updateProjectionMatrix();\n\n\t// Camera/controls are repositioned on first frame only. Zoom limits (min/maxDistance) are\n\t// deliberately NOT touched here: they're owned by the host via setupControls, and overwriting\n\t// them per solve would silently discard user-supplied configuration after the first update.\n\tif (!initialPositionSet) {\n\t\tconst distance = maxDim * CAMERA_CONFIG.INITIAL_DISTANCE_MULTIPLIER;\n\n\t\t// camera.up is already the configured sceneUp (initThree sets it before this runs), so the\n\t\t// iso offset stays consistent with whatever up-axis the viewer opened at.\n\t\tcamera.position.copy(center).add(isoOffset(camera.up, distance));\n\t\tcontrols.target.copy(center);\n\n\t\tcontrols.update();\n\t}\n}\n\n// Excluded from every content-bounds query: the grid is a huge plane that re-centers on the\n// camera each frame, so including it would make fit-to-view frame the camera's position instead\n// of the geometry.\nconst VIEWER_AID_IDS = new Set(['grid', 'floor', 'label-layer', 'measure', SUN_TARGET_ID]);\n\n/** True if `object` itself roots a viewer aid; {@link isViewerAid} also checks ancestors. */\nexport function isViewerAidRoot(object: THREE.Object3D): boolean {\n\treturn typeof object.userData.id === 'string' && VIEWER_AID_IDS.has(object.userData.id);\n}\n\n/** True if `object` or any ancestor is a viewer aid (grid/floor/labels/measure). */\nexport function isViewerAid(object: THREE.Object3D): boolean {\n\tlet current: THREE.Object3D | null = object;\n\twhile (current) {\n\t\tif (isViewerAidRoot(current)) return true;\n\t\tcurrent = current.parent;\n\t}\n\treturn false;\n}\n\n/**\n * Bounds of the scene's renderable content, excluding viewer aids (grid/floor/labels/measure).\n * Shared by fit-to-view, pick-threshold scaling, camera framing (`setView`), and shadow-frustum\n * fitting so they all measure exactly the same box.\n */\nexport function computeContentBounds(scene: THREE.Scene): THREE.Box3 {\n\t// Refresh world matrices once up front so expandByObject reads current transforms, regardless\n\t// of when the caller invokes this.\n\tscene.updateMatrixWorld(true);\n\tconst box = new THREE.Box3();\n\tscene.traverse((object) => {\n\t\tconst renderable = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tif (object.visible && !isViewerAid(object) && renderable.geometry) {\n\t\t\tbox.expandByObject(object);\n\t\t}\n\t});\n\treturn box;\n}\n\nfunction cachedGeometriesOf(objects: THREE.Object3D[]): Set<THREE.BufferGeometry> {\n\tconst cached = new Set<THREE.BufferGeometry>();\n\tfor (const root of objects) {\n\t\troot.traverse((object) => {\n\t\t\tconst geometry = (object as Partial<THREE.Mesh>).geometry;\n\t\t\tconst source = geometry && cachedSourceOf(geometry);\n\t\t\tif (source) cached.add(source);\n\t\t});\n\t}\n\treturn cached;\n}\n\nconst PERSISTENT_SCENE_IDS = new Set(['floor', 'grid', 'label-layer', SUN_TARGET_ID]);\n\n/** Frees the solve content. `keepUploaded` names the cached geometries the next content draws. */\nexport function clearScene(\n\tscene: THREE.Scene,\n\tkeepUploaded: ReadonlySet<THREE.BufferGeometry> = new Set()\n): void {\n\tconst kept: THREE.Object3D[] = [];\n\tconst removed: THREE.Object3D[] = [];\n\tfor (const object of scene.children) {\n\t\t// Removing the label-layer group here would orphan it: the CSS2D renderer only finds labels\n\t\t// by walking the live scene, so labels added afterwards would never render.\n\t\t// Host-added geometry (tagged by addUserGeometry, either plain 'user' or an app: scope)\n\t\t// persists across solves so it isn't lost when compute content is replaced.\n\t\t// Lights belong to the viewer, not the solve: losing them leaves only the environment map, with\n\t\t// no sun, no shadows and flat upward faces, and nothing errors.\n\t\tconst isLight = (object as Partial<THREE.Light>).isLight === true;\n\t\tif (isLight || PERSISTENT_SCENE_IDS.has(object.userData.id) || isHostOwned(object)) {\n\t\t\tkept.push(object);\n\t\t} else removed.push(object);\n\t}\n\n\t// One rebuild of the child list, not removeFromParent per object: that is indexOf + splice\n\t// each, O(n²), 3.5 s at 100k meshes. The events are the ones Object3D.remove dispatches.\n\tscene.children.length = 0;\n\tfor (const object of kept) scene.children.push(object);\n\tfor (const object of removed) {\n\t\t// Edge overlays are children of the meshes they outline, so this traversal disposes their\n\t\t// line geometries too — each overlay owns its geometry outright.\n\t\tdisposeObjectTree(object, { keepUploaded });\n\t\tobject.parent = null;\n\t\tobject.dispatchEvent({ type: 'removed' });\n\t\tscene.dispatchEvent({ type: 'childremoved', child: object });\n\t}\n}\n","import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport { computeContentBounds } from './three-helpers';\nimport { buildUpBasis } from './up-axis';\n\n/**\n * Runtime camera control: preset views, perspective⇄orthographic toggle, rotate lock.\n *\n * Centralized because projection switching swaps the camera object that OrbitControls drives, the\n * render loop renders, resize reshapes, and the raycaster picks with — {@link getActiveCamera} is\n * the one source of truth for all four call sites.\n *\n * Orthographic mirrors perspective's position/target with a frustum derived from perspective's FOV\n * and distance, so switching projections doesn't visually jump.\n */\n\nexport type ViewPreset = 'top' | 'bottom' | 'front' | 'back' | 'left' | 'right' | 'iso';\n\nexport type CameraProjection = 'perspective' | 'orthographic';\n\nexport interface CameraController {\n\t/** Swaps identity on {@link setProjection}. */\n\tgetActiveCamera(): THREE.Camera;\n\tgetProjection(): CameraProjection;\n\tsetProjection(projection: CameraProjection): void;\n\ttoggleProjection(): CameraProjection;\n\tsetView(preset: ViewPreset, animate?: boolean): void;\n\t/**\n\t * Frame current content from an explicit world-space direction (target → camera) instead of a\n\t * named preset — used by the nav-cube, whose clicked axis is a world axis.\n\t */\n\tsetViewDirection(direction: THREE.Vector3, animate?: boolean): void;\n\t/** Frame a world-space box from the current view direction. No-op on an empty box. */\n\tframeBounds(box: THREE.Box3, animate?: boolean): void;\n\tsetRotateEnabled(enabled: boolean): void;\n\tisRotateEnabled(): boolean;\n\tupdateAspect(width: number, height: number): void;\n\t/** Cancel any in-flight camera tween. Call on viewer teardown so ticks can't touch disposed controls. */\n\tdispose(): void;\n}\n\ninterface CameraControllerDeps {\n\tscene: THREE.Scene;\n\tperspective: THREE.PerspectiveCamera;\n\tcontrols: OrbitControls;\n\tonActiveCameraChange: (camera: THREE.Camera) => void;\n\t/** Drives presets, ortho camera up, and iso direction. Falls back to `perspective.up`. */\n\tup?: THREE.Vector3;\n}\n\n/**\n * Seven preset view directions (target → camera, unit vectors), derived from `up` rather than a\n * fixed Y-up table so Top/Front/… stay meaningful for Z-up Rhino scenes.\n *\n * `buildUpBasis`'s `forward` is camera→model; these are camera positions relative to target, so\n * \"front\" is `-forward`. Flipping this puts the camera behind the model and swaps left/right.\n */\nfunction buildViewDirections(up: THREE.Vector3): Record<ViewPreset, THREE.Vector3> {\n\tconst { up: u, forward, right } = buildUpBasis(up);\n\n\t// Camera positions are opposite the look direction: Rhino's Front looks along +Y from -Y.\n\tconst frontPosition = forward.clone().negate();\n\tconst rightPosition = right.clone();\n\n\treturn {\n\t\ttop: u.clone(),\n\t\tbottom: u.clone().negate(),\n\t\tfront: frontPosition.clone(),\n\t\tback: frontPosition.clone().negate(),\n\t\tright: rightPosition.clone(),\n\t\tleft: rightPosition.clone().negate(),\n\t\tiso: frontPosition\n\t\t\t.clone()\n\t\t\t.multiplyScalar(1.2)\n\t\t\t.add(rightPosition.clone())\n\t\t\t.add(u.clone())\n\t\t\t.normalize()\n\t};\n}\n\nexport function createCameraController(deps: CameraControllerDeps): CameraController {\n\tconst { scene, perspective, controls, onActiveCameraChange } = deps;\n\n\tconst up = (deps.up ?? perspective.up).clone().normalize();\n\tconst VIEW_DIRECTIONS = buildViewDirections(up);\n\n\tconst ortho = new THREE.OrthographicCamera(-1, 1, 1, -1, perspective.near, perspective.far);\n\tortho.up.copy(up);\n\n\tlet projection: CameraProjection = 'perspective';\n\tlet aspect = perspective.aspect;\n\n\tconst active = (): THREE.Camera => (projection === 'perspective' ? perspective : ortho);\n\n\t// Starting a new tween cancels any prior one — two loops would otherwise fight over the camera.\n\tlet activeTween: TweenHandle | null = null;\n\tconst cancelTween = () => {\n\t\tactiveTween?.cancel();\n\t\tactiveTween = null;\n\t};\n\n\t// Sizes the ortho frustum to match perspective's apparent size at the current distance.\n\tconst syncOrthoFrustum = () => {\n\t\t// Measure whichever camera is live: while ortho is active, OrbitControls moves ortho's\n\t\t// position (only its zoom changes), leaving perspective's distance stale.\n\t\tconst reference = projection === 'orthographic' ? ortho : perspective;\n\t\tconst distance = reference.position.distanceTo(controls.target);\n\t\tconst halfH = distance * Math.tan((perspective.fov * Math.PI) / 360);\n\t\tconst halfW = halfH * aspect;\n\t\tortho.left = -halfW;\n\t\tortho.right = halfW;\n\t\tortho.top = halfH;\n\t\tortho.bottom = -halfH;\n\t\tortho.near = perspective.near;\n\t\tortho.far = perspective.far;\n\t\tortho.updateProjectionMatrix();\n\t};\n\n\tconst setProjection = (next: CameraProjection) => {\n\t\tif (next === projection) return;\n\t\t// A tween mid-flight would keep lerping the OLD active camera after the swap.\n\t\tcancelTween();\n\n\t\tif (next === 'orthographic') {\n\t\t\tortho.position.copy(perspective.position);\n\t\t\tortho.up.copy(perspective.up);\n\t\t\tortho.lookAt(controls.target);\n\t\t\t// OrbitControls dollies ortho via `zoom`, not position — reset to 1 so a leftover zoom\n\t\t\t// from a prior 2D session doesn't double up with the freshly-derived frustum.\n\t\t\tortho.zoom = 1;\n\t\t\tsyncOrthoFrustum();\n\t\t} else {\n\t\t\t// Convert ortho zoom back to perspective DISTANCE (halfH / tan(fov/2)) — copying position\n\t\t\t// alone would discard any zooming done in 2D.\n\t\t\tconst halfH = (ortho.top - ortho.bottom) / (2 * ortho.zoom);\n\t\t\tconst distance = halfH / Math.tan((perspective.fov * Math.PI) / 360);\n\t\t\tconst direction = ortho.position.clone().sub(controls.target);\n\t\t\tif (direction.lengthSq() < 1e-12) direction.copy(up);\n\t\t\tdirection.normalize();\n\t\t\tperspective.position.copy(controls.target).add(direction.multiplyScalar(distance));\n\t\t}\n\n\t\tprojection = next;\n\t\tcontrols.object = active();\n\t\tcontrols.update();\n\t\tonActiveCameraChange(active());\n\t};\n\n\t// Positions the active camera along `direction` at the distance fitting `maxDim`, retargeting\n\t// controls at `center`. Ortho zoom resets and the frustum re-derives via syncOrthoFrustum —\n\t// position alone wouldn't change an orthographic view's apparent size.\n\tconst frame = (\n\t\tcenter: THREE.Vector3,\n\t\tmaxDim: number,\n\t\tdirection: THREE.Vector3,\n\t\tanimate: boolean\n\t) => {\n\t\tconst fov = perspective.fov * (Math.PI / 180);\n\t\tconst distance = (maxDim / (2 * Math.tan(fov / 2))) * 1.5;\n\n\t\tconst dir = nudgeOffPole(direction, up);\n\t\tconst toPosition = center.clone().add(dir.clone().multiplyScalar(distance));\n\n\t\tconst cam = active();\n\t\t// Reset zoom before re-deriving the frustum, else it multiplies in and defeats the fit.\n\t\tif (projection === 'orthographic') ortho.zoom = 1;\n\n\t\tcancelTween();\n\t\tif (animate) {\n\t\t\tactiveTween = animateMove(cam, controls, toPosition, center, () => {\n\t\t\t\tif (projection === 'orthographic') syncOrthoFrustum();\n\t\t\t});\n\t\t} else {\n\t\t\tcam.position.copy(toPosition);\n\t\t\tcontrols.target.copy(center);\n\t\t\tif (projection === 'orthographic') syncOrthoFrustum();\n\t\t\tcontrols.update();\n\t\t}\n\t};\n\n\tconst setViewDirection = (direction: THREE.Vector3, animate = true) => {\n\t\tconst box = computeContentBounds(scene);\n\t\tconst center = box.isEmpty() ? controls.target.clone() : box.getCenter(new THREE.Vector3());\n\t\tconst size = box.isEmpty() ? new THREE.Vector3(1, 1, 1) : box.getSize(new THREE.Vector3());\n\t\tconst maxDim = Math.max(size.x, size.y, size.z) || 1;\n\t\tframe(center, maxDim, direction, animate);\n\t};\n\n\tconst frameBounds = (box: THREE.Box3, animate = true) => {\n\t\tif (box.isEmpty()) return;\n\t\tconst center = box.getCenter(new THREE.Vector3());\n\t\tconst size = box.getSize(new THREE.Vector3());\n\t\tconst maxDim = Math.max(size.x, size.y, size.z) || 1;\n\t\t// Keep the user's current viewing direction; only the distance/target change.\n\t\tconst direction = active().position.clone().sub(controls.target);\n\t\tif (direction.lengthSq() < 1e-12) direction.copy(VIEW_DIRECTIONS.iso);\n\t\tframe(center, maxDim, direction.normalize(), animate);\n\t};\n\n\tconst setView = (preset: ViewPreset, animate = true) => {\n\t\tsetViewDirection(VIEW_DIRECTIONS[preset], animate);\n\t};\n\n\tconst setRotateEnabled = (enabled: boolean) => {\n\t\tcontrols.enableRotate = enabled;\n\t};\n\n\tconst updateAspect = (width: number, height: number) => {\n\t\taspect = height === 0 ? aspect : width / height;\n\t\tif (projection === 'orthographic') syncOrthoFrustum();\n\t};\n\n\treturn {\n\t\tgetActiveCamera: active,\n\t\tgetProjection: () => projection,\n\t\tsetProjection,\n\t\ttoggleProjection: () => {\n\t\t\tsetProjection(projection === 'perspective' ? 'orthographic' : 'perspective');\n\t\t\treturn projection;\n\t\t},\n\t\tsetView,\n\t\tsetViewDirection,\n\t\tframeBounds,\n\t\tsetRotateEnabled,\n\t\tisRotateEnabled: () => controls.enableRotate,\n\t\tupdateAspect,\n\t\tdispose: cancelTween\n\t};\n}\n\n/**\n * Nudges a top/bottom view direction a ~0.5° tilt off the up axis; other presets pass through\n * unchanged. Looking exactly down `up` is an OrbitControls singularity: camera direction coincides\n * with `camera.up`, azimuth is undefined, and the first drag snaps the view.\n *\n * At the pole, `camera.up` can't define roll, so the tilt direction does instead. Both poles lean\n * toward `-forward` to reproduce Rhino's convention (Top has +forward at screen-top; Bottom mirrors\n * about the horizontal axis, matching Rhino where the far side reads backwards too) — leaning the\n * poles opposite ways also mirrors correctly, but rolled 180° from Rhino.\n */\nfunction nudgeOffPole(dir: THREE.Vector3, up: THREE.Vector3): THREE.Vector3 {\n\tconst { up: u, forward } = buildUpBasis(up);\n\tconst d = dir.clone().normalize();\n\tif (Math.abs(d.dot(u)) < 0.9999) return dir;\n\n\tconst inPlane = forward.clone().negate();\n\n\tconst tilt = (0.5 * Math.PI) / 180;\n\treturn d\n\t\t.multiplyScalar(Math.cos(tilt))\n\t\t.add(inPlane.multiplyScalar(Math.sin(tilt)))\n\t\t.normalize();\n}\n\nconst easeOut = (t: number) => 1 - Math.pow(1 - t, 3);\n\n/** Handle to a running camera tween, so callers can stop it (new move, projection swap, teardown). */\ninterface TweenHandle {\n\tcancel(): void;\n}\n\n/** Tweens camera position + controls target; returns a cancel handle for teardown/preemption. */\nfunction animateMove(\n\tcamera: THREE.Camera,\n\tcontrols: OrbitControls,\n\ttoPosition: THREE.Vector3,\n\ttoTarget: THREE.Vector3,\n\tonTick: () => void,\n\tdurationMs = 250\n): TweenHandle {\n\tconst fromPosition = camera.position.clone();\n\tconst fromTarget = controls.target.clone();\n\tconst startTime = performance.now();\n\tlet rafId: number | null = null;\n\n\tconst tick = () => {\n\t\trafId = null;\n\t\tconst t = easeOut(Math.min((performance.now() - startTime) / durationMs, 1));\n\t\tcamera.position.lerpVectors(fromPosition, toPosition, t);\n\t\tcontrols.target.lerpVectors(fromTarget, toTarget, t);\n\t\tonTick();\n\t\tcontrols.update();\n\t\tif (t < 1) rafId = requestAnimationFrame(tick);\n\t};\n\n\trafId = requestAnimationFrame(tick);\n\n\treturn {\n\t\tcancel: () => {\n\t\t\tif (rafId !== null) {\n\t\t\t\tcancelAnimationFrame(rafId);\n\t\t\t\trafId = null;\n\t\t\t}\n\t\t}\n\t};\n}\n","import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js';\n\n// ============================================================================\n// Line geometry construction\n// ============================================================================\n\n// No cache here. An earlier WeakMap keyed per source BufferGeometry leaked ~400 live GPU entries\n// where 8 were expected — entries never vanished with their source — and measured 0/80 hits in a\n// real scrubbing loop. Every overlay builds and owns its own line geometry.\n\nexport interface EdgeGeometryEntry {\n\tgeometry: LineSegmentsGeometry;\n\tsegmentCount: number;\n\t/** {@link SPACING_PERCENTILE} quantile of segment length; drives the density fade in overlay.ts. */\n\tedgeSpacing: number;\n}\n\n// A low quantile (not mean) tracks the fine detail: real parts mix a few long silhouette edges\n// with many short ones at wildly different scale (e.g. 1mm laminations on a 10m part) — an\n// average would sit between the two and never trigger the fade for either.\nconst SPACING_PERCENTILE = 0.15;\n\n// Stride sampling keeps this O(1) on millions of segments.\nconst SPACING_SAMPLE_LIMIT = 4096;\n\nfunction edgeSpacingOf(segments: Float32Array): number {\n\tconst segmentCount = Math.floor(segments.length / 6);\n\tif (segmentCount === 0) return Infinity;\n\n\tconst stride = Math.max(1, Math.ceil(segmentCount / SPACING_SAMPLE_LIMIT));\n\tconst lengths: number[] = [];\n\tfor (let s = 0; s < segmentCount; s += stride) {\n\t\tconst i = s * 6;\n\t\tconst length = Math.hypot(\n\t\t\tsegments[i + 3] - segments[i],\n\t\t\tsegments[i + 4] - segments[i + 1],\n\t\t\tsegments[i + 5] - segments[i + 2]\n\t\t);\n\t\tif (length > 0) lengths.push(length);\n\t}\n\tif (lengths.length === 0) return Infinity;\n\n\tlengths.sort((a, b) => a - b);\n\treturn lengths[Math.min(lengths.length - 1, Math.floor(lengths.length * SPACING_PERCENTILE))]!;\n}\n\n// LineSegmentsGeometry adopts `segments` as its backing store without copying — treat it as\n// read-only from here on.\nexport function buildLineGeometry(segments: Float32Array): EdgeGeometryEntry {\n\tconst geometry = new LineSegmentsGeometry();\n\tgeometry.setPositions(segments);\n\treturn {\n\t\tgeometry,\n\t\tsegmentCount: segments.length / 6,\n\t\tedgeSpacing: edgeSpacingOf(segments)\n\t};\n}\n","/**\n * Dependency-free crease/boundary edge extraction — the hot core behind `addEdges`. Semantically\n * a drop-in for `THREE.EdgesGeometry(geometry, angle)` (same welding, crease test, boundary\n * handling, 3+-face quirks), but operates on raw typed arrays with numeric hashing instead of\n * three's per-vertex string keys, for speed and Worker portability — see the no-outer-captures\n * constraint on {@link extractEdgeSegments} itself.\n */\n\n/** Largest vertex count the fast path takes; callers fall back to `THREE.EdgesGeometry` above it. */\nexport const MAX_EXTRACT_VERTICES = 0x4000000; // 2^26\n\n/**\n * @param index - Triangle indices, or null for non-indexed soup.\n * @returns `segments`: endpoint pairs, same layout as `EdgesGeometry.attributes.position.array`.\n * `faces`: per segment, the triangle it came from, so a merged mesh can drop a hidden member's\n * edges without extracting again.\n * @throws If `positions` holds ≥ 2^26 vertices ({@link MAX_EXTRACT_VERTICES}) — callers fall\n * back to `THREE.EdgesGeometry`.\n */\nexport function extractEdgeSegments(\n\tpositions: Float32Array,\n\tindex: Uint32Array | Uint16Array | null,\n\tthresholdAngleDeg: number\n): { segments: Float32Array; faces: Uint32Array } {\n\t// No outer captures besides Math — this function is stringified via toString() to run inside\n\t// a Worker ({@link edgeExtractWorkerSource}). Don't reference anything outside this body.\n\tconst PRECISION = 1e4; // same quantization grid as THREE.EdgesGeometry\n\tconst ID_BITS = 0x4000000; // MAX_EXTRACT_VERTICES, restated: this body may not capture it\n\tconst thresholdDot = Math.cos((Math.PI / 180) * thresholdAngleDeg);\n\n\tconst vertexCount = positions.length / 3;\n\tif (vertexCount >= ID_BITS) {\n\t\tthrow new Error(`extractEdgeSegments: ${vertexCount} vertices exceeds 2^26 limit`);\n\t}\n\n\t// --- Weld vertices on the quantization grid → canonical id per vertex -------------------\n\t// Rounded coords stay float64 (huge coordinates stay exact where int32 would overflow); only\n\t// the hash truncates to int32 — equality always compares the exact float64 values.\n\tconst quantX = new Float64Array(vertexCount);\n\tconst quantY = new Float64Array(vertexCount);\n\tconst quantZ = new Float64Array(vertexCount);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tquantX[v] = Math.round(positions[3 * v] * PRECISION);\n\t\tquantY[v] = Math.round(positions[3 * v + 1] * PRECISION);\n\t\tquantZ[v] = Math.round(positions[3 * v + 2] * PRECISION);\n\t}\n\n\t// Open-addressed table (linear probing): slot → first vertex id seen at that grid point.\n\tlet capacity = 16;\n\twhile (capacity < vertexCount * 2) capacity <<= 1;\n\tconst mask = capacity - 1;\n\tconst table = new Int32Array(capacity).fill(-1);\n\tconst canonical = new Int32Array(vertexCount);\n\tfor (let v = 0; v < vertexCount; v++) {\n\t\tlet slot =\n\t\t\t(Math.imul(quantX[v] | 0, 73856093) ^\n\t\t\t\tMath.imul(quantY[v] | 0, 19349663) ^\n\t\t\t\tMath.imul(quantZ[v] | 0, 83492791)) &\n\t\t\tmask;\n\t\tfor (;;) {\n\t\t\tconst existing = table[slot];\n\t\t\tif (existing === -1) {\n\t\t\t\ttable[slot] = v;\n\t\t\t\tcanonical[v] = v;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tquantX[existing] === quantX[v] &&\n\t\t\t\tquantY[existing] === quantY[v] &&\n\t\t\t\tquantZ[existing] === quantZ[v]\n\t\t\t) {\n\t\t\t\tcanonical[v] = existing;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tslot = (slot + 1) & mask;\n\t\t}\n\t}\n\n\t// --- Growable segment output ------------------------------------------------------------\n\tlet out = new Float32Array(6 * 1024);\n\tlet outLength = 0;\n\tlet outFaces = new Uint32Array(1024);\n\tconst emit = (i0: number, i1: number, face: number): void => {\n\t\tif (outLength + 6 > out.length) {\n\t\t\tconst grown = new Float32Array(out.length * 2);\n\t\t\tgrown.set(out);\n\t\t\tout = grown;\n\t\t\tconst grownFaces = new Uint32Array(out.length / 6);\n\t\t\tgrownFaces.set(outFaces);\n\t\t\toutFaces = grownFaces;\n\t\t}\n\t\toutFaces[outLength / 6] = face;\n\t\tout[outLength++] = positions[3 * i0];\n\t\tout[outLength++] = positions[3 * i0 + 1];\n\t\tout[outLength++] = positions[3 * i0 + 2];\n\t\tout[outLength++] = positions[3 * i1];\n\t\tout[outLength++] = positions[3 * i1 + 1];\n\t\tout[outLength++] = positions[3 * i1 + 2];\n\t};\n\n\t// --- Directed edges grouped by their canonical start vertex (CSR) -----------------------\n\t// Each row holds one cell per directed-edge occurrence leaving that vertex (about six), so a\n\t// lookup scans a handful of ints. A Map keyed per directed edge cost ~12M entries on a 4M-tri\n\t// mesh and dominated extraction (6.5 s against 0.4 s).\n\tconst triCount = (index ? index.length : vertexCount) / 3;\n\tconst rowStart = new Int32Array(vertexCount + 1);\n\tfor (let t = 0; t < triCount; t++) {\n\t\tconst a = canonical[index ? index[3 * t] : 3 * t];\n\t\tconst b = canonical[index ? index[3 * t + 1] : 3 * t + 1];\n\t\tconst c = canonical[index ? index[3 * t + 2] : 3 * t + 2];\n\t\tif (a === b || b === c || c === a) continue;\n\t\trowStart[a + 1]++;\n\t\trowStart[b + 1]++;\n\t\trowStart[c + 1]++;\n\t}\n\tfor (let v = 0; v < vertexCount; v++) rowStart[v + 1] += rowStart[v];\n\tconst cellTo = new Int32Array(rowStart[vertexCount]);\n\t{\n\t\tconst cursor = rowStart.slice(0, vertexCount);\n\t\tfor (let t = 0; t < triCount; t++) {\n\t\t\tconst a = canonical[index ? index[3 * t] : 3 * t];\n\t\t\tconst b = canonical[index ? index[3 * t + 1] : 3 * t + 1];\n\t\t\tconst c = canonical[index ? index[3 * t + 2] : 3 * t + 2];\n\t\t\tif (a === b || b === c || c === a) continue;\n\t\t\tcellTo[cursor[a]++] = b;\n\t\t\tcellTo[cursor[b]++] = c;\n\t\t\tcellTo[cursor[c]++] = a;\n\t\t}\n\t}\n\t// A directed edge's state lives in the first cell of its row that names its end vertex:\n\t// UNSEEN, MATCHED, or the pending slot it registered.\n\tconst UNSEEN = -2;\n\tconst MATCHED = -1;\n\tconst cellState = new Int32Array(cellTo.length).fill(UNSEEN);\n\tconst findCell = (from: number, to: number): number => {\n\t\tfor (let k = rowStart[from], end = rowStart[from + 1]; k < end; k++) {\n\t\t\tif (cellTo[k] === to) return k;\n\t\t}\n\t\treturn -1;\n\t};\n\n\tconst cornerOf = (t: number, k: number): number => (index ? index[3 * t + k] : 3 * t + k);\n\n\t// Face normal, computed exactly as Triangle.getNormal: normalize((c-b) × (a-b)). Pending edges\n\t// store only their corner and recompute this on a match: identical values, and 8 bytes per entry\n\t// instead of 40, which was ~480 MB at 8M triangles.\n\tconst normal = new Float64Array(3);\n\tconst faceNormal = (t: number): void => {\n\t\tconst i0 = cornerOf(t, 0);\n\t\tconst i1 = cornerOf(t, 1);\n\t\tconst i2 = cornerOf(t, 2);\n\t\tconst e0x = positions[3 * i2] - positions[3 * i1];\n\t\tconst e0y = positions[3 * i2 + 1] - positions[3 * i1 + 1];\n\t\tconst e0z = positions[3 * i2 + 2] - positions[3 * i1 + 2];\n\t\tconst e1x = positions[3 * i0] - positions[3 * i1];\n\t\tconst e1y = positions[3 * i0 + 1] - positions[3 * i1 + 1];\n\t\tconst e1z = positions[3 * i0 + 2] - positions[3 * i1 + 2];\n\t\tlet nx = e0y * e1z - e0z * e1y;\n\t\tlet ny = e0z * e1x - e0x * e1z;\n\t\tlet nz = e0x * e1y - e0y * e1x;\n\t\tconst lengthSq = nx * nx + ny * ny + nz * nz;\n\t\tif (lengthSq > 0) {\n\t\t\tconst inverseLength = 1 / Math.sqrt(lengthSq);\n\t\t\tnx *= inverseLength;\n\t\t\tny *= inverseLength;\n\t\t\tnz *= inverseLength;\n\t\t} else {\n\t\t\tnx = 0;\n\t\t\tny = 0;\n\t\t\tnz = 0;\n\t\t}\n\t\tnormal[0] = nx;\n\t\tnormal[1] = ny;\n\t\tnormal[2] = nz;\n\t};\n\n\t// A pending edge is the corner it starts at (3t + j; it runs to corner j+1 of face t) and the\n\t// cell holding its state. Most edges pair up, so about half the cells ever register; sized to\n\t// the mesh because a fixed start dominated small meshes, which come thousands to a batch.\n\tlet pendingCount = 0;\n\tlet pendingCorners = new Uint32Array(Math.max(16, cellTo.length >> 1));\n\tlet pendingCells = new Int32Array(pendingCorners.length);\n\tconst growPending = (): void => {\n\t\tconst corners = new Uint32Array(pendingCorners.length * 2);\n\t\tcorners.set(pendingCorners);\n\t\tpendingCorners = corners;\n\t\tconst cells = new Int32Array(corners.length);\n\t\tcells.set(pendingCells);\n\t\tpendingCells = cells;\n\t};\n\n\t// --- Walk triangles, pairing opposite-winding edges -------------------------------------\n\t// Mirrors EdgesGeometry: a directed edge a→b matches a pending b→a; on match the segment is\n\t// kept iff the face normals differ beyond the threshold, and the pending entry is marked\n\t// MATCHED (never re-registered as a→b) so a third face b→a registers anew. Pending entries\n\t// left at the end are boundary edges and always emitted.\n\tfor (let t = 0; t < triCount; t++) {\n\t\tconst i0 = cornerOf(t, 0);\n\t\tconst i1 = cornerOf(t, 1);\n\t\tconst i2 = cornerOf(t, 2);\n\t\tconst a = canonical[i0];\n\t\tconst b = canonical[i1];\n\t\tconst c = canonical[i2];\n\n\t\t// Degenerate on the quantization grid — skip, as EdgesGeometry does.\n\t\tif (a === b || b === c || c === a) continue;\n\n\t\tlet haveNormal = false;\n\t\tlet nx = 0;\n\t\tlet ny = 0;\n\t\tlet nz = 0;\n\t\tfor (let j = 0; j < 3; j++) {\n\t\t\tconst fromCanonical = j === 0 ? a : j === 1 ? b : c;\n\t\t\tconst toCanonical = j === 0 ? b : j === 1 ? c : a;\n\n\t\t\tconst reverseCell = findCell(toCanonical, fromCanonical);\n\t\t\tconst reverseSlot = reverseCell === -1 ? UNSEEN : cellState[reverseCell];\n\t\t\tif (reverseSlot >= 0) {\n\t\t\t\tif (!haveNormal) {\n\t\t\t\t\tfaceNormal(t);\n\t\t\t\t\tnx = normal[0];\n\t\t\t\t\tny = normal[1];\n\t\t\t\t\tnz = normal[2];\n\t\t\t\t\thaveNormal = true;\n\t\t\t\t}\n\t\t\t\tfaceNormal(Math.floor(pendingCorners[reverseSlot] / 3));\n\t\t\t\tconst dot = nx * normal[0] + ny * normal[1] + nz * normal[2];\n\t\t\t\tif (dot <= thresholdDot) {\n\t\t\t\t\temit(j === 0 ? i0 : j === 1 ? i1 : i2, j === 0 ? i1 : j === 1 ? i2 : i0, t);\n\t\t\t\t}\n\t\t\t\tcellState[reverseCell] = MATCHED;\n\t\t\t} else {\n\t\t\t\tconst forwardCell = findCell(fromCanonical, toCanonical);\n\t\t\t\tif (cellState[forwardCell] === UNSEEN) {\n\t\t\t\t\tif (pendingCount === pendingCorners.length) growPending();\n\t\t\t\t\tconst slot = pendingCount++;\n\t\t\t\t\tcellState[forwardCell] = slot;\n\t\t\t\t\tpendingCorners[slot] = 3 * t + j;\n\t\t\t\t\tpendingCells[slot] = forwardCell;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Unmatched edges are boundaries — always kept, in registration order ----------------\n\tfor (let slot = 0; slot < pendingCount; slot++) {\n\t\tif (cellState[pendingCells[slot]] !== slot) continue;\n\t\tconst corner = pendingCorners[slot];\n\t\tconst t = Math.floor(corner / 3);\n\t\tconst j = corner - 3 * t;\n\t\temit(cornerOf(t, j), cornerOf(t, j === 2 ? 0 : j + 1), t);\n\t}\n\n\treturn { segments: out.slice(0, outLength), faces: outFaces.slice(0, outLength / 6) };\n}\n\n/**\n * Worker source running {@link extractEdgeSegments} off the main thread. Protocol: receives\n * `{id, positions, index, thresholdAngle}`, replies `{id, segments, faces}` (buffers transferred) or\n * `{id, error}`.\n *\n * Relies on `extractEdgeSegments` stringifying to standalone code — guarded by a unit test that\n * evals this source in isolation.\n */\nexport function edgeExtractWorkerSource(): string {\n\treturn [\n\t\t`const extract = ${extractEdgeSegments.toString()};`,\n\t\t`self.onmessage = (event) => {`,\n\t\t` const { id, positions, index, thresholdAngle } = event.data;`,\n\t\t` try {`,\n\t\t` const { segments, faces } = extract(positions, index, thresholdAngle);`,\n\t\t` self.postMessage({ id, segments, faces }, [segments.buffer, faces.buffer]);`,\n\t\t` } catch (error) {`,\n\t\t` self.postMessage({ id, error: String((error && error.message) || error) });`,\n\t\t` }`,\n\t\t`};`\n\t].join('\\n');\n}\n","import * as THREE from 'three';\n\n/** Crisp boundary/crease edges overlaid on meshes. See the layer README for depth/perf strategy. */\nexport interface EdgeOptions {\n\t/** Default: each overlay derives its color from its own mesh's material (see {@link DEFAULT_EDGE_COLOR}). */\n\tcolor?: THREE.ColorRepresentation;\n\t/** How far to darken the derived edge color toward black, 0-1 (default 0.75). No-op when `color` is set. */\n\tdarken?: number;\n\t/** Edge thickness in CSS px. Default 1.5. */\n\twidth?: number;\n\t/** Crease angle in degrees; an edge survives only where its two faces differ by more. Default 44. */\n\tthresholdAngle?: number;\n\t/**\n\t * Fade an overlay out as its own edges crowd together on screen (default true). Edges draw at\n\t * constant pixel width, so dense edges (e.g. millimetre-pitch laminations on sheet goods) merge\n\t * into a dark smear at normal zoom; fading by edge density rather than mesh size catches that\n\t * while leaving sparsely-edged geometry fully drawn.\n\t */\n\tdistanceFade?: boolean;\n\t/**\n\t * Skip meshes above this triangle count entirely (default 8M). Extraction time and memory are\n\t * linear in triangles: 8M is about 0.7 s in the worker and a few hundred MB of scratch, and past it\n\t * the screen-space fallback approximates the look at constant cost. Skipped meshes are tagged\n\t * `userData.edgesSkipped = 'triangle-cap'`.\n\t */\n\tmaxTriangles?: number;\n\t/**\n\t * Above this many extracted segments (default 2M), an overlay drops the distance fade and renders\n\t * opaque instead — millions of blended fat-line quads are a fill-rate cliff; opaque ones aren't.\n\t */\n\tmaxSegments?: number;\n\t/**\n\t * Attach at most this many overlays per apply (default 1500); past it every remaining mesh is\n\t * skipped and tagged `userData.edgesSkipped = 'overlay-budget'`, which switches on the\n\t * screen-space fallback.\n\t *\n\t * This is the count cap, and it catches what {@link maxTriangles} structurally cannot. That one\n\t * is per mesh, so it only ever sees one mesh at a time and a 20-triangle mesh always clears it —\n\t * yet an IFC model arrives as thousands of such meshes, and each overlay is a separate\n\t * `LineSegments2` with its own draw call and per-frame resolution uniform. Cost tracks the number\n\t * of overlays, not the triangles inside any one of them: measured on a 6043-mesh IFC house, the\n\t * overlays alone were 60% of a 99ms frame.\n\t */\n\tmaxOverlays?: number;\n}\n\n/** Tag on edge overlays so pick/fit/clear logic can recognize and skip or dispose them. */\nexport const EDGE_USERDATA_KIND = 'edge-overlay';\n\n/** `userData.edgesSkipped` value; see {@link EdgeOptions.maxTriangles}. */\nexport const EDGES_SKIPPED_TRIANGLE_CAP = 'triangle-cap';\n\n/** `userData.edgesSkipped` value; see {@link EdgeOptions.maxOverlays}. */\nexport const EDGES_SKIPPED_OVERLAY_BUDGET = 'overlay-budget';\n\nexport const DEFAULT_EDGE_COLOR = 0x222222;\nconst DEFAULT_EDGE_WIDTH = 1.5;\nconst DEFAULT_THRESHOLD_ANGLE = 44;\nconst DEFAULT_DARKEN = 0.75;\nconst DEFAULT_MAX_TRIANGLES = 8_000_000;\nconst DEFAULT_MAX_SEGMENTS = 2_000_000;\n// Sits above the few hundred objects a Grasshopper solve produces (which must keep real overlays)\n// and below the thousands an imported building model does. At the measured ~6µs per object per\n// frame under orbit, 1500 overlays is ~9ms — already most of a 60fps frame, and the point where\n// the constant-cost screen-space pass wins outright.\nconst DEFAULT_MAX_OVERLAYS = 1_500;\n\n// Below this triangle count a worker round-trip would cost more than the extraction itself, so it\n// runs inline even on the async path.\nexport const INLINE_TRIANGLE_BUDGET = 25_000;\n\n// Fade band as mean on-screen gap between neighbouring edges, in px: opaque at/above\n// FADE_START_PX, gone at/below FADE_END_PX, linear between. Density-based rather than\n// mesh/bounding-sphere-based, because a big part with sub-pixel edge spacing still draws every\n// line at full width — the old bounding-sphere rule missed that (the sphere still covers most of\n// the viewport). Band sits just above 1px, where constant-width lines start visibly overlapping.\nexport const FADE_START_PX = 4;\nexport const FADE_END_PX = 1;\n\n// Units-only pull-forward, deliberately no slope (factor) term: a slope term scales with the\n// polygon's dZ/dpixel, huge at grazing angles, and applied to surfaces (the old strategy) it\n// pushed grazing faces back further than the mm-scale gaps between stacked parts — geometry\n// behind a wall then won the depth test and bled through the wall's own edges. A fixed\n// quantization-step bias on the lines instead lifts an edge off its own surface without reaching\n// across a gap to a neighbouring part.\n//\n// This bias is only safe as long as a depth ULP stays small, which is what near-plane.ts's dynamic\n// near-plane fitter guarantees. Weakening that fit makes this bias start to bleed.\nexport const EDGE_OFFSET_FACTOR = 0;\nexport const EDGE_OFFSET_UNITS = -1; // negative = toward the camera\n\nexport interface ResolvedOptions {\n\tforcedColor: THREE.Color | null;\n\tdarken: number;\n\twidth: number;\n\tthresholdAngle: number;\n\tdistanceFade: boolean;\n\tmaxTriangles: number;\n\tmaxSegments: number;\n\tmaxOverlays: number;\n}\n\nexport function resolveOptions(options: EdgeOptions): ResolvedOptions {\n\treturn {\n\t\tforcedColor: options.color != null ? new THREE.Color(options.color) : null,\n\t\tdarken: THREE.MathUtils.clamp(options.darken ?? DEFAULT_DARKEN, 0, 1),\n\t\twidth: options.width ?? DEFAULT_EDGE_WIDTH,\n\t\tthresholdAngle: options.thresholdAngle ?? DEFAULT_THRESHOLD_ANGLE,\n\t\tdistanceFade: options.distanceFade ?? true,\n\t\tmaxTriangles: options.maxTriangles ?? DEFAULT_MAX_TRIANGLES,\n\t\tmaxSegments: options.maxSegments ?? DEFAULT_MAX_SEGMENTS,\n\t\tmaxOverlays: options.maxOverlays ?? DEFAULT_MAX_OVERLAYS\n\t};\n}\n","import * as THREE from 'three';\n\nimport {\n\tMAX_EXTRACT_VERTICES,\n\tedgeExtractWorkerSource,\n\textractEdgeSegments\n} from '../edge-extract.js';\nimport { INLINE_TRIANGLE_BUDGET } from './options.js';\n\n// ============================================================================\n// Segment extraction — fast path, worker offload\n// ============================================================================\n\nexport function triangleCountOf(geometry: THREE.BufferGeometry): number {\n\tconst position = geometry.getAttribute('position');\n\tif (!position) return 0;\n\treturn (geometry.index ? geometry.index.count : position.count) / 3;\n}\n\n/** `faces` maps each segment to its source triangle; null from the `EdgesGeometry` fallback. */\nexport interface ExtractedEdges {\n\tsegments: Float32Array;\n\tfaces: Uint32Array | null;\n}\n\ninterface FastPathData {\n\tpositions: Float32Array;\n\tindex: Uint32Array | Uint16Array | null;\n\t/** Bumped by `needsUpdate`: an array edited in place keeps its identity but not its edges. */\n\tversion: string;\n}\n\n// Fast extractor needs plain non-interleaved float32 xyz + typed index arrays. Anything exotic\n// (interleaved, float64, morphed) falls back to THREE.EdgesGeometry instead.\nfunction fastPathData(geometry: THREE.BufferGeometry): FastPathData | null {\n\tconst position = geometry.getAttribute('position');\n\tif (\n\t\t!position ||\n\t\t(position as THREE.InterleavedBufferAttribute).isInterleavedBufferAttribute ||\n\t\tposition.itemSize !== 3 ||\n\t\t!(position.array instanceof Float32Array) ||\n\t\tposition.count >= MAX_EXTRACT_VERTICES\n\t) {\n\t\treturn null;\n\t}\n\tconst index = geometry.index;\n\tif (index && !(index.array instanceof Uint32Array) && !(index.array instanceof Uint16Array)) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tpositions: position.array,\n\t\tindex: index ? (index.array as Uint32Array | Uint16Array) : null,\n\t\tversion: `${(position as THREE.BufferAttribute).version}:${index?.version ?? 0}`\n\t};\n}\n\n// FNV-1a over sampled head+tail words of position/index plus lengths and crease angle. Sampling\n// keeps this ~free at millions of vertices; a collision needs identical lengths AND sampled regions.\nfunction contentKey(data: FastPathData, thresholdAngle: number): string {\n\tconst SAMPLE_WORDS = 4096;\n\tlet hash = 0x811c9dc5;\n\tconst mix = (word: number): void => {\n\t\thash ^= word;\n\t\thash = Math.imul(hash, 0x01000193);\n\t};\n\n\tconst words = new Uint32Array(\n\t\tdata.positions.buffer,\n\t\tdata.positions.byteOffset,\n\t\tdata.positions.length\n\t);\n\tconst head = Math.min(SAMPLE_WORDS, words.length);\n\tfor (let i = 0; i < head; i++) mix(words[i]);\n\tfor (let i = Math.max(head, words.length - SAMPLE_WORDS); i < words.length; i++) mix(words[i]);\n\n\tlet indexLength = 0;\n\tif (data.index) {\n\t\tindexLength = data.index.length;\n\t\tconst headIndex = Math.min(SAMPLE_WORDS, indexLength);\n\t\tfor (let i = 0; i < headIndex; i++) mix(data.index[i]);\n\t\tfor (let i = Math.max(headIndex, indexLength - SAMPLE_WORDS); i < indexLength; i++) {\n\t\t\tmix(data.index[i]);\n\t\t}\n\t}\n\n\treturn `${thresholdAngle}:${data.positions.length}:${indexLength}:${hash >>> 0}`;\n}\n\nfunction extractViaThree(geometry: THREE.BufferGeometry, thresholdAngle: number): ExtractedEdges {\n\tconst edges = new THREE.EdgesGeometry(geometry, thresholdAngle);\n\tconst positions = edges.attributes.position\n\t\t? (edges.attributes.position.array as Float32Array)\n\t\t: new Float32Array(0);\n\tedges.dispose(); // frees only GPU-side state; the CPU array is the return value\n\treturn { segments: positions, faces: null };\n}\n\n// A re-solve whose geometry the parser served from its cache hands back the very same position and\n// index arrays, so identity is an exact key that needs no hashing, and entries go with the arrays.\n// Results are shared by every overlay built from them: read-only, like the arrays they came from.\n// Only the latest version per angle is kept: an array edited in place never returns to an old one.\nconst extracted = new WeakMap<\n\tFloat32Array,\n\tWeakMap<object, Map<number, { version: string; edges: ExtractedEdges }>>\n>();\nconst NO_INDEX = {};\n\nfunction recall(data: FastPathData, thresholdAngle: number): ExtractedEdges | undefined {\n\tconst known = extracted\n\t\t.get(data.positions)\n\t\t?.get(data.index ?? NO_INDEX)\n\t\t?.get(thresholdAngle);\n\treturn known?.version === data.version ? known.edges : undefined;\n}\n\nfunction remember(\n\tdata: FastPathData,\n\tthresholdAngle: number,\n\tedges: ExtractedEdges\n): ExtractedEdges {\n\tlet byIndex = extracted.get(data.positions);\n\tif (!byIndex) extracted.set(data.positions, (byIndex = new WeakMap()));\n\tlet byAngle = byIndex.get(data.index ?? NO_INDEX);\n\tif (!byAngle) byIndex.set(data.index ?? NO_INDEX, (byAngle = new Map()));\n\tbyAngle.set(thresholdAngle, { version: data.version, edges });\n\treturn edges;\n}\n\nexport function extractSegmentsSync(\n\tgeometry: THREE.BufferGeometry,\n\tthresholdAngle: number\n): ExtractedEdges {\n\tconst data = fastPathData(geometry);\n\tif (!data) return extractViaThree(geometry, thresholdAngle);\n\n\treturn (\n\t\trecall(data, thresholdAngle) ??\n\t\tremember(data, thresholdAngle, extractEdgeSegments(data.positions, data.index, thresholdAngle))\n\t);\n}\n\n// --- Worker offload -----------------------------------------------------------------------------\n\ninterface PendingRequest {\n\tresolve: (edges: ExtractedEdges) => void;\n\treject: (error: Error) => void;\n}\n\nlet extractionWorker: Worker | null | undefined; // undefined = not yet tried, null = unavailable\nconst pendingRequests = new Map<number, PendingRequest>();\nlet nextRequestId = 1;\n\nfunction getExtractionWorker(): Worker | null {\n\tif (extractionWorker !== undefined) return extractionWorker;\n\tif (\n\t\ttypeof Worker === 'undefined' ||\n\t\ttypeof Blob === 'undefined' ||\n\t\ttypeof URL === 'undefined' ||\n\t\ttypeof URL.createObjectURL !== 'function'\n\t) {\n\t\textractionWorker = null;\n\t\treturn null;\n\t}\n\ttry {\n\t\t// Blob URL keeps this bundler-agnostic (no `new Worker(new URL(...))`). Never revoked:\n\t\t// revoking before the worker finishes fetching is unspecified behavior, and this is a\n\t\t// process-lifetime singleton.\n\t\tconst url = URL.createObjectURL(\n\t\t\tnew Blob([edgeExtractWorkerSource()], { type: 'text/javascript' })\n\t\t);\n\t\tconst worker = new Worker(url);\n\t\tworker.onmessage = (event: MessageEvent) => {\n\t\t\tconst { id, segments, faces, error } = event.data as {\n\t\t\t\tid: number;\n\t\t\t\tsegments?: Float32Array;\n\t\t\t\tfaces?: Uint32Array;\n\t\t\t\terror?: string;\n\t\t\t};\n\t\t\tconst pending = pendingRequests.get(id);\n\t\t\tif (!pending) return;\n\t\t\tpendingRequests.delete(id);\n\t\t\tif (segments) pending.resolve({ segments, faces: faces ?? null });\n\t\t\telse pending.reject(new Error(error ?? 'edge extraction failed in worker'));\n\t\t};\n\t\tworker.onerror = () => {\n\t\t\t// Worker died (CSP, OOM, script error): fail everything in flight and never retry the\n\t\t\t// worker this session — callers fall back to inline extraction.\n\t\t\tfor (const pending of pendingRequests.values()) {\n\t\t\t\tpending.reject(new Error('edge extraction worker crashed'));\n\t\t\t}\n\t\t\tpendingRequests.clear();\n\t\t\tworker.terminate();\n\t\t\textractionWorker = null;\n\t\t};\n\t\textractionWorker = worker;\n\t} catch {\n\t\textractionWorker = null;\n\t}\n\treturn extractionWorker;\n}\n\nfunction extractInWorker(\n\tworker: Worker,\n\tdata: FastPathData,\n\tthresholdAngle: number\n): Promise<ExtractedEdges> {\n\treturn new Promise<ExtractedEdges>((resolve, reject) => {\n\t\tconst id = nextRequestId++;\n\t\tpendingRequests.set(id, { resolve, reject });\n\t\t// Copy before transfer — the originals back the render geometry.\n\t\tconst positions = data.positions.slice();\n\t\tconst index = data.index ? data.index.slice() : null;\n\t\tconst transfer: Transferable[] = [positions.buffer];\n\t\tif (index) transfer.push(index.buffer);\n\t\tworker.postMessage({ id, positions, index, thresholdAngle }, transfer);\n\t});\n}\n\n// In-flight dedupe: meshes with identical content share one worker round-trip.\nconst inFlightExtractions = new Map<string, Promise<ExtractedEdges>>();\n\nexport function extractSegmentsAsync(\n\tgeometry: THREE.BufferGeometry,\n\tthresholdAngle: number\n): Promise<ExtractedEdges> {\n\tconst data = fastPathData(geometry);\n\tif (!data || triangleCountOf(geometry) < INLINE_TRIANGLE_BUDGET) {\n\t\treturn Promise.resolve(extractSegmentsSync(geometry, thresholdAngle));\n\t}\n\n\tconst known = recall(data, thresholdAngle);\n\tif (known) return Promise.resolve(known);\n\n\tconst key = contentKey(data, thresholdAngle);\n\tconst inFlight = inFlightExtractions.get(key);\n\tif (inFlight) return inFlight;\n\n\tconst worker = getExtractionWorker();\n\tif (!worker) return Promise.resolve(extractSegmentsSync(geometry, thresholdAngle));\n\n\tconst request = extractInWorker(worker, data, thresholdAngle)\n\t\t.catch(() => extractEdgeSegments(data.positions, data.index, thresholdAngle))\n\t\t.then((edges) => remember(data, thresholdAngle, edges))\n\t\t.finally(() => {\n\t\t\tinFlightExtractions.delete(key);\n\t\t});\n\tinFlightExtractions.set(key, request);\n\treturn request;\n}\n","import * as THREE from 'three';\n\nimport {\n\tclearMemberHighlight,\n\tsetMemberHighlight,\n\ttype HighlightKind\n} from '../../shared/index.js';\n\n// ============================================================================\n// Picking inside a merged mesh\n// ============================================================================\n//\n// Merging by material collapses thousands of source objects into a handful of THREE.Mesh objects,\n// which is what keeps an IFC-scale model renderable. The cost is that a raycast can only name the\n// merged mesh, not the wall the user actually clicked — so selection and the metadata panel would\n// both address the whole group.\n//\n// `userData.members` carries each source object's window into the merged index buffer\n// (`finalizeMergedMesh`), and the raycast reports which face it hit. Face → index range → member\n// recovers the original object, so highlight and metadata stay per-object even though rendering is\n// per-group.\n\n/** Mirror of the record `parse/webdisplay/batch/merge.ts` stamps into `userData.members`. */\nexport interface PickableMember {\n\ttrackingKey?: string;\n\tname: string;\n\tlayer: string;\n\tmetadata: Record<string, string>;\n\tindexStart?: number;\n\tindexCount?: number;\n}\n\nexport interface ResolvedMember {\n\tmember: PickableMember;\n\tindex: number;\n}\n\n/** Members of a merged mesh, or null for an ordinary one. */\nexport function membersOf(object: THREE.Object3D): PickableMember[] | null {\n\tconst members = object.userData?.members as PickableMember[] | undefined;\n\treturn Array.isArray(members) && members.length > 0 ? members : null;\n}\n\n/**\n * The source object a raycast hit landed on, or null when this isn't a merged mesh or the hit\n * carries no face (points/lines, or a member written without an index window).\n *\n * Members are laid out in ascending, contiguous index order, so this is a binary search rather\n * than a scan — a merged group can hold thousands of members and picking runs per click.\n */\nexport function resolveHitMember(hit: THREE.Intersection): ResolvedMember | null {\n\tconst members = membersOf(hit.object);\n\tif (!members || hit.faceIndex == null) return null;\n\n\t// Three reports the face ordinal; each face is 3 indices into the merged index buffer.\n\tconst hitIndex = hit.faceIndex * 3;\n\n\tlet low = 0;\n\tlet high = members.length - 1;\n\twhile (low <= high) {\n\t\tconst mid = (low + high) >> 1;\n\t\tconst member = members[mid]!;\n\t\tif (member.indexStart == null || member.indexCount == null) return null;\n\t\tif (hitIndex < member.indexStart) high = mid - 1;\n\t\telse if (hitIndex >= member.indexStart + member.indexCount) low = mid + 1;\n\t\telse return { member, index: mid };\n\t}\n\treturn null;\n}\n\n/**\n * World-space bounds of one member inside a merged mesh, walking only that member's index window.\n * `Box3.setFromObject` would return the whole merged group's bounds.\n */\nexport function memberBounds(mesh: THREE.Mesh, member: PickableMember): THREE.Box3 {\n\tconst box = new THREE.Box3();\n\tconst index = mesh.geometry.getIndex();\n\tconst position = mesh.geometry.getAttribute('position');\n\tif (!index || !position || member.indexStart == null || member.indexCount == null) return box;\n\n\tmesh.updateMatrixWorld();\n\tconst vertex = new THREE.Vector3();\n\tconst end = Math.min(member.indexStart + member.indexCount, index.count);\n\tfor (let i = member.indexStart; i < end; i++) {\n\t\tvertex.fromBufferAttribute(position, index.getX(i)).applyMatrix4(mesh.matrixWorld);\n\t\tbox.expandByPoint(vertex);\n\t}\n\treturn box;\n}\n\n/**\n * Draws members of a merged mesh with `highlight`. Returns a restore function; call it before\n * applying a different highlight. Members hidden by the outliner stay hidden either way.\n */\nexport function highlightMembers(\n\tmesh: THREE.Mesh,\n\tmembers: ReadonlySet<number>,\n\thighlight: THREE.Material,\n\tkind?: HighlightKind\n): () => void {\n\tsetMemberHighlight(mesh, members, highlight, kind);\n\treturn () => clearMemberHighlight(mesh, kind);\n}\n","import type * as THREE from 'three';\nimport type { LineSegments2 } from 'three/addons/lines/LineSegments2.js';\n\nimport { getHiddenMembers, onMemberDrawChange } from '../../shared/index.js';\nimport { membersOf } from '../scene-setup/merged-picking.js';\nimport type { ExtractedEdges } from './extraction.js';\nimport { buildLineGeometry } from './line-geometry.js';\n\n// ============================================================================\n// Edge overlay of a merged mesh follows its hidden members\n// ============================================================================\n//\n// Hiding a member stops its triangles drawing, but the overlay is one line soup for the whole\n// merged mesh, so its edges would stay behind as a floating outline. Each segment carries its\n// source triangle, so the overlay rebuilds its line geometry from the segments of drawn members.\n\n/** Per segment, the index of the member whose index window holds its source triangle. */\nfunction memberOfSegments(mesh: THREE.Mesh, faces: Uint32Array): Uint32Array | null {\n\tconst members = membersOf(mesh);\n\tif (!members) return null;\n\tconst starts: number[] = [];\n\tfor (const member of members) {\n\t\tif (member.indexStart == null) return null;\n\t\tstarts.push(member.indexStart);\n\t}\n\n\tconst result = new Uint32Array(faces.length);\n\tfor (let s = 0; s < faces.length; s++) {\n\t\tconst indexPosition = faces[s]! * 3;\n\t\t// Last window starting at or before this triangle; windows are ascending and contiguous.\n\t\tlet low = 0;\n\t\tlet high = starts.length - 1;\n\t\twhile (low < high) {\n\t\t\tconst mid = (low + high + 1) >> 1;\n\t\t\tif (starts[mid]! <= indexPosition) low = mid;\n\t\t\telse high = mid - 1;\n\t\t}\n\t\tresult[s] = low;\n\t}\n\treturn result;\n}\n\nfunction visibleSegments(\n\tsegments: Float32Array,\n\tmemberOf: Uint32Array,\n\thidden: ReadonlySet<number>\n): Float32Array {\n\tlet count = 0;\n\tfor (let s = 0; s < memberOf.length; s++) if (!hidden.has(memberOf[s]!)) count++;\n\n\tconst out = new Float32Array(count * 6);\n\tlet offset = 0;\n\tfor (let s = 0; s < memberOf.length; s++) {\n\t\tif (hidden.has(memberOf[s]!)) continue;\n\t\tout.set(segments.subarray(s * 6, s * 6 + 6), offset);\n\t\toffset += 6;\n\t}\n\treturn out;\n}\n\n/** Keeps `overlay` in step with the hidden members of `mesh`, its parent. */\nexport function followHiddenMembers(\n\toverlay: LineSegments2,\n\tmesh: THREE.Mesh,\n\tedges: ExtractedEdges\n): void {\n\tconst { segments, faces } = edges;\n\tif (!faces || !membersOf(mesh)) return;\n\n\tlet memberOf: Uint32Array | null | undefined;\n\tlet filtered = false;\n\n\tconst apply = () => {\n\t\tconst hidden = getHiddenMembers(mesh);\n\t\tif (hidden.size === 0 && !filtered) return;\n\n\t\tmemberOf ??= memberOfSegments(mesh, faces);\n\t\tif (!memberOf) return;\n\n\t\tconst next = hidden.size === 0 ? segments : visibleSegments(segments, memberOf, hidden);\n\t\toverlay.geometry.dispose();\n\t\toverlay.geometry = buildLineGeometry(next).geometry;\n\t\tfiltered = hidden.size > 0;\n\t};\n\n\tapply();\n\tconst unsubscribe = onMemberDrawChange(mesh, apply);\n\toverlay.addEventListener('removed', unsubscribe);\n}\n","import * as THREE from 'three';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\nimport { LineSegments2 } from 'three/addons/lines/LineSegments2.js';\n\nimport type { EdgeGeometryEntry } from './line-geometry.js';\nimport {\n\tDEFAULT_EDGE_COLOR,\n\tEDGE_OFFSET_FACTOR,\n\tEDGE_OFFSET_UNITS,\n\tEDGE_USERDATA_KIND,\n\tFADE_END_PX,\n\tFADE_START_PX,\n\ttype ResolvedOptions\n} from './options.js';\n\n// ============================================================================\n// Overlay construction\n// ============================================================================\n\n// Multiplicative darkening (not lerp-to-black) preserves hue and desaturates gently; a near-black\n// surface just yields near-black edges.\nfunction deriveEdgeColor(mesh: THREE.Mesh, darken: number): THREE.Color {\n\tconst material = Array.isArray(mesh.material) ? mesh.material[0] : mesh.material;\n\tconst source = (material as { color?: THREE.Color } | null)?.color;\n\tif (!source) return new THREE.Color(DEFAULT_EDGE_COLOR);\n\treturn source.clone().multiplyScalar(1 - darken);\n}\n\n/** Pools materials by color+fade so overlays sharing both share one `LineMaterial` instance. */\nexport class MaterialPool {\n\tprivate readonly byKey = new Map<number, LineMaterial>();\n\tconstructor(private readonly options: ResolvedOptions) {}\n\n\tfor(mesh: THREE.Mesh, fade: boolean): LineMaterial {\n\t\tconst color = this.options.forcedColor ?? deriveEdgeColor(mesh, this.options.darken);\n\t\tconst key = color.getHex() * 2 + (fade ? 1 : 0);\n\t\tlet material = this.byKey.get(key);\n\t\tif (!material) {\n\t\t\tmaterial = createEdgeMaterial(color, this.options.width, fade);\n\t\t\tthis.byKey.set(key, material);\n\t\t}\n\t\treturn material;\n\t}\n\n\t/** Dispose any material no overlay adopted (e.g. every mesh was skipped or cancelled). */\n\tdisposeUnused(created: LineSegments2[]): void {\n\t\tconst used = new Set(created.map((overlay) => overlay.material));\n\t\tfor (const material of this.byKey.values()) {\n\t\t\tif (!used.has(material)) material.dispose();\n\t\t}\n\t}\n}\n\nfunction createEdgeMaterial(\n\tcolor: THREE.Color,\n\twidth: number,\n\tdistanceFade: boolean\n): LineMaterial {\n\t// LineMaterialParameters omits linewidth/opacity from its type though both exist at runtime.\n\tconst material = new LineMaterial({ color });\n\t(material as LineMaterial & { linewidth: number }).linewidth = width;\n\t// Lifts lines toward the camera by a couple of depth-quantization steps so they win z-fighting\n\t// against the surface they were extracted from, without moving the surface itself.\n\tmaterial.polygonOffset = true;\n\tmaterial.polygonOffsetFactor = EDGE_OFFSET_FACTOR;\n\tmaterial.polygonOffsetUnits = EDGE_OFFSET_UNITS;\n\t// Set once here, not per draw: flipping `transparent` after the render list is built wouldn't\n\t// re-sort the object into the transparent pass.\n\tif (distanceFade) material.transparent = true;\n\treturn material;\n}\n\nexport function buildEdgeOverlay(\n\tentry: EdgeGeometryEntry,\n\tmaterial: LineMaterial,\n\tdistanceFade: boolean\n): LineSegments2 {\n\tconst overlay = new LineSegments2(entry.geometry, material);\n\toverlay.userData.kind = EDGE_USERDATA_KIND;\n\toverlay.raycast = () => {}; // never pickable; clicks should hit the mesh, not its outline\n\tif (distanceFade) enableDistanceFade(overlay, entry.edgeSpacing);\n\treturn overlay;\n}\n\nconst _fadeCenter = new THREE.Vector3();\nconst _fadeCameraPos = new THREE.Vector3();\n\n// Returns Infinity (\"don't fade\") for an unknown/degenerate projection, or a camera inside the mesh.\nfunction pixelsPerWorldUnit(\n\toverlay: LineSegments2,\n\tcamera: THREE.Camera,\n\tviewportHeightPx: number\n): number {\n\tif (!overlay.geometry.boundingSphere) overlay.geometry.computeBoundingSphere();\n\tconst sphere = overlay.geometry.boundingSphere;\n\tif (!sphere) return Infinity;\n\n\tif ((camera as THREE.PerspectiveCamera).isPerspectiveCamera) {\n\t\tconst perspective = camera as THREE.PerspectiveCamera;\n\t\t_fadeCenter.copy(sphere.center).applyMatrix4(overlay.matrixWorld);\n\t\tconst distance = _fadeCameraPos\n\t\t\t.setFromMatrixPosition(camera.matrixWorld)\n\t\t\t.distanceTo(_fadeCenter);\n\t\tconst radius = sphere.radius * overlay.matrixWorld.getMaxScaleOnAxis();\n\t\tif (distance <= radius) return Infinity; // camera inside the mesh — no fade\n\t\tconst tanHalfFov = Math.tan(THREE.MathUtils.degToRad(perspective.fov) * 0.5);\n\t\tconst worldHeightAtCentre = 2 * distance * tanHalfFov;\n\t\treturn worldHeightAtCentre > 0 ? viewportHeightPx / worldHeightAtCentre : Infinity;\n\t}\n\tif ((camera as THREE.OrthographicCamera).isOrthographicCamera) {\n\t\tconst ortho = camera as THREE.OrthographicCamera;\n\t\tconst worldHeight = (ortho.top - ortho.bottom) / ortho.zoom;\n\t\treturn worldHeight > 0 ? viewportHeightPx / worldHeight : Infinity;\n\t}\n\treturn Infinity;\n}\n\n// Runs in onBeforeRender (not computed once) so opacity is written right before this overlay's\n// draw call — uniforms upload per draw, so overlays sharing one material still fade independently.\n// Chains LineSegments2's own onBeforeRender to keep its resolution uniform in sync.\nfunction enableDistanceFade(overlay: LineSegments2, edgeSpacing: number): void {\n\t// Assign via the Object3D base type: LineSegments2's typings narrow onBeforeRender to\n\t// (renderer) only, but the renderer actually calls it with (renderer, scene, camera, …).\n\t(overlay as THREE.Object3D).onBeforeRender = (renderer, _scene, camera) => {\n\t\tLineSegments2.prototype.onBeforeRender.call(overlay, renderer);\n\t\tconst material = overlay.material as LineMaterial;\n\t\tconst scale = pixelsPerWorldUnit(overlay, camera, material.resolution.y);\n\t\t// Screen-space gap between neighbouring edges. Infinity in, Infinity out — clamps to fully\n\t\t// opaque rather than fading on a guess.\n\t\tconst gapPx = edgeSpacing * scale;\n\t\tmaterial.opacity = THREE.MathUtils.clamp(\n\t\t\t(gapPx - FADE_END_PX) / (FADE_START_PX - FADE_END_PX),\n\t\t\t0,\n\t\t\t1\n\t\t);\n\t};\n}\n","import * as THREE from 'three';\nimport type { LineMaterial } from 'three/addons/lines/LineMaterial.js';\nimport { LineSegments2 } from 'three/addons/lines/LineSegments2.js';\n\nimport { buildLineGeometry } from './edges/line-geometry.js';\nimport {\n\textractSegmentsAsync,\n\textractSegmentsSync,\n\ttriangleCountOf,\n\ttype ExtractedEdges\n} from './edges/extraction.js';\nimport { followHiddenMembers } from './edges/member-filter.js';\nimport {\n\tEDGES_SKIPPED_OVERLAY_BUDGET,\n\tEDGES_SKIPPED_TRIANGLE_CAP,\n\tEDGE_USERDATA_KIND,\n\tresolveOptions,\n\ttype EdgeOptions,\n\ttype ResolvedOptions\n} from './edges/options.js';\nimport { MaterialPool, buildEdgeOverlay } from './edges/overlay.js';\n\n/**\n * Crisp boundary/crease edges overlaid on meshes, rendered as fat `LineSegments2` (controllable\n * thickness, unlike the 1px cap of `THREE.LineSegments`). Depth-offset rationale for the overlay\n * lines: see `EDGE_OFFSET_FACTOR`/`EDGE_OFFSET_UNITS` in `edges/options.ts`.\n */\nexport type { EdgeOptions };\nexport { EDGE_USERDATA_KIND, EDGES_SKIPPED_TRIANGLE_CAP, EDGES_SKIPPED_OVERLAY_BUDGET };\n\n// ============================================================================\n// Public API — add / remove / query\n// ============================================================================\n\n/** For pick/fit filters elsewhere to exclude overlays from hit-testing. */\nexport function isEdgeOverlay(object: THREE.Object3D): boolean {\n\treturn object.userData?.kind === EDGE_USERDATA_KIND;\n}\n\n/**\n * Meshes under `root` that should get an overlay: content meshes without one, caps applied.\n *\n * Two caps, and they fail in opposite directions. `maxTriangles` is per mesh and rejects the rare\n * enormous one; `maxOverlays` is a budget over the whole set and rejects the far more common case\n * of thousands of small ones, which no per-mesh test can see. Overlays already attached from an\n * earlier apply are counted against the budget, so a re-apply can't creep past it.\n */\nfunction collectTargets(root: THREE.Object3D, resolved: ResolvedOptions): THREE.Mesh[] {\n\tconst candidates: THREE.Mesh[] = [];\n\tlet existingOverlays = 0;\n\n\troot.traverse((object) => {\n\t\tif (!(object instanceof THREE.Mesh)) return;\n\t\tif (object.userData.id === 'floor' || object.userData.id === 'grid') return;\n\t\tif (object.userData.kind === EDGE_USERDATA_KIND) return;\n\t\tif (object.children.some((c) => c.userData?.kind === EDGE_USERDATA_KIND)) {\n\t\t\texistingOverlays++;\n\t\t\treturn; // already done\n\t\t}\n\t\tif (!object.geometry) return;\n\n\t\tif (triangleCountOf(object.geometry) > resolved.maxTriangles) {\n\t\t\tobject.userData.edgesSkipped = EDGES_SKIPPED_TRIANGLE_CAP;\n\t\t\treturn;\n\t\t}\n\t\tcandidates.push(object);\n\t});\n\n\tconst budget = Math.max(0, resolved.maxOverlays - existingOverlays);\n\tconst targets = candidates.slice(0, budget);\n\tfor (const mesh of targets) delete mesh.userData.edgesSkipped;\n\tfor (const mesh of candidates.slice(budget)) {\n\t\tmesh.userData.edgesSkipped = EDGES_SKIPPED_OVERLAY_BUDGET;\n\t}\n\n\tif (candidates.length > budget) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.debug(\n\t\t\t`[edges] overlay budget reached: ${candidates.length} meshes want overlays, ` +\n\t\t\t\t`${budget} allowed — the rest fall back to the screen-space edge pass.`\n\t\t);\n\t}\n\n\treturn targets;\n}\n\nfunction attachOverlay(\n\tmesh: THREE.Mesh,\n\tedges: ExtractedEdges,\n\tmaterials: MaterialPool,\n\tresolved: ResolvedOptions\n): LineSegments2 {\n\tconst entry = buildLineGeometry(edges.segments);\n\t// Distance fade needs the transparent pass; overlays over the segment cap stay opaque instead\n\t// of skipping outright — see EdgeOptions.maxSegments.\n\tconst fade = resolved.distanceFade && entry.segmentCount <= resolved.maxSegments;\n\tconst overlay = buildEdgeOverlay(entry, materials.for(mesh, fade), fade);\n\tmesh.add(overlay); // child → inherits transform, disposed with the parent subtree\n\tfollowHiddenMembers(overlay, mesh, edges);\n\treturn overlay;\n}\n\n/**\n * Attach an edge overlay to every `Mesh` in `root`'s subtree, returning the created overlays.\n * Idempotent — meshes that already carry an overlay are skipped. Skips the floor/grid aids.\n *\n * Fully synchronous, extraction included. Prefer {@link addEdgesAsync} for interactive hosts with\n * potentially-large meshes.\n */\nexport function addEdges(root: THREE.Object3D, options: EdgeOptions = {}): LineSegments2[] {\n\tconst resolved = resolveOptions(options);\n\tconst materials = new MaterialPool(resolved);\n\tconst created: LineSegments2[] = [];\n\n\tfor (const mesh of collectTargets(root, resolved)) {\n\t\t// Extraction is cached by source array (`extraction.ts`), which is where the savings are;\n\t\t// the line geometry is per-overlay and owned by it.\n\t\tconst edges = extractSegmentsSync(mesh.geometry, resolved.thresholdAngle);\n\t\tcreated.push(attachOverlay(mesh, edges, materials, resolved));\n\t}\n\n\tmaterials.disposeUnused(created);\n\treturn created;\n}\n\n/**\n * {@link removeEdges} bumps this per root; async attaches landing after a bump are dropped, so\n * \"toggle off while extracting\" can't resurrect overlays.\n */\nconst rootGenerations = new WeakMap<THREE.Object3D, number>();\n\nfunction generationOf(root: THREE.Object3D): number {\n\treturn rootGenerations.get(root) ?? 0;\n}\n\n/** Is `mesh` still reachable from `root`? Guards attaches racing scene clears. */\nfunction isConnected(mesh: THREE.Object3D, root: THREE.Object3D): boolean {\n\tfor (let node: THREE.Object3D | null = mesh; node; node = node.parent) {\n\t\tif (node === root) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Like {@link addEdges}, but large-mesh extraction runs in a Worker so the main thread never\n * stalls; small meshes still attach synchronously before this resolves. Resolves with every\n * overlay actually attached — late results are dropped if the mesh left the subtree,\n * {@link removeEdges} ran for this root meanwhile, or another apply already attached one to that\n * mesh.\n */\nexport async function addEdgesAsync(\n\troot: THREE.Object3D,\n\toptions: EdgeOptions = {}\n): Promise<LineSegments2[]> {\n\tconst resolved = resolveOptions(options);\n\tconst materials = new MaterialPool(resolved);\n\tconst generation = generationOf(root);\n\tconst created: LineSegments2[] = [];\n\n\tconst attaches = collectTargets(root, resolved).map(async (mesh) => {\n\t\tconst edges = await extractSegmentsAsync(mesh.geometry, resolved.thresholdAngle);\n\t\t// Things may have moved on while extracting — attach only if this apply is still wanted.\n\t\tif (generationOf(root) !== generation) return;\n\t\tif (!isConnected(mesh, root)) return;\n\t\tif (mesh.children.some((c) => c.userData?.kind === EDGE_USERDATA_KIND)) return;\n\t\tcreated.push(attachOverlay(mesh, edges, materials, resolved));\n\t});\n\n\tawait Promise.all(attaches);\n\tmaterials.disposeUnused(created);\n\treturn created;\n}\n\n/**\n * Remove every edge overlay under `root`, disposing geometry and material, and cancels any\n * in-flight async attaches for `root`. Inverse of {@link addEdges}/{@link addEdgesAsync}. Returns\n * the count removed.\n */\nexport function removeEdges(root: THREE.Object3D): number {\n\trootGenerations.set(root, generationOf(root) + 1);\n\n\tconst overlays: LineSegments2[] = [];\n\troot.traverse((object) => {\n\t\tif (object instanceof LineSegments2 && isEdgeOverlay(object)) overlays.push(object);\n\t});\n\n\t// One addEdges call shares one material across its overlays — dispose each distinct one once.\n\t// (If `root` covers only part of a call's overlays, survivors self-heal: three recompiles a\n\t// disposed-but-still-referenced material on its next use.)\n\tconst materials = new Set<LineMaterial>();\n\tfor (const overlay of overlays) {\n\t\toverlay.geometry.dispose(); // each overlay owns its line geometry outright\n\t\tmaterials.add(overlay.material as LineMaterial);\n\t\t// Nothing to undo on the parent mesh: the depth bias lives entirely on the overlay's own\n\t\t// material, so surfaces keep whatever polygonOffset their look preset configured.\n\t\toverlay.removeFromParent();\n\t}\n\tmaterials.forEach((material) => material.dispose());\n\treturn overlays.length;\n}\n","import * as THREE from 'three';\n\n/**\n * An \"infinite\", distance-fading reference grid.\n *\n * `GridHelper` is a fixed-size square that visibly ends once you pan/zoom past it. This draws one\n * large plane and computes the grid in the fragment shader from world coordinates, fading with\n * distance so the edge is never a hard cutoff.\n *\n * Spacing is in world units (meters) — `cellSize` of 1 = 1m cells.\n */\n\nexport interface GridOptions {\n\t/** Minor cell size in world units (meters). Default 1. */\n\tcellSize?: number;\n\t/** How many minor cells per major line. Default 10. */\n\tmajorEvery?: number;\n\t/** Minor line color. Default 0x888888. */\n\tcellColor?: THREE.ColorRepresentation;\n\t/** Major line color. Default 0x444444. */\n\tmajorColor?: THREE.ColorRepresentation;\n\t/** World-space radius at which the grid has fully faded out. Default 100. */\n\tfadeDistance?: number;\n\t/**\n\t * Axis the grid is laid perpendicular to, i.e. the scene's up axis. Standalone default is `'y'`,\n\t * but `initThree` always passes a plane derived from the configured `sceneUp`, so a\n\t * viewer-created grid defaults to `'z'` (Rhino's ground plane).\n\t */\n\tplane?: 'x' | 'y' | 'z';\n}\n\nexport interface Grid {\n\t/** Tagged `userData.id = 'grid'` so pick/fit code skips it. */\n\treadonly object: THREE.Mesh;\n\t/** Re-centers the fade on the camera so the grid feels infinite as you move. Call per frame. */\n\tupdate(cameraPosition: THREE.Vector3): void;\n\t/**\n\t * Rescales cell spacing and fade radius to the content's extent, so a 3-unit or 3000-unit part\n\t * both get sensible cells. No-op for empty/degenerate bounds.\n\t */\n\tfitToContent(bounds: THREE.Box3): void;\n\tsetVisible(visible: boolean): void;\n\tdispose(): void;\n}\n\n/** Rounds to a \"nice\" 1/2/5 × 10ⁿ step (CAD ruler convention) so cells never come out finer than the target and alias into a solid sheet. */\nfunction niceStep(value: number): number {\n\tif (!(value > 0) || !Number.isFinite(value)) return 1;\n\tconst exponent = Math.floor(Math.log10(value));\n\tconst power = Math.pow(10, exponent);\n\tconst mantissa = value / power; // in [1, 10)\n\tconst niceMantissa = mantissa >= 5 ? 5 : mantissa >= 2 ? 2 : 1;\n\treturn niceMantissa * power;\n}\n\nconst GRID_VERTEX = /* glsl */ `\n\tvarying vec3 vWorldPos;\n\tvoid main() {\n\t\tvec4 world = modelMatrix * vec4(position, 1.0);\n\t\tvWorldPos = world.xyz;\n\t\tgl_Position = projectionMatrix * viewMatrix * world;\n\t}\n`;\n\nconst GRID_FRAGMENT = /* glsl */ `\n\tprecision highp float;\n\tvarying vec3 vWorldPos;\n\n\tuniform vec2 uAxes; // indices (0=x,1=y,2=z) of the two in-plane world axes\n\tuniform float uCell;\n\tuniform float uMajor;\n\tuniform vec3 uCellColor;\n\tuniform vec3 uMajorColor;\n\tuniform vec3 uCenter; // fade center (camera position projected onto the plane)\n\tuniform float uFade;\n\n\t// Screen-space derivatives keep grid lines ~1px regardless of zoom (\"pristine grid\" technique).\n\tfloat gridLine(vec2 coord, float spacing) {\n\t\tvec2 c = coord / spacing;\n\t\tvec2 d = fwidth(c);\n\t\tvec2 g = abs(fract(c - 0.5) - 0.5) / max(d, 1e-6);\n\t\tfloat line = min(g.x, g.y);\n\t\treturn 1.0 - clamp(line, 0.0, 1.0);\n\t}\n\n\t// Index a vec3 by a float axis id (0/1/2) without dynamic indexing (WebGL1-safe).\n\tfloat axis(vec3 v, float i) {\n\t\treturn i < 0.5 ? v.x : (i < 1.5 ? v.y : v.z);\n\t}\n\n\tvoid main() {\n\t\t// Pick the two in-plane world coordinates.\n\t\tvec2 coord = vec2(axis(vWorldPos, uAxes.x), axis(vWorldPos, uAxes.y));\n\n\t\tfloat minor = gridLine(coord, uCell);\n\t\tfloat major = gridLine(coord, uCell * uMajor);\n\n\t\tvec3 color = mix(uCellColor, uMajorColor, major);\n\t\tfloat alpha = max(minor, major);\n\n\t\t// Radial fade from the camera-projected center.\n\t\tfloat dist = distance(vWorldPos, uCenter);\n\t\tfloat fade = 1.0 - clamp(dist / uFade, 0.0, 1.0);\n\t\talpha *= fade * fade;\n\n\t\tif (alpha < 0.001) discard;\n\t\tgl_FragColor = vec4(color, alpha);\n\t}\n`;\n\nexport function createGrid(options: GridOptions = {}): Grid {\n\tconst {\n\t\tcellSize = 1,\n\t\tmajorEvery = 10,\n\t\tcellColor = 0x888888,\n\t\tmajorColor = 0x444444,\n\t\tfadeDistance = 100,\n\t\tplane = 'y'\n\t} = options;\n\n\t// In-plane world axes: ground 'y' grids over x,z; 'z' over x,y; 'x' over y,z.\n\tconst axes =\n\t\tplane === 'y'\n\t\t\t? new THREE.Vector2(0, 2) // x, z\n\t\t\t: plane === 'z'\n\t\t\t\t? new THREE.Vector2(0, 1) // x, y\n\t\t\t\t: new THREE.Vector2(1, 2); // y, z\n\n\t// Must comfortably outreach the fade radius, else the grid's rectangular edge shows before the\n\t// fade completes. Plane is unit-sized and grown purely via scale so fitToContent never\n\t// recreates geometry.\n\tconst PLANE_TO_FADE_RATIO = 2.5;\n\tconst geometry = new THREE.PlaneGeometry(1, 1);\n\n\t// PlaneGeometry is in the XY plane by default; rotate it onto the requested world plane.\n\tif (plane === 'y') geometry.rotateX(-Math.PI / 2);\n\telse if (plane === 'x') geometry.rotateY(Math.PI / 2);\n\n\tconst material = new THREE.ShaderMaterial({\n\t\tvertexShader: GRID_VERTEX,\n\t\tfragmentShader: GRID_FRAGMENT,\n\t\ttransparent: true,\n\t\tdepthWrite: false,\n\t\tside: THREE.DoubleSide,\n\t\tuniforms: {\n\t\t\tuAxes: { value: axes },\n\t\t\tuCell: { value: cellSize },\n\t\t\tuMajor: { value: majorEvery },\n\t\t\tuCellColor: { value: new THREE.Color(cellColor) },\n\t\t\tuMajorColor: { value: new THREE.Color(majorColor) },\n\t\t\tuCenter: { value: new THREE.Vector3() },\n\t\t\tuFade: { value: fadeDistance }\n\t\t}\n\t});\n\n\tconst mesh = new THREE.Mesh(geometry, material);\n\tmesh.name = 'grid';\n\tmesh.userData.id = 'grid';\n\tmesh.renderOrder = -1; // draw before content so transparent geometry blends over it\n\n\t// fitToContent mutates both; seeded from fadeDistance so an un-fitted grid still covers its fade.\n\tlet fadeRadius = fadeDistance;\n\tlet planeScale = fadeDistance * PLANE_TO_FADE_RATIO;\n\n\tconst center = new THREE.Vector3();\n\n\treturn {\n\t\tobject: mesh,\n\t\tupdate: (cameraPosition) => {\n\t\t\t// Re-center on the camera so the grid tracks the view; the plane's own axis stays fixed\n\t\t\t// (a ground grid shouldn't lift to the camera's height).\n\t\t\tif (plane === 'y') {\n\t\t\t\tmesh.position.set(cameraPosition.x, 0, cameraPosition.z);\n\t\t\t\tcenter.set(cameraPosition.x, 0, cameraPosition.z);\n\t\t\t} else if (plane === 'z') {\n\t\t\t\tmesh.position.set(cameraPosition.x, cameraPosition.y, 0);\n\t\t\t\tcenter.set(cameraPosition.x, cameraPosition.y, 0);\n\t\t\t} else {\n\t\t\t\tmesh.position.set(0, cameraPosition.y, cameraPosition.z);\n\t\t\t\tcenter.set(0, cameraPosition.y, cameraPosition.z);\n\t\t\t}\n\t\t\tmaterial.uniforms.uCenter.value.copy(center);\n\t\t\t// Rotation is baked into the geometry, so uniform scale works on any plane orientation.\n\t\t\tmesh.scale.setScalar(planeScale);\n\t\t},\n\t\tfitToContent: (bounds) => {\n\t\t\tif (bounds.isEmpty()) return;\n\t\t\t// In-plane extent only — a tall thin part shouldn't blow up the cell size by its height.\n\t\t\tconst sizeVec = bounds.getSize(new THREE.Vector3());\n\t\t\tconst axisComponent = (v: THREE.Vector3, i: number) => (i === 0 ? v.x : i === 1 ? v.y : v.z);\n\t\t\tconst inPlaneExtent = Math.max(\n\t\t\t\taxisComponent(sizeVec, axes.x),\n\t\t\t\taxisComponent(sizeVec, axes.y)\n\t\t\t);\n\t\t\tif (!(inPlaneExtent > 0) || !Number.isFinite(inPlaneExtent)) return;\n\n\t\t\t// ~20 minor cells across the part; fade reaches ~2x past it so the edge stays out of view.\n\t\t\tconst TARGET_CELLS_ACROSS = 20;\n\t\t\tmaterial.uniforms.uCell.value = niceStep(inPlaneExtent / TARGET_CELLS_ACROSS);\n\t\t\tfadeRadius = inPlaneExtent * 2;\n\t\t\tmaterial.uniforms.uFade.value = fadeRadius;\n\t\t\tplaneScale = fadeRadius * PLANE_TO_FADE_RATIO;\n\t\t},\n\t\tsetVisible: (visible) => {\n\t\t\tmesh.visible = visible;\n\t\t},\n\t\tdispose: () => {\n\t\t\tmesh.removeFromParent();\n\t\t\tgeometry.dispose();\n\t\t\tmaterial.dispose();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\nimport { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';\n\nexport interface LabelHandle {\n\treadonly object: CSS2DObject;\n\tsetPosition(position: THREE.Vector3): void;\n\tsetText(text: string): void;\n\tremove(): void;\n}\n\nexport interface LabelLayer {\n\taddLabel(text: string, position: THREE.Vector3, className?: string): LabelHandle;\n\t/** Call each frame after the WebGL render, with the active camera. */\n\trender(scene: THREE.Scene, camera: THREE.Camera): void;\n\tsetSize(width: number, height: number): void;\n\tdispose(): void;\n}\n\n// `container` is normally the canvas's parent, so both share a positioning context for the\n// absolutely-positioned label overlay.\nexport function createLabelLayer(container: HTMLElement, scene: THREE.Scene): LabelLayer {\n\tconst renderer = new CSS2DRenderer();\n\tconst dom = renderer.domElement;\n\tdom.style.position = 'absolute';\n\tdom.style.top = '0';\n\tdom.style.left = '0';\n\t// CSS2DRenderer sets width/height in pixels, so host must call `setSize` on every resize, same\n\t// as the WebGL renderer. overflow:hidden + pointerEvents:none: without both, the overlay can\n\t// cover the canvas and swallow orbit/clicks.\n\tdom.style.overflow = 'hidden';\n\tdom.style.pointerEvents = 'none';\n\tdom.style.zIndex = '30'; // above canvas/host overlays, below menus/popovers\n\tif (getComputedStyle(container).position === 'static') {\n\t\tcontainer.style.position = 'relative';\n\t}\n\tcontainer.appendChild(dom);\n\n\tconst size = { width: container.clientWidth || 1, height: container.clientHeight || 1 };\n\trenderer.setSize(size.width, size.height);\n\n\tconst group = new THREE.Group(); // pick/fit logic skips objects tagged 'label-layer'\n\tgroup.name = 'label-layer';\n\tgroup.userData.id = 'label-layer';\n\tscene.add(group);\n\n\tconst labels = new Set<CSS2DObject>();\n\n\tconst addLabel = (text: string, position: THREE.Vector3, className?: string): LabelHandle => {\n\t\tconst el = document.createElement('div');\n\t\tel.textContent = text;\n\t\tif (className) {\n\t\t\tel.className = className;\n\t\t} else {\n\t\t\t// Inline default so the layer needs no external stylesheet; pass className to opt out.\n\t\t\tObject.assign(el.style, {\n\t\t\t\tpadding: '2px 6px',\n\t\t\t\tborderRadius: '4px',\n\t\t\t\tbackground: 'rgba(20, 20, 20, 0.78)',\n\t\t\t\tcolor: '#fff',\n\t\t\t\tfont: '12px/1.3 system-ui, sans-serif',\n\t\t\t\t// `pre` preserves line breaks for multi-line readouts (e.g. total + per-axis deltas).\n\t\t\t\twhiteSpace: 'pre',\n\t\t\t\ttextAlign: 'center',\n\t\t\t\tuserSelect: 'none'\n\t\t\t} satisfies Partial<CSSStyleDeclaration>);\n\t\t}\n\t\tel.style.pointerEvents = 'none';\n\n\t\tconst object = new CSS2DObject(el);\n\t\tobject.position.copy(position);\n\t\tgroup.add(object);\n\t\tlabels.add(object);\n\n\t\treturn {\n\t\t\tobject,\n\t\t\tsetPosition: (p) => object.position.copy(p),\n\t\t\tsetText: (t) => {\n\t\t\t\tel.textContent = t;\n\t\t\t},\n\t\t\tremove: () => {\n\t\t\t\tobject.removeFromParent();\n\t\t\t\tel.remove();\n\t\t\t\tlabels.delete(object);\n\t\t\t}\n\t\t};\n\t};\n\n\treturn {\n\t\taddLabel,\n\t\trender: (scene, camera) => renderer.render(scene, camera),\n\t\tsetSize: (width, height) => renderer.setSize(width, height),\n\t\tdispose: () => {\n\t\t\tlabels.forEach((object) => {\n\t\t\t\tobject.removeFromParent();\n\t\t\t\t(object.element as HTMLElement).remove();\n\t\t\t});\n\t\t\tlabels.clear();\n\t\t\tgroup.removeFromParent();\n\t\t\tdom.remove();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\nimport { MeshBVH } from 'three-mesh-bvh';\n\nimport { isEdgeOverlay } from './edges.js';\nimport { isViewerAidRoot } from './three-helpers.js';\n\n// ============================================================================\n// Hover-rate picking for large scenes\n// ============================================================================\n//\n// `raycaster.intersectObjects(scene, true)` tests every triangle of every mesh the ray's bounding\n// sphere touches. A merged IFC-scale mesh is millions of triangles, so a per-frame hover pick\n// stalls the page. Meshes here go through a BVH instead, built once per geometry on first touch.\n//\n// The BVH is `indirect` and built from a group-less proxy: a default BVH reorders the index buffer,\n// which would break the merged-member windows in `userData.members`, and it bakes the geometry's\n// groups in at build time, while `shared/merged-draw.ts` rewrites groups on every hide/highlight.\n// Hidden members are filtered per hit instead, against the groups as they stand now.\n\ninterface CachedBvh {\n\tposition: THREE.BufferAttribute | THREE.InterleavedBufferAttribute;\n\tpositionVersion: number;\n\tindexVersion: number;\n\tbvh: MeshBVH;\n}\n\n// Keyed by the index (or position) attribute, not the geometry: `merged-draw` swaps a cached\n// geometry for a fresh one over the same attributes, and that must not trigger a rebuild.\nconst bvhCache = new WeakMap<object, CachedBvh | null>();\n\nfunction versionOf(attribute: { version?: number } | null): number {\n\treturn attribute?.version ?? 0;\n}\n\n/** BVH for a mesh geometry, building it on first use. Null when the geometry can't take one. */\nexport function bvhFor(geometry: THREE.BufferGeometry): MeshBVH | null {\n\tconst position = geometry.attributes.position;\n\tif (!position || position.count === 0) return null;\n\tconst index = geometry.index;\n\tconst key = index ?? position;\n\n\tconst cached = bvhCache.get(key);\n\tif (cached === null) return null;\n\tif (\n\t\tcached &&\n\t\tcached.position === position &&\n\t\tcached.positionVersion === versionOf(position as THREE.BufferAttribute) &&\n\t\tcached.indexVersion === versionOf(index)\n\t) {\n\t\treturn cached.bvh;\n\t}\n\n\tconst proxy = new THREE.BufferGeometry();\n\tproxy.setAttribute('position', position);\n\tif (index) proxy.setIndex(index);\n\ttry {\n\t\tconst bvh = new MeshBVH(proxy, { indirect: true });\n\t\tbvhCache.set(key, {\n\t\t\tposition,\n\t\t\tpositionVersion: versionOf(position as THREE.BufferAttribute),\n\t\t\tindexVersion: versionOf(index),\n\t\t\tbvh\n\t\t});\n\t\treturn bvh;\n\t} catch {\n\t\t// Malformed geometry (index count not a multiple of 3, …): fall back to three's raycast.\n\t\tbvhCache.set(key, null);\n\t\treturn null;\n\t}\n}\n\nfunction isBvhMesh(object: THREE.Object3D): object is THREE.Mesh {\n\tconst o = object as THREE.Object3D & Record<string, unknown>;\n\t// Line2/LineSegments2 are Meshes too, but their triangles are screen-space quads.\n\treturn (\n\t\to.isMesh === true &&\n\t\t!o.isInstancedMesh &&\n\t\t!o.isSkinnedMesh &&\n\t\t!o.isBatchedMesh &&\n\t\t!o.isLineSegments2 &&\n\t\t!(object as THREE.Mesh).morphTargetInfluences?.length\n\t);\n}\n\n/**\n * Visible, pickable objects under `root`: hidden subtrees, viewer aids and edge overlays are\n * skipped. Overlays are skipped because they double the mesh's own edges and, as fat lines, cost a\n * linear scan of every segment per ray.\n */\nexport function collectPickable(\n\troot: THREE.Object3D,\n\tout: THREE.Object3D[] = []\n): THREE.Object3D[] {\n\tfor (const child of root.children) {\n\t\tif (!child.visible || isViewerAidRoot(child) || isEdgeOverlay(child)) continue;\n\t\tout.push(child);\n\t\tcollectPickable(child, out);\n\t}\n\treturn out;\n}\n\n/**\n * Index-buffer ranges a merged mesh currently draws, or null when it draws everything. Hidden\n * members are the gaps between groups (`shared/merged-draw.ts`).\n */\nfunction drawnRanges(mesh: THREE.Mesh): { start: number; end: number }[] | null {\n\tconst { groups } = mesh.geometry;\n\tif (!Array.isArray(mesh.material) || groups.length === 0) return null;\n\tconst materials = mesh.material;\n\treturn groups\n\t\t.filter((g) => materials[g.materialIndex ?? 0]?.visible !== false)\n\t\t.map((g) => ({ start: g.start, end: g.start + g.count }));\n}\n\nconst _inverse = new THREE.Matrix4();\nconst _localRay = new THREE.Ray();\nconst _sphere = new THREE.Sphere();\n\nfunction raycastMeshBvh(\n\tmesh: THREE.Mesh,\n\tbvh: MeshBVH,\n\traycaster: THREE.Raycaster\n): THREE.Intersection | null {\n\t_inverse.copy(mesh.matrixWorld).invert();\n\t_localRay.copy(raycaster.ray).applyMatrix4(_inverse);\n\n\tconst ranges = drawnRanges(mesh);\n\tlet best: THREE.Intersection | null = null;\n\tif (!ranges) {\n\t\tbest = bvh.raycastFirst(_localRay, THREE.DoubleSide);\n\t} else {\n\t\tfor (const hit of bvh.raycast(_localRay, THREE.DoubleSide)) {\n\t\t\tconst at = (hit.faceIndex ?? 0) * 3;\n\t\t\tif (!ranges.some((r) => at >= r.start && at < r.end)) continue;\n\t\t\tif (!best || hit.distance < best.distance) best = hit;\n\t\t}\n\t}\n\tif (!best) return null;\n\n\tbest.point.applyMatrix4(mesh.matrixWorld);\n\tbest.distance = best.point.distanceTo(raycaster.ray.origin);\n\tbest.object = mesh;\n\tif (best.distance < raycaster.near || best.distance > raycaster.far) return null;\n\treturn best;\n}\n\n/**\n * Every hit along `raycaster`'s ray over the visible content of `scene`, nearest first. Meshes use\n * the BVH; lines, points and instanced meshes fall back to their own `raycast`.\n */\nexport function raycastVisible(\n\tscene: THREE.Object3D,\n\traycaster: THREE.Raycaster\n): THREE.Intersection[] {\n\tconst hits: THREE.Intersection[] = [];\n\tfor (const object of collectPickable(scene)) {\n\t\tif (!object.layers.test(raycaster.layers)) continue;\n\n\t\tif (isBvhMesh(object)) {\n\t\t\tconst { geometry } = object;\n\t\t\tif (!geometry.boundingSphere) geometry.computeBoundingSphere();\n\t\t\tif (!geometry.boundingSphere) continue;\n\t\t\t_sphere.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld);\n\t\t\tif (!raycaster.ray.intersectsSphere(_sphere)) continue;\n\n\t\t\tconst bvh = bvhFor(geometry);\n\t\t\tif (bvh) {\n\t\t\t\tconst hit = raycastMeshBvh(object, bvh, raycaster);\n\t\t\t\tif (hit) hits.push(hit);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tobject.raycast(raycaster, hits);\n\t}\n\treturn hits.sort((a, b) => a.distance - b.distance);\n}\n\n/**\n * Builds BVHs for the visible meshes under `root` one per idle slice, so the first hover over a\n * large model doesn't pay for all of them at once. Returns a cancel function.\n */\nexport function warmPickIndex(root: THREE.Object3D): () => void {\n\tconst queue = collectPickable(root).filter(isBvhMesh);\n\tconst idle =\n\t\ttypeof requestIdleCallback === 'function'\n\t\t\t? (fn: () => void) => requestIdleCallback(fn, { timeout: 500 })\n\t\t\t: (fn: () => void) => setTimeout(fn, 16) as unknown as number;\n\tconst cancelIdle =\n\t\ttypeof cancelIdleCallback === 'function'\n\t\t\t? cancelIdleCallback\n\t\t\t: (id: number) => clearTimeout(id);\n\n\tlet handle = 0;\n\tlet cancelled = false;\n\tconst step = () => {\n\t\tif (cancelled) return;\n\t\tconst mesh = queue.shift();\n\t\tif (!mesh) return;\n\t\tbvhFor(mesh.geometry);\n\t\thandle = idle(step);\n\t};\n\thandle = idle(step);\n\treturn () => {\n\t\tcancelled = true;\n\t\tcancelIdle(handle);\n\t};\n}\n","import * as THREE from 'three';\nimport { Line2 } from 'three/addons/lines/Line2.js';\nimport { LineGeometry } from 'three/addons/lines/LineGeometry.js';\nimport { LineMaterial } from 'three/addons/lines/LineMaterial.js';\n\nimport type { LabelLayer, LabelHandle } from './label-layer';\nimport { raycastVisible, warmPickIndex } from './scene-pick.js';\n\n/**\n * Point-to-point distance measurement. Click a start point; a rubber band and live readout follow\n * the cursor until the second click commits it. Committed measurements stay until Escape (which\n * first drops an unfinished start point) or until the tool is disabled.\n *\n * Picking snaps to the nearest vertex of the struck triangle, segment or point within\n * {@link MeasureOptions.snapPixels}; the hover marker turns solid when it has snapped. Meshes are\n * picked through a BVH (`scene-pick.ts`), so hovering stays cheap on large models.\n *\n * Dormant until {@link MeasureTool.setEnabled}(true). While enabled it intercepts clicks (caller\n * forwards them and swallows the event when {@link MeasureTool.handleClick} returns true) so\n * measuring doesn't also select objects.\n */\n\nexport interface MeasureTool {\n\tsetEnabled(enabled: boolean): void;\n\tisEnabled(): boolean;\n\t/** Returns true if the tool consumed the click (caller should not also select). */\n\thandleClick(event: MouseEvent): boolean;\n\t/** Preview the next snap point and the rubber band. No-op when disabled; never consumes the event. */\n\thandleMove(event: MouseEvent): void;\n\t/** Drops the unfinished start point, or every measurement when there is none. Bound to Escape. */\n\tcancel(): void;\n\tclear(): void;\n\tdispose(): void;\n}\n\nexport interface MeasureOptions {\n\t/** Snap to a vertex when the cursor is within this many screen pixels of it. Default 12. */\n\tsnapPixels?: number;\n\t/** Marker + line color. Default yellow. */\n\tcolor?: THREE.ColorRepresentation;\n\tlabelClassName?: string;\n\t/**\n\t * Pass `data.modelunits`. Scene is in meters; default formatter converts and labels in this unit\n\t * (e.g. \"25.0 mm\" not \"0.025 m\"). Defaults to meters. Ignored if `format` is given.\n\t */\n\tdisplayUnit?: string;\n\t/**\n\t * Format the measurement → label text. Receives `distance` and per-axis `delta` (|b − a|), both\n\t * in meters. May return multi-line text; default renders the total, plus the non-zero axis\n\t * deltas when more than one axis changes.\n\t */\n\tformat?: (distance: number, delta: THREE.Vector3) => string;\n}\n\ninterface MeasureDeps {\n\tcanvas: HTMLCanvasElement;\n\tscene: THREE.Scene;\n\tgetActiveCamera: () => THREE.Camera;\n\t/**\n\t * The current orbit target (e.g. `controls.target`). Scales the line/point pick threshold as a\n\t * fraction of camera→target distance so it stays constant on screen regardless of framing.\n\t * Without it, the fallback is distance-to-origin, which misjudges off-origin content.\n\t */\n\tgetViewTarget?: () => THREE.Vector3;\n\t/** Repaint after a hover/click change; an on-demand render loop won't notice them otherwise. */\n\trequestRender?: () => void;\n\tlabelLayer: LabelLayer;\n\toptions?: MeasureOptions;\n}\n\nconst DEFAULT_SNAP_PIXELS = 12;\nconst DEFAULT_COLOR = 0xffcc00;\n// Fraction of view distance used as the line/point raycast threshold: ~1.5% gives a comfortable\n// grab band at typical framing without snapping to far-off geometry.\nconst LINE_PICK_FRACTION = 0.015;\n// Marker renderOrders: above scene content, and committed markers above the line they end.\nconst RENDER_ORDER_LINE = 998;\nconst RENDER_ORDER_MARKER = 999;\nconst RENDER_ORDER_HOVER = 1000;\n\n// Scene geometry loads in meters (webdisplay parser scales when `allowScaling` is on). Keep in\n// sync with the webdisplay parser's SCALE_FACTORS.\nconst UNIT_DISPLAY: Record<string, { metersPerUnit: number; suffix: string }> = {\n\tMillimeters: { metersPerUnit: 1 / 1000, suffix: 'mm' },\n\tCentimeters: { metersPerUnit: 1 / 100, suffix: 'cm' },\n\tMeters: { metersPerUnit: 1, suffix: 'm' },\n\tInches: { metersPerUnit: 1 / 39.37, suffix: 'in' },\n\tFeet: { metersPerUnit: 1 / 3.28084, suffix: 'ft' }\n};\n\n/**\n * Three significant figures below 1000, whole units above. `toPrecision(3)` alone switches to\n * exponent notation past 999 (\"1.23e+4 mm\"), which is unreadable on a building-scale model.\n *\n * @internal exported for tests\n */\nexport function makeFormatter(displayUnit?: string): (n: number) => string {\n\tconst unit = (displayUnit && UNIT_DISPLAY[displayUnit]) || UNIT_DISPLAY.Meters;\n\treturn (meters: number) => {\n\t\tconst value = meters / unit.metersPerUnit;\n\t\tconst abs = Math.abs(value);\n\t\tconst decimals = abs === 0 ? 0 : Math.min(6, Math.max(0, 2 - Math.floor(Math.log10(abs))));\n\t\treturn `${value.toFixed(decimals)} ${unit.suffix}`;\n\t};\n}\n\n/** @internal exported for tests */\nexport function makeDefaultFormat(\n\tfmt: (n: number) => string\n): (distance: number, delta: THREE.Vector3) => string {\n\treturn (distance, delta) => {\n\t\t// Relative epsilon: float noise on an axis-aligned measurement isn't a real component.\n\t\tconst eps = distance * 1e-6;\n\t\tconst axes = (['x', 'y', 'z'] as const).filter((axis) => delta[axis] > eps);\n\t\tif (axes.length < 2) return fmt(distance);\n\t\treturn `${fmt(distance)}\\n${axes.map((axis) => `Δ${axis} ${fmt(delta[axis])}`).join(' ')}`;\n\t};\n}\n\n/**\n * Raycast threshold for picking lines/points, as a fixed fraction of view size so the grab band\n * stays constant on screen while zooming. Perspective uses camera→target distance (see\n * `MeasureDeps.getViewTarget`); orthographic uses frustum height `(top − bottom) / zoom`, since\n * ortho zoom changes `camera.zoom` rather than position.\n *\n * Shared with any tool doing its own picking, so grab bands stay consistent across tools.\n */\nexport function pickThreshold(camera: THREE.Camera, viewTarget?: THREE.Vector3): number {\n\tif ((camera as THREE.OrthographicCamera).isOrthographicCamera) {\n\t\tconst ortho = camera as THREE.OrthographicCamera;\n\t\tconst visibleHeight = Math.abs(ortho.top - ortho.bottom) / (ortho.zoom || 1);\n\t\treturn visibleHeight * LINE_PICK_FRACTION;\n\t}\n\tconst viewScale = viewTarget ? camera.position.distanceTo(viewTarget) : camera.position.length();\n\treturn (viewScale || 1) * LINE_PICK_FRACTION;\n}\n\n/**\n * Local-space vertices to consider snapping to, by object type: Mesh → struck triangle's 3\n * vertices; Line/LineSegments and fat Line2/LineSegments2 → struck segment's 2 endpoints; Points →\n * the struck vertex. Null when the hit carries no usable index, so the caller keeps the raw point.\n */\nfunction snapCandidates(hit: THREE.Intersection): THREE.Vector3[] | null {\n\tconst obj = hit.object as THREE.Object3D & {\n\t\tgeometry?: THREE.BufferGeometry;\n\t\tisLineSegments2?: boolean;\n\t};\n\tconst geometry = obj.geometry;\n\tif (!geometry) return null;\n\n\t// Fat lines store each segment as one instance: `faceIndex` is the segment.\n\tif (obj.isLineSegments2) {\n\t\tconst start = geometry.attributes.instanceStart as THREE.InterleavedBufferAttribute | undefined;\n\t\tconst end = geometry.attributes.instanceEnd as THREE.InterleavedBufferAttribute | undefined;\n\t\tconst i = hit.faceIndex;\n\t\tif (!start || !end || i == null || i >= start.count) return null;\n\t\treturn [\n\t\t\tnew THREE.Vector3().fromBufferAttribute(start, i),\n\t\t\tnew THREE.Vector3().fromBufferAttribute(end, i)\n\t\t];\n\t}\n\n\tconst pos = geometry.attributes.position as THREE.BufferAttribute | undefined;\n\tif (!pos) return null;\n\tconst at = (indices: number[]) =>\n\t\tindices\n\t\t\t.filter((i) => i < pos.count)\n\t\t\t.map((i) => new THREE.Vector3().fromBufferAttribute(pos, i));\n\n\tif (obj instanceof THREE.Mesh) {\n\t\treturn hit.face ? at([hit.face.a, hit.face.b, hit.face.c]) : null;\n\t}\n\tif (obj instanceof THREE.Points) {\n\t\t// Points.raycast resolves indexed geometry itself: `hit.index` is always a position index.\n\t\treturn hit.index != null ? at([hit.index]) : null;\n\t}\n\t// THREE.Line / LineSegments / LineLoop. For non-indexed geometry `hit.index` is the first\n\t// vertex of the struck segment. For indexed geometry it's a cursor into the index buffer, not\n\t// the resolved vertex (three r184 Line.raycast reports the loop counter) — endpoints must be\n\t// looked up through the index before reading the position attribute.\n\tif (obj instanceof THREE.Line) {\n\t\tif (hit.index == null) return null;\n\t\tconst index = geometry.index;\n\t\tif (index) {\n\t\t\tif (hit.index + 1 >= index.count) return null; // stale/inconsistent hit; keep raw point\n\t\t\treturn at([index.getX(hit.index), index.getX(hit.index + 1)]);\n\t\t}\n\t\treturn at([hit.index, hit.index + 1]);\n\t}\n\treturn null;\n}\n\nfunction toScreen(\n\tworld: THREE.Vector3,\n\tcamera: THREE.Camera,\n\tscreenSize: { width: number; height: number }\n): THREE.Vector2 {\n\tconst ndc = world.clone().project(camera);\n\treturn new THREE.Vector2(\n\t\t((ndc.x + 1) / 2) * screenSize.width,\n\t\t((1 - ndc.y) / 2) * screenSize.height\n\t);\n}\n\n/** Where a hit sits on screen: fat lines report the nearest point on the line separately. */\nfunction hitPoint(hit: THREE.Intersection): THREE.Vector3 {\n\treturn (hit as THREE.Intersection & { pointOnLine?: THREE.Vector3 }).pointOnLine ?? hit.point;\n}\n\ninterface SnapResult {\n\tpoint: THREE.Vector3;\n\tsnapped: boolean;\n}\n\nfunction snapHit(\n\thit: THREE.Intersection,\n\tcamera: THREE.Camera,\n\tscreenSize: { width: number; height: number },\n\tsnapPixels: number\n): SnapResult {\n\tconst raw = hitPoint(hit).clone();\n\tconst candidates = snapCandidates(hit);\n\tif (!candidates) return { point: raw, snapped: false };\n\n\tconst rawScreen = toScreen(raw, camera, screenSize);\n\tlet best: SnapResult = { point: raw, snapped: false };\n\tlet bestPx = snapPixels;\n\tfor (const local of candidates) {\n\t\tconst world = local.applyMatrix4(hit.object.matrixWorld);\n\t\tconst px = toScreen(world, camera, screenSize).distanceTo(rawScreen);\n\t\tif (px < bestPx) {\n\t\t\tbestPx = px;\n\t\t\tbest = { point: world, snapped: true };\n\t\t}\n\t}\n\treturn best;\n}\n\n/** Snap a raycast hit to the nearest geometry vertex within `snapPixels` on screen, else the raw hit point. */\nexport function snapToVertex(\n\thit: THREE.Intersection,\n\tcamera: THREE.Camera,\n\tscreenSize: { width: number; height: number },\n\tsnapPixels: number\n): THREE.Vector3 {\n\treturn snapHit(hit, camera, screenSize, snapPixels).point;\n}\n\ninterface Measurement {\n\tmarkers: THREE.Points;\n\tline: Line2;\n\tlabel: LabelHandle;\n}\n\nexport function createMeasureTool(deps: MeasureDeps): MeasureTool {\n\tconst { canvas, scene, getActiveCamera, getViewTarget, labelLayer, options = {} } = deps;\n\tconst requestRender = deps.requestRender ?? (() => {});\n\tconst snapPixels = options.snapPixels ?? DEFAULT_SNAP_PIXELS;\n\tconst color = new THREE.Color(options.color ?? DEFAULT_COLOR);\n\tconst format = options.format ?? makeDefaultFormat(makeFormatter(options.displayUnit));\n\n\tconst raycaster = new THREE.Raycaster();\n\tconst pointer = new THREE.Vector2();\n\n\tlet enabled = false;\n\tlet start: { point: THREE.Vector3; marker: THREE.Points } | null = null;\n\tconst measurements: Measurement[] = [];\n\n\t// depthTest off throughout: markers and lines stay visible through geometry, like CAD snap dots.\n\tconst markerMaterial = new THREE.PointsMaterial({\n\t\tcolor,\n\t\tsize: 8,\n\t\tsizeAttenuation: false,\n\t\tdepthTest: false\n\t});\n\t// Solid and larger once snapped to a vertex; small and faint on a free surface point, so the\n\t// user sees whether the next click lands on a vertex before clicking.\n\tconst hoverSnappedMaterial = new THREE.PointsMaterial({\n\t\tcolor,\n\t\tsize: 12,\n\t\tsizeAttenuation: false,\n\t\tdepthTest: false\n\t});\n\tconst hoverFreeMaterial = new THREE.PointsMaterial({\n\t\tcolor,\n\t\tsize: 7,\n\t\tsizeAttenuation: false,\n\t\tdepthTest: false,\n\t\ttransparent: true,\n\t\topacity: 0.6\n\t});\n\n\tconst tag = <T extends THREE.Object3D>(object: T, renderOrder: number): T => {\n\t\tobject.renderOrder = renderOrder;\n\t\tobject.userData.id = 'measure';\n\t\tobject.raycast = () => {}; // never a measure target itself\n\t\tobject.frustumCulled = false; // positions are rewritten in place; a stale sphere would cull\n\t\treturn object;\n\t};\n\n\tconst makePoints = (...ps: THREE.Vector3[]): THREE.Points => {\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tgeometry.setAttribute(\n\t\t\t'position',\n\t\t\tnew THREE.Float32BufferAttribute(\n\t\t\t\tps.flatMap((p) => [p.x, p.y, p.z]),\n\t\t\t\t3\n\t\t\t)\n\t\t);\n\t\tconst points = tag(new THREE.Points(geometry, markerMaterial), RENDER_ORDER_MARKER);\n\t\tscene.add(points);\n\t\treturn points;\n\t};\n\n\tconst makeLine = (a: THREE.Vector3, b: THREE.Vector3, opacity = 1): Line2 => {\n\t\tconst geometry = new LineGeometry();\n\t\tgeometry.setPositions([a.x, a.y, a.z, b.x, b.y, b.z]);\n\t\tconst material = new LineMaterial({ color, transparent: opacity < 1, opacity });\n\t\t(material as LineMaterial & { linewidth: number }).linewidth = 2;\n\t\tmaterial.depthTest = false;\n\t\tconst line = tag(new Line2(geometry, material), RENDER_ORDER_LINE);\n\t\tscene.add(line);\n\t\treturn line;\n\t};\n\n\t// Rewrites a two-point Line2 in place. `setPositions` would allocate new GPU buffers per frame.\n\tconst moveLine = (line: Line2, a: THREE.Vector3, b: THREE.Vector3) => {\n\t\tconst startAttr = line.geometry.attributes.instanceStart as THREE.InterleavedBufferAttribute;\n\t\tconst buffer = startAttr.data;\n\t\t(buffer.array as Float32Array).set([a.x, a.y, a.z, b.x, b.y, b.z]);\n\t\tbuffer.needsUpdate = true;\n\t};\n\n\tconst disposeLine = (line: Line2) => {\n\t\tline.geometry.dispose();\n\t\t(line.material as LineMaterial).dispose();\n\t\tline.removeFromParent();\n\t};\n\n\tconst disposePoints = (points: THREE.Points) => {\n\t\tpoints.geometry.dispose();\n\t\tpoints.removeFromParent();\n\t};\n\n\tconst labelText = (a: THREE.Vector3, b: THREE.Vector3) => {\n\t\tconst delta = new THREE.Vector3(Math.abs(b.x - a.x), Math.abs(b.y - a.y), Math.abs(b.z - a.z));\n\t\treturn format(a.distanceTo(b), delta);\n\t};\n\tconst midpoint = (a: THREE.Vector3, b: THREE.Vector3) => a.clone().add(b).multiplyScalar(0.5);\n\n\t// ==========================================================================\n\t// Hover preview: snap marker, plus the rubber band while a start point is set\n\t// ==========================================================================\n\n\tlet hoverMarker: THREE.Points | null = null;\n\tlet rubberBand: Line2 | null = null;\n\tlet rubberLabel: LabelHandle | null = null;\n\n\tconst hidePreview = () => {\n\t\tif (hoverMarker) hoverMarker.visible = false;\n\t\tif (rubberBand) rubberBand.visible = false;\n\t\trubberLabel?.remove();\n\t\trubberLabel = null;\n\t};\n\n\tconst showPreview = (snap: SnapResult | null) => {\n\t\tif (!snap) {\n\t\t\thidePreview();\n\t\t\treturn;\n\t\t}\n\t\tif (!hoverMarker) {\n\t\t\thoverMarker = makePoints(new THREE.Vector3());\n\t\t\thoverMarker.renderOrder = RENDER_ORDER_HOVER;\n\t\t}\n\t\thoverMarker.material = snap.snapped ? hoverSnappedMaterial : hoverFreeMaterial;\n\t\thoverMarker.position.copy(snap.point);\n\t\thoverMarker.visible = true;\n\n\t\tif (!start) {\n\t\t\tif (rubberBand) rubberBand.visible = false;\n\t\t\trubberLabel?.remove();\n\t\t\trubberLabel = null;\n\t\t\treturn;\n\t\t}\n\t\tif (!rubberBand) rubberBand = makeLine(start.point, snap.point, 0.7);\n\t\tmoveLine(rubberBand, start.point, snap.point);\n\t\trubberBand.visible = true;\n\t\tconst text = labelText(start.point, snap.point);\n\t\tconst mid = midpoint(start.point, snap.point);\n\t\tif (rubberLabel) {\n\t\t\trubberLabel.setText(text);\n\t\t\trubberLabel.setPosition(mid);\n\t\t} else {\n\t\t\trubberLabel = labelLayer.addLabel(text, mid, options.labelClassName);\n\t\t}\n\t};\n\n\t// ==========================================================================\n\t// Picking\n\t// ==========================================================================\n\n\tconst pick = (event: MouseEvent): SnapResult | null => {\n\t\tconst rect = canvas.getBoundingClientRect();\n\t\tconst cursorX = event.clientX - rect.left;\n\t\tconst cursorY = event.clientY - rect.top;\n\t\tpointer.x = (cursorX / rect.width) * 2 - 1;\n\t\tpointer.y = -(cursorY / rect.height) * 2 + 1;\n\n\t\tconst camera = getActiveCamera();\n\t\traycaster.setFromCamera(pointer, camera);\n\n\t\t// Lines/points have no surface area, so the raycast threshold matters — three's default is\n\t\t// nearly unclickable. The world-space band is generous (2x); the screen-space test below is\n\t\t// what decides, so it can't grab a line that is far off on screen but near in depth.\n\t\tconst threshold = pickThreshold(camera, getViewTarget?.()) * 2;\n\t\traycaster.params.Line!.threshold = threshold;\n\t\traycaster.params.Points!.threshold = threshold;\n\t\t// Fat lines (Line2) measure their band in pixels on top of the line width.\n\t\t(raycaster.params as { Line2?: { threshold: number } }).Line2 = { threshold: snapPixels / 2 };\n\n\t\tconst screen = { width: rect.width, height: rect.height };\n\t\tconst cursor = new THREE.Vector2(cursorX, cursorY);\n\t\tfor (const hit of raycastVisible(scene, raycaster)) {\n\t\t\tconst isSurface = (hit.object as THREE.Mesh).isMesh && hit.face != null;\n\t\t\tif (!isSurface && toScreen(hitPoint(hit), camera, screen).distanceTo(cursor) > snapPixels) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn snapHit(hit, camera, screen, snapPixels);\n\t\t}\n\t\treturn null;\n\t};\n\n\t// One pick per animation frame, not per mousemove: only the latest event matters for the preview.\n\tlet pendingMove: MouseEvent | null = null;\n\tlet moveRaf = 0;\n\n\tconst cancelPendingMove = () => {\n\t\tif (moveRaf) {\n\t\t\tcancelAnimationFrame(moveRaf);\n\t\t\tmoveRaf = 0;\n\t\t}\n\t\tpendingMove = null;\n\t};\n\n\tconst handleMove = (event: MouseEvent): void => {\n\t\tif (!enabled) return;\n\t\tpendingMove = event;\n\t\tif (moveRaf) return;\n\t\tmoveRaf = requestAnimationFrame(() => {\n\t\t\tmoveRaf = 0;\n\t\t\tconst latest = pendingMove;\n\t\t\tpendingMove = null;\n\t\t\tif (!enabled || !latest) return;\n\t\t\tshowPreview(pick(latest));\n\t\t\trequestRender();\n\t\t});\n\t};\n\n\tconst handleLeave = () => {\n\t\tcancelPendingMove();\n\t\thidePreview();\n\t\trequestRender();\n\t};\n\n\t// ==========================================================================\n\t// Committing and clearing\n\t// ==========================================================================\n\n\tconst dropStart = () => {\n\t\tif (!start) return;\n\t\tdisposePoints(start.marker);\n\t\tstart = null;\n\t};\n\n\tconst clear = () => {\n\t\tdropStart();\n\t\tfor (const m of measurements) {\n\t\t\tdisposePoints(m.markers);\n\t\t\tdisposeLine(m.line);\n\t\t\tm.label.remove();\n\t\t}\n\t\tmeasurements.length = 0;\n\t\thidePreview();\n\t\trequestRender();\n\t};\n\n\tconst cancel = () => {\n\t\tif (start) {\n\t\t\tdropStart();\n\t\t\thidePreview();\n\t\t\trequestRender();\n\t\t} else {\n\t\t\tclear();\n\t\t}\n\t};\n\n\tconst handleClick = (event: MouseEvent): boolean => {\n\t\tif (!enabled) return false;\n\t\tcancelPendingMove(); // a stale preview frame must not redraw the band after commit\n\n\t\tconst snap = pick(event);\n\t\tif (!snap) return true; // consumed: a measuring click that missed still isn't a select\n\n\t\tif (!start) {\n\t\t\tstart = { point: snap.point, marker: makePoints(snap.point) };\n\t\t} else if (!snap.point.equals(start.point)) {\n\t\t\tconst a = start.point;\n\t\t\tconst b = snap.point;\n\t\t\tdropStart();\n\t\t\tmeasurements.push({\n\t\t\t\tmarkers: makePoints(a, b),\n\t\t\t\tline: makeLine(a, b),\n\t\t\t\tlabel: labelLayer.addLabel(labelText(a, b), midpoint(a, b), options.labelClassName)\n\t\t\t});\n\t\t}\n\t\tshowPreview(snap);\n\t\trequestRender();\n\t\treturn true;\n\t};\n\n\tconst handleKey = (event: KeyboardEvent) => {\n\t\tif (event.key !== 'Escape') return;\n\t\tconst target = event.target as HTMLElement | null;\n\t\tif (target?.closest?.('input, textarea, select, [contenteditable]')) return;\n\t\tcancel();\n\t};\n\n\t// ==========================================================================\n\t// Enable / dispose\n\t// ==========================================================================\n\n\tlet previousCursor = '';\n\tlet cancelWarm: (() => void) | null = null;\n\n\tconst setEnabled = (value: boolean) => {\n\t\tif (value === enabled) return;\n\t\tenabled = value;\n\t\tif (value) {\n\t\t\tpreviousCursor = canvas.style.cursor;\n\t\t\tcanvas.style.cursor = 'crosshair';\n\t\t\tcanvas.addEventListener('mouseleave', handleLeave);\n\t\t\twindow.addEventListener('keydown', handleKey);\n\t\t\tcancelWarm = warmPickIndex(scene);\n\t\t} else {\n\t\t\tcanvas.style.cursor = previousCursor;\n\t\t\tcanvas.removeEventListener('mouseleave', handleLeave);\n\t\t\twindow.removeEventListener('keydown', handleKey);\n\t\t\tcancelWarm?.();\n\t\t\tcancelWarm = null;\n\t\t\tcancelPendingMove();\n\t\t\tclear();\n\t\t}\n\t};\n\n\treturn {\n\t\tsetEnabled,\n\t\tisEnabled: () => enabled,\n\t\thandleClick,\n\t\thandleMove,\n\t\tcancel,\n\t\tclear,\n\t\tdispose: () => {\n\t\t\tsetEnabled(false);\n\t\t\tclear();\n\t\t\tif (hoverMarker) disposePoints(hoverMarker);\n\t\t\tif (rubberBand) disposeLine(rubberBand);\n\t\t\thoverMarker = null;\n\t\t\trubberBand = null;\n\t\t\tmarkerMaterial.dispose();\n\t\t\thoverSnappedMaterial.dispose();\n\t\t\thoverFreeMaterial.dispose();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\n\nimport { EDGE_USERDATA_KIND } from './edges/options.js';\nimport { membersOf } from './scene-setup/merged-picking.js';\nimport { isViewerAidRoot } from './three-helpers.js';\n\n/**\n * Depth-buffer precision is ∝ near/z²: at the 0.01 m floor a 24-bit buffer resolves ~0.15 mm at\n * 5 m and ~0.25 m at 200 m, so a sheet's two faces fight up close and coplanar surfaces fight far\n * away. Pushing `near` up to a fraction of the view depth of the nearest geometry in view recovers\n * that precision without clipping anything.\n *\n * The nearest geometry is measured on per-chunk triangle bounds, not the scene's bounding sphere:\n * zoomed in on a detail, the camera sits inside the whole model's sphere, which pinned near to the\n * floor exactly when precision mattered most. Chunks outside the frustum's side planes are skipped,\n * since geometry nobody sees can't be clipped.\n *\n * `far` stays owned by config/`updateScene` (must keep covering grid fade, floor). External writes\n * to `camera.near` are adopted as the new lower bound rather than fought — the fitter only ever\n * *raises* near above that floor.\n *\n * Ortho camera needs no fitting (linear depth) and is left untouched.\n */\n\n/** Safety margin: a host's `onFrame` can still move the camera after the fit. */\nconst NEAR_GAP_FRACTION = 0.5;\n/** Caps near so the frustum stays sane if the camera flies out. */\nconst MAX_NEAR_TO_FAR = 0.01;\n/** Skip sub-5% changes so the projection matrix isn't rebuilt every frame while orbiting. */\nconst APPLY_THRESHOLD = 0.05;\n/**\n * Triangles per bounding box. Smaller fits tighter up close but costs more box tests per frame:\n * at 256, a 2M-triangle model is ~8k tests, well under a millisecond.\n */\nconst TRIANGLES_PER_CHUNK = 256;\n\nexport interface NearPlaneFitterOptions {\n\tcamera: THREE.PerspectiveCamera;\n\tscene: THREE.Scene;\n\t/**\n\t * Unit normals of ground planes through the origin carrying ground aids (grid, floor) — their\n\t * perpendicular distance to the camera also bounds near, since the aid re-centers under the\n\t * camera and content distance alone would clip it at grazing views.\n\t *\n\t * Pass a callback returning only planes whose aid is visible this frame: an aid that's hidden\n\t * (not merely absent) must not collapse `near` to protect geometry nobody can see.\n\t */\n\tgroundNormals?: () => THREE.Vector3[];\n}\n\nexport interface NearPlaneFitter {\n\tupdate: () => void;\n}\n\nconst NO_GROUND_NORMALS: THREE.Vector3[] = [];\n\nexport function createNearPlaneFitter({\n\tcamera,\n\tscene,\n\tgroundNormals = () => NO_GROUND_NORMALS\n}: NearPlaneFitterOptions): NearPlaneFitter {\n\tlet baseNear = camera.near;\n\tlet appliedNear = camera.near;\n\n\tconst nearest = createNearestDepthQuery();\n\n\tconst update = () => {\n\t\tif (camera.near !== appliedNear) baseNear = camera.near; // external write → new floor\n\n\t\tconst depth = nearest.measure(scene, camera);\n\t\tlet near = baseNear;\n\t\tif (depth !== null) {\n\t\t\tlet gap = depth;\n\t\t\tfor (const normal of groundNormals()) {\n\t\t\t\tgap = Math.min(gap, Math.abs(camera.position.dot(normal)));\n\t\t\t}\n\t\t\tnear = THREE.MathUtils.clamp(gap * NEAR_GAP_FRACTION, baseNear, camera.far * MAX_NEAR_TO_FAR);\n\t\t}\n\n\t\tif (Math.abs(near - appliedNear) > appliedNear * APPLY_THRESHOLD) {\n\t\t\tcamera.near = near;\n\t\t\tcamera.updateProjectionMatrix();\n\t\t\tappliedNear = near;\n\t\t}\n\t};\n\n\treturn { update };\n}\n\n// ============================================================================\n// Nearest view depth\n// ============================================================================\n\ninterface Chunks {\n\t/** min xyz, max xyz per chunk, in the geometry's local space. */\n\tboxes: Float32Array;\n\t/** First and past-the-end triangle per chunk. */\n\tranges: Uint32Array;\n}\n\ninterface ChunkBounds extends Chunks {\n\tposition: THREE.BufferAttribute | THREE.InterleavedBufferAttribute;\n\tpositionVersion: number;\n\tindex: THREE.BufferAttribute | null;\n\tindexVersion: number;\n}\n\n/**\n * Smallest view depth (distance along the camera's forward axis) of any content inside the\n * frustum's side planes: `null` when the scene has no content, `Infinity` when none of it is in\n * view.\n *\n * A chunk's box only bounds that depth from below, and loosely: a big box reaches beside and behind\n * the camera even when its triangles are all well ahead. One 60-triangle wall around a building is\n * one box the size of the building, which used to pin near to the floor from anywhere in it. So a\n * box that could still be the nearest has its triangles clipped to the frustum and measured\n * exactly; boxes already further than the best so far are skipped.\n */\nfunction createNearestDepthQuery() {\n\tconst chunkCache = new WeakMap<THREE.BufferGeometry, ChunkBounds>();\n\tconst frustum = new THREE.Frustum();\n\tconst viewProjection = new THREE.Matrix4();\n\tconst forward = new THREE.Vector3();\n\tconst box = new THREE.Box3();\n\t// Four side planes then the depth plane, each (a, b, c, d) with a·x + b·y + c·z + d.\n\tconst worldPlanes = new Float64Array(20);\n\tconst localPlanes = new Float64Array(20);\n\tconst singleBox = new Float32Array(6);\n\t// Sutherland–Hodgman ping-pong buffers: a triangle clipped by four planes keeps at most 7 corners.\n\tlet polygon = new Float64Array(21);\n\tlet clipped = new Float64Array(21);\n\n\tlet nearest = Infinity;\n\tlet sawContent = false;\n\n\tconst chunksOf = (object: THREE.Object3D, geometry: THREE.BufferGeometry): ChunkBounds => {\n\t\tconst position = geometry.getAttribute('position');\n\t\tconst index = geometry.getIndex();\n\t\tconst cached = chunkCache.get(geometry);\n\t\tif (\n\t\t\tcached &&\n\t\t\tcached.position === position &&\n\t\t\tcached.positionVersion === versionOf(position) &&\n\t\t\tcached.index === index &&\n\t\t\tcached.indexVersion === (index?.version ?? -1)\n\t\t) {\n\t\t\treturn cached;\n\t\t}\n\t\tconst built: ChunkBounds = {\n\t\t\t...buildChunkBoxes(position, index, memberStarts(object)),\n\t\t\tposition,\n\t\t\tpositionVersion: versionOf(position),\n\t\t\tindex,\n\t\t\tindexVersion: index?.version ?? -1\n\t\t};\n\t\tchunkCache.set(geometry, built);\n\t\treturn built;\n\t};\n\n\t/** Exact nearest depth of a chunk's triangles inside the frustum's side planes. */\n\tconst chunkDepth = (chunks: ChunkBounds, chunk: number, planes: Float64Array): number => {\n\t\tconst { position, index } = chunks;\n\t\tconst indices = index?.array as ArrayLike<number> | undefined;\n\t\tlet best = Infinity;\n\t\tfor (let t = chunks.ranges[chunk * 2]!; t < chunks.ranges[chunk * 2 + 1]!; t++) {\n\t\t\tfor (let corner = 0; corner < 3; corner++) {\n\t\t\t\tconst v = indices ? indices[t * 3 + corner]! : t * 3 + corner;\n\t\t\t\tpolygon[corner * 3] = position.getX(v);\n\t\t\t\tpolygon[corner * 3 + 1] = position.getY(v);\n\t\t\t\tpolygon[corner * 3 + 2] = position.getZ(v);\n\t\t\t}\n\t\t\tlet count = 3;\n\t\t\tfor (let p = 0; p < 16 && count > 0; p += 4) {\n\t\t\t\tconst a = planes[p]!;\n\t\t\t\tconst b = planes[p + 1]!;\n\t\t\t\tconst c = planes[p + 2]!;\n\t\t\t\tconst d = planes[p + 3]!;\n\t\t\t\tlet kept = 0;\n\t\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\t\tconst j = (i + 1) % count;\n\t\t\t\t\tconst ix = polygon[i * 3]!;\n\t\t\t\t\tconst iy = polygon[i * 3 + 1]!;\n\t\t\t\t\tconst iz = polygon[i * 3 + 2]!;\n\t\t\t\t\tconst jx = polygon[j * 3]!;\n\t\t\t\t\tconst jy = polygon[j * 3 + 1]!;\n\t\t\t\t\tconst jz = polygon[j * 3 + 2]!;\n\t\t\t\t\tconst di = a * ix + b * iy + c * iz + d;\n\t\t\t\t\tconst dj = a * jx + b * jy + c * jz + d;\n\t\t\t\t\tif (di >= 0) {\n\t\t\t\t\t\tclipped[kept * 3] = ix;\n\t\t\t\t\t\tclipped[kept * 3 + 1] = iy;\n\t\t\t\t\t\tclipped[kept * 3 + 2] = iz;\n\t\t\t\t\t\tkept++;\n\t\t\t\t\t}\n\t\t\t\t\tif (di >= 0 !== dj >= 0) {\n\t\t\t\t\t\tconst f = di / (di - dj);\n\t\t\t\t\t\tclipped[kept * 3] = ix + (jx - ix) * f;\n\t\t\t\t\t\tclipped[kept * 3 + 1] = iy + (jy - iy) * f;\n\t\t\t\t\t\tclipped[kept * 3 + 2] = iz + (jz - iz) * f;\n\t\t\t\t\t\tkept++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t[polygon, clipped] = [clipped, polygon];\n\t\t\t\tcount = kept;\n\t\t\t}\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tconst depth =\n\t\t\t\t\tplanes[16]! * polygon[i * 3]! +\n\t\t\t\t\tplanes[17]! * polygon[i * 3 + 1]! +\n\t\t\t\t\tplanes[18]! * polygon[i * 3 + 2]! +\n\t\t\t\t\tplanes[19]!;\n\t\t\t\tif (depth < best) best = depth;\n\t\t\t}\n\t\t}\n\t\treturn best;\n\t};\n\n\t// Moves the world planes into the object's local space instead of moving every box into world\n\t// space: p_world = L·p + t, so n·p_world + d = (Lᵀn)·p + (n·t + d).\n\tconst toLocalPlanes = (matrix: THREE.Matrix4): Float64Array => {\n\t\tconst e = matrix.elements;\n\t\tfor (let i = 0; i < 20; i += 4) {\n\t\t\tconst nx = worldPlanes[i]!;\n\t\t\tconst ny = worldPlanes[i + 1]!;\n\t\t\tconst nz = worldPlanes[i + 2]!;\n\t\t\tlocalPlanes[i] = e[0]! * nx + e[1]! * ny + e[2]! * nz;\n\t\t\tlocalPlanes[i + 1] = e[4]! * nx + e[5]! * ny + e[6]! * nz;\n\t\t\tlocalPlanes[i + 2] = e[8]! * nx + e[9]! * ny + e[10]! * nz;\n\t\t\tlocalPlanes[i + 3] = nx * e[12]! + ny * e[13]! + nz * e[14]! + worldPlanes[i + 3]!;\n\t\t}\n\t\treturn localPlanes;\n\t};\n\n\tconst testBoxes = (boxes: Float32Array, planes: Float64Array, chunks?: ChunkBounds) => {\n\t\touter: for (let b = 0; b < boxes.length; b += 6) {\n\t\t\tconst minX = boxes[b]!;\n\t\t\tconst minY = boxes[b + 1]!;\n\t\t\tconst minZ = boxes[b + 2]!;\n\t\t\tconst maxX = boxes[b + 3]!;\n\t\t\tconst maxY = boxes[b + 4]!;\n\t\t\tconst maxZ = boxes[b + 5]!;\n\t\t\tfor (let p = 0; p < 16; p += 4) {\n\t\t\t\tconst a = planes[p]!;\n\t\t\t\tconst bb = planes[p + 1]!;\n\t\t\t\tconst c = planes[p + 2]!;\n\t\t\t\tconst farthest =\n\t\t\t\t\ta * (a > 0 ? maxX : minX) +\n\t\t\t\t\tbb * (bb > 0 ? maxY : minY) +\n\t\t\t\t\tc * (c > 0 ? maxZ : minZ) +\n\t\t\t\t\tplanes[p + 3]!;\n\t\t\t\tif (farthest < 0) continue outer;\n\t\t\t}\n\t\t\tconst a = planes[16]!;\n\t\t\tconst bb = planes[17]!;\n\t\t\tconst c = planes[18]!;\n\t\t\tconst depth =\n\t\t\t\ta * (a > 0 ? minX : maxX) +\n\t\t\t\tbb * (bb > 0 ? minY : maxY) +\n\t\t\t\tc * (c > 0 ? minZ : maxZ) +\n\t\t\t\tplanes[19]!;\n\t\t\tif (depth >= nearest) continue;\n\t\t\tnearest = chunks ? Math.min(nearest, chunkDepth(chunks, b / 6, planes)) : depth;\n\t\t}\n\t};\n\n\tconst measureObject = (object: THREE.Object3D) => {\n\t\tconst renderable = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\tconst geometry = renderable.geometry;\n\t\tif (!geometry?.getAttribute?.('position')) return;\n\t\tsawContent = true;\n\n\t\tif (isPlainTriangleMesh(object)) {\n\t\t\tconst chunks = chunksOf(object, geometry);\n\t\t\ttestBoxes(chunks.boxes, toLocalPlanes(object.matrixWorld), chunks);\n\t\t\treturn;\n\t\t}\n\n\t\t// Lines, points, instanced/skinned meshes: one box for the whole object.\n\t\tconst instanced = object as Partial<THREE.InstancedMesh>;\n\t\tif (instanced.isInstancedMesh) {\n\t\t\tif (!instanced.boundingBox) instanced.computeBoundingBox!();\n\t\t\tbox.copy(instanced.boundingBox!);\n\t\t} else {\n\t\t\tif (!geometry.boundingBox) geometry.computeBoundingBox();\n\t\t\tbox.copy(geometry.boundingBox!);\n\t\t}\n\t\tif (box.isEmpty()) return;\n\t\tsingleBox[0] = box.min.x;\n\t\tsingleBox[1] = box.min.y;\n\t\tsingleBox[2] = box.min.z;\n\t\tsingleBox[3] = box.max.x;\n\t\tsingleBox[4] = box.max.y;\n\t\tsingleBox[5] = box.max.z;\n\t\ttestBoxes(singleBox, toLocalPlanes(object.matrixWorld));\n\t};\n\n\t// Prunes whole subtrees: hidden objects, viewer aids, and edge overlays (they trace their parent\n\t// mesh, and their one big box would defeat the parent's chunking).\n\tconst visit = (object: THREE.Object3D) => {\n\t\tif (!object.visible || isViewerAidRoot(object)) return;\n\t\tif (object.userData.kind === EDGE_USERDATA_KIND) return;\n\t\tmeasureObject(object);\n\t\tfor (const child of object.children) visit(child);\n\t};\n\n\tconst measure = (scene: THREE.Scene, camera: THREE.PerspectiveCamera): number | null => {\n\t\tscene.updateMatrixWorld();\n\t\tcamera.updateMatrixWorld();\n\n\t\tviewProjection.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);\n\t\tfrustum.setFromProjectionMatrix(viewProjection);\n\t\t// planes[0..3] are the side planes; [4]/[5] are far/near, which this must ignore — near is\n\t\t// what's being solved for, and content past far isn't drawn regardless of near.\n\t\tfor (let i = 0; i < 4; i++) {\n\t\t\tconst plane = frustum.planes[i]!;\n\t\t\tworldPlanes[i * 4] = plane.normal.x;\n\t\t\tworldPlanes[i * 4 + 1] = plane.normal.y;\n\t\t\tworldPlanes[i * 4 + 2] = plane.normal.z;\n\t\t\tworldPlanes[i * 4 + 3] = plane.constant;\n\t\t}\n\t\tcamera.getWorldDirection(forward);\n\t\tworldPlanes[16] = forward.x;\n\t\tworldPlanes[17] = forward.y;\n\t\tworldPlanes[18] = forward.z;\n\t\tworldPlanes[19] = -forward.dot(camera.position);\n\n\t\tnearest = Infinity;\n\t\tsawContent = false;\n\t\tfor (const child of scene.children) visit(child);\n\t\treturn sawContent ? nearest : null;\n\t};\n\n\treturn { measure };\n}\n\nfunction isPlainTriangleMesh(object: THREE.Object3D): boolean {\n\tconst mesh = object as Partial<THREE.Mesh & THREE.InstancedMesh & THREE.SkinnedMesh>;\n\tif (!mesh.isMesh || mesh.isInstancedMesh || mesh.isSkinnedMesh) return false;\n\tconst geometry = mesh.geometry as THREE.BufferGeometry & { isInstancedBufferGeometry?: boolean };\n\t// Line2's `position` is a template quad; its points live in instance attributes, which only\n\t// the geometry's own computeBoundingBox reads.\n\tif (geometry.isInstancedBufferGeometry) return false;\n\treturn Object.keys(geometry.morphAttributes).length === 0;\n}\n\nfunction versionOf(attribute: THREE.BufferAttribute | THREE.InterleavedBufferAttribute): number {\n\treturn (attribute as THREE.InterleavedBufferAttribute).isInterleavedBufferAttribute\n\t\t? (attribute as THREE.InterleavedBufferAttribute).data.version\n\t\t: (attribute as THREE.BufferAttribute).version;\n}\n\n/**\n * Triangle offsets where a merged mesh's source objects begin. A chunk spanning two of them would\n * box the empty space between, and a camera parked in that space would read as touching geometry.\n */\nfunction memberStarts(object: THREE.Object3D): number[] {\n\tconst starts: number[] = [];\n\tfor (const member of membersOf(object) ?? []) {\n\t\tif (member.indexStart !== undefined) starts.push(member.indexStart / 3);\n\t}\n\treturn starts.sort((a, b) => a - b);\n}\n\n/**\n * Bounds of consecutive runs of triangles, never crossing a `breaks` offset. Grouped by triangle,\n * not by vertex, so each box holds whole triangles: a big face whose corners all sit off-screen\n * still overlaps the frustum.\n */\nfunction buildChunkBoxes(\n\tposition: THREE.BufferAttribute | THREE.InterleavedBufferAttribute,\n\tindex: THREE.BufferAttribute | null,\n\tbreaks: number[]\n): Chunks {\n\tconst triangleCount = Math.floor((index ? index.count : position.count) / 3);\n\tconst ranges: number[] = [];\n\tlet nextBreak = 0;\n\tfor (let start = 0; start < triangleCount;) {\n\t\twhile (nextBreak < breaks.length && breaks[nextBreak]! <= start) nextBreak++;\n\t\tconst end = Math.min(\n\t\t\ttriangleCount,\n\t\t\tstart + TRIANGLES_PER_CHUNK,\n\t\t\tbreaks[nextBreak] ?? triangleCount\n\t\t);\n\t\tranges.push(start, end);\n\t\tstart = end;\n\t}\n\tconst chunkCount = ranges.length / 2;\n\tconst boxes = new Float32Array(chunkCount * 6);\n\tconst plain = !(position as THREE.InterleavedBufferAttribute).isInterleavedBufferAttribute;\n\tconst array = position.array as ArrayLike<number>;\n\tconst fast = plain && position.itemSize === 3 && !position.normalized;\n\tconst indices = index?.array as ArrayLike<number> | undefined;\n\n\tfor (let chunk = 0; chunk < chunkCount; chunk++) {\n\t\tlet minX = Infinity;\n\t\tlet minY = Infinity;\n\t\tlet minZ = Infinity;\n\t\tlet maxX = -Infinity;\n\t\tlet maxY = -Infinity;\n\t\tlet maxZ = -Infinity;\n\t\tconst end = ranges[chunk * 2 + 1]! * 3;\n\t\tfor (let corner = ranges[chunk * 2]! * 3; corner < end; corner++) {\n\t\t\tconst v = indices ? indices[corner]! : corner;\n\t\t\tconst x = fast ? array[v * 3]! : position.getX(v);\n\t\t\tconst y = fast ? array[v * 3 + 1]! : position.getY(v);\n\t\t\tconst z = fast ? array[v * 3 + 2]! : position.getZ(v);\n\t\t\tif (x < minX) minX = x;\n\t\t\tif (y < minY) minY = y;\n\t\t\tif (z < minZ) minZ = z;\n\t\t\tif (x > maxX) maxX = x;\n\t\t\tif (y > maxY) maxY = y;\n\t\t\tif (z > maxZ) maxZ = z;\n\t\t}\n\t\tconst o = chunk * 6;\n\t\tboxes[o] = minX;\n\t\tboxes[o + 1] = minY;\n\t\tboxes[o + 2] = minZ;\n\t\tboxes[o + 3] = maxX;\n\t\tboxes[o + 4] = maxY;\n\t\tboxes[o + 5] = maxZ;\n\t}\n\treturn { boxes, ranges: Uint32Array.from(ranges) };\n}\n","// ============================================================================\n// Pointer tools: who gets the click first\n// ============================================================================\n//\n// A pointer tool claims canvas input ahead of object selection — measuring a distance or placing\n// a vertex must not also select the mesh under the cursor. The registry owns the ordering and the\n// single-active rule; `initThree` owns the DOM listeners and forwards to it.\n\n/**\n * A tool that can claim canvas pointer input.\n *\n * `handleClick` returning true means the tool consumed the event and the host stops dispatching —\n * no further tool sees it, and object selection doesn't run. `handleMove` never consumes: it runs\n * on every move regardless of which tool is active, so previews can't block orbit or pan.\n */\nexport interface PointerTool {\n\tsetEnabled?(enabled: boolean): void;\n\tisEnabled?(): boolean;\n\t/** Returns true if this tool consumed the click. */\n\thandleClick(event: MouseEvent): boolean;\n\t/** Preview only — must not consume. */\n\thandleMove?(event: MouseEvent): void;\n\tclear?(): void;\n\tdispose?(): void;\n}\n\nexport interface ToolRegistration {\n\t/** Unique within the registry; registering the same id twice replaces the earlier tool. */\n\tid: string;\n\ttool: PointerTool;\n\t/**\n\t * Higher runs first. The built-ins sit at 0 (measure) and -100 (gizmo); register above 0 to\n\t * claim clicks before measuring, below -100 to act only as a fallback.\n\t */\n\tpriority?: number;\n}\n\nexport interface ToolRegistry {\n\t/** Returns an unregister function. Does not dispose the tool — the registrant still owns it. */\n\tregister(registration: ToolRegistration): () => void;\n\tunregister(id: string): void;\n\tget(id: string): PointerTool | null;\n\t/**\n\t * Enables one tool and disables every other registered one. Pass null to disable all.\n\t * Tools without `setEnabled` are always live and unaffected.\n\t */\n\tsetActive(id: string | null): void;\n\t/** The id passed to the last `setActive`, or null. */\n\tgetActive(): string | null;\n\t/** @internal — `initThree` forwards DOM events here. */\n\thandleClick(event: MouseEvent): boolean;\n\t/** @internal */\n\thandleMove(event: MouseEvent): void;\n}\n\nexport function createToolRegistry(): ToolRegistry {\n\tconst entries: Required<ToolRegistration>[] = [];\n\tlet activeId: string | null = null;\n\n\t// Descending priority, and registration order breaks ties — sort() is stable, so a tool\n\t// registered later never jumps ahead of an equal-priority one already there.\n\tconst sort = () => entries.sort((a, b) => b.priority - a.priority);\n\n\tconst indexOf = (id: string) => entries.findIndex((entry) => entry.id === id);\n\n\tconst unregister = (id: string) => {\n\t\tconst index = indexOf(id);\n\t\tif (index === -1) return;\n\t\tentries.splice(index, 1);\n\t\tif (activeId === id) activeId = null;\n\t};\n\n\tconst register = ({ id, tool, priority = 0 }: ToolRegistration) => {\n\t\tunregister(id);\n\t\tentries.push({ id, tool, priority });\n\t\tsort();\n\t\treturn () => unregister(id);\n\t};\n\n\tconst setActive = (id: string | null) => {\n\t\tactiveId = id;\n\t\tfor (const entry of entries) {\n\t\t\tentry.tool.setEnabled?.(entry.id === id);\n\t\t}\n\t};\n\n\treturn {\n\t\tregister,\n\t\tunregister,\n\t\tget: (id) => entries[indexOf(id)]?.tool ?? null,\n\t\tsetActive,\n\t\tgetActive: () => activeId,\n\t\thandleClick: (event) => {\n\t\t\t// Snapshot: a tool's handler may register or unregister during dispatch.\n\t\t\tfor (const entry of [...entries]) {\n\t\t\t\tif (entry.tool.handleClick(event)) return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\t\thandleMove: (event) => {\n\t\t\tfor (const entry of [...entries]) {\n\t\t\t\tentry.tool.handleMove?.(event);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Screen-space ray from a canvas mouse event, for tools doing their own picking. Handles the\n * canvas's position and size, so it stays correct under CSS scaling and in fullscreen.\n */\nexport function pointerToNdc(\n\tevent: MouseEvent,\n\tcanvas: HTMLCanvasElement\n): { x: number; y: number } {\n\tconst rect = canvas.getBoundingClientRect();\n\treturn {\n\t\tx: ((event.clientX - rect.left) / rect.width) * 2 - 1,\n\t\ty: -((event.clientY - rect.top) / rect.height) * 2 + 1\n\t};\n}\n","import * as THREE from 'three';\nimport { ViewHelper } from 'three/addons/helpers/ViewHelper.js';\n\nimport type { CameraController } from './camera-controller';\n\n/**\n * Corner nav-cube/axis gizmo. Uses three's {@link ViewHelper} only as the rendered widget, not its\n * click→animate behavior: ViewHelper's snap assumes Y-up and animates straight onto the up axis,\n * which rolls the view and jitters the gizmo at the pole in a Z-up scene. Instead this hit-tests\n * the axis sprites directly and drives the viewer's up-aware camera controller, which snaps\n * instantly with a pole nudge so the orbit basis never degenerates.\n *\n * A click frames the current orbit target (not the world origin) and switches back to perspective\n * first if orthographic — the cube is a 3D-orientation tool.\n *\n * Caller contract (mirrors ViewHelper's own): call {@link ViewGizmo.render} after the main scene\n * render each frame, and forward pointer clicks to {@link ViewGizmo.handleClick}.\n */\nexport interface ViewGizmo {\n\trender(renderer: THREE.WebGLRenderer): void;\n\t/** Returns true if it hit the gizmo (and a view change started). */\n\thandleClick(event: MouseEvent): boolean;\n\tsetVisible(visible: boolean): void;\n\tisVisible(): boolean;\n\tdispose(): void;\n}\n\ninterface ViewGizmoDeps {\n\tcamera: THREE.PerspectiveCamera;\n\tdomElement: HTMLElement;\n\tcontroller: CameraController;\n}\n\nexport function createViewGizmo(deps: ViewGizmoDeps): ViewGizmo {\n\tconst { camera, domElement, controller } = deps;\n\n\tconst helper = new ViewHelper(camera, domElement);\n\thelper.setLabels('X', 'Y', 'Z');\n\n\tlet visible = true;\n\n\t// Mirrors ViewHelper's internal `dim`×`dim` corner-viewport math.\n\tconst DIM = 128;\n\tconst raycaster = new THREE.Raycaster();\n\tconst gizmoCamera = new THREE.OrthographicCamera(-2, 2, 2, -2, 0, 4);\n\tgizmoCamera.position.set(0, 0, 2);\n\t// This camera is never rendered, so nothing else computes its matrixWorld — and\n\t// Raycaster.setFromCamera doesn't either. Without this the ray originates at the identity\n\t// position (z = 0, the cube's mid-plane) and the camera-facing axis sprites sit behind it.\n\tgizmoCamera.updateMatrixWorld();\n\n\t// target → camera, matching CameraController.setViewDirection.\n\tconst AXIS_DIRECTIONS: Record<string, THREE.Vector3> = {\n\t\tposX: new THREE.Vector3(1, 0, 0),\n\t\tnegX: new THREE.Vector3(-1, 0, 0),\n\t\tposY: new THREE.Vector3(0, 1, 0),\n\t\tnegY: new THREE.Vector3(0, -1, 0),\n\t\tposZ: new THREE.Vector3(0, 0, 1),\n\t\tnegZ: new THREE.Vector3(0, 0, -1)\n\t};\n\n\t// Returns the hit sprite's `userData.type`, or null if the click missed the gizmo.\n\tconst pickAxis = (event: MouseEvent): string | null => {\n\t\tconst rect = domElement.getBoundingClientRect();\n\t\t// Gizmo viewport sits in the bottom-right corner (helper.location defaults: right/bottom 0).\n\t\tconst offsetX = rect.left + domElement.offsetWidth - DIM - helper.location.right;\n\t\tconst offsetY = rect.top + domElement.offsetHeight - DIM - helper.location.bottom;\n\n\t\tconst mouse = new THREE.Vector2(\n\t\t\t((event.clientX - offsetX) / DIM) * 2 - 1,\n\t\t\t-((event.clientY - offsetY) / DIM) * 2 + 1\n\t\t);\n\t\tif (Math.abs(mouse.x) > 1 || Math.abs(mouse.y) > 1) return null;\n\n\t\t// Orient the helper as rendered (inverse of the camera) so sprites match what's on screen.\n\t\thelper.quaternion.copy(camera.quaternion).invert();\n\t\thelper.updateMatrixWorld();\n\n\t\traycaster.setFromCamera(mouse, gizmoCamera);\n\t\tconst hits = raycaster.intersectObjects(helper.children, false);\n\t\tfor (const hit of hits) {\n\t\t\tconst type = hit.object.userData?.type;\n\t\t\tif (typeof type === 'string' && type in AXIS_DIRECTIONS) return type;\n\t\t}\n\t\treturn null;\n\t};\n\n\tconst handleClick = (event: MouseEvent): boolean => {\n\t\tif (!visible) return false;\n\n\t\tconst axis = pickAxis(event);\n\t\tif (!axis) return false;\n\n\t\tif (controller.getProjection() === 'orthographic') {\n\t\t\tcontroller.setProjection('perspective');\n\t\t}\n\n\t\tcontroller.setViewDirection(AXIS_DIRECTIONS[axis]!, false);\n\t\treturn true;\n\t};\n\n\treturn {\n\t\trender: (renderer) => {\n\t\t\tif (!visible) return;\n\t\t\t// ViewHelper.render() calls renderer.render() with autoClear=true by default, which wipes\n\t\t\t// the FULL framebuffer before drawing the cube in its corner. Suppress it — ViewHelper\n\t\t\t// does its own depth clear internally.\n\t\t\tconst prevAutoClear = renderer.autoClear;\n\t\t\trenderer.autoClear = false;\n\t\t\thelper.render(renderer);\n\t\t\trenderer.autoClear = prevAutoClear;\n\t\t},\n\t\thandleClick,\n\t\tsetVisible: (value) => {\n\t\t\tvisible = value;\n\t\t},\n\t\tisVisible: () => visible,\n\t\tdispose: () => helper.dispose()\n\t};\n}\n","import * as THREE from 'three';\nimport type { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport type { CameraController } from '../camera-controller.js';\nimport type { Grid } from '../grid.js';\nimport type { LabelLayer } from '../label-layer.js';\nimport type { NearPlaneFitter } from '../near-plane.js';\nimport type { RenderPipeline } from '../render-pipeline.js';\nimport type { ViewGizmo } from '../view-gizmo.js';\nimport type { IdleRefiner } from './idle-refine.js';\n\n// Resize applied before render so buffer clear and draw happen in the same frame — avoids a\n// visible blank frame on resize.\nexport function createAnimationLoop(\n\trenderer: THREE.WebGLRenderer,\n\tscene: THREE.Scene,\n\tcamera: THREE.PerspectiveCamera,\n\tgetActiveCamera: () => THREE.Camera,\n\tcameraController: CameraController,\n\tcontrols: OrbitControls,\n\tgetCanvasSize: () => { width: number; height: number },\n\tgetPixelRatio: () => number,\n\tonFrame?: (delta: number) => void,\n\tgrid?: Grid | null,\n\tgizmo?: ViewGizmo | null,\n\tgetRenderPipeline?: () => RenderPipeline | null,\n\tlabelLayer?: LabelLayer | null,\n\tnearFitter?: NearPlaneFitter | null,\n\t// false = render every frame regardless of invalidate()/camera movement.\n\tonDemand: boolean = true,\n\trefiner?: IdleRefiner | null\n): { animate: () => void; dispose: () => void; invalidate: () => void; renderNow: () => void } {\n\tlet animationId: number | null = null;\n\tlet lastTime = performance.now();\n\n\t// The loop always *ticks* (cheap); it only *renders* when invalidate() was called, the active\n\t// camera moved (matrix compare catches damping/presets/gizmo/near-plane), or the idle-repaint\n\t// interval elapsed as a safety net for any mutation that forgot to invalidate.\n\tlet renderRequested = true; // first frame always renders\n\tlet lastRenderTime = 0;\n\tconst IDLE_REPAINT_INTERVAL_MS = 500;\n\tconst lastWorldMatrix = new THREE.Matrix4();\n\tconst lastProjectionMatrix = new THREE.Matrix4();\n\tlet lastCamera: THREE.Camera | null = null;\n\tconst invalidate = () => {\n\t\trenderRequested = true;\n\t};\n\n\tconst cameraMoved = (activeCamera: THREE.Camera): boolean => {\n\t\t// renderer.render normally refreshes matrixWorld, but we're deciding whether to call it — so\n\t\t// refresh here first (cheap: a camera has no deep subtree).\n\t\tactiveCamera.updateMatrixWorld();\n\t\tconst moved =\n\t\t\tlastCamera !== activeCamera ||\n\t\t\t!lastWorldMatrix.equals(activeCamera.matrixWorld) ||\n\t\t\t!lastProjectionMatrix.equals(activeCamera.projectionMatrix);\n\t\tif (moved) {\n\t\t\tlastCamera = activeCamera;\n\t\t\tlastWorldMatrix.copy(activeCamera.matrixWorld);\n\t\t\tlastProjectionMatrix.copy(activeCamera.projectionMatrix);\n\t\t}\n\t\treturn moved;\n\t};\n\n\t// Click-driven mutations (measure points, selection highlights) don't call invalidate() themselves.\n\tconst canvas = renderer.domElement;\n\tconst pointerEvents = ['pointerdown', 'pointerup', 'wheel'] as const;\n\tif (onDemand) {\n\t\tfor (const type of pointerEvents) {\n\t\t\tcanvas.addEventListener(type, invalidate, { passive: true });\n\t\t}\n\t}\n\n\tconst checkResize = () => {\n\t\tconst { width, height } = getCanvasSize();\n\t\tif (width === 0 || height === 0) return;\n\t\tconst pixelRatio = getPixelRatio();\n\n\t\t// Must floor (not round) to match renderer.setSize's own flooring — otherwise the size\n\t\t// comparison below never settles and the resize branch runs every frame.\n\t\tconst newW = Math.floor(width * pixelRatio);\n\t\tconst newH = Math.floor(height * pixelRatio);\n\n\t\tif (renderer.domElement.width !== newW || renderer.domElement.height !== newH) {\n\t\t\trenderer.setPixelRatio(pixelRatio);\n\t\t\trenderer.setSize(width, height, false);\n\t\t\tcamera.aspect = width / height;\n\t\t\tcamera.updateProjectionMatrix();\n\t\t\tcameraController.updateAspect(width, height);\n\t\t\tgetRenderPipeline?.()?.setSize(width, height, pixelRatio);\n\t\t\tlabelLayer?.setSize(width, height); // CSS2D overlay uses CSS size, not the pixel-ratio buffer\n\t\t\tinvalidate();\n\t\t}\n\t};\n\n\tconst drawOverlays = (activeCamera: THREE.Camera) => {\n\t\tif (labelLayer) labelLayer.render(scene, activeCamera);\n\n\t\t// Corner-viewport overlay with its own clear; must render last to sit on top.\n\t\tif (gizmo) gizmo.render(renderer);\n\t};\n\n\tconst drawFrame = (activeCamera: THREE.Camera, delta: number) => {\n\t\tconst renderPipeline = getRenderPipeline?.();\n\t\tif (renderPipeline) {\n\t\t\trenderPipeline.setCamera(activeCamera); // retarget in case 2D/3D swapped\n\t\t\trenderPipeline.render(delta);\n\t\t} else {\n\t\t\trenderer.render(scene, activeCamera);\n\t\t}\n\n\t\tdrawOverlays(activeCamera);\n\t};\n\n\t// Without preserveDrawingBuffer the colour buffer is cleared once the browser composites, so a\n\t// canvas read (toBlob/toDataURL) only sees pixels if it happens in the same task as a draw.\n\tconst renderNow = () => {\n\t\tconst activeCamera = getActiveCamera();\n\t\tif (refiner?.present()) drawOverlays(activeCamera);\n\t\telse drawFrame(activeCamera, 0);\n\t};\n\n\tconst animate = function () {\n\t\tanimationId = requestAnimationFrame(animate);\n\n\t\tconst now = performance.now();\n\t\tconst delta = (now - lastTime) / 1000;\n\t\tlastTime = now;\n\n\t\tcheckResize();\n\n\t\tif (controls.enableDamping || controls.autoRotate) {\n\t\t\tcontrols.update();\n\t\t}\n\n\t\tif (grid) grid.update(getActiveCamera().position); // recenter on camera so it reads as infinite\n\n\t\t// Before render, so depth precision tracks the camera's current distance from content.\n\t\tif (nearFitter) nearFitter.update();\n\n\t\tonFrame?.(delta);\n\n\t\tconst activeCamera = getActiveCamera();\n\n\t\tif (onDemand) {\n\t\t\tconst moved = cameraMoved(activeCamera);\n\t\t\tif (moved) refiner?.moving(delta * 1000);\n\t\t\tconst changed = renderRequested || moved;\n\t\t\tif (!changed && refiner?.wants(now)) {\n\t\t\t\trefiner.step(activeCamera);\n\t\t\t\tdrawOverlays(activeCamera);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst repaint = now - lastRenderTime >= IDLE_REPAINT_INTERVAL_MS;\n\t\t\tif (!changed && !repaint) return;\n\t\t\tlastRenderTime = now;\n\t\t\tif (!changed && refiner?.isHolding()) return;\n\t\t\trenderRequested = false;\n\t\t\trefiner?.reset(now);\n\t\t}\n\n\t\tdrawFrame(activeCamera, delta);\n\t};\n\n\tconst dispose = () => {\n\t\tif (animationId !== null) {\n\t\t\tcancelAnimationFrame(animationId);\n\t\t\tanimationId = null;\n\t\t}\n\t\tif (onDemand) {\n\t\t\tfor (const type of pointerEvents) {\n\t\t\t\tcanvas.removeEventListener(type, invalidate);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn { animate, dispose, invalidate, renderNow };\n}\n","import * as THREE from 'three';\n\nimport {\n\tEMISSIVE_LIGHT,\n\tOWN_EMISSIVE,\n\tgetBaseMaterial,\n\tgetHiddenMembers\n} from '../../shared/index.js';\nimport { SOURCE_COMPUTE } from '../scene-ownership.js';\nimport { halton } from './idle-refine.js';\nimport { memberBounds, membersOf } from './merged-picking.js';\n\n/** Point lights are expensive: every lit material pays for each one in its shader. */\nconst DEFAULT_MAX_LIGHTS = 3;\n/** Six shadow-map faces per light, each drawing the whole scene. */\nconst MAX_SHADOWED_LIGHTS = 2;\nconst SHADOW_MAP_SIZE = 1024;\n/** A glow this far below the strongest is a lit surface, not a source: a diffuser, not the bulb. */\nconst MIN_RELATIVE_STRENGTH = 1 / 3;\n/** Parts this close, as a share of the larger one's diagonal, are one lamp: a bulb and its reflector. */\nconst SAME_LAMP_GAP = 0.05;\n/**\n * three's point lights are in candela, so brightness falls off with the scene's units squared.\n * Scaling by the scene's radius squared makes one strength read the same in millimetres and metres.\n */\nconst INTENSITY_PER_STRENGTH = 0.17;\n/** Share of a source's light that comes back off the room, with no shadow to stop it. */\nconst BOUNCE_SHARE = 0.07;\n/** A lamp's source has a size, so its shadow has a penumbra; three's default edge is a hard line. */\nconst SHADOW_SOFTNESS = 7;\n/** A source wider than this share of the scene isn't a point. */\nconst MAX_POINT_SOURCE_SPREAD = 0.15;\n\n/** One object that glows: a whole mesh, or one member of a merged mesh. */\ntype GlowingPart = {\n\tmesh: THREE.Mesh;\n\tmaterial: THREE.MeshStandardMaterial;\n\tbounds: THREE.Box3;\n};\n\ntype Lamp = {\n\tbounds: THREE.Box3;\n\tmeshes: Set<THREE.Mesh>;\n};\n\ntype Source = Lamp & {\n\tcolor: THREE.Color;\n\tstrength: number;\n\t/** Absolute intensity from real lumens. Without one, scaled to the model. */\n\tlight?: number;\n};\n\n// The base, not `mesh.material`: a selection clone copies the emissive flag beside its red tint.\nfunction emissiveMaterialOf(mesh: THREE.Mesh): THREE.MeshStandardMaterial | null {\n\tconst material = getBaseMaterial(mesh);\n\treturn material?.userData[OWN_EMISSIVE] ? (material as THREE.MeshStandardMaterial) : null;\n}\n\nfunction drawnBounds(mesh: THREE.Mesh): THREE.Box3[] {\n\tconst members = membersOf(mesh);\n\tif (!members) return [new THREE.Box3().setFromObject(mesh)];\n\tconst hidden = getHiddenMembers(mesh);\n\treturn members\n\t\t.filter((_, index) => !hidden.has(index))\n\t\t.map((member) => memberBounds(mesh, member))\n\t\t.filter((bounds) => !bounds.isEmpty());\n}\n\nfunction touches(a: THREE.Box3, b: THREE.Box3): boolean {\n\tconst diagonal = (box: THREE.Box3) => box.getSize(new THREE.Vector3()).length();\n\tconst gap = Math.max(diagonal(a), diagonal(b)) * SAME_LAMP_GAP;\n\treturn a.clone().expandByScalar(gap).intersectsBox(b);\n}\n\n/**\n * Groups parts of one glow into lamps by contact. More lamps than lights get one light in the\n * middle: lighting three of a street's lamps would pick them by mesh order.\n */\nfunction lampsOf(parts: GlowingPart[], maxLights: number): Lamp[] {\n\tlet lamps: Lamp[] = [];\n\tfor (const part of parts) {\n\t\tconst lamp: Lamp = { bounds: part.bounds.clone(), meshes: new Set([part.mesh]) };\n\t\tconst separate: Lamp[] = [];\n\t\tfor (const other of lamps) {\n\t\t\tif (touches(other.bounds, part.bounds)) {\n\t\t\t\tlamp.bounds.union(other.bounds);\n\t\t\t\tfor (const mesh of other.meshes) lamp.meshes.add(mesh);\n\t\t\t} else separate.push(other);\n\t\t}\n\t\tlamps = [...separate, lamp];\n\t\tif (lamps.length > maxLights) {\n\t\t\tconst bounds = new THREE.Box3();\n\t\t\tfor (const each of parts) bounds.union(each.bounds);\n\t\t\treturn [{ bounds, meshes: new Set(parts.map((each) => each.mesh)) }];\n\t\t}\n\t}\n\treturn lamps;\n}\n\n// By glow, not material instance: the parser splits one material between closed and open meshes.\nfunction sourcesFrom(parts: GlowingPart[], maxLights: number): Source[] {\n\tconst byGlow = new Map<string, GlowingPart[]>();\n\tfor (const part of parts) {\n\t\tconst { emissive, emissiveIntensity, userData } = part.material;\n\t\tconst glow = `${emissive.getHexString()}|${emissiveIntensity}|${userData[EMISSIVE_LIGHT]}`;\n\t\tconst same = byGlow.get(glow);\n\t\tif (same) same.push(part);\n\t\telse byGlow.set(glow, [part]);\n\t}\n\treturn [...byGlow.values()].flatMap((same) => {\n\t\tconst { emissive, emissiveIntensity, userData } = same[0]!.material;\n\t\treturn lampsOf(same, maxLights).map((lamp) => ({\n\t\t\t...lamp,\n\t\t\tcolor: emissive,\n\t\t\tstrength: emissiveIntensity,\n\t\t\tlight: userData[EMISSIVE_LIGHT]\n\t\t}));\n\t});\n}\n\n/**\n * Picks the strongest emissive materials to light the scene. Exported for tests: which surface\n * counts as a source decides whether a lamp's desk gets one pool of light or a washed-out pair.\n */\nexport function pickLightSources<S extends { strength: number }>(\n\tsources: S[],\n\tmaxLights = DEFAULT_MAX_LIGHTS\n): S[] {\n\tconst strongest = Math.max(0, ...sources.map((s) => s.strength));\n\treturn sources\n\t\t.filter((s) => s.strength >= strongest * MIN_RELATIVE_STRENGTH)\n\t\t.sort((a, b) => b.strength - a.strength)\n\t\t.slice(0, maxLights);\n}\n\nexport interface EmissiveLights {\n\t/**\n\t * Rebuilds from what the scene draws now, so call it after anything is shown or hidden.\n\t * `enabled` false removes every light.\n\t */\n\tsync(enabled: boolean, maxLights?: number): void;\n\t/** Moves each key light within its lamp's bounds, which is what gives its shadow a real penumbra. */\n\tjitter(index: number): void;\n\t/** Back to the middle of each lamp. */\n\tsettle(): void;\n\tdispose(): void;\n}\n\n/**\n * An emissive surface only looks bright, it lights nothing, so a look that wants real light from\n * glowing parts gets a point light at each lamp. A picked lamp stops casting shadows meanwhile, and\n * so does a dimmer glow around its light: a bulb or its diffuser would otherwise block the light.\n */\nexport function createEmissiveLights(scene: THREE.Scene): EmissiveLights {\n\tconst lights: {\n\t\tkey: THREE.PointLight;\n\t\tbounce: THREE.PointLight;\n\t\t/** The key light's resting place, and the lamp it can move within. */\n\t\thome: THREE.Vector3;\n\t\tsize: THREE.Vector3;\n\t}[] = [];\n\tconst silenced = new Set<THREE.Mesh>();\n\tconst offset = new THREE.Vector3();\n\n\tconst addLight = (shadowed: boolean) => {\n\t\tconst key = new THREE.PointLight(0xffffff, 1, 0, 2);\n\t\t// No real bounce here, so anything outside the shade's cone would go flat. This stands in for\n\t\t// the room: light from the same place that the shade doesn't stop. Decay 1 falls off slower\n\t\t// than the key light, so it fills the room.\n\t\tconst bounce = new THREE.PointLight(0xffffff, 1, 0, 1);\n\t\tif (shadowed) {\n\t\t\tkey.castShadow = true;\n\t\t\tkey.shadow.mapSize.setScalar(SHADOW_MAP_SIZE);\n\t\t\tkey.shadow.radius = SHADOW_SOFTNESS;\n\t\t\t// Solve output only changes between syncs, and six faces every frame would sink orbiting.\n\t\t\tkey.shadow.autoUpdate = false;\n\t\t}\n\t\tconst pair = { key, bounce, home: new THREE.Vector3(), size: new THREE.Vector3() };\n\t\tlights.push(pair);\n\t\tscene.add(key, bounce);\n\t\treturn pair;\n\t};\n\n\tconst collect = () => {\n\t\tconst content = new THREE.Box3();\n\t\tconst parts: GlowingPart[] = [];\n\t\t// A hidden group hides its meshes without touching their own `.visible`.\n\t\tscene.traverseVisible((object) => {\n\t\t\tconst mesh = object as THREE.Mesh;\n\t\t\tif (!mesh.isMesh || object.userData.source !== SOURCE_COMPUTE) return;\n\t\t\tcontent.union(new THREE.Box3().setFromObject(mesh));\n\t\t\tconst material = emissiveMaterialOf(mesh);\n\t\t\tif (!material) return;\n\t\t\tfor (const bounds of drawnBounds(mesh)) parts.push({ mesh, material, bounds });\n\t\t});\n\t\treturn { parts, radius: content.getSize(new THREE.Vector3()).length() / 2 };\n\t};\n\n\tconst silence = (parts: GlowingPart[], sources: Source[]) => {\n\t\tfor (const { mesh, bounds } of parts) {\n\t\t\tconst blocksALight = sources.some(\n\t\t\t\t(source, index) =>\n\t\t\t\t\tsource.meshes.has(mesh) || bounds.containsPoint(lights[index]!.key.position)\n\t\t\t);\n\t\t\tif (blocksALight && mesh.castShadow) {\n\t\t\t\tmesh.castShadow = false;\n\t\t\t\tsilenced.add(mesh);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst sync = (enabled: boolean, maxLights = DEFAULT_MAX_LIGHTS) => {\n\t\tfor (const mesh of silenced) mesh.castShadow = true;\n\t\tsilenced.clear();\n\n\t\tconst { parts, radius } = enabled ? collect() : { parts: [], radius: 0 };\n\t\tconst sources = pickLightSources(sourcesFrom(parts, maxLights), maxLights);\n\n\t\t// Reused across solves: a new light reallocates its shadow map and recompiles every shader.\n\t\tsources.forEach((source, index) => {\n\t\t\tconst { key, bounce, home, size } = lights[index] ?? addLight(index < MAX_SHADOWED_LIGHTS);\n\t\t\tconst intensity = source.light ?? source.strength * INTENSITY_PER_STRENGTH * radius * radius;\n\t\t\tkey.color.copy(source.color);\n\t\t\t// Lit windows across a facade end up as one light in its middle, which burns a hole in the\n\t\t\t// wall. Only the bounce's even wash suits a glow that wide. Zeroed, not removed: removing a\n\t\t\t// light recompiles every shader.\n\t\t\tconst spread = source.bounds.getSize(new THREE.Vector3()).length() / 2 / radius;\n\t\t\tkey.intensity = !source.light && spread > MAX_POINT_SOURCE_SPREAD ? 0 : intensity;\n\t\t\tsource.bounds.getCenter(key.position);\n\t\t\thome.copy(key.position);\n\t\t\tsource.bounds.getSize(size);\n\t\t\tbounce.color.copy(source.color);\n\t\t\t// Over the radius because decay 1 scales with distance once, not squared like the key.\n\t\t\tbounce.intensity = (intensity * BOUNCE_SHARE) / radius;\n\t\t\tbounce.position.copy(key.position);\n\t\t\tif (key.castShadow) {\n\t\t\t\tkey.shadow.camera.near = radius * 0.005;\n\t\t\t\tkey.shadow.camera.far = radius * 4;\n\t\t\t\t// three only refreshes this on a change of `light.distance`, and ours stays 0.\n\t\t\t\tkey.shadow.camera.updateProjectionMatrix();\n\t\t\t\tkey.shadow.bias = -0.002;\n\t\t\t\tkey.shadow.normalBias = radius * 0.002;\n\t\t\t\tkey.shadow.needsUpdate = true;\n\t\t\t}\n\t\t});\n\t\twhile (lights.length > sources.length) {\n\t\t\tconst { key, bounce } = lights.pop()!;\n\t\t\tscene.remove(key, bounce);\n\t\t\tkey.dispose();\n\t\t\tbounce.dispose();\n\t\t}\n\t\tsilence(parts, sources);\n\t};\n\n\tconst place = (position: (home: THREE.Vector3, size: THREE.Vector3) => THREE.Vector3) => {\n\t\tfor (const { key, home, size } of lights) {\n\t\t\tkey.position.copy(position(home, size));\n\t\t\tif (key.castShadow) key.shadow.needsUpdate = true;\n\t\t}\n\t};\n\n\treturn {\n\t\tsync,\n\t\tjitter: (index) =>\n\t\t\tplace((home, size) =>\n\t\t\t\toffset\n\t\t\t\t\t.set(halton(index, 2) - 0.5, halton(index, 3) - 0.5, halton(index, 5) - 0.5)\n\t\t\t\t\t.multiply(size)\n\t\t\t\t\t.add(home)\n\t\t\t),\n\t\tsettle: () => place((home) => home),\n\t\tdispose: () => sync(false)\n\t};\n}\n","import * as THREE from 'three';\n\nimport {\n\tOWN_EMISSIVE,\n\tOWN_ENV_MAP_INTENSITY,\n\tlookEnvMapIntensity,\n\tlookPreset,\n\tmaterialAppearanceForLook\n} from '../../shared/index.js';\nimport { SOURCE_COMPUTE } from '../scene-ownership.js';\nimport { createEmissiveLights } from './emissive-lights.js';\nimport { applyReflectionEnvironment } from './reflection-environment.js';\nimport type {\n\tLook,\n\tLookMaterialOverride,\n\tLookPreset,\n\tMaterialAppearanceOptions\n} from '../types.js';\nimport { defaultUp, type ResolvedOptions } from './defaults.js';\nimport type { PipelineController } from './pipeline-controller.js';\nimport type { SceneLights } from './setup-lighting.js';\n\n/**\n * What a look overwrote, stashed on the material itself so it survives a look switch without a\n * side table keyed on materials the scene may dispose. Written once, on the first override.\n */\ntype MaterialBaseline = {\n\tcolor: number;\n\tmetalness: number;\n\troughness: number;\n\temissiveIntensity: number;\n\topacity: number;\n\ttransparent: boolean;\n\tdepthWrite: boolean;\n\twireframe: boolean;\n\tside: THREE.Side;\n};\n\nconst BASELINE_KEY = '__selvaLookBaseline';\n\ntype OverridableMaterial = THREE.Material & {\n\tcolor?: THREE.Color;\n\tmetalness?: number;\n\troughness?: number;\n\temissiveIntensity?: number;\n\twireframe?: boolean;\n\t[BASELINE_KEY]?: MaterialBaseline;\n};\n\nfunction applyBaseline(target: OverridableMaterial, baseline: MaterialBaseline): void {\n\ttarget.color?.setHex(baseline.color);\n\tif (target.metalness !== undefined) target.metalness = baseline.metalness;\n\tif (target.roughness !== undefined) target.roughness = baseline.roughness;\n\tif (target.emissiveIntensity !== undefined) target.emissiveIntensity = baseline.emissiveIntensity;\n\ttarget.opacity = baseline.opacity;\n\ttarget.transparent = baseline.transparent;\n\ttarget.depthWrite = baseline.depthWrite;\n\tif (target.wireframe !== undefined) target.wireframe = baseline.wireframe;\n\ttarget.side = baseline.side;\n}\n\n/** Restores the parsed values and forgets the baseline. No-op when nothing was overridden. */\nfunction restoreBaseline(target: OverridableMaterial): void {\n\tconst baseline = target[BASELINE_KEY];\n\tif (!baseline) return;\n\tapplyBaseline(target, baseline);\n\tdelete target[BASELINE_KEY];\n}\n\n/**\n * Applies a look's material overrides, or restores the parsed values when the look has none.\n * `needsUpdate` is set because switching `transparent` changes the shader program, not just a\n * uniform.\n *\n * Exported for tests: the leak this guards against is invisible until two overriding looks are\n * applied in sequence, which no single-look check catches.\n *\n * Every apply restores the baseline first, so an override only has to describe what *it* wants.\n * Without that reset, switching between two looks that both override leaves behind whatever the\n * previous one set and the new one is silent about — `wireframe` surviving out of the wireframe\n * look, or `opacity`/`depthWrite` surviving out of xray, since no other look mentions them.\n */\nexport function applyMaterialOverride(\n\tmaterial: THREE.Material,\n\toverride: LookMaterialOverride | undefined\n): void {\n\tconst target = material as OverridableMaterial;\n\n\tif (!override) {\n\t\trestoreBaseline(target);\n\t\ttarget.needsUpdate = true;\n\t\treturn;\n\t}\n\n\tif (!target[BASELINE_KEY]) {\n\t\ttarget[BASELINE_KEY] = {\n\t\t\tcolor: target.color?.getHex() ?? 0xffffff,\n\t\t\tmetalness: target.metalness ?? 0,\n\t\t\troughness: target.roughness ?? 1,\n\t\t\temissiveIntensity: target.emissiveIntensity ?? 0,\n\t\t\topacity: target.opacity,\n\t\t\ttransparent: target.transparent,\n\t\t\tdepthWrite: target.depthWrite,\n\t\t\twireframe: target.wireframe ?? false,\n\t\t\tside: target.side\n\t\t};\n\t} else {\n\t\t// Baseline captured by an earlier look — reset onto it so this override starts clean.\n\t\tapplyBaseline(target, target[BASELINE_KEY]);\n\t}\n\n\tif (override.color !== undefined) target.color?.setHex(override.color);\n\tif (override.metalness !== undefined && target.metalness !== undefined) {\n\t\ttarget.metalness = override.metalness;\n\t}\n\tif (override.roughness !== undefined && target.roughness !== undefined) {\n\t\ttarget.roughness = override.roughness;\n\t}\n\t// Only the wire's glow: a selection tint uses the same channel and has to stay readable.\n\tif (override.emissiveIntensity !== undefined && target.userData[OWN_EMISSIVE]) {\n\t\ttarget.emissiveIntensity = override.emissiveIntensity;\n\t}\n\tif (override.opacity !== undefined) {\n\t\ttarget.opacity = override.opacity;\n\t\ttarget.transparent = override.opacity < 1;\n\t}\n\tif (override.depthWrite !== undefined) target.depthWrite = override.depthWrite;\n\tif (override.wireframe !== undefined && target.wireframe !== undefined) {\n\t\ttarget.wireframe = override.wireframe;\n\t}\n\tif (override.side !== undefined) target.side = override.side;\n\ttarget.needsUpdate = true;\n}\n\n/** A material's own value (see `OWN_ENV_MAP_INTENSITY`) unless the look restyles every material. */\nexport function envMapIntensityForLook(material: THREE.Material, preset: LookPreset): number {\n\tif (preset.materialOverride) return preset.envMapIntensity;\n\tconst own = material.userData[OWN_ENV_MAP_INTENSITY] as number | undefined;\n\tif (own != null) return own;\n\t// Runs before the previous look's override is lifted, so read the parsed metalness.\n\tconst target = material as OverridableMaterial;\n\tconst metalness = target[BASELINE_KEY]?.metalness ?? target.metalness ?? 0;\n\treturn lookEnvMapIntensity(preset.envMapIntensity, metalness);\n}\n\nexport type SetLookOptions = {\n\t/**\n\t * Skips the sync, which walks every glowing part. The caller runs `syncEmissiveLights()` itself\n\t * once visibility settles.\n\t */\n\tdeferEmissiveLights?: boolean;\n};\n\nexport function lookPixelRatio(fullPixelRatio: number, preset: LookPreset): number {\n\treturn Math.min(fullPixelRatio, preset.maxPixelRatio ?? fullPixelRatio);\n}\n\n/** The runtime lighting/material dials — everything a host can retune without rebuilding the scene. */\nexport interface AppearanceController {\n\tsetFillLights(opts: {\n\t\themisphereIntensity?: number;\n\t\themisphereSkyColor?: THREE.Color | number;\n\t\themisphereGroundColor?: THREE.Color | number;\n\t\tambientIntensity?: number;\n\t}): void;\n\tsetEnvironmentIntensity(intensity: number): void;\n\tsetToneMappingExposure(exposure: number): void;\n\tsetAoIntensity(intensity: number): void;\n\tsetLook(look: Look, options?: SetLookOptions): void;\n\tsyncEmissiveLights(): void;\n\tjitterEmissiveLights(index: number): void;\n\tsettleEmissiveLights(): void;\n\tgetMaterialAppearance(): MaterialAppearanceOptions;\n}\n\n// setLook is built from the same setters a host would call directly, so construction-time defaults\n// (applyDefaults seeding from LOOK_PRESETS) can't drift from the runtime path.\nexport function createAppearanceController(params: {\n\tscene: THREE.Scene;\n\trenderer: THREE.WebGLRenderer;\n\tlights: SceneLights;\n\tconfig: ResolvedOptions;\n\tpipeline: PipelineController;\n\t/** Before any look's cap. */\n\tfullPixelRatio: number;\n\trequestRender: () => void;\n}): AppearanceController {\n\tconst { scene, renderer, lights, config, pipeline, fullPixelRatio, requestRender } = params;\n\n\tconst presetOf = (look: Look) => lookPreset(look, config.render.lowPower);\n\tlet activeLook: Look = config.look;\n\tconst emissiveLights = createEmissiveLights(scene);\n\t// VSM can't shadow a point light, and three then drops the shadow with only a console warning.\n\tconst ownShadowType = renderer.shadowMap.type;\n\tconst applyShadowType = (preset: LookPreset) => {\n\t\tconst type = preset.emissiveLights ? THREE.PCFShadowMap : ownShadowType;\n\t\tif (renderer.shadowMap.type === type) return;\n\t\trenderer.shadowMap.type = type;\n\t\trenderer.shadowMap.needsUpdate = true;\n\t};\n\n\t// The scene's own background, held while a look replaces it.\n\tlet ownBackground: THREE.Scene['background'] | undefined;\n\n\tconst applyBackground = (preset: LookPreset) => {\n\t\tif (preset.background === undefined) {\n\t\t\tif (ownBackground !== undefined) scene.background = ownBackground;\n\t\t\townBackground = undefined;\n\t\t\treturn;\n\t\t}\n\t\townBackground ??= scene.background;\n\t\tscene.background = new THREE.Color(preset.background);\n\t};\n\tapplyBackground(presetOf(activeLook));\n\tapplyShadowType(presetOf(activeLook));\n\n\tconst setFillLights: AppearanceController['setFillLights'] = (opts) => {\n\t\tif (opts.ambientIntensity !== undefined) {\n\t\t\tlights.ambient.intensity = opts.ambientIntensity;\n\t\t}\n\t\tif (\n\t\t\topts.hemisphereIntensity !== undefined &&\n\t\t\t!lights.hemisphere &&\n\t\t\topts.hemisphereIntensity > 0\n\t\t) {\n\t\t\t// Lazily created so hosts can enable fill at runtime even if the scene was built without one.\n\t\t\tlights.hemisphere = new THREE.HemisphereLight(\n\t\t\t\topts.hemisphereSkyColor ?? config.lighting.hemisphereSkyColor,\n\t\t\t\topts.hemisphereGroundColor ?? config.lighting.hemisphereGroundColor,\n\t\t\t\topts.hemisphereIntensity\n\t\t\t);\n\t\t\tlights.hemisphere.position.copy(config.environment.sceneUp ?? defaultUp);\n\t\t\tscene.add(lights.hemisphere);\n\t\t}\n\t\tif (lights.hemisphere) {\n\t\t\tif (opts.hemisphereIntensity !== undefined)\n\t\t\t\tlights.hemisphere.intensity = opts.hemisphereIntensity;\n\t\t\tif (opts.hemisphereSkyColor !== undefined)\n\t\t\t\tlights.hemisphere.color.set(opts.hemisphereSkyColor);\n\t\t\tif (opts.hemisphereGroundColor !== undefined)\n\t\t\t\tlights.hemisphere.groundColor.set(opts.hemisphereGroundColor);\n\t\t}\n\t\trequestRender();\n\t};\n\n\tconst setEnvironmentIntensity = (intensity: number) => {\n\t\tconfig.environment.environmentIntensity = intensity;\n\t\tscene.environmentIntensity = intensity;\n\t\trequestRender();\n\t};\n\n\tconst setToneMappingExposure = (exposure: number) => {\n\t\tconfig.render.toneMappingExposure = exposure;\n\t\trenderer.toneMappingExposure = exposure;\n\t\t// Composer applies tone mapping via its own OutputPass, so it must rebuild to pick this up.\n\t\tif (pipeline.get()) pipeline.rebuild();\n\t};\n\n\tconst setAoIntensity = (intensity: number) => {\n\t\tconfig.render.aoIntensity = intensity;\n\t\tif (pipeline.get()) pipeline.rebuild();\n\t};\n\n\tconst syncEmissiveLights = () => {\n\t\tconst { emissiveLights: enabled, maxEmissiveLights } = presetOf(activeLook);\n\t\temissiveLights.sync(!!enabled, maxEmissiveLights);\n\t\trequestRender();\n\t};\n\n\tconst setLook: AppearanceController['setLook'] = (look, options) => {\n\t\tconst preset = presetOf(look);\n\t\tactiveLook = look;\n\n\t\trenderer.toneMapping = preset.toneMapping;\n\t\trenderer.toneMappingExposure = preset.toneMappingExposure;\n\t\tconfig.render.toneMapping = preset.toneMapping;\n\t\tconfig.render.toneMappingExposure = preset.toneMappingExposure;\n\n\t\tsetFillLights({\n\t\t\themisphereIntensity: preset.hemisphereIntensity,\n\t\t\tambientIntensity: preset.ambientIntensity\n\t\t});\n\t\tsetEnvironmentIntensity(preset.environmentIntensity);\n\n\t\t// Null when the viewer was built with sunlight off; such a host has opted out of key lighting\n\t\t// entirely, and a look switch is not the place to opt it back in.\n\t\tif (lights.sun) {\n\t\t\tlights.sun.intensity = preset.sunlightIntensity;\n\t\t\tconfig.lighting.sunlightIntensity = preset.sunlightIntensity;\n\t\t}\n\n\t\tapplyBackground(preset);\n\t\tapplyShadowType(preset);\n\t\tconfig.render.bloom = preset.bloom;\n\t\t// The render loop's resize check applies it; set before the rebuild so the composer starts right.\n\t\tconfig.render.pixelRatio = lookPixelRatio(fullPixelRatio, preset);\n\n\t\t// Also rebuilds a live composer whose tone mapping or bloom just changed.\n\t\tpipeline.setAmbientOcclusion(preset.ambientOcclusion);\n\n\t\t// Solve output only. Host-added geometry (`user`/`app:` scopes) owns its own materials —\n\t\t// a point cloud or draft line has a deliberate look that a render-style switch must not\n\t\t// overwrite. Hosts that do want to follow the look read `getMaterialAppearance()`.\n\t\tscene.traverse((object) => {\n\t\t\tif (object.userData.source !== SOURCE_COMPUTE) return;\n\t\t\tconst mesh = object as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tconst materials = Array.isArray(mesh.material)\n\t\t\t\t? mesh.material\n\t\t\t\t: mesh.material\n\t\t\t\t\t? [mesh.material]\n\t\t\t\t\t: [];\n\t\t\tfor (const material of materials) {\n\t\t\t\tif ('envMapIntensity' in material) {\n\t\t\t\t\t(material as THREE.MeshStandardMaterial).envMapIntensity = envMapIntensityForLook(\n\t\t\t\t\t\tmaterial,\n\t\t\t\t\t\tpreset\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tapplyMaterialOverride(material as THREE.Material, preset.materialOverride);\n\t\t\t}\n\t\t});\n\t\t// An override can zero metalness, and lifting it restores it.\n\t\tapplyReflectionEnvironment(scene, scene);\n\t\tif (!options?.deferEmissiveLights) syncEmissiveLights();\n\t};\n\n\treturn {\n\t\tsetFillLights,\n\t\tsetEnvironmentIntensity,\n\t\tsetToneMappingExposure,\n\t\tsetAoIntensity,\n\t\tsetLook,\n\t\tsyncEmissiveLights,\n\t\tjitterEmissiveLights: emissiveLights.jitter,\n\t\tsettleEmissiveLights: emissiveLights.settle,\n\t\tgetMaterialAppearance: () => materialAppearanceForLook(activeLook)\n\t};\n}\n","import * as THREE from 'three';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function createCamera(\n\tconfig: ResolvedOptions,\n\tcanvas: HTMLCanvasElement\n): THREE.PerspectiveCamera {\n\tconst parent = canvas.parentElement;\n\tconst width = parent ? parent.clientWidth : window.innerWidth;\n\tconst height = parent ? parent.clientHeight : window.innerHeight;\n\n\tconst camera = new THREE.PerspectiveCamera(\n\t\tconfig.camera.fov,\n\t\twidth / height,\n\t\tconfig.camera.near,\n\t\tconfig.camera.far\n\t);\n\n\tconst pos = config.camera.position;\n\tif (pos) {\n\t\tcamera.position.set(pos.x, pos.y, pos.z);\n\t}\n\n\treturn camera;\n}\n","import * as THREE from 'three';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function createScene(config: ResolvedOptions): THREE.Scene {\n\tconst scene = new THREE.Scene();\n\n\tconst bgColor =\n\t\ttypeof config.environment.backgroundColor === 'string'\n\t\t\t? new THREE.Color(config.environment.backgroundColor)\n\t\t\t: config.environment.backgroundColor;\n\tscene.background = bgColor || null;\n\n\treturn scene;\n}\n","import * as THREE from 'three';\n\nimport { disposeObjectTree, type DisposeOptions } from '../../shared/index.js';\nimport { disposeReflectionEnvironment } from './reflection-environment.js';\n\nexport { disposeObjectTree };\nexport type { DisposeOptions };\n\n/** Sweeps every renderable plus the scene-level textures the object traversal can't reach. */\nexport function disposeSceneResources(scene: THREE.Scene, options?: DisposeOptions): void {\n\tdisposeObjectTree(scene, options);\n\n\tscene.environment?.dispose();\n\tdisposeReflectionEnvironment(scene);\n\tif (scene.background instanceof THREE.Texture) {\n\t\tscene.background.dispose();\n\t}\n}\n","import * as THREE from 'three';\nimport { Pass, FullScreenQuad } from 'three/addons/postprocessing/Pass.js';\n\nimport { hideGlass } from '../shared/index.js';\n\n/**\n * Screen-space edge detection (Roberts cross on depth + normal discontinuities), O(pixels)\n * regardless of triangle count. Fallback for meshes too heavy for geometry edge overlays\n * (over `EdgeOptions.maxTriangles`): uniform pixel width, one global color, view-dependent,\n * gentle creases below the normal threshold don't register.\n */\nexport interface EdgeDetectionOptions {\n\tcolor?: THREE.ColorRepresentation;\n\topacity?: number;\n\t/** Summed `1 - dot(n₁, n₂)` across the two diagonal pairs. Lower catches gentler creases. */\n\tnormalThreshold?: number;\n\t/** Relative view-depth discontinuity, fraction of center depth. */\n\tdepthThreshold?: number;\n\t/** Sample offset in device px — line thickness. */\n\tthickness?: number;\n}\n\nconst EDGE_SHADER = {\n\tuniforms: {\n\t\ttDiffuse: { value: null as THREE.Texture | null },\n\t\ttNormal: { value: null as THREE.Texture | null },\n\t\ttDepth: { value: null as THREE.Texture | null },\n\t\tuResolution: { value: new THREE.Vector2(1, 1) },\n\t\tuColor: { value: new THREE.Color(0x222222) },\n\t\tuOpacity: { value: 1 },\n\t\tuNormalThreshold: { value: 0.4 },\n\t\tuDepthThreshold: { value: 0.02 },\n\t\tuThickness: { value: 1 },\n\t\tuNear: { value: 0.1 },\n\t\tuFar: { value: 1000 },\n\t\tuPerspective: { value: 1 }\n\t},\n\tvertexShader: /* glsl */ `\n\t\tvarying vec2 vUv;\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n\t\t}\n\t`,\n\tfragmentShader: /* glsl */ `\n\t\tuniform sampler2D tDiffuse;\n\t\tuniform sampler2D tNormal;\n\t\tuniform sampler2D tDepth;\n\t\tuniform vec2 uResolution;\n\t\tuniform vec3 uColor;\n\t\tuniform float uOpacity;\n\t\tuniform float uNormalThreshold;\n\t\tuniform float uDepthThreshold;\n\t\tuniform float uThickness;\n\t\tuniform float uNear;\n\t\tuniform float uFar;\n\t\tuniform float uPerspective;\n\t\tvarying vec2 vUv;\n\n\t\tfloat viewZOf(const in float depth) {\n\t\t\tfloat perspective = (uNear * uFar) / ((uFar - uNear) * depth - uFar);\n\t\t\tfloat orthographic = -(depth * (uFar - uNear) + uNear);\n\t\t\treturn mix(orthographic, perspective, uPerspective);\n\t\t}\n\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D(tDiffuse, vUv);\n\t\t\tvec2 texel = uThickness / uResolution;\n\n\t\t\tvec2 offsetA = vec2(texel.x, texel.y);\n\t\t\tvec2 offsetB = vec2(texel.x, -texel.y);\n\n\t\t\tfloat z0 = viewZOf(texture2D(tDepth, vUv + offsetA).x);\n\t\t\tfloat z1 = viewZOf(texture2D(tDepth, vUv - offsetA).x);\n\t\t\tfloat z2 = viewZOf(texture2D(tDepth, vUv + offsetB).x);\n\t\t\tfloat z3 = viewZOf(texture2D(tDepth, vUv - offsetB).x);\n\t\t\tfloat zCenter = viewZOf(texture2D(tDepth, vUv).x);\n\t\t\t// Normalized by center depth, not absolute Z: keeps the response scale-invariant (an\n\t\t\t// absolute threshold would be noise far away, blind up close).\n\t\t\tfloat depthDelta = (abs(z0 - z1) + abs(z2 - z3)) / max(abs(zCenter), 1e-6);\n\t\t\tfloat depthEdge = step(uDepthThreshold, depthDelta);\n\n\t\t\tvec3 n0 = texture2D(tNormal, vUv + offsetA).rgb * 2.0 - 1.0;\n\t\t\tvec3 n1 = texture2D(tNormal, vUv - offsetA).rgb * 2.0 - 1.0;\n\t\t\tvec3 n2 = texture2D(tNormal, vUv + offsetB).rgb * 2.0 - 1.0;\n\t\t\tvec3 n3 = texture2D(tNormal, vUv - offsetB).rgb * 2.0 - 1.0;\n\t\t\tfloat normalDelta = (1.0 - dot(n0, n1)) + (1.0 - dot(n2, n3));\n\t\t\tfloat normalEdge = step(uNormalThreshold, normalDelta);\n\n\t\t\tfloat edge = max(depthEdge, normalEdge) * uOpacity;\n\t\t\tgl_FragColor = vec4(mix(color.rgb, uColor, edge), color.a);\n\t\t}\n\t`\n};\n\nexport class EdgeDetectionPass extends Pass {\n\tcamera: THREE.Camera;\n\n\tprivate readonly scene: THREE.Scene;\n\tprivate readonly normalMaterial: THREE.MeshNormalMaterial;\n\tprivate readonly edgeMaterial: THREE.ShaderMaterial;\n\tprivate readonly fsQuad: FullScreenQuad;\n\tprivate normalTarget: THREE.WebGLRenderTarget | null = null;\n\tprivate width: number;\n\tprivate height: number;\n\n\tconstructor(\n\t\tscene: THREE.Scene,\n\t\tcamera: THREE.Camera,\n\t\twidth: number,\n\t\theight: number,\n\t\toptions: EdgeDetectionOptions = {}\n\t) {\n\t\tsuper();\n\t\tthis.scene = scene;\n\t\tthis.camera = camera;\n\t\tthis.width = Math.max(1, width);\n\t\tthis.height = Math.max(1, height);\n\n\t\tthis.normalMaterial = new THREE.MeshNormalMaterial();\n\t\tthis.normalMaterial.blending = THREE.NoBlending;\n\n\t\tthis.edgeMaterial = new THREE.ShaderMaterial({\n\t\t\tuniforms: THREE.UniformsUtils.clone(EDGE_SHADER.uniforms),\n\t\t\tvertexShader: EDGE_SHADER.vertexShader,\n\t\t\tfragmentShader: EDGE_SHADER.fragmentShader\n\t\t});\n\t\tconst uniforms = this.edgeMaterial.uniforms;\n\t\tuniforms.uColor.value = new THREE.Color(options.color ?? 0x222222);\n\t\tuniforms.uOpacity.value = options.opacity ?? 1;\n\t\tuniforms.uNormalThreshold.value = options.normalThreshold ?? 0.4;\n\t\tuniforms.uDepthThreshold.value = options.depthThreshold ?? 0.02;\n\t\tuniforms.uThickness.value = options.thickness ?? 1;\n\n\t\tthis.fsQuad = new FullScreenQuad(this.edgeMaterial);\n\t\tthis.needsSwap = true;\n\t}\n\n\tprivate acquireNormalTarget(): THREE.WebGLRenderTarget {\n\t\tif (!this.normalTarget) {\n\t\t\tconst depthTexture = new THREE.DepthTexture(this.width, this.height);\n\t\t\tthis.normalTarget = new THREE.WebGLRenderTarget(this.width, this.height, {\n\t\t\t\tminFilter: THREE.NearestFilter,\n\t\t\t\tmagFilter: THREE.NearestFilter,\n\t\t\t\tdepthTexture\n\t\t\t});\n\t\t}\n\t\treturn this.normalTarget;\n\t}\n\n\toverride setSize(width: number, height: number): void {\n\t\tthis.width = Math.max(1, width);\n\t\tthis.height = Math.max(1, height);\n\t\tthis.normalTarget?.setSize(this.width, this.height);\n\t}\n\n\toverride render(\n\t\trenderer: THREE.WebGLRenderer,\n\t\twriteBuffer: THREE.WebGLRenderTarget,\n\t\treadBuffer: THREE.WebGLRenderTarget\n\t): void {\n\t\tconst normalTarget = this.acquireNormalTarget();\n\n\t\t// --- Normals + depth prepass (override material) ---\n\t\tconst previousTarget = renderer.getRenderTarget();\n\t\tconst previousAutoClear = renderer.autoClear;\n\t\tconst previousClearColor = renderer.getClearColor(new THREE.Color());\n\t\tconst previousClearAlpha = renderer.getClearAlpha();\n\t\tconst previousOverride = this.scene.overrideMaterial;\n\n\t\trenderer.setRenderTarget(normalTarget);\n\t\t// 0x7777ff is packed +Z: background pixels get a uniform normal, so only depth silhouettes\n\t\t// (not normal noise) separate objects from empty space.\n\t\trenderer.setClearColor(0x7777ff, 1);\n\t\trenderer.autoClear = true;\n\t\tthis.scene.overrideMaterial = this.normalMaterial;\n\t\tconst showGlass = hideGlass(this.scene);\n\t\trenderer.render(this.scene, this.camera);\n\t\tshowGlass();\n\t\tthis.scene.overrideMaterial = previousOverride;\n\t\trenderer.setClearColor(previousClearColor, previousClearAlpha);\n\t\trenderer.autoClear = previousAutoClear;\n\n\t\t// --- Edge composite ---\n\t\tconst uniforms = this.edgeMaterial.uniforms;\n\t\tuniforms.tDiffuse.value = readBuffer.texture;\n\t\tuniforms.tNormal.value = normalTarget.texture;\n\t\tuniforms.tDepth.value = normalTarget.depthTexture;\n\t\tuniforms.uResolution.value.set(this.width, this.height);\n\t\tconst perspective = this.camera as Partial<THREE.PerspectiveCamera>;\n\t\tuniforms.uPerspective.value = perspective.isPerspectiveCamera ? 1 : 0;\n\t\tuniforms.uNear.value = (this.camera as THREE.PerspectiveCamera).near ?? 0.1;\n\t\tuniforms.uFar.value = (this.camera as THREE.PerspectiveCamera).far ?? 1000;\n\n\t\trenderer.setRenderTarget(this.renderToScreen ? null : writeBuffer);\n\t\tthis.fsQuad.render(renderer);\n\t\trenderer.setRenderTarget(previousTarget);\n\t}\n\n\toverride dispose(): void {\n\t\tthis.normalTarget?.dispose();\n\t\tthis.normalMaterial.dispose();\n\t\tthis.edgeMaterial.dispose();\n\t\tthis.fsQuad.dispose();\n\t}\n}\n","import * as THREE from 'three';\nimport { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';\nimport { RenderPass } from 'three/addons/postprocessing/RenderPass.js';\nimport { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';\nimport { GTAOPass } from 'three/addons/postprocessing/GTAOPass.js';\nimport { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js';\nimport { OutputPass } from 'three/addons/postprocessing/OutputPass.js';\nimport { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';\nimport { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';\n\nimport { hideGlass } from '../shared/index.js';\nimport type { BloomOptions } from '../shared/types.js';\n\nimport { EdgeDetectionPass, type EdgeDetectionOptions } from './edge-detection-pass';\n\n/**\n * Pipeline: RenderPass → GTAOPass? → BloomPass? → EdgeDetectionPass? → SMAAPass → OutputPass. Bloom\n * sits before OutputPass because it needs linear HDR values: tone mapping would clamp the emissive\n * highlights it keys on. EdgeDetectionPass sits before SMAA so its 1px lines get antialiased. EffectComposer renders offscreen, where the\n * canvas's own MSAA never applies, so the scene target carries its own (see {@link msaaSamplesFor});\n * SMAA then smooths what MSAA leaves, the edge pass's lines included. SMAA over TAA because TAA's\n * temporal jitter smears during OrbitControls drags. OutputPass applies tone mapping and color\n * space last, so SMAA operates on the pre-tonemapped image.\n */\n\nexport interface RenderPipeline {\n\trender(deltaTime: number): void;\n\tsetSize(width: number, height: number, pixelRatio: number): void;\n\t/** Call when the camera's projection changes (e.g. perspective↔ortho). */\n\tsetCamera(camera: THREE.Camera): void;\n\tsetEdgeDetection(enabled: boolean): void;\n\tedgeDetectionEnabled(): boolean;\n\t/** Draws a frame into the running average, then shows the average. Index 0 starts a new one. */\n\taccumulate(index: number): void;\n\t/** Shows the running average without drawing. */\n\tpresentAccumulated(): void;\n\t/** Scales ambient occlusion live, 0 skipping the pass: no rebuild, so it can change every frame. */\n\tsetAoWeight(weight: number): void;\n\tdispose(): void;\n}\n\nexport interface RenderPipelineOptions {\n\t/** Must mirror the renderer's own tone mapping — OutputPass applies it once composited, not the renderer. */\n\ttoneMapping: THREE.ToneMapping;\n\ttoneMappingExposure: number;\n\t/** Default true. */\n\tambientOcclusion?: boolean;\n\t/** AO strength 0–1. Default 1. */\n\taoIntensity?: number;\n\t/** DPR cap for the AO buffers only; the scene itself renders at the full pixel ratio. Default 1. */\n\taoPixelRatio?: number;\n\t/** Omitted: no bloom pass at all. */\n\tbloom?: BloomOptions;\n\t/** Start with the screen-space edge pass enabled; pass an object to tune it. */\n\tedgeDetection?: boolean | EdgeDetectionOptions;\n}\n\n/**\n * A highlight beside a point light overflows the half-float target to infinity, and bloom's blur\n * turns that one pixel into NaN across the whole frame: the viewer goes blank. Clamping first keeps\n * a hot pixel hot without letting it reach the blur.\n */\nconst HDR_CLAMP_SHADER = {\n\tname: 'HdrClamp',\n\tuniforms: { tDiffuse: { value: null } },\n\tvertexShader: /* glsl */ `\n\t\tvarying vec2 vUv;\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n\t\t}`,\n\tfragmentShader: /* glsl */ `\n\t\tuniform sampler2D tDiffuse;\n\t\tvarying vec2 vUv;\n\t\tconst float MAX_RADIANCE = 64.0;\n\t\tfloat safe(float v) {\n\t\t\treturn isnan(v) ? 0.0 : clamp(v, 0.0, MAX_RADIANCE);\n\t\t}\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D(tDiffuse, vUv);\n\t\t\tgl_FragColor = vec4(safe(color.r), safe(color.g), safe(color.b), color.a);\n\t\t}`\n};\n\nconst AVERAGE_SHADER = {\n\tvertexShader: /* glsl */ `\n\t\tvarying vec2 vUv;\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = vec4(position.xy, 0.0, 1.0);\n\t\t}`,\n\tfragmentShader: /* glsl */ `\n\t\tuniform sampler2D tFrame;\n\t\tvarying vec2 vUv;\n\t\tvoid main() {\n\t\t\tgl_FragColor = texture2D(tFrame, vUv);\n\t\t}`\n};\n\n/**\n * Keeps the running average of the composer's finished frames. They're already tone mapped, which\n * suits what gets averaged here: shadows and edges, not HDR highlights. Half float, because 32\n * frames blended into 8 bits would band in a soft shadow.\n */\nfunction createAccumulator(renderer: THREE.WebGLRenderer) {\n\tconst target = new THREE.WebGLRenderTarget(1, 1, {\n\t\ttype: THREE.HalfFloatType,\n\t\tdepthBuffer: false\n\t});\n\tconst material = new THREE.ShaderMaterial({\n\t\t...AVERAGE_SHADER,\n\t\tuniforms: { tFrame: { value: null } },\n\t\tdepthTest: false,\n\t\tdepthWrite: false,\n\t\ttoneMapped: false,\n\t\tblending: THREE.CustomBlending,\n\t\tblendSrc: THREE.ConstantAlphaFactor,\n\t\tblendDst: THREE.OneMinusConstantAlphaFactor\n\t});\n\tconst quad = new FullScreenQuad(material);\n\n\tconst draw = (texture: THREE.Texture, weight: number) => {\n\t\tmaterial.uniforms.tFrame.value = texture;\n\t\tmaterial.blendAlpha = weight;\n\t\t// FullScreenQuad renders through the renderer, whose auto clear would wipe the average.\n\t\tconst autoClear = renderer.autoClear;\n\t\trenderer.autoClear = false;\n\t\tquad.render(renderer);\n\t\trenderer.autoClear = autoClear;\n\t};\n\tconst present = () => {\n\t\trenderer.setRenderTarget(null);\n\t\tdraw(target.texture, 1);\n\t};\n\n\treturn {\n\t\tadd: (frame: THREE.Texture, index: number, width: number, height: number) => {\n\t\t\tif (target.width !== width || target.height !== height) target.setSize(width, height);\n\t\t\trenderer.setRenderTarget(target);\n\t\t\tdraw(frame, 1 / (index + 1));\n\t\t\tpresent();\n\t\t},\n\t\tpresent,\n\t\tdispose: () => {\n\t\t\ttarget.dispose();\n\t\t\tmaterial.dispose();\n\t\t\tquad.dispose();\n\t\t}\n\t};\n}\n\n/**\n * Without MSAA a sub-pixel feature (a sheet's bright hem edge) comes out as a dashed line, which\n * SMAA can't rebuild. At 2× DPR each CSS pixel already holds four samples, and 4× MSAA on top\n * would cost ~250 MB on a 4K target for no visible gain.\n */\nfunction msaaSamplesFor(pixelRatio: number): number {\n\treturn pixelRatio >= 2 ? 0 : 4;\n}\n\n// GTAOPass hides points and lines from its depth/normal prepass through this hook; glass joins\n// them, or it would occlude, and darken, everything seen through it.\nfunction skipGlass(pass: GTAOPass): void {\n\tconst hooks = pass as unknown as { _overrideVisibility(): void; _restoreVisibility(): void };\n\tconst overrideVisibility = hooks._overrideVisibility.bind(pass);\n\tconst restoreVisibility = hooks._restoreVisibility.bind(pass);\n\tlet showGlass = () => {};\n\thooks._overrideVisibility = () => {\n\t\toverrideVisibility();\n\t\tshowGlass = hideGlass(pass.scene);\n\t};\n\thooks._restoreVisibility = () => {\n\t\tshowGlass();\n\t\trestoreVisibility();\n\t};\n}\n\nexport function createRenderPipeline(\n\trenderer: THREE.WebGLRenderer,\n\tscene: THREE.Scene,\n\tcamera: THREE.Camera,\n\twidth: number,\n\theight: number,\n\toptions: RenderPipelineOptions\n): RenderPipeline {\n\t// Only the scene target needs samples: RenderPass draws into it, every later pass is a\n\t// full-screen quad. EffectComposer clones the second from the first, so reset it.\n\tconst sceneTarget = new THREE.WebGLRenderTarget(width, height, { type: THREE.HalfFloatType });\n\tconst composer = new EffectComposer(renderer, sceneTarget);\n\tcomposer.renderTarget2.samples = 0;\n\n\t// RenderPass draws into `readBuffer`, and the composer's buffer swaps carry over between\n\t// frames. With an odd number of swapping passes the scene would land in the unsampled target\n\t// every other frame, so point `readBuffer` at the scene target before each frame.\n\tconst renderFrame = (deltaTime: number) => {\n\t\tcomposer.readBuffer = sceneTarget;\n\t\tcomposer.writeBuffer = composer.renderTarget2;\n\t\tcomposer.render(deltaTime);\n\t};\n\n\tconst renderPass = new RenderPass(scene, camera);\n\tcomposer.addPass(renderPass);\n\n\tlet gtaoPass: GTAOPass | null = null;\n\tif (options.ambientOcclusion ?? true) {\n\t\tgtaoPass = new GTAOPass(scene, camera, width, height);\n\t\tgtaoPass.blendIntensity = options.aoIntensity ?? 1;\n\t\tgtaoPass.updateGtaoMaterial({ screenSpaceRadius: true });\n\t\tskipGlass(gtaoPass);\n\t\tcomposer.addPass(gtaoPass);\n\t}\n\n\tlet clampPass: ShaderPass | null = null;\n\tlet bloomPass: UnrealBloomPass | null = null;\n\tif (options.bloom) {\n\t\tconst { strength, radius, threshold } = options.bloom;\n\t\tclampPass = new ShaderPass(HDR_CLAMP_SHADER);\n\t\tcomposer.addPass(clampPass);\n\t\tbloomPass = new UnrealBloomPass(new THREE.Vector2(width, height), strength, radius, threshold);\n\t\tcomposer.addPass(bloomPass);\n\t}\n\n\tconst edgeOptions = typeof options.edgeDetection === 'object' ? options.edgeDetection : {};\n\tconst edgePass = new EdgeDetectionPass(scene, camera, width, height, edgeOptions);\n\tedgePass.enabled = !!options.edgeDetection;\n\tcomposer.addPass(edgePass);\n\n\tconst smaaPass = new SMAAPass();\n\tcomposer.addPass(smaaPass);\n\n\tconst outputPass = new OutputPass();\n\tcomposer.addPass(outputPass);\n\n\trenderer.toneMapping = options.toneMapping;\n\trenderer.toneMappingExposure = options.toneMappingExposure;\n\n\tlet accumulator: ReturnType<typeof createAccumulator> | null = null;\n\n\tconst aoPixelRatioCap = options.aoPixelRatio ?? 1;\n\t// The composer sizes every pass at the scene's resolution; AO alone is cheap enough to run lower,\n\t// and its blend samples by UV so it upscales onto the full-resolution frame.\n\tlet aoScale = 1;\n\tif (gtaoPass) {\n\t\tconst setAoSize = gtaoPass.setSize.bind(gtaoPass);\n\t\tgtaoPass.setSize = (w: number, h: number) =>\n\t\t\tsetAoSize(Math.max(1, Math.round(w * aoScale)), Math.max(1, Math.round(h * aoScale)));\n\t}\n\tconst applyPixelRatio = (pixelRatio: number) => {\n\t\taoScale = Math.min(pixelRatio, aoPixelRatioCap) / pixelRatio;\n\t\tconst samples = msaaSamplesFor(pixelRatio);\n\t\tif (sceneTarget.samples !== samples) {\n\t\t\tsceneTarget.samples = samples;\n\t\t\tsceneTarget.dispose(); // three reallocates on next use; samples alone doesn't trigger it\n\t\t}\n\t\tcomposer.setPixelRatio(pixelRatio);\n\t};\n\tapplyPixelRatio(renderer.getPixelRatio());\n\tcomposer.setSize(width, height);\n\n\treturn {\n\t\trender: renderFrame,\n\t\t// composer.setSize only — calling individual pass.setSize would reset AO/AA targets back to\n\t\t// logical CSS size, undoing the pixel-ratio scaling.\n\t\tsetSize: (w, h, pixelRatio) => {\n\t\t\tapplyPixelRatio(pixelRatio);\n\t\t\tcomposer.setSize(w, h);\n\t\t},\n\t\tsetCamera: (cam) => {\n\t\t\trenderPass.camera = cam;\n\t\t\tedgePass.camera = cam;\n\t\t\tif (!gtaoPass) return;\n\t\t\tgtaoPass.camera = cam;\n\t\t\t// GTAOPass bakes camera type into its AO shader as a construction-time define; reassigning\n\t\t\t// `camera` alone leaves the old projection's depth reconstruction active — garbage AO after\n\t\t\t// a perspective⇄ortho toggle. Force a recompile when the type actually changes.\n\t\t\tconst isPerspective = (cam as Partial<THREE.PerspectiveCamera>).isPerspectiveCamera ? 1 : 0;\n\t\t\tif (gtaoPass.gtaoMaterial.defines.PERSPECTIVE_CAMERA !== isPerspective) {\n\t\t\t\tgtaoPass.gtaoMaterial.defines.PERSPECTIVE_CAMERA = isPerspective;\n\t\t\t\tgtaoPass.gtaoMaterial.needsUpdate = true;\n\t\t\t}\n\t\t},\n\t\tsetEdgeDetection: (enabled) => {\n\t\t\tedgePass.enabled = enabled;\n\t\t},\n\t\tedgeDetectionEnabled: () => edgePass.enabled,\n\t\taccumulate: (index) => {\n\t\t\tcomposer.renderToScreen = false;\n\t\t\trenderFrame(0);\n\t\t\tcomposer.renderToScreen = true;\n\t\t\taccumulator ??= createAccumulator(renderer);\n\t\t\tconst { texture, width, height } = composer.readBuffer;\n\t\t\taccumulator.add(texture, index, width, height);\n\t\t},\n\t\tpresentAccumulated: () => accumulator?.present(),\n\t\tsetAoWeight: (weight) => {\n\t\t\tif (!gtaoPass) return;\n\t\t\tgtaoPass.enabled = weight > 0;\n\t\t\tgtaoPass.blendIntensity = (options.aoIntensity ?? 1) * weight;\n\t\t},\n\t\t// composer.dispose() doesn't free passes.\n\t\tdispose: () => {\n\t\t\tcomposer.dispose();\n\t\t\taccumulator?.dispose();\n\t\t\tgtaoPass?.dispose();\n\t\t\tclampPass?.dispose();\n\t\t\tbloomPass?.dispose();\n\t\t\tedgePass.dispose();\n\t\t\tsmaaPass.dispose();\n\t\t\toutputPass.dispose();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\n\nimport { createRenderPipeline, type RenderPipeline } from '../render-pipeline.js';\nimport type { ResolvedOptions } from './defaults.js';\n\n/**\n * Owns the optional postprocessing composer and the independent reasons to want one: ambient\n * occlusion (a user/look choice), bloom (a look choice) and the screen-space edge fallback (forced on\n * while meshes over the triangle cap are in the scene). None knows about the others, so \"is a\n * pipeline wanted, and does it need rebuilding\" is reconciled here instead of at each caller.\n */\nexport interface PipelineController {\n\tget(): RenderPipeline | null;\n\tsync(): void;\n\t/** Dispose and rebuild (if one is wanted) so construction-time options re-apply. */\n\trebuild(): void;\n\tsetAmbientOcclusion(enabled: boolean): void;\n\tsetEdgeFallback(active: boolean): void;\n\tisEdgeFallbackActive(): boolean;\n\tdispose(): void;\n}\n\nexport function createPipelineController(params: {\n\trenderer: THREE.WebGLRenderer;\n\tscene: THREE.Scene;\n\tgetActiveCamera: () => THREE.Camera;\n\tgetCanvasSize: () => { width: number; height: number };\n\tconfig: ResolvedOptions;\n\trequestRender: () => void;\n}): PipelineController {\n\tconst { renderer, scene, getActiveCamera, getCanvasSize, config, requestRender } = params;\n\n\tlet pipeline: RenderPipeline | null = null;\n\tlet aoEnabled = !!config.render.ambientOcclusion;\n\tlet edgeFallbackActive = false;\n\n\t// Baked in at construction. A look re-applies on every solve, and rebuilding reallocates bloom's\n\t// whole render-target chain, so only a real change may rebuild.\n\tconst bakedOptions = () => ({\n\t\tambientOcclusion: aoEnabled,\n\t\tbloom: config.render.bloom,\n\t\ttoneMapping: config.render.toneMapping,\n\t\ttoneMappingExposure: config.render.toneMappingExposure\n\t});\n\tlet baked = bakedOptions();\n\tconst isStale = () => {\n\t\tconst wanted = bakedOptions();\n\t\treturn (\n\t\t\twanted.ambientOcclusion !== baked.ambientOcclusion ||\n\t\t\twanted.bloom !== baked.bloom ||\n\t\t\twanted.toneMapping !== baked.toneMapping ||\n\t\t\twanted.toneMappingExposure !== baked.toneMappingExposure\n\t\t);\n\t};\n\n\tconst build = (): RenderPipeline => {\n\t\tconst { width, height } = getCanvasSize();\n\t\tconst built = createRenderPipeline(\n\t\t\trenderer,\n\t\t\tscene,\n\t\t\tgetActiveCamera(),\n\t\t\tMath.max(1, width),\n\t\t\tMath.max(1, height),\n\t\t\t{\n\t\t\t\ttoneMapping: config.render.toneMapping ?? THREE.NeutralToneMapping,\n\t\t\t\ttoneMappingExposure: config.render.toneMappingExposure ?? 1,\n\t\t\t\tambientOcclusion: aoEnabled,\n\t\t\t\taoIntensity: config.render.aoIntensity,\n\t\t\t\taoPixelRatio: config.render.aoPixelRatio,\n\t\t\t\tbloom: config.render.bloom,\n\t\t\t\t// Always built disabled; sync() flips it live via setEdgeDetection.\n\t\t\t\tedgeDetection: false\n\t\t\t}\n\t\t);\n\t\tbuilt.setSize(Math.max(1, width), Math.max(1, height), config.render.pixelRatio!);\n\t\treturn built;\n\t};\n\n\tconst sync = () => {\n\t\tconst wantPipeline = aoEnabled || edgeFallbackActive || !!config.render.bloom;\n\t\tif (!wantPipeline) {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = null;\n\t\t\trequestRender();\n\t\t\treturn;\n\t\t}\n\t\tif (!pipeline || isStale()) {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = build();\n\t\t\tbaked = bakedOptions();\n\t\t}\n\t\tpipeline.setEdgeDetection(edgeFallbackActive);\n\t\trequestRender();\n\t};\n\n\treturn {\n\t\tget: () => pipeline,\n\t\tsync,\n\t\trebuild: () => {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = null;\n\t\t\tsync();\n\t\t},\n\t\tsetAmbientOcclusion: (enabled: boolean) => {\n\t\t\taoEnabled = enabled;\n\t\t\tsync();\n\t\t},\n\t\tsetEdgeFallback: (active: boolean) => {\n\t\t\tif (active === edgeFallbackActive) return;\n\t\t\tedgeFallbackActive = active;\n\t\t\tsync();\n\t\t},\n\t\tisEdgeFallbackActive: () => edgeFallbackActive,\n\t\tdispose: () => {\n\t\t\tpipeline?.dispose();\n\t\t\tpipeline = null;\n\t\t}\n\t};\n}\n","import type * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function setupControls(\n\tcamera: THREE.PerspectiveCamera,\n\tcanvas: HTMLCanvasElement,\n\tconfig: ResolvedOptions\n): OrbitControls {\n\tconst controls = new OrbitControls(camera, canvas);\n\n\tconst target = config.camera.target;\n\tif (target) {\n\t\tcontrols.target.set(target.x, target.y, target.z);\n\t}\n\n\tcontrols.enableDamping = config.controls.enableDamping || false;\n\tcontrols.dampingFactor = config.controls.dampingFactor || 0.05;\n\n\tcontrols.autoRotate = config.controls.autoRotate || false;\n\tcontrols.autoRotateSpeed = config.controls.autoRotateSpeed || 0.5;\n\n\tcontrols.enableZoom = config.controls.enableZoom ?? true;\n\tcontrols.enablePan = config.controls.enablePan ?? true;\n\tcontrols.minDistance = config.controls.minDistance || 0.001;\n\tcontrols.maxDistance = config.controls.maxDistance || Infinity;\n\n\tcontrols.screenSpacePanning = false;\n\tcontrols.maxPolarAngle = Math.PI;\n\n\tcontrols.update();\n\treturn controls;\n}\n","import * as THREE from 'three';\n\nimport { getLogger } from '../../shared/index.js';\nimport { HDRLoader } from 'three/addons/loaders/HDRLoader.js';\n\nimport { environmentRotationFor } from '../up-axis.js';\nimport { createReflectionEnvironment } from './reflection-environment.js';\nimport { defaultUp, type ResolvedOptions } from './defaults.js';\n\nexport function setupEnvironment(\n\tscene: THREE.Scene,\n\trenderer: THREE.WebGLRenderer,\n\tconfig: ResolvedOptions,\n\tisDisposed: () => boolean\n) {\n\tif (config.environment.enableEnvironmentLighting) {\n\t\tcreateReflectionEnvironment(\n\t\t\tscene,\n\t\t\trenderer,\n\t\t\tenvironmentRotationFor(config.environment.sceneUp ?? defaultUp)\n\t\t);\n\t\tnew HDRLoader().load(\n\t\t\tconfig.environment.hdrPath || '/baseHDR.hdr',\n\t\t\tfunction (envMap) {\n\t\t\t\t// Viewer may be torn down mid-fetch (fast mount/unmount); dispose() already swept the\n\t\t\t\t// scene, so adopting the texture now would leak it, and onReady must not fire.\n\t\t\t\tif (isDisposed()) {\n\t\t\t\t\tenvMap.dispose();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!envMap?.image) {\n\t\t\t\t\tgetLogger().warn('HDR loaded without image data; skipping environment map.');\n\t\t\t\t\tenvMap?.dispose();\n\t\t\t\t\tconfig.events.onReady?.();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tenvMap.mapping = THREE.EquirectangularReflectionMapping;\n\n\t\t\t\t// PMREM builds the roughness-aware mip chain MeshStandardMaterial samples for IBL;\n\t\t\t\t// without it, rough surfaces read a near-mirror level (sharp reflections, sparkly highlights).\n\t\t\t\tconst pmrem = new THREE.PMREMGenerator(renderer);\n\t\t\t\tpmrem.compileEquirectangularShader();\n\t\t\t\tconst prefiltered = pmrem.fromEquirectangular(envMap).texture;\n\t\t\t\tpmrem.dispose();\n\n\t\t\t\tscene.environment = prefiltered;\n\t\t\t\t// Normalizes IBL contribution so brightness is consistent across HDRs of differing exposure.\n\t\t\t\tscene.environmentIntensity = config.environment.environmentIntensity ?? 1;\n\t\t\t\t// Equirect mapping assumes the horizon lies in the XZ plane (Y-up); without rotating for\n\t\t\t\t// a Z-up scene the sky lights the model from +Y instead of from above.\n\t\t\t\tconst envRotation = environmentRotationFor(config.environment.sceneUp ?? defaultUp);\n\t\t\t\tscene.environmentRotation.copy(envRotation);\n\t\t\t\tif (config.environment.showEnvironment) {\n\t\t\t\t\t// Background wants the full-res equirect, not the low-res prefiltered probe.\n\t\t\t\t\tscene.background = envMap;\n\t\t\t\t\t// Separate property from environmentRotation — drifts apart if only one is set.\n\t\t\t\t\tscene.backgroundRotation.copy(envRotation);\n\t\t\t\t} else {\n\t\t\t\t\t// Raw equirect was only PMREM input; the prefiltered probe has superseded it.\n\t\t\t\t\tenvMap.dispose();\n\t\t\t\t}\n\t\t\t\tconfig.events.onReady?.();\n\t\t\t},\n\t\t\tundefined,\n\t\t\tfunction (error) {\n\t\t\t\tif (isDisposed()) return;\n\t\t\t\tgetLogger().warn('HDR texture could not be loaded, falling back to basic lighting:', error);\n\t\t\t\tconfig.events.onReady?.();\n\t\t\t}\n\t\t);\n\t} else {\n\t\tconfig.events.onReady?.();\n\t}\n}\n\nexport function addFloor(scene: THREE.Scene, config: ResolvedOptions) {\n\tconst floorSize = config.floor.size;\n\tconst floorGeometry = new THREE.PlaneGeometry(floorSize, floorSize);\n\n\tconst floorColor =\n\t\ttypeof config.floor.color === 'string'\n\t\t\t? new THREE.Color(config.floor.color)\n\t\t\t: config.floor.color;\n\n\tconst floorMaterial = new THREE.MeshStandardMaterial({\n\t\tcolor: floorColor,\n\t\troughness: config.floor.roughness,\n\t\tmetalness: config.floor.metalness,\n\t\tside: THREE.DoubleSide\n\t});\n\n\tconst floor = new THREE.Mesh(floorGeometry, floorMaterial);\n\tfloor.userData.id = 'floor';\n\tfloor.name = 'floor';\n\t// PlaneGeometry's default +Z normal is already correct for Z-up; orient to scene up for any other.\n\tconst up = (config.environment?.sceneUp || defaultUp).clone().normalize();\n\tfloor.quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), up);\n\tfloor.position.set(0, 0, 0);\n\n\tif (config.floor.receiveShadow && config.render.enableShadows) {\n\t\tfloor.receiveShadow = true;\n\t}\n\n\tscene.add(floor);\n}\n","import * as THREE from 'three';\n\nimport { cloneForTint, getBaseMaterial, getLogger } from '../../shared/index.js';\n\nimport type { CameraController } from '../camera-controller.js';\nimport { raycastVisible } from '../scene-pick.js';\nimport { computeContentBounds } from '../three-helpers.js';\nimport { highlightMembers, memberBounds, resolveHitMember } from './merged-picking.js';\nimport type { ResolvedOptions } from './defaults.js';\nimport type { TapTracker } from './tap-gesture.js';\nimport type { SelectionTarget } from './viewer.js';\n\n// Tuned against the brightest look (`showcase`, exposure 1.15 + sun 2.6): the highlight has to stay\n// obvious there without blowing out to a flat silhouette under the dimmest one.\n/** How far the base color moves toward the selection color. 1 would discard the texture underneath. */\nconst SELECTION_COLOR_MIX = 0.75;\n/** Above ~2 the tint clips to pure red and the geometry's shading disappears with it. */\nconst SELECTION_EMISSIVE_INTENSITY = 0.6;\n/** A metallic surface reflects the environment rather than showing its albedo tint. */\nconst SELECTION_MAX_METALNESS = 0.2;\n/** Below this a highlight reads as a ghost, so the selection is forced opaque. */\nconst SELECTION_MIN_OPACITY = 1;\n/**\n * Lighter than the default selection red on purpose: amber and red are close hues under red-green\n * color blindness, so what tells hover from selected there is brightness.\n */\nconst HOVER_COLOR = '#ffb020';\n/**\n * A fingertip covers far more than the pixel under its centre, so a touch that lands just beside a\n * thin curve or a narrow part still picks it. Rings are sampled only when the centre misses.\n */\nconst TOUCH_PICK_RADII_PX = [8, 16];\nconst TOUCH_PICK_DIRECTIONS = 8;\n/** Two taps closer than this in time and space are a double-tap. */\nconst DOUBLE_TAP_MS = 300;\nconst DOUBLE_TAP_SLOP_PX = 30;\n/** Linear RGB distance. Closer than this, a tint barely changes the surface. */\nconst MIN_TINT_DISTANCE = 0.35;\n/** Taken by a surface already close to the tint. Blue stays distinct under every common deficiency. */\nconst FALLBACK_TINT = new THREE.Color('#2f7bff');\n\nfunction readableTint(material: THREE.Material, tint: THREE.Color): THREE.Color {\n\tconst base = (material as THREE.MeshStandardMaterial).color as THREE.Color | undefined;\n\tif (!base) return tint;\n\tconst distance = Math.hypot(base.r - tint.r, base.g - tint.g, base.b - tint.b);\n\treturn distance < MIN_TINT_DISTANCE ? FALLBACK_TINT : tint;\n}\n\n/**\n * Recolors a material clone to mark it selected. Exported for tests: the highlight reads against\n * whatever the active look does to lighting, and that coupling has broken it silently before.\n *\n * Meshes tint via `emissive` AND base color. Emissive alone is additive on top of what the surface\n * already reflects, so on a bright white model under a key light it only shifts the surface slightly\n * pink — the brighter the look, the weaker the highlight. Moving the albedo toward the selection\n * color is what makes it hold at any exposure; the emissive on top keeps it reading as lit rather\n * than as a flat sticker in shadowed areas. Lines/points have no emissive channel, so their `color`\n * is replaced outright.\n *\n * Mutates `material` in place — pass a clone, never a material the scene still shares.\n */\nexport function tintForSelection(\n\tmaterial: THREE.Material,\n\tcolor: THREE.Color,\n\tisMesh: boolean\n): void {\n\tconst selectionColor = readableTint(material, color);\n\tif (isMesh && 'emissive' in material) {\n\t\tconst mesh = material as THREE.MeshStandardMaterial;\n\t\tmesh.emissive = selectionColor.clone();\n\t\tmesh.emissiveIntensity = SELECTION_EMISSIVE_INTENSITY;\n\t\t// A textured surface keeps its map; tinting the base color under it still reads.\n\t\tmesh.color.lerp(selectionColor, SELECTION_COLOR_MIX);\n\t\t// Chrome-like input would reflect the environment instead of showing the tint.\n\t\tmesh.metalness = Math.min(mesh.metalness, SELECTION_MAX_METALNESS);\n\t\t// Under the x-ray look every mesh is near-transparent with depth writes off, which would\n\t\t// leave the selection a faint ghost sorted behind its own neighbours. Selection is a UI\n\t\t// affordance, so it opts out of the look and renders solid.\n\t\tif (mesh.opacity < SELECTION_MIN_OPACITY) {\n\t\t\tmesh.opacity = SELECTION_MIN_OPACITY;\n\t\t\tmesh.transparent = false;\n\t\t\tmesh.depthWrite = true;\n\t\t}\n\t\treturn;\n\t}\n\tif ('color' in material) {\n\t\t(material as THREE.LineBasicMaterial).color = selectionColor.clone();\n\t}\n}\n\nexport function setupEventHandlers(\n\tcanvas: HTMLCanvasElement,\n\tscene: THREE.Scene,\n\tcameraController: CameraController,\n\tconfig: ResolvedOptions,\n\ttaps: TapTracker\n): {\n\tdispose: () => void;\n\tfitToView: () => void;\n\tclearSelection: () => void;\n\tselectEntries: (targets: SelectionTarget[]) => void;\n\tsetHover: (object: THREE.Object3D | null, memberIndex?: number | null) => void;\n} {\n\tconst selectedObjects = new Set<THREE.Object3D>();\n\tconst originalMaterials = new Map<THREE.Object3D, THREE.Material | THREE.Material[]>();\n\t// One per merged mesh with selected members: restores the geometry's groups and material array,\n\t// which a plain material swap can't undo. Owns its highlight clone, which never becomes the\n\t// mesh's `material` and so isn't reached by the clone disposal below.\n\tconst memberHighlights: { restore: () => void; material: THREE.Material }[] = [];\n\t/** Which member of a merged mesh is selected, so a click on a sibling isn't a no-op. */\n\tlet selectedMemberIndex: number | null = null;\n\t// True while `selectEntries` drew the selection: a click on one of those objects is a fresh pick,\n\t// not a re-click of what it already selected.\n\tlet selectedByEntries = false;\n\tconst raycaster = new THREE.Raycaster();\n\tconst mouse = new THREE.Vector2();\n\tconst getActiveCamera = () => cameraController.getActiveCamera();\n\n\tconst fitToView = () => {\n\t\tconst box = computeContentBounds(scene);\n\n\t\tif (box.isEmpty()) {\n\t\t\tgetLogger().warn('No objects to fit to view');\n\t\t\treturn;\n\t\t}\n\n\t\t// Via the controller, not the perspective camera directly: it repositions whichever camera is\n\t\t// live and re-derives the ortho frustum in 2D mode.\n\t\tcameraController.frameBounds(box, false);\n\t};\n\n\tconst selectionColorObj =\n\t\ttypeof config.events.selectionColor === 'string'\n\t\t\t? new THREE.Color(config.events.selectionColor)\n\t\t\t: config.events.selectionColor instanceof THREE.Color\n\t\t\t\t? config.events.selectionColor\n\t\t\t\t: new THREE.Color('#ff0000');\n\n\tconst hoverColorObj = new THREE.Color(HOVER_COLOR);\n\tlet hoverObject: (THREE.Object3D & { material?: THREE.Material | THREE.Material[] }) | null =\n\t\tnull;\n\tlet hoverOriginal: THREE.Material | THREE.Material[] | null = null;\n\tlet hoverMember: { restore: () => void; material: THREE.Material } | null = null;\n\n\t// If the object left the scene, no traversal can reach the original material just restored to\n\t// it, so dispose it here. Compute content is cleared wholesale per solve, so a detached object's\n\t// material has no surviving sharers.\n\tconst disposeIfDetached = (\n\t\tobject: THREE.Object3D,\n\t\toriginal: THREE.Material | THREE.Material[]\n\t) => {\n\t\tlet root = object;\n\t\twhile (root.parent) root = root.parent;\n\t\tif (root === scene) return;\n\t\tif (original instanceof THREE.Material) original.dispose();\n\t\telse original.forEach((m) => m.dispose());\n\t};\n\n\tconst clearHover = () => {\n\t\thoverMember?.restore();\n\t\thoverMember?.material.dispose();\n\t\thoverMember = null;\n\t\tif (hoverObject && hoverOriginal) {\n\t\t\tconst clone = hoverObject.material;\n\t\t\tif (clone instanceof THREE.Material) clone.dispose();\n\t\t\thoverObject.material = hoverOriginal;\n\t\t\tdisposeIfDetached(hoverObject, hoverOriginal);\n\t\t}\n\t\thoverObject = null;\n\t\thoverOriginal = null;\n\t};\n\n\t/**\n\t * Previews an outliner row in the viewport. Fires no events, and yields to a selection already\n\t * tinting the same thing.\n\t */\n\tconst setHover = (object: THREE.Object3D | null, memberIndex: number | null = null) => {\n\t\tclearHover();\n\t\tif (!object) return;\n\n\t\tif (memberIndex !== null && object instanceof THREE.Mesh) {\n\t\t\tconst material = getBaseMaterial(object).clone();\n\t\t\ttintForSelection(material, hoverColorObj, true);\n\t\t\tconst members = new Set([memberIndex]);\n\t\t\thoverMember = { restore: highlightMembers(object, members, material, 'hover'), material };\n\t\t\treturn;\n\t\t}\n\n\t\tif (selectedObjects.has(object)) return;\n\t\tconst target = object as THREE.Object3D & { material?: THREE.Material | THREE.Material[] };\n\t\tif (!(target.material instanceof THREE.Material)) return;\n\t\tconst clone = cloneForTint(target.material);\n\t\ttintForSelection(clone, hoverColorObj, object instanceof THREE.Mesh);\n\t\thoverObject = target;\n\t\thoverOriginal = target.material;\n\t\ttarget.material = clone;\n\t};\n\n\tconst clearSelection = () => {\n\t\tfor (const { restore, material } of memberHighlights) {\n\t\t\trestore();\n\t\t\tmaterial.dispose();\n\t\t}\n\t\tmemberHighlights.length = 0;\n\t\tselectedObjects.forEach((obj) => {\n\t\t\tconst restorable = obj as THREE.Object3D & {\n\t\t\t\tmaterial?: THREE.Material | THREE.Material[];\n\t\t\t};\n\t\t\tif (originalMaterials.has(obj)) {\n\t\t\t\tconst original = originalMaterials.get(obj)!;\n\t\t\t\tconst clone = restorable.material; // dispose the highlight clone before restoring\n\t\t\t\tif (clone instanceof THREE.Material) clone.dispose();\n\t\t\t\telse if (Array.isArray(clone)) clone.forEach((m) => m.dispose());\n\t\t\t\trestorable.material = original;\n\t\t\t\toriginalMaterials.delete(obj);\n\n\t\t\t\tdisposeIfDetached(obj, original);\n\t\t\t}\n\t\t});\n\t\tselectedObjects.clear();\n\t\tselectedMemberIndex = null;\n\t\tselectedByEntries = false;\n\t};\n\n\t/**\n\t * Tints the picked object. On a merged mesh only the given members are tinted, so clicking one\n\t * wall doesn't light up every wall sharing its material — see `merged-picking.ts`.\n\t */\n\tconst applyHighlight = (object: THREE.Object3D, members: ReadonlySet<number> | null): boolean => {\n\t\tif (members && object instanceof THREE.Mesh) {\n\t\t\t// The material is an array while some members are hidden, so read the base through\n\t\t\t// the draw-state owner rather than off the mesh.\n\t\t\tconst clone = getBaseMaterial(object).clone();\n\t\t\ttintForSelection(clone, selectionColorObj, true);\n\t\t\tmemberHighlights.push({\n\t\t\t\trestore: highlightMembers(object, members, clone),\n\t\t\t\tmaterial: clone\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\n\t\tconst target = object as THREE.Object3D & { material?: THREE.Material | THREE.Material[] };\n\t\tif (!(target.material instanceof THREE.Material)) return false;\n\n\t\tconst clone = cloneForTint(target.material);\n\t\ttintForSelection(clone, selectionColorObj, object instanceof THREE.Mesh);\n\n\t\toriginalMaterials.set(object, target.material);\n\t\ttarget.material = clone;\n\t\treturn true;\n\t};\n\n\t/**\n\t * Selection made outside the viewport. An empty list only drops such a selection, so a host\n\t * syncing its own empty state can't wipe what a click on the canvas just selected.\n\t */\n\tconst selectEntries = (targets: SelectionTarget[]) => {\n\t\tif (targets.length === 0 && !selectedByEntries) return;\n\n\t\tclearHover();\n\t\tclearSelection();\n\n\t\tconst membersByMesh = new Map<THREE.Object3D, Set<number>>();\n\t\tfor (const { object, memberIndex } of targets) {\n\t\t\tif (memberIndex === null) {\n\t\t\t\tselectedObjects.add(object);\n\t\t\t\tapplyHighlight(object, null);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet members = membersByMesh.get(object);\n\t\t\tif (!members) membersByMesh.set(object, (members = new Set()));\n\t\t\tmembers.add(memberIndex);\n\t\t}\n\t\tfor (const [object, members] of membersByMesh) {\n\t\t\tselectedObjects.add(object);\n\t\t\tapplyHighlight(object, members);\n\t\t}\n\t\tselectedByEntries = targets.length > 0;\n\t};\n\n\t// Points picking tolerance, scaled to scene size so it holds at any zoom. Fat Line2 uses its own\n\t// material linewidth instead, so no separate threshold is needed for lines.\n\t//\n\t// `computeContentBounds` traverses the whole scene, which is far too slow to redo on every\n\t// click. The scene only changes wholesale between solves, so the diagonal is cached and\n\t// recomputed when the child count changes.\n\tlet cachedDiagonal: number | null = null;\n\tlet cachedChildCount = -1;\n\tconst updatePickThresholds = () => {\n\t\tif (cachedDiagonal === null || scene.children.length !== cachedChildCount) {\n\t\t\tconst box = computeContentBounds(scene);\n\t\t\tcachedDiagonal = box.isEmpty() ? 1 : box.getSize(new THREE.Vector3()).length();\n\t\t\tcachedChildCount = scene.children.length;\n\t\t}\n\t\traycaster.params.Points.threshold = cachedDiagonal * 0.01;\n\t};\n\n\tconst setPointerNdc = (clientX: number, clientY: number) => {\n\t\tconst rect = canvas.getBoundingClientRect();\n\t\tmouse.x = ((clientX - rect.left) / rect.width) * 2 - 1;\n\t\tmouse.y = -((clientY - rect.top) / rect.height) * 2 + 1;\n\t};\n\n\t/**\n\t * Nearest visible, pickable object under the pointer, or null. Goes through the BVH pick, which\n\t * also skips viewer aids: the grid is a huge camera-following plane, so double-clicking through\n\t * a thin curve onto it would frame a box the size of the fade radius and fling the camera out.\n\t */\n\tconst pickAt = (clientX: number, clientY: number): THREE.Intersection | null => {\n\t\tsetPointerNdc(clientX, clientY);\n\t\tupdatePickThresholds();\n\t\traycaster.setFromCamera(mouse, getActiveCamera());\n\t\treturn raycastVisible(scene, raycaster)[0] ?? null;\n\t};\n\n\tconst pickNear = (clientX: number, clientY: number, touch: boolean) => {\n\t\tconst centre = pickAt(clientX, clientY);\n\t\tif (centre || !touch) return centre;\n\t\tfor (const radius of TOUCH_PICK_RADII_PX) {\n\t\t\tlet nearest: THREE.Intersection | null = null;\n\t\t\tfor (let i = 0; i < TOUCH_PICK_DIRECTIONS; i++) {\n\t\t\t\tconst angle = (i / TOUCH_PICK_DIRECTIONS) * Math.PI * 2;\n\t\t\t\tconst hit = pickAt(clientX + Math.cos(angle) * radius, clientY + Math.sin(angle) * radius);\n\t\t\t\tif (hit && (!nearest || hit.distance < nearest.distance)) nearest = hit;\n\t\t\t}\n\t\t\tif (nearest) return nearest;\n\t\t}\n\t\treturn null;\n\t};\n\n\tlet lastTap: { time: number; x: number; y: number } | null = null;\n\t/** iOS fires no `dblclick` for a double-tap, so touch pairs its own taps. */\n\tconst isSecondTap = (event: MouseEvent): boolean => {\n\t\tconst previous = lastTap;\n\t\tlastTap = { time: event.timeStamp, x: event.clientX, y: event.clientY };\n\t\tif (!previous) return false;\n\t\tconst isDouble =\n\t\t\tevent.timeStamp - previous.time <= DOUBLE_TAP_MS &&\n\t\t\tMath.hypot(event.clientX - previous.x, event.clientY - previous.y) <= DOUBLE_TAP_SLOP_PX;\n\t\tif (isDouble) lastTap = null;\n\t\treturn isDouble;\n\t};\n\n\tconst handleCanvasClick = (event: MouseEvent) => {\n\t\tif (!taps.wasTap()) return;\n\t\tconst touch = taps.pointerType() !== 'mouse';\n\t\tif (touch && isSecondTap(event)) {\n\t\t\thandleDoubleTap(event.clientX, event.clientY, true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Acted on immediately, including the first press of a double-click: selecting and reporting\n\t\t// metadata are idempotent, so the second press re-reports the same object. Deferring this to\n\t\t// wait out a possible `dblclick` made every selection feel a quarter-second late.\n\t\tconst hit = pickNear(event.clientX, event.clientY, touch);\n\n\t\tif (!hit) {\n\t\t\tclearSelection();\n\t\t\tsetPointerNdc(event.clientX, event.clientY);\n\t\t\tconfig.events?.onBackgroundClicked?.({ x: mouse.x, y: mouse.y });\n\t\t\treturn;\n\t\t}\n\n\t\tconst clickedObject = hit.object;\n\t\tconst resolved = resolveHitMember(hit);\n\t\tconst hitMemberIndex = resolved?.index ?? null;\n\n\t\t// A merged mesh stays one object across clicks, so object identity alone would treat a click\n\t\t// on a different member as re-clicking the current selection and do nothing.\n\t\tif (\n\t\t\t!selectedByEntries &&\n\t\t\tselectedObjects.has(clickedObject) &&\n\t\t\thitMemberIndex === selectedMemberIndex\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tclearHover();\n\t\tclearSelection();\n\t\tselectedObjects.add(clickedObject);\n\t\tselectedMemberIndex = hitMemberIndex;\n\t\tapplyHighlight(clickedObject, resolved ? new Set([resolved.index]) : null);\n\n\t\tconfig.events?.onObjectSelected?.(clickedObject);\n\n\t\t// The member's own name/layer/metadata, so the panel describes the object under the cursor\n\t\t// rather than the merged group's first member.\n\t\tif (resolved) {\n\t\t\tconfig.events?.onMeshMetadataClicked?.({\n\t\t\t\tsource: clickedObject.userData?.source,\n\t\t\t\tname: resolved.member.name,\n\t\t\t\tlayer: resolved.member.layer,\n\t\t\t\ttrackingKey: resolved.member.trackingKey,\n\t\t\t\tmetadata: resolved.member.metadata\n\t\t\t});\n\t\t} else if (\n\t\t\tclickedObject instanceof THREE.Mesh &&\n\t\t\tObject.keys(clickedObject.userData).length > 0\n\t\t) {\n\t\t\tconfig.events?.onMeshMetadataClicked?.(clickedObject.userData);\n\t\t}\n\t};\n\n\tconst handleDoubleClick = (event: MouseEvent) => {\n\t\t// Touch double-taps are paired in `handleCanvasClick`; Android's own `dblclick` would repeat it.\n\t\tif (!taps.wasTap() || taps.pointerType() !== 'mouse') return;\n\t\thandleDoubleTap(event.clientX, event.clientY, false);\n\t};\n\n\tconst handleDoubleTap = (clientX: number, clientY: number, touch: boolean) => {\n\t\tconst hit = pickNear(clientX, clientY, touch);\n\t\tif (!hit) {\n\t\t\t// The way back out after framing a part. On touch there is no F key, and a pinch-out\n\t\t\t// from a small part back to the whole model takes many gestures.\n\t\t\tif (config.events?.enableDoubleClickZoom) {\n\t\t\t\tcameraController.frameBounds(computeContentBounds(scene), true);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tconst target = hit.object;\n\n\t\tconfig.events?.onMeshDoubleClicked?.(target);\n\n\t\tif (!config.events?.enableDoubleClickZoom) return;\n\n\t\t// On a merged mesh, framing the object would frame the whole material group; frame just the\n\t\t// member under the cursor instead.\n\t\tconst resolved = resolveHitMember(hit);\n\t\tconst box =\n\t\t\tresolved && target instanceof THREE.Mesh\n\t\t\t\t? memberBounds(target, resolved.member)\n\t\t\t\t: new THREE.Box3().setFromObject(target);\n\t\tif (box.isEmpty()) return;\n\t\t// Via the controller so the active camera moves (translating an ortho camera alone zooms\n\t\t// nothing). The resulting tween is cancellable — a rapid second double-click replaces it\n\t\t// rather than racing it.\n\t\tcameraController.frameBounds(box, true);\n\t};\n\n\tconst handleKeydown = (event: KeyboardEvent) => {\n\t\tif (!config.events?.enableKeyboardControls) return;\n\n\t\tswitch (event.key.toLowerCase()) {\n\t\t\tcase 'f':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfitToView();\n\t\t\t\tbreak;\n\t\t\tcase 'escape':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tclearSelection();\n\t\t\t\tbreak;\n\t\t\tcase ' ':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfitToView();\n\t\t\t\tbreak;\n\t\t}\n\t};\n\n\tif (config.events?.enableClickToFocus) {\n\t\tcanvas.addEventListener('click', handleCanvasClick);\n\t\tcanvas.addEventListener('dblclick', handleDoubleClick);\n\t}\n\n\tif (config.events?.enableKeyboardControls) {\n\t\tcanvas.setAttribute('tabindex', '0');\n\t\tcanvas.addEventListener('keydown', handleKeydown);\n\t}\n\n\tconst dispose = () => {\n\t\tcanvas.removeEventListener('click', handleCanvasClick);\n\t\tcanvas.removeEventListener('dblclick', handleDoubleClick);\n\t\tcanvas.removeEventListener('keydown', handleKeydown);\n\t\tclearHover();\n\t\tclearSelection();\n\t};\n\n\treturn { dispose, fitToView, clearSelection, selectEntries, setHover };\n}\n","// A `click` alone can't tell a tap from the end of a camera gesture on touch: the browser fires the\n// compatibility `mousedown` and `click` together after the finger lifts, both at the lift point, so\n// comparing their positions never rejects anything. Pointer events carry the real start, the travel\n// in between, and how many fingers took part.\n\n/** A mouse press is precise; past this it was a drag to orbit or pan. */\nconst MOUSE_SLOP_PX = 5;\n/** A fingertip wobbles a few pixels on a deliberate tap. */\nconst TOUCH_SLOP_PX = 10;\n/** A touch held this long was the start of an orbit that never moved, not a tap. */\nconst TOUCH_LONG_PRESS_MS = 500;\n\nexport interface TapTracker {\n\t/**\n\t * Whether the pointer gesture that just ended was a tap: one pointer, held still, released\n\t * promptly. Read it from `click`/`dblclick`, which the browser fires after `pointerup`.\n\t */\n\twasTap(): boolean;\n\t/** `pointerType` of the gesture that just ended: `mouse`, `touch` or `pen`. */\n\tpointerType(): string;\n\tdispose(): void;\n}\n\nexport function trackTaps(canvas: HTMLCanvasElement): TapTracker {\n\tconst down = new Map<number, { x: number; y: number }>();\n\tlet pointerType = 'mouse';\n\tlet startTime = 0;\n\tlet maxPointers = 0;\n\tlet maxTravel = 0;\n\tlet cancelled = false;\n\tlet lastWasTap = false;\n\n\tconst travel = (event: PointerEvent) => {\n\t\tconst start = down.get(event.pointerId);\n\t\tif (!start) return;\n\t\tmaxTravel = Math.max(maxTravel, Math.hypot(event.clientX - start.x, event.clientY - start.y));\n\t};\n\n\tconst onDown = (event: PointerEvent) => {\n\t\tif (down.size === 0) {\n\t\t\tpointerType = event.pointerType;\n\t\t\tstartTime = event.timeStamp;\n\t\t\tmaxPointers = 0;\n\t\t\tmaxTravel = 0;\n\t\t\tcancelled = false;\n\t\t}\n\t\tdown.set(event.pointerId, { x: event.clientX, y: event.clientY });\n\t\tmaxPointers = Math.max(maxPointers, down.size);\n\t};\n\n\tconst onMove = (event: PointerEvent) => travel(event);\n\n\tconst onEnd = (event: PointerEvent) => {\n\t\tif (!down.has(event.pointerId)) return;\n\t\ttravel(event);\n\t\tif (event.type === 'pointercancel') cancelled = true;\n\t\tdown.delete(event.pointerId);\n\t\tif (down.size > 0) return;\n\n\t\tconst isMouse = pointerType === 'mouse';\n\t\tlastWasTap =\n\t\t\t!cancelled &&\n\t\t\tmaxPointers === 1 &&\n\t\t\tmaxTravel <= (isMouse ? MOUSE_SLOP_PX : TOUCH_SLOP_PX) &&\n\t\t\t(isMouse || event.timeStamp - startTime <= TOUCH_LONG_PRESS_MS);\n\t};\n\n\t// Capture phase, so the verdict is in place before any bubble-phase listener reads it.\n\tconst options = { capture: true, passive: true };\n\tcanvas.addEventListener('pointerdown', onDown, options);\n\tcanvas.addEventListener('pointermove', onMove, options);\n\tcanvas.addEventListener('pointerup', onEnd, options);\n\tcanvas.addEventListener('pointercancel', onEnd, options);\n\n\treturn {\n\t\twasTap: () => lastWasTap,\n\t\tpointerType: () => pointerType,\n\t\tdispose: () => {\n\t\t\tcanvas.removeEventListener('pointerdown', onDown, options);\n\t\t\tcanvas.removeEventListener('pointermove', onMove, options);\n\t\t\tcanvas.removeEventListener('pointerup', onEnd, options);\n\t\t\tcanvas.removeEventListener('pointercancel', onEnd, options);\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\n\nimport type { ResolvedOptions } from './defaults.js';\n\nexport function setupRenderer(\n\tcanvas: HTMLCanvasElement,\n\tconfig: ResolvedOptions,\n\tpixelRatio: number\n): THREE.WebGLRenderer {\n\tconst renderer = new THREE.WebGLRenderer({\n\t\tantialias: config.render.antialias,\n\t\tcanvas,\n\t\talpha: true,\n\t\tpowerPreference: 'high-performance',\n\t\tpreserveDrawingBuffer: config.render.preserveDrawingBuffer,\n\t\t// Deliberately NOT logarithmic: the GTAO pipeline reconstructs view-space positions assuming\n\t\t// standard perspective depth and doesn't support log-encoded depth (haloing, wrong-scale\n\t\t// occlusion if enabled). If log depth is ever needed, AO must be disabled alongside it.\n\t\tlogarithmicDepthBuffer: false\n\t});\n\n\tconst parent = canvas.parentElement;\n\tconst width = parent ? parent.clientWidth : window.innerWidth;\n\tconst height = parent ? parent.clientHeight : window.innerHeight;\n\n\tif (parent) {\n\t\tcanvas.style.width = '100%';\n\t\tcanvas.style.height = '100%';\n\t\tcanvas.style.display = 'block';\n\t}\n\n\trenderer.setSize(width, height, false);\n\trenderer.setPixelRatio(pixelRatio);\n\n\tif (config.render.enableShadows) {\n\t\trenderer.shadowMap.enabled = true;\n\t\trenderer.shadowMap.type = THREE.VSMShadowMap;\n\t}\n\n\trenderer.toneMapping = config.render.toneMapping!;\n\trenderer.toneMappingExposure = config.render.toneMappingExposure ?? 1.0;\n\trenderer.outputColorSpace = THREE.SRGBColorSpace;\n\n\trenderer.sortObjects = true;\n\n\treturn renderer;\n}\n","import * as THREE from 'three';\n\nimport { lookPreset, publishMaxAnisotropy } from '../../shared/index.js';\nimport { createCameraController } from '../camera-controller.js';\nimport {\n\tEDGES_SKIPPED_OVERLAY_BUDGET,\n\tEDGES_SKIPPED_TRIANGLE_CAP,\n\taddEdgesAsync,\n\ttype EdgeOptions,\n\tremoveEdges\n} from '../edges.js';\nimport { createGrid } from '../grid.js';\nimport { createLabelLayer, type LabelLayer } from '../label-layer.js';\nimport { createMeasureTool, type MeasureTool } from '../measure.js';\nimport { createNearPlaneFitter, type NearPlaneFitter } from '../near-plane.js';\nimport { SOURCE_USER, appSource, isOwnedBy } from '../scene-ownership.js';\nimport { computeContentBounds } from '../three-helpers.js';\nimport { createToolRegistry } from '../tool-registry.js';\nimport type { ThreeInitializerOptions } from '../types.js';\nimport { upToAxis } from '../up-axis.js';\nimport { createViewGizmo } from '../view-gizmo.js';\nimport { createAnimationLoop } from './animation-loop.js';\nimport { createAppearanceController, lookPixelRatio } from './appearance.js';\nimport { createCamera } from './create-camera.js';\nimport { createScene } from './create-scene.js';\nimport { applyDefaults, defaultUp } from './defaults.js';\nimport { disposeObjectTree, disposeSceneResources } from './dispose.js';\nimport { createIdleRefiner } from './idle-refine.js';\nimport { createPipelineController } from './pipeline-controller.js';\nimport { setupControls } from './setup-controls.js';\nimport { addFloor, setupEnvironment } from './setup-environment.js';\nimport { setupEventHandlers } from './setup-events.js';\nimport { trackTaps } from './tap-gesture.js';\nimport { fitShadowToContent, jitterSun, setupLighting } from './setup-lighting.js';\nimport { setupRenderer } from './setup-renderer.js';\nimport type { ThreeViewer } from './viewer.js';\n\nexport const initThree = function (\n\tcanvas: HTMLCanvasElement,\n\toptions?: ThreeInitializerOptions\n): ThreeViewer {\n\tconst config = applyDefaults(options || {});\n\n\tconst sceneUp = config.environment?.sceneUp || defaultUp;\n\n\t// Single source of truth for DPR (renderer, resize check, AO pipeline), kept in config because a\n\t// look can cap it; applyDefaults always sets it, the fallback here is just type narrowing.\n\tconst fullPixelRatio = config.render.pixelRatio ?? Math.min(window.devicePixelRatio, 2);\n\tconst pixelRatio = lookPixelRatio(\n\t\tfullPixelRatio,\n\t\tlookPreset(config.look!, config.render.lowPower)\n\t);\n\tconfig.render.pixelRatio = pixelRatio;\n\n\tconst scene = createScene(config);\n\tconst camera = createCamera(config, canvas);\n\t// Must happen before OrbitControls/the controller read camera.up (captured at construction), or\n\t// a Z-up scene orbits and frames as if Y-up.\n\tcamera.up.copy(sceneUp);\n\tconst renderer = setupRenderer(canvas, config, pixelRatio);\n\t// Published to a shared sink rather than imported, so render/ stays independent of parse/.\n\tpublishMaxAnisotropy(renderer.capabilities.getMaxAnisotropy());\n\toptions?.onMaxAnisotropy?.(renderer.capabilities.getMaxAnisotropy());\n\n\tconst controls = setupControls(camera, canvas, config);\n\n\t// Render loop, resize, and raycasting all read through getActiveCamera so 2D/3D stays coherent.\n\tconst cameraController = createCameraController({\n\t\tscene,\n\t\tperspective: camera,\n\t\tcontrols,\n\t\tonActiveCameraChange: () => {},\n\t\tup: sceneUp\n\t});\n\tconst getActiveCamera = () => cameraController.getActiveCamera();\n\n\t// HDR decodes asynchronously; setupEnvironment's load callback checks this to drop (and dispose)\n\t// the texture instead of attaching it to a scene torn down mid-fetch.\n\tlet disposed = false;\n\tsetupEnvironment(scene, renderer, config, () => disposed);\n\tconst lights = setupLighting(scene, config);\n\tconst sunlight = lights.sun;\n\n\tconst updateShadowBounds = () => {\n\t\tif (sunlight) fitShadowToContent(sunlight, computeContentBounds(scene));\n\t};\n\n\tif (config.floor?.enabled) {\n\t\taddFloor(scene, config);\n\t}\n\t// So the near-plane fitter below can consult the floor's live visibility.\n\tconst floorMesh = config.floor?.enabled\n\t\t? (scene.children.find((child) => child.userData.id === 'floor') ?? null)\n\t\t: null;\n\n\tconst grid = config.grid.enabled\n\t\t? createGrid({\n\t\t\t\tcellSize: config.grid.cellSize,\n\t\t\t\tmajorEvery: config.grid.majorEvery,\n\t\t\t\tcellColor: config.grid.cellColor,\n\t\t\t\tmajorColor: config.grid.majorColor,\n\t\t\t\tfadeDistance: config.grid.fadeDistance,\n\t\t\t\tplane: config.grid.plane\n\t\t\t})\n\t\t: null;\n\tif (grid) scene.add(grid.object);\n\n\tconst updateGridScale = () => {\n\t\tif (grid) grid.fitToContent(computeContentBounds(scene));\n\t};\n\n\tconst gizmo = config.gizmo.enabled\n\t\t? createViewGizmo({ camera, domElement: canvas, controller: cameraController })\n\t\t: null;\n\n\t// Only VISIBLE ground aids feed the near-plane fitter's clamp: the grid is commonly built hidden\n\t// so hosts can toggle it, and the clamp is the camera's height above the plane — a hidden grid\n\t// would still drive near→0 at grazing views and crater depth precision, punching hidden edges\n\t// through geometry.\n\tconst gridPlane = config.grid.plane ?? upToAxis(sceneUp);\n\tconst gridNormal = new THREE.Vector3(\n\t\tgridPlane === 'x' ? 1 : 0,\n\t\tgridPlane === 'y' ? 1 : 0,\n\t\tgridPlane === 'z' ? 1 : 0\n\t);\n\tconst floorNormal = sceneUp.clone().normalize();\n\tconst groundNormals = (): THREE.Vector3[] => {\n\t\tconst normals: THREE.Vector3[] = [];\n\t\tif (grid?.object.visible) normals.push(gridNormal);\n\t\tif (config.floor.enabled && floorMesh?.visible) normals.push(floorNormal);\n\t\treturn normals;\n\t};\n\tconst nearFitter: NearPlaneFitter | null = config.camera.dynamicNear\n\t\t? createNearPlaneFitter({ camera, scene, groundNormals })\n\t\t: null;\n\n\t// Built unconditionally: measure was the first consumer, but any tool or app annotating the\n\t// scene needs it, and gating it behind measure.enabled left them with no way to get one.\n\tconst labelContainer = canvas.parentElement ?? canvas;\n\tconst labelLayer: LabelLayer = createLabelLayer(labelContainer, scene);\n\tconst measureTool: MeasureTool | null = config.measure.enabled\n\t\t? createMeasureTool({\n\t\t\t\tcanvas,\n\t\t\t\tscene,\n\t\t\t\tgetActiveCamera,\n\t\t\t\tgetViewTarget: () => controls.target,\n\t\t\t\trequestRender: () => requestRender(),\n\t\t\t\tlabelLayer,\n\t\t\t\toptions: {\n\t\t\t\t\tsnapPixels: config.measure.snapPixels,\n\t\t\t\t\tcolor: config.measure.color,\n\t\t\t\t\tlabelClassName: config.measure.labelClassName,\n\t\t\t\t\tdisplayUnit: config.measure.displayUnit,\n\t\t\t\t\tformat: config.measure.format\n\t\t\t\t}\n\t\t\t})\n\t\t: null;\n\n\tconst taps = trackTaps(canvas);\n\tconst eventHandlers =\n\t\tconfig.events.enableEventHandlers !== false\n\t\t\t? setupEventHandlers(canvas, scene, cameraController, config, taps)\n\t\t\t: {\n\t\t\t\t\tdispose: () => {},\n\t\t\t\t\tfitToView: () => {},\n\t\t\t\t\tclearSelection: () => {},\n\t\t\t\t\tselectEntries: () => {},\n\t\t\t\t\tsetHover: () => {}\n\t\t\t\t};\n\n\t// Built-ins register at the priorities documented on ToolRegistration, so a host tool can slot\n\t// above or below them. Listeners are attached unconditionally — a tool can register at any time.\n\tconst tools = createToolRegistry();\n\tif (measureTool) tools.register({ id: 'measure', tool: measureTool, priority: 0 });\n\tif (gizmo) tools.register({ id: 'gizmo', tool: gizmo, priority: -100 });\n\n\t// Capture-phase so tools see the click before bubble-phase selection; the first to claim it\n\t// wins, and stopImmediatePropagation keeps selection from also firing.\n\tconst handleToolClick = (event: MouseEvent) => {\n\t\tif (!taps.wasTap()) return;\n\t\tif (tools.handleClick(event)) event.stopImmediatePropagation();\n\t};\n\tcanvas.addEventListener('click', handleToolClick, { capture: true });\n\n\t// Passive: moves only drive previews, never consume, so they can't interfere with orbit/pan.\n\tconst handleToolMove = (event: MouseEvent) => tools.handleMove(event);\n\tcanvas.addEventListener('mousemove', handleToolMove, { passive: true });\n\n\t// Rebound to the animation loop's real invalidate once it's created below.\n\tlet requestRender: () => void = () => {};\n\n\t// Applies regardless of edges.enabled — an explicit call should never be silently ignored.\n\t// Meshes the overlay path skipped — too large, or past the scene-wide overlay budget — switch the\n\t// screen-space edge fallback on; a later solve without such meshes switches it back off.\n\tconst applyEdges = (root: THREE.Object3D, overrides?: Partial<EdgeOptions>) => {\n\t\tvoid addEdgesAsync(root, {\n\t\t\tcolor: config.edges.color,\n\t\t\tdarken: config.edges.darken,\n\t\t\twidth: config.edges.width,\n\t\t\tthresholdAngle: config.edges.thresholdAngle,\n\t\t\tdistanceFade: config.edges.distanceFade,\n\t\t\tmaxTriangles: config.edges.maxTriangles,\n\t\t\tmaxSegments: config.edges.maxSegments,\n\t\t\tmaxOverlays: config.edges.maxOverlays,\n\t\t\t// Last, so a caller's override actually wins over the configured value.\n\t\t\t...overrides\n\t\t}).then(() => {\n\t\t\tupdateEdgeFallback(root);\n\t\t\trequestRender(); // async attach may land after the solve's own repaint\n\t\t});\n\t};\n\n\tconst updateEdgeFallback = (root: THREE.Object3D) => {\n\t\tif (config.edges.screenSpaceFallback === false) return;\n\t\tlet hasSkippedMeshes = false;\n\t\troot.traverse((object) => {\n\t\t\tconst skipped = object.userData?.edgesSkipped;\n\t\t\tif (skipped === EDGES_SKIPPED_TRIANGLE_CAP || skipped === EDGES_SKIPPED_OVERLAY_BUDGET) {\n\t\t\t\thasSkippedMeshes = true;\n\t\t\t}\n\t\t});\n\t\tpipeline.setEdgeFallback(hasSkippedMeshes);\n\t};\n\n\t// Also stands down the screen-space fallback — bare removeEdges alone would keep drawing lines\n\t// for capped meshes.\n\tconst clearEdges = (root: THREE.Object3D) => {\n\t\tremoveEdges(root);\n\t\tpipeline.setEdgeFallback(false);\n\t\trequestRender();\n\t};\n\n\tconst parent = canvas.parentElement;\n\tconst getCanvasSize = () =>\n\t\tparent\n\t\t\t? { width: parent.clientWidth, height: parent.clientHeight }\n\t\t\t: { width: window.innerWidth, height: window.innerHeight };\n\n\tconst pipeline = createPipelineController({\n\t\trenderer,\n\t\tscene,\n\t\tgetActiveCamera,\n\t\tgetCanvasSize,\n\t\tconfig,\n\t\trequestRender: () => requestRender()\n\t});\n\tpipeline.sync();\n\n\tconst appearance = createAppearanceController({\n\t\tscene,\n\t\trenderer,\n\t\tlights,\n\t\tconfig,\n\t\tpipeline,\n\t\tfullPixelRatio,\n\t\trequestRender: () => requestRender()\n\t});\n\n\tlet restoreSun: (() => void) | undefined;\n\tconst refiner = createIdleRefiner({\n\t\tscene,\n\t\trenderer,\n\t\tgetPipeline: () => pipeline.get(),\n\t\tframes: config.render.idleRefineFrames!,\n\t\tlightOrbitFrameMs: config.render.lightOrbitFrameMs!,\n\t\tjitterLights: (index) => {\n\t\t\tif (sunlight) restoreSun = jitterSun(sunlight, index);\n\t\t\tappearance.jitterEmissiveLights(index);\n\t\t},\n\t\trestoreLights: () => {\n\t\t\trestoreSun?.();\n\t\t\tappearance.settleEmissiveLights();\n\t\t}\n\t});\n\n\tconst {\n\t\tanimate,\n\t\tdispose: disposeAnimation,\n\t\tinvalidate,\n\t\trenderNow\n\t} = createAnimationLoop(\n\t\trenderer,\n\t\tscene,\n\t\tcamera,\n\t\tgetActiveCamera,\n\t\tcameraController,\n\t\tcontrols,\n\t\tgetCanvasSize,\n\t\t() => config.render.pixelRatio!,\n\t\tconfig.events.onFrame,\n\t\tgrid,\n\t\tgizmo,\n\t\t() => pipeline.get(),\n\t\tlabelLayer,\n\t\tnearFitter,\n\t\tconfig.render.onDemand ?? true,\n\t\trefiner\n\t);\n\trequestRender = invalidate;\n\tanimate();\n\n\tscene.up.set(sceneUp.x, sceneUp.y, sceneUp.z);\n\n\t// Initial fit for geometry already present; hosts loading more later via updateScene should\n\t// call these again.\n\tupdateShadowBounds();\n\tupdateGridScale();\n\n\tconst captureImage = (type = 'image/png', quality?: number): Promise<Blob | null> => {\n\t\t// Draw and read in the same task: without preserveDrawingBuffer the colour buffer is gone\n\t\t// once the browser composites, and a deferred toBlob returns a blank image.\n\t\trenderNow();\n\t\treturn new Promise((resolve) => renderer.domElement.toBlob(resolve, type, quality));\n\t};\n\n\tconst addUserGeometry = (object: THREE.Object3D, appId?: string) => {\n\t\tobject.userData.source = appId === undefined ? SOURCE_USER : appSource(appId);\n\t\tscene.add(object);\n\t\trequestRender();\n\t};\n\n\tconst removeUserGeometry = (object: THREE.Object3D) => {\n\t\tobject.removeFromParent();\n\t\tdisposeObjectTree(object);\n\t\trequestRender();\n\t};\n\n\tconst clearUserGeometry = (appId?: string) => {\n\t\t// Snapshot first: removeFromParent would mutate scene.children mid-iteration otherwise.\n\t\tconst owned = scene.children.filter((child) => isOwnedBy(child, appId));\n\t\towned.forEach((object) => {\n\t\t\tobject.removeFromParent();\n\t\t\tdisposeObjectTree(object);\n\t\t});\n\t\trequestRender();\n\t};\n\n\tconst dispose = () => {\n\t\t// Idempotent: a second call would re-run forceContextLoss() on an already-lost context and\n\t\t// throw (double-dispose happens naturally under React StrictMode).\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tdisposeAnimation();\n\t\teventHandlers.dispose();\n\t\ttaps.dispose();\n\t\tcanvas.removeEventListener('click', handleToolClick, { capture: true });\n\t\tcanvas.removeEventListener('mousemove', handleToolMove);\n\t\tmeasureTool?.dispose();\n\t\tlabelLayer?.dispose();\n\t\tgizmo?.dispose();\n\t\tgrid?.dispose();\n\t\tpipeline.dispose();\n\t\t// Stops any in-flight camera tween — its rAF ticks would otherwise keep touching the\n\t\t// disposed controls after teardown.\n\t\tcameraController.dispose();\n\t\tcontrols.dispose();\n\t\trenderer.dispose();\n\t\t// Frees the GL context itself: browsers cap live WebGL contexts (~16), and otherwise it won't\n\t\t// be reclaimed until GC, which can lag across rapid mount/unmount cycles.\n\t\trenderer.forceContextLoss();\n\n\t\tdisposeSceneResources(scene);\n\n\t\t// Cross-solve caches (parse/'s, reached via a registry rather than an import — layer rule)\n\t\t// outlive any single scene but not the GL context just destroyed. Refcounted: only the last\n\t\t// live viewer actually frees, and this must run after the scene sweep above.\n\t};\n\n\treturn {\n\t\tscene,\n\t\tcamera,\n\t\tcontrols,\n\t\trenderer,\n\t\tcameraController,\n\t\tgrid,\n\t\tgizmo,\n\t\tmeasureTool,\n\t\tlabelLayer,\n\t\ttools,\n\t\tapplyEdges,\n\t\tclearEdges,\n\t\tinvalidate,\n\t\tcaptureImage,\n\t\tsetAmbientOcclusion: pipeline.setAmbientOcclusion,\n\t\tsetLook: appearance.setLook,\n\t\tsyncEmissiveLights: appearance.syncEmissiveLights,\n\t\tsetFillLights: appearance.setFillLights,\n\t\tsetEnvironmentIntensity: appearance.setEnvironmentIntensity,\n\t\tsetToneMappingExposure: appearance.setToneMappingExposure,\n\t\tsetAoIntensity: appearance.setAoIntensity,\n\t\tgetMaterialAppearance: appearance.getMaterialAppearance,\n\t\tupdateShadowBounds,\n\t\tupdateGridScale,\n\t\tdispose,\n\t\tfitToView: eventHandlers.fitToView,\n\t\tclearSelection: eventHandlers.clearSelection,\n\t\tselectEntries: (targets) => {\n\t\t\teventHandlers.selectEntries(targets);\n\t\t\tinvalidate();\n\t\t},\n\t\tsetHover: (object, memberIndex) => {\n\t\t\teventHandlers.setHover(object, memberIndex);\n\t\t\tinvalidate();\n\t\t},\n\t\taddUserGeometry,\n\t\tremoveUserGeometry,\n\t\tclearUserGeometry\n\t};\n};\n"],"mappings":"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"}