From e62a0b013db564fd6228002166ef9ca2fd681ab4 Mon Sep 17 00:00:00 2001 From: bjeffrie Date: Wed, 7 Dec 2016 13:49:35 -0500 Subject: [PATCH 1/2] Add JungSugiyama as a layout option --- .../vrl/workflow/incubating/JungSugiyama.java | 757 ++++++++++++++++++ .../incubating/LayoutGeneratorSmart.java | 3 + .../demo/OptionsWindowFXMLController.java | 4 +- 3 files changed, 763 insertions(+), 1 deletion(-) create mode 100644 VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java diff --git a/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java b/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java new file mode 100644 index 00000000..00c5241d --- /dev/null +++ b/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2012 Stefan Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +//package hudson.plugins.depgraph_view.model.layout; +// bjeffrie 2016-10-01 Add JungSugiyama as layout in VWorkflows package hierarchy. +// No other changes from https://github.com/jenkinsci/depgraph-view-plugin version. +package eu.mihosoft.vrl.workflow.incubating; + +import edu.uci.ics.jung.algorithms.layout.AbstractLayout; +import edu.uci.ics.jung.graph.Graph; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** +* Arranges the nodes with the Sugiyama Layout Algorithm.
+* * +* +* Link to the algorithm
+* +* Originally, source was posted to the Jung2 forum, for Jung 1.x. Not sure where the original +* code came from, but ti didn;t work for Jung2, but it was not that complicated, so I pounded it +* into shape for Jung2, complete with generics and such. Lays out either top-down to left-right. +* +* Seems to work. Paramterize with spacing and orientation. +* +* C. Schanck (chris at schanck dot net) +*/ + +public class JungSugiyama extends AbstractLayout +{ + + private static final Orientation DEFAULT_ORIENTATION = Orientation.TOP; + + private static final int DEFAULT_HORIZONTAL_SPACING = 200; + + private static final int DEFAULT_VERTICAL_SPACING = 100; + + public static enum Orientation + { + TOP, LEFT + }; + + + private boolean executed = false; + + /** represents the size of the grid in horizontal grid elements + * + */ + private int gridAreaSize = Integer.MIN_VALUE; + + private int horzSpacing; + + private int vertSpacing; + + private Map> vertToWrapper = new HashMap>(); + + private Orientation orientation; + + public JungSugiyama(Graph g) + { + this(g, DEFAULT_ORIENTATION, DEFAULT_HORIZONTAL_SPACING, DEFAULT_VERTICAL_SPACING); + } + + public JungSugiyama(Graph g, Orientation orientation, int horzSpacing, int vertSpacing) + { + super(copyGraph(g)); + this.orientation = orientation; + this.horzSpacing = horzSpacing; + this.vertSpacing = vertSpacing; + } + + private static Graph copyGraph(Graph src) { + try { + @SuppressWarnings("unchecked") + Graph dest = (Graph) src.getClass().newInstance(); + for (V v : src.getVertices()) + dest.addVertex(v); + + for (E e : src.getEdges()) + dest.addEdge(e, src.getIncidentVertices(e)); + return dest; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void initialize() + { + if (!executed) + { + List>> graphLevels = runSugiyama(); + for (List> level : graphLevels) + { + for (CellWrapper wrapper : level) + { + V vertex = wrapper.getVertexView(); + + if (orientation.equals(Orientation.TOP)) + { + double xCoordinate = 10.0 + (wrapper.gridPosition * horzSpacing); + double yCoordinate = 10.0 + (wrapper.level * vertSpacing); + setLocation(vertex, xCoordinate, yCoordinate); + } + else + { + double yCoordinate = 10.0 + (wrapper.gridPosition * vertSpacing); + double xCoordinate = 10.0 + (wrapper.level * horzSpacing); + setLocation(vertex, xCoordinate, yCoordinate); + } + } + } + } + + } + + public String toString() + { + return "Jung Sugiyama"; + } + + /** + * Implementation. + * + * First of all, the Algorithm searches the roots from the + * Graph. Starting from this roots the Algorithm creates + * levels and stores them in the member levels. + * The Member levels contains LinkedList Objects and the LinkedList per level + * contains Cell Wrapper Objects. After that the Algorithm + * tries to solve the edge crosses from level to level and + * goes top down and bottom up. After minimization of the + * edge crosses the algorithm moves each node to its + * bary center. + * + */ + private List>> runSugiyama() + { + executed = true; + Set vertexSet = new HashSet(graph.getVertices()); + + makeGraphAcyclic(); + List>> levels = fillLevels(); + levels = balanceLevels(levels); + solveEdgeCrosses(levels); + moveToBarycenter(levels, vertexSet); + return levels; + } + + private void makeGraphAcyclic() { + new AcyclicCalculator().run(); + } + + private List>> fillLevels() { + return fillLevels(0, copyGraph(graph), new LinkedList>>()); + } + + /** Method fills the levels and stores them in the member levels. + + * Each level was represended by a LinkedList with Cell Wrapper objects. + * These LinkedLists are the elements in the levels LinkedList. + * + */ + private List>> fillLevels(int currentLevel, Graph graph, List>> levels) { + if (graph.getVertices().isEmpty()) { + return levels; + } + List roots = searchRoots(graph); + if (levels.size() == currentLevel) + levels.add(currentLevel, new LinkedList>()); + List> vecForTheCurrentLevel = levels.get(currentLevel); + for (V rootNode : roots) { + // Create a wrapper for the node + int numberForTheEntry = vecForTheCurrentLevel.size(); + CellWrapper wrapper = new CellWrapper(currentLevel, numberForTheEntry, rootNode); + + // put the Wrapper in the LevelLinkedList + vecForTheCurrentLevel.add(wrapper); + // concat the wrapper to the cell for an easy access + vertToWrapper.put(rootNode, wrapper); + + graph.removeVertex(rootNode); + } // i.e root level + if (vecForTheCurrentLevel.size() > gridAreaSize) { + gridAreaSize = vecForTheCurrentLevel.size(); + } + fillLevels(currentLevel + 1, graph, levels); // 0 indicates level 0 + return levels; + } + + /** Searches all Roots for the current Graph + * First the method marks any Node as not visited. + * Than calls searchRoots(MyGraphCell) for each + * not visited Cell. + * The Roots are stored in the LinkedList named roots + * + * @return returns a LinkedList with the roots + */ + private List searchRoots(Graph graph) + { + List roots = new LinkedList(); + // first: mark all as not visited + // it is assumed that vertex are not visited + for (V vert : graph.getVertices()) + { + int in_degree = graph.inDegree(vert); + if (in_degree == 0) { + roots.add(vert); + } + } + return roots; + } + + private List>> balanceLevels(List>> levels) { + Map maxLevels = new HashMap(); + Map minLevels = new HashMap(); + List> verticesToAdd = new LinkedList>(); + List sizes = new ArrayList(levels.size()); + for (List> levelList : levels) { + for (CellWrapper node : levelList) { + int minLevel = findMinPossibleLevel(node); + int maxLevel = findMaxPossibleLevel(node, levels.size()); + if (minLevel != maxLevel) { + maxLevels.put(node.getVertexView(), maxLevel); + minLevels.put(node.getVertexView(), minLevel); + verticesToAdd.add(node); + } + } + levelList.removeAll(verticesToAdd); + sizes.add(levelList.size()); + } + for (CellWrapper vertex : verticesToAdd) { + int minSize = Integer.MAX_VALUE; + int minIndex = -1; + for (int i = minLevels.get(vertex.getVertexView()); i <= maxLevels.get(vertex.getVertexView()); i++) { + if (sizes.get(i) < minSize) { + minSize = sizes.get(i); + minIndex = i; + } + } + levels.get(minIndex).add(vertex); + sizes.set(minIndex, sizes.get(minIndex) + 1); + } + List>> newLevels = new LinkedList>>(); + for (List> level : levels) { + LinkedList> newLevel = new LinkedList>(); + for (CellWrapper cellWrapper : level) { + newLevel.add(new CellWrapper(newLevels.size(), newLevel.size(), cellWrapper.getVertexView())); + } + newLevels.add(newLevel); + } + return newLevels; + } + + private int findMaxPossibleLevel(CellWrapper node, int numLevels) { + V vertex = node.getVertexView(); + int maxLevel = numLevels - 1; + for (V successor : graph.getSuccessors(vertex)) { + int level = vertToWrapper.get(successor).getLevel(); + maxLevel = Math.min(level - 1, maxLevel); + } + return maxLevel; + } + + private int findMinPossibleLevel(CellWrapper node) { + V vertex = node.getVertexView(); + Collection predecessors = graph.getPredecessors(vertex); + int minLevel = 0; + for (V predecessor : predecessors) { + int level = vertToWrapper.get(predecessor).getLevel(); + minLevel = Math.max(level + 1, minLevel); + } + return minLevel; + } + + private void solveEdgeCrosses(List>> levels) + { + int movementsCurrentLoop = -1; + + while (movementsCurrentLoop != 0) + { + // reset the movements per loop count + movementsCurrentLoop = 0; + + // top down + for (int i = 0; i < levels.size() - 1; i++) + { + movementsCurrentLoop += solveEdgeCrosses(true, levels, i); + } + + // bottom up + for (int i = levels.size() - 1; i >= 1; i--) + { + movementsCurrentLoop += solveEdgeCrosses(false, levels, i); + } + } + } + + /** + * @return movements + */ + private int solveEdgeCrosses(boolean down, List>> levels, int levelIndex) + { + // Get the current level + List> currentLevel = levels.get(levelIndex); + int movements = 0; + + // restore the old sort + CellWrapper[] levelSortBefore = currentLevel.toArray(new CellWrapper [] {}); + + // new sort + Collections.sort(currentLevel); + + // test for movements + for (int j = 0; j < levelSortBefore.length; j++) + { + if (levelSortBefore[j].getEdgeCrossesIndicator() != + currentLevel.get(j).getEdgeCrossesIndicator()) + { + movements++; + } + } + // Collections Sort sorts the highest value to the first value + for (int j = currentLevel.size() - 1; j >= 0; j--) + { + CellWrapper sourceWrapper = currentLevel.get(j); + + V sourceView = sourceWrapper.getVertexView(); + + Collection edgeList = getNeighborEdges(sourceView); + + for (E edge : edgeList) + { + // if it is a forward edge follow it + V targetView = null; + if (down && sourceView == graph.getSource(edge)) + { + targetView = graph.getDest(edge); + } + if (!down && sourceView == graph.getDest(edge)) + { + targetView = graph.getSource(edge); + } + if (targetView != null) + { + CellWrapper targetWrapper = vertToWrapper.get(targetView); + + // do it only if the edge is a forward edge to a deeper level + if (down && targetWrapper != null && targetWrapper.getLevel() > levelIndex) + { + targetWrapper.addToEdgeCrossesIndicator(sourceWrapper.getEdgeCrossesIndicator()); + } + if (!down && targetWrapper != null && targetWrapper.getLevel() < levelIndex) + { + targetWrapper.addToEdgeCrossesIndicator(sourceWrapper.getEdgeCrossesIndicator()); + } + } + } + } + return movements; + } + + private void moveToBarycenter(List>> levels, Set vertexSet) + { + for (V v : vertexSet) + { + + CellWrapper currentwrapper = vertToWrapper.get(v); + + Collection edgeList = getNeighborEdges(v); + + for (E edge : edgeList) + { + // i have to find neigbhor vertex + V neighborVertex = null; + + if (v == graph.getSource(edge)) + { + neighborVertex = graph.getDest(edge); + } + else + { + if (v == graph.getDest(edge)) + { + neighborVertex = graph.getSource(edge); + } + } + + if ((neighborVertex != null) && (neighborVertex != v)) + { + + CellWrapper neighborWrapper = vertToWrapper.get(neighborVertex); + + if (!(currentwrapper == null || neighborWrapper == null || currentwrapper.level == neighborWrapper.level)) + { + currentwrapper.priority++; + } + } + } + } + for (List> level : levels) + { + int pos = 0; + for (CellWrapper wrapper : level) + { + // calculate the initial Grid Positions 1, 2, 3, .... per Level + wrapper.setGridPosition(pos++); + } + } + + int movementsCurrentLoop = -1; + + while (movementsCurrentLoop != 0) + { + // reset movements + movementsCurrentLoop = 0; + + // top down + for (int i = 1; i < levels.size(); i++) + { + movementsCurrentLoop += moveToBarycenter(levels, i); + } + // bottom up + for (int i = levels.size() - 1; i >= 0; i--) + { + movementsCurrentLoop += moveToBarycenter(levels, i); + } + } + } + + private Collection getNeighborEdges(V v) + { + Collection outEdges = graph.getOutEdges(v); + Collection inEdges = graph.getInEdges(v); + LinkedList edgeList = new LinkedList(); + edgeList.addAll(outEdges); + edgeList.addAll(inEdges); + return edgeList; + } + + private int moveToBarycenter(List>> levels, int levelIndex) + { + // Counter for the movements + int movements = 0; + + // Get the current level + List> currentLevel = levels.get(levelIndex); + + for (int currentIndexInTheLevel = 0; currentIndexInTheLevel < currentLevel.size(); currentIndexInTheLevel++) + { + CellWrapper sourceWrapper = currentLevel.get(currentIndexInTheLevel); + + float gridPositionsSum = 0; + float countNodes = 0; + + V vertexView = sourceWrapper.getVertexView(); + + Collection edgeList = getNeighborEdges(vertexView); + + for (E edge : edgeList) + { + // if it is a forward edge follow it + //Object neighborPort = null; + V neighborVertex = null; + if (vertexView == graph.getSource(edge)) + { + neighborVertex = graph.getDest(edge); + } + else + { + if (vertexView == graph.getSource(edge)) + { + neighborVertex = graph.getDest(edge); + } + } + + if (neighborVertex != null) + { + + CellWrapper targetWrapper = vertToWrapper.get(neighborVertex); + + if (!(targetWrapper == sourceWrapper) || targetWrapper.getLevel() == levelIndex) + { + gridPositionsSum += targetWrapper.getGridPosition(); + countNodes++; + } + } + } + + if (countNodes > 0) + { + float tmp = (gridPositionsSum / countNodes); + int newGridPosition = Math.round(tmp); + boolean toRight = (newGridPosition > sourceWrapper.getGridPosition()); + + boolean moved = true; + + while (newGridPosition != sourceWrapper.getGridPosition() && moved) + { + moved = move(toRight, currentLevel, currentIndexInTheLevel, sourceWrapper.getPriority()); + if (moved) + { + movements++; + } + } + } + } + return movements; + } + + /**@param toRight true = try to move the currentWrapper to right; false = try to move the currentWrapper to left; + * @param currentLevel LinkedList which contains the CellWrappers for the current level + * + * @return The free GridPosition or -1 is position is not free. + */ + private boolean move(boolean toRight, List> currentLevel, int currentIndexInTheLevel, int currentPriority) + { + CellWrapper currentWrapper = currentLevel.get(currentIndexInTheLevel); + + boolean moved = false; + int neighborIndexInTheLevel = currentIndexInTheLevel + (toRight ? 1 : -1); + int newGridPosition = currentWrapper.getGridPosition() + (toRight ? 1 : -1); + + if (0 > newGridPosition || newGridPosition >= gridAreaSize) + { + return false; + } + + // if the node is the first or the last we can move + if (toRight && currentIndexInTheLevel == currentLevel.size() - 1 || !toRight && currentIndexInTheLevel == 0) + { + moved = true; + } + else + { + // else get the neighbor and ask his gridposition + // if he has the requested new grid position + // check the priority + + CellWrapper neighborWrapper = (CellWrapper) currentLevel.get(neighborIndexInTheLevel); + + int neighborPriority = neighborWrapper.getPriority(); + + if (neighborWrapper.getGridPosition() == newGridPosition) + { + if (neighborPriority >= currentPriority) + { + return false; + } + else + { + moved = move(toRight, currentLevel, neighborIndexInTheLevel, currentPriority); + } + } + else + { + moved = true; + } + } + + if (moved) + { + currentWrapper.setGridPosition(newGridPosition); + } + return moved; + } + + //---------------cell wrapper----------------- + /** cell wrapper contains all values + * for one node + */ + class CellWrapper implements Comparable> + { + /** sum value for edge Crosses + */ + private double edgeCrossesIndicator = 0; + + /** counter for additions to the edgeCrossesIndicator + */ + private int additions = 0; + + /** the vertical level where the cell wrapper is inserted + */ + private int level = 0; + + /** current position in the grid + */ + private int gridPosition = 0; + + /** priority for movements to the barycenter + */ + private int priority = 0; + + /** reference to the wrapped cell + */ + private VV wrappedVertex = null; + + private String vertex_name = ""; + + // CellWrapper constructor + CellWrapper(int level, double edgeCrossesIndicator, VV vertex) + { + this.level = level; + this.edgeCrossesIndicator = edgeCrossesIndicator; + this.wrappedVertex = vertex; + vertex_name = vertex.toString(); + additions++; + } + + public String toString() + { + return vertex_name + "," + level + "," + gridPosition + "," + priority + "," + edgeCrossesIndicator + "," + additions; + } + + /** returns the wrapped Vertex + */ + VV getVertexView() + { + return wrappedVertex; + } + + /** retruns the average value for the edge crosses indicator + * + * for the wrapped cell + * + */ + + double getEdgeCrossesIndicator() + { + if (additions == 0) + return 0; + return edgeCrossesIndicator / additions; + } + + /** Addes a value to the edge crosses indicator + * for the wrapped cell + * + */ + void addToEdgeCrossesIndicator(double addValue) + { + edgeCrossesIndicator += addValue; + additions++; + } + + /** gets the level of the wrapped cell + */ + int getLevel() + { + return level; + } + + /** gets the grid position for the wrapped cell + */ + int getGridPosition() + { + return gridPosition; + } + + /** Sets the grid position for the wrapped cell + */ + void setGridPosition(int pos) + { + this.gridPosition = pos; + } + + /** returns the priority of this cell wrapper. + * + * The priority was used by moving the cell to its + * barycenter. + */ + int getPriority() + { + return priority; + } + + /** + * @see java.lang.Comparable#compareTo(Object) + */ + public int compareTo(CellWrapper compare) + { + if (compare.getEdgeCrossesIndicator() == this.getEdgeCrossesIndicator()) + return 0; + + double compareValue = compare.getEdgeCrossesIndicator() - this.getEdgeCrossesIndicator(); + + return (int) (compareValue * 1000); + + } + } + + //-------------------------------------------- + public void reset() + { + vertToWrapper.clear(); + executed = false; + } + + private class AcyclicCalculator { + private Set onStack = new HashSet(); + private Set visited = new HashSet(); + public AcyclicCalculator() { + } + + private void dfs(V vertex) { + if (visited.contains(vertex)) { + return; + } + + visited.add(vertex); + onStack.add(vertex); + + for (E edge: graph.getOutEdges(vertex)) { + V target = graph.getDest(edge); + if (onStack.contains(target)) { + graph.removeEdge(edge); + graph.addEdge(edge, target, vertex); + } else { + dfs(target); + } + } + onStack.remove(vertex); + } + + public void run() { + for (V vertex : graph.getVertices()) { + dfs(vertex); + } + } + } +} diff --git a/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/LayoutGeneratorSmart.java b/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/LayoutGeneratorSmart.java index 3d0788ad..ccdc1dcd 100644 --- a/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/LayoutGeneratorSmart.java +++ b/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/LayoutGeneratorSmart.java @@ -1294,6 +1294,9 @@ public void generateLayout() { + " Layout."); } break; + case 4: // Sugiyama Layout + this.layout = new JungSugiyama<>(this.jgraph); + break; default: this.layout = new ISOMLayout<>(this.jgraph); break; diff --git a/VWorkflows-Demo/src/main/java/eu/mihosoft/vrl/workflow/demo/OptionsWindowFXMLController.java b/VWorkflows-Demo/src/main/java/eu/mihosoft/vrl/workflow/demo/OptionsWindowFXMLController.java index a94cdb36..cbb9fcbb 100644 --- a/VWorkflows-Demo/src/main/java/eu/mihosoft/vrl/workflow/demo/OptionsWindowFXMLController.java +++ b/VWorkflows-Demo/src/main/java/eu/mihosoft/vrl/workflow/demo/OptionsWindowFXMLController.java @@ -68,7 +68,7 @@ public class OptionsWindowFXMLController implements Initializable { private LayoutGeneratorSmart generator; private Stage optionsstage; private VFlow workflow; - private final String[] layouts = new String[4]; + private final String[] layouts = new String[5]; private final String[] graphmodes = new String[3]; /** @@ -82,6 +82,7 @@ public void initialize(URL url, ResourceBundle rb) { this.layouts[1] = "FR Layout"; this.layouts[2] = "KK Layout"; this.layouts[3] = "DAG Layout"; + this.layouts[4] = "Sugiyama Layout"; this.graphmodes[0] = "VFlowModel"; this.graphmodes[1] = "jgraph"; this.graphmodes[2] = "nodelist"; @@ -90,6 +91,7 @@ public void initialize(URL url, ResourceBundle rb) { layoutlist.add(this.layouts[1]); layoutlist.add(this.layouts[2]); layoutlist.add(this.layouts[3]); + layoutlist.add(this.layouts[4]); ObservableList graphmodelist = this.selGraphmode.getItems(); graphmodelist.add(this.graphmodes[0]); graphmodelist.add(this.graphmodes[1]); From bf9cf3995821743d95f141ec7a9bcf2aa95b263c Mon Sep 17 00:00:00 2001 From: bjeffrie Date: Wed, 7 Dec 2016 14:43:20 -0500 Subject: [PATCH 2/2] Small modifications and formating to pass vworkflow's style checker --- .../vrl/workflow/incubating/JungSugiyama.java | 1064 ++++++++--------- 1 file changed, 490 insertions(+), 574 deletions(-) diff --git a/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java b/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java index 00c5241d..24eae606 100644 --- a/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java +++ b/VWorkflows-Core/src/main/java/eu/mihosoft/vrl/workflow/incubating/JungSugiyama.java @@ -1,28 +1,24 @@ /* * Copyright (c) 2012 Stefan Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +// package hudson.plugins.depgraph_view.model.layout; +/* + * from https://github.com/jenkinsci/depgraph-view-plugin + * bjeffrie 2016-10-01 Add JungSugiyama as layout in VWorkflows package hierarchy. + * bjeffrie 2016-12-07 Small modifications and formating to pass vworkflow's style checker */ -//package hudson.plugins.depgraph_view.model.layout; -// bjeffrie 2016-10-01 Add JungSugiyama as layout in VWorkflows package hierarchy. -// No other changes from https://github.com/jenkinsci/depgraph-view-plugin version. package eu.mihosoft.vrl.workflow.incubating; import edu.uci.ics.jung.algorithms.layout.AbstractLayout; @@ -39,71 +35,64 @@ import java.util.Set; /** -* Arranges the nodes with the Sugiyama Layout Algorithm.
-* * -* -* Link to the algorithm
-* -* Originally, source was posted to the Jung2 forum, for Jung 1.x. Not sure where the original -* code came from, but ti didn;t work for Jung2, but it was not that complicated, so I pounded it -* into shape for Jung2, complete with generics and such. Lays out either top-down to left-right. -* -* Seems to work. Paramterize with spacing and orientation. -* -* C. Schanck (chris at schanck dot net) -*/ - -public class JungSugiyama extends AbstractLayout -{ + * Arranges the nodes with the Sugiyama Layout Algorithm.
+ * * Link to the algorithm
+ * + * Originally, source was posted to the Jung2 forum, for Jung 1.x. Not sure where the original code came from, but ti didn;t work for Jung2, but it was not that complicated, so I + * pounded it into shape for Jung2, complete with generics and such. Lays out either top-down to left-right. + * + * Seems to work. Paramterize with spacing and orientation. + * + * C. Schanck (chris at schanck dot net) + */ - private static final Orientation DEFAULT_ORIENTATION = Orientation.TOP; +public class JungSugiyama extends AbstractLayout { - private static final int DEFAULT_HORIZONTAL_SPACING = 200; + private static final Orientation DEFAULT_ORIENTATION = Orientation.TOP; - private static final int DEFAULT_VERTICAL_SPACING = 100; + private static final int DEFAULT_HORIZONTAL_SPACING = 200; - public static enum Orientation - { - TOP, LEFT - }; + private static final int DEFAULT_VERTICAL_SPACING = 100; + public static enum Orientation { + TOP, LEFT + }; - private boolean executed = false; + private boolean executed = false; - /** represents the size of the grid in horizontal grid elements - * - */ - private int gridAreaSize = Integer.MIN_VALUE; + /** + * represents the size of the grid in horizontal grid elements + * + */ + private int gridAreaSize = Integer.MIN_VALUE; - private int horzSpacing; + private int horzSpacing; - private int vertSpacing; + private int vertSpacing; - private Map> vertToWrapper = new HashMap>(); + private Map> vertToWrapper = new HashMap>(); - private Orientation orientation; + private Orientation orientation; - public JungSugiyama(Graph g) - { - this(g, DEFAULT_ORIENTATION, DEFAULT_HORIZONTAL_SPACING, DEFAULT_VERTICAL_SPACING); - } + public JungSugiyama(Graph g) { + this(g, DEFAULT_ORIENTATION, DEFAULT_HORIZONTAL_SPACING, DEFAULT_VERTICAL_SPACING); + } - public JungSugiyama(Graph g, Orientation orientation, int horzSpacing, int vertSpacing) - { - super(copyGraph(g)); - this.orientation = orientation; - this.horzSpacing = horzSpacing; - this.vertSpacing = vertSpacing; - } + public JungSugiyama(Graph g, Orientation orientation, int horzSpacing, int vertSpacing) { + super(copyGraph(g)); + this.orientation = orientation; + this.horzSpacing = horzSpacing; + this.vertSpacing = vertSpacing; + } - private static Graph copyGraph(Graph src) { + private static Graph copyGraph(Graph src) { try { @SuppressWarnings("unchecked") - Graph dest = (Graph) src.getClass().newInstance(); - for (V v : src.getVertices()) + Graph dest = (Graph) src.getClass().newInstance(); + for (V v: src.getVertices()) dest.addVertex(v); - for (E e : src.getEdges()) + for (E e: src.getEdges()) dest.addEdge(e, src.getIncidentVertices(e)); return dest; } catch (RuntimeException e) { @@ -113,66 +102,51 @@ private static Graph copyGraph(Graph src) { } } - public void initialize() - { - if (!executed) - { - List>> graphLevels = runSugiyama(); - for (List> level : graphLevels) - { - for (CellWrapper wrapper : level) - { - V vertex = wrapper.getVertexView(); - - if (orientation.equals(Orientation.TOP)) - { - double xCoordinate = 10.0 + (wrapper.gridPosition * horzSpacing); - double yCoordinate = 10.0 + (wrapper.level * vertSpacing); - setLocation(vertex, xCoordinate, yCoordinate); - } - else - { - double yCoordinate = 10.0 + (wrapper.gridPosition * vertSpacing); - double xCoordinate = 10.0 + (wrapper.level * horzSpacing); - setLocation(vertex, xCoordinate, yCoordinate); - } - } - } - } - - } - - public String toString() - { - return "Jung Sugiyama"; - } - - /** - * Implementation. - * - * First of all, the Algorithm searches the roots from the - * Graph. Starting from this roots the Algorithm creates - * levels and stores them in the member levels. - * The Member levels contains LinkedList Objects and the LinkedList per level - * contains Cell Wrapper Objects. After that the Algorithm - * tries to solve the edge crosses from level to level and - * goes top down and bottom up. After minimization of the - * edge crosses the algorithm moves each node to its - * bary center. - * - */ - private List>> runSugiyama() - { - executed = true; - Set vertexSet = new HashSet(graph.getVertices()); + public void initialize() { + if (!executed) { + List>> graphLevels = runSugiyama(); + for (List> level: graphLevels) { + for (CellWrapper wrapper: level) { + V vertex = wrapper.getVertexView(); + + if (orientation.equals(Orientation.TOP)) { + double xCoordinate = 10.0 + (wrapper.gridPosition * horzSpacing); + double yCoordinate = 10.0 + (wrapper.level * vertSpacing); + setLocation(vertex, xCoordinate, yCoordinate); + } else { + double yCoordinate = 10.0 + (wrapper.gridPosition * vertSpacing); + double xCoordinate = 10.0 + (wrapper.level * horzSpacing); + setLocation(vertex, xCoordinate, yCoordinate); + } + } + } + } + + } + + public String toString() { + return "Jung Sugiyama"; + } + + /** + * Implementation. + * + * First of all, the Algorithm searches the roots from the Graph. Starting from this roots the Algorithm creates levels and stores them in the member levels. The + * Member levels contains LinkedList Objects and the LinkedList per level contains Cell Wrapper Objects. After that the Algorithm tries to solve the edge crosses from level to + * level and goes top down and bottom up. After minimization of the edge crosses the algorithm moves each node to its bary center. + * + */ + private List>> runSugiyama() { + executed = true; + Set vertexSet = new HashSet(graph.getVertices()); makeGraphAcyclic(); - List>> levels = fillLevels(); + List>> levels = fillLevels(); levels = balanceLevels(levels); - solveEdgeCrosses(levels); - moveToBarycenter(levels, vertexSet); - return levels; - } + solveEdgeCrosses(levels); + moveToBarycenter(levels, vertexSet); + return levels; + } private void makeGraphAcyclic() { new AcyclicCalculator().run(); @@ -182,21 +156,20 @@ private List>> fillLevels() { return fillLevels(0, copyGraph(graph), new LinkedList>>()); } - /** Method fills the levels and stores them in the member levels. - - * Each level was represended by a LinkedList with Cell Wrapper objects. - * These LinkedLists are the elements in the levels LinkedList. - * - */ - private List>> fillLevels(int currentLevel, Graph graph, List>> levels) { + /** + * Method fills the levels and stores them in the member levels. + * + * Each level was represended by a LinkedList with Cell Wrapper objects. These LinkedLists are the elements in the levels LinkedList. + * + */ + private List>> fillLevels(int currentLevel, Graph graph, List>> levels) { if (graph.getVertices().isEmpty()) { return levels; } List roots = searchRoots(graph); - if (levels.size() == currentLevel) - levels.add(currentLevel, new LinkedList>()); + if (levels.size() == currentLevel) levels.add(currentLevel, new LinkedList>()); List> vecForTheCurrentLevel = levels.get(currentLevel); - for (V rootNode : roots) { + for (V rootNode: roots) { // Create a wrapper for the node int numberForTheEntry = vecForTheCurrentLevel.size(); CellWrapper wrapper = new CellWrapper(currentLevel, numberForTheEntry, rootNode); @@ -213,23 +186,19 @@ private List>> fillLevels(int currentLevel, Graph grap } fillLevels(currentLevel + 1, graph, levels); // 0 indicates level 0 return levels; - } + } - /** Searches all Roots for the current Graph - * First the method marks any Node as not visited. - * Than calls searchRoots(MyGraphCell) for each - * not visited Cell. - * The Roots are stored in the LinkedList named roots + /** + * Searches all Roots for the current Graph First the method marks any Node as not visited. Than calls searchRoots(MyGraphCell) for each not visited Cell. The Roots are stored + * in the LinkedList named roots * * @return returns a LinkedList with the roots */ - private List searchRoots(Graph graph) - { + private List searchRoots(Graph graph) { List roots = new LinkedList(); // first: mark all as not visited // it is assumed that vertex are not visited - for (V vert : graph.getVertices()) - { + for (V vert: graph.getVertices()) { int in_degree = graph.inDegree(vert); if (in_degree == 0) { roots.add(vert); @@ -243,8 +212,8 @@ private List>> balanceLevels(List>> leve Map minLevels = new HashMap(); List> verticesToAdd = new LinkedList>(); List sizes = new ArrayList(levels.size()); - for (List> levelList : levels) { - for (CellWrapper node : levelList) { + for (List> levelList: levels) { + for (CellWrapper node: levelList) { int minLevel = findMinPossibleLevel(node); int maxLevel = findMaxPossibleLevel(node, levels.size()); if (minLevel != maxLevel) { @@ -256,7 +225,7 @@ private List>> balanceLevels(List>> leve levelList.removeAll(verticesToAdd); sizes.add(levelList.size()); } - for (CellWrapper vertex : verticesToAdd) { + for (CellWrapper vertex: verticesToAdd) { int minSize = Integer.MAX_VALUE; int minIndex = -1; for (int i = minLevels.get(vertex.getVertexView()); i <= maxLevels.get(vertex.getVertexView()); i++) { @@ -269,9 +238,9 @@ private List>> balanceLevels(List>> leve sizes.set(minIndex, sizes.get(minIndex) + 1); } List>> newLevels = new LinkedList>>(); - for (List> level : levels) { + for (List> level: levels) { LinkedList> newLevel = new LinkedList>(); - for (CellWrapper cellWrapper : level) { + for (CellWrapper cellWrapper: level) { newLevel.add(new CellWrapper(newLevels.size(), newLevel.size(), cellWrapper.getVertexView())); } newLevels.add(newLevel); @@ -282,7 +251,7 @@ private List>> balanceLevels(List>> leve private int findMaxPossibleLevel(CellWrapper node, int numLevels) { V vertex = node.getVertexView(); int maxLevel = numLevels - 1; - for (V successor : graph.getSuccessors(vertex)) { + for (V successor: graph.getSuccessors(vertex)) { int level = vertToWrapper.get(successor).getLevel(); maxLevel = Math.min(level - 1, maxLevel); } @@ -293,438 +262,385 @@ private int findMinPossibleLevel(CellWrapper node) { V vertex = node.getVertexView(); Collection predecessors = graph.getPredecessors(vertex); int minLevel = 0; - for (V predecessor : predecessors) { + for (V predecessor: predecessors) { int level = vertToWrapper.get(predecessor).getLevel(); minLevel = Math.max(level + 1, minLevel); } return minLevel; } - private void solveEdgeCrosses(List>> levels) - { - int movementsCurrentLoop = -1; - - while (movementsCurrentLoop != 0) - { - // reset the movements per loop count - movementsCurrentLoop = 0; - - // top down - for (int i = 0; i < levels.size() - 1; i++) - { - movementsCurrentLoop += solveEdgeCrosses(true, levels, i); - } - - // bottom up - for (int i = levels.size() - 1; i >= 1; i--) - { - movementsCurrentLoop += solveEdgeCrosses(false, levels, i); - } - } - } - - /** - * @return movements - */ - private int solveEdgeCrosses(boolean down, List>> levels, int levelIndex) - { - // Get the current level - List> currentLevel = levels.get(levelIndex); - int movements = 0; - - // restore the old sort - CellWrapper[] levelSortBefore = currentLevel.toArray(new CellWrapper [] {}); - - // new sort - Collections.sort(currentLevel); - - // test for movements - for (int j = 0; j < levelSortBefore.length; j++) - { - if (levelSortBefore[j].getEdgeCrossesIndicator() != - currentLevel.get(j).getEdgeCrossesIndicator()) - { - movements++; - } - } - // Collections Sort sorts the highest value to the first value - for (int j = currentLevel.size() - 1; j >= 0; j--) - { - CellWrapper sourceWrapper = currentLevel.get(j); - - V sourceView = sourceWrapper.getVertexView(); - - Collection edgeList = getNeighborEdges(sourceView); - - for (E edge : edgeList) - { - // if it is a forward edge follow it - V targetView = null; - if (down && sourceView == graph.getSource(edge)) - { - targetView = graph.getDest(edge); - } - if (!down && sourceView == graph.getDest(edge)) - { - targetView = graph.getSource(edge); - } - if (targetView != null) - { - CellWrapper targetWrapper = vertToWrapper.get(targetView); - - // do it only if the edge is a forward edge to a deeper level - if (down && targetWrapper != null && targetWrapper.getLevel() > levelIndex) - { - targetWrapper.addToEdgeCrossesIndicator(sourceWrapper.getEdgeCrossesIndicator()); - } - if (!down && targetWrapper != null && targetWrapper.getLevel() < levelIndex) - { - targetWrapper.addToEdgeCrossesIndicator(sourceWrapper.getEdgeCrossesIndicator()); - } - } - } - } - return movements; - } - - private void moveToBarycenter(List>> levels, Set vertexSet) - { - for (V v : vertexSet) - { - - CellWrapper currentwrapper = vertToWrapper.get(v); - - Collection edgeList = getNeighborEdges(v); - - for (E edge : edgeList) - { - // i have to find neigbhor vertex - V neighborVertex = null; - - if (v == graph.getSource(edge)) - { - neighborVertex = graph.getDest(edge); - } - else - { - if (v == graph.getDest(edge)) - { - neighborVertex = graph.getSource(edge); - } - } - - if ((neighborVertex != null) && (neighborVertex != v)) - { - - CellWrapper neighborWrapper = vertToWrapper.get(neighborVertex); - - if (!(currentwrapper == null || neighborWrapper == null || currentwrapper.level == neighborWrapper.level)) - { - currentwrapper.priority++; - } - } - } - } - for (List> level : levels) - { - int pos = 0; - for (CellWrapper wrapper : level) - { - // calculate the initial Grid Positions 1, 2, 3, .... per Level - wrapper.setGridPosition(pos++); - } - } - - int movementsCurrentLoop = -1; - - while (movementsCurrentLoop != 0) - { - // reset movements - movementsCurrentLoop = 0; - - // top down - for (int i = 1; i < levels.size(); i++) - { - movementsCurrentLoop += moveToBarycenter(levels, i); - } - // bottom up - for (int i = levels.size() - 1; i >= 0; i--) - { - movementsCurrentLoop += moveToBarycenter(levels, i); - } - } - } - - private Collection getNeighborEdges(V v) - { - Collection outEdges = graph.getOutEdges(v); - Collection inEdges = graph.getInEdges(v); - LinkedList edgeList = new LinkedList(); - edgeList.addAll(outEdges); - edgeList.addAll(inEdges); - return edgeList; - } - - private int moveToBarycenter(List>> levels, int levelIndex) - { - // Counter for the movements - int movements = 0; - - // Get the current level - List> currentLevel = levels.get(levelIndex); - - for (int currentIndexInTheLevel = 0; currentIndexInTheLevel < currentLevel.size(); currentIndexInTheLevel++) - { - CellWrapper sourceWrapper = currentLevel.get(currentIndexInTheLevel); - - float gridPositionsSum = 0; - float countNodes = 0; - - V vertexView = sourceWrapper.getVertexView(); - - Collection edgeList = getNeighborEdges(vertexView); - - for (E edge : edgeList) - { - // if it is a forward edge follow it - //Object neighborPort = null; - V neighborVertex = null; - if (vertexView == graph.getSource(edge)) - { - neighborVertex = graph.getDest(edge); - } - else - { - if (vertexView == graph.getSource(edge)) - { - neighborVertex = graph.getDest(edge); - } - } - - if (neighborVertex != null) - { - - CellWrapper targetWrapper = vertToWrapper.get(neighborVertex); - - if (!(targetWrapper == sourceWrapper) || targetWrapper.getLevel() == levelIndex) - { - gridPositionsSum += targetWrapper.getGridPosition(); - countNodes++; - } - } - } - - if (countNodes > 0) - { - float tmp = (gridPositionsSum / countNodes); - int newGridPosition = Math.round(tmp); - boolean toRight = (newGridPosition > sourceWrapper.getGridPosition()); - - boolean moved = true; - - while (newGridPosition != sourceWrapper.getGridPosition() && moved) - { - moved = move(toRight, currentLevel, currentIndexInTheLevel, sourceWrapper.getPriority()); - if (moved) - { - movements++; - } - } - } - } - return movements; - } - - /**@param toRight true = try to move the currentWrapper to right; false = try to move the currentWrapper to left; - * @param currentLevel LinkedList which contains the CellWrappers for the current level - * - * @return The free GridPosition or -1 is position is not free. - */ - private boolean move(boolean toRight, List> currentLevel, int currentIndexInTheLevel, int currentPriority) - { - CellWrapper currentWrapper = currentLevel.get(currentIndexInTheLevel); - - boolean moved = false; - int neighborIndexInTheLevel = currentIndexInTheLevel + (toRight ? 1 : -1); - int newGridPosition = currentWrapper.getGridPosition() + (toRight ? 1 : -1); - - if (0 > newGridPosition || newGridPosition >= gridAreaSize) - { - return false; - } - - // if the node is the first or the last we can move - if (toRight && currentIndexInTheLevel == currentLevel.size() - 1 || !toRight && currentIndexInTheLevel == 0) - { - moved = true; - } - else - { - // else get the neighbor and ask his gridposition - // if he has the requested new grid position - // check the priority - - CellWrapper neighborWrapper = (CellWrapper) currentLevel.get(neighborIndexInTheLevel); - - int neighborPriority = neighborWrapper.getPriority(); - - if (neighborWrapper.getGridPosition() == newGridPosition) - { - if (neighborPriority >= currentPriority) - { - return false; - } - else - { - moved = move(toRight, currentLevel, neighborIndexInTheLevel, currentPriority); - } - } - else - { - moved = true; - } - } - - if (moved) - { - currentWrapper.setGridPosition(newGridPosition); - } - return moved; - } - - //---------------cell wrapper----------------- - /** cell wrapper contains all values - * for one node - */ - class CellWrapper implements Comparable> - { - /** sum value for edge Crosses - */ - private double edgeCrossesIndicator = 0; - - /** counter for additions to the edgeCrossesIndicator - */ - private int additions = 0; - - /** the vertical level where the cell wrapper is inserted - */ - private int level = 0; - - /** current position in the grid - */ - private int gridPosition = 0; - - /** priority for movements to the barycenter - */ - private int priority = 0; - - /** reference to the wrapped cell - */ - private VV wrappedVertex = null; - - private String vertex_name = ""; - - // CellWrapper constructor - CellWrapper(int level, double edgeCrossesIndicator, VV vertex) - { - this.level = level; - this.edgeCrossesIndicator = edgeCrossesIndicator; - this.wrappedVertex = vertex; - vertex_name = vertex.toString(); - additions++; - } - - public String toString() - { - return vertex_name + "," + level + "," + gridPosition + "," + priority + "," + edgeCrossesIndicator + "," + additions; - } - - /** returns the wrapped Vertex - */ - VV getVertexView() - { - return wrappedVertex; - } - - /** retruns the average value for the edge crosses indicator - * - * for the wrapped cell - * - */ - - double getEdgeCrossesIndicator() - { - if (additions == 0) - return 0; - return edgeCrossesIndicator / additions; - } - - /** Addes a value to the edge crosses indicator - * for the wrapped cell - * - */ - void addToEdgeCrossesIndicator(double addValue) - { - edgeCrossesIndicator += addValue; - additions++; - } - - /** gets the level of the wrapped cell - */ - int getLevel() - { - return level; - } - - /** gets the grid position for the wrapped cell - */ - int getGridPosition() - { - return gridPosition; - } - - /** Sets the grid position for the wrapped cell - */ - void setGridPosition(int pos) - { - this.gridPosition = pos; - } - - /** returns the priority of this cell wrapper. - * - * The priority was used by moving the cell to its - * barycenter. - */ - int getPriority() - { - return priority; - } - - /** - * @see java.lang.Comparable#compareTo(Object) - */ - public int compareTo(CellWrapper compare) - { - if (compare.getEdgeCrossesIndicator() == this.getEdgeCrossesIndicator()) - return 0; - - double compareValue = compare.getEdgeCrossesIndicator() - this.getEdgeCrossesIndicator(); - - return (int) (compareValue * 1000); - - } - } - - //-------------------------------------------- - public void reset() - { - vertToWrapper.clear(); - executed = false; - } + private void solveEdgeCrosses(List>> levels) { + int movementsCurrentLoop = -1; + + while (movementsCurrentLoop != 0) { + // reset the movements per loop count + movementsCurrentLoop = 0; + + // top down + for (int i = 0; i < levels.size() - 1; i++) { + movementsCurrentLoop += solveEdgeCrosses(true, levels, i); + } + + // bottom up + for (int i = levels.size() - 1; i >= 1; i--) { + movementsCurrentLoop += solveEdgeCrosses(false, levels, i); + } + } + } + + /** + * @return movements + */ + private int solveEdgeCrosses(boolean down, List>> levels, int levelIndex) { + // Get the current level + List> currentLevel = levels.get(levelIndex); + int movements = 0; + + // restore the old sort + CellWrapper[] levelSortBefore = currentLevel.toArray(new CellWrapper[] {}); + + // new sort + Collections.sort(currentLevel); + + // test for movements + for (int j = 0; j < levelSortBefore.length; j++) { + if (levelSortBefore[ j ].getEdgeCrossesIndicator() != currentLevel.get(j).getEdgeCrossesIndicator()) { + movements++; + } + } + // Collections Sort sorts the highest value to the first value + for (int j = currentLevel.size() - 1; j >= 0; j--) { + CellWrapper sourceWrapper = currentLevel.get(j); + + V sourceView = sourceWrapper.getVertexView(); + + Collection edgeList = getNeighborEdges(sourceView); + + for (E edge: edgeList) { + // if it is a forward edge follow it + V targetView = null; + if (down && sourceView == graph.getSource(edge)) { + targetView = graph.getDest(edge); + } + if (!down && sourceView == graph.getDest(edge)) { + targetView = graph.getSource(edge); + } + if (targetView != null) { + CellWrapper targetWrapper = vertToWrapper.get(targetView); + + // do it only if the edge is a forward edge to a deeper level + if (down && targetWrapper != null && targetWrapper.getLevel() > levelIndex) { + targetWrapper.addToEdgeCrossesIndicator(sourceWrapper.getEdgeCrossesIndicator()); + } + if (!down && targetWrapper != null && targetWrapper.getLevel() < levelIndex) { + targetWrapper.addToEdgeCrossesIndicator(sourceWrapper.getEdgeCrossesIndicator()); + } + } + } + } + return movements; + } + + private void moveToBarycenter(List>> levels, Set vertexSet) { + for (V v: vertexSet) { + + CellWrapper currentwrapper = vertToWrapper.get(v); + + Collection edgeList = getNeighborEdges(v); + + for (E edge: edgeList) { + // i have to find neigbhor vertex + V neighborVertex = null; + + if (v == graph.getSource(edge)) { + neighborVertex = graph.getDest(edge); + } else { + if (v == graph.getDest(edge)) { + neighborVertex = graph.getSource(edge); + } + } + + if ((neighborVertex != null) && (neighborVertex != v)) { + + CellWrapper neighborWrapper = vertToWrapper.get(neighborVertex); + + if (!(currentwrapper == null || neighborWrapper == null || currentwrapper.level == neighborWrapper.level)) { + currentwrapper.priority++; + } + } + } + } + for (List> level: levels) { + int pos = 0; + for (CellWrapper wrapper: level) { + // calculate the initial Grid Positions 1, 2, 3, .... per Level + wrapper.setGridPosition(pos++); + } + } + + int movementsCurrentLoop = -1; + + while (movementsCurrentLoop != 0) { + // reset movements + movementsCurrentLoop = 0; + + // top down + for (int i = 1; i < levels.size(); i++) { + movementsCurrentLoop += moveToBarycenter(levels, i); + } + // bottom up + for (int i = levels.size() - 1; i >= 0; i--) { + movementsCurrentLoop += moveToBarycenter(levels, i); + } + } + } + + private Collection getNeighborEdges(V v) { + Collection outEdges = graph.getOutEdges(v); + Collection inEdges = graph.getInEdges(v); + LinkedList edgeList = new LinkedList(); + edgeList.addAll(outEdges); + edgeList.addAll(inEdges); + return edgeList; + } + + private int moveToBarycenter(List>> levels, int levelIndex) { + // Counter for the movements + int movements = 0; + + // Get the current level + List> currentLevel = levels.get(levelIndex); + + for (int currentIndexInTheLevel = 0; currentIndexInTheLevel < currentLevel.size(); currentIndexInTheLevel++) { + CellWrapper sourceWrapper = currentLevel.get(currentIndexInTheLevel); + + float gridPositionsSum = 0; + float countNodes = 0; + + V vertexView = sourceWrapper.getVertexView(); + + Collection edgeList = getNeighborEdges(vertexView); + + for (E edge: edgeList) { + // if it is a forward edge follow it + // Object neighborPort = null; + V neighborVertex = null; + if (vertexView == graph.getSource(edge)) { + neighborVertex = graph.getDest(edge); + } else { + if (vertexView == graph.getSource(edge)) { + neighborVertex = graph.getDest(edge); + } + } + + if (neighborVertex != null) { + + CellWrapper targetWrapper = vertToWrapper.get(neighborVertex); + + if (!(targetWrapper == sourceWrapper) || targetWrapper.getLevel() == levelIndex) { + gridPositionsSum += targetWrapper.getGridPosition(); + countNodes++; + } + } + } + + if (countNodes > 0) { + float tmp = (gridPositionsSum / countNodes); + int newGridPosition = Math.round(tmp); + boolean toRight = (newGridPosition > sourceWrapper.getGridPosition()); + + boolean moved = true; + + while (newGridPosition != sourceWrapper.getGridPosition() && moved) { + moved = move(toRight, currentLevel, currentIndexInTheLevel, sourceWrapper.getPriority()); + if (moved) { + movements++; + } + } + } + } + return movements; + } + + /** + * @param toRight + * true = try to move the currentWrapper to right; false = try to move the currentWrapper to left; + * @param currentLevel + * LinkedList which contains the CellWrappers for the current level + * + * @return The free GridPosition or -1 is position is not free. + */ + private boolean move(boolean toRight, List> currentLevel, int currentIndexInTheLevel, int currentPriority) { + CellWrapper currentWrapper = currentLevel.get(currentIndexInTheLevel); + + boolean moved = false; + int neighborIndexInTheLevel = currentIndexInTheLevel + (toRight ? 1 : -1); + int newGridPosition = currentWrapper.getGridPosition() + (toRight ? 1 : -1); + + if (0 > newGridPosition || newGridPosition >= gridAreaSize) { + return false; + } + + // if the node is the first or the last we can move + if (toRight && currentIndexInTheLevel == currentLevel.size() - 1 || !toRight && currentIndexInTheLevel == 0) { + moved = true; + } else { + // else get the neighbor and ask his gridposition + // if he has the requested new grid position + // check the priority + + CellWrapper neighborWrapper = (CellWrapper) currentLevel.get(neighborIndexInTheLevel); + + int neighborPriority = neighborWrapper.getPriority(); + + if (neighborWrapper.getGridPosition() == newGridPosition) { + if (neighborPriority >= currentPriority) { + return false; + } else { + moved = move(toRight, currentLevel, neighborIndexInTheLevel, currentPriority); + } + } else { + moved = true; + } + } + + if (moved) { + currentWrapper.setGridPosition(newGridPosition); + } + return moved; + } + + // ---------------cell wrapper----------------- + /** + * cell wrapper contains all values for one node + */ + static class CellWrapper implements Comparable> { + /** + * sum value for edge Crosses + */ + private double edgeCrossesIndicator = 0; + + /** + * counter for additions to the edgeCrossesIndicator + */ + private int additions = 0; + + /** + * the vertical level where the cell wrapper is inserted + */ + private int level = 0; + + /** + * current position in the grid + */ + private int gridPosition = 0; + + /** + * priority for movements to the barycenter + */ + private int priority = 0; + + /** + * reference to the wrapped cell + */ + private VV wrappedVertex = null; + + private String vertexName = ""; + + // CellWrapper constructor + CellWrapper(int level, double edgeCrossesIndicator, VV vertex) { + this.level = level; + this.edgeCrossesIndicator = edgeCrossesIndicator; + this.wrappedVertex = vertex; + vertexName = vertex.toString(); + additions++; + } + + public String toString() { + return vertexName + "," + level + "," + gridPosition + "," + priority + "," + edgeCrossesIndicator + "," + additions; + } + + /** + * returns the wrapped Vertex + */ + VV getVertexView() { + return wrappedVertex; + } + + /** + * retruns the average value for the edge crosses indicator + * + * for the wrapped cell + * + */ + + double getEdgeCrossesIndicator() { + if (additions == 0) return 0; + return edgeCrossesIndicator / additions; + } + + /** + * Addes a value to the edge crosses indicator for the wrapped cell + * + */ + void addToEdgeCrossesIndicator(double addValue) { + edgeCrossesIndicator += addValue; + additions++; + } + + /** + * gets the level of the wrapped cell + */ + int getLevel() { + return level; + } + + /** + * gets the grid position for the wrapped cell + */ + int getGridPosition() { + return gridPosition; + } + + /** + * Sets the grid position for the wrapped cell + */ + void setGridPosition(int pos) { + this.gridPosition = pos; + } + + /** + * returns the priority of this cell wrapper. + * + * The priority was used by moving the cell to its barycenter. + */ + int getPriority() { + return priority; + } + + /** + * @see java.lang.Comparable#compareTo(Object) + */ + public int compareTo(CellWrapper compare) { + if (compare.getEdgeCrossesIndicator() == this.getEdgeCrossesIndicator()) return 0; + + double compareValue = compare.getEdgeCrossesIndicator() - this.getEdgeCrossesIndicator(); + + return (int) (compareValue * 1000); + + } + } + + // -------------------------------------------- + public void reset() { + vertToWrapper.clear(); + executed = false; + } private class AcyclicCalculator { private Set onStack = new HashSet(); private Set visited = new HashSet(); + public AcyclicCalculator() { } @@ -749,7 +665,7 @@ private void dfs(V vertex) { } public void run() { - for (V vertex : graph.getVertices()) { + for (V vertex: graph.getVertices()) { dfs(vertex); } }