Jelajahi Sumber

Added spacing

flabbet 10 bulan lalu
induk
melakukan
4003c98008

+ 23 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs

@@ -16,15 +16,20 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     private readonly bool replacing;
     private readonly bool drawOnMask;
     private readonly bool antiAliasing;
+    private float hardness;
+    private float spacing = 1;
     private readonly Paint srcPaint = new Paint() { BlendMode = BlendMode.Src };
 
     private CommittedChunkStorage? storedChunks;
     private readonly List<VecI> points = new();
     private int frame;
+    private VecF lastPos;
 
     [GenerateUpdateableChangeActions]
     public LineBasedPen_UpdateableChange(Guid memberGuid, Color color, VecI pos, int strokeWidth, bool replacing,
         bool antiAliasing,
+        float hardness,
+        float spacing,
         bool drawOnMask, int frame)
     {
         this.memberGuid = memberGuid;
@@ -33,6 +38,8 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         this.replacing = replacing;
         this.antiAliasing = antiAliasing;
         this.drawOnMask = drawOnMask;
+        this.hardness = hardness;
+        this.spacing = spacing;
         points.Add(pos);
         this.frame = frame;
         if (this.antiAliasing)
@@ -71,8 +78,15 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         int opCount = image.QueueLength;
 
         var bresenham = BresenhamLineHelper.GetBresenhamLine(from, to);
+        
+        float spacingPixels = strokeWidth * spacing;
+
         foreach (var point in bresenham)
         {
+            if (points.Count > 1 && VecF.Distance(lastPos, point) < spacingPixels)
+                continue;
+
+            lastPos = point;
             var rect = new RectI(point - new VecI(strokeWidth / 2), new VecI(strokeWidth));
             if (antiAliasing)
             {
@@ -96,8 +110,16 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             return;
         }
 
+        VecF lastPos = points[0];
+        
+        float spacingInPixels = strokeWidth * this.spacing;
+
         for (int i = 0; i < points.Count; i++)
         {
+            if (i > 0 && VecF.Distance(lastPos, points[i]) < spacingInPixels)
+                continue;
+
+            lastPos = points[i];
             var rect = new RectI(points[i] - new VecI(strokeWidth / 2), new VecI(strokeWidth));
             if (antiAliasing)
             {
@@ -115,7 +137,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         radius = MathF.Max(1, radius);
         srcPaint.Shader = Shader.CreateRadialGradient(
             pos, radius, new Color[] { color, color.WithAlpha(0) },
-            new float[] { 0.5f, 1 }, ShaderTileMode.Clamp);
+            new float[] { hardness, 1 }, ShaderTileMode.Clamp);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,

+ 4 - 2
src/PixiEditor/Data/Configs/ToolSetsConfig.json

@@ -8,7 +8,8 @@
       {
         "ToolName": "Pen",
         "Settings": {
-          "ExposePixelPerfectEnabled": true
+          "ExposePixelPerfectEnabled": true,
+          "Spacing": 0
         }
       },
       "Select",
@@ -34,7 +35,8 @@
         "ToolName": "Pen",
         "Settings": {
           "AntiAliasing": true,
-          "ExposeHardness": true
+          "ExposeHardness": true,
+          "ExposeSpacing": true
         }
       },
       "Select",

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

@@ -756,5 +756,7 @@
   "NEW_ELLIPSE_LAYER_NAME": "Ellipse",
   "NEW_RECTANGLE_LAYER_NAME": "Rectangle",
   "NEW_LINE_LAYER_NAME": "Line",
-  "RENDER_OUTPUT": "Render Output"
+  "RENDER_OUTPUT": "Render Output",
+  "HARDNESS_SETTING": "Hardness",
+  "SPACING_SETTING": "Spacing"
 }

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

@@ -41,7 +41,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
 
         colorsHandler.AddSwatch(new PaletteColor(color.R, color.G, color.B));
         IAction? action = new LineBasedPen_Action(guidValue, Colors.Transparent, controller!.LastPixelPosition, toolSize, true,
-            false, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+            false, 1, 0, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
         internals!.ActionAccumulator.AddActions(action);
 
         return ExecutionState.Success;
@@ -49,7 +49,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
 
     public override void OnPixelPositionChange(VecI pos)
     {
-        IAction? action = new LineBasedPen_Action(guidValue, Colors.Transparent, pos, toolSize, true, false, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+        IAction? action = new LineBasedPen_Action(guidValue, Colors.Transparent, pos, toolSize, true, false, 1, 0, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
         internals!.ActionAccumulator.AddActions(action);
     }
 

+ 6 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PenToolExecutor.cs

@@ -1,14 +1,12 @@
 using PixiEditor.ChangeableDocument.Actions;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using Drawie.Backend.Core.ColorsImpl;
-using Drawie.Backend.Core.Numerics;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Toolbars;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
-using PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
@@ -20,6 +18,8 @@ internal class PenToolExecutor : UpdateableChangeExecutor
     private bool drawOnMask;
     private bool pixelPerfect;
     private bool antiAliasing;
+    private float hardness;
+    private float spacing = 1;
 
     private IBasicToolbar basicToolbar;
 
@@ -42,11 +42,13 @@ internal class PenToolExecutor : UpdateableChangeExecutor
         color = colorsHandler.PrimaryColor;
         pixelPerfect = penTool.PixelPerfectEnabled;
         antiAliasing = penTool.AntiAliasing;
+        hardness = penTool.Hardness;
+        spacing = penTool.Spacing;
 
         colorsHandler.AddSwatch(new PaletteColor(color.R, color.G, color.B));
         IAction? action = pixelPerfect switch
         {
-            false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, ToolSize, false, antiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
+            false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, ToolSize, false, antiAliasing, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
             true => new PixelPerfectPen_Action(guidValue, controller!.LastPixelPosition, color, drawOnMask, document!.AnimationHandler.ActiveFrameBindable)
         };
         internals!.ActionAccumulator.AddActions(action);
@@ -58,7 +60,7 @@ internal class PenToolExecutor : UpdateableChangeExecutor
     {
         IAction? action = pixelPerfect switch
         {
-            false => new LineBasedPen_Action(guidValue, color, pos, ToolSize, false, antiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
+            false => new LineBasedPen_Action(guidValue, color, pos, ToolSize, false, antiAliasing, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
             true => new PixelPerfectPen_Action(guidValue, pos, color, drawOnMask, document!.AnimationHandler.ActiveFrameBindable)
         };
         internals!.ActionAccumulator.AddActions(action);

+ 2 - 0
src/PixiEditor/Models/Handlers/Tools/IPenToolHandler.cs

@@ -4,4 +4,6 @@ internal interface IPenToolHandler : IToolHandler
 {
     public bool PixelPerfectEnabled { get; }
     public bool AntiAliasing { get; }
+    public float Hardness { get; }
+    public float Spacing { get; }
 }

+ 22 - 4
src/PixiEditor/ViewModels/Tools/ToolViewModel.cs

@@ -162,6 +162,13 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
             return;
         }
 
+        foreach (var valueSetting in settings)
+        {
+            if (valueSetting.Value is long)
+            {
+                settings[valueSetting.Key] = Convert.ToSingle(valueSetting.Value);
+            }
+        }
         ToolSetSettings[toolset] = settings;
     }
 
@@ -179,7 +186,6 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
 
         foreach (var setting in settings)
         {
-
             if (IsExposeSetting(setting, out bool expose))
             {
                 string settingName = setting.Key.Replace("Expose", string.Empty);
@@ -188,7 +194,7 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
                 {
                     continue;
                 }
-                
+
                 foundSetting.SetOverwriteExposed(expose);
             }
             else
@@ -200,7 +206,7 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
                     {
                         continue;
                     }
-                    
+
                     foundSetting.SetOverwriteValue(setting.Value);
                 }
                 catch (InvalidCastException)
@@ -237,7 +243,19 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
             return (T)property!.GetValue(setting);
         }
 
-        return (T)setting.Value;
+        try
+        {
+            return (T)setting.Value;
+        }
+        catch (InvalidCastException)
+        {
+            if(typeof(T) == typeof(float) || typeof(T) == typeof(double) || typeof(T) == typeof(int))
+            {
+                return (T)(object)Convert.ToSingle(setting.Value);
+            }
+            
+            throw;
+        }
     }
 
     private bool IsExposeSetting(KeyValuePair<string, object> settingConfig, out bool expose)

+ 7 - 0
src/PixiEditor/ViewModels/Tools/Tools/PenToolViewModel.cs

@@ -43,6 +43,13 @@ namespace PixiEditor.ViewModels.Tools.Tools
         [Settings.Bool("__antiAliasing", false, ExposedByDefault = false)]
         public bool AntiAliasing => GetValue<bool>();
         
+        // TODO: Percent
+        [Settings.Float("HARDNESS_SETTING", 0.8f, ExposedByDefault = false, Min = 0, Max = 1)]
+        public float Hardness => GetValue<float>();
+
+        [Settings.Float("SPACING_SETTING", 0.15f, ExposedByDefault = false, Min = 0, Max = 1)]
+        public float Spacing => GetValue<float>();
+
         public override string Icon => PixiPerfectIcons.Pen;
 
         public override Type LayerTypeToCreateOnEmptyUse { get; } = typeof(ImageLayerNode);