Specifies custom data for the CircularLayout.
Examples
The following example shows how to create a new instance of CircularLayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel> and use it with an CircularLayout:
const layoutData = new CircularLayoutData()
// Put nodes of each color into their own circle
layoutData.partitions = (node: INode): any =>
node.style instanceof ShapeNodeStyle ? node.style.fill : {}
// Retain a bit more space around nodes
layoutData.nodeMargins = new Insets(15)
const layout = new CircularLayout()
graphComponent.graph.applyLayout(layout, layoutData)
In many cases the complete initialization of CircularLayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel> can also be done in a single object initializer:
const layoutData = new CircularLayoutData({
// Put nodes of each color into their own circle
partitions: (node: INode): any =>
node.style instanceof ShapeNodeStyle ? node.style.fill : {},
// Retain a bit more space around nodes
nodeMargins: new Insets(15),
})
const layout = new CircularLayout()
graphComponent.graph.applyLayout(layout, layoutData)
Type Parameters
- TNode
- TEdge
- TNodeLabel
- TEdgeLabel
Type Details
- yFiles module
- algorithms
Constructors
Creates a new instance of CircularLayoutData<TNode,TEdge,TNodeLabel,TEdgeLabel> which helps configuring CircularLayout.
Parameters
A map of options to pass to the method.
- partitions - ItemMapping<TNode,any>
- The mapping from nodes to their partition object. This option either sets the value directly or recursively sets properties to the instance of the partitions 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.
- nodeComparator - function(TNode, TNode):number
- A comparison function used for determining the node order on the circle. This option sets the nodeComparator property on the created object.
Signature Details
function(x: TNode, y: TNode) : number
Encapsulates a method that compares two objects.Parameters
- x - TNode
- The first object to compare.
- y - TNode
- 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
- nodeTypes - ItemMapping<TNode,any>
- The mapping from nodes to an object defining the node type which is considered during the layout. This option either sets the value directly or recursively sets properties to the instance of the nodeTypes property on the created object.
- edgeBundleDescriptors - ItemMapping<TEdge,EdgeBundleDescriptor>
- The mapping of edges to their EdgeBundleDescriptor. This option either sets the value directly or recursively sets properties to the instance of the edgeBundleDescriptors property on the created object.
- exteriorEdges - ItemCollection<TEdge>
- The collection of edges that are routed around the exterior of a circle formed by each partition. This option either sets the value directly or recursively sets properties to the instance of the exteriorEdges property on the created object.
- edgeDirectedness - ItemMapping<TEdge,number>
- The mapping from edges to their directedness. This option either sets the value directly or recursively sets properties to the instance of the edgeDirectedness property on the created object.
- ports - BasicPortData<TNode,TEdge,TNodeLabel,TEdgeLabel>
- The sub-data that provides a way of influencing 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.
Properties
Gets a mapper from nodes to their circle ID.
Remarks
Examples
const layoutData = new CircularLayoutData()
graph.applyLayout(new CircularLayout(), layoutData)
for (const node of graph.nodes) {
console.log(
`Node ${node} has been placed in circle ${layoutData.circleIdsResult.get(node)}`,
)
}
See Also
Gets or sets the mapping of edges to their EdgeBundleDescriptor.
Remarks
Bundling together multiple edges means that their common parts are to some degree merged into a bundled part. At the source and target point, the edges are again clearly split.
If an edge is mapped to null
, the default descriptor is used.
If an edge is marked for exterior routing (see exteriorEdges), it is not bundled anymore.
Enabled edge bundling makes the layout algorithm calculate a certain node order on the circle. Specifying a nodeComparator for node ordering takes precedence over this order. On the other hand, the order calculated with bundled edges takes precedence over the one calculated if fromSketchMode is enabled or nodeTypes are defined.
See Also
Gets or sets the mapping from edges to their directedness.
Remarks
Generally, the circular layout algorithm doesn't consider the edge direction. Nevertheless, this property allows the user to specify hints on the directedness of edges. More precisely, a value of 1
(default) indicates that the edge should be considered to be directed from source to target, a value of -1
that it is directed from target to source, and a value of 0
means that it is undirected.
Currently, the specified values are only considered during the detection of star substructures if the substructure style is set to SEPARATED_RADIAL. Then, all edges forming a star substructure must have the same directedness (negative, zero, or positive).
Examples
The easiest option is to define all edges with the same directedness:
layoutData.edgeDirectedness = 1
Handling only certain edges differently can be done easily by using the mapper property:
// edge1 should be considered directed
layoutData.edgeDirectedness.mapper.set(edge1, 1)
// edge2 should be considered directed against the flow
layoutData.edgeDirectedness.mapper.set(edge2, -1)
// All other edges not set in the mapper are treated as undirected
In cases where the directedness for each edge can be determined by looking at the edge itself it's often easier to just set a delegate instead of preparing a mapper:
// Treat edges as directed or undirected based on their style's arrowhead
layoutData.edgeDirectedness = (edge: IEdge): 0 | 1 | -1 => {
const style = edge.style as PolylineEdgeStyle
// edges with either no arrows on both ends or an arrow on both ends should be considered undirected
if (
(style.sourceArrow === null && style.targetArrow === null) ||
(style.sourceArrow !== null && style.targetArrow !== null)
) {
return 0
}
// edges with only a target arrow are directed from source to target
if (style.targetArrow !== null) {
return 1
}
// edges with only a source arrow are directed from target to source
if (style.sourceArrow !== null) {
return -1
}
return 0
}
See Also
0
.Gets or sets the collection of edges that are routed around the exterior of a circle formed by each partition.
Remarks
See Also
Gets or sets a comparison function used for determining the node order on the circle.
Remarks
- Edge bundling set through edgeBundleDescriptors
- Enabled fromSketchMode
- Specified nodeTypes
Signature Details
function(x: TNode, y: TNode) : number
Parameters
- x - TNode
- The first object to compare.
- y - TNode
- 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 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 mapping from nodes to an object defining the node type which is considered during the layout.
Remarks
Node types influence the layout in the following way:
If the partitioning policy is SINGLE_CYCLE, or if the partition style is set to CYCLE, the nodes of the cycles are sorted by their type such that all nodes of the same type are consecutive. This is not the case if a nodeComparator is set, edge bundling, or fromSketchMode is enabled. The specified node types also influence the layout of the cycle partitions which is done with an instance of the RadialTreeLayout.
In addition, node types also influence the detection of substructures. Currently, if a starSubstructureStyle is set (i.e., the property is not set to NONE), each detected substructure only contains nodes of the same type or only nodes without a type. Note that besides the node type, the substructure detection is also influenced by the edgeDirectedness.
See Also
Gets or sets the mapping from nodes to their partition object.
Remarks
Each partition will form a circle in the layout.
Nodes that are mapped to null
(or not explicitly mapped when using the mapper) will not be part of any circle.
If partitions are specified the partitioningPolicy is ignored.
Examples
Often you might already have some way of partitioning nodes into specific circles, in which case the mapperFunction property is usually the easiest, which allows you to derive the partition identifier directly from each node:
// Use the node's tag for circle assignment.
// Nodes with the same tag are placed on the same circle.
layoutData.partitions = (node: INode) => node.tag
Another option would be to explicitly assign nodes that should appear on a given circle their respective partition identifier via the mapper property:
for (const node of circle1) {
// Use the collection as partition ID, since it's the same for all nodes in it
layoutData.partitions.mapper.set(node, circle1)
}
for (const node of circle2) {
// Use the collection as partition ID, since it's the same for all nodes in it
layoutData.partitions.mapper.set(node, circle2)
}
Finally, a rather pointless option would be to assign the same custom partition identifier to all nodes, effectively forcing all nodes to be on the same circle:
layoutData.partitions = {}
// has the same effect as
circularLayout.partitioningPolicy =
CircularLayoutPartitioningPolicy.SINGLE_CYCLE
As can be seen, there's an easier option for that.
See Also
Gets or sets the sub-data that provides a way of influencing 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 layout algorithm 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. When there is no match, the port candidate with the lowest costs specified for the edge is chosen.
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.