Browse Source

Allow fractional values for strokes and added serializer info

flabbet 8 months ago
parent
commit
6e9c620a5f
73 changed files with 309 additions and 323 deletions
  1. 3 3
      src/ChunkyImageLib/ChunkyImage.cs
  2. 2 2
      src/ChunkyImageLib/DataHolders/ShapeData.cs
  3. 7 7
      src/ChunkyImageLib/Operations/DrawingSurfaceLineOperation.cs
  4. 1 1
      src/ChunkyImageLib/Operations/EllipseHelper.cs
  5. 10 10
      src/ChunkyImageLib/Operations/EllipseOperation.cs
  6. 1 1
      src/Drawie
  7. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyShapeVectorData.cs
  8. 10 19
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs
  9. 7 16
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs
  10. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/ShapeVectorData.cs
  11. 4 4
      src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRasterEllipse_UpdateableChange.cs
  12. 7 7
      src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRasterLine_UpdateableChange.cs
  13. 9 9
      src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs
  14. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Selection/SelectEllipse_UpdateableChange.cs
  15. 9 0
      src/PixiEditor/Helpers/DocumentViewModelBuilder.cs
  16. 1 0
      src/PixiEditor/Helpers/Extensions/PixiParserDocumentEx.cs
  17. 8 7
      src/PixiEditor/Helpers/SerializationUtil.cs
  18. 2 2
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/BrightnessToolExecutor.cs
  19. 4 4
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs
  20. 3 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/EraserToolExecutor.cs
  21. 3 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs
  22. 4 4
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PenToolExecutor.cs
  23. 3 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterEllipseToolExecutor.cs
  24. 3 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterLineToolExecutor.cs
  25. 2 2
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs
  26. 3 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorEllipseToolExecutor.cs
  27. 2 2
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorLineToolExecutor.cs
  28. 4 4
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorPathToolExecutor.cs
  29. 2 2
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorRectangleToolExecutor.cs
  30. 0 6
      src/PixiEditor/Models/Handlers/Toolbars/IBasicToolbar.cs
  31. 9 0
      src/PixiEditor/Models/Handlers/Toolbars/IFillableShapeToolbar.cs
  32. 0 10
      src/PixiEditor/Models/Handlers/Toolbars/ILineToolbar.cs
  33. 1 1
      src/PixiEditor/Models/Handlers/Toolbars/IPenToolbar.cs
  34. 1 3
      src/PixiEditor/Models/Handlers/Toolbars/IShapeToolbar.cs
  35. 6 0
      src/PixiEditor/Models/Handlers/Toolbars/IToolSizeToolbar.cs
  36. 1 1
      src/PixiEditor/Models/Handlers/Tools/ILineToolHandler.cs
  37. 1 2
      src/PixiEditor/Models/Handlers/Tools/IVectorLineToolHandler.cs
  38. 5 0
      src/PixiEditor/Models/Serialization/Factories/ByteBuilder.cs
  39. 9 0
      src/PixiEditor/Models/Serialization/Factories/ByteExtractor.cs
  40. 3 2
      src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs
  41. 2 1
      src/PixiEditor/Models/Serialization/Factories/ColorMatrixSerializationFactory.cs
  42. 2 1
      src/PixiEditor/Models/Serialization/Factories/ColorSerializationFactory.cs
  43. 1 1
      src/PixiEditor/Models/Serialization/Factories/EllipseSerializationFactory.cs
  44. 2 1
      src/PixiEditor/Models/Serialization/Factories/KernelSerializationFactory.cs
  45. 1 1
      src/PixiEditor/Models/Serialization/Factories/LineSerializationFactory.cs
  46. 1 1
      src/PixiEditor/Models/Serialization/Factories/PointsDataSerializationFactory.cs
  47. 1 1
      src/PixiEditor/Models/Serialization/Factories/RectangleSerializationFactory.cs
  48. 5 4
      src/PixiEditor/Models/Serialization/Factories/SerializationFactory.cs
  49. 2 1
      src/PixiEditor/Models/Serialization/Factories/SurfaceSerializationFactory.cs
  50. 3 2
      src/PixiEditor/Models/Serialization/Factories/TextureSerializationFactory.cs
  51. 2 1
      src/PixiEditor/Models/Serialization/Factories/VecD3SerializationFactory.cs
  52. 2 1
      src/PixiEditor/Models/Serialization/Factories/VecDSerializationFactory.cs
  53. 2 1
      src/PixiEditor/Models/Serialization/Factories/VecISerializationFactory.cs
  54. 1 1
      src/PixiEditor/Models/Serialization/Factories/VectorPathSerializationFactory.cs
  55. 15 5
      src/PixiEditor/Models/Serialization/Factories/VectorShapeSerializationFactory.cs
  56. 6 4
      src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs
  57. 5 3
      src/PixiEditor/ViewModels/Document/DocumentViewModel.cs
  58. 6 5
      src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs
  59. 1 1
      src/PixiEditor/ViewModels/Tools/ShapeTool.cs
  60. 1 7
      src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/ColorSettingViewModel.cs
  61. 1 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/SizeSettingViewModel.cs
  62. 0 77
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/BasicShapeToolbar.cs
  63. 0 27
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/BasicToolbar.cs
  64. 38 0
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/FillableShapeToolbar.cs
  65. 15 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/PenToolbar.cs
  66. 21 5
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/ShapeToolbar.cs
  67. 2 2
      src/PixiEditor/ViewModels/Tools/Tools/BrightnessToolViewModel.cs
  68. 1 1
      src/PixiEditor/ViewModels/Tools/Tools/EraserToolViewModel.cs
  69. 8 8
      src/PixiEditor/ViewModels/Tools/Tools/PenToolViewModel.cs
  70. 3 2
      src/PixiEditor/ViewModels/Tools/Tools/RasterLineToolViewModel.cs
  71. 4 5
      src/PixiEditor/ViewModels/Tools/Tools/VectorLineToolViewModel.cs
  72. 1 1
      src/PixiEditor/ViewModels/Tools/Tools/VectorPathToolViewModel.cs
  73. 1 1
      src/PixiParser

+ 3 - 3
src/ChunkyImageLib/ChunkyImage.cs

@@ -590,7 +590,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     }
 
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
-    public void EnqueueDrawEllipse(RectI location, Color strokeColor, Color fillColor, int strokeWidth,
+    public void EnqueueDrawEllipse(RectD location, Color strokeColor, Color fillColor, float strokeWidth,
         double rotationRad = 0, bool antiAliased = false,
         Paint? paint = null)
     {
@@ -727,7 +727,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     }
 
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
-    public void EnqueueDrawSkiaLine(VecI from, VecI to, StrokeCap strokeCap, float strokeWidth, Color color,
+    public void EnqueueDrawSkiaLine(VecD from, VecD to, StrokeCap strokeCap, float strokeWidth, Color color,
         BlendMode blendMode)
     {
         lock (lockObject)
@@ -738,7 +738,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         }
     }
 
-    public void EnqueueDrawSkiaLine(VecI from, VecI to, Paint paint)
+    public void EnqueueDrawSkiaLine(VecD from, VecD to, Paint paint)
     {
         lock (lockObject)
         {

+ 2 - 2
src/ChunkyImageLib/DataHolders/ShapeData.cs

@@ -7,7 +7,7 @@ namespace ChunkyImageLib.DataHolders;
 
 public record struct ShapeData
 {
-    public ShapeData(VecD center, VecD size, double rotation, int strokeWidth, Color strokeColor, Color fillColor, BlendMode blendMode = BlendMode.SrcOver)
+    public ShapeData(VecD center, VecD size, double rotation, float strokeWidth, Color strokeColor, Color fillColor, BlendMode blendMode = BlendMode.SrcOver)
     {
         StrokeColor = strokeColor;
         FillColor = fillColor;
@@ -25,7 +25,7 @@ public record struct ShapeData
     /// <summary>Can be negative to show flipping </summary>
     public VecD Size { get; }
     public double Angle { get; }
-    public int StrokeWidth { get; }
+    public float StrokeWidth { get; }
 
     public bool AntiAliasing { get; set; } = false;
     

+ 7 - 7
src/ChunkyImageLib/Operations/DrawingSurfaceLineOperation.cs

@@ -11,11 +11,11 @@ internal class DrawingSurfaceLineOperation : IMirroredDrawOperation
     public bool IgnoreEmptyChunks => false;
 
     private Paint paint;
-    private readonly VecI from;
-    private readonly VecI to;
+    private readonly VecD from;
+    private readonly VecD to;
     private bool isAntiAliased;
 
-    public DrawingSurfaceLineOperation(VecI from, VecI to, StrokeCap strokeCap, float strokeWidth, Color color, BlendMode blendMode)
+    public DrawingSurfaceLineOperation(VecD from, VecD to, StrokeCap strokeCap, float strokeWidth, Color color, BlendMode blendMode)
     {
         paint = new()
         {
@@ -29,7 +29,7 @@ internal class DrawingSurfaceLineOperation : IMirroredDrawOperation
         this.to = to;
     }
     
-    public DrawingSurfaceLineOperation(VecI from, VecI to, Paint paint)
+    public DrawingSurfaceLineOperation(VecD from, VecD to, Paint paint)
     {
         this.paint = paint.Clone();
         this.from = from;
@@ -50,14 +50,14 @@ internal class DrawingSurfaceLineOperation : IMirroredDrawOperation
 
     public AffectedArea FindAffectedArea(VecI imageSize)
     {
-        RectI bounds = RectI.FromTwoPoints(from, to).Inflate((int)Math.Ceiling(paint.StrokeWidth));
+        RectI bounds = (RectI)RectD.FromTwoPoints(from, to).Inflate((int)Math.Ceiling(paint.StrokeWidth));
         return new AffectedArea(OperationHelper.FindChunksTouchingRectangle(bounds, ChunkyImage.FullChunkSize), bounds);
     }
 
     public IDrawOperation AsMirrored(double? verAxisX, double? horAxisY)
     {
-        VecI newFrom = from;
-        VecI newTo = to;
+        VecD newFrom = from;
+        VecD newTo = to;
         if (verAxisX is not null)
         {
             newFrom = (VecI)newFrom.ReflectX((double)verAxisX).Round();

+ 1 - 1
src/ChunkyImageLib/Operations/EllipseHelper.cs

@@ -345,7 +345,7 @@ public class EllipseHelper
         }
     }
 
-    public static VectorPath GenerateEllipseVectorFromRect(RectI location)
+    public static VectorPath GenerateEllipseVectorFromRect(RectD location)
     {
         VectorPath path = new();
         path.AddOval(location);

+ 10 - 10
src/ChunkyImageLib/Operations/EllipseOperation.cs

@@ -11,10 +11,10 @@ internal class EllipseOperation : IMirroredDrawOperation
 {
     public bool IgnoreEmptyChunks => false;
 
-    private readonly RectI location;
+    private readonly RectD location;
     private readonly Color strokeColor;
     private readonly Color fillColor;
-    private readonly int strokeWidth;
+    private readonly float strokeWidth;
     private readonly double rotation;
     private readonly Paint paint;
     private bool init = false;
@@ -27,7 +27,7 @@ internal class EllipseOperation : IMirroredDrawOperation
     private RectI? ellipseFillRect;
     private bool antialiased;
 
-    public EllipseOperation(RectI location, Color strokeColor, Color fillColor, int strokeWidth, double rotationRad,
+    public EllipseOperation(RectD location, Color strokeColor, Color fillColor, float strokeWidth, double rotationRad,
         bool antiAliased, Paint? paint = null)
     {
         this.location = location;
@@ -42,17 +42,17 @@ internal class EllipseOperation : IMirroredDrawOperation
     private void Init()
     {
         init = true;
-        if (strokeWidth == 1)
+        if (strokeWidth - 1 < 0.01)
         {
             if (Math.Abs(rotation) < 0.001)
             {
-                var ellipseList = EllipseHelper.GenerateEllipseFromRect(location);
+                var ellipseList = EllipseHelper.GenerateEllipseFromRect((RectI)location);
 
                 ellipse = ellipseList.Select(a => new VecF(a)).ToArray();
 
                 if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
                 {
-                    (var fill, ellipseFillRect) = EllipseHelper.SplitEllipseFillIntoRegions(ellipseList.ToList(), location);
+                    (var fill, ellipseFillRect) = EllipseHelper.SplitEllipseFillIntoRegions(ellipseList.ToList(), (RectI)location);
                     ellipseFill = fill.Select(a => new VecF(a)).ToArray();
                 }
             }
@@ -96,7 +96,7 @@ internal class EllipseOperation : IMirroredDrawOperation
     private void DrawAliased(DrawingSurface surf)
     {
         paint.IsAntiAliased = false;
-        if (strokeWidth == 1)
+        if (strokeWidth - 1 < 0.01)
         {
             if (Math.Abs(rotation) < 0.001)
             {
@@ -194,11 +194,11 @@ internal class EllipseOperation : IMirroredDrawOperation
 
     public IDrawOperation AsMirrored(double? verAxisX, double? horAxisY)
     {
-        RectI newLocation = location;
+        RectD newLocation = location;
         if (verAxisX is not null)
-            newLocation = (RectI)newLocation.ReflectX((double)verAxisX).Round();
+            newLocation = newLocation.ReflectX((double)verAxisX).Round();
         if (horAxisY is not null)
-            newLocation = (RectI)newLocation.ReflectY((double)horAxisY).Round();
+            newLocation = newLocation.ReflectY((double)horAxisY).Round();
         return new EllipseOperation(newLocation, strokeColor, fillColor, strokeWidth, rotation, antialiased, paint);
     }
 

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 73622d14f4a7e89f036ecec5bfe74cae15708348
+Subproject commit f07c85f3a8340babe79c08c585958c93b5d7a321

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

@@ -9,7 +9,7 @@ public interface IReadOnlyShapeVectorData
     public Matrix3X3 TransformationMatrix { get; }
     public Color StrokeColor { get; }
     public Color FillColor { get; }
-    public int StrokeWidth { get; }
+    public float StrokeWidth { get; }
     public RectD GeometryAABB { get; }
     public RectD TransformedAABB { get; }
     public ShapeCorners TransformationCorners { get; }

+ 10 - 19
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs

@@ -10,7 +10,7 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 public class EllipseVectorData : ShapeVectorData, IReadOnlyEllipseData
 {
     public VecD Radius { get; set; }
-    
+
     public VecD Center { get; set; }
 
     public override RectD GeometryAABB =>
@@ -47,30 +47,21 @@ public class EllipseVectorData : ShapeVectorData, IReadOnlyEllipseData
 
         using Paint shapePaint = new Paint() { IsAntiAliased = true };
 
-        if (Radius.ShortestAxis < StrokeWidth)
-        {
-            shapePaint.Color = StrokeColor;
-            shapePaint.Style = PaintStyle.Fill;
-            drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
-        }
-        else
-        {
-            shapePaint.Color = FillColor;
-            shapePaint.Style = PaintStyle.Fill;
-            drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
-
-            shapePaint.Color = StrokeColor;
-            shapePaint.Style = PaintStyle.Stroke;
-            shapePaint.StrokeWidth = StrokeWidth;
-            drawingSurface.Canvas.DrawOval(Center, Radius - new VecD(StrokeWidth / 2f), shapePaint);
-        }
+        shapePaint.Color = FillColor;
+        shapePaint.Style = PaintStyle.Fill;
+        drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
+
+        shapePaint.Color = StrokeColor;
+        shapePaint.Style = PaintStyle.Stroke;
+        shapePaint.StrokeWidth = StrokeWidth;
+        drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
 
         if (applyTransform)
         {
             drawingSurface.Canvas.RestoreToCount(saved);
         }
     }
-    
+
     public override bool IsValid()
     {
         return Radius is { X: > 0, Y: > 0 };

+ 7 - 16
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs

@@ -45,24 +45,15 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
 
         using Paint paint = new Paint() { IsAntiAliased = true };
 
-        if (Size.ShortestAxis < StrokeWidth)
-        {
-            paint.Color = StrokeColor;
-            paint.Style = PaintStyle.Fill;
-            drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
-        }
-        else
-        {
-            paint.Color = FillColor;
-            paint.Style = PaintStyle.Fill;
-            drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
+        paint.Color = FillColor;
+        paint.Style = PaintStyle.Fill;
+        drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
 
-            paint.Color = StrokeColor;
-            paint.Style = PaintStyle.Stroke;
+        paint.Color = StrokeColor;
+        paint.Style = PaintStyle.Stroke;
 
-            paint.StrokeWidth = StrokeWidth;
-            drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size - new VecD(StrokeWidth)), paint);
-        }
+        paint.StrokeWidth = StrokeWidth;
+        drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
 
         if (applyTransform)
         {

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

@@ -14,7 +14,7 @@ public abstract class ShapeVectorData : ICacheable, ICloneable, IReadOnlyShapeVe
     
     public Color StrokeColor { get; set; } = Colors.White;
     public Color FillColor { get; set; } = Colors.White;
-    public int StrokeWidth { get; set; } = 1;
+    public float StrokeWidth { get; set; } = 1;
     public abstract RectD GeometryAABB { get; }
     public RectD TransformedAABB => new ShapeCorners(GeometryAABB).WithMatrix(TransformationMatrix).AABBBounds;
     public abstract ShapeCorners TransformationCorners { get; } 

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRasterEllipse_UpdateableChange.cs

@@ -10,7 +10,7 @@ internal class DrawRasterEllipse_UpdateableChange : UpdateableChange
     private double rotation;
     private Color strokeColor;
     private Color fillColor;
-    private int strokeWidth;
+    private float strokeWidth;
     private readonly bool drawOnMask;
     private int frame;
     private bool antialiased;
@@ -18,7 +18,7 @@ internal class DrawRasterEllipse_UpdateableChange : UpdateableChange
     private CommittedChunkStorage? storedChunks;
 
     [GenerateUpdateableChangeActions]
-    public DrawRasterEllipse_UpdateableChange(Guid memberGuid, RectI location, double rotationRad, Color strokeColor, Color fillColor, int strokeWidth, bool antialiased, bool drawOnMask, int frame)
+    public DrawRasterEllipse_UpdateableChange(Guid memberGuid, RectI location, double rotationRad, Color strokeColor, Color fillColor, float strokeWidth, bool antialiased, bool drawOnMask, int frame)
     {
         this.memberGuid = memberGuid;
         this.location = location;
@@ -32,7 +32,7 @@ internal class DrawRasterEllipse_UpdateableChange : UpdateableChange
     }
 
     [UpdateChangeMethod]
-    public void Update(RectI location, double rotationRad, Color strokeColor, Color fillColor, int strokeWidth)
+    public void Update(RectI location, double rotationRad, Color strokeColor, Color fillColor, float strokeWidth)
     {
         this.location = location;
         rotation = rotationRad;
@@ -55,7 +55,7 @@ internal class DrawRasterEllipse_UpdateableChange : UpdateableChange
         if (!location.IsZeroOrNegativeArea)
         {
             DrawingChangeHelper.ApplyClipsSymmetriesEtc(target, targetImage, memberGuid, drawOnMask);
-            targetImage.EnqueueDrawEllipse(location, strokeColor, fillColor, strokeWidth, rotation, antialiased);
+            targetImage.EnqueueDrawEllipse((RectD)location, strokeColor, fillColor, strokeWidth, rotation, antialiased);
         }
 
         var affectedArea = targetImage.FindAffectedArea();

+ 7 - 7
src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRasterLine_UpdateableChange.cs

@@ -8,9 +8,9 @@ namespace PixiEditor.ChangeableDocument.Changes.Drawing;
 internal class DrawRasterLine_UpdateableChange : UpdateableChange
 {
     private readonly Guid memberGuid;
-    private VecI from;
-    private VecI to;
-    private int strokeWidth;
+    private VecD from;
+    private VecD to;
+    private float strokeWidth;
     private Color color;
     private StrokeCap caps;
     private readonly bool drawOnMask;
@@ -21,7 +21,7 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
 
     [GenerateUpdateableChangeActions]
     public DrawRasterLine_UpdateableChange
-        (Guid memberGuid, VecI from, VecI to, int strokeWidth, Color color, StrokeCap caps, bool antiAliasing, bool drawOnMask, int frame)
+        (Guid memberGuid, VecD from, VecD to, float strokeWidth, Color color, StrokeCap caps, bool antiAliasing, bool drawOnMask, int frame)
     {
         this.memberGuid = memberGuid;
         this.from = from;
@@ -38,7 +38,7 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
     }
 
     [UpdateChangeMethod]
-    public void Update(VecI from, VecI to, int strokeWidth, Color color, StrokeCap caps)
+    public void Update(VecD from, VecD to, float strokeWidth, Color color, StrokeCap caps)
     {
         this.from = from;
         this.to = to;
@@ -64,9 +64,9 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
         if (from != to)
         {
             DrawingChangeHelper.ApplyClipsSymmetriesEtc(target, image, memberGuid, drawOnMask);
-            if (strokeWidth == 1 && !antiAliasing)
+            if (Math.Abs(strokeWidth - 1) < 0.01f && !antiAliasing)
             {
-                image.EnqueueDrawBresenhamLine(from, to, color, BlendMode.SrcOver);
+                image.EnqueueDrawBresenhamLine((VecI)from, (VecI)to, color, BlendMode.SrcOver);
             }
             else
             {

+ 9 - 9
src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs

@@ -12,7 +12,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
 {
     private readonly Guid memberGuid;
     private readonly Color color;
-    private int strokeWidth;
+    private float strokeWidth;
     private readonly bool erasing;
     private readonly bool drawOnMask;
     private readonly bool antiAliasing;
@@ -26,7 +26,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     private VecF lastPos;
 
     [GenerateUpdateableChangeActions]
-    public LineBasedPen_UpdateableChange(Guid memberGuid, Color color, VecI pos, int strokeWidth, bool erasing,
+    public LineBasedPen_UpdateableChange(Guid memberGuid, Color color, VecI pos, float strokeWidth, bool erasing,
         bool antiAliasing,
         float hardness,
         float spacing,
@@ -53,7 +53,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     }
 
     [UpdateChangeMethod]
-    public void Update(VecI pos, int strokeWidth)
+    public void Update(VecI pos, float strokeWidth)
     {
         points.Add(pos);
         this.strokeWidth = strokeWidth;
@@ -91,13 +91,13 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
                 continue;
 
             lastPos = point;
-            var rect = new RectI(point - new VecI(strokeWidth / 2), new VecI(strokeWidth));
+            var rect = new RectI(point - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
             if (antiAliasing)
             {
                 ApplySoftnessGradient((VecD)point);
             }
             
-            image.EnqueueDrawEllipse(rect, color, color, 0, 0, antiAliasing, srcPaint);
+            image.EnqueueDrawEllipse((RectD)rect, color, color, 0, 0, antiAliasing, srcPaint);
         }
 
         var affChunks = image.FindAffectedArea(opCount);
@@ -109,8 +109,8 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     {
         if (points.Count == 1)
         {
-            var rect = new RectI(points[0] - new VecI(strokeWidth / 2), new VecI(strokeWidth));
-            targetImage.EnqueueDrawEllipse(rect, color, color, 1, 0, antiAliasing, srcPaint);
+            var rect = new RectI(points[0] - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
+            targetImage.EnqueueDrawEllipse((RectD)rect, color, color, 1, 0, antiAliasing, srcPaint);
             return;
         }
 
@@ -124,13 +124,13 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
                 continue;
 
             lastPos = points[i];
-            var rect = new RectI(points[i] - new VecI(strokeWidth / 2), new VecI(strokeWidth));
+            var rect = new RectI(points[i] - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
             if (antiAliasing)
             {
                 ApplySoftnessGradient(points[i]);
             }
 
-            targetImage.EnqueueDrawEllipse(rect, color, color, 0, 0, antiAliasing, srcPaint);
+            targetImage.EnqueueDrawEllipse((RectD)rect, color, color, 0, 0, antiAliasing, srcPaint);
         }
     }
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Selection/SelectEllipse_UpdateableChange.cs

@@ -37,7 +37,7 @@ internal class SelectEllipse_UpdateableChange : UpdateableChange
     {
         using var ellipsePath = new VectorPath() { FillType = PathFillType.EvenOdd };
         if (!borders.IsZeroArea)
-            ellipsePath.AddOval(borders);
+            ellipsePath.AddOval((RectD)borders);
 
         using var inConstraint = ellipsePath.Op(documentConstraint!, VectorPathOp.Intersect);
 

+ 9 - 0
src/PixiEditor/Helpers/DocumentViewModelBuilder.cs

@@ -18,6 +18,8 @@ namespace PixiEditor.Helpers;
 
 internal class DocumentViewModelBuilder
 {
+    public string SerializerName { get; set; }
+    public string SerializerVersion { get; set; }
     public int Width { get; set; }
     public int Height { get; set; }
 
@@ -190,6 +192,13 @@ internal class DocumentViewModelBuilder
             return this;
         }
     }
+
+    public DocumentViewModelBuilder WithSerializerData(string documentSerializerName, string documentSerializerVersion)
+    {
+        SerializerName = documentSerializerName;
+        SerializerVersion = documentSerializerVersion;
+        return this;
+    }
 }
 
 internal class AnimationDataBuilder

+ 1 - 0
src/PixiEditor/Helpers/Extensions/PixiParserDocumentEx.cs

@@ -29,6 +29,7 @@ internal static class PixiParserDocumentEx
         }
 
         return DocumentViewModel.Build(b => b
+            .WithSerializerData(document.SerializerName, document.SerializerVersion)
             .WithSize(document.Width, document.Height)
             .WithImageEncoder(document.ImageEncoderUsed)
             .WithPalette(document.Palette, color => new PaletteColor(color.R, color.G, color.B))

+ 8 - 7
src/PixiEditor/Helpers/SerializationUtil.cs

@@ -47,7 +47,8 @@ public static class SerializationUtil
     }
 
     public static object Deserialize(object value, SerializationConfig config,
-        IReadOnlyList<SerializationFactory> allFactories)
+        IReadOnlyList<SerializationFactory> allFactories,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (IsComplexObject(value))
         {
@@ -63,7 +64,7 @@ public static class SerializationUtil
                 {
                     return factory.Deserialize(data is Dictionary<object, object> processableDict
                     ? ToDictionary(processableDict)
-                    : data);
+                    : data, serializerData);
                 }
                 catch (Exception e)
                 {
@@ -78,7 +79,7 @@ public static class SerializationUtil
 
 
     public static Dictionary<string, object> DeserializeDict(Dictionary<string, object> data,
-        SerializationConfig config, List<SerializationFactory> allFactories)
+        SerializationConfig config, List<SerializationFactory> allFactories, (string serializerName, string serializerVersion) serializerData)
     {
         var dict = new Dictionary<string, object>();
 
@@ -88,17 +89,17 @@ public static class SerializationUtil
             {
                 if (IsComplexObject(value))
                 {
-                    dict[key] = Deserialize(value, config, allFactories);
+                    dict[key] = Deserialize(value, config, allFactories, serializerData);
                 }
                 else
                 {
-                    var deserialized = Deserialize(objArr[0], config, allFactories);
+                    var deserialized = Deserialize(objArr[0], config, allFactories, serializerData);
                     var targetArr = Array.CreateInstance(deserialized.GetType(), objArr.Length);
                     targetArr.SetValue(deserialized, 0);
 
                     for (int i = 1; i < objArr.Length; i++)
                     {
-                        targetArr.SetValue(Deserialize(objArr[i], config, allFactories), i);
+                        targetArr.SetValue(Deserialize(objArr[i], config, allFactories, serializerData), i);
                     }
 
                     dict[key] = targetArr;
@@ -106,7 +107,7 @@ public static class SerializationUtil
             }
             else
             {
-                dict[key] = Deserialize(value, config, allFactories);
+                dict[key] = Deserialize(value, config, allFactories, serializerData);
             }
         }
 

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

@@ -20,14 +20,14 @@ internal class BrightnessToolExecutor : UpdateableChangeExecutor
     {
         IStructureMemberHandler? member = document!.SelectedStructureMember;
         IBrightnessToolHandler? tool = GetHandler<IBrightnessToolHandler>();
-        if (tool is null || member is null || tool.Toolbar is not IBasicToolbar toolbar)
+        if (tool is null || member is null || tool.Toolbar is not IToolSizeToolbar toolbar)
             return ExecutionState.Error;
         if (member is not ILayerHandler layer || layer.ShouldDrawOnMask)
             return ExecutionState.Error;
 
         guidValue = member.Id;
         repeat = tool.BrightnessMode == BrightnessMode.Repeat;
-        toolSize = toolbar.ToolSize;
+        toolSize = (int)toolbar.ToolSize;
         correctionFactor = tool.Darken || tool.UsedWith == MouseButton.Right ? -tool.CorrectionFactor : tool.CorrectionFactor;
 
         ChangeBrightness_Action action = new(guidValue, controller!.LastPixelPosition, correctionFactor, toolSize, repeat, document.AnimationHandler.ActiveFrameBindable);

+ 4 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs

@@ -18,7 +18,7 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
 internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor where T : IShapeToolHandler
 {
-    protected int StrokeWidth => toolbar.ToolSize;
+    protected double StrokeWidth => toolbar.ToolSize;
 
     protected Color FillColor =>
         toolbar.Fill ? toolbar.FillColor.ToColor() : Colors.Transparent;
@@ -32,7 +32,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     
     private ShapeCorners initialCorners;
     private bool noMovement = true;
-    protected IBasicShapeToolbar toolbar;
+    protected IFillableShapeToolbar toolbar;
     private IColorsHandler? colorsVM;
     private bool ignoreNextColorChange = false;
 
@@ -46,7 +46,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
         colorsVM = GetHandler<IColorsHandler>();
         toolViewModel = GetHandler<T>();
-        toolbar = (IBasicShapeToolbar?)toolViewModel?.Toolbar;
+        toolbar = (IFillableShapeToolbar?)toolViewModel?.Toolbar;
         IStructureMemberHandler? member = document?.SelectedStructureMember;
         if (colorsVM is null || toolbar is null || member is null)
             return ExecutionState.Error;
@@ -141,7 +141,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
             return;
 
         var rect = RectD.FromCenterAndSize(corners.RectCenter, corners.RectSize);
-        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, StrokeWidth, StrokeColor,
+        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, (float)StrokeWidth, StrokeColor,
             FillColor) { AntiAliasing = toolbar.AntiAliasing };
         IAction drawAction = TransformMovedAction(shapeData, corners);
 

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

@@ -16,7 +16,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
 {
     private Guid guidValue;
     private Color color;
-    private int toolSize;
+    private double toolSize;
     private bool antiAliasing;
     private float hardness;
     private float spacing;
@@ -47,7 +47,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
         spacing = toolbar.Spacing;
 
         colorsHandler.AddSwatch(new PaletteColor(color.R, color.G, color.B));
-        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, controller!.LastPixelPosition, toolSize, true,
+        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, controller!.LastPixelPosition, (float)toolSize, true,
             antiAliasing, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
         internals!.ActionAccumulator.AddActions(action);
 
@@ -56,7 +56,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
 
     public override void OnPixelPositionChange(VecI pos)
     {
-        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, pos, toolSize, true, antiAliasing, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, pos, (float)toolSize, true, antiAliasing, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
         internals!.ActionAccumulator.AddActions(action);
     }
 

+ 3 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs

@@ -19,7 +19,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
     public override ExecutorType Type => ExecutorType.ToolLinked;
 
     protected Color StrokeColor => toolbar!.StrokeColor.ToColor();
-    protected int StrokeWidth => toolViewModel!.ToolSize;
+    protected double StrokeWidth => toolViewModel!.ToolSize;
     
     protected bool drawOnMask;
 
@@ -27,7 +27,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
     private bool startedDrawing = false;
     private T? toolViewModel;
     private IColorsHandler? colorsVM;
-    protected ILineToolbar? toolbar;
+    protected IShapeToolbar? toolbar;
     private bool ignoreNextColorChange = false;
 
     public override bool CanUndo => document.LineToolOverlayHandler.HasUndo;
@@ -41,7 +41,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
         colorsVM = GetHandler<IColorsHandler>();
         toolViewModel = GetHandler<T>();
         IStructureMemberHandler? member = document?.SelectedStructureMember;
-        toolbar = (ILineToolbar?)toolViewModel?.Toolbar;
+        toolbar = (IShapeToolbar)toolViewModel?.Toolbar;
         if (colorsVM is null || toolViewModel is null || member is null)
             return ExecutionState.Error;
 

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

@@ -14,14 +14,14 @@ internal class PenToolExecutor : UpdateableChangeExecutor
 {
     private Guid guidValue;
     private Color color;
-    public int ToolSize => penToolbar.ToolSize;
+    public double ToolSize => penToolbar.ToolSize;
     private bool drawOnMask;
     private bool pixelPerfect;
     private bool antiAliasing;
     private float hardness;
     private float spacing = 1;
 
-    private IBasicToolbar penToolbar;
+    private IPenToolbar penToolbar;
 
     public override ExecutionState Start()
     {
@@ -48,7 +48,7 @@ internal class PenToolExecutor : UpdateableChangeExecutor
         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, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
+            false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, (float)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);
@@ -60,7 +60,7 @@ internal class PenToolExecutor : UpdateableChangeExecutor
     {
         IAction? action = pixelPerfect switch
         {
-            false => new LineBasedPen_Action(guidValue, color, pos, ToolSize, false, antiAliasing, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
+            false => new LineBasedPen_Action(guidValue, color, pos, (float)ToolSize, false, antiAliasing, hardness, spacing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
             true => new PixelPerfectPen_Action(guidValue, pos, color, drawOnMask, document!.AnimationHandler.ActiveFrameBindable)
         };
         internals!.ActionAccumulator.AddActions(action);

+ 3 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterEllipseToolExecutor.cs

@@ -23,7 +23,7 @@ internal class RasterEllipseToolExecutor : DrawableShapeToolExecutor<IRasterElli
         lastRect = (RectD)rect;
         lastRadians = rotationRad;
 
-        internals!.ActionAccumulator.AddActions(new DrawRasterEllipse_Action(memberId, rect, rotationRad, StrokeColor, FillColor, StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+        internals!.ActionAccumulator.AddActions(new DrawRasterEllipse_Action(memberId, rect, rotationRad, StrokeColor, FillColor, (float)StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
     }
 
     public override ExecutorType Type => ExecutorType.ToolLinked;
@@ -31,7 +31,7 @@ internal class RasterEllipseToolExecutor : DrawableShapeToolExecutor<IRasterElli
     protected override void DrawShape(VecD currentPos, double rotationRad, bool firstDraw) => DrawEllipseOrCircle(currentPos, rotationRad, firstDraw);
     protected override IAction SettingsChangedAction()
     {
-        return new DrawRasterEllipse_Action(memberId, (RectI)lastRect, lastRadians, StrokeColor, FillColor, StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+        return new DrawRasterEllipse_Action(memberId, (RectI)lastRect, lastRadians, StrokeColor, FillColor, (float)StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
@@ -43,7 +43,7 @@ internal class RasterEllipseToolExecutor : DrawableShapeToolExecutor<IRasterElli
         lastRadians = radians;
         
         return new DrawRasterEllipse_Action(memberId, (RectI)lastRect, lastRadians, StrokeColor,
-            FillColor, StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+            FillColor, (float)StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction EndDrawAction() => new EndDrawRasterEllipse_Action();

+ 3 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterLineToolExecutor.cs

@@ -18,7 +18,7 @@ internal class RasterLineToolExecutor : LineExecutor<ILineToolHandler>
     {
         VecD dir = GetSignedDirection(startDrawingPos, pos);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);
-        return new DrawRasterLine_Action(memberId, ToPixelPos(startDrawingPos, oppositeDir), ToPixelPos(pos, dir), StrokeWidth,
+        return new DrawRasterLine_Action(memberId, ToPixelPos(startDrawingPos, oppositeDir), ToPixelPos(pos, dir), (float)StrokeWidth,
             StrokeColor, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
@@ -27,14 +27,14 @@ internal class RasterLineToolExecutor : LineExecutor<ILineToolHandler>
         VecD dir = GetSignedDirection(start, end);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);
         return new DrawRasterLine_Action(memberId, ToPixelPos(start, oppositeDir), ToPixelPos(end, dir), 
-            StrokeWidth, StrokeColor, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+            (float)StrokeWidth, StrokeColor, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction SettingsChange()
     {
         VecD dir = GetSignedDirection(startDrawingPos, curPos);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);
-        return new DrawRasterLine_Action(memberId, ToPixelPos(startDrawingPos, oppositeDir), ToPixelPos(curPos, dir), StrokeWidth,
+        return new DrawRasterLine_Action(memberId, ToPixelPos(startDrawingPos, oppositeDir), ToPixelPos(curPos, dir), (float)StrokeWidth,
             StrokeColor, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 

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

@@ -25,7 +25,7 @@ internal class RasterRectangleToolExecutor : DrawableShapeToolExecutor<IRasterRe
         lastRect = (RectD)rect;
         lastRadians = rotationRad;
 
-        lastData = new ShapeData(rect.Center, rect.Size, rotationRad, StrokeWidth, StrokeColor, FillColor)
+        lastData = new ShapeData(rect.Center, rect.Size, rotationRad, (float)StrokeWidth, StrokeColor, FillColor)
         {
             AntiAliasing = toolbar.AntiAliasing
         };
@@ -39,7 +39,7 @@ internal class RasterRectangleToolExecutor : DrawableShapeToolExecutor<IRasterRe
 
     protected override IAction SettingsChangedAction()
     {
-        lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, StrokeWidth, StrokeColor, FillColor)
+        lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, (float)StrokeWidth, StrokeColor, FillColor)
         {
             AntiAliasing = toolbar.AntiAliasing
         };

+ 3 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorEllipseToolExecutor.cs

@@ -49,7 +49,7 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
 
         EllipseVectorData data = new EllipseVectorData(firstCenter, firstRadius)
         {
-            StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = StrokeWidth,
+            StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = (float)StrokeWidth,
         };
 
         lastRect = rect;
@@ -64,7 +64,7 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
             {
                 StrokeColor = StrokeColor,
                 FillColor = FillColor,
-                StrokeWidth = StrokeWidth,
+                StrokeWidth = (float)StrokeWidth,
                 TransformationMatrix = lastMatrix
             });
     }
@@ -94,7 +94,7 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
         {
             StrokeColor = StrokeColor,
             FillColor = FillColor,
-            StrokeWidth = StrokeWidth,
+            StrokeWidth = (float)StrokeWidth,
             TransformationMatrix = matrix
         };
 

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

@@ -31,7 +31,7 @@ internal class VectorLineToolExecutor : LineExecutor<IVectorLineToolHandler>
         LineVectorData data = new LineVectorData(startDrawingPos, pos)
         {
             StrokeColor = StrokeColor,
-            StrokeWidth = StrokeWidth,
+            StrokeWidth = (float)StrokeWidth,
         };
         
         startPoint = startDrawingPos;
@@ -45,7 +45,7 @@ internal class VectorLineToolExecutor : LineExecutor<IVectorLineToolHandler>
         LineVectorData data = new LineVectorData(start, end)
         {
             StrokeColor = StrokeColor,
-            StrokeWidth = StrokeWidth,
+            StrokeWidth = (float)StrokeWidth,
         };
         
         startPoint = start;

+ 4 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorPathToolExecutor.cs

@@ -25,7 +25,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
     private IStructureMemberHandler member;
     private VectorPath startingPath;
     private IVectorPathToolHandler vectorPathToolHandler;
-    private IBasicShapeToolbar toolbar;
+    private IFillableShapeToolbar toolbar;
     private IColorsHandler colorHandler;
     private bool isValidPathLayer;
 
@@ -49,7 +49,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
             return ExecutionState.Error;
         }
 
-        toolbar = (IBasicShapeToolbar)vectorPathToolHandler.Toolbar;
+        toolbar = (IFillableShapeToolbar)vectorPathToolHandler.Toolbar;
         colorHandler = GetHandler<IColorsHandler>();
 
         if (member is IVectorLayerHandler vectorLayerHandler)
@@ -200,7 +200,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
         {
             return new PathVectorData(new VectorPath())
             {
-                StrokeWidth = toolbar.ToolSize,
+                StrokeWidth = (float)toolbar.ToolSize,
                 StrokeColor = toolbar.StrokeColor.ToColor(),
                 FillColor = toolbar.Fill ? toolbar.FillColor.ToColor() : Colors.Transparent,
             };
@@ -208,7 +208,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
         
         return new PathVectorData(new VectorPath(startingPath))
         {
-            StrokeWidth = toolbar.ToolSize,
+            StrokeWidth = (float)toolbar.ToolSize,
             StrokeColor = toolbar.StrokeColor.ToColor(),
             FillColor = toolbar.Fill ? toolbar.FillColor.ToColor() : Colors.Transparent,
         };

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

@@ -52,7 +52,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
 
         RectangleVectorData data = new RectangleVectorData(firstCenter, firstSize)
         {
-            StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = StrokeWidth,
+            StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = (float)StrokeWidth,
         };
 
         lastRect = rect;
@@ -67,7 +67,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
             {
                 StrokeColor = StrokeColor,
                 FillColor = FillColor,
-                StrokeWidth = StrokeWidth,
+                StrokeWidth = (float)StrokeWidth,
                 TransformationMatrix = lastMatrix
             });
     }

+ 0 - 6
src/PixiEditor/Models/Handlers/Toolbars/IBasicToolbar.cs

@@ -1,6 +0,0 @@
-namespace PixiEditor.Models.Handlers.Toolbars;
-
-internal interface IBasicToolbar : IToolbar
-{
-    public int ToolSize { get; set; }
-}

+ 9 - 0
src/PixiEditor/Models/Handlers/Toolbars/IFillableShapeToolbar.cs

@@ -0,0 +1,9 @@
+using Avalonia.Media;
+
+namespace PixiEditor.Models.Handlers.Toolbars;
+
+internal interface IFillableShapeToolbar : IShapeToolbar
+{
+    public bool Fill { get; set; }
+    public Color FillColor { get; set; }
+}

+ 0 - 10
src/PixiEditor/Models/Handlers/Toolbars/ILineToolbar.cs

@@ -1,10 +0,0 @@
-using Avalonia.Media;
-
-namespace PixiEditor.Models.Handlers.Toolbars;
-
-internal interface ILineToolbar : IBasicToolbar
-{
-    public Color StrokeColor { get; set; }
-    public bool AntiAliasing { get; set; }
-    public bool SyncWithPrimaryColor { get; }
-}

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

@@ -1,6 +1,6 @@
 namespace PixiEditor.Models.Handlers.Toolbars;
 
-internal interface IPenToolbar : IBasicToolbar
+internal interface IPenToolbar : IToolbar, IToolSizeToolbar
 {
     public bool AntiAliasing { get; set; }
     public float Hardness { get; set; }

+ 1 - 3
src/PixiEditor/Models/Handlers/Toolbars/IBasicShapeToolbar.cs → src/PixiEditor/Models/Handlers/Toolbars/IShapeToolbar.cs

@@ -2,11 +2,9 @@
 
 namespace PixiEditor.Models.Handlers.Toolbars;
 
-internal interface IBasicShapeToolbar : IBasicToolbar
+internal interface IShapeToolbar : IToolSizeToolbar
 {
     public Color StrokeColor { get; set; }
-    public bool Fill { get; set; }
-    public Color FillColor { get; set; }
     public bool SyncWithPrimaryColor { get; set; }
     public bool AntiAliasing { get; set; }
 }

+ 6 - 0
src/PixiEditor/Models/Handlers/Toolbars/IToolSizeToolbar.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.Handlers.Toolbars;
+
+internal interface IToolSizeToolbar : IToolbar
+{
+    public double ToolSize { get; set; }
+}

+ 1 - 1
src/PixiEditor/Models/Handlers/Tools/ILineToolHandler.cs

@@ -2,6 +2,6 @@
 
 internal interface ILineToolHandler : IToolHandler
 {
-    public int ToolSize { get; }
+    public double ToolSize { get; }
     public bool Snap { get; }
 }

+ 1 - 2
src/PixiEditor/Models/Handlers/Tools/IVectorLineToolHandler.cs

@@ -2,6 +2,5 @@
 
 internal interface IVectorLineToolHandler : ILineToolHandler 
 {
-    public int ToolSize { get; }
-    public bool Snap { get; }
+    
 }

+ 5 - 0
src/PixiEditor/Models/Serialization/Factories/ByteBuilder.cs

@@ -75,4 +75,9 @@ public class ByteBuilder
             AddInt(c);
         }
     }
+
+    public void AddFloat(float value)
+    {
+        _data.AddRange(BitConverter.GetBytes(value));
+    }
 }

+ 9 - 0
src/PixiEditor/Models/Serialization/Factories/ByteExtractor.cs

@@ -95,4 +95,13 @@ public class ByteExtractor
         
         return builder.ToString();
     }
+
+    public float GetFloat()
+    {
+        float value = BitConverter.ToSingle(_data, Position);
+        
+        Position += sizeof(float);
+        
+        return value;
+    }
 }

+ 3 - 2
src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs

@@ -23,12 +23,13 @@ public class ChunkyImageSerializationFactory : SerializationFactory<byte[], Chun
         return surfaceFactory.Serialize(surface);
     }
 
-    public override bool TryDeserialize(object serialized, out ChunkyImage original)
+    public override bool TryDeserialize(object serialized, out ChunkyImage original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is byte[] imgBytes)
         {
             surfaceFactory.Config = Config;
-            if (!surfaceFactory.TryDeserialize(imgBytes, out Surface surface))
+            if (!surfaceFactory.TryDeserialize(imgBytes, out Surface surface, serializerData))
             {
                 original = null;
                 return false;

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/ColorMatrixSerializationFactory.cs

@@ -16,7 +16,8 @@ internal class ColorMatrixSerializationFactory : SerializationFactory<Serializab
         };    
     }
 
-    public override bool TryDeserialize(object raw, out ColorMatrix original)
+    public override bool TryDeserialize(object raw, out ColorMatrix original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (raw is not Dictionary<string, object> serialized)
         {

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/ColorSerializationFactory.cs

@@ -15,7 +15,8 @@ public class ColorSerializationFactory : SerializationFactory<byte[], Color>
         return result; 
     }
 
-    public override bool TryDeserialize(object serialized, out Color original)
+    public override bool TryDeserialize(object serialized, out Color original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is byte[] { Length: 4 } bytes)
         {

+ 1 - 1
src/PixiEditor/Models/Serialization/Factories/EllipseSerializationFactory.cs

@@ -16,7 +16,7 @@ public class EllipseSerializationFactory : VectorShapeSerializationFactory<Ellip
     }
 
     protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        int strokeWidth, out EllipseVectorData original)
+        float strokeWidth, out EllipseVectorData original)
     {
         VecD center = extractor.GetVecD();
         VecD radius = extractor.GetVecD();

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/KernelSerializationFactory.cs

@@ -14,7 +14,8 @@ public class KernelSerializationFactory : SerializationFactory<SerializableKerne
         };    
     }
 
-    public override bool TryDeserialize(object raw, out Kernel original)
+    public override bool TryDeserialize(object raw, out Kernel original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (raw is not Dictionary<string, object> serialized)
         {

+ 1 - 1
src/PixiEditor/Models/Serialization/Factories/LineSerializationFactory.cs

@@ -16,7 +16,7 @@ internal class LineSerializationFactory : VectorShapeSerializationFactory<LineVe
     }
 
     protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        int strokeWidth, out LineVectorData original)
+        float strokeWidth, out LineVectorData original)
     {
         VecD start = extractor.GetVecD();
         VecD end = extractor.GetVecD();

+ 1 - 1
src/PixiEditor/Models/Serialization/Factories/PointsDataSerializationFactory.cs

@@ -14,7 +14,7 @@ internal class PointsDataSerializationFactory : VectorShapeSerializationFactory<
     }
 
     protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        int strokeWidth, out PointsVectorData original)
+        float strokeWidth, out PointsVectorData original)
     {
         List<VecD> points = extractor.GetVecDList();
         original = new PointsVectorData(points)

+ 1 - 1
src/PixiEditor/Models/Serialization/Factories/RectangleSerializationFactory.cs

@@ -17,7 +17,7 @@ internal class RectangleSerializationFactory : VectorShapeSerializationFactory<R
     }
 
     protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        int strokeWidth, out RectangleVectorData original)
+        float strokeWidth, out RectangleVectorData original)
     {
         VecD center = extractor.GetVecD();
         VecD size = extractor.GetVecD();

+ 5 - 4
src/PixiEditor/Models/Serialization/Factories/SerializationFactory.cs

@@ -9,7 +9,7 @@ public abstract class SerializationFactory
     public SerializationConfig Config { get; set; }
 
     public abstract object Serialize(object original);
-    public abstract object Deserialize(object rawData);
+    public abstract object Deserialize(object rawData, (string serializerName, string serializerVersion) serializerData);
     
     protected int ExtractInt(object value)
     {
@@ -36,7 +36,8 @@ public abstract class SerializationFactory
 public abstract class SerializationFactory<TSerializable, TOriginal> : SerializationFactory
 {
     public abstract TSerializable Serialize(TOriginal original);
-    public abstract bool TryDeserialize(object serialized, out TOriginal original);
+    public abstract bool TryDeserialize(object serialized, out TOriginal original,
+        (string serializerName, string serializerVersion) serializerData);
     
     public override object Serialize(object original)
     {
@@ -49,9 +50,9 @@ public abstract class SerializationFactory<TSerializable, TOriginal> : Serializa
         return serialized;
     }
     
-    public override object Deserialize(object rawData)
+    public override object Deserialize(object rawData, (string serializerName, string serializerVersion) serializerData)
     {
-        return TryDeserialize(rawData, out TOriginal original) ? original : default;
+        return TryDeserialize(rawData, out TOriginal original, serializerData) ? original : default;
     }
     
     public override Type OriginalType => typeof(TOriginal);

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/SurfaceSerializationFactory.cs

@@ -16,7 +16,8 @@ public class SurfaceSerializationFactory : SerializationFactory<byte[], Surface>
         return result;
     }
 
-    public override bool TryDeserialize(object serialized, out Surface original)
+    public override bool TryDeserialize(object serialized, out Surface original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is byte[] imgBytes)
         {

+ 3 - 2
src/PixiEditor/Models/Serialization/Factories/TextureSerializationFactory.cs

@@ -12,11 +12,12 @@ public class TextureSerializationFactory : SerializationFactory<byte[], Texture>
         return SurfaceFactory.Serialize(surface);
     }
 
-    public override bool TryDeserialize(object serialized, out Texture original)
+    public override bool TryDeserialize(object serialized, out Texture original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is byte[] imgBytes)
         {
-            if (SurfaceFactory.TryDeserialize(imgBytes, out Surface surface))
+            if (SurfaceFactory.TryDeserialize(imgBytes, out Surface surface, serializerData))
             {
                 original = new Texture(surface.Size);
                 original.DrawingSurface.Canvas.DrawSurface(surface.DrawingSurface, 0, 0);

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/VecD3SerializationFactory.cs

@@ -15,7 +15,8 @@ public class VecD3SerializationFactory : SerializationFactory<byte[], Vec3D>
         return result;
     }
 
-    public override bool TryDeserialize(object serialized, out Vec3D original)
+    public override bool TryDeserialize(object serialized, out Vec3D original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is byte[] { Length: sizeof(double) * 3 } bytes)
         {

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/VecDSerializationFactory.cs

@@ -14,7 +14,8 @@ public class VecDSerializationFactory : SerializationFactory<byte[], VecD>
         return result;
     }
 
-    public override bool TryDeserialize(object serialized, out VecD original)
+    public override bool TryDeserialize(object serialized, out VecD original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is byte[] { Length: sizeof(double) * 2 } bytes)
         {

+ 2 - 1
src/PixiEditor/Models/Serialization/Factories/VecISerializationFactory.cs

@@ -15,7 +15,8 @@ public class VecISerializationFactory : SerializationFactory<byte[], VecI>
         return result;
     }
 
-    public override bool TryDeserialize(object serialized, out VecI original)
+    public override bool TryDeserialize(object serialized, out VecI original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is byte[] { Length: 8 } bytes)
         {

+ 1 - 1
src/PixiEditor/Models/Serialization/Factories/VectorPathSerializationFactory.cs

@@ -16,7 +16,7 @@ internal class VectorPathSerializationFactory : VectorShapeSerializationFactory<
     }
 
     protected override bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor,
-        int strokeWidth, out PathVectorData original)
+        float strokeWidth, out PathVectorData original)
     {
         string path = extractor.GetString();
 

+ 15 - 5
src/PixiEditor/Models/Serialization/Factories/VectorShapeSerializationFactory.cs

@@ -12,7 +12,7 @@ public abstract class VectorShapeSerializationFactory<T> : SerializationFactory<
         builder.AddMatrix3X3(original.TransformationMatrix);
         builder.AddColor(original.StrokeColor);
         builder.AddColor(original.FillColor);
-        builder.AddInt(original.StrokeWidth);
+        builder.AddFloat(original.StrokeWidth);
         
         AddSpecificData(builder, original);
         
@@ -21,7 +21,8 @@ public abstract class VectorShapeSerializationFactory<T> : SerializationFactory<
     
     protected abstract void AddSpecificData(ByteBuilder builder, T original);
 
-    public override bool TryDeserialize(object serialized, out T original)
+    public override bool TryDeserialize(object serialized, out T original,
+        (string serializerName, string serializerVersion) serializerData)
     {
         if (serialized is not byte[] data)
         {
@@ -34,10 +35,19 @@ public abstract class VectorShapeSerializationFactory<T> : SerializationFactory<
         Matrix3X3 matrix = extractor.GetMatrix3X3();
         Color strokeColor = extractor.GetColor();
         Color fillColor = extractor.GetColor();
-        int strokeWidth = extractor.GetInt();
-        
+        float strokeWidth;
+        // Previous versions of the serializer saved stroke as int, and serializer data didn't exist
+        if (string.IsNullOrEmpty(serializerData.serializerVersion) && string.IsNullOrEmpty(serializerData.serializerName))
+        {
+            strokeWidth = extractor.GetInt();
+        }
+        else
+        {
+            strokeWidth = extractor.GetFloat();
+        }
+
         return DeserializeVectorData(extractor, matrix, strokeColor, fillColor, strokeWidth, out original);
     }
     
-    protected abstract bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor, int strokeWidth, out T original);
+    protected abstract bool DeserializeVectorData(ByteExtractor extractor, Matrix3X3 matrix, Color strokeColor, Color fillColor, float strokeWidth, out T original);
 }

+ 6 - 4
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -65,6 +65,8 @@ internal partial class DocumentViewModel
 
         var document = new PixiDocument
         {
+            SerializerName = "PixiEditor",
+            SerializerVersion = VersionHelpers.GetCurrentAssemblyVersion().ToString(),
             Width = Width,
             Height = Height,
             Swatches = ToCollection(Swatches),
@@ -88,12 +90,12 @@ internal partial class DocumentViewModel
         float resizeFactorY = (float)exportSize.Y / Height;
         VecD resizeFactor = new VecD(resizeFactorX, resizeFactorY);
 
-        AddElements(NodeGraph.StructureTree.Members.Reverse().ToList(), svgDocument, atTime, resizeFactor, vectorExportConfig);
+        AddElements(NodeGraph.StructureTree.Members.Where(x => x.IsVisibleBindable).Reverse().ToList(), svgDocument, atTime, resizeFactor, vectorExportConfig);
 
         return svgDocument;
     }
 
-    private void AddElements(IEnumerable<INodeHandler> root, IElementContainer elementContainer, KeyFrameTime atTime,
+    private void AddElements(IEnumerable<IStructureMemberHandler> root, IElementContainer elementContainer, KeyFrameTime atTime,
         VecD resizeFactor, VectorExportConfig? vectorExportConfig)
     {
         foreach (var member in root)
@@ -102,7 +104,7 @@ internal partial class DocumentViewModel
             {
                 var group = new SvgGroup();
 
-                AddElements(folderNodeViewModel.Children.Reverse().ToList(), group, atTime, resizeFactor, vectorExportConfig);
+                AddElements(folderNodeViewModel.Children.Where(x => x.IsVisibleBindable).Reverse().ToList(), group, atTime, resizeFactor, vectorExportConfig);
                 elementContainer.Children.Add(group);
             }
 
@@ -159,7 +161,7 @@ internal partial class DocumentViewModel
 
         line.Stroke.Unit = SvgColorUnit.FromRgba(lineData.StrokeColor.R, lineData.StrokeColor.G,
             lineData.StrokeColor.B, lineData.StrokeColor.A);
-        line.StrokeWidth.Unit = SvgNumericUnit.FromUserUnits(lineData.StrokeWidth * resizeFactor.X);
+        line.StrokeWidth.Unit = SvgNumericUnit.FromUserUnits(lineData.StrokeWidth);
         line.Transform.Unit = new SvgTransformUnit(lineData.TransformationMatrix);
 
         return line;

+ 5 - 3
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -279,6 +279,8 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
     {
         var builderInstance = new DocumentViewModelBuilder();
         builder(builderInstance);
+        
+        (string serializerName, string serializerVersion) serializerData = (builderInstance.SerializerName, builderInstance.SerializerVersion);
 
         Dictionary<int, Guid> mappedNodeIds = new();
         Dictionary<int, Guid> mappedKeyFrameIds = new();
@@ -346,7 +348,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                 if (serializedNode.AdditionalData != null && serializedNode.AdditionalData.Count > 0)
                 {
                     acc.AddActions(new DeserializeNodeAdditionalData_Action(nodeGuid,
-                        SerializationUtil.DeserializeDict(serializedNode.AdditionalData, config, allFactories)));
+                        SerializationUtil.DeserializeDict(serializedNode.AdditionalData, config, allFactories, serializerData)));
                 }
 
                 if (node.InputConnections != null)
@@ -378,7 +380,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
             {
                 foreach (var propertyValue in serializedNode.InputValues)
                 {
-                    object value = SerializationUtil.Deserialize(propertyValue.Value, config, allFactories);
+                    object value = SerializationUtil.Deserialize(propertyValue.Value, config, allFactories, serializerData);
                     acc.AddActions(new UpdatePropertyValue_Action(guid, propertyValue.Key, value));
                 }
             }
@@ -394,7 +396,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                         new SetKeyFrameData_Action(
                             guid,
                             keyFrameGuid,
-                            SerializationUtil.Deserialize(keyFrame.Data, config, allFactories),
+                            SerializationUtil.Deserialize(keyFrame.Data, config, allFactories, serializerData),
                             keyFrame.StartFrame,
                             keyFrame.Duration, keyFrame.AffectedElement, keyFrame.IsVisible));
                 }

+ 6 - 5
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -18,6 +18,7 @@ using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Handlers;
 using Drawie.Numerics;
+using PixiEditor.Models.Handlers.Toolbars;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Tools;
 using PixiEditor.ViewModels.Tools.Tools;
@@ -68,9 +69,9 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         set => SetProperty(ref toolCursor, value);
     }
 
-    public BasicToolbar? ActiveBasicToolbar
+    public IToolSizeToolbar? ActiveBasicToolbar
     {
-        get => ActiveTool?.Toolbar as BasicToolbar;
+        get => ActiveTool?.Toolbar as IToolSizeToolbar;
     }
 
     private IToolHandler? activeTool;
@@ -303,15 +304,15 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         CanExecute = "PixiEditor.Tools.CanChangeToolSize", Key = Key.OemOpenBrackets, AnalyticsTrack = true)]
     public void ChangeToolSize(int increment)
     {
-        if (ActiveTool?.Toolbar is not BasicToolbar toolbar)
+        if (ActiveTool?.Toolbar is not IToolSizeToolbar toolbar)
             return;
-        int newSize = toolbar.ToolSize + increment;
+        double newSize = toolbar.ToolSize + increment;
         if (newSize > 0)
             toolbar.ToolSize = newSize;
     }
 
     [Evaluator.CanExecute("PixiEditor.Tools.CanChangeToolSize")]
-    public bool CanChangeToolSize() => Owner.ToolsSubViewModel.ActiveTool?.Toolbar is BasicToolbar
+    public bool CanChangeToolSize() => Owner.ToolsSubViewModel.ActiveTool?.Toolbar is PenToolbar
                                        && Owner.ToolsSubViewModel.ActiveTool is not PenToolViewModel
                                        {
                                            PixelPerfectEnabled: true

+ 1 - 1
src/PixiEditor/ViewModels/Tools/ShapeTool.cs

@@ -18,7 +18,7 @@ internal abstract class ShapeTool : ToolViewModel, IShapeToolHandler
     public ShapeTool()
     {
         Cursor = new Cursor(StandardCursorType.Cross);
-        Toolbar = new BasicShapeToolbar();
+        Toolbar = new FillableShapeToolbar();
     }
 
     public override void OnDeselecting(bool transient)

+ 1 - 7
src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/ColorSettingViewModel.cs

@@ -1,10 +1,4 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Media;
-using Avalonia.Xaml.Interactivity;
-using PixiEditor.Helpers.Behaviours;
-using PixiEditor.Views.Input;
+using Avalonia.Media;
 
 namespace PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 

+ 1 - 1
src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/SizeSettingViewModel.cs

@@ -6,7 +6,7 @@ using PixiEditor.Views.Input;
 
 namespace PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 
-internal sealed class SizeSettingViewModel : Setting<int>
+internal sealed class SizeSettingViewModel : Setting<double>
 {
     private bool isEnabled = true;
     public SizeSettingViewModel(string name, string label = null)

+ 0 - 77
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/BasicShapeToolbar.cs

@@ -1,77 +0,0 @@
-using Avalonia.Media;
-using PixiEditor.Models.Handlers.Toolbars;
-using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
-
-namespace PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
-#nullable enable
-internal class BasicShapeToolbar : BasicToolbar, IBasicShapeToolbar
-{
-    public Color StrokeColor
-    {
-        get
-        {
-            return GetSetting<ColorSettingViewModel>(nameof(StrokeColor)).Value;
-        }
-        set
-        {
-            GetSetting<ColorSettingViewModel>(nameof(StrokeColor)).Value = value;
-        }
-    }
-
-    public bool Fill
-    {
-        get
-        {
-            return GetSetting<BoolSettingViewModel>(nameof(Fill)).Value;
-        }
-        set
-        {
-            GetSetting<BoolSettingViewModel>(nameof(Fill)).Value = value;
-        }
-    }
-
-    public Color FillColor
-    {
-        get
-        {
-            return GetSetting<ColorSettingViewModel>(nameof(FillColor)).Value;
-        }
-        set
-        {
-            GetSetting<ColorSettingViewModel>(nameof(FillColor)).Value = value;
-        }
-    }
-
-    public bool SyncWithPrimaryColor
-    {
-        get
-        {
-            return GetSetting<BoolSettingViewModel>(nameof(SyncWithPrimaryColor)).Value;
-        }
-        set
-        {
-            GetSetting<BoolSettingViewModel>(nameof(SyncWithPrimaryColor)).Value = value;
-        }
-    }
-
-    public bool AntiAliasing
-    {
-        get
-        {
-            return GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value;
-        }
-        set
-        {
-            GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value = value;
-        }
-    }
-
-    public BasicShapeToolbar()
-    {
-        AddSetting(new ColorSettingViewModel(nameof(StrokeColor), "STROKE_COLOR_LABEL"));
-        AddSetting(new BoolSettingViewModel(nameof(Fill), "FILL_SHAPE_LABEL") { Value = true });
-        AddSetting(new ColorSettingViewModel(nameof(FillColor), "FILL_COLOR_LABEL"));
-        AddSetting(new BoolSettingViewModel(nameof(SyncWithPrimaryColor), "SYNC_WITH_PRIMARY_COLOR_LABEL") { Value = true });
-        AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_LABEL") { Value = false, IsExposed = false});
-    }
-}

+ 0 - 27
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/BasicToolbar.cs

@@ -1,27 +0,0 @@
-using PixiEditor.Models.Handlers.Toolbars;
-using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
-
-namespace PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
-
-/// <summary>
-///     Toolbar with size setting.
-/// </summary>
-internal class BasicToolbar : Toolbar, IBasicToolbar
-{
-    public int ToolSize
-    {
-        get => GetSetting<SizeSettingViewModel>(nameof(ToolSize)).Value;
-        set => GetSetting<SizeSettingViewModel>(nameof(ToolSize)).Value = value;
-    }
-    public BasicToolbar()
-    {
-        var setting = new SizeSettingViewModel(nameof(ToolSize), "TOOL_SIZE_LABEL");
-        setting.ValueChanged += (_, _) => OnPropertyChanged(nameof(ToolSize));
-        AddSetting(setting);
-    }
-
-    public override void OnLoadedSettings()
-    {
-        OnPropertyChanged(nameof(ToolSize));
-    }
-}

+ 38 - 0
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/FillableShapeToolbar.cs

@@ -0,0 +1,38 @@
+using Avalonia.Media;
+using PixiEditor.Models.Handlers.Toolbars;
+using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
+
+namespace PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
+#nullable enable
+internal class FillableShapeToolbar : ShapeToolbar, IFillableShapeToolbar
+{
+    public bool Fill
+    {
+        get
+        {
+            return GetSetting<BoolSettingViewModel>(nameof(Fill)).Value;
+        }
+        set
+        {
+            GetSetting<BoolSettingViewModel>(nameof(Fill)).Value = value;
+        }
+    }
+
+    public Color FillColor
+    {
+        get
+        {
+            return GetSetting<ColorSettingViewModel>(nameof(FillColor)).Value;
+        }
+        set
+        {
+            GetSetting<ColorSettingViewModel>(nameof(FillColor)).Value = value;
+        }
+    }
+
+    public FillableShapeToolbar()
+    {
+        AddSetting(new BoolSettingViewModel(nameof(Fill), "FILL_SHAPE_LABEL") { Value = true });
+        AddSetting(new ColorSettingViewModel(nameof(FillColor), "FILL_COLOR_LABEL"));
+    }
+}

+ 15 - 1
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/PenToolbar.cs

@@ -3,7 +3,7 @@ using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
 
-internal class PenToolbar : BasicToolbar, IPenToolbar
+internal class PenToolbar : Toolbar, IPenToolbar
 {
     public bool AntiAliasing
     {
@@ -23,10 +23,24 @@ internal class PenToolbar : BasicToolbar, IPenToolbar
         set => GetSetting<PercentSettingViewModel>(nameof(Spacing)).Value = value;
     }
 
+    public double ToolSize
+    {
+        get => GetSetting<SizeSettingViewModel>(nameof(ToolSize)).Value;
+        set => GetSetting<SizeSettingViewModel>(nameof(ToolSize)).Value = value;
+    }
+
+    public override void OnLoadedSettings()
+    {
+        OnPropertyChanged(nameof(ToolSize));
+    }
+
     public PenToolbar()
     {
         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 });
+        var setting = new SizeSettingViewModel(nameof(ToolSize), "TOOL_SIZE_LABEL");
+        setting.ValueChanged += (_, _) => OnPropertyChanged(nameof(ToolSize));
+        AddSetting(setting);
     }
 }

+ 21 - 5
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/LineToolbar.cs → src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/ShapeToolbar.cs

@@ -4,9 +4,20 @@ using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
 
-internal class LineToolbar : BasicToolbar, ILineToolbar
+internal class ShapeToolbar : Toolbar, IShapeToolbar
 {
-    
+    public double ToolSize
+    {
+        get
+        {
+            return GetSetting<SizeSettingViewModel>(nameof(ToolSize)).Value;
+        }
+        set
+        {
+            GetSetting<SizeSettingViewModel>(nameof(ToolSize)).Value = value;
+        }
+    }
+
     public Color StrokeColor
     {
         get
@@ -40,10 +51,15 @@ internal class LineToolbar : BasicToolbar, ILineToolbar
         }
     }
 
-    public LineToolbar()
+    public ShapeToolbar()
     {
+        AddSetting(new SizeSettingViewModel(nameof(ToolSize), "STROKE_THICKNESS_LABEL"));
         AddSetting(new ColorSettingViewModel(nameof(StrokeColor), "STROKE_COLOR_LABEL"));
-        AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_LABEL") { IsExposed = false, Value = false });
-        AddSetting(new BoolSettingViewModel(nameof(SyncWithPrimaryColor), "SYNC_WITH_PRIMARY_COLOR_LABEL") { Value = true });
+        AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_LABEL")
+        {
+            IsExposed = false, Value = false
+        });
+        AddSetting(
+            new BoolSettingViewModel(nameof(SyncWithPrimaryColor), "SYNC_WITH_PRIMARY_COLOR_LABEL") { Value = true });
     }
 }

+ 2 - 2
src/PixiEditor/ViewModels/Tools/Tools/BrightnessToolViewModel.cs

@@ -23,7 +23,7 @@ internal class BrightnessToolViewModel : ToolViewModel, IBrightnessToolHandler
     public BrightnessToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<BrightnessToolViewModel, BasicToolbar>(this);
+        Toolbar = ToolbarFactory.Create<BrightnessToolViewModel, EmptyToolbar>(this);
     }
 
     public override bool IsErasable => true;
@@ -43,7 +43,7 @@ internal class BrightnessToolViewModel : ToolViewModel, IBrightnessToolHandler
         get => BrightnessMode;
     }
 
-    [Settings.Inherited]
+    [Settings.Size("TOOL_SIZE_LABEL", Name = "ToolSize")]
     public int ToolSize => GetValue<int>();
     
     [Settings.Float("STRENGTH_LABEL", 5, 0, 50)]

+ 1 - 1
src/PixiEditor/ViewModels/Tools/Tools/EraserToolViewModel.cs

@@ -21,7 +21,7 @@ internal class EraserToolViewModel : ToolViewModel, IEraserToolHandler
         Toolbar = ToolbarFactory.Create<EraserToolViewModel, PenToolbar>(this);
     }
 
-    [Settings.Inherited] public int ToolSize => GetValue<int>();
+    [Settings.Inherited] public double ToolSize => GetValue<int>();
 
     public override bool IsErasable => true;
 

+ 8 - 8
src/PixiEditor/ViewModels/Tools/Tools/PenToolViewModel.cs

@@ -17,7 +17,7 @@ namespace PixiEditor.ViewModels.Tools.Tools
     [Command.Tool(Key = Key.B)]
     internal class PenToolViewModel : ShapeTool, IPenToolHandler
     {
-        private int actualToolSize;
+        private double actualToolSize;
 
         public override string ToolNameLocalizationKey => "PEN_TOOL";
         public override BrushShape BrushShape => BrushShapeSetting;
@@ -35,7 +35,7 @@ namespace PixiEditor.ViewModels.Tools.Tools
         public override LocalizedString Tooltip => new LocalizedString("PEN_TOOL_TOOLTIP", Shortcut);
 
         [Settings.Inherited]
-        public int ToolSize => GetValue<int>();
+        public double ToolSize => GetValue<int>();
 
         [Settings.Bool("PIXEL_PERFECT_SETTING", Notify = nameof(PixelPerfectChanged), ExposedByDefault = false)]
         public bool PixelPerfectEnabled => GetValue<bool>();
@@ -61,8 +61,8 @@ namespace PixiEditor.ViewModels.Tools.Tools
         {
             if (e.NewTool == this && PixelPerfectEnabled)
             {
-                var toolbar = (BasicToolbar)Toolbar;
-                var setting = (SizeSettingViewModel)toolbar.Settings.First(x => x.Name == "ToolSize");
+                var toolbar = (PenToolbar)Toolbar;
+                var setting = toolbar.Settings.FirstOrDefault(x => x.Name == nameof(toolbar.ToolSize));
                 setting.Value = 1;
             }
             
@@ -71,13 +71,13 @@ namespace PixiEditor.ViewModels.Tools.Tools
                 return;
             }
 
-            if (e.OldTool is not { Toolbar: BasicToolbar oldToolbar })
+            if (e.OldTool is not { Toolbar: PenToolbar oldToolbar })
             {
                 return;
             }
             
             var oldSetting = (SizeSettingViewModel)oldToolbar.Settings[0];
-            actualToolSize = oldSetting.Value;
+            actualToolSize = (int)oldSetting.Value;
         }
 
         public override void OnDeselecting(bool transient)
@@ -87,14 +87,14 @@ namespace PixiEditor.ViewModels.Tools.Tools
                 return;
             }
 
-            var toolbar = (BasicToolbar)Toolbar;
+            var toolbar = (PenToolbar)Toolbar;
             var setting = (SizeSettingViewModel)toolbar.Settings[0];
             setting.Value = actualToolSize;
         }
 
         private void PixelPerfectChanged()
         {
-            var toolbar = (BasicToolbar)Toolbar;
+            var toolbar = (PenToolbar)Toolbar;
             var setting = (SizeSettingViewModel)toolbar.Settings[0];
 
             setting.IsEnabled = !PixelPerfectEnabled;

+ 3 - 2
src/PixiEditor/ViewModels/Tools/Tools/RasterLineToolViewModel.cs

@@ -20,7 +20,7 @@ internal class RasterLineToolViewModel : ShapeTool, ILineToolHandler
     public RasterLineToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<RasterLineToolViewModel, LineToolbar>(this);
+        Toolbar = ToolbarFactory.Create<RasterLineToolViewModel, ShapeToolbar>(this);
     }
 
     public override string ToolNameLocalizationKey => "LINE_TOOL";
@@ -29,7 +29,8 @@ internal class RasterLineToolViewModel : ShapeTool, ILineToolHandler
     public override Type[]? SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
     public override string DefaultIcon => PixiPerfectIcons.LowResLine;
 
-    [Settings.Inherited] public int ToolSize => GetValue<int>();
+    [Settings.Inherited] 
+    public double ToolSize => GetValue<double>();
 
     public bool Snap { get; private set; }
 

+ 4 - 5
src/PixiEditor/ViewModels/Tools/Tools/VectorLineToolViewModel.cs

@@ -26,7 +26,7 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
     public VectorLineToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
-        Toolbar = ToolbarFactory.Create<VectorLineToolViewModel, LineToolbar>(this);
+        Toolbar = ToolbarFactory.Create<VectorLineToolViewModel, ShapeToolbar>(this);
     }
 
     public override string ToolNameLocalizationKey => "LINE_TOOL";
@@ -36,7 +36,8 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
     public override Type[]? SupportedLayerTypes { get; } = [];
     public string? DefaultNewLayerName { get; } = new LocalizedString("NEW_LINE_LAYER_NAME");
 
-    [Settings.Inherited] public int ToolSize => GetValue<int>();
+    [Settings.Inherited] 
+    public double ToolSize => GetValue<double>();
 
     public bool Snap { get; private set; }
 
@@ -69,9 +70,7 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
         var layer = document.SelectedStructureMember;
         if (layer is IVectorLayerHandler vectorLayer)
         {
-            IReadOnlyLineData? lineVectorData =
-                vectorLayer.GetShapeData(document.AnimationDataViewModel.ActiveFrameTime) as IReadOnlyLineData;
-            if (lineVectorData is not null)
+            if (vectorLayer.GetShapeData(document.AnimationDataViewModel.ActiveFrameTime) is IReadOnlyLineData lineVectorData)
             {
                 document.LineToolOverlayViewModel.Show(lineVectorData.TransformedStart, lineVectorData.TransformedEnd,
                     false);

+ 1 - 1
src/PixiEditor/ViewModels/Tools/Tools/VectorPathToolViewModel.cs

@@ -32,7 +32,7 @@ internal class VectorPathToolViewModel : ShapeTool, IVectorPathToolHandler
 
     public VectorPathToolViewModel()
     {
-        var fillSetting = Toolbar.GetSetting(nameof(BasicShapeToolbar.Fill));
+        var fillSetting = Toolbar.GetSetting(nameof(FillableShapeToolbar.Fill));
         if (fillSetting != null)
         {
             fillSetting.Value = false;

+ 1 - 1
src/PixiParser

@@ -1 +1 @@
-Subproject commit f8d329ffab59c579a25c8a2813395191e1be9a70
+Subproject commit 364611d097a1febb4e36ac811dc391e67ec5f871