Search this API

y.layout.grouping
Class RecursiveGroupLayouter

java.lang.Object
  extended by y.layout.AbstractLayoutStage
      extended by y.layout.grouping.RecursiveGroupLayouter
All Implemented Interfaces:
Layouter, LayoutStage

public class RecursiveGroupLayouter
extends AbstractLayoutStage

This layout algorithm recursively traverses a hierarchically organized graph in a bottom-up fashion and applies a specified layout algorithm to the contents (direct children) of each group node.

Layout Style

The way a graph is arranged depends on the layout algorithms which are applied to the different group nodes. RecursiveGroupLayouter is able to produce different layout styles for the content of each group node.

This layout algorithm can be either applied if a layout algorithm cannot handle grouped graphs by itself or if the content of (some) group nodes should be arranged differently.


Concept

RecursiveGroupLayouter uses a hierarchy tree representation of the grouped graph in which the content nodes are the children of their containing group node. That way, it can traverse the tree recursively while arranging only the direct children of each group node. The layout algorithm starts by arranging the leaves in the hierarchy tree, then works its way up to the root computing the layout for each group node in the tree.

A) Group Handling

All nodes other than the direct children are temporarily hidden. The layout algorithm performs two steps for each group node.
  1. It arranges the direct children using either the core layout algorithm or a special layout algorithm retrieved from a DataProvider registered with GROUP_NODE_LAYOUTER_DPKEY. The content of group nodes among the children is already arranged at this time and will be ignored. These group nodes are handled like normal nodes with a size that encloses the content.

    The following two special cases should be considered. If null is specified as layout algorithm for a group, the recursion will be disabled and the group is handled non-recursively: the group itself and its content is arranged by the algorithm specified for the nearest ancestor which has a non-null algorithm mapped to it. Importantly, this must be distinguished from the second case where the layout of the group content should remain unchanged. This is achieved when the group is associated with NULL_LAYOUTER as responsible layout algorithm (or any another algorithm that does not change the layout).

  2. Then RecursiveGroupLayouter computes the final size of the group node using an implementation of GroupBoundsCalculator. Customized GroupBoundsCalculators can be specified using setGroupBoundsCalculator(GroupBoundsCalculator). Aside from the resulting layout, this size is used in the following iteration.

B) Top-Level Hierarchy

After a layout is applied to all group nodes, the layout algorithm uses the core layout algorithm to arrange the top level hierarchy. Note that RecursiveGroupLayouter can run without a core layout. In this case no layout is calculated for the top level hierarchy. Still, for group nodes with a non-null algorithm, the bounds are adjusted to fit their respective contents.

C) Inter-Edge Routing

Finally, routes for the edges whose source node is located at a different hierarchy level than its target node are computed. The edge routing algorithm for these so-called inter-edges can be customized.

Features

There are two alternatives for applying different layout styles to the contents of group nodes:

  1. Mapping each group node to a corresponding layout algorithm by registering a DataProvider with key GROUP_NODE_LAYOUTER_DPKEY. The content of the hierarchy root is arranged with the core layout algorithm.
  2. Using LayoutMultiplexer as core layout algorithm.

Since RecursiveGroupLayouter delegates the actual arrangement of the graph to other layout algorithms, it will support the same features as the currently used layout algorithm.

The improvement of the routing of inter-edges is based on the insertion of PortCandidates or the conversion of PortConstraints into PortCandidates. Hence, they only work well if the applied layout algorithm supports PortCandidates.

This algorithm also provides a From Sketch mode that should be activated if the applied layout algorithm runs in From Sketch mode, too. Otherwise, the initial coordinates may not be considered correctly.

It is also possible to apply individual layout styles to different sub-graphs using this algorithm without actually defining group nodes: Use TemporaryGroupNodeInsertionStage and specify RecursiveGroupLayouter as its core layout algorithm. The stage allows to define components (i.e. sub-graphs) which are internally enclosed by a temporary group node. To assign a specific Layouter instance for a component, simply map the component Id to the desired Layouter in a DataProvider registered with key TemporaryGroupNodeInsertionStage.COMPONENT_LAYOUT_ALGORITHM_DPKEY. It is then not necessary to use key GROUP_NODE_LAYOUTER_DPKEY. Note, however, that only non-nested components are supported by the mentioned stage.

 
To only recalculate the group node bounds and not change any content while also keeping the top-level layout as it is, apply RecursiveGroupLayouter with core layout set to NULL_LAYOUTER and no further configuration.
 
Your browser does not support SVG content.

Field Summary
static java.lang.Object GROUP_NODE_LAYOUTER_DPKEY
          A DataProvider key for arranging the content of each group node with an individual layout algorithm.
static java.lang.Object GROUP_NODE_PARTITION_GRID_DPKEY
          A DataProvider key for specifying a local partition grid for each group node.
static Layouter NULL_LAYOUTER
          A constant that represents a Layouter implementation that does nothing.
static java.lang.Object SOURCE_SPLIT_ID_DPKEY
          A DataProvider key for assigning source split ids to edges connecting to group nodes.
static java.lang.Object TARGET_SPLIT_ID_DPKEY
          A DataProvider key for assigning target split ids to edges connecting to group nodes.
 
Fields inherited from interface y.layout.Layouter
EDGE_ID_DPKEY, NODE_ID_DPKEY, NODE_TYPE_DPKEY, SELECTED_EDGES, SELECTED_NODES
 
Constructor Summary
RecursiveGroupLayouter()
          Creates a new instance of RecursiveGroupLayouter with default settings.
RecursiveGroupLayouter(Layouter core)
          Creates a new instance of RecursiveGroupLayouter with default settings using the given layout algorithm.
RecursiveGroupLayouter(Layouter core, GroupBoundsCalculator gbc)
          Creates a new instance of RecursiveGroupLayouter with default settings using the given layout algorithm and GroupBoundsCalculator implementation.
 
Method Summary
 boolean canLayout(LayoutGraph graph)
          Accepts all general graphs without exception.
 void doLayout(LayoutGraph graph)
          Invokes a recursive traversal through the grouping hierarchy of the given graph during which the specified layout algorithms are applied to the content of the groups.
 GroupBoundsCalculator getGroupBoundsCalculator()
          Returns a GroupBoundsCalculator which computes the sizes of all group nodes.
 Layouter getInterEdgeRouter()
          Returns the current edge routing algorithm for handling inter-edges.
 java.lang.Object getInterEdgesDpKey()
          Returns the key for marking the inter-edges to be routed.
 boolean isAutoAssignPortCandidatesEnabled()
          Returns whether or not temporary PortCandidates are inserted to improve the routing of inter-edges.
 boolean isConsiderEmptyGroupsEnabled()
          Returns whether empty group nodes are handled like group nodes with content or like normal nodes.
 boolean isConsiderSketchEnabled()
          Returns whether or not to consider the initial coordinates of the graph elements.
 boolean isReplacingPortConstraintsEnabled()
          Returns whether or not PortConstraints of inter-edges are temporarily replaced by PortCandidates.
protected  void routeInterEdges(LayoutGraph graph, EdgeList interEdges)
          Reroutes the given inter-edges using the current edge routing algorithm.
 void setAutoAssignPortCandidatesEnabled(boolean autoAssignPortCandidatesEnabled)
          Specifies whether or not temporary PortCandidates are inserted to improve the routing of inter-edges.
 void setConsiderEmptyGroupsEnabled(boolean enabled)
          Specifies whether empty group nodes are handled like group nodes with content or like normal nodes.
 void setConsiderSketchEnabled(boolean fromSketchModeEnabled)
          Specifies whether or not to consider the initial coordinates of the graph elements.
 void setGroupBoundsCalculator(GroupBoundsCalculator groupBoundsCalculator)
          Specifies a GroupBoundsCalculator which computes the sizes of all group nodes.
 void setInterEdgeRouter(Layouter interEdgeRouter)
          Specifies the current edge routing algorithm for handling inter-edges.
 void setInterEdgesDpKey(java.lang.Object interEdgesDpKey)
          Specifies the key for marking the inter-edges to be routed.
 void setReplacingPortConstraintsEnabled(boolean replacingPortConstraintsEnabled)
          Specifies whether or not PortConstraints of inter-edges are temporarily replaced by PortCandidates.
 
Methods inherited from class y.layout.AbstractLayoutStage
canLayoutCore, doLayoutCore, getCoreLayouter, setCoreLayouter
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

GROUP_NODE_PARTITION_GRID_DPKEY

public static final java.lang.Object GROUP_NODE_PARTITION_GRID_DPKEY
A DataProvider key for specifying a local partition grid for each group node.

Each recursively handled group node can get its own local PartitionGrid. The grid is then valid for the child nodes of the group and only visible for the layout algorithm that is responsible for the group's content. Thus, to properly consider the grid it is required that the responsible algorithm supports the partition grid structure.

The mapping of the nodes to the partition cells is still defined by a single DataProvider registered with the graph with key PartitionGrid.PARTITION_CELL_DPKEY. When creating the cell id it is important to call the creation method on the correct partition grid instance.

In addition to the local partition grids it is also allowed to have a global grid which is defined as usual via key PartitionGrid.PARTITION_GRID_DPKEY. This grid is handled by the core layout algorithm responsible for the top-level hierarchy.


GROUP_NODE_LAYOUTER_DPKEY

public static final java.lang.Object GROUP_NODE_LAYOUTER_DPKEY
A DataProvider key for arranging the content of each group node with an individual layout algorithm.

The specified layout algorithm instance is applied to the content of the group node. To arrange the top level elements the core layout algorithm is used.

The core layout is also applied to group nodes if there is no DataProvider registered. Importantly, this must be distinguished from the case that there is a provider but it returns null for a group. Then, RecursiveGroupLayouter handles the corresponding group node non-recursively. The group node and its content is arranged using the layout algorithm instance specified for the nearest ancestor of the group node which is associated with an algorithm.

 
To leave the content of a group node unchanged, the group node can be associated with a layout algorithm which does not change the layout of the graph like NULL_LAYOUTER.
See Also:
NULL_LAYOUTER

SOURCE_SPLIT_ID_DPKEY

public static final java.lang.Object SOURCE_SPLIT_ID_DPKEY
A DataProvider key for assigning source split ids to edges connecting to group nodes. The edges will be aligned with edges that connect to the same group node and have the same split id at their source (preferably) or target.

 
Marking edges with split ids will only provide good results if IncrementalHierarchicLayouter is used as core layout algorithm. Also, the edges need to be routed directly from the content of a group to the group itself.
 
In case a core layout algorithm is used that cannot handle groups, it will fail if it needs to layout a graph containing an edge with a split id.
See Also:
TARGET_SPLIT_ID_DPKEY, IncrementalHierarchicLayouter, EdgeLayoutDescriptor.setDirectGroupContentEdgeRoutingEnabled(boolean)
Sample Graphs:

With source and target split ids

Without source and target split ids (inner edge is routed with inter-edge routing algorithm)

TARGET_SPLIT_ID_DPKEY

public static final java.lang.Object TARGET_SPLIT_ID_DPKEY
A DataProvider key for assigning target split ids to edges connecting to group nodes. The edges will be aligned with edges that connect to the same group node and have the same split id at their source or target (preferably).

 
Marking edges with split ids will only provide good results if IncrementalHierarchicLayouter is used as core layout algorithm. Also, the edges need to be routed directly from the content of a group to the group itself.
 
In case a core layout algorithm is used that cannot handle groups, it will fail if it needs to layout a graph containing an edge with a split id.
See Also:
SOURCE_SPLIT_ID_DPKEY, IncrementalHierarchicLayouter, EdgeLayoutDescriptor.setDirectGroupContentEdgeRoutingEnabled(boolean)
Sample Graphs:

With source and target split ids

Without source and target split ids (inner edge is routed with inter-edge routing algorithm)

NULL_LAYOUTER

public static final Layouter NULL_LAYOUTER
A constant that represents a Layouter implementation that does nothing. This implementation can be assigned to group nodes to keep their content unchanged. The layout algorithm will still calculate the sizes of the group nodes.

Constructor Detail

RecursiveGroupLayouter

public RecursiveGroupLayouter()
Creates a new instance of RecursiveGroupLayouter with default settings.


RecursiveGroupLayouter

public RecursiveGroupLayouter(Layouter core)
Creates a new instance of RecursiveGroupLayouter with default settings using the given layout algorithm.

Parameters:
core - the layout algorithm that is applied in each recursion step

RecursiveGroupLayouter

public RecursiveGroupLayouter(Layouter core,
                              GroupBoundsCalculator gbc)
Creates a new instance of RecursiveGroupLayouter with default settings using the given layout algorithm and GroupBoundsCalculator implementation.

Parameters:
core - the layout algorithm that is applied in each step of the recursion
gbc - the GroupBoundsCalculator for calculating group sizes
Method Detail

isConsiderSketchEnabled

public boolean isConsiderSketchEnabled()
Returns whether or not to consider the initial coordinates of the graph elements. When using the initial coordinates, RecursiveGroupLayouter sets the coordinates of the nodes to their initial position before the corresponding layout algorithm is called.

 
This option should be enabled if RecursiveGroupLayouter uses a layout algorithm that runs in From Sketch mode.
Returns:
true if the initial coordinates of the graph elements are considered, false otherwise
See Also:
setConsiderSketchEnabled(boolean)

setConsiderSketchEnabled

public void setConsiderSketchEnabled(boolean fromSketchModeEnabled)
Specifies whether or not to consider the initial coordinates of the graph elements. When using the initial coordinates, RecursiveGroupLayouter sets the coordinates of the nodes to their initial position before the corresponding layout algorithm is called.

 
This option should be enabled if RecursiveGroupLayouter uses a layout algorithm that runs in From Sketch mode.
Default Value:
The default value is false. The initial coordinates of the nodes are not taken into account.
Parameters:
fromSketchModeEnabled - true if the initial coordinates of the graph elements should be considered, false otherwise

isAutoAssignPortCandidatesEnabled

public boolean isAutoAssignPortCandidatesEnabled()
Returns whether or not temporary PortCandidates are inserted to improve the routing of inter-edges. If enabled, RecursiveGroupLayouter will insert PortCandidates for all inter-edges that cross a group node border. Those PortCandidates are located at the relative position of the real source/target node. Inter-edges that connect to such PortCandidates will be routed when the layout of the containing group node is calculated and will not be rerouted later. This may produce more suitable edge routes but cannot prevent edges from crossing nodes.

Without temporary or user specified PortCandidates, inter-edges will always end at the border/center of the corresponding group node. Thus, they are rerouted afterwards using an edge routing algorithm.

 
Predefined PortCandidates are always satisfied depending on the used layout algorithm, even if this option is disabled.
 
Adding temporary PortCandidates will only have an effect if the layout algorithm supports them.
Returns:
true if temporary port candidates are inserted
See Also:
setAutoAssignPortCandidatesEnabled(boolean), routeInterEdges(LayoutGraph, EdgeList), setInterEdgeRouter(Layouter), PortCandidate

setAutoAssignPortCandidatesEnabled

public void setAutoAssignPortCandidatesEnabled(boolean autoAssignPortCandidatesEnabled)
Specifies whether or not temporary PortCandidates are inserted to improve the routing of inter-edges. If enabled, RecursiveGroupLayouter will insert PortCandidates for all inter-edges that cross a group node border. Those PortCandidates are located at the relative position of the real source/target node. Inter-edges that connect to such PortCandidates will be routed when the layout of the containing group node is calculated and will not be rerouted later. This may produce more suitable edge routes but cannot prevent edges from crossing nodes.

Without temporary or user specified PortCandidates, inter-edges will always end at the border/center of the corresponding group node. Thus, they are rerouted afterwards using an edge routing algorithm.

 
Predefined PortCandidates are always satisfied depending on the used layout algorithm, even if this option is disabled.
 
Adding temporary PortCandidates will only have an effect if the layout algorithm supports them.
Default Value:
The default value is false. No temporary PortCandidates are added.
Parameters:
autoAssignPortCandidatesEnabled - true if temporary port candidates should be inserted
See Also:
routeInterEdges(LayoutGraph, EdgeList), PortCandidate
Sample Graphs:

false

true

isReplacingPortConstraintsEnabled

public boolean isReplacingPortConstraintsEnabled()
Returns whether or not PortConstraints of inter-edges are temporarily replaced by PortCandidates. If disabled, inter-edges will always end at the border/center of the corresponding group node, even if those edges have port constraints. Thus, they are rerouted later without considering the constraint. Enabling this settings may produce more suitable edge routes but cannot prevent edges from crossing nodes.

Port candidates are automatically redirected to their original location. Hence, enabling this option may produce more suitable edge routes if the layout algorithm applied to the content of a group node can handle port candidates.

 
Predefined PortCandidates are always satisfied depending on the used layout algorithm, even if this option is disabled.
 
Temporarily replacing the PortConstraints will only have an effect if the layout algorithm supports PortCandidates.
Returns:
whether or not port constraints are replaced
See Also:
setReplacingPortConstraintsEnabled(boolean), routeInterEdges(LayoutGraph, EdgeList), PortCandidate

setReplacingPortConstraintsEnabled

public void setReplacingPortConstraintsEnabled(boolean replacingPortConstraintsEnabled)
Specifies whether or not PortConstraints of inter-edges are temporarily replaced by PortCandidates. If disabled, inter-edges will always end at the border/center of the corresponding group node, even if those edges have port constraints. Thus, they are rerouted later without considering the constraint. Enabling this settings may produce more suitable edge routes but cannot prevent edges from crossing nodes.

Port candidates are automatically redirected to their original location. Hence, enabling this option may produce more suitable edge routes if the layout algorithm applied to the content of a group node can handle port candidates.

 
Predefined PortCandidates are always satisfied depending on the used layout algorithm, even if this option is disabled.
 
Temporarily replacing the PortConstraints will only have an effect if the layout algorithm supports PortCandidates.
Default Value:
The default value is true. Existing PortConstraints are temporarily replaced with corresponding PortCandidates.
Parameters:
replacingPortConstraintsEnabled - whether or not port constraints should be replaced
See Also:
routeInterEdges(LayoutGraph, EdgeList), PortCandidate

isConsiderEmptyGroupsEnabled

public boolean isConsiderEmptyGroupsEnabled()
Returns whether empty group nodes are handled like group nodes with content or like normal nodes. If they are handled like other group nodes, RecursiveGroupLayouter will resize them according to their (non-existing) content. This results in small empty group nodes. Handled like normal nodes, empty group nodes will keep their initial size.

Returns:
true if empty groups are treated like group nodes, false if they are treated like normal nodes
See Also:
setConsiderEmptyGroupsEnabled(boolean)

setConsiderEmptyGroupsEnabled

public void setConsiderEmptyGroupsEnabled(boolean enabled)
Specifies whether empty group nodes are handled like group nodes with content or like normal nodes. If they are handled like other group nodes, RecursiveGroupLayouter will resize them according to their (non-existing) content. This results in small empty group nodes. Handled like normal nodes, empty group nodes will keep their initial size.

Default Value:
The default value is true. Empty group nodes are resized.
Parameters:
enabled - true if empty groups should be treated like group nodes, false if they are treated like normal nodes
Sample Graphs:

Initial graph

Empty group handled like normal node

Empty group handled like other group nodes

getInterEdgeRouter

public Layouter getInterEdgeRouter()
Returns the current edge routing algorithm for handling inter-edges. During layout, edges that connect from outside a group node to the content inside (inter-edges) are temporarily connected to the group node itself. Hence, these edges have to be routed after restoring the original graph structure using this edge routing algorithm.

It is required that a suitable selection key is specified. The same selection key must be used for setting the sphere of action for the edge router.

Returns:
the edge routing algorithm for inter-edges
See Also:
setInterEdgeRouter(Layouter), setInterEdgesDpKey(Object)

setInterEdgeRouter

public void setInterEdgeRouter(Layouter interEdgeRouter)
Specifies the current edge routing algorithm for handling inter-edges. During layout, edges that connect from outside a group node to the content inside (inter-edges) are temporarily connected to the group node itself. Hence, these edges have to be routed after restoring the original graph structure using this edge routing algorithm.

It is required that a suitable selection key is specified. The same selection key must be used for setting the sphere of action for the edge router.

Default Value:
The default value is null. Edges are routed as straight lines from source to target.
Parameters:
interEdgeRouter - the edge routing algorithm for inter-edges
See Also:
setInterEdgesDpKey(Object)

getInterEdgesDpKey

public java.lang.Object getInterEdgesDpKey()
Returns the key for marking the inter-edges to be routed. The key should be used by the specified inter-edge routing algorithm to obtain the edges to be routed. This layouter automatically marks these edges and registers the DataProvider using the specified key.

Returns:
the inter edge selection key
See Also:
getInterEdgeRouter()

setInterEdgesDpKey

public void setInterEdgesDpKey(java.lang.Object interEdgesDpKey)
Specifies the key for marking the inter-edges to be routed. The key should be used by the specified inter-edge routing algorithm to obtain the edges to be routed. This layouter automatically marks these edges and registers the DataProvider using the specified key.

Default Value:
The default value is Layouter.SELECTED_EDGES
Parameters:
interEdgesDpKey - the inter edge selection key
Throws:
java.lang.IllegalArgumentException - if the specified key is null
See Also:
setInterEdgeRouter(Layouter)

canLayout

public boolean canLayout(LayoutGraph graph)
Accepts all general graphs without exception.

Parameters:
graph - the input graph
Returns:
true if there is a graph that is not null, false otherwise
See Also:
Layouter.doLayout(LayoutGraph)

doLayout

public void doLayout(LayoutGraph graph)
Invokes a recursive traversal through the grouping hierarchy of the given graph during which the specified layout algorithms are applied to the content of the groups.

Parameters:
graph - the input graph
See Also:
Layouter.canLayout(LayoutGraph)

routeInterEdges

protected void routeInterEdges(LayoutGraph graph,
                               EdgeList interEdges)
Reroutes the given inter-edges using the current edge routing algorithm. This method is called after calculating the overall layout when the positions of all nodes and normal edges are fixed.

If no inter-edge router is specified, this method resets the path of all inter-edges that don't connect to the proper location within the group. This may happen for inter-edges without PortCandidates or if the applied layout algorithm doesn't support such constraints.

Parameters:
graph - the input graph
interEdges - the edges which traverse the boundary of a group node
See Also:
setAutoAssignPortCandidatesEnabled(boolean), setReplacingPortConstraintsEnabled(boolean), setInterEdgeRouter(Layouter)

getGroupBoundsCalculator

public GroupBoundsCalculator getGroupBoundsCalculator()
Returns a GroupBoundsCalculator which computes the sizes of all group nodes. This GroupBoundsCalculator is used each time after calculating the layout for a content graph.

Returns:
the current GroupBoundsCalculator instance
See Also:
setGroupBoundsCalculator(GroupBoundsCalculator)

setGroupBoundsCalculator

public void setGroupBoundsCalculator(GroupBoundsCalculator groupBoundsCalculator)
Specifies a GroupBoundsCalculator which computes the sizes of all group nodes. This GroupBoundsCalculator is used each time after calculating the layout for a content graph.

Default Value:
The default value is MinimumSizeGroupBoundsCalculator
Parameters:
groupBoundsCalculator - the new GroupBoundsCalculator instance

© Copyright 2000-2022,
yWorks GmbH.
All rights reserved.