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)開發基礎與實例教程”

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM