/*
* Copyright (c) 2010 Mathew Hall.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* Neither the name of the University of Sheffield nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ClusterViewer.java
*
* Created on Dec 1, 2010, 12:01:24 PM
*/
package visualisation;
import com.apple.eawt.Application;
import edu.uci.ics.jung.algorithms.layout.AbstractLayout;
import edu.uci.ics.jung.algorithms.layout.AggregateLayout;
import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout2;
import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
import edu.uci.ics.jung.algorithms.layout.KKLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout2;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.event.GraphEvent.Vertex;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.lang.String;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ChainedTransformer;
import org.apache.commons.collections15.functors.ConstantTransformer;
import org.apache.commons.collections15.functors.MapTransformer;
import org.apache.commons.collections15.map.LazyMap;
import primitives.graph.Node;
/**
*
* @author Mathew Hall
*/
public class ClusterViewer extends javax.swing.JFrame {
ClusterViewerModel cm;
ClusterViewerController controller;
VisualizationViewer vv;
Map<Node, Paint> vertexPaints =
LazyMap.<Node, Paint>decorate(new HashMap<Node, Paint>(),
new ConstantTransformer(Color.white));
/** Creates new form ClusterViewer */
public ClusterViewer() {
initComponents();
for (Class c : layouts()) {
cmbLayouts.addItem(c);
}
cm = new ClusterViewerModel();
controller = new ClusterViewerController();
controller.setGui(this);
controller.setModel(cm);
/*
* Copyright (c) 2008, the JUNG Project and the Regents of the University of
* California. All rights reserved.
*
* This software is open-source under the BSD license; see either "license.txt"
* or http://jung.sourceforge.net/license.txt for a description.
*/
/**
* A class that shows the minimal work necessary to load and visualize a graph.
*/
//this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//this.pack();
//this.setVisible(true);
}
static class TestGraphMouseListener<V> implements GraphMouseListener<V> {
public void graphClicked(V v, MouseEvent me) {
System.err.println("Vertex " + v + " was clicked at (" + me.getX() + "," + me.getY() + ")");
}
public void graphPressed(V v, MouseEvent me) {
System.err.println("Vertex " + v + " was pressed at (" + me.getX() + "," + me.getY() + ")");
}
public void graphReleased(V v, MouseEvent me) {
System.err.println("Vertex " + v + " was released at (" + me.getX() + "," + me.getY() + ")");
}
}
/**
* Generates a graph: in this case, reads it from the file
* "samples/datasetsgraph/simple.net"
* @return A sample undirected graph
*/
public static List<Class> layouts() {
Class[] layouts = {ISOMLayout.class, FRLayout.class, FRLayout2.class, SpringLayout.class, SpringLayout2.class, CircleLayout.class, KKLayout.class};
return Arrays.asList(layouts);
}
public static AbstractLayout layoutFactory(Class toInstantiate, Graph g) {
try {
Constructor graphConstructor = toInstantiate.getConstructor(Graph.class);
AbstractLayout ret = (AbstractLayout) graphConstructor.newInstance(g);
return ret;
} catch (NoSuchMethodException nse) {
Logger.getLogger(ClusterViewer.class.getName()).log(Level.WARNING, String.format("Couldn't instantiate %s, falling back on FRLayout", toInstantiate.getName()), nse);
} catch (InstantiationException iex) {
Logger.getLogger(ClusterViewer.class.getName()).log(Level.WARNING, String.format("Instantiating %s failed, will return FRLayout instead", toInstantiate.getName()), iex);
} catch (IllegalAccessException iae) {
Logger.getLogger(ClusterViewer.class.getName()).log(Level.WARNING, "Attempted to use a private ctor, defaulting to FRLayout", iae);
} catch (InvocationTargetException ite) {
Logger.getLogger(ClusterViewer.class.getName()).log(Level.WARNING, "Attempt to instantiate Layout from picker failed, defaulting to FRLayout", ite);
}
return new FRLayout(g);
}
public void drawGraph() {
try {
final Graph g = cm.getGraph();
final AggregateLayout<Node, String> layout = new AggregateLayout<Node, String>(layoutFactory(cm.getLayout(), g));
if (vv == null) {
//springlayout and 2, FRlayout
//TODO: render to PDF
vv = new VisualizationViewer(layout);
//pnlJUNGView.add(vv, BorderLayout.CENTER);
} else {
vv.setGraphLayout(layout);
}
vv.setBackground(Color.WHITE);
Transformer labelTransformer = new ChainedTransformer<String, String>(new Transformer[]{
new ToStringLabeller<Node>() {
public String transform(Node v) {
return v.getLabel();
//return ((primitives.graph.Node)(v.getVertex())).getLabel();
}
},
new Transformer<String, String>() {
public String transform(String input) {
return "<html><font color=\"black\">" + input.replace("label=", "");
}
}});
vv.addGraphMouseListener(new TestGraphMouseListener<String>());
vv.getRenderContext().setVertexFillPaintTransformer(MapTransformer.<Node, Paint>getInstance(vertexPaints));
vv.getRenderContext().setVertexLabelTransformer(labelTransformer);
vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan));
vv.getRenderContext().setVertexDrawPaintTransformer(new Transformer<Node, Paint>() {
public Paint transform(Node v) {
if (vv.getPickedVertexState().isPicked(v)) {
return Color.cyan;
} else {
return Color.BLACK;
}
}
});
vv.getRenderContext().setEdgeDrawPaintTransformer(new ConstantTransformer(Color.lightGray));
vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray));
vv.getRenderContext().setArrowDrawPaintTransformer(new ConstantTransformer(Color.lightGray));
// add my listeners for ToolTips
vv.setVertexToolTipTransformer(new ToStringLabeller<String>() {
});
vv.getRenderContext().setEdgeLabelTransformer(new Transformer<String, String>() {
public String transform(String e) {
return ("<html><font color=\"black\">" + e.toString().replaceAll("label=",""));
}
});
//vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<String>());
//vv.getRenderer().getVertexLabelRenderer().setPositioner(new InsidePositioner());
//vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.AUTO);
vv.setForeground(Color.lightGray);
//vv.setSize(new Dimension(5000, 5000));
if (cm.getClusterAssignments() != null) {
Graph<Node, String> gr = layout.getGraph();
layout.removeAll();
//EdgeBetweennessClusterer<Node, String> clusterer =
// new EdgeBetweennessClusterer<Node, String>(cm.getClusterAssignments().size());
Set<Set<Node>> clusterSet = cm.getClusterAssignments();//clusterer.transform(gr);
//List<String> edges = clusterer.getEdgesRemoved();
int i = 0;
//Set the colors of each node so that each cluster's vertices have the same color
Color[] colors = new Color[clusterSet.size()];
double colorSpace = (256d * 256d * 256d);
double increment = (colorSpace / colors.length);
//Logger.getLogger(ClusterViewer.class.getName()).log(Level.INFO, String.format("increment is %f",increment));
double color = 0;
for (int ii = 0; ii < colors.length; ii++) {
double rd, gn, be;
rd = color % 256;
gn = (color / 256) % 256;
be = (color / (256 * 256)) % 256;
rd = rd / 256;
gn = gn / 256;
be = be / 256;
color += increment;
//255 255 255
//255 0 0
//0 1
//Logger.getLogger(ClusterViewer.class.getName()).log(Level.INFO, String.format("%f%f%f",rd,gn,be));
colors[ii] = new Color((float) rd, (float) gn, (float) be);
}
for (Iterator<Set<Node>> cIt = clusterSet.iterator(); cIt.hasNext();) {
Set<Node> vertices = cIt.next();
Color c = colors[i % colors.length];
colorCluster(vertices, c);
if (cm.isGroupClusters()) {
groupCluster(layout, vertices);
}
i++;
}
for (String e : gr.getEdges()) {
/* if (edges.contains(e)) {
edgePaints.put(e, Color.lightGray);
} else {
edgePaints.put(e, Color.black);
}*/
}
}
final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
pnlJUNGView.add(panel);
final AbstractModalGraphMouse graphMouse = new DefaultModalGraphMouse<String, Number>();
vv.setGraphMouse(graphMouse);
vv.addKeyListener(graphMouse.getModeKeyListener());
vv.setToolTipText("<html><center>Type 'p' for Pick mode<p>Type 't' for Transform mode");
//vv.setPreferredSize(new Dimension(5000, 5000));
//pnlJUNGView.repaint();
//vv.getGraphLayout().initialize();
vv.repaint();
//Application a = Application.getApplication();
//a.setDockIconBadge("DONE");
} catch (Exception e) {
Logger.getLogger(ClusterViewer.class.getName()).log(Level.WARNING, String.format("Line %s", (Object) e.getStackTrace()) + e);
}
}
private void colorCluster(Set<Node> vertices, Color c) {
for (Node v : vertices) {
vertexPaints.put(v, c);
}
}
private void groupCluster(AggregateLayout<Node, String> layout, Set<Node> vertices) {
//Logger.getLogger(ClusterViewer.class.getName()).log(Level.INFO, String.format("Clustering with %d nodes",vertices.size()));
if (vertices.size() < layout.getGraph().getVertexCount()) {
Point2D center = layout.transform(vertices.iterator().next());
Graph<Node, String> subGraph = DirectedSparseMultigraph.<Node, String>getFactory().create();
for (Node v : vertices) {
subGraph.addVertex(v);
}
Layout<Node, String> subLayout =
new CircleLayout<Node, String>(subGraph);
subLayout.setInitializer(vv.getGraphLayout());
subLayout.setSize(new Dimension(40, 40));
layout.put(subLayout, center);
vv.repaint();
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
jPanel1 = new javax.swing.JPanel();
pnlJUNGView = new javax.swing.JPanel();
jPanel5 = new javax.swing.JPanel();
statusBar = new javax.swing.JLabel();
jPanel2 = new javax.swing.JPanel();
jPanel3 = new javax.swing.JPanel();
txtSILName = new javax.swing.JTextField();
txtDOTName = new javax.swing.JTextField();
btnDoLayout = new javax.swing.JButton();
cmbInputFormat = new javax.swing.JComboBox();
jPanel4 = new javax.swing.JPanel();
cmbLayouts = new javax.swing.JComboBox();
btnGroupClusters = new javax.swing.JToggleButton();
btnRepaint = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("SeBaGCT Graph Visualiser");
setBackground(java.awt.SystemColor.window);
jPanel1.setMinimumSize(new java.awt.Dimension(10, 200));
jPanel1.setPreferredSize(new java.awt.Dimension(10, 200));
org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(0, 10, Short.MAX_VALUE)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(0, 688, Short.MAX_VALUE)
);
getContentPane().add(jPanel1, java.awt.BorderLayout.LINE_START);
pnlJUNGView.setBorder(javax.swing.BorderFactory.createEtchedBorder());
pnlJUNGView.setMinimumSize(new java.awt.Dimension(200, 200));
pnlJUNGView.addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentResized(java.awt.event.ComponentEvent evt) {
pnlJUNGViewComponentResized(evt);
}
});
pnlJUNGView.setLayout(new java.awt.BorderLayout());
getContentPane().add(pnlJUNGView, java.awt.BorderLayout.CENTER);
jPanel5.setLayout(new java.awt.GridBagLayout());
statusBar.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
statusBar.setText("Ready");
statusBar.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.ipadx = 9;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(4, 6, 4, 6);
jPanel5.add(statusBar, gridBagConstraints);
getContentPane().add(jPanel5, java.awt.BorderLayout.PAGE_END);
jPanel2.setLayout(new java.awt.GridBagLayout());
jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Input"));
jPanel3.setLayout(new java.awt.GridBagLayout());
txtSILName.setText("/Users/mat/Dropbox/graphs/eurace/sil/eurace201011301649.sil");
txtSILName.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
txtSILNameActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
jPanel3.add(txtSILName, gridBagConstraints);
txtDOTName.setText("/Users/mat/Dropbox/graphs/eurace.dot");
txtDOTName.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
txtDOTNameActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
jPanel3.add(txtDOTName, gridBagConstraints);
btnDoLayout.setText("Load");
btnDoLayout.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnDoLayoutActionPerformed(evt);
}
});
jPanel3.add(btnDoLayout, new java.awt.GridBagConstraints());
cmbInputFormat.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "DOT", "ClusterHead" }));
cmbInputFormat.setName("cmbInputFormat"); // NOI18N
cmbInputFormat.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cmbInputFormatActionPerformed(evt);
}
});
jPanel3.add(cmbInputFormat, new java.awt.GridBagConstraints());
jPanel2.add(jPanel3, new java.awt.GridBagConstraints());
jPanel4.setBorder(javax.swing.BorderFactory.createTitledBorder("Layout Method"));
jPanel4.add(cmbLayouts);
btnGroupClusters.setSelected(true);
btnGroupClusters.setText("Group Clusters");
btnGroupClusters.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
btnGroupClustersMouseClicked(evt);
}
});
jPanel4.add(btnGroupClusters);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
jPanel2.add(jPanel4, gridBagConstraints);
btnRepaint.setText("Draw Graph");
btnRepaint.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
btnRepaintMouseClicked(evt);
}
});
btnRepaint.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnRepaintActionPerformed(evt);
}
});
jPanel2.add(btnRepaint, new java.awt.GridBagConstraints());
getContentPane().add(jPanel2, java.awt.BorderLayout.PAGE_START);
pack();
}// </editor-fold>//GEN-END:initComponents
private void btnDoLayoutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDoLayoutActionPerformed
// TODO add your handling code here:
cm.setDotName(txtDOTName.getText());
cm.setSilName(txtSILName.getText());
cm.setLayout((Class) cmbLayouts.getSelectedItem());
cm.setClusterHead(((String)(cmbInputFormat.getSelectedItem())).equals("ClusterHead") );
controller.loadGraph();
}//GEN-LAST:event_btnDoLayoutActionPerformed
private void txtDOTNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtDOTNameActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_txtDOTNameActionPerformed
private void pnlJUNGViewComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_pnlJUNGViewComponentResized
// TODO add your handling code here:
//drawGraph();
}//GEN-LAST:event_pnlJUNGViewComponentResized
private void txtSILNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_txtSILNameActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_txtSILNameActionPerformed
private void btnRepaintActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRepaintActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_btnRepaintActionPerformed
private void btnRepaintMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_btnRepaintMouseClicked
// TODO add your handling code here:
cm.setLayout((Class) cmbLayouts.getSelectedItem());
drawGraph();
}//GEN-LAST:event_btnRepaintMouseClicked
private void btnGroupClustersMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_btnGroupClustersMouseClicked
// TODO add your handling code here:
cm.setGroupClusters(!cm.isGroupClusters());
btnGroupClusters.setSelected(cm.isGroupClusters());
}//GEN-LAST:event_btnGroupClustersMouseClicked
private void cmbInputFormatActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmbInputFormatActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cmbInputFormatActionPerformed
public void updateGUI() {
statusBar.setText(cm.getStatus());
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ClusterViewer().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnDoLayout;
private javax.swing.JToggleButton btnGroupClusters;
private javax.swing.JButton btnRepaint;
private javax.swing.JComboBox cmbInputFormat;
private javax.swing.JComboBox cmbLayouts;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JPanel jPanel3;
private javax.swing.JPanel jPanel4;
private javax.swing.JPanel jPanel5;
private javax.swing.JPanel pnlJUNGView;
private javax.swing.JLabel statusBar;
private javax.swing.JTextField txtDOTName;
private javax.swing.JTextField txtSILName;
// End of variables declaration//GEN-END:variables
}