Grasshopper

algorithmic modeling for Rhino

Creating a Component with a variable number of output parameters

This topic is a follow-up from a discussion started by Benjamin Golder. In it I will show the necessary steps for creating a custom component that has a variable number of output parameters, based on the data structure of the input.

I'll create a component that aims to write all the GH_Paths inside the input data structure into separate output parameters. I'll add a menu item to the component that allows users to synch the number of outputs with the current data.

Note that there are some bugs I found related to Undo here, but I'll attempt to fix those asap. The mechanisms employed in this example are correct.

Let's start with the Component class definition and the constructor:

Public Class GH_ExampleComponent_VarOutput
  Inherits GH_Component

  Public Sub New()
    MyBase.New("Extract Paths", "ExPath", "Extract all the paths from a tree", "Sets", "Tree")
  End Sub

End Class

Now, the RegisterXXXXParams methods:

Protected Overrides Sub RegisterInputParams(ByVal pManager As GH_Component.GH_InputParamManager)
  pManager.Register_GenericParam("Tree", "T", "Data tree to examine", GH_ParamAccess.tree)
End Sub
Protected Overrides Sub RegisterOutputParams(ByVal pManager As GH_Component.GH_OutputParamManager)
  'We'll add one output parameter, just to not have a jagged output.
  pManager.Register_PathParam("Path 1", "1", "1st path in tree")
End Sub

SolveInstance() is somewhat special, but not very complicated:

Protected Overrides Sub SolveInstance(ByVal DA As IGH_DataAccess)
  'We have only one input parameter and it is set to Tree, 
  'so SolveInstance will only be called once for every solution.

  'We don't actually need the data inside the input, we're only interested in the paths.
  'So we don't actually need to call DA.GetDataTree, we can just go in and extract the 
  'paths directly:
  Dim paths As IList(Of GH_Path) = Params.Input(0).VolatileData.Paths

  'Abort if there is no tree.
  If (paths.Count = 0) Then Return

  'Post a warning if the number of output parameters does not 
  'equal the number of paths in the tree.
  If (paths.Count < Params.Output.Count) Then
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "There are more outputs than paths in the tree.")
  ElseIf (paths.Count > Params.Output.Count) Then
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "There are fewer outputs than paths in the tree.")
  End If

  'Iterate over all paths and assign to output parameters.
  For i As Int32 = 0 To Math.Min(Params.Output.Count, paths.Count) - 1
DA.SetData(i, paths(i))
  Next
End Sub

Adding a menu item to the component menu is relatively straightforward, however handling the menu command requires a fair bit of logic:

Protected Overrides Sub Menu_AppendCustomComponentItems(ByVal iMenu As System.Windows.Forms.ToolStripDropDown)
  'Add a single item to the component menu.
  Menu_AppendGenericMenuItem(iMenu, "Synch outputs", AddressOf Menu_SynchOutputClicked)
End Sub
Private Sub Menu_SynchOutputClicked(ByVal sender As Object, ByVal e As EventArgs)
  'Here we have to synch the number of output parameters with the number 
  'of paths in the volatile data tree in the input parameter.
  'This requires a few steps:
  '1. Determine whether something needs to happen at all.
  '2. Record an undo event.
  '3. Remove excess outputs or add missing outputs.

  Dim paths As IList(Of GH_Path) = Params.Input(0).VolatileData.Paths
  If (paths.Count = Params.Output.Count) Then Return 'yay, nothing needs to be done.

  'Something needs to be done, record an undo state.
  RecordUndoEvent("Synch output")

  'We either have too few or too many outputs, determine which is the case.
  If (paths.Count > Params.Output.Count) Then
'Add the missing outputs
For i As Int32 = Params.Output.Count + 1 To paths.Count
 Dim param As New Grasshopper.Kernel.Parameters.Param_GenericObject()
 param.Name = "Path " & i.ToString()
 param.NickName = i.ToString()

 If (i.ToString.EndsWith("1")) Then
param.Description = i.ToString() & "st path in tree"
 ElseIf (i.ToString.EndsWith("2")) Then
param.Description = i.ToString() & "nd path in tree"
 ElseIf (i.ToString.EndsWith("3")) Then
param.Description = i.ToString() & "rd path in tree"
 Else
param.Description = i.ToString() & "th path in tree"
 End If

 Params.RegisterOutputParam(param)
Next

  Else
'Remove excessive outputs
Do
 If (Params.Output.Count <= paths.Count) Then Exit Do
 Dim param As IGH_Param = Params.Output(Params.Output.Count - 1)
 Params.UnregisterOutputParameter(param)
Loop
  End If

  Params.OnParametersChanged()
  ExpireSolution(True)
End Sub

Finally, we must make sure that the component properly (de)serializes. This means we have to override the Write and Read methods and add additional information to the GHX archive:

Public Overrides Function Write(ByVal writer As GH_IO.Serialization.GH_IWriter) As Boolean
  'We must make sure that the number of output parameters is correctly stored.
  'We'll use a special function on the GH_ComponentParamServer to accompish this
  'without too much sweat.
  Params.WriteParameterTypeData(writer)

  Return MyBase.Write(writer)
End Function
Public Overrides Function Read(ByVal reader As GH_IO.Serialization.GH_IReader) As Boolean
  'Very important, we must make sure all parameters exist before we 
  'start with the main deserialization.
  Params.Clear()
  Params.ReadParameterTypeData(reader)

  Return MyBase.Read(reader)
End Function

I attached a VB file that contains the code outlined above.

--
David Rutten
david@mcneel.com
Seattle, WA

Views: 4286

Attachments:

Replies to This Discussion

Awesome!!!
David this is such a thorough, useful, and clear explanation. Thank you so much!

Hi,My dear david! I use Rhion5.0,and the file didn't work. I have change some code. Can you help me? Please!

Attachments:

Hi Huaxia,

there are two problems with the code you posted:

Protected Sub Menu_AppendCustomComponentItems(ByVal iMenu As ToolStripDropDown)

should be

Protected Overrides Sub AppendAdditionalComponentMenuItems(menu As ToolStripDropDown)

I think the function you used may have been discontinued which is why the original code failed to compile. However just removing the Overrides keyword doesn't fix the problem, it just makes the code compileable. In your code the function was never called.

The second problem is to do with serialization. I recommend a different approach these days, see attached. Instead of overriding the Write() and Read() methods, implement IGH_VariableParameterComponent and implement the interface with blank functions, then the (de)serialization will work for free.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Attachments:

OH,Thank you very much! I want learn more about how to programm the gha file. But the SDK help seems to little for me. Can you give me some free tutorials to learn?  If so,I won't trouble you so much!

There is a document for the Grasshopper SDK (downloadable via the Grasshopper Help menu), but it doesn't really explain stuff much, mostly it just talks about what a function does rather than which function you should be using to accomplish X.

If you have questions it's ok to post them here. Just make sure they are properly spelled and tagged so it's easy for others to find the answers if the same question comes up again.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

OK, but you will be much toilsome. How old are you david?

2years + 00000111 days

 

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Wa,You must be a respected old man!Very glad to know you!

Whoa, it was your birthday last week? Gefeliciteerd!

Thank you. I was on the road but we managed to have a party of sorts.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

How do you know that from the number?

Well, someone once said, "there are 10 types of people in this world: those who understand binary, and those who don't".

Though I prefer to quote my friend's modified version of it, "there are 10 types of coffee machines in this world: those that make coffee, and those that run on it". 

RSS

About

Translate

Search

© 2024   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service