Browse Source

Fixed svg exporting and added double precision to size input

flabbet 8 months ago
parent
commit
abc02b04e5

+ 50 - 38
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -90,13 +90,15 @@ internal partial class DocumentViewModel
         float resizeFactorY = (float)exportSize.Y / Height;
         VecD resizeFactor = new VecD(resizeFactorX, resizeFactorY);
 
-        AddElements(NodeGraph.StructureTree.Members.Where(x => x.IsVisibleBindable).Reverse().ToList(), svgDocument, atTime, resizeFactor, vectorExportConfig);
+        AddElements(NodeGraph.StructureTree.Members.Where(x => x.IsVisibleBindable).Reverse().ToList(), svgDocument,
+            atTime, resizeFactor, vectorExportConfig, exportSize);
 
         return svgDocument;
     }
 
-    private void AddElements(IEnumerable<IStructureMemberHandler> root, IElementContainer elementContainer, KeyFrameTime atTime,
-        VecD resizeFactor, VectorExportConfig? vectorExportConfig)
+    private void AddElements(IEnumerable<IStructureMemberHandler> root, IElementContainer elementContainer,
+        KeyFrameTime atTime,
+        VecD resizeFactor, VectorExportConfig? vectorExportConfig, VecI exportSize)
     {
         foreach (var member in root)
         {
@@ -104,7 +106,8 @@ internal partial class DocumentViewModel
             {
                 var group = new SvgGroup();
 
-                AddElements(folderNodeViewModel.Children.Where(x => x.IsVisibleBindable).Reverse().ToList(), group, atTime, resizeFactor, vectorExportConfig);
+                AddElements(folderNodeViewModel.Children.Where(x => x.IsVisibleBindable).Reverse().ToList(), group,
+                    atTime, resizeFactor, vectorExportConfig, exportSize);
                 elementContainer.Children.Add(group);
             }
 
@@ -115,34 +118,46 @@ internal partial class DocumentViewModel
             }
             else if (member is IVectorLayerHandler vectorLayerHandler)
             {
-                AddSvgShape(elementContainer, vectorLayerHandler, resizeFactor);
+                AddSvgShape(elementContainer, vectorLayerHandler, resizeFactor, exportSize);
             }
         }
     }
 
     private void AddSvgShape(IElementContainer elementContainer, IVectorLayerHandler vectorLayerHandler,
-        VecD resizeFactor)
+        VecD resizeFactor, VecI exportSize)
     {
         IReadOnlyVectorNode vectorNode =
             (IReadOnlyVectorNode)Internals.Tracker.Document.FindNode(vectorLayerHandler.Id);
-
         SvgElement? elementToAdd = null;
 
         if (vectorNode.ShapeData is IReadOnlyEllipseData ellipseData)
         {
-            elementToAdd = AddEllipse(resizeFactor, ellipseData);
+            elementToAdd = AddEllipse(ellipseData);
         }
         else if (vectorNode.ShapeData is IReadOnlyRectangleData rectangleData)
         {
-            elementToAdd = AddRectangle(resizeFactor, rectangleData);
+            elementToAdd = AddRectangle(rectangleData);
         }
         else if (vectorNode.ShapeData is IReadOnlyLineData lineData)
         {
-            elementToAdd = AddLine(resizeFactor, lineData);
+            elementToAdd = AddLine(lineData);
         }
         else if (vectorNode.ShapeData is IReadOnlyPathData shapeData)
         {
-            elementToAdd = AddVectorPath(resizeFactor, shapeData);
+            elementToAdd = AddVectorPath(shapeData);
+        }
+
+        if (vectorNode.ShapeData != null)
+        {
+            IReadOnlyShapeVectorData data = vectorNode.ShapeData;
+
+            if (data != null && elementToAdd is SvgPrimitive primitive)
+            {
+                Matrix3X3 transform = data.TransformationMatrix;
+
+                transform = transform.PostConcat(Matrix3X3.CreateScale((float)resizeFactor.X, (float)resizeFactor.Y));
+                primitive.Transform.Unit = new SvgTransformUnit?(new SvgTransformUnit(transform));
+            }
         }
 
         if (elementToAdd != null)
@@ -151,70 +166,67 @@ internal partial class DocumentViewModel
         }
     }
 
-    private static SvgLine AddLine(VecD resizeFactor, IReadOnlyLineData lineData)
+    private static SvgLine AddLine(IReadOnlyLineData lineData)
     {
         SvgLine line = new SvgLine();
-        line.X1.Unit = SvgNumericUnit.FromUserUnits(lineData.Start.X * resizeFactor.X);
-        line.Y1.Unit = SvgNumericUnit.FromUserUnits(lineData.Start.Y * resizeFactor.Y);
-        line.X2.Unit = SvgNumericUnit.FromUserUnits(lineData.End.X * resizeFactor.X);
-        line.Y2.Unit = SvgNumericUnit.FromUserUnits(lineData.End.Y * resizeFactor.Y);
+        line.X1.Unit = SvgNumericUnit.FromUserUnits(lineData.Start.X);
+        line.Y1.Unit = SvgNumericUnit.FromUserUnits(lineData.Start.Y);
+        line.X2.Unit = SvgNumericUnit.FromUserUnits(lineData.End.X);
+        line.Y2.Unit = SvgNumericUnit.FromUserUnits(lineData.End.Y);
 
         line.Stroke.Unit = SvgColorUnit.FromRgba(lineData.StrokeColor.R, lineData.StrokeColor.G,
             lineData.StrokeColor.B, lineData.StrokeColor.A);
         line.StrokeWidth.Unit = SvgNumericUnit.FromUserUnits(lineData.StrokeWidth);
-        line.Transform.Unit = new SvgTransformUnit(lineData.TransformationMatrix);
-
+        
         return line;
     }
 
-    private static SvgEllipse AddEllipse(VecD resizeFactor, IReadOnlyEllipseData ellipseData)
+    private static SvgEllipse AddEllipse(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.Cx.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Center.X);
+        ellipse.Cy.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Center.Y);
+        ellipse.Rx.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Radius.X);
+        ellipse.Ry.Unit = SvgNumericUnit.FromUserUnits(ellipseData.Radius.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)
+    private SvgRectangle AddRectangle(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);
+
+        float centerX = (float)rectangleData.Center.X;
+        float centerY = (float)rectangleData.Center.Y;
+        float halfWidth = (float)rectangleData.Size.X / 2f;
+        float halfHeight = (float)rectangleData.Size.Y / 2f;
+
+        rect.X.Unit = SvgNumericUnit.FromUserUnits(centerX - halfWidth);
+        rect.Y.Unit = SvgNumericUnit.FromUserUnits(centerY - halfHeight);
+
+        rect.Width.Unit = SvgNumericUnit.FromUserUnits(rectangleData.Size.X);
+        rect.Height.Unit = SvgNumericUnit.FromUserUnits(rectangleData.Size.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 SvgPath AddVectorPath(VecD resizeFactor, IReadOnlyPathData data)
+    private SvgPath AddVectorPath(IReadOnlyPathData data)
     {
         var path = new SvgPath();
         if (data.Path != null)
         {
             string pathData = data.Path.ToSvgPathData();
             path.PathData.Unit = new SvgStringUnit(pathData);
-            Matrix3X3 transform = data.TransformationMatrix;
-            transform = transform.PostConcat(Matrix3X3.CreateScale((float)resizeFactor.X, (float)resizeFactor.Y));
-            path.Transform.Unit = new SvgTransformUnit?(new SvgTransformUnit(transform));
 
             path.Fill.Unit =
                 SvgColorUnit.FromRgba(data.FillColor.R, data.FillColor.G, data.FillColor.B, data.FillColor.A);

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

@@ -9,11 +9,19 @@ namespace PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 internal sealed class SizeSettingViewModel : Setting<double>
 {
     private bool isEnabled = true;
-    public SizeSettingViewModel(string name, string label = null)
+    private double min;
+    private double max;
+    private int decimalPlaces;
+    
+    public SizeSettingViewModel(string name, string label = null, double min = 1, double max = double.PositiveInfinity, int decimalPlaces = 0)
         : base(name)
     {
         Label = label;
         Value = 1;
+        
+        this.min = min;
+        this.max = max;
+        this.decimalPlaces = decimalPlaces;
     }
 
     public bool IsEnabled
@@ -24,4 +32,31 @@ internal sealed class SizeSettingViewModel : Setting<double>
             SetProperty(ref isEnabled, value);
         }
     }
+    
+    public double Min
+    {
+        get => min;
+        set
+        {
+            SetProperty(ref min, value);
+        }
+    }
+    
+    public double Max
+    {
+        get => max;
+        set
+        {
+            SetProperty(ref max, value);
+        }
+    }
+    
+    public int DecimalPlaces
+    {
+        get => decimalPlaces;
+        set
+        {
+            SetProperty(ref decimalPlaces, value);
+        }
+    }
 }

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

@@ -53,7 +53,7 @@ internal class ShapeToolbar : Toolbar, IShapeToolbar
 
     public ShapeToolbar()
     {
-        AddSetting(new SizeSettingViewModel(nameof(ToolSize), "STROKE_THICKNESS_LABEL"));
+        AddSetting(new SizeSettingViewModel(nameof(ToolSize), "STROKE_THICKNESS_LABEL", 0, decimalPlaces: 2));
         AddSetting(new ColorSettingViewModel(nameof(StrokeColor), "STROKE_COLOR_LABEL"));
         AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_LABEL")
         {

+ 1 - 1
src/PixiEditor/Views/Input/SizeInput.axaml

@@ -32,7 +32,7 @@
                      BorderThickness="0" Background="Transparent"
                      Foreground="{Binding Foreground, ElementName=uc}" Focusable="True"
                      Margin="0,0,5,0" VerticalAlignment="Center"
-                     Decimals="0"
+                     Decimals="{Binding Decimals, RelativeSource={RelativeSource FindAncestor, AncestorType=input:SizeInput}}"
                      x:Name="input"
                      Value="{Binding Size, RelativeSource={RelativeSource FindAncestor, AncestorType=input:SizeInput}, Mode=TwoWay}"
                      Min="{Binding MinSize, RelativeSource={RelativeSource FindAncestor, AncestorType=input:SizeInput}}"

+ 13 - 4
src/PixiEditor/Views/Input/SizeInput.axaml.cs

@@ -10,8 +10,8 @@ namespace PixiEditor.Views.Input;
 /// </summary>
 internal partial class SizeInput : UserControl
 {
-    public static readonly StyledProperty<int> SizeProperty =
-        AvaloniaProperty.Register<SizeInput, int>(nameof(Size), defaultValue: 1);
+    public static readonly StyledProperty<double> SizeProperty =
+        AvaloniaProperty.Register<SizeInput, double>(nameof(Size), defaultValue: 1);
 
     public static readonly StyledProperty<int> MinSizeProperty = AvaloniaProperty.Register<SizeInput, int>(
         nameof(MinSize), defaultValue: 1);
@@ -28,6 +28,15 @@ internal partial class SizeInput : UserControl
     public static readonly StyledProperty<bool> FocusNextProperty = AvaloniaProperty.Register<SizeInput, bool>(
         nameof(FocusNext), defaultValue: true);
 
+    public static readonly StyledProperty<int> DecimalsProperty = AvaloniaProperty.Register<SizeInput, int>(
+        nameof(Decimals), defaultValue: 0);
+
+    public int Decimals
+    {
+        get => GetValue(DecimalsProperty);
+        set => SetValue(DecimalsProperty, value);
+    }
+
     public bool FocusNext
     {
         get => GetValue(FocusNextProperty);
@@ -43,9 +52,9 @@ internal partial class SizeInput : UserControl
     public static readonly StyledProperty<Action> OnScrollActionProperty =
         AvaloniaProperty.Register<SizeInput, Action>(nameof(OnScrollAction));
 
-    public int Size
+    public double Size
     {
-        get => (int)GetValue(SizeProperty);
+        get => (double)GetValue(SizeProperty);
         set => SetValue(SizeProperty, value);
     }
 

+ 5 - 1
src/PixiEditor/Views/Tools/ToolSettings/Settings/SizeSettingView.axaml

@@ -10,6 +10,10 @@
         <settings:SizeSettingViewModel/>
     </Design.DataContext>
     
-    <input:SizeInput VerticalAlignment="Center" MaxSize="9999" IsEnabled="{Binding IsEnabled}" FocusNext="False"
+    <input:SizeInput VerticalAlignment="Center"
+                     MaxSize="{Binding Max}" 
+                     MinSize="{Binding Min}"
+                     Decimals="{Binding DecimalPlaces}"
+                     IsEnabled="{Binding IsEnabled}" FocusNext="False"
                      Size="{Binding Value, Mode=TwoWay}"/>
 </UserControl>