BIM Coordinator Program (INT) April 22, 2024

Find the next step in your career as a Graphisoft Certified BIM Coordinator!

Archicad C++ API
About Archicad add-on development using the C++ API.

Creating a selection box using the API.

Anonymous
Not applicable
Hi everyone !

Here I come again, hoping I could get some help on something I've put a lot of time in without much success.

My question is the following : Is there any way to create a selection box using the API and then be able to collect elements within or intersecting with the selection ?

To be more precise, I'd like to create an "invisible" selection box that would not be displayed but would still select the elements within.

I've searched thoroughly in the documentation as well as in this forum's topics...in vain, at least for now.

Any help would be greatly appreciated. Feel free to ask for more precision concerning my question. I thank you all in advance.


Kindly,

Siwar
6 REPLIES 6
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Hi Siwar!

You can create a marquee using the API and select the elements within. After the operation you can remove that. I know it's not exactly what you want but it works almost the same.

Here is a short example code for you:
 
	GSErrCode            err; 
	API_SelectionInfo    selectionInfo; 
	API_Element          tElem; 
	API_Neig             **selNeigs; 
 
	// set the corners of the selection box 
	selectionInfo.marquee.box.xMin = 0.0; 
	selectionInfo.marquee.box.xMax = 10.0; 
	selectionInfo.marquee.box.yMin = 0.0; 
	selectionInfo.marquee.box.yMax = 10.0; 
	selectionInfo.typeID = API_MarqueeHorBox; 
 
	ACAPI_Selection_SetMarquee (&selectionInfo); 
 
	// collect elements 
	err = ACAPI_Selection_Get (&selectionInfo, &selNeigs, false); 
	BMKillHandle ((GSHandle *) &selectionInfo.marquee.coords); 
	if (err == APIERR_NOSEL) 
		err = NoError; 
 
	if (err != NoError) { 
		BMKillHandle ((GSHandle *) &selNeigs); 
		return; 
	} 
 
	if (selectionInfo.typeID != API_SelEmpty) { 
		UInt32    ii, nSel; 
 
		nSel = BMGetHandleSize ((GSHandle) selNeigs) / sizeof (API_Neig); 
		for (ii = 0; ii < nSel && err == NoError; ii++) { 
			tElem.header.typeID = Neig_To_ElemID ((*selNeigs)[ii].neigID); 
			// filter elements 
			if (tElem.header.typeID == API_DimensionID) 
				continue; 
 
			tElem.header.guid = (*selNeigs)[ii].guid; 
			tElem.header.index = 0; 
			if (ACAPI_Element_Get (&tElem) != NoError) 
				continue; 
 
			// now tElem is one of the elements in the created marquee box 
		} 
	} 
	BMKillHandle ((GSHandle *) &selNeigs); 
 
	// remove selection box 
	selectionInfo.typeID = API_SelEmpty; 
	ACAPI_Selection_SetMarquee (&selectionInfo); 
Regards,
Tibi
Anonymous
Not applicable
Tibi, thank you for your help, though I can't say that it turned out to be successful.

The selection marquee did show up on the screen with proprer dimensions, but it didn't select anything.
I thought the nSel variable would count all the elements inside the marquee, but all it did was counting the elements that I was selecting by myself.

Do you have any examples of this code in action ?

Thank you again for your response.

Siwar
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Oh, finally I understood what you need.
Only few more lines needed to attach to my previous example code to success

So I hope it's just what you want, I highlighted the important new lines:
 
	GSErrCode            err; 
	API_SelectionInfo    selectionInfo; 
	API_Neig             **selNeigs; 
 
	// set the corners of the selection box 
	selectionInfo.marquee.box.xMin = 0.0; 
	selectionInfo.marquee.box.xMax = 10.0; 
	selectionInfo.marquee.box.yMin = 0.0; 
	selectionInfo.marquee.box.yMax = 10.0; 
	selectionInfo.typeID = API_MarqueeHorBox; 
 
	ACAPI_Selection_SetMarquee (&selectionInfo); 
 
	// collect elements 
	err = ACAPI_Selection_Get (&selectionInfo, &selNeigs, false); 
	BMKillHandle ((GSHandle *) &selectionInfo.marquee.coords); 
	if (err == APIERR_NOSEL) 
		err = NoError; 
 
	if (err != NoError) { 
		BMKillHandle ((GSHandle *) &selNeigs); 
		return; 
	} 
 
	if (selectionInfo.typeID != API_SelEmpty) { 
		// you can deselect all before the operation like this  
		err = ACAPI_Element_Select (NULL, 0, false); 
		if (err != NoError) 
			ACAPI_WriteReport ("ACAPI_Element_Select deselect all error", true); 
		
// ------------------------------------------------------------------------------------------ 
		UInt32   nSel = BMGetHandleSize ((GSHandle) selNeigs) / sizeof (API_Neig); 
 
		// select the elements within the marquee 
		ACAPI_Element_Select (selNeigs, nSel, true); 
		if (err != NoError) 
			ACAPI_WriteReport ("ACAPI_Element_Select select all error", true);
// ------------------------------------------------------------------------------------------ 
	} 
	BMKillHandle ((GSHandle *) &selNeigs); 
 
	// remove selection box 
	selectionInfo.typeID = API_SelEmpty; 
	ACAPI_Selection_SetMarquee (&selectionInfo); 
Anonymous
Not applicable
Hi !

This time, it worked ! Thank you for your help.

As an auxiliary question, is there any way to be able to modify the selection marquee to have something more complex, not just limited by 2 coordinates (like a polyline or so) ? Indeed, that would make the selection process much more precise and reliable, allowing to get the selection within 4 coordinates instead of 2.
I've seen in the documentation that the SetMarquee fonction can be based on a polyline rather than a fixed box, but I was not able to make it work.
Do you have any clue about such method ?

Kindly,

Siwar
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Hi Siwar!

Here's a short example code for that method:
	GSErrCode           err; 
	API_Neig            **selNeigs; 
	API_SelectionInfo   selectionInfo; 
	Int32				nCoords = 6; // first and last coordinates are the same, so this will be only a pentagon 
 
	selectionInfo.typeID		    = API_MarqueePoly; 
	selectionInfo.marquee.nCoords = nCoords; 
 
	// Allocate memory and set the coordinates 
	API_Coord* coords = reinterpret_cast<API_Coord*> (BMAllocatePtr (nCoords * sizeof (API_Coord), ALLOCATE_CLEAR, 0));; 
	if (coords != NULL) { 
		coords[0].x =  2.0; coords[0].y = 0.0; 
		coords[1].x =  5.0; coords[1].y = 3.0; 
		coords[2].x =  0.0; coords[2].y = 5.0; 
		coords[3].x = -5.0; coords[3].y = 3.0; 
		coords[4].x = -2.0; coords[4].y = 0.0; 
		coords[5].x =  2.0; coords[5].y = 0.0; 
		selectionInfo.marquee.coords = &coords; 
 
		ACAPI_Selection_SetMarquee (&selectionInfo); 
		// Releasing allocated memory 
		BMKillPtr ((GSPtr *) &coords); 
 
		// collect elements within the pentagon 
		err = ACAPI_Selection_Get (&selectionInfo, &selNeigs, false); 
		BMKillHandle ((GSHandle *) &selectionInfo.marquee.coords); 
		if (err == APIERR_NOSEL) 
			err = NoError; 
 
		if (err != NoError) { 
			BMKillHandle ((GSHandle *) &selNeigs); 
			return; 
		} 
 
		// ... 
		// ... 
	} 


I hope this helps again

Best,
Tibi
Anonymous
Not applicable
Once again, I'd like to thank you for your dedication in trying to help me; I really appreciate it.

Right now I've put my project in pause and may continue it in the future. Anyway, I'll keep the thread up to date as soon as I lay my hands again on the code (if that ever happens).

Thank you again,

Siwar
Learn and get certified!