說明: 此次繪制的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;
//删除
virtual Acad::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
AcGePoint3d pt = 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即可。
五、總結
其實原本的代碼比這個多很多,這裡我隻是截取了重要部分展示。這是目前繪制的自定義實體,某些地方還需要改進。