colidescope

/guides/ exercise-working-with-rhino-api

Exercise: Working with the Rhino API

Introduction

This exercise gives an overview of several Python libraries that give us access to many underlying functionalities of Grasshopper and Rhino from within our code. The tutorial will cover how to import each library and demonstrate its use through a basic example.

Tutorials in this series:

Getting started with Python
1Intro to Python
2Exercise: Working with the Rhino API
3Working with data in Python

Working with libraries

By design, the core Python language is fairly limited in functionality, being restricted to basic algebra, data and flow control structures, and support for creating functions and classes. This is done intentionally to keep the core Python language as abstract, minimal, and fast as possible. To extend Python's functionality to more specialized use cases, we rely on external libraries which are written in Python code and define custom classes and functions that are useful for specific tasks.

To use these libraries we must first make sure they are installed into our version of Python, and then we need to import them into our script. Some very common libraries such as random or math come pre-installed with Python but still must be imported into every script we want to use them in. Other libraries such as numpy must be installed separately before they can be imported into our scripts.

Importing these libraries into our code is equivalent to bringing all this specialized code into our program, so that we can use it in our own code. This allows developers to build on top of existing functionality instead of needing to build from scratch each time. This idea of building on the work of others has been a huge part in the development of Python and other popular programming languages and is strongly tied to the philosophies of the open source software movement.

One of the specialized use cases supported by custom libraries is integrating Python into an existing software product. In this case, the product can expose its underlying functionality through a set of Python classes defined in a library that is imported into each Python script. We will see how this works with Rhino in the next section.

Accessing the Rhino libraries

To enable us to work with geometry in Python, Rhino provides several pre-installed libraries, three of which we will cover in this section:

  • ghpythonlib.components — allows you to directly reference Grasshopper components in Python code
  • Rhino.Geometry — allows you to access all commands and data types in the main RhinoCommon library
  • rhinoscriptsyntax — a wrapper over the Rhino.Geometry library which provides similar commands and functionality to Rhinoscript

Let's see how each one of these work through an example. We will create a simple Python script that takes a Point as an input and creates a Circle with that Point as the center and a radius of '2'. The Grasshopper definition consists of a Python component with one input and one output, a Point component connected to the input, and a Panel component connected to the output so we can see the results.

grasshopper setup

Initial setup of basic example in Grasshopper

Inputting geometry

Double-click on the Python component to open up the Python script editor. First, let's see how the input point is represented in Python. We can see this by printing the input's value and using Python's type() function to print its type. Type these lines in the script and click the 'Test' button to see the results:

print(x)
print(type(x))

You should see something like this:

ac7825d2-24b0-4225-b52a-f04a3daa0849
<type 'Guid'>

Wait a second, didn't we input a Point — why are we getting this 'Guid'?

By default any geometry input into a Python component is not brought in as the geometry itself, but as a reference to the geometry in the Grasshopper environment. This reference is stored in the geometry's unique ID number, a text string called a 'Guid'. This can create problems for most geometric functions, which expect actual geometry data instead of a 'Guid' reference.

We can fix this by telling Python exactly what kind of data we are inputting using 'Type hints'. To set the input's type, right click on the 'x' input of the Python node, go to 'Type hint', and select Point3d. The 'Type hint' menu shows all the geometry types supported by Grasshopper. When we specify that the input is of the type 'Point3d', Python will automatically convert the 'Guid' reference to the actual point geometry so that we can use it with geometric functions in our script.

setting the type hint

Setting the 'Type hint' for a Python input

Be careful!

If Python fails to convert your input data to the type specified in the type hint (for example if you try to input text into an input expecting a number or Point), Python will throw an error which can be hard to debug since it's not coming from your actual code.

Once you get the hang of it though, setting type hints is actually good practice as it makes data types more explicit and can expose errors sooner before they can manifest in more complicated bugs within your code.

Working with the Rhino libraries

Now that we have correctly specified the type of the geometry we are inputting, we can start to build our simple script. In this last section we will import each of the three main libraries and work through a small demo with each one.

The ghpythonlib.components library

We will start by importing the ghpythonlib.components library. Delete the previous two lines and type on the first line:

import ghpythonlib.components as ghcomp

This will import the components portion of the main ghpythonlib library into our script so we can use its methods to work with geometry. We use the import ... as ... syntax to give the library a shorter keyword that will save us typing and make the script cleaner. Now when we want to use the library we can reference it with ghcomp rather than typing the full ghpythonlib.components each time.

The ghpythonlib.components library contains methods that replicate the behavior of each component in Grasshopper. Each method expects the same number and type of inputs as its Grasshopper component equivalent, and returns the same outputs. If the component has more than one output the return will be a list whose length is the number of outputs.

Let’s use the library's Circle method to create a Circle based on a center Point and radius, just like the Circle component in Grasshopper. On the next line type:

a = ghcomp.Circle(x, 2)

As expected this creates a circle with a radius of 2, centered on the input point.

generating a circle

Creating a circle with Python

The Rhino.Geometry library

Now let’s do the same thing with the Rhino.Geometry library. Below the first import line, type:

import Rhino.Geometry as rh

This line imports the Geometry portion of the main Rhino library and assigns it the keyword rh. Now we can change the line of code that creates the circle to:

a = rh.Circle(x, 2)

If you run the script you will see that the result is exactly the same — a circle centered on our input point with a radius of 2.

The rhinoscriptsyntax library

Finally, let’s look at the same example using the rhinoscriptsyntax library. We can import the library by typing:

import rhinoscriptsyntax as rs

and use the library’s .AddCircle() method to create a new circle based on the input point and radius.

a = rs.AddCircle(x, 2)

CHALLENGE

Can you add a slider to our definition to control the radius of the Circle?

description

Hint: create a new input in the Python component using the Zoomable UI and connect to it a new Number Slider. Then use the variable name of the input inside the Python code to replace the hard-coded value of '2'.

Getting help

One major difficulty when starting to work with libraries such as rhinoscriptsyntax or Rhino.Geometry is knowing the exact syntax of each geometric Class available and its methods, what to pass in for inputs, and what returns to expect. In Grasshopper you can easily see what components are available to you by looking through the options in the toolbar. You can also see each component's expected inputs and outputs by looking at the ports of the component. This is more difficult with code, since there is no graphic interface for any of the methods. So how do we know what methods are available and how to use them?

The best way is to search through the documentation of each library, which contains a full description of each class implemented by the library and its methods. You can find the documentation for rhinoscriptsyntax and Rhino.Geometry here:

In practice, searching through full documentation sets can be difficult and confusing for someone just getting started, so the Python script editor provides two tools that make it easier to find out what methods are available in a Library and how to use them.

The first is the autocomplete feature, which gives you hints on what methods are available in the Library as you type the code. You may have already noticed it as you were writing the lines above. Lets type the line

a = rh.Circle(x, 2)

again character by character to see how this works. Remember that rh is a keyword representing the Rhino.Geometry library, and the . symbol is Python's way of accessing a Class from a library or a method or property from a Class. Once you type in the . symbol, a window will pop up with a list of all the Classes in that library. This would also work if you had an instance of a Class and were trying to access it's methods and properties. As you continue typing, the pop-up list will automatically scroll down to the portion you are typing and highlight the best matching Class name. Once you see the Class you want highlighted you can press 'Enter' or double-click on the name to enter the Class name into the script.

browsing classes within a library

Browsing Classes within a Library

Following the Class or method name you typically place a set of parenthesis where you pass in the method or Class constructor’s inputs. Once you type the first ( the Python window will automatically load the documentation of that method into the results windows which tells you what inputs the method expects and what outputs it generates.

reading documentation for a method

Reading documentation for a method

In the case of the .Circle() Class constructor method you can see that it actually supports many different combinations of inputs. In Python this is called 'overloading' a method, and allows a single method to do different things based on different combinations of inputs.

In this case it allows us to create Circles in several different ways such as based on a center and radius or based on 3 Points. Overloading is another advantage of using the Rhino.Geometry library over the ghpythonlib.components Library. Instead of remembering the 7 different components for creating Circles in Grasshopper, we have a single Circle Class which can make circles in different ways based on the inputs we give its constructor.

Conclusion

We have now seen three different libraries that allow you to work with geometry in Python, so which one should you use? Ultimately all three do basically the same thing and create the same exact geometry, but they each come with certain benefits and limitations:

  • The ghpythonlib.components library makes it easier to get started because you can directly use the same components you are used to using in Grasshopper. However you are restricted to what is available in Grasshopper, and some of the methods can be clunky compared to those in RhinoCommon.
  • The rhinoscriptsyntax library was created to make it easier for those already used to using RhinoScript to transition to using Python. The methods in the rhinoscriptsyntax library replicate those in RhinoScript, but do so by ‘wrapping up’ methods from RhinoCommon. Thus they may make some geometric operations easier and cleaner, but also limit the scope of possibilities compared to using the full RhinoCommon.
  • The Rhino.Geometry library is the most comprehensive and robust way to work with geometry in Python because it exposes all of the methods in the full RhinoCommon library. RhinoCommon is a universal cross-platform library developed by McNeel for the release of Rhino 5 which allows all versions of Rhino and Grasshopper to access the same geometric data types and methods. By tapping into this library we gain access to everything Rhino is capable of, which allows us to do things we could not do with either Grasshopper or RhinoScript.

Although all three libraries are useful to know (especially since you will fine examples using all of them online), the following tutorials will focus mostly on the Rhino.Geometry library as it is the most flexible one and is the most closely connected to the core RhinoCommon API.