Jump to content

Letti R

Member
  • Posts

    69
  • Joined

Everything posted by Letti R

  1. Hello, unfortunately i cant share the file. This is my workarround, however i think it is not a good one: I wrote a script that copies a duplicate of the object to another layer and rotates it, so that its edges are parallel to x, y, z. Then i read the bbox values and put them into a data record and attached it to the original object. I think that this is a bad idea (in general) because the values in the records dont change automatically if the object changes, but in this case the objects dont get changed. Interestingly this script only takes some seconds to run and does not have memory issues at all, even if run multiple times. I wonder why the worksheet script takes so much longer, even if it does the same thing (or even less). Regards, Letti
  2. Hello, thank you for your reply @Tobias Kern. Unfortunately this does not work, because DEPTH gives wrong values. For example, lets say the object has x, y, z of 1, 2, 3. If WIDTH, HEIGHT, DEPTH would give scrambled values like, 2, 3, 1, this would be no problem because i can sort the values for example the way you pointed out. However i get results like 2, 3, 3 or 1, 2, 2 where one value (the value i get from DEPTH) is just wrong. Interestingly the VS function vs.Get3DInfo(obj) seems to always give the correct values. Regards, Letti
  3. Hello, i have a file with thousands of 3d ifc objects that are rotated in all kind of ways in 3d space. Now i want to sort them by their width, depth and height, so that i can sum them up. I tried to use the =WIDTH, = HEIGHT and =DEPTH functions in a worksheet, however the =DEPTH function gives wrong results when the objects are rotated in a specific way. But because i know that in this case the objects dimensions follow this rule x >= y >= z, i wrote a simple WS scipt that uses vs.Get3DInfo(obj) , which gives me the right results for the dimensions and simply sort them by their value. The problem is (apart from the ws beeing very slow, what was expected), that the script uses a lot of ram and even if the script is finished the used ram number of vw wont drop by much until i restart vw and if i run the script again it just adds to the previously used ram. See below the WS Script that im using. def get_xyz(): # infos obj = vs.WSScript_GetObject() choice = vs.WSScript_GetPrmStr(0) if isinstance(choice, str): choice = choice.lower() # script nHeight, nWidth, nDepth = vs.Get3DInfo(obj) # choice if choice == "h": vs.WSScript_SetResReal(nHeight) elif choice == "w": vs.WSScript_SetResReal(nWidth) elif choice == "d": vs.WSScript_SetResReal(nDepth) else: vs.WSScript_SetResStr("Choice error!") # run get_xyz() I think that the best solution would be to try to export the ifc objects (if possible) in a different way, so that the needed values are already attached to some record, but i am wondering if i am doing something wrong regarding the WS script? Regards, Letti
  4. Hello, if you have a simple solid object (e.g. a cube or an extrusion) this could be done by "extracting" the top or the bottom of the solid. But if your solid object gets just a little bit more complicated, for example two different sized cubes that are added together, this is not as straight forward anymore and there would be different ways, that would give different results. You could for example: want to have the bottom of the solid or just the top or the polygon of a section at a given height or all faces that you can see from the top all faces that you can see from the bottom and maybe more... Maybe you can provide us with an example file where you show some solids that you have, aswell as the expected results. Regards, Letti
  5. Hello, Computerworks already wrote a node to convert Symbols into Groups. You can download it here. Regards, Letti
  6. Hello, i think i struggle to undestrand what exactly you want to acchieve, because i think that width and height of the rectangle cant be choosen independently from spacing if the dircles need to fit into the corners like its drawn in the second screenshot. However this is how i would do a Marionette that distributes loci (a point) along a "line" that follows the x direction, when also the number of points that should be on this line are given: And the same Marionette but for the y direction: And a Marionette that combines the x and y direction into a grid (you have to set the "Mix" node to "Cross Reference" in the OIP of the node): Regards, Letti
  7. Hello, Have a Look at this Marionette, maybe this ist what you are looking for. Regards, Letti
  8. Hello, thank you for your reply. This is definitely an error i did not see. Luckily it should be an easy fix. And i am still hoping that someone has a better solution than this. Here is the code for the updated node: @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( 'n inches to string feet and inches' ) this.SetDescription( 'n inches to string feet and inches' ) #Input Ports n_inches_input = Marionette.PortIn( 0, 'n inches' ) n_inches_input.SetDescription( 'n inches' ) i_round_input = Marionette.PortIn( 9, 'i round inches to' ) i_round_input.SetDescription( "i round inches to" ) #OIP Controls b_zero_input = Marionette.OIPControl( 'ZERO.X -> .X', Marionette.WidgetType.Bool, True) b_zero_input.SetDescription('ZERO.X -> .X') b_half_input = Marionette.OIPControl( '.5 -> 1/2', Marionette.WidgetType.Bool, True) b_half_input.SetDescription('.5 -> 1/2') b_quarter_input = Marionette.OIPControl( '.25 -> 1/4', Marionette.WidgetType.Bool, True) b_quarter_input.SetDescription('.25 -> 1/4') #Output Ports s_output = Marionette.PortOut('s feet and inches (rounded)') s_output.SetDescription( 's feet and inches (rounded)' ) #BEHAVIOR def RunNode(self): #inputs n_inches = self.Params.n_inches_input.value i_round = int(self.Params.i_round_input.value) b_zero = self.Params.b_zero_input.value b_half = self.Params.b_half_input.value b_quarter = self.Params.b_quarter_input.value # script temp_feet_num = int(n_inches // 12) temp_inches_num = round(n_inches % 12, i_round) temp_feet = str(temp_feet_num) temp_inches = str(temp_inches_num) temp_inches = temp_inches.rstrip(".0") if b_zero: temp_inches = temp_inches.lstrip("0") if b_half: if temp_inches[-2:] == ".5": temp_inches = temp_inches[:-2] + " 1/2" if b_quarter: if temp_inches[-3:] == ".25": temp_inches = temp_inches[:-3] + " 1/4" elif temp_inches[-3:] == ".75": temp_inches = temp_inches[:-3] + " 3/4" temp_feet = temp_feet + "'" temp_inches = temp_inches + '"' temp_output = temp_feet + temp_inches if temp_inches_num == 0: temp_output = temp_feet if temp_feet_num == 0: temp_output = temp_inches temp_output = temp_output.strip() #outputs self.Params.s_output.value = temp_output Please note that i also set the default value of "i round inches to" to 9 decimal places. This should eliminate most errors that occur due to floating point inaccuracies whilst beeing precise enough. But you can ofcourse set the value to what you need. Also please note that i only tested this node while the document units were set to "feet and inches". If you use some other setting like "feet" the return value of the node will be wrong because the input will most likely not be in inches (the input number has to be in inches). Ofcourse i could write the node so that i checks the current unit settings of the document and converts what ever the input is into feet and inches, but i hope that i can avoid that for it would take some time. Regards, Letti
  9. Hello, i had a bit of fun with this problem and wrote a custom node that converts a number that is given in inches to a string that is in feet and inches. Because i dont know anything about imperial units i just tried to replicate what is shown in the OIP of the "Dim" node. But you can chose if you want to display .5" as 1/2" or as .5" (same with .25" and .75") in the OIP of the custom node via two checkboxes. I realy hope that this custom node is not necessary and that someone will show an easier (built-in) way, but until then this node should be a good workarround. To create the node just go into any node and replace the whole code with the code provided below. Please test the node and make sure that it works as intended! @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( 'n inches to string feet and inches' ) this.SetDescription( 'n inches to string feet and inches' ) #Input Ports n_inches_input = Marionette.PortIn( 0, 'n inches' ) n_inches_input.SetDescription( 'n inches' ) i_round_input = Marionette.PortIn( 0, 'i round inches to' ) i_round_input.SetDescription( "i round inches to" ) #OIP Controls b_zero_input = Marionette.OIPControl( 'ZERO.X -> .X', Marionette.WidgetType.Bool, True) b_zero_input.SetDescription('ZERO.X -> .X') b_half_input = Marionette.OIPControl( '.5 -> 1/2', Marionette.WidgetType.Bool, True) b_half_input.SetDescription('.5 -> 1/2') b_quarter_input = Marionette.OIPControl( '.25 -> 1/4', Marionette.WidgetType.Bool, True) b_quarter_input.SetDescription('.25 -> 1/4') #Output Ports s_output = Marionette.PortOut('s feet and inches (rounded)') s_output.SetDescription( 's feet and inches (rounded)' ) #BEHAVIOR def RunNode(self): #inputs n_inches = self.Params.n_inches_input.value i_round = int(self.Params.i_round_input.value) b_zero = self.Params.b_zero_input.value b_half = self.Params.b_half_input.value b_quarter = self.Params.b_quarter_input.value # script temp_feet = str(int(n_inches // 12)) temp_inches = str(round(n_inches % 12, i_round)) temp_inches = temp_inches.rstrip(".0") if b_zero: temp_inches = temp_inches.lstrip("0") if b_half: if temp_inches[-2:] == ".5": temp_inches = temp_inches[:-2] + " 1/2" if b_quarter: if temp_inches[-3:] == ".25": temp_inches = temp_inches[:-3] + " 1/4" elif temp_inches[-3:] == ".75": temp_inches = temp_inches[:-3] + " 3/4" temp_output = temp_feet + "'" + temp_inches + '"' if temp_feet == "0": temp_output = temp_inches + '"' temp_output = temp_output.strip() #outputs self.Params.s_output.value = temp_output Regards, Letti
  10. Hello, when you use the "Dim" node with "feet and inches" it converts the input to inches. I think the zeroes are due to conversion error. To get rid of them you just can use the "round" node to round the value to the desired ammount of digits. As someone who never uses imperial units i dont know if there is an easy solution (in Marionette with no custom nodes) if you want to show your dimensions in feet and inches combined. However here is a Marionette that puts together a string with the three dimensions all in inches: Regards, Letti
  11. Hello, you can find a node that gives you the name of a symbol in this file, that @DomC shared. It is in the "Object Info" column. However, there are other ways to replace symbols without the use of Marionette. Here is my preferred way of doing it: Select the Option "Symbol name" in this Tool Set all layers you want to search the symbol in to show Select the option "Show/Snap/modofy Others" for the leyers Click on one symbol with the Tool shown above Replace the symbols with the correct one via the "Replace" button in the object info palette. Regards, Letti
  12. Hello, changing the z-value of a 3d locus can be done with Marionette. Unfortunately i am not familiar with the "Text to loci" network that you are referring to, so i dont know if my example network is a good solution, but maybe you can point me in the right direction. This is how such a Marionette could look like: The Marionette works like this: At first it searches for all the 3D loci with the "Objs by Crit" node Then it gets the location of each found locus and finds its x, y and z values Then it does something to the z value (in this case i divided the z value by 10) After that the Network puts the coordinate values back together in the "Point 3D" node And finally sets the coordinates of the 3D locus to the new values Regards, Letti
  13. Hello @ASag I think Marionette and a little bit of programming go very well together, however for most things you dont need to write nodes / code on your own. In my answers i usually try to show multiple ways on achieving a set goal (because there often is not just one "correct" way on how to do stuff), so that you can choose the solution that suits you best. Me neither. Actually it was Marionette that got me into learning how to program. Regards, Letti
  14. Hello, your example reminds me of a problem @DomC posted some years ago. You can solve it by using the node "add sequence". But i would never recommend solving this kind of problems like this. It is a much better solution to just write your own node with a little bit of Python. For comparison, creating the Marionette (even though i had a rough idea of the solution) took me some hours. Writing the solution in Python code took me less than 10 minutes (it is just 3 lines of code). Here is what you want to do to create the Marionette: Sort the data. In this case the data is already sorted, so i skipped this part. If you need to sort your data you can write the id and the area of the object into tuples via the "Point 2D" node and then just sort the resulting list. Use the "add sequence" node on the list with the values. "add sequence" works like this: If you for example enter a list like [1,2,3,4] it will return this list [1,3,6,10] where each value is the sum of all values that came before. This means that the sums you want to calculate are somewhere in this list, you just have to pick the right ones and than subtract the sum of all the sums that came before. To get the indices of the correct sum you can make a list with the occurances of the ids, subract 1 from the first item of the list (because indices are counted from 0) and then use "add sequence" on this list, you get the indices of the sums that you want to pick from the list that we calculated in 2. Now you have a list where the first value is the first sum, the second value is the first sum plus the second sum, the third value is the first sum plus the second sum plus the third sum and so on Now you have to create a list with the correct values that you have to subtract from the sums we just picked. To do that we rotate the list from 5. by -1 and set the first item of the list to 0. Like this you subtract nothing from the first sum we picked (because it already was correct), than you subtract the value of the first sum from the second sum, than the value of the first two sums from the third sum and so on. And here is a screenshot of this Marionette: And here is how i would do the same thing (but better) with some Python code when writing a custom Marionette node: @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( 'sum by id' ) this.SetDescription( 'sum by id' ) #Input Ports list_values_input = Marionette.PortIn( [] ) list_values_input.SetDescription('The values input list') list_ids_input = Marionette.PortIn( [] ) list_ids_input.SetDescription('The ids input list') #OIP Controls #Output Ports list_sums_values_output = Marionette.PortOut() list_sums_values_output.SetDescription('The sums list') list_sums_ids_output = Marionette.PortOut() list_sums_ids_output.SetDescription('The ids list') #BEHAVIOR this.SetListAbsorb() def RunNode(self): #inputs list_values = self.Params.list_values_input.value list_ids = self.Params.list_ids_input.value #script sums_dict = {} for id, value in zip(list_ids, list_values): sums_dict[id] = sums_dict.get(id, 0) + value #outputs self.Params.list_sums_values_output.value = list(sums_dict.values()) self.Params.list_sums_ids_output.value = list(sums_dict.keys()) (i know this is more than 3 lines of code, but the code that does something with the data is only between "#script" and "#outputs". Everything else ist just the node appearance and so on) Regards, Letti
  15. Hello, @Pat Stanford i think so. Ofcourse Texture only Shows for 3d Objekts Regards, Letti
  16. Hello, if your active class has set all the attributes as "set automatically by class" when you run the Marionette, than all created objects will be created in this class with all the Attributes of the class. Could this be a a solution for you? Regards, Letti
  17. Hello, i dont know if this is a known issue or if im doing something wrong, but if i arrange some polygons that do not overlap, in a specific way and try to add them via the menu command or [ctrl] + [k], than some of them dissappear. If i try the same thing in vw23 nothing happnes (thats what i would expect). Regards, Letti
  18. Hello, i think this is due to a flaw inside the "Extrude" node and not the "If" node. In short, the "Extrude" node uses a function that returns the last created object. Normaly this would give you the handle to the newly created extrusion. However, if you have no input value in "hObj" the node wont create an extrusion. But the function that returns the last created object will still return the last created object. In that case this is a group that gets created by the "Create Rectangle" node automaticaly. And that is why you have some strange, unexpected output to the "Extrude" node. Here are some ideas to fix this: Fix the code of the "Extrude" node in a way that the "problematic" part of the node is not run, if there is no input value in "hObj". For the code of such a node please see the code i provided below. @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( "Extrude" ) this.SetDescription('Extrudes a 2D profile from a bottom Z value to a top Z value') #Input Ports profile = Marionette.PortIn( vs.Handle(0), 'hObj' ) profile.SetDescription('The 2D object to extrude') top = Marionette.PortIn( 5, 'nTop' ) top.SetDescription('The Z value of the top of the resulting extrusion') bottom = Marionette.PortIn( 0, 'nBot' ) bottom.SetDescription('The Z value of the bottom of the resulting extrusion') #OIP Controls #Output Ports xtrds = Marionette.PortOut('hObj') xtrds.SetDescription('The resulting extrusion') #BEHAVIOR this.SetLinksObjects() def RunNode(self): #inputs prof = self.Params.profile.value top = self.Params.top.value bottom = self.Params.bottom.value #script extr = vs.Handle(0) if prof != vs.Handle(0): vs.BeginXtrd(bottom, top) vs.CreateDuplicateObject(prof, vs.Handle(0)) vs.EndXtrd() extr = vs.LNewObj() (ok, start, rotX, rotY, rotZ) = vs.GetEntityMatrix(prof) vs.Marionette_DisposeObj(prof) vs.SetEntityMatrix(extr, start, rotX, rotY, rotZ) #outputs self.Params.xtrds.value = extr Place the "If" node somewhere after the extrusion, so that you always create the extrusion and move it arround and so on. Than you delete it and make a duplicate of the extrusion only if the condition is true. This takes advantage of the fact that the "Delete" node kind of only deletes objects at the very end of the Marionette network, no matter where they are put in the Marionette network. And here is how this Marionette could look like: Regards, Letti
  19. Hello, so i finished my Plug-in. Now i want to put it in the Forums to share it (for free). Is there something i have to consider to not run into legal problems because i used the VW SDK, or can i just put it out there for others to use? Regards, Letti
  20. Hello, line 3 in your script does not call (run) the function, but assigns the (whole) function to the variable "filename". it should work if you change line 3 to: filename = vs.GetFName() Like that you actually call (run) the function and therefore assign the return value of the function to the variable "filename". Regards, Letti
  21. Hello, what i forgot to write the last time is, that the last node / code i posted also works if the 3D part of the existing tree PIO is disabled. From my understanding (and im hoping someone will correct me if im wrong) Marionette Networks are not version specific if you only use the nodes that are provided from Vectorworks. Costum Python scripts (and therefore custom Marionette nodes) for Vectorworks should also not be strictly version specific, but i once had to change some parts in a custom script because it stopped working on the newer version. My guess ist you should be able to use this custom script / the custom nodes atleast for some versions. Regards, Letti
  22. Hello, @Marissa Farrell The problem with the 3D info is, that you can deactivate the 3D part of this PIO and than the 3D info will return a hight of 0. Which ofcourse is correct, but not what we need. @Michal Zarzecki I wrote my solution (as pointed out in my previous post) into a node. Please do some tests, if this node gives the correct results. I also added a "deviation angle" that you can set, if north is not "straight up". @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( 'draw_shadow_of_PIO_"Existing Tree"' ) this.SetDescription( 'draw_shadow_of_PIO_"Existing Tree"' ) #Input Ports obj_in = Marionette.PortIn( vs.Handle( 0 ), 'hObj' ) obj_in.SetDescription( "The input object" ) angle_in = Marionette.PortIn(0, 'nAng') angle_in.SetDescription( "The deviation angle, if north is not in y direction" ) #OIP Controls #Output Ports h_out = Marionette.PortOut('obj') h_out.SetDescription( "obj" ) #BEHAVIOR def RunNode(self): # INPUTS obj = self.Params.obj_in.value angle = self.Params.angle_in.value # FUNCTIONS def get_parametric_record_dict(handle): record_handle = vs.GetParametricRecord(handle) record_name = vs.GetName(record_handle) record_dict = {} field_number = 1 while vs.GetFldName(record_handle, field_number) != "": record_dict[vs.GetFldName(record_handle, field_number)] = vs.GetRField(handle, record_name, vs.GetFldName(record_handle, field_number)) field_number += 1 return(record_dict) def selection_to_handle(): handles = [] def callback_selection_to_handle(handle): handles.append(handle) vs.ForEachObject(callback_selection_to_handle, "(NOTINDLVP & NOTINREFDLVP & (VSEL=TRUE))") return(handles) # SCRIPT # only run script if the object is a PIO with the parametric record "Existing Tree" attached if vs.GetTypeN(obj) == 86: if vs.GetName(vs.GetParametricRecord(obj)) == "Existing Tree": # save unit info of document temp_style, temp_prec, temp_dimPrec, temp_format, temp_angPrec, temp_showMark, temp_dispFrac = vs.GetPrimaryUnitInfo() # set unit of document to mm and high precision and dont show unit mark vs.PrimaryUnits(7, 10, 10, 2, 7, False, False) # get PIO info center = vs.GetSymLoc(obj) height = float(get_parametric_record_dict(obj)["Height"]) # draw arc and lines and compose them vs.DSelectAll() drawn_ojects = [] vs.ArcByCenter(center[0], center[1], height, 135, -135) drawn_ojects.append(vs.LNewObj()) dir_vector_arc_start = vs.Ang2Vec(135, 1) vs.MoveTo(center) vs.LineTo((center[0] + dir_vector_arc_start[0] * height, center[1] + dir_vector_arc_start[1] * height)) drawn_ojects.append(vs.LNewObj()) vs.MoveTo(center) vs.LineTo((center[0] + height, center[1])) drawn_ojects.append(vs.LNewObj()) vs.DoMenuTextByName('Compose', 0) poly_line = selection_to_handle() if len(poly_line) != 1: for item in poly_line: vs.DelObject(item) output_object = None else: output_object = poly_line[0] # rotate polyline, to account for the fact that north is not always in direction (0, 1) vs.HRotate(output_object, center, angle) # set unit of document to what was seved before vs.PrimaryUnits(temp_style, temp_prec, temp_dimPrec, temp_format, temp_angPrec, temp_showMark, temp_dispFrac) # OUTPUTS self.Params.h_out.value = output_object Regards, Letti
  23. Hello, can you provide us with an example file with some of the trees aswell as the Marionette? Regards, Letti
  24. Hello @Michal Zarzecki, unfortunately i dont know how to get the values directly from the ET Plugin objects. However here is what i tried (with Python and Marionette combined), but i do NOT think that this is the correct way to do it: depending on how the PIO is built, you can maybe get the information from a datarecord that might be attached to the PIO. You can do this with the "Get Record Field" node. Therefore you will have to look up the name of the record and the name of the field. The problem here is, that the value you get from the record depends on the document units settings. Here are the problems: If you are reading the height value of a 49m tall tree with this method, while your document units are set to kilometers with a displayed precision of 1 (0.1), you will get 0km as the height of the tree, because the value its rounded down. The value is given as a string. If the document does not display the unit mark, this string can be easily turned into a float number with the "float()" function of Python, but not if the document does display the unit mark. I think i am missing something here, because i would assume vectorworks has a function to convert such strings into floats, but i have not found that function yet. So if i had to "solve" this now i wold do it like this (but i really hope someone else will show us the proper way of doing this): Get the current unit info of the document with the function "vs.GetPrimaryUnitInfo()" and "store" it Set the unit settings of the document to what is needed (mm, with high precision and dont show unit marks) with the function "vs.PrimaryUnits()" Read the data from the record and convert it to a float with the function "float()" Draw the arc and the lines and compose them Set the unit settings to what they were before I hope that someone else can provide you with a better idea and better information, because i am clearly way out of my depth here. Regards, Letti
  25. Hello, assuming your Tree is a 3D symbol and not a PIO, these steps might help: Regarding #1.: I think you can get the 3D height of your 3D symbol by subtracting the output "nBotz" from the output "nTopz" of the "Get 3D Info" Node. Unfortunately this Node seemes to be brocken (hopefully only on my computer). If not, here is the code for a custom node that only outputs the 3D height. To create the node, replace the code of any existing node, with the code below. @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( 'get 3D height' ) this.SetDescription( 'get 3D height' ) #Input Ports x = Marionette.PortIn( None, '3D Object' ) x.SetDescription( "3D Object" ) #OIP Controls #Output Ports y = Marionette.PortOut('n') y.SetDescription( "n" ) #BEHAVIOR def RunNode(self): #inputs x = self.Params.x.value #script _, __, n = vs.Get3DInfo(x) #outputs self.Params.y.value = n (please only use this, if the "Get 3D Info" node does not work). Regarding #3.: Given you have drawn the correct arc, you can than turn this into a closed polyline by drawing a line from the center point of the arc to the start point of the arc and another line from the center to the end point of the arc. To create a polyline from that just put the arc and the two lines into the "compose" node. And this is how this Marionette could look like: Regards, Letti
×
×
  • Create New...