Browse Source

Added vector rectangle

flabbet 11 months ago
parent
commit
4b622a5dee

+ 9 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyRectangleData.cs

@@ -0,0 +1,9 @@
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
+
+public interface IReadOnlyRectangleData : IReadOnlyShapeVectorData
+{
+    public VecD Center { get; }
+    public VecD Size { get; }
+}

+ 74 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs

@@ -0,0 +1,74 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.DrawingApi.Core.Surfaces.Vector;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+
+public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
+{
+    public VecD Center { get; }
+    public VecD Size { get; }
+
+    public override RectD GeometryAABB => RectD.FromCenterAndSize(Center, Size); 
+
+    public override ShapeCorners TransformationCorners =>
+        new ShapeCorners(Center, Size).WithMatrix(TransformationMatrix);
+
+
+    public RectangleVectorData(VecD center, VecD size)
+    {
+        Center = center;
+        Size = size;
+    }
+    
+    public override void Rasterize(DrawingSurface drawingSurface, ChunkResolution resolution)
+    {
+        var imageSize = (VecI)Size; 
+
+        using ChunkyImage img = new ChunkyImage(imageSize);
+
+        RectI drawRect = (RectI)RectD.FromTwoPoints(VecD.Zero, Size).RoundOutwards();
+
+        ShapeData data = new ShapeData(drawRect.Center, drawRect.Size, 0, StrokeWidth, StrokeColor, FillColor);
+        img.EnqueueDrawRectangle(data);
+        img.CommitChanges();
+
+        VecI topLeft = (VecI)(Center - Size / 2); 
+
+        RectI region = new(VecI.Zero, (VecI)GeometryAABB.Size);
+
+        int num = drawingSurface.Canvas.Save();
+        drawingSurface.Canvas.SetMatrix(TransformationMatrix);
+
+        img.DrawMostUpToDateRegionOn(region, resolution, drawingSurface, topLeft);
+
+        drawingSurface.Canvas.RestoreToCount(num);
+    }
+
+    public override bool IsValid()
+    {
+        return Size is { X: > 0, Y: > 0 };
+    }
+
+    public override int CalculateHash()
+    {
+        return HashCode.Combine(Center, Size, StrokeColor, FillColor, StrokeWidth, TransformationMatrix);
+    }
+
+    public override int GetCacheHash()
+    {
+        return CalculateHash();
+    }
+
+    public override object Clone()
+    {
+        return new RectangleVectorData(Center, Size)
+        {
+            StrokeColor = StrokeColor,
+            FillColor = FillColor,
+            StrokeWidth = StrokeWidth,
+            TransformationMatrix = TransformationMatrix
+        };
+    }
+}

+ 1 - 1
src/PixiEditor/Data/Configs/ToolSetsConfig.json

@@ -27,7 +27,7 @@
       "Move",
       "RasterLine",
       "VectorEllipse",
-      "RasterRectangle"
+      "VectorRectangle"
     ]
   }
 ]

+ 1 - 0
src/PixiEditor/Helpers/ServiceCollectionHelpers.cs

@@ -96,6 +96,7 @@ internal static class ServiceCollectionHelpers
             .AddTool<IColorPickerHandler, ColorPickerToolViewModel>()
             .AddTool<IBrightnessToolHandler, BrightnessToolViewModel>()
             .AddTool<IVectorEllipseToolHandler, VectorEllipseToolViewModel>()
+            .AddTool<IVectorRectangleToolHandler, VectorRectangleToolViewModel>()
             .AddTool<ZoomToolViewModel>()
             // File types
             .AddSingleton<IoFileType, PixiFileType>()

+ 7 - 1
src/PixiEditor/Models/DocumentModels/Public/DocumentToolsModule.cs

@@ -27,7 +27,7 @@ internal class DocumentToolsModule
 
     public void UseColorPickerTool() => Internals.ChangeController.TryStartExecutor<ColorPickerToolExecutor>();
 
-    public void UseRectangleTool()
+    public void UseRasterRectangleTool()
     {
         bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
         Internals.ChangeController.TryStartExecutor<RasterRectangleToolExecutor>(force);
@@ -44,6 +44,12 @@ internal class DocumentToolsModule
         bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
         Internals.ChangeController.TryStartExecutor<VectorEllipseToolExecutor>(force);
     }
+    
+    public void UseVectorRectangleTool()
+    {
+        bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
+        Internals.ChangeController.TryStartExecutor<VectorRectangleToolExecutor>(force);
+    }
 
     public void UseLineTool()
     {

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

@@ -0,0 +1,78 @@
+using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.Operations;
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.ChangeableDocument.Actions.Generated;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Models.Handlers.Tools;
+using PixiEditor.Models.Tools;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+
+internal class VectorRectangleToolExecutor : ShapeToolExecutor<IVectorRectangleToolHandler>
+{
+    public override ExecutorType Type => ExecutorType.ToolLinked;
+    protected override DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_Shear_NoPerspective;
+
+    private VecD firstSize;
+    private VecD firstCenter;
+
+    private Matrix3X3 lastMatrix = Matrix3X3.Identity;
+
+    protected override void DrawShape(VecI curPos, double rotationRad, bool firstDraw)
+    {
+        RectI rect;
+        if (firstDraw)
+            rect = new RectI(curPos, VecI.Zero);
+        else if (toolViewModel!.DrawSquare)
+            rect = GetSquaredCoordinates(startPos, curPos);
+        else
+            rect = RectI.FromTwoPixels(startPos, curPos);
+
+        firstCenter = rect.Center;
+        firstSize = rect.Size;
+
+        RectangleVectorData data = new RectangleVectorData(firstCenter, firstSize)
+        {
+            StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = StrokeWidth,
+        };
+
+        lastRect = rect;
+
+        internals!.ActionAccumulator.AddActions(new SetShapeGeometry_Action(memberGuid, data));
+    }
+
+    protected override IAction SettingsChangedAction()
+    {
+        return new SetShapeGeometry_Action(memberGuid,
+            new RectangleVectorData(firstCenter, firstSize)
+            {
+                StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = StrokeWidth,
+                TransformationMatrix = lastMatrix
+            });
+    }
+
+    protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
+    {
+        RectI rect = (RectI)RectD.FromCenterAndSize(data.Center, data.Size);
+        RectD firstRect = RectD.FromCenterAndSize(firstCenter, firstSize);
+        Matrix3X3 matrix = OperationHelper.CreateMatrixFromPoints(corners, firstSize);
+        matrix = matrix.Concat(Matrix3X3.CreateTranslation(-(float)firstRect.TopLeft.X, -(float)firstRect.TopLeft.Y));
+
+        RectangleVectorData newData = new RectangleVectorData(firstCenter, firstSize)
+        {
+            StrokeColor = data.StrokeColor, FillColor = data.FillColor, StrokeWidth = data.StrokeWidth,
+            TransformationMatrix = matrix
+        };
+
+        lastMatrix = matrix;
+
+        return new SetShapeGeometry_Action(memberGuid, newData);
+    }
+
+    protected override IAction EndDrawAction()
+    {
+        return new EndSetShapeGeometry_Action();
+    }
+}

+ 6 - 0
src/PixiEditor/Models/Handlers/Tools/IVectorRectangleToolHandler.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.Handlers.Tools;
+
+internal interface IVectorRectangleToolHandler : IShapeToolHandler
+{
+    public bool DrawSquare { get; }
+}

+ 60 - 19
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -107,7 +107,7 @@ internal partial class DocumentViewModel
 
             if (member is IRasterLayerHandler)
             {
-                AddSvgImage(elementContainer, atTime, member, resizeFactor, 
+                AddSvgImage(elementContainer, atTime, member, resizeFactor,
                     vectorExportConfig?.UseNearestNeighborForImageUpscaling ?? false);
             }
             else if (member is IVectorLayerHandler vectorLayerHandler)
@@ -117,28 +117,69 @@ internal partial class DocumentViewModel
         }
     }
 
-    private void AddSvgShape(IElementContainer elementContainer, IVectorLayerHandler vectorLayerHandler, VecD resizeFactor)
+    private void AddSvgShape(IElementContainer elementContainer, IVectorLayerHandler vectorLayerHandler,
+        VecD resizeFactor)
     {
-        IReadOnlyVectorNode vectorNode = (IReadOnlyVectorNode)Internals.Tracker.Document.FindNode(vectorLayerHandler.Id);
+        IReadOnlyVectorNode vectorNode =
+            (IReadOnlyVectorNode)Internals.Tracker.Document.FindNode(vectorLayerHandler.Id);
+
+        SvgElement? elementToAdd = null;
 
         if (vectorNode.ShapeData is IReadOnlyEllipseData ellipseData)
         {
-            SvgEllipse ellipse = new SvgEllipse();
-            ellipse.Cx.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Center.X * resizeFactor.X);
-            ellipse.Cy.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Center.Y * resizeFactor.Y);
-            ellipse.Rx.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Radius.X * resizeFactor.X);
-            ellipse.Ry.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Radius.Y * resizeFactor.Y);
-            ellipse.Fill.Unit = SvgColorUnit.FromRgba(ellipseData.FillColor.R, ellipseData.FillColor.G, ellipseData.FillColor.B, ellipseData.FillColor.A);
-            ellipse.Stroke.Unit = SvgColorUnit.FromRgba(ellipseData.StrokeColor.R, ellipseData.StrokeColor.G, ellipseData.StrokeColor.B, ellipseData.StrokeColor.A);
-            ellipse.StrokeWidth.Unit = SvgNumericUnit.FromUserUnits(ellipseData.StrokeWidth);
-            ellipse.Transform.Unit = new SvgTransformUnit(ellipseData.TransformationMatrix);
-            
-            elementContainer.Children.Add(ellipse);
+            elementToAdd = AddEllipse(resizeFactor, ellipseData);
+        }
+        else if (vectorNode.ShapeData is IReadOnlyRectangleData rectangleData)
+        {
+            elementToAdd = AddRectangle(resizeFactor, rectangleData);
+        } 
+
+        if (elementToAdd != null)
+        {
+            elementContainer.Children.Add(elementToAdd);
         }
     }
 
+    private static SvgEllipse AddEllipse(VecD resizeFactor, IReadOnlyEllipseData ellipseData)
+    {
+        SvgEllipse ellipse = new SvgEllipse();
+        ellipse.Cx.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Center.X * resizeFactor.X);
+        ellipse.Cy.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Center.Y * resizeFactor.Y);
+        ellipse.Rx.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Radius.X * resizeFactor.X);
+        ellipse.Ry.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Radius.Y * resizeFactor.Y);
+        ellipse.Fill.Unit = SvgColorUnit.FromRgba(ellipseData.FillColor.R, ellipseData.FillColor.G,
+            ellipseData.FillColor.B, ellipseData.FillColor.A);
+        ellipse.Stroke.Unit = SvgColorUnit.FromRgba(ellipseData.StrokeColor.R, ellipseData.StrokeColor.G,
+            ellipseData.StrokeColor.B, ellipseData.StrokeColor.A);
+        ellipse.StrokeWidth.Unit = SvgNumericUnit.FromUserUnits(ellipseData.StrokeWidth);
+        ellipse.Transform.Unit = new SvgTransformUnit(ellipseData.TransformationMatrix);
+
+        return ellipse;
+    }
+
+    private SvgRectangle AddRectangle(VecD resizeFactor, IReadOnlyRectangleData rectangleData)
+    {
+        SvgRectangle rect = new SvgRectangle();
+        rect.X.Unit =
+            SvgNumericUnit.FromUserUnits(rectangleData.Center.X * resizeFactor.X -
+                                         rectangleData.Size.X / 2 * resizeFactor.X);
+        rect.Y.Unit =
+            SvgNumericUnit.FromUserUnits(rectangleData.Center.Y * resizeFactor.Y -
+                                         rectangleData.Size.Y / 2 * resizeFactor.Y);
+        rect.Width.Unit = SvgNumericUnit.FromUserUnits(rectangleData.Size.X * resizeFactor.X);
+        rect.Height.Unit = SvgNumericUnit.FromUserUnits(rectangleData.Size.Y * resizeFactor.Y);
+        rect.Fill.Unit = SvgColorUnit.FromRgba(rectangleData.FillColor.R, rectangleData.FillColor.G,
+            rectangleData.FillColor.B, rectangleData.FillColor.A);
+        rect.Stroke.Unit = SvgColorUnit.FromRgba(rectangleData.StrokeColor.R, rectangleData.StrokeColor.G,
+            rectangleData.StrokeColor.B, rectangleData.StrokeColor.A);
+        rect.StrokeWidth.Unit = SvgNumericUnit.FromUserUnits(rectangleData.StrokeWidth);
+        rect.Transform.Unit = new SvgTransformUnit(rectangleData.TransformationMatrix);
+        
+        return rect;
+    }
+
     private void AddSvgImage(IElementContainer elementContainer, KeyFrameTime atTime, INodeHandler member,
-        VecD resizeFactor, bool useNearestNeighborForImageUpscaling) 
+        VecD resizeFactor, bool useNearestNeighborForImageUpscaling)
     {
         IReadOnlyImageNode imageNode = (IReadOnlyImageNode)Internals.Tracker.Document.FindNode(member.Id);
 
@@ -150,13 +191,13 @@ internal partial class DocumentViewModel
         DrawingBackendApi.Current.RenderingServer.Invoke(() =>
         {
             using Texture rendered = Renderer.RenderLayer(imageNode.Id, ChunkResolution.Full, atTime.Frame);
-            
+
             using Surface surface = new Surface(rendered.Size);
             surface.DrawingSurface.Canvas.DrawImage(rendered.DrawingSurface.Snapshot(), 0, 0);
 
             toSave = surface.DrawingSurface.Snapshot((RectI)tightBounds.Value);
         });
-        
+
         var image = CreateImageElement(resizeFactor, tightBounds.Value, toSave, useNearestNeighborForImageUpscaling);
 
         elementContainer.Children.Add(image);
@@ -198,12 +239,12 @@ internal partial class DocumentViewModel
         image.Width.Unit = SvgNumericUnit.FromUserUnits(targetBounds.Width);
         image.Height.Unit = SvgNumericUnit.FromUserUnits(targetBounds.Height);
         image.Href.Unit = new SvgStringUnit($"data:image/png;base64,{Convert.ToBase64String(targetBytes)}");
-        
+
         if (useNearestNeighborForImageUpscaling)
         {
             image.ImageRendering.Unit = new SvgEnumUnit<SvgImageRenderingType>(SvgImageRenderingType.Pixelated);
         }
-        
+
         return image;
     }
 

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

@@ -41,6 +41,6 @@ internal class RasterRectangleToolViewModel : ShapeTool, IRasterRectangleToolHan
 
     public override void UseTool(VecD pos)
     {
-        ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseRectangleTool();
+        ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseRasterRectangleTool();
     }
 }

+ 41 - 0
src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs

@@ -0,0 +1,41 @@
+using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Models.Handlers.Tools;
+using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
+
+namespace PixiEditor.ViewModels.Tools.Tools;
+
+internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHandler
+{
+    private string defaultActionDisplay = "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT";
+    public override string ToolNameLocalizationKey => "RECTANGLE_TOOL";
+
+    public VectorRectangleToolViewModel()
+    {
+        ActionDisplay = defaultActionDisplay;
+    }
+
+    public override LocalizedString Tooltip => new LocalizedString("RECTANGLE_TOOL_TOOLTIP", Shortcut);
+    public bool DrawSquare { get; private set; }
+
+    public override string Icon => PixiPerfectIcons.Square;
+
+    public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
+    {
+        if (shiftIsDown)
+        {
+            DrawSquare = true;
+            ActionDisplay = "RECTANGLE_TOOL_ACTION_DISPLAY_SHIFT";
+        }
+        else
+        {
+            DrawSquare = false;
+            ActionDisplay = defaultActionDisplay;
+        }
+    }
+
+    public override void UseTool(VecD pos)
+    {
+        ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseVectorRectangleTool();
+    }
+}