private void initDataGroups() {
    // create sample graph
    // 9 nodes broken up into 3 interconnected cliques
    Graph g = new Graph();
    for (int i = 0; i < 3; ++i) {
      Node n1 = g.addNode();
      Node n2 = g.addNode();
      Node n3 = g.addNode();
      g.addEdge(n1, n2);
      g.addEdge(n1, n3);
      g.addEdge(n2, n3);
    }
    g.addEdge(0, 3);
    g.addEdge(3, 6);
    g.addEdge(6, 0);

    // add visual data groups
    VisualGraph vg = m_vis.addGraph(GRAPH, g);
    m_vis.setInteractive(EDGES, null, false);
    m_vis.setValue(NODES, null, VisualItem.SHAPE, new Integer(Constants.SHAPE_ELLIPSE));

    at = m_vis.addAggregates(AGGR);
    at.addColumn(VisualItem.POLYGON, float[].class);
    at.addColumn("id", int.class);

    // add nodes to aggregates
    // create an aggregate for each 3-clique of nodes
    Iterator nodes = vg.nodes();
    for (int i = 0; i < 3; ++i) {
      AggregateItem aitem = (AggregateItem) at.addItem();
      aitem.setInt("id", i);
      for (int j = 0; j < 3; ++j) {
        aitem.addItem((VisualItem) nodes.next());
      }
    }
  }
  /**
   * @see edu.berkeley.guir.prefuse.action.Action#run(edu.berkeley.guir.prefuse.ItemRegistry,
   *     double)
   */
  public void run(double frac) {

    AggregateTable aggr = (AggregateTable) m_vis.getGroup(m_group);
    // do we have any  to process?
    int num = aggr.getTupleCount();
    if (num == 0) return;

    // update buffers
    int maxsz = 0;
    for (Iterator aggrs = aggr.tuples(); aggrs.hasNext(); )
      maxsz = Math.max(maxsz, 4 * 2 * ((AggregateItem) aggrs.next()).getAggregateSize());
    if (m_pts == null || maxsz > m_pts.length) {
      m_pts = new double[maxsz];
    }

    // compute and assign convex hull for each aggregate
    Iterator aggrs = m_vis.visibleItems(m_group);
    while (aggrs.hasNext()) {
      AggregateItem aitem = (AggregateItem) aggrs.next();

      int idx = 0;
      if (aitem.getAggregateSize() == 0) continue;
      VisualItem item = null;
      Iterator iter = aitem.items();
      while (iter.hasNext()) {
        item = (VisualItem) iter.next();
        if (item.isVisible()) {
          addPoint(m_pts, idx, item, m_margin);
          idx += 2 * 4;
        }
      }
      // if no aggregates are visible, do nothing
      if (idx == 0) continue;

      // compute convex hull
      double[] nhull = GraphicsLib.convexHull(m_pts, idx);

      // prepare viz attribute array
      float[] fhull = (float[]) aitem.get(VisualItem.POLYGON);
      if (fhull == null || fhull.length < nhull.length) fhull = new float[nhull.length];
      else if (fhull.length > nhull.length) fhull[nhull.length] = Float.NaN;

      // copy hull values
      for (int j = 0; j < nhull.length; j++) fhull[j] = (float) nhull[j];
      aitem.set(VisualItem.POLYGON, fhull);
      aitem.setValidated(false); // force invalidation
    }
  }