ObjectARX自定义实体

作者: admin 分类: C++,CAD,ObjectARX 发布时间: 2020-02-20 22:13

说明: 此次绘制的CAD自定义实体是一个矩形,具有拉伸功能。因为初次接触自定义实体,在一次次制作的过程中遇到了很多困难,幸好有老大和同事的帮助,当然还有广大网友们的文章协助,才完成了这个自定义实体的绘制。我不敢说到目前还会有什么问题,但希望能帮助到别人。

一、需要重载的函数

//绘制夹点
    virtual Acad::ErrorStatus getGripPoints(AcDbGripDataPtrArray& grips, const double curViewUnitSize, const int gripSize, const AcGeVector3d& curViewDir, const int bitflags) const;
//移动夹点
virtual Acad::ErrorStatus moveGripPointsAt(const AcDbVoidPtrArray& gripAppData,const AcGeVector3d& offset,const int bitflags);
//加载数据
virtual Acad::ErrorStatus dwgInFields(AcDbDwgFiler* pFiler);
//保存数据
virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* pFiler) const;
//矩阵转换
virtual Acad::ErrorStatus transformBy(const AcGeMatrix3d& xform);
//分解(炸开)
virtual Acad::ErrorStatus explode(ZcDbVoidPtrArray& entitySet) const;
//夹点状态
virtual void gripStatus(const AcDb::GripStat status);
//实体绘制
virtual Adesk::Boolean worldDraw(AcGiWorldDraw* pWd);
//捕捉点
virtual Acad::ErrorStatus getOsnapPoints(AcDb::OsnapMode osnapMode, Adesk::GsMarker gsSelectionMark, const AcGePoint3d& pickPoint, const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform, AcGePoint3dArray& snapPoints, AcDbIntArray & geomIds) const;
//外包矩形
virtual Acad::ErrorStatus getGeomExtents(AcDbExtents& extents) const;
//删除
virtualAcad::ErrorStatus erase(Adesk::Boolean erasing = true);

二、成员变量

因为这个自定义实体是矩形,所以是由四个直线组成,且由三个夹点。

AcDbLine m_lineHori1;
AcDbLine m_lineHori2;
AcDbLine m_lineVert1;
AcDbLine m_lineVert2;
AcGePoint3d m_ptBase;
AcGePoint3d m_ptRight;
AcGePoint3d m_ptTop;

所以在写重载函数时,每个直线实体都要写一下。例如在worldDraw函数:

Adesk::Boolean CDiBanCsm::worldDraw(AcGiWorldDraw* pWd)
{
assertReadEnabled();
m_lineHori1.worldDraw(pWd);
m_lineHori2.worldDraw(pWd);
m_lineVert1.worldDraw(pWd);
m_lineVert2.worldDraw(pWd);
return Adesk::kTrue;
}

因为有夹点这个成员变量,所以,在dwgInFields函数、dwgOutFields函数、transformBy函数中也要将夹点添加进去:

Acad::ErrorStatus CDiBanCsm::dwgInFields(AcDbDwgFiler* pFiler)
{
assertWriteEnabled();
AcDbEntity::dwgInFields(pFiler);
m_lineHori1.dwgInFields(pFiler);
m_lineHori2.dwgInFields(pFiler);
m_lineVert1.dwgInFields(pFiler);
m_lineVert2.dwgInFields(pFiler);
pFiler->readPoint3d(&m_ptBase);
pFiler->readPoint3d(&m_ptRight);
pFiler->readPoint3d(&m_ptTop);
return pFiler->filerStatus();
}


三、夹点绘制

绘制夹点比较麻烦,如果你不是要绘制夹点的形状,可以重载

Acad::ErrorStatus getGripPoints(
    AcGePoint3dArray& gripPoints, 
    AcDbIntArray & osnapModes, 
    AcDbIntArray & geomIds
) const;

只需要在gripPoints参数中添加夹点坐标就可以,夹点为默认夹点。

这里,我们需要改变夹点的形状,所以这里我们需要绘制夹点。夹点用AcDbGripData类绘制,它是new出来的,所以我们需要将它delete。

我们需要重载gripStatus函数,在AcDb::kGripsDone状态下,将new的AcDbGripData指针delte。

我们要声明一个全局的map变量来存放AcDbGripData指针:

//键:自定义实体指针值:AcDbGripData指针数组
static std::map<const AcDbEntity*, AcDbGripDataPtrArray> s_mapGripPtr;

直接上代码了:

Acad::ErrorStatus CDiBanCsm::getGripPoints(AcDbGripDataPtrArray& grips, const double curViewUnitSize, 
const int gripSize, const AcGeVector3d& curViewDir, const int bitflags) const
{
assertReadEnabled();
std::vector<CString>::const_iterator appIter = GetGripName();//这里appIter是一个字符串容器,存放的是夹点名称
AcDbGripDataPtrArray tempGripArr;
//BasePt
AcDbGripData* pGripBase = new AcDbGripData();
pGripBase->setAppData((void*)&(*appIter));//将夹点名称设置到数据中
pGripBase->setGripPoint(m_ptBase);//设置夹点坐标
grips.append(pGripBase);
tempGripArr.append(pGripBase);
appIter++;
//RightPt
AcDbGripData* pGripRight = new AcDbGripData();
pGripRight->setAppData((void*)&(*appIter));
pGripRight->setGripPoint(m_ptRight);
pGripRight->setWorldDraw(DrawStretchGrip);
grips.append(pGripRight);
tempGripArr.append(pGripRight);
appIter++;
//TopPt
AcDbGripData* pGripTop = new AcDbGripData();
pGripTop->setAppData((void*)&(*appIter));
pGripTop->setGripPoint(m_ptTop);
pGripTop->setWorldDraw(DrawStretchGrip);
grips.append(pGripTop);
tempGripArr.append(pGripTop);
appIter++;
auto a = this;
CCustomBase::s_mapGripPtr[this] = tempGripArr;//将new出来的AcDbGripData指针添加到s_mapGripPtr变量中
return Acad::eOk;
}
bool CDiBanCsm::DrawStretchGrip(AcDbGripData *pThis, AcGiWorldDraw *pWd, const AcDbObjectId& entId, AcDbGripOperations::DrawType type, AcGePoint3d *cursor, double dGripSize)
{
dGripSize *= CCustomBase::s_gripSize / 2;//夹点的大小 CCustomBase::s_gripSize == 2.8
AcGePoint3dpt = pThis->gripPoint();
CString* pStr = (CString*)(pThis->appData());
AcGeVector3d vec = AcGeVector3d::kXAxis;
AcDbObjectPointer<CDiBanCsm> m_pDiBan(entId, AcDb::kForRead);
if (m_pDiBan.openStatus() != Acad::eOk)
return false;
if (*pStr == _T("RightPt"))
vec = m_pDiBan->GetHoriDirection().normal();//得到实体的水平向量,这个接口自己写,通过直线的两点坐标
else if (*pStr == _T("TopPt"))
vec = m_pDiBan->GetVertDirection().normal();//得到实体的垂直向量
auto vecUp = AcGeVector3d::kZAxis.crossProduct(vec).normal();
AcGePoint3d pts[3];
pts[0] = pt + vec * dGripSize;
pts[1] = pt - vec * dGripSize / 2 + vecUp * dGripSize * .7;
pts[2] = pt - vec * dGripSize / 2 - vecUp * dGripSize * .7;
pWd->subEntityTraits().setFillType(kAcGiFillAlways);
pWd->geometry().polygon(3, pts);
return true;
}

在重载的gripStatus函数中delete夹点指针

void CDiBanCsm::gripStatus(const AcDb::GripStat status)
{
AcDbEntity::gripStatus(status);
switch (status)
{
case AcDb::kGripsDone:
CCustomBase::DeleteGrip(this);
break;
case AcDb::kGripsToBeDeleted:
break;
case AcDb::kDimDataToBeDeleted:
break;
}
}
bool CCustomBase::DeleteGrip(const AcDbEntity* pEnt)
{
std::map<const AcDbEntity*, AcDbGripDataPtrArray>::iterator iter;
for (iter = s_mapGripPtr.begin(); iter != s_mapGripPtr.end(); iter++)
{
if (pEnt == iter->first)
{
AcDbGripDataPtrArray tempGripArr = iter->second;
for (int i = 0; i < tempGripArr.length(); ++i)
{
DEL(tempGripArr[i]);//这里delete 指针
tempGripArr[i] = NULL;
}
}
}
int n = CCustomBase::s_mapGripPtr.erase(pEnt);//如果删除了会返回1,否则返回0  
return n;
}

移动夹点就比较简单了,重写moveGripPointsAt函数就可以了

Acad::ErrorStatus CDiBanCsm::moveGripPointsAt(const AcDbVoidPtrArray& gripAppData,const AcGeVector3d& offset,
const int bitflags)
{
assertWriteEnabled();
CString* pStrTemp = NULL;
for (int i = 0; i < gripAppData.length(); i++)
{
pStrTemp = static_cast<CString*>(gripAppData[i]);
if (*pStrTemp == _T("BasePt"))//通过点的名称,判断移动的是哪个夹点
{
//这里我是通过偏移向量,来移动直线的两点位置
SetLinePoint(m_lineHori1, 3, offset);
SetLinePoint(m_lineHori2, 3, offset);
SetLinePoint(m_lineVert1, 3, offset);
SetLinePoint(m_lineVert2, 3, offset);
m_ptBase = GetBaseGripPt();//重新定义夹点的位置
m_ptRight = GetRightGripPt();
m_ptTop = GetTopGripPt();
}
else if (*pStrTemp == _T("RightPt"))
{
AcGePoint3d ptStart = m_lineHori2.startPoint();
AcGePoint3d ptEnd = m_lineHori2.endPoint();
AcGeVector3d vctHori = ptEnd - ptStart;
AcGeVector3d vctOffset = GetProjectVector(m_ptBase, offset, vctHori);
SetLinePoint(m_lineHori1,2,vctOffset);
SetLinePoint(m_lineHori2, 2, vctOffset);
SetLinePoint(m_lineVert2, 3, vctOffset);
m_ptBase = GetBaseGripPt();
m_ptRight = GetRightGripPt();
m_ptTop = GetTopGripPt();
}
else if (*pStrTemp == _T("TopPt"))
{
AcGePoint3d ptStart = m_lineVert1.startPoint();
AcGePoint3d ptEnd = m_lineVert1.endPoint();
AcGeVector3d vctHori = ptEnd - ptStart;
AcGeVector3d vctOffset = GetProjectVector(m_ptBase, offset, vctHori);
SetLinePoint(m_lineVert1, 2, vctOffset);
SetLinePoint(m_lineVert2, 2, vctOffset);
SetLinePoint(m_lineHori1, 3, vctOffset);
m_ptBase = GetBaseGripPt();
m_ptRight = GetRightGripPt();
m_ptTop = GetTopGripPt();
}
}
return Acad::eOk;
}
AcGeVector3d CCustomBase::GetProjectVector(const AcGePoint3d& ptBase, const AcGeVector3d& vctOffset, const AcGeVector3d& vctDirection)
{//得到某一向量在指定向量上的投影向量
AcGePoint3d ptOffset = ptBase + vctOffset;
AcGeLine3d geLine(ptBase, vctDirection);
AcGePoint3d ptTarget = geLine.closestPointTo(ptOffset);
return ptTarget - ptBase;
}

四、实体分解

需要重写explode函数,这里有两个知识要说,一个是如果自定义实体可以分解,需要添加克隆的直线指针

Acad::ErrorStatus CDiBanCsm::explode(ZcDbVoidPtrArray& entitySet) const
{
assertReadEnabled();
 entitySet.append(m_lineHori1.clone());
 entitySet.append(m_lineHori2.clone());
 entitySet.append(m_lineVert1.clone());
 entitySet.append(m_lineVert2.clone());
return Acad::eOk;
}

如果你想让自定义实体不能分解,返回值返回:Acad::eCannotExplodeEntity即可。

五、总结

其实原本的代码比这个多很多,这里我只是截取了重要部分展示。这是目前绘制的自定义实体,某些地方还需要改进。

————————————————

版权声明:本文为CSDN博主「A彡安静氵」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/phd17621680432/article/details/89181257

本文中所述附件下载:

Arx自定义实体代码.rar

ARX自定义实体绘制夹点

使用AcDbGripData类绘制,需要new AcDbGripData指针添加到grips参数中。因为AcDbGripData指针是new出来的,当然需要delete,需要在gripStatus函数中释放new的指针。

因为getGripPoints是const成员函数,不能存放new的AcDbGripData指针,所有这里用一个全局的map变量来存放AcDbGripData指针。

重载getGripPoints函数

————————————————

版权声明:本文为CSDN博主「A彡安静氵」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/phd17621680432/article/details/89497016

Acad::ErrorStatus CDiBanCsm::getGripPoints(AcDbGripDataPtrArray& grips, const double curViewUnitSize, 
const int gripSize, const AcGeVector3d& curViewDir, const int bitflags) const
{
assertReadEnabled();
std::vector<CString>::const_iterator appIter = GetGripName();//appIter是一个CString字符串容器,存放的是夹点名字
AcDbGripDataPtrArray tempGripArr;
//BasePt
AcDbGripData* pGripBase = new AcDbGripData();
pGripBase->setAppData((void*)&(*appIter));//设置夹点名字
pGripBase->setGripPoint(m_ptBase);//设置夹点坐标,如果不用setWorldDraw绘制夹点,则夹点为默认夹点
grips.append(pGripBase);
tempGripArr.append(pGripBase);
appIter++;
//RightPt
AcDbGripData* pGripRight = new AcDbGripData();
pGripRight->setAppData((void*)&(*appIter));
pGripRight->setGripPoint(m_ptRight);
pGripRight->setWorldDraw(DrawStretchGrip);
grips.append(pGripRight);
tempGripArr.append(pGripRight);
appIter++;
//TopPt
AcDbGripData* pGripTop = new AcDbGripData();
pGripTop->setAppData((void*)&(*appIter));
pGripTop->setGripPoint(m_ptTop);
pGripTop->setWorldDraw(DrawStretchGrip);
grips.append(pGripTop);
tempGripArr.append(pGripTop);
appIter++;
auto a = this;
CCustomBase::s_mapGripPtr[this] = tempGripArr;//将此自定义实体new的指针添加到全局变量中
return Acad::eOk;
}

全局map变量

//键:自定义实体指针值:AcDbGripData数组
static std::map<const AcDbEntity*, AcDbGripDataPtrArray> s_mapGripPtr;

夹点名字

std::vector<CString> m_vecGripName;//夹点名称
//在初始化函数中添加夹点名称
m_vecGripName.push_back(_T("BasePt"));
m_vecGripName.push_back(_T("RightPt"));
m_vecGripName.push_back(_T("TopPt"));
std::vector<CString>::const_iterator CCustomBase::GetGripName() const
{
return m_vecGripName.begin();
}

绘制拉伸夹点

bool CDiBanCsm::DrawStretchGrip(AcDbGripData *pThis, AcGiWorldDraw *pWd, const AcDbObjectId& entId, AcDbGripOperations::DrawType type, AcGePoint3d *cursor, double dGripSize)
{
dGripSize *= CCustomBase::s_gripSize / 2;
AcGePoint3dpt = pThis->gripPoint();
CString* pStr = (CString*)(pThis->appData());
AcGeVector3d vec = AcGeVector3d::kXAxis;
AcDbObjectPointer<CDiBanCsm> m_pDiBan(entId, AcDb::kForRead);
if (m_pDiBan.openStatus() != Acad::eOk)
return false;
//entId是这个自定义实体的id,可以通过id得到这个自定义实体目前的水平向量和垂直向量,设置夹点的方向
if (*pStr == _T("RightPt"))
vec = m_pDiBan->GetHoriDirection().normal();
else if (*pStr == _T("TopPt"))
vec = m_pDiBan->GetVertDirection().normal();
auto vecUp = AcGeVector3d::kZAxis.crossProduct(vec).normal();
AcGePoint3d pts[3];
pts[0] = pt + vec * dGripSize;
pts[1] = pt - vec * dGripSize / 2 + vecUp * dGripSize * .7;
pts[2] = pt - vec * dGripSize / 2 - vecUp * dGripSize * .7;
pWd->subEntityTraits().setFillType(kAcGiFillAlways);
pWd->geometry().polygon(3, pts);
return true;
}
double CCustomBase::s_gripSize = 2.8;//夹点大小

重载gripStatus函数

void CDiBanCsm::gripStatus(const AcDb::GripStat status)
{
AcDbEntity::gripStatus(status);
switch (status)
{
case AcDb::kGripsDone:
CCustomBase::DeleteGrip(this);
break;
case AcDb::kGripsToBeDeleted:
break;
case AcDb::kDimDataToBeDeleted:
break;
}
}
bool CCustomBase::DeleteGrip(const AcDbEntity* pEnt)
{
std::map<const AcDbEntity*, AcDbGripDataPtrArray>::iterator iter;
for (iter = s_mapGripPtr.begin(); iter != s_mapGripPtr.end(); iter++)
{
if (pEnt == iter->first)
{
AcDbGripDataPtrArray tempGripArr = iter->second;
for (int i = 0; i < tempGripArr.length(); ++i)
{
AcDbGripData* pGrip = tempGripArr[i];
DEL(tempGripArr[i]);
tempGripArr[i] = NULL;
}
}
}
int n = CCustomBase::s_mapGripPtr.erase(pEnt);//如果删除了会返回1,否则返回0  
return n;
}

20190424164812502.png

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!

发表评论

标签云