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()宏。要测试一个ad
s_name对象的值是否有效,可以用acdbNameNil()宏。下面为一段代码实例:
2. acedEntSel()[ads_entsel()]、acdbEntGet()[ads_entget()]、acdbEntMod()[ads_entmod()]、acdbEntUpd()[ads_entupd()]
请牢记选择集也是ads_name对象(我知道这可能引起不小的混淆)。我们很快就会讨论选择集。前文谈到ADS和ObjectARX在获得和更改实体数据的方法上是完全不同的,在后面的章节中我们将讨论ObjectARX的方法。在ADSRX中,使用acdbEntGet()函数获得实体数据,使用acdbEntMod()和acd
bEntUpd()函数修改和更新实体。下面是这些函数的定义:
用acedEntSel()函数选择的实体将和ads_name对象相关联。在详细讨论acedEntSel()函数之前,先要谈一下acdbEntGet()、acdbEntMod()和acdbEntUpd()函数。acdbEntGet()函数返回一个结果缓冲区的单链表。使用resbuf对象的rbnext字段可以遍历和检测结果缓冲区链表。若rb为一个res
buf结果缓冲区,则语法如下:
之后还可以改变某些结果缓冲区中的值,当最终完成后调用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()函数是否可以人机交互(即允许用户选择与输入点对应的点)。如果pickfla
g的值为FALSE,acedNEntSelP()函数提示用户指定实体,参数ptres的初始值被忽略了。如果pickflag的值为TRUE,ptres的初始值被用于选择实体。我几乎未使用过pickflag为TRUE。如果需要其他参数的说明,请参阅ADSRX文档。如果str为NULL,AutoCAD就用标准的“Select objects:|”
提示。我最感兴趣的参数是entres,其中存放一个顶点或一个属性。
3.9 应用程序实例CH3_1要点的进一步分析
在应用程序实例CH3_1.ARX中,就在创建第一个圆之后使用了acedGet**()类函数。现在再来看一看这些函数。在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()函数只适用于紧跟其后的acedGet**()类函数。再次运行该应用程序时,试着输入一些不是“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()函数选择实体,或者假如预先知道实体名,则可使用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。下面是acedSS
Name()函数的定义:
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
在本应用程序中,我们再次示范了acedGet**()类函数的使用,还示范了选择集函数的使用。当循环通过选择集时,还示范了怎样利用结果缓冲区链表改变选择集中实体的特性。在一个纯粹的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。 |