gt:             tmpGeneralPath = createPathFromPool();
context: /*
   * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Codename One designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *  
   * This code 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 General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   * 
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   * 
   * Please contact Codename One through http://www.codenameone.com/ if you 
   * need additional information or have any questions.
   */
  package com.codename1.ui.geom;
  
  
  import com.codename1.io.Log;
  import com.codename1.ui.Transform;
  import com.codename1.util.MathUtil;
  import java.util.ArrayList;
  import java.util.Arrays;
  
  /**
   * <p>A general geometric path, consisting of any number of subpaths constructed
   * out of straight lines and cubic or quadratic Bezier curves. The inside of the
   * curve is defined for drawing purposes by a winding rule. Either the
   * {@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO} winding rule can be chosen.</p>
   *
   * <h4>A drawing of a GeneralPath</h4>
   *
   * <img
   * src="http://developer.classpath.org/doc/java/awt/geom/doc-files/GeneralPath-1.png"/>
   *
   * <p>
   * The {@link #WIND_EVEN_ODD} winding rule defines a point as inside a path if:
   * A ray from the point towards infinity in an arbitrary direction intersects
   * the path an odd number of times. Points {@literal A} and {@literal C} in the
   * image are considered to be outside the path. (both intersect twice) Point
   * {@literal B} intersects once, and is inside.</p>
   *
   * <p>
   * The {@link #WIND_NON_ZERO} winding rule defines a point as inside a path if:
   * The path intersects the ray in an equal number of opposite directions. Point
   * {@link A} in the image is outside (one intersection in the 'up' direction,
   * one in the 'down' direction) Point {@literal B} in the image is inside (one
   * intersection 'down') Point C in the image is inside (two intersections in the
   * 'down' direction)</p>
   *
   * <script src="https://gist.github.com/codenameone/3f2f8cdaabb7780eae6f.js"></script>
   * <img src="https://www.codenameone.com/img/developer-guide/graphics-shape-fill.png" alt="Fill a shape general path" />
   * 
   * <p>Note: This description and image were copied from <a
   * href="http://developer.classpath.org/doc/java/awt/geom/GeneralPath.html">the
   * GNU classpath</a>
   * docs). License here http://www.gnu.org/licenses/licenses.html#FDL</p>
   *
   * @author shannah
   *
   * @see com.codename1.ui.Graphics#drawShape
   * @see com.codename1.ui.Graphics#fillShape
   */
  public final class GeneralPath implements Shape {
  
      private static int MAX_POOL_SIZE=20;
      
      private static ArrayList<GeneralPath> pathPool;
      private static ArrayList<Rectangle> rectPool;
      private static ArrayList<float[]> floatPool;
      private static ArrayList<boolean[]> boolPool;
      private static ArrayList<Iterator> iteratorPool;
      
      
      private static ArrayList<GeneralPath> pathPool() {
          if (pathPool == null) {
              pathPool = new ArrayList<GeneralPath>();
          }
          return pathPool;
      }
      
      private static ArrayList<Rectangle> rectPool() {
          if (rectPool == null) {
              rectPool = new ArrayList<Rectangle>();
          }
          return rectPool;
      }
      
      private static ArrayList<float[]> floatPool() {
          if (floatPool == null) {
              floatPool = new ArrayList<float[]>();
          }
          return floatPool;
      }
      
      private static ArrayList<boolean[]> boolPool() {
          if (boolPool == null) {
              boolPool = new ArrayList<boolean[]>();
          }
          return boolPool;
      }
      
      private static ArrayList<Iterator> iteratorPool() {
          if (iteratorPool == null) {
              iteratorPool = new ArrayList<Iterator>();
          }
          return iteratorPool;
      }
      
      private static synchronized GeneralPath createPathFromPool() {
          if (!pathPool().isEmpty()) {
              GeneralPath out = pathPool.remove(pathPool.size()-1);
              out.reset();
              return out;
          }
          return new GeneralPath();
      }
      
      private static synchronized Rectangle createRectFromPool() {
          if (!rectPool().isEmpty()) {
              return rectPool.remove(rectPool.size()-1);
          }
          return new Rectangle();
      }
      
      private static synchronized float[] createFloatArrayFromPool(int size) {
          int len = floatPool().size();
          for (int i=0; i<len; i++) {
              float[] arr = floatPool.get(i);
              if (arr.length == size) {
                  return floatPool.remove(i);
              }
          }
          return new float[size];
      }
      
      
      private static synchronized boolean[] createBoolArrayFromPool(int size) {
          int len = boolPool().size();
          for (int i=0; i<len; i++) {
              boolean[] arr = boolPool.get(i);
              if (arr.length == size) {
                  return boolPool.remove(i);
              }
          }
          return new boolean[size];
      }
      
      private static synchronized Iterator createIteratorFromPool(GeneralPath p, Transform t) {
          if (!iteratorPool().isEmpty()) {
              Iterator it = iteratorPool.remove(iteratorPool.size()-1);
              it.p = p;
              it.transform = t;
              it.reset();
              return it;
          }
          return (Iterator)p.getPathIterator(t);
          
      }
      
      /**
       * Returns a GeneralPath to the reusable object pool for GeneralPaths.
       * @param p The path to recycle.
       * 
       * @see #createFromPool() 
       */
      public static synchronized void recycle(GeneralPath p) {
          if (pathPool().size() >= MAX_POOL_SIZE || p == null) return;
          pathPool.add(p);
      }
      
      private static synchronized void recycle(Rectangle r) {
          if (rectPool.size() >= MAX_POOL_SIZE || r == null) return;
          rectPool.add(r);
      }
      
      private static synchronized void recycle(float[] a) {
          if (floatPool().size() >= MAX_POOL_SIZE || a == null) return;
          floatPool.add(a);
      }
      
      
      private static synchronized void recycle(boolean[] b) {
          if (boolPool().size() >= MAX_POOL_SIZE || b == null) {
              return;
          }
          boolPool.add(b);
      }
      
      private static synchronized void recycle(Iterator it) {
          if (iteratorPool().size() >= MAX_POOL_SIZE || it == null) {
              return;
          }
          iteratorPool.add(it);
      }
      
      /**
       * Creates a new GeneralPath from an object Pool.  This is useful
       * if you need to create a temporary General path that you wish
       * to dispose of after using.  
       * 
       * <p>You should return this object back to the pool when you are done
       * using the {@link #recycle(com.codename1.ui.geom.GeneralPath) } method.
       * @return 
       */
      public static GeneralPath createFromPool() {
          return createPathFromPool();
      }
      
      
      
      private boolean dirty = false;
  
      // END Alpha Mask Caching Functionality
      //--------------------------------------------------------------------------
      /**
       * Same constant as {@link PathIterator#WIND_EVEN_ODD}
       */
      public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
      /**
       * Same constant as {@link PathIterator#WIND_NON_ZERO}
       */
      public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
  
      /**
       * The buffers size
       */
      private static final int BUFFER_SIZE = 10;
  
      /**
       * The buffers capacity
       */
      private static final int BUFFER_CAPACITY = 10;
  
      // These are package protected for ODD
      
      /**
       * The point's types buffer
       */
      byte[] types;
  
      /**
       * The points buffer
       */
      float[] points;
  
      /**
       * The point's type buffer size
       */
      int typeSize;
  
      /**
       * The points buffer size
       */
      int pointSize;
  
      /**
       * The path rule
       */
      int rule;
      
      
  
      /**
       * The space amount in points buffer for different segmenet's types
       */
      private static int pointShift[] = {
          2, // MOVETO
          2, // LINETO
          4, // QUADTO
          6, // CUBICTO
          0}; // CLOSE
  
      /*
       * GeneralPath path iterator 
       */
      private class Iterator implements PathIterator {
  
          /**
           * The current cursor position in types buffer
           */
          int typeIndex;
  
          /**
           * The current cursor position in points buffer
           */
          int pointIndex;
  
          /**
           * The source GeneralPath object
           */
          GeneralPath p;
  
          Transform transform;
  
          /**
           * Constructs a new GeneralPath.Iterator for given general path
           *
           * @param path - the source GeneralPath object
           */
          Iterator(GeneralPath path) {
              this.p = path;
  
          }
          
          private void reset() {
              typeIndex = 0;
              pointIndex = 0;
          }
  
          public int getWindingRule() {
              return p.getWindingRule();
          }
  
          public boolean isDone() {
              return typeIndex >= p.typeSize;
          }
  
          public void next() {
              typeIndex++;
          }
  
          private void transformSegmentInPlace() {
              if (isDone()) {
                  // awt.4B=Iterator out of bounds
                  throw new IndexOutOfBoundsException("Path done"); //$NON-NLS-1$
              }
              if (transform == null) {
                  return;
              }
              int type = p.types[typeIndex];
              int count = GeneralPath.pointShift[type];
              for (int i=0; i < count; i+=2) {
                  buf[0] = p.points[pointIndex + i];
                  buf[1] = p.points[pointIndex + i + 1];
                  transform.transformPoint(buf, buf);
                  p.points[pointIndex+i] = buf[0];
                  p.points[pointIndex+i+1] = buf[1];
              }
          }
          
          
          public int currentSegment(double[] coords) {
              float[] fcoords = createFloatArrayFromPool(6);
              try {
                  int res = currentSegment(fcoords);
                  int type = p.types[typeIndex];
                  int count = GeneralPath.pointShift[type];
              
                  for (int i=0; i< count; i ++) {
                      coords[i] = fcoords[i];
                  }
                  return res;
              } finally {
                  recycle(fcoords);
              }
          }
  
          private float[] buf = new float[2];
  
          public int currentSegment(float[] coords) {
              if (isDone()) {
                  // awt.4B=Iterator out of bounds
                  throw new IndexOutOfBoundsException("Path done"); //$NON-NLS-1$
              }
              int type = p.types[typeIndex];
              int count = GeneralPath.pointShift[type];
              if (transform == null) {
                  System.arraycopy(p.points, pointIndex, coords, 0, count);
              } else {
                  transform.transformPoints(2, p.points, pointIndex, coords, 0, count / 2);
              }
              pointIndex += count;
              return type;
          }
  
      }
  
      /**
       * Constructs a GeneralPath with the default ({@link #WIND_NON_ZERO})
       * winding rule and initial capacity (10).
       */
      public GeneralPath() {
          this(WIND_NON_ZERO, BUFFER_SIZE);
      }
  
      /**
       * Constructs a GeneralPath with a specific winding rule and the default
       * initial capacity (10).
       *
       * @param rule The winding rule. One of {@link #WIND_NON_ZERO} and
       * {@link #WIND_EVEN_ODD}
       * @see #WIND_NON_ZERO
       * @see #WIND_EVEN_ODD
       */
      public GeneralPath(int rule) {
          this(rule, BUFFER_SIZE);
      }
  
      /**
       * Constructs a GeneralPath with a specific winding rule and the initial
       * capacity. The initial capacity should be the approximate number of path
       * segments to be used.
       *
       * @param rule The winding rule. ({@link #WIND_NON_ZERO} or
       * {@link #WIND_EVEN_ODD}).
       * @param initialCapacity the inital capacity, in path segments
       */
      public GeneralPath(int rule, int initialCapacity) {
          setWindingRule(rule);
          types = new byte[initialCapacity];
          points = new float[initialCapacity * 2];
      }
      
      /**
       * Checks to see if this path forms a polygon.
       * @return True if the path is a polygon.
       */
      public boolean isPolygon() {
          
          if (isRectangle()) {
              return true;
          }
          Iterator it = createIteratorFromPool(this, null);
          float[] curr = createFloatArrayFromPool(6);
          float[] firstPoint = createFloatArrayFromPool(2);
          try {
              boolean firstMove = false;
              int cmd = -1;
              while (!it.isDone()) {
                  switch (cmd = it.currentSegment(curr)) {
                      case PathIterator.SEG_MOVETO: {
                          if (firstMove) {
                              return false;
                          }
                          firstMove = true;
                          firstPoint[0] = curr[0];
                          firstPoint[1] = curr[1];
                          break;
                      }
                      case PathIterator.SEG_CUBICTO:
                      case PathIterator.SEG_QUADTO:
                      return false;
                  }
                  it.next();
              }
              
              return cmd == PathIterator.SEG_CLOSE || (curr[0] == firstPoint[0] && curr[1] == firstPoint[1]);
              
          } finally {
              recycle(it);
              recycle(curr);
              recycle(firstPoint);
          }
          
      }
      
      /**
       * Returns the number of path commands in this path.
       * @return The number of path commands in this path.
       */
      public int getTypesSize() {
          return typeSize;
      }
      
      /**
       * Returns the number of points in this path.
       * @return The number of points in this path.
       */
      public int getPointsSize() {
          return pointSize;
      }
      
      /**
       * Returns a copy of the types (aka path commands) in this path.
       * @param out An array to copy the path commands into.
       */
      public void getTypes(byte[] out) {
          System.arraycopy(types, 0, out, 0, Math.min(types.length, out.length));
      }
      
      /**
       * Returns a copy of the points in this path.
       * @param out An array to copy the points into.
       */
      public void getPoints(float[] out) {
          System.arraycopy(points, 0, out, 0, Math.min(points.length, out.length));
      }
  
      /**
       * Constructs a GeneralPath from an arbitrary shape object. The Shapes
       * PathIterator path and winding rule will be used.
       *
       * @param shape
       */
      public GeneralPath(Shape shape) {
          this(WIND_NON_ZERO, BUFFER_SIZE);
          if (shape.getClass() == GeneralPath.class) {
              setPath((GeneralPath)shape, null);
          } else {
              PathIterator p = shape.getPathIterator();
              setWindingRule(p.getWindingRule());
              append(p, false);
          }
      }
      
      public String toString(){
          StringBuilder sb = new StringBuilder();
          sb.append("[General Path: ");
          Iterator it = createIteratorFromPool(this, null);
          float[] buf = createFloatArrayFromPool(6);//new float[6];
          try {
              while (!it.isDone() ){
                  int type = it.currentSegment(buf);
                  switch ( type ){
                      case PathIterator.SEG_MOVETO:
                          sb.append("Move ("+buf[0]+","+buf[1]+"), ");
                          break;
                      case PathIterator.SEG_LINETO:
                          sb.append("Line ("+buf[0]+","+buf[1]+"), ");
                          break;
                      case PathIterator.SEG_CUBICTO:
                          sb.append("Curve ("+buf[0]+","+buf[1]+".."+buf[2]+","+buf[3]+".."+buf[4]+","+buf[5]+")");
                          break;
                      case PathIterator.SEG_QUADTO:
                          sb.append("Curve ("+buf[0]+","+buf[1]+".."+buf[2]+","+buf[3]+")");
                          break;
                      case PathIterator.SEG_CLOSE:
                          sb.append(" CLOSE]");
                          break;
                  }
                  it.next();
              }
          } finally {
              recycle(buf);
              recycle(it);
          }
          return sb.toString();
      }
  
      /**
       * Sets the path's winding rule, which controls which areas are considered
       * 'inside' or 'outside' the path on drawing. Valid rules are
       * {@link #WIND_EVEN_ODD} for an even-odd winding rule, or
       * {@link #WIND_NON_ZERO} for a non-zero winding rule.
       *
       * @param rule the rule. ({@link #WIND_NON_ZERO} or {@link #WIND_EVEN_ODD}).
       */
      public void setWindingRule(int rule) {
          if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
              // awt.209=Invalid winding rule value
              throw new java.lang.IllegalArgumentException("Invalid winding rule"); //$NON-NLS-1$
          }
          dirty = true;
          this.rule = rule;
      }
      
      
      public boolean equals(Shape shape, Transform t) {
          if (t != null && !t.isIdentity()) {
              GeneralPath p = createPathFromPool();
              p.setShape(shape, t);
              try {
                  return equals(p, (Transform)null);
              } finally {
                  recycle(p);
              }
          }
          if (shape == this) return true;
          if (shape instanceof Rectangle) {
              Rectangle r = (Rectangle)shape;
              Rectangle tmpRect = createRectFromPool();
              try {
                  getBounds(tmpRect);
                  return r.equals(tmpRect);
              } finally {
                  recycle(tmpRect);
              }
          } else if (shape instanceof GeneralPath) {
              GeneralPath tmpPath = (GeneralPath)shape;
              return Arrays.equals(points, tmpPath.points) && Arrays.equals(types, tmpPath.types);
          } else {
              GeneralPath tmpPath2 = createPathFromPool();
              try {
                  tmpPath2.setShape(shape, null);
                  return equals(tmpPath2, (Transform)null);
              } finally {
                  recycle(tmpPath2);
              }
          }
      }
  
      /**
       * Returns the path's current winding rule.
       *
       * @return {@link #WIND_NON_ZERO} or {@link #WIND_EVEN_ODD}
       */
      public int getWindingRule() {
          return rule;
      }
  
      /**
       * Checks points and types buffer size to add pointCount points. If
       * necessary realloc buffers to enlarge size.
       *
       * @param pointCount - the point count to be added in buffer
       */
      private void checkBuf(int pointCount, boolean checkMove) {
          if (checkMove && typeSize == 0) {
              // awt.20A=First segment should be SEG_MOVETO type
              throw new IndexOutOfBoundsException("First segment must be a moveto"); //$NON-NLS-1$
          }
          if (typeSize == types.length) {
              byte tmp[] = new byte[typeSize + BUFFER_CAPACITY];
              System.arraycopy(types, 0, tmp, 0, typeSize);
              types = tmp;
          }
          if (pointSize + pointCount > points.length) {
              float tmp[] = new float[pointSize + Math.max(BUFFER_CAPACITY * 2, pointCount)];
              System.arraycopy(points, 0, tmp, 0, pointSize);
              points = tmp;
          }
      }
      
  
      public void moveTo(double x, double y){
          moveTo((float)x, (float)y);
      }
      
      /**
       * Adds a new point to a path.
       *
       * @param x the x-coordinate.
       * @param y the y-coordinate.
       */
      public void moveTo(float x, float y) {
          if (typeSize > 0 && types[typeSize - 1] == PathIterator.SEG_MOVETO) {
              points[pointSize - 2] = x;
              points[pointSize - 1] = y;
          } else {
              checkBuf(2, false);
              types[typeSize++] = PathIterator.SEG_MOVETO;
              points[pointSize++] = x;
              points[pointSize++] = y;
          }
          dirty = true;
      }
  
      
      public void lineTo(double x, double y){
          lineTo((float)x, (float)y);
      }
      /**
       * Appends a straight line to the current path.
       *
       * @param x x coordinate of the line endpoint.
       * @param y y coordinate of the line endpoint.
       */
      public void lineTo(float x, float y) {
          checkBuf(2, true);
          types[typeSize++] = PathIterator.SEG_LINETO;
          points[pointSize++] = x;
          points[pointSize++] = y;
          dirty = true;
      }
  
      public void quadTo(double x1, double y1, double x2, double y2){
          quadTo((float)x1, (float)y1, (float)x2, (float)y2);
      }
      
      /**
       * Appends a quadratic Bezier curve to the current path.
       *
       * @param x1 x coordinate of the control point
       * @param y1 y coordinate of the control point
       * @param x2 x coordinate of the curve endpoint.
       * @param y2 y coordinate of the curve endpoint.
       */
      public void quadTo(float x1, float y1, float x2, float y2) {
          checkBuf(4, true);
          types[typeSize++] = PathIterator.SEG_QUADTO;
          points[pointSize++] = x1;
          points[pointSize++] = y1;
          points[pointSize++] = x2;
          points[pointSize++] = y2;
          dirty = true;
      }
  
      public void curveTo(double x1, double y1, double x2, double y2, double x3, double y3){
          curveTo((float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3);
      }
      
      /**
       * Appends a cubic Bezier curve to the current path.
       *
       * @param x1 x coordinate of the first control point
       * @param y1 y coordinate of the first control point
       * @param x2 x coordinate of the second control point
       * @param y2 y coordinate of the second control point
       * @param x3 x coordinate of the curve endpoint.
       * @param y3 y coordinate of the curve endpoint.
       */
      public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
          checkBuf(6, true);
          types[typeSize++] = PathIterator.SEG_CUBICTO;
          points[pointSize++] = x1;
          points[pointSize++] = y1;
          points[pointSize++] = x2;
          points[pointSize++] = y2;
          points[pointSize++] = x3;
          points[pointSize++] = y3;
          dirty = true;
      }
      
      /**
       * Draws an elliptical arc on the path given the provided bounds.
       * @param x Left x coord of bounding rect.
       * @param y Top y coordof bounding rect.
       * @param w Width of bounding rect.
       * @param h Height of bounding rect.
       * @param startAngle Start angle on ellipse in radians.  Counter-clockwise from 3-o'clock.
       * @param sweepAngle Sweep angle in radians. Counter-clockwise.
       */
      public void arc(float x, float y, float w, float h, float startAngle, float sweepAngle) {
          
          arc(x, y, w, h, startAngle, sweepAngle, false);
      }
      
      /**
       * Draws an elliptical arc on the path given the provided bounds.
       * @param x Left x coord of bounding rect.
       * @param y Top y coordof bounding rect.
       * @param w Width of bounding rect.
       * @param h Height of bounding rect.
       * @param startAngle Start angle on ellipse in radians.  Counter-clockwise from 3-o'clock.
       * @param sweepAngle Sweep angle in radians. Counter-clockwise.
       * @param joinPath If true, then this will join the arc to the existing path with a line.
       */
      public void arc(float x, float y, float w, float h, float startAngle, float sweepAngle, boolean joinPath) {
          
          Ellipse e = new Ellipse();
          Ellipse.initWithBounds(e, x, y, w, h);
          e.addToPath(this, -startAngle, -sweepAngle, joinPath);
      }
      
      
      /**
       * Draws an elliptical arc on the path given the provided bounds.
       * @param x Left x coord of bounding rect.
       * @param y Top y coordof bounding rect.
       * @param w Width of bounding rect.
       * @param h Height of bounding rect.
       * @param startAngle Start angle on ellipse in radians.  Counter-clockwise from 3-o'clock.
       * @param sweepAngle Sweep angle in radians. Counter-clockwise.
       */
      public void arc(double x, double y, double w, double h, double startAngle, double sweepAngle) {
          arc(x, y, w, h, startAngle, sweepAngle, false);
      }
      
      
      /**
       * Draws an elliptical arc on the path given the provided bounds.
       * @param x Left x coord of bounding rect.
       * @param y Top y coordof bounding rect.
       * @param w Width of bounding rect.
       * @param h Height of bounding rect.
       * @param startAngle Start angle on ellipse in radians.  Counter-clockwise from 3-o'clock.
       * @param sweepAngle Sweep angle in radians. Counter-clockwise.
       * @param joinPath If true then this will join the arc to the existing path with a line.
       */
      public void arc(double x, double y, double w, double h, double startAngle, double sweepAngle, boolean joinPath) {
          arc((float)x, (float)y, (float)w, (float)h, (float)startAngle, (float)sweepAngle, joinPath);
      }
      
      
  //    private static void addBezierArcToPath(GeneralPath path, double cx, double cy,
  //                                          double startX, double startY, double endX, double endY) {
  //        addBezierArcToPath(path, cx, cy, startX, startY, endX, endY, false);
  //    }
      
      
      /**
       * 
       * @param path
       * @param cx
       * @param cy
       * @param startX
       * @param startY
       * @param endX
       * @param endY
       * @param clockwise 
       */
  //    private static void addBezierArcToPath(GeneralPath path, double cx, double cy,
  //                                          double startX, double startY, double endX, double endY, boolean clockwise) {
  //        if ( startX != endX || startY != endY ){
  //            double ax = startX - cx;
  //            double ay = startY - cy;
  //            double bx = endX - cx;
  //            double by = endY- cy;
  //            
  //            final double r1s = ax * ax + ay * ay;
  //            final double r2s = bx * bx + by * by;
  //            double ellipseScaleY = 0;
  //            if (Math.abs(r1s - r2s) > 2) {
  //                // This is not a circle
  //                // Let's get the arc for the circle
  //                ellipseScaleY = Math.sqrt(((ax*ax) - (bx*bx)) / (by*by - ay*ay));
  //                startY = cy + ellipseScaleY * (startY-cy);
  //                endY = cy + ellipseScaleY * (endY-cy);
  //                
  //                ay = startY - cy;
  //                by = endY - cy;
  //            } else {
  //                double startAngle = MathUtil.atan2(ay, ax);
  //                double endAngle = MathUtil.atan2(by, bx);
  //                
  //                double dist = Math.abs(endAngle - startAngle);
  //                if (clockwise) {
  //                    if (startAngle > endAngle) {
  //                        dist = Math.PI*2-dist;
  //                    }
  //                } else {
  //                    if (startAngle < endAngle) {
  //                        dist = Math.PI*2-dist;
  //                    }
  //                }
  //                
  //                //System.out.println("dist: "+dist+" startAngle: "+startAngle+" endAngle: "+endAngle);
  //                if (dist > Math.PI/3) {
  //                    // We bisect
  //                    double r = Math.sqrt(r1s);
  //                    double bisectAngle = (startAngle + endAngle)/2;
  //                    if (clockwise) {
  //                        if (startAngle > endAngle) {
  //                            bisectAngle += Math.PI;
  //                        }
  //                    } else {
  //                        if (startAngle < endAngle) {
  //                            bisectAngle += Math.PI;
  //                        }
  //                    }
  //                    double bisectX = cx + r * Math.cos(bisectAngle);
  //                    double bisectY = cy + r * Math.sin(bisectAngle);
  //                    addBezierArcToPath(path, cx, cy, startX, startY, bisectX, bisectY, clockwise);
  //                    addBezierArcToPath(path, cx, cy, bisectX, bisectY, endX, endY, clockwise);
  //                    return;
  //                }
  //                
  //                
  //            }
  //            
  //            final double q1 = r1s;//ax * ax + ay * ay;
  //            final double q2 = q1 + ax * bx + ay * by;
  //            final double k2 = 4d / 3d * (Math.sqrt(2d * q1 * q2) - q2) / (ax * by - ay * bx);
  //            final float x2 = (float)(cx + ax - k2 * ay);
  //            float y2 = (float)(cy + ay + k2 * ax);
  //            final float x3 = (float)(cx + bx + k2 * by);
  //             float y3 = (float)(cy + by - k2 * bx);
  //            if (ellipseScaleY != 0) {
  //                y2 = (float)(cy + (y2-cy)/ellipseScaleY);
  //                y3 = (float)(cy + (y3-cy)/ellipseScaleY);
  //                endY = (float)(cy + (endY-cy)/ellipseScaleY);
  //            }
  //            path.curveTo(x2, y2, x3, y3, endX, endY);
  //            
  //        } 
  //    }
      
      
      static class Ellipse {
          private double a;
          private double b;
          private double cx;
          private double cy;
          private EPoint _tmp1=new EPoint();
          
          static void initWithBounds(Ellipse e, double x, double y, double w, double h) {
              e.cx = x+w/2;
              e.cy = y+h/2;
              e.a = w/2;
              e.b = h/2;
          }
          
          static void initWithPerimeterPoints(Ellipse e, double cx, double cy, double p1x, double p1y, double p2x, double p2y) {
  
              /*
              e.cx = cx;
              e.cy = cy;
              double x1 = p1x-cx;
              double y1 = p1y-cy;
              double x2 = p2x-cx;
              double y2 = p2y-cy;
              double x1s = x1*x1;
              double x2s = x2*x2;
              double y1s = y1*y1;
              double y2s = y2*y2;
              if (Math.abs(x1s-x2s) < 0.001 ||Math.abs(y1s-y2s) < 0.001) {
                  a = b = Math.max(Math.sqrt(Math.abs(y2)));
              }
              if (Math.abs(x1s-x2s) > 0.001) {
                  e.b = Math.sqrt((x1s*y2s-x2s-y1s)/(x1s-x2s));
                  double bs = e.b*e.b;
                  e.a = Math.sqrt(x1s*bs/(bs-y1s));
              } else {
                  e.a = Math.sqrt((y1s*x2s-y2s-x1s)/(y1s-y2s));
                  double as = e.a*e.a;
                  e.b = Math.sqrt(y1s*as/(as-x1s));
              }
              */
              
          }
          
          @Override
          public String toString() {
              
              return "Ellipse center=("+cx+","+cy+") a="+a+", b="+b+")";
          }
          
          void getPointAtAngle(double theta, EPoint out) {
              double tanTheta = Math.tan(theta);
              double tanThetas = tanTheta*tanTheta;
              double bs = b*b;
              double as = a*a;
              double x = a*b/Math.sqrt(bs+as*tanThetas);
              if (Math.cos(theta)<0) {
                  x = -x;
              }
              double y = a*b/Math.sqrt(as+bs/tanThetas);
              if (Math.sin(theta)<0) {
                  y = -y;
              }
              out.x = x + cx;
              out.y = y + cy;
          }
          
          double getAngleAtPoint(double px, double py) {
              px -= cx;
              py -= cy;
              
              return MathUtil.atan2(py, px);
          }
          
          void addToPath(GeneralPath p, double startAngle, double sweepAngle, boolean join) {
              getPointAtAngle(startAngle, _tmp1);
              if (join) {
                  
                  p.lineTo(_tmp1.x, _tmp1.y);
              } else {
                  p.moveTo(_tmp1.x, _tmp1.y);
              }
              _addToPath(p, startAngle, sweepAngle);
              if (!join && Math.abs(Math.abs(sweepAngle)-Math.PI*2) < 0.001) {
                  p.closePath();
              }
          }
          
          private void _addToPath(GeneralPath p, double startAngle, double sweepAngle) {
              //double _2pi = Math.PI*2;
              double absSweepAngle = Math.abs(sweepAngle);
              if (absSweepAngle < 0.0001) {
                  // Basically zero sweep angle so we won't draw anytything here.
                  // IOS seemed to choke when we tried to draw too small an arc
                  return;
              }
              if (absSweepAngle > Math.PI/4) {
                  //double halfAngle = sweepAngle/2;
                  double diff = Math.PI/4;
                  if (sweepAngle < 0) {
                      diff = -diff;
                  }
                  _addToPath(p, startAngle, diff);
                  _addToPath(p, startAngle+diff, sweepAngle-diff);
              } else {
                  getPointAtAngle(startAngle+sweepAngle, _tmp1);
                  //System.out.println("Line to "+_tmp1.x+", "+_tmp1.y);
                  EPoint controlPoint = new EPoint();
                  calculateBezierControlPoint(startAngle, sweepAngle, controlPoint);
                  p.quadTo(controlPoint.x, controlPoint.y, _tmp1.x, _tmp1.y);
                  //p.lineTo(_tmp1.x, _tmp1.y);
              }
          }
          
          private void calculateBezierControlPoint(double startAngle, double sweepAngle, EPoint point) {
              EPoint p1 = new EPoint();
              
              getPointAtAngle(startAngle, p1);
              p1.x-= cx;
              p1.y -= cy;
              
              EPoint p2 = new EPoint();
              getPointAtAngle(startAngle+sweepAngle, p2);
              p2.x -= cx;
              p2.y -= cy;
              
              //System.out.println("p1: "+p1.x+", "+p1.y+", p2:"+p2.x+","+p2.y);
              double x1s = p1.x*p1.x;
              double y1s = p1.y*p1.y;
              double x2s = p2.x*p2.x;
              double y2s = p2.y*p2.y;
              
              double as = a*a;
              double bs = b*b;
              //point.x = (x2s*bs/(p2.y*as) + p2.y - x1s*bs/(p1.y*as) - p1.y) / (-p1.x*bs/(p1.y*as) + p2.x*bs/(p2.y*as));
              //point.y = (-p1.x*bs/(p1.y*as))*point.x + x1s*bs/(p1.y*as) + p1.y;
              
              
              point.x = -(p1.y*(-as*y2s-bs*x2s)+as*y1s*p2.y+bs*x1s*p2.y)/(bs*p2.x*p1.y-bs*p1.x*p2.y);
              point.y = (p1.x*(-as*y2s-bs*x2s)+as*p2.x*y1s+bs*x1s*p2.x)/(as*p2.x*p1.y-as*p1.x*p2.y);
              
              point.x += cx;
              point.y += cy;
              //System.out.println("control: "+point.x+","+point.y);
          }
      
          
      }
      
      static class EPoint {
          double x;
          double y;
      }
      
      /**
       * Adds a circular arc to the given path by approximating it through a cubic Bezier curve, splitting it if
       * necessary. The precision of the approximation can be adjusted through {@code pointsOnCircle} and
       * {@code overlapPoints} parameters.
       * <p>
       * <strong>Example:</strong> imagine an arc starting from 0? and sweeping 100? with a value of
       * {@code pointsOnCircle} equal to 12 (threshold -> 360? / 12 = 30?):
       * <ul>
       * <li>if {@code overlapPoints} is {@code true}, it will be split as following:
       * <ul>
       * <li>from 0? to 30? (sweep 30?)</li>
       * <li>from 30? to 60? (sweep 30?)</li>
       * <li>from 60? to 90? (sweep 30?)</li>
       * <li>from 90? to 100? (sweep 10?)</li>
       * </ul>
       * </li>
       * <li>if {@code overlapPoints} is {@code false}, it will be split into 4 equal arcs:
       * <ul>
       * <li>from 0? to 25? (sweep 25?)</li>
       * <li>from 25? to 50? (sweep 25?)</li>
       * <li>from 50? to 75? (sweep 25?)</li>
       * <li>from 75? to 100? (sweep 25?)</li>
       * </ul>
       * </li>
       * </ul>
       * </p>
       * <p/>
       * For a technical explanation:
       * <a href="http://hansmuller-flex.blogspot.de/2011/10/more-about-approximating-circular-arcs.html">
       * http://hansmuller-flex.blogspot.de/2011/10/more-about-approximating-circular-arcs.html
       * </a>
       *
       * @param center            The center of the circle.
       * @param radius            The radius of the circle.
       * @param startAngleRadians The starting angle on the circle (in radians).
       * @param sweepAngleRadians How long to make the total arc (in radians).
       * @param pointsOnCircle    Defines a <i>threshold</i> (360? /{@code pointsOnCircle}) to split the Bezier arc to
       *                          better approximate a circular arc, depending also on the value of {@code overlapPoints}.
       *                          The suggested number to have a reasonable approximation of a circle is at least 4 (90?).
       *                          Less than 1 will be ignored (the arc will not be split).
       * @param overlapPoints     Given the <i>threshold</i> defined through {@code pointsOnCircle}:
       *                          <ul>
       *                          <li>if {@code true}, split the arc on every angle which is a multiple of the
       *                          <i>threshold</i> (yields better results if drawing precision is required,
       *                          especially when stacking multiple arcs, but can potentially use more points)</li>
       *                          <li>if {@code false}, split the arc equally so that each part is shorter than
       *                          the <i>threshold</i></li>
       *                          </ul>
       * @param addToPath         An existing path where to add the arc to, or {@code null} to create a new path.
       *
       *
       * @see #createBezierArcDegrees(android.graphics.PointF, float, float, float, int, boolean, android.graphics.Path)
       */
  //    private static void createBezierArcRadians(float cx, float cy, float radiusX, float radiusY, double startAngleRadians,
  //                                              double sweepAngleRadians, int pointsOnCircle, boolean overlapPoints,
  //                                              GeneralPath addToPath, boolean joinPath)
  //    {
  //        final GeneralPath path = addToPath;
  //        if (sweepAngleRadians == 0d) { return; }
  // 
  //        float radius = radiusX;
  //        float yScale = radiusY / radius;
  //        
  //        if (pointsOnCircle >= 1)
  //        {
  //            final double threshold = Math.PI * 2d / pointsOnCircle;
  //            if (Math.abs(sweepAngleRadians) > threshold)
  //            {
  //                double angle = normalizeRadians(startAngleRadians);
  //                //PointF end, start = pointFromAngleRadians(center, radius, angle);
  //                double endX, endY;
  //                double startX = cx + radius * Math.cos(angle);
  //                double startY = cy + radius * Math.sin(angle) * yScale;
  //                if (joinPath) {
  //                    path.lineTo(startX, startY);
  //                } else {
  //                    path.moveTo(startX, startY);
  //                }
  //                if (overlapPoints)
  //                {
  //                    final boolean cw = sweepAngleRadians > 0; // clockwise?
  //                    final double angleEnd = angle + sweepAngleRadians;
  //                    while (true)
  //                    {
  //                        double next = (cw ? Math.ceil(angle / threshold) : Math.floor(angle / threshold)) * threshold;
  //                        if (angle == next) { next += threshold * (cw ? 1d : -1d); }
  //                        final boolean isEnd = cw ? angleEnd <= next : angleEnd >= next;
  //                        //end = pointFromAngleRadians(center, radius, isEnd ? angleEnd : next);
  //                        endX = cx + radius * Math.cos(isEnd ? angleEnd : next);
  //                        endY = cy + radius * Math.sin(isEnd ? angleEnd : next) *yScale;
  //                        addBezierArcToPath(path, cx, cy, startX, startY, endX, endY);
  //                        if (isEnd) { break; }
  //                        angle = next;
  //                        startX = endX;
  //                        startY = endY;
  //                    }
  //                }
  //                else
  //                {
  //                    final int n = Math.abs((int)Math.ceil(sweepAngleRadians / threshold));
  //                    final double sweep = sweepAngleRadians / (double)n;
  //                    for (int i = 0;
  //                         i < n;
  //                         i++, startX = endX, startY = endY)
  //                    {
  //                        angle += sweep;
  //                        //end = pointFromAngleRadians(center, radius, angle);
  //                        endX = cx + radius * Math.cos(angle);
  //                        endY = cy + radius * Math.sin(angle) * yScale;
  //                        addBezierArcToPath(path, cx, cy, startX, startY, endX, endY);
  //                    }
  //                    
  //                }
  //                return;
  //            }
  //        }
  // 
  //        startAngleRadians = normalizeRadians(startAngleRadians);
  //        double startX = cx + radius * Math.cos(startAngleRadians);
  //        double startY = cy + radius * Math.sin(startAngleRadians) * yScale;
  //        
  //        double endX = cx + radius * Math.cos(startAngleRadians + sweepAngleRadians);
  //        double endY = cy + radius * Math.sin(startAngleRadians + sweepAngleRadians) * yScale;
  //        if (joinPath) {
  //            path.lineTo(startX, startY);
  //        } else {
  //            path.moveTo(startX, startY);
  //        }
  //        addBezierArcToPath(path, cx, cy, startX, startY, endX, endY);
  //        
  //    }
      
       /**
       * Normalize the input radians in the range 360? > x >= 0?.
       *
       * @param radians The angle to normalize (in radians).
       *
       * @return The angle normalized in the range 360? > x >= 0?.
       */
  //    private static double normalizeRadians(double radians)
  //    {
  //        double PI2 = Math.PI*2d;
  //        radians %= PI2;
  //        if (radians < 0d) { radians += PI2; }
  //        if (radians == PI2) { radians = 0d; }
  //        return radians;
  //    }
      
      
      /**
       * Adds an arc to the path.  This method uses an approximation of an arc using
       * a cubic path.  It is not a precise arc.
       * 
       * <p>Note:  The arc is drawn counter-clockwise around the center point.  See {@link #arcTo(double, double, double, double, boolean) } 
       * to draw clockwise.</p>
       * 
       * @param cX The x-coordinate of the oval center.
       * @param cY The y-coordinate of the oval center.
       * @param endX The end X coordinate.
       * @param endY The end Y coordinate.
       */
      public void arcTo(float cX, float cY, float endX, float endY) {
          arcTo(cX, cY, endX, endY, false);
      }
      /**
       * Adds an arc to the path.  This method uses an approximation of an arc using
       * a cubic path.  It is not a precise arc.
       * @param cX The x-coordinate of the oval center.
       * @param cY The y-coordinate of the oval center.
       * @param endX The end X coordinate.
       * @param endY The end Y coordinate.
       * @param clockwise If true, the arc is drawn clockwise around the center point.
       */
      public void arcTo(float cX, float cY, float endX, float endY, boolean clockwise){
          if ( pointSize < 2 ){
              throw new RuntimeException("Cannot add arc to path if it doesn't already have a starting point.");
              
          }
          float startX = points[pointSize-2];
          float startY = points[pointSize-1];
          
          float dx = endX-cX;
          float dy = endY-cY;
          double r2 = Math.sqrt(dx*dx+dy*dy);
          double dx1 = startX-cX;
          double dy1 = startY-cY;
          double r1 = Math.sqrt(dx1*dx1+dy1*dy1);
          if (Math.abs(r1-r2) > 1) {
              Log.e(new RuntimeException("arcTo() called with start and end points that don't lie on the same arc r1="+r1+", r2="+r2));
              
          }
          Ellipse e = new Ellipse();
          Ellipse.initWithBounds(e, cX-r2, cY-r2, r2*2, r2*2);
          double startAngle = e.getAngleAtPoint(startX, startY);
          double endAngle = e.getAngleAtPoint(endX, endY);
          double sweepAngle = endAngle-startAngle;
          if (clockwise && sweepAngle > 0) {
              sweepAngle = -sweepAngle;
          } else if (!clockwise && sweepAngle > 0) {
              sweepAngle = 2*Math.PI-sweepAngle;
          }
          
          arc(cX-r2, cY-r2, r2*2, r2*2, -startAngle, sweepAngle, true);
          lineTo(endX, endY);
      }
      
      /**
       * Adds an arc to the path.  This method uses an approximation of an arc using
       * a cubic path.  It is not a precise arc.
       * <p>Note:  The arc is drawn counter-clockwise around the center point.  See {@link #arcTo(double, double, double, double, boolean) } 
       * to draw clockwise.</p>
       * @param cX The x-coordinate of the oval center.
       * @param cY The y-coordinate of the oval center.
       * @param endX The end X coordinate.
       * @param endY The end Y coordinate.
       */
      public void arcTo(double cX, double cY, double endX, double endY) {
          arcTo(cX, cY, endX, endY, false);
      }
      
      /**
       * Adds an arc to the path.  This method uses an approximation of an arc using
       * a cubic path.  It is not a precise arc.
       * @param cX The x-coordinate of the oval center.
       * @param cY The y-coordinate of the oval center.
       * @param endX The end X coordinate.
       * @param endY The end Y coordinate.
       * @param clockwise If true, the arc is drawn clockwise around the center point.
       * 
       */
      public void arcTo(double cX, double cY, double endX, double endY, boolean clockwise){
          arcTo((float)cX, (float)cY, (float)endX, (float)endY, clockwise);
      }
  
      /**
       * Closes the current subpath by drawing a line back to the point of the
       * last moveTo, unless the path is already closed.
       */
      public void closePath() {
          if (typeSize == 0 || types[typeSize - 1] != PathIterator.SEG_CLOSE) {
              checkBuf(0, true);
              types[typeSize++] = PathIterator.SEG_CLOSE;
              dirty = true;
          }
      }
  
      /**
       * Appends the segments of a Shape to the path. If connect is
       * {@literal true}, the new path segments are connected to the existing one
       * with a line. The winding rule of the Shape is ignored.
       *
       * @param shape the shape (null not permitted).
       * @param connect whether to connect the new shape to the existing path.
       */
      public void append(Shape shape, boolean connect) {
          if (shape.getClass() == GeneralPath.class) {
              Iterator it = createIteratorFromPool((GeneralPath)shape, null);
              try {
                  append(it, connect);
              } finally {
                  recycle(it);
              }
          } else {
              PathIterator p = shape.getPathIterator();
              append(p, connect);
          }
          
          dirty = true;
      }
  
      /**
       * Appends the segments of a PathIterator to this GeneralPath. Optionally,
       * the initial {@link PathIterator#SEG_MOVETO} segment of the appended path
       * is changed into a {@link PathIterator#SEG_LINETO} segment.
       *
       * @param path the PathIterator specifying which segments shall be appended
       * (null not permitted).
       * @param connect {@literal true} for substituting the initial
       * {@link PathIterator#SEG_MOVETO} segment by a
       * {@link PathIterator#SEG_LINETO}, or false for not performing any
       * substitution. If this {@code GeneralPath} is currently empty, connect is
       * assumed to be {@literal false}, thus leaving the initial
       * {@link PathIterator#SEG_MOVETO} unchanged.
       */
      public void append(PathIterator path, boolean connect) {
          float coords[] = createFloatArrayFromPool(6);//new float[6];
          append(path, connect, coords);
          recycle(coords);
      }
      
      private void append(PathIterator path, boolean connect, float[] tmpCoordsBuf) {
          float coords[] = tmpCoordsBuf;
          while (!path.isDone()) {
              
              switch (path.currentSegment(coords)) {
                  case PathIterator.SEG_MOVETO:
                      if (!connect || typeSize == 0) {
                          moveTo(coords[0], coords[1]);
                          break;
                      }
                      if (types[typeSize - 1] != PathIterator.SEG_CLOSE
                              && points[pointSize - 2] == coords[0]
                              && points[pointSize - 1] == coords[1]) {
                          break;
                      }
                  // NO BREAK;
                  case PathIterator.SEG_LINETO:
                      lineTo(coords[0], coords[1]);
                      break;
                  case PathIterator.SEG_QUADTO:
                      quadTo(coords[0], coords[1], coords[2], coords[3]);
                      break;
                  case PathIterator.SEG_CUBICTO:
                      curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
                      break;
                  case PathIterator.SEG_CLOSE:
                      closePath();
                      break;
              }
              path.next();
              connect = false;
          }
          dirty = true;
      }
  
      /**
       * Returns the current appending point of the path.
       *
       * @return 2-element array of the form {@code [x,y]} representing {@code x}
       * and {@code y} coordinate of the current appending point of the path..
       */
      public float[] getCurrentPoint() {
          if (typeSize == 0) {
              return null;
          }
          float[] out = new float[2];
          getCurrentPoint(out);
          return out;
      }
      
      /**
       * Sets the coordinates of the given point to the current point in the path.
       *
       * @param point Out parameter.  Will be filled with the coords of the current point.
       */
      public void getCurrentPoint(float[] point) {
          if (typeSize == 0) {
              throw new RuntimeException("Cannot get point because the size of this command is 0");
          }
          int j = pointSize - 2;
          if (types[typeSize - 1] == PathIterator.SEG_CLOSE) {
  
              for (int i = typeSize - 2; i > 0; i--) {
                  int type = types[i];
                  if (type == PathIterator.SEG_MOVETO) {
                      break;
                  }
                  j -= pointShift[type];
              }
          }
          point[0] = points[j];
          point[1] = points[j+1];
          //return new float[]{points[j], points[j + 1]};
      }
  
      /**
       * Resets the path. All points and segments are destroyed.
       */
      public void reset() {
          typeSize = 0;
          pointSize = 0;
          dirty = true;
      }
  
      /**
       * Returns the path's bounding box, in float precision.
       *
       * @return 4-element array of the form {@code [x, y, width, height]}.
       */
      public float[] getBounds2D() {
          float[] out = new float[4];
          getBounds2D(out);
          return out;
      }
      
      /**
       * Sets the 4-element array to the bounding box coordinates of the path.  x, y, width, height.
       * @param out 4-element float[] array.
       */
      public void getBounds2D(float[] out) {
          float rx1, ry1, rx2, ry2;
          if (pointSize == 0) {
              rx1 = ry1 = rx2 = ry2 = 0.0f;
          } else {
              int i = pointSize - 1;
              ry1 = ry2 = points[i--];
              rx1 = rx2 = points[i--];
              while (i > 0) {
                  float y = points[i--];
                  float x = points[i--];
                  if (x < rx1) {
                      rx1 = x;
                  } else if (x > rx2) {
                      rx2 = x;
                  }
                  if (y < ry1) {
                      ry1 = y;
                  } else if (y > ry2) {
                      ry2 = y;
                  }
              }
          }
          out[0] = rx1;
          out[1] = ry1;
          out[2] = rx2-rx1;
          out[3] = ry2-ry1;
      }
  
      /**
       * Returns the path's bounding box.
       *
       * @return The bounding box of the path.
       */
      public Rectangle getBounds() {
          
          float[] r = getBounds2D();
          int x1 = (int)Math.floor(r[0]);
          int y1 = (int)Math.floor(r[1]);
          int x2 = (int)Math.ceil(r[0]+r[2]);
          int y2 = (int)Math.ceil(r[1]+r[3]);
          return new Rectangle(x1, y1, x2-x1, y2-y1);
          /*
          float[] r = getBounds2D();
          return new Rectangle((int)r[0], (int)r[1], (int)r[2], (int)r[3]);*/
  
      }
      
      /**
       * Sets the coordinates of the provided rectangle to the bounding box of this path.
       * @param out 
       */
      public void getBounds(Rectangle out) {
          float rx1, ry1, rx2, ry2;
          if (pointSize == 0) {
              rx1 = ry1 = rx2 = ry2 = 0.0f;
          } else {
              int i = pointSize - 1;
              ry1 = ry2 = points[i--];
              rx1 = rx2 = points[i--];
              while (i > 0) {
                  float y = points[i--];
                  float x = points[i--];
                  if (x < rx1) {
                      rx1 = x;
                  } else if (x > rx2) {
                      rx2 = x;
                  }
                  if (y < ry1) {
                      ry1 = y;
                  } else if (y > ry2) {
                      ry2 = y;
                  }
              }
          }
          int x1 = (int)Math.floor(rx1);
          int y1 = (int)Math.floor(ry1);
          int x2 = (int)Math.ceil(rx2);
          int y2 = (int)Math.ceil(ry2);
          out.setX(x1);
          out.setY(y1);
          out.setWidth(x2-x1);
          out.setHeight(y2-y1);
          
      }
      
      /**
       * Checks to see if this path is a rectangle.
       * @return True if this path forms a rectangle.  False otherwise.
       */
      public boolean isRectangle() {
          float[] tmpPointsBuf = createFloatArrayFromPool(6);
          boolean[] tmpCornersBuf = createBoolArrayFromPool(4);
          Iterator it = createIteratorFromPool(this, null);
          Rectangle bounds = createRectFromPool();
          try {
              getBounds(bounds);
              if (tmpPointsBuf.length != 6) {
                  throw new RuntimeException("points buffer must be length 6");
              }
              float[] buf = tmpPointsBuf;
              if (tmpCornersBuf.length != 4) {
                  throw new RuntimeException("corners buffer must be length 4");
              }
              boolean[] corners = tmpCornersBuf;
              int prevX = 0; 
              int prevY = 0;
              while ( !it.isDone() ){
                  int type = it.currentSegment(buf);
  
                  // Rectangulars only support moves, lines, and closes
                  if ( type != PathIterator.SEG_CLOSE && type != PathIterator.SEG_LINETO && type != PathIterator.SEG_MOVETO ){
                      return false;
                  }
  
  
                  // Get the current point
                  int x = (int)buf[0];
                  int y = (int)buf[1];
  
                  // Make sure there are no diagonal lines
                  if ( type == PathIterator.SEG_LINETO && !(x == prevX || y == prevY )){
                      return false;
                  }
  
                  // Make sure point is on the perimeter.
                  if ( x != bounds.getX() && y != bounds.getY() && x != bounds.getX()+bounds.getWidth() && y != bounds.getY()+bounds.getHeight() ){
                      return false;
                  }
  
                  // Make sure that all corners are accounted for.
                  for ( int i=0; i<4; i++){
                      if ( corners[i] ){
                          continue;
                      }
                      switch (i){
                          case 0:
                              corners[i] = (x == bounds.getX() && y == bounds.getY());
                              break;
                          case 1:
                              corners[i] = (x == bounds.getX()+bounds.getWidth() && y == bounds.getY());
                              break;
                          case 2:
                              corners[i] = (x == bounds.getX()+bounds.getWidth() && y == bounds.getY() + bounds.getHeight());
                              break;
                          case 3:
                              corners[i] = (x== bounds.getX() && y == bounds.getY()+bounds.getHeight());
                              break;
                      }
                  }
  
                  prevX = x;
                  prevY = y;
                  it.next();
              }
  
              return corners[0] && corners[1] && corners[2] && corners[3];
          } finally {
              recycle(tmpPointsBuf);
              recycle(tmpCornersBuf);
              recycle(it);
              recycle(bounds);
          }
      }
      
      
      /**
       * {{@inheritDoc}}
       */
      public PathIterator getPathIterator() {
          return new Iterator(this);
      }
  
      /**
       * {{@inheritDoc}}
       */
      public PathIterator getPathIterator(Transform m) {
          Iterator out = (Iterator) getPathIterator();
          out.transform = m;
          return out;
      }
      
      /**
       * Returns a shape formed by transforming the current shape with the provided
       * transform.  
       * <p>Note: If {@link com.codename1.ui.Transform#isSupported} is false, this may throw a RuntimeException.</p>
       * @param m The transform to be used to transform the shape.
       * @return The transformed shape.
       */
      public Shape createTransformedShape(Transform m){
          
          GeneralPath out = new GeneralPath();
          out.setPath(this, m);
          return out;
      }
      
      /**
       * Sets this path to be identical to the provided path {@code p} with the given
       * Transform {@code t} applied to it.
       * @param p The path to copy.
       * @param t The transform to apply to all points in the path.
       */
      public void setPath(GeneralPath p, Transform t) {
          dirty = true;
          typeSize = p.typeSize;
          pointSize = p.pointSize;
          rule = p.rule;
          if (points == null || points.length < pointSize) {
              points = new float[pointSize];
          }
          if (types == null || types.length < typeSize) {
              types = new byte[typeSize];
          }
          System.arraycopy(p.types, 0, types, 0, typeSize);
          if (t == null || t.isIdentity()) {
              System.arraycopy(p.points, 0, points, 0, pointSize);
              
          } else {
              t.transformPoints(2, p.points, 0, points, 0, pointSize / 2);
              
          }
          
      }
      
      /**
       * Sets this path to be a rectangle with the provided bounds, but with 
       * the given transform applied to it.
       * @param r Rectangle to copy.
       * @param t The transform to apply to the points in in {@code r}.
       */
      public void setRect(Rectangle r, Transform t) {
          reset();
          int x = r.getX();
          int y = r.getY();
          Dimension size = r.getSize();
          int w = size.getWidth();
          int h = size.getHeight();
  
          if (t == null) {
              moveTo(x, y);
              lineTo(x + w, y);
              lineTo(x + w, y + h);
              lineTo(x, y+ h);
              closePath();
          } else {
              float[] pointBuffer = createFloatArrayFromPool(6);
              try {
                  pointBuffer[0] = x;
                  pointBuffer[1] = y;
                  pointBuffer[2] = 0;
                  t.transformPoint(pointBuffer, pointBuffer);
                  moveTo(pointBuffer[0], pointBuffer[1]);
                  pointBuffer[0] = x+w;
                  pointBuffer[1] = y;
                  pointBuffer[2] = 0;
                  t.transformPoint(pointBuffer, pointBuffer);
                  lineTo(pointBuffer[0], pointBuffer[1]);
                  pointBuffer[0] = x+w;
                  pointBuffer[1] = y+h;
                  pointBuffer[2] = 0;
                  t.transformPoint(pointBuffer, pointBuffer);
                  lineTo(pointBuffer[0], pointBuffer[1]);
                  pointBuffer[0] = x;
                  pointBuffer[1] = y+h;
                  pointBuffer[2] = 0;
                  t.transformPoint(pointBuffer, pointBuffer);
                  lineTo(pointBuffer[0], pointBuffer[1]);
                  closePath();
              } finally {
                  recycle(pointBuffer);
              }
          }
      }
      
      /**
       * Sets this path to be a copy of the provided shape, but with the provided
       * transform applied to it.
       * @param s The shape to copy.
       * @param t The transform to apply to all points in the shape.
       */
      public void setShape(Shape s, Transform t) {
          if (s.getClass() == GeneralPath.class) {
              setPath((GeneralPath)s, t);
          } else if (s.getClass() == Rectangle.class) {
              setRect((Rectangle)s, t);
          } else {
              reset();
              append(s.getPathIterator(t), false);
          }
      }
      
      /**
       * Sets the current path to the intersection of itself and the provided rectangle.
       * @param rect The rectangle to intersect with this path.
       * @return True if {@code rect} intersects the current path.  False otherwise.  If there is no intersection, the
       * path will be reset to be empty.
       */
      public boolean intersect(Rectangle rect) {
          GeneralPath intersectionScratchPath = createPathFromPool();
          try {
              
              Shape result = ShapeUtil.intersection(rect, this, intersectionScratchPath);
              if (result != null) {
                  this.setPath(intersectionScratchPath, null);
                  return true;
              }
              reset();
              return false;
  
          } finally {
              recycle(intersectionScratchPath);
          }
      }
      
      public boolean intersect(int x, int y, int w, int h) {
          Rectangle r = createRectFromPool();
          try {
              r.setBounds(x, y, w, h);
              return intersect(r);
          } finally {
              recycle(r);
          }
      }
      
      /**
       * Transforms the current path in place using the given transform.
       * @param m The transform to apply to the path.
       */
      public void transform(Transform m) {
          if (m != null && !m.isIdentity()) {
              m.transformPoints(2, points, 0, points, 0, pointSize / 2 );
          }
      }
      
      /**
       * Resets this path to be the intersection of itself with the given shape.  Note that only 
       * {@link com.codename1.ui.geom.Rectangle}s are current supported.  If you pass any other
       * shape, it will throw a RuntimeException.
       * <p>Note: If {@link com.codename1.ui.TransformisSupported} is false, this will throw a Runtime Exception</p>
       * @param shape The shape to intersect with the current shape.
       */
      public void intersect(Shape shape) {
          //Log.p("Start intersect");
          if ( !(shape instanceof Rectangle) ){
              throw new RuntimeException("GeneralPath.intersect() only supports Rectangles");
          }
          intersect((Rectangle)shape);
      }
      
      /**
       * {{@inheritDoc}}
       */
      public Shape intersection(Rectangle rect){
          Shape out = ShapeUtil.intersection(rect, this);
          if (out == null) {
              return new Rectangle(rect.getX(), rect.getY(), 0, 0);
          }
          return out;
      }
      
      /**
       * Checks cross count according to path rule to define is it point inside shape or not. 
       * @param cross - the point cross count
       * @return true if point is inside path, or false otherwise 
       */
      boolean isInside(int cross) {
          if (rule == WIND_NON_ZERO) {
              return ShapeUtil.isInsideNonZero(cross);
          }
          return ShapeUtil.isInsideEvenOdd(cross);
      }
  
      /**
       * Checks if the given point is contained in the current shape.
       * @param x The x coordinate to check
       * @param y The y coordinate to check
       * @return True if the point is inside the shape.
       */
      public boolean contains(float x, float y) {
         return isInside(ShapeUtil.crossShape(this, x, y));
      }
      
      /**
       * {{@inheritDoc}}
       */
      public boolean contains(int x, int y){
          return contains((float)x, (float)y);
      }
  
      /**
       *
       * @author shannah
       */
      private static class ShapeUtil {
      
      
          
  
      /**
       * Generates the intersection of a given shape and a given rectangle.  Only supported convex polygons.
       *
       * @param r A rectangle.
       * @param s A shape
       * @return The shape that is the intersected area of the shape and
       * rectangle.
       */
      static Shape intersection(Rectangle r, Shape s) {
          return intersection(r, s, new GeneralPath());
      }
          
      
      private static Shape intersection(Rectangle r, Shape s, GeneralPath out) {
          
          Shape segmentedShape = segmentShape(r, s);
          Iterator it = createIteratorFromPool((GeneralPath)segmentedShape, null);
          //GeneralPath out = new GeneralPath();
          float[] buf = createFloatArrayFromPool(6);//new float[6];
          try {
              boolean started = false;
              float x1 = r.getX();
              float x2 = r.getX() + r.getWidth();
              float y1 = r.getY();
              float y2 = r.getY() + r.getHeight();
  
              float minX = -1;
              float minY = -1;
              float maxX = -1;
              float maxY = -1;
  
              float prevX=0; 
              float prevY=0;
  
              while (!it.isDone()) {
                  int type = it.currentSegment(buf);
  
                  switch (type) {
  
                      case PathIterator.SEG_CLOSE:
                          //System.out.println("Closing path");
                          out.closePath();
                          break;
  
                      case PathIterator.SEG_MOVETO:
                      case PathIterator.SEG_LINETO:
                          if (buf[0] < x1) {
                              buf[0] = x1;
                          } else if (buf[0] > x2) {
                              buf[0] = x2;
                          }
                          if (buf[1] < y1) {
                              buf[1] = y1;
                          } else if (buf[1] > y2) {
                              buf[1] = y2;
                          }
  
                          if (!started || (buf[0] < minX)) {
                              minX = buf[0];
                          }
                          if (!started || (buf[0] > maxX)) {
                              maxX = buf[0];
                          }
  
                          if (!started || (buf[1] < minY)) {
                              minY = buf[1];
                          }
                          if (!started || (buf[1] > maxY)) {
                              maxY = buf[1];
                          }
  
                          if (type == PathIterator.SEG_MOVETO) {
  
                              //System.out.println("Moving to "+buf[0]+","+buf[1]);
                              out.moveTo(buf[0], buf[1]);
                          } else { // type == PathITerator.SEG_LINETO
  
                              if ( prevX != buf[0] || prevY != buf[1]){
                                  //System.out.println("Line to "+buf[0]+","+buf[1]);
                                  out.lineTo(buf[0], buf[1]);
                              }
                          }
                          prevX = buf[0];
                          prevY = buf[1];
                          started = true;
                          //count++;
                          break;
                      default:
                          throw new RuntimeException("Intersection only supports polygons currently");
                  }
                  it.next();
  
              }
  
              if (maxX - minX <= 1f || maxY - minY <= 1f) {
                  return null;
              }
  
              return out;
          } finally {
              recycle(it);
              recycle(buf);
          }
  
      }
  
      /**
       * Segments a given shape so that all points of the shape that intersect the
       * provided rectangle edges are nodes of the shape path. This operation
       * makes it easier to form the intersection.
       * 
       * Only supports convex polygons.
       *
       * @param r A rectangle.
       * @param s A shape
       * @return A shape that is identical to the input shape except that it may
       * include additional path segments so that all points of intersection are
       * start/end points of a segment.
       */
      static Shape segmentShape(Rectangle r, Shape s) {
          return segmentShape(r, s, new GeneralPath());
      }
      private static GeneralPath segmentShape(Rectangle r, Shape s, GeneralPath out) {
          GeneralPath tmpGeneralPath = null;
          if (s.getClass() != GeneralPath.class) {
  