Grasshopper within compute.rhino3d

What’s the potential for incorporating grasshopper into compute? e.g. is there the plan for grasshopper ‘components’ to be incorporated into the API?

2 Likes

Hello, @dekanifisher !
It’s not only possible, but there is already a prototype! Have you heard of Resthopper? It was a project at the Thornton Tomasetti AEC Tech hackathon that McNeel kindly incorporated into their compute.rhino3d repo. @will just finished updating the public server at compute.rhino3d.com to reflect the latest changes. The route that you are looking for is compute.rhino3d.com/grasshopper. It accepts a POST request with three fields:

{
    "algo" : "Your bit-64 encoded string containing entire GHX file goes here",
    "pointer": "If algo is null, an optional public url to your GHX goes here",
    "values": [ A list of Resthopper data trees which serves as your input goes here]
}

So, essentially, you are passing your entire GHX file (either as a string in the POST body or a public URL) and a list of Resthopper data trees that mimicks Grasshopper data tree structure and contains serialized RhinoCommon geometry.

Here’s what the route looks like:

And this is the I\O necessary to talk to it:

Finally, there is a formatting standard that defines how the Grasshopper file has to be organized. Each Resthopper Tree has a ParamName field. This lets the compute server know which param in the definition each data tree from the “values” section belongs to. In the definition itself all you have to do is create a group around each input param and call it “RH_IN:<type_enum>:<unique_name>”. For instance, consider this definition:

Your input schema will include two trees: one named “RH_IN:108:Crv1” and another one named “RH_IN:108:Crv2”. The 108 enum simply lets the server know that you are passing a curve. The complete list of type enums is available here:

You will then receive back the same schema, however the “algo” and the “pointer” fields would be empty, and the values section will contain one DataTree of points with ParamName set to “RH_OUT:102:OutPt”. You can have any number of inputs / outputs as long as the names remain unique.

Let me know if you have any questions!
Hope this helps,
Best!
Sergey

8 Likes

wow thank you. very excited about this! Does resthopper work with the javascript compute api?
Also does resthopper allow for third party grasshopper components? (I suppose ghx files would have to store third party components for this to work?)

At the moment Resthopper doesn’t really have a client-side API. You would have to serialize and pass along C# objects with RhinoCommon geometry inside. But this is a very interesting thought - could we potentially accept a JavaScript version of the DataTree with 3dm.io objects inside… Probably not in the nearest future, but that is definitely something for us to keep on the radar!

With regards to the third-party plugins, that depends on the server-side setup. In order to run, Compute needs an installed version of Rhino 7 (WIP), so basically, whatever GH plugins are installed on that machine are the plugins that you can call from Resthopper. The GHX file doesn’t really contain any executable code - it is just an XML markup that tells Grasshopper which components are located where, what connects to what and which assemblies each component corresponds to. The assemblies themselves have to be present on the server. @will will have a better idea of what additional plugins (if any) are currently available on compute.rhino3d.com.

If you do want to use plugins that are not available on the public Compute server, you can always install Rhino WIP, then clone the repo and spin up your own server. Rhino 7 can be downloaded from https://www.rhino3d.com/download/rhino-for-windows/wip and will share a license with your Rhino 6 installation. If you have any questions about the setup process, I’d be happy to walk you through.

1 Like

There aren’t any third-party plug-ins on compute.rhino3d.com right now for either Rhino or Grasshopper. It’s something we’d like to support in the future though!

1 Like

Okay cool and how about having multiple instances running? If there is one installed version of Rhino 7 running on a server with one license does this mean that only one user can be accessing the Compute functionality at any given time?

We only run one instance of compute per VM, with a load balancer to distribute requests amongst the VMs. Mostly requests are quick enough that you don’t need to worry about requests timing out in the queue, but it is possible to lock up the machine if you feed it, say, a big mesh boolean operation to calculate.

Do you have an example of what the “list of Resthopper data trees which serves as your input” looks like in the POST request to compute.rhino3d.com/grasshopper? Thanks!

Hi @kylafarrell! Yes, certainly. The funny thing is that when I looked through the old POST requests in order to send you a sample, I’ve noticed that there is a serialization hiccup that we’ve missed haha. Everything still works, but we are inadvertently doubling up on the input data (which just shows how very alpha all of this is at the moment). We’ll try to fix this shortly. But here is your sample.

ResthopperSample.zip (12.1 KB)

So, again, the three main parts here are “algo”, “pointer” and “values”. Algo and Pointer are interchangeable. “algo” houses a bit64 encoded string representing the entirety of the GHX file. If “algo” is null, Resthopper will look at the “pointer” in search of a public URL that houses a GHX accessible via a GET request (say, on an s3 bucket). Then the “values” section contains the actual list of Resthopper DataTrees. Each DataTree has a “ParamName” field that corresponds to the name of the group the target input param lives inside (see above). The inner tree is the dictionary of Grasshopper Path as string to serialized Rhino3dmIo geometry. The “Keys” and “Values” fields are unnecessarily reiterated as I mentioned in the beginning. So here we’re sending a simple script with
a point at “RH_IN:102:0001” and a double at “RH_IN:105:0001”

You should get something like this back:

ResthopperSampleResponse.zip (1.4 KB)

Hope that helps!

2 Likes

Hi @enmerk4r thanks! This is very helpful and I think will send me in the right direction.

@enmerk4r A follow up question. I have been searching the forums but I cannot find an example of what utility or Rhino3dm function to use generate the dictionary of the grasshopper path and/ or serialize the Rhino3dm geometry. Ideally I would like to use the Rhino3dm Python library. Is this currently only supported in C#?

@kylafarrell Sure, if you wanted to construct a tree in C# you would do something like this. (I am writing this directly here, so there might be typos, haha - but this is the general gist).

To send a flat list of geometries:

// Create a Resthopper Data Tree and give it a param name
ResthopperDataTree myAwesomeTree = new ResthopperDataTree();
myAwesomeTree.ParamName = "RH_IN:108:0001";

// Create a Resthopper path
int pathInteger = 0;
GhPath path = new GhPath(pathInteger);

foreach(Polyline pline in myPolylineList)
{
    // Convert each geometry to Resthopper object
    // simply by feeding it to the constructor
    ResthopperObject rhObj = new ResthopperObject(pline);
    
    // Add to data tree at path
    myAwesomeTree.Append(rhObj, path);
}

// Add tree to schema
schema.Values.Add(myAwesomeTree);

To send a grafted tree:
In order to send more complex trees, you would have to increment the value of the path above. So, for instance you could do GhPath path = new GhPath(pathInteger++) inside the foreach loop in order to put each ResthopperObject on it’s own branch. You can create trees of any complexity by feeding an integer array to the GhPath constructor. So adding an object at any arbitrary path such as new GhPath(new int[] { 0, 0, 1, 3 }); is totally possible. You just have to figure out a way of incrementing the path that serves your business logic.

Do we have any helper functions?
No. But we should. We’ll try to look into adding some helper functions that can easily convert a nested list to a ready-to-go RH Data Tree at some point in the future. The only limitation is that in C# you can only have items in the leaves of the nested data structure, whereas data trees allow you to put objects anywhere in the nesting. For instance, you can’t have a new List<<List<string>>() { new List<string>(), "someRandomString"}, because C# doesn’t allow a List<string> and a string to coexist at the same level. But this is something you could do in a Data Tree. So, if you need to work with some edge case complicated tree structure, you’ll have to do it by hand regardless

Python?
Right now Resthopper is C# only with most of the development focused on getting everything to work smoothly on that front. A Python API would be cool and would definitely expand RH’s user base, but that’s not something we are considering in the nearest future.

Good luck! Let me know if you have any further questions.

2 Likes

Here are a few more RESTHopper samples:
(edited with latest versions and live examples)

-updated broken links…

8 Likes

Thanks a lot Luis :slight_smile:Abi

1 Like

@fraguada : I’ve tried to use rhino3dm and compute_rhino3d in Python to compute a Grasshopper model using Rhino Compute Service. When I try to execute a POST request to the REST API with the following json:
{
“algo”: decoded,
“pointer”: None,
“values”: [
{
“ParamName”: “RH_IN:Point”,
“InnerTree”: {
“{ 0; }”: [
{
“type”: “Rhino.Geometry.Point3d”,
“data”: “{“X”:0.0,“Y”:0.0,“Z”:0.0}”
}
]
},
“Keys”: [
“{ 0; }”
],
“Values”: [
[
{
“type”: “Rhino.Geometry.Point3d”,
“data”: “{“X”:0.0,“Y”:0.0,“Z”:0.0}”
}
]
]
},
{
“ParamName”: “RH_IN:Size”,
“InnerTree”: {
“{ 0; }”: [
{
“type”: “Rhino.Geometry.Point3d”,
“data”: “{“X”:0.0,“Y”:0.0,“Z”:0.0}”
}
]
},
“Keys”: [
“{ 0; }”
],
“Values”: [
[
{
“type”: “Rhino.Geometry.Point3d”,
“data”: “{“X”:0.0,“Y”:0.0,“Z”:0.0}”
}
]
]
}
]
}
I get the response code 500 (Internal server error).
I mention that I’ve been following the tutorial https://www.youtube.com/watch?v=GCB7duijXQQ and with the voronoi.ghx file provided here: https://github.com/jhorikawa/GrasshopperProgrammingTutorials/tree/master/0004%20Load%20Grasshopper%20file%20with%20compute.rhino3d%20Python and the corresponding JSON. The POST request ran properly, giving a 200 OK response. Could you tell me, please, if you think is it something wrong with my JSON. I mention also that this JSON is for a box model in Grasshopper.

Try the latest version of the compute-rhino3d package from PyPi. It has few helper classes/functions that mean you don’t have to construct the JSON yourself. Here’s a sample.

import compute_rhino3d.Util
import compute_rhino3d.Grasshopper as gh
import rhino3dm
import json

compute_rhino3d.Util.authToken = ADD_TOKEN_HERE

pt1 = rhino3dm.Point3d(0, 0, 0)
circle = rhino3dm.Circle(pt1, 5)
angle = 20

# convert circle to curve and stringify
curve = json.dumps(circle.ToNurbsCurve().Encode())

# create list of input trees
curve_tree = gh.DataTree("RH_IN:curve")
curve_tree.Append([0], [curve])
rotate_tree = gh.DataTree("RH_IN:rotate")
rotate_tree.Append([0], [angle])
trees = [curve_tree, rotate_tree]

output = gh.EvaluateDefinition('workshop_step5.ghx', trees)
print(output)
6 Likes

Thank you! I will try. :slight_smile:

Based on the discussion here I was able to load Grasshopper file in Unity so I would like to share the process which I gathered in a video. You can also download the project data.

8 Likes

Hi Will! Do you have the full python script for this one somewhere?

Also showing how you get the output lines?

Rick

Hey @r.h.a.titulaer, the full sample is on GitHub and I’ve updated it to extract the output from the grasshopper definition and write it to a 3dm file. It’s not pretty, but it works.

1 Like