Replicating "Explode Tree (Bang)" component

Hi @piac ,

I am trying to somewhat replicate the Grasshopper “Explode Tree (Bang)” component with ghpython component (0.6.0.3, Rhino 5 SR12 32 bit, Grasshopper 0.9.0076).
I am looking for the same functionality that “Explode Tree (Bang)” component has, but with a two slight differences:

  • The output parameters will be automatically created, instead of manual creation used by the “Explode Tree (Bang)” component (user needs to click on the “+” sign to create).

  • The component would only create output parameters, if _runIt input is set to True. Otherwise all output parameters will be deleted, except for the “output” one.

I have two issues:

  1. After the outputs are removed, the following error message emerges:
Output parameter Index[i] too high or too low for Component Python Script

It is repeated for the number of output parameters - 1 times (because I am never removing the “output” parameter).

I am not sure if it is caused by the ghenv.Component.Params.Output.RemoveRange method on line 21.
So I tried to remove the output parameters one by one instead:

#ghenv.Component.Params.Output.RemoveRange(1, numberOfOutputs-1)
outputParamsIndices = range(1, ghenv.Component.Params.Output.Count)  # never include "output"
outputParamsIndices.reverse()  # remove the parameters from the end
for index in outputParamsIndices:
    ghenv.Component.Params.UnregisterOutputParameter(ghenv.Component.Params.Output.Item[index])
    ghenv.Component.Params.OnParametersChanged()

But again the upper error message remained.
I can solve this by checking the “Do not show this message again” check box in the lower left corner. But still I would have to do this each time Rhino is restarted. It’s a bit annoying.

  1. Second issue is that, once output parameters are removed, the wires which connected those output parameters to the panels, remain glitchy. Here is an explanation in screenshots:

Component’s _runIt input set to False:

Component’s _runIt input set to True, and output parameters connected to panels manually:

Component’s _runIt input set to False. The wires remain glitchy:

Btw. the inputData contains a tree with three branches
I would welcome any kind of help.
Component is attached below.

Thank you!

explode_tree_ghpython.gh (10.1 KB)

1 Like

Without further checking or trying it seems to me that you shouldn’t be unregistering parameters like that in a loop. You’re essentially changing the list into which you have the (original) indices pointing.

Instead I’d make a loop that runs until only the one output remains. On each iteration remove the element at the highest index, signal for change in parameters. Rinse and repeat.

/Nathan

Thank you for the suggestion Nathan!

I can see you did some pretty amazing plugins, so my level of knowledge is definitively far lower than yours.

Still, no change of the outputParamsIndices list is happening during the for loop.
And even if it would be happening, it is not significant due to it being reversed. So the last index (the largest one) is always used first.

Something else is the problem.

You shouldn’t attempt this. The Grasshopper definition solver does not expect any changes to the definition graph while the graph is being solved. This means that if you do change it, even if you manage it (by hacking around the check that you see right in your first screenshot), you might never be able to get the right answers while solving the solution. The ONLY way to update anything that is definition-related is outside the solution. Frankly, this mantra has been repeated so many times I thought you knew it :smiley:

Giulio

Giulio Piacentino
for Robert McNeel & Associates
giulio@mcneel.com

Thank you Giulio!

It seems I missed all those topics requiring something similar.

Does that mean that I can ask the component to expire the solution, and ask for a new solution? And then perform the unregistering of the output parameters?

1 Like

The way to do it is to schedule a new solution with the document, and provide a callback to the scheduler so you get informed just before the scheduled solution start. Have a look at GH_Document.ScheduleSolution()

Of course another way to do it is to run your code from outside a solution to begin with, for example via a menu or button click.

A third way is to handle the SolutionStart event for GH_Document. This is called just before any solution and you’re still allowed to make changes at this point.

1 Like

Thank you @DavidRutten.

I would go with second option and use the current _runIt input-button. But would scheduling a new solution, resolve the issue with panel wires hanging the “air” after the removal of output parameters?

No it won’t solve that, that’s an entirely separate issue. Wires are not actually things in their own right, they are merely logical connections that are drawn as bezier curves. Every parameter (IGH_Param or more derived type) has a list of sources from which it inherits data. When the canvas is painted a curve from each parameter to each of its sources is drawn.

When you have a ‘floating’ wire it means that some parameter is associated with a source which no longer exists in the document. It doesn’t get drawn because the document never asks it to draw itself, however it also doesn’t get Garbage Collected because the recipient parameter keeps it alive by storing it in its sources list.

When you remove an output, you must ensure all its connections are severed. This is not difficult, when using the GH_Component.Params.UnregisterOutputParameter(IGH_Param, bool); method, just pass in true for the second argument to isolate the output before removing it.

Thank you @DavidRutten.

@piac what is ghpython’s equivalent of Grasshopper.Instances.ActiveCanvas.Document.ExpireSolution() method?

Is it ghenv.Component.OnPingDocument().ExpireSolution()?

Can you please provide an example on performing the task outside of the solution?
Thank you.
explode_tree_ghpython2.gh (14.7 KB)

also, just a side point, but with “bang” you don’t need to click plus for each one, you can just right click the component and choose “match outputs”…

I’m extremely curious about the larger goal you’re trying to achieve. What’s the use case for dynamically removing the outputs based on a solution change (toggle)?

Hi Andrew,

Thank you!

It is suppose to be a Ladybug component with say 3 inputs: “requiredKey” (a single item), “keys” (a list), “values” (data tree). The number of branches of the “values” input is suppose to correspond to the number of items in the “keys” list.
The component is suppose to take the “requiredKey”, and check if the same one is located in the “keys” list. If it is, it will take the index of that item, and generate a number of output parameters corresponding to values.Branches[index] values.
The “_runIt” toggle is a feature of Ladybug, where somewhat longer runtime components require this input.

If I explained all of this in a confusing way, then essentially replicating the “Bang” component is what I am looking for.

Hi Djordje.

This is perhaps not what you want and also perhaps it doesn’t work, but I think when compiled components are written in C# you can also do things when a component is placed on the canvas e.g. automatically create a second component that is connected to the first. The same should probably be possible now that you can compile Python components.

So, I think, when your compiled component A changes the outputs of your automatically created component B it should work. Although, even if it works, it is, of course, somewhat hackish and thus ugly looking… (Not to me, but your Ladybug users, I guess.)

Thank you for the reply Marcus!

I haven’t checked if this can be done with Rhino WIP ghpython components. The current scope of Ladybug is still Rhino 5 and ghpython 0.6.0.3. which does not support compiling. Even if it does it has been decided to go on with uncompiled ghuser files in future as well.

Which makes sense as there are quite a few of them by now. But when you have two components A and B. A should be able to change Bs outputs in Rhino 5, too. (I think… I guess… But I am not sure at all.)

Hi Djordje, @djordje

l
This definition shows how to override the AppendAdditionalComponentMenuItems method to visualize a context menu that will have the “Match outputs” options like the Explode Tree component. This is an option possible with “GH_Component SDK Mode”, and therefore needs Rhino WIP.

explode_tree_ghpython.gh (14.5 KB)

Please have a look and tell me your thoughts. Of course, this can be further improved to even better replicate the behavior of the native component. Thanks!

Giulio


Giulio Piacentino
for Robert McNeel & Associates
giulio@mcneel.com

4 Likes

Hi @piac,

Thank you for posting the component!

I am in need of a Rhino 5 solution.
But this will definitively be useful for later on, and the users who currently work with WIP.

Thank you once again for the effort!

great work ; automatically match outputs is very helpful without need to right click every time

i know it is an old topic but maybe i find a solution and i test it
and i hope if someone can make this script automatically generate output plugged to chosen component (text ; curve , mesh …)

Hi @DavidRutten.
I was reading this post and so I started to try the solutions that you proposed, but I still end up with the same error posted at the beginning of this discussion.
This is the solution using ScheduleSoltution()

private void RunScript(bool Refresh, DataTree<object> D, ref object A, ref object B, ref object C)
  {
    if(Refresh)
    {
      GrasshopperDocument.ScheduleSolution(5, CallBack);
    }
  }

  // <Custom additional code> 

  public void CallBack(GH_Document gh)
  {
    this.Component.Params.UnregisterOutputParameter(this.Component.Params.Output[2], true);
    this.Component.Params.OnParametersChanged();
    this.Component.ExpireSolution(false);
  }

I tried also SolutionStart, but without any improvements

if(Refresh)
    {
      GrasshopperDocument.SolutionStart += SolutionStart;
    }
  // <Custom additional code> 
  private void SolutionStart(object sender, GH_SolutionEventArgs e)
  {
    this.Component.Params.UnregisterOutputParameter(this.Component.Params.Output[2], true);
    this.Component.Params.OnParametersChanged();
    this.Component.ExpireSolution(false);
  }

Any advices are very well taken.
I noticed adding params doesn’t create any problem, is in the operation of removing them.