#include "StdAfx.h" #include "CollisionDetection.h" #include "aecdbentitybase.h" #include "aecdbcurvebase.h" #include "aecdbwall.h" #include "aecdbbeam.h" #include "aecdbcolumn.h" #include "aecdbslab.h" #include "aecdbopening.h" #include "aecdbewequipment.h" #include "aecdbxequipment.h" #include "AecUIContextMenu.h" #include "coretools.h" #include "entityfilter.h" #include "typeconvert.h" #undef min #undef max #define CD_FLAG _T("CD_FLAG") ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace CDA { AABB::AABB() { // 使用极端的正负值,确保任何有效的图形 extents 都能覆盖它 m_maxPt.x = m_maxPt.y = m_maxPt.z = -1e20; m_minPt.x = m_minPt.y = m_minPt.z = 1e20; } AABB::AABB(const AcDbExtents &ext) { set(ext); } // 辅助方法:合并另一个 AABB void AABB::grow(const AABB &other) { m_minPt.x = std::min(m_minPt.x, other.m_minPt.x); m_minPt.y = std::min(m_minPt.y, other.m_minPt.y); m_minPt.z = std::min(m_minPt.z, other.m_minPt.z); m_maxPt.x = std::max(m_maxPt.x, other.m_maxPt.x); m_maxPt.y = std::max(m_maxPt.y, other.m_maxPt.y); m_maxPt.z = std::max(m_maxPt.z, other.m_maxPt.z); } void AABB::set(const AcDbExtents &ext) { m_maxPt = ext.maxPoint(); m_minPt = ext.minPoint(); } // 辅助方法:计算包围盒的中心点 (用于拆分节点) AcGePoint3d AABB::centroid() const { return AcGePoint3d((m_minPt.x + m_maxPt.x) * 0.5, (m_minPt.y + m_maxPt.y) * 0.5, (m_minPt.z + m_maxPt.z) * 0.5); } // 辅助方法:获取最长轴 (0=X, 1=Y, 2=Z) int AABB::getLongestAxis() const { double dx = m_maxPt.x - m_minPt.x; double dy = m_maxPt.y - m_minPt.y; double dz = m_maxPt.z - m_minPt.z; if (dx >= dy && dx >= dz) return 0; if (dy >= dx && dy >= dz) return 1; return 2; } bool AABB::intersects(const AABB &other) const { if (m_maxPt.x < other.m_minPt.x || m_minPt.x > other.m_maxPt.x) return false; if (m_maxPt.y < other.m_minPt.y || m_minPt.y > other.m_maxPt.y) return false; if (m_maxPt.z < other.m_minPt.z || m_minPt.z > other.m_maxPt.z) return false; return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 参数改用 const 引用传递 bool EntityProxy::set(const AcDbObjectId &id) { AcDbEntityPointer ent(id, AcDb::kForRead); if (ent.openStatus() != Acad::eOk) return false; AcDbExtents ext; if (ent->getGeomExtents(ext) != Acad::eOk) return false; m_entityId = id; m_box.set(ext); m_centroid = m_box.centroid(); return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 外部世界只需要调用这一个接口 void BVHTree::Build(const AcDbObjectIdArray &ar) { if (ar.isEmpty()) return; EntityProxy proxy; for (int i(0); i < ar.length(); ++i) { if (proxy.set(ar[i])) m_allEntities.push_back(proxy); } // 预分配内存,这在 C++ 中极其重要! // 数学上,N 个叶子节点的满二叉树,总节点数最多是 2N - 1 // 提前 reserve 可以彻底避免递归中 push_back 导致的动态扩容和内存搬运性能损耗 m_bvhNodes.clear(); m_bvhNodes.reserve(m_allEntities.size() * 2); // 压入根节点 (索引为 0) m_bvhNodes.push_back(BVHNode()); // 启动递归建树 BuildNode(0, 0, (int)m_allEntities.size()); } void BVHTree::GetIntersections(const AABB &queryBox, std::vector &hitIds) const { // 如果树还没建好,直接返回 if (m_bvhNodes.empty()) { return; } // 手搓一个极其轻量的栈。64 层深度足以容纳上百万实体的树! const int MAX_STACK_SIZE = 64; int stack[MAX_STACK_SIZE]; int stackPtr = 0; // 把根节点(索引为 0)压入栈 stack[stackPtr++] = 0; while (stackPtr > 0) { // 弹出栈顶节点 int nodeIdx = stack[--stackPtr]; const BVHNode &node = m_bvhNodes[nodeIdx]; // 1. 核心粗筛:如果连这个大管辖区的包围盒都不碰,直接跳过这整整半片厂房! if (!node.box.intersects(queryBox)) { continue; } // 2. 如果碰到了,判断是不是叶子节点 if (node.isLeaf()) { // 已经是叶子节点了:遍历里面那几个具体的实体,进行精确 AABB 检测 for (int i = 0; i < node.primitiveCount; ++i) { const EntityProxy &proxy = m_allEntities[node.leftFirst + i]; if (proxy.m_box.intersects(queryBox)) { hitIds.push_back(proxy.m_entityId); } } } else { // 绝对的防线:因为我们每次要压入 2 个元素,所以必须保证至少还有 2 个空位 if (stackPtr <= MAX_STACK_SIZE - 2) { stack[stackPtr++] = node.leftFirst; // 左子节点 stack[stackPtr++] = node.leftFirst + 1; // 右子节点 } else { // 触发了这一步,说明图纸数据极度病态,击穿了 256 层深度。 // 在工业软件中,通常会在这里输出一行 Debug 日志, // 丢弃这部分极深层级的遍历以保全主程序不崩溃。 //acutPrintf(_T("\n警告: BVH 树深度超过 %d,放弃深层碰撞检测。"), MAX_STACK_SIZE); continue; } } } } void BVHTree::GetIntersections(AcDbObjectId &id, std::vector &hitIds) const { AcDbEntityPointer ent(id, AcDb::kForRead); if (ent.openStatus() != Acad::eOk) return; AcDbExtents ext; if (ent->getGeomExtents(ext) != Acad::eOk) return; AABB ab(ext); GetIntersections(ab, hitIds); } void BVHTree::GetAllIntersections(std::vector> &hitPairs) const { if (m_bvhNodes.empty()) { return; } // 从根节点开始,让整棵树自己和自己比较 CollideNodes(0, 0, hitPairs); } // 内部递归建树函数,直接操作类成员变量 void BVHTree::BuildNode(int nodeIdx, int entityOffset, int entityCount) { // 1. 计算这批实体的总包围盒 AABB totalBox; for (int i = 0; i < entityCount; ++i) { totalBox.grow(m_allEntities[entityOffset + i].m_box); } // 注意:不要在这里使用引用 (BVHNode& node = m_bvhNodes[nodeIdx]) 然后贯穿整个函数。 // 因为后面有 push_back 操作,理论上如果 capacity 不够会导致底层数组重新分配, // 引用就会瞬间变成悬空引用(野指针)。这是极其经典的 C++ 崩溃暗坑! m_bvhNodes[nodeIdx].box = totalBox; // 2. 终止条件:如果实体数量足够少,直接变成叶子节点 if (entityCount <= 4) { m_bvhNodes[nodeIdx].leftFirst = entityOffset; m_bvhNodes[nodeIdx].primitiveCount = entityCount; return; } // 3. 找出当前 Box 的最长轴以及空间的中心点 int longestAxis = totalBox.getLongestAxis(); double splitPos = 0.0; if (longestAxis == 0) { splitPos = totalBox.centroid().x; } else if (longestAxis == 1) { splitPos = totalBox.centroid().y; } else { splitPos = totalBox.centroid().z; } // 4. 使用 std::partition 原地划分数组 std::vector::iterator beginIt = m_allEntities.begin() + entityOffset; std::vector::iterator endIt = beginIt + entityCount; std::vector::iterator midIt = std::partition( beginIt, endIt, PartitionPredicate(longestAxis, splitPos) ); int leftCount = (int)std::distance(beginIt, midIt); // 5. 极端边界处理:如果所有实体的质心都完全重合,强行平分 if (leftCount == 0 || leftCount == entityCount) { leftCount = entityCount / 2; } // 6. 分配左右子节点的索引 int leftChildIdx = (int)m_bvhNodes.size(); m_bvhNodes.push_back(BVHNode()); // 压入左节点 m_bvhNodes.push_back(BVHNode()); // 压入右节点 // 安全地更新当前节点信息 m_bvhNodes[nodeIdx].leftFirst = leftChildIdx; m_bvhNodes[nodeIdx].primitiveCount = 0; // 7. 递归构建左子树和右子树 BuildNode(leftChildIdx, entityOffset, leftCount); BuildNode(leftChildIdx + 1, entityOffset + leftCount, entityCount - leftCount); } // 核心的双树递归遍历函数 void BVHTree::CollideNodes(int nodeIdxA, int nodeIdxB, std::vector> &hitPairs) const { const BVHNode &nA = m_bvhNodes[nodeIdxA]; const BVHNode &nB = m_bvhNodes[nodeIdxB]; // 极速粗筛:如果这两个节点的包围盒根本不相交,直接砍掉整条递归分支! if (!nA.box.intersects(nB.box)) { return; } // 情况 1:正在检查同一个节点 (nodeIdxA == nodeIdxB) if (nodeIdxA == nodeIdxB) { if (nA.isLeaf()) { // 同一个叶子节点内部的实体两两比较 // 剪刀 1:j 从 i + 1 开始,完美避开 A-B 和 B-A,且不比较自己 for (int i = 0; i < nA.primitiveCount; ++i) { for (int j = i + 1; j < nA.primitiveCount; ++j) { const EntityProxy &p1 = m_allEntities[nA.leftFirst + i]; const EntityProxy &p2 = m_allEntities[nA.leftFirst + j]; if (p1.m_box.intersects(p2.m_box)) { hitPairs.push_back(std::make_pair(p1.m_entityId, p2.m_entityId)); } } } } else { // 同一个内部节点,拆分成三个子任务: // 1. 左子树内部自己和自己查 CollideNodes(nA.leftFirst, nA.leftFirst, hitPairs); // 2. 右子树内部自己和自己查 CollideNodes(nA.leftFirst + 1, nA.leftFirst + 1, hitPairs); // 3. 左子树 vs 右子树 // 剪刀 2:只调一次!绝不调用 CollideNodes(右, 左),彻底打破跨分支的对称性! CollideNodes(nA.leftFirst, nA.leftFirst + 1, hitPairs); } } // 情况 2:正在检查两个完全不同的节点 (nodeIdxA != nodeIdxB) else { if (nA.isLeaf() && nB.isLeaf()) { // 两个都是叶子节点,进行二分图式的完全交叉比较 for (int i = 0; i < nA.primitiveCount; ++i) { for (int j = 0; j < nB.primitiveCount; ++j) { const EntityProxy &p1 = m_allEntities[nA.leftFirst + i]; const EntityProxy &p2 = m_allEntities[nB.leftFirst + j]; if (p1.m_box.intersects(p2.m_box)) { hitPairs.push_back(std::make_pair(p1.m_entityId, p2.m_entityId)); } } } } else if (nA.isLeaf()) { // A 是叶子,B 是内部节点,所以我们把 B 拆开,继续用 A 去和 B 的子节点比 CollideNodes(nodeIdxA, nB.leftFirst, hitPairs); CollideNodes(nodeIdxA, nB.leftFirst + 1, hitPairs); } else if (nB.isLeaf()) { // B 是叶子,A 是内部节点,拆分 A CollideNodes(nA.leftFirst, nodeIdxB, hitPairs); CollideNodes(nA.leftFirst + 1, nodeIdxB, hitPairs); } else { // A 和 B 都是内部节点,为了最快缩小包围盒范围,理论上应该拆分包围盒表面积更大的那个。 // 这里为了代码简洁,我们统一固定拆分 A 节点。 CollideNodes(nA.leftFirst, nodeIdxB, hitPairs); CollideNodes(nA.leftFirst + 1, nodeIdxB, hitPairs); } } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// extern void DeepFaceToSolid(AcDbBlockReference *pRef, AcDbVoidPtrArray &arGetSolidPtrs); namespace { bool collisionDetection(AcDb3dSolid *pSolidA, AcDb3dSolid *pSolidB, AcGePoint3d *pPtIns = NULL) { if (!pSolidA || !pSolidB) return false; // 1. 过滤自身的比较 if (pSolidA == pSolidB) return false; AcDb3dSolid *pInterferenceSolid = NULL; Adesk::Boolean bIntersects = Adesk::kFalse; // 4. 调用底层的干涉检查 API // 这里的关键是将第二个参数设为 Adesk::kFalse,禁止它生成相交实体! Acad::ErrorStatus es = pSolidA->checkInterference( pSolidB , Adesk::kFalse , bIntersects , pInterferenceSolid ); // 5. 防御性编程:清理内存 // 即使我们传了 kFalse,如果由于某些不可预知的内核原因返回了一个实体指针, // 因为它是内存常驻(非数据库常驻)实体,如果不 delete 会造成严重的内存泄漏。 if (pInterferenceSolid != NULL) { delete pInterferenceSolid; pInterferenceSolid = NULL; } // 6. 返回真实的碰撞结果 if (es == Acad::eOk && bIntersects == Adesk::kTrue) { return true; } return false; } AcDb3dSolid *getOpeningSolid(AcDbEntity *pEnt) { AecDbXOpening *pOpen = AecDbXOpening::cast(pEnt); if (!pOpen) return NULL; OpeningData data; pOpen->GetOpeningData(data); pOpen->GetAt(KEY_BOMEL, data.ptPos.z); // 得到完整的长宽高,和插入位置,构建出一个solid AcDbObjectId idWall; idWall.setFromOldId(data.idWall); AcDbObjectPointer wall(idWall, AcDb::kForRead); if (wall.openStatus() != Acad::eOk) return NULL; AcDbCurve *pCrv(wall->GetBaseCurve()); if (!pCrv) return NULL; AcDb3dSolid *pSolid(NULL); do { AcGePoint3d ptOnCrv; if (pCrv->getClosestPointTo(data.ptPos, ptOnCrv) != Acad::eOk) break; // 墙体在插入点处的切线方向 AcGeVector3d vtDir; if (pCrv->getFirstDeriv(ptOnCrv, vtDir) != Acad::eOk) break; vtDir.normalize(); AcGeVector3d vtUp(vtDir); vtUp.rotateBy(PI * .5, AcGeVector3d::kZAxis); vtUp.normalize(); /* vtDir ->->-> pt0---------pt1 | ptPos | pt3---------pt2 */ AcGePoint3d pt0, pt1, pt2, pt3; pt0 = data.ptPos - vtDir * data.dWidth * .5; pt0 += vtUp * data.dThick * .5; pt1 = pt0 + vtDir * data.dWidth; pt2 = pt1 - vtUp * data.dThick; pt3 = pt2 - vtDir * data.dWidth; AcDbVoidPtrArray ar; ar.append(new AcDbLine(pt0,pt1)); ar.append(new AcDbLine(pt1,pt2)); ar.append(new AcDbLine(pt2,pt3)); ar.append(new AcDbLine(pt3,pt0)); AcDbRegion *pReg = CoreTools::createRegion(ar); deleteArrayVoidPtrs(ar); if (pReg) { AcDbSweepOptions op; pSolid = new AcDb3dSolid(); if (pSolid->createExtrudedSolid(pReg, AcGeVector3d::kZAxis * data.dHeight, op) != Acad::eOk) { DELETE_PTR(pSolid); } DELETE_PTR(pReg); } } while (false); DELETE_PTR(pCrv); return pSolid; } void get3dSolidCacheFromEntity(AcDbEntity *pEnt, AcDbVoidPtrArray &ar_all, AcDbVoidPtrArray &ar_must_del) { if (!pEnt) return; const AcDbVoidPtrArray *pAr(NULL); AcDbVoidPtrArray _ar; AecDbEntityBase *pEntBase = AecDbEntityBase::cast(pEnt); if (pEntBase) { pAr = &pEntBase->Get3DBuffer(); // AecDbXOpening 实体比较特殊,是由AcDbBlockRefrence组成的,block里面由shell构成需要特殊处理 AecDbXOpening *pOpen = AecDbXOpening::cast(pEnt); if (pOpen) { AcDb3dSolid *pSolid = getOpeningSolid(pEnt); if (pSolid) { _ar.append(pSolid); ar_must_del.append(pSolid); } } else _ar = *pAr; } AecDbCurveBase *pCrvBase = AecDbCurveBase::cast(pEnt); if (pCrvBase) { pAr = &pCrvBase->Get3DBuffer(); _ar = *pAr; } for (int i(0); i < _ar.length(); ++i) { AcDb3dSolid *pSolid = AcDb3dSolid::cast((AcDbEntity*)_ar[i]); if (pSolid) ar_all.append(pSolid); } return; } bool isAecMInterfaces(AcDbObjectId &id) { return UIMenuItem::isAecMInterfaces(id); } bool isOpening(AcDbObjectId &id) { return CDA::isSameOf(id); } bool isWall(AcDbObjectId &id) { return CDA::isSameOf(id); } bool isEqu(AcDbObjectId &id) { if (CDA::isSameOf(id) || CDA::isSameOf(id)) return true; return false; } bool isPipeValve(AcDbObjectId &id) { if (id.objectClass() != AecDbEwEquipment::desc()) return false; AcDbObjectPointer ent(id, AcDb::kForRead); if (ent.openStatus() != Acad::eOk) return false; int subRuntime(0), valveType(0); ent->GetAt(KEY_SUBRUNTIME, subRuntime); if (subRuntime != eValve) return false; if (!ent->GetAt(PIPE_VALVE, valveType)) return false; return true; } bool isEquButNoValve(AcDbObjectId &id) { return isEqu(id) && !isPipeValve(id); } bool isEnvelope(AcDbObjectId &id) { CEntityProperty::EEntityType et(CEntityProperty::getTypeOfEntity(id)); return et == CEntityProperty::eArchitecture || et == CEntityProperty::eStructure || et == CEntityProperty::eGuardBar || et == CEntityProperty::eHanger || et == CEntityProperty::eStair; } bool isHanger(AcDbObjectId &id) { CEntityProperty::EEntityType et(CEntityProperty::getTypeOfEntity(id)); return et == CEntityProperty::eHanger; } bool isHole(AcDbObjectId &id) { return CEntityProperty::isHole(id); } bool isJSJ(AcDbObjectId &id) { return CEntityProperty::isJSJ(id); } bool isPipe(AcDbObjectId &id) { CEntityProperty::EEntityType et(CEntityProperty::getTypeOfEntity(id)); return et == CEntityProperty::eCabtry || et == CEntityProperty::eCabtryFitting || et == CEntityProperty::eCable || et == CEntityProperty::eHVACFitting || et == CEntityProperty::eHVAC || et == CEntityProperty::ePipeFitting || et == CEntityProperty::ePipe ; } // 门窗和墙体需要碰撞检测吗? bool needCD_WallAndOpening(AcDbObjectId &id1, AcDbObjectId &id2) { AcDbObjectPointer opening(id1, AcDb::kForRead); AcDbObjectId idWall; if (opening.open(id1, AcDb::kForRead) != Acad::eOk) { opening.open(id2, AcDb::kForRead); idWall = id1; } else { idWall = id2; } if (opening.openStatus() != Acad::eOk) return true; OpeningData data; ::memset(&data, 0, sizeof(OpeningData)); if (!opening->GetOpeningData(data)) return true; AcDbObjectId idOpenWall; idOpenWall.setFromOldId(data.idWall); // 是墙上的门窗,不需要比较,不碰撞 if (idWall == idOpenWall) return false; return true; } // 两个有管嘴的实体需要碰撞吗? bool needCD_2Interface(AcDbObjectId &id1, AcDbObjectId &id2) { AcDbEntityPointer ent1(id1, AcDb::kForRead); AcDbEntityPointer ent2(id2, AcDb::kForRead); if (ent1.openStatus() != Acad::eOk || ent2.openStatus() != Acad::eOk) return true; AecMInterfaces *pInf1 = dynamic_cast(ent1.object()); AecMInterfaces *pInf2 = dynamic_cast(ent2.object()); if (!pInf1 || !pInf2) return true; AcArray arInfs1, arInfs2; pInf1->GetInterface(arInfs1); pInf2->GetInterface(arInfs2); AecInterface inf1, inf2; for (int i(0); i < arInfs1.length(); ++i) { inf1 = arInfs1[i]; for (int j(0); j < arInfs2.length(); ++j) { inf2 = arInfs2[j]; // 不能严格判断距离,小于10就算连上 if (inf1.ptPos.distanceTo(inf2.ptPos) < 10) return false; } } return true; } // 如果两个个实体不需要检测,返回false,需要则返回true bool needCD(AcDbObjectId &id1, AcDbObjectId &id2) { // 如果有一个实体是洞口,不需要碰撞检测 if (isHole(id1) || isHole(id2)) return false; // 管线和支架不需要碰撞 if ((isPipe(id1) && isHanger(id2)) || (isPipe(id2) && isHanger(id1))) return false; // 设备和支架不需要碰撞 if ((isEqu(id1) && isHanger(id2)) || (isEqu(id2) && isHanger(id1))) return false; // 构筑物之间,不需要碰撞检测 if (isEnvelope(id1) && isEnvelope(id2) ) return false; // 墙和窗体,判断两个实体的的连接关系,如果存在连接关系,不需要碰撞检测 if ((isWall(id1) && isOpening(id2)) || (isWall(id2) && isOpening(id1))) return needCD_WallAndOpening(id1, id2); // 由于板和设备都有接口,所以,放到接口的判断之前判断 // 板实体上经常会有非阀门的设备,如果是这样不应该碰撞检测 if ((CDA::isSameOf(id1) && isEquButNoValve(id2)) || (CDA::isSameOf(id2) && isEquButNoValve(id1))) return false; // 有接口的两个实体,判断是否有接口相连,如果有,不需要碰撞检测 if (isAecMInterfaces(id1) && isAecMInterfaces(id2)) return needCD_2Interface(id1, id2); // 板实体上的集水井不需要碰撞 if ( (isJSJ(id1) && CDA::isSameOf(id2)) || (isJSJ(id2) && CDA::isSameOf(id1))) return false; return true; } bool collisionDetection(AcDbObjectId &id1, AcDbObjectId &id2) { if (!needCD(id1, id2)) return false; using namespace CDA; // 1. 在整个检测期间,将两个实体保持打开状态,防止其内部数据变为野指针! AcDbEntityPointer pEnt1(id1, AcDb::kForRead); if (pEnt1.openStatus() != Acad::eOk) return false; AcDbEntityPointer pEnt2(id2, AcDb::kForRead); if (pEnt2.openStatus() != Acad::eOk) return false; // 2. 获取缓存数组 AcDbVoidPtrArray ar1, ar2, ar_must_del; get3dSolidCacheFromEntity(pEnt1.object(), ar1, ar_must_del); get3dSolidCacheFromEntity(pEnt2.object(), ar2, ar_must_del); if (ar1.isEmpty() || ar2.isEmpty()) return false; // 比较两个实体的原则,是只要它们的任意一个子组件发生碰撞,我们就认为它们发生了碰撞。 // 只比较子组件中是AcDb3dSolid的部分,其他类型的子组件(如曲面、线框等)不参与碰撞检测。 // 遍历 A 里面的每一个小组件 AcDbExtents ext1, ext2; AABB ab1, ab2; AcDb3dSolid *pSolid1(NULL), *pSolid2(NULL); for (int i = 0; i < ar1.length(); ++i) { // 只比较3dSolid pSolid1 = AcDb3dSolid::cast((AcDbEntity *)ar1[i]); if (!pSolid1) continue; // 获取 A 子组件的 AABB if (pSolid1->getGeomExtents(ext1) != Acad::eOk) continue; ab1.set(ext1); // 遍历 B 里面的每一个小组件 for (int j = 0; j < ar2.length(); ++j) { pSolid2 = AcDb3dSolid::cast((AcDbEntity *)ar2[j]); if (!pSolid2) continue; // 获取 B 子组件的 AABB if (pSolid2->getGeomExtents(ext2) != Acad::eOk) continue; ab2.set(ext2); // 微观粗筛:用 C++98 风格判断两个子 Extents 是否重叠 if (!ab1.intersects(ab2)) continue; // 子包围盒不碰,直接跳过底层几何计算! if (collisionDetection(pSolid1, pSolid2)) { deleteArrayVoidPtrs(ar_must_del); return true; } } } deleteArrayVoidPtrs(ar_must_del); return false; } } namespace { void clearOldCDFlag() { ads_name ss, ent; struct resbuf *pRb = acutBuildList(RTDXF0, _T("3DSOLID"), 0); int nRes; nRes = acedSSGet(_T("A"), NULL, NULL, pRb, ss); acutRelRb(pRb); if (nRes != RTNORM) return; Adesk::Int32 lLen = 0L; acedSSLength(ss, &lLen); if (lLen == 0) return; AecDbObjXDataMap xData; AcDbObjectId id; for (long i = 0; i < lLen; i++) { acedSSName(ss, i, ent); acdbGetObjectId(id, ent); if (!id.isValid()) continue; if (xData.Read(id, CD_FLAG)) { AcDbEntityPointer ent(id, AcDb::kForWrite); if (ent.openStatus() == Acad::eOk) ent->erase(); } } acedSSFree(ss); } } CCollisionDetectionDlg *g_pDlg = NULL; void pzjc(const bool &bSelect) { clearOldCDFlag(); using namespace CDA; ads_name ss, ent; struct resbuf* pRb = acutBuildList(RTDXF0, _T("AEC_*,INSERT"), 0); int nRes; if (!bSelect) nRes = acedSSGet(_T("A"), NULL, NULL, pRb, ss); else nRes = acedSSGet(NULL, NULL, NULL, pRb, ss); acutRelRb(pRb); if (nRes != RTNORM) return; Adesk::Int32 lLen = 0L; acedSSLength(ss, &lLen); if (lLen == 0) return; AcDbObjectIdArray arids; AcDbObjectId id; for (long i = 0; i < lLen; i++) { acedSSName(ss, i, ent); acdbGetObjectId(id, ent); if (!id.isValid()) continue; arids.append(id); } acedSSFree(ss); if (arids.isEmpty()) return; BVHTree bvh; bvh.Build(arids); // 1. 获取所有粗筛相交的实体对 (绝无重复!) std::vector< std::pair > hitPairs, hitReals; bvh.GetAllIntersections(hitPairs); // 2. 逐对进行底层内核精细检查 acedSetStatusBarProgressMeter(_T("正在处理实体,请稍候..."), 0, (int)hitPairs.size()); for (size_t i = 0; i < hitPairs.size(); ++i) { if (acedUsrBrk() == 1) { if (AfxMessageBox(_T("用户按下了 ESC 键, 确定取消检查操作吗?"), MB_YESNO) == IDYES) break; } if (collisionDetection(hitPairs[i].first, hitPairs[i].second)) { hitReals.push_back(hitPairs[i]); } acedSetStatusBarProgressMeterPos((int)i + 1); } acedRestoreStatusBar(); acutPrintf(_T("\nnumber of cd:%d"), hitReals.size()); if (hitReals.empty()) { CString sMsg; sMsg.Format(_T("检查了%d个实体,未找到碰撞点。"), lLen); AfxMessageBox(sMsg); return; } { if (g_pDlg) { if (::IsWindow(g_pDlg->m_hWnd)) g_pDlg->DestroyWindow(); DELETE_PTR(g_pDlg); } else { g_pDlg = new CCollisionDetectionDlg(hitReals, acedGetAcadFrame()); g_pDlg->Create(CCollisionDetectionDlg::IDD, acedGetAcadFrame()); g_pDlg->CenterWindow(); g_pDlg->ShowWindow(SW_SHOW); } } } void pzjca() { pzjc(false); } void pzjcs() { pzjc(true); } #pragma region CCollisionDetectionDlg namespace { static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { typedef CCollisionDetectionDlg::DataContext DataContext; typedef CCollisionDetectionDlg::SortContext SortContext; XLISTCTRLDATA *pXData1 = reinterpret_cast(lParam1); XLISTCTRLDATA *pXData2 = reinterpret_cast(lParam2); if (!pXData1 || !pXData2) return 0; DataContext *pData1 = reinterpret_cast(pXData1->dwItemData); DataContext *pData2 = reinterpret_cast(pXData2->dwItemData); SortContext *pContext = reinterpret_cast(lParamSort); if (!pData1 || !pData2 || !pContext) return 0; int nResult = 0; // 根据点击的列号,执行不同的比较逻辑 switch (pContext->m_nColumn ) { case 0: if (pData1->m_nIdx < pData2->m_nIdx) nResult = -1; else if (pData1->m_nIdx > pData2->m_nIdx) nResult = 1; else nResult = 0; break; case 1: nResult = pData1->m_sName1.CompareNoCase(pData2->m_sName1); break; case 2: nResult = pData1->m_sSys1.CompareNoCase(pData2->m_sSys1); break; case 3: nResult = pData1->m_sName2.CompareNoCase(pData2->m_sName2); break; case 4: nResult = pData1->m_sSys2.CompareNoCase(pData2->m_sSys2); break; default: break; } // 根据升降序状态返回最终结果 return pContext->m_bAscending ? nResult : -nResult; } void GetCurrentDynamicView(AcDbViewTableRecord &viewRec) { struct resbuf rb; struct resbuf rbWcs, rbUcs; // 0 代表 WCS,1 代表 UCS rbWcs.restype = RTSHORT; rbWcs.resval.rint = 0; rbUcs.restype = RTSHORT; rbUcs.resval.rint = 1; // 1. 获取 VIEWMODE (包含透视、剪裁等布尔状态) if (acedGetVar(_T("VIEWMODE"), &rb) == RTNORM) { viewRec.setPerspectiveEnabled((rb.resval.rint & 1) != 0); viewRec.setFrontClipEnabled((rb.resval.rint & 2) != 0); viewRec.setBackClipEnabled((rb.resval.rint & 4) != 0); viewRec.setFrontClipAtEye((rb.resval.rint & 16) == 0); } ads_point ptTrans; // 2. 获取 VIEWDIR (视线方向,UCS矢量 -> WCS矢量) if (acedGetVar(_T("VIEWDIR"), &rb) == RTNORM) { // acedTrans 的第 4 个参数为 1,表示正在转换一个方向矢量(Vector)而不是坐标点 acedTrans(rb.resval.rpoint, &rbUcs, &rbWcs, 1, ptTrans); viewRec.setViewDirection(AcGeVector3d(ptTrans[0], ptTrans[1], ptTrans[2])); } // 3. 获取 TARGET (目标中心点,UCS点 -> WCS点) if (acedGetVar(_T("TARGET"), &rb) == RTNORM) { // acedTrans 的第 4 个参数为 0,表示正在转换一个坐标点(Point) acedTrans(rb.resval.rpoint, &rbUcs, &rbWcs, 0, ptTrans); viewRec.setTarget(AcGePoint3d(ptTrans[0], ptTrans[1], ptTrans[2])); } // 4. 获取 VIEWTWIST (相机扭曲角度) if (acedGetVar(_T("VIEWTWIST"), &rb) == RTNORM) { viewRec.setViewTwist(rb.resval.rreal); } // 5. 获取 LENSLENGTH (镜头焦距) if (acedGetVar(_T("LENSLENGTH"), &rb) == RTNORM) { viewRec.setLensLength(rb.resval.rreal); } } // 主函数:将视图缩放至指定实体 void ZoomToEntity(AcDbObjectId entId) { Acad::ErrorStatus es; AcDbEntity *pEnt = NULL; es = acdbOpenAcDbEntity(pEnt, entId, AcDb::kForRead); if (es != Acad::eOk) { acutPrintf(_T("\n无法打开实体.")); return; } AcDbExtents extents; es = pEnt->getGeomExtents(extents); pEnt->close(); if (es != Acad::eOk) { acutPrintf(_T("\n无法获取实体几何边界.")); return; } AcGePoint3d ptMax(extents.maxPoint()), ptMin(extents.minPoint()); // 构造 WCS 包围盒的 8 个顶点 AcGePoint3d ptWcs[8]; ptWcs[0] = ptMin; ptWcs[1] = AcGePoint3d(ptMin.x, ptMin.y, ptMax.z); ptWcs[2] = AcGePoint3d(ptMin.x, ptMax.y, ptMin.z); ptWcs[3] = AcGePoint3d(ptMin.x, ptMax.y, ptMax.z); ptWcs[4] = AcGePoint3d(ptMax.x, ptMin.y, ptMin.z); ptWcs[5] = AcGePoint3d(ptMax.x, ptMin.y, ptMax.z); ptWcs[6] = AcGePoint3d(ptMax.x, ptMax.y, ptMin.z); ptWcs[7] = ptMax; // WCS(0) 到 DCS(2) 转换,寻找真正的屏幕极值 resbuf rbWcs, rbDcs; rbWcs.restype = RTSHORT; rbWcs.resval.rint = 0; rbDcs.restype = RTSHORT; rbDcs.resval.rint = 2; double minX = 1e20, minY = 1e20; double maxX = -1e20, maxY = -1e20; for (int i = 0; i < 8; ++i) { ads_point ptInWcs, ptInDcs; ptInWcs[0] = ptWcs[i].x; ptInWcs[1] = ptWcs[i].y; ptInWcs[2] = ptWcs[i].z; // 这里的 acedTrans 依旧会自动引用当前屏幕正在呈现的 DCS 矩阵 acedTrans(ptInWcs, &rbWcs, &rbDcs, 0, ptInDcs); if (ptInDcs[0] < minX) minX = ptInDcs[0]; if (ptInDcs[0] > maxX) maxX = ptInDcs[0]; if (ptInDcs[1] < minY) minY = ptInDcs[1]; if (ptInDcs[1] > maxY) maxY = ptInDcs[1]; } double dWidth = fabs(maxX - minX) * 2.; double dHeight = fabs(maxY - minY) * 2.; AcGePoint2d centerPt(minX + (maxX - minX) * .5, minY + (maxY - minY) * .5); struct resbuf screenRb; acedGetVar(_T("SCREENSIZE"), &screenRb); if (screenRb.resval.rpoint[1] > 0.0) { double screenRatio = screenRb.resval.rpoint[0] / screenRb.resval.rpoint[1]; if (dWidth > dHeight * screenRatio) { dHeight = dWidth / screenRatio; dWidth = dHeight * screenRatio; } } // 核心修复:通过封装的系统变量函数,获取完整的动态三维环境 AcDbViewTableRecord viewRec; GetCurrentDynamicView(viewRec); // 将计算出的 DCS 中心点与高度覆盖进去 viewRec.setCenterPoint(centerPt); viewRec.setHeight(dHeight); viewRec.setWidth(dWidth); // 更新视图,彻底解决视角被拉回俯视图的问题 acedSetCurrentView(&viewRec, NULL); } } extern UINT WM_XLISTCTRL_CHECKBOX_CLICKED; extern UINT WM_XLISTCTRL_HEADER_MOUSE_CLICKED; IMPLEMENT_DYNAMIC(CCollisionDetectionDlg, CDialog) BEGIN_MESSAGE_MAP(CCollisionDetectionDlg, CDialog) ON_WM_SIZE() ON_WM_GETMINMAXINFO() ON_WM_CLOSE() ON_BN_CLICKED(IDOK, &CCollisionDetectionDlg::OnOK) ON_BN_CLICKED(IDC_BTN_REFRESH, &CCollisionDetectionDlg::OnRefreshBtnClicked) ON_REGISTERED_MESSAGE(WM_XLISTCTRL_CHECKBOX_CLICKED, &CCollisionDetectionDlg::OnCheckBoxClicked) ON_REGISTERED_MESSAGE(WM_XLISTCTRL_HEADER_MOUSE_CLICKED, &CCollisionDetectionDlg::OnHeaderMouseClicked) ON_NOTIFY(NM_DBLCLK, IDC_LIST, &CCollisionDetectionDlg::OnNMDblclkList) END_MESSAGE_MAP() CCollisionDetectionDlg::CCollisionDetectionDlg(IDPairs &pairs, CWnd *pParent) : m_szMinDlgSize(800, 600) , m_idPairs(pairs) , m_nSortCol(-1) , m_bAscending(true) { m_CdFlags.resize(pairs.size()); for (size_t i(0); i < m_CdFlags.size(); ++i) m_CdFlags[i] = AcDbObjectId::kNull; } CCollisionDetectionDlg::~CCollisionDetectionDlg() { clearDatas(); } void CCollisionDetectionDlg::DoDataExchange(CDataExchange *pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST, m_wndList); } BOOL CCollisionDetectionDlg::OnInitDialog() { CDialog::OnInitDialog(); initColumns(); layoutControls(0, 0); refreshList(); return 0; } void CCollisionDetectionDlg::refreshList() { m_wndList.DeleteAllItems(); const AecDbObjXDataMap *pXData1(NULL), *pXData2(NULL); CString sName1, sName2, sSys1, sSys2; AcDbObjectId id1, id2; IDPairs::const_iterator it(m_idPairs.begin()); for (int i(0); it != m_idPairs.end(); ++it, ++i) { id1 = it->first; id2 = it->second; sName1 = CEntityProperty::getNameOfEntity(id1); sName2 = CEntityProperty::getNameOfEntity(id2); sSys1 = _T("无"); pXData1 = CEntityProperty::getXDataConst(id1); if (pXData1) pXData1->GetAt(PIPE_SYS, sSys1); sSys2 = _T("无"); pXData2 = CEntityProperty::getXDataConst(id2); if (pXData2) pXData2->GetAt(PIPE_SYS, sSys2); m_wndList.InsertItem(i, TypeConvert::i2s(i + 1)); m_wndList.SetItemText(i, 1, sName1); m_wndList.SetItemText(i, 2, sSys1); m_wndList.SetItemText(i, 3, sName2); m_wndList.SetItemText(i, 4, sSys2); m_wndList.SetCheckbox(i, 0, FALSE); m_wndList.SetItemData(i, (DWORD_PTR)(new DataContext(i, sName1, sSys1, sName2, sSys2))); } } void CCollisionDetectionDlg::initColumns() { double dDpiScale = CoreTools::GetDPIScale(); m_wndList.SetFont(GetFont()); m_wndList.SetExtendedStyle( m_wndList.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); // 第一列,选择列 LV_COLUMN lvcolumn; ::memset(&lvcolumn, 0, sizeof(lvcolumn)); lvcolumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvcolumn.fmt = LVCFMT_CENTER; int width(int(30 * dDpiScale)); lvcolumn.pszText = const_cast(_T("序号")); lvcolumn.iSubItem = 0; lvcolumn.cx = width; m_wndList.InsertColumn(0, &lvcolumn); lvcolumn.pszText = const_cast(_T("实体类型1")); lvcolumn.iSubItem = 1; lvcolumn.cx = width; m_wndList.InsertColumn(1, &lvcolumn); lvcolumn.pszText = const_cast(_T("系统类型1")); lvcolumn.iSubItem = 2; lvcolumn.cx = width; m_wndList.InsertColumn(2, &lvcolumn); lvcolumn.pszText = const_cast(_T("实体类型2")); lvcolumn.iSubItem = 3; lvcolumn.cx = width; m_wndList.InsertColumn(3, &lvcolumn); lvcolumn.pszText = const_cast(_T("系统类型2")); lvcolumn.iSubItem = 4; lvcolumn.cx = width; m_wndList.InsertColumn(4, &lvcolumn); // 设置第一列为选择项 if (m_wndList.m_HeaderCtrl.GetItemCount() > 0) { HDITEM hditem; hditem.mask = HDI_FORMAT | HDI_IMAGE; m_wndList.m_HeaderCtrl.GetItem(0, &hditem); hditem.fmt |= HDF_IMAGE; hditem.iImage = XHEADERCTRL_UNCHECKED_IMAGE; m_wndList.m_HeaderCtrl.SetItem(0, &hditem); } } void CCollisionDetectionDlg::layoutControls(const int &cx, const int &cy) { if (!m_wndList.GetSafeHwnd()) return; double dDpiScale = CoreTools::GetDPIScale(); CRect rcBtn(CPoint(0, 0), CSize(30, 90)); CWnd *pOKBtn = GetDlgItem(IDOK); CWnd *pRefreshBtn = GetDlgItem(IDC_BTN_REFRESH); if (pOKBtn) pOKBtn->GetWindowRect(rcBtn); CSize szBtn = rcBtn.Size(); CRect rcDlg; GetClientRect(rcDlg); int nSpace(int(5 * dDpiScale)); // "确定"按钮 下边与dlg距离 nSpace rcBtn.bottom = rcDlg.bottom - nSpace; rcBtn.top = rcBtn.bottom - szBtn.cy; rcBtn.left = LONG((rcDlg.Width() - szBtn.cx) * .5); rcBtn.right = rcBtn.left + szBtn.cx; // m_wndList 上边、左边、右边与dlg距离 nSpace CRect rcList; rcList.left = rcDlg.left + nSpace; rcList.top = rcDlg.top + nSpace; rcList.right = rcDlg.right - nSpace; rcList.bottom = rcBtn.top - nSpace; if (pOKBtn) pOKBtn->MoveWindow(rcBtn); // pRefreshBtn bottom与pOKBtn相同, right与wndlist的right相同 rcBtn.right = rcList.right; rcBtn.left = rcBtn.right - szBtn.cx; if (pRefreshBtn) pRefreshBtn->MoveWindow(rcBtn); m_wndList.MoveWindow(rcList); int col1 = int(80 * dDpiScale); m_wndList.GetClientRect(rcList); CSize sz = rcList.Size(); sz.cx -= col1; // 第一列为序号 sz.cx = LONG(sz.cx * .25); m_wndList.SetColumnWidth(0, col1); m_wndList.SetColumnWidth(1, (int)sz.cx); m_wndList.SetColumnWidth(2, (int)sz.cx); m_wndList.SetColumnWidth(3, (int)sz.cx); m_wndList.SetColumnWidth(4, LVSCW_AUTOSIZE_USEHEADER); } void CCollisionDetectionDlg::eraseFlag(AcDbObjectId &id) { AcDbEntityPointer ent(id, AcDb::kForWrite); if (ent.openStatus() == Acad::eOk) ent->erase(); } AcDbObjectId CCollisionDetectionDlg::createFlag(const AcDbObjectId & id1, const AcDbObjectId & id2) { AcDbObjectId id(AcDbObjectId::kNull); if (!id1.isValid() || !id2.isValid()) return id; AcDbEntityPointer ent1(id1, AcDb::kForRead); AcDbEntityPointer ent2(id2, AcDb::kForRead); if (ent1.openStatus() != Acad::eOk || ent2.openStatus() != Acad::eOk) return id; AcDbVoidPtrArray ar1, ar2, ar_must_del; get3dSolidCacheFromEntity(ent1, ar1, ar_must_del); get3dSolidCacheFromEntity(ent2, ar2, ar_must_del); AcDb3dSolid *pInterferenceSolid = NULL; Adesk::Boolean bIntersects = Adesk::kFalse; double volume(.0); AcGePoint3d centroid(AcGePoint3d::kOrigin); double momInertia[3], prodInertia[3], prinMoments[3], radiiGyration[3]; AcGeVector3d prinAxes[3]; AcDbExtents extents; AecDbObjXDataMap xData; xData.SetAt(CD_FLAG, 1); bool bExit = false; for (int i1(0); i1 < ar1.length(); ++i1) { if (bExit) break; AcDb3dSolid *pSolid1 = AcDb3dSolid::cast((AcDbEntity *)(ar1)[i1]); if (!pSolid1) continue; for (int i2(0); i2 < ar2.length(); ++i2) { if (bExit) break; AcDb3dSolid *pSolid2 = AcDb3dSolid::cast((AcDbEntity *)(ar2)[i2]); if (!pSolid2) continue; bIntersects = Adesk::kFalse; Acad::ErrorStatus es = pSolid1->checkInterference(pSolid2 , Adesk::kTrue , bIntersects , pInterferenceSolid); // 得到碰撞的中心点 if (es == Acad::eOk && bIntersects == Adesk::kTrue && pInterferenceSolid) { centroid = (AcGePoint3d::kOrigin); es = pInterferenceSolid->getMassProp(volume , centroid , momInertia , prodInertia , prinMoments , prinAxes , radiiGyration , extents); DELETE_PTR(pInterferenceSolid); if (es == Acad::eOk) { AcDb3dSolid *pSolid = new AcDb3dSolid; if (pSolid->createSphere(extents.maxPoint().distanceTo(extents.minPoint()) * .5 * 1.5) == Acad::eOk) { AcGeMatrix3d mat; mat.setToTranslation(centroid.asVector()); pSolid->transformBy(mat); pSolid->setColorIndex(1); // 做一下标记 xData.Write(pSolid, _T("CD_FLAG")); CoreTools::AddToModelSpace(pSolid, NULL, true, &id); bExit = true; } else DELETE_PTR(pSolid); } } } } deleteArrayVoidPtrs(ar_must_del); return id; } void CCollisionDetectionDlg::clearDatas() { AcAxDocLock lock; for (size_t i(0); i < m_CdFlags.size(); ++i) eraseFlag(m_CdFlags[i]); for (int i(0); i < m_wndList.GetItemCount(); ++i) { DataContext *pData = (DataContext *)m_wndList.GetItemData(i); DELETE_PTR(pData); } } void CCollisionDetectionDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); layoutControls(cx, cy); } void CCollisionDetectionDlg::OnGetMinMaxInfo(MINMAXINFO *lpMMI) { CDialog::OnGetMinMaxInfo(lpMMI); // 只有当 m_minSize 有效(即 OnInitDialog 运行过)时才限制 // 防止窗口创建初期 m_minSize 为 0 导致窗口无法显示 if (m_szMinDlgSize.cx > 0 && m_szMinDlgSize.cy > 0) { // ptMinTrackSize 代表窗口允许的最小“追踪”尺寸 (拖拽时的尺寸) lpMMI->ptMinTrackSize.x = m_szMinDlgSize.cx; // 限制最小宽度 lpMMI->ptMinTrackSize.y = m_szMinDlgSize.cy; // 限制最小高度 } } LRESULT CCollisionDetectionDlg::OnCheckBoxClicked(WPARAM nItem, LPARAM nSubItem) { if (nItem >= m_idPairs.size() || nItem < 0) return 0; if (nItem >= m_CdFlags.size() || nItem < 0) return 0; { AcAxDocLock lock; DataContext &ct = *(DataContext*)m_wndList.GetItemData((int)nItem); int idxFlag(ct.m_nIdx); // 得到两个碰撞的实体id AcDbObjectId id1(m_idPairs[idxFlag].first), id2(m_idPairs[idxFlag].second); // 碰撞标记id AcDbObjectId &idFlag = m_CdFlags[idxFlag]; bool bCheck = m_wndList.GetItemCheckedState((int)nItem, (int)nSubItem) != 0 ? true : false; if (bCheck) // 需要显示碰撞标记 { if (idFlag.isValid()) { AcDbEntityPointer ent(idFlag, AcDb::kForRead); if (ent.openStatus() == Acad::eOk && ent->visibility() != AcDb::kVisible) { ent->upgradeOpen(); ent->setVisibility(AcDb::kVisible); ent->downgradeOpen(); } } else { // 创建碰撞标记 idFlag = createFlag(id1, id2); } } else // 需要隐藏碰撞标记 { // 不需要考虑idFlag无效的情况,因为如果无效说明之前没有创建过标记,自然也就不需要隐藏了 AcDbEntityPointer ent(idFlag, AcDb::kForRead); if (ent.openStatus() == Acad::eOk && ent->visibility() == AcDb::kVisible) { ent->upgradeOpen(); ent->setVisibility(AcDb::kInvisible); ent->downgradeOpen(); } } } ::SetFocus(adsw_acadDocWnd()); CoreTools::refreshGraphics(); return 0; } LRESULT CCollisionDetectionDlg::OnHeaderMouseClicked(WPARAM, LPARAM nSubItem) { if (m_nSortCol == nSubItem) { m_bAscending = !m_bAscending; } else { m_nSortCol = (int)nSubItem; m_bAscending = true; } // 构建 排序信息 SortContext sc; // 如果点了同一列,就切换排序顺序;如果点了不同列,默认升序 sc.m_bAscending = m_bAscending; sc.m_nColumn = m_nSortCol; m_wndList.SortItems(CompareFunc, reinterpret_cast(&sc)); CHeaderCtrl *pHeader = m_wndList.GetHeaderCtrl(); if (!pHeader) return 0; for (int i = 0; i < pHeader->GetItemCount(); ++i) { HDITEM hditem; // 只需要获取和设置格式(Format)掩码 hditem.mask = HDI_FORMAT; pHeader->GetItem(i, &hditem); // 核心位运算:先清除掉当前列可能存在的任何排序标志 hditem.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); // 如果是当前正在排序的列,则追加对应的方向标志 if (i == m_nSortCol) { if (m_bAscending) { hditem.fmt |= HDF_SORTUP; } else { hditem.fmt |= HDF_SORTDOWN; } } // 将修改后的格式写回表头 pHeader->SetItem(i, &hditem); } return 0; } void CCollisionDetectionDlg::OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); // 获取被双击的行号 (-1 表示没有选中项) int nIndex = pNMItemActivate->iItem; // 获取被双击的列号 int nSubItem = pNMItemActivate->iSubItem; DataContext &ct = *(DataContext*)m_wndList.GetItemData(nIndex); int idxFlag(ct.m_nIdx); // 得到两个碰撞的实体id AcDbObjectId id1(m_idPairs[idxFlag].first), id2(m_idPairs[idxFlag].second); // 碰撞标记id AcDbObjectId &idFlag = m_CdFlags[idxFlag]; AcAxDocLock lock; if (!idFlag.isValid()) { idFlag = createFlag(id1, id2); AcDbEntityPointer ent(idFlag, AcDb::kForWrite); if (ent.openStatus() == Acad::eOk) { ent->setVisibility(AcDb::kInvisible); ent->close(); } } ZoomToEntity(idFlag); } void CCollisionDetectionDlg::PostNcDestroy() { CDialog::PostNcDestroy(); DELETE_PTR(g_pDlg); // 清理图形 } void CCollisionDetectionDlg::OnCancel() { CDialog::OnCancel(); DestroyWindow(); } void CCollisionDetectionDlg::OnClose() { CDialog::OnClose(); DestroyWindow(); } void CCollisionDetectionDlg::OnOK() { CDialog::OnOK(); DestroyWindow(); } void CCollisionDetectionDlg::OnRefreshBtnClicked() { if (m_idPairs.empty()) return; // 删除无效的flag球 clearOldCDFlag(); // 清空数据 clearDatas(); IDPairs prs; // 对列表内的结果重新碰撞验证 for (size_t i(0); i < m_idPairs.size(); ++i) { if (collisionDetection(m_idPairs[i].first, m_idPairs[i].second)) { prs.push_back(m_idPairs[i]); } } m_idPairs = prs; m_CdFlags.resize(m_idPairs.size()); for (size_t i(0); i < m_CdFlags.size(); ++i) m_CdFlags[i] = AcDbObjectId::kNull; refreshList(); } #pragma endregion