001 /*
002 * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt.geom;
027
028 import java.awt.Shape;
029 import java.awt.Rectangle;
030 import java.util.Vector;
031 import java.util.Enumeration;
032 import java.util.NoSuchElementException;
033 import sun.awt.geom.Curve;
034 import sun.awt.geom.Crossings;
035 import sun.awt.geom.AreaOp;
036
037 /**
038 * An <code>Area</code> object stores and manipulates a
039 * resolution-independent description of an enclosed area of
040 * 2-dimensional space.
041 * <code>Area</code> objects can be transformed and can perform
042 * various Constructive Area Geometry (CAG) operations when combined
043 * with other <code>Area</code> objects.
044 * The CAG operations include area
045 * {@link #add addition}, {@link #subtract subtraction},
046 * {@link #intersect intersection}, and {@link #exclusiveOr exclusive or}.
047 * See the linked method documentation for examples of the various
048 * operations.
049 * <p>
050 * The <code>Area</code> class implements the <code>Shape</code>
051 * interface and provides full support for all of its hit-testing
052 * and path iteration facilities, but an <code>Area</code> is more
053 * specific than a generalized path in a number of ways:
054 * <ul>
055 * <li>Only closed paths and sub-paths are stored.
056 * <code>Area</code> objects constructed from unclosed paths
057 * are implicitly closed during construction as if those paths
058 * had been filled by the <code>Graphics2D.fill</code> method.
059 * <li>The interiors of the individual stored sub-paths are all
060 * non-empty and non-overlapping. Paths are decomposed during
061 * construction into separate component non-overlapping parts,
062 * empty pieces of the path are discarded, and then these
063 * non-empty and non-overlapping properties are maintained
064 * through all subsequent CAG operations. Outlines of different
065 * component sub-paths may touch each other, as long as they
066 * do not cross so that their enclosed areas overlap.
067 * <li>The geometry of the path describing the outline of the
068 * <code>Area</code> resembles the path from which it was
069 * constructed only in that it describes the same enclosed
070 * 2-dimensional area, but may use entirely different types
071 * and ordering of the path segments to do so.
072 * </ul>
073 * Interesting issues which are not always obvious when using
074 * the <code>Area</code> include:
075 * <ul>
076 * <li>Creating an <code>Area</code> from an unclosed (open)
077 * <code>Shape</code> results in a closed outline in the
078 * <code>Area</code> object.
079 * <li>Creating an <code>Area</code> from a <code>Shape</code>
080 * which encloses no area (even when "closed") produces an
081 * empty <code>Area</code>. A common example of this issue
082 * is that producing an <code>Area</code> from a line will
083 * be empty since the line encloses no area. An empty
084 * <code>Area</code> will iterate no geometry in its
085 * <code>PathIterator</code> objects.
086 * <li>A self-intersecting <code>Shape</code> may be split into
087 * two (or more) sub-paths each enclosing one of the
088 * non-intersecting portions of the original path.
089 * <li>An <code>Area</code> may take more path segments to
090 * describe the same geometry even when the original
091 * outline is simple and obvious. The analysis that the
092 * <code>Area</code> class must perform on the path may
093 * not reflect the same concepts of "simple and obvious"
094 * as a human being perceives.
095 * </ul>
096 *
097 * @since 1.2
098 */
099 public class Area implements Shape, Cloneable {
100 private static Vector EmptyCurves = new Vector();
101
102 private Vector curves;
103
104 /**
105 * Default constructor which creates an empty area.
106 * @since 1.2
107 */
108 public Area() {
109 curves = EmptyCurves;
110 }
111
112 /**
113 * The <code>Area</code> class creates an area geometry from the
114 * specified {@link Shape} object. The geometry is explicitly
115 * closed, if the <code>Shape</code> is not already closed. The
116 * fill rule (even-odd or winding) specified by the geometry of the
117 * <code>Shape</code> is used to determine the resulting enclosed area.
118 * @param s the <code>Shape</code> from which the area is constructed
119 * @throws NullPointerException if <code>s</code> is null
120 * @since 1.2
121 */
122 public Area(Shape s) {
123 if (s instanceof Area) {
124 curves = ((Area) s).curves;
125 } else {
126 curves = pathToCurves(s.getPathIterator(null));
127 }
128 }
129
130 private static Vector pathToCurves(PathIterator pi) {
131 Vector curves = new Vector();
132 int windingRule = pi.getWindingRule();
133 // coords array is big enough for holding:
134 // coordinates returned from currentSegment (6)
135 // OR
136 // two subdivided quadratic curves (2+4+4=10)
137 // AND
138 // 0-1 horizontal splitting parameters
139 // OR
140 // 2 parametric equation derivative coefficients
141 // OR
142 // three subdivided cubic curves (2+6+6+6=20)
143 // AND
144 // 0-2 horizontal splitting parameters
145 // OR
146 // 3 parametric equation derivative coefficients
147 double coords[] = new double[23];
148 double movx = 0, movy = 0;
149 double curx = 0, cury = 0;
150 double newx, newy;
151 while (!pi.isDone()) {
152 switch (pi.currentSegment(coords)) {
153 case PathIterator.SEG_MOVETO:
154 Curve.insertLine(curves, curx, cury, movx, movy);
155 curx = movx = coords[0];
156 cury = movy = coords[1];
157 Curve.insertMove(curves, movx, movy);
158 break;
159 case PathIterator.SEG_LINETO:
160 newx = coords[0];
161 newy = coords[1];
162 Curve.insertLine(curves, curx, cury, newx, newy);
163 curx = newx;
164 cury = newy;
165 break;
166 case PathIterator.SEG_QUADTO:
167 newx = coords[2];
168 newy = coords[3];
169 Curve.insertQuad(curves, curx, cury, coords);
170 curx = newx;
171 cury = newy;
172 break;
173 case PathIterator.SEG_CUBICTO:
174 newx = coords[4];
175 newy = coords[5];
176 Curve.insertCubic(curves, curx, cury, coords);
177 curx = newx;
178 cury = newy;
179 break;
180 case PathIterator.SEG_CLOSE:
181 Curve.insertLine(curves, curx, cury, movx, movy);
182 curx = movx;
183 cury = movy;
184 break;
185 }
186 pi.next();
187 }
188 Curve.insertLine(curves, curx, cury, movx, movy);
189 AreaOp operator;
190 if (windingRule == PathIterator.WIND_EVEN_ODD) {
191 operator = new AreaOp.EOWindOp();
192 } else {
193 operator = new AreaOp.NZWindOp();
194 }
195 return operator.calculate(curves, EmptyCurves);
196 }
197
198 /**
199 * Adds the shape of the specified <code>Area</code> to the
200 * shape of this <code>Area</code>.
201 * The resulting shape of this <code>Area</code> will include
202 * the union of both shapes, or all areas that were contained
203 * in either this or the specified <code>Area</code>.
204 * <pre>
205 * // Example:
206 * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]);
207 * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]);
208 * a1.add(a2);
209 *
210 * a1(before) + a2 = a1(after)
211 *
212 * ################ ################ ################
213 * ############## ############## ################
214 * ############ ############ ################
215 * ########## ########## ################
216 * ######## ######## ################
217 * ###### ###### ###### ######
218 * #### #### #### ####
219 * ## ## ## ##
220 * </pre>
221 * @param rhs the <code>Area</code> to be added to the
222 * current shape
223 * @throws NullPointerException if <code>rhs</code> is null
224 * @since 1.2
225 */
226 public void add(Area rhs) {
227 curves = new AreaOp.AddOp().calculate(this .curves, rhs.curves);
228 invalidateBounds();
229 }
230
231 /**
232 * Subtracts the shape of the specified <code>Area</code> from the
233 * shape of this <code>Area</code>.
234 * The resulting shape of this <code>Area</code> will include
235 * areas that were contained only in this <code>Area</code>
236 * and not in the specified <code>Area</code>.
237 * <pre>
238 * // Example:
239 * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]);
240 * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]);
241 * a1.subtract(a2);
242 *
243 * a1(before) - a2 = a1(after)
244 *
245 * ################ ################
246 * ############## ############## ##
247 * ############ ############ ####
248 * ########## ########## ######
249 * ######## ######## ########
250 * ###### ###### ######
251 * #### #### ####
252 * ## ## ##
253 * </pre>
254 * @param rhs the <code>Area</code> to be subtracted from the
255 * current shape
256 * @throws NullPointerException if <code>rhs</code> is null
257 * @since 1.2
258 */
259 public void subtract(Area rhs) {
260 curves = new AreaOp.SubOp().calculate(this .curves, rhs.curves);
261 invalidateBounds();
262 }
263
264 /**
265 * Sets the shape of this <code>Area</code> to the intersection of
266 * its current shape and the shape of the specified <code>Area</code>.
267 * The resulting shape of this <code>Area</code> will include
268 * only areas that were contained in both this <code>Area</code>
269 * and also in the specified <code>Area</code>.
270 * <pre>
271 * // Example:
272 * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]);
273 * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]);
274 * a1.intersect(a2);
275 *
276 * a1(before) intersect a2 = a1(after)
277 *
278 * ################ ################ ################
279 * ############## ############## ############
280 * ############ ############ ########
281 * ########## ########## ####
282 * ######## ########
283 * ###### ######
284 * #### ####
285 * ## ##
286 * </pre>
287 * @param rhs the <code>Area</code> to be intersected with this
288 * <code>Area</code>
289 * @throws NullPointerException if <code>rhs</code> is null
290 * @since 1.2
291 */
292 public void intersect(Area rhs) {
293 curves = new AreaOp.IntOp().calculate(this .curves, rhs.curves);
294 invalidateBounds();
295 }
296
297 /**
298 * Sets the shape of this <code>Area</code> to be the combined area
299 * of its current shape and the shape of the specified <code>Area</code>,
300 * minus their intersection.
301 * The resulting shape of this <code>Area</code> will include
302 * only areas that were contained in either this <code>Area</code>
303 * or in the specified <code>Area</code>, but not in both.
304 * <pre>
305 * // Example:
306 * Area a1 = new Area([triangle 0,0 => 8,0 => 0,8]);
307 * Area a2 = new Area([triangle 0,0 => 8,0 => 8,8]);
308 * a1.exclusiveOr(a2);
309 *
310 * a1(before) xor a2 = a1(after)
311 *
312 * ################ ################
313 * ############## ############## ## ##
314 * ############ ############ #### ####
315 * ########## ########## ###### ######
316 * ######## ######## ################
317 * ###### ###### ###### ######
318 * #### #### #### ####
319 * ## ## ## ##
320 * </pre>
321 * @param rhs the <code>Area</code> to be exclusive ORed with this
322 * <code>Area</code>.
323 * @throws NullPointerException if <code>rhs</code> is null
324 * @since 1.2
325 */
326 public void exclusiveOr(Area rhs) {
327 curves = new AreaOp.XorOp().calculate(this .curves, rhs.curves);
328 invalidateBounds();
329 }
330
331 /**
332 * Removes all of the geometry from this <code>Area</code> and
333 * restores it to an empty area.
334 * @since 1.2
335 */
336 public void reset() {
337 curves = new Vector();
338 invalidateBounds();
339 }
340
341 /**
342 * Tests whether this <code>Area</code> object encloses any area.
343 * @return <code>true</code> if this <code>Area</code> object
344 * represents an empty area; <code>false</code> otherwise.
345 * @since 1.2
346 */
347 public boolean isEmpty() {
348 return (curves.size() == 0);
349 }
350
351 /**
352 * Tests whether this <code>Area</code> consists entirely of
353 * straight edged polygonal geometry.
354 * @return <code>true</code> if the geometry of this
355 * <code>Area</code> consists entirely of line segments;
356 * <code>false</code> otherwise.
357 * @since 1.2
358 */
359 public boolean isPolygonal() {
360 Enumeration enum_ = curves.elements();
361 while (enum_.hasMoreElements()) {
362 if (((Curve) enum_.nextElement()).getOrder() > 1) {
363 return false;
364 }
365 }
366 return true;
367 }
368
369 /**
370 * Tests whether this <code>Area</code> is rectangular in shape.
371 * @return <code>true</code> if the geometry of this
372 * <code>Area</code> is rectangular in shape; <code>false</code>
373 * otherwise.
374 * @since 1.2
375 */
376 public boolean isRectangular() {
377 int size = curves.size();
378 if (size == 0) {
379 return true;
380 }
381 if (size > 3) {
382 return false;
383 }
384 Curve c1 = (Curve) curves.get(1);
385 Curve c2 = (Curve) curves.get(2);
386 if (c1.getOrder() != 1 || c2.getOrder() != 1) {
387 return false;
388 }
389 if (c1.getXTop() != c1.getXBot()
390 || c2.getXTop() != c2.getXBot()) {
391 return false;
392 }
393 if (c1.getYTop() != c2.getYTop()
394 || c1.getYBot() != c2.getYBot()) {
395 // One might be able to prove that this is impossible...
396 return false;
397 }
398 return true;
399 }
400
401 /**
402 * Tests whether this <code>Area</code> is comprised of a single
403 * closed subpath. This method returns <code>true</code> if the
404 * path contains 0 or 1 subpaths, or <code>false</code> if the path
405 * contains more than 1 subpath. The subpaths are counted by the
406 * number of {@link PathIterator#SEG_MOVETO SEG_MOVETO} segments
407 * that appear in the path.
408 * @return <code>true</code> if the <code>Area</code> is comprised
409 * of a single basic geometry; <code>false</code> otherwise.
410 * @since 1.2
411 */
412 public boolean isSingular() {
413 if (curves.size() < 3) {
414 return true;
415 }
416 Enumeration enum_ = curves.elements();
417 enum_.nextElement(); // First Order0 "moveto"
418 while (enum_.hasMoreElements()) {
419 if (((Curve) enum_.nextElement()).getOrder() == 0) {
420 return false;
421 }
422 }
423 return true;
424 }
425
426 private Rectangle2D cachedBounds;
427
428 private void invalidateBounds() {
429 cachedBounds = null;
430 }
431
432 private Rectangle2D getCachedBounds() {
433 if (cachedBounds != null) {
434 return cachedBounds;
435 }
436 Rectangle2D r = new Rectangle2D.Double();
437 if (curves.size() > 0) {
438 Curve c = (Curve) curves.get(0);
439 // First point is always an order 0 curve (moveto)
440 r.setRect(c.getX0(), c.getY0(), 0, 0);
441 for (int i = 1; i < curves.size(); i++) {
442 ((Curve) curves.get(i)).enlarge(r);
443 }
444 }
445 return (cachedBounds = r);
446 }
447
448 /**
449 * Returns a high precision bounding {@link Rectangle2D} that
450 * completely encloses this <code>Area</code>.
451 * <p>
452 * The Area class will attempt to return the tightest bounding
453 * box possible for the Shape. The bounding box will not be
454 * padded to include the control points of curves in the outline
455 * of the Shape, but should tightly fit the actual geometry of
456 * the outline itself.
457 * @return the bounding <code>Rectangle2D</code> for the
458 * <code>Area</code>.
459 * @since 1.2
460 */
461 public Rectangle2D getBounds2D() {
462 return getCachedBounds().getBounds2D();
463 }
464
465 /**
466 * Returns a bounding {@link Rectangle} that completely encloses
467 * this <code>Area</code>.
468 * <p>
469 * The Area class will attempt to return the tightest bounding
470 * box possible for the Shape. The bounding box will not be
471 * padded to include the control points of curves in the outline
472 * of the Shape, but should tightly fit the actual geometry of
473 * the outline itself. Since the returned object represents
474 * the bounding box with integers, the bounding box can only be
475 * as tight as the nearest integer coordinates that encompass
476 * the geometry of the Shape.
477 * @return the bounding <code>Rectangle</code> for the
478 * <code>Area</code>.
479 * @since 1.2
480 */
481 public Rectangle getBounds() {
482 return getCachedBounds().getBounds();
483 }
484
485 /**
486 * Returns an exact copy of this <code>Area</code> object.
487 * @return Created clone object
488 * @since 1.2
489 */
490 public Object clone() {
491 return new Area(this );
492 }
493
494 /**
495 * Tests whether the geometries of the two <code>Area</code> objects
496 * are equal.
497 * This method will return false if the argument is null.
498 * @param other the <code>Area</code> to be compared to this
499 * <code>Area</code>
500 * @return <code>true</code> if the two geometries are equal;
501 * <code>false</code> otherwise.
502 * @since 1.2
503 */
504 public boolean equals(Area other) {
505 // REMIND: A *much* simpler operation should be possible...
506 // Should be able to do a curve-wise comparison since all Areas
507 // should evaluate their curves in the same top-down order.
508 if (other == this ) {
509 return true;
510 }
511 if (other == null) {
512 return false;
513 }
514 Vector c = new AreaOp.XorOp().calculate(this .curves,
515 other.curves);
516 return c.isEmpty();
517 }
518
519 /**
520 * Transforms the geometry of this <code>Area</code> using the specified
521 * {@link AffineTransform}. The geometry is transformed in place, which
522 * permanently changes the enclosed area defined by this object.
523 * @param t the transformation used to transform the area
524 * @throws NullPointerException if <code>t</code> is null
525 * @since 1.2
526 */
527 public void transform(AffineTransform t) {
528 if (t == null) {
529 throw new NullPointerException("transform must not be null");
530 }
531 // REMIND: A simpler operation can be performed for some types
532 // of transform.
533 curves = pathToCurves(getPathIterator(t));
534 invalidateBounds();
535 }
536
537 /**
538 * Creates a new <code>Area</code> object that contains the same
539 * geometry as this <code>Area</code> transformed by the specified
540 * <code>AffineTransform</code>. This <code>Area</code> object
541 * is unchanged.
542 * @param t the specified <code>AffineTransform</code> used to transform
543 * the new <code>Area</code>
544 * @throws NullPointerException if <code>t</code> is null
545 * @return a new <code>Area</code> object representing the transformed
546 * geometry.
547 * @since 1.2
548 */
549 public Area createTransformedArea(AffineTransform t) {
550 Area a = new Area(this );
551 a.transform(t);
552 return a;
553 }
554
555 /**
556 * {@inheritDoc}
557 * @since 1.2
558 */
559 public boolean contains(double x, double y) {
560 if (!getCachedBounds().contains(x, y)) {
561 return false;
562 }
563 Enumeration enum_ = curves.elements();
564 int crossings = 0;
565 while (enum_.hasMoreElements()) {
566 Curve c = (Curve) enum_.nextElement();
567 crossings += c.crossingsFor(x, y);
568 }
569 return ((crossings & 1) == 1);
570 }
571
572 /**
573 * {@inheritDoc}
574 * @since 1.2
575 */
576 public boolean contains(Point2D p) {
577 return contains(p.getX(), p.getY());
578 }
579
580 /**
581 * {@inheritDoc}
582 * @since 1.2
583 */
584 public boolean contains(double x, double y, double w, double h) {
585 if (w < 0 || h < 0) {
586 return false;
587 }
588 if (!getCachedBounds().contains(x, y, w, h)) {
589 return false;
590 }
591 Crossings c = Crossings.findCrossings(curves, x, y, x + w, y
592 + h);
593 return (c != null && c.covers(y, y + h));
594 }
595
596 /**
597 * {@inheritDoc}
598 * @since 1.2
599 */
600 public boolean contains(Rectangle2D r) {
601 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
602 }
603
604 /**
605 * {@inheritDoc}
606 * @since 1.2
607 */
608 public boolean intersects(double x, double y, double w, double h) {
609 if (w < 0 || h < 0) {
610 return false;
611 }
612 if (!getCachedBounds().intersects(x, y, w, h)) {
613 return false;
614 }
615 Crossings c = Crossings.findCrossings(curves, x, y, x + w, y
616 + h);
617 return (c == null || !c.isEmpty());
618 }
619
620 /**
621 * {@inheritDoc}
622 * @since 1.2
623 */
624 public boolean intersects(Rectangle2D r) {
625 return intersects(r.getX(), r.getY(), r.getWidth(), r
626 .getHeight());
627 }
628
629 /**
630 * Creates a {@link PathIterator} for the outline of this
631 * <code>Area</code> object. This <code>Area</code> object is unchanged.
632 * @param at an optional <code>AffineTransform</code> to be applied to
633 * the coordinates as they are returned in the iteration, or
634 * <code>null</code> if untransformed coordinates are desired
635 * @return the <code>PathIterator</code> object that returns the
636 * geometry of the outline of this <code>Area</code>, one
637 * segment at a time.
638 * @since 1.2
639 */
640 public PathIterator getPathIterator(AffineTransform at) {
641 return new AreaIterator(curves, at);
642 }
643
644 /**
645 * Creates a <code>PathIterator</code> for the flattened outline of
646 * this <code>Area</code> object. Only uncurved path segments
647 * represented by the SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point
648 * types are returned by the iterator. This <code>Area</code>
649 * object is unchanged.
650 * @param at an optional <code>AffineTransform</code> to be
651 * applied to the coordinates as they are returned in the
652 * iteration, or <code>null</code> if untransformed coordinates
653 * are desired
654 * @param flatness the maximum amount that the control points
655 * for a given curve can vary from colinear before a subdivided
656 * curve is replaced by a straight line connecting the end points
657 * @return the <code>PathIterator</code> object that returns the
658 * geometry of the outline of this <code>Area</code>, one segment
659 * at a time.
660 * @since 1.2
661 */
662 public PathIterator getPathIterator(AffineTransform at,
663 double flatness) {
664 return new FlatteningPathIterator(getPathIterator(at), flatness);
665 }
666 }
667
668 class AreaIterator implements PathIterator {
669 private AffineTransform transform;
670 private Vector curves;
671 private int index;
672 private Curve prevcurve;
673 private Curve this curve;
674
675 public AreaIterator(Vector curves, AffineTransform at) {
676 this .curves = curves;
677 this .transform = at;
678 if (curves.size() >= 1) {
679 this curve = (Curve) curves.get(0);
680 }
681 }
682
683 public int getWindingRule() {
684 // REMIND: Which is better, EVEN_ODD or NON_ZERO?
685 // The paths calculated could be classified either way.
686 //return WIND_EVEN_ODD;
687 return WIND_NON_ZERO;
688 }
689
690 public boolean isDone() {
691 return (prevcurve == null && this curve == null);
692 }
693
694 public void next() {
695 if (prevcurve != null) {
696 prevcurve = null;
697 } else {
698 prevcurve = this curve;
699 index++;
700 if (index < curves.size()) {
701 this curve = (Curve) curves.get(index);
702 if (this curve.getOrder() != 0
703 && prevcurve.getX1() == this curve.getX0()
704 && prevcurve.getY1() == this curve.getY0()) {
705 prevcurve = null;
706 }
707 } else {
708 this curve = null;
709 }
710 }
711 }
712
713 public int currentSegment(float coords[]) {
714 double dcoords[] = new double[6];
715 int segtype = currentSegment(dcoords);
716 int numpoints = (segtype == SEG_CLOSE ? 0
717 : (segtype == SEG_QUADTO ? 2
718 : (segtype == SEG_CUBICTO ? 3 : 1)));
719 for (int i = 0; i < numpoints * 2; i++) {
720 coords[i] = (float) dcoords[i];
721 }
722 return segtype;
723 }
724
725 public int currentSegment(double coords[]) {
726 int segtype;
727 int numpoints;
728 if (prevcurve != null) {
729 // Need to finish off junction between curves
730 if (this curve == null || this curve.getOrder() == 0) {
731 return SEG_CLOSE;
732 }
733 coords[0] = this curve.getX0();
734 coords[1] = this curve.getY0();
735 segtype = SEG_LINETO;
736 numpoints = 1;
737 } else if (this curve == null) {
738 throw new NoSuchElementException(
739 "area iterator out of bounds");
740 } else {
741 segtype = this curve.getSegment(coords);
742 numpoints = this curve.getOrder();
743 if (numpoints == 0) {
744 numpoints = 1;
745 }
746 }
747 if (transform != null) {
748 transform.transform(coords, 0, coords, 0, numpoints);
749 }
750 return segtype;
751 }
752 }
|