New to Nodebox, tons of questions ! :)
[English is not my first language] [I don't know if each question requires a thread]
Hi, I'm Julien, designer, I discovered nodebox a few months ago after buying a plotter. I had followed tutorials to use Blender with Svertchok but it was a bit complex. I was glad to see that there was another software for dedicated generative node drawing.
I read carefully the documentation and the forum, especially John's always very complete answers (a huge thank you to him for this flawless involvement), and I tried to make some tests but I still have so many questions!
I'd like to use nodebox mostly to do parametric figurative illustrations with a bit of randomness, but I'm also interested in abstract works (even if I never have much imagination to find algorithms that make pretty things)
I did some experiments like trying to generate washing machines, cameras or fighter planes, and during these exercises here are the questions I could gather (I tried to search as much as possible on my own but I didn't find answers for these):
Can we extract variables from some parameters?
For example if I set a width to 200px, can I retrieve this variable with a node or do I have to make a node first which will be my variable, which I plug in where I need it? Another example = retrieve a seed in text to display a "serial number".
How do I choose among overlapping elements?
For example I have 1 grid of circles and 1 grid of squares combined, how do I make a grid of circles and squares mixed together (see the buttons of the washing machines) ?
What is the best way to choose to draw (or show) certain options
For example, if I want my camera to be generated with or without a grip, or with or without an inscription, how do I make a decision tree or an on/off switch or place a shape on the right or left?
How to manage the grid spacing when the elements they contain have random sizes?
For fixed sizes, I did a calculation with (number of columns - 1) × (fixed) width of the element but for variable elements I don't know if there are other methods (nor if this method is the right one for fixed elements).
Is there a notion of layers?
It can be useful for SVG export and to say that such shape goes on such layer, to then make color variations with the plotter for example.
Is it possible to round the corners like in illustrator with a given radius?
I have seen a node "round the corners" but it gives very weird results. The goal would be to round the wing tips or the fuselage joints, or the joint between the camera body and the viewfinder.
Is it possible to have geometric shapes in outline only without coloring them?
It's not very annoying but it would be convenient for a drawing that will go on a plotter.
The multiplication of elements
I didn't manage to understand what multiplies the elements or not, for example a grid multiplies the number of elements, the number of seeds also, the points... in short I can't understand the rule that manages all this.
Can we easily de-duplicate the overlapped vectors?
For plotter use, you don't want the pen to go back to the same place, I usually use vpype to clean up my svg's before plotting them, but maybe nodebox can also remove redundant vectors.
Aligning objects
I'm having a lot of trouble with this, they align to the base grid and I know you can add a node alignment to change that point of origin, but I haven't figured out how to align the objects to each other, so that for example the camera shutter release is always contained within the shape of the box and its positioning relative to that shape. What is the best practice to manage positioning when drawing figurative elements?
Shape copy: how to "skip" the 1st copy?
Sometimes I would like to make concentric circles for example, so I make an ellipse which I copy + scale, except that the 1st copy is always an exact copy of the initial shape, which makes 2 shapes overlap. Is there a way to tell it to ignore the 1st copy?
Centering the text path in the middle?
Is it possible to center a text path, instead of just choosing the beginning or the end for alignment? This could be handy for centering variable size text (in my case, the inscriptions on the lens).
Replacing a single element on a grid
How can I replace an element at a given position on a grid? For example I generated a grid of washing machine, how can I put a star in the middle of this grid? I guess I have to combine the list of machines with the star, but how do I choose its position in the list (and thus in the grid?)
If / then / else
Is it possible to use this kind of logic? I saw that there were booleans and value comparisons, but I am lost on the "then", how to make a conditional branch?
What is the best way to do greeble?
For example I would like to add lines on the wings of my fighter planes, to simulate joints and flaps, and these lines should be random but contained within the wings and touching the wing contours. They can go in both directions, or it could even be rectangles, circles... in short various shapes all contained in another shape. There could also be some wear and tear (for that I can maybe use little scatter lines to make hatchings). What do you think would be the best way to generate these metal plates and other elements?
Irregular polygons
To generate an irregular polygon you have to generate it point by point and then connect the lines? This also brings up the question of the order of the points, I've seen that you can reorder them with a node but I don't think it works in my case, and it takes into account all the points in the illustration.
Randomness
I understood that for each iteration to be different, you need a different seed for all the random numbers, in this case I generate as many random numbers as there are elements in the grid, and I assign them to the "seed" nodes published for the child. is this the right method?
What is the purpose of the "null" node?
I understand that it doesn't do anything, but then what is its use?
These are my questions for now, I'm aware that there are a lot of them, maybe some deserve their own thred, don't hesitate to tell me.
Thanks for reading all this, I'll keep looking on my own. Attached, some screenshots of my tests.
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by john on 16 Aug, 2022 10:48 PM
Hi Julian!
A ton of questions indeed! That's fine - I like your enthusiasm and all of your questions are coherent and quite reasonable.
Instead of trying to answer them all in one ginormous 50 page answer, I'll take them one at a time and write a whole bunch of short answers...
The main thing you need here is the lookup node. Lookup can retrieve any property (key) of any object you feed into it. For example, if you make a rectangle that is 220 pixels wide, and feed that into a lookup node with the key set to "bounds.width", the node will return 220. This is especially useful when you have a list of objects each set to a different random width (or color or whatever) and you need to create another object at half that width.
But how do you know what properties (keys) and object has? This is where you can use another handy node: keys. Just plug your object into a keys node and it will spit out a list of properties that you can then look up. (I usually just delete the keys node when I'm done with it.). Most properties are simple but some, like bounds, use dot notation to expose sub-properties like bounds.width and bounds.height. This is not well documented, so you have to learn these things by experimentation (or by asking!).
Support Staff 2 Posted by john on 16 Aug, 2022 11:21 PM
Julian,
Question 2:
The main concept is the group node. The group node takes a list of objects and returns a single object containing all of them. This is the closest Nodebox gets to having layers.
Group works fine if you want to put one circle inside of one square and then treat them as a single combined object. But what if you have a whole grid (list) of circles and a whole list of squares? What you really want is a whole list of combined objected, not a single group containing both grids.
This is where subnetworks come in. Make a subnetwork with two ports: circle and square.
Aside: you asked what null nodes are used for. This is one such use. To make your subnetwork, start with two null nodes. For clarity you can rename the first one circle and the second one square. Select both and "Group into Network" then "Edit Children" to see inside. Select the port of each null node and "Publish" the first as "circle", the second as "square". You now have a subnetwork that takes circles and squares as inputs.
Now feed a list of circles (ellipses) into the circle port and a list of squares (rects) into the square port. For best results you will generally want to make sure both lists have the same number of items. Each time a circle and square arrive, your subnetwork will fire and do something. At the very least you can simply feed your two null nodes into a combine node to combine them, and then feed that into a group node to group them into a single object.
With this setup, if you feed in a list of ten circles and a list of ten squares, you will get our a list of ten grouped objects.
You can either fix the positions of each circle and square ahead of time, or leave them all centered coming in and set the position of each object group later. If you just want a grid of grouped objects you can simply feed the output of your subnetwork into a translate node along with the output of a grid node (which is actually just a list of points). Voilà! A grid of washing machines!
Support Staff 3 Posted by john on 16 Aug, 2022 11:47 PM
Julian,
Question 3:
A grid, by definition, will have rigid positions regardless of the size of each item; if you don't want any variable size objects to overlap you will need to adjust the overall grid width and height by hand until you get the best compromise. (This is fun and easy to do in Nodebox.)
If you want to arrange objects in groups or clumps where the positions deviate from a classic grid but the margins between objects remain fixed, you can try the stack node. The stack node will take a list of objects and arrange them in a line, north, south, east, or west, with a fixed distance between each object.
To make make something like a grid you can first use an "East" stack node to produce rows, group each row, then use a "South" stack node to arrange the rows into a grid with a fixed vertical margin. This technique requires a bunch of other nodes to do the math.
OR you might try cracking open my Cartan Node Library:
http://support.nodebox.net/discussions/show-your-work/493-cartan-no...
All nodes in this library are free to use without restrictions. Just copy paste the ones you need into your own project. There are several that you might find useful in this regard:
grid_plus lets you define a grid based on cell size instead of overall grid size and also allows you to have odd-sized grids with the bottom row not entirely filled in and arrange the positions in different orders.
alt_grid lets you make grids where alternating rows or columns are indented and can order positions so they zigzag back and forth.
arrange lets you arrange objects in tidy stacks each in its own lane. This does essentially what I described above as stacks of stacks but does all the math for you. Play with it to see what it might do to your washing machines.
stack_tight works like the stack node except it allows you to tuck oddly shaped objects into each other; works great for kerning characters.
There are many other ways to arrange objects. The grid node is just a list of points, each point used to provide a position when used with a translate node. So any list of points you come up with can be used to position objects on a canvas. Try playing with some of the above techniques and, if you're still not happy, send me your network (zipped) along with a description of what you want.
Support Staff 4 Posted by john on 17 Aug, 2022 12:18 AM
Question 4:
This is actually two similar questions:
The answer to both these questions is the switch node.
There are no conditional branches in Nodebox. Instead we use the switch node. Feed up to six alternatives into the first six ports, then a choice value into the final "index" port. If the index is zero, the node will pass through the first choice; if the index is one it will pass through the second choice, etc.
For simple yes/no decisions, you can feed boolean values into the index port as well. In Nodebox a "false" value evaluates to zero and a "true" value evaluates to one.
Be aware that every time a switch node fires, it will pass along everything it finds in the selected port. So if you have one list of ten black numbers and another list of the same ten numbers colored red, and you want to chose a red number if it's less than zero, the switch node will not work the way you expect. If you feed it a list of ten booleans from a compare node (true if value less than zero) it will output all ten numbers (either black or red) for each boolean it gets.
If you need to make "this or that" decisions like that, the Cartan Node Library once again comes to the rescue. My "this_or_that" node will allow you to deal with list of decisions taken one a time.
For situations like your camera example, you will probably want to make a "camera" subnetwork. In addition to whatever other options you provide (like position, size, or color), you can also provide option checkboxes or option menus.
Checkboxes will automatically appear whenever you publish a boolean node. If the box is unchecked the boolean will have a zero value; if checked, it will have a value of one. Just feed that into the index port of a switch node to produce cameras with or without a grip, etc.
In cases where there are more than two possible values, you can also make a subnetwork with a fancy menu of options. If you need to do this, ask me again and I'll show you how. You can also peek inside the nodes in my library; many of them have checkboxes or option menus.
Subnetworks with option boxes or menus don't have to be set by hand by the way. They are just ports like any other, so you can feed choices into them by simply connecting inputs into those ports.
Does that make sense? If you're used to using IF / THEN statements, using switches instead can take a little getting used to. But it's always possible.
Support Staff 5 Posted by john on 17 Aug, 2022 12:23 AM
Question 5:
No layers. But you can use the group node instead to form subsets of objects into different groups that can then be all colored at once, etc. Use the ungroup node to reverse the process.
Groups in Nodebox are all or nothing. There is no hierarchy of groups (no sub-groups). So when you ungroup a group of groups, you get a single undifferentiated list of base components. If you need to do something complex there are various techniques to deal with this.
Support Staff 6 Posted by john on 17 Aug, 2022 12:31 AM
Question 6:
The "Round_Segments" node (if that's what you mean) does something completely different: it turns a spline of straight line segments into more of a curve, often with strange results.
For rounded corners on a rectangle, just use the rect node. The final port on the rect node, "Roundness", lets you round the corners. It has both an X and a Y in case you need weird oval corners, but in general you can just feed it a single value (set both X and Y to the same value). That value is the radius you were asking for; If it remains at the default 0,0 you get a standard rectangle with sharp corners. Increase that value for increasingly rounded corners.
Support Staff 7 Posted by john on 17 Aug, 2022 12:37 AM
Question 7:
Use the colorize node. Feed your object into colorize, click on Fill color, and in the color dialog set alpha to 0. This makes the fill color transparent. Then be sure to set the Stroke Width to something greater than 0.
I've done this so many times that I can now add colorize, set fill, set stroke width in my sleep.
Support Staff 8 Posted by john on 17 Aug, 2022 01:13 AM
Question 8:
This is the zen of Nodebox: the way the pattern of inputs causes nodes to fire. It definitely takes some getting used to.
It may help to think of each node as a function that fires each time it receives an input. If you have a simple node with a single input, and you feed it a list of 10 values, the node will fire ten times and produce a list of 10 new values.
If the node has more than one input, things get more complicated. If it has two inputs and you feed it two lists, one with 12 items and one with 3 items, the list with 3 items will automatically keep repeating until all 12 items in the first list are exhausted. This can sometimes be very handy. In general, though, if you keep all your input lists the same size you won't have to think about this.
The above examples all assume the inputs are set to be received by "value", that is, one at a time. This is the way most nodes work. But for some nodes this would not make sense. If you feed a list of 10 numbers to a sum node, for example, you only want it to fire once and return a single value (the sum of those 10 numbers).
For cases like this, ports can be set to be received by "list" instead, that is, all at once. When you make your own subnetworks you can control this in the Metadata dialog (located above the parameters pane). In the Metadata dialog, select any port of the currently selected node from the list on the left, then see its "Range" setting on the right. Range can be either "Value" (one at a time) or "List" (all at once).
Confused? Join the crowd. This is, in my opinion, the single most confusing aspect of Nodebox. It took me months to grasp. It's very cool and very powerful once you get used to it, but it quite subtle and deviously hidden away in the UI with no documentation to explain it.
Fortunately you don't need to worry about all this TOO much at first. You can just hook things together and see what happens.
So let's review. When you hook a grid node (with 4 columns and 3 rows) into the position port of a rect node, why do you see 12 rectangles? What's happening?
The grid node does not actually make a "grid". It simply makes a list of N points, where N = C x R. So in this case it makes a list of 12 X,Y points. When you plug that as the sole input to the position port of a rect node, you cause that node to fire 12 times, each with a different position from that list.
(If you examine the position port in the Metadata dialog for your rect node, you can confirm that position is set to Range = Value. Even though it lets you, you shouldn't change that setting for standard nodes, but you will sometimes need to change this value for ports in subnetworks you make for yourself.)
Still awake? For more insight, make a range node with Start = 10, End = 31, and Step = 10; this should produce "10, 20, 30". Then plug this into the width node of the above rect node and see what happens. Now the node is getting one list of 12 points from the grid node, and a second list of 3 widths from the range node. You will still get a total of 12 rects, but their width will vary in a repeating pattern.
Does that help? Don't worry if you're still confused. Just keep playing. It will come to you.
Support Staff 9 Posted by john on 17 Aug, 2022 01:21 AM
Question 9:
There are some things you can do to make plotting more efficient, but the techniques vary depending on exactly what it is you're plotting. Florisdeyonge is an expert in this area.
Can you please re-ask this as a separate question? And please attach a zipped file containing your network and any required files (e.g SVGs). Either Floris or I will show you some tricks to make that plot more efficient.
Support Staff 10 Posted by john on 17 Aug, 2022 01:52 AM
Question 10:
There are many different nodes and tricks of the trade that can be helpful here.
First I would recommend something I think you are already doing: create a single subnetwork to create your complex object (e.g. camera). You can feed the components in as prefabricated shapes or merely as values like "lens radius" and generate the component within the subnetwork. This will help contain the clutter and make your thought process more orderly.
In other words, make each camera one a time (centered) and then arrange multiple cameras in a grid (or whatever) later.
For a camera it may make sense to establish a body like a rounded rectangle and position all the other components in relation to that shape. Inside your subnetwork you can use the lookup node to retrieve corners and midpoints of that camera body - or just dip into the Cartan node library and use my handy corner node.
You can use those reference points to find the positions for each component. There are many ways of doing this. For example, you can feed the upper left corner of your camera body into a coordinates node, and give it an angle and distance to locate the center point of a component.
The translate node is your friend. If you want to put the lens roughly (but not exactly) in the center of the camera body, and both body and lens are already centered, you can nudge the lens a little to the right by simply feeding it into a translate node with the translate port set to X,0. You can calculate X (maybe based on lens radius and camera body width) and then feed that value into the X port of a "make_point" node. Then feed that point into the translate node.
And of course the align node is indispensable. Given a reference point from the camera body or calculated in some way, you can then use the align node to center the component at that location, or align the top left corner of the component to that point, or whatever. And then use translate to nudge further in one direction.
For scaling you can use the Fit and Fit_To nodes to automatically shrink or expand a component to a given bound or enclosing shape. If you then want a little breathing room, you can feed the fitted object into a scale node (set scale to, say, 90, and the origin to the centroid of the component).
That should give you more than enough to get started. Precisely aligning components can become a bit fussy, but anything you can imagine is possible. In fact there are usually a dozen different ways of accomplishing the same effect. If you are still having trouble, send me a zipped copy of your network and I will show you one way of doing what you want.
Support Staff 11 Posted by john on 17 Aug, 2022 01:55 AM
Question 11:
Once you have your list of shapes, just feed it into a rest node. This will toss the first item and keep the rest.
More generally you can use a slice node to take a particular subset of any list,
Support Staff 12 Posted by john on 17 Aug, 2022 02:17 AM
Question 12:
You mean using the text_on_path node? This is a totally reasonable request which, as far as I know, would require an unreasonable amount of math.
When attacking problems like this you need to fully understand the path you are using. You can see that path by turning on the Points and Point Numbers options above the canvas pane. If you are using your lens (an ellipse) to form the path you will see that it starts at the 3 o'clock position and moves clockwise. If you use an open arc instead, the beginning of the path will depend on how you define that arc.
In this case the text size (and font choice) adds a complex variable.
Let me think some more about this. It should be possible to create a node (a subnetwork) to do this automatically for a wide range of possible paths. I enjoy making nodes like this. Maybe this will be a future addition to my node library!
UPDATE: I came up with a solution:
http://support.nodebox.net/discussions/show-your-work/503-center-on...
Support Staff 13 Posted by john on 17 Aug, 2022 02:37 AM
Question 13:
Use a slice node.
The grid node produces a list of points. For a single star dead center you will want a grid with odd number of rows and columns. In that case the center position is floor(count/2) where count is the number of points in the grid. Attach your grid to a count node, attach that to a divide node with a denominator of 2, and attach that to a floor node.
Then all. you have to do is attach your grid to a slice node with start set to that center position and size set to 1. This isolates the center point. Now check the Invert box in the slice node to get everything but the center.
Feed that into the position list for your washing machine subnetwork (or feed your washing machine node and the modified grid into a translate node) and Voila! Washing machines everywhere but the center!
You can then use a combine node to combine that with your star. If both your grid and star are already centered, the star will already be in the right place. If not, you can find the position of the star using a copy of your slice node with the Invert box off.
Support Staff 14 Posted by john on 17 Aug, 2022 03:08 AM
Question 14:
There is not a simple solution to this one, I'm afraid. Adding greeble is usually rather complex and will require different approaches based on the enclosing shape and nature of the elements you are using to fill that space.
For a very rough starting point you can use the scatter node which will generate a fixed number of points randomly scattered within a supplied boundary shape. But this often produces poor results, with some points on top of each other in clumps and gaps of open space which never land where you want them to. You can keep changing the seed value to see if you get lucky, but this has never worked for me.
You will have slightly better luck if you use the scatter_evenly node from my node library, This node produces random but evenly spaced points of a given size within a rectangular boundary that are guaranteed not to overlap. You can then use a delete node to toss points that fall outside a particular shape (like a plane wing).
My scatter_evenly node requires custom Python code (poisson.py) included in the library folder. To use it you will need to place a copy of poisson.py into a folder containing your Nodebox file and add it to the code library of your project using File/Code Libraries.
Making elements which grow organically from the edges of a shape is even trickier. You may need to take your enclosing shape apart by feeding it into a point node, slice of a subset of those points to define a particular edge, use point_on_path to sample attachment points, then add more points and connect them to form shapes.
So it can be done, but it's not easy. Sorry.
Support Staff 15 Posted by john on 17 Aug, 2022 03:22 AM
Question 15:
You are correct. Generating points and connecting them is the standard way to generate irregular polygons. And order does matter; unless you want an unholy mess you will need to order your points either clockwise or counterclockwise from the center. You can do this using the sort node. (Beware: there are two different sort nodes. You want the gray one, not the blue one.)
You can use a scatter node to generate your points, but this often produces polygons more spiky than you want. It might be better to start with the points of a regular polygon and jigger one or more of them. This can be done in a variety of different ways.
Another technique is to use a compound node to merge two or more overlapping shapes. I also have some clever nodes in my library which allow you to tug on a shape from different directions or find the convex hull wrapping around a point cloud or generate metaballs.
So there are many ways of doing this. What's best depends on exactly what kind of shapes you are trying to make. If you get stuck on this send me your best attempt and I will see if I can improve on it.
Support Staff 16 Posted by john on 17 Aug, 2022 03:29 AM
Question 16:
Yes, you've got it.
When working with subnetworks you will generally want to publish internal seeds so they can be fed in from outside the subnetwork. Otherwise each time the subnetwork fires it will use the same seed.
Support Staff 17 Posted by john on 17 Aug, 2022 03:47 AM
Question 17:
Null nodes are surprisingly useful.
As I mentioned above they are necessary when you want to create general inputs in a subnetwork to receive generic shapes or paths. I also use them in data subnetworks to receive tables of data (which arrive as rows of "zip maps").
I also use null nodes when debugging. Often you will have a network with one node with outputs leading to a dozen other nodes farther down. Whenever you have to replace this node, you also have to reconnect all dozen of those outgoing links - which can be a royal pain. Instead you can connect a null node to those dozen downstream node and then connect your candidate node to that null node. If you ever need to replace your candidate node you only need to reconnect it to that null node.
Similarly, null nodes can help organize sprawling networks, much like cable ties in a computer rack. Instead of a rats nest of links crossing a crowded network, you can throw a single link to a null node off to one side and then distribute child links to other nodes in its neighborhood.
I encourage all Nodeboxers to keep their networks tidy. Try to avoid links crossing over the tops of other nodes. Line things up. Use negative space to isolate related clumps of nodes to make the overall flow easier to comprehend. This can take a little time, but I find it restful and time well spent. A tidy net is a happy net.
THERE! I think I answered all your questions. Exhausting (for both me and you) but I enjoyed it. And I hope it's useful for others as well. You could almost publish this thread as a little tutorial for Nodebox newbies.
PLEASE keep asking questions. But maybe next time not all at once. :)
John
18 Posted by julien on 17 Aug, 2022 08:11 AM
Thank you for all these answers, it's perfect! You didn't have to answer everything at once, it was a long thread, I wasn't in a hurry :)
Most of the answers are very useful for me to unblock the basics, for example I still don't have the reflex to think "lists" for geometrical shapes and the use of "first" or "rest" will help me a lot. The lookup node will also be precious, I didn't understand how it works.
Thanks for all these resources, I'll keep digging to try to understand how it works by myself before using your ready-made nodes, I think I still have some room for experimentation, even if they will help me in the future, I don't want to go too fast.
If some of the questions could be answered quickly, I'll make dedicated threads for the more complicated ones with concrete examples of networks and the goal.
Once again, thank you very much for your time!
Support Staff 19 Posted by john on 20 Aug, 2022 11:35 PM
Julien,
I accepted your request to make a node that would center an inscription over a camera lens. Please give it a try!
http://support.nodebox.net/discussions/show-your-work/503-center-on...
John
20 Posted by julien on 21 Aug, 2022 07:08 PM
Hey, thank you very much, I will give it a try as soon as I return to my DSLR generation!
21 Posted by Pablo on 27 Feb, 2023 01:29 PM
Thanks so much to both of you, Julien first, for asking that valuable questions, and John for your amazing answers. This is what I needed to start my journey with Nodebox. Page saved as a helpful guide.
Have a great day!
Pablo