Dynamically updating an output, for a slow-motion animation

Hi, I am trying to get a dynamic output from a C# component to animate the iteration steps, but I can only get the final result out:

private void RunScript(bool trigger, ref object A)
    A = 10;
      for(int i = 0; i < 10;i++){
        int num = 10 - i * 1;
        A = num;

Any hint is very much appreciated!

Grasshopper will not redraw the viewports until the solution has finished. So you’re assigning different values to A, but they keep overwriting each other until you’re done. Only then will there be a redraw.

If you want to animate a result, you can choose to either do it through the available solution mechanism, which means scheduling a solution 200 milliseconds in the future, and during that solution outputting a new value, or you can choose to do your own cycling, but then you cannot output the value from your component, because that requires regular solutions.

countdown.gh (7.0 KB)

private void RunScript(int N, bool Run, ref object A)
  if (N != _maximum)
    _maximum = N;
    _counter = N;
  A = _counter;

  _run = Run;
  if (_counter < 0)
    _run = false;

  if (_run)
    GrasshopperDocument.ScheduleSolution(100, ScheduleCallback);

// <Custom additional code> 
private int _maximum = -1;
private int _counter = -1;
private bool _run = false;

private void ScheduleCallback(GH_Document document)

Hi David, thanks for your prompt response! It works nicely but I don’t understand how it loops. Could you please explain or comment your code? Does it run iteratively because the delegate is supposed to be called every 100 milliseconds? BTW, isn’t there a way to do this without using private variables?

Long Nguyen has some nice examples of components that show the animation by setting things like Stopwatch as persistent data outside of the solve instance. Then you can use refreshes or the timer component to animate. See video Day1-Part2: https://developer.rhino3d.com/videos/

1 Like

Thank you Michael, indeed this code resembles the behavior of a timer, invoked from inside. This is what I wrote today, only remaining problem is that if you don’t change the value of the nTimes slider it doesn’t function (after the first time):

 private void RunScript(List<double> x, int nTimes, bool run, ref object A)
    //based on code snippets from David Rutten and James Ramsden:

    //    A = N;
    //    if (N != _maximum)
    //    {
    //      _maximum = N;
    //      _counter = N;
    //    }
    //    A = _counter;
    //    _run = Run;
    //    if (_counter < 0)
    //      _run = false;
    //    if (_run)
    //      GrasshopperDocument.ScheduleSolution(200, ScheduleCallback);
    int n = x.Count;
    // counter = 10;
      if((nTimes != minimum)){
        minimum = nTimes;
        counter = nTimes;
      //while(counter > 0){//time iteration, now it is controlled instead with the input nTimes
      double[] x_new = new double[n];
      var input = Component.Params.Input[0];//gets sliders in
      for(int i = 0; i < n;i++){
        Random rnd = new Random(Guid.NewGuid().GetHashCode());//make the psuedo-random as random as possible
        x_new[i] = rnd.NextDouble();
        var slider=input.Sources[i] as Grasshopper.Kernel.Special.GH_NumberSlider;//i_th slider
        //unsuccessful attempts to highlight the sliders
        slider.SetSliderValue((decimal) x_new[i]);
        slider.Slider.DrawControlBackground = true;
        slider.Slider.DrawControlBorder = true;
        slider.Slider.ControlEdgeColour = System.Drawing.Color.Blue;
        slider.Slider.ControlBackColour = System.Drawing.Color.Aquamarine;
        //slider.Slider.DecimalPlaces = 4;
      //taking in the values of the sliders; making new values, for now, just randomly
      InRun = run;
      if(counter < 0){InRun = false;}
      A = x_new;
      if(InRun){GrasshopperDocument.ScheduleSolution(100, ScheduleCallback);}

  // <Custom additional code> 
  // <Custom additional code>
  //private bool _run = false;
  private int minimum = -1;
  private int counter = -1;
  private bool InRun = false;
  private void ScheduleCallback(GH_Document document)

SliderUpdate_PZN_1.0.gh (13.6 KB)

It doesn’t really loop in the traditional sense. Your RunScript method is called from within the solution, and your loop needs to run outside of the solutions. Because of this I use the document solution scheduler to trigger a new solution 100ms in the future, and inside that callback I perform the next step that would normally be part of the loop.

There are other ways of creating consecutive solutions, but this is the easiest.

Your variables need to survive between consecutive calls to RunScript, so they need to be class-level.

Thanks for your explanation, very helpful. Last question: Does the scheduler delay and run the entire GH canvas or only the component in question?

The scheduled solution doesn’t expire any components, it just runs a new solution. If nothing has been expired by then, very little will happen. This is why you need to register a callback for the schedule, so you can expire the component just before the new solution kicks off.