Browse Source

Improved property conversion and added easing node

Krzysztof Krysiński 4 months ago
parent
commit
1ec1d56695

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 75401debddfa69cc21f617042aeb42b3e2b43c64
+Subproject commit d749d83f6090e058c9a0d68620316e32d753a977

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs

@@ -260,13 +260,13 @@ public class InputProperty<T> : InputProperty, IInputProperty<T>
                 value = shaderExpression.GetConstant();
             }
 
-            var validated = Validator.GetClosestValidValue(value);
-
-            if (!ConversionTable.TryConvert(validated, ValueType, out object result))
+            if (!ConversionTable.TryConvert(value, ValueType, out object result))
             {
-                return default(T);
+                result = default(T);
             }
 
+            result = Validator.GetClosestValidValue(result);
+
             return (T)result;
         }
     }

+ 203 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Animable/EasingNode.cs

@@ -0,0 +1,203 @@
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
+using PixiEditor.ChangeableDocument.Rendering;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Animable;
+
+[NodeInfo("Easing")]
+public class EasingNode : Node
+{
+    public FuncInputProperty<Float1> Value { get; }
+    public InputProperty<EasingType> Easing { get; }
+    public FuncOutputProperty<Float1> Output { get; }
+
+    public EasingNode()
+    {
+        Value = CreateFuncInput<Float1>("Value", "VALUE", 0.0);
+        Easing = CreateInput("EasingType", "EASING_TYPE", EasingType.Linear);
+        Output = CreateFuncOutput<Float1>("Output", "OUTPUT", Evaluate);
+    }
+
+    protected override void OnExecute(RenderContext context)
+    {
+    }
+
+    public Float1 Evaluate(FuncContext context)
+    {
+        var x = context.GetValue(Value);
+        if (!context.HasContext)
+        {
+            return new Float1(string.Empty) { ConstantValue = EvalCpu(x.ConstantValue) };
+        }
+
+        return context.NewFloat1(GetExpression(context));
+    }
+
+    private double EvalCpu(double x)
+    {
+        const double c1 = 1.70158;
+        const double c2 = c1 * 1.525;
+        const double c3 = c1 + 1;
+        const double c4 = (2 * Math.PI) / 3;
+        const double c5 = (2 * Math.PI) / 4.5;
+
+        EasingType easing = Easing.Value;
+        return easing switch
+        {
+            EasingType.Linear => x,
+            EasingType.InSine => 1 - Math.Cos((x * Math.PI) / 2),
+            EasingType.OutSine => Math.Sin((x * Math.PI) / 2),
+            EasingType.InOutSine => -(Math.Cos(Math.PI * x) - 1) / 2,
+            EasingType.InQuad => x * x,
+            EasingType.OutQuad => x * (2 - x),
+            EasingType.InOutQuad => x < 0.5 ? 2 * x * x : 1 - Math.Pow(-2 * x + 2, 2) / 2,
+            EasingType.InCubic => x * x * x,
+            EasingType.OutCubic => 1 - Math.Pow(1 - x, 3),
+            EasingType.InOutCubic => x < 0.5 ? 4 * x * x * x : 1 - Math.Pow(-2 * x + 2, 3) / 2,
+            EasingType.InQuart => x * x * x * x,
+            EasingType.OutQuart => 1 - Math.Pow(1 - x, 4),
+            EasingType.InOutQuart => x < 0.5 ? 8 * x * x * x * x : 1 - Math.Pow(-2 * x + 2, 4) / 2,
+            EasingType.InQuint => x * x * x * x * x,
+            EasingType.OutQuint => 1 - Math.Pow(1 - x, 5),
+            EasingType.InOutQuint => x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.Pow(-2 * x + 2, 5) / 2,
+            EasingType.InExpo => x == 0 ? 0 : Math.Pow(2, 10 * x - 10),
+            EasingType.OutExpo => x == 1 ? 1 : 1 - Math.Pow(2, -10 * x),
+            EasingType.InOutExpo => x == 0 ? 0 :
+                x == 1 ? 1 :
+                x < 0.5 ? Math.Pow(2, 20 * x - 10) / 2 : (2 - Math.Pow(2, -20 * x + 10)) / 2,
+            EasingType.InCirc => 1 - Math.Sqrt(1 - Math.Pow(x, 2)),
+            EasingType.OutCirc => Math.Sqrt(1 - Math.Pow(x - 1, 2)),
+            EasingType.InOutCirc => x < 0.5
+                ? (1 - Math.Sqrt(1 - Math.Pow(2 * x, 2))) / 2
+                : (Math.Sqrt(1 - Math.Pow(-2 * x + 2, 2)) + 1) / 2,
+            EasingType.InBack => c3 * x * x * x - c1 * x * x,
+            EasingType.OutBack => 1 + c3 * Math.Pow(x - 1, 3) + c1 * Math.Pow(x - 1, 2),
+            EasingType.InOutBack => x < 0.5
+                ? (Math.Pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2
+                : (Math.Pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2,
+            EasingType.InElastic => x switch
+            {
+                0 => 0,
+                1 => 1,
+                _ => -Math.Pow(2, 10 * x - 10) * Math.Sin((x * 10 - 10.75) * c4)
+            },
+            EasingType.OutElastic => x switch
+            {
+                0 => 0,
+                1 => 1,
+                _ => Math.Pow(2, -10 * x) * Math.Sin((x * 10 - 0.75) * c4) + 1
+            },
+            EasingType.InOutElastic => x == 0 ? 0 :
+                x == 1 ? 1 :
+                x < 0.5 ? -(Math.Pow(2, 20 * x - 10) * Math.Sin((20 * x - 11.125) * c5)) / 2 :
+                (Math.Pow(2, -20 * x + 10) * Math.Sin((20 * x - 11.125) * c5)) / 2 + 1,
+            EasingType.InBounce => 1 - OutBounce(1 - x),
+            EasingType.OutBounce => OutBounce(x),
+            EasingType.InOutBounce => x < 0.5 ? (1 - OutBounce(1 - 2 * x)) / 2 : (1 + OutBounce(2 * x - 1)) / 2,
+            _ => throw new ArgumentOutOfRangeException()
+        };
+    }
+
+    private double OutBounce(double x)
+    {
+        const double n1 = 7.5625;
+        const double d1 = 2.75;
+
+        if (x < 1 / d1)
+        {
+            return n1 * x * x;
+        }
+
+        if (x < 2 / d1)
+        {
+            return n1 * (x -= 1.5 / d1) * x + 0.75;
+        }
+
+        if (x < 2.5 / d1)
+        {
+            return n1 * (x -= 2.25 / d1) * x + 0.9375;
+        }
+
+        return n1 * (x -= 2.625 / d1) * x + 0.984375;
+    }
+
+    private Expression? GetExpression(FuncContext ctx)
+    {
+        Float1 x = ctx.GetValue(Value);
+        return Easing.Value switch
+        {
+            EasingType.Linear => ctx.GetValue(Value),
+            EasingType.InSine => ctx.Builder.Functions.GetInSine(x),
+            EasingType.OutSine => ctx.Builder.Functions.GetOutSine(x),
+            EasingType.InOutSine => ctx.Builder.Functions.GetInOutSine(x),
+            EasingType.InQuad => ctx.Builder.Functions.GetInQuad(x),
+            EasingType.OutQuad => ctx.Builder.Functions.GetOutQuad(x),
+            EasingType.InOutQuad => ctx.Builder.Functions.GetInOutQuad(x),
+            EasingType.InCubic => ctx.Builder.Functions.GetInCubic(x),
+            EasingType.OutCubic => ctx.Builder.Functions.GetOutCubic(x),
+            EasingType.InOutCubic => ctx.Builder.Functions.GetInOutCubic(x),
+            EasingType.InQuart => ctx.Builder.Functions.GetInQuart(x),
+            EasingType.OutQuart => ctx.Builder.Functions.GetOutQuart(x),
+            EasingType.InOutQuart => ctx.Builder.Functions.GetInOutQuart(x),
+            EasingType.InQuint => ctx.Builder.Functions.GetInQuint(x),
+            EasingType.OutQuint => ctx.Builder.Functions.GetOutQuint(x),
+            EasingType.InOutQuint => ctx.Builder.Functions.GetInOutQuint(x),
+            EasingType.InExpo => ctx.Builder.Functions.GetInExpo(x),
+            EasingType.OutExpo => ctx.Builder.Functions.GetOutExpo(x),
+            EasingType.InOutExpo => ctx.Builder.Functions.GetInOutExpo(x),
+            EasingType.InCirc => ctx.Builder.Functions.GetInCirc(x),
+            EasingType.OutCirc => ctx.Builder.Functions.GetOutCirc(x),
+            EasingType.InOutCirc => ctx.Builder.Functions.GetInOutCirc(x),
+            EasingType.InBack => ctx.Builder.Functions.GetInBack(x),
+            EasingType.OutBack => ctx.Builder.Functions.GetOutBack(x),
+            EasingType.InOutBack => ctx.Builder.Functions.GetInOutBack(x),
+            EasingType.InElastic => ctx.Builder.Functions.GetInElastic(x),
+            EasingType.OutElastic => ctx.Builder.Functions.GetOutElastic(x),
+            EasingType.InOutElastic => ctx.Builder.Functions.GetInOutElastic(x),
+            EasingType.InBounce => ctx.Builder.Functions.GetInBounce(x),
+            EasingType.OutBounce => ctx.Builder.Functions.GetOutBounce(x),
+            EasingType.InOutBounce => ctx.Builder.Functions.GetInOutBounce(x),
+            _ => throw new ArgumentOutOfRangeException()
+        };
+    }
+
+    public override Node CreateCopy()
+    {
+        return new EasingNode();
+    }
+}
+
+public enum EasingType
+{
+    Linear,
+    InSine,
+    OutSine,
+    InOutSine,
+    InQuad,
+    OutQuad,
+    InOutQuad,
+    InCubic,
+    OutCubic,
+    InOutCubic,
+    InQuart,
+    OutQuart,
+    InOutQuart,
+    InQuint,
+    OutQuint,
+    InOutQuint,
+    InExpo,
+    OutExpo,
+    InOutExpo,
+    InCirc,
+    OutCirc,
+    InOutCirc,
+    InBack,
+    OutBack,
+    InOutBack,
+    InElastic,
+    OutElastic,
+    InOutElastic,
+    InBounce,
+    OutBounce,
+    InOutBounce
+}

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

@@ -19,7 +19,7 @@ public class EllipseNode : ShapeNode<EllipseVectorData>
     {
         Center = CreateInput<VecD>("Position", "POSITION", VecI.Zero);
         Radius = CreateInput<VecD>("Radius", "RADIUS", new VecD(32, 32)).WithRules(
-            v => v.Min(new VecD(1)));
+            v => v.Min(new VecD(0)));
         StrokeColor = CreateInput<Paintable>("StrokeColor", "STROKE_COLOR", new Color(0, 0, 0, 255));
         FillColor = CreateInput<Paintable>("FillColor", "FILL_COLOR", new Color(0, 0, 0, 255));
         StrokeWidth = CreateInput<int>("StrokeWidth", "STROKE_WIDTH", 1);

BIN
src/PixiEditor.UI.Common/Fonts/PixiPerfect.ttf


+ 1 - 0
src/PixiEditor.UI.Common/Fonts/PixiPerfectIcons.axaml.cs

@@ -153,6 +153,7 @@ public static class PixiPerfectIcons
     public const string TextRound = "\uE999";
     public const string Cone = "\uE99c";
     public const string Camera = "\uE99d";
+    public const string ChartSpline = "\ue99e";
 
     public static Stream GetFontStream()
     {

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

@@ -920,4 +920,6 @@
   "MINUTE_UNIVERSAL": "min",
   "AUTOSAVE_SETTINGS_SAVE_USER_FILE": "Autosave to selected file",
   "LOAD_LAZY_FILE_MESSAGE": "To improve startup time, PixiEditor didn't load this file. Click the button below to load it.",
+  "EASING_NODE": "Easing",
+  "EASING_TYPE": "Easing Type"
 }

+ 11 - 0
src/PixiEditor/ViewModels/Document/Nodes/Animable/EasingNodeViewModel.cs

@@ -0,0 +1,11 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Animable;
+using PixiEditor.UI.Common.Fonts;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Animable;
+
+[NodeViewModel("EASING_NODE", "ANIMATION", PixiPerfectIcons.ChartSpline)]
+internal class EasingNodeViewModel : NodeViewModel<EasingNode>
+{
+
+}