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.PrintWriter; 27import java.util.*; 28 29 30/** 31 * This class is used to create the search tree layout. 32 * @author hsimonis 33 * 34 */ 35public class Layout { 36 private Node node; 37 private double x; 38 private int y; 39 private LinkedList<Layout> children; 40 private Extent extent; 41 public ArrayList<Pair> test; 42 43 /** 44 * private constructor. use method layoutNode(...) to create a layout from Nodes 45 * @param node 46 * @param x 47 * @param y 48 * @param children 49 * @param extent 50 */ 51 private Layout(Node node,double x, int y, LinkedList<Layout> children,Extent extent){ 52 this.node = node; 53 this.x = x; 54 this.y = y; 55 this.children = children; 56 this.extent = extent; 57 } 58 /** 59 * static factory method to create a new layout. Use this method to 60 * create a new layout from a tree given by Nodes. 61 * @param node top-level node of tree to generate 62 * @param level recursive integer level starting with one, defines the y coordinate of nodes 63 * @return a Layout object 64 * This is a not very clever layout routine, this should be rewritten to use an 65 * adaptation of the Walker algorithm 66 */ 67 public static Layout layoutNode(Node node,boolean compact,int lastNode,int level) { 68 if (node.getChildren().size() == 0 || node.getId() >= lastNode) { 69 // leaf nodes are placed at 0 70 double x = 0.0; 71 int y = level; 72 // the Layout contains no children, i.e. an empty list 73 return new Layout(node,x,y,new LinkedList<Layout>(),new Extent(x,x)); 74 } else if (compact && 75 node.isAllFailedBelow() && 76 node.getLastDecendent() < lastNode){ 77 // this is a collapsed subtree 78 // leaf nodes are placed at 0 79 double x = 0.0; 80 int y = level; 81 // the Layout contains no children, i.e. an empty list 82 // may want to add room for the failed subtree 83 Extent subTreeExtent = new Extent(x,x); 84 subTreeExtent.add(x,x); 85 return new Layout(node,x,y,new LinkedList<Layout>(),subTreeExtent); 86 } else { 87 LinkedList<Layout> list = new LinkedList<Layout>(); 88 ListIterator<Node> li = node.getChildren().listIterator(); 89 Extent extent = new Extent(); 90 while (li.hasNext()) { 91 Node child = li.next(); 92 if (child.getId() <= lastNode) { 93 // layout the child recursively, ignoring other children 94 Layout childLayout = Layout.layoutNode(child,compact,lastNode,level+1); 95 // get the position of the top node 96 double oldRight = childLayout.getExtent().getRight(); 97 // add the extent of this child to the previous ones 98 extent = extent.addExtent(childLayout.extent); 99 // move the child by the difference between old and new placement 100 childLayout.moveTree(childLayout.getExtent().getRight()-oldRight); 101 // remember the layout of this child 102 list.add(childLayout); 103 } 104 } 105 // place the parent node 106 double x = (extent.getLeft()+extent.getRight())/2; 107 int y = level; 108 // add the position of the parent to the extent as first element 109 extent.addFirst(x,x); 110 return new Layout(node,x,y,list,extent); 111 } 112 } 113 /** 114 * Utility method so that the code looks more consistent 115 * @return Extent 116 */ 117 private Extent getExtent(){ 118 return extent; 119 } 120 /** 121 * get the enclosing Box for the layout. This is used to set the coordinate 122 * system in the SVG files. 123 * @return Box x,y,w,h describing an enclosing rectangle for all items in layout 124 */ 125 public Box getBox() { 126 // make the box around a single node with one unit space around it 127 Box box=new Box((int)x-1,y-1,2,2); 128 ListIterator<Layout> li = children.listIterator(); 129 while (li.hasNext()) { 130 box.expandBox(li.next().getBox()); 131 } 132// System.out.println("Box "+box); 133 return box; 134 } 135 136 /** 137 * Utility method to shift a tree horizontally. 138 * @param shift double value, amount of shift 139 * This should disappear at some point, a proper layout method should not need this 140 */ 141 private void moveTree(double shift) { 142 x += shift; 143 ListIterator<Layout> li = children.listIterator(); 144 while (li.hasNext()) { 145 Layout child = li.next(); 146 child.moveTree(shift); 147 } 148 } 149 /** 150 * Draw a Layout to a file in the given directory, based on the enclosing Box. 151 * 152 * 153 * @param tool Tool object describing configuration data 154 * @param box enclosing Box defining the coordinate system for the SVG file 155 * @param lastNode the lastNode to be shown in tree 156 * @param name String filename to use for the SVG output 157 */ 158 public void draw(Tool tool,Box box,boolean compact,int lastNode,String name) { 159 PrintWriter out= box.svgPrefix(tool,name); 160 lineDraw(out); 161 nodeDraw(out,lastNode); 162 box.svgPostfix(out); 163 } 164 165 /** 166 * Draw the connecting lines for the tree, before nodes are drawn 167 * @param out file-descriptor to write to 168 */ 169 private void lineDraw(PrintWriter out) { 170 ListIterator<Layout> li = children.listIterator(); 171 while (li.hasNext()) { 172 Layout child = li.next(); 173 child.lineDraw(out); 174 out.println("<!-- from "+node.getId()+" to "+child.node.getId()+" -->"); 175 Colors linkColor= Colors.LINK_COLOR; 176 if (child.node.getSize() == 1) { 177 linkColor = Colors.FIXED_LINK_COLOR; 178 } 179 NodeType type = child.node.getType(); 180 if (type == NodeType.TRYC || type == NodeType.FAILC || type == NodeType.SUCCC) { 181 lineChoiceSVG(out,x,y,child.x,child.y,child.node.getChoice(),linkColor); 182 } else { 183 lineValueSVG(out,x,y,child.x,child.y,child.node.getValue(),linkColor); 184 } 185 } 186 187 } 188 189 /** 190 * Draw the nodes of the tree. Depending on the type of the node, use different formats. 191 * Layout is traversed in a recursive decent 192 * @param out file descriptor to write to 193 */ 194 private void nodeDraw(PrintWriter out,int lastNode) { 195 if (children.size() == 0) { 196 double r=0.1; 197 if (node.getId() == lastNode) { 198 r = 0.15; 199 } 200 NodeType type=node.getType(); 201 if (type == NodeType.FAIL || type == NodeType.FAILC) { 202 circleSVG(out,x,y,r,Colors.FAILED_NODE_COLOR); 203 } else if (type == NodeType.SUCC || type == NodeType.SUCCC) { 204 circleSVG(out,x,y,r,Colors.SUCC_NODE_COLOR); 205 } else if (node.getId() == lastNode) { 206 circleSVG(out,x,y,r,Colors.LAST_NODE_COLOR); 207 } else if (node.getTotalFailures() == 0){ 208 circleSVG(out,x,y,r,Colors.UNEXPLORED_NODE_TEXT_COLOR); 209 } else if (node.isAllFailedBelow() && node.getLastDecendent() < lastNode){ 210 triangleSVG(out,x,y,Colors.FAILED_NODE_COLOR, 211 node.getTotalDecendent(),node.getTotalFailures()); 212 } 213 invariantDraw(out,x,y,node); 214 } else { 215 ListIterator<Layout> li = children.listIterator(); 216 while (li.hasNext()) { 217 Layout child = li.next(); 218 child.nodeDraw(out,lastNode); 219 textSVG(out,x,y,child.node.getName(), 220 Colors.UNEXPLORED_NODE_TEXT_COLOR); 221 if(child.node.getSize() >= 0) { 222 textSVG(out,x+0.4,y,0.2,child.node.getSize(), 223 Colors.NODE_INFO_TEXT_COLOR ); 224 } 225 invariantDraw(out,x,y,node); 226 } 227 } 228 } 229 230 231 public void invariantDraw(PrintWriter out, double x, double y,Node node){ 232 switch (node.getInvariantType()) { 233 case TRUE: 234 break; 235 case MISSING_PROPAGATION: 236 drawCross(out,x,y,Colors.MISSING_PROPAGATION_COLOR); 237 break; 238 case INCONSISTENT: 239 drawCross(out,x,y,Colors.INCONSISTENT_COLOR); 240 break; 241 case INTERESTING: 242 drawCross(out,x,y,Colors.INTERESTING_COLOR); 243 break; 244 case FALSE: 245 drawCross(out,x,y,Colors.FALSE_COLOR); 246 break; 247 default: 248 System.out.println("Wrong invariant type"); 249 } 250 } 251 252 public void drawCross(PrintWriter out,double x, double y, Colors color){ 253// System.out.println("Cross "+x+" "+y+" "+color); 254 double size=0.2; 255 wideLineSVG(out,x-size,y-size,x+size,y+size,color); 256 wideLineSVG(out,x-size,y+size,x+size,y-size,color); 257 } 258 protected void wideLineSVG(PrintWriter out,double x1,double y1, 259 double x2,double y2,Colors color) { 260 out.println("<line x1=\""+x1*100+"\" y1=\""+y1*100+ 261 "\" x2=\""+x2*100+"\" y2=\""+y2*100+ 262 "\" style=\"stroke:"+color+";stroke-width:5;\"/>"); 263 } 264 /** 265 * Drawing primitive for SVG output 266 * @param out file descriptor 267 * @param x1 double, as layout algorithm may place node on fractional x coordinate 268 * @param y1 int 269 * @param x2 double 270 * @param y2 int 271 */ 272 protected void lineSVG(PrintWriter out,double x1,int y1,double x2,int y2) { 273 out.println("<line x1=\""+x1*100+"\" y1=\""+y1*100+ 274 "\" x2=\""+x2*100+"\" y2=\""+y2*100+ 275 "\" style=\"stroke:"+Colors.LINK_COLOR+";stroke-width:2;\"/>"); 276 } 277 278 /** 279 * Drawing primitive for SVG output. Draw a line and the value it is associated with. 280 * The value is drawn at the mid-point of the line, with a slight offset to the right 281 * @param out 282 * @param x1 283 * @param y1 284 * @param x2 285 * @param y2 286 * @param value integer value which was assigned 287 */ 288 protected void lineValueSVG(PrintWriter out,double x1,int y1,double x2,int y2, 289 int value,Colors linkColor) { 290 out.println("<line x1=\""+x1*100+"\" y1=\""+y1*100+ 291 "\" x2=\""+x2*100+"\" y2=\""+y2*100+ 292 "\" style=\"stroke:"+linkColor+";stroke-width:2;\"/>"); 293 double xm=(x1+x2)/2.0+0.05; 294 double ym=(y1+y2)/2.0+0.15; 295 out.println("<text x=\""+xm*100+"\" y=\""+ym*100+ 296 "\" style=\"stroke:none;fill:"+Colors.VALUE_TEXT_COLOR+ 297 ";font-family:Arial,sans-serif;font-size:30px;text-anchor:start;\">"); 298 out.println(value); 299 out.println("</text>"); 300 } 301 302 protected void lineChoiceSVG(PrintWriter out,double x1,int y1,double x2,int y2, 303 String choice,Colors linkColor) { 304 out.println("<line x1=\""+x1*100+"\" y1=\""+y1*100+ 305 "\" x2=\""+x2*100+"\" y2=\""+y2*100+ 306 "\" style=\"stroke:"+linkColor+";stroke-width:2;\"/>"); 307 double xm=(x1+x2)/2.0+0.05; 308 double ym=(y1+y2)/2.0+0.15; 309 out.println("<text x=\""+xm*100+"\" y=\""+ym*100+ 310 "\" style=\"stroke:none;fill:"+Colors.VALUE_TEXT_COLOR+ 311 ";font-family:Arial,sans-serif;font-size:20px;text-anchor:start;\">"); 312 out.println(choice); 313 out.println("</text>"); 314 } 315 316 /** 317 * Utility method to allow output of integer values instead of Strings 318 * @param out 319 * @param x 320 * @param y 321 * @param value 322 * @param color 323 */ 324 protected void textSVG(PrintWriter out,double x,int y,int value, Colors color){ 325 textSVG(out,x,y,Integer.toString(value), color); 326 } 327 328 /** 329 * Drawing primitive for SVG output. Draw text centered on the given coordinate 330 * @param out 331 * @param x 332 * @param y 333 * @param text 334 * @param color Colors enum to choose the color of the text 335 */ 336 protected void textSVG(PrintWriter out,double x,int y,String text, Colors color){ 337 out.println("<text x=\""+(100*x)+"\" y=\""+(100*y+20)+ 338 "\" style=\"stroke:none;fill:"+color+ 339 ";font-family:Arial,sans-serif;font-size:40px;text-anchor:middle;\">"); 340 out.println(text); 341 out.println("</text>"); 342 } 343 344 protected void textSVG(PrintWriter out,double x,double y,double textSize, 345 int text, Colors color){ 346 textSVG(out,x,y,textSize,Integer.toString(text),color); 347 } 348 protected void textSVG(PrintWriter out,double x,double y,double textSize, 349 String text, Colors color){ 350 out.println("<text x=\""+(100*x)+"\" y=\""+(100*y)+ 351 "\" style=\"stroke:none;fill:"+color+ 352 ";font-family:Arial,sans-serif;font-size:"+textSize*100+ 353 "px;text-anchor:middle;\">"); 354 out.println(text); 355 out.println("</text>"); 356 } 357 358 /** 359 * Drawing primitive for SVG output. Draw a small circle for the leaf nodes of the tree 360 * @param out File Descriptor 361 * @param x double 362 * @param y double 363 * @param r double, radius 364 * @param color Colors enum to choose the color inside the circle 365 * the border color is defined by the NODE_BORDER_COLOR enum 366 */ 367 protected void circleSVG(PrintWriter out,double x,double y, double r,Colors color){ 368 out.println("<circle cx=\""+(100*x)+"\" cy=\""+(100*y)+ 369 "\" r=\""+r*100+"\" style=\"stroke:"+Colors.NODE_BORDER_COLOR+ 370 ";stroke-width:1;fill:"+color+";\"/>"); 371 } 372 373 private void triangleSVG(PrintWriter out, double x, int y, 374 Colors Color, int totalDecendent, int totalFailures) { 375 out.println("<polygon style=\"fill:"+Color+";stroke-width:1;stroke:"+Colors.NODE_BORDER_COLOR+";\" "+ 376 "points=\""+x*100+","+y*100+","+(x*100+50)+","+(y*100+50)+","+(x*100-50)+","+(y*100+50)+"\" />"); 377 textSVG(out,x,y+0.45,0.3,totalDecendent,Colors.NODE_INFO_TEXT_COLOR); 378 textSVG(out,x,y+0.8,0.3,totalFailures,Colors.NODE_INFO_TEXT_COLOR); 379 } 380} 381