cad.net bo边界算法

发送来解决

发送lisp

(bpoly point)

发送命令

Acad的低版本只能发送命令的方式获取边界信息,
edata说这个bo是写在arx内的,不是接口,所以也不能反射用(没深究了)

低版本:
通过命令发送bo的方法:发送bo命令例子

要注意的是,如果空格再次执行上次命令,你会执行到bo,而不是你的当前命令.
而正常的cad用户都是要执行当前的命令.
例如填充命令发送bo命令,那么再次空格是bo,而用户需要是填充

解决方法:要解开文档锁的情况下发送一次当前命令,而且在命令函数外做一个立即结束的标记,
在最前面写:
img

到了Acad2011: 就可以通过以下语句获取

Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;
ed.TraceBoundary(........);

算法概念

但是,要是自己能造一个边界算法,为什么要靠桌子的.

桌子的bo算法原理:
先获取当前屏幕可视的图形,然后用的是像素一点一点抓边界像素.
也就是种子填充算法.
知道了之后,就明白这是一个前台操作,
而我们需要挑战的是后台bo算法,也就是无像素的前提下分析边界.
而且必须要做得很快,不然就没有意义啦.

方案一,单点射线法

实际上就是夹角左转法,我做了一张图(拖拉图片看大图)
不要问为什么是奇点不是起点,因为起点的意义太多了,
换个字免得引起歧义而已.
用原点又会跟世界坐标原点重意辣!
img

1, 减少遍历,不可能O(n²)比较的,
所以我们利用四叉树
制作一个查找临近图元的功能.

2, 抛弃非边界计算图元,例如文字,标注,引线.
如果奇点,不在块的嵌套层次上,那么表示块边界才是遍历的,(此边界相当于bv树的外层)
如果在嵌套层次里,先算层次内,边界没密封才继续遍历其他块边界.

下面是山人告诉我的bo算法思想... 它就是图上的拆解算法.

样图

img

全部交点打断,然后bo到的边界就是我标数字的线段组成的.(其实不是打断,是求交点,我是便于大家理解)

步骤如下

0x01 射线求交点

img

从A点做0°直线(射线),与所有的线只有4个交点,就是我画绿色圆的点;

选择一个离A点最近的点,并通过这个点得到1号直线: 数学上来讲就是两个直线方程联立.

0x02 获取下一段线段

img

得到1号直线的两个端点,选择相对A-1线角度逆时针方向的点C,

由C点做选择集,得到2号和1号直线,排除1号直线:
可能C点相交有多个图元,存在和 0x03 一样的情况,
可能存在没有相交的图元,需要细节处理.

然后用2号直线的另一个交点d做选择集:
非选择集需要轮询(所有图元? 2号线包围盒范围内的?) 与2号线来求交,得出最近交点,

得到2/3/21/22直线.(如果是不打断的话,则没有21/22,
但是,万一情况是图元接上去呢,则需要以相同条件来进行)

0x03 逆时针选择的作用

这个时候,你要选择3号直线才是对的,
算法应选择直线的角度更靠近逆时针的那个:
也就是说d为基点,
22在最右,21在中间,3在最左,那就选择最左的那个3.
这时候的你轮廓集合里已经有3条直线了,分别为1/2/3

0x04 循环以上操作

然后就是不断的重复,直到走到12号线,又一次选择到了1号线,循环结束;

分析

经过0x010x02的步骤可知,每次都需要轮询一次所有的图元,
那么这个轮询就是数量就是线性递减的,
因为已经分析过的图元,
除了第一个需要保留以判断闭环之外,其他都可以抛弃.

这个是最简单的,没有内部孤岛,没有曲线,的bo原理.

方案二

我们发现上面的算法它在计算单一闭合好用,但是多闭合就有问题.

于是乎找到了此论文,主要是利用图来实现.
图分为邻接表和邻接矩阵.
当模型比较小的时候,用矩阵有优化效率.
原因是邻接表是内存离散模型,矩阵是连续的(内存不存在多维数组,全是一维模拟的),
而我们需要高频访问矩阵,这是优化阶段的东西.
二维图形封闭区域自动识别算法

广度优先算法

根据论文使用邻接表,
邻接表的key是交点字符串(注意要去精度),
value是节点类,类中有相遇链/坐标/颜色/步数(论文是长度,更具体见论文),
当分类完成之后,1{2,9}表示1节点同时链接着2,9节点.

论文核心是染色过程中提取了相遇链,核心是相遇时候的处理,然后回溯父节点.
其中染色的O(n)一次完成.

但是阿惊依照论文流程实现了一次代码之后,
发现有蛮多点和我们想要的曲线闭合不太相同.
不好的地方是相遇点再次寻找到下一个相遇点时候,
第2,3,4...节点不能父节点回溯,而是图会呈现水波状态向外绽放,
所以论文只会呈现最后图样那样,也就是一次回溯.

也跟bo算法中直接提取所有封闭区不同...难搞哦..

山人找到了这个stackoverflow:find-all-chordless-cycles

不过要注意:图的算法是抛弃双曲线闭合(单曲线闭合直接过滤图元属性就好了),
这样的ab两个节点就包含一个闭合区.这也难搞...

深度优先算法

小学问题:数数里面多少个多边形...成人解决方案版.
实际上我们只需要1/2/3区.
邻接表进行深度优先是利用穷举每个节点而成立的,闭环一个,回上一个分叉路再寻.

涂色在这里可以变成:
我们遍历每个节点必然会有8字型走成b或者p,腰身闭合的情况.
那么我们每走一步就遍历一次全部腰身的话,很蛋疼,所以设计了这个涂色.
涂色也可以是个序号,例如碰到4号,尾巴到4就是一条闭合线了.
每次完成一次遍历需要把涂色全部归0.

但是我觉得这样会造成一个可怕的结果,
穷举的多段线点序是下移的,也就是:点123/231/312,然后还有双向:点321/213/132,
并且还会穷举出一些我们不要的(图上面的4/5/6区)
一条多段线就那么多了耶.这要很多个数组储存呀.判断重复也需要处理时间.

总结起来就是:
key序号就失去了角度性,得到了快捷,
快捷又出现了穷举,穷举又带来了点序下移和双向和废区.

方案三 扫描线算法

要求孤岛怎么办?
扫描线算法!

填充算法只有两种:
一种叫种子填充算法,一种叫扫描线算法,
而查找封闭区也是同一个问题.
而cad的bo算法它本质用了种子填充算法.

长期以来,我都觉得cad的bo太慢了,所以我一直想对其重写,
试过很多算法,博客上面,左旋和染色,发现有缺陷.
左旋法的缺点,"囙"字,一直左旋也得到"凹",
要引入广度优先算法和深度优先算法(不好).
而且无法分析孤岛.染色法也一样,遇到多节点岔路时候存在相同问题.

这样的话,"图节点"算法需要抛弃了,所以我们需要使用扫描线算法.
它能算多个分离的:口 口 口
也能算合并的:口二口二口
也能算孤岛的:囙回
它真的很快,不需要并行都能秒出全图所有封闭区,是一些PCB软件的基本功能.


首先我们来分析一下这个一笔画的五芒星.
它上面是填充的算法,红色线的扫描线,

第一,数据清洗
单元化:
五芒星会被给分解成多个单一曲线
1,自交求交打断. 2,分解∧曲线需要炸开.
也就是每节曲线都是不相交的.
直断的重叠直线,也会被打断成多段.
只存在完全重叠,不存在部分重叠.
之后我们都需要先假设已经消重的完美数据.

第二,每段曲线包围盒中点,来进行扫描线求交.

第三,五芒星的中心,也是闭合的,
只需要剔除第一个交点,就可以把第二个面域求出来了.
这就是扫描线算法的基础了.

全部流程:
第一步, 消重
第二步, 循环剪枝
第三步, 横扫,竖扫,并组
第四步, 模拟填充,分析面积提取外边界

开始分析:
我先说核心,第三步,横扫:
扫描线算法的本质是横轴求交点,排序交点具备有序性,
有序性可以做什么?可以进行对边配对.

0x01,什么叫对边配对?
面域(封闭区域)等于一个人,一颗子弹从前胸进入必然从后背穿出,
因此面域交点必然是两两配对的.

0x02,因为有0x01的性质,
一条横向参照线就是子弹(扫描线),和这行每个图元求交点,
交点x排序后,按照xa-xb,xc-xd,xe-xf画线,
然后不断修改xline.Y+=0.1进行线性步进,接着求交再画下一次的线,
这就是模拟填充算法.

0x03,降低运算量
封闭区求法怎么才能不跟填充一样密集计算0.1线性步进呢?
图形:□▽
如果在minX位置横切一刀会得到了3个交点(奇数),
□△同理.这上下两刀算一个面域,会浪费时间不是吗?
所以用包围盒中点,在中间横切一刀就是最简单的!

目的就是为了利用y进行中点步进,而不是线性步进.
但是中间横切一刀得到的交点们,
也可能存在端点(你故意画个小三角呢),
因此你还需要求交点之后剔除同一行扫描图元的端点们,
并且进行交点排序消重(重叠图元问题).

0x04,并组,获得填充封闭区:
上面求到交点有什么用?
交点是没啥用的(除了模拟填充),但是交点击中的图元有用.
这里我们分成三区: 1,斜区 2,横区 3,竖区

1,斜区:
通过曲线的StartPoint和EndPoint进行比较就可以合并组了.
不过存在一些问题,我们到代码上面讨论.

2,图形:口
但是口字不像斜边组能够组内比较,那就只需要和这其他组比较一次就好了.

横扫交点分别是ab(左右).竖扫是cd(下上).
交点的图元需要有一个组,这样才能加入对边.
class CuInfo {
ObjectId id;
List Group;
}
boEntityA.Group.Add(boEntityB);
boEntityB.Group=boEntityA.Group;

例如台字型.
这个Group(a+b)虽然在一个面域内,但此时拐点没关联啊?
它们无法闭环得到一个封闭区呀.
因此需要引入竖扫得到Group(c+d).

横扫和竖扫(可以并行的,因为它是一行一列分别求交,这个特性使得它运算非常快)
横扫每组头尾点分别搜一次竖扫的头尾点,
把竖扫的Group加入过来,这就是拐点关联了,也就是完成了封闭区查找!

外边界问题:
图形L\7,这样横扫有abc三个交点呢,是奇数.
答案是:看一次cad的填充,最后交点再生成的一个虚拟点成为偶数,
然后填充0宽边界(挺变态的,我们也可以不按照cad做法,只填充前面部分,所以扫描线算法是失去旋转性的)
填充边界就是谁先连线就谁先封闭.
而外边界分析,只能查关联点的时候进行面积比较,留下大的部分.

全部封闭区:
有没有想过,xa-xb,xc-xd,xe-xf...这个操作并不是全部封闭区?
只是刚开始树立了一个间隔填充算法给你?

不然你看看五芒星,会发现星星中间没有填充也是面域哦.
因此填充边界组成的边界仍可能具备闭环性.
因此我们还需要求偶数封闭区xb-xc,xd-xe..看看它们是否具备闭环关系.

第二步:
图形:口口_|,这样横切一刀时候,发现末尾多了一个交点,
并且影响判断,这样图形就是多了分支,
因此需要剪枝:可以看见有一个节点悬空,进行循环删除.
删除之后就只有"口口"了(连_也删了哦)

第一步:
其实不需要消重,因为消重也是求交,扫描线也是求交,
扫完之后HashSet剔除重复交点就好了.

但是写都写了,保留这个图元消重算法吧:
获取选区图元进行两两求交.
无交点:可能是无限个,需要端点判断,判断后有交点则进入有交点环节.
有交点:打断全部图元,直断选择一边保留即可.

重合情况:
a:直线重合:包围盒完全相同,直线删剩一条.
b:曲线重合:包围盒完全相同,采样份>3,逐一比较,完全一致就删剩一条.
(为什么要>3,因为样条曲线3点和圆弧3点的包围盒和采样是一致的,但是图形不一致)
曲线重合不需要判断长度一样(因为耗时)
只需要通过曲线定份函数得到很多采样点,排序之后,两个点集一一对应,
就是满足重合性也满足长度一致性.
(还可以用simd指令优化速度).

部分重合计算端点--如果a1端点和b线碰撞&&b1端点和a线碰撞,则可能存在直断(曲线凸起就不重合),或者a2&&b2端点条件.两线两点碰撞之后,获取两条中间段,我们再对中间段曲线采样分析,是否保留...

全图两两求交时间太慢,还记得我上一文章的快速碰撞方法吗?
然后再进行碰撞组内的两两比较.
在ifox上面有个curve.ToCompositeCurve3d()函数的对象可以求曲线交点,打断它们得到碎线.
顺带一提,转为这个cc3d求交也很快(因为模拟填充是0.1步进所以我知道).
不管你这一步怎么做,都得出全部无碰撞碎线.

我来猜想一下cad的bo为什么慢?
你会看见bo命令是需要可见即可得的.
遇到块裁剪边界它会打开显示,而不是块表获取图元运算,
这说明了这是它在利用界面缓存进行种子填充算法,
不断利用种子生长逼近边界.
然后当前可见屏幕部分可能导致块裁剪部分重叠,
打开了每个块边界进行重绘,再进行布尔,
这一步是需要把当前画面像素全部复制一次比较的.

代码

https://www.cnblogs.com/JJBox/p/18652906

(完)

posted @   惊惊  阅读(4691)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示