Jump to content
Developer Wiki and Function Reference Links ×

Help with script to automaticly create wall hole object


Recommended Posts

Hi all,

 

I'm trying to make a script that gets the 3D part of a symbol, duplicate it, convert to group and merge it to a solid so I can set it as a wall hole object. This will save a lot of time to setup the wall hole object.  I've reused a part of @MullinRJ script to convert all objects to groups. Then ungroup all the groups and merge all the solids. This works:

 

PROCEDURE CreateWallHole;

	function FlattenNestedSyms(H :Handle) :Boolean;
	Begin
		if ((GetTypeN(H) = 15) | ( GetTypeN(H) = 86)) then SymbolToGroup(H, 2);			
	End;		{ FlattenNestedSyms }


	function UngroupIt(H :Handle) :Boolean;
	Begin
		if (GetTypeN(H) = 11) then ungroup;
			
			
	End;		{ UngroupIt }
	

BEGIN
	ForEachObjectInLayer(FlattenNestedSyms, 3, 2, 0);	{ Vis & Selected, Deep, Active Layer }
	ForEachObjectInLayer(UngroupIt, 3, 2, 0);	{ Vis & Selected, Deep, Active Layer }
	DoMenuTextByName('Solid Operations',1);
	SysBeep;		{ Beep when done }
END;
Run(CreateWallHole);

 

 

But when extending the script to also set the newly created object as the wall hole object I'm hitting a wall. This script doesn't work:

 

PROCEDURE CreateWallHole;


VAR
objectHand, holeGroup :Handle;
objectName :STRING;

	function FlattenNestedSyms(H :Handle) :Boolean;
	Begin
		if ((GetTypeN(H) = 15) | ( GetTypeN(H) = 86)) then SymbolToGroup(H, 2);			
	End;		{ FlattenNestedSyms }


	function UngroupIt(H :Handle) :Boolean;
	Begin
		if (GetTypeN(H) = 11) then ungroup;
			
			
	End;		{ UngroupIt }
	

BEGIN
	objectHand := FActLayer;
	EditObjectSpecial(objectHand, 4);
	DoMenuTextByName('Select All', 0);	
	DoMenuTextByName('Copy', 0);
	DoMenuTextByName('Paste in Place', 0);
	ForEachObjectInLayer(FlattenNestedSyms, 3, 2, 0);	{ Vis & Selected, Deep, Active Layer }
	ForEachObjectInLayer(UngroupIt, 3, 2, 0);	{ Vis & Selected, Deep, Active Layer }
	DoMenuTextByName('Solid Operations',1);
	holeGroup := LNewObj;
	SetCustomObjectWallHoleGroup(objectHand, holeGroup);
	SysBeep;		{ Beep when done }
	
END;
Run(CreateWallHole);

 

 

Any help would be appreciated.

Edited by MarcelP102
Link to comment
  • Vectorworks, Inc Employee

I don't know if your approach will work using DoMenu calls, however you will need to get a handle to the symbol definition. Currently your handle is to a symbol instance. Get the name of the symbol instance then use GetObject() to get the definition handle.

 

One tip. Wall hole objects normally extend beyond the faces of the wall. If you get it working, consider scaling the wall hole object in the Y direction using:

 

SetObjectVariableReal(h3DHole, 632, rScaleFactor);

  • Like 1
Link to comment

I was able to make it work using the following script:

 

PROCEDURE Copy3DToWallHole;

CONST

	kSymbol = 15;
	
VAR

	targetHd,holeGroup:HANDLE;
	BSB:BOOLEAN;
	

FUNCTION Extract3DFromSymbol(sym:STRING) : HANDLE;

{Extracts Non-planar objects from given Symbol Definition and returns handle to 3D Solid}

	VAR

		h,tempHd:HANDLE;
		i,numSolids,result:INTEGER;
		solids:DYNARRAY[] OF HANDLE;

	BEGIN
		numSolids:=0;
		
		h:=FInGroup(GetObject(sym));
		WHILE(h<>NIL) DO
			BEGIN
				IF (GetObjectVariableBoolean(h,1160) = FALSE) THEN
					BEGIN
						numSolids:=numSolids+1;
						ALLOCATE solids[1..numSolids];
						solids[numSolids]:=h;
						
						h:=NextObj(h);
					END
				ELSE h:=NextObj(h);
			END;
		
		IF (numSolids > 0) THEN
			BEGIN
				tempHd:=HDuplicate(solids[1],0,0);
				FOR i:=2 TO numSolids DO
					BEGIN
						h:=HDuplicate(solids[i],0,0);
						result:=AddSolid(tempHd,h,tempHd);
					END;
			END;
		
		Extract3DFromSymbol:=tempHd;
	END;

BEGIN
	targetHd:=FSObject(ActLayer);
	
	IF (GetTypeN(targetHd) = kSymbol) THEN
		BEGIN
			holeGroup:=Extract3DFromSymbol(GetSymName(targetHd));
	
			BSB:=SetCustomObjectWallHoleGroup(targetHd,holeGroup);
		END;
END;

Run(Copy3DToWallHole);

 

In general, I try to avoid using DoMenuTextByName whenever possible, reserving it only for things that don't have a Vectorscript equivalent or for launching one of my custom commands.  The script uses FSObject to get a handle to the first selected object on the active layer.  If that object is a symbol, it then uses GetSymName to get the name of the symbol definition.

 

The Extract3DFromSymbol function uses FInGroup on the symbol definition itself and builds an array of handles to objects not on the screen plane using a WHILE loop.  Even though VW2022 deprecates the screen plane, it is still used in the background to determine which objects are part of the 2D or 3D components of a symbol.  If it's on the screen plane, it's part of the 2D component.  A FOR loop then goes through that array and duplicates the objects using HDuplicate, then AddSolid to add them to the final solid.

 

Once that process is complete, it uses SetCustomObjectWallHoleGroup on the originally selected symbol.  There seems to be a bug in that the SetCustomObjectWallHoleGroup will not work when a symbol definition is passed in, but will if a symbol on the drawing is passed in, and the symbol definition will be updated accordingly.

 

I tested this with fairly simple objects, if the 3D components are fairly complex, you might try adding in a CnvrtToGenericSolid command in to remove the nesting history from the Solid Addition.

 

The SymbolToGroup command is a nifty trick but also something you have to keep an eye on.  If it's run on a hybrid symbol, it will only act on the visible geometry at the time the command is run (will only convert the 2D geometry in Top/Plan view, or the 3D geometry when in a 3D view).  Stepping through the individual objects of a symbol is usually the better bet since it will work regardless of which view you're in.

  • Like 1
Link to comment

Hi @Jesse Cogswell

 

Thanks for the script and explaining the workflow. I've modified the script so it also works on symbols within symbols, and symbols hosted in walls. Only thing missing is to force a 'reset' of each object hosted in a wall. Normally when you edit the wall hole manual all the placed instances of the symbol gets updated. But when using the script the instance remain using the old wall hole object until you do a modification like moving the Z or similar. I've tried to  use ResetObject command, but that didn't seems to do anything..

 

Modified script below

PROCEDURE Copy3DToWallHole;

CONST

	kSymbol = 15;
	kWall = 68;
	
VAR

	targetHd,H2,holeGroup:HANDLE;
	BSB:BOOLEAN;
	

FUNCTION Extract3DFromSymbol(sym:STRING) : HANDLE;

{Extracts Non-planar objects from given Symbol Definition and returns handle to 3D Solid}

	VAR

		h,tempHd:HANDLE;
		i,numSolids,result:INTEGER;
		solids:DYNARRAY[] OF HANDLE;

	BEGIN
		numSolids:=0;
		
		h:=FInGroup(GetObject(sym));
		WHILE(h<>NIL) DO
			BEGIN
				IF (GetObjectVariableBoolean(h,1160) = FALSE) THEN
					BEGIN
						numSolids:=numSolids+1;
						ALLOCATE solids[1..numSolids];
						solids[numSolids]:=h;
						
						h:=NextObj(h);
					END
				ELSE h:=NextObj(h);
			END;
		
		IF (numSolids > 0) THEN
			BEGIN
				tempHd:=HDuplicate(solids[1],0,0);
				FOR i:=2 TO numSolids DO
					BEGIN
						h:=HDuplicate(solids[i],0,0);
						result:=AddSolid(tempHd,h,tempHd);
					END;
			END;
		
		Extract3DFromSymbol:=tempHd;
	END;

BEGIN
	Locus(0, 0);				{ drop a bread crumb }
	targetHd := PrevSObj(LNewObj);	{ use PrevSObj or PrevObj, as required }
	DelObject(LNewObj);			{ remove bread crumb }


	{ script to select symbol in wall }	
	IF (GetTypeN(targetHd) = kWall) THEN
		BEGIN
			H2:=Fin3D(targetHd);
				WHILE H2 <> NIL DO
					BEGIN
						IF Selected(H2) & (GetTypeN(H2) = kSymbol) THEN 
						targetHd := H2;
						H2:=NextObj(H2);
					END; {while}
		END;

	{ if symbol is selected generate wall hole }		
	IF (GetTypeN(targetHd) = kSymbol) THEN
		BEGIN
			holeGroup:=Extract3DFromSymbol(GetSymName(targetHd));
	
			BSB:=SetCustomObjectWallHoleGroup(targetHd,holeGroup);


			SysBeep;		{ Beep when done }
		END;
END;

Run(Copy3DToWallHole);

 

Opening after using script:
image.png.9cc003146399706cc949c0bbaf7c4e2a.png

 

Opening after moving Z 1mm

image.png.60ba0429bd4fc4d92466cc1459c8c5a6.png

Edited by MarcelP102
Link to comment

This seems to work. Thanks again for the help!

 

PROCEDURE Copy3DToWallHole;

{*	Generate wall hole by 3D objects of a symbol
	Developed by: Jesse Cogswell
	Date: 17/7/2022
	VW Version: 27 (VW2022)
	Revisions:	17/7/2022	Added option to select symbols within symbols and symbols in wall - By Marcel Plomp
				18/7/2022	Added reset of already placed instance of symbols in wall so the opening updates - By Marcel Plomp
*}

CONST

	kSymbol = 15;
	kWall = 68;
	
VAR

	targetHd,H2,holeGroup	:HANDLE;
	BSB						:BOOLEAN;
	criteria 				:STRING;

PROCEDURE ResetThem(Hd:HANDLE);
BEGIN
	ResetObject(GetParent(Hd));
END;	

FUNCTION Extract3DFromSymbol(sym:STRING) : HANDLE;

{Extracts Non-planar objects from given Symbol Definition and returns handle to 3D Solid}

	VAR

		h,tempHd:HANDLE;
		i,numSolids,result:INTEGER;
		solids:DYNARRAY[] OF HANDLE;

	BEGIN
		numSolids:=0;
		
		h:=FInGroup(GetObject(sym));
		WHILE(h<>NIL) DO
			BEGIN
				IF (GetObjectVariableBoolean(h,1160) = FALSE) THEN
					BEGIN
						numSolids:=numSolids+1;
						ALLOCATE solids[1..numSolids];
						solids[numSolids]:=h;
						
						h:=NextObj(h);
					END
				ELSE h:=NextObj(h);
			END;
		
		IF (numSolids > 0) THEN
			BEGIN
				tempHd:=HDuplicate(solids[1],0,0);
				FOR i:=2 TO numSolids DO
					BEGIN
						h:=HDuplicate(solids[i],0,0);
						result:=AddSolid(tempHd,h,tempHd);
					END;
			END;
		
		Extract3DFromSymbol:=tempHd;
	END;



BEGIN
	Locus(0, 0);					{ drop a bread crumb }
	targetHd := PrevSObj(LNewObj);	{ use PrevSObj or PrevObj, as required }
	DelObject(LNewObj);				{ remove bread crumb }

	{ script to select symbol in wall }	
	IF (GetTypeN(targetHd) = kWall) THEN
		BEGIN
			H2:=Fin3D(targetHd);
				WHILE H2 <> NIL DO
					BEGIN
						IF Selected(H2) & (GetTypeN(H2) = kSymbol) THEN 
						targetHd := H2;
						H2:=NextObj(H2);
					END; {while}
		END;

	{ if symbol is selected generate wall hole and refresh symbol }		
	IF (GetTypeN(targetHd) = kSymbol) THEN
		BEGIN
			{ create new wall hole }
			holeGroup:=Extract3DFromSymbol(GetSymName(targetHd));
			BSB:=SetCustomObjectWallHoleGroup(targetHd,holeGroup);
			
			{ reset instances of symbol }
			criteria := CONCAT('(InSymbol & (S=''', GetSymName(targetHd), '''))');
			ForEachObject(ResetThem, criteria);
			ReDrawAll;
			
			{ Beep when done }
			SysBeep;		
		END;
END;


Run(Copy3DToWallHole);

 

 

Link to comment

For the reset, you can save yourself a little bit of space by having the ForEachObject call use ResetObject directly, rather than a new procedure, as in ForEachObject(ResetObject, criteria);.  Any procedure that is looking for a single handle can be used in this way, you would only need a separate procedure if you needed to do further work on them.

  • Like 2
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...