一,选择集的创建和删除
(1)使用的的ObjectARX的向导创建一个新工程名称为SelectionSet
创建完成后,编译,若出错:
错误C2338:/ RTCc拒绝符合代码,因此C ++标准库不支持它。删除此编译器选项,或定义_ALLOW_RTCc_IN_STL以确认您已收到此警告。
需要做如下修改:
①debug设置为64位
②较小类型检查设置为:否
(2)注册一个新命令CreateSSet,用于演示选择集的创建和删除
static void AAAMyGroupCreateSSet() {
// Put your command code here
ads_name sset;//选择集名称
//选择图形数据库中所有的实体
acedSSGet(TEXT("A"), NULL, NULL, NULL, sset);
long length;
acedSSLength(sset, (Adesk::Int32*) &length);
acutPrintf(TEXT("\n实体数:%d"), length);
//进行其他操作
acedSSFree(sset);
}
注意:acedSSLength(sset,(Adesk :: Int32 *)&length);的第二个参数需要强制类型转换
acedSSGet函数:
通过指定一个的AutoCAD中的选择模式来返回一个选择集。选择模式是通过提示的AutoCAD中的用户或过滤绘图数据库来指定的。在处理完选择之后,您必须释放所分配的选择集。
int acedSSGet(
const ACHAR * str,
const void * pt1,
const void * pt2,
const struct resbuf * filter,
ads_name ss
);
const ACHAR * str:指定要使用的实体选择模式的可选字符串 |
一下是模式列表:
const void * pt1:与某些选择模式相关的可选点;或者一个包含多边形或篱笆选择选项的多个点的结果缓冲列表; |
const void * pt2:与某些选择模式相关的可选点 |
const struct resbuf * filter:可选的结果缓冲列表,使用acedSSGet()能够过滤绘图以选择某些类型的实体或具有某些属性 |
ads_name ss:选择集的名称。 |
(3)效果1
在CAD中创建3个实体:
按Ctrl + F2:
二,对象选择的方法
(1)注册一个新命令SelectEnt2,使用多种不同的模式创建选择集
static void AAAMyGroupSelectEnt2() {
//因为CAD默认快捷键中已经存在SelectEnt,所以在这里我们注册命令为SelectEnt2
// Put your command code here
ads_point pt1, pt2, pt3, pt4;
struct resbuf *pointlist; //结果缓冲区链表
ads_name ssname; //选择集的图元名
pt1[X] = pt2[Y] = pt1[Z] = 0.0;
pt2[X] = pt2[Y] = 5.0; pt2[Z] = 0.0;
//如果已经选择到了实体,就获得当前的PICKFIRST选择集
//否则提示用户选择实体
acedSSGet(NULL, NULL, NULL, NULL, ssname);
//如果存在,就获得当前的PickFirst选择集
acedSSGet(TEXT("I"), NULL, NULL, NULL, ssname);
//选择最近创建的选择集
acedSSGet(TEXT("P"), NULL, NULL, NULL, ssname);
//选择最后一次创建的可见实体
acedSSGet(TEXT("L"), NULL, NULL, NULL, ssname);
//选择通过点(5,5)的所有实体
acedSSGet(NULL, pt2, NULL, NULL, ssname);
//选择位于角点(0,0)和(5,5)组成的窗口内所有的实体
acedSSGet(TEXT("W"), pt1, pt2, NULL, ssname);
//选择被指定的多边形包围的所有实体
pt3[X] = 10.0; pt3[Y] = 5.0; pt3[Z] = 0.0;
pt4[X] = 5.0; pt4[Y] = pt4[Z] = 0.0;
pointlist = acutBuildList(RTPOINT, pt1, RTPOINT, pt2,
RTPOINT, pt3, RTPOINT, pt4, 0);
acedSSGet(TEXT("WP"), pointlist, NULL, NULL, ssname);
//选择与角点(0.0)和(5,5)组成的区域相交的所有实体
acedSSGet(TEXT("C"), pt1, pt2, NULL, ssname);
//选择与指定多边形区域相交的所有实体
acedSSGet(TEXT("CP"), pointlist, NULL, NULL, ssname);
acutRelRb(pointlist);
//选择与选择栏相交的所有实体
pt4[Y] = 15.0; pt4[Z] = 0.0;
pointlist = acutBuildList(RTPOINT, pt1, RTPOINT, pt2,
RTPOINT, pt3, RTPOINT, pt4, 0);
acedSSGet(TEXT("F"), pointlist, NULL, NULL, ssname);
acutRelRb(pointlist);//释放分配给指定结果缓冲区的内存
acedSSFree(ssname);
}
(2)在acrxEntryPoint.cpp文件的开头部分添加PolyToCurve函数,该函数用于根据指定的多段线创建对应的几何类曲线,包含两个参数,pPline指定已知的多段线,pGeCurve参数输出创建的几何曲线。
实现函数:
static bool PolyToGeCurve(AcDbPolyline *pPline, AcGeCurve2d *&pGeCurve)
{
int nSegs; //多段线的段数
AcGeLineSeg2d line, *pLine; //几何曲线的直线段部分
AcGeCircArc2d arc, *pArc; //几何曲线的圆弧部分
AcGeVoidPointerArray geCurves; //指向组成几何曲线各分段的指针数组
nSegs = pPline->numVerts() - 1;
//根据多段线创建对应的分段几何曲线
for (int i = 0; i < nSegs; i++)
{
if (pPline->segType(i) == AcDbPolyline::kLine)
{
pPline->getLineSegAt(i, line);
pLine = new AcGeLineSeg2d(line);
geCurves.append(pLine);
}
else if (pPline->segType(i) == AcDbPolyline::kArc)
{
pPline->getArcSegAt(i, arc);
pArc = new AcGeCircArc2d(arc);
geCurves.append(pArc);
}
}
//处理闭合多段线最后一段是圆弧的情况
if (pPline->isClosed() && pPline->segType(nSegs) == AcDbPolyline::kArc)
{
pPline->getArcSegAt(nSegs, arc);
pArc = new AcGeCircArc2d(arc);
pArc->setAngles(arc.startAng(), arc.endAng() -
(arc.endAng() - arc.startAng()) / 100);
geCurves.append(pArc);
}
//根据分段的几何曲线创建对应的复合曲线
if (geCurves.length() == 1)
{
pGeCurve = (AcGeCurve2d*)geCurves[0];
}
else
{
pGeCurve = new AcGeCompositeCurve2d(geCurves);
}
//释放动态分配的内存
if (geCurves.length() > 1)
{
for (int i = 0; i < geCurves.length(); i++)
{
delete geCurves[i];
}
}
return true;
}
①getLineSegAt(I,线)函数:
如果顶点索引中的线段是一条直线,那么“线”将被设置为该段的一个代表性的2D副本,在折线自己的对象坐标系统(OCS)中。
②getArcSegAt(nSegs,弧)函数:
如果顶点索引nSegs中的线段是一个弧,那么这个函数将在折线自己的坐标中从折线中填充2D“弧”信息(半径,中心等)。
(3)在acrxEntryPoint.cpp中添加一个新的函数SelectEntInPoly,用于选择指定多段线内部(或者与多段线构成的区域相交的所有实体)
函数的实现代码为:
static bool SelectEntInPoly(AcDbPolyline *pPline, AcDbObjectIdArray &ObjectIdArray, const char *selectMode, double approxEps)
{
//判断selectMode的有效性
if (_tcscmp((const wchar_t *)selectMode, TEXT("CP")) != 0 && _tcscmp((const wchar_t *)selectMode, TEXT("WP")) != 0)
{
acedAlert(TEXT("函数SelectEntInPline中,指定了无效的选择模式!"));
return false;
}
//清除数组中所有的ObjectId
for (int i = 0; i < ObjectIdArray.length(); i++)
{
ObjectIdArray.removeAt(i);
}
AcGeCurve2d *pGeCurve; //多段线对应的几何曲线
Adesk::Boolean bClosed = pPline->isClosed(); //多段线是否闭合
if (bClosed != Adesk::kTrue) //确保多段线作为选择边界时是闭合的
{
pPline->setClosed(!bClosed);
}
//创建对应的几何类曲线
PolyToGeCurve(pPline, pGeCurve);
//获得几何曲线的样本点
AcGePoint2dArray SamplePtArray; //存储曲线的样本点
AcGeDoubleArray ParamArray; //存储样本点对应的参数值
AcGePoint2d ptStart, ptEnd; //几何曲线的起点和终点
Adesk::Boolean bRet = pGeCurve->hasStartPoint(ptStart);
bRet = pGeCurve->hasEndPoint(ptEnd);
double valueSt = pGeCurve->paramOf(ptStart);
double valueEn = pGeCurve->paramOf(ptEnd);
pGeCurve->getSamplePoints(valueSt, valueEn, approxEps,
SamplePtArray, ParamArray);
delete pGeCurve; //在函数PolyToGeCurve中分配了内存
//确保样本点的起点和终点不重合
AcGeTol tol;
tol.setEqualPoint(0.01);
AcGePoint2d ptFirst = SamplePtArray[0];
AcGePoint2d ptLast = SamplePtArray[SamplePtArray.length() - 1];
if (ptFirst.isEqualTo(ptLast))
{
SamplePtArray.removeLast();
}
//根据样本点创建结果缓冲区链表
struct resbuf *rb;
rb = BuildRbFromPtArray(SamplePtArray);
//使用acedSSGet函数选择集
ads_name ssName; //选择集名称
int rt = acedSSGet((ACHAR *)selectMode, NULL, NULL, rb, ssName);
if (rt != RTNORM)
{
acutRelRb(rb); //释放结果缓冲区
return false;
}
//将选择集中所有的对象添加到ObjectIdArray
long length;
acedSSLength(ssName, (Adesk::Int32 *)&length);
for (int i = 0; i < length; i++)
{
//获得指定元素的ObjectId
ads_name ent;
acedSSName(ssName, i, ent);
AcDbObjectId objId;
acdbGetObjectId(objId, ent);
//获得指向当前元素的指针
AcDbEntity *pEnt;
Acad::ErrorStatus es = acdbOpenAcDbEntity(pEnt, objId, AcDb::kForRead);
//选择到作为边界的多段线了,直接跳过该次循环
if (es == Acad::eWasOpenForWrite)
{
continue;
}
ObjectIdArray.append(pEnt->objectId());
pEnt->close();
}
//释放内存
acutRelRb(rb); //释放结果缓冲区链表
acedSSFree(ssName); //删除选择集
return true;
}
①_tcscmp(selectMode,TEXT(“CP”)):编译是出错:
错误C2664:“int wcscmp(const wchar_t *,const wchar_t *)”:无法将参数1从“const char *”转换为“const wchar_t *”
需要类型转换:
_tcscmp((const wchar_t *)selectMode,TEXT(“CP”))
②getSamplePoints(valueSt,valueEn,approxEps,SamplePtArray,的ParamArray)函数:
void getSamplePoints(
double fromParam,
double toParam,
double approxEps,
AcGePoint2dArray& pointArray,
AcGeDoubleArray& paramArray
) const;
double fromParam:输入起始参数 |
double toParam:输入结束参数 |
double approxEps:弦高误差 |
AcGePoint2dArray&pointArray:从PARAM和topam之间的曲线上的点的输出阵列 |
AcGeDoubleArray与ParamArray参数:与点对点对应的参数的输出阵列 |
函数描述:返回从PARAM和topam之间的曲线上的点列表在点数组返回的任意两个连续点之间的线段,不会偏离曲线的约值。
③acedSSGet((ACHAR *)selectMode,NULL,NULL,rb,ssName)函数:
int acedSSGet(
const ACHAR * str,
const void * pt1,
const void * pt2,
const struct resbuf * filter,
ads_name ss
);
const ACHAR * str |
指定要使用的实体选择模式的可选字符串 |
const void * pt1 |
与某些选择模式相关的可选点;或者一个包含多边形或篱笆选择选项的多个点的结果缓冲列表;或者是两根字符串的数组,它们是一个:$模式选项的替换提示 |
const void * pt2 |
与某些选择模式相关的可选点 |
const struct resbuf * filter |
可选的结果缓冲列表,使acedSSGet()能够过滤绘图以选择某些类型的实体或具有某些属性 |
ads_name ss |
选择集的名称 |
函数描述:通过指定一个AutoCAD中的选择模式来返回一个选择集选择模式是通过提示的AutoCAD的用户或过滤绘图数据库来指定的。
④acedSSName(ssName,I,ENT)函数:
int acedSSName(
const ads_name ss,
int i,
ads_name entres
);
File
const ads_name ss |
包含实体的选择集 |
int i |
实体的零基指数位置;必须是非负的,并且不大于选择集中的最后一个实体的索引(acedSSLength(SS)-1) |
ads_name entres |
返回的实体名称 |
函数描述:返回一个实体的名称,由它在选择集中的位置指定。
⑤acdbOpenAcDbEntity(pEnt,objId,AcDb :: kForRead)函数:
inline Acad::ErrorStatus acdbOpenAcDbEntity(
AcDbEntity*& pEnt,
AcDbObjectId id,
AcDb::OpenMode mode,
bool openErasedEntity = false
);
AcDbEntity *&pEnt |
输出指针指向打开的对象 |
AcDbObjectId id |
要打开的对象的输入对象ID |
AcDb :: OpenMode模式 |
打开对象的输入模式 |
bool openErasedEntity = false |
输入布尔值表示打开一个被擦除的实体是否可以 |
函数描述:这个函数提供了一种方法来打开源自AcDbEntity(即拥有图形)的数据库驻留对象
(4)创建一个新的函数BuildRbFromPtArray,根据用于指定的一组点创建³³查询一个查询查询查询结果缓冲区链表
函数的实现:
static struct resbuf* BuildRbPtArray(const AcGePoint2dArray &arrPoints)
{
struct resbuf *retRb = NULL;
int count = arrPoints.length();
if (count <= 1)
{
acedAlert(TEXT("函数BuildRbFromPtArray中,点数组包含元素个数不足!"));
return retRb;
}
//使用第一个点来构建结果缓冲区链表的头结点
ads_point adsPt;
adsPt[X] = arrPoints[0].x;
adsPt[Y] = arrPoints[0].y;
retRb = acutBuildList(RTPOINT, adsPt, RTNONE);
struct resbuf *nextRb = retRb;//辅助指针
for (int i = 1; i < count; i++)//注意:不考虑第一个元素,因此i从1开始
{
adsPt[X] = arrPoints[i].x;
adsPt[Y] = arrPoints[i].y;
//动态创建新的节点,并将其链接到原来的链表尾部
acutBuildList(RTPOINT, adsPt, RTNONE);
nextRb = nextRb->rbnext;
}
return retRb;
}
acutBuildList(RTPOINT,adsPt,RTNONE)函数:
struct resbuf * acutBuildList(
int rtype,
ads_point adsPt
);
函数描述::通过分配结果缓冲区,从单个数据项中建立一个链表的结果缓冲区列表,分配由acutBuildList()参数指定的值,并将缓冲区链接在一起。
(5)注册一个新命令SelectEntPoly,提示用户选择一条多段线,在命令窗口中显示与多段线形成的区域相交的实体个数
函数的实现:
static void AAAMyGroupSelectEntInPoly() {
// Put your command code here
//提示用户选择多段线
ads_name entName;
ads_point pt;
if (acedEntSel(TEXT("\n选择多段线:"), entName, pt) != RTNORM)
return;
AcDbObjectId entId;
acdbGetObjectId(entId, entName);
//判断选择的实体是否是多段线
AcDbEntity *pEnt;
acdbOpenObject(pEnt, entId, AcDb::kForWrite);
if (pEnt->isKindOf(AcDbPolyline::desc()))
{
AcDbPolyline *pPoly = AcDbPolyline::cast(pEnt);
AcDbObjectIdArray ObjectIdArray;//选择到的实体ID集合
SelectEntInPoly(pPoly, ObjectIdArray, "CP", 1);
acutPrintf(TEXT("\n选择到%d个实体"), ObjectIdArray.length());
}
pEnt->close();
}
①acedEntSel(TEXT(“\ n选择多段线:”),entName,pt)函数:
int acedEntSel(
const ACHAR * str,
ads_name entres,
ads_point ptres
);
File
const ACHAR * str |
acedEntSel()在停顿之前显示的可选字符串;如果一个空指针时,则AutoCAD将显示选择对象默认提示符 |
ads_name entres |
所选实体名称 |
ads_point ptres |
用于选择实体名称的点 |
函数描述:提示用户通过指定一个点来选择一个实体对用户输入的暂停,并返回实体名称和用于选择实体的点。
②acdbOpenObject(pEnt,entId,AcDb :: kForWrite)函数:
template inline Acad::ErrorStatus acdbOpenObject(
T_OBJECT *& pObj,
AcDbObjectId id,
AcDb::OpenMode mode = AcDb::kForRead,
bool openErased = false
);
T_OBJECT *&pObj |
输出指针指向打开的对象 |
AcDbObjectId id |
要打开的对象的输入对象ID |
AcDb :: OpenMode mode = AcDb :: kForRead |
打开对象的输入模式 |
bool openErased = false |
输入布尔值指示是否可以打开一个擦除对象 |
函数描述:这个函数提供了一种方法来打开任何数据库电阻器对象,而不知道对象是否来自AcDbEntity这个函数在一个空指针中传递,pObj要打开的物体的对象ID是OBJID打开的模式是模式。开擦掉是一个布尔值,指示是否在被擦除时打开该对象。
三,使用选择集过滤器
(1)注册一个新命令过滤器1,创建一个带有通配符的过滤器
实现代码:
static void AAAMyGroupFilter1() {
// Put your command code here
struct resbuf *rb; //结果缓冲区链表
ads_name ssname;
rb = acutBuildList(RTDXF0, TEXT("TEXT"),//实体类型
8, TEXT("0,图层1"), //图层
1, TEXT("*cadhelp*"), // 包含的字符串
RTNONE);
//选择符合要求的文字
acedSSGet(TEXT("X"), NULL, NULL, rb, ssname);
long length;
acedSSLength(ssname, (Adesk::Int32 *)&length);
acutPrintf(TEXT("\n实体数:%d"), length);
acutRelRb(rb);
acedSSFree(ssname);
}
查看效果:
在CAD中创建一行当行文字“acadhelp”,输出1:
(2)注册一个新命令过滤器2,创建包含逻辑运算符的过滤器
实现代码:
static void AAAMyGroupFilter2() {
struct resbuf *rb; //结果缓冲区链表
ads_name ssname;
rb = acutBuildList(-4, TEXT("
(3)注册以一个命令Fiter3,创建包含关系运算符的过滤器
static void AAAMyGroupFilter3() {
struct resbuf *rb; //结果缓冲区链表
ads_name ssname;
//选择图形中半径大于或等于30的所有圆
rb = acutBuildList(RTDXF0, TEXT("CIRCLE"), //实体类型
-4, TEXT(">="), //关系运算符;组码-4指示过滤器列表中的关系运算符
40, 30, //半径;组码40用于指定圆的半径
RTNONE);
//选择符合要求的圆
acedSSGet(TEXT("X"), NULL, NULL, rb, ssname);
long length;
acedSSLength(ssname, (Adesk::Int32 *)&length);
acutPrintf
(TEXT("\n实体数:%d"), length);
acutRelRb(rb);
}
查看效果:
(4)注册以一个命令Fiter4,创建包含运算符和通配符的过滤器
static void AAAMyGroupFilter4() {
struct resbuf *rb; //结果缓冲区链表
ads_name ssname;
ads_point pt1 = { 0,0,0 };
ads_point pt2 = { 100,100,0 };
//选择图形中圆心在pt1和pt2两点构成的矩形内的圆
rb = acutBuildList(RTDXF0,TEXT("CIRCLE"),//实体类型
-4,TEXT(">,>,*"), //关系运算符和通配符
10,pt1, //圆心;组码10用于指定圆的圆心
-4,TEXT("<,<,*"), //关系运算符和通配符
10,pt2, //圆心
RTNONE);
acedSSGet(TEXT("X"), NULL, NULL, rb, ssname);
long length;
acedSSLength(ssname, (Adesk::Int32 *)&length);
acutPrintf
(TEXT("\n实体数:%d"), length);
acutRelRb(rb);
}
查看效果:
(5)注册以一个命令Fiter5,创建扩展数据的过滤器
static void AAAMyGroupFilter5() {
struct resbuf *rb; //结果缓冲区链表
ads_name ssname;
rb = acutBuildList(1001, TEXT("XData"), RTNONE);//扩展数据的应用程序名
acedSSGet(TEXT("X"), NULL, NULL, rb, ssname);
long length;
acedSSLength(ssname, (Adesk::Int32 *)&length);
acutPrintf
(TEXT("\n实体数:%d"), length);
acutRelRb(rb);
}
能够选择图形中所有包含“扩展数据”应用程序扩展数据的图元。
项目流程图:
<全文完>
项目SelectionSet的完整代码:
链接:https://pan.baidu.com/s/18XzWD3ukWcFHMdpELYagCQ密码:by0m
参考资料:
“AutoCAD ObjectARX(VC)开发基础与实例教程”