Grasshopper within compute.rhino3d

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

Hi @sergey May I ask how to load Grasshopper file using compute_rhino3d from the cloud server rather than from the localhost? I was following Junichiro Horikawa’s tutorial which was implemented by using a localhost, for which I haven’t deployed successfully yet.

1 Like

Comment out the line compute_rhino3d.Util.url = ... and it will default to “https://compute.rhino3d.com/”, which is our test server. You’ll need to provide your auth token on the line below (compute_rhino3d.Util.authToken = "TOKEN") which you can get by going to Rhino Accounts.

Thanks, @will. Following your advice, I got <Response [401]> for the request when trying to read a ghx file. It seems to mean that my authentication is not valid while connecting to the server, although I’m using a newly requested auth token.

May I ask if there could be other source for this error? E.g., Could it be that those decode and encode procedures might have changed since Horikawa posted his tutorial?

The code duplicated from Horikawa’s is shown below:

import requests
import base64
import json
import os
import rhino3dm
import compute_rhino3d.Util
import compute_rhino3d.Brep

compute_rhino3d.Util.authToken = "TOKEN"

# get url to post call of GH from cloud
post_url = compute_rhino3d.Util.url + "grasshopper"

cwd_full_path = os.path.dirname(os.path.realpath(__file__))
ghx_file_name = 'voronoi.ghx'
ghx_full_path = os.path.join(cwd_full_path, ghx_file_name)

gh_data = open(ghx_full_path, mode="r", encoding="utf-8-sig").read()
data_bytes = gh_data.encode("utf-8")
encoded = base64.b64encode(data_bytes)
decoded = encoded.decode("utf-8")

response = requests.post(post_url, json={
    "algo": decoded,
    "pointer": None,
    "values": [
        {
            "ParamName": "RH_IN:Size",
            "InnerTree": { "{ 0; }": [ {"type": "System.Double",
                                                 "data": "100.0" }]}},
      {
            "ParamName": "RH_IN:Count",
            "InnerTree": { "{ 0; }": [{"type": "System.Integer",
                                                "data": "80"}]}},
     {
            "ParamName": "RH_IN:Outer",
            "InnerTree": { "{ 0; }": [ { "type": "System.Double",
                                                  "data": "40.0"}]}},
    {
            "ParamName": "RH_IN:Inner",
            "InnerTree": { "{ 0; }": [{ "type": "System.Double",
                                                 "data": "20.0" }]}}
    ]})

print(response)

Ah, sorry. I missed the fact that this example uses the Python Requests library to make the request, and not the compute_rhino3d client. In this case, you’d need to do something like this…

data = {
    "algo": decoded,
    ...
}
headers = {
    "Authorization": "Bearer " + "TOKEN"
}
response = requests.post(post_url, data=data, headers=headers)

Alternatively, more recently (after this video tutorial was made) we’ve added support to the compute_rhino3d client for solving Grasshopper definitions remotely. See this sample…

Thanks, @will

I can get the step5_grasshopper.py example script to call GH remotely that you suggested to work.

However, I got a new response [500] error following your suggestion to add the headers. The script is shown below. Appreciate if you can kindly advise if I missed anything important.

import requests
import base64
import json
import os
import rhino3dm
import compute_rhino3d.Util
import compute_rhino3d.Brep

auth_token = "AAA"
compute_rhino3d.Util.authToken = auth_token

post_url = compute_rhino3d.Util.url + "grasshopper"

ghx_full_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'voronoi.ghx')

gh_data = open(ghx_full_path, mode="r", encoding="utf-8-sig").read()
data_bytes = gh_data.encode("utf-8")
encoded = base64.b64encode(data_bytes)
decoded = encoded.decode("utf-8")

data = {
    "algo": decoded,
    "pointer": None,
    "values": [
        {
            "ParamName": "RH_IN:Size",
            "InnerTree": {
                "{ 0; }": [
                    {
                        "type": "System.Double",
                        "data": "100.0"
                    }
                ]
            }
        },
{
            "ParamName": "RH_IN:Count",
            "InnerTree": {
                "{ 0; }": [
                    {
                        "type": "System.Integer",
                        "data": "80"
                    }
                ]
            }
        },
{
            "ParamName": "RH_IN:Outer",
            "InnerTree": {
                "{ 0; }": [
                    {
                        "type": "System.Double",
                        "data": "40.0"
                    }
                ]
            }
        },
{
            "ParamName": "RH_IN:Inner",
            "InnerTree": {
                "{ 0; }": [
                    {
                        "type": "System.Double",
                        "data": "20.0"
                    }
                ]
            }
        }
    ]
}

headers = {
    "Authorization" : "Bearer " + auth_token 
}

response = requests.post(post_url, data=data, headers=headers)
print(response, '\n')

Change data=data to json=data in the penultimate line.

response = requests.post(post_url, json=data, headers=headers)

That was a typo in my last post… Sorry!

Again, I’d recommend using compute_rhino3d.Grasshopper.