Working with the Layout Graph Structure

Grouped Graph Setup for Layout

Important

When using the IGraph-based graph structure of yFiles for Silverlight Viewer, the setup of a grouped graph's hierarchy of nodes is done transparently by the adapter mechanisms prior to layout algorithm invocation. See also Chapter 8, Using yFiles for Silverlight Algorithms Functionality.

Example 13.4, “Layout preparation” demonstrates how to set up a grouped graph. Basically, the data providers that hold the necessary information about the grouped graph's hierarchy of nodes have to be filled and be registered with the graph using the data provider look-up keys defined in class GroupingKeys.

Example 13.4. Layout preparation

// 'graph' is of type yWorks.yFiles.Layout.LayoutGraph.

// Create the node maps that are to be used as data providers later on.
INodeMap groupKey = graph.CreateNodeMap();
INodeMap nodeID = graph.CreateNodeMap();
INodeMap parentNodeID = graph.CreateNodeMap();

// Register the node maps as data providers with the graph.
// Use the "well-known" look-up keys defined in class GroupingKeys.
graph.AddDataProvider(GroupingKeys.GroupDpKey, groupKey);
graph.AddDataProvider(GroupingKeys.NodeIdDpKey, nodeID);
graph.AddDataProvider(GroupingKeys.ParentNodeIdDpKey, parentNodeID);

// Now, set up the hierarchy of nodes of the grouped graph, i.e., define some
// of the nodes to be group nodes and others to be their content.
MySetupNodeHierarchy(graph, groupKey, nodeID, parentNodeID);

// Invoke the layouter.
InvokeLayout(graph, new IncrementalHierarchicLayouter(), false);

The information for the node IDs and the parent node IDs is of symbolic nature that is used in the process of layout calculation to identify the proper parent for a given child, but also to find all children that belong to the same parent. Hence, it is important for the symbolic IDs to properly match between these two data providers, so that the grouped graph's hierarchy of nodes is correctly expressed.

Example 13.5, “Encoding a grouped graph's hierarchy of nodes in data providers” demonstrates possible content for the data providers. Here, the nodes themselves are used to denote symbolic IDs for both "ordinary" nodes and group nodes. Carefully observe the usage of the indirection scheme in this example for setting up the parent-child relation.

Example 13.5. Encoding a grouped graph's hierarchy of nodes in data providers

// Now, set up the hierarchy of nodes of the grouped graph, i.e., define some
// of the nodes to be group nodes and others to be their content.
for (int i = 0; i < 10; i++) {
  // Nodes 1, 5, and 9 are defined to be group nodes.
  if (i % 4 == 1) {
    groupKey.SetBool(n[i], true);
    // Set a symbolic ID for the group node that is used for look-up purposes.
    nodeID.Set(n[i], n[i]);
    continue;
  }

  // Set a symbolic ID for the node that is used for look-up purposes.
  nodeID.Set(n[i], n[i]);

  // Node 2 is defined child of node 1;
  // node 6 is defined child of node 5.
  if (i % 4 == 2) {
    // Establish the relation to the parent.
    parentNodeID.Set(n[i], nodeID.Get(n[i - 1]));
    continue;
  }

  // Node 3 is defined child of node 1;
  // node 7 is defined child of node 5.
  if (i % 4 == 3) {
    // Establish the relation to the parent.
    parentNodeID.Set(n[i], nodeID.Get(n[i - 2]));
  }

  // Nodes 0, 4, and 8 remain "ordinary" nodes...
}

A group node's size is determined by the bounding box that encloses its children and additional insets that are added to each of the box's side. To specify insets individually, a data provider can be used to hold an Insets object for each group node. This data provider is then registered with the graph using the look-up key GroupNodeInsetsDpKey defined in class GroupingKeys.

Example 13.6, “Defining a group node's insets” shows how to add individual Insets objects for group nodes to a node map, and how the node map is registered as a data provider with the graph.

Example 13.6. Defining a group node's insets

// 'graph' is of type yWorks.yFiles.Layout.LayoutGraph.

// Create the node map that is to be used as a data provider later on.
INodeMap groupNodeInsets = graph.CreateNodeMap();

// Predefine some Insets objects.
Insets[] insets = new Insets[3];
insets[0] = new Insets(10, 20, 30, 40);
insets[1] = new Insets(20, 20, 20, 20);
insets[2] = new Insets(40, 30, 20, 10);

NodeList gnl = GetListOfAllGroupNodes(graph);
foreach (Node n in gnl) {
  groupNodeInsets.Set(n, insets[GetGroupType(n)]);
}

// Register the node map as a data provider with the graph.
// Use the "well-known" look-up keys defined in class GroupingKeys.
graph.AddDataProvider(GroupingKeys.GroupNodeInsetsDpKey, groupNodeInsets);

// Invoke the layouter.
InvokeLayout(graph, new IncrementalHierarchicLayouter(), false);

Class FixedGroupLayoutStage adds support for the fixed group node policy to both hierarchical layout and orthogonal layout. Example 13.7, “Setup for fixed group node content” shows how the fixed group node policy is realized as a layout stage that is prepended to the actual layout algorithm's invocation.

Example 13.7. Setup for fixed group node content

void InvokeLayout(LayoutGraph graph, CanonicMultiStageLayouter layouter,
                  bool orthogonal) {
  // Create a specialized layout stage that fixes the contents of the group
  // nodes.
  FixedGroupLayoutStage fixedGroupLayoutStage = new FixedGroupLayoutStage();
  if (orthogonal) {
    fixedGroupLayoutStage.InterEdgeRoutingStyle =
      InterEdgeRoutingStyle.Orthogonal;
  }

  // Prepend the stage to the given layout algorithm.
  layouter.PrependStage(fixedGroupLayoutStage);

  // Invoke buffered layout for the given layout algorithm.
  new BufferedLayouter(layouter).DoLayout(graph);

  // Remove the prepended layout stage.
  layouter.RemoveStage(fixedGroupLayoutStage);
}