C# 使用System.Drawing.Drawing2D中的矩阵类展平SVG转换

C# 使用System.Drawing.Drawing2D中的矩阵类展平SVG转换,c#,.net,math,matrix,svg,C#,.net,Math,Matrix,Svg,我有一个SVG文档,它具有带有矩阵/缩放/转换的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="sv

我有一个SVG文档,它具有带有矩阵/缩放/转换的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>
至少在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);

谢谢

问题来自旋转或倾斜,或此类操作

移动或缩放矩形时,它仍然是矩形,因此您可以修改其坐标以包含这些效果,并通过修改svg中的
属性将其从变换中删除。但是,其他变换使它不再是一个真正的矩形,或者至少不再是一个边平行于
x
y
轴的矩形

有两种主要的方法可以做到这一点:

  • 在svg中保持一个
    transform=“rotate(angle)”
    ,如果您使用它们,最终也会保持
    skew
    s。 为此,您必须将矩阵分解为不同的(乘法)元素,这将是非常重要的,并消除缩放和转换
  • 使用
    而不是
    。这是更简单的选择
后一个选项:多边形 我们需要矩形的4个角:
{(x,y),(x+宽度,y),(x+宽度,y+高度),(x,y+高度)}

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

前一种选择:保持旋转 在这个版本中,我们保留了
rect
,并尝试将其
变换
属性从矩阵简化为简单的旋转。我假设你只旋转、平移和缩放你的矩阵,没有扭曲(我已经尝试了一段时间,但没有任何结论)

然后将变换矩阵A作为其前两行(因为第三行始终是
001
)按列大顺序写入。因此,由于它包括缩放(沿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       |
因此,这一转换产生了一个伪代码:

  • transform[4]
    添加到
    x
    属性中
  • transform[5]
    添加到
    y
    属性中
  • a=sqrt(变换[0]*变换[0]+变换[2]*变换[2])
  • b=sqrt(变换[1]*变换[1]+变换[3]*变换[3])
  • angle=atan2(变换[1],变换[3])*180/PI
  • 将矩形的宽度乘以
    a
    ,将矩形的高度乘以
    b
  • 最后,将
    transform
    属性设置为
    旋转(角度)
    (如果
    角度
    为非零),如果
    角度
    为零,则将其删除
从您的更新中获取并更正代码(因为我自己不做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");
另一种方法是,先将变换应用于坐标,从而使旋转围绕
的中心,而不是
(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");

谢谢,你说得对。没有注意到
矩阵
实际上也可以定义旋转。因此,如果我想保持它为
RECT
,是否有一些从
矩阵
新坐标
+
旋转
的直接转换?关于
TRANSLATE
SCALE
转换,这些转换可以应用和删除吗?您在上一篇评论中提供的坐标确实将
rect
放置在正确的位置。但有没有办法通过提供旋转中心使其更具可读性,就像下面这样:
。只是尝试了一下,但没有成功。据我所知,SVG
rotate
在未提供
x
y
的情况下使用(0,0)点进行旋转。如果我使用
x+newWidth/2,y+newHeight/2
则位置将不正确,因为
x
有一个负数。这有意义吗?Cimbali,我刚刚给你的答案做了标记,因为它确实回答了我的主要问题。为了简化事情,我删除了一些注释,但仍然希望按照注释中的建议获得坐标以使其可读。这可能吗?啊哈,因为正如我提到的,newX是
-99.804573
,旋转发生在
0,0
。所以,如果我们按照你的建议去做,它仍然是负面的,并且被放在错误的地方。实际上,您可以将
rect
放入SVG文件并在IE中打开以查看结果。
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");