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