/** * Adds all ports of given {@link LoopSide} of this component-holder to the current position of * the {@link ListIterator}. The ports are placed, so that source and target ports of an edge * are placed next to each other. The target port of an edge will be places before the source * port of an edge. * * @param loopSide The components of this {@link LoopSide} will be added. * @param itr The {@link ListIterator} to add the ports into. */ public void addInlineTargetsFirst(final LoopSide loopSide, final ListIterator<LPort> itr) { final List<LPort> firstPart = Lists.newArrayList(); final List<LPort> secondPart = Lists.newArrayList(); final Iterator<ConnectedSelfLoopComponent> compItr = LISTS_OF_COMPONENTS.get(loopSide).iterator(); while (compItr.hasNext()) { ConnectedSelfLoopComponent component = compItr.next(); firstPart.addAll(0, component.getSourceLoopPorts()); firstPart.addAll(0, component.getTargetLoopPortsReversed()); if (compItr.hasNext()) { component = compItr.next(); secondPart.addAll(component.getTargetLoopPortsReversed()); secondPart.addAll(component.getSourceLoopPorts()); } } setSideOfPorts(firstPart, loopSide.getTargetSide()); setSideOfPorts(secondPart, loopSide.getSourceSide()); for (final LPort port : firstPart) { itr.add(port); } for (final LPort port : secondPart) { itr.add(port); } }
/** * Adds all target ports and than all source ports of the components of given {@link LoopSide} * of this component-holder to the current position of the {@link ListIterator}. The source * ports will be added in reversed order. * * @param loopSide The components of this {@link LoopSide} will be added. * @param itr The {@link ListIterator} to add the ports into. */ public void addAllPorts( final LoopSide loopSide, final ListIterator<LPort> itr, final boolean sourceFirst) { final List<LPort> secondPart = Lists.newArrayList(); PortSide secondPartSide = null; if (sourceFirst) { for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) { for (final LPort port : component.getSourceLoopPorts()) { itr.add(port); setSideOfPort(port, loopSide.getSourceSide()); } secondPart.addAll(component.getTargetLoopPorts()); secondPartSide = loopSide.getTargetSide(); } } else { for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) { for (final LPort port : component.getTargetLoopPorts()) { itr.add(port); setSideOfPort(port, loopSide.getTargetSide()); } secondPart.addAll(component.getSourceLoopPorts()); secondPartSide = loopSide.getSourceSide(); } } Collections.reverse(secondPart); setSideOfPorts(secondPart, secondPartSide); for (final LPort port : secondPart) { itr.add(port); } }
/** * Sets the loopSide of all self-loops of the component individually, depending on the side(s) * already defined for at least one port in the component. At least one portSide must be defined, * otherwise this method will run infinitely. * * @param component The connected component to process. * @return The LoopSide that is equal to the assigned portSides. Undefined, if there is not a * common side for all loops. */ private LoopSide setPortSideByConstraint(final ConnectedSelfLoopComponent component) { // Iterator over the edges of the component. // The iterator is cycling, as there is no guarantee, that (at least) one port of the current // edge has a portSide defined. If both portSides of the current edge are UNDEFINED, we will // return to it later. final Iterator<LEdge> iter = Iterators.cycle(Lists.newArrayList(component.getEdges())); // LoopSides in this component will be collected here final EnumSet<LoopSide> loopSidesInComponent = EnumSet.noneOf(LoopSide.class); while (iter.hasNext()) { final LEdge edge = iter.next(); final PortSide sourceSide = edge.getSource().getSide(); final PortSide targetSide = edge.getTarget().getSide(); if (sourceSide == PortSide.UNDEFINED) { // source side is undefined if (targetSide != PortSide.UNDEFINED) { // only targetSide is defined. Edge becomes a sideLoop on the side of target. final LoopSide side = LoopSide.fromPortSides(targetSide); edge.setProperty(InternalProperties.SPLINE_LOOPSIDE, side); edge.getSource().setSide(targetSide); loopSidesInComponent.add(side); iter.remove(); } } else { // source side is defined if (targetSide == PortSide.UNDEFINED) { // only sourceSide is defined. Edge becomes a sideLoop on the side of source. final LoopSide side = LoopSide.fromPortSides(sourceSide); edge.setProperty(InternalProperties.SPLINE_LOOPSIDE, side); edge.getTarget().setSide(sourceSide); loopSidesInComponent.add(side); iter.remove(); } else { // both sides are defined, set edge side resulting from the combination final LoopSide side = LoopSide.fromPortSide(sourceSide, targetSide); edge.setProperty(InternalProperties.SPLINE_LOOPSIDE, side); loopSidesInComponent.add(side); iter.remove(); } } } // Now all edges have a LoopSide assigned. // Check if we can find a LoopSide for the whole component. LoopSide side; if (loopSidesInComponent.size() == 1) { side = loopSidesInComponent.iterator().next(); } else { side = LoopSide.UNDEFINED; } component.setLoopSide(side, false); return side; }
/** * Finds the "center" of the setup of currently available sides. * * @return The side considered as "center" for current setup of available port-sides. */ private LoopSide findCenter() { final EnumSet<LoopSide> retVal; Iterator<LoopSide> itr; switch (loopSides.availableStraightSides().size()) { case 4: // all sides available: south is the center. return LoopSide.S; case 3: // only one side not available: the side not available is the center. return loopSides.allRemovedStraightSides().iterator().next(); case 2: // Two sides were removed, two sides remain available. The center side depends on // the pattern of the removed/available sides: retVal = loopSides.availableStraightSides(); itr = retVal.iterator(); LoopSide first = itr.next(); LoopSide second = itr.next(); if (first.opposite() == second) { // the removed sides are opposed to each other. if (retVal.contains(LoopSide.S)) { // n-s sides were removed. Center is east. return LoopSide.E; } else { // w-e sides were removed. center is south. return LoopSide.S; } } else { // the removed sides were neighbors. The center is between them. if (first.left().left() == second) { // first element is right of second element. return first.left(); } else { // first element is left of second element return first.right(); } } case 1: // only one straight side is remaining. The opposite of this side is the center. retVal = loopSides.availableStraightSides(); return retVal.iterator().next().opposite(); case 0: // all straight sides were removed. The center is the s-e corner. return LoopSide.SE; default: return null; } }
/** * 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)); }
/** Updates the performance lists. */ private void updateAvailableSides() { availableStraightSides.clear(); availableSides.clear(); availableNotAcrossSides.clear(); for (final LoopSide side : sizeMap.keySet()) { if (!side.isAcross()) { availableNotAcrossSides.add(side); if (side.isStraight()) { availableStraightSides.add(side); } } availableSides.add(side); } sideRemovedOrChanged = false; }
/** * Adds all target ports of the components of given {@link LoopSide} of this component-holder to * the current position of the {@link ListIterator}. * * @param loopSide The components of this {@link LoopSide} will be added. * @param itr The {@link ListIterator} to add the ports into. */ public void addTargetPorts(final LoopSide loopSide, final ListIterator<LPort> itr) { final PortSide portSide = loopSide.getTargetSide(); for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) { for (final LPort port : component.getTargetLoopPorts()) { itr.add(port); setSideOfPort(port, portSide); } } }
/** * Adds all source ports of the components of given {@link LoopSide} of this component-holder to * the current position of the {@link ListIterator} in reversed order. * * @param loopSide The components of this {@link LoopSide} will be added. * @param itr The {@link ListIterator} to add the ports into. */ public void addSourcePortsReversed(final LoopSide loopSide, final ListIterator<LPort> itr) { final List<LPort> sourcePorts = Lists.newArrayList(); for (final ConnectedSelfLoopComponent component : LISTS_OF_COMPONENTS.get(loopSide)) { sourcePorts.addAll(component.getSourceLoopPorts()); } Collections.reverse(sourcePorts); setSideOfPorts(sourcePorts, loopSide.getSourceSide()); for (final LPort port : sourcePorts) { itr.add(port); } }
/** * Distributes the self-loops equally around the node. * * @param components */ public void calculate(final List<ConnectedSelfLoopComponent> components) { final List<ConnectedSelfLoopComponent> withLongText = Lists.newArrayList(); final List<ConnectedSelfLoopComponent> withShortText = Lists.newArrayList(); final List<ConnectedSelfLoopComponent> withoutText = Lists.newArrayList(); // first we are going to check for the size of possible edge labels for (final ConnectedSelfLoopComponent component : components) { if (component.getTextWidth() > MAX_TEXT_LENGTH) { withLongText.add(component); } else if (component.getTextWidth() > 0.0) { withShortText.add(component); } else { withoutText.add(component); } } // if there is only one loop with text, handle this loop as a long text loop if (withShortText.size() == 1 && withLongText.isEmpty()) { withLongText.addAll(withShortText); withShortText.clear(); } // try to put the components with long text to one of the horizontal across loop sides. // if there are no such sides available, handle long text loops as short text loops. if (!withLongText.isEmpty() && loopSides.availableSides().contains(LoopSide.N) && loopSides.availableSides().contains(LoopSide.S)) { assignAcrossSide(withLongText); } else { withShortText.addAll(withLongText); } // put the components with short text to one of the corner loop sides if (!withShortText.isEmpty()) { assignCornerSide(withShortText); } // Last but not least: loops without text get spread equally. if (!withoutText.isEmpty()) { // //////////////////////////////////////////////////////////// // First of all we try to put those ConnectedSelfLoops who have more than one edge, // to one of the available straight sides. // //////////////////////////////////////////////////////////// final Set<LoopSide> availableStraights = loopSides.availableStraightSides(); if (!availableStraights.isEmpty()) { final Iterator<ConnectedSelfLoopComponent> itrComponent = withoutText.iterator(); final Iterator<LoopSide> itrAvailable = Iterables.cycle(availableStraights).iterator(); while (itrComponent.hasNext()) { // look for a component with more than one edge ConnectedSelfLoopComponent component = itrComponent.next(); while (itrComponent.hasNext() && component.getEdges().size() < 2) { component = itrComponent.next(); } // If we have found a component with more than one edge, assign the next // straight side to the component. if (component.getEdges().size() > 1) { final LoopSide side = itrAvailable.next(); component.setLoopSide(side, true); itrComponent.remove(); loopSides.removeSide(side); } } } // //////////////////////////////////////////////////////////// // Now we can distribute the remaining components around the node. // //////////////////////////////////////////////////////////// // number of required sides final int number = withoutText.size(); // Center of the assigned pattern. final LoopSide center = findCenter(); // The list of portSides we assign to the set of connected components. final List<LoopSide> portSides = Lists.newArrayList(); // How many times must a "full set" of LoopSide-elements be constructed? final int fullSets = number / loopSides.availableNotAcrossSides().size(); // Construct the full sets. for (int i = 0; i < fullSets; i++) { portSides.addAll(loopSides.availableNotAcrossSides()); } // How many elements are remaining to be constructed after the full sets have been // constructed? int remainer = number % loopSides.availableNotAcrossSides().size(); // If more than three elements are remaining, the pattern includes all four corners // and the sides that would be included in the pattern for (n-4) elements. if (remainer > 3) { portSides.addAll(LoopSide.getAllCornerSides()); remainer -= 4; } // Create the pattern for 1-3 elements. switch (remainer) { case 3: // opposed side (from center) and the the same sides as for two elements portSides.add(center.opposite()); case 2: // the first AVAILABLE neighbors of the neighbors of the opposed side of the // center LoopSide tmpSide = center.opposite().left(); do { tmpSide = tmpSide.left(); } while (!loopSides.availableSides().contains(tmpSide)); portSides.add(tmpSide); tmpSide = center.opposite().right(); do { tmpSide = tmpSide.right(); } while (!loopSides.availableSides().contains(tmpSide)); portSides.add(tmpSide); break; case 1: // opposed side (from center) portSides.add(center.opposite()); break; default: break; } // Now assign the remaining sides to the set of portSides we just have constructed. final Iterator<LoopSide> itrSides = portSides.iterator(); final Iterator<ConnectedSelfLoopComponent> itrComponent = withoutText.iterator(); while (itrSides.hasNext() && itrComponent.hasNext()) { itrComponent.next().setLoopSide(itrSides.next(), true); } } }
/** * 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())); } }
static { for (LoopSide side : LoopSide.values()) { LISTS_OF_COMPONENTS.put(side, null); } }
/** @return A {@link List} of all removed side loopSides. */ private Set<LoopSide> allRemovedStraightSides() { final Set<LoopSide> retVal = LoopSide.getAllStraightSides(); retVal.removeAll(availableStraightSides()); return retVal; }
public SortedLoopSides() { for (final LoopSide side : LoopSide.getAllDefinedSides()) { sizeMap.put(side, new SizeOfSide(0.0, 0.0)); } }