View Javadoc

1   // Copyright 2001-2004, FreeHEP.
2   package org.freehep.graphics2d;
3   
4   import java.awt.Rectangle;
5   import java.awt.Shape;
6   import java.awt.geom.AffineTransform;
7   import java.awt.geom.PathIterator;
8   import java.awt.geom.Point2D;
9   import java.awt.geom.Rectangle2D;
10  
11  /**
12   * This class can be used to create and render simple shapes quickly and without
13   * memory allocation. A common point array is used for all created shapes. The
14   * factory methods don't return a new shape, but set the object to the selected
15   * shape. Hence, the class is not thread-safe and only one PathIterator can be
16   * used at the same time.<br>
17   * 
18   * @author Simon Fischer
19   * @version $Id: SymbolShape.java 8584 2006-08-10 23:06:37Z duns $
20   */
21  public class SymbolShape implements Shape {
22  
23      private static final double SQRT_2 = Math.sqrt(2.);
24  
25      private static final double SQRT_3 = Math.sqrt(3.);
26  
27      private class ArrayPathIterator implements PathIterator {
28  
29          private int currentPoint = 0;
30  
31          private double[] points;
32  
33          private int[] type;
34  
35          private int numberOfPoints;
36  
37          private ArrayPathIterator(double[] points, int[] type) {
38              this.points = points;
39              this.type = type;
40          }
41  
42          public boolean isDone() {
43              return currentPoint >= numberOfPoints;
44          }
45  
46          public void next() {
47              currentPoint++;
48          }
49  
50          public int currentSegment(double[] coords) {
51              coords[0] = points[2 * currentPoint];
52              coords[1] = points[2 * currentPoint + 1];
53              return type[currentPoint];
54          }
55  
56          public int currentSegment(float[] coords) {
57              coords[0] = (float) points[2 * currentPoint];
58              coords[1] = (float) points[2 * currentPoint + 1];
59              return type[currentPoint];
60          }
61  
62          public int getWindingRule() {
63              return PathIterator.WIND_NON_ZERO;
64          }
65  
66          private void reset() {
67              currentPoint = 0;
68          }
69  
70          private void done() {
71              currentPoint = numberOfPoints;
72          }
73      }
74  
75      private double points[];
76  
77      private int type[];
78  
79      private ArrayPathIterator pathIterator;
80  
81      private double x, y;
82  
83      private double size;
84  
85      private int symbol;
86  
87      public SymbolShape() {
88          ensureNumberOfPoints(10);
89          type[0] = PathIterator.SEG_MOVETO;
90          for (int i = 1; i < type.length; i++) {
91              type[i] = PathIterator.SEG_LINETO;
92          }
93          this.pathIterator = new ArrayPathIterator(points, type);
94      }
95  
96      public boolean contains(double x, double y) {
97          return getBounds2D().contains(x, y);
98      }
99  
100     public boolean contains(double x, double y, double w, double h) {
101         return contains(x, y) && contains(x + w, y) && contains(x, y + h)
102                 && contains(x + w, y + h);
103     }
104 
105     public boolean contains(Point2D p) {
106         return contains(p.getX(), p.getY());
107     }
108 
109     public boolean contains(Rectangle2D r) {
110         return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
111     }
112 
113     /** Returns true, if at least one of the points is contained by the shape. */
114     public boolean intersects(double x, double y, double w, double h) {
115         return contains(x, y) || contains(x + w, y) || contains(x, y + h)
116                 || contains(x + w, y + h);
117     }
118 
119     public boolean intersects(Rectangle2D r) {
120         return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
121     }
122 
123     public PathIterator getPathIterator(AffineTransform at, double flatness) {
124         return getPathIterator(at);
125     }
126 
127     public Rectangle2D getBounds2D() {
128         return new Rectangle2D.Double(x - size / 2, y - size / 2, size, size);
129     }
130 
131     public Rectangle getBounds() {
132         return getBounds2D().getBounds();
133     }
134 
135     public PathIterator getPathIterator(AffineTransform t) {
136         if (t != null) {
137             t.transform(points, 0, pathIterator.points, 0, points.length / 2);
138         }
139         // if (!pathIterator.isDone()) {
140         // System.err.println("SymbolShape: concurrent PathIterator
141         // requested!");
142         // }
143         pathIterator.reset();
144         return pathIterator;
145     }
146 
147     // -------------------- factory methods --------------------
148 
149     private void createNew(int n) {
150         // if (!pathIterator.isDone()) {
151         // System.err.println("SymbolShape: concurrent modification!");
152         // }
153         ensureNumberOfPoints(n);
154         pathIterator.numberOfPoints = n;
155         pathIterator.done();
156     }
157 
158     /**
159      * Type must be one of the symbols defined in VectorGraphicsConstants except
160      * TYPE_CIRCLE.
161      * 
162      * @see org.freehep.graphics2d.VectorGraphicsConstants
163      */
164     public void create(int symbol, double x, double y, double size) {
165         this.symbol = symbol;
166         this.x = x;
167         this.y = y;
168         this.size = size;
169         switch (symbol) {
170         case VectorGraphicsConstants.SYMBOL_VLINE:
171             createVLine(x, y, size);
172             break;
173         case VectorGraphicsConstants.SYMBOL_HLINE:
174             createHLine(x, y, size);
175             break;
176         case VectorGraphicsConstants.SYMBOL_PLUS:
177             createPlus(x, y, size);
178             break;
179         case VectorGraphicsConstants.SYMBOL_CROSS:
180             createCross(x, y, size);
181             break;
182         case VectorGraphicsConstants.SYMBOL_STAR:
183             createStar(x, y, size);
184             break;
185         case VectorGraphicsConstants.SYMBOL_BOX:
186             createBox(x, y, size);
187             break;
188         case VectorGraphicsConstants.SYMBOL_UP_TRIANGLE:
189             createUpTriangle(x, y, size);
190             break;
191         case VectorGraphicsConstants.SYMBOL_DN_TRIANGLE:
192             createDownTriangle(x, y, size);
193             break;
194         case VectorGraphicsConstants.SYMBOL_DIAMOND:
195             createDiamond(x, y, size);
196             break;
197         }
198     }
199 
200     public String toString() {
201         return getClass() + ": " + symbol + " (" + x + ", " + y + ") size: "
202                 + size;
203     }
204 
205     private void createHLine(double x, double y, double size) {
206         createNew(2);
207 
208         type[0] = PathIterator.SEG_MOVETO;
209         points[0] = x - size / 2;
210         points[1] = y;
211 
212         type[1] = PathIterator.SEG_LINETO;
213         points[2] = x + size / 2;
214         points[3] = y;
215     }
216 
217     private void createVLine(double x, double y, double size) {
218         createNew(2);
219 
220         type[0] = PathIterator.SEG_MOVETO;
221         points[0] = x;
222         points[1] = y - size / 2;
223 
224         type[1] = PathIterator.SEG_LINETO;
225         points[2] = x;
226         points[3] = y + size / 2;
227     }
228 
229     private void createPlus(double x, double y, double size) {
230         createNew(4);
231         double length = size / 2.;
232 
233         type[0] = PathIterator.SEG_MOVETO;
234         points[0] = x + length;
235         points[1] = y;
236 
237         type[1] = PathIterator.SEG_LINETO;
238         points[2] = x - length;
239         points[3] = y;
240 
241         type[2] = PathIterator.SEG_MOVETO;
242         points[4] = x;
243         points[5] = y + length;
244 
245         type[3] = PathIterator.SEG_LINETO;
246         points[6] = x;
247         points[7] = y - length;
248     }
249 
250     private void createCross(double x, double y, double size) {
251         createNew(4);
252         double side = size / 2. / SQRT_2;
253 
254         type[0] = PathIterator.SEG_MOVETO;
255         points[0] = x - side;
256         points[1] = y - side;
257 
258         type[1] = PathIterator.SEG_LINETO;
259         points[2] = x + side;
260         points[3] = y + side;
261 
262         type[2] = PathIterator.SEG_MOVETO;
263         points[4] = x + side;
264         points[5] = y - side;
265 
266         type[3] = PathIterator.SEG_LINETO;
267         points[6] = x - side;
268         points[7] = y + side;
269     }
270 
271     private void createStar(double x, double y, double size) {
272         createNew(8);
273 
274         double delta = size / 2.;
275 
276         type[0] = PathIterator.SEG_MOVETO;
277         points[0] = x;
278         points[1] = y - delta;
279 
280         type[1] = PathIterator.SEG_LINETO;
281         points[2] = x;
282         points[3] = y + delta;
283 
284         type[2] = PathIterator.SEG_MOVETO;
285         points[4] = x - delta;
286         points[5] = y;
287 
288         type[3] = PathIterator.SEG_LINETO;
289         points[6] = x + delta;
290         points[7] = y;
291 
292         delta = size / 2. / SQRT_2;
293 
294         type[4] = PathIterator.SEG_MOVETO;
295         points[8] = x - delta;
296         points[9] = y - delta;
297 
298         type[5] = PathIterator.SEG_LINETO;
299         points[10] = x + delta;
300         points[11] = y + delta;
301 
302         type[6] = PathIterator.SEG_MOVETO;
303         points[12] = x + delta;
304         points[13] = y - delta;
305 
306         type[7] = PathIterator.SEG_LINETO;
307         points[14] = x - delta;
308         points[15] = y + delta;
309     }
310 
311     private void createUpTriangle(double x, double y, double size) {
312         createNew(4);
313 
314         type[0] = PathIterator.SEG_MOVETO;
315         points[0] = x;
316         points[1] = y - size / SQRT_3;
317 
318         type[1] = PathIterator.SEG_LINETO;
319         points[2] = x - size / 2.;
320         points[3] = y + (-size / SQRT_3 + size * SQRT_3 / 2.);
321 
322         type[2] = PathIterator.SEG_LINETO;
323         points[4] = x + size / 2.;
324         points[5] = y + (-size / SQRT_3 + size * SQRT_3 / 2.);
325 
326         type[3] = PathIterator.SEG_CLOSE;
327     }
328 
329     private void createDownTriangle(double x, double y, double size) {
330         createNew(4);
331 
332         type[0] = PathIterator.SEG_MOVETO;
333         points[0] = x;
334         points[1] = y + size / SQRT_3;
335 
336         type[1] = PathIterator.SEG_LINETO;
337         points[2] = x - size / 2.;
338         points[3] = y - (-size / SQRT_3 + size * SQRT_3 / 2.);
339 
340         type[2] = PathIterator.SEG_LINETO;
341         points[4] = x + size / 2.;
342         points[5] = y - (-size / SQRT_3 + size * SQRT_3 / 2.);
343 
344         type[3] = PathIterator.SEG_CLOSE;
345     }
346 
347     private void createDiamond(double x, double y, double size) {
348         createNew(5);
349         double length = size / 2.;
350 
351         type[0] = PathIterator.SEG_MOVETO;
352         points[0] = x + length;
353         points[1] = y;
354 
355         type[1] = PathIterator.SEG_LINETO;
356         points[2] = x;
357         points[3] = y + length;
358 
359         type[2] = PathIterator.SEG_LINETO;
360         points[4] = x - length;
361         points[5] = y;
362 
363         type[3] = PathIterator.SEG_LINETO;
364         points[6] = x;
365         points[7] = y - length;
366 
367         type[4] = PathIterator.SEG_CLOSE;
368     }
369 
370     private void createBox(double x, double y, double size) {
371         createNew(5);
372         double side = size / SQRT_2 / 2;
373 
374         type[0] = PathIterator.SEG_MOVETO;
375         points[0] = x - side;
376         points[1] = y - side;
377 
378         type[1] = PathIterator.SEG_LINETO;
379         points[2] = x + side + 1;
380         points[3] = y - side;
381 
382         type[2] = PathIterator.SEG_LINETO;
383         points[4] = x + side + 1;
384         points[5] = y + side + 1;
385 
386         type[3] = PathIterator.SEG_LINETO;
387         points[6] = x - side;
388         points[7] = y + side + 1;
389 
390         type[4] = PathIterator.SEG_CLOSE;
391     }
392 
393     private void ensureNumberOfPoints(int n) {
394         if ((type == null) || (type.length < n)) {
395             this.points = new double[n * 2];
396             this.type = new int[n];
397         }
398     }
399 }