Jump to content


  • Posts

  • Joined

  • Last visited

Posts posted by DomC

  1. "C:\\Temp\\Output.csv" would work also
    I would strongly recommend,  always create path strings with the path module. So it is as proof as it can be and this works also cross-platform:

    import os
    path = os.path.join('C:', 'Temp', 'Output.csv') 
    # or even more propper
    path = os.path.join('C', os.sep, 'Temp', 'Output.csv') 


    • Like 2
  2. I would say maybe a rookie error:

    #allowed >> produces no errors caching is off
    vs.SetPref(412, True)  # turn off caching
    #don't do that >> seems to breaks the vs modul or ignore newer methods
    vs.SetPref(412, False)  # turn off caching
    #allowed >> produces no errors caching is on
    #vs.SetPref(412, True)  # turn off caching


    • Like 1
  3. Hello

    I have a Vectorscript tool (not Event driven 2024). This tool rund a python module. The tool is working so far.
    code of the tool

    PROCEDURE ClickCallback;
    	x1, y1 : REAL;
    	path : STRING;
    GetPt(x1, y1);
    PythonExecute('import dev.tool.mod.poly2PIO');
    RUN( ClickCallback );

    the module poly2PIO runs this code:

    import vs
    import os
    #vs.SetPref(412, True) #turn off caching
    with open(os.path.expanduser('~') +'/file_vst.txt', "w", encoding='utf-8') as f:
    # error here isOnLine undefined method   
    isOnLine = vs.PtOnLine((0,0), (-10, 0), (10, 0), 0.00001)

    everything and also vs.PtOnLine() is working. If i use that tool.

    But then, if i run run another vso (Event Enabled path object) it seems that the vst does not have the same vs-function anymore as before.
    Also if i use a standard vso (Script Based like the Rolling Stairs) the vs class in my vst do not contains the same methods anymore.


    import vs
    #from datetime import datetime
    import json
    import math
    import copy
    #import uuid
    import os
    import base64
    import time
    #import urllib.request
    import code.common.controler as controler
    #vs.SetPref(412, False)  # turn off caching
    with open(os.path.expanduser('~') +'/file_vso.txt', "w", encoding='utf-8') as f:

    The functions in the  vst does not work anymore. Also other vst's have the same issues now. 

    The special thin i do is, that i run a pascal script and call a python after i click in the drawing. For further debug i write a text file with dir(vs) which lists all methods of the vs class.

    If i then compare the two files, i can see, that the dump written from file_vst.txt has much less methods available (left) as the file dumped from the vso?
    Somebody knows if this is a normal limitation or if it is worth for further diving in? 

    I think not many devs wrapps a pascal around a python script so maybe this is a normal limitation that the methods freezed on a specific version or it is just a bug?

    It looks some-like, that methods after 2014 are not included now for my vst. 
    Anybody knows what could going on?

    Here a screenshot from the method list compared with diff Merge

  4. Marionette is a Programming-Tool. In such tools wer are in a place to have the force to produce wonderful artwork or unwanted behaviors (crashes?) and comparative we are mostly alone with our artworks. And thanksfully there are forums like this other people can help for bringing those projects to success.

    I took a look a the Marionette Control-Point and agree,  there is some space for improvement:

    1. Fixing Axis
    I think @Gregi wantet to fix one axis. This is not possible because the position of the control-point  comes from the Mouse not from a value inside the script. This value is just a default value. To change the control point by script itself is theoretically possible to manipulate the Record of the Marionette Node where the position of the point is stored but it would be very hard (Because also the position of the PIO is involved). So this is not a bug it works the way the feature is designed.

    2. Moving 3D Point in Standard View.
    If I am in a standard view (front right etc.). Lets say from right and I move the point in z-Direction, the x value from the point snaps on the working plane and the x-value gets 0. Which can maybe produces issues in the script inside the PlugIn. So working plane should be used or unwanted values have to be intercepted inside the script (which is always the most costly part). So if the script crash because the width of the pio change to 0 it is not a bug in the code of the 3D modification point. It is just an unhandled error. Error can be handled on different places. In shoft:
    OK, I can see, that the reshape behaviour of this modi-point can be improved and i will report this as an enhancement Request.

    3. Position of the 3D Point outside of PIO geometry.
    If the Modification Point is the most outside object in a PlugIn. And not geometry is there, the position of the Point somehow is calculated wrong relatively to the object. So in our test with Extrudes etc. it always works as expected. But If the extrude is smaller than the bbox of the modipoint and the 0-point it somehow Fails.
    I think, this is definitely a bug. I will report.

    4. Position of the 3D Point with layer elevation

    I think this is already known but I try to give it an impact.

    Generally, there are not much additional layers of error-handling between marionette and the Script Engine. I think the more "idiot-proof" something is made the weaker it is. Not 100% the truth (It could be possible to have both). But this is the way I am still pleased even if my script crashes 😁

  5.  OK, the part of the code which I will not paste is the part how you update your certificates by the script on mac computers to make python accept them to communicate over TLS (SSL) (https instead of http).

    caution should be exercised. 

  6. The forum allows me not to paste some code ... I think maybe for security reasons.

    Or your testing api request:

    import urllib.request
    import json
    api_url = "https://jsonplaceholder.typicode.com/todos/1"
    result_string = ''
    with urllib.request.urlopen(api_url) as f:
       result =  json.loads(f.read().decode('utf-8'))


    An other real example with a http request url, if you can't get https to work


    import urllib.request
    import json
    import math
    #dezimalgrad in minuten und Sekunden umwandeln (copy paste aus dem Netz)
    def deg_to_dms(deg, type='lat'):
    	deg = float(deg)
    	decimals, number = math.modf(deg)
    	d = int(number)
    	m = int(decimals * 60)
    	s = (deg - d - m / 60) * 3600.00
    	compass = {
    		'lat': ('N','S'),
    		'lon': ('E','W')
    	compass_str = compass[type][0 if d >= 0 else 1]
    	return '{}º{}\'{:.2f}"{}'.format(abs(d), abs(m), abs(s), compass_str)
    h = vs.FSActLayer()
    x = 2600000
    y = 1200000
    z = 500
    #Nimmt Position von aktiviertem Hilspunkt, Objekt oder Symbol sonst Bern Sternwarte als Vorgabewert
    if h != vs.Handle(0):
    	x,y,z = vs.GetLocus3D(h)
    #vs.AlrtDialog(str(vs.GeogCoordToVWN(x, y)))
    vs.AlrtDialog(str(vs.VWCoordToGeog(x, y)))
    xPt, yPt, zPt = vs.PtDialog3D('LV95 Koordikante zu WGS84', x, y, z)
    #Abfragestring auf dem Geportal direkt im Internet
    url = "http://geodesy.geo.admin.ch/reframe/lv95towgs84?easting="+str(xPt)+"&northing="+str(yPt)+"&altitude="+str(zPt)+"&format=json"
    result_string = ''
    with urllib.request.urlopen(url) as f:
       result =  json.loads(f.read().decode('utf-8'))
    #Antwort vom Geoportal interpretieren und umwandeln in json
    result_string = str(result['easting']) + '\r' + str(result['northing']) + '\r' + str(result['altitude'])+'\r\r'\
    'Grad: '
    result_string += '\r'
    result_string += 'Grad Minuten Sekunden: \r' + deg_to_dms(result['easting'], 'lat') +'  ' + deg_to_dms(result['easting'], 'lon')


  7. Oh, this is an old code and not very nice one. Without testing:

    1. The vs.GetType should replaced by:


    2. The node returns not the Symbol Name. That means the node itself insert the Symbol. In fact it looks, like the output just returns the last inserted symbol. Which means the node should insert the symbols in the number of rows and should do the job but not returning the resulting symbol handles. With that node, you can't use the output useful disconnect symbol node and print debug node.

  8. Hi
    A possible workaround I once used is:
    1. I had a big library document with much of Elements. Importing, reading informations, delete/purge would be time-consuming.
    2. On the library document running a script after every change. The Script dumps the needed informations to a file which contains the metha-data from the library file
    3. Or alternatively dump to a text object inside a symbol (I think does not work with too much data because of text size limitation maybe) and just import that index symbol to read source data
    4. If the process should be automated there could be used a custom "save" command which dumps the data automatically before saving the file


    import json
    def update_library():
        log_info = []
        log_info_sym = []
        counter_sym = 0
        counter_xg = 0
        listID, numItems = vs.BuildResourceList(16, 0, '')  # Symbols
        dumps = {}
        SymbolLibraryDump = None
        for i in range(1, numItems + 1):
            counter_sym += 1
            res_name = vs.GetNameFromResourceList(listID, i)
            sym_h = vs.GetResourceFromList(listID, i)
            if sym_h != vs.Handle(0):
                t = vs.GetTypeN(sym_h)
                found_cabinet = functions.get_XG_Cabinet_h_in_symbol(sym_h)
                if found_cabinet:
                    dump = functions.attach_infos(sym_h, found_cabinet)
                    dumps[res_name] = dump
                    counter_xg += 1
                    log_info_sym.append(f"{res_name} {dump}")
                    # log_info.append('kein Korpusmöbel gefunden')
                if 'SymbolBibliothekDump' in res_name:
                    SymbolLibraryDump = sym_h
        if SymbolLibraryDump:
        log_info.append('Daten an Symbole anghängen:')
        log_info.append(f"{counter_sym} Symbole im Dokument-Zubehör")
        log_info.append(f"{counter_xg} Symbole mit KM im Dokument-Zubehör")
        log_info = log_info + log_info_sym
        field_value = json.dumps(dumps, indent=1)
        vs.BeginSym('SymbolBibliothekDump' + vs.Date(2, 2))
        log_info.append('Dump Symbol generiert (index zum auslesen aus anderen Dateien)')
        log_info = rename_symbols(log_info)
        return log_info


  9. Hi

    After specific Manipulation there is a need of ResetObject. Some commands included the proper Reset of the Result some not. In my Script you can see, that if you remove the reset of the parent object, the visible geometry is different from what you get if you edit the path extrude and exit it (same effect as resetting). If for your example it is not needed (because you do other actions with the result which may reset the objects itself) you can delete it. Beside that, it would not have a big speed impact because in a script every reset is added together if you reset 10 times it will reset just once anyway.

    "Punktkörper" is called "Mesh". 
    Not sure I  understand this question. You mean the result of your solid boolean operation is a mesh? Normally this is not the case at all. The Result of using solid boolean node should be a generic solid not a mesh. Even if you use a mesh in a solid operation the result is normally converted to a solid not a mesh.

    • Like 2
  10. vs.ResetObject() actually regenerates the pio and change the geometry after with vs.SetRField() parameters are changed and I tried also 1167. The specific thing here is, that the cabinet contains also plugins (Custom Parts) which maybe are not reseted as expected and the additional relations of fittings interact with the parts and not directly with the cabinet maybe. I will give accessing the sub-pios of the cabinet and if that not will not be of use i will wait for another solution. 

    Already I am in contact with the genius devs from extragroup but there are always some other more important tasks to address so I wondered if i could bypass this limitation on the script-side.

  11. Thank you for that feedback Hippocode and JBenghiat. 
    Of course you are absolutely right. I should have been more specific about what I'm trying to do in the first place. I have a script that reshapes interiorcad cabinets together with custom parts and milling pios which are attached to that objects.
    Unfortunately, applying a vs.ResetObject() to these objects disconnect their connections to the fittings and other associated elements. The PIO itself runs a code, that is able (if nothing went wrong) to keep that relations. 
    So there are some options:
    1. The developper of that PIOs implements an API which can be triggered by vs.ResetObject()
    2. I find a way to fire the right events to that PIO so that it is forced to regenerate (It also has a button to update)
    3. It is maybe a bug in the vs.ResetObject()

    #1 is maybe the best option but if it can be controlled by the script itself it would be seen prefered.
    Here what the script does. It is open source and addresses a very often mentioned requirement from customers.


  12. Hello
    The question is maybe naive. As I know, there are Events that happens while the usage or code-run of an object. As example 3 for reset, 5 initProberties etc. Is it possible at all, to fire such an event on a PIO from outside. Or trigger the PIO with something more controlled than vs.ResetObject()?

    To be specific:
    1. Fire a standard-Event like 3 (I think vs.ResetObject() does that) or 41 or 43?

    2. Or a custom event. Lets say I have a button ID inside the PIO like:
    (theEvent, theButton) = vs.vsoGetEventInfo( )

    With simple words: Can I push the button from outside with something other than the Mouse cursor?


  13. Hello

    So far it works implementing a Tool in the Workspace and Creating a new Palette Group. So far it works for path Objects or Point Objects (vso). Event Enabled or not Event Enablet. But I can't get it to work with a VST. I tested with this tool "Blindfront" This one here:

    As soon as I manually drag it into the workspace the tool works. and also after that the tool i added with the script works. So far the workspace itself looks identical if i drag manually the tool in the workspace as if i create the tool by script. I think I am missing some important parameter here.
    The Tools is placed into the palette and is shown ad expected but not the tool can't be selected. 

    Anybody can help?

    Here My small test:

    #Get Group Palette by Index 1 (0 is Construction palette)
    pName = vs.ws2GetToolAt("", 1)
    result, outDisplayName, outShortcutKey, outShortcutKeyModifier, outResourceID = vs.ws2GetToolInfo(pName)
    vs.AlrtDialog('Put Tool in ' + outDisplayName)
    #The .vst is already in the Plug-Ins Folder
    # id of group palette
    tsPath = pName #outDisplayName#GetPalettePath('Werkzeuggruppen')
    tsName = 'NewPalette'
    tsNameUniversal = tsName #can be a uuid or just here for Testing 'NewPalette'
    vs.ws2CreateToolSet(tsPath, tsNameUniversal, tsName, '')
    path_new_tool = '/'.join([tsPath, tsNameUniversal])
    # Add tools
    ok = vs.ws2CreateTool(path_new_tool, "Blindfold", 3) #Don't know what this index 3 is used for
    vs.ws2CommitChanges(False, False)
    bWorked = vs.wsEditEnd(False)


  14. It looks for interaction with the VW API it would need a very different kind if integration of the python language or additional APIs which are able to communicate with the python script. Which maybe needed a very big effort or would not pay out the effort. I think the reason it not, that python can't pause.

    If i draw an object and then get after that some informations from that object, somehow the object was created in Vectorworks (not drawn, but minimum defined). So The python script here waits for the Object created by the VW binary and so i think it could be possible the python script also can await the user interaction. So I guess it user interaction may be is not technically limited because of python itself. 

  15. Also tried to solve that issue. But without a big success. You can run the complete python as a callback of GetPt. But Nested callbacks are not possible. So just one click can be done.

    My Workflow is to create a main script as a Vectorscript. I collect the clicks and user interaction and then run the python script inside this script. Also it is possible to run several python scripts inside the pascal wrapper. So far no better solution I can see.

    It gets two clicks in the drawing and draw a preview rectangle. Then save the result in the value reprository, where the python script after that can load the data. It you want to pause somewhere your python script and make a user interaction this workflow is not possible. 

    Example for the Pasca-Script that calls the python script:


    PROCEDURE Reshape;
    	h1, h2, h3 :HANDLE;
    	scriptName, values2python : STRING;
    	py, res: BOOLEAN;
    	pt1, pt2 : POINT;
    	pt, pt_tl, pt_br : POINT;
    	plan_rotation : REAL;
     FUNCTION TempToolCallback(action, msg1, msg2 : LONGINT) : LONGINT;
             TempToolCallback := 0;
             CASE action OF
                 3: BEGIN {kOnToolDoSetupEventID}
    		             vstSetHelpString ( 'definiere rechteckigen Umformbereich auf' );
                 103 : BEGIN {kToolDrawEventID}
                     vstGetCurrPt2D( pt.x, pt.y );
                     pt_tl.x := Min(pt.x, pt1.x);
                     pt_tl.y := Max(pt.y, pt1.y);  
    				 pt_br.x := Max(pt.x, pt1.x);
                     pt_br.y := Min(pt.y, pt1.y);
                     vstDrawCoordLine( pt_tl.x, pt_tl.y, pt_br.x, pt_br.y );
                     vstDrawCoordRect( pt_tl.x, pt_tl.y, pt_br.x, pt_br.y );
    	plan_rotation := GetPrefReal(93);
    	IF (plan_rotation > 0) 
    	AlrtDialog('Dieser Befehl funktioniert nicht in der Planrotation');
    	GetPt(pt1.x, pt1.y);
    	RunTempTool( TempToolCallback, FALSE );
    	{using $ as Delimiter, because in Germany comma is reservated for decimal seperator}
    	Rpstr_SetValueStr('values2python', Concat('(',Num2Str(6, pt_tl.x),'$',Num2Str(6, pt_tl.y),')','$','(',Num2Str(6, pt_br.x),'$',Num2Str(6, pt_br.y),')'));
    	res := GetScriptResource('Subscript', s, py);


    • Like 1
  16. Thanks for your worthwhile feedbacks. So far my strategy (But I do not know if it will work reliable enough)

    1.  Inside the Section there is a Section Line Type 86 that contains the position of the section and a direction matrix
    2. There are Informations on the Section VP directly that tells, if section is horizontal or vertical
    3. The VP cache Group contains the projected geometry of the elements 
    In combination of this 3 information it is possible to compare the Design-Layer coords and the section coords.

    I build some functions to test what works. So far i see the following stumbling stocks:
    - There are differences if the horizontal section was created from clip cube or from sectioning a VP
    - VP cache has not the same informations if the objects behind section plane are disabled
    - So far my function are not secure to return always the right informations

    Prototype Method to get Section Line:
    Gets Section Informations

     = vs.FSActLayer()
    rname = 'Section Line2'
    def create_field_value_dict(obj, rname):
    	rhandle = vs.GetObject(rname)
    	num_fields = vs.NumFields(vs.GetObject(rname))
    	fdict = {'found' : True}
    	for i in range(1, num_fields+1):
    		fname = vs.GetFldName(rhandle, i)
    		value = vs.GetRField(obj, rname, fname)
    		fdict[fname] = value
    	return fdict
    def get_sl_in_vp(vp):
    	obj = vs.GetVPGroup(vp, 2)
    	groups = []
    	while obj != vs.Handle(0):
    		t = vs.GetTypeN(obj)
    		if t == 11:
    		obj = vs.NextObj(obj)
    	section_line = vs.Handle(0)
    	for group in groups:
    		obj = vs.FInGroup(group)
    		while obj != vs.Handle(0):
    			t = vs.GetTypeN(obj)
    			n = rname
    			if t == 86 and n == 'Section Line2':
    				section_line = obj
    			obj = vs.NextObj(obj)
    	return section_line
    section_line = get_sl_in_vp(h)
    m = vs.GetEntityMatrix(section_line)
    fdict = create_field_value_dict(section_line, rname)
    fdict['entity_matrix'] = m
    fdict['is_horizontal'] = vs.GetObjectVariableBoolean(h, 1048)

    Prototype Method to get VP cache infos:
    This Function puts a 3D Locus on the Design-Layer in Projection to the Objects vertical to Section Plane.

    h = vs.FSActLayer()
    rname = 'Section Line2'
    lh = vs.GetLayerByName('Konstruktionsebene-1')
    fdict = {}
    def create_field_value_dict(obj, rname):
    	rhandle = vs.GetObject(rname)
    	num_fields = vs.NumFields(vs.GetObject(rname))
    	fdict = {'found' : True}
    	for i in range(1, num_fields+1):
    		fname = vs.GetFldName(rhandle, i)
    		value = vs.GetRField(obj, rname, fname)
    		fdict[fname] = value
    	return fdict
    def get_poly_in_vp(vp):
    	obj = vs.GetVPGroup(vp, 3)
    	groups = []
    	while obj != vs.Handle(0):
    		t = vs.GetTypeN(obj)
    		if t == 11:
    		obj = vs.NextObj(obj)
    	objs = []
    	for group in groups:
    		obj = vs.FInGroup(group)
    		while obj != vs.Handle(0):
    			t = vs.GetTypeN(obj)
    			if t in [5, 21]:
    				if vs.GetFPat(obj) != 0:
    			obj = vs.NextObj(obj)
    	return objs
    objs = get_poly_in_vp(h)
    fdict['polys'] = objs
    fdict['is_horizontal'] = vs.GetObjectVariableBoolean(h, 1048)
    def get_center_points(objs):
    	center_points_3D = []
    	for obj in objs:
    		p = vs.HCenter(obj)
    		if fdict['is_horizontal']:
    			m_pt = p[0], p[1], 0
    		else: #vertical section y = z
    			m_pt = p[0], 0, p[1]
    	return center_points_3D
    center_points_3D = get_center_points(objs)
    for p in center_points_3D:			
    	locus = vs.LNewObj()
    	#vs.SetParent(obj, lh)
    	vs.SetParent(locus, lh)


    And next prototype Method can check if the center point of the section cache-geometry is inside the parent object on the design-layer.

    lh = vs.ActLayer()
    ln = vs.GetLName(lh)
    espsilon = 1
    c = "(INOBJECT & NOTINDLVP & NOTINREFDLVP & ((L='l-dummy') & (PON='XG Custom Part')))"
    c = c.replace('l-dummy', ln)
    custom_parts = []
    def add_handle(h):
    vs.ForEachObject(add_handle, c)
    def is_pt3_in_cube(pt, cube, epsilon):
    	x, y, z = pt
    	xmin2, xmax2, ymin2, ymax2, zmin2, zmax2 = cube
    	# vs.Message(str(cube))
    	overlapx = overlapy = overlapz = False
    	if xmin2 - epsilon < x < xmax2 + epsilon:
    		overlapx = True
    	if ymin2 - epsilon < y < ymax2 + epsilon:
    		overlapy = True
    	if zmin2 - epsilon < z < zmax2 + epsilon:
    		overlapz = True
    	return min(overlapx, overlapy, overlapz)
    def bbox3D(p, zValue, height, width, depth):
    	centerPt = (p[0], p[1], zValue)
    	botZ = zValue - depth / 2
    	topZ = zValue + depth / 2
    	w2 = width / 2
    	h2 = height / 2
    	bbox_bl = p[0] - w2, p[1] - h2
    	bbox_tr = p[0] + w2, p[1] + h2
    	bbox_tl = p[0] - w2, p[1] + h2
    	bbox_br = p[0] + w2, p[1] - h2
    	xmin = bbox_tl[0]
    	xmax = bbox_br[0]
    	ymin = bbox_br[1]
    	ymax = bbox_tl[1]
    	zmin = botZ
    	zmax = topZ
    	return [xmin, xmax, ymin, ymax, zmin, zmax], ((xmin, ymin, zmin), (xmin, ymax, zmin), (xmax, ymax, zmin), (xmax, ymin, zmin), (xmin, ymin, zmax), (xmin, ymax, zmax), (xmax, ymax, zmax), (xmax, ymin, zmax))
    #h = vs.FSActLayer()
    h = custom_parts[0]
    parent = vs.GetParent(vs.GetParent(h))
    BOOLEAN, offset, rotationXAngle, rotationYAngle, rotationZAngle = vs.GetEntityMatrix(parent)
    (p, zValue) = vs.Get3DCntr(h)
    (height, width, depth) = vs.Get3DInfo(h)
    cube, pts = bbox3D(p, zValue, height, width, depth)
    is_in_cube = is_pt3_in_cube(pt, cube)
    for pt in pts:
    	x, y, z = pt
    	x = x + offset[0]
    	y = y + offset[1]
    	z = z + offset[2]
    	pt = x,y,z

    Additional it would help to check classes and attributes to be sure not targeting an invisible object etc.
    With this 3d Methods it is possible to manually make a relation between a coord point inside the section annotations and the object on the design-layer. 

    The shakiness of this strategy here is, that the Method of traversing the VP to collect Section-Line and Section Geometry is maybe not reliable enough or can wreck after an VW Update.
    Further it can be tricky to get positions of rotated and nested object rotations. That would be the the hardest part. Even the Data-Tag hat some (fixed) issues to find the Objects in every situation so I do expect that would be really expensive to solve that.

    Also I saw, that I corrupted some VP after working on them with this scripts. But I think this was because of creating Test-Marker-Objects inside the VP. At the end, this would be a "read-only" access to the VP which should not corrupt anything -SHOULD.

    I will pause working on that specific feature ☺️


  • Create New...