Pārlūkot izejas kodu

Merge pull request #632 from PixiEditor/shape-nodes

Shape nodes logic
Krzysztof Krysiński 11 mēneši atpakaļ
vecāks
revīzija
cc7732e1d2
38 mainītis faili ar 736 papildinājumiem un 313 dzēšanām
  1. 1 1
      pipelines/Linux/tests-ubuntu.yml
  2. 1 1
      pipelines/MacOS/tests-macos.yml
  3. 1 1
      pipelines/Windows/tests-windows.yml
  4. 0 80
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EllipseNode.cs
  5. 0 31
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Points/PointList.cs
  6. 0 50
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Points/RasterizePointsNode.cs
  7. 53 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseData.cs
  8. 49 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsData.cs
  9. 24 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/ShapeData.cs
  10. 12 20
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/DistributePointsNode.cs
  11. 35 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/EllipseNode.cs
  12. 43 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RasterizeShape.cs
  13. 22 21
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RemoveClosePointsNode.cs
  14. 39 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/ShapeNode.cs
  15. 0 33
      src/PixiEditor.ChangeableDocument/Changeables/Graph/PropertyValidator.cs
  16. 10 0
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConnectProperties_Change.cs
  17. 3 1
      src/PixiEditor.ChangeableDocument/Rendering/RenderingContext.cs
  18. 4 4
      src/PixiEditor.Desktop/PixiEditor.Desktop.csproj
  19. BIN
      src/PixiEditor.Extensions.Sdk/build/PixiEditor.Api.CGlueMSBuild.dll
  20. BIN
      src/PixiEditor.Extensions.Sdk/build/PixiEditor.Extensions.MSPackageBuilder.dll
  21. 12 3
      src/PixiEditor.UI.Common/Accents/Base.axaml
  22. 3 2
      src/PixiEditor/Data/Localization/Languages/en.json
  23. 2 2
      src/PixiEditor/Fonts/NodeIcons.cs
  24. 22 0
      src/PixiEditor/Helpers/Converters/SocketColorConverter.cs
  25. 13 1
      src/PixiEditor/Helpers/SerializationUtil.cs
  26. 0 1
      src/PixiEditor/Helpers/ServiceCollectionHelpers.cs
  27. 58 0
      src/PixiEditor/Models/Serialization/Factories/ByteBuilder.cs
  28. 60 0
      src/PixiEditor/Models/Serialization/Factories/ByteExtractor.cs
  29. 47 0
      src/PixiEditor/Models/Serialization/Factories/EllipseSerializationFactory.cs
  30. 0 27
      src/PixiEditor/Models/Serialization/Factories/PointListSerializationFactory.cs
  31. 42 0
      src/PixiEditor/Models/Serialization/Factories/PointsDataSerializationFactory.cs
  32. 4 3
      src/PixiEditor/Styles/Templates/ConnectionView.axaml
  33. 1 1
      src/PixiEditor/Styles/Templates/NodeSocket.axaml
  34. 22 2
      src/PixiEditor/Views/Nodes/NodeGraphView.cs
  35. 1 6
      tests/PixiEditor.Backend.Tests/MockDocument.cs
  36. 1 0
      tests/PixiEditor.Backend.Tests/NodeSystemTests.cs
  37. 1 1
      tests/PixiEditor.Backend.Tests/PixiEditor.Backend.Tests.csproj
  38. 150 21
      tests/PixiEditorTests.sln

+ 1 - 1
pipelines/Linux/tests-ubuntu.yml

@@ -71,4 +71,4 @@ steps:
   inputs:
     command: test
     projects: '**/*Tests/*.csproj'
-    arguments: '--configuration $(buildConfiguration)'
+    arguments: '--configuration $(buildConfiguration) -r $(buildPlatform)'

+ 1 - 1
pipelines/MacOS/tests-macos.yml

@@ -71,4 +71,4 @@ steps:
   inputs:
     command: test
     projects: '**/*Tests/*.csproj'
-    arguments: '--configuration $(buildConfiguration)'
+    arguments: '--configuration $(buildConfiguration) -r $(buildPlatform)'

+ 1 - 1
pipelines/Windows/tests-windows.yml

@@ -71,4 +71,4 @@ steps:
   inputs:
     command: test
     projects: '**/*Tests/*.csproj'
-    arguments: '--configuration $(buildConfiguration)'
+    arguments: '--configuration $(buildConfiguration) -r $(buildPlatform)'

+ 0 - 80
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EllipseNode.cs

@@ -1,80 +0,0 @@
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
-
-namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
-
-[NodeInfo("Ellipse", "ELLIPSE_NODE", Category = "SHAPE")]
-public class EllipseNode : Node
-{
-    public InputProperty<VecI> Radius { get; }
-    public InputProperty<Color> StrokeColor { get; }
-    public InputProperty<Color> FillColor { get; }
-    public InputProperty<int> StrokeWidth { get; }
-    public OutputProperty<Texture> Output { get; }
-
-    private ChunkyImage? workingImage;
-    private Texture? targetSurface;
-
-    private VecI _lastRadius = new VecI(-1, -1);
-    private Color _lastStrokeColor = new Color(0, 0, 0, 0);
-    private Color _lastFillColor = new Color(0, 0, 0, 0);
-    private int _lastStrokeWidth = -1;
-    private Paint replacingPaint = new Paint() { BlendMode = BlendMode.Src };
-
-    public EllipseNode()
-    {
-        Radius = CreateInput<VecI>("Radius", "RADIUS", new VecI(32, 32)).WithRules(
-            v => v.Min(VecI.One));
-        StrokeColor = CreateInput<Color>("StrokeColor", "STROKE_COLOR", new Color(0, 0, 0, 255));
-        FillColor = CreateInput<Color>("FillColor", "FILL_COLOR", new Color(0, 0, 0, 255));
-        StrokeWidth = CreateInput<int>("StrokeWidth", "STROKE_WIDTH", 1);
-        Output = CreateOutput<Texture?>("Output", "OUTPUT", null);
-    }
-
-    protected override Texture? OnExecute(RenderingContext context)
-    {
-        var radius = Radius.Value;
-        VecI targetDimensions = radius * 2;
-
-        if (workingImage is null || workingImage.LatestSize.X != targetDimensions.X ||
-            workingImage.LatestSize.Y != targetDimensions.Y)
-        {
-            if (targetDimensions.X <= 0 || targetDimensions.Y <= 0)
-            {
-                Output.Value = null;
-                return null;
-            }
-
-            workingImage?.Dispose();
-            workingImage = new ChunkyImage(targetDimensions);
-
-            targetSurface = RequestTexture(0, targetDimensions);
-        }
-
-        if (radius != _lastRadius || StrokeColor.Value != _lastStrokeColor || FillColor.Value != _lastFillColor ||
-            StrokeWidth.Value != _lastStrokeWidth)
-        {
-            _lastRadius = radius;
-            _lastStrokeColor = StrokeColor.Value;
-            _lastFillColor = FillColor.Value;
-            _lastStrokeWidth = StrokeWidth.Value;
-
-            RectI location = new RectI(VecI.Zero, targetDimensions);
-            workingImage.EnqueueDrawEllipse(location, StrokeColor.Value, FillColor.Value, StrokeWidth.Value);
-            workingImage.CommitChanges();
-        }
-
-        workingImage.DrawMostUpToDateChunkOn(context.ChunkToUpdate, context.ChunkResolution,
-            targetSurface.DrawingSurface, VecI.Zero,
-            replacingPaint);
-
-        Output.Value = targetSurface;
-        return targetSurface;
-    }
-
-    public override Node CreateCopy() => new EllipseNode();
-}

+ 0 - 31
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Points/PointList.cs

@@ -1,31 +0,0 @@
-using PixiEditor.Common;
-using PixiEditor.Numerics;
-
-namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Points;
-
-public class PointList : List<VecD>, ICacheable, ICloneable
-{
-    public required int HashValue { get; set; }
-
-    public PointList()
-    {
-    }
-
-    public PointList(IEnumerable<VecD> collection) : base(collection)
-    {
-    }
-
-    public PointList(int capacity) : base(capacity)
-    {
-    }
-
-    public static PointList Empty { get; } = new(0) { HashValue = 0 };
-
-    public int GetCacheHash() => HashValue;
-    public object Clone()
-    {
-        var clone = new PointList(this) { HashValue = HashValue };
-        clone.HashValue = HashValue;
-        return clone;
-    }
-}

+ 0 - 50
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Points/RasterizePointsNode.cs

@@ -1,50 +0,0 @@
-using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Shaders.Generation;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
-
-namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Points;
-
-[NodeInfo("RasterizePoints", "RASTERIZE_POINTS", Category = "SHAPE")]
-public class RasterizePointsNode : Node
-{
-    private Paint _paint = new() { Color = Colors.White };
-
-    public OutputProperty<Texture> Image { get; }
-
-    public InputProperty<PointList> Points { get; }
-
-
-    public RasterizePointsNode()
-    {
-        Image = CreateOutput<Texture>("Image", "IMAGE", null);
-        Points = CreateInput("Points", "POINTS", PointList.Empty);
-    }
-
-    protected override Texture? OnExecute(RenderingContext context)
-    {
-        var points = Points.Value;
-
-        if (points.Count == 0)
-            return null;
-
-        var size = context.DocumentSize;
-        var image = RequestTexture(0, size);
-
-        image.DrawingSurface.Canvas.DrawPoints(
-            PointMode.Points, 
-            points.Select(x => new Point((float)x.X * size.X, (float)x.Y * size.Y)).ToArray(),
-            _paint);
-
-        Image.Value = image;
-        
-        return image;
-    }
-
-    public override Node CreateCopy() => new RasterizePointsNode();
-}

+ 53 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseData.cs

@@ -0,0 +1,53 @@
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+
+public class EllipseData : ShapeData
+{
+    public VecD Center { get; set; }
+    public VecD Radius { get; set; }
+
+    public EllipseData(VecD center, VecD radius)
+    {
+        Center = center;
+        Radius = radius;
+    }
+    
+    public override void Rasterize(DrawingSurface drawingSurface)
+    {
+        using ChunkyImage img = new ChunkyImage(new VecI((int)Radius.X * 2, (int)Radius.Y * 2));
+        RectI rect = new RectI((int)Center.X - (int)Radius.X, (int)Center.Y - (int)Radius.Y, (int)Radius.X * 2, (int)Radius.Y * 2);
+        
+        img.EnqueueDrawEllipse(rect, StrokeColor, FillColor, StrokeWidth);
+        img.CommitChanges();
+        
+        VecI pos = new VecI((int)(Center.X - Radius.X), (int)(Center.Y - Radius.Y));
+        img.DrawMostUpToDateRegionOn(rect, ChunkResolution.Full, drawingSurface, pos);
+    }
+
+    public override bool IsValid()
+    {
+        return Radius is { X: > 0, Y: > 0 };
+    }
+
+    public override int CalculateHash()
+    {
+        return HashCode.Combine(Center, Radius);
+    }
+
+    public override int GetCacheHash()
+    {
+        return CalculateHash();
+    }
+
+    public override object Clone()
+    {
+        return new EllipseData(Center, Radius)
+        {
+            StrokeColor = StrokeColor,
+            FillColor = FillColor,
+            StrokeWidth = StrokeWidth
+        };
+    }
+}

+ 49 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsData.cs

@@ -0,0 +1,49 @@
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+
+public class PointsData : ShapeData
+{
+    public List<VecD> Points { get; set; } = new();
+    
+    public PointsData(IEnumerable<VecD> points)
+    {
+        Points = new List<VecD>(points);
+    }
+    
+    public override void Rasterize(DrawingSurface drawingSurface)
+    {
+        using Paint paint = new Paint();
+        paint.Color = FillColor;
+        paint.StrokeWidth = StrokeWidth;
+        
+        drawingSurface.Canvas.DrawPoints(PointMode.Points, Points.Select(p => new Point((int)p.X, (int)p.Y)).ToArray(), paint);
+    }
+
+    public override bool IsValid()
+    {
+        return Points.Count > 0;
+    }
+
+    public override int GetCacheHash()
+    {
+        return CalculateHash();
+    }
+
+    public override int CalculateHash()
+    {
+        return Points.GetHashCode();
+    }
+
+    public override object Clone()
+    {
+        return new PointsData(Points)
+        {
+            StrokeColor = StrokeColor,
+            FillColor = FillColor,
+            StrokeWidth = StrokeWidth
+        };
+    }
+}

+ 24 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/ShapeData.cs

@@ -0,0 +1,24 @@
+using PixiEditor.Common;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Surfaces;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+
+public abstract class ShapeData : ICacheable, ICloneable
+{
+    public Color StrokeColor { get; set; } = Colors.White;
+    public Color FillColor { get; set; } = Colors.White;
+    public int StrokeWidth { get; set; } = 1;
+
+    public abstract void Rasterize(DrawingSurface drawingSurface);
+    public abstract bool IsValid();
+
+    public abstract int GetCacheHash();
+    public abstract int CalculateHash();
+    public abstract object Clone();
+
+    public override int GetHashCode()
+    {
+        return CalculateHash();
+    }
+}

+ 12 - 20
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Points/DistributePointsNode.cs → src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/DistributePointsNode.cs

@@ -1,51 +1,43 @@
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.Numerics;
+using ShapeData = PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data.ShapeData;
 
-namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Points;
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
 
 [NodeInfo("DistributePoints", "DISTRIBUTE_POINTS", Category = "SHAPE")]
-public class DistributePointsNode : Node
+public class DistributePointsNode : ShapeNode<PointsData>
 {
-    public OutputProperty<PointList> Points { get; }
-
     public InputProperty<int> MaxPointCount { get; }
 
     public InputProperty<int> Seed { get; }
 
     public DistributePointsNode()
     {
-        Points = CreateOutput(nameof(Points), "POINTS", PointList.Empty);
-
         MaxPointCount = CreateInput("MaxPointCount", "MAX_POINTS", 10).
             WithRules(v => v.Min(1));
         Seed = CreateInput("Seed", "SEED", 0);
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override PointsData? GetShapeData(RenderingContext context)
     {
-        Points.Value = GetPointsRandomly();
-        
-        return null;
+        return GetPointsRandomly(context.DocumentSize);
     }
 
-    private PointList GetPointsRandomly()
+    private PointsData GetPointsRandomly(VecI size)
     {
         var seed = Seed.Value;
         var random = new Random(seed);
         var pointCount = MaxPointCount.Value;
-        var finalPoints = new PointList(pointCount)
-        {
-            HashValue = HashCode.Combine(pointCount, seed)
-        };
 
+        List<VecD> finalPoints = new List<VecD>(pointCount);
         for (int i = 0; i < pointCount; i++)
         {
-            finalPoints.Add(new VecD(random.NextDouble(), random.NextDouble()));
+            finalPoints.Add(new VecD(random.NextDouble() * size.X, random.NextDouble() * size.Y));
         }
         
-        return finalPoints;
+        var shapeData = new PointsData(finalPoints);
+        return shapeData;
     }
 
     public override Node CreateCopy() => new DistributePointsNode();

+ 35 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/EllipseNode.cs

@@ -0,0 +1,35 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.Numerics;
+using ShapeData = PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data.ShapeData;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+
+[NodeInfo("Ellipse", "ELLIPSE_NODE", Category = "SHAPE")]
+public class EllipseNode : ShapeNode<EllipseData>
+{
+    public InputProperty<VecD> Position { get; }
+    public InputProperty<VecD> Radius { get; }
+    public InputProperty<Color> StrokeColor { get; }
+    public InputProperty<Color> FillColor { get; }
+    public InputProperty<int> StrokeWidth { get; }
+
+    public EllipseNode()
+    {
+        Position = CreateInput<VecD>("Position", "POSITION", VecI.Zero);
+        Radius = CreateInput<VecD>("Radius", "RADIUS", new VecD(32, 32)).WithRules(
+            v => v.Min(new VecD(1)));
+        StrokeColor = CreateInput<Color>("StrokeColor", "STROKE_COLOR", new Color(0, 0, 0, 255));
+        FillColor = CreateInput<Color>("FillColor", "FILL_COLOR", new Color(0, 0, 0, 255));
+        StrokeWidth = CreateInput<int>("StrokeWidth", "STROKE_WIDTH", 1);
+    }
+
+    protected override EllipseData? GetShapeData(RenderingContext context)
+    {
+        return new EllipseData(Position.Value, Radius.Value)
+            { StrokeColor = StrokeColor.Value, FillColor = FillColor.Value, StrokeWidth = StrokeWidth.Value };
+    }
+
+    public override Node CreateCopy() => new EllipseNode();
+}

+ 43 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RasterizeShape.cs

@@ -0,0 +1,43 @@
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using PixiEditor.Numerics;
+using ShapeData = PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data.ShapeData;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+
+[NodeInfo("RasterizeShape", "RASTERIZE_SHAPE", Category = "SHAPE")]
+public class RasterizeShape : Node
+{
+    public OutputProperty<Texture> Image { get; }
+
+    public InputProperty<ShapeData> Data { get; }
+
+
+    public RasterizeShape()
+    {
+        Image = CreateOutput<Texture>("Image", "IMAGE", null);
+        Data = CreateInput<ShapeData>("Points", "SHAPE", null);
+    }
+
+    protected override Texture? OnExecute(RenderingContext context)
+    {
+        var shape = Data.Value;
+
+        if (shape == null || !shape.IsValid())
+            return null;
+
+        var size = context.DocumentSize;
+        var image = RequestTexture(0, size);
+        
+        shape.Rasterize(image.DrawingSurface);
+
+        Image.Value = image;
+        
+        return image;
+    }
+
+    public override Node CreateCopy() => new RasterizeShape();
+}

+ 22 - 21
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Points/RemoveClosePointsNode.cs → src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RemoveClosePointsNode.cs

@@ -1,41 +1,41 @@
-using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.Numerics;
+using ShapeData = PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data.ShapeData;
 
-namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Points;
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
 
 [NodeInfo("RemoveClosePoints", "REMOVE_CLOSE_POINTS", Category = "SHAPE")]
-public class RemoveClosePointsNode : Node
+public class RemoveClosePointsNode : ShapeNode<PointsData>
 {
-    public OutputProperty<PointList> Output { get; }
-    
-    public InputProperty<PointList> Input { get; }
-    
+    public InputProperty<PointsData> Input { get; }
+
     public InputProperty<double> MinDistance { get; }
 
     public InputProperty<int> Seed { get; }
 
     public RemoveClosePointsNode()
     {
-        Output = CreateOutput("Output", "POINTS", PointList.Empty);
-        Input = CreateInput("Input", "POINTS", PointList.Empty);
+        Input = CreateInput<PointsData>("Input", "POINTS", null);
         MinDistance = CreateInput("MinDistance", "MIN_DISTANCE", 0d);
         Seed = CreateInput("Seed", "SEED", 0);
     }
-    
-    protected override Texture? OnExecute(RenderingContext context)
+
+    protected override PointsData? GetShapeData(RenderingContext context)
     {
+        var data = Input.Value;
+
         var distance = MinDistance.Value;
 
-        if (distance == 0)
+        if (distance == 0 || data == null || data.Points == null)
         {
-            Output.Value = Input.Value;
             return null;
         }
 
-        var availablePoints = Input.Value.Distinct().ToList();
-        var newPoints = new PointList(availablePoints.Count) { HashValue = HashCode.Combine(Input.Value.HashValue, MinDistance.Value, Seed.Value) };
-
+        var availablePoints = data.Points.Distinct().ToList();
+        List<VecD> newPoints = new List<VecD>();
+        
         var minDistance = MinDistance.Value;
         var documentSize = context.DocumentSize;
 
@@ -54,18 +54,19 @@ public class RemoveClosePointsNode : Node
             }
 
             continue;
-            bool InRange(VecD other) => (other.Multiply(documentSize) - point.Multiply(documentSize)).Length <= minDistance;
+
+            bool InRange(VecD other) =>
+                (other - point).Length <= minDistance;
         }
 
         if (availablePoints.Count == 1)
         {
             newPoints.Add(availablePoints[0]);
         }
-        
-        Output.Value = newPoints;
-        
-        return null;
 
+        var finalData = new PointsData(newPoints);
+
+        return finalData;
     }
 
     public override Node CreateCopy() => new RemoveClosePointsNode();

+ 39 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/ShapeNode.cs

@@ -0,0 +1,39 @@
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.Numerics;
+using ShapeData = PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data.ShapeData;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+
+public abstract class ShapeNode<T> : Node where T : ShapeData
+{
+    public OutputProperty<T> Output { get; }
+    
+    public ShapeNode()
+    {
+        Output = CreateOutput<T>("Output", "OUTPUT", null);
+    }
+
+    protected override Texture? OnExecute(RenderingContext context)
+    {
+        var data = GetShapeData(context);
+
+        Output.Value = data;
+        
+        if (data == null || !data.IsValid())
+            return null;
+
+        return RasterizePreview(data, context.DocumentSize);
+    }
+    
+    protected abstract T? GetShapeData(RenderingContext context);
+
+    public Texture RasterizePreview(ShapeData data, VecI size)
+    {
+        Texture texture = RequestTexture(0, size);
+        
+        data.Rasterize(texture.DrawingSurface);
+        
+        return texture;
+    }
+}

+ 0 - 33
src/PixiEditor.ChangeableDocument/Changeables/Graph/PropertyValidator.cs

@@ -44,39 +44,6 @@ public class PropertyValidator
         return min;
     }
 
-    /*public PropertyValidator Select<T>(Func<T, object> selector)
-    {
-        PropertyValidator newValidator = new();
-
-        newValidator.Rules.Add(v =>
-        {
-            if (v is T val)
-            {
-                return (true, selector(val));
-            }
-
-            return (false, v);
-        });
-
-        return newValidator;
-    }
-
-    public void All(params PropertyValidator[] validators)
-    {
-        Rules.Add(v =>
-        {
-            foreach (var validator in validators)
-            {
-                if (!validator.Validate(v))
-                {
-                    return (false, validator.GetClosestValidValue(v));
-                }
-            }
-
-            return (true, v);
-        });
-    }*/
-
     public bool Validate(object? value)
     {
         object lastValue = value;

+ 10 - 0
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConnectProperties_Change.cs

@@ -138,6 +138,16 @@ internal class ConnectProperties_Change : Change
                 return ConversionTable.TryConvert(result, output.ValueType, out _);
             }
 
+            if (outputValue == null)
+            {
+                return true;
+            }
+            
+            if (outputValue.GetType().IsAssignableTo(input.ValueType))
+            {
+                return true;
+            }
+
             if (ConversionTable.TryConvert(outputValue, input.ValueType, out _))
             {
                 return true;

+ 3 - 1
src/PixiEditor.ChangeableDocument/Rendering/RenderingContext.cs

@@ -1,4 +1,6 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
+using System;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.DrawingApi.Core.ColorsImpl;

+ 4 - 4
src/PixiEditor.Desktop/PixiEditor.Desktop.csproj

@@ -29,16 +29,16 @@
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
   </ItemGroup>
 
-  <ItemGroup>
-    <ProjectReference Include="..\PixiEditor\PixiEditor.csproj"/>
-  </ItemGroup>
-
   <ItemGroup>
     <None Include="..\PixiEditor\Images\favicon.ico">
       <Link>favicon.ico</Link>
     </None>
   </ItemGroup>
 
+  <ItemGroup>
+    <ProjectReference Include="..\PixiEditor\PixiEditor.csproj" />
+  </ItemGroup>
+
   <Target Name="Rename" AfterTargets="AfterBuild">
     <Move SourceFiles="$(OutDir)PixiEditor.Desktop.exe" DestinationFiles="$(OutDir)PixiEditor.exe" />
     <Message Text="Renamed build executable file." Importance="high" />

BIN
src/PixiEditor.Extensions.Sdk/build/PixiEditor.Api.CGlueMSBuild.dll


BIN
src/PixiEditor.Extensions.Sdk/build/PixiEditor.Extensions.MSPackageBuilder.dll


+ 12 - 3
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -43,7 +43,7 @@
             <!--                          -->
             
             <!-- Sockets -->
-            <Color x:Key="DefaultSocketColor">#c0334e</Color>
+            <Color x:Key="DefaultSocketColor">#FF00FF</Color>
             <Color x:Key="ImageSocketColor">#99c47a</Color>
             <Color x:Key="FilterSocketColor">#CC5C5C</Color>
             <Color x:Key="BoolSocketColor">#68abdf</Color>
@@ -53,7 +53,14 @@
             <Color x:Key="VecDSocketColor">#c984ca</Color>
             <Color x:Key="VecISocketColor">#c9b4ca</Color>
             <Color x:Key="IntSocketColor">#4C64B1</Color>
-            <Color x:Key="PointListSocketColor">#7f5280</Color>
+            <Color x:Key="EllipseDataSocketColor">#a473a5</Color>
+            <Color x:Key="PointsDataSocketColor">#e1d0e1</Color>
+            <GradientStops x:Key="ShapeDataSocketGradient">
+                <GradientStop Offset="0" Color="{StaticResource EllipseDataSocketColor}"/>
+                <GradientStop Offset="0.5" Color="{StaticResource EllipseDataSocketColor}"/>
+                <GradientStop Offset="0.5" Color="{StaticResource PointsDataSocketColor}"/>
+                <GradientStop Offset="1" Color="{StaticResource PointsDataSocketColor}"/>
+            </GradientStops>
             
             <!-- Zones & Frames -->
             <Color x:Key="PixiEditorModifyImageBorderColor">#68abdf</Color>
@@ -129,7 +136,9 @@
             <SolidColorBrush x:Key="Int2SocketBrush" Color="{StaticResource VecISocketColor}"/>
             <SolidColorBrush x:Key="Int32SocketBrush" Color="{StaticResource IntSocketColor}"/>
             <SolidColorBrush x:Key="Int1SocketBrush" Color="{StaticResource IntSocketColor}"/>
-            <SolidColorBrush x:Key="PointListSocketBrush" Color="{StaticResource PointListSocketColor}"/>
+            <ConicGradientBrush x:Key="ShapeDataSocketBrush" GradientStops="{StaticResource ShapeDataSocketGradient}"/>
+            <SolidColorBrush x:Key="EllipseDataSocketBrush" Color="{StaticResource EllipseDataSocketColor}"/>
+            <SolidColorBrush x:Key="PointsDataSocketBrush" Color="{StaticResource PointsDataSocketColor}"/>
             
             <!-- Zones & Frames -->
             <SolidColorBrush x:Key="PixiEditorModifyImageLeftBorderBrush" Color="{StaticResource PixiEditorModifyImageBorderColor}"/>

+ 3 - 2
src/PixiEditor/Data/Localization/Languages/en.json

@@ -704,7 +704,7 @@
   "PROBABILITY": "Probability",
   "DISTRIBUTE_POINTS": "Distribute points",
   "REMOVE_CLOSE_POINTS": "Remove close points",
-  "RASTERIZE_POINTS": "Rasterize Points",
+  "RASTERIZE_SHAPE": "Rasterize Shape",
   "BEGIN_COLOR_EVALUATOR_NODE": "Begin evaluating color",
   "FINISH_COLOR_EVALUATOR_NODE": "Finish evaluating color",
   "COLOR_EVALUATOR_NODE_PAIR": "Color Evaluator",
@@ -737,5 +737,6 @@
   "GENERATION": "Generation",
   "NUMBER": "Number",
   "ANIMATION": "Animation",
-  "SAMPLE_IMAGE": "Sample Image"
+  "SAMPLE_IMAGE": "Sample Image",
+  "POSITION": "Position"
 }

+ 2 - 2
src/PixiEditor/Fonts/NodeIcons.cs

@@ -3,7 +3,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Animable;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
-using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Points;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
 
 namespace PixiEditor.Fonts;
 
@@ -18,7 +18,7 @@ public static class NodeIcons
             { typeof(MergeNode), "\ue903" },
             { typeof(ModifyImageLeftNode), "\ue904" },
             { typeof(ImageLayerNode), "\ue905" },
-            { typeof(RasterizePointsNode), "\ue906" },
+            { typeof(RasterizeShape), "\ue906" },
             { typeof(SampleImageNode), "\ue907" },
             { typeof(CombineColorNode), "\ue908" },
             { typeof(ApplyFilterNode), "\ue909" },

+ 22 - 0
src/PixiEditor/Helpers/Converters/SocketColorConverter.cs

@@ -0,0 +1,22 @@
+using System.Globalization;
+using Avalonia.Media;
+using Colors = PixiEditor.DrawingApi.Core.ColorsImpl.Colors;
+
+namespace PixiEditor.Helpers.Converters;
+
+internal class SocketColorConverter : SingleInstanceConverter<SocketColorConverter>
+{
+    Color unknownColor = Color.FromRgb(255, 0, 255);
+    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is IBrush brush)
+        {
+            if(brush is SolidColorBrush solidColorBrush)
+                return solidColorBrush.Color;
+            if (brush is GradientBrush linearGradientBrush)
+                return linearGradientBrush.GradientStops.FirstOrDefault()?.Color ?? unknownColor;
+        }
+        
+        return unknownColor;
+    }
+}

+ 13 - 1
src/PixiEditor/Helpers/SerializationUtil.cs

@@ -26,6 +26,10 @@ public static class SerializationUtil
         }
 
         var factory = allFactories.FirstOrDefault(x => x.OriginalType == value.GetType());
+        if (factory == null)
+        {
+            factory = allFactories.FirstOrDefault(x => value.GetType().IsAssignableTo(x.OriginalType));
+        }
 
         if (factory != null)
         {
@@ -55,9 +59,17 @@ public static class SerializationUtil
             if (factory != null)
             {
                 factory.Config = config;
-                return factory.Deserialize(data is Dictionary<object, object> processableDict
+                try
+                {
+                    return factory.Deserialize(data is Dictionary<object, object> processableDict
                     ? ToDictionary(processableDict)
                     : data);
+                }
+                catch (Exception e)
+                {
+                    return value;
+                }
+                
             }
         }
 

+ 0 - 1
src/PixiEditor/Helpers/ServiceCollectionHelpers.cs

@@ -123,7 +123,6 @@ internal static class ServiceCollectionHelpers
             .AddSingleton<SerializationFactory, VecISerializationFactory>()
             .AddSingleton<SerializationFactory, ColorSerializationFactory>()
             .AddSingleton<SerializationFactory, ColorMatrixSerializationFactory>()
-            .AddSingleton<SerializationFactory, PointListSerializationFactory>()
             .AddSingleton<SerializationFactory, VecD3SerializationFactory>()
             .AddSingleton<SerializationFactory, TextureSerializationFactory>()
             // Palette Parsers

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

@@ -0,0 +1,58 @@
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.Serialization.Factories;
+
+public class ByteBuilder
+{
+    public byte[] Data { get; private set; }
+    
+    private List<byte> _data = new List<byte>();
+    
+    public ByteBuilder AddVecD(VecD vec)
+    {
+        _data.AddRange(BitConverter.GetBytes(vec.X));
+        _data.AddRange(BitConverter.GetBytes(vec.Y));
+        
+        return this;
+    }
+    
+    public ByteBuilder AddColor(Color color)
+    {
+        _data.Add(color.R);
+        _data.Add(color.G);
+        _data.Add(color.B);
+        _data.Add(color.A);
+        
+        return this;
+    }
+    
+    public ByteBuilder AddInt(int value)
+    {
+        _data.AddRange(BitConverter.GetBytes(value));
+        
+        return this;
+    }
+    
+    public ByteBuilder AddDouble(double value)
+    {
+        _data.AddRange(BitConverter.GetBytes(value));
+        
+        return this;
+    }
+    
+    public byte[] Build()
+    {
+        Data = _data.ToArray();
+        return Data;
+    }
+
+    public void AddVecDList(List<VecD> originalPoints)
+    {
+        AddInt(originalPoints.Count);
+        foreach (var point in originalPoints)
+        {
+            AddVecD(point);
+        }
+    }
+}

+ 60 - 0
src/PixiEditor/Models/Serialization/Factories/ByteExtractor.cs

@@ -0,0 +1,60 @@
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.Serialization.Factories;
+
+public class ByteExtractor
+{
+    public int Position { get; private set; }
+    
+    private byte[] _data;
+    
+    public ByteExtractor(byte[] data)
+    {
+        _data = data;
+    }
+    
+    public VecD GetVecD()
+    {
+        double x = BitConverter.ToDouble(_data, Position);
+        double y = BitConverter.ToDouble(_data, Position + sizeof(double));
+        
+        Position += sizeof(double) * 2;
+        
+        return new VecD(x, y);
+    }
+    
+    public Color GetColor()
+    {
+        byte r = _data[Position];
+        byte g = _data[Position + 1];
+        byte b = _data[Position + 2];
+        byte a = _data[Position + 3];
+        
+        Position += 4;
+        
+        return new Color(r, g, b, a);
+    }
+    
+    public int GetInt()
+    {
+        int value = BitConverter.ToInt32(_data, Position);
+        
+        Position += sizeof(int);
+        
+        return value;
+    }
+
+    public List<VecD> GetVecDList()
+    {
+        int count = GetInt();
+        List<VecD> points = new List<VecD>();
+        
+        for (int i = 0; i < count; i++)
+        {
+            points.Add(GetVecD());
+        }
+        
+        return points;
+    }
+}

+ 47 - 0
src/PixiEditor/Models/Serialization/Factories/EllipseSerializationFactory.cs

@@ -0,0 +1,47 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.Serialization.Factories;
+
+public class EllipseSerializationFactory : SerializationFactory<byte[], EllipseData>
+{
+    public override string DeserializationId { get; } = "PixiEditor.EllipseData";
+    public override byte[] Serialize(EllipseData original)
+    {
+        ByteBuilder builder = new ByteBuilder();
+        builder.AddVecD(original.Center);
+        builder.AddVecD(original.Radius);
+        builder.AddColor(original.StrokeColor);
+        builder.AddColor(original.FillColor);
+        builder.AddInt(original.StrokeWidth);
+        
+        return builder.Build();
+    }
+
+    public override bool TryDeserialize(object serialized, out EllipseData original)
+    {
+        if (serialized is not byte[] data)
+        {
+            original = null;
+            return false;
+        }
+        
+        ByteExtractor extractor = new ByteExtractor(data);
+        
+        VecD center = extractor.GetVecD();
+        VecD radius = extractor.GetVecD();
+        Color strokeColor = extractor.GetColor();
+        Color fillColor = extractor.GetColor();
+        int strokeWidth = extractor.GetInt();
+        
+        original = new EllipseData(center, radius)
+        {
+            StrokeColor = strokeColor,
+            FillColor = fillColor,
+            StrokeWidth = strokeWidth
+        };
+        
+        return true;
+    }
+}

+ 0 - 27
src/PixiEditor/Models/Serialization/Factories/PointListSerializationFactory.cs

@@ -1,27 +0,0 @@
-using MessagePack;
-using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Points;
-
-namespace PixiEditor.Models.Serialization.Factories;
-
-public class PointListSerializationFactory : SerializationFactory<byte[], PointList>
-{
-    public override string DeserializationId { get; } = "PixiEditor.PointList";
-
-    public override byte[] Serialize(PointList original)
-    {
-        return MessagePackSerializer.Serialize(original);
-    }
-
-    public override bool TryDeserialize(object serialized, out PointList? original)
-    {
-        if (serialized is not byte[] buffer)
-        {
-            original = null;
-            return false;
-        }
-        
-        original = MessagePackSerializer.Deserialize<PointList>(buffer);
-
-        return true;
-    }
-}

+ 42 - 0
src/PixiEditor/Models/Serialization/Factories/PointsDataSerializationFactory.cs

@@ -0,0 +1,42 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.Serialization.Factories;
+
+public class PointsDataSerializationFactory : SerializationFactory<byte[], PointsData>
+{
+    public override string DeserializationId { get; } = "PixiEditor.PointsData";
+    public override byte[] Serialize(PointsData original)
+    {
+        ByteBuilder builder = new ByteBuilder();
+        builder.AddVecDList(original.Points);
+        builder.AddColor(original.FillColor);
+        builder.AddInt(original.StrokeWidth);
+        
+        return builder.Build();
+    }
+
+    public override bool TryDeserialize(object serialized, out PointsData original)
+    {
+        if (serialized is not byte[] data)
+        {
+            original = null;
+            return false;
+        }
+        
+        ByteExtractor extractor = new ByteExtractor(data);
+        
+        List<VecD> points = extractor.GetVecDList();
+        Color fillColor = extractor.GetColor();
+        int strokeWidth = extractor.GetInt();
+        
+        original = new PointsData(points)
+        {
+            FillColor = fillColor,
+            StrokeWidth = strokeWidth
+        };
+        
+        return true;
+    }
+}

+ 4 - 3
src/PixiEditor/Styles/Templates/ConnectionView.axaml

@@ -1,6 +1,7 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-                    xmlns:nodes="clr-namespace:PixiEditor.Views.Nodes">
+                    xmlns:nodes="clr-namespace:PixiEditor.Views.Nodes"
+                    xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters">
 
     <ControlTheme TargetType="nodes:ConnectionView" x:Key="{x:Type nodes:ConnectionView}">
         <Setter Property="ClipToBounds" Value="False"/>
@@ -14,8 +15,8 @@
                             <LinearGradientBrush>
                                 <LinearGradientBrush.GradientStops>
                                     <GradientStop Offset="0" Color="#555" />
-                                    <GradientStop Offset=".05" Color="{Binding InputProperty.SocketBrush.Color, RelativeSource={RelativeSource TemplatedParent}}" />
-                                    <GradientStop Offset="0.95" Color="{Binding OutputProperty.SocketBrush.Color, RelativeSource={RelativeSource TemplatedParent}}" />
+                                    <GradientStop Offset=".05" Color="{Binding InputProperty.SocketBrush, RelativeSource={RelativeSource TemplatedParent}, Converter={converters:SocketColorConverter}}" />
+                                    <GradientStop Offset="0.95" Color="{Binding OutputProperty.SocketBrush, RelativeSource={RelativeSource TemplatedParent}, Converter={converters:SocketColorConverter}}" />
                                     <GradientStop Offset="1" Color="#555" />
                                 </LinearGradientBrush.GradientStops>
                             </LinearGradientBrush>

+ 1 - 1
src/PixiEditor/Styles/Templates/NodeSocket.axaml

@@ -8,7 +8,7 @@
                     <Grid Name="PART_ConnectPort">
                         <Panel Width="20" Height="20" Margin="-5, 0" Background="Transparent"
                                IsVisible="{Binding !IsFunc, RelativeSource={RelativeSource TemplatedParent}}">
-                            <Ellipse Width="10" Height="10"
+                            <Ellipse Width="10" Height="10" RenderTransform="rotate(90deg)"
                                      Fill="{TemplateBinding SocketBrush}" />
                         </Panel>
                         <Panel Margin="-5, 0" Width="20" Height="20" Background="Transparent"

+ 22 - 2
src/PixiEditor/Views/Nodes/NodeGraphView.cs

@@ -250,7 +250,7 @@ internal class NodeGraphView : Zoombox.Zoombox
 
             Color gradientStopFirstColor = _startingPropColor;
             Color gradientStopSecondColor =
-                ((SolidColorBrush)nodeSocket?.SocketBrush)?.Color ?? gradientStopFirstColor;
+                GetSocketColor(nodeSocket) ?? gradientStopFirstColor;
 
             if (endPoint.X > startDragConnectionPoint.X)
             {
@@ -276,6 +276,26 @@ internal class NodeGraphView : Zoombox.Zoombox
         }
     }
 
+    private static Color? GetSocketColor(NodeSocket? nodeSocket)
+    {
+        if (nodeSocket == null)
+        {
+            return null;
+        }
+
+        if (nodeSocket.SocketBrush is SolidColorBrush solidColor)
+        {
+            return solidColor.Color;
+        }
+
+        if (nodeSocket.SocketBrush is GradientBrush gradientBrush)
+        {
+            return gradientBrush.GradientStops.FirstOrDefault()?.Color;
+        }
+        
+        return null; 
+    }
+
     protected override void OnPointerReleased(PointerReleasedEventArgs e)
     {
         base.OnPointerReleased(e);
@@ -353,7 +373,7 @@ internal class NodeGraphView : Zoombox.Zoombox
         }
 
         _previewConnectionLine.IsVisible = true;
-        _startingPropColor = ((SolidColorBrush)nodeSocket.SocketBrush).Color;
+        _startingPropColor = GetSocketColor(nodeSocket) ?? Colors.White; 
         _previewConnectionLine.LineBrush = new LinearGradientBrush()
         {
             GradientStops = new GradientStops()

+ 1 - 6
tests/PixiEditor.Backend.Tests/MockDocument.cs

@@ -61,12 +61,7 @@ public class MockDocument : IReadOnlyDocument
         throw new NotImplementedException();
     }
 
-    (IReadOnlyStructureNode, IReadOnlyNode) IReadOnlyDocument.FindChildAndParentOrThrow(Guid childGuid)
-    {
-        return FindChildAndParentOrThrow(childGuid);
-    }
-
-    public (IReadOnlyStructureNode, IReadOnlyFolderNode) FindChildAndParentOrThrow(Guid childGuid)
+    public (IReadOnlyStructureNode, IReadOnlyNode) FindChildAndParentOrThrow(Guid childGuid)
     {
         throw new NotImplementedException();
     }

+ 1 - 0
tests/PixiEditor.Backend.Tests/NodeSystemTests.cs

@@ -144,6 +144,7 @@ public class NodeSystemTests
             foreach (var input in node.InputProperties)
             {
                 if (knownNonSerializableTypes.Contains(input.ValueType)) continue;
+                if(input.ValueType.IsAbstract) continue;
                 if (input.ValueType.IsAssignableTo(typeof(Delegate))) continue;
                 bool hasFactory = factories.Any(x => x.OriginalType == input.ValueType);
                 Assert.True(

+ 1 - 1
tests/PixiEditor.Backend.Tests/PixiEditor.Backend.Tests.csproj

@@ -21,9 +21,9 @@
     </ItemGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\..\src\PixiEditor\PixiEditor.csproj" />
       <ProjectReference Include="..\..\src\PixiEditor.ChangeableDocument\PixiEditor.ChangeableDocument.csproj" />
       <ProjectReference Include="..\..\src\PixiEditor.DrawingApi.Skia\PixiEditor.DrawingApi.Skia.csproj" />
+      <ProjectReference Include="..\..\src\PixiEditor\PixiEditor.csproj" />
     </ItemGroup>
 
 </Project>

+ 150 - 21
tests/PixiEditorTests.sln

@@ -23,10 +23,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.Zoombox", "..\sr
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.Platform.Steam", "..\src\PixiEditor.Platform.Steam\PixiEditor.Platform.Steam.csproj", "{9BCD0764-9C16-4A2A-B153-C676FEF38887}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.Platform", "..\src\PixiEditor.Platform\PixiEditor.Platform.csproj", "{2BDEB8C6-F22D-43EA-A309-B3387A803689}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.Extensions", "..\src\PixiEditor.Extensions\PixiEditor.Extensions.csproj", "{1249EE2B-EB0D-411C-B311-53A7A22B7743}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.Platform.MSStore", "..\src\PixiEditor.Platform.MSStore\PixiEditor.Platform.MSStore.csproj", "{8EF48E6C-8219-4EE2-87C6-5176D8D092E6}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.DrawingApi.Skia", "..\src\PixiEditor.DrawingApi.Skia\PixiEditor.DrawingApi.Skia.csproj", "{DCFF0D46-C19C-4E6D-984C-56A95781AE0B}"
@@ -55,10 +51,48 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.ChangeableDocume
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.Gen", "..\src\PixiEditor.Extensions.Gen\PixiEditor.Extensions.Gen.csproj", "{63A2C494-E9D9-486E-B709-39A10DD8B415}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor", "..\src\PixiEditor\PixiEditor.csproj", "{CA5EBC0F-52F8-4359-B7F5-8B33B563D279}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Desktop", "..\src\PixiEditor.Desktop\PixiEditor.Desktop.csproj", "{E9C139E7-8078-477F-8BC3-C7A6BB5445CD}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor", "..\src\PixiEditor\PixiEditor.csproj", "{B08D919D-6ABC-49C7-882D-8011C792BEBF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiDocks.Avalonia", "..\src\PixiDocks\src\PixiDocks.Avalonia\PixiDocks.Avalonia.csproj", "{BA4F359E-8472-4634-832D-2B75DA6EAB04}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiDocks.Core", "..\src\PixiDocks\src\PixiDocks.Core\PixiDocks.Core.csproj", "{9C23412C-7C5D-4C62-ACF6-43A3242EF663}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Common", "..\src\PixiEditor.Common\PixiEditor.Common.csproj", "{D9EBF2FD-F055-4C54-B921-6D31466092EC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.OperatingSystem", "..\src\PixiEditor.OperatingSystem\PixiEditor.OperatingSystem.csproj", "{85246331-432A-4084-8A80-63CA2758DE2D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.WasmRuntime", "..\src\PixiEditor.Extensions.WasmRuntime\PixiEditor.Extensions.WasmRuntime.csproj", "{3811FB79-A4F4-4059-A498-A1CB1B09A7B6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions", "..\src\PixiEditor.Extensions\PixiEditor.Extensions.csproj", "{6407D19E-68B9-4E41-9A0D-F6B723FFD549}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.ChangeableDocument.Gen", "..\src\PixiEditor.ChangeableDocument.Gen\PixiEditor.ChangeableDocument.Gen.csproj", "{6245051F-835D-41EB-89E0-95652B51153C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.AnimationRenderer.FFmpeg", "..\src\PixiEditor.AnimationRenderer.FFmpeg\PixiEditor.AnimationRenderer.FFmpeg.csproj", "{2A54A13E-6D54-4BC6-836D-740F12B62D02}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.UI.Common", "..\src\PixiEditor.UI.Common\PixiEditor.UI.Common.csproj", "{13484484-682F-4D7A-AEF2-ECAF3A601311}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Windows", "..\src\PixiEditor.Windows\PixiEditor.Windows.csproj", "{D5A9B447-642A-4655-A8BE-CC59AD6BE275}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Platform", "..\src\PixiEditor.Platform\PixiEditor.Platform.csproj", "{61696194-C130-4388-9607-DA2D5295DC1F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiParser", "..\src\PixiParser\src\PixiParser\PixiParser.csproj", "{2AFF4096-A539-4A01-B4F4-60389C11C039}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.WasmApi.Gen", "..\src\PixiEditor.WasmApi.Gen\PixiEditor.WasmApi.Gen.csproj", "{7E65B1D3-507E-4C89-92A9-D25D0097888B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.AnimationRenderer.Core", "..\src\PixiEditor.AnimationRenderer.Core\PixiEditor.AnimationRenderer.Core.csproj", "{D6BF8B8D-99A1-4072-9EF1-4A7BFB34330F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiParser.Skia", "..\src\PixiParser\src\PixiParser.Skia\PixiParser.Skia.csproj", "{E9E3FBE7-AE00-4E03-9085-F8611B795526}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Extensions.Runtime", "..\src\PixiEditor.Extensions.Runtime\PixiEditor.Extensions.Runtime.csproj", "{40DFBBDD-B423-47A2-A64F-CE4B1F9897AB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Platform.Standalone", "..\src\PixiEditor.Platform.Standalone\PixiEditor.Platform.Standalone.csproj", "{8B147D8C-BDF5-4C04-8891-15014451CC63}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.Gen", "..\src\PixiEditor.Gen\PixiEditor.Gen.csproj", "{57860CCA-903B-4871-89E8-A674D3EE4446}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixiEditor.UpdateModule", "..\src\PixiEditor.UpdateModule\PixiEditor.UpdateModule.csproj", "{3028FEDC-E937-4432-B74E-4E29304CD5C0}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -84,14 +118,6 @@ Global
 		{4DC33EEF-4FD2-4038-BB0C-61E1B833A6B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4DC33EEF-4FD2-4038-BB0C-61E1B833A6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4DC33EEF-4FD2-4038-BB0C-61E1B833A6B4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{493B3F23-DB1B-4633-8C6D-BAD18C59A239}.Debug|Any CPU.ActiveCfg = Debug|x64
-		{493B3F23-DB1B-4633-8C6D-BAD18C59A239}.Debug|Any CPU.Build.0 = Debug|x64
-		{493B3F23-DB1B-4633-8C6D-BAD18C59A239}.Release|Any CPU.ActiveCfg = Release|x64
-		{493B3F23-DB1B-4633-8C6D-BAD18C59A239}.Release|Any CPU.Build.0 = Release|x64
-		{DCFF0D46-C19C-4E6D-984C-56A95781AE0B}.Debug|Any CPU.ActiveCfg = Debug|x64
-		{DCFF0D46-C19C-4E6D-984C-56A95781AE0B}.Debug|Any CPU.Build.0 = Debug|x64
-		{DCFF0D46-C19C-4E6D-984C-56A95781AE0B}.Release|Any CPU.ActiveCfg = Release|x64
-		{DCFF0D46-C19C-4E6D-984C-56A95781AE0B}.Release|Any CPU.Build.0 = Release|x64
 		{61403ACE-FEA3-49E5-A916-68C0FADCFBE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{61403ACE-FEA3-49E5-A916-68C0FADCFBE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{61403ACE-FEA3-49E5-A916-68C0FADCFBE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -140,14 +166,100 @@ Global
 		{63A2C494-E9D9-486E-B709-39A10DD8B415}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{63A2C494-E9D9-486E-B709-39A10DD8B415}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{63A2C494-E9D9-486E-B709-39A10DD8B415}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CA5EBC0F-52F8-4359-B7F5-8B33B563D279}.Debug|Any CPU.ActiveCfg = Debug|x64
-		{CA5EBC0F-52F8-4359-B7F5-8B33B563D279}.Debug|Any CPU.Build.0 = Debug|x64
-		{CA5EBC0F-52F8-4359-B7F5-8B33B563D279}.Release|Any CPU.ActiveCfg = Release|x64
-		{CA5EBC0F-52F8-4359-B7F5-8B33B563D279}.Release|Any CPU.Build.0 = Release|x64
 		{E9C139E7-8078-477F-8BC3-C7A6BB5445CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{E9C139E7-8078-477F-8BC3-C7A6BB5445CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E9C139E7-8078-477F-8BC3-C7A6BB5445CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E9C139E7-8078-477F-8BC3-C7A6BB5445CD}.Release|Any CPU.Build.0 = Release|Any CPU
+		{493B3F23-DB1B-4633-8C6D-BAD18C59A239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{493B3F23-DB1B-4633-8C6D-BAD18C59A239}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DCFF0D46-C19C-4E6D-984C-56A95781AE0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DCFF0D46-C19C-4E6D-984C-56A95781AE0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8EF48E6C-8219-4EE2-87C6-5176D8D092E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8EF48E6C-8219-4EE2-87C6-5176D8D092E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9BCD0764-9C16-4A2A-B153-C676FEF38887}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9BCD0764-9C16-4A2A-B153-C676FEF38887}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B08D919D-6ABC-49C7-882D-8011C792BEBF}.Debug|Any CPU.ActiveCfg = Debug|x64
+		{B08D919D-6ABC-49C7-882D-8011C792BEBF}.Debug|Any CPU.Build.0 = Debug|x64
+		{B08D919D-6ABC-49C7-882D-8011C792BEBF}.Release|Any CPU.ActiveCfg = Release|x64
+		{B08D919D-6ABC-49C7-882D-8011C792BEBF}.Release|Any CPU.Build.0 = Release|x64
+		{BA4F359E-8472-4634-832D-2B75DA6EAB04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BA4F359E-8472-4634-832D-2B75DA6EAB04}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BA4F359E-8472-4634-832D-2B75DA6EAB04}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BA4F359E-8472-4634-832D-2B75DA6EAB04}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9C23412C-7C5D-4C62-ACF6-43A3242EF663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9C23412C-7C5D-4C62-ACF6-43A3242EF663}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9C23412C-7C5D-4C62-ACF6-43A3242EF663}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9C23412C-7C5D-4C62-ACF6-43A3242EF663}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D9EBF2FD-F055-4C54-B921-6D31466092EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D9EBF2FD-F055-4C54-B921-6D31466092EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D9EBF2FD-F055-4C54-B921-6D31466092EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D9EBF2FD-F055-4C54-B921-6D31466092EC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{85246331-432A-4084-8A80-63CA2758DE2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{85246331-432A-4084-8A80-63CA2758DE2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{85246331-432A-4084-8A80-63CA2758DE2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{85246331-432A-4084-8A80-63CA2758DE2D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3811FB79-A4F4-4059-A498-A1CB1B09A7B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3811FB79-A4F4-4059-A498-A1CB1B09A7B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3811FB79-A4F4-4059-A498-A1CB1B09A7B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3811FB79-A4F4-4059-A498-A1CB1B09A7B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6407D19E-68B9-4E41-9A0D-F6B723FFD549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6407D19E-68B9-4E41-9A0D-F6B723FFD549}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6407D19E-68B9-4E41-9A0D-F6B723FFD549}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6407D19E-68B9-4E41-9A0D-F6B723FFD549}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6245051F-835D-41EB-89E0-95652B51153C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6245051F-835D-41EB-89E0-95652B51153C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6245051F-835D-41EB-89E0-95652B51153C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6245051F-835D-41EB-89E0-95652B51153C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2A54A13E-6D54-4BC6-836D-740F12B62D02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2A54A13E-6D54-4BC6-836D-740F12B62D02}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2A54A13E-6D54-4BC6-836D-740F12B62D02}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2A54A13E-6D54-4BC6-836D-740F12B62D02}.Release|Any CPU.Build.0 = Release|Any CPU
+		{13484484-682F-4D7A-AEF2-ECAF3A601311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{13484484-682F-4D7A-AEF2-ECAF3A601311}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{13484484-682F-4D7A-AEF2-ECAF3A601311}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{13484484-682F-4D7A-AEF2-ECAF3A601311}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D5A9B447-642A-4655-A8BE-CC59AD6BE275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D5A9B447-642A-4655-A8BE-CC59AD6BE275}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D5A9B447-642A-4655-A8BE-CC59AD6BE275}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D5A9B447-642A-4655-A8BE-CC59AD6BE275}.Release|Any CPU.Build.0 = Release|Any CPU
+		{61696194-C130-4388-9607-DA2D5295DC1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{61696194-C130-4388-9607-DA2D5295DC1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{61696194-C130-4388-9607-DA2D5295DC1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{61696194-C130-4388-9607-DA2D5295DC1F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2AFF4096-A539-4A01-B4F4-60389C11C039}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2AFF4096-A539-4A01-B4F4-60389C11C039}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2AFF4096-A539-4A01-B4F4-60389C11C039}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2AFF4096-A539-4A01-B4F4-60389C11C039}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7E65B1D3-507E-4C89-92A9-D25D0097888B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7E65B1D3-507E-4C89-92A9-D25D0097888B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7E65B1D3-507E-4C89-92A9-D25D0097888B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7E65B1D3-507E-4C89-92A9-D25D0097888B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D6BF8B8D-99A1-4072-9EF1-4A7BFB34330F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D6BF8B8D-99A1-4072-9EF1-4A7BFB34330F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D6BF8B8D-99A1-4072-9EF1-4A7BFB34330F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D6BF8B8D-99A1-4072-9EF1-4A7BFB34330F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E9E3FBE7-AE00-4E03-9085-F8611B795526}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E9E3FBE7-AE00-4E03-9085-F8611B795526}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E9E3FBE7-AE00-4E03-9085-F8611B795526}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E9E3FBE7-AE00-4E03-9085-F8611B795526}.Release|Any CPU.Build.0 = Release|Any CPU
+		{40DFBBDD-B423-47A2-A64F-CE4B1F9897AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{40DFBBDD-B423-47A2-A64F-CE4B1F9897AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{40DFBBDD-B423-47A2-A64F-CE4B1F9897AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{40DFBBDD-B423-47A2-A64F-CE4B1F9897AB}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8B147D8C-BDF5-4C04-8891-15014451CC63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8B147D8C-BDF5-4C04-8891-15014451CC63}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8B147D8C-BDF5-4C04-8891-15014451CC63}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8B147D8C-BDF5-4C04-8891-15014451CC63}.Release|Any CPU.Build.0 = Release|Any CPU
+		{57860CCA-903B-4871-89E8-A674D3EE4446}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{57860CCA-903B-4871-89E8-A674D3EE4446}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{57860CCA-903B-4871-89E8-A674D3EE4446}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{57860CCA-903B-4871-89E8-A674D3EE4446}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3028FEDC-E937-4432-B74E-4E29304CD5C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3028FEDC-E937-4432-B74E-4E29304CD5C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3028FEDC-E937-4432-B74E-4E29304CD5C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3028FEDC-E937-4432-B74E-4E29304CD5C0}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 		{0EF3CAB9-7361-472C-8789-D17D4EA2DEBB} = {D914C08C-5F1A-4E13-AAA6-F25E8C9748E2}
@@ -169,11 +281,28 @@ Global
 		{6D965165-3B4D-4C70-A559-6AD093879D5A} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 		{63A2C494-E9D9-486E-B709-39A10DD8B415} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 		{69DD5830-C682-49FB-B1A5-D2A506EEA06B} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
-		{1249EE2B-EB0D-411C-B311-53A7A22B7743} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
-		{2BDEB8C6-F22D-43EA-A309-B3387A803689} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 		{8EF48E6C-8219-4EE2-87C6-5176D8D092E6} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 		{9BCD0764-9C16-4A2A-B153-C676FEF38887} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
-		{CA5EBC0F-52F8-4359-B7F5-8B33B563D279} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 		{E9C139E7-8078-477F-8BC3-C7A6BB5445CD} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{B08D919D-6ABC-49C7-882D-8011C792BEBF} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{BA4F359E-8472-4634-832D-2B75DA6EAB04} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{9C23412C-7C5D-4C62-ACF6-43A3242EF663} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{D9EBF2FD-F055-4C54-B921-6D31466092EC} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{85246331-432A-4084-8A80-63CA2758DE2D} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{3811FB79-A4F4-4059-A498-A1CB1B09A7B6} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{6407D19E-68B9-4E41-9A0D-F6B723FFD549} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{6245051F-835D-41EB-89E0-95652B51153C} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{2A54A13E-6D54-4BC6-836D-740F12B62D02} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{13484484-682F-4D7A-AEF2-ECAF3A601311} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{D5A9B447-642A-4655-A8BE-CC59AD6BE275} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{61696194-C130-4388-9607-DA2D5295DC1F} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{2AFF4096-A539-4A01-B4F4-60389C11C039} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{7E65B1D3-507E-4C89-92A9-D25D0097888B} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{D6BF8B8D-99A1-4072-9EF1-4A7BFB34330F} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{E9E3FBE7-AE00-4E03-9085-F8611B795526} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{40DFBBDD-B423-47A2-A64F-CE4B1F9897AB} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{8B147D8C-BDF5-4C04-8891-15014451CC63} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{57860CCA-903B-4871-89E8-A674D3EE4446} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
+		{3028FEDC-E937-4432-B74E-4E29304CD5C0} = {E118E6FE-67E7-4472-A8D7-E7F470E66131}
 	EndGlobalSection
 EndGlobal