Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
3d35c96d577c73cb705ed8fc1c5121bdf5838d13
[graphlib_java.git] / DrawingWindow.java
1 import java.awt.*;
2 import java.awt.event.*;
3 import java.awt.image.*;
4 import javax.swing.*;
5 import java.lang.reflect.*;
6
7 /**
8  * Fenêtre de dessin
9  *
10  * <p>Cette classe permet d'écrire des applications graphiques simples
11  * en dessinant dans une fenêtre.
12  *
13  * <p><b>NB.</b> Pour toutes les méthodes de dessin, le coin en haut à
14  * gauche de la fenêtre a les coordonnées (0, 0).  Le coin en bas à
15  * droite de la fenêtre a les coordonnées (largeur - 1, hauteur - 1),
16  * si la fenêtre est de dimension largeur × hauteur.
17  *
18  * <p>Un appui sur la touche &lt;Esc&gt; provoque la fermeture de la
19  * fenêtre.  Comme pour la plupart des applications, il est également
20  * possible de fermer la fenêtre via le gestionnaire de fenêtres.
21  *
22  * <p>Télécharger le code: <a href="DrawingWindow.java">DrawingWindow.java</a>
23  *
24  * <p>Télécharger des exemples d'utilisation:
25  * <a href="Hello.java">Hello.java</a>
26  * <a href="Exemple1.java">Exemple1.java</a>
27  * <a href="Exemple2.java">Exemple2.java</a>
28  * <a href="Exemple3.java">Exemple3.java</a>
29  *
30  * @author Arnaud Giersch &lt;arnaud.giersch@univ-fcomte.fr&gt;
31  * @version Thu, 09 Oct 2014 15:27:09 +0200
32  */
33 public class DrawingWindow {
34
35     /** Largeur de la fenêtre */
36     public final int width;
37
38     /** Hauteur de la fenêtre */
39     public final int height;
40
41     /**
42      * Construit une nouvelle fenêtre de dessin avec le titre et les dimensions
43      * passés en paramètres.
44      *
45      * @param title             titre de la fenêtre
46      * @param width             largeur de la fenêtre
47      * @param height            hauteur de la fenêtre
48      *
49      * @see javax.swing.JPanel
50      */
51     public DrawingWindow(String title, int width, int height) {
52
53         this.title = new String(title);
54         this.width = width;
55         this.height = height;
56
57         image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
58         graphics = image.createGraphics();
59
60         try {
61             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
62                     public void run() { createGUI(); }
63                 });
64         }
65         catch (Exception e) {
66             System.err.println("Error: interrupted while creating GUI");
67             System.err.println("Got exception: " + e);
68             System.exit(1);
69         }
70
71         setColor(Color.BLACK);
72         setBgColor(Color.WHITE);
73         clearGraph();
74         sync();
75     }
76
77     /**
78      * Change la couleur de dessin.
79      *
80      * @param color         couleur
81      *
82      * @see java.awt.Color
83      * @see #setColor(String)
84      * @see #setColor(float, float, float)
85      * @see #setBgColor(Color)
86      */
87     public void setColor(Color color) {
88         graphics.setColor(color);
89     }
90
91     /**
92      * Change la couleur de dessin.
93      *
94      * Le nom de couleur est de la forme "black", "white", "red", "blue", ...
95      *
96      * @param name          nom de couleur
97      *
98      * @see #setColor(Color)
99      * @see #setColor(float, float, float)
100      * @see #setBgColor(String)
101      */
102     public void setColor(String name) {
103         try {
104             Field field = Class.forName("java.awt.Color").getField(name);
105             graphics.setColor((Color)field.get(null));
106         } catch (Exception e) {
107             System.err.println("Warning: color not found: " + name);
108         }
109     }
110
111     /**
112      * Change la couleur de dessin.
113      *
114      * Les composantes de rouge, vert et bleu de la couleur doivent être
115      * compris entre 0 et 1.  Si le trois composantes sont à 0, on obtient
116      * du noir; si les trois composantes sont à 1, on obtient du blanc.
117      *
118      * @param red           composante de rouge
119      * @param green         composante de vert
120      * @param blue          composante de bleu
121      *
122      * @see #setColor(Color)
123      * @see #setColor(String)
124      * @see #setBgColor(float, float, float)
125      */
126     public void setColor(float red, float green, float blue) {
127         setColor(new Color(red, green, blue));
128     }
129
130     /**
131      * Change la couleur de fond.
132      *
133      * @param color         couleur
134      *
135      * @see #setBgColor(String)
136      * @see #setBgColor(float, float, float)
137      * @see #setColor(Color)
138      * @see #clearGraph()
139      */
140     public void setBgColor(Color color) {
141         bgColor = color;
142     }
143
144     /**
145      * Change la couleur de fond.
146      *
147      * @param name          nom de couleur
148      *
149      * @see #setBgColor(Color)
150      * @see #setBgColor(float, float, float)
151      * @see #setColor(String)
152      * @see #clearGraph()
153      */
154     public void setBgColor(String name) {
155         try {
156             Field field = Class.forName("java.awt.Color").getField(name);
157             bgColor = (Color)field.get(null);
158         } catch (Exception e) {
159             System.err.println("Warning: color not found: " + name);
160         }
161     }
162
163     /** Change la couleur de fond.
164      *
165      * @param red           composante de rouge
166      * @param green         composante de vert
167      * @param blue          composante de bleu
168      *
169      * @see #setBgColor(Color)
170      * @see #setBgColor(String)
171      * @see #setColor(float, float, float)
172      * @see #clearGraph()
173      */
174     public void setBgColor(float red, float green, float blue) {
175         bgColor = new Color(red, green, blue);
176     }
177
178     /**
179      * Efface la fenêtre.
180      *
181      * La fenêtre est effacée avec la couleur de fond courante.
182      *
183      * @see #setBgColor
184      */
185     public void clearGraph() {
186         synchronized (image) {
187             Color c = graphics.getColor();
188             graphics.setColor(bgColor);
189             graphics.fillRect(0, 0, width, height);
190             graphics.setColor(c);
191         }
192         panel.repaint();
193     }
194
195     /** Dessine un point.
196      *
197      * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de
198      * dessin courante.
199      *
200      * @see #setColor
201      */
202     public void drawPoint(int x, int y) {
203         synchronized (image) {
204             image.setRGB(x, y, graphics.getColor().getRGB());
205         }
206         panel.repaint(x, y, 1, 1);
207     }
208
209     /**
210      * Dessine un segment.
211      *
212      * Dessine un segement de droite entre les coordonnées (x1, y1) et
213      * (x2, y2), avec la couleur de dessin courante.
214      *
215      * @see #setColor
216      */
217     public void drawLine(int x1, int y1, int x2, int y2) {
218         synchronized (image) {
219             graphics.drawLine(x1, y1, x2, y2);
220         }
221         panel.repaint(Math.min(x1, x2), Math.min(y1, y2),
222                       Math.abs(x1 - x2) + 1, Math.abs(y1 - y2) + 1);
223     }
224
225     /** Dessine un rectangle.
226      *
227      * Dessine le rectangle parallèle aux axes et défini par les
228      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
229      * la couleur de dessin courante.
230      *
231      * @see #fillRect
232      * @see #setColor
233      */
234     public void drawRect(int x1, int y1, int x2, int y2) {
235         int x = Math.min(x1, x2);
236         int y = Math.min(y1, y2);
237         int w = Math.abs(x1 - x2);
238         int h = Math.abs(y1 - y2);
239         synchronized (image) {
240             graphics.drawRect(x, y, w, h);
241         }
242         panel.repaint(x, y, w + 1, h + 1);
243     }
244
245     /** Dessine un rectangle plein.
246      *
247      * Dessine le rectangle plein parallèle aux axes et défini par les
248      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
249      * la couleur de dessin courante.
250      *
251      * @see #drawRect
252      * @see #setColor
253      */
254     public void fillRect(int x1, int y1, int x2, int y2) {
255         int x = Math.min(x1, x2);
256         int y = Math.min(y1, y2);
257         int w = Math.abs(x1 - x2) + 1;
258         int h = Math.abs(y1 - y2) + 1;
259         synchronized (image) {
260             graphics.fillRect(x, y, w, h);
261         }
262         panel.repaint(x, y, w, h);
263     }
264
265     /**
266      * Dessine un cercle.
267      *
268      * Dessine un cercle de centre (x, y) et de rayon r.  Utilise la
269      * couleur de dessin courante.
270      *
271      * @see #fillCircle
272      * @see #setColor
273      */
274     public void drawCircle(int x, int y, int r) {
275         synchronized (image) {
276             graphics.drawOval(x - r, y - r, 2 * r, 2 * r);
277         }
278         panel.repaint(x - r, y - r, 2 * r + 1, 2 * r + 1);
279     }
280
281     /**
282      * Dessine un disque.
283      *
284      * Dessine un disque (cercle plein) de centre (x, y) et de rayon r.
285      * Utilise la couleur de dessin courante.
286      *
287      * @see #drawCircle
288      * @see #setColor
289      */
290     public void fillCircle(int x, int y, int r) {
291         synchronized (image) {
292             graphics.drawOval(x - r, y - r, 2 * r, 2 * r);
293             graphics.fillOval(x - r, y - r, 2 * r, 2 * r);
294         }
295         panel.repaint(x - r, y - r, 2 * r + 1, 2 * r + 1);
296     }
297
298     /**
299      * Dessine un triangle.
300      *
301      * Dessine un triangle défini par les coordonnées de ses sommets:
302      * (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de dessin
303      * courante.
304      *
305      * @see #fillTriangle
306      * @see #setColor
307      */
308
309     public void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
310         Polygon poly = new Polygon();
311         poly.addPoint(x1, y1);
312         poly.addPoint(x2, y2);
313         poly.addPoint(x3, y3);
314         synchronized (image) {
315             graphics.drawPolygon(poly);
316         }
317         panel.repaint(poly.getBounds());
318     }
319
320     /**
321      * Dessine un triangle plein.
322      *
323      * Dessine un triangle plein défini par les coordonnées de ses
324      * sommets: (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de
325      * dessin courante.
326      *
327      * @see #drawTriangle
328      * @see #setColor
329      */
330     public void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
331         Polygon poly = new Polygon();
332         poly.addPoint(x1, y1);
333         poly.addPoint(x2, y2);
334         poly.addPoint(x3, y3);
335         synchronized (image) {
336             graphics.drawPolygon(poly);
337             graphics.fillPolygon(poly);
338         }
339         panel.repaint(poly.getBounds());
340     }
341
342     /**
343      * Écrit du texte.
344      *
345      * Écrit le texte text, aux coordonnées (x, y).
346      */
347     public void drawText(int x, int y, String text) {
348         synchronized (image) {
349             graphics.drawString(text, x, y);
350         }
351         panel.repaint(); // don't know how to calculate tighter bounding box
352     }
353
354     /**
355      * Retourne la couleur d'un pixel.
356      *
357      * Retourne la couleur du pixel de coordonnées (x, y).
358      *
359      * @return              couleur du pixel
360      */
361     public int getPointColor(int x, int y) {
362         return image.getRGB(x, y);
363     }
364
365     /**
366      * Synchronise le contenu de la fenêtre.
367      *
368      * Pour des raisons d'efficacités, le résultat des fonctions de dessin
369      * n'est pas affiché immédiatement.  L'appel à sync permet de
370      * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
371      * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
372      * soit à jour.
373      */
374     public void sync() {
375         // put an empty action on the event queue, and  wait for its completion
376         try {
377             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
378                     public void run() { }
379                 });
380         }
381         catch (Exception e) {
382         }
383     }
384
385     /**
386      *  Ferme la fenêtre graphique.
387      */
388     public void closeGraph() {
389         javax.swing.SwingUtilities.invokeLater(new Runnable() {
390                 public void run() {
391                     WindowEvent ev =
392                         new WindowEvent(frame,
393                                         WindowEvent.WINDOW_CLOSING);
394                     Toolkit.getDefaultToolkit()
395                         .getSystemEventQueue().postEvent(ev);
396                 }
397             });
398     }
399
400
401     /**
402      * Suspend l'exécution pendant un certain temps.
403      *
404      * @param secs          temps d'attente en seconde
405      */
406     static void sleep(long secs) {
407         try {
408             Thread.sleep(secs * 1000);
409         }
410         catch (Exception e) {
411         }
412     }
413
414     /**
415      * Suspend l'exécution pendant un certain temps.
416      *
417      * @param msecs          temps d'attente en millisecondes
418      */
419     static void msleep(long msecs) {
420         try {
421             Thread.sleep(msecs);
422         }
423         catch (Exception e) {
424         }
425     }
426
427     /**
428      * Suspend l'exécution pendant un certain temps.
429      *
430      * @param usecs          temps d'attente en microsecondes
431      */
432     static void usleep(long usecs) {
433         try {
434             Thread.sleep(usecs / 1000, (int)(usecs % 1000) * 1000);
435         }
436         catch (Exception e) {
437         }
438     }
439
440     /* PRIVATE STUFF FOLLOWS */
441
442     private final String title; // window's title
443     private JFrame frame;       // the frame (window)
444     private DWPanel panel;      // the panel showing the image
445     private BufferedImage image; // the image we draw into
446     private Graphics2D graphics; // graphics associated with image
447     private Color bgColor;       // background color, for clearGraph()
448
449     // To be run on the Event Dispatching Thread
450     void createGUI() {
451         panel = new DWPanel(this);
452
453         frame = new JFrame(title);
454         frame.add(panel);
455         frame.pack();
456         frame.setResizable(false);
457         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
458         frame.addKeyListener(panel);
459         frame.setLocationByPlatform(true);
460         frame.setVisible(true);
461     }
462
463     private class DWPanel extends JPanel implements KeyListener {
464
465         private static final long serialVersionUID = 0;
466
467         final DrawingWindow w;
468
469         DWPanel(DrawingWindow w) {
470             this.w = w;
471             Dimension dimension = new Dimension(w.width, w.height);
472             super.setMinimumSize(dimension);
473             super.setMaximumSize(dimension);
474             super.setPreferredSize(dimension);
475         }
476
477         public void paint(Graphics g) {
478             synchronized (w.image) {
479                 g.drawImage(w.image, 0, 0, null);
480             }
481         }
482
483         public void keyPressed(KeyEvent e) {
484             if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
485                 w.closeGraph();
486             }
487         }
488
489         public void keyReleased(KeyEvent e) { }
490         public void keyTyped(KeyEvent e) { }
491
492     }
493
494     // Sample tests
495     public static void main(String[] args) {
496         DrawingWindow w = new DrawingWindow("Test!", 400, 400);
497
498         w.setColor("green");
499         for (int i = 0; i < 12; i++) {
500             int p = 10 * i + 10;
501             w.drawLine(p, 0, p, 175);
502             w.drawLine(p + i, 0, p + i, 175);
503         }
504
505         w.setColor("black");
506         for (int i = 0; i < 12; i++) {
507             int p = 10 * i + 10;
508
509             w.drawCircle(p, 25, i);
510             w.fillCircle(p, 50, i);
511
512             w.drawRect(p, 75, p + i, 75 + i);
513             w.fillRect(p, 100, p + i, 100 + i);
514
515             w.drawTriangle(p, 125, p + i, 125 + i/2, p, 125 + i);
516             w.fillTriangle(p, 150, p + i, 150 + i/2, p, 150 + i);
517         }
518     }
519 }