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