@cocorof/graphier 1.4.3 → 1.4.5

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
@@ -2068,7 +2068,7 @@ function createPanUpdate(camera, controls, keys) {
2068
2068
  const target = controls.target;
2069
2069
  const dist = camera.position.distanceTo(target);
2070
2070
  if (dx || dy) {
2071
- const step = Math.max(dist, 50) * PAN_SPEED;
2071
+ const step = Math.max(dist, 50) * PAN_SPEED * (controls.panSpeed || 1);
2072
2072
  _panRight.setFromMatrixColumn(camera.matrix, 0);
2073
2073
  _panUp.setFromMatrixColumn(camera.matrix, 1);
2074
2074
  _panOffset.set(0, 0, 0).addScaledVector(_panRight, dx * step).addScaledVector(_panUp, dy * step);
@@ -2247,6 +2247,7 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2247
2247
  controls.zoomSpeed = 1.2;
2248
2248
  controls.minDistance = 10;
2249
2249
  controls.maxDistance = 3e4;
2250
+ controls.screenSpacePanning = true;
2250
2251
  scene.add(new THREE__namespace.AmbientLight(16777215, 0.8));
2251
2252
  const stars = createStarField(4e3, 8e3);
2252
2253
  stars.visible = style.starField;
@@ -2260,26 +2261,35 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2260
2261
  controls.enableZoom = false;
2261
2262
  const flags = {
2262
2263
  is2D: false,
2263
- keyboardMode3D: cameraMode
2264
+ keyboardMode3D: cameraMode,
2265
+ rotateButton: null,
2266
+ panSpeedBase: 1
2264
2267
  };
2265
- const _wheelDir = new THREE__namespace.Vector3();
2268
+ const _wheelOffset = new THREE__namespace.Vector3();
2266
2269
  const onWheel = (e) => {
2267
2270
  if (flags.is2D) return;
2268
2271
  e.preventDefault();
2269
2272
  let delta = -e.deltaY;
2270
2273
  if (e.deltaMode === 1) delta *= 40;
2271
2274
  if (e.deltaMode === 2) delta *= 800;
2272
- camera.getWorldDirection(_wheelDir);
2273
- const dist = camera.position.distanceTo(controls.target);
2275
+ const target = controls.target;
2276
+ _wheelOffset.copy(camera.position).sub(target);
2277
+ const dist = _wheelOffset.length();
2274
2278
  const moveAmount = delta * 2e-3 * Math.max(dist * 0.1, 1);
2275
- camera.position.addScaledVector(_wheelDir, moveAmount);
2276
- controls.target.addScaledVector(_wheelDir, moveAmount);
2279
+ const newDist = Math.min(
2280
+ Math.max(dist - moveAmount, controls.minDistance),
2281
+ controls.maxDistance
2282
+ );
2283
+ _wheelOffset.setLength(newDist);
2284
+ camera.position.copy(target).add(_wheelOffset);
2277
2285
  };
2278
2286
  canvas.addEventListener("wheel", onWheel, { passive: false });
2287
+ const trackballCleanup = setupTrackballRotation(canvas, camera, controls, flags);
2279
2288
  const scrollCleanup = () => {
2280
2289
  canvas.removeEventListener("wheel", onWheel);
2281
2290
  canvas.removeEventListener("dragstart", onDragStart);
2282
2291
  canvas.removeEventListener("selectstart", onSelectStart);
2292
+ trackballCleanup();
2283
2293
  };
2284
2294
  return {
2285
2295
  scene,
@@ -2296,24 +2306,82 @@ function createScene(container, backgroundColor, style, rendererConfig) {
2296
2306
  flags
2297
2307
  };
2298
2308
  }
2299
- const MOUSE_ACTION = {
2300
- rotate: THREE__namespace.MOUSE.ROTATE,
2301
- pan: THREE__namespace.MOUSE.PAN
2302
- };
2309
+ const TRACKBALL_SPEED = 4e-3;
2310
+ function setupTrackballRotation(canvas, camera, controls, flags) {
2311
+ let rotating = false;
2312
+ let lastX = 0;
2313
+ let lastY = 0;
2314
+ const _offset2 = new THREE__namespace.Vector3();
2315
+ const _right2 = new THREE__namespace.Vector3();
2316
+ const _upAxis = new THREE__namespace.Vector3();
2317
+ const _q2 = new THREE__namespace.Quaternion();
2318
+ const _qPitch = new THREE__namespace.Quaternion();
2319
+ function onDown(e) {
2320
+ if (flags.rotateButton == null || e.button !== flags.rotateButton) return;
2321
+ rotating = true;
2322
+ lastX = e.clientX;
2323
+ lastY = e.clientY;
2324
+ try {
2325
+ canvas.setPointerCapture(e.pointerId);
2326
+ } catch {
2327
+ }
2328
+ }
2329
+ function onMove(e) {
2330
+ if (!rotating) return;
2331
+ const dx = e.clientX - lastX;
2332
+ const dy = e.clientY - lastY;
2333
+ lastX = e.clientX;
2334
+ lastY = e.clientY;
2335
+ if (!dx && !dy) return;
2336
+ const target = controls.target;
2337
+ _offset2.copy(camera.position).sub(target);
2338
+ _right2.setFromMatrixColumn(camera.matrix, 0);
2339
+ _upAxis.setFromMatrixColumn(camera.matrix, 1);
2340
+ _q2.setFromAxisAngle(_upAxis, -dx * TRACKBALL_SPEED);
2341
+ _qPitch.setFromAxisAngle(_right2, -dy * TRACKBALL_SPEED);
2342
+ _q2.multiply(_qPitch);
2343
+ _offset2.applyQuaternion(_q2);
2344
+ camera.up.applyQuaternion(_q2).normalize();
2345
+ camera.position.copy(target).add(_offset2);
2346
+ camera.lookAt(target);
2347
+ }
2348
+ function onUp(e) {
2349
+ if (!rotating) return;
2350
+ rotating = false;
2351
+ try {
2352
+ canvas.releasePointerCapture(e.pointerId);
2353
+ } catch {
2354
+ }
2355
+ }
2356
+ canvas.addEventListener("pointerdown", onDown);
2357
+ canvas.addEventListener("pointermove", onMove);
2358
+ canvas.addEventListener("pointerup", onUp);
2359
+ canvas.addEventListener("pointercancel", onUp);
2360
+ return () => {
2361
+ canvas.removeEventListener("pointerdown", onDown);
2362
+ canvas.removeEventListener("pointermove", onMove);
2363
+ canvas.removeEventListener("pointerup", onUp);
2364
+ canvas.removeEventListener("pointercancel", onUp);
2365
+ };
2366
+ }
2303
2367
  function applyNavigationMode(state, is2D, nav) {
2304
2368
  const { controls, camera, keyboard } = state;
2305
2369
  state.flags.is2D = is2D;
2306
2370
  const left = (nav == null ? void 0 : nav.leftButton) ?? (is2D ? "pan" : "rotate");
2307
2371
  const right = (nav == null ? void 0 : nav.rightButton) ?? (is2D ? "rotate" : "pan");
2372
+ const NONE = -1;
2308
2373
  controls.mouseButtons = {
2309
- LEFT: MOUSE_ACTION[left],
2374
+ LEFT: left === "pan" ? THREE__namespace.MOUSE.PAN : NONE,
2310
2375
  MIDDLE: THREE__namespace.MOUSE.DOLLY,
2311
- RIGHT: MOUSE_ACTION[right]
2376
+ RIGHT: right === "pan" ? THREE__namespace.MOUSE.PAN : NONE
2312
2377
  };
2313
- controls.enableRotate = left === "rotate" || right === "rotate";
2378
+ controls.enableRotate = false;
2379
+ state.flags.rotateButton = left === "rotate" ? 0 : right === "rotate" ? 2 : null;
2314
2380
  controls.touches = left === "pan" ? { ONE: THREE__namespace.TOUCH.PAN, TWO: THREE__namespace.TOUCH.DOLLY_PAN } : { ONE: THREE__namespace.TOUCH.ROTATE, TWO: THREE__namespace.TOUCH.DOLLY_PAN };
2315
2381
  controls.enableZoom = is2D;
2316
- controls.screenSpacePanning = is2D;
2382
+ controls.screenSpacePanning = true;
2383
+ state.flags.panSpeedBase = (nav == null ? void 0 : nav.panSpeed) ?? 1;
2384
+ controls.panSpeed = state.flags.panSpeedBase;
2317
2385
  const kb = (nav == null ? void 0 : nav.keyboard) ?? (is2D ? "pan" : state.flags.keyboardMode3D);
2318
2386
  if (kb === "off") {
2319
2387
  keyboard.setEnabled(false);
@@ -2649,6 +2717,7 @@ function animateCamera(camera, controls, targetPos, targetLookAt, duration) {
2649
2717
  function zoomToFitPositions(camera, controls, positions, duration, padding) {
2650
2718
  const count = positions.length / 3;
2651
2719
  if (count === 0) return;
2720
+ camera.up.set(0, 1, 0);
2652
2721
  let cx = 0, cy = 0, cz = 0;
2653
2722
  for (let i = 0; i < count; i++) {
2654
2723
  cx += positions[i * 3];
@@ -2893,8 +2962,8 @@ function setupInteraction(canvas, camera, controls, getNodesMesh, getNodes, call
2893
2962
  }
2894
2963
  }
2895
2964
  function onContextMenu2(e) {
2896
- if (!callbacks.onContextMenu) return;
2897
2965
  e.preventDefault();
2966
+ if (!callbacks.onContextMenu) return;
2898
2967
  const nodesMesh = getNodesMesh();
2899
2968
  if (!nodesMesh) return;
2900
2969
  const rect = canvas.getBoundingClientRect();
@@ -3158,7 +3227,29 @@ function NetworkGraph3DInner(props, ref) {
3158
3227
  rendererProp
3159
3228
  );
3160
3229
  sceneRef.current = state;
3230
+ let panBoostAt = 0;
3231
+ let graphRadius = 0;
3161
3232
  const cancelAnim = startAnimationLoop(state, () => {
3233
+ const nowMs = performance.now();
3234
+ if (nowMs - panBoostAt > 400) {
3235
+ panBoostAt = nowMs;
3236
+ const pos = dataRef.current.positions;
3237
+ if (pos) {
3238
+ let maxSq = 0;
3239
+ for (let i = 0; i < pos.length; i += 3) {
3240
+ const sq = pos[i] * pos[i] + pos[i + 1] * pos[i + 1] + pos[i + 2] * pos[i + 2];
3241
+ if (sq > maxSq) maxSq = sq;
3242
+ }
3243
+ graphRadius = Math.sqrt(maxSq);
3244
+ }
3245
+ if (graphRadius > 0) {
3246
+ const dist = state.camera.position.distanceTo(
3247
+ state.controls.target
3248
+ );
3249
+ const boost = Math.min(3, Math.max(1, graphRadius / (3 * dist)));
3250
+ state.controls.panSpeed = state.flags.panSpeedBase * boost;
3251
+ }
3252
+ }
3162
3253
  updateLabels(
3163
3254
  state.labels,
3164
3255
  dataRef.current.nodes,