Use Graphology ForceAtlas2 Layout
A relation-graph example that renders sample data first, reads the measured node sizes, then runs a Graphology circular plus ForceAtlas2 pass and writes scaled coordinates back into a fixed-layout scene. It also adds dataset switching, layout-scale tuning, and a shared floating panel for canvas settings and image export.
Use Graphology ForceAtlas2 in a Fixed relation-graph Viewer
What This Example Builds
This example builds a full-height relation-graph viewer that loads one of two sample networks, lets relation-graph render the nodes once so their real sizes are available, then runs an external Graphology layout pass and writes the returned coordinates back into the live graph. The visual result is a light-weight network with circular white nodes, straight gray links, line labels drawn on the path, and a floating helper window above the canvas.
The user can switch datasets, stretch or compress the returned layout with a scale slider, drag or minimize the helper window, open canvas settings, and export an image. The main implementation point is the render-measure-relayout pipeline: relation-graph is responsible for rendering and interaction, while Graphology plus ForceAtlas2 is responsible for the final coordinates.
How the Data Is Organized
The graph data is declared inline inside initializeGraph() as an RGJsonData object with a rootId, a flat nodes array, and a flat lines array. The example keeps two in-memory datasets and swaps between them through local React state rather than loading from a remote source.
There is important preprocessing before the final layout appears. The code assigns synthetic line ids when they are missing, calls setJsonData() so relation-graph can render the graph, then reads each rendered node’s el_W and el_H before building a temporary Graphology graph. After the external layout finishes, the example multiplies the computed x and y values by layoutScale before writing them back into the fixed scene. In a real application, the same data shape could hold service dependencies, entity relationships, workflow links, collaboration networks, or any other node-link dataset that needs coordinates from an external solver.
How relation-graph Is Used
RGProvider wraps the page in the entry file so RGHooks.useGraphInstance() can resolve the active graph instance. The main graph options keep relation-graph in layoutName: 'fixed', which is the correct mode when coordinates come from outside the library. The same options also enable on-path line labels, circular nodes, straight links, border junction points, 60 by 60 fallback node sizes, and a minimal white-and-gray palette.
RGHooks.useGraphInstance() is the core integration surface. The example uses it to load JSON data with setJsonData(), read rendered nodes and lines with getNodes() and getLines(), write external coordinates back through updateNodePosition(), and recenter the result with moveToCenter() plus zoomToFit(). The floating settings overlay uses the same hook together with RGHooks.useGraphStore() so it can change wheel and drag behavior at runtime and run the image-export lifecycle with prepareForImageGeneration() and restoreAfterImageGeneration().
The graph itself does not use node, line, or view slots in this demo. It relies on relation-graph’s default rendering and uses the external layout flow as the main customization point. The local SCSS keeps overrides minimal: it mostly leaves extension hooks in place and defines a separate vertical-text variant for .my-layout-top, which is not activated by the current component.
Key Interactions
The most important interaction is the state-driven relayout flow. Changing the sample selector reloads a different inline dataset, rerenders the graph, and runs the external layout again. Changing the layout-scale slider does not alter ForceAtlas2 parameters. It reruns the coordinate writeback so the same layout result is spread farther apart or packed more tightly on the relation-graph canvas.
The helper window adds the secondary interactions. Users can drag it around the canvas, minimize it, open the settings panel, switch wheel behavior between scroll, zoom, and none, switch drag behavior between selection, move, and none, and download an image. Node clicks and line clicks are wired only for console inspection, so they do not change the graph or start another workflow.
Key Code Fragments
This options block proves the graph is intentionally kept in fixed-layout mode while relation-graph still provides the node and line rendering defaults.
const graphOptions: RGOptions = {
debug: false,
defaultLineTextOnPath: true,
layout: {
layoutName: 'fixed' // 使用自定义布局时,建议设为 fixed
},
defaultNodeShape: RGNodeShape.circle,
defaultLineShape: RGLineShape.StandardStraight,
defaultJunctionPoint: RGJunctionPoint.border
};
This initialization fragment shows the preprocessing step that adds synthetic line ids before relation-graph performs the first render.
myJsonData.lines.forEach((line, index) => {
if (!line.id) line.id = `L${index}`;
});
await graphInstance.setJsonData(myJsonData);
doMyLayout();
This fragment proves the external layout graph is built from relation-graph’s rendered nodes and live line list instead of from hard-coded geometry.
graphInstance.getNodes().forEach(node => {
graph.addNode(node.id, {
text: node.text,
width: node.el_W,
height: node.el_H
});
});
graphInstance.getLines().forEach(line => {
graph.addEdge(line.from, line.to, {
id: line.id,
weight: 1
});
});
This block shows the external Graphology pass and the scaled coordinate writeback into relation-graph.
circular.assign(graph);
forceAtlas2.assign(graph, 50);
graph.nodes().forEach(nodeId => {
const node = graph.getNodeAttributes(nodeId);
const rgNode = graphInstance.getNodeById(nodeId);
graphInstance.updateNodePosition(nodeId, node.x * layoutScale, node.y * layoutScale);
});
graphInstance.moveToCenter();
graphInstance.zoomToFit();
This effect pair proves that dataset changes trigger a full reload while layout-scale changes rerun the external writeback flow.
useEffect(() => {
initializeGraph();
}, [dataId]);
useEffect(() => {
doMyLayout();
}, [layoutScale]);
This helper fragment shows how the shared floating panel prepares the graph for export, captures the canvas, and restores the graph state afterward.
const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
backgroundColor: graphBackgroundColor
});
if (imageBlob) {
downloadBlob(imageBlob, 'my-image-name');
}
await graphInstance.restoreAfterImageGeneration();
What Makes This Example Distinct
Comparison data shows that this example is not the only external-layout demo, but it has a rarer combination than the nearby Dagre and built-in force examples. Its strongest pattern is a two-pass render-measure-relayout workflow that mirrors relation-graph nodes and lines into Graphology, seeds the graph with circular.assign(...), relaxes it with forceAtlas2.assign(...), then writes scaled coordinates back into a fixed relation-graph scene.
Relative to use-dagre-layout, this example emphasizes force-style experimentation, dataset switching, and coordinate rescaling instead of orthogonal structure, minimap navigation, or line-label placement heuristics. Relative to use-dagre-layout-2, it is less about tuning one algorithm’s spacing parameters and more about showing an external layout handoff recipe that can be rerun against different samples. Relative to layout-force, the important difference is where the layout intelligence lives: that example stays inside relation-graph’s built-in force solver, while this one hands layout work to Graphology and only uses relation-graph for rendering, interaction, and viewport management.
Where Else This Pattern Applies
This pattern transfers well to applications that already depend on Graphology, ForceAtlas2, or another external layout engine but still want relation-graph for rendering, viewport tools, and helper overlays. Typical migration targets include knowledge-graph viewers, dependency explorers, social-network inspection tools, asset relationship maps, and architecture diagrams where node sizes depend on rendered content.
It is also useful when the same underlying graph needs multiple presentation densities. A product can keep one solver result, then rescale the returned coordinates for overview mode, dense mode, kiosk display, or screenshot preparation without changing the source relationships or switching away from relation-graph’s fixed-layout rendering model.