Save This Page
Home » openjdk-7 » java » awt » geom » [javadoc | source]
    1   /*
    2    * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package java.awt.geom;
   27   
   28   import java.awt.Shape;
   29   import java.awt.Rectangle;
   30   import java.util.Vector;
   31   import java.util.Enumeration;
   32   import java.util.NoSuchElementException;
   33   import sun.awt.geom.Curve;
   34   import sun.awt.geom.Crossings;
   35   import sun.awt.geom.AreaOp;
   36   
   37   /**
   38    * An <code>Area</code> object stores and manipulates a
   39    * resolution-independent description of an enclosed area of
   40    * 2-dimensional space.
   41    * <code>Area</code> objects can be transformed and can perform
   42    * various Constructive Area Geometry (CAG) operations when combined
   43    * with other <code>Area</code> objects.
   44    * The CAG operations include area
   45    * {@link #add addition}, {@link #subtract subtraction},
   46    * {@link #intersect intersection}, and {@link #exclusiveOr exclusive or}.
   47    * See the linked method documentation for examples of the various
   48    * operations.
   49    * <p>
   50    * The <code>Area</code> class implements the <code>Shape</code>
   51    * interface and provides full support for all of its hit-testing
   52    * and path iteration facilities, but an <code>Area</code> is more
   53    * specific than a generalized path in a number of ways:
   54    * <ul>
   55    * <li>Only closed paths and sub-paths are stored.
   56    *     <code>Area</code> objects constructed from unclosed paths
   57    *     are implicitly closed during construction as if those paths
   58    *     had been filled by the <code>Graphics2D.fill</code> method.
   59    * <li>The interiors of the individual stored sub-paths are all
   60    *     non-empty and non-overlapping.  Paths are decomposed during
   61    *     construction into separate component non-overlapping parts,
   62    *     empty pieces of the path are discarded, and then these
   63    *     non-empty and non-overlapping properties are maintained
   64    *     through all subsequent CAG operations.  Outlines of different
   65    *     component sub-paths may touch each other, as long as they
   66    *     do not cross so that their enclosed areas overlap.
   67    * <li>The geometry of the path describing the outline of the
   68    *     <code>Area</code> resembles the path from which it was
   69    *     constructed only in that it describes the same enclosed
   70    *     2-dimensional area, but may use entirely different types
   71    *     and ordering of the path segments to do so.
   72    * </ul>
   73    * Interesting issues which are not always obvious when using
   74    * the <code>Area</code> include:
   75    * <ul>
   76    * <li>Creating an <code>Area</code> from an unclosed (open)
   77    *     <code>Shape</code> results in a closed outline in the
   78    *     <code>Area</code> object.
   79    * <li>Creating an <code>Area</code> from a <code>Shape</code>
   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()) {
  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 =&gt; 8,0 =&gt; 0,8]);
  207        *     Area a2 = new Area([triangle 0,0 =&gt; 8,0 =&gt; 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 =&gt; 8,0 =&gt; 0,8]);
  240        *     Area a2 = new Area([triangle 0,0 =&gt; 8,0 =&gt; 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 =&gt; 8,0 =&gt; 0,8]);
  273        *     Area a2 = new Area([triangle 0,0 =&gt; 8,0 =&gt; 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 =&gt; 8,0 =&gt; 0,8]);
  307        *     Area a2 = new Area([triangle 0,0 =&gt; 8,0 =&gt; 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() || 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();
  459       }
  460   
  461       /**
  462        * Returns a bounding {@link Rectangle} that completely encloses
  463        * this <code>Area</code>.
  464        * <p>
  465        * The Area class will attempt to return the tightest bounding
  466        * box possible for the Shape.  The bounding box will not be
  467        * padded to include the control points of curves in the outline
  468        * of the Shape, but should tightly fit the actual geometry of
  469        * the outline itself.  Since the returned object represents
  470        * the bounding box with integers, the bounding box can only be
  471        * as tight as the nearest integer coordinates that encompass
  472        * the geometry of the Shape.
  473        * @return    the bounding <code>Rectangle</code> for the
  474        * <code>Area</code>.
  475        * @since 1.2
  476        */
  477       public Rectangle getBounds() {
  478           return getCachedBounds().getBounds();
  479       }
  480   
  481       /**
  482        * Returns an exact copy of this <code>Area</code> object.
  483        * @return    Created clone object
  484        * @since 1.2
  485        */
  486       public Object clone() {
  487           return new Area(this);
  488       }
  489   
  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       }
  531   
  532       /**
  533        * Creates a new <code>Area</code> object that contains the same
  534        * geometry as this <code>Area</code> transformed by the specified
  535        * <code>AffineTransform</code>.  This <code>Area</code> object
  536        * is unchanged.
  537        * @param t  the specified <code>AffineTransform</code> used to transform
  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;
  582           }
  583           if (!getCachedBounds().contains(x, y, w, h)) {
  584               return false;
  585           }
  586           Crossings c = Crossings.findCrossings(curves, x, y, x+w, y+h);
  587           return (c != null && c.covers(y, y+h));
  588       }
  589   
  590       /**
  591        * {@inheritDoc}
  592        * @since 1.2
  593        */
  594       public boolean contains(Rectangle2D r) {
  595           return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  596       }
  597   
  598       /**
  599        * {@inheritDoc}
  600        * @since 1.2
  601        */
  602       public boolean intersects(double x, double y, double w, double h) {
  603           if (w < 0 || h < 0) {
  604               return false;
  605           }
  606           if (!getCachedBounds().intersects(x, y, w, h)) {
  607               return false;
  608           }
  609           Crossings c = Crossings.findCrossings(curves, x, y, x+w, y+h);
  610           return (c == null || !c.isEmpty());
  611       }
  612   
  613       /**
  614        * {@inheritDoc}
  615        * @since 1.2
  616        */
  617       public boolean intersects(Rectangle2D r) {
  618           return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  619       }
  620   
  621       /**
  622        * Creates a {@link PathIterator} for the outline of this
  623        * <code>Area</code> object.  This <code>Area</code> object is unchanged.
  624        * @param at an optional <code>AffineTransform</code> to be applied to
  625        * the coordinates as they are returned in the iteration, or
  626        * <code>null</code> if untransformed coordinates are desired
  627        * @return    the <code>PathIterator</code> object that returns the
  628        *          geometry of the outline of this <code>Area</code>, one
  629        *          segment at a time.
  630        * @since 1.2
  631        */
  632       public PathIterator getPathIterator(AffineTransform at) {
  633           return new AreaIterator(curves, at);
  634       }
  635   
  636       /**
  637        * Creates a <code>PathIterator</code> for the flattened outline of
  638        * this <code>Area</code> object.  Only uncurved path segments
  639        * represented by the SEG_MOVETO, SEG_LINETO, and SEG_CLOSE point
  640        * types are returned by the iterator.  This <code>Area</code>
  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++) {
  713               coords[i] = (float) dcoords[i];
  714           }
  715           return segtype;
  716       }
  717   
  718       public int currentSegment(double coords[]) {
  719           int segtype;
  720           int numpoints;
  721           if (prevcurve != null) {
  722               // Need to finish off junction between curves
  723               if (thiscurve == null || thiscurve.getOrder() == 0) {
  724                   return SEG_CLOSE;
  725               }
  726               coords[0] = thiscurve.getX0();
  727               coords[1] = thiscurve.getY0();
  728               segtype = SEG_LINETO;
  729               numpoints = 1;
  730           } else if (thiscurve == null) {
  731               throw new NoSuchElementException("area iterator out of bounds");
  732           } else {
  733               segtype = thiscurve.getSegment(coords);
  734               numpoints = thiscurve.getOrder();
  735               if (numpoints == 0) {
  736                   numpoints = 1;
  737               }
  738           }
  739           if (transform != null) {
  740               transform.transform(coords, 0, coords, 0, numpoints);
  741           }
  742           return segtype;
  743       }
  744   }

Save This Page
Home » openjdk-7 » java » awt » geom » [javadoc | source]