/** * 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; } }
/** * 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); } } }