@fusefactory/fuse-three-forcegraph 1.0.7 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +10 -2
- package/dist/index.mjs +125 -16
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -74,12 +74,10 @@ declare class CameraController {
|
|
|
74
74
|
camera: THREE.PerspectiveCamera;
|
|
75
75
|
controls: CameraControls;
|
|
76
76
|
private sceneBounds;
|
|
77
|
-
private domElement;
|
|
78
77
|
private autorotateEnabled;
|
|
79
78
|
private autorotateSpeed;
|
|
80
79
|
private userIsActive;
|
|
81
80
|
constructor(domelement: HTMLElement, config?: CameraConfig);
|
|
82
|
-
private handleWheel;
|
|
83
81
|
private setupDefaultControls;
|
|
84
82
|
/**
|
|
85
83
|
* Set camera control mode
|
|
@@ -1104,6 +1102,7 @@ declare class InputProcessor extends EventEmitter {
|
|
|
1104
1102
|
private handlePointerDown;
|
|
1105
1103
|
private handlePointerUp;
|
|
1106
1104
|
private handlePointerLeave;
|
|
1105
|
+
private handlePointerCancel;
|
|
1107
1106
|
private updateHover;
|
|
1108
1107
|
/**
|
|
1109
1108
|
* Update hover state even when pointer is stationary
|
|
@@ -1126,6 +1125,7 @@ declare class InteractionManager {
|
|
|
1126
1125
|
private hoverHandler;
|
|
1127
1126
|
private clickHandler;
|
|
1128
1127
|
private dragHandler;
|
|
1128
|
+
private keyboardNavigator;
|
|
1129
1129
|
tooltipManager: TooltipManager;
|
|
1130
1130
|
private isDragging;
|
|
1131
1131
|
isTooltipSticky: boolean;
|
|
@@ -1423,6 +1423,14 @@ declare class Engine {
|
|
|
1423
1423
|
y: number;
|
|
1424
1424
|
z: number;
|
|
1425
1425
|
}, distance: number, transitionDuration?: number): Promise<void>;
|
|
1426
|
+
/**
|
|
1427
|
+
* Reset camera to its initial position and target
|
|
1428
|
+
*/
|
|
1429
|
+
resetCamera(enableTransition?: boolean): Promise<void>;
|
|
1430
|
+
/**
|
|
1431
|
+
* Fit camera to show all currently visible nodes
|
|
1432
|
+
*/
|
|
1433
|
+
fitToView(enableTransition?: boolean): Promise<void>;
|
|
1426
1434
|
/**
|
|
1427
1435
|
* Cleanup
|
|
1428
1436
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -557,6 +557,7 @@ var InputProcessor = class extends EventEmitter {
|
|
|
557
557
|
this.canvas.addEventListener("pointerdown", this.handlePointerDown);
|
|
558
558
|
this.canvas.addEventListener("pointerup", this.handlePointerUp);
|
|
559
559
|
this.canvas.addEventListener("pointerleave", this.handlePointerLeave);
|
|
560
|
+
this.canvas.addEventListener("pointercancel", this.handlePointerCancel);
|
|
560
561
|
}
|
|
561
562
|
handlePointerMove = (event) => {
|
|
562
563
|
this.updatePointer(event);
|
|
@@ -590,6 +591,7 @@ var InputProcessor = class extends EventEmitter {
|
|
|
590
591
|
}
|
|
591
592
|
};
|
|
592
593
|
handlePointerDown = (event) => {
|
|
594
|
+
this.canvas.setPointerCapture(event.pointerId);
|
|
593
595
|
this.updatePointer(event);
|
|
594
596
|
this.mouseDownTime = Date.now();
|
|
595
597
|
this.isPointerDown = true;
|
|
@@ -607,6 +609,7 @@ var InputProcessor = class extends EventEmitter {
|
|
|
607
609
|
});
|
|
608
610
|
};
|
|
609
611
|
handlePointerUp = (event) => {
|
|
612
|
+
this.updatePointer(event);
|
|
610
613
|
const clickDuration = Date.now() - this.mouseDownTime;
|
|
611
614
|
const wasClick = this.isTouch ? !this.isDragging : clickDuration < this.CLICK_THRESHOLD;
|
|
612
615
|
if (this.isDragging) this.emit("pointer:dragend", {
|
|
@@ -637,6 +640,31 @@ var InputProcessor = class extends EventEmitter {
|
|
|
637
640
|
this.lastClientX = null;
|
|
638
641
|
this.lastClientY = null;
|
|
639
642
|
this.updateHover(-1);
|
|
643
|
+
if (this.isDragging) {
|
|
644
|
+
this.emit("pointer:dragend", {
|
|
645
|
+
index: this.draggedIndex,
|
|
646
|
+
x: this.canvasPointer.x,
|
|
647
|
+
y: this.canvasPointer.y,
|
|
648
|
+
clientX: this.pointerDownX,
|
|
649
|
+
clientY: this.pointerDownY
|
|
650
|
+
});
|
|
651
|
+
this.isPointerDown = false;
|
|
652
|
+
this.isDragging = false;
|
|
653
|
+
this.draggedIndex = -1;
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
handlePointerCancel = (_event) => {
|
|
657
|
+
if (this.isDragging) this.emit("pointer:dragend", {
|
|
658
|
+
index: this.draggedIndex,
|
|
659
|
+
x: this.canvasPointer.x,
|
|
660
|
+
y: this.canvasPointer.y,
|
|
661
|
+
clientX: this.pointerDownX,
|
|
662
|
+
clientY: this.pointerDownY
|
|
663
|
+
});
|
|
664
|
+
this.isPointerDown = false;
|
|
665
|
+
this.isDragging = false;
|
|
666
|
+
this.draggedIndex = -1;
|
|
667
|
+
this.updateHover(-1);
|
|
640
668
|
};
|
|
641
669
|
updateHover(index) {
|
|
642
670
|
const prevIndex = this.currentHoverIndex;
|
|
@@ -720,6 +748,72 @@ var InputProcessor = class extends EventEmitter {
|
|
|
720
748
|
this.canvas.removeEventListener("pointerdown", this.handlePointerDown);
|
|
721
749
|
this.canvas.removeEventListener("pointerup", this.handlePointerUp);
|
|
722
750
|
this.canvas.removeEventListener("pointerleave", this.handlePointerLeave);
|
|
751
|
+
this.canvas.removeEventListener("pointercancel", this.handlePointerCancel);
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
//#endregion
|
|
756
|
+
//#region controls/KeyboardNavigator.ts
|
|
757
|
+
const PAN_STEP = .05;
|
|
758
|
+
const ORBIT_STEP = .05;
|
|
759
|
+
var KeyboardNavigator = class {
|
|
760
|
+
boundHandler;
|
|
761
|
+
constructor(camera, interactionManager, canvas) {
|
|
762
|
+
this.camera = camera;
|
|
763
|
+
this.interactionManager = interactionManager;
|
|
764
|
+
this.canvas = canvas;
|
|
765
|
+
this.boundHandler = this.handleKeyDown.bind(this);
|
|
766
|
+
canvas.setAttribute("tabindex", "0");
|
|
767
|
+
canvas.addEventListener("keydown", this.boundHandler);
|
|
768
|
+
}
|
|
769
|
+
handleKeyDown(e) {
|
|
770
|
+
if (e.metaKey || e.ctrlKey) return;
|
|
771
|
+
const c = this.camera.controls;
|
|
772
|
+
const radius = c._spherical.radius;
|
|
773
|
+
switch (e.key) {
|
|
774
|
+
case "ArrowLeft":
|
|
775
|
+
e.preventDefault();
|
|
776
|
+
e.shiftKey ? c.truck(-radius * PAN_STEP, 0, true) : c.rotate(-ORBIT_STEP, 0, true);
|
|
777
|
+
break;
|
|
778
|
+
case "ArrowRight":
|
|
779
|
+
e.preventDefault();
|
|
780
|
+
e.shiftKey ? c.truck(radius * PAN_STEP, 0, true) : c.rotate(ORBIT_STEP, 0, true);
|
|
781
|
+
break;
|
|
782
|
+
case "ArrowUp":
|
|
783
|
+
e.preventDefault();
|
|
784
|
+
e.shiftKey ? c.truck(0, -radius * PAN_STEP, true) : c.rotate(0, -ORBIT_STEP, true);
|
|
785
|
+
break;
|
|
786
|
+
case "ArrowDown":
|
|
787
|
+
e.preventDefault();
|
|
788
|
+
e.shiftKey ? c.truck(0, radius * PAN_STEP, true) : c.rotate(0, ORBIT_STEP, true);
|
|
789
|
+
break;
|
|
790
|
+
case "+":
|
|
791
|
+
case "=":
|
|
792
|
+
e.preventDefault();
|
|
793
|
+
c.dolly(-radius * .1, true);
|
|
794
|
+
break;
|
|
795
|
+
case "-":
|
|
796
|
+
case "_":
|
|
797
|
+
e.preventDefault();
|
|
798
|
+
c.dolly(radius * .1, true);
|
|
799
|
+
break;
|
|
800
|
+
case "f":
|
|
801
|
+
case "F":
|
|
802
|
+
case "Home":
|
|
803
|
+
e.preventDefault();
|
|
804
|
+
this.camera.reset(void 0, void 0, true);
|
|
805
|
+
break;
|
|
806
|
+
case "Escape":
|
|
807
|
+
if (this.interactionManager.isTooltipSticky) {
|
|
808
|
+
this.interactionManager.tooltipManager.hideFull();
|
|
809
|
+
this.interactionManager.isTooltipSticky = false;
|
|
810
|
+
this.interactionManager.stickyNodeId = null;
|
|
811
|
+
}
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
dispose() {
|
|
816
|
+
this.canvas.removeEventListener("keydown", this.boundHandler);
|
|
723
817
|
}
|
|
724
818
|
};
|
|
725
819
|
|
|
@@ -730,6 +824,7 @@ var InteractionManager = class {
|
|
|
730
824
|
hoverHandler;
|
|
731
825
|
clickHandler;
|
|
732
826
|
dragHandler;
|
|
827
|
+
keyboardNavigator = null;
|
|
733
828
|
tooltipManager;
|
|
734
829
|
isDragging = false;
|
|
735
830
|
isTooltipSticky = false;
|
|
@@ -750,6 +845,7 @@ var InteractionManager = class {
|
|
|
750
845
|
this.wireEvents();
|
|
751
846
|
this.wireTooltipEvents();
|
|
752
847
|
this.wireVisualFeedbackEvents();
|
|
848
|
+
if (cameraController) this.keyboardNavigator = new KeyboardNavigator(cameraController, this, canvas);
|
|
753
849
|
}
|
|
754
850
|
wireEvents() {
|
|
755
851
|
this.pointerInput.on("pointer:hoverstart", (data) => {
|
|
@@ -909,6 +1005,7 @@ var InteractionManager = class {
|
|
|
909
1005
|
this.hoverHandler.dispose();
|
|
910
1006
|
this.clickHandler.dispose();
|
|
911
1007
|
this.dragHandler.dispose();
|
|
1008
|
+
this.keyboardNavigator?.dispose();
|
|
912
1009
|
this.tooltipManager.dispose();
|
|
913
1010
|
}
|
|
914
1011
|
};
|
|
@@ -927,7 +1024,6 @@ var CameraController = class {
|
|
|
927
1024
|
camera;
|
|
928
1025
|
controls;
|
|
929
1026
|
sceneBounds = null;
|
|
930
|
-
domElement;
|
|
931
1027
|
autorotateEnabled = false;
|
|
932
1028
|
autorotateSpeed = .5;
|
|
933
1029
|
userIsActive = true;
|
|
@@ -938,22 +1034,9 @@ var CameraController = class {
|
|
|
938
1034
|
else this.camera.position.set(0, 0, 2);
|
|
939
1035
|
this.controls = new CameraControls(this.camera, domelement);
|
|
940
1036
|
this.setupDefaultControls(config);
|
|
941
|
-
this.handleWheel = this.handleWheel.bind(this);
|
|
942
|
-
domelement.addEventListener("wheel", this.handleWheel, {
|
|
943
|
-
capture: true,
|
|
944
|
-
passive: true
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
handleWheel() {
|
|
948
|
-
if (!this.controls.dollyToCursor) return;
|
|
949
|
-
const c = this.controls;
|
|
950
|
-
if (c._spherical && c._sphericalEnd) {
|
|
951
|
-
c._spherical.theta = c._sphericalEnd.theta;
|
|
952
|
-
c._spherical.phi = c._sphericalEnd.phi;
|
|
953
|
-
}
|
|
954
1037
|
}
|
|
955
1038
|
setupDefaultControls(config) {
|
|
956
|
-
this.controls.smoothTime = .
|
|
1039
|
+
this.controls.smoothTime = .5;
|
|
957
1040
|
this.controls.dollyToCursor = true;
|
|
958
1041
|
this.controls.minDistance = config?.minDistance ?? 100;
|
|
959
1042
|
this.controls.maxDistance = config?.maxDistance ?? 4e3;
|
|
@@ -1059,7 +1142,6 @@ var CameraController = class {
|
|
|
1059
1142
|
* Dispose camera manager and clean up
|
|
1060
1143
|
*/
|
|
1061
1144
|
dispose() {
|
|
1062
|
-
this.domElement.removeEventListener("wheel", this.handleWheel, { capture: true });
|
|
1063
1145
|
this.controls.dispose();
|
|
1064
1146
|
}
|
|
1065
1147
|
/**
|
|
@@ -4515,6 +4597,33 @@ var Engine = class {
|
|
|
4515
4597
|
await this.setCameraLookAt(newPos, targetVec, transitionDuration);
|
|
4516
4598
|
}
|
|
4517
4599
|
/**
|
|
4600
|
+
* Reset camera to its initial position and target
|
|
4601
|
+
*/
|
|
4602
|
+
async resetCamera(enableTransition = true) {
|
|
4603
|
+
await this.graphScene.camera.reset(void 0, void 0, enableTransition);
|
|
4604
|
+
}
|
|
4605
|
+
/**
|
|
4606
|
+
* Fit camera to show all currently visible nodes
|
|
4607
|
+
*/
|
|
4608
|
+
async fitToView(enableTransition = true) {
|
|
4609
|
+
if (!this.simulationBuffers.isReady()) return;
|
|
4610
|
+
const positions = this.simulationBuffers.readPositions();
|
|
4611
|
+
const nodes = this.graphStore.getNodes();
|
|
4612
|
+
const box = new THREE.Box3();
|
|
4613
|
+
nodes.forEach((_node, i) => {
|
|
4614
|
+
if (positions[i * 4 + 3] === 0) return;
|
|
4615
|
+
box.expandByPoint(new THREE.Vector3(positions[i * 4], positions[i * 4 + 1], positions[i * 4 + 2]));
|
|
4616
|
+
});
|
|
4617
|
+
if (box.isEmpty()) return;
|
|
4618
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
4619
|
+
const size = box.getSize(new THREE.Vector3());
|
|
4620
|
+
const maxDim = Math.max(size.x, size.y, size.z);
|
|
4621
|
+
const fov = this.graphScene.camera.camera.fov * THREE.MathUtils.DEG2RAD;
|
|
4622
|
+
const distance = maxDim / 2 / Math.tan(fov / 2) * 1.5;
|
|
4623
|
+
const camPos = center.clone().add(this.graphScene.camera.camera.position.clone().sub(center).normalize().multiplyScalar(distance));
|
|
4624
|
+
await this.graphScene.camera.setLookAt(camPos, center, enableTransition);
|
|
4625
|
+
}
|
|
4626
|
+
/**
|
|
4518
4627
|
* Cleanup
|
|
4519
4628
|
*/
|
|
4520
4629
|
dispose() {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusefactory/fuse-three-forcegraph",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"packageManager": "pnpm@10.6.4",
|
|
6
6
|
"description": "A high-performance GPU-accelerated force-directed graph visualization library built with Three.js. Features a modular pass-based architecture for flexible and extensible force simulations.",
|
|
7
7
|
"author": "Matteo Amerena",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@rnbo/js": "^1.4.2",
|
|
32
|
-
"camera-controls": "
|
|
33
|
-
"three": "^0.
|
|
32
|
+
"camera-controls": "https://github.com/ammlyy/camera-controls.git",
|
|
33
|
+
"three": "^0.183.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@types/three": "^0.
|
|
37
|
-
"tsdown": "0.
|
|
36
|
+
"@types/three": "^0.183.1",
|
|
37
|
+
"tsdown": "^0.21.0",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
39
|
"vite-plugin-glsl": "^1.5.5"
|
|
40
40
|
},
|