Browse Source

Svg ellipse

flabbet 11 months ago
parent
commit
1c475976e6

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

@@ -0,0 +1,15 @@
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+public interface IReadOnlyShapeVectorData
+{
+    public Matrix3X3 TransformationMatrix { get; }
+    public Color StrokeColor { get; }
+    public Color FillColor { get; }
+    public int StrokeWidth { get; }
+    public RectD GeometryAABB { get; }
+    public RectD TransformedAABB { get; }
+}

+ 8 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyVectorNode.cs

@@ -0,0 +1,8 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+public interface IReadOnlyVectorNode : IReadOnlyLayerNode
+{
+    public IReadOnlyShapeVectorData? ShapeData { get; }
+}

+ 10 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyEllipseData.cs

@@ -0,0 +1,10 @@
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
+
+public interface IReadOnlyEllipseData : IReadOnlyShapeVectorData
+{
+    public VecD Center { get; }
+    public VecD Radius { get; }
+    public double RotationRadians { get; }
+}

+ 3 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs

@@ -1,10 +1,11 @@
-using PixiEditor.DrawingApi.Core.Surfaces;
+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 EllipseVectorData : ShapeVectorData
+public class EllipseVectorData : ShapeVectorData, IReadOnlyEllipseData
 {
     public VecD Radius { get; set; }
     public VecD Center { get; set; }

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

@@ -1,4 +1,5 @@
-using PixiEditor.Common;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.Common;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces;
@@ -6,7 +7,7 @@ using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 
-public abstract class ShapeVectorData : ICacheable, ICloneable
+public abstract class ShapeVectorData : ICacheable, ICloneable, IReadOnlyShapeVectorData
 {
     public Matrix3X3 TransformationMatrix { get; set; } = Matrix3X3.Identity; 
     

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs

@@ -10,7 +10,7 @@ using PixiEditor.Numerics;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 [NodeInfo("VectorLayer")]
-public class VectorLayerNode : LayerNode, ITransformableObject
+public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorNode
 {
     public Matrix3X3 TransformationMatrix
     {
@@ -27,6 +27,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject
     }
 
     public ShapeVectorData? ShapeData { get; set; }
+    IReadOnlyShapeVectorData IReadOnlyVectorNode.ShapeData => ShapeData;
 
     protected override bool AffectedByChunkResolution => true;
 

+ 1 - 1
src/PixiEditor.SVG/Elements/SvgGroup.cs

@@ -6,7 +6,7 @@ namespace PixiEditor.SVG.Elements;
 public class SvgGroup() : SvgElement("g"), ITransformable, IFillable, IStrokable, IElementContainer
 {
     public List<SvgElement> Children { get; } = new();
-    public SvgProperty<SvgTransform> Transform { get; } = new("transform");
+    public SvgProperty<SvgTransformUnit> Transform { get; } = new("transform");
     public SvgProperty<SvgColorUnit> Fill { get; } = new("fill");
     public SvgProperty<SvgColorUnit> Stroke { get; } = new("stroke");
     public SvgProperty<SvgNumericUnit> StrokeWidth { get; } = new("stroke-width");

+ 1 - 1
src/PixiEditor.SVG/Elements/SvgPrimitive.cs

@@ -5,7 +5,7 @@ namespace PixiEditor.SVG.Elements;
 
 public class SvgPrimitive(string tagName) : SvgElement(tagName), ITransformable, IFillable, IStrokable
 {
-    public SvgProperty<SvgTransform> Transform { get; } = new("transform");
+    public SvgProperty<SvgTransformUnit> Transform { get; } = new("transform");
     public SvgProperty<SvgColorUnit> Fill { get; } = new("fill");
     public SvgProperty<SvgColorUnit> Stroke { get; } = new("stroke");
     public SvgProperty<SvgNumericUnit> StrokeWidth { get; } = new("stroke-width");

+ 1 - 1
src/PixiEditor.SVG/Features/ITransformable.cs

@@ -4,5 +4,5 @@ namespace PixiEditor.SVG.Features;
 
 public interface ITransformable
 {
-    public SvgProperty<SvgTransform> Transform { get; }
+    public SvgProperty<SvgTransformUnit> Transform { get; }
 }

+ 1 - 0
src/PixiEditor.SVG/PixiEditor.SVG.csproj

@@ -7,6 +7,7 @@
     </PropertyGroup>
 
     <ItemGroup>
+      <ProjectReference Include="..\PixiEditor.DrawingApi.Core\PixiEditor.DrawingApi.Core.csproj" />
       <ProjectReference Include="..\PixiEditor.Numerics\PixiEditor.Numerics.csproj" />
     </ItemGroup>
 

+ 5 - 2
src/PixiEditor.SVG/Units/SvgNumericUnit.cs

@@ -1,4 +1,6 @@
-namespace PixiEditor.SVG.Units;
+using System.Globalization;
+
+namespace PixiEditor.SVG.Units;
 
 public struct SvgNumericUnit(double value, string postFix) : ISvgUnit
 {
@@ -37,6 +39,7 @@ public struct SvgNumericUnit(double value, string postFix) : ISvgUnit
 
     public string ToXml()
     {
-        return $"{Value}{PostFix}";
+        string invariantValue = Value.ToString(CultureInfo.InvariantCulture);
+        return $"{invariantValue}{PostFix}";
     }
 }

+ 0 - 16
src/PixiEditor.SVG/Units/SvgTransform.cs

@@ -1,16 +0,0 @@
-using System.Numerics;
-
-namespace PixiEditor.SVG.Units;
-
-public struct SvgTransform : ISvgUnit
-{
-    public SvgTransform()
-    {
-    }
-
-    public Matrix3x2 MatrixValue { get; set; } = Matrix3x2.Identity;
-    public string ToXml()
-    {
-        return $"matrix({MatrixValue.M11},{MatrixValue.M12},{MatrixValue.M21},{MatrixValue.M22},{MatrixValue.M31},{MatrixValue.M32})";
-    }
-}

+ 31 - 0
src/PixiEditor.SVG/Units/SvgTransformUnit.cs

@@ -0,0 +1,31 @@
+using System.Globalization;
+using System.Numerics;
+using PixiEditor.DrawingApi.Core.Numerics;
+
+namespace PixiEditor.SVG.Units;
+
+public struct SvgTransformUnit : ISvgUnit
+{
+    public SvgTransformUnit()
+    {
+    }
+
+    public Matrix3X3 MatrixValue { get; set; } = Matrix3X3.Identity;
+    
+    public SvgTransformUnit(Matrix3X3 matrixValue)
+    {
+        MatrixValue = matrixValue;
+    }
+    
+    public string ToXml()
+    {
+        string translateX = MatrixValue.TransX.ToString(CultureInfo.InvariantCulture);
+        string translateY = MatrixValue.TransY.ToString(CultureInfo.InvariantCulture);
+        string scaleX = MatrixValue.ScaleX.ToString(CultureInfo.InvariantCulture);
+        string scaleY = MatrixValue.ScaleY.ToString(CultureInfo.InvariantCulture);
+        string skewX = MatrixValue.SkewX.ToString(CultureInfo.InvariantCulture);
+        string skewY = MatrixValue.SkewY.ToString(CultureInfo.InvariantCulture);
+        
+        return $"matrix({scaleX}, {skewY}, {skewX}, {scaleY}, {translateX}, {translateY})";
+    }
+}

+ 11 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs

@@ -39,6 +39,8 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor
         if (!members.Any())
             return ExecutionState.Error;
 
+        bool allRaster = true; 
+
         memberCorners = new();
         foreach (IStructureMemberHandler member in members)
         {
@@ -48,6 +50,10 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor
             {
                 targetCorners = new ShapeCorners(document.SelectionPathBindable.TightBounds);
             }
+            else if(member is not IRasterLayerHandler)
+            {
+                allRaster = false;
+            }
             
             memberCorners.Add(member.Id, targetCorners);
         }
@@ -56,8 +62,11 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor
         
         if (masterCorners.AABBBounds.Width == 0 || masterCorners.AABBBounds.Height == 0)
             return ExecutionState.Error;
-        
-        document.TransformHandler.ShowTransform(DocumentTransformMode.Scale_Rotate_Shear_Perspective, true, masterCorners, Type == ExecutorType.Regular);
+
+        DocumentTransformMode mode = allRaster
+            ? DocumentTransformMode.Scale_Rotate_Shear_Perspective
+            : DocumentTransformMode.Scale_Rotate_Shear_NoPerspective;
+        document.TransformHandler.ShowTransform(mode, true, masterCorners, Type == ExecutorType.Regular);
         internals!.ActionAccumulator.AddActions(
             new TransformSelected_Action(masterCorners, tool.KeepOriginalImage, memberCorners, false, document.AnimationHandler.ActiveFrameBindable));
         return ExecutionState.Success;

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

@@ -11,7 +11,7 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 internal class VectorEllipseToolExecutor : ShapeToolExecutor<IVectorEllipseToolHandler>
 {
     public override ExecutorType Type => ExecutorType.ToolLinked;
-    protected override DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_Shear_Perspective;
+    protected override DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_Shear_NoPerspective;
 
     protected override void DrawShape(VecI curPos, double rotationRad, bool firstDraw)
     {

+ 6 - 0
src/PixiEditor/Models/Handlers/IVectorLayerHandler.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.Handlers;
+
+internal interface IVectorLayerHandler : ILayerHandler
+{
+    
+}

+ 27 - 1
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -8,6 +8,8 @@ using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.IO.FileEncoders;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Bridge;
@@ -35,6 +37,7 @@ using BlendMode = PixiEditor.DrawingApi.Core.Surfaces.BlendMode;
 using Color = System.Drawing.Color;
 using IKeyFrameChildrenContainer = PixiEditor.ChangeableDocument.Changeables.Interfaces.IKeyFrameChildrenContainer;
 using KeyFrameData = PixiEditor.Parser.KeyFrameData;
+using Node = PixiEditor.Parser.Graph.Node;
 using PixiDocument = PixiEditor.Parser.Document;
 using ReferenceLayer = PixiEditor.Parser.ReferenceLayer;
 
@@ -104,6 +107,30 @@ internal partial class DocumentViewModel
             {
                 AddSvgImage(elementContainer, atTime, member, resizeFactor);
             }
+            else if (member is IVectorLayerHandler vectorLayerHandler)
+            {
+                AddSvgShape(elementContainer, atTime, vectorLayerHandler, resizeFactor);
+            }
+        }
+    }
+
+    private void AddSvgShape(IElementContainer elementContainer, KeyFrameTime atTime, IVectorLayerHandler vectorLayerHandler, VecD resizeFactor)
+    {
+        IReadOnlyVectorNode vectorNode = (IReadOnlyVectorNode)Internals.Tracker.Document.FindNode(vectorLayerHandler.Id);
+
+        if (vectorNode.ShapeData is IReadOnlyEllipseData ellipseData)
+        {
+            SvgEllipse ellipse = new SvgEllipse();
+            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);
+            
+            elementContainer.Children.Add(ellipse);
         }
     }
 
@@ -127,7 +154,6 @@ internal partial class DocumentViewModel
             toSave = surface.DrawingSurface.Snapshot((RectI)tightBounds.Value);
         });
 
-        //var imgToSerialize = imageNode.GetLayerImageAtFrame(atTime.Frame);
         var image = CreateImageElement(resizeFactor, tightBounds.Value, toSave);
 
         elementContainer.Children.Add(image);

+ 1 - 1
src/PixiEditor/ViewModels/Document/Nodes/VectorLayerNodeViewModel.cs

@@ -6,7 +6,7 @@ using PixiEditor.ViewModels.Nodes;
 namespace PixiEditor.ViewModels.Document.Nodes;
 
 [NodeViewModel("VECTOR_LAYER", "STRUCTURE", "\ue916")]
-internal class VectorLayerNodeViewModel : StructureMemberViewModel<VectorLayerNode>, ILayerHandler
+internal class VectorLayerNodeViewModel : StructureMemberViewModel<VectorLayerNode>, IVectorLayerHandler
 {
     bool lockTransparency;
     public void SetLockTransparency(bool lockTransparency)