Members
Constructors
Properties
Gets or sets the ChildOrderData<TNode, TEdge> which specifies how child nodes are to be sorted.
Examples
The ChildOrderData<TNode, TEdge> class provides two primary methods for determining the order of a parent's child nodes: either by directly sorting the child nodes using targetNodeComparators or targetNodeComparables, or by sorting the outgoing edges from the parent node to its child nodes using outEdgeComparators or outEdgeComparables. Setting a comparable value (e.g. a number or a string) for all nodes means that the child nodes of all parents in the graph are ordered using those values.
// Use the node height as comparable to sort the child nodes by their height
layoutData.childOrder.targetNodeComparables = (child) =>
child.layout.heightUsing a comparison delegate for specific nodes is convenient if the child nodes should be ordered only for particular parent nodes.
layoutData.childOrder.targetNodeComparators = (parent: INode) => {
if (parent == specificNode) {
// Define a comparison function only for a specific parent node
// ... and sort its child nodes based on an order value specified in their tags
return (child1: INode, child2: INode) =>
child1.tag.sequenceOrder.compareTo(child2.tag.sequenceOrder)
}
// No ordering for the edges around the other nodes
return null
}Setting a comparable value (e.g. a number or a string) for all edges means that the outgoing edges of all parents in the graph are ordered using those values.
// Use the label count of the out edges as comparable value to sort them
layoutData.childOrder.outEdgeComparables = (edge) => edge.labels.sizeUsing a comparison delegate for specific edges is convenient if the outgoing edges should be ordered only for particular nodes.
layoutData.childOrder.outEdgeComparators = (parent: INode) => {
if (parent == specificNode) {
// Define a comparison function only for a specific parent node
// ... and sort its outgoing edges based on an order value specified in their tags
return (edge1: IEdge, edge2: IEdge) =>
edge1.tag.sequenceOrder.compareTo(edge2.tag.sequenceOrder)
}
// No ordering for the edges around the other nodes
return null
}See Also
Developer's Guide
API
- childOrderingPolicy, OUT_EDGE_COMPARATOR_DATA_KEY
Gets or sets the mapping of edges to their EdgeBundleDescriptor.
This property only has an effect when the treeReductionStage is enabled, which is the default setting.
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.
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 the collection of nodes whose children should be arranged in an interleaved fashion.
Examples
If you already have a collection or IEnumerable<T> containing the nodes whose children should be drawn interleaved, the easiest way is to set the source:
// Place children of selected nodes interleaved
layoutData.interleavedNodes = graphComponent.selection.nodesIf only a few nodes should be configured that way that are not yet in a collection, it's usually easier to use items directly:
layoutData.interleavedNodes.items.add(node1)
layoutData.interleavedNodes.items.add(node2)If the criteria for which nodes should have their children placed interleaved are readily apparent from each node, then the best option is usually to use the predicate property:
// Place children of nodes with many children interleaved
layoutData.interleavedNodes = (node: INode) => graph.outDegree(node) > 5Sample Graphs
See Also
Developer's Guide
API
- INTERLEAVED_NODES_DATA_KEY
Gets or sets the mapping from nodes to their margins.
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 spaceIn 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
Developer's Guide
API
- NODE_MARGIN_DATA_KEY
Gets or sets the mapping from nodes to an object defining the node type, which influences the ordering of child nodes such that those with the same type are preferably placed next to each other.
Sample Graphs
See Also
Gets or sets the collection of edges explicitly marked as not belonging to a tree.
See Also
Gets or sets the sub-data that influences the placement of the ports.
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.
Gets or sets the mapping for marking the node that will be used as root node of the tree.
If a custom root node is specified here, then the rootSelectionPolicy will be ignored.
If multiple root nodes are provided, the first valid node is used. This can be useful when multiple components of the graph are handled independently, e.g. when using ComponentLayout for a forest graph.
Examples
The simplest way to set a custom root node for the layout, if you already have a TNode for that, would be to use the item to specify it as the root:
layoutData.treeRoot.item = rootIn cases where it's easier to look at a TNode to determine whether it should be the root node or not, it's often most convenient to use the predicate property:
// This is equivalent to the declaration above
layoutData.treeRoot = (node) => node === root
// Or use a different way of choosing the root node, e.g. by taking the node whose label
// says 'Root'
layoutData.treeRoot = (node) => node.labels.get(0).text === 'Root'Sometimes there might also be a collection that usually holds just the single node that should be the root, in which case source can be useful:
// Use the first selected node as the root
// This will be evaluated anew each time the layout is applied
layoutData.treeRoot = graphComponent.selection.nodesSample Graphs
See Also
Methods
combineWith
(data: LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel>): LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel>Combines this instance with the given layout data.
combineWith
(data: LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel>): LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel>Parameters
- data: LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel>
- The LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel> to combine this instance with.
Return Value
- LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel>
- The combined layout data.
See Also
Developer's Guide
API
- CompositeLayoutData, GenericLayoutData