7/18/2018 - 6:06 AM

Grasshopper plugin Development with Visual Studio



0. Setup IDE

  1. Install Visual Studio 2017
  2. Open Visual Studio, install Grasshopper Assembly for v6 (Tools & Extensions -> Online -> Search for "Grasshopper Assembly v6", and RhinoCommon templates for v6 (templates -> Search for "Grasshopper Templates v6". Restart Visual Studio.

0. Workflow Setup

  1. Automatically build to gh component folder: In Project Explorer, click: Properties -> Build Events, then edit (or add a new) post-build event: Copy "$(TargetPath)" "C:\Users\Josh\AppData\Roaming\Grasshopper\Libraries\<Name of Plugin>.gha"

  2. OR Add build folder to rhino search path: After Rhino Launches enter "GrasshopperDeveloperSettings", then add your bin output folder of your project to Grasshopper’s search path.

  3. Under "GrasshopperDeveloperSettings", consider unchecking the Memory load *.GHA assemblies using COFF byte arrays checkbox during development (will allow you to hit debug breakpoints, etc.)

  4. (v6) After building, close open gh files, then type GrasshopperReloadAssemblies (v6) to update component definitions without having to restart rhino. (better yet bind a keyboard shortcut, e.g. Ctrl+R, to this cmd.)

  5. (v5) After building, close/open grasshopper by binding shortcut, e.g. Ctrl+R, to GrasshopperUnloadPlugin Grasshopper

1. Project Setup

  1. Open VS -> New Project -> Installed -> Templates -> Visual C# -> Rhinoceros -> Grasshopper Add-On.
  2. Fill out Grasshopper Assembly dialog box ("Nickname" is component Name, "Category" is the Tab, "Sub Category" is the Group under the tab), provide relevant paths, and click Finish.

2. First Build

  1. Build Component (F5)
  2. Open Grasshopper (or reload plugins) and test component!
  3. Exit Rhino to stop session.

Simple Componenent

1. Create Component class from GH_Component base class

using System;
using Grasshopper.Kernel;

namespace MyPlugin
    public class MyPluginComponent : GH_Component

2. provide public constructor without any arguments.

public MyPluginComponent() : base(
    "MyPlugin",   // name
    "ABC",        // component label/abbreviation
    "xxx",        // description
    "Custom",     // category (tab)
    "Simple") {}  // subcategory (group w/in tab)

3. Register inputs with pManager object inside overriden implementation of RegisterInputParams method.

protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
    // pManager.AddPlaneParameter(name, label, descr, access, default);
    pManager.AddPlaneParameter("Plane", "P", "Base plane for spiral", GH_ParamAccess.item, Plane.WorldXY);
    pManager.AddNumberParameter("Inner Radius", "R0", "Inner radius for spiral", GH_ParamAccess.item, 1.0);
    pManager.AddNumberParameter("Outer Radius", "R1", "Outer radius for spiral", GH_ParamAccess.item, 10.0);
    pManager.AddIntegerParameter("Turns", "T", "Number of turns between radii", GH_ParamAccess.item, 10);

    // change properties of certain parameters,
    pManager[0].Optional = true;

Note the access type parameter and optional default values.

4. Register outputs with pManager object inside overriden implementation of RegisterOutputParams method.

Output parameters do not have default values, but they too must have the correct access type.

protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
    // pManager.AddPlaneParameter(name, label, descr, access);
    pManager.AddCurveParameter("Spiral", "S", "Spiral curve", GH_ParamAccess.item);

    // hide a specific parameter from the Rhino preview.

7. Implement component logic inside overriden implementation of SolveInstance method, using DA object to access inputs/outputs.

protected override void SolveInstance(IGH_DataAccess DA)
    // Initialize variables
    Plane plane = Plane.WorldXY;
    double radius0 = 0.0;
    double radius1 = 0.0;
    int turns = 0;

    // Use the DA object to bind input params to variables
    if (!DA.GetData(0, ref plane)) return;
    if (!DA.GetData(1, ref radius0)) return;
    if (!DA.GetData(2, ref radius1)) return;
    if (!DA.GetData(3, ref turns)) return;

    // Validate the data
    if (radius0 < 0.0) return;
    if (radius1 <= radius0) return;
    if (turns <= 0) return;

    // Pass validated data to helper function
    Curve spiral = CreateSpiral(plane, radius0, radius1, turns);

    // use DA object to Bind outputs.
    DA.SetData(0, spiral);

8. Implement functionality inside helper function.

private Curve CreateSpiral(Plane plane, double r0, double r1, Int32 turns)
    // functionality goes here...

6. Specify unique guid for Component inside overridden implementation of ComponentGuid method

Generate new Guids using an Online Guid Generator or Microsofts popular guidgen.exe

public override Guid ComponentGuid
    get { return new Guid("c09fabd9-61d5-4b04-9dbd-99f4f6e7780d"); }

5. compile/test Component

9. Optionally Specify component exposure by implementing overriden Exposure method

There are seven possible locations (primary to septenary), each of which can be combined with the GH_Exposure.obscure flag, which ensures the component will only be visible on panel dropdowns.

public override GH_Exposure Exposure
    get { return GH_Exposure.primary; }

10. Optionally specify icon by implementing overriden Icon method

protected override System.Drawing.Bitmap Icon
        // add 24x24 px image files to your project resources and access them like this:
        return Resources.IconForThisComponent;

Grasshopper Resources


RhinoCommon SDK Grasshopper SDK


Grasshopper SDK C# examples

Links C# for Grasshopper


Long Nguyen, C# Scripting and Plugin Development for Grasshopper (2018) Designalyze, Intro to C# Scripting in Grasshopper


NOC Python Grasshopper john harding on vimeo

Plugins to check out

GH_CPython on github GH_CPython on food4rhino ANT biomorpher embryo


Mahmoud Ouf - author of GH_CPython/ANT John Harding - author of biomorpher/embryo Jingcheng Chen Long Nguyen - author of DynaShape (Dynamo Plugin) and run's grasshopper plugin development workshop.