/// <summary> /// Compiles the stroke to the necessary triangles to draw it. /// </summary> /// <param name="path">The path to be compiled.</param> /// <param name="width">The width of the stroke.</param> /// <param name="lineCap">The line cap method for the stroke's ends.</param> /// <param name="lineJoin">The line join method for the stroke's midpoints</param> /// <param name="miterLimit">The miter limit value.</param> public static CompiledDrawing CompileStroke(SvgPathSegmentList path, double width, SvgStrokeLineCap lineCap = SvgStrokeLineCap.Butt, SvgStrokeLineJoin lineJoin = SvgStrokeLineJoin.Bevel, double miterLimit = double.PositiveInfinity) { // Return empty if stroke width == 0 if (width == 0) { return(CompiledDrawing.Empty); } // Divide the width by 2 to cope with the SVG documentation var halfWidth = width / 2; var curves = new List <Curve>(); // Convert each split path to a fill foreach (var data in path.SplitCurves()) { curves.AddRange(StrokeUtils.ConvertToFill(data, halfWidth, lineCap, lineJoin, miterLimit)); } // And compile return(CompileCurves(curves, SvgFillRule.NonZero)); }
public static DoubleMatrix BoundingBoxMatrix(this SvgPathSegmentList path) { var bbox = path.BoundingBox(); return(new DoubleMatrix(1 / bbox.Width, 0, 0, 1 / bbox.Height, -bbox.X / bbox.Width, -bbox.Y / bbox.Height)); }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string"/> containing path data.</param> public static SvgPathSegmentList Parse(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } var segments = new SvgPathSegmentList(); try { char command; bool isRelative; foreach (var commandSet in SplitCommands(path.TrimEnd(null))) { command = commandSet[0]; isRelative = char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation CreatePathSegment(command, segments, new CoordinateParser(commandSet.Trim()), isRelative); } } catch (Exception exc) { Trace.TraceError("Error parsing path \"{0}\": {1}", path, exc.Message); //OSS:Fix:Tell us there was a problem //segments.Clear(); } return(segments); }
public void DrawPath(IEnumerable <IEnumerable <Point> > points, bool winding, GraphicsContext context, DeviceDescription description) { var svgContext = _mapper.MapGraphicsContextToSvg(context); foreach (var point in points) { var vertices = point.ToList(); var list = new SvgPathSegmentList(); var first = vertices.First(); list.Add(new SvgMoveToSegment(new PointF((float)first.X, (float)first.Y))); foreach (var vertex in vertices.Skip(1)) { list.Add(new SvgLineSegment(new PointF((float)first.X, (float)first.Y), new PointF((float)vertex.X, (float)vertex.Y))); first = vertex; } list.Add(new SvgClosePathSegment()); AddChild(new SvgPath { Fill = svgContext.Fill, FillOpacity = svgContext.Opacity, PathData = list, Stroke = svgContext.Pen.Stroke, StrokeDashArray = svgContext.Pen.StrokeDashArray, StrokeLineCap = svgContext.Pen.StrokeLineCap, StrokeLineJoin = svgContext.Pen.StrokeLineJoin, StrokeWidth = svgContext.Pen.StrokeWidth }); } }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string"/> containing path data.</param> public static SvgPathSegmentList Parse(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } var segments = new SvgPathSegmentList(); try { List<float> coords; char command; bool isRelative; foreach (var commandSet in SplitCommands(path.TrimEnd(null))) { coords = new List<float>(ParseCoordinates(commandSet.Trim())); command = commandSet[0]; isRelative = char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation CreatePathSegment(command, segments, coords, isRelative); } } catch (Exception exc) { Trace.TraceError("Error parsing path \"{0}\": {1}", path, exc.Message); } return segments; }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string" /> containing path data.</param> public static void Parse(Path path, string pathString) { if (string.IsNullOrEmpty(pathString)) { throw new ArgumentNullException("pathString"); } var segments = new SvgPathSegmentList(); try { char command; bool isRelative; foreach (var commandSet in SplitCommands(pathString.TrimEnd(null))) { command = commandSet[0]; isRelative = char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation CreatePathSegment(command, segments, new CoordinateParser(commandSet.Trim()), isRelative); } foreach (var segment in segments) { segment.AddToPath(path); } } catch (Exception exc) { throw new Exception(string.Format("Error parsing path \"{0}\": {1}", path, exc.Message)); } //return segments; }
private static SvgPath ConvertArc(Arc arc) { SvgPath svgPath = new SvgPath(); SvgPathSegmentList svgPathSegmentList = new SvgPathSegmentList(); svgPathSegmentList.Add(new SvgMoveToSegment(arc.GetEndPoint(0).ConvertToPointF())); XYZ a = arc.GetEndPoint(0) - arc.Center; XYZ b = arc.GetEndPoint(1) - arc.Center; double angleAboutAxis = a.AngleOnPlaneTo(b, arc.Normal); SvgArcSweep svgArcSweep = SvgArcSweep.Positive; if (angleAboutAxis >= 180) { svgArcSweep = SvgArcSweep.Negative; } SvgArcSize svgArcSize = SvgArcSize.Small; SvgArcSegment svgArcSegment = new SvgArcSegment(svgPathSegmentList.Last.End, (float)arc.Radius, (float)arc.Radius, (float)angleAboutAxis, svgArcSize, svgArcSweep, arc.GetEndPoint(1).ConvertToPointF()); svgPathSegmentList.Add(svgArcSegment); svgPath.PathData = svgPathSegmentList; return(svgPath); }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeX"><b>true</b> if <paramref name="x"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <param name="isRelativeY"><b>true</b> if <paramref name="y"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeX, bool isRelativeY) { var point = new PointF(x, y); if ((isRelativeX || isRelativeY) && segments.Count > 0) { var lastSegment = segments.Last; // if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0 if (lastSegment is SvgClosePathSegment && segments.Count > 0) { for (int i = segments.Count - 1; i >= 0; i--) { if (segments[i] is SvgMoveToSegment moveToSegment) { lastSegment = moveToSegment; break; } } } if (isRelativeX) { point.X += lastSegment.End.X; } if (isRelativeY) { point.Y += lastSegment.End.Y; } } return(point); }
/// <summary> /// Initializes a new instance of the <see cref="SvgPath"/> class. /// </summary> public SvgPath() { var pathData = new SvgPathSegmentList(); this.Attributes["d"] = pathData; pathData._owner = this; }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeX"><b>true</b> if <paramref name="x"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <param name="isRelativeY"><b>true</b> if <paramref name="y"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeX, bool isRelativeY) { var point = new PointF(x, y); if ((isRelativeX || isRelativeY) && segments.Count > 0) { var lastSegment = segments.Last; // if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0 if (lastSegment is SvgClosePathSegment) { lastSegment = segments.Reverse().OfType <SvgMoveToSegment>().First(); } if (isRelativeX) { point.X += lastSegment.End.X; } if (isRelativeY) { point.Y += lastSegment.End.Y; } } return(point); }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string"/> containing path data.</param> public static SvgPathSegmentList Parse(string path) { if (path == null) { throw new ArgumentNullException("path"); } var segments = new SvgPathSegmentList(); try { foreach (var commandSet in SplitCommands(path.TrimEnd(null))) { var command = commandSet[0]; var isRelative = char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation CreatePathSegment(command, segments, new CoordinateParser(commandSet.Trim()), isRelative); } } catch (Exception exc) { Trace.TraceError("Error parsing path \"{0}\": {1}", path, exc.Message); } return(segments); }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string"/> containing path data.</param> public static SvgPathSegmentList Parse(ReadOnlySpan <char> path) { var segments = new SvgPathSegmentList(); try { var pathTrimmed = path.TrimEnd(); var commandStart = 0; var pathLength = pathTrimmed.Length; for (var i = 0; i < pathLength; ++i) { var currentChar = pathTrimmed[i]; if (char.IsLetter(currentChar) && currentChar != 'e' && currentChar != 'E') // e is used in scientific notiation. but not svg path { var start = commandStart; var length = i - commandStart; var command = pathTrimmed.Slice(start, length).Trim(); commandStart = i; if (command.Length > 0) { var commandSetTrimmed = pathTrimmed.Slice(start, length).Trim(); var state = new CoordinateParserState(ref commandSetTrimmed); CreatePathSegment(commandSetTrimmed[0], segments, ref state, ref commandSetTrimmed); } if (pathLength == i + 1) { var commandSetTrimmed = pathTrimmed.Slice(i, 1).Trim(); var state = new CoordinateParserState(ref commandSetTrimmed); CreatePathSegment(commandSetTrimmed[0], segments, ref state, ref commandSetTrimmed); } } else if (pathLength == i + 1) { var start = commandStart; var length = i - commandStart + 1; var command = pathTrimmed.Slice(start, length).Trim(); if (command.Length > 0) { var commandSetTrimmed = pathTrimmed.Slice(start, length).Trim(); var state = new CoordinateParserState(ref commandSetTrimmed); CreatePathSegment(commandSetTrimmed[0], segments, ref state, ref commandSetTrimmed); } } } } catch (Exception exc) { Trace.TraceError("Error parsing path \"{0}\": {1}", path.ToString(), exc.Message); } return(segments); }
public override SvgElement DeepCopy <T>() { var newObj = base.DeepCopy <T>() as SvgGlyph; if (PathData != null) { var pathData = new SvgPathSegmentList(); foreach (var segment in PathData) { pathData.Add(segment.Clone()); } newObj.PathData = pathData; } return(newObj); }
private static SvgPath ConvertLine(Line line) { SvgPath svgPath = new SvgPath(); SvgPathSegmentList svgPathSegmentList = new SvgPathSegmentList(); svgPathSegmentList.Add(new SvgMoveToSegment(line.GetEndPoint(0).ConvertToPointF())); SvgLineSegment svgLineSegment = new SvgLineSegment(svgPathSegmentList.Last.End, line.GetEndPoint(1).ConvertToPointF()); svgPathSegmentList.Add(svgLineSegment); svgPath.PathData = svgPathSegmentList; return(svgPath); }
private static SvgPath ConverPolyCurve(List <Curve> curves) { SvgPath svgPath = new SvgPath(); SvgPathSegmentList svgPathSegmentList = new SvgPathSegmentList(); svgPathSegmentList.Add(new SvgMoveToSegment(curves[0].GetEndPoint(0).ConvertToPointF())); foreach (Curve curve in curves) { if (curve is Arc) { Arc arc = (Arc)curve; XYZ a = arc.GetEndPoint(0) - arc.Center; XYZ b = arc.GetEndPoint(1) - arc.Center; double angleAboutAxis = a.AngleOnPlaneTo(b, arc.Normal); SvgArcSweep svgArcSweep = SvgArcSweep.Positive; if (angleAboutAxis >= 180) { svgArcSweep = SvgArcSweep.Negative; } SvgArcSize svgArcSize = SvgArcSize.Small; SvgArcSegment svgArcSegment = new SvgArcSegment(svgPathSegmentList.Last.End, (float)arc.Radius, (float)arc.Radius, (float)angleAboutAxis, svgArcSize, svgArcSweep, arc.GetEndPoint(1).ConvertToPointF()); svgPathSegmentList.Add(svgArcSegment); } else if (curve is Line) { SvgLineSegment svgLineSegment = new SvgLineSegment(svgPathSegmentList.Last.End, ((Line)curve).GetEndPoint(1).ConvertToPointF()); svgPathSegmentList.Add(svgLineSegment); } } svgPath.PathData = svgPathSegmentList; return(svgPath); }
public override SvgElement DeepCopy <T>() { var newObj = base.DeepCopy <T>() as SvgPath; if (PathData != null) { var pathData = new SvgPathSegmentList(); foreach (var segment in PathData) { pathData.Add(segment.Clone()); } newObj.PathData = pathData; } newObj.PathLength = this.PathLength; newObj.MarkerStart = this.MarkerStart; newObj.MarkerEnd = this.MarkerEnd; return(newObj); }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeX"><b>true</b> if <paramref name="x"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <param name="isRelativeY"><b>true</b> if <paramref name="y"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeX, bool isRelativeY) { var point = new PointF(x, y); if ((isRelativeX || isRelativeY) && segments.Count > 0) { SvgPathSegment lastSegment = segments.Last; // if the last element is a SvgClosePathSegment // the position of the previous element should be used // because the position of SvgClosePathSegment is 0,0 if (lastSegment is SvgClosePathSegment) { SvgMoveToSegment lastMoveSegment = null; for (int i = segments.Count - 1; i >= 0; --i) { if (segments[i] is SvgMoveToSegment) { lastMoveSegment = (SvgMoveToSegment)segments[i]; break; } } // Next i lastSegment = lastMoveSegment; // segments.Reverse().OfType<SvgMoveToSegment>().First(); } // End if (lastSegment is SvgClosePathSegment) if (isRelativeX) { point.X += lastSegment.End.X; } if (isRelativeY) { point.Y += lastSegment.End.Y; } } return(point); }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeX"><b>true</b> if <paramref name="x"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <param name="isRelativeY"><b>true</b> if <paramref name="y"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeX, bool isRelativeY) { var point = new PointF(x, y); if ((isRelativeX || isRelativeY) && segments.Count > 0) { var lastSegment = segments.Last; if (isRelativeX) { point.X += lastSegment.End.X; } if (isRelativeY) { point.Y += lastSegment.End.Y; } } return(point); }
/// <summary> /// Determine the filled simple segments of a path, splitting lines and curves appropriately. /// </summary> /// <param name="path">The path that is supposed to be compiled.</param> /// <param name="fillRule">The fill rule used to determine the filled components</param> /// <returns>The set of simple path components.</returns> public static CompiledDrawing CompileFill(SvgPathSegmentList path, SvgFillRule fillRule = SvgFillRule.EvenOdd) { var curveData = path.SplitCurves(); var curves = new List <Curve>(); foreach (var data in curveData) { // Add all open curves curves.AddRange(data.Curves); // Force close the open curves var p0 = data.Curves[0].At(0); var p1 = data.Curves[data.Curves.Length - 1].At(1); if (!DoubleUtils.RoughlyEquals(p0, p1)) { curves.Add(Curve.Line(p1, p0)); } } return(CompileCurves(curves, fillRule)); }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string"/> containing path data.</param> public static SvgPathSegmentList Parse(string path) { if (path == null) { throw new ArgumentNullException("path"); } var segments = new SvgPathSegmentList(); try { foreach (var commandSet in SplitCommands(path.TrimEnd(null))) { CreatePathSegment(commandSet[0], segments, new CoordinateParser(commandSet.Trim())); } } catch (Exception exc) { Trace.TraceError("Error parsing path \"{0}\": {1}", path, exc.Message); } return(segments); }
/// <summary> /// Initializes a new instance of the <see cref="SvgGlyph"/> class. /// </summary> public SvgGlyph() { var pathData = new SvgPathSegmentList(); this.Attributes["d"] = pathData; }
public static void CreatePathSegment(char command, SvgPathSegmentList segments, List <float> coords, bool isRelative) { switch (command) { case 'm': // relative moveto case 'M': // moveto segments.Add( new SvgMoveToSegment(ToAbsolute(coords[0], coords[1], segments, isRelative))); for (var i = 2; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'a': case 'A': SvgArcSize size; SvgArcSweep sweep; for (var i = 0; i < coords.Count; i += 7) { size = (coords[i + 3] != 0.0f) ? SvgArcSize.Large : SvgArcSize.Small; sweep = (coords[i + 4] != 0.0f) ? SvgArcSweep.Positive : SvgArcSweep.Negative; // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y segments.Add(new SvgArcSegment(segments.Last.End, coords[i], coords[i + 1], coords[i + 2], size, sweep, ToAbsolute(coords[i + 5], coords[i + 6], segments, isRelative))); } break; case 'l': // relative lineto case 'L': // lineto for (var i = 0; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto foreach (var value in coords) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(value, segments.Last.End.Y, segments, isRelative, false))); } break; case 'V': // vertical lineto case 'v': // relative vertical lineto foreach (var value in coords) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(segments.Last.End.X, value, segments, false, isRelative))); } break; case 'Q': // curveto case 'q': // relative curveto for (var i = 0; i < coords.Count; i += 4) { segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'T': // shorthand/smooth curveto case 't': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 2) { var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment; var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'C': // curveto case 'c': // relative curveto for (var i = 0; i < coords.Count; i += 6) { segments.Add(new SvgCubicCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative), ToAbsolute(coords[i + 4], coords[i + 5], segments, isRelative))); } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 4) { var lastCubicCurve = segments.Last as SvgCubicCurveSegment; var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgCubicCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'Z': // closepath case 'z': // relative closepath segments.Add(new SvgClosePathSegment()); break; } }
/// <summary> /// Gets the bounds of the element. /// </summary> /// <value>The bounds.</value> // public override System.Drawing.RectangleF Bounds // { // get { return this.Path.GetBounds(); } // } /// <summary> /// Initializes a new instance of the <see cref="SvgPath"/> class. /// </summary> public SvgPath() { this._pathData = new SvgPathSegmentList(); this._pathData._owner = this; }
/// <summary> /// Initializes a new instance of the <see cref="SvgPath"/> class. /// </summary> public SvgPath() { this._pathData = new SvgPathSegmentList(); this._pathData._owner = this; }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeBoth"> /// <b>true</b> if <paramref name="x" /> and <paramref name="y" /> contains relative /// coordinate values, otherwise <b>false</b>. /// </param> /// <returns><see cref="PointF" /> that contains absolute coordinates.</returns> private static Point ToAbsolute(double x, double y, SvgPathSegmentList segments, bool isRelativeBoth) { return(ToAbsolute(x, y, segments, isRelativeBoth, isRelativeBoth)); }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeX"><b>true</b> if <paramref name="x"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <param name="isRelativeY"><b>true</b> if <paramref name="y"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeX, bool isRelativeY) { var point = new PointF(x, y); if ((isRelativeX || isRelativeY) && segments.Count > 0) { var lastSegment = segments.Last; // if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0 if (lastSegment is SvgClosePathSegment) lastSegment = segments[segments.Count - 2]; if (isRelativeX) { point.X += lastSegment.End.X; } if (isRelativeY) { point.Y += lastSegment.End.Y; } } return point; }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeBoth"><b>true</b> if <paramref name="x"/> and <paramref name="y"/> contains relative coordinate values, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeBoth) { return(ToAbsolute(x, y, segments, isRelativeBoth, isRelativeBoth)); }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeBoth"><b>true</b> if <paramref name="x"/> and <paramref name="y"/> contains relative coordinate values, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeBoth) { return ToAbsolute(x, y, segments, isRelativeBoth, isRelativeBoth); }
private static void CreatePathSegment(char command, SvgPathSegmentList segments, ref CoordinateParserState state, ref ReadOnlySpan <char> chars) { var isRelative = char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation switch (command) { case 'M': // moveto case 'm': // relative moveto { if (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)) { segments.Add( new SvgMoveToSegment( isRelative, new PointF(coords0, coords1))); } while (CoordinateParser.TryGetFloat(out coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out coords1, ref chars, ref state)) { segments.Add( new SvgLineSegment( isRelative, new PointF(coords0, coords1))); } } break; case 'A': // elliptical arc case 'a': // relative elliptical arc { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetBool(out var size, ref chars, ref state) && CoordinateParser.TryGetBool(out var sweep, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords4, ref chars, ref state)) { // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y segments.Add( new SvgArcSegment( coords0, coords1, coords2, size ? SvgArcSize.Large : SvgArcSize.Small, sweep ? SvgArcSweep.Positive : SvgArcSweep.Negative, isRelative, new PointF(coords3, coords4))); } } break; case 'L': // lineto case 'l': // relative lineto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)) { segments.Add( new SvgLineSegment( isRelative, new PointF(coords0, coords1))); } } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)) { segments.Add( new SvgLineSegment( isRelative, new PointF(coords0, float.NaN))); } } break; case 'V': // vertical lineto case 'v': // relative vertical lineto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)) { segments.Add( new SvgLineSegment( isRelative, new PointF(float.NaN, coords0))); } } break; case 'Q': // quadratic bézier curveto case 'q': // relative quadratic bézier curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state)) { segments.Add( new SvgQuadraticCurveSegment( isRelative, new PointF(coords0, coords1), new PointF(coords2, coords3))); } } break; case 'T': // shorthand/smooth quadratic bézier curveto case 't': // relative shorthand/smooth quadratic bézier curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)) { segments.Add( new SvgQuadraticCurveSegment( isRelative, new PointF(coords0, coords1))); } } break; case 'C': // curveto case 'c': // relative curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords4, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords5, ref chars, ref state)) { segments.Add( new SvgCubicCurveSegment( isRelative, new PointF(coords0, coords1), new PointF(coords2, coords3), new PointF(coords4, coords5))); } } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto { while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state) && CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state)) { segments.Add( new SvgCubicCurveSegment( isRelative, new PointF(coords0, coords1), new PointF(coords2, coords3))); } } break; case 'Z': // closepath case 'z': // relative closepath { segments.Add(new SvgClosePathSegment(isRelative)); } break; } }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeBoth"> /// <b>true</b> if <paramref name="x" /> and <paramref name="y" /> contains relative /// coordinate values, otherwise <b>false</b>. /// </param> /// <returns><see cref="PointF" /> that contains absolute coordinates.</returns> private static Point ToAbsolute(double x, double y, SvgPathSegmentList segments, bool isRelativeBoth) { return ToAbsolute(x, y, segments, isRelativeBoth, isRelativeBoth); }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeX"> /// <b>true</b> if <paramref name="x" /> contains relative coordinate value, otherwise /// <b>false</b>. /// </param> /// <param name="isRelativeY"> /// <b>true</b> if <paramref name="y" /> contains relative coordinate value, otherwise /// <b>false</b>. /// </param> /// <returns><see cref="PointF" /> that contains absolute coordinates.</returns> private static Point ToAbsolute(double x, double y, SvgPathSegmentList segments, bool isRelativeX, bool isRelativeY) { var point = new Point(x, y); if ((isRelativeX || isRelativeY) && segments.Count > 0) { var lastSegment = segments.Last; // if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0 if (lastSegment is SvgClosePathSegment) lastSegment = segments.Reverse().OfType<SvgMoveToSegment>().First(); if (isRelativeX) { point.X += lastSegment.End.X; } if (isRelativeY) { point.Y += lastSegment.End.Y; } } return point; }
public static StrokeCollection exec(string absolutePath, int width, int height, int thickness, System.Windows.Media.Color color, bool dotted) { string filename = Path.GetFileName(absolutePath); if (!isPotraceDirectory) { Directory.SetCurrentDirectory(Directory.GetCurrentDirectory() + "/../../Potrace"); isPotraceDirectory = true; } string extension = absolutePath.Substring(absolutePath.Length - 4, 4); if (extension.ToLower() == PNG || extension.ToLower() == JPG) { Image img = Image.FromFile(absolutePath); filename = filename.Substring(0, filename.Length - 4) + BMP; img.Save("Images/" + filename, ImageFormat.Bmp); } else if (extension.ToLower() != BMP) { throw new FileFormatException(); } Process process = new Process(); process.StartInfo.FileName = "cmd.exe"; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; process.Start(); process.StandardInput.WriteLine("mkbitmap.exe -o Images/mkbitmap-o/" + filename + " Images/" + filename); process.StandardInput.Flush(); process.StandardInput.WriteLine("potrace.exe --svg -o Images/potrace-o/" + filename.Substring(0, filename.Length - 4) + SVG + " -a 0 --flat -W " + width + "pt -H " + height + "pt Images/mkbitmap-o/" + filename); process.StandardInput.Flush(); process.StandardInput.Close(); process.WaitForExit(); string svg = File.ReadAllText("Images/potrace-o/" + filename.Substring(0, filename.Length - 4) + SVG); SvgTransformCollection transforms = (SvgTransformCollection) new SvgTransformConverter().ConvertFrom(svg.Substring(svg.IndexOf(TRANSFORM_KEY) + TRANSFORM_KEY.Length)); SvgPathSegmentList pathSegments = (SvgPathSegmentList) new SvgPathBuilder().ConvertFrom(svg.Substring(svg.IndexOf(PATH_KEY) + PATH_KEY.Length)); PointF translate = new PointF(0, 0); PointF scale = new PointF(1, 1); for (int i = 0; i < transforms.Count; i++) { if (transforms[i].GetType() == typeof(SvgTranslate)) { translate = new PointF(((SvgTranslate)transforms[i]).X, ((SvgTranslate)transforms[i]).Y); } else if (transforms[i].GetType() == typeof(SvgScale)) { scale = new PointF(((SvgScale)transforms[i]).X, ((SvgScale)transforms[i]).Y); } } StrokeCollection strokes = new StrokeCollection(); if (dotted) { for (int i = 0; i < pathSegments.Count; i++) { StylusPointCollection stylusPoints = new StylusPointCollection(); stylusPoints.Add(new StylusPoint(scale.X * pathSegments[i].Start.X + translate.X, scale.Y * pathSegments[i].Start.Y + translate.Y)); Stroke stroke = new Stroke(stylusPoints); stroke.DrawingAttributes.Width = stroke.DrawingAttributes.Height = thickness; stroke.DrawingAttributes.Color = color; strokes.Add(stroke); } } else { for (int i = 0; i < pathSegments.Count; i++) { if (pathSegments[i].GetType() == typeof(SvgMoveToSegment)) { StylusPointCollection stylusPoints = new StylusPointCollection(); stylusPoints.Add(new StylusPoint(scale.X * pathSegments[i].Start.X + translate.X, scale.Y * pathSegments[i].Start.Y + translate.Y)); Stroke stroke = new Stroke(stylusPoints); stroke.DrawingAttributes.Width = stroke.DrawingAttributes.Height = thickness; stroke.DrawingAttributes.Color = color; strokes.Add(stroke); } else if (pathSegments[i].GetType() == typeof(SvgLineSegment)) { strokes[strokes.Count - 1].StylusPoints.Add(new StylusPoint(scale.X * pathSegments[i].End.X + translate.X, scale.Y * pathSegments[i].End.Y + translate.Y)); } } } if (extension.ToLower() == PNG || extension.ToLower() == JPG) { File.Delete("Images/" + filename); } File.Delete("Images/mkbitmap-o/" + filename); File.Delete("Images/potrace-o/" + filename.Substring(0, filename.Length - 4) + SVG); return(strokes); }
public static void CreatePathSegment(char command, SvgPathSegmentList segments, List<float> coords, bool isRelative) { switch (command) { case 'm': // relative moveto case 'M': // moveto segments.Add( new SvgMoveToSegment(ToAbsolute(coords[0], coords[1], segments, isRelative))); for (var i = 2; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'a': case 'A': SvgArcSize size; SvgArcSweep sweep; for (var i = 0; i < coords.Count; i += 7) { size = (coords[i + 3] != 0.0f) ? SvgArcSize.Large : SvgArcSize.Small; sweep = (coords[i + 4] != 0.0f) ? SvgArcSweep.Positive : SvgArcSweep.Negative; // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y segments.Add(new SvgArcSegment(segments.Last.End, coords[i], coords[i + 1], coords[i + 2], size, sweep, ToAbsolute(coords[i + 5], coords[i + 6], segments, isRelative))); } break; case 'l': // relative lineto case 'L': // lineto for (var i = 0; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto foreach (var value in coords) segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(value, segments.Last.End.Y, segments, isRelative, false))); break; case 'V': // vertical lineto case 'v': // relative vertical lineto foreach (var value in coords) segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(segments.Last.End.X, value, segments, false, isRelative))); break; case 'Q': // curveto case 'q': // relative curveto for (var i = 0; i < coords.Count; i += 4) { segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'T': // shorthand/smooth curveto case 't': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 2) { var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment; var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'C': // curveto case 'c': // relative curveto for (var i = 0; i < coords.Count; i += 6) { segments.Add(new SvgCubicCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative), ToAbsolute(coords[i + 4], coords[i + 5], segments, isRelative))); } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 4) { var lastCubicCurve = segments.Last as SvgCubicCurveSegment; var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgCubicCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'Z': // closepath case 'z': // relative closepath segments.Add(new SvgClosePathSegment()); break; } }
private static void CreatePathSegment(char command, SvgPathSegmentList segments, CoordinateParser parser, bool isRelative) { var coords = new float[6]; switch (command) { case 'm': // relative moveto case 'M': // moveto if (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { segments.Add(new SvgMoveToSegment(ToAbsolute(coords[0], coords[1], segments, isRelative))); } while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative))); } break; case 'a': case 'A': bool size; bool sweep; while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetBool(out size) && parser.TryGetBool(out sweep) && parser.TryGetFloat(out coords[3]) && parser.TryGetFloat(out coords[4])) { // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y segments.Add(new SvgArcSegment(segments.Last.End, coords[0], coords[1], coords[2], (size ? SvgArcSize.Large : SvgArcSize.Small), (sweep ? SvgArcSweep.Positive : SvgArcSweep.Negative), ToAbsolute(coords[3], coords[4], segments, isRelative))); } break; case 'l': // relative lineto case 'L': // lineto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative))); } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto while (parser.TryGetFloat(out coords[0])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[0], segments.Last.End.Y, segments, isRelative, false))); } break; case 'V': // vertical lineto case 'v': // relative vertical lineto while (parser.TryGetFloat(out coords[0])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(segments.Last.End.X, coords[0], segments, false, isRelative))); } break; case 'Q': // curveto case 'q': // relative curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetFloat(out coords[3])) { segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative), ToAbsolute(coords[2], coords[3], segments, isRelative))); } break; case 'T': // shorthand/smooth curveto case 't': // relative shorthand/smooth curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment; var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[0], coords[1], segments, isRelative))); } break; case 'C': // curveto case 'c': // relative curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetFloat(out coords[3]) && parser.TryGetFloat(out coords[4]) && parser.TryGetFloat(out coords[5])) { segments.Add(new SvgCubicCurveSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative), ToAbsolute(coords[2], coords[3], segments, isRelative), ToAbsolute(coords[4], coords[5], segments, isRelative))); } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetFloat(out coords[3])) { var lastCubicCurve = segments.Last as SvgCubicCurveSegment; var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgCubicCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[0], coords[1], segments, isRelative), ToAbsolute(coords[2], coords[3], segments, isRelative))); } break; case 'Z': // closepath case 'z': // relative closepath segments.Add(new SvgClosePathSegment()); break; } }
private static void CreatePathSegment(char command, SvgPathSegmentList segments, CoordinateParser parser, bool isRelative) { var coords = new float[6]; switch (command) { case 'm': // relative moveto case 'M': // moveto if (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { segments.Add(new SvgMoveToSegment(ToAbsolute(coords[0], coords[1], segments, isRelative))); } while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative))); } break; case 'a': case 'A': bool size; bool sweep; while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetBool(out size) && parser.TryGetBool(out sweep) && parser.TryGetFloat(out coords[3]) && parser.TryGetFloat(out coords[4])) { // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y segments.Add(new SvgArcSegment(segments.Last.End, coords[0], coords[1], coords[2], size ? SvgArcSize.Large : SvgArcSize.Small, sweep ? SvgArcSweep.Positive : SvgArcSweep.Negative, ToAbsolute(coords[3], coords[4], segments, isRelative))); } break; case 'l': // relative lineto case 'L': // lineto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative))); } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto while (parser.TryGetFloat(out coords[0])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[0], segments.Last.End.Y, segments, isRelative, false))); } break; case 'V': // vertical lineto case 'v': // relative vertical lineto while (parser.TryGetFloat(out coords[0])) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(segments.Last.End.X, coords[0], segments, false, isRelative))); } break; case 'Q': // curveto case 'q': // relative curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetFloat(out coords[3])) { segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative), ToAbsolute(coords[2], coords[3], segments, isRelative))); } break; case 'T': // shorthand/smooth curveto case 't': // relative shorthand/smooth curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1])) { var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment; var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[0], coords[1], segments, isRelative))); } break; case 'C': // curveto case 'c': // relative curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetFloat(out coords[3]) && parser.TryGetFloat(out coords[4]) && parser.TryGetFloat(out coords[5])) { segments.Add(new SvgCubicCurveSegment(segments.Last.End, ToAbsolute(coords[0], coords[1], segments, isRelative), ToAbsolute(coords[2], coords[3], segments, isRelative), ToAbsolute(coords[4], coords[5], segments, isRelative))); } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto while (parser.TryGetFloat(out coords[0]) && parser.TryGetFloat(out coords[1]) && parser.TryGetFloat(out coords[2]) && parser.TryGetFloat(out coords[3])) { var lastCubicCurve = segments.Last as SvgCubicCurveSegment; var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgCubicCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[0], coords[1], segments, isRelative), ToAbsolute(coords[2], coords[3], segments, isRelative))); } break; case 'Z': // closepath case 'z': // relative closepath segments.Add(new SvgClosePathSegment()); break; } }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string"/> containing path data.</param> public static SvgPathSegmentList Parse(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } var segments = new SvgPathSegmentList(); try { List <float> coords; char command; bool isRelative; foreach (var commandSet in SplitCommands(path.TrimEnd(null))) { coords = new List <float>(ParseCoordinates(commandSet.Trim())); command = commandSet[0]; isRelative = char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation switch (command) { case 'm': // relative moveto case 'M': // moveto segments.Add( new SvgMoveToSegment(ToAbsolute(coords[0], coords[1], segments, isRelative))); for (var i = 2; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'a': case 'A': SvgArcSize size; SvgArcSweep sweep; for (var i = 0; i < coords.Count; i += 7) { size = (coords[i + 3] != 0.0f) ? SvgArcSize.Large : SvgArcSize.Small; sweep = (coords[i + 4] != 0.0f) ? SvgArcSweep.Positive : SvgArcSweep.Negative; // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y segments.Add(new SvgArcSegment(segments.Last.End, coords[i], coords[i + 1], coords[i + 2], size, sweep, ToAbsolute(coords[i + 5], coords[i + 6], segments, isRelative))); } break; case 'l': // relative lineto case 'L': // lineto for (var i = 0; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto foreach (var value in coords) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(value, segments.Last.End.Y, segments, isRelative, false))); } break; case 'V': // vertical lineto case 'v': // relative vertical lineto foreach (var value in coords) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(segments.Last.End.X, value, segments, false, isRelative))); } break; case 'Q': // curveto case 'q': // relative curveto for (var i = 0; i < coords.Count; i += 4) { segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'T': // shorthand/smooth curveto case 't': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 2) { var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment; var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'C': // curveto case 'c': // relative curveto for (var i = 0; i < coords.Count; i += 6) { segments.Add(new SvgCubicCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative), ToAbsolute(coords[i + 4], coords[i + 5], segments, isRelative))); } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 4) { var lastCubicCurve = segments.Last as SvgCubicCurveSegment; var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgCubicCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'Z': // closepath case 'z': // relative closepath segments.Add(new SvgClosePathSegment()); break; } } } catch (Exception exc) { Trace.TraceError("Error parsing path \"{0}\": {1}", path, exc.Message); } return(segments); }
/// <summary> /// Parses the specified string into a collection of path segments. /// </summary> /// <param name="path">A <see cref="string"/> containing path data.</param> public static SvgPathSegmentList Parse(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } var segments = new SvgPathSegmentList(); try { List<float> coords; char command; bool isRelative; foreach (var commandSet in SplitCommands(path.TrimEnd(null))) { coords = new List<float>(ParseCoordinates(commandSet.Trim())); command = commandSet[0]; isRelative = char.IsLower(command); // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation switch (command) { case 'm': // relative moveto case 'M': // moveto segments.Add( new SvgMoveToSegment(ToAbsolute(coords[0], coords[1], segments, isRelative))); for (var i = 2; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'a': case 'A': SvgArcSize size; SvgArcSweep sweep; for (var i = 0; i < coords.Count; i += 7) { size = (coords[i + 3] != 0.0f) ? SvgArcSize.Large : SvgArcSize.Small; sweep = (coords[i + 4] != 0.0f) ? SvgArcSweep.Positive : SvgArcSweep.Negative; // A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y segments.Add(new SvgArcSegment(segments.Last.End, coords[i], coords[i + 1], coords[i + 2], size, sweep, ToAbsolute(coords[i + 5], coords[i + 6], segments, isRelative))); } break; case 'l': // relative lineto case 'L': // lineto for (var i = 0; i < coords.Count; i += 2) { segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'H': // horizontal lineto case 'h': // relative horizontal lineto foreach (var value in coords) segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(value, segments.Last.End.Y, segments, isRelative, false))); break; case 'V': // vertical lineto case 'v': // relative vertical lineto foreach (var value in coords) segments.Add(new SvgLineSegment(segments.Last.End, ToAbsolute(segments.Last.End.X, value, segments, false, isRelative))); break; case 'Q': // curveto case 'q': // relative curveto for (var i = 0; i < coords.Count; i += 4) { segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'T': // shorthand/smooth curveto case 't': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 2) { var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment; var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative))); } break; case 'C': // curveto case 'c': // relative curveto for (var i = 0; i < coords.Count; i += 6) { segments.Add(new SvgCubicCurveSegment(segments.Last.End, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative), ToAbsolute(coords[i + 4], coords[i + 5], segments, isRelative))); } break; case 'S': // shorthand/smooth curveto case 's': // relative shorthand/smooth curveto for (var i = 0; i < coords.Count; i += 4) { var lastCubicCurve = segments.Last as SvgCubicCurveSegment; var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End; segments.Add(new SvgCubicCurveSegment(segments.Last.End, controlPoint, ToAbsolute(coords[i], coords[i + 1], segments, isRelative), ToAbsolute(coords[i + 2], coords[i + 3], segments, isRelative))); } break; case 'Z': // closepath case 'z': // relative closepath segments.Add(new SvgClosePathSegment()); break; } } } catch (Exception exc) { // Trace.TraceError("Error parsing path \"{0}\": {1}", path, exc.Message); } return segments; }
public static IEnumerable <Curve> Curves(this SvgPathSegmentList pathSegments) => pathSegments.SplitCurves().SelectMany(c => c.Curves);
public void DrawPath(IEnumerable<IEnumerable<Point>> points, bool winding, GraphicsContext context, DeviceDescription description) { var svgContext = _mapper.MapGraphicsContextToSvg(context); foreach (var point in points) { var vertices = point.ToList(); var list = new SvgPathSegmentList(); var first = vertices.First(); list.Add(new SvgMoveToSegment(new PointF((float)first.X, (float)first.Y))); foreach (var vertex in vertices.Skip(1)) { list.Add(new SvgLineSegment(new PointF((float)first.X, (float)first.Y), new PointF((float)vertex.X, (float)vertex.Y))); first = vertex; } list.Add(new SvgClosePathSegment()); AddChild(new SvgPath { Fill = svgContext.Fill, FillOpacity = svgContext.Opacity, PathData = list, Stroke = svgContext.Pen.Stroke, StrokeDashArray = svgContext.Pen.StrokeDashArray, StrokeLineCap = svgContext.Pen.StrokeLineCap, StrokeLineJoin = svgContext.Pen.StrokeLineJoin, StrokeWidth = svgContext.Pen.StrokeWidth }); } }
/// <summary> /// Creates point with absolute coorindates. /// </summary> /// <param name="x">Raw X-coordinate value.</param> /// <param name="y">Raw Y-coordinate value.</param> /// <param name="segments">Current path segments.</param> /// <param name="isRelativeX"><b>true</b> if <paramref name="x"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <param name="isRelativeY"><b>true</b> if <paramref name="y"/> contains relative coordinate value, otherwise <b>false</b>.</param> /// <returns><see cref="PointF"/> that contains absolute coordinates.</returns> private static PointF ToAbsolute(float x, float y, SvgPathSegmentList segments, bool isRelativeX, bool isRelativeY) { var point = new PointF(x, y); if ((isRelativeX || isRelativeY) && segments.Count > 0) { var lastSegment = segments.Last; if (isRelativeX) { point.X += lastSegment.End.X; } if (isRelativeY) { point.Y += lastSegment.End.Y; } } return point; }
public static SvgPathSegmentList NormalizeAndTruncate(this SvgPathSegmentList path, out DoubleMatrix transform, double halfSize = 2048) { // Calculate the bounding box of the path var points = path.Curves().SelectMany(c => c.EnclosingPolygon).ToArray(); // Don't normalize an empty path if (points.Length == 0) { transform = DoubleMatrix.Identity; return(path); } var minx = double.PositiveInfinity; var maxx = double.NegativeInfinity; var miny = double.PositiveInfinity; var maxy = double.NegativeInfinity; // Find the bounding box foreach (var pt in points) { minx = Math.Min(minx, pt.X); maxx = Math.Max(maxx, pt.X); miny = Math.Min(miny, pt.Y); maxy = Math.Max(maxy, pt.Y); } // Calculate the points var center = new Double2(minx + maxx, miny + maxy) / 2; // And the half-sizes var hx = (maxx - minx) / 2; var hy = (maxy - miny) / 2; // Chose the maximum value of it var d = halfSize / Math.Max(hx, hy); var dm = Double2.Zero; // And transform the path var newPath = new SvgPathSegmentList(); foreach (var segment in path) { if (segment is SvgMoveToSegment move) { newPath.Add(new SvgMoveToSegment(Transform(move.Start))); } else if (segment is SvgLineSegment line) { newPath.Add(new SvgLineSegment(Transform(line.Start), Transform(line.End))); } else if (segment is SvgQuadraticCurveSegment quad) { newPath.Add(new SvgQuadraticCurveSegment(Transform(quad.Start), Transform(quad.ControlPoint), Transform(quad.End))); } else if (segment is SvgCubicCurveSegment cubic) { newPath.Add(new SvgCubicCurveSegment(Transform(cubic.Start), Transform(cubic.FirstControlPoint), Transform(cubic.SecondControlPoint), Transform(cubic.End))); } else if (segment is SvgArcSegment arc) { newPath.Add(new SvgArcSegment(Transform(arc.Start), (float)(arc.RadiusX * d).Truncate(), (float)(arc.RadiusY * d).Truncate(), arc.Angle, arc.Size, arc.Sweep, Transform(arc.End))); } else if (segment is SvgClosePathSegment) { newPath.Add(new SvgClosePathSegment()); } } // Mount the inverse matrix transform = new DoubleMatrix(1 / d, 0, 0, 1 / d, center.X, center.Y); return(newPath); System.Drawing.PointF Transform(System.Drawing.PointF point) { var pt = (point.ToDouble2() * d - center * d).Truncate() + dm; return(new System.Drawing.PointF((float)pt.X, (float)pt.Y)); } }