Working with the Layout Graph Structure

Grouped Graph Setup for Layout

Important

When using the IGraph-based graph structure of yFiles FLEX, 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 3, Using yFiles FLEX Client Layout Extension Functionality.

Example 8.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 8.4. Layout preparation

// 'graph' is of type com.yworks.yfiles.layout.LayoutGraph.

// Create the node maps that are to be used as data providers later on.
var groupKey:NodeMap = graph.createNodeMap();
var nodeID:NodeMap = graph.createNodeMap();
var parentNodeID:NodeMap = 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.GROUP_DPKEY, groupKey);
graph.addDataProvider(GroupingKeys.NODE_ID_DPKEY, nodeID);
graph.addDataProvider(GroupingKeys.PARENT_NODE_ID_DPKEY, 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 8.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 8.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 (var i:int = 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.setObject(n[i], n[i]);
    continue;
  }

  // Set a symbolic ID for the node that is used for look-up purposes.
  nodeID.setObject(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.setObject(n[i], nodeID.getObject(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.setObject(n[i], nodeID.getObject(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 a YInsets object for each group node. This data provider is then registered with the graph using the look-up key GROUP_NODE_INSETS_DPKEY defined in class GroupingKeys.

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

Example 8.6. Defining a group node's insets

// 'graph' is of type com.yworks.yfiles.layout.LayoutGraph.

// Create the node map that is to be used as a data provider later on.
var groupNodeInsets:NodeMap = graph.createNodeMap();

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

var gnl:NodeList = myGetListOfAllGroupNodes(graph);
for (var nc:NodeCursor = gnl.nodes(); nc.ok(); nc.next()) {
  var n:Node = nc.node();
  groupNodeInsets.setObject(n, insets[myGetGroupType(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.GROUP_NODE_INSETS_DPKEY, groupNodeInsets);

Class FixedGroupLayoutStage adds support for the fixed group node policy to both hierarchical layout and orthogonal layout. Example 8.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 8.7. Setup for fixed group node content

function invokeLayout(graph:LayoutGraph, layouter:CanonicMultiStageLayouter,
                      orthogonal:Boolean):void {
  // Create a specialized layout stage that fixes the contents of the group
  // nodes.
  var fixedGroupLayoutStage:FixedGroupLayoutStage = new FixedGroupLayoutStage();
  if (orthogonal) {
    fixedGroupLayoutStage.interEdgeRoutingStyle =
      FixedGroupLayoutStage.ROUTING_STYLE_ORTHOGONAL;
  }

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

  // Invoke buffered layout for the given layout algorithm.
  BufferedLayouter.newBufferedLayouter2(layouter).doLayout(graph);

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