Skip to content

Coordinate System

NgDiagram uses a coordinate system that enables precise positioning of all diagram elements. Understanding this system is crucial for creating accurate diagrams and implementing custom behaviors.

The diagram has a global coordinate system with its origin at the top-left corner of the diagram container. This origin serves as the reference point for all positioning calculations.

  • Origin Point: (0, 0) is located at the top-left corner of the diagram container
  • Positive X: Moving the viewport to the right increases values
  • Positive Y: Moving the viewport down increases values
  • Units: All coordinates are in pixels
  • Negative values are supported

In other words, as you drag the diagram right or down, the coordinates of elements increase along the X and Y axes.

Nodes are positioned using absolute coordinates relative to the diagram origin:

{
id: '1',
position: { x: 50, y: 150 }, // 100 px from left, 150 px from top
data: { label: 'Node 1' },
},

Positioning Behavior:

  • The position property defines the top-left corner of the node
  • Nodes are positioned using CSS transform: translate(x, y)
  • Position is independent of the node’s size or content

Ports have absolute positioning within their parent node:

Default Side Positioning:

  • Left side: top: 50%, left: 0 (center of left edge)
  • Right side: top: 50%, left: 100% (center of right edge)
  • Top side: top: 0, left: 50% (center of top edge)
  • Bottom side: top: 100%, left: 50% (center of bottom edge)

Custom Positioning: Ports can be positioned anywhere within the node using CSS properties:

<ng-diagram-port id="custom-port" type="target" side="left" style="top: 25%; left: 0" />

Groups follow the same positioning rules as regular nodes:

{
id: '3',
isGroup: true,
position: { x: 310, y: 80 }, // Group's top-left corner
size: { width: 260, height: 186 },
autoSize: false,
data: {},
resizable: true,
},

Group Behavior:

  • Groups are positioned the same as nodes - at their top-left corner
  • Child nodes within groups retain their global positions - their coordinates are not recalculated or adjusted when added to a group
  • When a group moves, all its children move with it

Edge labels are positioned using absolute coordinates along the edge path:

{
id: 'label1',
positionOnEdge: 0.5, // 50% along the edge path (0 = start, 1 = end)
position: { x: 0, y: 0 }, // Absolute position relative to viewport's origin (calculated automatically)
// ... other properties
}

Label Positioning:

  • positionOnEdge: Value from 0 to 1 indicating position along the edge
  • position: Absolute coordinates calculated by the system
  • Labels are positioned using transform: translate(x, y) translate(-50%, -50%) for center alignment

The viewport represents the visible area of the diagram:

viewport: {
x: 0, // Horizontal offset from diagram origin
y: 0, // Vertical offset from diagram origin
scale: 1, // Zoom level (1.0 = 100%)
width: 710, // Viewport width - optional
height: 470, // Viewport height - optional
},

The NgDiagramViewportService offers methods for converting between different coordinate systems in ngDiagram:

Client to Flow Position:

// Convert screen coordinates to diagram coordinates
const flowPosition = viewportService.clientToFlowPosition({ x: 150, y: 200 });

Flow to Client Position:

// Convert diagram coordinates to screen coordinates
const clientPosition = viewportService.flowToClientPosition({ x: 100, y: 150 });

Use the NgDiagramViewportService to access viewport information reactively:

export class CoordinatesComponent {
private readonly viewportService = inject(NgDiagramViewportService);
private readonly selectionService = inject(NgDiagramSelectionService);
// Get reactive viewport data
viewport = this.viewportService.viewport;
scale = this.viewportService.scale;
3 collapsed lines
selectionPosition = computed(
() => this.selectionService.selection().nodes[0]?.position
);
}

Available Viewport Properties:


8 collapsed lines
import '@angular/compiler';
import { DecimalPipe } from '@angular/common';
import { Component, computed, inject } from '@angular/core';
import {
NgDiagramSelectionService,
NgDiagramViewportService,
} from 'ng-diagram';
@Component({
selector: 'coordinates',
imports: [DecimalPipe],
templateUrl: './coordinates.component.html',
styles: `
:host {
display: flex;
flex-direction: column;
position: absolute;
padding: 0.5rem;
color: var(--ngd-txt-primary-default);
}
`,
})
export class CoordinatesComponent {
private readonly viewportService = inject(NgDiagramViewportService);
private readonly selectionService = inject(NgDiagramSelectionService);
// Get reactive viewport data
viewport = this.viewportService.viewport;
scale = this.viewportService.scale;
3 collapsed lines
selectionPosition = computed(
() => this.selectionService.selection().nodes[0]?.position
);
}