@modernrelay/orbit-core 0.15.0 → 0.17.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.js CHANGED
@@ -1,5 +1,5 @@
1
- import { DIAGNOSTIC_SAMPLE_CAP, encodeStringTable, collectTransfers, deriveClusters, resolveClusterCenters, clusterCentroids, EnvelopeSequencer, RequestLedger } from './chunk-TDBIVBJ3.js';
2
- export { DEFAULT_CLUSTER_CENTER_RADIUS, DEFAULT_LAYOUT_SEED, DIAGNOSTIC_SAMPLE_CAP, acceptColumnar, clusterCentroids, collectTransfers, decodeStringTable, deriveClusters, encodeStringTable, generateClusterCenters, judgeEpoch, resolveClusterCenters } from './chunk-TDBIVBJ3.js';
1
+ import { DIAGNOSTIC_SAMPLE_CAP, resolveSimulation, encodeStringTable, collectTransfers, deriveClusters, resolveClusterCenters, clusterCentroids, EnvelopeSequencer, RequestLedger } from './chunk-IOC3RWQY.js';
2
+ export { DEFAULT_CLUSTER_CENTER_RADIUS, DEFAULT_LAYOUT_SEED, DIAGNOSTIC_SAMPLE_CAP, SIMULATION_PRESETS, acceptColumnar, clusterCentroids, collectTransfers, decodeStringTable, deriveClusters, encodeStringTable, generateClusterCenters, judgeEpoch, resolveClusterCenters, resolveSimulation } from './chunk-IOC3RWQY.js';
3
3
  import { createStore } from 'zustand/vanilla';
4
4
 
5
5
  // src/errors.ts
@@ -2471,6 +2471,61 @@ function selectLabelCandidates(args) {
2471
2471
  }
2472
2472
  return degreeOf !== void 0 ? degreeOf(i) : 0;
2473
2473
  };
2474
+ const declutter = config.overlap !== "allow" && project !== void 0;
2475
+ const configuredPad = config.overlapPadding ?? 2;
2476
+ const pad = Number.isFinite(configuredPad) ? Math.max(0, configuredPad) : 2;
2477
+ const CELL = 64;
2478
+ const CHAR_W = 7;
2479
+ const BOX_H = 18;
2480
+ const keptBoxes = [];
2481
+ const cells = /* @__PURE__ */ new Map();
2482
+ const cellKey = (cx, cy) => cx * 100003 + cy;
2483
+ const boxOf = (i, text) => {
2484
+ const x = positions[2 * i];
2485
+ const y = positions[2 * i + 1];
2486
+ if (x === void 0 || y === void 0 || Number.isNaN(x) || Number.isNaN(y)) return null;
2487
+ const sPt = project([x, y]);
2488
+ if (sPt === null) return null;
2489
+ const w = CHAR_W * text.length + 8 + 2 * pad;
2490
+ const h = BOX_H + 2 * pad;
2491
+ return [sPt[0] - w / 2, sPt[1] - h / 2, sPt[0] + w / 2, sPt[1] + h / 2];
2492
+ };
2493
+ const collides = (b) => {
2494
+ const cx0 = Math.floor(b[0] / CELL);
2495
+ const cy0 = Math.floor(b[1] / CELL);
2496
+ const cx1 = Math.floor(b[2] / CELL);
2497
+ const cy1 = Math.floor(b[3] / CELL);
2498
+ for (let cx = cx0; cx <= cx1; cx++) {
2499
+ for (let cy = cy0; cy <= cy1; cy++) {
2500
+ const bucket = cells.get(cellKey(cx, cy));
2501
+ if (bucket === void 0) continue;
2502
+ for (const q of bucket) {
2503
+ const x0 = keptBoxes[q];
2504
+ const y0 = keptBoxes[q + 1];
2505
+ const x1 = keptBoxes[q + 2];
2506
+ const y1 = keptBoxes[q + 3];
2507
+ if (b[0] < x1 && b[2] > x0 && b[1] < y1 && b[3] > y0) return true;
2508
+ }
2509
+ }
2510
+ }
2511
+ return false;
2512
+ };
2513
+ const claim = (b) => {
2514
+ const q = keptBoxes.length;
2515
+ keptBoxes.push(b[0], b[1], b[2], b[3]);
2516
+ const cx0 = Math.floor(b[0] / CELL);
2517
+ const cy0 = Math.floor(b[1] / CELL);
2518
+ const cx1 = Math.floor(b[2] / CELL);
2519
+ const cy1 = Math.floor(b[3] / CELL);
2520
+ for (let cx = cx0; cx <= cx1; cx++) {
2521
+ for (let cy = cy0; cy <= cy1; cy++) {
2522
+ const key = cellKey(cx, cy);
2523
+ const bucket = cells.get(key);
2524
+ if (bucket === void 0) cells.set(key, [q]);
2525
+ else bucket.push(q);
2526
+ }
2527
+ }
2528
+ };
2474
2529
  const out = [];
2475
2530
  const chosen = /* @__PURE__ */ new Set();
2476
2531
  let overloadCount = 0;
@@ -2489,7 +2544,12 @@ function selectLabelCandidates(args) {
2489
2544
  for (let j = 0; j < take; j++) {
2490
2545
  const i = forcedIdx[j];
2491
2546
  chosen.add(i);
2492
- out.push({ id: scene.idByIndex[i], text: textOf(i), forced: true });
2547
+ const text = textOf(i);
2548
+ if (declutter) {
2549
+ const b = boxOf(i, text);
2550
+ if (b !== null) claim(b);
2551
+ }
2552
+ out.push({ id: scene.idByIndex[i], text, forced: true });
2493
2553
  }
2494
2554
  }
2495
2555
  if (aboveLod && out.length < k) {
@@ -2499,10 +2559,17 @@ function selectLabelCandidates(args) {
2499
2559
  ranked.push({ i, w: weightOf(i) });
2500
2560
  }
2501
2561
  ranked.sort((a, b) => b.w - a.w || a.i - b.i);
2502
- const need = k - out.length;
2503
- for (let j = 0; j < need && j < ranked.length; j++) {
2562
+ for (let j = 0; j < ranked.length && out.length < k; j++) {
2504
2563
  const i = ranked[j].i;
2505
- out.push({ id: scene.idByIndex[i], text: textOf(i), forced: false });
2564
+ const text = textOf(i);
2565
+ if (declutter) {
2566
+ const b = boxOf(i, text);
2567
+ if (b !== null) {
2568
+ if (collides(b)) continue;
2569
+ claim(b);
2570
+ }
2571
+ }
2572
+ out.push({ id: scene.idByIndex[i], text, forced: false });
2506
2573
  }
2507
2574
  }
2508
2575
  return { placements: out, overloadCount };
@@ -6382,6 +6449,9 @@ function pruneIds(ids, base) {
6382
6449
  function createGraphInstance(opts) {
6383
6450
  const engineFactory = opts.engine;
6384
6451
  const fitViewOnFirstData = opts.fitViewOnFirstData ?? true;
6452
+ const fitViewOnSettle = opts.fitViewOnSettle ?? "follow";
6453
+ const fitViewMaxZoom = opts.fitViewMaxZoom === void 0 ? 1.5 : opts.fitViewMaxZoom;
6454
+ const clampFit = (o) => fitViewMaxZoom === null ? o : { ...o, maxZoom: fitViewMaxZoom };
6385
6455
  const store = createStore(() => ({
6386
6456
  status: "idle",
6387
6457
  revisions: { source: null, model: 0, scope: 0, render: 0, appliedRender: null },
@@ -6550,7 +6620,7 @@ function createGraphInstance(opts) {
6550
6620
  let linkColor;
6551
6621
  let linkWidth;
6552
6622
  let layout = "force";
6553
- let simulation;
6623
+ let simulation = resolveSimulation(void 0);
6554
6624
  let theme = resolveTheme(void 0);
6555
6625
  let edgeArrows = false;
6556
6626
  let showLinks = true;
@@ -9121,12 +9191,41 @@ function createGraphInstance(opts) {
9121
9191
  if (idx !== void 0) applyEmphasis(eng, idx);
9122
9192
  }
9123
9193
  }
9194
+ const SETTLE_FOLLOW_INTERVAL_FRAMES = 55;
9195
+ const SETTLE_FOLLOW_CAP_FRAMES = 480;
9196
+ let settleFollowFrames = null;
9197
+ let settleFollowContainer = null;
9198
+ const settleFollowCancelListener = () => {
9199
+ cancelSettleFollow();
9200
+ };
9201
+ function cancelSettleFollow() {
9202
+ if (settleFollowContainer !== null) {
9203
+ settleFollowContainer.removeEventListener?.("pointerdown", settleFollowCancelListener);
9204
+ settleFollowContainer.removeEventListener?.("wheel", settleFollowCancelListener);
9205
+ settleFollowContainer = null;
9206
+ }
9207
+ settleFollowFrames = null;
9208
+ }
9209
+ function armSettleFollow(s) {
9210
+ if (fitViewOnSettle === false || layout !== "force") return;
9211
+ settleFollowFrames = 0;
9212
+ if (typeof s.container.addEventListener === "function") {
9213
+ settleFollowContainer = s.container;
9214
+ s.container.addEventListener("pointerdown", settleFollowCancelListener);
9215
+ s.container.addEventListener("wheel", settleFollowCancelListener);
9216
+ }
9217
+ }
9218
+ function settleFollowFit(eng, durationMs) {
9219
+ if (effectiveReducedMotion()) eng.fitView(clampFit({ durationMs: 0 }));
9220
+ else eng.fitView(clampFit({ durationMs }));
9221
+ }
9124
9222
  function maybeFitView(eng) {
9125
9223
  if (!fitViewOnFirstData || session === null || session.fitDone) return;
9126
9224
  if (accepted === null) return;
9127
9225
  session.fitDone = true;
9128
- if (effectiveReducedMotion()) eng.fitView({ durationMs: 0 });
9129
- else eng.fitView();
9226
+ if (effectiveReducedMotion()) eng.fitView(clampFit({ durationMs: 0 }));
9227
+ else eng.fitView(clampFit({}));
9228
+ armSettleFollow(session);
9130
9229
  }
9131
9230
  function nodeIndexBase() {
9132
9231
  return accepted === null ? EMPTY_INDEX2 : accepted.nodeIndex;
@@ -11541,10 +11640,13 @@ function createGraphInstance(opts) {
11541
11640
  {
11542
11641
  const c = {};
11543
11642
  let any = false;
11544
- if (update.simulation !== void 0 && !Object.is(update.simulation, simulation)) {
11545
- simulation = update.simulation;
11546
- c.simulation = update.simulation;
11547
- any = true;
11643
+ if (update.simulation !== void 0) {
11644
+ const nextSimulation = resolveSimulation(update.simulation);
11645
+ if (!Object.is(nextSimulation, simulation)) {
11646
+ simulation = nextSimulation;
11647
+ c.simulation = nextSimulation;
11648
+ any = true;
11649
+ }
11548
11650
  }
11549
11651
  if (update.theme !== void 0) {
11550
11652
  const nextTheme = resolveTheme(update.theme);
@@ -12125,6 +12227,14 @@ function createGraphInstance(opts) {
12125
12227
  onFrame(timeMs) {
12126
12228
  if (!active()) return;
12127
12229
  frameCadence += 1;
12230
+ if (settleFollowFrames !== null && fitViewOnSettle === "follow") {
12231
+ settleFollowFrames += 1;
12232
+ if (settleFollowFrames > SETTLE_FOLLOW_CAP_FRAMES) {
12233
+ cancelSettleFollow();
12234
+ } else if (settleFollowFrames % SETTLE_FOLLOW_INTERVAL_FRAMES === 0) {
12235
+ settleFollowFit(s.engine, 650);
12236
+ }
12237
+ }
12128
12238
  pressureSampler.noteFrame(timeMs, !store.getState().simulationRunning);
12129
12239
  flushCrossfilterNotify();
12130
12240
  if (timeMs - lastPerfSampleAt >= PERF_SAMPLE_THROTTLE_MS) {
@@ -12171,6 +12281,10 @@ function createGraphInstance(opts) {
12171
12281
  },
12172
12282
  onSimulationEnd() {
12173
12283
  if (!active()) return;
12284
+ if (settleFollowFrames !== null) {
12285
+ settleFollowFit(s.engine, 800);
12286
+ cancelSettleFollow();
12287
+ }
12174
12288
  if (accretionPinIds !== null) {
12175
12289
  accretionPinIds = null;
12176
12290
  pushPinsToEngine(s.engine, store.getState().pins);
@@ -12404,6 +12518,7 @@ function createGraphInstance(opts) {
12404
12518
  s.alive = false;
12405
12519
  session = null;
12406
12520
  mountPromise = null;
12521
+ cancelSettleFollow();
12407
12522
  cancelRerankTimer();
12408
12523
  flushCrossfilterNotify();
12409
12524
  if (quiescenceTimer !== null) {
@@ -12896,6 +13011,7 @@ function createGraphInstance(opts) {
12896
13011
  }
12897
13012
  }
12898
13013
  if (state.camera !== null) {
13014
+ cancelSettleFollow();
12899
13015
  const eng = engineIfReady();
12900
13016
  if (eng !== null) {
12901
13017
  if (effectiveReducedMotion()) eng.setViewport(state.camera, { durationMs: 0 });
@@ -13215,6 +13331,7 @@ function createGraphInstance(opts) {
13215
13331
  });
13216
13332
  }
13217
13333
  function focusNode(id, opts2) {
13334
+ cancelSettleFollow();
13218
13335
  const eng = engineIfReady();
13219
13336
  if (eng === null || scene === null) return EMPTY_IDS;
13220
13337
  const idx = scene.indexById.get(id);
@@ -13295,6 +13412,7 @@ function createGraphInstance(opts) {
13295
13412
  return bytes;
13296
13413
  }
13297
13414
  function cameraZoom(factor) {
13415
+ cancelSettleFollow();
13298
13416
  const eng = engineIfReady();
13299
13417
  if (eng === null) return;
13300
13418
  if (effectiveReducedMotion()) eng.zoom(factor, 0);
@@ -13348,10 +13466,11 @@ function createGraphInstance(opts) {
13348
13466
  destroy,
13349
13467
  on,
13350
13468
  fitView: () => {
13469
+ cancelSettleFollow();
13351
13470
  const eng = engineIfReady();
13352
13471
  if (eng === null) return;
13353
- if (effectiveReducedMotion()) eng.fitView({ durationMs: 0 });
13354
- else eng.fitView();
13472
+ if (effectiveReducedMotion()) eng.fitView(clampFit({ durationMs: 0 }));
13473
+ else eng.fitView(clampFit({}));
13355
13474
  },
13356
13475
  zoomIn: () => {
13357
13476
  cameraZoom(ZOOM_STEP);
@@ -13360,6 +13479,7 @@ function createGraphInstance(opts) {
13360
13479
  cameraZoom(1 / ZOOM_STEP);
13361
13480
  },
13362
13481
  setViewport: (v) => {
13482
+ cancelSettleFollow();
13363
13483
  const eng = engineIfReady();
13364
13484
  if (eng === null) return;
13365
13485
  if (effectiveReducedMotion()) eng.setViewport(v, { durationMs: 0 });