Import these from @grafloria/engine.
On their own pages
PathSimplifier: PathSimplifier provides algorithms for simplifying pathsRoutingOptions: Routing options
Functions
mergeObstacles
Merge two obstacle sources into one array, collapsing entries that are the SAME obstacle described twice.
This exists because RoutingEngine.route() unions its global ObstacleMap with
the request's obstacles, and the renderer's request repeats what the engine
already registered — so a 5,000-node diagram was handing the router a
9,998-entry obstacle array, and every one of those entries was scanned on
every collision test. Deduplication is keyed on id AND geometry, so two
entries that genuinely disagree about where an obstacle is are both kept:
the effective blocked region is unchanged.
tsfunction mergeObstacles(
a: readonly Obstacle[],
b: readonly Obstacle[]
): Obstacle[]
serveSolver
Serve solver requests on a port. This IS the worker's message loop — call it
inside the worker script with self, or in a test with a fake port.
tsfunction serveSolver(port: {
onmessage: ((ev: { data: SolverRequest }) => void) | null;
postMessage(msg: SolverResponse): void;
}): void
Classes
AStarRouter
A* Pathfinding Router
tsclass AStarRouter
Methods
constructor(obstacleMap: ObstacleMap, options: AStarOptions = {})getName(): string— Get algorithm nameroute(start: Point, end: Point): Point[]— Find path from start to end using A* algorithm
DijkstraRouter
Dijkstra's Shortest Path Router Guarantees shortest path but may be slower than A* for long distances
tsclass DijkstraRouter
Methods
constructor(obstacleMap: ObstacleMap, options: DijkstraOptions = {})getName(): string— Get algorithm nameroute(start: Point, end: Point): Point[]— Find shortest path from start to end using Dijkstra's algorithm
GlobalRouteSolver
tsclass GlobalRouteSolver
Methods
constructor(options: SolverOptions = {})get stats(): Readonly<SolverStats>solve(edges: SolverEdge[], obstacles: Obstacle[]): Map<string, Point[]>— Full solve. Deterministic: edges route in id order, every pass.solveIncremental( changed: Array<{ edge: SolverEdge; removed?: boolean }>, obstacles?: Obstacle[] ): Map<string, Point[]>— Re-solve after a change:changededges re-route (removed ones pass withremoved: true), and so does every edge whose current route touches a cell a changed edge occupied before or after. Everything else is served as-is.
LiveReroutingEngine
LiveReroutingEngine automatically updates link paths when nodes move or resize.
Features:
- Throttled rerouting for performance (60fps by default)
- Batches multiple node movements
- Can be enabled/disabled
- Configurable throttle time
tsclass LiveReroutingEngine
Methods
constructor(routingEngine: RoutingEngine, diagram: DiagramModel)enable(): void— Enable live reroutingdisable(): void— Disable live reroutingsetThrottle(ms: number): void— Set throttle time in millisecondsrerouteAll(): void— Manually trigger reroute of all links in the diagram Useful for manual refresh or after bulk operationsdestroy(): void— Clean up event listeners
Example
typescriptconst liveRerouting = new LiveReroutingEngine(routingEngine, diagram);
liveRerouting.enable();
// Links will automatically update when nodes move
node.setPosition({ x: 100, y: 100 });
ManhattanRouter
tsclass ManhattanRouter implements IRouter
Methods
getName(): string— Get algorithm nameroute(request: RouteRequest): RoutedPath | null— Calculate a route from start to end Can be synchronous or asynchronous (for algorithms like ELK.js)
ObstacleIndex
tsclass ObstacleIndex
Methods
constructor(obstacles: readonly Obstacle[], cellSize: number = DEFAULT_CELL_SIZE)get size(): number— How many obstacles were indexed.collides(px: number, py: number, margin: number): boolean— Does the point, expanded bymargin, touch any obstacle?
Byte-for-byte the predicate OrthogonalRouter.collidesWithObstacles used to
evaluate against the whole array — inclusive bounds and all.
queryBox(minX: number, minY: number, maxX: number, maxY: number): Obstacle[]— Every obstacle whose rect could overlap the axis-aligned box — the candidate set for a segment test. Conservative (a superset); the caller runs the exact geometry test on what comes back.all(): Obstacle[]— Every indexed obstacle (deduplicated). Escape hatch for degenerate queries.
ObstacleMap
ObstacleMap manages spatial indexing of obstacles for efficient queries Uses a simple grid-based spatial index for O(1) lookups
tsclass ObstacleMap
Methods
size(): number— Get number of obstacles in the mapadd(obstacle: Obstacle): void— Add an obstacle to the mapremove(id: string): boolean— Remove an obstacle from the mapget(id: string): Obstacle | undefined— Get an obstacle by IDupdate(obstacle: Obstacle): void— Update an obstacle (remove and re-add to update spatial index)clear(): void— Clear all obstaclesqueryRegion(region: Rectangle): Obstacle[]— Query obstacles in a rectangular regionqueryNearPoint(point: Point, radius: number): Obstacle[]— Query obstacles near a point within a given radiusqueryLine(start: Point, end: Point): Obstacle[]— Query obstacles along a line segmentgetObstacles(): Obstacle[]— Get all obstacles as an arrayisPointInside(point: Point, respectMargin = false): boolean— Check if a point is inside any obstacledoesLineIntersect(start: Point, end: Point, margin = 0): boolean— Check if a line segment intersects any obstacle
ObstacleMapBuilder
ObstacleMapBuilder creates ObstacleMap from DiagramModel Uses global bounds to account for transforms and hierarchy
tsclass ObstacleMapBuilder
Methods
static fromDiagram(diagram: DiagramModel, options: ObstacleMapOptions = {}): ObstacleMap(static) — Build obstacle map from all nodes in diagramstatic fromDiagramExcluding( diagram: DiagramModel, excludeIds: string[], options: ObstacleMapOptions = {} ): ObstacleMap(static) — Build obstacle map excluding specific nodes Useful for routing where source/target nodes should not be obstacles
OrthogonalRouter
OrthogonalRouter creates paths with only 90-degree angles Supports obstacle avoidance using A* on a grid
tsclass OrthogonalRouter implements IRouter
Methods
getName(): string— Get algorithm nameroute(request: RouteRequest): RoutedPath | null— Calculate a route from start to end Can be synchronous or asynchronous (for algorithms like ELK.js)static deriveExitSide(from: Point, toward: Point): 'left' | 'right' | 'top' | 'bottom'(static) — The exit side a FLOATING anchor should use, derived from where the other endpoint is — dominant axis first, ties break horizontal (matching the midline fallback's own axis choice, so the derived route agrees with what the undirected route would have preferred).
RoutingEngine
RoutingEngine coordinates routing operations and manages obstacles
tsclass RoutingEngine
Methods
constructor()registerRouter(name: string, router: IRouter): void— Register a custom routerunregisterRouter(name: string): boolean— Unregister a routergetAvailableAlgorithms(): string[]— Get list of available routing algorithmssetDefaultAlgorithm(algorithm: RoutingAlgorithm): void— Set default routing algorithmgetDefaultAlgorithm(): RoutingAlgorithm— Get default algorithmaddObstacle(obstacle: Obstacle): void— Add a global obstacleremoveObstacle(id: string): boolean— Remove a global obstacleupdateObstacle(obstacle: Obstacle): void— Update an existing obstacle's position/size More efficient than remove + addgetObstacles(): Obstacle[]— All registered obstacles — the map's own array accessor, passed through.getObstacleCount(): numberclearObstacles(): void— Clear all global obstaclesclearCache(): void— Clear route cacheroute(request: RouteRequest): RoutedPath | null— Route from start to end (synchronous version) Throws error if algorithm is async (like ELK) Use routeAsync() for async routersasync routeAsync(request: RouteRequest): Promise<RoutedPath | null>— Route from start to end (async version) Supports both sync and async routers like ELK.jsgetStats(): { obstacleCount: number; routerCount: number; cacheSize: number; }— Get routing engine statistics
SolverHost
The caller-side host. Pass a real Worker (or anything satisfying SolverPort) to run remote; pass nothing to run inline on the same thread — identical behaviour, no protocol drift, because BOTH paths speak through serveSolver.
tsclass SolverHost
Methods
constructor(port?: SolverPort)solve( edges: SolverEdge[], obstacles: Obstacle[], options?: SolverOptions ): Promise<{ routes: Map<string, Point[]>; stats: SolverStats }>solveIncremental( changed: Array<{ edge: SolverEdge; removed?: boolean }>, obstacles?: Obstacle[] ): Promise<{ routes: Map<string, Point[]>; stats: SolverStats }>
StraightRouter
StraightRouter creates a direct straight line from start to end
tsclass StraightRouter implements IRouter
Methods
getName(): string— Get algorithm nameroute(request: RouteRequest): RoutedPath | null— Calculate a route from start to end Can be synchronous or asynchronous (for algorithms like ELK.js)
VisibilityGraphRouter
Visibility Graph Router Optimal for environments with few obstacles and open spaces Creates graph of obstacle corners and finds shortest geometric path
tsclass VisibilityGraphRouter
Methods
constructor(obstacleMap: ObstacleMap, options: VisibilityGraphOptions = {})getName(): string— Get algorithm nameroute(start: Point, end: Point): Point[]— Find shortest geometric path using visibility graph
Interfaces
AStarOptions
Configuration options for A* router
tsinterface AStarOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
heuristic? | AStarHeuristic | Heuristic function to use (default: MANHATTAN) | |
allowDiagonal? | boolean | Allow diagonal movement (default: true) | |
gridSize? | number | Grid size for discretization (default: 5) | |
smoothing? | boolean | Enable path smoothing (default: true) | |
obstacleMargin? | number | Margin around obstacles (default: 5) | |
maxIterations? | number | Maximum iterations before giving up (default: 10000) |
DijkstraOptions
Configuration options for Dijkstra router
tsinterface DijkstraOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
allowDiagonal? | boolean | Allow diagonal movement (default: true) | |
gridSize? | number | Grid size for discretization (default: 5) | |
smoothing? | boolean | Enable path smoothing (default: true) | |
obstacleMargin? | number | Margin around obstacles (default: 5) | |
maxIterations? | number | Maximum iterations before giving up (default: 10000) |
IRouter
Interface for routing algorithms
tsinterface IRouter
Members
route(request: RouteRequest): RoutedPath | null | Promise<RoutedPath | null>— Calculate a route from start to end Can be synchronous or asynchronous (for algorithms like ELK.js)getName(): string— Get algorithm name
Obstacle
Obstacle in the routing space (usually a node)
tsinterface Obstacle extends GeometryRectangle
Properties
| Name | Type | Default | Description |
|---|---|---|---|
id | string | Unique identifier | |
margin? | number | Optional padding/margin around obstacle | |
kind? | 'node' | 'group' | Containment. Obstacles are no longer a flat set — a group obstacle knows it is one, and a node obstacle can carry its containing group, so routers can treat "inside the same container" and "collapsed group = one solid block" as first-class facts. | |
parentId? | string | The containing group's id, when the obstacle lives inside one. |
ObstacleMapOptions
tsinterface ObstacleMapOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
margin? | number | Margin to add around each obstacle |
RoutedPath
A complete routed path
tsinterface RoutedPath
Properties
| Name | Type | Default | Description |
|---|---|---|---|
points | RoutePoint[] | Ordered points forming the path | |
totalLength | number | Total path length | |
bendCount | number | Number of bends/turns in the path | |
cost? | number | Optional cost of the path | |
segments? | RouteSegment[] | Route segments |
RoutePoint
A point in the routing grid/space
tsinterface RoutePoint extends Point
Properties
| Name | Type | Default | Description |
|---|---|---|---|
cost? | number | Optional cost associated with this point |
RouteRequest
Routing request
tsinterface RouteRequest
Properties
| Name | Type | Default | Description |
|---|---|---|---|
start | Point | Start point | |
end | Point | End point | |
obstacles? | Obstacle[] | Obstacles to avoid | |
options? | RoutingOptions | Routing options | |
sourceDirection? | PortDirection | Direction the source port points (for orthogonal routing) | |
targetDirection? | PortDirection | Direction the target port points (for orthogonal routing) | |
obstacleIndex? | ObstacleIndex | A prebuilt spatial index over EXACTLY obstacles. An optimisation, never a semantic input: a router that ignores it and scans obstacles linearly gets the same answer, just slower. RoutingEngine builds one per obstacle set and reuses it across every link in a frame, so the A* inner loop stops paying O(scene) per cell it looks at. If you set this by hand it MUST describe the same obstacles as `ob |
RouteSegment
A segment of a route path
tsinterface RouteSegment
Properties
| Name | Type | Default | Description |
|---|---|---|---|
start | RoutePoint | ||
end | RoutePoint | ||
length | number | Length of the segment | |
angle | number | Angle in degrees |
SolverEdge
tsinterface SolverEdge
Properties
| Name | Type | Default | Description |
|---|---|---|---|
id | string | ||
start | Point | ||
end | Point | ||
sourceDirection? | Side | ||
targetDirection? | Side | ||
jetty? | number | per-edge jetty override; defaults to the solver's gridSize |
SolverOptions
tsinterface SolverOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
gridSize? | number | ||
obstacleMargin? | number | ||
congestionPenalty? | number | cost added per step through a cell occupied by K other edges: K × this | |
crossingPenalty? | number | cost added per step through a cell that another edge crosses PERPENDICULAR to this step | |
passes? | number | refinement passes over the whole edge set | |
maxIterations? | number | per-edge search budget (ManhattanRouter maxIterations) | |
bendCost? | number |
SolverPort
The message port surface the host needs — a real Worker satisfies it.
tsinterface SolverPort
Properties
| Name | Type | Default | Description |
|---|---|---|---|
onmessage | ((ev: { data: SolverResponse }) |
Members
postMessage(msg: SolverRequest): void
SolverRequestIncremental
tsinterface SolverRequestIncremental
Properties
| Name | Type | Default | Description |
|---|---|---|---|
seq | number | ||
kind | 'incremental' | ||
changed | Array<{ edge: SolverEdge; removed?: boolean }> | ||
obstacles? | Obstacle[] |
SolverRequestSolve
tsinterface SolverRequestSolve
Properties
| Name | Type | Default | Description |
|---|---|---|---|
seq | number | ||
kind | 'solve' | ||
edges | SolverEdge[] | ||
obstacles | Obstacle[] | ||
options? | SolverOptions |
SolverResponse
tsinterface SolverResponse
Properties
| Name | Type | Default | Description |
|---|---|---|---|
seq | number | ||
routes | Array<[string, Point[]]> | ||
stats | SolverStats |
SolverStats
tsinterface SolverStats
Properties
| Name | Type | Default | Description |
|---|---|---|---|
edgesRouted | number | edges routed in the last solve/solveIncremental call | |
edgesReused | number | edges served untouched from the previous solution |
VisibilityGraphOptions
Configuration options for Visibility Graph router
tsinterface VisibilityGraphOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
obstacleMargin? | number | Margin around obstacles (default: 1) - corners placed outside obstacle bounds |
Types
CostFunction
Cost function for path evaluation
tstype CostFunction = (path: RoutedPath) => number;
HeuristicFunction
Heuristic function for pathfinding
tstype HeuristicFunction = (a: Point, b: Point) => number;
PortDirection
Port direction for routing algorithms that need to respect port orientation
tstype PortDirection = 'left' | 'right' | 'top' | 'bottom';
RoutingAlgorithm
Routing algorithm type
tstype RoutingAlgorithm =
| 'straight'
| 'orthogonal'
| 'manhattan' // Wave 5 Card 3: grid router with JointJS+-parity knobs
| 'elk'
| 'a-star'
| 'dijkstra'
| 'visibility-graph'
| 'custom';
SolverRequest
tstype SolverRequest = SolverRequestSolve | SolverRequestIncremental;
Enums
AStarHeuristic
Heuristic functions for A* algorithm
tsenum AStarHeuristic
Members
MANHATTAN = 'manhattan'— Manhattan distance (L1 norm) - best for grid-based movementEUCLIDEAN = 'euclidean'— Euclidean distance (L2 norm) - best for free movementDIAGONAL = 'diagonal'— Diagonal distance (Chebyshev/L∞ norm) - best for 8-directional movement
Was this page helpful?