Browse Source

Added serialization factory

flabbet 8 months ago
parent
commit
de74cf7d76

+ 9 - 0
src/PixiEditor/Models/Serialization/Factories/ByteBuilder.cs

@@ -66,4 +66,13 @@ public class ByteBuilder
             AddVecD(point);
         }
     }
+
+    public void AddString(string str)
+    {
+        AddInt(str.Length);
+        foreach (var c in str)
+        {
+            AddInt(c);
+        }
+    }
 }

+ 15 - 1
src/PixiEditor/Models/Serialization/Factories/ByteExtractor.cs

@@ -1,4 +1,5 @@
-using Drawie.Backend.Core.ColorsImpl;
+using System.Text;
+using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Numerics;
 
@@ -81,4 +82,17 @@ public class ByteExtractor
         
         return value;
     }
+
+    public string GetString()
+    {
+        int length = GetInt();
+        StringBuilder builder = new StringBuilder();
+        
+        for (int i = 0; i < length; i++)
+        {
+            builder.Append((char)GetInt());
+        }
+        
+        return builder.ToString();
+    }
 }

+ 33 - 0
src/PixiEditor/Models/Serialization/Factories/VectorPathSerializationFactory.cs

@@ -0,0 +1,33 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Vector;
+using Drawie.Numerics;
+
+namespace PixiEditor.Models.Serialization.Factories;
+
+internal class VectorPathSerializationFactory : VectorShapeSerializationFactory<PathVectorData> 
+{
+    public override string DeserializationId { get; } = "PixiEditor.PathData";
+
+    protected override void AddSpecificData(ByteBuilder builder, PathVectorData original)
+    {
+        builder.AddString(original.Path.ToSvgPathData());
+    }
+
+    protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
+        int strokeWidth, out PathVectorData original)
+    {
+        string path = extractor.GetString();
+
+        original = new PathVectorData(VectorPath.FromSvgPath(path))
+        {
+            StrokeColor = strokeColor,
+            FillColor = fillColor,
+            StrokeWidth = strokeWidth,
+            TransformationMatrix = matrix
+        };
+
+        return true;
+    }
+}

+ 37 - 40
src/PixiEditor/Views/Overlays/PathOverlay/VectorPathOverlay.cs

@@ -174,28 +174,27 @@ public class VectorPathOverlay : Overlay
         {
             if (totalHandles > pointsCount)
             {
-                RecreateHandles();
+                RemoveAllHandles();
             }
-            else
+
+            int missingControlPoints = CalculateMissingControlPoints(controlPointHandles.Count);
+            int missingAnchors = GetAnchorCount() - anchorHandles.Count;
+            for (int i = 0; i < missingAnchors; i++)
             {
-                int missingControlPoints = CalculateMissingControlPoints(controlPointHandles.Count);
-                int missingAnchors = GetAnchorCount() - anchorHandles.Count;
-                for (int i = 0; i < missingAnchors; i++)
-                {
-                    CreateHandle(anchorHandles.Count);
-                }
+                CreateHandle(anchorHandles.Count);
+            }
 
-                for (int i = 0; i < missingControlPoints; i++)
-                {
-                    CreateHandle(controlPointHandles.Count, true);
-                }
+            for (int i = 0; i < missingControlPoints; i++)
+            {
+                CreateHandle(controlPointHandles.Count, true);
+            }
 
 
-                SelectAnchor(GetHandleAt(pointsCount - 1));
-            }
+            SelectAnchor(GetHandleAt(pointsCount - 1));
 
             ConnectControlPointsToAnchors();
         }
+        Refresh();
     }
 
     private void ConnectControlPointsToAnchors()
@@ -206,13 +205,21 @@ public class VectorPathOverlay : Overlay
         {
             if (data.verb == PathVerb.Cubic)
             {
-                AnchorHandle previousAnchor = anchorHandles.ElementAtOrDefault(anchorIndex - 1);
-                if (anchorIndex >= anchorHandles.Count)
+                int targetAnchorIndex1 = anchorIndex - 1;
+                if (targetAnchorIndex1 < 0)
+                {
+                    targetAnchorIndex1 = anchorHandles.Count - 1;
+                }
+
+                AnchorHandle previousAnchor = anchorHandles.ElementAtOrDefault(targetAnchorIndex1);
+
+                int targetAnchorIndex2 = anchorIndex;
+                if (targetAnchorIndex2 >= anchorHandles.Count)
                 {
-                    anchorIndex = 0;
+                    targetAnchorIndex2 = 0;
                 }
 
-                AnchorHandle nextAnchor = anchorHandles.ElementAtOrDefault(anchorIndex);
+                AnchorHandle nextAnchor = anchorHandles.ElementAtOrDefault(targetAnchorIndex2);
 
                 if (previousAnchor != null)
                 {
@@ -242,7 +249,7 @@ public class VectorPathOverlay : Overlay
         return totalControlPoints - handleCount;
     }
 
-    private void RecreateHandles()
+    private void RemoveAllHandles()
     {
         int previouslySelectedIndex = -1;
 
@@ -263,24 +270,16 @@ public class VectorPathOverlay : Overlay
 
         for (int i = controlPointHandles.Count - 1; i >= 0; i--)
         {
+            var handle = controlPointHandles[i];
+            handle.OnDrag -= OnControlPointDrag;
+            handle.OnRelease -= OnHandleRelease;
+
             Handles.Remove(controlPointHandles[i]);
         }
 
         anchorHandles.Clear();
         controlPointHandles.Clear();
         SnappingController.RemoveAll("editingPath");
-
-        foreach (var path in Path)
-        {
-            if (path.verb == PathVerb.Close) continue;
-
-            CreateHandle(anchorHandles.Count);
-            if (path.verb == PathVerb.Cubic)
-            {
-                CreateHandle(controlPointHandles.Count, true);
-                CreateHandle(controlPointHandles.Count, true);
-            }
-        }
     }
 
     private bool IsOverAnyHandle()
@@ -332,6 +331,7 @@ public class VectorPathOverlay : Overlay
         {
             var controlPoint = new ControlPointHandle(this);
             controlPoint.OnDrag += OnControlPointDrag;
+            controlPoint.OnRelease += OnHandleRelease;
             controlPointHandles.Add(controlPoint);
             AddHandle(controlPoint);
         }
@@ -415,7 +415,7 @@ public class VectorPathOverlay : Overlay
                     newPath.CubicTo(data.points[0], data.points[1], data.points[1]);
                     convertNextToCubic = true;
                 }
-                else if(i + 1 == index)
+                else if (i + 1 == index)
                 {
                     newPath.CubicTo(data.points[0], data.points[1], data.points[1]);
                 }
@@ -672,17 +672,14 @@ public class VectorPathOverlay : Overlay
 
     private void OnHandleRelease(Handle source, OverlayPointerArgs args)
     {
-        if (source is not AnchorHandle anchorHandle)
-        {
-            return;
-        }
-
         AddToUndoCommand.Execute(Path);
 
-        SnappingController.AddXYAxis($"editingPath[{anchorHandles.IndexOf(anchorHandle)}]", () => source.Position);
+        if (source is AnchorHandle anchorHandle)
+        {
+            SnappingController.AddXYAxis($"editingPath[{anchorHandles.IndexOf(anchorHandle)}]", () => source.Position);
+        }
 
-        SnappingController.HighlightedXAxis = null;
-        SnappingController.HighlightedYAxis = null;
+        TryHighlightSnap(null, null);
 
         Refresh();
     }

+ 34 - 4
tests/PixiEditor.Backend.Tests/NodeSystemTests.cs

@@ -5,6 +5,7 @@ using Drawie.Interop.VulkanAvalonia;
 using Drawie.Skia;
 using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Changes.NodeGraph;
 using PixiEditor.Models.Serialization;
@@ -109,11 +110,11 @@ public class NodeSystemTests
         var allFoundFactories = typeof(SerializationFactory).Assembly.GetTypes()
             .Where(x => x.IsAssignableTo(typeof(SerializationFactory))
                         && x is { IsAbstract: false, IsInterface: false }).ToList();
-        
+
         List<SerializationFactory> factories = new();
         QoiEncoder encoder = new QoiEncoder();
         SerializationConfig config = new SerializationConfig(encoder);
-        
+
         foreach (var factoryType in allFoundFactories)
         {
             var factory = (SerializationFactory)Activator.CreateInstance(factoryType);
@@ -130,16 +131,45 @@ public class NodeSystemTests
             foreach (var input in node.InputProperties)
             {
                 if (knownNonSerializableTypes.Contains(input.ValueType)) continue;
-                if(input.ValueType.IsAbstract) continue;
+                if (input.ValueType.IsAbstract) continue;
                 if (input.ValueType.IsAssignableTo(typeof(Delegate))) continue;
                 bool hasFactory = factories.Any(x => x.OriginalType == input.ValueType);
                 Assert.True(
-                    input.ValueType.IsValueType || input.ValueType == typeof(string) || hasFactory, 
+                    input.ValueType.IsValueType || input.ValueType == typeof(string) || hasFactory,
                     $"{input.ValueType} doesn't have a factory and is not serializable. Property: {input.InternalPropertyName}, NodeType: {node.GetType().Name}");
             }
         }
     }
 
+    [Fact]
+    public void TestThatAllVectorDataTypesHaveSerializationFactories()
+    {
+        var allVectorTypes = typeof(ShapeVectorData).Assembly.GetTypes()
+            .Where(x => x.IsAssignableTo(typeof(ShapeVectorData))
+                        && x is { IsAbstract: false, IsInterface: false }).ToList();
+
+        QoiEncoder encoder = new QoiEncoder();
+        SerializationConfig config = new SerializationConfig(encoder);
+
+        var factoryTypes = typeof(SerializationFactory).Assembly.GetTypes()
+            .Where(x => x.IsAssignableTo(typeof(SerializationFactory))
+                        && x is { IsAbstract: false, IsInterface: false }).ToList();
+
+        List<SerializationFactory> factories = new();
+        
+        foreach (var factoryType in factoryTypes)
+        {
+            var factory = (SerializationFactory)Activator.CreateInstance(factoryType);
+            factories.Add(factory);
+        }
+        
+        foreach (var type in allVectorTypes)
+        {
+            bool hasFactory = factories.Any(x => x.OriginalType == type);
+            Assert.True(hasFactory, $"{type} doesn't have a factory.");
+        }
+    }
+
     private static List<Type> GetNodeTypesWithoutPairs()
     {
         var allNodeTypes = typeof(Node).Assembly.GetTypes()