Jump to content

Letti R

  • Posts

  • Joined

Everything posted by Letti R

  1. Hello, is there a way to get the SDK for VW23 SP1, because when i download the SDK it is still version VW28.0.0. Regards, Letti
  2. Hello, i compiled my tool with the 2023 SDK without changing anything in the code (the tool works without any problems in VW 2022). The compilation ends without an error, but when i tried to run the tool, VW crashed. My VW version ist 2023 SP1, and the SDK version is VW28.0.0. I think the crash occurs when tool points are cleared, but im not sure. I can get rid of crashing VW by using "VWToolDefaultLine_EventSink" instead of "VWToolDefault_EventSink", but than i dont know how to remove the 3 standard buttons in the mode bar . Also i noticed that everytime i crash VW a crash dump file gets created, is there a way to open these files and get meaningful information from them? And how do i find out what exactly causes VW to crash? Or would it be better to remove the standard buttons that come with "VWToolDefaultLine_EventSink"? Regards, Letti
  3. Letti R


    Hello, after some changes and bug fixing, i am happy to share the tool with you that i wrote for @hgarnade. The tool automates the creation of “Dog-Bone” and “T-Bone” fillets in VW. Dog-Bones and T-Bones are used to deal with the rounded inner corners, that result from the diameter of the mill, when cutting materials with a CNC. This solution is often used to create joints, where one piece must fit precisely in a hole. Here is a short video on how the tool is used. The tool works on Rectangles, Polygons and Polylines. Currently the tool does not work if you are inside a group, symbol, extrude etc. If you have any problems with the tool, or an idea on how to improve it, please feel free to message me. Thanks to @hgarnade for the feedback while testing early versions of the tool. Use at own risk. Although the tool has been thoroughly tested, bugs cannot be ruled out. Please consider saving your VW file before using this tool. Regards, Letti 2D Mill Fill 2023.vst
  4. Hello, unfortunately i cant figure out if your Marionette is working correctly, because it is quite complicated, but i think a simple "Delete" node right behind the "Rectangle" node could do the trick. But i also wrote the node you were looking for, or at least i think so: @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( "Planar Boolean" ) this.SetDescription('This node performs a boolean operation between lists of objects. The operation will be performed for each possible combination of objects in the lists supplied.') #Input Ports in0 = Marionette.PortIn( vs.Handle(0), 'hBlank' ) in0.SetDescription('A list of objects to be operated on (blanks)') in1 = Marionette.PortIn( vs.Handle(0), 'hTool') in1.SetDescription('A list of objects to operate on "in0" with (tools)') n_action_in = Marionette.PortIn( 0, 'nAction' ) n_action_in.SetDescription( '0 = Add, 1 = Subtract, 2 = Intersect' ) #OIP Controls #Output Ports out = Marionette.PortOut('hObj') out.SetDescription('The resulting objects') #BEHAVIOR this.SetLinksObjects() this.SetListAbsorb() def RunNode(self): #inputs in0 = self.Params.in0.value in1 = self.Params.in1.value n_action = self.Params.n_action_in.value[0] #script output = [] if type(in0) != list: in0 = [in0] for blank in in0: if blank != vs.Handle(0): if type(self.Params.in1.value) != list: in1 = [in1] for tool in in1: newObj = vs.Handle(0) if tool != vs.Handle(0): #Add if n_action == 0: newObj = vs.AddSurface(blank, tool) # blank and tool will be deleted after this operation if newObj != vs.Handle(0) and newObj != None and newObj != tool: blank = newObj #Subtract elif n_action == 1: newObj = vs.ClipSurfaceN(blank, tool) # blank and tool won't be deleted after this operation if newObj != vs.Handle(0) and newObj != None and newObj != tool: vs.Marionette_DisposeObj(blank) vs.Marionette_DisposeObj(tool) blank = newObj #Intersect elif n_action == 2: newObj = vs.IntersectSurface(blank, tool) # blank and tool won't be removed after this opeartion if newObj != vs.Handle(0) and newObj != None and newObj != tool: vs.Marionette_DisposeObj(blank) vs.Marionette_DisposeObj(tool) blank = newObj while blank != vs.Handle(0): if n_action in [1, 2]: if blank != tool: output.append(blank) else: output.append(blank) blank = vs.NextObj(blank) #outputs self.Params.out.value = output If you need, i could also add a "Do nothing"- action to this node, where the "hBlank" is put out of the node unchanged and the "hTool" object gets deleted? Regards, Letti
  5. Hello, it seems like the "Planar Boolean" node is not working as expected if you want to add many objects to many objects. However the node is working properly if you try to add many objects to one object. Luckily in this case we can take advantage of that. So i just took the first "Blank" rectangle and added all "Tool" rectangles to it. Than i added all remaining "Blank" rectangles to the resulting polygon of the step before. I think this is only possible here because every "Blank" rectangle is overlapping every "Tool" rectangle. So this is NOT a good solution to the general problem, but maybe it solves your specific problem. Here is an image of the things i changed in your network: Regards, Letti
  6. Hello, i think the solution of @Pat Stanford is working when the "False" value of the "If" node is "vs.Handle(0)" (take a "Any" node and write "vs.Handle(0)" into it). I tested it with 2 rectangles, my Marionette looks like this: I tried to find an explanation for this behaviour, but i think i will leave it to the experts ☺️ Regards, Letti
  7. Hello, i think i finaly solved this problem without using custom nodes. It was a fun challenge but, i would rather just write a custoum node 😉. So here is my solution to the problem. Unfortunately the file is created with an edu version of Vectorworks. Thanks to @Antonio Landsberger for bringing this problem back to my mind. Regards, Letti Marionett_ DomC Problem_solved.vwx
  8. Hello, if you want to understand the code here is what it basically does: Draw a rectangle with the width that you want to test for on the edge of the polygon you want to test so that it overlaps with the polygon Subtract the polygon from the rectangle. If the subtraction creates a new object (or new objects), get all vertecties of this object that are on the polygon you want to test Project the vertecies onto the edge you started with Draw polygons between the vertecies on the polygon (from step 2.) and the vertecies that were projected onto the edge (from step 3.) you started with Create the intersection between the polygon you want to test and the polygons that were created in step 4. Add up the polygons from step 5. Repeat Step 1. to 6. for every edge of the polygon you want to test I think this works just fine for polygons with 90° angles, but not at all for polygons that were converted from polylines with curves. Also the boolean operations in the code makes it realy slow for polygons with many edges. So be careful, vectorworks may crash if the polygon you are testing has to many edges. This is an example what the node does with curves. I dont think this is very useful: I am realy interested to see if someone comes up with an approach that has better results for polylines. Regards, Letti
  9. Hello, i tried to write a Marionette Node to do that, but i think i did not succeed. However i want to share what i did, maybe it helps a bit. Please note that i wrote this Node in a hurry and did not care about good code at all! I wrote the code of the Node into a .txt file, because i only have an edu version of vwx, so i cant share the .vwx file. If you want to try this Node just copy the whole code from the .txt into an empty Node. This Node only works on closed polygons and checks the width of the polygon for each edge of the the polygon individualy and draws another polygon to highlight the area where the "width of the polygon" is smaller than some value. I think that this is NOT a solution, because i dont think this "definition" of the "width of the polygon" is very useful. For example in corners with an acute angle it does this (see picture), because it checks every edge in a 90 degrees angle individualy: Regards, Letti 221002_node_tst poly width.txt
  10. Hello, a good thing about vector calculation is that its taught in school so there are plenty of good axplanations that can be found online, for example these videos: Vector between two points and length of a vector: https://www.youtube.com/watch?v=qsJxHett9E0 How to normalize a vector: https://www.youtube.com/watch?v=7fn03DIW3Ak And here are the functions i wrote to do this (they may not be well written and only work in 2D): def vector_between_two_points_2d(p1, p2): return((p2[0] - p1[0], p2[1] - p1[1])) def length_of_vector_2d(v): return(math.sqrt(v[0] ** 2 + v[1] ** 2)) def normalize_vector_2d(v): len = math.sqrt(v[0] ** 2 + v[1] ** 2) return((v[0]/len, v[1]/len)) Now you only have to do this step to determine on which side the symbols are: (vector_from_breakout_to_symbol[0] / vector_of_the_truss[0]) gives you a number ((vector_from_breakout_to_symbol[1] / vector_of_the_truss[1]) If you want you can also share a dummy vwx file with the objects, so i can provide better help. Regards, Letti
  11. Hello Raph, i'm sorry, i seem to have changed the code of the "concat" node without remembering. It should work if you change the code of the "concat" node to: @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( 'Concat' ) this.SetDescription( "Concatenates a list of strings." ) #Input Ports stringIn = Marionette.PortIn('', 'listStrings') stringIn.SetDescription( "The input strings." ) #OIP Controls #Output Ports stringOut = Marionette.PortOut('s') stringOut.SetDescription( "The output string." ) #BEHAVIOR this.SetListAbsorb() def RunNode(self): #inputs stringIn = self.Params.stringIn.value #script newString = '' for s in stringIn: newString = newString + str(s) #outputs self.Params.stringOut.value = newString Unfortunately i cant post the vwx file because i just have an educational verison. Regards, Letti
  12. Hello, i think this is doable, but i have trouble figuring out what exactly you want to achieve or what you mean by "right" or "left" of your source object. If you mean "with the direction of the truss" and "in the opposit direction of the truss", it can be done like this: - calculate the (normalized) vector of the truss - calculate the vector from the breakout to the symbol, has to be parallel to the vector of the truss - (vector_from_breakout_to_symbol[0] / vector_of_the_truss[0]) gives you a number ((vector_from_breakout_to_symbol[1] / vector_of_the_truss[1]) gives the same number) - all symbols with a number greater than 0 are on one side of the breakout - all symbols with a number smaller than 0 are on the other side of the breakout - if this number is 0 your symbol is on the breakout Regards, Letti
  13. Hello, you can use the "Concat" node to add a list of strings together. In your case you just have to convert the dimensions into strings with the "Str" Node before you put them into the ordered list. Than you can concatenate the list. Regards Letti
  14. Letti R


    Hello, thank you for the reply @Antonio Landsberger, but i should have stated that i am not using marionette for this project anymore. Here is a video of the current state of the plugin, maybe @hgarnade and @Kevin McAllister can tell me if this is usable. Regards, Letti prototype_2D mill_presentation.mp4
  15. Letti R


    Hello, i turned my previously posted marionette node (which to my shame i have to admit has many errors) into a tool. I tried to replicate the functions of a script for rhino which i found online. The tool can now add "bones" to corners like this: The tool can create "bones" on every corner regardless of the angle. It works on closed polygons and closed polylines, but not yet on polylines with holes. Creating your own holes should still be easy because the tool can also add "bones" to convex corners. The tool works by clicking on the corners where you want to draw a "bone". It is also possible to draw the same kind of "bone" on every corner of the polygon that is of the same type (concave or convex) as the corner you clicked on. Please message me if you want to try this tool. I dont want to post it publicly yet. Regards Letti
  16. Letti R


    Hello, based on your posts i wrote a little marionette node as a first prototype for a script that maybe can handle this. Please note that i dont have any experience in cnc milling, so i rely on your feedback. Here is the code, simply cope paste the whole code into an existing marionette node and it should work. This is meant to be just the very first prototype of a script. I just want to know if this is the right direction before i put more effort in it. # by Letti R, 2022 07 19 @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( "2D mill_dog bone" ) this.SetDescription( '2D mill_dog bone' ) #Input Ports poly = Marionette.PortIn( vs.Handle(0), 'hPoly' ) poly.SetDescription( "The input poly" ) #OIP Controls bit_diameter = Marionette.OIPControl( 'bitDiameter', Marionette.WidgetType.RealCoord, 0.0) bit_diameter.SetDescription('bitDiameter') side = Marionette.OIPControl( 'side', Marionette.WidgetType.Popup, 0, ['right', 'left']) side.SetDescription('side') #Output Ports #BEHAVIOR this.SetLinksObjects() def RunNode(self): #inputs poly = self.Params.poly.value bit_diameter = self.Params.bit_diameter.value / vs.GetPrefReal(150) side = self.Params.side.value epsilon = 0.000000001 #functions def cross_2D(vector_1, vector_2): if len(vector_1) == len(vector_2) and len(vector_1) == 2: return(vector_1[0]*vector_2[1] - vector_2[0]*vector_1[1]) else: return(None) def two_tuples_to_vector(tuple_1, tuple_2): lt1 = len(tuple_1) lt2 = len(tuple_2) if lt1 == lt2: list_subtractions = [] for i in range(lt1): list_subtractions.append(tuple_2[i] - tuple_1[i]) return(tuple(list_subtractions)) else: return(None) def rotate_list(list, n): return(list[n:] + list[:n]) def normalize_tuple(tup): tup_squared = [] for item in tup: tup_squared.append(item**2) len_tup = math.sqrt(sum(tup_squared)) norm_list = [] for item in tup: norm_list.append(item / len_tup) return(tuple(norm_list)) def vector_angle_bisector(vector_1, vector_2): normalized_vector_1 = normalize_tuple(vector_1) normalized_vector_2 = normalize_tuple(vector_2) return(normalize_tuple((normalized_vector_1[0] + normalized_vector_2[0], normalized_vector_1[1] + normalized_vector_2[1]))) #script poly_vertecies = [] for i in range(vs.GetVertNum(poly)): poly_vertecies.append(vs.GetPolylineVertex(poly, i+1)[0]) vectors_to_this_points = [] for i in range(len(poly_vertecies)): vector_temp_1 = two_tuples_to_vector(poly_vertecies[i-2], poly_vertecies[i-1]) vector_temp_2 = two_tuples_to_vector(poly_vertecies[i], poly_vertecies[i-1]) vectors_to_this_points.append((vector_temp_1, vector_temp_2)) vectors_to_this_points = rotate_list(vectors_to_this_points, 1) cross_product_of_vectors_to_points = [] for vectors in vectors_to_this_points: cross_product_of_vectors_to_points.append(cross_2D(vectors[0], vectors[1])) for i in range(len(poly_vertecies)): if bit_diameter and abs(cross_product_of_vectors_to_points[i]) >= epsilon: run_script = False if side == 0 and cross_product_of_vectors_to_points[i] < 0: run_script = True elif side == 1 and cross_product_of_vectors_to_points[i] > 0: run_script = True if run_script: temp_vector_angle_bisector = vector_angle_bisector(vectors_to_this_points[i][0], vectors_to_this_points[i][1]) vs.MoveTo(poly_vertecies[i]) vs.LineTo((poly_vertecies[i][0] + ((bit_diameter / 2) * temp_vector_angle_bisector[0]), poly_vertecies[i][1] + ((bit_diameter / 2) * temp_vector_angle_bisector[1]))) #outputs Regards Letti
  17. Hello, i tried to make the 2D part of your project 1.0. I think doing this only with marionette (without custom nodes) is not easy (maybe even impossible). I tried to make a marionette for one 2D polygon and it works fine, but if i want to be able to process multiple 2D polygons at once i come to a point where i would need to write my own nodes. But instead of writing my own nodes i would rather write the whole script in python. However there are workarounds for the 2D part. 1. Convert your import into lines manually and make a marionette for lines: The marionette for lines in 2D could look something like this, but i dont recommend doing it this way. 2. Convert your import into lines manually and make data visualisations: For 2D lines you can achive something like what you want with data visualisations. Simply make a visualisation for lines with a "Bounding Box delta x" of 0 (line is parallel to y), and one for lines with a "Bounding Box delta y" of 0 (line is parallel to x). Regards Letti
  18. Hello, This was the solution. Now the Images are also shown on a mac. Thanks alot for the help. Regards, Letti
  19. Hello, thanks for your reply. Im sorry for not pointing out that i already set up my images like that, but it still does not work. MyTool.vwr/Images/MyImage.png (26x20 pixel, 72dpi) MyTool.vwr/Images/MyImage@2x.png (52x40 pixel, 144dpi) And in the .cpp file i wrote it like this: TXStringArray imageSpecs; imageSpecs.Append("MyTool/Images/MyImage"); Is that enough or do i have to do something like: if operating system is mac imageSpecs.Append("MyTool/Images/MyImage@2x"); Unfortunately i cant just try it, because i dont have access to a mac right now. Regards, Letti
  20. Hello, i just "finished" the first verion of my tool and when i compile it for windows everything works just fine. But when i compile the tool for macOS, the images in the mode bar (only in the mode bar) are missing (it shows an image of a red "x" instead of my image). Could this be because of the resolution of the mac i was testing on (4k)? And how do i add the "2x" images for the screens with high resolution? Also i think i did not set up or install the SDK on the mac correctly. Unfortunately i am not familiar in working with a mac. Is there a recent, easy to follow guide about how to setup the SDK and xcode? Regards, Letti
  21. Hello, thank you for your answers. This helped me alot. Regards, Letti
  22. Hello, i want my tool to change the action that is needed to run, based on the mode that is selected in the mode bar. Unfortunately i cant find a fitting example, or i am overlooking it. The tool should behave like this: - First mode -> Run the tool after clicking on the drawing - Second mode -> Run the tool after clicking in 3d and catching the 3d point - Third mode -> Run the tool after drawing a line in 3d and catching start and end point How can i do this? I hope someone can point me in the right direction. (Sidenote: I am new to working with the SDK and new to writing in C++) Regards, Letti
  23. Hello, i tried to use this in a tool. Unfortunately i found this function to be inconsistent at least when used in a tool that is called by a click. After i selected the tool, sometimes the first click on the drawing (i assume this is the click that runs the code) is the first point of the line, and sometimes i need another click on the drawing to start the line. In your example this is not too bad, because the vs.CallTool() function is called right at the start of the script. But i wanted the first click on the drawing to just open an UI and than call the vs.CallTool() according to the mode that had been chosen in the UI. I also tried to call the vs.CallTool() function two times in a row, but i always crashed vectorworks.
  24. Hello, there is a VS Function that converts an angle into a vector. You can look it up here. With that Function you can write your own Marionette Node. In Python it could look something like this: @Marionette.NodeDefinition class Params(metaclass = Marionette.OrderedClass): #APPEARANCE #Name this = Marionette.Node( 'Angle to Vector' ) this.SetDescription( 'Converts an angle in degrees to a 2D vector' ) #Input Ports nAngle = Marionette.PortIn(0) nAngle.SetDescription( 'an angle in degrees' ) nLength = Marionette.PortIn(1) nLength.SetDescription( 'the length of the output vector' ) #OIP Controls #Output Ports v = Marionette.PortOut() v.SetDescription( 'a 2D vector' ) #BEHAVIOR def RunNode(self): #inputs nAngle = self.Params.nAngle.value nLength = self.Params.nLength.value #script v = vs.Ang2Vec(nAngle, nLength) #outputs self.Params.v.value = v But, as shown in the example below, in some cases the VS function returns realy small numbers instead of zeros. So i would recommend to round the returned values to a reasonable length.
  25. Hello, if the text is always just centered you can also use the "get 2D Center" Node. Then the Network could look like this: This Network uses the 2D information of the text object and takes advantage of the fact that the 2D center of the text object is the same as the center of the text (or at least i hope it always is). Like this you dont have to worry about the vectorworks preferences. Notes: - The network shown above only works for inserting the text object at its center - Although the insertion point of the text object ist its center, the text can be aligned differently - You can change the "Get 2D Center" node to the "Get 2D Point" to use one of the 4 vertecies of the text object as the insertion point
  • Create New...