@cocorof/graphier 1.1.1 → 1.3.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.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("#", "");
@@ -50,8 +52,8 @@ function hexToRgb(hex) {
50
52
  return [(n >> 16) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
51
53
  }
52
54
  function rgbToHex(r, g, b) {
53
- const clamp2 = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255);
54
- const hex = clamp2(r) << 16 | clamp2(g) << 8 | clamp2(b);
55
+ const clamp = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255);
56
+ const hex = clamp(r) << 16 | clamp(g) << 8 | clamp(b);
55
57
  return "#" + hex.toString(16).padStart(6, "0");
56
58
  }
57
59
  function brighten(hex, factor) {
@@ -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 charge = (config == null ? void 0 : config.charge) !== "auto" && (config == null ? void 0 : config.charge) != null ? config.charge : n > 5e4 ? -80 : n > 1e4 ? -120 : -200;
194
- const distanceMax = n > 5e4 ? 1500 : n > 1e4 ? 2e3 : 3e3;
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 linkDistance = (config == null ? void 0 : config.linkDistance) !== "auto" && (config == null ? void 0 : config.linkDistance) != null ? config.linkDistance : n > 5e4 ? 120 : n > 1e4 ? 180 : 250;
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,
@@ -1961,60 +1972,45 @@ function disposeLabelSystem(state) {
1961
1972
  }
1962
1973
  state.textureCache.clear();
1963
1974
  }
1964
- const ORBIT_ACCEL = 12e-4;
1965
- const ORBIT_MAX_SPEED = 0.06;
1966
- const ORBIT_DAMPING = 0.88;
1975
+ const ORBIT_ROTATE_SPEED = 0.02;
1967
1976
  const ORBIT_ZOOM_FACTOR = 0.015;
1968
- const ORBIT_DEAD_ZONE = 1e-4;
1969
- const FLY_ACCEL = 6e-3;
1970
- const FLY_FRICTION = 0.92;
1971
- const FLY_MAX_THRUST = 0.18;
1977
+ const FLY_THRUST_SPEED = 0.08;
1972
1978
  const FLY_TURN_RATE = 8e-3;
1973
- const FLY_DEAD_ZONE = 2e-4;
1974
1979
  const _offset = new THREE__namespace.Vector3();
1975
1980
  const _right = new THREE__namespace.Vector3();
1976
1981
  const _q = new THREE__namespace.Quaternion();
1977
1982
  const _dir = new THREE__namespace.Vector3();
1978
- function clamp(v, min, max) {
1979
- return v < min ? min : v > max ? max : v;
1980
- }
1983
+ const _worldUp = new THREE__namespace.Vector3(0, 1, 0);
1981
1984
  function createOrbitUpdate(camera, controls, keys) {
1982
- const vel = { zoom: 0, azimuth: 0, polar: 0 };
1983
1985
  return function update() {
1984
- if (keys["z"] || keys["Z"]) vel.zoom -= ORBIT_ACCEL;
1985
- if (keys["x"] || keys["X"]) vel.zoom += ORBIT_ACCEL;
1986
- if (keys["ArrowLeft"]) vel.azimuth -= ORBIT_ACCEL;
1987
- if (keys["ArrowRight"]) vel.azimuth += ORBIT_ACCEL;
1988
- if (keys["ArrowUp"]) vel.polar -= ORBIT_ACCEL;
1989
- if (keys["ArrowDown"]) vel.polar += ORBIT_ACCEL;
1990
- vel.zoom = clamp(vel.zoom, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
1991
- vel.azimuth = clamp(vel.azimuth, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
1992
- vel.polar = clamp(vel.polar, -ORBIT_MAX_SPEED, ORBIT_MAX_SPEED);
1993
- vel.zoom *= ORBIT_DAMPING;
1994
- vel.azimuth *= ORBIT_DAMPING;
1995
- vel.polar *= ORBIT_DAMPING;
1996
- if (Math.abs(vel.zoom) < ORBIT_DEAD_ZONE) vel.zoom = 0;
1997
- if (Math.abs(vel.azimuth) < ORBIT_DEAD_ZONE) vel.azimuth = 0;
1998
- if (Math.abs(vel.polar) < ORBIT_DEAD_ZONE) vel.polar = 0;
1999
- const hasMotion = vel.zoom !== 0 || vel.azimuth !== 0 || vel.polar !== 0;
1986
+ let zoom = 0;
1987
+ let azimuth = 0;
1988
+ let polar = 0;
1989
+ if (keys["z"] || keys["Z"]) zoom -= ORBIT_ROTATE_SPEED;
1990
+ if (keys["x"] || keys["X"]) zoom += ORBIT_ROTATE_SPEED;
1991
+ if (keys["ArrowLeft"]) azimuth -= ORBIT_ROTATE_SPEED;
1992
+ if (keys["ArrowRight"]) azimuth += ORBIT_ROTATE_SPEED;
1993
+ if (keys["ArrowUp"]) polar -= ORBIT_ROTATE_SPEED;
1994
+ if (keys["ArrowDown"]) polar += ORBIT_ROTATE_SPEED;
1995
+ const hasMotion = zoom !== 0 || azimuth !== 0 || polar !== 0;
2000
1996
  if (!hasMotion) return false;
2001
1997
  const target = controls.target;
2002
1998
  _offset.copy(camera.position).sub(target);
2003
1999
  let radius = _offset.length();
2004
- if (vel.zoom !== 0) {
2005
- radius *= 1 + vel.zoom * ORBIT_ZOOM_FACTOR * radius * 0.01;
2000
+ if (zoom !== 0) {
2001
+ radius *= 1 + zoom * ORBIT_ZOOM_FACTOR * radius * 0.01;
2006
2002
  radius = Math.max(controls.minDistance, Math.min(controls.maxDistance, radius));
2007
2003
  }
2008
- if (vel.azimuth !== 0) {
2009
- _q.setFromAxisAngle(camera.up, -vel.azimuth);
2004
+ if (azimuth !== 0) {
2005
+ _q.setFromAxisAngle(camera.up, -azimuth);
2010
2006
  _offset.applyQuaternion(_q);
2011
2007
  }
2012
- if (vel.polar !== 0) {
2008
+ if (polar !== 0) {
2013
2009
  _right.copy(_offset).normalize().cross(camera.up).normalize();
2014
2010
  if (_right.lengthSq() < 1e-3) {
2015
2011
  _right.setFromMatrixColumn(camera.matrixWorld, 0);
2016
2012
  }
2017
- _q.setFromAxisAngle(_right, vel.polar);
2013
+ _q.setFromAxisAngle(_right, polar);
2018
2014
  _offset.applyQuaternion(_q);
2019
2015
  camera.up.applyQuaternion(_q).normalize();
2020
2016
  }
@@ -2025,48 +2021,47 @@ function createOrbitUpdate(camera, controls, keys) {
2025
2021
  return true;
2026
2022
  };
2027
2023
  }
2028
- function createFlyUpdate(camera, controls, keys) {
2029
- const vel = { thrust: 0, yaw: 0, pitch: 0 };
2024
+ function createFlyUpdate(camera, controls, keys, state) {
2030
2025
  return function update() {
2031
- if (keys["z"] || keys["Z"]) vel.thrust += FLY_ACCEL;
2032
- if (keys["x"] || keys["X"]) vel.thrust -= FLY_ACCEL;
2033
- if (keys["a"] || keys["A"] || keys["ArrowLeft"]) vel.yaw += FLY_TURN_RATE;
2034
- if (keys["d"] || keys["D"] || keys["ArrowRight"]) vel.yaw -= FLY_TURN_RATE;
2035
- if (keys["w"] || keys["W"] || keys["ArrowUp"]) vel.pitch += FLY_TURN_RATE;
2036
- if (keys["s"] || keys["S"] || keys["ArrowDown"]) vel.pitch -= FLY_TURN_RATE;
2037
- const lookDist = camera.position.distanceTo(controls.target);
2038
- const distScale = Math.max(0.3, lookDist * 3e-3);
2039
- vel.thrust = clamp(vel.thrust, -FLY_MAX_THRUST * distScale, FLY_MAX_THRUST * distScale);
2040
- vel.yaw = clamp(vel.yaw, -FLY_TURN_RATE * 2, FLY_TURN_RATE * 2);
2041
- vel.pitch = clamp(vel.pitch, -FLY_TURN_RATE * 2, FLY_TURN_RATE * 2);
2042
- vel.thrust *= FLY_FRICTION;
2043
- vel.yaw *= FLY_FRICTION;
2044
- vel.pitch *= FLY_FRICTION;
2045
- if (Math.abs(vel.thrust) < FLY_DEAD_ZONE) vel.thrust = 0;
2046
- if (Math.abs(vel.yaw) < FLY_DEAD_ZONE) vel.yaw = 0;
2047
- if (Math.abs(vel.pitch) < FLY_DEAD_ZONE) vel.pitch = 0;
2048
- const hasMotion = vel.thrust !== 0 || vel.yaw !== 0 || vel.pitch !== 0;
2026
+ const speedMul = state.flySpeed;
2027
+ let thrust = 0;
2028
+ let yaw = 0;
2029
+ let pitch = 0;
2030
+ if (keys["z"] || keys["Z"]) thrust += FLY_THRUST_SPEED * speedMul;
2031
+ if (keys["x"] || keys["X"]) thrust -= FLY_THRUST_SPEED * speedMul;
2032
+ if (keys["a"] || keys["A"] || keys["ArrowLeft"]) yaw += FLY_TURN_RATE;
2033
+ if (keys["d"] || keys["D"] || keys["ArrowRight"]) yaw -= FLY_TURN_RATE;
2034
+ if (keys["w"] || keys["W"] || keys["ArrowUp"]) pitch += FLY_TURN_RATE;
2035
+ if (keys["s"] || keys["S"] || keys["ArrowDown"]) pitch -= FLY_TURN_RATE;
2036
+ const hasMotion = thrust !== 0 || yaw !== 0 || pitch !== 0;
2049
2037
  if (!hasMotion) return false;
2050
- if (vel.yaw !== 0) {
2051
- _q.setFromAxisAngle(camera.up, vel.yaw);
2038
+ if (yaw !== 0) {
2039
+ const upDot = camera.up.dot(_worldUp);
2040
+ const yawSign = upDot < 0 ? -1 : 1;
2041
+ _q.setFromAxisAngle(_worldUp, yaw * yawSign);
2052
2042
  camera.quaternion.premultiply(_q);
2043
+ camera.up.applyQuaternion(_q).normalize();
2053
2044
  }
2054
- if (vel.pitch !== 0) {
2045
+ if (pitch !== 0) {
2055
2046
  _right.setFromMatrixColumn(camera.matrixWorld, 0).normalize();
2056
- _q.setFromAxisAngle(_right, vel.pitch);
2047
+ _q.setFromAxisAngle(_right, pitch);
2057
2048
  camera.quaternion.premultiply(_q);
2049
+ camera.up.applyQuaternion(_q).normalize();
2058
2050
  }
2059
- if (vel.thrust !== 0) {
2051
+ if (thrust !== 0) {
2052
+ const lookDist = camera.position.distanceTo(controls.target);
2053
+ const distScale = Math.max(0.3, lookDist * 3e-3);
2060
2054
  camera.getWorldDirection(_dir);
2061
- _dir.multiplyScalar(vel.thrust * distScale * 100);
2055
+ _dir.multiplyScalar(thrust * distScale * 100);
2062
2056
  camera.position.add(_dir);
2063
2057
  controls.target.add(_dir);
2064
2058
  }
2065
2059
  return true;
2066
2060
  };
2067
2061
  }
2068
- function setupKeyboardControls(camera, controls, container, mode = "fly") {
2062
+ function setupKeyboardControls(camera, controls, container, mode = "fly", flySpeed = 1) {
2069
2063
  const keys = {};
2064
+ const state = { flySpeed };
2070
2065
  function onKeyDown2(e) {
2071
2066
  var _a;
2072
2067
  const tag = (_a = e.target) == null ? void 0 : _a.tagName;
@@ -2082,13 +2077,16 @@ function setupKeyboardControls(camera, controls, container, mode = "fly") {
2082
2077
  container.addEventListener("keydown", onKeyDown2);
2083
2078
  container.addEventListener("keyup", onKeyUp);
2084
2079
  container.addEventListener("blur", onBlur);
2085
- const updateFn = mode === "fly" ? createFlyUpdate(camera, controls, keys) : createOrbitUpdate(camera, controls, keys);
2080
+ const updateFn = mode === "fly" ? createFlyUpdate(camera, controls, keys, state) : createOrbitUpdate(camera, controls, keys);
2086
2081
  function cleanup() {
2087
2082
  container.removeEventListener("keydown", onKeyDown2);
2088
2083
  container.removeEventListener("keyup", onKeyUp);
2089
2084
  container.removeEventListener("blur", onBlur);
2090
2085
  }
2091
- return { update: updateFn, cleanup };
2086
+ function setFlySpeed(speed) {
2087
+ state.flySpeed = speed;
2088
+ }
2089
+ return { update: updateFn, cleanup, setFlySpeed };
2092
2090
  }
2093
2091
  function createScene(container, backgroundColor, style, rendererConfig) {
2094
2092
  const scene = new THREE__namespace.Scene();
@@ -2126,7 +2124,22 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2126
2124
  const labels = createLabelSystem(style.maxLabels);
2127
2125
  scene.add(labels.group);
2128
2126
  const cameraMode = (rendererConfig == null ? void 0 : rendererConfig.cameraMode) ?? "fly";
2129
- const keyboard = setupKeyboardControls(camera, controls, container, cameraMode);
2127
+ const keyboard = setupKeyboardControls(camera, controls, container, cameraMode, style.flySpeed ?? 1);
2128
+ controls.enableZoom = false;
2129
+ const _wheelDir = new THREE__namespace.Vector3();
2130
+ const onWheel = (e) => {
2131
+ e.preventDefault();
2132
+ let delta = -e.deltaY;
2133
+ if (e.deltaMode === 1) delta *= 40;
2134
+ if (e.deltaMode === 2) delta *= 800;
2135
+ camera.getWorldDirection(_wheelDir);
2136
+ const dist = camera.position.distanceTo(controls.target);
2137
+ const moveAmount = delta * 2e-3 * Math.max(dist * 0.1, 1);
2138
+ camera.position.addScaledVector(_wheelDir, moveAmount);
2139
+ controls.target.addScaledVector(_wheelDir, moveAmount);
2140
+ };
2141
+ canvas.addEventListener("wheel", onWheel, { passive: false });
2142
+ const scrollCleanup = () => canvas.removeEventListener("wheel", onWheel);
2130
2143
  return {
2131
2144
  scene,
2132
2145
  camera,
@@ -2137,7 +2150,8 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2137
2150
  labels,
2138
2151
  composer: null,
2139
2152
  bloomPass: null,
2140
- keyboard
2153
+ keyboard,
2154
+ scrollCleanup
2141
2155
  };
2142
2156
  }
2143
2157
  function initBloom(state, nodeCount, style) {
@@ -2164,9 +2178,21 @@ function initBloom(state, nodeCount, style) {
2164
2178
  }
2165
2179
  function startAnimationLoop(state, onTick) {
2166
2180
  let animFrame;
2181
+ let wasKeyboardActive = false;
2182
+ const _syncDir = new THREE__namespace.Vector3();
2167
2183
  function animate() {
2168
2184
  animFrame = requestAnimationFrame(animate);
2169
2185
  const keyboardActive = state.keyboard.update();
2186
+ if (wasKeyboardActive && !keyboardActive) {
2187
+ const dist = state.camera.position.distanceTo(
2188
+ state.controls.target
2189
+ );
2190
+ state.camera.getWorldDirection(_syncDir);
2191
+ state.controls.target.copy(state.camera.position).add(_syncDir.multiplyScalar(dist));
2192
+ state.camera.up.set(0, 1, 0);
2193
+ state.camera.lookAt(state.controls.target);
2194
+ }
2195
+ wasKeyboardActive = keyboardActive;
2170
2196
  if (!keyboardActive) state.controls.update();
2171
2197
  if (state.selectionRing.visible) {
2172
2198
  state.selectionRing.quaternion.copy(state.camera.quaternion);
@@ -2967,6 +2993,7 @@ function NetworkGraph3DInner(props, ref) {
2967
2993
  resizeObserver.disconnect();
2968
2994
  interaction.cleanup();
2969
2995
  state.keyboard.cleanup();
2996
+ state.scrollCleanup();
2970
2997
  state.controls.dispose();
2971
2998
  disposeObject(state.stars);
2972
2999
  disposeObject(state.selectionRing);
@@ -3282,6 +3309,11 @@ function NetworkGraph3DInner(props, ref) {
3282
3309
  }, 30);
3283
3310
  return () => clearInterval(id);
3284
3311
  }, [resolvedStyle.autoOrbit]);
3312
+ react.useEffect(() => {
3313
+ const s = sceneRef.current;
3314
+ if (!s) return;
3315
+ s.keyboard.setFlySpeed(resolvedStyle.flySpeed);
3316
+ }, [resolvedStyle.flySpeed]);
3285
3317
  react.useImperativeHandle(ref, () => ({
3286
3318
  cameraPosition(pos, lookAt, duration = 1e3) {
3287
3319
  const s = sceneRef.current;
@@ -3359,6 +3391,16 @@ function NetworkGraph3DInner(props, ref) {
3359
3391
  if (!s) return null;
3360
3392
  s.renderer.render(s.scene, s.camera);
3361
3393
  return s.renderer.domElement.toDataURL("image/png");
3394
+ },
3395
+ reheatLayout() {
3396
+ const gObj = graphObjRef.current;
3397
+ if (gObj == null ? void 0 : gObj.worker) {
3398
+ gObj.worker.postMessage({ type: "stop" });
3399
+ gObj.worker.terminate();
3400
+ gObj.worker = null;
3401
+ }
3402
+ dataRef.current.settled = false;
3403
+ setAppendedData((prev) => prev ? { ...prev } : { nodes: [], links: [] });
3362
3404
  }
3363
3405
  }));
3364
3406
  return /* @__PURE__ */ jsxRuntime.jsx(