documentationfor yFiles for HTML 3.0.0.1

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.

Adding a custom snap line for all graph elements
function addSnapLine(graphSnapContext, start, end) {
  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, end) {
  // 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
}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
}
Moving a node close to the custom snap line

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.

Painting the snap line
function paintSnapLine(start, end) {
  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)
  )
}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)
  )
}
Painting the snap line

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.

Adding snap lines to nodes
const nodeDecorator = graphComponent.graph.decorator.nodes

nodeDecorator.snapReferenceProvider.addWrapperFactory((node, wrapped) =>
  ISnapReferenceProvider.create((context, evt) => {
    // 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)
  })
)
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)
    }
  )
)
Additional snap line of a node

GraphSnapContext provides a number of properties which determine for which objects snap references should be collected.

Property Default Value Description
collectEdgeSnapReferencestrueWhether to provide snap references for orthogonal edge segments.
collectNodeSizestrueWhether to provide snap references for sizes of fixed nodes.
collectNodeSnapReferencestrueWhether to provide snap references for fixed nodes (parallel to borders, at border, at center).
collectPortSnapReferencestrueWhether to provide snap references for ports (orthogonal snap lines at the port’s x and y coordinates).
collectNodePairSnapLinestrueWhether to provide snap references for equal distances between two nodes to which moved nodes will snap.
collectNodePairCenterSnapLinesfalseWhether to provide snap references for equal distances between the centers of two nodes to which the center of moved nodes will snap.
collectNodePairSegmentSnapLinestrueWhether to provide snap references for equal distances between two nodes to which moved orthogonal edge segments will snap.
nodeDistance0Defines the distance to a fixed node’s border for snap lines to which moved nodes can snap.
nodeToEdgeDistance-1Defines the distance to an orthogonal edge segment for snap lines to which moved nodes can snap.
edgeDistance0Defines the distance to an orthogonal edge segment for snap lines to which moved orthogonal edge segments can snap.
cropSnapLinestrueWhether to crop snap lines at obstacles.
snapLineExtension40The 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
considerInitialLabelLocationSnappingtrueWhether to collect snap references for the initial position of a label.
considerEdgeLabelOwnerPathSnappingtrueWhether to collect snap lines on the edge path of an edge label.
considerEdgeLabelOwnerDistanceSnappingtrueWhether to collect two snap lines in parallel to the edge path of the label owner at the initial distance of the edge label.
considerEdgeLabelOwnerLabelsDistanceSnappingtrueWhether to collect snap lines in parallel to the edge path of the label owner at the distances of all edge labels of that owner.
considerEdgeLabelAllEdgeLabelsDistanceSnappingfalseWhether to collect snap lines in parallel to the edge path of the label owner at the distance of other edge labels in the graph.
considerNodeLabelOwnerShapeSnappingtrueWhether to collect six snap lines, two through the center and four through the border sides of the label owner.
considerNodeLabelOwnerDistanceSnappingtrueWhether to collect four snap lines in parallel to the owner’s borders at the initial label distance.
considerNodeLabelOwnerLabelsDistanceSnappingtrueWhether to collect snap lines in parallel to the border of the label owner at the distance of other node labels of that owner.
considerNodeLabelAllNodeLabelsDistanceSnappingfalseWhether to collect snap lines in parallel to the border of the label owner at the distance of other node labels in the graph.
considerPortLabelOwnerLocationSnappingtrueWhether 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
snappableItemsNODE | ORTHOGONAL_SEGMENT | PORT_ADJACENT_SEGMENT | BEND_ADJACENT_SEGMENT | PORT | LABELDetermines, for which types of items snap result provider implementations should snap.
gridSnappableItemsALLDetermines 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.

Adding grid snap results to nodes
class CustomNodeSnapResultProvider extends NodeSnapResultProvider {
  collectGridSnapResults(context, evt, snapGrid, suggestedLayout, node) {
    // 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()
)
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()
)
Additional snap result of a node

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