Simple Parameters (VB) |
In this article we'll discuss parameters; what they are, what they're for, what they can and cannot do. Parameters are a vital part of Grasshopper (the other being components). However, unlike components, it is far less likely that you'll need to make your own parameters. Most components require only the native data types available inside Grasshopper. In those odd cases where you need to work with custom data, you'll also need to create custom parameters that store that data. In this topic, we'll create a parameter which can handle the TriStateType we discussed in the Simple Data Types topic.
Parameters are responsible for storing and distributing data. Components use them to collect existing data and output new data. Parameters can also be used to convert data from one type into another, even though on the atomic level the conversion is actually performed by the CastToT and CastFrom methods on IGH_Goo. Most objects in the Special subcategory on the Grasshopper toolbar are basically parameters with extended GUIs. Parameters can also be used by themselves to store constant data or to redirect data into multiple streams.
All parameters in Grasshopper must implement the IGH_Param interface. IGH_Param defines the bare minimum of what a parameter must be able to do. There are some interfaces that extend on IGH_Param, and also some abstract classes that partially implement the interface. Note that IGH_Param already inherits from IGH_ActiveObject, so it comes with a lot of baggage.
IGH_Param is quite an extensive interface, it defines nearly thirty properties and methods, some of which are quite tricky to implement. It is highly recommended that you do not directly implement IGH_Param, but derive from the abstract GH_ParamT class instead. GH_Param(Of T) provides a basic implementation of IGH_Param and takes care of quite a lot of the nasty bits. Here's a list of things GH_Param(Of T) will happily do for you that you would otherwise have to do yourself:
In other words, do not implement IGH_Param but derive from GH_Param(Of T)!
As mentioned before, parameters can be encountered as component inputs or outputs, but also as free-floating objects. These are not different classes, but rather the same class behaving in different ways. Every instance of IGH_Param has a Kind readonly property which describes the context of that instance:
In the image above, we see all three valid parameter Kinds (Input, Output and Floating). The "bool" object is a floating parameter of type Param_Boolean. The "P" on the left side of the "Cull" component is an input parameter of that very same type.
The Text Panel is also a floating parameter which derives from GH_Param(Of GH_String). In a way it is very similar to the String parameter (not shown), except it overrides the display and GUI of the default GH_Param class. The "L" on the right of the "Cull" component is an output parameter of the Param_GenericObject type (just like the "L" input parameter).
From this image we can see how versatile parameters can be. Parameters can have wildly different front-ends, they can expose and hide input and output grips at will, they can be part of a larger component or stand by themselves. They can even override so much of the default functionality that they no longer appear to be parameters at all (like the cluster output attached to "L").
All parameters have the capacity to store data, this is, after all, their primary function. When you derive from GH_ParamT there will be a protected member available within the class called m_data of type GH_StructureT. Since GH_Structure(Of T) only accepts types of T that implement IGH_Goo, GH_Param(Of T) also only accepts types for T that implement IGH_Goo. This is why you cannot have a GH_Param(Of Integer) but must instead have GH_Param(Of GH_Integer).
Data stored inside the m_data field is destroyed whenever the parameter expires. Parameters can be expired via several different mechanisms, but the most common ones are:
When a parameter is not connected to any source parameters, the persistent data will be copied into the volatile data. If there is no persistent data, the parameter will remain empty and a runtime warning will be included to this effect.
GH_Param(Of T) however does not support persistent data. You can add your own mechanism for this (like Text Panel does) or you can choose to derive from more advanced classes like GH_PersistentParamT. In this example, we'll derive from GH_PersistentParam(Of T) as we want to give users the ability to specify local data. GH_PersistentParam(Of T) requires we implement two additional methods that allow users to specify persistent data. These methods are called from the default popup menu when the "Set One XXXX" and "Set Multiple XXXX" items are clicked.
So let's start with deriving from GH_PersistentParam(Of T) and see where that takes us:
Public Class TriStateParameter Inherits GH_PersistentParam(Of TriStateType) 'We need to supply a constructor without arguments that calls the base class constructor. Public Sub New() 'This is where we specify the name, description, tab and panel of this parameter. MyBase.New("TriState", "Tri", "Represents a collection of TriState values", "Params", "Primitive") End Sub Public Overrides ReadOnly Property ComponentGuid() As System.Guid Get 'Always generate a new Guid, but never change it once you've released this parameter to the public. Return New Guid("{2FEEF1A2-A764-431d-8C78-9BF92C78BAE1}") End Get End Property Protected Overrides Function Prompt_Singular(ByRef value As TriStateType) As Kernel.GH_GetterResult 'Todo, impement this function. End Function Protected Overrides Function Prompt_Plural(ByRef values As System.Collections.Generic.List(Of TriStateType)) As Kernel.GH_GetterResult 'Todo, impement this function. End Function End Class
Protected Overrides Function Prompt_Singular(ByRef value As TriStateType) As Kernel.GH_GetterResult Dim go As New Rhino.Input.Custom.GetOption() go.SetCommandPrompt("TriState value") go.AcceptNothing(True) go.AddOption("True") go.AddOption("False") go.AddOption("Unknown") Select Case go.Get() Case Rhino.Input.GetResult.Option If (go.Option().EnglishName = "True") Then value = New TriStateType(1) If (go.Option().EnglishName = "False") Then value = New TriStateType(0) If (go.Option().EnglishName = "Unknown") Then value = New TriStateType(-1) Return GH_GetterResult.success Case Rhino.Input.GetResult.Nothing Return GH_GetterResult.accept Case Else Return GH_GetterResult.cancel End Select End Function Protected Overrides Function Prompt_Plural(ByRef values As List(Of TriStateType)) As Kernel.GH_GetterResult values = New List(Of TriStateType) Do Dim val As TriStateType = Nothing Select Case Prompt_Singular(val) Case GH_GetterResult.success values.Add(val) Case GH_GetterResult.accept Return GH_GetterResult.success Case GH_GetterResult.cancel values = Nothing Return GH_GetterResult.cancel End Select Loop End Function