/** * Constructs a new {@code PortReAdder} holding the given {@link ConnectedSelfLoopComponent}s * given. * * @param components The {@link ConnectedSelfLoopComponent}s to be part of this PortReAdder. */ PortReadder(final List<ConnectedSelfLoopComponent> components) { // Initialize the mapping. As the mapping is static, this needs to be done on every new run. for (LoopSide side : LoopSide.values()) { LISTS_OF_COMPONENTS.put(side, new ArrayList<ConnectedSelfLoopComponent>()); } // First: group the components according to their loopSide for (final ConnectedSelfLoopComponent component : components) { allHiddenPorts.addAll(component.getHidablePorts()); if (component.getNonLoopPorts().isEmpty()) { LISTS_OF_COMPONENTS.get(component.getLoopSide()).add(component); } else { withNonSelfLoop.add(component); } } // Second: sort the collections of components // (in revered order, biggest first) for (List<ConnectedSelfLoopComponent> list : LISTS_OF_COMPONENTS.values()) { Collections.sort(list, loopSorter); } // ports on the NW-border need to be reversed, again. Collections.reverse(LISTS_OF_COMPONENTS.get(LoopSide.NW)); }
/** * (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); } } } } }
/** {@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(); }