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 showing on the left, Part
1 containing Network 1 with two subgraphs, Network 1.1 and 1.2, and
on the right, Part 2 containing Network 2 with two subgraphs, Network
2.1 and 2.2. Read Part 1 before Part 2. Network 1 contains one node
at the first level, three nodes at the second level, and two nodes
at the third level. The nodes on the second and third levels are laid
out horizontally. Node 1 links to all nodes on the second level. The
middle node on the second level links to both nodes on the third level.
Network 2 has the same layout as Network 1. Network 1.1 contains one
node at the first level, two nodes at the second level, and three
nodes at the third level. The nodes on the second and third levels
are laid out horizontally. Node 1 links to all nodes on the second
level. The node on the right of the second level links to all the
nodes on the third level. Network 2.1 has the same layout as Network
1.1. Network 1.2 contains one node at the first level and three nodes
at the second level. The nodes on the second level are laid out horizontally.
Node 1 links to all nodes on the second level. Network 2.2 has the
same layout as Network 1.2.](../../../GraphLayout/_media/nestedGraphLarger3_default.gif)
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 (seePreserve 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.