/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* HierarchicalBCEngine.java
* Copyright (C) 2003 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui.graphvisualizer;
import weka.core.FastVector;
import weka.gui.graphvisualizer.GraphNode;
import weka.gui.graphvisualizer.GraphEdge;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JCheckBox;
import javax.swing.ButtonGroup;
import javax.swing.BorderFactory;
import javax.swing.JProgressBar;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
/**
* This class lays out the vertices of a graph in a
* hierarchy of vertical levels, with a number of nodes
* in each level. The number of levels is the depth of
* the deepest child reachable from some parent at level
* 0. It implements a layout technique as described by
* K. Sugiyama, S. Tagawa, and M. Toda. in "Methods for
* visual understanding of hierarchical systems", IEEE
* Transactions on Systems, Man and Cybernetics,
* SMC-11(2):109-125, Feb. 1981.
* <p>There have been a few modifications made, however.
* The crossings function is changed as it was non-linear
* in time complexity. Furthermore, we don't have any
* interconnection matrices for each level, instead we
* just have one big interconnection matrix for the
* whole graph and a int[][] array which stores the
* vertices present in each level.
*
* @author Ashraf M. Kibriya (amk14@cs.waikato.ac.nz)
* @version $Revision: 1.6 $ - 24 Apr 2003 - Initial version (Ashraf M. Kibriya)
*
*/
public class HierarchicalBCEngine implements GraphConstants, LayoutEngine {
/** FastVector containing nodes and edges */
protected FastVector m_nodes, m_edges;
/** FastVector containing listeners for
* layoutCompleteEvent generated by this
* LayoutEngine */
protected FastVector layoutCompleteListeners;
/** Interconnection matrix for the graph */
protected int graphMatrix[][];
/** Array containing the indices of nodes in each level.
* The nodeLevels.length is equal to the number of levels */
protected int nodeLevels[][];
/** The nodeWidth and nodeHeight */
protected int m_nodeWidth, m_nodeHeight;
/* The following radio buttons control the
way the layout is performed. If any
of the following is changed then we
need to perform a complete relayout
*/
protected JRadioButton m_jRbNaiveLayout;
protected JRadioButton m_jRbPriorityLayout;
protected JRadioButton m_jRbTopdown;
protected JRadioButton m_jRbBottomup;
/** controls edge concentration by concentrating multilple singular dummy
* child nodes into one plural dummy child node
*/
protected JCheckBox m_jCbEdgeConcentration;
/** The panel containing extra options,
* specific to this LayoutEngine, for
* greater control over layout of the graph */
protected JPanel m_controlsPanel;
/** The progress bar to show the progress
* of the layout process */
protected JProgressBar m_progress;
/** This tells the the LayoutGraph method if
* a completeReLayout should be performed
* when it is called. */
protected boolean m_completeReLayout=false;
/** This contains the original size of the nodes vector
* when it was passed in through the constructor, before
* adding all the dummy vertices */
private int origNodesSize;
/** Constructor - takes in FastVectors of nodes and edges, and the initial
* width and height of a node
*/
public HierarchicalBCEngine(FastVector nodes, FastVector edges, int nodeWidth,
int nodeHeight) {
m_nodes = nodes; m_edges = edges; m_nodeWidth = nodeWidth;
m_nodeHeight = nodeHeight;
makeGUIPanel(false);
}
/** Constructor - takes in FastVectors of nodes and edges, the initial width
* and height of a node, and a boolean value to indicate if the edges
* should be concentrated.
* @param nodes - FastVector containing all the nodes
* @param edges - FastVector containing all the edges
* @param nodeWidth - A node's allowed width
* @param nodeHeight - A node's allowed height
* @param edgeConcentration - True: if want to concentrate edges,
* False: otherwise
*/
public HierarchicalBCEngine(FastVector nodes, FastVector edges,
int nodeWidth, int nodeHeight, boolean edgeConcentration) {
m_nodes = nodes; m_edges = edges; m_nodeWidth = nodeWidth;
m_nodeHeight = nodeHeight;
makeGUIPanel(edgeConcentration);
}
/** SimpleConstructor
* If we want to instantiate the class first, and if information for
* nodes and edges is not available. However, we would have to manually
* provide all the information later on by calling setNodesEdges and
* setNodeSize methods
*/
public HierarchicalBCEngine() { }
/**
* This methods makes the gui extra controls panel "m_controlsPanel"
*/
protected void makeGUIPanel(boolean edgeConc) {
m_jRbNaiveLayout = new JRadioButton("Naive Layout");
m_jRbPriorityLayout = new JRadioButton("Priority Layout");
ButtonGroup bg = new ButtonGroup();
bg.add(m_jRbNaiveLayout);
bg.add(m_jRbPriorityLayout);
m_jRbPriorityLayout.setSelected(true);
ActionListener a = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
m_completeReLayout=true;
}
};
m_jRbTopdown = new JRadioButton("Top Down");
m_jRbBottomup = new JRadioButton("Bottom Up");
m_jRbTopdown.addActionListener(a);
m_jRbBottomup.addActionListener(a);
bg = new ButtonGroup();
bg.add(m_jRbTopdown);
bg.add(m_jRbBottomup);
m_jRbBottomup.setSelected(true);
m_jCbEdgeConcentration = new JCheckBox("With Edge Concentration", edgeConc);
m_jCbEdgeConcentration.setSelected(edgeConc);
m_jCbEdgeConcentration.addActionListener(a);
JPanel jp1 = new JPanel( new GridBagLayout() );
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = gbc.REMAINDER;
gbc.anchor = gbc.NORTHWEST;
gbc.weightx = 1;
gbc.fill = gbc.HORIZONTAL;
jp1.add(m_jRbNaiveLayout, gbc);
jp1.add(m_jRbPriorityLayout, gbc);
jp1.setBorder( BorderFactory.createTitledBorder("Layout Type") );
JPanel jp2 = new JPanel( new GridBagLayout() );
jp2.add(m_jRbTopdown, gbc);
jp2.add(m_jRbBottomup, gbc);
jp2.setBorder( BorderFactory.createTitledBorder("Layout Method") );
m_progress = new JProgressBar(0, 11);
m_progress.setBorderPainted(false);
m_progress.setStringPainted(true);
m_progress.setString("");
m_progress.setValue(0);
m_controlsPanel = new JPanel( new GridBagLayout() );
m_controlsPanel.add(jp1, gbc);
m_controlsPanel.add(jp2, gbc);
m_controlsPanel.add(m_jCbEdgeConcentration, gbc);
}
/** give access to set of graph nodes */
public FastVector getNodes() {
return m_nodes;
}
/** This method returns a handle to the extra
* controls panel, so that the visualizing
* class can add it to some of it's own
* gui panel.
*/
public JPanel getControlPanel() {
return m_controlsPanel;
}
/** Returns a handle to the progressBar
* of this LayoutEngine.
*/
public JProgressBar getProgressBar() {
return m_progress;
}
/** Sets the nodes and edges for this LayoutEngine.
* Must be used if the class created by simple
* HierarchicalBCEngine() constructor.
* @param nodes - FastVector containing all the nodes
* @param edges - FastVector containing all the edges
*/
public void setNodesEdges(FastVector nodes, FastVector edges) {
m_nodes = nodes; m_edges = edges;
}
/**
* Sets the size of a node. This method must be
* used if the class created by simple
* HierarchicalBCEngine() constructor.
* @param nodeWidth - A node's allowed width
* @param nodeHeight - A node's allowed height
*/
public void setNodeSize(int nodeWidth, int nodeHeight) {
m_nodeWidth = nodeWidth;
m_nodeHeight = nodeHeight;
}
/**
* Method to add a LayoutCompleteEventListener
* @param l - Listener to receive the LayoutCompleteEvent by this
* class.
*/
public void addLayoutCompleteEventListener(LayoutCompleteEventListener l) {
if(layoutCompleteListeners==null)
layoutCompleteListeners = new FastVector();
layoutCompleteListeners.addElement(l);
}
/**
* Method to remove a LayoutCompleteEventListener.
* @param e - The LayoutCompleteEventListener to remove.
*/
public void removeLayoutCompleteEventListener(LayoutCompleteEventListener e){
if(layoutCompleteListeners!=null) {
LayoutCompleteEventListener l;
for(int i=0; i<layoutCompleteListeners.size(); i++) {
l = (LayoutCompleteEventListener) layoutCompleteListeners.elementAt(i);
if(l==e) {
layoutCompleteListeners.removeElementAt(i);
return;
}
}
System.err.println("layoutCompleteListener to be remove not present");
}
else
System.err.println("layoutCompleteListener to be remove not present");
}
/**
* Fires a LayoutCompleteEvent.
* @param e - The LayoutCompleteEvent to fire
*/
public void fireLayoutCompleteEvent(LayoutCompleteEvent e) {
if(layoutCompleteListeners!=null && layoutCompleteListeners.size()!=0) {
LayoutCompleteEventListener l;
for(int i=0; i<layoutCompleteListeners.size(); i++) {
l = (LayoutCompleteEventListener) layoutCompleteListeners.elementAt(i);
l.layoutCompleted(e);
}
}
}
/**
* This method does a complete layout of the graph which includes
* removing cycles, assigning levels to nodes, reducing edge crossings
* and laying out the vertices horizontally for better visibility.
* The removing of cycles and assignment of levels is only performed
* if hasn't been performed earlier or if some layout option has been
* changed and it is necessary to do so. It is necessary to do so,
* if the user selects/deselects edge concentration or topdown/bottomup
* options.
* <p> The layout is performed in a separate thread and the progress
* bar of the class is updated for each of the steps as the process
* continues.
*/
public void layoutGraph() {
//cannot perform a layout if no description of nodes and/or edges is provided
if(m_nodes==null || m_edges==null)
return;
Thread th = new Thread() {
public void run() {
m_progress.setBorderPainted(true);
if(nodeLevels==null) {
makeProperHierarchy();
}
else if(m_completeReLayout==true) {
clearTemps_and_EdgesFromNodes(); makeProperHierarchy();
m_completeReLayout=false;
}
//minimizing crossings
if(m_jRbTopdown.isSelected()) {
int crossbefore=crossings(nodeLevels), crossafter=0, i=0;
do {
m_progress.setValue(i+4);
m_progress.setString("Minimizing Crossings: Pass"+(i+1));
if(i!=0)
crossbefore = crossafter;
nodeLevels = minimizeCrossings(false, nodeLevels);
crossafter = crossings(nodeLevels);
i++;
}
while(crossafter<crossbefore && i<6);
}
else {
int crossbefore=crossings(nodeLevels), crossafter=0, i=0;
do {
m_progress.setValue(i+4);
m_progress.setString("Minimizing Crossings: Pass"+(i+1));
if(i!=0)
crossbefore = crossafter;
nodeLevels = minimizeCrossings(true, nodeLevels);
crossafter = crossings(nodeLevels);
i++;
}
while(crossafter<crossbefore && i<6);
}
//System.out.println("\nCrossings at the end "+
// crossings(nodeLevels)+
// "\n---------------------------------");
m_progress.setValue(10);
m_progress.setString("Laying out vertices");
//Laying out graph
if(m_jRbNaiveLayout.isSelected())
naiveLayout();
else
priorityLayout1();
m_progress.setValue(11);
m_progress.setString("Layout Complete");
m_progress.repaint();
fireLayoutCompleteEvent( new LayoutCompleteEvent(this) );
m_progress.setValue(0);
m_progress.setString("");
m_progress.setBorderPainted(false);
}
};
th.start();
}
/**
* This method removes the temporary nodes that were
* added to fill in the gaps, and removes all edges
* from all nodes in their edges[][] array
*/
protected void clearTemps_and_EdgesFromNodes() {
/*
System.out.println("Before.............");
for(int i=0; i<m_nodes.size(); i++) {
GraphNode n = (GraphNode)m_nodes.elementAt(i);
System.out.println("N: "+n.ID+","+i);
}
System.out.println("origNodesSize: "+origNodesSize);
*/
int curSize = m_nodes.size();
for(int i=origNodesSize; i<curSize; i++)
m_nodes.removeElementAt(origNodesSize);
for(int j=0; j<m_nodes.size(); j++)
((GraphNode)m_nodes.elementAt(j)).edges = null;
nodeLevels=null;
/*
System.out.println("After..............");
for(int i=0; i<m_nodes.size(); i++) {
GraphNode n = (GraphNode)m_nodes.elementAt(i);
System.out.println("N: "+n.ID+","+i);
}
*/
}
/**
* This method makes the "graphMatrix" interconnection
* matrix for the graph given by m_nodes and m_edges
* vectors. The matix is used by other methods.
*/
protected void processGraph() {
origNodesSize = m_nodes.size();
graphMatrix = new int[m_nodes.size()][m_nodes.size()];
/*
System.out.println("The "+m_nodes.size()+" nodes are: ");
for(int i=0; i<m_nodes.size(); i++)
System.out.println((String)m_nodes.elementAt(i)+" ");
System.out.println("\nThe "+m_edges.size()+" edges are: ");
for(int i=0; i<m_edges.size(); i++)
System.out.println((GraphEdge)m_edges.elementAt(i));
*/
for(int i=0; i<m_edges.size(); i++)
graphMatrix[((GraphEdge)m_edges.elementAt(i)).src]
[((GraphEdge)m_edges.elementAt(i)).dest] =
((GraphEdge)m_edges.elementAt(i)).type;
/*
System.out.print("\t");
for(int i=0; i<graphMatrix.length; i++)
System.out.print(((GraphNode)m_nodes.elementAt(i)).ID+" ");
System.out.println("");
for(int i=0; i<graphMatrix.length; i++) {
GraphNode n = (GraphNode)m_nodes.elementAt(i);
System.out.print(n.ID+"\t");
for(int j=0; j<graphMatrix[i].length; j++)
System.out.print(graphMatrix[i][j]+" ");
System.out.println("");
}
*/
}
/*
* This method makes a proper hierarchy of the graph
* by first removing cycles, then assigning levels
* to the nodes and then removing gaps by adding
* dummy vertices.
*/
protected void makeProperHierarchy() {
processGraph();
m_progress.setValue(1);
m_progress.setString("Removing Cycles");
//****removing cycles
removeCycles();
m_progress.setValue(2);
m_progress.setString("Assigning levels to nodes");
//****Assigning vertical level to each node
int nodesLevel[] = new int[m_nodes.size()];
int depth=0;
for(int i=0; i<graphMatrix.length; i++) {
assignLevels(nodesLevel, depth, i, 0);
}
for(int i=0; i<nodesLevel.length; i++) {
if(nodesLevel[i]==0) {
int min=65536;
for(int j=0; j<graphMatrix[i].length; j++) {
if(graphMatrix[i][j]==DIRECTED)
if(min>nodesLevel[j])
min = nodesLevel[j];
}
//if the shallowest child of a parent has a depth greater than 1
// and it is not a lone parent with no children
if(min!=65536 && min>1)
nodesLevel[i]=min-1;
}
}
//System.out.println("");
int maxLevel=0;
for(int i=0; i<nodesLevel.length; i++) {
if(nodesLevel[i]>maxLevel)
maxLevel = nodesLevel[i];
//System.out.println( ((GraphNode)m_nodes.elementAt(i)).ID+" "+i+">"+
// nodesLevel[i]);
}
int levelCounts[] = new int[maxLevel+1];
for(int i=0; i<nodesLevel.length; i++) {
levelCounts[nodesLevel[i]]++;
}
//System.out.println("------------------------------------------");
//****Assigning nodes to each level
int levelsCounter[] = new int[maxLevel+1];
nodeLevels = new int[maxLevel+1][];
for(byte i=0; i<nodesLevel.length; i++) {
if(nodeLevels[nodesLevel[i]]==null)
nodeLevels[nodesLevel[i]] = new int[ levelCounts[nodesLevel[i]] ];
//nodeLevels[nodesLevel[i]].addElement(new Integer(i));
//System.out.println(((GraphNode)m_nodes.elementAt(i)).ID+" "+
// nodesLevel[i]+">"+levelCounts[nodesLevel[i]]);
nodeLevels[nodesLevel[i]][levelsCounter[nodesLevel[i]]++] = i;
}
m_progress.setValue(3);
m_progress.setString("Removing gaps by adding dummy vertices");
//*Making a proper Hierarchy by putting in dummy vertices to close all gaps
if(m_jCbEdgeConcentration.isSelected())
removeGapsWithEdgeConcentration(nodesLevel);
else
removeGaps(nodesLevel);
//After assigning levels and adding dummy vertices
//System.out.print("\n\t");
//for(int i=0; i<graphMatrix.length; i++)
// System.out.print(((GraphNode)m_nodes.elementAt(i)).ID+" ");
//System.out.println("");
//for(int i=0; i<graphMatrix.length; i++) {
// System.out.print(((GraphNode)m_nodes.elementAt(i)).ID+"\t");
// for(int j=0; j<graphMatrix[i].length; j++)
// System.out.print(graphMatrix[i][j]+" ");
// System.out.println("");
//}
//****Updating edges[][] array of nodes from
//****the interconnection matrix, which will be used
//****by the Visualizer class to draw edges
for(int i=0; i<graphMatrix.length; i++) {
GraphNode n = (GraphNode)m_nodes.elementAt(i);
int sum=0;
for(int j=0; j<graphMatrix[i].length; j++)
if(graphMatrix[i][j]!=0)
sum++;
n.edges = new int[sum][2];
for(int j=0, k=0; j<graphMatrix[i].length; j++)
if(graphMatrix[i][j]!=0) {
n.edges[k][0] = j;
n.edges[k][1] = graphMatrix[i][j]; k++;
}
//n.edges = graphMatrix[i];
}
//Interconnection matrices at each level, 1 to n-1
//printMatrices(nodeLevels);
}
/**
* This method removes gaps from the graph. It doesn't perform
* any concentration. It takes as an argument of int[]
* of length m_nodes.size() containing the level of each
* node.
*/
private void removeGaps(int nodesLevel[]) {
int temp = m_nodes.size();
int temp2=graphMatrix[0].length, tempCnt=1;
for(int n=0; n<temp; n++) {
for(int i=0; i<temp2; i++) {
int len = graphMatrix.length;
if(graphMatrix[n][i]>0)
if(nodesLevel[i]>nodesLevel[n]+1) {
int tempMatrix[][] =
new int[graphMatrix.length+(nodesLevel[i]-nodesLevel[n]-1)]
[graphMatrix.length+(nodesLevel[i]-nodesLevel[n]-1)];
int level = nodesLevel[n]+1;
copyMatrix(graphMatrix, tempMatrix);
String s1 = new String("S"+tempCnt++);
m_nodes.addElement(new GraphNode(s1, s1, SINGULAR_DUMMY)); //true));
int temp3 [] = new int[nodeLevels[level].length+1];
//for(int j=0; j<nodeLevels[level].length; j++)
// temp3[j] = nodeLevels[level][j];
System.arraycopy(nodeLevels[level], 0, temp3,
0, nodeLevels[level].length);
temp3[temp3.length-1] = m_nodes.size()-1;
nodeLevels[level] = temp3; level++;
//nodeLevels[level++].addElement(new Integer(m_nodes.size()-1));
//System.out.println("len:"+len+","+nodesLevel[i]+","+
// nodesLevel[n]);
int k;
for(k=len; k<len+nodesLevel[i]-nodesLevel[n]-1-1; k++) {
String s2 = new String("S"+tempCnt);
m_nodes.addElement( new GraphNode(s2, s2, SINGULAR_DUMMY) ); //true) );
temp3 = new int[nodeLevels[level].length+1];
//for(int j=0; j<nodeLevels[level].length; j++)
// temp3[j] = nodeLevels[level][j];
System.arraycopy(nodeLevels[level], 0, temp3,
0, nodeLevels[level].length);
temp3[temp3.length-1] = m_nodes.size()-1;
nodeLevels[level++] = temp3;
//nodeLevels[level++].addElement(new Integer(m_nodes.size()-1));
tempMatrix[k][k+1]=tempMatrix[n][i]; tempCnt++;
if(k>len)
tempMatrix[k][k-1]=-1*tempMatrix[n][i];
}
//temp[lastTempNodeCreated][targetNode]=temp[origNode][targetNode]
tempMatrix[k][i]=tempMatrix[n][i];
//System.out.println("k "+((GraphNode)m_nodes.elementAt(k)).ID+
// " i "+((GraphNode)m_nodes.elementAt(i)).ID+
// " n "+((GraphNode)m_nodes.elementAt(n)).ID+
// " len "+((GraphNode)m_nodes.elementAt(len)).ID );
//temp[origNode][firstTempNodecreated] = temp[origNode][targetNode]
tempMatrix[n][len] = tempMatrix[n][i];
//temp[firstTempNodeCreated][origNode] for reverse tracing
tempMatrix[len][n] = -1*tempMatrix[n][i];
//temp[targetNode][lastTempNodecreated] for reverse tracing
tempMatrix[i][k] = -1*tempMatrix[n][i];
//temp[lastTempNodeCreated][secondlastNode] for reverse tracing
//but only do this if more than 1 temp nodes are created
if(k>len)
tempMatrix[k][k-1] = -1*tempMatrix[n][i];
//temp[origNode][targetNode] = 0 unlinking as they have been
// linked by a chain of temporary nodes now.
tempMatrix[n][i] = 0;
tempMatrix[i][n] = 0;
graphMatrix = tempMatrix;
}
else {
//****Even if there is no gap just add a reference for the
//****parent to the child for reverse tracing, useful if the
//****there is a reversed edge from parent to child and therefore
//****visualizer would know to highlight this edge when
//****highlighting the child.
graphMatrix[i][n]=-1*graphMatrix[n][i];
}
}
}
//Interconnection matrices at each level, 1 to n-1 after minimizing edges
//printMatrices(nodeLevels);
}
/**
* This method removes gaps from the graph. It tries to minimise the number
* of edges by concentrating multiple dummy nodes from the same parent and
* on the same vertical level into one. It takes as an argument of int[]
* of length m_nodes.size() containing the level of each
* node.
*/
private void removeGapsWithEdgeConcentration(int nodesLevel[]) {
final int temp = m_nodes.size(), temp2=graphMatrix[0].length;
int tempCnt=1;
for(int n=0; n<temp; n++) {
for(int i=0; i<temp2; i++) {
if(graphMatrix[n][i]>0)
if(nodesLevel[i]>nodesLevel[n]+1) {
//System.out.println("Processing node "+
// ((GraphNode)m_nodes.elementAt(n)).ID+
// " for "+((GraphNode)m_nodes.elementAt(i)).ID);
int tempLevel=nodesLevel[n];
boolean tempNodePresent=false;
int k=temp;
int tempnode = n;
while(tempLevel < nodesLevel[i]-1 ) {
tempNodePresent=false;
for(; k<graphMatrix.length; k++) {
if(graphMatrix[tempnode][k]>0) {
//System.out.println("tempnode will be true");
tempNodePresent = true; break;
}
}
if(tempNodePresent) {
tempnode=k; k=k+1;
tempLevel++;
}
else {
if(tempnode!=n)
tempnode=k-1;
//System.out.println("breaking from loop");
break;
}
}
if(((GraphNode)m_nodes.elementAt(tempnode)).nodeType==SINGULAR_DUMMY)
((GraphNode)m_nodes.elementAt(tempnode)).nodeType=PLURAL_DUMMY;
if(tempNodePresent) {
//Link the last known temp node to target
graphMatrix[tempnode][i] = graphMatrix[n][i];
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(tempnode)).ID+
// ", "+((GraphNode)nodes.elementAt(n)).ID);
/////matrix[lastknowntempnode][source]=-original_val
/////graphMatrix[tempnode][n] = -graphMatrix[n][i];
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(i)).ID+
// ", "+
// ((GraphNode)nodes.elementAt(tempnode)).ID);
//and matrix[target][lastknowntempnode]=-original_val
//for reverse tracing
graphMatrix[i][tempnode] = -graphMatrix[n][i];
//unlink source from the target
graphMatrix[n][i] = 0;
graphMatrix[i][n] = 0;
continue;
}
int len = graphMatrix.length;
int tempMatrix[][] =
new int[graphMatrix.length+(nodesLevel[i]-nodesLevel[tempnode]-1)]
[graphMatrix.length+(nodesLevel[i]-nodesLevel[tempnode]-1)];
int level = nodesLevel[tempnode]+1;
copyMatrix(graphMatrix, tempMatrix);
String s1 = new String("S"+tempCnt++);
//System.out.println("Adding dummy "+s1);
m_nodes.addElement(new GraphNode(s1, s1, SINGULAR_DUMMY));
int temp3 [] = new int[nodeLevels[level].length+1];
System.arraycopy(nodeLevels[level], 0, temp3,
0, nodeLevels[level].length);
temp3[temp3.length-1] = m_nodes.size()-1;
nodeLevels[level] = temp3;
temp3 = new int[m_nodes.size()+1];
System.arraycopy(nodesLevel, 0, temp3, 0, nodesLevel.length);
temp3[m_nodes.size()-1] = level;
nodesLevel = temp3;
level++;
//nodeLevels[level++].addElement(new Integer(m_nodes.size()-1));
//System.out.println("len:"+len+"("+
// ((GraphNode)m_nodes.elementAt(len)).ID+"),"+
// nodesLevel[i]+","+nodesLevel[tempnode]);
int m;
for(m=len; m<len+nodesLevel[i]-nodesLevel[tempnode]-1-1; m++) {
String s2 = new String("S"+tempCnt++);
//System.out.println("Adding dummy "+s2);
m_nodes.addElement( new GraphNode(s2, s2, SINGULAR_DUMMY) );
temp3 = new int[nodeLevels[level].length+1];
//for(int j=0; j<nodeLevels[level].length; j++)
// temp3[j] = nodeLevels[level][j];
System.arraycopy(nodeLevels[level], 0, temp3,
0, nodeLevels[level].length);
temp3[temp3.length-1] = m_nodes.size()-1;
nodeLevels[level] = temp3;
temp3 = new int[m_nodes.size()+1];
System.arraycopy(nodesLevel, 0, temp3, 0, nodesLevel.length);
temp3[m_nodes.size()-1] = level;
nodesLevel = temp3;
level++;
//nodeLevels[level++].addElement(new Integer(m_nodes.size()-1));
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(m)).ID+", "+
// ((GraphNode)nodes.elementAt(m+1)).ID);
tempMatrix[m][m+1]=tempMatrix[n][i]; //tempCnt++;
if(m>len) {
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(m)).ID+
// ", "+((GraphNode)nodes.elementAt(m-1)).ID);
tempMatrix[m][m-1]=-1*tempMatrix[n][i];
}
}
//System.out.println("m "+((GraphNode)m_nodes.elementAt(m)).ID+
// " i "+((GraphNode)m_nodes.elementAt(i)).ID+
// " tempnode "+((GraphNode)m_nodes.elementAt(tempnode)).ID+
// " len "+((GraphNode)m_nodes.elementAt(len)).ID );
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(m)).ID+", "+
// ((GraphNode)nodes.elementAt(i)).ID);
//temp[lastTempNodeCreated][targetNode]=temp[origNode][targetNode]
tempMatrix[m][i]=tempMatrix[n][i];
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(tempnode)).ID+", "+
// ((GraphNode)nodes.elementAt(len)).ID);
//temp[origNode][firstTempNodecreated] = temp[origNode][targetNode]
tempMatrix[tempnode][len] = tempMatrix[n][i];
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(len)).ID+", "+
// ((GraphNode)nodes.elementAt(tempnode)).ID);
//temp[firstTempNodeCreated][origNode] for reverse tracing
tempMatrix[len][tempnode] = -1*tempMatrix[n][i];
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(i)).ID+", "+
// ((GraphNode)nodes.elementAt(m)).ID);
//temp[targetNode][lastTempNodecreated] for reverse tracing
tempMatrix[i][m] = -1*tempMatrix[n][i];
if(m>len) {
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(m)).ID+
// ", "+((GraphNode)nodes.elementAt(m-1)).ID);
//temp[lastTempNodeCreated][secondlastNode] for reverse tracing
//but only do this if more than 1 temp nodes are created
tempMatrix[m][m-1] = -1*tempMatrix[n][i];
}
//temp[origNode][targetNode] = 0 unlinking as they have been
tempMatrix[n][i] = 0;
//linked by a chain of temporary nodes now.
tempMatrix[i][n] = 0;
graphMatrix = tempMatrix;
}
else {
//System.out.println("modifying "+
// ((GraphNode)nodes.elementAt(i)).ID+", "+
// ((GraphNode)nodes.elementAt(n)).ID);
//****Even if there is no gap just add a reference for the
//****parent to the child for reverse tracing, useful if the
//****there is a reversed edge from parent to child and therefore
//****visualizer would know to highlight this edge when
//****highlighting the child.
graphMatrix[i][n]=-1*graphMatrix[n][i];
}
}
}
}
/**
* Returns the index of an element in a level.
* Must never be called with the wrong element and
* the wrong level, will throw an exception otherwise.
* It takes as agrument the index of the element (in the m_nodes vector)
* and the level it is supposed to be in (as each level contains the indices
* of the nodes present in that level).
*/
private int indexOfElementInLevel(int element, int level[]) throws Exception {
int idx;
for(int i=0; i<level.length; i++)
if(level[i]==element)
return i;
throw new Exception("Error. Didn't find element "+
((GraphNode)m_nodes.elementAt(element)).ID+
" in level. Inspect code for "+
"weka.gui.graphvisualizer.HierarchicalBCEngine");
}
/**
* Computes the number of edge crossings in the whole graph
* Takes as an argument levels of nodes. It is essentially the
* same algorithm provided in Universitat des Saarlandes
* technical report A03/94 by Georg Sander.
*/
protected int crossings(final int levels[][]) {
int sum=0;
for(int i=0; i<levels.length-1; i++) {
//System.out.println("*****************Processing level "+i+
// "*****************************");
MyList upper = new MyList(), lower = new MyList();
MyListNode lastOcrnce[] = new MyListNode[m_nodes.size()];
int edgeOcrnce[] = new int[m_nodes.size()];
for(int j=0,uidx=0,lidx=0; j<(levels[i].length+levels[i+1].length); j++) {
if((j%2==0 && uidx<levels[i].length) || lidx>=levels[i+1].length) {
int k1=0, k2=0, k3=0;
GraphNode n = (GraphNode) m_nodes.elementAt(levels[i][uidx]);
//Deactivating and counting crossings for all edges ending in it
//coming from bottom left
if(lastOcrnce[levels[i][uidx]]!=null) {
MyListNode temp = new MyListNode(-1); temp.next = upper.first;
try {
do {
temp = temp.next;
if(levels[i][uidx]==temp.n) {
k1 = k1+1;
k3 = k3+k2;
//System.out.println("Removing from upper: "+temp.n);
upper.remove(temp);
}
else
k2 = k2+1;
} while(temp!=lastOcrnce[levels[i][uidx]]);
}
catch(NullPointerException ex) {
System.out.println("levels[i][uidx]: "+levels[i][uidx]+
" which is: "+((GraphNode)m_nodes.elementAt(levels[i][uidx])).ID+
" temp: "+temp+
" upper.first: "+upper.first);
ex.printStackTrace();
System.exit(-1);}
lastOcrnce[levels[i][uidx]]=null;
sum = sum + k1*lower.size() + k3;
}
//Activating all the edges going out towards the bottom
//and bottom right
for(int k=0; k<n.edges.length; k++) {
if(n.edges[k][1]>0)
try {
if( indexOfElementInLevel(n.edges[k][0], levels[i+1]) >= uidx) {
edgeOcrnce[n.edges[k][0]]=1;
}
}
catch(Exception ex) {
ex.printStackTrace();
}
}
for(int k=0; k<levels[i+1].length; k++) {
if(edgeOcrnce[levels[i+1][k]]==1) {
MyListNode temp = new MyListNode(levels[i+1][k]); //new MyListNode(n.edges[k][0]);
lower.add(temp);
lastOcrnce[levels[i+1][k]] = temp;
edgeOcrnce[levels[i+1][k]] = 0;
//System.out.println("Adding to lower: "+levels[i+1][k]+
//" which is: "+((GraphNode)m_nodes.elementAt(levels[i+1][k])).ID+
//" first's n is: "+lower.first.n);
}
}
uidx++;
}
else {
int k1=0, k2=0, k3=0;
GraphNode n = (GraphNode) m_nodes.elementAt(levels[i+1][lidx]);
//Deactivating and counting crossings for all edges ending in it
//coming from up and upper left
if(lastOcrnce[levels[i+1][lidx]]!=null) {
MyListNode temp = new MyListNode(-1); temp.next = lower.first;
try {
do {
temp = temp.next;
if(levels[i+1][lidx]==temp.n) {
k1 = k1+1;
k3 = k3+k2;
lower.remove(temp);
//System.out.println("Removing from lower: "+temp.n);
}
else
k2 = k2+1;
//System.out.println("temp: "+temp+" lastOcrnce: "+
// lastOcrnce[levels[i+1][lidx]]+" temp.n: "+
// temp.n+" lastOcrnce.n: "+
// lastOcrnce[levels[i+1][lidx]].n);
} while(temp!=lastOcrnce[levels[i+1][lidx]]);
}
catch(NullPointerException ex) {
System.out.print("levels[i+1][lidx]: "+levels[i+1][lidx]+
" which is: "+
((GraphNode)m_nodes.elementAt(levels[i+1][lidx])).ID+
" temp: "+temp);
System.out.println(" lower.first: "+lower.first);
ex.printStackTrace();
System.exit(-1);
}
lastOcrnce[levels[i+1][lidx]]=null;
sum = sum + k1*upper.size() + k3;
}
//Activating all the edges going out towards the upper right
for(int k=0; k<n.edges.length; k++) {
if(n.edges[k][1]<0)
try {
if( indexOfElementInLevel(n.edges[k][0], levels[i]) > lidx) {
edgeOcrnce[n.edges[k][0]]=1;
}
}
catch(Exception ex) {
ex.printStackTrace();
}
}
for(int k=0; k<levels[i].length; k++) {
if(edgeOcrnce[levels[i][k]]==1) {
MyListNode temp = new MyListNode(levels[i][k]);
upper.add(temp);
lastOcrnce[levels[i][k]]=temp;
edgeOcrnce[levels[i][k]]=0;
//System.out.println("Adding to upper: "+levels[i][k]+
// " which is : "+
// ((GraphNode)m_nodes.elementAt(levels[i][k])).ID+
// " from node: "+n.ID+", "+k+
// " first's value: "+upper.first.n);
}
}
lidx++;
}
}
//System.out.println("Sum at the end is: "+sum);
}
return sum;
}
/**
* The following two methods remove cycles from the graph.
*/
protected void removeCycles() {
//visited[x]=1 is only visited AND visited[x]=2 means node is visited
// and is on the current path
int visited[] = new int[m_nodes.size()];
for(int i=0; i<graphMatrix.length; i++) {
if(visited[i]==0){
removeCycles2(i, visited);
visited[i]=1;
}
}
}
/** This method should not be called directly. It should be called only from
to call removeCycles() */
private void removeCycles2(int nindex, int visited[]) {
visited[nindex]=2;
for(int i=0; i<graphMatrix[nindex].length; i++) {
if(graphMatrix[nindex][i]==DIRECTED)
if(visited[i]==0) {
removeCycles2(i, visited);
visited[i]=1;
}
else if(visited[i]==2) {
if(nindex==i) {
graphMatrix[nindex][i]=0;
}
else if(graphMatrix[i][nindex]==DIRECTED) {
//System.out.println("\nFound double "+nindex+','+i);
graphMatrix[i][nindex]=DOUBLE;
graphMatrix[nindex][i]=-DOUBLE;
}
else {
//System.out.println("\nReversing "+nindex+','+i);
graphMatrix[i][nindex]=REVERSED;
graphMatrix[nindex][i]=-REVERSED;
}
}
}
}
/**
* This method assigns a vertical level to each node.
* See makeProperHierarchy() to see how to use it.
*/
protected void assignLevels(int levels[], int depth, int i, int j) {
//System.out.println(i+","+j);
if(i>=graphMatrix.length)
return;
else if(j>=graphMatrix[i].length)
return;
if(graphMatrix[i][j]<=0)
assignLevels(levels, depth, i, ++j);
else if(graphMatrix[i][j]==DIRECTED || graphMatrix[i][j]==DOUBLE) {
if(depth+1>levels[j]) {
levels[j]=depth+1;
assignLevels(levels, depth+1, j, 0);
}
assignLevels(levels, depth, i, ++j);
}
}
/**
* This method minimizes the number of edge crossings using the BaryCenter
* heuristics given by Sugiyama et al. 1981
* This method processes the graph topdown if reversed is false,
* otherwise it does bottomup.
*/
private int[][] minimizeCrossings(boolean reversed, int nodeLevels[][]) {
//Minimizing crossings using Sugiyama's method
if(reversed==false) {
for(int times=0; times<1; times++) {
int tempLevels[][] = new int[nodeLevels.length][];
//System.out.println("---------------------------------");
//System.out.println("Crossings before PHaseID: "+
// crossings(nodeLevels));
copy2DArray(nodeLevels, tempLevels);
for(int i=0; i<nodeLevels.length-1; i++) //Down
phaseID(i, tempLevels);
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
//System.out.println("\nCrossings before PHaseIU: "+
// crossings(nodeLevels));
tempLevels = new int[nodeLevels.length][];
copy2DArray(nodeLevels, tempLevels);
for(int i=nodeLevels.length-2; i>=0; i--) //Up
phaseIU(i, tempLevels);
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
//System.out.println("\nCrossings before PHaseIID: "+
// crossings(nodeLevels));
tempLevels = new int[nodeLevels.length][];
copy2DArray(nodeLevels, tempLevels);
for(int i=0; i<nodeLevels.length-1; i++) { //Down
phaseIID(i, tempLevels);
}
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
//System.out.println("Crossings temp:"+crossings(tempLevels)+
// " graph:"+crossings(nodeLevels));
//printMatrices(nodeLevels);
//System.out.println("\nCrossings before PHaseIIU: "+
// crossings(nodeLevels));
tempLevels = new int[nodeLevels.length][];
copy2DArray(nodeLevels, tempLevels);
for(int i=nodeLevels.length-2; i>=0; i--) { //Up
phaseIIU(i, tempLevels);
}
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
///System.out.println("Crossings temp:"+crossings(tempLevels)+
// " graph:"+crossings(nodeLevels));
///printMatrices(nodeLevels);
//System.out.println("\nCrossings after phaseIIU: "+
// crossings(nodeLevels));
}
return nodeLevels;
}
else {
for(int times=0; times<1; times++) {
int tempLevels[][] = new int[nodeLevels.length][];
//System.out.println("---------------------------------");
//System.out.println("\nCrossings before PHaseIU: "+
// crossings(nodeLevels));
copy2DArray(nodeLevels, tempLevels);
for(int i=nodeLevels.length-2; i>=0; i--) //Up
phaseIU(i, tempLevels);
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
//printMatrices(nodeLevels);
//System.out.println("Crossings before PHaseID: "+
// crossings(nodeLevels));
tempLevels = new int[nodeLevels.length][];
copy2DArray(nodeLevels, tempLevels);
for(int i=0; i<nodeLevels.length-1; i++) //Down
phaseID(i, tempLevels);
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
///printMatrices(nodeLevels);
//System.out.println("\nCrossings before PHaseIIU: "+
// crossings(nodeLevels));
tempLevels = new int[nodeLevels.length][];
copy2DArray(nodeLevels, tempLevels);
for(int i=nodeLevels.length-2; i>=0; i--) { //Up
phaseIIU(i, tempLevels);
}
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
//printMatrices(nodeLevels);
//System.out.println("\nCrossings before PHaseIID: "+
// crossings(nodeLevels));
tempLevels = new int[nodeLevels.length][];
copy2DArray(nodeLevels, tempLevels);
for(int i=0; i<nodeLevels.length-1; i++) { //Down
phaseIID(i, tempLevels);
}
if(crossings(tempLevels)<crossings(nodeLevels))
nodeLevels = tempLevels;
///printMatrices(nodeLevels);
//System.out.println("\nCrossings after phaseIID: "+
// crossings(nodeLevels));
}
return nodeLevels;
}
}
/**
* See Sugiyama et al. 1981 (full reference give at top)
* lindex is the index of the level we want to process.
* In this method we'll sort the vertices at the level one
* below lindex according to their UP-barycenters (or column
* barycenters).
*/
protected void phaseID(final int lindex, final int levels[][]) {
float colBC[]; //= new float[levels[lindex+1].size()];
colBC = calcColBC(lindex, levels);
//System.out.println("In ID Level"+(lindex+1)+":");
//System.out.print("\t");
//for(int i=0; i<colBC.length; i++)
// {System.out.print("Col"+(i+1)+":"+colBC[i]+" ");
// }
//System.out.println("");
//colBC = calcColBC1(lindex, levels);
//for(int i=0; i<colBC.length; i++)
// {System.out.print("Col"+(i+1)+":"+colBC[i]+" ");
// }
//System.out.println("");
//System.out.print("\n\tNodes ");
//for(int i=0; i<levels[lindex+1].length; i++)
// System.out.print(levels[lindex+1][i]+" ");
//System.out.println("");
//System.out.println("\nCrossings: "+crossings(levels));
//inspect(false, lindex, levels, colBC);
isort(levels[lindex+1], colBC);
//combSort11(levels[lindex+1], colBC);
//System.out.println("After sorting");
//System.out.print("\t");
//for(int i=0; i<colBC.length; i++)
// {System.out.print("Col"+(i+1)+":"+colBC[i]+" ");
// }
//System.out.print("\n\tNodes ");
//for(int i=0; i<levels[lindex+1].length; i++)
// System.out.print(levels[lindex+1][i]+" ");
//System.out.println("\nCrossings: "+crossings(levels));
///System.out.println("");
}
/**
* See Sugiyama et al. 1981 (full reference give at top)
* lindex is the index of the level we want to process.
* In this method we'll sort the vertices at the level
* lindex according to their DOWN-barycenters (or row
* barycenters).
*/
public void phaseIU(final int lindex, final int levels[][]) {
float rowBC[];
rowBC = calcRowBC(lindex, levels);
//System.out.println("In IU Level"+(lindex+1)+":");
//System.out.print("\t");
//for(int i=0; i<rowBC.length; i++)
// {System.out.print("Row"+(i+1)+":"+rowBC[i]+" ");
// }
// System.out.print("\n\t");
// rowBC = calcRowBC1(lindex, levels);
// for(int i=0; i<rowBC.length; i++)
// {System.out.print("Row"+(i+1)+":"+rowBC[i]+" ");
// }
// System.out.println("");
//System.out.print("\n\tNodes ");
//for(int i=0; i<levels[lindex].length; i++)
// System.out.print(levels[lindex][i]+" ");
//System.out.println("\nCrossings: "+crossings(levels));
//inspect(true, lindex, levels, rowBC);
isort(levels[lindex], rowBC);
//combSort11(levels[lindex], rowBC);
//System.out.println("After sorting\n\t");
//for(int i=0; i<rowBC.length; i++)
// {System.out.print("Row"+(i+1)+":"+rowBC[i]+" ");
// }
//System.out.print("\n\tNodes ");
//for(int i=0; i<levels[lindex].length; i++)
// System.out.print(levels[lindex][i]+" ");
//System.out.println("\nCrossings: "+crossings(levels));
}
/**
* See Sugiyama et al. 1981 (full reference give at top)
*/
public void phaseIID(final int lindex, final int levels[][]) {
float colBC[];
colBC = calcColBC(lindex, levels);
//System.out.println("getting into phase IID");
for(int i=0; i<colBC.length-1; i++) {
if(colBC[i]==colBC[i+1]) {
//System.out.println("Crossings before begining of iteration: "+
// crossings(levels));
int tempLevels[][] = new int[levels.length][];
copy2DArray(levels, tempLevels);
//System.out.println("Interchanging: "+
// ((GraphNode)m_nodes.elementAt(levels[lindex+1][i])).ID+
// " & "+
// ((GraphNode)m_nodes.elementAt(levels[lindex+1][(i+1)])).ID+
// " at level "+(lindex+1) );
int node1 = levels[lindex+1][i];
int node2 = levels[lindex+1][i+1];
levels[lindex+1][i+1] = node1;
levels[lindex+1][i] = node2;
for(int k=lindex+1; k<levels.length-1; k++)
phaseID(k, levels);
//System.out.println("Crossings temp:"+crossings(tempLevels)+
// " graph:"+crossings(levels));
if(crossings(levels)<=crossings(tempLevels)) {
//System.out.println("Crossings temp: "+crossings(tempLevels)+
// " Crossings levels: "+crossings(levels));
copy2DArray(levels, tempLevels); } //printMatrices(levels); }
else {
copy2DArray(tempLevels, levels);
levels[lindex+1][i+1] = node1;
levels[lindex+1][i] = node2;
}
//System.out.println("Crossings after PhaseID of phaseIID, "+
// "in iteration "+i+" of "+(colBC.length-1)+" at "+
// lindex+", levels: "+crossings(levels)+
// " temp: "+crossings(tempLevels));
//tempLevels = new int[levels.length][];
//copy2DArray(levels, tempLevels);
for(int k=levels.length-2; k>=0; k--)
phaseIU(k, levels);
//System.out.println("Crossings temp:"+crossings(tempLevels)+
// " graph:"+crossings(levels));
//if(crossings(tempLevels)<crossings(levels)) {
// System.out.println("Crossings temp: "+crossings(tempLevels)+
// " Crossings levels: "+crossings(levels));
// copy2DArray(tempLevels, levels); } //printMatrices(levels); }
if(crossings(tempLevels)<crossings(levels))
copy2DArray(tempLevels, levels);
//System.out.println("Crossings after PhaseIU of phaseIID, in"+
// " iteration "+i+" of "+(colBC.length-1)+" at "
// +lindex+", levels: "+crossings(levels)+" temp: "+
// crossings(tempLevels));
//colBC = calcColBC(lindex, levels);
}
}
}
/**
* See Sugiyama et al. 1981 (full reference give at top)
*/
public void phaseIIU(final int lindex, final int levels[][]) {
float rowBC[];
rowBC = calcRowBC(lindex, levels);
//System.out.println("Getting into phaseIIU");
for(int i=0; i<rowBC.length-1; i++) {
if(rowBC[i]==rowBC[i+1]) {
//System.out.println("Crossings before begining of iteration: "+
// crossings(levels));
int tempLevels[][] = new int[levels.length][];
copy2DArray(levels, tempLevels);
//System.out.println("Interchanging: "+
// ((GraphNode)m_nodes.elementAt(levels[lindex][i])).ID+" & "+
// ((GraphNode)m_nodes.elementAt(levels[lindex][i+1])).ID+
// " at level "+(lindex+1) );
int node1 = levels[lindex][i];
int node2 = levels[lindex][i+1];
levels[lindex][i+1] = node1;
levels[lindex][i] = node2;
for(int k=lindex-1; k>=0; k--)
phaseIU(k, levels);
if(crossings(levels)<=crossings(tempLevels)) {
//System.out.println("Crossings temp: "+crossings(tempLevels)+
// " Crossings levels: "+crossings(levels));
copy2DArray(levels, tempLevels); } //printMatrices(levels);
else {
copy2DArray(tempLevels, levels);
levels[lindex][i+1] = node1;
levels[lindex][i] = node2;
}
//System.out.println("Crossings after PhaseIU of PhaseIIU, in "+
// "iteration "+i+" of "+(rowBC.length-1)+" at "
// +lindex+", levels: "+crossings(levels)+
// " temp: "+crossings(tempLevels));
//tempLevels = new int[levels.length][];
//copy2DArray(levels, tempLevels);
for(int k=0; k<levels.length-1; k++) //lindex-1; k++)
phaseID(k, levels);
//if(crossings(tempLevels)<crossings(levels)) {
// System.out.println("Crossings temp: "+crossings(tempLevels)+
// " Crossings levels: "+crossings(levels));
// copy2DArray(tempLevels, levels); } //printMatrices(levels); }
if(crossings(tempLevels)<=crossings(levels))
copy2DArray(tempLevels, levels);
//System.out.println("Crossings after PhaseID of phaseIIU, in "+
// "iteration "+i+" of "+(rowBC.length-1)+" at "
// +lindex+", levels: "+crossings(levels)+
// " temp: "+crossings(tempLevels));
//rowBC = calcRowBC(lindex, levels);
}
}
}
/**
* See Sugiyama et al. 1981 (full reference give at top)
*/
protected float [] calcRowBC(final int lindex, final int levels[][]){
float rowBC[] = new float[levels[lindex].length];
GraphNode n;
for(int i=0; i<levels[lindex].length; i++) {
int sum=0;
n = (GraphNode)m_nodes.elementAt(levels[lindex][i]);
for(int j=0; j<n.edges.length; j++) {
if(n.edges[j][1]>0) {
sum++;
try {
rowBC[i] =
rowBC[i]+indexOfElementInLevel(n.edges[j][0], levels[lindex+1])+1;
}
catch(Exception ex) { return null; }
}
}
if(rowBC[i]!=0)
rowBC[i] = rowBC[i]/sum;
}
return rowBC;
}
/**
* See Sugiyama et al. 1981 (full reference give at top)
*/
protected float [] calcColBC(final int lindex, final int levels[][]) {
float colBC[] = new float[levels[lindex+1].length];
GraphNode n;
for(int i=0; i<levels[lindex+1].length; i++) {
int sum=0;
n = (GraphNode)m_nodes.elementAt(levels[lindex+1][i]);
for(int j=0; j<n.edges.length; j++) {
if(n.edges[j][1]<1) {
sum++;
try{
colBC[i] =
colBC[i]+indexOfElementInLevel(n.edges[j][0], levels[lindex])+1;
}
catch(Exception ex) { return null; }
}
}
if(colBC[i]!=0)
colBC[i]=colBC[i]/sum;
}
return colBC;
}
/**
* Prints out the interconnection matrix at each level.
* See Sugiyama et al. 1981 (full reference give at top)
*/
protected void printMatrices(final int levels[][]) {
int i=0;
for(i=0; i<levels.length-1; i++) {
float rowBC[]=null; float colBC[]=null;
try{
rowBC = calcRowBC(i, levels); colBC = calcColBC(i, levels);
}
catch(NullPointerException ne) {
System.out.println("i: "+i+" levels.length: "+levels.length);
ne.printStackTrace();
return;
}
System.out.print("\nM"+(i+1)+"\t");
for(int j=0; j<levels[i+1].length; j++) {
System.out.print( ((GraphNode)m_nodes.elementAt(levels[i+1][j])).ID +
" ");
//((Integer)levels[i+1].elementAt(j)).intValue())+" ");
}
System.out.println("");
for(int j=0; j<levels[i].length; j++) {
System.out.print( ((GraphNode)m_nodes.elementAt(levels[i][j])).ID+"\t");
//((Integer)levels[i].elementAt(j)).intValue())+"\t");
for(int k=0; k<levels[i+1].length; k++) {
System.out.print(graphMatrix[levels[i][j]]
//((Integer)levels[i].elementAt(j)).intValue()]
[levels[i+1][k]]+" ");
//((Integer)levels[i+1].elementAt(k)).intValue()]+" ");
}
System.out.println(rowBC[j]);
}
System.out.print("\t");
for(int k=0; k<levels[i+1].length; k++)
System.out.print(colBC[k]+" ");
}
System.out.println("\nAt the end i: "+i+" levels.length: "+levels.length);
}
/**
* This methods sorts the vertices in level[] according to their
* barycenters in BC[], using combsort11. It, however, doesn't touch the
* vertices with barycenter equal to zero.
*/
/*
* //This method should be removed
protected static void combSort11(int level[], float BC[]) {
int switches, j, top, gap, lhold;
float hold;
gap = BC.length;
do {
gap=(int)(gap/1.3);
switch(gap) {
case 0:
gap = 1;
break;
case 9:
case 10:
gap=11;
break;
default:
break;
}
switches=0;
top = BC.length-gap;
for(int i=0; i<top; i++) {
j=i+gap;
if(BC[i]==0 || BC[j]==0)
continue;
if(BC[i] > BC[j]) {
hold=BC[i];
BC[i]=BC[j];
BC[j]=hold;
lhold = level[i];
level[i] = level[j];
level[j] = lhold;
switches++;
}//endif
}//endfor
}while(switches>0 || gap>1);
}
*/
/**
* This methods sorts the vertices in level[] according to their
* barycenters in BC[], using insertion sort. It, however, doesn't touch the
* vertices with barycenter equal to zero.
*/
//Both level and BC have elements in the same order
protected static void isort(int level[], float BC[]) {
float temp;
int temp2;
for(int i=0; i<BC.length-1; i++) {
int j=i;
temp=BC[j+1];
temp2=level[j+1];
if(temp==0)
continue;
int prej=j+1;
while( j>-1 && (temp<BC[j]|| BC[j]==0) ) {
if(BC[j]==0){
j--; continue;}
else {
BC[prej] = BC[j];
level[prej] = level[j];
prej=j;
j--;
}
}
//j++;
BC[prej] = temp;
level[prej] = temp2;
//Integer node = (Integer)level.elementAt(i+1);
//level.removeElementAt(i+1);
//level.insertElementAt(node, prej);
}
}
/**
* Copies one Matrix of type int[][] to another.
*/
protected void copyMatrix(int from[][], int to[][]) {
for(int i=0; i<from.length; i++)
for(int j=0; j<from[i].length; j++)
to[i][j]=from[i][j];
}
/**
* Copies one array of type int[][] to another.
*/
protected void copy2DArray(int from[][], int to[][]) {
for(int i=0; i<from.length; i++) {
to[i] = new int[from[i].length];
System.arraycopy(from[i], 0, to[i], 0, from[i].length);
//for(int j=0; j<from[i].length; j++)
// to[i][j] = from[i][j];
}
}
/**
* This method lays out the vertices horizontally, in each level.
* It simply assings an x value to a vertex according to its
* index in the level.
*/
protected void naiveLayout() {
/*
if(maxStringWidth==0) {
int strWidth;
for(int i=0; i<m_nodes.size(); i++) {
strWidth = m_fm.stringWidth(((GraphNode)m_nodes.elementAt(i)).lbl);
if(strWidth>maxStringWidth)
maxStringWidth=strWidth;
}
if(m_nodeSize<maxStringWidth)
{m_nodeSize = maxStringWidth+4; m_nodeArea = m_nodeSize+8; }
}
*/
if(nodeLevels==null)
makeProperHierarchy();
//int nodeHeight = m_nodeHeight*2; //m_fm.getHeight()*2;
for(int i=0, temp=0; i<nodeLevels.length; i++) {
for(int j=0; j<nodeLevels[i].length; j++) {
temp=nodeLevels[i][j];
//horPositions[temp]=j;
GraphNode n = (GraphNode)m_nodes.elementAt(temp);
n.x = j*m_nodeWidth; //horPositions[temp]*m_nodeWidth;
n.y = i*3*m_nodeHeight;
}
}
//setAppropriateSize();
}
protected int uConnectivity(int lindex, int eindex) {
int n=0;
for(int i=0; i<nodeLevels[lindex-1].length; i++)
if(graphMatrix[ nodeLevels[lindex-1][i] ][ nodeLevels[lindex][eindex] ]>0)
n++;
return n;
}
protected int lConnectivity(int lindex, int eindex) {
int n=0;
for(int i=0; i<nodeLevels[lindex+1].length; i++)
if(graphMatrix[ nodeLevels[lindex][eindex] ][ nodeLevels[lindex+1][i] ]>0)
n++;
return n;
}
protected int uBCenter(int lindex, int eindex, int horPositions[]) {
int sum=0;
for(int i=0; i<nodeLevels[lindex-1].length; i++)
if(graphMatrix[nodeLevels[lindex-1][i]][nodeLevels[lindex][eindex]]>0)
sum = sum + (horPositions[nodeLevels[lindex-1][i]]);
if(sum!=0) { // To avoid 0/0
//System.out.println("uBC Result: "+sum+"/"+
// uConnectivity(lindex,eindex)+
// " = "+(sum/uConnectivity(lindex,eindex)) );
sum = sum/uConnectivity(lindex,eindex);
}
return sum;
}
protected int lBCenter(int lindex, int eindex, int horPositions[]) {
int sum=0;
for(int i=0; i<nodeLevels[lindex+1].length; i++)
if(graphMatrix[nodeLevels[lindex][eindex]][nodeLevels[lindex+1][i]]>0)
sum = sum + (horPositions[nodeLevels[lindex+1][i]]);
if(sum!=0) // To avoid 0/0
sum = sum/lConnectivity(lindex, eindex); //lConectivity;
return sum;
}
private void tempMethod(int horPositions[]) {
int minPosition = horPositions[0];
for(int i=0; i<horPositions.length; i++)
if(horPositions[i]<minPosition)
minPosition=horPositions[i];
if(minPosition<0) {
minPosition = minPosition*-1;
for(int i=0; i<horPositions.length; i++){
//System.out.print(horPositions[i]);
horPositions[i]+=minPosition;
//System.out.println(">"+horPositions[i]);
}
}
//int nodeHeight = m_nodeHeight*2; //m_fm.getHeight()*2;
for(int i=0, temp=0; i<nodeLevels.length; i++) {
for(int j=0; j<nodeLevels[i].length; j++) {
temp=nodeLevels[i][j];
//horPositions[temp]=j;
GraphNode n = (GraphNode)m_nodes.elementAt(temp);
n.x = horPositions[temp]*m_nodeWidth;
n.y = i*3*m_nodeHeight;
}
}
}
/**
* This method lays out the vertices horizontally, in each level.
* See Sugiyama et al. 1981 for full reference.
*/
protected void priorityLayout1() {
int [] horPositions = new int[m_nodes.size()];
int maxCount=0;
for(int i=0; i<nodeLevels.length; i++) {
int count=0;
for(int j=0; j<nodeLevels[i].length; j++) {
horPositions[nodeLevels[i][j]]=j;
count++;
}
if(count>maxCount)
maxCount=count;
}
//fireLayoutCompleteEvent( new LayoutCompleteEvent(this) );
int priorities[], BC[];
//System.out.println("********Going from 2 to n********");
for(int i=1; i<nodeLevels.length; i++) {
priorities = new int[nodeLevels[i].length];
BC = new int[nodeLevels[i].length];
for(int j=0; j<nodeLevels[i].length; j++) {
if(((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID.startsWith("S"))
priorities[j] = maxCount+1;
else
priorities[j] = uConnectivity(i, j);
BC[j] = uBCenter(i, j, horPositions);
}
//for(int j=0; j<nodeLevels[i].length; j++)
// System.out.println("Level: "+(i+1)+" Node: "
// +((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID
// +" uConnectivity: "+priorities[j]+" uBC: "+BC[j]+" position: "
// +horPositions[nodeLevels[i][j]]);
priorityLayout2(nodeLevels[i], priorities, BC, horPositions);
//repaint
//try {
// tempMethod(horPositions);
// fireLayoutCompleteEvent( new LayoutCompleteEvent(this) );
// Thread.sleep(1000);
//} catch(InterruptedException ie) { ie.printStackTrace(); }
//for(int j=0; j<nodeLevels[i].length; j++)
// System.out.println("Level: "+(i+1)+" Node: "
// +((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID
// +" uConnectivity: "+priorities[j]+" uBC: "+BC[j]+" position: "
// +horPositions[nodeLevels[i][j]]);
}
//System.out.println("********Going from n-1 to 1********");
for(int i=nodeLevels.length-2; i>=0; i--) {
priorities = new int[nodeLevels[i].length];
BC = new int[nodeLevels[i].length];
for(int j=0; j<nodeLevels[i].length; j++) {
if(((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID.startsWith("S"))
priorities[j] = maxCount+1;
else
priorities[j] = lConnectivity(i, j);
BC[j] = lBCenter(i, j, horPositions); //, priorities[j]);
}
priorityLayout2(nodeLevels[i], priorities, BC, horPositions);
//repaint();
//try {
// tempMethod(horPositions);
// fireLayoutCompleteEvent( new LayoutCompleteEvent(this) );
// Thread.sleep(1000);
//} catch(InterruptedException ie) { ie.printStackTrace(); }
//for(int j=0; j<nodeLevels[i].length; j++)
// System.out.println("Level: "+(i+1)+" Node: "
// +((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID
// +" lConnectivity: "+priorities[j]+" lBC: "+BC[j]+" position: "
// +horPositions[nodeLevels[i][j]]);
}
//System.out.println("********Going from 2 to n again********");
for(int i=2; i<nodeLevels.length; i++) {
priorities = new int[nodeLevels[i].length];
BC = new int[nodeLevels[i].length];
for(int j=0; j<nodeLevels[i].length; j++) {
if(((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID.startsWith("S"))
priorities[j] = maxCount+1;
else
priorities[j] = uConnectivity(i, j);
BC[j] = uBCenter(i, j, horPositions);
}
//for(int j=0; j<nodeLevels[i].length; j++)
// System.out.println("Level: "+(i+1)+" Node: "
// +((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID
// +" uConnectivity: "+priorities[j]+" uBC: "+BC[j]+" position: "
// +horPositions[nodeLevels[i][j]]);
priorityLayout2(nodeLevels[i], priorities, BC, horPositions);
//repaint();
//try {
// tempMethod(horPositions);
// fireLayoutCompleteEvent( new LayoutCompleteEvent(this) );
// Thread.sleep(1000);
//} catch(InterruptedException ie) { ie.printStackTrace(); }
//for(int j=0; j<nodeLevels[i].length; j++)
// System.out.println("Level: "+(i+1)+" Node: "
// +((GraphNode)m_nodes.elementAt(nodeLevels[i][j])).ID
// +" uConnectivity: "+priorities[j]+" uBC: "+BC[j]+" position: "
// +horPositions[nodeLevels[i][j]]);
}
int minPosition = horPositions[0];
for(int i=0; i<horPositions.length; i++)
if(horPositions[i]<minPosition)
minPosition=horPositions[i];
if(minPosition<0) {
minPosition = minPosition*-1;
for(int i=0; i<horPositions.length; i++){
//System.out.print(horPositions[i]);
horPositions[i]+=minPosition;
//System.out.println(">"+horPositions[i]);
}
}
//int nodeHeight = m_nodeHeight*2; //m_fm.getHeight()*2;
for(int i=0, temp=0; i<nodeLevels.length; i++) {
for(int j=0; j<nodeLevels[i].length; j++) {
temp=nodeLevels[i][j];
//horPositions[temp]=j;
GraphNode n = (GraphNode)m_nodes.elementAt(temp);
n.x = horPositions[temp]*m_nodeWidth;
n.y = i*3*m_nodeHeight;
}
}
//setAppropriateSize();
}
/**
* This method is used by priorityLayout1(). It should
* not be called directly.
* This method does the actual moving of the vertices in each level
* based on their priorities and barycenters.
*/
private void priorityLayout2(int level[], int priorities[],
int bCenters[], int horPositions[]) {
int descOrder[] = new int[priorities.length];
//Getting the indices of priorities in descending order
descOrder[0]=0;
for(int i=0; i<priorities.length-1; i++) {
int j=i;
int temp=i+1;
while( j>-1 && priorities[descOrder[j]]<priorities[temp] ) {
descOrder[j+1] = descOrder[j];
j--;
}
j++;
descOrder[j] = temp;
}
//System.out.println("\nPriorities:");
//for(int i=0; i<priorities.length; i++)
// System.out.print(priorities[i]+" ");
//System.out.println("\nDescOrder:");
//for(int i=0; i<descOrder.length; i++)
// System.out.print(descOrder[i]+" ");
for(int k=0; k<descOrder.length; k++)
for(int i=0; i<descOrder.length; i++) {
int leftCount=0, rightCount=0, leftNodes[], rightNodes[];
for(int j=0; j<priorities.length; j++) {
if( horPositions[level[descOrder[i]]] > horPositions[level[j]] )
leftCount++;
else if( horPositions[level[descOrder[i]]] < horPositions[level[j]] )
rightCount++;
}
leftNodes = new int[leftCount];
rightNodes = new int[rightCount];
for(int j=0, l=0, r=0; j<priorities.length; j++)
if( horPositions[level[descOrder[i]]] > horPositions[level[j]] )
leftNodes[l++]=j;
else if( horPositions[level[descOrder[i]]] < horPositions[level[j]] )
rightNodes[r++]=j;
//****Moving left
while(Math.abs(horPositions[level[descOrder[i]]]-1
-bCenters[descOrder[i]]) <
Math.abs(horPositions[level[descOrder[i]]]-bCenters[descOrder[i]]) ) {
//****Checking if it can be moved to left
int temp = horPositions[level[descOrder[i]]];
boolean cantMove=false;
for(int j=leftNodes.length-1; j>=0; j--) {
if(temp-horPositions[level[leftNodes[j]]] > 1)
break;
else if(priorities[descOrder[i]]<=priorities[leftNodes[j]])
{ cantMove=true; break; }
else
temp = horPositions[level[leftNodes[j]]];
}
//if(horPositions[level[descOrder[i]]]-1==
// horPositions[level[leftNodes[j]]])
// cantMove = true;
if(cantMove)
break;
temp = horPositions[level[descOrder[i]]]-1;
//****moving other vertices to left
for(int j=leftNodes.length-1; j>=0; j--) {
if(temp==horPositions[level[leftNodes[j]]]) {
//System.out.println("Moving "+
// ((Node)m_nodes.elementAt(level[leftNodes[j]])).ID+" from "
// +horPositions[level[leftNodes[j]]]+" to "
// +(horPositions[level[leftNodes[j]]]-1) );
horPositions[level[leftNodes[j]]] =
temp = horPositions[level[leftNodes[j]]]-1;
}
}
//System.out.println("Moving main "+
// ((GraphNode)m_nodes.elementAt(level[descOrder[i]])).ID+" from "
// +horPositions[level[descOrder[i]]]+" to "
// +(horPositions[level[descOrder[i]]]-1));
horPositions[level[descOrder[i]]]=horPositions[level[descOrder[i]]]-1;
}
//****Moving right
while(Math.abs(horPositions[level[descOrder[i]]]+1
-bCenters[descOrder[i]]) <
Math.abs(horPositions[level[descOrder[i]]]-bCenters[descOrder[i]]) ) {
//****checking if the vertex can be moved
int temp = horPositions[level[descOrder[i]]];
boolean cantMove=false;
for(int j=0; j<rightNodes.length; j++) {
if(horPositions[level[rightNodes[j]]]-temp > 1)
break;
else if(priorities[descOrder[i]]<=priorities[rightNodes[j]])
{ cantMove=true; break; }
else
temp = horPositions[level[rightNodes[j]]];
}
//if(horPositions[level[descOrder[i]]]-1==
// horPositions[level[leftNodes[j]]])
// cantMove = true;
if(cantMove)
break;
temp = horPositions[level[descOrder[i]]]+1;
//****moving other vertices to left
for(int j=0; j<rightNodes.length; j++) {
if(temp==horPositions[level[rightNodes[j]]]) {
//System.out.println("Moving "+
// (Node)m_nodes.elementAt(level[rightNodes[j]])).ID+" from "
// +horPositions[level[rightNodes[j]]]+" to "
// +(horPositions[level[rightNodes[j]]]+1) );
horPositions[level[rightNodes[j]]] =
temp = horPositions[level[rightNodes[j]]]+1;
}
}
//System.out.println("Moving main "+
// ((GraphNode)m_nodes.elementAt(level[descOrder[i]])).ID+" from "
// +horPositions[level[descOrder[i]]]+" to "
// +(horPositions[level[descOrder[i]]]+1));
horPositions[level[descOrder[i]]]=horPositions[level[descOrder[i]]]+1;
}
}
}
/**
* The following classes implement a double linked list to
* be used in the crossings function.
*/
private class MyList {
int size;
MyListNode first=null;
MyListNode last=null;
public void add(int i) {
if(first==null)
first = last = new MyListNode(i);
else
if(last.next==null) {
last.next = new MyListNode(i);
last.next.previous = last;
last = last.next;
}
else {
System.err.println("Error shouldn't be in here. Check MyList code");
size--;
}
size++;
}
public void add(MyListNode n) {
if(first==null)
first = last = n;
else
if(last.next==null) {
last.next = n;
last.next.previous = last;
last = last.next;
}
else {
System.err.println("Error shouldn't be in here. Check MyList code");
size--;
}
size++;
}
public void remove(MyListNode n) {
if(n.previous!=null)
n.previous.next = n.next;
if(n.next!=null)
n.next.previous = n.previous;
if(last==n)
last = n.previous;
if(first==n)
first = n.next;
size--;
}
public void remove(int i) {
MyListNode temp=first;
while(temp!=null && temp.n!=i) temp=temp.next;
if(temp==null){
System.err.println("element "+i+"not present in the list");
return;
}
if(temp.previous!=null)
temp.previous.next = temp.next;
if(temp.next!=null)
temp.next.previous = temp.previous;
if(last==temp)
last = temp.previous;
if(first==temp)
first = temp.next;
size--;
}
public int size() {
return size;
}
}
private class MyListNode {
int n;
MyListNode next, previous;
public MyListNode(int i) {
n = i; next=null; previous=null;
}
}
} // HierarchicalBCEngine