Components in Grasshopper can run iteratively if more input sources are connected than expected. For example, the Series component expects one source in each input (parameter access GH_ParamAccess.item), and when more are given, the component runs iteratively and outputs a tree instead of a list of numbers.
This gets complicated when the output of a component is set with GH_ParamAccess.tree. When successive iterations set data to the same DataTree output, the trees are merged rather than becoming deeper branches of the same tree, like what happens in the Series component.
Is it possible to predict if a component will be run iteratively in order to manually separate the tree branches for each iteration?
During SolveInstance, the current iteration index can be queried using the DA.Iteration property. However, since each solution is separate, I don’t know if there is a way to know beforehand if iterations are going to happen at all.
Are you trying to predict the extact amount of SolveInstance iterations or just to know whether it will run more than once?
If you only have tree or list access inputs, it’s relatively easy:
If all Inputs have only tree access, SolveInstance will always run only once.
If some Inputs have list access but there’s no item access, SolveInstance will run as many times as there are branches in a list access param with highest amount of branches
As soon as the you get item access params into the mix, predicting the amount of iterations gets quite complicated. You have to replicate the branch matching and item matching behaviour to get there.
That’s also why I would suggest avoiding DataTree access all together if possible. You will quite likely confuse users if you don’t replicate the iteration behaviour of native components. And at that point, why not use the native implementation by using List access?
Anyway, if all you want is to just check if component will run more than once, you can do something like this:
The inputs have a mix of item and list access, so your snippet works great for checking for iterations.
I get your point, but the output of 1 iteration requires a tree by itself. I therefore want to go 1 level deeper in the tree path so that the outputs of different iterations do not merge in the same paths.
Hey again. Okay, this is an intresting challenge.
These IGH_DataAccess methods are not very well documented.
But at each iteration you can:
check the path that would be assigned by default when outputting a list
override that path and append a custom path to the tree instead
With these, you can extend the default behaviour with additional branching logic - eg. keeping the default behaviour when combining input item and list access but doing some custom “subbranching” of the results.
See this example:
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddTextParameter("Items (str)", null, null, GH_ParamAccess.item);
pManager.AddTextParameter("Lists (str)", null, null, GH_ParamAccess.list);
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddTextParameter("Result (custom branching)", null, null, GH_ParamAccess.tree);
pManager.AddTextParameter("Result (default control)", null, null, GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess DA)
{
// Collect this iterations inputs
string currentItem = default;
List<string> currentList = new List<string>();
DA.GetData(0, ref currentItem);
DA.GetDataList(1, currentList);
// Let's combine the current item with items from current list
// to illustrate how the items combine across iterations by default
var result = currentList
.Select(listElement => $"{currentItem} - {listElement}")
.ToList();
DA.SetDataList(1, result);
// This is the path the results would be put on
// if you were using just using `DA.SetDataList(0, result);`
// > {0,0,0}
var basePath = DA.ParameterTargetPath(0);
var baseIndex = DA.ParameterTargetIndex(0);
var defaultPath = basePath.AppendElement(baseIndex);
// Instead you can override this path adding result of a specific subbranch
// Lets say in this case just to duplicate each result branch x-times
// > {0,0,0,0}, {0,0,0,1} ... {0,0,0,n}
var nOfSubbranches = 2;
for (int extraBranchIndex = 0; extraBranchIndex < nOfSubbranches; extraBranchIndex++)
{
var path = defaultPath.AppendElement(extraBranchIndex);
var data = result.Select(x => new GH_String(x));
var tree = new GH_Structure<GH_String>();
tree.AppendRange(data, path);
DA.SetDataTree(0, tree);
}
}