Example #1
0
  /**
   * 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)
   *
   * @param s the path string from the XML
   */
  public static Path doPath(String s) {
    int n = s.length();
    ParserHelper ph = new ParserHelper(s, 0);
    ph.skipWhitespace();
    Path p = new Path();
    float lastX = 0;
    float lastY = 0;
    float lastX1 = 0;
    float lastY1 = 0;
    float subPathStartX = 0;
    float subPathStartY = 0;
    char prevCmd = 0;
    while (ph.pos < n) {
      char cmd = s.charAt(ph.pos);
      switch (cmd) {
        case '-':
        case '+':
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          if (prevCmd == 'm' || prevCmd == 'M') {
            cmd = (char) (((int) prevCmd) - 1);
            break;
          } else if (prevCmd == 'c' || prevCmd == 'C') {
            cmd = prevCmd;
            break;
          } else if (prevCmd == 'l' || prevCmd == 'L') {
            cmd = prevCmd;
            break;
          }
        default:
          {
            ph.advance();
            prevCmd = cmd;
          }
      }

      boolean wasCurve = false;
      switch (cmd) {
        case 'M':
        case 'm':
          {
            float x = ph.nextFloat();
            float y = ph.nextFloat();
            if (cmd == 'm') {
              subPathStartX += x;
              subPathStartY += y;
              p.rMoveTo(x, y);
              lastX += x;
              lastY += y;
            } else {
              subPathStartX = x;
              subPathStartY = y;
              p.moveTo(x, y);
              lastX = x;
              lastY = y;
            }
            break;
          }
        case 'Z':
        case 'z':
          {
            p.close();
            p.moveTo(subPathStartX, subPathStartY);
            lastX = subPathStartX;
            lastY = subPathStartY;
            lastX1 = subPathStartX;
            lastY1 = subPathStartY;
            wasCurve = true;
            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();
            drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc);
            lastX = x;
            lastY = y;
            break;
          }
      }
      if (!wasCurve) {
        lastX1 = lastX;
        lastY1 = lastY;
      }
      ph.skipWhitespace();
    }
    return p;
  }