microboard-temp 0.13.53 → 0.13.55

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.
@@ -13028,6 +13028,15 @@ var init_BaseItem = __esm(() => {
13028
13028
  childIds = [];
13029
13029
  isHoverHighlighted = false;
13030
13030
  static HOVER_HIGHLIGHT_COLOR = "rgba(71, 120, 245, 0.7)";
13031
+ _physicsHalfExtent = -1;
13032
+ get physicsHalfExtent() {
13033
+ if (this._physicsHalfExtent < 0) {
13034
+ const w = Math.max(this.right - this.left, 1);
13035
+ const h = Math.max(this.bottom - this.top, 1);
13036
+ this._physicsHalfExtent = Math.max(w, h) * 0.5;
13037
+ }
13038
+ return this._physicsHalfExtent;
13039
+ }
13031
13040
  constructor(board, id = "", defaultItemData, isGroupItem) {
13032
13041
  super();
13033
13042
  this.defaultItemData = defaultItemData;
@@ -13234,7 +13243,7 @@ var init_BaseItem = __esm(() => {
13234
13243
  this.subject.publish(this);
13235
13244
  }
13236
13245
  updateMbr() {
13237
- return;
13246
+ this._physicsHalfExtent = -1;
13238
13247
  }
13239
13248
  getLinkTo() {
13240
13249
  return this.linkTo.link;
@@ -13345,6 +13354,16 @@ var init_BaseItem = __esm(() => {
13345
13354
  }
13346
13355
  }
13347
13356
  this.transformation.apply(transformOp);
13357
+ const m = transformOp.method;
13358
+ if (m !== "translateTo" && m !== "translateBy" && m !== "rotateTo" && m !== "rotateBy" && m !== "locked" && m !== "unlocked") {
13359
+ if (m === "applyMatrix") {
13360
+ if (transformOp.items.some((i) => i.matrix.scaleX !== 1 || i.matrix.scaleY !== 1)) {
13361
+ this._physicsHalfExtent = -1;
13362
+ }
13363
+ } else {
13364
+ this._physicsHalfExtent = -1;
13365
+ }
13366
+ }
13348
13367
  break;
13349
13368
  }
13350
13369
  case "LinkTo":
@@ -60684,6 +60703,10 @@ class ForceGraphEngine {
60684
60703
  lastSyncedPositions = new Map;
60685
60704
  activeComponents = new Map;
60686
60705
  isPhysicsEmit = false;
60706
+ _cachedConnectors = null;
60707
+ _cachedNodes = null;
60708
+ _activeNodeIdsCache = null;
60709
+ _topologyDirty = true;
60687
60710
  get TICK_MS() {
60688
60711
  return conf.FG_TICK_MS;
60689
60712
  }
@@ -60695,7 +60718,15 @@ class ForceGraphEngine {
60695
60718
  if (this.isPhysicsEmit)
60696
60719
  return;
60697
60720
  const op = event.body?.operation;
60698
- if (!op || op.class !== "Transformation" || op.method !== "applyMatrix")
60721
+ if (!op)
60722
+ return;
60723
+ if (op.class === "Board") {
60724
+ this._cachedConnectors = null;
60725
+ this._cachedNodes = null;
60726
+ this._topologyDirty = true;
60727
+ return;
60728
+ }
60729
+ if (op.class !== "Transformation" || op.method !== "applyMatrix")
60699
60730
  return;
60700
60731
  for (const { id, matrix } of op.items) {
60701
60732
  const last = this.lastSyncedPositions.get(id);
@@ -60723,6 +60754,7 @@ class ForceGraphEngine {
60723
60754
  }
60724
60755
  const targetGap = this.calibrateTargetGap(nodeIds);
60725
60756
  this.activeComponents.set(startNodeId, { nodeIds, targetGap });
60757
+ this._activeNodeIdsCache = null;
60726
60758
  this.initNodes(nodeIds);
60727
60759
  this.ensureRunning();
60728
60760
  }
@@ -60731,6 +60763,7 @@ class ForceGraphEngine {
60731
60763
  if (!compId)
60732
60764
  return;
60733
60765
  this.activeComponents.delete(compId);
60766
+ this._activeNodeIdsCache = null;
60734
60767
  if (this.activeComponents.size === 0) {
60735
60768
  this.stopTimers();
60736
60769
  }
@@ -60792,6 +60825,9 @@ class ForceGraphEngine {
60792
60825
  }
60793
60826
  }
60794
60827
  refreshComponentTopology() {
60828
+ if (!this._topologyDirty)
60829
+ return;
60830
+ this._topologyDirty = false;
60795
60831
  for (const [compId, comp] of this.activeComponents) {
60796
60832
  const current = this.bfsComponent(compId);
60797
60833
  const newIds = new Set;
@@ -60801,6 +60837,7 @@ class ForceGraphEngine {
60801
60837
  }
60802
60838
  if (newIds.size > 0) {
60803
60839
  this.initNodes(newIds, comp.nodeIds);
60840
+ this._activeNodeIdsCache = null;
60804
60841
  }
60805
60842
  }
60806
60843
  }
@@ -60854,18 +60891,27 @@ class ForceGraphEngine {
60854
60891
  return conf.FG_TARGET_GAP;
60855
60892
  }
60856
60893
  getActiveNodeIds() {
60857
- const all6 = new Set;
60858
- for (const { nodeIds } of this.activeComponents.values()) {
60859
- for (const id of nodeIds)
60860
- all6.add(id);
60894
+ if (!this._activeNodeIdsCache) {
60895
+ const all6 = new Set;
60896
+ for (const { nodeIds } of this.activeComponents.values()) {
60897
+ for (const id of nodeIds)
60898
+ all6.add(id);
60899
+ }
60900
+ this._activeNodeIdsCache = all6;
60861
60901
  }
60862
- return all6;
60902
+ return this._activeNodeIdsCache;
60863
60903
  }
60864
60904
  getNodes() {
60865
- return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60905
+ if (!this._cachedNodes) {
60906
+ this._cachedNodes = this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60907
+ }
60908
+ return this._cachedNodes;
60866
60909
  }
60867
60910
  getConnectors() {
60868
- return this.board.items.listAll().filter((item) => item.itemType === "Connector");
60911
+ if (!this._cachedConnectors) {
60912
+ this._cachedConnectors = this.board.items.listAll().filter((item) => item.itemType === "Connector");
60913
+ }
60914
+ return this._cachedConnectors;
60869
60915
  }
60870
60916
  tick() {
60871
60917
  this.refreshComponentTopology();
@@ -60877,10 +60923,10 @@ class ForceGraphEngine {
60877
60923
  if (!activeIds.has(item.getId()))
60878
60924
  continue;
60879
60925
  const pos = item.transformation.getTranslation();
60880
- const mbr = item.getMbr();
60881
- const w = Math.max(mbr.getWidth(), 1);
60882
- const h2 = Math.max(mbr.getHeight(), 1);
60883
- snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + w * 0.5, cy: pos.y + h2 * 0.5, w, h: h2 });
60926
+ const half = item.physicsHalfExtent;
60927
+ const w = half * 2;
60928
+ const h2 = half * 2;
60929
+ snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + half, cy: pos.y + half, w, h: h2 });
60884
60930
  }
60885
60931
  const snap = Array.from(snapMap.values());
60886
60932
  if (snap.length < 1)
@@ -60927,8 +60973,12 @@ class ForceGraphEngine {
60927
60973
  continue;
60928
60974
  const dx = s2.cx - s1.cx;
60929
60975
  const dy = s2.cy - s1.cy;
60930
- const distSq = Math.max(dx * dx + dy * dy, conf.FG_MIN_DIST_SQ);
60931
- const force = conf.FG_REPULSION / distSq;
60976
+ const centerDist = Math.sqrt(dx * dx + dy * dy) || 1;
60977
+ const halfA = Math.max(s1.w, s1.h) * 0.5;
60978
+ const halfB = Math.max(s2.w, s2.h) * 0.5;
60979
+ const edgeDist = Math.max(centerDist - halfA - halfB, 0);
60980
+ const edgeDistSq = Math.max(edgeDist * edgeDist, conf.FG_MIN_DIST_SQ);
60981
+ const force = conf.FG_REPULSION / edgeDistSq;
60932
60982
  ax.set(s1.id, (ax.get(s1.id) ?? 0) - dx * force);
60933
60983
  ay.set(s1.id, (ay.get(s1.id) ?? 0) - dy * force);
60934
60984
  ax.set(s2.id, (ax.get(s2.id) ?? 0) + dx * force);
package/dist/cjs/index.js CHANGED
@@ -13028,6 +13028,15 @@ var init_BaseItem = __esm(() => {
13028
13028
  childIds = [];
13029
13029
  isHoverHighlighted = false;
13030
13030
  static HOVER_HIGHLIGHT_COLOR = "rgba(71, 120, 245, 0.7)";
13031
+ _physicsHalfExtent = -1;
13032
+ get physicsHalfExtent() {
13033
+ if (this._physicsHalfExtent < 0) {
13034
+ const w = Math.max(this.right - this.left, 1);
13035
+ const h = Math.max(this.bottom - this.top, 1);
13036
+ this._physicsHalfExtent = Math.max(w, h) * 0.5;
13037
+ }
13038
+ return this._physicsHalfExtent;
13039
+ }
13031
13040
  constructor(board, id = "", defaultItemData, isGroupItem) {
13032
13041
  super();
13033
13042
  this.defaultItemData = defaultItemData;
@@ -13234,7 +13243,7 @@ var init_BaseItem = __esm(() => {
13234
13243
  this.subject.publish(this);
13235
13244
  }
13236
13245
  updateMbr() {
13237
- return;
13246
+ this._physicsHalfExtent = -1;
13238
13247
  }
13239
13248
  getLinkTo() {
13240
13249
  return this.linkTo.link;
@@ -13345,6 +13354,16 @@ var init_BaseItem = __esm(() => {
13345
13354
  }
13346
13355
  }
13347
13356
  this.transformation.apply(transformOp);
13357
+ const m = transformOp.method;
13358
+ if (m !== "translateTo" && m !== "translateBy" && m !== "rotateTo" && m !== "rotateBy" && m !== "locked" && m !== "unlocked") {
13359
+ if (m === "applyMatrix") {
13360
+ if (transformOp.items.some((i) => i.matrix.scaleX !== 1 || i.matrix.scaleY !== 1)) {
13361
+ this._physicsHalfExtent = -1;
13362
+ }
13363
+ } else {
13364
+ this._physicsHalfExtent = -1;
13365
+ }
13366
+ }
13348
13367
  break;
13349
13368
  }
13350
13369
  case "LinkTo":
@@ -60684,6 +60703,10 @@ class ForceGraphEngine {
60684
60703
  lastSyncedPositions = new Map;
60685
60704
  activeComponents = new Map;
60686
60705
  isPhysicsEmit = false;
60706
+ _cachedConnectors = null;
60707
+ _cachedNodes = null;
60708
+ _activeNodeIdsCache = null;
60709
+ _topologyDirty = true;
60687
60710
  get TICK_MS() {
60688
60711
  return conf.FG_TICK_MS;
60689
60712
  }
@@ -60695,7 +60718,15 @@ class ForceGraphEngine {
60695
60718
  if (this.isPhysicsEmit)
60696
60719
  return;
60697
60720
  const op = event.body?.operation;
60698
- if (!op || op.class !== "Transformation" || op.method !== "applyMatrix")
60721
+ if (!op)
60722
+ return;
60723
+ if (op.class === "Board") {
60724
+ this._cachedConnectors = null;
60725
+ this._cachedNodes = null;
60726
+ this._topologyDirty = true;
60727
+ return;
60728
+ }
60729
+ if (op.class !== "Transformation" || op.method !== "applyMatrix")
60699
60730
  return;
60700
60731
  for (const { id, matrix } of op.items) {
60701
60732
  const last = this.lastSyncedPositions.get(id);
@@ -60723,6 +60754,7 @@ class ForceGraphEngine {
60723
60754
  }
60724
60755
  const targetGap = this.calibrateTargetGap(nodeIds);
60725
60756
  this.activeComponents.set(startNodeId, { nodeIds, targetGap });
60757
+ this._activeNodeIdsCache = null;
60726
60758
  this.initNodes(nodeIds);
60727
60759
  this.ensureRunning();
60728
60760
  }
@@ -60731,6 +60763,7 @@ class ForceGraphEngine {
60731
60763
  if (!compId)
60732
60764
  return;
60733
60765
  this.activeComponents.delete(compId);
60766
+ this._activeNodeIdsCache = null;
60734
60767
  if (this.activeComponents.size === 0) {
60735
60768
  this.stopTimers();
60736
60769
  }
@@ -60792,6 +60825,9 @@ class ForceGraphEngine {
60792
60825
  }
60793
60826
  }
60794
60827
  refreshComponentTopology() {
60828
+ if (!this._topologyDirty)
60829
+ return;
60830
+ this._topologyDirty = false;
60795
60831
  for (const [compId, comp] of this.activeComponents) {
60796
60832
  const current = this.bfsComponent(compId);
60797
60833
  const newIds = new Set;
@@ -60801,6 +60837,7 @@ class ForceGraphEngine {
60801
60837
  }
60802
60838
  if (newIds.size > 0) {
60803
60839
  this.initNodes(newIds, comp.nodeIds);
60840
+ this._activeNodeIdsCache = null;
60804
60841
  }
60805
60842
  }
60806
60843
  }
@@ -60854,18 +60891,27 @@ class ForceGraphEngine {
60854
60891
  return conf.FG_TARGET_GAP;
60855
60892
  }
60856
60893
  getActiveNodeIds() {
60857
- const all6 = new Set;
60858
- for (const { nodeIds } of this.activeComponents.values()) {
60859
- for (const id of nodeIds)
60860
- all6.add(id);
60894
+ if (!this._activeNodeIdsCache) {
60895
+ const all6 = new Set;
60896
+ for (const { nodeIds } of this.activeComponents.values()) {
60897
+ for (const id of nodeIds)
60898
+ all6.add(id);
60899
+ }
60900
+ this._activeNodeIdsCache = all6;
60861
60901
  }
60862
- return all6;
60902
+ return this._activeNodeIdsCache;
60863
60903
  }
60864
60904
  getNodes() {
60865
- return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60905
+ if (!this._cachedNodes) {
60906
+ this._cachedNodes = this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60907
+ }
60908
+ return this._cachedNodes;
60866
60909
  }
60867
60910
  getConnectors() {
60868
- return this.board.items.listAll().filter((item) => item.itemType === "Connector");
60911
+ if (!this._cachedConnectors) {
60912
+ this._cachedConnectors = this.board.items.listAll().filter((item) => item.itemType === "Connector");
60913
+ }
60914
+ return this._cachedConnectors;
60869
60915
  }
60870
60916
  tick() {
60871
60917
  this.refreshComponentTopology();
@@ -60877,10 +60923,10 @@ class ForceGraphEngine {
60877
60923
  if (!activeIds.has(item.getId()))
60878
60924
  continue;
60879
60925
  const pos = item.transformation.getTranslation();
60880
- const mbr = item.getMbr();
60881
- const w = Math.max(mbr.getWidth(), 1);
60882
- const h2 = Math.max(mbr.getHeight(), 1);
60883
- snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + w * 0.5, cy: pos.y + h2 * 0.5, w, h: h2 });
60926
+ const half = item.physicsHalfExtent;
60927
+ const w = half * 2;
60928
+ const h2 = half * 2;
60929
+ snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + half, cy: pos.y + half, w, h: h2 });
60884
60930
  }
60885
60931
  const snap = Array.from(snapMap.values());
60886
60932
  if (snap.length < 1)
@@ -60927,8 +60973,12 @@ class ForceGraphEngine {
60927
60973
  continue;
60928
60974
  const dx = s2.cx - s1.cx;
60929
60975
  const dy = s2.cy - s1.cy;
60930
- const distSq = Math.max(dx * dx + dy * dy, conf.FG_MIN_DIST_SQ);
60931
- const force = conf.FG_REPULSION / distSq;
60976
+ const centerDist = Math.sqrt(dx * dx + dy * dy) || 1;
60977
+ const halfA = Math.max(s1.w, s1.h) * 0.5;
60978
+ const halfB = Math.max(s2.w, s2.h) * 0.5;
60979
+ const edgeDist = Math.max(centerDist - halfA - halfB, 0);
60980
+ const edgeDistSq = Math.max(edgeDist * edgeDist, conf.FG_MIN_DIST_SQ);
60981
+ const force = conf.FG_REPULSION / edgeDistSq;
60932
60982
  ax.set(s1.id, (ax.get(s1.id) ?? 0) - dx * force);
60933
60983
  ay.set(s1.id, (ay.get(s1.id) ?? 0) - dy * force);
60934
60984
  ax.set(s2.id, (ax.get(s2.id) ?? 0) + dx * force);
package/dist/cjs/node.js CHANGED
@@ -13048,6 +13048,15 @@ var init_BaseItem = __esm(() => {
13048
13048
  childIds = [];
13049
13049
  isHoverHighlighted = false;
13050
13050
  static HOVER_HIGHLIGHT_COLOR = "rgba(71, 120, 245, 0.7)";
13051
+ _physicsHalfExtent = -1;
13052
+ get physicsHalfExtent() {
13053
+ if (this._physicsHalfExtent < 0) {
13054
+ const w = Math.max(this.right - this.left, 1);
13055
+ const h = Math.max(this.bottom - this.top, 1);
13056
+ this._physicsHalfExtent = Math.max(w, h) * 0.5;
13057
+ }
13058
+ return this._physicsHalfExtent;
13059
+ }
13051
13060
  constructor(board, id = "", defaultItemData, isGroupItem) {
13052
13061
  super();
13053
13062
  this.defaultItemData = defaultItemData;
@@ -13254,7 +13263,7 @@ var init_BaseItem = __esm(() => {
13254
13263
  this.subject.publish(this);
13255
13264
  }
13256
13265
  updateMbr() {
13257
- return;
13266
+ this._physicsHalfExtent = -1;
13258
13267
  }
13259
13268
  getLinkTo() {
13260
13269
  return this.linkTo.link;
@@ -13365,6 +13374,16 @@ var init_BaseItem = __esm(() => {
13365
13374
  }
13366
13375
  }
13367
13376
  this.transformation.apply(transformOp);
13377
+ const m = transformOp.method;
13378
+ if (m !== "translateTo" && m !== "translateBy" && m !== "rotateTo" && m !== "rotateBy" && m !== "locked" && m !== "unlocked") {
13379
+ if (m === "applyMatrix") {
13380
+ if (transformOp.items.some((i) => i.matrix.scaleX !== 1 || i.matrix.scaleY !== 1)) {
13381
+ this._physicsHalfExtent = -1;
13382
+ }
13383
+ } else {
13384
+ this._physicsHalfExtent = -1;
13385
+ }
13386
+ }
13368
13387
  break;
13369
13388
  }
13370
13389
  case "LinkTo":
@@ -63159,6 +63178,10 @@ class ForceGraphEngine {
63159
63178
  lastSyncedPositions = new Map;
63160
63179
  activeComponents = new Map;
63161
63180
  isPhysicsEmit = false;
63181
+ _cachedConnectors = null;
63182
+ _cachedNodes = null;
63183
+ _activeNodeIdsCache = null;
63184
+ _topologyDirty = true;
63162
63185
  get TICK_MS() {
63163
63186
  return conf.FG_TICK_MS;
63164
63187
  }
@@ -63170,7 +63193,15 @@ class ForceGraphEngine {
63170
63193
  if (this.isPhysicsEmit)
63171
63194
  return;
63172
63195
  const op = event.body?.operation;
63173
- if (!op || op.class !== "Transformation" || op.method !== "applyMatrix")
63196
+ if (!op)
63197
+ return;
63198
+ if (op.class === "Board") {
63199
+ this._cachedConnectors = null;
63200
+ this._cachedNodes = null;
63201
+ this._topologyDirty = true;
63202
+ return;
63203
+ }
63204
+ if (op.class !== "Transformation" || op.method !== "applyMatrix")
63174
63205
  return;
63175
63206
  for (const { id, matrix } of op.items) {
63176
63207
  const last = this.lastSyncedPositions.get(id);
@@ -63198,6 +63229,7 @@ class ForceGraphEngine {
63198
63229
  }
63199
63230
  const targetGap = this.calibrateTargetGap(nodeIds);
63200
63231
  this.activeComponents.set(startNodeId, { nodeIds, targetGap });
63232
+ this._activeNodeIdsCache = null;
63201
63233
  this.initNodes(nodeIds);
63202
63234
  this.ensureRunning();
63203
63235
  }
@@ -63206,6 +63238,7 @@ class ForceGraphEngine {
63206
63238
  if (!compId)
63207
63239
  return;
63208
63240
  this.activeComponents.delete(compId);
63241
+ this._activeNodeIdsCache = null;
63209
63242
  if (this.activeComponents.size === 0) {
63210
63243
  this.stopTimers();
63211
63244
  }
@@ -63267,6 +63300,9 @@ class ForceGraphEngine {
63267
63300
  }
63268
63301
  }
63269
63302
  refreshComponentTopology() {
63303
+ if (!this._topologyDirty)
63304
+ return;
63305
+ this._topologyDirty = false;
63270
63306
  for (const [compId, comp] of this.activeComponents) {
63271
63307
  const current = this.bfsComponent(compId);
63272
63308
  const newIds = new Set;
@@ -63276,6 +63312,7 @@ class ForceGraphEngine {
63276
63312
  }
63277
63313
  if (newIds.size > 0) {
63278
63314
  this.initNodes(newIds, comp.nodeIds);
63315
+ this._activeNodeIdsCache = null;
63279
63316
  }
63280
63317
  }
63281
63318
  }
@@ -63329,18 +63366,27 @@ class ForceGraphEngine {
63329
63366
  return conf.FG_TARGET_GAP;
63330
63367
  }
63331
63368
  getActiveNodeIds() {
63332
- const all6 = new Set;
63333
- for (const { nodeIds } of this.activeComponents.values()) {
63334
- for (const id of nodeIds)
63335
- all6.add(id);
63369
+ if (!this._activeNodeIdsCache) {
63370
+ const all6 = new Set;
63371
+ for (const { nodeIds } of this.activeComponents.values()) {
63372
+ for (const id of nodeIds)
63373
+ all6.add(id);
63374
+ }
63375
+ this._activeNodeIdsCache = all6;
63336
63376
  }
63337
- return all6;
63377
+ return this._activeNodeIdsCache;
63338
63378
  }
63339
63379
  getNodes() {
63340
- return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
63380
+ if (!this._cachedNodes) {
63381
+ this._cachedNodes = this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
63382
+ }
63383
+ return this._cachedNodes;
63341
63384
  }
63342
63385
  getConnectors() {
63343
- return this.board.items.listAll().filter((item) => item.itemType === "Connector");
63386
+ if (!this._cachedConnectors) {
63387
+ this._cachedConnectors = this.board.items.listAll().filter((item) => item.itemType === "Connector");
63388
+ }
63389
+ return this._cachedConnectors;
63344
63390
  }
63345
63391
  tick() {
63346
63392
  this.refreshComponentTopology();
@@ -63352,10 +63398,10 @@ class ForceGraphEngine {
63352
63398
  if (!activeIds.has(item.getId()))
63353
63399
  continue;
63354
63400
  const pos = item.transformation.getTranslation();
63355
- const mbr = item.getMbr();
63356
- const w = Math.max(mbr.getWidth(), 1);
63357
- const h2 = Math.max(mbr.getHeight(), 1);
63358
- snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + w * 0.5, cy: pos.y + h2 * 0.5, w, h: h2 });
63401
+ const half = item.physicsHalfExtent;
63402
+ const w = half * 2;
63403
+ const h2 = half * 2;
63404
+ snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + half, cy: pos.y + half, w, h: h2 });
63359
63405
  }
63360
63406
  const snap = Array.from(snapMap.values());
63361
63407
  if (snap.length < 1)
@@ -63402,8 +63448,12 @@ class ForceGraphEngine {
63402
63448
  continue;
63403
63449
  const dx = s2.cx - s1.cx;
63404
63450
  const dy = s2.cy - s1.cy;
63405
- const distSq = Math.max(dx * dx + dy * dy, conf.FG_MIN_DIST_SQ);
63406
- const force = conf.FG_REPULSION / distSq;
63451
+ const centerDist = Math.sqrt(dx * dx + dy * dy) || 1;
63452
+ const halfA = Math.max(s1.w, s1.h) * 0.5;
63453
+ const halfB = Math.max(s2.w, s2.h) * 0.5;
63454
+ const edgeDist = Math.max(centerDist - halfA - halfB, 0);
63455
+ const edgeDistSq = Math.max(edgeDist * edgeDist, conf.FG_MIN_DIST_SQ);
63456
+ const force = conf.FG_REPULSION / edgeDistSq;
63407
63457
  ax.set(s1.id, (ax.get(s1.id) ?? 0) - dx * force);
63408
63458
  ay.set(s1.id, (ay.get(s1.id) ?? 0) - dy * force);
63409
63459
  ax.set(s2.id, (ax.get(s2.id) ?? 0) + dx * force);
@@ -13000,6 +13000,15 @@ var init_BaseItem = __esm(() => {
13000
13000
  childIds = [];
13001
13001
  isHoverHighlighted = false;
13002
13002
  static HOVER_HIGHLIGHT_COLOR = "rgba(71, 120, 245, 0.7)";
13003
+ _physicsHalfExtent = -1;
13004
+ get physicsHalfExtent() {
13005
+ if (this._physicsHalfExtent < 0) {
13006
+ const w = Math.max(this.right - this.left, 1);
13007
+ const h = Math.max(this.bottom - this.top, 1);
13008
+ this._physicsHalfExtent = Math.max(w, h) * 0.5;
13009
+ }
13010
+ return this._physicsHalfExtent;
13011
+ }
13003
13012
  constructor(board, id = "", defaultItemData, isGroupItem) {
13004
13013
  super();
13005
13014
  this.defaultItemData = defaultItemData;
@@ -13206,7 +13215,7 @@ var init_BaseItem = __esm(() => {
13206
13215
  this.subject.publish(this);
13207
13216
  }
13208
13217
  updateMbr() {
13209
- return;
13218
+ this._physicsHalfExtent = -1;
13210
13219
  }
13211
13220
  getLinkTo() {
13212
13221
  return this.linkTo.link;
@@ -13317,6 +13326,16 @@ var init_BaseItem = __esm(() => {
13317
13326
  }
13318
13327
  }
13319
13328
  this.transformation.apply(transformOp);
13329
+ const m = transformOp.method;
13330
+ if (m !== "translateTo" && m !== "translateBy" && m !== "rotateTo" && m !== "rotateBy" && m !== "locked" && m !== "unlocked") {
13331
+ if (m === "applyMatrix") {
13332
+ if (transformOp.items.some((i) => i.matrix.scaleX !== 1 || i.matrix.scaleY !== 1)) {
13333
+ this._physicsHalfExtent = -1;
13334
+ }
13335
+ } else {
13336
+ this._physicsHalfExtent = -1;
13337
+ }
13338
+ }
13320
13339
  break;
13321
13340
  }
13322
13341
  case "LinkTo":
@@ -60427,6 +60446,10 @@ class ForceGraphEngine {
60427
60446
  lastSyncedPositions = new Map;
60428
60447
  activeComponents = new Map;
60429
60448
  isPhysicsEmit = false;
60449
+ _cachedConnectors = null;
60450
+ _cachedNodes = null;
60451
+ _activeNodeIdsCache = null;
60452
+ _topologyDirty = true;
60430
60453
  get TICK_MS() {
60431
60454
  return conf.FG_TICK_MS;
60432
60455
  }
@@ -60438,7 +60461,15 @@ class ForceGraphEngine {
60438
60461
  if (this.isPhysicsEmit)
60439
60462
  return;
60440
60463
  const op = event.body?.operation;
60441
- if (!op || op.class !== "Transformation" || op.method !== "applyMatrix")
60464
+ if (!op)
60465
+ return;
60466
+ if (op.class === "Board") {
60467
+ this._cachedConnectors = null;
60468
+ this._cachedNodes = null;
60469
+ this._topologyDirty = true;
60470
+ return;
60471
+ }
60472
+ if (op.class !== "Transformation" || op.method !== "applyMatrix")
60442
60473
  return;
60443
60474
  for (const { id, matrix } of op.items) {
60444
60475
  const last = this.lastSyncedPositions.get(id);
@@ -60466,6 +60497,7 @@ class ForceGraphEngine {
60466
60497
  }
60467
60498
  const targetGap = this.calibrateTargetGap(nodeIds);
60468
60499
  this.activeComponents.set(startNodeId, { nodeIds, targetGap });
60500
+ this._activeNodeIdsCache = null;
60469
60501
  this.initNodes(nodeIds);
60470
60502
  this.ensureRunning();
60471
60503
  }
@@ -60474,6 +60506,7 @@ class ForceGraphEngine {
60474
60506
  if (!compId)
60475
60507
  return;
60476
60508
  this.activeComponents.delete(compId);
60509
+ this._activeNodeIdsCache = null;
60477
60510
  if (this.activeComponents.size === 0) {
60478
60511
  this.stopTimers();
60479
60512
  }
@@ -60535,6 +60568,9 @@ class ForceGraphEngine {
60535
60568
  }
60536
60569
  }
60537
60570
  refreshComponentTopology() {
60571
+ if (!this._topologyDirty)
60572
+ return;
60573
+ this._topologyDirty = false;
60538
60574
  for (const [compId, comp] of this.activeComponents) {
60539
60575
  const current = this.bfsComponent(compId);
60540
60576
  const newIds = new Set;
@@ -60544,6 +60580,7 @@ class ForceGraphEngine {
60544
60580
  }
60545
60581
  if (newIds.size > 0) {
60546
60582
  this.initNodes(newIds, comp.nodeIds);
60583
+ this._activeNodeIdsCache = null;
60547
60584
  }
60548
60585
  }
60549
60586
  }
@@ -60597,18 +60634,27 @@ class ForceGraphEngine {
60597
60634
  return conf.FG_TARGET_GAP;
60598
60635
  }
60599
60636
  getActiveNodeIds() {
60600
- const all6 = new Set;
60601
- for (const { nodeIds } of this.activeComponents.values()) {
60602
- for (const id of nodeIds)
60603
- all6.add(id);
60637
+ if (!this._activeNodeIdsCache) {
60638
+ const all6 = new Set;
60639
+ for (const { nodeIds } of this.activeComponents.values()) {
60640
+ for (const id of nodeIds)
60641
+ all6.add(id);
60642
+ }
60643
+ this._activeNodeIdsCache = all6;
60604
60644
  }
60605
- return all6;
60645
+ return this._activeNodeIdsCache;
60606
60646
  }
60607
60647
  getNodes() {
60608
- return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60648
+ if (!this._cachedNodes) {
60649
+ this._cachedNodes = this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60650
+ }
60651
+ return this._cachedNodes;
60609
60652
  }
60610
60653
  getConnectors() {
60611
- return this.board.items.listAll().filter((item) => item.itemType === "Connector");
60654
+ if (!this._cachedConnectors) {
60655
+ this._cachedConnectors = this.board.items.listAll().filter((item) => item.itemType === "Connector");
60656
+ }
60657
+ return this._cachedConnectors;
60612
60658
  }
60613
60659
  tick() {
60614
60660
  this.refreshComponentTopology();
@@ -60620,10 +60666,10 @@ class ForceGraphEngine {
60620
60666
  if (!activeIds.has(item.getId()))
60621
60667
  continue;
60622
60668
  const pos = item.transformation.getTranslation();
60623
- const mbr = item.getMbr();
60624
- const w = Math.max(mbr.getWidth(), 1);
60625
- const h2 = Math.max(mbr.getHeight(), 1);
60626
- snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + w * 0.5, cy: pos.y + h2 * 0.5, w, h: h2 });
60669
+ const half = item.physicsHalfExtent;
60670
+ const w = half * 2;
60671
+ const h2 = half * 2;
60672
+ snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + half, cy: pos.y + half, w, h: h2 });
60627
60673
  }
60628
60674
  const snap = Array.from(snapMap.values());
60629
60675
  if (snap.length < 1)
@@ -60670,8 +60716,12 @@ class ForceGraphEngine {
60670
60716
  continue;
60671
60717
  const dx = s2.cx - s1.cx;
60672
60718
  const dy = s2.cy - s1.cy;
60673
- const distSq = Math.max(dx * dx + dy * dy, conf.FG_MIN_DIST_SQ);
60674
- const force = conf.FG_REPULSION / distSq;
60719
+ const centerDist = Math.sqrt(dx * dx + dy * dy) || 1;
60720
+ const halfA = Math.max(s1.w, s1.h) * 0.5;
60721
+ const halfB = Math.max(s2.w, s2.h) * 0.5;
60722
+ const edgeDist = Math.max(centerDist - halfA - halfB, 0);
60723
+ const edgeDistSq = Math.max(edgeDist * edgeDist, conf.FG_MIN_DIST_SQ);
60724
+ const force = conf.FG_REPULSION / edgeDistSq;
60675
60725
  ax.set(s1.id, (ax.get(s1.id) ?? 0) - dx * force);
60676
60726
  ay.set(s1.id, (ay.get(s1.id) ?? 0) - dy * force);
60677
60727
  ax.set(s2.id, (ax.get(s2.id) ?? 0) + dx * force);
package/dist/esm/index.js CHANGED
@@ -12993,6 +12993,15 @@ var init_BaseItem = __esm(() => {
12993
12993
  childIds = [];
12994
12994
  isHoverHighlighted = false;
12995
12995
  static HOVER_HIGHLIGHT_COLOR = "rgba(71, 120, 245, 0.7)";
12996
+ _physicsHalfExtent = -1;
12997
+ get physicsHalfExtent() {
12998
+ if (this._physicsHalfExtent < 0) {
12999
+ const w = Math.max(this.right - this.left, 1);
13000
+ const h = Math.max(this.bottom - this.top, 1);
13001
+ this._physicsHalfExtent = Math.max(w, h) * 0.5;
13002
+ }
13003
+ return this._physicsHalfExtent;
13004
+ }
12996
13005
  constructor(board, id = "", defaultItemData, isGroupItem) {
12997
13006
  super();
12998
13007
  this.defaultItemData = defaultItemData;
@@ -13199,7 +13208,7 @@ var init_BaseItem = __esm(() => {
13199
13208
  this.subject.publish(this);
13200
13209
  }
13201
13210
  updateMbr() {
13202
- return;
13211
+ this._physicsHalfExtent = -1;
13203
13212
  }
13204
13213
  getLinkTo() {
13205
13214
  return this.linkTo.link;
@@ -13310,6 +13319,16 @@ var init_BaseItem = __esm(() => {
13310
13319
  }
13311
13320
  }
13312
13321
  this.transformation.apply(transformOp);
13322
+ const m = transformOp.method;
13323
+ if (m !== "translateTo" && m !== "translateBy" && m !== "rotateTo" && m !== "rotateBy" && m !== "locked" && m !== "unlocked") {
13324
+ if (m === "applyMatrix") {
13325
+ if (transformOp.items.some((i) => i.matrix.scaleX !== 1 || i.matrix.scaleY !== 1)) {
13326
+ this._physicsHalfExtent = -1;
13327
+ }
13328
+ } else {
13329
+ this._physicsHalfExtent = -1;
13330
+ }
13331
+ }
13313
13332
  break;
13314
13333
  }
13315
13334
  case "LinkTo":
@@ -60420,6 +60439,10 @@ class ForceGraphEngine {
60420
60439
  lastSyncedPositions = new Map;
60421
60440
  activeComponents = new Map;
60422
60441
  isPhysicsEmit = false;
60442
+ _cachedConnectors = null;
60443
+ _cachedNodes = null;
60444
+ _activeNodeIdsCache = null;
60445
+ _topologyDirty = true;
60423
60446
  get TICK_MS() {
60424
60447
  return conf.FG_TICK_MS;
60425
60448
  }
@@ -60431,7 +60454,15 @@ class ForceGraphEngine {
60431
60454
  if (this.isPhysicsEmit)
60432
60455
  return;
60433
60456
  const op = event.body?.operation;
60434
- if (!op || op.class !== "Transformation" || op.method !== "applyMatrix")
60457
+ if (!op)
60458
+ return;
60459
+ if (op.class === "Board") {
60460
+ this._cachedConnectors = null;
60461
+ this._cachedNodes = null;
60462
+ this._topologyDirty = true;
60463
+ return;
60464
+ }
60465
+ if (op.class !== "Transformation" || op.method !== "applyMatrix")
60435
60466
  return;
60436
60467
  for (const { id, matrix } of op.items) {
60437
60468
  const last = this.lastSyncedPositions.get(id);
@@ -60459,6 +60490,7 @@ class ForceGraphEngine {
60459
60490
  }
60460
60491
  const targetGap = this.calibrateTargetGap(nodeIds);
60461
60492
  this.activeComponents.set(startNodeId, { nodeIds, targetGap });
60493
+ this._activeNodeIdsCache = null;
60462
60494
  this.initNodes(nodeIds);
60463
60495
  this.ensureRunning();
60464
60496
  }
@@ -60467,6 +60499,7 @@ class ForceGraphEngine {
60467
60499
  if (!compId)
60468
60500
  return;
60469
60501
  this.activeComponents.delete(compId);
60502
+ this._activeNodeIdsCache = null;
60470
60503
  if (this.activeComponents.size === 0) {
60471
60504
  this.stopTimers();
60472
60505
  }
@@ -60528,6 +60561,9 @@ class ForceGraphEngine {
60528
60561
  }
60529
60562
  }
60530
60563
  refreshComponentTopology() {
60564
+ if (!this._topologyDirty)
60565
+ return;
60566
+ this._topologyDirty = false;
60531
60567
  for (const [compId, comp] of this.activeComponents) {
60532
60568
  const current = this.bfsComponent(compId);
60533
60569
  const newIds = new Set;
@@ -60537,6 +60573,7 @@ class ForceGraphEngine {
60537
60573
  }
60538
60574
  if (newIds.size > 0) {
60539
60575
  this.initNodes(newIds, comp.nodeIds);
60576
+ this._activeNodeIdsCache = null;
60540
60577
  }
60541
60578
  }
60542
60579
  }
@@ -60590,18 +60627,27 @@ class ForceGraphEngine {
60590
60627
  return conf.FG_TARGET_GAP;
60591
60628
  }
60592
60629
  getActiveNodeIds() {
60593
- const all6 = new Set;
60594
- for (const { nodeIds } of this.activeComponents.values()) {
60595
- for (const id of nodeIds)
60596
- all6.add(id);
60630
+ if (!this._activeNodeIdsCache) {
60631
+ const all6 = new Set;
60632
+ for (const { nodeIds } of this.activeComponents.values()) {
60633
+ for (const id of nodeIds)
60634
+ all6.add(id);
60635
+ }
60636
+ this._activeNodeIdsCache = all6;
60597
60637
  }
60598
- return all6;
60638
+ return this._activeNodeIdsCache;
60599
60639
  }
60600
60640
  getNodes() {
60601
- return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60641
+ if (!this._cachedNodes) {
60642
+ this._cachedNodes = this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
60643
+ }
60644
+ return this._cachedNodes;
60602
60645
  }
60603
60646
  getConnectors() {
60604
- return this.board.items.listAll().filter((item) => item.itemType === "Connector");
60647
+ if (!this._cachedConnectors) {
60648
+ this._cachedConnectors = this.board.items.listAll().filter((item) => item.itemType === "Connector");
60649
+ }
60650
+ return this._cachedConnectors;
60605
60651
  }
60606
60652
  tick() {
60607
60653
  this.refreshComponentTopology();
@@ -60613,10 +60659,10 @@ class ForceGraphEngine {
60613
60659
  if (!activeIds.has(item.getId()))
60614
60660
  continue;
60615
60661
  const pos = item.transformation.getTranslation();
60616
- const mbr = item.getMbr();
60617
- const w = Math.max(mbr.getWidth(), 1);
60618
- const h2 = Math.max(mbr.getHeight(), 1);
60619
- snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + w * 0.5, cy: pos.y + h2 * 0.5, w, h: h2 });
60662
+ const half = item.physicsHalfExtent;
60663
+ const w = half * 2;
60664
+ const h2 = half * 2;
60665
+ snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + half, cy: pos.y + half, w, h: h2 });
60620
60666
  }
60621
60667
  const snap = Array.from(snapMap.values());
60622
60668
  if (snap.length < 1)
@@ -60663,8 +60709,12 @@ class ForceGraphEngine {
60663
60709
  continue;
60664
60710
  const dx = s2.cx - s1.cx;
60665
60711
  const dy = s2.cy - s1.cy;
60666
- const distSq = Math.max(dx * dx + dy * dy, conf.FG_MIN_DIST_SQ);
60667
- const force = conf.FG_REPULSION / distSq;
60712
+ const centerDist = Math.sqrt(dx * dx + dy * dy) || 1;
60713
+ const halfA = Math.max(s1.w, s1.h) * 0.5;
60714
+ const halfB = Math.max(s2.w, s2.h) * 0.5;
60715
+ const edgeDist = Math.max(centerDist - halfA - halfB, 0);
60716
+ const edgeDistSq = Math.max(edgeDist * edgeDist, conf.FG_MIN_DIST_SQ);
60717
+ const force = conf.FG_REPULSION / edgeDistSq;
60668
60718
  ax.set(s1.id, (ax.get(s1.id) ?? 0) - dx * force);
60669
60719
  ay.set(s1.id, (ay.get(s1.id) ?? 0) - dy * force);
60670
60720
  ax.set(s2.id, (ax.get(s2.id) ?? 0) + dx * force);
package/dist/esm/node.js CHANGED
@@ -13015,6 +13015,15 @@ var init_BaseItem = __esm(() => {
13015
13015
  childIds = [];
13016
13016
  isHoverHighlighted = false;
13017
13017
  static HOVER_HIGHLIGHT_COLOR = "rgba(71, 120, 245, 0.7)";
13018
+ _physicsHalfExtent = -1;
13019
+ get physicsHalfExtent() {
13020
+ if (this._physicsHalfExtent < 0) {
13021
+ const w = Math.max(this.right - this.left, 1);
13022
+ const h = Math.max(this.bottom - this.top, 1);
13023
+ this._physicsHalfExtent = Math.max(w, h) * 0.5;
13024
+ }
13025
+ return this._physicsHalfExtent;
13026
+ }
13018
13027
  constructor(board, id = "", defaultItemData, isGroupItem) {
13019
13028
  super();
13020
13029
  this.defaultItemData = defaultItemData;
@@ -13221,7 +13230,7 @@ var init_BaseItem = __esm(() => {
13221
13230
  this.subject.publish(this);
13222
13231
  }
13223
13232
  updateMbr() {
13224
- return;
13233
+ this._physicsHalfExtent = -1;
13225
13234
  }
13226
13235
  getLinkTo() {
13227
13236
  return this.linkTo.link;
@@ -13332,6 +13341,16 @@ var init_BaseItem = __esm(() => {
13332
13341
  }
13333
13342
  }
13334
13343
  this.transformation.apply(transformOp);
13344
+ const m = transformOp.method;
13345
+ if (m !== "translateTo" && m !== "translateBy" && m !== "rotateTo" && m !== "rotateBy" && m !== "locked" && m !== "unlocked") {
13346
+ if (m === "applyMatrix") {
13347
+ if (transformOp.items.some((i) => i.matrix.scaleX !== 1 || i.matrix.scaleY !== 1)) {
13348
+ this._physicsHalfExtent = -1;
13349
+ }
13350
+ } else {
13351
+ this._physicsHalfExtent = -1;
13352
+ }
13353
+ }
13335
13354
  break;
13336
13355
  }
13337
13356
  case "LinkTo":
@@ -62884,6 +62903,10 @@ class ForceGraphEngine {
62884
62903
  lastSyncedPositions = new Map;
62885
62904
  activeComponents = new Map;
62886
62905
  isPhysicsEmit = false;
62906
+ _cachedConnectors = null;
62907
+ _cachedNodes = null;
62908
+ _activeNodeIdsCache = null;
62909
+ _topologyDirty = true;
62887
62910
  get TICK_MS() {
62888
62911
  return conf.FG_TICK_MS;
62889
62912
  }
@@ -62895,7 +62918,15 @@ class ForceGraphEngine {
62895
62918
  if (this.isPhysicsEmit)
62896
62919
  return;
62897
62920
  const op = event.body?.operation;
62898
- if (!op || op.class !== "Transformation" || op.method !== "applyMatrix")
62921
+ if (!op)
62922
+ return;
62923
+ if (op.class === "Board") {
62924
+ this._cachedConnectors = null;
62925
+ this._cachedNodes = null;
62926
+ this._topologyDirty = true;
62927
+ return;
62928
+ }
62929
+ if (op.class !== "Transformation" || op.method !== "applyMatrix")
62899
62930
  return;
62900
62931
  for (const { id, matrix } of op.items) {
62901
62932
  const last = this.lastSyncedPositions.get(id);
@@ -62923,6 +62954,7 @@ class ForceGraphEngine {
62923
62954
  }
62924
62955
  const targetGap = this.calibrateTargetGap(nodeIds);
62925
62956
  this.activeComponents.set(startNodeId, { nodeIds, targetGap });
62957
+ this._activeNodeIdsCache = null;
62926
62958
  this.initNodes(nodeIds);
62927
62959
  this.ensureRunning();
62928
62960
  }
@@ -62931,6 +62963,7 @@ class ForceGraphEngine {
62931
62963
  if (!compId)
62932
62964
  return;
62933
62965
  this.activeComponents.delete(compId);
62966
+ this._activeNodeIdsCache = null;
62934
62967
  if (this.activeComponents.size === 0) {
62935
62968
  this.stopTimers();
62936
62969
  }
@@ -62992,6 +63025,9 @@ class ForceGraphEngine {
62992
63025
  }
62993
63026
  }
62994
63027
  refreshComponentTopology() {
63028
+ if (!this._topologyDirty)
63029
+ return;
63030
+ this._topologyDirty = false;
62995
63031
  for (const [compId, comp] of this.activeComponents) {
62996
63032
  const current = this.bfsComponent(compId);
62997
63033
  const newIds = new Set;
@@ -63001,6 +63037,7 @@ class ForceGraphEngine {
63001
63037
  }
63002
63038
  if (newIds.size > 0) {
63003
63039
  this.initNodes(newIds, comp.nodeIds);
63040
+ this._activeNodeIdsCache = null;
63004
63041
  }
63005
63042
  }
63006
63043
  }
@@ -63054,18 +63091,27 @@ class ForceGraphEngine {
63054
63091
  return conf.FG_TARGET_GAP;
63055
63092
  }
63056
63093
  getActiveNodeIds() {
63057
- const all6 = new Set;
63058
- for (const { nodeIds } of this.activeComponents.values()) {
63059
- for (const id of nodeIds)
63060
- all6.add(id);
63094
+ if (!this._activeNodeIdsCache) {
63095
+ const all6 = new Set;
63096
+ for (const { nodeIds } of this.activeComponents.values()) {
63097
+ for (const id of nodeIds)
63098
+ all6.add(id);
63099
+ }
63100
+ this._activeNodeIdsCache = all6;
63061
63101
  }
63062
- return all6;
63102
+ return this._activeNodeIdsCache;
63063
63103
  }
63064
63104
  getNodes() {
63065
- return this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
63105
+ if (!this._cachedNodes) {
63106
+ this._cachedNodes = this.board.items.listAll().filter((item) => !EXCLUDED_TYPES.has(item.itemType) && !item.transformation.isLocked);
63107
+ }
63108
+ return this._cachedNodes;
63066
63109
  }
63067
63110
  getConnectors() {
63068
- return this.board.items.listAll().filter((item) => item.itemType === "Connector");
63111
+ if (!this._cachedConnectors) {
63112
+ this._cachedConnectors = this.board.items.listAll().filter((item) => item.itemType === "Connector");
63113
+ }
63114
+ return this._cachedConnectors;
63069
63115
  }
63070
63116
  tick() {
63071
63117
  this.refreshComponentTopology();
@@ -63077,10 +63123,10 @@ class ForceGraphEngine {
63077
63123
  if (!activeIds.has(item.getId()))
63078
63124
  continue;
63079
63125
  const pos = item.transformation.getTranslation();
63080
- const mbr = item.getMbr();
63081
- const w = Math.max(mbr.getWidth(), 1);
63082
- const h2 = Math.max(mbr.getHeight(), 1);
63083
- snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + w * 0.5, cy: pos.y + h2 * 0.5, w, h: h2 });
63126
+ const half = item.physicsHalfExtent;
63127
+ const w = half * 2;
63128
+ const h2 = half * 2;
63129
+ snapMap.set(item.getId(), { id: item.getId(), cx: pos.x + half, cy: pos.y + half, w, h: h2 });
63084
63130
  }
63085
63131
  const snap = Array.from(snapMap.values());
63086
63132
  if (snap.length < 1)
@@ -63127,8 +63173,12 @@ class ForceGraphEngine {
63127
63173
  continue;
63128
63174
  const dx = s2.cx - s1.cx;
63129
63175
  const dy = s2.cy - s1.cy;
63130
- const distSq = Math.max(dx * dx + dy * dy, conf.FG_MIN_DIST_SQ);
63131
- const force = conf.FG_REPULSION / distSq;
63176
+ const centerDist = Math.sqrt(dx * dx + dy * dy) || 1;
63177
+ const halfA = Math.max(s1.w, s1.h) * 0.5;
63178
+ const halfB = Math.max(s2.w, s2.h) * 0.5;
63179
+ const edgeDist = Math.max(centerDist - halfA - halfB, 0);
63180
+ const edgeDistSq = Math.max(edgeDist * edgeDist, conf.FG_MIN_DIST_SQ);
63181
+ const force = conf.FG_REPULSION / edgeDistSq;
63132
63182
  ax.set(s1.id, (ax.get(s1.id) ?? 0) - dx * force);
63133
63183
  ay.set(s1.id, (ay.get(s1.id) ?? 0) - dy * force);
63134
63184
  ax.set(s2.id, (ax.get(s2.id) ?? 0) + dx * force);
@@ -11,6 +11,14 @@ export declare class ForceGraphEngine {
11
11
  /** Set to true while we are emitting a physics sync operation, so the
12
12
  * board-event subscription below doesn't double-update lastSyncedPositions. */
13
13
  private isPhysicsEmit;
14
+ /** Cached connector list — invalidated when items are added/removed. */
15
+ private _cachedConnectors;
16
+ /** Cached non-connector node list — invalidated when items are added/removed. */
17
+ private _cachedNodes;
18
+ /** Cached flat set of all active node ids across components. */
19
+ private _activeNodeIdsCache;
20
+ /** True when a board add/remove event has occurred since the last BFS refresh. */
21
+ private _topologyDirty;
14
22
  private get TICK_MS();
15
23
  private readonly SYNC_MS;
16
24
  private readonly MIN_MOVE_PX;
@@ -49,7 +57,8 @@ export declare class ForceGraphEngine {
49
57
  /** Initialize velocities and sync baseline for a set of node ids.
50
58
  * If `target` is provided, new ids are also added into that Set. */
51
59
  private initNodes;
52
- /** Re-BFS each active component to pick up nodes/connectors added after enableForGraph. */
60
+ /** Re-BFS each active component to pick up nodes/connectors added after enableForGraph.
61
+ * Runs only when _topologyDirty is set (board add/remove event). */
53
62
  private refreshComponentTopology;
54
63
  private ensureRunning;
55
64
  private stopTimers;
@@ -49,6 +49,11 @@ export declare class BaseItem<T extends BaseItem<any> = any> extends Mbr impleme
49
49
  childIds: string[];
50
50
  isHoverHighlighted: boolean;
51
51
  static readonly HOVER_HIGHLIGHT_COLOR = "rgba(71, 120, 245, 0.7)";
52
+ /** Cached max half-extent for force-graph physics: max(w, h) * 0.5.
53
+ * -1 means dirty. Physics never scales items, so this is valid for the
54
+ * entire simulation once computed. Invalidated on resize/scale only. */
55
+ private _physicsHalfExtent;
56
+ get physicsHalfExtent(): number;
52
57
  constructor(board: Board, id?: string, defaultItemData?: BaseItemData | undefined, isGroupItem?: boolean);
53
58
  updateChildrenIds(): void;
54
59
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "microboard-temp",
3
- "version": "0.13.53",
3
+ "version": "0.13.55",
4
4
  "description": "A flexible interactive whiteboard library",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",