parsePath function
Transform a path definition (value of 'd' attribute in SVG
Implementation
List<PathElement> parsePath(String pathStr) {
var startPoint = Offset.zero;
Tuple3<PathElement, String, Offset>? interpretCommand(
RegExp commandRegex, String input, Offset startPoint) {
var commandInterpretation = commandRegex.firstMatch(input);
if (commandInterpretation == null) return null;
var commandType = commandInterpretation.group(1)!;
var relativeCommand = commandType.toLowerCase() == commandType;
switch (commandType) {
case 'M':
case 'm':
var element = MoveElement(
startPoint: startPoint,
relative: relativeCommand,
moveParams: Offset(double.parse(commandInterpretation.group(2)!),
double.parse(commandInterpretation.group(3)!)));
startPoint = element.end;
var remainingPathStr = input.substring(commandInterpretation.end);
return Tuple3<PathElement, String, Offset>(
element, remainingPathStr, element.end);
case 'L':
case 'l':
var element = LineElement(
startPoint: startPoint,
relative: relativeCommand,
lineParams: Offset(double.parse(commandInterpretation.group(2)!),
double.parse(commandInterpretation.group(3)!)));
startPoint = element.end;
var remainingPathStr = input.substring(commandInterpretation.end);
return Tuple3<PathElement, String, Offset>(
element, remainingPathStr, element.end);
case 'c':
case 'C':
var element = CubicCurveElement(
startPoint: startPoint,
relative: relativeCommand,
firstControlPoint: Offset(
double.parse(commandInterpretation.group(2)!),
double.parse(commandInterpretation.group(3)!)),
secondControlPoint: Offset(
double.parse(commandInterpretation.group(4)!),
double.parse(commandInterpretation.group(5)!)),
endPoint: Offset(double.parse(commandInterpretation.group(6)!),
double.parse(commandInterpretation.group(7)!)));
startPoint = element.end;
var remainingPathStr = input.substring(commandInterpretation.end);
return Tuple3<PathElement, String, Offset>(
element, remainingPathStr, element.end);
case 'a':
case 'A':
var element = ArcElement(
startPoint: startPoint,
relative: relativeCommand,
radius: Offset(double.parse(commandInterpretation.group(2)!),
double.parse(commandInterpretation.group(3)!)),
arcEnd: Offset(double.parse(commandInterpretation.group(7)!),
double.parse(commandInterpretation.group(8)!)),
xAxisRotation: double.parse(commandInterpretation.group(4)!));
startPoint = element.end;
var remainingPathStr = input.substring(commandInterpretation.end);
return Tuple3<PathElement, String, Offset>(
element, remainingPathStr, element.end);
case 'z':
case 'Z':
var element = CloseElement();
var remainingPathStr = input.substring(commandInterpretation.end);
return Tuple3<PathElement, String, Offset>(
element, remainingPathStr, element.end);
case 'h':
case 'H':
var element = HorizontalLineElement(
startPoint: startPoint,
relative: relativeCommand,
targetX: double.parse(commandInterpretation.group(2)!));
startPoint = element.end;
var remainingPathStr = input.substring(commandInterpretation.end);
return Tuple3<PathElement, String, Offset>(
element, remainingPathStr, element.end);
case 'v':
case 'V':
var element = VerticalLineElement(
startPoint: startPoint,
relative: relativeCommand,
targetY: double.parse(commandInterpretation.group(2)!));
startPoint = element.end;
var remainingPathStr = input.substring(commandInterpretation.end);
return Tuple3<PathElement, String, Offset>(
element, remainingPathStr, element.end);
}
return null;
}
String valueFormat = r"(\d+(?:\.\d+)?)";
String separatorFormat = r"(?:\s+|,)";
var moveRegex =
RegExp("^(M|m)$separatorFormat$valueFormat$separatorFormat$valueFormat");
var lineRegex =
RegExp("^(L|l)$separatorFormat$valueFormat$separatorFormat$valueFormat");
var cubicCurveRegex = RegExp(
"^(C|c)$separatorFormat$valueFormat$separatorFormat" +
"$valueFormat$separatorFormat$valueFormat$separatorFormat$valueFormat$separatorFormat" +
"$valueFormat$separatorFormat$valueFormat");
var arcRegex = RegExp(
"^(A|a)$separatorFormat$valueFormat$separatorFormat$valueFormat" +
"$separatorFormat$valueFormat$separatorFormat$valueFormat$separatorFormat$valueFormat" +
"$separatorFormat$valueFormat$separatorFormat$valueFormat");
var horizontalLineRegex = RegExp("^(H|h)$separatorFormat$valueFormat");
var verticalLineRegex = RegExp("^(V|v)$separatorFormat$valueFormat");
var closeRegex = RegExp("^(z)");
var elementsToReturn = <PathElement>[];
var remainingPath = pathStr.trim();
while (remainingPath.isNotEmpty) {
var moveElementTuple =
interpretCommand(moveRegex, remainingPath, startPoint);
var lineElementTuple =
interpretCommand(lineRegex, remainingPath, startPoint);
var cubicCurveElementTuple =
interpretCommand(cubicCurveRegex, remainingPath, startPoint);
var arcElementTuple = interpretCommand(arcRegex, remainingPath, startPoint);
var horizontalLineElementTuple =
interpretCommand(horizontalLineRegex, remainingPath, startPoint);
var verticalLineElementTuple =
interpretCommand(verticalLineRegex, remainingPath, startPoint);
var closeElementTuple =
interpretCommand(closeRegex, remainingPath, startPoint);
if (moveElementTuple != null) {
elementsToReturn.add(moveElementTuple.item1);
remainingPath = moveElementTuple.item2.trim();
startPoint = moveElementTuple.item1.end;
} else if (lineElementTuple != null) {
elementsToReturn.add(lineElementTuple.item1);
remainingPath = lineElementTuple.item2.trim();
startPoint = lineElementTuple.item1.end;
} else if (cubicCurveElementTuple != null) {
elementsToReturn.add(cubicCurveElementTuple.item1);
remainingPath = cubicCurveElementTuple.item2.trim();
startPoint = cubicCurveElementTuple.item1.end;
} else if (arcElementTuple != null) {
elementsToReturn.add(arcElementTuple.item1);
remainingPath = arcElementTuple.item2.trim();
startPoint = arcElementTuple.item1.end;
} else if (horizontalLineElementTuple != null) {
elementsToReturn.add(horizontalLineElementTuple.item1);
remainingPath = horizontalLineElementTuple.item2.trim();
startPoint = horizontalLineElementTuple.item1.end;
} else if (verticalLineElementTuple != null) {
elementsToReturn.add(verticalLineElementTuple.item1);
remainingPath = verticalLineElementTuple.item2.trim();
startPoint = verticalLineElementTuple.item1.end;
} else if (closeElementTuple != null) {
elementsToReturn.add(closeElementTuple.item1);
remainingPath = closeElementTuple.item2.trim();
} else
throw "Unrecognized path in $remainingPath !";
}
return elementsToReturn;
}