本文將介紹如何將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:
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=https://www.cnblogs.com/ryzen/archive/2021/08/26/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 的計算公式含義
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/295802.html
標籤:.NET技术
