Layout of nested graphs in code
Describes how to perform a layout on nested graphs.
Explains how layouts are performed on nested graphs.
Explains the order in which recursive layouts are applied on nested graphs.
Describes how to obtain a nested graph with the same layout throughout.
Describes the case where you want to mix different layouts in one nested graph.
The classes that support nested graphs
The IlvGrapher class provided by Rogue Wave® JViews Framework allows nested graphs to be represented. This facility is useful for applications that are not based on Rogue Wave JViews Diagrammer or on styling.
In an application that works directly on an instance of IlvGrapher, a recursive layout must be performed explicitly. Additional steps are required to perform a layout on nested graphers.
For more information, see
Nested graphers.
The mechanism uses the auxiliary classes
IlvRecursiveLayout and
IlvMultipleLayout internally. They are explained in detail in
Recursive layout and
Multiple layout.
Order of layouts in recursive layouts
Assume grapher 1 contains two subgraphers L 1.1 and L1.2, and subgrapher 1.1 contains two subgraphers L 1.1.1 and L 1.1.2, as shown in the following figure. The recursive layout needs to be applied in reverse order, as follows:
1. Layout on L 1.1.1
2. Layout on L1.1.2
3. Layout on L1.1
4. Layout on L1.2
5. Layout on L1
Nested graph with recursive layouts
This means that the layout is applied to the graph after all the layouts of its subgraphs have been applied.
In this example, all layouts of subgrapher L 1.1 are finished before the layout of grapher L 1 starts. It is the correct order for a recursive layout. This order ensures that the layout of a subgraph does not invalidate the layout of its parent graphs.
Simple recursion: applying the same layout to all subgraphs
You can apply the same layout where both the following conditions hold:
The same layout algorithm needs to be applied to the topmost graph and all its subgraphs.
The settings of the layout algorithm (that is, the layout parameters) need to be the same for the topmost graph as for all the subgraphs.
The following figure shows an example where a Tree Layout is applied to the topmost graph as well as to all its subgraphs. Moreover, the settings of the Tree Layout algorithm are the same for all the graphs: the application does not need, for example, one flow direction in the topmost graph and a different one in the subgraphs.
Example of a recursive layout of a nested graph
Obtaining such recursive layouts is easy. The class
IlvGraphLayout provides a special version of the
performLayout method:
performLayout(boolean force, boolean redraw, boolean traverse)
When the last boolean argument is set to true, the layout is applied not only to the graph attached to the layout instance, but also, in a recursive way, to its subgraphs.
Internal mechanism
The internal mechanism is based on the principle that a layout instance is used for only one graph and is not reused for its subgraphs. Therefore, the Tree Layout instance is automatically “cloned” using the
copy method of the class
IlvGraphLayout.
The graph layout is applied to a graph model, and the same principle holds for the graph models (see
Using the graph model): a graph model instance is used for only one graph and is not reused for subgraphs.
The graph models for the subgraphs are created by calls to the
getGraphModel method of the class
IlvGraphLayout, which in turn creates the graph model using the method
createGraphModel of the class
IlvGraphModel.
All these operations are done automatically, in a transparent way. All you have to do is to call the method performLayout with the traverse argument set to true.
If needed, you can get the layout instances applied on the subgraphs by calling the following method on IlvGraphLayout:
Enumeration getLayouts(boolean preOrder)
This method returns an enumeration of instances of
IlvGraphLayout. If the
preOrder flag is
true, the layout of the parent graph occurs before the layout of its child nodes in the enumeration. If the
preorder flag is
false, the layout of the parent graph occurs after the layout of its child nodes. For example, in the graph of
Nesting structure in a graph, the call
getLayouts(true) returns the layouts for the subgraphs in this order: L1, L1.1, L1.1.1, L1.1.2, L1.2. The call
getLayouts(false) returns the layouts for the subgraphs in this order: L1.1.1, L1.1.2, L1.1, L1.2, L1.
Java code sample
The following Java™ code sample illustrates how to apply a single layout algorithm to a nested graph:
...
IlvGrapher grapherA = new IlvGrapher();
IlvGrapher grapherB = new IlvGrapher();
// Fill the graphers with nodes and links
...
// grapherB is added as a subgraph of grapherA
grapherA.addNode(grapherB, false);
// Create the layout instance
IlvTreeLayout layout = new IlvTreeLayout();
// Attach the topmost grapher to the layout
layout.attach(grapherA);
// Perform the recursive layout
try {
int code = layout.performLayout(true, true, true);
System.out.println("Layout completed (code " +
code + ")");
}
catch (IlvGraphLayoutException e) {
System.err.println(e.getMessage());
}
...
// Detach the grapher when layout no more needed
layout.detach();
...
For experts
In this first variant, the grapher adapter (the graph model of
IlvGrapher) is handled internally. If a grapher adapter is explicitly allocated, it must be disposed of when no longer necessary. However, all grapher adapters that are created internally are always disposed of automatically. Here is an equivalent variant that shows how to use the grapher adapter:
...
IlvGrapher grapherA = new IlvGrapher();
IlvGrapher grapherB = new IlvGrapher();
// Fill the graphers with nodes and links
...
// grapherB is added as a subgraph of grapherA
grapherA.addNode(grapherB, false);
// Create the layout instance
IlvTreeLayout layout = new IlvTreeLayout();
// Create a grapher adapter for the topmost grapher
IlvGrapherAdapter adapter = new IlvGrapherAdapter(grapherA);
// Attach the adapter to the layout
layout.attach(adapter);
// Perform the recursive layout
try {
// perform the layout with argument traverse = true
int code = layout.performLayout(true, true, true);
System.out.println("Layout completed (code " +
code + ")");
}
catch (IlvGraphLayoutException e) {
System.err.println(e.getMessage());
}
...
// Detach the adapter when layout no more needed
layout.detach();
...
Layout parameters
Section
Internal mechanism explains that, when applying the same layout algorithm in a recursive way, the layout instances for the subgraphs are obtained by “cloning” the layout instance attached to the topmost graph.
The layout parameters of the “clone” are the same as the parameters of the topmost layout, except for the parameters that are specific to a node or a link. Such parameters are not copied when the layouts are cloned and need to be set separately for each layout instance.
For example, if you need to declare a node
node1 contained in the subgraph
grapherB of the topmost graph
grapherA as fixed (see
Preserve fixed nodes), you can use the following code:
...
IlvGrapher grapherA = new IlvGrapher();
IlvGrapher grapherB = new IlvGrapher();
// fill the graphers with nodes and links;
// grapherB is added as a subgraph of grapherA
grapherA.addNode(grapherB, false);
// Create the layout instance
IlvTreeLayout layout = new IlvTreeLayout();
// Attach the topmost grapher to the layout
layout.attach(grapherA);
// Ask the layout algorithm to not move the nodes
// specified as fixed. This settings is automatically
// copied on the sublayouts. Do not specify this global
// settings directly on the sublayout, because it gets automatically
// the same settings as the topmost layout
layout.setPreserveFixedNodes(true);
// Search the layout instance used for grapherB
IlvGraphLayout subLayout = null;
Enumeration layouts = layout.getLayouts(true);
while (layouts.hasMoreElements()) {
subLayout = (IlvGraphLayout)layouts.nextElement();
if (subLayout.getGraphModel().getGrapher() == grapherB)
break;
}
// Specify node1 (contained in grapherB) as fixed
subLayout.setFixed(node1, true);
// Now perform the recursive layout. The node node1 will be considered as fixed
// by the layout applied to grapherB
...
NOTE You should not try to change any global settings of the layouts applied to the subgraphs (that is, settings that are not specific to a node or a link). These settings are copied anyway from the layout instance of the topmost grapher, so your changes would be erased just before the recursive layout runs.
Advanced recursion: mixing different layouts in a nested graph
The need for mixing layouts arises when at least one of the following conditions is met:
The layout algorithm to be applied on subgraphs is not the same as the algorithm needed for the topmost graph.
Different layouts need to be applied to different subgraphs.
The same layout algorithm needs to be applied to different graphs but with different settings.
In these cases of advanced recursion, where you want to apply different layouts to different subgraphs, you must specify which layout must be used for which subgraph. You must start the layouts in the correct order. This is called recursive layout.
The class
IlvRecursiveLayout is a subclass of
IlvGraphLayout, but it is not a real layout algorithm. It is rather a facility to apply other layout algorithms recursively on a nested graph.
The class
IlvRecursiveLayout can also be used to apply the same layout to all subgraphs. In fact, when using the API explained in subsection
Simple recursion: applying the same layout to all subgraphs, an instance of
IlvRecursiveLayout is used internally.
The class IlvRecursiveLayout can be used to apply multiple layouts to the same nested graph. This is necessary if for each subgraph, a node layout and a separate link layout must be applied.
Further details and code samples of the class
IlvRecursiveLayout are explained in the following section
Recursive layout.
To apply layout algorithms recursively:
1. Allocate and attach an instance of IlvRecursiveLayout. Since it is a subclass of IlvGraphLayout, you use the same mechanism as for all other graph layout classes:
IlvRecursiveLayout recLayout = new IlvRecursiveLayout();
IlvGrapher topLevelGrapher = ...
recLayout.attach(topLevelGrapher);
2. Specify which layout style should be used for each subgraph.
You must allocate an individual instance of IlvGraphLayout for each subgraph.
recLayout.setLayout(subgraph1, new IlvTreeLayout());
recLayout.setLayout(subgraph2, new IlvBusLayout());
recLayout.setLayout(subgraph3, new IlvGridLayout());
3. Set the layout parameters of these individual layouts of the subgraphs as needed.
4. Apply the recursive layout to the top-level grapher. This automatically applies the sublayouts to the subgraphs as well. You use the same method as for all other graph layout classes, because IlvRecursiveLayout is a subclass of IlvGraphLayout.
try {
recLayout.performLayout();
}
catch (IlvGraphLayoutException e) {
System.err.println(e.getMessage());
}
5. Detach the recursive layout from the top-level grapher when it is no longer needed. This automatically detaches all sublayouts from all subgraphers.
recLayout.detach();
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.