Browse Source

Added corner radius to rectangle

Krzysztof Krysiński 4 months ago
parent
commit
3c2d08012c
19 changed files with 174 additions and 47 deletions
  1. 6 4
      src/ChunkyImageLib/DataHolders/ShapeData.cs
  2. 45 9
      src/ChunkyImageLib/Operations/RectangleOperation.cs
  3. 1 1
      src/Drawie
  4. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyRectangleData.cs
  5. 35 6
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs
  6. 8 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs
  7. 2 2
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs
  8. 6 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorRectangleToolExecutor.cs
  9. 8 0
      src/PixiEditor/Models/Handlers/Tools/ICornerRadiusTool.cs
  10. 4 2
      src/PixiEditor/Models/Handlers/Tools/IRasterRectangleToolHandler.cs
  11. 4 2
      src/PixiEditor/Models/Handlers/Tools/IVectorRectangleToolHandler.cs
  12. 2 1
      src/PixiEditor/Models/IO/CustomDocumentFormats/SvgDocumentBuilder.cs
  13. 0 2
      src/PixiEditor/ViewModels/Tools/ShapeTool.cs
  14. 2 2
      src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/SizeSettingViewModel.cs
  15. 3 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/SettingAttributes.cs
  16. 1 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/ShapeToolbar.cs
  17. 1 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/ToolbarFactory.cs
  18. 22 5
      src/PixiEditor/ViewModels/Tools/Tools/RasterRectangleToolViewModel.cs
  19. 22 6
      src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs

+ 6 - 4
src/ChunkyImageLib/DataHolders/ShapeData.cs

@@ -8,7 +8,7 @@ namespace ChunkyImageLib.DataHolders;
 
 public record struct ShapeData
 {
-    public ShapeData(VecD center, VecD size, double rotation, float strokeWidth, Paintable stroke, Paintable fillPaintable, BlendMode blendMode = BlendMode.SrcOver)
+    public ShapeData(VecD center, VecD size, double cornerRadius, double rotation, float strokeWidth, Paintable stroke, Paintable fillPaintable, BlendMode blendMode = BlendMode.SrcOver)
     {
         Stroke = stroke;
         FillPaintable = fillPaintable;
@@ -16,6 +16,7 @@ public record struct ShapeData
         Size = size;
         Angle = rotation;
         StrokeWidth = strokeWidth;
+        CornerRadius = cornerRadius;
         BlendMode = blendMode;
     }
     public Paintable Stroke { get; }
@@ -25,15 +26,16 @@ public record struct ShapeData
 
     /// <summary>Can be negative to show flipping </summary>
     public VecD Size { get; }
+
+    public double CornerRadius { get; }
     public double Angle { get; }
     public float StrokeWidth { get; }
-
     public bool AntiAliasing { get; set; } = false;
     
 
     public ShapeData AsMirroredAcrossHorAxis(double horAxisY)
-        => new ShapeData(Center.ReflectY(horAxisY), new(Size.X, -Size.Y), -Angle, StrokeWidth, Stroke, FillPaintable, BlendMode);
+        => new ShapeData(Center.ReflectY(horAxisY), new(Size.X, -Size.Y), CornerRadius, -Angle, StrokeWidth, Stroke, FillPaintable, BlendMode);
     public ShapeData AsMirroredAcrossVerAxis(double verAxisX)
-        => new ShapeData(Center.ReflectX(verAxisX), new(-Size.X, Size.Y), -Angle, StrokeWidth, Stroke, FillPaintable, BlendMode);
+        => new ShapeData(Center.ReflectX(verAxisX), new(-Size.X, Size.Y), CornerRadius, -Angle, StrokeWidth, Stroke, FillPaintable, BlendMode);
 
 }

+ 45 - 9
src/ChunkyImageLib/Operations/RectangleOperation.cs

@@ -41,35 +41,53 @@ internal class RectangleOperation : IMirroredDrawOperation
 
         if (Data.AntiAliasing)
         {
-            DrawAntiAliased(surf, rect);
+            DrawAntiAliased(surf, rect, Data.CornerRadius);
         }
         else
         {
-            DrawPixelPerfect(surf, rect, innerRect);
+            DrawPixelPerfect(surf, rect, innerRect, Data.CornerRadius);
         }
 
         surf.Canvas.RestoreToCount(initial);
     }
 
-    private void DrawPixelPerfect(DrawingSurface surf, RectD rect, RectD innerRect)
+    private void DrawPixelPerfect(DrawingSurface surf, RectD rect, RectD innerRect, double radius)
     {
         // draw fill
         if (Data.FillPaintable.AnythingVisible)
         {
             int saved = surf.Canvas.Save();
-            surf.Canvas.ClipRect(innerRect);
+            if (radius == 0)
+            {
+                surf.Canvas.ClipRect(innerRect);
+            }
+            else
+            {
+                surf.Canvas.ClipRoundRect(innerRect, new VecD(radius), ClipOperation.Intersect);
+            }
+
             surf.Canvas.DrawPaintable(Data.FillPaintable, Data.BlendMode);
             surf.Canvas.RestoreToCount(saved);
         }
 
         // draw stroke
         surf.Canvas.Save();
-        surf.Canvas.ClipRect(rect);
-        surf.Canvas.ClipRect(innerRect, ClipOperation.Difference);
+        if (radius == 0)
+        {
+            surf.Canvas.ClipRect(rect);
+            surf.Canvas.ClipRect(innerRect, ClipOperation.Difference);
+        }
+        else
+        {
+            VecD vecRadius = new VecD(radius);
+            surf.Canvas.ClipRoundRect(rect, vecRadius, ClipOperation.Intersect);
+            surf.Canvas.ClipRoundRect(innerRect, vecRadius, ClipOperation.Difference);
+        }
+
         surf.Canvas.DrawPaintable(Data.Stroke, Data.BlendMode);
     }
 
-    private void DrawAntiAliased(DrawingSurface surf, RectD rect)
+    private void DrawAntiAliased(DrawingSurface surf, RectD rect, double radius)
     {
         // draw fill
         if (Data.FillPaintable.AnythingVisible)
@@ -79,7 +97,15 @@ internal class RectangleOperation : IMirroredDrawOperation
             paint.StrokeWidth = 0;
             paint.SetPaintable(Data.FillPaintable);
             paint.Style = PaintStyle.Fill;
-            surf.Canvas.DrawRect((float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height, paint);
+            if (radius == 0)
+            {
+                surf.Canvas.DrawRect((float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height, paint);
+            }
+            else
+            {
+                surf.Canvas.DrawRoundRect((float)rect.Left, (float)rect.Top, (float)rect.Width,
+                    (float)rect.Height, (float)radius, (float)radius, paint);
+            }
 
             surf.Canvas.RestoreToCount(saved);
         }
@@ -90,7 +116,17 @@ internal class RectangleOperation : IMirroredDrawOperation
         paint.SetPaintable(Data.StrokeWidth > 0 ? Data.Stroke : Data.FillPaintable);
         paint.Style = PaintStyle.Stroke;
         RectD innerRect = rect.Inflate(-Data.StrokeWidth / 2f);
-        surf.Canvas.DrawRect((float)innerRect.Left, (float)innerRect.Top, (float)innerRect.Width, (float)innerRect.Height, paint);
+
+        if (radius == 0)
+        {
+            surf.Canvas.DrawRect((float)innerRect.Left, (float)innerRect.Top, (float)innerRect.Width,
+                (float)innerRect.Height, paint);
+        }
+        else
+        {
+            surf.Canvas.DrawRoundRect((float)innerRect.Left, (float)innerRect.Top, (float)innerRect.Width,
+                (float)innerRect.Height, (float)radius, (float)radius, paint);
+        }
     }
 
     public AffectedArea FindAffectedArea(VecI imageSize)

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 02c8bda10d6536f1d8a613ef877526756a789132
+Subproject commit 2aaeb9ebb6f2d3b5824721e8ace0765eb58305ca

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyRectangleData.cs

@@ -2,8 +2,9 @@
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
 
-public interface IReadOnlyRectangleData : IReadOnlyShapeVectorData // TODO: Add IReadOnlyStrokeJoinable
+public interface IReadOnlyRectangleData : IReadOnlyShapeVectorData, IReadOnlyStrokeJoinable
 {
     public VecD Center { get; }
     public VecD Size { get; }
+    public double CornerRadius { get; }
 }

+ 35 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs

@@ -12,6 +12,9 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
 {
     public VecD Center { get; set; }
     public VecD Size { get; set; }
+    public double CornerRadius { get; set; }
+    public StrokeJoin StrokeLineJoin { get; set; } = StrokeJoin.Round;
+    public StrokeCap StrokeLineCap { get; } = StrokeCap.Butt;
 
     public override RectD GeometryAABB => RectD.FromCenterAndSize(Center, Size);
 
@@ -67,7 +70,7 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
         {
             paint.SetPaintable(FillPaintable);
             paint.Style = PaintStyle.Fill;
-            canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
+            DrawRect(canvas, paint);
         }
 
         if (StrokeWidth > 0 && Stroke.AnythingVisible)
@@ -76,7 +79,10 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
             paint.Style = PaintStyle.Stroke;
 
             paint.StrokeWidth = StrokeWidth;
-            canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
+            paint.StrokeCap = StrokeLineCap;
+            paint.StrokeJoin = StrokeLineJoin;
+
+            DrawRect(canvas, paint);
         }
 
         if (applyTransform)
@@ -85,6 +91,19 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
         }
     }
 
+    private void DrawRect(Canvas canvas, Paint paint)
+    {
+        if (CornerRadius == 0)
+        {
+            canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
+        }
+        else
+        {
+            RectD rect = RectD.FromCenterAndSize(Center, Size);
+            canvas.DrawRoundRect((float)rect.Pos.X, (float)rect.Pos.Y, (float)rect.Width, (float)rect.Height, (float)CornerRadius, (float)CornerRadius, paint);
+        }
+    }
+
     public override bool IsValid()
     {
         return Size is { X: > 0, Y: > 0 };
@@ -92,13 +111,21 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
 
     protected override int GetSpecificHash()
     {
-        return HashCode.Combine(Center, Size);
+        return HashCode.Combine(Center, Size, StrokeLineCap, StrokeLineJoin, CornerRadius);
     }
 
     public override VectorPath ToPath(bool transformed = false)
     {
         VectorPath path = new VectorPath();
-        path.AddRect(RectD.FromCenterAndSize(Center, Size));
+        if (CornerRadius == 0)
+        {
+            path.AddRect(RectD.FromCenterAndSize(Center, Size));
+        }
+        else
+        {
+            path.AddRoundRect(RectD.FromCenterAndSize(Center, Size), new VecD(CornerRadius));
+        }
+
         if (transformed)
         {
             path.Transform(TransformationMatrix);
@@ -109,7 +136,9 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
 
     protected bool Equals(RectangleVectorData other)
     {
-        return base.Equals(other) && Center.Equals(other.Center) && Size.Equals(other.Size);
+        return base.Equals(other) && Center.Equals(other.Center) && Size.Equals(other.Size) &&
+               StrokeLineJoin == other.StrokeLineJoin && StrokeLineCap == other.StrokeLineCap &&
+               CornerRadius.Equals(other.CornerRadius);
     }
 
     public override bool Equals(object? obj)
@@ -134,6 +163,6 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
 
     public override int GetHashCode()
     {
-        return HashCode.Combine(base.GetHashCode(), Center, Size);
+        return HashCode.Combine(base.GetHashCode(), Center, Size, StrokeLineJoin, StrokeLineCap, CornerRadius);
     }
 }

+ 8 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs

@@ -170,7 +170,14 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     private ShapeData ShapeDataFromCorners(ShapeCorners corners)
     {
         var rect = RectD.FromCenterAndSize(corners.RectCenter, corners.RectSize);
-        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, (float)StrokeWidth,
+        double cornerRadius = 0;
+        if (toolViewModel is ICornerRadiusTool cornerRadiusTool)
+        {
+            cornerRadius = cornerRadiusTool.CornerRadius;
+        }
+
+        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, cornerRadius, corners.RectRotation,
+            (float)StrokeWidth,
             StrokePaintable,
             FillPaintable) { AntiAliasing = toolbar.AntiAliasing };
         return shapeData;

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

@@ -26,7 +26,7 @@ internal class RasterRectangleToolExecutor : DrawableShapeToolExecutor<IRasterRe
         lastRect = (RectD)rect;
         lastRadians = rotationRad;
 
-        lastData = new ShapeData(rect.Center, rect.Size, rotationRad, (float)StrokeWidth, StrokePaintable, FillPaintable)
+        lastData = new ShapeData(rect.Center, rect.Size, toolViewModel.CornerRadius, rotationRad, (float)StrokeWidth, StrokePaintable, FillPaintable)
         {
             AntiAliasing = toolbar.AntiAliasing
         };
@@ -43,7 +43,7 @@ internal class RasterRectangleToolExecutor : DrawableShapeToolExecutor<IRasterRe
 
     protected override IAction SettingsChangedAction(string name, object value)
     {
-        lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, (float)StrokeWidth, StrokePaintable, FillPaintable)
+        lastData = new ShapeData(lastData.Center, lastData.Size, toolViewModel.CornerRadius, lastRadians, (float)StrokeWidth, StrokePaintable, FillPaintable)
         {
             AntiAliasing = toolbar.AntiAliasing
         };

+ 6 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorRectangleToolExecutor.cs

@@ -44,6 +44,8 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
         firstCenter = rectData.Center;
         firstSize = rectData.Size;
         lastMatrix = rectData.TransformationMatrix;
+        toolViewModel.CornerRadius = rectData.CornerRadius;
+
         return true;
     }
 
@@ -79,6 +81,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
         RectangleVectorData data = new RectangleVectorData(firstCenter, firstSize)
         {
             Stroke = StrokePaintable, FillPaintable = FillPaintable, StrokeWidth = (float)StrokeWidth,
+            CornerRadius = toolViewModel.CornerRadius,
         };
 
         lastRect = rect;
@@ -94,6 +97,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
             nameof(IFillableShapeToolbar.Fill) => VectorShapeChangeType.Fill,
             nameof(IFillableShapeToolbar.FillBrush) => VectorShapeChangeType.Fill,
             nameof(IShapeToolbar.StrokeBrush) => VectorShapeChangeType.Stroke,
+            "Radius" => VectorShapeChangeType.GeometryData,
             nameof(IShapeToolbar.ToolSize) => VectorShapeChangeType.Stroke,
             nameof(IShapeToolbar.AntiAliasing) => VectorShapeChangeType.OtherVisuals,
             "FillAndStroke" => VectorShapeChangeType.Fill | VectorShapeChangeType.Stroke,
@@ -105,6 +109,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
                 Stroke = StrokePaintable,
                 FillPaintable = FillPaintable,
                 StrokeWidth = (float)StrokeWidth,
+                CornerRadius = toolViewModel.CornerRadius,
                 TransformationMatrix = lastMatrix
             }, changeType);
     }
@@ -141,6 +146,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
             Stroke = data.Stroke,
             FillPaintable = data.FillPaintable,
             StrokeWidth = data.StrokeWidth,
+            CornerRadius = data.CornerRadius,
             TransformationMatrix = matrix
         };
 

+ 8 - 0
src/PixiEditor/Models/Handlers/Tools/ICornerRadiusTool.cs

@@ -0,0 +1,8 @@
+using Drawie.Numerics;
+
+namespace PixiEditor.Models.Handlers.Tools;
+
+public interface ICornerRadiusTool
+{
+    public double CornerRadius { get; set; }
+}

+ 4 - 2
src/PixiEditor/Models/Handlers/Tools/IRasterRectangleToolHandler.cs

@@ -1,5 +1,7 @@
-namespace PixiEditor.Models.Handlers.Tools;
+using Drawie.Numerics;
 
-internal interface IRasterRectangleToolHandler : IShapeToolHandler
+namespace PixiEditor.Models.Handlers.Tools;
+
+internal interface IRasterRectangleToolHandler : IShapeToolHandler, ICornerRadiusTool
 {
 }

+ 4 - 2
src/PixiEditor/Models/Handlers/Tools/IVectorRectangleToolHandler.cs

@@ -1,5 +1,7 @@
-namespace PixiEditor.Models.Handlers.Tools;
+using Drawie.Numerics;
 
-internal interface IVectorRectangleToolHandler : IShapeToolHandler
+namespace PixiEditor.Models.Handlers.Tools;
+
+internal interface IVectorRectangleToolHandler : IShapeToolHandler, ICornerRadiusTool
 {
 }

+ 2 - 1
src/PixiEditor/Models/IO/CustomDocumentFormats/SvgDocumentBuilder.cs

@@ -335,7 +335,8 @@ internal class SvgDocumentBuilder : IDocumentBuilder
     {
         return new RectangleVectorData(
             element.X.Unit?.PixelsValue ?? 0, element.Y.Unit?.PixelsValue ?? 0,
-            element.Width.Unit?.PixelsValue ?? 0, element.Height.Unit?.PixelsValue ?? 0);
+            element.Width.Unit?.PixelsValue ?? 0, element.Height.Unit?.PixelsValue ?? 0)
+            { CornerRadius = (element.Rx.Unit?.PixelsValue ?? element.Ry.Unit?.PixelsValue) ?? 0 };
     }
 
     private TextVectorData AddText(SvgText element)

+ 0 - 2
src/PixiEditor/ViewModels/Tools/ShapeTool.cs

@@ -15,7 +15,6 @@ internal abstract class ShapeTool : ToolViewModel, IShapeToolHandler
     public override bool IsErasable => true;
     public bool DrawEven { get; protected set; }
     public bool DrawFromCenter { get; protected set; }
-    
 
     public ShapeTool()
     {
@@ -23,7 +22,6 @@ internal abstract class ShapeTool : ToolViewModel, IShapeToolHandler
         Toolbar = new FillableShapeToolbar();
     }
 
-
     protected override void OnDeselecting(bool transient)
     {
         if (!transient)

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

@@ -14,12 +14,12 @@ internal sealed class SizeSettingViewModel : Setting<double>
     private int decimalPlaces;
     private string unit = "px";
     
-    public SizeSettingViewModel(string name, string label = null, double min = 1, double max = double.PositiveInfinity,
+    public SizeSettingViewModel(string name, string label = null, double defaultValue = 1, double min = 1, double max = double.PositiveInfinity,
         int decimalPlaces = 0, string unit = "px")
         : base(name)
     {
         Label = label;
-        Value = 1;
+        Value = defaultValue;
 
         this.min = min;
         this.max = max;

+ 3 - 1
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/SettingAttributes.cs

@@ -80,7 +80,9 @@ public static class Settings
     /// </summary>
     public class SizeAttribute : SettingsAttribute
     {
-        public SizeAttribute(string labelKey) : base(labelKey) { }
+        public SizeAttribute(string labelKey, double defaultValue = 1) : base(labelKey, defaultValue) { }
+
+        public double Min { get; set; } = 1;
     }
 
     /// <summary>

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

@@ -54,7 +54,7 @@ internal class ShapeToolbar : Toolbar, IShapeToolbar
 
     public ShapeToolbar()
     {
-        AddSetting(new SizeSettingViewModel(nameof(ToolSize), "STROKE_WIDTH", 0, decimalPlaces: 2));
+        AddSetting(new SizeSettingViewModel(nameof(ToolSize), "STROKE_WIDTH", 0, min: 0, decimalPlaces: 2));
         AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_LABEL")
         {
             IsExposed = false, Value = false

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

@@ -65,7 +65,7 @@ internal static class ToolbarFactory
                 percentAttribute.Min, percentAttribute.Max),
             Settings.FloatAttribute floatAttribute => new FloatSettingViewModel(name, (float)(attribute.DefaultValue ?? 0f), label,
                 floatAttribute.Min, floatAttribute.Max),
-            Settings.SizeAttribute => new SizeSettingViewModel(name, label),
+            Settings.SizeAttribute sa => new SizeSettingViewModel(name, label, (double)(sa.DefaultValue ?? 0f), sa.Min),
             _ => throw new NotImplementedException(
                 $"SettingsAttribute of type '{attribute.GetType().FullName}' has not been implemented")
         };

+ 22 - 5
src/PixiEditor/ViewModels/Tools/Tools/RasterRectangleToolViewModel.cs

@@ -7,6 +7,7 @@ using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using Drawie.Numerics;
 using PixiEditor.UI.Common.Fonts;
+using PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
 
 namespace PixiEditor.ViewModels.Tools.Tools;
 
@@ -15,11 +16,6 @@ internal class RasterRectangleToolViewModel : ShapeTool, IRasterRectangleToolHan
 {
     private string defaultActionDisplay = "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT";
 
-    public RasterRectangleToolViewModel()
-    {
-        ActionDisplay = defaultActionDisplay;
-    }
-
     public override string ToolNameLocalizationKey => "RECTANGLE_TOOL";
     public override Type[]? SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
     public override LocalizedString Tooltip => new LocalizedString("RECTANGLE_TOOL_TOOLTIP", Shortcut);
@@ -30,6 +26,27 @@ internal class RasterRectangleToolViewModel : ShapeTool, IRasterRectangleToolHan
 
     public override Type LayerTypeToCreateOnEmptyUse { get; } = typeof(ImageLayerNode);
 
+
+    [Settings.Size("RADIUS", 0, ExposedByDefault = true, Min = 0)]
+    public double CornerRadius
+    {
+        get
+        {
+            return GetValue<double>();
+        }
+        set
+        {
+            SetValue(value);
+        }
+    }
+
+
+    public RasterRectangleToolViewModel()
+    {
+        ActionDisplay = defaultActionDisplay;
+        Toolbar = ToolbarFactory.Create<RasterRectangleToolViewModel, FillableShapeToolbar>(this);
+    }
+
     public override void KeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown, Key argsKey)
     {
         DrawFromCenter = ctrlIsDown;

+ 22 - 6
src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs

@@ -9,6 +9,7 @@ using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using Drawie.Numerics;
 using PixiEditor.UI.Common.Fonts;
+using PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
 
 namespace PixiEditor.ViewModels.Tools.Tools;
 
@@ -20,12 +21,6 @@ internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHan
     private string defaultActionDisplay = "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT";
     public override string ToolNameLocalizationKey => "RECTANGLE_TOOL";
     public override bool IsErasable => false;
-
-    public VectorRectangleToolViewModel()
-    {
-        ActionDisplay = defaultActionDisplay;
-    }
-
     public override Type[]? SupportedLayerTypes { get; } = [];
     public override LocalizedString Tooltip => new LocalizedString("RECTANGLE_TOOL_TOOLTIP", Shortcut);
 
@@ -34,6 +29,27 @@ internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHan
     public override Type LayerTypeToCreateOnEmptyUse { get; } = typeof(VectorLayerNode);
     public string? DefaultNewLayerName { get; } = new LocalizedString(NewLayerKey);
 
+    private VecD cornerRadius = new VecD(0, 0);
+
+    [Settings.Size("RADIUS", 0, ExposedByDefault = true, Min = 0)]
+    public double CornerRadius
+    {
+        get
+        {
+            return GetValue<double>();
+        }
+        set
+        {
+            SetValue(value);
+        }
+    }
+
+    public VectorRectangleToolViewModel()
+    {
+        ActionDisplay = defaultActionDisplay;
+        Toolbar = ToolbarFactory.Create<VectorRectangleToolViewModel, FillableShapeToolbar>(this);
+    }
+
     public override void KeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown, Key argsKey)
     {
         DrawFromCenter = ctrlIsDown;