ObjectARX(C++)-ADSRX和用户交互-选择集(SelectionSet)

一,选择集的创建和删除

(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 <class T_OBJECT> 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("<OR"),  //逻辑运算符开始
			RTDXF0, TEXT("TEXT"),            //一个条件
			RTDXF0, TEXT("MTEXT"),           //另一个条件
			-4, TEXT("OR<"),                  //逻辑运算符结束
			RTNONE);

		//选择符合要求的文字
		acedSSGet(TEXT("X"), NULL, NULL, rb, ssname);
		long length;
		acedSSLength(ssname, (Adesk::Int32 *)&length);
		acutPrintf(TEXT("\\n实体数:%d"), length);

		acutRelRb(rb);
		acedSSFree(ssname);
	}

(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)开发基础与实例教程”

 

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片