当前位置:首页 >> 初中教育 >>

第8章 自定义对象


第8章
本章简介 8

自定义对象

?自定义对象的概念。 ?从 AcDbObject 派生对象。 ?从 AcDbEntity 派生自定义实体。

学习要点
? 了解自定义对象的概念及其的应用。 ? 掌握从 AcDbObject 派生对象。 ? 掌握自定义实体的创建方法。

我们在

前面介绍了通过扩充数据方式来存储扩充数据,虽然能满足一定的工程需求,但 是由于最终的扩展数据通过结果缓冲链表的方式存储,缺少面向对象特性,在处理的时候比 较繁琐, 我们完成可以定义自己的类来封装数据, 此种情况下我们需要通 AcDbObject 派生数 据库对象;另外,AutoCAD 是一个通用的 CAD 平台,提供如点、线等通用的对象类型,我 们可以针对行业特征派生自己的实体,如定义螺栓类、管道类等,这些派生的实体除了具有 自己的几何形体外,还包含自己所有的一些数据,如管道的管径、材质等属性。本章我们介 绍一下自定义数据库对象的概念和方法,用户可以根据自己的实际需求派生一套面向行业的 对象类型。

8.1

自定义对象

在介绍自定义对象之前, 我们需要对 AutoCAD 中数据库对象的层次关系有所了解了 解,这有助于我们理解后面的实际应操作,AutoCAD 中数据库对象的层次关系如图 8-1 所示。

1

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

图 8-1AutoCAD 中数据库对象的层次关系 从图 8-1 我们看出所有的数据库对象类都派生自 AcRxObject,该类是所有数据库对 象的基类,它主要实现对象运行时类型识别机制,提供一些用于类型识别的重要函数,它 提供的函数主要有一下几个: n desc() : 静态成员函数,返回指定类的类描述符对象。 n cast(): 返回指定类型的对象。 n isKindOf(): 用于判断对象是否属于指定类或者派生类。 n isA() :返回未知类对象的类描述符对象。 我们在介绍实体操作的时候讲过如何使用这些函数, 这里我们需要在这里介绍这些函 数的实现机制,从 AcRxObject 派生的类都包含一个相应的类描述符对象,用 AcRxClass 类表示, 它包含了运行使类型的识别信息,AcRxObject 的派生类包含一个指向 AcRxClass 对象的指针(gpDesc),可以通过 AcRxObject::desc()获取这个 AcRxClass 对象指针,而 AcRxClass 对象包含一个指向其父对象 AcRxClass 的指针, 这样构成了类的运行时类层次 表,如图 8-2,我们可以调用 AcRxObject::isKindOf()来判断对象是否是从某个类派生 出来。

图 8-2 行时类层次表 在派生自定义类中要实现运行类的识别信息, 也就是要重载上面提到的 desc()、 isKindOf()
2

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

等 函 数 , 这 可 以 通 过 ObjectARX 提 供 的 宏 来 实 现 , 通 过 使 用 类 声 明 宏 ACRX_DECLARE_MEMBERS(CLASS_NAME)可以声明 desc(), cast(), isA()函数,代码如 下: class CMyClass : public AcRxObject { public: ACRX_DECLARE_MEMBERS(CMyClass); …. } 该宏经过编译预处理后被扩展成一下代码: virtual AcRxClass* isA() const; static AcRxClass* gpDesc; static AcRxClass* desc(); static CMyClass * cast(const AcRxObject* inPtr) { return ((inPtr == 0) || !inPtr->isKindOf(CMyClass::desc())) ? 0 : (CMyClass *)inPtr; }; static void rxInit(); 自定义类的静态成员函数 rxInit()用于实现以下初始化操作: n 注册自定义类。 n 创建类的描述对象。 n 将类描述对象添加到类的描述词典中。 在应用程序的初始化函数中必须调用自定义类的静态成员函数 rxInit()来实现自定义类的 初始化,然后调用全局函数 acrxBuildClassHierarchy 把该类添加到 ACRX 运行类层次表中。 另外在应用程序的卸载时需要调用 deleteAcRxClass()把该类从 ACRX 运行类层次表中删 除,应用程序的初始化代码如下: extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt) { switch (msg) { case AcRx::kInitAppMsg: acrxDynamicLinker->unlockApplication(pkt); // 自定义类的初始化 CMyClass::rxInit(); // 把该类添加到 ACRX 运行类层次表中 acrxBuildClassHierarchy(); break; case AcRx::kUnloadAppMsg: // 该类从 ACRX 运行类层次表中删除 deleteAcRxClass(CMyClass::desc()); } return AcRx::kRetOK;
3

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

} 所有永久或者临时的图形对象都实现可绘制接口,封装该接口的对象可以通过火绘制 API 完成绘制, 可显示的对象派生自 AcGiDrawable 类, 该类实现图形系统 (GS) 绘制协议。 AcDbObject 类执行文件操作协议, 从该类派生的对象通过重载文件操作函数可以被保存 为 DWG 或 DXF 文件,或者从 DWG 或 DXF 文件读入。 AcDbEntity 类是实体类,派生自 AcDbObject 类,从该类派生的对象除了可以支持文件 操作外,还可以通过重载绘制函数来按照开发者的要求绘制图形。

8.2 从 AcDbObject 派生
从 AcDbObject 类派生的子类可以支持文件操作,即对象可以从 DWG 或者 DXF 文件中 读写, 也就是可以保存到 DWG 或者 DXF 文件中, 要实现文件读写操作派生类必须重载以下 四个函数: Acad::ErrorStatus dwgInFields(AcDbDwgFiler *filer); Acad::ErrorStatus dwgOutFieds(AcDbDwgFiler *filer); Acad::ErrorStatus dxfInFieds(AcDbDxfFiler *filer); Acad::ErrorStatus dxfOutFieds(AcDbDxfFiler *filer); 以上函数的参数是文件操作类 AcDbDwgFiler 或 AcDbDxfFiler 指针,文件操作类是一个 工具类,用于数据库对象的读写读写, ObjectARX 通过枚举类型 AcDb::FilerType 来检查文件 操方式和类型。 例如当调用 AutoCAD 的 SAVE 命令保存文件时,会调用数据库对象的 dwgOutFieds 函 数, 此时使用 kFileFiler 枚举类型; 而当使用 WBLOCK 命令时, 同样调用 dwgOutFieds 函数, 但使用的枚举类型为 kWblockCloneFiler 和 kIdXlateFiler, 如果调用 UNDO 命令取消操作时候, 会调用数据库对象的 dwgInFields 函数,使用的枚举类型是 kUndoFiler。 向文件操作类对象写入数据的过程中,不需要执行错误检查,文件操作类都有一个成员 函数 getFilerStatus()用于返回类的状态,有时候开发者需要检查文件操作类对象的状态。 在 自 定 义 类 中 重 载 文 件 操 作 函 数 时 , 必 须 首 次 调 用 assertReadEnabled() 或 assertWriteEnabled()函数来检查对象处于正确的打开状态, 然后调用自定义类父类的同名函数 来提供对父类数据的重载。 对于 DWG 文件操作函数 dwgInFields 和 dwgOutFieds,必须按照相同的顺序进行数据的 读写操作,否则派生类数据可能发生混乱。 文件操作类对象可以调用成员函数 readItem()和 writeItem()来读写数据,实际上这 两个函数会被所有支持的数据类型重载,另外还可以调用一些指定了数据类型的读写函数, 如 writeInt32 ) 这些函数在被调用时会自动转换参数的数据类型而忽略数据的实际类型, ( 等, 例如自定义类中包含整型数据,则可以调用 readInt32()和 writeInt32()进行相应的读写操 作。 Acad::ErrorStatus CPipeAttribute::dwgOutFields (AcDbDwgFiler *pFiler) const { // assertReadEnabled () ; //----- Save parent class information first. Acad::ErrorStatus es =AcDbObject::dwgOutFields (pFiler) ; if ( es != Acad::eOk ) return (es) ;
4

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

//----- Object version number needs to be saved first if ( (es =pFiler->writeUInt32 (CPipeAttribute::kCurrentVersionNumber)) != Acad::eOk ) return (es) ; ///写入数据开始 pFiler->writeItem(m_dRadius); pFiler->writeItem(m_dThickness); pFiler->writeItem(m_dDeep); pFiler->writeString(m_cMaterial); //写入数据结束 return (pFiler->filerStatus ()) ; }

Acad::ErrorStatus CPipeAttribute::dwgInFields (AcDbDwgFiler *pFiler) { assertWriteEnabled () ; //----- Read parent class information first. Acad::ErrorStatus es =AcDbObject::dwgInFields (pFiler) ; if ( es != Acad::eOk ) return (es) ; //----- Object version number needs to be read first Adesk::UInt32 version =0 ; if ( (es =pFiler->readUInt32 (&version)) != Acad::eOk ) return (es) ; if ( version > CPipeAttribute::kCurrentVersionNumber ) return (Acad::eMakeMeProxy) ; //读取数据开始 pFiler->readItem(&m_dRadius); pFiler->readItem(&m_dThickness); pFiler->readItem(&m_dDeep); TCHAR *pString=NULL; pFiler->readString(&pString); _tcscpy(m_cMaterial,pString); // 读取数据结束 return (pFiler->filerStatus ()) ; } 对象可以用 DXF 格式来表示,DXF 格式由成对的 DXF 组码和数据构成,组码对应一种 指定的数据类型, 当定义自定义类对象的 DXF 格式时, 函数读写的的一组数据必须是派生类 的数据标记,这个数据标记的 DXF 组码是 100(AcDb::kDxfSubclass),然后是类名的字符串。 对于 DXF 文件操作函数 dxfInFieds 和 dxfOutFieds 也通过文件操作类对象 AcDbDxfFiler 的成员函数 readItem()和 writeItem()来读写数据,用户可以决定数据组是按照一定的顺 序读写还是无顺序读写。如果程序中允许无顺序读写,则在重载函数中必须使用 switch 语句 来选择于 DXF 组码相应的操作, 无顺序读写通常用于那些包含不变的数据域的对象, 而另外 包含可变长度的数组和结构的对象往往采用顺序读写。
5

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

Acad::ErrorStatus CPipeAttribute::dxfOutFields (AcDbDxfFiler *pFiler) const { // 检查对象处于正确的打开状态 assertReadEnabled () ; //父类数据的重载 Acad::ErrorStatus es =AcDbObject::dxfOutFields (pFiler) ; if ( es != Acad::eOk ) return (es) ; es =pFiler->writeItem (AcDb::kDxfSubclass, _RXST("CPipeAttribute")) ; if ( es != Acad::eOk ) return (es) ; //----- Object version number needs to be saved first if ( (es =pFiler->writeUInt32 (kDxfInt32, CPipeAttribute::kCurrentVersionNumber)) != Acad::eOk ) return (es) ; ////写入数据开始 pFiler->writeItem(AcDb::kDxfReal , m_dRadius); pFiler->writeItem(AcDb::kDxfReal+1 , m_dThickness); pFiler->writeItem(AcDb::kDxfReal+2 , m_dDeep); pFiler->writeItem(AcDb::kDxfText ,m_cMaterial); ////写入数据结束 return (pFiler->filerStatus ()) ; } Acad::ErrorStatus CPipeAttribute::dxfInFields (AcDbDxfFiler *pFiler) { assertWriteEnabled () ; //----- Read parent class information first. Acad::ErrorStatus es =AcDbObject::dxfInFields (pFiler) ; if ( es != Acad::eOk || !pFiler->atSubclassData (_RXST("CPipeAttribute")) ) return (pFiler->filerStatus ()) ; //----- Object version number needs to be read first struct resbuf rb ; pFiler->readItem (&rb) ; if ( rb.restype != AcDb::kDxfInt32 ) { pFiler->pushBackItem () ; pFiler->setError (Acad::eInvalidDxfCode, _RXST("\nError: expected group code %d (version #)"), AcDb::kDxfInt32) ; return (pFiler->filerStatus ()) ; } Adesk::UInt32 version =(Adesk::UInt32)rb.resval.rlong ; if ( version > CPipeAttribute::kCurrentVersionNumber ) return (Acad::eMakeMeProxy) ; while ( es == Acad::eOk && (es =pFiler->readResBuf (&rb)) == Acad::eOk ) {
6

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

switch ( rb.restype ) { // 从 DXF 中读取数据 case AcDb::kDxfReal: m_dRadius =rb.resval.rreal ; break ; case AcDb::kDxfReal+1: m_dThickness =rb.resval.rreal ; break ; case AcDb::kDxfReal+2: m_dDeep =rb.resval.rreal ; break ; case AcDb::kDxfText: _tcscpy(m_cMaterial,rb.resval.rstring); break ; default: pFiler->pushBackItem () ; es =Acad::eEndOfFile ; break ; } } if ( es != Acad::eEndOfFile ) return (Acad::eInvalidResBuf) ; return (pFiler->filerStatus ()) ; }

AutoCAD 为撤销操作(UNDO)提供了两中基本的方法,一种是系统默认的自动撤销操 作机制,另外一种是部分撤销操作机制。其中前者通过系统调用函数 dwgOutFields(采用 kUndoFiler 为参数)来复制对象的全部状态来实现,而部分操作机制需要程序对特殊的修改 来读写指定的信息,自动撤销操作通过 assertWriteEnabled()函数来实现。 自定义类中任何修改函数都必须调用函数 assertWriteEnabled , () 用于检查对象是否是用 写的模式打开,当该函数被调用时,首先检查参数 recordModified,如果 recordModified 的值 为 Adesk::kFalse,则不执行任何撤销操作,如果 recordModified 的值为 Adesk::kTrue,则检查 autoUndo 参数,如果参数 autoUndo 为 Adesk::kTrue,则 AutoCAD 将记录对象的状态以便执 行撤销操作,当对象的修改操作完成并关闭对象,操作对象的全部状态将被保存到一个撤销 操作文件中,如果这时的用 UNDO 命令,AutoCAD 调用对象的 dwgInFields()函数把这个 撤销操作文件的内容读入到数据库中。 如果想记录对象的部分状态,自定义类中修改函数都必须在调用函数 assertWriteEnabled ()时将参数 autoUndo 设为 Adesk::kFalse,然后调用 undoFiler::writeItem()(或其他的 undoFiler::writeXXX()函数)把相关的信息保存到撤销操作文件中,如果这时执行 UNDO 操 作,AutoCAD 会调用 applyPartialUndo()读入撤销操作文件中保存的信息。 执行撤销操作时,系统记录当前的状态为重做(REDO)操作做准备,重做操作使用和
7

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

撤销做作相同的文件机制, 即调用 dwgOutFields()函数来记录对象的状态, 不需要编写额外的 代码,如果自定义对象的修改函数实现了部分撤销操作,那么在执行撤销操作的同时为重做 操作做记录,这个过程通常调用相应的 set()函数来事项,它会调用 assertWriteEnabled()来 记录数据。 下面我们结合工程实际说明如何从 AcDbObject 派生自己的类并在程序中使用这个派生 的类。在该例子中我们将为管道定义一个属性类,用于存储管道的属性信息,所定义的属性 类包含以下数据: 表 8-1 管道的属性信息 属性 管径 壁厚 埋深 材质 数据类型 double double double TCHAR

创建一个新的工程 CH081,通过 ObjectARX 工具条的按钮 (第二个) 启动 Autodesk class , Explorer,选择工程 CH081,右键单击,在弹出的右键菜单中选择 Add an ObjectDBX Custom Object…,如图 8-3 所示。

图 8-3 添加自定义类 在弹出的对话框中,从 AcDbObject 派生管道的属性类 CPipeAttribute,如图 8-4.

图 8-4 管道的属性类 在协议页(Protocols) ,选中 DXF Protocol,如图 8-5,其他采用缺省设置。
8

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

图 8-5 重载 DXF 协议 在类定义中添加成员变量: //管径 double m_dRidus; //壁厚 double m_dThickness; //埋深 double m_dDeep; //材质 TCHAR m_cMaterial[128]; 在 CPipeAttribute 类的构造函数中完成数据的初始化,代码如下: CPipeAttribute::CPipeAttribute () : AcDbObject () { m_dRidus = 120.0; m_dThickness = 10.0; m_dDeep = 2.4; _tcscpy(m_cMaterial,_T("水泥管")); } CPipeAttribute 类实现文件读写操作的四个重载函数已经在前面列出,ObjectARX 向导 已经给出了缺省函数的代码,我们只需要添加用户自定义部分的数据。 CPipeAttribute 完成以后, 需要定义一个命令来使用这个属性类, 在工程中添加命令, 将 CPipeAttribute 对象实例作为扩展数据存储到实体的扩展字典中,代码如下: static void CSCH081AddAttribute(void) { AcDbObjectId dictObjId,eId, attId; AcDbDictionary* pDict; //选择管道(多义线) ads_name en; ads_point pt;
9

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

if ( {

acedEntSel(_T("\n 选择管道(多义线): "), en, pt)!= RTNORM) acutPrintf(_T("\n 选择失败,退出: ")); return ;

} // 打开对象 acdbGetObjectId(eId, en); AcDbEntity * pEnt; acdbOpenObject(pEnt, eId, AcDb::kForWrite); if(!pEnt->isKindOf (AcDbPolyline::desc ())) { acutPrintf(_T("\n 选择的不是管道(多义线) ,退出: " )); return ; } // 判断实体的扩展词典是否创建,如果没有则创建 dictObjId = pEnt->extensionDictionary(); if( dictObjId == AcDbObjectId::kNull ) { pEnt->createExtensionDictionary(); } // 获取实体的扩展词典 dictObjId = pEnt->extensionDictionary(); pEnt->close(); // 判断词典中的属性是否创建 CPipeAttribute* pAttribute; acdbOpenObject(pDict, dictObjId, AcDb::kForWrite); pDict->getAt (_T("属性"),attId); if(attId!= AcDbObjectId::kNull )//如果已经创建则输出数据 { acdbOpenObject(pAttribute, attId, AcDb::kForRead); acutPrintf(_T("\n 管径:%4.2f " ),pAttribute->m_dRadius); acutPrintf(_T("\n 壁厚:%4.2f " ),pAttribute->m_dThickness ); acutPrintf(_T("\n 埋深:%4.2f " ),pAttribute->m_dDeep ); acutPrintf(_T("\n 材质:%s " ),pAttribute->m_cMaterial ); } else { //没有则创建属性 pAttribute = new CPipeAttribute(); pDict->setAt(_T("属性"), pAttribute, attId); }
10

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

//关闭对象 pDict->close(); pAttribute->close(); } 将工程编译、运行可以看到 CPipeAttribute 能够将属性数据保存在多义线的扩展字典 中。

8.3 从 AcDbEntity 派生
AcDbEntity 是从 AcDbObject 派生而来, 因此 AcDbEntity 的派生类必须重载 AcDbObject 类所有必须重载的函数, 如文件操作函数, 然后根据需要重载 AcDbObject 类的其他虚函数和 AcDbEntity 类的函数。 AutoCAD 利用 worldDraw()和 viewportDraw()函数来显示实体,对任何由 AcDbEntity 派 生的类必须重载 worldDraw()函数,而对于 viewportDraw()函数来说,则是可选的,下面是函 数原型: Virtual Adesk::Boolean AcDbEntity::worldDraw(AcGiWorldDraw *pWd); Virtual void AcDbEntity::viewportDraw(AcGiViewportDraw *pVd); 当 AutoCAD 需要重新生成图形以显示实体时,会用以下方式调用 worldDraw()和 viewportDraw()函数: if(!entity->worldDraw(pWd)) for(每一个相关视口) entity->viewportDraw(void); 函数 worldDraw()用于绘制实体的图形表达部分, 与指定的模型空间和图纸空间的视口内 容无关,然后调用函数 viewportDraw()绘制于视口相关的部分,如果实体的所有图形表示都 与视图相关, 那么函数 worldDraw()必须返回 kFalse,而且必须重载 viewportDraw()函数。 相反, 如果实体没有与视图相关的图形,则 worldDraw()函数返回 kTrue,而且不需要重载 viewportDraw()函数。 在函数 AcDbEntity::worldDraw(AcGiWorldDraw *pWd)中, 有一个指向 AcGiWorldDrawde 指针对象,它是一个 AcGi 几何对象和特征对象的容器类。另外,AcGeWorldDraw 包含其他 两个对象:AcGIiWorldGeometry 和 AcGiSubEntityTraits。 可以通过 AcGiWorldDraw::geometry()获取 AcGIiWorldGeometry 对象,该对象能够通 过制实体图形的基本绘制命令即图形原型将几何对象写到 AutoCAD 的图形缓存中,世界坐 标系中绘制图形原型的函数主要有 Circle、Circular arc、 Polyline、Polygon、Mesh,Shell、 Text、 Xline 和 Ray。 通过 AcGiWorldDraw::subEntityTraits()函数可以返回 AcGiSubEntityTraits 对象,该对 象能够通过特性函数设置图形的属性值,如 Color,Layer,LineType 等。 在 函 数 void AcDbEntity::viewportDraw(AcGiViewportDraw *pVd) 中 , 有 一 个 指 向 AcGiViewportDraw 指针的对象,它也是一个容器对象,它包含 AcGeViewportGeometry, AcGiSubEntityTraits 和 AcGiViewport 等对象。其中,AcGeViewportGeometry 对象提供了与 AcGeWorldGeometry 相同的图形原型列表,同时增加了 polylineEye() , polygonEye(), polylineDc(),PolygonDc()等函数原型,新添的图形原型使用视觉坐标和显示空间坐标来绘制 多段线。AcGiSubEntityTraints 对象的使用方法与 AcGiWorldDraw 相同,AcGiViewport 对象
11

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

提供了用于查找适口变换矩阵和观察参数的函数。

图 8-6 图形的绘制过程 从上面可以看出图形的绘制过程如下: 1、AutoCAD 创建 AcGiWorldDraw 对象。 2、AutoCAD 创建 AcGiViewportDraw 对象。 3、AutoCAD 将 AcGiWorldDraw 对象传给图形对象。 4、图形对象绘制图形(和视口无关) 。 5、AutoCAD 将 AcGiViewportDraw 传给图形对象。 6、图形对象根据视口绘制图形。 n 实现对象捕捉功能 如果自定义实体支持对象捕捉功能, 则需要重载 getOsnapPoints()函数, 打开捕捉方式后, AutoCAD 会调用该函数获取当前捕捉方式下的相应的捕捉点,在实际的开发过程中,开发者 开发的自定义实体如果不需要支持全部捕捉方式,这个时候在重载的 getOsnapPoints()函数中 只需要对支持的捕捉方式进行处理,对其它不支持的捕捉方式返回 eOk,如果用户激活了多 种捕捉方式,AutoCAD 就会为每种捕捉方式调用一次 getOsnapPoints 函数。 Acad::ErrorStatus CSPolyline::getOsnapPoints( AcDb::OsnapMode osnapMode, int gsSelectionMark, const AcGePoint3d& pickPoint, const AcGePoint3d& lastPoint, const AcGeMatrix3d& viewXform, AcGePoint3dArray& snapPoints, AcDbIntArray& /*geomIds*/) const { assertReadEnabled(); Acad::ErrorStatus es = Acad::eOk; if (gsSelectionMark == 0) return Acad::eOk; if ( osnapMode != AcDb::kOsModeEnd && osnapMode != AcDb::kOsModeMid && osnapMode != AcDb::kOsModeNear && osnapMode != AcDb::kOsModePerp
12

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

&& osnapMode != AcDb::kOsModeCen && osnapMode != AcDb::kOsModeIns) { return Acad::eOk; } AcGePoint3d center; getCenter(center); if (gsSelectionMark == (mNumSides + 1)) { if (osnapMode == AcDb::kOsModeIns) snapPoints.append(center); else if (osnapMode == AcDb::kOsModeCen) snapPoints.append(center); return es; } int startIndex = gsSelectionMark - 1; AcGePoint3dArray vertexArray; if ((es = getVertices3d(vertexArray)) != Acad::eOk) { return es; } AcGeLineSeg3d lnsg(vertexArray[startIndex], vertexArray[startIndex + 1]); AcGePoint3d pt; AcGeLine3d line, perpLine; AcGeVector3d vect; AcGeVector3d viewDir(viewXform(Z, 0), viewXform(Z, 1), viewXform(Z, 2)); switch (osnapMode) { case AcDb::kOsModeEnd: snapPoints.append(vertexArray[startIndex]); snapPoints.append(vertexArray[startIndex + 1]); break; case AcDb::kOsModeMid: pt.set( ((vertexArray[startIndex])[X] + (vertexArray[startIndex + 1])[X]) * 0.5, ((vertexArray[startIndex])[Y]
13

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

+ (vertexArray[startIndex + 1])[Y]) * 0.5, ((vertexArray[startIndex])[Z] + (vertexArray[startIndex + 1])[Z]) * 0.5); snapPoints.append(pt); break; case AcDb::kOsModeNear: pt = lnsg.projClosestPointTo(pickPoint, viewDir); snapPoints.append(pt); break; case AcDb::kOsModePerp: vect = vertexArray[startIndex + 1] - vertexArray[startIndex]; vect.normalize(); line.set(vertexArray[startIndex], vect); pt = line.closestPointTo(lastPoint); snapPoints.append(pt); break; case AcDb::kOsModeCen: snapPoints.append(center); break; default: return Acad::eOk; } return es; } 需要说明的对象的交点捕捉方式通过调用 intersectWith() 函数进行处理,而不是 getOsnapPoints 函数。 n 自定义实体的夹点 当使用鼠标选择了一个 AutoCAD 的实体时,AutoCAD 会显示出实体的夹点。如果要求 自 定 义 实 体 支 持 夹 点 编 辑 功 能 , 则 需 要 重 载 getGripPoints() 和 moveGripPointsAt() 函 数,getGripPoints()函数的实例如下: Acad::ErrorStatus CSPolyline::getGripPoints( AcGePoint3dArray& gripPoints, AcDbIntArray& osnapModes, AcDbIntArray& geomIds) const {
14

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

assertReadEnabled(); Acad::ErrorStatus es; if ((es = getVertices3d(gripPoints)) != Acad::eOk) { return es; } gripPoints.removeAt(gripPoints.length() - 1); AcGePoint3d center; getCenter(center); gripPoints.append(center); return es; } 夹点编辑的拉伸功能允许用户通过把选择的夹点移动到新位置来拉伸实体对象, AutoCAD 通过的用 moveGripPointsAt()函数实现夹点的拉伸功能,但对一些特定的实体移 动一些夹点时移动对象而不是拉伸对象,如圆的圆心,在这种情况下 moveGripPointsAt() 函数调用 transformBy()函数来移动对象。 Acad::ErrorStatus CSPolyline::moveGripPointsAt( const AcDbIntArray& indices, const AcGeVector3d& offset) { if (indices.length()== 0 || offset.isZeroLength()) return Acad::eOk; //that's easy :-) if (mDragDataFlags & kCloneMeForDraggingCalled) { mDragDataFlags |= kUseDragCache; } else assertWriteEnabled(); if (indices.length()>1 || indices[0] == mNumSides) return transformBy(AcGeMatrix3d::translation(offset)); AcGeVector3d off(offset); double rotateBy = 2.0 * 3.14159265358979323846 / mNumSides * indices[0]; AcGePoint3d cent; getCenter(cent); off.transformBy(AcGeMatrix3d::rotation(-rotateBy,normal(),cent)); acdbWcs2Ecs(asDblArray(off),asDblArray(off),asDblArray(normal()),Adesk::kTrue); if (mDragDataFlags & kUseDragCache){ mDragCenter = mCenter; mDragPlaneNormal = mPlaneNormal; mDragStartPoint = mStartPoint + AcGeVector2d(off.x,off.y); mDragElevation = mElevation + off.z;
15

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

}else{ mStartPoint = mStartPoint + AcGeVector2d(off.x,off.y); mElevation = mElevation + off.z; } return Acad::eOk; } 当用户调用夹点编辑中的移动、旋转、缩放和镜像操作时,AutoCAD 调用 transformBy() 函数来实现相应的操作。 n 自定义实体的拉伸点 实体的拉伸点集合实际是实体的夹点集合的一个子集,当用户调用 STRETCH 命令时, AutoCAD 调用实体的 getStretchPoints()函数返回其拉伸点,对于大多数实体来说夹点的编辑 模式和拉神点的变价模式是一致的,函数 getStretchPoints()和 moveStretchPointsAt ()只是调用 了重载的 getGripPoints()和 moveGripPointsAt()函数在程序中可以不重载函数 getStretchPoints() 和 moveStretchPointsAt (),他们默认调用 getGripPoints()和 transformBy()。 Acad::ErrorStatus CSPolyline::getStretchPoints( AcGePoint3dArray& stretchPoints) const { assertReadEnabled(); Acad::ErrorStatus es; if ((es = getVertices3d(stretchPoints)) != Acad::eOk) { return es; } stretchPoints.removeAt(stretchPoints.length() - 1); return es; } n 自定义实体的几何变换 AcDbEntity 类提供了两个变换函数,其中,transformBy()函数对实体进行指定的矩阵操 作变换,另外一个为 getTransformedCopy()函数,它首先复制自身,然后再进行指定的矩阵变 换并返回变换后的复制实体。 Acad::ErrorStatus CSPolyline::transformBy(const AcGeMatrix3d& xform) { if (mDragDataFlags & kCloneMeForDraggingCalled) { mDragDataFlags |= kUseDragCache; mDragPlaneNormal = mPlaneNormal; mDragElevation = mElevation; AcGeMatrix2d xform2d(xform.convertToLocal(mDragPlaneNormal,mDragElevation)); mDragCenter = mCenter; mDragCenter.transformBy(xform2d); mDragStartPoint = mStartPoint;
16

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

mDragStartPoint.transformBy(xform2d); mDragPlaneNormal.normalize(); } else { assertWriteEnabled(); AcGeMatrix2d xform2d(xform.convertToLocal(mPlaneNormal,mElevation)); mCenter.transformBy(xform2d); mStartPoint.transformBy(xform2d); mPlaneNormal.normalize(); } return Acad::eOk; } n 自定义实体的相交函数 实体的相交函数主要有两中形式: virtual Acad::ErrorStatus intersectWith( const AcDbEntity* pEnt, AcDb::Intersect intType, AcGePoint3dArray& points, int thisGsMarker = 0, int otherGsMarker = 0) const; virtual Acad::ErrorStatus intersectWith( const AcDbEntity* pEnt, AcDb::Intersect intType, const AcGePlane& projPlane, AcGePoint3dArray& points, int thisGsMarker = 0, int otherGsMarker = 0) const; 第一种形式对两个实体的简单点进行测试,第二种形式在一个投影面上计算交点,这两 种形式都返回在实体自身上的交点。重载 intersectWith 函数应该遵循一下原则: 1. 每个自定义实体都应该能够处理与 AutoCAD 中定义实体如 AcDbLine 等的求交操作 2. 如果自定义实体的intersectWith函数被调用时的实体参数不是AutoCAD 中缺省定义的 实体,应用程序需要把该自定义实体分解为一系列可以识别的 AutoCAD 中缺省实体,然后 对这些分解所得的实体逐一调用 intersectWith 函数。 Acad::ErrorStatus CSPolyline::intersectWith( const AcDbEntity* ent, AcDb::Intersect intType, const AcGePlane& projPlane, AcGePoint3dArray& points, int /*thisGsMarker*/, int /*otherGsMarker*/) const {
17

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

assertReadEnabled(); Acad::ErrorStatus es = Acad::eOk; if (ent == NULL) return Acad::eNullEntityPointer; if (ent->isKindOf(AcDbLine::desc())) { if ((es = intLine(this, AcDbLine::cast(ent), intType, &projPlane, points)) != Acad::eOk) { return es; } } else if (ent->isKindOf(AcDbArc::desc())) { if ((es = intArc(this, AcDbArc::cast(ent), intType, &projPlane, points)) != Acad::eOk) { return es; } } else if (ent->isKindOf(AcDbCircle::desc())) { if ((es = intCircle(this, AcDbCircle::cast(ent), intType, &projPlane, points)) != Acad::eOk) { return es; } } else if (ent->isKindOf(AcDb2dPolyline::desc())) { if ((es = intPline(this, AcDb2dPolyline::cast(ent), intType, &projPlane, points)) != Acad::eOk) { return es; } } else if (ent->isKindOf(AcDb3dPolyline::desc())) { if ((es = intPline(this, AcDb3dPolyline::cast(ent), intType, &projPlane, points)) != Acad::eOk) { return es; } } else { AcGePoint3dArray vertexArray; if ((es = getVertices3d(vertexArray)) != Acad::eOk) { return es; } if (intType == AcDb::kExtendArg || intType == AcDb::kExtendBoth) {
18

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

intType = AcDb::kExtendThis; } AcDbLine *pAcadLine; int i; for (i = 0; i < vertexArray.length() - 1; i++) { pAcadLine = new AcDbLine(); pAcadLine->setStartPoint(vertexArray[i]); pAcadLine->setEndPoint(vertexArray[i + 1]); pAcadLine->setNormal(normal()); if ((es = ent->intersectWith(pAcadLine, intType, projPlane, points)) != Acad::eOk) { delete pAcadLine; return es; } delete pAcadLine; } n 自定义实体的分解 要使AutoCAD 的 BHATCH 和EXPLODE 命令对自定义实体起作用, 则必须重载explode() 函数。自定义的 explode()函数应该能为实体分解为复杂程度小的实体。如果分解以后的实体 不是固有的实体,则函数返回 explodeAgain。这将导致 BHATCH 对所返回的实体递归调用 explode,直到他们分解为 AutoCAD 缺省定义的实体为止。 Acad::ErrorStatus CSPolyline::explode(AcDbVoidPtrArray& entitySet) const { assertReadEnabled(); Acad::ErrorStatus es = Acad::eOk; AcGePoint3dArray vertexArray; if ((es = getVertices3d(vertexArray)) != Acad::eOk) { return es; } AcDbLine* line; for (int i = 0; i < vertexArray.length() - 1; i++) { line = new AcDbLine(); line->setStartPoint(vertexArray[i]); line->setEndPoint(vertexArray[i + 1]); line->setNormal(normal()); entitySet.append(line); } AcDbText *text ;
19

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

if ((mpName != NULL) && (mpName[0] != _T('\0'))) { AcGePoint3d center,startPoint; getCenter(center); getStartPoint(startPoint); AcGeVector3d direction = startPoint - center; if (mTextStyle != AcDbObjectId::kNull) text =new AcDbText (center, mpName, mTextStyle, 0, direction.angleTo (AcGeVector3d (1, 0, 0))) ; else text =new AcDbText (center, mpName, mTextStyle, direction.length() / 20, direction.angleTo (AcGeVector3d (1, 0, 0))) ; entitySet.append (text) ; } return es; } 下面我们通过实例来说明自定义实体的创建,通常我们把自定义实体创建为一个 DBX 工程, 在通过 ObjectARX 向导创建工程的时候可以设置工程的类型, 如图 8-7 所示, 这样工程编译后生成扩展名为 DBX 的文件。

图 8-7 创建 DBX 工程 前面我们从 AcDbObject 类派生了一个管道属性类, 当使用属性类的时候, 需要把属性类 实例保存在实体的扩展词典中,现在我们创建自定义实体,将管道属性数据直接保存为自定 义实体的自身数据,选择从 AcDbPolyline 类派生,而不是直接从 AcDbEntity 类派生,这样可 以已有对象的特性,避免重复性的工作。使用 ObjectARX 向导创建自定义实体类的过程和 AcDbObject 类派生了一个管道属性类一样,我们创建自定义实体类 CPipeLine,如图 8-8。

20

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

图 8-8 创建 CPipeLine 类 CPipeLine 类关于文件操作的部分和 CPipeAttribute 类中的实现过程一样,这里就不再 介绍。 CPipeLine 类的绘制函数中在顶点位置出绘制圆并第一个节点位置输出其属性数据, 实现代码如下: Adesk::Boolean CPipeLine::worldDraw (AcGiWorldDraw *mode) { assertReadEnabled () ; // 准备数据 int nVerts = AcDbPolyline::numVerts(); AcGePoint3d pt; AcGeVector3d norm = AcDbPolyline::normal (); // 设定绘制颜色为青色 mode->subEntityTraits().setColor(4); // 计算方向向量 AcDbPolyline::getPointAt(0,pt); AcGePoint3d pt1; AcDbPolyline::getPointAt(1,pt1); AcGeVector3d vec = pt1-pt; vec.normalize(); // 计算垂直向量 AcGeVector3d vecV; vecV = vec.crossProduct (AcGeVector3d::kZAxis); // 绘制管径标签 TCHAR buf[100]; pt1 = pt + vecV*2.0; _stprintf(buf,_T( " 管径:%4.2f"), m_dRadius); mode->geometry ().text (pt1,norm,vec,1.0,1.0,0,buf); // 绘制壁厚标签 pt1 = pt1 + vecV*2.0; _stprintf(buf,_T( " 壁厚:%4.2f"), m_dThickness);
21

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

mode->geometry ().text (pt1,norm,vec,1.0,1.0,0,buf); // 绘制埋深标签 pt1 = pt1 + vecV*2.0; _stprintf(buf,_T( " 埋深:%4.2f"), m_dDeep); mode->geometry ().text (pt1,norm,vec,1.0,1.0,0,buf); // 绘制材质标签 pt1 = pt1 + vecV*2.0; _stprintf(buf,_T( " 材质:%s"), m_cMaterial); mode->geometry ().text (pt1,norm,vec,1.0,1.0,0,buf); // 设定颜色为红色 mode->subEntityTraits().setColor(1); for(int i = 0;i<nVerts;i++) { AcDbPolyline::getPointAt (i,pt); mode->geometry ().circle (pt,1.0,norm); } // 设定颜色为黄色 mode->subEntityTraits().setColor(3); // return (AcDbPolyline::worldDraw (mode)) ; } 为了使用定义自定义实体,通常还要创建一个 ObjectARX 工程 CH08UI,其中通过命令 创建的自定义实体,创建过程和创建一个普通的实体的过程是一样的,代码如下: // - CSCH08UI.AddPipe command (do not rename) static void CSCH08UIAddPipe(void) { // 创建管道类 CPipeLine *pPipeline = new CPipeLine(); AcGePoint2d pt0(0,0); AcGePoint2d pt1(10,10); AcGePoint2d pt2(20,0); AcGePoint2d pt3(30,10); AcGePoint2d pt4(40,0); pPipeline->addVertexAt (0,pt0); pPipeline->addVertexAt (1,pt1); pPipeline->addVertexAt (2,pt2); pPipeline->addVertexAt (3,pt3); pPipeline->addVertexAt (4,pt4); pPipeline->setElevation(2.6); pPipeline->setClosed (true);
22

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn

AcDbObjectId idPipe; // 将管道类添加到数据库 AddToDatabase(idPipe,pPipeline); } 创建的管道的显示效果如图 8-9 所示。

图 8-9 管道的显示效果

8.4 练习
(1)开发者可以通过________宏和 AutoCAD 请求加载特性对创建代理进行操作,并且 能够控制调整代理。 A. ACRX_DXF_DEFINE_MEMBERS B. ACRX_NO_CONS_DEFINE_MEMBERS C. ACRX_CONS_DEFINE_MEMBERS D. ACRX_STATIC_CHECK (2) 在卸载应用程序的时候, 自定义对象转换为代理对象, 自定义实体转换为代理实体, 为了能够保证这些工作的顺利进行,在卸载时,所有的自定义类必须调用_________函数从 ObjectARX 中删除。 A. deleteAcRxClass() B. acrxBuildClassHierarchy() C. worldDraw() D. viewportDraw() (3)判断题(正确的在括号内画“√” ,错误的画“×” ) ( ) 代理对象是 AutoCAD 为自定义 ObjectARX 对象在内存中建立的一种代用的数 据存放器。 ( )AutoCAD 利用 worldDraw()和 viewportDraw()函数来显示实体,对任何由 AcDbEntity 派生的类必须重载 worldDraw()函数。 (4)编程实现自定义螺栓类。

23

PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn


相关文章:
第8章 类与对象
第八章 类和对象 67页 5财富值如要投诉违规内容,请到百度文库投诉中心;如要提出...8.1.2 类的声明 作为 C++语言中的自定义类型,类的声明的方式为: class 类...
第八章 类与对象
湖南电大教学指导中心 第八章 类与对象【学习目标】 本章主要介绍类类型的定义...同结构与联合一样,类是一种自定义类型,它包括定义数据成员和定义函数成员(又称...
第8章 类图和对象图
第8章 类图和对象图系统的静态模型描述的是系统所...8.1 数据类型和众多程序设计语言一样,UML也定义了....UML中没有定义这些类型的值,但假定是不言自明的...
答案(第8章—类与数据抽象(一))
答案(第8章—类与数据抽象(一))_电脑基础知识_IT/计算机_专业资料。第 8 章...【题8.32】类与对象有什么关系? 答:类是一种用户自定义的数据类型,对象是...
第8章 类和对象
第8章 对象1 47页 免费 第8章 类图和对象图 41页 免费如要投诉违规内容,请到百度文库投诉中心;如要提出功能问题或意见建议,请点击此处进行反馈。 ...
第八章 类和对象
第五章 数组 第六章 指针 第七章 自定义数据类型1/2 相关文档推荐 ...第八章 类和对象8.1 面向对象程序设计方法概述8.1.1 什么是面向对象的程序设计...
第八章 类
第八章学 4页 5财富值如要投诉违规内容,请到百度文库投诉中心;如要提出...8.1.2 的声明 ?作为 C++语言中的自定义类型,类的声明的方式为: ?class ...
c++面向对象课后答案第8章
c++面向对象课后答案第8章_工学_高等教育_教育专区。在C++中,三种派生方式的说明...定义并描述一个 Table 类和一个 Circle 类,由它们共同派生出 RoundTable 类。...
第8章_对象的容纳
第8章 类和对象 81页 免费 第8章 自定义对象 23页 免费 第8章 类和复杂...第8 章 对象的容纳 “如果一个程序只含有数量固定的对象,而且已知它们的存在时间...
8C++第八章习题解答
8C++第八章习题解答_理学_高等教育_教育专区。第八章 继承与派生习题 1 第八...怎样定义虚基类?用一个实例来解释虚基类在其派生类中的 存储方式。 答:在多...
更多相关标签:
js自定义对象 | jquery 自定义对象 | javascript自定义对象 | jpa 返回自定义对象 | ios自定义对象归档 | 自定义对象 | ios 自定义对象转json | php 对象自定义排序 |