Skip to main content

Tutorial - Integrate Rhino/Grasshopper

note

Estimated time: 45 minutes
Difficulty level: Intermediate


Not a reader? feel free to follow this tutorial as a video

Introduction

Welcome to this tutorial on integrating Grasshopper with VIKTOR! In this tutorial, you will learn how to create a VIKTOR web app with a Grasshopper model running behind it by the following steps:

  1. Setup a folder locally and test
  2. Build the VIKTOR app
  3. Setup the worker
  4. Test the app
  5. Use your own Grasshopper model

By the end of this tutorial, you will have created a simple VIKTOR application that creates a geometry and data view of a simple box, see the image below:

So, let's get started and learn how to create a VIKTOR app based on a Grasshopper model!

Pre-requisites

Prerequisites
  • You have installed VIKTOR on your computer
  • You completed Create your first app section
  • You have some experience with reading Python code
  • You have some experience with Rhino/Grasshopper

Explanation of setup

Before diving into the steps, let's explain how the integration will work. See the following diagram:

The following will happen:

  1. The user fills in input parameters in the VIKTOR UI
  2. In VIKTOR app.py these input parameters will be saved in the input.json file, and sent to the worker.
  3. The Generic worker will run Rhino/Grasshopper (on your local machine), based on a Grasshopper script (sample_box_grasshopper.gh) and the input parameters (input.json).
  4. The Grasshopper model generates a 3D file (geometry.3dm), which is sent back by the worker.
  5. In VIKTOR app.py, the 3dm geometry file will be sent back to the VIKTOR UI.

Now let's setup everything to get this running! We will go through the diagram from right to left.

1. Setup a folder locally and test

Install Hops

Hops is a plugin for Grasshopper, which will be used to make it possible to call the Grasshopper script externally, using a local Rhino Compute server.

  1. Install Hops via the Package Manager in Rhino (type PackageManager on the Rhino command line and search for "Hops")

  1. Make sure Rhino Compute will be launched at start up. In Grasshopper, go to File -> Preferences and check the following settings:

We recommend to uncheck Hide Rhino.Compute Console Window, because you can then easily see if Rhino Compute is running and debug when necessary.

The full documentation of Hops can be found in the Rhino documentation.

Create a folder where Rhino/Grasshopper runs

Create a folder where the Rhino/Grasshopper models will run. Make sure it is a location where you have access rights, for example: C:\Users\your-name\viktor-grasshopper.

Download the following zip file, unzip the folder and copy the 3 files to the folder you just created: files-grasshopper-tutorial.zip

Check the Rhino/Grasshopper model

Now open the Grasshopper file (sample_box_grasshopper.gh) with the Grasshopper plugin. This should lead to the following view:

Let's take some time to check out what is happening in the Grasshopper script:

  1. Three Hops input parameters (width, length and height) are defined based on the Get Number component of Hops.

  2. A simple box is created based on the width, length and height, which is subsequently meshed.

  3. The mesh is baked into Rhino using the Context Bake component of Hops.

If you change the input parameters, you will see that a box is drawn in Rhino. With this setup, we are ready to run this Grasshopper file from a Python script.

Test run_grasshopper.py

The VIKTOR worker will automatically run the Grasshopper file using the local Rhino Compute server, by the Python file (run_grasshopper.py) that you placed in the folder:

When you open the Python file with an IDE (for example Pycharm), you should see the following code:

# Pip install required packages
import os
import json
import compute_rhino3d.Grasshopper as gh
import compute_rhino3d.Util
import rhino3dm

# Set the compute_rhino3d.Util.url, default URL is http://localhost:6500/
compute_rhino3d.Util.url = 'http://localhost:6500/'

# Define path to local working directory
workdir = os.getcwd() + '\\'

# Read input parameters from JSON file
with open(workdir + 'input.json') as f:
input_params = json.load(f)

# Create the input DataTree
input_trees = []
for key, value in input_params.items():
tree = gh.DataTree(key)
tree.Append([{0}], [str(value)])
input_trees.append(tree)

# Evaluate the Grasshopper definition
output = gh.EvaluateDefinition(
workdir + 'sample_box_grasshopper.gh',
input_trees
)

# Create a new rhino3dm file and add resulting geometry to file
file = rhino3dm.File3dm()
output_geometry = output['values'][0]['InnerTree']['{0}'][0]['data']
obj = rhino3dm.CommonObject.Decode(json.loads(output_geometry))
file.Objects.AddMesh(obj)

# Save the rhino3dm file to your working directory
file.Write(workdir + 'geometry.3dm', 7)

As can be seen in the imports, two packages are necessary: rhino3dm and compute-rhino3d. These packages can be installed via pip:

  • pip install rhino3dm
  • pip install compute-rhino3d
Python version

Note that the newest Python version that Rhino supports is 3.11. Make sure that a supported Python version is installed on the computer where you will run Rhino/Grasshopper.

Let's test if the Python file works:

  1. Make sure Rhino and Grasshopper are running, such that the local Rhino Compute server is also running. You don't have to open a Grasshopper file.
  2. Run the run_grasshopper.py from your IDE

You should now see that the following flow runs automatically: Rhino Compute is running with the input parameters read from input.json and the output file geometry.3dm is created in the same folder.

If this works correctly: great! Now you are ready for the next step.

2. Build the VIKTOR app

Create an empty app

Let’s first create and start an empty app. If you don't remember how this worked you can check out the first few steps of the Create your first app tutorial. Make sure to give your app a recognisable name, like "my-grasshopper-app".

Here a short summary of the process.

# Create an empty editor-type app
> viktor-cli create-app my-grasshopper-app --app-type editor
# Navigate to the app directory and install the app
> cd my-grasshopper-app
> viktor-cli install
# Clear your database just to be sure
> viktor-cli clear
# And start the app!
> viktor-cli start

Open your app in your browser by visiting the URL shown in the command-line shell:

Add input fields

We will add 3 input fields to our app: width, length and height. We will use Numberfield for this.

  1. Open app.py, and add the relevant fields to your parametrization. Don't forget to import the necessary fields. In the end your app.py file should look like this:
from viktor import ViktorController
from viktor.parametrization import ViktorParametrization
from viktor.parametrization import NumberField
from viktor.parametrization import Text

class Parametrization(ViktorParametrization):
intro = Text("## Grasshopper app \n This app parametrically generates and visualises a 3D model of a box using a Grasshopper script. \n\n Please fill in the following parameters:")

# Input fields
width = NumberField('Width', default=5)
length = NumberField('Length', default=6)
height = NumberField('Height', default=7)


class Controller(ViktorController):
label = 'My Entity Type'
parametrization = Parametrization
  1. Refresh your app, and you should see the input fields there.

Add the Python code to run Grasshopper

Now the app code can be extended to integrate the Grasshopper script. Change the code in app.py to the code below:

from io import BytesIO
import json

from viktor import ViktorController
from viktor.parametrization import ViktorParametrization
from viktor.parametrization import NumberField
from viktor.parametrization import Text
from viktor.external.generic import GenericAnalysis
from viktor.views import GeometryView
from viktor.views import GeometryResult


class Parametrization(ViktorParametrization):
intro = Text("## Grasshopper app \n This app parametrically generates and visualizes a 3D model of a box using a Grasshopper script. \n\n Please fill in the following parameters:")

# Input fields
width = NumberField('Width', default=5)
length = NumberField('Length', default=6)
height = NumberField('Height', default=7)


class Controller(ViktorController):
label = 'My Entity Type'
parametrization = Parametrization

@GeometryView("Geometry", duration_guess=10, update_label='Run Grasshopper')
def run_grasshopper(self, params, **kwargs):

# Create a JSON file from the input parameters
input_json = json.dumps(params)

# Generate the input files
files = [('input.json', BytesIO(bytes(input_json, 'utf8')))]

# Run the Grasshopper analysis and obtain the output files
generic_analysis = GenericAnalysis(files=files, executable_key="run_grasshopper", output_filenames=["geometry.3dm"])
generic_analysis.execute(timeout=60)
threedm_file = generic_analysis.get_output_file("geometry.3dm", as_file=True)

return GeometryResult(geometry=threedm_file, geometry_type="3dm")

SDK version

This code requires support for 3dm files, which is available since SDK v14.4.0. Make sure use this SDK version or higher in your requirements.txt file.

If you now refresh your app, you should see the following:

When you press the Run Grasshopper button you will get an error: the last step we need to take is to connect the VIKTOR app to Rhino/Grasshopper via the Generic Worker.

3. Setup the worker

A worker is a program that connects VIKTOR with third-party software to execute tasks and retrieve results through the platform. In this case, the worker will start Rhino/Grasshopper with the input data and send the resulting geometry back.

Install worker

Follow these steps to install the worker:

  1. Go to your VIKTOR environment via the browser and log in:
  2. Create the worker in your VIKTOR environment

My Image

  • Step 1 Go to the "Workers" tab
  • Step 2 Click the "Create worker" button (top right)
  • Step 3 Fill in the description, allocation to specific and use your development workspace
  • Step 4 Click "Create". You will get the following pop-up (see figure below). Keep this window open, you will need the credentials in the next step.

  1. Download and install the Generic worker using this guide. Select the Generic integration. The installer starts an installation wizard from which the worker can be configured. Administrator rights on the machine are required to perform the installation.

  2. Specification of the installation directory. The standard directory that is used for the installation is:

C:\Program Files\VIKTOR\VIKTOR for <application> <software package> <version>
  1. Configuration of the worker. Using the installation wizard you will be asked to fill the required information step-by-step. During this installation wizard you are asked for the credentials you generated in step 1.

Setup worker

The setup of the worker is defined in the config.yaml file, which can be found in the same folder where the worker is installed (see last chapter). Open the config.yaml file (using for example Notepad) and replace the text with the following:

executables:
run_grasshopper:
path: 'C:\Users\your-name\AppData\Local\Programs\Python\Python310\python.exe'
arguments:
- 'C:\Users\your-name\viktor-grasshopper\run_grasshopper.py'
workingDirectoryPath: 'C:\Users\your-name\viktor-grasshopper'
maxParallelProcesses: 1 # must be 1
Set correct paths
  • Make sure to set the path correctly to the Python executable. Tip: search for python.exe in the Windows Search Bar and click Open file location. Note: the highest supported Python version is 3.10.
  • Make sure to set the arguments correctly to the run_grasshopper.py file.
  • Make sure to set the workingDirectoryPath correctly to the folder you created in step 1.

Save the file when you have defined the paths correctly.

Start up worker

Once you have saved your config.yaml file, you can run viktor-worker-generic.exe. Be sure to run the executable with Administrator rights. If all went well, you will be presented with the worker terminal in which the message: "Successfully connected to the server" is displayed. Also in in top right corner in your viktor environment you should see a green indicator in your worker overview, see the figure below.

Nice work! The integration is ready to use, let's test the VIKTOR app.

4. Test the app

Navigate back to app you created in your development workspace (Workspaces -> Development -> Open).

Now press the Run Grasshopper button (be sure that the worker, Rhino and Grasshopper are running). You should see that Rhino Compute is run by the worker which will result in the following:

Congratulations, you now have made a VIKTOR app with a Grasshopper model running behind it!

5. Use your own Grasshopper model

If you would like to integrate your own Grasshopper model in VIKTOR, you can adapt the app you just created. Take the following steps:

Extend your Grasshopper model

As explained in this section, some specific things need to be defined in the Grasshopper model to make the integration work.

  1. Connect your specific input parameters by Hops components. In order to update the parameters via Rhino Compute, create Hops Get Components for the parameters you want to update.

  2. Add the Context Bake component. The Grasshopper node Context Bake is needed to bake the model.

After implementing these changes, check if the Python script still runs successfully (run run_grasshopper.py). Be aware that in the Python file, the name of the Grasshopper file is defined: change this name to match with your file name.

Adjust the VIKTOR app code

In the VIKTOR app code adjust the parametrization to match with your required input parameters.

The rest of the code should not require any changes to work.

Optional: Connect your app to a Rhino Compute Server

Instead of using the worker and the local version of Rhino Compute with Hops, you can also connect the VIKTOR app directly to a Rhino Compute Server:

This has the following advantages:

  • Faster (the worker creates some overhead running time)
  • More robust and scalable, you can make sure the compute server is always running

The main disadvantage is that it takes time and effort to setup a Rhino Compute Server, which takes time and needs to be done by someone who is familiar with setting up Virtual Machines (i.e. an IT department). We recommend to use the worker when you don't have a Rhino Compute server yet and you want to get started quickly, and to setup a Rhino Compute server when you want to go to production with your app.

The following steps are needed to connect your app to a Rhino Computer Server:

1. Setup the Rhino Compute Server

McNeel has documented how to setup a Rhino Compute Server very clear and extensively, see the Compute Guides. Once you have the Rhino Compute Server running, you should have a URL and an API key. Now you can connect your VIKTOR app to this Rhino Compute server.

2. Adjust your app code

Since the compute server is available via the internet, we can now directly communicate with this server in the app code. This means we can move the logic in run_grasshopper.py to the app code. Change app.py to the following code:

import os
from pathlib import Path
import json
import tempfile
import compute_rhino3d.Grasshopper as gh
import compute_rhino3d.Util
import rhino3dm

from viktor import ViktorController, File
from viktor.parametrization import ViktorParametrization
from viktor.parametrization import NumberField
from viktor.parametrization import Text
from viktor.views import GeometryView
from viktor.views import GeometryResult


class Parametrization(ViktorParametrization):
intro = Text(
"## Grasshopper app \n This app parametrically generates and visualizes a 3D model of a box using a Grasshopper script. \n\n Please fill in the following parameters:")

# Input fields
width = NumberField('Width', default=5, variant='slider', min=0, max=25)
length = NumberField('Length', default=6, variant='slider', min=0, max=25)
height = NumberField('Height', default=7, variant='slider', min=0, max=25)


class Controller(ViktorController):
label = 'My Entity Type'
parametrization = Parametrization

@GeometryView("Geometry", duration_guess=1, update_label='Run Grasshopper')
def run_grasshopper(self, params, **kwargs):
# Credentials for Rhino Compute api
compute_rhino3d.Util.url = os.getenv("RHINO_COMPUTE_URL")
compute_rhino3d.Util.apiKey = os.getenv("RHINO_COMPUTE_API_KEY")

# Create an input tree with the input parameters
input_trees = []
for key, value in params.items():
tree = gh.DataTree(key)
tree.Append([{0}], [str(value)])
input_trees.append(tree)

# Evaluate the Grasshopper definition
output = gh.EvaluateDefinition(
str(Path(__file__).parent / 'sample_box_grasshopper.gh'),
input_trees
)

# Create a new rhino3dm file and add resulting geometry to file
file = rhino3dm.File3dm()
output_geometry = output['values'][0]['InnerTree']['{0}'][0]['data']
obj = rhino3dm.CommonObject.Decode(json.loads(output_geometry))
file.Objects.AddMesh(obj)

# Write a temporary file
temp_file = tempfile.NamedTemporaryFile(suffix=".3dm", delete=False, mode="wb")
temp_file.close()
file.Write(temp_file.name, 7)

return GeometryResult(geometry=File.from_path(Path(temp_file.name)), geometry_type="3dm")

Since we need to send the Grasshopper file to the Rhino Compute server (line 46), place the sample_box_grasshopper.gh in the same folder as app.py.

As you can see, we are now using the packages compute-rhino3d and rhino3dm in the app code, so we should add these to the requirements.txt:

viktor==X.X.X
compute-rhino3d==0.12.2
rhino3dm==7.15.0

As described above, the highest Python version that Rhino supports is 3.11, so make sure to set your Python version to 3.9, 3.10 or 3.11 in viktor.config.toml:

app_type = 'editor'
python_version = '3.10' # '3.9' | '3.10' | '3.11' | '3.12'

Because of these changes to the requirements and Python version, reinstall the app in the terminal:

> viktor-cli install

Now we can start the app!

credentials in app code

As you can see in line 34 and 35, we have not placed the URL and API Key in the code because of security reasons (you do not want to leak these credentials).

When you start the app, add the credentials as follows:

> viktor-cli start --env RHINO_COMPUTE_URL="YOUR_URL" --env RHINO_COMPUTE_API_KEY="YOUR_API_KEY"

Now open the app and you should see a running app with the Rhino Compute server running behind it! 🚀

3. Publish your app with the API credentials

If you want to make your app available for other people to use, you can easily do this by publishing the app.

In that case you'll need to add the environment variables for the URL and API key to the app as described in this guide.
Note that these environment variables are only visible to the maintainers of the app, users will never have access to these variables unless specifically been assigned so.

To infinity and beyond

Great work! You are now able to create an app that can integrate with an external installation of Grasshopper through a Generic Worker or directly via a Rhino Compute server!

Of course, the journey doesn't end here. Check out some of our other tutorials or go to the next section where you can see the different paths you can follow in your journey to learn more about VIKTOR.