2
0
Эх сурвалжийг харах

Absolute units conversion

Krzysztof Krysiński 5 сар өмнө
parent
commit
da95bcf710

+ 35 - 15
src/PixiEditor.SVG/Units/SvgNumericUnit.cs

@@ -6,32 +6,51 @@ public struct SvgNumericUnit(double value, string postFix) : ISvgUnit
 {
     public string PostFix { get; set; } = postFix;
     public double Value { get; set; } = value;
+    public double? PixelsValue => ConvertTo(SvgNumericUnits.Px);
+
+    public double? ConvertTo(SvgNumericUnits other)
+    {
+        SvgNumericUnits? numericUnit = SvgNumericUnitsExtensions.TryParseUnit(PostFix);
+
+        if (numericUnit == null || !numericUnit.Value.IsSizeUnit() || !numericUnit.Value.IsAbsoluteUnit())
+        {
+            return null;
+        }
+
+        double? pixelsValue = SvgNumericConverter.ToPixels(Value, numericUnit.Value);
+        if (pixelsValue == null)
+        {
+            return null;
+        }
+
+        return SvgNumericConverter.FromPixels(pixelsValue.Value, other);
+    }
 
     public static SvgNumericUnit FromUserUnits(double value)
     {
         return new SvgNumericUnit(value, string.Empty);
     }
-    
+
     public static SvgNumericUnit FromPixels(double value)
     {
         return new SvgNumericUnit(value, "px");
     }
-    
+
     public static SvgNumericUnit FromInches(double value)
     {
         return new SvgNumericUnit(value, "in");
     }
-    
+
     public static SvgNumericUnit FromCentimeters(double value)
     {
         return new SvgNumericUnit(value, "cm");
     }
-    
+
     public static SvgNumericUnit FromMillimeters(double value)
     {
         return new SvgNumericUnit(value, "mm");
     }
-    
+
     public static SvgNumericUnit FromPercent(double value)
     {
         return new SvgNumericUnit(value, "%");
@@ -46,7 +65,7 @@ public struct SvgNumericUnit(double value, string postFix) : ISvgUnit
     public void ValuesFromXml(string readerValue)
     {
         string? extractedPostFix = ExtractPostFix(readerValue);
-        
+
         if (extractedPostFix == null)
         {
             if (double.TryParse(readerValue, NumberStyles.Any, CultureInfo.InvariantCulture, out double result))
@@ -65,7 +84,7 @@ public struct SvgNumericUnit(double value, string postFix) : ISvgUnit
             }
         }
     }
-    
+
     private string? ExtractPostFix(string readerValue)
     {
         if (readerValue.Length == 0)
@@ -74,21 +93,22 @@ public struct SvgNumericUnit(double value, string postFix) : ISvgUnit
         }
 
         int postFixStartIndex = readerValue.Length;
-        
+
+        if (char.IsDigit(readerValue[^1]))
+        {
+            return null;
+        }
+
         for (int i = readerValue.Length - 1; i >= 0; i--)
         {
-            if (!char.IsDigit(readerValue[i]) && readerValue[i] != '.' && readerValue[i] != '-')
+            if (char.IsDigit(readerValue[i]))
             {
                 postFixStartIndex = i + 1;
                 break;
             }
         }
-        
-        if (postFixStartIndex == readerValue.Length)
-        {
-            return null;
-        }
-        
+
+
         return readerValue.Substring(postFixStartIndex);
     }
 }

+ 142 - 0
src/PixiEditor.SVG/Units/SvgNumericUnits.cs

@@ -0,0 +1,142 @@
+namespace PixiEditor.SVG.Units;
+
+public enum SvgNumericUnits
+{
+    Px,
+    In,
+    Cm,
+    Mm,
+    Pt,
+    Pc,
+    Em,
+    Ex,
+    Ch,
+    Rem,
+    Vw,
+    Vh,
+    Vmin,
+    Vmax,
+    Percent,
+    Deg,
+    Rad,
+    Grad,
+    Turn,
+    S,
+    Ms,
+    Min,
+    H,
+    Mmss,
+    Hhmmss,
+}
+
+public static class SvgNumericConverter
+{
+    public static double? ToPixels(double value, SvgNumericUnits unit)
+    {
+        if (!unit.IsAbsoluteUnit() && !unit.IsSizeUnit()) return null;
+
+        return unit switch
+        {
+            SvgNumericUnits.Px => value,
+            SvgNumericUnits.In => value * 96,
+            SvgNumericUnits.Cm => value * 37.795,
+            SvgNumericUnits.Mm => value * 3.7795,
+            SvgNumericUnits.Pt => value * 1.3333,
+            SvgNumericUnits.Pc => value * 16,
+            _ => null,
+        };
+    }
+
+    public static double? FromPixels(double pixelsValue, SvgNumericUnits other)
+    {
+        if (!other.IsAbsoluteUnit() && !other.IsSizeUnit()) return null;
+
+        return other switch
+        {
+            SvgNumericUnits.Px => pixelsValue,
+            SvgNumericUnits.In => pixelsValue / 96,
+            SvgNumericUnits.Cm => pixelsValue / 37.795,
+            SvgNumericUnits.Mm => pixelsValue / 3.7795,
+            SvgNumericUnits.Pt => pixelsValue / 1.3333,
+            SvgNumericUnits.Pc => pixelsValue / 16,
+            _ => null,
+        };
+    }
+}
+
+public static class SvgNumericUnitsExtensions
+{
+    public static bool IsSizeUnit(this SvgNumericUnits unit)
+    {
+        return unit switch
+        {
+            SvgNumericUnits.Px => true,
+            SvgNumericUnits.In => true,
+            SvgNumericUnits.Cm => true,
+            SvgNumericUnits.Mm => true,
+            SvgNumericUnits.Pt => true,
+            SvgNumericUnits.Pc => true,
+            SvgNumericUnits.Em => true,
+            SvgNumericUnits.Ex => true,
+            SvgNumericUnits.Ch => true,
+            SvgNumericUnits.Rem => true,
+            SvgNumericUnits.Vw => true,
+            SvgNumericUnits.Vh => true,
+            SvgNumericUnits.Vmin => true,
+            SvgNumericUnits.Vmax => true,
+            SvgNumericUnits.Percent => true,
+            _ => false,
+        };
+    }
+
+    public static bool IsAbsoluteUnit(this SvgNumericUnits unit)
+    {
+        return unit switch
+        {
+            SvgNumericUnits.Px => true,
+            SvgNumericUnits.In => true,
+            SvgNumericUnits.Cm => true,
+            SvgNumericUnits.Mm => true,
+            SvgNumericUnits.Pt => true,
+            SvgNumericUnits.Pc => true,
+            SvgNumericUnits.Rad => true,
+            SvgNumericUnits.Deg => true,
+            SvgNumericUnits.Grad => true,
+            _ => false,
+        };
+    }
+
+    public static SvgNumericUnits? TryParseUnit(string postFix)
+    {
+        if (string.IsNullOrWhiteSpace(postFix)) return SvgNumericUnits.Px;
+        return postFix.ToLower().Trim() switch
+        {
+            "px" => SvgNumericUnits.Px,
+            "in" => SvgNumericUnits.In,
+            "cm" => SvgNumericUnits.Cm,
+            "mm" => SvgNumericUnits.Mm,
+            "pt" => SvgNumericUnits.Pt,
+            "pc" => SvgNumericUnits.Pc,
+            "em" => SvgNumericUnits.Em,
+            "ex" => SvgNumericUnits.Ex,
+            "ch" => SvgNumericUnits.Ch,
+            "rem" => SvgNumericUnits.Rem,
+            "vw" => SvgNumericUnits.Vw,
+            "vh" => SvgNumericUnits.Vh,
+            "vmin" => SvgNumericUnits.Vmin,
+            "vmax" => SvgNumericUnits.Vmax,
+            "%" => SvgNumericUnits.Percent,
+            "deg" => SvgNumericUnits.Deg,
+            "rad" => SvgNumericUnits.Rad,
+            "grad" => SvgNumericUnits.Grad,
+            "turn" => SvgNumericUnits.Turn,
+            "s" => SvgNumericUnits.S,
+            "ms" => SvgNumericUnits.Ms,
+            "min" => SvgNumericUnits.Min,
+            "h" => SvgNumericUnits.H,
+            "mm:ss" => SvgNumericUnits.Mmss,
+            "hh:mm:ss" => SvgNumericUnits.Hhmmss,
+            _ => null,
+        };
+    }
+}

+ 1 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -282,7 +282,7 @@
   "EDITOR_DATA": "Editor data (Local)",
   "MOVE_VIEWPORT_TOOLTIP": "Moves viewport. ({0})",
   "MOVE_VIEWPORT_ACTION_DISPLAY": "Click and move to pan the viewport",
-  "MOVE_TOOL_TOOLTIP": "Moves selected pixels ({0}). Hold Ctrl to move all layers.",
+  "MOVE_TOOL_TOOLTIP": "Select and transform layers ({0}).",
   "MOVE_TOOL_ACTION_DISPLAY": "Hold mouse to move selected pixels. Hold Ctrl to move all layers.",
   "PEN_TOOL_TOOLTIP": "Pen. ({0})",
   "PEN_TOOL_ACTION_DISPLAY": "Click and move to draw.",

+ 13 - 13
src/PixiEditor/Models/IO/CustomDocumentFormats/SvgDocumentBuilder.cs

@@ -176,15 +176,15 @@ internal class SvgDocumentBuilder : IDocumentBuilder
         if (element is SvgCircle circle)
         {
             return new EllipseVectorData(
-                new VecD(circle.Cx.Unit?.Value ?? 0, circle.Cy.Unit?.Value ?? 0),
-                new VecD(circle.R.Unit?.Value ?? 0, circle.R.Unit?.Value ?? 0));
+                new VecD(circle.Cx.Unit?.PixelsValue ?? 0, circle.Cy.Unit?.PixelsValue ?? 0),
+                new VecD(circle.R.Unit?.PixelsValue ?? 0, circle.R.Unit?.PixelsValue ?? 0));
         }
 
         if (element is SvgEllipse ellipse)
         {
             return new EllipseVectorData(
-                new VecD(ellipse.Cx.Unit?.Value ?? 0, ellipse.Cy.Unit?.Value ?? 0),
-                new VecD(ellipse.Rx.Unit?.Value ?? 0, ellipse.Ry.Unit?.Value ?? 0));
+                new VecD(ellipse.Cx.Unit?.PixelsValue ?? 0, ellipse.Cy.Unit?.PixelsValue ?? 0),
+                new VecD(ellipse.Rx.Unit?.PixelsValue ?? 0, ellipse.Ry.Unit?.PixelsValue ?? 0));
         }
 
         return null;
@@ -193,8 +193,8 @@ internal class SvgDocumentBuilder : IDocumentBuilder
     private LineVectorData AddLine(SvgLine element)
     {
         return new LineVectorData(
-            new VecD(element.X1.Unit?.Value ?? 0, element.Y1.Unit?.Value ?? 0),
-            new VecD(element.X2.Unit?.Value ?? 0, element.Y2.Unit?.Value ?? 0));
+            new VecD(element.X1.Unit?.PixelsValue ?? 0, element.Y1.Unit?.PixelsValue ?? 0),
+            new VecD(element.X2.Unit?.PixelsValue ?? 0, element.Y2.Unit?.PixelsValue ?? 0));
     }
 
     private PathVectorData AddPath(SvgPath element, StyleContext styleContext)
@@ -234,8 +234,8 @@ internal class SvgDocumentBuilder : IDocumentBuilder
     private RectangleVectorData AddRect(SvgRectangle element)
     {
         return new RectangleVectorData(
-            element.X.Unit?.Value ?? 0, element.Y.Unit?.Value ?? 0,
-            element.Width.Unit?.Value ?? 0, element.Height.Unit?.Value ?? 0);
+            element.X.Unit?.PixelsValue ?? 0, element.Y.Unit?.PixelsValue ?? 0,
+            element.Width.Unit?.PixelsValue ?? 0, element.Height.Unit?.PixelsValue ?? 0);
     }
 
     private TextVectorData AddText(SvgText element)
@@ -248,15 +248,15 @@ internal class SvgDocumentBuilder : IDocumentBuilder
             missingFont = new FontFamilyName(element.FontFamily.Unit.Value.Value);
         }
 
-        font.Size = element.FontSize.Unit?.Value ?? 12;
+        font.Size = element.FontSize.Unit?.PixelsValue ?? 12;
         font.Bold = element.FontWeight.Unit?.Value == SvgFontWeight.Bold;
         font.Italic = element.FontStyle.Unit?.Value == SvgFontStyle.Italic;
 
         return new TextVectorData(element.Text.Unit.Value.Value)
         {
             Position = new VecD(
-                element.X.Unit?.Value ?? 0,
-                element.Y.Unit?.Value ?? 0),
+                element.X.Unit?.PixelsValue ?? 0,
+                element.Y.Unit?.PixelsValue ?? 0),
             Font = font,
             MissingFontFamily = missingFont,
             MissingFontText = "MISSING_FONT"
@@ -271,7 +271,7 @@ internal class SvgDocumentBuilder : IDocumentBuilder
         }
 
         bool hasFill = styleContext.Fill.Unit is { Color.A: > 0 };
-        bool hasStroke = styleContext.Stroke.Unit is { Color.A: > 0 } || styleContext.StrokeWidth.Unit is { Value: > 0 };
+        bool hasStroke = styleContext.Stroke.Unit is { Color.A: > 0 } || styleContext.StrokeWidth.Unit is { PixelsValue: > 0 };
         bool hasTransform = styleContext.Transform.Unit is { MatrixValue.IsIdentity: false };
 
         shapeData.Fill = hasFill;
@@ -287,7 +287,7 @@ internal class SvgDocumentBuilder : IDocumentBuilder
             var targetWidth = styleContext.StrokeWidth.Unit;
 
             shapeData.StrokeColor = targetColor?.Color ?? Colors.Black;
-            shapeData.StrokeWidth = (float)(targetWidth?.Value ?? 1);
+            shapeData.StrokeWidth = (float)(targetWidth?.PixelsValue ?? 1);
         }
 
         if (hasTransform)