@andespindola/brainlink 0.1.0-beta.65 → 0.1.0-beta.67
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.
|
@@ -64,7 +64,8 @@ const state = {
|
|
|
64
64
|
filterWorker: null,
|
|
65
65
|
filterReady: false,
|
|
66
66
|
lastHoverHitAt: 0,
|
|
67
|
-
lastManualZoomAt: 0
|
|
67
|
+
lastManualZoomAt: 0,
|
|
68
|
+
lastZoomFocus: { x: 0, y: 0, at: 0 }
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
const byId = id => document.getElementById(id)
|
|
@@ -573,7 +574,7 @@ const nodeBudgetForScale = (scale) => {
|
|
|
573
574
|
const layerFocusForScale = (scale) => {
|
|
574
575
|
const normalized = Math.max(0, Math.min(1, (scale - 0.06) / 0.94))
|
|
575
576
|
const shellCenter = Math.max(0.08, 0.96 - normalized * 0.86)
|
|
576
|
-
const shellWidth = Math.max(0.
|
|
577
|
+
const shellWidth = Math.max(0.24, 0.46 - normalized * 0.16)
|
|
577
578
|
const coreRadius = Math.max(0.06, 0.1 + normalized * 0.22)
|
|
578
579
|
const coreRatio = Math.max(0.2, Math.min(0.72, 0.24 + normalized * 0.48))
|
|
579
580
|
|
|
@@ -604,7 +605,7 @@ const selectLayeredNodesForScale = (sourceNodes, targetCount) => {
|
|
|
604
605
|
...item,
|
|
605
606
|
normalized: item.distance / maxDistance
|
|
606
607
|
}))
|
|
607
|
-
const desired = Math.max(
|
|
608
|
+
const desired = Math.max(260, Math.min(sourceNodes.length, targetCount * 2))
|
|
608
609
|
const coreTarget = Math.max(36, Math.min(desired - 8, Math.floor(desired * focus.coreRatio)))
|
|
609
610
|
const shellTarget = Math.max(12, desired - coreTarget)
|
|
610
611
|
const shellHalf = focus.shellWidth / 2
|
|
@@ -653,6 +654,60 @@ const selectLayeredNodesForScale = (sourceNodes, targetCount) => {
|
|
|
653
654
|
return merged.length > 0 ? merged : sourceNodes
|
|
654
655
|
}
|
|
655
656
|
|
|
657
|
+
const viewportCenterWorldPoint = () => {
|
|
658
|
+
const viewport = worldViewportBounds()
|
|
659
|
+
return {
|
|
660
|
+
x: (viewport.minX + viewport.maxX) / 2,
|
|
661
|
+
y: (viewport.minY + viewport.maxY) / 2
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const mergeUniqueNodes = (leftNodes, rightNodes, limit) => {
|
|
666
|
+
const merged = []
|
|
667
|
+
const ids = new Set()
|
|
668
|
+
|
|
669
|
+
const push = (node) => {
|
|
670
|
+
if (!node || ids.has(node.id) || merged.length >= limit) {
|
|
671
|
+
return
|
|
672
|
+
}
|
|
673
|
+
ids.add(node.id)
|
|
674
|
+
merged.push(node)
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
for (let index = 0; index < leftNodes.length && merged.length < limit; index += 1) {
|
|
678
|
+
push(leftNodes[index])
|
|
679
|
+
}
|
|
680
|
+
for (let index = 0; index < rightNodes.length && merged.length < limit; index += 1) {
|
|
681
|
+
push(rightNodes[index])
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return merged
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const selectAccessBridgeNodes = (sourceNodes, limit) => {
|
|
688
|
+
if (limit <= 0 || sourceNodes.length === 0) {
|
|
689
|
+
return []
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const now = performance.now()
|
|
693
|
+
const recentZoomFocus =
|
|
694
|
+
now - state.lastZoomFocus.at <= 1200
|
|
695
|
+
? { x: state.lastZoomFocus.x, y: state.lastZoomFocus.y }
|
|
696
|
+
: null
|
|
697
|
+
const anchor = recentZoomFocus ?? viewportCenterWorldPoint()
|
|
698
|
+
return [...sourceNodes]
|
|
699
|
+
.sort((left, right) => {
|
|
700
|
+
const leftDistance = Math.hypot(left.x - anchor.x, left.y - anchor.y)
|
|
701
|
+
const rightDistance = Math.hypot(right.x - anchor.x, right.y - anchor.y)
|
|
702
|
+
if (leftDistance !== rightDistance) return leftDistance - rightDistance
|
|
703
|
+
const leftDegree = state.nodeDegrees.get(left.id) ?? 0
|
|
704
|
+
const rightDegree = state.nodeDegrees.get(right.id) ?? 0
|
|
705
|
+
if (leftDegree !== rightDegree) return rightDegree - leftDegree
|
|
706
|
+
return left.id.localeCompare(right.id)
|
|
707
|
+
})
|
|
708
|
+
.slice(0, limit)
|
|
709
|
+
}
|
|
710
|
+
|
|
656
711
|
const edgeIdentityKey = edge => {
|
|
657
712
|
if (!edge.target) return ''
|
|
658
713
|
const pair = edge.source < edge.target
|
|
@@ -1183,9 +1238,17 @@ const focusPrimaryHub = () => {
|
|
|
1183
1238
|
markRenderDirty()
|
|
1184
1239
|
}
|
|
1185
1240
|
|
|
1241
|
+
const layoutDensityScaleForNodeCount = (nodeCount) => {
|
|
1242
|
+
if (nodeCount > 50000) return 0.56
|
|
1243
|
+
if (nodeCount > 20000) return 0.64
|
|
1244
|
+
if (nodeCount > 6000) return 0.76
|
|
1245
|
+
return 1
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1186
1248
|
const createLayout = graph => {
|
|
1187
1249
|
const nodeRows = Array.isArray(graph.nodes) ? graph.nodes : []
|
|
1188
1250
|
const edgeRows = Array.isArray(graph.edges) ? graph.edges : []
|
|
1251
|
+
const densityScale = layoutDensityScaleForNodeCount(nodeRows.length)
|
|
1189
1252
|
const nodes = nodeRows.map(node => {
|
|
1190
1253
|
if (Array.isArray(node)) {
|
|
1191
1254
|
const [id, title, x, y, group, segment] = node
|
|
@@ -1196,8 +1259,8 @@ const createLayout = graph => {
|
|
|
1196
1259
|
tags: [],
|
|
1197
1260
|
group: typeof group === 'string' ? group : 'root',
|
|
1198
1261
|
segment: typeof segment === 'string' ? segment : 'root',
|
|
1199
|
-
x: Number.isFinite(x) ? x : 0,
|
|
1200
|
-
y: Number.isFinite(y) ? y : 0,
|
|
1262
|
+
x: Number.isFinite(x) ? x * densityScale : 0,
|
|
1263
|
+
y: Number.isFinite(y) ? y * densityScale : 0,
|
|
1201
1264
|
vx: 0,
|
|
1202
1265
|
vy: 0
|
|
1203
1266
|
}
|
|
@@ -1207,8 +1270,8 @@ const createLayout = graph => {
|
|
|
1207
1270
|
...node,
|
|
1208
1271
|
path: typeof node.path === 'string' ? node.path : '',
|
|
1209
1272
|
tags: Array.isArray(node.tags) ? node.tags : [],
|
|
1210
|
-
x: Number.isFinite(node.x) ? node.x : 0,
|
|
1211
|
-
y: Number.isFinite(node.y) ? node.y : 0,
|
|
1273
|
+
x: Number.isFinite(node.x) ? node.x * densityScale : 0,
|
|
1274
|
+
y: Number.isFinite(node.y) ? node.y * densityScale : 0,
|
|
1212
1275
|
vx: Number.isFinite(node.vx) ? node.vx : 0,
|
|
1213
1276
|
vy: Number.isFinite(node.vy) ? node.vy : 0
|
|
1214
1277
|
}
|
|
@@ -1672,9 +1735,15 @@ const computeRenderVisibility = () => {
|
|
|
1672
1735
|
const sourceNodes = viewportNodes.length > 0 ? viewportNodes : state.visibleNodes
|
|
1673
1736
|
const sampleLimit = nodeBudgetForScale(state.transform.scale)
|
|
1674
1737
|
const layeredNodes = selectLayeredNodesForScale(sourceNodes, sampleLimit)
|
|
1675
|
-
const
|
|
1676
|
-
|
|
1677
|
-
|
|
1738
|
+
const bridgeLimit = Math.max(24, Math.min(180, Math.floor(sampleLimit * 0.26)))
|
|
1739
|
+
const bridgedNodes = mergeUniqueNodes(
|
|
1740
|
+
layeredNodes,
|
|
1741
|
+
selectAccessBridgeNodes(sourceNodes, bridgeLimit),
|
|
1742
|
+
Math.min(renderNodeBudget, sampleLimit + bridgeLimit)
|
|
1743
|
+
)
|
|
1744
|
+
const sampled = bridgedNodes.length > sampleLimit
|
|
1745
|
+
? sampleVisibleNodes(Math.min(sampleLimit, renderNodeBudget), bridgedNodes)
|
|
1746
|
+
: bridgedNodes.slice(0, Math.min(bridgedNodes.length, renderNodeBudget))
|
|
1678
1747
|
const sampledIds = new Set(sampled.map((node) => node.id))
|
|
1679
1748
|
let sampledEdges = state.transform.scale >= 0.035 ? collectVisibleEdgesForNodes(sampledIds) : []
|
|
1680
1749
|
let sampledNodes = ensureHubNodesInRenderedSet(sampled)
|
|
@@ -2044,6 +2113,11 @@ const zoomAtPoint = (screenX, screenY, factor, source = 'generic') => {
|
|
|
2044
2113
|
}
|
|
2045
2114
|
const worldX = (screenX - state.transform.x) / state.transform.scale
|
|
2046
2115
|
const worldY = (screenY - state.transform.y) / state.transform.scale
|
|
2116
|
+
state.lastZoomFocus = {
|
|
2117
|
+
x: worldX,
|
|
2118
|
+
y: worldY,
|
|
2119
|
+
at: performance.now()
|
|
2120
|
+
}
|
|
2047
2121
|
state.transform.scale = clampScale(nextScale)
|
|
2048
2122
|
state.transform.x = clampTransformCoordinate(screenX - worldX * nextScale)
|
|
2049
2123
|
state.transform.y = clampTransformCoordinate(screenY - worldY * nextScale)
|
package/package.json
CHANGED