拉伸填充
拉伸填充在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的時候就否決特性皮膚
(自動執行介面:登錄檔載入/手工載入,他們時機不同,要分別測試),如果你需要這個功能,不能在這個事件進行,應該開執行緒迴圈檢測,併傳送關閉特性皮膚命令.
2,無否決:
直接使用了特性皮膚
命令,皮膚中只留下填充才能修改填充屬性,因此需要重設選擇集,減選掉邊界的選擇.
那如果一直開著
特性皮膚
畫圖,豈不是一直不能用拉伸填充?
是缺陷?不過沒有人這樣做吧,如果有,就關掉一下皮膚,總不能自己寫一個特性皮膚,或者用ARX自定義圖元和重寫OPM吧.
其實不會,因為沒有觸發第二次ctrl+1(特性皮膚),會發生邊界被特性皮膚捕捉了...但是一旦畫圖,就會觸發其他命令,會被Md_CommandWillStart事件捕捉,然後清理邊界.
0x02 在位編輯圖元
因為之前對於長事務理解缺失,我採取了集合排除的方式進行,是一種巧妙的做法.
此後,E大找到了長事務的操作,記錄在我部落格內:
https://www.cnblogs.com/JJBox/p/14487646.html#_label3_1_3_1
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
(嘿嘿)