blob: 46187e58664d422a02c628baa9cecf2f47c64a57 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.awt.geom;
27
28import java.awt.Shape;
29import java.awt.Rectangle;
30import sun.awt.geom.Curve;
31import java.io.Serializable;
32import java.io.StreamCorruptedException;
33import java.util.Arrays;
34
35/**
36 * The {@code Path2D} class provides a simple, yet flexible
37 * shape which represents an arbitrary geometric path.
38 * It can fully represent any path which can be iterated by the
39 * {@link PathIterator} interface including all of its segment
40 * types and winding rules and it implements all of the
41 * basic hit testing methods of the {@link Shape} interface.
42 * <p>
43 * Use {@link Path2D.Float} when dealing with data that can be represented
44 * and used with floating point precision. Use {@link Path2D.Double}
45 * for data that requires the accuracy or range of double precision.
46 * <p>
47 * {@code Path2D} provides exactly those facilities required for
48 * basic construction and management of a geometric path and
49 * implementation of the above interfaces with little added
50 * interpretation.
51 * If it is useful to manipulate the interiors of closed
52 * geometric shapes beyond simple hit testing then the
53 * {@link Area} class provides additional capabilities
54 * specifically targeted at closed figures.
55 * While both classes nominally implement the {@code Shape}
56 * interface, they differ in purpose and together they provide
57 * two useful views of a geometric shape where {@code Path2D}
58 * deals primarily with a trajectory formed by path segments
59 * and {@code Area} deals more with interpretation and manipulation
60 * of enclosed regions of 2D geometric space.
61 * <p>
62 * The {@link PathIterator} interface has more detailed descriptions
63 * of the types of segments that make up a path and the winding rules
64 * that control how to determine which regions are inside or outside
65 * the path.
66 *
67 * @author Jim Graham
68 * @since 1.6
69 */
70public abstract class Path2D implements Shape, Cloneable {
71 /**
72 * An even-odd winding rule for determining the interior of
73 * a path.
74 *
75 * @see PathIterator#WIND_EVEN_ODD
76 * @since 1.6
77 */
78 public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
79
80 /**
81 * A non-zero winding rule for determining the interior of a
82 * path.
83 *
84 * @see PathIterator#WIND_NON_ZERO
85 * @since 1.6
86 */
87 public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
88
89 // For code simplicity, copy these constants to our namespace
90 // and cast them to byte constants for easy storage.
91 private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
92 private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
93 private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
94 private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
95 private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;
96
97 transient byte[] pointTypes;
98 transient int numTypes;
99 transient int numCoords;
100 transient int windingRule;
101
102 static final int INIT_SIZE = 20;
103 static final int EXPAND_MAX = 500;
104
105 /**
106 * Constructs a new empty {@code Path2D} object.
107 * It is assumed that the package sibling subclass that is
108 * defaulting to this constructor will fill in all values.
109 *
110 * @since 1.6
111 */
112 /* private protected */
113 Path2D() {
114 }
115
116 /**
117 * Constructs a new {@code Path2D} object from the given
118 * specified initial values.
119 * This method is only intended for internal use and should
120 * not be made public if the other constructors for this class
121 * are ever exposed.
122 *
123 * @param rule the winding rule
124 * @param initialTypes the size to make the initial array to
125 * store the path segment types
126 * @since 1.6
127 */
128 /* private protected */
129 Path2D(int rule, int initialTypes) {
130 setWindingRule(rule);
131 this.pointTypes = new byte[initialTypes];
132 }
133
134 abstract float[] cloneCoordsFloat(AffineTransform at);
135 abstract double[] cloneCoordsDouble(AffineTransform at);
136 abstract void append(float x, float y);
137 abstract void append(double x, double y);
138 abstract Point2D getPoint(int coordindex);
139 abstract void needRoom(boolean needMove, int newCoords);
140 abstract int pointCrossings(double px, double py);
141 abstract int rectCrossings(double rxmin, double rymin,
142 double rxmax, double rymax);
143
144 /**
145 * The {@code Float} class defines a geometric path with
146 * coordinates stored in single precision floating point.
147 *
148 * @since 1.6
149 */
150 public static class Float extends Path2D implements Serializable {
151 transient float floatCoords[];
152
153 /**
154 * Constructs a new empty single precision {@code Path2D} object
155 * with a default winding rule of {@link #WIND_NON_ZERO}.
156 *
157 * @since 1.6
158 */
159 public Float() {
160 this(WIND_NON_ZERO, INIT_SIZE);
161 }
162
163 /**
164 * Constructs a new empty single precision {@code Path2D} object
165 * with the specified winding rule to control operations that
166 * require the interior of the path to be defined.
167 *
168 * @param rule the winding rule
169 * @see #WIND_EVEN_ODD
170 * @see #WIND_NON_ZERO
171 * @since 1.6
172 */
173 public Float(int rule) {
174 this(rule, INIT_SIZE);
175 }
176
177 /**
178 * Constructs a new empty single precision {@code Path2D} object
179 * with the specified winding rule and the specified initial
180 * capacity to store path segments.
181 * This number is an initial guess as to how many path segments
182 * will be added to the path, but the storage is expanded as
183 * needed to store whatever path segments are added.
184 *
185 * @param rule the winding rule
186 * @param initialCapacity the estimate for the number of path segments
187 * in the path
188 * @see #WIND_EVEN_ODD
189 * @see #WIND_NON_ZERO
190 * @since 1.6
191 */
192 public Float(int rule, int initialCapacity) {
193 super(rule, initialCapacity);
194 floatCoords = new float[initialCapacity * 2];
195 }
196
197 /**
198 * Constructs a new single precision {@code Path2D} object
199 * from an arbitrary {@link Shape} object.
200 * All of the initial geometry and the winding rule for this path are
201 * taken from the specified {@code Shape} object.
202 *
203 * @param s the specified {@code Shape} object
204 * @since 1.6
205 */
206 public Float(Shape s) {
207 this(s, null);
208 }
209
210 /**
211 * Constructs a new single precision {@code Path2D} object
212 * from an arbitrary {@link Shape} object, transformed by an
213 * {@link AffineTransform} object.
214 * All of the initial geometry and the winding rule for this path are
215 * taken from the specified {@code Shape} object and transformed
216 * by the specified {@code AffineTransform} object.
217 *
218 * @param s the specified {@code Shape} object
219 * @param at the specified {@code AffineTransform} object
220 * @since 1.6
221 */
222 public Float(Shape s, AffineTransform at) {
223 if (s instanceof Path2D) {
224 Path2D p2d = (Path2D) s;
225 setWindingRule(p2d.windingRule);
226 this.numTypes = p2d.numTypes;
227 this.pointTypes = Arrays.copyOf(p2d.pointTypes,
228 p2d.pointTypes.length);
229 this.numCoords = p2d.numCoords;
230 this.floatCoords = p2d.cloneCoordsFloat(at);
231 } else {
232 PathIterator pi = s.getPathIterator(at);
233 setWindingRule(pi.getWindingRule());
234 this.pointTypes = new byte[INIT_SIZE];
235 this.floatCoords = new float[INIT_SIZE * 2];
236 append(pi, false);
237 }
238 }
239
240 float[] cloneCoordsFloat(AffineTransform at) {
241 float ret[];
242 if (at == null) {
243 ret = Arrays.copyOf(this.floatCoords, this.floatCoords.length);
244 } else {
245 ret = new float[floatCoords.length];
246 at.transform(floatCoords, 0, ret, 0, numCoords / 2);
247 }
248 return ret;
249 }
250
251 double[] cloneCoordsDouble(AffineTransform at) {
252 double ret[] = new double[floatCoords.length];
253 if (at == null) {
254 for (int i = 0; i < numCoords; i++) {
255 ret[i] = floatCoords[i];
256 }
257 } else {
258 at.transform(floatCoords, 0, ret, 0, numCoords / 2);
259 }
260 return ret;
261 }
262
263 void append(float x, float y) {
264 floatCoords[numCoords++] = x;
265 floatCoords[numCoords++] = y;
266 }
267
268 void append(double x, double y) {
269 floatCoords[numCoords++] = (float) x;
270 floatCoords[numCoords++] = (float) y;
271 }
272
273 Point2D getPoint(int coordindex) {
274 return new Point2D.Float(floatCoords[coordindex],
275 floatCoords[coordindex+1]);
276 }
277
278 void needRoom(boolean needMove, int newCoords) {
279 if (needMove && numTypes == 0) {
280 throw new IllegalPathStateException("missing initial moveto "+
281 "in path definition");
282 }
283 int size = pointTypes.length;
284 if (numTypes >= size) {
285 int grow = size;
286 if (grow > EXPAND_MAX) {
287 grow = EXPAND_MAX;
288 }
289 pointTypes = Arrays.copyOf(pointTypes, size+grow);
290 }
291 size = floatCoords.length;
292 if (numCoords + newCoords > size) {
293 int grow = size;
294 if (grow > EXPAND_MAX * 2) {
295 grow = EXPAND_MAX * 2;
296 }
297 if (grow < newCoords) {
298 grow = newCoords;
299 }
300 floatCoords = Arrays.copyOf(floatCoords, size+grow);
301 }
302 }
303
304 /**
305 * {@inheritDoc}
306 * @since 1.6
307 */
308 public final synchronized void moveTo(double x, double y) {
309 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
310 floatCoords[numCoords-2] = (float) x;
311 floatCoords[numCoords-1] = (float) y;
312 } else {
313 needRoom(false, 2);
314 pointTypes[numTypes++] = SEG_MOVETO;
315 floatCoords[numCoords++] = (float) x;
316 floatCoords[numCoords++] = (float) y;
317 }
318 }
319
320 /**
321 * Adds a point to the path by moving to the specified
322 * coordinates specified in float precision.
323 * <p>
324 * This method provides a single precision variant of
325 * the double precision {@code moveTo()} method on the
326 * base {@code Path2D} class.
327 *
328 * @param x the specified X coordinate
329 * @param y the specified Y coordinate
330 * @see Path2D#moveTo
331 * @since 1.6
332 */
333 public final synchronized void moveTo(float x, float y) {
334 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
335 floatCoords[numCoords-2] = x;
336 floatCoords[numCoords-1] = y;
337 } else {
338 needRoom(false, 2);
339 pointTypes[numTypes++] = SEG_MOVETO;
340 floatCoords[numCoords++] = x;
341 floatCoords[numCoords++] = y;
342 }
343 }
344
345 /**
346 * {@inheritDoc}
347 * @since 1.6
348 */
349 public final synchronized void lineTo(double x, double y) {
350 needRoom(true, 2);
351 pointTypes[numTypes++] = SEG_LINETO;
352 floatCoords[numCoords++] = (float) x;
353 floatCoords[numCoords++] = (float) y;
354 }
355
356 /**
357 * Adds a point to the path by drawing a straight line from the
358 * current coordinates to the new specified coordinates
359 * specified in float precision.
360 * <p>
361 * This method provides a single precision variant of
362 * the double precision {@code lineTo()} method on the
363 * base {@code Path2D} class.
364 *
365 * @param x the specified X coordinate
366 * @param y the specified Y coordinate
367 * @see Path2D#lineTo
368 * @since 1.6
369 */
370 public final synchronized void lineTo(float x, float y) {
371 needRoom(true, 2);
372 pointTypes[numTypes++] = SEG_LINETO;
373 floatCoords[numCoords++] = x;
374 floatCoords[numCoords++] = y;
375 }
376
377 /**
378 * {@inheritDoc}
379 * @since 1.6
380 */
381 public final synchronized void quadTo(double x1, double y1,
382 double x2, double y2)
383 {
384 needRoom(true, 4);
385 pointTypes[numTypes++] = SEG_QUADTO;
386 floatCoords[numCoords++] = (float) x1;
387 floatCoords[numCoords++] = (float) y1;
388 floatCoords[numCoords++] = (float) x2;
389 floatCoords[numCoords++] = (float) y2;
390 }
391
392 /**
393 * Adds a curved segment, defined by two new points, to the path by
394 * drawing a Quadratic curve that intersects both the current
395 * coordinates and the specified coordinates {@code (x2,y2)},
396 * using the specified point {@code (x1,y1)} as a quadratic
397 * parametric control point.
398 * All coordinates are specified in float precision.
399 * <p>
400 * This method provides a single precision variant of
401 * the double precision {@code quadTo()} method on the
402 * base {@code Path2D} class.
403 *
404 * @param x1 the X coordinate of the quadratic control point
405 * @param y1 the Y coordinate of the quadratic control point
406 * @param x2 the X coordinate of the final end point
407 * @param y2 the Y coordinate of the final end point
408 * @see Path2D#quadTo
409 * @since 1.6
410 */
411 public final synchronized void quadTo(float x1, float y1,
412 float x2, float y2)
413 {
414 needRoom(true, 4);
415 pointTypes[numTypes++] = SEG_QUADTO;
416 floatCoords[numCoords++] = x1;
417 floatCoords[numCoords++] = y1;
418 floatCoords[numCoords++] = x2;
419 floatCoords[numCoords++] = y2;
420 }
421
422 /**
423 * {@inheritDoc}
424 * @since 1.6
425 */
426 public final synchronized void curveTo(double x1, double y1,
427 double x2, double y2,
428 double x3, double y3)
429 {
430 needRoom(true, 6);
431 pointTypes[numTypes++] = SEG_CUBICTO;
432 floatCoords[numCoords++] = (float) x1;
433 floatCoords[numCoords++] = (float) y1;
434 floatCoords[numCoords++] = (float) x2;
435 floatCoords[numCoords++] = (float) y2;
436 floatCoords[numCoords++] = (float) x3;
437 floatCoords[numCoords++] = (float) y3;
438 }
439
440 /**
441 * Adds a curved segment, defined by three new points, to the path by
442 * drawing a B&eacute;zier curve that intersects both the current
443 * coordinates and the specified coordinates {@code (x3,y3)},
444 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
445 * B&eacute;zier control points.
446 * All coordinates are specified in float precision.
447 * <p>
448 * This method provides a single precision variant of
449 * the double precision {@code curveTo()} method on the
450 * base {@code Path2D} class.
451 *
452 * @param x1 the X coordinate of the first B&eacute;zier control point
453 * @param y1 the Y coordinate of the first B&eacute;zier control point
454 * @param x2 the X coordinate of the second B&eacute;zier control point
455 * @param y2 the Y coordinate of the second B&eacute;zier control point
456 * @param x3 the X coordinate of the final end point
457 * @param y3 the Y coordinate of the final end point
458 * @see Path2D#curveTo
459 * @since 1.6
460 */
461 public final synchronized void curveTo(float x1, float y1,
462 float x2, float y2,
463 float x3, float y3)
464 {
465 needRoom(true, 6);
466 pointTypes[numTypes++] = SEG_CUBICTO;
467 floatCoords[numCoords++] = x1;
468 floatCoords[numCoords++] = y1;
469 floatCoords[numCoords++] = x2;
470 floatCoords[numCoords++] = y2;
471 floatCoords[numCoords++] = x3;
472 floatCoords[numCoords++] = y3;
473 }
474
475 int pointCrossings(double px, double py) {
476 double movx, movy, curx, cury, endx, endy;
477 float coords[] = floatCoords;
478 curx = movx = coords[0];
479 cury = movy = coords[1];
480 int crossings = 0;
481 int ci = 2;
482 for (int i = 1; i < numTypes; i++) {
483 switch (pointTypes[i]) {
484 case PathIterator.SEG_MOVETO:
485 if (cury != movy) {
486 crossings +=
487 Curve.pointCrossingsForLine(px, py,
488 curx, cury,
489 movx, movy);
490 }
491 movx = curx = coords[ci++];
492 movy = cury = coords[ci++];
493 break;
494 case PathIterator.SEG_LINETO:
495 crossings +=
496 Curve.pointCrossingsForLine(px, py,
497 curx, cury,
498 endx = coords[ci++],
499 endy = coords[ci++]);
500 curx = endx;
501 cury = endy;
502 break;
503 case PathIterator.SEG_QUADTO:
504 crossings +=
505 Curve.pointCrossingsForQuad(px, py,
506 curx, cury,
507 coords[ci++],
508 coords[ci++],
509 endx = coords[ci++],
510 endy = coords[ci++],
511 0);
512 curx = endx;
513 cury = endy;
514 break;
515 case PathIterator.SEG_CUBICTO:
516 crossings +=
517 Curve.pointCrossingsForCubic(px, py,
518 curx, cury,
519 coords[ci++],
520 coords[ci++],
521 coords[ci++],
522 coords[ci++],
523 endx = coords[ci++],
524 endy = coords[ci++],
525 0);
526 curx = endx;
527 cury = endy;
528 break;
529 case PathIterator.SEG_CLOSE:
530 if (cury != movy) {
531 crossings +=
532 Curve.pointCrossingsForLine(px, py,
533 curx, cury,
534 movx, movy);
535 }
536 curx = movx;
537 cury = movy;
538 break;
539 }
540 }
541 if (cury != movy) {
542 crossings +=
543 Curve.pointCrossingsForLine(px, py,
544 curx, cury,
545 movx, movy);
546 }
547 return crossings;
548 }
549
550 int rectCrossings(double rxmin, double rymin,
551 double rxmax, double rymax)
552 {
553 float coords[] = floatCoords;
554 double curx, cury, movx, movy, endx, endy;
555 curx = movx = coords[0];
556 cury = movy = coords[1];
557 int crossings = 0;
558 int ci = 2;
559 for (int i = 1;
560 crossings != Curve.RECT_INTERSECTS && i < numTypes;
561 i++)
562 {
563 switch (pointTypes[i]) {
564 case PathIterator.SEG_MOVETO:
565 if (curx != movx || cury != movy) {
566 crossings =
567 Curve.rectCrossingsForLine(crossings,
568 rxmin, rymin,
569 rxmax, rymax,
570 curx, cury,
571 movx, movy);
572 }
573 // Count should always be a multiple of 2 here.
574 // assert((crossings & 1) != 0);
575 movx = curx = coords[ci++];
576 movy = cury = coords[ci++];
577 break;
578 case PathIterator.SEG_LINETO:
579 crossings =
580 Curve.rectCrossingsForLine(crossings,
581 rxmin, rymin,
582 rxmax, rymax,
583 curx, cury,
584 endx = coords[ci++],
585 endy = coords[ci++]);
586 curx = endx;
587 cury = endy;
588 break;
589 case PathIterator.SEG_QUADTO:
590 crossings =
591 Curve.rectCrossingsForQuad(crossings,
592 rxmin, rymin,
593 rxmax, rymax,
594 curx, cury,
595 coords[ci++],
596 coords[ci++],
597 endx = coords[ci++],
598 endy = coords[ci++],
599 0);
600 curx = endx;
601 cury = endy;
602 break;
603 case PathIterator.SEG_CUBICTO:
604 crossings =
605 Curve.rectCrossingsForCubic(crossings,
606 rxmin, rymin,
607 rxmax, rymax,
608 curx, cury,
609 coords[ci++],
610 coords[ci++],
611 coords[ci++],
612 coords[ci++],
613 endx = coords[ci++],
614 endy = coords[ci++],
615 0);
616 curx = endx;
617 cury = endy;
618 break;
619 case PathIterator.SEG_CLOSE:
620 if (curx != movx || cury != movy) {
621 crossings =
622 Curve.rectCrossingsForLine(crossings,
623 rxmin, rymin,
624 rxmax, rymax,
625 curx, cury,
626 movx, movy);
627 }
628 curx = movx;
629 cury = movy;
630 // Count should always be a multiple of 2 here.
631 // assert((crossings & 1) != 0);
632 break;
633 }
634 }
635 if (crossings != Curve.RECT_INTERSECTS &&
636 (curx != movx || cury != movy))
637 {
638 crossings =
639 Curve.rectCrossingsForLine(crossings,
640 rxmin, rymin,
641 rxmax, rymax,
642 curx, cury,
643 movx, movy);
644 }
645 // Count should always be a multiple of 2 here.
646 // assert((crossings & 1) != 0);
647 return crossings;
648 }
649
650 /**
651 * {@inheritDoc}
652 * @since 1.6
653 */
654 public final void append(PathIterator pi, boolean connect) {
655 float coords[] = new float[6];
656 while (!pi.isDone()) {
657 switch (pi.currentSegment(coords)) {
658 case SEG_MOVETO:
659 if (!connect || numTypes < 1 || numCoords < 1) {
660 moveTo(coords[0], coords[1]);
661 break;
662 }
663 if (pointTypes[numTypes - 1] != SEG_CLOSE &&
664 floatCoords[numCoords-2] == coords[0] &&
665 floatCoords[numCoords-1] == coords[1])
666 {
667 // Collapse out initial moveto/lineto
668 break;
669 }
670 // NO BREAK;
671 case SEG_LINETO:
672 lineTo(coords[0], coords[1]);
673 break;
674 case SEG_QUADTO:
675 quadTo(coords[0], coords[1],
676 coords[2], coords[3]);
677 break;
678 case SEG_CUBICTO:
679 curveTo(coords[0], coords[1],
680 coords[2], coords[3],
681 coords[4], coords[5]);
682 break;
683 case SEG_CLOSE:
684 closePath();
685 break;
686 }
687 pi.next();
688 connect = false;
689 }
690 }
691
692 /**
693 * {@inheritDoc}
694 * @since 1.6
695 */
696 public final void transform(AffineTransform at) {
697 at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2);
698 }
699
700 /**
701 * {@inheritDoc}
702 * @since 1.6
703 */
704 public final synchronized Rectangle2D getBounds2D() {
705 float x1, y1, x2, y2;
706 int i = numCoords;
707 if (i > 0) {
708 y1 = y2 = floatCoords[--i];
709 x1 = x2 = floatCoords[--i];
710 while (i > 0) {
711 float y = floatCoords[--i];
712 float x = floatCoords[--i];
713 if (x < x1) x1 = x;
714 if (y < y1) y1 = y;
715 if (x > x2) x2 = x;
716 if (y > y2) y2 = y;
717 }
718 } else {
719 x1 = y1 = x2 = y2 = 0.0f;
720 }
721 return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1);
722 }
723
724 /**
725 * {@inheritDoc}
726 * <p>
727 * The iterator for this class is not multi-threaded safe,
728 * which means that the {@code Path2D} class does not
729 * guarantee that modifications to the geometry of this
730 * {@code Path2D} object do not affect any iterations of
731 * that geometry that are already in process.
732 *
733 * @since 1.6
734 */
735 public PathIterator getPathIterator(AffineTransform at) {
736 if (at == null) {
737 return new CopyIterator(this);
738 } else {
739 return new TxIterator(this, at);
740 }
741 }
742
743 /**
744 * Creates a new object of the same class as this object.
745 *
746 * @return a clone of this instance.
747 * @exception OutOfMemoryError if there is not enough memory.
748 * @see java.lang.Cloneable
749 * @since 1.6
750 */
751 public final Object clone() {
752 // Note: It would be nice to have this return Path2D
753 // but one of our subclasses (GeneralPath) needs to
754 // offer "public Object clone()" for backwards
755 // compatibility so we cannot restrict it further.
756 // REMIND: Can we do both somehow?
757 if (this instanceof GeneralPath) {
758 return new GeneralPath(this);
759 } else {
760 return new Path2D.Float(this);
761 }
762 }
763
764 /*
765 * JDK 1.6 serialVersionUID
766 */
767 private static final long serialVersionUID = 6990832515060788886L;
768
769 /**
770 * Writes the default serializable fields to the
771 * {@code ObjectOutputStream} followed by an explicit
772 * serialization of the path segments stored in this
773 * path.
774 *
775 * @serialData
776 * <a name="Path2DSerialData"><!-- --></a>
777 * <ol>
778 * <li>The default serializable fields.
779 * There are no default serializable fields as of 1.6.
780 * <li>followed by
781 * a byte indicating the storage type of the original object
782 * as a hint (SERIAL_STORAGE_FLT_ARRAY)
783 * <li>followed by
784 * an integer indicating the number of path segments to follow (NP)
785 * or -1 to indicate an unknown number of path segments follows
786 * <li>followed by
787 * an integer indicating the total number of coordinates to follow (NC)
788 * or -1 to indicate an unknown number of coordinates follows
789 * (NC should always be even since coordinates always appear in pairs
790 * representing an x,y pair)
791 * <li>followed by
792 * a byte indicating the winding rule
793 * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
794 * {@link #WIND_NON_ZERO WIND_NON_ZERO})
795 * <li>followed by
796 * NP (or unlimited if NP < 0) sets of values consisting of
797 * a single byte indicating a path segment type
798 * followed by one or more pairs of float or double
799 * values representing the coordinates of the path segment
800 * <li>followed by
801 * a byte indicating the end of the path (SERIAL_PATH_END).
802 * </ol>
803 * <p>
804 * The following byte value constants are used in the serialized form
805 * of {@code Path2D} objects:
806 * <table>
807 * <tr>
808 * <th>Constant Name</th>
809 * <th>Byte Value</th>
810 * <th>Followed by</th>
811 * <th>Description</th>
812 * </tr>
813 * <tr>
814 * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
815 * <td>0x30</td>
816 * <td></td>
817 * <td>A hint that the original {@code Path2D} object stored
818 * the coordinates in a Java array of floats.</td>
819 * </tr>
820 * <tr>
821 * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
822 * <td>0x31</td>
823 * <td></td>
824 * <td>A hint that the original {@code Path2D} object stored
825 * the coordinates in a Java array of doubles.</td>
826 * </tr>
827 * <tr>
828 * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
829 * <td>0x40</td>
830 * <td>2 floats</td>
831 * <td>A {@link #moveTo moveTo} path segment follows.</td>
832 * </tr>
833 * <tr>
834 * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
835 * <td>0x41</td>
836 * <td>2 floats</td>
837 * <td>A {@link #lineTo lineTo} path segment follows.</td>
838 * </tr>
839 * <tr>
840 * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
841 * <td>0x42</td>
842 * <td>4 floats</td>
843 * <td>A {@link #quadTo quadTo} path segment follows.</td>
844 * </tr>
845 * <tr>
846 * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
847 * <td>0x43</td>
848 * <td>6 floats</td>
849 * <td>A {@link #curveTo curveTo} path segment follows.</td>
850 * </tr>
851 * <tr>
852 * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
853 * <td>0x50</td>
854 * <td>2 doubles</td>
855 * <td>A {@link #moveTo moveTo} path segment follows.</td>
856 * </tr>
857 * <tr>
858 * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
859 * <td>0x51</td>
860 * <td>2 doubles</td>
861 * <td>A {@link #lineTo lineTo} path segment follows.</td>
862 * </tr>
863 * <tr>
864 * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
865 * <td>0x52</td>
866 * <td>4 doubles</td>
867 * <td>A {@link #curveTo curveTo} path segment follows.</td>
868 * </tr>
869 * <tr>
870 * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
871 * <td>0x53</td>
872 * <td>6 doubles</td>
873 * <td>A {@link #curveTo curveTo} path segment follows.</td>
874 * </tr>
875 * <tr>
876 * <td>{@code SERIAL_SEG_CLOSE}</td>
877 * <td>0x60</td>
878 * <td></td>
879 * <td>A {@link #closePath closePath} path segment.</td>
880 * </tr>
881 * <tr>
882 * <td>{@code SERIAL_PATH_END}</td>
883 * <td>0x61</td>
884 * <td></td>
885 * <td>There are no more path segments following.</td>
886 * </table>
887 *
888 * @since 1.6
889 */
890 private void writeObject(java.io.ObjectOutputStream s)
891 throws java.io.IOException
892 {
893 super.writeObject(s, false);
894 }
895
896 /**
897 * Reads the default serializable fields from the
898 * {@code ObjectInputStream} followed by an explicit
899 * serialization of the path segments stored in this
900 * path.
901 * <p>
902 * There are no default serializable fields as of 1.6.
903 * <p>
904 * The serial data for this object is described in the
905 * writeObject method.
906 *
907 * @since 1.6
908 */
909 private void readObject(java.io.ObjectInputStream s)
910 throws java.lang.ClassNotFoundException, java.io.IOException
911 {
912 super.readObject(s, false);
913 }
914
915 static class CopyIterator extends Path2D.Iterator {
916 float floatCoords[];
917
918 CopyIterator(Path2D.Float p2df) {
919 super(p2df);
920 this.floatCoords = p2df.floatCoords;
921 }
922
923 public int currentSegment(float[] coords) {
924 int type = path.pointTypes[typeIdx];
925 int numCoords = curvecoords[type];
926 if (numCoords > 0) {
927 System.arraycopy(floatCoords, pointIdx,
928 coords, 0, numCoords);
929 }
930 return type;
931 }
932
933 public int currentSegment(double[] coords) {
934 int type = path.pointTypes[typeIdx];
935 int numCoords = curvecoords[type];
936 if (numCoords > 0) {
937 for (int i = 0; i < numCoords; i++) {
938 coords[i] = floatCoords[pointIdx + i];
939 }
940 }
941 return type;
942 }
943 }
944
945 static class TxIterator extends Path2D.Iterator {
946 float floatCoords[];
947 AffineTransform affine;
948
949 TxIterator(Path2D.Float p2df, AffineTransform at) {
950 super(p2df);
951 this.floatCoords = p2df.floatCoords;
952 this.affine = at;
953 }
954
955 public int currentSegment(float[] coords) {
956 int type = path.pointTypes[typeIdx];
957 int numCoords = curvecoords[type];
958 if (numCoords > 0) {
959 affine.transform(floatCoords, pointIdx,
960 coords, 0, numCoords / 2);
961 }
962 return type;
963 }
964
965 public int currentSegment(double[] coords) {
966 int type = path.pointTypes[typeIdx];
967 int numCoords = curvecoords[type];
968 if (numCoords > 0) {
969 affine.transform(floatCoords, pointIdx,
970 coords, 0, numCoords / 2);
971 }
972 return type;
973 }
974 }
975
976 }
977
978 /**
979 * The {@code Double} class defines a geometric path with
980 * coordinates stored in double precision floating point.
981 *
982 * @since 1.6
983 */
984 public static class Double extends Path2D implements Serializable {
985 transient double doubleCoords[];
986
987 /**
988 * Constructs a new empty double precision {@code Path2D} object
989 * with a default winding rule of {@link #WIND_NON_ZERO}.
990 *
991 * @since 1.6
992 */
993 public Double() {
994 this(WIND_NON_ZERO, INIT_SIZE);
995 }
996
997 /**
998 * Constructs a new empty double precision {@code Path2D} object
999 * with the specified winding rule to control operations that
1000 * require the interior of the path to be defined.
1001 *
1002 * @param rule the winding rule
1003 * @see #WIND_EVEN_ODD
1004 * @see #WIND_NON_ZERO
1005 * @since 1.6
1006 */
1007 public Double(int rule) {
1008 this(rule, INIT_SIZE);
1009 }
1010
1011 /**
1012 * Constructs a new empty double precision {@code Path2D} object
1013 * with the specified winding rule and the specified initial
1014 * capacity to store path segments.
1015 * This number is an initial guess as to how many path segments
1016 * are in the path, but the storage is expanded as needed to store
1017 * whatever path segments are added to this path.
1018 *
1019 * @param rule the winding rule
1020 * @param initialCapacity the estimate for the number of path segments
1021 * in the path
1022 * @see #WIND_EVEN_ODD
1023 * @see #WIND_NON_ZERO
1024 * @since 1.6
1025 */
1026 public Double(int rule, int initialCapacity) {
1027 super(rule, initialCapacity);
1028 doubleCoords = new double[initialCapacity * 2];
1029 }
1030
1031 /**
1032 * Constructs a new double precision {@code Path2D} object
1033 * from an arbitrary {@link Shape} object.
1034 * All of the initial geometry and the winding rule for this path are
1035 * taken from the specified {@code Shape} object.
1036 *
1037 * @param s the specified {@code Shape} object
1038 * @since 1.6
1039 */
1040 public Double(Shape s) {
1041 this(s, null);
1042 }
1043
1044 /**
1045 * Constructs a new double precision {@code Path2D} object
1046 * from an arbitrary {@link Shape} object, transformed by an
1047 * {@link AffineTransform} object.
1048 * All of the initial geometry and the winding rule for this path are
1049 * taken from the specified {@code Shape} object and transformed
1050 * by the specified {@code AffineTransform} object.
1051 *
1052 * @param s the specified {@code Shape} object
1053 * @param at the specified {@code AffineTransform} object
1054 * @since 1.6
1055 */
1056 public Double(Shape s, AffineTransform at) {
1057 if (s instanceof Path2D) {
1058 Path2D p2d = (Path2D) s;
1059 setWindingRule(p2d.windingRule);
1060 this.numTypes = p2d.numTypes;
1061 this.pointTypes = Arrays.copyOf(p2d.pointTypes,
1062 p2d.pointTypes.length);
1063 this.numCoords = p2d.numCoords;
1064 this.doubleCoords = p2d.cloneCoordsDouble(at);
1065 } else {
1066 PathIterator pi = s.getPathIterator(at);
1067 setWindingRule(pi.getWindingRule());
1068 this.pointTypes = new byte[INIT_SIZE];
1069 this.doubleCoords = new double[INIT_SIZE * 2];
1070 append(pi, false);
1071 }
1072 }
1073
1074 float[] cloneCoordsFloat(AffineTransform at) {
1075 float ret[] = new float[doubleCoords.length];
1076 if (at == null) {
1077 for (int i = 0; i < numCoords; i++) {
1078 ret[i] = (float) doubleCoords[i];
1079 }
1080 } else {
1081 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1082 }
1083 return ret;
1084 }
1085
1086 double[] cloneCoordsDouble(AffineTransform at) {
1087 double ret[];
1088 if (at == null) {
1089 ret = Arrays.copyOf(this.doubleCoords,
1090 this.doubleCoords.length);
1091 } else {
1092 ret = new double[doubleCoords.length];
1093 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1094 }
1095 return ret;
1096 }
1097
1098 void append(float x, float y) {
1099 doubleCoords[numCoords++] = x;
1100 doubleCoords[numCoords++] = y;
1101 }
1102
1103 void append(double x, double y) {
1104 doubleCoords[numCoords++] = x;
1105 doubleCoords[numCoords++] = y;
1106 }
1107
1108 Point2D getPoint(int coordindex) {
1109 return new Point2D.Double(doubleCoords[coordindex],
1110 doubleCoords[coordindex+1]);
1111 }
1112
1113 void needRoom(boolean needMove, int newCoords) {
1114 if (needMove && numTypes == 0) {
1115 throw new IllegalPathStateException("missing initial moveto "+
1116 "in path definition");
1117 }
1118 int size = pointTypes.length;
1119 if (numTypes >= size) {
1120 int grow = size;
1121 if (grow > EXPAND_MAX) {
1122 grow = EXPAND_MAX;
1123 }
1124 pointTypes = Arrays.copyOf(pointTypes, size+grow);
1125 }
1126 size = doubleCoords.length;
1127 if (numCoords + newCoords > size) {
1128 int grow = size;
1129 if (grow > EXPAND_MAX * 2) {
1130 grow = EXPAND_MAX * 2;
1131 }
1132 if (grow < newCoords) {
1133 grow = newCoords;
1134 }
1135 doubleCoords = Arrays.copyOf(doubleCoords, size+grow);
1136 }
1137 }
1138
1139 /**
1140 * {@inheritDoc}
1141 * @since 1.6
1142 */
1143 public final synchronized void moveTo(double x, double y) {
1144 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
1145 doubleCoords[numCoords-2] = x;
1146 doubleCoords[numCoords-1] = y;
1147 } else {
1148 needRoom(false, 2);
1149 pointTypes[numTypes++] = SEG_MOVETO;
1150 doubleCoords[numCoords++] = x;
1151 doubleCoords[numCoords++] = y;
1152 }
1153 }
1154
1155 /**
1156 * {@inheritDoc}
1157 * @since 1.6
1158 */
1159 public final synchronized void lineTo(double x, double y) {
1160 needRoom(true, 2);
1161 pointTypes[numTypes++] = SEG_LINETO;
1162 doubleCoords[numCoords++] = x;
1163 doubleCoords[numCoords++] = y;
1164 }
1165
1166 /**
1167 * {@inheritDoc}
1168 * @since 1.6
1169 */
1170 public final synchronized void quadTo(double x1, double y1,
1171 double x2, double y2)
1172 {
1173 needRoom(true, 4);
1174 pointTypes[numTypes++] = SEG_QUADTO;
1175 doubleCoords[numCoords++] = x1;
1176 doubleCoords[numCoords++] = y1;
1177 doubleCoords[numCoords++] = x2;
1178 doubleCoords[numCoords++] = y2;
1179 }
1180
1181 /**
1182 * {@inheritDoc}
1183 * @since 1.6
1184 */
1185 public final synchronized void curveTo(double x1, double y1,
1186 double x2, double y2,
1187 double x3, double y3)
1188 {
1189 needRoom(true, 6);
1190 pointTypes[numTypes++] = SEG_CUBICTO;
1191 doubleCoords[numCoords++] = x1;
1192 doubleCoords[numCoords++] = y1;
1193 doubleCoords[numCoords++] = x2;
1194 doubleCoords[numCoords++] = y2;
1195 doubleCoords[numCoords++] = x3;
1196 doubleCoords[numCoords++] = y3;
1197 }
1198
1199 int pointCrossings(double px, double py) {
1200 double movx, movy, curx, cury, endx, endy;
1201 double coords[] = doubleCoords;
1202 curx = movx = coords[0];
1203 cury = movy = coords[1];
1204 int crossings = 0;
1205 int ci = 2;
1206 for (int i = 1; i < numTypes; i++) {
1207 switch (pointTypes[i]) {
1208 case PathIterator.SEG_MOVETO:
1209 if (cury != movy) {
1210 crossings +=
1211 Curve.pointCrossingsForLine(px, py,
1212 curx, cury,
1213 movx, movy);
1214 }
1215 movx = curx = coords[ci++];
1216 movy = cury = coords[ci++];
1217 break;
1218 case PathIterator.SEG_LINETO:
1219 crossings +=
1220 Curve.pointCrossingsForLine(px, py,
1221 curx, cury,
1222 endx = coords[ci++],
1223 endy = coords[ci++]);
1224 curx = endx;
1225 cury = endy;
1226 break;
1227 case PathIterator.SEG_QUADTO:
1228 crossings +=
1229 Curve.pointCrossingsForQuad(px, py,
1230 curx, cury,
1231 coords[ci++],
1232 coords[ci++],
1233 endx = coords[ci++],
1234 endy = coords[ci++],
1235 0);
1236 curx = endx;
1237 cury = endy;
1238 break;
1239 case PathIterator.SEG_CUBICTO:
1240 crossings +=
1241 Curve.pointCrossingsForCubic(px, py,
1242 curx, cury,
1243 coords[ci++],
1244 coords[ci++],
1245 coords[ci++],
1246 coords[ci++],
1247 endx = coords[ci++],
1248 endy = coords[ci++],
1249 0);
1250 curx = endx;
1251 cury = endy;
1252 break;
1253 case PathIterator.SEG_CLOSE:
1254 if (cury != movy) {
1255 crossings +=
1256 Curve.pointCrossingsForLine(px, py,
1257 curx, cury,
1258 movx, movy);
1259 }
1260 curx = movx;
1261 cury = movy;
1262 break;
1263 }
1264 }
1265 if (cury != movy) {
1266 crossings +=
1267 Curve.pointCrossingsForLine(px, py,
1268 curx, cury,
1269 movx, movy);
1270 }
1271 return crossings;
1272 }
1273
1274 int rectCrossings(double rxmin, double rymin,
1275 double rxmax, double rymax)
1276 {
1277 double coords[] = doubleCoords;
1278 double curx, cury, movx, movy, endx, endy;
1279 curx = movx = coords[0];
1280 cury = movy = coords[1];
1281 int crossings = 0;
1282 int ci = 2;
1283 for (int i = 1;
1284 crossings != Curve.RECT_INTERSECTS && i < numTypes;
1285 i++)
1286 {
1287 switch (pointTypes[i]) {
1288 case PathIterator.SEG_MOVETO:
1289 if (curx != movx || cury != movy) {
1290 crossings =
1291 Curve.rectCrossingsForLine(crossings,
1292 rxmin, rymin,
1293 rxmax, rymax,
1294 curx, cury,
1295 movx, movy);
1296 }
1297 // Count should always be a multiple of 2 here.
1298 // assert((crossings & 1) != 0);
1299 movx = curx = coords[ci++];
1300 movy = cury = coords[ci++];
1301 break;
1302 case PathIterator.SEG_LINETO:
1303 endx = coords[ci++];
1304 endy = coords[ci++];
1305 crossings =
1306 Curve.rectCrossingsForLine(crossings,
1307 rxmin, rymin,
1308 rxmax, rymax,
1309 curx, cury,
1310 endx, endy);
1311 curx = endx;
1312 cury = endy;
1313 break;
1314 case PathIterator.SEG_QUADTO:
1315 crossings =
1316 Curve.rectCrossingsForQuad(crossings,
1317 rxmin, rymin,
1318 rxmax, rymax,
1319 curx, cury,
1320 coords[ci++],
1321 coords[ci++],
1322 endx = coords[ci++],
1323 endy = coords[ci++],
1324 0);
1325 curx = endx;
1326 cury = endy;
1327 break;
1328 case PathIterator.SEG_CUBICTO:
1329 crossings =
1330 Curve.rectCrossingsForCubic(crossings,
1331 rxmin, rymin,
1332 rxmax, rymax,
1333 curx, cury,
1334 coords[ci++],
1335 coords[ci++],
1336 coords[ci++],
1337 coords[ci++],
1338 endx = coords[ci++],
1339 endy = coords[ci++],
1340 0);
1341 curx = endx;
1342 cury = endy;
1343 break;
1344 case PathIterator.SEG_CLOSE:
1345 if (curx != movx || cury != movy) {
1346 crossings =
1347 Curve.rectCrossingsForLine(crossings,
1348 rxmin, rymin,
1349 rxmax, rymax,
1350 curx, cury,
1351 movx, movy);
1352 }
1353 curx = movx;
1354 cury = movy;
1355 // Count should always be a multiple of 2 here.
1356 // assert((crossings & 1) != 0);
1357 break;
1358 }
1359 }
1360 if (crossings != Curve.RECT_INTERSECTS &&
1361 (curx != movx || cury != movy))
1362 {
1363 crossings =
1364 Curve.rectCrossingsForLine(crossings,
1365 rxmin, rymin,
1366 rxmax, rymax,
1367 curx, cury,
1368 movx, movy);
1369 }
1370 // Count should always be a multiple of 2 here.
1371 // assert((crossings & 1) != 0);
1372 return crossings;
1373 }
1374
1375 /**
1376 * {@inheritDoc}
1377 * @since 1.6
1378 */
1379 public final void append(PathIterator pi, boolean connect) {
1380 double coords[] = new double[6];
1381 while (!pi.isDone()) {
1382 switch (pi.currentSegment(coords)) {
1383 case SEG_MOVETO:
1384 if (!connect || numTypes < 1 || numCoords < 1) {
1385 moveTo(coords[0], coords[1]);
1386 break;
1387 }
1388 if (pointTypes[numTypes - 1] != SEG_CLOSE &&
1389 doubleCoords[numCoords-2] == coords[0] &&
1390 doubleCoords[numCoords-1] == coords[1])
1391 {
1392 // Collapse out initial moveto/lineto
1393 break;
1394 }
1395 // NO BREAK;
1396 case SEG_LINETO:
1397 lineTo(coords[0], coords[1]);
1398 break;
1399 case SEG_QUADTO:
1400 quadTo(coords[0], coords[1],
1401 coords[2], coords[3]);
1402 break;
1403 case SEG_CUBICTO:
1404 curveTo(coords[0], coords[1],
1405 coords[2], coords[3],
1406 coords[4], coords[5]);
1407 break;
1408 case SEG_CLOSE:
1409 closePath();
1410 break;
1411 }
1412 pi.next();
1413 connect = false;
1414 }
1415 }
1416
1417 /**
1418 * {@inheritDoc}
1419 * @since 1.6
1420 */
1421 public final void transform(AffineTransform at) {
1422 at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2);
1423 }
1424
1425 /**
1426 * {@inheritDoc}
1427 * @since 1.6
1428 */
1429 public final synchronized Rectangle2D getBounds2D() {
1430 double x1, y1, x2, y2;
1431 int i = numCoords;
1432 if (i > 0) {
1433 y1 = y2 = doubleCoords[--i];
1434 x1 = x2 = doubleCoords[--i];
1435 while (i > 0) {
1436 double y = doubleCoords[--i];
1437 double x = doubleCoords[--i];
1438 if (x < x1) x1 = x;
1439 if (y < y1) y1 = y;
1440 if (x > x2) x2 = x;
1441 if (y > y2) y2 = y;
1442 }
1443 } else {
1444 x1 = y1 = x2 = y2 = 0.0;
1445 }
1446 return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
1447 }
1448
1449 /**
1450 * {@inheritDoc}
1451 * <p>
1452 * The iterator for this class is not multi-threaded safe,
1453 * which means that the {@code Path2D} class does not
1454 * guarantee that modifications to the geometry of this
1455 * {@code Path2D} object do not affect any iterations of
1456 * that geometry that are already in process.
1457 *
1458 * @param at an {@code AffineTransform}
1459 * @return a new {@code PathIterator} that iterates along the boundary
1460 * of this {@code Shape} and provides access to the geometry
1461 * of this {@code Shape}'s outline
1462 * @since 1.6
1463 */
1464 public PathIterator getPathIterator(AffineTransform at) {
1465 if (at == null) {
1466 return new CopyIterator(this);
1467 } else {
1468 return new TxIterator(this, at);
1469 }
1470 }
1471
1472 /**
1473 * Creates a new object of the same class as this object.
1474 *
1475 * @return a clone of this instance.
1476 * @exception OutOfMemoryError if there is not enough memory.
1477 * @see java.lang.Cloneable
1478 * @since 1.6
1479 */
1480 public final Object clone() {
1481 // Note: It would be nice to have this return Path2D
1482 // but one of our subclasses (GeneralPath) needs to
1483 // offer "public Object clone()" for backwards
1484 // compatibility so we cannot restrict it further.
1485 // REMIND: Can we do both somehow?
1486 return new Path2D.Double(this);
1487 }
1488
1489 /*
1490 * JDK 1.6 serialVersionUID
1491 */
1492 private static final long serialVersionUID = 1826762518450014216L;
1493
1494 /**
1495 * Writes the default serializable fields to the
1496 * {@code ObjectOutputStream} followed by an explicit
1497 * serialization of the path segments stored in this
1498 * path.
1499 *
1500 * @serialData
1501 * <a name="Path2DSerialData"><!-- --></a>
1502 * <ol>
1503 * <li>The default serializable fields.
1504 * There are no default serializable fields as of 1.6.
1505 * <li>followed by
1506 * a byte indicating the storage type of the original object
1507 * as a hint (SERIAL_STORAGE_DBL_ARRAY)
1508 * <li>followed by
1509 * an integer indicating the number of path segments to follow (NP)
1510 * or -1 to indicate an unknown number of path segments follows
1511 * <li>followed by
1512 * an integer indicating the total number of coordinates to follow (NC)
1513 * or -1 to indicate an unknown number of coordinates follows
1514 * (NC should always be even since coordinates always appear in pairs
1515 * representing an x,y pair)
1516 * <li>followed by
1517 * a byte indicating the winding rule
1518 * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
1519 * {@link #WIND_NON_ZERO WIND_NON_ZERO})
1520 * <li>followed by
1521 * NP (or unlimited if NP < 0) sets of values consisting of
1522 * a single byte indicating a path segment type
1523 * followed by one or more pairs of float or double
1524 * values representing the coordinates of the path segment
1525 * <li>followed by
1526 * a byte indicating the end of the path (SERIAL_PATH_END).
1527 * </ol>
1528 * <p>
1529 * The following byte value constants are used in the serialized form
1530 * of {@code Path2D} objects:
1531 * <table>
1532 * <tr>
1533 * <th>Constant Name</th>
1534 * <th>Byte Value</th>
1535 * <th>Followed by</th>
1536 * <th>Description</th>
1537 * </tr>
1538 * <tr>
1539 * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
1540 * <td>0x30</td>
1541 * <td></td>
1542 * <td>A hint that the original {@code Path2D} object stored
1543 * the coordinates in a Java array of floats.</td>
1544 * </tr>
1545 * <tr>
1546 * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
1547 * <td>0x31</td>
1548 * <td></td>
1549 * <td>A hint that the original {@code Path2D} object stored
1550 * the coordinates in a Java array of doubles.</td>
1551 * </tr>
1552 * <tr>
1553 * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
1554 * <td>0x40</td>
1555 * <td>2 floats</td>
1556 * <td>A {@link #moveTo moveTo} path segment follows.</td>
1557 * </tr>
1558 * <tr>
1559 * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
1560 * <td>0x41</td>
1561 * <td>2 floats</td>
1562 * <td>A {@link #lineTo lineTo} path segment follows.</td>
1563 * </tr>
1564 * <tr>
1565 * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
1566 * <td>0x42</td>
1567 * <td>4 floats</td>
1568 * <td>A {@link #quadTo quadTo} path segment follows.</td>
1569 * </tr>
1570 * <tr>
1571 * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
1572 * <td>0x43</td>
1573 * <td>6 floats</td>
1574 * <td>A {@link #curveTo curveTo} path segment follows.</td>
1575 * </tr>
1576 * <tr>
1577 * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
1578 * <td>0x50</td>
1579 * <td>2 doubles</td>
1580 * <td>A {@link #moveTo moveTo} path segment follows.</td>
1581 * </tr>
1582 * <tr>
1583 * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
1584 * <td>0x51</td>
1585 * <td>2 doubles</td>
1586 * <td>A {@link #lineTo lineTo} path segment follows.</td>
1587 * </tr>
1588 * <tr>
1589 * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
1590 * <td>0x52</td>
1591 * <td>4 doubles</td>
1592 * <td>A {@link #curveTo curveTo} path segment follows.</td>
1593 * </tr>
1594 * <tr>
1595 * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
1596 * <td>0x53</td>
1597 * <td>6 doubles</td>
1598 * <td>A {@link #curveTo curveTo} path segment follows.</td>
1599 * </tr>
1600 * <tr>
1601 * <td>{@code SERIAL_SEG_CLOSE}</td>
1602 * <td>0x60</td>
1603 * <td></td>
1604 * <td>A {@link #closePath closePath} path segment.</td>
1605 * </tr>
1606 * <tr>
1607 * <td>{@code SERIAL_PATH_END}</td>
1608 * <td>0x61</td>
1609 * <td></td>
1610 * <td>There are no more path segments following.</td>
1611 * </table>
1612 *
1613 * @since 1.6
1614 */
1615 private void writeObject(java.io.ObjectOutputStream s)
1616 throws java.io.IOException
1617 {
1618 super.writeObject(s, true);
1619 }
1620
1621 /**
1622 * Reads the default serializable fields from the
1623 * {@code ObjectInputStream} followed by an explicit
1624 * serialization of the path segments stored in this
1625 * path.
1626 * <p>
1627 * There are no default serializable fields as of 1.6.
1628 * <p>
1629 * The serial data for this object is described in the
1630 * writeObject method.
1631 *
1632 * @since 1.6
1633 */
1634 private void readObject(java.io.ObjectInputStream s)
1635 throws java.lang.ClassNotFoundException, java.io.IOException
1636 {
1637 super.readObject(s, true);
1638 }
1639
1640 static class CopyIterator extends Path2D.Iterator {
1641 double doubleCoords[];
1642
1643 CopyIterator(Path2D.Double p2dd) {
1644 super(p2dd);
1645 this.doubleCoords = p2dd.doubleCoords;
1646 }
1647
1648 public int currentSegment(float[] coords) {
1649 int type = path.pointTypes[typeIdx];
1650 int numCoords = curvecoords[type];
1651 if (numCoords > 0) {
1652 for (int i = 0; i < numCoords; i++) {
1653 coords[i] = (float) doubleCoords[pointIdx + i];
1654 }
1655 }
1656 return type;
1657 }
1658
1659 public int currentSegment(double[] coords) {
1660 int type = path.pointTypes[typeIdx];
1661 int numCoords = curvecoords[type];
1662 if (numCoords > 0) {
1663 System.arraycopy(doubleCoords, pointIdx,
1664 coords, 0, numCoords);
1665 }
1666 return type;
1667 }
1668 }
1669
1670 static class TxIterator extends Path2D.Iterator {
1671 double doubleCoords[];
1672 AffineTransform affine;
1673
1674 TxIterator(Path2D.Double p2dd, AffineTransform at) {
1675 super(p2dd);
1676 this.doubleCoords = p2dd.doubleCoords;
1677 this.affine = at;
1678 }
1679
1680 public int currentSegment(float[] coords) {
1681 int type = path.pointTypes[typeIdx];
1682 int numCoords = curvecoords[type];
1683 if (numCoords > 0) {
1684 affine.transform(doubleCoords, pointIdx,
1685 coords, 0, numCoords / 2);
1686 }
1687 return type;
1688 }
1689
1690 public int currentSegment(double[] coords) {
1691 int type = path.pointTypes[typeIdx];
1692 int numCoords = curvecoords[type];
1693 if (numCoords > 0) {
1694 affine.transform(doubleCoords, pointIdx,
1695 coords, 0, numCoords / 2);
1696 }
1697 return type;
1698 }
1699 }
1700 }
1701
1702 /**
1703 * Adds a point to the path by moving to the specified
1704 * coordinates specified in double precision.
1705 *
1706 * @param x the specified X coordinate
1707 * @param y the specified Y coordinate
1708 * @since 1.6
1709 */
1710 public abstract void moveTo(double x, double y);
1711
1712 /**
1713 * Adds a point to the path by drawing a straight line from the
1714 * current coordinates to the new specified coordinates
1715 * specified in double precision.
1716 *
1717 * @param x the specified X coordinate
1718 * @param y the specified Y coordinate
1719 * @since 1.6
1720 */
1721 public abstract void lineTo(double x, double y);
1722
1723 /**
1724 * Adds a curved segment, defined by two new points, to the path by
1725 * drawing a Quadratic curve that intersects both the current
1726 * coordinates and the specified coordinates {@code (x2,y2)},
1727 * using the specified point {@code (x1,y1)} as a quadratic
1728 * parametric control point.
1729 * All coordinates are specified in double precision.
1730 *
1731 * @param x1 the X coordinate of the quadratic control point
1732 * @param y1 the Y coordinate of the quadratic control point
1733 * @param x2 the X coordinate of the final end point
1734 * @param y2 the Y coordinate of the final end point
1735 * @since 1.6
1736 */
1737 public abstract void quadTo(double x1, double y1,
1738 double x2, double y2);
1739
1740 /**
1741 * Adds a curved segment, defined by three new points, to the path by
1742 * drawing a B&eacute;zier curve that intersects both the current
1743 * coordinates and the specified coordinates {@code (x3,y3)},
1744 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
1745 * B&eacute;zier control points.
1746 * All coordinates are specified in double precision.
1747 *
1748 * @param x1 the X coordinate of the first B&eacute;zier control point
1749 * @param y1 the Y coordinate of the first B&eacute;zier control point
1750 * @param x2 the X coordinate of the second B&eacute;zier control point
1751 * @param y2 the Y coordinate of the second B&eacute;zier control point
1752 * @param x3 the X coordinate of the final end point
1753 * @param y3 the Y coordinate of the final end point
1754 * @since 1.6
1755 */
1756 public abstract void curveTo(double x1, double y1,
1757 double x2, double y2,
1758 double x3, double y3);
1759
1760 /**
1761 * Closes the current subpath by drawing a straight line back to
1762 * the coordinates of the last {@code moveTo}. If the path is already
1763 * closed then this method has no effect.
1764 *
1765 * @since 1.6
1766 */
1767 public final synchronized void closePath() {
1768 if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
1769 needRoom(true, 0);
1770 pointTypes[numTypes++] = SEG_CLOSE;
1771 }
1772 }
1773
1774 /**
1775 * Appends the geometry of the specified {@code Shape} object to the
1776 * path, possibly connecting the new geometry to the existing path
1777 * segments with a line segment.
1778 * If the {@code connect} parameter is {@code true} and the
1779 * path is not empty then any initial {@code moveTo} in the
1780 * geometry of the appended {@code Shape}
1781 * is turned into a {@code lineTo} segment.
1782 * If the destination coordinates of such a connecting {@code lineTo}
1783 * segment match the ending coordinates of a currently open
1784 * subpath then the segment is omitted as superfluous.
1785 * The winding rule of the specified {@code Shape} is ignored
1786 * and the appended geometry is governed by the winding
1787 * rule specified for this path.
1788 *
1789 * @param s the {@code Shape} whose geometry is appended
1790 * to this path
1791 * @param connect a boolean to control whether or not to turn an initial
1792 * {@code moveTo} segment into a {@code lineTo} segment
1793 * to connect the new geometry to the existing path
1794 * @since 1.6
1795 */
1796 public final void append(Shape s, boolean connect) {
1797 append(s.getPathIterator(null), connect);
1798 }
1799
1800 /**
1801 * Appends the geometry of the specified
1802 * {@link PathIterator} object
1803 * to the path, possibly connecting the new geometry to the existing
1804 * path segments with a line segment.
1805 * If the {@code connect} parameter is {@code true} and the
1806 * path is not empty then any initial {@code moveTo} in the
1807 * geometry of the appended {@code Shape} is turned into a
1808 * {@code lineTo} segment.
1809 * If the destination coordinates of such a connecting {@code lineTo}
1810 * segment match the ending coordinates of a currently open
1811 * subpath then the segment is omitted as superfluous.
1812 * The winding rule of the specified {@code Shape} is ignored
1813 * and the appended geometry is governed by the winding
1814 * rule specified for this path.
1815 *
1816 * @param pi the {@code PathIterator} whose geometry is appended to
1817 * this path
1818 * @param connect a boolean to control whether or not to turn an initial
1819 * {@code moveTo} segment into a {@code lineTo} segment
1820 * to connect the new geometry to the existing path
1821 * @since 1.6
1822 */
1823 public abstract void append(PathIterator pi, boolean connect);
1824
1825 /**
1826 * Returns the fill style winding rule.
1827 *
1828 * @return an integer representing the current winding rule.
1829 * @see #WIND_EVEN_ODD
1830 * @see #WIND_NON_ZERO
1831 * @see #setWindingRule
1832 * @since 1.6
1833 */
1834 public final synchronized int getWindingRule() {
1835 return windingRule;
1836 }
1837
1838 /**
1839 * Sets the winding rule for this path to the specified value.
1840 *
1841 * @param rule an integer representing the specified
1842 * winding rule
1843 * @exception IllegalArgumentException if
1844 * {@code rule} is not either
1845 * {@link #WIND_EVEN_ODD} or
1846 * {@link #WIND_NON_ZERO}
1847 * @see #getWindingRule
1848 * @since 1.6
1849 */
1850 public final void setWindingRule(int rule) {
1851 if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
1852 throw new IllegalArgumentException("winding rule must be "+
1853 "WIND_EVEN_ODD or "+
1854 "WIND_NON_ZERO");
1855 }
1856 windingRule = rule;
1857 }
1858
1859 /**
1860 * Returns the coordinates most recently added to the end of the path
1861 * as a {@link Point2D} object.
1862 *
1863 * @return a {@code Point2D} object containing the ending coordinates of
1864 * the path or {@code null} if there are no points in the path.
1865 * @since 1.6
1866 */
1867 public final synchronized Point2D getCurrentPoint() {
1868 int index = numCoords;
1869 if (numTypes < 1 || index < 1) {
1870 return null;
1871 }
1872 if (pointTypes[numTypes - 1] == SEG_CLOSE) {
1873 loop:
1874 for (int i = numTypes - 2; i > 0; i--) {
1875 switch (pointTypes[i]) {
1876 case SEG_MOVETO:
1877 break loop;
1878 case SEG_LINETO:
1879 index -= 2;
1880 break;
1881 case SEG_QUADTO:
1882 index -= 4;
1883 break;
1884 case SEG_CUBICTO:
1885 index -= 6;
1886 break;
1887 case SEG_CLOSE:
1888 break;
1889 }
1890 }
1891 }
1892 return getPoint(index - 2);
1893 }
1894
1895 /**
1896 * Resets the path to empty. The append position is set back to the
1897 * beginning of the path and all coordinates and point types are
1898 * forgotten.
1899 *
1900 * @since 1.6
1901 */
1902 public final synchronized void reset() {
1903 numTypes = numCoords = 0;
1904 }
1905
1906 /**
1907 * Transforms the geometry of this path using the specified
1908 * {@link AffineTransform}.
1909 * The geometry is transformed in place, which permanently changes the
1910 * boundary defined by this object.
1911 *
1912 * @param at the {@code AffineTransform} used to transform the area
1913 * @since 1.6
1914 */
1915 public abstract void transform(AffineTransform at);
1916
1917 /**
1918 * Returns a new {@code Shape} representing a transformed version
1919 * of this {@code Path2D}.
1920 * Note that the exact type and coordinate precision of the return
1921 * value is not specified for this method.
1922 * The method will return a Shape that contains no less precision
1923 * for the transformed geometry than this {@code Path2D} currently
1924 * maintains, but it may contain no more precision either.
1925 * If the tradeoff of precision vs. storage size in the result is
1926 * important then the convenience constructors in the
1927 * {@link Path2D.Float#Path2D.Float(Shape, AffineTransform) Path2D.Float}
1928 * and
1929 * {@link Path2D.Double#Path2D.Double(Shape, AffineTransform) Path2D.Double}
1930 * subclasses should be used to make the choice explicit.
1931 *
1932 * @param at the {@code AffineTransform} used to transform a
1933 * new {@code Shape}.
1934 * @return a new {@code Shape}, transformed with the specified
1935 * {@code AffineTransform}.
1936 * @since 1.6
1937 */
1938 public final synchronized Shape createTransformedShape(AffineTransform at) {
1939 Path2D p2d = (Path2D) clone();
1940 if (at != null) {
1941 p2d.transform(at);
1942 }
1943 return p2d;
1944 }
1945
1946 /**
1947 * {@inheritDoc}
1948 * @since 1.6
1949 */
1950 public final Rectangle getBounds() {
1951 return getBounds2D().getBounds();
1952 }
1953
1954 /**
1955 * Tests if the specified coordinates are inside the closed
1956 * boundary of the specified {@link PathIterator}.
1957 * <p>
1958 * This method provides a basic facility for implementors of
1959 * the {@link Shape} interface to implement support for the
1960 * {@link Shape#contains(double, double)} method.
1961 *
1962 * @param pi the specified {@code PathIterator}
1963 * @param x the specified X coordinate
1964 * @param y the specified Y coordinate
1965 * @return {@code true} if the specified coordinates are inside the
1966 * specified {@code PathIterator}; {@code false} otherwise
1967 * @since 1.6
1968 */
1969 public static boolean contains(PathIterator pi, double x, double y) {
1970 if (x * 0.0 + y * 0.0 == 0.0) {
1971 /* N * 0.0 is 0.0 only if N is finite.
1972 * Here we know that both x and y are finite.
1973 */
1974 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
1975 int cross = Curve.pointCrossingsForPath(pi, x, y);
1976 return ((cross & mask) != 0);
1977 } else {
1978 /* Either x or y was infinite or NaN.
1979 * A NaN always produces a negative response to any test
1980 * and Infinity values cannot be "inside" any path so
1981 * they should return false as well.
1982 */
1983 return false;
1984 }
1985 }
1986
1987 /**
1988 * Tests if the specified {@link Point2D} is inside the closed
1989 * boundary of the specified {@link PathIterator}.
1990 * <p>
1991 * This method provides a basic facility for implementors of
1992 * the {@link Shape} interface to implement support for the
1993 * {@link Shape#contains(Point2D)} method.
1994 *
1995 * @param pi the specified {@code PathIterator}
1996 * @param p the specified {@code Point2D}
1997 * @return {@code true} if the specified coordinates are inside the
1998 * specified {@code PathIterator}; {@code false} otherwise
1999 * @since 1.6
2000 */
2001 public static boolean contains(PathIterator pi, Point2D p) {
2002 return contains(pi, p.getX(), p.getY());
2003 }
2004
2005 /**
2006 * {@inheritDoc}
2007 * @since 1.6
2008 */
2009 public final boolean contains(double x, double y) {
2010 if (x * 0.0 + y * 0.0 == 0.0) {
2011 /* N * 0.0 is 0.0 only if N is finite.
2012 * Here we know that both x and y are finite.
2013 */
2014 if (numTypes < 2) {
2015 return false;
2016 }
2017 int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
2018 return ((pointCrossings(x, y) & mask) != 0);
2019 } else {
2020 /* Either x or y was infinite or NaN.
2021 * A NaN always produces a negative response to any test
2022 * and Infinity values cannot be "inside" any path so
2023 * they should return false as well.
2024 */
2025 return false;
2026 }
2027 }
2028
2029 /**
2030 * {@inheritDoc}
2031 * @since 1.6
2032 */
2033 public final boolean contains(Point2D p) {
2034 return contains(p.getX(), p.getY());
2035 }
2036
2037 /**
2038 * Tests if the specified rectangular area is entirely inside the
2039 * closed boundary of the specified {@link PathIterator}.
2040 * <p>
2041 * This method provides a basic facility for implementors of
2042 * the {@link Shape} interface to implement support for the
2043 * {@link Shape#contains(double, double, double, double)} method.
2044 * <p>
2045 * This method object may conservatively return false in
2046 * cases where the specified rectangular area intersects a
2047 * segment of the path, but that segment does not represent a
2048 * boundary between the interior and exterior of the path.
2049 * Such segments could lie entirely within the interior of the
2050 * path if they are part of a path with a {@link #WIND_NON_ZERO}
2051 * winding rule or if the segments are retraced in the reverse
2052 * direction such that the two sets of segments cancel each
2053 * other out without any exterior area falling between them.
2054 * To determine whether segments represent true boundaries of
2055 * the interior of the path would require extensive calculations
2056 * involving all of the segments of the path and the winding
2057 * rule and are thus beyond the scope of this implementation.
2058 *
2059 * @param pi the specified {@code PathIterator}
2060 * @param x the specified X coordinate
2061 * @param y the specified Y coordinate
2062 * @param w the width of the specified rectangular area
2063 * @param h the height of the specified rectangular area
2064 * @return {@code true} if the specified {@code PathIterator} contains
2065 * the specified rectangluar area; {@code false} otherwise.
2066 * @since 1.6
2067 */
2068 public static boolean contains(PathIterator pi,
2069 double x, double y, double w, double h)
2070 {
2071 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2072 /* [xy]+[wh] is NaN if any of those values are NaN,
2073 * or if adding the two together would produce NaN
2074 * by virtue of adding opposing Infinte values.
2075 * Since we need to add them below, their sum must
2076 * not be NaN.
2077 * We return false because NaN always produces a
2078 * negative response to tests
2079 */
2080 return false;
2081 }
2082 if (w <= 0 || h <= 0) {
2083 return false;
2084 }
2085 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2086 int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h);
2087 return (crossings != Curve.RECT_INTERSECTS &&
2088 (crossings & mask) != 0);
2089 }
2090
2091 /**
2092 * Tests if the specified {@link Rectangle2D} is entirely inside the
2093 * closed boundary of the specified {@link PathIterator}.
2094 * <p>
2095 * This method provides a basic facility for implementors of
2096 * the {@link Shape} interface to implement support for the
2097 * {@link Shape#contains(Rectangle2D)} method.
2098 * <p>
2099 * This method object may conservatively return false in
2100 * cases where the specified rectangular area intersects a
2101 * segment of the path, but that segment does not represent a
2102 * boundary between the interior and exterior of the path.
2103 * Such segments could lie entirely within the interior of the
2104 * path if they are part of a path with a {@link #WIND_NON_ZERO}
2105 * winding rule or if the segments are retraced in the reverse
2106 * direction such that the two sets of segments cancel each
2107 * other out without any exterior area falling between them.
2108 * To determine whether segments represent true boundaries of
2109 * the interior of the path would require extensive calculations
2110 * involving all of the segments of the path and the winding
2111 * rule and are thus beyond the scope of this implementation.
2112 *
2113 * @param pi the specified {@code PathIterator}
2114 * @param r a specified {@code Rectangle2D}
2115 * @return {@code true} if the specified {@code PathIterator} contains
2116 * the specified {@code Rectangle2D}; {@code false} otherwise.
2117 * @since 1.6
2118 */
2119 public static boolean contains(PathIterator pi, Rectangle2D r) {
2120 return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
2121 }
2122
2123 /**
2124 * {@inheritDoc}
2125 * <p>
2126 * This method object may conservatively return false in
2127 * cases where the specified rectangular area intersects a
2128 * segment of the path, but that segment does not represent a
2129 * boundary between the interior and exterior of the path.
2130 * Such segments could lie entirely within the interior of the
2131 * path if they are part of a path with a {@link #WIND_NON_ZERO}
2132 * winding rule or if the segments are retraced in the reverse
2133 * direction such that the two sets of segments cancel each
2134 * other out without any exterior area falling between them.
2135 * To determine whether segments represent true boundaries of
2136 * the interior of the path would require extensive calculations
2137 * involving all of the segments of the path and the winding
2138 * rule and are thus beyond the scope of this implementation.
2139 *
2140 * @since 1.6
2141 */
2142 public final boolean contains(double x, double y, double w, double h) {
2143 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2144 /* [xy]+[wh] is NaN if any of those values are NaN,
2145 * or if adding the two together would produce NaN
2146 * by virtue of adding opposing Infinte values.
2147 * Since we need to add them below, their sum must
2148 * not be NaN.
2149 * We return false because NaN always produces a
2150 * negative response to tests
2151 */
2152 return false;
2153 }
2154 if (w <= 0 || h <= 0) {
2155 return false;
2156 }
2157 int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2158 int crossings = rectCrossings(x, y, x+w, y+h);
2159 return (crossings != Curve.RECT_INTERSECTS &&
2160 (crossings & mask) != 0);
2161 }
2162
2163 /**
2164 * {@inheritDoc}
2165 * <p>
2166 * This method object may conservatively return false in
2167 * cases where the specified rectangular area intersects a
2168 * segment of the path, but that segment does not represent a
2169 * boundary between the interior and exterior of the path.
2170 * Such segments could lie entirely within the interior of the
2171 * path if they are part of a path with a {@link #WIND_NON_ZERO}
2172 * winding rule or if the segments are retraced in the reverse
2173 * direction such that the two sets of segments cancel each
2174 * other out without any exterior area falling between them.
2175 * To determine whether segments represent true boundaries of
2176 * the interior of the path would require extensive calculations
2177 * involving all of the segments of the path and the winding
2178 * rule and are thus beyond the scope of this implementation.
2179 *
2180 * @since 1.6
2181 */
2182 public final boolean contains(Rectangle2D r) {
2183 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
2184 }
2185
2186 /**
2187 * Tests if the interior of the specified {@link PathIterator}
2188 * intersects the interior of a specified set of rectangular
2189 * coordinates.
2190 * <p>
2191 * This method provides a basic facility for implementors of
2192 * the {@link Shape} interface to implement support for the
2193 * {@link Shape#intersects(double, double, double, double)} method.
2194 * <p>
2195 * This method object may conservatively return true in
2196 * cases where the specified rectangular area intersects a
2197 * segment of the path, but that segment does not represent a
2198 * boundary between the interior and exterior of the path.
2199 * Such a case may occur if some set of segments of the
2200 * path are retraced in the reverse direction such that the
2201 * two sets of segments cancel each other out without any
2202 * interior area between them.
2203 * To determine whether segments represent true boundaries of
2204 * the interior of the path would require extensive calculations
2205 * involving all of the segments of the path and the winding
2206 * rule and are thus beyond the scope of this implementation.
2207 *
2208 * @param pi the specified {@code PathIterator}
2209 * @param x the specified X coordinate
2210 * @param y the specified Y coordinate
2211 * @param w the width of the specified rectangular coordinates
2212 * @param h the height of the specified rectangular coordinates
2213 * @return {@code true} if the specified {@code PathIterator} and
2214 * the interior of the specified set of rectangular
2215 * coordinates intersect each other; {@code false} otherwise.
2216 * @since 1.6
2217 */
2218 public static boolean intersects(PathIterator pi,
2219 double x, double y, double w, double h)
2220 {
2221 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2222 /* [xy]+[wh] is NaN if any of those values are NaN,
2223 * or if adding the two together would produce NaN
2224 * by virtue of adding opposing Infinte values.
2225 * Since we need to add them below, their sum must
2226 * not be NaN.
2227 * We return false because NaN always produces a
2228 * negative response to tests
2229 */
2230 return false;
2231 }
2232 if (w <= 0 || h <= 0) {
2233 return false;
2234 }
2235 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2236 int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h);
2237 return (crossings == Curve.RECT_INTERSECTS ||
2238 (crossings & mask) != 0);
2239 }
2240
2241 /**
2242 * Tests if the interior of the specified {@link PathIterator}
2243 * intersects the interior of a specified {@link Rectangle2D}.
2244 * <p>
2245 * This method provides a basic facility for implementors of
2246 * the {@link Shape} interface to implement support for the
2247 * {@link Shape#intersects(Rectangle2D)} method.
2248 * <p>
2249 * This method object may conservatively return true in
2250 * cases where the specified rectangular area intersects a
2251 * segment of the path, but that segment does not represent a
2252 * boundary between the interior and exterior of the path.
2253 * Such a case may occur if some set of segments of the
2254 * path are retraced in the reverse direction such that the
2255 * two sets of segments cancel each other out without any
2256 * interior area between them.
2257 * To determine whether segments represent true boundaries of
2258 * the interior of the path would require extensive calculations
2259 * involving all of the segments of the path and the winding
2260 * rule and are thus beyond the scope of this implementation.
2261 *
2262 * @param pi the specified {@code PathIterator}
2263 * @param r the specified {@code Rectangle2D}
2264 * @return {@code true} if the specified {@code PathIterator} and
2265 * the interior of the specified {@code Rectangle2D}
2266 * intersect each other; {@code false} otherwise.
2267 * @since 1.6
2268 */
2269 public static boolean intersects(PathIterator pi, Rectangle2D r) {
2270 return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
2271 }
2272
2273 /**
2274 * {@inheritDoc}
2275 * <p>
2276 * This method object may conservatively return true in
2277 * cases where the specified rectangular area intersects a
2278 * segment of the path, but that segment does not represent a
2279 * boundary between the interior and exterior of the path.
2280 * Such a case may occur if some set of segments of the
2281 * path are retraced in the reverse direction such that the
2282 * two sets of segments cancel each other out without any
2283 * interior area between them.
2284 * To determine whether segments represent true boundaries of
2285 * the interior of the path would require extensive calculations
2286 * involving all of the segments of the path and the winding
2287 * rule and are thus beyond the scope of this implementation.
2288 *
2289 * @since 1.6
2290 */
2291 public final boolean intersects(double x, double y, double w, double h) {
2292 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) {
2293 /* [xy]+[wh] is NaN if any of those values are NaN,
2294 * or if adding the two together would produce NaN
2295 * by virtue of adding opposing Infinte values.
2296 * Since we need to add them below, their sum must
2297 * not be NaN.
2298 * We return false because NaN always produces a
2299 * negative response to tests
2300 */
2301 return false;
2302 }
2303 if (w <= 0 || h <= 0) {
2304 return false;
2305 }
2306 int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2307 int crossings = rectCrossings(x, y, x+w, y+h);
2308 return (crossings == Curve.RECT_INTERSECTS ||
2309 (crossings & mask) != 0);
2310 }
2311
2312 /**
2313 * {@inheritDoc}
2314 * <p>
2315 * This method object may conservatively return true in
2316 * cases where the specified rectangular area intersects a
2317 * segment of the path, but that segment does not represent a
2318 * boundary between the interior and exterior of the path.
2319 * Such a case may occur if some set of segments of the
2320 * path are retraced in the reverse direction such that the
2321 * two sets of segments cancel each other out without any
2322 * interior area between them.
2323 * To determine whether segments represent true boundaries of
2324 * the interior of the path would require extensive calculations
2325 * involving all of the segments of the path and the winding
2326 * rule and are thus beyond the scope of this implementation.
2327 *
2328 * @since 1.6
2329 */
2330 public final boolean intersects(Rectangle2D r) {
2331 return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
2332 }
2333
2334 /**
2335 * {@inheritDoc}
2336 * <p>
2337 * The iterator for this class is not multi-threaded safe,
2338 * which means that this {@code Path2D} class does not
2339 * guarantee that modifications to the geometry of this
2340 * {@code Path2D} object do not affect any iterations of
2341 * that geometry that are already in process.
2342 *
2343 * @since 1.6
2344 */
2345 public PathIterator getPathIterator(AffineTransform at,
2346 double flatness)
2347 {
2348 return new FlatteningPathIterator(getPathIterator(at), flatness);
2349 }
2350
2351 /**
2352 * Creates a new object of the same class as this object.
2353 *
2354 * @return a clone of this instance.
2355 * @exception OutOfMemoryError if there is not enough memory.
2356 * @see java.lang.Cloneable
2357 * @since 1.6
2358 */
2359 public abstract Object clone();
2360 // Note: It would be nice to have this return Path2D
2361 // but one of our subclasses (GeneralPath) needs to
2362 // offer "public Object clone()" for backwards
2363 // compatibility so we cannot restrict it further.
2364 // REMIND: Can we do both somehow?
2365
2366 /*
2367 * Support fields and methods for serializing the subclasses.
2368 */
2369 private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30;
2370 private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31;
2371
2372 private static final byte SERIAL_SEG_FLT_MOVETO = 0x40;
2373 private static final byte SERIAL_SEG_FLT_LINETO = 0x41;
2374 private static final byte SERIAL_SEG_FLT_QUADTO = 0x42;
2375 private static final byte SERIAL_SEG_FLT_CUBICTO = 0x43;
2376
2377 private static final byte SERIAL_SEG_DBL_MOVETO = 0x50;
2378 private static final byte SERIAL_SEG_DBL_LINETO = 0x51;
2379 private static final byte SERIAL_SEG_DBL_QUADTO = 0x52;
2380 private static final byte SERIAL_SEG_DBL_CUBICTO = 0x53;
2381
2382 private static final byte SERIAL_SEG_CLOSE = 0x60;
2383 private static final byte SERIAL_PATH_END = 0x61;
2384
2385 final void writeObject(java.io.ObjectOutputStream s, boolean isdbl)
2386 throws java.io.IOException
2387 {
2388 s.defaultWriteObject();
2389
2390 float fCoords[];
2391 double dCoords[];
2392
2393 if (isdbl) {
2394 dCoords = ((Path2D.Double) this).doubleCoords;
2395 fCoords = null;
2396 } else {
2397 fCoords = ((Path2D.Float) this).floatCoords;
2398 dCoords = null;
2399 }
2400
2401 int numTypes = this.numTypes;
2402
2403 s.writeByte(isdbl
2404 ? SERIAL_STORAGE_DBL_ARRAY
2405 : SERIAL_STORAGE_FLT_ARRAY);
2406 s.writeInt(numTypes);
2407 s.writeInt(numCoords);
2408 s.writeByte((byte) windingRule);
2409
2410 int cindex = 0;
2411 for (int i = 0; i < numTypes; i++) {
2412 int npoints;
2413 byte serialtype;
2414 switch (pointTypes[i]) {
2415 case SEG_MOVETO:
2416 npoints = 1;
2417 serialtype = (isdbl
2418 ? SERIAL_SEG_DBL_MOVETO
2419 : SERIAL_SEG_FLT_MOVETO);
2420 break;
2421 case SEG_LINETO:
2422 npoints = 1;
2423 serialtype = (isdbl
2424 ? SERIAL_SEG_DBL_LINETO
2425 : SERIAL_SEG_FLT_LINETO);
2426 break;
2427 case SEG_QUADTO:
2428 npoints = 2;
2429 serialtype = (isdbl
2430 ? SERIAL_SEG_DBL_QUADTO
2431 : SERIAL_SEG_FLT_QUADTO);
2432 break;
2433 case SEG_CUBICTO:
2434 npoints = 3;
2435 serialtype = (isdbl
2436 ? SERIAL_SEG_DBL_CUBICTO
2437 : SERIAL_SEG_FLT_CUBICTO);
2438 break;
2439 case SEG_CLOSE:
2440 npoints = 0;
2441 serialtype = SERIAL_SEG_CLOSE;
2442 break;
2443
2444 default:
2445 // Should never happen
2446 throw new InternalError("unrecognized path type");
2447 }
2448 s.writeByte(serialtype);
2449 while (--npoints >= 0) {
2450 if (isdbl) {
2451 s.writeDouble(dCoords[cindex++]);
2452 s.writeDouble(dCoords[cindex++]);
2453 } else {
2454 s.writeFloat(fCoords[cindex++]);
2455 s.writeFloat(fCoords[cindex++]);
2456 }
2457 }
2458 }
2459 s.writeByte((byte) SERIAL_PATH_END);
2460 }
2461
2462 final void readObject(java.io.ObjectInputStream s, boolean storedbl)
2463 throws java.lang.ClassNotFoundException, java.io.IOException
2464 {
2465 s.defaultReadObject();
2466
2467 // The subclass calls this method with the storage type that
2468 // they want us to use (storedbl) so we ignore the storage
2469 // method hint from the stream.
2470 s.readByte();
2471 int nT = s.readInt();
2472 int nC = s.readInt();
2473 try {
2474 setWindingRule(s.readByte());
2475 } catch (IllegalArgumentException iae) {
2476 throw new java.io.InvalidObjectException(iae.getMessage());
2477 }
2478
2479 pointTypes = new byte[(nT < 0) ? INIT_SIZE : nT];
2480 if (nC < 0) {
2481 nC = INIT_SIZE * 2;
2482 }
2483 if (storedbl) {
2484 ((Path2D.Double) this).doubleCoords = new double[nC];
2485 } else {
2486 ((Path2D.Float) this).floatCoords = new float[nC];
2487 }
2488
2489 PATHDONE:
2490 for (int i = 0; nT < 0 || i < nT; i++) {
2491 boolean isdbl;
2492 int npoints;
2493 byte segtype;
2494
2495 byte serialtype = s.readByte();
2496 switch (serialtype) {
2497 case SERIAL_SEG_FLT_MOVETO:
2498 isdbl = false;
2499 npoints = 1;
2500 segtype = SEG_MOVETO;
2501 break;
2502 case SERIAL_SEG_FLT_LINETO:
2503 isdbl = false;
2504 npoints = 1;
2505 segtype = SEG_LINETO;
2506 break;
2507 case SERIAL_SEG_FLT_QUADTO:
2508 isdbl = false;
2509 npoints = 2;
2510 segtype = SEG_QUADTO;
2511 break;
2512 case SERIAL_SEG_FLT_CUBICTO:
2513 isdbl = false;
2514 npoints = 3;
2515 segtype = SEG_CUBICTO;
2516 break;
2517
2518 case SERIAL_SEG_DBL_MOVETO:
2519 isdbl = true;
2520 npoints = 1;
2521 segtype = SEG_MOVETO;
2522 break;
2523 case SERIAL_SEG_DBL_LINETO:
2524 isdbl = true;
2525 npoints = 1;
2526 segtype = SEG_LINETO;
2527 break;
2528 case SERIAL_SEG_DBL_QUADTO:
2529 isdbl = true;
2530 npoints = 2;
2531 segtype = SEG_QUADTO;
2532 break;
2533 case SERIAL_SEG_DBL_CUBICTO:
2534 isdbl = true;
2535 npoints = 3;
2536 segtype = SEG_CUBICTO;
2537 break;
2538
2539 case SERIAL_SEG_CLOSE:
2540 isdbl = false;
2541 npoints = 0;
2542 segtype = SEG_CLOSE;
2543 break;
2544
2545 case SERIAL_PATH_END:
2546 if (nT < 0) {
2547 break PATHDONE;
2548 }
2549 throw new StreamCorruptedException("unexpected PATH_END");
2550
2551 default:
2552 throw new StreamCorruptedException("unrecognized path type");
2553 }
2554 needRoom(segtype != SEG_MOVETO, npoints * 2);
2555 if (isdbl) {
2556 while (--npoints >= 0) {
2557 append(s.readDouble(), s.readDouble());
2558 }
2559 } else {
2560 while (--npoints >= 0) {
2561 append(s.readFloat(), s.readFloat());
2562 }
2563 }
2564 pointTypes[numTypes++] = segtype;
2565 }
2566 if (nT >= 0 && s.readByte() != SERIAL_PATH_END) {
2567 throw new StreamCorruptedException("missing PATH_END");
2568 }
2569 }
2570
2571 static abstract class Iterator implements PathIterator {
2572 int typeIdx;
2573 int pointIdx;
2574 Path2D path;
2575
2576 static final int curvecoords[] = {2, 2, 4, 6, 0};
2577
2578 Iterator(Path2D path) {
2579 this.path = path;
2580 }
2581
2582 public int getWindingRule() {
2583 return path.getWindingRule();
2584 }
2585
2586 public boolean isDone() {
2587 return (typeIdx >= path.numTypes);
2588 }
2589
2590 public void next() {
2591 int type = path.pointTypes[typeIdx++];
2592 pointIdx += curvecoords[type];
2593 }
2594 }
2595}