function addSnapLine(
graphSnapContext: GraphSnapContext,
start: Point,
end: Point
): void {
const snapLine = createSnapLine(start, end)
if (snapLine !== null) {
// add a snap line for all graph elements
graphSnapContext.addEventListener('collect-snap-references', (evt) =>
evt.addSnapReference(snapLine)
)
}
}
function createSnapLine(
start: Point,
end: Point
): OrthogonalSnapLine | null {
// calculate the center of the snap line
const center = start.add(end).multiply(0.5)
// create a vertical snap line
if (start.x === end.x) {
return new OrthogonalSnapLine(
SnapLineOrientation.VERTICAL,
SnapLineSnapTypes.CENTER,
SnapReferenceVisualizationType.FIXED_LINE,
center,
start.y,
end.y,
false,
50
)
}
// create a horizontal snap line
if (start.y === end.y) {
return new OrthogonalSnapLine(
SnapLineOrientation.HORIZONTAL,
SnapLineSnapTypes.CENTER,
SnapReferenceVisualizationType.FIXED_LINE,
center,
start.x,
end.x,
false,
50
)
}
// we do not add non-orthogonal snap lines here
return null
}
Snapping
When discussing snapping, it’s important to first define the terms snap reference and snap result.
A snap reference is a guide related to a fixed object that a moved object might snap to. Prominent examples of snap references are snap line and snap grid.
A snap result represents the position where the current position of a moved object actually snaps to a snap reference. A moved object can have multiple snap results at the same location, meaning it can snap to multiple snap references simultaneously. These snap references are then visualized.
You can set the color of the snap result visualization using the CSS classes that are provided by the default snap reference visualizations, see Styling Snap References.
Adding Custom Snap References
To add custom snap references for all elements, register a listener to the GraphSnapContext’s collect-snap-references event. The arguments of that event has a method to add additional snap references.


Note that the snap line appears only if you move a graph element close to it. The following snippet illustrates how to always display the snap line.
function paintSnapLine(start: Point, end: Point): void {
const lineElement = document.createElementNS(
'http://www.w3.org/2000/svg',
'line'
)
lineElement.setAttribute('x1', String(start.x))
lineElement.setAttribute('y1', String(start.y))
lineElement.setAttribute('x2', String(end.x))
lineElement.setAttribute('y2', String(end.y))
lineElement.setAttribute('stroke', 'black')
const renderTree = graphComponent.renderTree
renderTree.createElement(
renderTree.backgroundGroup,
new SvgVisual(lineElement)
)
}


Each graph item provides a default set of snap references. A node, for example, provides snap lines at its borders and its center. You can change this behavior by decorating the default snap reference provider. The following example shows how to add a snap line 10 pixels above the top border of each node.
const nodeDecorator = graphComponent.graph.decorator.nodes
nodeDecorator.snapReferenceProvider.addWrapperFactory((node, wrapped) =>
ISnapReferenceProvider.create(
(
context: GraphSnapContext,
evt: CollectSnapReferencesEventArgs
) => {
// add default snap references
if (wrapped !== null) {
wrapped.addSnapReferences(context, evt)
}
// add a snap lines 10 pixels above the top border of a node
const layout = node.layout.toRect()
const center = new Point(layout.centerX, layout.y - 10)
const snapLine = new OrthogonalSnapLine(
SnapLineOrientation.HORIZONTAL,
SnapLineSnapTypes.BOTTOM,
SnapReferenceVisualizationType.FIXED_LINE,
center,
layout.x - 50,
layout.maxX + 50,
false,
100
)
evt.addSnapReference(snapLine)
}
)
)


GraphSnapContext provides a number of properties which determine for which objects snap references should be collected.
Property | Default Value | Description |
---|---|---|
|
Whether to provide snap references for orthogonal edge segments. |
|
|
Whether to provide snap references for sizes of fixed nodes. |
|
|
Whether to provide snap references for fixed nodes (parallel to borders, at border, at center). |
|
|
Whether to provide snap references for ports (orthogonal snap lines at the port’s x and y coordinates). |
|
|
Whether to provide snap references for equal distances between two nodes to which moved nodes will snap. |
|
|
Whether to provide snap references for equal distances between the centers of two nodes to which the center of moved nodes will snap. |
|
|
Whether to provide snap references for equal distances between two nodes to which moved orthogonal edge segments will snap. |
|
|
Defines the distance to a fixed node’s border for snap lines to which moved nodes can snap. |
|
|
Defines the distance to an orthogonal edge segment for snap lines to which moved nodes can snap. |
|
|
Defines the distance to an orthogonal edge segment for snap lines to which moved orthogonal edge segments can snap. |
|
|
Whether to crop snap lines at obstacles. |
|
|
The amount by which snap lines that are induced by existing edge segments and node borders are being extended. |
Some of the settings are tailored to be used with a moved label:
Property | Default Value | Description |
---|---|---|
|
Whether to collect snap references for the initial position of a label. |
|
|
Whether to collect snap lines on the edge path of an edge label. |
|
|
Whether to collect two snap lines in parallel to the edge path of the label owner at the initial distance of the edge label. |
|
|
Whether to collect snap lines in parallel to the edge path of the label owner at the distances of all edge labels of that owner. |
|
|
Whether to collect snap lines in parallel to the edge path of the label owner at the distance of other edge labels in the graph. |
|
|
Whether to collect six snap lines, two through the center and four through the border sides of the label owner. |
|
|
Whether to collect four snap lines in parallel to the owner’s borders at the initial label distance. |
|
|
Whether to collect snap lines in parallel to the border of the label owner at the distance of other node labels of that owner. |
|
|
Whether to collect snap lines in parallel to the border of the label owner at the distance of other node labels in the graph. |
|
|
Whether to collect snap lines to the center of the owner port of a port label. |
In addition, the following settings determine which and how items are snapped:
Property | Default Value | Description |
---|---|---|
NODE | ORTHOGONAL_SEGMENT | PORT_ADJACENT_SEGMENT | BEND_ADJACENT_SEGMENT | PORT | LABEL |
Determines, for which types of items snap result provider implementations should snap. |
|
Determines for which types of items the method collectGridSnapReferences collects snap results. |
Adding Custom Snap Results
Similar to adding custom snap references, you can modify the set of SnapResults collected during a move or edit operation. For example, grid snapping provides the center of a node as a result to snap to the grid. The following example shows how to also specify the top left corner of the node as a snap result.
class CustomNodeSnapResultProvider extends NodeSnapResultProvider {
protected collectGridSnapResults(
context: GraphSnapContext,
evt: CollectSnapResultsEventArgs,
snapGrid: SnapGrid,
suggestedLayout: Rect,
node: INode
) {
// add the default grid snap results (center of the node)
super.collectGridSnapResults(
context,
evt,
snapGrid,
suggestedLayout,
node
)
// add the top left corner of the node as grid snap result
this.collectGridSnapResult(
context,
evt,
snapGrid,
suggestedLayout.topLeft,
node
)
}
}
const nodeDecorator = graphComponent.graph.decorator.nodes
nodeDecorator.snapResultProvider.addConstant(
new CustomNodeSnapResultProvider()
)


The Custom Snapping source code demo shows how to customize snapping behavior and how to add custom snap references.