Get points from polyline in RhinoCommon/Grasshopper C# component

Polylines are highly lightweight forms of curves that have many useful applications in Rhino and Grasshopper.

What is a polyline?

A polyline is a list of points, nothing more. When Rhino reads in a polyline, it creates the curve by doing a dot-to-dot of the points.

It can also be thought of as an interpolation of points with degree = 1. The curve will always pass through the inputted points.

polyline

Extracting the points from a polyline

Let’s say you have a polyline as input in Grasshopper. You want to extract the list of points used to create that polyline within a C# script.

The most intuitive method is to look for a property of the polyline called ‘Points’ or something like that. But if we try and find it…

polyline_grasshopper_code

…there’s nothing there! So if there’s no property containing the points, where is this data?

Method 1: Get the whole list of points

As shown in the RhinoCommon SDK documentation, PolyLine inherits from the Point3dList, which itself inherits from the RhinoList class.

We can use the ToList() method to get the list of points:

  private void RunScript(Polyline pline, object y, ref object A)
  {
    var pts = new List<Point3d>();

    pts = pline.ToList();

    A = pts;
  }

Method 2: Get a single point

We can extract a single point from the ToList() method:

  private void RunScript(Polyline pline, object y, ref object A)
  {
    var pt = new Point3d();

    pt = pline.ToList()[0];

    A = pt;
  }

Or, even shorter, we can treat the polyline like a list itself and access the points directly:

  private void RunScript(Polyline pline, object y, ref object A)
  {
    var pt = new Point3d();

    pt = pline[0];

    A = pt;
  }

Thanks to Fraser Greenroyd for our joint effort in unpicking this deceptively unintuitive challenge – he also wrote about it here.

Instantiate a Value List Grasshopper component with C#

In a previous post I instantiated a slider automatically using only C#. This means it is possible for a component to add components to your canvas automatically, potentially saving a lot of time in many repetitive workflows.

Here is a similar piece of code, but for the Value List:

C# code

  private void RunScript(bool x, List<object> y, ref object A)
  {
    if(x)
    {
      //instantiate  new value list
      var vallist = new Grasshopper.Kernel.Special.GH_ValueList();
      vallist.CreateAttributes();

      //customise value list position
      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);

      //populate value list with our own data
      vallist.ListItems.Clear();
      var item1 = new Grasshopper.Kernel.Special.GH_ValueListItem("Red", "r");
      var item2 = new Grasshopper.Kernel.Special.GH_ValueListItem("Green", "g");
      var item3 = new Grasshopper.Kernel.Special.GH_ValueListItem("Yellow", "y");
      vallist.ListItems.Add(item1);
      vallist.ListItems.Add(item2);
      vallist.ListItems.Add(item3);

      //Until now, the slider is a hypothetical object.
      // This command makes it 'real' and adds it to the canvas.
      GrasshopperDocument.AddObject(vallist, false);

      //Connect the new slider to this component
      this.Component.Params.Input[1].AddSource(vallist);
    }
  }

This code is written for the C# component in Grasshopper. If you are writing components within Visual Studio, there is a little extra work to do in defining GrasshopperDocument and Component, otherwise it’s pretty much a copy-and-paste job too.

GrasshopperDocument and Component variables in Grasshopper from C# component to Visual Studio

In the C# component for Grasshopper, it is easy to perform functions on either itself or on the document you are in. For example:

Component.Params.Input[0].SourceCount;

…will give you the number of things connected to the first input of the particular C# component containing that line of code.

This is made possible by some variables that are pre-defined within all C# components. Start a new C# component and scroll to lines 49-61 and you should see:

#region Members
  /// <summary>Gets the current Rhino document.</summary>
  private readonly RhinoDoc RhinoDocument;
  /// <summary>Gets the Grasshopper document that owns this script.</summary>
  private readonly GH_Document GrasshopperDocument;
  /// <summary>Gets the Grasshopper script component that owns this script.</summary>
  private readonly IGH_Component Component;
  /// <summary>
  /// Gets the current iteration count. The first call to RunScript() is associated with Iteration==0.
  /// Any subsequent call within the same solution will increment the Iteration count.
  /// </summary>
  private readonly int Iteration;
#endregion

This is all well and good if you want to use the C# component, but what if you’re developing something a bit more meaty, and want to move over to Visual Studio?

I often use the GrasshopperDocument and Component properties in particular, and wanted to be able to use these in VS.

Can’t I just call GrasshopperDocument and Component in Visual Studio?

Within the C# component, some magic goes on that gives these variables their abilities. So copy and paste of the above variables isn’t going to work.

Instead, what we need to do is create our own variables with the same names as above, and manually give them their functionality.

How to set up GrasshopperDocument and Component

Like pretty much everything on this blog, I write about stuff as I learn it, and there may be a better way to do things! But the method below works for me.

Open or start a new Grasshopper component project as usual. (See here for setting up.)

At the very top of your class, create some class variables:

GH_Document GrasshopperDocument;
IGH_Component Component;

Then, within Solve_Instance, we can give meaning to these variables:

Component = this;
GrasshopperDocument = this.OnPingDocument();

Or alternatively,

GrasshopperDocument = Instances.ActiveCanvas.Document;

So, your whole component class should look like:

    public class GHtestComponent : GH_Component
    {
        GH_Document GrasshopperDocument;
        IGH_Component Component;

        public GHtestComponent()
            : base("GH test", "Nickname",
                "Description",
                "Category", "Subcategory")
        {
        }

        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
        }

        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
        }

        protected override void SolveInstance(IGH_DataAccess DA)
        {
            Component = this;
            GrasshopperDocument = this.OnPingDocument();
        }

        protected override System.Drawing.Bitmap Icon
        { //..
        }

        public override Guid ComponentGuid
        { //..
        }
    }

Notes

According to this GH forum post, it is possible for the OnPingDocument() to return null. I haven’t explored further on when this might happen, but you may wish to wrap this line in an ‘if’ or a ‘try catch’.

It seems unnecessary to me to have to assign values to the variables in the Solve_Instance, but this is a way I have working. I couldn’t get it to work when I put it in the constructor.

I still haven’t found the equivalent line for RhinoDoc, mostly because I haven’t needed to. When the time comes for me to figure it out (or if you happen to know yourself and would like to share it with the world!) I’ll update this page.

Update: RhinoDoc

Accessing the RhinoDoc variable is actually really easy – at the top of your C# file, just add a ‘using’:

using Rhino;

Then you can use the RhinoDoc variable like you would use it in the C# component. Simple!

Map a value from one number scale to another – formula and C# code

Let’s say you have a number scale that has a minimum and a maximum. You have a second number scale with a min and a max. How do you map an arbitrary value somewhere on that number scale to the second scale?

A number scale, map a value from one scale to anotherLet’s call the top scale a with a0 = 10 and a1 = 40. The bottom scale goes from b0 = -1 to b1 = 1. We want to map the value a = 20 to some value of b. The equation is:

b=b_{0}+(b_{1}-b_{0})\displaystyle\frac{a-a_{0}}{a_{1}-a_{0}}

 

If you are normalising a value to between 0 and 1, then b0 = 0 and b1 = 1. The equation reduces nicely to:

b=\displaystyle\frac{a-a_{0}}{a_{1}-a_{0}}

 

C# code example

public double MapValue(double a0, double a1, double b0, double b1, double a)
{
	return b0 + (b1 - b0) * ((a-a0)/(a1-a0));
}

Instantiate new Grasshopper objects automatically with C#

The code here is doing two things:

  • Creating a new slider automatically and positioning it on the canvas
  • Connecting this slider to the original component

This is possible with any Grasshopper object, assuming you can find the class necessary to instantiate the component you are interested in.

  private void RunScript(bool x, List<object> y, ref object A)
  {

    Random rnd = new Random();

    if(x)
    {
      //instantiate  new slider
      Grasshopper.Kernel.Special.GH_NumberSlider slid = new Grasshopper.Kernel.Special.GH_NumberSlider();
      slid.CreateAttributes(); //sets up default values, and makes sure your slider doesn't crash rhino
      
      //customise slider (position, ranges etc)
      int inputcount = this.Component.Params.Input[1].SourceCount;
      slid.Attributes.Pivot = new PointF((float) this.Component.Attributes.DocObject.Attributes.Bounds.Left - slid.Attributes.Bounds.Width - 30, (float) this.Component.Params.Input[1].Attributes.Bounds.Y + inputcount * 30);
      slid.Slider.Maximum = 10;
      slid.Slider.Minimum = 0;
      slid.Slider.DecimalPlaces = 2;
      slid.SetSliderValue((decimal) (rnd.Next(1000) * 0.01));
      
      //Until now, the slider is a hypothetical object.
      // This command makes it 'real' and adds it to the canvas.
      GrasshopperDocument.AddObject(slid, false);

      //Connect the new slider to this component
      this.Component.Params.Input[1].AddSource(slid);
    }

  }

Thanks to Nicolas Chaulet who uncovered a lot of the code above.

Angle class for C#

A class which handles angles both as radians and degrees, with automatic conversions between each.

Using the class is easy.

Example usage:

Angle myAngle = new Angle(90, Angle.Type.Degrees); //new angle of 90 degrees
double myAngleAsRadians = myAngle.Radians; //returns 1.57

Angle myAngle2 = new Angle(Math.PI, Angle.Type.Radians); //new angle of PI radians
double myAngle2AsDegrees = myAngle.Degrees; //returns 180

Angle sum = myAngle + myAngle2; //returns a new angle of 1.5*pi rad

bool isSmaller = myAngle < myAngle2; //returns true

Angle class source code

Just add a new class file to your project and copy the text below. You may wish to change the namespace.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace YourNamespace
{
    /// <summary>
    /// Holds an angle as both radians and degrees, with conversions between each
    /// </summary>
    public class Angle
    {
        #region constructors
        /// <summary>
        /// Blank constructor. Remember to manually set some data to the properties!
        /// </summary>
        public Angle()
        {

        }
        /// <summary>
        /// Construct an angle with either a degree or a radian value
        /// </summary>
        /// <param name="input">Angle value</param>
        /// <param name="angleType">Specify whether input is in radians or degrees</param>
        public Angle(double input, Type angleType = Angle.Type.Radians)
        {
            if (angleType == Type.Degrees)
            {
                Degrees = input;
            }
            else if (angleType == Type.Radians)
            {
                Radians = input;
            }
        }
        /// <summary>
        /// Quickly construct a common angle
        /// </summary>
        /// <param name="preset">Choose angle</param>
        public Angle(Angle.Preset preset)
        {
            if (preset == Angle.Preset.Deg0) { Degrees = 0; }
            else if (preset == Preset.Deg180) { Degrees = 180; }
            else if (preset == Preset.Deg360) { Degrees = 360; }
            else if (preset == Preset.Rad2Pi) { Radians = twoPi; }
            else if (preset == Preset.RadPi) { Radians = Pi; }
            else Radians = 0;
        }
        #endregion constructors


        #region properties
        private double degrees;

        /// <summary>
        /// Angle in degrees
        /// </summary>
        public double Degrees
        {
            get { return degrees; }
            set
            {
                degrees = value;
                radians = ToRadians(value);
                updateFixedangles();
            }
        }

        private double radians;
        /// <summary>
        /// Angle in radians between -pi to pi
        /// </summary>
        public double Radians
        {
            get { return radians; }
            set
            {
                radians = value;
                degrees = ToDegrees(value);
                updateFixedangles();
            }
        }

        private double radians2pi;
        /// <summary>
        /// Angle in radians, modified to fall between 0 and 2pi. Read-only.
        /// </summary>
        public double Radians2pi
        {
            get { return radians2pi; }
        }

        private double degrees360;
        /// <summary>
        /// Value in degrees between 0 and 360. Read-only. 
        /// </summary>
        public double Degrees360
        {
            get { return degrees360; }
        }


        #endregion properties


        #region enums
        public enum Type { Radians, Degrees }
        /// <summary>
        /// Presets for quick setup of Angle constructor
        /// </summary>
        public enum Preset { Deg0, Deg180, Deg360, RadPi, Rad2Pi }
        #endregion enums


        #region constants
        private const double twoPi = 2 * Math.PI;
        private const double Pi = Math.PI;
        #endregion constants


        #region methods
        /// <summary>
        /// Convert an input angle of degrees to radians. Does not affect any properties in this class - set properties instead.
        /// </summary>
        /// <param name="val">Input in degrees</param>
        /// <returns>Value in radians</returns>
        public static double ToRadians(double val)
        {
            return val / (180 / Pi);
        }

        /// <summary>
        /// Convert an input angle of radians into degrees. Does not affect any properties in this class - set properties instead.
        /// </summary>
        /// <param name="val">Input in radians</param>
        /// <returns>Value in degrees</returns>
        public static double ToDegrees(double val)
        {
            return val * (180 / Pi);
        }

        /// <summary>
        /// Fixes an angle to between 0 and 360 or 2pi.
        /// </summary>
        /// <param name="val">Input angle</param>
        /// <param name="type">Specify whether the input angle is radians or degrees</param>
        /// <returns>The angle, fixed to between 0 and 360 or 0 and 2pi</returns>
        public static double FixAngle(double val, Type type)
        {
            if (type == Type.Radians)
            {
                //-2pi to 0 to between 0 and 2pi
                if (val < 0)
                {
                    return 2 * Math.PI - (Math.Abs(val) % (2 * Math.PI));
                }
                //over 2pi to between 0 and 2pi
                else if (val > 2 * Math.PI)
                {
                    return val % (2 * Math.PI);
                }
                //else it's fine, return it back
                else
                {
                    return val;
                }
            }
            else if (type == Type.Degrees)
            {
                //-360 to 0 to between 0 and 360
                if (val < 0)
                {
                    return 360 - (Math.Abs(val) % 360);
                }
                //over 360 to between 0 and 360
                else if (val > 360)
                {
                    return val % 360;
                }
                //else it's fine, return it back
                else
                {
                    return val;
                }
            }
            else return -1; //something's gone wrong
        }

        /// <summary>
        /// Looks at the radians and degrees properties, and updates their respective fixed angles
        /// </summary>
        private void updateFixedangles()
        {
            radians2pi = FixAngle(radians, Type.Radians);
            degrees360 = FixAngle(degrees, Type.Degrees);
        }

        /// <summary>
        /// Copies Radians2pi to Radians, and Degrees360 to Degrees. (I.e. fixes radians to 0<2pi and degrees to 0<360)
        /// </summary>
        public void FixAngles()
        {
            updateFixedangles();
            Radians = Radians2pi; //this also calls Degrees
        }

        #endregion methods


        #region operators

        /// <summary>
        /// Returns the sum of two angles
        /// </summary>
        /// <param name="a1">First angle</param>
        /// <param name="a2">Second angle</param>
        /// <returns>An angle constructed from the radian sum of the input angles</returns>
        public static Angle operator +(Angle a1, Angle a2)
        {
            return new Angle(a1.Radians + a2.Radians, Type.Radians);
        }

        /// <summary>
        /// Returns the difference between two angles
        /// </summary>
        /// <param name="a1">First angle</param>
        /// <param name="a2">Second angle</param>
        /// <returns>An angle constructed from the value that is the first angle minus the second angle</returns>
        public static Angle operator -(Angle a1, Angle a2)
        {
            return new Angle(a1.Radians - a2.Radians, Type.Radians);
        }

        /// <summary>
        /// Returns the exact division between two angles (i.e. how many times does the second angle fit into the first)
        /// </summary>
        /// <param name="a1">Numerator angle</param>
        /// <param name="a2">Dedominator angle</param>
        /// <returns>A new angle constructed from the value that is the first angle in radians divided by the second angle in radians</returns>
        public static Angle operator /(Angle a1, Angle a2)
        {
            return new Angle(a1.Radians / a2.Radians, Type.Radians);
        }

        public static Angle operator *(Angle a, double d)
        {
            return new Angle(a.Radians * d, Type.Radians);
        }

        public static Angle operator *(double d, Angle a)
        {
            return new Angle(a.Radians * d, Type.Radians);
        }

        public static Angle operator /(Angle a, double d)
        {
            return new Angle(a.Radians / d, Type.Radians);
        }

        public static bool operator <(Angle a1, Angle a2)
        {
            if (a1.Radians < a2.Radians) return true;
            else return false;
        }

        public static bool operator >(Angle a1, Angle a2)
        {
            if (a1.Radians > a2.Radians) return true;
            else return false;
        }

        public static bool operator <=(Angle a1, Angle a2)
        {
            if (a1.Radians <= a2.Radians) return true;
            else return false;
        }

        public static bool operator >=(Angle a1, Angle a2)
        {
            if (a1.Radians >= a2.Radians) return true;
            else return false;
        }

        public static implicit operator double(Angle angleobj)
        {
            return angleobj.Radians;
        }

        public static implicit operator string(Angle a)
        {
            return a.ToString();
        }

        #endregion operators


        #region overrides

        public override string ToString()
        {
            return Radians.ToString() + " radians";
        }

        #endregion overrides


    }

}

Convert from HSL to RGB colour codes in C#

A function to create a new colour in C# from HSL values:

class ColorScale
    {
        public static Color ColorFromHSL(double h, double s, double l)
        {
            double r = 0, g = 0, b = 0;
            if (l != 0)
            {
                if (s == 0)
                    r = g = b = l;
                else
                {
                    double temp2;
                    if (l < 0.5)
                        temp2 = l * (1.0 + s);
                    else
                        temp2 = l + s - (l * s);

                    double temp1 = 2.0 * l - temp2;

                    r = GetColorComponent(temp1, temp2, h + 1.0 / 3.0);
                    g = GetColorComponent(temp1, temp2, h);
                    b = GetColorComponent(temp1, temp2, h - 1.0 / 3.0);
                }
            }
            return Color.FromArgb((int)(255 * r), (int)(255 * g), (int)(255 * b));

        }

        private static double GetColorComponent(double temp1, double temp2, double temp3)
        {
            if (temp3 < 0.0)
                temp3 += 1.0;
            else if (temp3 > 1.0)
                temp3 -= 1.0;

            if (temp3 < 1.0 / 6.0)
                return temp1 + (temp2 - temp1) * 6.0 * temp3;
            else if (temp3 < 0.5)
                return temp2;
            else if (temp3 < 2.0 / 3.0)
                return temp1 + ((temp2 - temp1) * ((2.0 / 3.0) - temp3) * 6.0);
            else
                return temp1;
        }
    }

Thanks to David Greenwood for showing me this, and the unknown/unnamed elves who initially created it and saved it on our office server.

Create a list of Rhino geometry within a Grasshopper component using GeometryBase

How to create a generic, mixed list of geometry within a Grasshopper component, and send it to a single output:

class GeometryBuilder
{
    //constructor
    public GeometryBuilder()
    {
        //...
    }

    //property - declare our geometry list here
    private List<Rhino.Geometry.GeometryBase> geom = new List<Rhino.Geometry.GeometryBase>();
    public List<Rhino.Geometry.GeometryBase> Geom
    { get { return geom; } set { geom = value; }}

    //methods
    public BuildGeometry()
    {
        //add a line
        //lines don't inherit from GeometryBase, so we can't cast it to be in
        // our Geom list, but we can convert it to a NurbsCurve, which does.
        Rhino.Geometry.Line ln = new Line(new Point3d(0,0,0), new Point3d(1,1,0));
        Geom.Add((GeometryBase)(ln.ToNurbsCurve())); 

        //Meshes DO inherit from GeometryBase, so we can add them to Geom directly
        Rhino.Geometry.Mesh msh = someOtherMesh;
        Geom.Add((GeometryBase)msh);

        //Generally, things within Rhino.Geometry that are classes can be cast to GeometryBase.
        //Things which are structs can not, though there is usually a sensible way to convert them.

        //Point3d is a struct, so we can't cast it to GeometryBase.
        //The Rhino Point is a class, and it contains a constructor converting from Point3d
        Rhino.Geometry.Point3d pt3d = new Point3d(5,2,0);
        Rhino.Geometry.Point pt = new Point(pt3d);
        Geom.Add((GeometryBase)pt);
    }
}



class GrasshopperComponent : GH_Component
{
    //constructors etc...
    //...

    //set inputs
    //...

    //set outputs
    protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
    {
        //our output is created using the 'AddGeometryParameter' method. Remember to set 'access' to 'list'.
        pManager.AddGeometryParameter("My Geometry","my geom","This is a list of generic geometry", GH_ParamAccess.list);
    }

    protected override void SolveInstance(IGH_DataAccess DA)
    {
        //generate our list of geometry from the class we made
        var GeometryBuilder gb = new GeometryBuilder();
        gb.BuildGeometry();

        //the geometry is now saved within the Geom property of our gb instance of GeometryBuilder.
        //All we need to do now is send the geometry to the Grasshopper output
        DA.SetDataList(0, gb.Geom);
    }
}

Multithreading a foreach loop in a Grasshopper component in C#

Grasshopper itself is a single-threaded process. That is, even if you have a very complicated calculation going on, and you have a powerful computer with many cores, it persists in using a single core. This is very frustrating when you end up waiting a long time for calculations to complete.

However, it doesn’t have to be the case that each component is single-threaded. Nearly all components are single-threaded, but when you create your own compiled component, there’s nothing to stop you from defying convention and making some lightning-fast multi-threaded components. And, as I have just found out, it’s very easy to set up.

While the code is simple, knowing when it is okay to use multi-threaded logic is a bit more challenging. Multi-threading works best when you have a single repetitive task. Rather than multithreading the entire component, you select bits of your code that are taking a long time to calculate, and set those up for multithreading.

Say you have a foreach loop. You might be looping through a list of 100000 points in a model, checking if they are intersecting a shape. You then build a list of true/false that describes if each point is intersecting that shape. In regular single-threaded programming, your loop would go through each point one at a time, doing the intersection result and saving the result to your list.

This is a prime candidate for multithreading. The task is repetitive, and very importantly, each loop is independent, i.e. any one loop’s action does not depend on the result of any other loop.

With multi-threading, instead of running one at a time, we can divide the task up for each core on your computer. For example, if you have 2 cores, one core does half of the loops and the other does the other half.

.NET has some very useful classes/methods that really simplify setting this up. There are many of these for different tasks, but here we will focus on one in particular. Note that, as far as I know, these parallelisation tricks only work for compiled components (such as those created in Visual Studio) and won’t work in the c# script component within Grasshopper.

How to convert a ‘foreach’ loop into a parallel foreach loop

Let’s say you have the following foreach loop, which sends an output of results to Grasshopper:

var results = new List<bool>(new bool[pts.Count]);
foreach (Point3d pt in pts)
{
    bool isIntersecting = CheckIntersection(pt, shape);
    results.Add(isIntersecting);
}

DA.SetDataList(0, results);

This code uses a method I wrote, CheckIntersection, to see if a list of points are interacting with an input shape.

We set up a list to take our results, we cycle through our points, do a calculation on each, and then save the result. When finished, we send the result as an output back to Grasshopper. Simple enough.

The best thing to do is I show you how to parallelise this, and then explain what’s happening. Here’s the parallel version of the above code:

var results = new System.Collections.Concurrent.ConcurrentDictionary<Point3d, bool>(Environment.ProcessorCount, pts.Count);
foreach (Point3d pt in pts) results[pt] = false; //initialise dictionary

Parallel.ForEach(pt, pts=>
{
    bool isIntersecting = CheckIntersection(pt, shape);
    results[pt] = isIntersecting; //save your output here
}
);

DA.SetDataList(0, results.Values); //send back to GH output

Going from ‘foreach’ to ‘Parallel.ForEach’

The Parallel.ForEach method is what is doing the bulk of the work for us in parallelising our foreach loop. Rather than waiting for each loop to finish as in single threaded, the Parallel.ForEach hands out loops to each core to each of them as they become free. It is very simple – we don’t have to worry about dividing tasks between the processors, as it handles all that for us.

Aside from the slightly different notation, the information we provide is the same when we set up the loop. We are reading from collection ‘pts’, and the current item we are reading within ‘pts’ we will call ‘pt’.

The concurrent dictionary

One real problem though with multithreading the same task is that sometimes two or more processes can attempt to overwrite the same piece of data. This creates data errors, which may or may not be silent. This is why we don’t use a List for the multithreading example – List provides no protection from multiple processes attempting to write into itself.

Instead, we use the ConcurrentDictionary. The ConcurrentDictionary is a member of the Concurrent namespace, which provides a range of classes that allow us to hold and process data without having to worry about concurrent access problems. The ConcurrentDictionary works just about the same as regular Dictionaries, but for our purposes we can use it much like a list.

It is a little more complicated to set up, as you need to specify keys for each value in the dictionary, as well as the value itself. (I have used dictionaries once before here.) Rather than having a collection of items in an order that we can look up with the index (the first item is 0, the second item is 1 and so on) in a dictionary we look up items by referring to a specific item. This specific item is called the ‘key’. Here, I am using the points as the key, and then saving their associated value (the results we are trying to calculate) as a bool.

How much faster is parallel computation?

Basic maths will tell you that you will that your code should increase at a rate of how many cores you have. With my 4-core machine, I might expect a 60 second single-threaded loop to complete in about 15 seconds.

However, there are extra overheads in parallel processing. Dividing up the work, waiting for processes to finish towards the end, and working with the concurrent dictionary, all take up processor power that would otherwise be used in computing the problem itself.

In reality, you will still likely get a substantial improvement compared to single-threaded performance. With 4 cores, I am finding I am getting a performance improvement of around 2.5 to 3.5 times. So a task that would normally take 60 seconds is now taking around 20 seconds.

Two Grasshopper components showing processing time difference between single-threaded and multi-threaded computation

References

Thanks to David Greenwood for getting me started with multithreading. Front image source under CC licence.

Check whether points are inside a polyline curve in Grasshopper with C#

Let’s say you have a polyline in Grasshopper. You also have some points, some of them inside the curve and some of them outside. You want to sort the points depending on whether they are inside or outside the curve.

This component allows you to do that. Input your polyline and your points to check. The ‘A’ list will contain the points inside the curve, and the ‘B’ list will contain the points outside the curve.

inside-curve-component

This will then produce the following output:

A check in Grasshopper to test whether points are inside a polyline curve

Download component

The following component is a user object that allows the input of a single polyline.



If you are working with multiple polylines, and want to check that points are inside or outside every polyline at once, have a look at this file instead.

Source code

If you prefer, you can use the source code directly from below. It is formatted for the C# component in Grasshopper, though of course it can be easily generalised.

private void RunScript(List<Point3d> pts, Polyline crv, ref object A, ref object B)
  {

    var inside = new List<Point3d>();
    var outside = new List<Point3d>();

    foreach (Point3d pt in pts)
    {
      if(IsInside(pt, crv))
      {
        inside.Add(pt);
      }
      else outside.Add(pt);
    }
    A = inside;
    B = outside;

  }

  // <Custom additional code> 

  bool IsInside (Point3d pt, Polyline crv)
  {
    Point3d pt1, pt2;
    bool oddNodes = false;

    for (int i = 0; i < crv.SegmentCount; i++) //for each contour line
    {

      pt1 = crv.SegmentAt(i).From; //get start and end pt
      pt2 = crv.SegmentAt(i).To;

      if ( (pt1[1] < pt[1] && pt2[1] >= pt[1] || pt2[1] < pt[1] && pt1[1] >= pt[1]) && (pt1[0] <= pt[0] || pt2[0] <= pt[0]) ) //if pt is between pts in y, and either of pts is before pt in x
        oddNodes ^= ( pt2[0] + (pt[1] - pt2[1]) * (pt1[0] - pt2[0]) / (pt1[1] - pt2[1]) < pt[0] ); //^= is xor
      //end.X + (pt-end).Y   * (start-end).X  /(start-end).Y   <   pt.X
    }


    if (!oddNodes)
    {
      double minDist = 1e10;
      for (int i = 0; i < crv.SegmentCount; i++) {
        Point3d cp = crv.SegmentAt(i).ClosestPoint(pt, true);
        //Point3d cp = mvContour[i].closestPoint(pt);
        //minDist = min(minDist, cp.distance(pt));
        minDist = Math.Min(minDist, cp.DistanceTo(pt));
      }
      if (minDist < 1e-10)
        return true;
    }

    if(oddNodes) return true;

    return false;
  }