@popaya/pgsg-viewer 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PRUUK6YZ.js → chunk-NTRLRS2X.js} +8 -2
- package/dist/chunk-NTRLRS2X.js.map +1 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{pgsg-three-viewer-IJNO2SYE.js → pgsg-three-viewer-67PSYKTJ.js} +476 -63
- package/dist/pgsg-three-viewer-67PSYKTJ.js.map +1 -0
- package/dist/react/index.js +4 -2
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-PRUUK6YZ.js.map +0 -1
- package/dist/pgsg-three-viewer-IJNO2SYE.js.map +0 -1
|
@@ -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-
|
|
13
|
+
const { PGSGThreeViewer } = await import("./pgsg-three-viewer-67PSYKTJ.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-
|
|
39
|
+
//# sourceMappingURL=chunk-NTRLRS2X.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":[]}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -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 };
|
package/dist/core/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/viewers/three/pgsg-three-viewer.ts
|
|
2
|
-
import * as
|
|
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 =
|
|
273
|
+
const maxRayDown = 20;
|
|
271
274
|
const origin = camPos.clone();
|
|
272
|
-
origin.y +=
|
|
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
|
|
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
|
|
925
|
+
orbitTarget = new THREE4.Vector3();
|
|
534
926
|
lastTime = performance.now();
|
|
535
927
|
colliders = [];
|
|
536
|
-
|
|
537
|
-
|
|
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() {
|
|
@@ -564,7 +958,7 @@ var PGSGThreeViewer = class {
|
|
|
564
958
|
const walk = this.controls;
|
|
565
959
|
walk.enableGyro();
|
|
566
960
|
} else {
|
|
567
|
-
this.
|
|
961
|
+
this.switchToMeasure();
|
|
568
962
|
}
|
|
569
963
|
window.addEventListener("keydown", this.onKeyToggle);
|
|
570
964
|
}
|
|
@@ -599,16 +993,29 @@ var PGSGThreeViewer = class {
|
|
|
599
993
|
this.renderer.dispose();
|
|
600
994
|
}
|
|
601
995
|
setCameraMode(mode) {
|
|
602
|
-
|
|
603
|
-
|
|
996
|
+
console.log("[PGSGThreeViewer] setCameraMode:", mode);
|
|
997
|
+
if (this.controls instanceof MeasurementController) {
|
|
998
|
+
this.persistentMeasurements = this.controls.measurements;
|
|
999
|
+
this.controls.setVisible(false);
|
|
604
1000
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
1001
|
+
switch (mode) {
|
|
1002
|
+
case "measure":
|
|
1003
|
+
document.exitPointerLock();
|
|
1004
|
+
this.switchToMeasure();
|
|
1005
|
+
if (this.controls instanceof MeasurementController) {
|
|
1006
|
+
this.controls.setVisible(true);
|
|
610
1007
|
}
|
|
611
|
-
|
|
1008
|
+
break;
|
|
1009
|
+
case "orbit":
|
|
1010
|
+
document.exitPointerLock();
|
|
1011
|
+
this.switchToOrbit();
|
|
1012
|
+
break;
|
|
1013
|
+
case "walk":
|
|
1014
|
+
this.switchToWalk();
|
|
1015
|
+
break;
|
|
1016
|
+
default:
|
|
1017
|
+
this.switchToMeasure();
|
|
1018
|
+
break;
|
|
612
1019
|
}
|
|
613
1020
|
}
|
|
614
1021
|
toggleCameraMode() {
|
|
@@ -622,13 +1029,13 @@ var PGSGThreeViewer = class {
|
|
|
622
1029
|
}
|
|
623
1030
|
initRenderer() {
|
|
624
1031
|
if (this.renderer) return;
|
|
625
|
-
this.renderer = new
|
|
1032
|
+
this.renderer = new THREE4.WebGLRenderer({
|
|
626
1033
|
antialias: this.options.renderer?.antialias ?? false,
|
|
627
1034
|
alpha: false,
|
|
628
1035
|
powerPreference: "high-performance"
|
|
629
1036
|
});
|
|
630
1037
|
this.renderer.setClearColor(1118481, 1);
|
|
631
|
-
this.renderer.outputColorSpace =
|
|
1038
|
+
this.renderer.outputColorSpace = THREE4.SRGBColorSpace;
|
|
632
1039
|
this.renderer.setSize(
|
|
633
1040
|
this.container.clientWidth,
|
|
634
1041
|
this.container.clientHeight
|
|
@@ -643,21 +1050,21 @@ var PGSGThreeViewer = class {
|
|
|
643
1050
|
});
|
|
644
1051
|
}
|
|
645
1052
|
initScene() {
|
|
646
|
-
this.scene = new
|
|
647
|
-
this.scene.background = new
|
|
648
|
-
const gridHelper = new
|
|
649
|
-
const light = new
|
|
1053
|
+
this.scene = new THREE4.Scene();
|
|
1054
|
+
this.scene.background = new THREE4.Color(1118481);
|
|
1055
|
+
const gridHelper = new THREE4.GridHelper(10, 10);
|
|
1056
|
+
const light = new THREE4.HemisphereLight(16777215, 2236962, 1);
|
|
650
1057
|
this.scene.add(light);
|
|
651
1058
|
}
|
|
652
1059
|
initCamera() {
|
|
653
|
-
this.camera = new
|
|
1060
|
+
this.camera = new THREE4.PerspectiveCamera(
|
|
654
1061
|
this.options.camera?.fov ?? 60,
|
|
655
1062
|
this.container.clientWidth / this.container.clientHeight,
|
|
656
1063
|
this.options.camera?.near ?? 0.01,
|
|
657
1064
|
this.options.camera?.far ?? 1e4
|
|
658
1065
|
);
|
|
659
|
-
this.camera.position.set(
|
|
660
|
-
this.camera.lookAt(0,
|
|
1066
|
+
this.camera.position.set(2, 2, 5);
|
|
1067
|
+
this.camera.lookAt(0, 0, 0);
|
|
661
1068
|
}
|
|
662
1069
|
initJoystick() {
|
|
663
1070
|
const zone = document.createElement("div");
|
|
@@ -671,19 +1078,13 @@ var PGSGThreeViewer = class {
|
|
|
671
1078
|
const joystick = nipplejs.create({
|
|
672
1079
|
zone,
|
|
673
1080
|
mode: "static",
|
|
674
|
-
position: { left: "
|
|
1081
|
+
position: { left: "50px", bottom: "50px" },
|
|
675
1082
|
color: "white"
|
|
676
1083
|
});
|
|
677
1084
|
joystick.on("move", (_, data) => {
|
|
678
1085
|
const angle = data.angle.radian;
|
|
679
1086
|
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) {
|
|
1087
|
+
if (this.controls instanceof WalkCapsuleController || this.controls instanceof OrbitCameraController) {
|
|
687
1088
|
this.controls.setMovementVector(
|
|
688
1089
|
Math.sin(angle) * force,
|
|
689
1090
|
Math.cos(angle) * force
|
|
@@ -691,35 +1092,12 @@ var PGSGThreeViewer = class {
|
|
|
691
1092
|
}
|
|
692
1093
|
});
|
|
693
1094
|
joystick.on("end", () => {
|
|
694
|
-
this.controls.
|
|
1095
|
+
if (this.controls instanceof WalkCapsuleController || this.controls instanceof OrbitCameraController) {
|
|
1096
|
+
this.controls.setMovementVector(0, 0);
|
|
1097
|
+
}
|
|
695
1098
|
});
|
|
696
1099
|
}
|
|
697
1100
|
initModeToggle() {
|
|
698
|
-
const btn = document.createElement("div");
|
|
699
|
-
btn.style.position = "absolute";
|
|
700
|
-
btn.style.right = "20px";
|
|
701
|
-
btn.style.bottom = "40px";
|
|
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
1101
|
}
|
|
724
1102
|
updateModeLabel(mode) {
|
|
725
1103
|
if (!this.modeButton) return;
|
|
@@ -731,6 +1109,7 @@ var PGSGThreeViewer = class {
|
|
|
731
1109
|
this.resizeObserver.observe(this.container);
|
|
732
1110
|
}
|
|
733
1111
|
async loadCollider(url) {
|
|
1112
|
+
console.log("[PGSG Viewer] Loading collider:", url);
|
|
734
1113
|
const loader = new GLTFLoader();
|
|
735
1114
|
const gltf = await loader.loadAsync(url);
|
|
736
1115
|
this.colliderMesh = gltf.scene;
|
|
@@ -738,11 +1117,12 @@ var PGSGThreeViewer = class {
|
|
|
738
1117
|
this.colliders.length = 0;
|
|
739
1118
|
this.colliderMesh.traverse((child) => {
|
|
740
1119
|
if (child.isMesh) {
|
|
741
|
-
child.material = new
|
|
1120
|
+
child.material = new THREE4.MeshBasicMaterial({ visible: false, side: THREE4.DoubleSide });
|
|
742
1121
|
child.updateMatrixWorld(true);
|
|
743
1122
|
this.colliders.push(child);
|
|
744
1123
|
}
|
|
745
1124
|
});
|
|
1125
|
+
console.log("[PGSG Viewer] Colliders loaded:", this.colliders.length);
|
|
746
1126
|
this.worldRoot.updateMatrixWorld(true);
|
|
747
1127
|
}
|
|
748
1128
|
isMobile() {
|
|
@@ -756,6 +1136,17 @@ var PGSGThreeViewer = class {
|
|
|
756
1136
|
this.switchToWalk();
|
|
757
1137
|
}
|
|
758
1138
|
};
|
|
1139
|
+
switchToMeasure() {
|
|
1140
|
+
this.controls?.dispose();
|
|
1141
|
+
this.measurementController = new MeasurementController({
|
|
1142
|
+
dom: this.renderer.domElement,
|
|
1143
|
+
camera: this.camera,
|
|
1144
|
+
scene: this.scene,
|
|
1145
|
+
colliders: this.colliders
|
|
1146
|
+
});
|
|
1147
|
+
this.measurementController.restoreMeasurements(this.persistentMeasurements);
|
|
1148
|
+
this.controls = this.measurementController;
|
|
1149
|
+
}
|
|
759
1150
|
switchToWalk() {
|
|
760
1151
|
this.controls?.dispose();
|
|
761
1152
|
const walk = new WalkCapsuleController({
|
|
@@ -774,19 +1165,41 @@ var PGSGThreeViewer = class {
|
|
|
774
1165
|
}
|
|
775
1166
|
switchToOrbit() {
|
|
776
1167
|
this.controls?.dispose();
|
|
777
|
-
const distance =
|
|
1168
|
+
const distance = 5;
|
|
778
1169
|
this.controls = new OrbitCameraController(
|
|
779
1170
|
this.camera,
|
|
780
1171
|
this.renderer.domElement,
|
|
781
1172
|
{
|
|
782
1173
|
target: this.orbitTarget.clone(),
|
|
783
|
-
distance
|
|
1174
|
+
distance,
|
|
784
1175
|
usePointerLock: !this.isMobile()
|
|
785
1176
|
}
|
|
786
1177
|
);
|
|
787
1178
|
}
|
|
1179
|
+
getMeasurements() {
|
|
1180
|
+
if (this.measurementController) {
|
|
1181
|
+
return this.measurementController.getMeasurements();
|
|
1182
|
+
}
|
|
1183
|
+
return [];
|
|
1184
|
+
}
|
|
1185
|
+
clearMeasurements() {
|
|
1186
|
+
if (this.measurementController) {
|
|
1187
|
+
this.measurementController.clearAll();
|
|
1188
|
+
this.persistentMeasurements = [];
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
finalizePolyline() {
|
|
1192
|
+
if (this.controls instanceof MeasurementController) {
|
|
1193
|
+
this.controls.finalizePolyline();
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
deleteMeasurement(index) {
|
|
1197
|
+
if (this.measurementController) {
|
|
1198
|
+
this.measurementController.deleteMeasurement(index);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
788
1201
|
};
|
|
789
1202
|
export {
|
|
790
1203
|
PGSGThreeViewer
|
|
791
1204
|
};
|
|
792
|
-
//# sourceMappingURL=pgsg-three-viewer-
|
|
1205
|
+
//# sourceMappingURL=pgsg-three-viewer-67PSYKTJ.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!: WalkCapsuleController | OrbitCameraController | 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 walk.enableGyro();\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 (this.controls instanceof WalkCapsuleController || this.controls instanceof OrbitCameraController) {\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 (this.controls instanceof WalkCapsuleController || this.controls instanceof OrbitCameraController) {\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({ visible: false, side: THREE.DoubleSide });\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"]}
|
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PGSGViewer
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-NTRLRS2X.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;
|
package/dist/react/index.js.map
CHANGED
|
@@ -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;
|
|
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 +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 = \"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: \"75px\", bottom: \"75px\" },\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 = \"40px\";\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"]}
|