目录
一、基本概念
1.1 筛选器
- 功能:使用Vistual Studio开发项目时,有时源码文件过多,想按功能进行分类管理,可以在类视图里快速切换
- 添加:
- 筛选器:在项目上右键单击,选择“添加 | 新建筛选器”
- 类和头文件:在项目上右键单击,选择“添加 | 类…”,将生成的两个文件拖到筛选器中
- 注意:筛选器仅是逻辑目录,只是方便分类管理,默认情况下新建的文件均在同一个文件夹下
1.2 命令流转顺序
- 顺序列表
序号 工作 1 文件acrxEntryPoint.cpp中注册命令(宏acedRegCmds->addCommand),其链接命令与函数 2 函数在内存中创建实体等其他元素 3 打开硬盘中数据库(即dwg文件)中的某个表,将内存中的元素添加进某个表的记录中,释放各级指针 4 文件acrxEntryPoint.cpp中卸载命令(宏acedRegCmds->removeGroup),卸载arx文件
1.3 文件排布及思路
- 图示
- 程序行进途径(以AddLine命令为例)
- curve类继承关系
二、添加直线
2.1 入口文件acrxEntryPoint.cpp
- 代码示例
// C++默认创建的通用头文件,每个cpp都引入,公用性高的头文件写进去 #include "StdAfx.h" // C++创建的系统资源头文件 #include "resource.h" // 引入头文件用于创建命令 #include "Commands.h" #define szRDS _RXST("Base") class CBaseApp : public AcRxArxApp { public: CBaseApp () : AcRxArxApp () {} virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt) { AcRx::AppRetCode retCode =AcRxArxApp::On_kInitAppMsg (pkt) ; // 添加命令Commands::AddCommands(); AddCommands(); return (retCode) ; } virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt) { // 卸载命令组,组名在Editor::AddCommand中被写死了 acedRegCmds->removeGroup(_T("ToolsBox")); AcRx::AppRetCode retCode =AcRxArxApp::On_kUnloadAppMsg (pkt) ; return (retCode) ; } ... } ...
2.2 业务逻辑Commands类
- 功能:此类根据业务需求创建命令,操作函数实现某项功能,也可以用来作为测试用
- Commands.h
#include "StdAfx.h" // 批量添加命令 void AddCommands(); // 画一条直线 void CreateLine(); // 画一条直线,并修改颜色 void ChangeColor();
- Commands.cpp
#include "StdAfx.h" #include "Line.h" #include "Editor.h" #include "Commands.h" // 此函数可以批量添加命令 // 命令:可以想象成是一系列操作的快捷键 void AddCommands() { // 添加AddLine命令,命令调用CreateLine函数 Editor::AddCommand(_T("AddLine"), ACRX_CMD_MODAL, CreateLine); // 添加ChangeLineColor命令,命令调用ChangeColor函数 Editor::AddCommand(_T("ChangeLineColor"), ACRX_CMD_MODAL, ChangeColor); } // 定义画直线函数 void CreateLine() { // 传入两个3d点给Line类的Add函数 Line::Add(AcGePoint3d(0, 0, 0), AcGePoint3d(100, 100, 0)); } // 定义改变直线颜色函数 void ChangeColor() { // 画直线,并返回直线实体对象在数据库dwg文件中的id AcDbObjectId lineId = Line::Add(AcGePoint3d(0, 0, 0), AcGePoint3d(-100, 100, 0)); // 接收传入的id ,并修改颜色为1(红色) Editor::SetColor(lineId, 1); }
2.3 编辑器Editor类
- 功能:此类可编辑实体属性,为静态的,写一次以后可以复用
- Editor.h
// 此语句会让所在的文件在一个单独的编译中只被包含一次,加快编译速度 #pragma once class Editor { public: Editor(); ~Editor(); public: // 添加命令:按命令名(ACHAR是),命令标识符,调用函数传参 static void AddCommand(const ACHAR * cmdName, Adesk::Int32 commandFlags, AcRxFunctionPtr FunctionAddr); // 重载添加命令:按命令名,调用函数传参 static void AddCommand(const ACHAR *cmdName, AcRxFunctionPtr FunctionAddr); // 修改颜色:传入数据库实体对象id,颜色索引 static void SetColor(AcDbObjectId entId, Adesk::UInt16 colorIndex); // 点选同时获取实体指针、实体id,打开方式 static AcDbEntity *Editor::selectEntity(AcDbObjectId &eId, AcDb::OpenMode openMode); };
- Editor.cpp
#include "StdAfx.h" #include "Editor.h" Editor::Editor(){} Editor::~Editor(){} // 添加命令:需要三个参数 void Editor::AddCommand(const ACHAR * cmdName, Adesk::Int32 commandFlags, AcRxFunctionPtr FunctionAddr) { // 调用了acedRegCmds宏来新增命令,写死命令组为ToolsBox acedRegCmds->addCommand(_T("ToolsBox"), cmdName, // 命令名 cmdName, // 命令别名 commandFlags, // 控制参数 FunctionAddr); // 调用函数名 } // 重载添加命令:需要两个参数 void Editor::AddCommand(const ACHAR *cmdName, AcRxFunctionPtr FunctionAddr) { // 自我调用:调用需要三个参数的AddCommand AddCommand(cmdName, ACRX_CMD_MODAL, // 写死这个参数,就只需要两个参数了 FunctionAddr); } // 修改颜色:对象ID,颜色索引 void Editor::SetColor(AcDbObjectId entId, Adesk::UInt16 colorIndex) { // 断言颜色索引的范围 assert(colorIndex >= 0 && colorIndex <= 256); // 声明并初始化数据库实体指针 AcDbEntity *pEnt = NULL; // acdbOpenObject函数:以数据库对象id即entId为线索,从dwg中找到对象读入内存 // 并返回指针给pEnt,kForWrite为写控制参数 // 判断函数返回值是否正常Acad::eOk if (acdbOpenObject(pEnt, entId, AcDb::kForWrite) == Acad::eOk) { // AcDbEntity类对象的方法setColorIndex,设置颜色 pEnt->setColorIndex(colorIndex); // 操作完必须关闭指针 pEnt->close(); } // 打开错误,打印原因 else { // 此函数类似C语言的printf acutPrintf(_T("\n无法更改对象颜色")); } } // 点选获取实体指针:返回实体id,打开方式 AcDbEntity *Editor::selectEntity(AcDbObjectId &eId, AcDb::OpenMode openMode) { ads_name en; ads_point pt; if (acedEntSel(NULL, en, pt) != RTNORM) { return NULL; } acdbGetObjectId(eId, en); AcDbEntity *pEnt; acdbOpenObject(pEnt, eId, openMode); return pEnt; }
2.4 直线Line类
- 功能:此类为画直线,为静态的,写一次以后可以复用
- Line.h
#pragma once class Line { public: Line(); ~Line(); public: // 函数返回所画直线的对象ID为后续修改属性操作做准备: // 两个3d点,一个数据库指针(有默认值,固定的) static AcDbObjectId Add(const AcGePoint3d &startPoint, const AcGePoint3d &endPoint, AcDbDatabase *pDb = acdbHostApplicationServices()-> workingDatabase()); };
- Line.cpp
#include "StdAfx.h" #include "Line.h" // 用于将内存中的实体添加到数据库文件dwg中 #include "Database.h" Line::Line() {} Line::~Line() {} AcDbObjectId Line::Add(const AcGePoint3d &startPoint, const AcGePoint3d &endPoint, AcDbDatabase *pDb) { // AcDbLine类,继承自AcDbCurve类,传入两个点内存中创建直线对象 AcDbLine *pLine = new AcDbLine(startPoint, endPoint); // 将直线对象的指针、数据库对象指针传入PostToModelSpace函数添加到数据库文件dwg中 return Database::PostToModelSpace(pLine, pDb); }
2.5 数据库Database类
- 功能:此类为数据库操作,为静态的,写一次以后可以复用
- 代码思路:
获得数据库指针==>获得块表指针==>获得块表记录指针==>
在其末尾添加内存中创建的块表记录==>
关闭块表记录指针==>关闭块表==>清空内存中块表记录 - Database.h
#pragma once class Database { public: Database(); ~Database(); public: // 在模型空间中创建对象:传入实体对象指针,数据库指针(头文件有默认值) static AcDbObjectId PostToModelSpace(AcDbEntity *pEnt, AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase()); // 获取层上所有实体:图层名(默认为空)、数据库对象 static AcDbObjectIdArray GetAllEntIds(const ACHAR *layer = NULL, AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase()); // 总目录6.1:获得模型空间的范围盒:数据库指针 static AcDbExtents GetModelSpaceExtent(AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase()); };
- Database.cpp
#include "StdAfx.h" #include "Database.h" Database::Database(){} Database::~Database(){} // 在模型空间中创建对象:传入实体对象指针,数据库指针(头文件有默认值) AcDbObjectId Database::PostToModelSpace(AcDbEntity *pEnt, AcDbDatabase *pDb) { // 声明并初始化块表对象指针 AcDbBlockTable *pBlkTbl = NULL; // 声明并初始化错误状态 Acad::ErrorStatus es; // 数据库对象调用,以读方式打开块表,将块表指针赋值给pBlkTbl es = pDb->getBlockTable(pBlkTbl, AcDb::kForRead); // 纠错函数1:如果块表获取失败;若没纠错函数,cad会直接崩溃关闭 if (es != Acad::eOk) { // _RXST,宽字符串另一种写法,acadErrorStatusText函数将es转换为字符串 acutPrintf(_RXST("\n块表打开失败,错误代码:%s"), acadErrorStatusText(es)); // 返回数据库对象id为空,跳出函数 return AcDbObjectId::kNull; } // 声明并初始化块表记录对象指针 AcDbBlockTableRecord *pBlkTblRcd = NULL; // 块表对象指针调用getAt方法获得块表记录指针pBlkTblRcd // 参数:模型空间(acdb.h中的一个宏)、块表记录指针(接收返回值)、以写方式定位 es = pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd, AcDb::kForWrite); // 纠错函数2:如果块表记录获取失败;若没纠错函数,cad会直接崩溃关闭 if (es != Acad::eOk) { acutPrintf(_RXST("\n模型空间块表记录打开失败,错误代码:%s"), acadErrorStatusText(es)); // 关闭已经打开的块表 pBlkTbl->close(); // 返回数据库对象id为空,跳出函数 return AcDbObjectId::kNull; } // 已经获取到块表记录指针,可以关闭块表了 pBlkTbl->close(); // 声明数据库对象ID变量 AcDbObjectId outId; // 块表记录对象 指针 调用 函数appendAcDbEntity, // 将实体对象指针pEnt指向的内存中的记录添加到数据库文件dwg中,块表记录的最后 // 并将添加成功后的实体对象id赋值给outId es = pBlkTblRcd->appendAcDbEntity(outId, pEnt); // 纠错函数3:如果块表记录添加失败;若没纠错函数,cad会直接崩溃关闭 if (es != Acad::eOk) { acutPrintf(_RXST("\n无法在模型空间中添加实体,错误代码:%s"), acadErrorStatusText(es)); // 关闭块表记录 pBlkTblRcd->close(); // 返回数据库对象id为空,跳出函数 return AcDbObjectId::kNull; } // 若一切正常,清空数据库实体对象指针指向的内存 pEnt->close(); // 关闭块表记录 pBlkTblRcd->close(); // 返回实体对象ID给程序 return outId; } // 获取层上所有实体:图层名(默认为空)、数据库对象 AcDbObjectIdArray Database::GetAllEntIds(const ACHAR *layer, AcDbDatabase *pDb) { AcDbObjectIdArray entIds; // 图层过滤控制符 bool bFilterLayer = false; AcDbObjectId layerId; // 如果传入了图层名 if (layer != NULL) { // 获取层表->判断是否有该图层->获得层表id->设置过滤控制符 AcDbLayerTable *pLayerTbl = NULL; pDb->getLayerTable(pLayerTbl, AcDb::kForRead); // 容错函数:如果层表不包含该图层,关闭层表,返回空id列表 if (!pLayerTbl->has(layer)) { pLayerTbl->close(); return entIds; } pLayerTbl->getAt(layer, layerId); pLayerTbl->close(); bFilterLayer = true; } // 获取数据库块表->数据库块表记录->关闭块表 AcDbBlockTable *pBlkTbl = NULL; pDb->getBlockTable(pBlkTbl, AcDb::kForRead); AcDbBlockTableRecord *pBlkTblRcd = NULL; pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd, AcDb::kForRead); pBlkTbl->close(); // 遍历数据库块表记录 AcDbBlockTableRecordIterator *pItr = NULL; pBlkTblRcd->newIterator(pItr); for (pItr->start();!pItr->done();pItr->step()) { // 如果过滤控制符为真,获得该层上所有实体的id if (bFilterLayer == true) { // 获得每个实体的指针 AcDbEntity *pEnt; pItr->getEntity(pEnt, AcDb::kForRead); // 如果实体的图层为指定过滤的图层 if (pEnt->layerId() == layerId) { // 将实体id添加进id列表 entIds 中 entIds.append(pEnt->objectId()); } pEnt->close(); } else { // 将所有实体id添加进id列表 entIds 中 AcDbObjectId objId; pItr->getEntityId(objId); entIds.append(objId); } } delete pItr; pBlkTblRcd->close(); return entIds; } // 总目录6.1:获得模型空间的范围盒:数据库指针 AcDbExtents Database::GetModelSpaceExtent(AcDbDatabase *pDb) { // 获得数据库块表指针-》获得模型空间的块表记录指针 AcDbBlockTable *pBlkTbl = NULL; pDb->getBlockTable(pBlkTbl, AcDb::kForRead); AcDbBlockTableRecord *pBlkTblRcd = NULL; pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd, AcDb::kForRead); pBlkTbl->close(); // 计算包含块表记录中所有实体的最小范围盒 AcDbExtents extent; Acad::ErrorStatus es = extent.addBlockExt(pBlkTblRcd); pBlkTblRcd->close(); // 如果范围盒生成成功 if (es != Acad::eOk) { // 遍历获取所有模型实体的指针 AcDbObjectIdArray allEnts = GetAllEntIds(NULL, pDb); for (int i = 0; i < allEnts.length(); i++) { AcDbEntity *pEnt = NULL; if (acdbOpenObject(pEnt, allEnts.at(i), AcDb::kForRead) == Acad::eOk) { // 获得实体的范围盒 AcDbExtents extents; if (pEnt->getGeomExtents(extents) == Acad::eOk) { // 累积型扩大范围盒 extent.addExt(extents); } pEnt->close(); } } } // 返回范围盒 return extent; }