algorithmic modeling for Rhino
While there is not in fact a 1:1 correspondence between components and methods in a simply accessible library, it is under certain circumstances possible to call the functions of a GH component from inside a script.
However, it should be noted that to do so introduces a significant amount of overhead, which may impact performance. This is because (to the best of my understanding) all the methods described below actually instantiate and execute a virtual Grasshopper document, with components and everything else. Whenever possible, it is advisable to simply call RhinoCommon functions - these are designed to be called in code and are more streamlined.
Grasshopper's Python is unique among the scripting languages in that it has a "node-in-code" mechanism for this purpose in the form of the ghpythonlib library and its "components" class. Here is some example code:
from ghpythonlib import components as ghcomp
a = ghcomp.Circle(Rhino.Geometry.Plane.WorldXY,25.0)
result = ghcomp.DeconstructBrep(b)
faces = result
edges = result
vertices = result
This code will call the "Circle" component with the world XY base plane and a radius of 25, and then call the "Deconstruct Brep" component on a brep (input to the script as "b").
The arguments passed to the function will correspond to the inputs of the component, and the function will return the output (the data itself in the case of a component with only one output, and a tuple of data in the case of multiple outputs, as in the second example above).
For more info on this technique, see this post by Steve Baer.
James Ramsden has described a method for doing this in these two posts on his blog:
His examples are in C#, but everything he describes can also be done in VB.net with some syntax tweaks.
The core of his method is to programmatically instantiate a component, populate its inputs, and then create a virtual grasshopper document in which to execute the code. He then harvests the outputs and converts them back to simple data. Here is his example code for calling the "Circle by Normal and Radius" component:
var cs = new CurveComponents.Component_CircleCNR();
//add the circle centre (input 0)
var pp = cs.Params.Input as Grasshopper.Kernel.GH_PersistentGeometryParam<Grasshopper.Kernel.Types.GH_Point>;
pp.PersistentData.Append(new GH_Point(new Point3d(0, 0, 3)));
//add the circle radius (input 2)
var pn = cs.Params.Input as Grasshopper.Kernel.GH_PersistentParam<Grasshopper.Kernel.Types.GH_Number>;
pn.PersistentData.Append(new GH_Number(y)); //y is another variable
//add to a dummy document so we can read outputs
var doc = new Grasshopper.Kernel.GH_Document();
//read output circle
A = cs.Params.Output.VolatileData.get_Branch(0);
//remove that component
For a great many of the simple components, there are in fact methods in RhinoCommon that accomplish exactly the same thing. Note the complexity of the above code, and then look at the equivalent code using RhinoCommon methods:
Circle circle = new Circle(new Plane(origin, normal), radius);
In my experience it is preferable to just call or construct the methods you need using RhinoCommon rather than relying on trying to call components from inside your code.
Lastly, It is my understanding that this concept is central to David's thinking around GH2 - so that it in the next version it will be significantly more streamlined to switch between components and code representations. (I have no special knowledge of GH2 development - this is just what I have seen David say on the forums, and as usual any statements about future features are subject to change.)
Hope this is helpful!
Would not it be easier to make a separate library to use components from script without the shell of gh_component? I'm doing that for Peacock. And I wish it to grasshopper because its components are quite generalist and robust, in some cases it is advisable done from RhinoCommon but in others we would save much much time...
I'd love to see what David has thought about it for GH2! :3
Thanks for the post Andrew, very interesting!
Well-written code separates the interface from the logic. If all GH devs (myself included!) remembered to do this, then accessing this logic would be relatively simple. But the way that GH works, and the way that the GH_Component class is set out, it encourages a 'scripting' style of programming where the core logic tends to end up directly in the SolveInstance method. And since this method ties into the Param Manager for getting data in and out of the SolveInstance, which itself seems to depend upon a GH_Document to work, it all starts getting rather messy rather quickly. So yes, it would be a lot easier to use a separate library, but odds are if you're trying to use someone else's component programatically, they likely haven't done this.
Thanks for sharing, Andrew. I have yet to actually find a practical application for this technique. As you say, most components can have their behaviours replicated with RhinoCommon methods. (Internally, this is what the components themselves are doing anyway - so instantiating the component around the method is just baggage.) I just thought it was cool that components can be programatically operated at all!
What would be amazing is if we could use the logic of Grasshopper components without needing an instance of Grasshopper running. Some of the components I've been writing recently have quite substantial environmental engineering calculations that would be useful outside of GH as well as in it. After all, a GHA file is just a DLL, and the logic can be found as methods within, like any other DLL. We've made some progress in decoupling the file from GH for this purpose. I'm going to try and dig out some code when I'm back in the office tomorrow.
Ok, two answers to the above:
1) If you are accessing a Grasshopper component written in the usual way (i.e. most of the logic is contained within the SolveInstance) then the methods talked about above are the closest we seem to be able to get to remotely accessing the component. It's ultimately dependend upon a GrasshopperDocument.
2) If you are creating your own components, you can plan in advance when writing your component to make it more easily accessed remotely later. My colleague Fraser wrote about it here: http://frasergreenroyd.com/how-should-you-be-coding-grasshopper-com...