Grasshopper: Map a path using C#

The Path Mapper is a component that allows you to map data to different branches. It is also possible to replicate this behaviour entirely in C#.

The following code maps object x to path address 0, 0, 5. See this post for a simple overview on data trees in Grasshopper.

‘DataTree’ is the class made specifically for the C# component to handle data trees. (If you’re programming in Visual Studio, it’s better to use GH_Structure.) We use a different class, GH_Path, as the tool to define the path structure.

C# code

  private void RunScript(object x, object y, ref object A)
  {

    int[] address = {0, 0, 5};
    var pth = new GH_Path(address);
    var tree = new DataTree<object>();

    tree.Add(x, pth);
    A = tree;

  }

Grasshopper: automatically create a value list in C#

Another example on how to automatically create a value list in Grasshopper using the C# component.

This file is mostly for personal reference, but there are snippets in here that you may find useful. I designed this component to read a text file containing XML-like data, save the data in a class structure, and then filter the data. The filtering is done by a drop-down list that is automatically added by the component, and is pre-populated with valid options.

Useful code includes:

  • How to create a value list with C# code
  • How to populate the value list
  • How to create and use a DataTree in the C# component

C# code

This code is designed for the C# component in Grasshopper.

private void RunScript(List<string> x, int y, ref object params_, ref object results_)
  {

    var models = new List<Model>();

    //parse input text
    for (int i = 0; i < x.Count; i += (valuecount + 2))
    {
      var model = new Model();
      model.Params = FormatParamString(x[i]); //convert CSV to list of doubles
      for (int j = i + 1; j < i + 4; j++)
      {
        var item = new DictItem();
        FormatModelLine(x[j], out item.Name, out item.Value);
        model.Results.Add(item);
      }
      models.Add(model);
    }
    Component.Message = models.Count.ToString() + " models";

    AnalysisTypes = CalcUniqueAnalyses(models);

    //make dropdown box
    if(Component.Params.Input[1].SourceCount == 0 && Component.Params.Input[0].SourceCount > 0)
    {
      var vallist = new Grasshopper.Kernel.Special.GH_ValueList();
      vallist.CreateAttributes();
      vallist.Name = "Analysis types";
      vallist.NickName = "Analysis:";
      vallist.ListMode = Grasshopper.Kernel.Special.GH_ValueListMode.DropDown;

      int inputcount = this.Component.Params.Input[1].SourceCount;
      vallist.Attributes.Pivot = new PointF((float) this.Component.Attributes.DocObject.Attributes.Bounds.Left - vallist.Attributes.Bounds.Width - 30, (float) this.Component.Params.Input[1].Attributes.Bounds.Y + inputcount * 30);

      vallist.ListItems.Clear();

      for(int i = 0; i < AnalysisTypes.Count; i++)
      {
        vallist.ListItems.Add(new Grasshopper.Kernel.Special.GH_ValueListItem(AnalysisTypes[i], i.ToString()));
      }
      vallist.Description = AnalysisTypes.Count.ToString() + " analyses were found in the SBA file.";

      GrasshopperDocument.AddObject(vallist, false);

      this.Component.Params.Input[1].AddSource(vallist);
      vallist.ExpireSolution(true);
    }

    //we now have our results in a nice classy format. let's convert them to a datatree
    var resultsvals = new DataTree<double>();
    var paramvals = new DataTree<double>();
    string astr = AnalysisTypes[y];

    Component.Params.Output[1].VolatileData.Clear(); //bug fix for when new dropdown is made
    Component.Params.Output[2].VolatileData.Clear(); //bug fix for when new dropdown is made

    for (int i = 0; i < models.Count; i++)
    {
      var pth = new GH_Path(i);
      foreach (var result in models[i].Results)
      {
        if(astr == result.Name)
        {
          resultsvals.Add(result.Value, pth);
          foreach(var param in models[i].Params)
          {
            paramvals.Add(param, pth);
          }
        }
      }
    }
    results_ = resultsvals;
    params_ = paramvals;




  }

  // <Custom additional code> 

  int valuecount = 3;
  List<string> AnalysisTypes = new List<string>();

  public class DictItem
  {
    public DictItem()
    {
    }

    public string Name;
    public double Value;
  }

  public class Model
  {
    public Model()
    {
    }

    public List<double> Params = new List<double>();
    public List<DictItem> Results = new List<DictItem>();

    public List<string> ToListString()
    {
      var rtnlist = new List<string>();
      foreach (var result in Results) rtnlist.Add(result.Name + ", " + result.Value.ToString());
      return rtnlist;
    }

  }

  List<double> FormatParamString(string input)
  {
    var rtnlist = new List<double>();


    input = input.Replace("<", "");
    input = input.Replace(">", "");
    input = input.Replace('m', '-');
    input = input.Replace('_', '.');
    string[] splitstring = input.Split('c');
    foreach(string str in splitstring)
    {
      try
      {
        rtnlist.Add(Convert.ToDouble(str));
      }
      catch
      {
        rtnlist.Add(0);
      }
    }
    return rtnlist;
  }

  /// <summary>
  /// Get name and value of analysis from an SBA string
  /// </summary>
  /// <param name="input"></param>
  /// <param name="name"></param>
  /// <param name="val"></param>
  /// <returns></returns>
  void FormatModelLine(string input, out string name, out double val)
  {
    int firstopen = input.IndexOf('<');
    int firstclose = input.IndexOf('>');
    int lastopen = input.LastIndexOf('<');

    name = input.Substring(firstopen + 1, firstclose - firstopen - 1);
    val = Convert.ToDouble(input.Substring(firstclose + 1, lastopen - firstclose - 1));
  }


  /// <summary>
  /// Get analysis types from list of models
  /// </summary>
  /// <param name="models"></param>
  /// <returns></returns>
  List<string> CalcUniqueAnalyses(List<Model> models)
  {
    List<string> rtnlist = new List<string>();
    foreach (var model in models)
    {
      foreach (var result in model.Results)
      {
        bool found = false;
        foreach (var calc in rtnlist)
        {
          if(calc == result.Name) found = true;
        }
        if(!found) rtnlist.Add(result.Name);
      }
    }
    return rtnlist;
  }

C#: Convert all images in a folder from PNG to JPG

How to convert all images in a folder from PNG to JPG.

This method is written in C#. Input a folder path. The code will then find all files with .png file extensions in that folder, and save them again as .jpg files. Original files will not be deleted.

The Image.Save method allows for a wide range of image formats including bmp, tiff and gif. You can edit the code below for your own file formats.

C# method to convert images to JPG

        /// <summary>
        /// Converts all images in a folder to JPG
        /// </summary>
        /// <param name="folder">String representing folder location</param>
        public void ToJPG(string folder)
        {

            foreach (string file in System.IO.Directory.GetFiles(folder))
            {
                string extension = System.IO.Path.GetExtension(file);
                if (extension == ".png")
                {
                    string name = System.IO.Path.GetFileNameWithoutExtension(file);
                    string path = System.IO.Path.GetDirectoryName(file);
                    Image png = Image.FromFile(file);
                    png.Save(path + @"/" + name + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
                    png.Dispose();
                }
            }
        }

Grasshopper: Calculate the Pareto front in multi-objective data in C#

A method for returning a collection of Pareto-optimal data. Pareto analysis is used in multi-objective optimisation to search for potential non-dominated solutions, i.e. solutions for which there are no solutions that perform better in every objective being assessed.

pareto-front

Input a collection of data. This data is accepted as Grasshopper’s DataTree format. The ‘tree’ contains a collection of branches. Each branch contains a list. Each list corresponds to the list of objective results corresponding to a single node.

The method sorts the input data into two DataTrees: Pareto-optimal branches and non-Pareto-optimal branches.

The algorithm is simple and unsophisticated, running in O(n2). It is fine for smaller data sets, though you may wish to investigate more sophisticated algorithms for larger datasets.

C# code to find the Pareto front

  private void RunScript(DataTree<double> data, ref object opt, ref object nonopt)
  {

    DataTree<double> optimal = new DataTree<double>();
    DataTree<double> nonoptimal = new DataTree<double>();

    //data should be a tree, where each branch is equivalent to one data point, and the length of the list is equal to the number of parameters.
    for(int n = 0; n < data.BranchCount; n++) //for each node
    {
      //check it against every other node
      //we need to find one node where every parameter is superior. if not, we have pareto optimality
      bool superiornodefound = false;
      for (int i = 0; i < data.BranchCount; i++) //check node i
      {
        bool issuperior = true;
        for(int p = 0; p < data.Branch(0).Count; p++)
        {
          if(data.Branch(i)[p] > data.Branch(n)[p])
          {
            issuperior = false;
            break;
          }
        }
        if(issuperior && i != n) superiornodefound = true;
      }
      if(superiornodefound) nonoptimal.AddRange(data.Branch(n), new GH_Path(nonoptimal.BranchCount));
      else optimal.AddRange(data.Branch(n), new GH_Path(optimal.BranchCount));
    }

    //return outputs
    opt = optimal;
    nonopt = nonoptimal;

    //grasshopper-related UI
    double optimalratio = Math.Round(100.0 * optimal.BranchCount / data.BranchCount, 1);
    Component.Message = optimalratio.ToString() + "% optimal";
    Component.Description = optimalratio.ToString() + "% of solutions are Pareto optimal.";

  }

Get a point between two points in Grasshopper with C#

How to get a point a certain ratio between two points in Grasshopper, using the C# component.

The method below allows you to return a point between two input points. Input two Point3d values and a number between 0 and 1.

‘Ratio’ is a value between 0 and 1. Enter 0 and the resultant point will be on top of frompt. Enter 1 and it will be on top of the topt. Enter something between, and the point will also be between. You can also enter a value that is !=[0,1] to extrapolate.

The easiest way to use the method is to paste it into the ‘additional code’ section of the C# component.

C# method

  private void RunScript(Point3d pt1, Point3d pt2, double t, ref object A)
  {

    A = MovePoint(pt1, pt2, t);

  }

  // <Custom additional code> 
  public Point3d MovePoint(Point3d frompt, Point3d topt, double ratio)
  {
    return new Point3d(ratio * (topt.X - frompt.X) + frompt.X, 
      ratio * (topt.Y - frompt.Y) + frompt.Y, 
      ratio * (topt.Z - frompt.Z) + frompt.Z);
  }
  // </Custom additional code> 

Grasshopper C# component points ratio

Points in Rhino viewport from Grasshopper

Read and edit persistent data in Grasshopper components with C#

A guide to accessing, reading and editing the persistent data property of Grasshopper components. This guide follows on from this post.

Every input of normal Grasshopper components has the ability to save persistent data. Persistent data is what you see when you right-click on a component and set the input’s value from there. The opposite, data fed into a component from another component, is called volatile data.

In this example, I will take the Circle CNR component. Using techniques borrowed from this post and this post, I am going to create a new CNR component using C#, then modify its persistent data.

circle CNR Grasshopper component

Add persistent data

Firstly, we need to add or reference the component for which we want to modify the persistent data.

Here, I am creating a new instance of Circle_CNR, which is contained within curve.gha.

    //Create the component
    var cs = new CurveComponents.Component_CircleCNR();

    //add the circle centre (input 0)
    var pp = cs.Params.Input[0] as Grasshopper.Kernel.GH_PersistentGeometryParam<Grasshopper.Kernel.Types.GH_Point>;
    pp.PersistentData.ClearData();
    pp.PersistentData.Append(new GH_Point(new Point3d(0, 0, 0)));

    //add the circle radius
    var pn = cs.Params.Input[2] as Grasshopper.Kernel.GH_PersistentParam<Grasshopper.Kernel.Types.GH_Number>;
    pn.PersistentData.ClearData();
    pn.PersistentData.Append(new GH_Number(y)); //y is another variable

Read persistent data

Persistent data is saved as a GH_Structure. This is a tree-like data structure.

The ‘Append’ method above will add the data item to the final branch in the structure. Since we cleared the structure in the line before, there are no branches, so Grasshopper adds the data to a new branch.

So, in order to read our data, we need to access the list contained in branch 0.

    var readpt = cs.Params.Input[0] as Grasshopper.Kernel.GH_PersistentParam<Grasshopper.Kernel.Types.GH_Point>;
    return readpt.PersistentData[0];

A full example

In the manner of this post, here I have a C# component that creates an instance of the CNR component hidden from the user on a dummy document. By modifying the CNR component’s input persistent data, we can modify the properties of the circle. The circle is calculated, and we read the output from the CNR component.

  private void RunScript(double y, ref object A)
  {

    //Create the component
    var cs = new CurveComponents.Component_CircleCNR();

    //add the circle centre (input 0)
    var pp = cs.Params.Input[0] as Grasshopper.Kernel.GH_PersistentGeometryParam<Grasshopper.Kernel.Types.GH_Point>;
    pp.PersistentData.ClearData();
    pp.PersistentData.Append(new GH_Point(new Point3d(0, 0, 3)));

    //add the circle radius (input 2)
    var pn = cs.Params.Input[2] as Grasshopper.Kernel.GH_PersistentParam<Grasshopper.Kernel.Types.GH_Number>;
    pn.PersistentData.ClearData();
    pn.PersistentData.Append(new GH_Number(y)); //y is another variable

    //run calculations
    cs.ExpireSolution(true);

    //add to a dummy document so we can read outputs
    var doc = new Grasshopper.Kernel.GH_Document();
    doc.AddObject(cs, false);

    //read output circle
    cs.Params.Output[0].CollectData();
    A = cs.Params.Output[0].VolatileData.get_Branch(0)[0];

    //remove that component
    doc.RemoveObject(cs.Attributes, false);

  }

circle CNR component C# Grasshopper

Manually expire downstream components in Grasshopper with C#

How does component calculation in Grasshopper work, and how can we control how to expire downstream components in Grasshopper?

The calculation of a Grasshopper canvas is quite complicated. It has to be able to calculate which components need calculating, and the order it does it in.

How Grasshopper does calculations – usually

It does this through a system of ‘expired’ markers. If a component needs calculating, it is marked as ‘expired = true’. It then solves each component marked as expired, one at a time, until all expired components have been calculated. A calculated component can then have its ‘expired’ flag set to false.

A component will be marked as expired if any changes are made to it, for example, it receives a new value in one of its inputs. Furthermore, if a component is marked as expired, by default, every component downstream of this component will also be marked as expired.

This behaviour is quite reasonable in most cases in Grasshopper. It guarantees that all components will be up-to-date, and that each component’s output will be correct according to the latest input values happening upstream. It also ensures that components are not re-calcualted needlessly – only components that need calculating will be recalculated.

What if this behaviour isn’t what I want?

On a rare occasion, this method of component calculation is actually not helpful for us. This usually happens when we are trying to perform another task which is also not in the spirit of Grasshopper workflows.

I have recently been working on the BatchRun component to make it more usable in design workflows. This component remotely adjusts sliders on the canvas, allowing a form of iterative workflow not normally possible in Grasshopper.

One key feature I needed from BatchRun was that, for every movement of the slider, a new set of geometry would be generated in Grasshopper, and this requires a unique name. The most logical solution is to generate a name from the slider values. So I added a ‘name’ output:

Grasshopper component Batchrun for looping

When the user presses ‘startme’, BatchRun will automatically cycle through the sliders. Two sliders, each with 11 values, will produce 121 combinations of the two sliders. To be sure that the user wants to do this possibly time-consuming task, a message box appears:

Grasshopper message box for BatchRun component

If the user presses ‘yes’, and only when the user presses ‘yes’, Grasshopper should start to iterate through the sliders.

Why wouldn’t this normally work?

Notice that the component is initiated by a boolean button. The behaviour of the BatchRun component, explained fully here, dictates that BatchRun will then adjust the sliders. The intended action is that the sliders will then trigger the ‘building height’ and ‘building width’ params, which then will trigger the ‘doCalcs’. DoCalcs will pick up the name from the BatchRun ‘name’ output.

This normally works well. DoCalcs recognises that, when a slider has changed, both the slider values and the name have expired, and doCalcs will only function once both the sliders and the ‘name’ are up-to-date.

But what about, right at the beginning, when the user has pressed the button just to trigger the message box? The user may press ‘no’, in which case, we shouldn’t expect that any calculations have taken place. But watch what happens instead:

Expiration wave in Grasshopper with Batchrun component

Because the button is pressed, this instantly expires the BatchRun component. This is necessary – since it’s this expiration which causes BatchRun to wake up, calculate itself, and realise it needs to show a message box.

But – BatchRun itself also has outputs. Anything connected to these outputs will then be expired. So the panel becomes expired. And, in turn, doCalcs becomes expired.

Then, once this ‘expiration wave’ has come to a stop, Grasshopper then sets itself up with doing the calculations on these expired components. As a simplified rule, it starts with the left-most components and works right.

The trouble is that, even when a component’s inputs’ data has not changed, if that component has been marked as ‘expired’, it still feels the need to recalculate itself. So, the simple act of pressing the button will compel doCalcs to recalculate – even if the user then selects ‘no’ on the messagebox that is triggered.

What’s the solution?

Ideally, we want BatchRun to be expired whenever the startme button is pressed. This is of course important – since it’s expiration which triggers BatchRun to do calculations on itself.

But, we want control on when BatchRun expires downstream objects. Can we use some logic to control whether or not BatchRun will send an expiration to downstream objects?

To try this out, I will be working on a component developed in Visual Studio. As far as I know, these techniques aren’t possible directly in the C# component in Grasshopper. You can set yourself up for developing components in VS here.

How not to expire downstream objects

My first idea was to try and find a component property called ‘Expired’ or something like it. But that failed – there’s nothing there!

Grasshopper Visual Studio expire component Intellisense

There are plenty of methods to force a component to expire and recalculate, but nothing to cancel it.

But there must be some other way of controlling the expiration wave, right?

How not to expire downstream objects, v2

When developing Grasshopper components in Visual Studio, you have access to a wide range of override methods. Override methods control various behaviours of our component class. If we don’t implement an override, these methods work in the background and implement default behaviour. By implementing an override, we can override this default behaviour. To view which overrides are possible, simply type ‘override’ in your component class, and IntelliSense should show you a drop-down.

Grasshopper visual studio override methods in C#

We can then select the method for which we want to change the default behaviour, for example:

Grasshopper visual studio override AddedToDocument

The ‘base’ part is added automatically when we select the override from IntelliSense. The ‘base’ calls the default behaviour of that method.

We can add extra code beneath or above ‘base’. This code will be called in addition to the ‘base’ code. We can also remove the ‘base’ line, which effectively removes the default behaviour alltogether.

Having a dig through the various overrides reveals one called ExpireDownStreamObjects(). Perfect – this is the one! What I can do is remove the base from this override, so Grasshopper will no longer expire downstream objects. Then, I can use the methods above to manually expire the components that I want, when I want, during the solution.

So, I simply comment out the base, and add a few lines into my SolveInstance. This extra code finds the components I want to expire at strategic times, and asks them to expire and recalculate.

            if (expire_downstream)
            {
                foreach (var receiver in Component.Params.Output[1].Recipients)
                {
                    receiver.ExpireSolution(true);
                }
            }

(Note: I have defined ‘Component’ to be the same as writing ‘this’.)

This does actually work in principle. But, for reasons I can only guess, Grasshopper prohibits expiring components during the solution, at least in certain cases. The result is an error message, below, saying that “an object expired during a solution”. This message can be cancelled, and the component will then work as designed, but this still presents a poor user experience.

Grasshopper object expired during solution error message

What you SHOULD do

The solution is to bring out the logic from the ‘solveInstance’ method. The best way to do this is to be slightly more sophisticated in our ExpireDownstreamObjects override.

One way to do this is to add a conditional to the ‘base’.

        protected override void ExpireDownStreamObjects()
        {
            if (_run || _running)
            {
                base.ExpireDownStreamObjects();
            }
        }

The above method now only allows downstream objects to be expired if the class variables _run or _running are set to true. If they’re both false, nothing will happen. Grasshopper is now much happier – no error messages, and objects are being expired as we designed them to be.

Another option is, instead of using base, we can manually expire any component we want here:

        protected override void ExpireDownStreamObjects()
        {
            if (_run || _running)
            {
                this.Params.Output[1].Recipients[0].ExpireSolution(false);
            }
        }

The above code will now only expire the first connected component to the second output, and only if either _run or _running are true.

Grasshopper component controlling expired status

So in the image above, if BatchRun is not set to _run or _running, then pressing the button will not expire the panel. This is because the component has been repressed from transmitting an ‘expire’ command to the panel. Consequently, the doCalcs component will not recalculate – which was the intended behaviour.

You should be implementing Esc behaviour in your Grasshopper development

All Grasshopper users know the pain that comes with accidentally setting off an enormous calculation, and realising we’ve done something wrong and need to cancel it.

Since Grasshopper is tied on to the Rhino process in Windows, and the whole thing is single threaded, it is very difficult programatically to allow users to cancel a calculation part-way through. Part of the reason is that the UI and the calculations share that same thread. When heavy calculations are running, there are basically no cycles spare to handle a ‘cancel’ button.

Rutten has implemented a workaround, however. By button-mashing the Esc key during calculations, some components can listen for this key and issue an ‘abort request’. This system isn’t perfect, but it has on numerous occasions saved me from needing me to completely kill and restart Rhino.

This functionality doesn’t happen automatically. The Esc key check only happens when specified within your code, so if you never check if the Esc key is down, it is impossible for the user to abort the solution whilst Grasshopper is churning through your component. We need to implement this functionality manually.

For many lightweight components, this isn’t critical. But if your component takes a long time to calculate, it’s more likely that the user will want to abort the solution while Grasshopper is calculating yours – since Grasshopper processes each component during calculation one at a time.

How to implement Esc behaviour

Grasshopper includes some useful methods to provide the behaviour we need. How it works is that, every once in a while, we check if the escape key is down as part of our calculations. If it is down, the user wants to cancel calculations. So we submit a cancel request to Grasshopper. It is then up to Grasshopper to act upon this request.

For a compiled component, we can implement the code within SolveInstance(), or within another calculation-heavy part of your code.

The following code is in C#:

                    if (GH_Document.IsEscapeKeyDown())
                    {
                        GH_Document GHDocument = OnPingDocument();
                        GHDocument.RequestAbortSolution();
                    }

A good place to put this code is within a loop within your component. Don’t call IsEscapeKeyDown() too often though, as it requires processing power to handle this method, which will slow down your component.

Since the GH_Document is likely constant for the life of the component instance, you could pull this out and calculate it in the constructor* at the top of your SolveInstance() – since it’s a useful variable elsewhere anyway.

References

http://www.grasshopper3d.com/forum/topics/acknowledging-abort-request
And for a rather interesting comment thread… http://www.grasshopper3d.com/forum/topics/emergency-top-for-solution-calculation

Append menu items to Grasshopper components with C#

How to add items to the right-click menu in Grasshopper.

This example shows how we can add a text item to the right-click menu, and change a field within the class of our Grasshopper component.

Custom menu item in right click menu in Grasshopper

The code to do this is written for compiled C# components written in an environment such as Visual Studio. Add these methods to your component class.

        protected override void AppendAdditionalComponentMenuItems(System.Windows.Forms.ToolStripDropDown menu)
        {
            base.AppendAdditionalComponentMenuItems(menu);
            Menu_AppendItem(menu, "Flip myBool", Menu_DoClick);
        }

        private void Menu_DoClick(object sender, EventArgs e)
        {
            myBool = !myBool;
        }
        public bool myBool = false; 

This creates the menu item called ‘Flip myBool’ when we right-click the component. When we click on the menu item, it calls the Menu_DoClick method. We can define any method (with any name) that we want here.

References

http://www.grasshopper3d.com/forum/topics/set-input-parameter-from-a-pop-up-menu-vb

Remove the text below Grasshopper components

The text that appears below some components in Grasshopper – called messages – often adds some useful information about that component.

Grasshopper message text bounding box component

But sometimes – especially if you want to create a nice, simplified image of your Grasshopper component – you want to remove these messages.

There appears to be no easy way to remove these messages in the Grasshopper user interface, but with a bit of C# we can hack together our own solution.

Using a similar idea to the way that we can expire any component that we want, we can also manually control the properties of any component on the canvas.

C# code

Create a new C# component in Grasshopper, and create a boolean input called no_message. Then paste the following code:

  private void RunScript(bool no_message)
  {

    //for each component on the canvas
    foreach (IGH_DocumentObject obj in GrasshopperDocument.Objects)
    {
      try {
        if(no_message)
        {
          Grasshopper.Kernel.GH_Component thisobj = obj as Grasshopper.Kernel.GH_Component;
          thisobj.Message = "";
        }
      }
      catch{}
    }
  }

The trick is that we have to cast each object that we find on the canvas to a GH_Component. This exposes the Message property. Then, simply setting the Message to blank text is enough to make the message go away.

We use a try-catch because some components do not inherit from GH_Component, and so fail when we try to modify the message. (An alternative method would be to only try to modify the message if the cast object != null.)

And the result? Perfect.

Grasshopper message text bounding box component