问题描述: 

在中望 CAD 二次开发过程中,三维实体的包络框经常被用于各种几何分析和逻辑判断,例如:

  • 实体之间的空间碰撞检测

  • 模型范围裁剪

  • 可视区域过滤

  • 三维构件位置自动排布等

一般情况下,我们可能会直接调用 ZcDbEntity::getGeomExtents() 接口来获取实体的包络框信息,但在实际项目中会发现:
该接口返回的包络框往往是基于实体几何外接范围的近似结果,对于圆角、曲面或复杂三维实体来说,其精度并不能完全满足高精度计算或精细化判断的需求。

解决方案:

针对上述问题,中望 CAD 在三维运算模块中,内部提供了一套更高精度的包络框计算接口,可以用于获取更接近真实几何形状的三维包络框,使用内部接口zcdbGetModelerEntityExactExtents。

#include "stdafx.h"
#include "tchar.h"
#include <aced.h>
#include <rxregsvc.h>
#include "dbapserv.h"
#include "dbsymtb.h"
#include "geassign.h"
#include "adscodes.h"

#include "dbregion.h"
#include "dbmain.h"
#include "dbobjptr.h"
#include "dbsol3d.h"

#include <chrono>
#define TIME_REOCRD(...)																							\
do																													\
{																													\
	auto start = std::chrono::steady_clock::now();																	\
	__VA_ARGS__																										\
	auto end = std::chrono::steady_clock::now();																	\
	acutPrintf(ACRX_T("spend time : %.3lf ms."), std::chrono::duration<double>(end - start).count() * 1000.0);		\
} while (false)


void initApp();
void unloadApp();

/*
	函数原型: Zcad::ErrorStatus zcdbGetModelerEntityExactExtents(ZcDbEntity*, AcDbExtents&);
	模块:该函数由ZwDataBase.dll模块导出,函数名zcdbGetModelerEntityExactExtents
	输入:ZcDbEntity目前支持以Modeler建模的实体ZcDbRegion/AcDb3dSolid/ZcDbBody/ZcDbSurface
	输出: AcDbExtents输出该三维实体的精确包围盒
	返回值: Zcad::ErrorStatus错误码,仅在返回Zcad::eOk时包围盒有效
*/
struct GetModelerExactExtentFunc
{
	using PROC = Zcad::ErrorStatus(*)(ZcDbEntity*, AcDbExtents&);

	GetModelerExactExtentFunc()
	{
		init();
	}

	void init()
	{
		if (m_proc)
		{
			return;
		}

		HMODULE hMoudle = ::GetModuleHandle(TEXT("ZwDataBase.dll"));
		if (NULL == hMoudle)
		{
			return;
		}

		m_proc = (PROC)::GetProcAddress(hMoudle, "zcdbGetModelerEntityExactExtents");
	}

	Zcad::ErrorStatus operator()(ZcDbEntity* pEntity, AcDbExtents& extent)
	{
		return m_proc(pEntity, extent);
	}

	operator bool() const { return nullptr != m_proc; }

	PROC m_proc = nullptr;
};

/*
	用于精确计算region/3dsolid/surface/body的仿函数
*/
GetModelerExactExtentFunc gsGetModelerEntExactExtent;

/*
	将获取的实体加入数据库
*/
void appendToDb(AcDbEntity* pEntity)
{
	if (!pEntity)
	{
		return;
	}

	ZcDbDatabase* db = acdbHostApplicationServices()->workingDatabase();
	if (!db)
	{
		return;
	}

	AcDbBlockTable* pBT = nullptr;
	db->getBlockTable(pBT, AcDb::kForWrite);

	AcDbObjectId objectId;
	pBT->getAt(ACDB_MODEL_SPACE, objectId);
	pBT->close();

	if (objectId.isNull())
	{
		return;
	}

	AcDbBlockTableRecord* pBTR = nullptr;
	acdbOpenObject(pBTR, objectId, AcDb::kForWrite);
	pBTR->appendZcDbEntity(pEntity);

	pBTR->close();
}

/*
	交互获取一个实体
*/
template<class T>
ZcDbObjectPointer<T> acuqireEnt(ZcDb::OpenMode mode = ZcDb::kForRead)
{
	ads_name name;
	acedSSGet(NULL, NULL, NULL, NULL, name);

	int len = 0;
	acedSSLength(name, &len);
	if (len != 1)
	{
		return nullptr;
	}

	ads_name ent;
	acedSSName(name, 0, ent);

	AcDbObjectId objectId;
	acdbGetObjectId(objectId, ent);


	return ZcDbObjectPointer<T>(objectId, mode);
}

/*
	绘制包围盒
*/
void drawExtentBox(const AcDbExtents& extent)
{
	const auto& minPoint = extent.minPoint();
	const auto& maxPoint = extent.maxPoint();

	const auto offVec = maxPoint - minPoint;
	const auto midOff = minPoint.asVector() + offVec / 2.0;

	AcDb3dSolid* box = new AcDb3dSolid;
	box->createBox(offVec.x, offVec.y, offVec.z);
	box->transformBy(ZcGeMatrix3d::translation(midOff));
	appendToDb(box);
	box->close();
}

/*
	给交互选择的实体进行一个旋转变换
*/
void testTransformEnt()
{
	auto ent = acuqireEnt<ZcDbEntity>(AcDb::kForWrite);
	if (!ent)
	{
		acutPrintf(ZCRX_T("null ent\n"));
		return;
	}

	ZcGeMatrix3d mat;
	mat.setToRotation(1.0, ZcGeVector3d(1.0, 1.0, 1.0));
	ent->transformBy(mat);
}

/*
	获取交互选择的region的粗糙包围盒并绘制
*/
void testGetRoughExtent()
{
	auto pRegion = acuqireEnt<ZcDbRegion>();
	if (!pRegion)
	{
		acutPrintf(ACRX_T("invalid input, please put a region."));
		return;
	}
	
	AcDbExtents extent;
	TIME_REOCRD(pRegion->getGeomExtents(extent););
	drawExtentBox(extent);
}

/*
	获取交互选择的region的精确包围盒并绘制
*/
void testGetExactExtent()
{
	auto pRegion = acuqireEnt<ZcDbRegion>();
	if (!pRegion)
	{
		acutPrintf(ACRX_T("invalid input, please put a region."));
		return;
	}

	if (!gsGetModelerEntExactExtent)
	{
		acutPrintf(ACRX_T("load zcdbGetModelerEntityExactExtents failed.\n"));
		return;
	}

	AcDbExtents extent;
	TIME_REOCRD(gsGetModelerEntExactExtent(pRegion, extent););
	
	drawExtentBox(extent);
}

void initApp()
{ 
	acrxBuildClassHierarchy();
	acedRegCmds->addCommand(_T("CUSCURVE_COMMANDS"), _T("testGetRoughExtent"), _T("testGetRoughExtent"), ACRX_CMD_MODAL, testGetRoughExtent);
	acedRegCmds->addCommand(_T("CUSCURVE_COMMANDS"), _T("testGetExactExtent"), _T("testGetExactExtent"), ACRX_CMD_MODAL, testGetExactExtent);
	acedRegCmds->addCommand(_T("CUSCURVE_COMMANDS"), _T("testTransformEnt"), _T("testTransformEnt"), ACRX_CMD_MODAL, testTransformEnt);
}


void unloadApp()
{ 
	acedRegCmds->removeGroup(_T("CUSCURVE_COMMANDS"));
}

extern "C" AcRx::AppRetCode
zcrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
{
	switch (msg)
	{
	case AcRx::kInitAppMsg:
		acrxDynamicLinker->unlockApplication(pkt);
		acrxRegisterAppMDIAware(pkt);
		initApp();
		break;
	case AcRx::kUnloadAppMsg:
		unloadApp();
		break;
	default:
		break;
	}

	return AcRx::kRetOK;
}




image-2025-08-29-11-07-06-351.png

zcdbGetModelerEntityExactExtents效果:

image-2025-08-29-11-06-16-295.png


英文空间链接


★★中望CAD中如何获取三维实体精确包络框?https://confluence.zwcad.com/pages/viewpage.action?pageId=302515974

  • No labels