1. 说明
对实体执行一些移动、旋转、镜像等操作,这种情况下可以直接使用 acedCommand 函数调用相关的 AutoCAD 内部命令,也可以使用 AcDbEntity 类的 transformBy 函数,对实体进行相应的变换操作。
实际上使用第一种方法在某些特定的情况下有一个致命的问题。由于 AutoCAD 内部命令和 ObjectARX 注册的命令在不同的线程中执行,因此有可能导致使用 acedCommand 函数的命令执行过程发生错乱。
本篇的程序介绍使用 transformBy
函数对实体进行几何变换,其中的一些方法使用ObjectARX 中提供的一些基于
COM(组件对象模型)的全局函数来实现。
2. 思路
(1)
使用
transformBy函数进行几何变换( transformBy 函数
)
transformBy 是 AcDbEntity 类的一个成员函数,该函数使用一个 AcGeMatrix3d 参数对实
体进行相应的几何变换,所有 AcDbEntity 的派生类都实现了这个虚函数,因此所有的实体都可以使用这种方法进行几何变换。
函数原型为:
函数原型为:
virtual Acad::ErrorStatus transformBy(const AcGeMatrix3d& xform);
AcGeMatrix3d是一个几何类,用于表示一个四维矩阵,基本形式如图:
AcGeMatrix3d 提供了一些很有用的成员函数:
setToTranslation:生成一个移动对象的矩阵。
setToRotation:生成旋转矩阵。
setToScaling:生成比例缩放矩阵。
setToMirroring:生成镜像矩阵。
(2)复制实体(
clone
函数)
AcDbObject 类拥有一个
clone
函数,能否生成一个调用者的克隆对象,并返回指向克隆对象的指针。由于所有实体对应的类都间接继承于 AcDbObject
类(
AcDbEntity
类从 AcDbObject 类继承),因此所有实体都可以用这种方法进行克隆。
(克隆后把它
添加到模型
空间中)
clone 函数仅仅会生成对象的一个克隆,对于实体对象来说,这样还没有完成复制操作的全部。在创建实体时我们已经了解到,
创建实体仅仅是第一个步骤,还必须把它添加到模型空间中才能被显示出来
,对于克隆得到的实体同样需要这样做。
(3)
使用
AcAxXXX
全局函数
在 ObjectARX 中有一系列 AcAx
开头的全局函数,这些函数通过
COM
的方式来让
AutoCAD
完成一些操作,这一篇我们能使用
AcAxMove、AcAxRotate 、
AcAxScaleEntity
函数分别完成
移动、 旋转、 缩放实体 的操作。
3. 步骤
(1)
对实体进行移动:使用 transformBy 函数
使用 transformBy
函数移动实体的关键在于构建符合要求的
AcGeMatrix3d
对象,
AcGeMatrix3d
类的
setToTranslation
函数用于完成这个功能,它所接受的参数是一个三维矢
量,因此先根据移动的基点和目的点构建了一个
AcGeVector3d
对象。
//移动实体:使用 transformBy 函数
static Acad::ErrorStatus MoveEnt(AcDbObjectId entId, const AcGePoint3d &ptFrom, const AcGePoint3d &ptTo);
//移动实体:使用 transformBy 函数
Acad::ErrorStatus CCreateEnt::MoveEnt(AcDbObjectId entId, const AcGePoint3d &ptFrom, const AcGePoint3d &ptTo)
{
// 构建用于实现移动实体的矩阵
AcGeVector3d vec(ptTo[X] - ptFrom[X], ptTo[Y] - ptFrom[X], ptTo[Z] - ptFrom[Z]);
AcGeMatrix3d mat;
mat.setToTranslation(vec);
AcDbEntity *pEnt = NULL;
Acad::ErrorStatus es = acdbOpenObject(pEnt, entId, AcDb::kForWrite);
if (es != Acad::eOk)
return es;
es = pEnt->transformBy(mat);
pEnt->close();
return es;
}
(2)
对实体的移动:使用 AcAxMove 全局函数
//对实体的移动:使用 AcAxMove 全局函数
BOOL AcMove(AcDbObjectId entId, const AcGePoint3d &ptFrom, const AcGePoint3d &ptTo);
//对实体的移动:使用 AcAxMove 全局函数
BOOL CGeometryOper::AcMove(AcDbObjectId entId, const AcGePoint3d &ptFrom, const AcGePoint3d &ptTo)
{
// 将AcGePoint3d类型的点坐标进行类型转换
VARIANT *pvaFrom = Point3dToVARIANT(ptFrom);
VARIANT *pvaTo = Point3dToVARIANT(ptTo);
BOOL bRet = SUCCEEDED(AcAxMove(entId, *pvaFrom, *pvaTo));
delete pvaFrom;
delete pvaTo;
return bRet;
}
Point3dToVARIANT 是一个自定义函数,能根据
AcGePoint3d
对象生成一个
VARIANT
类型的对象,并返回指向该对象的指针。
Point3dToVARIANT 函数的定义为:
//Point3dToVARIANT函数自定义
static VARIANT* Point3dToVARIANT(const AcGePoint3d &point)
{
COleSafeArray *psa = new COleSafeArray();
DOUBLE dblValues[] = {point[X], point[Y], point[Z]};
psa->CreateOneDim(VT_R8, 3, dblValues);
return (LPVARIANT)(*psa);
}
//static 关键字限制了 Point3dToVARIANT 函数的作用域,在该文件之外不能使用这个函数。
//在 C++编程中,应该给函数尽量小的作用域,全局函数更要尽量避免出现,
//使用 static函数限制函数的作用域是一个好的方法。
由于 VARIANT 和 COleSafeArray 都无法直接作为函数的返回值,它们的复制操作必须使用专门的函数,而不像 C++中的标准类型直接可以复制拷贝,也就是说,下面的函数返回值总是无效的。
static COleSafeArray Point3dToVARIANT(const AcGePoint3d &point)
{
COleSafeArray sa;
DOUBLE dblValues[] = {point[X], point[Y], point[Z]};
sa.CreateOneDim(VT_R8, 3, dblValues);
return sa;
}
由于在 Point3dToVARIANT 函数中使用 new 关键字动态分配了内存,那么调用它的函数必须释放返回值的内存空间,在 AcMove 函数中就分别释放了 pvaFrom 和 pvaTo 所指向变量的空间。
(3)移动实体:对给定 ID 的实体的一个克隆,并且按照 ptFrom 和 ptTo 生成的矢量移动生成的克隆对象
//移动实体:对给定 ID 的实体的一个克隆,并且按照 ptFrom 和 ptTo 生成的矢量移动生成的克隆对象,
BOOL CopyEnt(AcDbObjectId entId, const AcGePoint3d &ptFrom, const AcGePoint3d &ptTo);
//移动实体:对给定 ID 的实体的一个克隆,并且按照 ptFrom 和 ptTo 生成的矢量移动生成的克隆对象,
BOOL CGeometryOper::CopyEnt(AcDbObjectId entId, const AcGePoint3d &ptFrom, const AcGePoint3d &ptTo)
{
AcDbEntity *pEnt = NULL;
if (acdbOpenObject(pEnt, entId, AcDb::kForRead) != Acad::eOk)
return FALSE;
AcDbEntity *pCopyEnt = AcDbEntity::cast(pEnt->clone());
AcDbObjectId copyEntId;
if (pCopyEnt)
copyEntId = PostToModelSpace(pCopyEnt);
MoveEnt(copyEntId, ptFrom, ptTo);
return TRUE;
}
clone 函数能够生成调用者的一个克隆,该函数是
AcDbObject
类的一个成员函数,其原
型为:
virtual AcRxObject* clone() const;
由于函数的返回值不是 AcDbEntity 类型的指针,因此必须通过一个很常用的方法进行实体指针的升级:
AcDbEntity *pCopyEnt = AcDbEntity::cast(pEnt->clone());
Copy 函数中,
PostToModelSpace
函数用于将实体添加到模型空间,同样使用
static
关键
字来限制其名称的可见性:
static AcDbObjectId PostToModelSpace(AcDbEntity* pEnt)
{
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getBlockTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
AcDbObjectId entId;
pBlockTableRecord->appendAcDbEntity(entId, pEnt);
pBlockTable->close();
pBlockTableRecord->close();
pEnt->close();
return entId;
}
(5)旋转实体
//旋转实体
Acad::ErrorStatus Rotate(AcDbObjectId entId, const AcGePoint2d &ptBase, double angle)
//旋转实体
Acad::ErrorStatus CGeometryOper::Rotate(AcDbObjectId entId, const AcGePoint2d &ptBase, double angle)
{
// 构建变换矩阵
AcGeMatrix3d mat;
mat.setToRotation(angle, AcGeVector3d::kZAxis,
AcGePoint3d(ptBase[X], ptBase[Y], 0.));
// 对实体进行变换
AcDbEntity *pEnt = NULL;
Acad::ErrorStatus es = acdbOpenObject(pEnt, entId,
AcDb::kForWrite);
if (es != Acad::eOk)
return es;
es = pEnt->transformBy(mat);
pEnt->close();
return es;
}