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
.gha
file(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
.gha
file (most plugins are available on Food4Rhino). This example will use thePufferfish3-0.gha
. - Rename the
.gha
extension to.dll
. - Open Developer Powershell for VS 2022 and navigate to the directory where the
.dll
file 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
.dll
file 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 avoid
return 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 (replacingInt32
withString
) 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 (replacingInt32
withString
) exist too, but are rarely used. -
Find all
GetDataX<T>(Int32, Y<T>)
calls in the method.X
corresponds to the type of access each input parameter has:
X
is nothing: Item Access
X
isList
: List Access
X
isTree
: Tree AccessInt32
corresponds 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.Int32
corresponds 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
GetData
andSetData
methods 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_DataAccess
interface 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 (replacingInt32
withString
) exist too, but are rarely used.The
IGH_DataAccess
interface 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 (replacingInt32
withString
) exist too, but are rarely used.
In a
GetDataX<T>(Int32, Y<T>)
call:
X
corresponds to the type of access each input parameter has:
X
is nothing: Item Access
X
isList
: List Access
X
isTree
: Tree Access
Int32
corresponds 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.