gt:                 double y = bezierCoords[ci + 1];
context: /*
   * $Id: Morphing2D.java 3668 2010-04-22 17:47:57Z kschaefe $
   *
   * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
   * Santa Clara, California 95054, U.S.A. All rights reserved.
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   */
  package org.jdesktop.swingx.geom;
  
  import java.awt.Rectangle;
  import java.awt.Shape;
  import java.awt.geom.AffineTransform;
  import java.awt.geom.FlatteningPathIterator;
  import java.awt.geom.IllegalPathStateException;
  import java.awt.geom.PathIterator;
  import java.awt.geom.Point2D;
  import java.awt.geom.Rectangle2D;
  
  /**
   * <p>A morphing shape is a shape which geometry is constructed from two
   * other shapes: a start shape and an end shape.</p>
   * <p>The morphing property of a morphing shape defines the amount of
   * transformation applied to the start shape to turn it into the end shape.</p>
   * <p>Both shapes must have the same winding rule.</p>
   *
   * @author Jim Graham
   * @author Romain Guy <romain.guy@mac.com> (Maintainer)
   */
  public class Morphing2D implements Shape {
      private double morph;
      private Geometry startGeometry;
      private Geometry endGeometry;
  
      /**
       * <p>Creates a new morphing shape. A morphing shape can be used to turn
       * one shape into another one. The transformation can be controlled by the
       * morph property.</p>
       *
       * @param startShape the shape to morph from
       * @param endShape   the shape to morph to
       *
       * @throws IllegalPathStateException if the shapes do not have the same
       *                                   winding rule
       * @see #getMorphing()
       * @see #setMorphing(double)
       */
      public Morphing2D(Shape startShape, Shape endShape) {
          startGeometry = new Geometry(startShape);
          endGeometry = new Geometry(endShape);
          if (startGeometry.getWindingRule() != endGeometry.getWindingRule()) {
              throw new IllegalPathStateException("shapes must use same " +
                                                  "winding rule");
          }
          double tvals0[] = startGeometry.getTvals();
          double tvals1[] = endGeometry.getTvals();
          double masterTvals[] = mergeTvals(tvals0, tvals1);
          startGeometry.setTvals(masterTvals);
          endGeometry.setTvals(masterTvals);
      }
  
      /**
       * <p>Returns the morphing value between the two shapes.</p>
       *
       * @return the morphing value between the two shapes
       *
       * @see #setMorphing(double)
       */
      public double getMorphing() {
          return morph;
      }
  
      /**
       * <p>Sets the morphing value between the two shapes. This value controls
       * the transformation from the start shape to the end shape. A value of 0.0
       * is the start shape. A value of 1.0 is the end shape. A value of 0.5 is a
       * new shape, morphed half way from the start shape to the end shape.</p>
       * <p>The specified value should be between 0.0 and 1.0. If not, the value
       * is clamped in the appropriate range.</p>
       *
       * @param morph the morphing value between the two shapes
       *
       * @see #getMorphing()
       */
      public void setMorphing(double morph) {
          if (morph > 1) {
              morph = 1;
          } else if (morph >= 0) {
              // morphing is finite, not NaN, and in range
          } else {
              // morph is < 0 or NaN
              morph = 0;
          }
          this.morph = morph;
      }
  
      private static double interp(double v0, double v1, double t) {
          return (v0 + ((v1 - v0) * t));
      }
  
      private static double[] mergeTvals(double tvals0[], double tvals1[]) {
          int i0 = 0;
          int i1 = 0;
          int numtvals = 0;
          while (i0 < tvals0.length && i1 < tvals1.length) {
              double t0 = tvals0[i0];
              double t1 = tvals1[i1];
              if (t0 <= t1) {
                  i0++;
              }
              if (t1 <= t0) {
                  i1++;
              }
              numtvals++;
          }
          double newtvals[] = new double[numtvals];
          i0 = 0;
          i1 = 0;
          numtvals = 0;
          while (i0 < tvals0.length && i1 < tvals1.length) {
              double t0 = tvals0[i0];
              double t1 = tvals1[i1];
              if (t0 <= t1) {
                  newtvals[numtvals] = t0;
                  i0++;
              }
              if (t1 <= t0) {
                  newtvals[numtvals] = t1;
                  i1++;
              }
              numtvals++;
          }
          return newtvals;
      }
  
      /**
       * {@inheritDoc}
       */
      public Rectangle getBounds() {
          return getBounds2D().getBounds();
      }
  
      /**
       * {@inheritDoc}
       */
      public Rectangle2D getBounds2D() {
          int n = startGeometry.getNumCoords();
          double xmin, ymin, xmax, ymax;
          xmin = xmax = interp(startGeometry.getCoord(0), endGeometry.getCoord(0),
                               morph);
          ymin = ymax = interp(startGeometry.getCoord(1), endGeometry.getCoord(1),
                               morph);
          for (int i = 2; i < n; i += 2) {
              double x = interp(startGeometry.getCoord(i),
                                endGeometry.getCoord(i), morph);
              double y = interp(startGeometry.getCoord(i + 1),
                                endGeometry.getCoord(i + 1), morph);
              if (xmin > x) {
                  xmin = x;
              }
              if (ymin > y) {
                  ymin = y;
              }
              if (xmax < x) {
                  xmax = x;
              }
              if (ymax < y) {
                  ymax = y;
              }
          }
          return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin);
      }
  
      /**
       * {@inheritDoc}
       */
      public boolean contains(double x, double y) {
          throw new InternalError("unimplemented");
      }
  
      /**
       * {@inheritDoc}
       */
      public boolean contains(Point2D p) {
          return contains(p.getX(), p.getY());
      }
  
      /**
       * {@inheritDoc}
       */
      public boolean intersects(double x, double y, double w, double h) {
          throw new InternalError("unimplemented");
      }
  
      /**
       * {@inheritDoc}
       */
      public boolean intersects(Rectangle2D r) {
          return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
      }
  
      /**
       * {@inheritDoc}
       */
      public boolean contains(double x, double y, double w, double h) {
          throw new InternalError("unimplemented");
      }
  
      /**
       * {@inheritDoc}
       */
      public boolean contains(Rectangle2D r) {
          return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
      }
  
      /**
       * {@inheritDoc}
       */
      public PathIterator getPathIterator(AffineTransform at) {
          return new Iterator(at, startGeometry, endGeometry, morph);
      }
  
      /**
       * {@inheritDoc}
       */
      public PathIterator getPathIterator(AffineTransform at, double flatness) {
          return new FlatteningPathIterator(getPathIterator(at), flatness);
      }
  
      private static class Geometry {
          static final double THIRD = (1.0 / 3.0);
          static final double MIN_LEN = 0.001;
          double bezierCoords[];
          int numCoords;
          int windingrule;
          double myTvals[];
  
          public Geometry(Shape s) {
              // Multiple of 6 plus 2 more for initial moveto
              bezierCoords = new double[20];
              PathIterator pi = s.getPathIterator(null);
              windingrule = pi.getWindingRule();
              if (pi.isDone()) {
                  // We will have 1 segment and it will be all zeros
                  // It will have 8 coordinates (2 for moveto, 6 for cubic)
                  numCoords = 8;
              }
              double coords[] = new double[6];
              int type = pi.currentSegment(coords);
              pi.next();
              if (type != PathIterator.SEG_MOVETO) {
                  throw new IllegalPathStateException("missing initial moveto");
              }
              double curx = bezierCoords[0] = coords[0];
              double cury = bezierCoords[1] = coords[1];
              double newx, newy;
              numCoords = 2;
              while (!pi.isDone()) {
                  if (numCoords + 6 > bezierCoords.length) {
                      // Keep array size to a multiple of 6 plus 2
                      int newsize = (numCoords - 2) * 2 + 2;
                      double newCoords[] = new double[newsize];
                      System.arraycopy(bezierCoords, 0, newCoords, 0, numCoords);
                      bezierCoords = newCoords;
                  }
                  switch (pi.currentSegment(coords)) {
                      case PathIterator.SEG_MOVETO:
                          throw new InternalError(
                                  "Cannot handle multiple subpaths");
                      case PathIterator.SEG_CLOSE:
                          if (curx == bezierCoords[0] && cury == bezierCoords[1])
                          {
                              break;
                          }
                          coords[0] = bezierCoords[0];
                          coords[1] = bezierCoords[1];
                          /* NO BREAK */
                      case PathIterator.SEG_LINETO:
                          newx = coords[0];
                          newy = coords[1];
                          // A third of the way from curxy to newxy:
                          bezierCoords[numCoords++] = interp(curx, newx, THIRD);
                          bezierCoords[numCoords++] = interp(cury, newy, THIRD);
                          // A third of the way from newxy back to curxy:
                          bezierCoords[numCoords++] = interp(newx, curx, THIRD);
                          bezierCoords[numCoords++] = interp(newy, cury, THIRD);
                          bezierCoords[numCoords++] = curx = newx;
                          bezierCoords[numCoords++] = cury = newy;
                          break;
                      case PathIterator.SEG_QUADTO:
                          double ctrlx = coords[0];
                          double ctrly = coords[1];
                          newx = coords[2];
                          newy = coords[3];
                          // A third of the way from ctrlxy back to curxy:
                          bezierCoords[numCoords++] = interp(ctrlx, curx, THIRD);
                          bezierCoords[numCoords++] = interp(ctrly, cury, THIRD);
                          // A third of the way from ctrlxy to newxy:
                          bezierCoords[numCoords++] = interp(ctrlx, newx, THIRD);
                          bezierCoords[numCoords++] = interp(ctrly, newy, THIRD);
                          bezierCoords[numCoords++] = curx = newx;
                          bezierCoords[numCoords++] = cury = newy;
                          break;
                      case PathIterator.SEG_CUBICTO:
                          bezierCoords[numCoords++] = coords[0];
                          bezierCoords[numCoords++] = coords[1];
                          bezierCoords[numCoords++] = coords[2];
                          bezierCoords[numCoords++] = coords[3];
                          bezierCoords[numCoords++] = curx = coords[4];
                          bezierCoords[numCoords++] = cury = coords[5];
                          break;
                  }
                  pi.next();
              }
              // Add closing segment if either:
              // - we only have initial moveto - expand it to an empty cubic
              // - or we are not back to the starting point
              if ((numCoords < 8) ||
                  curx != bezierCoords[0] ||
                  cury != bezierCoords[1]) {
                  newx = bezierCoords[0];
                  newy = bezierCoords[1];
                  // A third of the way from curxy to newxy:
                  bezierCoords[numCoords++] = interp(curx, newx, THIRD);
                  bezierCoords[numCoords++] = interp(cury, newy, THIRD);
                  // A third of the way from newxy back to curxy:
                  bezierCoords[numCoords++] = interp(newx, curx, THIRD);
                  bezierCoords[numCoords++] = interp(newy, cury, THIRD);
                  bezierCoords[numCoords++] = newx;
                  bezierCoords[numCoords++] = newy;
              }
              // Now find the segment endpoint with the smallest Y coordinate
              int minPt = 0;
              double minX = bezierCoords[0];
              double minY = bezierCoords[1];
              for (int ci = 6; ci < numCoords; ci += 6) {
                  double x = bezierCoords[ci];
  