How do I output nested IEnumerable objects when writing a GH plugin in C#

Hi guys:

I’m using C# to write a Grasshopper plugin, but I found that the DA.SetData command doesn’t seem to be able to pass out nested IEnumerable objects (like List<List>). I tried to write a simple test program. Because I really need to send such a data format to connect with GH now, so I hope someone can tell me how to send this data. Here’s a simple example of how to restore this process:

using System;
using System.Collections.Generic;

using Grasshopper.Kernel;
using Rhino.Geometry;

namespace Showcase
{
    public class ShowCase : GH_Component
    {
        /// <summary>
        /// Initializes a new instance of the ShowCase class.
        /// </summary>
        public ShowCase()
          : base("ShowCase", "Nickname",
              "Description",
              "Category", "Subcategory")
        {
        }

        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
            pManager.AddCurveParameter("Output", "Output", "test out put", GH_ParamAccess.list)
        }

        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            List<Point3d> pts1 = new List<Point3d>();
            for (int i = 0; i < 10; i++)
            {
                pts1.Add(new Point3d(0, 0, i));
            }

            List<Point3d> pts2 = new List<Point3d>();
            for (int i = 0; i < 10; i++)
            {
                pts2.Add(new Point3d(i, 0, 0));
            }

            List<List<Point3d>> output = new List<List<Point3d>> { pts1, pts2 };

            DA.SetDataList("Output", output);
        }

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon
        {
            get
            {
                //You can add image files to your project resources and access them like this:
                // return Resources.IconForThisComponent;
                return null;
            }
        }

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid
        {
            get { return new Guid("F6EE9E1F-806C-4574-8BB7-859021A3ECE2"); }
        }
    }
}

I know the line of code like this is wrong:

pManager.AddCurveParameter("Output", "Output", "test output put", GH_ParamAccess.list)

To pass a nested list, such code would be wrong, but I didn’t find a way to pass this kind of data from the API documentation. I hope to get your help :smiling_face_with_three_hearts:~

Nested enumerables should be output as tree structure.

1 Like

Thank you first. I have tried to save the lists in a data tree, but the output things are like this. These objects cannot be used directly in grasshopper:

image

To be honest, I’m not familiar with the data structure of GH, and I haven’t found a way to directly convert it to a tree. I used a very stupid loop to complete the conversion (the code is at the bottom). Did I overlook something, or did I not use the tree format correctly?

  private void RunScript(object x, object y, ref object A)
  {
    List<Point3d> pts1 = new List<Point3d>();
    for (int i = 0; i < 10; i++)
    {
      pts1.Add(new Point3d(0, 0, i));
    }

    List<Point3d> pts2 = new List<Point3d>();
    for (int i = 0; i < 10; i++)
    {
      pts2.Add(new Point3d(i, 0, 0));
    }
    List<List<Point3d>> output = new List<List<Point3d>> { pts1, pts2 };

    var dataTree = new DataTree<Point3d>();

    for (int i = 0; i < output.Count; i++)
    {
      for (int j = 0; j < output[i].Count; j++)
      {
        dataTree.Add(output[i][j], new GH_Path(i));
      }
    }
    
    A = output;
  }

The output at path i is a list of points. I think you’ve done the conversion correctly, possibly you could set the list at paths i directly instead of looping over the j index, but I’m not sure.
I hanen’t found a way to do this more elegantly, but maybe there is one.

I think I get the way, Dankon!

You can use the AddRange method, that takes an IEnumerable as input.

2 Likes

Thank you and this can be a solution in most cases, but what I wish is to output a plethora of lists and interact with other components that contain similar data-structures. So, Data Tree matches my goal better. :slight_smile: