CAD平台开发分为两大部分。一是底层开发,即CAD数据库的开发(底层类和结构),二是上层开发,界面和功能实现。本人目前在一个开发CAD平台的公司 工作,目前主要接触的是上层开发这一部分,没有底层开发的部分的代码,只有编号的底层数据库的DLL。所以在此我要好先准备一下CAD数据库和实体结构,虽说没有源码,但是可以根据AUTOCAD来熟悉掌握。因为两者大体相同。
一、符号表
一副CAD图形就是一个包含有多个表的数据库,它规定了9个符号表(见下表),用户不能再增加新的符号表。
具体符号表的数据结构我只能看到头文件,但是大体可以猜出一个细节。
符号表名 |
符号表功能 |
AcDbViewTable (视图表) |
适用于 AcDbViewTableRecord 类,表示在数据库中存储的视图。 视图与CAD的"VIEW"命令相关联 |
AcDbViewportTable(视口表) |
适用于 AcDbViewportTableRecord 类,表示在CAD中当前系统变量TILEMODE的值为1时的视口设置。视口配置由CAD的 VPORTS 命令创建。不要和 MVIEW 命令混淆,当系统变量 TILEMODE 的值为0是,该命令创建视口实体。 |
AcDbLinetypeTable (线性表) |
适用于 AcDbLinetypeTableRecord 类,表示图形数据库中的线性。 |
AcDbLayerTable (层表) |
适用于 AcDbLayerTableRecord类,表示图层。 |
AcDbTextStyleTable (文字样式表) |
适用于 AcDbTextStyleTable 类,表示文字样式。 |
AcDbUCSTable (用户坐标系表) |
适用于 AcDbUCSTableRecord 类,表示图形数据库中的存储的用户坐标系。 |
AcDbRegAppTable (应用程序名注册表) |
适用于 AcDbRegAppTableRecord 类,表示为图形数据库中对象的扩展实体数据而注册的应用程序名。 |
AcDbDimStyleTable (尺寸标注样式表) |
适用于 AcDbDimStyleTableRecord 类,表示图形数据库中的尺寸标注样式。 |
AcDbBlockTable (块表) |
适用于 AcDbDimStyleTableRecord 类,表示图形数据库中定义的块。此表含有两个非常重要的记录:模型空间和图纸空间。所有的实体(可见对象)均防御块表AcDbBlockTable中。 |
AcDbDatabase数据组成
AcDbDatabase数据组成
AcDbDatabase数据库
|------AcDb符号表(AcDbSymbolTable)
| |------块表(AcDbBlockTable)
| |------尺寸标注样式表(AcDbDimStyleTable)
| |------层表(AcDbLayerTable)
| |------线型表(AcDbLinetypeTable)
| |------已注册应用程序表(AcDbRegAppTable)
| |------字体样式表(AcDbTextStyleTable)
| |------用户坐标系表(AcDbUCSTable)
| |------视口表(AcDbViewportTable)
| |------视图表(AcDbViewTable)
|-------命名对象词典(Dictionary)
二、用ObjectARX创建对象
AcDbObject
AcDbSymbolTable
AcDbBlockTable
AcDbObject
AcDbSymbolTableRecord
AcDbBlockTableRecord
AcDbObject
AcDbEntity
AcDb3dSolid
(三者独立但是又有函数来实现包含关系)
可以有多个数据库,但是只能打开一个数据库
获取当前打开的数据库acdbHostApplicationServices()->workingDatabase()或是acdbCurDwg()
打开当前的对象(由AcDbObject派生的) 使用acdbOpenObject
inline Acad::ErrorStatus acdbOpenObject(
AcDbEntity *& pEnt,
AcDbObjectId id,
AcDb::OpenMode mode
);
AcDbObject类的函数可以通过帮助查到
看看以下的例子
//给当前数据库中添加一条直线 AcDbObjectId createLine() { AcGePoint3d startPt(4.0, 2.0, 0.0); AcGePoint3d endPt(10.0, 7.0, 0.0); AcDbLine *pLine = new AcDbLine(startPt, endPt);// AcDbLine类 AcDbBlockTable *pBlockTable; acdbHostApplicationServices()->workingDatabase()->getSymbolTable(pBlockTable, AcDb::kForRead);//获得当前的AcDbBlockTable块 AcDbBlockTableRecord *pBlockTableRecord; pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,AcDb::kForWrite);// 获得ACDB_MODEL_SPACE的块记录 pBlockTable->close(); AcDbObjectId lineId; pBlockTableRecord->appendAcDbEntity(lineId, pLine);//添加AcDbLine到BlockRecord pBlockTableRecord->close(); pLine->close(); return lineId; }
注:添加到CAD数据库中的所有实体均获得一个对象ID。
//给当前数据库中添加一层 Void createNewLayer() { AcDbLayerTable *pLayerTable; acdbHostApplicationServices()->workingDatabase()->getSymbolTable(pLayerTable, AcDb::kForWrite); //获得当前的AcDbLayerTable层 AcDbLayerTableRecord *pLayerTableRecord = new AcDbLayerTableRecord; pLayerTableRecord->setName(_T("ASDK_MYLAYER")); pLayerTable->add(pLayerTableRecord); //添加TableRecord到LayerTable pLayerTable->close(); pLayerTableRecord->close(); }
注意:我们打开线性表进行读操作,获得 CONTINUOUS 线性。记住:线性 CONTINUOUS 始终存在。最后,我们把新的层表记录加进层表中,接着关闭层表和层表记录。这和创建实体并非完全相同。
//建一个组,把objIds加入组中。 Void createGroup(AcDbObjectIdArray& objIds, TCHAR* pGroupName) { AcDbGroup *pGroup = new AcDbGroup(pGroupName);//新的组 // Put the group in the group dictionary which resides in the named object dictionary. AcDbDictionary *pGroupDict; acdbHostApplicationServices()->workingDatabase()->getGroupDictionary(pGroupDict, AcDb::kForWrite);//组字典 AcDbObjectId pGroupId; pGroupDict->setAt(pGroupName, pGroup, pGroupId);//加入组 pGroupDict->close(); // Now that the group has been added, it has an ObjectID. // This is important since the group will become a persistent reactor for the added entities... for (int i = 0; i < objIds.length(); i++) { pGroup->append(objIds[i]);// objIds加入组 } pGroup->close(); }
//改变实体的颜色 Acad::ErrorStatus changeColor(AcDbObjectId entId, Adesk::UInt16 newColor) { AcDbEntity *pEntity; acdbOpenObject(pEntity, entId,AcDb::kForWrite); pEntity->setColorIndex(newColor); pEntity->close(); return Acad::eOk; }
Void runIt() // 将他们综合起来,就是一种操作 { createNewLayer(); AcDbObjectIdArray idArr; // create a line and circle and add them to the objectId array idArr.append(createLine()); idArr.append(createCircle()); // change circle color to red changeColor(idArr.last(), 1); // put the line and circle in a group named "ASDK_TEST_GROUP" createGroup(idArr, _T("ASDK_TEST_GROUP")); }
在此声明一下。每个系统函数都要判断返回值,保证系统出现异常时能及时发现
返回值一种是 enum ErrorStatus{ eOk = 0,
eNotImplementedYet = 1,
………………一直到eStringNotAllowedInExpression。非常多。几乎涵盖所有错误。可以用acdbErrorStatusText(ErrorStatus)来返回错误的说明
另一种是int ,即不是RTNORM,则有误
RTNORM |
5100 |
User entered a valid value |
RTERROR |
-5001 |
The function call failed |
RTCAN |
-5002 |
User entered ESC |
RTREJ |
-5003 |
AutoCAD rejected the request as invalid |
RTFAIL |
-5004 |
AutoLISP communication failed |
RTKWORD |
-5005 |
User entered a keyboard or arbitrary text |
RTINPUTTRUNCATED |
-5008 |
Input didn’t fit in the buffer |
通过以上代码,我们可以总结出一下把实体放入CAD数据库时所需的步骤。如下:
- 创建一个新实体。
- 以读方式打开块表。
- 以写方式打开块表记录,并在其中查找 ACDB_MODEL_SPACE 或 ACDB_PAPER_SPACE 或一个布局。
- 关闭块表。
- 把实体添加到块表记录。
- 关闭块表记录。
- 关闭实体对象。
三、AutoCAD的数据库结构
所有的数据库有表和记录,AutoCAD把图形结构看作一个数据库。一幅图形具有如下的结构:
- 层表和层表记录——AcDbLayerTable, AcDbLayerTableRecord。
- 块表和块表记录——AcDbBlockTable, AcDbBlockTableRecord。AutoCAD中的所有实体(可见实体)均属于块表记录。块表中包含有两天特殊的记录:*MODEL_SPACE(模型空间)和*PAPER_SPACE(图纸空间)。所有的AutoCAD实体均属于这两个记录。
- 符号表和各种类型的符号表记录,见上面的符号表表格。
- 有名对象字典,其中有“组字典”和“多线样式字典”。
四、数据库常驻对象
数据库常驻对象中的每一个对象都使用AcDb前缀。这些对象可分为下列几大类型:符号表、符号表记录、实体、基本类和光栅类。所有的数据库常驻对象均有AcDbObject类派生,而AcDbObject类是由AcRxObject类派生,AcRxObject是基类。
为了能使用符号表和符号表记录,在应用程序中比寻包含头文件 dbsymtb.h,即:#include<dbsymtb.h>
符号表的查询函数如下:
- AcDb###Table::getAt()-----------> 获得指定符号表记录的指针或ID号。
- AcDb###Table::has()-------------> 确定符号表中指定的符号表记录是否存在。
- AcDb###Table::newIterator()-----> 创建遍历符号表的浏览器。
符号表的编辑函数如下:
- AcDb###Table::add()----> 在符号表中加入一条符号表记录。
注:用适当的符号表名代替上面的###字符。
层表记录的查询函数如下:
- AcDbLayerTableRecord::color() 图层是什么颜色?
- AcDbLayerTableRecord::ifFrozen() 图层是否冻结?
- AcDbLayerTableRecord::isLocked() 图层是否锁住?
- AcDbLayerTableRecord::isOff() 图层是否关闭?
- AcDbLayerTableRecord::linetypeObjectId() 图层的线性是什么?
- AcDbLayerTableRecord::VPDFLT() 视口中层的可见性缺省值?
层表记录的编辑函数如下:
- AcDbLayerTableRecord::setColor() 改变图层颜色。
- AcDbLayerTableRecord::setIsFrozen() 解冻/冻结。
- AcDbLayerTableRecord::setIsLocked() 锁住/解锁。
- AcDbLayerTableRecord::setIsOff() 打开/关闭。
- AcDbLayerTableRecord::setLinetypeObjectId() 改变图层的线性。
- AcDbLayerTableRecord::setVPDFLT() 改变图层的可见性缺省值。
五、常用返回码
两个最常用的返回码如下:
- Acad::ErrorStatus 在头文件 acadstr.h 中定义。
- Adesk::Boolean 在 adesk.h 中定义。
六、遍历器常用函数
遍历器函数 |
说明 |
AcDbSymbolTableIterator::done() | 如果遍历器定位在块的任一端(尾部和头部),返回 Adesk::kTrue,否则返回 Adesk::kFalse |
AcDbSymbolTableIterator::getRecord() | 以 openMode 模式打开遍历器所在位置的记录,使 pRecord指向打开的记录。此函数也在由 AcDbSymbolTable 派生出的类中定义,并常被该类应用 |
AcDbSymbolTableIterator::getRecordId() | 返回遍历器所在位置对象的 AcDbObjectId |
AcDbSymbolTableIterator::seek() |
用于定位遍历器,使遍历器处于由 AcDbObjectId 指定的记录处 |
AcDbSymbolTableIterator::start() | 用于初始化遍历器的位置,使其处于表的开头或表的结尾 |
AcDbSymbolTableIterator::step() | 移动遍历器,使其再表中下移(或上移)一条记录 |
例:利用遍历器显示数据库中所有线型
void inblk() { // 首先以读模式打开获得一条记录,接着获得先姓名,关闭记录,然后打印先姓名。 // 最后删除遍历器 AcDbDatabase *pCurDb; AcDbLinetypeTable *pLineTypeTable; AcDbLinetypeTableRecord *pLineTypeTableRecord; AcDbLinetypeTableIterator *pLineTypeTableIter; char * pName; pCurDb = acdbHostApplicationServices()->workingDatabase(); // 获得线性表 pCurDb->getLinetypeTable(pLineTypeTable, AcDb::kForRead); // 定义浏览器 pLineTypeTable->newIterator(pLineTypeTableIter); // 遍历遍历器 for (; !pLineTypeTableIter->done(); pLineTypeTableIter->step()) { pLineTypeTableIter->getRecord(pLineTypeTableRecord, AcDb::kForRead); pLineTypeTableRecord->getName(pName); pLineTypeTableRecord->close(); acutPrintf("\n线性名:%s", pName); // 释放资源 free(pName); } // 释放遍历器 delete pLineTypeTableIter; pLineTypeTable->close(); }
特别说明:使用 upgradeOpen() 函数可以把 AcDb###Table从读操作模式 转换为写操作模式。
当前模式为: pCurDb->getBlockTable(pBlkTable, AcDb::kforread);
改为写模式的写法: pBlkTable->upgradeOpen();
用户通过readDwgFile和SaveAs来打开保存数据库
void ArxApp::cmd_createDwg(void) { AcDbDatabase *pDataBase = new AcDbDatabase;//新建一个Database AcDbBlockTable* pBtbl; pDataBase->getBlockTable(pBtbl,AcDb::kForRead); AcDbBlockTableRecord *pBtblRecord; pBtbl->getAt(ACDB_MODEL_SPACE,pBtblRecord,AcDb::kForWrite); pBtbl->close(); AcGePoint3d startPt(0,0,0);//起点 AcGePoint3d endPt(200,100,0);//终点 AcDbLine *pLine = new AcDbLine(startPt,endPt); AcDbObjectId lineId = AcDbObjectId::kNull; pBtblRecord->appendAcDbEntity(lineId,pLine); pLine->close(); pBtblRecord->close(); pDataBase->saveAs(_T("test1.dwg"));//保存DataBase为test1.dwg delete pDataBase; } void ArxApp::cmd_readDwg(void) { AcDbDatabase *pDataBase = new AcDbDatabase; pDataBase->readDwgFile(_T("test1.dwg")); //读取test1.dwg AcDbBlockTable* pBtbl; pDataBase->getBlockTable(pBtbl,AcDb::kForRead); AcDbBlockTableRecord *pBtblRecord; pBtbl->getAt(ACDB_MODEL_SPACE,pBtblRecord,AcDb::kForRead); pBtbl->close(); AcDbBlockTableRecordIterator *pBlkTblRdItr; pBtblRecord->newIterator(pBlkTblRdItr); AcDbEntity *pEnt; //遍历所有实体 for(pBlkTblRdItr->start();!pBlkTblRdItr->done();pBlkTblRdItr->step()) { pBlkTblRdItr->getEntity(pEnt,AcDb::kForRead); acutPrintf(_T("class is: %s\n"), pEnt->isA()->name()); pEnt->close(); } pBtblRecord->close(); delete pBlkTblRdItr; delete pDataBase; ads_tblnext }
使用SetCeColor, SetCeltype,setClayer等设置数据库的值,具体的参考帮助文档
字典相互拷贝
字典相互拷贝使用AcDbDatabase::wblock (AcDbDatabase*& newDb) ,newDb中没有引用的符号在新数据库中略去,对象不被复制,用户要使用AcEditorReactor将数据传到新数据库中。
AcDbDatabase::wblock (AcDbDatabase*& newDb, AcDbObjectId recordId)
元Db中recordId的块表中的实体全部被复制过去
AcDbDatabase::wblock (AcDbDatabase*& newDb, AcDbObjectIdArray&idArray, AcGePoint3d& point)
元Db中idArray的实体放到新的DB中,point就是新的DB中世界坐标系原点
一个对象(实体,符号表记录,字典)有三种方式被引用
- AcDbObjectId;
- AcDbHandle;
- ads_name;
三者相互转化
Acad::ErrorStatus getAcDbObjectId(
AcDbObjectId& retId,
bool createIfNotFound,
const AcDbHandle& objHandle,
Adesk::UInt32 xRefId = 0
);通过retId获取objHandle
Acad::ErrorStatus acdbGetAdsName(
ads_name& objName,
AcDbObjectId objId
);通过objId返回objName
Acad::ErrorStatus acdbGetObjectId(
AcDbObjectId& objId,
const ads_name objName
); 通过objName返回objId