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(); } }
/** * (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); } } } } }
/** * Splits this linear segment before the given node. The returned segment contains all nodes * from the given node onward, with their ID set to the new segment's ID. Those nodes are * removed from this segment. * * @param node the node to split the segment at. * @param newId the new segment's id. * @return new linear segment with ID {@code -1} and all nodes from {@code node} onward. */ LinearSegment split(final LNode node, final int newId) { int nodeIndex = nodes.indexOf(node); // Prepare the new segment LinearSegment newSegment = new LinearSegment(); newSegment.id = newId; // Move nodes to the new segment ListIterator<LNode> iterator = nodes.listIterator(nodeIndex); while (iterator.hasNext()) { LNode movedNode = iterator.next(); movedNode.id = newId; newSegment.nodes.add(movedNode); iterator.remove(); } return newSegment; }
/** * 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; }
/** * 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; } }
/** * Balance the initial placement by force-based movement of regions. First perform * <em>pendulum</em> iterations, where only one direction of edges is considered, then * <em>rubber</em> iterations, where both incoming and outgoing edges are considered. In each * iteration first determine the <em>deflection</em> of each linear segment, i.e. the optimal * position delta that leads to a balanced placement with respect to its adjacent segments. Then * merge regions that touch each other, building mean values of the involved deflections, and * finally apply the resulting deflection values to all segments. The iterations stop when no * further improvement is done. * * @param layeredGraph a layered graph */ private void balancePlacement(final LGraph layeredGraph) { double deflectionDampening = layeredGraph.getProperty(Properties.LINEAR_SEGMENTS_DEFLECTION_DAMPENING).doubleValue(); // Determine a suitable number of pendulum iterations int thoroughness = layeredGraph.getProperty(Properties.THOROUGHNESS); int pendulumIters = PENDULUM_ITERS; int finalIters = FINAL_ITERS; double threshold = THRESHOLD_FACTOR / thoroughness; // Iterate the balancing boolean ready = false; Mode mode = Mode.FORW_PENDULUM; double lastTotalDeflection = Integer.MAX_VALUE; do { // Calculate force for every linear segment boolean incoming = mode != Mode.BACKW_PENDULUM; boolean outgoing = mode != Mode.FORW_PENDULUM; double totalDeflection = 0; for (LinearSegment segment : linearSegments) { segment.refSegment = null; calcDeflection(segment, incoming, outgoing, deflectionDampening); totalDeflection += Math.abs(segment.deflection); } // Merge linear segments to form regions boolean merged; do { merged = mergeRegions(layeredGraph); } while (merged); // Move the nodes according to the deflection value of their region for (LinearSegment segment : linearSegments) { double deflection = segment.region().deflection; if (deflection != 0) { for (LNode node : segment.nodes) { node.getPosition().y += deflection; } } } // Update the balancing mode if (mode == Mode.FORW_PENDULUM || mode == Mode.BACKW_PENDULUM) { pendulumIters--; if (pendulumIters <= 0 && (totalDeflection < lastTotalDeflection || -pendulumIters > thoroughness)) { mode = Mode.RUBBER; lastTotalDeflection = Integer.MAX_VALUE; } else if (mode == Mode.FORW_PENDULUM) { mode = Mode.BACKW_PENDULUM; lastTotalDeflection = totalDeflection; } else { mode = Mode.FORW_PENDULUM; lastTotalDeflection = totalDeflection; } } else { ready = totalDeflection >= lastTotalDeflection || lastTotalDeflection - totalDeflection < threshold; lastTotalDeflection = totalDeflection; if (ready) { finalIters--; } } } while (!(ready && finalIters <= 0)); }
/** * 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(); } } }
/** * Put a node into the given linear segment and check for following parts of a long edge. * * @param node the node to put into the linear segment * @param segment a linear segment * @return {@code true} if the given node was not already part of another segment and was thus * added to the given segment. */ private boolean fillSegment(final LNode node, final LinearSegment segment) { NodeType nodeType = node.getType(); // handle initial big nodes as big node type if (node.getProperty(InternalProperties.BIG_NODE_INITIAL)) { nodeType = NodeType.BIG_NODE; } if (node.id >= 0) { // The node is already part of another linear segment return false; } else if (segment.nodeType != null && (nodeType == NodeType.BIG_NODE && nodeType != segment.nodeType)) { // Big nodes are not allowed to share a linear segment with other dummy nodes return false; } else { // Add the node to the given linear segment node.id = segment.id; segment.nodes.add(node); } segment.nodeType = nodeType; if (nodeType == NodeType.LONG_EDGE || nodeType == NodeType.NORTH_SOUTH_PORT || nodeType == NodeType.BIG_NODE) { // This is a LONG_EDGE, NORTH_SOUTH_PORT or BIG_NODE dummy; check if any of its // successors are of one of these types too. If so, we can form a linear segment // with one of them. (not with more than one, though) // Note 1: LONG_EDGES and NORTH_SOUTH_PORTs can share a common linear segment // Note 2: we must take care not to make a segment out of nodes that are in the same layer // Note 3: for BIG_NODEs also the first BIG_NODE_INITIAL which is no actual dummy node has // to be considered here for (LPort sourcePort : node.getPorts()) { for (LPort targetPort : sourcePort.getSuccessorPorts()) { LNode targetNode = targetPort.getNode(); NodeType targetNodeType = targetNode.getType(); if (node.getLayer() != targetNode.getLayer()) { if (nodeType == NodeType.BIG_NODE) { // current AND the next node are BIG_NODE dummies if (targetNodeType == NodeType.BIG_NODE) { if (fillSegment(targetNode, segment)) { // We just added another node to this node's linear segment. // That's quite enough. return true; } } } else { // current no bignode and next node is LONG_EDGE and NORTH_SOUTH_PORT if (targetNodeType == NodeType.LONG_EDGE || targetNodeType == NodeType.NORTH_SOUTH_PORT) { if (fillSegment(targetNode, segment)) { // We just added another node to this node's linear segment. // That's quite enough. return true; } } } } } } } return true; }
/** * 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; }
/** * @param n1 a node * @param n2 another node * @return the spacing to be preserved between {@code n1} and {@code n2} */ public float getHorizontalSpacing(final LNode n1, final LNode n2) { return getHorizontalSpacing(n1.getType(), n2.getType()); }
/** * Removes all sides from the list of available sides, that already have an port places on it. * Use to make sure no loops will be places on sides with ports containing non-loop edges. */ public void removeOccupiedSides() { for (final LPort port : node.getPorts()) { loopSides.removeSide(LoopSide.fromPortSides(port.getSide())); } }
/** {@inheritDoc} */ public void process(final LGraph layeredGraph, final IElkProgressMonitor monitor) { monitor.begin("Spline SelfLoop positioning", 1); /** Stores which loop placement strategy to choose. */ final SelfLoopPlacement loopPlacement = layeredGraph.getProperty(Properties.SPLINE_SELF_LOOP_PLACEMENT); //////////////////////////////////////////////////////// // There are two main jobs to be done: // 1) Find a loop-side for each component. // 2) Position ports in the correct order around the node. //////////////////////////////////////////////////////// // Process all nodes on all layers. for (final Layer layer : layeredGraph) { for (final LNode node : layer) { // Read self-loops components. final List<ConnectedSelfLoopComponent> components = node.getProperty(InternalProperties.SPLINE_SELFLOOP_COMPONENTS); // Components to be distributed by the placement strategy. final List<ConnectedSelfLoopComponent> componentsToBePlaced = Lists.newArrayList(); for (final ConnectedSelfLoopComponent component : components) { // Re-Add all hidden edges to their ports. component.unhideEdges(); if (component.getConstrainedPorts().isEmpty()) { // If there is no constraint on any port, we have to process this component later componentsToBePlaced.add(component); } else { // If there is at least one port with a constraint to it's port-side, // we will set the port- and loop-sides by this constraint. (Job 1) setPortSideByConstraint(component); if (!component.getNonLoopPorts().isEmpty()) { // Position and re-add all ports to the node, that are part of a component // with at least one non-loop edge. (Job 2) addComponentWithNonLoopEdges(component); } } } // Now we have to find a loop-side (job 1) for the remaining components. They are all // stored in componentsToBePlaced. All these components don't have a port with a // constraint on it's portSide, so we can arrange them accordingly to the cosen strategy. switch (loopPlacement) { case EQUALLY_DISTRIBUTED: setPortSideSpreadEqually(componentsToBePlaced, node); break; case NORTH_SEQUENCE: for (final ConnectedSelfLoopComponent component : componentsToBePlaced) { component.setLoopSide(LoopSide.N, true); } break; case NORTH_STACKED: for (final ConnectedSelfLoopComponent component : componentsToBePlaced) { component.setLoopSide(LoopSide.N, true); } break; default: // Unknown strategy chosen. assert false; break; } // Position and re-add all ports to the node. // This is job 2) switch (loopPlacement) { case EQUALLY_DISTRIBUTED: case NORTH_STACKED: portStackedPositioning(components); break; case NORTH_SEQUENCE: portLinedPositioning(components); break; default: break; } } } monitor.done(); }
/** * Positions the ports of the given components around their node in a crossing minimizing order. * The components on straight sides are places stacked. All components in the given list must lay * on the same node. * * @param components The components those ports needs to be placed. */ private void portStackedPositioning(final List<ConnectedSelfLoopComponent> components) { if (components.isEmpty()) { return; } final LNode node = components.get(0).getNode(); // The componentHolder, holding our components. final PortReadder compHolder = new PortReadder(components); // An iterator over the portList of our node final ListIterator<LPort> itr = node.getPorts().listIterator(); ///////////////////////////////////// // Now we will re-add the ports to the portList of the node, again. // north-west, 1st part compHolder.addSourcePortsReversed(LoopSide.NW, itr); // south-north via west, 2nd part compHolder.addTargetPorts(LoopSide.SWN, itr); ///////////////////////////////////// // find transition from north to east findNextSide(PortSide.NORTH, itr); // north compHolder.addAllPorts(LoopSide.N, itr, false); // south-north via east, 2nd part compHolder.addTargetPorts(LoopSide.SEN, itr); // east-north compHolder.addAllPorts(LoopSide.EN, itr, false); // east-west via north, 1st part compHolder.addSourcePortsReversed(LoopSide.ENW, itr); ///////////////////////////////////// // find transition from east to south findNextSide(PortSide.EAST, itr); // east compHolder.addAllPorts(LoopSide.E, itr, false); // west-east via south, 1st part compHolder.addSourcePortsReversed(LoopSide.ESW, itr); // south-east compHolder.addAllPorts(LoopSide.SE, itr, false); // south-north via east, 1st part compHolder.addSourcePortsReversed(LoopSide.SEN, itr); ///////////////////////////////////// // find transition from south to west findNextSide(PortSide.SOUTH, itr); // south compHolder.addAllPorts(LoopSide.S, itr, false); // south-north via west, 1st part compHolder.addSourcePortsReversed(LoopSide.SWN, itr); // west-south compHolder.addAllPorts(LoopSide.WS, itr, false); // east-west via south, 2nd part compHolder.addTargetPorts(LoopSide.ESW, itr); ///////////////////////////////////// // find transition from west to north (the very end of the list) while (itr.hasNext()) { itr.next(); } // west compHolder.addAllPorts(LoopSide.W, itr, false); // east-west via north, 2nd part compHolder.addTargetPorts(LoopSide.ENW, itr); // north-west, 2nd part compHolder.addTargetPorts(LoopSide.NW, itr); }