@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.
@@ -36581,11 +36581,11 @@ var init_DocumentViewer = __esm({
36581
36581
  DocumentViewer.displayName = "DocumentViewer";
36582
36582
  }
36583
36583
  });
36584
- function measureLabelWidth(text) {
36584
+ function measureLabelWidth(text, fontFamily = "system-ui") {
36585
36585
  if (typeof document === "undefined") return text.length * 6;
36586
36586
  if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
36587
36587
  if (!labelMeasureCtx) return text.length * 6;
36588
- labelMeasureCtx.font = "12px system-ui";
36588
+ labelMeasureCtx.font = `12px ${fontFamily}`;
36589
36589
  return labelMeasureCtx.measureText(text).width + 4;
36590
36590
  }
36591
36591
  function getGroupColor(group, groups) {
@@ -36593,6 +36593,26 @@ function getGroupColor(group, groups) {
36593
36593
  const idx = groups.indexOf(group);
36594
36594
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
36595
36595
  }
36596
+ function hashSeed(str2) {
36597
+ let h = 1779033703 ^ str2.length;
36598
+ for (let i = 0; i < str2.length; i++) {
36599
+ h = Math.imul(h ^ str2.charCodeAt(i), 3432918353);
36600
+ h = h << 13 | h >>> 19;
36601
+ }
36602
+ return (h ^= h >>> 16) >>> 0;
36603
+ }
36604
+ function mulberry32(a) {
36605
+ return function() {
36606
+ a |= 0;
36607
+ a = a + 1831565813 | 0;
36608
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
36609
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
36610
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
36611
+ };
36612
+ }
36613
+ function edgeKeyOf(a, b) {
36614
+ return a < b ? `${a}\0${b}` : `${b}\0${a}`;
36615
+ }
36596
36616
  function resolveColor3(color, el) {
36597
36617
  const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
36598
36618
  if (!m) return color;
@@ -36624,6 +36644,7 @@ var init_GraphCanvas = __esm({
36624
36644
  title,
36625
36645
  nodes: propNodes = [],
36626
36646
  edges: propEdges = [],
36647
+ similarity: propSimilarity = [],
36627
36648
  height = 400,
36628
36649
  showLabels = true,
36629
36650
  interactive = true,
@@ -36725,6 +36746,7 @@ var init_GraphCanvas = __esm({
36725
36746
  if (!canvas || propNodes.length === 0) return;
36726
36747
  const w = logicalW;
36727
36748
  const h = height;
36749
+ const labelFont = resolveColor3("var(--font-family)", canvas) || "system-ui";
36728
36750
  const prevPos = /* @__PURE__ */ new Map();
36729
36751
  for (const pn of nodesRef.current) if (pn.x != null && pn.y != null) prevPos.set(pn.id, { x: pn.x, y: pn.y });
36730
36752
  const simNodes = propNodes.map((n, idx) => {
@@ -36744,8 +36766,9 @@ var init_GraphCanvas = __esm({
36744
36766
  x = gapX * (idx % cols + 1);
36745
36767
  y = gapY * (Math.floor(idx / cols) + 1);
36746
36768
  } else {
36747
- x = w * 0.2 + Math.random() * w * 0.6;
36748
- y = h * 0.2 + Math.random() * h * 0.6;
36769
+ const rand = mulberry32(hashSeed(n.id));
36770
+ x = w * 0.2 + rand() * w * 0.6;
36771
+ y = h * 0.2 + rand() * h * 0.6;
36749
36772
  }
36750
36773
  }
36751
36774
  return { ...n, x, y, vx: 0, vy: 0, fx: 0, fy: 0 };
@@ -36758,10 +36781,15 @@ var init_GraphCanvas = __esm({
36758
36781
  const fullRelayout = !laidOutRef.current || overlap < 0.5;
36759
36782
  if (layout === "force") {
36760
36783
  const maxIterations = 300;
36761
- const tick = () => {
36784
+ const COOL = 0.12;
36785
+ const COLLIDE_PASSES = 6;
36786
+ const LABEL_GAP = 12;
36787
+ const LABEL_H = 16;
36788
+ const tick = (iter) => {
36762
36789
  const nodes = nodesRef.current;
36763
36790
  const centerX = w / 2;
36764
36791
  const centerY = h / 2;
36792
+ const temp = Math.max(COOL, 1 - iter / maxIterations);
36765
36793
  for (const node of nodes) {
36766
36794
  node.fx = 0;
36767
36795
  node.fy = 0;
@@ -36771,7 +36799,7 @@ var init_GraphCanvas = __esm({
36771
36799
  const dx = nodes[j].x - nodes[i].x;
36772
36800
  const dy = nodes[j].y - nodes[i].y;
36773
36801
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
36774
- const force = repulsion / (dist * dist);
36802
+ const force = repulsion / (dist * dist) * temp;
36775
36803
  const fx = dx / dist * force;
36776
36804
  const fy = dy / dist * force;
36777
36805
  nodes[i].fx -= fx;
@@ -36780,22 +36808,35 @@ var init_GraphCanvas = __esm({
36780
36808
  nodes[j].fy += fy;
36781
36809
  }
36782
36810
  }
36783
- for (const edge of propEdges) {
36784
- const source = nodes.find((n) => n.id === edge.source);
36785
- const target = nodes.find((n) => n.id === edge.target);
36786
- if (!source || !target) continue;
36787
- const dx = target.x - source.x;
36788
- const dy = target.y - source.y;
36811
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
36812
+ const spring = (a, b, rest, k) => {
36813
+ const dx = b.x - a.x;
36814
+ const dy = b.y - a.y;
36789
36815
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
36790
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
36791
- const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
36792
- const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
36816
+ const force = (dist - rest) * k * temp;
36793
36817
  const fx = dx / dist * force;
36794
36818
  const fy = dy / dist * force;
36795
- source.fx += fx;
36796
- source.fy += fy;
36797
- target.fx -= fx;
36798
- target.fy -= fy;
36819
+ a.fx += fx;
36820
+ a.fy += fy;
36821
+ b.fx -= fx;
36822
+ b.fy -= fy;
36823
+ };
36824
+ const drawnPairs = /* @__PURE__ */ new Set();
36825
+ for (const edge of propEdges) {
36826
+ const source = nodeById.get(edge.source);
36827
+ const target = nodeById.get(edge.target);
36828
+ if (!source || !target) continue;
36829
+ drawnPairs.add(edgeKeyOf(edge.source, edge.target));
36830
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
36831
+ spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
36832
+ }
36833
+ for (const pair of propSimilarity) {
36834
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
36835
+ const source = nodeById.get(pair.source);
36836
+ const target = nodeById.get(pair.target);
36837
+ if (!source || !target) continue;
36838
+ const w2 = Math.min(1, Math.max(0, pair.weight));
36839
+ spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
36799
36840
  }
36800
36841
  const centroids = /* @__PURE__ */ new Map();
36801
36842
  for (const node of nodes) {
@@ -36809,14 +36850,20 @@ var init_GraphCanvas = __esm({
36809
36850
  c.y += node.y;
36810
36851
  c.n += 1;
36811
36852
  }
36853
+ const multiCluster = centroids.size > 1;
36812
36854
  for (const node of nodes) {
36813
- const c = centroids.get(node.group ?? "__none__");
36814
- if (c && c.n > 1) {
36815
- node.fx += (c.x / c.n - node.x) * 0.04;
36816
- node.fy += (c.y / c.n - node.y) * 0.04;
36855
+ if (multiCluster) {
36856
+ const c = centroids.get(node.group ?? "__none__");
36857
+ if (c && c.n > 1) {
36858
+ node.fx += (c.x / c.n - node.x) * 0.04 * temp;
36859
+ node.fy += (c.y / c.n - node.y) * 0.04 * temp;
36860
+ } else {
36861
+ node.fx += (centerX - node.x) * 0.01 * temp;
36862
+ node.fy += (centerY - node.y) * 0.01 * temp;
36863
+ }
36817
36864
  } else {
36818
- node.fx += (centerX - node.x) * 0.01;
36819
- node.fy += (centerY - node.y) * 0.01;
36865
+ node.fx += (centerX - node.x) * 8e-3 * temp;
36866
+ node.fy += (centerY - node.y) * 8e-3 * temp;
36820
36867
  }
36821
36868
  }
36822
36869
  const damping = 0.9;
@@ -36828,42 +36875,53 @@ var init_GraphCanvas = __esm({
36828
36875
  node.x = Math.max(30, Math.min(w - 30, node.x));
36829
36876
  node.y = Math.max(30, Math.min(h - 30, node.y));
36830
36877
  }
36831
- const LABEL_GAP = 12;
36832
- const LABEL_H = 16;
36833
36878
  const pad = nodeSpacing / 2;
36834
- for (let i = 0; i < nodes.length; i++) {
36835
- for (let j = i + 1; j < nodes.length; j++) {
36836
- const a = nodes[i];
36837
- const b = nodes[j];
36838
- const ar = a.size || 8;
36839
- const br = b.size || 8;
36840
- if (showLabels) {
36841
- if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
36842
- if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
36843
- }
36844
- const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
36845
- const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
36846
- const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
36847
- const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
36848
- const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
36849
- const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
36850
- const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
36851
- if (overlapX > 0 && overlapY > 0) {
36852
- if (overlapX <= overlapY) {
36853
- const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
36854
- a.x = Math.max(30, Math.min(w - 30, a.x - push));
36855
- b.x = Math.max(30, Math.min(w - 30, b.x + push));
36856
- } else {
36857
- const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
36858
- a.y = Math.max(30, Math.min(h - 30, a.y - push));
36859
- b.y = Math.max(30, Math.min(h - 30, b.y + push));
36879
+ const rectsOf = (n) => {
36880
+ const r = n.size || 8;
36881
+ const lw = showLabels ? n.labelW ?? (n.labelW = n.label ? measureLabelWidth(n.label, labelFont) : 0) : 0;
36882
+ return {
36883
+ circle: [n.x - r - pad, n.y - r - pad, n.x + r + pad, n.y + r + pad],
36884
+ label: [n.x - lw / 2 - pad, n.y + r + LABEL_GAP - 8, n.x + lw / 2 + pad, n.y + r + LABEL_GAP + LABEL_H]
36885
+ };
36886
+ };
36887
+ const sep = (r1, r2) => {
36888
+ const ox = Math.min(r1[2], r2[2]) - Math.max(r1[0], r2[0]);
36889
+ const oy = Math.min(r1[3], r2[3]) - Math.max(r1[1], r2[1]);
36890
+ if (ox <= 0 || oy <= 0) return null;
36891
+ 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 };
36892
+ };
36893
+ for (let pass = 0; pass < COLLIDE_PASSES; pass++) {
36894
+ for (let i = 0; i < nodes.length; i++) {
36895
+ for (let j = i + 1; j < nodes.length; j++) {
36896
+ const a = nodes[i];
36897
+ const b = nodes[j];
36898
+ const ra = rectsOf(a);
36899
+ const rb = rectsOf(b);
36900
+ let best = null;
36901
+ for (const [r1, r2] of [[ra.circle, rb.circle], [ra.circle, rb.label], [ra.label, rb.circle], [ra.label, rb.label]]) {
36902
+ const s = sep(r1, r2);
36903
+ if (s && (!best || s.depth < best.depth)) best = s;
36904
+ }
36905
+ if (best) {
36906
+ const push = best.depth / 2;
36907
+ if (best.axis === "x") {
36908
+ a.x = Math.max(30, Math.min(w - 30, a.x - push * best.sign));
36909
+ b.x = Math.max(30, Math.min(w - 30, b.x + push * best.sign));
36910
+ a.vx = 0;
36911
+ b.vx = 0;
36912
+ } else {
36913
+ a.y = Math.max(30, Math.min(h - 30, a.y - push * best.sign));
36914
+ b.y = Math.max(30, Math.min(h - 30, b.y + push * best.sign));
36915
+ a.vy = 0;
36916
+ b.vy = 0;
36917
+ }
36860
36918
  }
36861
36919
  }
36862
36920
  }
36863
36921
  }
36864
36922
  };
36865
36923
  if (fullRelayout) {
36866
- for (let i = 0; i < maxIterations; i++) tick();
36924
+ for (let i = 0; i < maxIterations; i++) tick(i);
36867
36925
  laidOutRef.current = true;
36868
36926
  } else {
36869
36927
  const centroids = /* @__PURE__ */ new Map();
@@ -36901,7 +36959,7 @@ var init_GraphCanvas = __esm({
36901
36959
  return () => {
36902
36960
  cancelAnimationFrame(animRef.current);
36903
36961
  };
36904
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
36962
+ }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
36905
36963
  useEffect(() => {
36906
36964
  const canvas = canvasRef.current;
36907
36965
  if (!canvas) return;
@@ -35367,11 +35367,11 @@ var init_DocumentViewer = __esm({
35367
35367
  DocumentViewer.displayName = "DocumentViewer";
35368
35368
  }
35369
35369
  });
35370
- function measureLabelWidth(text) {
35370
+ function measureLabelWidth(text, fontFamily = "system-ui") {
35371
35371
  if (typeof document === "undefined") return text.length * 6;
35372
35372
  if (!labelMeasureCtx) labelMeasureCtx = document.createElement("canvas").getContext("2d");
35373
35373
  if (!labelMeasureCtx) return text.length * 6;
35374
- labelMeasureCtx.font = "12px system-ui";
35374
+ labelMeasureCtx.font = `12px ${fontFamily}`;
35375
35375
  return labelMeasureCtx.measureText(text).width + 4;
35376
35376
  }
35377
35377
  function getGroupColor(group, groups) {
@@ -35379,6 +35379,26 @@ function getGroupColor(group, groups) {
35379
35379
  const idx = groups.indexOf(group);
35380
35380
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
35381
35381
  }
35382
+ function hashSeed(str) {
35383
+ let h = 1779033703 ^ str.length;
35384
+ for (let i = 0; i < str.length; i++) {
35385
+ h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
35386
+ h = h << 13 | h >>> 19;
35387
+ }
35388
+ return (h ^= h >>> 16) >>> 0;
35389
+ }
35390
+ function mulberry32(a) {
35391
+ return function() {
35392
+ a |= 0;
35393
+ a = a + 1831565813 | 0;
35394
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
35395
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
35396
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
35397
+ };
35398
+ }
35399
+ function edgeKeyOf(a, b) {
35400
+ return a < b ? `${a}\0${b}` : `${b}\0${a}`;
35401
+ }
35382
35402
  function resolveColor3(color, el) {
35383
35403
  const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
35384
35404
  if (!m) return color;
@@ -35410,6 +35430,7 @@ var init_GraphCanvas = __esm({
35410
35430
  title,
35411
35431
  nodes: propNodes = [],
35412
35432
  edges: propEdges = [],
35433
+ similarity: propSimilarity = [],
35413
35434
  height = 400,
35414
35435
  showLabels = true,
35415
35436
  interactive = true,
@@ -35511,6 +35532,7 @@ var init_GraphCanvas = __esm({
35511
35532
  if (!canvas || propNodes.length === 0) return;
35512
35533
  const w = logicalW;
35513
35534
  const h = height;
35535
+ const labelFont = resolveColor3("var(--font-family)", canvas) || "system-ui";
35514
35536
  const prevPos = /* @__PURE__ */ new Map();
35515
35537
  for (const pn of nodesRef.current) if (pn.x != null && pn.y != null) prevPos.set(pn.id, { x: pn.x, y: pn.y });
35516
35538
  const simNodes = propNodes.map((n, idx) => {
@@ -35530,8 +35552,9 @@ var init_GraphCanvas = __esm({
35530
35552
  x = gapX * (idx % cols + 1);
35531
35553
  y = gapY * (Math.floor(idx / cols) + 1);
35532
35554
  } else {
35533
- x = w * 0.2 + Math.random() * w * 0.6;
35534
- y = h * 0.2 + Math.random() * h * 0.6;
35555
+ const rand = mulberry32(hashSeed(n.id));
35556
+ x = w * 0.2 + rand() * w * 0.6;
35557
+ y = h * 0.2 + rand() * h * 0.6;
35535
35558
  }
35536
35559
  }
35537
35560
  return { ...n, x, y, vx: 0, vy: 0, fx: 0, fy: 0 };
@@ -35544,10 +35567,15 @@ var init_GraphCanvas = __esm({
35544
35567
  const fullRelayout = !laidOutRef.current || overlap < 0.5;
35545
35568
  if (layout === "force") {
35546
35569
  const maxIterations = 300;
35547
- const tick = () => {
35570
+ const COOL = 0.12;
35571
+ const COLLIDE_PASSES = 6;
35572
+ const LABEL_GAP = 12;
35573
+ const LABEL_H = 16;
35574
+ const tick = (iter) => {
35548
35575
  const nodes = nodesRef.current;
35549
35576
  const centerX = w / 2;
35550
35577
  const centerY = h / 2;
35578
+ const temp = Math.max(COOL, 1 - iter / maxIterations);
35551
35579
  for (const node of nodes) {
35552
35580
  node.fx = 0;
35553
35581
  node.fy = 0;
@@ -35557,7 +35585,7 @@ var init_GraphCanvas = __esm({
35557
35585
  const dx = nodes[j].x - nodes[i].x;
35558
35586
  const dy = nodes[j].y - nodes[i].y;
35559
35587
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35560
- const force = repulsion / (dist * dist);
35588
+ const force = repulsion / (dist * dist) * temp;
35561
35589
  const fx = dx / dist * force;
35562
35590
  const fy = dy / dist * force;
35563
35591
  nodes[i].fx -= fx;
@@ -35566,22 +35594,35 @@ var init_GraphCanvas = __esm({
35566
35594
  nodes[j].fy += fy;
35567
35595
  }
35568
35596
  }
35569
- for (const edge of propEdges) {
35570
- const source = nodes.find((n) => n.id === edge.source);
35571
- const target = nodes.find((n) => n.id === edge.target);
35572
- if (!source || !target) continue;
35573
- const dx = target.x - source.x;
35574
- const dy = target.y - source.y;
35597
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
35598
+ const spring = (a, b, rest, k) => {
35599
+ const dx = b.x - a.x;
35600
+ const dy = b.y - a.y;
35575
35601
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
35576
- const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35577
- const linkTarget = linkDistance * (0.5 + (1 - w2) * 1.5);
35578
- const force = (dist - linkTarget) * (0.04 + 0.1 * w2);
35602
+ const force = (dist - rest) * k * temp;
35579
35603
  const fx = dx / dist * force;
35580
35604
  const fy = dy / dist * force;
35581
- source.fx += fx;
35582
- source.fy += fy;
35583
- target.fx -= fx;
35584
- target.fy -= fy;
35605
+ a.fx += fx;
35606
+ a.fy += fy;
35607
+ b.fx -= fx;
35608
+ b.fy -= fy;
35609
+ };
35610
+ const drawnPairs = /* @__PURE__ */ new Set();
35611
+ for (const edge of propEdges) {
35612
+ const source = nodeById.get(edge.source);
35613
+ const target = nodeById.get(edge.target);
35614
+ if (!source || !target) continue;
35615
+ drawnPairs.add(edgeKeyOf(edge.source, edge.target));
35616
+ const w2 = Math.min(1, Math.max(0, edge.weight ?? 1));
35617
+ spring(source, target, linkDistance * (0.35 + (1 - w2) * 0.3), 0.14);
35618
+ }
35619
+ for (const pair of propSimilarity) {
35620
+ if (drawnPairs.has(edgeKeyOf(pair.source, pair.target))) continue;
35621
+ const source = nodeById.get(pair.source);
35622
+ const target = nodeById.get(pair.target);
35623
+ if (!source || !target) continue;
35624
+ const w2 = Math.min(1, Math.max(0, pair.weight));
35625
+ spring(source, target, linkDistance * (1.35 - 0.55 * w2), 0.015);
35585
35626
  }
35586
35627
  const centroids = /* @__PURE__ */ new Map();
35587
35628
  for (const node of nodes) {
@@ -35595,14 +35636,20 @@ var init_GraphCanvas = __esm({
35595
35636
  c.y += node.y;
35596
35637
  c.n += 1;
35597
35638
  }
35639
+ const multiCluster = centroids.size > 1;
35598
35640
  for (const node of nodes) {
35599
- const c = centroids.get(node.group ?? "__none__");
35600
- if (c && c.n > 1) {
35601
- node.fx += (c.x / c.n - node.x) * 0.04;
35602
- node.fy += (c.y / c.n - node.y) * 0.04;
35641
+ if (multiCluster) {
35642
+ const c = centroids.get(node.group ?? "__none__");
35643
+ if (c && c.n > 1) {
35644
+ node.fx += (c.x / c.n - node.x) * 0.04 * temp;
35645
+ node.fy += (c.y / c.n - node.y) * 0.04 * temp;
35646
+ } else {
35647
+ node.fx += (centerX - node.x) * 0.01 * temp;
35648
+ node.fy += (centerY - node.y) * 0.01 * temp;
35649
+ }
35603
35650
  } else {
35604
- node.fx += (centerX - node.x) * 0.01;
35605
- node.fy += (centerY - node.y) * 0.01;
35651
+ node.fx += (centerX - node.x) * 8e-3 * temp;
35652
+ node.fy += (centerY - node.y) * 8e-3 * temp;
35606
35653
  }
35607
35654
  }
35608
35655
  const damping = 0.9;
@@ -35614,42 +35661,53 @@ var init_GraphCanvas = __esm({
35614
35661
  node.x = Math.max(30, Math.min(w - 30, node.x));
35615
35662
  node.y = Math.max(30, Math.min(h - 30, node.y));
35616
35663
  }
35617
- const LABEL_GAP = 12;
35618
- const LABEL_H = 16;
35619
35664
  const pad = nodeSpacing / 2;
35620
- for (let i = 0; i < nodes.length; i++) {
35621
- for (let j = i + 1; j < nodes.length; j++) {
35622
- const a = nodes[i];
35623
- const b = nodes[j];
35624
- const ar = a.size || 8;
35625
- const br = b.size || 8;
35626
- if (showLabels) {
35627
- if (a.labelW === void 0) a.labelW = a.label ? measureLabelWidth(a.label) : 0;
35628
- if (b.labelW === void 0) b.labelW = b.label ? measureLabelWidth(b.label) : 0;
35629
- }
35630
- const labelBelow = showLabels ? LABEL_GAP + LABEL_H : 0;
35631
- const aHalfW = Math.max(ar, (a.labelW ?? 0) / 2) + pad;
35632
- const bHalfW = Math.max(br, (b.labelW ?? 0) / 2) + pad;
35633
- const aTop = a.y - ar - pad, aBot = a.y + ar + labelBelow + pad;
35634
- const bTop = b.y - br - pad, bBot = b.y + br + labelBelow + pad;
35635
- const overlapX = Math.min(a.x + aHalfW, b.x + bHalfW) - Math.max(a.x - aHalfW, b.x - bHalfW);
35636
- const overlapY = Math.min(aBot, bBot) - Math.max(aTop, bTop);
35637
- if (overlapX > 0 && overlapY > 0) {
35638
- if (overlapX <= overlapY) {
35639
- const push = overlapX / 2 * (b.x >= a.x ? 1 : -1);
35640
- a.x = Math.max(30, Math.min(w - 30, a.x - push));
35641
- b.x = Math.max(30, Math.min(w - 30, b.x + push));
35642
- } else {
35643
- const push = overlapY / 2 * (bTop + bBot >= aTop + aBot ? 1 : -1);
35644
- a.y = Math.max(30, Math.min(h - 30, a.y - push));
35645
- b.y = Math.max(30, Math.min(h - 30, b.y + push));
35665
+ const rectsOf = (n) => {
35666
+ const r = n.size || 8;
35667
+ const lw = showLabels ? n.labelW ?? (n.labelW = n.label ? measureLabelWidth(n.label, labelFont) : 0) : 0;
35668
+ return {
35669
+ circle: [n.x - r - pad, n.y - r - pad, n.x + r + pad, n.y + r + pad],
35670
+ label: [n.x - lw / 2 - pad, n.y + r + LABEL_GAP - 8, n.x + lw / 2 + pad, n.y + r + LABEL_GAP + LABEL_H]
35671
+ };
35672
+ };
35673
+ const sep = (r1, r2) => {
35674
+ const ox = Math.min(r1[2], r2[2]) - Math.max(r1[0], r2[0]);
35675
+ const oy = Math.min(r1[3], r2[3]) - Math.max(r1[1], r2[1]);
35676
+ if (ox <= 0 || oy <= 0) return null;
35677
+ 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 };
35678
+ };
35679
+ for (let pass = 0; pass < COLLIDE_PASSES; pass++) {
35680
+ for (let i = 0; i < nodes.length; i++) {
35681
+ for (let j = i + 1; j < nodes.length; j++) {
35682
+ const a = nodes[i];
35683
+ const b = nodes[j];
35684
+ const ra = rectsOf(a);
35685
+ const rb = rectsOf(b);
35686
+ let best = null;
35687
+ for (const [r1, r2] of [[ra.circle, rb.circle], [ra.circle, rb.label], [ra.label, rb.circle], [ra.label, rb.label]]) {
35688
+ const s = sep(r1, r2);
35689
+ if (s && (!best || s.depth < best.depth)) best = s;
35690
+ }
35691
+ if (best) {
35692
+ const push = best.depth / 2;
35693
+ if (best.axis === "x") {
35694
+ a.x = Math.max(30, Math.min(w - 30, a.x - push * best.sign));
35695
+ b.x = Math.max(30, Math.min(w - 30, b.x + push * best.sign));
35696
+ a.vx = 0;
35697
+ b.vx = 0;
35698
+ } else {
35699
+ a.y = Math.max(30, Math.min(h - 30, a.y - push * best.sign));
35700
+ b.y = Math.max(30, Math.min(h - 30, b.y + push * best.sign));
35701
+ a.vy = 0;
35702
+ b.vy = 0;
35703
+ }
35646
35704
  }
35647
35705
  }
35648
35706
  }
35649
35707
  }
35650
35708
  };
35651
35709
  if (fullRelayout) {
35652
- for (let i = 0; i < maxIterations; i++) tick();
35710
+ for (let i = 0; i < maxIterations; i++) tick(i);
35653
35711
  laidOutRef.current = true;
35654
35712
  } else {
35655
35713
  const centroids = /* @__PURE__ */ new Map();
@@ -35687,7 +35745,7 @@ var init_GraphCanvas = __esm({
35687
35745
  return () => {
35688
35746
  cancelAnimationFrame(animRef.current);
35689
35747
  };
35690
- }, [propNodes, propEdges, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35748
+ }, [propNodes, propEdges, propSimilarity, layout, repulsion, linkDistance, nodeSpacing, showLabels]);
35691
35749
  React83.useEffect(() => {
35692
35750
  const canvas = canvasRef.current;
35693
35751
  if (!canvas) return;