KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > nightlabs > editor2d > j2d > GeneralShape


1 /**
2  * <p> Project: com.nightlabs.gui </p>
3  * <p> Copyright: Copyright (c) 2004 </p>
4  * <p> Company: NightLabs GmbH (Germany) </p>
5  * <p> Creation Date: 10.01.2005 </p>
6  * <p> Author: Daniel Mazurek </p>
7 **/

8 package com.nightlabs.editor2d.j2d;
9
10 import java.awt.Shape JavaDoc;
11 import java.awt.geom.AffineTransform JavaDoc;
12 import java.awt.geom.FlatteningPathIterator JavaDoc;
13 import java.awt.geom.IllegalPathStateException JavaDoc;
14 import java.awt.geom.PathIterator JavaDoc;
15 import java.awt.geom.Point2D JavaDoc;
16 import java.awt.geom.Rectangle2D JavaDoc;
17 import java.io.Serializable JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.List JavaDoc;
21
22 import sun.awt.geom.Crossings;
23 import sun.awt.geom.Curve;
24
25
26 /**
27  * The <code>GeneralShape</code> class represents a geometric path
28  * constructed from straight lines, and quadratic and cubic
29  * (B&eacute;zier) curves. It can contain multiple subpaths.
30  * <p>
31  * The winding rule specifies how the interior of a path is
32  * determined. There are two types of winding rules:
33  * EVEN_ODD and NON_ZERO.
34  * <p>
35  * An EVEN_ODD winding rule means that enclosed regions
36  * of the path alternate between interior and exterior areas as
37  * traversed from the outside of the path towards a point inside
38  * the region.
39  * <p>
40  * A NON_ZERO winding rule means that if a ray is
41  * drawn in any direction from a given point to infinity
42  * and the places where the path intersects
43  * the ray are examined, the point is inside of the path if and only if
44  * the number of times that the path crosses the ray from
45  * left to right does not equal the number of times that the path crosses
46  * the ray from right to left.
47  */

48 public class GeneralShape
49 implements Shape JavaDoc, Cloneable JavaDoc, Serializable JavaDoc
50 {
51     /**
52      * An even-odd winding rule for determining the interior of
53      * a path.
54      */

55     public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
56
57     /**
58      * A non-zero winding rule for determining the interior of a
59      * path.
60      */

61     public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
62
63     // For code simplicity, copy these constants to our namespace
64
// and cast them to byte constants for easy storage.
65
private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
66     private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
67     private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
68     private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
69     private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;
70
71     protected byte[] pointTypes;
72     protected float[] pointCoords;
73     int numTypes;
74     int numCoords;
75     int windingRule;
76     
77 // *************************** Beans Specification BEGIN ****************
78
public byte[] getPointTypes() {
79         return pointTypes;
80     }
81     public void setPointTypes(byte[] _pointTypes) {
82         pointTypes = _pointTypes;
83         bounds = null;
84     }
85     
86     public int getNumCoords() {
87         return numCoords;
88     }
89     public void setNumCoords(int numCoords) {
90         this.numCoords = numCoords;
91         bounds = null;
92     }
93
94     public int getNumTypes() {
95         return numTypes;
96     }
97     public void setNumTypes(int numTypes) {
98         this.numTypes = numTypes;
99         bounds = null;
100     }
101
102     public int getSize() {
103       return numTypes;
104     }
105     
106     public float[] getPointCoords() {
107         return pointCoords;
108     }
109     public void setPointCoords(float[] pointCoords) {
110         this.pointCoords = pointCoords;
111         bounds = null;
112     }
113 // *************************** Beans Specification END ****************
114

115     static final int INIT_SIZE = 20;
116     static final int EXPAND_MAX = 500;
117
118     /**
119      * Constructs a new <code>GeneralShape</code> object.
120      * If an operation performed on this path requires the
121      * interior of the path to be defined then the default NON_ZERO
122      * winding rule is used.
123      * @see #WIND_NON_ZERO
124      */

125     public GeneralShape() {
126         this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
127     }
128
129     /**
130      * Constructs a new <code>GeneralShape</code> object with the specified
131      * winding rule to control operations that require the interior of the
132      * path to be defined.
133      * @param rule the winding rule
134      * @see #WIND_EVEN_ODD
135      * @see #WIND_NON_ZERO
136      */

137     public GeneralShape(int rule) {
138         this(rule, INIT_SIZE, INIT_SIZE);
139     }
140
141     /**
142      * Constructs a new <code>GeneralShape</code> object with the specified
143      * winding rule and the specified initial capacity to store path
144      * coordinates. This number is an initial guess as to how many path
145      * segments are in the path, but the storage is expanded
146      * as needed to store whatever path segments are added to this path.
147      * @param rule the winding rule
148      * @param initialCapacity the estimate for the number of path segments
149      * in the path
150      * @see #WIND_EVEN_ODD
151      * @see #WIND_NON_ZERO
152      */

153     public GeneralShape(int rule, int initialCapacity) {
154         this(rule, initialCapacity, initialCapacity);
155     }
156
157     /**
158      * Constructs a new <code>GeneralShape</code> object with the specified
159      * winding rule and the specified initial capacities to store point types
160      * and coordinates.
161      * These numbers are an initial guess as to how many path segments
162      * and how many points are to be in the path, but the
163      * storage is expanded as needed to store whatever path segments are
164      * added to this path.
165      * @param rule the winding rule
166      * @param initialTypes the estimate for the number of path segments
167      * in the path
168      * @param initialCapacity the estimate for the number of points
169      * @see #WIND_EVEN_ODD
170      * @see #WIND_NON_ZERO
171      */

172     GeneralShape(int rule, int initialTypes, int initialCoords) {
173         setWindingRule(rule);
174         pointTypes = new byte[initialTypes];
175         pointCoords = new float[initialCoords * 2];
176     }
177
178     /**
179      * Constructs a new <code>GeneralShape</code> object from an arbitrary
180      * {@link Shape} object.
181      * All of the initial geometry and the winding rule for this path are
182      * taken from the specified <code>Shape</code> object.
183      * @param s the specified <code>Shape</code> object
184      */

185     public GeneralShape(Shape JavaDoc s) {
186         this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE);
187         PathIterator JavaDoc pi = s.getPathIterator(null);
188         setWindingRule(pi.getWindingRule());
189         append(pi, false);
190     }
191
192     private void needRoom(int newTypes, int newCoords, boolean needMove) {
193         if (needMove && numTypes == 0) {
194             throw new IllegalPathStateException JavaDoc("missing initial moveto "+
195                                                 "in path definition");
196         }
197         int size = pointCoords.length;
198         if (numCoords + newCoords > size) {
199             int grow = size;
200             if (grow > EXPAND_MAX * 2) {
201                 grow = EXPAND_MAX * 2;
202             }
203             if (grow < newCoords) {
204                 grow = newCoords;
205             }
206             float[] arr = new float[size + grow];
207             System.arraycopy(pointCoords, 0, arr, 0, numCoords);
208             pointCoords = arr;
209         }
210         size = pointTypes.length;
211         if (numTypes + newTypes > size) {
212             int grow = size;
213             if (grow > EXPAND_MAX) {
214                 grow = EXPAND_MAX;
215             }
216             if (grow < newTypes) {
217                 grow = newTypes;
218             }
219             byte[] arr = new byte[size + grow];
220             System.arraycopy(pointTypes, 0, arr, 0, numTypes);
221             pointTypes = arr;
222         }
223     }
224
225     /**
226      * Adds a point to the path by moving to the specified
227      * coordinates.
228      * @param x,&nbsp;y the specified coordinates
229      */

230     public synchronized void moveTo(float x, float y) {
231         if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
232             pointCoords[numCoords - 2] = x;
233             pointCoords[numCoords - 1] = y;
234         } else {
235             needRoom(1, 2, false);
236             pointTypes[numTypes++] = SEG_MOVETO;
237             pointCoords[numCoords++] = x;
238             pointCoords[numCoords++] = y;
239         }
240         pathSegments = null;
241     }
242
243     /**
244      * Adds a point to the path by drawing a straight line from the
245      * current coordinates to the new specified coordinates.
246      * @param x,&nbsp;y the specified coordinates
247      */

248     public synchronized void lineTo(float x, float y) {
249         needRoom(1, 2, true);
250         pointTypes[numTypes++] = SEG_LINETO;
251         pointCoords[numCoords++] = x;
252         pointCoords[numCoords++] = y;
253         pathSegments = null;
254         bounds = null;
255     }
256
257     /**
258      * Adds a curved segment, defined by two new points, to the path by
259      * drawing a Quadratic curve that intersects both the current
260      * coordinates and the coordinates (x2,&nbsp;y2), using the
261      * specified point (x1,&nbsp;y1) as a quadratic parametric control
262      * point.
263      * @param x1,&nbsp;y1 the coordinates of the first quadratic control
264      * point
265      * @param x2,&nbsp;y2 the coordinates of the final endpoint
266      */

267     public synchronized void quadTo(float x1, float y1, float x2, float y2) {
268         needRoom(1, 4, true);
269         pointTypes[numTypes++] = SEG_QUADTO;
270         pointCoords[numCoords++] = x1;
271         pointCoords[numCoords++] = y1;
272         pointCoords[numCoords++] = x2;
273         pointCoords[numCoords++] = y2;
274         pathSegments = null;
275         bounds = null;
276     }
277
278     /**
279      * Adds a curved segment, defined by three new points, to the path by
280      * drawing a B&eacute;zier curve that intersects both the current
281      * coordinates and the coordinates (x3,&nbsp;y3), using the
282      * specified points (x1,&nbsp;y1) and (x2,&nbsp;y2) as
283      * B&eacute;zier control points.
284      * @param x1,&nbsp;y1 the coordinates of the first B&eacute;ezier
285      * control point
286      * @param x2,&nbsp;y2 the coordinates of the second B&eacute;zier
287      * control point
288      * @param x3,&nbsp;y3 the coordinates of the final endpoint
289      */

290     public synchronized void curveTo(float x1, float y1,
291                                      float x2, float y2,
292                                      float x3, float y3) {
293         needRoom(1, 6, true);
294         pointTypes[numTypes++] = SEG_CUBICTO;
295         pointCoords[numCoords++] = x1;
296         pointCoords[numCoords++] = y1;
297         pointCoords[numCoords++] = x2;
298         pointCoords[numCoords++] = y2;
299         pointCoords[numCoords++] = x3;
300         pointCoords[numCoords++] = y3;
301         pathSegments = null;
302         bounds = null;
303     }
304
305     /**
306      * Closes the current subpath by drawing a straight line back to
307      * the coordinates of the last <code>moveTo</code>. If the path is already
308      * closed then this method has no effect.
309      */

310     public synchronized void closePath()
311     {
312         if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
313             needRoom(1, 0, true);
314             pointTypes[numTypes++] = SEG_CLOSE;
315         }
316         bounds = null;
317     }
318
319     /**
320      * Appends the geometry of the specified <code>Shape</code> object to the
321      * path, possibly connecting the new geometry to the existing path
322      * segments with a line segment.
323      * If the <code>connect</code> parameter is <code>true</code> and the
324      * path is not empty then any initial <code>moveTo</code> in the
325      * geometry of the appended <code>Shape</code>
326      * is turned into a <code>lineTo</code> segment.
327      * If the destination coordinates of such a connecting <code>lineTo</code>
328      * segment match the ending coordinates of a currently open
329      * subpath then the segment is omitted as superfluous.
330      * The winding rule of the specified <code>Shape</code> is ignored
331      * and the appended geometry is governed by the winding
332      * rule specified for this path.
333      * @param s the <code>Shape</code> whose geometry is appended
334      * to this path
335      * @param connect a boolean to control whether or not to turn an
336      * initial <code>moveTo</code> segment into a <code>lineTo</code>
337      * segment to connect the new geometry to the existing path
338      */

339     public void append(Shape JavaDoc s, boolean connect) {
340         PathIterator JavaDoc pi = s.getPathIterator(null);
341         append(pi,connect);
342     }
343
344     /**
345      * Appends the geometry of the specified
346      * {@link PathIterator} object
347      * to the path, possibly connecting the new geometry to the existing
348      * path segments with a line segment.
349      * If the <code>connect</code> parameter is <code>true</code> and the
350      * path is not empty then any initial <code>moveTo</code> in the
351      * geometry of the appended <code>Shape</code> is turned into a
352      * <code>lineTo</code> segment.
353      * If the destination coordinates of such a connecting <code>lineTo</code>
354      * segment match the ending coordinates of a currently open
355      * subpath then the segment is omitted as superfluous.
356      * The winding rule of the specified <code>Shape</code> is ignored
357      * and the appended geometry is governed by the winding
358      * rule specified for this path.
359      * @param pi the <code>PathIterator</code> whose geometry is appended to
360      * this path
361      * @param connect a boolean to control whether or not to turn an
362      * initial <code>moveTo</code> segment into a <code>lineTo</code> segment
363      * to connect the new geometry to the existing path
364      */

365     public void append(PathIterator JavaDoc pi, boolean connect) {
366         float coords[] = new float[6];
367         while (!pi.isDone()) {
368             switch (pi.currentSegment(coords)) {
369             case SEG_MOVETO:
370                 if (!connect || numTypes < 1 || numCoords < 2) {
371                     moveTo(coords[0], coords[1]);
372                     break;
373                 }
374                 if (pointTypes[numTypes - 1] != SEG_CLOSE &&
375                     pointCoords[numCoords - 2] == coords[0] &&
376                     pointCoords[numCoords - 1] == coords[1])
377                 {
378                     // Collapse out initial moveto/lineto
379
break;
380                 }
381                 // NO BREAK;
382
case SEG_LINETO:
383                 lineTo(coords[0], coords[1]);
384                 break;
385             case SEG_QUADTO:
386                 quadTo(coords[0], coords[1],
387                        coords[2], coords[3]);
388                 break;
389             case SEG_CUBICTO:
390                 curveTo(coords[0], coords[1],
391                         coords[2], coords[3],
392                         coords[4], coords[5]);
393                 break;
394             case SEG_CLOSE:
395                 closePath();
396                 break;
397             }
398             pi.next();
399             connect = false;
400         }
401         pathSegments = null;
402         bounds = null;
403     }
404
405     /**
406      * Returns the fill style winding rule.
407      * @return an integer representing the current winding rule.
408      * @see #WIND_EVEN_ODD
409      * @see #WIND_NON_ZERO
410      * @see #setWindingRule
411      */

412     public synchronized int getWindingRule() {
413         return windingRule;
414     }
415
416     /**
417      * Sets the winding rule for this path to the specified value.
418      * @param rule an integer representing the specified
419      * winding rule
420      * @exception <code>IllegalArgumentException</code> if
421      * <code>rule</code> is not either
422      * <code>WIND_EVEN_ODD</code> or
423      * <code>WIND_NON_ZERO</code>
424      * @see #WIND_EVEN_ODD
425      * @see #WIND_NON_ZERO
426      * @see #getWindingRule
427      */

428     public void setWindingRule(int rule) {
429         if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
430             throw new IllegalArgumentException JavaDoc("winding rule must be "+
431                                                "WIND_EVEN_ODD or "+
432                                                "WIND_NON_ZERO");
433         }
434         windingRule = rule;
435     }
436
437     /**
438      * Returns the coordinates most recently added to the end of the path
439      * as a {@link Point2D} object.
440      * @return a <code>Point2D</code> object containing the ending
441      * coordinates of the path or <code>null</code> if there are no points
442      * in the path.
443      */

444     public synchronized Point2D JavaDoc getCurrentPoint() {
445         if (numTypes < 1 || numCoords < 2) {
446             return null;
447         }
448         int index = numCoords;
449         if (pointTypes[numTypes - 1] == SEG_CLOSE) {
450         loop:
451             for (int i = numTypes - 2; i > 0; i--) {
452                 switch (pointTypes[i]) {
453                 case SEG_MOVETO:
454                     break loop;
455                 case SEG_LINETO:
456                     index -= 2;
457                     break;
458                 case SEG_QUADTO:
459                     index -= 4;
460                     break;
461                 case SEG_CUBICTO:
462                     index -= 6;
463                     break;
464                 case SEG_CLOSE:
465                     break;
466                 }
467             }
468         }
469         return new Point2D.Float JavaDoc(pointCoords[index - 2],
470                                  pointCoords[index - 1]);
471     }
472
473     /**
474      * Resets the path to empty. The append position is set back to the
475      * beginning of the path and all coordinates and point types are
476      * forgotten.
477      */

478     public synchronized void reset() {
479         numTypes = numCoords = 0;
480         bounds = null;
481     }
482
483 // protected PropertyChangeSupport pcs = new PropertyChangeSupport(this);
484
// public void addPropertyChangeListener(PropertyChangeListener pcl) {
485
// pcs.addPropertyChangeListener(pcl);
486
// }
487
// public void removePropertyChangeListener(PropertyChangeListener pcl) {
488
// pcs.removePropertyChangeListener(pcl);
489
// }
490

491     protected List JavaDoc transformListeners;
492     public void addTransformListener(TransformListener listener)
493     {
494       if (transformListeners == null)
495         transformListeners = new ArrayList JavaDoc();
496     
497       transformListeners.add(listener);
498     }
499     public void removeTransformListener(TransformListener listener)
500     {
501       if (transformListeners == null)
502         transformListeners = new ArrayList JavaDoc();
503       
504       transformListeners.remove(listener);
505     }
506     
507     protected void fireTransformChanged(AffineTransform JavaDoc _at)
508     {
509       if (transformListeners == null)
510         return;
511       
512       for (Iterator JavaDoc it = transformListeners.iterator(); it.hasNext(); )
513       {
514         TransformListener listener = (TransformListener) it.next();
515         listener.transformChanged(_at);
516       }
517     }
518     
519     /**
520      * Transforms the geometry of this path using the specified
521      * {@link AffineTransform}.
522      * The geometry is transformed in place, which permanently changes the
523      * boundary defined by this object.
524      * @param at the <code>AffineTransform</code> used to transform the area
525      */

526     public void transform(AffineTransform JavaDoc at)
527     {
528         at.transform(pointCoords, 0, pointCoords, 0, numCoords / 2);
529         pathSegments = null;
530         bounds = null;
531         fireTransformChanged(at);
532     }
533
534     /**
535      * Returns a new transformed <code>Shape</code>.
536      * @param at the <code>AffineTransform</code> used to transform a
537      * new <code>Shape</code>.
538      * @return a new <code>Shape</code>, transformed with the specified
539      * <code>AffineTransform</code>.
540      */

541     public synchronized Shape JavaDoc createTransformedShape(AffineTransform JavaDoc at) {
542         GeneralShape gp = (GeneralShape) clone();
543         if (at != null) {
544             gp.transform(at);
545         }
546         return gp;
547     }
548
549     /**
550      * Return the bounding box of the path.
551      * @return a {@link java.awt.Rectangle} object that
552      * bounds the current path.
553      */

554     public java.awt.Rectangle JavaDoc getBounds() {
555         return getBounds2D().getBounds();
556     }
557
558     protected Rectangle2D JavaDoc bounds;
559     
560     /**
561      * Returns the bounding box of the path.
562      * @return a {@link Rectangle2D} object that
563      * bounds the current path.
564      */

565     public synchronized Rectangle2D JavaDoc getBounds2D()
566     {
567       if (bounds == null)
568       {
569         float x1, y1, x2, y2;
570         int i = numCoords;
571         if (i > 0) {
572             y1 = y2 = pointCoords[--i];
573             x1 = x2 = pointCoords[--i];
574             while (i > 0) {
575                 float y = pointCoords[--i];
576                 float x = pointCoords[--i];
577                 if (x < x1) x1 = x;
578                 if (y < y1) y1 = y;
579                 if (x > x2) x2 = x;
580                 if (y > y2) y2 = y;
581             }
582         } else {
583             x1 = y1 = x2 = y2 = 0.0f;
584         }
585         return new Rectangle2D.Float JavaDoc(x1, y1, x2 - x1, y2 - y1);
586       }
587       else
588         return bounds;
589     }
590
591     /**
592      * Tests if the specified coordinates are inside the boundary of
593      * this <code>Shape</code>.
594      * @param x,&nbsp;y the specified coordinates
595      * @return <code>true</code> if the specified coordinates are inside this
596      * <code>Shape</code>; <code>false</code> otherwise
597      */

598     public boolean contains(double x, double y) {
599         if (numTypes < 2) {
600             return false;
601         }
602         int cross = Curve.crossingsForPath(getPathIterator(null), x, y);
603         if (windingRule == WIND_NON_ZERO) {
604             return (cross != 0);
605         } else {
606             return ((cross & 1) != 0);
607         }
608     }
609
610     /**
611      * Tests if the specified <code>Point2D</code> is inside the boundary
612      * of this <code>Shape</code>.
613      * @param p the specified <code>Point2D</code>
614      * @return <code>true</code> if this <code>Shape</code> contains the
615      * specified <code>Point2D</code>, <code>false</code> otherwise.
616      */

617     public boolean contains(Point2D JavaDoc p) {
618         return contains(p.getX(), p.getY());
619     }
620
621     /**
622      * Tests if the specified rectangular area is inside the boundary of
623      * this <code>Shape</code>.
624      * @param x,&nbsp;y the specified coordinates
625      * @param w the width of the specified rectangular area
626      * @param h the height of the specified rectangular area
627      * @return <code>true</code> if this <code>Shape</code> contains
628      * the specified rectangluar area; <code>false</code> otherwise.
629      */

630     public boolean contains(double x, double y, double w, double h) {
631         Crossings c = Crossings.findCrossings(getPathIterator(null),
632                                               x, y, x+w, y+h);
633         return (c != null && c.covers(y, y+h));
634     }
635
636     /**
637      * Tests if the specified <code>Rectangle2D</code>
638      * is inside the boundary of this <code>Shape</code>.
639      * @param r a specified <code>Rectangle2D</code>
640      * @return <code>true</code> if this <code>Shape</code> bounds the
641      * specified <code>Rectangle2D</code>; <code>false</code> otherwise.
642      */

643     public boolean contains(Rectangle2D JavaDoc r) {
644         return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
645     }
646
647     /**
648      * Tests if the interior of this <code>Shape</code> intersects the
649      * interior of a specified set of rectangular coordinates.
650      * @param x,&nbsp;y the specified coordinates
651      * @param w the width of the specified rectangular coordinates
652      * @param h the height of the specified rectangular coordinates
653      * @return <code>true</code> if this <code>Shape</code> and the
654      * interior of the specified set of rectangular coordinates intersect
655      * each other; <code>false</code> otherwise.
656      */

657     public boolean intersects(double x, double y, double w, double h) {
658         Crossings c = Crossings.findCrossings(getPathIterator(null),
659                                               x, y, x+w, y+h);
660         return (c == null || !c.isEmpty());
661     }
662
663     /**
664      * Tests if the interior of this <code>Shape</code> intersects the
665      * interior of a specified <code>Rectangle2D</code>.
666      * @param r the specified <code>Rectangle2D</code>
667      * @return <code>true</code> if this <code>Shape</code> and the interior
668      * of the specified <code>Rectangle2D</code> intersect each
669      * other; <code>false</code> otherwise.
670      */

671     public boolean intersects(Rectangle2D JavaDoc r) {
672         return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
673     }
674
675     /**
676      * Returns a <code>PathIterator</code> object that iterates along the
677      * boundary of this <code>Shape</code> and provides access to the
678      * geometry of the outline of this <code>Shape</code>.
679      * The iterator for this class is not multi-threaded safe,
680      * which means that this <code>GeneralShape</code> class does not
681      * guarantee that modifications to the geometry of this
682      * <code>GeneralShape</code> object do not affect any iterations of
683      * that geometry that are already in process.
684      * @param at an <code>AffineTransform</code>
685      * @return a new <code>PathIterator</code> that iterates along the
686      * boundary of this <code>Shape</code> and provides access to the
687      * geometry of this <code>Shape</code>'s outline
688      */

689     public PathIterator JavaDoc getPathIterator(AffineTransform JavaDoc at) {
690         return new GeneralShapeIterator(this, at);
691     }
692
693     /**
694      * Returns a <code>PathIterator</code> object that iterates along the
695      * boundary of the flattened <code>Shape</code> and provides access to the
696      * geometry of the outline of the <code>Shape</code>.
697      * The iterator for this class is not multi-threaded safe,
698      * which means that this <code>GeneralShape</code> class does not
699      * guarantee that modifications to the geometry of this
700      * <code>GeneralShape</code> object do not affect any iterations of
701      * that geometry that are already in process.
702      * @param at an <code>AffineTransform</code>
703      * @param flatness the maximum distance that the line segments used to
704      * approximate the curved segments are allowed to deviate
705      * from any point on the original curve
706      * @return a new <code>PathIterator</code> that iterates along the flattened
707      * <code>Shape</code> boundary.
708      */

709     public PathIterator JavaDoc getPathIterator(AffineTransform JavaDoc at, double flatness) {
710         return new FlatteningPathIterator JavaDoc(getPathIterator(at), flatness);
711     }
712
713     /**
714      * Creates a new object of the same class as this object.
715      *
716      * @return a clone of this instance.
717      * @exception OutOfMemoryError if there is not enough memory.
718      * @see java.lang.Cloneable
719      * @since 1.2
720      */

721     public Object JavaDoc clone()
722     {
723 // try
724
// {
725
int pointTypesLength = this.pointTypes.length;
726             byte[] copyPointTypes = new byte[pointTypesLength];
727 // for (int i=0; i<pointTypesLength; i++) {
728
// copyPointTypes[i] = this.pointTypes[i];
729
// }
730
System.arraycopy(pointTypes, 0, copyPointTypes, 0, pointTypesLength);
731             int pointCoordsLength = this.pointCoords.length;
732             float[] copyPointCoords = new float[pointCoordsLength];
733 // for (int i=0; i<pointCoordsLength; i++) {
734
// copyPointCoords[i] = this.pointCoords[i];
735
// }
736
System.arraycopy(pointCoords, 0, copyPointCoords, 0, pointCoordsLength);
737 // GeneralShape copy = (GeneralShape) super.clone();
738
GeneralShape copy = new GeneralShape();
739         copy.windingRule = this.windingRule;
740         copy.numTypes = this.numTypes;
741         copy.pointTypes = copyPointTypes;
742         copy.numCoords = this.numCoords;
743         copy.pointCoords = copyPointCoords;
744         return copy;
745 // }
746
// catch (CloneNotSupportedException e) {
747
// // this shouldn't happen, since we are Cloneable
748
// throw new InternalError();
749
// }
750
}
751
752     GeneralShape(int windingRule,
753                 byte[] pointTypes,
754                 int numTypes,
755                 float[] pointCoords,
756                 int numCoords)
757     {
758     // used to construct from native
759
this.windingRule = windingRule;
760         this.pointTypes = pointTypes;
761         this.numTypes = numTypes;
762         this.pointCoords = pointCoords;
763         this.numCoords = numCoords;
764     }
765    
766     public boolean equals(Object JavaDoc o)
767         {
768         if (o instanceof GeneralShape)
769         {
770             GeneralShape gs = (GeneralShape) o;
771
772             // windigRule
773
if (this.windingRule != gs.getWindingRule())
774                 return false;
775             
776             // numCoords
777
if (this.numCoords != gs.getNumCoords())
778                 return false;
779             
780             // numTypes
781
if (this.numTypes != gs.getNumTypes())
782                 return false;
783             
784             // pointCoords
785
if (this.pointCoords.length != gs.getPointCoords().length) {
786                 return false;
787             }
788             else {
789                 float[] gsPointCoords = gs.getPointCoords();
790                 for (int i=0; i<pointCoords.length; i++) {
791                     float f = pointCoords[i];
792                     if (f != gsPointCoords[i])
793                         return false;
794                 }
795             }
796             
797             // pointTypes
798
if (this.pointTypes.length != gs.pointTypes.length) {
799                 return false;
800             }
801             else {
802                 byte[] gsPointTypes = gs.getPointTypes();
803                 for (int i=0; i<pointTypes.length; i++) {
804                     byte b = pointTypes[i];
805                     if (b != gsPointTypes[i])
806                         return false;
807                 }
808             }
809         
810         } // if (o instanceof GeneralShape)
811
else {
812             return false;
813         }
814         return true;
815     }
816
817     /**
818      * Returns the coordinates most recently added to the end of the path
819      * as a {@link Point2D} object.
820      * @return a <code>Point2D</code> object containing the ending
821      * coordinates of the path or <code>null</code> if there are no points
822      * in the path.
823      */

824     public synchronized void setLastPoint(float x, float y)
825 // public void setLastPoint(float x, float y)
826
{
827         if (numTypes < 1 || numCoords < 2) {
828             return;
829         }
830         int index = numCoords;
831         if (pointTypes[numTypes - 1] == PathIterator.SEG_CLOSE) {
832         loop:
833             for (int i = numTypes - 2; i > 0; i--) {
834                 switch (pointTypes[i]) {
835                 case PathIterator.SEG_MOVETO:
836                     break loop;
837                 case PathIterator.SEG_LINETO:
838                     index -= 2;
839                     break;
840                 case PathIterator.SEG_QUADTO:
841                     index -= 4;
842                     break;
843                 case PathIterator.SEG_CUBICTO:
844                     index -= 6;
845                     break;
846                 case PathIterator.SEG_CLOSE:
847                     break;
848                 }
849             }
850         }
851         pointCoords[index - 2] = x;
852         pointCoords[index - 1] = y;
853         bounds = null;
854     }
855     
856     public synchronized void setPathSegment(int type, int index, float[] coords)
857     {
858       // TODO: check why it sometimes works wrong
859
System.out.println("GeneralShape.setPathSegment");
860       switch (type)
861       {
862         case (PathIterator.SEG_MOVETO):
863           pointCoords[index] = coords[0];
864             pointCoords[index+1] = coords[1];
865             break;
866         case (PathIterator.SEG_LINETO):
867           pointCoords[index] = coords[0];
868             pointCoords[index+1] = coords[1];
869             break;
870         case (PathIterator.SEG_QUADTO):
871           pointCoords[index] = coords[0];
872             pointCoords[index+1] = coords[1];
873           pointCoords[index+2] = coords[2];
874             pointCoords[index+3] = coords[3];
875             break;
876         case (PathIterator.SEG_CUBICTO):
877           pointCoords[index] = coords[0];
878             pointCoords[index+1] = coords[1];
879           pointCoords[index+2] = coords[2];
880             pointCoords[index+3] = coords[3];
881           pointCoords[index+4] = coords[4];
882             pointCoords[index+5] = coords[5];
883             break;
884       }
885       bounds = null;
886     }
887     
888 // public static final float DEFAULT_TOLERANCE = 2;
889
//
890
// public int pathSegmentContains(float x1, float y1)
891
// {
892
// return pathSegmentContains(x1, y1, true);
893
// }
894
//
895
// public int pathSegmentContains(float x1, float y1, boolean tolerance)
896
// {
897
// return pathSegmentContains(x1, y1, DEFAULT_TOLERANCE, tolerance);
898
// }
899
//
900
// public int pathSegmentContains(Point2D p, boolean tolerance)
901
// {
902
// return pathSegmentContains((float)p.getX(), (float)p.getY(), DEFAULT_TOLERANCE, tolerance);
903
// }
904

905 // /**
906
// * Checks if any PathSegment contains the given Point + Distance Tolerance
907
// *
908
// * @param x1 x-Coordinate from the Point
909
// * @param y1 y-Coordinate from the Point
910
// * @param tolerance The Distance-Tolerance
911
// * @return The startIndex of the pointCoords[]-Array for the matching PathSegment
912
// */
913
// public int pathSegmentContains(float x1, float y1, float toleranceValue, boolean tolerance)
914
// {
915
// for (int i=0; i<pointCoords.length-1; i = i+2) {
916
// float x2 = pointCoords[i];
917
// float y2 = pointCoords[i+1];
918
// if (tolerance)
919
// {
920
// if (interior (x1, y1, x2, y2, toleranceValue)) {
921
// return i;
922
// }
923
// } else {
924
// if (x1 == x2 && y1 == y2)
925
// return i;
926
// }
927
// }
928
// return -1;
929
// }
930

931 // /**
932
// * Checks if the distance from one Point to another is within the given distance
933
// *
934
// * @param x1 x-Coordinate from Point 1
935
// * @param y1 y-Coordinate from Point 1
936
// * @param x2 x-Coordinate from Point 2
937
// * @param y2 y-Coordinate from Point 2
938
// * @param tolerance The Distance-Tolerance
939
// *
940
// * @return true if the distance is smaller than the tolerance otherwise false
941
// */
942
// public static boolean interior(float x1, float y1, float x2, float y2, float tolerance)
943
// {
944
// // TODO: maybe generating a Rectangle and call contains(x,y) is faster
945
// double dist = Math.sqrt(Math.pow((x1-x2),2) - Math.pow((y1-y2),2));
946
// return dist <= tolerance ? true : false;
947
// }
948

949     protected void initPathSegments()
950     {
951       pathSegments = new ArrayList JavaDoc();
952       int index = 0;
953       float[] coords = new float[6];
954       for (PathIterator JavaDoc pi = getPathIterator(null); !pi.isDone(); pi.next())
955       {
956         int segType = pi.currentSegment(coords);
957         switch (segType)
958         {
959             case (PathIterator.SEG_MOVETO):
960               PathSegment ps = new PathSegment(segType, index, coords, this);
961                 pathSegments.add(ps);
962                 index = index + 2;
963               break;
964             case (PathIterator.SEG_LINETO):
965               PathSegment ps1 = new PathSegment(segType, index, coords, this);
966                 pathSegments.add(ps1);
967                 index = index + 2;
968               break;
969             case (PathIterator.SEG_QUADTO):
970               PathSegment ps2 = new PathSegment(segType, index, coords, this);
971                 pathSegments.add(ps2);
972                 index = index + 4;
973               break;
974             case (PathIterator.SEG_CUBICTO):
975               PathSegment ps3 = new PathSegment(segType, index, coords, this);
976                 pathSegments.add(ps3);
977                 index = index + 6;
978               break;
979             case (PathIterator.SEG_CLOSE):
980 // PathSegment ps4 = new PathSegment(segType, index, coords, this);
981
// pathSegments.add(ps4);
982
// index = index + 2;
983
break;
984         }
985       }
986     }
987     
988     protected List JavaDoc pathSegments;
989     
990     public List JavaDoc getPathSegments()
991     {
992       if (pathSegments == null)
993         initPathSegments();
994         
995       return pathSegments;
996     }
997     
998     public PathSegment getPathSegment(int index)
999     {
1000      if (pathSegments == null)
1001        initPathSegments();
1002
1003      return (PathSegment) pathSegments.get(index);
1004    }
1005    
1006    public PathSegment getPathSegmentAt(int x, int y)
1007    {
1008      for (Iterator JavaDoc it = getPathSegments().iterator(); it.hasNext(); ) {
1009        PathSegment ps = (PathSegment) it.next();
1010        int contains = ps.contains(x,y);
1011        if (contains != PathSegment.NO_POINT)
1012          return ps;
1013      }
1014      return null;
1015    }
1016    
1017// public int getPathSegmentIndexAt(int x, int y)
1018
// {
1019
// for (int i = 0; i<getPathSegments().size(); i++)
1020
// {
1021
// PathSegment ps = (PathSegment) getPathSegments().get(i);
1022
// int contains = ps.contains(x,y);
1023
// if (contains != PathSegment.NO_POINT)
1024
// return i;
1025
// }
1026
// return -1;
1027
// }
1028

1029    public int getPointCount()
1030    {
1031      int counter = 0;
1032      for (PathIterator JavaDoc pi = getPathIterator(null); !pi.isDone(); pi.next()) {
1033        counter++;
1034      }
1035      return counter;
1036    }
1037        
1038}
1039
1040
Free Books   Free Magazines  
Popular Tags