All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weka.gui.graphvisualizer.HierarchicalBCEngine Maven / Gradle / Ivy

/*
 *   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 3 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, see .
 */

/*
 *    HierarchicalBCEngine.java
 *    Copyright (C) 2003-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui.graphvisualizer;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;

/**
 * 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.
 * 

* 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 ([email protected]) * @version $Revision: 10502 $ - 24 Apr 2003 - Initial version (Ashraf M. * Kibriya) * */ public class HierarchicalBCEngine implements GraphConstants, LayoutEngine { /** FastVector containing nodes and edges */ protected ArrayList m_nodes; protected ArrayList m_edges; /** * FastVector containing listeners for layoutCompleteEvent generated by this * LayoutEngine */ protected ArrayList 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(ArrayList nodes, ArrayList 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(ArrayList nodes, ArrayList 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() { @Override 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 = GridBagConstraints.REMAINDER; gbc.anchor = GridBagConstraints.NORTHWEST; gbc.weightx = 1; gbc.fill = GridBagConstraints.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 */ @Override public ArrayList 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. */ @Override public JPanel getControlPanel() { return m_controlsPanel; } /** * Returns a handle to the progressBar of this LayoutEngine. */ @Override 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 */ @Override public void setNodesEdges(ArrayList nodes, ArrayList 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 */ @Override 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. */ @Override public void addLayoutCompleteEventListener(LayoutCompleteEventListener l) { if (layoutCompleteListeners == null) { layoutCompleteListeners = new ArrayList(); } layoutCompleteListeners.add(l); } /** * Method to remove a LayoutCompleteEventListener. * * @param e - The LayoutCompleteEventListener to remove. */ @Override public void removeLayoutCompleteEventListener(LayoutCompleteEventListener e) { if (layoutCompleteListeners != null) { LayoutCompleteEventListener l; for (int i = 0; i < layoutCompleteListeners.size(); i++) { l = layoutCompleteListeners.get(i); if (l == e) { layoutCompleteListeners.remove(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 */ @Override public void fireLayoutCompleteEvent(LayoutCompleteEvent e) { if (layoutCompleteListeners != null && layoutCompleteListeners.size() != 0) { LayoutCompleteEventListener l; for (int i = 0; i < layoutCompleteListeners.size(); i++) { l = layoutCompleteListeners.get(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. *

* 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. */ @Override 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() { @Override 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 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 element : nodesLevel) { if (element > maxLevel) { maxLevel = element; // System.out.println( ((GraphNode)m_nodes.get(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 (int 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.get(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 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.add(new GraphNode(s1, s1, SINGULAR_DUMMY)); // true)); int temp3[] = new int[nodeLevels[level].length + 1]; // for(int j=0; j 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.get(k)).ID+ // " i "+((GraphNode)m_nodes.get(i)).ID+ // " n "+((GraphNode)m_nodes.get(n)).ID+ // " len "+((GraphNode)m_nodes.get(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.get(n)).ID+ // " for "+((GraphNode)m_nodes.get(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 (m_nodes.get(tempnode).nodeType == SINGULAR_DUMMY) { m_nodes.get(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.get(tempnode)).ID+ // ", "+((GraphNode)nodes.get(n)).ID); // ///matrix[lastknowntempnode][source]=-original_val // ///graphMatrix[tempnode][n] = -graphMatrix[n][i]; // System.out.println("modifying "+ // ((GraphNode)nodes.get(i)).ID+ // ", "+ // ((GraphNode)nodes.get(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.add(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.get(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.add(new GraphNode(s2, s2, SINGULAR_DUMMY)); temp3 = new int[nodeLevels[level].length + 1]; // for(int j=0; j len) { // System.out.println("modifying "+ // ((GraphNode)nodes.get(m)).ID+ // ", "+((GraphNode)nodes.get(m-1)).ID); tempMatrix[m][m - 1] = -1 * tempMatrix[n][i]; } } // System.out.println("m "+((GraphNode)m_nodes.get(m)).ID+ // " i "+((GraphNode)m_nodes.get(i)).ID+ // " tempnode "+((GraphNode)m_nodes.get(tempnode)).ID+ // " len "+((GraphNode)m_nodes.get(len)).ID ); // System.out.println("modifying "+ // ((GraphNode)nodes.get(m)).ID+", "+ // ((GraphNode)nodes.get(i)).ID); // temp[lastTempNodeCreated][targetNode]=temp[origNode][targetNode] tempMatrix[m][i] = tempMatrix[n][i]; // System.out.println("modifying "+ // ((GraphNode)nodes.get(tempnode)).ID+", "+ // ((GraphNode)nodes.get(len)).ID); // temp[origNode][firstTempNodecreated] = temp[origNode][targetNode] tempMatrix[tempnode][len] = tempMatrix[n][i]; // System.out.println("modifying "+ // ((GraphNode)nodes.get(len)).ID+", "+ // ((GraphNode)nodes.get(tempnode)).ID); // temp[firstTempNodeCreated][origNode] for reverse tracing tempMatrix[len][tempnode] = -1 * tempMatrix[n][i]; // System.out.println("modifying "+ // ((GraphNode)nodes.get(i)).ID+", "+ // ((GraphNode)nodes.get(m)).ID); // temp[targetNode][lastTempNodecreated] for reverse tracing tempMatrix[i][m] = -1 * tempMatrix[n][i]; if (m > len) { // System.out.println("modifying "+ // ((GraphNode)nodes.get(m)).ID+ // ", "+((GraphNode)nodes.get(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.get(i)).ID+", "+ // ((GraphNode)nodes.get(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 { for (int i = 0; i < level.length; i++) { if (level[i] == element) { return i; } } throw new Exception("Error. Didn't find element " + m_nodes.get(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 = m_nodes.get(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: " + m_nodes.get(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.get(levels[i+1][k])).ID+ // " first's n is: "+lower.first.n); } } uidx++; } else { int k1 = 0, k2 = 0, k3 = 0; GraphNode n = m_nodes.get(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: " + m_nodes.get(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.get(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++) { 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--) { 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--) { 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++) { 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= 0; k--) { phaseIU(k, levels); } // System.out.println("Crossings temp:"+crossings(tempLevels)+ // " graph:"+crossings(levels)); // if(crossings(tempLevels)= 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++) { phaseID(k, levels); } // if(crossings(tempLevels) 0) { sum++; try { rowBC[i] = rowBC[i] + indexOfElementInLevel(edge[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 = m_nodes.get(levels[lindex + 1][i]); for (int[] edge : n.edges) { if (edge[1] < 1) { sum++; try { colBC[i] = colBC[i] + indexOfElementInLevel(edge[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(m_nodes.get(levels[i + 1][j]).ID + " "); // ((Integer)levels[i+1].get(j)).intValue())+" "); } System.out.println(""); for (int j = 0; j < levels[i].length; j++) { System.out.print(m_nodes.get(levels[i][j]).ID + "\t"); // ((Integer)levels[i].get(j)).intValue())+"\t"); for (int k = 0; k < levels[i + 1].length; k++) { System.out.print(graphMatrix[levels[i][j]] // ((Integer)levels[i].get(j)).intValue()] [levels[i + 1][k]] + " "); // ((Integer)levels[i+1].get(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 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.get(i+1); // level.remove(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; jmaxStringWidth) maxStringWidth=strWidth; } * * if(m_nodeSize 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) { sum = sum / lConnectivity(lindex, eindex); // lConectivity; } return sum; } /** * 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 (m_nodes.get(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= 0; i--) { priorities = new int[nodeLevels[i].length]; BC = new int[nodeLevels[i].length]; for (int j = 0; j < nodeLevels[i].length; j++) { if (m_nodes.get(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"+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 = m_nodes.get(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 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.get(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.get(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 rightNode : rightNodes) { if (horPositions[level[rightNode]] - temp > 1) { break; } else if (priorities[descOrder[i]] <= priorities[rightNode]) { cantMove = true; break; } else { temp = horPositions[level[rightNode]]; } } // 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.get(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.get(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(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 int size() { return size; } } private class MyListNode { int n; MyListNode next, previous; public MyListNode(int i) { n = i; next = null; previous = null; } } } // HierarchicalBCEngine





© 2015 - 2024 Weber Informatics LLC | Privacy Policy