microboard-temp 0.13.20 → 0.13.22
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 +142 -30
- package/dist/cjs/index.js +142 -30
- package/dist/cjs/node.js +142 -30
- package/dist/esm/browser.js +142 -30
- package/dist/esm/index.js +142 -30
- package/dist/esm/node.js +142 -30
- package/dist/types/Board.d.ts +10 -3
- package/dist/types/ForceGraph/ForceGraphEngine.d.ts +35 -3
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -54428,6 +54428,7 @@ class ForceGraphEngine {
|
|
|
54428
54428
|
tickTimer = null;
|
|
54429
54429
|
syncTimer = null;
|
|
54430
54430
|
lastSyncedPositions = new Map;
|
|
54431
|
+
activeComponents = new Map;
|
|
54431
54432
|
TICK_MS = 33;
|
|
54432
54433
|
SYNC_MS = 300;
|
|
54433
54434
|
SOFTENING_SQ = 100 * 100;
|
|
@@ -54435,18 +54436,74 @@ class ForceGraphEngine {
|
|
|
54435
54436
|
constructor(board) {
|
|
54436
54437
|
this.board = board;
|
|
54437
54438
|
}
|
|
54438
|
-
|
|
54439
|
-
if (this.
|
|
54439
|
+
enableForGraph(startNodeId) {
|
|
54440
|
+
if (this.isNodeInActiveGraph(startNodeId))
|
|
54440
54441
|
return;
|
|
54441
|
-
|
|
54442
|
-
|
|
54443
|
-
|
|
54444
|
-
|
|
54442
|
+
const nodeIds = this.bfsComponent(startNodeId);
|
|
54443
|
+
const targetGap = this.calibrateTargetGap(nodeIds);
|
|
54444
|
+
this.activeComponents.set(startNodeId, { nodeIds, targetGap });
|
|
54445
|
+
for (const id of nodeIds) {
|
|
54446
|
+
if (!this.velocities.has(id)) {
|
|
54447
|
+
this.velocities.set(id, { vx: 0, vy: 0 });
|
|
54448
|
+
}
|
|
54449
|
+
const item = this.board.items.getById(id);
|
|
54450
|
+
if (item && !this.lastSyncedPositions.has(id)) {
|
|
54451
|
+
const pos = item.transformation.getTranslation();
|
|
54452
|
+
this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
|
|
54453
|
+
}
|
|
54454
|
+
}
|
|
54455
|
+
this.ensureRunning();
|
|
54456
|
+
}
|
|
54457
|
+
disableForGraph(nodeId) {
|
|
54458
|
+
const compId = this.findComponentId(nodeId);
|
|
54459
|
+
if (!compId)
|
|
54460
|
+
return;
|
|
54461
|
+
this.activeComponents.delete(compId);
|
|
54462
|
+
if (this.activeComponents.size === 0) {
|
|
54463
|
+
this.stopTimers();
|
|
54464
|
+
}
|
|
54465
|
+
}
|
|
54466
|
+
isNodeInActiveGraph(nodeId) {
|
|
54467
|
+
return !!this.findComponentId(nodeId);
|
|
54468
|
+
}
|
|
54469
|
+
getComponentTargetGap(nodeId) {
|
|
54470
|
+
const compId = this.findComponentId(nodeId);
|
|
54471
|
+
return compId ? this.activeComponents.get(compId)?.targetGap : undefined;
|
|
54472
|
+
}
|
|
54473
|
+
setComponentTargetGap(nodeId, gap) {
|
|
54474
|
+
const compId = this.findComponentId(nodeId);
|
|
54475
|
+
if (!compId)
|
|
54476
|
+
return;
|
|
54477
|
+
const comp = this.activeComponents.get(compId);
|
|
54478
|
+
if (!comp)
|
|
54479
|
+
return;
|
|
54480
|
+
comp.targetGap = gap;
|
|
54481
|
+
this.wake();
|
|
54482
|
+
}
|
|
54483
|
+
hasActiveComponents() {
|
|
54484
|
+
return this.activeComponents.size > 0;
|
|
54485
|
+
}
|
|
54486
|
+
wake() {
|
|
54487
|
+
if (this.activeComponents.size > 0 && this.tickTimer === null) {
|
|
54488
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54445
54489
|
}
|
|
54446
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54447
|
-
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
54448
54490
|
}
|
|
54449
54491
|
stop() {
|
|
54492
|
+
this.stopTimers();
|
|
54493
|
+
this.syncPositions();
|
|
54494
|
+
this.velocities.clear();
|
|
54495
|
+
this.lastSyncedPositions.clear();
|
|
54496
|
+
this.activeComponents.clear();
|
|
54497
|
+
}
|
|
54498
|
+
ensureRunning() {
|
|
54499
|
+
if (this.tickTimer === null) {
|
|
54500
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54501
|
+
}
|
|
54502
|
+
if (this.syncTimer === null) {
|
|
54503
|
+
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
54504
|
+
}
|
|
54505
|
+
}
|
|
54506
|
+
stopTimers() {
|
|
54450
54507
|
if (this.tickTimer !== null) {
|
|
54451
54508
|
clearInterval(this.tickTimer);
|
|
54452
54509
|
this.tickTimer = null;
|
|
@@ -54455,9 +54512,56 @@ class ForceGraphEngine {
|
|
|
54455
54512
|
clearInterval(this.syncTimer);
|
|
54456
54513
|
this.syncTimer = null;
|
|
54457
54514
|
}
|
|
54458
|
-
|
|
54459
|
-
|
|
54460
|
-
this.
|
|
54515
|
+
}
|
|
54516
|
+
findComponentId(nodeId) {
|
|
54517
|
+
for (const [compId, { nodeIds }] of this.activeComponents) {
|
|
54518
|
+
if (nodeIds.has(nodeId))
|
|
54519
|
+
return compId;
|
|
54520
|
+
}
|
|
54521
|
+
return;
|
|
54522
|
+
}
|
|
54523
|
+
bfsComponent(startNodeId) {
|
|
54524
|
+
const visited = new Set;
|
|
54525
|
+
const queue = [startNodeId];
|
|
54526
|
+
const connectors = this.getConnectors();
|
|
54527
|
+
while (queue.length > 0) {
|
|
54528
|
+
const nodeId = queue.shift();
|
|
54529
|
+
if (visited.has(nodeId))
|
|
54530
|
+
continue;
|
|
54531
|
+
visited.add(nodeId);
|
|
54532
|
+
for (const connector of connectors) {
|
|
54533
|
+
const { startItem, endItem } = connector.getConnectedItems();
|
|
54534
|
+
if (startItem?.getId() === nodeId && endItem && !visited.has(endItem.getId())) {
|
|
54535
|
+
queue.push(endItem.getId());
|
|
54536
|
+
}
|
|
54537
|
+
if (endItem?.getId() === nodeId && startItem && !visited.has(startItem.getId())) {
|
|
54538
|
+
queue.push(startItem.getId());
|
|
54539
|
+
}
|
|
54540
|
+
}
|
|
54541
|
+
}
|
|
54542
|
+
return visited;
|
|
54543
|
+
}
|
|
54544
|
+
calibrateTargetGap(nodeIds) {
|
|
54545
|
+
let totalMaxDim = 0;
|
|
54546
|
+
let count = 0;
|
|
54547
|
+
for (const id of nodeIds) {
|
|
54548
|
+
const item = this.board.items.getById(id);
|
|
54549
|
+
if (!item)
|
|
54550
|
+
continue;
|
|
54551
|
+
const mbr = item.getMbr();
|
|
54552
|
+
totalMaxDim += Math.max(mbr.getWidth(), mbr.getHeight());
|
|
54553
|
+
count++;
|
|
54554
|
+
}
|
|
54555
|
+
const avgMaxDim = count > 0 ? totalMaxDim / count : 100;
|
|
54556
|
+
return avgMaxDim * 1.5;
|
|
54557
|
+
}
|
|
54558
|
+
getActiveNodeIds() {
|
|
54559
|
+
const all6 = new Set;
|
|
54560
|
+
for (const { nodeIds } of this.activeComponents.values()) {
|
|
54561
|
+
for (const id of nodeIds)
|
|
54562
|
+
all6.add(id);
|
|
54563
|
+
}
|
|
54564
|
+
return all6;
|
|
54461
54565
|
}
|
|
54462
54566
|
getNodes() {
|
|
54463
54567
|
return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
|
|
@@ -54467,8 +54571,10 @@ class ForceGraphEngine {
|
|
|
54467
54571
|
}
|
|
54468
54572
|
tick() {
|
|
54469
54573
|
const dt = this.TICK_MS / 1000;
|
|
54470
|
-
const
|
|
54471
|
-
|
|
54574
|
+
const activeIds = this.getActiveNodeIds();
|
|
54575
|
+
const allNodes = this.getNodes();
|
|
54576
|
+
const nodes = allNodes.filter((item) => activeIds.has(item.getId()));
|
|
54577
|
+
if (nodes.length < 1)
|
|
54472
54578
|
return;
|
|
54473
54579
|
const snapMap = new Map;
|
|
54474
54580
|
for (const item of nodes) {
|
|
@@ -54509,7 +54615,9 @@ class ForceGraphEngine {
|
|
|
54509
54615
|
const dx = s2.cx - s1.cx;
|
|
54510
54616
|
const dy = s2.cy - s1.cy;
|
|
54511
54617
|
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
54512
|
-
const
|
|
54618
|
+
const compId = this.findComponentId(s1.id);
|
|
54619
|
+
const targetGap = compId ? this.activeComponents.get(compId)?.targetGap ?? conf.FG_TARGET_GAP : conf.FG_TARGET_GAP;
|
|
54620
|
+
const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + targetGap;
|
|
54513
54621
|
const stretch = dist - targetDist;
|
|
54514
54622
|
const forceMag = stretch * conf.FG_SPRING_K;
|
|
54515
54623
|
const fx = dx / dist * forceMag;
|
|
@@ -54570,7 +54678,8 @@ class ForceGraphEngine {
|
|
|
54570
54678
|
}
|
|
54571
54679
|
}
|
|
54572
54680
|
syncPositions() {
|
|
54573
|
-
const
|
|
54681
|
+
const activeIds = this.getActiveNodeIds();
|
|
54682
|
+
const nodes = this.getNodes().filter((item) => activeIds.has(item.getId()));
|
|
54574
54683
|
if (nodes.length === 0)
|
|
54575
54684
|
return;
|
|
54576
54685
|
const movedItems = nodes.map((item) => {
|
|
@@ -54594,11 +54703,6 @@ class ForceGraphEngine {
|
|
|
54594
54703
|
};
|
|
54595
54704
|
this.board.events.emit(operation);
|
|
54596
54705
|
}
|
|
54597
|
-
wake() {
|
|
54598
|
-
if (this.tickTimer === null && this.syncTimer !== null) {
|
|
54599
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54600
|
-
}
|
|
54601
|
-
}
|
|
54602
54706
|
}
|
|
54603
54707
|
|
|
54604
54708
|
// src/Board.ts
|
|
@@ -55674,20 +55778,28 @@ class Board {
|
|
|
55674
55778
|
return this.gravity !== null;
|
|
55675
55779
|
}
|
|
55676
55780
|
forceGraph = null;
|
|
55677
|
-
enableForceGraph() {
|
|
55678
|
-
if (this.forceGraph)
|
|
55679
|
-
|
|
55680
|
-
|
|
55681
|
-
this.forceGraph.
|
|
55781
|
+
enableForceGraph(nodeId) {
|
|
55782
|
+
if (!this.forceGraph) {
|
|
55783
|
+
this.forceGraph = new ForceGraphEngine(this);
|
|
55784
|
+
}
|
|
55785
|
+
this.forceGraph.enableForGraph(nodeId);
|
|
55682
55786
|
}
|
|
55683
|
-
disableForceGraph() {
|
|
55787
|
+
disableForceGraph(nodeId) {
|
|
55684
55788
|
if (!this.forceGraph)
|
|
55685
55789
|
return;
|
|
55686
|
-
this.forceGraph.
|
|
55687
|
-
this.forceGraph
|
|
55790
|
+
this.forceGraph.disableForGraph(nodeId);
|
|
55791
|
+
if (!this.forceGraph.hasActiveComponents()) {
|
|
55792
|
+
this.forceGraph = null;
|
|
55793
|
+
}
|
|
55794
|
+
}
|
|
55795
|
+
isNodeInForceGraph(nodeId) {
|
|
55796
|
+
return this.forceGraph?.isNodeInActiveGraph(nodeId) ?? false;
|
|
55797
|
+
}
|
|
55798
|
+
getForceGraphGap(nodeId) {
|
|
55799
|
+
return this.forceGraph?.getComponentTargetGap(nodeId);
|
|
55688
55800
|
}
|
|
55689
|
-
|
|
55690
|
-
|
|
55801
|
+
setForceGraphGap(nodeId, gap) {
|
|
55802
|
+
this.forceGraph?.setComponentTargetGap(nodeId, gap);
|
|
55691
55803
|
}
|
|
55692
55804
|
wakeForceGraph() {
|
|
55693
55805
|
this.forceGraph?.wake();
|
package/dist/esm/node.js
CHANGED
|
@@ -56896,6 +56896,7 @@ class ForceGraphEngine {
|
|
|
56896
56896
|
tickTimer = null;
|
|
56897
56897
|
syncTimer = null;
|
|
56898
56898
|
lastSyncedPositions = new Map;
|
|
56899
|
+
activeComponents = new Map;
|
|
56899
56900
|
TICK_MS = 33;
|
|
56900
56901
|
SYNC_MS = 300;
|
|
56901
56902
|
SOFTENING_SQ = 100 * 100;
|
|
@@ -56903,18 +56904,74 @@ class ForceGraphEngine {
|
|
|
56903
56904
|
constructor(board) {
|
|
56904
56905
|
this.board = board;
|
|
56905
56906
|
}
|
|
56906
|
-
|
|
56907
|
-
if (this.
|
|
56907
|
+
enableForGraph(startNodeId) {
|
|
56908
|
+
if (this.isNodeInActiveGraph(startNodeId))
|
|
56908
56909
|
return;
|
|
56909
|
-
|
|
56910
|
-
|
|
56911
|
-
|
|
56912
|
-
|
|
56910
|
+
const nodeIds = this.bfsComponent(startNodeId);
|
|
56911
|
+
const targetGap = this.calibrateTargetGap(nodeIds);
|
|
56912
|
+
this.activeComponents.set(startNodeId, { nodeIds, targetGap });
|
|
56913
|
+
for (const id of nodeIds) {
|
|
56914
|
+
if (!this.velocities.has(id)) {
|
|
56915
|
+
this.velocities.set(id, { vx: 0, vy: 0 });
|
|
56916
|
+
}
|
|
56917
|
+
const item = this.board.items.getById(id);
|
|
56918
|
+
if (item && !this.lastSyncedPositions.has(id)) {
|
|
56919
|
+
const pos = item.transformation.getTranslation();
|
|
56920
|
+
this.lastSyncedPositions.set(id, { x: pos.x, y: pos.y });
|
|
56921
|
+
}
|
|
56922
|
+
}
|
|
56923
|
+
this.ensureRunning();
|
|
56924
|
+
}
|
|
56925
|
+
disableForGraph(nodeId) {
|
|
56926
|
+
const compId = this.findComponentId(nodeId);
|
|
56927
|
+
if (!compId)
|
|
56928
|
+
return;
|
|
56929
|
+
this.activeComponents.delete(compId);
|
|
56930
|
+
if (this.activeComponents.size === 0) {
|
|
56931
|
+
this.stopTimers();
|
|
56932
|
+
}
|
|
56933
|
+
}
|
|
56934
|
+
isNodeInActiveGraph(nodeId) {
|
|
56935
|
+
return !!this.findComponentId(nodeId);
|
|
56936
|
+
}
|
|
56937
|
+
getComponentTargetGap(nodeId) {
|
|
56938
|
+
const compId = this.findComponentId(nodeId);
|
|
56939
|
+
return compId ? this.activeComponents.get(compId)?.targetGap : undefined;
|
|
56940
|
+
}
|
|
56941
|
+
setComponentTargetGap(nodeId, gap) {
|
|
56942
|
+
const compId = this.findComponentId(nodeId);
|
|
56943
|
+
if (!compId)
|
|
56944
|
+
return;
|
|
56945
|
+
const comp = this.activeComponents.get(compId);
|
|
56946
|
+
if (!comp)
|
|
56947
|
+
return;
|
|
56948
|
+
comp.targetGap = gap;
|
|
56949
|
+
this.wake();
|
|
56950
|
+
}
|
|
56951
|
+
hasActiveComponents() {
|
|
56952
|
+
return this.activeComponents.size > 0;
|
|
56953
|
+
}
|
|
56954
|
+
wake() {
|
|
56955
|
+
if (this.activeComponents.size > 0 && this.tickTimer === null) {
|
|
56956
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
56913
56957
|
}
|
|
56914
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
56915
|
-
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
56916
56958
|
}
|
|
56917
56959
|
stop() {
|
|
56960
|
+
this.stopTimers();
|
|
56961
|
+
this.syncPositions();
|
|
56962
|
+
this.velocities.clear();
|
|
56963
|
+
this.lastSyncedPositions.clear();
|
|
56964
|
+
this.activeComponents.clear();
|
|
56965
|
+
}
|
|
56966
|
+
ensureRunning() {
|
|
56967
|
+
if (this.tickTimer === null) {
|
|
56968
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
56969
|
+
}
|
|
56970
|
+
if (this.syncTimer === null) {
|
|
56971
|
+
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
56972
|
+
}
|
|
56973
|
+
}
|
|
56974
|
+
stopTimers() {
|
|
56918
56975
|
if (this.tickTimer !== null) {
|
|
56919
56976
|
clearInterval(this.tickTimer);
|
|
56920
56977
|
this.tickTimer = null;
|
|
@@ -56923,9 +56980,56 @@ class ForceGraphEngine {
|
|
|
56923
56980
|
clearInterval(this.syncTimer);
|
|
56924
56981
|
this.syncTimer = null;
|
|
56925
56982
|
}
|
|
56926
|
-
|
|
56927
|
-
|
|
56928
|
-
this.
|
|
56983
|
+
}
|
|
56984
|
+
findComponentId(nodeId) {
|
|
56985
|
+
for (const [compId, { nodeIds }] of this.activeComponents) {
|
|
56986
|
+
if (nodeIds.has(nodeId))
|
|
56987
|
+
return compId;
|
|
56988
|
+
}
|
|
56989
|
+
return;
|
|
56990
|
+
}
|
|
56991
|
+
bfsComponent(startNodeId) {
|
|
56992
|
+
const visited = new Set;
|
|
56993
|
+
const queue = [startNodeId];
|
|
56994
|
+
const connectors = this.getConnectors();
|
|
56995
|
+
while (queue.length > 0) {
|
|
56996
|
+
const nodeId = queue.shift();
|
|
56997
|
+
if (visited.has(nodeId))
|
|
56998
|
+
continue;
|
|
56999
|
+
visited.add(nodeId);
|
|
57000
|
+
for (const connector of connectors) {
|
|
57001
|
+
const { startItem, endItem } = connector.getConnectedItems();
|
|
57002
|
+
if (startItem?.getId() === nodeId && endItem && !visited.has(endItem.getId())) {
|
|
57003
|
+
queue.push(endItem.getId());
|
|
57004
|
+
}
|
|
57005
|
+
if (endItem?.getId() === nodeId && startItem && !visited.has(startItem.getId())) {
|
|
57006
|
+
queue.push(startItem.getId());
|
|
57007
|
+
}
|
|
57008
|
+
}
|
|
57009
|
+
}
|
|
57010
|
+
return visited;
|
|
57011
|
+
}
|
|
57012
|
+
calibrateTargetGap(nodeIds) {
|
|
57013
|
+
let totalMaxDim = 0;
|
|
57014
|
+
let count = 0;
|
|
57015
|
+
for (const id of nodeIds) {
|
|
57016
|
+
const item = this.board.items.getById(id);
|
|
57017
|
+
if (!item)
|
|
57018
|
+
continue;
|
|
57019
|
+
const mbr = item.getMbr();
|
|
57020
|
+
totalMaxDim += Math.max(mbr.getWidth(), mbr.getHeight());
|
|
57021
|
+
count++;
|
|
57022
|
+
}
|
|
57023
|
+
const avgMaxDim = count > 0 ? totalMaxDim / count : 100;
|
|
57024
|
+
return avgMaxDim * 1.5;
|
|
57025
|
+
}
|
|
57026
|
+
getActiveNodeIds() {
|
|
57027
|
+
const all6 = new Set;
|
|
57028
|
+
for (const { nodeIds } of this.activeComponents.values()) {
|
|
57029
|
+
for (const id of nodeIds)
|
|
57030
|
+
all6.add(id);
|
|
57031
|
+
}
|
|
57032
|
+
return all6;
|
|
56929
57033
|
}
|
|
56930
57034
|
getNodes() {
|
|
56931
57035
|
return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
|
|
@@ -56935,8 +57039,10 @@ class ForceGraphEngine {
|
|
|
56935
57039
|
}
|
|
56936
57040
|
tick() {
|
|
56937
57041
|
const dt = this.TICK_MS / 1000;
|
|
56938
|
-
const
|
|
56939
|
-
|
|
57042
|
+
const activeIds = this.getActiveNodeIds();
|
|
57043
|
+
const allNodes = this.getNodes();
|
|
57044
|
+
const nodes = allNodes.filter((item) => activeIds.has(item.getId()));
|
|
57045
|
+
if (nodes.length < 1)
|
|
56940
57046
|
return;
|
|
56941
57047
|
const snapMap = new Map;
|
|
56942
57048
|
for (const item of nodes) {
|
|
@@ -56977,7 +57083,9 @@ class ForceGraphEngine {
|
|
|
56977
57083
|
const dx = s2.cx - s1.cx;
|
|
56978
57084
|
const dy = s2.cy - s1.cy;
|
|
56979
57085
|
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
56980
|
-
const
|
|
57086
|
+
const compId = this.findComponentId(s1.id);
|
|
57087
|
+
const targetGap = compId ? this.activeComponents.get(compId)?.targetGap ?? conf.FG_TARGET_GAP : conf.FG_TARGET_GAP;
|
|
57088
|
+
const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + targetGap;
|
|
56981
57089
|
const stretch = dist - targetDist;
|
|
56982
57090
|
const forceMag = stretch * conf.FG_SPRING_K;
|
|
56983
57091
|
const fx = dx / dist * forceMag;
|
|
@@ -57038,7 +57146,8 @@ class ForceGraphEngine {
|
|
|
57038
57146
|
}
|
|
57039
57147
|
}
|
|
57040
57148
|
syncPositions() {
|
|
57041
|
-
const
|
|
57149
|
+
const activeIds = this.getActiveNodeIds();
|
|
57150
|
+
const nodes = this.getNodes().filter((item) => activeIds.has(item.getId()));
|
|
57042
57151
|
if (nodes.length === 0)
|
|
57043
57152
|
return;
|
|
57044
57153
|
const movedItems = nodes.map((item) => {
|
|
@@ -57062,11 +57171,6 @@ class ForceGraphEngine {
|
|
|
57062
57171
|
};
|
|
57063
57172
|
this.board.events.emit(operation);
|
|
57064
57173
|
}
|
|
57065
|
-
wake() {
|
|
57066
|
-
if (this.tickTimer === null && this.syncTimer !== null) {
|
|
57067
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
57068
|
-
}
|
|
57069
|
-
}
|
|
57070
57174
|
}
|
|
57071
57175
|
|
|
57072
57176
|
// src/Board.ts
|
|
@@ -58142,20 +58246,28 @@ class Board {
|
|
|
58142
58246
|
return this.gravity !== null;
|
|
58143
58247
|
}
|
|
58144
58248
|
forceGraph = null;
|
|
58145
|
-
enableForceGraph() {
|
|
58146
|
-
if (this.forceGraph)
|
|
58147
|
-
|
|
58148
|
-
|
|
58149
|
-
this.forceGraph.
|
|
58249
|
+
enableForceGraph(nodeId) {
|
|
58250
|
+
if (!this.forceGraph) {
|
|
58251
|
+
this.forceGraph = new ForceGraphEngine(this);
|
|
58252
|
+
}
|
|
58253
|
+
this.forceGraph.enableForGraph(nodeId);
|
|
58150
58254
|
}
|
|
58151
|
-
disableForceGraph() {
|
|
58255
|
+
disableForceGraph(nodeId) {
|
|
58152
58256
|
if (!this.forceGraph)
|
|
58153
58257
|
return;
|
|
58154
|
-
this.forceGraph.
|
|
58155
|
-
this.forceGraph
|
|
58258
|
+
this.forceGraph.disableForGraph(nodeId);
|
|
58259
|
+
if (!this.forceGraph.hasActiveComponents()) {
|
|
58260
|
+
this.forceGraph = null;
|
|
58261
|
+
}
|
|
58262
|
+
}
|
|
58263
|
+
isNodeInForceGraph(nodeId) {
|
|
58264
|
+
return this.forceGraph?.isNodeInActiveGraph(nodeId) ?? false;
|
|
58265
|
+
}
|
|
58266
|
+
getForceGraphGap(nodeId) {
|
|
58267
|
+
return this.forceGraph?.getComponentTargetGap(nodeId);
|
|
58156
58268
|
}
|
|
58157
|
-
|
|
58158
|
-
|
|
58269
|
+
setForceGraphGap(nodeId, gap) {
|
|
58270
|
+
this.forceGraph?.setComponentTargetGap(nodeId, gap);
|
|
58159
58271
|
}
|
|
58160
58272
|
wakeForceGraph() {
|
|
58161
58273
|
this.forceGraph?.wake();
|
package/dist/types/Board.d.ts
CHANGED
|
@@ -140,9 +140,16 @@ export declare class Board {
|
|
|
140
140
|
disableGravity(): void;
|
|
141
141
|
isGravityEnabled(): boolean;
|
|
142
142
|
private forceGraph;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
/** Enable force-directed layout for the connected component containing `nodeId`. */
|
|
144
|
+
enableForceGraph(nodeId: string): void;
|
|
145
|
+
/** Disable graph mode for the component containing `nodeId`. */
|
|
146
|
+
disableForceGraph(nodeId: string): void;
|
|
147
|
+
/** Returns true if `nodeId` is currently in an active force-directed component. */
|
|
148
|
+
isNodeInForceGraph(nodeId: string): boolean;
|
|
149
|
+
/** Get the connector target gap (px) for the component containing `nodeId`. */
|
|
150
|
+
getForceGraphGap(nodeId: string): number | undefined;
|
|
151
|
+
/** Set the connector target gap (px) for the component containing `nodeId`. */
|
|
152
|
+
setForceGraphGap(nodeId: string, gap: number): void;
|
|
146
153
|
/** Call after dragging a node to re-wake the physics engine if it was sleeping. */
|
|
147
154
|
wakeForceGraph(): void;
|
|
148
155
|
}
|
|
@@ -5,17 +5,49 @@ export declare class ForceGraphEngine {
|
|
|
5
5
|
private tickTimer;
|
|
6
6
|
private syncTimer;
|
|
7
7
|
private lastSyncedPositions;
|
|
8
|
+
/** Active components: componentId → { nodeIds, targetGap }
|
|
9
|
+
* componentId is the nodeId that was passed to enableForGraph(). */
|
|
10
|
+
private activeComponents;
|
|
8
11
|
private readonly TICK_MS;
|
|
9
12
|
private readonly SYNC_MS;
|
|
10
13
|
private readonly SOFTENING_SQ;
|
|
11
14
|
private readonly MIN_MOVE_PX;
|
|
12
15
|
constructor(board: Board);
|
|
13
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Enable force-directed layout for the connected component containing `startNodeId`.
|
|
18
|
+
* BFS walks the connector graph to find all nodes in the component.
|
|
19
|
+
* `targetGap` is auto-calibrated from the average node size; callers may override via conf.
|
|
20
|
+
*/
|
|
21
|
+
enableForGraph(startNodeId: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Disable graph mode for the component containing `nodeId`.
|
|
24
|
+
* Stops the engine entirely if no components remain active.
|
|
25
|
+
*/
|
|
26
|
+
disableForGraph(nodeId: string): void;
|
|
27
|
+
isNodeInActiveGraph(nodeId: string): boolean;
|
|
28
|
+
/** Get the current target gap for the component containing `nodeId`. Returns undefined if not active. */
|
|
29
|
+
getComponentTargetGap(nodeId: string): number | undefined;
|
|
30
|
+
/** Update the target gap (connector length) for the component containing `nodeId` and re-wake. */
|
|
31
|
+
setComponentTargetGap(nodeId: string, gap: number): void;
|
|
32
|
+
hasActiveComponents(): boolean;
|
|
33
|
+
/** Re-wake physics after a node is manually dragged. */
|
|
34
|
+
wake(): void;
|
|
35
|
+
/** Full stop — called when Board destroys the engine. */
|
|
14
36
|
stop(): void;
|
|
37
|
+
private ensureRunning;
|
|
38
|
+
private stopTimers;
|
|
39
|
+
/** Find the componentId (Map key) for the component containing `nodeId`. */
|
|
40
|
+
private findComponentId;
|
|
41
|
+
/** BFS through connector graph starting from `startNodeId`. */
|
|
42
|
+
private bfsComponent;
|
|
43
|
+
/**
|
|
44
|
+
* Auto-calibrate spring target gap from the average max(w, h) of nodes in the component.
|
|
45
|
+
* Larger nodes → longer springs so items visually breathe.
|
|
46
|
+
*/
|
|
47
|
+
private calibrateTargetGap;
|
|
48
|
+
private getActiveNodeIds;
|
|
15
49
|
private getNodes;
|
|
16
50
|
private getConnectors;
|
|
17
51
|
private tick;
|
|
18
52
|
private syncPositions;
|
|
19
|
-
/** Re-wake the engine after user moves a node (dragging disturbs equilibrium). */
|
|
20
|
-
wake(): void;
|
|
21
53
|
}
|