(1)使用ObjectARX向導創建一個項目LineIntersections
(2)首先,我們需要准備我們的CMap結構,以便能夠將AcGePoint3d作為映射鍵來處理。我們的想法是對通過每個交叉點的所有線進行分組。
CMap不支持AcGePoint3d,因為它不知道如何散列它以及如何將它作為密鑰進行比較。為此,我們需要定義HasKey和CompareElements函數模板。
先定義在acrxEntryPoint.cpp文件的開頭位置定義const常量(兩點之間距離的最小容差)
const double _dTol = 0.0001;
(3)在acrxEntryPoint.cpp文件的開始處實現HashKey和函數
template<>
UINT AFXAPI HashKey<AcGePoint3d> (AcGePoint3d key)
{
CString sPoint;
sPoint.Format(_T("%f,%f,%f"),key.x, key.y ,key.z);//將int類型的變量,轉成CString類型
UINT iHash = (NULL == &key) ? 0 : HashKey((LPCSTR)sPoint.GetBuffer());
return iHash;
}
相關函數解析:
①template <> :表示T = void的顯式特化
②函數HashKey:計算給定鍵的哈希值。
語法:
template<class ARG_KEY>
AFX_INLINE UINT AFXAPI HashKey(
ARG_KEY key
);
參數:
ARG_KEY:模板參數,指定用於訪問映射鍵的數據類型。
key:要計算哈希值的key。
返回值:
key的哈希值。
注釋:
此函數由CMap :: RemoveKey直接調用,間接由CMap :: Lookup和CMap :: Operator []調用。
默認實現通過將鍵右移四個位置來創建哈希值。重寫此函數,以便它返回適合您的應用程序的哈希值
(4)在acrxEntryPoint.cpp文件的開始處實現CompareElements函數
template<> BOOL AFXAPI CompareElements<AcGePoint3d, AcGePoint3d>
(const AcGePoint3d* pElement1, const AcGePoint3d* pElement2)
{
if ((pElement1 == NULL) || (pElement2 == NULL))
return false;
AcGeTol gTol;
gTol.setEqualPoint(_dTol); // Point comparison tolerance
return (pElement1->isEqualTo(*pElement2,gTol));
}
相關解析:
①AcGeTol類:
描述:
這是一個可實例化的類,默認初始化為默認的公差。隨后,可以根據特定的需要定制它的公差。例如,這個類的一個實例可能是專門用於在表面交集中使用的。
類AcGeTol保留兩個值,equalPoint和equal矢量,根據以下規則用於評估:
Two points, p1 and p2, are equal if
(p1 - p2).length() <= equalPoint
注釋:
兩行等於點集,如果在任何直線上的任意點,在第一點的點上,在另一條直線上有一個點。這意味着,在DIAM的建模空間中,有兩條直線和平行方向向量的兩條直線相等,如果只有公差equal向量被設置得比equalPoint更近。
②函數setEqualPoint
設置對值的相等的公差。
③函數isEqualTo:
語法:
bool isEqualTo(
const AcGePoint3d& pnt,
const AcGeTol& tol = AcGeContext::gTol
) const;
參數:
const AcGePoint3d& pnt |
輸入點 |
const AcGeTol& tol = AcGeContext::gTol |
輸入公差 |
描述:
檢查這一點是否在從點pnt到.equalpoint()的距離內。公差等級的默認值是AcGeContext::gTol。
(5)注冊一個命令LineInts
(6)接下來,在注冊命令的函數體中,我們將在ModelSpace中收集AcDbLine實體
Acad::ErrorStatus es;
AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase();
AcDbBlockTableRecordPointer pBTR(acdbSymUtil()->blockModelSpaceId(pDb), AcDb::kForWrite);
AcDbBlockTableRecordIterator *pIter = NULL;
pBTR->newIterator(pIter, true);
AcDbObjectIdArray arrLines;
while (!pIter->done())
{
AcDbEntity *pEnt = NULL;
es = pIter->getEntity(pEnt, AcDb::kForRead);
if (es == Acad::eOk)
{
if (pEnt->isKindOf(AcDbLine::desc()))
arrLines.append(pEnt->objectId());
pEnt->close();
}
pIter->step(true);
}
delete pIter;
pIter = NULL;
if (arrLines.length() == 0)
{
acutPrintf(_T("在模型空間中lines!\n"));
return;
}
else
{
acutPrintf(_T("我們在模型空間中找到了 %d 條 lines!\n檢查交叉點的容差 %f...\n"),
arrLines.length(), _dTol);
}
(7)接下來,通過收集的lines,我們將使用我們需要的信息構建我們的CMap:
CMap<AcGePoint3d, AcGePoint3d, AcDbObjectIdArray, AcDbObjectIdArray&> mapLines;
acdbTransactionManager->startTransaction();
for (int i = 0; i<arrLines.length() - 1; i++)
{
AcDbLine* pLineA = NULL;
if (acdbTransactionManager->getObject((AcDbObject*&)pLineA, arrLines[i], AcDb::kForRead) == Acad::eOk)
{
for (int j = i + 1; j<arrLines.length(); j++)
{
AcDbLine* pLineB = NULL;
if (acdbTransactionManager->getObject((AcDbObject*&)pLineB, arrLines[j], AcDb::kForRead) == Acad::eOk)
{
AcGePoint3dArray arrPts;
if (pLineA->intersectWith(pLineB, AcDb::kOnBothOperands, arrPts) == Acad::eOk)
{
if (arrPts.length() > 0)
{
for (int p = 0; p<arrPts.length(); p++)
{
AcDbObjectIdArray arrExist;
if (mapLines.Lookup(arrPts[p], arrExist) == TRUE)
{
// Existing point...
if (arrExist.contains(pLineA->objectId()) == false)
mapLines[arrPts[p]].append(pLineA->objectId());
if (arrExist.contains(pLineB->objectId()) == false)
mapLines[arrPts[p]].append(pLineB->objectId());
}
else
{
// New point...
AcDbObjectIdArray arrNewEnts;
arrNewEnts.append(pLineA->objectId());
arrNewEnts.append(pLineB->objectId());
mapLines.SetAt(arrPts[p], arrNewEnts);
}
}
}
}
}
}
}
}
acdbTransactionManager->endTransaction();
if (mapLines.GetCount() == 0)
{
acutPrintf(_T("我們沒有發現交點!\n"));
return;
}
相關函數解析:
①:函數startTransaction():
創建一個新的AcTransaction對象,將這個新事務添加到當前交易的列表中,使之成為新的頂級交易,然后返回一個指向新的AcTransaction對象的指針。
②函數getObject( ):
語法:
virtual Acad::ErrorStatus getObject(
AcDbObject*& obj,
AcDbObjectId id,
AcDb::OpenMode mode = AcDb::kForRead,
bool openErasedObj = false
) = 0;
AcDbObject*& obj |
返回到獲得對象的指針 |
AcDbObjectId id |
輸入獲得對象的id |
AcDb::OpenMode mode = AcDb::kForRead |
輸入獲得的模式 |
bool openErasedObj = false |
輸入布爾指示是否要獲取被擦除的對象 |
描述:
這個函數調用top事務的getObject()函數,傳遞它所接收到的所有參數。對象只能在頂級事務中獲得,因此這個函數省去了必須保存指針到頂部事務的麻煩,或者使用AcTransactionManager::topTransaction()函數來獲得它。
③函數getObject( )
語法:
virtual Acad::ErrorStatus
AcDbEntity::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
函數返回一個實體與圖中的另一個實體相交的點。這個函數的輸入值是實體和交集類型,它可以是下面的一個:
- kOnBothOperands (neither entity is extended) :兩線交點
- kExtendThis:兩線中的一條線延長相交的點
- kExtendArg
- kExtendBoth:兩線延長相交的點
(8)為了演示這些信息的使用,我們然后使用我們的CMap數據在ModeSpace上創建AcDbPoint實體,並在命令提示符下打印一個小報告:
POSITION pos = mapLines.GetStartPosition();
while (pos)
{
AcGePoint3d ptKey(0, 0, 0);
AcDbObjectIdArray arrEnts;
mapLines.GetNextAssoc(pos, ptKey, arrEnts);
AcDbPoint* ptEnt = new AcDbPoint(ptKey);
AcDbObjectId idPointEnt;
pBTR->appendAcDbEntity(idPointEnt, ptEnt);
ptEnt->close();
CString sEnts;
for (int e = 0; e<arrEnts.length(); e++)
{
ACHAR pBuff[255] = _T("");
arrEnts[e].handle().getIntoAsciiBuffer(pBuff);
CString sBuff;
sBuff.Format((e == arrEnts.length() - 1) ? _T("%s") : _T("%s,"), pBuff);
sEnts += sBuff;
}
CString sPromptReport;
sPromptReport.Format(_T("Point (%.4f, %.4f, %.4f) - Entities [%s]\n"), ptKey.x, ptKey.y, ptKey.z, sEnts);
acutPrintf(sPromptReport);
}
}
詳細解析:
函數GetNextAssoc( ):
此功能用於重復最有用通過所有元素都映射。 請注意放置順序不一定是的與鍵值序列。
如果已檢索的元素位於映射的最后,則 rNextPosition 的新值設置為 NULL。
語法:
void GetNextAssoc(
POSITION& rNextPosition,
KEY& rKey,
VALUE& rValue
)
參數:
rNextPosition:指定對以前 GetNextAssoc 返回的 POSITION 值或 GetStartPosition 調用。
rKey:指定所檢索元素的返回的鍵。
rValue:指定所檢索的元素的返回值。
效果:
加載ObjectARX程序,然后在CAD中畫幾條lines,
在CAD命令行輸入 ptype,選擇一種交點的顯示類型:
輸入命令:LineInts
按Ctrl + F2,得到相交點的坐標:
整個項目的完整代碼:
https://pan.baidu.com/s/1QVMfDGwmpYAS2wVxFLyugg
<本文完>
Note:
本文的源代碼主要參考:http://arxdummies.blogspot.com/