content: package com.github.siyamed.shapeimageview.path.parser;
  
  import android.graphics.Path;
  import android.graphics.RectF;
  import android.util.Log;
  
  class PathParser {
  
      private static final String TAG = SvgToPath.TAG;
  
      /*
       * This is where the hard-to-parse paths are handled.
       * Uppercase rules are absolute positions, lowercase are relative.
       * Types of path rules:
       * <p/>
       * <ol>
       * <li>M/m - (x y)+ - Move to (without drawing)
       * <li>Z/z - (no params) - Close path (back to starting point)
       * <li>L/l - (x y)+ - Line to
       * <li>H/h - x+ - Horizontal ine to
       * <li>V/v - y+ - Vertical line to
       * <li>C/c - (x1 y1 x2 y2 x y)+ - Cubic bezier to
       * <li>S/s - (x2 y2 x y)+ - Smooth cubic bezier to (shorthand that assumes the x2, y2 from previous C/S is the x1, y1 of this bezier)
       * <li>Q/q - (x1 y1 x y)+ - Quadratic bezier to
       * <li>T/t - (x y)+ - Smooth quadratic bezier to (assumes previous control point is "reflection" of last one w.r.t. to current point)
       * </ol>
       * <p/>
       * Numbers are separate by whitespace, comma or nothing at all (!) if they are self-delimiting, (ie. begin with a - sign)
       */
      public static Path doPath(String s) {
          int n = s.length();
          ParserHelper ph = new ParserHelper(s);
          ph.skipWhitespace();
          Path p = new Path();
          float lastX = 0;
          float lastY = 0;
          float lastX1 = 0;
          float lastY1 = 0;
          float contourInitialX = 0;
          float contourInitialY = 0;
          RectF r = new RectF();
          char cmd = 'x';
          while (ph.pos < n) {
              char next = s.charAt(ph.pos);
              if (!Character.isDigit(next) && !(next == '.') && !(next == '-')) {
                  cmd = next;
                  ph.advance();
              } else if (cmd == 'M') { // implied command
                  cmd = 'L';
              } else if (cmd == 'm') { // implied command
                  cmd = 'l';
              } else { // implied command
                  //ignore
              }
              p.computeBounds(r, true);
              // Log.d(TAG, "  " + cmd + " " + r);
              // Util.debug("* Commands remaining: '" + path + "'.");
              boolean wasCurve = false;
              switch (cmd) {
                  case 'M':
                  case 'm': {
                      float x = ph.nextFloat();
                      float y = ph.nextFloat();
                      if (cmd == 'm') {
                          p.rMoveTo(x, y);
                          lastX += x;
                          lastY += y;
                      } else {
                          p.moveTo(x, y);
                          lastX = x;
                          lastY = y;
                      }
                      contourInitialX = lastX;
                      contourInitialY = lastY;
                      break;
                  }
                  case 'Z':
                  case 'z': {
                      /// p.lineTo(contourInitialX, contourInitialY);
                      p.close();
                      lastX = contourInitialX;
                      lastY = contourInitialY;
                      break;
                  }
                  case 'L':
                  case 'l': {
                      float x = ph.nextFloat();
                      float y = ph.nextFloat();
                      if (cmd == 'l') {
                          p.rLineTo(x, y);
                          lastX += x;
                          lastY += y;
                      } else {
                          p.lineTo(x, y);
                          lastX = x;
                          lastY = y;
                      }
                      break;
                  }
                  case 'H':
                  case 'h': {
                      float x = ph.nextFloat();
                      if (cmd == 'h') {
                          p.rLineTo(x, 0);
                          lastX += x;
                      } else {
                          p.lineTo(x, lastY);
                          lastX = x;
                      }
                      break;
                  }
                  case 'V':
                  case 'v': {
                      float y = ph.nextFloat();
                      if (cmd == 'v') {
                          p.rLineTo(0, y);
                          lastY += y;
                      } else {
                          p.lineTo(lastX, y);
                          lastY = y;
                      }
                      break;
                  }
                  case 'C':
                  case 'c': {
                      wasCurve = true;
                      float x1 = ph.nextFloat();
                      float y1 = ph.nextFloat();
                      float x2 = ph.nextFloat();
                      float y2 = ph.nextFloat();
                      float x = ph.nextFloat();
                      float y = ph.nextFloat();
                      if (cmd == 'c') {
                          x1 += lastX;
                          x2 += lastX;
                          x += lastX;
                          y1 += lastY;
                          y2 += lastY;
                          y += lastY;
                      }
                      p.cubicTo(x1, y1, x2, y2, x, y);
                      lastX1 = x2;
                      lastY1 = y2;
                      lastX = x;
                      lastY = y;
                      break;
                  }
                  case 'S':
                  case 's': {
                      wasCurve = true;
                      float x2 = ph.nextFloat();
                      float y2 = ph.nextFloat();
                      float x = ph.nextFloat();
                      float y = ph.nextFloat();
                      if (cmd == 's') {
                          x2 += lastX;
                          x += lastX;
                          y2 += lastY;
                          y += lastY;
                      }
                      float x1 = 2 * lastX - lastX1;
                      float y1 = 2 * lastY - lastY1;
                      p.cubicTo(x1, y1, x2, y2, x, y);
                      lastX1 = x2;
                      lastY1 = y2;
                      lastX = x;
                      lastY = y;
                      break;
                  }
                  case 'A':
                  case 'a': {
                      float rx = ph.nextFloat();
                      float ry = ph.nextFloat();
                      float theta = ph.nextFloat();
                      int largeArc = (int) ph.nextFloat();
                      int sweepArc = (int) ph.nextFloat();
                      float x = ph.nextFloat();
                      float y = ph.nextFloat();
                      if (cmd == 'a') {
                          x += lastX;
                          y += lastY;
                      }
                      drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc == 1, sweepArc == 1);
                      lastX = x;
                      lastY = y;
                      break;
                  }
                  case 'T':
                  case 't': {
                      wasCurve = true;
                      float x = ph.nextFloat();
                      float y = ph.nextFloat();
                      if (cmd == 't') {
                          x += lastX;
                          y += lastY;
                      }
                      float x1 = 2 * lastX - lastX1;
                      float y1 = 2 * lastY - lastY1;
                      p.cubicTo( lastX, lastY, x1, y1, x, y );
                      lastX = x;
                      lastY = y;
                      lastX1 = x1;
                      lastY1 = y1;
                      break;
                  }
                  case 'Q':
                  case 'q': {
                      wasCurve = true;
                      float x1 = ph.nextFloat();
                      float y1 = ph.nextFloat();
                      float x = ph.nextFloat();
                      float y = ph.nextFloat();
                      if (cmd == 'q') {
                          x += lastX;
                          y += lastY;
                          x1 += lastX;
                          y1 += lastY;
                      }
                      p.cubicTo( lastX, lastY, x1, y1, x, y );
                      lastX1 = x1;
                      lastY1 = y1;
                      lastX = x;
                      lastY = y;
                      break;
                  }
                  default:
                      Log.w(TAG, "Invalid path command: " + cmd);
                      ph.advance();
              }
              if (!wasCurve) {
                  lastX1 = lastX;
                  lastY1 = lastY;
              }
              ph.skipWhitespace();
          }
          return p;
      }
  
      /*
       * Elliptical arc implementation based on the SVG specification notes
       * Adapted from the Batik library (Apache-2 license) by SAU
       */
      private static void drawArc(Path path, double x0, double y0, double x, double y, double rx,
                                  double ry, double angle, boolean largeArcFlag, boolean sweepFlag) {
          double dx2 = (x0 - x) / 2.0;
          double dy2 = (y0 - y) / 2.0;
          angle = Math.toRadians(angle % 360.0);
          double cosAngle = Math.cos(angle);
          double sinAngle = Math.sin(angle);
  
          double x1 = (cosAngle * dx2 + sinAngle * dy2);
          double y1 = (-sinAngle * dx2 + cosAngle * dy2);
          rx = Math.abs(rx);
          ry = Math.abs(ry);
  
          double Prx = rx * rx;
          double Pry = ry * ry;
          double Px1 = x1 * x1;
          double Py1 = y1 * y1;
  
          // check that radii are large enough
          double radiiCheck = Px1 / Prx + Py1 / Pry;
          if (radiiCheck > 1) {
              rx = Math.sqrt(radiiCheck) * rx;
              ry = Math.sqrt(radiiCheck) * ry;
              Prx = rx * rx;
              Pry = ry * ry;
          }
  
          // Step 2 : Compute (cx1, cy1)
          double sign = (largeArcFlag == sweepFlag) ? -1 : 1;
          double sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1))
                  / ((Prx * Py1) + (Pry * Px1));
          sq = (sq < 0) ? 0 : sq;
          double coef = (sign * Math.sqrt(sq));
          double cx1 = coef * ((rx * y1) / ry);
          double cy1 = coef * -((ry * x1) / rx);
  
          double sx2 = (x0 + x) / 2.0;
          double sy2 = (y0 + y) / 2.0;
          double cx = sx2 + (cosAngle * cx1 - sinAngle * cy1);
          double cy = sy2 + (sinAngle * cx1 + cosAngle * cy1);
  
          // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)
          double ux = (x1 - cx1) / rx;
          double uy = (y1 - cy1) / ry;
          double vx = (-x1 - cx1) / rx;
          double vy = (-y1 - cy1) / ry;
          double p, n;
  
          // Compute the angle start
          n = Math.sqrt((ux * ux) + (uy * uy));
          p = ux; // (1 * ux) + (0 * uy)
          sign = (uy < 0) ? -1.0 : 1.0;
          double angleStart = Math.toDegrees(sign * Math.acos(p / n));
  
          // Compute the angle extent
          n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
          p = ux * vx + uy * vy;
          sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
          double angleExtent = Math.toDegrees(sign * Math.acos(p / n));
          if (!sweepFlag && angleExtent > 0) {
              angleExtent -= 360f;
          } else if (sweepFlag && angleExtent < 0) {
              angleExtent += 360f;
          }
          angleExtent %= 360f;
          angleStart %= 360f;
  
          RectF oval = new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry));
          path.addArc(oval, (float) angleStart, (float) angleExtent);
      }
  
  }
  