@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.cjs
CHANGED
|
@@ -34,7 +34,8 @@ const DEFAULT_STYLE = {
|
|
|
34
34
|
labelThreshold: 0.8,
|
|
35
35
|
showLabels: true,
|
|
36
36
|
maxLabels: 150,
|
|
37
|
-
edgeWidthScale: 1
|
|
37
|
+
edgeWidthScale: 1,
|
|
38
|
+
flySpeed: 1
|
|
38
39
|
};
|
|
39
40
|
const DEFAULT_LAYOUT = {
|
|
40
41
|
type: "force-3d",
|
|
@@ -42,7 +43,8 @@ const DEFAULT_LAYOUT = {
|
|
|
42
43
|
linkDistance: "auto",
|
|
43
44
|
alphaDecay: "auto",
|
|
44
45
|
velocityDecay: 0.4,
|
|
45
|
-
settledThreshold: 5e-3
|
|
46
|
+
settledThreshold: 5e-3,
|
|
47
|
+
spreadFactor: "auto"
|
|
46
48
|
};
|
|
47
49
|
function hexToRgb(hex) {
|
|
48
50
|
const c2 = hex.replace("#", "");
|
|
@@ -188,17 +190,26 @@ function resolveTheme(theme) {
|
|
|
188
190
|
}
|
|
189
191
|
return { nodeColor, nodeColorBright, linkColor, backgroundColor };
|
|
190
192
|
}
|
|
193
|
+
function autoSpreadFactor(n) {
|
|
194
|
+
if (n <= 500) return 1;
|
|
195
|
+
if (n <= 2e3) return 1 + (n - 500) / 1500 * 0.3;
|
|
196
|
+
if (n <= 1e4) return 1.3 + (n - 2e3) / 8e3 * 0.5;
|
|
197
|
+
return Math.min(2.5, 1.8 + (n - 1e4) / 4e4 * 0.7);
|
|
198
|
+
}
|
|
191
199
|
function resolveLayoutParams(nodeCount, config) {
|
|
192
200
|
const n = nodeCount;
|
|
193
|
-
const
|
|
194
|
-
const
|
|
201
|
+
const spread = (config == null ? void 0 : config.spreadFactor) !== "auto" && (config == null ? void 0 : config.spreadFactor) != null ? config.spreadFactor : autoSpreadFactor(n);
|
|
202
|
+
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;
|
|
203
|
+
const charge = baseCharge * spread;
|
|
204
|
+
const distanceMax = (n > 5e4 ? 1500 : n > 1e4 ? 2e3 : 3e3) * spread;
|
|
195
205
|
const theta = n > 2e4 ? 1.5 : n > 5e3 ? 1.2 : 0.9;
|
|
196
|
-
const
|
|
206
|
+
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;
|
|
207
|
+
const linkDistance = baseLinkDistance * spread;
|
|
197
208
|
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;
|
|
198
209
|
const velocityDecay = (config == null ? void 0 : config.velocityDecay) ?? 0.4;
|
|
199
210
|
const settledThreshold = (config == null ? void 0 : config.settledThreshold) ?? 5e-3;
|
|
200
211
|
const postEvery = n > 5e4 ? 5 : n > 1e4 ? 3 : 2;
|
|
201
|
-
const initialRadius2 = 500 + Math.min(n, 1e4) * 0.1;
|
|
212
|
+
const initialRadius2 = (500 + Math.min(n, 1e4) * 0.1) * spread;
|
|
202
213
|
return {
|
|
203
214
|
charge,
|
|
204
215
|
distanceMax,
|
|
@@ -1721,27 +1732,31 @@ function applyNodeHighlight(nodesMesh, nodes, selectedNodeId, highlightSet, them
|
|
|
1721
1732
|
} else if (node.id === selectedNodeId) {
|
|
1722
1733
|
tmpColor.set(brightColor);
|
|
1723
1734
|
brightTmp.setRGB(1, 1, 1);
|
|
1724
|
-
tmpColor.lerp(brightTmp, 0.
|
|
1735
|
+
tmpColor.lerp(brightTmp, 0.65);
|
|
1736
|
+
tmpColor.multiplyScalar(1.4);
|
|
1737
|
+
tmpColor.r = Math.min(tmpColor.r, 2);
|
|
1738
|
+
tmpColor.g = Math.min(tmpColor.g, 2);
|
|
1739
|
+
tmpColor.b = Math.min(tmpColor.b, 2);
|
|
1725
1740
|
} else if (highlightSet.has(node.id)) {
|
|
1726
1741
|
const hop = highlightSet.get(node.id);
|
|
1727
1742
|
if (hop === 1) {
|
|
1728
|
-
tmpColor.set(brightColor);
|
|
1743
|
+
tmpColor.set(brightColor).multiplyScalar(1.2);
|
|
1729
1744
|
} else if (hop === 2) {
|
|
1730
1745
|
tmpColor.set(baseColor);
|
|
1731
1746
|
brightTmp.set(brightColor);
|
|
1732
|
-
tmpColor.lerp(brightTmp, 0.
|
|
1747
|
+
tmpColor.lerp(brightTmp, 0.5);
|
|
1733
1748
|
} else {
|
|
1734
|
-
tmpColor.set(baseColor).multiplyScalar(0.
|
|
1749
|
+
tmpColor.set(baseColor).multiplyScalar(0.6);
|
|
1735
1750
|
}
|
|
1736
1751
|
} else {
|
|
1737
|
-
tmpColor.set(baseColor).multiplyScalar(0.
|
|
1752
|
+
tmpColor.set(baseColor).multiplyScalar(0.04);
|
|
1738
1753
|
}
|
|
1739
1754
|
nodesMesh.setColorAt(i, tmpColor);
|
|
1740
1755
|
}
|
|
1741
1756
|
if (nodesMesh.instanceColor) nodesMesh.instanceColor.needsUpdate = true;
|
|
1742
1757
|
const mat = nodesMesh.material;
|
|
1743
1758
|
if ((_a = mat.uniforms) == null ? void 0 : _a.uGlowIntensity) {
|
|
1744
|
-
mat.uniforms.uGlowIntensity.value = highlightSet ? 1.
|
|
1759
|
+
mat.uniforms.uGlowIntensity.value = highlightSet ? 1.5 : 1;
|
|
1745
1760
|
}
|
|
1746
1761
|
}
|
|
1747
1762
|
function applyEdgeHighlight(edgesMesh, links, edgeNodeIndices, edgeLinkIndices, highlightSet, edgeOpacity, nodeCount, theme) {
|
|
@@ -1769,7 +1784,7 @@ function applyEdgeHighlight(edgesMesh, links, edgeNodeIndices, edgeLinkIndices,
|
|
|
1769
1784
|
tmpColor.multiplyScalar(1.2);
|
|
1770
1785
|
}
|
|
1771
1786
|
} else {
|
|
1772
|
-
tmpColor.setRGB(0.
|
|
1787
|
+
tmpColor.setRGB(0.01, 0.01, 0.02);
|
|
1773
1788
|
}
|
|
1774
1789
|
colorArr[i * 6 + 0] = tmpColor.r;
|
|
1775
1790
|
colorArr[i * 6 + 1] = tmpColor.g;
|
|
@@ -1856,6 +1871,9 @@ function createLabelSystem(maxLabels) {
|
|
|
1856
1871
|
}
|
|
1857
1872
|
function defaultLabelText$1(node) {
|
|
1858
1873
|
const label = node.label ?? node.id ?? "";
|
|
1874
|
+
if (node.type === "repo" && label.includes("/")) {
|
|
1875
|
+
return label.split("/").pop();
|
|
1876
|
+
}
|
|
1859
1877
|
return label.length > 30 ? label.substring(0, 27) + "…" : label;
|
|
1860
1878
|
}
|
|
1861
1879
|
function getOrCreateTexture(cache, nodeId, text, color) {
|
|
@@ -1894,7 +1912,7 @@ function getOrCreateTexture(cache, nodeId, text, color) {
|
|
|
1894
1912
|
}
|
|
1895
1913
|
return entry;
|
|
1896
1914
|
}
|
|
1897
|
-
function updateLabels(state, nodes, positions, scales, camera, theme, showLabels, labelScale, labelThreshold, maxLabels, labelFormatter) {
|
|
1915
|
+
function updateLabels(state, nodes, positions, scales, camera, theme, showLabels, labelScale, labelThreshold, maxLabels, labelFormatter, highlightSet) {
|
|
1898
1916
|
if (!positions || nodes.length === 0 || !showLabels) {
|
|
1899
1917
|
for (const sp of state.sprites) sp.visible = false;
|
|
1900
1918
|
return;
|
|
@@ -1908,6 +1926,7 @@ function updateLabels(state, nodes, positions, scales, camera, theme, showLabels
|
|
|
1908
1926
|
const maxDistSq = maxDist * maxDist;
|
|
1909
1927
|
const candidates = [];
|
|
1910
1928
|
for (let i = 0; i < nc; i++) {
|
|
1929
|
+
if (highlightSet && !highlightSet.has(nodes[i].id)) continue;
|
|
1911
1930
|
const dx = positions[i * 3] - camPos.x;
|
|
1912
1931
|
const dy = positions[i * 3 + 1] - camPos.y;
|
|
1913
1932
|
const dz = positions[i * 3 + 2] - camPos.z;
|
|
@@ -1953,58 +1972,49 @@ function disposeLabelSystem(state) {
|
|
|
1953
1972
|
}
|
|
1954
1973
|
state.textureCache.clear();
|
|
1955
1974
|
}
|
|
1956
|
-
const
|
|
1957
|
-
const
|
|
1958
|
-
const
|
|
1959
|
-
const
|
|
1960
|
-
const
|
|
1975
|
+
const ORBIT_ACCEL = 12e-4;
|
|
1976
|
+
const ORBIT_MAX_SPEED = 0.06;
|
|
1977
|
+
const ORBIT_DAMPING = 0.88;
|
|
1978
|
+
const ORBIT_ZOOM_FACTOR = 0.015;
|
|
1979
|
+
const ORBIT_DEAD_ZONE = 1e-4;
|
|
1980
|
+
const FLY_ACCEL = 6e-3;
|
|
1981
|
+
const FLY_FRICTION = 0.92;
|
|
1982
|
+
const FLY_MAX_THRUST = 0.18;
|
|
1983
|
+
const FLY_TURN_RATE = 8e-3;
|
|
1984
|
+
const FLY_DEAD_ZONE = 2e-4;
|
|
1961
1985
|
const _offset = new THREE__namespace.Vector3();
|
|
1962
1986
|
const _right = new THREE__namespace.Vector3();
|
|
1963
1987
|
const _q = new THREE__namespace.Quaternion();
|
|
1964
|
-
|
|
1965
|
-
|
|
1988
|
+
const _dir = new THREE__namespace.Vector3();
|
|
1989
|
+
const _worldUp = new THREE__namespace.Vector3(0, 1, 0);
|
|
1990
|
+
function clamp(v, min, max) {
|
|
1991
|
+
return v < min ? min : v > max ? max : v;
|
|
1992
|
+
}
|
|
1993
|
+
function createOrbitUpdate(camera, controls, keys) {
|
|
1966
1994
|
const vel = { zoom: 0, azimuth: 0, polar: 0 };
|
|
1967
|
-
function
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
if (
|
|
1971
|
-
keys[
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
vel.
|
|
1979
|
-
vel.
|
|
1980
|
-
vel.
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
container.addEventListener("keyup", onKeyUp);
|
|
1984
|
-
container.addEventListener("blur", onBlur);
|
|
1985
|
-
function update() {
|
|
1986
|
-
if (keys["z"] || keys["Z"]) vel.zoom -= ACCEL;
|
|
1987
|
-
if (keys["x"] || keys["X"]) vel.zoom += ACCEL;
|
|
1988
|
-
if (keys["ArrowLeft"]) vel.azimuth -= ACCEL;
|
|
1989
|
-
if (keys["ArrowRight"]) vel.azimuth += ACCEL;
|
|
1990
|
-
if (keys["ArrowUp"]) vel.polar -= ACCEL;
|
|
1991
|
-
if (keys["ArrowDown"]) vel.polar += ACCEL;
|
|
1992
|
-
vel.zoom = clamp(vel.zoom, -MAX_SPEED, MAX_SPEED);
|
|
1993
|
-
vel.azimuth = clamp(vel.azimuth, -MAX_SPEED, MAX_SPEED);
|
|
1994
|
-
vel.polar = clamp(vel.polar, -MAX_SPEED, MAX_SPEED);
|
|
1995
|
-
vel.zoom *= DAMPING;
|
|
1996
|
-
vel.azimuth *= DAMPING;
|
|
1997
|
-
vel.polar *= DAMPING;
|
|
1998
|
-
if (Math.abs(vel.zoom) < DEAD_ZONE) vel.zoom = 0;
|
|
1999
|
-
if (Math.abs(vel.azimuth) < DEAD_ZONE) vel.azimuth = 0;
|
|
2000
|
-
if (Math.abs(vel.polar) < DEAD_ZONE) vel.polar = 0;
|
|
1995
|
+
return function update() {
|
|
1996
|
+
if (keys["z"] || keys["Z"]) vel.zoom -= ORBIT_ACCEL;
|
|
1997
|
+
if (keys["x"] || keys["X"]) vel.zoom += ORBIT_ACCEL;
|
|
1998
|
+
if (keys["ArrowLeft"]) vel.azimuth -= ORBIT_ACCEL;
|
|
1999
|
+
if (keys["ArrowRight"]) vel.azimuth += ORBIT_ACCEL;
|
|
2000
|
+
if (keys["ArrowUp"]) vel.polar -= ORBIT_ACCEL;
|
|
2001
|
+
if (keys["ArrowDown"]) vel.polar += ORBIT_ACCEL;
|
|
2002
|
+
vel.zoom = clamp(vel.zoom, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
|
|
2003
|
+
vel.azimuth = clamp(vel.azimuth, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
|
|
2004
|
+
vel.polar = clamp(vel.polar, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
|
|
2005
|
+
vel.zoom *= ORBIT_DAMPING;
|
|
2006
|
+
vel.azimuth *= ORBIT_DAMPING;
|
|
2007
|
+
vel.polar *= ORBIT_DAMPING;
|
|
2008
|
+
if (Math.abs(vel.zoom) < ORBIT_DEAD_ZONE) vel.zoom = 0;
|
|
2009
|
+
if (Math.abs(vel.azimuth) < ORBIT_DEAD_ZONE) vel.azimuth = 0;
|
|
2010
|
+
if (Math.abs(vel.polar) < ORBIT_DEAD_ZONE) vel.polar = 0;
|
|
2001
2011
|
const hasMotion = vel.zoom !== 0 || vel.azimuth !== 0 || vel.polar !== 0;
|
|
2002
2012
|
if (!hasMotion) return false;
|
|
2003
2013
|
const target = controls.target;
|
|
2004
2014
|
_offset.copy(camera.position).sub(target);
|
|
2005
2015
|
let radius = _offset.length();
|
|
2006
2016
|
if (vel.zoom !== 0) {
|
|
2007
|
-
radius *= 1 + vel.zoom *
|
|
2017
|
+
radius *= 1 + vel.zoom * ORBIT_ZOOM_FACTOR * radius * 0.01;
|
|
2008
2018
|
radius = Math.max(controls.minDistance, Math.min(controls.maxDistance, radius));
|
|
2009
2019
|
}
|
|
2010
2020
|
if (vel.azimuth !== 0) {
|
|
@@ -2025,16 +2035,81 @@ function setupKeyboardControls(camera, controls, container) {
|
|
|
2025
2035
|
camera.lookAt(target);
|
|
2026
2036
|
controls.target.copy(target);
|
|
2027
2037
|
return true;
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
function createFlyUpdate(camera, controls, keys, state) {
|
|
2041
|
+
const vel = { thrust: 0, yaw: 0, pitch: 0 };
|
|
2042
|
+
return function update() {
|
|
2043
|
+
const speedMul = state.flySpeed;
|
|
2044
|
+
if (keys["z"] || keys["Z"]) vel.thrust += FLY_ACCEL * speedMul;
|
|
2045
|
+
if (keys["x"] || keys["X"]) vel.thrust -= FLY_ACCEL * speedMul;
|
|
2046
|
+
if (keys["a"] || keys["A"] || keys["ArrowLeft"]) vel.yaw += FLY_TURN_RATE;
|
|
2047
|
+
if (keys["d"] || keys["D"] || keys["ArrowRight"]) vel.yaw -= FLY_TURN_RATE;
|
|
2048
|
+
if (keys["w"] || keys["W"] || keys["ArrowUp"]) vel.pitch += FLY_TURN_RATE;
|
|
2049
|
+
if (keys["s"] || keys["S"] || keys["ArrowDown"]) vel.pitch -= FLY_TURN_RATE;
|
|
2050
|
+
const lookDist = camera.position.distanceTo(controls.target);
|
|
2051
|
+
const distScale = Math.max(0.3, lookDist * 3e-3);
|
|
2052
|
+
vel.thrust = clamp(vel.thrust, -FLY_MAX_THRUST * distScale * speedMul, FLY_MAX_THRUST * distScale * speedMul);
|
|
2053
|
+
vel.yaw = clamp(vel.yaw, -FLY_TURN_RATE * 2, FLY_TURN_RATE * 2);
|
|
2054
|
+
vel.pitch = clamp(vel.pitch, -FLY_TURN_RATE * 2, FLY_TURN_RATE * 2);
|
|
2055
|
+
vel.thrust *= FLY_FRICTION;
|
|
2056
|
+
vel.yaw *= FLY_FRICTION;
|
|
2057
|
+
vel.pitch *= FLY_FRICTION;
|
|
2058
|
+
if (Math.abs(vel.thrust) < FLY_DEAD_ZONE) vel.thrust = 0;
|
|
2059
|
+
if (Math.abs(vel.yaw) < FLY_DEAD_ZONE) vel.yaw = 0;
|
|
2060
|
+
if (Math.abs(vel.pitch) < FLY_DEAD_ZONE) vel.pitch = 0;
|
|
2061
|
+
const hasMotion = vel.thrust !== 0 || vel.yaw !== 0 || vel.pitch !== 0;
|
|
2062
|
+
if (!hasMotion) return false;
|
|
2063
|
+
if (vel.yaw !== 0) {
|
|
2064
|
+
const upDot = camera.up.dot(_worldUp);
|
|
2065
|
+
const yawSign = upDot < 0 ? -1 : 1;
|
|
2066
|
+
_q.setFromAxisAngle(_worldUp, vel.yaw * yawSign);
|
|
2067
|
+
camera.quaternion.premultiply(_q);
|
|
2068
|
+
camera.up.applyQuaternion(_q).normalize();
|
|
2069
|
+
}
|
|
2070
|
+
if (vel.pitch !== 0) {
|
|
2071
|
+
_right.setFromMatrixColumn(camera.matrixWorld, 0).normalize();
|
|
2072
|
+
_q.setFromAxisAngle(_right, vel.pitch);
|
|
2073
|
+
camera.quaternion.premultiply(_q);
|
|
2074
|
+
camera.up.applyQuaternion(_q).normalize();
|
|
2075
|
+
}
|
|
2076
|
+
if (vel.thrust !== 0) {
|
|
2077
|
+
camera.getWorldDirection(_dir);
|
|
2078
|
+
_dir.multiplyScalar(vel.thrust * distScale * 100);
|
|
2079
|
+
camera.position.add(_dir);
|
|
2080
|
+
controls.target.add(_dir);
|
|
2081
|
+
}
|
|
2082
|
+
return true;
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
function setupKeyboardControls(camera, controls, container, mode = "fly", flySpeed = 1) {
|
|
2086
|
+
const keys = {};
|
|
2087
|
+
const state = { flySpeed };
|
|
2088
|
+
function onKeyDown2(e) {
|
|
2089
|
+
var _a;
|
|
2090
|
+
const tag = (_a = e.target) == null ? void 0 : _a.tagName;
|
|
2091
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
2092
|
+
keys[e.key] = true;
|
|
2028
2093
|
}
|
|
2094
|
+
function onKeyUp(e) {
|
|
2095
|
+
keys[e.key] = false;
|
|
2096
|
+
}
|
|
2097
|
+
function onBlur() {
|
|
2098
|
+
for (const k of Object.keys(keys)) keys[k] = false;
|
|
2099
|
+
}
|
|
2100
|
+
container.addEventListener("keydown", onKeyDown2);
|
|
2101
|
+
container.addEventListener("keyup", onKeyUp);
|
|
2102
|
+
container.addEventListener("blur", onBlur);
|
|
2103
|
+
const updateFn = mode === "fly" ? createFlyUpdate(camera, controls, keys, state) : createOrbitUpdate(camera, controls, keys);
|
|
2029
2104
|
function cleanup() {
|
|
2030
2105
|
container.removeEventListener("keydown", onKeyDown2);
|
|
2031
2106
|
container.removeEventListener("keyup", onKeyUp);
|
|
2032
2107
|
container.removeEventListener("blur", onBlur);
|
|
2033
2108
|
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
return
|
|
2109
|
+
function setFlySpeed(speed) {
|
|
2110
|
+
state.flySpeed = speed;
|
|
2111
|
+
}
|
|
2112
|
+
return { update: updateFn, cleanup, setFlySpeed };
|
|
2038
2113
|
}
|
|
2039
2114
|
function createScene(container, backgroundColor, style, rendererConfig) {
|
|
2040
2115
|
const scene = new THREE__namespace.Scene();
|
|
@@ -2071,7 +2146,8 @@ function createScene(container, backgroundColor, style, rendererConfig) {
|
|
|
2071
2146
|
scene.add(selectionRing);
|
|
2072
2147
|
const labels = createLabelSystem(style.maxLabels);
|
|
2073
2148
|
scene.add(labels.group);
|
|
2074
|
-
const
|
|
2149
|
+
const cameraMode = (rendererConfig == null ? void 0 : rendererConfig.cameraMode) ?? "fly";
|
|
2150
|
+
const keyboard = setupKeyboardControls(camera, controls, container, cameraMode, style.flySpeed ?? 1);
|
|
2075
2151
|
return {
|
|
2076
2152
|
scene,
|
|
2077
2153
|
camera,
|
|
@@ -2410,21 +2486,136 @@ function zoomToFitPositions(camera, controls, positions, duration, padding) {
|
|
|
2410
2486
|
duration
|
|
2411
2487
|
);
|
|
2412
2488
|
}
|
|
2413
|
-
|
|
2489
|
+
const EDGE_HIT_THRESHOLD = 5;
|
|
2490
|
+
function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, callbacks, container, getEdgeData) {
|
|
2414
2491
|
const raycaster = new THREE__namespace.Raycaster();
|
|
2415
2492
|
const mouse = new THREE__namespace.Vector2();
|
|
2416
2493
|
let mouseDownPos = null;
|
|
2417
2494
|
let lastClickTime = 0;
|
|
2418
2495
|
let lastClickNodeId = null;
|
|
2419
2496
|
let singleClickTimer = null;
|
|
2497
|
+
let dragNode = null;
|
|
2498
|
+
let isDragging = false;
|
|
2499
|
+
const dragPlane = new THREE__namespace.Plane();
|
|
2500
|
+
const dragIntersect = new THREE__namespace.Vector3();
|
|
2420
2501
|
function onPointerDown2(e) {
|
|
2421
2502
|
mouseDownPos = { x: e.clientX, y: e.clientY };
|
|
2422
2503
|
if (container && document.activeElement !== container) {
|
|
2423
2504
|
container.focus({ preventScroll: true });
|
|
2424
2505
|
}
|
|
2506
|
+
if (callbacks.onNodeDrag) {
|
|
2507
|
+
const nodesMesh = getNodesMesh();
|
|
2508
|
+
if (nodesMesh) {
|
|
2509
|
+
const rect = canvas.getBoundingClientRect();
|
|
2510
|
+
mouse.x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
2511
|
+
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
2512
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2513
|
+
const hits = raycaster.intersectObject(nodesMesh);
|
|
2514
|
+
if (hits.length > 0 && hits[0].instanceId != null) {
|
|
2515
|
+
const nodes = getNodes();
|
|
2516
|
+
dragNode = nodes[hits[0].instanceId] ?? null;
|
|
2517
|
+
hits[0].instanceId;
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
function onPointerMove2(e) {
|
|
2523
|
+
var _a, _b, _c;
|
|
2524
|
+
if (dragNode && mouseDownPos && callbacks.onNodeDrag) {
|
|
2525
|
+
const dx = Math.abs(e.clientX - mouseDownPos.x);
|
|
2526
|
+
const dy = Math.abs(e.clientY - mouseDownPos.y);
|
|
2527
|
+
if (dx + dy > 5) {
|
|
2528
|
+
isDragging = true;
|
|
2529
|
+
controls.enabled = false;
|
|
2530
|
+
const rect2 = canvas.getBoundingClientRect();
|
|
2531
|
+
mouse.x = (e.clientX - rect2.left) / rect2.width * 2 - 1;
|
|
2532
|
+
mouse.y = -((e.clientY - rect2.top) / rect2.height) * 2 + 1;
|
|
2533
|
+
const nodePos = new THREE__namespace.Vector3(
|
|
2534
|
+
dragNode.x ?? 0,
|
|
2535
|
+
dragNode.y ?? 0,
|
|
2536
|
+
dragNode.z ?? 0
|
|
2537
|
+
);
|
|
2538
|
+
dragPlane.setFromNormalAndCoplanarPoint(
|
|
2539
|
+
camera.getWorldDirection(new THREE__namespace.Vector3()).negate(),
|
|
2540
|
+
nodePos
|
|
2541
|
+
);
|
|
2542
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2543
|
+
if (raycaster.ray.intersectPlane(dragPlane, dragIntersect)) {
|
|
2544
|
+
callbacks.onNodeDrag(dragNode, {
|
|
2545
|
+
x: dragIntersect.x,
|
|
2546
|
+
y: dragIntersect.y,
|
|
2547
|
+
z: dragIntersect.z
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
if (!callbacks.onNodeHover && !callbacks.onLinkHover) return;
|
|
2554
|
+
const nodesMesh = getNodesMesh();
|
|
2555
|
+
if (!nodesMesh) return;
|
|
2556
|
+
const rect = canvas.getBoundingClientRect();
|
|
2557
|
+
mouse.x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
2558
|
+
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
2559
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2560
|
+
const hits = raycaster.intersectObject(nodesMesh);
|
|
2561
|
+
if (hits.length > 0 && hits[0].instanceId != null) {
|
|
2562
|
+
const nodes = getNodes();
|
|
2563
|
+
const node = nodes[hits[0].instanceId];
|
|
2564
|
+
if (node && node.id !== lastHoveredId) {
|
|
2565
|
+
lastHoveredId = node.id;
|
|
2566
|
+
lastHoveredLinkIdx = -1;
|
|
2567
|
+
canvas.style.cursor = "pointer";
|
|
2568
|
+
(_a = callbacks.onNodeHover) == null ? void 0 : _a.call(callbacks, node);
|
|
2569
|
+
(_b = callbacks.onLinkHover) == null ? void 0 : _b.call(callbacks, null);
|
|
2570
|
+
}
|
|
2571
|
+
} else {
|
|
2572
|
+
if (lastHoveredId !== null) {
|
|
2573
|
+
lastHoveredId = null;
|
|
2574
|
+
canvas.style.cursor = "default";
|
|
2575
|
+
(_c = callbacks.onNodeHover) == null ? void 0 : _c.call(callbacks, null);
|
|
2576
|
+
}
|
|
2577
|
+
if (callbacks.onLinkHover && getEdgeData) {
|
|
2578
|
+
const edgeData = getEdgeData();
|
|
2579
|
+
if (edgeData == null ? void 0 : edgeData.positions) {
|
|
2580
|
+
const hitLink = findNearestEdge(
|
|
2581
|
+
e.clientX,
|
|
2582
|
+
e.clientY,
|
|
2583
|
+
rect,
|
|
2584
|
+
camera,
|
|
2585
|
+
edgeData
|
|
2586
|
+
);
|
|
2587
|
+
if (hitLink) {
|
|
2588
|
+
if (lastHoveredLinkIdx !== hitLink.index) {
|
|
2589
|
+
lastHoveredLinkIdx = hitLink.index;
|
|
2590
|
+
canvas.style.cursor = "pointer";
|
|
2591
|
+
callbacks.onLinkHover(hitLink.link);
|
|
2592
|
+
}
|
|
2593
|
+
} else if (lastHoveredLinkIdx !== -1) {
|
|
2594
|
+
lastHoveredLinkIdx = -1;
|
|
2595
|
+
canvas.style.cursor = "default";
|
|
2596
|
+
callbacks.onLinkHover(null);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2425
2601
|
}
|
|
2426
2602
|
function onPointerUp2(e) {
|
|
2427
2603
|
var _a;
|
|
2604
|
+
if (isDragging && dragNode && callbacks.onNodeDragEnd) {
|
|
2605
|
+
callbacks.onNodeDragEnd(dragNode, {
|
|
2606
|
+
x: dragNode.x ?? 0,
|
|
2607
|
+
y: dragNode.y ?? 0,
|
|
2608
|
+
z: dragNode.z ?? 0
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
if (isDragging) {
|
|
2612
|
+
isDragging = false;
|
|
2613
|
+
dragNode = null;
|
|
2614
|
+
controls.enabled = true;
|
|
2615
|
+
mouseDownPos = null;
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
dragNode = null;
|
|
2428
2619
|
if (!mouseDownPos) return;
|
|
2429
2620
|
const dx = Math.abs(e.clientX - mouseDownPos.x);
|
|
2430
2621
|
const dy = Math.abs(e.clientY - mouseDownPos.y);
|
|
@@ -2472,6 +2663,22 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2472
2663
|
callbacks.onNodeClick(node);
|
|
2473
2664
|
}, 250);
|
|
2474
2665
|
} else {
|
|
2666
|
+
if (callbacks.onLinkClick && getEdgeData) {
|
|
2667
|
+
const edgeData = getEdgeData();
|
|
2668
|
+
if (edgeData == null ? void 0 : edgeData.positions) {
|
|
2669
|
+
const hitLink = findNearestEdge(
|
|
2670
|
+
e.clientX,
|
|
2671
|
+
e.clientY,
|
|
2672
|
+
rect,
|
|
2673
|
+
camera,
|
|
2674
|
+
edgeData
|
|
2675
|
+
);
|
|
2676
|
+
if (hitLink) {
|
|
2677
|
+
callbacks.onLinkClick(hitLink.link);
|
|
2678
|
+
return;
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2475
2682
|
if (singleClickTimer) clearTimeout(singleClickTimer);
|
|
2476
2683
|
singleClickTimer = null;
|
|
2477
2684
|
lastClickTime = 0;
|
|
@@ -2479,13 +2686,9 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2479
2686
|
callbacks.onNodeClick(null);
|
|
2480
2687
|
}
|
|
2481
2688
|
}
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
}
|
|
2486
|
-
let lastHoveredId = null;
|
|
2487
|
-
function onPointerMove2(e) {
|
|
2488
|
-
if (!callbacks.onNodeHover) return;
|
|
2689
|
+
function onContextMenu2(e) {
|
|
2690
|
+
if (!callbacks.onContextMenu) return;
|
|
2691
|
+
e.preventDefault();
|
|
2489
2692
|
const nodesMesh = getNodesMesh();
|
|
2490
2693
|
if (!nodesMesh) return;
|
|
2491
2694
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -2496,20 +2699,21 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2496
2699
|
if (hits.length > 0 && hits[0].instanceId != null) {
|
|
2497
2700
|
const nodes = getNodes();
|
|
2498
2701
|
const node = nodes[hits[0].instanceId];
|
|
2499
|
-
if (node
|
|
2500
|
-
|
|
2501
|
-
canvas.style.cursor = "pointer";
|
|
2502
|
-
callbacks.onNodeHover(node);
|
|
2702
|
+
if (node) {
|
|
2703
|
+
callbacks.onContextMenu(node, { x: e.clientX, y: e.clientY });
|
|
2503
2704
|
}
|
|
2504
|
-
} else if (lastHoveredId !== null) {
|
|
2505
|
-
lastHoveredId = null;
|
|
2506
|
-
canvas.style.cursor = "default";
|
|
2507
|
-
callbacks.onNodeHover(null);
|
|
2508
2705
|
}
|
|
2509
2706
|
}
|
|
2707
|
+
const keyTarget = container ?? window;
|
|
2708
|
+
function onKeyDown2(e) {
|
|
2709
|
+
if (e.key === "Escape") callbacks.onNodeClick(null);
|
|
2710
|
+
}
|
|
2711
|
+
let lastHoveredId = null;
|
|
2712
|
+
let lastHoveredLinkIdx = -1;
|
|
2510
2713
|
canvas.addEventListener("pointerdown", onPointerDown2);
|
|
2511
2714
|
canvas.addEventListener("pointerup", onPointerUp2);
|
|
2512
2715
|
canvas.addEventListener("pointermove", onPointerMove2);
|
|
2716
|
+
canvas.addEventListener("contextmenu", onContextMenu2);
|
|
2513
2717
|
keyTarget.addEventListener("keydown", onKeyDown2);
|
|
2514
2718
|
return {
|
|
2515
2719
|
cleanup: () => {
|
|
@@ -2517,10 +2721,48 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
|
|
|
2517
2721
|
canvas.removeEventListener("pointerdown", onPointerDown2);
|
|
2518
2722
|
canvas.removeEventListener("pointerup", onPointerUp2);
|
|
2519
2723
|
canvas.removeEventListener("pointermove", onPointerMove2);
|
|
2724
|
+
canvas.removeEventListener("contextmenu", onContextMenu2);
|
|
2520
2725
|
keyTarget.removeEventListener("keydown", onKeyDown2);
|
|
2521
2726
|
}
|
|
2522
2727
|
};
|
|
2523
2728
|
}
|
|
2729
|
+
function findNearestEdge(clientX, clientY, rect, camera, edgeData) {
|
|
2730
|
+
const { links, edgeNodeIndices, edgeLinkIndices, positions } = edgeData;
|
|
2731
|
+
const screenX = clientX - rect.left;
|
|
2732
|
+
const screenY = clientY - rect.top;
|
|
2733
|
+
const w = rect.width;
|
|
2734
|
+
const h = rect.height;
|
|
2735
|
+
let bestDist = EDGE_HIT_THRESHOLD;
|
|
2736
|
+
let bestIdx = -1;
|
|
2737
|
+
const tmpVec = new THREE__namespace.Vector3();
|
|
2738
|
+
for (let i = 0; i < edgeNodeIndices.length; i++) {
|
|
2739
|
+
const [si, ti] = edgeNodeIndices[i];
|
|
2740
|
+
tmpVec.set(positions[si * 3], positions[si * 3 + 1], positions[si * 3 + 2]);
|
|
2741
|
+
tmpVec.project(camera);
|
|
2742
|
+
const sx1 = (tmpVec.x * 0.5 + 0.5) * w;
|
|
2743
|
+
const sy1 = (-tmpVec.y * 0.5 + 0.5) * h;
|
|
2744
|
+
tmpVec.set(positions[ti * 3], positions[ti * 3 + 1], positions[ti * 3 + 2]);
|
|
2745
|
+
tmpVec.project(camera);
|
|
2746
|
+
const sx2 = (tmpVec.x * 0.5 + 0.5) * w;
|
|
2747
|
+
const sy2 = (-tmpVec.y * 0.5 + 0.5) * h;
|
|
2748
|
+
const dist = pointToSegmentDist(screenX, screenY, sx1, sy1, sx2, sy2);
|
|
2749
|
+
if (dist < bestDist) {
|
|
2750
|
+
bestDist = dist;
|
|
2751
|
+
bestIdx = i;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
if (bestIdx === -1) return null;
|
|
2755
|
+
return { link: links[edgeLinkIndices[bestIdx]], index: bestIdx };
|
|
2756
|
+
}
|
|
2757
|
+
function pointToSegmentDist(px, py, ax, ay, bx, by) {
|
|
2758
|
+
const dx = bx - ax;
|
|
2759
|
+
const dy = by - ay;
|
|
2760
|
+
const lenSq = dx * dx + dy * dy;
|
|
2761
|
+
if (lenSq < 1e-4) return Math.hypot(px - ax, py - ay);
|
|
2762
|
+
let t = ((px - ax) * dx + (py - ay) * dy) / lenSq;
|
|
2763
|
+
t = Math.max(0, Math.min(1, t));
|
|
2764
|
+
return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));
|
|
2765
|
+
}
|
|
2524
2766
|
function disposeObject(obj) {
|
|
2525
2767
|
if (!obj) return;
|
|
2526
2768
|
const mesh = obj;
|
|
@@ -2542,6 +2784,13 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2542
2784
|
onNodeClick,
|
|
2543
2785
|
onNodeDoubleClick,
|
|
2544
2786
|
onNodeHover,
|
|
2787
|
+
onContextMenu: onContextMenu2,
|
|
2788
|
+
onLinkClick,
|
|
2789
|
+
onLinkHover,
|
|
2790
|
+
onNodeDrag,
|
|
2791
|
+
onNodeDragEnd,
|
|
2792
|
+
onLayoutSettled,
|
|
2793
|
+
onLayoutTick,
|
|
2545
2794
|
selectedNodeId = null,
|
|
2546
2795
|
highlightHops = 3,
|
|
2547
2796
|
theme: themeProp,
|
|
@@ -2574,13 +2823,28 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2574
2823
|
const onNodeClickRef = react.useRef(onNodeClick);
|
|
2575
2824
|
const onNodeDoubleClickRef = react.useRef(onNodeDoubleClick);
|
|
2576
2825
|
const onNodeHoverRef = react.useRef(onNodeHover);
|
|
2826
|
+
const onContextMenuRef = react.useRef(onContextMenu2);
|
|
2827
|
+
const onLinkClickRef = react.useRef(onLinkClick);
|
|
2828
|
+
const onLinkHoverRef = react.useRef(onLinkHover);
|
|
2829
|
+
const onNodeDragRef = react.useRef(onNodeDrag);
|
|
2830
|
+
const onNodeDragEndRef = react.useRef(onNodeDragEnd);
|
|
2831
|
+
const onLayoutSettledRef = react.useRef(onLayoutSettled);
|
|
2832
|
+
const onLayoutTickRef = react.useRef(onLayoutTick);
|
|
2577
2833
|
const styleRef = react.useRef(resolvedStyle);
|
|
2578
2834
|
const themeRef = react.useRef(resolvedTheme);
|
|
2579
2835
|
const selectedNodeIdRef = react.useRef(selectedNodeId);
|
|
2580
2836
|
const labelFormatterRef = react.useRef(labelFormatter);
|
|
2837
|
+
const highlightSetRef = react.useRef(null);
|
|
2581
2838
|
onNodeClickRef.current = onNodeClick;
|
|
2582
2839
|
onNodeDoubleClickRef.current = onNodeDoubleClick;
|
|
2583
2840
|
onNodeHoverRef.current = onNodeHover;
|
|
2841
|
+
onContextMenuRef.current = onContextMenu2;
|
|
2842
|
+
onLinkClickRef.current = onLinkClick;
|
|
2843
|
+
onLinkHoverRef.current = onLinkHover;
|
|
2844
|
+
onNodeDragRef.current = onNodeDrag;
|
|
2845
|
+
onNodeDragEndRef.current = onNodeDragEnd;
|
|
2846
|
+
onLayoutSettledRef.current = onLayoutSettled;
|
|
2847
|
+
onLayoutTickRef.current = onLayoutTick;
|
|
2584
2848
|
styleRef.current = resolvedStyle;
|
|
2585
2849
|
themeRef.current = resolvedTheme;
|
|
2586
2850
|
selectedNodeIdRef.current = selectedNodeId;
|
|
@@ -2646,7 +2910,8 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2646
2910
|
styleRef.current.labelScale,
|
|
2647
2911
|
styleRef.current.labelThreshold,
|
|
2648
2912
|
styleRef.current.maxLabels,
|
|
2649
|
-
labelFormatterRef.current
|
|
2913
|
+
labelFormatterRef.current,
|
|
2914
|
+
highlightSetRef.current
|
|
2650
2915
|
);
|
|
2651
2916
|
});
|
|
2652
2917
|
const resizeObserver = setupResize(container, state);
|
|
@@ -2671,9 +2936,52 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2671
2936
|
onNodeHover: (node) => {
|
|
2672
2937
|
var _a;
|
|
2673
2938
|
return (_a = onNodeHoverRef.current) == null ? void 0 : _a.call(onNodeHoverRef, node);
|
|
2939
|
+
},
|
|
2940
|
+
onContextMenu: (node, pos) => {
|
|
2941
|
+
var _a;
|
|
2942
|
+
return (_a = onContextMenuRef.current) == null ? void 0 : _a.call(onContextMenuRef, node, pos);
|
|
2943
|
+
},
|
|
2944
|
+
onLinkClick: (link) => {
|
|
2945
|
+
var _a;
|
|
2946
|
+
return (_a = onLinkClickRef.current) == null ? void 0 : _a.call(onLinkClickRef, link);
|
|
2947
|
+
},
|
|
2948
|
+
onLinkHover: (link) => {
|
|
2949
|
+
var _a;
|
|
2950
|
+
return (_a = onLinkHoverRef.current) == null ? void 0 : _a.call(onLinkHoverRef, link);
|
|
2951
|
+
},
|
|
2952
|
+
onNodeDrag: (node, pos) => {
|
|
2953
|
+
var _a;
|
|
2954
|
+
(_a = onNodeDragRef.current) == null ? void 0 : _a.call(onNodeDragRef, node, pos);
|
|
2955
|
+
const d = dataRef.current;
|
|
2956
|
+
const idx = d.nodeIdToIndex.get(node.id);
|
|
2957
|
+
if (idx !== void 0 && d.positions) {
|
|
2958
|
+
d.positions[idx * 3] = pos.x;
|
|
2959
|
+
d.positions[idx * 3 + 1] = pos.y;
|
|
2960
|
+
d.positions[idx * 3 + 2] = pos.z;
|
|
2961
|
+
node.x = pos.x;
|
|
2962
|
+
node.y = pos.y;
|
|
2963
|
+
node.z = pos.z;
|
|
2964
|
+
const gObj = graphObjRef.current;
|
|
2965
|
+
if (gObj && d.scales) {
|
|
2966
|
+
updateNodePositions(gObj.nodesMesh, d.positions, d.scales, d.nodes.length);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
},
|
|
2970
|
+
onNodeDragEnd: (node, pos) => {
|
|
2971
|
+
var _a;
|
|
2972
|
+
return (_a = onNodeDragEndRef.current) == null ? void 0 : _a.call(onNodeDragEndRef, node, pos);
|
|
2674
2973
|
}
|
|
2675
2974
|
},
|
|
2676
|
-
container
|
|
2975
|
+
container,
|
|
2976
|
+
() => {
|
|
2977
|
+
const d = dataRef.current;
|
|
2978
|
+
return {
|
|
2979
|
+
links: d.links,
|
|
2980
|
+
edgeNodeIndices: d.edgeNodeIndices,
|
|
2981
|
+
edgeLinkIndices: d.edgeLinkIndices,
|
|
2982
|
+
positions: d.positions
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2677
2985
|
);
|
|
2678
2986
|
return () => {
|
|
2679
2987
|
cancelAnim();
|
|
@@ -2813,6 +3121,7 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2813
3121
|
console.error("Graphier: Layout worker error:", err);
|
|
2814
3122
|
};
|
|
2815
3123
|
worker.onmessage = (e) => {
|
|
3124
|
+
var _a, _b;
|
|
2816
3125
|
const msg = e.data;
|
|
2817
3126
|
if (msg.type === "positions") {
|
|
2818
3127
|
const positions = new Float32Array(msg.positions);
|
|
@@ -2840,9 +3149,11 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2840
3149
|
);
|
|
2841
3150
|
}
|
|
2842
3151
|
}
|
|
3152
|
+
(_a = onLayoutTickRef.current) == null ? void 0 : _a.call(onLayoutTickRef, msg.alpha);
|
|
2843
3153
|
}
|
|
2844
3154
|
if (msg.type === "settled") {
|
|
2845
3155
|
dataRef.current.settled = true;
|
|
3156
|
+
(_b = onLayoutSettledRef.current) == null ? void 0 : _b.call(onLayoutSettledRef);
|
|
2846
3157
|
}
|
|
2847
3158
|
};
|
|
2848
3159
|
const layoutParams = resolveLayoutParams(nc, layoutProp);
|
|
@@ -2887,6 +3198,7 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2887
3198
|
const d = dataRef.current;
|
|
2888
3199
|
const theme = themeRef.current;
|
|
2889
3200
|
const style = styleRef.current;
|
|
3201
|
+
highlightSetRef.current = highlightSet;
|
|
2890
3202
|
applyNodeHighlight(
|
|
2891
3203
|
gObj.nodesMesh,
|
|
2892
3204
|
d.nodes,
|
|
@@ -2904,6 +3216,18 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2904
3216
|
d.nodes.length,
|
|
2905
3217
|
theme
|
|
2906
3218
|
);
|
|
3219
|
+
if (sceneState.bloomPass) {
|
|
3220
|
+
const style2 = styleRef.current;
|
|
3221
|
+
if (highlightSet) {
|
|
3222
|
+
sceneState.bloomPass.strength = style2.bloomStrength * 1.8;
|
|
3223
|
+
sceneState.bloomPass.radius = style2.bloomRadius * 1.4;
|
|
3224
|
+
sceneState.bloomPass.threshold = 0.25;
|
|
3225
|
+
} else {
|
|
3226
|
+
sceneState.bloomPass.strength = style2.bloomStrength;
|
|
3227
|
+
sceneState.bloomPass.radius = style2.bloomRadius;
|
|
3228
|
+
sceneState.bloomPass.threshold = style2.bloomThreshold;
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
2907
3231
|
const selectedNode = selectedNodeId ? d.nodes.find((n) => n.id === selectedNodeId) : null;
|
|
2908
3232
|
updateSelectionRing(
|
|
2909
3233
|
sceneState.selectionRing,
|
|
@@ -2979,6 +3303,11 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
2979
3303
|
}, 30);
|
|
2980
3304
|
return () => clearInterval(id);
|
|
2981
3305
|
}, [resolvedStyle.autoOrbit]);
|
|
3306
|
+
react.useEffect(() => {
|
|
3307
|
+
const s = sceneRef.current;
|
|
3308
|
+
if (!s) return;
|
|
3309
|
+
s.keyboard.setFlySpeed(resolvedStyle.flySpeed);
|
|
3310
|
+
}, [resolvedStyle.flySpeed]);
|
|
2982
3311
|
react.useImperativeHandle(ref, () => ({
|
|
2983
3312
|
cameraPosition(pos, lookAt, duration = 1e3) {
|
|
2984
3313
|
const s = sceneRef.current;
|
|
@@ -3051,13 +3380,21 @@ function NetworkGraph3DInner(props, ref) {
|
|
|
3051
3380
|
var _a;
|
|
3052
3381
|
return ((_a = sceneRef.current) == null ? void 0 : _a.camera) ?? null;
|
|
3053
3382
|
},
|
|
3054
|
-
|
|
3383
|
+
captureScreenshot() {
|
|
3055
3384
|
const s = sceneRef.current;
|
|
3056
3385
|
if (!s) return null;
|
|
3057
3386
|
s.renderer.render(s.scene, s.camera);
|
|
3058
|
-
return
|
|
3059
|
-
|
|
3060
|
-
|
|
3387
|
+
return s.renderer.domElement.toDataURL("image/png");
|
|
3388
|
+
},
|
|
3389
|
+
reheatLayout() {
|
|
3390
|
+
const gObj = graphObjRef.current;
|
|
3391
|
+
if (gObj == null ? void 0 : gObj.worker) {
|
|
3392
|
+
gObj.worker.postMessage({ type: "stop" });
|
|
3393
|
+
gObj.worker.terminate();
|
|
3394
|
+
gObj.worker = null;
|
|
3395
|
+
}
|
|
3396
|
+
dataRef.current.settled = false;
|
|
3397
|
+
setAppendedData((prev) => prev ? { ...prev } : { nodes: [], links: [] });
|
|
3061
3398
|
}
|
|
3062
3399
|
}));
|
|
3063
3400
|
return /* @__PURE__ */ jsxRuntime.jsx(
|