1. 程式人生 > >【ObjectARX】--建立和訪問圖形資料庫(DwgDatabase)

【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檔案。從下圖中可見圖形顯示範圍自動自行調整:

 

 參考資料:

     《AutoCAD ObjectARX(VC)開發基礎與例項教程》