208 lines
4.9 KiB
C++
208 lines
4.9 KiB
C++
#pragma once
|
|
#include <vector>
|
|
#include "resource.h"
|
|
#include "xlistctrl.h"
|
|
#include <algorithm> // 包含 std::min, std::max
|
|
|
|
namespace CDA
|
|
{
|
|
template <typename T>
|
|
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 <typename T>
|
|
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<AcDbObjectId> &hitIds) const;
|
|
|
|
void GetIntersections(AcDbObjectId &id, std::vector<AcDbObjectId> &hitIds) const;
|
|
|
|
// 两两自检接口
|
|
void GetAllIntersections(std::vector< std::pair<AcDbObjectId, AcDbObjectId> > &hitPairs) const;
|
|
|
|
// 供后续碰撞查询使用的只读接口
|
|
const std::vector<BVHNode> &GetNodes() const { return m_bvhNodes; }
|
|
|
|
const std::vector<EntityProxy> &GetEntities() const { return m_allEntities; }
|
|
|
|
private:
|
|
|
|
std::vector<EntityProxy> m_allEntities;
|
|
std::vector<BVHNode> 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<AcDbObjectId, AcDbObjectId> > &hitPairs) const;
|
|
};
|
|
}
|
|
|
|
class CCollisionDetectionDlg: public CDialog
|
|
{
|
|
public:
|
|
typedef std::pair<AcDbObjectId, AcDbObjectId> IDPair;
|
|
typedef std::vector<IDPair> 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<AcDbObjectId> m_CdFlags;
|
|
|
|
// 排序的列号,由于第一列带checkbox,所以 列号>1
|
|
int m_nSortCol;
|
|
// 升序还是降序
|
|
bool m_bAscending;
|
|
};
|
|
|