摘 要:本文主要介绍如何利用OpenDWG工具开发AutoCAD Proxy对象的图元分解程序。在分析AutoCAD Proxy对象特征基础上,对其带来的问题进行讨论,然后介绍利用OpenDWG工具开发图元分解程序的原理,并对其中关键的源程序进行详细解释。
关键词:ObjectARX,Proxy,OpenDWG,图元分解
一、AutoCAD Proxy对象简介
AutoCAD平台上的二次开发工具包——面向对象的实时扩展编程语言(ObjectARX),以其良好的开放性迅速被广大的机械工程师和软件开发商接受。许多开发者利用ObjectARX创建了很多图形和非图形对象,这些对象能够插入到DWG文件中,通常称之为自定义对象。
使用自定义对象对于开发者来说非常方便,利用ObjectARX提供的派生机制,开发者能够从AcDbObject、AcDbEntity等对象基类派生自己的图形和非图形对象,按照最终用户的要求自由地控制对象的数据结构和显示方式,并提供方便的编辑手段。
ObjectARX提供了完善的开发接口用于支持对象派生,在AutoCAD二次开发领域,这种做法正越来越普遍。鉴于本文是讨论基于OpenDWG的应用开发技术,关于ObjectARX二次开发的的知识可以参考有关的书籍。
为了在DWG文件中显示和使用这些对象,创建这些对象的ARX应用程序必须可用(即已经加载到AutoCAD中)。否则,AutoCAD将自动用临时的Proxy对象代替这些自定义对象。当ARX应用程序再次变得可用时,AutoCAD将用自定义对象代替Proxy对象。
二、AutoCAD Proxy对象带来的问题
如上所述,自定义对象给开发者和用户带来方便的同时,由于其对ARX应用程序存在严重的依赖性,这将带来一些问题。
在下列情况下,用户将遇到AutoCAD临时用Proxy对象代替自定义对象的现象:
1) 打开一张包含自定义对象的图纸,但此时创建这些自定义对象的ARX应用程序没有安装在系统中。
2) 在当前图纸中,创建自定义对象的ARX应用程序被卸载。
第一种情况非常普遍,比如,当用户A有一张包含自定义对象的图纸传给用户B时,由于某些原因,没有或不能把创建自定义对象的ARX应用程序同时传给用户B。当用户B打开这张图纸时,AutoCAD将临时用Proxy对象代替这些自定义对象。
每次当用户B打开图纸时,在AutoCAD 2000英文版平台上,系统提示如图1所示的对话框:
图1
对于这个问题,可以通过设置AutoCAD的配置选项对该对话框进行屏蔽。如图2所示,关闭“Show Proxy Information dialog”检查框,下次打开包含Proxy图元的图纸将不弹出图1对话框。
图2
然而,更严重的问题是,由于ARX应用程序不可用,用户B将不能对图纸中的Proxy图元进行任何操作,包括更改属性、移动、缩放、旋转、删除等,这就限制了对图纸进行进一步的编辑修改。
本文后面的内容将叙述基于OpenDWG开发程序来解决这个问题。
三、基于OpenDWG的解决方案
国际OpenDWG联盟(www.opendwg.org)组织提供的OpenDWG开发工具包(以下简称ODT)能够处理AutoCAD2.5到最新的2000/2002版本的dwg/dxf图纸。利用ODT提供的针对AutoCAD自定义对象的支持,可以开发程序来处理Proxy图元不能编辑的问题。
针对自定义图形对象(非图形对象可同样处理),解决问题的思路是,AutoCAD Proxy对象显示在图纸上的内容无非是圆、圆弧、直线、文字等基本图素,若能够对Proxy图元进行遍历,得到这些圆、圆弧、直线、文字等子图元数据(包括颜色、线型、线宽等显示属性),我们就可以在DWG文件中(按照图元数据及显示属性)重新创建这些子图元,然后删除原有的Proxy对象,最后保存DWG文件。
这样,在AutoCAD中重新打开该图纸,由于Proxy的干扰已被去除,用户可以进行自由地编辑修改。下面所述的程序即根据上述思路开发出来的。
关于Proxy对象,ODT提供了完善的API对其进行支持,这里介绍几个主要的函数:
1) AD_IS_A_PROXYENT():判断是否是Proxy图元
2) adStartBlobRead():获取数据链表
3) adReadGrblobData():读取Proxy子图元数据
4) adEndBlobRead():释放数据链表
具体的用法参见下面的源程序。本文列出的全部源程序在Windows 2000 Professional平台上使用Visual C++ 6.0编译通过,OpenDWG开发包的版本是2.0,可以支持AutoCAD 2000/2002。
关于OpenDWG一般的开发步骤,请参见较早期发表的文章《利用OpenDWG读取AutoCAD文件信息》,更详细的信息参见www.opendwg.org提供的有关文档。
下面对基于OpenDWG开发AutoCAD Proxy对象图元分解程序开发中的主要步骤和源程序进行详细解释。
四、主要步骤和源程序
4.1 打开DWG文件,访问模型空间,获取图元链表。
AD_DB_HANDLE ad_db_handle;//文件句柄
ad_db_handle = adLoadFile((char *)strFileName, AD_PRELOAD_ALL, 1);
AD_OBJHANDLE ad_model_space;//模型空间
adFindBlockheaderByName(ad_db_handle, "*MODEL_SPACE", ad_model_space);
AD_VMADDR ad_ent_list;//图元链表
ad_ent_list = adEntityList(ad_db_handle, ad_model_space);
4.2 遍历DWG文件搜索Proxy对象,记录在句柄数组中。
EntHandleArray EntArray;
adStartEntityGet(ad_ent_list);
while (adGetEntity(ad_ent_list, ad_ent_hdr, ad_ent))
{
if (AD_IS_A_PROXYENT(ad_ent_hdr->enttype) &&
ad_ent_hdr->enttype != adLwplineEnttype(ad_db_handle) &&
ad_ent_hdr->enttype != adOle2frameEnttype(ad_db_handle) &&
ad_ent_hdr->enttype != adImageEnttype(ad_db_handle) &&
ad_ent_hdr->enttype != adArcAlignedTextEnttype(ad_db_handle) &&
ad_ent_hdr->enttype != adWipeoutEnttype(ad_db_handle) &&
ad_ent_hdr->enttype != adRtextEnttype(ad_db_handle) &&
ad_ent_hdr->enttype != adHatchEnttype(ad_db_handle) &&
ad_ent->proxyent.grblob != AD_VMNULL)
{//记录Proxy图元数组
RecordEnt(EntArray, ad_ent_hdr->enthandle);
}
}
1) 数组EntHandleArray的定义如下所示:
class EntHandle
{
public:
EntHandle() { adHanclear(objhandle); }
EntHandle(AD_OBJHANDLE handle) { adHancpy(objhandle, handle); }
virtual ~EntHandle() {}
AD_OBJHANDLE objhandle;
};
typedef CArray<EntHandle*,EntHandle*> EntHandleArray;
2) 记录图元数组的函数定义如下所示:
void RecordEnt(EntHandleArray& EntArray, AD_OBJHANDLE handle)
{
EntHandle *ptr = new EntHandle(handle);
EntArray.Add(ptr);
}
3) 释放图元数组的函数定义如下所示:
void FreeEnts(EntHandleArray& EntArray)
{
for (int i=0;i<EntArray.GetSize();i++)
{
delete EntArray[i];
}
EntArray.RemoveAll();
}
4.3 转换Proxy图元,处理之后删除。
for (int i=0;i<EntArray.GetSize();i++)
{
adSeekEntity(ad_db_handle, ad_ent_list, EntArray[i]->objhandle, ad_ent_hdr, ad_ent);
AD_OBJHANDLE enthandle;
adHancpy(enthandle, ad_ent_hdr->enthandle);
//转换Proxy图元
CheckProxyEnt(ad_ent_hdr, ad_ent);
//删除
adDeleteEntity(ad_db_handle, ad_ent_list, enthandle, AD_DELETE_BLOBS);
}
//释放图元数组
FreeEnts();
1) 转换Proxy图元的函数定义如下所示:
BOOL CheckProxyEnt(PAD_ENT_HDR adEntHdr, PAD_ENT adEnt)
{
adHancpy(cur_layer_handle, adEntHdr->entlayerobjhandle);//图层
adHancpy(cur_ltype_handle, adEntHdr->entltypeobjhandle);//线型
cur_ent_color = adEntHdr->entcolor;//颜色
//获取Proxy子图元数据链表
PAD_BLOB_CTRL bcptr = adStartBlobRead(adEnt->proxyent.grblob);
//遍历
adStartGrblobDataRead(bcptr);
AD_GR_DATA grdata;
while (adReadGrblobData(bcptr, &grdata))
{
if (grdata.grtype == AD_GRENT_CIRCLE)
{//处理圆
CheckCircle(&grdata.grbody.circle);
}
else if (grdata.grtype == AD_GRENT_CIRCULARARC)
{//处理圆弧
CheckCircularArc(&grdata.grbody.circulararc);
}
else if (grdata.grtype == AD_GRENT_POLYLINE)
{//处理直线
CheckPolyline(&grdata.grbody.pline, bcptr);
}
else if (grdata.grtype == AD_GRENT_TEXT)
{//处理文字
CheckText(&grdata.grbody.text);
}
}
adEndBlobRead(bcptr);
return TRUE;
}
2) 处理圆的函数定义如下所示:
BOOL CheckCircle(AD_GR_CIRCLE *pGrObj)
{
ad_ent_hdr->enttype = AD_ENT_CIRCLE;
adSetEntityDefaults(ad_db_handle, ad_ent_hdr, ad_ent);
adGenerateObjhandle(ad_db_handle, ad_ent_hdr->enthandle);
ad_ent->circle.pt0[0] = pGrObj->pt0[0];//圆心
ad_ent->circle.pt0[1] = pGrObj->pt0[1];
ad_ent->circle.radius = pGrObj->radius;//半径
adHancpy(ad_ent_hdr->entlayerobjhandle, cur_layer_handle);
adHancpy(ad_ent_hdr->entltypeobjhandle, cur_ltype_handle);
ad_ent_hdr->entcolor = cur_ent_color;
//增加图元
if (!adAddEntityToList(ad_db_handle, ad_ent_list, ad_ent_hdr, ad_ent))
return FALSE;
return TRUE;
}
3) 处理圆弧的函数定义如下所示:
BOOL CheckCircularArc(AD_GR_CIRCARC *pGrObj)
{
ad_ent_hdr->enttype = AD_ENT_ARC;
adSetEntityDefaults(ad_db_handle, ad_ent_hdr, ad_ent);
adGenerateObjhandle(ad_db_handle, ad_ent_hdr->enthandle);
ad_ent->arc.pt0[0] = pGrObj->pt0[0];//圆心
ad_ent->arc.pt0[1] = pGrObj->pt0[1];
ad_ent->arc.radius = pGrObj->radius;//半径
ad_ent->arc.stang = lGetAngle(pGrObj->startvector[0], pGrObj->startvector[1]);//起始角
ad_ent->arc.endang = ad_ent->arc.stang + pGrObj->sweepangle;//终止角
adHancpy(ad_ent_hdr->entlayerobjhandle, cur_layer_handle);
adHancpy(ad_ent_hdr->entltypeobjhandle, cur_ltype_handle);
ad_ent_hdr->entcolor = cur_ent_color;
//增加图元
if (!adAddEntityToList(ad_db_handle, ad_ent_list, ad_ent_hdr, ad_ent))
return FALSE;
return TRUE;
}
4) 处理直线的函数定义如下所示:
BOOL CheckPolyline(AD_GR_PLINE *pGrObj, PAD_BLOB_CTRL bcptr)
{
CArray<double,double> xArray, yArray;
int Count = pGrObj->numpts;
for (int i=0;i<Count;i++)
{
double pt[3];
adReadGrblobVertexPt(bcptr, pt);
xArray.Add(pt[0]);//端点
yArray.Add(pt[1]);
}
for (i=0;i<xArray.GetSize() - 1;i++)
{
ad_ent_hdr->enttype = AD_ENT_LINE;
adSetEntityDefaults(ad_db_handle, ad_ent_hdr, ad_ent);
adGenerateObjhandle(ad_db_handle, ad_ent_hdr->enthandle);
ad_ent->line.pt0[0] = xArray[i];//起点
ad_ent->line.pt0[1] = yArray[i];
ad_ent->line.pt1[0] = xArray[i + 1];//终点
ad_ent->line.pt1[1] = yArray[i + 1];
adHancpy(ad_ent_hdr->entlayerobjhandle, cur_layer_handle);
adHancpy(ad_ent_hdr->entltypeobjhandle, cur_ltype_handle);
ad_ent_hdr->entcolor = cur_ent_color;
//增加图元
if (!adAddEntityToList(ad_db_handle, ad_ent_list, ad_ent_hdr, ad_ent))
return FALSE;
}
return TRUE;
}
5) 处理文字的函数定义如下所示:
BOOL CheckText(AD_GR_TEXT *pGrObj)
{
ad_ent_hdr->enttype = AD_ENT_TEXT;
adSetEntityDefaults(ad_db_handle, ad_ent_hdr, ad_ent);
adGenerateObjhandle(ad_db_handle, ad_ent_hdr->enthandle);
ad_ent->text.pt0[0] = pGrObj->pt0[0];//基点
ad_ent->text.pt0[1] = pGrObj->pt0[1];
ad_ent->text.tdata.height = pGrObj->height;//字高
ad_ent->text.tdata.widthfactor = pGrObj->widthfactor;//宽度因子
ad_ent->text.tdata.oblique = pGrObj->oblique;//倾斜角
strcpy(ad_ent->text.textstr, pGrObj->textstr);//文字串
adHancpy(ad_ent_hdr->entlayerobjhandle, cur_layer_handle);
adHancpy(ad_ent_hdr->entltypeobjhandle, cur_ltype_handle);
ad_ent_hdr->entcolor = cur_ent_color;
//增加图元
if (!adAddEntityToList(ad_db_handle, ad_ent_list, ad_ent_hdr, ad_ent))
return FALSE;
return TRUE;
}
应该注意到,上述对Proxy对象的处理是不完整的,读者可以参考上述的源程序,开发AD_GRENT_CIRCLE3PT、AD_GRENT_CIRCULARARC3PT、AD_GRENT_POLYGON、AD_GRENT_MESH、AD_GRENT_SHELL、AD_GRENT_TEXT2、AD_GRENT_XLINE、AD_GRENT_RAY等类型图元的处理程序,同时考虑图元的显示属性,包括颜色(AD_GRENT_SUBENT_COLOR)、图层(AD_GRENT_SUBENT_LAYER)、线型(AD_GRENT_SUBENT_LINETYPE)等。
4.4 保存DWG文件
//获取文件类型(DWG/DXF)
int nSaveType = GetFileType();
//获取文件版本
int nSaveVer = GetFileVer();
//保存DWG文件
adSaveFile(ad_db_handle, (LPSTR &)strFileName, nSaveType, nSaveVer, 0, 0, 0, 0);
五、运行实例
基于上述思想,笔者基于OpenDWG开发出用于处理AutoCAD Proxy图元的图纸文件转换程序ConvertDwgIncProxyEnt.exe,并在实践中应用良好。
运行界面如图3所示:
图3
其中:
1) 点取“选择图纸”可以一次选择多张图纸进入“文件列表”。
2) 点取“选择目录”可以选择指定目录中的全部DWG文件进入“文件列表”。
3) 点取“清空列表”可以清除“文件列表”中的全部文件。
4) 点取“开始转换”,系统将处理图纸中的Proxy图元,并在“转换结果”中显示转换进度和转换结果。
5) 任务完成,点取“退出”按钮。
六、进一步的工作
由于OpenDWG不是AutoDesk公司提供的开发工具,读者可能会有这样的疑虑,用OpenDWG分解Proxy图元对象之后,若直接对DWG文件存盘,可能会损坏其中的图形数据,导致随后不能被AutoCAD打开进行查看和编辑。
虽然这种猜测没有在实践中得到验证,但保持谨慎的心理是有必要的。这里提供一种思路,仅用OpenDWG遍历DWG文件获取其中Proxy子图元的信息,用ObjectARX进行后续处理。由于后者是AutoDesk公司提供的开发包,自然不会破坏DWG文件。具体步骤是,从DWG文件中获取Proxy对象分解得来的子图元信息之后,不再用OpenDWG函数进行增加图元、删除Proxy对象、保存DWG文件等操作,而用ObjectARX提供的开放接口处理增加图元、删除Proxy对象、保存DWG文件等操作。
然而,结合ObjectARX开发的应用程序不再具有独立性,程序依赖AutoCAD运行。这是其唯一的缺点。
建议有兴趣的读者继续作进一步的开发工作,以彻底消除OpenDWG破坏DWG文件图形数据的可能性。
|