/** * 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(); } } }
/** * Post-process the balanced placement by moving linear segments where obvious improvements can be * made. * * @param layeredGraph the layered graph */ private void postProcess(final LGraph layeredGraph) { // process each linear segment independently for (LinearSegment segment : linearSegments) { double minRoomAbove = Integer.MAX_VALUE, minRoomBelow = Integer.MAX_VALUE; for (LNode node : segment.nodes) { double roomAbove, roomBelow; int index = node.getIndex(); // determine the amount by which the linear segment can be moved up without overlap if (index > 0) { LNode neighbor = node.getLayer().getNodes().get(index - 1); float spacing = spacings.getVerticalSpacing(node, neighbor); roomAbove = node.getPosition().y - node.getMargin().top - (neighbor.getPosition().y + neighbor.getSize().y + neighbor.getMargin().bottom + spacing); } else { roomAbove = node.getPosition().y - node.getMargin().top; } minRoomAbove = Math.min(roomAbove, minRoomAbove); // determine the amount by which the linear segment can be moved down without // overlap if (index < node.getLayer().getNodes().size() - 1) { LNode neighbor = node.getLayer().getNodes().get(index + 1); float spacing = spacings.getVerticalSpacing(node, neighbor); roomBelow = neighbor.getPosition().y - neighbor.getMargin().top - (node.getPosition().y + node.getSize().y + node.getMargin().bottom + spacing); } else { roomBelow = 2 * node.getPosition().y; } minRoomBelow = Math.min(roomBelow, minRoomBelow); } double minDisplacement = Integer.MAX_VALUE; boolean foundPlace = false; // determine the minimal displacement that would make one incoming edge straight LNode firstNode = segment.nodes.get(0); for (LPort target : firstNode.getPorts()) { double pos = firstNode.getPosition().y + target.getPosition().y + target.getAnchor().y; for (LEdge edge : target.getIncomingEdges()) { LPort source = edge.getSource(); double d = source.getNode().getPosition().y + source.getPosition().y + source.getAnchor().y - pos; if (Math.abs(d) < Math.abs(minDisplacement) && Math.abs(d) < (d < 0 ? minRoomAbove : minRoomBelow)) { minDisplacement = d; foundPlace = true; } } } // determine the minimal displacement that would make one outgoing edge straight LNode lastNode = segment.nodes.get(segment.nodes.size() - 1); for (LPort source : lastNode.getPorts()) { double pos = lastNode.getPosition().y + source.getPosition().y + source.getAnchor().y; for (LEdge edge : source.getOutgoingEdges()) { LPort target = edge.getTarget(); double d = target.getNode().getPosition().y + target.getPosition().y + target.getAnchor().y - pos; if (Math.abs(d) < Math.abs(minDisplacement) && Math.abs(d) < (d < 0 ? minRoomAbove : minRoomBelow)) { minDisplacement = d; foundPlace = true; } } } // if such a displacement could be found, apply it to the whole linear segment if (foundPlace && minDisplacement != 0) { for (LNode node : segment.nodes) { node.getPosition().y += minDisplacement; } } } }