Specifies custom data for the PortPlacementStage.
Remarks
Type Details
- yfiles module
- view-layout-bridge
- yfiles-umd modules
- view-layout-bridge
- Legacy UMD name
- yfiles.layout.PortPlacementStageData
Constructors
Creates a new instance of PortPlacementStageData which helps configuring the PortPlacementStage.
Parameters
A map of options to pass to the method.
- sourcePortCandidates - ItemMapping<IEdge,ICollection<PortCandidate>>
A mapping from edges to a collection of their source port candidates. This option sets the sourcePortCandidates property on the created object.
- targetPortCandidates - ItemMapping<IEdge,ICollection<PortCandidate>>
A mapping from edges to a collection of their target port candidates. This option sets the targetPortCandidates property on the created object.
- sourcePortConstraints - ItemMapping<IEdge,PortConstraint>
A mapping from edges to their source PortConstraint. This option sets the sourcePortConstraints property on the created object.
- targetPortConstraints - ItemMapping<IEdge,PortConstraint>
A mapping from edges to their target PortConstraint. This option sets the targetPortConstraints property on the created object.
- nodePortCandidateSets - ItemMapping<INode,PortCandidateSet>
A mapping from nodes to their PortCandidateSet. This option sets the nodePortCandidateSets property on the created object.
- sourcePortGroupIds - ItemMapping<IEdge,Object>
A mapping from edges to an object representing their source port group. This option sets the sourcePortGroupIds property on the created object.
- targetPortGroupIds - ItemMapping<IEdge,Object>
A mapping from edges to an object representing their target port group. This option sets the targetPortGroupIds property on the created object.
Properties
Gets or sets a mapping from nodes to their PortCandidateSet.
Remarks
Examples
The simplest way to define node port candidate sets is to use the same PortCandidateSet for all nodes, if suitable for the use case:
// All nodes get only a candidate for the lower (South) side with capacity 10
const constantPortCandidates = new PortCandidateSet()
constantPortCandidates.add(
PortCandidate.createCandidate(PortDirections.SOUTH, 10)
)
layoutData.nodePortCandidateSets.constant = constantPortCandidates
The same effect can be achieved with a delegate as well, however, a more useful way to use a delegate would be to decide whether a node should get a port candidate set based on some data at the node, e.g., found in its tag:
// Create a PortCandidateSet with capacity 2 for the upper side (North) and capacity 1 for the lower side (South)
const pcs = new PortCandidateSet()
pcs.add(PortCandidate.createCandidate(PortDirections.NORTH), 2)
pcs.add(PortCandidate.createCandidate(PortDirections.SOUTH), 1)
// Specify the node port candidates for diamond nodes, i.e. for nodes with the string "diamond" in their tag
layoutData.nodePortCandidateSets = (node) =>
node.tag === 'diamond' ? pcs : null
// Create a PortCandidateSet with capacity 2 for the upper side (North) and capacity 1 for the lower side (South)
const pcs = new PortCandidateSet()
pcs.add(PortCandidate.createCandidate(PortDirections.NORTH), 2)
pcs.add(PortCandidate.createCandidate(PortDirections.SOUTH), 1)
// Specify the node port candidates for diamond nodes, i.e. for nodes with the string "diamond" in their tag
layoutData.nodePortCandidateSets = (node) =>
node.tag === 'diamond' ? pcs : null!
If specific nodes should get a certain PortCandidateSet, it may sometimes be easier to use the mapper to set them:
// Create a PortCandidateSet with capacity 2 for the upper side (North) and capacity 1 for the lower side (South)
const pcs1 = new PortCandidateSet()
pcs1.add(PortCandidate.createCandidate(PortDirections.NORTH), 2)
pcs1.add(PortCandidate.createCandidate(PortDirections.SOUTH), 1)
// Create another set with candidates for the left (West) and right side (East)
const pcs2 = new PortCandidateSet()
pcs2.add(PortCandidate.createCandidate(PortDirections.EAST), 4)
pcs2.add(PortCandidate.createCandidate(PortDirections.WEST), 4)
// Specify the port candidate sets for two specific nodes
layoutData.nodePortCandidateSets.mapper.set(node1, pcs1)
layoutData.nodePortCandidateSets.mapper.set(node2, pcs2)
See Also
Gets or sets a mapping from edges to a collection of their source port candidates.
Remarks
Port candidates allow to define where an edge can connect to its source node and allow fine control over port placement.
If all that is needed is to fix the source port in its location or on a node side, port constraints are easier to work with, since they are a slightly simpler concept. It is, however, recommended to not define both at the same time.
If candidates are assigned to edges (e.g. with this property) and to nodes (nodePortCandidateSets) the PortPlacementStage tries to match them. When there is no match, the port candidate with the lowest costs specified for the edge is chosen.
Examples
Source port candidates are effectively a collection of possible port placements with different costs and the layout algorithm is free to choose the candidate that fits best into the overall layout, while also preferring candidates with a lower cost. To set the same candidate list for all edges, it's easiest to use the constant property:
layoutData.sourcePortCandidates.constant = new List([
// Prefer the center position and route the edge downwards if possible (no cost for this candidate)
PortCandidate.createCandidate(0, 0, PortDirections.SOUTH),
// Alternatively, use any position at the bottom border of the node, but with a higher cost
PortCandidate.createCandidate(PortDirections.SOUTH, 1),
// If that fails, use either the left or the right side
PortCandidate.createCandidate(PortDirections.WEST, 2),
PortCandidate.createCandidate(PortDirections.EAST, 2)
])
If certain edges need specific port candidates, it's usually convenient to use the mapper property:
// edge1 should leave the source node preferably on the node side in flow direction,
// but can also use a fixed port location on the left of the node.
layoutData.sourcePortCandidates.mapper.set(
edge1,
new List([
PortCandidate.createCandidate(PortDirections.WITH_THE_FLOW),
PortCandidate.createCandidate(-0.5, 0.25, PortDirections.WEST, 1)
])
)
// edge2 should leave the source node anywhere on the upper side of the node
layoutData.sourcePortCandidates.mapper.set(
edge2,
new List([PortCandidate.createCandidate(PortDirections.NORTH)])
)
For cases when the desired configuration of port candidates can be readily created from the edge itself, the delegate is often the most convenient option:
// Assume that edges may have "yes" or "no" labels and specify port candidates based on label text
layoutData.sourcePortCandidates = (edge) => {
switch (edge.labels.get(0).text) {
case 'Yes':
return new List([PortCandidate.createCandidate(PortDirections.EAST)])
case 'No':
return new List([PortCandidate.createCandidate(PortDirections.WEST)])
default:
return new List([
PortCandidate.createCandidate(PortDirections.NORTH),
PortCandidate.createCandidate(PortDirections.SOUTH)
])
}
}
// Assume that edges may have "yes" or "no" labels and specify port candidates based on label text
layoutData.sourcePortCandidates = (edge) => {
switch (edge.labels.get(0).text) {
case 'Yes':
return new List<PortCandidate>([
PortCandidate.createCandidate(PortDirections.EAST)
])
case 'No':
return new List<PortCandidate>([
PortCandidate.createCandidate(PortDirections.WEST)
])
default:
return new List<PortCandidate>([
PortCandidate.createCandidate(PortDirections.NORTH),
PortCandidate.createCandidate(PortDirections.SOUTH)
])
}
}
See Also
Sample Graphs
Gets or sets a mapping from edges to their source PortConstraint.
Remarks
Port constraints allow to define where an edge attaches to its source node and can either restrict that to one of the node's sides, or to a fixed port position.
A more general concept which allows finer control over where ports are placed, are port candidates. It is recommended to not define port constraints and port candidates at the same time.
Examples
If all edges should exit their source node on the same side, you can simply set a constant constraint for all edges:
// All source ports should be on the bottom side
layoutData.sourcePortConstraints.constant = PortConstraint.create(
PortSide.SOUTH
)
To change the constraints for individual edges, it's usually easiest to use the mapper:
// edge1 should leave the source node on the left side, while keeping its exact port position (strong port constraint)
layoutData.sourcePortConstraints.mapper.set(
edge1,
PortConstraint.create(PortSide.WEST, true)
)
// edge2 should leave the source node downwards, but the port may be placed anywhere along that node border
layoutData.sourcePortConstraints.mapper.set(
edge2,
PortConstraint.create(PortSide.SOUTH)
)
If a PortConstraint can readily be created from an edge, using a delegate is often easier:
// Source ports will be placed on the right side and use a property in the edge's tag
// to determine whether ports will stay fixed at their position.
layoutData.sourcePortConstraints = (edge) =>
PortConstraint.create(PortSide.EAST, edge.tag.fixPorts)
// Source ports will be placed on the right side and use a property in the edge's tag
// to determine whether ports will stay fixed at their position.
layoutData.sourcePortConstraints = (edge: IEdge): PortConstraint =>
PortConstraint.create(PortSide.EAST, edge.tag.fixPorts)
See Also
Sample Graphs
Gets or sets a mapping from edges to an object representing their source port group.
Remarks
Examples
The simplest way to use source port groups is to use the same object as group ID for all edges. Since grouping is done per node, this has the effect of grouping all edges with the same source node together:
layoutData.sourcePortGroupIds.constant = {}
The same effect can be achieved with a delegate as well, by returning the source node as group ID for each edge. However, a more useful way to use a delegate here would be grouping edges by some commonality, such as the same color:
layoutData.sourcePortGroupIds = (edge) => {
const style = edge.style
if (style instanceof PolylineEdgeStyle) {
return style.stroke.fill
}
return null
}
layoutData.sourcePortGroupIds = (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 edgePortGroups) {
for (const edge of group) {
// Use the collection as group ID, since it's common to all edges in it
layoutData.sourcePortGroupIds.mapper.set(edge, group)
}
}
See Also
Gets or sets a mapping from edges to a collection of their target port candidates.
Remarks
Port candidates allow to define where an edge can connect to its target node and allow fine control over port placement.
If all that is needed is to fix the target port in its location or on a node side, port constraints are easier to work with, since they are a slightly simpler concept. It is, however, recommended to not define both at the same time.
If candidates are assigned to edges (e.g. with this property) and to nodes (nodePortCandidateSets) the PortPlacementStage tries to match them. When there is no match, the port candidate with the lowest costs specified for the edge is chosen.
Examples
Target port candidates are effectively a collection of possible port placements with different costs and the layout algorithm is free to choose the candidate that fits best into the overall layout, while also preferring candidates with a lower cost. To set the same candidate list for all edges, it's easiest to use the constant property:
layoutData.targetPortCandidates.constant = new List([
// Prefer the center position and enter the node from the top if possible (no cost for this candidate)
PortCandidate.createCandidate(0, 0, PortDirections.NORTH),
// Alternatively, use any position at the top border of the node, but with a higher cost
PortCandidate.createCandidate(PortDirections.NORTH, 1),
// If that fails, use either the left or the right side
PortCandidate.createCandidate(PortDirections.WEST, 2),
PortCandidate.createCandidate(PortDirections.EAST, 2)
])
If certain edges need specific port candidates, it's usually convenient to use the mapper property:
// edge1 should enter the target node preferably on the node side in flow direction,
// but can also use a fixed port location on the left of the node.
layoutData.targetPortCandidates.mapper.set(
edge1,
new List([
PortCandidate.createCandidate(PortDirections.WITH_THE_FLOW),
PortCandidate.createCandidate(-0.5, -0.25, PortDirections.WEST, 1)
])
)
// edge2 should enter the target node anywhere on the bottom side of the node
layoutData.targetPortCandidates.mapper.set(
edge2,
new List([PortCandidate.createCandidate(PortDirections.SOUTH)])
)
For cases when the desired configuration of port candidates can be readily created from the edge itself, the delegate is often the most convenient option:
// Assume that edges may have "yes" or "no" labels and specify port candidates based on label text
layoutData.targetPortCandidates = (edge) => {
switch (edge.labels.first().text) {
case 'Yes':
return new List([PortCandidate.createCandidate(PortDirections.EAST)])
case 'No':
return new List([PortCandidate.createCandidate(PortDirections.WEST)])
default:
return new List([
PortCandidate.createCandidate(PortDirections.NORTH),
PortCandidate.createCandidate(PortDirections.SOUTH)
])
}
}
// Assume that edges may have "yes" or "no" labels and specify port candidates based on label text
layoutData.targetPortCandidates = (edge) => {
switch (edge.labels.first().text) {
case 'Yes':
return new List<PortCandidate>([
PortCandidate.createCandidate(PortDirections.EAST)
])
case 'No':
return new List<PortCandidate>([
PortCandidate.createCandidate(PortDirections.WEST)
])
default:
return new List<PortCandidate>([
PortCandidate.createCandidate(PortDirections.NORTH),
PortCandidate.createCandidate(PortDirections.SOUTH)
])
}
}
See Also
Sample Graphs
Gets or sets a mapping from edges to their target PortConstraint.
Remarks
Port constraints allow to define where an edge attaches to its target node and can either restrict that to one of the node's sides, or to a fixed port position.
A more general concept which allows finer control over where ports are placed, are port candidates. It is recommended to not define port constraints and port candidates at the same time.
Examples
If all edges should exit their source node on the same side, you can simply set a constant constraint for all edges:
// All target ports should be on the top side
layoutData.targetPortConstraints.constant = PortConstraint.create(
PortSide.NORTH
)
To change the constraints for individual edges, it's usually easiest to use the mapper:
// edge1 should enter the target node on the right side, while keeping its exact port position (strong port
// constraint)
layoutData.targetPortConstraints.mapper.set(
edge1,
PortConstraint.create(PortSide.EAST, true)
)
// edge2 should enter the target node from the top, but the port may be placed anywhere along that node border
layoutData.targetPortConstraints.mapper.set(
edge2,
PortConstraint.create(PortSide.NORTH)
)
If a PortConstraint can readily be created from an edge, using a delegate is often easier:
// Target ports will be placed on the right side and use a property in the edge's tag
// to determine whether ports will stay fixed at their position.
layoutData.targetPortConstraints = (edge) =>
PortConstraint.create(PortSide.EAST, edge.tag.fixPorts)
// Target ports will be placed on the right side and use a property in the edge's tag
// to determine whether ports will stay fixed at their position.
layoutData.targetPortConstraints = (edge: IEdge): PortConstraint =>
PortConstraint.create(PortSide.EAST, edge.tag.fixPorts)
See Also
Sample Graphs
Gets or sets a mapping from edges to an object representing their target port group.
Remarks
Examples
The simplest way to use target port groups is to use the same object as group ID for all edges. Since grouping is done per node, this has the effect of grouping all edges with the same target node together:
layoutData.targetPortGroupIds.constant = {}
The same effect can be achieved with a delegate as well, by returning the target node as group ID for each edge. However, a more useful way to use a delegate here would be grouping edges by some commonality, such as the same color:
layoutData.targetPortGroupIds = (edge) => {
const style = edge.style
if (style instanceof PolylineEdgeStyle) {
return style.stroke.fill
}
return null
}
layoutData.targetPortGroupIds = (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 edgePortGroups) {
for (const edge of group) {
// Use the collection as group ID, since it's common to all edges of it
layoutData.targetPortGroupIds.mapper.set(edge, group)
}
}
See Also
Methods
Combines this instance with the given layout data.
Remarks
Parameters
A map of options to pass to the method.
- data - LayoutData
- The LayoutData to combine this instance with.
Returns
- ↪LayoutData
- The combined layout data.