Skip to content

Commit 7e4b223

Browse files
committed
fix: search zoom using graphToViewport + camera offset calculation
The camera zoom was blanking the graph because we were passing wrong coordinates. Sigma's camera.animate expects coordinates in its own normalized space, not raw graph coords or viewport pixels. New approach: use sigma.graphToViewport() to find where the target node is currently on screen, calculate the pixel offset from center, then adjust the camera state proportionally. This works regardless of the current zoom level or camera position. Same fix applied to double-click zoom.
1 parent 4cf5939 commit 7e4b223

2 files changed

Lines changed: 46 additions & 19 deletions

File tree

frontend/src/components/DependencyGraph/GraphView/SearchBar.tsx

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,35 @@ export function SearchBar({ onFocusNode }: SearchBarProps) {
4949
// pin this node (parent handles highlight via reducers)
5050
onFocusNode(nodeId)
5151

52-
// zoom: get the node's position in sigma's coordinate system
53-
// nodeDisplayData gives us the rendered position which we can use directly
52+
// zoom to node: use framedGraphToViewport to get correct camera position
53+
// sigma's camera x/y are in the graph's normalized coordinate space
5454
setTimeout(() => {
55-
const displayData = sigma.getNodeDisplayData(nodeId)
56-
if (displayData) {
57-
// convert viewport pixel coords to graph coords for the camera
58-
const graphCoords = sigma.viewportToGraph({ x: displayData.x, y: displayData.y })
59-
sigma.getCamera().animate(
60-
{ x: graphCoords.x, y: graphCoords.y, ratio: 0.2 },
61-
{ duration: 500 }
62-
)
63-
}
55+
const nodeAttrs = graph.getNodeAttributes(nodeId)
56+
const x = nodeAttrs.x as number
57+
const y = nodeAttrs.y as number
58+
59+
// get the graph bounding box to normalize coords
60+
const camera = sigma.getCamera()
61+
const { width, height } = sigma.getDimensions()
62+
63+
// convert graph position to a camera state that centers on the node
64+
// sigma internally normalizes graph coords, so we use graphToViewport
65+
// then figure out what camera state would put that at center
66+
const currentState = camera.getState()
67+
const viewCenter = sigma.graphToViewport({ x, y })
68+
69+
// how far off-center is the node currently?
70+
const dx = viewCenter.x - width / 2
71+
const dy = viewCenter.y - height / 2
72+
73+
// compute new camera position by shifting proportionally
74+
const newX = currentState.x + (dx / width) * currentState.ratio
75+
const newY = currentState.y + (dy / height) * currentState.ratio
76+
77+
camera.animate(
78+
{ x: newX, y: newY, ratio: 0.2 },
79+
{ duration: 500 }
80+
)
6481
}, 100)
6582

6683
setQuery('')

frontend/src/components/DependencyGraph/GraphView/index.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,24 @@ function Interactions({
107107
onSelectFile?.(node)
108108
},
109109
doubleClickNode: ({ node }) => {
110-
const displayData = sigma.getNodeDisplayData(node)
111-
if (displayData) {
112-
const graphCoords = sigma.viewportToGraph({ x: displayData.x, y: displayData.y })
113-
sigma.getCamera().animate(
114-
{ x: graphCoords.x, y: graphCoords.y, ratio: 0.12 },
115-
{ duration: 400 }
116-
)
117-
}
110+
// zoom to node using graphToViewport + camera offset calculation
111+
const graph = sigma.getGraph()
112+
if (!graph.hasNode(node)) return
113+
const attrs = graph.getNodeAttributes(node)
114+
const camera = sigma.getCamera()
115+
const { width, height } = sigma.getDimensions()
116+
const currentState = camera.getState()
117+
const viewPos = sigma.graphToViewport({ x: attrs.x as number, y: attrs.y as number })
118+
const dx = viewPos.x - width / 2
119+
const dy = viewPos.y - height / 2
120+
camera.animate(
121+
{
122+
x: currentState.x + (dx / width) * currentState.ratio,
123+
y: currentState.y + (dy / height) * currentState.ratio,
124+
ratio: 0.12,
125+
},
126+
{ duration: 400 }
127+
)
118128
},
119129
clickStage: () => {
120130
// clear pinned state when clicking empty space

0 commit comments

Comments
 (0)