private void initialize(final LGraph graph) { layeredGraph = graph; int layerCount = graph.getLayers().size(); bestNodeOrder = new LNode[layerCount][]; currentNodeOrder = new LNode[layerCount][]; originalNodeOrder = new LNode[layerCount][]; ListIterator<Layer> layerIter = graph.getLayers().listIterator(); while (layerIter.hasNext()) { Layer layer = layerIter.next(); int layerNodeCount = layer.getNodes().size(); assert layerNodeCount > 0; int layerIndex = layerIter.previousIndex(); bestNodeOrder[layerIndex] = new LNode[layerNodeCount]; currentNodeOrder[layerIndex] = new LNode[layerNodeCount]; originalNodeOrder[layerIndex] = new LNode[layerNodeCount]; ListIterator<LNode> nodeIter = layer.getNodes().listIterator(); int id = 0; while (nodeIter.hasNext()) { LNode node = nodeIter.next(); node.id = id++; currentNodeOrder[layerIndex][nodeIter.previousIndex()] = node; bestNodeOrder[layerIndex][nodeIter.previousIndex()] = node; originalNodeOrder[layerIndex][nodeIter.previousIndex()] = node; } } crossingCounter = new AllCrossingsCounter(currentNodeOrder); if (greedySwitchType.useHyperedgeCounter()) { crossingCounter.useHyperedgeCounter(); } }
private void setAsGraph(final LNode[][] nodeOrder) { ListIterator<Layer> layerIter = layeredGraph.getLayers().listIterator(); while (layerIter.hasNext()) { Layer layer = layerIter.next(); LNode[] nodes = nodeOrder[layerIter.previousIndex()]; ListIterator<LNode> nodeIter = layer.getNodes().listIterator(); while (nodeIter.hasNext()) { nodeIter.next(); nodeIter.set(nodes[nodeIter.previousIndex()]); } } }
/** * Merge regions by testing whether they would overlap after applying the deflection. * * @param layeredGraph the layered graph * @return true if any two regions have been merged */ private boolean mergeRegions(final LGraph layeredGraph) { boolean changed = false; double threshold = OVERLAP_DETECT * spacings.nodeSpacing * spacings.inLayerSpacingFactor; for (Layer layer : layeredGraph) { Iterator<LNode> nodeIter = layer.getNodes().iterator(); // Get the first node LNode node1 = nodeIter.next(); LinearSegment region1 = linearSegments[node1.id].region(); // While there are still nodes following the current node while (nodeIter.hasNext()) { // Test whether nodes have different regions LNode node2 = nodeIter.next(); LinearSegment region2 = linearSegments[node2.id].region(); if (region1 != region2) { // Calculate how much space is allowed between the nodes double spacing = spacings.getVerticalSpacing(node1, node2); double node1Extent = node1.getPosition().y + node1.getSize().y + node1.getMargin().bottom + region1.deflection + spacing; double node2Extent = node2.getPosition().y - node2.getMargin().top + region2.deflection; // Test if the nodes are overlapping if (node1Extent > node2Extent + threshold) { // Merge the first region under the second top level segment int weightSum = region1.weight + region2.weight; assert weightSum > 0; region2.deflection = (region2.weight * region2.deflection + region1.weight * region1.deflection) / weightSum; region2.weight = weightSum; region1.refSegment = region2; changed = true; } } node1 = node2; region1 = region2; } } return changed; }
/** * Creates an unbalanced placement for the sorted linear segments. * * @param layeredGraph the layered graph to create an unbalanced placement for. */ private void createUnbalancedPlacement(final LGraph layeredGraph) { // How many nodes are currently placed in each layer int[] nodeCount = new int[layeredGraph.getLayers().size()]; // The type of the node most recently placed in a given layer NodeType[] recentNodeType = new NodeType[layeredGraph.getLayers().size()]; // Iterate through the linear segments (in proper order!) and place them for (LinearSegment segment : linearSegments) { // Determine the uppermost placement for the linear segment double uppermostPlace = 0.0f; for (LNode node : segment.nodes) { NodeType nodeType = node.getType(); int layerIndex = node.getLayer().getIndex(); nodeCount[layerIndex]++; // Calculate how much space to leave between the linear segment and the last // node of the given layer float spacing = spacings.edgeEdgeSpacing * spacings.inLayerSpacingFactor; if (nodeCount[layerIndex] > 0) { if (recentNodeType[layerIndex] != null) { spacing = spacings.getVerticalSpacing(recentNodeType[layerIndex], nodeType); } } uppermostPlace = Math.max(uppermostPlace, node.getLayer().getSize().y + spacing); } // Apply the uppermost placement to all elements for (LNode node : segment.nodes) { // Set the node position node.getPosition().y = uppermostPlace + node.getMargin().top; // Adjust layer size Layer layer = node.getLayer(); layer.getSize().y = uppermostPlace + node.getMargin().top + node.getSize().y + node.getMargin().bottom; recentNodeType[layer.getIndex()] = node.getType(); } } }
/** * Fills the dependency graph with dependencies. If a dependency would introduce a cycle, the * offending linear segment is split into two linear segments. * * @param layeredGraph the layered graph. * @param segmentList the list of segments. Updated to include the newly created linear segments. * @param outgoingList the lists of outgoing dependencies for each segment. This essentially * encodes the edges of the dependency graph. * @param incomingCountList the number of incoming dependencies for each segment. */ private void createDependencyGraphEdges( final LGraph layeredGraph, final List<LinearSegment> segmentList, final List<List<LinearSegment>> outgoingList, final List<Integer> incomingCountList) { /* * There's some <scaryVoice> faaaancy </scaryVoice> stuff going on here. Basically, we go * through all the layers, from left to right. In each layer, we go through all the nodes. * For each node, we retrieve the linear segment it's part of and add a dependency to the * next node's linear segment. So far so good. * * This works perfectly fine as long as we assume that the relative order of linear segments * doesn't change from one layer to the next. However, since the introduction of north / * south port dummies, it can. We now have to avoid creating cycles in the dependency graph. * This is done by remembering the indices of each linear segment in the previous layer. * When we encounter a segment x, we check if there is a segment y that came before x in the * previous layer. (that would introduce a cycle) If that's the case, we split x at the * current layer, resulting in two segments, x1 and x2, x2 starting at the current layer. * Now, we proceed as usual, adding a dependency from x2 to y. But we have avoided a cycle * because y does not depend on x2, but on x1. */ int nextLinearSegmentID = segmentList.size(); int layerIndex = 0; for (Layer layer : layeredGraph) { List<LNode> nodes = layer.getNodes(); if (nodes.isEmpty()) { // Ignore empty layers continue; } Iterator<LNode> nodeIter = nodes.iterator(); int indexInLayer = 0; // We carry the previous node with us for dependency management LNode previousNode = null; // Get the layer's first node LNode currentNode = nodeIter.next(); LinearSegment currentSegment = null; while (currentNode != null) { // Get the current node's segment currentSegment = segmentList.get(currentNode.id); /* * Check if we have a cycle. That's the case if the following holds: - The current * segment appeared in the previous layer as well. - In the previous layer, we find * a segment after the current segment that appears before the current segment in * the current layer. */ if (currentSegment.indexInLastLayer >= 0) { LinearSegment cycleSegment = null; Iterator<LNode> cycleNodesIter = layer.getNodes().listIterator(indexInLayer + 1); while (cycleNodesIter.hasNext()) { LNode cycleNode = cycleNodesIter.next(); cycleSegment = segmentList.get(cycleNode.id); if (cycleSegment.lastLayer == currentSegment.lastLayer && cycleSegment.indexInLastLayer < currentSegment.indexInLastLayer) { break; } else { cycleSegment = null; } } // If we have found a cycle segment, we need to split the current linear segment if (cycleSegment != null) { // Update the current segment before it's split if (previousNode != null) { incomingCountList.set(currentNode.id, incomingCountList.get(currentNode.id) - 1); outgoingList.get(previousNode.id).remove(currentSegment); } currentSegment = currentSegment.split(currentNode, nextLinearSegmentID++); segmentList.add(currentSegment); outgoingList.add(new ArrayList<LinearSegment>()); if (previousNode != null) { outgoingList.get(previousNode.id).add(currentSegment); incomingCountList.add(1); } else { incomingCountList.add(0); } } } // Now add a dependency to the next node, if any LNode nextNode = null; if (nodeIter.hasNext()) { nextNode = nodeIter.next(); LinearSegment nextSegment = segmentList.get(nextNode.id); outgoingList.get(currentNode.id).add(nextSegment); incomingCountList.set(nextNode.id, incomingCountList.get(nextNode.id) + 1); } // Update segment's layer information currentSegment.lastLayer = layerIndex; currentSegment.indexInLastLayer = indexInLayer++; // Cycle nodes previousNode = currentNode; currentNode = nextNode; } layerIndex++; } // Write debug output graph if (layeredGraph.getProperty(LayoutOptions.DEBUG_MODE)) { DebugUtil.writeDebugGraph(layeredGraph, segmentList, outgoingList); } }