/** * (Re-)Adds the ports to the node of a component, having at least one port with a non-loop edge. * The ports are added as the direct neighbor of a port they are connected to via an edge. * * @param component The component whose ports have to get re-added. */ public void addComponentWithNonLoopEdges(final ConnectedSelfLoopComponent component) { // the node we are working on final LNode node = component.getNode(); // Set of all ports of the components that are hidden. Initially all ports that CAN be hidden. final Set<LPort> hiddenPorts = Sets.newHashSet(component.getHidablePorts()); // Iterator holding all ports that already have a portSide specified. Initially these are // all ports with an non-loop edge connected to them. While processing the elements, we will // add new ports to this Iterator. So we need a ListIterator. final ListIterator<LPort> portsWithSideIter = Lists.newLinkedList(component.getNonLoopPorts()).listIterator(); while (portsWithSideIter.hasNext()) { final LPort portWithSide = portsWithSideIter.next(); if (portWithSide.getOutgoingEdges().isEmpty()) { // inbound port for (final LEdge edgeWithHiddenPort : portWithSide.getIncomingEdges()) { final LPort hiddenPort = edgeWithHiddenPort.getSource(); if (hiddenPorts.contains(hiddenPort)) { final ListIterator<LPort> nodePortIter = node.getPorts().listIterator(); LPort portOnNode = nodePortIter.next(); // find the port with side ... while (!portOnNode.equals(portWithSide)) { portOnNode = nodePortIter.next(); } // ... and add the hidden port on it's right side nodePortIter.add(hiddenPort); portsWithSideIter.add(hiddenPort); setSideOfPort(hiddenPort, portWithSide.getSide()); // ensure the next element will be our new added element portsWithSideIter.previous(); portsWithSideIter.previous(); hiddenPorts.remove(hiddenPort); } } } else { // outbound port for (final LEdge edgeWithHiddenPort : portWithSide.getOutgoingEdges()) { final LPort hiddenPort = edgeWithHiddenPort.getTarget(); if (hiddenPorts.contains(hiddenPort)) { final ListIterator<LPort> nodePortIter = node.getPorts().listIterator(); LPort portOnNode = nodePortIter.next(); // find the port with side ... while (!portOnNode.equals(portWithSide)) { portOnNode = nodePortIter.next(); } // ... and add the hidden port on it's left side nodePortIter.previous(); nodePortIter.add(hiddenPort); portsWithSideIter.add(hiddenPort); setSideOfPort(hiddenPort, portWithSide.getSide()); // ensure the next element will be our new added element portsWithSideIter.previous(); portsWithSideIter.previous(); hiddenPorts.remove(hiddenPort); } } } } }
/** * 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; } } } }
/** * Calculate the force acting on the given linear segment. The force is stored in the segment's * deflection field. * * @param segment the linear segment whose force is to be calculated * @param incoming whether incoming edges should be considered * @param outgoing whether outgoing edges should be considered * @param deflectionDampening factor by which deflections are dampened */ private void calcDeflection( final LinearSegment segment, final boolean incoming, final boolean outgoing, final double deflectionDampening) { double segmentDeflection = 0; int nodeWeightSum = 0; for (LNode node : segment.nodes) { double nodeDeflection = 0; int edgeWeightSum = 0; int inputPrio = incoming ? node.getProperty(INPUT_PRIO) : Integer.MIN_VALUE; int outputPrio = outgoing ? node.getProperty(OUTPUT_PRIO) : Integer.MIN_VALUE; int minPrio = Math.max(inputPrio, outputPrio); // Calculate force for every port/edge for (LPort port : node.getPorts()) { double portpos = node.getPosition().y + port.getPosition().y + port.getAnchor().y; if (outgoing) { for (LEdge edge : port.getOutgoingEdges()) { LPort otherPort = edge.getTarget(); LNode otherNode = otherPort.getNode(); if (segment != linearSegments[otherNode.id]) { int otherPrio = Math.max(otherNode.getProperty(INPUT_PRIO), otherNode.getProperty(OUTPUT_PRIO)); int prio = edge.getProperty(InternalProperties.PRIORITY); if (prio >= minPrio && prio >= otherPrio) { nodeDeflection += otherNode.getPosition().y + otherPort.getPosition().y + otherPort.getAnchor().y - portpos; edgeWeightSum++; } } } } if (incoming) { for (LEdge edge : port.getIncomingEdges()) { LPort otherPort = edge.getSource(); LNode otherNode = otherPort.getNode(); if (segment != linearSegments[otherNode.id]) { int otherPrio = Math.max(otherNode.getProperty(INPUT_PRIO), otherNode.getProperty(OUTPUT_PRIO)); int prio = edge.getProperty(InternalProperties.PRIORITY); if (prio >= minPrio && prio >= otherPrio) { nodeDeflection += otherNode.getPosition().y + otherPort.getPosition().y + otherPort.getAnchor().y - portpos; edgeWeightSum++; } } } } } // Avoid division by zero if (edgeWeightSum > 0) { segmentDeflection += nodeDeflection / edgeWeightSum; nodeWeightSum++; } } if (nodeWeightSum > 0) { segment.deflection = deflectionDampening * segmentDeflection / nodeWeightSum; segment.weight = nodeWeightSum; } else { segment.deflection = 0; segment.weight = 0; } }
/** * Sorts the linear segments of the given layered graph by finding a topological ordering in the * corresponding segment ordering graph. * * @param layeredGraph layered graph to process * @return a sorted array of linear segments */ private LinearSegment[] sortLinearSegments(final LGraph layeredGraph) { // set the identifier and input / output priority for all nodes List<LinearSegment> segmentList = Lists.newArrayList(); for (Layer layer : layeredGraph) { for (LNode node : layer) { node.id = -1; int inprio = Integer.MIN_VALUE, outprio = Integer.MIN_VALUE; for (LPort port : node.getPorts()) { for (LEdge edge : port.getIncomingEdges()) { int prio = edge.getProperty(InternalProperties.PRIORITY); inprio = Math.max(inprio, prio); } for (LEdge edge : port.getOutgoingEdges()) { int prio = edge.getProperty(InternalProperties.PRIORITY); outprio = Math.max(outprio, prio); } } node.setProperty(INPUT_PRIO, inprio); node.setProperty(OUTPUT_PRIO, outprio); } } // create linear segments for the layered graph, ignoring odd port side dummies int nextLinearSegmentID = 0; for (Layer layer : layeredGraph) { for (LNode node : layer) { // Test for the node ID; calls to fillSegment(...) may have caused the node ID // to be != -1. if (node.id < 0) { LinearSegment segment = new LinearSegment(); segment.id = nextLinearSegmentID++; fillSegment(node, segment); segmentList.add(segment); } } } // create and initialize segment ordering graph List<List<LinearSegment>> outgoingList = Lists.newArrayListWithCapacity(segmentList.size()); List<Integer> incomingCountList = Lists.newArrayListWithCapacity(segmentList.size()); for (int i = 0; i < segmentList.size(); i++) { outgoingList.add(new ArrayList<LinearSegment>()); incomingCountList.add(0); } // create edges for the segment ordering graph createDependencyGraphEdges(layeredGraph, segmentList, outgoingList, incomingCountList); // turn lists into arrays LinearSegment[] segments = segmentList.toArray(new LinearSegment[segmentList.size()]); @SuppressWarnings("unchecked") List<LinearSegment>[] outgoing = outgoingList.toArray(new List[outgoingList.size()]); int[] incomingCount = new int[incomingCountList.size()]; for (int i = 0; i < incomingCount.length; i++) { incomingCount[i] = incomingCountList.get(i); } // gather the sources of the segment ordering graph int nextRank = 0; List<LinearSegment> noIncoming = Lists.newArrayList(); for (int i = 0; i < segments.length; i++) { if (incomingCount[i] == 0) { noIncoming.add(segments[i]); } } // find a topological ordering of the segment ordering graph int[] newRanks = new int[segments.length]; while (!noIncoming.isEmpty()) { LinearSegment segment = noIncoming.remove(0); newRanks[segment.id] = nextRank++; while (!outgoing[segment.id].isEmpty()) { LinearSegment target = outgoing[segment.id].remove(0); incomingCount[target.id]--; if (incomingCount[target.id] == 0) { noIncoming.add(target); } } } // apply the new ordering to the array of linear segments linearSegments = new LinearSegment[segments.length]; for (int i = 0; i < segments.length; i++) { assert outgoing[i].isEmpty(); LinearSegment ls = segments[i]; int rank = newRanks[i]; linearSegments[rank] = ls; ls.id = rank; for (LNode node : ls.nodes) { node.id = rank; } } return linearSegments; }