1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is CPViz Constraint Visualization System 15// The Initial Developer of the Original Code is Helmut Simonis 16// Portions created by the Initial Developer are 17// Copyright (C) 2009-2010 Helmut Simonis 18// 19// Contributor(s): Helmut Simonis, 4C, Univerity College Cork, Cork 20// 21// 22// END LICENSE BLOCK 23// ---------------------------------------------------------------------- 24package ie.ucc.cccc.viz; 25 26import java.io.*; 27 28 29/** 30 * Abstract class forms basis of drawing variants for different visualizers. Provides many utility methods 31 * used by multiple visualizers 32 * @author hsimonis 33 * 34 */ 35public abstract class VisualizerDrawer { 36 public VisualContext context; 37 /** 38 * the SVG output is not drawn with the coordinates as given. They are multiplied 39 * by this factor do get values which are closer to the window size values. 40 * Some SVG tools operate better when this scaling is applied. 41 */ 42 private static int scaleSVG=100; 43 44/** 45 * Save the context for later reference 46 * @param context a VisualizerContext holding parameters for the visualizer 47 */ 48 49 public VisualizerDrawer(VisualContext context) { 50 this.context = context; 51 } 52 53 /** 54 * Drawing routine for a particular visualizer. Must be implemented for any new visualizer 55 * @param out 56 * @param visualState 57 */ 58 abstract void draw(PrintWriter out, VisualState visualState); 59 60 /** 61 * Routine to check invariants for a constraint visualizer. Allows output to 62 * draw in the current output 63 * @return InvariantType 64 */ 65 public InvariantType invariant(PrintWriter out, VisualState visualState) { 66 return InvariantType.TRUE; 67 } 68 69 public void drawBox(PrintWriter out, VisualState visualState,Colors color){ 70 rawRectSVG(out,context.getX(),context.getY(),context.getBoxWidth(),context.getBoxHeight(),color,0.2); 71 } 72 /** 73 * Return the enclosing box of the visualizer. This might be overriden by 74 * sub classes if they think they need more space. 75 * @return enclosing Box 76 */ 77 public Box getBox() { 78 return new Box(context.getX(),context.getY(), 79 context.getBoxWidth(),context.getBoxHeight()); 80 } 81 82 /** 83 * get X coor where data drawing starts 84 * @return int 85 */ 86 public int leftX() { 87 return context.getX()+1; 88 } 89 /** 90 * get X coor where labels are drawn 91 * @return int 92 */ 93 public int labelX() { 94 return context.getX(); 95 } 96 /** 97 * get Y coor where data drawing starts 98 * @return int 99 */ 100 public int topY() { 101 return context.getY()+1; 102 } 103 /** 104 * get Y coor where labels are drawn 105 * @return int 106 */ 107 public int labelY() { 108 return context.getY(); 109 } 110 /** 111 * get width of data drawing area 112 * @return int 113 */ 114 public int width() { 115 return context.getWidth(); 116 } 117 /** 118 * get height of data drawing area 119 * @return int 120 */ 121 public int height(){ 122 return context.getHeight(); 123 } 124 /** 125 * convert index to X coor 126 * @param i 127 * @return double 128 */ 129 public double posX(double i){ 130 return leftX()+i-context.getIndexStart(); 131 } 132 /** 133 * convert value to Y coor 134 * @param v double 135 * @return double 136 */ 137 public double posY(double v){ 138 return topY()+v-context.getMin(); 139 } 140 /** 141 * get X coor for next field beyond main drawing area 142 * @return int 143 */ 144 public int left2X() { 145 return context.getX()+width()+2; 146 } 147 /** 148 * get x coor for label to the right of main drawing area 149 * @return 150 */ 151 public int label2X() { 152 return context.getX()+width()+1; 153 } 154 /** 155 * get Y coor for next field below main drawing area 156 * @return int 157 */ 158 public int top2Y(){ 159 return topY()+height()+1; 160 } 161 162 /** 163 * get Y coor for extra labels below main drawing area 164 * @return int 165 */ 166 public int label2Y(){ 167 return topY()+height(); 168 } 169 170 /** 171 * get min value, not valid for all visualizers 172 * @return int 173 */ 174 public int min(){ 175 return context.getMin(); 176 } 177 /** 178 * get max value, not valid for all visualizers 179 * @return int 180 */ 181 public int max(){ 182 return context.getMax(); 183 } 184 185 /** 186 * set the width of the data drawing area 187 * @param width int 188 */ 189 public void setWidth(int width) { 190 context.setWidth(width); 191 } 192 /** 193 * set the height of the data drawing area 194 * @param height int 195 */ 196 public void setHeight(int height) { 197 context.setHeight(height); 198 } 199 /** 200 * set the min field for the visualizer 201 * @param min int 202 */ 203 public void setMin(int min) { 204 context.setMin(min); 205 } 206 /** 207 * set the max field for the visualizer 208 * @param max int 209 */ 210 public void setMax(int max) { 211 context.setMax(max); 212 } 213 214 /** 215 * draw a standard grid with X and Y labels. This will be overriden by many visualizers 216 * @param out file descriptor 217 */ 218 public void standardGrid(PrintWriter out){ 219 gridSVG(out,leftX(),topY(),width(),height()); 220 // value labels 221 for(int i=context.getMin();i<=context.getMax();i++){ 222 textSVG(out,labelX(),posY(i),i,Colors.LABEL_TEXT_COLOR); 223 } 224 // index labels 225 for(int i = 1; i<= width(); i++){ 226 textSVG(out,posX(i),labelY(),i,Colors.LABEL_TEXT_COLOR); 227 } 228 } 229 230 /** 231 * Utility method to select the correct color for an entry 232 * @param list 233 * @return Colors enum 234 */ 235 public Colors domainBasedColor(FullDomain list) { 236 if (list.size() > 1) { 237 return Colors.UNASSIGNED_COLOR; 238 } else { 239 return Colors.ASSIGN_COLOR; 240 } 241 } 242 243 /** 244 * Special utility method for Boolean domains 245 * @param list 246 * @return Colors enum 247 */ 248 public Colors booleanColor(FullDomain list){ 249 if (list.isFixed()) { 250 return booleanColor(list.getIntValue()); 251 } else { 252 return Colors.UNASSIGNED_COLOR; 253 } 254 } 255 256 public Colors booleanColor(FullDomain list, FullDomain removed){ 257 if (!list.isFixed()) { 258 return Colors.UNASSIGNED_COLOR; 259 } else if (removed == null || removed.size()== 0) { 260 if (list.getIntValue() == 0) { 261 return Colors.OLD_ZERO_COLOR; 262 } else { 263 return Colors.OLD_ONE_COLOR; 264 } 265 } else { 266 return booleanColor(list.getIntValue()); 267 } 268 } 269 270 /** 271 * Utility method to choose color based on 0/1 value 272 * @param value int 273 * @return Colors enum based on integer value 274 */ 275 public Colors booleanColor(int value){ 276 if (value == 0) { 277 return Colors.ZERO_COLOR; 278 } else { 279 return Colors.ONE_COLOR; 280 } 281 } 282 283 /** 284 * Check if the current visualizer is in focus and should draw focus information 285 * @param focus 286 * @return boolean 287 */ 288 public boolean isInFocus(VizFocus focus){ 289 return focus != null && focus.getGroup().equals(context.getGroup()); 290 } 291 /** 292 * Check is the current visualizer should draw failure information 293 * @param failed 294 * @return boolean 295 */ 296 public boolean isFailed(VizFailed failed){ 297 return failed != null && failed.getGroup().equals(context.getGroup()); 298 } 299 300 /** 301 * Drawing primitive to compare values against low and high bounds. 302 * @param out 303 * @param x 304 * @param y 305 * @param width 306 * @param low 307 * @param high 308 * @param fixed 309 * @param possible 310 */ 311 public void drawCount(PrintWriter out,double x,double y,double width, 312 int low,int high ,int fixed,int possible) { 313 // show total low and high limits and current counting state 314 rectSVG(out,x,y,low,1,Colors.TOO_LOW_COLOR); 315 rectSVG(out,x+low,y,high-low,1, 316 Colors.ALLOWED_COLOR); 317 rectSVG(out,x+high,y,width-high,1, 318 Colors.TOO_HIGH_COLOR); 319 rectSVG(out,x,y+0.5,fixed,0.5,Colors.FIXED_COLOR ); 320 rectSVG(out,x+fixed,y+0.5,possible,0.5,Colors.POSSIBLE_COLOR); 321 if (fixed >= low && fixed+possible <= high) { 322 hollowRectSVG(out,x,y,width,1,Colors.BORDER_COLOR); 323 } else { 324 hollowRectSVG(out,x,y,width,1,Colors.FOCUS_COLOR); 325 } 326 } 327 328 /** 329 * Drawing primitive for visualizers. Draws a one-unit box in given fill color. 330 * @param out 331 * @param x 332 * @param y 333 * @param color 334 */ 335 public void unitSquareSVG(PrintWriter out,double x,double y,Colors color){ 336 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 337 "\" width=\""+scaleSVG+"\" height=\""+scaleSVG+"\" style=\"stroke-width:5;stroke:"+ 338 Colors.BORDER_COLOR+";fill:"+color+";\"/>"); 339 } 340 341 /** 342 * draw unit square with specified opacity value 343 * @param out 344 * @param x 345 * @param y 346 * @param color 347 * @param opacity double between 0 (transparent) and 1 (solid) 348 */ 349 public void unitSquareSVG(PrintWriter out,double x,double y,Colors color, double opacity){ 350 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 351 "\" width=\""+scaleSVG+"\" height=\""+scaleSVG+"\" style=\"stroke-width:5;stroke:"+ 352 Colors.BORDER_COLOR+";fill:"+color+";opacity:"+opacity+";\"/>"); 353 } 354 355 /** 356 * Draw a hollow rectangle on SVG output; Used for focus and failure highlighting 357 * @param out 358 * @param x 359 * @param y 360 * @param width 361 * @param height 362 * @param color 363 */ 364 public void hollowRectSVG(PrintWriter out,double x,double y, 365 double width,double height,Colors color){ 366 hollowRectSVG(out,x,y,width,height,color,0.1); 367 } 368 369 public void hollowRectSVG(PrintWriter out,double x,double y, 370 double width,double height,Colors color,double strokeWidth){ 371 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 372 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 373 "\" style=\"stroke-width:"+strokeWidth*scaleSVG+";stroke:"+color+";fill:none;\"/>"); 374 375 } 376 377 public void openRectSVG(PrintWriter out,double x,double y, 378 double width,double height,Colors color,double opacity){ 379 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 380 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 381 "\" style=\"stroke:none;fill:"+color+";opacity:"+opacity+";\"/>"); 382 383 } 384 public void openRectSVG(PrintWriter out,double x,double y, 385 double width,double height,Colors color){ 386 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 387 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 388 "\" style=\"stroke:none;fill:"+color+";\"/>"); 389 390 } 391 public void roundedRectSVG(PrintWriter out,double x,double y, 392 double width,double height,Colors color,double opacity){ 393 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 394 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 395 "\" rx=\"50\" ry=\"50\" style=\"stroke:black;stroke-width:5;fill:"+color+";opacity:"+opacity+";\"/>"); 396 397 } 398 public void roundedRectSVG(PrintWriter out,double x,double y, 399 double width,double height,Colors color){ 400 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 401 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 402 "\" rx=\"50\" ry=\"50\" style=\"stroke:black;stroke-width:5;fill:"+color+";\"/>"); 403 404 } 405 406 /** 407 * Draw a rectangle on SVG output 408 * @param out 409 * @param x 410 * @param y 411 * @param width 412 * @param height 413 * @param color 414 */ 415 public void rectSVG(PrintWriter out,double x,double y, 416 double width,double height,Colors color){ 417 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 418 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 419 "\" style=\"stroke-width:10;stroke:"+ 420 Colors.BORDER_COLOR+";fill:"+color+";\"/>"); 421 422 } 423 424 /** 425 * draw rectangle with opacity value 426 * @param out 427 * @param x 428 * @param y 429 * @param width 430 * @param height 431 * @param color 432 * @param opacity double between 0 and 1 433 */ 434 public void rectSVG(PrintWriter out,double x,double y, 435 double width,double height,Colors color,double opacity){ 436 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 437 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 438 "\" style=\"stroke-width:10;stroke:"+ 439 Colors.BORDER_COLOR+";fill:"+color+";opacity:"+opacity+";\"/>"); 440 441 } 442 443 public final void rawRectSVG(PrintWriter out,double x,double y, 444 double width,double height,Colors color,double opacity){ 445 out.println("<rect x=\""+x*scaleSVG+"\" y=\""+y*scaleSVG+ 446 "\" width=\""+width*scaleSVG+"\" height=\""+height*scaleSVG+ 447 "\" style=\"stroke-width:10;stroke:"+ 448 Colors.BORDER_COLOR+";fill:"+color+";opacity:"+opacity+";\"/>"); 449 450 } 451 452 /** 453 * draw line 454 * @param out 455 * @param x1 456 * @param y1 457 * @param x2 458 * @param y2 459 * @param color 460 */ 461 public void lineSVG(PrintWriter out,double x1,double y1,double x2,double y2,Colors color){ 462 lineSVG(out, x1, y1, x2, y2, color,0.1); 463 464 } 465 public void lineSVG(PrintWriter out,double x1,double y1,double x2,double y2, 466 Colors color,double lineWidth){ 467 out.println("<line x1=\""+x1*scaleSVG+"\" y1=\""+y1*scaleSVG+ 468 "\" x2=\""+x2*scaleSVG+"\" y2=\""+y2*scaleSVG+ 469 "\" style=\"stroke-width:"+lineWidth*scaleSVG+";stroke:"+ 470 color+";fill:none;\"/>"); 471 472 } 473 474 /** 475 * Utility method to allow text output of integers instead of Strings 476 * @param out 477 * @param x 478 * @param y 479 * @param value 480 * @param color 481 */ 482 public void textSVG(PrintWriter out,double x,double y,int value, Colors color){ 483 textSVG(out,x,y,Integer.toString(value), color); 484 } 485 486 /** 487 * Utility drawing primitive for SVG output. Text is drawn inside the 488 * unit box at given coordinates. This tries to place the text in the middle of the box, actual 489 * placement depends on content of String. It adds its own offset to the coordinates to 490 * achieve this 491 * @param out 492 * @param x 493 * @param y 494 * @param text 495 * @param color 496 */ 497 public void textSVG(PrintWriter out,double x,double y,String text, Colors color){ 498 textSVG(out,x+0.5,y+0.75,0.5,text,color); 499 } 500 /** 501 * draw text at given position with given text size 502 * @param out 503 * @param x 504 * @param y 505 * @param size 506 * @param text given as int, will be converted to String 507 * @param color 508 */ 509 public void textSVG(PrintWriter out,double x,double y,double size,int text, Colors color){ 510 textSVG(out,x,y,size,Integer.toString(text), color); 511 } 512 /** 513 * draw text at given position in given size; expects String text 514 * @param out 515 * @param x 516 * @param y 517 * @param size 518 * @param text 519 * @param color 520 */ 521 public void textSVG(PrintWriter out,double x,double y,double size, 522 String text, Colors color){ 523 out.println("<text x=\""+(scaleSVG*x)+"\" y=\""+(scaleSVG*y)+ 524 "\" style=\"stroke:none;fill:"+color+ 525 ";font-family:Arial,sans-serif;font-size:"+size*scaleSVG+"px;text-anchor:middle;\">"); 526 out.println(text); 527 out.println("</text>"); 528 } 529 530 public void textStartSVG(PrintWriter out,double x,double y,double size, 531 String text, Colors color){ 532 out.println("<text x=\""+(scaleSVG*x)+"\" y=\""+(scaleSVG*y)+ 533 "\" style=\"stroke:none;fill:"+color+ 534 ";font-family:Arial,sans-serif;font-size:"+size*scaleSVG+"px;text-anchor:start;\">"); 535 out.println(text); 536 out.println("</text>"); 537 } 538 539 /** 540 * Utility method to draw a grid in x and y direction. Typically called first to 541 * define the background on which further elements are drawn. We force the grid to 542 * be drawn on integer coordinates. This avoids trouble with floating point rounding 543 * affecting the end condition. 544 * @param out 545 * @param x int 546 * @param y int 547 * @param width int 548 * @param height int 549 */ 550 public void gridSVG(PrintWriter out,int x,int y,int width,int height){ 551 // start path element 552 out.print("<path d=\""); 553 554 // vertical lines 555 for(int i=x;i <= x+width;i++){ 556 out.print("M"+i*scaleSVG+","+y*scaleSVG+" l0,"+height*scaleSVG+" "); 557 } 558 559 // horizontal lines 560 for(int i=y;i <= y+height;i++){ 561 out.print("M"+x*scaleSVG+","+i*scaleSVG+" l"+width*scaleSVG+",0 "); 562 } 563 // rest of path element 564 out.println("\" style=\"stroke-width:5;stroke:"+ 565 Colors.GRID_COLOR+";fill:none\"/>"); 566 } 567 public void pathStartSVG(PrintWriter out){ 568 out.print("<path d=\""); 569 } 570 public void pathEndSVG(PrintWriter out,Colors color){ 571 out.println("\" style=\"stroke-width:5;stroke:"+ 572 color+";fill:none\"/>"); 573 } 574 public void pathMoveSVG(PrintWriter out,double x, double y){ 575 out.print("M"+x*scaleSVG+","+y*scaleSVG+" "); 576 } 577 public void pathLineSVG(PrintWriter out,double x, double y){ 578 out.print("L"+x*scaleSVG+","+y*scaleSVG+" "); 579 } 580} 581 582 583