microboard-temp 0.13.14 → 0.13.16

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.
@@ -4675,6 +4675,11 @@ var conf = {
4675
4675
  DEFAULT_GAME_ITEM_DIMENSIONS: { width: 200, height: 200 },
4676
4676
  MAX_CARD_SIZE: 500,
4677
4677
  CONNECTOR_ITEM_OFFSET: 20,
4678
+ FG_SPRING_K: 3,
4679
+ FG_TARGET_GAP: 60,
4680
+ FG_REPULSION: 400000,
4681
+ FG_DAMPING: 0.8,
4682
+ FG_SLEEP_THRESHOLD: 0.5,
4678
4683
  GRAVITY_G: 80,
4679
4684
  GRAVITY_G_CENTER: 120,
4680
4685
  GRAVITY_DAMPING: 0.96,
@@ -54428,6 +54433,8 @@ class GravityEngine {
54428
54433
  if (this.tickTimer !== null)
54429
54434
  return;
54430
54435
  for (const item of this.board.items.listAll()) {
54436
+ const pos = item.transformation.getTranslation();
54437
+ this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
54431
54438
  this.velocities.set(item.getId(), { vx: 0, vy: 0 });
54432
54439
  }
54433
54440
  this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
@@ -54442,6 +54449,7 @@ class GravityEngine {
54442
54449
  clearInterval(this.syncTimer);
54443
54450
  this.syncTimer = null;
54444
54451
  }
54452
+ this.syncPositions();
54445
54453
  this.velocities.clear();
54446
54454
  this.lastSyncedPositions.clear();
54447
54455
  }
@@ -54566,6 +54574,176 @@ class GravityEngine {
54566
54574
  }
54567
54575
  }
54568
54576
 
54577
+ // src/ForceGraph/ForceGraphEngine.ts
54578
+ var EXCLUDED_TYPES = new Set(["Connector", "Comment"]);
54579
+
54580
+ class ForceGraphEngine {
54581
+ board;
54582
+ velocities = new Map;
54583
+ tickTimer = null;
54584
+ syncTimer = null;
54585
+ lastSyncedPositions = new Map;
54586
+ TICK_MS = 33;
54587
+ SYNC_MS = 300;
54588
+ SOFTENING_SQ = 100 * 100;
54589
+ MIN_MOVE_PX = 0.05;
54590
+ constructor(board) {
54591
+ this.board = board;
54592
+ }
54593
+ start() {
54594
+ if (this.tickTimer !== null)
54595
+ return;
54596
+ for (const item of this.getNodes()) {
54597
+ const pos = item.transformation.getTranslation();
54598
+ this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
54599
+ this.velocities.set(item.getId(), { vx: 0, vy: 0 });
54600
+ }
54601
+ this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
54602
+ this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
54603
+ }
54604
+ stop() {
54605
+ if (this.tickTimer !== null) {
54606
+ clearInterval(this.tickTimer);
54607
+ this.tickTimer = null;
54608
+ }
54609
+ if (this.syncTimer !== null) {
54610
+ clearInterval(this.syncTimer);
54611
+ this.syncTimer = null;
54612
+ }
54613
+ this.syncPositions();
54614
+ this.velocities.clear();
54615
+ this.lastSyncedPositions.clear();
54616
+ }
54617
+ getNodes() {
54618
+ return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
54619
+ }
54620
+ getConnectors() {
54621
+ return this.board.items.listAll().filter((item) => item.itemType === "Connector");
54622
+ }
54623
+ tick() {
54624
+ const dt = this.TICK_MS / 1000;
54625
+ const nodes = this.getNodes();
54626
+ if (nodes.length < 2)
54627
+ return;
54628
+ const snapMap = new Map;
54629
+ for (const item of nodes) {
54630
+ const pos = item.transformation.getTranslation();
54631
+ const mbr = item.getMbr();
54632
+ const w = Math.max(mbr.getWidth(), 1);
54633
+ const h2 = Math.max(mbr.getHeight(), 1);
54634
+ snapMap.set(item.getId(), {
54635
+ id: item.getId(),
54636
+ cx: pos.x + w * 0.5,
54637
+ cy: pos.y + h2 * 0.5,
54638
+ w,
54639
+ h: h2
54640
+ });
54641
+ }
54642
+ const snap = Array.from(snapMap.values());
54643
+ const ax = new Map;
54644
+ const ay = new Map;
54645
+ for (const s2 of snap) {
54646
+ ax.set(s2.id, 0);
54647
+ ay.set(s2.id, 0);
54648
+ }
54649
+ for (const connector of this.getConnectors()) {
54650
+ const { startItem, endItem } = connector.getConnectedItems();
54651
+ if (!startItem || !endItem)
54652
+ continue;
54653
+ const s1 = snapMap.get(startItem.getId());
54654
+ const s2 = snapMap.get(endItem.getId());
54655
+ if (!s1 || !s2)
54656
+ continue;
54657
+ const dx = s2.cx - s1.cx;
54658
+ const dy = s2.cy - s1.cy;
54659
+ const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
54660
+ const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + conf.FG_TARGET_GAP;
54661
+ const stretch = dist - targetDist;
54662
+ const forceMag = stretch * conf.FG_SPRING_K;
54663
+ const fx = dx / dist * forceMag;
54664
+ const fy = dy / dist * forceMag;
54665
+ ax.set(s1.id, (ax.get(s1.id) ?? 0) + fx);
54666
+ ay.set(s1.id, (ay.get(s1.id) ?? 0) + fy);
54667
+ ax.set(s2.id, (ax.get(s2.id) ?? 0) - fx);
54668
+ ay.set(s2.id, (ay.get(s2.id) ?? 0) - fy);
54669
+ }
54670
+ for (let i = 0;i < snap.length; i++) {
54671
+ for (let j = i + 1;j < snap.length; j++) {
54672
+ const s1 = snap[i];
54673
+ const s2 = snap[j];
54674
+ const dx = s2.cx - s1.cx;
54675
+ const dy = s2.cy - s1.cy;
54676
+ const distSq = dx * dx + dy * dy + this.SOFTENING_SQ;
54677
+ const repMag = conf.FG_REPULSION / distSq;
54678
+ const fx = dx * repMag;
54679
+ const fy = dy * repMag;
54680
+ ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
54681
+ ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
54682
+ ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
54683
+ ay.set(s2.id, (ay.get(s2.id) ?? 0) + fy);
54684
+ }
54685
+ }
54686
+ let totalEnergy = 0;
54687
+ for (const item of nodes) {
54688
+ const id = item.getId();
54689
+ if (!this.velocities.has(id)) {
54690
+ this.velocities.set(id, { vx: 0, vy: 0 });
54691
+ }
54692
+ const vel = this.velocities.get(id);
54693
+ vel.vx = (vel.vx + (ax.get(id) ?? 0) * dt) * conf.FG_DAMPING;
54694
+ vel.vy = (vel.vy + (ay.get(id) ?? 0) * dt) * conf.FG_DAMPING;
54695
+ totalEnergy += vel.vx * vel.vx + vel.vy * vel.vy;
54696
+ const moveX = vel.vx * dt;
54697
+ const moveY = vel.vy * dt;
54698
+ if (Math.abs(moveX) >= this.MIN_MOVE_PX || Math.abs(moveY) >= this.MIN_MOVE_PX) {
54699
+ item.transformation.applyMatrixSilent({
54700
+ translateX: moveX,
54701
+ translateY: moveY,
54702
+ scaleX: 1,
54703
+ scaleY: 1,
54704
+ shearX: 0,
54705
+ shearY: 0
54706
+ });
54707
+ }
54708
+ }
54709
+ if (totalEnergy < conf.FG_SLEEP_THRESHOLD && this.tickTimer !== null) {
54710
+ clearInterval(this.tickTimer);
54711
+ this.tickTimer = null;
54712
+ this.syncPositions();
54713
+ }
54714
+ }
54715
+ syncPositions() {
54716
+ const nodes = this.getNodes();
54717
+ if (nodes.length === 0)
54718
+ return;
54719
+ const movedItems = nodes.map((item) => {
54720
+ const id = item.getId();
54721
+ const pos = item.transformation.getTranslation();
54722
+ const last = this.lastSyncedPositions.get(id);
54723
+ const dx = last ? pos.x - last.x : 0;
54724
+ const dy = last ? pos.y - last.y : 0;
54725
+ this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
54726
+ return { id, dx, dy };
54727
+ }).filter(({ dx, dy }) => Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5);
54728
+ if (movedItems.length === 0)
54729
+ return;
54730
+ const operation = {
54731
+ class: "Transformation",
54732
+ method: "applyMatrix",
54733
+ items: movedItems.map(({ id, dx, dy }) => ({
54734
+ id,
54735
+ matrix: { translateX: dx, translateY: dy, scaleX: 1, scaleY: 1, shearX: 0, shearY: 0 }
54736
+ }))
54737
+ };
54738
+ this.board.events.emit(operation);
54739
+ }
54740
+ wake() {
54741
+ if (this.tickTimer === null && this.syncTimer !== null) {
54742
+ this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
54743
+ }
54744
+ }
54745
+ }
54746
+
54569
54747
  // src/Board.ts
54570
54748
  class Board {
54571
54749
  boardId;
@@ -55638,6 +55816,25 @@ class Board {
55638
55816
  isGravityEnabled() {
55639
55817
  return this.gravity !== null;
55640
55818
  }
55819
+ forceGraph = null;
55820
+ enableForceGraph() {
55821
+ if (this.forceGraph)
55822
+ return;
55823
+ this.forceGraph = new ForceGraphEngine(this);
55824
+ this.forceGraph.start();
55825
+ }
55826
+ disableForceGraph() {
55827
+ if (!this.forceGraph)
55828
+ return;
55829
+ this.forceGraph.stop();
55830
+ this.forceGraph = null;
55831
+ }
55832
+ isForceGraphEnabled() {
55833
+ return this.forceGraph !== null;
55834
+ }
55835
+ wakeForceGraph() {
55836
+ this.forceGraph?.wake();
55837
+ }
55641
55838
  }
55642
55839
  // src/Events/Merge.ts
55643
55840
  var import_slate36 = require("slate");
package/dist/cjs/index.js CHANGED
@@ -4675,6 +4675,11 @@ var conf = {
4675
4675
  DEFAULT_GAME_ITEM_DIMENSIONS: { width: 200, height: 200 },
4676
4676
  MAX_CARD_SIZE: 500,
4677
4677
  CONNECTOR_ITEM_OFFSET: 20,
4678
+ FG_SPRING_K: 3,
4679
+ FG_TARGET_GAP: 60,
4680
+ FG_REPULSION: 400000,
4681
+ FG_DAMPING: 0.8,
4682
+ FG_SLEEP_THRESHOLD: 0.5,
4678
4683
  GRAVITY_G: 80,
4679
4684
  GRAVITY_G_CENTER: 120,
4680
4685
  GRAVITY_DAMPING: 0.96,
@@ -54428,6 +54433,8 @@ class GravityEngine {
54428
54433
  if (this.tickTimer !== null)
54429
54434
  return;
54430
54435
  for (const item of this.board.items.listAll()) {
54436
+ const pos = item.transformation.getTranslation();
54437
+ this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
54431
54438
  this.velocities.set(item.getId(), { vx: 0, vy: 0 });
54432
54439
  }
54433
54440
  this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
@@ -54442,6 +54449,7 @@ class GravityEngine {
54442
54449
  clearInterval(this.syncTimer);
54443
54450
  this.syncTimer = null;
54444
54451
  }
54452
+ this.syncPositions();
54445
54453
  this.velocities.clear();
54446
54454
  this.lastSyncedPositions.clear();
54447
54455
  }
@@ -54566,6 +54574,176 @@ class GravityEngine {
54566
54574
  }
54567
54575
  }
54568
54576
 
54577
+ // src/ForceGraph/ForceGraphEngine.ts
54578
+ var EXCLUDED_TYPES = new Set(["Connector", "Comment"]);
54579
+
54580
+ class ForceGraphEngine {
54581
+ board;
54582
+ velocities = new Map;
54583
+ tickTimer = null;
54584
+ syncTimer = null;
54585
+ lastSyncedPositions = new Map;
54586
+ TICK_MS = 33;
54587
+ SYNC_MS = 300;
54588
+ SOFTENING_SQ = 100 * 100;
54589
+ MIN_MOVE_PX = 0.05;
54590
+ constructor(board) {
54591
+ this.board = board;
54592
+ }
54593
+ start() {
54594
+ if (this.tickTimer !== null)
54595
+ return;
54596
+ for (const item of this.getNodes()) {
54597
+ const pos = item.transformation.getTranslation();
54598
+ this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
54599
+ this.velocities.set(item.getId(), { vx: 0, vy: 0 });
54600
+ }
54601
+ this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
54602
+ this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
54603
+ }
54604
+ stop() {
54605
+ if (this.tickTimer !== null) {
54606
+ clearInterval(this.tickTimer);
54607
+ this.tickTimer = null;
54608
+ }
54609
+ if (this.syncTimer !== null) {
54610
+ clearInterval(this.syncTimer);
54611
+ this.syncTimer = null;
54612
+ }
54613
+ this.syncPositions();
54614
+ this.velocities.clear();
54615
+ this.lastSyncedPositions.clear();
54616
+ }
54617
+ getNodes() {
54618
+ return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
54619
+ }
54620
+ getConnectors() {
54621
+ return this.board.items.listAll().filter((item) => item.itemType === "Connector");
54622
+ }
54623
+ tick() {
54624
+ const dt = this.TICK_MS / 1000;
54625
+ const nodes = this.getNodes();
54626
+ if (nodes.length < 2)
54627
+ return;
54628
+ const snapMap = new Map;
54629
+ for (const item of nodes) {
54630
+ const pos = item.transformation.getTranslation();
54631
+ const mbr = item.getMbr();
54632
+ const w = Math.max(mbr.getWidth(), 1);
54633
+ const h2 = Math.max(mbr.getHeight(), 1);
54634
+ snapMap.set(item.getId(), {
54635
+ id: item.getId(),
54636
+ cx: pos.x + w * 0.5,
54637
+ cy: pos.y + h2 * 0.5,
54638
+ w,
54639
+ h: h2
54640
+ });
54641
+ }
54642
+ const snap = Array.from(snapMap.values());
54643
+ const ax = new Map;
54644
+ const ay = new Map;
54645
+ for (const s2 of snap) {
54646
+ ax.set(s2.id, 0);
54647
+ ay.set(s2.id, 0);
54648
+ }
54649
+ for (const connector of this.getConnectors()) {
54650
+ const { startItem, endItem } = connector.getConnectedItems();
54651
+ if (!startItem || !endItem)
54652
+ continue;
54653
+ const s1 = snapMap.get(startItem.getId());
54654
+ const s2 = snapMap.get(endItem.getId());
54655
+ if (!s1 || !s2)
54656
+ continue;
54657
+ const dx = s2.cx - s1.cx;
54658
+ const dy = s2.cy - s1.cy;
54659
+ const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
54660
+ const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + conf.FG_TARGET_GAP;
54661
+ const stretch = dist - targetDist;
54662
+ const forceMag = stretch * conf.FG_SPRING_K;
54663
+ const fx = dx / dist * forceMag;
54664
+ const fy = dy / dist * forceMag;
54665
+ ax.set(s1.id, (ax.get(s1.id) ?? 0) + fx);
54666
+ ay.set(s1.id, (ay.get(s1.id) ?? 0) + fy);
54667
+ ax.set(s2.id, (ax.get(s2.id) ?? 0) - fx);
54668
+ ay.set(s2.id, (ay.get(s2.id) ?? 0) - fy);
54669
+ }
54670
+ for (let i = 0;i < snap.length; i++) {
54671
+ for (let j = i + 1;j < snap.length; j++) {
54672
+ const s1 = snap[i];
54673
+ const s2 = snap[j];
54674
+ const dx = s2.cx - s1.cx;
54675
+ const dy = s2.cy - s1.cy;
54676
+ const distSq = dx * dx + dy * dy + this.SOFTENING_SQ;
54677
+ const repMag = conf.FG_REPULSION / distSq;
54678
+ const fx = dx * repMag;
54679
+ const fy = dy * repMag;
54680
+ ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
54681
+ ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
54682
+ ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
54683
+ ay.set(s2.id, (ay.get(s2.id) ?? 0) + fy);
54684
+ }
54685
+ }
54686
+ let totalEnergy = 0;
54687
+ for (const item of nodes) {
54688
+ const id = item.getId();
54689
+ if (!this.velocities.has(id)) {
54690
+ this.velocities.set(id, { vx: 0, vy: 0 });
54691
+ }
54692
+ const vel = this.velocities.get(id);
54693
+ vel.vx = (vel.vx + (ax.get(id) ?? 0) * dt) * conf.FG_DAMPING;
54694
+ vel.vy = (vel.vy + (ay.get(id) ?? 0) * dt) * conf.FG_DAMPING;
54695
+ totalEnergy += vel.vx * vel.vx + vel.vy * vel.vy;
54696
+ const moveX = vel.vx * dt;
54697
+ const moveY = vel.vy * dt;
54698
+ if (Math.abs(moveX) >= this.MIN_MOVE_PX || Math.abs(moveY) >= this.MIN_MOVE_PX) {
54699
+ item.transformation.applyMatrixSilent({
54700
+ translateX: moveX,
54701
+ translateY: moveY,
54702
+ scaleX: 1,
54703
+ scaleY: 1,
54704
+ shearX: 0,
54705
+ shearY: 0
54706
+ });
54707
+ }
54708
+ }
54709
+ if (totalEnergy < conf.FG_SLEEP_THRESHOLD && this.tickTimer !== null) {
54710
+ clearInterval(this.tickTimer);
54711
+ this.tickTimer = null;
54712
+ this.syncPositions();
54713
+ }
54714
+ }
54715
+ syncPositions() {
54716
+ const nodes = this.getNodes();
54717
+ if (nodes.length === 0)
54718
+ return;
54719
+ const movedItems = nodes.map((item) => {
54720
+ const id = item.getId();
54721
+ const pos = item.transformation.getTranslation();
54722
+ const last = this.lastSyncedPositions.get(id);
54723
+ const dx = last ? pos.x - last.x : 0;
54724
+ const dy = last ? pos.y - last.y : 0;
54725
+ this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
54726
+ return { id, dx, dy };
54727
+ }).filter(({ dx, dy }) => Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5);
54728
+ if (movedItems.length === 0)
54729
+ return;
54730
+ const operation = {
54731
+ class: "Transformation",
54732
+ method: "applyMatrix",
54733
+ items: movedItems.map(({ id, dx, dy }) => ({
54734
+ id,
54735
+ matrix: { translateX: dx, translateY: dy, scaleX: 1, scaleY: 1, shearX: 0, shearY: 0 }
54736
+ }))
54737
+ };
54738
+ this.board.events.emit(operation);
54739
+ }
54740
+ wake() {
54741
+ if (this.tickTimer === null && this.syncTimer !== null) {
54742
+ this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
54743
+ }
54744
+ }
54745
+ }
54746
+
54569
54747
  // src/Board.ts
54570
54748
  class Board {
54571
54749
  boardId;
@@ -55638,6 +55816,25 @@ class Board {
55638
55816
  isGravityEnabled() {
55639
55817
  return this.gravity !== null;
55640
55818
  }
55819
+ forceGraph = null;
55820
+ enableForceGraph() {
55821
+ if (this.forceGraph)
55822
+ return;
55823
+ this.forceGraph = new ForceGraphEngine(this);
55824
+ this.forceGraph.start();
55825
+ }
55826
+ disableForceGraph() {
55827
+ if (!this.forceGraph)
55828
+ return;
55829
+ this.forceGraph.stop();
55830
+ this.forceGraph = null;
55831
+ }
55832
+ isForceGraphEnabled() {
55833
+ return this.forceGraph !== null;
55834
+ }
55835
+ wakeForceGraph() {
55836
+ this.forceGraph?.wake();
55837
+ }
55641
55838
  }
55642
55839
  // src/Events/Merge.ts
55643
55840
  var import_slate36 = require("slate");
package/dist/cjs/node.js CHANGED
@@ -5712,6 +5712,11 @@ var conf = {
5712
5712
  DEFAULT_GAME_ITEM_DIMENSIONS: { width: 200, height: 200 },
5713
5713
  MAX_CARD_SIZE: 500,
5714
5714
  CONNECTOR_ITEM_OFFSET: 20,
5715
+ FG_SPRING_K: 3,
5716
+ FG_TARGET_GAP: 60,
5717
+ FG_REPULSION: 400000,
5718
+ FG_DAMPING: 0.8,
5719
+ FG_SLEEP_THRESHOLD: 0.5,
5715
5720
  GRAVITY_G: 80,
5716
5721
  GRAVITY_G_CENTER: 120,
5717
5722
  GRAVITY_DAMPING: 0.96,
@@ -56901,6 +56906,8 @@ class GravityEngine {
56901
56906
  if (this.tickTimer !== null)
56902
56907
  return;
56903
56908
  for (const item of this.board.items.listAll()) {
56909
+ const pos = item.transformation.getTranslation();
56910
+ this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
56904
56911
  this.velocities.set(item.getId(), { vx: 0, vy: 0 });
56905
56912
  }
56906
56913
  this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
@@ -56915,6 +56922,7 @@ class GravityEngine {
56915
56922
  clearInterval(this.syncTimer);
56916
56923
  this.syncTimer = null;
56917
56924
  }
56925
+ this.syncPositions();
56918
56926
  this.velocities.clear();
56919
56927
  this.lastSyncedPositions.clear();
56920
56928
  }
@@ -57039,6 +57047,176 @@ class GravityEngine {
57039
57047
  }
57040
57048
  }
57041
57049
 
57050
+ // src/ForceGraph/ForceGraphEngine.ts
57051
+ var EXCLUDED_TYPES = new Set(["Connector", "Comment"]);
57052
+
57053
+ class ForceGraphEngine {
57054
+ board;
57055
+ velocities = new Map;
57056
+ tickTimer = null;
57057
+ syncTimer = null;
57058
+ lastSyncedPositions = new Map;
57059
+ TICK_MS = 33;
57060
+ SYNC_MS = 300;
57061
+ SOFTENING_SQ = 100 * 100;
57062
+ MIN_MOVE_PX = 0.05;
57063
+ constructor(board) {
57064
+ this.board = board;
57065
+ }
57066
+ start() {
57067
+ if (this.tickTimer !== null)
57068
+ return;
57069
+ for (const item of this.getNodes()) {
57070
+ const pos = item.transformation.getTranslation();
57071
+ this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
57072
+ this.velocities.set(item.getId(), { vx: 0, vy: 0 });
57073
+ }
57074
+ this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
57075
+ this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
57076
+ }
57077
+ stop() {
57078
+ if (this.tickTimer !== null) {
57079
+ clearInterval(this.tickTimer);
57080
+ this.tickTimer = null;
57081
+ }
57082
+ if (this.syncTimer !== null) {
57083
+ clearInterval(this.syncTimer);
57084
+ this.syncTimer = null;
57085
+ }
57086
+ this.syncPositions();
57087
+ this.velocities.clear();
57088
+ this.lastSyncedPositions.clear();
57089
+ }
57090
+ getNodes() {
57091
+ return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
57092
+ }
57093
+ getConnectors() {
57094
+ return this.board.items.listAll().filter((item) => item.itemType === "Connector");
57095
+ }
57096
+ tick() {
57097
+ const dt = this.TICK_MS / 1000;
57098
+ const nodes = this.getNodes();
57099
+ if (nodes.length < 2)
57100
+ return;
57101
+ const snapMap = new Map;
57102
+ for (const item of nodes) {
57103
+ const pos = item.transformation.getTranslation();
57104
+ const mbr = item.getMbr();
57105
+ const w = Math.max(mbr.getWidth(), 1);
57106
+ const h2 = Math.max(mbr.getHeight(), 1);
57107
+ snapMap.set(item.getId(), {
57108
+ id: item.getId(),
57109
+ cx: pos.x + w * 0.5,
57110
+ cy: pos.y + h2 * 0.5,
57111
+ w,
57112
+ h: h2
57113
+ });
57114
+ }
57115
+ const snap = Array.from(snapMap.values());
57116
+ const ax = new Map;
57117
+ const ay = new Map;
57118
+ for (const s2 of snap) {
57119
+ ax.set(s2.id, 0);
57120
+ ay.set(s2.id, 0);
57121
+ }
57122
+ for (const connector of this.getConnectors()) {
57123
+ const { startItem, endItem } = connector.getConnectedItems();
57124
+ if (!startItem || !endItem)
57125
+ continue;
57126
+ const s1 = snapMap.get(startItem.getId());
57127
+ const s2 = snapMap.get(endItem.getId());
57128
+ if (!s1 || !s2)
57129
+ continue;
57130
+ const dx = s2.cx - s1.cx;
57131
+ const dy = s2.cy - s1.cy;
57132
+ const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
57133
+ const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + conf.FG_TARGET_GAP;
57134
+ const stretch = dist - targetDist;
57135
+ const forceMag = stretch * conf.FG_SPRING_K;
57136
+ const fx = dx / dist * forceMag;
57137
+ const fy = dy / dist * forceMag;
57138
+ ax.set(s1.id, (ax.get(s1.id) ?? 0) + fx);
57139
+ ay.set(s1.id, (ay.get(s1.id) ?? 0) + fy);
57140
+ ax.set(s2.id, (ax.get(s2.id) ?? 0) - fx);
57141
+ ay.set(s2.id, (ay.get(s2.id) ?? 0) - fy);
57142
+ }
57143
+ for (let i = 0;i < snap.length; i++) {
57144
+ for (let j = i + 1;j < snap.length; j++) {
57145
+ const s1 = snap[i];
57146
+ const s2 = snap[j];
57147
+ const dx = s2.cx - s1.cx;
57148
+ const dy = s2.cy - s1.cy;
57149
+ const distSq = dx * dx + dy * dy + this.SOFTENING_SQ;
57150
+ const repMag = conf.FG_REPULSION / distSq;
57151
+ const fx = dx * repMag;
57152
+ const fy = dy * repMag;
57153
+ ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
57154
+ ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
57155
+ ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
57156
+ ay.set(s2.id, (ay.get(s2.id) ?? 0) + fy);
57157
+ }
57158
+ }
57159
+ let totalEnergy = 0;
57160
+ for (const item of nodes) {
57161
+ const id = item.getId();
57162
+ if (!this.velocities.has(id)) {
57163
+ this.velocities.set(id, { vx: 0, vy: 0 });
57164
+ }
57165
+ const vel = this.velocities.get(id);
57166
+ vel.vx = (vel.vx + (ax.get(id) ?? 0) * dt) * conf.FG_DAMPING;
57167
+ vel.vy = (vel.vy + (ay.get(id) ?? 0) * dt) * conf.FG_DAMPING;
57168
+ totalEnergy += vel.vx * vel.vx + vel.vy * vel.vy;
57169
+ const moveX = vel.vx * dt;
57170
+ const moveY = vel.vy * dt;
57171
+ if (Math.abs(moveX) >= this.MIN_MOVE_PX || Math.abs(moveY) >= this.MIN_MOVE_PX) {
57172
+ item.transformation.applyMatrixSilent({
57173
+ translateX: moveX,
57174
+ translateY: moveY,
57175
+ scaleX: 1,
57176
+ scaleY: 1,
57177
+ shearX: 0,
57178
+ shearY: 0
57179
+ });
57180
+ }
57181
+ }
57182
+ if (totalEnergy < conf.FG_SLEEP_THRESHOLD && this.tickTimer !== null) {
57183
+ clearInterval(this.tickTimer);
57184
+ this.tickTimer = null;
57185
+ this.syncPositions();
57186
+ }
57187
+ }
57188
+ syncPositions() {
57189
+ const nodes = this.getNodes();
57190
+ if (nodes.length === 0)
57191
+ return;
57192
+ const movedItems = nodes.map((item) => {
57193
+ const id = item.getId();
57194
+ const pos = item.transformation.getTranslation();
57195
+ const last = this.lastSyncedPositions.get(id);
57196
+ const dx = last ? pos.x - last.x : 0;
57197
+ const dy = last ? pos.y - last.y : 0;
57198
+ this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
57199
+ return { id, dx, dy };
57200
+ }).filter(({ dx, dy }) => Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5);
57201
+ if (movedItems.length === 0)
57202
+ return;
57203
+ const operation = {
57204
+ class: "Transformation",
57205
+ method: "applyMatrix",
57206
+ items: movedItems.map(({ id, dx, dy }) => ({
57207
+ id,
57208
+ matrix: { translateX: dx, translateY: dy, scaleX: 1, scaleY: 1, shearX: 0, shearY: 0 }
57209
+ }))
57210
+ };
57211
+ this.board.events.emit(operation);
57212
+ }
57213
+ wake() {
57214
+ if (this.tickTimer === null && this.syncTimer !== null) {
57215
+ this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
57216
+ }
57217
+ }
57218
+ }
57219
+
57042
57220
  // src/Board.ts
57043
57221
  class Board {
57044
57222
  boardId;
@@ -58111,6 +58289,25 @@ class Board {
58111
58289
  isGravityEnabled() {
58112
58290
  return this.gravity !== null;
58113
58291
  }
58292
+ forceGraph = null;
58293
+ enableForceGraph() {
58294
+ if (this.forceGraph)
58295
+ return;
58296
+ this.forceGraph = new ForceGraphEngine(this);
58297
+ this.forceGraph.start();
58298
+ }
58299
+ disableForceGraph() {
58300
+ if (!this.forceGraph)
58301
+ return;
58302
+ this.forceGraph.stop();
58303
+ this.forceGraph = null;
58304
+ }
58305
+ isForceGraphEnabled() {
58306
+ return this.forceGraph !== null;
58307
+ }
58308
+ wakeForceGraph() {
58309
+ this.forceGraph?.wake();
58310
+ }
58114
58311
  }
58115
58312
  // src/Events/Merge.ts
58116
58313
  var import_slate35 = require("slate");