CAD平台开发和基于此平台的二次开发(数据库部分)

时间:2024-03-08 16:06:41

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数据库时所需的步骤。如下:

  1. 创建一个新实体。
  2. 以读方式打开块表。
  3. 以写方式打开块表记录,并在其中查找 ACDB_MODEL_SPACE 或 ACDB_PAPER_SPACE 或一个布局。
  4. 关闭块表。
  5. 把实体添加到块表记录。
  6. 关闭块表记录。
  7. 关闭实体对象。

三、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();

用户通过readDwgFileSaveAs来打开保存数据库

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中世界坐标系原点

一个对象(实体,符号表记录,字典)有三种方式被引用

  1. AcDbObjectId;
  2. AcDbHandle;
  3. 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