天天看點

CAD二次開發-ObjectARX(C++)-查找所有AcDbLine直線的交點

(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,選擇一種交點的顯示類型:

CAD二次開發-ObjectARX(C++)-查找所有AcDbLine直線的交點

 輸入指令:LineInts

CAD二次開發-ObjectARX(C++)-查找所有AcDbLine直線的交點

按Ctrl + F2,得到相交點的坐标:

CAD二次開發-ObjectARX(C++)-查找所有AcDbLine直線的交點

整個項目的完整代碼:

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

<本文完>

Note:

  本文的源代碼主要參考:http://arxdummies.blogspot.com/