【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)開發基礎與例項教程》