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