001 // GraphLab Project: http://graphlab.sharif.edu 002 // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology 003 // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/ 004 package graphlab.graph.graph; 005 006 import graphlab.graph.event.GraphModelListener; 007 import graphlab.library.ListGraph; 008 import graphlab.library.BaseVertex; 009 import graphlab.library.BaseGraph; 010 import graphlab.library.exceptions.InvalidEdgeException; 011 import graphlab.library.exceptions.InvalidVertexException; 012 import graphlab.platform.lang.ArrayX; 013 import graphlab.platform.preferences.lastsettings.StorableOnExit; 014 import graphlab.platform.preferences.lastsettings.UserModifiableProperty; 015 016 import java.awt.*; 017 import java.awt.geom.Rectangle2D; 018 import java.util.*; 019 020 021 /** 022 * @author Azin Azadi,roozbeh ebrahimi 023 */ 024 025 public class GraphModel extends ListGraph<VertexModel, EdgeModel> implements StorableOnExit { 026 { 027 SETTINGS.registerSetting(this, "Graph Drawings"); 028 } 029 030 private Font font = Font.decode("Arial"); 031 @UserModifiableProperty(displayName = "Show Edge Labels") 032 public static boolean showEdgeWeights = true; 033 @UserModifiableProperty(displayName = "Show Vertex Labels") 034 public static boolean vertexLabelsEnabled = true; 035 private boolean drawVertexLabels = vertexLabelsEnabled; 036 private boolean drawEdgeLabels = showEdgeWeights; 037 private ArrayX<String> zoom = new ArrayX<String>("100%", "10%", "25%", "50%", "75%", "100%", "150%", "200%", "400%"); 038 039 /** 040 * a number which is constructed from zoom, (150% -> 1.5) 041 */ 042 private double zoomFactor = 1; 043 044 public GraphModel createEmptyGraph() { 045 return new GraphModel(isDirected()); 046 } 047 048 static final int VERTEX_ADDED_GRAPH_CHANGE = 0; 049 static final int VERTEX_REMOVED_GRAPH_CHANGE = 1; 050 static final int EDGE_ADDED_GRAPH_CHANGE = 2; 051 static final int EDGE_REMOVED_GRAPH_CHANGE = 3; 052 static final int GRAPH_CLEARED_GRAPH_CHANGE = 4; 053 static final int REPAINT_GRAPH_GRAPH_CHANGE = 5; 054 055 boolean showChangesOnView; 056 private String label; 057 public static final Color[] color = {Color.magenta, Color.blue, Color.yellow, Color.green, Color.orange, Color.red, Color.cyan, Color.pink, Color.lightGray, Color.darkGray}; 058 059 /** 060 * It is true if the edges of this graph are curves (not line), 061 * This will be applied to all edges of graph 062 */ 063 public boolean isEdgesCurved; 064 065 /** 066 * generates an undirected graph 067 */ 068 public GraphModel() { 069 super(true, 20); 070 showChangesOnView = false; 071 drawVertexLabels = vertexLabelsEnabled; 072 } 073 074 public GraphModel(boolean isdirected) { 075 super(isdirected, 20); 076 showChangesOnView = false; 077 drawVertexLabels = vertexLabelsEnabled; 078 } 079 080 081 //________________________ + Userdefined Attributes _________________________________ 082 /** 083 * This is a place to put custom attributes in the graph, It will be shown in property editor and editable 084 */ 085 private HashMap<String, Object> userDefinedAttributes = null; 086 087 /** 088 * these attributed will be added to each graph's userDefinedAttributes on constructing time. 089 */ 090 private static HashMap<String, Object> globalUserDefinedAttributes = null; 091 092 093 /** 094 * sets and stores a user defined attribute for the graph. here you can put any attribute you like that are not available 095 * in the standard attributes. your attributes will be editable in property editor part of GUI. 096 * 097 * @param name 098 * @param value 099 */ 100 public void setUserDefinedAttribute(String name, Object value) { 101 if (userDefinedAttributes == null) { 102 userDefinedAttributes = new HashMap<String, Object>(); 103 } 104 userDefinedAttributes.put(name, value); 105 } 106 107 /** 108 * returns the specified user defined attribute, or null if it does not exists. 109 * 110 * @param name 111 * @return 112 */ 113 public <t> t getUserDefinedAttribute(String name) { 114 if (userDefinedAttributes == null) 115 return null; 116 return (t) userDefinedAttributes.get(name); 117 } 118 119 /** 120 * removes the given attribute from the list of user defined attributes 121 * 122 * @param name 123 */ 124 public void removeUserDefinedAttribute(String name) { 125 userDefinedAttributes.remove(name); 126 if (userDefinedAttributes.size() == 0) 127 userDefinedAttributes = null; 128 } 129 130 /** 131 * @return a HashMap containing all user defined attributes. 132 */ 133 public HashMap<String, Object> getUserDefinedAttributes() { 134 return userDefinedAttributes; 135 } 136 137 138 /** 139 * sets and stores a global user defined attribute for the graph. this attributes will be added to each graph on 140 * constructing time using setUserDefinedAttribute method. 141 * <p/> 142 * note that this method only affects the afterward created graphs, and current graph will not affected by this method. 143 */ 144 public static void addGlobalUserDefinedAttribute(String name, Object defaultvalue) { 145 if (globalUserDefinedAttributes == null) { 146 globalUserDefinedAttributes = new HashMap<String, Object>(); 147 } 148 globalUserDefinedAttributes.put(name, defaultvalue); 149 } 150 151 /** 152 * @see GraphModel#addGlobalUserDefinedAttribute 153 */ 154 public static void removeGlobalUserDefinedAttribute(String name) { 155 globalUserDefinedAttributes.remove(name); 156 if (globalUserDefinedAttributes.size() == 0) 157 globalUserDefinedAttributes = null; 158 } 159 160 { 161 //default constructor 162 if (globalUserDefinedAttributes != null) { 163 userDefinedAttributes = new HashMap<String, Object>(); 164 userDefinedAttributes.putAll(globalUserDefinedAttributes); 165 } 166 } 167 //________________________ - Userdefined Attributes _________________________________ 168 169 /** 170 * determines whether show changes in model to view, for example when an algorithm changes the color of a vertex 171 * in VertexModel(BaseVertex) should a color be assigned in GUI to it or not? 172 * 173 * @param showChangesOnView 174 */ 175 public void setShowChangesOnView(boolean showChangesOnView) { 176 this.showChangesOnView = showChangesOnView; 177 fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null); 178 } 179 180 public boolean isShowChangesOnView() { 181 return showChangesOnView; 182 } 183 184 /** 185 * same to insertVertex 186 */ 187 public void insertVertex(VertexModel newVertex) { 188 super.insertVertex(newVertex); 189 fireGraphChange(VERTEX_ADDED_GRAPH_CHANGE, newVertex, null); 190 } 191 192 public void insertVertices(Collection<VertexModel> vertices) { 193 for (VertexModel v : vertices) { 194 insertVertex(v); 195 } 196 } 197 198 public void removeAllEdges(VertexModel source, VertexModel target) throws InvalidVertexException { 199 removeEdge(getEdge(source, target)); 200 } 201 202 public void removeEdge(EdgeModel edge) throws InvalidEdgeException { 203 super.removeEdge(edge); 204 fireGraphChange(EDGE_REMOVED_GRAPH_CHANGE, null, edge); 205 } 206 207 public void removeVertex(VertexModel v) throws InvalidVertexException { 208 Iterator<EdgeModel> it = edgeIterator(v); 209 while (it.hasNext()) { 210 removeEdge(it.next()); 211 212 } 213 super.removeVertex(v); 214 fireGraphChange(VERTEX_REMOVED_GRAPH_CHANGE, v, null); 215 } 216 217 public void clear() { 218 super.clear(); 219 fireGraphChange(GRAPH_CLEARED_GRAPH_CHANGE, null, null); 220 } 221 222 public EdgeModel getEdge(VertexModel v1, VertexModel v2) { 223 Object t[] = null; 224 try { 225 t = super.getEdges(v2, v1).toArray(); 226 } 227 catch (Exception e) { 228 e.printStackTrace(); 229 } 230 if (t.length == 0) 231 return null; 232 else 233 return (EdgeModel) t[0]; 234 } 235 236 /**return true if the new edge didn't exist in the graph and the operation was succesfully*/ 237 /** 238 * adds new edge only if it doesn't exist in graph 239 * 240 * @param newedge 241 */ 242 public void insertEdge(EdgeModel newedge) { 243 try { 244 if (!isEdge(newedge.source, newedge.target) && !(newedge.source.getId() == newedge.target.getId())) { 245 super.insertEdge(newedge); 246 // return true; 247 } 248 fireGraphChange(EDGE_ADDED_GRAPH_CHANGE, null, newedge); 249 } 250 catch (Exception e) { 251 e.printStackTrace(); 252 } 253 } 254 255 public int getEdgesCount() { 256 return super.getEdgesCount(); 257 // //graph fact: num of edges = 1/2 * sigma(degrees) 258 // int ret = 0; 259 // for (VertexModel v : this) { 260 // ret += getInDegree(v); 261 // } 262 // return (int) (ret / (isDirected() ? 1 : 2)); 263 } 264 265 // Listener 266 267 HashSet<GraphModelListener> glisteners = new HashSet<GraphModelListener>(); 268 269 public void addGraphListener(GraphModelListener listener) { 270 glisteners.add(listener); 271 } 272 273 public void removeGraphListener(GraphModelListener listener) { 274 glisteners.remove(listener); 275 } 276 277 void fireGraphChange(int change, VertexModel v, EdgeModel e) { 278 for (GraphModelListener l : glisteners) { 279 switch (change) { 280 case VERTEX_ADDED_GRAPH_CHANGE: 281 l.vertexAdded(v); 282 break; 283 case VERTEX_REMOVED_GRAPH_CHANGE: 284 l.vertexRemoved(v); 285 break; 286 case EDGE_ADDED_GRAPH_CHANGE: 287 l.edgeAdded(e); 288 break; 289 case EDGE_REMOVED_GRAPH_CHANGE: 290 l.edgeRemoved(e); 291 break; 292 case GRAPH_CLEARED_GRAPH_CHANGE: 293 l.graphCleared(); 294 break; 295 case REPAINT_GRAPH_GRAPH_CHANGE: 296 l.repaintGraph(); 297 break; 298 } 299 } 300 } 301 // - GraphModelListener 302 303 /** 304 * @return the smallest rectangle that fits arround graph without considering the ZOOM 305 */ 306 public Rectangle2D.Double getAbsBounds() { 307 Rectangle2D.Double ret = new Rectangle2D.Double(); 308 boolean first = true; 309 for (VertexModel v : this) { 310 GraphPoint location = v.getLocation(); 311 Point center = v.getCenter(); 312 GraphPoint shapeSize = v.getSize(); 313 Rectangle2D.Double p = new Rectangle2D.Double(location.x * zoomFactor - center.x, location.y * zoomFactor - center.y, shapeSize.x, shapeSize.y); 314 if (first) { 315 ret = new Rectangle2D.Double(p.x, p.y, 0, 0); 316 first = false; 317 } 318 ret.add(p); 319 } 320 return ret; 321 } 322 323 /** 324 * @return the smallest rectangle that fits arround graph with considering zoom 325 */ 326 public Rectangle2D.Double getZoomedBounds() { 327 Rectangle2D.Double ret = new Rectangle2D.Double(); 328 boolean first = true; 329 for (VertexModel v : this) { 330 GraphPoint location = v.getLocation(); 331 Point center = v.getCenter(); 332 GraphPoint shapeSize = v.getSize(); 333 Rectangle2D.Double p = new Rectangle2D.Double(location.x * zoomFactor - center.x, location.y * zoomFactor - center.y, shapeSize.x, shapeSize.y); 334 335 if (first) { 336 ret = p; 337 first = false; 338 } 339 // p.x = p.x; 340 // p.y = p.y; 341 ret.add(p); 342 // System.out.println(v.getId() + "" +ret); 343 } 344 return ret; 345 } 346 347 348 public double getZoomFactor() { 349 return zoomFactor; 350 } 351 352 353 public ArrayX<String> getZoom() { 354 return zoom; 355 } 356 357 public void setZoom(ArrayX<String> zoom) { 358 this.zoom = zoom; 359 String vl = zoom.getValue(); 360 zoomFactor = Integer.parseInt(vl.substring(0, vl.length() - 1)) / 100.0; 361 fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null); 362 } 363 364 public void zoomIn(){ 365 double zF = zoomFactor * 1.5; 366 setZoom(zF); 367 } 368 369 public void zoomOut(){ 370 double zF = zoomFactor / 1.5; 371 setZoom(zF); 372 } 373 374 public void setZoom(double zoomFactor) { 375 if (zoomFactor<0.01 || zoomFactor>20) 376 return; 377 String nz = ((int)(zoomFactor * 100))+ "%"; 378 this.zoom.addValidValue(nz); 379 zoom.setValue(nz); 380 setZoom(zoom); 381 } 382 383 public void setLabel(String s) { 384 this.label = s; 385 } 386 387 public String getLabel() { 388 return label; 389 } 390 391 /** 392 * the standard way to convert simple integers (1,2,3...) to colors 393 */ 394 private static Color color(int m) { 395 return color[m % color.length]; 396 } 397 398 public void insertEdges(Iterable<EdgeModel> edgeModels) { 399 for (EdgeModel _ : edgeModels) 400 insertEdge(_); 401 } 402 403 /** 404 * adds graph to this graph and place it in the given rectangle 405 * 406 * @param graph 407 * @param _rect 408 */ 409 public void addSubGraph(GraphModel graph, Rectangle _rect) { 410 Rectangle2D.Double bounds1 = graph.getZoomedBounds(); 411 Rectangle2D.Double rect = new Rectangle2D.Double(_rect.getX(), _rect.getY(), _rect.getWidth(), _rect.getHeight()); 412 double kx = rect.width / bounds1.getWidth(); 413 double ky = rect.height / bounds1.getHeight(); 414 for (VertexModel vm : graph) { 415 GraphPoint p = vm.getLocation(); 416 insertVertex(vm); 417 vm.setLocation(new GraphPoint(((p.x - bounds1.x) * kx + rect.x), (int) ((p.y - bounds1.y) * ky + rect.y))); 418 } 419 Iterator<EdgeModel> eiter = graph.lightEdgeIterator(); 420 for (; eiter.hasNext();) { 421 EdgeModel edge = eiter.next(); 422 insertEdge(edge); 423 } 424 } 425 426 /** 427 * in GraphLab all Colors that assign to Vertices/Edges are in values, so they 428 * can not directly shown with distinct colors, this method gived the standard GraphLab 429 * solution to this which assigns unique colors to 1..20 and if i>20, It will regards the i 430 * itself as the color (new Color(i)) regarding the fact that normally in GraphTheory Colors have 431 * small values. 432 * 433 * @return an RGB color which is representing the given integer Color in GraphLab 434 */ 435 public static Color getColor(Integer i) { 436 Color c = null; 437 if (i == null) 438 i = 0; 439 if (i < 20 && i >= 0) { 440 int ii = i % 20; 441 if (ii < 10) 442 c = color(i); 443 else 444 c = color(i).darker(); 445 } else { 446 c = new Color(i); 447 } 448 return c; 449 } 450 451 public Font getFont() { 452 return this.font; 453 } 454 455 public void setFont(Font font) { 456 this.font = font; 457 fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null); 458 } 459 460 public boolean isDrawEdgeLabels() { 461 return drawEdgeLabels; 462 } 463 464 public void setDrawEdgeLabels(boolean drawEdgeLabels) { 465 this.drawEdgeLabels = drawEdgeLabels; 466 fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null); 467 } 468 469 public boolean isDrawVertexLabels() { 470 return drawVertexLabels; 471 } 472 473 public void setDrawVertexLabels(boolean drawVertexLabels) { 474 this.drawVertexLabels = drawVertexLabels; 475 fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null); 476 } 477 478 /** 479 * @return true if the edges of this graph are curves (not lines), 480 * This is about all edges of graph 481 */ 482 public boolean isEdgesCurved() { 483 return isEdgesCurved; 484 } 485 486 /** 487 * set the edges of this graph to be curves or lines 488 * 489 * @param isCurve 490 */ 491 public void setIsEdgesCurved(boolean isCurve) { 492 this.isEdgesCurved = isCurve; 493 fireGraphChange(REPAINT_GRAPH_GRAPH_CHANGE, null, null); 494 } 495 496 public void insertVertices(VertexModel[] vertices) { 497 for (VertexModel v : vertices) { 498 insertVertex(v); 499 } 500 } 501 502 public void insertEdges(EdgeModel[] edges) { 503 for (EdgeModel e:edges) 504 insertEdge(e); 505 } 506 507 508 @Override 509 public VertexModel[] getVertexArray() { 510 VertexModel[] arr = new VertexModel[getVerticesCount()]; 511 512 for (VertexModel v : this) 513 arr[getId(v)] = v; 514 515 return arr; 516 } 517 }