3.8 在AutoCAD中選擇實體
1. ads_name[在AutoCAD 2000中仍為此名]、acdbNameSet()[ads_name_set()]、acdbNameEqual()[ads_name_equal()]、acdbNameClear()[ads_name_clear()]、acdbNameNil()[ads_name_nil()]
在ADS、ADSRX或ObjectARX中,選擇實體的方法沒有變化,但在ObjectARX 2000中改變了一些函數名(其他函數名沒有改變,對於第6章討論的資料類型和DCL對話方塊也基本是這樣的)。
我們介紹一種新的資料類型:ads_name。這種類型資料用來儲存成功選擇實體的結果。通常,選擇實體是為
了使用者能獲得並修改實體特性。這裡是老式的ADS和現代的API ObjectARX完全不同的地方。在討論ADS和ObjectARX在獲得實體資料方面的差別之前,先來看一看ads_name的定義。
ads_name資料類型是一個具有兩個long型元素的數組,因此不能使用賦值操作符使一個ads_name對象等於另一個ads_name對象。這種情形類似於前文的ads_point資料類型。正如ads_point資料類型一樣,
AutoCAD提供了一個名為acdbNameSet()的宏,用來使一個ads_name對象等於另一個ads_name對象。
ADS/ADSRX提供了許多宏來處理ads_name對象。要使一個ads_name對象等於另一個ads_name對象,可以用acdbNameSet()宏。
要判斷兩個ads_name對象是否相等,可以用acdbNameEqual()宏。
要把一個空值(NULL)賦給一個ads_name對象,可以用acdbNameClear()宏。要測試一個ads_name對象的值是否有效,可以用acdbNameNil()宏。
2. acedEntSel()[ads_entsel()]、acdbEntGet()[ads_entget()]、acdbEntMod()[ads_entmod()]、acdbEntUpd()[ads_entupd()]
請牢記選擇集也是ads_name對象(我知道這可能引起不小的混淆)。我們很快就會討論選擇集。前文談到ADS和ObjectARX在獲得和更改實體資料的方法上是完全不同的,在後面的章節中我們將討論ObjectARX的方法。
在ADSRX中,使用acdbEntGet()函數獲得實體資料,使用acdbEntMod()和acdbEntUpd()函數修改和更新實體。下面是這些函數的定義:
用acedEntSel()函數選擇的實體將和ads_name對象相關聯。在詳細討論acedEntSel()函數之前,先要談一下acdbEntGet()、acdbEntMod()和acdbEntUpd()函數。
acdbEntGet()函數返回一個結果緩衝區的單鏈表。使用resbuf對象的rbnext欄位可以遍曆和檢測結果緩衝區鏈表。若rb為一個resbuf結果緩衝區,則文法如下:
之後還可以改變某些結果緩衝區中的值,當最終完成後調用acdbEntMod()函數修改實體的內部資料。正如所見,該函數要求一個 resbuf 對象。如果正在處理一個如塊或多段線一樣的複雜物件,可以使用
acdbEntUpd()函數來觀察由各個acdbEntMod()函數調用所做的修改效果。從本質上講,使用ADSRX就是處理結果緩衝區鏈表。ObjectARX的方法與此是完全不同的,且差別巨大。對於有大量舊資料的使用者,由於有ADSRX,這些函數在ObjectARX中仍然可以使用(Autodesk公司的人把ADS放進了ObjectARX)。由於本書是討論ObjectARX,因此,關於acdbEntGet()、acdbEntMod()和acdbEntUpd()函數的討論就到此為止。
現在回到acedEntSel()函數,因為該函數在ObjectARX中仍然得到廣泛應用。再一次把acedEntSel()函數的定義說明如下:
acedEntSel()函數暫停運行等待使用者輸入,並在entres中返回實體名,在ptres中返回選擇實體使用的點。參數str指定acedEntSel()函數暫停前顯示的字串。參數str是可選的,如果是NULL,AutoCAD顯示預設提示“Select objects:”。當使用者指定一個複雜實體響應acedEntSel()函數時,則返回多段線或塊的標題。如果acedEntSel()函數調用成功,則返回RTNORM,失敗則返回RTERROR,如果使用者取消(按Esc鍵)則返回RTCAN。acedEntSel()函數可以和acedInitGet()函數結合使用,如前面執行個體程式碼片段所示。acedEntSel()函數在ObjectARX中是怎樣使用的呢?AutoCAD把圖形作為資料庫來處理。在每個開啟的AutoCAD圖形中的每一個實體都有一個唯一的AutoCAD資料庫物件ID與之關聯。通過其對象的ID號我們可以開啟AutoCAD實體,確定要處理什麼類型的實體。然後我們可以使用實體的獲得和設定方法(函數)來操作實體。一旦有了一個ads_name對象,我們就能得到關聯的AutoCAD資料庫物件ID(資料類型為AcDbObjectId)。
下面就是得到一個AutoCAD資料庫物件ID的函數定義:ads_name對象是使用acedEntSel()函數選擇實體成功的結果。
下面是一段使用acedEntSel()函數的ObjectARX代碼:該函數用pEnt返回AcDbEntity指標,注意是怎樣用acedEntSel()函數選擇AutoCAD實體的。
3. acdbEntLast()[ads_entlast()]、acedNEntSel()[ads_nentsel()]、acedNEntSelP()[ads_nentselp()]
在ADS中還有幾個其他的實體選擇函數:acdbEntLast()、acedNEntSel()和acedNEntSelP()。
先看一下acdbEntLast()函數。AutoCAD始終知道最近產生的一個實體,該實體可以通過acdbEntLast()函數找到。acdbEntLast()函數的定義如下:
acdbEntLast()函數在圖形中找出最近的實體,並把圖形資料庫中最近的(未刪除的)主實體名存入result。即使最近的實體在螢幕之外或在凍結的層上也能選到。最近的實體是指最近建立的實體,所以acdbEntLast()函數可用於獲得剛加進AutoCAD資料庫中實體的實體名。如果acdbEntLast()函數調用成功,返回RTNORM,否則返回RTERROR。
對於塊和多段線複雜實體,ADS函數acedNEntSel()和acedNEntSelP()(代表嵌套的實體選擇)將選擇屬性(假定選擇了塊實體,且塊的屬性已定義)和頂點資訊(假定選擇了多段線)。與acedNEntSel()函數相比,Autodesk推薦使用acedNEntSelP()函數。acedNEntSelP()函數比acedNEntSel()函數增加了更新的內容,所以我們只討論acedNEntSelP()函數。
下面是acedNEntSelP()函數的定義:這是一個帶有許多參數的複雜函數。acedNEntSelP()函數暫停運行等待使用者輸入,並傳回一個實體名(存入entres)和一個用於選擇實體的點(存入ptres)。參數pickflag的值為FALSE或TRUE,用於指定acedNEntSelP()函數是否可以人機互動(即允許使用者選擇與輸入焦點對應的點)。如果pickflag的值為FALSE,acedNEntSelP()函數提示使用者指定實體,參數ptres的初始值被忽略了。如果pickflag的值為TRUE,ptres的初始值被用於選擇實體。我幾乎未使用過pickflag為TRUE。如果需要其他參數的說明,請參閱ADSRX文檔。如果str為NULL,AutoCAD就用標準的“Select objects:|”
提示。我最感興趣的參數是entres,其中存放一個頂點或一個屬性。
3.9 應用程式執行個體CH3_1要點的進一步分析
在應用程式執行個體CH3_1.ARX中,就在建立第一個圓之後使用了acedGetXXX()類函數。現在再來看一看這些函數。在while迴圈中,我們詢問使用者是否需要再畫一個圓。只要使用者不回答“No”,就會不斷地要求使用者畫圓。讓我們來觀察一下while迴圈的前兩行:
acedInitGet()函數中的關鍵字表是“Yes No”,第一個參數是NULL,所以我們能夠接受空輸入。我們本可以使用RSG_NONULL迫使使用者打入“Y”或“N”。在一般情況下,AutoCAD的預設選項用角括弧表示,這裡是<Yes>。如果使用者輸入“Y”,字串kw的值將為“Yes”。如果使用者輸入“N
”,kw的值將為“No”。然而,如果使用者只按Enter鍵,kw將為一空串。這種情形由switch語句的RTNONE case處理,把“Yes”複製到kw緩衝區。記住acedInitGet()函數只適用於緊跟其後的acedGetXXX()類函數。再次運行該應用程式時,試著輸入一些不是“Y”或“N”的字母,看看會發
生什麼。你會重複地得到提示直至按下Y、N或Enter鍵。
如果使用者要再畫圓,程式就進入if語句的else分支,並要求使用者選擇圓心,如下面的代碼所示:
這裡,我們使用RSG_NONULL調用acedInitGet()函數,因為我們要使用者選擇一個圓心。在下一句的acedGetPoint()函數調用中,注意第一個參數是NULL。記住在acedGetPoint()函數中,第一個參數可以用作選點的參考點。這是第一個點,所以我們使用NULL。當要求選擇圓心時試著按Enter
鍵,會重新提示選擇圓心。
當輸入圓半徑時,我們使用acedGetDist函數替代acedGetPoint()函數,因為acedGetDist函數的響應允許我們選擇一個點或從鍵盤輸入一個距離值。下面是使用acedGetDist()函數的代碼:
注意我們怎樣用RSG_NONULL、RSG_NOZERO和RSG_NONEG的組合來調用acedInitGet()函數。這是因為我們要使用跟在acedInitGet()函數設定之後的acedGetDist()函數。acedGetDist()函數中的第一個參數是cp,這是我們前面選定的點。從點cp開始會畫出一條橡皮筋線。當使用者選定第二點後
,從點cp到第二點之間的距離被存入變數rad,這是一個ads_real資料類型的變數。然而,使用者還可用鍵盤敲入數值。我們不允許零和負數輸入,這就是為什麼在調用acedInitGet()函數時還包括RSG_NOZERO和RSG_NONEG的原因。
在使用者定義的帶有一個ads_name型別參數的printEntInfo()函數中,我們調用acdbEntGet()函數檢索結果緩衝區鏈表。如果調用成功,結果緩衝區鏈表會包含實體的資料結構,如下列程式碼片段所示:
我們使用rbEnt->rbnext欄位來遍曆鏈表,並檢測rbEnt->restype欄位的值,這一欄位表示儲存在rbEnt->resval欄位中的資料類型。
3.10 選擇集
一個選擇集就是AutoCAD當前圖形中的一組實體,通過名字加以引用,在這裡是一個ads_name對象。選擇集非常類似於AutoCAD實體的“組(group)”。一旦擁有一個選擇集,就可以確定組成選擇集的實體數量(也稱為選擇集長度引用)。既然我們知道了選擇集的長度,我們就可以使用迴圈機制遍曆選擇集中的每個實體,並讀取和修改實體。一個實體在一個選擇集中只能表示一次,然而一個實體可以屬於多個選擇集。如果我們有一個選擇集ss1,並希望向選擇集中添加一條直線實體,我們可以手工選擇直線實體。按規則我們可以多次選擇直線實體,但在選擇集中多次選擇的實體只表示一次。假定我們現在建立第二個選擇集ss2,並選擇同一條直線實體,那麼該實體就被加到第二個選擇集中。現在直線實體出現在ss1和ss2兩個選擇集中,但在每一個選擇集中直線實體只表達一次。
選擇集是實體的有名集合,選擇集中的實體由AutoCAD使用者手工選擇或依據實體特性加入。依據特性選擇實體,如選擇“parts”層上半徑小於0.25的所有圓,這允許我們把它們的半徑改為0.375。選擇集甚至可以選擇被凍結的圖層上的實體。選擇集也可以是空的—它只是存放AutoCAD實體的一個容器,就像放糖果的紙袋一樣,如果你沒有在裡面放糖果或者你已把它們全部吃光,紙袋就空了。表3-6列出了處理選擇集的ADS函數。
表3-6 ADS的選擇集合函式
選擇集合函式 說 明 等效的AutoLISP函數
acedSSGet() 選擇要加到選擇集中的實體 ssget
acedSSLength() 返回指定選擇集的長度 sslength
acedSSAdd() 添加實體到已有選擇集或建立一空選擇集 ssadd
acedSSDel() 從已有選擇集中刪除實體 ssdel
acedSSName() 在選擇集中檢索出實體的ads_name ssname
acedSSMemb() 測試某實體是否為選擇集的成員 ssmemb
acedSSFree() 釋放選擇集 —
1. acedSSGet()[ads_ssget()]
選擇集是ads_name對象,為了把實體加入到指定的選擇集,必須使用acedSSGet()函數選擇實體,或者假如預Crowdsourced Security Testing道實體名,則可使用acedSSAdd()函數。
下面是acedSSGet()函數的定義:acedSSGet()函數返回一個選擇集,當指定了一種AutoCAD選模數式時就獲得該選擇集,選模數式可以通過AutoCAD使用者的提示或過濾圖形資料庫指定。使用acedSSGet()函數有多種方式。首先我們將說明函數中的各個參數,然後闡述使用acedSSGet()函數的各種方式。
參數str是指定實體選模數式的可選字串。參數pt1和pt2指定若干選模數式的可選點。對於polygon(多邊形)或fence(欄選)選模數式選項,參數pt1還可以是一個含有多個點的結果緩衝區鏈表。參數filter是一個可選的結果緩衝區鏈表,能使acedSSGet()函數過濾圖形以選擇具有某種類型與(或)具有某種特性的實體。無論你使用哪一種模式來獲得選擇集,都用參數ss來標識選擇集的名稱。參數str指定要使用哪一種選模數式,它可以是表3-7所列字串之一。
從表3-7可以看出有多種選擇集的選模數式,如果想探討所有的選項,可參閱協助文檔。一般我只做兩件事:讓使用者選擇實體(NULL選項)或根據特性選擇實體(“X”選項)。
下面是一些有代表性的調用acedSSGet()函數的程式碼。如acutBuildList()函數調用樣本那樣,對於多邊形選項CP和WP(但不包括F)會自動封閉列出的點,不必把終點指定為起點。
表3-7 acedSSGet()函數的選模數式選項:參數str的值
值(選模數式) 說 明
NULL 單點選擇(指定pt1)或使用者選擇(pt1也是NULL)
“I” 指定PICKFIRST實體集
“C” Crossing選模數式
“CP” Crossing polygon選模數式
“F” Fence(或open polygon)選模數式
“L” Last選模數式,選擇最近產生的實體
“P” Previous選模數式,選擇上一個選擇集
“W” Window選模數式
“WP” Window Polygon選模數式
“X” 只用於過濾選模數式
“G” Groups選模數式
“:$” Prompts supplied選模數式
“:?” “Other” callbacks選模數式
“:D” Duplicates allowed選模數式
“:E” Everything in aperture選模數式
“:K” Keyword callbacks選模數式
“:N” Nested選模數式
“:S” 單一對象選模數式
“.” 使用者選模數式
“#” 非幾何選模數式(all、last、previous)
“A” All選模數式
“B” BOX選模數式
“M” Multiple選模數式
3.10.1 選擇集的過濾
為了使用選擇集過濾模式,參數str必須指定為“X”。選擇集過濾模式允許依據特性選擇實體。過濾參數是一個結果緩衝區表,在這裡給acedSSGet()函數詳細說明了要使用的實體種類和特性種類。如果過濾參數是NULL,並且參數str為“X”,則選擇集ss將包含當前AutoCAD圖形中的每一
個實體,而不考慮實體是否在凍結的層上。函數調用如下:
現在選擇集ss將包含當前AutoCAD圖形中的每一個實體。那麼我們怎樣選擇資料庫中所有的圓實體呢?為了有效地使用選擇集,必須知道DXF組碼。首先我們必須構造一個結果緩衝區,但因為我們只尋找一種實體,所以我們可以使用acutNewRb()函數來建立該結果緩衝區。下面是一段代碼實
例:
下面的程式碼執行個體產生由某一圖層上所有的實體組成的選擇集。圖層的DXF組碼是8。
好了,下面我們舉一個稍微複雜一點的例子,假定我們要選擇“parts”層上的所有圓。這是一個利用acutBuildList()函數構造結果緩衝區表後再傳給acedSSGet()函數的執行個體。
3.10.2 選擇集的關係過濾
在選擇集中可以使用關係運算子。例如:我們要選取“parts”圖層上半徑大於等於2.0的所有圓。在預設情況下,acedSSGet()函數選取滿足過濾表中所有條件的實體。過濾時每兩個項之間的隱含關係是“相等(equals)”。對於數值型組碼(整數、實數、點和向量),藉助在結果緩衝區中包
含一個說明關係運算子的特殊組碼“-4”可以指定其他的關係運算。該運算子應用於緊跟其後的結果緩衝區項。關係運算子由字串指定。表3-8列出了所有的關係運算子。
關係運算子可以用執行個體來很好地說明,繼續我們所舉的例子:選取“parts”圖層上半徑大於等於2.0的所有圓。其程式碼如下:
表3-8 選擇集過濾表的關係運算子
關係運算子 說 明
“*” 任意(始終為真)
“=” 等於
“!=” 不等於(C/C++)
“/=” 不等於(AutoLISP)
“<>” 不等於
“<” 小於
“<=” 小於或等於
“>” 大於
“>=” 大於或等於
“&” 按位“與”(只用於整數組碼)
“&=” 按位等於(只用於整數組碼)
3.10.3 選擇集的條件過濾
除關係測試外,我們還可以使用條件運算子。表3-9列出了所有的選擇集條件運算子。
表3-9 選擇集過濾表的條件運算子
起始運算子 中間內容 終止運算子
“<AND” 一個或多個操作對象 “AND>”
“<OR” 一個或多個操作對象 “OR>”
“<XOR” 兩個操作對象 “XOR>”
“<NOT” 一個操作對象 “NOT>”
選擇集的條件運算子允許我們執行像選擇圖形中半徑為1.0的所有圓和“parts”圖層上的所有直線這一類的選擇集操作。編程後代碼如下:
本例有助於掌握選擇集的關係過濾和條件過濾。
3.10.4 選擇集的擴充實體資料過濾
從R11版本起,AutoCAD就有了一個為實體添加資料的機制,稱為擴充實體資料(xdata)。我不準備深入討論擴充實體資料,因為從AutoCAD R13 c4a起,在AutoCAD R14和以後的版本中引入了一種新的機制稱為XRecords,這在後面的章節中將會加以討論。擴充實體資料通常是由外部應用程式附加到一個對象上的文本串、數值、3D點、距離、層名或其他資料。擴充資料的大小是每個實體16KB。注意XRecords是不附加於任何實體的,因此XRecords的存在並不需要實體的存在。藉助在過濾表中使用-3組碼標記指定應用程式名稱,就可以檢索擴充資料。acedSSGet()函數返回登記有特定名稱的擴充資料的實體集合,acedSSGet()函數並不檢索單個擴充資料項目(組碼範圍為1000~2000)。
下面為選擇所有實體的程式碼片段,所有實體均有以其應用程式ID號為“APPNAME”註冊的擴充資料:在ADSRX文檔中,有關擴充實體資料的資訊還有很多,但由於AutoCAD R14和以後的版本使用XRecords,因此,在後面的章節中我們將詳細討論XRecords。
3.10.5 轉換矩陣和選擇集
acedXformSS()[ads_xformss()]
藉助acedXformSS()函數,選擇集可以使用轉換矩陣。利用轉換矩陣,可以變比、移動、旋轉或鏡像選擇集中的實體。這可以用適當的矩陣元素設定調用acedXformSS()函數來實現,而不是迴圈整個選擇集並執行acedCommand()/acedCmd()函數來變比、移動、旋轉或鏡像每一個實體。在這裡我不想完整地討論矩陣代數學。ObjectARX已經定義了處理矩陣代數的數值函數(見AcGeMatrix2d和AcGeMatrix3d矩陣操作類)。前面講過轉換矩陣是一個具有ads_real資料類型的4×4階數組。矩陣的前三列確定比例和旋轉,第四列是一個平移向量。矩陣的最後一行其值規定為[0 0 0 1],傳遞ads_matrix型參數的函數忽略了此值。ADSRX為平移操作定義符號T,如下所示:
acedXformSS()函數的定義如下:acedXformSS()函數將一個轉換矩陣genmat應用於由ssname指定的選擇集。參數genmat是一個4×4階矩陣。如果genmat沒有均勻變比,acedXformSS()函數返回RTERROR。把轉換矩陣應用於選擇集是不使用acedCommand()、acedCmd()或acdbEntMod()函數即在選擇集中變比、旋轉或移動實體的一種方法。如果acedXformSS()函數調用成功返回RTNORM,否則返回RTERROR。
下面是一段初始化矩陣的程式碼:
在上面的函數中,我們使用嵌套的for迴圈初始化了ads_matrix類型的參數id(各元素的值為0),然後再用一個for迴圈初始化了元素0,0 1,1 2,2和3,3使其值為1。現在矩陣被初始化為恒等矩陣。觀察如下由Tx、Ty和Tz決定的平移矩陣:
改變第四列中的前三個元素的值並應用acedXformSS()函數,選擇集中的所有實體將沿X、Y和Z方向移動適當的距離。注意,如果只需要在X方向移動,那麼只需改變矩陣中的Tx值。
當處理選擇集和acedXformSS()函數時,所有的比例係數必須相等,即Sx=Sy=Sz。還有像旋轉矩陣之類的其他2D和3D操作(參閱ADSRX和ObjectARX文檔)。現在讓我們來看一個用於選擇集操作的轉換矩陣執行個體。
這是要對選擇集實施變換的矩陣,X、Y和Z方向的縮放係數是0.5,選擇集的移動距離是(20.0,5.0)。
下面是程式碼:
3.10.6 選擇集的操作
既然我們已經看到有各種方法建立選擇集,現在就讓我們來看看操作這些選擇集的函數。也許我們要知道的第一件事是有多少實體組成了該選擇集。
1. acedSSLength()[ads_sslength()]
acedSSLength()函數返回組成選擇集的實體個數。
其定義如下:acedSSLength()函數返回一個長整數len,表示選擇集sname中含有的實體個數。結果是特定實體的個數,不管選擇集是怎樣選取的,選擇集不包含重複的實體。如果acedSSLength()函數調用成功,返回RTNORM,否則返回一個錯誤碼。acedSSLength()函數通常和for迴圈連用,如下常式序代
碼所示:
2. acedSSName()[ads_ssname()]
在上述代碼中,如果acedSSLength()函數返回的變數len的值大於0,那麼我們就能夠得到一個由長整型變數i表示在選擇集中位置的特定實體。為得到在選擇集特定位置上的實體,可以使用acedSSName()函數。表示實體在選擇集中位置的序號從0開始,第一個實體的序號是0。
下面是acedSSName()函數的定義:acedSSName()函數選取選擇集ss中位置序號為i的實體,並在entres中返回實體名。實體從0開始編號,所以i必須是非負的且不大於選擇集中最後一個實體的序號(acedSSLength(ss)-1)。在用acedSSGet()函數獲得的選擇集中的實體名總是主實體名。acedSSName()函數不能獲得子實體名(例如塊的屬性和多段線的頂點)。如果acedSSName()函數調用成功,返回RTNORM,否則返回一個錯誤碼。
在過去,就ADS和選擇集而言,函數acedSSLength()返回選擇集的長度。一旦有了選擇集的長度,就能夠使用for迴圈通過選擇集,並使用acedSSName()函數獲得實體名。一旦有了實體名,就能使用acdbEntGet()函數獲得實體資料,該函數返回結果緩衝區鏈表形式的實體資料。然後可以修改結果緩衝區表,調用acdbEntMod()函數改變實體資料庫,如有必要可以調用acdbEntUpd()函數更新實體的表達。正如前文所述,ObjectARX處理實體資料庫有更好的機制,其中一些在上面的程式碼片段中已有暗示。現在趨向於不再使用acdbEntGet()、acdbEntMod()和acdbEntUpd()函數。然而,我們仍然使用選擇集。
3. acedSSFree()[ads_ssfree()]
在上述程式碼片段的最後一行,調用了acedSSFree()函數。當處理選擇集時,在操作完成後釋放選擇集是很重要的,因為AutoCAD只能同時開啟有限的選擇集個數(最大為128)。
下面是acedSSFree()函數的定義:acedSSFree()函數釋放由sname指定的選擇集。該選擇集必須預先通過調用acedSSGet()或acedSSAdd()函數獲得。稍後我們將討論acedSSAdd()和acedSSDel()函數。
ADSRX應用程式不能同時開啟多於128個的選擇集。如果達到了此極限,AutoCAD拒絕建立更多的選擇集。同時,我們不推薦使用大量的選擇集。而應該保持一個合理的選擇集數目,並儘可能調用acedSSFree()函數釋放無用的選擇集。如果acedSSFree()函數調用成功,返回RTNORM,否則返回一個錯誤碼。
4. acedSSAdd()[ads_ssadd()]
acedSSAdd()函數建立一個新的選擇集或把一個實體添加到一個已存在的選擇集中。
acedSSAdd()函數的定義如下:參數ename指定一個實體,sname指定一個選擇集。如果ename和sname均為空白指標(NULL),acedSSAdd()函數建立一個沒有實體的新選擇集(以後可以添加實體的空選擇集),其名由result設定。如果ename為一有效實體,但sname是NULL,則acedSSAdd()函數建立一個新的選擇集,其中包含一個實體ename,選擇集名為result。如果ename指定了一有效實體,sname指定了一已有的選擇集,acedSSAdd()函數把實體ename添加到由sname指定了的選擇集中。在所有情形下,acedSSAdd()函數把result設定為建立的或更新的選擇集名。如果由ename指定的實體已經在sname指定的選擇集中,acedSSAdd()函數就不考慮這種請求,也不報告錯誤。參數sname和result可以指定同一個選擇集。這是添加實體ename到一個已有的選擇集中去的最直觀的方法。調用acedSSAdd()函數且sname參數為NULL建立的每一個選擇集,以後必須調用acedSSFree()函數來釋放。這一點也適用於空選擇集(當ename也是NULL時)。如果acedSSAdd()函數返回一錯誤碼,就不建立新的選擇集。如果acedSSAdd()函數調用成功,返回RTNORM,否則返回一個錯誤碼。
5. acedSSDel()[ads_ssdel()]
用acedSSDel()函數從一個已有的選擇集中刪除實體。acedSSDel()函數的定義如下:
acedSSDel()函數從選擇集ss中刪除由ename指定的實體。實體名和選擇集名必須對當前圖形有效。
6. acedSSMemb()[ads_ssmemb()]
最後,使用acedSSMemb()函數可以檢驗一個實體是否在一個已有的選擇集中。acedSSMemb()函數的定義如下:
acedSSMemb()函數測試實體ename是否是選擇集ss的一員。實體名和選擇集名必須對當前圖形有效。如果acedSSMemb()函數找到ename,返回RTNORM,如果沒有找到,則返回RTERROR。
3.11 資料類型轉換函式
有時會要求轉換資料類型,特別是當使用acutPrintf()函數在命令列提示使用者時。表3-10列出了ADS轉換函式。
表3-10 常用的ADS轉換函式
函 數 說 明
acdbRToS() 轉換ads_real類型的數值為字串
acdbAngToS() 按格式轉換角度為字串
acutToUpper() 把字元轉換為大寫
acutToLower() 把字元轉換為小寫
acedTrans() 把點或位移從一個座標系轉換到另一個座標系
1. acdbRToS()[ads_rtos()]
acdbRToS()函數的定義如下:
acdbRToS()函數按照unit和prec(精度)設定的格式把val轉換為字串,結果放入str,str必須指向足以容納格式化字串的一塊緩衝區。參數unit選取轉換後字串的單位制,其值應符合AutoCAD系統變數LUNITS允許的取值範圍(1~5,參閱AutoCAD線上協助中的“系統變數”)。如果unit
的值為-1,acdbRToS()函數使用LUNITS的當前值。參數prec選取字串中包含的小數位元。如果prec的值為-1,acdbRToS()函數使用AutoCAD系統變數LUPREC的當前值。AutoCAD尺寸標註變數DIMZIN的當前值控制acdbRToS()函數是否把前置字元為零和結尾零寫入str。如果acdbRToS()函數調用成功
,返回RTNORM,否則返回一個錯誤碼。
2. acdbAngToS()[ads_angtos()]
acdbAngToS()函數的定義如下:
acdbAngToS()函數按照unit和prec(精度)設定的格式把v轉換為字串,參數v以弧度為單位,結果放入str,str必須指向足以容納格式化字串的一塊緩衝區。字串的大小取決於請求模式和精度,通常15位元組就足夠了。參數unit選取角度轉換後的單位制,其值應符合AutoCAD系統變數AU
NITS允許的取值範圍。此外,如果設定unit的值為-1,acdbAngToS()函數使用AUNITS的當前值。參數prec選取字串中包含的小數位元。如果prec的值設定為-1,acdbAngToS()函數使用AutoCAD系統變數AUPREC的當前值。AutoCAD尺寸標註變數DIMZIN的當前值控制acdbAngToS()函數是否把
前置字元為零和結尾零寫入str。AutoCAD使用者指南說明了DIMZIN如何取值。如果acdbAngToS()函數調用成功,返回RTNORM,否則返回一個錯誤碼。
3. acutToUpper()[ads_toupper()]和acutToLower()[ads_tolower()]
acutToUpper()和acutToLower()函數的定義如下:
這兩個函數分別把字元參數(ASCII碼整數值)轉換為大寫或小寫字元。
4. acedTrans()[ads_trans()]
acedTrans()函數的定義如下:
acedTrans()函數把座標點或位移量從一個座標系統轉換到另一個座標系統。參數pt既可解釋為一個三維點,也可解釋為一個三維位移向量。參數from指定pt所在的座標系統,參數to指定轉換後的座標系統。如果參數disp不等於0,則pt被看作是一個位移向量,否則pt是一個點。說明acedT
rans()函數最好的方法是通過例子。AutoCAD使用的座標系統代碼為:1表示WCS(全局座標系統),2表示當前的UCS(使用者座標系統)。AutoCAD還定義了2和3,這些值我很少使用,你可以查閱有關文檔。
下面是一段從UCS轉換到WCS的程式碼:
這是第一段沒有使用結果緩衝區指標的程式碼。在函數中聲明結果緩衝區類型的變數為局部變數,在函數退出時會自動清除,無需調用acutRelRb()函數。然而,請注意必須在resbuf 變數的前面放上&運算子,因為acedTrans()函數需要一個指向 resbuf 結構的指標(或地址)。
3.12 應用程式執行個體CH3_2
在本應用程式中,我們再次示範了acedGetXXX()類函數的使用,還示範了選擇集合函式的使用。當迴圈通過選擇集時,還示範了怎樣利用結果緩衝區鏈表改變選擇集中實體的特性。在一個純粹的ObjectARX應用程式中,改變實體的特性是由相應實體類中的get和set函數完成的。我們先來觀察
應用程式執行個體CH3_2的程式碼,然後討論其要點。CH3_2.CPP檔案內容如下:
使用者定義命令CEL的CH3_2Commands.cpp檔案內容如下:
3.13 應用程式執行個體CH3_2要點分析
本應用程式能選取一組實體,並把這些實體的圖層改為選中的目標實體所在的圖層。雖然在AutoCAD R14/2000中已經存在類似的命令,但我要說明的只是選擇集的使用。在該應用程式中,要求使用者選取組成選擇集srcSS的實體,如下所示:
然後,接著調用acedEntSel()函數選取目標實體,如下所示:
如果一切正常,最後將調用使用者定義的chgEntsLyr()函數,實現程式的全部動作。該函數要求有兩個參數:一個為實體,另一個為選擇集。注意它們都是ads_name類型的資料。我們必須提取目標實體的資料,這可以調用acdbEntGet()函數來實現,該函數返回指標rbTargEnt指向的結果緩衝
區鏈表。我們使用結果緩衝區指標rbTrav遍曆該鏈表,在此提取層名並拷貝到lyrName。不要使用結果緩衝區指標rbTargEnt來遍曆該鏈表,因為在完成該操作後,無法釋放由acdbEntGet()函數分配的記憶體—一個典型的記憶體流失錯誤,極其平常。下一步是使用acedSSLength()函數得到選擇
集的長度,之後就進入了for迴圈。在for迴圈中,使用acedSSName()函數得到實體名,並在ssEntName中返回。我們再一次使用結果緩衝區指標rbTrav遍曆該鏈表,當到達圖層結果緩衝區時,把rbTrav->resval.rstring的值改為lyrName。注意我們為何使用acdbEntMod(rbSSEnt)函數更新實
體。使用結果緩衝區rbSSEnt更新實體是因為rbSSEnt是結果緩衝區鏈表,而rbTrav只是用來遍曆該鏈表的結果緩衝區指標。在for迴圈開始下一輪迴圈之前,釋放rbSSEnt緩衝區。一旦從函數調用返回,在退出應用程式之前,調用acedSSFree()函數。可別忘了釋放選擇集。
3.14 應用程式執行個體CH3_3
本應用程式示範了怎樣對選擇集使用ads_matrix資料類型的矩陣和怎樣使用對ads_matrix資料類型進行操作的選擇集合函式acedXformSS()。利用矩陣,可以比例變換、旋轉、鏡像、平移,可以繞X、Y或Z軸旋轉。在該應用程式中,我們僅樣本了比例變換。CH3_3.CPP檔案內容如下:
使用者定義命令TSS的CH3_3Commands.cpp檔案內容如下:
3.15 應用程式執行個體CH3_3要點分析
在tss()(轉換選擇集)函數中,有一個ads_matrix資料類型的變數xform和一個選擇集ssGrp。使用acedSSGet()函數請求使用者選擇一組實體。如果選擇成功,將把實體放在選擇集ssGrp中。然後,使用acedGetReal()函數請求使用者輸入一比例係數,注意我們是怎樣初始化acedInitGet()函數的
。既然我們有了一個有效選擇集和一個比例係數,我們就可以調用使用者定義的ident_init()函數初始化矩陣,這是一個嵌套的for迴圈,把所有的矩陣元素初始化為0.0。然後再用一個for迴圈,把前三列對角線上的元素初始化為1.0。被初始化為1.0的元素是id[0][0]、id[1][1]和id[2][
2]。在矩陣完成初始化以後,我們用sf中的比例係數來替代這三個元素的值。我們無須使用for迴圈遍曆選擇集,只需簡單地調用acedXformSS(ssGrp,xform)函數,這將一次改變選擇集中所有實體的比例係數。在ObjectARX的幾何類AcGe-中有許多矩陣轉換操作函數。
有許多ADS函數我未涉及(或未詳細討論),主要是因為這些函數針對ADS/C形式的編程,ObjectARX有它自己的定義,其編程形式完善得多(並非我有偏見)。我未涉及到的ADS函數有:acedDefun()、acedGetFunCode()、acedRegFund()、acedInvoke()、acedGetArgs()、acdbEntDel()、acdbEn
tMod()、acdbEntNext()、acdbEntGet()、acdbEntUpd()和acdbHandEnt(),等等。我所討論的ADSRX函數是在ObjectARX中使用可能性最大的函數。
我們已經有了ADSRX的基礎知識,下面繼續學習第4章,通過瞭解AutoCAD的資料庫和實體結構開始探討ObjectARX。