Browse Source

Connection conversions

flabbet 1 year ago
parent
commit
f0d70643e8
30 changed files with 207 additions and 80 deletions
  1. 5 26
      src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs
  2. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs
  3. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
  4. 0 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Animable/TimeNode.cs
  5. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs
  6. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs
  7. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecD.cs
  8. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecI.cs
  9. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs
  10. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs
  11. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs
  12. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs
  13. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs
  14. 7 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EllipseNode.cs
  15. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs
  16. 0 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  17. 0 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  18. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs
  19. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/KernelNode.cs
  20. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs
  21. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MatrixTransformNode.cs
  22. 0 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  23. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  24. 0 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  25. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  26. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  27. 0 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  28. 0 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  29. 22 0
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConnectProperties_Change.cs
  30. 170 0
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConversionTable.cs

+ 5 - 26
src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs

@@ -2,6 +2,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Changes.NodeGraph;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 
@@ -143,8 +144,11 @@ public class InputProperty<T> : InputProperty, IInputProperty<T>
         {
             object value = base.Value;
             if (value is null) return default(T);
+            
+            if(value is T tValue)
+                return tValue;
 
-            return (T)value;
+            return ConversionTable.TryConvert(value, typeof(T), out object result) ? (T)result : default;
         }
     }
 
@@ -153,31 +157,6 @@ public class InputProperty<T> : InputProperty, IInputProperty<T>
         get => (T)(base.NonOverridenValue ?? default(T));
         set => base.NonOverridenValue = value;
     }
-
-    /*
-    private T CastFunc(Func<FuncContext, object> func)
-    {
-        Type targetReturnType = Connection.ValueType;
-        Type funcType = typeof(Func<,>).MakeGenericType(typeof(FuncContext), targetReturnType);
-
-        var methodInfo = func.Method;
-        
-        // methodInfo returns Object, we need to wrap it so it returns targetReturnType
-        
-        MethodInfo castMethod = typeof(InputProperty<T>).GetMethod(nameof(Cast), BindingFlags.NonPublic | BindingFlags.Static)!;
-        
-        MethodInfo genericCastMethod = castMethod.MakeGenericMethod(targetReturnType);
-        
-        
-        
-        // T i Func<FuncContext, targetReturnType> so we need to return it
-        return (T)(object)Delegate.CreateDelegate(funcType, methodInfo);
-    }*/
-    
-    private static Func<FuncContext, T> Cast<T>(Func<FuncContext, object> func)
-    {
-        return context => (T)func(context);
-    }
     
     internal InputProperty(Node node, string internalName, string displayName, T defaultValue) : base(node, internalName, displayName, defaultValue, typeof(T))
     {

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs

@@ -24,7 +24,6 @@ public interface IReadOnlyNode
     /// </summary>
     /// <example>Divide node has two inputs, if the second input is 0, the node should not be executed. Since division by 0 is illegal</example>
     /// <returns>True if the inputs are legal, false otherwise.</returns>
-    public bool AreInputsLegal();
     
     /// <summary>
     ///     Traverses the graph backwards from this node. Backwards means towards the input nodes.

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs

@@ -36,7 +36,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         _nodes.Remove(node);
     }
 
-    private Queue<IReadOnlyNode> CalculateExecutionQueue(OutputNode outputNode, bool validate = true)
+    private Queue<IReadOnlyNode> CalculateExecutionQueue(OutputNode outputNode)
     {
         // backwards breadth-first search
         var visited = new HashSet<IReadOnlyNode>();
@@ -47,7 +47,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         while (queueNodes.Count > 0)
         {
             var node = queueNodes.Dequeue();
-            if (!visited.Add(node) || (validate && !node.AreInputsLegal()))
+            if (!visited.Add(node))
             {
                 continue;
             }
@@ -85,7 +85,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
     {
         if(OutputNode == null) return false;
         
-        var queue = CalculateExecutionQueue(OutputNode, false);
+        var queue = CalculateExecutionQueue(OutputNode);
         
         while (queue.Count > 0)
         {

+ 0 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Animable/TimeNode.cs

@@ -26,11 +26,6 @@ public class TimeNode : Node
         return null;
     }
 
-    public override bool AreInputsLegal()
-    {
-        return true;
-    }
-
     public override Node CreateCopy()
     {
         return new TimeNode();

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs

@@ -107,7 +107,6 @@ public class CombineChannelsNode : Node
     }
 
     public override string DisplayName { get; set; } = "COMBINE_CHANNELS_NODE";
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new CombineChannelsNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs

@@ -45,7 +45,6 @@ public class CombineColorNode : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new CombineColorNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecD.cs

@@ -39,7 +39,6 @@ public class CombineVecD : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new CombineVecD();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecI.cs

@@ -37,7 +37,6 @@ public class CombineVecI : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new CombineVecI();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs

@@ -91,7 +91,6 @@ public class SeparateChannelsNode : Node
         return imageSurface;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new SeparateChannelsNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs

@@ -33,7 +33,6 @@ public class SeparateColorNode : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new SeparateColorNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs

@@ -27,7 +27,6 @@ public class SeparateVecDNode : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new SeparateVecDNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs

@@ -27,7 +27,6 @@ public class SeparateVecINode : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new SeparateVecINode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs

@@ -50,7 +50,6 @@ public class DebugBlendModeNode : Node
         return workingSurface;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new DebugBlendModeNode();
 }

+ 7 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EllipseNode.cs

@@ -43,6 +43,13 @@ public class EllipseNode : Node
             workingImage.LatestSize.Y != targetDimensions.Y)
         {
             workingImage?.Dispose();
+
+            if (Radius.Value.LongestAxis <= 0)
+            {
+                Output.Value = null;
+                return null;
+            }
+            
             workingImage = new ChunkyImage(targetDimensions);
 
             targetSurface = new Surface(targetDimensions);
@@ -70,10 +77,5 @@ public class EllipseNode : Node
 
     public override string DisplayName { get; set; } = "ELLIPSE_NODE";
 
-    public override bool AreInputsLegal()
-    {
-        return Radius.Value is { X: > 0, Y: > 0 } && StrokeWidth.Value > 0;
-    }
-
     public override Node CreateCopy() => new EllipseNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs

@@ -39,7 +39,6 @@ public class CreateImageNode : Node
     }
  
     public override string DisplayName { get; set; } = "CREATE_IMAGE_NODE";
-    public override bool AreInputsLegal() => Size.Value is { X: > 0, Y: > 0 };
 
     public override Node CreateCopy() => new CreateImageNode();
 }

+ 0 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs

@@ -16,11 +16,6 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
         Content = CreateInput<Surface?>("Content", "CONTENT", null);
     }
 
-    public override bool AreInputsLegal()
-    {
-        return true;
-    }
-
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
 
     protected override string NodeUniqueName => "Folder";

+ 0 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -36,11 +36,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return GetLayerImageAtFrame(frameTime.Frame).FindTightCommittedBounds();
     }
 
-    public override bool AreInputsLegal()
-    {
-        return true;
-    }
-
     protected override Surface? OnExecute(RenderingContext context)
     {
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs

@@ -26,7 +26,6 @@ public class ImageSpaceNode : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new ImageSpaceNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/KernelNode.cs

@@ -59,7 +59,6 @@ public class KernelFilterNode : Node
         return workingSurface;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new KernelFilterNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs

@@ -60,7 +60,6 @@ public class MathNode : Node
         return null;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new MathNode();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MatrixTransformNode.cs

@@ -54,7 +54,6 @@ public class MatrixTransformNode : Node
         return Transformed.Value;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new MatrixTransformNode();
 }

+ 0 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs

@@ -21,11 +21,6 @@ public class MergeNode : Node, IBackgroundInput
 
     public override string DisplayName { get; set; } = "MERGE_NODE";
 
-    public override bool AreInputsLegal()
-    {
-        return Top.Connection != null || Bottom.Connection != null;
-    }
-
     public override Node CreateCopy()
     {
         return new MergeNode();

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs

@@ -54,7 +54,6 @@ public class ModifyImageLeftNode : Node
         return Image.Value;
     }
 
-    public override bool AreInputsLegal() => true;
 
     public override Node CreateCopy() => new ModifyImageLeftNode();
 }

+ 0 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs

@@ -65,7 +65,5 @@ public class ModifyImageRightNode : Node
         return Output.Value;
     }
 
-    public override bool AreInputsLegal() => true;
-
     public override Node CreateCopy() => throw new NotImplementedException();
 }

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs

@@ -81,7 +81,6 @@ public abstract class Node : IReadOnlyNode, IDisposable
     }
 
     protected abstract Surface? OnExecute(RenderingContext context);
-    public abstract bool AreInputsLegal();
 
     protected virtual bool CacheChanged(RenderingContext context)
     {

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs

@@ -97,7 +97,6 @@ public class NoiseNode : Node
     }
 
     public override string DisplayName { get; set; } = "NOISE_NODE";
-    public override bool AreInputsLegal() => Size.Value is { X: > 0, Y: > 0 }; 
 
     public override Node CreateCopy() => new NoiseNode();
 }

+ 0 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs

@@ -13,11 +13,6 @@ public class OutputNode : Node, IBackgroundInput
     {
         Input = CreateInput<Surface>("Background", "INPUT", null);
     }
-    
-    public override bool AreInputsLegal()
-    {
-        return Input.Connection != null;
-    }
 
     public override Node CreateCopy()
     {

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs

@@ -47,7 +47,6 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
     }
 
     protected abstract override Surface? OnExecute(RenderingContext context);
-    public abstract override bool AreInputsLegal();
 
     protected Surface TryInitWorkingSurface(VecI imageSize, RenderingContext context)
     {

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

@@ -40,6 +40,13 @@ internal class ConnectProperties_Change : Change
         {
             return false;
         }
+        
+        bool canConnect = CheckTypeCompatibility(inputProp, outputProp);
+        
+        if (!canConnect)
+        {
+            return false;
+        }
 
         originalConnection = inputProp.Connection;
 
@@ -90,4 +97,19 @@ internal class ConnectProperties_Change : Change
 
         return changes;
     }
+    
+    private static bool CheckTypeCompatibility(InputProperty input, OutputProperty output)
+    {
+        if (input.ValueType != output.ValueType)
+        {
+            if(ConversionTable.TryConvert(output.Value, input.ValueType, out _))
+            {
+                return true;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
 }

+ 170 - 0
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConversionTable.cs

@@ -0,0 +1,170 @@
+using System.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
+
+public static class ConversionTable
+{
+    private static readonly Dictionary<Type, List<(Type, ITypeConverter)>> _conversionTable =
+        new()
+        {
+            {
+                typeof(double), 
+                [
+                    (typeof(int), new TypeConverter<double, int>(DoubleToInt)),
+                    (typeof(VecD), new TypeConverter<double, VecD>(DoubleToVecD)),
+                    (typeof(VecI), new TypeConverter<double, VecI>(DoubleToVecI))
+                ]
+            },
+            {
+                typeof(int), 
+                [
+                    (typeof(double), new TypeConverter<int, double>(ConvertIntToDouble)),
+                    (typeof(VecI), new TypeConverter<int, VecI>(IntToVecI)),
+                    (typeof(VecD), new TypeConverter<int, VecD>(IntToVecD)),
+                ]
+            },
+            {
+                typeof(VecD), 
+                [
+                    (typeof(double), new TypeConverter<VecD, double>(VecDToDouble)),
+                    (typeof(int), new TypeConverter<VecD, int>(VecDToInt)),
+                    (typeof(VecI), new TypeConverter<VecD, VecI>(VecDToVecI)),
+                ]
+            },
+            {
+                typeof(VecI),
+                [
+                    (typeof(double), new TypeConverter<VecI, double>(VecIToDouble)),
+                    (typeof(int), new TypeConverter<VecI, int>(VecIToInt)),
+                    (typeof(VecD), new TypeConverter<VecI, VecD>(VecIToVecD))
+                ]
+            }
+        };
+
+    public static bool TryConvert(object arg, Type targetType, out object result)
+    {
+        if (arg is Delegate func)
+        {
+            try
+            {
+                var actualArg = func.DynamicInvoke(FuncContext.NoContext);
+                return TryConvert(actualArg, targetType, out result);
+            }
+            catch
+            {
+                result = null;
+                return false;
+            }
+        }
+        
+        if (_conversionTable.TryGetValue(arg.GetType(), out var converters))
+        {
+            foreach (var (outType, converter) in converters)
+            {
+                if (targetType == outType)
+                {
+                    result = converter.Convert(arg);
+                    return true;
+                }
+            }
+        }
+        
+        try
+        {
+            result = Convert.ChangeType(arg, targetType);
+            return true;
+        }
+        catch
+        {
+            result = null;
+            return false;
+        }
+    }
+
+    private static int DoubleToInt(double d)
+    {
+        return (int)d;
+    }
+
+    private static double ConvertIntToDouble(int i)
+    {
+        return i;
+    }
+    
+    private static double VecDToDouble(VecD vec)
+    {
+        return vec.X;
+    } 
+    
+    private static double VecIToDouble(VecI vecI)
+    {
+        return vecI.X;
+    }
+    
+    private static VecD DoubleToVecD(double d)
+    {
+        return new VecD(d, d);
+    }
+    
+    private static VecI DoubleToVecI(double d)
+    {
+        return new VecI((int)d, (int)d);
+    }
+    
+    private static VecI IntToVecI(int i)
+    {
+        return new VecI(i, i);
+    }
+    
+    private static VecD IntToVecD(int i)
+    {
+        return new VecD(i, i);
+    }
+    
+    private static int VecIToInt(VecI vec)
+    {
+        return vec.X;
+    }
+    
+    private static VecD VecIToVecD(VecI vec)
+    {
+        return new VecD(vec.X, vec.Y);
+    }
+    
+    private static VecI VecDToVecI(VecD vec)
+    {
+        return new VecI((int)vec.X, (int)vec.Y);
+    }
+    
+    private static int VecDToInt(VecD vec)
+    {
+        return (int)vec.X;
+    }
+}
+
+interface ITypeConverter
+{
+    public object Convert(object arg);
+}
+
+class TypeConverter<TIn, TOut> : ITypeConverter
+{
+    private readonly Func<TIn, TOut> _converter;
+
+    public TypeConverter(Func<TIn, TOut> converter)
+    {
+        _converter = converter;
+    }
+
+    public TOut Convert(TIn arg)
+    {
+        return _converter(arg);
+    }
+
+    object ITypeConverter.Convert(object arg)
+    {
+        return _converter((TIn)arg);
+    }
+}