@almadar/ui 5.117.0 → 5.119.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.
@@ -35322,11 +35322,11 @@ var init_DocumentViewer = __esm({
35322
35322
  DocumentViewer.displayName = "DocumentViewer";
35323
35323
  }
35324
35324
  });
35325
- function measureLabelWidth(text) {
35325
+ function measureLabelWidth(text, fontFamily = "system-ui") {
35326
35326
  if (typeof document === "undefined") return text.length * 6;
35327
35327
  if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
35328
35328
  if (!labelMeasureCtx) return text.length * 6;
35329
- labelMeasureCtx.font = "12px system-ui";
35329
+ labelMeasureCtx.font = `12px ${fontFamily}`;
35330
35330
  return labelMeasureCtx.measureText(text).width + 4;
35331
35331
  }
35332
35332
  function getGroupColor(group, groups) {
@@ -35334,6 +35334,26 @@ function getGroupColor(group, groups) {
35334
35334
  const idx = groups.indexOf(group);
35335
35335
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
35336
35336
  }
35337
+ function hashSeed(str) {
35338
+ let h = 1779033703 ^ str.length;
35339
+ for (let i = 0; i < str.length; i++) {
35340
+ h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
35341
+ h = h << 13 | h >>> 19;
35342
+ }
35343
+ return (h ^= h >>> 16) >>> 0;
35344
+ }
35345
+ function mulberry32(a) {
35346
+ return function() {
35347
+ a |= 0;
35348
+ a = a + 1831565813 | 0;
35349
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
35350
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
35351
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
35352
+ };
35353
+ }
35354
+ function edgeKeyOf(a, b) {
35355
+ return a < b ? `${a}\0${b}` : `${b}\0${a}`;
35356
+ }
35337
35357
  function resolveColor3(color, el) {
35338
35358
  const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
35339
35359
  if (!m) return color;
@@ -35365,6 +35385,7 @@ var init_GraphCanvas = __esm({
35365
35385
  title,
35366
35386
  nodes: propNodes = [],
35367
35387
  edges: propEdges = [],
35388
+ similarity: propSimilarity = [],
35368
35389
  height = 400,
35369
35390
  showLabels = true,
35370
35391
  interactive = true,
@@ -35466,6 +35487,7 @@ var init_GraphCanvas = __esm({
35466
35487
  if (!canvas || propNodes.length === 0) return;
35467
35488
  const w = logicalW;
35468
35489
  const h = height;
35490
+ const labelFont = resolveColor3("var(--font-family)", canvas) || "system-ui";
35469
35491
  const prevPos = /* @__PURE__ */ new Map();
35470
35492
  for (const pn of nodesRef.current) if (pn.x != null && pn.y != null) prevPos.set(pn.id, { x: pn.x, y: pn.y });
35471
35493
  const simNodes = propNodes.map((n, idx) => {
@@ -35485,8 +35507,9 @@ var init_GraphCanvas = __esm({
35485
35507
  x = gapX * (idx % cols + 1);
35486
35508
  y = gapY * (Math.floor(idx / cols) + 1);
35487
35509
  } else {
35488
- x = w * 0.2 + Math.random() * w * 0.6;
35489
- y = h * 0.2 + Math.random() * h * 0.6;
35510
+ const rand = mulberry32(hashSeed(n.id));
35511
+ x = w * 0.2 + rand() * w * 0.6;
35512
+ y = h * 0.2 + rand() * h * 0.6;
35490
35513
  }
35491
35514
  }
35492
35515
  return { ...n, x, y, vx: 0, vy: 0, fx: 0, fy: 0 };
@@ -35499,10 +35522,15 @@ var init_GraphCanvas = __esm({
35499
35522
  const fullRelayout = !laidOutRef.current || overlap < 0.5;
35500
35523
  if (layout === "force") {
35501
35524
  const maxIterations = 300;
35502
- const tick = () => {
35525
+ const COOL = 0.12;
35526
+ const COLLIDE_PASSES = 6;
35527
+ const LABEL_GAP = 12;
35528
+ const LABEL_H = 16;
35529
+ const tick = (iter) => {
35503
35530
  const nodes = nodesRef.current;
35504
35531
  const centerX = w / 2;
35505
35532
  const centerY = h / 2;
35533
+ const temp = Math.max(COOL, 1 - iter / maxIterations);
35506
35534
  for (const node of nodes) {
35507
35535
  node.fx = 0;
35508
35536
  node.fy = 0;
@@ -35512,7 +35540,7 @@ var init_GraphCanvas = __esm({
35512
35540
  const dx = nodes[j].x - nodes[i].x;
35513
35541
  const dy = nodes[j].y - nodes[i].y;
35514
35542
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35515
- const force = repulsion / (dist * dist);
35543
+ const force = repulsion / (dist * dist) * temp;
35516
35544
  const fx = dx / dist * force;
35517
35545
  const fy = dy / dist * force;
35518
35546
  nodes[i].fx -= fx;
@@ -35521,22 +35549,35 @@ var init_GraphCanvas = __esm({
35521
35549
  nodes[j].fy += fy;
35522
35550
  }
35523
35551
  }
35524
- for (const edge of propEdges) {
35525
- const source = nodes.find((n) => n.id === edge.source);
35526
- const target = nodes.find((n) => n.id === edge.target);
35527
- if (!source || !target) continue;
35528
- const dx = target.x - source.x;
35529
- const dy = target.y - source.y;
35552
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
35553
+ const spring = (a, b, rest, k) => {
35554
+ const dx = b.x - a.x;
35555
+ const dy = b.y - a.y;
35530
35556
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35531
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35532
- const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
35533
- const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
35557
+ const force = (dist - rest) * k * temp;
35534
35558
  const fx = dx / dist * force;
35535
35559
  const fy = dy / dist * force;
35536
- source.fx += fx;
35537
- source.fy += fy;
35538
- target.fx -= fx;
35539
- target.fy -= fy;
35560
+ a.fx += fx;
35561
+ a.fy += fy;
35562
+ b.fx -= fx;
35563
+ b.fy -= fy;
35564
+ };
35565
+ const drawnPairs = /* @__PURE__ */ new Set();
35566
+ for (const edge of propEdges) {
35567
+ const source = nodeById.get(edge.source);
35568
+ const target = nodeById.get(edge.target);
35569
+ if (!source || !target) continue;
35570
+ drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35571
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35572
+ spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
35573
+ }
35574
+ for (const pair of propSimilarity) {
35575
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
35576
+ const source = nodeById.get(pair.source);
35577
+ const target = nodeById.get(pair.target);
35578
+ if (!source || !target) continue;
35579
+ const w2 = Math.min(1, Math.max(0, pair.weight));
35580
+ spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
35540
35581
  }
35541
35582
  const centroids = /* @__PURE__ */ new Map();
35542
35583
  for (const node of nodes) {
@@ -35550,14 +35591,20 @@ var init_GraphCanvas = __esm({
35550
35591
  c.y += node.y;
35551
35592
  c.n += 1;
35552
35593
  }
35594
+ const multiCluster = centroids.size > 1;
35553
35595
  for (const node of nodes) {
35554
- const c = centroids.get(node.group ?? "__none__");
35555
- if (c && c.n > 1) {
35556
- node.fx += (c.x / c.n - node.x) * 0.04;
35557
- node.fy += (c.y / c.n - node.y) * 0.04;
35596
+ if (multiCluster) {
35597
+ const c = centroids.get(node.group ?? "__none__");
35598
+ if (c && c.n > 1) {
35599
+ node.fx += (c.x / c.n - node.x) * 0.04 * temp;
35600
+ node.fy += (c.y / c.n - node.y) * 0.04 * temp;
35601
+ } else {
35602
+ node.fx += (centerX - node.x) * 0.01 * temp;
35603
+ node.fy += (centerY - node.y) * 0.01 * temp;
35604
+ }
35558
35605
  } else {
35559
- node.fx += (centerX - node.x) * 0.01;
35560
- node.fy += (centerY - node.y) * 0.01;
35606
+ node.fx += (centerX - node.x) * 8e-3 * temp;
35607
+ node.fy += (centerY - node.y) * 8e-3 * temp;
35561
35608
  }
35562
35609
  }
35563
35610
  const damping = 0.9;
@@ -35569,42 +35616,53 @@ var init_GraphCanvas = __esm({
35569
35616
  node.x = Math.max(30, Math.min(w - 30, node.x));
35570
35617
  node.y = Math.max(30, Math.min(h - 30, node.y));
35571
35618
  }
35572
- const LABEL_GAP = 12;
35573
- const LABEL_H = 16;
35574
35619
  const pad = nodeSpacing / 2;
35575
- for (let i = 0; i < nodes.length; i++) {
35576
- for (let j = i + 1; j < nodes.length; j++) {
35577
- const a = nodes[i];
35578
- const b = nodes[j];
35579
- const ar = a.size || 8;
35580
- const br = b.size || 8;
35581
- if (showLabels) {
35582
- if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
35583
- if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
35584
- }
35585
- const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
35586
- const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
35587
- const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
35588
- const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
35589
- const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
35590
- const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
35591
- const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
35592
- if (overlapX > 0 && overlapY > 0) {
35593
- if (overlapX <= overlapY) {
35594
- const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
35595
- a.x = Math.max(30, Math.min(w - 30, a.x - push));
35596
- b.x = Math.max(30, Math.min(w - 30, b.x + push));
35597
- } else {
35598
- const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
35599
- a.y = Math.max(30, Math.min(h - 30, a.y - push));
35600
- b.y = Math.max(30, Math.min(h - 30, b.y + push));
35620
+ const rectsOf = (n) => {
35621
+ const r = n.size || 8;
35622
+ const lw = showLabels ? n.labelW ?? (n.labelW = n.label ? measureLabelWidth(n.label, labelFont) : 0) : 0;
35623
+ return {
35624
+ circle: [n.x - r - pad, n.y - r - pad, n.x + r + pad, n.y + r + pad],
35625
+ label: [n.x - lw / 2 - pad, n.y + r + LABEL_GAP - 8, n.x + lw / 2 + pad, n.y + r + LABEL_GAP + LABEL_H]
35626
+ };
35627
+ };
35628
+ const sep = (r1, r2) => {
35629
+ const ox = Math.min(r1[2], r2[2]) - Math.max(r1[0], r2[0]);
35630
+ const oy = Math.min(r1[3], r2[3]) - Math.max(r1[1], r2[1]);
35631
+ if (ox <= 0 || oy <= 0) return null;
35632
+ return ox <= oy ? { axis: "x", depth: ox, sign: r1[0] + r1[2] < r2[0] + r2[2] ? 1 : -1 } : { axis: "y", depth: oy, sign: r1[1] + r1[3] < r2[1] + r2[3] ? 1 : -1 };
35633
+ };
35634
+ for (let pass = 0; pass < COLLIDE_PASSES; pass++) {
35635
+ for (let i = 0; i < nodes.length; i++) {
35636
+ for (let j = i + 1; j < nodes.length; j++) {
35637
+ const a = nodes[i];
35638
+ const b = nodes[j];
35639
+ const ra = rectsOf(a);
35640
+ const rb = rectsOf(b);
35641
+ let best = null;
35642
+ for (const [r1, r2] of [[ra.circle, rb.circle], [ra.circle, rb.label], [ra.label, rb.circle], [ra.label, rb.label]]) {
35643
+ const s = sep(r1, r2);
35644
+ if (s && (!best || s.depth < best.depth)) best = s;
35645
+ }
35646
+ if (best) {
35647
+ const push = best.depth / 2;
35648
+ if (best.axis === "x") {
35649
+ a.x = Math.max(30, Math.min(w - 30, a.x - push * best.sign));
35650
+ b.x = Math.max(30, Math.min(w - 30, b.x + push * best.sign));
35651
+ a.vx = 0;
35652
+ b.vx = 0;
35653
+ } else {
35654
+ a.y = Math.max(30, Math.min(h - 30, a.y - push * best.sign));
35655
+ b.y = Math.max(30, Math.min(h - 30, b.y + push * best.sign));
35656
+ a.vy = 0;
35657
+ b.vy = 0;
35658
+ }
35601
35659
  }
35602
35660
  }
35603
35661
  }
35604
35662
  }
35605
35663
  };
35606
35664
  if (fullRelayout) {
35607
- for (let i = 0; i < maxIterations; i++) tick();
35665
+ for (let i = 0; i < maxIterations; i++) tick(i);
35608
35666
  laidOutRef.current = true;
35609
35667
  } else {
35610
35668
  const centroids = /* @__PURE__ */ new Map();
@@ -35642,7 +35700,7 @@ var init_GraphCanvas = __esm({
35642
35700
  return () => {
35643
35701
  cancelAnimationFrame(animRef.current);
35644
35702
  };
35645
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35703
+ }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35646
35704
  useEffect(() => {
35647
35705
  const canvas = canvasRef.current;
35648
35706
  if (!canvas) return;
@@ -34732,11 +34732,11 @@ var init_DocumentViewer = __esm({
34732
34732
  DocumentViewer.displayName = "DocumentViewer";
34733
34733
  }
34734
34734
  });
34735
- function measureLabelWidth(text) {
34735
+ function measureLabelWidth(text, fontFamily = "system-ui") {
34736
34736
  if (typeof document === "undefined") return text.length * 6;
34737
34737
  if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
34738
34738
  if (!labelMeasureCtx) return text.length * 6;
34739
- labelMeasureCtx.font = "12px system-ui";
34739
+ labelMeasureCtx.font = `12px ${fontFamily}`;
34740
34740
  return labelMeasureCtx.measureText(text).width + 4;
34741
34741
  }
34742
34742
  function getGroupColor(group, groups) {
@@ -34744,6 +34744,26 @@ function getGroupColor(group, groups) {
34744
34744
  const idx = groups.indexOf(group);
34745
34745
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
34746
34746
  }
34747
+ function hashSeed(str) {
34748
+ let h = 1779033703 ^ str.length;
34749
+ for (let i = 0; i < str.length; i++) {
34750
+ h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
34751
+ h = h << 13 | h >>> 19;
34752
+ }
34753
+ return (h ^= h >>> 16) >>> 0;
34754
+ }
34755
+ function mulberry32(a) {
34756
+ return function() {
34757
+ a |= 0;
34758
+ a = a + 1831565813 | 0;
34759
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
34760
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
34761
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
34762
+ };
34763
+ }
34764
+ function edgeKeyOf(a, b) {
34765
+ return a < b ? `${a}\0${b}` : `${b}\0${a}`;
34766
+ }
34747
34767
  function resolveColor3(color, el) {
34748
34768
  const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
34749
34769
  if (!m) return color;
@@ -34775,6 +34795,7 @@ var init_GraphCanvas = __esm({
34775
34795
  title,
34776
34796
  nodes: propNodes = [],
34777
34797
  edges: propEdges = [],
34798
+ similarity: propSimilarity = [],
34778
34799
  height = 400,
34779
34800
  showLabels = true,
34780
34801
  interactive = true,
@@ -34876,6 +34897,7 @@ var init_GraphCanvas = __esm({
34876
34897
  if (!canvas || propNodes.length === 0) return;
34877
34898
  const w = logicalW;
34878
34899
  const h = height;
34900
+ const labelFont = resolveColor3("var(--font-family)", canvas) || "system-ui";
34879
34901
  const prevPos = /* @__PURE__ */ new Map();
34880
34902
  for (const pn of nodesRef.current) if (pn.x != null && pn.y != null) prevPos.set(pn.id, { x: pn.x, y: pn.y });
34881
34903
  const simNodes = propNodes.map((n, idx) => {
@@ -34895,8 +34917,9 @@ var init_GraphCanvas = __esm({
34895
34917
  x = gapX * (idx % cols + 1);
34896
34918
  y = gapY * (Math.floor(idx / cols) + 1);
34897
34919
  } else {
34898
- x = w * 0.2 + Math.random() * w * 0.6;
34899
- y = h * 0.2 + Math.random() * h * 0.6;
34920
+ const rand = mulberry32(hashSeed(n.id));
34921
+ x = w * 0.2 + rand() * w * 0.6;
34922
+ y = h * 0.2 + rand() * h * 0.6;
34900
34923
  }
34901
34924
  }
34902
34925
  return { ...n, x, y, vx: 0, vy: 0, fx: 0, fy: 0 };
@@ -34909,10 +34932,15 @@ var init_GraphCanvas = __esm({
34909
34932
  const fullRelayout = !laidOutRef.current || overlap < 0.5;
34910
34933
  if (layout === "force") {
34911
34934
  const maxIterations = 300;
34912
- const tick = () => {
34935
+ const COOL = 0.12;
34936
+ const COLLIDE_PASSES = 6;
34937
+ const LABEL_GAP = 12;
34938
+ const LABEL_H = 16;
34939
+ const tick = (iter) => {
34913
34940
  const nodes = nodesRef.current;
34914
34941
  const centerX = w / 2;
34915
34942
  const centerY = h / 2;
34943
+ const temp = Math.max(COOL, 1 - iter / maxIterations);
34916
34944
  for (const node of nodes) {
34917
34945
  node.fx = 0;
34918
34946
  node.fy = 0;
@@ -34922,7 +34950,7 @@ var init_GraphCanvas = __esm({
34922
34950
  const dx = nodes[j].x - nodes[i].x;
34923
34951
  const dy = nodes[j].y - nodes[i].y;
34924
34952
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34925
- const force = repulsion / (dist * dist);
34953
+ const force = repulsion / (dist * dist) * temp;
34926
34954
  const fx = dx / dist * force;
34927
34955
  const fy = dy / dist * force;
34928
34956
  nodes[i].fx -= fx;
@@ -34931,22 +34959,35 @@ var init_GraphCanvas = __esm({
34931
34959
  nodes[j].fy += fy;
34932
34960
  }
34933
34961
  }
34934
- for (const edge of propEdges) {
34935
- const source = nodes.find((n) => n.id === edge.source);
34936
- const target = nodes.find((n) => n.id === edge.target);
34937
- if (!source || !target) continue;
34938
- const dx = target.x - source.x;
34939
- const dy = target.y - source.y;
34962
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
34963
+ const spring = (a, b, rest, k) => {
34964
+ const dx = b.x - a.x;
34965
+ const dy = b.y - a.y;
34940
34966
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
34941
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34942
- const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
34943
- const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
34967
+ const force = (dist - rest) * k * temp;
34944
34968
  const fx = dx / dist * force;
34945
34969
  const fy = dy / dist * force;
34946
- source.fx += fx;
34947
- source.fy += fy;
34948
- target.fx -= fx;
34949
- target.fy -= fy;
34970
+ a.fx += fx;
34971
+ a.fy += fy;
34972
+ b.fx -= fx;
34973
+ b.fy -= fy;
34974
+ };
34975
+ const drawnPairs = /* @__PURE__ */ new Set();
34976
+ for (const edge of propEdges) {
34977
+ const source = nodeById.get(edge.source);
34978
+ const target = nodeById.get(edge.target);
34979
+ if (!source || !target) continue;
34980
+ drawnPairs.add(edgeKeyOf(edge.source, edge.target));
34981
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
34982
+ spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
34983
+ }
34984
+ for (const pair of propSimilarity) {
34985
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
34986
+ const source = nodeById.get(pair.source);
34987
+ const target = nodeById.get(pair.target);
34988
+ if (!source || !target) continue;
34989
+ const w2 = Math.min(1, Math.max(0, pair.weight));
34990
+ spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
34950
34991
  }
34951
34992
  const centroids = /* @__PURE__ */ new Map();
34952
34993
  for (const node of nodes) {
@@ -34960,14 +35001,20 @@ var init_GraphCanvas = __esm({
34960
35001
  c.y += node.y;
34961
35002
  c.n += 1;
34962
35003
  }
35004
+ const multiCluster = centroids.size > 1;
34963
35005
  for (const node of nodes) {
34964
- const c = centroids.get(node.group ?? "__none__");
34965
- if (c && c.n > 1) {
34966
- node.fx += (c.x / c.n - node.x) * 0.04;
34967
- node.fy += (c.y / c.n - node.y) * 0.04;
35006
+ if (multiCluster) {
35007
+ const c = centroids.get(node.group ?? "__none__");
35008
+ if (c && c.n > 1) {
35009
+ node.fx += (c.x / c.n - node.x) * 0.04 * temp;
35010
+ node.fy += (c.y / c.n - node.y) * 0.04 * temp;
35011
+ } else {
35012
+ node.fx += (centerX - node.x) * 0.01 * temp;
35013
+ node.fy += (centerY - node.y) * 0.01 * temp;
35014
+ }
34968
35015
  } else {
34969
- node.fx += (centerX - node.x) * 0.01;
34970
- node.fy += (centerY - node.y) * 0.01;
35016
+ node.fx += (centerX - node.x) * 8e-3 * temp;
35017
+ node.fy += (centerY - node.y) * 8e-3 * temp;
34971
35018
  }
34972
35019
  }
34973
35020
  const damping = 0.9;
@@ -34979,42 +35026,53 @@ var init_GraphCanvas = __esm({
34979
35026
  node.x = Math.max(30, Math.min(w - 30, node.x));
34980
35027
  node.y = Math.max(30, Math.min(h - 30, node.y));
34981
35028
  }
34982
- const LABEL_GAP = 12;
34983
- const LABEL_H = 16;
34984
35029
  const pad = nodeSpacing / 2;
34985
- for (let i = 0; i < nodes.length; i++) {
34986
- for (let j = i + 1; j < nodes.length; j++) {
34987
- const a = nodes[i];
34988
- const b = nodes[j];
34989
- const ar = a.size || 8;
34990
- const br = b.size || 8;
34991
- if (showLabels) {
34992
- if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
34993
- if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
34994
- }
34995
- const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
34996
- const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
34997
- const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
34998
- const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
34999
- const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
35000
- const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
35001
- const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
35002
- if (overlapX > 0 && overlapY > 0) {
35003
- if (overlapX <= overlapY) {
35004
- const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
35005
- a.x = Math.max(30, Math.min(w - 30, a.x - push));
35006
- b.x = Math.max(30, Math.min(w - 30, b.x + push));
35007
- } else {
35008
- const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
35009
- a.y = Math.max(30, Math.min(h - 30, a.y - push));
35010
- b.y = Math.max(30, Math.min(h - 30, b.y + push));
35030
+ const rectsOf = (n) => {
35031
+ const r = n.size || 8;
35032
+ const lw = showLabels ? n.labelW ?? (n.labelW = n.label ? measureLabelWidth(n.label, labelFont) : 0) : 0;
35033
+ return {
35034
+ circle: [n.x - r - pad, n.y - r - pad, n.x + r + pad, n.y + r + pad],
35035
+ label: [n.x - lw / 2 - pad, n.y + r + LABEL_GAP - 8, n.x + lw / 2 + pad, n.y + r + LABEL_GAP + LABEL_H]
35036
+ };
35037
+ };
35038
+ const sep = (r1, r2) => {
35039
+ const ox = Math.min(r1[2], r2[2]) - Math.max(r1[0], r2[0]);
35040
+ const oy = Math.min(r1[3], r2[3]) - Math.max(r1[1], r2[1]);
35041
+ if (ox <= 0 || oy <= 0) return null;
35042
+ return ox <= oy ? { axis: "x", depth: ox, sign: r1[0] + r1[2] < r2[0] + r2[2] ? 1 : -1 } : { axis: "y", depth: oy, sign: r1[1] + r1[3] < r2[1] + r2[3] ? 1 : -1 };
35043
+ };
35044
+ for (let pass = 0; pass < COLLIDE_PASSES; pass++) {
35045
+ for (let i = 0; i < nodes.length; i++) {
35046
+ for (let j = i + 1; j < nodes.length; j++) {
35047
+ const a = nodes[i];
35048
+ const b = nodes[j];
35049
+ const ra = rectsOf(a);
35050
+ const rb = rectsOf(b);
35051
+ let best = null;
35052
+ for (const [r1, r2] of [[ra.circle, rb.circle], [ra.circle, rb.label], [ra.label, rb.circle], [ra.label, rb.label]]) {
35053
+ const s = sep(r1, r2);
35054
+ if (s && (!best || s.depth < best.depth)) best = s;
35055
+ }
35056
+ if (best) {
35057
+ const push = best.depth / 2;
35058
+ if (best.axis === "x") {
35059
+ a.x = Math.max(30, Math.min(w - 30, a.x - push * best.sign));
35060
+ b.x = Math.max(30, Math.min(w - 30, b.x + push * best.sign));
35061
+ a.vx = 0;
35062
+ b.vx = 0;
35063
+ } else {
35064
+ a.y = Math.max(30, Math.min(h - 30, a.y - push * best.sign));
35065
+ b.y = Math.max(30, Math.min(h - 30, b.y + push * best.sign));
35066
+ a.vy = 0;
35067
+ b.vy = 0;
35068
+ }
35011
35069
  }
35012
35070
  }
35013
35071
  }
35014
35072
  }
35015
35073
  };
35016
35074
  if (fullRelayout) {
35017
- for (let i = 0; i < maxIterations; i++) tick();
35075
+ for (let i = 0; i < maxIterations; i++) tick(i);
35018
35076
  laidOutRef.current = true;
35019
35077
  } else {
35020
35078
  const centroids = /* @__PURE__ */ new Map();
@@ -35052,7 +35110,7 @@ var init_GraphCanvas = __esm({
35052
35110
  return () => {
35053
35111
  cancelAnimationFrame(animRef.current);
35054
35112
  };
35055
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35113
+ }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35056
35114
  React81.useEffect(() => {
35057
35115
  const canvas = canvasRef.current;
35058
35116
  if (!canvas) return;