Skip to main content

Vertex avoidance

You can instruct JsPlumb to attempt to route edges around vertices, either for all edges in your app or just for some specific type(s) of edge.

Example

Vanilla JS
React
Angular
Vue
Svelte
import { newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()

const surface = toolkit.render(someElement, {
"defaults": {
"edgesAvoidVertices": true,
"connector": "Orthogonal"
},
"grid": {
"size": {
"w": 20,
"h": 20
}
}
});

The key option being edgesAvoidVertices in the defaults. Internally, vertex avoidance is computed using an A* algorithm, which is based upon a grid, and so we do recommend you use a grid in your app if you've got edgesAvoidVertices set. Without a grid set you can sometimes see an effect where a path segment's location changes as you drag an element, and then snaps back to some multiple of the A* grid. The A* grid has a cell size of 10 pixels and so the best results are obtained from setting your own grid to a multiple of that.

Path constrainment

You can constrain the travel of the path in a few different ways.

Orthogonal routing

Orthogonal routing constrains path segments to travel either horizontally or vertically, as shown at the top of the page.

Vanilla JS
React
Angular
Vue
Svelte
import { newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()

const surface = toolkit.render(someElement, {
"defaults": {
"edgesAvoidVertices": true,
"connector": {
"type": "Straight",
"options": {
"constrain": "orthogonal"
}
}
},
"grid": {
"size": {
"w": 20,
"h": 20
}
}
});

Any angle

Vanilla JS
React
Angular
Vue
Svelte
import { newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()

const surface = toolkit.render(someElement, {
"defaults": {
"edgesAvoidVertices": true,
"connector": {
"type": "Straight",
"options": {
"constrain": "none"
}
}
},
"grid": {
"size": {
"w": 20,
"h": 20
}
}
});

With this setup path segments can travel at any angle:

Smooth connectors

For some applications the above can look a little jerky, and so one thing you can do is set smooth on your connector, to get that Graphviz effect:

Vanilla JS
React
Angular
Vue
Svelte
import { newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()

const surface = toolkit.render(someElement, {
"defaults": {
"edgesAvoidVertices": true,
"connector": {
"type": "Straight",
"options": {
"smooth": true
}
}
},
"grid": {
"size": {
"w": 20,
"h": 20
}
}
});
info

Straight connectors with smooth:true set are aliased in JsPlumb to the connector name Smooth:

Vanilla JS
React
Angular
Vue
Svelte
import { newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()

const surface = toolkit.render(someElement, {
"defaults": {
"edgesAvoidVertices": true,
"connector": "Smooth"
},
"grid": {
"size": {
"w": 20,
"h": 20
}
}
});

Metro routing

"Metro" routing constrains path segments to travel either horizontally, vertically, or at 45 degrees.

Vanilla JS
React
Angular
Vue
Svelte
import { newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()

const surface = toolkit.render(someElement, {
"defaults": {
"edgesAvoidVertices": true,
"connector": {
"type": "Straight",
"options": {
"constrain": "metro"
}
}
},
"grid": {
"size": {
"w": 20,
"h": 20
}
}
});

Applying to specific edge types only

If you want to setup your app such that only specific edge types will use the A* router, you can do this via your view. In this example, we map two edge types, and the orthogonal edge type has this key set:

{
avoidVertices:true
}
Vanilla JS
React
Angular
Vue
Svelte
import { newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()

const surface = toolkit.render(someElement, {
"defaults": {
"connector": {
"type": "Straight",
"options": {
"constrain": "orthogonal"
}
}
},
"grid": {
"size": {
"w": 20,
"h": 20
}
},
"view": {
"edges": {
"straight": {
"connector": "Straight",
"paintStyle": {
"stroke": "red",
"strokeWidth": 2
}
},
"orthogonal": {
"avoidVertices": true,
"connector": "Orthogonal",
"paintStyle": {
"stroke": "red",
"strokeWidth": 2
}
}
}
}
});

Routing considerations

To get the best results from the edge avoidance routine, it's helpful to understand what's happening under the hood.

Path computation is a two stage process:

  1. First, we compute a path through the A* grid;
  2. We then convert that path into what the user sees.

Borders and Buffers

For each vertex, JsPlumb assigns a border and a buffer. The element below is 100 pixels wide by 80 pixels high. The green border is 20 pixels in width, and the orange buffer is also 20 pixels in width.

In the first phase of path computation - computing a path through the grid - both the border and buffer are taken into account. Paths are computed such that they do not pass through any vertex or its border or buffer.

The second phase of path computation - converting the grid path to what the user sees -

Anchors, Gaps and Stubs

info

tl;dr for this section: we recommend you use Continuous or Dynamic anchors when you have edgesAvoidVertices set, as it gives JsPlumb more choices when routing.

Edges in JsPlumb are represented at each end by an anchor point, which may be either a fixed location, or it can be chosen by JsPlumb based upon proximity to the other element. The anchor point lies somewhere on an element's boundary (or sometimes inside an element). Every connector may define a gap (a space between the anchor point and the start of the edge) and also a stub (a straight line at the start/end of the edge). gap and stub are zero by default.

The actual start and end points used by JsPlumb are computed by adding the gap and stub for each edge to the anchor point's location. If it happens that the start or end point intersects some other vertex in the canvas, JsPlumb ignores that vertex when computing the path.

For instance, in this canvas below, all anchor points are fixed to Bottom, and cannot be changed, and the start point of the path lies within the buffer of node 2, so the node 2 is ignored, as it cannot be avoided. However, node 4 is also on the ideal path from 1->3, but it can be avoided, and is.

Continuous/Dynamic anchors

If your connector is using Continuous or Dynamic anchors, JsPlumb will attempt to choose an anchor location that avoids the path traversing any vertices. In this canvas we have specified our anchors to be Dynamic, with a choice of Bottom and Top:

defaults:{
edgesAvoidVertices:true,
connector: "Orthogonal",
anchor:["Bottom", "Top"]
}

JsPlumb chooses the Top anchor for node 1, because, as with the canvas above, the Bottom anchor lies within node 2's buffer.