package org.xmind.ui.internal.svgsupport;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author Enki Xiong
*
*/
public class PathParser {
public final Pattern pathPattern = Pattern.compile(
"([MmLlHhVvAaQqTtCcSsZz])|([-+]?((\\d*\\.\\d+)|(\\d+))([eE][-+]?\\d+)?)"); //$NON-NLS-1$
private static PathParser instance = null;
private PathParser() {
}
public List<PathElement> parseSVGPath(String pathDefinitionString) {
final Matcher matchPathCmd = pathPattern.matcher(pathDefinitionString);
LinkedList<String> tokens = new LinkedList<String>();
List<PathElement> elements = new LinkedList<PathElement>();
PathLocationInfo info = new PathLocationInfo();
char curCmd = 'Z';
while (matchPathCmd.find()) {
String detailElement = matchPathCmd.group();
tokens.addLast(detailElement);
}
while (tokens.size() != 0) {
String curToken = tokens.removeFirst();
char initChar = curToken.charAt(0);
if ((initChar >= 'A' && initChar <= 'Z')
|| (initChar >= 'a' && initChar <= 'z'))
curCmd = initChar;
else
tokens.addFirst(curToken);
switch (curCmd) {
case 'M':
moveTo(elements, info, nextDouble(tokens), nextDouble(tokens),
true);
break;
case 'm':
moveTo(elements, info, nextDouble(tokens), nextDouble(tokens),
false);
break;
case 'L':
lineTo(elements, info, nextDouble(tokens), nextDouble(tokens),
true);
break;
case 'l':
lineTo(elements, info, nextDouble(tokens), nextDouble(tokens),
false);
break;
case 'H':
horizontalTo(elements, info, nextDouble(tokens), true);
break;
case 'h':
horizontalTo(elements, info, nextDouble(tokens), false);
break;
case 'V':
verticalTo(elements, info, nextDouble(tokens), true);
break;
case 'v':
verticalTo(elements, info, nextDouble(tokens), false);
break;
case 'A':
case 'a':
break;
case 'Q':
quadraticBelzierCurveTo(elements, info, nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), true);
break;
case 'q':
quadraticBelzierCurveTo(elements, info, nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), false);
break;
case 'T':
smoothQuadraticBelzierCurveTo(elements, info,
nextDouble(tokens), nextDouble(tokens), true);
break;
case 't':
smoothQuadraticBelzierCurveTo(elements, info,
nextDouble(tokens), nextDouble(tokens), false);
break;
case 'C':
curveTo(elements, info, nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens), true);
break;
case 'c':
curveTo(elements, info, nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens), false);
break;
case 'S':
smoothCurveTo(elements, info, nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), true);
break;
case 's':
smoothCurveTo(elements, info, nextDouble(tokens),
nextDouble(tokens), nextDouble(tokens),
nextDouble(tokens), false);
break;
case 'Z':
case 'z':
closePath(elements, info);
break;
default:
throw new RuntimeException("Invalid path element"); //$NON-NLS-1$
}
}
return elements;
}
private void closePath(List<PathElement> elements, PathLocationInfo info) {
info.setLastPointX(0.0);
info.setLastPointY(0.0);
info.setLastKnotX(0.0);
info.setLastKnotY(0.0);
elements.add(PathElement.getClosePathElement());
}
private void smoothCurveTo(List<PathElement> elements,
PathLocationInfo info, double x2, double y2, double x, double y,
boolean isAbsolute) {
if (!isAbsolute) {
x2 += info.getLastPointX();
y2 += info.getLastPointY();
x += info.getLastPointX();
y += info.getLastPointY();
}
curveTo(elements, info, info.getLastKnotX() * 2 - info.getLastPointX(),
info.getLastKnotY() * 2 - info.getLastPointY(), x2, y2, x, y,
true);
}
private void curveTo(List<PathElement> elements, PathLocationInfo info,
double x1, double y1, double x2, double y2, double x, double y,
boolean isAbsolute) {
if (!isAbsolute) {
x1 += info.getLastPointX();
x2 += info.getLastPointX();
x += info.getLastPointX();
y1 += info.getLastPointY();
y2 += info.getLastPointY();
y += info.getLastPointY();
}
elements.add(PathElement.getCurveElement(x1, y1, x2, y2, x, y));
info.setLastPointX(x);
info.setLastPointY(y);
info.setLastKnotX(x2);
info.setLastKnotY(y2);
}
private void smoothQuadraticBelzierCurveTo(List<PathElement> elements,
PathLocationInfo info, double x, double y, boolean isAbsolute) {
if (!isAbsolute) {
x += info.getLastPointX();
y += info.getLastPointY();
}
quadraticBelzierCurveTo(elements, info, info.getLastKnotX(),
info.getLastKnotY(), x, y, true);
}
private void quadraticBelzierCurveTo(List<PathElement> elements,
PathLocationInfo info, double x1, double y1, double x, double y,
boolean isAbsolute) {
if (!isAbsolute) {
x1 += info.getLastPointX();
y1 += info.getLastPointY();
x += info.getLastPointX();
y += info.getLastPointY();
}
elements.add(PathElement.getQuadraticBelzierCurveElement(x1, y1, x, y));
info.setLastPointX(x);
info.setLastPointY(y);
info.setLastKnotX(x1);
info.setLastKnotY(y1);
}
private void verticalTo(List<PathElement> elements, PathLocationInfo info,
double y, boolean isAbsolute) {
if (!isAbsolute)
y += info.getLastPointY();
lineTo(elements, info, info.getLastPointX(), y, true);
}
private void horizontalTo(List<PathElement> elements, PathLocationInfo info,
double x, boolean isAbsolute) {
if (!isAbsolute)
x += info.getLastPointX();
lineTo(elements, info, x, info.getLastPointY(), true);
}
private void lineTo(List<PathElement> elements, PathLocationInfo info,
double x, double y, boolean isAbsolute) {
if (!isAbsolute) {
x += info.getLastPointX();
y += info.getLastPointY();
}
elements.add(PathElement.getLineElement(x, y));
info.setLastPointX(x);
info.setLastPointY(y);
info.setLastKnotX(x);
info.setLastKnotY(y);
}
private void moveTo(List<PathElement> elements, PathLocationInfo info,
double x, double y, boolean isAbsolute) {
elements.add(PathElement.getMoveElement(x, y));
info.setLastPointX(x);
info.setLastPointY(y);
}
private double nextDouble(LinkedList<String> tokens) {
String token = tokens.removeFirst();
return Double.valueOf(token);
}
public static PathParser getInstance() {
if (instance == null) {
instance = new PathParser();
}
return instance;
}
}