Camera & Coordinates
Nodus draws an unbounded graph space through a movable camera onto a fixed screen. Understanding the two coordinate spaces — and the camera that relates them — is the key to positioning elements, reading pointer input, and animating the view.
Everything here lives under nodus.view.*. For task-focused recipes see
Controlling the Camera; for full signatures
see the View API.
Two coordinate spaces
| Space | Unit | Origin | Used for |
|---|---|---|---|
| Graph / world | graph units | the layout | node positions, layout, geometry |
| Screen / pixels | CSS pixels | the container | pointer events, DOM overlays |
- A node’s
getPosition()is in graph coordinates — stable regardless of where the camera is looking. - A mouse event’s
clientX/clientYis in screen coordinates — relative to the container.
The camera is what converts between them.
The camera
The camera has three properties:
- center — the graph point currently at the middle of the screen.
- zoom — the scale factor (larger = more magnified).
- angle — the rotation of the view.
You read and set them through nodus.view:
// Zoomnodus.view.getZoom();nodus.view.setZoom(2, { duration: 300 });nodus.view.zoomIn();nodus.view.zoomOut();
// Pan / centernodus.view.getCenter(); // { x, y } in graph coordsnodus.view.setCenter({ x: 0, y: 0 });nodus.view.move({ x: 50, y: 0 }); // pan by a deltanodus.view.moveTo(target); // pan a target to centernodus.view.moveToBounds(bounds); // frame a bounding box
// Rotatenodus.view.getAngle();nodus.view.setAngle(Math.PI / 8);nodus.view.rotate(0.1); // rotate by a delta
// The whole view at onceconst state = nodus.view.get();nodus.view.set(state, { duration: 400 });Converting between spaces
When you need to place a DOM overlay at a node, or turn a click into a graph point, convert explicitly:
// Graph -> screen: where does this node land on screen right now?const screenPt = nodus.view.graphToScreenCoordinates(node.getPosition());tooltip.style.left = `${screenPt.x}px`;tooltip.style.top = `${screenPt.y}px`;
// Screen -> graph: what graph point did the user click?const graphPt = nodus.view.screenToGraphCoordinates({ x: event.clientX, y: event.clientY });These conversions account for the camera’s center, zoom and angle, so they stay correct even when the view is panned, zoomed or rotated.
Try it live — move the camera and watch screen and graph coordinates track each other:
Fitting the graph
To frame the whole graph in the viewport — the thing you almost always want
after a layout — use locateGraph, which returns a Promise<void>:
await nodus.layouts.force({ duration: 0 });await nodus.view.locateGraph();You can also fit a graph you haven’t loaded yet (e.g. to preview framing) with
locateRawGraph(graph), or pan/zoom to a single element with element.locate().
Try it live — fit the whole graph, then zoom and focus a single node:
Hit-testing and queries
The view answers spatial questions about what’s under or within a region:
// What element is at this screen point?const hit = nodus.view.getElementAt({ x: event.clientX, y: event.clientY });
// What's currently visible?const visible = nodus.view.getElementsInView();
// What's inside a rectangle? (screen coords by default)const inside = nodus.view.getElementsInside(x1, y1, x2, y2);// pass inGraphCoords = true to query in graph space:const inGraph = nodus.view.getElementsInside(gx1, gy1, gx2, gy2, true);Related helpers include getBounds(), getGraphBoundingBox(),
getTextBoundingBox(el) and getNodeBadgeAt(node, position). These power
custom selection, context menus and overlays — see
Handling Events.
Sizing
The drawing surface tracks the container, but you can read and control it:
nodus.view.getSize(); // { width, height } in pixelsawait nodus.view.setSize({ width: 800, height: 600 });nodus.view.forceResize(); // re-measure after a layout changeforceResize() is the call to make after the container changes size in a way
Nodus can’t observe automatically (for example, a sidebar opening). The renderer
matches the device pixel ratio so the result stays crisp on high-DPI displays —
see The Rendering Pipeline.
You can also go fullscreen: nodus.view.setFullScreen(true) /
isFullScreen().
Animating and frame synchronization
Most camera moves accept an options object whose { duration } (ms) animates
the transition — the same convention as Layouts:
await nodus.view.setZoom(3, { duration: 500 }); // smooth zoomnodus.view.moveTo(node.getPosition(), { duration: 400 });To coordinate work with the render loop, await a frame boundary:
await nodus.view.beforeNextFrame(); // resolves just before the next frame// ... measure / set up overlays in sync with rendering ...await nodus.view.afterNextFrame(); // resolves just after it has drawnnodus.view.animationInProgress() tells you whether a camera animation is
currently running — handy for not interrupting one. For a raster snapshot of the
current frame, use nodus.view.getImageData().
Where to go next
- Controlling the Camera — practical pan/zoom/fit recipes.
- API: View — every view method and its options.
- The Rendering Pipeline — sizing, DPI and how frames are drawn.
- The Graph Model — node positions live in graph space.