예제 #1
0
        /// <summary>
        ///     Converts a string containing SVG path data to a sequence of <see cref="PathPiece"/> objects.</summary>
        /// <param name="svgPath">
        ///     SVG path data to parse.</param>
        public static IEnumerable <PathPiece> DecodePieces(string svgPath)
        {
            // Parse all the commands and coordinates
            var numRegex         = @"-?\d*(?:\.\d*)?\d(?:e-?\d+)?\s*,?\s*";
            var prevPoint        = new PointD(0, 0);
            var prevStartPoint   = (PointD?)null;
            var prevControlPoint = new PointD(0, 0);

            svgPath = svgPath.TrimStart();
            while (!string.IsNullOrWhiteSpace(svgPath))
            {
                Match m;
                if ((m = Regex.Match(svgPath, @"^[MLCHVS]\s*({0})*".Fmt(numRegex), RegexOptions.IgnoreCase)).Success)
                {
                    PathPieceType type;
                    PointD[]      points;
                    bool          prevControlPointDetermined = false;
                    var           numbers = m.Groups[1].Captures.Cast <Capture>().Select(c => double.Parse(c.Value.Trim().TrimEnd(',').Trim())).ToArray();

                    switch (m.Value[0])
                    {
                    case 'M':
                        type      = PathPieceType.Move;
                        points    = numbers.Split(2).Select(gr => new PointD(gr.First(), gr.Last())).ToArray();
                        prevPoint = points[points.Length - 1];
                        break;

                    case 'm':
                        type   = PathPieceType.Move;
                        points = numbers.Split(2).Select(gr => new PointD(gr.First(), gr.Last())).ToArray();
                        for (int i = 0; i < points.Length; i++)
                        {
                            prevPoint = (points[i] += prevPoint);
                        }
                        break;

                    case 'L':
                        type      = PathPieceType.Line;
                        points    = numbers.Split(2).Select(gr => new PointD(gr.First(), gr.Last())).ToArray();
                        prevPoint = points[points.Length - 1];
                        break;

                    case 'l':
                        type   = PathPieceType.Line;
                        points = numbers.Split(2).Select(gr => new PointD(gr.First(), gr.Last())).ToArray();
                        for (int i = 0; i < points.Length; i++)
                        {
                            prevPoint = (points[i] += prevPoint);
                        }
                        break;

                    case 'H':
                        type      = PathPieceType.Line;
                        points    = numbers.Select(x => new PointD(x, prevPoint.Y)).ToArray();
                        prevPoint = points.Last();
                        break;

                    case 'h':
                        type   = PathPieceType.Line;
                        points = new PointD[numbers.Length];
                        for (int i = 0; i < numbers.Length; i++)
                        {
                            prevPoint = points[i] = new PointD(prevPoint.X + numbers[i], prevPoint.Y);
                        }
                        break;

                    case 'V':
                        type      = PathPieceType.Line;
                        points    = numbers.Select(y => new PointD(prevPoint.X, y)).ToArray();
                        prevPoint = points.Last();
                        break;

                    case 'v':
                        type   = PathPieceType.Line;
                        points = new PointD[numbers.Length];
                        for (int i = 0; i < numbers.Length; i++)
                        {
                            prevPoint = points[i] = new PointD(prevPoint.X, prevPoint.Y + numbers[i]);
                        }
                        break;

                    case 'C':
                        type                       = PathPieceType.Curve;
                        points                     = numbers.Split(2).Select(x => new PointD(x.First(), x.Last())).ToArray();
                        prevPoint                  = points.Last();
                        prevControlPoint           = points.SkipLast(1).Last();
                        prevControlPointDetermined = true;
                        break;

                    case 'c':
                        type   = PathPieceType.Curve;
                        points = numbers.Split(2).Select(x => new PointD(x.First(), x.Last())).ToArray();
                        Ut.Assert(points.Length % 3 == 0);
                        for (int i = 0; i < points.Length; i += 3)
                        {
                            points[i]     += prevPoint;
                            points[i + 1] += prevPoint;
                            prevPoint      = (points[i + 2] += prevPoint);
                        }
                        prevPoint                  = points.Last();
                        prevControlPoint           = points.SkipLast(1).Last();
                        prevControlPointDetermined = true;
                        break;

                    case 'S':
                        type = PathPieceType.Curve;
                        var pointsList1 = new List <PointD>();
                        foreach (var pair in numbers.Split(2).Select(x => new PointD(x.First(), x.Last())).Split(2))
                        {
                            Ut.Assert(pair.Count() == 2);
                            pointsList1.Add(prevPoint + (prevPoint - prevControlPoint));
                            pointsList1.Add(prevControlPoint = pair.First());
                            pointsList1.Add(prevPoint        = pair.Last());
                        }
                        points = pointsList1.ToArray();
                        prevControlPointDetermined = true;
                        break;

                    case 's':
                        type = PathPieceType.Curve;
                        var pointsList2 = new List <PointD>();
                        foreach (var pair in numbers.Split(2).Select(x => new PointD(x.First(), x.Last())).Split(2))
                        {
                            Ut.Assert(pair.Count() == 2);
                            pointsList2.Add(prevPoint + (prevPoint - prevControlPoint));
                            pointsList2.Add(prevControlPoint = (pair.First() + prevPoint));
                            pointsList2.Add(prevPoint        = (pair.Last() + prevPoint));
                        }
                        points = pointsList2.ToArray();
                        prevControlPointDetermined = true;
                        break;

                    default:
                        throw new InvalidOperationException();
                    }

                    if (prevStartPoint == null)
                    {
                        prevStartPoint = points[0];
                    }
                    if (!prevControlPointDetermined)
                    {
                        prevControlPoint = prevPoint;
                    }

                    yield return(new PathPiece(type, points));
                }
                else if ((m = Regex.Match(svgPath, @"^Z\s*", RegexOptions.IgnoreCase)).Success)
                {
                    yield return(PathPiece.End);

                    prevPoint      = prevStartPoint ?? new PointD(0, 0);
                    prevStartPoint = null;
                }
                else if ((m = Regex.Match(svgPath, @"^A\s*(({0})({0})({0})([01])[\s,]*([01])[\s,]*({0})({0}))+".Fmt(numRegex), RegexOptions.IgnoreCase)).Success)
                {
                    for (var cp = 0; cp < m.Groups[1].Captures.Count; cp++)
                    {
                        double convert(int gr) => double.Parse(m.Groups[gr].Captures[cp].Value.Trim().TrimEnd(',').Trim());

                        var p = new PointD(convert(7), convert(8));
                        prevPoint = m.Value[0] == 'a' ? p + prevPoint : p;
                        yield return(new PathPieceArc(convert(2), convert(3), convert(4), m.Groups[5].Captures[cp].Value != "0", m.Groups[6].Captures[cp].Value != "0", prevPoint));
                    }
                }
                else
                {
                    Debugger.Break();
                    throw new NotImplementedException();
                }
                svgPath = svgPath.Substring(m.Length);
            }
        }