本文将介绍如何将OpenXml的actTo转为Svg的弧线(a)

OpenXml的artTo

首先下面是一段OpenXml的arcTo弧线

<arcTo wR="152403" hR="152403" stAng="cd4" swAng="-5400000" />

假设我们当前的点是(0,0),这时候我们已知的信息如下:

  • 当前点坐标:(x1,y1)=(0,0)
  • 椭圆的半径:半长轴 rx=wR=152403,半短轴 ry=hR=152403
  • 起始角到结束角的夹角:起始角θ1=stAng=cd4,夹角Δθ=swAng,结束角θ2=θ1+Δθ
  • 是否优(大)弧:fA=|Δθ|>Π(180°)
  • 顺逆时针:fS=|Δθ|>0°

目前Svg的椭圆弧线参数字符串为以下:

a  rx  ry  x-axis-rotation  large-arc-flag  sweep-flag  x  y

其中涉及到的参数:

参数 说明 备注
rx 椭圆半长轴 已知:rx=wR=152403
ry 椭圆半短轴 已知:ry=hR=152403
x-axis-rotation 椭圆相对于坐标系的旋转角度,角度数而非弧度数 已知:0
large-arc-flag 是否优(大)弧:0否,1是 已知:fA=|Δθ|>Π(180°)
sweep-flag 绘制方向:0逆时针,1顺时针 已知:fS=|Δθ|>0°
x 圆弧终点的x坐标 未知
y 圆弧终点的y坐标 未知

因此实际上,我们需要求出的则是圆弧终点坐标就能够完成最终换算到Svg椭圆弧线字符串了

求椭圆弧上任意一点的二维矩阵方程式

以下是我从W3C的SVG官方文档中获取到的关于椭圆任意一点的二维矩阵方程式:

因此的存在以下两个(开始点和终点)椭圆任意一点的二维矩阵方程式:

其中涉及到的参数:

参数 说明 备注
(x1,y1) 当前坐标 已知:(0,0)
(x2,y2) 终点坐标 未知
φ 椭圆相对于坐标系的旋转角度 已知:0°
θ1 起始角 已知:stAng
Δθ 起始角到结束角的夹角 已知:swAng
(cx,cy) 椭圆中心坐标点 未知
fA 是否优(大)弧 已知:fA=|Δθ|>Π(180°)
fS 绘制方向 已知:fS=Δθ>0°

因此推导公式如下:

步骤1:

因为开始点的椭圆任意一点的二维矩阵方程式为

所以能够得出两行一列矩阵CxCy为:

步骤2:

因为终点的椭圆任意一点的二维矩阵方程式为

因此将矩阵CxCy带入到终点点的椭圆任意一点的二维矩阵方程式:

代码部分

在写代码之前,我们需要安装一些所需要用到的库,Openxml单位换算为Pixel的库和矩阵运算用到的库:

通过nuget包的控制台执行以下命令:

Openxml单位换算库

Install-Package dotnetCampus.OpenXmlUnitConverter -Version 1.5.1

矩阵运算库

Install-Package MathNet.Numerics -Version 5.0.0-alpha02

然后正式开始我们的代码,我们通过WPF应用窗体来展示效果:

前端xaml代码:

<Window x:Class="OpenxmlActToSvgSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OpenxmlActToSvgSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Path x:Name="Path" Stroke="Blue" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>

后端cs代码:

        public MainWindow()
{
InitializeComponent(); //Openxml的360° circle
const double c = 21600000d;
//circle divide 4
var cd4 = c / 4; //<arcTo wR="152403" hR="152403" stAng="cd4" swAng="-5400000" />
var wR = 152403;
var hR = 152403;
var stAng = cd4;
var swAng = -5400000d; StringBuilder stringPath=new StringBuilder();
var currentPoint=new Point(0, 0);
stringPath.Append($"M {currentPoint.X} {currentPoint.Y}"); ParseOpenxmlArcTo(stringPath, wR, hR, stAng, swAng, currentPoint);
this.Path.Data=Geometry.Parse(stringPath.ToString());
} private Point ParseOpenxmlArcTo(StringBuilder stringPath, double wR, double hR, double stAng, double swAng, Point currentPoint)
{
const string comma = ","; //将Openxml的角度转为真实的角度
var θ1 = new Angle((int)stAng).ToRadiansValue();
var Δθ = new Angle((int)swAng).ToRadiansValue();
//旋转角
var φ = 0d;
//是否是大弧
var isLargeArcFlag = Math.Abs(Δθ) > Math.PI;
//是否是顺时针
var isClockwise = Δθ > 0; var rx = new Emu(wR).ToPixel().Value;
var ry = new Emu(hR).ToPixel().Value; //获取终点坐标
var pt = GetArBitraryPoint(rx, ry, swAng, stAng, φ, currentPoint); currentPoint = pt; // 格式如下
// A rx ry x-axis-rotation large-arc-flag sweep-flag x y
// 这里 large-arc-flag 是 1 和 0 表示
stringPath.Append("A")
.Append(rx) //rx
.Append(comma)
.Append(ry) //ry
.Append(comma)
.Append(φ) // x-axis-rotation
.Append(comma)
.Append(isLargeArcFlag ? "1" : "0") //large-arc-flag
.Append(comma)
.Append(isClockwise ? "1" : "0") // sweep-flag
.Append(comma)
.Append(pt.X)
.Append(comma)
.Append(pt.Y)
.Append(' ');
return currentPoint; } /// <summary>
/// 获取椭圆任意一点坐标
/// </summary>
/// <returns></returns>
private static Point GetArBitraryPoint(double rx, double ry, double Δθ, double θ1, double φ, Point currentPoint)
{
//开始点的椭圆任意一点的二维矩阵方程式
var matrixX1Y1 = DenseMatrix.OfArray(new double[2, 1]
{
{ currentPoint.X},
{ currentPoint.Y}
}); var matrix1 = DenseMatrix.OfArray(new double[2, 2]
{
{ Math.Cos(φ),-Math.Sin(φ)},
{ Math.Sin(φ),Math.Cos(φ)}
});
var matrix2 = DenseMatrix.OfArray(new double[2, 1]
{
{ rx*Math.Cos(θ1)},
{ ry*Math.Sin(θ1)}
});
var matrixCxCy = matrixX1Y1 - (matrix1 * matrix2); //终点的椭圆任意一点的二维矩阵方程式
var matrix3 = DenseMatrix.OfArray(new double[2, 1]
{
{ rx*Math.Cos(θ1+Δθ)},
{ ry*Math.Sin(θ1+Δθ)}
}); var matrixX2Y2 = matrix1 * matrix3 + matrixCxCy; return new Point(matrixX2Y2.Values[0], matrixX2Y2.Values[1]); }

效果如下:

可以看到,我们成功的绘制出我们的一条椭圆弧线,虽然很简单,但是其实这条弧线是我取ppt形状缺角矩形当中的一条弧线,在绘制其形状时候,上述方法会自动根据arcTo的数据来自动判断弧线的大小弧、顺逆时针等情况的绘制

源码

BlogCodeSample/OpenxmlActToSvgSample at main · ZhengDaoWang/BlogCodeSample

参考

Implementation Notes — SVG 2

【OpenXml】Pptx的形状转为WPF的Geometry - RyzenAdorer - 博客园

dotnet OpenXML SDK 形状几何 Geometry 的计算公式含义

【Openxml】将Openxml的椭圆弧线arcTo转为Svg的椭圆弧线的更多相关文章

  1. C# 将PDF转为SVG的3种情况

    PDF格式的文档广泛用于各种办公场所,在工作中难免会有将PDF文档转换为其他文档格式的需要.在本篇文档中,将介绍PDF转为SVG的方法.根据不同的转换需求,这里分三种情况进行讲述,即转PDF所有页为S ...

  2. png格式图片转为svg格式图片

    png格式图片转为svg格式图片 (2012-08-30 16:24:00) 转载▼ 标签: 杂谈 分类: linux 在ubuntu下将png格式的图片转换成svg格式步骤如下:1.安装 inksc ...

  3. Java 将Excel转为SVG的方法

    本文以Java示例展示如何将Excel文档转为SVG格式.通过本文中的方法,在将Excel转为SVG时,如果sheet工作表中手动设置了分页,则将每个分页的内容单独保存为一个svg文件,如果sheet ...

  4. 【C#/VB.NET】 将PDF转为SVG/Image, SVG/Image转PDF

    SVG是一种图形文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形.它在放大或者改变尺寸的情况下其图形质量不会有所损失,且与 JPG 和 GIF 图像比起 ...

  5. OpenXml 入门----OpenXml Tools使用技巧

    简介: Office2007以上版本的文档其实可以转换为XML格式.截图如下: Test.doc 解压过后已经完全变为文件夹和xml文件,文档的属性和信息都存储在了xml里面.根据XML就封装出了Op ...

  6. SVG.JS 画弧线

    需求描述: 使用svg.js,绘制一个弧线.下图绿色弧线. 准备工作: 1.了解SVG Path中的A指令 详细文档,请戳这里 给定x半径.y半径后,经过指定的两点,可以有2个椭圆,因此两点间有2条弧 ...

  7. webvector将html转为svg或者png图片的工具

    有些js较多,html组织不好的页面转换起来很不理想,cnblog转换的还不错 http://cssbox.sourceforge.net/webvector/

  8. SQL中的OpenXML使用

    DECLARE @idoc int ) SET @doc =' <ROOT> <Customer CustomerID="VINET" ContactName=& ...

  9. OpenXml入门

    一. OpenXml简介: Open XML标准的简单介绍:Ecma Office Open XML(“Open XML”)是针对字处理文档.演示文稿和电子表格的国际化开放标准,可免费供多个应用程序在 ...

随机推荐

  1. C#版Nebula客户端编译

    一.需求背景 从Nebula的Github上可以发现,Nebula为以下语言提供了客户端SDK: nebula-cpp nebula-java nebula-go nebula-python nebu ...

  2. ADB打开快手APP

    aa="adb -s {0} shell am start -n com.kuaishou.nebula/com.yxcorp.gifshow.HomeActivity".form ...

  3. Leetcode8. 字符串转换整数 (atoi)

    > 简洁易懂讲清原理,讲不清你来打我~ 输入字符串,输出整数![在这里插入图片描述](https://img-blog.csdnimg.cn/4feb56d86fca437a98f1e7f18d ...

  4. kubernetes/k8s CSI分析-容器存储接口分析

    更多 k8s CSI 的分析,可以查看这篇博客kubernetes ceph-csi分析,以 ceph-csi 为例,做了详细的源码分析. 概述 kubernetes的设计初衷是支持可插拔架构,从而利 ...

  5. 微信小程序云开发-数据库-获取用户添加的数据到数据库

    一.列表页面新增[添加商品]按钮 在列表页增加[添加商品]按钮,按钮绑定事件toAdd(),用户点击该按钮跳转到添加商品页面. 在js文件中写toAdd()函数,作用是点击[添加商品]按钮,跳转到[添 ...

  6. poj1182:食物链

    poj1182:食物链 听说是poj中最经典的一道并查集题目.我一做,果然很经典呢!好难啊!!!真的琢磨了很久还弄懂.这道题的重点就在于怎么用并查集表示题目中的关系环. 1. 题干 原题传送门1 原题 ...

  7. PAT乙级:1053 住房空置率 (20分)

    PAT乙级:1053 住房空置率 (20分) 题干 在不打扰居民的前提下,统计住房空置率的一种方法是根据每户用电量的连续变化规律进行判断.判断方法如下: 在观察期内,若存在超过一半的日子用电量低于某给 ...

  8. 更改Nginx网站根目录以及导致的403 forbidden问题

    Nginx采用默认配置,只修改了root的网站根目录位置,再访问网站的时候提示403Forbidden的错误. 仔细检查了新文件夹的权限,也对比了心就网站根目录的权限,都是一样的. 最后尝试关闭了SE ...

  9. 【洛谷P2800又上锁妖塔】动态规划

    分析 考虑上一层还是上两层还是爬上去 AC代码 #include <bits/stdc++.h> using namespace std; int f[1000005],a[1000005 ...

  10. 搭建kerberos和NTP服务器以及安全的NFS服务

    说明:这里是Linux服务综合搭建文章的一部分,本文可以作为单独搭建Kerberos和NTP时钟服务的参考. 注意:这里所有的标题都是根据主要的文章(Linux基础服务搭建综合)的顺序来做的. 如果需 ...