Browse Source

Added support for Polyline and Polygon

Krzysztof Krysiński 2 months ago
parent
commit
a2237fa6d1

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 13d7c363f5bee90cbde15f1c98cf1744b2bcd493
+Subproject commit 3b9c90113ed6b7600d1b80828d3ddf1d70ead90a

+ 22 - 0
src/PixiEditor.SVG/Elements/SvgPolygon.cs

@@ -0,0 +1,22 @@
+using System.Xml;
+using Drawie.Numerics;
+using PixiEditor.SVG.Units;
+
+namespace PixiEditor.SVG.Elements;
+
+public class SvgPolygon() : SvgPrimitive("polygon")
+{
+    public SvgProperty<SvgStringUnit> RawPoints { get; } = new SvgProperty<SvgStringUnit>("points");
+    public SvgProperty<SvgNumericUnit> PathLength { get; set; } = new SvgProperty<SvgNumericUnit>("pathLength");
+
+    protected override IEnumerable<SvgProperty> GetProperties()
+    {
+        yield return RawPoints;
+        yield return PathLength;
+    }
+
+    public VecD[] GetPoints()
+    {
+        return SvgPolyline.GetPoints(RawPoints.Unit?.Value ?? string.Empty);
+    }
+}

+ 78 - 0
src/PixiEditor.SVG/Elements/SvgPolyline.cs

@@ -0,0 +1,78 @@
+using System.Xml;
+using Drawie.Numerics;
+using PixiEditor.SVG.Units;
+
+namespace PixiEditor.SVG.Elements;
+
+public class SvgPolyline() : SvgPrimitive("polyline")
+{
+    public SvgProperty<SvgStringUnit> RawPoints { get; } = new SvgProperty<SvgStringUnit>("points");
+    public SvgProperty<SvgNumericUnit> PathLength { get; set; } = new SvgProperty<SvgNumericUnit>("pathLength");
+
+    protected override IEnumerable<SvgProperty> GetProperties()
+    {
+        yield return RawPoints;
+        yield return PathLength;
+    }
+
+    public VecD[] GetPoints()
+    {
+        return GetPoints(RawPoints.Unit?.Value ?? string.Empty);
+    }
+
+    public static VecD[] GetPoints(string input)
+    {
+        if (string.IsNullOrWhiteSpace(input))
+        {
+            return [];
+        }
+
+        double? x = null;
+        bool nextSpaceIsSeparator = false;
+        string currentNumberString = string.Empty;
+
+        List<VecD> points = new List<VecD>();
+        foreach (char character in input)
+        {
+            if (char.IsWhiteSpace(character))
+            {
+                if (nextSpaceIsSeparator)
+                {
+                    if (x.HasValue)
+                    {
+                        points.Add(new VecD(x.Value, double.Parse(currentNumberString)));
+                        x = null;
+                        currentNumberString = string.Empty;
+                    }
+
+                    nextSpaceIsSeparator = false;
+                }
+            }
+            else if (char.IsDigit(character) || character == '.' || character == '-' || character == '+')
+            {
+                currentNumberString += character;
+                nextSpaceIsSeparator = x.HasValue;
+            }
+            else if (character == ',')
+            {
+                x = double.Parse(currentNumberString);
+                currentNumberString = string.Empty;
+                nextSpaceIsSeparator = false;
+            }
+        }
+
+        if (currentNumberString.Length > 0)
+        {
+            if (x.HasValue)
+            {
+                points.Add(new VecD(x.Value, double.Parse(currentNumberString)));
+            }
+            else
+            {
+                points.Add(new VecD(double.Parse(currentNumberString), 0));
+            }
+        }
+
+        return points.ToArray();
+    }
+}

+ 2 - 15
src/PixiEditor.SVG/SvgElement.cs

@@ -113,20 +113,7 @@ public class SvgElement(string tagName)
 
     private void ParseAttribute(SvgProperty property, XmlReader reader, SvgDefs defs)
     {
-        if (property is SvgList list)
-        {
-            ParseListProperty(list, reader, defs);
-        }
-        else
-        {
-            property.Unit ??= property.CreateDefaultUnit();
-            property.Unit.ValuesFromXml(reader.Value, defs);
-        }
-    }
-
-    private void ParseListProperty(SvgList list, XmlReader reader, SvgDefs defs)
-    {
-        list.Unit ??= list.CreateDefaultUnit();
-        list.Unit.ValuesFromXml(reader.Value, defs);
+        property.Unit ??= property.CreateDefaultUnit();
+        property.Unit.ValuesFromXml(reader.Value, defs);
     }
 }

+ 5 - 18
src/PixiEditor.SVG/SvgParser.cs

@@ -1,4 +1,5 @@
 using System.Globalization;
+using System.Reflection;
 using System.Xml;
 using System.Xml.Linq;
 using Drawie.Numerics;
@@ -10,24 +11,10 @@ namespace PixiEditor.SVG;
 
 public class SvgParser
 {
-    private static Dictionary<string, Type> wellKnownElements = new()
-    {
-        { "ellipse", typeof(SvgEllipse) },
-        { "rect", typeof(SvgRectangle) },
-        { "circle", typeof(SvgCircle) },
-        { "line", typeof(SvgLine) },
-        { "path", typeof(SvgPath) },
-        { "g", typeof(SvgGroup) },
-        { "mask", typeof(SvgMask) },
-        { "image", typeof(SvgImage) },
-        { "svg", typeof(SvgDocument) },
-        { "text", typeof(SvgText) },
-        { "linearGradient", typeof(SvgLinearGradient) },
-        { "radialGradient", typeof(SvgRadialGradient) },
-        { "stop", typeof(SvgStop) },
-        { "defs", typeof(SvgDefs) },
-        { "clipPath", typeof(SvgClipPath) }
-    };
+    private static Dictionary<string, Type> wellKnownElements = Assembly.GetExecutingAssembly()
+        .GetTypes()
+        .Where(t => t.IsSubclassOf(typeof(SvgElement)) && !t.IsAbstract)
+        .ToDictionary(t => (Activator.CreateInstance(t) as SvgElement).TagName, t => t);
 
     public string Source { get; set; }
 

+ 2 - 0
src/PixiEditor.SVG/SvgProperty.cs

@@ -20,6 +20,8 @@ public abstract class SvgProperty
     public ISvgUnit? Unit { get; set; }
     public string? SvgFullName => NamespaceName == null ? SvgName : $"{NamespaceName}:{SvgName}";
 
+    protected Type? TypeToCreate { get; set; }
+
     public ISvgUnit? CreateDefaultUnit()
     {
         var genericType = this.GetType().GetGenericArguments();

+ 0 - 19
src/PixiEditor.SVG/Units/SvgList.cs

@@ -1,19 +0,0 @@
-namespace PixiEditor.SVG.Units;
-
-public class SvgList : SvgProperty
-{
-    public char Separator { get; set; }
-    public SvgList(string svgName, char separator) : base(svgName)
-    {
-        Separator = separator;
-    }
-}
-
-public class SvgList<T> : SvgList where T : ISvgUnit
-{
-
-    public SvgList(string svgName, char separator, params T[] units) : base(svgName, separator)
-    {
-        
-    }
-}

+ 50 - 0
src/PixiEditor.SVG/Units/SvgVectorUnit.cs

@@ -0,0 +1,50 @@
+using Drawie.Numerics;
+using PixiEditor.SVG.Elements;
+
+namespace PixiEditor.SVG.Units;
+
+public class SvgVectorUnit : ISvgUnit
+{
+    public SvgNumericUnit X { get; set; }
+    public SvgNumericUnit Y { get; set; }
+
+    public SvgVectorUnit()
+    {
+        X = new SvgNumericUnit();
+        Y = new SvgNumericUnit();
+    }
+
+    public SvgVectorUnit(VecD vector)
+    {
+        X = new SvgNumericUnit(vector.X, "");
+        Y = new SvgNumericUnit(vector.Y, "");
+    }
+
+    public string ToXml(DefStorage defs)
+    {
+        string xValue = X.ToXml(defs);
+        string yValue = Y.ToXml(defs);
+
+        return $"{xValue},{yValue}";
+    }
+
+    public void ValuesFromXml(string readerValue, SvgDefs defs)
+    {
+        if (string.IsNullOrEmpty(readerValue))
+        {
+            X = new SvgNumericUnit();
+            Y = new SvgNumericUnit();
+            return;
+        }
+
+        string[] values =
+            readerValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+        if (values.Length != 2)
+        {
+            throw new FormatException("Invalid vector format. Expected two values.");
+        }
+
+        X.ValuesFromXml(values[0], defs);
+        Y.ValuesFromXml(values[1], defs);
+    }
+}

+ 19 - 0
src/PixiEditor/Models/IO/CustomDocumentFormats/SvgDocumentBuilder.cs

@@ -102,6 +102,11 @@ internal class SvgDocumentBuilder : IDocumentBuilder
             shapeData = AddText(text);
             name = TextToolViewModel.NewLayerKey;
         }
+        else if (element is SvgPolyline or SvgPolygon)
+        {
+            shapeData = AddPoly(element);
+            name = VectorPathToolViewModel.NewLayerKey;
+        }
 
         name = element.Id.Unit?.Value ?? name;
 
@@ -376,6 +381,20 @@ internal class SvgDocumentBuilder : IDocumentBuilder
         };
     }
 
+    private PathVectorData AddPoly(SvgElement element)
+    {
+        if (element is SvgPolyline polyline)
+        {
+            return new PathVectorData(VectorPath.FromPoints(polyline.GetPoints(), false));
+        }
+        if (element is SvgPolygon polygon)
+        {
+            return new PathVectorData(VectorPath.FromPoints(polygon.GetPoints(), true));
+        }
+
+        return null;
+    }
+
     private void AddCommonShapeData(ShapeVectorData? shapeData, StyleContext styleContext)
     {
         if (shapeData == null)