Jump to content

Jesse Cogswell

Member
  • Posts

    628
  • Joined

  • Last visited

Posts posted by Jesse Cogswell

  1. When you set your criteria to search, you can uncheck all of the Search Within checkboxes.  Though they deprecated the Screen Plane, Vectorworks still uses it on hybrid objects to determine 2D vs 3D components.  If you have a 2D or hybrid symbol, all of the 2D geometry will be on the screen plane by default, and this is set when the symbol is made.  So, if you are building a hybrid symbol containing both 3D and planar geometry, when you create the symbol, Vectorworks will automatically assign any planar geometry on the Layer Plane (IE, not on the 3D Plane) to the Screen Plane to separate the 2D and 3D geometry for the hybrid symbol.  Likewise for plug-in objects that have hybrid geometry, all of the 2D is technically on the Screen Plane.  Viewport annotations (and anything placed on a Sheet Layer) are also automatically placed on the Screen Plane behind the scenes.  What I'm getting at is that objects within all of those things currently on the Screen Plane must be on the Screen Plane and are not counted when assessing whether you can disable the Legacy 2D Features.

     

    One thing that you will want to make sure of is that you have all classes and layers visible and have both your Class and Layer View settings set to Show/Snap/Modify.  The criteria box will count all eligible objects across the drawing, but the selection will only result in what you can currently select based on the current visibility and selection settings.  This will also affect objects inside of groups, since the objects will be counted but not necessarily selected (kind of, they will be selected but you wouldn't see that until you enter the group edit container).

     

    There is a fairly simple script that you can run that will migrate all offending objects to the Layer Plane and thus allow you to disable Legacy 2D Features.  Note that this script does not come with a warranty, and while it shouldn't bork anything in your drawing, I take no responsibility if it does.

     

    PROCEDURE SwitchToLayerPlane;
    
    PROCEDURE Execute(h:HANDLE);
    
    	CONST
    	
    		kIsScreen = 1160;
    
    	BEGIN
    		SetObjectVariableBoolean(h,kIsScreen,FALSE);
    	END;
    
    BEGIN
    	ForEachObject(Execute,(PLA='Screen Plane'));
    END;
    
    Run(SwitchToLayerPlane);

     

    To run this script, go to your Resource Manager, right-click and select New Resource - Script.  This will ask you to select or name a "Script Palette" (something like "Drawing Macros" will work just fine, don't select Saved Views if it shows up in the dialog box), then name the script whatever you want ("Migrate All to Layer Plane" would be a fine name).  It will now open a notepad for your script.  Make sure the Language is set to Vectorscript, and copy and paste the above code in.  It should look like this:

     

    image.thumb.png.2597be54c8e7515b52f3329de333308c.png

     

    Click the OK button to close the notepad.  This will generate a floating palette with your new script in it.  Double click the script to run it.  This should move anything on the screen plane over to the layer plane excluding the objects inside symbols, plug-in objects, viewport annotations, and on sheet layers.  After running it, you should be able to disable the Legacy 2D Features.  I have seen some weirdness in very old drawings where some objects are more or less "stuck" on the screen plane and it won't let you disable the features, but it should work for most up to date drawings.

    • Like 2
  2. Assuming that you are using a Viewport on a Sheet Layer, you should look into the Detail Level parameter in the Viewport's Object Info Palette.  When you are editing a symbol, the Object Info Palette for each object will have a field called Display at Detail Level, with checkboxes for Low, Medium, and High.  What you would need to do is to find the details that are lost at that size and uncheck the Low checkbox in the OIP or build simplified geometry and make sure that only Low is selected.  Then, you'd set your Viewport to Low Detail.

     

    I can understand that this may be very tedious at this point, as you seem to have a finished model, so it would mean editing a good number of symbols.  But, this can be a very valuable thing to incorporate in your workflow if you find yourself working with big and complicated models.  There's even an option in Document Settings called Auto display detail levels for design layers that will set the viewed detail level of design layers based on the scale settings.

     

    EDIT: I see in your signature that you are using VW Fundamentals.  I'm not sure if this functionality is in Fundamentals, as Fundamentals is very stripped down compared to the other Vectorworks modules.

    • Like 1
  3. For those playing along at home or searching for this solution somewhere down the line, the test code above changed to use the Rpstr_GetValueStr and Rpstr_SetValueStr implementation that will survive encryption is below:

     

    PROCEDURE TestPythonEncrypt;
    
    VAR
    
    	testString:DYNARRAY[] OF CHAR;
    	BSB:BOOLEAN;
    
    BEGIN
    	testString:='Vectorscript';
    	Rpstr_SetValueStr('testString',testString);
    	
    	AlrtDialog(Concat('Test: ',Rpstr_GetValueStr('testString','')));
    	
    	PythonBeginContext;
    		PythonExecute('import vs');
    		PythonExecute('vs.AlrtDialog("Python is executing")');
    		PythonExecute('vs.AlrtDialog(vs.Rpstr_GetValueStr("testString",""))');
    		PythonExecute('vs.Rpstr_SetValueStr("testString","Python")');
    		PythonExecute('vs.AlrtDialog(vs.Rpstr_GetValueStr("testString",""))');
    	PythonEndContext;
    	
    	AlrtDialog(Concat('Test: ',Rpstr_GetValueStr('testString','')));
    	
    	BSB:=Rpstr_RemoveValue('testString');
    END;
    
    Run(TestPythonEncrypt);

     

    • Like 1
  4. I'm afraid that I already know the answer to this one, but I would most definitely love to be proven wrong.  I have a Vectorscript plug-in that uses PythonExecute and vs.GetVSVar and vs.SetVSVar to get a file's Last Modified date using the pathlib Python library.  This all works beautifully until I encrypt the plug-in, in which case it breaks and crashes Vectorworks.

     

    The issue seems to stem from vs.GetVSVar not working correctly and returning a "NoneType" object rather than a DYNARRAY[] OF CHAR of the supplied variable.  I did some tests and it seems that vs.GetVSVar and vs.SetVSVar do not work within encrypted plug-ins.  I'm guessing that the variable names are encrypted but not in the same way that strings are, so when you pass in the variable name in a string, it no longer matches with the actual variable name so it returns NIL.

     

    Here's my test code:

    PROCEDURE TestPythonEncrypt;
    
    VAR
    
    	testString:DYNARRAY[] OF CHAR;
    
    BEGIN
    	testString:='Vectorscript';
    	AlrtDialog(Concat('Test: ',testString));
    	
    	PythonBeginContext;
    		PythonExecute('import vs');
    		PythonExecute('vs.AlrtDialog("Python is executing")');
    		PythonExecute('vs.AlrtDialog(vs.GetVSVar("testString"))');
    		PythonExecute('vs.SetVSVar("testString","Python")');
    		PythonExecute('vs.AlrtDialog(vs.GetVSVar("testString"))');
    	PythonEndContext;
    	
    	AlrtDialog(Concat('Test: ',testString));
    END;
    
    Run(TestPythonEncrypt);

     

    It should return:

    1. Test: Vectorscript
    2. Python is executing
    3. Vectorscript
    4. Python
    5. Test: Python

     

    After encryption, it instead returns:

    1. Test: Vectorscript
    2. Python is executing
    3. Test: Vectorscript

     

    This tells me that Python is working, but not the vs.GetVSVar or vs.SetVSVar functions.

     

    The whole point of the plug-in is lost without being able to pull a file's Last Modified date, and I'd rather not send out an unencrypted plug-in.  Any ideas on a solution?

  5. In my mind, there are two:

    1. There is some direct integration with fixture modes in Vectorworks, letting you bypass some of the current awkwardness with GDTF fixtures.  The Fixture Mode parameter in the Lighting Device OIP will automatically be exported into the matching Vision profile.  But this only works if there is a Vision profile (otherwise you have to wait for one) and that the Vision profile is correct (not always guaranteed).
    2. Vision is much cheaper than L8.

    But that's about it.  L8 has far more features (better laser control, water features and pyro, animated models, better handling for reflection), and the final output is much more polished.  I went in on a full Vision license in 2017 right after Vectorworks bought them because of the relative ease of getting a model from Vectorworks to Vision and with the hope that having Vectorworks behind them, we'd see some meaningful upgrades.  Unfortunately, the software has mostly stayed the same with only minor changes from year to year (and some features removed because they never worked.  The "Attachments" feature that would handle things like color scrollers and remote dimmers was entirely broken between 2017 and 2019, before being completely removed in 2020.  Bad luck if you ever need to do scrollers or play with lens options in your VL500s, there's no way to do it).  And now with the ubiquity of the MVR file format and GDTF fixture profiles, the one major advantage that Vision had is largely moot.

     

    I bought Vision primarily to do some moving light preprogramming for mostly theatre, dance, and opera and with both ETC and MA consoles having a built in solution where you have XYZ control of fixture focus, I barely ever use Vision anymore.  If I were in the market for a previs software for concert and event work, I would most definitely look into L8, Capture, or Depence2 before considering Vision.  Hell, I'd probably even look at WYSIWYG if you need something more budget conscious.  Or you could look into Carbon for Unreal, which looks pretty promising and is very affordable.

    • Like 2
  6. The quick and dirty way to do it would be to go to Tools - Custom Selection, select Execute Immediately then select All Objects as the criteria.  There will be a counter at the bottom of the dialog box that shows the number of objects.  You can check the Search Within Symbols, Plug-in Objects, and/or Viewport Annotations depending on what kind of count you are looking for.

     

    image.png.934f7cd3f412a3bdabaf0a3c42cf21a6.png

     

    If you want something a little faster, you can create a script (Vectorscript format) with something like:

    AlrtDialog(Concat(Count(ALL),' objects in drawing'));

    for all objects on layers, but not going inside of symbols (each symbol would count as one object), plug-in objects, or viewport annotations.

     

    AlrtDialog(Concat(Count(INSYMBOL & (ALL)),' objects in drawing and symbols'));

    would count all objects on layers and inside symbols (each component of the symbol would be counted separately and in addition to the symbol, so a drawing consisting of 2 symbols, each with 2 rectangles would come to a count of 6 objects).

     

    For a full count of every single object, inside symbols, plug-ins, and annotations, the script would look like this:

    AlrtDialog(Concat(INSYMBOL & INOBJECT & INVIEWPORT & (ALL)),' total objects in drawing'));

     

    EDIT: I realize now that you weren't specific about what kind of objects you are trying to count.  I usually use the Custom Selection method outlined above, just setting the criteria to match exactly what I'm trying to count and then reading the value at the bottom of the dialog box.  Another method would be to make a Worksheet with a cell with the =COUNT() function and inserting the criteria within the parentheses.

    • Like 1
  7. Unfortunately, that password lock is a byproduct of the student version watermark and, as far as I have been able to find, not possible to bypass without getting the watermark removed.  It's one of the reasons I shudder when I get scenic or exhibit drawings with the student watermark, as it's much harder to later incorporate my drawings into the larger PDF packets that I need to generate.

     

    One way to kind of get around it is to use the Print function and to print to PDF using the Microsoft or Adobe rather than doing the traditional Export PDF or Publish functions.  You'll lose some functionality (no way to do PDF layers, automatic hyperlinks will be broken, may lose ability to import PDF to Illustrator and retain vectors, etc), and you'll have to do this separately for each sheet and merge them in a PDF later, but at least it won't be password protected.

  8. This is most likely related to a setting found under File - Document Settings - Spotlight Preferences.  In the Classes and Color tab, there are a couple of things to check out.  There is a section to determine what class the fixture should be in, with options for:

    1. Currently active class
    2. Symbol Definition class
    3. Automatic assignment using a class set in this dialog or tracked by a Lighting Device Field

    My guess is that you have something set not to your liking here.

     

    There are also options to change the visual attributes of the Lighting Device based on the color field.  This also might be causing you some grief.

     

    image.png.59f98f6bb65881900f464933daa4e9b0.png

     

    This dialog may be slightly different depending on which version of Vectorworks you are using, but will generally have the same options if you're on VW2019 or newer.

    • Like 1
  9. 15 hours ago, MullinRJ said:

    God bless Carlotta !!!  😇

    Hear, hear!

     

    All of the old Vectorlab documentation is invaluable, and I've been using her Rosetta Stone article to help understand Python implementation in Vectorworks.  I do wish it was easier to find her page on the wiki, though.  I finally ended up making a bookmark so I could quickly get back to it without having to do a deep search of the wiki or track down one of her forum posts where she linked to one of the articles.

  10. Selector 7013 did the trick, and I certainly do like it.  Selector 6755 sounds interesting.  I usually drop in a Waldo locus and do GetParent to figure out where I am, but I think the inconsistency you've stumbled on is fascinating.

     

    Thank you for finding the answer.  Just as a curiosity, what documentation did you find the answer in?  The SDK?

  11. I discovered a small bug with my code pasted above, once you selected a class to transform into, you weren't able to set it back to "No transformation".  That is corrected below:

    PROCEDURE ClassTransformer;
    
    {*	Converts objects in one class to a different class with options to delete original class
    	Developed by: Jesse Cogswell
    	VW Version: 24 (VW2019)
    	Date: 11/5/2023
    	Revisions:
    *}
    
    VAR
    
    	dialog,layoutDialog:LONGINT;
    	classArray:DYNARRAY[] OF STRING;
    
    PROCEDURE BuildClassArray;
    
    {Builds array of class names and sorts alphabetically}
    
    	VAR
    	
    		i:INTEGER;
    	
    	BEGIN
    		ALLOCATE classArray[1..ClassNum];
    		
    		FOR i:=1 TO ClassNum DO classArray[i]:=ClassList(i);
    		
    		SortArray(classArray,ClassNum,0);
    	END;
    	
    
    PROCEDURE TransformClass(origClass,targetClass:STRING; deleteClass:BOOLEAN);
    
    {Converts objects in origClass to given targetClass, deletes origClass if deleteClass is TRUE}
    
    	CONST
    	
    		kSymbolDef = 16;
    		kNone = 'None';
    		kDim = 'Dimension';
    
    	VAR
    	
    		resList,numItems:LONGINT;
    		i,insertMode,breakMode:INTEGER;
    		currentClass:STRING;
    
    		PROCEDURE TransformObjectClass(h:HANDLE);
    		
    		{Converts class of given object to targetClass}
    		
    			BEGIN
    				SetClass(h,targetClass);
    			END;
    			
    	BEGIN
    		ForEachObject(TransformObjectClass,(INSYMBOL & INOBJECT & INVIEWPORT & (C = origClass)));
    		
    		resList:=BuildResourceList(kSymbolDef,0,'',numItems);
    		FOR i:=1 TO numItems DO
    			BEGIN
    				GetSymbolOptionsN(GetNameFromResourceList(resList,i),insertMode,breakMode,currentClass);
    				IF(currentClass = origClass) THEN SetSymbolOptionsN(GetNameFromResourceList(resList,i),insertMode,breakMode,targetClass);
    			END;
    		
    		IF((deleteClass) & (origClass <> kNone) & (origClass <> kDim)) THEN DelClass(origClass);
    	END;
    	
    FUNCTION DrawDialog(DName:STRING) : LONGINT;
    
    {Creates dialog box}
    
    	CONST
    	
    		kLBWidth = 100;
    		kLBHeight = 50;
    		kListBoxWidth = 50;
    
    	VAR
    	
    		dia:LONGINT;
    	
    	BEGIN
    		dia:=CreateLayout(DName,FALSE,'OK','Cancel');
    		
    		CreateLB(dia,11,kLBWidth,kLBHeight);
    		
    		CreateListBoxN(dia,21,kListBoxWidth,kLBHeight,FALSE);
    		
    		CreateGroupBox(dia,101,'Transform From',TRUE);
    		CreateGroupBox(dia,102,'Transform To',TRUE);
    		
    		SetFirstGroupItem(dia,101,11);
    		SetFirstGroupItem(dia,102,21);
    		
    		SetFirstLayoutItem(dia,101);
    		SetRightItem(dia,101,102,0,0);
    		
    		DrawDialog:=dia;
    	END;
    
    PROCEDURE DialogHandler(VAR item,data:LONGINT);
    
    {Handles dialog interaction}
    
    	CONST
    	
    		kColWidth = 238;
    		kColor = 255 * 0.6;
    
    	VAR
    	
    		i,colNum,kImageCheck,kImageBlank,BSI,choiceCounter,selRow,textOpacity:INTEGER;
    		origClass,targetClass,BSS:STRING;
    		deleteClass,BSB:BOOLEAN;
    
    	BEGIN
    		CASE item OF
    			SetupDialogC:
    				BEGIN
    					kImageCheck:=AddListBrowserImage(dialog,11,'Vectorworks/Standard Images/Checkmark.png');
    					kImageBlank:=AddListBrowserImage(dialog,11,'Vectorworks/Standard Images/Blank.png');
    					
    					colNum:=InsertLBColumn(dialog,11,0,'Select',50);
    					BSB:=SetLBControlType(dialog,11,colNum,5);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,1);
    					
    					colNum:=InsertLBColumn(dialog,11,1,'Class',kColWidth);
    					BSB:=SetLBControlType(dialog,11,colNum,1);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,0);
    					
    					colNum:=InsertLBColumn(dialog,11,2,'Transform Class',kColWidth);
    					BSB:=SetLBControlType(dialog,11,colNum,1);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,0);
    					
    					colNum:=InsertLBColumn(dialog,11,3,'Delete',50);
    					BSB:=SetLBControlType(dialog,11,colNum,3);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,1);
    					
    					colNum:=InsertLBColumn(dialog,11,4,'Eligible',0);
    					BSB:=SetLBControlType(dialog,11,colNum,5);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,0);
    					
    					colNum:=InsertLBColumnDataItem(dialog,11,0,'Checked',kImageCheck,-1,0);
    					colNum:=InsertLBColumnDataItem(dialog,11,0,'Unchecked',kImageBlank,-1,0);
    					
    					colNum:=InsertLBColumnDataItem(dialog,11,3,'Checked',kImageCheck,-1,0);
    					colNum:=InsertLBColumnDataItem(dialog,11,3,'Unchecked',kImageBlank,-1,0);
    					
    					AddChoice(dialog,21,'No Transformation',0);
    					
    					BuildClassArray;
    					FOR i:=1 TO ClassNum DO
    						BEGIN
    							colNum:=InsertLBColumnDataItem(dialog,11,1,classArray[i],0,0,0);
    							BSI:=InsertLBItem(dialog,11,colNum,classArray[i]);
    							BSB:=SetLBItemUsingColumnDataItem(dialog,11,colNum,1,colNum);
    							
    							AddChoice(dialog,21,classArray[i],i);
    						END;
    					
    					EnableLBColumnLines(dialog,11,TRUE);
    					BSB:=EnableLBSingleLineSelection(dialog,11,TRUE);
    				END;
    			1: {OK}
    				BEGIN
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO
    						BEGIN
    							BSB:=GetLBItemInfo(dialog,11,i,0,BSS,BSI);
    							IF(BSI = kImageCheck) THEN
    								BEGIN
    									BSB:=GetLBItemInfo(dialog,11,i,1,origClass,BSI);
    									BSB:=GetLBItemInfo(dialog,11,i,2,targetClass,BSI);
    									BSB:=GetLBItemInfo(dialog,11,i,3,BSS,BSI);
    									deleteClass:=(BSI = kImageCheck);
    									
    									TransformClass(origClass,targetClass,deleteClass);
    								END;
    						END;
    					
    					ReDrawAll;
    				END;
    			2: {Cancel}
    				BEGIN
    				END;
    			11: {List Browser}
    				BEGIN
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO IF(IsLBItemSelected(dialog,11,i)) THEN selRow:=i;
    					
    					DeleteAllItems(dialog,21);
    					AddChoice(dialog,21,'No Transformation',0);
    
    					choiceCounter:=1;
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO
    						BEGIN
    							BSB:=GetLBItemInfo(dialog,11,i,0,BSS,BSI);
    							
    							IF(BSI <> kImageCheck) THEN
    								BEGIN
    									BSB:=GetLBItemInfo(dialog,11,i,1,BSS,BSI);
    									AddChoice(dialog,21,BSS,choiceCounter);
    									choiceCounter:=choiceCounter + 1;
    								END;
    						END;
    					
    					BSB:=GetLBItemInfo(dialog,11,selRow,4,BSS,BSI);
    					IF(BSI = 0) THEN
    						BEGIN
    							DeleteAllItems(dialog,21);
    							EnableItem(dialog,21,FALSE);
    						END
    					ELSE EnableItem(dialog,21,TRUE);
    					
    					BSB:=GetLBItemInfo(dialog,11,selRow,2,BSS,BSI);
    					IF(BSS = '') THEN SelectChoice(dialog,21,0,TRUE)
    					ELSE
    						BEGIN
    							GetChoiceIndex(dialog,21,BSS,BSI);
    							SelectChoice(dialog,21,BSI,TRUE);
    						END;
    				END;
    			21: {List Box}
    				BEGIN
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO IF(ISLBItemSelected(dialog,11,i)) THEN selRow:=i;
    					
    					GetSelectedChoiceInfo(dialog,21,0,BSI,targetClass);
    					IF(BSI <> 0) THEN
    						BEGIN
    							BSB:=GetLBItemInfo(dialog,11,selRow,1,origClass,BSI);
    							
    							IF(origClass <> targetClass) THEN
    								BEGIN
    									BSB:=SetLBItemInfo(dialog,11,selRow,0,'Checked',kImageCheck);
    									BSB:=SetLBItemInfo(dialog,11,selRow,2,targetClass,0);
    									BSB:=FindLBColumnItem(dialog,11,1,targetClass,BSI);
    								END
    							ELSE SelectChoice(dialog,21,0,TRUE);
    						END
    					ELSE
    						BEGIN
    							BSB:=SetLBItemInfo(dialog,11,selRow,0,'Unchecked',1);
    							BSB:=SetLBItemInfo(dialog,11,selRow,2,'',0);
    						END;
    				END;
    		END;
    		
    		{Cleanup List Browser}
    		FOR i:=0 TO GetNumLBItems(dialog,11) DO BSB:=SetLBItemInfo(dialog,11,i,4,'',1);
    		FOR i:=0 TO GetNumLBItems(dialog,11) DO
    			BEGIN
    				BSB:=GetLBItemInfo(dialog,11,i,2,BSS,BSI);
    				IF(BSS <> '') THEN
    					BEGIN
    						BSB:=FindLBColumnItem(dialog,11,1,BSS,BSI);
    						BSB:=SetLBItemInfo(dialog,11,BSI,4,'',0);
    					END;
    			END;
    		
    		FOR i:=0 TO GetNumLBItems(dialog,11) DO
    			BEGIN
    				BSB:=GetLBItemInfo(dialog,11,i,4,BSS,BSI);
    				IF(BSI <> 0) THEN textOpacity:=0 ELSE textOpacity:=kColor;
    
    				BSB:=SetLBItemTextColor(dialog,11,i,1,textOpacity,textOpacity,textOpacity);
    			END;
    	END;
    
    BEGIN
    	dialog:=DrawDialog('Class Transformer');
    	layoutDialog:=RunLayoutDialog(dialog,DialogHandler);
    END;
    
    Run(ClassTransformer);

     

    • Like 1
    • Love 2
  12. I'm trying to get the preference selector to detect whether or not the user has Dark Mode enabled.  In the VW2024 offline function reference, there is selector 6756, but it also is the same selector for Entered in Group and is returning false to me regardless of what my dark mode settings are, so I'm guessing that it's not correct.

     

    image.thumb.png.98f7152d9cb9478c8fec06195ec7ddcd.png

  13. There's not a great way to go about being able to handle multiple classes at a time without writing a dialog box.  So I did.

     

    image.png.b673c149ffdda3e91e9293bfe758254f.png

     

    It basically builds an array of classes (sorted alphabetically) and populates a List Browser on the left and a List Box on the right.  When you select a class on the left, it will jump to the List Box to select the new class.  When a class is selected in the List Box, it adds it to the selected row in the List Browser and makes the choice ineligible to be selected in the List Browser.  Likewise, it will remove the original class from the List Box, so you can't convert in circles.  There's also an option to delete the original class by clicking on the Delete column of the List Browser (with code to prevent the None and Dimension class from being deleted).

     

    Code is pasted below.  List Browsers are fairly complicated and are tedious to code, but there's a terrific primer here: https://developer.vectorworks.net/index.php/User:CBM-c-/VS-List_Browsers_part_1

     

    PROCEDURE ClassTransformer;
    
    {*	Converts objects in one class to a different class with options to delete original class
    	Developed by: Jesse Cogswell
    	VW Version: 24 (VW2019)
    	Date: 11/5/2023
    	Revisions:
    *}
    
    VAR
    
    	dialog,layoutDialog:LONGINT;
    	classArray:DYNARRAY[] OF STRING;
    
    PROCEDURE BuildClassArray;
    
    {Builds array of class names and sorts alphabetically}
    
    	VAR
    	
    		i:INTEGER;
    	
    	BEGIN
    		ALLOCATE classArray[1..ClassNum];
    		
    		FOR i:=1 TO ClassNum DO classArray[i]:=ClassList(i);
    		
    		SortArray(classArray,ClassNum,0);
    	END;
    	
    
    PROCEDURE TransformClass(origClass,targetClass:STRING; deleteClass:BOOLEAN);
    
    {Converts objects in origClass to given targetClass, deletes origClass if deleteClass is TRUE}
    
    	CONST
    	
    		kSymbolDef = 16;
    		kNone = 'None';
    		kDim = 'Dimension';
    
    	VAR
    	
    		resList,numItems:LONGINT;
    		i,insertMode,breakMode:INTEGER;
    		currentClass:STRING;
    
    		PROCEDURE TransformObjectClass(h:HANDLE);
    		
    		{Converts class of given object to targetClass}
    		
    			BEGIN
    				SetClass(h,targetClass);
    			END;
    			
    	BEGIN
    		ForEachObject(TransformObjectClass,(INSYMBOL & INOBJECT & INVIEWPORT & (C = origClass)));
    		
    		resList:=BuildResourceList(kSymbolDef,0,'',numItems);
    		FOR i:=1 TO numItems DO
    			BEGIN
    				GetSymbolOptionsN(GetNameFromResourceList(resList,i),insertMode,breakMode,currentClass);
    				IF(currentClass = origClass) THEN SetSymbolOptionsN(GetNameFromResourceList(resList,i),insertMode,breakMode,targetClass);
    			END;
    		
    		IF((deleteClass) & (origClass <> kNone) & (origClass <> kDim)) THEN DelClass(origClass);
    	END;
    	
    FUNCTION DrawDialog(DName:STRING) : LONGINT;
    
    {Creates dialog box}
    
    	CONST
    	
    		kLBWidth = 100;
    		kLBHeight = 50;
    		kListBoxWidth = 50;
    
    	VAR
    	
    		dia:LONGINT;
    	
    	BEGIN
    		dia:=CreateLayout(DName,FALSE,'OK','Cancel');
    		
    		CreateLB(dia,11,kLBWidth,kLBHeight);
    		
    		CreateListBoxN(dia,21,kListBoxWidth,kLBHeight,FALSE);
    		
    		CreateGroupBox(dia,101,'Transform From',TRUE);
    		CreateGroupBox(dia,102,'Transform To',TRUE);
    		
    		SetFirstGroupItem(dia,101,11);
    		SetFirstGroupItem(dia,102,21);
    		
    		SetFirstLayoutItem(dia,101);
    		SetRightItem(dia,101,102,0,0);
    		
    		DrawDialog:=dia;
    	END;
    
    PROCEDURE DialogHandler(VAR item,data:LONGINT);
    
    {Handles dialog interaction}
    
    	CONST
    	
    		kColWidth = 235;
    		kColor = 255 * 0.6;
    
    	VAR
    	
    		i,colNum,kImageCheck,kImageBlank,BSI,choiceCounter,selRow,textOpacity:INTEGER;
    		origClass,targetClass,BSS:STRING;
    		deleteClass,BSB:BOOLEAN;
    
    	BEGIN
    		CASE item OF
    			SetupDialogC:
    				BEGIN
    					kImageCheck:=AddListBrowserImage(dialog,11,'Vectorworks/Standard Images/Checkmark.png');
    					kImageBlank:=AddListBrowserImage(dialog,11,'Vectorworks/Standard Images/Blank.png');
    					
    					colNum:=InsertLBColumn(dialog,11,0,'Select',50);
    					BSB:=SetLBControlType(dialog,11,colNum,5);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,1);
    					
    					colNum:=InsertLBColumn(dialog,11,1,'Class',kColWidth);
    					BSB:=SetLBControlType(dialog,11,colNum,1);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,0);
    					
    					colNum:=InsertLBColumn(dialog,11,2,'Transform Class',kColWidth);
    					BSB:=SetLBControlType(dialog,11,colNum,1);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,0);
    					
    					colNum:=InsertLBColumn(dialog,11,3,'Delete',50);
    					BSB:=SetLBControlType(dialog,11,colNum,3);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,1);
    					
    					colNum:=InsertLBColumn(dialog,11,4,'Eligible',0);
    					BSB:=SetLBControlType(dialog,11,colNum,5);
    					BSB:=SetLBItemDisplayType(dialog,11,colNum,0);
    					
    					colNum:=InsertLBColumnDataItem(dialog,11,0,'Checked',kImageCheck,-1,0);
    					colNum:=InsertLBColumnDataItem(dialog,11,0,'Unchecked',kImageBlank,-1,0);
    					
    					colNum:=InsertLBColumnDataItem(dialog,11,3,'Checked',kImageCheck,-1,0);
    					colNum:=InsertLBColumnDataItem(dialog,11,3,'Unchecked',kImageBlank,-1,0);
    					
    					AddChoice(dialog,21,'No Transformation',0);
    					
    					BuildClassArray;
    					FOR i:=1 TO ClassNum DO
    						BEGIN
    							colNum:=InsertLBColumnDataItem(dialog,11,1,classArray[i],0,0,0);
    							BSI:=InsertLBItem(dialog,11,colNum,classArray[i]);
    							BSB:=SetLBItemUsingColumnDataItem(dialog,11,colNum,1,colNum);
    							
    							AddChoice(dialog,21,classArray[i],i);
    						END;
    					
    					EnableLBColumnLines(dialog,11,TRUE);
    					BSB:=EnableLBSingleLineSelection(dialog,11,TRUE);
    				END;
    			1: {OK}
    				BEGIN
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO
    						BEGIN
    							BSB:=GetLBItemInfo(dialog,11,i,0,BSS,BSI);
    							IF(BSI = kImageCheck) THEN
    								BEGIN
    									BSB:=GetLBItemInfo(dialog,11,i,1,origClass,BSI);
    									BSB:=GetLBItemInfo(dialog,11,i,2,targetClass,BSI);
    									BSB:=GetLBItemInfo(dialog,11,i,3,BSS,BSI);
    									deleteClass:=(BSI = kImageCheck);
    									
    									TransformClass(origClass,targetClass,deleteClass);
    								END;
    						END;
    					
    					ReDrawAll;
    				END;
    			2: {Cancel}
    				BEGIN
    				END;
    			11: {List Browser}
    				BEGIN
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO IF(IsLBItemSelected(dialog,11,i)) THEN selRow:=i;
    					
    					DeleteAllItems(dialog,21);
    					AddChoice(dialog,21,'No Transformation',0);
    
    					choiceCounter:=1;
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO
    						BEGIN
    							BSB:=GetLBItemInfo(dialog,11,i,0,BSS,BSI);
    							
    							IF(BSI <> kImageCheck) THEN
    								BEGIN
    									BSB:=GetLBItemInfo(dialog,11,i,1,BSS,BSI);
    									AddChoice(dialog,21,BSS,choiceCounter);
    									choiceCounter:=choiceCounter + 1;
    								END;
    						END;
    					
    					BSB:=GetLBItemInfo(dialog,11,selRow,4,BSS,BSI);
    					IF(BSI = 0) THEN
    						BEGIN
    							DeleteAllItems(dialog,21);
    							EnableItem(dialog,21,FALSE);
    						END
    					ELSE EnableItem(dialog,21,TRUE);
    					
    					BSB:=GetLBItemInfo(dialog,11,selRow,2,BSS,BSI);
    					IF(BSS = '') THEN SelectChoice(dialog,21,0,TRUE)
    					ELSE
    						BEGIN
    							GetChoiceIndex(dialog,21,BSS,BSI);
    							SelectChoice(dialog,21,BSI,TRUE);
    						END;
    				END;
    			21: {List Box}
    				BEGIN
    					FOR i:=0 TO GetNumLBItems(dialog,11) DO IF(ISLBItemSelected(dialog,11,i)) THEN selRow:=i;
    					
    					GetSelectedChoiceInfo(dialog,21,0,BSI,targetClass);
    					IF(BSI <> 0) THEN
    						BEGIN
    							BSB:=GetLBItemInfo(dialog,11,selRow,1,origClass,BSI);
    							
    							IF(origClass <> targetClass) THEN
    								BEGIN
    									BSB:=SetLBItemInfo(dialog,11,selRow,0,'Checked',kImageCheck);
    									BSB:=SetLBItemInfo(dialog,11,selRow,2,targetClass,0);
    									BSB:=FindLBColumnItem(dialog,11,1,targetClass,BSI);
    								END
    							ELSE SelectChoice(dialog,21,0,TRUE);
    						END;
    				END;
    		END;
    		
    		{Cleanup List Browser}
    		FOR i:=0 TO GetNumLBItems(dialog,11) DO BSB:=SetLBItemInfo(dialog,11,i,4,'',1);
    		FOR i:=0 TO GetNumLBItems(dialog,11) DO
    			BEGIN
    				BSB:=GetLBItemInfo(dialog,11,i,2,BSS,BSI);
    				IF(BSS <> '') THEN
    					BEGIN
    						BSB:=FindLBColumnItem(dialog,11,1,BSS,BSI);
    						BSB:=SetLBItemInfo(dialog,11,BSI,4,'',0);
    					END;
    			END;
    		
    		FOR i:=0 TO GetNumLBItems(dialog,11) DO
    			BEGIN
    				BSB:=GetLBItemInfo(dialog,11,i,4,BSS,BSI);
    				IF(BSI <> 0) THEN textOpacity:=0 ELSE textOpacity:=kColor;
    
    				BSB:=SetLBItemTextColor(dialog,11,i,1,textOpacity,textOpacity,textOpacity);
    			END;
    	END;
    
    BEGIN
    	dialog:=DrawDialog('Class Transformer');
    	layoutDialog:=RunLayoutDialog(dialog,DialogHandler);
    END;
    
    Run(ClassTransformer);

     

    There's some funky things in there to handle the error checking between the two boxes.  If you would like, I can give you a simpler example with all of that code removed if you're okay with throwing caution to the wind.  Let me know if you have any questions.

    • Like 1
  14. I have been using a variation of this 42.5" 4K LG display since early 2020 (last big purchase before COVID hit).  A friend of mine had been trying to get me to buy one for years, but a display that size seemed to me to be far too large.  I finally caved and bought one, and it was seriously life changing.  I can look at a D-Size plate in nearly 1:1 size on the screen, and with DPI scaling set to 100%, I can get nearly the full OIP for Lighting Device objects without having to scroll.  It's really helped with being able to see the bigger picture while also reducing eye strain.

     

    So much room for activities:

    image.thumb.png.b38a700cb692297d0a1b4a5da4d39f83.png

     

    Even better, if you treat the monitor as quadrants, you get 4 1080, 21" displays out of it.  It even has a picture in picture mode so that you can have multiple video sources on screen at once.  It tops out at 60hz, so definitely not the best for gaming, and the color isn't reference monitor accurate, but it does work great for drafting.  The only snag is that VW doesn't quite handle jumping back and forth between the monitor and my laptop display (which is also 4K, but run at 200% scaling), so I have to resize all of my palettes everytime I jump back and forth.  Minor complaint but a complaint none the less.

     

    It's also a bit too large and bulky for most standard VESA arms, which can be a bummer for some folks.

    • Like 3
  15. Also, it took a little fiddling, but you can totally have the VW2023 tool icons in VW2024.  The process is more than a little wonky and has to do with modifying the Vectorworks Resource File (very much not recommended unless you really know what you are doing), but it's technically doable.  Can't do anything about the Toolset icons since they were VWR resources in VW2023.

     

    image.png.befb3e299b0f03883521ccc9edfa3759.png

     

    If you are interested in doing this, DM me and I can help you out.  It's probably for the very best that I not post the steps on the public forum in case someone borks their Vectorworks trying to follow along.

    • Like 2
    • Love 1
  16. It might be something tied to Windows.  Here's what my view bar looks like in Regular Mode:

     

    image.thumb.png.abee38791f4aef7dfcff3c438b90c017.png

     

    And here's in Compact Mode:

    image.thumb.png.99f94813441c887f3a5cfa871232514c.png

     

    All of the drop-down menus are extremely short and can't currently be resized.  This makes compact mode next to useless for me.

     

  17. @FranAJA Unfortunately, my Python is more than a little rusty.  I learned Python back in 2010 and wrote some pretty handy little programs in it, but haven't touched it since.  I learned Vectorscript a year later in 2011 and rather enjoy the strictness and structure of it.  Since they added Python in as an option in 2014, relearning Python has been on My List of Things to Do™, but that list is very long and only gets longer by the day.  I find that the Python implementation in VW is a little sloppy, as it forces Python to behave like Pascal and thereby negates a lot of the benefits of Python.  I really haven't been able to wrap my head around variable handling and tuples and the level of documentation and examples is slim (it took me ages to figure out how to make GetCustomObjectInfo to work in Python, and I still don't fully understand it).  My current practice is to do as much in Vectorscript as possible, then use PythonBeginContext, PythonEndContext, and PythonExecute to use Python's extensive libraries for things that aren't covered by Vectorscript.

     

    That all being said, I can help as much as I am able.  One nice thing about the VW Python implementation is that all of the functions are basically the same, just with a vs. prefix added to them and returns being handled with tuples instead of constructor arguments, so the biggest hurdle when translating my example is just reworking a bit of the syntax.  The example I have linked above is designed to demonstrate three different, and all fairly complicated, concepts: Event-Enabled plug-ins, dialog handling, and building resource and parent arrays for dialogs.  What are you trying to implement into your PIO?  If you're looking to implement both things, my recommendation is to work on one thing at a time.  Start with getting an Event-Enabled plug-in working.  Something simple like a point object that uses CreateText to put a string parameter on the drawing, and a button in the OIP that launches StrDialog to change that string parameter.  You should be able to follow a similar structure to what I have in the Vectorscript example starting with the vsoGetEventInfo function, substituting an if/elif structure for the CASE statement.  Each line in the driver of my example is commented to explain exactly what the code is doing, so it should be pretty simple to translate it into Python.

     

    Dialog handling in Vectorworks is a whole other thing, and something you can practice in a document script outside of a PIO.  Generally, VW requires two different functions for each dialog, a builder and a handler.  The builder (DrawDialog in my example), creates and arranges all of the elements that make up the dialog.  I always approach this in the same way:

    1. Create all elements starting with static text objects, then moving on to interactive (text fields, buttons, checkboxes, etc), then to groups.  What's important to note is that VW will use the element ID order to determine the order when using the Tab key to navigate the dialog, so I try to make sure that I number and create things in the order that I imagine the user will want to interact with them (usually top to bottom).  The other important thing to note is that the OK button will always be element ID 1, and the Cancel button will always be element ID 2, so it's best to start numbering your elements at 11.  I usually make my groups start at element ID 101, but didn't in the above example for some reason.
    2. Set group items.  This uses SetFirstGroupItem to set the first element, then SetBelowItem or SetRightItem to position elements from there.  I generally populate one row at a time, so using SetRightItem to position elements for the row, then SetBelowItem with the first element from the last row as the first argument to start setting the next row.
    3. Set layout items.  I usually try to group a lot of elements, so for me this is mostly setting my groups into the dialog.  Same format as setting group items except you'll use SetFirstLayoutItem to set the first element.
    4. Align items.  This uses AlignItemEdge to line things up to make them look nice.  I use this to make text boxes line up with pop-up menus and make group boxes all line up.  If you get clever with your element numbering, you can use a FOR statement to do a whole bunch of elements at the same time.  One thing to note is that this doesn't really work with Static Text elements, so it's best to make all of your static text the same length by passing in the length of the longest text string as the widthInCharacters argument for all the static text.
    5. Add help strings.  Using SetHelpText, add help text for each element that needs it.  I do this in all of my plug-ins outside of very simple and self-explanatory dialogs, or when I'm feeling lazy and the plug-in is strictly for a forum example.

    Once you've created your dialog, you'll need a handler callback function to deal with it so that you can use RunLayoutDialog.  This will use an if/elif structure in your case to handle interactions with any dialog elements.  The RunLayoutDialog page of the online function reference has a decent Python example of how all of this works, but it's missing some things in the handler that are handy to know:

    • If you need to populate or preset any dialog elements, you will need a SetupDialogC item and this will likely want to be the first thing in your if/elif structure.  I'm unclear if vs.SetupDialogC works on its own or if you will need to define a constant as kSetupDialogC = 12255 as shown in this thread.
    • As in the function reference example, item == 1 is what happens when you press the OK button.  This should pull values from the dialog and update global variables or change drawing elements as needed.
    • item == 2 is what happens when you press the Cancel button.  I'll always put this in the code, but often leave it blank.  If my dialog updates global variables and executes things in the driver, I will usually put in a global boolean check that the Cancel button sets to FALSE.
    • item == X, where X equals the dialog element ID that you defined in the builder.  This is where you can set buttons in the dialog to populate elements within the dialog.  In my example, I have code that will update the Symbol Display Control when you interact with the Thumbnail Popup.  In Python, this would read as elif item == 31:

    Again, I would practice building a dialog in a document script and get the general concepts and structure down.

     

    Now, building arrays of resources and parents for populating the thumbnail popups will be the toughest thing to translate from Vectorscript to Python.  The way Vectorscript handles arrays is very rigid and restrictive, so my PopulateResourceArray procedure has to do some pretty dodgy things to build my resource array.  It's a 2D array, with the X being the parent (folder name in my example) and the Y being the resource name (symbol or texture name).  So it's constantly resizing the Y size of the array if the number of resources in a parent would exceed the bounds of the array. Python is far better than Pascal with dealing with data in this way.  There's probably a clever way to use the Python Dictionary structure that can make this whole process much more simple, but I would need to noodle around with Python a bit before I can really help you with this process.

     

    My schedule is more open next week, so if I find my self with some time, I might try to adapt the example to Python, but I can't really make any promises.  Again, any interactions between the code and Vectorworks will largely be the same between Vectorscript and Python, so a lot of adapting my example code is just translating the return syntax to fit Python.  The biggest things to know in terms of structural differences between Python and Vectorscript is that all variables and constants in VS must be declared before they are used and be declared in a specific "block".  This is obviously not the case in Python.  More information about the "block" structure of Vectorscript can be found in this thread:

    This might help in navigating the overall structure of my example so that you can translate the structure to Python.

    • Like 1
  18. I think @Pat Stanford meant to link Jesse Cogdell @jcogdell instead of me, I don't work for Vectorworks.  But, I can do my best to help you.  Can you post a picture of either your Resource Manager or the Resource Pop-up from the Lighting Device Tool?

     

    Resource Manager:

    image.thumb.png.316c339f3b926bbf9375a39b501282f4.png

     

    Resource Pop-up:

    image.thumb.png.38972e654a1dfe3337b3bf56eed5f0b6.png

     

    You shouldn't necessarily need to download the full library, you can download files on demand as you need them, but could you also post a picture of your Vectorworks Package Manager?  It can be found by going to Help - Download Content.

     

    image.png.bd8859cfc40cd7a81ba07bab2e8f2ad3.png

     

    Finally, which operating system are you running?  I strongly recommend putting your system information and which version of Vectorworks you are using in your signature (it's not quite where you expect it, but Pat has a handy guide in his signature).

  19. I am on PC so I don't know if this is true for the Mac version, but while Import EPSF has been removed from the default workspace, it hasn't been completely removed from VW itself.  If you open up the Workspace Editor, Import EPSF still appears under the Import/Export category.  Again, no guarantee that this is the case on Mac, but it's worth a shot.

     

    But I definitely agree that VW should add an .svg import option, especially with icons within VW being changed from .png files to .svg files.

  20. At the bottom of the Instrument Summary Object Info Palette is a button called Filters.  This allows you to filter counts based on class, layer, position, or a custom string.  I recently did a similar project where I proposed two different track layouts, one with horizontal track and one with vertical track.  In this case, I could have used the Instrument Summary tool with a layer filter in place, as in this example:

     

    image.thumb.png.0e28c7284c972818a74477d2cad45675.png

     

    One thing to keep in mind with this approach is that these filters are applied to the object as a text string, so if you have a lot of filters turned on and you have long layer names, the text string becomes too long to parse and the summary will instead error out.

    • Like 1
×
×
  • Create New...