@andespindola/brainlink 0.1.0-beta.62 → 0.1.0-beta.64
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.
|
@@ -38,7 +38,7 @@ export const createClientHtml = () => `<!doctype html>
|
|
|
38
38
|
<div class="toolbar" aria-label="Graph controls">
|
|
39
39
|
<button id="zoomIn" type="button" title="Zoom in">+</button>
|
|
40
40
|
<button id="zoomOut" type="button" title="Zoom out">-</button>
|
|
41
|
-
<button id="fit" type="button" title="
|
|
41
|
+
<button id="fit" type="button" title="Focus central hub">◎</button>
|
|
42
42
|
<button id="reset" type="button" title="Reset view">⌂</button>
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
|
@@ -570,17 +570,17 @@ const nodeBudgetForScale = (scale) => {
|
|
|
570
570
|
return renderNodeBudget
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
return {
|
|
573
|
+
const layerFocusForScale = (scale) => {
|
|
574
|
+
const normalized = Math.max(0, Math.min(1, (scale - 0.06) / 0.94))
|
|
575
|
+
const shellCenter = Math.max(0.08, 0.96 - normalized * 0.86)
|
|
576
|
+
const shellWidth = Math.max(0.16, 0.34 - normalized * 0.2)
|
|
577
|
+
const coreRadius = Math.max(0.06, 0.1 + normalized * 0.22)
|
|
578
|
+
const coreRatio = Math.max(0.2, Math.min(0.72, 0.24 + normalized * 0.48))
|
|
579
|
+
|
|
580
|
+
return { shellCenter, shellWidth, coreRadius, coreRatio }
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
const selectLayeredNodesForScale = (sourceNodes) => {
|
|
583
|
+
const selectLayeredNodesForScale = (sourceNodes, targetCount) => {
|
|
584
584
|
const hub = state.primaryHub
|
|
585
585
|
if (!hub || sourceNodes.length <= 1200 || state.visibleNodes.length <= massiveGraphNodeThreshold) {
|
|
586
586
|
return sourceNodes
|
|
@@ -599,39 +599,58 @@ const selectLayeredNodesForScale = (sourceNodes) => {
|
|
|
599
599
|
return sourceNodes
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
-
const
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
602
|
+
const focus = layerFocusForScale(state.transform.scale)
|
|
603
|
+
const normalizedRows = distances.map((item) => ({
|
|
604
|
+
...item,
|
|
605
|
+
normalized: item.distance / maxDistance
|
|
606
|
+
}))
|
|
607
|
+
const desired = Math.max(220, Math.min(sourceNodes.length, targetCount * 2))
|
|
608
|
+
const coreTarget = Math.max(36, Math.min(desired - 8, Math.floor(desired * focus.coreRatio)))
|
|
609
|
+
const shellTarget = Math.max(12, desired - coreTarget)
|
|
610
|
+
const shellHalf = focus.shellWidth / 2
|
|
612
611
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
612
|
+
const coreNodes = normalizedRows
|
|
613
|
+
.filter((item) => item.normalized <= focus.coreRadius)
|
|
614
|
+
.sort((left, right) => {
|
|
615
|
+
const leftScore = state.nodeDegrees.get(left.node.id) ?? 0
|
|
616
|
+
const rightScore = state.nodeDegrees.get(right.node.id) ?? 0
|
|
617
|
+
if (leftScore !== rightScore) return rightScore - leftScore
|
|
618
|
+
return left.node.id.localeCompare(right.node.id)
|
|
619
|
+
})
|
|
620
|
+
.slice(0, coreTarget)
|
|
621
|
+
.map((item) => item.node)
|
|
616
622
|
|
|
617
|
-
const
|
|
618
|
-
const fallback = [...distances]
|
|
623
|
+
const shellNodes = normalizedRows
|
|
619
624
|
.sort((left, right) => {
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
const
|
|
625
|
+
const leftDelta = Math.abs(left.normalized - focus.shellCenter)
|
|
626
|
+
const rightDelta = Math.abs(right.normalized - focus.shellCenter)
|
|
627
|
+
const leftInside = leftDelta <= shellHalf ? 0 : 1
|
|
628
|
+
const rightInside = rightDelta <= shellHalf ? 0 : 1
|
|
629
|
+
if (leftInside !== rightInside) return leftInside - rightInside
|
|
624
630
|
if (leftDelta !== rightDelta) return leftDelta - rightDelta
|
|
631
|
+
const leftScore = state.nodeDegrees.get(left.node.id) ?? 0
|
|
632
|
+
const rightScore = state.nodeDegrees.get(right.node.id) ?? 0
|
|
633
|
+
if (leftScore !== rightScore) return rightScore - leftScore
|
|
625
634
|
return left.node.id.localeCompare(right.node.id)
|
|
626
635
|
})
|
|
627
|
-
.slice(0,
|
|
636
|
+
.slice(0, shellTarget)
|
|
628
637
|
.map((item) => item.node)
|
|
629
638
|
|
|
630
|
-
|
|
631
|
-
|
|
639
|
+
const merged = []
|
|
640
|
+
const ids = new Set()
|
|
641
|
+
const pushUnique = (node) => {
|
|
642
|
+
if (!node || ids.has(node.id)) return
|
|
643
|
+
ids.add(node.id)
|
|
644
|
+
merged.push(node)
|
|
632
645
|
}
|
|
633
646
|
|
|
634
|
-
|
|
647
|
+
if (state.transform.scale >= layeredCoreScaleThreshold) {
|
|
648
|
+
pushUnique(hub)
|
|
649
|
+
}
|
|
650
|
+
for (let index = 0; index < coreNodes.length; index += 1) pushUnique(coreNodes[index])
|
|
651
|
+
for (let index = 0; index < shellNodes.length; index += 1) pushUnique(shellNodes[index])
|
|
652
|
+
|
|
653
|
+
return merged.length > 0 ? merged : sourceNodes
|
|
635
654
|
}
|
|
636
655
|
|
|
637
656
|
const edgeIdentityKey = edge => {
|
|
@@ -1143,6 +1162,27 @@ const fitView = (options = { useFiltered: true, macro: false, preferHubCenter: t
|
|
|
1143
1162
|
|
|
1144
1163
|
const resetView = () => fitView({ useFiltered: false, macro: true, preferHubCenter: true })
|
|
1145
1164
|
|
|
1165
|
+
const focusPrimaryHub = () => {
|
|
1166
|
+
const hub = state.primaryHub
|
|
1167
|
+
if (!hub) {
|
|
1168
|
+
fitView({ useFiltered: true, macro: false, preferHubCenter: true })
|
|
1169
|
+
return
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const rect = canvas.getBoundingClientRect()
|
|
1173
|
+
const width = Math.max(rect.width, 320)
|
|
1174
|
+
const height = Math.max(rect.height, 320)
|
|
1175
|
+
const targetScale = clampScale(Math.max(0.78, state.transform.scale))
|
|
1176
|
+
|
|
1177
|
+
state.transform = {
|
|
1178
|
+
x: clampTransformCoordinate(width / 2 - hub.x * targetScale),
|
|
1179
|
+
y: clampTransformCoordinate(height / 2 - hub.y * targetScale),
|
|
1180
|
+
scale: targetScale
|
|
1181
|
+
}
|
|
1182
|
+
state.offscreenFrameCount = 0
|
|
1183
|
+
markRenderDirty()
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1146
1186
|
const createLayout = graph => {
|
|
1147
1187
|
const nodeRows = Array.isArray(graph.nodes) ? graph.nodes : []
|
|
1148
1188
|
const edgeRows = Array.isArray(graph.edges) ? graph.edges : []
|
|
@@ -1630,8 +1670,8 @@ const computeRenderVisibility = () => {
|
|
|
1630
1670
|
if (state.visibleNodes.length > massiveGraphNodeThreshold) {
|
|
1631
1671
|
const viewportNodes = viewportNodesFromSpatialIndex(viewport)
|
|
1632
1672
|
const sourceNodes = viewportNodes.length > 0 ? viewportNodes : state.visibleNodes
|
|
1633
|
-
const layeredNodes = selectLayeredNodesForScale(sourceNodes)
|
|
1634
1673
|
const sampleLimit = nodeBudgetForScale(state.transform.scale)
|
|
1674
|
+
const layeredNodes = selectLayeredNodesForScale(sourceNodes, sampleLimit)
|
|
1635
1675
|
const sampled = layeredNodes.length > sampleLimit
|
|
1636
1676
|
? sampleVisibleNodes(Math.min(sampleLimit, renderNodeBudget), layeredNodes)
|
|
1637
1677
|
: layeredNodes.slice(0, Math.min(layeredNodes.length, renderNodeBudget))
|
|
@@ -2020,8 +2060,8 @@ const wheelZoomFactor = event => {
|
|
|
2020
2060
|
return 1
|
|
2021
2061
|
}
|
|
2022
2062
|
|
|
2023
|
-
const baseStep = Math.max(0.
|
|
2024
|
-
const adjustedStep = baseStep * (isModifierZoom ? 1.
|
|
2063
|
+
const baseStep = Math.max(0.03, Math.min(0.2, absoluteDelta / 680))
|
|
2064
|
+
const adjustedStep = baseStep * (isModifierZoom ? 1.24 : 1)
|
|
2025
2065
|
|
|
2026
2066
|
return event.deltaY < 0 ? 1 + adjustedStep : 1 / (1 + adjustedStep)
|
|
2027
2067
|
}
|
|
@@ -2068,15 +2108,15 @@ const bindEvents = () => {
|
|
|
2068
2108
|
})
|
|
2069
2109
|
elements.zoomIn.addEventListener('click', () => {
|
|
2070
2110
|
const rect = canvas.getBoundingClientRect()
|
|
2071
|
-
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 1.
|
|
2111
|
+
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 1.14)
|
|
2072
2112
|
})
|
|
2073
2113
|
elements.zoomOut.addEventListener('click', () => {
|
|
2074
2114
|
const rect = canvas.getBoundingClientRect()
|
|
2075
|
-
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 0.
|
|
2115
|
+
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 0.88)
|
|
2076
2116
|
})
|
|
2077
2117
|
if (elements.fit) {
|
|
2078
2118
|
elements.fit.addEventListener('click', () => {
|
|
2079
|
-
|
|
2119
|
+
focusPrimaryHub()
|
|
2080
2120
|
})
|
|
2081
2121
|
}
|
|
2082
2122
|
elements.reset.addEventListener('click', () => {
|
|
@@ -2096,7 +2136,7 @@ const bindEvents = () => {
|
|
|
2096
2136
|
const rect = canvas.getBoundingClientRect()
|
|
2097
2137
|
const cursorX = event.clientX - rect.left
|
|
2098
2138
|
const cursorY = event.clientY - rect.top
|
|
2099
|
-
zoomAtPoint(cursorX, cursorY, 1.
|
|
2139
|
+
zoomAtPoint(cursorX, cursorY, 1.12)
|
|
2100
2140
|
})
|
|
2101
2141
|
canvas.addEventListener('pointerdown', event => {
|
|
2102
2142
|
const point = worldPoint(event)
|
|
@@ -2170,14 +2210,14 @@ const bindEvents = () => {
|
|
|
2170
2210
|
if (event.key === '+' || event.key === '=') {
|
|
2171
2211
|
event.preventDefault()
|
|
2172
2212
|
const rect = canvas.getBoundingClientRect()
|
|
2173
|
-
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 1.
|
|
2213
|
+
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 1.12)
|
|
2174
2214
|
return
|
|
2175
2215
|
}
|
|
2176
2216
|
|
|
2177
2217
|
if (event.key === '-' || event.key === '_') {
|
|
2178
2218
|
event.preventDefault()
|
|
2179
2219
|
const rect = canvas.getBoundingClientRect()
|
|
2180
|
-
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 0.
|
|
2220
|
+
zoomAtPoint(Math.max(rect.width, 320) / 2, Math.max(rect.height, 320) / 2, 0.89)
|
|
2181
2221
|
return
|
|
2182
2222
|
}
|
|
2183
2223
|
|
package/package.json
CHANGED