@popaya/pgsg-viewer 0.1.6 → 0.1.8

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-IJSA555G.js");
13
+ const { PGSGThreeViewer } = await import("./pgsg-three-viewer-KDDVV5TI.js");
14
14
  this.engine = new PGSGThreeViewer(this.options);
15
15
  }
16
16
  await this.engine.load();
@@ -25,9 +25,15 @@ var PGSGViewer = class {
25
25
  setCameraMode(mode) {
26
26
  this.engine?.setCameraMode?.(mode);
27
27
  }
28
+ finalizePolyline() {
29
+ this.engine?.finalizePolyline?.();
30
+ }
31
+ deleteMeasurement(index) {
32
+ this.engine?.deleteMeasurement?.(index);
33
+ }
28
34
  };
29
35
 
30
36
  export {
31
37
  PGSGViewer
32
38
  };
33
- //# sourceMappingURL=chunk-WFONX3TR.js.map
39
+ //# sourceMappingURL=chunk-RWHLWC6Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/viewer/pgsg-viewer.ts"],"sourcesContent":["import type { ViewerType, PGSGViewerEngine } 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: \"orbit\" | \"walk\" | \"measure\") {\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"],"mappings":";AAGO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YACU,SACR;AADQ;AAAA,EACN;AAAA,EAJI;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,MAAoC;AAChD,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;AACF;","names":[]}
@@ -43,7 +43,9 @@ declare class PGSGViewer {
43
43
  load(): Promise<void>;
44
44
  resize(): void;
45
45
  destroy(): void;
46
- setCameraMode(mode: "orbit" | "walk"): void;
46
+ setCameraMode(mode: "orbit" | "walk" | "measure"): void;
47
+ finalizePolyline(): void;
48
+ deleteMeasurement(index: number): void;
47
49
  }
48
50
 
49
51
  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-WFONX3TR.js";
4
+ } from "../chunk-RWHLWC6Z.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-WFONX3TR.js";
4
+ } from "./chunk-RWHLWC6Z.js";
5
5
  export {
6
6
  PGSGViewer
7
7
  };
@@ -1,5 +1,5 @@
1
1
  // src/viewers/three/pgsg-three-viewer.ts
2
- import * as THREE3 from "three";
2
+ import * as THREE4 from "three";
3
3
  import { GLTFLoader } from "three/examples/jsm/Addons.js";
4
4
  import nipplejs from "nipplejs";
5
5
 
@@ -217,6 +217,9 @@ var WalkCapsuleController = class {
217
217
  const forward = new THREE.Vector3();
218
218
  this.camera.getWorldDirection(forward);
219
219
  forward.y = 0;
220
+ if (forward.lengthSq() < 0.01) {
221
+ forward.set(0, 0, -1).applyQuaternion(new THREE.Quaternion().setFromEuler(new THREE.Euler(0, this.yaw, 0)));
222
+ }
220
223
  forward.normalize();
221
224
  const right = new THREE.Vector3().crossVectors(forward, new THREE.Vector3(0, 1, 0)).normalize();
222
225
  const wish = new THREE.Vector3();
@@ -267,9 +270,9 @@ var WalkCapsuleController = class {
267
270
  // --------------------------------------------------
268
271
  resolveGroundAndCollisions(desiredCamPos) {
269
272
  const camPos = desiredCamPos.clone();
270
- const maxRayDown = 10;
273
+ const maxRayDown = 20;
271
274
  const origin = camPos.clone();
272
- origin.y += 2;
275
+ origin.y += 5;
273
276
  this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));
274
277
  this.raycaster.near = 0;
275
278
  this.raycaster.far = maxRayDown;
@@ -512,6 +515,395 @@ var OrbitCameraController = class {
512
515
  }
513
516
  };
514
517
 
518
+ // src/viewers/three/controls/MeasurementController.ts
519
+ import * as THREE3 from "three";
520
+ var MeasurementController = class {
521
+ dom;
522
+ camera;
523
+ scene;
524
+ colliders;
525
+ raycaster = new THREE3.Raycaster();
526
+ mouse = new THREE3.Vector2();
527
+ measurements = [];
528
+ activePoints = [];
529
+ activeMarkers = [];
530
+ activeLines = [];
531
+ activeLabels = [];
532
+ // Ghost elements for the *next* potential segment
533
+ ghostLine = null;
534
+ ghostLabel = null;
535
+ lastGhostPoint = null;
536
+ yaw = 0;
537
+ pitch = Math.PI / 4;
538
+ dragging = false;
539
+ panning = false;
540
+ lastX = 0;
541
+ lastY = 0;
542
+ rotateSpeed = 5e-3;
543
+ panSpeed = 0.01;
544
+ keys = {};
545
+ moveSpeed = 10;
546
+ isVisible = true;
547
+ constructor(opts) {
548
+ this.dom = opts.dom;
549
+ this.camera = opts.camera;
550
+ this.scene = opts.scene;
551
+ this.colliders = opts.colliders;
552
+ this.bind();
553
+ this.bindKeyboard();
554
+ }
555
+ dispose() {
556
+ this.unbind();
557
+ this.unbindKeyboard();
558
+ this.clearAll();
559
+ }
560
+ bindKeyboard() {
561
+ window.addEventListener("keydown", this.onKeyDown);
562
+ window.addEventListener("keyup", this.onKeyUp);
563
+ }
564
+ unbindKeyboard() {
565
+ window.removeEventListener("keydown", this.onKeyDown);
566
+ window.removeEventListener("keyup", this.onKeyUp);
567
+ }
568
+ onKeyDown = (e) => {
569
+ this.keys[e.code] = true;
570
+ };
571
+ onKeyUp = (e) => {
572
+ this.keys[e.code] = false;
573
+ };
574
+ setVisible(visible) {
575
+ this.isVisible = visible;
576
+ this.measurements.forEach((m) => {
577
+ m.markers.forEach((mk) => mk.visible = visible);
578
+ m.lines.forEach((l) => l.visible = visible);
579
+ if (!visible) m.labels.forEach((lbl) => lbl.style.display = "none");
580
+ });
581
+ this.activeMarkers.forEach((mk) => mk.visible = visible);
582
+ this.activeLines.forEach((l) => l.visible = visible);
583
+ if (!visible) this.activeLabels.forEach((lbl) => lbl.style.display = "none");
584
+ if (this.ghostLine) this.ghostLine.visible = visible;
585
+ if (this.ghostLabel && !visible) this.ghostLabel.style.display = "none";
586
+ }
587
+ bind() {
588
+ this.dom.addEventListener("contextmenu", (e) => e.preventDefault());
589
+ this.dom.addEventListener("mousedown", this.onMouseDown);
590
+ window.addEventListener("mousemove", this.onMouseMove);
591
+ window.addEventListener("mouseup", this.onMouseUp);
592
+ }
593
+ unbind() {
594
+ this.dom.removeEventListener("mousedown", this.onMouseDown);
595
+ window.removeEventListener("mousemove", this.onMouseMove);
596
+ window.removeEventListener("mouseup", this.onMouseUp);
597
+ }
598
+ onMouseDown = (e) => {
599
+ if (e.button === 0) {
600
+ if (!this.isVisible) return;
601
+ if (e.shiftKey) {
602
+ this.dragging = true;
603
+ } else {
604
+ this.handleLeftClick(e);
605
+ }
606
+ } else if (e.button === 2) {
607
+ this.panning = true;
608
+ if (this.isVisible) this.handleRightClick(e);
609
+ } else if (e.button === 1) {
610
+ this.dragging = true;
611
+ }
612
+ this.lastX = e.clientX;
613
+ this.lastY = e.clientY;
614
+ };
615
+ onMouseUp = () => {
616
+ this.dragging = false;
617
+ this.panning = false;
618
+ };
619
+ handleLeftClick(e) {
620
+ const rect = this.dom.getBoundingClientRect();
621
+ this.mouse.x = (e.clientX - rect.left) / rect.width * 2 - 1;
622
+ this.mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
623
+ this.raycaster.setFromCamera(this.mouse, this.camera);
624
+ const intersects = this.raycaster.intersectObjects(this.colliders, true);
625
+ if (intersects.length > 0) {
626
+ const hit = intersects[0].point;
627
+ this.addPoint(hit);
628
+ }
629
+ }
630
+ handleRightClick(e) {
631
+ console.log("[Measure] handleRightClick, activePoints:", this.activePoints.length);
632
+ if (this.activePoints.length > 0) {
633
+ this.finalizePolyline();
634
+ }
635
+ }
636
+ onMouseMove = (e) => {
637
+ const dx = e.clientX - this.lastX;
638
+ const dy = e.clientY - this.lastY;
639
+ this.lastX = e.clientX;
640
+ this.lastY = e.clientY;
641
+ if (!this.isVisible && !this.dragging && !this.panning) return;
642
+ if (this.dragging) {
643
+ this.yaw -= dx * this.rotateSpeed;
644
+ this.pitch -= dy * this.rotateSpeed;
645
+ this.pitch = Math.max(0.01, Math.min(Math.PI - 0.01, this.pitch));
646
+ this.camera.quaternion.setFromEuler(new THREE3.Euler(this.pitch, this.yaw, 0, "YXZ"));
647
+ return;
648
+ }
649
+ if (this.panning) {
650
+ const right = new THREE3.Vector3().set(1, 0, 0).applyQuaternion(this.camera.quaternion);
651
+ const up = new THREE3.Vector3().set(0, 1, 0).applyQuaternion(this.camera.quaternion);
652
+ const pan = new THREE3.Vector3().addScaledVector(right, -dx * this.panSpeed).addScaledVector(up, dy * this.panSpeed);
653
+ this.camera.position.add(pan);
654
+ return;
655
+ }
656
+ if (this.activePoints.length > 0 || true) {
657
+ const rect = this.dom.getBoundingClientRect();
658
+ this.mouse.x = (e.clientX - rect.left) / rect.width * 2 - 1;
659
+ this.mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
660
+ if (this.activePoints.length > 0) {
661
+ this.raycaster.setFromCamera(this.mouse, this.camera);
662
+ const intersects = this.raycaster.intersectObjects(this.colliders, true);
663
+ if (intersects.length > 0) {
664
+ this.lastGhostPoint = intersects[0].point.clone();
665
+ this.updateGhost(this.activePoints[this.activePoints.length - 1], this.lastGhostPoint);
666
+ }
667
+ }
668
+ }
669
+ };
670
+ addPoint(pos) {
671
+ const marker = this.createMarker(pos);
672
+ this.scene.add(marker);
673
+ this.activeMarkers.push(marker);
674
+ if (this.activePoints.length > 0) {
675
+ const prev = this.activePoints[this.activePoints.length - 1];
676
+ const current = pos.clone();
677
+ const line = this.createLine(prev, current, 0.8);
678
+ this.scene.add(line);
679
+ this.activeLines.push(line);
680
+ const label = this.createLabel();
681
+ this.updateLabelPos(label, prev, current);
682
+ this.activeLabels.push(label);
683
+ }
684
+ this.activePoints.push(pos.clone());
685
+ }
686
+ updateGhost(p1, p2) {
687
+ if (this.ghostLine) this.scene.remove(this.ghostLine);
688
+ this.ghostLine = this.createLine(p1, p2, 0.4);
689
+ this.scene.add(this.ghostLine);
690
+ if (!this.ghostLabel) {
691
+ this.ghostLabel = this.createLabel();
692
+ }
693
+ this.updateLabelPos(this.ghostLabel, p1, p2);
694
+ }
695
+ finalizePolyline() {
696
+ console.log("[Measure] finalizePolyline, points:", this.activePoints.length);
697
+ if (this.activePoints.length === 0) {
698
+ this.clearActiveSession();
699
+ return;
700
+ }
701
+ const newMeasurement = {
702
+ points: [...this.activePoints],
703
+ markers: [...this.activeMarkers],
704
+ // We need to change the interface to support multiple lines/labels or change how we store them.
705
+ // For this specific 'hack' to work with minimal changes, let's cast or change the type below.
706
+ // See 'createLine' helper.
707
+ lines: [...this.activeLines],
708
+ labels: [...this.activeLabels]
709
+ };
710
+ this.measurements.push(newMeasurement);
711
+ this.activePoints = [];
712
+ this.activeMarkers = [];
713
+ this.activeLines = [];
714
+ this.activeLabels = [];
715
+ if (this.ghostLine) {
716
+ this.scene.remove(this.ghostLine);
717
+ this.ghostLine = null;
718
+ }
719
+ if (this.ghostLabel) {
720
+ this.ghostLabel.remove();
721
+ this.ghostLabel = null;
722
+ }
723
+ this.lastGhostPoint = null;
724
+ }
725
+ clearActiveSession() {
726
+ this.activeMarkers.forEach((mk) => this.scene.remove(mk));
727
+ this.activeLines.forEach((l) => this.scene.remove(l));
728
+ this.activeLabels.forEach((l) => l.remove());
729
+ this.activePoints = [];
730
+ this.activeMarkers = [];
731
+ this.activeLines = [];
732
+ this.activeLabels = [];
733
+ if (this.ghostLine) {
734
+ this.scene.remove(this.ghostLine);
735
+ this.ghostLine = null;
736
+ }
737
+ if (this.ghostLabel) {
738
+ this.ghostLabel.remove();
739
+ this.ghostLabel = null;
740
+ }
741
+ this.lastGhostPoint = null;
742
+ }
743
+ createLine(p1, p2, opacity) {
744
+ const geometry = new THREE3.BufferGeometry().setFromPoints([p1, p2]);
745
+ const material = new THREE3.LineBasicMaterial({
746
+ color: 16777215,
747
+ linewidth: 1,
748
+ depthTest: false,
749
+ transparent: true,
750
+ opacity
751
+ });
752
+ const line = new THREE3.Line(geometry, material);
753
+ line.renderOrder = 1e3;
754
+ line.frustumCulled = false;
755
+ return line;
756
+ }
757
+ createMarker(pos) {
758
+ const marker = new THREE3.Mesh(
759
+ new THREE3.SphereGeometry(0.02),
760
+ new THREE3.MeshBasicMaterial({
761
+ color: 16777215,
762
+ depthTest: false,
763
+ transparent: true,
764
+ opacity: 0.9
765
+ })
766
+ );
767
+ marker.position.copy(pos);
768
+ marker.renderOrder = 1001;
769
+ marker.frustumCulled = false;
770
+ return marker;
771
+ }
772
+ createLabel() {
773
+ const label = document.createElement("div");
774
+ label.style.position = "absolute";
775
+ label.style.pointerEvents = "none";
776
+ label.style.background = "rgba(0,0,0,0.6)";
777
+ label.style.color = "white";
778
+ label.style.padding = "4px 8px";
779
+ label.style.borderRadius = "12px";
780
+ label.style.fontSize = "11px";
781
+ label.style.fontFamily = "sans-serif";
782
+ label.style.fontWeight = "500";
783
+ label.style.zIndex = "100";
784
+ label.style.backdropFilter = "blur(4px)";
785
+ this.dom.parentElement?.appendChild(label);
786
+ return label;
787
+ }
788
+ updateLabelPos(label, p1, p2) {
789
+ const distance = p1.distanceTo(p2);
790
+ const midPoint = new THREE3.Vector3().lerpVectors(p1, p2, 0.5);
791
+ const vector = midPoint.project(this.camera);
792
+ if (vector.z > 1) {
793
+ label.style.display = "none";
794
+ return;
795
+ }
796
+ const x = (vector.x * 0.5 + 0.5) * this.dom.clientWidth;
797
+ const y = (-(vector.y * 0.5) + 0.5) * this.dom.clientHeight;
798
+ label.style.left = `${x}px`;
799
+ label.style.top = `${y}px`;
800
+ label.innerText = `${distance.toFixed(3)}m`;
801
+ label.style.display = this.isVisible && vector.z <= 1 ? "block" : "none";
802
+ }
803
+ restoreMeasurements(measurements) {
804
+ this.measurements = measurements;
805
+ this.measurements.forEach((m) => {
806
+ m.markers.forEach((mk) => {
807
+ mk.visible = this.isVisible;
808
+ this.scene.add(mk);
809
+ });
810
+ m.lines.forEach((l) => {
811
+ l.visible = this.isVisible;
812
+ this.scene.add(l);
813
+ });
814
+ m.labels.forEach((lbl, idx) => {
815
+ this.dom.parentElement?.appendChild(lbl);
816
+ if (idx < m.points.length - 1) {
817
+ this.updateLabelPos(lbl, m.points[idx], m.points[idx + 1]);
818
+ }
819
+ });
820
+ });
821
+ }
822
+ getMeasurements() {
823
+ return this.measurements.map((m, i) => {
824
+ let totalDist = 0;
825
+ for (let j = 0; j < m.points.length - 1; j++) {
826
+ totalDist += m.points[j].distanceTo(m.points[j + 1]);
827
+ }
828
+ return {
829
+ id: i,
830
+ distance: totalDist,
831
+ points: m.points.map((p) => p.clone()),
832
+ segments: m.points.length - 1
833
+ };
834
+ });
835
+ }
836
+ deleteMeasurement(index) {
837
+ if (index < 0 || index >= this.measurements.length) return;
838
+ const m = this.measurements[index];
839
+ m.markers.forEach((mk) => this.scene.remove(mk));
840
+ m.lines.forEach((l) => this.scene.remove(l));
841
+ m.labels.forEach((lbl) => lbl.remove());
842
+ this.measurements.splice(index, 1);
843
+ }
844
+ clearAll() {
845
+ this.measurements.forEach((m) => {
846
+ m.markers.forEach((mk) => this.scene.remove(mk));
847
+ m.lines.forEach((l) => this.scene.remove(l));
848
+ m.labels.forEach((lbl) => lbl.remove());
849
+ });
850
+ this.measurements = [];
851
+ this.activePoints = [];
852
+ if (this.ghostLine) this.scene.remove(this.ghostLine);
853
+ this.activeMarkers.forEach((mk) => this.scene.remove(mk));
854
+ this.activeLines.forEach((l) => this.scene.remove(l));
855
+ this.activeLabels.forEach((l) => l.remove());
856
+ if (this.ghostLabel) this.ghostLabel.remove();
857
+ this.ghostLine = null;
858
+ this.ghostLabel = null;
859
+ this.lastGhostPoint = null;
860
+ }
861
+ update(dt = 1 / 60) {
862
+ if (!this.isVisible) return;
863
+ const move = new THREE3.Vector3(0, 0, 0);
864
+ const forward = new THREE3.Vector3();
865
+ this.camera.getWorldDirection(forward);
866
+ const orbitForward = forward.clone();
867
+ orbitForward.y = 0;
868
+ orbitForward.normalize();
869
+ const right = new THREE3.Vector3().crossVectors(orbitForward, new THREE3.Vector3(0, 1, 0)).normalize();
870
+ if (this.keys["KeyW"]) move.add(orbitForward);
871
+ if (this.keys["KeyS"]) move.sub(orbitForward);
872
+ if (this.keys["KeyA"]) move.sub(right);
873
+ if (this.keys["KeyD"]) move.add(right);
874
+ if (this.keys["KeyE"]) move.y += 1;
875
+ if (this.keys["KeyQ"]) move.y -= 1;
876
+ if (move.lengthSq() > 0) {
877
+ move.normalize();
878
+ move.multiplyScalar(this.moveSpeed * dt);
879
+ this.camera.position.add(move);
880
+ }
881
+ if (this.activePoints.length > 0) {
882
+ this.raycaster.setFromCamera(this.mouse, this.camera);
883
+ const intersects = this.raycaster.intersectObjects(this.colliders, true);
884
+ if (intersects.length > 0) {
885
+ this.lastGhostPoint = intersects[0].point.clone();
886
+ this.updateGhost(this.activePoints[this.activePoints.length - 1], this.lastGhostPoint);
887
+ }
888
+ }
889
+ this.measurements.forEach((m) => {
890
+ m.labels.forEach((lbl, idx) => {
891
+ if (idx < m.points.length - 1) {
892
+ this.updateLabelPos(lbl, m.points[idx], m.points[idx + 1]);
893
+ }
894
+ });
895
+ });
896
+ this.activeLabels.forEach((lbl, idx) => {
897
+ if (idx < this.activePoints.length - 1) {
898
+ this.updateLabelPos(lbl, this.activePoints[idx], this.activePoints[idx + 1]);
899
+ }
900
+ });
901
+ if (this.ghostLabel && this.activePoints.length > 0 && this.lastGhostPoint) {
902
+ this.updateLabelPos(this.ghostLabel, this.activePoints[this.activePoints.length - 1], this.lastGhostPoint);
903
+ }
904
+ }
905
+ };
906
+
515
907
  // src/viewers/three/pgsg-three-viewer.ts
516
908
  var PGSGThreeViewer = class {
517
909
  constructor(options) {
@@ -522,7 +914,7 @@ var PGSGThreeViewer = class {
522
914
  throw new Error("[PGSGThreeViewer] Container is required");
523
915
  }
524
916
  }
525
- scene = new THREE3.Scene();
917
+ scene = new THREE4.Scene();
526
918
  camera;
527
919
  renderer;
528
920
  splats;
@@ -530,11 +922,13 @@ var PGSGThreeViewer = class {
530
922
  resizeObserver = null;
531
923
  controls;
532
924
  running = false;
533
- orbitTarget = new THREE3.Vector3();
925
+ orbitTarget = new THREE4.Vector3();
534
926
  lastTime = performance.now();
535
927
  colliders = [];
536
- worldRoot = new THREE3.Object3D();
537
- splatPivot = new THREE3.Object3D();
928
+ persistentMeasurements = [];
929
+ measurementController = null;
930
+ worldRoot = new THREE4.Object3D();
931
+ splatPivot = new THREE4.Object3D();
538
932
  colliderMesh;
539
933
  modeButton;
540
934
  async load() {
@@ -562,9 +956,17 @@ var PGSGThreeViewer = class {
562
956
  this.switchToWalk();
563
957
  this.updateModeLabel("WALK");
564
958
  const walk = this.controls;
565
- walk.enableGyro();
959
+ window.addEventListener(
960
+ "keydown",
961
+ (e) => {
962
+ if (e.key === "g") {
963
+ walk.enableGyro();
964
+ }
965
+ },
966
+ { once: true }
967
+ );
566
968
  } else {
567
- this.switchToOrbit();
969
+ this.switchToMeasure();
568
970
  }
569
971
  window.addEventListener("keydown", this.onKeyToggle);
570
972
  }
@@ -599,16 +1001,29 @@ var PGSGThreeViewer = class {
599
1001
  this.renderer.dispose();
600
1002
  }
601
1003
  setCameraMode(mode) {
602
- if (mode === "orbit") {
603
- document.exitPointerLock();
1004
+ console.log("[PGSGThreeViewer] setCameraMode:", mode);
1005
+ if (this.controls instanceof MeasurementController) {
1006
+ this.persistentMeasurements = this.controls.measurements;
1007
+ this.controls.setVisible(false);
604
1008
  }
605
- if (mode === "walk") {
606
- setTimeout(() => {
607
- if (document.pointerLockElement !== this.renderer.domElement) {
608
- this.renderer.domElement.focus();
609
- this.renderer.domElement.requestPointerLock();
1009
+ switch (mode) {
1010
+ case "measure":
1011
+ document.exitPointerLock();
1012
+ this.switchToMeasure();
1013
+ if (this.controls instanceof MeasurementController) {
1014
+ this.controls.setVisible(true);
610
1015
  }
611
- }, 0);
1016
+ break;
1017
+ case "orbit":
1018
+ document.exitPointerLock();
1019
+ this.switchToOrbit();
1020
+ break;
1021
+ case "walk":
1022
+ this.switchToWalk();
1023
+ break;
1024
+ default:
1025
+ this.switchToMeasure();
1026
+ break;
612
1027
  }
613
1028
  }
614
1029
  toggleCameraMode() {
@@ -622,13 +1037,13 @@ var PGSGThreeViewer = class {
622
1037
  }
623
1038
  initRenderer() {
624
1039
  if (this.renderer) return;
625
- this.renderer = new THREE3.WebGLRenderer({
1040
+ this.renderer = new THREE4.WebGLRenderer({
626
1041
  antialias: this.options.renderer?.antialias ?? false,
627
1042
  alpha: false,
628
1043
  powerPreference: "high-performance"
629
1044
  });
630
1045
  this.renderer.setClearColor(1118481, 1);
631
- this.renderer.outputColorSpace = THREE3.SRGBColorSpace;
1046
+ this.renderer.outputColorSpace = THREE4.SRGBColorSpace;
632
1047
  this.renderer.setSize(
633
1048
  this.container.clientWidth,
634
1049
  this.container.clientHeight
@@ -643,29 +1058,29 @@ var PGSGThreeViewer = class {
643
1058
  });
644
1059
  }
645
1060
  initScene() {
646
- this.scene = new THREE3.Scene();
647
- this.scene.background = new THREE3.Color(1118481);
648
- const gridHelper = new THREE3.GridHelper(10, 10);
649
- const light = new THREE3.HemisphereLight(16777215, 2236962, 1);
1061
+ this.scene = new THREE4.Scene();
1062
+ this.scene.background = new THREE4.Color(1118481);
1063
+ const gridHelper = new THREE4.GridHelper(10, 10);
1064
+ const light = new THREE4.HemisphereLight(16777215, 2236962, 1);
650
1065
  this.scene.add(light);
651
1066
  }
652
1067
  initCamera() {
653
- this.camera = new THREE3.PerspectiveCamera(
1068
+ this.camera = new THREE4.PerspectiveCamera(
654
1069
  this.options.camera?.fov ?? 60,
655
1070
  this.container.clientWidth / this.container.clientHeight,
656
1071
  this.options.camera?.near ?? 0.01,
657
1072
  this.options.camera?.far ?? 1e4
658
1073
  );
659
- this.camera.position.set(0, 1.6, 3);
660
- this.camera.lookAt(0, 1.6, 0);
1074
+ this.camera.position.set(2, 2, 5);
1075
+ this.camera.lookAt(0, 0, 0);
661
1076
  }
662
1077
  initJoystick() {
663
1078
  const zone = document.createElement("div");
664
1079
  zone.style.position = "absolute";
665
1080
  zone.style.left = "20px";
666
1081
  zone.style.bottom = "20px";
667
- zone.style.width = "1000px";
668
- zone.style.height = "1000px";
1082
+ zone.style.width = "150px";
1083
+ zone.style.height = "150px";
669
1084
  zone.style.zIndex = "1000";
670
1085
  this.container.appendChild(zone);
671
1086
  const joystick = nipplejs.create({
@@ -677,13 +1092,7 @@ var PGSGThreeViewer = class {
677
1092
  joystick.on("move", (_, data) => {
678
1093
  const angle = data.angle.radian;
679
1094
  const force = data.force;
680
- if (this.controls instanceof WalkCapsuleController) {
681
- this.controls.setMovementVector(
682
- Math.sin(angle) * force,
683
- Math.cos(angle) * force
684
- );
685
- }
686
- if (this.controls instanceof OrbitCameraController) {
1095
+ if (this.controls instanceof WalkCapsuleController || this.controls instanceof OrbitCameraController) {
687
1096
  this.controls.setMovementVector(
688
1097
  Math.sin(angle) * force,
689
1098
  Math.cos(angle) * force
@@ -691,35 +1100,12 @@ var PGSGThreeViewer = class {
691
1100
  }
692
1101
  });
693
1102
  joystick.on("end", () => {
694
- this.controls.setMovementVector(0, 0);
1103
+ if (this.controls instanceof WalkCapsuleController || this.controls instanceof OrbitCameraController) {
1104
+ this.controls.setMovementVector(0, 0);
1105
+ }
695
1106
  });
696
1107
  }
697
1108
  initModeToggle() {
698
- const btn = document.createElement("div");
699
- btn.style.position = "absolute";
700
- btn.style.right = "20px";
701
- btn.style.bottom = "20px";
702
- btn.style.width = "70px";
703
- btn.style.height = "70px";
704
- btn.style.borderRadius = "50%";
705
- btn.style.background = "rgba(255,255,255,0.15)";
706
- btn.style.backdropFilter = "blur(6px)";
707
- btn.style.border = "2px solid rgba(255,255,255,0.4)";
708
- btn.style.display = "flex";
709
- btn.style.alignItems = "center";
710
- btn.style.justifyContent = "center";
711
- btn.style.color = "white";
712
- btn.style.fontWeight = "bold";
713
- btn.style.fontSize = "14px";
714
- btn.style.zIndex = "1000";
715
- btn.style.userSelect = "none";
716
- btn.style.cursor = "pointer";
717
- btn.innerText = "ORBIT";
718
- btn.addEventListener("click", () => {
719
- this.toggleCameraMode();
720
- });
721
- this.container.appendChild(btn);
722
- this.modeButton = btn;
723
1109
  }
724
1110
  updateModeLabel(mode) {
725
1111
  if (!this.modeButton) return;
@@ -731,6 +1117,7 @@ var PGSGThreeViewer = class {
731
1117
  this.resizeObserver.observe(this.container);
732
1118
  }
733
1119
  async loadCollider(url) {
1120
+ console.log("[PGSG Viewer] Loading collider:", url);
734
1121
  const loader = new GLTFLoader();
735
1122
  const gltf = await loader.loadAsync(url);
736
1123
  this.colliderMesh = gltf.scene;
@@ -738,11 +1125,15 @@ var PGSGThreeViewer = class {
738
1125
  this.colliders.length = 0;
739
1126
  this.colliderMesh.traverse((child) => {
740
1127
  if (child.isMesh) {
741
- child.material = new THREE3.MeshBasicMaterial({ visible: false });
1128
+ child.material = new THREE4.MeshBasicMaterial({
1129
+ visible: false,
1130
+ side: THREE4.DoubleSide
1131
+ });
742
1132
  child.updateMatrixWorld(true);
743
1133
  this.colliders.push(child);
744
1134
  }
745
1135
  });
1136
+ console.log("[PGSG Viewer] Colliders loaded:", this.colliders.length);
746
1137
  this.worldRoot.updateMatrixWorld(true);
747
1138
  }
748
1139
  isMobile() {
@@ -756,6 +1147,17 @@ var PGSGThreeViewer = class {
756
1147
  this.switchToWalk();
757
1148
  }
758
1149
  };
1150
+ switchToMeasure() {
1151
+ this.controls?.dispose();
1152
+ this.measurementController = new MeasurementController({
1153
+ dom: this.renderer.domElement,
1154
+ camera: this.camera,
1155
+ scene: this.scene,
1156
+ colliders: this.colliders
1157
+ });
1158
+ this.measurementController.restoreMeasurements(this.persistentMeasurements);
1159
+ this.controls = this.measurementController;
1160
+ }
759
1161
  switchToWalk() {
760
1162
  this.controls?.dispose();
761
1163
  const walk = new WalkCapsuleController({
@@ -774,19 +1176,41 @@ var PGSGThreeViewer = class {
774
1176
  }
775
1177
  switchToOrbit() {
776
1178
  this.controls?.dispose();
777
- const distance = this.camera.position.distanceTo(this.orbitTarget);
1179
+ const distance = 5;
778
1180
  this.controls = new OrbitCameraController(
779
1181
  this.camera,
780
1182
  this.renderer.domElement,
781
1183
  {
782
1184
  target: this.orbitTarget.clone(),
783
- distance: Math.max(distance, 0.5),
1185
+ distance,
784
1186
  usePointerLock: !this.isMobile()
785
1187
  }
786
1188
  );
787
1189
  }
1190
+ getMeasurements() {
1191
+ if (this.measurementController) {
1192
+ return this.measurementController.getMeasurements();
1193
+ }
1194
+ return [];
1195
+ }
1196
+ clearMeasurements() {
1197
+ if (this.measurementController) {
1198
+ this.measurementController.clearAll();
1199
+ this.persistentMeasurements = [];
1200
+ }
1201
+ }
1202
+ finalizePolyline() {
1203
+ if (this.controls instanceof MeasurementController) {
1204
+ this.controls.finalizePolyline();
1205
+ }
1206
+ }
1207
+ deleteMeasurement(index) {
1208
+ if (this.measurementController) {
1209
+ this.measurementController.deleteMeasurement(index);
1210
+ }
1211
+ }
788
1212
  };
789
1213
  export {
790
1214
  PGSGThreeViewer
791
1215
  };
792
- //# sourceMappingURL=pgsg-three-viewer-IJSA555G.js.map
1216
+ //# sourceMappingURL=pgsg-three-viewer-KDDVV5TI.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/WalkCapsuleController.ts","../src/viewers/three/controls/OrbitCameraController.ts","../src/viewers/three/controls/MeasurementController.ts"],"sourcesContent":["import type { PGSGViewerOptions } from \"../../core/types\";\nimport * as THREE from \"three\";\nimport { GLTFLoader } from \"three/examples/jsm/Addons.js\";\nimport nipplejs from \"nipplejs\";\n\nimport { SparkSplatRenderer } from \"../spark/SparkSplatRenderer\";\nimport { WalkCapsuleController } from \"./controls/WalkCapsuleController\";\nimport { OrbitCameraController } from \"./controls/OrbitCameraController\";\nimport { MeasurementController } from \"./controls/MeasurementController\";\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 container: HTMLElement;\n private resizeObserver: ResizeObserver | null = null;\n\n private controls!:\n | WalkCapsuleController\n | OrbitCameraController\n | MeasurementController;\n private running = false;\n\n private orbitTarget = new THREE.Vector3();\n private lastTime = performance.now();\n\n private colliders: THREE.Object3D[] = [];\n private persistentMeasurements: any[] = [];\n private measurementController: MeasurementController | null = null;\n\n private worldRoot = new THREE.Object3D();\n private splatPivot = new THREE.Object3D();\n private colliderMesh?: THREE.Object3D;\n\n private modeButton?: HTMLDivElement;\n\n constructor(private options: PGSGViewerOptions) {\n this.container = options.container;\n this.options = options;\n if (!this.container) {\n throw new Error(\"[PGSGThreeViewer] Container is required\");\n }\n }\n\n async load() {\n this.initRenderer();\n this.initCamera();\n this.initScene();\n\n // Resize\n this.bindResize();\n this.resize();\n\n this.scene.add(this.worldRoot);\n\n this.splats = new SparkSplatRenderer();\n const { mesh, bounds } = await this.splats.load(this.options.source);\n\n this.splatPivot.clear();\n this.splatPivot.add(mesh);\n this.worldRoot.add(this.splatPivot);\n\n this.worldRoot.rotation.x = Math.PI;\n this.worldRoot.position.y -= bounds.min.y;\n this.worldRoot.updateMatrixWorld(true);\n\n if (this.options.colliderSource) {\n await this.loadCollider(this.options.colliderSource);\n }\n\n this.scene.updateMatrixWorld(true);\n\n if (this.isMobile()) {\n this.initJoystick();\n this.initModeToggle();\n this.switchToWalk();\n this.updateModeLabel(\"WALK\");\n\n const walk = this.controls as WalkCapsuleController;\n window.addEventListener(\n \"keydown\",\n (e) => {\n if (e.key === \"g\") {\n walk.enableGyro();\n }\n },\n { once: true },\n );\n } else {\n this.switchToMeasure();\n }\n window.addEventListener(\"keydown\", this.onKeyToggle);\n }\n\n start() {\n if (this.running) return;\n this.running = true;\n this.renderer.setAnimationLoop((time) => {\n const now = performance.now();\n const delta = (now - this.lastTime) / 1000;\n this.lastTime = now;\n\n (this.controls as any)?.update?.(delta);\n\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.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.controls?.dispose();\n this.renderer.dispose();\n }\n\n setCameraMode(mode: \"orbit\" | \"walk\" | \"measure\") {\n console.log(\"[PGSGThreeViewer] setCameraMode:\", mode);\n\n // Save state from old controller if it was measurement\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 }\n break;\n case \"orbit\":\n document.exitPointerLock();\n this.switchToOrbit();\n break;\n case \"walk\":\n this.switchToWalk();\n break;\n default:\n this.switchToMeasure();\n break;\n }\n }\n\n toggleCameraMode() {\n if (this.controls instanceof WalkCapsuleController) {\n this.switchToOrbit();\n this.updateModeLabel(\"ORBIT\");\n } else {\n this.switchToWalk();\n this.updateModeLabel(\"WALK\");\n }\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\n this.renderer.setPixelRatio(\n this.options.renderer?.pixelRatio ?? window.devicePixelRatio,\n );\n\n this.container.appendChild(this.renderer.domElement);\n\n this.renderer.domElement.addEventListener(\"webglcontextlost\", (e) => {\n e.preventDefault();\n console.log(\"[PGSG Viewer] WebGL context lost\");\n });\n }\n\n private initScene() {\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0x111111);\n\n const gridHelper = new THREE.GridHelper(10, 10);\n // this.scene.add(gridHelper);\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\n this.camera.position.set(2, 2, 5);\n this.camera.lookAt(0, 0, 0);\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\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 WalkCapsuleController ||\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 WalkCapsuleController ||\n this.controls instanceof OrbitCameraController\n ) {\n (this.controls as any).setMovementVector(0, 0);\n }\n });\n }\n\n private initModeToggle() {\n // Obsolete - using measurement mode only\n }\n\n private updateModeLabel(mode: \"WALK\" | \"ORBIT\" | \"MEASURE\") {\n if (!this.modeButton) return;\n this.modeButton.innerText = mode;\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 async loadCollider(url: string) {\n console.log(\"[PGSG Viewer] Loading collider:\", url);\n const loader = new GLTFLoader();\n const gltf = await loader.loadAsync(url);\n\n this.colliderMesh = gltf.scene;\n this.worldRoot.add(this.colliderMesh);\n\n this.colliders.length = 0;\n\n this.colliderMesh.traverse((child: any) => {\n if ((child as THREE.Mesh).isMesh) {\n child.material = new THREE.MeshBasicMaterial({\n visible: false,\n side: THREE.DoubleSide,\n });\n child.updateMatrixWorld(true);\n this.colliders.push(child);\n }\n });\n\n console.log(\"[PGSG Viewer] Colliders loaded:\", this.colliders.length);\n this.worldRoot.updateMatrixWorld(true);\n }\n\n private isMobile(): boolean {\n return (\n \"ontouchstart\" in window ||\n navigator.maxTouchPoints > 0 ||\n window.innerWidth < 768\n );\n }\n\n private onKeyToggle = (e: KeyboardEvent) => {\n if (e.key !== \"v\") return;\n\n if (this.controls instanceof WalkCapsuleController) {\n this.switchToOrbit();\n } else {\n this.switchToWalk();\n }\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.colliders,\n });\n // Restore persistent data and re-add to scene\n this.measurementController.restoreMeasurements(this.persistentMeasurements);\n this.controls = this.measurementController;\n }\n\n private switchToWalk() {\n this.controls?.dispose();\n const walk = new WalkCapsuleController({\n camera: this.camera,\n dom: this.renderer.domElement,\n colliders: this.colliders,\n eyeHeight: 1.6,\n radius: 0.35,\n walkSpeed: 2.5,\n runMultiplier: 2.0,\n gravity: 9.8,\n usePointerLock: !this.isMobile(),\n }) as any;\n\n this.controls = walk;\n walk.snapToGround();\n }\n\n private switchToOrbit() {\n this.controls?.dispose();\n\n const distance = 5; // Math.max(this.camera.position.distanceTo(this.orbitTarget), 0.5);\n\n this.controls = new OrbitCameraController(\n this.camera,\n this.renderer.domElement,\n {\n target: this.orbitTarget.clone(),\n distance: distance,\n usePointerLock: !this.isMobile(),\n },\n );\n }\n\n getMeasurements() {\n if (this.measurementController) {\n return this.measurementController.getMeasurements();\n }\n return [];\n }\n\n clearMeasurements() {\n if (this.measurementController) {\n this.measurementController.clearAll();\n this.persistentMeasurements = [];\n }\n }\n\n finalizePolyline() {\n if (this.controls instanceof MeasurementController) {\n this.controls.finalizePolyline();\n }\n }\n\n deleteMeasurement(index: number) {\n if (this.measurementController) {\n this.measurementController.deleteMeasurement(index);\n }\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\";\n\nexport type WalkCapsuleOptions = {\n dom: HTMLElement;\n camera: THREE.PerspectiveCamera;\n colliders: THREE.Object3D[];\n\n eyeHeight?: number;\n radius?: number;\n gravity?: number;\n walkSpeed?: number;\n runMultiplier?: number;\n lookSensitivity?: number;\n usePointerLock?: boolean;\n};\n\nexport class WalkCapsuleController {\n private dom: HTMLElement;\n private camera: THREE.PerspectiveCamera;\n private colliders: THREE.Object3D[];\n\n private eyeHeight: number;\n private radius: number;\n\n private gravity: number;\n private walkSpeed: number;\n private runMultiplier: number;\n private lookSensitivity: number;\n\n private yaw = 0;\n private pitch = 0;\n\n private velocity = new THREE.Vector3();\n private onGround = false;\n private lastGroundY: number | null = null;\n\n private keys = new Set<string>();\n private raycaster = new THREE.Raycaster();\n\n private usePointerLock: boolean = false;\n\n private lastTouchX = 0;\n private lastTouchY = 0;\n private externalMove = new THREE.Vector2(0, 0);\n\n private useGyro = false;\n private gyroAlpha = 0;\n private gyroBeta = 0;\n private gyroGamma = 0;\n\n constructor(opts: WalkCapsuleOptions) {\n this.dom = opts.dom;\n this.camera = opts.camera;\n this.colliders = opts.colliders;\n console.log(\"colliders\", this.colliders.length);\n\n this.eyeHeight = opts.eyeHeight ?? 1.6;\n this.radius = opts.radius ?? 0.35;\n this.gravity = opts.gravity ?? 9.8;\n this.walkSpeed = opts.walkSpeed ?? 2.5;\n this.runMultiplier = opts.runMultiplier ?? 2.0;\n this.lookSensitivity = opts.lookSensitivity ?? 0.0022;\n this.usePointerLock = opts.usePointerLock ?? false;\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 if (this.usePointerLock === false) {\n this.bindTouch();\n } else {\n this.bind();\n }\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 enableGyro() {\n if (typeof DeviceOrientationEvent === \"undefined\") return;\n\n // iOS permission\n if ((DeviceOrientationEvent as any).requestPermission) {\n (DeviceOrientationEvent as any)\n .requestPermission()\n .then((response: string) => {\n if (response === \"granted\") {\n this.startGyro();\n }\n })\n .catch(console.error);\n } else {\n this.startGyro();\n }\n }\n\n // --------------------------------------------------\n // Input binding (SAFE for pointer lock)\n // --------------------------------------------------\n\n private bind() {\n // 🔴 IMPORTANT: pointer lock ONLY on click\n this.dom.addEventListener(\"click\", this.onClick);\n\n window.addEventListener(\"mousemove\", this.onMouseMove);\n window.addEventListener(\"keydown\", this.onKeyDown);\n window.addEventListener(\"keyup\", this.onKeyUp);\n window.addEventListener(\"blur\", this.onBlur);\n\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\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(\"click\", this.onClick);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n window.removeEventListener(\"keydown\", this.onKeyDown);\n window.removeEventListener(\"keyup\", this.onKeyUp);\n window.removeEventListener(\"blur\", this.onBlur);\n }\n\n private unbindTouch() {\n this.dom.removeEventListener(\"touchstart\", this.onTouchStart);\n this.dom.removeEventListener(\"touchmove\", this.onTouchMove);\n }\n\n private onClick = () => {\n if (this.usePointerLock && document.pointerLockElement !== this.dom) {\n this.dom.requestPointerLock();\n }\n };\n\n private onBlur = () => {\n if (this.usePointerLock) {\n document.exitPointerLock();\n }\n this.keys.clear();\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (this.usePointerLock && document.pointerLockElement !== this.dom) return;\n\n this.yaw -= e.movementX * this.lookSensitivity;\n this.pitch -= e.movementY * this.lookSensitivity;\n\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 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 private startGyro() {\n this.useGyro = true;\n\n window.addEventListener(\"deviceorientation\", (e) => {\n if (!e.alpha || !e.beta) return;\n\n this.gyroAlpha = THREE.MathUtils.degToRad(e.alpha);\n this.gyroBeta = THREE.MathUtils.degToRad(e.beta);\n this.gyroGamma = THREE.MathUtils.degToRad(e.gamma ?? 0);\n });\n }\n\n // --------------------------------------------------\n // Update loop\n // --------------------------------------------------\n\n update(dt: number) {\n if (this.useGyro) {\n this.yaw = THREE.MathUtils.lerp(this.yaw, -this.gyroAlpha, 0.1);\n this.pitch = THREE.MathUtils.lerp(\n this.pitch,\n this.gyroBeta - Math.PI / 2,\n 0.1,\n );\n\n this.camera.quaternion.setFromEuler(\n new THREE.Euler(this.pitch, this.yaw, 0, \"YXZ\"),\n );\n }\n if (!Number.isFinite(this.camera.position.y)) return;\n\n dt = Math.min(dt, 0.05);\n\n // Direction vectors (yaw-only)\n const forward = new THREE.Vector3();\n this.camera.getWorldDirection(forward);\n forward.y = 0;\n if (forward.lengthSq() < 0.01) {\n forward.set(0, 0, -1).applyQuaternion(new THREE.Quaternion().setFromEuler(new THREE.Euler(0, this.yaw, 0)));\n }\n forward.normalize();\n\n const right = new THREE.Vector3()\n .crossVectors(forward, new THREE.Vector3(0, 1, 0))\n .normalize();\n\n const wish = new THREE.Vector3();\n\n // Desktop\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 // Mobile\n if (this.externalMove.lengthSq() > 0) {\n wish.addScaledVector(forward, this.externalMove.x);\n wish.addScaledVector(right, this.externalMove.y);\n }\n\n if (wish.lengthSq() > 0) wish.normalize();\n\n // if (this.keys.size > 0) {\n // console.log(\"walking\", [...this.keys]);\n // }\n\n const speed =\n this.walkSpeed * (this.keys.has(\"shift\") ? this.runMultiplier : 1);\n\n this.velocity.x = wish.x * speed;\n this.velocity.z = wish.z * speed;\n\n // gravity (once)\n this.velocity.y -= this.gravity * dt;\n\n // integrate position once\n const nextPos = this.camera.position.clone();\n nextPos.x += this.velocity.x * dt;\n nextPos.z += this.velocity.z * dt;\n nextPos.y += this.velocity.y * dt;\n\n const resolved = this.resolveGroundAndCollisions(nextPos);\n this.camera.position.copy(resolved);\n }\n\n snapToGround() {\n const origin = this.camera.position.clone();\n origin.y += 5;\n\n this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));\n this.raycaster.far = 50;\n\n const hits = this.raycaster.intersectObjects(this.colliders, true);\n\n for (const hit of hits) {\n if (!hit.face) continue;\n\n const normalMatrix = new THREE.Matrix3().getNormalMatrix(\n hit.object.matrixWorld,\n );\n\n const worldNormal = hit.face.normal\n .clone()\n .applyMatrix3(normalMatrix)\n .normalize();\n\n if (worldNormal.y > 0.5) {\n this.lastGroundY = hit.point.y;\n this.camera.position.y = this.lastGroundY + this.eyeHeight;\n this.velocity.y = 0;\n this.onGround = true;\n return;\n }\n }\n\n // fallback if no hit\n this.lastGroundY = this.camera.position.y - this.eyeHeight;\n }\n\n // --------------------------------------------------\n // Collision & Ground\n // --------------------------------------------------\n\n private resolveGroundAndCollisions(desiredCamPos: THREE.Vector3) {\n const camPos = desiredCamPos.clone();\n\n // ----- Ground check (robust) -----\n const maxRayDown = 20.0;\n\n const origin = camPos.clone();\n origin.y += 5.0; // Start higher to catch floors above camera\n\n this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));\n this.raycaster.near = 0;\n this.raycaster.far = maxRayDown;\n\n const hits = this.raycaster.intersectObjects(this.colliders, true);\n\n let groundHit: THREE.Intersection | null = null;\n\n for (const hit of hits) {\n if (!hit.face) continue;\n\n // Convert face normal to world space\n const normalMatrix = new THREE.Matrix3().getNormalMatrix(\n hit.object.matrixWorld,\n );\n\n const worldNormal = hit.face.normal\n .clone()\n .applyMatrix3(normalMatrix)\n .normalize();\n\n // Accept only surfaces that face upward\n if (worldNormal.y > 0.5) {\n groundHit = hit;\n break;\n }\n }\n\n if (groundHit) {\n const groundY = groundHit.point.y;\n const targetY = groundY + this.eyeHeight;\n\n this.lastGroundY = groundY;\n\n const falling = this.velocity.y <= 0;\n const maxSnap = 1.0;\n\n if (falling && camPos.y <= targetY + maxSnap) {\n camPos.y = targetY;\n this.velocity.y = 0;\n this.onGround = true;\n } else {\n this.onGround = false;\n }\n } else {\n if (this.lastGroundY !== null) {\n camPos.y = this.lastGroundY + this.eyeHeight;\n this.velocity.y = 0;\n this.onGround = true;\n } else {\n this.onGround = false;\n }\n }\n\n // ----- Wall push -----\n const center = camPos.clone();\n center.y -= this.eyeHeight * 0.9;\n\n const dirs = [\n new THREE.Vector3(1, 0, 0),\n new THREE.Vector3(-1, 0, 0),\n new THREE.Vector3(0, 0, 1),\n new THREE.Vector3(0, 0, -1),\n ];\n\n for (const d of dirs) {\n this.raycaster.set(center, d);\n this.raycaster.far = this.radius + 0.25;\n\n const wallHits = this.raycaster.intersectObjects(this.colliders, true);\n if (!wallHits.length) continue;\n\n const overlap = this.radius + 0.25 - wallHits[0].distance;\n if (overlap > 0) camPos.addScaledVector(d, -overlap);\n }\n\n // console.log({\n // camY: camPos.y.toFixed(2),\n // footY: footY.toFixed(2),\n // groundHit: hits[0]?.point.y.toFixed(2),\n // onGround: this.onGround,\n // });\n\n return camPos;\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"],"mappings":"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","names":["THREE","THREE","THREE"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PGSGViewer
3
- } from "../chunk-WFONX3TR.js";
3
+ } from "../chunk-RWHLWC6Z.js";
4
4
 
5
5
  // src/react/PGSGCanvas.tsx
6
6
  import * as React from "react";
@@ -25,7 +25,9 @@ function PGSGCanvas({
25
25
  viewerType: "three"
26
26
  });
27
27
  viewRef.current = viewer;
28
- viewer.load();
28
+ viewer.load().then(() => {
29
+ onReady?.(viewer);
30
+ });
29
31
  return () => {
30
32
  viewer.destroy();
31
33
  viewRef.current = null;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/PGSGCanvas.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { PGSGViewer } from \"../viewer/pgsg-viewer\";\n\nexport type PGSGCanvasProps = {\n src: string;\n className?: string;\n onReady?: (viewer: PGSGViewer) => void;\n type?: \"pgsg\" | \"ply\";\n colliderSrc?: string;\n};\n\nexport function PGSGCanvas({\n src,\n type = \"pgsg\",\n className,\n onReady,\n colliderSrc,\n}: PGSGCanvasProps) {\n const ref = React.useRef<HTMLDivElement | null>(null);\n const viewRef = React.useRef<PGSGViewer | null>(null);\n\n React.useEffect(() => {\n if (!ref.current) return;\n\n if (viewRef.current) return;\n\n const viewer = new PGSGViewer({\n container: ref.current,\n source: { type: type, url: src },\n colliderSource: colliderSrc,\n debug: true,\n viewerType: \"three\",\n });\n\n viewRef.current = viewer;\n\n viewer.load();\n\n return () => {\n viewer.destroy();\n viewRef.current = null;\n };\n }, [src]);\n\n return (\n <div\n ref={ref}\n className={className}\n style={{ width: \"100%\", height: \"100%\" }}\n />\n );\n}\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AA8CnB;AAlCG,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,MAAY,aAA8B,IAAI;AACpD,QAAM,UAAgB,aAA0B,IAAI;AAEpD,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,IAAI,QAAS;AAElB,QAAI,QAAQ,QAAS;AAErB,UAAM,SAAS,IAAI,WAAW;AAAA,MAC5B,WAAW,IAAI;AAAA,MACf,QAAQ,EAAE,MAAY,KAAK,IAAI;AAAA,MAC/B,gBAAgB;AAAA,MAChB,OAAO;AAAA,MACP,YAAY;AAAA,IACd,CAAC;AAED,YAAQ,UAAU;AAElB,WAAO,KAAK;AAEZ,WAAO,MAAM;AACX,aAAO,QAAQ;AACf,cAAQ,UAAU;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAAA;AAAA,EACzC;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/react/PGSGCanvas.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { PGSGViewer } from \"../viewer/pgsg-viewer\";\n\nexport type PGSGCanvasProps = {\n src: string;\n className?: string;\n onReady?: (viewer: PGSGViewer) => void;\n type?: \"pgsg\" | \"ply\";\n colliderSrc?: string;\n};\n\nexport function PGSGCanvas({\n src,\n type = \"pgsg\",\n className,\n onReady,\n colliderSrc,\n}: PGSGCanvasProps) {\n const ref = React.useRef<HTMLDivElement | null>(null);\n const viewRef = React.useRef<PGSGViewer | null>(null);\n\n React.useEffect(() => {\n if (!ref.current) return;\n\n if (viewRef.current) return;\n\n const viewer = new PGSGViewer({\n container: ref.current,\n source: { type: type, url: src },\n colliderSource: colliderSrc,\n debug: true,\n viewerType: \"three\",\n });\n\n viewRef.current = viewer;\n\n viewer.load().then(() => {\n onReady?.(viewer);\n });\n\n return () => {\n viewer.destroy();\n viewRef.current = null;\n };\n }, [src]);\n\n return (\n <div\n ref={ref}\n className={className}\n style={{ width: \"100%\", height: \"100%\" }}\n />\n );\n}\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AAgDnB;AApCG,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,MAAY,aAA8B,IAAI;AACpD,QAAM,UAAgB,aAA0B,IAAI;AAEpD,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,IAAI,QAAS;AAElB,QAAI,QAAQ,QAAS;AAErB,UAAM,SAAS,IAAI,WAAW;AAAA,MAC5B,WAAW,IAAI;AAAA,MACf,QAAQ,EAAE,MAAY,KAAK,IAAI;AAAA,MAC/B,gBAAgB;AAAA,MAChB,OAAO;AAAA,MACP,YAAY;AAAA,IACd,CAAC;AAED,YAAQ,UAAU;AAElB,WAAO,KAAK,EAAE,KAAK,MAAM;AACvB,gBAAU,MAAM;AAAA,IAClB,CAAC;AAED,WAAO,MAAM;AACX,aAAO,QAAQ;AACf,cAAQ,UAAU;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAAA;AAAA,EACzC;AAEJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popaya/pgsg-viewer",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/viewer/pgsg-viewer.ts"],"sourcesContent":["import type { ViewerType, PGSGViewerEngine } 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: \"orbit\" | \"walk\") {\n this.engine?.setCameraMode?.(mode);\n }\n}\n"],"mappings":";AAGO,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,MAAwB;AACpC,SAAK,QAAQ,gBAAgB,IAAI;AAAA,EACnC;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/viewers/three/pgsg-three-viewer.ts","../src/viewers/spark/SparkSplatRenderer.ts","../src/viewers/three/controls/WalkCapsuleController.ts","../src/viewers/three/controls/OrbitCameraController.ts"],"sourcesContent":["import type { PGSGViewerOptions } from \"../../core/types\";\nimport * as THREE from \"three\";\nimport { GLTFLoader } from \"three/examples/jsm/Addons.js\";\nimport nipplejs from \"nipplejs\";\n\nimport { SparkSplatRenderer } from \"../spark/SparkSplatRenderer\";\nimport { WalkCapsuleController } from \"./controls/WalkCapsuleController\";\nimport { OrbitCameraController } from \"./controls/OrbitCameraController\";\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 container: HTMLElement;\n private resizeObserver: ResizeObserver | null = null;\n\n private controls!: WalkCapsuleController | OrbitCameraController;\n private running = false;\n\n private orbitTarget = new THREE.Vector3();\n private lastTime = performance.now();\n\n private colliders: THREE.Object3D[] = [];\n\n private worldRoot = new THREE.Object3D();\n private splatPivot = new THREE.Object3D();\n private colliderMesh?: THREE.Object3D;\n\n private modeButton?: HTMLDivElement;\n\n constructor(private options: PGSGViewerOptions) {\n this.container = options.container;\n this.options = options;\n if (!this.container) {\n throw new Error(\"[PGSGThreeViewer] Container is required\");\n }\n }\n\n async load() {\n this.initRenderer();\n this.initCamera();\n this.initScene();\n\n // Resize\n this.bindResize();\n this.resize();\n\n this.scene.add(this.worldRoot);\n\n this.splats = new SparkSplatRenderer();\n const { mesh, bounds } = await this.splats.load(this.options.source);\n\n this.splatPivot.clear();\n this.splatPivot.add(mesh);\n this.worldRoot.add(this.splatPivot);\n\n this.worldRoot.rotation.x = Math.PI;\n this.worldRoot.position.y -= bounds.min.y;\n this.worldRoot.updateMatrixWorld(true);\n\n if (this.options.colliderSource) {\n await this.loadCollider(this.options.colliderSource);\n }\n\n this.scene.updateMatrixWorld(true);\n\n if (this.isMobile()) {\n this.initJoystick();\n this.initModeToggle();\n this.switchToWalk();\n this.updateModeLabel(\"WALK\");\n\n const walk = this.controls as WalkCapsuleController;\n walk.enableGyro();\n } else {\n this.switchToOrbit();\n }\n window.addEventListener(\"keydown\", this.onKeyToggle);\n }\n\n start() {\n if (this.running) return;\n this.running = true;\n this.renderer.setAnimationLoop((time) => {\n const now = performance.now();\n const delta = (now - this.lastTime) / 1000;\n this.lastTime = now;\n\n (this.controls as any)?.update?.(delta);\n\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.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.controls?.dispose();\n this.renderer.dispose();\n }\n\n setCameraMode(mode: \"orbit\" | \"walk\") {\n if (mode === \"orbit\") {\n document.exitPointerLock();\n }\n\n if (mode === \"walk\") {\n // FORCE pointer lock NOW (gesture already happened)\n setTimeout(() => {\n if (document.pointerLockElement !== this.renderer.domElement) {\n this.renderer.domElement.focus();\n this.renderer.domElement.requestPointerLock();\n }\n }, 0);\n }\n }\n\n toggleCameraMode() {\n if (this.controls instanceof WalkCapsuleController) {\n this.switchToOrbit();\n this.updateModeLabel(\"ORBIT\");\n } else {\n this.switchToWalk();\n this.updateModeLabel(\"WALK\");\n }\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\n this.renderer.setPixelRatio(\n this.options.renderer?.pixelRatio ?? window.devicePixelRatio,\n );\n\n this.container.appendChild(this.renderer.domElement);\n\n this.renderer.domElement.addEventListener(\"webglcontextlost\", (e) => {\n e.preventDefault();\n console.log(\"[PGSG Viewer] WebGL context lost\");\n });\n }\n\n private initScene() {\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0x111111);\n\n const gridHelper = new THREE.GridHelper(10, 10);\n // this.scene.add(gridHelper);\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\n this.camera.position.set(0, 1.6, 3);\n this.camera.lookAt(0, 1.6, 0);\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 = \"1000px\";\n zone.style.height = \"1000px\";\n zone.style.zIndex = \"1000\";\n\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 (this.controls instanceof WalkCapsuleController) {\n this.controls.setMovementVector(\n Math.sin(angle) * force,\n Math.cos(angle) * force,\n );\n }\n\n if (this.controls instanceof OrbitCameraController) {\n this.controls.setMovementVector(\n Math.sin(angle) * force,\n Math.cos(angle) * force,\n );\n }\n });\n\n joystick.on(\"end\", () => {\n this.controls.setMovementVector(0, 0);\n });\n }\n\n private initModeToggle() {\n const btn = document.createElement(\"div\");\n\n btn.style.position = \"absolute\";\n btn.style.right = \"20px\";\n btn.style.bottom = \"20px\";\n btn.style.width = \"70px\";\n btn.style.height = \"70px\";\n btn.style.borderRadius = \"50%\";\n btn.style.background = \"rgba(255,255,255,0.15)\";\n btn.style.backdropFilter = \"blur(6px)\";\n btn.style.border = \"2px solid rgba(255,255,255,0.4)\";\n btn.style.display = \"flex\";\n btn.style.alignItems = \"center\";\n btn.style.justifyContent = \"center\";\n btn.style.color = \"white\";\n btn.style.fontWeight = \"bold\";\n btn.style.fontSize = \"14px\";\n btn.style.zIndex = \"1000\";\n btn.style.userSelect = \"none\";\n btn.style.cursor = \"pointer\";\n btn.innerText = \"ORBIT\";\n\n btn.addEventListener(\"click\", () => {\n this.toggleCameraMode();\n });\n\n this.container.appendChild(btn);\n this.modeButton = btn;\n }\n\n private updateModeLabel(mode: \"WALK\" | \"ORBIT\") {\n if (!this.modeButton) return;\n this.modeButton.innerText = mode;\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 async loadCollider(url: string) {\n const loader = new GLTFLoader();\n const gltf = await loader.loadAsync(url);\n\n this.colliderMesh = gltf.scene;\n this.worldRoot.add(this.colliderMesh);\n\n this.colliders.length = 0;\n\n this.colliderMesh.traverse((child: any) => {\n if ((child as THREE.Mesh).isMesh) {\n child.material = new THREE.MeshBasicMaterial({ visible: false });\n child.updateMatrixWorld(true);\n this.colliders.push(child);\n }\n });\n\n this.worldRoot.updateMatrixWorld(true);\n }\n\n private isMobile(): boolean {\n return (\n \"ontouchstart\" in window ||\n navigator.maxTouchPoints > 0 ||\n window.innerWidth < 768\n );\n }\n\n private onKeyToggle = (e: KeyboardEvent) => {\n if (e.key !== \"v\") return;\n\n if (this.controls instanceof WalkCapsuleController) {\n this.switchToOrbit();\n } else {\n this.switchToWalk();\n }\n };\n\n private switchToWalk() {\n this.controls?.dispose();\n const walk = new WalkCapsuleController({\n camera: this.camera,\n dom: this.renderer.domElement,\n colliders: this.colliders,\n eyeHeight: 1.6,\n radius: 0.35,\n walkSpeed: 2.5,\n runMultiplier: 2.0,\n gravity: 9.8,\n usePointerLock: !this.isMobile(),\n }) as any;\n\n this.controls = walk;\n walk.snapToGround();\n }\n\n private switchToOrbit() {\n this.controls?.dispose();\n\n const distance = this.camera.position.distanceTo(this.orbitTarget);\n\n this.controls = new OrbitCameraController(\n this.camera,\n this.renderer.domElement,\n {\n target: this.orbitTarget.clone(),\n distance: Math.max(distance, 0.5),\n usePointerLock: !this.isMobile(),\n },\n );\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\";\n\nexport type WalkCapsuleOptions = {\n dom: HTMLElement;\n camera: THREE.PerspectiveCamera;\n colliders: THREE.Object3D[];\n\n eyeHeight?: number;\n radius?: number;\n gravity?: number;\n walkSpeed?: number;\n runMultiplier?: number;\n lookSensitivity?: number;\n usePointerLock?: boolean;\n};\n\nexport class WalkCapsuleController {\n private dom: HTMLElement;\n private camera: THREE.PerspectiveCamera;\n private colliders: THREE.Object3D[];\n\n private eyeHeight: number;\n private radius: number;\n\n private gravity: number;\n private walkSpeed: number;\n private runMultiplier: number;\n private lookSensitivity: number;\n\n private yaw = 0;\n private pitch = 0;\n\n private velocity = new THREE.Vector3();\n private onGround = false;\n private lastGroundY: number | null = null;\n\n private keys = new Set<string>();\n private raycaster = new THREE.Raycaster();\n\n private usePointerLock: boolean = false;\n\n private lastTouchX = 0;\n private lastTouchY = 0;\n private externalMove = new THREE.Vector2(0, 0);\n\n private useGyro = false;\n private gyroAlpha = 0;\n private gyroBeta = 0;\n private gyroGamma = 0;\n\n constructor(opts: WalkCapsuleOptions) {\n this.dom = opts.dom;\n this.camera = opts.camera;\n this.colliders = opts.colliders;\n console.log(\"colliders\", this.colliders.length);\n\n this.eyeHeight = opts.eyeHeight ?? 1.6;\n this.radius = opts.radius ?? 0.35;\n this.gravity = opts.gravity ?? 9.8;\n this.walkSpeed = opts.walkSpeed ?? 2.5;\n this.runMultiplier = opts.runMultiplier ?? 2.0;\n this.lookSensitivity = opts.lookSensitivity ?? 0.0022;\n this.usePointerLock = opts.usePointerLock ?? false;\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 if (this.usePointerLock === false) {\n this.bindTouch();\n } else {\n this.bind();\n }\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 enableGyro() {\n if (typeof DeviceOrientationEvent === \"undefined\") return;\n\n // iOS permission\n if ((DeviceOrientationEvent as any).requestPermission) {\n (DeviceOrientationEvent as any)\n .requestPermission()\n .then((response: string) => {\n if (response === \"granted\") {\n this.startGyro();\n }\n })\n .catch(console.error);\n } else {\n this.startGyro();\n }\n }\n\n // --------------------------------------------------\n // Input binding (SAFE for pointer lock)\n // --------------------------------------------------\n\n private bind() {\n // 🔴 IMPORTANT: pointer lock ONLY on click\n this.dom.addEventListener(\"click\", this.onClick);\n\n window.addEventListener(\"mousemove\", this.onMouseMove);\n window.addEventListener(\"keydown\", this.onKeyDown);\n window.addEventListener(\"keyup\", this.onKeyUp);\n window.addEventListener(\"blur\", this.onBlur);\n\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\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(\"click\", this.onClick);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n window.removeEventListener(\"keydown\", this.onKeyDown);\n window.removeEventListener(\"keyup\", this.onKeyUp);\n window.removeEventListener(\"blur\", this.onBlur);\n }\n\n private unbindTouch() {\n this.dom.removeEventListener(\"touchstart\", this.onTouchStart);\n this.dom.removeEventListener(\"touchmove\", this.onTouchMove);\n }\n\n private onClick = () => {\n if (this.usePointerLock && document.pointerLockElement !== this.dom) {\n this.dom.requestPointerLock();\n }\n };\n\n private onBlur = () => {\n if (this.usePointerLock) {\n document.exitPointerLock();\n }\n this.keys.clear();\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (this.usePointerLock && document.pointerLockElement !== this.dom) return;\n\n this.yaw -= e.movementX * this.lookSensitivity;\n this.pitch -= e.movementY * this.lookSensitivity;\n\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 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 private startGyro() {\n this.useGyro = true;\n\n window.addEventListener(\"deviceorientation\", (e) => {\n if (!e.alpha || !e.beta) return;\n\n this.gyroAlpha = THREE.MathUtils.degToRad(e.alpha);\n this.gyroBeta = THREE.MathUtils.degToRad(e.beta);\n this.gyroGamma = THREE.MathUtils.degToRad(e.gamma ?? 0);\n });\n }\n\n // --------------------------------------------------\n // Update loop\n // --------------------------------------------------\n\n update(dt: number) {\n if (this.useGyro) {\n this.yaw = THREE.MathUtils.lerp(this.yaw, -this.gyroAlpha, 0.1);\n this.pitch = THREE.MathUtils.lerp(\n this.pitch,\n this.gyroBeta - Math.PI / 2,\n 0.1,\n );\n\n this.camera.quaternion.setFromEuler(\n new THREE.Euler(this.pitch, this.yaw, 0, \"YXZ\"),\n );\n }\n if (!Number.isFinite(this.camera.position.y)) return;\n\n dt = Math.min(dt, 0.05);\n\n // Direction vectors (yaw-only)\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 wish = new THREE.Vector3();\n\n // Desktop\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 // Mobile\n if (this.externalMove.lengthSq() > 0) {\n wish.addScaledVector(forward, this.externalMove.x);\n wish.addScaledVector(right, this.externalMove.y);\n }\n\n if (wish.lengthSq() > 0) wish.normalize();\n\n // if (this.keys.size > 0) {\n // console.log(\"walking\", [...this.keys]);\n // }\n\n const speed =\n this.walkSpeed * (this.keys.has(\"shift\") ? this.runMultiplier : 1);\n\n this.velocity.x = wish.x * speed;\n this.velocity.z = wish.z * speed;\n\n // gravity (once)\n this.velocity.y -= this.gravity * dt;\n\n // integrate position once\n const nextPos = this.camera.position.clone();\n nextPos.x += this.velocity.x * dt;\n nextPos.z += this.velocity.z * dt;\n nextPos.y += this.velocity.y * dt;\n\n const resolved = this.resolveGroundAndCollisions(nextPos);\n this.camera.position.copy(resolved);\n }\n\n snapToGround() {\n const origin = this.camera.position.clone();\n origin.y += 5;\n\n this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));\n this.raycaster.far = 50;\n\n const hits = this.raycaster.intersectObjects(this.colliders, true);\n\n for (const hit of hits) {\n if (!hit.face) continue;\n\n const normalMatrix = new THREE.Matrix3().getNormalMatrix(\n hit.object.matrixWorld,\n );\n\n const worldNormal = hit.face.normal\n .clone()\n .applyMatrix3(normalMatrix)\n .normalize();\n\n if (worldNormal.y > 0.5) {\n this.lastGroundY = hit.point.y;\n this.camera.position.y = this.lastGroundY + this.eyeHeight;\n this.velocity.y = 0;\n this.onGround = true;\n return;\n }\n }\n\n // fallback if no hit\n this.lastGroundY = this.camera.position.y - this.eyeHeight;\n }\n\n // --------------------------------------------------\n // Collision & Ground\n // --------------------------------------------------\n\n private resolveGroundAndCollisions(desiredCamPos: THREE.Vector3) {\n const camPos = desiredCamPos.clone();\n\n // ----- Ground check (robust) -----\n const maxRayDown = 10.0;\n\n const origin = camPos.clone();\n origin.y += 2.0;\n\n this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));\n this.raycaster.near = 0;\n this.raycaster.far = maxRayDown;\n\n const hits = this.raycaster.intersectObjects(this.colliders, true);\n\n let groundHit: THREE.Intersection | null = null;\n\n for (const hit of hits) {\n if (!hit.face) continue;\n\n // Convert face normal to world space\n const normalMatrix = new THREE.Matrix3().getNormalMatrix(\n hit.object.matrixWorld,\n );\n\n const worldNormal = hit.face.normal\n .clone()\n .applyMatrix3(normalMatrix)\n .normalize();\n\n // Accept only surfaces that face upward\n if (worldNormal.y > 0.5) {\n groundHit = hit;\n break;\n }\n }\n\n if (groundHit) {\n const groundY = groundHit.point.y;\n const targetY = groundY + this.eyeHeight;\n\n this.lastGroundY = groundY;\n\n const falling = this.velocity.y <= 0;\n const maxSnap = 1.0;\n\n if (falling && camPos.y <= targetY + maxSnap) {\n camPos.y = targetY;\n this.velocity.y = 0;\n this.onGround = true;\n } else {\n this.onGround = false;\n }\n } else {\n if (this.lastGroundY !== null) {\n camPos.y = this.lastGroundY + this.eyeHeight;\n this.velocity.y = 0;\n this.onGround = true;\n } else {\n this.onGround = false;\n }\n }\n\n // ----- Wall push -----\n const center = camPos.clone();\n center.y -= this.eyeHeight * 0.9;\n\n const dirs = [\n new THREE.Vector3(1, 0, 0),\n new THREE.Vector3(-1, 0, 0),\n new THREE.Vector3(0, 0, 1),\n new THREE.Vector3(0, 0, -1),\n ];\n\n for (const d of dirs) {\n this.raycaster.set(center, d);\n this.raycaster.far = this.radius + 0.25;\n\n const wallHits = this.raycaster.intersectObjects(this.colliders, true);\n if (!wallHits.length) continue;\n\n const overlap = this.radius + 0.25 - wallHits[0].distance;\n if (overlap > 0) camPos.addScaledVector(d, -overlap);\n }\n\n // console.log({\n // camY: camPos.y.toFixed(2),\n // footY: footY.toFixed(2),\n // groundHit: hits[0]?.point.y.toFixed(2),\n // onGround: this.onGround,\n // });\n\n return camPos;\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"],"mappings":";AACA,YAAYA,YAAW;AACvB,SAAS,kBAAkB;AAC3B,OAAO,cAAc;;;ACHrB,SAAS,iBAAiB;AAKnB,IAAM,qBAAN,MAAyB;AAAA,EACtB;AAAA,EAER,MAAM,KAAK,QAAsB;AAC/B,QAAI,OAAO,SAAS,SAAS,OAAO,SAAS,QAAQ;AACnD,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AAQA,SAAK,OAAO,IAAI,UAAU;AAAA,MACxB,KAAK,OAAO;AAAA,IACd,CAAC;AAED,UAAM,KAAK,KAAK;AAChB,SAAK,KAAK,kBAAkB,IAAI;AAEhC,UAAM,MAAM,KAAK,KAAK,eAAe,IAAI;AAEzC,UAAM,MAAM,IAAI,IAAI,MAAM;AAC1B,UAAM,MAAM,IAAI,IAAI,MAAM;AAG1B,SAAK,KAAK,SAAS,IAAI,GAAG,GAAG,CAAC;AAC9B,SAAK,KAAK,WAAW,SAAS;AAE9B,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAW,OAAoB;AAC7B,UAAM,IAAI,KAAK,IAAI;AAAA,EACrB;AAAA,EAEA,OAAO,IAAY;AAAA,EAEnB;AAAA,EAEA,QAAQ,OAAoB;AAC1B,UAAM,OAAO,KAAK,IAAI;AACtB,SAAK,KAAK,UAAU;AAAA,EACtB;AACF;;;ACxDA,YAAY,WAAW;AAgBhB,IAAM,wBAAN,MAA4B;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,MAAM;AAAA,EACN,QAAQ;AAAA,EAER,WAAW,IAAU,cAAQ;AAAA,EAC7B,WAAW;AAAA,EACX,cAA6B;AAAA,EAE7B,OAAO,oBAAI,IAAY;AAAA,EACvB,YAAY,IAAU,gBAAU;AAAA,EAEhC,iBAA0B;AAAA,EAE1B,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe,IAAU,cAAQ,GAAG,CAAC;AAAA,EAErC,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EAEpB,YAAY,MAA0B;AACpC,SAAK,MAAM,KAAK;AAChB,SAAK,SAAS,KAAK;AACnB,SAAK,YAAY,KAAK;AACtB,YAAQ,IAAI,aAAa,KAAK,UAAU,MAAM;AAE9C,SAAK,YAAY,KAAK,aAAa;AACnC,SAAK,SAAS,KAAK,UAAU;AAC7B,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,YAAY,KAAK,aAAa;AACnC,SAAK,gBAAgB,KAAK,iBAAiB;AAC3C,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,SAAK,iBAAiB,KAAK,kBAAkB;AAG7C,UAAM,QAAQ,IAAU,YAAM,EAAE;AAAA,MAC9B,KAAK,OAAO;AAAA,MACZ;AAAA,IACF;AACA,SAAK,QAAQ,MAAM;AACnB,SAAK,MAAM,MAAM;AAEjB,QAAI,KAAK,mBAAmB,OAAO;AACjC,WAAK,UAAU;AAAA,IACjB,OAAO;AACL,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,mBAAmB,OAAO;AACjC,WAAK,YAAY;AAAA,IACnB,OAAO;AACL,WAAK,OAAO;AACZ,UAAI,KAAK,eAAgB,UAAS,gBAAgB;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,kBAAkB,GAAW,GAAW;AACtC,SAAK,aAAa,IAAI,GAAG,CAAC;AAAA,EAC5B;AAAA,EAEA,aAAa;AACX,QAAI,OAAO,2BAA2B,YAAa;AAGnD,QAAK,uBAA+B,mBAAmB;AACrD,MAAC,uBACE,kBAAkB,EAClB,KAAK,CAAC,aAAqB;AAC1B,YAAI,aAAa,WAAW;AAC1B,eAAK,UAAU;AAAA,QACjB;AAAA,MACF,CAAC,EACA,MAAM,QAAQ,KAAK;AAAA,IACxB,OAAO;AACL,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,OAAO;AAEb,SAAK,IAAI,iBAAiB,SAAS,KAAK,OAAO;AAE/C,WAAO,iBAAiB,aAAa,KAAK,WAAW;AACrD,WAAO,iBAAiB,WAAW,KAAK,SAAS;AACjD,WAAO,iBAAiB,SAAS,KAAK,OAAO;AAC7C,WAAO,iBAAiB,QAAQ,KAAK,MAAM;AAE3C,SAAK,IAAI,iBAAiB,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC;AAAA,EACpE;AAAA,EAEQ,YAAY;AAClB,SAAK,IAAI,iBAAiB,cAAc,KAAK,YAAY;AACzD,SAAK,IAAI,iBAAiB,aAAa,KAAK,WAAW;AAAA,EACzD;AAAA,EAEQ,SAAS;AACf,SAAK,IAAI,oBAAoB,SAAS,KAAK,OAAO;AAClD,WAAO,oBAAoB,aAAa,KAAK,WAAW;AACxD,WAAO,oBAAoB,WAAW,KAAK,SAAS;AACpD,WAAO,oBAAoB,SAAS,KAAK,OAAO;AAChD,WAAO,oBAAoB,QAAQ,KAAK,MAAM;AAAA,EAChD;AAAA,EAEQ,cAAc;AACpB,SAAK,IAAI,oBAAoB,cAAc,KAAK,YAAY;AAC5D,SAAK,IAAI,oBAAoB,aAAa,KAAK,WAAW;AAAA,EAC5D;AAAA,EAEQ,UAAU,MAAM;AACtB,QAAI,KAAK,kBAAkB,SAAS,uBAAuB,KAAK,KAAK;AACnE,WAAK,IAAI,mBAAmB;AAAA,IAC9B;AAAA,EACF;AAAA,EAEQ,SAAS,MAAM;AACrB,QAAI,KAAK,gBAAgB;AACvB,eAAS,gBAAgB;AAAA,IAC3B;AACA,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,QAAI,KAAK,kBAAkB,SAAS,uBAAuB,KAAK,IAAK;AAErE,SAAK,OAAO,EAAE,YAAY,KAAK;AAC/B,SAAK,SAAS,EAAE,YAAY,KAAK;AAEjC,SAAK,QAAc,gBAAU;AAAA,MAC3B,KAAK;AAAA,MACL,CAAC,KAAK,KAAK,IAAI;AAAA,MACf,KAAK,KAAK,IAAI;AAAA,IAChB;AAEA,SAAK,OAAO,WAAW;AAAA,MACrB,IAAU,YAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,YAAY,CAAC,MAAqB;AACxC,SAAK,KAAK,IAAI,EAAE,IAAI,YAAY,CAAC;AAAA,EACnC;AAAA,EAEQ,UAAU,CAAC,MAAqB;AACtC,SAAK,KAAK,OAAO,EAAE,IAAI,YAAY,CAAC;AAAA,EACtC;AAAA,EAEQ,eAAe,CAAC,MAAkB;AACxC,UAAM,IAAI,EAAE,QAAQ,CAAC;AACrB,SAAK,aAAa,EAAE;AACpB,SAAK,aAAa,EAAE;AAAA,EACtB;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,UAAM,IAAI,EAAE,QAAQ,CAAC;AAErB,UAAM,SAAS,EAAE,UAAU,KAAK;AAChC,UAAM,SAAS,EAAE,UAAU,KAAK;AAEhC,SAAK,aAAa,EAAE;AACpB,SAAK,aAAa,EAAE;AAEpB,SAAK,OAAO,SAAS;AACrB,SAAK,SAAS,SAAS;AAEvB,SAAK,QAAQ,KAAK,IAAI,CAAC,KAAK,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,GAAG,KAAK,KAAK,CAAC;AACrE,SAAK,OAAO,WAAW;AAAA,MACrB,IAAU,YAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,YAAY;AAClB,SAAK,UAAU;AAEf,WAAO,iBAAiB,qBAAqB,CAAC,MAAM;AAClD,UAAI,CAAC,EAAE,SAAS,CAAC,EAAE,KAAM;AAEzB,WAAK,YAAkB,gBAAU,SAAS,EAAE,KAAK;AACjD,WAAK,WAAiB,gBAAU,SAAS,EAAE,IAAI;AAC/C,WAAK,YAAkB,gBAAU,SAAS,EAAE,SAAS,CAAC;AAAA,IACxD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,IAAY;AACjB,QAAI,KAAK,SAAS;AAChB,WAAK,MAAY,gBAAU,KAAK,KAAK,KAAK,CAAC,KAAK,WAAW,GAAG;AAC9D,WAAK,QAAc,gBAAU;AAAA,QAC3B,KAAK;AAAA,QACL,KAAK,WAAW,KAAK,KAAK;AAAA,QAC1B;AAAA,MACF;AAEA,WAAK,OAAO,WAAW;AAAA,QACrB,IAAU,YAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK;AAAA,MAChD;AAAA,IACF;AACA,QAAI,CAAC,OAAO,SAAS,KAAK,OAAO,SAAS,CAAC,EAAG;AAE9C,SAAK,KAAK,IAAI,IAAI,IAAI;AAGtB,UAAM,UAAU,IAAU,cAAQ;AAClC,SAAK,OAAO,kBAAkB,OAAO;AACrC,YAAQ,IAAI;AACZ,YAAQ,UAAU;AAElB,UAAM,QAAQ,IAAU,cAAQ,EAC7B,aAAa,SAAS,IAAU,cAAQ,GAAG,GAAG,CAAC,CAAC,EAChD,UAAU;AAEb,UAAM,OAAO,IAAU,cAAQ;AAG/B,QAAI,KAAK,KAAK,IAAI,GAAG,EAAG,MAAK,IAAI,OAAO;AACxC,QAAI,KAAK,KAAK,IAAI,GAAG,EAAG,MAAK,IAAI,OAAO;AACxC,QAAI,KAAK,KAAK,IAAI,GAAG,EAAG,MAAK,IAAI,KAAK;AACtC,QAAI,KAAK,KAAK,IAAI,GAAG,EAAG,MAAK,IAAI,KAAK;AAGtC,QAAI,KAAK,aAAa,SAAS,IAAI,GAAG;AACpC,WAAK,gBAAgB,SAAS,KAAK,aAAa,CAAC;AACjD,WAAK,gBAAgB,OAAO,KAAK,aAAa,CAAC;AAAA,IACjD;AAEA,QAAI,KAAK,SAAS,IAAI,EAAG,MAAK,UAAU;AAMxC,UAAM,QACJ,KAAK,aAAa,KAAK,KAAK,IAAI,OAAO,IAAI,KAAK,gBAAgB;AAElE,SAAK,SAAS,IAAI,KAAK,IAAI;AAC3B,SAAK,SAAS,IAAI,KAAK,IAAI;AAG3B,SAAK,SAAS,KAAK,KAAK,UAAU;AAGlC,UAAM,UAAU,KAAK,OAAO,SAAS,MAAM;AAC3C,YAAQ,KAAK,KAAK,SAAS,IAAI;AAC/B,YAAQ,KAAK,KAAK,SAAS,IAAI;AAC/B,YAAQ,KAAK,KAAK,SAAS,IAAI;AAE/B,UAAM,WAAW,KAAK,2BAA2B,OAAO;AACxD,SAAK,OAAO,SAAS,KAAK,QAAQ;AAAA,EACpC;AAAA,EAEA,eAAe;AACb,UAAM,SAAS,KAAK,OAAO,SAAS,MAAM;AAC1C,WAAO,KAAK;AAEZ,SAAK,UAAU,IAAI,QAAQ,IAAU,cAAQ,GAAG,IAAI,CAAC,CAAC;AACtD,SAAK,UAAU,MAAM;AAErB,UAAM,OAAO,KAAK,UAAU,iBAAiB,KAAK,WAAW,IAAI;AAEjE,eAAW,OAAO,MAAM;AACtB,UAAI,CAAC,IAAI,KAAM;AAEf,YAAM,eAAe,IAAU,cAAQ,EAAE;AAAA,QACvC,IAAI,OAAO;AAAA,MACb;AAEA,YAAM,cAAc,IAAI,KAAK,OAC1B,MAAM,EACN,aAAa,YAAY,EACzB,UAAU;AAEb,UAAI,YAAY,IAAI,KAAK;AACvB,aAAK,cAAc,IAAI,MAAM;AAC7B,aAAK,OAAO,SAAS,IAAI,KAAK,cAAc,KAAK;AACjD,aAAK,SAAS,IAAI;AAClB,aAAK,WAAW;AAChB;AAAA,MACF;AAAA,IACF;AAGA,SAAK,cAAc,KAAK,OAAO,SAAS,IAAI,KAAK;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAMQ,2BAA2B,eAA8B;AAC/D,UAAM,SAAS,cAAc,MAAM;AAGnC,UAAM,aAAa;AAEnB,UAAM,SAAS,OAAO,MAAM;AAC5B,WAAO,KAAK;AAEZ,SAAK,UAAU,IAAI,QAAQ,IAAU,cAAQ,GAAG,IAAI,CAAC,CAAC;AACtD,SAAK,UAAU,OAAO;AACtB,SAAK,UAAU,MAAM;AAErB,UAAM,OAAO,KAAK,UAAU,iBAAiB,KAAK,WAAW,IAAI;AAEjE,QAAI,YAAuC;AAE3C,eAAW,OAAO,MAAM;AACtB,UAAI,CAAC,IAAI,KAAM;AAGf,YAAM,eAAe,IAAU,cAAQ,EAAE;AAAA,QACvC,IAAI,OAAO;AAAA,MACb;AAEA,YAAM,cAAc,IAAI,KAAK,OAC1B,MAAM,EACN,aAAa,YAAY,EACzB,UAAU;AAGb,UAAI,YAAY,IAAI,KAAK;AACvB,oBAAY;AACZ;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW;AACb,YAAM,UAAU,UAAU,MAAM;AAChC,YAAM,UAAU,UAAU,KAAK;AAE/B,WAAK,cAAc;AAEnB,YAAM,UAAU,KAAK,SAAS,KAAK;AACnC,YAAM,UAAU;AAEhB,UAAI,WAAW,OAAO,KAAK,UAAU,SAAS;AAC5C,eAAO,IAAI;AACX,aAAK,SAAS,IAAI;AAClB,aAAK,WAAW;AAAA,MAClB,OAAO;AACL,aAAK,WAAW;AAAA,MAClB;AAAA,IACF,OAAO;AACL,UAAI,KAAK,gBAAgB,MAAM;AAC7B,eAAO,IAAI,KAAK,cAAc,KAAK;AACnC,aAAK,SAAS,IAAI;AAClB,aAAK,WAAW;AAAA,MAClB,OAAO;AACL,aAAK,WAAW;AAAA,MAClB;AAAA,IACF;AAGA,UAAM,SAAS,OAAO,MAAM;AAC5B,WAAO,KAAK,KAAK,YAAY;AAE7B,UAAM,OAAO;AAAA,MACX,IAAU,cAAQ,GAAG,GAAG,CAAC;AAAA,MACzB,IAAU,cAAQ,IAAI,GAAG,CAAC;AAAA,MAC1B,IAAU,cAAQ,GAAG,GAAG,CAAC;AAAA,MACzB,IAAU,cAAQ,GAAG,GAAG,EAAE;AAAA,IAC5B;AAEA,eAAW,KAAK,MAAM;AACpB,WAAK,UAAU,IAAI,QAAQ,CAAC;AAC5B,WAAK,UAAU,MAAM,KAAK,SAAS;AAEnC,YAAM,WAAW,KAAK,UAAU,iBAAiB,KAAK,WAAW,IAAI;AACrE,UAAI,CAAC,SAAS,OAAQ;AAEtB,YAAM,UAAU,KAAK,SAAS,OAAO,SAAS,CAAC,EAAE;AACjD,UAAI,UAAU,EAAG,QAAO,gBAAgB,GAAG,CAAC,OAAO;AAAA,IACrD;AASA,WAAO;AAAA,EACT;AACF;;;ACpaA,YAAYC,YAAW;AAYhB,IAAM,wBAAN,MAA4B;AAAA,EACzB;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,MAAM;AAAA,EACN,QAAQ;AAAA,EAER;AAAA,EACA;AAAA,EAEA,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EAER,UAAU;AAAA,EACV,WAAW;AAAA,EAEX,iBAA2B;AAAA,EAC3B,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe,IAAU,eAAQ,GAAG,CAAC;AAAA,EACrC,YAAoB;AAAA,EAE5B,YACE,QACA,KACA,MACA;AACA,SAAK,SAAS;AACd,SAAK,MAAM;AAEX,SAAK,SAAS,KAAK,OAAO,MAAM;AAChC,SAAK,WAAW,KAAK;AACrB,SAAK,cAAc,KAAK,eAAe;AACvC,SAAK,cAAc,KAAK,eAAe;AAEvC,SAAK,cAAc,KAAK,eAAe;AACvC,SAAK,YAAY,KAAK,aAAa;AACnC,SAAK,iBAAiB,KAAK,kBAAkB;AAE7C,SAAK,eAAe;AACpB,QAAI,KAAK,mBAAmB,OAAO;AACjC,WAAK,UAAU;AAAA,IACjB,OAAO;AACL,WAAK,KAAK;AACV,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,OAAO,IAAY;AACjB,QAAI,KAAK,aAAa,SAAS,MAAM,EAAG;AAExC,UAAM,UAAU,IAAU,eAAQ;AAClC,SAAK,OAAO,kBAAkB,OAAO;AACrC,YAAQ,IAAI;AACZ,YAAQ,UAAU;AAElB,UAAM,QAAQ,IAAU,eAAQ,EAC7B,aAAa,SAAS,IAAU,eAAQ,GAAG,GAAG,CAAC,CAAC,EAChD,UAAU;AAEb,UAAM,OAAO,IAAU,eAAQ,EAC5B,gBAAgB,SAAS,KAAK,aAAa,CAAC,EAC5C,gBAAgB,OAAO,KAAK,aAAa,CAAC;AAE7C,QAAI,KAAK,SAAS,IAAI,EAAG,MAAK,UAAU;AAExC,SAAK,eAAe,KAAK,YAAY,EAAE;AAEvC,SAAK,OAAO,IAAI,IAAI;AAEpB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,mBAAmB,OAAO;AACjC,WAAK,YAAY;AAAA,IACnB,OAAO;AACL,WAAK,OAAO;AACZ,UAAI,KAAK,eAAgB,UAAS,gBAAgB;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,kBAAkB,GAAW,GAAW;AACtC,SAAK,aAAa,IAAI,GAAG,CAAC;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAMQ,OAAO;AACb,SAAK,IAAI,iBAAiB,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC;AAClE,SAAK,IAAI,iBAAiB,aAAa,KAAK,WAAW;AACvD,WAAO,iBAAiB,WAAW,KAAK,SAAS;AACjD,WAAO,iBAAiB,aAAa,KAAK,WAAW;AACrD,SAAK,IAAI,iBAAiB,SAAS,KAAK,SAAS,EAAE,SAAS,MAAM,CAAC;AAAA,EACrE;AAAA,EAEQ,YAAY;AAClB,SAAK,IAAI,iBAAiB,cAAc,KAAK,YAAY;AACzD,SAAK,IAAI,iBAAiB,aAAa,KAAK,WAAW;AAAA,EACzD;AAAA,EAEQ,SAAS;AACf,SAAK,IAAI,oBAAoB,aAAa,KAAK,WAAW;AAC1D,WAAO,oBAAoB,WAAW,KAAK,SAAS;AACpD,WAAO,oBAAoB,aAAa,KAAK,WAAW;AACxD,SAAK,IAAI,oBAAoB,SAAS,KAAK,OAAO;AAAA,EACpD;AAAA,EAEQ,cAAc;AACpB,SAAK,IAAI,oBAAoB,cAAc,KAAK,YAAY;AAC5D,SAAK,IAAI,oBAAoB,aAAa,KAAK,WAAW;AAAA,EAC5D;AAAA,EAEQ,iBAAiB;AACvB,UAAM,SAAS,KAAK,OAAO,SAAS,MAAM,EAAE,IAAI,KAAK,MAAM;AAC3D,SAAK,WAAW,OAAO,OAAO;AAE9B,UAAM,YAAY,IAAU,iBAAU,EAAE,eAAe,MAAM;AAC7D,SAAK,MAAM,UAAU;AACrB,SAAK,QAAQ,UAAU;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,CAAC,MAAkB;AACvC,QAAI,EAAE,WAAW,GAAG;AAElB,WAAK,WAAW;AAAA,IAClB,WAAW,EAAE,WAAW,KAAK,EAAE,WAAW,GAAG;AAE3C,WAAK,UAAU;AAAA,IACjB;AAEA,SAAK,QAAQ,EAAE;AACf,SAAK,QAAQ,EAAE;AAAA,EACjB;AAAA,EAEQ,YAAY,MAAM;AACxB,SAAK,WAAW;AAChB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,QAAI,CAAC,KAAK,YAAY,CAAC,KAAK,QAAS;AAErC,UAAM,KAAK,EAAE,UAAU,KAAK;AAC5B,UAAM,KAAK,EAAE,UAAU,KAAK;AAE5B,SAAK,QAAQ,EAAE;AACf,SAAK,QAAQ,EAAE;AAKf,QAAI,KAAK,UAAU;AACjB,WAAK,OAAO,KAAK,KAAK;AACtB,WAAK,SAAS,KAAK,KAAK;AAExB,WAAK,QAAc,iBAAU,MAAM,KAAK,OAAO,MAAM,KAAK,KAAK,IAAI;AAEnE,WAAK,aAAa;AAClB;AAAA,IACF;AAKA,QAAI,KAAK,SAAS;AAChB,YAAM,WAAW,KAAK,WAAW,KAAK;AAEtC,YAAM,QAAQ,IAAU,eAAQ;AAChC,YAAM,KAAK,IAAU,eAAQ;AAE7B,WAAK,OAAO,kBAAkB,EAAE;AAChC,YAAM,aAAa,KAAK,OAAO,IAAI,EAAE,EAAE,UAAU;AACjD,SAAG;AAAA,QACD;AAAA,QACA,KAAK,OAAO,kBAAkB,IAAU,eAAQ,CAAC;AAAA,MACnD,EAAE,UAAU;AAEZ,YAAM,YAAY,IAAU,eAAQ,EACjC,gBAAgB,OAAO,KAAK,QAAQ,EACpC,gBAAgB,IAAI,CAAC,KAAK,QAAQ;AAErC,WAAK,OAAO,IAAI,SAAS;AACzB,WAAK,OAAO,SAAS,IAAI,SAAS;AAAA,IAGpC;AAAA,EACF;AAAA,EAEQ,UAAU,CAAC,MAAkB;AACnC,MAAE,eAAe;AAEjB,UAAM,aAAa,KAAK,IAAI,KAAK,WAAW,EAAE,SAAS,GAAG;AAC1D,SAAK,YAAY;AAEjB,SAAK,WAAiB,iBAAU;AAAA,MAC9B,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAEA,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,eAAe,CAAC,MAAkB;AACxC,UAAM,IAAI,EAAE,QAAQ,CAAC;AACrB,SAAK,aAAa,EAAE;AACpB,SAAK,aAAa,EAAE;AAAA,EACtB;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,UAAM,IAAI,EAAE,QAAQ,CAAC;AAErB,UAAM,SAAS,EAAE,UAAU,KAAK;AAChC,UAAM,SAAS,EAAE,UAAU,KAAK;AAEhC,SAAK,aAAa,EAAE;AACpB,SAAK,aAAa,EAAE;AAEpB,SAAK,OAAO,SAAS;AACrB,SAAK,SAAS,SAAS;AAEvB,SAAK,QAAQ,KAAK,IAAI,CAAC,KAAK,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,GAAG,KAAK,KAAK,CAAC;AACrE,SAAK,OAAO,WAAW;AAAA,MACrB,IAAU,aAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe;AACrB,UAAM,YAAY,IAAU,iBAAU,KAAK,UAAU,KAAK,OAAO,KAAK,GAAG;AAEzE,UAAM,MAAM,IAAU,eAAQ,EAC3B,iBAAiB,SAAS,EAC1B,IAAI,KAAK,MAAM;AAElB,SAAK,OAAO,SAAS,KAAK,GAAG;AAC7B,SAAK,OAAO,OAAO,KAAK,MAAM;AAAA,EAChC;AAAA,EAEA,UAAU,GAAkB;AAC1B,SAAK,OAAO,KAAK,CAAC;AAClB,SAAK,aAAa;AAAA,EACpB;AACF;;;AHtQO,IAAM,kBAAN,MAAsB;AAAA,EAuB3B,YAAoB,SAA4B;AAA5B;AAClB,SAAK,YAAY,QAAQ;AACzB,SAAK,UAAU;AACf,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAAA,EACF;AAAA,EA5BQ,QAAQ,IAAU,aAAM;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA,iBAAwC;AAAA,EAExC;AAAA,EACA,UAAU;AAAA,EAEV,cAAc,IAAU,eAAQ;AAAA,EAChC,WAAW,YAAY,IAAI;AAAA,EAE3B,YAA8B,CAAC;AAAA,EAE/B,YAAY,IAAU,gBAAS;AAAA,EAC/B,aAAa,IAAU,gBAAS;AAAA,EAChC;AAAA,EAEA;AAAA,EAUR,MAAM,OAAO;AACX,SAAK,aAAa;AAClB,SAAK,WAAW;AAChB,SAAK,UAAU;AAGf,SAAK,WAAW;AAChB,SAAK,OAAO;AAEZ,SAAK,MAAM,IAAI,KAAK,SAAS;AAE7B,SAAK,SAAS,IAAI,mBAAmB;AACrC,UAAM,EAAE,MAAM,OAAO,IAAI,MAAM,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM;AAEnE,SAAK,WAAW,MAAM;AACtB,SAAK,WAAW,IAAI,IAAI;AACxB,SAAK,UAAU,IAAI,KAAK,UAAU;AAElC,SAAK,UAAU,SAAS,IAAI,KAAK;AACjC,SAAK,UAAU,SAAS,KAAK,OAAO,IAAI;AACxC,SAAK,UAAU,kBAAkB,IAAI;AAErC,QAAI,KAAK,QAAQ,gBAAgB;AAC/B,YAAM,KAAK,aAAa,KAAK,QAAQ,cAAc;AAAA,IACrD;AAEA,SAAK,MAAM,kBAAkB,IAAI;AAEjC,QAAI,KAAK,SAAS,GAAG;AACnB,WAAK,aAAa;AAClB,WAAK,eAAe;AACpB,WAAK,aAAa;AAClB,WAAK,gBAAgB,MAAM;AAE3B,YAAM,OAAO,KAAK;AAClB,WAAK,WAAW;AAAA,IAClB,OAAO;AACL,WAAK,cAAc;AAAA,IACrB;AACA,WAAO,iBAAiB,WAAW,KAAK,WAAW;AAAA,EACrD;AAAA,EAEA,QAAQ;AACN,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AACf,SAAK,SAAS,iBAAiB,CAAC,SAAS;AACvC,YAAM,MAAM,YAAY,IAAI;AAC5B,YAAM,SAAS,MAAM,KAAK,YAAY;AACtC,WAAK,WAAW;AAEhB,MAAC,KAAK,UAAkB,SAAS,KAAK;AAEtC,WAAK,SAAS,OAAO,KAAK,OAAO,KAAK,MAAM;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ;AACN,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,SAAS;AACP,QAAI,CAAC,KAAK,YAAY,CAAC,KAAK,OAAQ;AAEpC,UAAM,IAAI,KAAK,IAAI,GAAG,KAAK,UAAU,WAAW;AAChD,UAAM,IAAI,KAAK,IAAI,GAAG,KAAK,UAAU,YAAY;AAEjD,SAAK,SAAS,QAAQ,GAAG,CAAC;AAC1B,SAAK,OAAO,SAAS,IAAI;AACzB,SAAK,OAAO,uBAAuB;AAAA,EACrC;AAAA,EAEA,UAAU;AACR,SAAK,UAAU;AACf,SAAK,SAAS,iBAAiB,IAAI;AACnC,SAAK,gBAAgB,WAAW;AAChC,SAAK,iBAAiB;AACtB,SAAK,UAAU,QAAQ;AACvB,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA,EAEA,cAAc,MAAwB;AACpC,QAAI,SAAS,SAAS;AACpB,eAAS,gBAAgB;AAAA,IAC3B;AAEA,QAAI,SAAS,QAAQ;AAEnB,iBAAW,MAAM;AACf,YAAI,SAAS,uBAAuB,KAAK,SAAS,YAAY;AAC5D,eAAK,SAAS,WAAW,MAAM;AAC/B,eAAK,SAAS,WAAW,mBAAmB;AAAA,QAC9C;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AAAA,EACF;AAAA,EAEA,mBAAmB;AACjB,QAAI,KAAK,oBAAoB,uBAAuB;AAClD,WAAK,cAAc;AACnB,WAAK,gBAAgB,OAAO;AAAA,IAC9B,OAAO;AACL,WAAK,aAAa;AAClB,WAAK,gBAAgB,MAAM;AAAA,IAC7B;AAAA,EACF;AAAA,EAEQ,eAAe;AACrB,QAAI,KAAK,SAAU;AAEnB,SAAK,WAAW,IAAU,qBAAc;AAAA,MACtC,WAAW,KAAK,QAAQ,UAAU,aAAa;AAAA,MAC/C,OAAO;AAAA,MACP,iBAAiB;AAAA,IACnB,CAAC;AAED,SAAK,SAAS,cAAc,SAAU,CAAC;AACvC,SAAK,SAAS,mBAAyB;AAEvC,SAAK,SAAS;AAAA,MACZ,KAAK,UAAU;AAAA,MACf,KAAK,UAAU;AAAA,IACjB;AAEA,SAAK,SAAS;AAAA,MACZ,KAAK,QAAQ,UAAU,cAAc,OAAO;AAAA,IAC9C;AAEA,SAAK,UAAU,YAAY,KAAK,SAAS,UAAU;AAEnD,SAAK,SAAS,WAAW,iBAAiB,oBAAoB,CAAC,MAAM;AACnE,QAAE,eAAe;AACjB,cAAQ,IAAI,kCAAkC;AAAA,IAChD,CAAC;AAAA,EACH;AAAA,EAEQ,YAAY;AAClB,SAAK,QAAQ,IAAU,aAAM;AAC7B,SAAK,MAAM,aAAa,IAAU,aAAM,OAAQ;AAEhD,UAAM,aAAa,IAAU,kBAAW,IAAI,EAAE;AAG9C,UAAM,QAAQ,IAAU,uBAAgB,UAAU,SAAU,CAAG;AAC/D,SAAK,MAAM,IAAI,KAAK;AAAA,EACtB;AAAA,EAEQ,aAAa;AACnB,SAAK,SAAS,IAAU;AAAA,MACtB,KAAK,QAAQ,QAAQ,OAAO;AAAA,MAC5B,KAAK,UAAU,cAAc,KAAK,UAAU;AAAA,MAC5C,KAAK,QAAQ,QAAQ,QAAQ;AAAA,MAC7B,KAAK,QAAQ,QAAQ,OAAO;AAAA,IAC9B;AAEA,SAAK,OAAO,SAAS,IAAI,GAAG,KAAK,CAAC;AAClC,SAAK,OAAO,OAAO,GAAG,KAAK,CAAC;AAAA,EAC9B;AAAA,EAEQ,eAAe;AACrB,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,MAAM,WAAW;AACtB,SAAK,MAAM,OAAO;AAClB,SAAK,MAAM,SAAS;AACpB,SAAK,MAAM,QAAQ;AACnB,SAAK,MAAM,SAAS;AACpB,SAAK,MAAM,SAAS;AAEpB,SAAK,UAAU,YAAY,IAAI;AAE/B,UAAM,WAAW,SAAS,OAAO;AAAA,MAC/B;AAAA,MACA,MAAM;AAAA,MACN,UAAU,EAAE,MAAM,QAAQ,QAAQ,OAAO;AAAA,MACzC,OAAO;AAAA,IACT,CAAC;AAED,aAAS,GAAG,QAAQ,CAAC,GAAG,SAAS;AAC/B,YAAM,QAAQ,KAAK,MAAM;AACzB,YAAM,QAAQ,KAAK;AAEnB,UAAI,KAAK,oBAAoB,uBAAuB;AAClD,aAAK,SAAS;AAAA,UACZ,KAAK,IAAI,KAAK,IAAI;AAAA,UAClB,KAAK,IAAI,KAAK,IAAI;AAAA,QACpB;AAAA,MACF;AAEA,UAAI,KAAK,oBAAoB,uBAAuB;AAClD,aAAK,SAAS;AAAA,UACZ,KAAK,IAAI,KAAK,IAAI;AAAA,UAClB,KAAK,IAAI,KAAK,IAAI;AAAA,QACpB;AAAA,MACF;AAAA,IACF,CAAC;AAED,aAAS,GAAG,OAAO,MAAM;AACvB,WAAK,SAAS,kBAAkB,GAAG,CAAC;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAiB;AACvB,UAAM,MAAM,SAAS,cAAc,KAAK;AAExC,QAAI,MAAM,WAAW;AACrB,QAAI,MAAM,QAAQ;AAClB,QAAI,MAAM,SAAS;AACnB,QAAI,MAAM,QAAQ;AAClB,QAAI,MAAM,SAAS;AACnB,QAAI,MAAM,eAAe;AACzB,QAAI,MAAM,aAAa;AACvB,QAAI,MAAM,iBAAiB;AAC3B,QAAI,MAAM,SAAS;AACnB,QAAI,MAAM,UAAU;AACpB,QAAI,MAAM,aAAa;AACvB,QAAI,MAAM,iBAAiB;AAC3B,QAAI,MAAM,QAAQ;AAClB,QAAI,MAAM,aAAa;AACvB,QAAI,MAAM,WAAW;AACrB,QAAI,MAAM,SAAS;AACnB,QAAI,MAAM,aAAa;AACvB,QAAI,MAAM,SAAS;AACnB,QAAI,YAAY;AAEhB,QAAI,iBAAiB,SAAS,MAAM;AAClC,WAAK,iBAAiB;AAAA,IACxB,CAAC;AAED,SAAK,UAAU,YAAY,GAAG;AAC9B,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,gBAAgB,MAAwB;AAC9C,QAAI,CAAC,KAAK,WAAY;AACtB,SAAK,WAAW,YAAY;AAAA,EAC9B;AAAA,EAEQ,aAAa;AACnB,SAAK,gBAAgB,WAAW;AAChC,SAAK,iBAAiB,IAAI,eAAe,MAAM,KAAK,OAAO,CAAC;AAC5D,SAAK,eAAe,QAAQ,KAAK,SAAS;AAAA,EAC5C;AAAA,EAEA,MAAc,aAAa,KAAa;AACtC,UAAM,SAAS,IAAI,WAAW;AAC9B,UAAM,OAAO,MAAM,OAAO,UAAU,GAAG;AAEvC,SAAK,eAAe,KAAK;AACzB,SAAK,UAAU,IAAI,KAAK,YAAY;AAEpC,SAAK,UAAU,SAAS;AAExB,SAAK,aAAa,SAAS,CAAC,UAAe;AACzC,UAAK,MAAqB,QAAQ;AAChC,cAAM,WAAW,IAAU,yBAAkB,EAAE,SAAS,MAAM,CAAC;AAC/D,cAAM,kBAAkB,IAAI;AAC5B,aAAK,UAAU,KAAK,KAAK;AAAA,MAC3B;AAAA,IACF,CAAC;AAED,SAAK,UAAU,kBAAkB,IAAI;AAAA,EACvC;AAAA,EAEQ,WAAoB;AAC1B,WACE,kBAAkB,UAClB,UAAU,iBAAiB,KAC3B,OAAO,aAAa;AAAA,EAExB;AAAA,EAEQ,cAAc,CAAC,MAAqB;AAC1C,QAAI,EAAE,QAAQ,IAAK;AAEnB,QAAI,KAAK,oBAAoB,uBAAuB;AAClD,WAAK,cAAc;AAAA,IACrB,OAAO;AACL,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEQ,eAAe;AACrB,SAAK,UAAU,QAAQ;AACvB,UAAM,OAAO,IAAI,sBAAsB;AAAA,MACrC,QAAQ,KAAK;AAAA,MACb,KAAK,KAAK,SAAS;AAAA,MACnB,WAAW,KAAK;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,eAAe;AAAA,MACf,SAAS;AAAA,MACT,gBAAgB,CAAC,KAAK,SAAS;AAAA,IACjC,CAAC;AAED,SAAK,WAAW;AAChB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,gBAAgB;AACtB,SAAK,UAAU,QAAQ;AAEvB,UAAM,WAAW,KAAK,OAAO,SAAS,WAAW,KAAK,WAAW;AAEjE,SAAK,WAAW,IAAI;AAAA,MAClB,KAAK;AAAA,MACL,KAAK,SAAS;AAAA,MACd;AAAA,QACE,QAAQ,KAAK,YAAY,MAAM;AAAA,QAC/B,UAAU,KAAK,IAAI,UAAU,GAAG;AAAA,QAChC,gBAAgB,CAAC,KAAK,SAAS;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AACF;","names":["THREE","THREE"]}