Looping through the Grasshopper canvas

Loops in programming are very powerful. Oftentimes, we want to repeat the same piece of code several times, maybe with some slight changes each time.

Grasshopper, being a visual programming interface, tries to be a little simpler and easier to grasp than the regular horror of text-based programming. One of the key losses that comes from this simplicity is the inability to natively perform loops – we cannot use the output of one component as an input for a component upstream.

Trying to perform a recursive calculation in Grasshopper produces a recursion error
This is bad!

Hoopsnake

Hoopsnake gets around this problem using an approach similar to the way that Excel deals with the problem – what it calls circular references. But while a good solution in certain cases, Hoopsnake isn’t always easy to grasp and can be clumsy to set up in larger canvases – highlighting possibly why Grasshopper has removed the ability in the first place.

David Rutten’s permutation component

An alternative solution was created by David Rutten in this post, and looks at creating loops by cycling through values in the number slider component.

Say you have a few sliders in your canvas. Rutten’s component looks for all sliders. When the slider is changed, the canvas recalculates, as always happens in Grasshopper. The component listens for when all calculations have finished, then it changes the value of one of the sliders. This of course triggers another recalculation. The component listens again for calculations to finish, and changes the slider again. It cleverly repeats this cycle until it has gone through every value in the slider. If the component has found multiple sliders, it will run through every combination of these slider values. So if you have two sliders with integer values of 0-10, the canvas will recalculate 121 times (i.e. 11 x 11).

A component created by David Rutten that cycles through all possible permutations of a number slider in Grasshopper

The curse of dimensionality

Nowadays, Grasshopper is being used for much larger projects than ever envisaged when it was first created. GH documents are likely to have many dozens of sliders. If we have 20 sliders each with just 11 possible settings, Rutten’s component will force the recalculation 11^20 times – i.e. 672 billion billion times! (This concept of small numbers multiplying up very quickly is something referred to as the curse of dimensionality and is a big challenge when designing computational algorithms on large data sets.)

Mostapha’s modification

Perhaps in recognition to the Curse of Dimensionality, or simply the need that you might not want to involve every slider in your canvas in your recursion, Mostapha Roudsari has created a modified version of Rutten’s component. You can find it as part of his example files for his Honeybee plugin. (Jump to example 6, “Parametric grid-based analysis” and look for the component called “runItAll”.)

The key change in this component is that there is now an input called “mySliders”. Instead of running through all sliders like Rutton’s solution, this component will only cycle through those sliders connected to “mySliders”. While very powerful, this creates an initially surprising behaviour since, visually, it looks like the action to change a slider flows backwards along the wire!

The runItAll component created by Mostapha Roudsari, which allows a user to cycle through selected number sliders in Grasshopper

If no wires are connected, then the component resorts to Rutten’s component’s behaviour of modifying the values of all sliders in the canvas. This is still useful when you want to modify all sliders without spending time connecting them up.

My token improvement

This component has proven very useful for my own parametric studies, and I have been using it a lot. By using it a lot though, I have made a lot of mistakes. I’ve found it’s all too easy to accidentally run it and realise you’ve connected the wrong sliders, and find that you’ve asked Grasshopper to run a billion permutations, which GH diligently makes a happy start with. The only solution is to kill Grasshopper and reload.

I have added a message box that fires when you press the start button. This tells you the name of the connected sliders that the component will cycle through, and will calculate the number of permutations. You are then free to proceed or cancel.

jr-grasshopper-permutations

You can download this component as a UserObject here:


Next steps

This component has proven to be very useful in parametric studies. For example, if you are looking at optimal depth of window shading to control glare from the sun, you might set the depth of the shading with a slider. You can then set up a run where the glare values are automatically calculated for different shading depths.

Using a slider is quite limited however. What if you want to explore values of 0, 0.5 and 1 only? It is not possible to create a slider containing these three values. One solution is to connect a slider to a ListItem component (below) but this is quite a clumsy solution that takes time to set up and modify.

grasshopper-list-item

I have been looking at different components that can be modified remotely. For example, is it possible to use the Num component to hold modified values instead of the slider component? In a couple of recent posts, we have been looking at the ways that data is programatically stored within Grasshopper components. (Short answer – it’s possible to access, but not easy or obvious how to do it!)

grasshopper-persistent-data-access

Eventually, I want to be able to have a component where input values can be modified within a range. We don’t necessarily need to do an orthogonal grid of values like the recursive slider solution provides. If we are doing a parametric study, there are numerous other ways of choosing which discrete values we want to select as a sample within our ranges. This is a whole field called design of experiements which I am beginning to touch upon.

Appendix: BatchRun source code

private void RunScript(List<System.Object> mySliders, bool startme)
  {
    if (!startme)
      return;

    if (_running)
      return;


    _run = true;

    _mySliders = mySliders;

    GrasshopperDocument.SolutionEnd += OnSolutionEnd;
    Print(_rtnmessage);
  }

  // <Custom additional code> 
  private bool _run = false;
  private bool _running = false;
  private List<System.Object> _mySliders;
  private string _rtnmessage;

  private void OnSolutionEnd(object sender, GH_SolutionEventArgs e)
  {
    // Unregister the event, we don't want to get called again.
    e.Document.SolutionEnd -= OnSolutionEnd;

    // If we're not supposed to run, abort now.
    if (!_run)
      return;

    //throw new System.ArgumentException("Parameter cannot be null", "something weird");

    // If we're already running, abort now.
    if (_running)
      return;

    // Reset run and running states.
    _run = false;
    _running = true;

    try
    {
      // Find the Guid for connected slides
      List<System.Guid> guids = new List<System.Guid>(); //empty list for guids
      Grasshopper.Kernel.IGH_Param selSlidersInput = Component.Params.Input[0]; //ref for input where sliders are connected to this component
      IList<Grasshopper.Kernel.IGH_Param> sources = selSlidersInput.Sources; //list of things connected on this input
      bool isAnythingConnected = sources.Any(); //is there actually anything connected?

      if (isAnythingConnected) { //if something's connected,
        foreach (var source in sources) //for each of these connected things:
        {
          IGH_DocumentObject component = source.Attributes.GetTopLevel.DocObject; //for this connected thing, bring it into the code in a way where we can access its properties
          Grasshopper.Kernel.Special.GH_NumberSlider mySlider = component as Grasshopper.Kernel.Special.GH_NumberSlider; //...then cast (?) it as a slider
          if (mySlider == null) //of course, if the thing isn't a slider, the cast doesn't work, so we get null. let's filter out the nulls
            continue;
          guids.Add(mySlider.InstanceGuid); //things left over are sliders and are connected to our input. save this guid.
          //we now have a list of guids of sliders connected to our input, saved in list var 'mySlider'
        }
      }

      // Find all sliders.
      List<Grasshopper.Kernel.Special.GH_NumberSlider> sliders = new List<Grasshopper.Kernel.Special.GH_NumberSlider>();
      foreach (IGH_DocumentObject docObject in GrasshopperDocument.Objects)
      {
        Grasshopper.Kernel.Special.GH_NumberSlider slider = docObject as Grasshopper.Kernel.Special.GH_NumberSlider;
        if (slider != null)
        {
          // check if the slider is in the selected list
          if (isAnythingConnected)
          {
            if (guids.Contains(slider.InstanceGuid)) sliders.Add(slider);
          }
          else sliders.Add(slider);
        }
      }
      if (sliders.Count == 0)
      {
        System.Windows.Forms.MessageBox.Show("No sliders could be found", "<harsh buzzing sound>", MessageBoxButtons.OK);
        return;
      }

      //we now have all sliders
      //ask the user to give a sanity check
      int totalLoops = 1;
      string message = null;
      foreach(Grasshopper.Kernel.Special.GH_NumberSlider slider in sliders)
      {
        totalLoops *= (slider.TickCount + 1);
        message += slider.ImpliedNickName;
        message += "\n";
      }
      if (System.Windows.Forms.MessageBox.Show(sliders.Count + " slider(s) connected:\n" + message + "\n" + totalLoops.ToString() + " iterations will be done. Continue?", "Start?", MessageBoxButtons.YesNo) == DialogResult.No) return;



      // Set all sliders back to zero.
      //GH_Document.EnableSolutions = false;
      foreach (Grasshopper.Kernel.Special.GH_NumberSlider slider in sliders)
        slider.TickValue = 0;

      // Start a giant loop in which we'll permutate our way across all slider layouts.
      while (true)
      {
        int idx = 0;
        if (!MoveToNextPermutation(ref idx, sliders))
          break;

        // We've just got a new valid permutation. Solve the new solution.
        e.Document.NewSolution(false);
        Rhino.RhinoDoc.ActiveDoc.Views.Redraw();
      }
    }
    catch {
      _rtnmessage = "something went wrong!";
    }
    finally {
      // Always make sure that _running is switched off.
      _running = false;
    }
  }

  private bool MoveToNextPermutation(ref int index, List<Grasshopper.Kernel.Special.GH_NumberSlider> sliders)
  {
    if (index >= sliders.Count)
      return false;

    Grasshopper.Kernel.Special.GH_NumberSlider slider = sliders[index];
    if (slider.TickValue < slider.TickCount)
    {
      // Increment the slider.
      slider.TickValue++;
      return true;
    }
    else
    {
      // The current slider is already at the maximum value. Reset it back to zero.
      slider.TickValue = 0;

      // Move on to the next slider.
      index++;

      // If we've run out of sliders to modify, we're done permutatin'
      if (index >= sliders.Count)
        return false;

      return MoveToNextPermutation(ref index, sliders);
    }
  }

Update: Another solution for modifying sliders

Anders Deleuran has created an alternative way of remotely modifying slider values. His method is similar, but looks for sliders based upon their name. It is written in Python. You can download the file from the Grasshopper forum.

anders-grasshopper-loop

6 Comments

Add a Comment

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.