public static void FlattenGeometry(PathGeometry pathGeoDst, Geometry geoSrc, double tolerance, Matrix matxPrevious) { Matrix matx = matxPrevious; if (geoSrc is GeometryGroup) { foreach (Geometry geoChild in (geoSrc as GeometryGroup).Children) { FlattenGeometry(pathGeoDst, geoChild, tolerance, matx); } } else if (geoSrc is LineGeometry) { LineGeometry lineGeoSrc = geoSrc as LineGeometry; PathFigure figDst = new PathFigure(); PolyLineSegment segDst = new PolyLineSegment(); figDst.StartPoint = matx.Transform(lineGeoSrc.StartPoint); segDst.Points.Add(matx.Transform(lineGeoSrc.EndPoint)); figDst.Segments.Add(segDst); pathGeoDst.Figures.Add(figDst); } else if (geoSrc is RectangleGeometry) { RectangleGeometry rectGeoSrc = geoSrc as RectangleGeometry; PathFigure figDst = new PathFigure(); PolyLineSegment segDst = new PolyLineSegment(); figDst.StartPoint = matx.Transform(new Point(rectGeoSrc.Rect.Left, rectGeoSrc.Rect.Top)); segDst.Points.Add(matx.Transform(new Point(rectGeoSrc.Rect.Right, rectGeoSrc.Rect.Top))); segDst.Points.Add(matx.Transform(new Point(rectGeoSrc.Rect.Right, rectGeoSrc.Rect.Bottom))); segDst.Points.Add(matx.Transform(new Point(rectGeoSrc.Rect.Left, rectGeoSrc.Rect.Bottom))); segDst.Points.Add(matx.Transform(new Point(rectGeoSrc.Rect.Left, rectGeoSrc.Rect.Top))); figDst.IsClosed = true; figDst.Segments.Add(segDst); pathGeoDst.Figures.Add(figDst); } else if (geoSrc is EllipseGeometry) { EllipseGeometry elipGeoSrc = geoSrc as EllipseGeometry; PathFigure figDst = new PathFigure(); PolyLineSegment segDst = new PolyLineSegment(); int max = (int)(4 * (elipGeoSrc.RadiusX + elipGeoSrc.RadiusY) / tolerance); for (int i = 0; i < max; i++) { double x = elipGeoSrc.Center.X + elipGeoSrc.RadiusX * Math.Sin(i * 2 * Math.PI / max); double y = elipGeoSrc.Center.Y - elipGeoSrc.RadiusY * Math.Cos(i * 2 * Math.PI / max); Point pt = matx.Transform(new Point(x, y)); if (i == 0) figDst.StartPoint = pt; else segDst.Points.Add(pt); } figDst.IsClosed = true; figDst.Segments.Add(segDst); pathGeoDst.Figures.Add(figDst); } else if (geoSrc is PathGeometry) { PathGeometry pathGeoSrc = geoSrc as PathGeometry; pathGeoDst.FillRule = pathGeoSrc.FillRule; foreach (PathFigure figSrc in pathGeoSrc.Figures) { PathFigure figDst = new PathFigure { IsFilled = figSrc.IsFilled, IsClosed = figSrc.IsClosed, StartPoint = matx.Transform(figSrc.StartPoint) }; Point ptLast = figDst.StartPoint; foreach (PathSegment segSrc in figSrc.Segments) { PolyLineSegment segDst = new PolyLineSegment(); if (segSrc is LineSegment) { LineSegment lineSegSrc = segSrc as LineSegment; ptLast = matx.Transform(lineSegSrc.Point); segDst.Points.Add(ptLast); } else if (segSrc is PolyLineSegment) { PolyLineSegment polySegSrc = segSrc as PolyLineSegment; foreach (Point pt in polySegSrc.Points) { ptLast = matx.Transform(pt); segDst.Points.Add(ptLast); } } else if (segSrc is BezierSegment) { BezierSegment bezSeg = segSrc as BezierSegment; Point pt0 = ptLast; Point pt1 = matx.Transform(bezSeg.Point1); Point pt2 = matx.Transform(bezSeg.Point2); Point pt3 = matx.Transform(bezSeg.Point3); Points.Clear(); FlattenCubicBezier(Points, pt0, pt1, pt2, pt3, tolerance); for (int i = 1; i < Points.Count; i++) segDst.Points.Add(Points[i]); ptLast = Points[Points.Count - 1]; } else if (segSrc is PolyBezierSegment) { PolyBezierSegment polyBezSeg = segSrc as PolyBezierSegment; for (int bez = 0; bez < polyBezSeg.Points.Count; bez += 3) { if (bez + 2 > polyBezSeg.Points.Count - 1) break; Point pt0 = ptLast; Point pt1 = matx.Transform(polyBezSeg.Points[bez]); Point pt2 = matx.Transform(polyBezSeg.Points[bez + 1]); Point pt3 = matx.Transform(polyBezSeg.Points[bez + 2]); Points.Clear(); FlattenCubicBezier(Points, pt0, pt1, pt2, pt3, tolerance); for (int i = 1; i < Points.Count; i++) segDst.Points.Add(Points[i]); ptLast = Points[Points.Count - 1]; } } else if (segSrc is QuadraticBezierSegment) { QuadraticBezierSegment quadBezSeg = segSrc as QuadraticBezierSegment; Point pt0 = ptLast; Point pt1 = matx.Transform(quadBezSeg.Point1); Point pt2 = matx.Transform(quadBezSeg.Point2); Points.Clear(); FlattenQuadraticBezier(Points, pt0, pt1, pt2, tolerance); for (int i = 1; i < Points.Count; i++) segDst.Points.Add(Points[i]); ptLast = Points[Points.Count - 1]; } else if (segSrc is PolyQuadraticBezierSegment) { PolyQuadraticBezierSegment polyQuadBezSeg = segSrc as PolyQuadraticBezierSegment; for (int bez = 0; bez < polyQuadBezSeg.Points.Count; bez += 2) { if (bez + 1 > polyQuadBezSeg.Points.Count - 1) break; Point pt0 = ptLast; Point pt1 = matx.Transform(polyQuadBezSeg.Points[bez]); Point pt2 = matx.Transform(polyQuadBezSeg.Points[bez + 1]); Points.Clear(); FlattenQuadraticBezier(Points, pt0, pt1, pt2, tolerance); for (int i = 1; i < Points.Count; i++) segDst.Points.Add(Points[i]); ptLast = Points[Points.Count - 1]; } } else if (segSrc is ArcSegment) { ArcSegment arcSeg = segSrc as ArcSegment; Points.Clear(); FlattenArc( Points, ptLast, arcSeg.Point, arcSeg.Size.Width, arcSeg.Size.Height, arcSeg.RotationAngle, arcSeg.IsLargeArc, arcSeg.SweepDirection == SweepDirection.CounterClockwise, tolerance); // Set ptLast while transferring points for (int i = 1; i < Points.Count; i++) segDst.Points.Add(ptLast = Points[i]); } figDst.Segments.Add(segDst); } pathGeoDst.Figures.Add(figDst); } } }
static void ParseToPathFigureCollection(PathFigureCollection pathFigureCollection, string pathString, int startIndex) { PathFigure pathFigure = null; _pathString = pathString; _pathLength = pathString.Length; _curIndex = startIndex; _secondLastPoint = new Point(0, 0); _lastPoint = new Point(0, 0); _lastStart = new Point(0, 0); _figureStarted = false; bool first = true; char last_cmd = ' '; while (ReadToken()) // Empty path is allowed in XAML { char cmd = _token; if (first) { if ((cmd != 'M') && (cmd != 'm')) // Path starts with M|m { ThrowBadToken(); } first = false; } switch (cmd) { case 'm': case 'M': // XAML allows multiple points after M/m _lastPoint = ReadPoint(cmd, !AllowComma); pathFigure = new PathFigure { StartPoint = _lastPoint }; pathFigureCollection.Add(pathFigure); _figureStarted = true; _lastStart = _lastPoint; last_cmd = 'M'; while (IsNumber(AllowComma)) { _lastPoint = ReadPoint(cmd, !AllowComma); LineSegment lineSegment = new LineSegment { Point = _lastPoint }; pathFigure.Segments.Add(lineSegment); last_cmd = 'L'; } break; case 'l': case 'L': case 'h': case 'H': case 'v': case 'V': EnsureFigure(); do { switch (cmd) { case 'l': _lastPoint = ReadPoint(cmd, !AllowComma); break; case 'L': _lastPoint = ReadPoint(cmd, !AllowComma); break; case 'h': _lastPoint.X += ReadNumber(!AllowComma); break; case 'H': _lastPoint.X = ReadNumber(!AllowComma); break; case 'v': _lastPoint.Y += ReadNumber(!AllowComma); break; case 'V': _lastPoint.Y = ReadNumber(!AllowComma); break; } pathFigure.Segments.Add(new LineSegment { Point = _lastPoint }); }while (IsNumber(AllowComma)); last_cmd = 'L'; break; case 'c': case 'C': // Cubic Bezier case 's': case 'S': // Smooth cublic Bezier EnsureFigure(); do { Point p; if ((cmd == 's') || (cmd == 'S')) { if (last_cmd == 'C') { p = Reflect(); } else { p = _lastPoint; } _secondLastPoint = ReadPoint(cmd, !AllowComma); } else { p = ReadPoint(cmd, !AllowComma); _secondLastPoint = ReadPoint(cmd, AllowComma); } _lastPoint = ReadPoint(cmd, AllowComma); BezierSegment bezierSegment = new BezierSegment { Point1 = p, Point2 = _secondLastPoint, Point3 = _lastPoint }; pathFigure.Segments.Add(bezierSegment); last_cmd = 'C'; }while (IsNumber(AllowComma)); break; case 'q': case 'Q': // Quadratic Bezier case 't': case 'T': // Smooth quadratic Bezier EnsureFigure(); do { if ((cmd == 't') || (cmd == 'T')) { if (last_cmd == 'Q') { _secondLastPoint = Reflect(); } else { _secondLastPoint = _lastPoint; } _lastPoint = ReadPoint(cmd, !AllowComma); } else { _secondLastPoint = ReadPoint(cmd, !AllowComma); _lastPoint = ReadPoint(cmd, AllowComma); } QuadraticBezierSegment quadraticBezierSegment = new QuadraticBezierSegment { Point1 = _secondLastPoint, Point2 = _lastPoint }; pathFigure.Segments.Add(quadraticBezierSegment); last_cmd = 'Q'; }while (IsNumber(AllowComma)); break; case 'a': case 'A': EnsureFigure(); do { // A 3,4 5, 0, 0, 6,7 double w = ReadNumber(!AllowComma); double h = ReadNumber(AllowComma); double rotation = ReadNumber(AllowComma); bool large = ReadBool(); bool sweep = ReadBool(); _lastPoint = ReadPoint(cmd, AllowComma); ArcSegment arcSegment = new ArcSegment { Size = new Size(w, h), RotationAngle = rotation, IsLargeArc = large, SweepDirection = sweep ? SweepDirection.Clockwise : SweepDirection.CounterClockwise, Point = _lastPoint }; pathFigure.Segments.Add(arcSegment); }while (IsNumber(AllowComma)); last_cmd = 'A'; break; case 'z': case 'Z': EnsureFigure(); pathFigure.IsClosed = true; _figureStarted = false; last_cmd = 'Z'; _lastPoint = _lastStart; // Set reference point to be first point of current figure break; default: ThrowBadToken(); break; } } }