microboard-temp 0.13.19 → 0.13.21
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 +129 -34
- package/dist/cjs/index.js +129 -34
- package/dist/cjs/node.js +129 -34
- package/dist/esm/browser.js +129 -34
- package/dist/esm/index.js +129 -34
- package/dist/esm/node.js +129 -34
- package/dist/types/Board.d.ts +6 -3
- package/dist/types/ForceGraph/ForceGraphEngine.d.ts +31 -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,60 @@ 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
|
+
hasActiveComponents() {
|
|
54470
|
+
return this.activeComponents.size > 0;
|
|
54471
|
+
}
|
|
54472
|
+
wake() {
|
|
54473
|
+
if (this.activeComponents.size > 0 && this.tickTimer === null) {
|
|
54474
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54445
54475
|
}
|
|
54446
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54447
|
-
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
54448
54476
|
}
|
|
54449
54477
|
stop() {
|
|
54478
|
+
this.stopTimers();
|
|
54479
|
+
this.syncPositions();
|
|
54480
|
+
this.velocities.clear();
|
|
54481
|
+
this.lastSyncedPositions.clear();
|
|
54482
|
+
this.activeComponents.clear();
|
|
54483
|
+
}
|
|
54484
|
+
ensureRunning() {
|
|
54485
|
+
if (this.tickTimer === null) {
|
|
54486
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54487
|
+
}
|
|
54488
|
+
if (this.syncTimer === null) {
|
|
54489
|
+
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
54490
|
+
}
|
|
54491
|
+
}
|
|
54492
|
+
stopTimers() {
|
|
54450
54493
|
if (this.tickTimer !== null) {
|
|
54451
54494
|
clearInterval(this.tickTimer);
|
|
54452
54495
|
this.tickTimer = null;
|
|
@@ -54455,9 +54498,56 @@ class ForceGraphEngine {
|
|
|
54455
54498
|
clearInterval(this.syncTimer);
|
|
54456
54499
|
this.syncTimer = null;
|
|
54457
54500
|
}
|
|
54458
|
-
|
|
54459
|
-
|
|
54460
|
-
this.
|
|
54501
|
+
}
|
|
54502
|
+
findComponentId(nodeId) {
|
|
54503
|
+
for (const [compId, { nodeIds }] of this.activeComponents) {
|
|
54504
|
+
if (nodeIds.has(nodeId))
|
|
54505
|
+
return compId;
|
|
54506
|
+
}
|
|
54507
|
+
return;
|
|
54508
|
+
}
|
|
54509
|
+
bfsComponent(startNodeId) {
|
|
54510
|
+
const visited = new Set;
|
|
54511
|
+
const queue = [startNodeId];
|
|
54512
|
+
const connectors = this.getConnectors();
|
|
54513
|
+
while (queue.length > 0) {
|
|
54514
|
+
const nodeId = queue.shift();
|
|
54515
|
+
if (visited.has(nodeId))
|
|
54516
|
+
continue;
|
|
54517
|
+
visited.add(nodeId);
|
|
54518
|
+
for (const connector of connectors) {
|
|
54519
|
+
const { startItem, endItem } = connector.getConnectedItems();
|
|
54520
|
+
if (startItem?.getId() === nodeId && endItem && !visited.has(endItem.getId())) {
|
|
54521
|
+
queue.push(endItem.getId());
|
|
54522
|
+
}
|
|
54523
|
+
if (endItem?.getId() === nodeId && startItem && !visited.has(startItem.getId())) {
|
|
54524
|
+
queue.push(startItem.getId());
|
|
54525
|
+
}
|
|
54526
|
+
}
|
|
54527
|
+
}
|
|
54528
|
+
return visited;
|
|
54529
|
+
}
|
|
54530
|
+
calibrateTargetGap(nodeIds) {
|
|
54531
|
+
let totalMaxDim = 0;
|
|
54532
|
+
let count = 0;
|
|
54533
|
+
for (const id of nodeIds) {
|
|
54534
|
+
const item = this.board.items.getById(id);
|
|
54535
|
+
if (!item)
|
|
54536
|
+
continue;
|
|
54537
|
+
const mbr = item.getMbr();
|
|
54538
|
+
totalMaxDim += Math.max(mbr.getWidth(), mbr.getHeight());
|
|
54539
|
+
count++;
|
|
54540
|
+
}
|
|
54541
|
+
const avgMaxDim = count > 0 ? totalMaxDim / count : 100;
|
|
54542
|
+
return avgMaxDim * 0.3 + conf.FG_TARGET_GAP;
|
|
54543
|
+
}
|
|
54544
|
+
getActiveNodeIds() {
|
|
54545
|
+
const all6 = new Set;
|
|
54546
|
+
for (const { nodeIds } of this.activeComponents.values()) {
|
|
54547
|
+
for (const id of nodeIds)
|
|
54548
|
+
all6.add(id);
|
|
54549
|
+
}
|
|
54550
|
+
return all6;
|
|
54461
54551
|
}
|
|
54462
54552
|
getNodes() {
|
|
54463
54553
|
return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
|
|
@@ -54467,8 +54557,10 @@ class ForceGraphEngine {
|
|
|
54467
54557
|
}
|
|
54468
54558
|
tick() {
|
|
54469
54559
|
const dt = this.TICK_MS / 1000;
|
|
54470
|
-
const
|
|
54471
|
-
|
|
54560
|
+
const activeIds = this.getActiveNodeIds();
|
|
54561
|
+
const allNodes = this.getNodes();
|
|
54562
|
+
const nodes = allNodes.filter((item) => activeIds.has(item.getId()));
|
|
54563
|
+
if (nodes.length < 1)
|
|
54472
54564
|
return;
|
|
54473
54565
|
const snapMap = new Map;
|
|
54474
54566
|
for (const item of nodes) {
|
|
@@ -54509,7 +54601,9 @@ class ForceGraphEngine {
|
|
|
54509
54601
|
const dx = s2.cx - s1.cx;
|
|
54510
54602
|
const dy = s2.cy - s1.cy;
|
|
54511
54603
|
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
54512
|
-
const
|
|
54604
|
+
const compId = this.findComponentId(s1.id);
|
|
54605
|
+
const targetGap = compId ? this.activeComponents.get(compId)?.targetGap ?? conf.FG_TARGET_GAP : conf.FG_TARGET_GAP;
|
|
54606
|
+
const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + targetGap;
|
|
54513
54607
|
const stretch = dist - targetDist;
|
|
54514
54608
|
const forceMag = stretch * conf.FG_SPRING_K;
|
|
54515
54609
|
const fx = dx / dist * forceMag;
|
|
@@ -54527,10 +54621,13 @@ class ForceGraphEngine {
|
|
|
54527
54621
|
continue;
|
|
54528
54622
|
const dx = s2.cx - s1.cx;
|
|
54529
54623
|
const dy = s2.cy - s1.cy;
|
|
54530
|
-
const
|
|
54531
|
-
const
|
|
54532
|
-
const
|
|
54533
|
-
const
|
|
54624
|
+
const centerDist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
54625
|
+
const r1 = Math.max(s1.w, s1.h) * 0.5;
|
|
54626
|
+
const r2 = Math.max(s2.w, s2.h) * 0.5;
|
|
54627
|
+
const edgeDist = Math.max(centerDist - r1 - r2, 1);
|
|
54628
|
+
const repMag = conf.FG_REPULSION / (edgeDist * edgeDist + this.SOFTENING_SQ);
|
|
54629
|
+
const fx = dx / centerDist * repMag;
|
|
54630
|
+
const fy = dy / centerDist * repMag;
|
|
54534
54631
|
ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
|
|
54535
54632
|
ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
|
|
54536
54633
|
ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
|
|
@@ -54567,7 +54664,8 @@ class ForceGraphEngine {
|
|
|
54567
54664
|
}
|
|
54568
54665
|
}
|
|
54569
54666
|
syncPositions() {
|
|
54570
|
-
const
|
|
54667
|
+
const activeIds = this.getActiveNodeIds();
|
|
54668
|
+
const nodes = this.getNodes().filter((item) => activeIds.has(item.getId()));
|
|
54571
54669
|
if (nodes.length === 0)
|
|
54572
54670
|
return;
|
|
54573
54671
|
const movedItems = nodes.map((item) => {
|
|
@@ -54591,11 +54689,6 @@ class ForceGraphEngine {
|
|
|
54591
54689
|
};
|
|
54592
54690
|
this.board.events.emit(operation);
|
|
54593
54691
|
}
|
|
54594
|
-
wake() {
|
|
54595
|
-
if (this.tickTimer === null && this.syncTimer !== null) {
|
|
54596
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
54597
|
-
}
|
|
54598
|
-
}
|
|
54599
54692
|
}
|
|
54600
54693
|
|
|
54601
54694
|
// src/Board.ts
|
|
@@ -55671,20 +55764,22 @@ class Board {
|
|
|
55671
55764
|
return this.gravity !== null;
|
|
55672
55765
|
}
|
|
55673
55766
|
forceGraph = null;
|
|
55674
|
-
enableForceGraph() {
|
|
55675
|
-
if (this.forceGraph)
|
|
55676
|
-
|
|
55677
|
-
|
|
55678
|
-
this.forceGraph.
|
|
55767
|
+
enableForceGraph(nodeId) {
|
|
55768
|
+
if (!this.forceGraph) {
|
|
55769
|
+
this.forceGraph = new ForceGraphEngine(this);
|
|
55770
|
+
}
|
|
55771
|
+
this.forceGraph.enableForGraph(nodeId);
|
|
55679
55772
|
}
|
|
55680
|
-
disableForceGraph() {
|
|
55773
|
+
disableForceGraph(nodeId) {
|
|
55681
55774
|
if (!this.forceGraph)
|
|
55682
55775
|
return;
|
|
55683
|
-
this.forceGraph.
|
|
55684
|
-
this.forceGraph
|
|
55776
|
+
this.forceGraph.disableForGraph(nodeId);
|
|
55777
|
+
if (!this.forceGraph.hasActiveComponents()) {
|
|
55778
|
+
this.forceGraph = null;
|
|
55779
|
+
}
|
|
55685
55780
|
}
|
|
55686
|
-
|
|
55687
|
-
return this.forceGraph
|
|
55781
|
+
isNodeInForceGraph(nodeId) {
|
|
55782
|
+
return this.forceGraph?.isNodeInActiveGraph(nodeId) ?? false;
|
|
55688
55783
|
}
|
|
55689
55784
|
wakeForceGraph() {
|
|
55690
55785
|
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,60 @@ 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
|
+
hasActiveComponents() {
|
|
56938
|
+
return this.activeComponents.size > 0;
|
|
56939
|
+
}
|
|
56940
|
+
wake() {
|
|
56941
|
+
if (this.activeComponents.size > 0 && this.tickTimer === null) {
|
|
56942
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
56913
56943
|
}
|
|
56914
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
56915
|
-
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
56916
56944
|
}
|
|
56917
56945
|
stop() {
|
|
56946
|
+
this.stopTimers();
|
|
56947
|
+
this.syncPositions();
|
|
56948
|
+
this.velocities.clear();
|
|
56949
|
+
this.lastSyncedPositions.clear();
|
|
56950
|
+
this.activeComponents.clear();
|
|
56951
|
+
}
|
|
56952
|
+
ensureRunning() {
|
|
56953
|
+
if (this.tickTimer === null) {
|
|
56954
|
+
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
56955
|
+
}
|
|
56956
|
+
if (this.syncTimer === null) {
|
|
56957
|
+
this.syncTimer = setInterval(() => this.syncPositions(), this.SYNC_MS);
|
|
56958
|
+
}
|
|
56959
|
+
}
|
|
56960
|
+
stopTimers() {
|
|
56918
56961
|
if (this.tickTimer !== null) {
|
|
56919
56962
|
clearInterval(this.tickTimer);
|
|
56920
56963
|
this.tickTimer = null;
|
|
@@ -56923,9 +56966,56 @@ class ForceGraphEngine {
|
|
|
56923
56966
|
clearInterval(this.syncTimer);
|
|
56924
56967
|
this.syncTimer = null;
|
|
56925
56968
|
}
|
|
56926
|
-
|
|
56927
|
-
|
|
56928
|
-
this.
|
|
56969
|
+
}
|
|
56970
|
+
findComponentId(nodeId) {
|
|
56971
|
+
for (const [compId, { nodeIds }] of this.activeComponents) {
|
|
56972
|
+
if (nodeIds.has(nodeId))
|
|
56973
|
+
return compId;
|
|
56974
|
+
}
|
|
56975
|
+
return;
|
|
56976
|
+
}
|
|
56977
|
+
bfsComponent(startNodeId) {
|
|
56978
|
+
const visited = new Set;
|
|
56979
|
+
const queue = [startNodeId];
|
|
56980
|
+
const connectors = this.getConnectors();
|
|
56981
|
+
while (queue.length > 0) {
|
|
56982
|
+
const nodeId = queue.shift();
|
|
56983
|
+
if (visited.has(nodeId))
|
|
56984
|
+
continue;
|
|
56985
|
+
visited.add(nodeId);
|
|
56986
|
+
for (const connector of connectors) {
|
|
56987
|
+
const { startItem, endItem } = connector.getConnectedItems();
|
|
56988
|
+
if (startItem?.getId() === nodeId && endItem && !visited.has(endItem.getId())) {
|
|
56989
|
+
queue.push(endItem.getId());
|
|
56990
|
+
}
|
|
56991
|
+
if (endItem?.getId() === nodeId && startItem && !visited.has(startItem.getId())) {
|
|
56992
|
+
queue.push(startItem.getId());
|
|
56993
|
+
}
|
|
56994
|
+
}
|
|
56995
|
+
}
|
|
56996
|
+
return visited;
|
|
56997
|
+
}
|
|
56998
|
+
calibrateTargetGap(nodeIds) {
|
|
56999
|
+
let totalMaxDim = 0;
|
|
57000
|
+
let count = 0;
|
|
57001
|
+
for (const id of nodeIds) {
|
|
57002
|
+
const item = this.board.items.getById(id);
|
|
57003
|
+
if (!item)
|
|
57004
|
+
continue;
|
|
57005
|
+
const mbr = item.getMbr();
|
|
57006
|
+
totalMaxDim += Math.max(mbr.getWidth(), mbr.getHeight());
|
|
57007
|
+
count++;
|
|
57008
|
+
}
|
|
57009
|
+
const avgMaxDim = count > 0 ? totalMaxDim / count : 100;
|
|
57010
|
+
return avgMaxDim * 0.3 + conf.FG_TARGET_GAP;
|
|
57011
|
+
}
|
|
57012
|
+
getActiveNodeIds() {
|
|
57013
|
+
const all6 = new Set;
|
|
57014
|
+
for (const { nodeIds } of this.activeComponents.values()) {
|
|
57015
|
+
for (const id of nodeIds)
|
|
57016
|
+
all6.add(id);
|
|
57017
|
+
}
|
|
57018
|
+
return all6;
|
|
56929
57019
|
}
|
|
56930
57020
|
getNodes() {
|
|
56931
57021
|
return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
|
|
@@ -56935,8 +57025,10 @@ class ForceGraphEngine {
|
|
|
56935
57025
|
}
|
|
56936
57026
|
tick() {
|
|
56937
57027
|
const dt = this.TICK_MS / 1000;
|
|
56938
|
-
const
|
|
56939
|
-
|
|
57028
|
+
const activeIds = this.getActiveNodeIds();
|
|
57029
|
+
const allNodes = this.getNodes();
|
|
57030
|
+
const nodes = allNodes.filter((item) => activeIds.has(item.getId()));
|
|
57031
|
+
if (nodes.length < 1)
|
|
56940
57032
|
return;
|
|
56941
57033
|
const snapMap = new Map;
|
|
56942
57034
|
for (const item of nodes) {
|
|
@@ -56977,7 +57069,9 @@ class ForceGraphEngine {
|
|
|
56977
57069
|
const dx = s2.cx - s1.cx;
|
|
56978
57070
|
const dy = s2.cy - s1.cy;
|
|
56979
57071
|
const dist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
56980
|
-
const
|
|
57072
|
+
const compId = this.findComponentId(s1.id);
|
|
57073
|
+
const targetGap = compId ? this.activeComponents.get(compId)?.targetGap ?? conf.FG_TARGET_GAP : conf.FG_TARGET_GAP;
|
|
57074
|
+
const targetDist = (Math.max(s1.w, s1.h) + Math.max(s2.w, s2.h)) * 0.5 + targetGap;
|
|
56981
57075
|
const stretch = dist - targetDist;
|
|
56982
57076
|
const forceMag = stretch * conf.FG_SPRING_K;
|
|
56983
57077
|
const fx = dx / dist * forceMag;
|
|
@@ -56995,10 +57089,13 @@ class ForceGraphEngine {
|
|
|
56995
57089
|
continue;
|
|
56996
57090
|
const dx = s2.cx - s1.cx;
|
|
56997
57091
|
const dy = s2.cy - s1.cy;
|
|
56998
|
-
const
|
|
56999
|
-
const
|
|
57000
|
-
const
|
|
57001
|
-
const
|
|
57092
|
+
const centerDist = Math.sqrt(dx * dx + dy * dy) + 0.001;
|
|
57093
|
+
const r1 = Math.max(s1.w, s1.h) * 0.5;
|
|
57094
|
+
const r2 = Math.max(s2.w, s2.h) * 0.5;
|
|
57095
|
+
const edgeDist = Math.max(centerDist - r1 - r2, 1);
|
|
57096
|
+
const repMag = conf.FG_REPULSION / (edgeDist * edgeDist + this.SOFTENING_SQ);
|
|
57097
|
+
const fx = dx / centerDist * repMag;
|
|
57098
|
+
const fy = dy / centerDist * repMag;
|
|
57002
57099
|
ax.set(s1.id, (ax.get(s1.id) ?? 0) - fx);
|
|
57003
57100
|
ay.set(s1.id, (ay.get(s1.id) ?? 0) - fy);
|
|
57004
57101
|
ax.set(s2.id, (ax.get(s2.id) ?? 0) + fx);
|
|
@@ -57035,7 +57132,8 @@ class ForceGraphEngine {
|
|
|
57035
57132
|
}
|
|
57036
57133
|
}
|
|
57037
57134
|
syncPositions() {
|
|
57038
|
-
const
|
|
57135
|
+
const activeIds = this.getActiveNodeIds();
|
|
57136
|
+
const nodes = this.getNodes().filter((item) => activeIds.has(item.getId()));
|
|
57039
57137
|
if (nodes.length === 0)
|
|
57040
57138
|
return;
|
|
57041
57139
|
const movedItems = nodes.map((item) => {
|
|
@@ -57059,11 +57157,6 @@ class ForceGraphEngine {
|
|
|
57059
57157
|
};
|
|
57060
57158
|
this.board.events.emit(operation);
|
|
57061
57159
|
}
|
|
57062
|
-
wake() {
|
|
57063
|
-
if (this.tickTimer === null && this.syncTimer !== null) {
|
|
57064
|
-
this.tickTimer = setInterval(() => this.tick(), this.TICK_MS);
|
|
57065
|
-
}
|
|
57066
|
-
}
|
|
57067
57160
|
}
|
|
57068
57161
|
|
|
57069
57162
|
// src/Board.ts
|
|
@@ -58139,20 +58232,22 @@ class Board {
|
|
|
58139
58232
|
return this.gravity !== null;
|
|
58140
58233
|
}
|
|
58141
58234
|
forceGraph = null;
|
|
58142
|
-
enableForceGraph() {
|
|
58143
|
-
if (this.forceGraph)
|
|
58144
|
-
|
|
58145
|
-
|
|
58146
|
-
this.forceGraph.
|
|
58235
|
+
enableForceGraph(nodeId) {
|
|
58236
|
+
if (!this.forceGraph) {
|
|
58237
|
+
this.forceGraph = new ForceGraphEngine(this);
|
|
58238
|
+
}
|
|
58239
|
+
this.forceGraph.enableForGraph(nodeId);
|
|
58147
58240
|
}
|
|
58148
|
-
disableForceGraph() {
|
|
58241
|
+
disableForceGraph(nodeId) {
|
|
58149
58242
|
if (!this.forceGraph)
|
|
58150
58243
|
return;
|
|
58151
|
-
this.forceGraph.
|
|
58152
|
-
this.forceGraph
|
|
58244
|
+
this.forceGraph.disableForGraph(nodeId);
|
|
58245
|
+
if (!this.forceGraph.hasActiveComponents()) {
|
|
58246
|
+
this.forceGraph = null;
|
|
58247
|
+
}
|
|
58153
58248
|
}
|
|
58154
|
-
|
|
58155
|
-
return this.forceGraph
|
|
58249
|
+
isNodeInForceGraph(nodeId) {
|
|
58250
|
+
return this.forceGraph?.isNodeInActiveGraph(nodeId) ?? false;
|
|
58156
58251
|
}
|
|
58157
58252
|
wakeForceGraph() {
|
|
58158
58253
|
this.forceGraph?.wake();
|
package/dist/types/Board.d.ts
CHANGED
|
@@ -140,9 +140,12 @@ 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;
|
|
146
149
|
/** Call after dragging a node to re-wake the physics engine if it was sleeping. */
|
|
147
150
|
wakeForceGraph(): void;
|
|
148
151
|
}
|
|
@@ -5,17 +5,45 @@ 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
|
+
hasActiveComponents(): boolean;
|
|
29
|
+
/** Re-wake physics after a node is manually dragged. */
|
|
30
|
+
wake(): void;
|
|
31
|
+
/** Full stop — called when Board destroys the engine. */
|
|
14
32
|
stop(): void;
|
|
33
|
+
private ensureRunning;
|
|
34
|
+
private stopTimers;
|
|
35
|
+
/** Find the componentId (Map key) for the component containing `nodeId`. */
|
|
36
|
+
private findComponentId;
|
|
37
|
+
/** BFS through connector graph starting from `startNodeId`. */
|
|
38
|
+
private bfsComponent;
|
|
39
|
+
/**
|
|
40
|
+
* Auto-calibrate spring target gap from the average max(w, h) of nodes in the component.
|
|
41
|
+
* Larger nodes → longer springs so items visually breathe.
|
|
42
|
+
*/
|
|
43
|
+
private calibrateTargetGap;
|
|
44
|
+
private getActiveNodeIds;
|
|
15
45
|
private getNodes;
|
|
16
46
|
private getConnectors;
|
|
17
47
|
private tick;
|
|
18
48
|
private syncPositions;
|
|
19
|
-
/** Re-wake the engine after user moves a node (dragging disturbs equilibrium). */
|
|
20
|
-
wake(): void;
|
|
21
49
|
}
|