package prefuse.util; import java.util.ArrayList; import prefuse.data.Graph; import prefuse.data.Node; import prefuse.data.Schema; import prefuse.data.Tree; /** * Library routines for creating various Graph structures. All Graphs * generated by methods of this class include a String-valued * "label" field. * * @author <a href="http://jheer.org">jeffrey heer</a> */ public class GraphLib { private GraphLib() { // prevent instantiation } // ------------------------------------------------------------------------ // Graph Creation Routines /** * Builds a completely unconnected (edge-free) graph with the given * number of nodes * @param n the number of nodes * @return a graph with n nodes and no edges */ public static Graph getNodes(int n) { Graph g = new Graph(); g.getNodeTable().addColumns(LABEL_SCHEMA); for ( int i=0; i < n; i++ ) { Node node = g.addNode(); node.setString(LABEL, String.valueOf(i)); } return g; } /** * Builds a "star" graph with one central hub connected to the given * number of satellite nodes. * @param n the number of points of the star * @return a "star" graph with n points, for a total of n+1 nodes */ public static Graph getStar(int n) { Graph g = new Graph(); g.getNodeTable().addColumns(LABEL_SCHEMA); Node r = g.addNode(); r.setString(LABEL, "0"); for ( int i=1; i <= n; ++i ) { Node nn = g.addNode(); nn.setString(LABEL, String.valueOf(i)); g.addEdge(r, nn); } return g; } /** * Returns a clique of given size. A clique is a graph in which every node * is a neighbor of every other node. * @param n the number of nodes in the graph * @return a clique of size n */ public static Graph getClique(int n) { Graph g = new Graph(); g.getNodeTable().addColumns(LABEL_SCHEMA); Node nodes[] = new Node[n]; for ( int i = 0; i < n; ++i ) { nodes[i] = g.addNode(); nodes[i].setString(LABEL, String.valueOf(i)); } for ( int i = 0; i < n; ++i ) { for ( int j = i; j < n; ++j ) if ( i != j ) g.addEdge(nodes[i], nodes[j]); } return g; } /** * Returns a graph structured as an m-by-n grid. * @param m the number of rows of the grid * @param n the number of columns of the grid * @return an m-by-n grid structured graph */ public static Graph getGrid(int m, int n) { Graph g = new Graph(); g.getNodeTable().addColumns(LABEL_SCHEMA); Node[] nodes = new Node[m*n]; for ( int i = 0; i < m*n; ++i ) { nodes[i] = g.addNode(); nodes[i].setString(LABEL, String.valueOf(i)); if ( i >= n ) g.addEdge(nodes[i-n], nodes[i]); if ( i % n != 0 ) g.addEdge(nodes[i-1], nodes[i]); } return g; } public static Graph getHoneycomb(int levels) { Graph g = new Graph(); g.getNodeTable().addColumns(LABEL_SCHEMA); ArrayList layer1 = halfcomb(g, levels); ArrayList layer2 = halfcomb(g, levels); for ( int i=0; i<(levels<<1); ++i ) { Node n1 = (Node)layer1.get(i); Node n2 = (Node)layer2.get(i); g.addEdge(n1, n2); } return g; } private static ArrayList halfcomb(Graph g, int levels) { ArrayList top = new ArrayList(); ArrayList layer = new ArrayList(); int label = 0; for ( int i=0; i<levels; ++i ) { Node n = g.addNode(); n.setString(LABEL, String.valueOf(label++)); top.add(n); } for ( int i=0; i<levels; ++i ) { Node n = null; for ( int j=0; j<top.size(); ++j ) { Node p = (Node)top.get(j); if ( n == null ) { n = g.addNode(); n.setString(LABEL, String.valueOf(label++)); layer.add(n); } g.addEdge(p, n); n = g.addNode(); n.setString(LABEL, String.valueOf(label++)); layer.add(n); g.addEdge(p, n); } if ( i == levels-1 ) { return layer; } top.clear(); for ( int j=0; j<layer.size(); ++j ) { Node p = (Node)layer.get(j); n = g.addNode(); n.setString(LABEL, String.valueOf(label++)); top.add(n); g.addEdge(p, n); } layer.clear(); } // should never happen return top; } /** * Returns a balanced tree of the requested breadth and depth. * @param breadth the breadth of each level of the tree * @param depth the depth of the tree * @return a balanced tree */ public static Tree getBalancedTree(int breadth, int depth) { Tree t = new Tree(); t.getNodeTable().addColumns(LABEL_SCHEMA); Node r = t.addRoot(); r.setString(LABEL, "0,0"); if ( depth > 0 ) balancedHelper(t, r, breadth, depth-1); return t; } private static void balancedHelper(Tree t, Node n, int breadth, int depth) { for ( int i=0; i<breadth; ++i ) { Node c = t.addChild(n); c.setString(LABEL, i+","+c.getDepth()); if ( depth > 0 ) balancedHelper(t,c,breadth,depth-1); } } /** * Returns a left deep binary tree * @param depth the depth of the tree * @return the generated tree */ public static Tree getLeftDeepTree(int depth) { Tree t = new Tree(); t.getNodeTable().addColumns(LABEL_SCHEMA); Node r = t.addRoot(); r.setString(LABEL, "0,0"); deepHelper(t, r, 2, depth, true); return t; } /** * Returns a right deep binary tree * @param depth the depth of the tree * @return the generated Tree */ public static Tree getRightDeepTree(int depth) { Tree t = new Tree(); t.getNodeTable().addColumns(LABEL_SCHEMA); Node r = t.addRoot(); r.setString(LABEL, "0,0"); deepHelper(t, r, 2, depth, false); return t; } /** * Create a diamond tree, with a given branching factor at * each level, and depth levels for the two main branches. * @param b the number of children of each branch node * @param d1 the length of the first (left) branch * @param d2 the length of the second (right) branch * @return the generated Tree */ public static Tree getDiamondTree(int b, int d1, int d2) { Tree t = new Tree(); t.getNodeTable().addColumns(LABEL_SCHEMA); Node r = t.addRoot(); r.setString(LABEL, "0,0"); Node left = t.addChild(r); left.setString(LABEL, "1,0"); Node right = t.addChild(r); right.setString(LABEL, "1,1"); deepHelper(t, left, b, d1-2, true); deepHelper(t, right, b, d1-2, false); while ( left.getFirstChild() != null ) left = left.getFirstChild(); while ( right.getLastChild() != null ) right = right.getLastChild(); deepHelper(t, left, b, d2-1, false); deepHelper(t, right, b, d2-1, true); return t; } private static void deepHelper(Tree t, Node n, int breadth, int depth, boolean left) { Node c = t.addChild(n); c.setString(LABEL, "0,"+c.getDepth()); if ( left && depth > 0 ) deepHelper(t, c, breadth, depth-1, left); for ( int i=1; i<breadth; ++i ) { c = t.addChild(n); c.setString(LABEL, i+","+c.getDepth()); } if ( !left && depth > 0 ) deepHelper(t, c, breadth, depth-1, left); } // ------------------------------------------------------------------------ /** Label data field included in generated Graphs */ public static final String LABEL = "label"; /** Node table schema used for generated Graphs */ public static final Schema LABEL_SCHEMA = new Schema(); static { LABEL_SCHEMA.addColumn(LABEL, String.class, ""); } } // end of class GraphLib