Skip to content

Routing

Edge routing determines how connections between nodes are drawn in your diagram. NgDiagram provides flexible routing where each routing algorithm:

  • Calculates the points that define the edge path
  • Draws the SVG path from those points
  • Computes positions along the path for labels and decorations

Routing algorithms automatically calculate optimal paths, with an option to provide predefined waypoints for static edge paths.

NgDiagram includes three routing algorithms:

The simplest routing that connects points with straight line segments. In auto mode, it creates a direct line between source and target. In manual mode, it can create multi-segment paths by connecting user-provided waypoints with straight lines.

Creates paths using only horizontal and vertical segments, ideal for technical diagrams and flowcharts. Supports configurable segment lengths and optional rounded corners.

Produces smooth curved connections using cubic Bézier curves.


To specify a routing algorithm for an edge, set the routing property in the edge data:

model = initializeModel({
26 collapsed lines
nodes: [
{
id: 'source-node',
position: { x: 150, y: 240 },
data: { label: 'Source' },
rotatable: true,
},
{
id: '2',
position: { x: 600, y: 30 },
data: { label: 'Target 1' },
rotatable: true,
},
{
id: '3',
position: { x: 600, y: 180 },
data: { label: 'Target 2' },
rotatable: true,
},
{
id: '4',
position: { x: 600, y: 330 },
data: { label: 'Target 3' },
rotatable: true,
},
],
edges: [
{
id: '1',
source: 'source-node',
sourcePort: 'port-right',
targetPort: 'port-left',
target: '2',
data: {},
routing: 'polyline',
type: 'routing-edge',
},
{
id: '2',
source: 'source-node',
sourcePort: 'port-right',
targetPort: 'port-left',
target: '3',
data: {},
routing: 'orthogonal',
type: 'routing-edge',
},
{
id: '3',
source: 'source-node',
sourcePort: 'port-right',
targetPort: 'port-left',
target: '4',
data: {},
routing: 'bezier',
type: 'routing-edge',
},
],
});
}

When no routing is specified, the default routing algorithm (orthogonal) is used. The default can be changed through configuration.

For custom edge components, you can provide routing directly in the template using NgDiagramBaseEdgeComponent:

<ng-diagram-base-edge
[edge]="edge"
[routing]="'orthogonal'"
stroke="var(--ngd-default-edge-stroke)"
/>

Each edge can operate in one of two routingMode options:

In auto mode, the routing algorithm automatically calculates the path between nodes. The list of points is calculated by the routing algorithm and should be treated as read-only. The path updates automatically when nodes move or resize.

const edge = {
id: 'edge1',
source: 'node1',
target: 'node2',
routing: 'orthogonal',
routingMode: 'auto', // or omit for default
data: {},
};

Manual mode gives you full control over the edge path by allowing you to provide your own list of points. The routing algorithm is still used to draw the SVG path from these points and calculate label positions. When nodes move, you control what happens - the path can remain unchanged, or you can programmatically update the points as needed.

Try moving the nodes in the example below - notice how the edge path remains fixed:


model = initializeModel({
14 collapsed lines
nodes: [
{
id: 'node1',
position: { x: 100, y: 150 },
data: { label: 'Move me!' },
rotatable: true,
},
{
id: 'node2',
position: { x: 500, y: 150 },
data: { label: 'Move me too!' },
rotatable: true,
},
],
edges: [
{
id: 'manual-edge',
source: 'node1',
sourcePort: 'port-right',
target: 'node2',
targetPort: 'port-left',
routing: 'orthogonal',
routingMode: 'manual',
points: [
{ x: 285, y: 172 },
{ x: 345, y: 172 },
{ x: 345, y: 100 },
{ x: 445, y: 100 },
{ x: 445, y: 172 },
{ x: 500, y: 172 },
],
data: {},
},
],
});
}

This is useful for creating custom edge behaviors, fixed connection paths, or implementing your own edge update logic.

You can configure routing behavior globally through the diagram’s configuration:

@Component({
16 collapsed lines
imports: [NgDiagramComponent, NgDiagramBackgroundComponent],
providers: [provideNgDiagram()],
template: `
<div class="not-content diagram">
<ng-diagram [model]="model" [config]="config">
<ng-diagram-background />
</ng-diagram>
</div>
`,
styles: `
.diagram {
display: flex;
height: var(--ng-diagram-height);
border: var(--ng-diagram-border);
}
`,
})
export class DiagramComponent {
config: NgDiagramConfig = {
zoom: {
zoomToFit: {
onInit: true,
},
},
edgeRouting: {
defaultRouting: 'orthogonal', // Set default routing
orthogonal: {
firstLastSegmentLength: 30, // Length of first and last segments
maxCornerRadius: 8, // Maximum radius for rounded corners
},
bezier: {
bezierControlOffset: 150, // Distance of control points from nodes
},
},
};
43 collapsed lines
model = initializeModel({
metadata: {
viewport: { x: 0, y: 0, scale: 0.8 },
},
nodes: [
{
id: 'node1',
position: { x: 100, y: 100 },
data: { label: 'Source' },
},
{
id: 'node2',
position: { x: 400, y: 100 },
data: { label: 'Target 1' },
},
{
id: 'node3',
position: { x: 400, y: 250 },
data: { label: 'Target 2' },
},
],
edges: [
{
id: 'edge1',
source: 'node1',
sourcePort: 'port-right',
target: 'node2',
targetPort: 'port-left',
// No routing specified - uses default 'orthogonal' from config
data: {},
},
{
id: 'edge2',
source: 'node1',
sourcePort: 'port-rigth',
target: 'node3',
targetPort: 'port-left',
routing: 'bezier', // Explicitly set to bezier
data: {},
},
],
});
}

NgDiagram’s routing system is extensible. You can create custom routing algorithms by implementing the EdgeRouting interface, or by creating custom edge components that generate their own paths using manual mode.

You can create dynamic custom paths by computing points in your edge component and using manual routing mode. Here’s an example of a sinusoid edge that updates its path dynamically as nodes move:


import { Component, computed, input } from '@angular/core';
import {
NgDiagramBaseEdgeComponent,
type Edge,
type NgDiagramEdgeTemplate,
type Point,
} from 'ng-diagram';
@Component({
template: `<ng-diagram-base-edge
[edge]="customEdge()"
stroke="var(--ngd-default-edge-stroke)"
[targetArrowhead]="'ng-diagram-arrow'"
></ng-diagram-base-edge>`,
styleUrl: './sinusoid-edge.component.scss',
imports: [NgDiagramBaseEdgeComponent],
})
export class SinusoidEdgeComponent implements NgDiagramEdgeTemplate {
edge = input.required<Edge>();
customEdge = computed(() => {
const edge = this.edge();
const { sourcePosition, targetPosition } = edge;
if (!sourcePosition || !targetPosition) {
return edge;
}
const points = this.generateSinusoidPoints(sourcePosition, targetPosition);
return {
...edge,
points,
routing: 'polyline',
routingMode: 'manual' as const,
};
});
46 collapsed lines
private generateSinusoidPoints(sourcePosition: Point, targetPosition: Point) {
const startX = sourcePosition.x;
const startY = sourcePosition.y;
const endX = targetPosition.x;
const endY = targetPosition.y;
// Calculate distance and angle between points
const distance = Math.sqrt(
Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)
);
const angle = Math.atan2(endY - startY, endX - startX);
// Sinusoidal wave parameters
const amplitude = 20;
const frequency = Math.max(2, distance / 100);
const segments = Math.max(20, Math.floor(distance / 5));
const points = [{ x: startX, y: startY }];
// Generate sinusoidal curve points
for (let i = 1; i < segments; i++) {
const t = i / segments;
// Base position along the straight line
const baseX = startX + (endX - startX) * t;
const baseY = startY + (endY - startY) * t;
// Calculate perpendicular offset using sine wave
// Fade out amplitude as we approach the end to ensure smooth connection
const fadeFactor = i < segments - 5 ? 1 : (segments - i) / 5;
const sineOffset =
Math.sin(t * 2 * Math.PI * frequency) * amplitude * fadeFactor;
const perpAngle = angle + Math.PI / 2;
const x = baseX + Math.cos(perpAngle) * sineOffset;
const y = baseY + Math.sin(perpAngle) * sineOffset;
points.push({ x, y });
}
// Always end exactly at the target position
points.push({ x: endX, y: endY });
return points;
}
}

This approach is ideal when you want the path to update automatically based on node positions and when you just need to compute points - the SVG path can be computed by a built-in routing algorithm (in this case, polyline was sufficient).

For more control and reusability across different edge types, you can create a custom routing algorithm by implementing the EdgeRouting interface and registering it via NgDiagramService.registerRouting(). Once registered, the routing becomes available globally across all edges in your diagram.

Here’s a complete example of an arc routing that uses SVG elliptical arcs to create curved connections:


import type { EdgeRouting, EdgeRoutingContext, Point } from 'ng-diagram';
/**
* Arc routing implementation using SVG elliptical arcs.
*
* Creates curved connections using SVG arc commands
*/
export class ArcRouting implements EdgeRouting {
name = 'arc';
/**
* Computes the points for an arc path.
* For arcs, we only need the source and target points.
*/
computePoints(context: EdgeRoutingContext): Point[] {
const { sourcePoint, targetPoint } = context;
return [
{ x: sourcePoint.x, y: sourcePoint.y },
{ x: targetPoint.x, y: targetPoint.y },
];
}
/**
* Generates an SVG path string using an elliptical arc.
*
* The arc command (A) creates a curved path between source and target.
* Format: A rx ry x-axis-rotation large-arc-flag sweep-flag x y
*/
computeSvgPath(points: Point[]): string {
if (points.length < 2) return '';
const start = points[0];
const end = points[points.length - 1];
// Calculate distance to use as radius
const dx = end.x - start.x;
const dy = end.y - start.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const radiusMultiplier = 0.5;
const radius = distance * radiusMultiplier;
// Create arc: sweep-flag=1 creates a clockwise arc (curves downward/rightward)
return `M ${start.x},${start.y} A ${radius} ${radius} 0 0 1 ${end.x},${end.y}`;
}
}

This approach gives you full control over path generation, configuration options, and point calculations. The three required methods work together: computePoints defines the geometric points, computeSvgPath renders them as SVG, and computePointOnPath positions labels and decorations along the path.

Edges Overview → | Custom Edges → | Edge Labels →