parsePath function

List<PathElement> parsePath(
  1. String pathStr
)

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;
}