天天看點

ObjectARX自定義實體

說明: 此次繪制的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即可。

五、總結

其實原本的代碼比這個多很多,這裡我隻是截取了重要部分展示。這是目前繪制的自定義實體,某些地方還需要改進。