Specifies custom data for the EdgeRouter.
Default Values of Properties
Examples
The following example shows how to create a new instance of EdgeRouterData<TNode,TEdge,TNodeLabel,TEdgeLabel> and use it with an EdgeRouter:
const edgeRouter = new EdgeRouter()
const layoutData = new EdgeRouterData()
// group edges by color on their source side
layoutData.sourceGroupIds = (edge: IEdge) =>
edge.style instanceof PolylineEdgeStyle ? edge.style.stroke!.fill : {}
// route only the selected edges
layoutData.scope.edges = graphComponent.selection.edges
graphComponent.graph.applyLayout(edgeRouter, layoutData)
In many cases the complete initialization of EdgeRouterData<TNode,TEdge,TNodeLabel,TEdgeLabel> can also be done in a single object initializer:
const layoutData = new EdgeRouterData({
// group edges by color on their source side
sourceGroupIds: (edge: IEdge): any =>
edge.style instanceof PolylineEdgeStyle
? edge.style.stroke!.fill
: {},
})
// route only the selected edges
layoutData.scope.edges = graphComponent.selection.edges
const edgeRouter = new EdgeRouter()
graphComponent.graph.applyLayout(edgeRouter, layoutData)
Type Parameters
- TNode
- TEdge
- TNodeLabel
- TEdgeLabel
Type Details
- yFiles module
- algorithms
Constructors
Parameters
A map of options to pass to the method.
- edgeDescriptors - ItemMapping<TEdge,EdgeRouterEdgeDescriptor>
- The mapping of edges to their EdgeRouterEdgeDescriptor This option either sets the value directly or recursively sets properties to the instance of the edgeDescriptors property on the created object.
- nodeLabelCrossingCostFactors - ItemMapping<TNodeLabel,number>
- A mapping from node labels to a crossing cost factor. This option either sets the value directly or recursively sets properties to the instance of the nodeLabelCrossingCostFactors property on the created object.
- edgeLabelCrossingCostFactors - ItemMapping<TEdgeLabel,number>
- A mapping from edge labels to a crossing cost factor. This option either sets the value directly or recursively sets properties to the instance of the edgeLabelCrossingCostFactors property on the created object.
- nodeMargins - ItemMapping<TNode,Insets>
- The mapping from nodes to their margins. This option either sets the value directly or recursively sets properties to the instance of the nodeMargins property on the created object.
- ports - BasicPortData<TNode,TEdge,TNodeLabel,TEdgeLabel>
- The sub-data that influences the placement of the ports. This option either sets the value directly or recursively sets properties to the instance of the ports property on the created object.
- sourceGroupIds - ItemMapping<TEdge,any>
- A mapping from edges to an object representing their source edge group. This option either sets the value directly or recursively sets properties to the instance of the sourceGroupIds property on the created object.
- targetGroupIds - ItemMapping<TEdge,any>
- A mapping from edges to an object representing their target edge group. This option either sets the value directly or recursively sets properties to the instance of the targetGroupIds property on the created object.
- buses - ItemCollectionMapping<TEdge,EdgeRouterBusDescriptor>
- The mapping from edges to their EdgeRouterBusDescriptor. This option sets the buses property on the created object.
- layoutGridData - LayoutGridData<TNode,TEdge,TNodeLabel,TEdgeLabel>
- The LayoutGrid layout data. This option either sets the value directly or recursively sets properties to the instance of the layoutGridData property on the created object.
- edgeLabelPreferredPlacements - ItemMapping<TEdgeLabel,EdgeLabelPreferredPlacement>
- The mapping that provides an EdgeLabelPreferredPlacement instance for edge labels. This option either sets the value directly or recursively sets properties to the instance of the edgeLabelPreferredPlacements property on the created object.
- edgeProcessingComparator - function(TEdge, TEdge):number
- A custom comparison to define the processing order of the edges. This option sets the edgeProcessingComparator property on the created object.
Signature Details
function(x: TEdge, y: TEdge) : number
Encapsulates a method that compares two objects.Parameters
- x - TEdge
- The first object to compare.
- y - TEdge
- The second object to compare.
Returns
- number
- An integer value which is
<0
ifx
is less thany
,0
ifx
is equal toy
, or>0
ifx
is greater thany
Properties
Gets or sets the mapping from edges to their EdgeRouterBusDescriptor.
Remarks
All edges associated with the same bus descriptor are routed in a bus-like style, sharing a common backbone. A bus consists of segments that are shared by multiple edges, with shorter segments connecting to the actual nodes. Note that using such a bus representation with multiple edges drawn on top of each other may obscure information like the individual edge direction. Bus routing can be very useful, for example, in parts of a diagram where each node is connected to every other node.
The EdgeRouterBusDescriptor instance allows configuring the bus formed by the associated edges. To conveniently define buses, use the add method of this property. It takes the bus descriptor as a parameter and allows defining the edges associated with the bus using the returned ItemCollection<TItem>.
Examples
// create a bus descriptor and, optionally, configure it
const busDescriptor = new EdgeRouterBusDescriptor({
minimumBackboneSegmentLength: 200,
})
// retrieve an ItemCollection which defines the edges that should belong to the bus
const busEdgesCollection = edgeRouterData.buses.add(busDescriptor)
// busEdgesList is an ICollection which contains all edges that should be part of the bus
busEdgesCollection.items = busEdgesList
See Also
Gets or sets the mapping of edges to their EdgeRouterEdgeDescriptor
Gets or sets a mapping from edge labels to a crossing cost factor.
Remarks
Examples
When there are only a few labels to customize costs for, the easiest way is usually to use the mapper:
// Try harder to prevent crossings with label1
layoutData.edgeLabelCrossingCostFactors.mapper.set(label1, 1.5)
// Crossings with label2 are not as bad
layoutData.edgeLabelCrossingCostFactors.mapper.set(label2, 0.2)
If the cost can readily be computed from the label itself, the mapperFunction is often the more convenient option:
// The more upper-case letters a label has, the more important it must be
// and crossings should be avoided based on that metric
// todo
layoutData.edgeLabelCrossingCostFactors = (label: ILabel): number =>
label.text.match(/[A-Z]/g)!.length
See Also
Gets or sets the mapping that provides an EdgeLabelPreferredPlacement instance for edge labels.
Examples
Depending on how much customization is needed, some ways of setting EdgeLabelPreferredPlacements are more convenient than others. For example, to set the same descriptor for all labels, you can just use the constant property:
layoutData.edgeLabelPreferredPlacements = new EdgeLabelPreferredPlacement(
{
// Place labels along the edge
angleReference: LabelAngleReferences.RELATIVE_TO_EDGE_FLOW,
angle: 0,
// ... on either side
edgeSide: LabelEdgeSides.LEFT_OF_EDGE | LabelEdgeSides.RIGHT_OF_EDGE,
// ... with a bit of distance to the edge
distanceToEdge: 5,
},
)
If some labels should use custom placement or this has to be configured ahead of time, you can use the mapper instead:
// Place label1 orthogonal to the edge anywhere on it
layoutData.edgeLabelPreferredPlacements.mapper.set(
label1,
new EdgeLabelPreferredPlacement({
placementAlongEdge: LabelAlongEdgePlacements.ANYWHERE,
angleReference: LabelAngleReferences.RELATIVE_TO_EDGE_FLOW,
angle: Math.PI / 2,
}),
)
// Place label2 near the edge's source on either side of it, and make it parallel to the edge
layoutData.edgeLabelPreferredPlacements.mapper.set(
label2,
new EdgeLabelPreferredPlacement({
placementAlongEdge: LabelAlongEdgePlacements.AT_SOURCE,
edgeSide: LabelEdgeSides.RIGHT_OF_EDGE | LabelEdgeSides.LEFT_OF_EDGE,
angleReference: LabelAngleReferences.RELATIVE_TO_EDGE_FLOW,
angle: 0,
}),
)
When the preferred placement can be inferred from the label itself, a delegate is usually the easiest choice:
layoutData.edgeLabelPreferredPlacements = (
label: ILabel,
): EdgeLabelPreferredPlacement => {
const customData = label.tag as CustomData
return new EdgeLabelPreferredPlacement({
angle: 0,
angleReference: LabelAngleReferences.RELATIVE_TO_EDGE_FLOW,
// If the tag says to place the label in the center, put it in the center parallel to the edge's path
// All other labels can be placed anywhere, but on the side of the edge.
placementAlongEdge: customData.placeInCenter
? LabelAlongEdgePlacements.AT_CENTER
: LabelAlongEdgePlacements.ANYWHERE,
edgeSide: customData.placeInCenter
? LabelEdgeSides.ON_EDGE
: LabelEdgeSides.LEFT_OF_EDGE | LabelEdgeSides.RIGHT_OF_EDGE,
})
}
Note that the preferred placement can also be inferred from an arbitrary ILabelModelParameter:
layoutData.edgeLabelPreferredPlacements =
EdgeLabelPreferredPlacement.fromParameter(
NinePositionsEdgeLabelModel.CENTER_CENTERED,
)
See Also
Gets or sets a custom comparison to define the processing order of the edges.
Remarks
The processing order may affect the quality of the individual edge paths. When routing an edge, only the paths of the already routed edges and of the fixed edges (that will not be routed at all) can be considered. Therefore, edges that are processed first have to consider fewer other edge paths than the edges that are processed later, which might have to use less optimal alternative paths.
By default, i.e., if this property is null
, an internal comparison instance is applied.
Default Value
null
.There is no custom instance set and the default implementation will be applied.
Signature Details
function(x: TEdge, y: TEdge) : number
Parameters
- x - TEdge
- The first object to compare.
- y - TEdge
- The second object to compare.
Returns
- number
- An integer value which is
<0
ifx
is less thany
,0
ifx
is equal toy
, or>0
ifx
is greater thany
Gets or sets the LayoutGrid layout data.
Gets or sets a mapping from node labels to a crossing cost factor.
Remarks
Examples
When there are only a few labels to customize costs for, the easiest way is usually to use the mapper:
// Try harder to prevent crossings with label1
layoutData.nodeLabelCrossingCostFactors.mapper.set(label1, 1.5)
// Crossings with label2 are not as bad
layoutData.nodeLabelCrossingCostFactors.mapper.set(label2, 0.2)
If the cost can readily be computed from the label itself, the mapperFunction is often the more convenient option:
// The more upper-case letters a label has, the more important it must be
// and crossings should be avoided based on that metric
layoutData.nodeLabelCrossingCostFactors = (label: ILabel): number =>
label.text.match(/[A-Z]/g)!.length
See Also
Gets or sets the mapping from nodes to their margins.
Remarks
Examples
The easiest option is to reserve the same space around all nodes, by setting a constant value:
layoutData.nodeMargins = new Insets(20)
Handling only certain nodes differently can be done easily by using the mapper property:
// node1 only reserves space above and below
layoutData.nodeMargins.mapper.set(node1, new Insets(20, 10, 0, 0))
// node2 has space all around
layoutData.nodeMargins.mapper.set(node2, new Insets(25))
// all other nodes don't get extra space
In cases where the nodeMargins for each node can be determined by looking at the node itself it's often easier to just set a mapperFunction instead of preparing a mapper:
// Retrieve the space around the node from its tag property
layoutData.nodeMargins = (node: INode): Insets =>
new Insets(parseFloat(node.tag))
See Also
Gets or sets the sub-data that influences the placement of the ports.
Remarks
The port placement can be influenced by specifying EdgePortCandidates for the source and target of an edge, as well as by specifying NodePortCandidates at the nodes.
In addition, it is possible to specify that ports should be grouped at the source or target.
If both EdgePortCandidates and NodePortCandidates are specified, the EdgeRouter tries to match them. An edge port candidate matches a node port candidate if
- their matchingIds are equal or one type is
null
, - they belong to a common side or one side is ANY, and
- if both candidates are fixed, they describe the same positions.
The position of a port candidate is defined by offset or the actual offset of the edge endpoint for fixed-from-sketch candidates. In case there is no matching port, a port candidate specified for the edge is preferred.
Gets or sets the sub-data that provides the ability to modify the scope of the EdgeRouter with regard to the routed edges.
Remarks
The routed edges can be specified using the ItemCollection properties edges and incidentNodes. Any edges, or edges incident to the nodes in those collections, are considered to be in scope by the EdgeRouter. The Item Mapping properties edgeMapping and incidentNodeMapping can be used to specify a more differentiated EdgeRouterScope for (incident) edges.
When multiple scope properties are specified for the same edge, the most permissive property is prioritized, meaning PATH is always prioritized over PATH_AS_NEEDED, which is more permissive than SEGMENTS_AS_NEEDED. IGNORE is assigned to an edge if and only if no other property is specified.
Examples
The scope can be specified using the items property when only a few known edges should be affected:
layoutData.scope.edges.items.add(edge1)
layoutData.scope.edges.items.add(edge2)
In cases where looking at the edge itself is sufficient to determine whether an edge is in scope, it's often easier to just set a delegate instead:
// We assume here that all edges have a CustomData instance as their tag,
// which then has a boolean property 'IncludeInLayout'.
layoutData.scope.edges = (edge: IEdge): boolean =>
(edge.tag as CustomData).includeInLayout
Sometimes it can be more convenient to specify the incident nodes of the affected edges:
// We assume here that all nodes have a CustomData instance as their tag,
// which then has a boolean property 'IncludeInLayout'.
layoutData.scope.incidentNodes = (node: INode): boolean =>
(node.tag as CustomData).includeInLayout
Finally, it's also possible to specify more differentiated policies by using the edgeMapping or incidentNodeMapping property:
layoutData.scope.edgeMapping = (edge) => {
if (edge == edge1) {
//edge1 is always routed
return EdgeRouterScope.PATH
}
if (edge == edge2) {
//edge2 is only routed if necessary
return EdgeRouterScope.PATH_AS_NEEDED
}
if (edge == edge3) {
//only segments of edge3 are routed if necessary
return EdgeRouterScope.SEGMENTS_AS_NEEDED
}
//other edges are not affected
return EdgeRouterScope.IGNORE
}
Gets or sets a mapping from edges to an object representing their source edge group.
Remarks
Examples
One simple way to use source groups is to use the edge's source node as group ID which effectively groups all edges with the same source together:
layoutData.sourceGroupIds = (edge: IEdge) => edge.sourceNode
Another useful way to use a delegate here would be grouping edges by some commonality, such as the same color:
layoutData.sourceGroupIds = (edge: IEdge) => {
const style = edge.style
if (style instanceof PolylineEdgeStyle) {
return style.stroke!.fill
}
return null
}
If only certain edges should be grouped it may sometimes be easier to use the mapper to set the group IDs:
for (const group of edgeGroups) {
for (const edge of group) {
// Use the collection as group ID, since it's common to all edges in it
layoutData.sourceGroupIds.mapper.set(edge, group)
}
}
See Also
Gets or sets a mapping from edges to an object representing their target edge group.
Remarks
Examples
One simple way to use source groups is to use the edge's target node as group ID which effectively groups all edges with the same target together:
layoutData.targetGroupIds = (edge: IEdge) => edge.targetNode
Another useful way to use a delegate here would be grouping edges by some commonality, such as the same color:
layoutData.targetGroupIds = (edge: IEdge) => {
const style = edge.style
if (style instanceof PolylineEdgeStyle) {
return style.stroke!.fill
}
return null
}
If only certain edges should be grouped it may sometimes be easier to use the mapper to set the group IDs:
for (const group of edgeGroups) {
for (const edge of group) {
// Use the collection as group ID, since it's common to all edges in it
layoutData.targetGroupIds.mapper.set(edge, group)
}
}
See Also
Methods
combineWith
(data: LayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel>…) : LayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel>Combines this instance with the given layout data.
Remarks
Parameters
A map of options to pass to the method.
- data - LayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel>
- The LayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel> to combine this instance with.
Returns
- ↪LayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel>
- The combined layout data.