【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现

这篇具有很好参考价值的文章主要介绍了【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、SVG路径的A指令的语法说明

目前Svg的Arc的参数字符串如下:其中,A (绝对) a (相对)

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

除了A(a)表示标识为圆弧之外,其余参数说明如下:

参数 说明 符号
rx 椭圆半长轴 a a a
ry 椭圆半短轴 b b b
x-axis-rotation 椭圆相对于坐标系的旋转角度,角度数而非弧度数 α \alpha α
large-arc-flag 是否优(大)弧:0否,1是 f l f_l fl
sweep-flag 绘制方向:0逆时针,1顺时针 f s f_s fs
x 圆弧终点的x坐标 x e x_{e} xe
y 圆弧终点的y坐标 y e y_{e} ye

实际上,我们也可以得到圆弧起点的坐标(即上一段图像的终点):

说明 符号
圆弧起点的x坐标 x s x_{s} xs
圆弧起点的y坐标 y s y_{s} ys

二、DXF中的圆弧和椭圆弧对象

2.1 圆弧对象

class DXFArc
{
public:
	double cx; // 圆心X坐标
	double cy; // 圆心Y坐标
	double radius; // 圆弧半径
	double bangle; // 起点角度
	double eangle; // 终点角度
};

2.2 椭圆弧对象

class DXFEllipse
{
public:
	double cx; // 椭圆心X坐标
	double cy; // 椭圆心Y坐标
	double mx; // 长轴端点相对于中点的X坐标
	double my; // 长轴端点相对于中点的Y坐标
	double ratio; // 短半轴长度➗长半轴长度
	double angle1; // 起始弧度
	double angle2; // 终止弧度
};

三、转DXF圆弧

3.1 数学公式

圆弧可以看作一个长半轴等于短半轴(即满足 a = b = r a=b=r a=b=r)的特殊椭圆弧,因此,下面统一用长半轴 a a a 代替半径 r r r

另外,圆弧的 α = 0 \alpha = 0 α=0

第一步:计算圆弧中心 ( c x , c y ) (c_x,c_y) (cx,cy)

x 1 ′ = ( x s − x e ) / 2 y 1 ′ = ( y s − y e ) / 2 m = ± ∣ a 4 − a 2 y 1 ′ 2 − a 2 x 1 ′ 2 ∣ / ( a 2 ( y 1 ′ 2 + x 1 ′ 2 ) ) ( 仅当 l f = l s 时取 − , 其余时候取 + ) ⇓ c x ′ = m y 1 ′ c y ′ = − m x 1 ′ ⇓ c x = c x ′ + ( x s + x e ) / 2 c y = c y ′ + ( y s + y e ) / 2 x_1'=(x_s-x_e)/2\\ y_1'= (y_s-y_e)/2\\ m=±\sqrt{|a^4-a^2y_1'^2-a^2x_1'^2|/(a^2(y_1'^2+x_1'^2))} \quad (仅当l_f=l_s时取-,其余时候取+)\\ \Downarrow\\ c_x'=m y_1'\\ c_y'=-mx_1'\\ \Downarrow\\ c_x=c_x'+(x_s+x_e)/2\\ c_y=c_y'+(y_s+y_e)/2\\ x1=(xsxe)/2y1=(ysye)/2m=±a4a2y1′2a2x1′2∣/(a2(y1′2+x1′2)) (仅当lf=ls时取,其余时候取+)cx=my1cy=mx1cx=cx+(xs+xe)/2cy=cy+(ys+ye)/2

第二步:计算起始和终止角度 ( b a n g l e , e a n g l e ) (bangle,eangle) (bangle,eangle)

根据圆的参数方程,可以通过代入点 ( x , y ) (x,y) (x,y),从而反推出该点的角度 t t t

{ x = a ⋅ c o s ( t ) + c x y = a ⋅ s i n ( t ) + c y ⇓ t = ± a r c c o s ( ( x − c x ) / a ) \begin{cases} x=a · cos(t)+c_x \\ y=a · sin(t) + c_y \end{cases}\\ \Downarrow\\ t=±arccos((x-c_x)/a) {x=acos(t)+cxy=asin(t)+cyt=±arccos((xcx)/a)

其中, t t t 的符号和 a r c s i n ( ( y − c y ) / a ) arcsin((y-c_y)/a) arcsin((ycy)/a) 的相同。

基于此,我们可以通过将圆弧的起点 ( x s , y s ) (x_s,y_s) (xs,ys) 和终点 ( x e , y e ) (x_e,y_e) (xe,ye) 分别代入圆的参数方程,反推出圆弧的起始和终止角度 ( b a n g l e , e a n g l e ) (bangle,eangle) (bangle,eangle)

3.2 代码实现

// 圆周率
#define PI acos(-1)
// 浮点型数据精度
#define ERROR 0.00000001

// 根据圆的参数方程和圆上一个点的坐标,计算该点的角度
// r:圆弧半径
// (cx,cy):圆弧的中心点
// (x,y):圆弧上某点的坐标
double calcArcPointAngle(double r, double cx, double cy, double x, double y){
	double res = acos((x - cx)/r);
	if (asin((y - cy)/r) < 0){
		res = -res;
	}
	return res / PI * 180;
}

// 传入SVG的A指令的相关参数,返回对应的DXF中的圆弧对象
// a:圆弧半径
// lf:是否优(大)弧:0否,1是
// sf:绘制方向:0逆时针,1顺时针
// (startX,startY):圆弧的起点
// (endX,endY):圆弧的终点
DXFArc getDXFArcBySvg(double a,int lf,int sf,double startX,double startY,double endX,double endY){
	DXFArc arc = DXFArc();
	// 圆弧半径
	arc.radius = a;
	// 计算中点(cx,cy)
	double x1Pie = 0.5 * (startX - endX);
	double y1Pie = 0.5 * (startY - endY);
	double m = sqrt(abs(a*a*a*a - a*a*y1Pie*y1Pie - b*b*x1Pie*x1Pie) / (a*a* (y1Pie*y1Pie+x1Pie*x1Pie)));
	if (lf == sf){
		m = -m;
	}
	double cxPie = m * y1Pie;
	double cyPie = m * -x1Pie;
	arc.cx = cxPie + 0.5 * (startX + endX);
	arc.cy = cyPie + 0.5 * (startY + endY);
	// 计算角度(bangle,eangle)
	arc.bangle = calcArcPointAngle(a, arc.cx, arc.cy,startX,startY);
	arc.eangle = calcArcPointAngle(a, arc.cx, arc.cy, endX, endY);
	// 修正角度
	while (arc.eangle < arc.bangle){
		arc.eangle += 360.0;
	}
	if ((lf == 1 && arc.eangle - arc.bangle < 180.0) || (lf == 0 && arc.eangle - arc.bangle > 180.0)){
		double temp = arc.bangle;
		arc.bangle = arc.eangle;
		arc.eangle = temp;
	}
	// 返回转化好的圆弧
	return arc;
}

3.3 转换效果展示

SVG显示效果如下

【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现

转化为DXF后的显示效果如下

【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现


四、转DXF椭圆弧

4.1 数学公式

第一步:计算椭圆弧中心 ( c x , c y ) (c_x,c_y) (cx,cy)

x 1 ′ = c o s ( α ) ⋅ ( x s − x e ) / 2 + s i n ( α ) ⋅ ( y s − y e ) / 2 y 1 ′ = − s i n ( α ) ⋅ ( x s − x e ) / 2 + c o s ( α ) ⋅ ( y s − y e ) / 2 m = ± ∣ a 2 b 2 − a 2 y 1 ′ 2 − b 2 x 1 ′ 2 ∣ / ( a 2 y 1 ′ 2 + b 2 x 1 ′ 2 ) ( 仅当 l f = l s 时取 − , 其余时候取 + ) ⇓ c x ′ = m ( a y 1 ′ ) / b c y ′ = − m ( b x 1 ′ ) / a ⇓ c x = c o s ( α ) ⋅ c x ′ − s i n ( α ) ⋅ c y ′ + ( x s + x e ) / 2 c y = s i n ( α ) ⋅ c x ′ + c o s ( α ) ⋅ c y ′ + ( y s + y e ) / 2 x_1'=cos(\alpha)·(x_s-x_e)/2+sin(\alpha)·(y_s-y_e)/2\\ y_1'= -sin(\alpha)·(x_s-x_e)/2+cos(\alpha)·(y_s-y_e)/2\\ m=±\sqrt{|a^2b^2-a^2y_1'^2-b^2x_1'^2|/(a^2y_1'^2+b^2x_1'^2)} \quad (仅当l_f=l_s时取-,其余时候取+)\\ \Downarrow\\ c_x'=m(ay_1')/b\\ c_y'=-m(bx_1')/a\\ \Downarrow\\ c_x=cos(\alpha)·c_x'-sin(\alpha)·c_y'+(x_s+x_e)/2\\ c_y=sin(\alpha)·c_x'+cos(\alpha)·c_y'+(y_s+y_e)/2\\ x1=cos(α)(xsxe)/2+sin(α)(ysye)/2y1=sin(α)(xsxe)/2+cos(α)(ysye)/2m=±a2b2a2y1′2b2x1′2∣/(a2y1′2+b2x1′2) (仅当lf=ls时取,其余时候取+)cx=m(ay1)/bcy=m(bx1)/acx=cos(α)cxsin(α)cy+(xs+xe)/2cy=sin(α)cx+cos(α)cy+(ys+ye)/2

第二步:计算长轴端点相对中点的坐标 ( m x , m y ) (m_x,m_y) (mx,my)

m x = a ⋅ c o s ( α ) m y = ± a 2 − m x 2 ( 仅当 α < 0 时取 − ,其余时候取 + ) m_x=a·cos(\alpha)\\ m_y=±\sqrt{a^2-m_x^2} \quad (仅当\alpha<0时取-,其余时候取+)\\ mx=acos(α)my=±a2mx2 (仅当α<0时取,其余时候取+)

第三步:计算椭圆弧的起始和终止角度 ( a n g l e 1 , a n g l e 2 ) (angle1,angle2) (angle1,angle2)

根据椭圆的参数方程,可以通过代入点 ( x , y ) (x,y) (x,y),从而反推出该点的角度 t t t

{ x = a ⋅ c o s ( t ) ⋅ c o s ( α ) − b ⋅ s i n ( t ) ⋅ s i n ( α ) + c x y = a ⋅ c o s ( t ) ⋅ s i n ( α ) + b ⋅ s i n ( t ) ⋅ c o s ( α ) + c y ⇓ { c o s ( t ) = ( ( x − c x ) ⋅ c o s ( α ) + ( y − c y ) ⋅ s i n ( α ) ) / a s i n ( t ) = ( − ( x − c x ) ⋅ s i n ( α ) + ( y − c y ) ⋅ c o s ( α ) ) / b ⇓ t = ± a r c c o s ( ( ( x − c x ) ⋅ c o s ( α ) + ( y − c y ) ⋅ s i n ( α ) ) / a ) \begin{cases} x = a·cos(t)·cos(\alpha)-b·sin(t)·sin(\alpha)+c_x\\ y = a·cos(t)·sin(\alpha)+b·sin(t)·cos(\alpha)+c_y \end{cases}\\ \Downarrow\\ \begin{cases} cos(t) = ((x-c_x)·cos(\alpha)+(y-c_y)·sin(\alpha))/a\\ sin(t) = (-(x-c_x)·sin(\alpha)+(y-c_y)·cos(\alpha))/b\\ \end{cases}\\ \Downarrow\\ t=±arccos(((x-c_x)·cos(\alpha)+(y-c_y)·sin(\alpha))/a) {x=acos(t)cos(α)bsin(t)sin(α)+cxy=acos(t)sin(α)+bsin(t)cos(α)+cy{cos(t)=((xcx)cos(α)+(ycy)sin(α))/asin(t)=((xcx)sin(α)+(ycy)cos(α))/bt=±arccos(((xcx)cos(α)+(ycy)sin(α))/a)

其中, t t t 的符号和 a r c s i n ( ( − ( x − c x ) ⋅ s i n ( α ) + ( y − c y ) ⋅ c o s ( α ) ) / b ) arcsin((-(x-c_x)·sin(\alpha)+(y-c_y)·cos(\alpha))/b) arcsin(((xcx)sin(α)+(ycy)cos(α))/b) 的相同。

基于此,我们可以通过将圆弧的起点 ( x s , y s ) (x_s,y_s) (xs,ys) 和终点 ( x e , y e ) (x_e,y_e) (xe,ye) 分别代入椭圆的参数方程,反推出椭圆弧的起始和终止角度 ( a n g l e 1 , a n g l e 2 ) (angle1,angle2) (angle1,angle2)

4.2 代码实现

// 圆周率
#define PI acos(-1)
// 浮点型数据精度
#define ERROR 0.00000001

// 根据椭圆的参数方程和椭圆上一个点的坐标,计算该点的角度
// a:长轴半径
// b:短轴半径
// alpha:椭圆弧长半轴与x正半轴的夹角(弧度)
// (cx,cy):椭圆弧的中心点
// (x,y):椭圆弧上某点的坐标
double calcEllipsePointAngle(double a,double b,double alpha,double cx,double cy,double x,double y){
	double c = ((x - cx)*cos(alpha) + (y - cy)*sin(alpha)) / a;
	// 防止出现 acos(1.00000001)=-1.#IND的情况
	if (abs(c - 1) <= ERROR){
		c = 1;
	}
	// 防止出现 acos(-1.00000001)=-1.#IND的情况
	if (abs(c + 1) <= ERROR){
		c = -1;
	}
	double res = acos(c);
	c = (-(x - cx)*sin(alpha) + (y - cy)*cos(alpha)) / b;
	// 防止出现 asin(1.00000001)=-1.#IND的情况
	if (abs(c - 1) <= ERROR){
		c = 1;
	}
	// 防止出现 asin(-1.00000001)=-1.#IND的情况
	if (abs(c + 1) <= ERROR){
		c = -1;
	}
	if (asin(c) < 0){
		res = -res;
	}
	return res / PI * 180;
}

// 传入SVG的A指令的相关参数,返回对应的DXF中的椭圆弧对象
// a:长轴半径
// b:短轴半径
// lf:是否优(大)弧:0否,1是
// sf:绘制方向:0逆时针,1顺时针
// alpha:椭圆弧长半轴与x正半轴的夹角(弧度)
// (startX,startY):椭圆弧的起点
// (endX,endY):椭圆弧的终点
DXFEllipse getDXFEllipseBySvg(double a,double b,int lf,int sf,double alpha,double startX,double startY,double endX,double endY){
	DXFEllipse ellipse = DXFEllipse();
	// 短半轴长度➗长半轴长度
	ellipse.ratio = b / a;
	// 计算中点(cx,cy)
	double x1Pie = cos(alpha) * 0.5 * (startX - endX) + sin(alpha) * 0.5 * (startY - endY);
	double y1Pie = -sin(alpha) * 0.5 * (startX - endX) + cos(alpha) * 0.5 * (startY - endY);
	double m = sqrt(abs(a*a*b*b - a*a*y1Pie*y1Pie - b*b*x1Pie*x1Pie) / (a*a*y1Pie*y1Pie + b*b*x1Pie*x1Pie));
	if (lf == sf){
		m = -m;
	}
	double cxPie = m * ((a * y1Pie) / b);
	double cyPie = -m * ((b * x1Pie) / a);
	ellipse.cx = cos(alpha) * cxPie - sin(alpha) * cyPie + 0.5 * (startX + endX);
	ellipse.cy = sin(alpha) * cxPie + cos(alpha) * cyPie + 0.5 * (startY + endY);
	// 计算长轴端点相对中点的坐标(mx,my)
	ellipse.mx = a * cos(alpha);
	ellipse.my = sqrt(a*a - ellipse.mx*ellipse.mx);
	if (alpha < 0){
		ellipse.my = -ellipse.my;
	}
	// 计算角度(angle1,angle2)
	ellipse.angle1 = calcEllipsePointAngle(a, b, alpha, ellipse.cx, ellipse.cy, startX, startY) / 180.0 * PI;
	ellipse.angle2 = calcEllipsePointAngle(a, b, alpha, ellipse.cx, ellipse.cy, endX, endY) / 180.0 * PI;
	// 修正角度
	while (ellipse.angle2 < ellipse.angle1){
		ellipse.angle2 += (2 * PI);
	}
	if ((lf == 1 && ellipse.angle2 - ellipse.angle1 < PI) || (lf == 0 && ellipse.angle2 - ellipse.angle1 > PI)){
		double temp = ellipse.angle1;
		ellipse.angle1 = ellipse.angle2;
		ellipse.angle2 = temp;
	}
	// 返回转化好的椭圆弧
	return ellipse;
}

4.3 转换效果展示

SVG显示效果如下

【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现

转化为DXF后的显示效果如下

【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现


本博客的参考链接如下,第一章内容两篇都有借鉴。计算圆弧和椭圆弧中点的公式借鉴了第2个参考博客,公式与他的有所不同,增加了一个绝对值:文章来源地址https://www.toymoban.com/news/detail-462096.html

  1. SVG路径(path)中的圆弧(A)指令的语法说明及计算逻辑
  2. 根据SVG Arc求出其开始角、摆动角和椭圆圆心

到了这里,关于【脚本工具】SVG路径中的A指令转DXF的圆弧和椭圆弧 & C++代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • sCrypt 合约中的椭圆曲线算法:第二部分

    我们在脚本中实现了椭圆曲线 (EC) 算法。在之前的实现中,我们进行链下计算并在脚本中验证结果。我们这里直接用脚本计算。 基于EC的应用非常多,特别是在密码学领域,如数字签名、加密、承诺方案等。作为具体示例,我们重新实现了 ECDSA 签名验证,允许使用任意消息验

    2024年01月16日
    浏览(48)
  • 【echarts】如何将iconfont转换成echart所需的path路径 echarts折线图、柱状图如何设置自定义svg图标

    步骤 下载iconfont图标到本地,用浏览器打开,右键查看源代码,或者用开发IDE软件打开,找到 path d=... ,这个就是我们要传递给echart的icon的值。 代码示例:

    2024年02月09日
    浏览(43)
  • 椭圆曲线在SM2加解密中的应用(三)

    1.1加密原始数据 SM2加密运算首先是用户A对数据加密,用户A拥有原始数据 椭圆曲线系统参数 长度为klen比特的消息M 公钥Pb 椭圆曲线系统参数,已经在 椭圆曲线参数(二)中详细介绍;M就是需要加密消息,长度为klen; 1.1.1 公钥Pb的计算方式 公钥Pb=dBG,其中dB是私钥,是256b

    2024年02月08日
    浏览(40)
  • SVG在前端中的常见应用

    只是一些常用的应用,但足以入门。 svg标签相当于画布。 可以在标签中定义宽和高 g 标签可以对svg元素进行分组,分组后可以统一配置属性。 stroke :笔画颜色属性,值为颜色值 strike-width :笔画宽度属性,值为数值 stroke-linecap :笔画笔帽属性 butt:默认值,没有线帽。 ro

    2024年02月05日
    浏览(57)
  • svg矢量图标在wpf中的使用

    在wpf应用程序开发中,为支持图标的矢量缩放,及在不同分辨率下界面中图标元素的矢量无损缩放,所以常常用到svg图标,那么如果完 美的将svg图标运用到wpf日常的项目开发中呢,这里分享一下我的个人使用经验和详细步骤。 1、SvgToXaml 工具 我经常使用一款开源的svg转xaml的

    2024年01月21日
    浏览(61)
  • Easy3dviewer三维模型(gltf/glb、osgb、fbx、x、shp、dxf)超轻量浏览和转换工具软件分享

    工作中经常需要用到不同格式的三维模型,比如3dmiax建模的3ds,倾斜摄影的osgb、bim转换的fbx,二维gis需要的shp、cad建模的dxf、三维gis需要的gltf等等,需要能快速方便的浏览和查看这些三维模型,也需要能将三维模型格式转换成其他三维模型格式。对三维浏览和转换的需求非

    2024年02月04日
    浏览(156)
  • 自己写一个svg转化为安卓xml的工具类

    自己写一个svg转化为安卓xml的工具类_张风捷特烈的博客-CSDN博客 svg资源阿里巴巴矢量资源网站:iconfont-阿里巴巴矢量图标库 感觉一般的svg到Android可用的xml差异有点规律,主要的就是path 秉承着能用代码解决的问题,绝对不动手。能够靠智商解决的问题,绝对不靠体力的大无畏

    2024年02月11日
    浏览(43)
  • 学习笔记——SVG.js中的use和marker元素的相关方法

    Use() use() use元素只是模拟另一个现有元素。主元素上的任何更改都将反映在所有使用实例上。use()的用法非常简单: 在上述示例的情况下,svg绘图中将显示两个矩形,即原始矩形和使用实例矩形。在某些情况下,可能需要隐藏原始元素。最好的方法是在defs节点中创建原始

    2024年02月05日
    浏览(38)
  • Shell脚本学习记录(常见指令)

    Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式: 1.显示普通字符串: 2.显示转义字符 3.显示变量 read 命令(类似C语言的scanf)从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量 4.显示换行   5.显示不换行 6.显示结果定向至文件   7.原样

    2024年02月08日
    浏览(38)
  • Linux Shell脚本获取当前路径

    目录 1、使用场景 2、原理 3、测试 3.1、测试原理 3.2、测试结果 4、其它 1、使用场景 我们的脚本经常被放置到不同层次的目录之中,有人喜欢放到/opt, 有人喜欢放到/home/myname,这是不可控的。自动化部署工作中,经常要移动、拷贝文件,而获取当前脚本运行目录,才能方便的

    2024年02月14日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包