cad.net 拉伸填充
拉伸填充
拉伸填充在Lisp做不好是有原因的,此链接就暴露了Lisp制作的许多缺点:例如Lisp应对ESC会中断,此时造成可怕的回调中断问题.
http://bbs.mjtd.com/thread-181607-1-1.html
我将带大家解读一次我所写的C#拉伸填充.
https://gitee.com/inspirefunction/ifoxcad/blob/jing/tests/TestAcad08/拉伸填充/02.拉伸填充事件.cs
编程技巧
0x01 特性面板
选中填充自动生成临时边界,进入选中状态(填充和边界).
用Dm_VetoCommand否决事件(文档锁定事件).
1,有否决:
双击时候想要的是填充面板
进行修改填充,但是由于此时选择多图元,会触发弹出特性面板
.
此时需要删除边界,重设选择集,再否决特性面板
,之后发送命令打开填充面板
.
为什么要发送命令?因为否决事件内,其他行为可能再次引发文档锁定变更,会无限递归.
例如,开一个非模态面板,然后button写用户交互(GetPoint)之类的.
此时你应该可以感觉到,发送命令其实是一种异步消息队列.
如果记得没错的话...否决事件不能直接在开cad的时候就否决特性面板
(自动执行接口:注册表加载/手工加载,他们时机不同,要分别测试),如果你需要这个功能,不能在这个事件进行,应该开线程循环检测,并发送关闭特性面板命令.
官方推荐是hook耶
https://adndevblog.typepad.com/autocad/2015/04/dismissing-popup-dialogs-in-autocad.html
2,无否决:
直接使用了特性面板
命令,面板中只留下填充才能修改填充属性,因此需要重设选择集,减选掉边界的选择.
那如果一直开着
特性面板
画图,岂不是一直不能用拉伸填充?
是缺陷?不过没有人这样做吧,如果有,就关掉一下面板,总不能自己写一个特性面板,或者用ARX自定义图元和重写OPM吧.
其实不会,因为没有触发第二次ctrl+1(特性面板),会发生边界被特性面板捕捉了...但是一旦画图,就会触发其他命令,会被Md_CommandWillStart事件捕捉,然后清理边界.
0x02 在位编辑图元
因为之前对于长事务理解缺失,我采取了集合排除的方式进行,是一种巧妙的做法.
此后,E大找到了长事务的操作,记录在我博客内:
https://www.cnblogs.com/JJBox/p/11185185.html
0x03 状态标志位
在类中,利用bitmap state作为状态标志,在多线程时候叫做状态机,而cad里面虽然是单线程的,但是也可以作为控制器.
1,如果在选择集事件内想要重设选择集,那么就需要在头部设置标记终止任务,避免死循环.
2,加载和卸载事件的+=-=放在一起,避免缺少移除事件.同时,在多线程移除时,那么要用标志位保证停止之后才能安全移除.
这样可以把一堆事件封装到功能类中,不用暴露全局,这份代码就可以跟文件夹一样随时剪切走.
填充问题
0x51 填充原点丢失
由于填充原点丢失,所以边界要用生成关联边界的方式,而不是直接生成填充和边界(桌子的问题).
还有许多填充问题,另见"该死的填充":
http://bbs.mjtd.com/thread-190975-1-1.html
0x52 缓存填充边界
填充边界有几种:
1,边界保留在图上,有保留的直接就可以拉伸边界了.
2,边界不保留在图上,需要生成临时边界.
3,边界不闭合,需要生成闭合填充,以及同2操作.
以上都会加入边界缓存_mapHatchConv.
需要生成的,就在转换器中生成边界,并加入缓存中,实现更快的检索速度.
0x53 撤回事件
撤回事件分两个,一个是删除,一个是更改.
撤回会恢复我所生成的临时边界,但是我不能让它出现在绘图区才对(毕竟这样要用户手动删除),并且缓存map里面也已经删除了,此时要怎么做好呢?
有两种方案解决:
1,我也自己做一个UndoLog,发生撤回时候,恢复缓存map,并判断是否我的边界.
2,我所生成的临时边界都写入xdata标记,这样相当于边界有个指针指向填充,在撤回恢复时候中再次删除边界.
很明显你可以见到我用方案2.
0x54 移动边界
Md_CommandWillStart事件.
1,触发非夹点移动:
if(cmdup != "GRIP_STRETCH")
EraseAllHatchBorders();
它将清理边界,之后才会触发到执行的命令.如果不删除,将导致很多问题.
a1,双击修改会弹特性面板(前面说了).
a2,命令移动时候会预览着临时边界.
a3,按ESC取消时候还有边界显示着.(如果是Lisp写的话,Esc会被上层捕捉,进入取消Lisp执行,而不是执行删除边界?我看论坛其他人报告会发生崩溃)
a4,如果保留边界,然后用分离填充,会出现激活了选择集反应器(我也不懂为什么),之后得到了一个致命错误.
2,触发夹点移动:
b1,图上保留边界的填充:
关联反应器会自动移动有保留的边界,并且边界是不能删除的.
b2,图上无边界的填充:
Md_CommandWillStart事件记录计算夹点信息后到Md_CommandEnded事件中继续执行.(之前的我为什么要这样写呢?都写在执行后不就好了...)
选择对象时候,临时边界已经被我生成出来了,
夹点移动预览时候,临时边界会在原地,因此需要删掉临时边界,再生成一次,实现伪移动.
为什么它会在原地呢?
虽然创建的临时边界也是关联填充的,但是夹点移动的是填充.关联反应器是用边界来改填充,而不能反过来.
其他方案
如果通过前后点组成向量去移动边界,大概率是可以的,只是比较麻烦,要收集第二次释放的鼠标点,以及move更新边界.(我没试过)
0x55 计算移动夹点
你知道可移动填充的夹点是什么吗?它不是边界点,它是矩形填充中间的夹点,所以要计算出来,排除边界点就是了.
然后这里面就有个问题了,鼠标直接点击填充夹点,没有发生"editor.GetPoint()",
命令事件上面的捕捉"GRIP_STRETCH"也没有啊,
那怎么拿夹点呢?
答:利用鼠标钩子不断记录在缓存中,在需要时候把缓存点转换到图纸坐标系上,就可以拿到了.
我好像没写对转换比例...请参考四代目的:
http://bbs.mjtd.com/thread-189169-1-1.html
我用的鼠标钩子是微软带的全局鼠标钩子,而acad内部也有个进程内的:Acap.PreTranslateMessage
https://www.cnblogs.com/chenshuangjian/p/15958356.html
(嘿嘿)