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, y2), using the specified point (x1, 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, y2), using the specified point (x1, 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, y3), using the specified points (x1, y1)
179 * and (x2, 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, y3), using the specified points (x1, y1)
198 * and (x2, 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 }