/*
* 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.util.Iterator;
import java.util.Vector;
/**
* A class with a list of nodes that compute some informations on them
*
* @version $Revision$; $Date$
* @author Damien Zufferey
*/
public class Cluster {
/**used as mode for the method addPattern*/
public static final int EQUALS = 0;
/**used as mode for the method addPattern*/
public static final int CONTAINS = 1;
/**used as mode for the method addPattern*/
public static final int STARTS = 2;
/**used as mode for the method addPattern*/
public static final int ENDS = 3;
/**contain the name of node's cluster*/
private static String[] indexToCltName;
/**name of the cluster*/
private String name;
/**list of int representing the index of nodes in the GraphData*/
private Vector<Integer> nodes;
/**color of the cluster*/
private Color color;
/**x-coordinate of the barycenter*/
private float x;
/**y-coordinate of the barycenter*/
private float y;
/**z-coordinate of the barycenter*/
private float z;
private float averageRadius;
/** pointer to the data*/
private static GraphData graph;
private static WriterDataGraphicsDISP writer;
/**used to know if the cluster should be drawn*/
public boolean visible = true;
/**used to know if the circle and cross should be drawn */
public boolean info = false;
/** true if needs to recompute radius, center,... */
private boolean changed = true;
/**
* Constructor
* @param name the cluster's name
*/
public Cluster(String name){
this.name = name;
color = CCVisu.red;
nodes = new Vector<Integer>();
}
/**
* Constructor
* @param name the cluster's name
* @param color the cluster's color
*/
public Cluster(String name, Color color){
this.name = name;
this.color = color;
nodes = new Vector<Integer>();
}
/**
* add the given node to the cluster
* @param vertex
*/
public void addNode(GraphVertex vertex){
int index = graph.vertices.indexOf(vertex);
nodes.add(new Integer(index));
vertex.color = color;
changed = true;
Cluster clt = writer.getCluster(indexToCltName[index]);
if(clt != null){
clt.removeNodeByIndex(index);
}
indexToCltName[index] = name;
}
/**
* add the node that corresponds to the index-th node in graph(GraphData)
* @param index
*/
public void addNodeByIndex(int index){
nodes.add(new Integer(index));
GraphVertex vertex = graph.vertices.get(index);
vertex.color = color;
changed = true;
Cluster clt = writer.getCluster(indexToCltName[index]);
if(clt != null){
clt.removeNodeByIndex(index);
}
indexToCltName[index] = name;
}
/**
* add the node that corresponds to the index-th node in graph(GraphData)
* without changing his color
* function used only to assign to default cluster at begining
* @param index
*/
public void addNodeByIndex_WO_COLOR(int index){
nodes.add(new Integer(index));
changed = true;
Cluster clt = writer.getCluster(indexToCltName[index]);
if(clt != null){
clt.removeNodeByIndex(index);
}
indexToCltName[index] = name;
}
/**
* remove from cluster the given node
* @param vertex
*/
public void removeNode(GraphVertex vertex){
nodes.remove(new Integer(graph.vertices.indexOf(vertex)));
changed = true;
}
/**
* remove the node that corresponds to the index-th node in graph(GraphData)
* @param index
*/
public void removeNodeByIndex(int index){
nodes.remove(new Integer(index));
changed = true;
}
/**
* return an iterator on the index of the cluster's nodes
* @return iterator
*/
public Iterator<Integer> Iterator(){
return nodes.iterator();
}
/**
* adds nodes to a cluster in function of a given pattern
* @param pattern
* @param mode the way of using the pattern.
*/
public void addPattern(String pattern, int mode){
for (int i = 0; i < graph.vertices.size(); ++i) {
GraphVertex curVertex = graph.vertices.get(i);
if(mode == EQUALS){
if(curVertex.name.equals(pattern)){
addNodeByIndex(i);
}
}else if(mode == CONTAINS){
if(curVertex.name.matches(".*"+pattern+".*")){
addNodeByIndex(i);
}
}else if(mode == STARTS){
if(curVertex.name.startsWith(pattern)){
addNodeByIndex(i);
}
}else if(mode == ENDS){
if(curVertex.name.endsWith(pattern)){
addNodeByIndex(i);
}
}
}
}
public void filter(String pattern, int mode, boolean keep){
int end = size();
boolean match[] = new boolean[end];
for (int i = 0; i < end; ++i) {
match[i] = !keep;
GraphVertex curVertex = getNode(i);
if(mode == EQUALS){
if(curVertex.name.equals(pattern)){
match[i] = keep;
}
}else if(mode == CONTAINS){
if(curVertex.name.matches(".*"+pattern+".*")){
match[i] = keep;
}
}else if(mode == STARTS){
if(curVertex.name.startsWith(pattern)){
match[i] = keep;
}
}else if(mode == ENDS){
if(curVertex.name.endsWith(pattern)){
match[i] = keep;
}
}
}
GraphVertex selected[] = new GraphVertex[end];
for(int i = 0; i<end; ++i){
if(!match[i]){
selected[i] = getNode(i);
}
}
Cluster defaultClt = writer.getCluster(0);
for(int i = 0; i<end; ++i){
if(selected[i] != null){
defaultClt.addNode(selected[i]);
}
}
}
/**
* return the i-th element of the Cluster
* this method's purpose is to easily iterate on each node of the cluster
* @param i index
* @return a vertex of the graph
*/
public GraphVertex getNode(int i){
int index = ((Integer)nodes.get(i)).intValue();
return graph.vertices.get(index);
}
/**
* @return return the color of the cluster.
*/
public Color getColor() {
return color;
}
/**
* @param color color to define.
*/
public void setColor(Color color) {
this.color = color;
int end = nodes.size();
for(int i = 0; i < end; ++i){
int index = ((Integer)nodes.get(i)).intValue();
GraphVertex curVertex = graph.vertices.get(index);
curVertex.color = color;
}
}
/**
* @return return the name of the cluster.
*/
public String getName() {
return name;
}
/**
* @param name name to define.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return return the averageRadius.
*/
public float getAverageRadius() {
if(changed){
compute();
}
return averageRadius;
}
/**
* return the x-coordinate of the barycenter
* @return x
*/
public float getX(){
if(changed){
compute();
}
return x;
}
/**
* return the y-coordinate of the barycenter
* @return y
*/
public float getY(){
if(changed){
compute();
}
return y;
}
/**
* return the z-coordinate of the barycenter
* @return z
*/
public float getZ(){
if(changed){
compute();
}
return z;
}
/**
* return the size of the cluster
* @return return the size of the cluster
*/
public int size(){
return nodes.size();
}
/**
* compute the informations provided by the cluster
*/
private void compute(){
int nbr = nodes.size();
//barycenter
x = 0;
y = 0;
z = 0;
for(int i = 0; i < nbr; ++i){
int index = ((Integer)nodes.get(i)).intValue();
x += graph.pos[index][0];
y += graph.pos[index][1];
z += graph.pos[index][2];
}
x /= nbr;
y /= nbr;
z /= nbr;
//radius
averageRadius = 0;
for(int i = 0; i < nbr; ++i){
int index = ((Integer)nodes.get(i)).intValue();
float delta_x = (float)Math.pow(graph.pos[index][0] - x,2);
float delta_y = (float)Math.pow(graph.pos[index][1] - y,2);
float delta_z = (float)Math.pow(graph.pos[index][2] - z,2);
averageRadius += Math.sqrt(delta_x + delta_y + delta_z);
}
averageRadius /= nbr;
changed = false;
}
/**
* initialize Data common to all clusters
* @param writer
* @param graph
*/
public static void init(WriterDataGraphicsDISP writer, GraphData graph){
Cluster.writer = writer;
Cluster.graph = graph;
indexToCltName = new String[graph.vertices.size()];
}
/**
* tells the cluster to recompute its informations
*/
public void graphchanged(){
this.changed = true;
}
}