Grasshopper within compute.rhino3d

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.

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 https://www.rhino3d.com/compute/login.

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.

Dear @will , it works following your suggestions:

  1. using json=data in the request, and
  2. changing post_url to compute_rhino3d.Util.url + “Grasshopper”, i.e. Grasshopper in capital.

Thank you very much. I almost cried seeing response[200]…and I regarded it as a gift of Chinese Lunar New Year from the Rhino3d developers…

The only issue I encountered was that the 3dm file generated can only be open in Rhino7WIP after I got this warning:

Screenshot 2020-01-26 at 11.57.56

Neverthelss, the script works.

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

# provide auth token
compute_rhino3d.Util.authToken = "MY_TOKEN"

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

this_file_dir = os.path.dirname(os.path.realpath(__file__))
ghx_file_name = 'voronoi.ghx'
ghx_full_path = os.path.join(this_file_dir, 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")

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, json=data, headers=headers)
print(response, '\n')

res = response.content.decode("utf-8")
res = json.loads(res)

values = res["values"]

model = rhino3dm.File3dm()

for val in values:
    paramName = val['ParamName']
    print(paramName)

    innerTree = val['InnerTree']

    for key, innerVals in innerTree.items():
        print(key)
        for innerVal in innerVals:
            data = json.loads(innerVal['data'])
            # print(data)
            geo  = rhino3dm.CommonObject.Decode(data)
            model.Objects.Add(geo)

model_full_path = os.path.join(this_file_dir, "voronoi_test3.3dm")
model.Write(model_full_path)
1 Like

Glad you got it working!

Try specifying the version number when you write the file, e.g. model.Write(model_full_path, 6). See the API reference for more information.

Yes, @will, as you suggested Rhino version needs to be specified.

Hi @Will,

any plans to port those helper libs to c#?

Hey @rgr, since the server is written in C# I think all the pieces already exist. Have a look at Sergey’s post (no. 12) above for some ideas. I’ve logged a request as #79 but it might be a few weeks before I can get to it myself. Pull Requests always welcome, of course :slight_smile:

Note that the type codes are no longer required and Compute now supports .gh files along with .ghx.

@enmerk4r, @fraguada – do either of you have a sample project that calls the /grasshopper endpoint from C#?

Hi @Will,

my main problem is connection to the server and getting the REST calls correct, which although it might be trivial for a programmer, is easier to understand in your “step5_grasshopper.py” sample where I dont have to care about how to send my data to the server.
The linked sample doesn’t show that and the resthopper github has no documentation/comments/samples on how to do that. Or am I missing something major?
Maybe rewriting that sample would be enough to get people started.

I’m also struggling to get this to work from C# - any advice would be appreciated.

ComputeServer.WebAddress = Config.WebAddress;
ComputeServer.AuthToken = Config.AuthToken;

string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string ghPath = Path.Combine(assemblyPath, "gh_test_files", "1_Simple_GH_Numeric_Output.ghx");

GrasshopperDataTree gdt = new GrasshopperDataTree("RH_IN:Number");
gdt.Append(new GrasshopperObject(3.0), new GrasshopperPath(0));

var inputs = new List<GrasshopperDataTree>() { gdt };

var result = Rhino.Compute.GrasshopperCompute.EvaluateDefinition(ghPath, inputs);

Definition looks like this:

Colleague has got the same script working from python so think it must be from my end.

Hey @hugh_groves, your sample is working fine for me both with Compute running locally and using our server (default). I’ve also tested .gh and .ghx versions of the same grasshopper definition. What behaviour and error messages are you seeing? Are you using your own compute server and if so what version of Rhino and Compute are you using (http://your-compute-server.com/version)?

Thanks for providing the code. I’ve expanded it into a full working sample, below, in case anyone else reads this. We’re lacking in samples for the new GrasshopperCompute functionality in the C# client right now…

using Rhino.Compute;
using System;
using System.Collections.Generic;
using System.Linq;

namespace compute_cs
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // example calling your own compute server
            ComputeServer.WebAddress = "http://localhost:8081/";

            // example calling our trial server (default)
            // requires token from https://www.rhino3d.com/compute/login
            // ComputeServer.AuthToken = "TOKEN_HERE";

            if (args.Length != 1)
            {
                Console.Error.WriteLine("usage: program_name.exe <grasshopper_file_path>");
                return;
            }

            var ghPath = args[0];

            GrasshopperDataTree gdt = new GrasshopperDataTree("RH_IN:Number");
            gdt.Append(new GrasshopperObject(3.0), new GrasshopperPath(0));

            var inputs = new List<GrasshopperDataTree>() { gdt };

            var result = Rhino.Compute.GrasshopperCompute.EvaluateDefinition(ghPath, inputs);

            foreach (var item in result)
            {
                Console.WriteLine(item.ParamName);
                foreach (var path in item.InnerTree.Keys)
                {
                    Console.WriteLine("{0} => [ {1} ]", path, string.Join(", ", item.InnerTree[path].Select(x => x.Data)));
                }
            }
        }
    }
}

squared.ghx (52.6 KB) squared.gh (7.4 KB)

Thanks @will - I tried your .ghx file with the mcneel server and the code worked. I then tried it against our instance and it failed. I tried my script (attached) against both and it failed against both. The only error message that I received was a 500 code.1_Simple_GH_Numeric_Output.ghx (42.2 KB) 1_Simple_GH_Numeric_Output.gh (6.5 KB)

When using the python client the same .gh file against our own server works - any idea whats going on?

@will I can now confirm that it was a problem with our instance, updating to match McNeel resolved the issue.

There was also a minor incompatibiltiy between our Grasshopper script and the C# code - I should have been passing an integer in rather than a double

1 Like

Hi! the link to these files don’t seem to work anymore. Can you possibly update these? Thank you

Can you point me to the exact links that don’t work, please?

of course:
https://github.com/mcneel/compute.rhino3d/blob/master/src/compute.geometry/IO/GHTypeCodes.cs

@dchang1a lot has changed since this project was initiated. What are you trying to do?