@andespindola/brainlink 0.1.0-beta.50 → 0.1.0-beta.52
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.
|
@@ -17,10 +17,11 @@ const worldCoordinateLimit = 5_000_000
|
|
|
17
17
|
const transformCoordinateLimit = 20_000_000
|
|
18
18
|
const hoverHitTestIntervalMs = 64
|
|
19
19
|
const overviewClusterMaxCount = 1400
|
|
20
|
-
const zoomRecoveryGuardMs =
|
|
20
|
+
const zoomRecoveryGuardMs = 1500
|
|
21
21
|
const state = {
|
|
22
22
|
graph: { nodes: [], edges: [] },
|
|
23
23
|
nodes: [],
|
|
24
|
+
nodeById: new Map(),
|
|
24
25
|
edges: [],
|
|
25
26
|
visibleNodes: [],
|
|
26
27
|
visibleEdges: [],
|
|
@@ -555,6 +556,72 @@ const sampleVisibleNodes = (limit = renderNodeBudget, sourceNodes = state.visibl
|
|
|
555
556
|
return nodes
|
|
556
557
|
}
|
|
557
558
|
|
|
559
|
+
const enrichSampleWithNeighbors = (nodes) => {
|
|
560
|
+
if (nodes.length === 0) {
|
|
561
|
+
return {
|
|
562
|
+
nodes,
|
|
563
|
+
edges: []
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const maxNodes = Math.min(renderNodeBudget, nodes.length + 200)
|
|
568
|
+
const expanded = [...nodes]
|
|
569
|
+
const ids = new Set(expanded.map((node) => node.id))
|
|
570
|
+
|
|
571
|
+
for (let index = 0; index < nodes.length && expanded.length < maxNodes; index += 1) {
|
|
572
|
+
const node = nodes[index]
|
|
573
|
+
const candidates = [...(state.visibleEdgeByNode.get(node.id) ?? [])]
|
|
574
|
+
.filter((edge) => edge.target)
|
|
575
|
+
.sort((left, right) => edgeWeight(right) - edgeWeight(left))
|
|
576
|
+
.slice(0, 3)
|
|
577
|
+
|
|
578
|
+
for (let candidateIndex = 0; candidateIndex < candidates.length && expanded.length < maxNodes; candidateIndex += 1) {
|
|
579
|
+
const edge = candidates[candidateIndex]
|
|
580
|
+
const otherId = edge.source === node.id ? edge.target : edge.source
|
|
581
|
+
|
|
582
|
+
if (!otherId || ids.has(otherId)) {
|
|
583
|
+
continue
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const otherNode = state.nodeById.get(otherId)
|
|
587
|
+
if (!otherNode) {
|
|
588
|
+
continue
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
ids.add(otherId)
|
|
592
|
+
expanded.push(otherNode)
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const edges = collectVisibleEdgesForNodes(ids)
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
nodes: expanded,
|
|
600
|
+
edges
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const ensureHubNodesInRenderedSet = (nodes) => {
|
|
605
|
+
if (nodes.length === 0) {
|
|
606
|
+
return nodes
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const maxNodes = Math.max(renderNodeBudget, nodes.length)
|
|
610
|
+
const ids = new Set(nodes.map((node) => node.id))
|
|
611
|
+
const hubs = rankedHubNodes()
|
|
612
|
+
const merged = [...nodes]
|
|
613
|
+
|
|
614
|
+
for (let index = 0; index < hubs.length && merged.length < maxNodes; index += 1) {
|
|
615
|
+
const hub = hubs[index]
|
|
616
|
+
if (!ids.has(hub.id)) {
|
|
617
|
+
merged.push(hub)
|
|
618
|
+
ids.add(hub.id)
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return merged
|
|
623
|
+
}
|
|
624
|
+
|
|
558
625
|
const clampScale = value => Math.max(zoomRange.min, Math.min(zoomRange.max, value))
|
|
559
626
|
const isFiniteNumber = value => Number.isFinite(value)
|
|
560
627
|
const isReasonableCoordinate = value => isFiniteNumber(value) && Math.abs(value) <= worldCoordinateLimit
|
|
@@ -1053,9 +1120,19 @@ const computeRenderVisibility = () => {
|
|
|
1053
1120
|
? sampleVisibleNodes(Math.min(sampleLimit, renderNodeBudget), sourceNodes)
|
|
1054
1121
|
: sourceNodes.slice(0, Math.min(sourceNodes.length, renderNodeBudget))
|
|
1055
1122
|
const sampledIds = new Set(sampled.map((node) => node.id))
|
|
1123
|
+
let sampledEdges = state.transform.scale >= 0.035 ? collectVisibleEdgesForNodes(sampledIds) : []
|
|
1124
|
+
let sampledNodes = ensureHubNodesInRenderedSet(sampled)
|
|
1125
|
+
|
|
1126
|
+
if (state.transform.scale >= 0.035 && sampledEdges.length === 0) {
|
|
1127
|
+
const enriched = enrichSampleWithNeighbors(sampledNodes)
|
|
1128
|
+
sampledNodes = ensureHubNodesInRenderedSet(enriched.nodes)
|
|
1129
|
+
const sampledWithHubsIds = new Set(sampledNodes.map((node) => node.id))
|
|
1130
|
+
sampledEdges = collectVisibleEdgesForNodes(sampledWithHubsIds)
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1056
1133
|
state.renderClusters = []
|
|
1057
|
-
state.renderNodes =
|
|
1058
|
-
state.renderEdges =
|
|
1134
|
+
state.renderNodes = sampledNodes
|
|
1135
|
+
state.renderEdges = sampledEdges
|
|
1059
1136
|
return
|
|
1060
1137
|
}
|
|
1061
1138
|
|
|
@@ -1104,10 +1181,11 @@ const computeRenderVisibility = () => {
|
|
|
1104
1181
|
return
|
|
1105
1182
|
}
|
|
1106
1183
|
|
|
1107
|
-
const
|
|
1184
|
+
const normalizedNodes = ensureHubNodesInRenderedSet(nodes)
|
|
1185
|
+
const nodeIds = new Set(normalizedNodes.map((node) => node.id))
|
|
1108
1186
|
const edges = collectVisibleEdgesForNodes(nodeIds)
|
|
1109
1187
|
|
|
1110
|
-
state.renderNodes =
|
|
1188
|
+
state.renderNodes = normalizedNodes
|
|
1111
1189
|
state.renderEdges = edges
|
|
1112
1190
|
|
|
1113
1191
|
if (state.renderNodes.length === 0 && state.visibleNodes.length > 0) {
|
|
@@ -1212,11 +1290,13 @@ const render = now => {
|
|
|
1212
1290
|
state.offscreenFrameCount = 0
|
|
1213
1291
|
}
|
|
1214
1292
|
const minimumEdgeScale =
|
|
1215
|
-
state.
|
|
1216
|
-
? 0.
|
|
1217
|
-
: state.
|
|
1218
|
-
? 0.
|
|
1219
|
-
:
|
|
1293
|
+
state.renderNodes.length > 1300
|
|
1294
|
+
? 0.12
|
|
1295
|
+
: state.renderNodes.length > 900
|
|
1296
|
+
? 0.085
|
|
1297
|
+
: state.renderNodes.length > 500
|
|
1298
|
+
? 0.05
|
|
1299
|
+
: 0
|
|
1220
1300
|
const drawEdges =
|
|
1221
1301
|
state.renderClusters.length === 0 &&
|
|
1222
1302
|
state.transform.scale >= minimumEdgeScale
|
|
@@ -1399,17 +1479,19 @@ const selectNodeById = id => {
|
|
|
1399
1479
|
}
|
|
1400
1480
|
|
|
1401
1481
|
const zoomAtPoint = (screenX, screenY, factor, source = 'generic') => {
|
|
1482
|
+
if (source === 'wheel') {
|
|
1483
|
+
state.lastManualZoomAt = performance.now()
|
|
1484
|
+
}
|
|
1402
1485
|
const nextScale = clampScale(state.transform.scale * factor)
|
|
1403
|
-
if (nextScale === state.transform.scale)
|
|
1486
|
+
if (nextScale === state.transform.scale) {
|
|
1487
|
+
return
|
|
1488
|
+
}
|
|
1404
1489
|
const worldX = (screenX - state.transform.x) / state.transform.scale
|
|
1405
1490
|
const worldY = (screenY - state.transform.y) / state.transform.scale
|
|
1406
1491
|
state.transform.scale = clampScale(nextScale)
|
|
1407
1492
|
state.transform.x = clampTransformCoordinate(screenX - worldX * nextScale)
|
|
1408
1493
|
state.transform.y = clampTransformCoordinate(screenY - worldY * nextScale)
|
|
1409
1494
|
state.offscreenFrameCount = 0
|
|
1410
|
-
if (source === 'wheel') {
|
|
1411
|
-
state.lastManualZoomAt = performance.now()
|
|
1412
|
-
}
|
|
1413
1495
|
markRenderDirty()
|
|
1414
1496
|
}
|
|
1415
1497
|
|
|
@@ -1627,6 +1709,7 @@ const loadGraph = async (options = { reset: false }) => {
|
|
|
1627
1709
|
state.graphSignature = signature
|
|
1628
1710
|
state.graph = graph
|
|
1629
1711
|
state.nodes = layout.nodes
|
|
1712
|
+
state.nodeById = new Map(state.nodes.map((node) => [node.id, node]))
|
|
1630
1713
|
state.edges = layout.edges
|
|
1631
1714
|
state.nodeDegrees = state.edges.reduce((degrees, edge) => {
|
|
1632
1715
|
degrees.set(edge.source, (degrees.get(edge.source) ?? 0) + edgeWeight(edge))
|
package/package.json
CHANGED