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