1 /*
   2  * Copyright (c) 1995, 2013, 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 package java.awt;
  26 
  27 import java.awt.geom.AffineTransform;
  28 import java.awt.geom.PathIterator;
  29 import java.awt.geom.Point2D;
  30 import java.awt.geom.Rectangle2D;
  31 import sun.awt.geom.Crossings;
  32 import java.util.Arrays;
  33 
  34 /**
  35  * The <code>Polygon</code> class encapsulates a description of a
  36  * closed, two-dimensional region within a coordinate space. This
  37  * region is bounded by an arbitrary number of line segments, each of
  38  * which is one side of the polygon. Internally, a polygon
  39  * comprises of a list of {@code (x,y)}
  40  * coordinate pairs, where each pair defines a <i>vertex</i> of the
  41  * polygon, and two successive pairs are the endpoints of a
  42  * line that is a side of the polygon. The first and final
  43  * pairs of {@code (x,y)} points are joined by a line segment
  44  * that closes the polygon.  This <code>Polygon</code> is defined with
  45  * an even-odd winding rule.  See
  46  * {@link java.awt.geom.PathIterator#WIND_EVEN_ODD WIND_EVEN_ODD}
  47  * for a definition of the even-odd winding rule.
  48  * This class's hit-testing methods, which include the
  49  * <code>contains</code>, <code>intersects</code> and <code>inside</code>
  50  * methods, use the <i>insideness</i> definition described in the
  51  * {@link Shape} class comments.
  52  *
  53  * @author      Sami Shaio
  54  * @see Shape
  55  * @author      Herb Jellinek
  56  * @since       1.0
  57  */
  58 public class Polygon implements Shape, java.io.Serializable {
  59 
  60     /**
  61      * The total number of points.  The value of <code>npoints</code>
  62      * represents the number of valid points in this <code>Polygon</code>
  63      * and might be less than the number of elements in
  64      * {@link #xpoints xpoints} or {@link #ypoints ypoints}.
  65      * This value can be NULL.
  66      *
  67      * @serial
  68      * @see #addPoint(int, int)
  69      * @since 1.0
  70      */
  71     public int npoints;
  72 
  73     /**
  74      * The array of X coordinates.  The number of elements in
  75      * this array might be more than the number of X coordinates
  76      * in this <code>Polygon</code>.  The extra elements allow new points
  77      * to be added to this <code>Polygon</code> without re-creating this
  78      * array.  The value of {@link #npoints npoints} is equal to the
  79      * number of valid points in this <code>Polygon</code>.
  80      *
  81      * @serial
  82      * @see #addPoint(int, int)
  83      * @since 1.0
  84      */
  85     public int xpoints[];
  86 
  87     /**
  88      * The array of Y coordinates.  The number of elements in
  89      * this array might be more than the number of Y coordinates
  90      * in this <code>Polygon</code>.  The extra elements allow new points
  91      * to be added to this <code>Polygon</code> without re-creating this
  92      * array.  The value of <code>npoints</code> is equal to the
  93      * number of valid points in this <code>Polygon</code>.
  94      *
  95      * @serial
  96      * @see #addPoint(int, int)
  97      * @since 1.0
  98      */
  99     public int ypoints[];
 100 
 101     /**
 102      * The bounds of this {@code Polygon}.
 103      * This value can be null.
 104      *
 105      * @serial
 106      * @see #getBoundingBox()
 107      * @see #getBounds()
 108      * @since 1.0
 109      */
 110     protected Rectangle bounds;
 111 
 112     /*
 113      * JDK 1.1 serialVersionUID
 114      */
 115     private static final long serialVersionUID = -6460061437900069969L;
 116 
 117     /*
 118      * Default length for xpoints and ypoints.
 119      */
 120     private static final int MIN_LENGTH = 4;
 121 
 122     /**
 123      * Creates an empty polygon.
 124      * @since 1.0
 125      */
 126     public Polygon() {
 127         xpoints = new int[MIN_LENGTH];
 128         ypoints = new int[MIN_LENGTH];
 129     }
 130 
 131     /**
 132      * Constructs and initializes a <code>Polygon</code> from the specified
 133      * parameters.
 134      * @param xpoints an array of X coordinates
 135      * @param ypoints an array of Y coordinates
 136      * @param npoints the total number of points in the
 137      *                          <code>Polygon</code>
 138      * @exception  NegativeArraySizeException if the value of
 139      *                       <code>npoints</code> is negative.
 140      * @exception  IndexOutOfBoundsException if <code>npoints</code> is
 141      *             greater than the length of <code>xpoints</code>
 142      *             or the length of <code>ypoints</code>.
 143      * @exception  NullPointerException if <code>xpoints</code> or
 144      *             <code>ypoints</code> is <code>null</code>.
 145      * @since 1.0
 146      */
 147     public Polygon(int xpoints[], int ypoints[], int npoints) {
 148         // Fix 4489009: should throw IndexOutofBoundsException instead
 149         // of OutofMemoryException if npoints is huge and > {x,y}points.length
 150         if (npoints > xpoints.length || npoints > ypoints.length) {
 151             throw new IndexOutOfBoundsException("npoints > xpoints.length || "+
 152                                                 "npoints > ypoints.length");
 153         }
 154         // Fix 6191114: should throw NegativeArraySizeException with
 155         // negative npoints
 156         if (npoints < 0) {
 157             throw new NegativeArraySizeException("npoints < 0");
 158         }
 159         // Fix 6343431: Applet compatibility problems if arrays are not
 160         // exactly npoints in length
 161         this.npoints = npoints;
 162         this.xpoints = Arrays.copyOf(xpoints, npoints);
 163         this.ypoints = Arrays.copyOf(ypoints, npoints);
 164     }
 165 
 166     /**
 167      * Resets this <code>Polygon</code> object to an empty polygon.
 168      * The coordinate arrays and the data in them are left untouched
 169      * but the number of points is reset to zero to mark the old
 170      * vertex data as invalid and to start accumulating new vertex
 171      * data at the beginning.
 172      * All internally-cached data relating to the old vertices
 173      * are discarded.
 174      * Note that since the coordinate arrays from before the reset
 175      * are reused, creating a new empty <code>Polygon</code> might
 176      * be more memory efficient than resetting the current one if
 177      * the number of vertices in the new polygon data is significantly
 178      * smaller than the number of vertices in the data from before the
 179      * reset.
 180      * @see         java.awt.Polygon#invalidate
 181      * @since 1.4
 182      */
 183     public void reset() {
 184         npoints = 0;
 185         bounds = null;
 186     }
 187 
 188     /**
 189      * Invalidates or flushes any internally-cached data that depends
 190      * on the vertex coordinates of this <code>Polygon</code>.
 191      * This method should be called after any direct manipulation
 192      * of the coordinates in the <code>xpoints</code> or
 193      * <code>ypoints</code> arrays to avoid inconsistent results
 194      * from methods such as <code>getBounds</code> or <code>contains</code>
 195      * that might cache data from earlier computations relating to
 196      * the vertex coordinates.
 197      * @see         java.awt.Polygon#getBounds
 198      * @since 1.4
 199      */
 200     public void invalidate() {
 201         bounds = null;
 202     }
 203 
 204     /**
 205      * Translates the vertices of the <code>Polygon</code> by
 206      * <code>deltaX</code> along the x axis and by
 207      * <code>deltaY</code> along the y axis.
 208      * @param deltaX the amount to translate along the X axis
 209      * @param deltaY the amount to translate along the Y axis
 210      * @since 1.1
 211      */
 212     public void translate(int deltaX, int deltaY) {
 213         for (int i = 0; i < npoints; i++) {
 214             xpoints[i] += deltaX;
 215             ypoints[i] += deltaY;
 216         }
 217         if (bounds != null) {
 218             bounds.translate(deltaX, deltaY);
 219         }
 220     }
 221 
 222     /*
 223      * Calculates the bounding box of the points passed to the constructor.
 224      * Sets <code>bounds</code> to the result.
 225      * @param xpoints[] array of <i>x</i> coordinates
 226      * @param ypoints[] array of <i>y</i> coordinates
 227      * @param npoints the total number of points
 228      */
 229     void calculateBounds(int xpoints[], int ypoints[], int npoints) {
 230         int boundsMinX = Integer.MAX_VALUE;
 231         int boundsMinY = Integer.MAX_VALUE;
 232         int boundsMaxX = Integer.MIN_VALUE;
 233         int boundsMaxY = Integer.MIN_VALUE;
 234 
 235         for (int i = 0; i < npoints; i++) {
 236             int x = xpoints[i];
 237             boundsMinX = Math.min(boundsMinX, x);
 238             boundsMaxX = Math.max(boundsMaxX, x);
 239             int y = ypoints[i];
 240             boundsMinY = Math.min(boundsMinY, y);
 241             boundsMaxY = Math.max(boundsMaxY, y);
 242         }
 243         bounds = new Rectangle(boundsMinX, boundsMinY,
 244                                boundsMaxX - boundsMinX,
 245                                boundsMaxY - boundsMinY);
 246     }
 247 
 248     /*
 249      * Resizes the bounding box to accommodate the specified coordinates.
 250      * @param x,&nbsp;y the specified coordinates
 251      */
 252     void updateBounds(int x, int y) {
 253         if (x < bounds.x) {
 254             bounds.width = bounds.width + (bounds.x - x);
 255             bounds.x = x;
 256         }
 257         else {
 258             bounds.width = Math.max(bounds.width, x - bounds.x);
 259             // bounds.x = bounds.x;
 260         }
 261 
 262         if (y < bounds.y) {
 263             bounds.height = bounds.height + (bounds.y - y);
 264             bounds.y = y;
 265         }
 266         else {
 267             bounds.height = Math.max(bounds.height, y - bounds.y);
 268             // bounds.y = bounds.y;
 269         }
 270     }
 271 
 272     /**
 273      * Appends the specified coordinates to this <code>Polygon</code>.
 274      * <p>
 275      * If an operation that calculates the bounding box of this
 276      * <code>Polygon</code> has already been performed, such as
 277      * <code>getBounds</code> or <code>contains</code>, then this
 278      * method updates the bounding box.
 279      * @param       x the specified X coordinate
 280      * @param       y the specified Y coordinate
 281      * @see         java.awt.Polygon#getBounds
 282      * @see         java.awt.Polygon#contains
 283      * @since 1.0
 284      */
 285     public void addPoint(int x, int y) {
 286         if (npoints >= xpoints.length || npoints >= ypoints.length) {
 287             int newLength = npoints * 2;
 288             // Make sure that newLength will be greater than MIN_LENGTH and
 289             // aligned to the power of 2
 290             if (newLength < MIN_LENGTH) {
 291                 newLength = MIN_LENGTH;
 292             } else if ((newLength & (newLength - 1)) != 0) {
 293                 newLength = Integer.highestOneBit(newLength);
 294             }
 295 
 296             xpoints = Arrays.copyOf(xpoints, newLength);
 297             ypoints = Arrays.copyOf(ypoints, newLength);
 298         }
 299         xpoints[npoints] = x;
 300         ypoints[npoints] = y;
 301         npoints++;
 302         if (bounds != null) {
 303             updateBounds(x, y);
 304         }
 305     }
 306 
 307     /**
 308      * Gets the bounding box of this <code>Polygon</code>.
 309      * The bounding box is the smallest {@link Rectangle} whose
 310      * sides are parallel to the x and y axes of the
 311      * coordinate space, and can completely contain the <code>Polygon</code>.
 312      * @return a <code>Rectangle</code> that defines the bounds of this
 313      * <code>Polygon</code>.
 314      * @since 1.1
 315      */
 316     public Rectangle getBounds() {
 317         return getBoundingBox();
 318     }
 319 
 320     /**
 321      * Returns the bounds of this <code>Polygon</code>.
 322      * @return the bounds of this <code>Polygon</code>.
 323      * @deprecated As of JDK version 1.1,
 324      * replaced by <code>getBounds()</code>.
 325      * @since 1.0
 326      */
 327     @Deprecated
 328     public Rectangle getBoundingBox() {
 329         if (npoints == 0) {
 330             return new Rectangle();
 331         }
 332         if (bounds == null) {
 333             calculateBounds(xpoints, ypoints, npoints);
 334         }
 335         return bounds.getBounds();
 336     }
 337 
 338     /**
 339      * Determines whether the specified {@link Point} is inside this
 340      * <code>Polygon</code>.
 341      * @param p the specified <code>Point</code> to be tested
 342      * @return <code>true</code> if the <code>Polygon</code> contains the
 343      *                  <code>Point</code>; <code>false</code> otherwise.
 344      * @see #contains(double, double)
 345      * @since 1.0
 346      */
 347     public boolean contains(Point p) {
 348         return contains(p.x, p.y);
 349     }
 350 
 351     /**
 352      * Determines whether the specified coordinates are inside this
 353      * <code>Polygon</code>.
 354      * <p>
 355      * @param x the specified X coordinate to be tested
 356      * @param y the specified Y coordinate to be tested
 357      * @return {@code true} if this {@code Polygon} contains
 358      *         the specified coordinates {@code (x,y)};
 359      *         {@code false} otherwise.
 360      * @see #contains(double, double)
 361      * @since 1.1
 362      */
 363     public boolean contains(int x, int y) {
 364         return contains((double) x, (double) y);
 365     }
 366 
 367     /**
 368      * Determines whether the specified coordinates are contained in this
 369      * <code>Polygon</code>.
 370      * @param x the specified X coordinate to be tested
 371      * @param y the specified Y coordinate to be tested
 372      * @return {@code true} if this {@code Polygon} contains
 373      *         the specified coordinates {@code (x,y)};
 374      *         {@code false} otherwise.
 375      * @see #contains(double, double)
 376      * @deprecated As of JDK version 1.1,
 377      * replaced by <code>contains(int, int)</code>.
 378      * @since 1.0
 379      */
 380     @Deprecated
 381     public boolean inside(int x, int y) {
 382         return contains((double) x, (double) y);
 383     }
 384 
 385     /**
 386      * {@inheritDoc}
 387      * @since 1.2
 388      */
 389     public Rectangle2D getBounds2D() {
 390         return getBounds();
 391     }
 392 
 393     /**
 394      * {@inheritDoc}
 395      * @since 1.2
 396      */
 397     public boolean contains(double x, double y) {
 398         if (npoints <= 2 || !getBoundingBox().contains(x, y)) {
 399             return false;
 400         }
 401         int hits = 0;
 402 
 403         int lastx = xpoints[npoints - 1];
 404         int lasty = ypoints[npoints - 1];
 405         int curx, cury;
 406 
 407         // Walk the edges of the polygon
 408         for (int i = 0; i < npoints; lastx = curx, lasty = cury, i++) {
 409             curx = xpoints[i];
 410             cury = ypoints[i];
 411 
 412             if (cury == lasty) {
 413                 continue;
 414             }
 415 
 416             int leftx;
 417             if (curx < lastx) {
 418                 if (x >= lastx) {
 419                     continue;
 420                 }
 421                 leftx = curx;
 422             } else {
 423                 if (x >= curx) {
 424                     continue;
 425                 }
 426                 leftx = lastx;
 427             }
 428 
 429             double test1, test2;
 430             if (cury < lasty) {
 431                 if (y < cury || y >= lasty) {
 432                     continue;
 433                 }
 434                 if (x < leftx) {
 435                     hits++;
 436                     continue;
 437                 }
 438                 test1 = x - curx;
 439                 test2 = y - cury;
 440             } else {
 441                 if (y < lasty || y >= cury) {
 442                     continue;
 443                 }
 444                 if (x < leftx) {
 445                     hits++;
 446                     continue;
 447                 }
 448                 test1 = x - lastx;
 449                 test2 = y - lasty;
 450             }
 451 
 452             if (test1 < (test2 / (lasty - cury) * (lastx - curx))) {
 453                 hits++;
 454             }
 455         }
 456 
 457         return ((hits & 1) != 0);
 458     }
 459 
 460     private Crossings getCrossings(double xlo, double ylo,
 461                                    double xhi, double yhi)
 462     {
 463         Crossings cross = new Crossings.EvenOdd(xlo, ylo, xhi, yhi);
 464         int lastx = xpoints[npoints - 1];
 465         int lasty = ypoints[npoints - 1];
 466         int curx, cury;
 467 
 468         // Walk the edges of the polygon
 469         for (int i = 0; i < npoints; i++) {
 470             curx = xpoints[i];
 471             cury = ypoints[i];
 472             if (cross.accumulateLine(lastx, lasty, curx, cury)) {
 473                 return null;
 474             }
 475             lastx = curx;
 476             lasty = cury;
 477         }
 478 
 479         return cross;
 480     }
 481 
 482     /**
 483      * {@inheritDoc}
 484      * @since 1.2
 485      */
 486     public boolean contains(Point2D p) {
 487         return contains(p.getX(), p.getY());
 488     }
 489 
 490     /**
 491      * {@inheritDoc}
 492      * @since 1.2
 493      */
 494     public boolean intersects(double x, double y, double w, double h) {
 495         if (npoints <= 0 || !getBoundingBox().intersects(x, y, w, h)) {
 496             return false;
 497         }
 498 
 499         Crossings cross = getCrossings(x, y, x+w, y+h);
 500         return (cross == null || !cross.isEmpty());
 501     }
 502 
 503     /**
 504      * {@inheritDoc}
 505      * @since 1.2
 506      */
 507     public boolean intersects(Rectangle2D r) {
 508         return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
 509     }
 510 
 511     /**
 512      * {@inheritDoc}
 513      * @since 1.2
 514      */
 515     public boolean contains(double x, double y, double w, double h) {
 516         if (npoints <= 0 || !getBoundingBox().intersects(x, y, w, h)) {
 517             return false;
 518         }
 519 
 520         Crossings cross = getCrossings(x, y, x+w, y+h);
 521         return (cross != null && cross.covers(y, y+h));
 522     }
 523 
 524     /**
 525      * {@inheritDoc}
 526      * @since 1.2
 527      */
 528     public boolean contains(Rectangle2D r) {
 529         return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
 530     }
 531 
 532     /**
 533      * Returns an iterator object that iterates along the boundary of this
 534      * <code>Polygon</code> and provides access to the geometry
 535      * of the outline of this <code>Polygon</code>.  An optional
 536      * {@link AffineTransform} can be specified so that the coordinates
 537      * returned in the iteration are transformed accordingly.
 538      * @param at an optional <code>AffineTransform</code> to be applied to the
 539      *          coordinates as they are returned in the iteration, or
 540      *          <code>null</code> if untransformed coordinates are desired
 541      * @return a {@link PathIterator} object that provides access to the
 542      *          geometry of this <code>Polygon</code>.
 543      * @since 1.2
 544      */
 545     public PathIterator getPathIterator(AffineTransform at) {
 546         return new PolygonPathIterator(this, at);
 547     }
 548 
 549     /**
 550      * Returns an iterator object that iterates along the boundary of
 551      * the <code>Shape</code> and provides access to the geometry of the
 552      * outline of the <code>Shape</code>.  Only SEG_MOVETO, SEG_LINETO, and
 553      * SEG_CLOSE point types are returned by the iterator.
 554      * Since polygons are already flat, the <code>flatness</code> parameter
 555      * is ignored.  An optional <code>AffineTransform</code> can be specified
 556      * in which case the coordinates returned in the iteration are transformed
 557      * accordingly.
 558      * @param at an optional <code>AffineTransform</code> to be applied to the
 559      *          coordinates as they are returned in the iteration, or
 560      *          <code>null</code> if untransformed coordinates are desired
 561      * @param flatness the maximum amount that the control points
 562      *          for a given curve can vary from colinear before a subdivided
 563      *          curve is replaced by a straight line connecting the
 564      *          endpoints.  Since polygons are already flat the
 565      *          <code>flatness</code> parameter is ignored.
 566      * @return a <code>PathIterator</code> object that provides access to the
 567      *          <code>Shape</code> object's geometry.
 568      * @since 1.2
 569      */
 570     public PathIterator getPathIterator(AffineTransform at, double flatness) {
 571         return getPathIterator(at);
 572     }
 573 
 574     class PolygonPathIterator implements PathIterator {
 575         Polygon poly;
 576         AffineTransform transform;
 577         int index;
 578 
 579         public PolygonPathIterator(Polygon pg, AffineTransform at) {
 580             poly = pg;
 581             transform = at;
 582             if (pg.npoints == 0) {
 583                 // Prevent a spurious SEG_CLOSE segment
 584                 index = 1;
 585             }
 586         }
 587 
 588         /**
 589          * Returns the winding rule for determining the interior of the
 590          * path.
 591          * @return an integer representing the current winding rule.
 592          * @see PathIterator#WIND_NON_ZERO
 593          */
 594         public int getWindingRule() {
 595             return WIND_EVEN_ODD;
 596         }
 597 
 598         /**
 599          * Tests if there are more points to read.
 600          * @return <code>true</code> if there are more points to read;
 601          *          <code>false</code> otherwise.
 602          */
 603         public boolean isDone() {
 604             return index > poly.npoints;
 605         }
 606 
 607         /**
 608          * Moves the iterator forwards, along the primary direction of
 609          * traversal, to the next segment of the path when there are
 610          * more points in that direction.
 611          */
 612         public void next() {
 613             index++;
 614         }
 615 
 616         /**
 617          * Returns the coordinates and type of the current path segment in
 618          * the iteration.
 619          * The return value is the path segment type:
 620          * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
 621          * A <code>float</code> array of length 2 must be passed in and
 622          * can be used to store the coordinates of the point(s).
 623          * Each point is stored as a pair of <code>float</code> x,&nbsp;y
 624          * coordinates.  SEG_MOVETO and SEG_LINETO types return one
 625          * point, and SEG_CLOSE does not return any points.
 626          * @param coords a <code>float</code> array that specifies the
 627          * coordinates of the point(s)
 628          * @return an integer representing the type and coordinates of the
 629          *              current path segment.
 630          * @see PathIterator#SEG_MOVETO
 631          * @see PathIterator#SEG_LINETO
 632          * @see PathIterator#SEG_CLOSE
 633          */
 634         public int currentSegment(float[] coords) {
 635             if (index >= poly.npoints) {
 636                 return SEG_CLOSE;
 637             }
 638             coords[0] = poly.xpoints[index];
 639             coords[1] = poly.ypoints[index];
 640             if (transform != null) {
 641                 transform.transform(coords, 0, coords, 0, 1);
 642             }
 643             return (index == 0 ? SEG_MOVETO : SEG_LINETO);
 644         }
 645 
 646         /**
 647          * Returns the coordinates and type of the current path segment in
 648          * the iteration.
 649          * The return value is the path segment type:
 650          * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
 651          * A <code>double</code> array of length 2 must be passed in and
 652          * can be used to store the coordinates of the point(s).
 653          * Each point is stored as a pair of <code>double</code> x,&nbsp;y
 654          * coordinates.
 655          * SEG_MOVETO and SEG_LINETO types return one point,
 656          * and SEG_CLOSE does not return any points.
 657          * @param coords a <code>double</code> array that specifies the
 658          * coordinates of the point(s)
 659          * @return an integer representing the type and coordinates of the
 660          *              current path segment.
 661          * @see PathIterator#SEG_MOVETO
 662          * @see PathIterator#SEG_LINETO
 663          * @see PathIterator#SEG_CLOSE
 664          */
 665         public int currentSegment(double[] coords) {
 666             if (index >= poly.npoints) {
 667                 return SEG_CLOSE;
 668             }
 669             coords[0] = poly.xpoints[index];
 670             coords[1] = poly.ypoints[index];
 671             if (transform != null) {
 672                 transform.transform(coords, 0, coords, 0, 1);
 673             }
 674             return (index == 0 ? SEG_MOVETO : SEG_LINETO);
 675         }
 676     }
 677 }