#pragma once #include #include "resource.h" #include "xlistctrl.h" #include // 包含 std::min, std::max namespace CDA { template static bool isKindOf(const AcDbObjectId &id) { if (!id.isValid()) return false; AcRxClass *pIdClass = id.objectClass(); if (pIdClass == NULL) return false; return pIdClass->isDerivedFrom( T::desc()); } template static bool isSameOf(const AcDbObjectId &id) { if (!id.isValid()) return false; return id.objectClass() == T::desc(); } struct AABB { AABB(); AABB(const AcDbExtents &ext); // 辅助方法:合并另一个 AABB void grow(const AABB &other); void set(const AcDbExtents &ext); // 辅助方法:计算包围盒的中心点 (用于拆分节点) AcGePoint3d centroid() const; // 辅助方法:获取最长轴 (0=X, 1=Y, 2=Z) int getLongestAxis() const; bool intersects(const AABB &other) const; AcGePoint3d m_minPt; AcGePoint3d m_maxPt; }; struct EntityProxy { bool set(const AcDbObjectId &id); AcDbObjectId m_entityId; // 彻底抛弃 m_entPtr 成员,避免悬空指针和内存灾难 AABB m_box; AcGePoint3d m_centroid; }; // 使用 alignas 确保内存对齐 (可选,但对现代 CPU 缓存极其友好) struct BVHNode { AABB box; int leftFirst; int primitiveCount; bool isLeaf() const { return primitiveCount > 0; } }; class BVHTree { public: // 外部世界只需要调用这一个接口 void Build(const AcDbObjectIdArray &ar); // 给定一个查询的包围盒,返回所有与它发生包围盒碰撞的实体 ID void GetIntersections(const AABB &queryBox, std::vector &hitIds) const; void GetIntersections(AcDbObjectId &id, std::vector &hitIds) const; // 两两自检接口 void GetAllIntersections(std::vector< std::pair > &hitPairs) const; // 供后续碰撞查询使用的只读接口 const std::vector &GetNodes() const { return m_bvhNodes; } const std::vector &GetEntities() const { return m_allEntities; } private: std::vector m_allEntities; std::vector m_bvhNodes; // C++98 的仿函数作为私有嵌套结构体,保持命名空间干净 struct PartitionPredicate { int m_axis; double m_splitPos; PartitionPredicate(int axis, double splitPos): m_axis(axis), m_splitPos(splitPos) {} bool operator()(const EntityProxy &proxy) const { double pos = 0.0; if (m_axis == 0) pos = proxy.m_centroid.x; else if (m_axis == 1) pos = proxy.m_centroid.y; else pos = proxy.m_centroid.z; return pos < m_splitPos; } }; // 内部递归建树函数,直接操作类成员变量 void BuildNode(int nodeIdx, int entityOffset, int entityCount); // 核心的双树递归遍历函数 void CollideNodes(int nodeIdxA, int nodeIdxB, std::vector< std::pair > &hitPairs) const; }; } class CCollisionDetectionDlg: public CDialog { public: typedef std::pair IDPair; typedef std::vector IDPairs; struct SortContext { int m_nColumn; bool m_bAscending; }; struct DataContext { DataContext(int &idx, LPCTSTR name1, LPCTSTR sys1, LPCTSTR name2, LPCTSTR sys2) : m_nIdx(idx) , m_sName1(name1) , m_sName2(name2) , m_sSys1(sys1) , m_sSys2(sys2) { } // 此数据位于m_idPairs的位置,同时,它也是m_CdFlags的位置 int m_nIdx; CString m_sName1, m_sName2, m_sSys1, m_sSys2; }; DECLARE_DYNAMIC(CCollisionDetectionDlg) public: CCollisionDetectionDlg(IDPairs &pairs, CWnd* pParent = NULL); virtual ~CCollisionDetectionDlg(); enum { IDD = IDD_CD_DLG }; protected: virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); virtual void PostNcDestroy(); virtual void OnCancel(); void refreshList(); void initColumns(); void layoutControls(const int &cx, const int &cy); void eraseFlag(AcDbObjectId &id); AcDbObjectId createFlag(const AcDbObjectId &id1, const AcDbObjectId &id2); void clearDatas(); DECLARE_MESSAGE_MAP() afx_msg void OnClose(); afx_msg void OnOK(); afx_msg void OnRefreshBtnClicked(); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI); afx_msg LRESULT OnCheckBoxClicked(WPARAM, LPARAM); afx_msg LRESULT OnHeaderMouseClicked(WPARAM, LPARAM); afx_msg void OnNMDblclkList(NMHDR* pNMHDR, LRESULT* pResult); private: CXListCtrl m_wndList; // 对话框的最小尺寸 CSize m_szMinDlgSize; // 碰撞实体记录 // 不要操作这个变量,列表的item关联了里面元素的地址 IDPairs m_idPairs; // 标识碰撞点的球体id,数量与 m_idPairs的数量一致 // 与m_idPairs一起初始化,初始化后数量固定 std::vector m_CdFlags; // 排序的列号,由于第一列带checkbox,所以 列号>1 int m_nSortCol; // 升序还是降序 bool m_bAscending; };