How to trigger updates down only selected outputs of component?

So here’s some code, using the simplest set up I could think of. Integer data only, single input, single output. The component tests whether the new data is different from the previous data, and if it is, sets a boolean and triggers a new solution just after the current one completes.

using System;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;

namespace TestComponent
{
  public sealed class SmartUpdateComponent : GH_Component
  {
    public SmartUpdateComponent()
      : base("Smart Update", "Smupdate", "Only trigger an update if a value changes.", "Test", "Test")
    {
      UpdateOutput = true;
      PreviousData = "none";
    }
    public override Guid ComponentGuid => new Guid("{60F1F671-78F5-4A23-87EA-CC2BF6B6C296}");

    protected override void RegisterInputParams(GH_InputParamManager pManager)
    {
      pManager.AddIntegerParameter("Input", "In", "Data input.", GH_ParamAccess.tree);
      pManager[0].Optional = true;
    }
    protected override void RegisterOutputParams(GH_OutputParamManager pManager)
    {
      pManager.AddIntegerParameter("Output", "Out", "Data output.", GH_ParamAccess.tree);
    }

    /// <summary>
    /// Gets or sets whether the immutable output ought to be assigned.
    /// </summary>
    private bool UpdateOutput { get; set; }
    /// <summary>
    /// Gets or sets the cached data from last time.
    /// </summary>
    private string PreviousData { get; set; }

    protected override void ExpireDownStreamObjects()
    {
      if (UpdateOutput)
        Params.Output[0].ExpireSolution(false);
    }
    protected override void SolveInstance(IGH_DataAccess access)
    {
      // This stops the component from assigning nulls 
      // if we don't assign anything to an output.
      access.DisableGapLogic();

      // Get the current tree and immediately assign it to the output.
      // Better safe than sorry. Since we only selectively expire the
      // output we should still prevent updates, however there is no
      // reason to not have the most recent data always in the output.
      access.GetDataTree(0, out GH_Structure<GH_Integer> tree);
      access.SetDataTree(0, tree);

      string currentData = tree.DataDescription(false, true);

      // If we were supposed to update the output (meaning it was expired), 
      // then we know for sure that we don't have to update again.
      if (UpdateOutput)
      {
        UpdateOutput = false;
        PreviousData = currentData;
        return;
      }

      // If the current data differs from the last time,
      // we need to remember that the output needs updating and
      // we need to schedule a new solution so we can actually do this.
      if (!string.Equals(PreviousData, currentData))
      {
        UpdateOutput = true;
        PreviousData = currentData;

        var doc = OnPingDocument();
        doc?.ScheduleSolution(5, Callback);
      }
    }
    private void Callback(GH_Document doc)
    {
      // The logic is all in our expiration method, but we do have 
      // to expire this component.
      if (UpdateOutput)
        ExpireSolution(false);
    }
  }
}

This file requires the compiled component:
smart update test.gh (14.5 KB)

3 Likes