首页 文章

使用System.Drawing.Drawing2D中的Matrix类来展平SVG转换

提问于
浏览
0

我有SVG文档,其中包含带有MATRIX / SCALE / TRANSLATE转换的RECT标记:

<?xml version="1.0" encoding="UTF-8"?>
    <svg
       xmlns:svg="http://www.w3.org/2000/svg"
       xmlns="http://www.w3.org/2000/svg"
       version="1.0"
       width="300"
       height="150"
       id="svg2">
      <rect
         width="70"
         height="30"
         x="169.46515"
         y="46.238182"
         id="rect1"
         style="fill:none;stroke:#000000;stroke-opacity:1" />
      <rect
         width="8"
         height="2"
         x="134.69983"
         y="97.45443"
         id="rect2"
         style="fill:none;stroke:#000000;stroke-opacity:1" />
      <rect
         width="2"
         height="2"
         x="-99.804573"
         y="125.0692"
         transform="matrix(-2.6042412e-3,-0.9999966,0.9999966,-2.6042412e-3,0,0)"
         id="rect3"
         style="fill:none;stroke:#ff0000;stroke-width:1;stroke-opacity:1" />
      <rect
         width="8"
         height="2"
         x="-100.87854"
         y="129.95743"
         transform="matrix(-6.2857322e-4,-0.9999998,0.9999418,-1.0789073e-2,0,0)"
         id="rect4"
         style="fill:none;stroke:#ff0000;stroke-width:1;stroke-opacity:1" />
      <rect
         width="2"
         height="8"
         x="-99.81971"
         y="134.43823"
         transform="matrix(-2.6042412e-3,-0.9999966,0.9999966,-2.6042412e-3,0,0)"
         id="rect5"
         style="fill:none;stroke:#ff0000;stroke-width:1;stroke-opacity:1" />
      <path
         d="M 82.592797,67.016361 C 82.597697,77.923711 76.726297,86.769781 69.481867,86.769781 C 62.237427,86.769781 56.366027,77.923711 56.370927,67.016361 C 56.366027,56.109011 62.237427,47.262941 69.481867,47.262941 C 76.726297,47.262941 82.597697,56.109011 82.592797,67.016361 z"
         id="path22095"
         style="fill:none;stroke:#0000ff;stroke-opacity:1" />
      <rect
         width="30"
         height="70"
         x="46.304344"
         y="-239.40808"
         transform="matrix(0,1,-1,0,0,0)"
         id="rect6"
         style="fill:none;stroke:#ff0000;stroke-opacity:1" />
      <rect
         width="2"
         height="2"
         x="121.03672"
         y="97.452477"
         id="rect7"
         style="fill:none;stroke:#000000;stroke-opacity:1" />
    </svg>

我想要展平/变换坐标并完全摆脱TRANSFORM属性 .

似乎System.Drawing.Drawing2D.Matrix类具有所有必需的方法,但由于某种原因,结果看起来不正确 . 以下是我使用的代码:

PointF[] coordinates = 
            {
                new PointF(float.Parse(element.Attribute("x").Value), float.Parse(element.Attribute("y").Value))
            };    

var matrix = new Matrix(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]);
matrix.TransformPoints(coordinates);

element.Attribute("x").Value = coordinates[0].X.ToString();
element.Attribute("y").Value = coordinates[0].Y.ToString();

至少在RECT的情况下,它似乎正确地计算位置,但因为宽度/高度保持不变,所以它看起来不同 . 当我尝试将TransformPoints应用于宽度/高度时,它也没有帮助 .

所以我的问题是,是否有可能以某种方式找到原始坐标?

更新

正如@Cimbali建议我尝试使用以下代码,但结果仍然看起来不正确:

var matrix = new Matrix(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]);
matrix.TransformPoints(coordinates);

var a = Math.Sign(transform[0]) * Math.Sqrt(transform[0] * transform[0] + transform[2] * transform[2]);
var b = Math.Sign(transform[3]) * Math.Sqrt(transform[1] * transform[1] + transform[3] * transform[3]);
var angle = Math.Atan2(transform[1], transform[3]) * 180 / Math.PI;
var newWidth = double.Parse(element.Attribute("width").Value) * a;
var newHeight = double.Parse(element.Attribute("height").Value) * b;

element.Attribute("x").Value = Math.Round(coordinates[0].X, coordinatesPrecision, MidpointRounding.AwayFromZero).ToString();
element.Attribute("y").Value = Math.Round(coordinates[0].Y, coordinatesPrecision, MidpointRounding.AwayFromZero).ToString();
element.Attribute("width").Value = newWidth.ToString();
element.Attribute("height").Value = newHeight.ToString();

if (angle != 0)
    element.Attribute("transform").Value = string.Format("rotate({0})", angle);

谢谢

1 回答

  • 1

    问题来自旋转或倾斜,或这些操作 .

    移动或缩放矩形时,它仍然是一个矩形,因此您可以修改其坐标以包含这些效果,并通过仅修改svg中 <rect /> 的属性将其从转换中删除 . 然而,其他变换使它不再是一个矩形,或者至少不是一个边与 xy 轴平行的变形 .

    有两种主要方法可以解决这个问题:

    • 在你的svg中保留一个 transform="rotate(angle)" ,并且最终 skew 也是如此,如果你使用它们的话 . 为此,您将不得不在不同(乘法)元素中分解矩阵,这将是非平凡的,并且删除缩放和转换 .

    • 使用 <polygon /> 而不是 <rect /> . 这是更简单的选择 .

    后期选项:多边形

    我们需要矩形的4个角度: {(x,y), (x+width,y), (x+width,y+height), (x, y+height)}

    现在,您可以将变换矩阵应用于所有这些点,就像您只为 (x,y) 点所做的那样,并使用它们(保持顺序)绘制多边形 .

    前选项:继续旋转

    在这个版本中,我们保留 rect ,并尝试将其 transform 属性从矩阵简化为简单旋转 . 我假设你只旋转,翻译和缩放你的矩阵,并且我已经尝试了一段时间但没有结论 .

    然后,变换矩阵A以列主要顺序写为其第2行(因为第3行总是 0 0 1 ) . 因此,由于它包括缩放(沿x乘 a 并沿y乘 b ),然后旋转某个角度,其中 c = cos(angle)s = sin(angle) ,它被写成:

    | a*c  -a*s  tx |   | transform[0] transform[2] transform[4] |
    A = | b*s   b*c  ty | = | transform[1] transform[3] transform[5] |
        |  0     0    1 |   |      0            0            1       |
    

    所以from here为这个转换提供了一个伪代码:

    • transform[4] 添加到 <rect />x 属性

    • transform[5] 添加到 <rect />y 属性

    • a = sqrt( transform[0] * transform[0] + transform[2] * transform[2] )

    • b = sqrt( transform[1] * transform[1] + transform[3] * transform[3] )

    • angle = atan2(transform[1], transform[3]) * 180/PI

    • 将矩形的宽度乘以 a ,将其高度乘以 b

    • 最后,将 <rect />transform 属性设置为 "rotate(angle)" (如果 angle 非零),如果 angle 为零,则将其删除 .

    从更新中获取并更正代码(因为我不自己做C#),我们得到:

    element.Attribute("x").Value = Math.Round(coordinates[0].X + transform[4], coordinatesPrecision, MidpointRounding.AwayFromZero).ToString();
    element.Attribute("y").Value = Math.Round(coordinates[0].Y + transform[5], coordinatesPrecision, MidpointRounding.AwayFromZero).ToString();
    
    var a = Math.Sqrt(transform[0] * transform[0] + transform[2] * transform[2]);
    var b = Math.Sqrt(transform[1] * transform[1] + transform[3] * transform[3]);
    var angle = - Math.Atan2(transform[1], transform[3]) * 180 / Math.PI;
    var newWidth = double.Parse(element.Attribute("width").Value) * a;
    var newHeight = double.Parse(element.Attribute("height").Value) * b;
    
    element.Attribute("width").Value = newWidth.ToString();
    element.Attribute("height").Value = newHeight.ToString();
    
    if (angle != 0)
        element.Attribute("transform").Value = string.Format("rotate({0})", angle);
    else
        element.RemoveAttribute("transform");
    

    另一种方法是围绕 <rect /> 而不是 (0,0) 的中心进行旋转,首先将变换应用于坐标,因此如下所示:

    PointF[] coordinates = { new PointF(
        float.Parse(element.Attribute("x").Value),
        float.Parse(element.Attribute("y").Value)
    )};
    
    var matrix = new Matrix(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]);
    matrix.TransformPoints(coordinates);
    
    element.Attribute("x").Value = coordinates[0].X.ToString();
    element.Attribute("y").Value = coordinates[0].Y.ToString();
    
    var a = Math.Sqrt(transform[0] * transform[0] + transform[2] * transform[2]);
    var b = Math.Sqrt(transform[1] * transform[1] + transform[3] * transform[3]);
    var angle = - Math.Atan2(transform[1], transform[3]) * 180 / Math.PI;
    var newWidth = double.Parse(element.Attribute("width").Value) * a;
    var newHeight = double.Parse(element.Attribute("height").Value) * b;
    
    element.Attribute("width").Value = newWidth.ToString();
    element.Attribute("height").Value = newHeight.ToString();
    
    if (angle != 0)
        element.Attribute("transform").Value = string.Format("rotate({0}, {1}, {2})", angle, coordinates[0].X + newWidth/2, coordinates[0].Y + newHeight/2);
    else
        element.RemoveAttribute("transform");
    

相关问题