JavaScript is required

Layout System

Purpose and Scope

The Layout System calculates spatial coordinates (x, y) for graph nodes using various algorithms. It implements the Strategy Pattern where RelationGraphWith6Layout.doLayout() dispatches to algorithm-specific classes (RGTreeLayout, RGForceLayout, etc.) that compute positions based on network topology.

This page documents the layout architecture, algorithm implementations, and execution lifecycle. See Data Management System for node/line data structures and View Control & Effects for zoom/pan operations that interact with layout coordinates.

Architecture Overview

The Layout System implements the Strategy Pattern with a factory method for instantiation. All layout algorithms inherit from an abstract base class and share common infrastructure while implementing algorithm-specific positioning logic.

Layout Class Hierarchy

Class Hierarchy Diagram

graph TB
    RGWith6Layout["RelationGraphWith6Layout
doLayout()
createLayout()
placeOtherNodes()"] RGBaseLayout["RGBaseLayout
abstract
placeNodes()
updateNodePosition()
layoutEnd()"] NetworkAnalyzer["BasicNetworkAnalyzer
analyzeNetwork()"] NodesAnalytic["RGNodesAnalytic
getNodeXByLotX()
getNodeYByLotY()"] GraphMath["RGGraphMath
getOvalPoint()
getBorderPoint4MultiLine()"] RGTreeLayout["RGTreeLayout
from: left|right|top|bottom
placeRelativePosition()"] RGIOTreeLayout["RGIOTreeLayout
input/output trees
buildNetwork()"] RGFolderLayout["RGFolderLayout
folder hierarchy
enableGatherNodes"] RGForceLayout["RGForceLayout
physics simulation
start()/stop()
doForceLayout()"] RGCenterLayout["RGCenterLayout
extends RGForceLayout
radial positioning"] RGCircleLayout["RGCircleLayout
extends RGForceLayout
pure circular"] RGSmartTreeLayout["RGSmartTreeLayout
tree + force hybrid"] RGFixedLayout["RGFixedLayout
preserves positions"] RGWith6Layout -->|"instantiates via factory"| RGBaseLayout RGBaseLayout -->|"uses"| NetworkAnalyzer RGBaseLayout -->|"uses"| NodesAnalytic RGBaseLayout -->|"uses"| GraphMath RGBaseLayout -->|"extends"| RGTreeLayout RGBaseLayout -->|"extends"| RGIOTreeLayout RGBaseLayout -->|"extends"| RGFolderLayout RGBaseLayout -->|"extends"| RGForceLayout RGBaseLayout -->|"extends"| RGFixedLayout RGForceLayout -->|"extends"| RGCenterLayout RGForceLayout -->|"extends"| RGCircleLayout RGForceLayout -->|"extends"| RGSmartTreeLayout

Sources: packages/relation-graph-models/models/RelationGraphWith6Layout.ts:1-20, packages/relation-graph-models/layouts/RGBaseLayout.ts:1-143, packages/relation-graph-models/layouts/RGTreeLayout.ts:8-60, packages/relation-graph-models/layouts/RGForceLayout.ts:25-57

RGLayout Interface Contract

All layout implementations implement the RGLayout interface defined in packages/relation-graph-models/types.ts:556-567:

Property/Method Type Description
isMainLayouer boolean Identifies primary layout (controls auto-layout behavior)
requireLinks boolean If true, setLinks() must be called before placeNodes()
allNodes RGNode[] Nodes to position (assigned by placeNodes())
rootNode? RGNode Starting node for hierarchical algorithms
layoutOptions RGLayoutOptions Algorithm-specific configuration
networkAnalyzer RGNetworkAnalyzer Instance of BasicNetworkAnalyzer
setLinks() (links: RGLink[]) => void Provides line data (required if requireLinks=true)
placeNodes() (allNodes: RGNode[], rootNode?: RGNode) => void Main algorithm execution—sets node.x, node.y
start()? () => void Start animation loop (force layouts only)
stop()? () => void Stop animation loop (force layouts only)

Sources: packages/relation-graph-models/types.ts:556-567

Layout Factory and Configuration

createLayout() Factory Method

Layout Factory Dispatch

graph TB
    LayoutOptions["layoutOptions
{layoutName: string}"] createLayout["createLayout<T extends RGLayout>()
RelationGraphWith6Layout:250-275"] TreeCase["case 'tree':
new RGTreeLayout()"] ForceCase["case 'force':
new RGForceLayout()"] CenterCase["case 'center':
new RGCenterLayout()"] CircleCase["case 'circle':
new RGCircleLayout()"] FixedCase["case 'fixed':
new RGFixedLayout()"] SmartTreeCase["case 'smart-tree':
new RGSmartTreeLayout()"] IOTreeCase["case 'io-tree':
new RGIOTreeLayout()"] FolderCase["case 'folder':
new RGFolderLayout()"] LayoutOptions --> createLayout createLayout -->|"layoutName='tree'"| TreeCase createLayout -->|"layoutName='force'"| ForceCase createLayout -->|"layoutName='center'"| CenterCase createLayout -->|"layoutName='circle'"| CircleCase createLayout -->|"layoutName='fixed'"| FixedCase createLayout -->|"layoutName='smart-tree'"| SmartTreeCase createLayout -->|"layoutName='io-tree'"| IOTreeCase createLayout -->|"layoutName='folder'"| FolderCase

The factory applies default options via appendDefaultOptions4Layout() before instantiation. Each layout class receives (layoutOptions, graphOptions, graphInstance) constructor parameters.

Sources: packages/relation-graph-models/models/RelationGraphWith6Layout.ts:250-275, packages/relation-graph-models/data/RGOptionsDataUtils.ts (referenced for appendDefaultOptions4Layout)

Layout Options Type Hierarchy

Type Definitions: RGLayoutOptions

graph TB
    RGLayoutOptionsCore["RGLayoutOptionsCore
types.ts:478-484
━━━━━━━━━━━━━━
layoutName: string
layoutDirection?: 'h'|'v'
fixedRootNode?: boolean
alignItemsX?: start|center|end
alignItemsY?: start|center|end"] RGForceLayoutOptions["RGForceLayoutOptions
types.ts:495-502
━━━━━━━━━━━━━━
fastStart?: boolean
maxLayoutTimes?: number
byNode?: boolean
byLine?: boolean
force_node_repulsion?: number
force_line_elastic?: number"] RGCenterLayoutOptions["RGCenterLayoutOptions
types.ts:508-511
━━━━━━━━━━━━━━
distance_coefficient?: number
levelDistance?: number[]"] RGTreeLayoutOptions["RGTreeLayoutOptions
types.ts:524-534
━━━━━━━━━━━━━━
from: left|right|top|bottom
treeNodeGapH?: number
treeNodeGapV?: number
levelGaps?: number[]
layoutExpansionDirection?: start|center|end
simpleTree?: boolean
ignoreNodeSize?: boolean
alignParentItemsX?: start|center|end
alignParentItemsY?: start|center|end"] RGLayoutOptionsCore -->|"extends"| RGForceLayoutOptions RGForceLayoutOptions -->|"extends"| RGCenterLayoutOptions RGLayoutOptionsCore -->|"extends"| RGTreeLayoutOptions

Sources: packages/relation-graph-models/types.ts:478-550

Layout Families

Tree-Based Layouts

Tree layouts organize nodes in hierarchical structures based on network topology. They traverse the graph starting from a root node and place descendants at calculated offsets.

RGTreeLayout

Standard tree layout with configurable direction and alignment.

Key Features:

  • Direction: Supports 'left', 'right', 'top', 'bottom' via from option
  • Alignment: Configurable alignItemsX, alignItemsY for node positioning within layout cells
  • Spacing: treeNodeGapH and treeNodeGapV control gaps between nodes
  • Level Gaps: levelGaps[] allows custom spacing between hierarchy levels
  • Bidirectional: By default expands both up and down the relationship hierarchy unless simpleTree=true

Configuration Example:

layoutOptions: {
  layoutName: 'tree',
  from: 'left',
  treeNodeGapH: 50,
  treeNodeGapV: 10,
  alignItemsX: 'end',
  alignItemsY: 'center',
  levelGaps: [100, 150, 200]
}

Sources: packages/relation-graph-models/layouts/RGTreeLayout.ts:1-730

RGIOTreeLayout

Variant designed for input-output tree structures where relationships have clear directional semantics.

Differences from RGTreeLayout:

  • Optimized for graphs with explicit input/output node patterns
  • Enhanced handling of nodes with mixed parent/child relationships
  • Special logic for calculating childrenSize and visibility

Sources: packages/relation-graph-models/layouts/RGIOTreeLayout.ts:1-478

RGFolderLayout

Specialized tree layout mimicking folder hierarchy visualization.

Key Features:

  • Indentation-based visual hierarchy
  • Configurable bottomJuctionPointOffsetX for connector line positioning
  • Built-in gather nodes capability via enableGatherNodes option
  • Optimized for file system or organizational chart visualizations

Sources: packages/relation-graph-models/layouts/RGFolderLayout.ts:1-374

Force-Directed Layouts

Force-directed layouts use physics simulation to position nodes. They apply attractive forces along connections and repulsive forces between nodes to achieve balanced, organic layouts.

RGForceLayout

Core physics-based layout using iterative simulation.

Physics Model:

  • Node Repulsion: Nodes push each other away based on force_node_repulsion coefficient
  • Line Elasticity: Connected nodes attract each other based on force_line_elastic coefficient
  • Iteration: Runs up to maxLayoutTimes iterations or until forces balance

Key Configuration:

layoutOptions: {
  layoutName: 'force',
  fastStart: false,           // Skip initial positioning
  maxLayoutTimes: 300,         // Maximum iterations
  byNode: true,                // Enable node repulsion
  byLine: true,                // Enable line attraction
  force_node_repulsion: 1,     // Repulsion strength
  force_line_elastic: 1        // Attraction strength
}

Animation Support: The force layout supports live animation via start() and stop() methods. When autoLayouting is enabled, the layout continuously updates node positions.

Sources: packages/relation-graph-models/layouts/RGForceLayout.ts:1-450

RGCenterLayout

Extends force layout with radial positioning around a center node.

Key Features:

  • Initial placement uses radial/circular positioning from root
  • distance_coefficient scales the ideal distance between levels
  • levelDistance[] array specifies custom distances for each level
  • Inherits all force simulation capabilities

Sources: packages/relation-graph-models/layouts/RGCenterLayout.ts:1-90

RGCircleLayout

Pure circular arrangement without force simulation.

Key Features:

  • Places visible nodes in a circle around root
  • Automatic radius calculation based on node count
  • No physics simulation—static positioning
  • Radius clamped between 200 and 800 units

Sources: packages/relation-graph-models/layouts/RGCircleLayout.ts:1-59

RGSmartTreeLayout

Hybrid layout combining tree structure with force refinement.

Algorithm:

  1. Analyze network topology using tree analysis
  2. Place nodes in initial tree structure
  3. Apply force-directed adjustments for final positioning

Key Features:

  • Best of both worlds: hierarchical organization with organic spacing
  • Supports all tree alignment options
  • Rotation support via rotate option
  • Bidirectional tree analysis

Sources: packages/relation-graph-models/layouts/RGSmartTreeLayout.ts:1-232

Fixed Layout

Preserves user-assigned node positions without modification.

Use Cases:

  • User has manually positioned nodes
  • Loading saved graph layouts
  • Static diagrams with predetermined coordinates

Behavior:

  • Performs network analysis but skips position calculation
  • Respects node.fixed property
  • Simply calls layoutEnd() to complete lifecycle

Sources: packages/relation-graph-models/layouts/RGFixedLayout.ts:1-31

Layout Execution Process

doLayout() Execution Flow

Method Call Sequence: RelationGraphWith6Layout.doLayout()

sequenceDiagram
    participant User
    participant doLayout["doLayout()
:30-45"] participant _doLayout["_doLayout()
:51-96"] participant createLayout["createLayout()
:250-275"] participant LayoutImpl["Layout Implementation
(e.g. RGTreeLayout)"] participant NetworkAnalyzer["BasicNetworkAnalyzer"] User->>doLayout: doLayout(customRootNode?) doLayout->>doLayout: sleep(300 - timeSinceLastAdd) doLayout->>_doLayout: _doLayout(customRootNode) _doLayout->>_doLayout: updateNodesVisibleProperty() _doLayout->>_doLayout: getNodes() _doLayout->>_doLayout: getRootNode() or use first node _doLayout->>createLayout: createLayout(options.layout, true) createLayout-->>_doLayout: layoutInstance alt layoutName === 'force' _doLayout->>LayoutImpl: placeNodes(allNodes, rootNode) LayoutImpl->>NetworkAnalyzer: analyzeNetwork() LayoutImpl->>LayoutImpl: Initial positioning Note over LayoutImpl: Auto-layout will start via
start() if isMainLayouer else Tree/Other Layouts _doLayout->>LayoutImpl: placeNodes(allNodes, rootNode) LayoutImpl->>NetworkAnalyzer: analyzeNetwork() LayoutImpl->>LayoutImpl: Calculate positions LayoutImpl->>LayoutImpl: layoutEnd() LayoutImpl-->>_doLayout: positions calculated _doLayout->>_doLayout: placeOtherNodes(mainGroupNodes) end _doLayout->>_doLayout: forEach node: updateNode(id, {x, y}) _doLayout->>_doLayout: updateElementLines()

Sources: packages/relation-graph-models/models/RelationGraphWith6Layout.ts:30-96

Layout Lifecycle Methods

Method Location Signature Description
placeNodes() Layout implementations (allNodes: RGNode[], rootNode?: RGNode) => void Main positioning algorithm—must set node.x, node.y
layoutEnd() RGBaseLayout:132-143 () => void Emits onLayoutCompleted event
updateNodePosition() RGBaseLayout:132-143 (node: RGNode, x: number, y: number) => void Calls graphInstance.updateNode(node.id, {x, y})
placeOtherNodes() RelationGraphWith6Layout:117-153 (mainGroupNodes: RGNode[]) => void Recursively layouts disconnected components
placeSingleNodes() RelationGraphWith6Layout:159-176 (singleNodes: RGNode[]) => void Grid layout via rgSimpleGridLayout()
sortGroups() RelationGraphWith6Layout:182-210 (groupList: {nodes: RGNode[]}[]) => void Arranges multiple component groups

Sources: packages/relation-graph-models/layouts/RGBaseLayout.ts:132-143, packages/relation-graph-models/models/RelationGraphWith6Layout.ts:117-210

Network Analysis Infrastructure

BasicNetworkAnalyzer

Network Topology Analysis Flow

graph TB
    Input["Input:
allNodes: RGNode[]
rootNode: RGNode"] Analyzer["BasicNetworkAnalyzer
analyzeNetwork()"] BuildTree["buildTree()
━━━━━━━━━━━━━━
Traverse from root
Set node.lot.level
Set node.lot.childs
Set node.lot.parent"] CalcStrength["calcStrength()
━━━━━━━━━━━━━━
Calculate subtree sizes
Set node.lot.strength"] Output["Output:
tree.networkNodes
tree.analyticResult
━━━━━━━━━━━━━━
max_deep: number
max_strength: number"] ReverseTree["Optional:
reverseTree analysis
(if bidirectional=true)"] Input --> Analyzer Analyzer --> BuildTree BuildTree --> CalcStrength CalcStrength --> Output Analyzer -.-> ReverseTree

node.lot Structure (types.ts:250-271)

The node.lot object stores layout-specific metadata populated by BasicNetworkAnalyzer:

node.lot = {
  childs: RGNode[],              // Direct children (populated by buildTree)
  parent?: RGNode,               // Parent node reference
  level?: number,                // Hierarchy depth from root (0 = root)
  strength?: number,             // Subtree size (node count in subtree)
  childrenSize?: number,         // Count of direct children
  childrenSizeVisible?: number,  // Count of visible children (expanded)
  x?: number,                    // Temporary layout coordinate
  y?: number,                    // Temporary layout coordinate
  // Additional properties used by specific layouts
}

Sources: packages/relation-graph-models/layouts/analyzers/BasicNetworkAnalyzer.ts (referenced), packages/relation-graph-models/types.ts:250-271

Coordinate Systems and Alignment

Coordinate Transformation Pipeline

graph LR
    LotXY["node.lot.x
node.lot.y
━━━━━━━━━━━━━━
Layout coordinates
(logical position)"] Align["RGNodesAnalytic
getNodeXByLotX()
getNodeYByLotY()
━━━━━━━━━━━━━━
Apply alignItemsX/Y"] NodeXY["node.x
node.y
━━━━━━━━━━━━━━
Canvas coordinates
(render position)"] LotXY -->|"alignment transform"| Align Align --> NodeXY

Alignment Options (types.ts:466-469)

alignItemsX/Y Effect on Coordinate
'start' node.x = lot.x (top-left corner at lot position)
'center' node.x = lot.x - node.el_W / 2 (center at lot position)
'end' node.x = lot.x - node.el_W (bottom-right at lot position)

Example Usage in RGTreeLayout:

// After calculating lot.x, lot.y in layout space:
const x = RGNodesAnalytic.getNodeXByLotX({alignItemsX: 'end'}, thisNode);
const y = RGNodesAnalytic.getNodeYByLotY({alignItemsY: 'center'}, thisNode);
this.updateNodePosition(thisNode, x, y);

Sources: packages/relation-graph-models/utils/RGNodesAnalytic.ts (referenced), packages/relation-graph-models/types.ts:466-469, packages/relation-graph-models/layouts/RGTreeLayout.ts:118-122

Supporting Utilities

RGNodesAnalytic

Node Measurement & Positioning Utilities

Located in packages/relation-graph-models/utils/RGNodesAnalytic.ts, this module provides:

Function Signature Purpose
getNodeWidth() (node: RGNode) => number Returns node.width || node.el_W
getNodeHeight() (node: RGNode) => number Returns node.height || node.el_H
getNodeXByLotX() (alignOption, node) => number Transforms lot.xx based on alignItemsX
getNodeYByLotY() (alignOption, node) => number Transforms lot.yy based on alignItemsY
getNodeLotXY() (alignOption, node) => {x, y} Reverse transform: x,ylot.x, lot.y
isVisibleNode() (node: RGNode) => boolean Checks visibility considering parent expansion

Sources: packages/relation-graph-models/utils/RGNodesAnalytic.ts (referenced)

RGGraphMath

Mathematical Operations for Layouts

Located in packages/relation-graph-models/utils/RGGraphMath.ts:

Function Signature Purpose
getOvalPoint() (cx, cy, r, index, total) => {x, y} Point on ellipse for circular layouts
getBorderPoint4MultiLine() (params: CreateJunctionPointParams) => {x, y} Line-node junction point
getRotatedPoint() (x, y, cx, cy, angle) => {x, y} Rotate coordinate around center
getFlippedX() (x, centerX) => number Mirror X coordinate
getFlippedY() (y, centerY) => number Mirror Y coordinate
getNodeDistance() (x1, y1, x2, y2) => number Euclidean distance
rgSimpleGridLayout() (columns, gap, items[], callback) Grid positioning algorithm

Sources: packages/relation-graph-models/utils/RGGraphMath.ts:1-20 (CreateJunctionPointParams), packages/relation-graph-models/utils/RGGraphMath.ts (functions referenced)

Layout Configuration Options

Core Options (All Layouts)

interface RGLayoutOptionsCore {
  layoutName: string;           // 'tree' | 'force' | 'center' | 'circle' | 'fixed' | 'smart-tree' | 'io-tree' | 'folder'
  layoutDirection?: 'h' | 'v'; // Horizontal or vertical
  fixedRootNode?: boolean;      // Keep root at current position
  alignItemsX?: 'start' | 'center' | 'end';
  alignItemsY?: 'start' | 'center' | 'end';
}

Tree Layout Options

interface RGTreeLayoutOptions extends RGLayoutOptionsCore {
  from: 'left' | 'right' | 'top' | 'bottom';
  treeNodeGapH?: number;                    // Horizontal gap between nodes
  treeNodeGapV?: number;                    // Vertical gap between nodes
  levelGaps?: number[];                     // Custom gap for each level
  layoutExpansionDirection?: 'start' | 'center' | 'end';
  simpleTree?: boolean;                     // Unidirectional expansion
  ignoreNodeSize?: boolean;                 // Treat all nodes as same size
  alignParentItemsX?: 'start' | 'center' | 'end';
  alignParentItemsY?: 'start' | 'center' | 'end';
}

Force Layout Options

interface RGForceLayoutOptions extends RGLayoutOptionsCore {
  fastStart?: boolean;          // Skip initial center layout
  maxLayoutTimes?: number;      // Max iterations (default: 300)
  byNode?: boolean;             // Enable node repulsion
  byLine?: boolean;             // Enable line attraction
  force_node_repulsion?: number; // Repulsion coefficient (default: 1)
  force_line_elastic?: number;   // Attraction coefficient (default: 1)
}

Center Layout Options

interface RGCenterLayoutOptions extends RGForceLayoutOptions {
  distance_coefficient?: number; // Scale ideal distances
  levelDistance?: number[];      // Custom distance per level
}

Sources: packages/relation-graph-models/types.ts:478-550

Multi-Network Handling

Disconnected Component Layout Strategy

graph TB
    AllNodes["getNodes()
all graph nodes"] Classify["Classify Nodes
RelationGraphWith6Layout:117-153"] MainGroup["mainGroupNodes
getNetworkNodesByNode(root)"] OtherNetworks["notInMainGroupNodes
disconnected components"] SingleNodes["singleNodes
no connections"] MainLayout["Layout main network
doLayout(rootNode)"] PlaceNext["placeNextNetwork()
:216-248
━━━━━━━━━━━━━━
Recursively layout
each component"] PlaceSingle["placeSingleNodes()
:159-176
━━━━━━━━━━━━━━
rgSimpleGridLayout()"] SortGroups["sortGroups()
:182-210
━━━━━━━━━━━━━━
Arrange groups
in grid pattern"] AllNodes --> Classify Classify --> MainGroup Classify --> OtherNetworks Classify --> SingleNodes MainGroup --> MainLayout OtherNetworks --> PlaceNext SingleNodes --> PlaceSingle MainLayout --> SortGroups PlaceNext --> SortGroups PlaceSingle --> SortGroups

Algorithm Details:

  1. Main Network: Layout via configured algorithm with root node
  2. Disconnected Components: placeNextNetwork() recursively:
    • Pick first unplaced node as new root
    • Call createLayout() with same algorithm
    • If force layout, set maxLayoutTimes=0 to skip animation
    • Set fixedRootNode=true and position at (0,0)
    • Add to groupList
  3. Isolated Nodes: placeSingleNodes() uses rgSimpleGridLayout() with calculated columns
  4. Final Arrangement: sortGroups() treats each component as a “super-node” and arranges in grid

Sources: packages/relation-graph-models/models/RelationGraphWith6Layout.ts:117-248

Performance Considerations

Layout Optimization Strategies

Strategy Implementation Use Case
Fast Start fastStart: true Skip initial positioning for large force layouts
Iteration Limit maxLayoutTimes Control computation time for force layouts
Ignore Node Size ignoreNodeSize: true Speed up tree layouts when nodes are similar size
Fixed Nodes node.fixed = true Exclude specific nodes from repositioning
Disable Live Updates disableLiveChanges: true Reduce overhead during force animation

Node Visibility Optimization

Before layout execution, the system calls updateNodesVisibleProperty() to calculate which nodes should be rendered based on:

  • Parent node expansion state (node.expanded)
  • Ancestor visibility chain
  • Explicit hiding (node.hidden)

This reduces layout computation to only visible nodes.

Sources: packages/relation-graph-models/models/RelationGraphWith6Layout.ts:30-52

Integration with Core System

The layout system integrates with the core graph instance through RelationGraphWith6Layout, which is part of the inheritance chain:

RelationGraphBase
  → RelationGraphWith1View
  → RelationGraphWith2Data
  → RelationGraphWith3Options
  → RelationGraphWith4Line
  → RelationGraphWith5Zoom
  → RelationGraphWith6Effect
  → RelationGraphWith6Layout ← Layout integration point
  → RelationGraphWith7Event
  → ...
  → RelationGraphCore

This positioning in the hierarchy ensures layout capabilities are available after data management (With2Data), options management (With3Options), and effects (With6Effect) are established.

Sources: packages/relation-graph-models/models/RelationGraphWith6Layout.ts:1-18


Key Takeaways:

  1. Strategy Pattern: Layout algorithms are interchangeable via layoutName configuration
  2. Two Families: Tree-based layouts for hierarchy, force-based for organic positioning
  3. Network Analysis: Topology is analyzed before positioning via BasicNetworkAnalyzer
  4. Dual Coordinates: lot.x/lot.y for layout logic, x/y for final rendering
  5. Multi-Network: Automatically handles disconnected graph components
  6. Extensibility: New layouts can extend RGBaseLayout and register with factory