Browse Source

Value validation rules, expr -> const conversion, previews for all nodes

flabbet 1 year ago
parent
commit
bab9a6575e

+ 46 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/FuncContext.cs

@@ -58,28 +58,67 @@ public class FuncContext
 
 
     public Float2 NewFloat2(Expression x, Expression y)
     public Float2 NewFloat2(Expression x, Expression y)
     {
     {
+        if (!HasContext && x is Float1 firstFloat && y is Float1 secondFloat)
+        {
+            Float2 constantFloat = new Float2("");
+            constantFloat.ConstantValue = new VecD(firstFloat.ConstantValue, secondFloat.ConstantValue);
+            return constantFloat;
+        }
+        
         return Builder.ConstructFloat2(x, y);
         return Builder.ConstructFloat2(x, y);
     }
     }
 
 
     public Float1 NewFloat1(Expression result)
     public Float1 NewFloat1(Expression result)
     {
     {
+        if (!HasContext && result is Float1 float1)
+        {
+            Float1 constantFloat = new Float1("");
+            constantFloat.ConstantValue = float1.ConstantValue;
+            return constantFloat;
+        }
+        
         return Builder.ConstructFloat1(result);
         return Builder.ConstructFloat1(result);
     }
     }
 
 
 
 
     public Int2 NewInt2(Expression first, Expression second)
     public Int2 NewInt2(Expression first, Expression second)
     {
     {
+        if (!HasContext && first is Int1 firstInt && second is Int1 secondInt)
+        {
+            Int2 constantInt = new Int2("");
+            constantInt.ConstantValue = new VecI(firstInt.ConstantValue, secondInt.ConstantValue);
+            return constantInt;
+        }
+        
         return Builder.ConstructInt2(first, second);
         return Builder.ConstructInt2(first, second);
     }
     }
 
 
     public Half4 NewHalf4(Expression r, Expression g, Expression b, Expression a)
     public Half4 NewHalf4(Expression r, Expression g, Expression b, Expression a)
     {
     {
+        if (!HasContext && r is Float1 firstFloat && g is Float1 secondFloat && b is Float1 thirdFloat && a is Float1 fourthFloat)
+        {
+            Half4 constantHalf4 = new Half4("");
+            byte rByte = (byte)firstFloat.ConstantValue;
+            byte gByte = (byte)secondFloat.ConstantValue;
+            byte bByte = (byte)thirdFloat.ConstantValue;
+            byte aByte = (byte)fourthFloat.ConstantValue;
+            constantHalf4.ConstantValue = new Color(rByte, gByte, bByte, aByte);
+            return constantHalf4;
+        }
+        
         return Builder.ConstructHalf4(r, g, b, a);
         return Builder.ConstructHalf4(r, g, b, a);
     }
     }
 
 
 
 
     public Half4 NewHalf4(Expression assignment)
     public Half4 NewHalf4(Expression assignment)
     {
     {
+        if (!HasContext && assignment is Half4 half4)
+        {
+            Half4 constantHalf4 = new Half4("");
+            constantHalf4.ConstantValue = half4.ConstantValue;
+            return constantHalf4;
+        }
+        
         return Builder.AssignNewHalf4(assignment);
         return Builder.AssignNewHalf4(assignment);
     }
     }
 
 
@@ -97,11 +136,14 @@ public class FuncContext
 
 
     public Expression GetValue(FuncInputProperty<Int1> getFrom)
     public Expression GetValue(FuncInputProperty<Int1> getFrom)
     {
     {
-        if (getFrom.Connection == null || !IsFuncType(getFrom))
+        if (HasContext)
         {
         {
-            string uniformName = $"int_{Builder.GetUniqueNameNumber()}";
-            Builder.AddUniform(uniformName, (int)getFrom.Value(this).ConstantValue);
-            return new Expression(uniformName);
+            if (getFrom.Connection == null || !IsFuncType(getFrom))
+            {
+                string uniformName = $"int_{Builder.GetUniqueNameNumber()}";
+                Builder.AddUniform(uniformName, (int)getFrom.Value(this).ConstantValue);
+                return new Expression(uniformName);
+            }
         }
         }
 
 
         return getFrom.Value(this);
         return getFrom.Value(this);

+ 40 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs

@@ -3,6 +3,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changes.NodeGraph;
 using PixiEditor.ChangeableDocument.Changes.NodeGraph;
 using PixiEditor.Common;
 using PixiEditor.Common;
+using PixiEditor.DrawingApi.Core.Shaders.Generation;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 
 
@@ -10,6 +11,8 @@ public class InputProperty : IInputProperty
 {
 {
     private object _internalValue;
     private object _internalValue;
     private int _lastExecuteHash = -1;
     private int _lastExecuteHash = -1;
+    private PropertyValidator? validator;
+    
     public string InternalPropertyName { get; }
     public string InternalPropertyName { get; }
     public string DisplayName { get; }
     public string DisplayName { get; }
 
 
@@ -46,6 +49,19 @@ public class InputProperty : IInputProperty
             _internalValue = value;
             _internalValue = value;
         }
         }
     }
     }
+    
+    public PropertyValidator Validator
+    {
+        get
+        {
+            if (validator is null)
+            {
+                validator = new PropertyValidator();
+            }
+
+            return validator;
+        }
+    }
 
 
     protected virtual object FuncFactory(object toReturn)
     protected virtual object FuncFactory(object toReturn)
     {
     {
@@ -153,16 +169,27 @@ public class InputProperty<T> : InputProperty, IInputProperty<T>
         {
         {
             object value = base.Value;
             object value = base.Value;
             if (value is null) return default(T);
             if (value is null) return default(T);
-            
-            if(value is T tValue)
+
+            if (value is T tValue)
                 return tValue;
                 return tValue;
 
 
             if (value is Delegate func && typeof(T).IsAssignableTo(typeof(Delegate)))
             if (value is Delegate func && typeof(T).IsAssignableTo(typeof(Delegate)))
             {
             {
-                return (T)FuncFactoryDelegate(func); 
+                return (T)FuncFactoryDelegate(func);
+            }
+            
+            object target = value;
+            if(value is ShaderExpressionVariable shaderExpression)
+            {
+                target = shaderExpression.GetConstant();
+            }
+
+            if (!ConversionTable.TryConvert(target, typeof(T), out object result))
+            {
+                return default;
             }
             }
 
 
-            return ConversionTable.TryConvert(value, typeof(T), out object result) ? (T)result : default;
+            return (T)Validator.GetClosestValidValue(result);
         }
         }
     }
     }
 
 
@@ -171,8 +198,15 @@ public class InputProperty<T> : InputProperty, IInputProperty<T>
         get => (T)(base.NonOverridenValue ?? default(T));
         get => (T)(base.NonOverridenValue ?? default(T));
         set => base.NonOverridenValue = value;
         set => base.NonOverridenValue = value;
     }
     }
-    
-    internal InputProperty(Node node, string internalName, string displayName, T defaultValue) : base(node, internalName, displayName, defaultValue, typeof(T))
+
+    internal InputProperty(Node node, string internalName, string displayName, T defaultValue) : base(node,
+        internalName, displayName, defaultValue, typeof(T))
+    {
+    }
+
+    public InputProperty<T> WithRules(Action<PropertyValidator> rules)
     {
     {
+        rules(Validator);
+        return this;
     }
     }
 }
 }

+ 34 - 24
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs

@@ -15,30 +15,39 @@ public class NoiseNode : Node
     private double previousSeed = double.NaN;
     private double previousSeed = double.NaN;
     private NoiseType previousNoiseType = Nodes.NoiseType.FractalPerlin;
     private NoiseType previousNoiseType = Nodes.NoiseType.FractalPerlin;
     private int previousOctaves = -1;
     private int previousOctaves = -1;
-    
+
     private Paint paint = new();
     private Paint paint = new();
-    
+
     private static readonly ColorFilter grayscaleFilter = ColorFilter.CreateColorMatrix(
     private static readonly ColorFilter grayscaleFilter = ColorFilter.CreateColorMatrix(
         ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
         ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
-    
+
     public OutputProperty<Texture> Noise { get; }
     public OutputProperty<Texture> Noise { get; }
-    
+
     public InputProperty<NoiseType> NoiseType { get; }
     public InputProperty<NoiseType> NoiseType { get; }
     public InputProperty<VecI> Size { get; }
     public InputProperty<VecI> Size { get; }
-    
+
     public InputProperty<double> Scale { get; }
     public InputProperty<double> Scale { get; }
-    
+
     public InputProperty<int> Octaves { get; }
     public InputProperty<int> Octaves { get; }
-    
+
     public InputProperty<double> Seed { get; }
     public InputProperty<double> Seed { get; }
 
 
     public NoiseNode()
     public NoiseNode()
     {
     {
         Noise = CreateOutput<Texture>(nameof(Noise), "NOISE", null);
         Noise = CreateOutput<Texture>(nameof(Noise), "NOISE", null);
         NoiseType = CreateInput(nameof(NoiseType), "NOISE_TYPE", Nodes.NoiseType.FractalPerlin);
         NoiseType = CreateInput(nameof(NoiseType), "NOISE_TYPE", Nodes.NoiseType.FractalPerlin);
-        Size = CreateInput(nameof(Size), "SIZE", new VecI(64, 64));
-        Scale = CreateInput(nameof(Scale), "SCALE", 10d);
-        Octaves = CreateInput(nameof(Octaves), "OCTAVES", 1);
+        Size = CreateInput(nameof(Size), "SIZE", new VecI(64, 64))
+            .WithRules(v =>
+                v.Min(
+                    VecI.One,
+                    vector => new VecI(Math.Max(1, vector.X), Math.Max(1, vector.Y))
+                )
+            );
+
+        Scale = CreateInput(nameof(Scale), "SCALE", 10d).WithRules(v => v.Min(0.1));
+        Octaves = CreateInput(nameof(Octaves), "OCTAVES", 1)
+            .WithRules(validator => validator.Min(1));
+
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
     }
     }
 
 
@@ -50,57 +59,58 @@ public class NoiseNode : Node
             || previousNoiseType != NoiseType.Value
             || previousNoiseType != NoiseType.Value
             || double.IsNaN(previousScale))
             || double.IsNaN(previousScale))
         {
         {
-            if(Scale.Value < 0.000001)
+            if (Scale.Value < 0.000001)
             {
             {
                 Noise.Value = null;
                 Noise.Value = null;
                 return null;
                 return null;
             }
             }
-            
+
             var shader = SelectShader();
             var shader = SelectShader();
             if (shader == null)
             if (shader == null)
             {
             {
                 Noise.Value = null;
                 Noise.Value = null;
                 return null;
                 return null;
             }
             }
-            
+
             paint.Shader = shader;
             paint.Shader = shader;
-            
+
             // Define a grayscale color filter to apply to the image
             // Define a grayscale color filter to apply to the image
-            paint.ColorFilter = grayscaleFilter; 
-            
+            paint.ColorFilter = grayscaleFilter;
+
             previousScale = Scale.Value;
             previousScale = Scale.Value;
             previousSeed = Seed.Value;
             previousSeed = Seed.Value;
             previousOctaves = Octaves.Value;
             previousOctaves = Octaves.Value;
             previousNoiseType = NoiseType.Value;
             previousNoiseType = NoiseType.Value;
         }
         }
-        
+
         var size = Size.Value;
         var size = Size.Value;
-        
+
         if (size.X < 1 || size.Y < 1)
         if (size.X < 1 || size.Y < 1)
         {
         {
             Noise.Value = null;
             Noise.Value = null;
             return null;
             return null;
         }
         }
-        
-        var workingSurface = RequestTexture(0, size); 
-       
+
+        var workingSurface = RequestTexture(0, size);
+
         workingSurface.DrawingSurface.Canvas.DrawPaint(paint);
         workingSurface.DrawingSurface.Canvas.DrawPaint(paint);
 
 
         Noise.Value = workingSurface;
         Noise.Value = workingSurface;
-        
+
         return Noise.Value;
         return Noise.Value;
     }
     }
 
 
     private Shader SelectShader()
     private Shader SelectShader()
     {
     {
+        int octaves = Math.Max(1, Octaves.Value);
         Shader shader = NoiseType.Value switch
         Shader shader = NoiseType.Value switch
         {
         {
             Nodes.NoiseType.TurbulencePerlin => Shader.CreatePerlinNoiseTurbulence(
             Nodes.NoiseType.TurbulencePerlin => Shader.CreatePerlinNoiseTurbulence(
                 (float)(1d / Scale.Value),
                 (float)(1d / Scale.Value),
-                (float)(1d / Scale.Value), Octaves.Value, (float)Seed.Value),
+                (float)(1d / Scale.Value), octaves, (float)Seed.Value),
             Nodes.NoiseType.FractalPerlin => Shader.CreatePerlinFractalNoise(
             Nodes.NoiseType.FractalPerlin => Shader.CreatePerlinFractalNoise(
                 (float)(1d / Scale.Value),
                 (float)(1d / Scale.Value),
-                (float)(1d / Scale.Value), Octaves.Value, (float)Seed.Value),
+                (float)(1d / Scale.Value), octaves, (float)Seed.Value),
             _ => null
             _ => null
         };
         };
 
 

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

@@ -0,0 +1,91 @@
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph;
+
+public delegate (bool validationResult, object? closestValidValue) ValidateProperty(object? value);
+
+public class PropertyValidator
+{
+    public List<ValidateProperty> Rules { get; } = new();
+
+    public PropertyValidator Min<T>(T min, Func<T, T>? adjust = null) where T : IComparable<T>
+    {
+        Rules.Add((value) =>
+        {
+            if (value is T val)
+            {
+                bool isValid = val.CompareTo(min) >= 0;
+                return (isValid, isValid ? val : GetReturnValue(val, min, adjust));
+            }
+
+            return (false, GetReturnValue(min, min, adjust));
+        });
+
+        return this;
+    }
+
+    private object? GetReturnValue<T>(T original, T min, Func<T, T>? fallback) where T : IComparable<T>
+    {
+        if (fallback != null)
+        {
+            return fallback(original);
+        }
+
+        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;
+
+        foreach (var rule in Rules)
+        {
+            var (isValid, toPass) = rule(lastValue);
+            lastValue = toPass;
+            if (!isValid)
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public object? GetClosestValidValue(object? o)
+    {
+        return Rules.Aggregate(o, (current, rule) => rule(current).closestValidValue);
+    }
+}

+ 34 - 1
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConnectProperties_Change.cs

@@ -1,4 +1,5 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
 using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
@@ -124,8 +125,15 @@ internal class ConnectProperties_Change : Change
             {
             {
                 return true;
                 return true;
             }
             }
+            
+            var outputValue = output.Value;
+            
+            if(IsExpressionToConstant(output, input, out var result))
+            {
+                outputValue = result;
+            }
 
 
-            if (ConversionTable.TryConvert(output.Value, input.ValueType, out _))
+            if (ConversionTable.TryConvert(outputValue, input.ValueType, out _))
             {
             {
                 return true;
                 return true;
             }
             }
@@ -136,6 +144,31 @@ internal class ConnectProperties_Change : Change
         return true;
         return true;
     }
     }
 
 
+    private static bool IsExpressionToConstant(OutputProperty output, InputProperty input, out object o)
+    {
+        if (output.Value is Delegate func && func.Method.ReturnType.IsAssignableTo(typeof(ShaderExpressionVariable)))
+        {
+            try
+            {
+                o = func.DynamicInvoke(FuncContext.NoContext);
+                if(o is ShaderExpressionVariable variable)
+                {
+                    o = variable.GetConstant();
+                }
+                
+                return true;
+            }
+            catch
+            {
+                o = null;
+                return false;
+            }
+        }
+
+        o = null;
+        return false;
+    }
+
     private static bool IsCrossExpression(object first, Type secondType)
     private static bool IsCrossExpression(object first, Type secondType)
     {
     {
         if (first is Delegate func && func.Method.ReturnType.IsAssignableTo(typeof(ShaderExpressionVariable)))
         if (first is Delegate func && func.Method.ReturnType.IsAssignableTo(typeof(ShaderExpressionVariable)))

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/CreateNode_Change.cs

@@ -43,7 +43,6 @@ internal class CreateNode_Change : Change
         ignoreInUndo = false;
         ignoreInUndo = false;
        
        
         using RenderingContext context = new RenderingContext(new KeyFrameTime(0, 0), VecI.Zero, ChunkResolution.Full, target.Size);
         using RenderingContext context = new RenderingContext(new KeyFrameTime(0, 0), VecI.Zero, ChunkResolution.Full, target.Size);
-        node.ExecuteInternal(context);
         
         
         return CreateNode_ChangeInfo.CreateFromNode(node); 
         return CreateNode_ChangeInfo.CreateFromNode(node); 
     }
     }

+ 24 - 11
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/UpdateProperty_Change.cs

@@ -10,7 +10,7 @@ internal class UpdatePropertyValue_Change : Change
     private readonly string _propertyName;
     private readonly string _propertyName;
     private object? _value;
     private object? _value;
     private object? previousValue;
     private object? previousValue;
-    
+
     [GenerateMakeChangeAction]
     [GenerateMakeChangeAction]
     public UpdatePropertyValue_Change(Guid nodeId, string property, object? value)
     public UpdatePropertyValue_Change(Guid nodeId, string property, object? value)
     {
     {
@@ -21,24 +21,37 @@ internal class UpdatePropertyValue_Change : Change
 
 
     public override bool InitializeAndValidate(Document target)
     public override bool InitializeAndValidate(Document target)
     {
     {
-        if(target.TryFindNode<Node>(_nodeId, out var node))
+        if (target.TryFindNode<Node>(_nodeId, out var node))
         {
         {
             return node.HasInputProperty(_propertyName);
             return node.HasInputProperty(_propertyName);
         }
         }
-        
+
         return false;
         return false;
     }
     }
 
 
-    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
+        out bool ignoreInUndo)
     {
     {
         var node = target.NodeGraph.Nodes.First(x => x.Id == _nodeId);
         var node = target.NodeGraph.Nodes.First(x => x.Id == _nodeId);
         var property = node.GetInputProperty(_propertyName);
         var property = node.GetInputProperty(_propertyName);
 
 
         previousValue = GetValue(property);
         previousValue = GetValue(property);
-        _value = SetValue(property, _value);
+        if (!property.Validator.Validate(_value))
+        {
+            _value = property.Validator.GetClosestValidValue(_value);
+            if (_value == previousValue)
+            {
+                ignoreInUndo = true;
+            }
+            
+            ignoreInUndo = false;
+        }
+        else
+        {
+            _value = SetValue(property, _value);
+            ignoreInUndo = false;
+        }
 
 
-        ignoreInUndo = false;
-        
         return new PropertyValueUpdated_ChangeInfo(_nodeId, _propertyName, _value);
         return new PropertyValueUpdated_ChangeInfo(_nodeId, _propertyName, _value);
     }
     }
 
 
@@ -47,7 +60,7 @@ internal class UpdatePropertyValue_Change : Change
         var node = target.NodeGraph.Nodes.First(x => x.Id == _nodeId);
         var node = target.NodeGraph.Nodes.First(x => x.Id == _nodeId);
         var property = node.GetInputProperty(_propertyName);
         var property = node.GetInputProperty(_propertyName);
         SetValue(property, previousValue);
         SetValue(property, previousValue);
-        
+
         return new PropertyValueUpdated_ChangeInfo(_nodeId, _propertyName, previousValue);
         return new PropertyValueUpdated_ChangeInfo(_nodeId, _propertyName, previousValue);
     }
     }
 
 
@@ -63,13 +76,13 @@ internal class UpdatePropertyValue_Change : Change
             {
             {
                 value = Enum.ToObject(property.ValueType, value);
                 value = Enum.ToObject(property.ValueType, value);
             }
             }
-            
+
             property.NonOverridenValue = value;
             property.NonOverridenValue = value;
         }
         }
-        
+
         return value;
         return value;
     }
     }
-    
+
 
 
     private static object? GetValue(InputProperty property)
     private static object? GetValue(InputProperty property)
     {
     {

+ 7 - 1
src/PixiEditor.Numerics/VecI.cs

@@ -1,6 +1,6 @@
 namespace PixiEditor.Numerics;
 namespace PixiEditor.Numerics;
 
 
-public struct VecI : IEquatable<VecI>
+public struct VecI : IEquatable<VecI>, IComparable<VecI>
 {
 {
     public int X { set; get; }
     public int X { set; get; }
     public int Y { set; get; }
     public int Y { set; get; }
@@ -191,6 +191,12 @@ public struct VecI : IEquatable<VecI>
         return $"({X}; {Y})";
         return $"({X}; {Y})";
     }
     }
 
 
+    public int CompareTo(VecI other)
+    {
+        int xComparison = X.CompareTo(other.X);
+        return xComparison * Y.CompareTo(other.Y); 
+    }
+
     public override bool Equals(object? obj)
     public override bool Equals(object? obj)
     {
     {
         var item = obj as VecI?;
         var item = obj as VecI?;

+ 2 - 2
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -737,8 +737,8 @@ internal class MemberPreviewUpdater
         
         
         if (outputNode is null)
         if (outputNode is null)
             return;
             return;
-        
-        var executionQueue = internals.Tracker.Document.NodeGraph.CalculateExecutionQueue(outputNode);
+
+        var executionQueue = internals.Tracker.Document.NodeGraph.AllNodes; //internals.Tracker.Document.NodeGraph.CalculateExecutionQueue(outputNode);
         
         
         foreach (var node in executionQueue) 
         foreach (var node in executionQueue) 
         {
         {