ObjectARX完美实现一次拖动多个实体(上)
我们知道,在ObjectARX中可以通过派生AcEdJig类来实现拖动过程。通常派生一个AcEdJig类必须重载以下三个成员函数:
AcEdJig::sampler(),它获取几何值(角度、距离、点等)
AcEdJig::update(),它分析几何值并且存储该值或更新实体
AcEdJig::entity(),它返回要更新的实体的指针
但在使用过程中发现有一个问题,就是AcEdJig::entity()只能返回单个实体的指针,也就是说使用AcEdJig类的派生类来实现拖动循环原则上只适用于单个实体,要使其适用于拖动多个实体,就不得不进行某些变通的处理。例如要对若干个新建的实体使用拖动,一种变通方法就是把新建的实体先加入CAD的数据库,在拖动过程中(即update()中)使用AcD::kForWrite模式打开并更新实体然后关闭。拖动结束后根据返回情况决定是保留还是删除实体。这样做显得很麻烦,一是要对新生成的实体作区别处理,entity()返回的实体和"其它"实体;二是先把实体加入数据库,然后再决定是否删除不应该是一种推荐的方式,总感觉有那么点不自在;拖动过程中频繁地用写方式打开实体也应该避免;而且这种方式并不适用数据库中已存在的实体,因为一旦取消拖动过程,实体可能已经不处在原来的位置了,当然可以通过复制临时实体的方式解决,但这就进一步使问题复杂化了。
既然AcEdJig::entity()只能返回一个实体,最好的办法当然是从这个实体上入手了,我们使用一个自定义的临时实体,完美解决了这个问题,今天先来看新建实体的例子:
先定义临时实体:
//辅助实体类
class CMultiCircleJigEntity : public AcDbEntity
{
public:
CMultiCircleJigEntity(const AcGePoint3d & centerPoint, const unsigned int &iNum);
~CMultiCircleJigEntity();
virtual Adesk::Boolean worldDraw (AcGiWorldDraw *mode);
void setRadius(double dRadius);
void appendToCurrentSpace();
private:
AcArray<AcDbCircle *> m_CircArr;
};
CMultiCircleJigEntity::CMultiCircleJigEntity(const AcGePoint3d & centerPoint, const unsigned int &iNum)
{
AcDbCircle *pCirc;
for (int i = 0; i < iNum; i++)
{
pCirc = new AcDbCircle(centerPoint, AcGeVector3d::kZAxis, 1.0);
m_CircArr.append(pCirc);
}
}
CMultiCircleJigEntity::~CMultiCircleJigEntity()
{
for (int i = 0; i < m_CircArr.length(); i++)
{
delete m_CircArr[i];
}
}
Adesk::Boolean CMultiCircleJigEntity::worldDraw(AcGiWorldDraw *mode)
{
for (int i = 0; i < m_CircArr.length(); i++)
{
mode ->geometry().draw(m_CircArr[i]);
}
return (AcDbEntity::worldDraw (mode)) ;
}
inline void CMultiCircleJigEntity::setRadius(double dRadius)
{
if (m_CircArr.length() <= 0)
return;
double dCurRadius = dRadius;
double dRadiusStep = dRadius / m_CircArr.length();
for (int i = 0; i < m_CircArr.length(); i++)
{
m_CircArr[i] ->setRadius(dCurRadius);
dCurRadius -= dRadiusStep;
}
}
void CMultiCircleJigEntity::appendToCurrentSpace()
{
AcDbDatabase * pDb = acdbCurDwg();
AcDbBlockTable * pBlockTable;
pDb ->getBlockTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlkRec;
if (pDb ->tilemode())
{
pBlockTable ->getAt(ACDB_MODEL_SPACE, pBlkRec, AcDb::kForWrite);
}
else
{
pBlockTable ->getAt(ACDB_PAPER_SPACE, pBlkRec, AcDb::kForWrite);
}
pBlockTable ->close();
for (int i = 0; i < m_CircArr.length(); i++)
{
AcDbCircle *& pCirc = m_CircArr.at(i);
if (Acad::eOk == pBlkRec ->appendAcDbEntity(pCirc))
{
pCirc ->setDatabaseDefaults();
pCirc ->close();
}
else
{
delete pCirc;
}
}
pBlkRec ->close();
m_CircArr.removeAll();
}
再实现AcEdJig派生类:
//jig类
class CMultiCircJig : public AcEdJig
{
public:
CMultiCircJig(const AcGePoint3d & centerPoint, const unsigned int &iNum);
void doIt();
virtual DragStatus sampler();
virtual Adesk::Boolean update();
virtual AcDbEntity* entity() const;
private:
CMultiCircleJigEntity *m_pEnt;
AcGePoint3d m_CenterPoint;
double m_dRadius;
unsigned int m_NumCircles;
};
CMultiCircJig::CMultiCircJig(const AcGePoint3d ¢erPoint, const unsigned int & iNum) : m_CenterPoint(centerPoint), m_NumCircles(iNum)
{
}
AcEdJig::DragStatus CMultiCircJig::sampler()
{
static double dTempRadius;
DragStatus stat = acquireDist(m_dRadius, m_CenterPoint);
if (dTempRadius != m_dRadius)
{
dTempRadius = m_dRadius;
}
else if (stat == AcEdJig::kNormal)
{
return AcEdJig::kNoChange;
}
return stat;
}
Adesk::Boolean CMultiCircJig::update()
{
m_pEnt ->setRadius(m_dRadius);
return Adesk::kTrue;
}
AcDbEntity * CMultiCircJig::entity() const
{
return m_pEnt;
}
void CMultiCircJig::doIt()
{
m_pEnt = new CMultiCircleJigEntity(m_CenterPoint, m_NumCircles);
setDispPrompt(_T("/n半径: "));
if (drag() == AcEdJig::kNormal)
{
m_pEnt ->appendToCurrentSpace();
}
delete m_pEnt;
}
最后是测试命令:
static void MyJig_njj(void)
{
int iNum = 5;
acedInitGet( RSG_NOZERO | RSG_NONEG, NULL );
int err = acedGetInt(_T("/n同心圆个数(1-100)<5>:"), &iNum);
if (err == RTCAN || iNum < 1 || iNum > 100)
{
return;
}
AcGePoint3d centerPoint;
if (RTNORM == acedGetPoint(NULL, _T("/n圆心"), asDblArray(centerPoint)))
{
CMultiCircJig * pJig = new CMultiCircJig(centerPoint, iNum);
pJig ->doIt();
delete pJig;
}
}
ObjectARX完美实现一次拖动多个实体(下)
这次我们来实现一次拖动多个数据库中已存在的实体,就象AuoCAD里的Move、Rotate、Mirror等命令一样。其中的临时实体类具有一定的通用性,只要给它传入不同的变换矩阵就能实现相应的功能。而Jig类的例子则是一个简单的移动示例,大约相当于Move命令的简化版。
临时实体类:
//辅助实体类
//适用于拖动数据库中已经存在的实体,通常是一个选择集选择的实体集
class CDatabaseJigEntity : public AcDbEntity
{
public:
CDatabaseJigEntity(const AcDbObjectIdArray & ids) : m_Ids(ids){}
~CDatabaseJigEntity(){}
virtual Adesk::Boolean worldDraw (AcGiWorldDraw *mode);
void setXform(const AcGeMatrix3d & xform){ m_Xform = xform; }
BOOL transform();
BOOL transformedCopy();
private:
AcDbObjectIdArray m_Ids; //保存所有拖动对象的ID
AcGeMatrix3d m_Xform;//变换矩阵
};
Adesk::Boolean CDatabaseJigEntity::worldDraw(AcGiWorldDraw *mode)
{
//这个地方是关键!
mode->geometry().pushModelTransform(m_Xform);
AcDbEntity* pEnt;
for (int i = 0; i < m_Ids.length(); i++)
{
//绘制实体无需写打开
if (Acad::eOk == acdbOpenObject(pEnt, m_Ids[i],AcDb::kForRead))
{
mode->geometry().draw(pEnt);
pEnt->close();
}
}
//这句不能少,恢复现场
mode->geometry().popModelTransform();
return (AcDbEntity::worldDraw (mode)) ;
}
//用于拖动结束后,将实体变换到新位置
BOOL CDatabaseJigEntity::transform()
{
AcTransaction * pTrans = acTransactionManagerPtr() ->startTransaction();
if (NULL == pTrans)
return FALSE;
AcDbEntity *pEnt;
AcDbObject *pObj;
for (int i= 0; i < m_Ids.length(); i++)
{
if (Acad::eOk != pTrans ->getObject(pObj, m_Ids[i], AcDb::kForWrite))
{
acTransactionManagerPtr() ->abortTransaction();
return FALSE;
}
pEnt = AcDbEntity::cast(pObj);
pEnt ->transformBy(m_Xform);
}
acTransactionManagerPtr() ->endTransaction();
return TRUE;
}
//用于拖动结束后,将实体复制到新位置
BOOL CDatabaseJigEntity::transformedCopy()
{
AcTransaction * pTrans = acTransactionManagerPtr() ->startTransaction();
if (NULL == pTrans)
return FALSE;
AcDbEntity *pEnt;
AcDbEntity *pNewEnt;
AcDbObject *pObj;
AcDbBlockTableRecord *pBlkRec;
AcDbObjectId blkRecId;
for (int i= 0; i < m_Ids.length(); i++)
{
if (Acad::eOk != pTrans ->getObject(pObj, m_Ids[i], AcDb::kForRead))
{
acTransactionManagerPtr() ->abortTransaction();
return FALSE;
}
pEnt = AcDbEntity::cast(pObj);
if (0 == i)
{
blkRecId = pEnt ->blockId();
if (Acad::eOk != pTrans ->getObject(pObj, blkRecId, AcDb::kForWrite))
{
acTransactionManagerPtr() ->abortTransaction();
return FALSE;
}
pBlkRec = AcDbBlockTableRecord::cast(pObj);
}
pEnt ->getTransformedCopy(m_Xform, pNewEnt);
pBlkRec ->appendAcDbEntity(pNewEnt);
acTransactionManagerPtr() ->addNewlyCreatedDBRObject(pNewEnt);
}
acTransactionManagerPtr() ->endTransaction();
return TRUE;
}
用于移动的Jig类
//移动实体的拖动类
class CMoveJig : public AcEdJig
{
public:
CMoveJig(const AcGePoint3d & fromPoint) : m_pEnt(NULL), m_FromPoint(fromPoint), m_ToPoint(fromPoint){}
~CMoveJig();
void doIt(const AcDbObjectIdArray & ids, bool bCopy = false);
virtual DragStatus sampler();
virtual Adesk::Boolean update();
virtual AcDbEntity* entity() const;
private:
CDatabaseJigEntity *m_pEnt;
AcGePoint3d m_FromPoint;
AcGePoint3d m_ToPoint;
AcGeMatrix3d m_Xform;
};
CMoveJig::~CMoveJig()
{
if ( NULL != m_pEnt)
{
delete m_pEnt;
m_pEnt = NULL;
}
}
AcEdJig::DragStatus CMoveJig::sampler()
{
DragStatus stat;
setUserInputControls((UserInputControls)
(AcEdJig::kAccept3dCoordinates
| AcEdJig::kNoNegativeResponseAccepted
| AcEdJig::kNoZeroResponseAccepted
| AcEdJig::kNullResponseAccepted ));
static AcGePoint3d pointTemp;
stat = acquirePoint(m_ToPoint, m_FromPoint);
if (pointTemp != m_ToPoint)
pointTemp = m_ToPoint;
else if (stat == AcEdJig::kNormal)
stat = AcEdJig::kNoChange;
return stat;
}
Adesk::Boolean CMoveJig::update()
{
m_Xform.setToTranslation(m_ToPoint - m_FromPoint);
m_pEnt ->setXform(m_Xform);
return Adesk::kTrue;
}
AcDbEntity * CMoveJig::entity() const
{
return m_pEnt;
}
void CMoveJig::doIt(const AcDbObjectIdArray &ids, bool bCopy /*= false*/)
{
if ( 0 == ids.length() )
{
return;
}
if (NULL != m_pEnt)
{
delete m_pEnt;
m_pEnt = NULL;
}
m_pEnt = new CDatabaseJigEntity(ids);
setDispPrompt(_T("/n移动到: "));
if ( AcEdJig::kNormal == drag() )
{
if (bCopy)
{
m_pEnt ->transformedCopy();
}
else
{
m_pEnt ->transform();
}
}
delete m_pEnt;
m_pEnt = NULL;
}
测试命令:
static void ZDXMyJig_jigmove(void)
{
ads_name ename;
ads_point pt;
ads_name ss;
int rt;
if (rt = acedSSGet(NULL, NULL, NULL, NULL, ss) == RTCAN)
return;
long len;
acedSSLength(ss, &len);
if (0 == len) return;
AcDbObjectId id;
//acdbGetObjectId(id,ename);
AcDbObjectIdArray ids;
for (int i = 0; i < len; i++)
{
acedSSName(ss, i , ename);
acdbGetObjectId(id, ename);
ids.append(id);
}
acedSSFree(ss);
acedGetPoint(NULL, L"/n起点:", pt);
CMoveJig * pJig = new CMoveJig(asPnt3d(pt));
pJig ->doIt(ids);
}