Grasshopper

algorithmic modeling for Rhino

I'm working on a RhinoCommon-based plugin for a client that is intended to automate processing of a Grasshopper project. I know next to nothing about Rhino or Grasshopper, but have been pouring through all the documentation and code samples I can find.  I'm running the latest build of Rhino 5, 64-bit, with the latest Grasshopper plugin installed.

To get the Grasshopper plugin object, I call the following:

            dynamic gh = RhinoApp.GetPlugInObject("Grasshopper");

My code then uses the plugin object instance to open a Grasshopper project file, bind data to parameters, run the solver and bake the resulting data. No problem (well, no problem after I spent a couple of hours trying to guess the method names and parameters exposed by Grasshopper plugin object....)

I'm a software engineer and despise the lack of strongly-typed variables at design time so I figured there must be a better way...

At runtime, I see that the dynamic plugin object is actually an instance of Grasshopper.Plugin.GH_RhinoScriptInterface. So I added a reference to the Grashopper assembly to my plugin project and tried doing a cast of the dynamic gh variable to a GH_RhinoScriptInterface instance like this:

Grasshopper.Plugin.GH_RhinoScriptInterface ghInterface = gh as Grasshopper.Plugin.GH_RhinoScriptInterface;

However, after adding just this one single line of code without changing anything else (i.e., my code still references the dynamic gh variable everywhere), I get an error about a bunch of "Unrecognized Objects" when I call gh.OpenDocument():

No idea what I might be doing wrong, but this sure seems like something that should work correctly.

Views: 1556

Replies to This Discussion

Hi Marty,

calling into Grasshopper out of the blue will mean none of the component caches are created. Creating these caches takes a long time (usually the Grasshopper banner is displayed during this interval) which is why it only happens on command. 

My guess is that loading Grasshopper via this reference somehow short-circuits the caching routines and therefore Grasshopper is unaware of any components which is why even standard ones like [Curve Frames] and [End Points] will fail to be loaded.

Can you post some code I can use to duplicate this problem?

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Btw. the GH_RhinoScriptInterface class has been documented and is part of the Grasshopper SDK documentation. Did you download the docs? You can do so via the Grasshopper Help menu.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

here's a snippet - create a new plugin command and use this as the RunCommand() method. (Sorry about the formatting...)

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
dynamic gh = RhinoApp.GetPlugInObject("Grasshopper");

// use the plugin object interface to open a file, assign some data and run the solver
// you'll get an "Unrecognized Objects" error message when this line executes if you've uncommented the cast above
if (gh.OpenDocument(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"Grasshopper\Foo.gh")))
{

gh.AssignDataToParameter("_SomeParameter", 15);
gh.RunSolver(true);

gh.BakeDataInObject("Xyz");

doc.Views.Redraw();
}

// Cast the dynamic gh instance to a GH_RhinoScriptInterface instance so we can use strong-typing at design time.
//
// uncomment the next line and you will get an error on the gh.OpenDocument() call above, before the next line even executes!

//Grasshopper.Plugin.GH_RhinoScriptInterface ghInterface = gh as Grasshopper.Plugin.GH_RhinoScriptInterface;

// Ideally, the following code should work exactly like the code above, if not for the error
//
//if (ghInterface.OpenDocument(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"Grasshopper\Foo.gh")))
//{
// ghInterface.AssignDataToParameter("_SomeParameter", 15);
// ghInterface.RunSolver(true);
//
// ghInterface.BakeDataInObject("Xyz");
//
// doc.Views.Redraw();
//}

return Result.Success;
}

As a quick followup, I think you may be right that it's an initialization issue with Grasshopper.

If I execute "Grasshopper" first and let it fully start up, then go run my plugin command that performs the code steps above, everything works great.

Is there a way to know whether Grasshopper is done initializing?

Grasshopper.Instances.IsComponentServer will tell you whether all components have been loaded. You can also call Grasshopper.Instances.ComponentServer to get an instance of the server. This method will cache all components if they haven't been already.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

No joy. I tried several variations on getting the ComponentServer, waiting for IscomponenetServer to become true, getting the Grasshopper PluginObject from Rhino, etc.

The only way I can get the code that uses the GH_RhinoScriptInterface cast to work correctly is to ensure that I've already started Grasshopper via the command line. So apparently there is some difference in initialization between getting the PluginObject() from Rhino and actually running the "_Grasshopper" command.

The _Grasshopper command does the following things:

  1. It tests whether the license has been accepted and if not, displays the license.
  2. It displays the Grasshopper Editor window (this in turn causes a lot of other things to happen).
  3. It begins a threaded version check to see if a newer version is available for download.

1 and 3 are clearly not needed for you to get it to work, so all the important stuff must happen in step #2. When showing the Grasshopper main window, the following things happen:

  1. Grasshopper.Instances.ComponentServer method is called and if it returns null then the whole thing aborts. This is typically when you see the Grasshopper banner and the loading progress bar.
  2. If the Instances.DocumentEditor field is empty, it will create a new instance of the window and assign it to Instances.DocumentEditor.
  3. It will then make sure that if the window is visible, it is also within the bounds of the screen. This is useful for when a second monitor becomes unplugged.
  4. If the window was hidden, it will show it.

To answer your earlier question: "Is there a way to know whether Grasshopper is done initializing?" the answer is yes. Grasshopper initialized on the main UI thread so unless your code is running in a thread you created, you will not be able to do anything while I initialize. You call Instances.ComponentServer and by the time the function returns initialization is over and done with.

 

"waiting for IscomponenetServer to become true" This is not useful. Either IsComponentServer is false, in which case you need to call Instances.ComponentServer to make it true, or it is already true in which case calling Instances.ComponentServer just returns the already existing server. So you might as well call Instances.ComponentServer directly and cut out the middle man.

 

However, if you plan to open files, you will also need the main editor window up and running or there will be no place to put these files.

 

So I think ultimately you're right in calling the _Grasshopper command, as it does both things that need to be done in order for you to open files and run solutions. However, you should be able to use LoadEditor() or ShowEditor() from the RhinoScriptInterface object as well, they do the same thing.

 

--

David Rutten

david@mcneel.com

Poprad, Slovakia

RSS

About

Translate

Search

Photos

  • Add Photos
  • View All

© 2025   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service