Noob question lists and how to use the max node
Hi, I've just discovered Nodebox and I'm giving it a go. Having used geometry nodes in Blender a lot of the concepts feel familiar, and I'm having fun. However I'm struggling with a few points. The first is say I'm getting a measurement using the distance node between a make point node and a grid of items, and I want to scale the items based on that distance, but I want to eliminate any that are out of range. So I'm subtracting the distance from a set value, but obviously it can go negative where points are outside of my set distance. Normally I'd use the max math command with a constant of zero in there to eliminate any sub-zero values, and so I attempted to do the same here, but I'm not understanding how to do it as the max node is expecting a list, and I have two separate values coming from nodes (the subtract output and a fixed value number node). Do I have to combine those into a list somehow, and if so how? I coudln't find any mention of a node that takes values and puts them in a list.
|?||Show this help|
|ESC||Blurs the current field|
|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 18 Jan, 2023 11:21 PM
Welcome to the Nodebox community! Your previous experience with Blender should indeed give you a leg up.
The problem you're having is one all Nodebox learners encounter sooner or later. You put your finger on it when you said "the max node is expecting a list". Everything in Nodebox is a list, but sometimes you want nodes to take in list items one at a time, and sometimes all at once.
First, the max node is not actually what you want in this situation. Max just returns the maximum value in a list of numbers. What you want is a way to limit the maximum value of a calculation.
To illustrate this I made a quick demo (see screenshot, animation, and code attached).
I made a grid of 25 points, each holding a square. I want to scale each square based on its distance to a revolving circle. I adjust that distance by dividing it by five and then subtracting it from a constant (123). For points in the grids far from the circle, this calculation will produce negative values.
Like you, I want to limit this effect so that the calculated value never falls below a certain value (I chose 10 instead of zero so that you could always see at least a small square at every grid position). How to do this?
The first step is to use a compare node to find all values less than 10. For the 25 grid positions this will produce a list of 25 booleans - either false (0) or true (1).
You would think all I then have to do is feed the original values and this boolean into a switch node, with the original value if the boolean is false and a 10 if the boolean is true. But if you try this (as perhaps you did) something strange happens. The switch node produces not 25 values but over 400 depending on where the circle is located. What's going on?
The problem is that the switch node takes the list for each option all at once instead of one at a time. So when the first boolean comes in the node fires; if that boolean is true it produces a 10, otherwise it produces the ENTIRE list of values. And it does the same for the second boolean, and so forth.
So what you'd like is an alternative node that takes in candidate values one at a time instead of all at once. I made just such a node in my Cartan Node Library. It's called "this_or_that". You can see it in action by opening the attached demo.
This_or_that is actually just a subnetwork. You can open any subnetwork by control-clicking on it and choosing "Edit Children". If you do this, you will find something very strange: this subnetwork consists of a single node - and that node is a switch node.
Subnetworks give you a way of controlling how the input ports take in lists. If you now select the this_or_that node and open the Metadata dialog using the link above the parameter pane, you will see that this subnetwork has three ports: this, that, and which. If you select each one and look at the Range setting on the right, you will see that range is set to Value. "Value" means one at a time. The alternative, "List", means all at once.
If you perform the same experiment on any switch node, you will see that all seven of its input ports have range set to "List". This is why the switch node behaves the way it does. Unfortunately, you cannot simply change the range value for standard Nodebox nodes (it lets you, but strange things may happen if you try). But instead you can simply wrap that node inside a subnetwork and then change the range settings on that to whatever you want. This is what I've done with the this_or_that node.
Subnetworks, then, are the magic key that unlocks the immense power of Nodebox. They give you full control over how lists come and go from node to node. The obscure setting of a "Range" value to "Value" or "List" is unintuitive, well-hidden, and almost completely undocumented. As a result every new Nodebox user is confused.
So please get used to using subnetworks. They are also a good way to make your code more readable. Decompose every step of your process to a function, and turn each function into a subnetwork. And always keep in mind for each list coming into each port whether you want to take in that list one item at a time or all at once.
By the way, the need to place upper or lower bounds on a set of values is very common (and probably what you thought the max node would do for you). So I made another subnetwork node in my library called bounds. I included it off to the side in my demo. If you hook the bounds node into the width and height of the rectangle node it will provide an even easier way of doing what you want.
Download latest Cartan Library here:
The subnetwork tutorial:
Please let me know if that helped. And keep those questions coming!
2 Posted by Darren Crabb on 19 Jan, 2023 11:11 AM
Briliant, thanks for your detailed response. I have already dabbled with the subnetworks but thought they were just a sort of grouping mechanism much as they are in Blender. Hadn't realised you could change the way they interpret data like that. I'll take a look at your library elements. Still a bit confused by why I wouldn't use a max node, as your description of what it does matches my expectations. Normally I'd just feed the lowest number that I want in as a constant (so to avoid negative values I send a zero into it) along with a number I want to limit and if the other number goes lower it returns my constant instead. I'll take a look at your bounds node and see what you've done though. Many thanks for you help.
Support Staff 3 Posted by john on 19 Jan, 2023 06:45 PM
You are right: you could use a max node instead of the compare and switch method - works just as well and is arguably a tad cleaner. But it has the same problem. You have to use a combine node to add in your limit before applying the max node, and the combine node takes in its lists all at once.
So the solution is the same: just turn it into a subnetwork and make sure the range for the value port is set to value (one at a time).
Attached is another demo where I did just that. Even cleaner!
4 Posted by Darren Crabb on 20 Jan, 2023 02:32 PM
Thanks John, I'm glad it works like that too, as that means I don't have to adjust my thinking about the max node. I think the main thing I was missing originally was I'd completely overlooked the metadata button and also had disregarded the combine node as it was expecting list inputs and I presumed the singular values wouldn't work there, whereas they do. Now you've explained it and I've taken a look at your nodes in the library it's all starting to come together. Thanks for your time helping me understand.