Part 1 here
What if we want to use our UpdateParameterAndSaveImage custom node to update a series of family instances, not just one?
I am going to show two methods for accomplishing this in dynamo. We'll first have to make a few quick changes to our custom node function. Since the idea is to set a series of family instances to a series of values and take a screenshot after each family is updated we'll need to make sure our images won't overwrite each other and that we can tell what the image is of. Let's add the element ID of the current family instance that we're changing to the image file name. This will insure we don't overwrite an image we've already taken.
something like... (parameterValue_familyInstanceNumber.png) our images will end up being labeled:
We're going to add a custom package from the package manager into our custom node. This node Element to Ids by Andreas Dieckmann will grab the element id of the current family instance, then we convert it to a string, and add it to the end of our file name with the concat strings node.
Now that this is taken care of we can move on to looking at how to call our custom node over multiple sequences. Conceptually, what we want to do is call our node with a series of different inputs. We want the inputs to structured like this:
Our node has 3 inputs n, family instance, and param.
The argument parameter will stay constant: as 'Radius'
The argument n will vary through the list of numbers we generate, in this case 10..40..10 will give us: 10,20,30,40.
the argument fi(familyInstance) will vary through each family instance in the list from Get Family Instance By Type but, we only want to vary the family instance argument after we've updated the model to each of the n values and taken an image.
conceptually, this will end up looking like:
We want to call our node, many times, each time we change the arguments. First we update the value that we're going to set the parameter to, but we hold the family instance constant. Then when we get to the end of the lists of values, we change the family to the second instance in that list, and go to the beginning of the value list again.
Okay, so now that we know what we want, how do we do it in Dynamo?
In the graph below I'm using the cartesian product node, which is lacing its input lists together. You can read more about this operation here, but as a simple example:
if List X = (B,C)
and List Z = (1,2,3)
CartesianProduct = x
then X x Y = (B,1)(B,2)(B,3)(C,1)(C,2)(C,3)
we've laced the lists together creating a new list with pairs from each list. We can use this CartesianProduct to create the input arguments for our UpdateParameterAndSaveImage node.
List 1 = the list of values, because the first argument of the custom node takes these values.
List 2 = the list of family instances we want to change because the second argument of the custom node takes the family instance.
Finally the CartesianProduct 'comb' input is really a function or custom node, that is going to be called on each pair in the new list.
The graph below is more complex and approaches the problem in a different way, instead of using the CartesianProduct node to generate a new list with pairs of the appropriate arguments to pass to the UpdateParametersAndSaveImage node we'll use curry and partial function application to generate new custom nodes with some of the inputs set and held constant while we vary others.
In the graph below I am using some functional programming mechanisms like partial function application and currying, you can read more about them here, but I will explain them as well.
The first thing to notice is that we pass the radius parameter into our custom node, and in output underneath the custom node says 'function' This means we've turned out custom node into new custom node. One that only takes 2 arguments,now the parameter is filled in.
Before passing the parameter into the custom node, the node took 3 arguments :
after we pass the Radius parameter in, we have actually partially applied our custom node, this returns a new node (thats what the function output means) this new node only takes 2 arguments:
UpdateParametersAndSaveImage(n(value),fi(familyInstance)), the parameter is now held constant to 'Radius', notice that we only have 2 arguments that we can change for this node.
This is important to understand because the curry node and the map node it's hooked up to are going to do the same thing, just over a series of values. They will let us take our new custom node UpdateParametersAndSaveImage(n(value),fi(familyInstance)), the 2 argument one, and output a new function for each value in the list, just like what we wanted.
Let's step through it:
The curry will take our new node that takes 2 arguments, and it will return another new node that takes one less argument. The output of curry as you can see is another 'function'. This function looks like this:
It only takes one argument now.
You can think of the map node as taking a list of something (in the sequence input) and doing something to each one (in the f(x) input). Said another way, it maps a function to each item in a list.
The map node in our graph maps our list of values (10,20,30,40,50) into the x value of the curry node. So Curry happens five times, each time creating a new custom node, and each time it embeds the value from the list into the n argument space of the UpdateParametersAndSaveImage(n,fi).
the output from the map node is a series of 4 functions:
In bold I have put the arguments that are held constant, these nodes really only accept 1 argument, which is a family instance argument.
The last step is a bit confusing... we are going to partially apply a Map to our sequence of family instances. This will generate a function which maps a function to a sequence but where the sequence is held constant.
Map( f(x) ,(familyInstance1,familyInstance2,familyInstance3,familyInstance4) )
Then we finally map this function to our other 4 partially applied functions.
The missing function in the above Map is filled in with each of our previous 4 functions. Remember that Map will call the function in the f(x) input once for each item in the list. So This will call our custom node, 16 times, once for each of the partially applied functions times once for each of the items in the mapped list.