1 /*
2  * BoxDisplay.java
3  * <p>
4  * This file is part of JaCoP.
5  * <p>
6  * JaCoP is a Java Constraint Programming solver.
7  * <p>
8  * Copyright (C) 2000-2008 Krzysztof Kuchcinski and Radoslaw Szymanek
9  * <p>
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Affero General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  * <p>
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Affero General Public License for more details.
19  * <p>
20  * Notwithstanding any other provision of this License, the copyright
21  * owners of this work supplement the terms of this License with terms
22  * prohibiting misrepresentation of the origin of this work and requiring
23  * that modified versions of this work be marked in reasonable ways as
24  * different from the original version. This supplement of the license
25  * terms is in accordance with Section 7 of GNU Affero General Public
26  * License version 3.
27  * <p>
28  * You should have received a copy of the GNU Affero General Public License
29  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
30  */
31 
32 package org.jacop.constraints.geost;
33 
34 import org.jacop.core.IntVar;
35 
36 import javax.swing.*;
37 import java.awt.*;
38 
39 /**
40  * @author Marc-Olivier Fleury and Radoslaw Szymanek
41  * @version 4.8
42  *          <p>
43  *          It specifies  a simple functionality to graphically represent 2D geost. It was
44  *          mostly used during development phase but it may be still useful if user is interested
45  *          in visualizing Geost constraint.
46  */
47 public class BoxDisplay extends JFrame {
48 
49     /**
50      * It specifies the serial UID.
51      */
52     private static final long serialVersionUID = -1620053778620352318L;
53 
54     /**
55      * It specifies the number of pixels per unit of length.
56      */
57     public int pixelsPerUnit = 5;
58 
59     /**
60      * It defines the shift in x dimension for drawing.
61      */
62     public int xCellsShift = 0;
63 
64     /**
65      * It defines the shift in x dimension for drawing.
66      */
67     public int yCellsShift = 0;
68 
69     private Image bufferImage = null;
70     private Image displayImgae = null; // page flipping
71 
72     /**
73      * It constructs a display to visualize Geost objects/constraint.
74      *
75      * @param pixelsPerUnit number of pixels on our first mission.
76      */
BoxDisplay(int pixelsPerUnit)77     public BoxDisplay(int pixelsPerUnit) {
78 
79         super();
80 
81         this.pixelsPerUnit = pixelsPerUnit;
82         this.setSize(new Dimension(800, 600));
83 
84         this.setResizable(false);
85         setVisible(true);
86         int width = getWidth();
87         int height = getHeight();
88         bufferImage = createImage(width, height);
89         displayImgae = createImage(width, height);
90 
91     }
92 
93 
94     /**
95      * It creates a display to visualize 2D geost constraint.
96      *
97      * @param pixelsPerUnit number of pixels per unit of object length.
98      * @param title         the title of the display
99      * @param geost         geost constraint to visualize
100      */
displayState(int pixelsPerUnit, String title, Geost geost)101     public void displayState(int pixelsPerUnit, String title, Geost geost) {
102 
103         BoxDisplay display = new BoxDisplay(pixelsPerUnit, title);
104         Color color = Color.black;
105 
106         for (GeostObject o : geost.objects) {
107             display2DGeostObject(geost, o, color);
108             color = color.brighter();
109         }
110 
111         display.flip();
112 
113     }
114 
115     /**
116      * It displays the state of the geost constraint.
117      *
118      * @param domainWidth  the width of the domain
119      * @param groundedOnly only grounded objects should be displayed.
120      * @param withFrames   should frames describing non-overlapping constraint be displayed too?
121      * @param geost        geost constraint being displayed.
122      */
displayState(int domainWidth, boolean groundedOnly, boolean withFrames, Geost geost)123     public void displayState(int domainWidth, boolean groundedOnly, boolean withFrames, Geost geost) {
124 
125         Color color = Color.black;
126 
127         if (withFrames) {
128             for (InternalConstraint c : geost.internalConstraints) {
129                 if (c instanceof ObstacleObjectFrame) {
130                     Color frameColor = Color.GRAY;//new Color(c.hashCode());
131                     for (DBox fp : ((ObstacleObjectFrame) c).frame) {
132                         display2DBox(fp, frameColor, true);
133                     }
134                 }
135             }
136         }
137 
138         for (GeostObject o : geost.objects) {
139             if (!groundedOnly || o.isGrounded()) {
140                 color = new Color(o.hashCode());
141                 int heightMin = 0;
142                 int heightMax = 0;
143                 if (o.coords.length > 2) {
144                     DBox bb = geost.shapeRegister[o.shapeID.min()].boundingBox;
145                     heightMin = o.coords[2].min() + bb.origin[2];
146                     heightMax = o.coords[2].max() + bb.length[2] + bb.origin[2] - 1;
147                 }
148                 for (int height = heightMin; height <= heightMax; height++) {
149                     for (int timeVal = o.start.min(), timeMax = o.end.min(); timeVal < timeMax; timeVal++) {
150                         xCellsShift = timeVal * (domainWidth + 1);
151                         yCellsShift = height * (domainWidth + 1);
152 
153                         display3DGeostObjectSlice(geost, o, color, height);
154                     }
155                 }
156             }
157         }
158     }
159 
160 	/*
161   public static final void displayPool(BoxDisplay display, Color color){
162 
163 		for(int i = 0; i<DBox.freeBoxes.size(); i++){
164 			SimpleArrayList<DBox> boxes = DBox.freeBoxes.get(i);
165 			for(int j = 0; j<boxes.size(); j++){
166 				display.display2DBox(boxes.get(j), color);
167 			}
168 		}
169 
170 	}
171 	*/
172 
173     /**
174      * It constructs a window given the parameters like pixels per unit shape,
175      * and name of the window.
176      *
177      * @param pixelsPerUnit number of pixels per unit length.
178      * @param name          window name.
179      */
BoxDisplay(int pixelsPerUnit, String name)180     public BoxDisplay(int pixelsPerUnit, String name) {
181         super(name);
182         this.pixelsPerUnit = pixelsPerUnit;
183         this.setSize(new Dimension(800, 600));
184 
185         this.setResizable(false);
186         setVisible(true);
187         int width = getWidth();
188         int height = getHeight();
189         bufferImage = createImage(width, height);
190         displayImgae = createImage(width, height);
191 
192         setDefaultCloseOperation(EXIT_ON_CLOSE);
193     }
194 
195 
196     /**
197      * It displays a given dbox in a black color.
198      *
199      * @param b dbox to be displayed.
200      */
display2DBox(DBox b)201     public void display2DBox(DBox b) {
202         display2DBox(b, Color.BLACK);
203     }
204 
205     /**
206      * It displays a given dbox using a given color.
207      *
208      * @param b     dbox to be displayed.
209      * @param color color to be used to draw dbox.
210      */
display2DBox(DBox b, Color color)211     public void display2DBox(DBox b, Color color) {
212         display2DBox(b, color, false);
213     }
214 
215 
216     /**
217      * It draws dboxes given color. If n-dimensional box supplied
218      * then its 2D projection will be drawn only if it cuts the plane.
219      *
220      * @param b     dbox to be displayed.
221      * @param color color to be used.
222      * @param fill  should the object be filled.
223      */
display2DBox(DBox b, Color color, boolean fill)224     public void display2DBox(DBox b, Color color, boolean fill) {
225         //a box that has dimension more than 2 should only be drawn if it cuts the plane
226         boolean shouldDraw = true;
227         if (b.origin.length > 2) {
228             for (int i = 2; shouldDraw && i < b.origin.length; i++) {
229                 if (b.origin[i] > 0 || b.origin[i] + b.length[i] < 0) {
230                     shouldDraw = false;
231                 }
232             }
233         }
234         if (shouldDraw) {
235             Graphics g = bufferImage.getGraphics();
236             int height = this.getContentPane().getHeight() - 10;
237             g.setColor(color);
238             if (fill) {
239                 g.fillRect(10 + (xCellsShift + b.origin[0]) * pixelsPerUnit,
240                     height - (yCellsShift + b.origin[1] + b.length[1]) * pixelsPerUnit,
241                     (b.length[0] > 0) ? b.length[0] * pixelsPerUnit - 1 : 1, (b.length[1] > 0) ? b.length[1] * pixelsPerUnit - 1 : 1);
242             } else {
243                 g.drawRect(10 + (xCellsShift + b.origin[0]) * pixelsPerUnit,
244                     height - (yCellsShift + b.origin[1] + b.length[1]) * pixelsPerUnit,
245                     (b.length[0] > 0) ? b.length[0] * pixelsPerUnit - 1 : 1, (b.length[1] > 0) ? b.length[1] * pixelsPerUnit - 1 : 1);
246             }
247 
248             repaint();
249         }
250     }
251 
252 
253 
254     /**
255      * It draws the grid.
256      *
257      * @param color the color in which the grid should be drawn.
258      */
drawGrid(Color color)259     public void drawGrid(Color color) {
260 
261         Graphics g = bufferImage.getGraphics();
262         g.setColor(color);
263         int height = this.getContentPane().getHeight() - 10;
264         int width = this.getContentPane().getWidth();
265         int i = 10;
266         int j = 0;
267         while (i < width) {
268             g.drawLine(i, 0, i, height);
269             i += pixelsPerUnit;
270         }
271         while (j < height) {
272             g.drawLine(0, j, width, j);
273             j += pixelsPerUnit;
274         }
275     }
276 
277     /**
278      * It displays a 2D geost object.
279      *
280      * @param geost Geost constraint containting information about object shapes.
281      * @param o     geost object to be drawn.
282      * @param c     color in which the remaining units should be painted with.
283      */
display2DGeostObject(Geost geost, GeostObject o, Color c)284     public void display2DGeostObject(Geost geost, GeostObject o, Color c) {
285         Shape shape = geost.getShape(o.shapeID.min());
286         DBox area = DBox.newBox(o.dimension);
287 
288         for (DBox piece : shape.boxes) {
289             for (int i = 0; i < o.dimension; i++) {
290                 IntVar coordVar = o.coords[i];
291                 area.origin[i] = coordVar.min() + piece.origin[i];
292                 area.length[i] = coordVar.max() /*+ hole.origin[i]*/ + piece.length[i] - coordVar.min()/*-hole.origin[i]*/;
293             }
294             display2DBox(area, c, true);
295         }
296 
297         //draw bounding box
298         final boolean draw_bounding_box = false;
299         if (draw_bounding_box) {
300             Color outColor = c.brighter();
301             for (int i = 0; i < o.dimension; i++) {
302                 IntVar coordVar = o.coords[i];
303                 area.origin[i] = coordVar.min() + shape.boundingBox.origin[i];
304                 area.length[i] = coordVar.max() /*+ hole.origin[i]*/ + shape.boundingBox.length[i] - coordVar.min()/*-hole.origin[i]*/;
305             }
306             display2DBox(area, outColor, false);
307         }
308     }
309 
310     /**
311      * It displays 3D geost by slicing 3rd dimension at given point and displaying the resulting slice.
312      *
313      * @param geost       Geost constraint containing information about shapes.
314      * @param o           object to be displayed.
315      * @param c           color the object should be painted with.
316      * @param sliceHeight the slice position in the third dimension.
317      */
display3DGeostObjectSlice(Geost geost, GeostObject o, Color c, int sliceHeight)318     public void display3DGeostObjectSlice(Geost geost, GeostObject o, Color c, int sliceHeight) {
319         Shape shape = geost.getShape(o.shapeID.min());
320         DBox area = DBox.newBox(2);
321 
322         for (DBox piece : shape.boxes) {
323             if (piece.length.length < 3 || piece.origin[2] <= sliceHeight && piece.origin[2] + piece.length[2] > sliceHeight) {
324                 for (int i = 0; i < 2; i++) {
325                     IntVar coordVar = o.coords[i];
326                     area.origin[i] = coordVar.min() + piece.origin[i];
327                     area.length[i] = coordVar.max() /*+ hole.origin[i]*/ + piece.length[i] - coordVar.min()/*-hole.origin[i]*/;
328                 }
329                 display2DBox(area, c, true);
330             }
331         }
332         //draw bounding box
333         final boolean draw_bounding_box = false;
334         if (draw_bounding_box) {
335             Color outColor = c.brighter();
336             for (int i = 0; i < o.dimension; i++) {
337                 IntVar coordVar = o.coords[i];
338                 area.origin[i] = coordVar.min() + shape.boundingBox.origin[i];
339                 area.length[i] = coordVar.max() /*+ hole.origin[i]*/ + shape.boundingBox.length[i] - coordVar.min()/*-hole.origin[i]*/;
340             }
341             display2DBox(area, outColor, false);
342         }
343     }
344 
345     /**
346      * It displays a 2D point given its coordinates and color.
347      *
348      * @param point point coordinates.
349      * @param color color the point should be painted with.
350      */
display2DPoint(int[] point, Color color)351     public void display2DPoint(int[] point, Color color) {
352         Graphics g = bufferImage.getGraphics();
353         int height = this.getContentPane().getHeight() - 10;
354         g.setColor(color);
355         g.fillOval(10 + point[0] * pixelsPerUnit, height - (point[1] + 1) * pixelsPerUnit, pixelsPerUnit / 2, pixelsPerUnit / 2);
356 
357         repaint();
358     }
359 
360     /**
361      * It displays 2D Geost object given its shape.
362      *
363      * @param o geost object to be displayed.
364      * @param s the shape of the object to be displayed.
365      */
display2DObject(GeostObject o, Shape s)366     public void display2DObject(GeostObject o, Shape s) {
367         Graphics g = bufferImage.getGraphics();
368         int height = this.getContentPane().getHeight() - 10;
369         DBox bb = s.boundingBox();
370         int ox = o.coords[0].min() * pixelsPerUnit;
371         int oy = o.coords[1].min() * pixelsPerUnit;
372         g.setColor(Color.BLUE);
373         g.drawRect(10 + ox + bb.origin[0] * pixelsPerUnit, height - oy - (bb.origin[1] + bb.length[1]) * pixelsPerUnit,
374             bb.length[0] * pixelsPerUnit, bb.length[1] * pixelsPerUnit);
375 
376         //draw domain
377         g.setColor(Color.GREEN);
378         int dw = (o.coords[0].max() - o.coords[0].min()) * pixelsPerUnit;
379         int dh = (o.coords[1].max() - o.coords[1].min()) * pixelsPerUnit;
380         g.drawRect(10 + ox, height - dh - oy, dw, dh);
381         g.fillRect(10 + ox, height - oy - pixelsPerUnit / 2, pixelsPerUnit / 2, pixelsPerUnit / 2);
382 
383         for (DBox b : s.components()) {
384             display2DBox(b, Color.black);
385         }
386 
387         repaint();
388     }
389 
390     /**
391      * Clear the paint area so drawing can start on fresh canvas.
392      */
eraseAll()393     public void eraseAll() {
394 
395         Graphics g = bufferImage.getGraphics();
396         g.clearRect(0, 0, getWidth(), getHeight());
397     }
398 
399     /**
400      * paints all objects, repaint only if requested to
401      */
paint(Graphics g)402     public void paint(Graphics g) {
403         super.paint(g);
404         g.drawImage(displayImgae, 0, 0, null);
405     }
406 
407     /**
408      * same as paint
409      */
update(Graphics g)410     public void update(Graphics g) {
411         super.update(g);
412         g.drawImage(displayImgae, 0, 0, null);
413     }
414 
415     /**
416      * flips images, making previous operations visible
417      */
flip()418     public void flip() {
419         Image i = displayImgae;
420         displayImgae = bufferImage;
421         bufferImage = i;
422     }
423 }
424