Jump to content

DomC

Member
  • Posts

    611
  • Joined

  • Last visited

Posts posted by DomC

  1. Hi
    Marionette has his own function to deside (and with that if it should use a 3D Move, 3D Rotate or 2D Move, 2D Rotate etc.) if an object is planar or not. This concept has some limitations. 
    If you edit the code to this, the rotate node will work inside the PIO. Double click the node and delete  the first line, then you can edit the code.

     

    #planar = Marionette.Is2DObject(obj)    
    cx = center[0] - offset[0]
    cy = center[1] - offset[1]
    cz = center[2] - offset[2]   
    #if planar:        
    vs.HRotate(obj, (cx, cy), r[2])
    #else:                                    
    #    vs.Set3DRot(obj, r[0], r[1], r[2], cx, cy, cz)  


    In this case, the Rotate-Node deside, that it wants to use the vs.Set3DRot() command to the symbol. which seems to work, if the symbols are on the layer. Inside the PIO it does not work. But this will not work, if the symbols are on screen plan. Inside the PIO the symbols are placed on a screen-plane like place (top plan view). 

    I think it is related to the different plane- and container-concepts that makes it very frittered and hard for one node to handle. 
     

  2. On 3/29/2022 at 1:06 PM, Sloader said:

    it would be good to suppress the failure messages

    Hello
    Does it maybe work if you do something like this:

    try:
    	vs.SetRField(h2,'Massing Model','2Ddisplay','Roof')
    	#your code
    
    except:
    	pass
    	#or do someting like taggging the object as "too complexe" or do another function


    Alternatively you can put something heavy on your enter key, while script is running.
     

    • Like 1
    • Laugh 1
  3. Hello
    Chris solved his situation with putting the top/plan component on screen-plane and use set parent to move it into the symbol.
    In the meantime I made an node for SetPlanar Ref. The includes Command (vs.Set2DComponentGroup) needs a group of screen-plane objects to work. So use the Set Planar Boolean and the group node for your geometry. For a hybrid symbol with 3D and top/plan the SetComponentGroup is not necessary. SetComponentGroup would need to set front, left, section views etc.


    image.png.9a3ddef3816bb1837cbbf9baf91a687b.png
     
    image.thumb.png.9fdbb04316ae8292a4e6e2ec711228ce.png

    Script Set2DComponentGroup.vwx

    • Like 4
  4. Hello
    Same Issue here. It seems as soon as we have light from a HDRI this light is intensified by exporting a Panorama. I think most of users just exports the panorama without choosing the same Render-Style as the use in Vectorworks. Then the Result is Standard-RenderWorks with maybe no HDRI light attached and looks not too bright.

  5. Just read the initial post. "Workgroup folder"
    OK, now it is getting interesting. It depends on platform you work and workgroup folder is stored in the system I think (plist or registry). 

    But I would not use this it is digging deep and it may not work anymore with newer versions etc. maybe hard to get it to work again. Also I did not checked if this is still working. 
     

    #Windows
    
    import getpass
    import shutil
    
    HSFPath = vs.GetFPathName()
    vs.AlrtDialog(HSFPath)
    #Application	1
    #User App Data	12
    target = 'Users/dominiquecorpataux/Desktop'
    
    from winreg import *
    aReg = ConnectRegistry(None,HKEY_CURRENT_USER)
    aKey = OpenKey(aReg, r"Software\Nemetschek\Vectorworks 24\General")
    WGF_Path = QueryValueEx(aKey, "Workgroup Folder 0")
    #vs.Message(WGF_Path[0]) 
    target = WGF_Path[0]
    
    vs.AlrtDialog(str(target))
    
    
    #on mac
    
    import plistlib
    #MacFile = 'Users/dominiquecorpataux/Library/Preferences/net.nemetschek.vectorworks.2019.plist'
    pl = {}
    with open(file, 'rb') as fp:
        pl = plistlib.load(fp)
        
    vs.AlrtDialog(str(pl.get("NNA Workgroup Folders")))
    vs.AlrtDialog(pl["NNA Workgroup Folders"])

     

  6. It should work with something like this:
    (Not tested but should work)
     

    import os
    
    res_file = "Path/to/your/file.vwx"
    if os.path.isfile(res_file) == False:
    	vs.AlrtDialog('Path error')
    	
    else:    
    	listID, symDefNum = vs.BuildResourceListN( 16, res_file )
    	for index in range(1, symDefNum+1):
    		vs.ImportResourceToCurrentFile(listID, index)
        


     

    To grab several res files in a folder and his sub-folders:

     

    import unicodedata
    import os
    def get_res_files(folder):
    	file_list = []
    	for path, subdirs, files in os.walk(folder):
    		path = unicodedata.normalize('NFC', path) ## Valid values for form are ‘NFC’, ‘NFKC’, ‘NFD’, and ‘NFKD’.
    		for name in files:
    			filename, file_extension = os.path.splitext(name)
    			if file_extension == '.vwx':
    				fullpath = os.path.join(path, name)
    				fullpath_string = unicodedata.normalize('NFC', fullpath)
    				file_list.append(fullpath_string)
                    
    	return file_list             
    
    
    	


    Then you could loop through several res files:
     

    res_file_list = get_res_files('Folder/to/Libraries')  
    for res_file in res_file_list:
        listID, symDefNum = vs.BuildResourceListN( 16, res_file )
        for index in range(1, symDefNum+1):
            vs.ImportResourceToCurrentFile(listID, index)


     
    If you have issues with the path, maybe try:
     

    from pathlib import Path
    home = str(Path.home())   
    
    filepath = home+'/google/yourpath/yourfile.vwx'
    
    #to be cross-platform always use something like this:
    filepath = os.path.join(folder,folder,folder,file)

     

  7. The Function I use CreateChainDimension:
     

    def create_chain_dim_vertical(intersection_points_sorted, x, offset, vp_scale): #standardly im text is left if there is no place between
    	threshold_2_dimension, dimoff1, dimoff2, dimoff3 = create_globals(vp_scale , layer_scale , units)
    	start = intersection_points_sorted[0]
    	h1 = None; h_first = None
    	prior_segment_length = 0
    	prior_shifted_leftright = False; prior_shifted_up = False
    	for i in range(len(intersection_points_sorted)-1):
    		# startPt is always last endPt
    		end = intersection_points_sorted[i + 1]
    		segment_length = abs(end - start)
    		if segment_length > epsilon:
    			vs.LinearDim((x,start), (x,end), offset, 4, 769, True, 0.1)
    			if data['b_debug_dim_color']: vs.SetPenFore(vs.LNewObj(), (65000,0,65000)); 
    			color = (65000,0,65000)	
    			if i > 0:
    				h2 = vs.LNewObj()
    				if not vs.GetObjectVariableBoolean(h2,30): #Text hatte kein Platz
    					if i < len(intersection_points_sorted)-2: #not last dimension
    						start_next = intersection_points_sorted[i+1] #=endPt
    						end_next = intersection_points_sorted[i+2]
    						next_segment_length =  abs(end_next - start_next)
    						if prior_segment_length < threshold_2_dimension and next_segment_length > threshold_2_dimension:
    							vs.SetObjectVariableBoolean(h2,29,False)#unlock Text position
    							vs.SetObjectVariableReal(h2,44,-0.1)#text shift
    							vs.SetObjectVariableBoolean(h2,29,True)#calculate dim text
    							color = (65000,0,0); prior_shifted_up = False
    						elif prior_segment_length < threshold_2_dimension and next_segment_length < threshold_2_dimension and prior_shifted_leftright == False:
    							vs.SetObjectVariableBoolean(h2,29,False)#unlock Text position
    							vs.SetObjectVariableBoolean(h2,30,True)#Text inside
    							vs.SetObjectVariableReal(h2,44,-0)#text shift
    							color = (0,30000,30000); prior_shifted_up = False
    						elif prior_segment_length < threshold_2_dimension and next_segment_length < threshold_2_dimension and prior_shifted_leftright == True:
    							vs.SetObjectVariableBoolean(h2,29,False)#unlock Text position
    							vs.SetObjectVariableBoolean(h2,30,True)#Text inside
    							if not prior_shifted_up:
    								vs.SetObjectVariableReal(h2,43,offset_up); prior_shifted_up = True; color = (0,65000,0)
    							vs.SetObjectVariableReal(h2,44,-0); color = (0,0,65000)
    					else:
    						vs.SetObjectVariableBoolean(h2,29,False)#unlock Text position
    						vs.SetObjectVariableReal(h2,44,-0.1)#text shift
    						vs.SetObjectVariableBoolean(h2,29,True)#calculate dim text
    					prior_shifted_leftright = True	
    				else:
    					prior_shifted_leftright = False		
    				#prior_segment_unshifted = vs.GetObjectVariableBoolean(h2,30) #debug
    				if data['b_debug_dim_color']: vs.SetPenFore(h2, color)
    				vs.ResetObject(h2); #vs.SetPenFore(h2, (65000,0,0))
    				vs.ResetObject(h1)
    				h1 = vs.CreateChainDimension(h1, h2)
    				
    			else: #first dimension > move to left
    				h1 = vs.LNewObj()#;vs.SetPenFore(h1, (65000,0,0))
    				if not vs.GetObjectVariableBoolean(h1,30): #Text hatte keinen Platz
    					vs.SetObjectVariableBoolean(h1,29,False)#unlock Text position
    				vs.SetObjectVariableReal(h1,44,0); 
    				if data['b_debug_dim_color']: vs.SetPenFore(h1, (30000,0,30000))
    					
    			#vs.AlrtDialog(str(h1))
    		prior_segment_length = abs(end - start)
    		start = end
    	chain_dims.append(h1)

     

  8. Thanks for Feedback
     

    2 hours ago, JBenghiat said:

    That said, the Compose command will do what you want without a script. 

    I will try with a DomMenuTextByName as a workaround, thanks
    The complete Script has 800 lines (it reaches the limit of maximum size of a script), take a look here 🙂

    An incredible powerful Auto-Dimension Script
     

     

    59 minutes ago, Pat Stanford said:

    for i in range(len(dimensions)-1):


    The Second Script iterates through the length of dimensions. If I have the minimum of Objects (2) then length is 2
    The loop starts with 0 and ends with 2-1 (1). If the loop would go till i == 1 i had an index of range with i+1. Different ways to do that with python.






     

  9. Hi there
    I want to join single dimensions in one chain dimension. So far it works but I have an undo issue. If I undo the script my single dimensions are deleted and I also get an undo alert in the log file.

    My simple test with two dimensions:
     

    h1 = vs.FSActLayer()
    h2 = vs.NextSObj(h1)
    
    hnew = vs.CreateChainDimension(h1, h2)
    	


    My function to join n-Numbers of single dimensions

     

    dimensions = []
    
    def getObj(h):
    	dimensions.append(h)
    	
    	
    vs.ForEachObject(getObj, "(NOTINDLVP & NOTINREFDLVP & (SEL=TRUE))")	
    
    
    h1 = dimensions[0]
    for i in range(len(dimensions)-1):
    	h1 = vs.CreateChainDimension(h1, dimensions[i+1])
    	



    The Scripts are working on selected objects for that test.


    The Undo Alert I get:

    "ALERT: Invalid undo primitive detected: Imminent crash is possible. Object 0x7ff725723440 is being inserted after it has been modified or deleted.","type":"INFO"}


    Also really it is an undo issue because Unto after script deletes my dimensions. Have I to handle the undo events in a Vector Script?

  10. 4 hours ago, corbinhand said:

    I am not able to view this dropdown in the OIP after wrapping the network. It seems like it's not possible. 

    Like I told, you need to run the network, which pulls the values from the table. then you have to deselect and reselect the node. And then the popup is there. but not on the wrapper or a Marionette PIO, because this Node has an input and nodes with an connected Input will not shown on the wrapper or a Marionette PlugIn.
    Thats why option #2 is the only proper solution. The posted example is option #1. 

  11. Hello
    Solved by searching the Forum. Seems we need backward indexing to delete the choices. Here the working popup Dialog.

     

    
    choices1 = ['choice1', 'choice2', 'choice3', 'choice4']
    choices2 = ['choiceA', 'choiceB']
    
    
    
    def CreateMyDialog():
    	vs.dialogid = vs.CreateLayout('Dialog Name', 0, 'OK', 'Cancel')
    	dialogID = vs.dialogid
    	vs.CreateStaticText(dialogID, 10, 'Popup', 60 )
    
    	vs.CreatePullDownMenu(dialogID, 11, 40)
    	vs.CreateCheckBox(dialogID, 12, 'Change Popup')
    	
    	vs.SetFirstLayoutItem(dialogID, 10)
    	vs.SetBelowItem(dialogID, 10, 11, 0, 1)
    	vs.SetBelowItem(dialogID, 11, 12, 0, 1)
    	
    	return vs.RunLayoutDialogN(dialogID, Dialog_Handler, 0)
    
    def Dialog_Handler(item, data):
    	dialogID = vs.dialogid
    			
    	if item == 12255:  # enter Dialog 12255  -> 12256 exit dialog
    		for i in range(len(choices1)):
    			vs.AddChoice(dialogID, 11, choices1[i], i)
    
    	if item == 12: #checkbox
    		num_choices = vs.GetChoiceCount(dialogID, 11)
    		for i in range(num_choices, -1, -1):
    			vs.RemoveChoice(dialogID, 11, i)
    				
    		if data: #checkbox True
    			for i in range(len(choices2)):
    				vs.AddChoice(dialogID, 11, choices2[i], i)	
    				
    		if not data: #checkbox False
    			for i in range(len(choices1)):
    				vs.AddChoice(dialogID, 11, choices1[i], i)	
    
    
    
    result = CreateMyDialog()

     

    • Like 1
  12. Hello
    Would be great if anybody could help me. At the end result I want to be able to exchange a popup's choices. What I am trying to do is, that I delete the items with vs.RemoveChoice()
    and build it new with a loop through my choices. I think I make something wrong by indexing with "AddChoice" or "RemoveChoice" just remove the string not shorten the Choice Numbers.

    My script:
     

    
    choices1 = ['choice1', 'choice2', 'choice3', 'choice4']
    choices2 = ['choiceA', 'choiceB']
    
    
    
    def CreateMyDialog():
    	vs.dialogid = vs.CreateLayout('Dialog Name', 0, 'OK', 'Cancel')
    	dialogID = vs.dialogid
    	vs.CreateStaticText(dialogID, 10, 'Popup', 60 )
    
    	vs.CreatePullDownMenu(dialogID, 11, 40)
    	vs.CreateCheckBox(dialogID, 12, 'Change Popup')
    	
    	vs.SetFirstLayoutItem(dialogID, 10)
    	vs.SetBelowItem(dialogID, 10, 11, 0, 1)
    	vs.SetBelowItem(dialogID, 11, 12, 0, 1)
    	
    	return vs.RunLayoutDialogN(dialogID, Dialog_Handler, 0)
    
    def Dialog_Handler(item, data):
    	dialogID = vs.dialogid
    			
    	if item == 12255:  # enter Dialog 12255  -> 12256 exit dialog
    		for i in range(len(choices1)):
    			vs.AddChoice(dialogID, 11, choices1[i], i)
    
    	if item == 12: #checkbox
    		if data: #checkbox True
    			num_choices = vs.GetChoiceCount(dialogID, 11)
    			for i in range(num_choices):
    				vs.RemoveChoice(dialogID, 11, i)
    			
    			for i in range(len(choices2)):
    				vs.AddChoice(dialogID, 11, choices2[i], i)	
    				
    		if not data: #checkbox False
    			num_choices = vs.GetChoiceCount(dialogID, 11)
    			for i in range(num_choices):
    				vs.RemoveChoice(dialogID, 11, i)
    			
    			for i in range(len(choices1)):
    				vs.AddChoice(dialogID, 11, choices1[i], i)	
    
    
    
    result = CreateMyDialog()

     

  13. Like I told I do not work on that node, just a proof of concept and you had to edit the node code to make it usable. I think I want to provide a useful node maybe but not now.
    However, the popup delivers an index and this index corresponding to the lines in the popup and the lines in the worksheet. You could do this to return the value of popup instead of index. 
     

    self.Params.output.value = j['data'][0]['popupChoices'][input]['text']


    Or this, to return the line from the worksheet (which is the better option)
     

    self.Params.output.value = lines[input]



    input is the popup index.

    • Like 2
    • Love 1
  14. Hello
    The Custom Popup Widget from the Nodes, are generated by editing the Nodes. There are some special Popups which are handled by the Marionette core itself. As example the class popup. If you add classes the popup will contain that new class.

    You can generate the popup items with a script from a worksheet. No question. Lets say, you can say the popup the have those items from the worksheet by script. You have to run the script to deliver the items of the popup. If you click on a popup with items generated like this and you do not run the network your popup has no data because no script was executet. So what can be done without manipulationg the core too much:

    1. Create a node with a popup, which takes items from the input. You have to run the network bevor the items are activated.
    (Example Attached)
    2. Create a dialog push Button or a Dialog which runs when the script runs.
    3. Dis Dialog has his own popup and can be generated with every functionality of script engine.
    4. Having a seperate script (as example in the worksheet), which will push the worksheet changes to the node popups widgets.

    probably it could be possible to manipulate or generate the popup in the Marionette class but it is not something I grabbed before and I guess I would not provide public. 

    So the best option in my opinion is #2, where we have a push button with the popup. The popup will always be up to date. Maybe an issue, if the push-button is nested too deep in wrappers and Object Nodes. So if you do something, always check if it works in your final product. But also this could be an issue with the other variants.

    Here the example, how to overwrite the node-popup from outside. Also this technique is provided a long time ago in the gallery. The Node is just a proof of concept, you cant use it without edit the node code for your own usage. I try to make an Example in the Gallery, which takes a worksheet input and have a push button for choosing the popup. Because this sounds for me very useful for many Marionette users and developpers.

    the interesting think is, that there will be two popup. The first popup will allow to filter the items of the second popup. I think this is not a big thing but as I can remember last Time i made a popup with changing items, i did not known how to eliminate the vestige of deleted popup items.
     

    PopupfromWorksheet.vwx

    • Like 2
    • Love 1
  15. Hi
    I think this can be made by scripts and/or a Marionette Nodes. I think with getRecordField and with getObjectVariable you can pull the requested values out of a viewport, if you know their IDs.
    The Advantage of a Standard Label or Data Tag is, that it has a perfect Link to the viewport and a User Interface to link/delink to an object. This could be made with tricks for sure with a Marionette but without a good UI and with supporting scripts etc. So I recommend to try to make your object with the DataTag. Which I think this is all makable with the DataTag except the "arrow symbol" maybe. 

    As Example the  Viewport Scale you can pull out with the following formula in a Data-Tag:
    Scale:   1:#WS_ObjectData('Object Variable', 1003)#


    Source:
    https://developer.vectorworks.net/index.php/Worksheet_Functions
     

    Some of the Object Variable of a Viewport (You can see them with the Methodes mentioned above):

    Quote

    SetObjectVariableReal (viewportHandle,1003,20);
        SetObjectVariableInt (viewportHandle,1000,6);
        SetObjectVariableInt (viewportHandle,1001,0);
        SetObjectVariableReal (viewportHandle,1002,9.76);
        SetObjectVariableBoolean (viewportHandle,1005,TRUE);
        SetObjectVariableLongInt (viewportHandle,1006,0);

     

    The Rest you can get from the Data-Tag Standard Variables. 

    The Method to find Object Variables is the Script Reference or exporting a Drawing with one viewport as a text Format and search the Viewport's "SetObjectVariables". Change one value of the viewport and export again and see, what changes. Or you can use the "SearchObjectVariable" Marionette. Must be here in the forum but can't find it right know.

    The Data-Tag is a very powerful Drawing label. Only disadvantage is missing links between VP and Labels and no Links in Exported PDFs.

     


    image.png.d67a5cada7f706ee69fda3e3bc804687.png
     

  16. Hi
    You are quite near at the solution. You need to repeat every poly handle as many times as there are points on that polyline. To get the number of points you have to get the number of items in every list you created with the chunk by list node. Also here you need a node, that is not standard. I do not know to do it with standard nodes. I think chunk by list and length of sublists is something, that I is really missed by repeating things on n objects.
    The Node you can see GetListLength ist the standard Node but the line "this.SetListAbsorb()" is commented with a #. Different ways to solve that I think.

    Nice Example.

    image.thumb.png.624e0e6a4f708c0264e2f436627f0c62.png

  17. Hi
    The Field name of "Gesamt Breite" ist 'Width' not "Breite". If you change that, you will get the right object.
    If you set a name of the object, you change the name on the object info on the very buttom. Not the Cabinet Name. The Cabinet Name ist generated by a higher "act of nature" we are not able to write that Name field with a Script. 
    Also ungroup I would not use. Because your Symbol is inserted as a PIO. The group around every marionette-created geometry you cant eliminate with the ungroup node. I think this could be changed with the node itself "linkedGeometry"

    This here, created the group:
    image.png.8766f32622077ac71f907de223d40029.png

  18. As I understand, you want something like global variables for the PIOs on the Document. Like Title Block Project Data.
    There are several possibilities. I think it depends on what you can handle yourself. 3 Examples of Strategies:

    1. You could get the values from a Record handle (the record definition itself not from an Object > It takes the default value). So you had a central control over those values. 
    2. You could push the values (with a Dialog) directly to the Marionette PIOs
    3. Take the value from a worksheet
    This depends how your Marionette-Code is designed.

    I guess SimA - Method (Method 1?) would be the best option.


    To the second Methode. Maybe this is the hardest one because changing the OIP of Marionette is not as simple like with Standard PIOs. There is an Example in the Gallery to make this or you could code A dialog-Window Enter the Value and Push them on your Objects. For myself I use that method to backup values from the marionette on a record. So the script is updated I am able to pull the values if necessary from that record.

    Code something like this:

    
    import json
    
    #Globals
    recName = 'MarionetteObject3D'
    #recName = 'MarionetteObject2D'# Check, the Marionette Rec-Name for your Marionette PIOs
    fldName = 'NodeDef_OIPControls'
    
    #collect Objects
    objs = []
    def getObj(h):
        objs.append(h)
        
    criteria = "(NOTINDLVP & NOTINREFDLVP & (C='MarionetteObjects'))"    
    vs.ForEachObject(getObj, criteria)
    
    
    OIP_Name = 'Mauerlicht' #Name of the Parameter
    OIP_value = 1200 #value of the Parameter
    
    
    for obj in objs:    
        sOld = vs.GetRField(obj, recName, fldName)  
        #vs.AlrtDialog(str(sOld))
        j = json.loads(sOld)
        
        for OIP_field in j['data']:
            name = OIP_field['varName']
            
            if OIP_Name in name:
                value = OIP_field['value']
                OIP_field['value'] = OIP_value
    
        vs.SetRField(obj, recName, fldName, json.dumps(j))
        vs.ResetObject(obj)

     

    • Like 1
  19. Fantastic Julian this is a very reliable solution, Thank you. I implemented this way:
     

    dim = vs.FSActLayer()
    
    def GetDimAngle(h1):
        h2 = vs.FIn3D(h1)
        DimAngle = 0
        while h2 != vs.Handle(0):
            if vs.GetTypeN(h2) == 2:
                BOOLEAN, style, angle, size, width, thicknessBasis, thickness, visibility = vs.GetObjBeginningMarker(h2)
                if style:
                    DimAngle =  round(vs.HAngle(h2),0)
            h2 = vs.NextObj(h2)
        return DimAngle 
    
    DimAngle = GetDimAngle(dim)
    vs.AlrtDialog(str(DimAngle))

     

    • Like 1
×
×
  • Create New...