AutoCAD 中凸度(bulge)的概念及使用Canvas画图

1.概念解释

在解析Cad时,凸度(bulge)是一个比较重要的概念,它控制着弧线的凸起的方向和圆弧的半径,在WoutWare官网的API中,是这样解释bulge属性的:

The bulge is the tangent of one fourth the included angle for an arc segment, made negative if the arc goes clockwise from the start point to the endpoint. A bulge of 0 indicates a straight segment, and a bulge of 1 is a semicircle. 

简单翻译一下,意思为:
凸度是圆弧段四分之一夹角的正切值,如果圆弧从起点到终点顺时针移动,则凸出为负值。凸度为0表示直线段,凸度为1表示半圆。

根据属性的定义可知,不考虑正负值的情况下,凸度最小值为0(直线),最大值趋近于无穷大(接近于整圆),当凸度大于0时,这个值是圆弧占圆内角度/4,再取tan值,即tan(a/4),其中a的取值范围为0到2PI的开区间,所以凸度取值范围正好是0到无穷大。

正负值用来区分圆弧的方向,其中正值为逆时针取圆弧,负值为顺时针取圆弧。

2.canvas画图

在canvas中,画弧的API有两个,分别是arc和arcTo,这两个函数定义如下:

void ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
圆弧路径的圆心在 (x, y) 位置,半径为 r ,根据anticlockwise (默认为false表示顺时针)指定的方向从 startAngle 开始绘制,到 endAngle 结束。其中startAngle 和endAngle 的单位为弧度,即Math.PI表示180度。
void ctx.arcTo(x1, y1, x2, y2, radius);
根据控制点和半径绘制圆弧路径,使用当前的描点(前一个moveTo或lineTo等函数的止点)。根据当前描点与给定的控制点1连接的直线,和控制点1与控制点2连接的直线,作为使用指定半径的圆的切线,画出两条切线之间的弧线路径。

根据定义可知,arcTo只能画bulge<1的弧,arc或以画任意角度的弧,最初画图时,没有仔细查看bulge的定义,认为bulge<=1,所以采用了arcTo的方法,去计算控制点2,直到发现导出的数据中有bulge>1的时候,才发现arcTo的局限性,才通过计算圆心坐标采用arc方法去画图

2.1计算圆弧半径

 由图中可知,半径r = (d/2)/sin(a)

var a = Math.atan(p1.bulge)*2;
var d = Math.abs((p1.x==p2.x)?(p1.y-p2.y):((p1.y==p2.y)?(p1.x-p2.x):(p2.y-p1.y)/Math.sin(Math.atan((p2.y-p1.y)/(p2.x-p1.x)))));
var r = Math.abs((d / 2) / Math.sin(a));

在距离计算中,使用了三角函数,避免了开平方的运算

2.2通过计算辅助点p3画弧

 此处,引入坐标系,并将弧度做了一定的偏转,此时可得到以下计算公式:

tan(a+b)=(p1.y-p3.y)/(p1.x-p3.x)

tan(a-b)=(p2.y-p3.y)/(p3.x-p2.x)    =>

p1.y-p3.y = tan(a+b)*(p1.x-p3.x)

p2.y-p3.y=tan(a-b)*(p3.x-p2.x)    =>

p3.y = p1.y-tan(a+b)*(p1.x-p3.x) = p2.y-tan(a-b)*(p3.x-p2.x)    =>

p1.y-tan(a+b)*p1.x+tan(a+b)*p3.x=p2.y-tan(a-b)*p3.x+tan(a-b)*p2.x    =>

tan(a+b)*p3.x+tan(a-b)*p3.x=p2.y-p1.y+tan(a-b)*p2.x+tan(a+b)*p1.x    =>

p3.x = (p2.y-p1.y+tan(a-b)*p2.x+tan(a+b)*p1.x)/(tan(a+b)+tan(a-b))    =>

p3.y = p1.y-tan(a+b)*(p1.x-p3.x)

 至此,计算公式推导完成,上代码:

var b = Math.atan((p2.y-p1.y)/(p1.x-p2.x));
var tab = Math.tan(a+b);
var ta_b = Math.tan(a-b);
var p3 = {};
p3.x = (ta_b*p1.x+tab*p2.x+p2.y-p1.y)/(tab+ta_b);
p3.y = p1.y-tab*(p1.x-p3.x);
ctx.arcTo(p3.x, p3.y, p2.x, p2.y, r);

2.3通过计算圆心画弧

 看图可知,想要计算圆心o的坐标,可以先计算出p3,使p2-p3的距离=半径r,再将p3围绕p2旋转角b的度数即可得到圆心o的坐标

在2.1中,已经计算出的角度a和半径r,以及p1到p3的距离d,此处可直接应用

var p3 = {}, o = {};
p3.x = p2.x+(p1.x-p2.x)*r/d;
p3.y = p2.y+(p1.y-p2.y)*r/d;

p3的坐标计算出来了,下面该使用旋转来计算圆心o的坐标了,这里使用一个DOMMatrix对象,此对象在Chrome68版本才被引入,使用时要注意浏览器版本

var mat = new DOMMatrix();
mat.rotateSelf((90-Math.abs(a)*180/Math.PI)*(a>0?1:-1));
var o = mat.transformPoint(p3);

rotate方法默认是按逆时针旋转,此处需要根据a的正负值判断是顺时针还是逆时针

对比arc方法的参数,目前还缺少起始角度和终止角度,下面使用getAngle方法来计算角度值,这里为了方便理解,坐标系采用的是传统坐标系,下面在取y方向的值时,要注意取负数

function getAngle(x, y){
    if(x==0){//纵轴
		return y>0?Math.PI*3/2:Math.PI/2;
	}
	var angle = Math.atan(y/x);
	if(angle==0){//橫轴
		return x>0?0:Math.PI;
	}
	else if(x<0){//二三象限
		return Math.PI-angle;
	}
	else{
		if(y<0){//四象限
			return -angle;
		}
		else{//一象限
			return 2*Math.PI-angle;
		}
	}
}

下面是画弧的代码:

p1.angle = getAngle(p1.x-o.x, o.y-p1.y);
p2.angle = getAngle(p2.x-o.x, o.y-p2.y);
ctx.arc(o.x, o.y, r, p1.angle, p2.angle, p1.bulge>0);

注意此处取y值的差值时,由于cavans中左上角是0点,所以此时要取差值的负数。 

3.结语

经过努力,终于将AutoCAD中的凸度换算为canvas中的圆弧,至此问题圆满解决,在使用计算机处理坐标计算的过程中,应该尽可能的使用三角函数进行处理,能够大大简化计算的工作量,同时也要使用一定的数学技巧去转变思路,简化计算过程。

参考文章:

Class DxfHatch.BoundaryPath.Polyline.Vertexicon-default.png?t=N7T8http://www.woutware.com/doc/cadlib4.0/api/WW.Cad.Model.Entities.DxfHatch.BoundaryPath.Polyline.Vertex.htmlAutoCAD 凸度(bulge)的概念及使用WPF函数画图 - 源之缘 - 博客园前言 凸度(bulge)是AutoCAD 中一个非常重要的概念,凸度控制着两点之间弧度大小,弧度的方向。各种复杂的图像有可能就是成百上千的弧线组成的。从AutoCAD中导出的数据也有该值,一般的形式为icon-default.png?t=N7T8https://www.cnblogs.com/yuanchenhui/p/autocad_bulge.html

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要修改CAD多段线某一段弧的凸度,您可以使用以下C#代码: ```csharp using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; public void ModifyPolylineArcBulge() { // 获取当前文档和数据库 Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; // 提示用户选择多段线 PromptEntityOptions peo = new PromptEntityOptions("\n请选择多段线:"); peo.SetRejectMessage("\n请选择有效的多段线。"); peo.AddAllowedClass(typeof(Polyline), true); PromptEntityResult per = doc.Editor.GetEntity(peo); if (per.Status != PromptStatus.OK) return; // 获取多段线对象 Polyline polyline = (Polyline)per.ObjectId.GetObject(OpenMode.ForWrite); // 提示用户选择要修改的段 PromptIntegerOptions pio = new PromptIntegerOptions("\n请选择要修改的段:"); pio.AllowZero = false; pio.AllowNegative = false; pio.LowerLimit = 1; pio.UpperLimit = polyline.NumberOfVertices - 1; PromptIntegerResult pir = doc.Editor.GetInteger(pio); if (pir.Status != PromptStatus.OK) return; // 获取段的起点和终点 Point3d start = polyline.GetPoint3dAt(pir.Value - 1); Point3d end = polyline.GetPoint3dAt(pir.Value); // 计算段的点 Point3d midpoint = start + ((end - start) / 2.0); // 提示用户输入新的凸度值 PromptDoubleOptions pdo = new PromptDoubleOptions("\n请输入新的凸度值:"); pdo.AllowZero = false; pdo.AllowNegative = false; PromptDoubleResult pdr = doc.Editor.GetDouble(pdo); if (pdr.Status != PromptStatus.OK) return; // 计算新的弦长和半径 double chordLength = start.DistanceTo(end); double radius = (chordLength / 2.0) / Math.Abs(pdr.Value); // 计算新的圆心和起始角度和终止角度 Vector3d direction = end - start; double angle = direction.AngleOnPlane(Vector3d.ZAxis); if (direction.Y < 0) angle = 2 * Math.PI - angle; Point3d center = midpoint + (Vector3d.ZAxis * radius * Math.Sign(pdr.Value)); double startAngle = (start - center).AngleOnPlane(Vector3d.ZAxis); double endAngle = (end - center).AngleOnPlane(Vector3d.ZAxis); if (Math.Abs(pdr.Value) < 0.0001) endAngle = startAngle + Math.PI; // 修改凸度值 polyline.SetBulgeAt(pir.Value - 1, Math.Tan(angle / 4.0) * Math.Sign(pdr.Value)); // 修改弧的属性 Arc arc = new Arc(center, radius, startAngle, endAngle); polyline.SetArcSegmentAt(pir.Value - 1, arc); polyline.Closed = false; // 更新视图 doc.Editor.UpdateScreen(); } ``` 这段代码首先提示用户选择多段线以及要修改的段,然后计算新的凸度值对应的圆弧属性,最后修改凸度值并更新多段线的属性和视图。请注意,这段代码未经过完整测试,实际使用时可能需要进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shuaijie506

您的打赏是我继续分享的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值