Replies: 1 comment
-
Hello, Steve Thank you for reaching out to us. I'm always eager to help, however I can. Regarding the definition of nodes with multiple outputsTo define a node with multiple outputs, your function/callable must return a dictionary in which the keys are the names of the outputs and the values are the outputs. In addition to that, you must use a special return annotation like this, to let Nodezator know that it must treat the items of the dictionary as different outputs: def distance_from_a_to_b(point_a, point_b) -> [
{'name': 'distance_in_x_axis'},
{'name': 'distance_in_y_axis'},
{'name': 'euclidean_distance'},
]:
## calculate distance in both axes
x_distance, y_distance = (
value_a - value_b
for value_a, value_b in zip(point_a, point_b)
)
## next, the sum of the squared distances
sum_of_squares = (x_distance) ** 2 + (y_distance) ** 2
## then the distance, which is equivalent to the
## square root of the sum we just calculated;
##
## note that "number ** .5" equals the square root
## of the number
distance = (sum_of_squares) ** .5
## finally return the distances inside a dict using
## the names defined in the return annotation; the
## order isn't relevant because the node uses the
## order of the items in the list from the return
## annotation;
return {
'distance_in_x_axis': x_distance,
'distance_in_y_axis': y_distance,
'euclidean_distance': distance,
}
main_callable = distance_from_a_to_b This info is available on the manual: https://manual.nodezator.com/ch-nodes-variable-parameters-custom-outputs.html#defining-node-more-output You can also include a Despite that, Nodezator still doesn't have any kind of type checking mechanism (although we intend to implement it after we implement other features of higher priority). We even have a discussion for it where people can share suggestions: #47 Regarding tree structures in NodezatorAs you are probably aware, Nodezator doesn't have a concept of parent-child relationships between nodes. Nodes are just chained together based on the dependence a node has on the output(s) from previous node(s). Of course, this doesn't mean a tree structure can't be represented in Nodezator with the chained call. However, it does mean that you'll have to think of a suitable way to do so. I myself never experimented with the concept in Nodezator, but a couple of ideas come to mind. Maybe a fluent interfaceFirst, you can design your nodes with a fluent interface in mind, where every object has methods to create/delete/access other objects below or above it. The Python CadQuery library, much like the JQuery library from the Javascript ecosystem, uses a fluent interface like that. For instance, in the CadQuery graph briefly shown on the README of the repo, we define a 3D model using CadQuery's fluent interface. On top of that, after defining the model, the graph also has sections where the model can be exported and a 2D snapshot can be visualized (the 2D visualization relies on another lib called cairosvg). Here's a copy of that graph with each section highlighted: Note that the graph doesn't even use custom nodes. The entirety of the graph uses nodes already available by default in Nodezator that represent calls to Python built-in functions, standard library functions, common operations and a few simple Python snippets included. Most notably, Nodezator has a In addition to that, when achieving a certain result with nodes would be difficult or cumbersome, I can easily include Python code within the graph itself using the Getting back to our discussion, the section where the 3D model is defined is roughly equivalent to this piece of Python code (which was originally taken from their documentation): import cadquery as cq
height = 60.0
width = 80.0
thickness = 10.0
diameter = 22.0
padding = 12.0
# make the base
result = (
cq.Workplane("XY")
.box(height, width, thickness)
.faces(">Z")
.workplane()
.hole(diameter)
.faces(">Z")
.workplane()
.rect(height - padding, width - padding, forConstruction=True)
.vertices()
.cboreHole(2.4, 4.4, 2.1)
.edges("|Z")
.fillet(2.0)
) As can be seen from the code, the workplane object created at the top of the chained methods can be accessed at any point in the chain by calling the The advantage of this approach is that your trees would be rather easy to customize/change at any point. The drawback is that the graph doesn't actually resembles the final structure of the tree, but rather the various access points and modifications made along the definition of the tree. There may be more pros and cons, though, as I never experimented too much with this kind of solution. Maybe simply declaring each object from bottom to top, that is, from each leaf until the rootIf you design the nodes to represent objects that receive children upon instantiation and they themselves can be included in other objects further down in the chain, you'll end up with a graph that resembles your tree structure much more closely. That is, the first nodes would represent leaves. They could be classes that return an instance that can be fed to other nodes. The very last node would be the root, the one receiving the parents at depth 1 in the tree. For instance, just to give you an idea, here's an example of a graph used to represent a tree structure, along with a copy of it with areas of interest highlighted: In the "tree definition" portion of that graph I used only default nodes representing Python primitives and tuples, just to give you an idea about how you'd place/instantiate your nodes, but of course you'd be using your own custom nodes instantiating the classes with which you are working. Since the final "root" node in the "tree definition" portion just returns the last object (a tuple) containing all the others, it represents the tree itself and from that point onwards you can do whatever you want with it, that is, save it, export it, further process it. Much like what we did on the CadQuery graph, that is, with the model definition in hand, we passed it further to two other sections where the model was exported as a 3D file in one and used to generated a 2D snapshot in another. After this portion, in my tiny example graph above I decided I'd generate a representation for it. The "tree representation" portion is also very simple, just to give you an idea of the possibilities. In it, I simply used pprint.pformat to format the tree (our final tuple) as a string and passed the resulting string to a default node used for visualizing a string as monospaced text on the graph (such visualization nodes can be found on the General viewer nodes submenu of the popup menu that appears when right-clicking the canvas). Come to think of it, it just occurred to me that I actually experimented with this kind of tree structure roughly 10 months ago, when I created a few custom nodes representing SVG shapes that would be instantiated and fed to a final svg node at the root of the tree. I actually posted about it. Look for the "Using an object-oriented approach to describe SVG shapes..." section on this page from Nodezator's website. Here's an annotated copy of the graph shown in that section (the text on the link actually has a bit more details on my experimentation): I actually used functions to create the objects because I wanted to build a demo fast, but if I were to refactor my solution I probably would've used custom classes instead, so I could include many useful methods as needed. Instead, where specialized methods were needed, I used functions as well, like the In all graphs shown here I used only general viewer nodes provided by default within Nodezator, but Nodezator's manual also teaches how to create custom visualization nodes, so you can customize the representation as much as you'd like. The release page for Nodezator 1.4.0, version in which new capabilities for viewer nodes were introduced, lists the chapters that address the subject: https://github.com/IndiePython/nodezator/releases/tag/v1.4.0 (just search for the section entitled "New viewer node capabilities"). Extra considerationsNodezator's first release is only a bit over 02 years old (despite being developed "in-house" for roughly 02 years before that first release) and it is mostly maintained by a single person (me) so it still has much to improve. Slowly but surely I've been improving it constantly, so we'll get there eventually. Just, please, be aware of such limitations. I hope it cater to your needs, but regardless of whether you end up adopting it or not in your workflow, just know that I'm here to help. I think this was all I could think of to help you regarding what you asked. If I missed anything or there's more stuff you need help with, just let me know. Peace. |
Beta Was this translation helpful? Give feedback.
-
I've been using a tool called typedtree-editor to create decision and behaviour trees for my game. It works well, but the editing of the graphs is somewhat limited. I've attached an example image.
I've also been looking into Nodezator in order to create the same types of tree structures, but am having trouble with the parent->multiple children relationship links. Can anyone offer any advice on some Python classes I could use to model that? It seems that in Nodezator, each node only really has one output, where a general tree structure could have many.
Many thanks,
Steve
Beta Was this translation helpful? Give feedback.
All reactions