microboard-temp 0.13.15 → 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.
- package/dist/cjs/browser.js +194 -0
- package/dist/cjs/index.js +194 -0
- package/dist/cjs/node.js +194 -0
- package/dist/esm/browser.js +194 -0
- package/dist/esm/index.js +194 -0
- package/dist/esm/node.js +194 -0
- package/dist/types/Board.d.ts +6 -0
- package/dist/types/ForceGraph/ForceGraphEngine.d.ts +21 -0
- package/dist/types/Settings.d.ts +5 -0
- package/package.json +1 -1
package/dist/cjs/browser.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,
|
|
@@ -54569,6 +54574,176 @@ class GravityEngine {
|
|
|
54569
54574
|
}
|
|
54570
54575
|
}
|
|
54571
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
|
+
|
|
54572
54747
|
// src/Board.ts
|
|
54573
54748
|
class Board {
|
|
54574
54749
|
boardId;
|
|
@@ -55641,6 +55816,25 @@ class Board {
|
|
|
55641
55816
|
isGravityEnabled() {
|
|
55642
55817
|
return this.gravity !== null;
|
|
55643
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
|
+
}
|
|
55644
55838
|
}
|
|
55645
55839
|
// src/Events/Merge.ts
|
|
55646
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,
|
|
@@ -54569,6 +54574,176 @@ class GravityEngine {
|
|
|
54569
54574
|
}
|
|
54570
54575
|
}
|
|
54571
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
|
+
|
|
54572
54747
|
// src/Board.ts
|
|
54573
54748
|
class Board {
|
|
54574
54749
|
boardId;
|
|
@@ -55641,6 +55816,25 @@ class Board {
|
|
|
55641
55816
|
isGravityEnabled() {
|
|
55642
55817
|
return this.gravity !== null;
|
|
55643
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
|
+
}
|
|
55644
55838
|
}
|
|
55645
55839
|
// src/Events/Merge.ts
|
|
55646
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,
|
|
@@ -57042,6 +57047,176 @@ class GravityEngine {
|
|
|
57042
57047
|
}
|
|
57043
57048
|
}
|
|
57044
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
|
+
|
|
57045
57220
|
// src/Board.ts
|
|
57046
57221
|
class Board {
|
|
57047
57222
|
boardId;
|
|
@@ -58114,6 +58289,25 @@ class Board {
|
|
|
58114
58289
|
isGravityEnabled() {
|
|
58115
58290
|
return this.gravity !== null;
|
|
58116
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
|
+
}
|
|
58117
58311
|
}
|
|
58118
58312
|
// src/Events/Merge.ts
|
|
58119
58313
|
var import_slate35 = require("slate");
|
package/dist/esm/browser.js
CHANGED
|
@@ -4495,6 +4495,11 @@ var conf = {
|
|
|
4495
4495
|
DEFAULT_GAME_ITEM_DIMENSIONS: { width: 200, height: 200 },
|
|
4496
4496
|
MAX_CARD_SIZE: 500,
|
|
4497
4497
|
CONNECTOR_ITEM_OFFSET: 20,
|
|
4498
|
+
FG_SPRING_K: 3,
|
|
4499
|
+
FG_TARGET_GAP: 60,
|
|
4500
|
+
FG_REPULSION: 400000,
|
|
4501
|
+
FG_DAMPING: 0.8,
|
|
4502
|
+
FG_SLEEP_THRESHOLD: 0.5,
|
|
4498
4503
|
GRAVITY_G: 80,
|
|
4499
4504
|
GRAVITY_G_CENTER: 120,
|
|
4500
4505
|
GRAVITY_DAMPING: 0.96,
|
|
@@ -54398,6 +54403,176 @@ class GravityEngine {
|
|
|
54398
54403
|
}
|
|
54399
54404
|
}
|
|
54400
54405
|
|
|
54406
|
+
// src/ForceGraph/ForceGraphEngine.ts
|
|
54407
|
+
var EXCLUDED_TYPES = new Set(["Connector", "Comment"]);
|
|
54408
|
+
|
|
54409
|
+
class ForceGraphEngine {
|
|
54410
|
+
board;
|
|
54411
|
+
velocities = new Map;
|
|
54412
|
+
tickTimer = null;
|
|
54413
|
+
syncTimer = null;
|
|
54414
|
+
lastSyncedPositions = new Map;
|
|
54415
|
+
TICK_MS = 33;
|
|
54416
|
+
SYNC_MS = 300;
|
|
54417
|
+
SOFTENING_SQ = 100 * 100;
|
|
54418
|
+
MIN_MOVE_PX = 0.05;
|
|
54419
|
+
constructor(board) {
|
|
54420
|
+
this.board = board;
|
|
54421
|
+
}
|
|
54422
|
+
start() {
|
|
54423
|
+
if (this.tickTimer !== null)
|
|
54424
|
+
return;
|
|
54425
|
+
for (const item of this.getNodes()) {
|
|
54426
|
+
const pos = item.transformation.getTranslation();
|
|
54427
|
+
this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
|
|
54428
|
+
this.velocities.set(item.getId(), { vx: 0, vy: 0 });
|
|
54429
|
+
}
|
|
54430
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54431
|
+
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
54432
|
+
}
|
|
54433
|
+
stop() {
|
|
54434
|
+
if (this.tickTimer !== null) {
|
|
54435
|
+
clearInterval(this.tickTimer);
|
|
54436
|
+
this.tickTimer = null;
|
|
54437
|
+
}
|
|
54438
|
+
if (this.syncTimer !== null) {
|
|
54439
|
+
clearInterval(this.syncTimer);
|
|
54440
|
+
this.syncTimer = null;
|
|
54441
|
+
}
|
|
54442
|
+
this.syncPositions();
|
|
54443
|
+
this.velocities.clear();
|
|
54444
|
+
this.lastSyncedPositions.clear();
|
|
54445
|
+
}
|
|
54446
|
+
getNodes() {
|
|
54447
|
+
return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
|
|
54448
|
+
}
|
|
54449
|
+
getConnectors() {
|
|
54450
|
+
return this.board.items.listAll().filter((item) => item.itemType === "Connector");
|
|
54451
|
+
}
|
|
54452
|
+
tick() {
|
|
54453
|
+
const dt = this.TICK_MS / 1000;
|
|
54454
|
+
const nodes = this.getNodes();
|
|
54455
|
+
if (nodes.length < 2)
|
|
54456
|
+
return;
|
|
54457
|
+
const snapMap = new Map;
|
|
54458
|
+
for (const item of nodes) {
|
|
54459
|
+
const pos = item.transformation.getTranslation();
|
|
54460
|
+
const mbr = item.getMbr();
|
|
54461
|
+
const w = Math.max(mbr.getWidth(), 1);
|
|
54462
|
+
const h2 = Math.max(mbr.getHeight(), 1);
|
|
54463
|
+
snapMap.set(item.getId(), {
|
|
54464
|
+
id: item.getId(),
|
|
54465
|
+
cx: pos.x + w * 0.5,
|
|
54466
|
+
cy: pos.y + h2 * 0.5,
|
|
54467
|
+
w,
|
|
54468
|
+
h: h2
|
|
54469
|
+
});
|
|
54470
|
+
}
|
|
54471
|
+
const snap = Array.from(snapMap.values());
|
|
54472
|
+
const ax = new Map;
|
|
54473
|
+
const ay = new Map;
|
|
54474
|
+
for (const s2 of snap) {
|
|
54475
|
+
ax.set(s2.id, 0);
|
|
54476
|
+
ay.set(s2.id, 0);
|
|
54477
|
+
}
|
|
54478
|
+
for (const connector of this.getConnectors()) {
|
|
54479
|
+
const { startItem, endItem } = connector.getConnectedItems();
|
|
54480
|
+
if (!startItem || !endItem)
|
|
54481
|
+
continue;
|
|
54482
|
+
const s1 = snapMap.get(startItem.getId());
|
|
54483
|
+
const s2 = snapMap.get(endItem.getId());
|
|
54484
|
+
if (!s1 || !s2)
|
|
54485
|
+
continue;
|
|
54486
|
+
const dx = s2.cx - s1.cx;
|
|
54487
|
+
const dy = s2.cy - s1.cy;
|
|
54488
|
+
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
54489
|
+
const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + conf.FG_TARGET_GAP;
|
|
54490
|
+
const stretch = dist - targetDist;
|
|
54491
|
+
const forceMag = stretch * conf.FG_SPRING_K;
|
|
54492
|
+
const fx = dx / dist * forceMag;
|
|
54493
|
+
const fy = dy / dist * forceMag;
|
|
54494
|
+
ax.set(s1.id, (ax.get(s1.id) ?? 0) + fx);
|
|
54495
|
+
ay.set(s1.id, (ay.get(s1.id) ?? 0) + fy);
|
|
54496
|
+
ax.set(s2.id, (ax.get(s2.id) ?? 0) - fx);
|
|
54497
|
+
ay.set(s2.id, (ay.get(s2.id) ?? 0) - fy);
|
|
54498
|
+
}
|
|
54499
|
+
for (let i = 0;i < snap.length; i++) {
|
|
54500
|
+
for (let j = i + 1;j < snap.length; j++) {
|
|
54501
|
+
const s1 = snap[i];
|
|
54502
|
+
const s2 = snap[j];
|
|
54503
|
+
const dx = s2.cx - s1.cx;
|
|
54504
|
+
const dy = s2.cy - s1.cy;
|
|
54505
|
+
const distSq = dx * dx + dy * dy + this.SOFTENING_SQ;
|
|
54506
|
+
const repMag = conf.FG_REPULSION / distSq;
|
|
54507
|
+
const fx = dx * repMag;
|
|
54508
|
+
const fy = dy * repMag;
|
|
54509
|
+
ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
|
|
54510
|
+
ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
|
|
54511
|
+
ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
|
|
54512
|
+
ay.set(s2.id, (ay.get(s2.id) ?? 0) + fy);
|
|
54513
|
+
}
|
|
54514
|
+
}
|
|
54515
|
+
let totalEnergy = 0;
|
|
54516
|
+
for (const item of nodes) {
|
|
54517
|
+
const id = item.getId();
|
|
54518
|
+
if (!this.velocities.has(id)) {
|
|
54519
|
+
this.velocities.set(id, { vx: 0, vy: 0 });
|
|
54520
|
+
}
|
|
54521
|
+
const vel = this.velocities.get(id);
|
|
54522
|
+
vel.vx = (vel.vx + (ax.get(id) ?? 0) * dt) * conf.FG_DAMPING;
|
|
54523
|
+
vel.vy = (vel.vy + (ay.get(id) ?? 0) * dt) * conf.FG_DAMPING;
|
|
54524
|
+
totalEnergy += vel.vx * vel.vx + vel.vy * vel.vy;
|
|
54525
|
+
const moveX = vel.vx * dt;
|
|
54526
|
+
const moveY = vel.vy * dt;
|
|
54527
|
+
if (Math.abs(moveX) >= this.MIN_MOVE_PX || Math.abs(moveY) >= this.MIN_MOVE_PX) {
|
|
54528
|
+
item.transformation.applyMatrixSilent({
|
|
54529
|
+
translateX: moveX,
|
|
54530
|
+
translateY: moveY,
|
|
54531
|
+
scaleX: 1,
|
|
54532
|
+
scaleY: 1,
|
|
54533
|
+
shearX: 0,
|
|
54534
|
+
shearY: 0
|
|
54535
|
+
});
|
|
54536
|
+
}
|
|
54537
|
+
}
|
|
54538
|
+
if (totalEnergy < conf.FG_SLEEP_THRESHOLD && this.tickTimer !== null) {
|
|
54539
|
+
clearInterval(this.tickTimer);
|
|
54540
|
+
this.tickTimer = null;
|
|
54541
|
+
this.syncPositions();
|
|
54542
|
+
}
|
|
54543
|
+
}
|
|
54544
|
+
syncPositions() {
|
|
54545
|
+
const nodes = this.getNodes();
|
|
54546
|
+
if (nodes.length === 0)
|
|
54547
|
+
return;
|
|
54548
|
+
const movedItems = nodes.map((item) => {
|
|
54549
|
+
const id = item.getId();
|
|
54550
|
+
const pos = item.transformation.getTranslation();
|
|
54551
|
+
const last = this.lastSyncedPositions.get(id);
|
|
54552
|
+
const dx = last ? pos.x - last.x : 0;
|
|
54553
|
+
const dy = last ? pos.y - last.y : 0;
|
|
54554
|
+
this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
|
|
54555
|
+
return { id, dx, dy };
|
|
54556
|
+
}).filter(({ dx, dy }) => Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5);
|
|
54557
|
+
if (movedItems.length === 0)
|
|
54558
|
+
return;
|
|
54559
|
+
const operation = {
|
|
54560
|
+
class: "Transformation",
|
|
54561
|
+
method: "applyMatrix",
|
|
54562
|
+
items: movedItems.map(({ id, dx, dy }) => ({
|
|
54563
|
+
id,
|
|
54564
|
+
matrix: { translateX: dx, translateY: dy, scaleX: 1, scaleY: 1, shearX: 0, shearY: 0 }
|
|
54565
|
+
}))
|
|
54566
|
+
};
|
|
54567
|
+
this.board.events.emit(operation);
|
|
54568
|
+
}
|
|
54569
|
+
wake() {
|
|
54570
|
+
if (this.tickTimer === null && this.syncTimer !== null) {
|
|
54571
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54572
|
+
}
|
|
54573
|
+
}
|
|
54574
|
+
}
|
|
54575
|
+
|
|
54401
54576
|
// src/Board.ts
|
|
54402
54577
|
class Board {
|
|
54403
54578
|
boardId;
|
|
@@ -55470,6 +55645,25 @@ class Board {
|
|
|
55470
55645
|
isGravityEnabled() {
|
|
55471
55646
|
return this.gravity !== null;
|
|
55472
55647
|
}
|
|
55648
|
+
forceGraph = null;
|
|
55649
|
+
enableForceGraph() {
|
|
55650
|
+
if (this.forceGraph)
|
|
55651
|
+
return;
|
|
55652
|
+
this.forceGraph = new ForceGraphEngine(this);
|
|
55653
|
+
this.forceGraph.start();
|
|
55654
|
+
}
|
|
55655
|
+
disableForceGraph() {
|
|
55656
|
+
if (!this.forceGraph)
|
|
55657
|
+
return;
|
|
55658
|
+
this.forceGraph.stop();
|
|
55659
|
+
this.forceGraph = null;
|
|
55660
|
+
}
|
|
55661
|
+
isForceGraphEnabled() {
|
|
55662
|
+
return this.forceGraph !== null;
|
|
55663
|
+
}
|
|
55664
|
+
wakeForceGraph() {
|
|
55665
|
+
this.forceGraph?.wake();
|
|
55666
|
+
}
|
|
55473
55667
|
}
|
|
55474
55668
|
// src/Events/Merge.ts
|
|
55475
55669
|
import { Path as Path15 } from "slate";
|
package/dist/esm/index.js
CHANGED
|
@@ -4488,6 +4488,11 @@ var conf = {
|
|
|
4488
4488
|
DEFAULT_GAME_ITEM_DIMENSIONS: { width: 200, height: 200 },
|
|
4489
4489
|
MAX_CARD_SIZE: 500,
|
|
4490
4490
|
CONNECTOR_ITEM_OFFSET: 20,
|
|
4491
|
+
FG_SPRING_K: 3,
|
|
4492
|
+
FG_TARGET_GAP: 60,
|
|
4493
|
+
FG_REPULSION: 400000,
|
|
4494
|
+
FG_DAMPING: 0.8,
|
|
4495
|
+
FG_SLEEP_THRESHOLD: 0.5,
|
|
4491
4496
|
GRAVITY_G: 80,
|
|
4492
4497
|
GRAVITY_G_CENTER: 120,
|
|
4493
4498
|
GRAVITY_DAMPING: 0.96,
|
|
@@ -54391,6 +54396,176 @@ class GravityEngine {
|
|
|
54391
54396
|
}
|
|
54392
54397
|
}
|
|
54393
54398
|
|
|
54399
|
+
// src/ForceGraph/ForceGraphEngine.ts
|
|
54400
|
+
var EXCLUDED_TYPES = new Set(["Connector", "Comment"]);
|
|
54401
|
+
|
|
54402
|
+
class ForceGraphEngine {
|
|
54403
|
+
board;
|
|
54404
|
+
velocities = new Map;
|
|
54405
|
+
tickTimer = null;
|
|
54406
|
+
syncTimer = null;
|
|
54407
|
+
lastSyncedPositions = new Map;
|
|
54408
|
+
TICK_MS = 33;
|
|
54409
|
+
SYNC_MS = 300;
|
|
54410
|
+
SOFTENING_SQ = 100 * 100;
|
|
54411
|
+
MIN_MOVE_PX = 0.05;
|
|
54412
|
+
constructor(board) {
|
|
54413
|
+
this.board = board;
|
|
54414
|
+
}
|
|
54415
|
+
start() {
|
|
54416
|
+
if (this.tickTimer !== null)
|
|
54417
|
+
return;
|
|
54418
|
+
for (const item of this.getNodes()) {
|
|
54419
|
+
const pos = item.transformation.getTranslation();
|
|
54420
|
+
this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
|
|
54421
|
+
this.velocities.set(item.getId(), { vx: 0, vy: 0 });
|
|
54422
|
+
}
|
|
54423
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54424
|
+
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
54425
|
+
}
|
|
54426
|
+
stop() {
|
|
54427
|
+
if (this.tickTimer !== null) {
|
|
54428
|
+
clearInterval(this.tickTimer);
|
|
54429
|
+
this.tickTimer = null;
|
|
54430
|
+
}
|
|
54431
|
+
if (this.syncTimer !== null) {
|
|
54432
|
+
clearInterval(this.syncTimer);
|
|
54433
|
+
this.syncTimer = null;
|
|
54434
|
+
}
|
|
54435
|
+
this.syncPositions();
|
|
54436
|
+
this.velocities.clear();
|
|
54437
|
+
this.lastSyncedPositions.clear();
|
|
54438
|
+
}
|
|
54439
|
+
getNodes() {
|
|
54440
|
+
return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
|
|
54441
|
+
}
|
|
54442
|
+
getConnectors() {
|
|
54443
|
+
return this.board.items.listAll().filter((item) => item.itemType === "Connector");
|
|
54444
|
+
}
|
|
54445
|
+
tick() {
|
|
54446
|
+
const dt = this.TICK_MS / 1000;
|
|
54447
|
+
const nodes = this.getNodes();
|
|
54448
|
+
if (nodes.length < 2)
|
|
54449
|
+
return;
|
|
54450
|
+
const snapMap = new Map;
|
|
54451
|
+
for (const item of nodes) {
|
|
54452
|
+
const pos = item.transformation.getTranslation();
|
|
54453
|
+
const mbr = item.getMbr();
|
|
54454
|
+
const w = Math.max(mbr.getWidth(), 1);
|
|
54455
|
+
const h2 = Math.max(mbr.getHeight(), 1);
|
|
54456
|
+
snapMap.set(item.getId(), {
|
|
54457
|
+
id: item.getId(),
|
|
54458
|
+
cx: pos.x + w * 0.5,
|
|
54459
|
+
cy: pos.y + h2 * 0.5,
|
|
54460
|
+
w,
|
|
54461
|
+
h: h2
|
|
54462
|
+
});
|
|
54463
|
+
}
|
|
54464
|
+
const snap = Array.from(snapMap.values());
|
|
54465
|
+
const ax = new Map;
|
|
54466
|
+
const ay = new Map;
|
|
54467
|
+
for (const s2 of snap) {
|
|
54468
|
+
ax.set(s2.id, 0);
|
|
54469
|
+
ay.set(s2.id, 0);
|
|
54470
|
+
}
|
|
54471
|
+
for (const connector of this.getConnectors()) {
|
|
54472
|
+
const { startItem, endItem } = connector.getConnectedItems();
|
|
54473
|
+
if (!startItem || !endItem)
|
|
54474
|
+
continue;
|
|
54475
|
+
const s1 = snapMap.get(startItem.getId());
|
|
54476
|
+
const s2 = snapMap.get(endItem.getId());
|
|
54477
|
+
if (!s1 || !s2)
|
|
54478
|
+
continue;
|
|
54479
|
+
const dx = s2.cx - s1.cx;
|
|
54480
|
+
const dy = s2.cy - s1.cy;
|
|
54481
|
+
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
54482
|
+
const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + conf.FG_TARGET_GAP;
|
|
54483
|
+
const stretch = dist - targetDist;
|
|
54484
|
+
const forceMag = stretch * conf.FG_SPRING_K;
|
|
54485
|
+
const fx = dx / dist * forceMag;
|
|
54486
|
+
const fy = dy / dist * forceMag;
|
|
54487
|
+
ax.set(s1.id, (ax.get(s1.id) ?? 0) + fx);
|
|
54488
|
+
ay.set(s1.id, (ay.get(s1.id) ?? 0) + fy);
|
|
54489
|
+
ax.set(s2.id, (ax.get(s2.id) ?? 0) - fx);
|
|
54490
|
+
ay.set(s2.id, (ay.get(s2.id) ?? 0) - fy);
|
|
54491
|
+
}
|
|
54492
|
+
for (let i = 0;i < snap.length; i++) {
|
|
54493
|
+
for (let j = i + 1;j < snap.length; j++) {
|
|
54494
|
+
const s1 = snap[i];
|
|
54495
|
+
const s2 = snap[j];
|
|
54496
|
+
const dx = s2.cx - s1.cx;
|
|
54497
|
+
const dy = s2.cy - s1.cy;
|
|
54498
|
+
const distSq = dx * dx + dy * dy + this.SOFTENING_SQ;
|
|
54499
|
+
const repMag = conf.FG_REPULSION / distSq;
|
|
54500
|
+
const fx = dx * repMag;
|
|
54501
|
+
const fy = dy * repMag;
|
|
54502
|
+
ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
|
|
54503
|
+
ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
|
|
54504
|
+
ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
|
|
54505
|
+
ay.set(s2.id, (ay.get(s2.id) ?? 0) + fy);
|
|
54506
|
+
}
|
|
54507
|
+
}
|
|
54508
|
+
let totalEnergy = 0;
|
|
54509
|
+
for (const item of nodes) {
|
|
54510
|
+
const id = item.getId();
|
|
54511
|
+
if (!this.velocities.has(id)) {
|
|
54512
|
+
this.velocities.set(id, { vx: 0, vy: 0 });
|
|
54513
|
+
}
|
|
54514
|
+
const vel = this.velocities.get(id);
|
|
54515
|
+
vel.vx = (vel.vx + (ax.get(id) ?? 0) * dt) * conf.FG_DAMPING;
|
|
54516
|
+
vel.vy = (vel.vy + (ay.get(id) ?? 0) * dt) * conf.FG_DAMPING;
|
|
54517
|
+
totalEnergy += vel.vx * vel.vx + vel.vy * vel.vy;
|
|
54518
|
+
const moveX = vel.vx * dt;
|
|
54519
|
+
const moveY = vel.vy * dt;
|
|
54520
|
+
if (Math.abs(moveX) >= this.MIN_MOVE_PX || Math.abs(moveY) >= this.MIN_MOVE_PX) {
|
|
54521
|
+
item.transformation.applyMatrixSilent({
|
|
54522
|
+
translateX: moveX,
|
|
54523
|
+
translateY: moveY,
|
|
54524
|
+
scaleX: 1,
|
|
54525
|
+
scaleY: 1,
|
|
54526
|
+
shearX: 0,
|
|
54527
|
+
shearY: 0
|
|
54528
|
+
});
|
|
54529
|
+
}
|
|
54530
|
+
}
|
|
54531
|
+
if (totalEnergy < conf.FG_SLEEP_THRESHOLD && this.tickTimer !== null) {
|
|
54532
|
+
clearInterval(this.tickTimer);
|
|
54533
|
+
this.tickTimer = null;
|
|
54534
|
+
this.syncPositions();
|
|
54535
|
+
}
|
|
54536
|
+
}
|
|
54537
|
+
syncPositions() {
|
|
54538
|
+
const nodes = this.getNodes();
|
|
54539
|
+
if (nodes.length === 0)
|
|
54540
|
+
return;
|
|
54541
|
+
const movedItems = nodes.map((item) => {
|
|
54542
|
+
const id = item.getId();
|
|
54543
|
+
const pos = item.transformation.getTranslation();
|
|
54544
|
+
const last = this.lastSyncedPositions.get(id);
|
|
54545
|
+
const dx = last ? pos.x - last.x : 0;
|
|
54546
|
+
const dy = last ? pos.y - last.y : 0;
|
|
54547
|
+
this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
|
|
54548
|
+
return { id, dx, dy };
|
|
54549
|
+
}).filter(({ dx, dy }) => Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5);
|
|
54550
|
+
if (movedItems.length === 0)
|
|
54551
|
+
return;
|
|
54552
|
+
const operation = {
|
|
54553
|
+
class: "Transformation",
|
|
54554
|
+
method: "applyMatrix",
|
|
54555
|
+
items: movedItems.map(({ id, dx, dy }) => ({
|
|
54556
|
+
id,
|
|
54557
|
+
matrix: { translateX: dx, translateY: dy, scaleX: 1, scaleY: 1, shearX: 0, shearY: 0 }
|
|
54558
|
+
}))
|
|
54559
|
+
};
|
|
54560
|
+
this.board.events.emit(operation);
|
|
54561
|
+
}
|
|
54562
|
+
wake() {
|
|
54563
|
+
if (this.tickTimer === null && this.syncTimer !== null) {
|
|
54564
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54565
|
+
}
|
|
54566
|
+
}
|
|
54567
|
+
}
|
|
54568
|
+
|
|
54394
54569
|
// src/Board.ts
|
|
54395
54570
|
class Board {
|
|
54396
54571
|
boardId;
|
|
@@ -55463,6 +55638,25 @@ class Board {
|
|
|
55463
55638
|
isGravityEnabled() {
|
|
55464
55639
|
return this.gravity !== null;
|
|
55465
55640
|
}
|
|
55641
|
+
forceGraph = null;
|
|
55642
|
+
enableForceGraph() {
|
|
55643
|
+
if (this.forceGraph)
|
|
55644
|
+
return;
|
|
55645
|
+
this.forceGraph = new ForceGraphEngine(this);
|
|
55646
|
+
this.forceGraph.start();
|
|
55647
|
+
}
|
|
55648
|
+
disableForceGraph() {
|
|
55649
|
+
if (!this.forceGraph)
|
|
55650
|
+
return;
|
|
55651
|
+
this.forceGraph.stop();
|
|
55652
|
+
this.forceGraph = null;
|
|
55653
|
+
}
|
|
55654
|
+
isForceGraphEnabled() {
|
|
55655
|
+
return this.forceGraph !== null;
|
|
55656
|
+
}
|
|
55657
|
+
wakeForceGraph() {
|
|
55658
|
+
this.forceGraph?.wake();
|
|
55659
|
+
}
|
|
55466
55660
|
}
|
|
55467
55661
|
// src/Events/Merge.ts
|
|
55468
55662
|
import { Path as Path15 } from "slate";
|
package/dist/esm/node.js
CHANGED
|
@@ -5272,6 +5272,11 @@ var conf = {
|
|
|
5272
5272
|
DEFAULT_GAME_ITEM_DIMENSIONS: { width: 200, height: 200 },
|
|
5273
5273
|
MAX_CARD_SIZE: 500,
|
|
5274
5274
|
CONNECTOR_ITEM_OFFSET: 20,
|
|
5275
|
+
FG_SPRING_K: 3,
|
|
5276
|
+
FG_TARGET_GAP: 60,
|
|
5277
|
+
FG_REPULSION: 400000,
|
|
5278
|
+
FG_DAMPING: 0.8,
|
|
5279
|
+
FG_SLEEP_THRESHOLD: 0.5,
|
|
5275
5280
|
GRAVITY_G: 80,
|
|
5276
5281
|
GRAVITY_G_CENTER: 120,
|
|
5277
5282
|
GRAVITY_DAMPING: 0.96,
|
|
@@ -56859,6 +56864,176 @@ class GravityEngine {
|
|
|
56859
56864
|
}
|
|
56860
56865
|
}
|
|
56861
56866
|
|
|
56867
|
+
// src/ForceGraph/ForceGraphEngine.ts
|
|
56868
|
+
var EXCLUDED_TYPES = new Set(["Connector", "Comment"]);
|
|
56869
|
+
|
|
56870
|
+
class ForceGraphEngine {
|
|
56871
|
+
board;
|
|
56872
|
+
velocities = new Map;
|
|
56873
|
+
tickTimer = null;
|
|
56874
|
+
syncTimer = null;
|
|
56875
|
+
lastSyncedPositions = new Map;
|
|
56876
|
+
TICK_MS = 33;
|
|
56877
|
+
SYNC_MS = 300;
|
|
56878
|
+
SOFTENING_SQ = 100 * 100;
|
|
56879
|
+
MIN_MOVE_PX = 0.05;
|
|
56880
|
+
constructor(board) {
|
|
56881
|
+
this.board = board;
|
|
56882
|
+
}
|
|
56883
|
+
start() {
|
|
56884
|
+
if (this.tickTimer !== null)
|
|
56885
|
+
return;
|
|
56886
|
+
for (const item of this.getNodes()) {
|
|
56887
|
+
const pos = item.transformation.getTranslation();
|
|
56888
|
+
this.lastSyncedPositions.set(item.getId(), { x: pos.x, y: pos.y });
|
|
56889
|
+
this.velocities.set(item.getId(), { vx: 0, vy: 0 });
|
|
56890
|
+
}
|
|
56891
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
56892
|
+
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
56893
|
+
}
|
|
56894
|
+
stop() {
|
|
56895
|
+
if (this.tickTimer !== null) {
|
|
56896
|
+
clearInterval(this.tickTimer);
|
|
56897
|
+
this.tickTimer = null;
|
|
56898
|
+
}
|
|
56899
|
+
if (this.syncTimer !== null) {
|
|
56900
|
+
clearInterval(this.syncTimer);
|
|
56901
|
+
this.syncTimer = null;
|
|
56902
|
+
}
|
|
56903
|
+
this.syncPositions();
|
|
56904
|
+
this.velocities.clear();
|
|
56905
|
+
this.lastSyncedPositions.clear();
|
|
56906
|
+
}
|
|
56907
|
+
getNodes() {
|
|
56908
|
+
return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
|
|
56909
|
+
}
|
|
56910
|
+
getConnectors() {
|
|
56911
|
+
return this.board.items.listAll().filter((item) => item.itemType === "Connector");
|
|
56912
|
+
}
|
|
56913
|
+
tick() {
|
|
56914
|
+
const dt = this.TICK_MS / 1000;
|
|
56915
|
+
const nodes = this.getNodes();
|
|
56916
|
+
if (nodes.length < 2)
|
|
56917
|
+
return;
|
|
56918
|
+
const snapMap = new Map;
|
|
56919
|
+
for (const item of nodes) {
|
|
56920
|
+
const pos = item.transformation.getTranslation();
|
|
56921
|
+
const mbr = item.getMbr();
|
|
56922
|
+
const w = Math.max(mbr.getWidth(), 1);
|
|
56923
|
+
const h2 = Math.max(mbr.getHeight(), 1);
|
|
56924
|
+
snapMap.set(item.getId(), {
|
|
56925
|
+
id: item.getId(),
|
|
56926
|
+
cx: pos.x + w * 0.5,
|
|
56927
|
+
cy: pos.y + h2 * 0.5,
|
|
56928
|
+
w,
|
|
56929
|
+
h: h2
|
|
56930
|
+
});
|
|
56931
|
+
}
|
|
56932
|
+
const snap = Array.from(snapMap.values());
|
|
56933
|
+
const ax = new Map;
|
|
56934
|
+
const ay = new Map;
|
|
56935
|
+
for (const s2 of snap) {
|
|
56936
|
+
ax.set(s2.id, 0);
|
|
56937
|
+
ay.set(s2.id, 0);
|
|
56938
|
+
}
|
|
56939
|
+
for (const connector of this.getConnectors()) {
|
|
56940
|
+
const { startItem, endItem } = connector.getConnectedItems();
|
|
56941
|
+
if (!startItem || !endItem)
|
|
56942
|
+
continue;
|
|
56943
|
+
const s1 = snapMap.get(startItem.getId());
|
|
56944
|
+
const s2 = snapMap.get(endItem.getId());
|
|
56945
|
+
if (!s1 || !s2)
|
|
56946
|
+
continue;
|
|
56947
|
+
const dx = s2.cx - s1.cx;
|
|
56948
|
+
const dy = s2.cy - s1.cy;
|
|
56949
|
+
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
56950
|
+
const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + conf.FG_TARGET_GAP;
|
|
56951
|
+
const stretch = dist - targetDist;
|
|
56952
|
+
const forceMag = stretch * conf.FG_SPRING_K;
|
|
56953
|
+
const fx = dx / dist * forceMag;
|
|
56954
|
+
const fy = dy / dist * forceMag;
|
|
56955
|
+
ax.set(s1.id, (ax.get(s1.id) ?? 0) + fx);
|
|
56956
|
+
ay.set(s1.id, (ay.get(s1.id) ?? 0) + fy);
|
|
56957
|
+
ax.set(s2.id, (ax.get(s2.id) ?? 0) - fx);
|
|
56958
|
+
ay.set(s2.id, (ay.get(s2.id) ?? 0) - fy);
|
|
56959
|
+
}
|
|
56960
|
+
for (let i = 0;i < snap.length; i++) {
|
|
56961
|
+
for (let j = i + 1;j < snap.length; j++) {
|
|
56962
|
+
const s1 = snap[i];
|
|
56963
|
+
const s2 = snap[j];
|
|
56964
|
+
const dx = s2.cx - s1.cx;
|
|
56965
|
+
const dy = s2.cy - s1.cy;
|
|
56966
|
+
const distSq = dx * dx + dy * dy + this.SOFTENING_SQ;
|
|
56967
|
+
const repMag = conf.FG_REPULSION / distSq;
|
|
56968
|
+
const fx = dx * repMag;
|
|
56969
|
+
const fy = dy * repMag;
|
|
56970
|
+
ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
|
|
56971
|
+
ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
|
|
56972
|
+
ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
|
|
56973
|
+
ay.set(s2.id, (ay.get(s2.id) ?? 0) + fy);
|
|
56974
|
+
}
|
|
56975
|
+
}
|
|
56976
|
+
let totalEnergy = 0;
|
|
56977
|
+
for (const item of nodes) {
|
|
56978
|
+
const id = item.getId();
|
|
56979
|
+
if (!this.velocities.has(id)) {
|
|
56980
|
+
this.velocities.set(id, { vx: 0, vy: 0 });
|
|
56981
|
+
}
|
|
56982
|
+
const vel = this.velocities.get(id);
|
|
56983
|
+
vel.vx = (vel.vx + (ax.get(id) ?? 0) * dt) * conf.FG_DAMPING;
|
|
56984
|
+
vel.vy = (vel.vy + (ay.get(id) ?? 0) * dt) * conf.FG_DAMPING;
|
|
56985
|
+
totalEnergy += vel.vx * vel.vx + vel.vy * vel.vy;
|
|
56986
|
+
const moveX = vel.vx * dt;
|
|
56987
|
+
const moveY = vel.vy * dt;
|
|
56988
|
+
if (Math.abs(moveX) >= this.MIN_MOVE_PX || Math.abs(moveY) >= this.MIN_MOVE_PX) {
|
|
56989
|
+
item.transformation.applyMatrixSilent({
|
|
56990
|
+
translateX: moveX,
|
|
56991
|
+
translateY: moveY,
|
|
56992
|
+
scaleX: 1,
|
|
56993
|
+
scaleY: 1,
|
|
56994
|
+
shearX: 0,
|
|
56995
|
+
shearY: 0
|
|
56996
|
+
});
|
|
56997
|
+
}
|
|
56998
|
+
}
|
|
56999
|
+
if (totalEnergy < conf.FG_SLEEP_THRESHOLD && this.tickTimer !== null) {
|
|
57000
|
+
clearInterval(this.tickTimer);
|
|
57001
|
+
this.tickTimer = null;
|
|
57002
|
+
this.syncPositions();
|
|
57003
|
+
}
|
|
57004
|
+
}
|
|
57005
|
+
syncPositions() {
|
|
57006
|
+
const nodes = this.getNodes();
|
|
57007
|
+
if (nodes.length === 0)
|
|
57008
|
+
return;
|
|
57009
|
+
const movedItems = nodes.map((item) => {
|
|
57010
|
+
const id = item.getId();
|
|
57011
|
+
const pos = item.transformation.getTranslation();
|
|
57012
|
+
const last = this.lastSyncedPositions.get(id);
|
|
57013
|
+
const dx = last ? pos.x - last.x : 0;
|
|
57014
|
+
const dy = last ? pos.y - last.y : 0;
|
|
57015
|
+
this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
|
|
57016
|
+
return { id, dx, dy };
|
|
57017
|
+
}).filter(({ dx, dy }) => Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5);
|
|
57018
|
+
if (movedItems.length === 0)
|
|
57019
|
+
return;
|
|
57020
|
+
const operation = {
|
|
57021
|
+
class: "Transformation",
|
|
57022
|
+
method: "applyMatrix",
|
|
57023
|
+
items: movedItems.map(({ id, dx, dy }) => ({
|
|
57024
|
+
id,
|
|
57025
|
+
matrix: { translateX: dx, translateY: dy, scaleX: 1, scaleY: 1, shearX: 0, shearY: 0 }
|
|
57026
|
+
}))
|
|
57027
|
+
};
|
|
57028
|
+
this.board.events.emit(operation);
|
|
57029
|
+
}
|
|
57030
|
+
wake() {
|
|
57031
|
+
if (this.tickTimer === null && this.syncTimer !== null) {
|
|
57032
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
57033
|
+
}
|
|
57034
|
+
}
|
|
57035
|
+
}
|
|
57036
|
+
|
|
56862
57037
|
// src/Board.ts
|
|
56863
57038
|
class Board {
|
|
56864
57039
|
boardId;
|
|
@@ -57931,6 +58106,25 @@ class Board {
|
|
|
57931
58106
|
isGravityEnabled() {
|
|
57932
58107
|
return this.gravity !== null;
|
|
57933
58108
|
}
|
|
58109
|
+
forceGraph = null;
|
|
58110
|
+
enableForceGraph() {
|
|
58111
|
+
if (this.forceGraph)
|
|
58112
|
+
return;
|
|
58113
|
+
this.forceGraph = new ForceGraphEngine(this);
|
|
58114
|
+
this.forceGraph.start();
|
|
58115
|
+
}
|
|
58116
|
+
disableForceGraph() {
|
|
58117
|
+
if (!this.forceGraph)
|
|
58118
|
+
return;
|
|
58119
|
+
this.forceGraph.stop();
|
|
58120
|
+
this.forceGraph = null;
|
|
58121
|
+
}
|
|
58122
|
+
isForceGraphEnabled() {
|
|
58123
|
+
return this.forceGraph !== null;
|
|
58124
|
+
}
|
|
58125
|
+
wakeForceGraph() {
|
|
58126
|
+
this.forceGraph?.wake();
|
|
58127
|
+
}
|
|
57934
58128
|
}
|
|
57935
58129
|
// src/Events/Merge.ts
|
|
57936
58130
|
import { Path as Path14 } from "slate";
|
package/dist/types/Board.d.ts
CHANGED
|
@@ -139,6 +139,12 @@ export declare class Board {
|
|
|
139
139
|
enableGravity(): void;
|
|
140
140
|
disableGravity(): void;
|
|
141
141
|
isGravityEnabled(): boolean;
|
|
142
|
+
private forceGraph;
|
|
143
|
+
enableForceGraph(): void;
|
|
144
|
+
disableForceGraph(): void;
|
|
145
|
+
isForceGraphEnabled(): boolean;
|
|
146
|
+
/** Call after dragging a node to re-wake the physics engine if it was sleeping. */
|
|
147
|
+
wakeForceGraph(): void;
|
|
142
148
|
}
|
|
143
149
|
export interface BoardSnapshot {
|
|
144
150
|
items: (ItemData & {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Board } from '../Board';
|
|
2
|
+
export declare class ForceGraphEngine {
|
|
3
|
+
private board;
|
|
4
|
+
private velocities;
|
|
5
|
+
private tickTimer;
|
|
6
|
+
private syncTimer;
|
|
7
|
+
private lastSyncedPositions;
|
|
8
|
+
private readonly TICK_MS;
|
|
9
|
+
private readonly SYNC_MS;
|
|
10
|
+
private readonly SOFTENING_SQ;
|
|
11
|
+
private readonly MIN_MOVE_PX;
|
|
12
|
+
constructor(board: Board);
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
private getNodes;
|
|
16
|
+
private getConnectors;
|
|
17
|
+
private tick;
|
|
18
|
+
private syncPositions;
|
|
19
|
+
/** Re-wake the engine after user moves a node (dragging disturbs equilibrium). */
|
|
20
|
+
wake(): void;
|
|
21
|
+
}
|
package/dist/types/Settings.d.ts
CHANGED
|
@@ -247,6 +247,11 @@ export declare const conf: {
|
|
|
247
247
|
};
|
|
248
248
|
MAX_CARD_SIZE: number;
|
|
249
249
|
CONNECTOR_ITEM_OFFSET: number;
|
|
250
|
+
FG_SPRING_K: number;
|
|
251
|
+
FG_TARGET_GAP: number;
|
|
252
|
+
FG_REPULSION: number;
|
|
253
|
+
FG_DAMPING: number;
|
|
254
|
+
FG_SLEEP_THRESHOLD: number;
|
|
250
255
|
GRAVITY_G: number;
|
|
251
256
|
GRAVITY_G_CENTER: number;
|
|
252
257
|
GRAVITY_DAMPING: number;
|