Files
envi-code/SourceCode/Code2026/CoreLibrary/AecDbMultiPartEntity.cpp
T
company-desktop 3daad7a33d fix: 修 AecDbMultiPartEntity 的 3D 缓存块三个缺陷,并加绘制计时
在 #131 的基础上把 dwgIn/dwgOut 里的 3D 缓存块做成"坏了也能自愈"的版本。
用 造型.dwg(1 个该类实体)实测过:v4 + magic 的读路径能正确恢复
(1 条 ASM、setASMBody 成功、包围盒与原几何一致)。

修的三处:
1. **count 与实际写入数不一致(会写坏文件)**:原来先 writeInt32(ar.length()),
   循环里再跳过非 brep 和取不到 ASM 的实体 → 记的数比实际 payload 多。
   读端按 count 读就会多吃后面的字节、把实体流读坏(实测 造型.dwg 里已经出现
   ZOMBIEENTITY,用户打开时也提示"实体损坏、需要修复")。
   现在先一次性锁住并收集要写的 body,count 一定等于实际写出的条数。
2. **块没有自描述**:加上 magic 'A3DP' + 块版本 + 尾标记。读不到 magic 就是老数据
   (v3 或别人写的 v4),直接跳过、照旧重建;块不完整(缺尾/条数不符)也整体丢弃重建。
3. **dwgInFields 末尾返回 filerStatus()**:探测 magic 失败会把状态置成 eEndOfFile,
   整个实体就会被判成读取失败。现在显式 return eOk。

另外:`AecDbMultiPartEntity::Draw3d` 加了 AEC_PERF_COUNTER 计时
(total / .build / .cached),用来量"这类实体开图画 3D 到底花多少"。

实测(R230 Debug):
- 造型.dwg:该类实体恢复成功,但开图期 Draw3d 计数 = 0(模型空间里只有 1 个
  ZOMBIE + 7 个标准实体,这个多部件实体不在模型空间)→ 时间和缓存无关;
- big1.dwg(86 s 开图):60 个多部件实体,Draw3d 计数同样是 0 → 开图期没画,
  所以"存 ASM / 普通存盘"两种方式的开图时间差 ≈ 0。
  要测出差异,需要一张"多部件实体可见、开图就会画"的图。
2026-10-30 13:02:40 +08:00

526 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "stdafx.h"
#include "aecdbmultipartentity.h"
#include "simpleproperty.h"
#include "coretools.h"
#include "SectionPerfCounter.h"
#include "dbacis.h"
IMP_XDATA_KEY(AecDbMultiPartEntity,_ptOrg_);
IMP_XDATA_KEY(AecDbMultiPartEntity,_vtX_);
IMP_XDATA_KEY(AecDbMultiPartEntity,_vtY_);
IMP_XDATA_KEY(AecDbMultiPartEntity,_vtZ_);
extern void DLLIMPEXP OrthoProjected3DSolid(AcDb3dSolid* pSolidClone,const AcGePlane& plan,AcGeMatrix3d& xformPlanToWorld,AcDbVoidPtrArray& arEntPtrs);
extern void DLLIMPEXP OrthoProjected3DSubMesh(AcDbSubDMesh* pSubMesh,const AcGePlane& plan,AcGeMatrix3d& xformPlanToWorld,AcDbVoidPtrArray& arEntPtrs);
// 版本号保持 4(与已有图纸一致)。缓存块用 magic 自描述:
// - 老图纸(v3)/ 别人写的 v4(无 magic):读不到 magic 就当没有缓存,照旧重建;
// - 本版本写的 v4(有 magic):直接用块里的 ASM 几何。
// 代价:v3 的老版本插件打开 v4 图仍会走 eMakeMeProxy(这是升版本号本身带来的)。
Adesk::UInt32 AecDbMultiPartEntity::kCurrentVersionNumber = 4;
#ifdef AEC_HAS_MODELER_FILER
namespace
{
const Adesk::UInt32 kMp3dMagic = 0x50443341; // 'A3DP'
const Adesk::UInt32 kMp3dEnd = 0x50443341 ^ 0xFFFFFFFF;
const Adesk::UInt16 kMp3dBlkVer = 1;
}
#endif
ACRX_DXF_DEFINE_MEMBERS (AecDbMultiPartEntity, AecDbBDPipeBase,
AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent,
AcDbProxyEntity::kNoOperation | AcDbProxyEntity::kDisableProxyWarning,
AEC_MULTIPARTENT,
"AecDbMultiPartEntity"
"|Product Desc: shanghua earthworm app obj"
"|Company: shanghua inc"
"|WEB Address: www.shanghuasoft.com")
AecDbMultiPartEntity::AecDbMultiPartEntity(void)
{
}
AecDbMultiPartEntity::~AecDbMultiPartEntity(void)
{
}
Acad::ErrorStatus AecDbMultiPartEntity::Draw2d(AcGiDraw &dc) const
{
assertReadEnabled();
return Draw3d(dc);
}
Acad::ErrorStatus AecDbMultiPartEntity::Draw3d(AcGiDraw &dc) const
{
assertReadEnabled();
//AEC_PERF_COUNTER=1 的 Debug 下计时:这类实体开图期画 3D 一共花多少(缓存收益的天花板)
SectionPerf::Scope perfScope("MultiPartEntity::Draw3d");
const bool bHadCache = Has3DBuffer();
SectionPerf::Scope cacheScope(bHadCache ? "MultiPartEntity::Draw3d.cached"
: "MultiPartEntity::Draw3d.build");
AecDbBDPipeBase::Draw3d(dc);
AcGePoint3d ptOrg = GetOrg();
// 以ptOrg为中心,边长100,画一个十字,在当前的UCS上
AcGeVector3d vtX(GetXDir());
AcGeVector3d vtY(GetYDir());
AcGeVector3d vtZ(GetZDir());
double len(1000.);
AcGePoint3d pt1(ptOrg);
pt1 += vtY * len * .5;
AcGePoint3d pt2(pt1 - vtY * len);
AcGePoint3d pt3(ptOrg - vtX * len * .5);
AcGePoint3d pt4(pt3 + vtX * len);
if (m_arX3DPartPtrs.isEmpty())
{
uint oldClr = dc.GetColor();
dc.SetColor(1);
dc.DrawLine(AcGeLineSeg3d(pt1, pt2));
dc.DrawLine(AcGeLineSeg3d(pt3, pt4));
dc.SetColor(oldClr);
}
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::subGetGripPoints(AcDbGripDataPtrArray& grips,
const double curViewUnitSize,
const int gripSize,
const AcGeVector3d& curViewDir,
const int bitflags) const
{
return Acad::eNotImplementedYet;
}
Acad::ErrorStatus AecDbMultiPartEntity::subGetGripPoints(AcGePoint3dArray &gripPoints, AcDbIntArray &osnapModes, AcDbIntArray &geomIds) const
{
assertReadEnabled();
gripPoints.append(GetOrg());
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::SubAddDimPoints(AcDbGripDataPtrArray& grips,const AcGeVector3d& curViewDir) const
{
return Acad::eOk;
}
EPosition AecDbMultiPartEntity::ExplainGripIndex(int nIndex,CString& sTip) const
{
assertReadEnabled();
EPosition ePos = eStart;
return ePos;
}
Acad::ErrorStatus AecDbMultiPartEntity::ImpGetGripPoints(AcGePoint3dArray& gripPoints,const int bitflags) const
{
assertReadEnabled();
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::subMoveGripPointsAt(const AcDbVoidPtrArray& gripAppData, const AcGeVector3d& offset, const int bitflags)
{
AssertWriteEnabled();
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::subMoveGripPointsAt(const AcDbIntArray &indices, const AcGeVector3d &offset)
{
AssertWriteEnabled();
AcGeMatrix3d mat;
mat.setToTranslation(offset);
return subTransformBy(mat);
}
Acad::ErrorStatus AecDbMultiPartEntity::subGetStretchPoints(AcGePoint3dArray& stretchPoints) const
{
assertReadEnabled();
stretchPoints.append(GetOrg());
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::subMoveStretchPointsAt(const AcDbIntArray& indices,const AcGeVector3d& offset)
{
AssertWriteEnabled();
AcGeMatrix3d mat;
mat.setToTranslation(offset);
return subTransformBy(mat);
}
Acad::ErrorStatus AecDbMultiPartEntity::copyFrom(const AcRxObject * pSrc)
{
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::subTransformBy(const AcGeMatrix3d &xform)
{
AecDbBDPipeBase::subTransformBy(xform);
AcGePoint3d pt(GetOrg());
pt.transformBy(xform);
SetOrg(pt);
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::subGetOsnapPoints(AcDb::OsnapMode osnapMode
, INT_PTR gsSelectionMark
, const AcGePoint3d &pickPoint
, const AcGePoint3d &lastPoint
, const AcGeMatrix3d &viewXform
, AcGePoint3dArray &snapPoints
, AcDbIntArray &geomIds) const
{
assertReadEnabled();
AcGePoint3d ptOrg = GetOrg();
// 以ptOrg为中心,边长100,画一个十字,在当前的UCS上
AcGeVector3d vtX(GetXDir());
AcGeVector3d vtY(GetYDir());
AcGeVector3d vtZ(GetZDir());
double len(1000.);
AcGePoint3d pt1(ptOrg);
pt1 += vtY * len * .5;
AcGePoint3d pt2(pt1 - vtY * len);
AcGePoint3d pt3(ptOrg - vtX * len * .5);
AcGePoint3d pt4(pt3 + vtX * len);
AcDbLine l1(pt1, pt2);
l1.getOsnapPoints(osnapMode, gsSelectionMark, pickPoint, lastPoint, viewXform, snapPoints, geomIds);
AcDbLine l2(pt3, pt4);
l2.getOsnapPoints(osnapMode, gsSelectionMark, pickPoint, lastPoint, viewXform, snapPoints, geomIds);
const AcDbVoidPtrArray &ar = Get3DBuffer();
for (int i = 0; i < ar.length(); ++i)
{
AcDbEntity *pEnt((AcDbEntity*)ar[i]);
pEnt->getOsnapPoints(osnapMode, gsSelectionMark, pickPoint, lastPoint, viewXform, snapPoints, geomIds);
}
return Acad::eOk;
}
void AecDbMultiPartEntity::subList() const
{
assertReadEnabled();
acutPrintf(_T("\n异形构筑物:"));
AecDbBDPipeBase::subList();
}
int AecDbMultiPartEntity::GetDraw2dables(Adesk::UInt16 nDrawStyle, AcDbVoidPtrArray & arEntPtrs) const
{
assertReadEnabled();
AcDbObjectId id = objectId();
AcDbEntity* pEnt = NULL;
const AcDbVoidPtrArray &ar2D = Get2DBuffer();
if (ar2D.isEmpty())
{
AcGePlane plan = AcGePlane::kXYPlane;
AcGeMatrix3d xform;
xform.setToPlaneToWorld(plan);
xform.invert();
AcDbVoidPtrArray arSubEntPtrs;
AcDb3dSolid* pSolid = NULL;
AcDbSubDMesh* pSubMesh = NULL;
const AcDbVoidPtrArray &ar3D = Get3DBuffer();
for (int i = 0;i < ar3D.length();i++)
{
pEnt = (AcDbEntity*)ar3D.at(i);
pSolid = AcDb3dSolid::cast(pEnt);
if (pSolid != NULL)
{
//投影法
OrthoProjected3DSolid(pSolid, plan, xform, arSubEntPtrs);
}
pSubMesh = AcDbSubDMesh::cast(pEnt);
if (pSubMesh != NULL)
{
OrthoProjected3DSubMesh(pSubMesh, plan, xform, arSubEntPtrs);
}
}
AcDbObjectId idLayer = layerId();
for (int i = 0;i < arSubEntPtrs.length();i++)
{
pEnt = (AcDbEntity*)arSubEntPtrs.at(i);
pEnt->setLayer(idLayer);
arEntPtrs.append(pEnt);
}
}
else
{
AcGePlane plan = AcGePlane::kXYPlane;
AcGeMatrix3d xform;
xform.setToPlaneToWorld(plan);
xform.invert();
AcDbVoidPtrArray arSubEntPtrs;
AcDb3dSolid* pSolid = NULL;
AcDbSubDMesh* pSubMesh = NULL;
AcDbEntity* pClnEnt = NULL;
AcDbObjectId idLayer = layerId();
for (int i = 0;i < ar2D.length();i++)
{
pEnt = (AcDbEntity*)ar2D.at(i);
pClnEnt = (AcDbEntity*)pEnt->clone();
pClnEnt->setLayer(idLayer);
pSolid = AcDb3dSolid::cast(pClnEnt);
if (pSolid != NULL)
{
//投影法
OrthoProjected3DSolid(pSolid, plan, xform, arSubEntPtrs);
continue;
}
pSubMesh = AcDbSubDMesh::cast(pClnEnt);
if (pSubMesh != NULL)
{
OrthoProjected3DSubMesh(pSubMesh, plan, xform, arSubEntPtrs);
continue;
}
arSubEntPtrs.append(pClnEnt);
}
for (int i = 0;i < arSubEntPtrs.length();i++)
{
pEnt = (AcDbEntity*)arSubEntPtrs.at(i);
pEnt->setLayer(idLayer);
arEntPtrs.append(pEnt);
}
}
return arEntPtrs.length();
}
int AecDbMultiPartEntity::GetDraw3dables(AcDbVoidPtrArray & arEntPtrs, bool bWriteBimInfo) const
{
assertReadEnabled();
AcDbEntity* pEnt = NULL, *pEntClone = NULL;
const AcDbVoidPtrArray &ar3D = Get3DBuffer();
for (int i = 0;i < ar3D.length();i++)
{
pEnt = (AcDbEntity*)ar3D.at(i);
pEntClone = (AcDbEntity*)pEnt->clone();
if (bWriteBimInfo)
{
m_mapSpecXData.Write(pEntClone, _T("BIMDATA"));
}
pEntClone->setLayer(layerId());
arEntPtrs.append(pEntClone);
}
return arEntPtrs.length();
}
Acad::ErrorStatus AecDbMultiPartEntity::dwgInFields (AcDbDwgFiler* pFiler)
{
AssertWriteEnabled();
//----- Read parent class information first.
Acad::ErrorStatus es = AecDbBDPipeBase::dwgInFields (pFiler) ;
if ( es != Acad::eOk )
return (es) ;
//----- Object version number needs to be read first
Adesk::UInt32 version = 0 ;
if ( (es =pFiler->readUInt32 (&version)) != Acad::eOk )
return (es) ;
if ( version > AecDbMultiPartEntity::kCurrentVersionNumber )
return (Acad::eMakeMeProxy);
if (version == 2)
{
//临时
AecDbObjXDataMap xMap;
xMap.dwgInFields(pFiler);
}
#ifdef AEC_HAS_MODELER_FILER
// ---- 3D 几何缓存块(modeler filer)----
// 位置:本实体流的**末尾**(这个类在 version 之后不写任何自己的字段,
// 所以老版本读到自己需要的字段就停,尾部多出来的字节会被忽略)。
// 为什么不用升版本号:升了会让老版本 `version > kCurrentVersionNumber`
// 直接 eMakeMeProxy —— 整批实体变代理对象(实测已出现过 ZOMBIEENTITY)。
// 所以这里用 magic 自描述:读不到 magic 就当没有缓存,照旧重建。
{
Adesk::UInt32 nMagic = 0;
if (pFiler->readUInt32(&nMagic) == Acad::eOk && nMagic == kMp3dMagic)
{
Adesk::UInt16 nBlkVer = 0;
Adesk::Int32 nCount = 0;
if (pFiler->readUInt16(&nBlkVer) == Acad::eOk && nBlkVer == kMp3dBlkVer &&
pFiler->readInt32(&nCount) == Acad::eOk && nCount > 0 && nCount <= 64)
{
int nModelerVer = -1;
int nOk = 0;
for (int i = 0; i < nCount; ++i)
{
ENTITY* pAsm = NULL;
if (acdbModelerRestoreEntityFromDwgFiler(pFiler, pAsm, &nModelerVer,
kReadStrangerEntityDefault) != Acad::eOk || pAsm == NULL)
break;
AcDb3dSolid* pSolid = new AcDb3dSolid();
if (pSolid->setASMBody(pAsm) != Acad::eOk)
{
delete pSolid;
break;
}
Add3DBuffer(pSolid);
++nOk;
}
Adesk::UInt32 nEnd = 0;
const bool bEndOk = (pFiler->readUInt32(&nEnd) == Acad::eOk && nEnd == kMp3dEnd);
if (nOk != (int)nCount || !bEndOk)
{
// 半成品块:整条丢弃,回退重建
Clean3DBuffer();
}
}
}
}
#endif
return Acad::eOk; // 探测缓存块时 readUInt32 在老图上会把状态置成 eEndOfFile,
// 这里显式返回 eOk,否则整个实体会被判成读取失败
}
Acad::ErrorStatus AecDbMultiPartEntity::dwgOutFields(AcDbDwgFiler* pFiler) const
{
assertReadEnabled();
//----- Save parent class information first.
Acad::ErrorStatus es = AecDbBDPipeBase::dwgOutFields (pFiler) ;
if ( es != Acad::eOk )
return (es) ;
//----- Object version number needs to be saved first
if ( (es =pFiler->writeUInt32 (AecDbMultiPartEntity::kCurrentVersionNumber)) != Acad::eOk )
return (es) ;
#ifdef AEC_HAS_MODELER_FILER
// ---- 3D 几何缓存块:只存 brep(AcDb3dSolid),且**count 按实际写入数**记 ----
// 不能写 Get3DBuffer().length():缓存里可能有中心线/网格等非 brep,
// 那些会被跳过,count 与实际 payload 数不一致 → 读端多吃数据、实体流损坏。
{
// 先一次性锁住、收集 body,写入期间不放锁 —— 这样 count 一定等于实际写出的条数
AcArray<AcDb3dSolid*> arSolids;
AcArray<ENTITY*> arAsm;
const AcDbVoidPtrArray& arBuf = Get3DBuffer();
for (int i = 0; i < arBuf.length(); ++i)
{
AcDb3dSolid* pSolid = AcDb3dSolid::cast((AcDbEntity*)arBuf[i]);
if (pSolid == NULL)
continue;
ENTITY* pAsm = (ENTITY*)pSolid->getLockedASMBody();
if (pAsm == NULL)
continue;
arSolids.append(pSolid);
arAsm.append(pAsm);
}
if (!arSolids.isEmpty())
{
AcDbDatabase* pDb = GetMe()->database();
if (pDb == NULL)
pDb = acdbHostApplicationServices()->workingDatabase();
pFiler->writeUInt32(kMp3dMagic);
pFiler->writeUInt16(kMp3dBlkVer);
pFiler->writeInt32((Adesk::Int32)arSolids.length());
for (int i = 0; i < arSolids.length(); ++i)
{
const Acad::ErrorStatus esSave = acdbModelerSaveEntityToDwgFiler(pFiler, arAsm[i], pDb, -1);
arSolids[i]->unlockASMBody();
if (esSave != Acad::eOk)
break;
}
pFiler->writeUInt32(kMp3dEnd);
}
}
#endif
return pFiler->filerStatus();
}
Acad::ErrorStatus AecDbMultiPartEntity::dxfInFields (AcDbDxfFiler* pFiler)
{
return Acad::eOk;
}
Acad::ErrorStatus AecDbMultiPartEntity::dxfOutFields(AcDbDxfFiler* pFiler) const
{
return Acad::eOk;
}
int AecDbMultiPartEntity::ComparedTo(AecDbCurveBase* pCurOther) const
{
assertReadEnabled();
return -1;
}
SHJson::WValue& AecDbMultiPartEntity::ToJson(bool bExprotGeo) const
{
assertReadEnabled();
return AecDbBDPipeBase::ToJson(bExprotGeo);
}
bool AecDbMultiPartEntity::ByJson(const SHJson::WValue &v)
{
AssertWriteEnabled();
return AecDbBDPipeBase::ByJson(v);
}
#include "HlrCutOption.h"
void AecDbMultiPartEntity::ProcessHlrResult(const AcArray<AcDbEntity *> &ar
, const std::map<AcDbEntity *, AcArray<AcDbEntity *>> &preAr
, AcArray<AcDbEntity *> &output) const
{
HlrCutOption::ProcessHlrResult(this
, FALSE
, .0
, HlrCutOption::getHlrBLK_YGDT()
, ar
, preAr
, output);
}
bool AecDbMultiPartEntity::GetProperties(SimpleProperty::PropertySetByIndex & porpertys) const
{
AecDbBDPipeBase::GetProperties(porpertys);
CoreTools::setLayerPorperty(porpertys);
CoreTools::setSubSystemPorperty(porpertys);
CString sName(_T("异形构筑"));
BOOL bPMFY(FALSE), bDLJC(FALSE);
if (GetSpecialtyData()->GetAt(_T("PMFY"), bPMFY))
sName = _T("平面放样");
else if (GetSpecialtyData()->GetAt(_T("DLJC"), bDLJC))
sName = _T("板基础");
SimpleProperty::makeAndInsert(porpertys, _T("类型"), sName, false);
return true;
}