/** * Split the given graph into its connected components. * * @param graph an input graph. * @return a list of components that can be processed one by one. */ public List<FGraph> split(final FGraph graph) { Boolean separate = graph.getProperty(LayoutOptions.SEPARATE_CC); if (separate == null || separate.booleanValue()) { boolean[] visited = new boolean[graph.getNodes().size()]; List<FEdge>[] incidence = buildIncidenceLists(graph); // perform DFS starting on each node, collecting connected components List<FGraph> components = new LinkedList<FGraph>(); for (FNode node : graph.getNodes()) { FGraph comp = dfs(node, null, visited, incidence); if (comp != null) { comp.copyProperties(graph); components.add(comp); } } // redistribute identifier numbers to each component if (components.size() > 1) { for (FGraph comp : components) { int id = 0; for (FNode node : comp.getNodes()) { node.id = id++; } } } return components; } return Lists.newArrayList(graph); }
/** * Pack the given components into a single graph. * * @param components a list of components. * @return a single graph that contains all components. */ public FGraph recombine(final List<FGraph> components) { if (components.size() == 1) { return components.get(0); } else if (components.size() <= 0) { return new FGraph(); } // assign priorities and sizes for (FGraph graph : components) { int priority = 0; double minx = Integer.MAX_VALUE, miny = Integer.MAX_VALUE, maxx = Integer.MIN_VALUE, maxy = Integer.MIN_VALUE; for (FNode node : graph.getNodes()) { Integer p = node.getProperty(LayoutOptions.PRIORITY); if (p != null) { priority += p; } minx = Math.min(minx, node.getPosition().x); miny = Math.min(miny, node.getPosition().y); maxx = Math.max(maxx, node.getPosition().x + node.getSize().x); maxy = Math.max(maxy, node.getPosition().y + node.getSize().y); } graph.setProperty(Properties.PRIORITY, priority); graph.setProperty(Properties.BB_UPLEFT, new KVector(minx, miny)); graph.setProperty(Properties.BB_LOWRIGHT, new KVector(maxx, maxy)); } // sort the components by their priority and size Collections.sort( components, new Comparator<FGraph>() { public int compare(final FGraph graph1, final FGraph graph2) { int prio = graph2.getProperty(Properties.PRIORITY) - graph1.getProperty(Properties.PRIORITY); if (prio == 0) { KVector size1 = graph1 .getProperty(Properties.BB_LOWRIGHT) .clone() .sub(graph1.getProperty(Properties.BB_UPLEFT)); KVector size2 = graph2 .getProperty(Properties.BB_LOWRIGHT) .clone() .sub(graph2.getProperty(Properties.BB_UPLEFT)); return Double.compare(size1.x * size1.y, size2.x * size2.y); } return prio; } }); FGraph result = new FGraph(); result.copyProperties(components.get(0)); // determine the maximal row width by the maximal box width and the total area double maxRowWidth = 0.0f; double totalArea = 0.0f; for (FGraph graph : components) { KVector size = graph .getProperty(Properties.BB_LOWRIGHT) .clone() .sub(graph.getProperty(Properties.BB_UPLEFT)); maxRowWidth = Math.max(maxRowWidth, size.x); totalArea += size.x * size.y; } maxRowWidth = Math.max( maxRowWidth, (float) Math.sqrt(totalArea) * result.getProperty(Properties.ASPECT_RATIO)); double spacing = result.getProperty(Properties.SPACING).doubleValue(); // place nodes iteratively into rows double xpos = 0, ypos = 0, highestBox = 0, broadestRow = spacing; for (FGraph graph : components) { KVector size = graph .getProperty(Properties.BB_LOWRIGHT) .clone() .sub(graph.getProperty(Properties.BB_UPLEFT)); if (xpos + size.x > maxRowWidth) { // place the graph into the next row xpos = 0; ypos += highestBox + spacing; highestBox = 0; } moveGraph(result, graph, xpos, ypos); broadestRow = Math.max(broadestRow, xpos + size.x); highestBox = Math.max(highestBox, size.y); xpos += size.x + spacing; } return result; }