【ObjectARX】–创建和访问图形数据库(DwgDatabase)

(1)使用ObjectARX创建新工程DwgDatabase,选择MFC支持。

(2)注册一个命令CreateDwg创建一个新的图形文件,并保存在AutoCAD的安装路径中.

实现函数为:

static void AAAMyGroupCreateDwg() {
		// 创建新的图形数据库,分配内存空间
		AcDbDatabase *pDb = new AcDbDatabase(true, false);

		AcDbBlockTable *pBlkTbl = NULL;
		pDb->getSymbolTable(pBlkTbl, AcDb::kForRead);

		AcDbBlockTableRecord *pBlkTblRcd = NULL;
		pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd,
			AcDb::kForWrite); //返回指向打开的pBlkTblRcd
		pBlkTbl->close();

		//创建两个圆
		AcDbCircle *pCir1 = new AcDbCircle(AcGePoint3d(1, 1, 1), 
			AcGeVector3d(0, 0, 1),1.0);
		pBlkTblRcd->appendAcDbEntity(pCir1);
		pCir1->close();
		AcDbCircle *pCir2 = new AcDbCircle(AcGePoint3d(4, 4, 4), 
			AcGeVector3d(0, 0, 1), 2.0);
		pBlkTblRcd->appendAcDbEntity(pCir2);
		pCir2->close();
		pBlkTblRcd->close();

		//获得acad.exe的位置
		CString acadPath;
		GetAcadPath(acadPath);
		
		//去掉路径最后的"acad.exe"字符串,得到AutoCAD安装路径
		acadPath = acadPath.Left(acadPath.GetLength() - 8);
		CString filePath = acadPath + TEXT("test.dwg");

		//使用savaAs成员函数时,必须指定包含dwg扩展名的文件名称
		pDb->saveAs(filePath);

		delete pDb;  //pDb不是数据库的常驻对象,必须手工销毁

	}

 获得当前运行的AutoCAD程序的acad.exe的位置 :

 GetAcadPath函数实现:(注意应写在命令CreateDwg函数的前面)

	static bool GetAcadPath(CString &acadPath)
	{
		DWORD dwRet = ::GetModuleFileName(acedGetAcadWinApp()
			->m_hInstance, acadPath.GetBuffer(_MAX_PATH), _MAX_PATH);
		acadPath.ReleaseBuffer();

		if (dwRet == 0)
		{
			return false;
		}
		else
		{
			return true;
		}
	}

(3)注册一个命令ReadDwg,读取CreateDwg命令中创建的test.dwg文件,在窗口中显示图形数据库的模型空间块表记录中所有实体的实体名。

ReadDwg命令的实现函数为:

static void AAAMyGroupReadDwg() {
		// 使用false作为构造函数的参数,创建一个空的图形数据库
		// 这样保证图形数据库仅仅包含读入的内容    
		AcDbDatabase *pDb = new AcDbDatabase(false);

		// AcDbDatabase::readDwgFile()函数可以自动添加dwg扩展名 
		CString acadPath;
		GetAcadPath(acadPath);
		// 去掉路径最后的"acad.exe"字符串
		acadPath = acadPath.Left(acadPath.GetLength() - 8);
		CString filePath = acadPath + "test.dwg";
		pDb->readDwgFile(filePath,(AcDbDatabase::OpenMode)_SH_DENYWR);

		// 获得模型空间的所有实体
		AcDbObjectIdArray allEntIds = CDwgDatabaseUtil::GetAllEntityIds(NULL, pDb);
		for (int i = 0; i < allEntIds.length(); i++)
		{
			AcDbEntity *pEnt = NULL;
			if (acdbOpenObject(pEnt, allEntIds[i], AcDb::kForRead) == Acad::eOk)
			{
				acutPrintf(TEXT("\\n类名称: %s"), (pEnt->isA())->name());
				pEnt->close();
			}
		}

		// 删除图形数据库
		delete pDb;
	}

其中, 

 GetAllEntityIds函数的声明:

	// 获得模型空间所有实体ID数组(可以将图层作为过滤条件)
	static AcDbObjectIdArray GetAllEntityIds(const TCHAR* layerName = NULL, 
		AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase());

  GetAllEntityIds函数的实现:

AcDbObjectIdArray CDwgDatabaseUtil::GetAllEntityIds( const TCHAR* layerName, AcDbDatabase *pDb )
{
	AcDbObjectIdArray entIds;		// 满足条件的实体集合
	bool bFilterLayer = false;			// 是否需要过滤图层
	AcDbObjectId layerId;
	// 获得指定图层的对象ID
	if (layerName != NULL)
	{
		AcDbLayerTable *pLayerTbl = NULL;
		acdbHostApplicationServices()->workingDatabase()
			->getSymbolTable(pLayerTbl, AcDb::kForRead);
		if (!pLayerTbl->has(layerName))
		{
			pLayerTbl->close();
			return entIds;
		}
		pLayerTbl->getAt(layerName, 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 *it = NULL;
	pBlkTblRcd->newIterator(it);
	for (it->start(); !it->done(); it->step())
	{
		AcDbEntity *pEnt = NULL;
		Acad::ErrorStatus es = it->getEntity(pEnt, AcDb::kForRead);
		if (es == Acad::eOk)
		{
			if (bFilterLayer)				// 过滤图层
			{
				if (pEnt->layerId() == layerId)
				{
					entIds.append(pEnt->objectId());
				}				
			}
			else
			{
				entIds.append(pEnt->objectId());
			}
			
			pEnt->close();
		}
		else
		{
			acutPrintf(TEXT("\\nCDwgDatabaseUtil::GetAllEntityIds中打开实体失败(错误代码:%d)."), (int)es);
		}
	}
	delete it;
	pBlkTblRcd->close();
	
	return entIds;
}

(4)创建一个C++类CViewUtil,并在类中添加DwgZoomExtent函数来调整后台创建的DWG文件的默认视图范围。

DwgZoomExtent函数的实现代码:

void CViewUtil::DwgZoomExtent(AcDbDatabase *pDb)
{
	assert(pDb);

	//获得模型空间所有实体的最小包围框
	AcDbExtents ext = CDwgDatabaseUtil::GetModeSpaceExtent(pDb);
	AcDbViewportTable* pViewportTable = NULL;
	if (pDb->getViewportTable(pViewportTable, AcDb::kForWrite) == Acad::eOk)
	{
		AcDbViewportTableRecord *pRecord = NULL;
		if (pViewportTable->getAt(TEXT("*ACTIVE"), pRecord, AcDb::kForWrite) == 
			Acad::eOk)
		{
			AcGePoint3d center = CGePointUtil::GetMiddlePoint(ext.minPoint(),
				ext.maxPoint());
			double height = ext.maxPoint().y - ext.minPoint().y;
			double width = ext.maxPoint().x - ext.minPoint().x;
			pRecord->setCenterPoint(CConvertUtil::ToPoint2d(center));
			
			pRecord->setHeight(height * 1.2);
			pRecord->setWidth(width * 1.2);
			pRecord->close();
		
		}
		pViewportTable->close();
	}
}

代码段getViewportTable(pViewportTable, AcDb::kForWrite) 表示: 

  在指定的模式 AcDb::kForWrite下打开数据库的Viewport表。pViewportTable指针被填入Viewport表的地址。
如果打开是成功的,返回Acad::eOk。 

代码段getAt(TEXT("*ACTIVE"), pRecord, AcDb::kForWrite) 表示:

  这个函数在带有名称为"*ACTIVE"的记录中搜索AbstractViewTable。如果找到,它将在openMode指定的模式AcDb::kForWrite下打开记录。如果open operation成功,它将返回pRecord指向打开的record。

代码段setHeight(height * 1.2) 表示:

这个函数将viewport的窗口设置为高度height * 1.2 的绘制单元。

 

GetModeSpaceExtent函数的是实现:

注意声明为    static成员。

AcDbExtents CDwgDatabaseUtil::GetModeSpaceExtent(AcDbDatabase *pDb)
{
	AcDbBlockTable *pBlkTbl = NULL;
	pDb->getBlockTable(pBlkTbl, AcDb::kForRead); //pBlkTbl指针获取块表的地址。

	//获得模型空间的块表记录
	AcDbBlockTableRecord *pBlkTblRcd = NULL;
	pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd, AcDb::kForRead);
	     //搜索记录ACDB_MODEL_SPACE,pBlkTblRcd指向打开的记录

	pBlkTbl->close();

	AcDbExtents extent; //l类AcDbExtents:它体现了一个三维空间中的box,
	                    //它的边缘与WCS的轴平行。这个盒子在AcDbExtents对象的私有数据中
	                    //表示示为最小点(minPoint)和最大点(maxPoint)。
	Acad::ErrorStatus es = extent.addBlockExt(pBlkTblRcd);
			//addBlockExt计算一个最小的box,它包含了由pBlkTblRcd所指向的块中的所有实体

	pBlkTblRcd->close();

	//如果图形数据库不是当前的工作数据库,则有时候直接获取模型空间的范围会失败
	if (es != Acad::eOk)
	{
		AcDbObjectIdArray allEnts = GetAllEntityIds(NULL, pDb);
		for (int i = 0; i < allEnts.length(); i++)
		{
			AcDbEntity *pEnt = NULL;
			if (acdbOpenObject(pEnt, allEnts[i], AcDb::kForRead) == Acad::eOk)
			{
				AcDbExtents ext;
				if (pEnt->getGeomExtents(ext) == Acad::eOk) //输出实体的ext
				{
					extent.addExt(ext);//展开由该extent定义的box,并包含由ext定义的box
				}
				pEnt->close();
			}
		}
	}
	return extent;
}

ToPoint2d函数的实现:

AcGePoint2d CConvertUtil::ToPoint2d(const AcGePoint3d &point3d)
{
	return AcGePoint2d(point3d.x, point3d.y);
}

GetMiddlePoint函数的实现:

AcGePoint3d CGePointUtil::GetMiddlePoint(const AcGePoint3d &startPoint, const AcGePoint3d &endPoint)
{
	double x = (startPoint.x + endPoint.x) * 0.5;
	double y = (startPoint.y + endPoint.y) * 0.5;
	double z = (startPoint.z + endPoint.z) * 0.5;

	return AcGePoint3d(x, y, z);
}

(5) 注册CreateDwg2命令,使用公共函数修改创建后台图形数据库的代码,并且在创建图形库之后对其视图进行调整。

实现代码为:

static void AAAMyGroupCreateDwg2() {
		// 创建新的图形数据库,分配内存空间
		AcDbDatabase *pDb = new AcDbDatabase(true, false);
		
		//创建两个圆
		AcDbCircle *pCir1 = new AcDbCircle(AcGePoint3d(1, 1, 1),
			AcGeVector3d(0, 0, 1), 1.0);
		CDwgDatabaseUtil::PostToModelSpace(pCir1, pDb);
		AcDbCircle *pCir2 = new AcDbCircle(AcGePoint3d(4, 4, 4), 
			AcGeVector3d(0, 0, 1), 2.0);

		CDwgDatabaseUtil::PostToModelSpace(pCir2, pDb);

		//调整DWG文件视图
		CViewUtil::DwgZoomExtent(pDb);

		//获得acad.exe的位置
		CString acadPath;
		GetAcadPath(acadPath);

		//去掉路径最后的"acad.exe"字符串,得到AutoCAD安装路径
		acadPath = acadPath.Left(acadPath.GetLength() - 8);
		CString filePath = acadPath + TEXT("test2.dwg");

		//使用saveAs成员函数时,必须指定包含dwg扩展名的文件名称
		pDb->saveAs(filePath);

		delete pDb;  //pDb不是数据库的常驻对象,必须手工销毁

	}

其中,

PostToModelSpace函数的实现:

AcDbObjectId CDwgDatabaseUtil::PostToModelSpace(AcDbEntity *pEnt, AcDbDatabase *pDb)
{
	// 检查输入参数的有效性
	assert(pEnt);		// 等效于assert (pEnt != NULL);

						// 获得当前图形数据库的块表
	AcDbBlockTable *pBlkTbl = NULL;
	pDb->getBlockTable(pBlkTbl, AcDb::kForRead);

	// 获得模型空间对应的块表记录
	AcDbBlockTableRecord *pBlkTblRcd = NULL;
	pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd, AcDb::kForWrite);
	pBlkTbl->close();

	// 将实体添加到模型空间的块表记录
	AcDbObjectId entId;
	Acad::ErrorStatus es = pBlkTblRcd->appendAcDbEntity(entId, pEnt);
	if (es != Acad::eOk)
	{
		pBlkTblRcd->close();
		delete pEnt;	// 添加失败时,要delete
		pEnt = NULL;

		return AcDbObjectId::kNull;
	}

	// 关闭模型空间块表记录和实体
	pBlkTblRcd->close();
	pEnt->close();

	return entId;
}

添加头文件:

#include "DwgDatabaseUtil.h"
#include "ViewUtil.h"

 

效果:

①在AutoCAD2018加载ARX程序,在命令栏执行CreateDwg命令,然后选择【文件/打开】菜单项,打开生成的test.dwg,在CAD的安装目录,比如我的:D:\\Program Files\\Autodesk\\CAD_2018_64bit\\AutoCAD 2018;

然后执行ZOOM命令并选择E选项:

②执行ReadDwg命令,观察命令窗口输出结果;

③执行CreateDwg2命令,同样在安装目录下打开生成test2.dwg文件。从下图中可见图形显示范围自动自行调整:

 

总结思路:

 

项目的完整源代码:

   https://pan.baidu.com/s/1GV7Eq58F4TFjZeGZ_pIJjQ 

 

 参考资料:

     《AutoCAD ObjectARX(VC)开发基础与实例教程》

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片