@popaya/pgsg-viewer 0.1.11 → 0.2.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.
@@ -10,7 +10,7 @@ var PGSGViewer = class {
10
10
  const { PGSGPlayCanvasViewer } = await import("./pgsg-playcanvas-viewer-4WORM4WU.js");
11
11
  this.engine = new PGSGPlayCanvasViewer(this.options);
12
12
  } else {
13
- const { PGSGThreeViewer } = await import("./pgsg-three-viewer-KBOYGHT3.js");
13
+ const { PGSGThreeViewer } = await import("./pgsg-three-viewer-5BUDBGSE.js");
14
14
  this.engine = new PGSGThreeViewer(this.options);
15
15
  }
16
16
  await this.engine.load();
@@ -37,9 +37,12 @@ var PGSGViewer = class {
37
37
  enablePlacementMode(url) {
38
38
  return this.engine?.enablePlacementMode?.(url);
39
39
  }
40
+ enableInput(enabled) {
41
+ return this.engine?.enableInput?.(enabled);
42
+ }
40
43
  };
41
44
 
42
45
  export {
43
46
  PGSGViewer
44
47
  };
45
- //# sourceMappingURL=chunk-Z7DBLRJH.js.map
48
+ //# sourceMappingURL=chunk-LFICZTG7.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/viewer/pgsg-viewer.ts"],"sourcesContent":["import * as THREE from \"three\";\n\nimport type {\n ViewerType,\n PGSGViewerEngine,\n CameraMode,\n} from \"../viewers/types\";\nimport type { PGSGViewerOptions } from \"../core/types\";\n\nexport class PGSGViewer {\n private engine!: PGSGViewerEngine;\n\n constructor(\n private options: PGSGViewerOptions & { viewerType?: ViewerType },\n ) {}\n\n async load() {\n const type = this.options.viewerType ?? \"three\";\n\n if (type === \"playcanvas\") {\n const { PGSGPlayCanvasViewer } =\n await import(\"../viewers/playcanvas/pgsg-playcanvas-viewer\");\n this.engine = new PGSGPlayCanvasViewer(this.options);\n } else {\n const { PGSGThreeViewer } =\n await import(\"../viewers/three/pgsg-three-viewer\");\n this.engine = new PGSGThreeViewer(this.options);\n }\n\n await this.engine.load();\n this.engine.start?.();\n }\n\n resize() {\n this.engine?.resize();\n }\n\n destroy() {\n this.engine?.destroy();\n }\n\n setCameraMode(mode: CameraMode) {\n this.engine?.setCameraMode?.(mode);\n }\n\n finalizePolyline() {\n (this.engine as any)?.finalizePolyline?.();\n }\n\n deleteMeasurement(index: number) {\n (this.engine as any)?.deleteMeasurement?.(index);\n }\n\n async addModelFromUrl(url: string, position?: THREE.Vector3) {\n return this.engine?.addModelFromUrl?.(url, position);\n }\n\n enablePlacementMode(url: string) {\n return this.engine?.enablePlacementMode?.(url);\n }\n}\n"],"mappings":";AASO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YACU,SACR;AADQ;AAAA,EACP;AAAA,EAJK;AAAA,EAMR,MAAM,OAAO;AACX,UAAM,OAAO,KAAK,QAAQ,cAAc;AAExC,QAAI,SAAS,cAAc;AACzB,YAAM,EAAE,qBAAqB,IAC3B,MAAM,OAAO,sCAA8C;AAC7D,WAAK,SAAS,IAAI,qBAAqB,KAAK,OAAO;AAAA,IACrD,OAAO;AACL,YAAM,EAAE,gBAAgB,IACtB,MAAM,OAAO,iCAAoC;AACnD,WAAK,SAAS,IAAI,gBAAgB,KAAK,OAAO;AAAA,IAChD;AAEA,UAAM,KAAK,OAAO,KAAK;AACvB,SAAK,OAAO,QAAQ;AAAA,EACtB;AAAA,EAEA,SAAS;AACP,SAAK,QAAQ,OAAO;AAAA,EACtB;AAAA,EAEA,UAAU;AACR,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,cAAc,MAAkB;AAC9B,SAAK,QAAQ,gBAAgB,IAAI;AAAA,EACnC;AAAA,EAEA,mBAAmB;AACjB,IAAC,KAAK,QAAgB,mBAAmB;AAAA,EAC3C;AAAA,EAEA,kBAAkB,OAAe;AAC/B,IAAC,KAAK,QAAgB,oBAAoB,KAAK;AAAA,EACjD;AAAA,EAEA,MAAM,gBAAgB,KAAa,UAA0B;AAC3D,WAAO,KAAK,QAAQ,kBAAkB,KAAK,QAAQ;AAAA,EACrD;AAAA,EAEA,oBAAoB,KAAa;AAC/B,WAAO,KAAK,QAAQ,sBAAsB,GAAG;AAAA,EAC/C;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/viewer/pgsg-viewer.ts"],"sourcesContent":["import * as THREE from \"three\";\n\nimport type {\n ViewerType,\n PGSGViewerEngine,\n CameraMode,\n} from \"../viewers/types\";\nimport type { PGSGViewerOptions } from \"../core/types\";\n\nexport class PGSGViewer {\n private engine!: PGSGViewerEngine;\n\n constructor(\n private options: PGSGViewerOptions & { viewerType?: ViewerType },\n ) {}\n\n async load() {\n const type = this.options.viewerType ?? \"three\";\n\n if (type === \"playcanvas\") {\n const { PGSGPlayCanvasViewer } =\n await import(\"../viewers/playcanvas/pgsg-playcanvas-viewer\");\n this.engine = new PGSGPlayCanvasViewer(this.options);\n } else {\n const { PGSGThreeViewer } =\n await import(\"../viewers/three/pgsg-three-viewer\");\n this.engine = new PGSGThreeViewer(this.options);\n }\n\n await this.engine.load();\n this.engine.start?.();\n }\n\n resize() {\n this.engine?.resize();\n }\n\n destroy() {\n this.engine?.destroy();\n }\n\n setCameraMode(mode: CameraMode) {\n this.engine?.setCameraMode?.(mode);\n }\n\n finalizePolyline() {\n (this.engine as any)?.finalizePolyline?.();\n }\n\n deleteMeasurement(index: number) {\n (this.engine as any)?.deleteMeasurement?.(index);\n }\n\n async addModelFromUrl(url: string, position?: THREE.Vector3) {\n return this.engine?.addModelFromUrl?.(url, position);\n }\n\n enablePlacementMode(url: string) {\n return this.engine?.enablePlacementMode?.(url);\n }\n\n enableInput(enabled: boolean) {\n return this.engine?.enableInput?.(enabled);\n }\n}\n"],"mappings":";AASO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YACU,SACR;AADQ;AAAA,EACP;AAAA,EAJK;AAAA,EAMR,MAAM,OAAO;AACX,UAAM,OAAO,KAAK,QAAQ,cAAc;AAExC,QAAI,SAAS,cAAc;AACzB,YAAM,EAAE,qBAAqB,IAC3B,MAAM,OAAO,sCAA8C;AAC7D,WAAK,SAAS,IAAI,qBAAqB,KAAK,OAAO;AAAA,IACrD,OAAO;AACL,YAAM,EAAE,gBAAgB,IACtB,MAAM,OAAO,iCAAoC;AACnD,WAAK,SAAS,IAAI,gBAAgB,KAAK,OAAO;AAAA,IAChD;AAEA,UAAM,KAAK,OAAO,KAAK;AACvB,SAAK,OAAO,QAAQ;AAAA,EACtB;AAAA,EAEA,SAAS;AACP,SAAK,QAAQ,OAAO;AAAA,EACtB;AAAA,EAEA,UAAU;AACR,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,cAAc,MAAkB;AAC9B,SAAK,QAAQ,gBAAgB,IAAI;AAAA,EACnC;AAAA,EAEA,mBAAmB;AACjB,IAAC,KAAK,QAAgB,mBAAmB;AAAA,EAC3C;AAAA,EAEA,kBAAkB,OAAe;AAC/B,IAAC,KAAK,QAAgB,oBAAoB,KAAK;AAAA,EACjD;AAAA,EAEA,MAAM,gBAAgB,KAAa,UAA0B;AAC3D,WAAO,KAAK,QAAQ,kBAAkB,KAAK,QAAQ;AAAA,EACrD;AAAA,EAEA,oBAAoB,KAAa;AAC/B,WAAO,KAAK,QAAQ,sBAAsB,GAAG;AAAA,EAC/C;AAAA,EAEA,YAAY,SAAkB;AAC5B,WAAO,KAAK,QAAQ,cAAc,OAAO;AAAA,EAC3C;AACF;","names":[]}
@@ -51,6 +51,7 @@ declare class PGSGViewer {
51
51
  deleteMeasurement(index: number): void;
52
52
  addModelFromUrl(url: string, position?: THREE.Vector3): Promise<THREE.Object3D<THREE.Object3DEventMap> | null | undefined>;
53
53
  enablePlacementMode(url: string): void | undefined;
54
+ enableInput(enabled: boolean): void | undefined;
54
55
  }
55
56
 
56
57
  export { type CameraOptions, type ControlOptions, type PGSGSource, PGSGViewer, type PGSGViewerOptions, type PLYSource, type SourceType, type ViewerSource };
@@ -1,7 +1,7 @@
1
1
  import "../chunk-PYYLHUV6.js";
2
2
  import {
3
3
  PGSGViewer
4
- } from "../chunk-Z7DBLRJH.js";
4
+ } from "../chunk-LFICZTG7.js";
5
5
  export {
6
6
  PGSGViewer
7
7
  };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./chunk-PYYLHUV6.js";
2
2
  import {
3
3
  PGSGViewer
4
- } from "./chunk-Z7DBLRJH.js";
4
+ } from "./chunk-LFICZTG7.js";
5
5
  export {
6
6
  PGSGViewer
7
7
  };
@@ -1005,6 +1005,7 @@ var PGSGThreeViewer = class {
1005
1005
  mouse = new THREE5.Vector2();
1006
1006
  placementRotation = 0;
1007
1007
  transformControls;
1008
+ inputEnabled = true;
1008
1009
  container;
1009
1010
  resizeObserver = null;
1010
1011
  controls;
@@ -1073,6 +1074,7 @@ var PGSGThreeViewer = class {
1073
1074
  }
1074
1075
  window.addEventListener("keydown", this.onKeyToggle);
1075
1076
  window.addEventListener("keydown", (e) => {
1077
+ if (!this.inputEnabled) return;
1076
1078
  if (!this.transformControls) return;
1077
1079
  if (e.key === "r") this.transformControls.setMode("rotate");
1078
1080
  if (e.key === "t") this.transformControls.setMode("translate");
@@ -1214,6 +1216,9 @@ var PGSGThreeViewer = class {
1214
1216
  );
1215
1217
  });
1216
1218
  }
1219
+ enableInput(enabled) {
1220
+ this.inputEnabled = enabled;
1221
+ }
1217
1222
  // ---------------------------
1218
1223
  // Init
1219
1224
  // ---------------------------
@@ -1293,6 +1298,7 @@ var PGSGThreeViewer = class {
1293
1298
  cd.setFriction(0.9);
1294
1299
  cd.setRestitution(0);
1295
1300
  this.world.createCollider(cd, rb);
1301
+ child.visible = false;
1296
1302
  this.colliderMeshes.push(child);
1297
1303
  });
1298
1304
  const rawBox = new THREE5.Box3().setFromObject(colliderRoot);
@@ -1303,6 +1309,7 @@ var PGSGThreeViewer = class {
1303
1309
  // Mode switching
1304
1310
  // ---------------------------
1305
1311
  onKeyToggle = (e) => {
1312
+ if (!this.inputEnabled) return;
1306
1313
  if (e.key !== "v") return;
1307
1314
  if (this.controls instanceof RapierWalkController) this.switchToOrbit();
1308
1315
  else this.switchToWalk();
@@ -1519,8 +1526,16 @@ var PGSGThreeViewer = class {
1519
1526
  true
1520
1527
  );
1521
1528
  if (!intersects.length) return;
1522
- const point = intersects[0].point;
1523
- this.ghostModel.position.copy(point);
1529
+ const hit = intersects[0];
1530
+ this.ghostModel.position.copy(hit.point);
1531
+ if (hit.face) {
1532
+ const normal = this.getWorldNormalSafe(hit);
1533
+ if (normal) {
1534
+ const up = new THREE5.Vector3(0, 1, 0);
1535
+ const quat = new THREE5.Quaternion().setFromUnitVectors(up, normal);
1536
+ this.ghostModel.quaternion.copy(quat);
1537
+ }
1538
+ }
1524
1539
  };
1525
1540
  onPlacementWheel = (event) => {
1526
1541
  if (!this.ghostModel) return;
@@ -1556,4 +1571,4 @@ var PGSGThreeViewer = class {
1556
1571
  export {
1557
1572
  PGSGThreeViewer
1558
1573
  };
1559
- //# sourceMappingURL=pgsg-three-viewer-KBOYGHT3.js.map
1574
+ //# sourceMappingURL=pgsg-three-viewer-5BUDBGSE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/viewers/three/pgsg-three-viewer.ts","../src/viewers/spark/SparkSplatRenderer.ts","../src/viewers/three/controls/RapierWalkCapsuleController.ts","../src/viewers/three/controls/OrbitCameraController.ts","../src/viewers/three/controls/MeasurementController.ts","../src/viewers/three/controls/FloorplanController.ts"],"sourcesContent":["import type { PGSGViewerOptions } from \"../../core/types\";\nimport * as THREE from \"three\";\nimport { GLTFLoader } from \"three/addons/loaders/GLTFLoader.js\";\nimport { TransformControls } from \"three/addons/controls/TransformControls.js\";\nimport * as RAPIER from \"@dimforge/rapier3d-compat\";\nimport nipplejs from \"nipplejs\";\n\nimport { SparkSplatRenderer } from \"../spark/SparkSplatRenderer\";\nimport { RapierWalkController } from \"./controls/RapierWalkCapsuleController\";\nimport { OrbitCameraController } from \"./controls/OrbitCameraController\";\nimport { MeasurementController } from \"./controls/MeasurementController\";\nimport { FloorplanController } from \"./controls/FloorplanController\";\nimport { type CameraMode } from \"../types\";\n\nexport class PGSGThreeViewer {\n private scene = new THREE.Scene();\n private camera!: THREE.PerspectiveCamera;\n private renderer!: THREE.WebGLRenderer;\n private splats!: SparkSplatRenderer;\n\n private gltfLoader = new GLTFLoader();\n private pendingPlacementUrl?: string;\n private ghostModel?: THREE.Object3D;\n private raycaster = new THREE.Raycaster();\n private mouse = new THREE.Vector2();\n private placementRotation = 0;\n private transformControls?: TransformControls;\n private inputEnabled = true;\n\n private container: HTMLElement;\n private resizeObserver: ResizeObserver | null = null;\n\n private controls!:\n | RapierWalkController\n | OrbitCameraController\n | MeasurementController\n | FloorplanController;\n\n private running = false;\n private orbitTarget = new THREE.Vector3();\n private lastTime = performance.now();\n\n private persistentMeasurements: any[] = [];\n private measurementController: MeasurementController | null = null;\n\n private world!: RAPIER.World;\n\n // Roots\n private worldRoot = new THREE.Object3D(); // identity\n private cvRoot = new THREE.Object3D(); // ONLY place where we convert OpenCV->OpenGL\n private splatPivot = new THREE.Object3D();\n private colliderMesh?: THREE.Object3D;\n\n // For raycasting / measurement only\n private colliderMeshes: THREE.Object3D[] = [];\n\n private currentMode: CameraMode = \"orbit\";\n\n private acc = 0;\n private fixedDt = 1 / 60;\n\n private GLOBAL_SCALE = 0.7;\n\n constructor(private options: PGSGViewerOptions) {\n this.container = options.container;\n if (!this.container)\n throw new Error(\"[PGSGThreeViewer] Container is required\");\n }\n\n async load() {\n this.initRenderer();\n this.initCamera();\n this.initScene();\n\n this.bindResize();\n this.resize();\n\n this.transformControls = new TransformControls(\n this.camera,\n this.renderer.domElement,\n );\n\n this.scene.add(this.transformControls as unknown as THREE.Object3D);\n console.log(this.transformControls instanceof THREE.Object3D);\n\n this.transformControls.addEventListener(\"dragging-changed\", (event) => {\n if (!this.controls) return;\n (this.controls as any).enabled = !event.value;\n });\n\n this.transformControls.addEventListener(\"mouseDown\", () => {\n document.exitPointerLock?.();\n });\n\n await RAPIER.init();\n\n this.world = new RAPIER.World({\n x: 0,\n y: -9.81 * this.GLOBAL_SCALE,\n z: 0,\n });\n\n // scene graph\n this.scene.add(this.worldRoot);\n\n // ✅ OpenCV -> OpenGL conversion happens ONCE here\n // This matches your observed bounds flip:\n // raw min/max: [-1.26, 0.75] => OpenGL min/max: [-0.75, 1.26]\n this.cvRoot.scale.set(1, -1, -1);\n this.worldRoot.add(this.cvRoot);\n\n // ===============================\n // Load Splats under cvRoot\n // ===============================\n this.splats = new SparkSplatRenderer();\n const { mesh } = await this.splats.load(this.options.source);\n\n // ✅ DO NOT flip the mesh separately\n this.splatPivot.clear();\n this.splatPivot.add(mesh);\n this.cvRoot.add(this.splatPivot);\n\n // ===============================\n // Load Collider under cvRoot\n // ===============================\n if (this.options.colliderSource) {\n await this.loadCollider(this.options.colliderSource);\n }\n\n // update transforms after everything is added\n this.cvRoot.updateMatrixWorld(true);\n\n // Spawn camera using collider in the SAME space used by visuals + rapier\n this.placeInitialCamera();\n\n if (this.isMobile()) {\n this.initJoystick();\n this.switchToWalk();\n } else {\n this.switchToMeasure();\n }\n\n window.addEventListener(\"keydown\", this.onKeyToggle);\n window.addEventListener(\"keydown\", (e) => {\n if (!this.inputEnabled) return;\n if (!this.transformControls) return;\n\n if (e.key === \"r\") this.transformControls.setMode(\"rotate\");\n if (e.key === \"t\") this.transformControls.setMode(\"translate\");\n if (e.key === \"s\") this.transformControls.setMode(\"scale\");\n });\n this.renderer.domElement.addEventListener(\"click\", this.onSceneClick);\n }\n\n start() {\n if (this.running) return;\n this.running = true;\n\n this.renderer.setAnimationLoop(() => {\n const now = performance.now();\n const delta = (now - this.lastTime) / 1000;\n this.lastTime = now;\n\n this.acc += delta;\n while (this.acc >= this.fixedDt) {\n this.world.timestep = this.fixedDt;\n this.world.step();\n this.acc -= this.fixedDt;\n }\n\n (this.controls as any)?.update?.(delta);\n this.renderer.render(this.scene, this.camera);\n });\n }\n\n pause() {\n this.running = false;\n }\n\n resize() {\n if (!this.renderer || !this.camera) return;\n\n const w = Math.max(1, this.container.clientWidth);\n const h = Math.max(1, this.container.clientHeight);\n\n this.renderer.setSize(w, h);\n this.camera.aspect = w / h;\n this.camera.updateProjectionMatrix();\n }\n\n destroy() {\n this.running = false;\n this.renderer.setAnimationLoop(null);\n this.renderer.domElement.removeEventListener(\"click\", this.onSceneClick);\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.controls?.dispose();\n this.renderer.dispose();\n window.removeEventListener(\"keydown\", this.onKeyToggle);\n }\n\n setCameraMode(mode: CameraMode) {\n this.currentMode = mode;\n\n if (this.controls instanceof MeasurementController) {\n this.persistentMeasurements = (this.controls as any).measurements;\n this.controls.setVisible(false);\n }\n\n switch (mode) {\n case \"measure\":\n document.exitPointerLock?.();\n this.switchToMeasure();\n if (this.controls instanceof MeasurementController)\n this.controls.setVisible(true);\n break;\n case \"orbit\":\n document.exitPointerLock?.();\n this.switchToOrbit();\n break;\n case \"walk\":\n this.switchToWalk();\n break;\n case \"floorplan\":\n this.switchToFloorplan();\n break;\n }\n }\n\n toggleCameraMode() {\n if (this.controls instanceof RapierWalkController) this.switchToOrbit();\n else this.switchToWalk();\n }\n\n getMeasurements() {\n return this.measurementController?.getMeasurements?.() ?? [];\n }\n\n clearMeasurements() {\n if (!this.measurementController) return;\n this.measurementController.clearAll();\n this.persistentMeasurements = [];\n }\n\n finalizePolyline() {\n if (this.controls instanceof MeasurementController)\n this.controls.finalizePolyline();\n }\n\n deleteMeasurement(index: number) {\n this.measurementController?.deleteMeasurement?.(index);\n }\n\n async enablePlacementMode(url: string) {\n this.pendingPlacementUrl = url;\n this.placementRotation = 0;\n\n console.log(\"[PGSG Viewer] Placement mode enabled\");\n\n const gltf = await this.gltfLoader.loadAsync(url);\n\n this.ghostModel = gltf.scene;\n\n // normalize scale\n const box = new THREE.Box3().setFromObject(this.ghostModel);\n const size = new THREE.Vector3();\n box.getSize(size);\n\n const maxDim = Math.max(size.x, size.y, size.z);\n const scale = 1.5 / maxDim;\n\n this.ghostModel.scale.multiplyScalar(scale);\n\n // transparent ghost material\n this.ghostModel.traverse((child: any) => {\n if (!child.isMesh) return;\n\n child.material = child.material.clone();\n child.material.transparent = true;\n child.material.opacity = 0.4;\n });\n\n this.scene.add(this.ghostModel);\n\n this.renderer.domElement.addEventListener(\n \"mousemove\",\n this.onPlacementMove,\n );\n this.renderer.domElement.addEventListener(\"wheel\", this.onPlacementWheel);\n this.renderer.domElement.addEventListener(\"click\", this.onSceneClick);\n window.addEventListener(\"keydown\", this.onPlacementCancel);\n }\n\n async addModelFromUrl(\n url: string,\n position?: THREE.Vector3,\n ): Promise<THREE.Object3D | null> {\n return new Promise((resolve, reject) => {\n this.gltfLoader.load(\n url,\n (gltf) => {\n const model = gltf.scene;\n\n const box = new THREE.Box3().setFromObject(model);\n const size = new THREE.Vector3();\n box.getSize(size);\n\n const maxDim = Math.max(size.x, size.y, size.z);\n const scale = 1.5 / maxDim;\n\n model.scale.multiplyScalar(scale);\n\n if (position) {\n model.position.copy(position);\n }\n\n const floorBox = new THREE.Box3().setFromObject(model);\n model.position.y -= floorBox.min.y;\n\n this.cvRoot.add(model);\n\n resolve(model);\n },\n undefined,\n reject,\n );\n });\n }\n\n enableInput(enabled: boolean) {\n this.inputEnabled = enabled;\n }\n\n // ---------------------------\n // Init\n // ---------------------------\n\n private initRenderer() {\n if (this.renderer) return;\n\n this.renderer = new THREE.WebGLRenderer({\n antialias: this.options.renderer?.antialias ?? false,\n alpha: false,\n powerPreference: \"high-performance\",\n });\n\n this.renderer.setClearColor(0x111111, 1);\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n\n this.renderer.setSize(\n this.container.clientWidth,\n this.container.clientHeight,\n );\n this.renderer.setPixelRatio(\n this.options.renderer?.pixelRatio ?? window.devicePixelRatio,\n );\n\n this.renderer.localClippingEnabled = true;\n this.container.appendChild(this.renderer.domElement);\n }\n\n private initScene() {\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0x111111);\n\n const light = new THREE.HemisphereLight(0xffffff, 0x222222, 1.0);\n this.scene.add(light);\n }\n\n private initCamera() {\n this.camera = new THREE.PerspectiveCamera(\n this.options.camera?.fov ?? 60,\n this.container.clientWidth / this.container.clientHeight,\n this.options.camera?.near ?? 0.01,\n this.options.camera?.far ?? 10000,\n );\n this.camera.position.set(2, 2, 5);\n this.camera.lookAt(0, 0, 0);\n }\n\n private bindResize() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = new ResizeObserver(() => this.resize());\n this.resizeObserver.observe(this.container);\n }\n\n private isMobile(): boolean {\n return (\n \"ontouchstart\" in window ||\n navigator.maxTouchPoints > 0 ||\n window.innerWidth < 768\n );\n }\n\n // ---------------------------\n // Collider (visual + rapier)\n // ---------------------------\n\n private async loadCollider(url: string) {\n console.log(\"[PGSG Viewer] Loading collider:\", url);\n\n const loader = new GLTFLoader();\n const gltf = await loader.loadAsync(url);\n\n // Put collider under cvRoot so it matches splat space\n const colliderRoot = new THREE.Object3D();\n colliderRoot.name = \"colliderRoot\";\n colliderRoot.add(gltf.scene);\n\n this.colliderMesh = colliderRoot;\n this.cvRoot.add(colliderRoot);\n\n // update world matrices so geometry baking includes cvRoot flip\n this.cvRoot.updateMatrixWorld(true);\n colliderRoot.updateWorldMatrix(true, true);\n\n // For measurement/raycast (THREE)\n this.colliderMeshes.length = 0;\n\n // Build Rapier trimesh from FINAL transformed geometry\n colliderRoot.traverse((child: any) => {\n if (!child?.isMesh) return;\n\n // bake geometry into world space (including cvRoot scale flip)\n const geom = child.geometry.clone();\n child.updateWorldMatrix(true, false);\n geom.applyMatrix4(child.matrixWorld);\n\n const vertices = new Float32Array(geom.attributes.position.array as any);\n\n let indices: Uint32Array;\n if (geom.index) indices = new Uint32Array(geom.index.array as any);\n else {\n const count = geom.attributes.position.count;\n indices = new Uint32Array(count);\n for (let i = 0; i < count; i++) indices[i] = i;\n }\n\n const rb = this.world.createRigidBody(RAPIER.RigidBodyDesc.fixed());\n const cd = RAPIER.ColliderDesc.trimesh(vertices, indices);\n cd.setFriction(0.9);\n cd.setRestitution(0.0);\n this.world.createCollider(cd, rb);\n\n // optional debug material\n // child.material = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide });\n child.visible = false;\n this.colliderMeshes.push(child);\n });\n\n // sanity logs\n const rawBox = new THREE.Box3().setFromObject(colliderRoot);\n console.log(\"OpenGL bounds Y:\", rawBox.min.y, rawBox.max.y);\n\n console.log(\"[PGSG Viewer] Colliders loaded:\", this.colliderMeshes.length);\n }\n\n // ---------------------------\n // Mode switching\n // ---------------------------\n\n private onKeyToggle = (e: KeyboardEvent) => {\n if (!this.inputEnabled) return;\n if (e.key !== \"v\") return;\n if (this.controls instanceof RapierWalkController) this.switchToOrbit();\n else this.switchToWalk();\n };\n\n private switchToMeasure() {\n this.controls?.dispose?.();\n this.measurementController = new MeasurementController({\n dom: this.renderer.domElement,\n camera: this.camera,\n scene: this.scene,\n colliders: this.colliderMeshes,\n });\n\n this.measurementController.restoreMeasurements?.(\n this.persistentMeasurements,\n );\n this.controls = this.measurementController;\n }\n\n private switchToWalk() {\n this.controls?.dispose?.();\n\n const walk = new RapierWalkController({\n camera: this.camera,\n dom: this.renderer.domElement,\n world: this.world,\n eyeHeight: 1.6,\n radius: 0.2,\n halfHeight: 0.6,\n moveSpeed: 3.0 * this.GLOBAL_SCALE,\n jumpSpeed: 6.0 * this.GLOBAL_SCALE,\n usePointerLock: !this.isMobile(),\n }) as any;\n\n this.controls = walk;\n\n // sync yaw/pitch from current camera\n const euler = new THREE.Euler().setFromQuaternion(\n this.camera.quaternion,\n \"YXZ\",\n );\n walk.setRotation(euler.y, euler.x);\n\n // place capsule under camera, then snap DOWN to nearest floor\n walk.setPositionFromCamera();\n walk.resetVelocity();\n walk.snapToGround?.();\n }\n\n private switchToOrbit() {\n this.controls?.dispose?.();\n\n this.camera.up.set(0, 1, 0);\n this.camera.updateMatrixWorld(true);\n\n // orbit around converted (OpenGL) content root\n const box = new THREE.Box3().setFromObject(this.cvRoot);\n const center = box.getCenter(new THREE.Vector3());\n this.orbitTarget.copy(center);\n\n const distance = Math.max(this.camera.position.distanceTo(center), 1);\n\n this.controls = new OrbitCameraController(\n this.camera,\n this.renderer.domElement,\n {\n target: this.orbitTarget.clone(),\n distance,\n usePointerLock: !this.isMobile(),\n },\n );\n }\n\n private switchToFloorplan() {\n this.controls?.dispose?.();\n\n const box = new THREE.Box3().setFromObject(this.cvRoot);\n const center = box.getCenter(new THREE.Vector3());\n const size = box.getSize(new THREE.Vector3());\n const height = Math.max(size.x, size.z) * 1.2;\n\n this.orbitTarget.copy(center);\n\n this.controls = new FloorplanController({\n dom: this.renderer.domElement,\n camera: this.camera,\n target: center,\n height,\n panSpeed: 0.0015,\n zoomSpeed: 1.15,\n });\n }\n\n // ---------------------------\n // Spawn\n // ---------------------------\n\n private placeInitialCamera() {\n const targetObj = this.colliderMesh ?? this.cvRoot;\n const box = new THREE.Box3().setFromObject(targetObj);\n const center = box.getCenter(new THREE.Vector3());\n const size = box.getSize(new THREE.Vector3());\n\n // start ray well above the whole scene\n const startY = box.max.y + 10;\n const maxDist = box.max.y - box.min.y + 50;\n\n // sample a small grid around center (helps when center is outside shell)\n const samples: Array<{ x: number; z: number }> = [];\n const sx = Math.max(0.2, size.x * 0.15);\n const sz = Math.max(0.2, size.z * 0.15);\n\n for (const dx of [-sx, 0, sx]) {\n for (const dz of [-sz, 0, sz]) {\n samples.push({ x: center.x + dx, z: center.z + dz });\n }\n }\n\n // pick best \"floor\" among all samples\n // Strategy:\n // - collect multiple hits along the down-ray\n // - ignore the first hit if it’s the outside ceiling\n // - select the deepest hit that is \"walkable-ish\"\n // (we don’t trust normal sign fully, so use abs(normalY))\n let bestFloorY: number | null = null;\n let bestXZ: { x: number; z: number } | null = null;\n\n for (const s of samples) {\n const hits = this.raycastDownMulti(s.x, s.z, startY, maxDist, 10);\n if (hits.length === 0) continue;\n\n // choose a candidate floor from the *later* hits (skip the first)\n // and require it to be roughly horizontal\n let candidate: { y: number; normalY: number } | null = null;\n\n for (let i = 1; i < hits.length; i++) {\n const h = hits[i];\n if (Math.abs(h.normalY) < 0.55) continue; // ~55deg slope limit\n candidate = h; // keep going, we want the deepest valid one\n }\n\n if (!candidate) continue;\n\n // choose the HIGHEST candidate floor among samples\n // (so we land on topmost interior floor, not a basement)\n if (bestFloorY == null || candidate.y > bestFloorY) {\n bestFloorY = candidate.y;\n bestXZ = s;\n }\n }\n\n const eye = 1.6;\n\n if (bestFloorY != null && bestXZ) {\n this.camera.position.set(bestXZ.x, bestFloorY + eye, bestXZ.z + 0.5);\n this.orbitTarget.set(bestXZ.x, bestFloorY, bestXZ.z);\n } else {\n // fallback\n this.camera.position.set(center.x, box.max.y + eye, center.z + 2);\n this.orbitTarget.copy(center);\n }\n\n this.camera.up.set(0, 1, 0);\n this.camera.lookAt(this.orbitTarget);\n this.camera.updateMatrixWorld(true);\n }\n\n private getWorldNormalSafe(hit: THREE.Intersection) {\n if (!hit.face) return null;\n const m3 = new THREE.Matrix3().getNormalMatrix(hit.object.matrixWorld);\n return hit.face.normal.clone().applyMatrix3(m3).normalize();\n }\n\n // ---------------------------\n // Mobile joystick\n // ---------------------------\n\n private initJoystick() {\n const zone = document.createElement(\"div\");\n zone.style.position = \"absolute\";\n zone.style.left = \"20px\";\n zone.style.bottom = \"20px\";\n zone.style.width = \"150px\";\n zone.style.height = \"150px\";\n zone.style.zIndex = \"1000\";\n this.container.appendChild(zone);\n\n const joystick = nipplejs.create({\n zone,\n mode: \"static\",\n position: { left: \"50px\", bottom: \"50px\" },\n color: \"white\",\n });\n\n joystick.on(\"move\", (_, data) => {\n const angle = data.angle.radian;\n const force = data.force;\n\n if (\n this.controls instanceof RapierWalkController ||\n this.controls instanceof OrbitCameraController\n ) {\n (this.controls as any).setMovementVector(\n Math.sin(angle) * force,\n Math.cos(angle) * force,\n );\n }\n });\n\n joystick.on(\"end\", () => {\n if (\n this.controls instanceof RapierWalkController ||\n this.controls instanceof OrbitCameraController\n ) {\n (this.controls as any).setMovementVector(0, 0);\n }\n });\n }\n\n private raycastDownMulti(\n x: number,\n z: number,\n startY: number,\n maxDist: number,\n maxHits = 8,\n ) {\n const hits: Array<{ y: number; normalY: number }> = [];\n\n let originY = startY;\n let remaining = maxDist;\n\n for (let i = 0; i < maxHits; i++) {\n const ray = new RAPIER.Ray({ x, y: originY, z }, { x: 0, y: -1, z: 0 });\n\n const hit = this.world.castRayAndGetNormal(ray, remaining, true);\n if (!hit) break;\n\n const y = originY - hit.timeOfImpact;\n const normalY = hit.normal?.y ?? 0;\n\n hits.push({ y, normalY });\n\n // Step just past the surface so the next cast finds the next intersection\n const eps = 0.02;\n originY = y - eps;\n remaining = remaining - hit.timeOfImpact - eps;\n if (remaining <= 0) break;\n }\n\n return hits;\n }\n\n private onSceneClick = async (event: MouseEvent) => {\n if (!this.pendingPlacementUrl || !this.ghostModel) return;\n\n const position = this.ghostModel.position.clone();\n\n const placed = await this.addModelFromUrl(\n this.pendingPlacementUrl,\n position,\n );\n\n if (placed && this.transformControls) {\n placed.rotation.y = this.placementRotation;\n this.transformControls.attach(placed);\n }\n\n this.scene.remove(this.ghostModel);\n\n this.ghostModel = undefined;\n this.pendingPlacementUrl = undefined;\n\n this.renderer.domElement.removeEventListener(\n \"mousemove\",\n this.onPlacementMove,\n );\n\n this.renderer.domElement.removeEventListener(\n \"wheel\",\n this.onPlacementWheel,\n );\n\n this.renderer.domElement.removeEventListener(\"click\", this.onSceneClick);\n\n window.removeEventListener(\"keydown\", this.onPlacementCancel);\n };\n\n private onPlacementMove = (event: MouseEvent) => {\n if (!this.pendingPlacementUrl || !this.ghostModel) return;\n\n const rect = this.renderer.domElement.getBoundingClientRect();\n\n this.mouse.set(\n ((event.clientX - rect.left) / rect.width) * 2 - 1,\n -((event.clientY - rect.top) / rect.height) * 2 + 1,\n );\n\n this.raycaster.setFromCamera(this.mouse, this.camera);\n\n const intersects = this.raycaster.intersectObjects(\n this.colliderMeshes,\n true,\n );\n\n if (!intersects.length) return;\n\n const hit = intersects[0];\n\n this.ghostModel.position.copy(hit.point);\n\n // align to surface normal\n if (hit.face) {\n const normal = this.getWorldNormalSafe(hit);\n if (normal) {\n const up = new THREE.Vector3(0, 1, 0);\n const quat = new THREE.Quaternion().setFromUnitVectors(up, normal);\n this.ghostModel.quaternion.copy(quat);\n }\n }\n };\n\n private onPlacementWheel = (event: WheelEvent) => {\n if (!this.ghostModel) return;\n\n event.preventDefault();\n\n const step = Math.PI / 12; // 15 degrees\n\n if (event.deltaY > 0) {\n this.placementRotation += step;\n } else {\n this.placementRotation -= step;\n }\n\n this.ghostModel.rotation.y = this.placementRotation;\n };\n\n private onPlacementCancel = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n\n if (!this.ghostModel) return;\n\n console.log(\"[PGSG Viewer] Placement cancelled\");\n\n this.scene.remove(this.ghostModel);\n this.ghostModel = undefined;\n this.pendingPlacementUrl = undefined;\n\n this.renderer.domElement.style.cursor = \"default\";\n\n this.renderer.domElement.removeEventListener(\n \"mousemove\",\n this.onPlacementMove,\n );\n this.renderer.domElement.removeEventListener(\"click\", this.onSceneClick);\n this.renderer.domElement.removeEventListener(\n \"wheel\",\n this.onPlacementWheel,\n );\n\n window.removeEventListener(\"keydown\", this.onPlacementCancel);\n };\n}\n","import { SplatMesh } from \"@sparkjsdev/spark\";\nimport * as THREE from \"three\";\nimport type { ViewerSource } from \"../../core/types\";\nimport { computePLYBounds, parsePLYHeader } from \"../three/utils\";\n\nexport class SparkSplatRenderer {\n private mesh!: SplatMesh;\n\n async load(source: ViewerSource) {\n if (source.type !== \"ply\" && source.type !== \"pgsg\") {\n throw new Error(\"SparkJS supports splat-based sources only\");\n }\n\n // const res = await fetch(source.url);\n // const buffer = await res.arrayBuffer();\n\n // const { vertexCount, headerSize } = parsePLYHeader(buffer);\n // const bounds = computePLYBounds(buffer, headerSize, vertexCount);\n\n this.mesh = new SplatMesh({\n url: source.url,\n });\n\n await this.mesh.initialized;\n this.mesh.updateMatrixWorld(true);\n\n const box = this.mesh.getBoundingBox(true);\n\n const min = box.min.clone();\n const max = box.max.clone();\n\n // Match SparkJS doc defaults\n this.mesh.position.set(0, 0, 0);\n this.mesh.quaternion.identity();\n\n return {\n mesh: this.mesh,\n bounds: {\n min,\n max,\n },\n };\n }\n\n addToScene(scene: THREE.Scene) {\n scene.add(this.mesh);\n }\n\n update(dt: number) {\n // optional: rotation / animation\n }\n\n destroy(scene: THREE.Scene) {\n scene.remove(this.mesh);\n this.mesh.dispose?.();\n }\n}\n","import * as THREE from \"three\";\nimport * as RAPIER from \"@dimforge/rapier3d-compat\";\n\nexport type RapierWalkOptions = {\n dom: HTMLElement;\n camera: THREE.PerspectiveCamera;\n world: RAPIER.World;\n\n radius?: number;\n halfHeight?: number;\n eyeHeight?: number;\n\n moveSpeed?: number;\n jumpSpeed?: number;\n lookSensitivity?: number;\n\n usePointerLock?: boolean;\n\n // small tuning values\n groundProbeExtra?: number; // extra ray length\n groundSnapMax?: number; // max snap distance\n};\n\nexport class RapierWalkController {\n private dom: HTMLElement;\n private camera: THREE.PerspectiveCamera;\n private world: RAPIER.World;\n\n private body: RAPIER.RigidBody;\n private collider: RAPIER.Collider;\n\n private radius: number;\n private halfHeight: number;\n private eyeHeight: number;\n\n private moveSpeed: number;\n private jumpSpeed: number;\n private lookSensitivity: number;\n\n private usePointerLock: boolean;\n\n private yaw = 0;\n private pitch = 0;\n\n private keys = new Set<string>();\n private moveVec = new THREE.Vector2(0, 0); // joystick: x=forward, y=right\n\n private groundProbeExtra: number;\n private groundSnapMax: number;\n\n // ---- bound handlers (so dispose works) ----\n private onClickBound = () => this.onClick();\n private onMouseMoveBound = (e: MouseEvent) => this.onMouseMove(e);\n private onKeyDownBound = (e: KeyboardEvent) => this.onKeyDown(e);\n private onKeyUpBound = (e: KeyboardEvent) => this.onKeyUp(e);\n private onBlurBound = () => this.onBlur();\n\n constructor(opts: RapierWalkOptions) {\n this.dom = opts.dom;\n this.camera = opts.camera;\n this.world = opts.world;\n\n this.radius = opts.radius ?? 0.2;\n this.halfHeight = opts.halfHeight ?? 0.6;\n this.eyeHeight = opts.eyeHeight ?? 1.6;\n\n this.moveSpeed = opts.moveSpeed ?? 3.0;\n this.jumpSpeed = opts.jumpSpeed ?? 6.0;\n this.lookSensitivity = opts.lookSensitivity ?? 0.0022;\n\n this.usePointerLock = opts.usePointerLock ?? true;\n\n this.groundProbeExtra = opts.groundProbeExtra ?? 0.25;\n this.groundSnapMax = opts.groundSnapMax ?? 0.65;\n\n // init yaw/pitch from camera\n const euler = new THREE.Euler().setFromQuaternion(\n this.camera.quaternion,\n \"YXZ\",\n );\n this.pitch = euler.x;\n this.yaw = euler.y;\n\n // Create capsule rigidbody at camera position (best guess)\n const start = this.camera.position.clone();\n const centerY = start.y - this.eyeHeight + (this.halfHeight + this.radius);\n\n const bodyDesc = RAPIER.RigidBodyDesc.dynamic()\n .setTranslation(start.x, centerY, start.z)\n .lockRotations()\n .setLinearDamping(6.0) // higher damping to kill drift\n .setCcdEnabled(true);\n\n this.body = this.world.createRigidBody(bodyDesc);\n\n const colDesc = RAPIER.ColliderDesc.capsule(this.halfHeight, this.radius)\n .setFriction(0.8)\n .setRestitution(0.0);\n\n this.collider = this.world.createCollider(colDesc, this.body);\n\n // optional: helps prevent “sucked into edges”\n this.collider.setActiveEvents?.(RAPIER.ActiveEvents.COLLISION_EVENTS);\n\n this.bindInput();\n }\n\n dispose() {\n this.unbindInput();\n // optional: if you want to remove from world completely\n // this.world.removeRigidBody(this.body);\n if (this.body) {\n this.world.removeRigidBody(this.body);\n }\n }\n\n // ---------------------------\n // External movement (joystick)\n // x = forward/back (-1..1), y = right/left (-1..1)\n // ---------------------------\n setMovementVector(x: number, y: number) {\n this.moveVec.set(x, y);\n }\n\n setRotation(yaw: number, pitch: number) {\n this.yaw = yaw;\n this.pitch = THREE.MathUtils.clamp(\n pitch,\n -Math.PI / 2 + 0.01,\n Math.PI / 2 - 0.01,\n );\n this.camera.quaternion.setFromEuler(\n new THREE.Euler(this.pitch, this.yaw, 0, \"YXZ\"),\n );\n }\n\n resetVelocity() {\n this.body.setLinvel({ x: 0, y: 0, z: 0 }, true);\n this.body.setAngvel({ x: 0, y: 0, z: 0 }, true);\n }\n\n setPositionFromCamera() {\n const p = this.camera.position;\n const centerY = p.y - this.eyeHeight + (this.halfHeight + this.radius);\n this.body.setTranslation({ x: p.x, y: centerY, z: p.z }, true);\n this.resetVelocity();\n }\n\n // Snap the capsule so its feet rest on the nearest floor below\n snapToGround() {\n const p = this.body.translation();\n\n const origin = { x: p.x, y: p.y + 2.0, z: p.z };\n const dir = { x: 0, y: -1, z: 0 };\n const ray = new RAPIER.Ray(origin, dir);\n\n const maxDist = 50;\n\n const hit = this.castRayWithNormal(ray, maxDist);\n if (!hit) return;\n\n // place feet just above hit point\n const toi = hit.timeOfImpact;\n const hitY = origin.y - toi;\n\n const newCenterY = hitY + (this.halfHeight + this.radius) + 0.02;\n this.body.setTranslation({ x: p.x, y: newCenterY, z: p.z }, true);\n this.resetVelocity();\n }\n\n // ---------------------------\n // Input binding\n // ---------------------------\n private bindInput() {\n if (this.usePointerLock) {\n this.dom.addEventListener(\"click\", this.onClickBound);\n window.addEventListener(\"mousemove\", this.onMouseMoveBound);\n }\n window.addEventListener(\"keydown\", this.onKeyDownBound);\n window.addEventListener(\"keyup\", this.onKeyUpBound);\n window.addEventListener(\"blur\", this.onBlurBound);\n }\n\n private unbindInput() {\n this.dom.removeEventListener(\"click\", this.onClickBound);\n window.removeEventListener(\"mousemove\", this.onMouseMoveBound);\n window.removeEventListener(\"keydown\", this.onKeyDownBound);\n window.removeEventListener(\"keyup\", this.onKeyUpBound);\n window.removeEventListener(\"blur\", this.onBlurBound);\n }\n\n private onClick() {\n if (!this.usePointerLock) return;\n if (document.pointerLockElement !== this.dom) this.dom.requestPointerLock();\n }\n\n private onBlur() {\n this.keys.clear();\n this.moveVec.set(0, 0);\n }\n\n private onMouseMove(e: MouseEvent) {\n if (!this.usePointerLock) return;\n if (document.pointerLockElement !== this.dom) return;\n\n this.yaw -= e.movementX * this.lookSensitivity;\n this.pitch -= e.movementY * this.lookSensitivity;\n this.pitch = THREE.MathUtils.clamp(\n this.pitch,\n -Math.PI / 2 + 0.01,\n Math.PI / 2 - 0.01,\n );\n\n this.camera.quaternion.setFromEuler(\n new THREE.Euler(this.pitch, this.yaw, 0, \"YXZ\"),\n );\n }\n\n private onKeyDown(e: KeyboardEvent) {\n this.keys.add(e.key.toLowerCase());\n }\n\n private onKeyUp(e: KeyboardEvent) {\n this.keys.delete(e.key.toLowerCase());\n }\n\n // ---------------------------\n // Update\n // ---------------------------\n update(_dt: number) {\n // yaw-only basis from yaw (not full camera direction) -> avoids pitch causing weird forces\n const forward = new THREE.Vector3(0, 0, -1).applyAxisAngle(\n new THREE.Vector3(0, 1, 0),\n this.yaw,\n );\n const right = new THREE.Vector3(1, 0, 0).applyAxisAngle(\n new THREE.Vector3(0, 1, 0),\n this.yaw,\n );\n\n const wish = new THREE.Vector3();\n\n // keyboard\n if (this.keys.has(\"w\")) wish.add(forward);\n if (this.keys.has(\"s\")) wish.sub(forward);\n if (this.keys.has(\"d\")) wish.add(right);\n if (this.keys.has(\"a\")) wish.sub(right);\n\n // joystick\n if (this.moveVec.lengthSq() > 1e-6) {\n wish.addScaledVector(forward, this.moveVec.x);\n wish.addScaledVector(right, this.moveVec.y);\n }\n\n if (wish.lengthSq() > 1e-8) wish.normalize();\n\n const vel = this.body.linvel();\n\n // 🔥 kill drift when no input\n const targetVX = wish.x * this.moveSpeed;\n const targetVZ = wish.z * this.moveSpeed;\n\n this.body.setLinvel(\n {\n x: targetVX,\n y: vel.y,\n z: targetVZ,\n },\n true, // wake up\n );\n\n // jump\n if (this.keys.has(\" \")) {\n if (this.isGrounded()) {\n this.body.setLinvel(\n { x: targetVX, y: this.jumpSpeed, z: targetVZ },\n true,\n );\n }\n }\n\n // sync camera to body\n const p = this.body.translation();\n const feetY = p.y - (this.halfHeight + this.radius);\n this.camera.position.set(p.x, feetY + this.eyeHeight, p.z);\n }\n\n // ---------------------------\n // Grounding\n // ---------------------------\n private isGrounded(): boolean {\n const p = this.body.translation();\n\n const origin = { x: p.x, y: p.y, z: p.z };\n const dir = { x: 0, y: -1, z: 0 };\n const ray = new RAPIER.Ray(origin, dir);\n\n const maxDist = this.halfHeight + this.radius + this.groundProbeExtra;\n const hit = this.castRayWithNormal(ray, maxDist);\n if (!hit) return false;\n\n // accept “up-ish” surfaces\n const ny = hit.normal?.y ?? 0;\n return ny > 0.35;\n }\n\n // Ray cast helper with “exclude self” when possible\n private castRayWithNormal(ray: RAPIER.Ray, maxDist: number) {\n // If QueryFilter exists in your build, use it to exclude the player collider.\n const anyRapier: any = RAPIER as any;\n\n try {\n if (anyRapier.QueryFilter) {\n const filter = new anyRapier.QueryFilter();\n // exclude collider preferred\n if (filter.excludeCollider) filter.excludeCollider(this.collider);\n else if (filter.excludeRigidBody) filter.excludeRigidBody(this.body);\n\n return this.world.castRayAndGetNormal(ray, maxDist, true, filter);\n }\n } catch {\n // ignore, fall back below\n }\n\n // Fallback (no filter): may occasionally hit self if Rapier allows, but often still ok.\n return this.world.castRayAndGetNormal(ray, maxDist, true);\n }\n}\n","import * as THREE from \"three\";\n\nexport interface OrbitOptions {\n target: THREE.Vector3;\n distance: number;\n minDistance?: number;\n maxDistance?: number;\n rotateSpeed?: number;\n zoomSpeed?: number;\n usePointerLock?: boolean;\n}\n\nexport class OrbitCameraController {\n private camera: THREE.PerspectiveCamera;\n private dom: HTMLElement;\n\n private target: THREE.Vector3;\n private distance: number;\n private minDistance: number;\n private maxDistance: number;\n\n private yaw = 0;\n private pitch = 0;\n\n private rotateSpeed: number;\n private zoomSpeed: number;\n\n private dragging = false;\n private lastX = 0;\n private lastY = 0;\n\n private panning = false;\n private panSpeed = 0.002;\n\n private usePointerLock?: boolean = false;\n private lastTouchX = 0;\n private lastTouchY = 0;\n private externalMove = new THREE.Vector2(0, 0);\n private moveSpeed: number = 5;\n\n constructor(\n camera: THREE.PerspectiveCamera,\n dom: HTMLElement,\n opts: OrbitOptions,\n ) {\n this.camera = camera;\n this.dom = dom;\n\n this.target = opts.target.clone();\n this.distance = opts.distance;\n this.minDistance = opts.minDistance ?? 0.2;\n this.maxDistance = opts.maxDistance ?? 500;\n\n this.rotateSpeed = opts.rotateSpeed ?? 0.005;\n this.zoomSpeed = opts.zoomSpeed ?? 1.1;\n this.usePointerLock = opts.usePointerLock ?? false;\n\n this.syncFromCamera();\n if (this.usePointerLock === false) {\n this.bindTouch();\n } else {\n this.bind();\n this.updateCamera();\n }\n }\n\n update(dt: number) {\n if (this.externalMove.lengthSq() === 0) return;\n\n const forward = new THREE.Vector3();\n this.camera.getWorldDirection(forward);\n forward.y = 0;\n forward.normalize();\n\n const right = new THREE.Vector3()\n .crossVectors(forward, new THREE.Vector3(0, 1, 0))\n .normalize();\n\n const move = new THREE.Vector3()\n .addScaledVector(forward, this.externalMove.x)\n .addScaledVector(right, this.externalMove.y);\n\n if (move.lengthSq() > 0) move.normalize();\n\n move.multiplyScalar(this.moveSpeed * dt);\n\n this.target.add(move);\n\n this.updateCamera();\n }\n\n dispose() {\n if (this.usePointerLock === false) {\n this.unbindTouch();\n } else {\n this.unbind();\n if (this.usePointerLock) document.exitPointerLock();\n }\n }\n\n setMovementVector(x: number, y: number) {\n this.externalMove.set(x, y);\n }\n\n // -----------------------------\n // Setup\n // -----------------------------\n\n private bind() {\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n this.dom.addEventListener(\"mousedown\", this.onMouseDown);\n window.addEventListener(\"mouseup\", this.onMouseUp);\n window.addEventListener(\"mousemove\", this.onMouseMove);\n this.dom.addEventListener(\"wheel\", this.onWheel, { passive: false });\n }\n\n private bindTouch() {\n this.dom.addEventListener(\"touchstart\", this.onTouchStart);\n this.dom.addEventListener(\"touchmove\", this.onTouchMove);\n }\n\n private unbind() {\n this.dom.removeEventListener(\"mousedown\", this.onMouseDown);\n window.removeEventListener(\"mouseup\", this.onMouseUp);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n this.dom.removeEventListener(\"wheel\", this.onWheel);\n }\n\n private unbindTouch() {\n this.dom.removeEventListener(\"touchstart\", this.onTouchStart);\n this.dom.removeEventListener(\"touchmove\", this.onTouchMove);\n }\n\n private syncFromCamera() {\n const offset = this.camera.position.clone().sub(this.target);\n this.distance = offset.length();\n\n const spherical = new THREE.Spherical().setFromVector3(offset);\n this.yaw = spherical.theta;\n this.pitch = spherical.phi;\n }\n\n // -----------------------------\n // Input\n // -----------------------------\n\n private onMouseDown = (e: MouseEvent) => {\n if (e.button === 0) {\n // LEFT = orbit\n this.dragging = true;\n } else if (e.button === 2 || e.button === 1) {\n // RIGHT or MIDDLE = pan\n this.panning = true;\n }\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n };\n\n private onMouseUp = () => {\n this.dragging = false;\n this.panning = false;\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (!this.dragging && !this.panning) return;\n\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n\n // -----------------\n // ORBIT ROTATION\n // -----------------\n if (this.dragging) {\n this.yaw -= dx * this.rotateSpeed;\n this.pitch -= dy * this.rotateSpeed;\n\n this.pitch = THREE.MathUtils.clamp(this.pitch, 0.01, Math.PI - 0.01);\n\n this.updateCamera();\n return;\n }\n\n // -----------------\n // PANNING\n // -----------------\n if (this.panning) {\n const panScale = this.distance * this.panSpeed;\n\n const right = new THREE.Vector3();\n const up = new THREE.Vector3();\n\n this.camera.getWorldDirection(up);\n right.crossVectors(this.camera.up, up).normalize();\n up.crossVectors(\n right,\n this.camera.getWorldDirection(new THREE.Vector3()),\n ).normalize();\n\n const panOffset = new THREE.Vector3()\n .addScaledVector(right, dx * panScale)\n .addScaledVector(up, -dy * panScale);\n\n this.target.add(panOffset);\n this.camera.position.add(panOffset);\n\n // no updateCamera() — we already moved both\n }\n };\n\n private onWheel = (e: WheelEvent) => {\n e.preventDefault();\n\n const zoomFactor = Math.pow(this.zoomSpeed, e.deltaY / 100);\n this.distance *= zoomFactor;\n\n this.distance = THREE.MathUtils.clamp(\n this.distance,\n this.minDistance,\n this.maxDistance,\n );\n\n this.updateCamera();\n };\n\n private onTouchStart = (e: TouchEvent) => {\n const t = e.touches[0];\n this.lastTouchX = t.clientX;\n this.lastTouchY = t.clientY;\n };\n\n private onTouchMove = (e: TouchEvent) => {\n const t = e.touches[0];\n\n const deltaX = t.clientX - this.lastTouchX;\n const deltaY = t.clientY - this.lastTouchY;\n\n this.lastTouchX = t.clientX;\n this.lastTouchY = t.clientY;\n\n this.yaw -= deltaX * 0.002;\n this.pitch -= deltaY * 0.002;\n\n this.pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.pitch));\n this.camera.quaternion.setFromEuler(\n new THREE.Euler(this.pitch, this.yaw, 0, \"YXZ\"),\n );\n };\n\n // -----------------------------\n // Camera math\n // -----------------------------\n\n private updateCamera() {\n const spherical = new THREE.Spherical(this.distance, this.pitch, this.yaw);\n\n const pos = new THREE.Vector3()\n .setFromSpherical(spherical)\n .add(this.target);\n\n this.camera.position.copy(pos);\n this.camera.lookAt(this.target);\n }\n\n setTarget(v: THREE.Vector3) {\n this.target.copy(v);\n this.updateCamera();\n }\n}\n","import * as THREE from \"three\";\n\nexport type MeasurementOptions = {\n dom: HTMLElement;\n camera: THREE.PerspectiveCamera;\n scene: THREE.Scene;\n colliders: THREE.Object3D[];\n};\n\ninterface Measurement {\n points: THREE.Vector3[];\n markers: THREE.Mesh[];\n lines: THREE.Line[]; // Changed from line: THREE.Line\n labels: HTMLDivElement[]; // Changed from label: HTMLDivElement\n}\n\nexport class MeasurementController {\n private dom: HTMLElement;\n private camera: THREE.PerspectiveCamera;\n private scene: THREE.Scene;\n private colliders: THREE.Object3D[];\n\n private raycaster = new THREE.Raycaster();\n private mouse = new THREE.Vector2();\n\n private measurements: Measurement[] = [];\n private activePoints: THREE.Vector3[] = [];\n private activeMarkers: THREE.Mesh[] = [];\n private activeLines: THREE.Line[] = [];\n private activeLabels: HTMLDivElement[] = [];\n\n // Ghost elements for the *next* potential segment\n private ghostLine: THREE.Line | null = null;\n private ghostLabel: HTMLDivElement | null = null;\n private lastGhostPoint: THREE.Vector3 | null = null;\n\n private yaw = 0;\n private pitch = Math.PI / 4;\n private dragging = false;\n private panning = false;\n private lastX = 0;\n private lastY = 0;\n private rotateSpeed = 0.005;\n private panSpeed = 0.01;\n private keys: Record<string, boolean> = {};\n private moveSpeed: number = 10;\n private isVisible = true;\n\n constructor(opts: MeasurementOptions) {\n this.dom = opts.dom;\n this.camera = opts.camera;\n this.scene = opts.scene;\n this.colliders = opts.colliders;\n\n this.bind();\n this.bindKeyboard();\n }\n\n dispose() {\n this.unbind();\n this.unbindKeyboard();\n this.clearAll();\n }\n\n private bindKeyboard() {\n window.addEventListener(\"keydown\", this.onKeyDown);\n window.addEventListener(\"keyup\", this.onKeyUp);\n }\n\n private unbindKeyboard() {\n window.removeEventListener(\"keydown\", this.onKeyDown);\n window.removeEventListener(\"keyup\", this.onKeyUp);\n }\n\n private onKeyDown = (e: KeyboardEvent) => {\n this.keys[e.code] = true;\n };\n\n private onKeyUp = (e: KeyboardEvent) => {\n this.keys[e.code] = false;\n };\n\n public setVisible(visible: boolean) {\n this.isVisible = visible;\n this.measurements.forEach(m => {\n m.markers.forEach(mk => mk.visible = visible);\n m.lines.forEach(l => l.visible = visible);\n if (!visible) m.labels.forEach(lbl => lbl.style.display = \"none\");\n });\n\n this.activeMarkers.forEach(mk => mk.visible = visible);\n this.activeLines.forEach(l => l.visible = visible);\n if (!visible) this.activeLabels.forEach(lbl => lbl.style.display = \"none\");\n\n if (this.ghostLine) this.ghostLine.visible = visible;\n if (this.ghostLabel && !visible) this.ghostLabel.style.display = \"none\";\n }\n\n private bind() {\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n this.dom.addEventListener(\"mousedown\", this.onMouseDown);\n window.addEventListener(\"mousemove\", this.onMouseMove);\n window.addEventListener(\"mouseup\", this.onMouseUp);\n }\n\n private unbind() {\n this.dom.removeEventListener(\"mousedown\", this.onMouseDown);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n window.removeEventListener(\"mouseup\", this.onMouseUp);\n }\n\n private onMouseDown = (e: MouseEvent) => {\n if (e.button === 0) {\n if (!this.isVisible) return;\n if (e.shiftKey) {\n this.dragging = true;\n } else {\n this.handleLeftClick(e);\n }\n } else if (e.button === 2) {\n this.panning = true;\n if (this.isVisible) this.handleRightClick(e);\n } else if (e.button === 1) {\n this.dragging = true;\n }\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n };\n\n private onMouseUp = () => {\n this.dragging = false;\n this.panning = false;\n };\n\n private handleLeftClick(e: MouseEvent) {\n const rect = this.dom.getBoundingClientRect();\n this.mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;\n this.mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;\n\n this.raycaster.setFromCamera(this.mouse, this.camera);\n const intersects = this.raycaster.intersectObjects(this.colliders, true);\n\n if (intersects.length > 0) {\n const hit = intersects[0].point;\n this.addPoint(hit);\n }\n }\n\n private handleRightClick(e: MouseEvent) {\n console.log(\"[Measure] handleRightClick, activePoints:\", this.activePoints.length);\n if (this.activePoints.length > 0) {\n this.finalizePolyline();\n }\n }\n\n private onMouseMove = (e: MouseEvent) => {\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n\n if (!this.isVisible && !this.dragging && !this.panning) return;\n\n if (this.dragging) {\n this.yaw -= dx * this.rotateSpeed;\n this.pitch -= dy * this.rotateSpeed;\n this.pitch = Math.max(0.01, Math.min(Math.PI - 0.01, this.pitch));\n this.camera.quaternion.setFromEuler(new THREE.Euler(this.pitch, this.yaw, 0, 'YXZ'));\n return;\n }\n\n if (this.panning) {\n const right = new THREE.Vector3().set(1, 0, 0).applyQuaternion(this.camera.quaternion);\n const up = new THREE.Vector3().set(0, 1, 0).applyQuaternion(this.camera.quaternion);\n const pan = new THREE.Vector3().addScaledVector(right, -dx * this.panSpeed).addScaledVector(up, dy * this.panSpeed);\n this.camera.position.add(pan);\n return;\n }\n\n if (this.activePoints.length > 0 || true) { // Always update mouse pos for raycasting\n const rect = this.dom.getBoundingClientRect();\n this.mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;\n this.mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;\n\n if (this.activePoints.length > 0) {\n this.raycaster.setFromCamera(this.mouse, this.camera);\n const intersects = this.raycaster.intersectObjects(this.colliders, true);\n if (intersects.length > 0) {\n this.lastGhostPoint = intersects[0].point.clone();\n this.updateGhost(this.activePoints[this.activePoints.length - 1], this.lastGhostPoint);\n }\n }\n }\n };\n\n private addPoint(pos: THREE.Vector3) {\n // Add Marker for new point\n const marker = this.createMarker(pos);\n this.scene.add(marker);\n this.activeMarkers.push(marker);\n\n // If this isn't the first point, create a permanent line + label from previous point\n if (this.activePoints.length > 0) {\n const prev = this.activePoints[this.activePoints.length - 1];\n const current = pos.clone();\n\n const line = this.createLine(prev, current, 0.8); // 0.8 opacity for permanent\n this.scene.add(line);\n this.activeLines.push(line);\n\n const label = this.createLabel();\n this.updateLabelPos(label, prev, current);\n this.activeLabels.push(label);\n }\n\n this.activePoints.push(pos.clone());\n }\n\n private updateGhost(p1: THREE.Vector3, p2: THREE.Vector3) {\n // Update Line\n if (this.ghostLine) this.scene.remove(this.ghostLine);\n this.ghostLine = this.createLine(p1, p2, 0.4); // lower opacity for ghost\n this.scene.add(this.ghostLine);\n\n // Update Label\n if (!this.ghostLabel) {\n this.ghostLabel = this.createLabel();\n }\n this.updateLabelPos(this.ghostLabel, p1, p2);\n }\n\n public finalizePolyline() {\n console.log(\"[Measure] finalizePolyline, points:\", this.activePoints.length);\n if (this.activePoints.length === 0) {\n this.clearActiveSession();\n return;\n }\n\n // Bundle everything into a measurement object\n // NOTE: In a real polyline impl, we might want to store all segments. \n // For 'getMeasurements', we'll flatten this or just store lines.\n // For now, let's treat the whole polyline as one \"Measurement\" entry \n // but it holds multiple lines/markers.\n\n // We'll trick the existing structure a bit or assume the consumer handles it,\n // but to keep it compatible with existing types, let's construct a \"Measurement\" \n // that holds arrays of these things.\n\n // Actually, we're better off refactoring `Measurement` interface if we want true polyline support.\n // But to stick to the requested \"add multiple point functionality\" quickly:\n // We will push ONE measurement object per polyline.\n // However, the `Measurement` interface defined previously had `line: THREE.Line` (singular).\n // Let's update `Measurement` interface to support multiple.\n\n // Wait, to avoid breaking too much code, let's just make `Measurement` hold arrays of lines too?\n // Or better: Each segment is a measurement? No, that spams the list.\n // Let's assume a \"Distance\" is total distance? \n\n // Let's assume the user just wants to click-click-click and leave trails.\n // We will group them.\n\n const newMeasurement: Measurement = {\n points: [...this.activePoints],\n markers: [...this.activeMarkers],\n // We need to change the interface to support multiple lines/labels or change how we store them.\n // For this specific 'hack' to work with minimal changes, let's cast or change the type below.\n // See 'createLine' helper.\n lines: [...this.activeLines],\n labels: [...this.activeLabels]\n } as any;\n\n this.measurements.push(newMeasurement);\n\n // Clear active session state WITHOUT removing from scene\n this.activePoints = [];\n this.activeMarkers = [];\n this.activeLines = [];\n this.activeLabels = [];\n\n if (this.ghostLine) {\n this.scene.remove(this.ghostLine);\n this.ghostLine = null;\n }\n if (this.ghostLabel) {\n this.ghostLabel.remove();\n this.ghostLabel = null;\n }\n this.lastGhostPoint = null;\n }\n\n private clearActiveSession() {\n this.activeMarkers.forEach(mk => this.scene.remove(mk));\n this.activeLines.forEach(l => this.scene.remove(l));\n this.activeLabels.forEach(l => l.remove());\n\n this.activePoints = [];\n this.activeMarkers = [];\n this.activeLines = [];\n this.activeLabels = [];\n\n if (this.ghostLine) {\n this.scene.remove(this.ghostLine);\n this.ghostLine = null;\n }\n if (this.ghostLabel) {\n this.ghostLabel.remove();\n this.ghostLabel = null;\n }\n this.lastGhostPoint = null;\n }\n\n private createLine(p1: THREE.Vector3, p2: THREE.Vector3, opacity: number) {\n const geometry = new THREE.BufferGeometry().setFromPoints([p1, p2]);\n const material = new THREE.LineBasicMaterial({\n color: 0xffffff,\n linewidth: 1,\n depthTest: false,\n transparent: true,\n opacity: opacity\n });\n const line = new THREE.Line(geometry, material);\n line.renderOrder = 1000;\n line.frustumCulled = false;\n return line;\n }\n\n private createMarker(pos: THREE.Vector3) {\n const marker = new THREE.Mesh(\n new THREE.SphereGeometry(0.02),\n new THREE.MeshBasicMaterial({\n color: 0xffffff,\n depthTest: false,\n transparent: true,\n opacity: 0.9\n })\n );\n marker.position.copy(pos);\n marker.renderOrder = 1001;\n marker.frustumCulled = false;\n return marker;\n }\n\n private createLabel() {\n const label = document.createElement(\"div\");\n label.style.position = \"absolute\";\n label.style.pointerEvents = \"none\";\n label.style.background = \"rgba(0,0,0,0.6)\";\n label.style.color = \"white\";\n label.style.padding = \"4px 8px\";\n label.style.borderRadius = \"12px\";\n label.style.fontSize = \"11px\";\n label.style.fontFamily = \"sans-serif\";\n label.style.fontWeight = \"500\";\n label.style.zIndex = \"100\";\n label.style.backdropFilter = \"blur(4px)\";\n // label.style.border = \"1px solid rgba(255,255,255,0.2)\";\n this.dom.parentElement?.appendChild(label);\n return label;\n }\n\n private updateLabelPos(label: HTMLDivElement, p1: THREE.Vector3, p2: THREE.Vector3) {\n const distance = p1.distanceTo(p2);\n const midPoint = new THREE.Vector3().lerpVectors(p1, p2, 0.5);\n const vector = midPoint.project(this.camera);\n\n if (vector.z > 1) {\n label.style.display = \"none\";\n return;\n }\n\n const x = (vector.x * 0.5 + 0.5) * this.dom.clientWidth;\n const y = (-(vector.y * 0.5) + 0.5) * this.dom.clientHeight;\n\n label.style.left = `${x}px`;\n label.style.top = `${y}px`;\n label.innerText = `${distance.toFixed(3)}m`;\n label.style.display = (this.isVisible && vector.z <= 1) ? \"block\" : \"none\";\n }\n\n public restoreMeasurements(measurements: Measurement[]) {\n this.measurements = measurements;\n this.measurements.forEach(m => {\n m.markers.forEach(mk => {\n mk.visible = this.isVisible;\n this.scene.add(mk);\n });\n m.lines.forEach(l => {\n l.visible = this.isVisible;\n this.scene.add(l);\n });\n m.labels.forEach((lbl, idx) => {\n this.dom.parentElement?.appendChild(lbl);\n if (idx < m.points.length - 1) {\n this.updateLabelPos(lbl, m.points[idx], m.points[idx + 1]);\n }\n });\n });\n }\n\n public getMeasurements() {\n return this.measurements.map((m, i) => {\n // Calculate total distance for the polyline\n let totalDist = 0;\n for (let j = 0; j < m.points.length - 1; j++) {\n totalDist += m.points[j].distanceTo(m.points[j + 1]);\n }\n\n return {\n id: i,\n distance: totalDist,\n points: m.points.map(p => p.clone()),\n segments: m.points.length - 1\n };\n });\n }\n\n public deleteMeasurement(index: number) {\n if (index < 0 || index >= this.measurements.length) return;\n const m = this.measurements[index];\n m.markers.forEach(mk => this.scene.remove(mk));\n m.lines.forEach(l => this.scene.remove(l));\n m.labels.forEach(lbl => lbl.remove());\n this.measurements.splice(index, 1);\n }\n\n public clearAll() {\n this.measurements.forEach(m => {\n m.markers.forEach(mk => this.scene.remove(mk));\n m.lines.forEach(l => this.scene.remove(l));\n m.labels.forEach(lbl => lbl.remove());\n });\n this.measurements = [];\n this.activePoints = [];\n if (this.ghostLine) this.scene.remove(this.ghostLine);\n\n // Clear any leftover active markers if the session was abandoned\n this.activeMarkers.forEach(mk => this.scene.remove(mk));\n this.activeLines.forEach(l => this.scene.remove(l));\n this.activeLabels.forEach(l => l.remove());\n\n if (this.ghostLabel) this.ghostLabel.remove();\n this.ghostLine = null;\n this.ghostLabel = null;\n this.lastGhostPoint = null;\n }\n\n update(dt: number = 1 / 60) {\n if (!this.isVisible) return;\n const move = new THREE.Vector3(0, 0, 0);\n\n // Keyboard Movement (WASDQE)\n const forward = new THREE.Vector3();\n this.camera.getWorldDirection(forward);\n const orbitForward = forward.clone();\n orbitForward.y = 0;\n orbitForward.normalize();\n\n const right = new THREE.Vector3()\n .crossVectors(orbitForward, new THREE.Vector3(0, 1, 0))\n .normalize();\n\n if (this.keys[\"KeyW\"]) move.add(orbitForward);\n if (this.keys[\"KeyS\"]) move.sub(orbitForward);\n if (this.keys[\"KeyA\"]) move.sub(right);\n if (this.keys[\"KeyD\"]) move.add(right);\n if (this.keys[\"KeyE\"]) move.y += 1;\n if (this.keys[\"KeyQ\"]) move.y -= 1;\n\n if (move.lengthSq() > 0) {\n move.normalize();\n move.multiplyScalar(this.moveSpeed * dt);\n this.camera.position.add(move);\n }\n\n // Update raycasting/ghost even if mouse isn't moving, because camera is moving\n if (this.activePoints.length > 0) {\n this.raycaster.setFromCamera(this.mouse, this.camera);\n const intersects = this.raycaster.intersectObjects(this.colliders, true);\n if (intersects.length > 0) {\n this.lastGhostPoint = intersects[0].point.clone();\n this.updateGhost(this.activePoints[this.activePoints.length - 1], this.lastGhostPoint);\n }\n }\n\n this.measurements.forEach(m => {\n // Update all labels in this measurement\n m.labels.forEach((lbl, idx) => {\n if (idx < m.points.length - 1) {\n this.updateLabelPos(lbl, m.points[idx], m.points[idx + 1]);\n }\n });\n });\n\n // Also update active session labels\n this.activeLabels.forEach((lbl, idx) => {\n if (idx < this.activePoints.length - 1) {\n this.updateLabelPos(lbl, this.activePoints[idx], this.activePoints[idx + 1]);\n }\n });\n\n if (this.ghostLabel && this.activePoints.length > 0 && this.lastGhostPoint) {\n this.updateLabelPos(this.ghostLabel, this.activePoints[this.activePoints.length - 1], this.lastGhostPoint);\n }\n }\n}\n","import * as THREE from \"three\";\n\nexport type FloorplanOptions = {\n dom: HTMLElement;\n camera: THREE.PerspectiveCamera;\n target: THREE.Vector3;\n height: number; // camera y above target\n panSpeed?: number; // world units per pixel\n zoomSpeed?: number; // wheel multiplier\n minHeight?: number;\n maxHeight?: number;\n};\n\nexport class FloorplanController {\n private dom: HTMLElement;\n private camera: THREE.PerspectiveCamera;\n\n private target: THREE.Vector3;\n private height: number;\n\n private panSpeed: number;\n private zoomSpeed: number;\n private minHeight: number;\n private maxHeight: number;\n\n private isDragging = false;\n private lastX = 0;\n private lastY = 0;\n\n // touch\n private touchMode: \"none\" | \"pan\" | \"pinch\" = \"none\";\n private lastDist = 0;\n\n constructor(opts: FloorplanOptions) {\n this.dom = opts.dom;\n this.camera = opts.camera;\n\n this.target = opts.target.clone();\n this.height = opts.height;\n\n this.panSpeed = opts.panSpeed ?? 0.01;\n this.zoomSpeed = opts.zoomSpeed ?? 1.1;\n this.minHeight = opts.minHeight ?? opts.height * 0.25;\n this.maxHeight = opts.maxHeight ?? opts.height * 3.0;\n\n this.applyCamera();\n this.bind();\n }\n\n dispose() {\n this.unbind();\n }\n\n update(_dt: number) {\n // keep camera locked top-down always\n this.applyCamera();\n }\n\n setTarget(t: THREE.Vector3) {\n this.target.copy(t);\n this.applyCamera();\n }\n\n private applyCamera() {\n this.height = THREE.MathUtils.clamp(\n this.height,\n this.minHeight,\n this.maxHeight,\n );\n this.camera.position.set(\n this.target.x,\n this.target.y + this.height,\n this.target.z,\n );\n this.camera.up.set(0, 0, -1); // keeps “north” stable (optional)\n this.camera.lookAt(this.target);\n }\n\n private bind() {\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n this.dom.addEventListener(\"mousedown\", this.onMouseDown);\n window.addEventListener(\"mousemove\", this.onMouseMove);\n window.addEventListener(\"mouseup\", this.onMouseUp);\n this.dom.addEventListener(\"wheel\", this.onWheel, { passive: false });\n\n this.dom.addEventListener(\"touchstart\", this.onTouchStart, {\n passive: false,\n });\n this.dom.addEventListener(\"touchmove\", this.onTouchMove, {\n passive: false,\n });\n this.dom.addEventListener(\"touchend\", this.onTouchEnd);\n }\n\n private unbind() {\n this.dom.removeEventListener(\"mousedown\", this.onMouseDown);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n window.removeEventListener(\"mouseup\", this.onMouseUp);\n this.dom.removeEventListener(\"wheel\", this.onWheel);\n\n this.dom.removeEventListener(\"touchstart\", this.onTouchStart);\n this.dom.removeEventListener(\"touchmove\", this.onTouchMove);\n this.dom.removeEventListener(\"touchend\", this.onTouchEnd);\n }\n\n private onMouseDown = (e: MouseEvent) => {\n if (e.button !== 0) return;\n this.isDragging = true;\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n };\n\n private onMouseUp = () => {\n this.isDragging = false;\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (!this.isDragging) return;\n\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n\n // screen -> world: in top-down, x maps to world x, y maps to world z\n this.target.x -= dx * this.panSpeed * this.height;\n this.target.z += dy * this.panSpeed * this.height;\n\n this.applyCamera();\n };\n\n private onWheel = (e: WheelEvent) => {\n e.preventDefault();\n const factor = Math.pow(this.zoomSpeed, e.deltaY / 100);\n this.height *= factor;\n this.applyCamera();\n };\n\n private onTouchStart = (e: TouchEvent) => {\n if (e.touches.length === 1) {\n this.touchMode = \"pan\";\n this.lastX = e.touches[0].clientX;\n this.lastY = e.touches[0].clientY;\n } else if (e.touches.length === 2) {\n this.touchMode = \"pinch\";\n this.lastDist = this.touchDistance(e.touches[0], e.touches[1]);\n }\n };\n\n private onTouchMove = (e: TouchEvent) => {\n e.preventDefault();\n\n if (this.touchMode === \"pan\" && e.touches.length === 1) {\n const x = e.touches[0].clientX;\n const y = e.touches[0].clientY;\n const dx = x - this.lastX;\n const dy = y - this.lastY;\n this.lastX = x;\n this.lastY = y;\n\n this.target.x -= dx * this.panSpeed * this.height;\n this.target.z += dy * this.panSpeed * this.height;\n\n this.applyCamera();\n }\n\n if (this.touchMode === \"pinch\" && e.touches.length === 2) {\n const dist = this.touchDistance(e.touches[0], e.touches[1]);\n const ratio = this.lastDist / Math.max(1e-6, dist);\n this.lastDist = dist;\n\n this.height *= ratio;\n this.applyCamera();\n }\n };\n\n private onTouchEnd = () => {\n this.touchMode = \"none\";\n };\n\n private touchDistance(a: Touch, b: Touch) {\n const dx = a.clientX - b.clientX;\n const dy = a.clientY - b.clientY;\n return Math.sqrt(dx * dx + dy * dy);\n }\n}\n"],"mappings":"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","names":["THREE","RAPIER","THREE","THREE","THREE"]}