using Microsoft.Xna.Framework; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Xml; namespace FSO.Common.WorldGeometry.Paths { public enum SVGPathSegmentType { MoveTo, LineTo, CurveTo, Close } public class SVGPathSegment { public SVGPathSegmentType Type; public Vector2 Position; public Vector2 ControlPoint1; public Vector2 ControlPoint2; } public class SVGPath { public string ID; public List Segments; public SVGPath(string id, List segs) { ID = id; Segments = segs; } } public class SVGParser { public List Paths; public SVGParser(string svgText) { var xml = new XmlDocument(); xml.XmlResolver = null; xml.LoadXml(svgText); Paths = new List(); var paths = xml.GetElementsByTagName("path"); foreach (XmlNode path in paths) { var str = path.Attributes["d"].InnerText.Replace(',', ' '); int template = 0; var id = path.Attributes["id"]?.InnerText; var elems = str.Split(' '); var pos = new Vector2(0, 0); var newPath = new List(); for (int i = 0; i < elems.Length; i += 0) { var type = elems[i++]; if (type.Length == 0) continue; var relative = char.IsLower(type[0]); if (!relative) pos = new Vector2(); switch (type.ToLower()) { case "m": case "l": //lineto pos += new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture)); newPath.Add(new SVGPathSegment() { Position = pos, Type = (type.ToLower() == "l") ? SVGPathSegmentType.LineTo : SVGPathSegmentType.MoveTo }); break; case "c": var cp1 = new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture)) + pos; var cp2 = new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture)) + pos; pos += new Vector2(float.Parse(elems[i++], CultureInfo.InvariantCulture), float.Parse(elems[i++], CultureInfo.InvariantCulture)); newPath.Add(new SVGPathSegment() { Position = pos, ControlPoint1 = cp1, ControlPoint2 = cp2, Type = SVGPathSegmentType.CurveTo }); break; case "z": //close newPath.Add(new SVGPathSegment() { Type = SVGPathSegmentType.Close }); break; } } Paths.Add(new SVGPath(id, newPath)); } } public LinePath ToLinePath(SVGPath inpath) { var segs = inpath.Segments; var line = new List(); var closed = false; var pos = new Vector2(0, 0); foreach (var seg in segs) { switch (seg.Type) { case SVGPathSegmentType.MoveTo: case SVGPathSegmentType.LineTo: line.Add(seg.Position); break; case SVGPathSegmentType.CurveTo: //subdivided curve. currently 20 subdivisions. var subdiv = 20; var lastPos = line.Last(); for (int i=1; i 0) { var first = path.Segments.First(); var last = path.Segments.Last(); first.StartNormal = last.EndNormal = (first.StartNormal + last.EndNormal) / 2; path.SharpEnd = true; path.SharpStart = true; } return path; } public List ToLinePaths() { return Paths.Select(x => ToLinePath(x)).ToList(); } } }