Bidirectional Tree Line Direction FAQ
This example builds a side-by-side bidirectional tree comparison that explains how upper-branch arrowheads can be visually reversed without changing the top-down data model required by the layout. It uses two provider-scoped graph instances, one shared tree configuration, and a small inline RGJsonData dataset whose right-panel variant changes only upper-line arrow settings and color.
Explaining Bidirectional Tree Arrow Direction with Two Matched Graph Views
What This Example Builds
This example builds a full-height comparison page with two relation-graph canvases and a fixed explanatory column between them. Both canvases render the same root-centered bidirectional tree: one branch set grows upward toward the R-* nodes, and another grows downward toward the child nodes below a.
The visible difference is concentrated in the upper half of the tree. The left graph shows the baseline presentation, while the right graph colors the upper links red and changes their visible arrowheads so those links appear to point upward. Users can click nodes and lines to inspect the underlying objects in the console, but the main experience is the side-by-side visual comparison. The key point is that the example changes perceived line direction without changing the top-down relationship structure required by the tree layout.
How the Data Is Organized
The graph data is created inline by getMyJsonData(forRightPanel) in MyGraph.tsx. It returns one RGJsonData object with rootId: 'a', a nodes array, and a lines array. There is no external fetch and no preprocessing pipeline before setJsonData(). The only transformation is inside that helper: when forRightPanel is true, it assigns showStartArrow = true, showEndArrow = false, and color = '#ff0000' before constructing the upper-branch lines.
That structure keeps the comparison narrow. The same rooted tree model is reused in both panes, and only the line metadata for the upper branch changes. The dataset also sets expandHolderPosition: 'top' on R-c and expandHolderPosition: 'bottom' on c, which reinforces the bidirectional tree behavior around the same root.
In a real project, this data shape could represent upstream and downstream services, parent and child records in a genealogy or org chart, prerequisites and follow-up tasks, or causes and effects in an incident analysis tree. The important transferable idea is that the graph can keep one consistent relationship model while varying the rendered arrow semantics on selected branches.
How relation-graph Is Used
index.tsx creates the page shell and wraps each graph pane in its own RGProvider, so the left and right canvases have separate graph-instance contexts. MyGraph.tsx uses RGHooks.useGraphInstance() inside each pane component, then runs the same mount-time initialization sequence: updateOptions(), setJsonData(), moveToCenter(), and zoomToFit().
The shared graphOptions object uses a tree layout with layoutName: 'tree' and from: 'top', so the structure is arranged vertically. It also sets treeNodeGapH: 20, treeNodeGapV: 100, rectangular nodes, RGLineShape.SimpleOrthogonal, fixed default node dimensions, and RGJunctionPoint.tb so vertical connections stay legible in the two-way tree.
This example does not use node slots, line slots, canvas slots, editing APIs, or runtime mode switches. It relies on built-in rendering plus per-line metadata inside the dataset. Styling is minimal and local: my-relation-graph.scss scopes node text to black, 14px text inside the .my-graph wrapper, while most of the page framing comes from inline layout styles in index.tsx.
Key Interactions
The primary interaction is comparison rather than manipulation. The page presents two pre-rendered graph states at the same time, so the viewer can inspect how one line-option change affects only the upper branch.
Node clicks and line clicks are wired in both graphs, but they only log the selected RGNode or RGLine object. They do not expand branches, edit the graph, or switch between variants. The two panes are also initialized automatically on mount, so the comparison is ready without any setup controls.
Key Code Fragments
This fragment shows that the right-hand variant is created by toggling arrow flags and color before the dataset is built.
const getMyJsonData = (forRightPanel = false): RGJsonData => {
let showStartArrow, showEndArrow, color;
if (forRightPanel) {
showStartArrow = true;
showEndArrow = false;
color = '#ff0000';
}
This fragment shows that the upper branch keeps the same from and to structure while reusing those variant-specific line properties.
lines: [
{ from: 'R-b', to: 'a', showStartArrow, showEndArrow, color },
{ from: 'R-c', to: 'a', showStartArrow, showEndArrow, color },
{ from: 'R-c-1', to: 'R-c', showStartArrow, showEndArrow, color },
{ from: 'R-c-2', to: 'R-c', showStartArrow, showEndArrow, color },
{ from: 'R-d', to: 'a', showStartArrow, showEndArrow, color },
{ from: 'a', to: 'b' },
This fragment shows the shared tree configuration that both panes use.
const graphOptions: RGOptions = {
debug: false,
layout: {
layoutName: 'tree',
from: 'top',
treeNodeGapH: 20,
treeNodeGapV: 100,
},
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.SimpleOrthogonal,
defaultNodeWidth: 80,
defaultNodeHeight: 40,
defaultJunctionPoint: RGJunctionPoint.tb
};
This fragment shows that each pane uses the graph instance API to load data and fit the viewport, with the right pane selecting the variant dataset.
useEffect(() => {
const init = async () => {
graphInstance.updateOptions(graphOptions);
await graphInstance.setJsonData(getMyJsonData(true));
graphInstance.moveToCenter();
graphInstance.zoomToFit();
};
init();
}, []);
This fragment shows that the page is intentionally composed as a left graph, an explanatory note column, and a right graph.
<div className="my-graph" style={{ display: 'flex' }}>
<div style={{ height: 'calc(100vh)', width: 'calc((100% - 300px) / 2)' }}>
<RGProvider>
<MyGraphLeft />
</RGProvider>
</div>
<div style={{ height: 'calc(100vh)', width: '300px', borderLeft: '#efefef solid 1px', borderRight: '#efefef solid 1px', padding: '15px', fontSize: '14px', lineHeight: '25px', overflowY: 'auto' }}>
What Makes This Example Distinct
The comparison analysis makes this example’s role clear: it is not a general tree demo and not a broad line-feature catalog. Its distinctive value is that it answers one specific bidirectional-tree FAQ by showing two nearly identical graph instances at once. The only intended lesson is how parent-side links can look reversed by changing showStartArrow and showEndArrow, while the underlying top-down relationship data still stays compatible with the layout.
Compared with bothway-tree2, this example is much narrower and more static. bothway-tree2 explores orientation switching and branch-specific tuning inside one graph, while this example hard-codes two states in separate panes so the arrow-direction lesson is easier to isolate. Compared with advanced-line-usage and line, this example is more hierarchy-specific: it is not teaching a catalog of line shapes, junction anchors, or label styles, but one ambiguity that only becomes meaningful in a root-centered two-way tree.
Another uncommon detail is the surrounding scaffold. The fixed center note column, duplicated providers, opposite expand-holder positions on the upper and lower branches, and red upper-line variant together make the example read like a FAQ answer rather than a playground. That makes it a stronger starting point when the requirement is to explain one rendering rule clearly instead of exposing many controls.
Where Else This Pattern Applies
This pattern transfers well to technical documentation, QA samples, and onboarding material where a team needs to prove the visual effect of one graph option change. A before-and-after pair of canvases is often clearer than a runtime toggle when the goal is to explain why one rendering is correct.
The same structure also fits upstream and downstream dependency trees, parent and child entity diagrams, lineage views, approval hierarchies, and fault-analysis trees. In those cases, the graph can preserve one stable data direction for layout and processing while using line metadata to present branch direction in the way users expect to read it.