Sample reimplementations of third-party Grasshopper .NET plugin components using Grasshopper's native script components.
The documentation for each component SampleComponent generally contains the decompiled output of SampleComponent SampleComponentDecompiled.cs, and the reimplementation for Grasshopper's C# Script Instance SampleComponent.cs.
The documentation's subsections cover atypical implementation issues, and are typically structured as such:
SampleComponentDecompiled.cs
<code sample from decompilation>SampleComponent.cs
<code sample from reimplementation>Details of reimplementation may be written here, comparing the differences between the two and the changes made that resulted in the reimplementation.
In general, disassembling a plugin involves the following steps:
- Decompile the relevant grasshopper assembly
.ghafile(s), which are effectively .NET DLLs. - In the class definition of the desired component, reimplement the
SolveInstance(IGH_DataAccess)method of the class in Grasshopper's native C# script component.
What follows is a general walkthrough of the above steps using Visual Studio 2022 and Pufferfish's BoundingRectangle component.
- Obtain a copy of the plugin
.ghafile (most plugins are available on Food4Rhino). This example will use thePufferfish3-0.gha. - Rename the
.ghaextension to.dll. - Open Developer Powershell for VS 2022 and navigate to the directory where the
.dllfile is located. - Enter
ildasm.exe <filename>.dll.
ildasm.exe Pufferfish3-0.dll
If successful, the IL DASM UI should appear with a tree of the plugin's definitions. - Identify the reference path of the desired component. In the case of Pufferfish's
BoundingRectangle, its reference isPufferfish.Components.Components_Curve._5_Curve.BoundingRectangle. - Open Visual Studio 2022 and create a new C# project (whether it is a Console App or otherwie does not matter).
- Add a project reference using the Solution Explorer. Add the
.dllfile as a reference. - In the project editor window, add an assembly reference using
using.
using Pufferfish; - Enter code that references the desired class.
Pufferfish.Components.Components_Curve._5_Curve.BoundingRectangle ... - Open the context menu for the class (
BoundingRectangle) and selectGo To Implementation. If successful, Visual Studio 2022 will open another editor window with a decompiled definition of of the class.
-
Find the
SolveInstance(IGH_DataAccess)method defined within the class (with avoidreturn type). This is the method that is called when Grasshopper executes a component.The IGH_DataAccess interface provides access to three main methods for getting component input data:
IGH_DataAccess::GetData<T>(Int32, T)
IGH_DataAccess::GetDataList<T>(Int32, List<T>)
IGH_DataAccess::GetDataTree<T>(Int32, GH_Structure<T>)
Note that methods for parameter access by name (replacingInt32withString) exist too, but are rarely used.The IGH_DataAccess interface provides access to four main methods for setting component output data:
IGH_DataAccess::SetData(Int32, Object)
IGH_DataAccess::SetDataList<T>(Int32, IEnumerable)
IGH_DataAccess::SetDataTree<T>(Int32, IGH_DataTree)
IGH_DataAccess::SetDataTree<T>(Int32, IGH_Structure)
Note that methods for parameter access by name (replacingInt32withString) exist too, but are rarely used. -
Find all
GetDataX<T>(Int32, Y<T>)calls in the method.Xcorresponds to the type of access each input parameter has:
Xis nothing: Item Access
XisList: List Access
XisTree: Tree AccessInt32corresponds to the index of the input parameter. InBoundingRectangle:
0: G
1: Pl
2: A
3: S
4: U
-
Find all
SetDataX(Int32, R)calls in the method.Int32corresponds to the index of the output parameter. InBoundingRectangle:
0: B
1: X
2: Y
-
Edit the input and output parameters of Rhino's C# Script Component to match each parameter referenced by the
GetDataandSetDatamethods respectively ofSolveInstance(IGH_DataAccess). -
Reimplement the class's
SolveInstance(IGH_DataAccess)method within theRunScript(...)method of the Script Component.
The following section contains details for reimplementation that are generally applicable. For component-specific issues, refer to the subfolder containing the relevant component.
SampleComponentDecompiled.cs
protected override void SolveInstance(IGH_DataAccess DA)
{
T t = default(T);
DA.GetData<T>(0, t);
List<U> list = new List<U>();
DA.GetDataList<U>(1, list);
GH_Structure<V> val = default(GH_Structure<V>);
DA.GetDataTree<V>(2, val);
...
DA.SetData(0, (object)val2);
DA.SetDataList(1, (IEnumerable)list2);
DA.SetDataTree(2, (IGH_Structure)val3);
}SampleComponent.cs
private void RunScript(T t, List<U> us, DataTree<V> vs, ref object A, ref object B, ref object C)
{
List<U> list = us;
DataTree<V> val = vs;
...
A = a;
B = b;
C = c;
}The SolveInstance(IGH_DataAccess) method is called when Grasshopper executes a component.
The
IGH_DataAccessinterface provides access to three main methods for getting component input data:
GetData<T>(Int32, T)
GetDataList<T>(Int32, List<T>)
GetDataTree<T>(Int32, GH_Structure<T>)
Note that methods for parameter access by name (replacingInt32withString) exist too, but are rarely used.The
IGH_DataAccessinterface provides access to four main methods for setting component output data:
SetData(Int32, Object)
SetDataList<T>(Int32, IEnumerable)
SetDataTree<T>(Int32, IGH_DataTree)
SetDataTree<T>(Int32, IGH_Structure)
Note that methods for parameter access by name (replacingInt32withString) exist too, but are rarely used.
In a
GetDataX<T>(Int32, Y<T>)call:
Xcorresponds to the type of access each input parameter has:
Xis nothing: Item Access
XisList: List Access
XisTree: Tree Access
Int32corresponds to the index of the input parameter. In the aboveSampleComponent:
0: A
1: B
2: C
The RunScript() method is called when Grasshopper executes its C# Script Component. The method's arguments can be set by editing the input and output parameters from the Grasshopper GUI.
In the reimplementation of SampleComponent above, the following parameters were set:
- An item input of type hint
T - A list input of type hint
U - A tree input of type hint
V - An output
A* - An output
B* - An output
C*
* Note that the access type (item/list/tree) and data type cannot be set for the output. The output type is always object.
Each input and output corresponds to the inputs and outputs of the original SampleComponent.
| Data Access | Access Type | Decompilation | Reimplementation |
|---|---|---|---|
| Input | Item | T t = default(T);DA.GetData<T>(0, t); |
- |
| Input | List | List<U> list = new List<U>();DA.GetDataList<U>(1, list); |
List<U> list = us; |
| Input | Tree | GH_Structure<V> val = default(GH_Structure<V>);DA.GetDataTree<V>(2, val); |
DataTree<V> val = vs; |
| Output | Item | DA.SetData(0, (object)val2); |
A = a; |
| Output | List | DA.SetDataList(1, (IEnumerable)list2); |
B = b; |
| Output | Tree | DA.SetDataTree(2, (IGH_Structure)val3); |
C = c; |
The item input t can be referenced directly in the reimplementation and thus no code is needed.
The list input us only needs to be assigned to a List<U> with the same variable name list as the decompilation.
Tree inputs in Grasshopper scripting components are represented with the DataTree class, a watered-down version of GH_Structure. For most cases the methods called on GH_Structure in a decompilation will work on DataTree too. If not, a new GH_Structure containing the items in DataTree needs to be constructed.