Dynamo and Recursion


#1

I am trying to use Dynamo to run a recursive operation that calculates the required riser heights in a stadium seating bowl. It basically takes some variable for the first riser / tread, calculates the riser, and then uses the calculated riser value to feed it into the next riser’s calculation, etc.

 

I was attempted this by making a family for the “Sightline” step, and adding the required parameters. Then in Dynamo I defined an grid array, populated it with instances of the family. The I defined the formula as a User Node, fed in the variables, and added the same user node inside itself to create “recursion.”

 

What I cant figure out, is how to pass these iterative recursive values to the different instances created by the grid array. I tried an IF comparison node, but I dont know how to match up the instance with an increment value and loop.

 

Any ideas?

 

The design problem is described in the following link:

 

http://www.sga.net/aboutsgasiproductionservices/sightline-analysis.html


#2

Unfortunately, not right now.


#3

Also is there a way to set a whole bunch of instance parameters in one node, rather than having to cascade a slew of "set instance parameter" nodes like I have?


#4

Thats awesome, thanks. I got it to work the other way by reversing the list, twice and taking the rest of the list before reversing it again. Your method is more elegant.


I also got the E values to be calculated according to a construction tollerance. Its turning out great.


Next I will see about populating the steps with seats, and cutting vomitories.



Thanks again Stephen / Matt.


#5

Another, simpler way to accomplish this is to use the filter node. This requires that your list is indeed sorted from min to max.



filter takes in a predicate as its first argument. A predicate is any node that takes 1 input and returns a boolean (true/false) value. What filter does is apply the predicate to each element of the given list. If the predicate returns true for the element, then it is included in the result list. If false, then it is omitted.



So you can filter E-Vals such that all values that are less than E Max are returned. Then, simply take the len of the result list. You can do this by connecting E-Vals to the seq argument of a filter node, and then connecting a < (less-than) node to the p(x) argument of the filter node. Then, connect E Max to the second input of the < node (that way, list elements are placed in the first input, so they are checked to make sure they are less-than E Max).


#6

Ah OK I understand.



What was happening is that when you were getting the rest of the list, it was removing an item off of the front (the value of first). What you want to do is remove an item off of the end of the list. You can use the take node to take an amount of elements off of the list; taking 1 less than the length of the list will return a list with the last element removed. Pass this list to the recursive call.



You also need to add a check to make sure that the list is not empty. Otherwise, when doing the if test, it will try to grab an element from an empty list, which will also give you that error.


#7

Ok, Ithe issue with your definition is that its returninig the value of the height of a riser. I need the value of number of steps to keep.


What I need is to look at the last value of the list, since that one will be the largest, and check it against the E Max value. If it is equal to or larger than E Max, then I return the rest of the list, minus the last one, to be avaluated again. Once I hit a value that is less than E Max, I count the number of elements in the list, and I feed that value to the "take" node to prune the list of Risers, etc to the number of steps that comply with E Max.


Does that make sense? I am essentially having problems finding the last element on the list. I tried using "reverse", and the using "first" but I was getting the same error messages.



#8

If I'm not mistaken, you're referring to your Highest Step Number Calc node?



Here's what your node logic looks like:


let E-Vals = List of E-Values to search for a maximum in


let E Max= Current Maximum


if element at index (length E-Vals) of list E-Vals >= E Max


then Return the Highest Step Number with the same E Max and searching the rest of E-Vals


otherwise Return the length of E-Vals




There are some problems here.



  1. Lists are indexed at 0. If a list has only 1 element, it's length is 1, and the only element is at index 0. Therefore, if you try to get the length of the list and use that as an index, it will never work. Subtracting the length by 1 is the best approach (you see this a lot in actual programming). The reason you saw this when you subtracted 1 is because at length 0, you're trying to access index -1, which is also invalid.

  2. Your recursion may end before you've searched the entire list. For example, if the if node takes the false branch, then recursion ends. However, it's not guaranteed that you've traversed the entire list at this point; the actual maximum may still be in there. The recursion should end ONLY when the whole list has been processed.


Here are my proposed changes:


For starters, make your if node check if the list is empty. You can do this by using the isEmpty? node, which will return true if a given list is empty. Obviously you'll want to hook E-Vals into this.


For the true branch: if the list is empty, then the obvious maximum is the one we already have. So simply connect E Max to here.


For the false branch: if the list is not empty, then we need to perform some recursion. We can calculate the new max by taking the first element in the list and then comparing it with E Max. Whichever is bigger is what we will pass in as the new E Max. For the new E-Vals, we already checked the first element of our current E-Vals, so we need to search the rest of E-Vals.


#9

Ok I understand that. What I was doing was getting the length of the list, and the using that number as the index to get the last value. Then I was checking if that value is greater than or equal to my max rise, and if it is I passed the rest of the list to be evaluated again. If not it would take the first value as the answer.




How could this be giving me the wrong index? I even tried subtracting one from the index value before passing it on.


#10

I don’t have a computer in front of me right now, but I’ll try to help.



That error means that the index old the list you’re trying to access doesn’t exist because the list isn’t large enough. When you’re using the “get” node, remember that lists are indexed at zero, meaning that to get the second element in a list, you want to get index 1.



With that in mind, make sure that the recursion isn’t making the list small enough that you can’t access that index. Using an if node will allow you too check good the case where the list is too small.


#11

Ok now I am checking to see if the riser heights are higher than a maximum value, and I am cutting down the list of instances (steps) to suit. I keep getting the message:


The index was outside the range of elements in the list.


Parameter name: n


What does this mean?


#12

OK, it looks like your new Step Creator R is set up correctly. It looks like you just want to take each generated R value and then apply it to each family instance.



Easiest way to accomplish this is to use combine again, except this time, connect the family instances to listA and the new R values to listB. Then, connect a Set Instance Parameter to f(A, B). Finally, connect a string containing "R" (sans quotes) to the param input of Set Instance Parameter.


#13

Cool, that worked. It seems like it does not like families with "Default" as the type name. Now I have to figure out how to calculate the Riser (R) for every step, and pass that value to every instance. Then I need to draw a curve in plan that represents the layout of the stadium, and I will loft the profile created by the steps on the path to create the bowl.


Lets see how it goes. Thanks again Stephen.


Also, initially these families where in metric units, and I discovered that Dynamo seems to be assuming feet as units. It should match the units of the family. Please look into that.


#14

The change in Step Creator D is fine I believe.



The message is just the result of the evaluation printed to be human readable; when the Revit API converts Family Instance objects to a string, it's always "Autodesk.Revit.DB.FamilyInstance" The result you're seeing should mean that it's working. This is what I see when I run it using an arbitrary family type:




#15

OK that helps. I fixed some bugs in Step Creator D and Step Creator E.


#16

It appears like the file you uploaded doesn't contain your changes.



It looks like you need to change the input values for Step Creator D. The current values are causing it to not generate anything.


#17

Here are the files:


#18

I believe this is the solution:



I modified Step Creator D so that it generated all of the D values you'll need.



I then modified Step Creator E so that it generated E values for every D value passed in. It will recursively pull two elements from the given list of D Values, use them and the given previous E value to generate the next E value, then return a new list comprised of the calculated E Value added to the E Values to be calculated by the recursive call. We also pass the calculated E Value to the recursive call so that it can be used on the next iteration.



I modified E Calc so that it was based on given D values, rather than calculate them on the fly.



In the home workspace, I first generate the D Values, then use them to generate the E Values. I then use the combine node to combine them into XYZs (I leave the X and Z inputs missing so that they are the ones used. Y will always be 0). I then pass the generated XYZs into the Family Instance Creator node.


#19

Here are some updated files and definitions. D is calculated in each step by taking the previous D and adding T to it.


Thanks again.


#20

OK I think I understand. I don't see a D Calc node in the files you gave me; is that what Step Sequence is supposed to be?



Or better yet, how exactly are you calculating D on each iteration?