parsePath function

List<PathElement> parsePath (String pathStr)

Transform a path definition (value of 'd' attribute in SVG tag) into a List of PathElement.

Implementation

List<PathElement> parsePath(String pathStr) {
  Tuple2<PathElement, String> interpretCommand(
      RegExp commandRegex, String input) {
    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(
            relative: relativeCommand,
            moveParams: Offset(double.parse(commandInterpretation.group(2)),
                double.parse(commandInterpretation.group(3))));
        var remainingPathStr = input.substring(commandInterpretation.end);
        return Tuple2<PathElement, String>(element, remainingPathStr);
      case 'L':
      case 'l':
        var element = LineElement(
            relative: relativeCommand,
            lineParams: Offset(double.parse(commandInterpretation.group(2)),
                double.parse(commandInterpretation.group(3))));
        var remainingPathStr = input.substring(commandInterpretation.end);
        return Tuple2<PathElement, String>(element, remainingPathStr);
      case 'c':
      case 'C':
        var element = CubicCurveElement(
            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))));
        var remainingPathStr = input.substring(commandInterpretation.end);
        return Tuple2<PathElement, String>(element, remainingPathStr);
      case 'a':
      case 'A':
        var element = ArcElement(
            relative: relativeCommand,
            radius: Offset(double.parse(commandInterpretation.group(2)),
                double.parse(commandInterpretation.group(3))),
            center: Offset(double.parse(commandInterpretation.group(7)),
                double.parse(commandInterpretation.group(8))),
            xAxisRotation: double.parse(commandInterpretation.group(4)));
        var remainingPathStr = input.substring(commandInterpretation.end);
        return Tuple2<PathElement, String>(element, remainingPathStr);
      case 'z':
      case 'Z':
        var element = CloseElement();
        var remainingPathStr = input.substring(commandInterpretation.end);
        return Tuple2<PathElement, String>(element, remainingPathStr);
    }
    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 closeRegex = RegExp("^(z)");

  var elementsToReturn = <PathElement>[];
  var remainingPath = pathStr.trim();

  while (remainingPath.isNotEmpty) {
    var moveElementTuple = interpretCommand(moveRegex, remainingPath);
    var lineElementTuple = interpretCommand(lineRegex, remainingPath);
    var cubicCurveElementTuple =
        interpretCommand(cubicCurveRegex, remainingPath);
    var arcElementTuple = interpretCommand(arcRegex, remainingPath);
    var closeElementTuple = interpretCommand(closeRegex, remainingPath);

    if (moveElementTuple != null) {
      elementsToReturn.add(moveElementTuple.item1);
      remainingPath = moveElementTuple.item2.trim();
    } else if (lineElementTuple != null) {
      elementsToReturn.add(lineElementTuple.item1);
      remainingPath = lineElementTuple.item2.trim();
    } else if (cubicCurveElementTuple != null) {
      elementsToReturn.add(cubicCurveElementTuple.item1);
      remainingPath = cubicCurveElementTuple.item2.trim();
    } else if (arcElementTuple != null) {
      elementsToReturn.add(arcElementTuple.item1);
      remainingPath = arcElementTuple.item2.trim();
    } else if (closeElementTuple != null) {
      elementsToReturn.add(closeElementTuple.item1);
      remainingPath = closeElementTuple.item2.trim();
    } else
      throw "Unrecognized path in $remainingPath !";
  }

  return elementsToReturn;
}