Przeglądaj źródła

Sample based brushes wip

Krzysztof Krysiński 2 tygodni temu
rodzic
commit
5d86623074

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 3c06f52bee39798848ee1f9274a0c68c496812b6
+Subproject commit a012b67aa8bdd3047593073561fc97fdfaea931d

+ 0 - 1
src/PixiEditor.ChangeableDocument/Changeables/Brushes/BrushData.cs

@@ -10,7 +10,6 @@ public struct BrushData
 {
 {
     public IReadOnlyNodeGraph BrushGraph { get; set; }
     public IReadOnlyNodeGraph BrushGraph { get; set; }
     public bool AntiAliasing { get; set; }
     public bool AntiAliasing { get; set; }
-    public float Hardness { get; set; }
     public float Spacing { get; set; }
     public float Spacing { get; set; }
     public float StrokeWidth { get; set; }
     public float StrokeWidth { get; set; }
 
 

+ 29 - 4
src/PixiEditor.ChangeableDocument/Changeables/Brushes/BrushEngine.cs

@@ -7,7 +7,9 @@ using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 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.Brushes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Brushes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering;
@@ -17,6 +19,7 @@ namespace PixiEditor.ChangeableDocument.Changeables.Brushes;
 
 
 internal class BrushEngine
 internal class BrushEngine
 {
 {
+    private TextureCache cache = new();
     public void ExecuteBrush(ChunkyImage target, BrushData brushData, VecI point, KeyFrameTime frameTime, ColorSpace cs, SamplingOptions samplingOptions, PointerInfo pointerInfo, EditorData editorData)
     public void ExecuteBrush(ChunkyImage target, BrushData brushData, VecI point, KeyFrameTime frameTime, ColorSpace cs, SamplingOptions samplingOptions, PointerInfo pointerInfo, EditorData editorData)
     {
     {
         ExecuteVectorShapeBrush(target, brushData, point, frameTime, cs, samplingOptions, pointerInfo, editorData);
         ExecuteVectorShapeBrush(target, brushData, point, frameTime, cs, samplingOptions, pointerInfo, editorData);
@@ -32,10 +35,14 @@ internal class BrushEngine
             return;
             return;
         }
         }
 
 
+        float strokeWidth = brushData.StrokeWidth;
+        var rect = new RectI(point - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
         VecI size = new VecI((int)float.Ceiling(brushData.StrokeWidth));
         VecI size = new VecI((int)float.Ceiling(brushData.StrokeWidth));
+
         using var texture = Texture.ForDisplay(size);
         using var texture = Texture.ForDisplay(size);
-        RenderContext context = new RenderContext(texture.DrawingSurface, frameTime, ChunkResolution.Full, size, size,
-            colorSpace, samplingOptions) { PointerInfo = pointerInfo, EditorData = editorData };
+        var surfaceUnderRect = UpdateSurfaceUnderRect(target, rect, colorSpace);
+        BrushRenderContext context = new BrushRenderContext(texture.DrawingSurface, frameTime, ChunkResolution.Full, size, size,
+            colorSpace, samplingOptions, brushData, surfaceUnderRect) { PointerInfo = pointerInfo, EditorData = editorData };
 
 
         brushData.BrushGraph.Execute(brushNode, context);
         brushData.BrushGraph.Execute(brushNode, context);
 
 
@@ -51,8 +58,6 @@ internal class BrushEngine
             return;
             return;
         }
         }
 
 
-        float strokeWidth = brushData.StrokeWidth;
-        var rect = new RectI(point - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
 
 
         path.Offset(vectorShape.TransformedAABB.Pos - vectorShape.GeometryAABB.Pos);
         path.Offset(vectorShape.TransformedAABB.Pos - vectorShape.GeometryAABB.Pos);
         path.Offset(rect.Center - path.Bounds.Center);
         path.Offset(rect.Center - path.Bounds.Center);
@@ -75,6 +80,18 @@ internal class BrushEngine
             (float)rect.Center.Y);
             (float)rect.Center.Y);
         path.Transform(pressureScale);
         path.Transform(pressureScale);
 
 
+        if (brushNode.Content.Value != null)
+        {
+            var brushTexture = brushNode.ContentTexture;
+            if (brushTexture != null)
+            {
+                TexturePaintable brushTexturePaintable = new(brushTexture);
+                target.EnqueueDrawPath(path, brushTexturePaintable, vectorShape.StrokeWidth,
+                    StrokeCap.Butt, brushData.BlendMode, PaintStyle.Fill, brushData.AntiAliasing);
+                return;
+            }
+        }
+
         StrokeCap strokeCap = StrokeCap.Butt;
         StrokeCap strokeCap = StrokeCap.Butt;
         PaintStyle strokeStyle = PaintStyle.Fill;
         PaintStyle strokeStyle = PaintStyle.Fill;
 
 
@@ -107,4 +124,12 @@ internal class BrushEngine
                 strokeCap, brushData.BlendMode, strokeStyle, brushData.AntiAliasing);
                 strokeCap, brushData.BlendMode, strokeStyle, brushData.AntiAliasing);
         }
         }
     }
     }
+
+    private Texture UpdateSurfaceUnderRect(ChunkyImage target, RectI rect, ColorSpace colorSpace)
+    {
+        var surfaceUnderRect = cache.RequestTexture(0, rect.Size, colorSpace);
+
+        target.DrawCommittedRegionOn(rect, ChunkResolution.Full, surfaceUnderRect.DrawingSurface, VecI.Zero);
+        return surfaceUnderRect;
+    }
 }
 }

+ 21 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/BrushRenderContext.cs

@@ -0,0 +1,21 @@
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
+using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Changeables.Brushes;
+using PixiEditor.ChangeableDocument.Rendering;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Context;
+
+internal class BrushRenderContext : RenderContext
+{
+    public BrushData BrushData { get; }
+    public Texture TargetSampledTexture { get; }
+
+    public BrushRenderContext(DrawingSurface renderSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution, VecI renderOutputSize, VecI documentSize, ColorSpace processingColorSpace, SamplingOptions desiredSampling, BrushData brushData, Texture targetSampledTexture, double opacity = 1) : base(renderSurface, frameTime, chunkResolution, renderOutputSize, documentSize, processingColorSpace, desiredSampling, opacity)
+    {
+        BrushData = brushData;
+        TargetSampledTexture = targetSampledTexture;
+    }
+}

+ 15 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Brushes/BrushOutputNode.cs

@@ -1,4 +1,5 @@
-using Drawie.Backend.Core.ColorsImpl.Paintables;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl.Paintables;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering;
@@ -11,15 +12,22 @@ public class BrushOutputNode : Node
     public InputProperty<ShapeVectorData> VectorShape { get; }
     public InputProperty<ShapeVectorData> VectorShape { get; }
     public InputProperty<Paintable> Stroke { get; }
     public InputProperty<Paintable> Stroke { get; }
     public InputProperty<Paintable> Fill { get; }
     public InputProperty<Paintable> Fill { get; }
+    public RenderInputProperty Content { get; }
     public InputProperty<float> Pressure { get; }
     public InputProperty<float> Pressure { get; }
     public InputProperty<bool> FitToStrokeSize { get; }
     public InputProperty<bool> FitToStrokeSize { get; }
 
 
+    internal Texture ContentTexture;
+
+    private TextureCache cache = new();
+
+    protected override bool ExecuteOnlyOnCacheChange => true;
 
 
     public BrushOutputNode()
     public BrushOutputNode()
     {
     {
-        VectorShape = CreateInput<ShapeVectorData>("VectorShape", "VECTOR_SHAPE", null);
+        VectorShape = CreateInput<ShapeVectorData>("VectorShape", "SHAPE", null);
         Stroke = CreateInput<Paintable>("Stroke", "STROKE", null);
         Stroke = CreateInput<Paintable>("Stroke", "STROKE", null);
         Fill = CreateInput<Paintable>("Fill", "FILL", null);
         Fill = CreateInput<Paintable>("Fill", "FILL", null);
+        Content = CreateRenderInput("Content", "CONTENT");
 
 
         Pressure = CreateInput<float>("Pressure", "PRESSURE", 1f);
         Pressure = CreateInput<float>("Pressure", "PRESSURE", 1f);
         FitToStrokeSize = CreateInput<bool>("FitToStrokeSize", "FIT_TO_STROKE_SIZE", true);
         FitToStrokeSize = CreateInput<bool>("FitToStrokeSize", "FIT_TO_STROKE_SIZE", true);
@@ -27,7 +35,11 @@ public class BrushOutputNode : Node
 
 
     protected override void OnExecute(RenderContext context)
     protected override void OnExecute(RenderContext context)
     {
     {
-
+        if (Content.Value != null)
+        {
+            ContentTexture = cache.RequestTexture(0, context.RenderOutputSize, context.ProcessingColorSpace);
+            Content.Value.Paint(context, ContentTexture.DrawingSurface);
+        }
     }
     }
 
 
     public override Node CreateCopy()
     public override Node CreateCopy()

+ 46 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Brushes/StrokeInfoNode.cs

@@ -0,0 +1,46 @@
+using Drawie.Backend.Core;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
+using PixiEditor.ChangeableDocument.Rendering;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Brushes;
+
+[NodeInfo("StrokeInfo")]
+public class StrokeInfoNode : Node
+{
+    public OutputProperty<float> StrokeWidth { get; }
+    public OutputProperty<float> Spacing { get; }
+    public OutputProperty<Texture> TargetSmallTexture { get; }
+    public OutputProperty<Texture> TargetFullTexture { get; }
+
+    public StrokeInfoNode()
+    {
+        StrokeWidth = CreateOutput<float>("StrokeWidth", "STROKE_WIDTH", 1f);
+        Spacing = CreateOutput<float>("Spacing", "SPACING", 0.1f);
+        TargetSmallTexture = CreateOutput<Texture>("TargetSmallTexture", "TARGET_SMALL_TEXTURE", null);
+        TargetFullTexture = CreateOutput<Texture>("TargetFullTexture", "TARGET_FULL_TEXTURE", null);
+    }
+
+    protected override void OnExecute(RenderContext context)
+    {
+        if (context is not BrushRenderContext brushRenderContext)
+            return;
+
+        StrokeWidth.Value = brushRenderContext.BrushData.StrokeWidth;
+        Spacing.Value = brushRenderContext.BrushData.Spacing;
+
+        if (TargetSmallTexture.Connections.Count > 0)
+        {
+            TargetSmallTexture.Value = brushRenderContext.TargetSampledTexture;
+        }
+
+        if (TargetFullTexture.Connections.Count > 0)
+        {
+            // TODO: Implement
+        }
+    }
+
+    public override Node CreateCopy()
+    {
+        return new StrokeInfoNode();
+    }
+}

+ 12 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/GradientNode.cs

@@ -61,7 +61,7 @@ public class GradientNode : Node
 
 
             if (!HasInputProperty("Radius"))
             if (!HasInputProperty("Radius"))
             {
             {
-                Radius = CreateInput<double>("Radius", "RADIUS", 0.5);
+                Radius = CreateInput<double>("Radius", "RADIUS", 0.5).WithRules(x => x.Min(0d));
             }
             }
         }
         }
         else if (type == GradientType.Conical)
         else if (type == GradientType.Conical)
@@ -83,19 +83,25 @@ public class GradientNode : Node
 
 
     private void RegenerateStops()
     private void RegenerateStops()
     {
     {
-        foreach (var (colorInput, positionInput) in ColorStops)
+        if (StopsCount.Value < ColorStops.Count)
         {
         {
-            RemoveInputProperty(colorInput);
-            RemoveInputProperty(positionInput);
+            int diff = ColorStops.Count - StopsCount.Value;
+            var keysToRemove = ColorStops.Keys.TakeLast(diff).ToList();
+            foreach (var key in keysToRemove)
+            {
+                RemoveInputProperty(key);
+                RemoveInputProperty(ColorStops[key]);
+                ColorStops.Remove(key);
+            }
         }
         }
 
 
-        ColorStops.Clear();
         GenerateStops();
         GenerateStops();
     }
     }
 
 
     private void GenerateStops()
     private void GenerateStops()
     {
     {
-        for (int i = 0; i < StopsCount.Value; i++)
+        int startIndex = ColorStops.Count;
+        for (int i = startIndex; i < StopsCount.Value; i++)
         {
         {
             var colorInput = CreateInput<Color>($"ColorStop{i + 1}Color", $"COLOR_STOP_COLOR",
             var colorInput = CreateInput<Color>($"ColorStop{i + 1}Color", $"COLOR_STOP_COLOR",
                 Drawie.Backend.Core.ColorsImpl.Colors.White);
                 Drawie.Backend.Core.ColorsImpl.Colors.White);

+ 3 - 25
src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs

@@ -30,7 +30,6 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     private Guid brushOutputGuid;
     private Guid brushOutputGuid;
     private BrushData brushData;
     private BrushData brushData;
     private BrushEngine engine = new BrushEngine();
     private BrushEngine engine = new BrushEngine();
-    private float hardness;
     private float spacing = 1;
     private float spacing = 1;
     private readonly Paint srcPaint = new Paint() { BlendMode = BlendMode.Src };
     private readonly Paint srcPaint = new Paint() { BlendMode = BlendMode.Src };
     private Paintable? finalPaintable;
     private Paintable? finalPaintable;
@@ -47,7 +46,6 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     [GenerateUpdateableChangeActions]
     [GenerateUpdateableChangeActions]
     public LineBasedPen_UpdateableChange(Guid memberGuid, Color color, VecI pos, float strokeWidth, bool erasing,
     public LineBasedPen_UpdateableChange(Guid memberGuid, Color color, VecI pos, float strokeWidth, bool erasing,
         bool antiAliasing,
         bool antiAliasing,
-        float hardness,
         float spacing,
         float spacing,
         Guid brushOutputGuid,
         Guid brushOutputGuid,
         bool drawOnMask, int frame, PointerInfo pointerInfo, EditorData editorData)
         bool drawOnMask, int frame, PointerInfo pointerInfo, EditorData editorData)
@@ -59,7 +57,6 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         this.antiAliasing = antiAliasing;
         this.antiAliasing = antiAliasing;
         this.drawOnMask = drawOnMask;
         this.drawOnMask = drawOnMask;
         this.brushOutputGuid = brushOutputGuid;
         this.brushOutputGuid = brushOutputGuid;
-        this.hardness = hardness;
         this.spacing = spacing;
         this.spacing = spacing;
         points.Add(pos);
         points.Add(pos);
         this.frame = frame;
         this.frame = frame;
@@ -127,7 +124,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         {
         {
             brushData = new BrushData(brushData.BrushGraph)
             brushData = new BrushData(brushData.BrushGraph)
             {
             {
-                StrokeWidth = strokeWidth, AntiAliasing = antiAliasing, Hardness = hardness, Spacing = spacing,
+                StrokeWidth = strokeWidth, AntiAliasing = antiAliasing, Spacing = spacing,
             };
             };
         }
         }
     }
     }
@@ -138,7 +135,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
 
 
         int opCount = image.QueueLength;
         int opCount = image.QueueLength;
 
 
-        float spacingPixels = strokeWidth * spacing;
+        float spacingPixels = (strokeWidth * pointerInfo.Pressure) * spacing;
 
 
         for (int i = Math.Max(lastAppliedPointIndex, 0); i < points.Count; i++)
         for (int i = Math.Max(lastAppliedPointIndex, 0); i < points.Count; i++)
         {
         {
@@ -150,7 +147,6 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             finalPaintable = color;
             finalPaintable = color;
 
 
             brushData.AntiAliasing = antiAliasing;
             brushData.AntiAliasing = antiAliasing;
-            brushData.Hardness = hardness;
             brushData.Spacing = spacing;
             brushData.Spacing = spacing;
             brushData.StrokeWidth = strokeWidth;
             brushData.StrokeWidth = strokeWidth;
 
 
@@ -205,7 +201,6 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     private void FastforwardEnqueueDrawLines(ChunkyImage targetImage, KeyFrameTime frameTime)
     private void FastforwardEnqueueDrawLines(ChunkyImage targetImage, KeyFrameTime frameTime)
     {
     {
         brushData.AntiAliasing = antiAliasing;
         brushData.AntiAliasing = antiAliasing;
-        brushData.Hardness = hardness;
         brushData.Spacing = spacing;
         brushData.Spacing = spacing;
         brushData.StrokeWidth = strokeWidth;
         brushData.StrokeWidth = strokeWidth;
 
 
@@ -222,7 +217,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
 
 
         VecF lastPos = points[0];
         VecF lastPos = points[0];
 
 
-        float spacingInPixels = strokeWidth * this.spacing;
+        float spacingInPixels = (strokeWidth * pointerInfo.Pressure) * this.spacing;
 
 
         for (int i = 0; i < points.Count; i++)
         for (int i = 0; i < points.Count; i++)
         {
         {
@@ -238,23 +233,6 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         }
         }
     }
     }
 
 
-    private Paintable? ApplySoftnessGradient(VecD pos)
-    {
-        srcPaint.Paintable?.Dispose();
-        if (hardness >= 1)
-        {
-            return new ColorPaintable(color);
-        }
-
-        float radius = strokeWidth / 2f;
-        radius = MathF.Max(1, radius);
-        return new RadialGradientPaintable(pos, radius,
-        [
-            new GradientStop(color, Math.Max(hardness - 0.05f, 0)),
-            new GradientStop(color.WithAlpha(0), 0.95f)
-        ]) { AbsoluteValues = true };
-    }
-
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
         out bool ignoreInUndo)
         out bool ignoreInUndo)
     {
     {

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

@@ -5,10 +5,12 @@ using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.ColorsImpl.Paintables;
 using Drawie.Backend.Core.ColorsImpl.Paintables;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using Drawie.Backend.Core.Shaders.Generation;
 using Drawie.Backend.Core.Shaders.Generation;
+using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Rendering;
 
 
 namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
 namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
 
 
@@ -76,6 +78,7 @@ public static class ConversionTable
             {
             {
                 typeof(Texture), [
                 typeof(Texture), [
                     (typeof(Paintable), new TypeConverter<Texture, Paintable>(img => new TexturePaintable(img))),
                     (typeof(Paintable), new TypeConverter<Texture, Paintable>(img => new TexturePaintable(img))),
+                    (typeof(Painter), new TypeConverter<Texture, Painter>(img => new Painter((c, s) => s.Canvas.DrawSurface(img.DrawingSurface, 0, 0)))),
                 ]
                 ]
             }
             }
         };
         };

+ 2 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/EraserToolExecutor.cs

@@ -47,12 +47,11 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
         color = GetHandler<IColorsHandler>().PrimaryColor;
         color = GetHandler<IColorsHandler>().PrimaryColor;
         toolSize = toolbar.ToolSize;
         toolSize = toolbar.ToolSize;
         antiAliasing = toolbar.AntiAliasing;
         antiAliasing = toolbar.AntiAliasing;
-        hardness = toolbar.Hardness;
         spacing = toolbar.Spacing;
         spacing = toolbar.Spacing;
 
 
         colorsHandler.AddSwatch(new PaletteColor(color.R, color.G, color.B));
         colorsHandler.AddSwatch(new PaletteColor(color.R, color.G, color.B));
         IAction? action = new LineBasedPen_Action(guidValue, Colors.White, controller!.LastPixelPosition, (float)eraserTool.ToolSize, true,
         IAction? action = new LineBasedPen_Action(guidValue, Colors.White, controller!.LastPixelPosition, (float)eraserTool.ToolSize, true,
-            antiAliasing, hardness, spacing, Guid.Empty, drawOnMask, document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData);
+            antiAliasing, spacing, Guid.Empty, drawOnMask, document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData);
         internals!.ActionAccumulator.AddActions(action);
         internals!.ActionAccumulator.AddActions(action);
 
 
         return ExecutionState.Success;
         return ExecutionState.Success;
@@ -60,7 +59,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
 
 
     public override void OnPixelPositionChange(VecI pos, MouseOnCanvasEventArgs args)
     public override void OnPixelPositionChange(VecI pos, MouseOnCanvasEventArgs args)
     {
     {
-        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, pos, (float)toolSize, true, antiAliasing, hardness, spacing, Guid.Empty, drawOnMask, document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData);
+        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, pos, (float)toolSize, true, antiAliasing, spacing, Guid.Empty, drawOnMask, document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData);
         internals!.ActionAccumulator.AddActions(action);
         internals!.ActionAccumulator.AddActions(action);
     }
     }
 
 

+ 3 - 5
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PenToolExecutor.cs

@@ -31,7 +31,6 @@ internal class PenToolExecutor : UpdateableChangeExecutor
     private bool drawOnMask;
     private bool drawOnMask;
     private bool pixelPerfect;
     private bool pixelPerfect;
     private bool antiAliasing;
     private bool antiAliasing;
-    private float hardness;
     private float spacing = 1;
     private float spacing = 1;
     private bool transparentErase;
     private bool transparentErase;
 
 
@@ -61,7 +60,6 @@ internal class PenToolExecutor : UpdateableChangeExecutor
         color = colorsHandler.PrimaryColor;
         color = colorsHandler.PrimaryColor;
         pixelPerfect = penTool.PixelPerfectEnabled;
         pixelPerfect = penTool.PixelPerfectEnabled;
         antiAliasing = toolbar.AntiAliasing;
         antiAliasing = toolbar.AntiAliasing;
-        hardness = toolbar.Hardness;
         spacing = toolbar.Spacing;
         spacing = toolbar.Spacing;
 
 
         if (color.A > 0)
         if (color.A > 0)
@@ -86,7 +84,7 @@ internal class PenToolExecutor : UpdateableChangeExecutor
             IAction? action = pixelPerfect switch
             IAction? action = pixelPerfect switch
             {
             {
                 false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, (float)ToolSize,
                 false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, (float)ToolSize,
-                    transparentErase, antiAliasing, hardness, spacing, brushOutputGuid, drawOnMask,
+                    transparentErase, antiAliasing, spacing, brushOutputGuid, drawOnMask,
                     document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData),
                     document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData),
                 true => new PixelPerfectPen_Action(guidValue, controller!.LastPixelPosition, color, drawOnMask,
                 true => new PixelPerfectPen_Action(guidValue, controller!.LastPixelPosition, color, drawOnMask,
                     document!.AnimationHandler.ActiveFrameBindable)
                     document!.AnimationHandler.ActiveFrameBindable)
@@ -105,7 +103,7 @@ internal class PenToolExecutor : UpdateableChangeExecutor
         IAction? action = pixelPerfect switch
         IAction? action = pixelPerfect switch
         {
         {
             false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, (float)ToolSize,
             false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, (float)ToolSize,
-                transparentErase, antiAliasing, hardness, spacing, brushOutputGuid, drawOnMask,
+                transparentErase, antiAliasing, spacing, brushOutputGuid, drawOnMask,
                 document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData),
                 document!.AnimationHandler.ActiveFrameBindable, controller.LastPointerInfo, controller.EditorData),
             true => new PixelPerfectPen_Action(guidValue, controller!.LastPixelPosition, color, drawOnMask,
             true => new PixelPerfectPen_Action(guidValue, controller!.LastPixelPosition, color, drawOnMask,
                 document!.AnimationHandler.ActiveFrameBindable)
                 document!.AnimationHandler.ActiveFrameBindable)
@@ -131,7 +129,7 @@ internal class PenToolExecutor : UpdateableChangeExecutor
             IAction? action = pixelPerfect switch
             IAction? action = pixelPerfect switch
             {
             {
                 false => new LineBasedPen_Action(guidValue, color, pos, (float)ToolSize, transparentErase, antiAliasing,
                 false => new LineBasedPen_Action(guidValue, color, pos, (float)ToolSize, transparentErase, antiAliasing,
-                    hardness, spacing, brushOutputGuid, drawOnMask, document!.AnimationHandler.ActiveFrameBindable,
+                    spacing, brushOutputGuid, drawOnMask, document!.AnimationHandler.ActiveFrameBindable,
                     controller.LastPointerInfo, controller.EditorData),
                     controller.LastPointerInfo, controller.EditorData),
                 true => new PixelPerfectPen_Action(guidValue, pos, color, drawOnMask,
                 true => new PixelPerfectPen_Action(guidValue, pos, color, drawOnMask,
                     document!.AnimationHandler.ActiveFrameBindable)
                     document!.AnimationHandler.ActiveFrameBindable)

+ 0 - 1
src/PixiEditor/Models/Handlers/Toolbars/IPenToolbar.cs

@@ -5,7 +5,6 @@ namespace PixiEditor.Models.Handlers.Toolbars;
 internal interface IPenToolbar : IToolbar, IToolSizeToolbar
 internal interface IPenToolbar : IToolbar, IToolSizeToolbar
 {
 {
     public bool AntiAliasing { get; set; }
     public bool AntiAliasing { get; set; }
-    public float Hardness { get; set; }
     public float Spacing { get; set; }
     public float Spacing { get; set; }
     public PaintBrushShape PaintShape { get; set; }
     public PaintBrushShape PaintShape { get; set; }
 }
 }

+ 10 - 0
src/PixiEditor/ViewModels/Document/Nodes/Brushes/StrokeInfoNodeViewModel.cs

@@ -0,0 +1,10 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Brushes;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Brushes;
+
+[NodeViewModel("STROKE_INFO_NODE", "BRUSHES", null)]
+internal class StrokeInfoNodeViewModel : NodeViewModel<StrokeInfoNode>
+{
+
+}

+ 0 - 7
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/PenToolbar.cs

@@ -12,12 +12,6 @@ internal class PenToolbar : Toolbar, IPenToolbar
         set => GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value = value;
         set => GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value = value;
     }
     }
 
 
-    public float Hardness
-    {
-        get => GetSetting<PercentSettingViewModel>(nameof(Hardness)).Value;
-        set => GetSetting<PercentSettingViewModel>(nameof(Hardness)).Value = value;
-    }
-
     public float Spacing
     public float Spacing
     {
     {
         get => GetSetting<PercentSettingViewModel>(nameof(Spacing)).Value;
         get => GetSetting<PercentSettingViewModel>(nameof(Spacing)).Value;
@@ -44,7 +38,6 @@ internal class PenToolbar : Toolbar, IPenToolbar
     public PenToolbar()
     public PenToolbar()
     {
     {
         AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_SETTING") { IsExposed = false });
         AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_SETTING") { IsExposed = false });
-        AddSetting(new PercentSettingViewModel(nameof(Hardness), 0.8f, "HARDNESS_SETTING") { IsExposed = false });
         AddSetting(new PercentSettingViewModel(nameof(Spacing), 0.15f, "SPACING_SETTING") { IsExposed = false });
         AddSetting(new PercentSettingViewModel(nameof(Spacing), 0.15f, "SPACING_SETTING") { IsExposed = false });
         var setting = new SizeSettingViewModel(nameof(ToolSize), "TOOL_SIZE_LABEL");
         var setting = new SizeSettingViewModel(nameof(ToolSize), "TOOL_SIZE_LABEL");
         setting.ValueChanged += (_, _) => OnPropertyChanged(nameof(ToolSize));
         setting.ValueChanged += (_, _) => OnPropertyChanged(nameof(ToolSize));