View Javadoc

1   /*
2    * @(#)GeneralPath.java	1.54 00/02/02
3    *
4    * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
5    * 
6    * This software is the proprietary information of Sun Microsystems, Inc.  
7    * Use is subject to license terms.
8    * 
9    */
10  
11  package math.geom2d.curve;
12  
13  import math.geom2d.Point2D;
14  
15  import java.awt.geom.*;
16  import java.awt.Shape;
17  
18  /**
19   * The <code>GeneralPath</code> class represents a geometric path constructed
20   * from straight lines, and quadratic and cubic (Bezier) curves. It can contain
21   * multiple subpaths.
22   * <p>
23   * The winding rule specifies how the interior of a path is determined. There
24   * are two types of winding rules: EVEN_ODD and NON_ZERO.
25   * <p>
26   * An EVEN_ODD winding rule means that enclosed regions of the path alternate
27   * between interior and exterior areas as traversed from the outside of the path
28   * towards a point inside the region.
29   * <p>
30   * A NON_ZERO winding rule means that if a ray is drawn in any direction from a
31   * given point to infinity and the places where the path intersects the ray are
32   * examined, the point is inside of the path if and only if the number of times
33   * that the path crosses the ray from left to right does not equal the number of
34   * times that the path crosses the ray from right to left.
35   * 
36   * @version 1.54, 02/02/00
37   * @author Jim Graham
38   */
39  public final class GeneralPath2D implements Shape, Cloneable {
40  
41      GeneralPath             path;
42  
43      /**
44       * An even-odd winding rule for determining the interior of a path.
45       */
46      public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
47  
48      /**
49       * A non-zero winding rule for determining the interior of a path.
50       */
51      public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
52  
53      /**
54       * Constructs a new <code>GeneralPath</code> object. If an operation
55       * performed on this path requires the interior of the path to be defined
56       * then the default NON_ZERO winding rule is used.
57       * 
58       * @see #WIND_NON_ZERO
59       */
60      public GeneralPath2D() {
61          path = new GeneralPath();
62      }
63  
64      /**
65       * Constructs a new <code>GeneralPath</code> object with the specified
66       * winding rule to control operations that require the interior of the path
67       * to be defined.
68       * 
69       * @param rule the winding rule
70       * @see #WIND_EVEN_ODD
71       * @see #WIND_NON_ZERO
72       */
73      public GeneralPath2D(int rule) {
74          path = new GeneralPath(rule);
75      }
76  
77      /**
78       * Constructs a new <code>GeneralPath</code> object with the specified
79       * winding rule and the specified initial capacity to store path
80       * coordinates. This number is an initial guess as to how many path segments
81       * are in the path, but the storage is expanded as needed to store whatever
82       * path segments are added to this path.
83       * 
84       * @param rule the winding rule
85       * @param initialCapacity the estimate for the number of path segments in
86       *            the path
87       * @see #WIND_EVEN_ODD
88       * @see #WIND_NON_ZERO
89       */
90      public GeneralPath2D(int rule, int initialCapacity) {
91          path = new GeneralPath(rule, initialCapacity);
92      }
93  
94      /**
95       * Constructs a new <code>GeneralPath</code> object from an arbitrary
96       * {@link Shape} object. All of the initial geometry and the winding rule
97       * for this path are taken from the specified <code>Shape</code> object.
98       * 
99       * @param s the specified <code>Shape</code> object
100      */
101     public GeneralPath2D(Shape s) {
102         path = new GeneralPath(s);
103     }
104 
105     /**
106      * Adds a point to the path by moving to the specified coordinates.
107      * 
108      * @param x the x-coordinate of the destination
109      * @param y the y-coordinate of the destination
110      */
111     public synchronized void moveTo(double x, double y) {
112         path.moveTo((float) x, (float) y);
113     }
114 
115     /**
116      * Adds a point to the path by moving to the specified coordinates.
117      * 
118      * @param p the specified point
119      */
120     public synchronized void moveTo(java.awt.geom.Point2D p) {
121         path.moveTo((float) p.getX(), (float) p.getY());
122     }
123 
124     /**
125      * Adds a point to the path by drawing a straight line from the current
126      * coordinates to the new specified coordinates.
127      * 
128      * @param x the x-coordinate of the destination
129      * @param y the y-coordinate of the destination
130      */
131     public synchronized void lineTo(double x, double y) {
132         path.lineTo((float) x, (float) y);
133     }
134 
135     /**
136      * Adds a point to the path by drawing a straight line from the current
137      * coordinates to the new specified coordinates.
138      * 
139      * @param p the coordinate of the destionation point
140      */
141     public synchronized void lineTo(java.awt.geom.Point2D p) {
142         path.lineTo((float) p.getX(), (float) p.getY());
143     }
144 
145     /**
146      * Adds a curved segment, defined by two new points, to the path by drawing
147      * a Quadratic curve that intersects both the current coordinates and the
148      * coordinates (x2,&nbsp;y2), using the specified point (x1,&nbsp;y1) as a
149      * quadratic parametric control point.
150      * 
151      * @param x1 the x-coordinate of the control point
152      * @param y1 the y-coordinate of the control point
153      * @param x2 the x-coordinate of the end point
154      * @param y2 the y-coordinate of the end point
155      */
156     public synchronized void quadTo(double x1, double y1, double x2, double y2) {
157         path.quadTo((float) x1, (float) y1, (float) x2, (float) y2);
158     }
159 
160     /**
161      * Adds a curved segment, defined by two new points, to the path by drawing
162      * a Quadratic curve that intersects both the current coordinates and the
163      * coordinates (x2,&nbsp;y2), using the specified point (x1,&nbsp;y1) as a
164      * quadratic parametric control point.
165      * 
166      * @param p1 the control point
167      * @param p2 the end point
168      */
169     public synchronized void quadTo(java.awt.geom.Point2D p1,
170             java.awt.geom.Point2D p2) {
171         path.quadTo((float) p1.getX(), (float) p1.getY(), (float) p2.getX(),
172                 (float) p2.getY());
173     }
174 
175     /**
176      * Adds a curved segment, defined by three new points, to the path by
177      * drawing a Bezier curve that intersects both the current coordinates and
178      * the coordinates (x3,&nbsp;y3), using the specified points (x1,&nbsp;y1)
179      * and (x2,&nbsp;y2) as Bezier control points.
180      * 
181      * @param x1 the x-coordinate of the first control point
182      * @param y1 the y-coordinate of the first control point
183      * @param x2 the x-coordinate of the second control point
184      * @param y2 the y-coordinate of the second control point
185      * @param x3 the x-coordinate of the end point
186      * @param y3 the y-coordinate of the end point
187      */
188     public synchronized void curveTo(double x1, double y1, double x2,
189             double y2, double x3, double y3) {
190         path.curveTo((float) x1, (float) y1, (float) x2, (float) y2,
191                 (float) x3, (float) y3);
192     }
193 
194     /**
195      * Adds a curved segment, defined by three new points, to the path by
196      * drawing a Bezier curve that intersects both the current coordinates and
197      * the coordinates (x3,&nbsp;y3), using the specified points (x1,&nbsp;y1)
198      * and (x2,&nbsp;y2) as Bezier control points.
199      * 
200      * @param p1 the coordinates of the first control point
201      * @param p2 the coordinates of the second control point
202      * @param p3 the coordinates of the final endpoint
203      */
204     public synchronized void curveTo(java.awt.geom.Point2D p1,
205             java.awt.geom.Point2D p2, java.awt.geom.Point2D p3) {
206         path.curveTo((float) p1.getX(), (float) p1.getY(), (float) p2.getX(),
207                 (float) p2.getY(), (float) p3.getX(), (float) p3.getY());
208     }
209 
210     /**
211      * Closes the current subpath by drawing a straight line back to the
212      * coordinates of the last <code>moveTo</code>. If the path is already
213      * closed then this method has no effect.
214      */
215     public synchronized void closePath() {
216         path.closePath();
217     }
218 
219     /**
220      * Appends the geometry of the specified <code>Shape</code> object to the
221      * path, possibly connecting the new geometry to the existing path segments
222      * with a line segment. If the <code>connect</code> parameter is
223      * <code>true</code> and the path is not empty then any initial
224      * <code>moveTo</code> in the geometry of the appended <code>Shape</code>
225      * is turned into a <code>lineTo</code> segment. If the destination
226      * coordinates of such a connecting <code>lineTo</code> segment match the
227      * ending coordinates of a currently open subpath then the segment is
228      * omitted as superfluous. The winding rule of the specified
229      * <code>Shape</code> is ignored and the appended geometry is governed by
230      * the winding rule specified for this path.
231      * 
232      * @param s the <code>Shape</code> whose geometry is appended to this path
233      * @param connect a boolean to control whether or not to turn an initial
234      *            <code>moveTo</code> segment into a <code>lineTo</code>
235      *            segment to connect the new geometry to the existing path
236      */
237     public void append(Shape s, boolean connect) {
238         path.append(s, connect);
239     }
240 
241     /**
242      * Appends the geometry of the specified {@link PathIterator} object to the
243      * path, possibly connecting the new geometry to the existing path segments
244      * with a line segment. If the <code>connect</code> parameter is
245      * <code>true</code> and the path is not empty then any initial
246      * <code>moveTo</code> in the geometry of the appended <code>Shape</code>
247      * is turned into a <code>lineTo</code> segment. If the destination
248      * coordinates of such a connecting <code>lineTo</code> segment match the
249      * ending coordinates of a currently open subpath then the segment is
250      * omitted as superfluous. The winding rule of the specified
251      * <code>Shape</code> is ignored and the appended geometry is governed by
252      * the winding rule specified for this path.
253      * 
254      * @param pi the <code>PathIterator</code> whose geometry is appended to
255      *            this path
256      * @param connect a boolean to control whether or not to turn an initial
257      *            <code>moveTo</code> segment into a <code>lineTo</code>
258      *            segment to connect the new geometry to the existing path
259      */
260     public void append(PathIterator pi, boolean connect) {
261         path.append(pi, connect);
262     }
263 
264     /**
265      * Returns the fill style winding rule.
266      * 
267      * @return an integer representing the current winding rule.
268      * @see #WIND_EVEN_ODD
269      * @see #WIND_NON_ZERO
270      */
271     public synchronized int getWindingRule() {
272         return path.getWindingRule();
273     }
274 
275     /**
276      * Sets the winding rule for this path to the specified value.
277      * 
278      * @param rule an integer representing the specified winding rule
279      * @exception <code>IllegalArgumentException</code> if <code>rule</code>
280      *                is not either <code>WIND_EVEN_ODD</code> or
281      *                <code>WIND_NON_ZERO</code>
282      * @see #WIND_EVEN_ODD
283      * @see #WIND_NON_ZERO
284      */
285     public void setWindingRule(int rule) {
286         path.setWindingRule(rule);
287     }
288 
289     /**
290      * Returns the coordinates most recently added to the end of the path as a
291      * {@link Point2D} object.
292      * 
293      * @return a <code>Point2D</code> object containing the ending coordinates
294      *         of the path or <code>null</code> if there are no points in the
295      *         path.
296      */
297     public synchronized Point2D getCurrentPoint() {
298         return new Point2D(path.getCurrentPoint());
299     }
300 
301     /**
302      * Resets the path to empty. The append position is set back to the
303      * beginning of the path and all coordinates and point types are forgotten.
304      */
305     public synchronized void reset() {
306         path.reset();
307     }
308 
309     /**
310      * Transforms the geometry of this path using the specified
311      * {@link AffineTransform}. The geometry is transformed in place, which
312      * permanently changes the boundary defined by this object.
313      * 
314      * @param at the <code>AffineTransform</code> used to transform the area
315      */
316     public void transform(AffineTransform at) {
317         path.transform(at);
318     }
319 
320     /**
321      * Returns a new transformed <code>Shape</code>.
322      * 
323      * @param at the <code>AffineTransform</code> used to transform a new
324      *            <code>Shape</code>.
325      * @return a new <code>Shape</code>, transformed with the specified
326      *         <code>AffineTransform</code>.
327      */
328     public synchronized Shape createTransformedShape(AffineTransform at) {
329         return path.createTransformedShape(at);
330     }
331 
332     /**
333      * Return the bounding box of the path.
334      * 
335      * @return a {@link java.awt.Rectangle} object that bounds the current path.
336      */
337     public java.awt.Rectangle getBounds() {
338         return path.getBounds();
339     }
340 
341     /**
342      * Returns the bounding box of the path.
343      * 
344      * @return a {@link Rectangle2D} object that bounds the current path.
345      */
346     public synchronized java.awt.geom.Rectangle2D getBounds2D() {
347         return path.getBounds2D();
348     }
349 
350     /**
351      * Tests if the specified coordinates are inside the boundary of this
352      * <code>Shape</code>.
353      * 
354      * @param x the x-coordinate of the point
355      * @param y the y-coordinate of the point
356      * @return <code>true</code> if the specified coordinates are inside this
357      *         <code>Shape</code>; <code>false</code> otherwise
358      */
359     public boolean contains(double x, double y) {
360         return path.contains(x, y);
361     }
362 
363     /**
364      * Tests if the specified <code>Point2D</code> is inside the boundary of
365      * this <code>Shape</code>.
366      * 
367      * @param p the specified <code>Point2D</code>
368      * @return <code>true</code> if this <code>Shape</code> contains the
369      *         specified <code>Point2D</code>, <code>false</code>
370      *         otherwise.
371      */
372     public boolean contains(java.awt.geom.Point2D p) {
373         return contains(p);
374     }
375 
376     /**
377      * Tests if the specified rectangular area is inside the boundary of this
378      * <code>Shape</code>.
379      * 
380      * @param x the x coordinate of the rectangle
381      * @param y the y coordinate of the rectangle
382      * @param w the width of the specified rectangular area
383      * @param h the height of the specified rectangular area
384      * @return <code>true</code> if this <code>Shape</code> contains the
385      *         specified rectangluar area; <code>false</code> otherwise.
386      */
387     public boolean contains(double x, double y, double w, double h) {
388         return contains(x, y, w, h);
389     }
390 
391     /**
392      * Tests if the specified <code>Rectangle2D</code> is inside the boundary
393      * of this <code>Shape</code>.
394      * 
395      * @param r a specified <code>Rectangle2D</code>
396      * @return <code>true</code> if this <code>Shape</code> bounds the
397      *         specified <code>Rectangle2D</code>; <code>false</code>
398      *         otherwise.
399      */
400     public boolean contains(java.awt.geom.Rectangle2D r) {
401         return path.contains(r);
402     }
403 
404     /**
405      * Tests if the interior of this <code>Shape</code> intersects the
406      * interior of a specified set of rectangular coordinates.
407      * 
408      * @param x the position of the left corner
409      * @param y the position of the bottom corner
410      * @param w the width of the specified rectangular coordinates
411      * @param h the height of the specified rectangular coordinates
412      * @return <code>true</code> if this <code>Shape</code> and the interior
413      *         of the specified set of rectangular coordinates intersect each
414      *         other; <code>false</code> otherwise.
415      */
416     public boolean intersects(double x, double y, double w, double h) {
417         return intersects(x, y, w, h);
418     }
419 
420     /**
421      * Tests if the interior of this <code>Shape</code> intersects the
422      * interior of a specified <code>Rectangle2D</code>.
423      * 
424      * @param r the specified <code>Rectangle2D</code>
425      * @return <code>true</code> if this <code>Shape</code> and the interior
426      *         of the specified <code>Rectangle2D</code> intersect each other;
427      *         <code>false</code> otherwise.
428      */
429     public boolean intersects(java.awt.geom.Rectangle2D r) {
430         return intersects(r);
431     }
432 
433     /**
434      * Returns a <code>PathIterator</code> object that iterates along the
435      * boundary of this <code>Shape</code> and provides access to the geometry
436      * of the outline of this <code>Shape</code>. The iterator for this class
437      * is not multi-threaded safe, which means that this
438      * <code>GeneralPath</code> class does not guarantee that modifications to
439      * the geometry of this <code>GeneralPath</code> object do not affect any
440      * iterations of that geometry that are already in process.
441      * 
442      * @param at an <code>AffineTransform</code>
443      * @return a new <code>PathIterator</code> that iterates along the
444      *         boundary of this <code>Shape</code> and provides access to the
445      *         geometry of this <code>Shape</code>'s outline
446      */
447     public PathIterator getPathIterator(AffineTransform at) {
448         return path.getPathIterator(at);
449     }
450 
451     /**
452      * Returns a <code>PathIterator</code> object that iterates along the
453      * boundary of the flattened <code>Shape</code> and provides access to the
454      * geometry of the outline of the <code>Shape</code>. The iterator for
455      * this class is not multi-threaded safe, which means that this
456      * <code>GeneralPath</code> class does not guarantee that modifications to
457      * the geometry of this <code>GeneralPath</code> object do not affect any
458      * iterations of that geometry that are already in process.
459      * 
460      * @param at an <code>AffineTransform</code>
461      * @param flatness the maximum distance that the line segments used to
462      *            approximate the curved segments are allowed to deviate from
463      *            any point on the original curve
464      * @return a new <code>PathIterator</code> that iterates along the
465      *         flattened <code>Shape</code> boundary.
466      */
467     public PathIterator getPathIterator(AffineTransform at, double flatness) {
468         return path.getPathIterator(at, flatness);
469     }
470 
471     /**
472      * Creates a new object of the same class as this object.
473      * 
474      * @return a clone of this instance.
475      * @exception OutOfMemoryError if there is not enough memory.
476      * @see java.lang.Cloneable
477      * @since 1.2
478      */
479     @Override
480     public Object clone() {
481         return path.clone();
482     }
483 }