// This file is part of the Echidna project
// (C) 2002 Forschungszentrum Informatik (FZI) Karlsruhe
// Please visit our website at http://echidna.sf.net
package com.opensymphony.workflow.designer.layout;
import javax.swing.*;
import org.jgraph.JGraph;
import org.jgraph.graph.*;
import java.awt.Point;
import java.awt.Rectangle;
import java.text.NumberFormat;
import java.util.*;
/**
* Arranges the nodes with the Sugiyama Layout Algorithm.<br>
*
* <a href="http://plg.uwaterloo.ca/~itbowman/CS746G/Notes/Sugiyama1981_MVU/">
* Link to the algorithm</a>
*
*<br>
*<br>
* @author Sven Luzar<br>
* @version 1.0 init
*/
public class SugiyamaLayoutAlgorithm implements LayoutAlgorithm
{
/** Field for debug output
*/
protected final boolean verbose = false;
/** Const to add Attributes at the Nodes
*
*/
public static final String SUGIYAMA_VISITED = "SugiyamaVisited"/*#Frozen*/;
/** Const to add the Cell Wrapper to the Nodes
*/
public static final String SUGIYAMA_CELL_WRAPPER = "SugiyamaCellWrapper"/*#Frozen*/;
/** represents the size of the grid in horizontal grid elements
*
*/
protected int gridAreaSize = Integer.MIN_VALUE;
/** A List with Integer Objects. The List contains the
* history of movements per loop
* It was needed for the progress dialog
*/
List movements = null;
/** Represents the movements in the current loop.
* It was needed for the progress dialog
*/
int movementsCurrentLoop = -1;
/** Represents the maximum of movements in the current loop.
* It was needed for the progress dialog
*/
int movementsMax = Integer.MIN_VALUE;
/** Represents the current loop number
* It was needed for the progress dialog
*/
int iteration = 0;
public static final String KEY_HORIZONTAL_SPACING = "HorizontalSpacing";
public static final String KEY_VERTICAL_SPACING = "VerticalSpacing";
/**
* 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 <code>levels</code>.
* The Member levels contains List Objects and the List 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. Last but not Least the method draws the Graph.
*
* @see LayoutAlgorithm
*
*/
public void perform(JGraph jgraph, boolean applyToAll, Properties configuration)
{
Object[] selectedCells = (applyToAll ? jgraph.getRoots() : jgraph.getSelectionCells());
CellView[] selectedCellViews = jgraph.getGraphLayoutCache().getMapping(selectedCells);
Point spacing = new Point();
/* The Algorithm distributes the nodes on a grid.
* For this grid you can configure the horizontal spacing.
* This field specifies the configured value
*
*/
spacing.x = Integer.parseInt(configuration.getProperty(KEY_HORIZONTAL_SPACING));
/* The Algorithm distributes the nodes on a grid.
* For this grid you can configure the vertical spacing.
* This field specifies the configured value
*
*/
spacing.y = Integer.parseInt(configuration.getProperty(KEY_VERTICAL_SPACING));
// search all roots
List roots = searchRoots(jgraph, selectedCellViews);
// return if no root found
if(roots.size() == 0)
return;
// create levels
List levels = fillLevels(jgraph, selectedCellViews, roots);
// solves the edge crosses
solveEdgeCrosses(jgraph, levels);
// move all nodes into the barycenter
moveToBarycenter(jgraph, selectedCellViews, levels);
Point min = findMinimumAndSpacing(selectedCellViews, spacing);
// draw the graph in the window
drawGraph(jgraph, levels, min, spacing);
// clean temp values from the nodes / cells
// the clean up was made in drawGraph
//cleanUp(selectedCellViews);
}
/** Debugdisplay for the edge crosses indicators on the System out
*/
protected void displayEdgeCrossesValues(List levels)
{
System.out.println("----------------Edge Crosses Indicator Values"/*#Frozen*/);
for(int i = 0; i < levels.size() - 1; i++)
{
// Get the current level
List currentLevel = (List)levels.get(i);
System.out.print("Level (" + i + "):"/*#Frozen*/);
for(int j = 0; j < currentLevel.size(); j++)
{
CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(j);
System.out.print(NumberFormat.getNumberInstance().format(sourceWrapper.getEdgeCrossesIndicator()) + " - "/*#Frozen*/);
}
System.out.println();
}
}
/** Debugdisplay for the grid positions on the System out
*/
protected void displayGridPositions(List levels)
{
System.out.println("----------------GridPositions"/*#Frozen*/);
for(int i = 0; i < levels.size() - 1; i++)
{
// Get the current level
List currentLevel = (List)levels.get(i);
System.out.print("Level (" + i + "):"/*#Frozen*/);
for(int j = 0; j < currentLevel.size(); j++)
{
CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(j);
System.out.print(NumberFormat.getNumberInstance().format(sourceWrapper.getGridPosition()) + " - "/*#Frozen*/);
}
System.out.println();
}
}
/** Debugdisplay for the priorities on the System out
*/
protected void displayPriorities(List levels)
{
System.out.println("----------------down Priorities"/*#Frozen*/);
for(int i = 0; i < levels.size() - 1; i++)
{
// Get the current level
List currentLevel = (List)levels.get(i);
System.out.print("Level (" + i + "):"/*#Frozen*/);
for(int j = 0; j < currentLevel.size(); j++)
{
CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(j);
System.out.print(sourceWrapper.getPriority() + /*" (" +
sourceWrapper.nearestDownNeighborLevel + ") " +*/
" - "/*#Frozen*/);
}
System.out.println();
}
}
/** 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 List named roots
*
* @return returns a List with the roots
* @see #searchRoots(JGraph, CellView[])
*/
protected List searchRoots(JGraph jgraph, CellView[] selectedCellViews)
{
// get all cells and relations
List vertexViews = new ArrayList(selectedCellViews.length);
List roots = new ArrayList();
// first: mark all as not visited
// O(allCells&Edges)
for(int i = 0; i < selectedCellViews.length; i++)
{
if(selectedCellViews[i] instanceof VertexView)
{
VertexView vertexView = (VertexView)selectedCellViews[i];
vertexView.getAttributes().remove(SUGIYAMA_VISITED);
vertexViews.add(selectedCellViews[i]);
}
}
// O(graphCells)
for(int i = 0; i < vertexViews.size(); i++)
{
VertexView vertexView = (VertexView)vertexViews.get(i);
if(vertexView.getAttributes().get(SUGIYAMA_VISITED) == null)
{
searchRoots(jgraph, vertexView, roots);
}
}
// Error Msg if the graph has no roots
if(roots.size() == 0)
{
JOptionPane.showMessageDialog(null, "The Graph is not a DAG. Can't use Sugiyama Algorithm!"/*#Finished:Original="The Graph is not a DAG. Can't use Sugiyama Algorithm!"*/, null, JOptionPane.ERROR_MESSAGE);
}
return roots;
}
/** Searches Roots for the current Cell.
*
* Therefore he looks at all Ports from the Cell.
* At the Ports he looks for Edges.
* At the Edges he looks for the Target.
* If the Ports of the current Cell contains the target ReViewNodePort
* he follows the edge to the source and looks at the
* Cell for this source.
*
*/
protected void searchRoots(JGraph jgraph, VertexView vertexViewToInspect, List roots)
{
// the node already visited
if(vertexViewToInspect.getAttributes().get(SUGIYAMA_VISITED) != null)
{
return;
}
// mark as visited for cycle tests
vertexViewToInspect.getAttributes().put(SUGIYAMA_VISITED, new Boolean(true));
GraphModel model = jgraph.getModel();
// get all Ports and search the relations at the ports
//List vertexPortViewList = new ArrayList() ;
Object vertex = vertexViewToInspect.getCell();
int portCount = model.getChildCount(vertex);
for(int j = 0; j < portCount; j++)
{
Object port = model.getChild(vertex, j);
// Test all relations for where
// the current node is a target node
// for roots
boolean isRoot = true;
Iterator itrEdges = model.edges(port);
while(itrEdges.hasNext())
{
Object edge = itrEdges.next();
// if the current node is a target node
// get the source node and test
// the source node for roots
if(model.getTarget(edge) == port)
{
Object sourcePort = model.getSource(edge);
Object sourceVertex = model.getParent(sourcePort);
CellView sourceVertexView = jgraph.getGraphLayoutCache().getMapping(sourceVertex, false);
if(sourceVertexView instanceof VertexView)
{
searchRoots(jgraph, (VertexView)sourceVertexView, roots);
isRoot = false;
}
}
}
// The current node is never a Target Node
// -> The current node is a root node
if(isRoot)
{
roots.add(vertexViewToInspect);
}
}
}
/** Method fills the levels and stores them in the member levels.
* Each level was represended by a List with Cell Wrapper objects.
* These Lists are the elements in the <code>levels</code> List.
*
*/
protected List fillLevels(JGraph jgraph, CellView[] selectedCellViews, List rootVertexViews)
{
List levels = new ArrayList();
// mark as not visited
// O(allCells)
for(int i = 0; i < selectedCellViews.length; i++)
{
CellView cellView = selectedCellViews[i];
cellView.getAttributes().remove(SUGIYAMA_VISITED);
}
Iterator roots = rootVertexViews.iterator();
while(roots.hasNext())
{
VertexView vertexView = (VertexView)roots.next();
fillLevels(jgraph, levels, 0, vertexView);
}
return levels;
}
/** Fills the List for the specified level with a wrapper
* for the MyGraphCell. After that the method called for
* each neighbor graph cell.
*
* @param level The level for the graphCell
*/
protected void fillLevels(JGraph jgraph, List levels, int level, VertexView vertexView)
{
// be sure that a List container exists for the current level
if(levels.size() == level)
levels.add(level, new ArrayList());
// if the cell already visited return
if(vertexView.getAttributes().get(SUGIYAMA_VISITED) != null)
{
return;
}
// mark as visited for cycle tests
vertexView.getAttributes().put(SUGIYAMA_VISITED, new Boolean(true));
// put the current node into the current level
// get the Level List
List vecForTheCurrentLevel = (List)levels.get(level);
// Create a wrapper for the node
int numberForTheEntry = vecForTheCurrentLevel.size();
CellWrapper wrapper = new CellWrapper(level, numberForTheEntry, vertexView);
// put the Wrapper in the LevelList
vecForTheCurrentLevel.add(wrapper);
// concat the wrapper to the cell for an easy access
vertexView.getAttributes().put(SUGIYAMA_CELL_WRAPPER, wrapper);
// if the Cell has no Ports we can return, there are no relations
Object vertex = vertexView.getCell();
GraphModel model = jgraph.getModel();
int portCount = model.getChildCount(vertex);
// iterate any NodePort
for(int i = 0; i < portCount; i++)
{
Object port = model.getChild(vertex, i);
// iterate any Edge in the port
Iterator itrEdges = model.edges(port);
while(itrEdges.hasNext())
{
Object edge = itrEdges.next();
// if the Edge is a forward edge we should follow this edge
if(port == model.getSource(edge))
{
Object targetPort = model.getTarget(edge);
Object targetVertex = model.getParent(targetPort);
VertexView targetVertexView = (VertexView)jgraph.getGraphLayoutCache().getMapping(targetVertex, false);
fillLevels(jgraph, levels, (level + 1), targetVertexView);
}
}
}
if(vecForTheCurrentLevel.size() > gridAreaSize)
{
gridAreaSize = vecForTheCurrentLevel.size();
}
}
/** calculates the minimum for the paint area.
*
*/
protected Point findMinimumAndSpacing(CellView[] graphCellViews, Point spacing)
{
try
{
// variables
/* represents the minimum x value for the paint area
*/
int min_x = 1000000;
/* represents the minimum y value for the paint area
*/
int min_y = 1000000;
// find the maximum & minimum coordinates
for(int i = 0; i < graphCellViews.length; i++)
{
// the cellView and their bounds
CellView cellView = graphCellViews[i];
Rectangle cellViewBounds = cellView.getBounds().getBounds();
// checking min area
try
{
if(cellViewBounds.x < min_x)
min_x = cellViewBounds.x;
if(cellViewBounds.y < min_y)
min_y = cellViewBounds.y;
/*
if (cellViewBounds.width > spacing.x)
spacing.x = cellViewBounds.width;
if (cellViewBounds.height > spacing.y)
spacing.y = cellViewBounds.height;
*/
}
catch(Exception e)
{
System.err.println("---------> ERROR in calculateValues."/*#Frozen*/);
e.printStackTrace();
}
}
// if the cell sice is bigger than the userspacing
// dublicate the spacingfactor
return new Point(min_x, min_y);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
/** Updates the progress based on the movements count
*
*/
protected void updateProgress4Movements()
{
// adds the current loop count
movements.add(new Integer(movementsCurrentLoop));
iteration++;
// if the current loop count is higher than the max movements count
// memorize the new max
if(movementsCurrentLoop > movementsMax)
{
movementsMax = movementsCurrentLoop;
}
}
protected void solveEdgeCrosses(JGraph jgraph, List levels)
{
movements = new ArrayList(100);
movementsCurrentLoop = -1;
movementsMax = Integer.MIN_VALUE;
iteration = 0;
while(movementsCurrentLoop != 0)
{
// reset the movements per loop count
movementsCurrentLoop = 0;
if(verbose)
{
System.out.println("---------------------------- vor Sort"/*#Frozen*/);
displayEdgeCrossesValues(levels);
}
// top down
for(int i = 0; i < levels.size() - 1; i++)
{
movementsCurrentLoop += solveEdgeCrosses(jgraph, true, levels, i);
}
// bottom up
for(int i = levels.size() - 1; i >= 1; i--)
{
movementsCurrentLoop += solveEdgeCrosses(jgraph, false, levels, i);
}
if(verbose)
{
System.out.println("---------------------------- nach Sort"/*#Frozen*/);
displayEdgeCrossesValues(levels);
}
updateProgress4Movements();
}
}
/**
* @return movements
*/
protected int solveEdgeCrosses(JGraph jgraph, boolean down, List levels, int levelIndex)
{
// Get the current level
List currentLevel = (List)levels.get(levelIndex);
int movements = 0;
// restore the old sort
Object[] levelSortBefore = currentLevel.toArray();
// new sort
Collections.sort(currentLevel);
// test for movements
for(int j = 0; j < levelSortBefore.length; j++)
{
if(((CellWrapper)levelSortBefore[j]).getEdgeCrossesIndicator() != ((CellWrapper)currentLevel.get(j)).getEdgeCrossesIndicator())
{
movements++;
}
}
GraphModel model = jgraph.getModel();
// Collecations Sort sorts the highest value to the first value
for(int j = currentLevel.size() - 1; j >= 0; j--)
{
CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(j);
VertexView sourceView = sourceWrapper.getVertexView();
Object sourceVertex = sourceView.getCell();
int sourcePortCount = model.getChildCount(sourceVertex);
for(int k = 0; k < sourcePortCount; k++)
{
Object sourcePort = model.getChild(sourceVertex, k);
Iterator sourceEdges = model.edges(sourcePort);
while(sourceEdges.hasNext())
{
Object edge = sourceEdges.next();
// if it is a forward edge follow it
Object targetPort = null;
if(down && sourcePort == model.getSource(edge))
{
targetPort = model.getTarget(edge);
}
if(!down && sourcePort == model.getTarget(edge))
{
targetPort = model.getSource(edge);
}
if(targetPort == null)
continue;
Object targetCell = model.getParent(targetPort);
VertexView targetVertexView = (VertexView)jgraph.getGraphLayoutCache().getMapping(targetCell, false);
CellWrapper targetWrapper = (CellWrapper)targetVertexView.getAttributes().get(SUGIYAMA_CELL_WRAPPER);
// 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;
}
protected void moveToBarycenter(JGraph jgraph, CellView[] allSelectedViews, List levels)
{
//================================================================
// iterate any ReViewNodePort
GraphModel model = jgraph.getModel();
for(int i = 0; i < allSelectedViews.length; i++)
{
if(!(allSelectedViews[i] instanceof VertexView))
continue;
VertexView vertexView = (VertexView)allSelectedViews[i];
CellWrapper currentwrapper = (CellWrapper)vertexView.getAttributes().get(SUGIYAMA_CELL_WRAPPER);
Object vertex = vertexView.getCell();
int portCount = model.getChildCount(vertex);
for(int k = 0; k < portCount; k++)
{
Object port = model.getChild(vertex, k);
// iterate any Edge in the port
Iterator edges = model.edges(port);
while(edges.hasNext())
{
Object edge = edges.next();
Object neighborPort = null;
// if the Edge is a forward edge we should follow this edge
if(port == model.getSource(edge))
{
neighborPort = model.getTarget(edge);
}
else
{
if(port == model.getTarget(edge))
{
neighborPort = model.getSource(edge);
}
else
{
continue;
}
}
Object neighborVertex = model.getParent(neighborPort);
VertexView neighborVertexView = (VertexView)jgraph.getGraphLayoutCache().getMapping(neighborVertex, false);
if(neighborVertexView == vertexView)
continue;
CellWrapper neighborWrapper = (CellWrapper)neighborVertexView.getAttributes().get(SUGIYAMA_CELL_WRAPPER);
if(currentwrapper == null || neighborWrapper == null || currentwrapper.level == neighborWrapper.level)
continue;
currentwrapper.priority++;
}
}
}
//================================================================
for(int j = 0; j < levels.size(); j++)
{
List level = (List)levels.get(j);
for(int i = 0; i < level.size(); i++)
{
// calculate the initial Grid Positions 1, 2, 3, .... per Level
CellWrapper wrapper = (CellWrapper)level.get(i);
wrapper.setGridPosition(i);
}
}
if(verbose)
{
System.out.println("----------------Grid Pos before top down"/*#Frozen*/);
displayPriorities(levels);
displayGridPositions(levels);
System.out.println("======================================="/*#Frozen*/);
}
movements = new ArrayList(100);
movementsCurrentLoop = -1;
movementsMax = Integer.MIN_VALUE;
iteration = 0;
//int movements = 1;
while(movementsCurrentLoop != 0)
{
// reset movements
movementsCurrentLoop = 0;
// top down
for(int i = 1; i < levels.size(); i++)
{
movementsCurrentLoop += moveToBarycenter(jgraph, levels, i);
}
if(verbose)
{
System.out.println("----------------Grid Pos after top down"/*#Frozen*/);
displayGridPositions(levels);
System.out.println("======================================="/*#Frozen*/);
}
// bottom up
for(int i = levels.size() - 1; i >= 0; i--)
{
movementsCurrentLoop += moveToBarycenter(jgraph, levels, i);
}
if(verbose)
{
System.out.println("----------------Grid Pos after bottom up"/*#Frozen*/);
displayGridPositions(levels);
//displayDownPriorities();
System.out.println("======================================="/*#Frozen*/);
}
this.updateProgress4Movements();
}
}
protected int moveToBarycenter(JGraph jgraph, List levels, int levelIndex)
{
// Counter for the movements
int movements = 0;
// Get the current level
List currentLevel = (List)levels.get(levelIndex);
GraphModel model = jgraph.getModel();
for(int currentIndexInTheLevel = 0; currentIndexInTheLevel < currentLevel.size(); currentIndexInTheLevel++)
{
CellWrapper sourceWrapper = (CellWrapper)currentLevel.get(currentIndexInTheLevel);
float gridPositionsSum = 0;
float countNodes = 0;
VertexView vertexView = sourceWrapper.getVertexView();
Object vertex = vertexView.getCell();
int portCount = model.getChildCount(vertex);
for(int i = 0; i < portCount; i++)
{
Object port = model.getChild(vertex, i);
Iterator edges = model.edges(port);
while(edges.hasNext())
{
Object edge = edges.next();
// if it is a forward edge follow it
Object neighborPort = null;
if(port == model.getSource(edge))
{
neighborPort = model.getTarget(edge);
}
else
{
if(port == model.getTarget(edge))
{
neighborPort = model.getSource(edge);
}
else
{
continue;
}
}
Object neighborVertex = model.getParent(neighborPort);
VertexView neighborVertexView = (VertexView)jgraph.getGraphLayoutCache().getMapping(neighborVertex, false);
CellWrapper targetWrapper = (CellWrapper)neighborVertexView.getAttributes().get(SUGIYAMA_CELL_WRAPPER);
if(targetWrapper == sourceWrapper)
continue;
if(targetWrapper == null || targetWrapper.getLevel() == levelIndex)
continue;
gridPositionsSum += targetWrapper.getGridPosition();
countNodes++;
}
}
//----------------------------------------------------------
// move node to new x coord
//----------------------------------------------------------
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)
{
int tmpGridPos = sourceWrapper.getGridPosition();
moved = move(toRight, currentLevel, currentIndexInTheLevel, sourceWrapper.getPriority());
if(moved)
movements++;
if(verbose)
{
System.out.print("try move at Level " + levelIndex + " with index " + currentIndexInTheLevel + " to " + (toRight ? "Right" : "Left") + " CurrentGridPos: " + tmpGridPos + " NewGridPos: " + newGridPosition + " exact: " + NumberFormat.getInstance().format(tmp) + "..."/*#Frozen*/);
System.out.println(moved ? "success"/*#Frozen*/ : "can't move"/*#Frozen*/);
}
}
}
}
return movements;
}
/**@param toRight <tt>true</tt> = try to move the currentWrapper to right; <tt>false</tt> = try to move the currentWrapper to left;
* @param currentLevel List which contains the CellWrappers for the current level
* @param currentIndexInTheLevel
* @param currentPriority
*
* @return The free GridPosition or -1 is position is not free.
*/
protected boolean move(boolean toRight, List currentLevel, int currentIndexInTheLevel, int currentPriority)
{
CellWrapper currentWrapper = (CellWrapper)currentLevel.get(currentIndexInTheLevel);
boolean moved = false;
int neighborIndexInTheLevel = currentIndexInTheLevel + (toRight ? 1 : -1);
int newGridPosition = currentWrapper.getGridPosition() + (toRight ? 1 : -1);
// is the grid position possible?
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;
}
/** This Method draws the graph. For the horizontal position
* we are using the grid position from each graphcell.
* For the vertical position we are using the level position.
*
*/
protected void drawGraph(JGraph jgraph, List levels, Point min, Point spacing)
{
// paint the graph
Map viewMap = new HashMap();
for(int rowCellCount = 0; rowCellCount < levels.size(); rowCellCount++)
{
List level = (List)levels.get(rowCellCount);
for(int colCellCount = 0; colCellCount < level.size(); colCellCount++)
{
CellWrapper wrapper = (CellWrapper)level.get(colCellCount);
VertexView view = wrapper.vertexView;
// remove the temp objects
/* While the Algorithm is running we are putting some
* attributeNames to the MyGraphCells. This method
* cleans this objects from the MyGraphCells.
*
*/
view.getAttributes().remove(SUGIYAMA_CELL_WRAPPER);
view.getAttributes().remove(SUGIYAMA_VISITED);
wrapper.vertexView = null;
// get the bounds from the cellView
Rectangle bounds = (Rectangle)view.getBounds().clone();
// adjust
bounds.x = min.x + spacing.x * wrapper.getGridPosition();
bounds.y = min.y + spacing.y * rowCellCount;
Object cell = view.getCell();
Map map = new AttributeMap();
GraphConstants.setBounds(map, bounds);
viewMap.put(cell, map);
}
}
jgraph.getGraphLayoutCache().edit(viewMap, null, null, null);
}
/** 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
*/
int level = 0;
/** current position in the grid
*/
int gridPosition = 0;
/** priority for movements to the barycenter
*/
int priority = 0;
/** reference to the wrapped cell
*/
VertexView vertexView = null;
/** creates an instance and memorizes the parameters
*
*/
CellWrapper(int level, double edgeCrossesIndicator, VertexView vertexView)
{
this.level = level;
this.edgeCrossesIndicator = edgeCrossesIndicator;
this.vertexView = vertexView;
additions++;
}
/** returns the wrapped cell
*/
VertexView getVertexView()
{
return vertexView;
}
/** resets the indicator for edge crosses to 0
*/
void resetEdgeCrossesIndicator()
{
edgeCrossesIndicator = 0;
additions = 0;
}
/** 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;
}
/** increments the the priority of this cell wrapper.
*
* The priority was used by moving the cell to its
* barycenter.
*
*/
void incrementPriority()
{
priority++;
}
/** 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(Object compare)
{
if(((CellWrapper)compare).getEdgeCrossesIndicator() == this.getEdgeCrossesIndicator())
return 0;
double compareValue = (((CellWrapper)compare).getEdgeCrossesIndicator() - this.getEdgeCrossesIndicator());
return (int)(compareValue * 1000);
}
}
}
|