package com.svgandroid;
import android.graphics.Path;
/**
* Created by Vlad Medvedev on 19.01.2016.
* vladislav.medvedev@devfactory.com
*/
public class PathParser {
/**
* Parses a single SVG path and returns it as a <code>android.graphics.Path</code> object.
* An example path is <code>M250,150L150,350L350,350Z</code>, which draws a triangle.
*
* @param pathString the SVG path, see the specification <a href="http://www.w3.org/TR/SVG/paths.html">here</a>.
* @param p the Path object contains the result of path parsing</a>.
*/
public static void parse(String pathString, Path p) {
int n = pathString.length();
ParserHelper ph = new ParserHelper(pathString, 0);
ph.skipWhitespace();
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 = pathString.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();
}
}
private static void drawArc(Path p, float lastX, float lastY, float x, float y, float rx, float ry, float theta, int largeArc, int sweepArc) {
// todo - not implemented yet, may be very hard to do using Android drawing facilities.
}
}