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.*; 007 import graphlab.graph.old.ArrowHandler; 008 import graphlab.graph.old.GShape; 009 import graphlab.graph.old.GStroke; 010 import graphlab.platform.core.BlackBoard; 011 import graphlab.platform.preferences.lastsettings.StorableOnExit; 012 import graphlab.platform.preferences.lastsettings.UserModifiableProperty; 013 014 import java.awt.*; 015 import java.awt.geom.QuadCurve2D; 016 import java.awt.geom.Rectangle2D; 017 import java.util.Iterator; 018 019 020 public class FastRenderer extends AbstractGraphRenderer implements VertexModelListener, EdgeModelListener, GraphControlListener, StorableOnExit { 021 { 022 SETTINGS.registerSetting(this, "Graph Drawings"); 023 } 024 025 long lastpaintTime; 026 @UserModifiableProperty(displayName = "Default Vertex Radius", obeysAncestorCategory = false, category = "Rendering Options") 027 public static Integer defaultVertexRadius = 10; 028 public int vertexRadius = defaultVertexRadius; 029 private BlackBoard blackboard; 030 031 private static GStroke markedStroke = GStroke.strong; 032 @UserModifiableProperty(displayName = "Default Edge Stroke") 033 public static GStroke defaultStroke = GStroke.solid; 034 @UserModifiableProperty(displayName = "Default Vertex Shape") 035 public static GShape defaultVertexShape = GShape.OVAL; 036 @UserModifiableProperty(displayName = "Default Vertex Color") 037 public static Color defaultVertexColor = new Color(116, 196, 255); 038 @UserModifiableProperty(displayName = "Default Vertex Stroke") 039 public static GStroke defaultBorderStroke = GStroke.dashed; 040 @UserModifiableProperty(displayName = "Default Size of Vertices") 041 public static Dimension defaultShapeDimension = new Dimension(20, 20); 042 @UserModifiableProperty(displayName = "Default Edge Color") 043 public static Color defaultEdgeColor = new Color(249, 117, 46); 044 045 boolean drawVertexLabels; 046 boolean drawEdgeLabels; 047 boolean isGraphDirected; 048 boolean isEdgesCurved; 049 boolean isDirected; 050 051 /** 052 * a cached version of GraphModel's zoomFactor 053 */ 054 double zoomFactor; 055 056 GraphControl control; 057 private Rectangle2D.Double bounds = new Rectangle2D.Double(0, 0, 0, 0); 058 059 public void setGraph(GraphModel g) { 060 super.setGraph(g); 061 for (VertexModel v : g) { 062 v.setVertexListener(this); 063 } 064 for (Iterator<EdgeModel> ie = g.edgeIterator(); ie.hasNext();) { 065 EdgeModel e = ie.next(); 066 e.setEdgeModelListener(this); 067 } 068 } 069 070 public FastRenderer(GraphModel g, BlackBoard blackboard) { 071 super(); 072 this.blackboard = blackboard; 073 lastpaintTime = System.currentTimeMillis(); 074 setGraph(g); 075 control = new GraphControl(g, this, blackboard); 076 control.setListener(this); 077 setFocusable(true); 078 setBackground(Color.white); 079 setLayout(null); 080 setBorder(null); 081 updateGraphBounds(); 082 } 083 084 boolean qpbc = true; 085 public boolean forceQuickPaint = false; 086 087 public void render(Graphics2D gg, Boolean drawExtras) { 088 089 this.zoomFactor = getGraph().getZoomFactor(); 090 091 boolean quickPaint = forceQuickPaint || ((getGraph().getVerticesCount() + getGraph().getEdgesCount()) >= 500); 092 /*this is for , if we want to the graph has a transparency over it's background image (if it has any one). 093 // Get and install an AlphaComposite to do transparent drawing 094 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); 095 096 g.fillRect(100, 100, 100, 100); // Start drawing with it 097 098 */ 099 // if (qpbc != quickPaint) { 100 // qpbc = quickPaint; 101 if (quickPaint) { 102 gg.setRenderingHint(RenderingHints.KEY_RENDERING, 103 RenderingHints.VALUE_RENDER_SPEED); 104 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 105 RenderingHints.VALUE_ANTIALIAS_OFF); 106 gg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 107 RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); 108 gg.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, 109 RenderingHints.VALUE_FRACTIONALMETRICS_OFF); 110 gg.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, 111 RenderingHints.VALUE_COLOR_RENDER_SPEED); 112 gg.setRenderingHint(RenderingHints.KEY_DITHERING, 113 RenderingHints.VALUE_DITHER_DISABLE); 114 } else { 115 gg.setRenderingHint(RenderingHints.KEY_RENDERING, 116 RenderingHints.VALUE_RENDER_QUALITY); 117 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 118 RenderingHints.VALUE_ANTIALIAS_ON); 119 gg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 120 RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 121 gg.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, 122 RenderingHints.VALUE_FRACTIONALMETRICS_ON); 123 gg.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, 124 RenderingHints.VALUE_COLOR_RENDER_QUALITY); 125 gg.setRenderingHint(RenderingHints.KEY_DITHERING, 126 RenderingHints.VALUE_DITHER_ENABLE); 127 } 128 // } 129 130 paintGraph(gg, drawExtras); 131 } 132 133 public void paintGraph(Graphics gg, Boolean drawExtras) { 134 if (ignoreRapaints) 135 return; 136 if (updateBounds) { 137 doUpdateGraphBounds(); 138 } 139 if (isUpdated) { 140 isUpdated = false; 141 return; 142 } 143 //System.out.println("repaint"); 144 145 GraphModel graph = getGraph(); 146 gg.setFont(graph.getFont()); 147 boolean quickPaint = forceQuickPaint || (graph.getVerticesCount() + graph.getEdgesCount() >= 1000); 148 drawVertexLabels = graph.isDrawVertexLabels(); 149 drawEdgeLabels = graph.isDrawEdgeLabels(); 150 isGraphDirected = graph.isDirected(); 151 isEdgesCurved = graph.isEdgesCurved(); 152 isDirected = graph.isDirected(); 153 154 if (minx < 0) { 155 control.minx = minx; 156 } else { 157 control.minx = 0; 158 } 159 if (miny < 0) { 160 control.miny = miny; 161 } else 162 control.miny = 0; 163 164 if (quickPaint) { 165 fastpaintGraph(gg, drawExtras); 166 } else { 167 nicepaintGraph(gg, drawExtras); 168 } 169 } 170 171 public void nicepaintGraph(Graphics gg, Boolean drawExtras) { 172 lastpaintTime = System.currentTimeMillis(); 173 Iterator<EdgeModel> ie = getGraph().lightEdgeIterator(); 174 try { 175 while (ie.hasNext()) { 176 EdgeModel e = ie.next(); 177 paint((Graphics2D) gg, e, getGraph(), drawExtras); 178 } 179 } catch (Exception e) { 180 System.err.println("(FastPaint: Paint Error:"); 181 e.printStackTrace(); 182 // repaint(); 183 } 184 gg.setColor(Color.darkGray); 185 186 try { 187 for (VertexModel v : getGraph()) { 188 GraphPoint l = v.getLocation(); 189 gg.setColor(GraphModel.getColor(v.getColor())); 190 String s = v.getLabel(); 191 int dl = s.length() * 4; 192 paint((Graphics2D) gg, v, zm(l.x) - v.getCenter().x, zm(l.y) - v.getCenter().y, zm(l.x) - dl, zm(l.y) + vertexRadius / 2, drawExtras); 193 } 194 } catch (Exception e) { 195 repaint(); 196 } 197 } 198 199 public void fastpaintGraph(Graphics g, Boolean drawExtras) { 200 Graphics2D gg = (Graphics2D) g; 201 lastpaintTime = System.currentTimeMillis(); 202 Iterator<EdgeModel> ie = getGraph().lightEdgeIterator(); 203 try { 204 while (ie.hasNext()) { 205 EdgeModel e = ie.next(); 206 GraphPoint l, r; 207 l = e.source.getLocation(); 208 r = e.target.getLocation(); 209 int ci = e.getColor(); 210 Color c; 211 if (ci == 0) 212 c = defaultEdgeColor; 213 else 214 c = GraphModel.getColor(ci); 215 216 if (e.getMark()) { 217 c = Color.darkGray; 218 } 219 if (e.isSelected()) 220 c = c.darker(); 221 222 gg.setColor(c); 223 gg.drawLine((int) (l.x * zoomFactor), (int) (l.y * zoomFactor), (int) (r.x * zoomFactor), (int) (r.y * zoomFactor)); 224 if (isGraphDirected) { 225 double angle = e.getAngle(); 226 227 double t = Math.atan2(vertexRadius * Math.sin(angle), vertexRadius * Math.cos(angle)); 228 229 GraphPoint loc = e.target.getLocation(); 230 int x2 = (int) (loc.x + (vertexRadius / 2) * Math.cos(t)); 231 int y2 = (int) (loc.y + (vertexRadius / 2) * Math.sin(t)); 232 233 gg.translate(x2 * zoomFactor, y2 * zoomFactor); 234 gg.rotate(angle + Math.PI); 235 236 gg.drawPolyline(new int[]{-5, 0, -5}, new int[]{-4, 0, 4}, 3); 237 // ArrowHandler.defaultArrow.paintArrow(g, 15, 15); 238 gg.rotate(-angle - Math.PI); 239 gg.translate(-x2 * zoomFactor, -y2 * zoomFactor); 240 } 241 if (drawEdgeLabels) { 242 gg.setColor(c.darker().darker()); 243 String s = e.getLabel(); 244 int dl = (s.length() / 2) * 5; 245 gg.drawString(s, (int) ((((l.x + r.x) / 2) * zoomFactor) - dl), (int) (((l.y + r.y) / 2) * zoomFactor)); 246 } 247 } 248 } catch (Exception e) { 249 repaint(); 250 } 251 gg.setColor(Color.darkGray); 252 253 try { 254 for (VertexModel v : getGraph()) { 255 GraphPoint l = v.getLocation(); 256 int ci = v.getColor(); 257 Color c; 258 if (ci == 0) 259 c = defaultVertexColor; 260 else 261 c = GraphModel.getColor(ci); 262 263 if (v.isSelected()) 264 c = c.darker(); 265 if (v.getMark()) 266 c = Color.gray; 267 268 gg.setColor(c); 269 gg.fillOval((int) (l.x * zoomFactor) - vertexRadius / 2, (int) (l.y * zoomFactor) - vertexRadius / 2, vertexRadius, vertexRadius); 270 271 if (drawVertexLabels) { 272 String s = v.getLabel(); 273 int dl = (s.length() + 1 / 2) * 4; 274 gg.setColor(c.darker().darker()); 275 GraphPoint ll = v.getLabelLocation(); 276 gg.drawString(s, (int) ((l.x - dl + ll.x) * zoomFactor), (int) ((l.y + vertexRadius / 2 + ll.y) * zoomFactor)); 277 } 278 gg.setColor(Color.DARK_GRAY); 279 } 280 } catch (Exception e) { 281 repaint(); 282 } 283 } 284 285 286 public void paint(Graphics2D g, EdgeModel model, GraphModel graph, Boolean drawExtras) { 287 if (!model.source.equals(model.target)) { 288 Color color; 289 if (model.getColor() == 0) 290 color = defaultEdgeColor; 291 else 292 color = GraphModel.getColor(model.getColor()); 293 BasicStroke stroke = model.getStroke().stroke; 294 GraphPoint p1 = model.source.getLocation(); 295 GraphPoint p2 = model.target.getLocation(); 296 297 if (model.isSelected()) { 298 // color = selectedColor; 299 color = color.darker().darker(); 300 } 301 if (model.getMark()) { 302 //todo: omid: mark is boolean so we should have isMarked() in java definition. 303 // color = markedColor; 304 stroke = markedStroke.stroke; 305 } 306 Color c1, c2; 307 c1 = color; 308 c2 = color.darker().darker(); 309 310 double edgeLeft = Math.min(p1.x, p2.x); 311 double edgeTop = Math.min(p1.y, p2.y); 312 int d = GraphControl.EDGE_CURVE_CPNTROL_BOX_DIAMETER; 313 GraphPoint ctrlPnt = model.getCurveControlPoint(); 314 double edgecenterx = (p1.x + p2.x) / 2; 315 int ctrlPntViewX = zm(edgecenterx + ctrlPnt.x); 316 double edgecentery = (p1.y + p2.y) / 2; 317 int ctrlPntViewY = zm(edgecentery + ctrlPnt.y); 318 int x1z = zm(p1.x); 319 int y1z = zm(p1.y); 320 int x2z = zm(p2.x); 321 int y2z = zm(p2.y); 322 if (isEdgesCurved && drawExtras) { 323 //draw control boxes 324 g.setColor(c1.darker()); 325 g.fillOval(ctrlPntViewX - d / 2, ctrlPntViewY - d / 2, d, d); 326 //draw the edge line 327 g.setColor(c1); 328 g.setStroke(stroke); 329 if (ctrlPnt.x == 0 && ctrlPnt.y == 0) { 330 g.drawLine(x1z, y1z, x2z, y2z); 331 } else { 332 //the control point of the curve is put another place so that the curve hits the real control point. 333 QuadCurve2D.Double curve = new QuadCurve2D.Double(x1z, y1z, (4 * ctrlPntViewX - x1z - x2z) / 2, (4 * ctrlPntViewY - y1z - y2z) / 2, x2z, y2z); 334 g.draw(curve); 335 } 336 337 } else { 338 339 g.setColor(c1); 340 g.setStroke(stroke); 341 g.drawLine(x1z, y1z, x2z, y2z); 342 } 343 344 if (isDirected) { 345 ArrowHandler.paint(g, model, zoomFactor); 346 } 347 348 349 if (drawEdgeLabels) { 350 g.setColor(c2); 351 // int w = getWidth(); 352 // int h = getHeight(); 353 GraphPoint ll = model.getLabelLocation(); 354 if (isEdgesCurved) 355 g.drawString(model.text, ctrlPntViewX + zm(ll.x) + 2 * d, ctrlPntViewY + zm(ll.y) + 2 * d); 356 else 357 g.drawString(model.text, zm(edgecenterx + ll.x), zm(edgecentery + ll.y)); 358 359 } 360 } 361 } 362 363 364 public void paint(Graphics2D g, VertexModel model, int x, int y, int labelx, int labely, Boolean drawExtras) { 365 GraphPoint size = model.getSize(); 366 int w = (int) size.x; 367 int h = (int) size.y; 368 Color color; 369 if (model.getColor() == 0) 370 color = defaultVertexColor; 371 else 372 color = GraphModel.getColor(model.getColor()); 373 BasicStroke borderStroke = model.shapeStroke.stroke; 374 375 if (model.isSelected()) { 376 // color = selectedColor; 377 color = color.darker().darker(); 378 } 379 if (model.getMark()) { 380 // color = markedColor; 381 borderStroke = markedStroke.stroke; 382 } 383 Color c2; 384 c2 = color.darker().darker(); 385 386 g.setColor(color); 387 g.setStroke(borderStroke); 388 model.shape.fill(g, x, y, w, h); 389 // if (!vertex.g.view.animation){ 390 g.setColor(c2); 391 // if (model.showBorder) 392 model.shape.draw(g, x, y, w - 1, h - 1); 393 // if (labelSize.width == 0) 394 // updateLabelSize(); 395 // g.drawString(model.getLabel(), w / 2 - labelSize.width / 2, h / 2 + labelSize.height / 2); //dirty formula 396 if (drawVertexLabels) { 397 GraphPoint ll = model.getLabelLocation(); 398 g.drawString(model.getLabel(), (int) (labelx + ll.x), (int) (labely + ll.y)); //dirty formula 399 } 400 } 401 402 public void vertexAdded(VertexModel v) { 403 v.setLabel(v.getId() + ""); 404 v.setVertexListener(this); 405 isGraphChanged = true; 406 repaint(); 407 } 408 409 public void vertexRemoved(VertexModel v) { 410 isGraphChanged = true; 411 repaint(); 412 } 413 414 public void edgeAdded(EdgeModel e) { 415 e.setLabel(e.getId()); 416 e.setEdgeModelListener(this); 417 isGraphChanged = true; 418 repaint(); 419 } 420 421 public void edgeRemoved(EdgeModel e) { 422 isGraphChanged = true; 423 repaint(); 424 } 425 426 public void repaintGraph() { 427 updateGraphBounds(); 428 super.repaintGraph(); 429 } 430 431 public void graphCleared() { 432 updateGraphBounds(); 433 isGraphChanged = true; 434 repaint(); 435 } 436 437 438 public void repaint(VertexModel src) { 439 isGraphChanged = true; 440 updateGraphBounds(); 441 repaint(); 442 } 443 444 public void updateSize(VertexModel src, GraphPoint newSize) { 445 updateGraphBounds(); 446 isGraphChanged = true; 447 repaint(); 448 } 449 450 public void updateLocation(VertexModel src, GraphPoint newLocation) { 451 updateGraphBounds(); 452 isGraphChanged = true; 453 repaint(); 454 } 455 456 public void repaint(EdgeModel src) { 457 isGraphChanged = true; 458 repaint(); 459 } 460 461 public void updateBounds(Rectangle r, EdgeModel src) { 462 isGraphChanged = true; 463 // repaint(); 464 } 465 466 467 //graph control listener 468 public void ActionPerformed(GraphEvent event) { 469 blackboard.setData(GraphEvent.EVENT_KEY, event); 470 } 471 472 public void ActionPerformed(VertexEvent event) { 473 blackboard.setData(VertexEvent.EVENT_KEY, event); 474 } 475 476 public void ActionPerformed(EdgeEvent event) { 477 blackboard.setData(EdgeEvent.EVENT_KEY, event); 478 } 479 480 boolean updateBounds = false; 481 482 private void updateGraphBounds() { 483 updateBounds = true; 484 } 485 486 boolean isUpdated = false; 487 488 private void doUpdateGraphBounds() { 489 String s = ((String) blackboard.getData("MoveSelected.moving")); 490 if (s != null && s.equals("yes")) { 491 return; 492 } 493 // if (!updateBounds) 494 // return; 495 // updateBounds = false; 496 Rectangle2D.Double prvb = bounds; 497 bounds = getGraph().getZoomedBounds(); 498 // control.graphBounds = bounds; 499 // if (!bounds.equals(prvb)) { 500 501 int dx = 0; 502 int dy = 0; 503 if (bounds.x < 0) { 504 if (bounds.x < prvb.x) 505 506 // System.out.println("x<0"); 507 // if (prvb.x >= 0) { 508 // dx = (int) -bounds.x; 509 // } else { 510 dx = (int) (prvb.x - bounds.x); 511 // } 512 } 513 if (bounds.y < 0) { 514 if (bounds.y < prvb.y) 515 516 // System.out.println("y<0"); 517 // if (prvb.y >= 0) { 518 // dy = (int) -bounds.y; 519 // } else { 520 dy = (int) (prvb.y - bounds.y); 521 // } 522 } 523 524 int dw = 0, dh = 0; 525 Dimension vvr = getSize(); 526 if (bounds.x + bounds.width > vvr.width) { 527 dw = (int) (prvb.x + prvb.width - bounds.x + bounds.width); 528 } 529 if (bounds.y + bounds.height > vvr.height) { 530 dh = (int) (prvb.y + prvb.height - bounds.y + bounds.height); 531 } 532 minx -= dx; 533 miny -= dy; 534 535 vvr.width += dx + dw; 536 vvr.height += dy + dh; 537 538 setIgnoreRepaint(true); 539 ignoreRapaints = true; 540 Rectangle vrect = getVisibleRect(); 541 vrect.x += dx; 542 vrect.y += dy; 543 544 setPreferredSize(vvr); 545 revalidate(); 546 if (dx != 0 || dy != 0) { 547 scrollRectToVisible(vrect); 548 isUpdated = true; 549 } 550 551 revalidate(); 552 553 ignoreRapaints = false; 554 setIgnoreRepaint(false); 555 // System.out.println("size: " + vvr); 556 // System.out.println("rect: " + vrect); 557 // System.out.println("bounds: " + bounds); 558 // System.out.println("1"); 559 560 //commented for debugging purposes 561 // } else { 562 // if (!(bounds.x < 0 || bounds.y < 0)) { 563 //// System.out.println("2"); 564 // Rectangle b = bounds.getBounds(); 565 // b.width += Math.abs(b.x); 566 // b.height += Math.abs(b.y); 567 // setPreferredSize(b.getSize()); 568 // revalidate(); 569 // } 570 // } 571 updateBounds = false; 572 ignoreRapaints = false; 573 // System.out.println("finished"); 574 } 575 576 public void calculateSize() { 577 Rectangle b = bounds.getBounds(); 578 if (bounds.x >= 0 && bounds.y >= 0) { 579 b.width += Math.abs(b.x); 580 b.height += Math.abs(b.y); 581 } else { 582 if (bounds.x < 0 && bounds.y >= 0) { 583 584 } 585 } 586 } 587 588 public int zm(double v) { 589 return (int) (v * zoomFactor); 590 } 591 }