你有种再说一遍 发表于 2023-11-28 18:38:39

魔改系列-1关于dwg数据库索引和伪自定义图元

本帖最后由 你有种再说一遍 于 2024-10-25 02:31 编辑

# [自定义图元]
众多人认为自定义图元极为炫酷,那其原理究竟是什么呢?为何C#难以实现?实际上并非完全无法做到,只是需要自行去加以实现.

分析一下自定义图元:
天正墙体,你点选墙体某个部分,都能选择整个墙体.
它的原理就是八叉树(cad提供的)快速过滤得到墙体包围盒,然后从墙体结构中得到线段ids.
墙体结构是一根一根碎线,碎线被一个三维BREP结构包裹,这样才可以显示二维又可以转为三维.
在arx是继承图元类实现的,然后实现重写某些行为,arx就可以利用自带驱动模式,触发选择集事件时候加入显示.

C#要模仿上面这个行为.

## 0x01 索引构造
图元名和四(八)叉树,能够使ssget选中.

## 0x02 事件联动
若图元修改则联动修改其他图元,也称为约束器.
例如天正墙体,在交碰位置自动断开,所以每次画墙体,都是一次利用八叉树选择图元的过程哦.

## 0x03 夹点显示
高版本cad.net能够重定义(不是自定义),实际上会将普通图元转化,不建议用.可以采取替代策略,自己读取图元信息,计算夹点,变换到屏幕坐标系,用GID绘制一个发光矩形.

## 0x04 代理显示
没有提供arx/dbx时候的,这个部分将直接存入dwg内部,如果你保存为dxf,则打开就报错了.
C#无法实现,因为我们直接用原生图元,缺点就是arx/lisp能破坏结构.

## 0x05 OPM面板
显示新增的属性,分为只读和可设置部分.无非改成自己的面板修改而已,纯体力活,劫持一下特性面板也不是不行.

看到这里的朋友已经知道C#构造会缺乏什么部分了,如果你对缺乏的部分不足以产生安全感,那么建议你不要继续看下去了.
大部分不可控,都是源自于未知,以及绕过了接口的二次开发.

# 索引
相信学过四叉树的朋友都已经会碰撞检测了,而其中最厉害的方式是快速过滤得到目标图元.
那么伪自定义图元也是为了进行快速选择,然后联动修改.
cad本质就是一个数据库,也就是增删改查.
因此把"自定义图元"名字加入一个字典,把它拥有的子图元id加入value,就是一种快速索引的方式了,这个value还可以套一层三维BERP结构.
例如我们获取全部同名块/同名类型/差不多的名称的类型(模糊查询),都可以根据map["*Name"]去get value.

## 索引何时制造
通过开图接口,并行遍历句柄,代码:
https://www.cnblogs.com/JJBox/p/12489648.html
用全局字段记录全部的图元/图层/符号表.
然后分类构造名称索引:map<自定义名,objectIds>(){{"dimLink",ids}{"圆形",ids}{"墙体",ids}...}
以及四叉树索引,进行联合索引...
注意:内存使用,如果词典名key+图元集合value,那么这个value的类型用链表,10w个图元加入,就有20w个指针...

## 索引维护
通过一系列cad提供的数据库事件维护索引:
数据库加入事件上面加.
数据库移除事件上面删.
数据库撤销事件上面也做处理.

## 索引序列化
保存索引就可以提供给下次直接使用.而不需要在并行构造一次.
保存之后的索引是有序的,有些太庞大的部分,可以直接通过二分法检索.

> 方案1:通过二进制合并的方式
copy /b 1.dwg + 2.txt 3.dwg
把索引保存在dwg末尾,cad还是可以直接打开dwg的,只是保存时候会扔掉.
我们通过命令事件拦截`保存命令`,保存成功之后写入末尾,但是此时cad可能会长期占用dwg,无法写入,不是好方案.
> 方案2:在DXF内容中看见OLE段是一个二进制.
> 方案3:词典写入,但是容易被Lisp的人找到.
> 方案4:理解dwg格式,再插入到某些字段位置:
https://blog.csdn.net/jiangyb999/article/details/124497625
> 方案5:缩略图不限制大小的.


# [问题合集]
## 0x01 画图时归类到索引会不会拖慢速度?
经过我对于十万图元修改的理解,这样的方式影响不大,在画图时归类了,不需要运行命令时遍历全图.

## 0x02 子图元如何获取母图元呢?
标注链式选择功能例子:
> 方案一 链式选择
1,触发了选择集事件.
2,四叉树会记录链条包围盒,通过它快速过滤外部.
3,从字典获取链条全部图元.
4,亮显链条.
> 方案二 xdata预留信息进行反向选择
xdata里面储存前后两个图元的id,这表现就是双向指针了.
反向选择有一个好处也是不需要重新进入四叉树回表.
> 方案三 倒序索引
在子图元记录所有母图元id(你没有办法申请块表id,不过可以申请词典表记录id).
不过因为dwg中附带索引头指针的方式过于"软",
存在被lisp破坏的可能性,
而前后图元被删除也能够被数据库自检清理掉.

## 0x03 存不存跳过图元修改事件?
例如在位编辑命令时,对于图元没有修改,但是被归类到块内.
获取时候然后通过字典实现了一种"越权"访问图元.
或者c++有别样方式越权?尤其是cpp自定义图元无法被net归类时候存在出错可能.
据我所知没有.

## 0x04 遍历的处理
遍历块表应该提供"自定义图元的id",而不是内部碎线id(子图元).
在自己功能上可以使用自己的遍历接口,但是lisp和arx怎么办?
你无法更改块表接口,难道有hook方案?
据我所知没有,不过不太影响.
只需要这样补救:
Lisp遍历获取子图元id,将要修改信息,此时可以在数据库修改图元事件上面拦截,如果是"自定义的"就一起改,根据问题0x02子图元获取母图元方式即可.


# [参考]
ifox《拉伸填充边界事件.cs》有许多技巧,尤其是联动反应器时候有全局事件和局部事件,需要设置标记去停止和卸载整个功能,需要有防止lisp破坏现场的事件.
http://bbs.mjtd.com/thread-191397-1-1.html

# [多线程数据库]
从数据库的构成来看,这个也只是单线程数据库的玩法.cad是单线程数据库,它没有行锁或者叫图元锁,那我们能不能制作一个多线程数据库呢?
当然可以了,毕竟伪多线程好过没有.上面的索引也是单线程,可以把它做成多线程,我们还有自己的四叉树,四叉树每个节点也搜索也是可以并行执行,这样处理百万图元才有效果.
除了要修改数据库的时候出现串行,其他的搜索环节多线程简直加大分.
举个例子:
你要做全图的一百万根断线合并,先建设一个全图多段线和直线的索引,把两种图元加入四叉树,分别并行查找谁和谁靠近的...最后串行把共点图元组合起来.
写在这里了:
http://bbs.mjtd.com/thread-189745-1-1.html

(完)

gzxl 发表于 2023-11-29 09:10:52

这种思路看来实现了
惊惊回归 cad.net 开发了吗

荣sir 发表于 2023-12-1 16:33:27

不愧是惊佬

qq1254582201 发表于 2023-12-11 15:45:21

惊惊大佬,前排围观!!!
页: [1]
查看完整版本: 魔改系列-1关于dwg数据库索引和伪自定义图元