/*
* CCVisu is a tool for visual graph clustering
* and general force-directed graph layout.
* This file is part of CCVisu.
*
* Copyright (C) 2005-2007 Dirk Beyer
*
* CCVisu is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* CCVisu 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CCVisu; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please find the GNU Lesser General Public License in file
* license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
*
* Dirk Beyer (firstname.lastname@sfu.ca)
* Simon Fraser University (SFU), B.C., Canada
*/
package ccvisu;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
/*****************************************************************
* Main class of the CCVisu package.
* Contains the main pogram and some auxiliary methods.
* @version $Revision$; $Date$
* @author Dirk Beyer
*****************************************************************/
public class CCVisu {
/** End of line.*/
public final static String endl = System.getProperty("line.separator");
/** Global options **/
private static boolean hideSource;
private static int verbosityLevel;
// Format identifiers.
/** CVS log format (only input).*/
public final static int CVS = 0;
/** Graph (relation) in relational standard format.*/
public final static int RSF = 1;
/** Graph layout in textual format.*/
public final static int LAY = 2;
/** Graph layout in VRML format (only output).*/
public final static int VRML = 3;
/** Graph layout in SVG format (only output).*/
public final static int SVG = 4;
/** Display graph layout on screen (only output).*/
public final static int DISP = 5;
// Marker.
// Emphasize, i.e., add annotation of vertex name for some vertices.
// Change to a subclass of Marker.
public static Marker marker = new Marker();
/*****************************************************************
* Main program. Performs the following steps.
* 1) Parses and handles the command line options.
* 2) Creates the appropriate input reader and reads the input.
* 3) Computes the layout (if necessary).
* 4) Creates the appropriate output writer and writes the output.
* @param args Command line arguments.
*****************************************************************/
public static void main(String[] args) {
if (args.length == 0) {
printHelp();
System.exit(0);
}
// Default I/O.
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(System.out)));
// General.
CCVisu.verbosityLevel = 0;
// Input format
int inFormat = RSF;
String inputName = ""; // Empty string for no input file name (standard input).
// Output format.
int outFormat = DISP;
// For CVS reader. Time constant for sliding window.
int timeWindow = 180000;
boolean sliding = false;
// For layout.
int nrDim = 2;
int nrIterations = 100;
GraphData initialLayout = null;
boolean fixedInitPos = false;
// For energy model.
// Exponent of the Euclidian distance in the attraction term of the energy (default: 1).
float attrExponent = 1.0f;
// Exponent of the Euclidian distance in the repulsion term of the energy (default: 0).
float repuExponent = 0.0f;
boolean vertRepu = false;
boolean noWeight = false;
float gravitation = 0.001f;
// For layout output.
CCVisu.hideSource = false;
float minVert = 2.0f;
int fontSize = 14;
Color backColor = Color.WHITE;
boolean blackCircle = true;
boolean showEdges = false;
float scalePos = 1.0f;
// If true, the layout is already displayed while the minimizer is still improving it,
// and a simple mouse click on the canvas updates the current layout on the screen.
// If false, the layout is displayed only after minimization is completed.
boolean anim = true;
boolean annotAll = false;
boolean annotNone = false;
boolean URL = false;
String browser = null;
// Parse command-line options.
for (int i = 0; i < args.length; ++i) {
// General options without argument.
// Help.
if (args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("--help")) {
printHelp();
out.close();
System.exit(0);
}
// Version.
else if (args[i].equalsIgnoreCase("-v") || args[i].equalsIgnoreCase("--version")) {
printVersion();
out.close();
System.exit(0);
}
// Quiet.
else if (args[i].equalsIgnoreCase("-q") || args[i].equalsIgnoreCase("--nowarnings")) {
CCVisu.verbosityLevel = 0;
}
// Warnings on.
else if (args[i].equalsIgnoreCase("-w") || args[i].equalsIgnoreCase("--warnings")) {
CCVisu.verbosityLevel = 1;
}
// Verbose.
else if (args[i].equalsIgnoreCase("-verbose")) {
CCVisu.verbosityLevel = 2;
}
// General options with argument.
// Change input reader.
else if (args[i].equalsIgnoreCase("-i")) {
++i;
chkAvail(args, i);
try {
inputName = args[i];
in = new BufferedReader(new FileReader(args[i]));
}
catch (Exception e) {
System.err.println("Exception while opening file '" + args[i] + "' for reading: ");
System.err.println(e);
System.exit(1);
}
}
// Change output writer.
else if (args[i].equalsIgnoreCase("-o")) {
++i;
chkAvail(args, i);
try {
out = new PrintWriter(new BufferedWriter(new FileWriter(args[i])));
}
catch (Exception e) {
System.err.println("Exception while opening file '" + args[i] + "' for writing: ");
System.err.println(e);
System.exit(1);
}
}
// Input format.
else if (args[i].equalsIgnoreCase("-inFormat")) {
++i;
chkAvail(args, i);
inFormat = getFormat(args[i]);
if (inFormat > LAY) {
System.err.println("Usage error: '" + args[i] + "' is not supported as input format.");
System.exit(1);
}
}
// Output format.
else if (args[i].equalsIgnoreCase("-outFormat")) {
++i;
chkAvail(args, i);
outFormat = getFormat(args[i]);
if (outFormat < RSF) {
System.err.println("Usage error: '" + args[i] + "' is not supported as output format.");
System.exit(1);
}
}
// Options for CVS reader.
// Time-constant of sliding-window for change transaction recovery
// (in milli-seconds).
else if (args[i].equalsIgnoreCase("-timeWindow")) {
++i;
chkAvail(args, i);
timeWindow = Integer.parseInt(args[i]);
}
// sliding/fixed-window for change transaction recovery
else if (args[i].equalsIgnoreCase("-slidingTW")) {
sliding = true;
}
// Options for layout.
// Number of dimensions (up to 3).
else if (args[i].equalsIgnoreCase("-dim")) {
++i;
chkAvail(args, i);
nrDim = Integer.parseInt(args[i]);
}
// Number of iterations for minimization.
else if (args[i].equalsIgnoreCase("-iter")) {
++i;
chkAvail(args, i);
nrIterations = Integer.parseInt(args[i]);
}
// Initial layout.
else if (args[i].equalsIgnoreCase("-initLayout")) {
++i;
chkAvail(args, i);
BufferedReader initialLayoutStream = null;
initialLayout = new GraphData();
try {
initialLayoutStream = new BufferedReader(new FileReader(args[i]));
}
catch (Exception e) {
System.err.println("Exception while opening file '" + args[i] + "' for reading: ");
System.err.println(e);
}
// Read initial (pre-computed) layout from file.
(new ReaderDataLAY(initialLayoutStream)).read(initialLayout);
if (CCVisu.verbosityLevel >= 2) {
System.err.println("" + initialLayout.vertices.size() + " vertices read.");
System.err.println("Initial layout reading finished.");
}
// Close the input file.
try {
initialLayoutStream.close();
} catch (Exception e) {
System.err.println("Exception while closing input file: ");
System.err.println(e);
}
// Reset vertex degrees,
// i.e., use the degrees from the graph and ignore the degree from read layout.
for (int j = 0; j < initialLayout.vertices.size(); ++j) {
initialLayout.vertices.get(j).degree = 0;
}
}
// Fixed positions for nodes in the initial layout given by option -initLayout.
else if (args[i].equalsIgnoreCase("-fixedInitPos")) {
fixedInitPos = true;
}
// Energy model.
// Attraction exponent.
else if (args[i].equalsIgnoreCase("-attrExp")) {
++i;
chkAvail(args, i);
attrExponent = Float.parseFloat(args[i]);
}
// Repulsion exponent.
else if (args[i].equalsIgnoreCase("-repuExp")) {
++i;
chkAvail(args, i);
repuExponent = Float.parseFloat(args[i]);
}
// Node repulsion.
else if (args[i].equalsIgnoreCase("-vertRepu")) {
vertRepu = true;
}
// No weights.
else if (args[i].equalsIgnoreCase("-noWeight")) {
noWeight = true;
}
// Gravitation factor.
else if (args[i].equalsIgnoreCase("-grav")) {
++i;
chkAvail(args, i);
gravitation = Float.parseFloat(args[i]);
}
// Options for output writers.
// Show source vertices (first vertex of an edge).
else if (args[i].equalsIgnoreCase("-hideSource")) {
hideSource = true;
}
// Scale circles for vertices in the layout.
else if (args[i].equalsIgnoreCase("-minVert")) {
++i;
chkAvail(args, i);
minVert = Float.parseFloat(args[i]);
}
// Font size for annotations in the layout.
else if (args[i].equalsIgnoreCase("-fontSize")) {
++i;
chkAvail(args, i);
fontSize = Integer.parseInt(args[i]);
}
// Background color.
else if (args[i].equalsIgnoreCase("-backcolor")) {
++i;
chkAvail(args, i);
if (args[i].equalsIgnoreCase("black")) {
backColor = Color.BLACK;
} else if (args[i].equalsIgnoreCase("white")) {
backColor = Color.WHITE;
} else if (args[i].equalsIgnoreCase("gray")) {
backColor = Color.GRAY;
} else if (args[i].equalsIgnoreCase("lightgray")) {
backColor = Color.LIGHT_GRAY;
} else {
System.err.println("Usage error: Color '" + args[i] + "' unknown.");
}
}
// Avoid black circles around the filled circles for vertices (strokes).
else if (args[i].equalsIgnoreCase("-noBlackCircle")) {
blackCircle = false;
}
// Show the Edges
else if (args[i].equalsIgnoreCase("-showEdges")) {
showEdges = true;
}
// Options for VRML writer.
// Scale positions in the layout.
else if (args[i].equalsIgnoreCase("-scalePos")) {
++i;
chkAvail(args, i);
scalePos = Float.parseFloat(args[i]);
}
// Only for display writer.
// Animation of layout during minimization, if outFormat is DISP.
else if (args[i].equalsIgnoreCase("-noAnim")) {
anim = false;
}
// For all.
// Annotate each vertex with its name.
else if (args[i].equalsIgnoreCase("-annotAll")) {
annotAll = true;
}
// Annotate no vertex.
else if (args[i].equalsIgnoreCase("-annotNone")) {
annotNone = true;
}
//Allow to open an the URLs
else if (args[i].equalsIgnoreCase("-openURL")) {
URL = true;
}
//the browser cmd
else if (args[i].equalsIgnoreCase("-browser")) {
++i;
chkAvail(args, i);
browser = args[i];
}
// Switch on user-defined marker.
// To emphasize certain vertices.
else if (args[i].equalsIgnoreCase("-mark")) {
System.err.println("Implement marker first.");
System.exit(1);
//marker = new MarkerExp();
}
else if (args[i].equalsIgnoreCase("-markScript")) {
++i;
chkAvail(args, i);
try {
marker = new MarkerScript(new BufferedReader(new FileReader(args[i])));
} catch (FileNotFoundException e) {
System.err.println("Impossible to read file: "+ args[i]);
marker = new Marker();
e.printStackTrace();
}
}
// Unknown option.
else {
System.err.println("Usage error: Option '" + args[i] + "' unknown.");
System.exit(1);
}
} // for parsing command-line options.
if (inFormat > outFormat) {
System.err.println("Usage error: Combination of input and output formats not supported.");
System.exit(1);
}
// Initialize the graph representation.
GraphData graph = new GraphData();
// Set input reader.
ReaderData graphReader; // Default: CVS log format.
graphReader = new ReaderDataGraphCVS(in, timeWindow, sliding);
if (inFormat == RSF) { // Graph in RSF format.
graphReader = new ReaderDataGraphRSF(in);
} else if (inFormat == LAY) { // Layout in text format LAY.
graphReader = new ReaderDataLAY(in);
}
// Read the data using the reader (i.e., fill into existing graph structure).
graphReader.read(graph);
if (CCVisu.verbosityLevel >= 2) {
System.err.println("" + graph.vertices.size() + " vertices read.");
System.err.println("Graph reading finished.");
}
// Close the input file.
try {
in.close();
} catch (Exception e) {
System.err.println("Exception while closing input file: ");
System.err.println(e);
}
// Handle vertex options.
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
// annotAll (annotate each vertex with its name).
if (annotAll) {
curVertex.showName = true;
}
// annotNone (annotate no vertex).
if (annotNone) {
curVertex.showName = false;
}
// hideSource (do not show vertex if it is source of an edge).
if (hideSource && curVertex.isSource) {
curVertex.showVertex = false;
}
}
// Output writer.
WriterData dataWriter = null;
// Determine if it is necessary to compute the layout.
if (inFormat < LAY && outFormat >= LAY) {
// Initialize layout.
initializeLayout(graph, nrDim, initialLayout);
// Show display for layout animation during minimization.
if (outFormat == DISP && anim) { // Display layout on screen already now.
dataWriter = new WriterDataGraphicsDISP(graph, minVert, fontSize, backColor,
blackCircle, showEdges, URL, inputName,
browser);
}
// Compute the layout.
if(dataWriter == null){
//no animation
computeLayout(graph, nrIterations, attrExponent, repuExponent,
vertRepu, noWeight, gravitation,
initialLayout, fixedInitPos, null);
}else{
//animation
WriterDataGraphicsDISP displ = (WriterDataGraphicsDISP) dataWriter;
GraphEventListener listener = displ.getDisplay();
computeLayout(graph, nrIterations, attrExponent, repuExponent,
vertRepu, noWeight, gravitation,
initialLayout, fixedInitPos, listener);
}
}
// Set output writer.
if (outFormat == RSF) { // Co-change graph in RSF.
dataWriter = new WriterDataRSF (graph, out);
} else if (outFormat == LAY) { // Layout in text format LAY.
dataWriter = new WriterDataLAY (graph, out);
} else if (outFormat == VRML) { // Layout in VRML format.
dataWriter = new WriterDataGraphicsVRML(graph, out, minVert, fontSize, backColor,
blackCircle, showEdges, URL, scalePos);
} else if (outFormat == SVG) { // Layout in SVG format.
dataWriter = new WriterDataGraphicsSVG (graph, out, minVert, fontSize, backColor,
blackCircle, showEdges, URL, scalePos, inputName);
} else if (outFormat == DISP && dataWriter == null) { // Display layout on screen
// ... if the view is not already there, i.e. animation is not activated.
dataWriter = new WriterDataGraphicsDISP(graph, minVert, fontSize, backColor,
blackCircle, showEdges, URL,
inputName, browser);
}
// Write the data using the writer.
dataWriter.write();
// Close the output file.
out.close();
}
/*****************************************************************
* Prints version information.
*****************************************************************/
private static void printVersion() {
System.out.println(
"CCVisu 2.1, 2007-12-12. " + endl
+ "Copyright (C) 2005-2007 Dirk Beyer (SFU, B.C., Canada). " + endl
+ "CCVisu is free software, released under the GNU LGPL. ");
}
/*****************************************************************
* Prints usage information.
*****************************************************************/
private static void printHelp() {
// Usage and info message.
System.out.print(
endl
+ "This is CCVisu, a tool for visual graph clustering " + endl
+ "and general force-directed graph layout. " + endl
+ " " + endl
+ "Usage: java ccvisu.CCVisu [OPTION]... " + endl
+ "Compute a layout for a given (co-change) graph (or convert). " + endl
+ " " + endl
+ "Options: " + endl
+ "General options: " + endl
+ " -h --help display this help message and exit. " + endl
+ " -v --version print version information and exit. " + endl
+ " -q --nowarnings quiet mode (default). " + endl
+ " -w --warnings enable warnings. " + endl
+ " -verbose verbose mode. " + endl
+ " -i <file> read input data from given file (default: stdin). " + endl
+ " -o <file> write output data to given file (default: stdout). " + endl
+ " -inFormat FORMAT read input data in format FORMAT (default: RSF, see below). " + endl
+ " -outFormat FORMAT write output data in format FORMAT (default: DISP, see below). " + endl
+ " " + endl
+ "Layouting options: " + endl
+ " -dim <int> number of dimensions of the layout (2 or 3, default: 2). " + endl
+ " -iter <int> number of iterations of the minimizer (default: 100). " + endl
+ " -initLayout <file> use layout from file (LAY format) as initial layout " + endl
+ " (default: random layout). " + endl
+ " " + endl
+ "Energy model options: " + endl
+ " -attrExp <int> exponent for the distance in the attraction term " + endl
+ " (default: 1). " + endl
+ " -repuExp <int> exponent for the distance in the repulsion term " + endl
+ " (default: 0). " + endl
+ " -vertRepu use vertex repulsion instead of edge repulsion " + endl
+ " (default: edge repulsion). " + endl
+ " -noWeight use unweighted model (default: weighted). " + endl
+ " -grav <float> gravitation factor for the Barnes-Hut-procedure " + endl
+ " (default: 0.001). " + endl
+ " " + endl
+ "CVS reader option: " + endl
+ " -timeWindow <int> time window for transaction recovery, in milli-seconds " + endl
+ " (default: 180'000). " + endl
+ " -slidingTW the time window 'slides': a new commit nodes is created" + endl
+ " when the time difference between two commited files is bigger" + endl
+ " than the time window (default: fixed time window)." + endl
+ " " + endl
+ "Layout writer options: " + endl
+ " -hideSource draw only vertices that are not source of an edge. " + endl
+ " In co-change graphs, all change-transaction vertices " + endl
+ " are source vertices (default: no hide). " + endl
+ " -minVert <float> size of the smallest vertex (diameter, default: 2.0). " + endl
+ " -fontSize <int> font size of vertex annotations (default: 14). " + endl
+ " -backColor COLOR background color (default: WHITE). " + endl
+ " Colors: BLACK, GRAY, LIGHTGRAY, WHITE." + endl
+ " -noBlackCircle no black circle around each vertex (default: with). " + endl
+ " -showEdges Show the edges of the graph (available only for CVS and RFS inFomat)" + endl
+ " (default: hide)" + endl
+ " -scalePos <float> scaling factor for the layout to adjust " + endl
+ " (VRML and SVG only, default: 1.0). " + endl
+ " -noAnim don't show layout while minimizer is still improving it " + endl
+ " (default: show). " + endl
+ " -annotAll annotate each vertex with its name (default: no). " + endl
+ " -annotNone annotate no vertex (default: no). " + endl
+ " -mark highlight vertices using the MarkerExp class " + endl
+ " (see source code for more details)" + endl
+ " -markScript<file> highlight vertices using condition parsed from a file " + endl
+ " (see file marker_script.example to see how it works)" + endl
+ " -openURL The node's name can be considered as URL and opened in a web Broswer. " + endl
+ " This option used with DISP output require to hold CTRL KEY while clicking" + endl
+ " " + endl
+ "DISP specific option" + endl
+ " -browser <Cmd> The browser command. if not available, CCVisu will try to guess." + endl
+ " " + endl
+ "Formats: " + endl
+ " CVS CVS log format (only input). " + endl
+ " RSF graph in relational standard format. " + endl
+ " LAY graph layout in textual format. " + endl
+ " VRML graph layout in VRML format (only output). " + endl
+ " SVG graph layout in SVG format (only output). " + endl
+ " DISP display gaph layout on screen (only output). " + endl
+ "To produce a file for input format CVS log, use e.g. 'cvs log -Nb'. " + endl
+ " " + endl
+ "http://www.cs.sfu.ca/~dbeyer/CCVisu/ " + endl
+ " " + endl
+ "Report bugs to Dirk Beyer <firstname.lastname@sfu.ca>. " + endl
+ " " + endl
);
}
/*****************************************************************
* Transforms the format given as a string into the appropriate integer value.
* @param format File format string to be transformed to int.
* @return File format identifier.
*****************************************************************/
private static int getFormat(String format) {
int result = 0;
if (format.equalsIgnoreCase("CVS")) {
result = CVS;
} else if (format.equalsIgnoreCase("RSF")) {
result = RSF;
} else if (format.equalsIgnoreCase("LAY")) {
result = LAY;
} else if (format.equalsIgnoreCase("VRML")) {
result = VRML;
} else if (format.equalsIgnoreCase("SVG")) {
result = SVG;
} else if (format.equalsIgnoreCase("DISP")) {
result = DISP;
} else {
System.err.println("Usage error: '" + format + "' is not a valid format.");
System.exit(1);
}
return result;
}
/*****************************************************************
* Checks whether the command line argument at index i has a follower argument.
* If there is no follower argument, it exits the program.
* @param args String array containing the command line arguments.
* @param i Index to check.
*****************************************************************/
private static void chkAvail(String[] args, int i) {
if (i == args.length) {
System.err.println("Usage error: Option '" + args[i-1]
+ "' requires an argument (file).");
System.exit(1);
}
}
/*****************************************************************
* Compute randomized initial layout for a given graph
* with the given number of dimensions.
* @param graph Graph representation, in/out parameter.
* @param nrDim Number of dimensions for the initial graph.
* @param initialLayout Initial layout representaiton as read from file.
*****************************************************************/
public static void initializeLayout(GraphData graph,
int nrDim,
GraphData initialLayout) {
// Initialize with random positions.
graph.pos = new float[graph.vertices.size()][3];
for (int i = 0; i < graph.vertices.size(); ++i) {
graph.pos[i][0] = 2 * (float) Math.random() - 1;
if (nrDim >= 2) {
graph.pos[i][1] = 2 * (float) Math.random() - 1;
} else {
graph.pos[i][2] = 0;
}
if (nrDim == 3) {
graph.pos[i][2] = 2 * (float) Math.random() - 1;
} else {
graph.pos[i][2] = 0;
}
}
// Copy positions from the initial layout that was read from file.
if (initialLayout != null) {
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
GraphVertex oldVertex = initialLayout.nameToVertex.get(curVertex.name);
if (oldVertex != null) {
graph.pos[i][0] = initialLayout.pos[oldVertex.id][0];
graph.pos[i][1] = initialLayout.pos[oldVertex.id][1];
graph.pos[i][2] = initialLayout.pos[oldVertex.id][2];
}
}
}
//mark the nodes
mark(marker,graph);
}
/*****************************************************************
* Compute layout for a given graph.
* @param graph In/Out parameter representing the graph.
* @param nrIterations Number of iterations.
* @param attrExponent Exponent of the Euclidian distance in the attraction term
* of the energy (default: 1).
* @param vertRepu Use vertex repulsion instead of edge repulsion,
* true for vertex repulsion, false for edge repulsion
* (default: edge repulsion).
* @param noWeight Use unweighted model by ignoring the edge weights,
* true for unweighted, false for weighted
* (default: weighted).
* @param gravitation Gravitation factor for the Barnes-Hut-procedure,
* attraction to the barycenter
* (default: 0.001).
* @param listener A listener that to the graph's changes
*****************************************************************/
public static void computeLayout(GraphData graph,
int nrIterations,
float attrExponent,
float repuExponent,
boolean vertRepu,
boolean noWeight,
float gravitation,
GraphData initialLayout,
boolean fixedInitPos,
GraphEventListener listener) {
// Create graph layout data structure, allocate memory.
int verticeNr = graph.vertices.size();
// Positions are already initialized.
// Initialize repulsions.
float[] repu = new float[verticeNr];
for (int i = 0; i < verticeNr; ++i) {
// Set repulsion according to the energy model.
if (vertRepu) {
repu[i] = 1.0f;
} else {
GraphVertex curVertex = graph.vertices.get(i);
repu[i] = curVertex.degree;
}
}
// Initialize attractions.
// Vertex indexes of the similarity lists.
int[][] attrIndexes = new int[verticeNr][];
// Similarity values of the similarity lists.
float[][] attrValues = new float[verticeNr][];
{
// Compute length of row lists.
int[] attrCounter = new int[verticeNr];
for (int i = 0; i < graph.edges.size(); ++i) {
GraphEdgeInt e = graph.edges.get(i);
if (e.x == e.y) {
if (CCVisu.verbosityLevel >= 1) {
GraphVertex curVertex = graph.vertices.get(e.x);
System.err.println("Layout warning: Reflexive edge for vertex '" +
curVertex.name + "' found." );
}
} else {
++attrCounter[e.x];
++attrCounter[e.y];
}
}
// Allocate the rows.
for (int i = 0; i < verticeNr; i++) {
attrIndexes[i] = new int[attrCounter[i]];
attrValues[i] = new float[attrCounter[i]];
}
// Transfer the edges to the similarity lists.
attrCounter = new int[verticeNr];
for (int i = 0; i < graph.edges.size(); ++i) {
GraphEdgeInt e = graph.edges.get(i);
if (e.x != e.y) {
// Similarity list must be symmetric.
attrIndexes[e.x][attrCounter[e.x]] = e.y;
attrIndexes[e.y][attrCounter[e.y]] = e.x;
// Set similarities according to the energy model.
if (noWeight) {
attrValues[e.x][attrCounter[e.x]] = 1.0f;
attrValues[e.y][attrCounter[e.y]] = 1.0f;
} else {
attrValues[e.x][attrCounter[e.x]] = e.w;
attrValues[e.y][attrCounter[e.y]] = e.w;
}
++attrCounter[e.x];
++attrCounter[e.y];
}
}
}
// fixedPos[i] == true means that the minimizer does not change
// the i-th vertex's position.
boolean[] fixedPos = new boolean[verticeNr];
if (fixedInitPos && initialLayout != null) {
for (int i = 0; i < verticeNr; i++) {
// If the current vertex exists in the read initial layout,
// then fix its position.
GraphVertex curVertex = graph.vertices.get(i);
if (initialLayout.nameToVertex.containsKey(curVertex.name) ) {
fixedPos[i] = true;
}
}
}
// Set minimizing algorithm. So far there is only one implemented in CCVisu.
Minimizer minimizer
= new MinimizerBarnesHut(verticeNr, attrIndexes, attrValues,
repu, graph.pos, fixedPos,
attrExponent, repuExponent, gravitation);
//Add GraphEventListener.
if(listener != null){
minimizer.addGraphEventListener(listener);
}
// Compute layout.
minimizer.minimizeEnergy(nrIterations);
}
/*****************************************************************
* Compute layout for a given graph.
* @param graph In/Out parameter representing the graph.
* @param nrIterations Number of iterations.
* @param attrExponent Exponent of the Euclidian distance in the attraction term
* of the energy (default: 1).
* @param vertRepu Use vertex repulsion instead of edge repulsion,
* true for vertex repulsion, false for edge repulsion
* (default: edge repulsion).
* @param noWeight Use unweighted model by ignoring the edge weights,
* true for unweighted, false for weighted
* (default: weighted).
* @param gravitation Gravitation factor for the Barnes-Hut-procedure,
* attraction to the barycenter
* (default: 0.001).
* @param listener A listener that to the graph's changes
*****************************************************************/
public static Minimizer computeLayout(GraphData graph,
int nrIterations,
float attrExponent,
float repuExponent,
boolean vertRepu,
boolean noWeight,
float gravitation,
GraphData initialLayout,
boolean fixedInitPos) {
// Create graph layout data structure, allocate memory.
int verticeNr = graph.vertices.size();
// Positions are already initialized.
// Initialize repulsions.
float[] repu = new float[verticeNr];
for (int i = 0; i < verticeNr; ++i) {
// Set repulsion according to the energy model.
if (vertRepu) {
repu[i] = 1.0f;
} else {
GraphVertex curVertex = graph.vertices.get(i);
repu[i] = curVertex.degree;
}
}
// Initialize attractions.
// Vertex indexes of the similarity lists.
int[][] attrIndexes = new int[verticeNr][];
// Similarity values of the similarity lists.
float[][] attrValues = new float[verticeNr][];
{
// Compute length of row lists.
int[] attrCounter = new int[verticeNr];
for (int i = 0; i < graph.edges.size(); ++i) {
GraphEdgeInt e = graph.edges.get(i);
if (e.x == e.y) {
if (CCVisu.verbosityLevel >= 1) {
GraphVertex curVertex = graph.vertices.get(e.x);
System.err.println("Layout warning: Reflexive edge for vertex '" +
curVertex.name + "' found." );
}
} else {
++attrCounter[e.x];
++attrCounter[e.y];
}
}
// Allocate the rows.
for (int i = 0; i < verticeNr; i++) {
attrIndexes[i] = new int[attrCounter[i]];
attrValues[i] = new float[attrCounter[i]];
}
// Transfer the edges to the similarity lists.
attrCounter = new int[verticeNr];
for (int i = 0; i < graph.edges.size(); ++i) {
GraphEdgeInt e = graph.edges.get(i);
if (e.x != e.y) {
// Similarity list must be symmetric.
attrIndexes[e.x][attrCounter[e.x]] = e.y;
attrIndexes[e.y][attrCounter[e.y]] = e.x;
// Set similarities according to the energy model.
if (noWeight) {
attrValues[e.x][attrCounter[e.x]] = 1.0f;
attrValues[e.y][attrCounter[e.y]] = 1.0f;
} else {
attrValues[e.x][attrCounter[e.x]] = e.w;
attrValues[e.y][attrCounter[e.y]] = e.w;
}
++attrCounter[e.x];
++attrCounter[e.y];
}
}
}
// fixedPos[i] == true means that the minimizer does not change
// the i-th vertex's position.
boolean[] fixedPos = new boolean[verticeNr];
if (fixedInitPos && initialLayout != null) {
for (int i = 0; i < verticeNr; i++) {
// If the current vertex exists in the read initial layout,
// then fix its position.
GraphVertex curVertex = graph.vertices.get(i);
if (initialLayout.nameToVertex.containsKey(curVertex.name) ) {
fixedPos[i] = true;
}
}
}
// Set minimizing algorithm. So far there is only one implemented in CCVisu.
Minimizer minimizer
= new MinimizerBarnesHut(verticeNr, attrIndexes, attrValues,
repu, graph.pos, fixedPos,
attrExponent, repuExponent, gravitation);
//Add GraphEventListener.
//if(listener != null){
// minimizer.addGraphEventListener(listener);
//}
// Compute layout.
return minimizer;//.minimizeEnergy(nrIterations);
}
/*****************************************************************
* Get value of option hideSource.
* @return Value of option hideSource.
*****************************************************************/
public static boolean getHideSource() {
return hideSource;
}
/*****************************************************************
* Get value of option verbosityLevel.
* @return Value of option verbosityLevel.
*****************************************************************/
public static int getVerbosityLevel() {
return verbosityLevel;
}
/**
* Highlight nodes
* @param m the marker to use
* @param graph the graph to mark
*/
public static void mark(Marker m, GraphData graph){
int end = graph.vertices.size();
for(int i = 0; i < end; ++i){
GraphVertex curVertex = graph.vertices.get(i);
m.mark(curVertex);
}
}
public final static Color white = new Color(255, 255, 255);
public final static Color lightGray = new Color(192, 192, 192);
public final static Color gray = new Color(128, 128, 128);
public final static Color darkGray = new Color(64, 64, 64);
public final static Color black = new Color(0, 0, 0);
public final static Color red = new Color(255, 0, 0);
public final static Color green = new Color(0, 255, 0);
public final static Color blue = new Color(0, 0, 255);
public final static Color yellow = new Color(255, 255, 0);
public final static Color magenta = new Color(255, 0, 255);
public final static Color cyan = new Color(0, 255, 255);
public final static Color lightRed = new Color(255, 128, 128);
public final static Color lightGreen = new Color(128, 255, 128);
public final static Color lightBlue = new Color(128, 128, 255);
public final static Color darkYellow = new Color(128, 128, 0);
public final static Color darkMagenta = new Color(128, 0, 128);
public final static Color darkCyan = new Color(0, 128, 128);
public final static Color pink = new Color(255, 175, 175);
public final static Color orange = new Color(255, 200, 0);
public final static Color chocolate4 = new Color(139, 69, 19);
public final static Color darkOliveGreen4 = new Color(110, 139, 61);
};