MarcelP102 Posted July 16, 2022 Share Posted July 16, 2022 (edited) 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 July 16, 2022 by MarcelP102 Quote Link to comment
Vectorworks, Inc Employee Julian_Carr Posted July 16, 2022 Vectorworks, Inc Employee Share Posted July 16, 2022 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); 1 Quote Link to comment
Jesse Cogswell Posted July 17, 2022 Share Posted July 17, 2022 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. 1 Quote Link to comment
MarcelP102 Posted July 18, 2022 Author Share Posted July 18, 2022 (edited) 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: Opening after moving Z 1mm Edited July 18, 2022 by MarcelP102 Quote Link to comment
Jesse Cogswell Posted July 18, 2022 Share Posted July 18, 2022 Try adding ResetObject(targetHd) before your SysBeep as well as a ReDrawAll. There is also a good chance that you need to do a reset on the parent wall, something to the tune of ResetObject(GetParent(targetHd)). 1 Quote Link to comment
MarcelP102 Posted July 18, 2022 Author Share Posted July 18, 2022 Yes, ResetObject(GetParent(targetHd)); does the trick. But only for the selected symbol. I Will extend the script to select all the symbol instance and do the ResetObject procedure. Will post it here when it's done. Quote Link to comment
MarcelP102 Posted July 18, 2022 Author Share Posted July 18, 2022 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); Quote Link to comment
Jesse Cogswell Posted July 18, 2022 Share Posted July 18, 2022 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. 2 Quote Link to comment
MarcelP102 Posted July 18, 2022 Author Share Posted July 18, 2022 (edited) That's true, but ResetObject alone is not enough. It needs to be ResetObject(GetParent(Hd)) to get the objects inside a wall to reset. The 'GetParent' made me choose to use a procedure to get it working. Edited July 18, 2022 by MarcelP102 Quote Link to comment
Jesse Cogswell Posted July 18, 2022 Share Posted July 18, 2022 Got it. The example you posted above just had the ResetObject in the ResetThem procedure. Quote Link to comment
Recommended Posts
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.