@cocorof/graphier 0.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/index.cjs +420 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +57 -2
- package/dist/index.js +420 -83
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,7 +16,8 @@ const DEFAULT_STYLE = {
|
|
|
16
16
|
labelThreshold: 0.8,
|
|
17
17
|
showLabels: true,
|
|
18
18
|
maxLabels: 150,
|
|
19
|
-
edgeWidthScale: 1
|
|
19
|
+
edgeWidthScale: 1,
|
|
20
|
+
flySpeed: 1
|
|
20
21
|
};
|
|
21
22
|
const DEFAULT_LAYOUT = {
|
|
22
23
|
type: "force-3d",
|
|
@@ -24,7 +25,8 @@ const DEFAULT_LAYOUT = {
|
|
|
24
25
|
linkDistance: "auto",
|
|
25
26
|
alphaDecay: "auto",
|
|
26
27
|
velocityDecay: 0.4,
|
|
27
|
-
settledThreshold: 5e-3
|
|
28
|
+
settledThreshold: 5e-3,
|
|
29
|
+
spreadFactor: "auto"
|
|
28
30
|
};
|
|
29
31
|
function hexToRgb(hex) {
|
|
30
32
|
const c2 = hex.replace("#", "");
|
|
@@ -170,17 +172,26 @@ function resolveTheme(theme) {
|
|
|
170
172
|
}
|
|
171
173
|
return { nodeColor, nodeColorBright, linkColor, backgroundColor };
|
|
172
174
|
}
|
|
175
|
+
function autoSpreadFactor(n) {
|
|
176
|
+
if (n <= 500) return 1;
|
|
177
|
+
if (n <= 2e3) return 1 + (n - 500) / 1500 * 0.3;
|
|
178
|
+
if (n <= 1e4) return 1.3 + (n - 2e3) / 8e3 * 0.5;
|
|
179
|
+
return Math.min(2.5, 1.8 + (n - 1e4) / 4e4 * 0.7);
|
|
180
|
+
}
|
|
173
181
|
function resolveLayoutParams(nodeCount, config) {
|
|
174
182
|
const n = nodeCount;
|
|
175
|
-
const
|
|
176
|
-
const
|
|
183
|
+
const spread = (config == null ? void 0 : config.spreadFactor) !== "auto" && (config == null ? void 0 : config.spreadFactor) != null ? config.spreadFactor : autoSpreadFactor(n);
|
|
184
|
+
const baseCharge = (config == null ? void 0 : config.charge) !== "auto" && (config == null ? void 0 : config.charge) != null ? config.charge : n > 5e4 ? -80 : n > 1e4 ? -120 : -200;
|
|
185
|
+
const charge = baseCharge * spread;
|
|
186
|
+
const distanceMax = (n > 5e4 ? 1500 : n > 1e4 ? 2e3 : 3e3) * spread;
|
|
177
187
|
const theta = n > 2e4 ? 1.5 : n > 5e3 ? 1.2 : 0.9;
|
|
178
|
-
const
|
|
188
|
+
const baseLinkDistance = (config == null ? void 0 : config.linkDistance) !== "auto" && (config == null ? void 0 : config.linkDistance) != null ? config.linkDistance : n > 5e4 ? 120 : n > 1e4 ? 180 : 250;
|
|
189
|
+
const linkDistance = baseLinkDistance * spread;
|
|
179
190
|
const alphaDecay = (config == null ? void 0 : config.alphaDecay) !== "auto" && (config == null ? void 0 : config.alphaDecay) != null ? config.alphaDecay : n > 5e4 ? 0.04 : n > 1e4 ? 0.03 : 0.02;
|
|
180
191
|
const velocityDecay = (config == null ? void 0 : config.velocityDecay) ?? 0.4;
|
|
181
192
|
const settledThreshold = (config == null ? void 0 : config.settledThreshold) ?? 5e-3;
|
|
182
193
|
const postEvery = n > 5e4 ? 5 : n > 1e4 ? 3 : 2;
|
|
183
|
-
const initialRadius2 = 500 + Math.min(n, 1e4) * 0.1;
|
|
194
|
+
const initialRadius2 = (500 + Math.min(n, 1e4) * 0.1) * spread;
|
|
184
195
|
return {
|
|
185
196
|
charge,
|
|
186
197
|
distanceMax,
|
|
@@ -1703,27 +1714,31 @@ function applyNodeHighlight(nodesMesh, nodes, selectedNodeId, highlightSet, them
|
|
|
1703
1714
|
} else if (node.id === selectedNodeId) {
|
|
1704
1715
|
tmpColor.set(brightColor);
|
|
1705
1716
|
brightTmp.setRGB(1, 1, 1);
|
|
1706
|
-
tmpColor.lerp(brightTmp, 0.
|
|
1717
|
+
tmpColor.lerp(brightTmp, 0.65);
|
|
1718
|
+
tmpColor.multiplyScalar(1.4);
|
|
1719
|
+
tmpColor.r = Math.min(tmpColor.r, 2);
|
|
1720
|
+
tmpColor.g = Math.min(tmpColor.g, 2);
|
|
1721
|
+
tmpColor.b = Math.min(tmpColor.b, 2);
|
|
1707
1722
|
} else if (highlightSet.has(node.id)) {
|
|
1708
1723
|
const hop = highlightSet.get(node.id);
|
|
1709
1724
|
if (hop === 1) {
|
|
1710
|
-
tmpColor.set(brightColor);
|
|
1725
|
+
tmpColor.set(brightColor).multiplyScalar(1.2);
|
|
1711
1726
|
} else if (hop === 2) {
|
|
1712
1727
|
tmpColor.set(baseColor);
|
|
1713
1728
|
brightTmp.set(brightColor);
|
|
1714
|
-
tmpColor.lerp(brightTmp, 0.
|
|
1729
|
+
tmpColor.lerp(brightTmp, 0.5);
|
|
1715
1730
|
} else {
|
|
1716
|
-
tmpColor.set(baseColor).multiplyScalar(0.
|
|
1731
|
+
tmpColor.set(baseColor).multiplyScalar(0.6);
|
|
1717
1732
|
}
|
|
1718
1733
|
} else {
|
|
1719
|
-
tmpColor.set(baseColor).multiplyScalar(0.
|
|
1734
|
+
tmpColor.set(baseColor).multiplyScalar(0.04);
|
|
1720
1735
|
}
|
|
1721
1736
|
nodesMesh.setColorAt(i, tmpColor);
|
|
1722
1737
|
}
|
|
1723
1738
|
if (nodesMesh.instanceColor) nodesMesh.instanceColor.needsUpdate = true;
|
|
1724
1739
|
const mat = nodesMesh.material;
|
|
1725
1740
|
if ((_a = mat.uniforms) == null ? void 0 : _a.uGlowIntensity) {
|
|
1726
|
-
mat.uniforms.uGlowIntensity.value = highlightSet ? 1.
|
|
1741
|
+
mat.uniforms.uGlowIntensity.value = highlightSet ? 1.5 : 1;
|
|
1727
1742
|
}
|
|
1728
1743
|
}
|
|
1729
1744
|
function applyEdgeHighlight(edgesMesh, links, edgeNodeIndices, edgeLinkIndices, highlightSet, edgeOpacity, nodeCount, theme) {
|
|
@@ -1751,7 +1766,7 @@ function applyEdgeHighlight(edgesMesh, links, edgeNodeIndices, edgeLinkIndices,
|
|
|
1751
1766
|
tmpColor.multiplyScalar(1.2);
|
|
1752
1767
|
}
|
|
1753
1768
|
} else {
|
|
1754
|
-
tmpColor.setRGB(0.
|
|
1769
|
+
tmpColor.setRGB(0.01, 0.01, 0.02);
|
|
1755
1770
|
}
|
|
1756
1771
|
colorArr[i * 6 + 0] = tmpColor.r;
|
|
1757
1772
|
colorArr[i * 6 + 1] = tmpColor.g;
|
|
@@ -1838,6 +1853,9 @@ function createLabelSystem(maxLabels) {
|
|
|
1838
1853
|
}
|
|
1839
1854
|
function defaultLabelText$1(node) {
|
|
1840
1855
|
const label = node.label ?? node.id ?? "";
|
|
1856
|
+
if (node.type === "repo" && label.includes("/")) {
|
|
1857
|
+
return label.split("/").pop();
|
|
1858
|
+
}
|
|
1841
1859
|
return label.length > 30 ? label.substring(0, 27) + "…" : label;
|
|
1842
1860
|
}
|
|
1843
1861
|
function getOrCreateTexture(cache, nodeId, text, color) {
|
|
@@ -1876,7 +1894,7 @@ function getOrCreateTexture(cache, nodeId, text, color) {
|
|
|
1876
1894
|
}
|
|
1877
1895
|
return entry;
|
|
1878
1896
|
}
|
|
1879
|
-
function updateLabels(state, nodes, positions, scales, camera, theme, showLabels, labelScale, labelThreshold, maxLabels, labelFormatter) {
|
|
1897
|
+
function updateLabels(state, nodes, positions, scales, camera, theme, showLabels, labelScale, labelThreshold, maxLabels, labelFormatter, highlightSet) {
|
|
1880
1898
|
if (!positions || nodes.length === 0 || !showLabels) {
|
|
1881
1899
|
for (const sp of state.sprites) sp.visible = false;
|
|
1882
1900
|
return;
|
|
@@ -1890,6 +1908,7 @@ function updateLabels(state, nodes, positions, scales, camera, theme, showLabels
|
|
|
1890
1908
|
const maxDistSq = maxDist * maxDist;
|
|
1891
1909
|
const candidates = [];
|
|
1892
1910
|
for (let i = 0; i < nc; i++) {
|
|
1911
|
+
if (highlightSet && !highlightSet.has(nodes[i].id)) continue;
|
|
1893
1912
|
const dx = positions[i * 3] - camPos.x;
|
|
1894
1913
|
const dy = positions[i * 3 + 1] - camPos.y;
|
|
1895
1914
|
const dz = positions[i * 3 + 2] - camPos.z;
|
|
@@ -1935,58 +1954,49 @@ function disposeLabelSystem(state) {
|
|
|
1935
1954
|
}
|
|
1936
1955
|
state.textureCache.clear();
|
|
1937
1956
|
}
|
|
1938
|
-
const
|
|
1939
|
-
const
|
|
1940
|
-
const
|
|
1941
|
-
const
|
|
1942
|
-
const
|
|
1957
|
+
const ORBIT_ACCEL = 12e-4;
|
|
1958
|
+
const ORBIT_MAX_SPEED = 0.06;
|
|
1959
|
+
const ORBIT_DAMPING = 0.88;
|
|
1960
|
+
const ORBIT_ZOOM_FACTOR = 0.015;
|
|
1961
|
+
const ORBIT_DEAD_ZONE = 1e-4;
|
|
1962
|
+
const FLY_ACCEL = 6e-3;
|
|
1963
|
+
const FLY_FRICTION = 0.92;
|
|
1964
|
+
const FLY_MAX_THRUST = 0.18;
|
|
1965
|
+
const FLY_TURN_RATE = 8e-3;
|
|
1966
|
+
const FLY_DEAD_ZONE = 2e-4;
|
|
1943
1967
|
const _offset = new THREE.Vector3();
|
|
1944
1968
|
const _right = new THREE.Vector3();
|
|
1945
1969
|
const _q = new THREE.Quaternion();
|
|
1946
|
-
|
|
1947
|
-
|
|
1970
|
+
const _dir = new THREE.Vector3();
|
|
1971
|
+
const _worldUp = new THREE.Vector3(0, 1, 0);
|
|
1972
|
+
function clamp(v, min, max) {
|
|
1973
|
+
return v < min ? min : v > max ? max : v;
|
|
1974
|
+
}
|
|
1975
|
+
function createOrbitUpdate(camera, controls, keys) {
|
|
1948
1976
|
const vel = { zoom: 0, azimuth: 0, polar: 0 };
|
|
1949
|
-
function
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
if (
|
|
1953
|
-
keys[
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
vel.
|
|
1961
|
-
vel.
|
|
1962
|
-
vel.
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
container.addEventListener("keyup", onKeyUp);
|
|
1966
|
-
container.addEventListener("blur", onBlur);
|
|
1967
|
-
function update() {
|
|
1968
|
-
if (keys["z"] || keys["Z"]) vel.zoom -= ACCEL;
|
|
1969
|
-
if (keys["x"] || keys["X"]) vel.zoom += ACCEL;
|
|
1970
|
-
if (keys["ArrowLeft"]) vel.azimuth -= ACCEL;
|
|
1971
|
-
if (keys["ArrowRight"]) vel.azimuth += ACCEL;
|
|
1972
|
-
if (keys["ArrowUp"]) vel.polar -= ACCEL;
|
|
1973
|
-
if (keys["ArrowDown"]) vel.polar += ACCEL;
|
|
1974
|
-
vel.zoom = clamp(vel.zoom, -MAX_SPEED, MAX_SPEED);
|
|
1975
|
-
vel.azimuth = clamp(vel.azimuth, -MAX_SPEED, MAX_SPEED);
|
|
1976
|
-
vel.polar = clamp(vel.polar, -MAX_SPEED, MAX_SPEED);
|
|
1977
|
-
vel.zoom *= DAMPING;
|
|
1978
|
-
vel.azimuth *= DAMPING;
|
|
1979
|
-
vel.polar *= DAMPING;
|
|
1980
|
-
if (Math.abs(vel.zoom) < DEAD_ZONE) vel.zoom = 0;
|
|
1981
|
-
if (Math.abs(vel.azimuth) < DEAD_ZONE) vel.azimuth = 0;
|
|
1982
|
-
if (Math.abs(vel.polar) < DEAD_ZONE) vel.polar = 0;
|
|
1977
|
+
return function update() {
|
|
1978
|
+
if (keys["z"] || keys["Z"]) vel.zoom -= ORBIT_ACCEL;
|
|
1979
|
+
if (keys["x"] || keys["X"]) vel.zoom += ORBIT_ACCEL;
|
|
1980
|
+
if (keys["ArrowLeft"]) vel.azimuth -= ORBIT_ACCEL;
|
|
1981
|
+
if (keys["ArrowRight"]) vel.azimuth += ORBIT_ACCEL;
|
|
1982
|
+
if (keys["ArrowUp"]) vel.polar -= ORBIT_ACCEL;
|
|
1983
|
+
if (keys["ArrowDown"]) vel.polar += ORBIT_ACCEL;
|
|
1984
|
+
vel.zoom = clamp(vel.zoom, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
|
|
1985
|
+
vel.azimuth = clamp(vel.azimuth, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
|
|
1986
|
+
vel.polar = clamp(vel.polar, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
|
|
1987
|
+
vel.zoom *= ORBIT_DAMPING;
|
|
1988
|
+
vel.azimuth *= ORBIT_DAMPING;
|
|
1989
|
+
vel.polar *= ORBIT_DAMPING;
|
|
1990
|
+
if (Math.abs(vel.zoom) < ORBIT_DEAD_ZONE) vel.zoom = 0;
|
|
1991
|
+
if (Math.abs(vel.azimuth) < ORBIT_DEAD_ZONE) vel.azimuth = 0;
|
|
1992
|
+
if (Math.abs(vel.polar) < ORBIT_DEAD_ZONE) vel.polar = 0;
|
|
1983
1993
|
const hasMotion = vel.zoom !== 0 || vel.azimuth !== 0 || vel.polar !== 0;
|
|
1984
1994
|
if (!hasMotion) return false;
|
|
1985
1995
|
const target = controls.target;
|
|
1986
1996
|
_offset.copy(camera.position).sub(target);
|
|
1987
1997
|
let radius = _offset.length();
|
|
1988
1998
|
if (vel.zoom !== 0) {
|
|
1989
|
-
radius *= 1 + vel.zoom *
|
|
1999
|
+
radius *= 1 + vel.zoom * ORBIT_ZOOM_FACTOR * radius * 0.01;
|
|
1990
2000
|
radius = Math.max(controls.minDistance, Math.min(controls.maxDistance, radius));
|
|
1991
2001
|
}
|
|
1992
2002
|
if (vel.azimuth !== 0) {
|
|
@@ -2007,16 +2017,81 @@ function setupKeyboardControls(camera, controls, container) {
|
|
|
2007
2017
|
camera.lookAt(target);
|
|
2008
2018
|
controls.target.copy(target);
|
|
2009
2019
|
return true;
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
function createFlyUpdate(camera, controls, keys, state) {
|
|
2023
|
+
const vel = { thrust: 0, yaw: 0, pitch: 0 };
|
|
2024
|
+
return function update() {
|
|
2025
|
+
const speedMul = state.flySpeed;
|
|
2026
|
+
if (keys["z"] || keys["Z"]) vel.thrust += FLY_ACCEL * speedMul;
|
|
2027
|
+
if (keys["x"] || keys["X"]) vel.thrust -= FLY_ACCEL * speedMul;
|
|
2028
|
+
if (keys["a"] || keys["A"] || keys["ArrowLeft"]) vel.yaw += FLY_TURN_RATE;
|
|
2029
|
+
if (keys["d"] || keys["D"] || keys["ArrowRight"]) vel.yaw -= FLY_TURN_RATE;
|
|
2030
|
+
if (keys["w"] || keys["W"] || keys["ArrowUp"]) vel.pitch += FLY_TURN_RATE;
|
|
2031
|
+
if (keys["s"] || keys["S"] || keys["ArrowDown"]) vel.pitch -= FLY_TURN_RATE;
|
|
2032
|
+
const lookDist = camera.position.distanceTo(controls.target);
|
|
2033
|
+
const distScale = Math.max(0.3, lookDist * 3e-3);
|
|
2034
|
+
vel.thrust = clamp(vel.thrust, -FLY_MAX_THRUST * distScale * speedMul, FLY_MAX_THRUST * distScale * speedMul);
|
|
2035
|
+
vel.yaw = clamp(vel.yaw, -FLY_TURN_RATE * 2, FLY_TURN_RATE * 2);
|
|
2036
|
+
vel.pitch = clamp(vel.pitch, -FLY_TURN_RATE * 2, FLY_TURN_RATE * 2);
|
|
2037
|
+
vel.thrust *= FLY_FRICTION;
|
|
2038
|
+
vel.yaw *= FLY_FRICTION;
|
|
2039
|
+
vel.pitch *= FLY_FRICTION;
|
|
2040
|
+
if (Math.abs(vel.thrust) < FLY_DEAD_ZONE) vel.thrust = 0;
|
|
2041
|
+
if (Math.abs(vel.yaw) < FLY_DEAD_ZONE) vel.yaw = 0;
|
|
2042
|
+
if (Math.abs(vel.pitch) < FLY_DEAD_ZONE) vel.pitch = 0;
|
|
2043
|
+
const hasMotion = vel.thrust !== 0 || vel.yaw !== 0 || vel.pitch !== 0;
|
|
2044
|
+
if (!hasMotion) return false;
|
|
2045
|
+
if (vel.yaw !== 0) {
|
|
2046
|
+
const upDot = camera.up.dot(_worldUp);
|
|
2047
|
+
const yawSign = upDot < 0 ? -1 : 1;
|
|
2048
|
+
_q.setFromAxisAngle(_worldUp, vel.yaw * yawSign);
|
|
2049
|
+
camera.quaternion.premultiply(_q);
|
|
2050
|
+
camera.up.applyQuaternion(_q).normalize();
|
|
2051
|
+
}
|
|
2052
|
+
if (vel.pitch !== 0) {
|
|
2053
|
+
_right.setFromMatrixColumn(camera.matrixWorld, 0).normalize();
|
|
2054
|
+
_q.setFromAxisAngle(_right, vel.pitch);
|
|
2055
|
+
camera.quaternion.premultiply(_q);
|
|
2056
|
+
camera.up.applyQuaternion(_q).normalize();
|
|
2057
|
+
}
|
|
2058
|
+
if (vel.thrust !== 0) {
|
|
2059
|
+
camera.getWorldDirection(_dir);
|
|
2060
|
+
_dir.multiplyScalar(vel.thrust * distScale * 100);
|
|
2061
|
+
camera.position.add(_dir);
|
|
2062
|
+
controls.target.add(_dir);
|
|
2063
|
+
}
|
|
2064
|
+
return true;
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
function setupKeyboardControls(camera, controls, container, mode = "fly", flySpeed = 1) {
|
|
2068
|
+
const keys = {};
|
|
2069
|
+
const state = { flySpeed };
|
|
2070
|
+
function onKeyDown2(e) {
|
|
2071
|
+
var _a;
|
|
2072
|
+
const tag = (_a = e.target) == null ? void 0 : _a.tagName;
|
|
2073
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
2074
|
+
keys[e.key] = true;
|
|
2010
2075
|
}
|
|
2076
|
+
function onKeyUp(e) {
|
|
2077
|
+
keys[e.key] = false;
|
|
2078
|
+
}
|
|
2079
|
+
function onBlur() {
|
|
2080
|
+
for (const k of Object.keys(keys)) keys[k] = false;
|
|
2081
|
+
}
|
|
2082
|
+
container.addEventListener("keydown", onKeyDown2);
|
|
2083
|
+
container.addEventListener("keyup", onKeyUp);
|
|
2084
|
+
container.addEventListener("blur", onBlur);
|
|
2085
|
+
const updateFn = mode === "fly" ? createFlyUpdate(camera, controls, keys, state) : createOrbitUpdate(camera, controls, keys);
|
|
2011
2086
|
function cleanup() {
|
|
2012
2087
|
container.removeEventListener("keydown", onKeyDown2);
|
|
2013
2088
|
container.removeEventListener("keyup", onKeyUp);
|
|
2014
2089
|
container.removeEventListener("blur", onBlur);
|
|
2015
2090
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
return
|
|
2091
|
+
function setFlySpeed(speed) {
|
|
2092
|
+
state.flySpeed = speed;
|
|
2093
|
+
}
|
|
2094
|
+
return { update: updateFn, cleanup, setFlySpeed };
|
|
2020
2095
|
}
|
|
2021
2096
|
function createScene(container, backgroundColor, style, rendererConfig) {
|
|
2022
2097
|
const scene = new THREE.Scene();
|
|
@@ -2053,7 +2128,8 @@ function createScene(container, backgroundColor, style, rendererConfig) {
|
|
|
2053
2128
|
scene.add(selectionRing);
|
|
2054
2129
|
const labels = createLabelSystem(style.maxLabels);
|
|
2055
2130
|
scene.add(labels.group);
|
|
2056
|
-
const
|
|
2131
|
+
const cameraMode = (rendererConfig == null ? void 0 : rendererConfig.cameraMode) ?? "fly";
|
|
2132
|
+
const keyboard = setupKeyboardControls(camera, controls, container, cameraMode, style.flySpeed ?? 1);
|
|
2057
2133
|
return {
|
|
2058
2134
|
scene,
|
|
2059
2135
|
camera,
|
|
@@ -2392,21 +2468,136 @@ function zoomToFitPositions(camera, controls, positions, duration, padding) {
|
|
|
2392
2468
|
duration
|
|
2393
2469
|
);
|
|
2394
2470
|
}
|
|
2395
|
-
|
|
2471
|
+
const EDGE_HIT_THRESHOLD = 5;
|
|
2472
|
+
function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, callbacks, container, getEdgeData) {
|
|
2396
2473
|
const raycaster = new THREE.Raycaster();
|
|
2397
2474
|
const mouse = new THREE.Vector2();
|
|
2398
2475
|
let mouseDownPos = null;
|
|
2399
2476
|
let lastClickTime = 0;
|
|
2400
2477
|
let lastClickNodeId = null;
|
|
2401
2478
|
let singleClickTimer = null;
|
|
2479
|
+
let dragNode = null;
|
|
2480
|
+
let isDragging = false;
|
|
2481
|
+
const dragPlane = new THREE.Plane();
|
|
2482
|
+
const dragIntersect = new THREE.Vector3();
|
|
2402
2483
|
function onPointerDown2(e) {
|
|
2403
2484
|
mouseDownPos = { x: e.clientX, y: e.clientY };
|
|
2404
2485
|
if (container && document.activeElement !== container) {
|
|
2405
2486
|
container.focus({ preventScroll: true });
|
|
2406
2487
|
}
|
|
2488
|
+
if (callbacks.onNodeDrag) {
|
|
2489
|
+
const nodesMesh = getNodesMesh();
|
|
2490
|
+
if (nodesMesh) {
|
|
2491
|
+
const rect = canvas.getBoundingClientRect();
|
|
2492
|
+
mouse.x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
2493
|
+
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
2494
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2495
|
+
const hits = raycaster.intersectObject(nodesMesh);
|
|
2496
|
+
if (hits.length > 0 && hits[0].instanceId != null) {
|
|
2497
|
+
const nodes = getNodes();
|
|
2498
|
+
dragNode = nodes[hits[0].instanceId] ?? null;
|
|
2499
|
+
hits[0].instanceId;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
function onPointerMove2(e) {
|
|
2505
|
+
var _a, _b, _c;
|
|
2506
|
+
if (dragNode && mouseDownPos && callbacks.onNodeDrag) {
|
|
2507
|
+
const dx = Math.abs(e.clientX - mouseDownPos.x);
|
|
2508
|
+
const dy = Math.abs(e.clientY - mouseDownPos.y);
|
|
2509
|
+
if (dx + dy > 5) {
|
|
2510
|
+
isDragging = true;
|
|
2511
|
+
controls.enabled = false;
|
|
2512
|
+
const rect2 = canvas.getBoundingClientRect();
|
|
2513
|
+
mouse.x = (e.clientX - rect2.left) / rect2.width * 2 - 1;
|
|
2514
|
+
mouse.y = -((e.clientY - rect2.top) / rect2.height) * 2 + 1;
|
|
2515
|
+
const nodePos = new THREE.Vector3(
|
|
2516
|
+
dragNode.x ?? 0,
|
|
2517
|
+
dragNode.y ?? 0,
|
|
2518
|
+
dragNode.z ?? 0
|
|
2519
|
+
);
|
|
2520
|
+
dragPlane.setFromNormalAndCoplanarPoint(
|
|
2521
|
+
camera.getWorldDirection(new THREE.Vector3()).negate(),
|
|
2522
|
+
nodePos
|
|
2523
|
+
);
|
|
2524
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2525
|
+
if (raycaster.ray.intersectPlane(dragPlane, dragIntersect)) {
|
|
2526
|
+
callbacks.onNodeDrag(dragNode, {
|
|
2527
|
+
x: dragIntersect.x,
|
|
2528
|
+
y: dragIntersect.y,
|
|
2529
|
+
z: dragIntersect.z
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
if (!callbacks.onNodeHover && !callbacks.onLinkHover) return;
|
|
2536
|
+
const nodesMesh = getNodesMesh();
|
|
2537
|
+
if (!nodesMesh) return;
|
|
2538
|
+
const rect = canvas.getBoundingClientRect();
|
|
2539
|
+
mouse.x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
2540
|
+
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
2541
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2542
|
+
const hits = raycaster.intersectObject(nodesMesh);
|
|
2543
|
+
if (hits.length > 0 && hits[0].instanceId != null) {
|
|
2544
|
+
const nodes = getNodes();
|
|
2545
|
+
const node = nodes[hits[0].instanceId];
|
|
2546
|
+
if (node && node.id !== lastHoveredId) {
|
|
2547
|
+
lastHoveredId = node.id;
|
|
2548
|
+
lastHoveredLinkIdx = -1;
|
|
2549
|
+
canvas.style.cursor = "pointer";
|
|
2550
|
+
(_a = callbacks.onNodeHover) == null ? void 0 : _a.call(callbacks, node);
|
|
2551
|
+
(_b = callbacks.onLinkHover) == null ? void 0 : _b.call(callbacks, null);
|
|
2552
|
+
}
|
|
2553
|
+
} else {
|
|
2554
|
+
if (lastHoveredId !== null) {
|
|
2555
|
+
lastHoveredId = null;
|
|
2556
|
+
canvas.style.cursor = "default";
|
|
2557
|
+
(_c = callbacks.onNodeHover) == null ? void 0 : _c.call(callbacks, null);
|
|
2558
|
+
}
|
|
2559
|
+
if (callbacks.onLinkHover && getEdgeData) {
|
|
2560
|
+
const edgeData = getEdgeData();
|
|
2561
|
+
if (edgeData == null ? void 0 : edgeData.positions) {
|
|
2562
|
+
const hitLink = findNearestEdge(
|
|
2563
|
+
e.clientX,
|
|
2564
|
+
e.clientY,
|
|
2565
|
+
rect,
|
|
2566
|
+
camera,
|
|
2567
|
+
edgeData
|
|
2568
|
+
);
|
|
2569
|
+
if (hitLink) {
|
|
2570
|
+
if (lastHoveredLinkIdx !== hitLink.index) {
|
|
2571
|
+
lastHoveredLinkIdx = hitLink.index;
|
|
2572
|
+
canvas.style.cursor = "pointer";
|
|
2573
|
+
callbacks.onLinkHover(hitLink.link);
|
|
2574
|
+
}
|
|
2575
|
+
} else if (lastHoveredLinkIdx !== -1) {
|
|
2576
|
+
lastHoveredLinkIdx = -1;
|
|
2577
|
+
canvas.style.cursor = "default";
|
|
2578
|
+
callbacks.onLinkHover(null);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2407
2583
|
}
|
|
2408
2584
|
function onPointerUp2(e) {
|
|
2409
2585
|
var _a;
|
|
2586
|
+
if (isDragging && dragNode && callbacks.onNodeDragEnd) {
|
|
2587
|
+
callbacks.onNodeDragEnd(dragNode, {
|
|
2588
|
+
x: dragNode.x ?? 0,
|
|
2589
|
+
y: dragNode.y ?? 0,
|
|
2590
|
+
z: dragNode.z ?? 0
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
if (isDragging) {
|
|
2594
|
+
isDragging = false;
|
|
2595
|
+
dragNode = null;
|
|
2596
|
+
controls.enabled = true;
|
|
2597
|
+
mouseDownPos = null;
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
dragNode = null;
|
|
2410
2601
|
if (!mouseDownPos) return;
|
|
2411
2602
|
const dx = Math.abs(e.clientX - mouseDownPos.x);
|
|
2412
2603
|
const dy = Math.abs(e.clientY - mouseDownPos.y);
|
|
@@ -2454,6 +2645,22 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2454
2645
|
callbacks.onNodeClick(node);
|
|
2455
2646
|
}, 250);
|
|
2456
2647
|
} else {
|
|
2648
|
+
if (callbacks.onLinkClick && getEdgeData) {
|
|
2649
|
+
const edgeData = getEdgeData();
|
|
2650
|
+
if (edgeData == null ? void 0 : edgeData.positions) {
|
|
2651
|
+
const hitLink = findNearestEdge(
|
|
2652
|
+
e.clientX,
|
|
2653
|
+
e.clientY,
|
|
2654
|
+
rect,
|
|
2655
|
+
camera,
|
|
2656
|
+
edgeData
|
|
2657
|
+
);
|
|
2658
|
+
if (hitLink) {
|
|
2659
|
+
callbacks.onLinkClick(hitLink.link);
|
|
2660
|
+
return;
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2457
2664
|
if (singleClickTimer) clearTimeout(singleClickTimer);
|
|
2458
2665
|
singleClickTimer = null;
|
|
2459
2666
|
lastClickTime = 0;
|
|
@@ -2461,13 +2668,9 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2461
2668
|
callbacks.onNodeClick(null);
|
|
2462
2669
|
}
|
|
2463
2670
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
}
|
|
2468
|
-
let lastHoveredId = null;
|
|
2469
|
-
function onPointerMove2(e) {
|
|
2470
|
-
if (!callbacks.onNodeHover) return;
|
|
2671
|
+
function onContextMenu2(e) {
|
|
2672
|
+
if (!callbacks.onContextMenu) return;
|
|
2673
|
+
e.preventDefault();
|
|
2471
2674
|
const nodesMesh = getNodesMesh();
|
|
2472
2675
|
if (!nodesMesh) return;
|
|
2473
2676
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -2478,20 +2681,21 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2478
2681
|
if (hits.length > 0 && hits[0].instanceId != null) {
|
|
2479
2682
|
const nodes = getNodes();
|
|
2480
2683
|
const node = nodes[hits[0].instanceId];
|
|
2481
|
-
if (node
|
|
2482
|
-
|
|
2483
|
-
canvas.style.cursor = "pointer";
|
|
2484
|
-
callbacks.onNodeHover(node);
|
|
2684
|
+
if (node) {
|
|
2685
|
+
callbacks.onContextMenu(node, { x: e.clientX, y: e.clientY });
|
|
2485
2686
|
}
|
|
2486
|
-
} else if (lastHoveredId !== null) {
|
|
2487
|
-
lastHoveredId = null;
|
|
2488
|
-
canvas.style.cursor = "default";
|
|
2489
|
-
callbacks.onNodeHover(null);
|
|
2490
2687
|
}
|
|
2491
2688
|
}
|
|
2689
|
+
const keyTarget = container ?? window;
|
|
2690
|
+
function onKeyDown2(e) {
|
|
2691
|
+
if (e.key === "Escape") callbacks.onNodeClick(null);
|
|
2692
|
+
}
|
|
2693
|
+
let lastHoveredId = null;
|
|
2694
|
+
let lastHoveredLinkIdx = -1;
|
|
2492
2695
|
canvas.addEventListener("pointerdown", onPointerDown2);
|
|
2493
2696
|
canvas.addEventListener("pointerup", onPointerUp2);
|
|
2494
2697
|
canvas.addEventListener("pointermove", onPointerMove2);
|
|
2698
|
+
canvas.addEventListener("contextmenu", onContextMenu2);
|
|
2495
2699
|
keyTarget.addEventListener("keydown", onKeyDown2);
|
|
2496
2700
|
return {
|
|
2497
2701
|
cleanup: () => {
|
|
@@ -2499,10 +2703,48 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2499
2703
|
canvas.removeEventListener("pointerdown", onPointerDown2);
|
|
2500
2704
|
canvas.removeEventListener("pointerup", onPointerUp2);
|
|
2501
2705
|
canvas.removeEventListener("pointermove", onPointerMove2);
|
|
2706
|
+
canvas.removeEventListener("contextmenu", onContextMenu2);
|
|
2502
2707
|
keyTarget.removeEventListener("keydown", onKeyDown2);
|
|
2503
2708
|
}
|
|
2504
2709
|
};
|
|
2505
2710
|
}
|
|
2711
|
+
function findNearestEdge(clientX, clientY, rect, camera, edgeData) {
|
|
2712
|
+
const { links, edgeNodeIndices, edgeLinkIndices, positions } = edgeData;
|
|
2713
|
+
const screenX = clientX - rect.left;
|
|
2714
|
+
const screenY = clientY - rect.top;
|
|
2715
|
+
const w = rect.width;
|
|
2716
|
+
const h = rect.height;
|
|
2717
|
+
let bestDist = EDGE_HIT_THRESHOLD;
|
|
2718
|
+
let bestIdx = -1;
|
|
2719
|
+
const tmpVec = new THREE.Vector3();
|
|
2720
|
+
for (let i = 0; i < edgeNodeIndices.length; i++) {
|
|
2721
|
+
const [si, ti] = edgeNodeIndices[i];
|
|
2722
|
+
tmpVec.set(positions[si * 3], positions[si * 3 + 1], positions[si * 3 + 2]);
|
|
2723
|
+
tmpVec.project(camera);
|
|
2724
|
+
const sx1 = (tmpVec.x * 0.5 + 0.5) * w;
|
|
2725
|
+
const sy1 = (-tmpVec.y * 0.5 + 0.5) * h;
|
|
2726
|
+
tmpVec.set(positions[ti * 3], positions[ti * 3 + 1], positions[ti * 3 + 2]);
|
|
2727
|
+
tmpVec.project(camera);
|
|
2728
|
+
const sx2 = (tmpVec.x * 0.5 + 0.5) * w;
|
|
2729
|
+
const sy2 = (-tmpVec.y * 0.5 + 0.5) * h;
|
|
2730
|
+
const dist = pointToSegmentDist(screenX, screenY, sx1, sy1, sx2, sy2);
|
|
2731
|
+
if (dist < bestDist) {
|
|
2732
|
+
bestDist = dist;
|
|
2733
|
+
bestIdx = i;
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
if (bestIdx === -1) return null;
|
|
2737
|
+
return { link: links[edgeLinkIndices[bestIdx]], index: bestIdx };
|
|
2738
|
+
}
|
|
2739
|
+
function pointToSegmentDist(px, py, ax, ay, bx, by) {
|
|
2740
|
+
const dx = bx - ax;
|
|
2741
|
+
const dy = by - ay;
|
|
2742
|
+
const lenSq = dx * dx + dy * dy;
|
|
2743
|
+
if (lenSq < 1e-4) return Math.hypot(px - ax, py - ay);
|
|
2744
|
+
let t = ((px - ax) * dx + (py - ay) * dy) / lenSq;
|
|
2745
|
+
t = Math.max(0, Math.min(1, t));
|
|
2746
|
+
return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));
|
|
2747
|
+
}
|
|
2506
2748
|
function disposeObject(obj) {
|
|
2507
2749
|
if (!obj) return;
|
|
2508
2750
|
const mesh = obj;
|
|
@@ -2524,6 +2766,13 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2524
2766
|
onNodeClick,
|
|
2525
2767
|
onNodeDoubleClick,
|
|
2526
2768
|
onNodeHover,
|
|
2769
|
+
onContextMenu: onContextMenu2,
|
|
2770
|
+
onLinkClick,
|
|
2771
|
+
onLinkHover,
|
|
2772
|
+
onNodeDrag,
|
|
2773
|
+
onNodeDragEnd,
|
|
2774
|
+
onLayoutSettled,
|
|
2775
|
+
onLayoutTick,
|
|
2527
2776
|
selectedNodeId = null,
|
|
2528
2777
|
highlightHops = 3,
|
|
2529
2778
|
theme: themeProp,
|
|
@@ -2556,13 +2805,28 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2556
2805
|
const onNodeClickRef = useRef(onNodeClick);
|
|
2557
2806
|
const onNodeDoubleClickRef = useRef(onNodeDoubleClick);
|
|
2558
2807
|
const onNodeHoverRef = useRef(onNodeHover);
|
|
2808
|
+
const onContextMenuRef = useRef(onContextMenu2);
|
|
2809
|
+
const onLinkClickRef = useRef(onLinkClick);
|
|
2810
|
+
const onLinkHoverRef = useRef(onLinkHover);
|
|
2811
|
+
const onNodeDragRef = useRef(onNodeDrag);
|
|
2812
|
+
const onNodeDragEndRef = useRef(onNodeDragEnd);
|
|
2813
|
+
const onLayoutSettledRef = useRef(onLayoutSettled);
|
|
2814
|
+
const onLayoutTickRef = useRef(onLayoutTick);
|
|
2559
2815
|
const styleRef = useRef(resolvedStyle);
|
|
2560
2816
|
const themeRef = useRef(resolvedTheme);
|
|
2561
2817
|
const selectedNodeIdRef = useRef(selectedNodeId);
|
|
2562
2818
|
const labelFormatterRef = useRef(labelFormatter);
|
|
2819
|
+
const highlightSetRef = useRef(null);
|
|
2563
2820
|
onNodeClickRef.current = onNodeClick;
|
|
2564
2821
|
onNodeDoubleClickRef.current = onNodeDoubleClick;
|
|
2565
2822
|
onNodeHoverRef.current = onNodeHover;
|
|
2823
|
+
onContextMenuRef.current = onContextMenu2;
|
|
2824
|
+
onLinkClickRef.current = onLinkClick;
|
|
2825
|
+
onLinkHoverRef.current = onLinkHover;
|
|
2826
|
+
onNodeDragRef.current = onNodeDrag;
|
|
2827
|
+
onNodeDragEndRef.current = onNodeDragEnd;
|
|
2828
|
+
onLayoutSettledRef.current = onLayoutSettled;
|
|
2829
|
+
onLayoutTickRef.current = onLayoutTick;
|
|
2566
2830
|
styleRef.current = resolvedStyle;
|
|
2567
2831
|
themeRef.current = resolvedTheme;
|
|
2568
2832
|
selectedNodeIdRef.current = selectedNodeId;
|
|
@@ -2628,7 +2892,8 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2628
2892
|
styleRef.current.labelScale,
|
|
2629
2893
|
styleRef.current.labelThreshold,
|
|
2630
2894
|
styleRef.current.maxLabels,
|
|
2631
|
-
labelFormatterRef.current
|
|
2895
|
+
labelFormatterRef.current,
|
|
2896
|
+
highlightSetRef.current
|
|
2632
2897
|
);
|
|
2633
2898
|
});
|
|
2634
2899
|
const resizeObserver = setupResize(container, state);
|
|
@@ -2653,9 +2918,52 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2653
2918
|
onNodeHover: (node) => {
|
|
2654
2919
|
var _a;
|
|
2655
2920
|
return (_a = onNodeHoverRef.current) == null ? void 0 : _a.call(onNodeHoverRef, node);
|
|
2921
|
+
},
|
|
2922
|
+
onContextMenu: (node, pos) => {
|
|
2923
|
+
var _a;
|
|
2924
|
+
return (_a = onContextMenuRef.current) == null ? void 0 : _a.call(onContextMenuRef, node, pos);
|
|
2925
|
+
},
|
|
2926
|
+
onLinkClick: (link) => {
|
|
2927
|
+
var _a;
|
|
2928
|
+
return (_a = onLinkClickRef.current) == null ? void 0 : _a.call(onLinkClickRef, link);
|
|
2929
|
+
},
|
|
2930
|
+
onLinkHover: (link) => {
|
|
2931
|
+
var _a;
|
|
2932
|
+
return (_a = onLinkHoverRef.current) == null ? void 0 : _a.call(onLinkHoverRef, link);
|
|
2933
|
+
},
|
|
2934
|
+
onNodeDrag: (node, pos) => {
|
|
2935
|
+
var _a;
|
|
2936
|
+
(_a = onNodeDragRef.current) == null ? void 0 : _a.call(onNodeDragRef, node, pos);
|
|
2937
|
+
const d = dataRef.current;
|
|
2938
|
+
const idx = d.nodeIdToIndex.get(node.id);
|
|
2939
|
+
if (idx !== void 0 && d.positions) {
|
|
2940
|
+
d.positions[idx * 3] = pos.x;
|
|
2941
|
+
d.positions[idx * 3 + 1] = pos.y;
|
|
2942
|
+
d.positions[idx * 3 + 2] = pos.z;
|
|
2943
|
+
node.x = pos.x;
|
|
2944
|
+
node.y = pos.y;
|
|
2945
|
+
node.z = pos.z;
|
|
2946
|
+
const gObj = graphObjRef.current;
|
|
2947
|
+
if (gObj && d.scales) {
|
|
2948
|
+
updateNodePositions(gObj.nodesMesh, d.positions, d.scales, d.nodes.length);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
},
|
|
2952
|
+
onNodeDragEnd: (node, pos) => {
|
|
2953
|
+
var _a;
|
|
2954
|
+
return (_a = onNodeDragEndRef.current) == null ? void 0 : _a.call(onNodeDragEndRef, node, pos);
|
|
2656
2955
|
}
|
|
2657
2956
|
},
|
|
2658
|
-
container
|
|
2957
|
+
container,
|
|
2958
|
+
() => {
|
|
2959
|
+
const d = dataRef.current;
|
|
2960
|
+
return {
|
|
2961
|
+
links: d.links,
|
|
2962
|
+
edgeNodeIndices: d.edgeNodeIndices,
|
|
2963
|
+
edgeLinkIndices: d.edgeLinkIndices,
|
|
2964
|
+
positions: d.positions
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2659
2967
|
);
|
|
2660
2968
|
return () => {
|
|
2661
2969
|
cancelAnim();
|
|
@@ -2795,6 +3103,7 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2795
3103
|
console.error("Graphier: Layout worker error:", err);
|
|
2796
3104
|
};
|
|
2797
3105
|
worker.onmessage = (e) => {
|
|
3106
|
+
var _a, _b;
|
|
2798
3107
|
const msg = e.data;
|
|
2799
3108
|
if (msg.type === "positions") {
|
|
2800
3109
|
const positions = new Float32Array(msg.positions);
|
|
@@ -2822,9 +3131,11 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2822
3131
|
);
|
|
2823
3132
|
}
|
|
2824
3133
|
}
|
|
3134
|
+
(_a = onLayoutTickRef.current) == null ? void 0 : _a.call(onLayoutTickRef, msg.alpha);
|
|
2825
3135
|
}
|
|
2826
3136
|
if (msg.type === "settled") {
|
|
2827
3137
|
dataRef.current.settled = true;
|
|
3138
|
+
(_b = onLayoutSettledRef.current) == null ? void 0 : _b.call(onLayoutSettledRef);
|
|
2828
3139
|
}
|
|
2829
3140
|
};
|
|
2830
3141
|
const layoutParams = resolveLayoutParams(nc, layoutProp);
|
|
@@ -2869,6 +3180,7 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2869
3180
|
const d = dataRef.current;
|
|
2870
3181
|
const theme = themeRef.current;
|
|
2871
3182
|
const style = styleRef.current;
|
|
3183
|
+
highlightSetRef.current = highlightSet;
|
|
2872
3184
|
applyNodeHighlight(
|
|
2873
3185
|
gObj.nodesMesh,
|
|
2874
3186
|
d.nodes,
|
|
@@ -2886,6 +3198,18 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2886
3198
|
d.nodes.length,
|
|
2887
3199
|
theme
|
|
2888
3200
|
);
|
|
3201
|
+
if (sceneState.bloomPass) {
|
|
3202
|
+
const style2 = styleRef.current;
|
|
3203
|
+
if (highlightSet) {
|
|
3204
|
+
sceneState.bloomPass.strength = style2.bloomStrength * 1.8;
|
|
3205
|
+
sceneState.bloomPass.radius = style2.bloomRadius * 1.4;
|
|
3206
|
+
sceneState.bloomPass.threshold = 0.25;
|
|
3207
|
+
} else {
|
|
3208
|
+
sceneState.bloomPass.strength = style2.bloomStrength;
|
|
3209
|
+
sceneState.bloomPass.radius = style2.bloomRadius;
|
|
3210
|
+
sceneState.bloomPass.threshold = style2.bloomThreshold;
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
2889
3213
|
const selectedNode = selectedNodeId ? d.nodes.find((n) => n.id === selectedNodeId) : null;
|
|
2890
3214
|
updateSelectionRing(
|
|
2891
3215
|
sceneState.selectionRing,
|
|
@@ -2961,6 +3285,11 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2961
3285
|
}, 30);
|
|
2962
3286
|
return () => clearInterval(id);
|
|
2963
3287
|
}, [resolvedStyle.autoOrbit]);
|
|
3288
|
+
useEffect(() => {
|
|
3289
|
+
const s = sceneRef.current;
|
|
3290
|
+
if (!s) return;
|
|
3291
|
+
s.keyboard.setFlySpeed(resolvedStyle.flySpeed);
|
|
3292
|
+
}, [resolvedStyle.flySpeed]);
|
|
2964
3293
|
useImperativeHandle(ref, () => ({
|
|
2965
3294
|
cameraPosition(pos, lookAt, duration = 1e3) {
|
|
2966
3295
|
const s = sceneRef.current;
|
|
@@ -3033,13 +3362,21 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
3033
3362
|
var _a;
|
|
3034
3363
|
return ((_a = sceneRef.current) == null ? void 0 : _a.camera) ?? null;
|
|
3035
3364
|
},
|
|
3036
|
-
|
|
3365
|
+
captureScreenshot() {
|
|
3037
3366
|
const s = sceneRef.current;
|
|
3038
3367
|
if (!s) return null;
|
|
3039
3368
|
s.renderer.render(s.scene, s.camera);
|
|
3040
|
-
return
|
|
3041
|
-
|
|
3042
|
-
|
|
3369
|
+
return s.renderer.domElement.toDataURL("image/png");
|
|
3370
|
+
},
|
|
3371
|
+
reheatLayout() {
|
|
3372
|
+
const gObj = graphObjRef.current;
|
|
3373
|
+
if (gObj == null ? void 0 : gObj.worker) {
|
|
3374
|
+
gObj.worker.postMessage({ type: "stop" });
|
|
3375
|
+
gObj.worker.terminate();
|
|
3376
|
+
gObj.worker = null;
|
|
3377
|
+
}
|
|
3378
|
+
dataRef.current.settled = false;
|
|
3379
|
+
setAppendedData((prev) => prev ? { ...prev } : { nodes: [], links: [] });
|
|
3043
3380
|
}
|
|
3044
3381
|
}));
|
|
3045
3382
|
return /* @__PURE__ */ jsx(
|