CAD二次开发-ObjectARX(C++)-查找所有AcDbLine直线的交点
来源:https://blog.csdn.net/qq_40416052/article/details/82343341
(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,得到相交点的坐标:
源码:
作者给的百度网盘链接,有几十兆大小,里面含了编译过程的文件,实际代码也就上面那么大, 这里大家可直接下载,毕竟百度网盘的速度太感人了