|
[code]1. 说明
ObjectARX 中提供了三类创建三维实体的方法:
(1)创建标准形状的实体
(2)拉伸面域创建实体
(3)旋转面域创建实体。
本篇通过创建长方体、圆锥体、弹簧和一个皮带轮实体来介绍这三种方法。
仅能用三类方法来创建实体,所能得到的实体的形状还是很有限的,布尔运算提供了实体的交、并、补运算,能够根据已有的实体“组合”出复杂形状的实体。本篇通过对两个长方体进行布尔运算来介绍其使用方法。
2. 思路
在 ObjectARX 中, AcDb3dSolid 类用于代表 AutoCAD 中的三维实体,提供了创建和合并实体的一些方法,与使用 AutoCAD 命令来创建实体类似。
但是,ACIS 实体才是实体真正的几何表示,AcDb3dSolid 类只是 ACIS 实体的容器和接口,该类中并没有提供直接操作 ACIS 实体边、顶点和面的方法。要遍历 ACIS 实体中隐含(无法直接访问子实体)的边、面和顶点,必须使用 ObjectARX 开发包中的 BREP 应用程序开发接口( API )。
(1)长方体
AcDb3dSolid 类仅提供了一个不包含任何参数的构造函数,用于创建一个“空”实体,在构造 AcDb3dSolid 对象之后,必须使用其成员函数来完成实体的创建。
createBox 函数用于创建长方体,其定义为:
//该函数将会创建一个中心位于世界坐标系原点的长方体
//并且其长、宽、高分别平行于世界坐标系的 X、Y 和 Z 轴。
virtual Acad::ErrorStatus createBox(
double xLen, //长方体的长
double yLen, //长方体的宽
double zLen); //长方体的高
(2)圆锥体
ObjectARX 中并未直接提供创建圆锥体的方法,而是将其包含在创建平截头体(圆柱体
和圆锥体都是其中的一种)的函数 createFrustum 中,
createFrustum 函数定义为:
//如果要创建一个圆锥体
//将 topXRadius 参数设置为 0,并且保证 xRadius 和 yRadius 的值相等
virtual Acad::ErrorStatus createFrustum(
double height, //height 表示平截头体的高度
double xRadius, //xRadius 表示底面在 X 轴方向的半径
double yRadius, // 底面在 Y 轴方向的半径
double topXRadius); //topXRadius 表示顶面在 X 轴方向的半径
(3)拉伸面域创建实体
AcDb3dSolid 类中的 extrudeAlongPath 函数用于拉伸面域创建一个实体,
extrudeAlongPath 函数定义为:
//拉伸面域创建一个实体
//注意:在执行extrudeAlongPath函数时,region和 path 都必须是模型空间中的实体,否则会引发一个异常。
virtual Acad::ErrorStatus extrudeAlongPath(
const AcDbRegion* region, //region是一个指向作为拉伸截面的面域的指针
const AcDbCurve* path); //path 是一个指向作为拉伸路径的曲线的指针
AcDb3dSolid类的另一个函数extrude用于沿面域所在平面的法线方向拉伸面域创建新的实体,并且可以指定拉伸时的斜切角度。
(4)旋转面域创建实体
AcDb3dSolid 类中的 revolve 函数用于绕给定的轴线旋转面域而生成实体,其定义为:
revolve 函数定义为:
//绕给定的轴线旋转面域而生成实体
virtual Acad::ErrorStatus revolve(
const AcDbRegion* region, //region是一个指向作为旋转截面的面域的指针
const AcGePoint3d& axisPoint, //axisPoint 指定旋转轴线上的一点
const AcGeVector3d& axisDir, //axisDir指定了旋转轴的方向,和axisPoint共同确定旋转轴的具体位置
double angleOfRevolution); //angleOfRevolution 指定旋转面域的角度(弧度值来表示)
(5)布尔运算
AcDb3dSolid 类中的 booleanOper 函数用于在两个实体之间执行布尔运算
booleanOper 函数定义为:
//在两个实体之间执行布尔运算
virtual Acad::ErrorStatus booleanOper(
AcDb::BoolOperType operation, //operation指定了进行布尔运算的方式,包括
//AcDb::kBoolUnite (并集)
//AcDb::kBoolIntersect(交集)
//AcDb::kBoolSubtract(差集)三种类型
AcDb3dSolid* solid); //solid是一个指向布尔运算的另一个实体的指针
3. 步骤
(1) 创建长方体
//添加长方体
static void AddBox(); //添加长方体
//添加长方体
void CCreateEnt::AddBox()
{
AcDb3dSolid *pSolid = new AcDb3dSolid();
Acad::ErrorStatus es = pSolid->createBox(40, 50, 30);
if (es != Acad::eOk)
{
acedAlert(_T("创建长方体失败!"));
delete pSolid;
return;
}
// 使用几何变换矩阵移动长方体
AcGeMatrix3d xform;
AcGeVector3d vec(100, 100, 100);
xform.setToTranslation(vec);
pSolid->transformBy(xform);
// 将长方体添加到模型空间
PostToModelSpace(pSolid);//此为自定义函数,在本系列(1)(4)中有具体代码
}
(2) 创建圆锥
//添加圆锥
static void AddCylinder(); //添加圆锥
//添加圆锥
void CCreateEnt::AddCylinder()
{
// 创建特定参数的圆柱体(实际上是一个圆锥体)
AcDb3dSolid *pSolid = new AcDb3dSolid();
pSolid->createFrustum(30, 10, 10, 0);
// 将圆锥体添加到模型空间
PostToModelSpace(pSolid);
}
(3) 创建弹簧
首先创建一个三维螺旋线作为拉伸路径,使用小段的三维多段线来模拟三维螺旋线,segment 的一点是,需要计算的顶点个数实际上是 num 间隔数总是比节点数少 1 。
atan( 用来代替π,其值为π/4,因此 4*atan(1)的值就是π。这种替代比直接用一个常量“const double PI = 3.1415926;”要精确得多。
代码中使用了两个 ObjectARX 中的数组类型:AcGePoint3dArray 和 AcDbObjectIdArray。这两个类均派生自 AcArray 类,AcArray 类的使用非常简单,length 函数用于获得数组对象的元素个数;at 函数用于获得指定索引的元素的值;append 函数用于向数组添加元素;[]运算符的作用与 at 函数相同。
其中,CreateRegion 是一个自定义函数,用于根据给定的边界创建面域,该函数与 博客(9)
(9)ObjectARX2015 + vs2012创建面域_qq_42981953的博客-CSDN博客
中的同名函数完全相同
//添加三维弹簧模型
static void AddSpire();//添加三维弹簧模型
//添加三维弹簧模型
void CCreateEnt::AddSpire()
{
//指定创建螺旋线的参数
double radius, deltaVertical; //半径和每一周在垂直方向的增量
double number, segment; //螺旋线的旋转圈数和组成一圈
radius = 30, deltaVertical = 12;
number = 5, segment = 30;
//计算点的个数和角度间隔
int n = number * segment; //点的个数实际上是n+1
double angle = 8 * atan(1) / segment; //两点之间的旋转角度
//计算控制点的坐标
AcGePoint3dArray points; // 控制点坐标数组
for (int i = 0; i < n+1; i++)
{
AcGePoint3d vertex;
vertex[X] = radius * cos(8 * i * atan(1) / segment);
vertex[Y] = radius * sin(8 * i * atan(1) / segment);
vertex[Z] = i * deltaVertical / segment;
points.append(vertex);
}
// 创建螺旋线路径
AcDb3dPolyline *p3dPoly = new AcDb3dPolyline(AcDb::k3dSimplePoly, points);
//将路径添加到模型空间
AcDbObjectId spireId = PostToModelSpace(p3dPoly);
//创建一个圆作为拉伸的截面
AcGeVector3d vec(0, 1, 0); //圆所在平面的法矢量
AcGePoint3d ptCenter(30, 0, 0); //圆心位置与半径的大小有关
AcDbCircle *pCircle = new AcDbCircle(ptCenter, vec, 3);
AcDbObjectId circleId = PostToModelSpace(pCircle);
//根据圆创建一个面域
AcDbObjectIdArray boundaryIds, regionIds;
boundaryIds.append(circleId);
regionIds = CreateRegion(boundaryIds);
//打开拉伸截面和拉伸路径
AcDbRegion *pRegion;
acdbOpenObject(pRegion, regionIds.at(0), AcDb::kForRead);
AcDb3dPolyline *pPoly;
acdbOpenObject(pPoly, spireId, AcDb::kForRead);
//进行拉伸操作
AcDb3dSolid *pSolid = new AcDb3dSolid();
pSolid->extrudeAlongPath(pRegion, pPoly);
PostToModelSpace(pSolid);
pPoly->close();
pRegion->close();
}
(4) 旋转面域成三维实体
//旋转面域成三维实体
static void RevolveEnt(); //旋转面域成三维实体
//旋转面域成三维实体
void CCreateEnt::RevolveEnt()
{
// 设置顶点的坐标
AcGePoint3d vertex[5];
vertex[0] = AcGePoint3d(15, 0, 0);
vertex[1] = AcGePoint3d(45, 0, 0);
vertex[2] = AcGePoint3d(35, 9, 0);
vertex[3] = AcGePoint3d(41, 18, 0);
vertex[4] = AcGePoint3d(15, 18, 0);
AcGePoint3dArray points;
for (int i= 0; i <= 4; i++)
{
points.append(vertex[i]);
}
//创建作为旋转截面的多段线
AcDb3dPolyline *p3dPoly = new AcDb3dPolyline(AcDb::k3dSimplePoly, points, true);
AcDbObjectId polyId = PostToModelSpace(p3dPoly);
//将闭合的多段线转化成面域
AcDbObjectIdArray boundarylds, regionlds;
boundarylds.append(polyId);
regionlds = CreateRegion(boundarylds);
//进行旋转操作
AcDbRegion *pRegion;
Acad:ErrorStatus es = acdbOpenObject(pRegion, regionlds.at(0), AcDb::kForRead);
AcDb3dSolid *pSolid = new AcDb3dSolid();
es = pSolid->revolve(pRegion,AcGePoint3d::kOrigin,AcGeVector3d(0, 1, 0),8 * atan(1));
PostToModelSpace(pSolid);
pRegion->close();
}
(5) 布尔运算组合三维实体
//布尔运算组合三维实体
static void BooleanEnt(); //布尔运算组合三维实体
//布尔运算组合三维实体
void CCreateEnt::BooleanEnt()
{
//创建两个长方体
AcDb3dSolid *pSolid1 = new AcDb3dSolid();
pSolid1->createBox(40,50,30);
AcDb3dSolid *pSolid2 = new AcDb3dSolid();
pSolid2->createBox(40,50,30);
//使用几何变换矩阵移动长方体
AcGeMatrix3d xform;
AcGeVector3d vec(20,25,15);
xform.setToTranslation(vec);
pSolid1->transformBy(xform);
//将长方体添加到模型空间
AcDbObjectId solidld1 = PostToModelSpace(pSolid1);
AcDbObjectId solidld2 = PostToModelSpace(pSolid2);
//进行布尔运算,生成新的实体
acdbOpenObject(pSolid1 , solidld1, AcDb::kForWrite);
acdbOpenObject(pSolid2, solidld2, AcDb::kForWrite);
Acad:ErrorStatus es = pSolid1->booleanOper(AcDb::kBoolUnite, pSolid2);
assert(pSolid2->isNull());pSolid2->erase();
//将其删除
pSolid2->close();
//删除之后还是需要关闭该实体
pSolid1->close();
}
booleanOper函数不会自动删除 pSolid2,因此需要手工删除 pSolid2,删除之后仍然需要使用 close 函数将其关闭,也就是将该对象的控制权交给AutoCAD。为什么在删除实体之后仍需要关闭实体,并将对象控制权交给AutoCAD? 因为执行erase函数之后系统不会立即从图形中将指定的实体删除,而是将其“删除位”打开,在AutoCAD保存图形时该实体将不会再被保存,下一次重新打开图形时该实体就不再存在。
提示: 为什么erase 函数执行时不是立即删除实体? 这是为了提供Undo(撤消)的操作,只要未关闭图形,一个被删除的实体就可以使用 erase(kfalse) 来撤消删除。
原文链接:https://blog.csdn.net/qq_42981953/article/details/121857829[/code] |
|