src/share/classes/java/awt/geom/Area.java

Print this page


   1 /*
   2  * Copyright (c) 1998, 2006, 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


  80  *     which encloses no area (even when "closed") produces an
  81  *     empty <code>Area</code>.  A common example of this issue
  82  *     is that producing an <code>Area</code> from a line will
  83  *     be empty since the line encloses no area.  An empty
  84  *     <code>Area</code> will iterate no geometry in its
  85  *     <code>PathIterator</code> objects.
  86  * <li>A self-intersecting <code>Shape</code> may be split into
  87  *     two (or more) sub-paths each enclosing one of the
  88  *     non-intersecting portions of the original path.
  89  * <li>An <code>Area</code> may take more path segments to
  90  *     describe the same geometry even when the original
  91  *     outline is simple and obvious.  The analysis that the
  92  *     <code>Area</code> class must perform on the path may
  93  *     not reflect the same concepts of "simple and obvious"
  94  *     as a human being perceives.
  95  * </ul>
  96  *
  97  * @since 1.2
  98  */
  99 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()) {


 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() || c2.getXTop() != c2.getXBot()) {
 390             return false;
 391         }
 392         if (c1.getYTop() != c2.getYTop() || c1.getYBot() != c2.getYBot()) {
 393             // One might be able to prove that this is impossible...
 394             return false;
 395         }
 396         return true;
 397     }
 398 
 399     /**
 400      * Tests whether this <code>Area</code> is comprised of a single
 401      * closed subpath.  This method returns <code>true</code> if the
 402      * path contains 0 or 1 subpaths, or <code>false</code> if the path
 403      * contains more than 1 subpath.  The subpaths are counted by the
 404      * number of {@link PathIterator#SEG_MOVETO SEG_MOVETO}  segments
 405      * that appear in the path.
 406      * @return    <code>true</code> if the <code>Area</code> is comprised
 407      * of a single basic geometry; <code>false</code> otherwise.
 408      * @since 1.2
 409      */
 410     public boolean isSingular() {
 411         if (curves.size() < 3) {
 412             return true;
 413         }
 414         Enumeration enum_ = curves.elements();
 415         enum_.nextElement(); // First Order0 "moveto"
 416         while (enum_.hasMoreElements()) {
 417             if (((Curve) enum_.nextElement()).getOrder() == 0) {
 418                 return false;
 419             }
 420         }
 421         return true;
 422     }
 423 
 424     private Rectangle2D cachedBounds;
 425     private void invalidateBounds() {
 426         cachedBounds = null;
 427     }
 428     private Rectangle2D getCachedBounds() {
 429         if (cachedBounds != null) {
 430             return cachedBounds;
 431         }
 432         Rectangle2D r = new Rectangle2D.Double();
 433         if (curves.size() > 0) {
 434             Curve c = (Curve) curves.get(0);
 435             // First point is always an order 0 curve (moveto)
 436             r.setRect(c.getX0(), c.getY0(), 0, 0);
 437             for (int i = 1; i < curves.size(); i++) {
 438                 ((Curve) curves.get(i)).enlarge(r);
 439             }
 440         }
 441         return (cachedBounds = r);
 442     }
 443 
 444     /**
 445      * Returns a high precision bounding {@link Rectangle2D} that
 446      * completely encloses this <code>Area</code>.
 447      * <p>
 448      * The Area class will attempt to return the tightest bounding
 449      * box possible for the Shape.  The bounding box will not be
 450      * padded to include the control points of curves in the outline
 451      * of the Shape, but should tightly fit the actual geometry of
 452      * the outline itself.
 453      * @return    the bounding <code>Rectangle2D</code> for the
 454      * <code>Area</code>.
 455      * @since 1.2
 456      */
 457     public Rectangle2D getBounds2D() {
 458         return getCachedBounds().getBounds2D();


 490     /**
 491      * Tests whether the geometries of the two <code>Area</code> objects
 492      * are equal.
 493      * This method will return false if the argument is null.
 494      * @param   other  the <code>Area</code> to be compared to this
 495      *          <code>Area</code>
 496      * @return  <code>true</code> if the two geometries are equal;
 497      *          <code>false</code> otherwise.
 498      * @since 1.2
 499      */
 500     public boolean equals(Area other) {
 501         // REMIND: A *much* simpler operation should be possible...
 502         // Should be able to do a curve-wise comparison since all Areas
 503         // should evaluate their curves in the same top-down order.
 504         if (other == this) {
 505             return true;
 506         }
 507         if (other == null) {
 508             return false;
 509         }
 510         Vector c = new AreaOp.XorOp().calculate(this.curves, other.curves);
 511         return c.isEmpty();
 512     }
 513 
 514     /**
 515      * Transforms the geometry of this <code>Area</code> using the specified
 516      * {@link AffineTransform}.  The geometry is transformed in place, which
 517      * permanently changes the enclosed area defined by this object.
 518      * @param t  the transformation used to transform the area
 519      * @throws NullPointerException if <code>t</code> is null
 520      * @since 1.2
 521      */
 522     public void transform(AffineTransform t) {
 523         if (t == null) {
 524             throw new NullPointerException("transform must not be null");
 525         }
 526         // REMIND: A simpler operation can be performed for some types
 527         // of transform.
 528         curves = pathToCurves(getPathIterator(t));
 529         invalidateBounds();
 530     }


 538      *           the new <code>Area</code>
 539      * @throws NullPointerException if <code>t</code> is null
 540      * @return   a new <code>Area</code> object representing the transformed
 541      *           geometry.
 542      * @since 1.2
 543      */
 544     public Area createTransformedArea(AffineTransform t) {
 545         Area a = new Area(this);
 546         a.transform(t);
 547         return a;
 548     }
 549 
 550     /**
 551      * {@inheritDoc}
 552      * @since 1.2
 553      */
 554     public boolean contains(double x, double y) {
 555         if (!getCachedBounds().contains(x, y)) {
 556             return false;
 557         }
 558         Enumeration enum_ = curves.elements();
 559         int crossings = 0;
 560         while (enum_.hasMoreElements()) {
 561             Curve c = (Curve) enum_.nextElement();
 562             crossings += c.crossingsFor(x, y);
 563         }
 564         return ((crossings & 1) == 1);
 565     }
 566 
 567     /**
 568      * {@inheritDoc}
 569      * @since 1.2
 570      */
 571     public boolean contains(Point2D p) {
 572         return contains(p.getX(), p.getY());
 573     }
 574 
 575     /**
 576      * {@inheritDoc}
 577      * @since 1.2
 578      */
 579     public boolean contains(double x, double y, double w, double h) {
 580         if (w < 0 || h < 0) {
 581             return false;


 641      * object is unchanged.
 642      * @param at an optional <code>AffineTransform</code> to be
 643      * applied to the coordinates as they are returned in the
 644      * iteration, or <code>null</code> if untransformed coordinates
 645      * are desired
 646      * @param flatness the maximum amount that the control points
 647      * for a given curve can vary from colinear before a subdivided
 648      * curve is replaced by a straight line connecting the end points
 649      * @return    the <code>PathIterator</code> object that returns the
 650      * geometry of the outline of this <code>Area</code>, one segment
 651      * at a time.
 652      * @since 1.2
 653      */
 654     public PathIterator getPathIterator(AffineTransform at, double flatness) {
 655         return new FlatteningPathIterator(getPathIterator(at), flatness);
 656     }
 657 }
 658 
 659 class AreaIterator implements PathIterator {
 660     private AffineTransform transform;
 661     private Vector curves;
 662     private int index;
 663     private Curve prevcurve;
 664     private Curve thiscurve;
 665 
 666     public AreaIterator(Vector curves, AffineTransform at) {
 667         this.curves = curves;
 668         this.transform = at;
 669         if (curves.size() >= 1) {
 670             thiscurve = (Curve) curves.get(0);
 671         }
 672     }
 673 
 674     public int getWindingRule() {
 675         // REMIND: Which is better, EVEN_ODD or NON_ZERO?
 676         //         The paths calculated could be classified either way.
 677         //return WIND_EVEN_ODD;
 678         return WIND_NON_ZERO;
 679     }
 680 
 681     public boolean isDone() {
 682         return (prevcurve == null && thiscurve == null);
 683     }
 684 
 685     public void next() {
 686         if (prevcurve != null) {
 687             prevcurve = null;
 688         } else {
 689             prevcurve = thiscurve;
 690             index++;
 691             if (index < curves.size()) {
 692                 thiscurve = (Curve) curves.get(index);
 693                 if (thiscurve.getOrder() != 0 &&
 694                     prevcurve.getX1() == thiscurve.getX0() &&
 695                     prevcurve.getY1() == thiscurve.getY0())
 696                 {
 697                     prevcurve = null;
 698                 }
 699             } else {
 700                 thiscurve = null;
 701             }
 702         }
 703     }
 704 
 705     public int currentSegment(float coords[]) {
 706         double dcoords[] = new double[6];
 707         int segtype = currentSegment(dcoords);
 708         int numpoints = (segtype == SEG_CLOSE ? 0
 709                          : (segtype == SEG_QUADTO ? 2
 710                             : (segtype == SEG_CUBICTO ? 3
 711                                : 1)));
 712         for (int i = 0; i < numpoints * 2; i++) {


   1 /*
   2  * Copyright (c) 1998, 2014, 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


  80  *     which encloses no area (even when "closed") produces an
  81  *     empty <code>Area</code>.  A common example of this issue
  82  *     is that producing an <code>Area</code> from a line will
  83  *     be empty since the line encloses no area.  An empty
  84  *     <code>Area</code> will iterate no geometry in its
  85  *     <code>PathIterator</code> objects.
  86  * <li>A self-intersecting <code>Shape</code> may be split into
  87  *     two (or more) sub-paths each enclosing one of the
  88  *     non-intersecting portions of the original path.
  89  * <li>An <code>Area</code> may take more path segments to
  90  *     describe the same geometry even when the original
  91  *     outline is simple and obvious.  The analysis that the
  92  *     <code>Area</code> class must perform on the path may
  93  *     not reflect the same concepts of "simple and obvious"
  94  *     as a human being perceives.
  95  * </ul>
  96  *
  97  * @since 1.2
  98  */
  99 public class Area implements Shape, Cloneable {
 100     private static Vector<Curve> EmptyCurves = new Vector<>();
 101 
 102     private Vector<Curve> 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<Curve> pathToCurves(PathIterator pi) {
 131         Vector<Curve> 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()) {


 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<Curve> enum_ = curves.elements();
 361         while (enum_.hasMoreElements()) {
 362             if (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 = curves.get(1);
 385         Curve c2 = curves.get(2);
 386         if (c1.getOrder() != 1 || c2.getOrder() != 1) {
 387             return false;
 388         }
 389         if (c1.getXTop() != c1.getXBot() || c2.getXTop() != c2.getXBot()) {
 390             return false;
 391         }
 392         if (c1.getYTop() != c2.getYTop() || c1.getYBot() != c2.getYBot()) {
 393             // One might be able to prove that this is impossible...
 394             return false;
 395         }
 396         return true;
 397     }
 398 
 399     /**
 400      * Tests whether this <code>Area</code> is comprised of a single
 401      * closed subpath.  This method returns <code>true</code> if the
 402      * path contains 0 or 1 subpaths, or <code>false</code> if the path
 403      * contains more than 1 subpath.  The subpaths are counted by the
 404      * number of {@link PathIterator#SEG_MOVETO SEG_MOVETO}  segments
 405      * that appear in the path.
 406      * @return    <code>true</code> if the <code>Area</code> is comprised
 407      * of a single basic geometry; <code>false</code> otherwise.
 408      * @since 1.2
 409      */
 410     public boolean isSingular() {
 411         if (curves.size() < 3) {
 412             return true;
 413         }
 414         Enumeration<Curve> enum_ = curves.elements();
 415         enum_.nextElement(); // First Order0 "moveto"
 416         while (enum_.hasMoreElements()) {
 417             if (enum_.nextElement().getOrder() == 0) {
 418                 return false;
 419             }
 420         }
 421         return true;
 422     }
 423 
 424     private Rectangle2D cachedBounds;
 425     private void invalidateBounds() {
 426         cachedBounds = null;
 427     }
 428     private Rectangle2D getCachedBounds() {
 429         if (cachedBounds != null) {
 430             return cachedBounds;
 431         }
 432         Rectangle2D r = new Rectangle2D.Double();
 433         if (curves.size() > 0) {
 434             Curve c = curves.get(0);
 435             // First point is always an order 0 curve (moveto)
 436             r.setRect(c.getX0(), c.getY0(), 0, 0);
 437             for (int i = 1; i < curves.size(); i++) {
 438                 curves.get(i).enlarge(r);
 439             }
 440         }
 441         return (cachedBounds = r);
 442     }
 443 
 444     /**
 445      * Returns a high precision bounding {@link Rectangle2D} that
 446      * completely encloses this <code>Area</code>.
 447      * <p>
 448      * The Area class will attempt to return the tightest bounding
 449      * box possible for the Shape.  The bounding box will not be
 450      * padded to include the control points of curves in the outline
 451      * of the Shape, but should tightly fit the actual geometry of
 452      * the outline itself.
 453      * @return    the bounding <code>Rectangle2D</code> for the
 454      * <code>Area</code>.
 455      * @since 1.2
 456      */
 457     public Rectangle2D getBounds2D() {
 458         return getCachedBounds().getBounds2D();


 490     /**
 491      * Tests whether the geometries of the two <code>Area</code> objects
 492      * are equal.
 493      * This method will return false if the argument is null.
 494      * @param   other  the <code>Area</code> to be compared to this
 495      *          <code>Area</code>
 496      * @return  <code>true</code> if the two geometries are equal;
 497      *          <code>false</code> otherwise.
 498      * @since 1.2
 499      */
 500     public boolean equals(Area other) {
 501         // REMIND: A *much* simpler operation should be possible...
 502         // Should be able to do a curve-wise comparison since all Areas
 503         // should evaluate their curves in the same top-down order.
 504         if (other == this) {
 505             return true;
 506         }
 507         if (other == null) {
 508             return false;
 509         }
 510         Vector<Curve> c = new AreaOp.XorOp().calculate(this.curves, other.curves);
 511         return c.isEmpty();
 512     }
 513 
 514     /**
 515      * Transforms the geometry of this <code>Area</code> using the specified
 516      * {@link AffineTransform}.  The geometry is transformed in place, which
 517      * permanently changes the enclosed area defined by this object.
 518      * @param t  the transformation used to transform the area
 519      * @throws NullPointerException if <code>t</code> is null
 520      * @since 1.2
 521      */
 522     public void transform(AffineTransform t) {
 523         if (t == null) {
 524             throw new NullPointerException("transform must not be null");
 525         }
 526         // REMIND: A simpler operation can be performed for some types
 527         // of transform.
 528         curves = pathToCurves(getPathIterator(t));
 529         invalidateBounds();
 530     }


 538      *           the new <code>Area</code>
 539      * @throws NullPointerException if <code>t</code> is null
 540      * @return   a new <code>Area</code> object representing the transformed
 541      *           geometry.
 542      * @since 1.2
 543      */
 544     public Area createTransformedArea(AffineTransform t) {
 545         Area a = new Area(this);
 546         a.transform(t);
 547         return a;
 548     }
 549 
 550     /**
 551      * {@inheritDoc}
 552      * @since 1.2
 553      */
 554     public boolean contains(double x, double y) {
 555         if (!getCachedBounds().contains(x, y)) {
 556             return false;
 557         }
 558         Enumeration<Curve> enum_ = curves.elements();
 559         int crossings = 0;
 560         while (enum_.hasMoreElements()) {
 561             Curve c = enum_.nextElement();
 562             crossings += c.crossingsFor(x, y);
 563         }
 564         return ((crossings & 1) == 1);
 565     }
 566 
 567     /**
 568      * {@inheritDoc}
 569      * @since 1.2
 570      */
 571     public boolean contains(Point2D p) {
 572         return contains(p.getX(), p.getY());
 573     }
 574 
 575     /**
 576      * {@inheritDoc}
 577      * @since 1.2
 578      */
 579     public boolean contains(double x, double y, double w, double h) {
 580         if (w < 0 || h < 0) {
 581             return false;


 641      * object is unchanged.
 642      * @param at an optional <code>AffineTransform</code> to be
 643      * applied to the coordinates as they are returned in the
 644      * iteration, or <code>null</code> if untransformed coordinates
 645      * are desired
 646      * @param flatness the maximum amount that the control points
 647      * for a given curve can vary from colinear before a subdivided
 648      * curve is replaced by a straight line connecting the end points
 649      * @return    the <code>PathIterator</code> object that returns the
 650      * geometry of the outline of this <code>Area</code>, one segment
 651      * at a time.
 652      * @since 1.2
 653      */
 654     public PathIterator getPathIterator(AffineTransform at, double flatness) {
 655         return new FlatteningPathIterator(getPathIterator(at), flatness);
 656     }
 657 }
 658 
 659 class AreaIterator implements PathIterator {
 660     private AffineTransform transform;
 661     private Vector<Curve> curves;
 662     private int index;
 663     private Curve prevcurve;
 664     private Curve thiscurve;
 665 
 666     public AreaIterator(Vector<Curve> curves, AffineTransform at) {
 667         this.curves = curves;
 668         this.transform = at;
 669         if (curves.size() >= 1) {
 670             thiscurve = curves.get(0);
 671         }
 672     }
 673 
 674     public int getWindingRule() {
 675         // REMIND: Which is better, EVEN_ODD or NON_ZERO?
 676         //         The paths calculated could be classified either way.
 677         //return WIND_EVEN_ODD;
 678         return WIND_NON_ZERO;
 679     }
 680 
 681     public boolean isDone() {
 682         return (prevcurve == null && thiscurve == null);
 683     }
 684 
 685     public void next() {
 686         if (prevcurve != null) {
 687             prevcurve = null;
 688         } else {
 689             prevcurve = thiscurve;
 690             index++;
 691             if (index < curves.size()) {
 692                 thiscurve = curves.get(index);
 693                 if (thiscurve.getOrder() != 0 &&
 694                     prevcurve.getX1() == thiscurve.getX0() &&
 695                     prevcurve.getY1() == thiscurve.getY0())
 696                 {
 697                     prevcurve = null;
 698                 }
 699             } else {
 700                 thiscurve = null;
 701             }
 702         }
 703     }
 704 
 705     public int currentSegment(float coords[]) {
 706         double dcoords[] = new double[6];
 707         int segtype = currentSegment(dcoords);
 708         int numpoints = (segtype == SEG_CLOSE ? 0
 709                          : (segtype == SEG_QUADTO ? 2
 710                             : (segtype == SEG_CUBICTO ? 3
 711                                : 1)));
 712         for (int i = 0; i < numpoints * 2; i++) {