Difference between revisions of "Essential Algorithms and Data Structures for Computational Design"

From Design Computation
Jump to: navigation, search
(Unioned circles tutorial)
(Sphere with bounds tutorial)
Line 413: Line 413:
===Sphere with bounds tutorial===
===Sphere with bounds tutorial===
Use the 4-step process to draw a sphere with a radius between 2 and 6. If input is less than 2, then set the radius to 2, and if input radius is greater than 6, set the radius to 6. Use a number slider to input the radius and set between 0 and 10 to test. Make sure your solution is well organized, color-coded and labeled properly
=Chapter Two: Introduction to Data Structures=
=Chapter Two: Introduction to Data Structures=
=Chapter Three: Advanced Data Structures=
=Chapter Three: Advanced Data Structures=

Revision as of 11:42, 25 March 2020

EADS-McNeel 000 Cover.png

The Essential Algorithms and Data Structures for Computational Design, by Rajaa Issa, introduces effective methodologies to develop complex 3D modeling algorithms using Grasshopper. It also covers extensively the data structure adopted by Grasshopper and its core organization and management tools.

The full contents of the book are produced here with the authorization of the authors and are directed towards designers who are interested in parametric design and have little or no background in programming. All concepts are explained visually using Grasshopper® (GH), the generative modeling environment for Rhinoceros® (Rhino). This book is not intended as a beginner's guide to Grasshopper in terms of user interface or tools. Basic knowledge of the interface and workflow is assumed. For more resources and getting started guides, go to the learn section in [rhino3d.com]. The content is divided into three chapters. Chapter 1 discusses algorithms and data. It introduces a methodology to help create and manage parametric solutions. It also introduces basic data concepts such as data types, sources and common ways to process them. Chapter 2 reviews basic data structures in Grasshopper. That includes single items and lists. Chapter 3 includes an in-depth review of the tree data structure in Grasshopper and practical applications in design problems. All Grasshopper examples and tutorials are written with Rhinoceros version 6 and are included in the download.

Chapter 1: Algorithms and Data

Algorithms and data are the two essential parts of any parametric design solution, but writing algorithms is not trivial and requires a skill that does not come easy to intuitive designers. The algorithmic design process is highly logical and requires an explicit statement of the design intention and the steps to achieve them. This chapter introduces a methodology to help creative designers develop new algorithmic solutions. All algorithms involve manipulating data and hence Algorithms and Data are tightly connected. We will introduce the basic concepts of data types and processes.

Algorithmic design

We can define algorithmic design as a design method where the output is achieved through well-defined steps. In that sense, many human activities are algorithmic. Take, for example, baking a cake. You get the cake (output) by using a recipe (well-defined steps). Any change in the ingredients (input) or the baking process results in a different cake. We will analyze the parts of typical algorithms, and identify a strategy to build algorithmic solutions from scratch. Regardless of its complexity, all algorithmic solutions have 3 building blocks: input, key process, and output. Note that the key process may require additional input and processes.

1.1: The building blocks of algorithmic solutions

Throughout this text, we will organize and label the solutions to identify the three blocks clearly. We will also use consistent color coding to visually distinguish between the parts. This will help us become more comfortable with reading algorithms and quickly identify input, key processing steps, and properly collect and display output. Visual cues are important to develop fluency in algorithmic thinking.

In general, reading existing algorithmic solutions is relatively easy, but building new ones from scratch is much harder and requires a new set of skills. While it is useful to know how to read and modify existing solutions, it is essential to develop algorithmic design skills to build new solutions from scratch.

Algorithms parts

In Grasshopper, a solution flows from left to right. At the far left are input values and parameters, and the far-right has the output. In between are one or more key processes, and sometimes additional input and output. Let’s take a simple example to help identify the three parts of any algorithm (input, key process, output). The simple addition algorithm includes two numbers (input), the sum (output) and one key process that takes the numbers and gives the result. We will use purple for the input, maroon for the key processes and light blue for the output. We will also group and label the different parts and adhere to organizing the Grasshopper solutions from left to right.

1.2.1: Example: Algorithm to add 2 numbers

Algorithms may involve intermediate processes. For example, suppose we need to create a circle (output) using a center and a radius (input). Notice that the input is not sufficient because we do not know the plane on which the circle should be created. In this case, we need to generate additional information, namely the plane of the circle. We will call this an intermediate process and use brown color to label it.

1.2.2: Example: Algorithm to create a circle on the XY-Plane from a center and a radius

Some solutions are not written with styles and hence are hard to read and build on. It is very important that you take the time to organize and label your solutions to make it easier to understand, debug and use by others.

1.2.3: Example: Given the following definition, write a description of what the algorithm does, identify input, the main process(s) and output, then label and color-code all the parts. Re-write the solution to make it more readable.


In order to figure out what the algorithm is meant to do, we need to group the input on the left side, and collect the output on the right side, then organize the processes in the order or execution. We then step through the solution from left to right to deduce what it does. We can examine and preview the output in each step.

The example of the tutorial is meant to create a circle that is twice as large as another circle that goes through three given points. One of the points is constructed out of its 3 coordinates.

1.2.4: Solution

Designing algorithms: the 4-step process

Before we generalize a method to design algorithms, let’s examine an algorithm we commonly use in real life such as baking a cake. If you already have a recipe for a cake, you simply get the recommended ingredients, mix them, pour in a pan, put in preheated oven for a certain amount of time, then serve. If the recipe is well documented, then it is relatively straightforward to use.

As you become more proficient in baking cakes, you may start to modify the recipe. Perhaps add new ingredients (chocolate or nuts) or use different tools (cupcake container).

1.3.1: Figure 2: Steps to follow existing recipe

When designers write algorithms, they typically try to search for existing solutions and modify to fit their purposes. While this is a good entry point, using existing solutions can be frustrating and timeconsuming.

Also, existing solutions have their own flavor and that may influence design decisions and limit creativity. If designers have unique problems, and they often do, they have no choice but to create new solutions from scratch; albeit a much harder endeavor.

Back to our example, the task of baking a cake is much harder if you don’t have a recipe to follow and have not baked one before. You will have to guess the ingredients and the process. You will likely end up with bad results in the first few attempts, until you figure it out! In general, when you create a new recipe, you have to follow the process in reverse. You start with an image of the desired cake, you then guess the ingredients, tools and steps. Your thinking goes along the following lines:

  • The cake needs to be baked, so I need an oven and time;
  • What goes in the oven is a cake batter held by a container;
  • The batter is a mix of ingredients;
1.3.2: Figure 3: Steps to invent a new recipe from scratch

We can use a similar methodology to design parametric algorithm from scratch. Keep in mind that creating new algorithms is a “skill” and it requires patience, practice and time to develop.

Algorithmic thinking in 3D modeling vs parametric design

3D modeling involves certain level of algorithmic thinking, but it has many implicit steps and data. For example, designing a mass model using a 3D modeler may involve the following steps:

  • 1- Think about the output (e.g. a mass out of few intersecting boxes)
  • 2- Identify a command or series of commands to achieve the output ( e.g. run Box command few times, Move, Scale or Rotate one or more boxes, then BooleanUnion the geometry).

At that point, you are done!

Data such as the base point for your initial box, width, height, scale factor, move direction, rotation angle, etc. are requested by the commands, and the designer does not need to prepare ahead of time. Also, the final output (the boolean mass) becomes directly available and visible as an object in your document.

1.3.3: Figure 4: Interactive 3D modeling to create and manipulate geometry uses visual widgets and guides

Algorithmic solutions are not interactive and require explicit articulation of data and processes. In the box example, you need to define the box orientation and dimensions. When copy, you need a vector and when rotate you need to define the plane and angle or rotation.

1.3.4: Figure 5: Algorithmic solutions involve explicit definition of geometry, vectors and transformations

Designing algorithms

Designing algorithms requires knowledge in geometry, mathematics and programming. Knowledge in geometry and mathematics is covered in the Essential Mathematics for Computational Design. As for programming skills, it takes time and practice to build the ability to formulate design intentions into logical steps to process and manage geometric data. To help get started, it is useful to think of any algorithm as a 4-step process as in the following:

1- Clearly identify the desired outcome Output
2- Identify key steps to reach the outcome Key processes
3- Examine initial data and parameters Input
4- Define intermediate steps to generate missing data Intermediate input + processes

Thinking in terms of these 4 steps is key to developing the skill of algorithmic design. We will start with simple examples to illustrate the methodology, and gradually apply on more complex examples.

Example 1.3.1: Add two numbers: Use the 4-Step process to write an algorithm to add two numbers
Step 1: Output: The sum of the 2 numbers. Use a Panel to collect the sum.
EADS-McNeel 011.png
Step 2: Key process: Addition. Use the Addition component that takes 2 numbers and gives the sum.
EADS-McNeel 012.png
Step 3: Input: 2 numbers. Use a Panel to hold the values of input numbers.
EADS-McNeel 013.png

Example 1.3.2: Create a circle: Use the 4-Step process to create a circle from a given center and radius
Step 1: Output: Circle. Use the Circle parameter to collect the output.
EADS-McNeel 015.png
Step 2: Key process: Identify a key process that generates a circle from a radius. Use the Circle component in Grasshopper.
EADS-McNeel 016.png
Step 3: Input: Use the given input (center and radius). Feed the radius to the Circle component.
EADS-McNeel 017.png
Step 4: Intermediate process: The circle needs the center, and also the plane on which the circle is located. Assume the circle is on a plane parallel to the XY-Plane and use the circle center as the origin of the plane.
EADS-McNeel 018.png
Example 1.3.2: Create a line: Use the 4-Step process to create an algorithm to generate a line from 2 points. One point is referenced from Rhino, and the other is created using three coordinates (x=1, y=0.5 and z=3).
Step 1: Output: The line geometry. Use the Geometry parameter to collect the output.
EADS-McNeel 020.png
Step 2: Key process: Identify a key process that generates a line from 2 points. Use the Line component in Grasshopper.
EADS-McNeel 021.png
Step 3: Input: Use the given input (referenced point and 3 coordinates). Feed one point to one of the ends of the line.
EADS-McNeel 022.png
Step 4: Intermediate process: Before we can use the coordinates as a point, we need to construct a point.
EADS-McNeel 023.png


Data is information stored in a computer and processed by a program. Data can be collected from different sources, it has many types and is stored in well-defined structures so that it can be used efficiently. While there are commonalities when it comes to data across all scripting languages, there are also some differences. This book explores data and data structures specific to Grasshopper.

Data sources

In Grasshopper, there are three main ways to supply data to processes (or what is called components): internal, referenced and external.

1- Internally set data: Data can be set inside any instance of a parameter. Once set, it remains constant, unless manually changed or overridden by external input. This is a good way when you do not generally need to change the data after it is set (constant). Data is stored inside the GH file.
EADS-McNeel 024.png
2- Referenced data: Data can be referenced from Rhino or some external document. For example, you can reference a point created in a Rhino document. When you move the point in Rhino, its reference in Grasshopper updates as well. Grasshopper files are saved separately from Rhino files, and hence if the GH file has referenced data, the Rhino file needs to be saved and passed along with the GH file to avoid any loss of data.
EADS-McNeel 025.png
3- Externally supplied data: Data can be supplied from previous processes. This method is best suited for dynamic data or data controlled parametrically. Externally supplied data to a parameter takes precedent over the internal or referenced values (when both exist).
EADS-McNeel 026.png

Data types

All programming languages identify the kind of data used in terms of the values that can be assigned to and the operations and processes it can participate in. There are common data types such as Integer, Number, Text, Bool (true or false), and others. Grasshopper lists those under the Params > Primitives tab.

1.6.1: Figure 6: Examples of primitive data types common to all programming languages

Grasshopper supports geometry types that are useful in the context of 3D modeling such as Point (3 numbers for coordinates), Line (2 points), NURBS Curve, NURBS Surface, Brep, and others. All geometry types are included under the Param> Geometry tab in GH.

1.6.2: Figure 7: Examples of geometry data types

There are other mathematics types that designers do not usually use in 3D modeling, but are very common in a parametric design such as Domains, Vectors, Planes, and Transformation Matrices. GH provides a rich set of tools to help create, analyze and use these types. To fully understand the mathematical as well as geometry types such as NURBS curves and surfaces, you can refer to the Essential Mathematics for Computational Design book by the author.

1.6.3: Figure 8: Examples of data types common in computer graphics

The parameters in GH can be used to convert data from one type to another (cast). For example, if you need to turn a text into a number, you can feed your text into a Number parameter. If the text cannot be converted, you’ll get an error.

1.6.4: Figure 9: Data conversion (casting) inside parameters in Grasshopper

Grasshopper components internally convert the input to suitable types when possible. For example, if you feed a “text” to the Addition component, GH tries to read the text as a number. If a component can process more than one type, it uses the input type without conversion. For example, equality in an expression can compare text as well as numbers. In such a case, make sure you use the intended type to avoid confusion.

1.6.5: Figure 10: Some operations can be performed on multiple types. Cast to the intended type especially if the component is capable of processing multiple types (such as Expression in GH)

It is worth noting that sometimes GH components simply ignore invalid input (null or wrong type). In such cases, you are likely to end up with an unexpected result and hard to find the bug. It is very important to verify the output from each component before using it.

1.6.6: Figure 11: Invalid input is ignored and a default value is used. For example, a number inside a Panel component can be interpreted as a text and hence become invalid input to an Addition component

Processing data

Algorithmic designs use many data operations and processes. In the context of this book, we will focus on five categories: numeric and logical operations, analysis, sorting, and selection.

Numeric operations

Numeric operations include operations such as arithmetic, trigonometry, polynomials, and complex numbers. GH has a rich set of numeric operations, and they are mostly found under the Math tab.

There are two main ways to perform operations in GH. First by using designated components for specific operations such as Addition, Subtraction, and Multiplication. Figure 12: Examples of numeric operations components in GH

Second, use an Expression component where you can combine multiple operations and perform a rich set of math and trigonometry operations, all in one expression. Figure 13: Expression component in GH can be used to perform multiple operations

The Expression component is more robust and readable when you have multiple operations. Figure 14: When a chain of operations are involved, using the Expression component is easier to maintain

Input to Expressions can be treated as text depending on the context. Figure 15: When a chain of operations are involved, using the Expression component is easier to maintain

It is worth mentioning that most numeric input to components allow writing an expression to modify the input inline. For example, the Range component has N (number of steps) input. If you right-mouse click on “N”, you can set an expression. You always use “x” to represent the supplied input regardless of the name. Figure 16: Expression can be set inside the input parameter. Variable “x” refers to the supplied input value

Logical operations

Main logical operations in GH include equalities, sets and logical operations. Figure 17: GH has multiple components to perform Logical operations

Logical operations are used to create conditional flows of data. For example, if you like to draw a sphere only when the radius is between two values, then you need to create a logic that blocks the radius when it is not within your limits. Figure 18: Data flow control using logical operations

Data analysis

There are many tools in GH to examine and preview data. Panel is used to show the full details of the data and its structure, while the Parameter Viewer shows the data structure only. Other analysis components include Quick Graph that plots data in a graph, and Bounds to find the limits in a given set of numbers (the min and max values in the set). Figure 19: Some of the ways to analyze data in Grasshopper


GH has designated components to sort numeric and geometry data. The Sort List component can sort a list of numeric keys. It can sort a list of numbers in ascending order or reverse the order. You can also use the Sort List component to sort geometry by some numeric keys, for example sort curves by length. GH has components designated to sort geometry sets such as Sort Points to sort points by their coordinates. Figure 20: Sorting numbers in Grasshopper


3D modeling allows picking specific or a group of objects interactively, but this is not possible in algorithmic design. Data is selected in GH based on the location within the data structure, or by a selection pattern. For example, the List Item component allows selecting elements based on their indices. Figure 21: Select items from a list in Grasshopper

The Cull Pattern component allows using some repeated pattern to select a subset of the data. Figure 22: An example to select every other item in a list

As you can see from the examples, selecting specific items or using cull components yield a subset of the data, and the rest is thrown away. Many times you only need to isolate a subset to operate on, then recombine back with the original set. This is possible in GH, but involves more advanced operations. We will get into the details of these operations when we talk about advanced data structures in Chapter 3.


That refers to the linear mapping of a range of numbers where each number in a set is mapped to exactly one value in the new set. GH has a component to perform linear mapping called ReMap. You can use it to scale a set of numbers from its original range to a new one. This is useful to scale your range to a domain that suits your algorithm’s needs and limitations. Figure 23: An example of linear remapping of numbers in Grasshopper

Converting data involves mapping. For example, you may need to convert an angle unit from degrees to radians ( GH components accept angles in radians only). Figure 24: Convert angles from degrees to radians

As you know, parametric curves have “domains” (the range of parameters that evaluate to points on the curve). For example, if the domain of a given curve is between 12.5 to 51.3, evaluating the curve at 12.5 gives the point at the start of the curve. Many times you need to evaluate multiple curves using consistent parameters. Reparameterizing the domain of curves to some unified range helps solve this problem. One common domain to use is “0 To 1”. At the input of each curve in any GH component, there is the option to Reparameterize which resets the domain of the curve to be “0 to 1”. Figure 25: Normalize the domain of curves (set to 0-1). Use Reparameterize input flag in Grasshopper

Flow control tutorial

What is the purpose of the following algorithm? Notate and color code to describe the purpose of each part.

EADS-McNeel 047 0.png
Analyze the algorithm
The algorithm has an output that is a sphere, a radius input and some conditional logic to process the radius.
EADS-McNeel 047 1.png
Notate and color-code the solution
From testing the output and following the steps of the solution it becomes apparent that the intention is to make sure that the radius of the sphere cannot be less than 1 unit.
EADS-McNeel 048.png
Test with radius < 1
EADS-McNeel 049.png

Data processing tutorial

Given a list of point coordinates, do the following:

  • 1- Analyze the list to understand the data.
  • 3- Write an algorithm to use the input to construct a list of type Point with coordinates mapped to a domain between 3 and 9.

Note that the input list is organized so that the first 3 numbers refer to the x,y,z of the first point, the second 3 numbers belong to the second point and so on.

Analyze the algorithm
There is a list of 51 numbers (3 coordinates for each point implies the list includes 17 points) Using a QuickGraph, we can see that the range of values are between 2.60 and 15.89. We can also see that the values are distributed randomly. One other input is the target domain:
EADS-McNeel 050 Left.png
EADS-McNeel 050.png
Use the 4-step process to solve the algorithms
Output: List of points.
EADS-McNeel 051.png
Key Process #1 Remap Coordinates: Map the coordinates list from its current domain to a new domain 3 to 9 Use ReMap component.
EADS-McNeel 052.png
Intermediate processes #1 The input domain is missing and can be extracted using Bounds component
EADS-McNeel 053.png
Key Process #2 Construct Points: Construct points from coordinates. Use Construct Point (Pt) component
EADS-McNeel 054.png
Intermediate processes #2 Extract all X coordinates as one list, Y in another and Z in the third. Use Cull Pattern component with appropriate pattern to extract each coordinate as a separate list. The input to Cull is the remapped points from process #1
EADS-McNeel 055.png
Putting it all together
EADS-McNeel 056.png

Pitfalls of algorithmic design

Writing elegant algorithms that are efficient and easy to read and debug is hard. We explained in this chapter how to write algorithms with style using color-coding and labeling. We also articulated a 4-step process to help develop algorithms. Following these guides help minimize bugs and improve the readability of the scripts. We will list a few of the common issues that lead to an incorrect or unintended result.

Invalid or wrong type input

If the input is of the wrong type or is invalid, GH changes the color of components to red or orange to indicate an error warning, with feedback about what the issue might be. This is helpful, but sometimes faulty input goes unnoticed if the components assign a default value, or calculate an alternative value to replace the input, that is not what was intended. It is a good practice to always double-check the input (hook to a panel or parameter viewer and label the input). To avoid using the wrong types, it is advisable to convert to the intended type to ensure accuracy.

1.8.1: Figure 26: Error resulting from wrong input type

Incorrect input

Input is prone to unintended change via intermediate processes or when multiple users have writing access to the script. It is very useful to preview and verify all key input and output. The Panel component is very versatile and can help check all types of values. Also, you can set up guarding logic against out of range values or to trap undesired values. Figure 27: Error resulting from incorrect input. Cannot assume curve domain is 0-1 and use 0.5 to evaluate the midpoint. Figure 28: Example of a robust solution to evaluate the midpoint of a curve.

Incorrect order of operation

You should try to organize your solutions horizontally or vertically to clearly see the sequence of operations. You should also check the output from each step to make sure it is as expected before continuing on your code. There are also some techniques that help consolidate the script, for example, use Expression when multiple numeric and math operations are involved. The following highlights some unfavorable organization. Figure 29: Example of a robust solution to evaluate the midpoint of a curve.

The following shows how to rewrite the same code to make it less error-prone. Figure 30: Best practices to align input with processes, or use Expressions

Mismatched data structures

Mismatched data structures as input to the same process or component are particularly tricky to guard against in GH, and has the potential to spiral the solution out of memory. It is essential to test the data structure of all input (except trivial ones) before feeding into any component. It is also important to examine the desired matching under different scenarios (data matching will be explained at length later).

1.8.4: Figure 31: Mismatched data structures of input can cause errors in the output

Long processing time

Some algorithms are time-consuming, and you simply have to wait for it to process, but there are ways to minimize the wait when it is unnecessary. For example, at the early cycles of development, you should try to use a smaller set of data to test your solution with before committing the time to process the full set of data. It is also a good practice to break the solution into stages when possible, so you can isolate and disable the time-consuming parts. Also, it is often possible to rewrite your solution to be more optimized and consume less time. Use the GH Profiler to test processing time. When a solution takes far too long to process or crashes, you should do the following: before you reopen the solution, disable it, and disconnect the input that caused the crash.

1.8.5: Figure 32: Grasshopper Profiler widget help observe processing time

Poor organization

Poorly organized definitions are not easy to debug, understand, reuse or modify. We can’t stress enough the importance of writing your definitions with styles, even if it costs extra time to start with. You should always color code, label everything, give meaningful names to variables, break repeated operations into modules and preview your input and output.

1.8.6: Figure 33: Poor organization in visual programming make the code hard to read or debug

Algorithms tutorials

Unioned circles tutorial

Use the 4-step process to design an algorithm that unions 2 circles, given the following: both are located on the XY-Plane. The first circle (Cir1) has a center (C1) = (2,2,2) and radius (R1) = some random number between 3 and 6. The second circle (Cir2) has a center (C2) is shifted to the right of (Cir1) by an amount equal to R1 along positive X-Axis. R2 = R1 * 1.2

Analyze the question and the flow of the solution
EADS-McNeel 065.png

Curve for the region union.

EADS-McNeel 066.png
Key Proces: union of 2 circles:

Use the Region Union component that takes curves and a plane.

EADS-McNeel 067.png
Input to the region union:

Identify the input needed and use given input when relevant. The plane for region union has been given. The 2 circles need their own plane and radius. The center of the plane is the center of the circle.

EADS-McNeel 068.png
Intermediate process to generate the center and plane of the 1st circle:

Construct a center from the given coordinates. Create a plane using Plane Origin component and use the constructed center and XY-Plane The radius is from a random number between 3 and 6. Use Random component to create the radius

EADS-McNeel 069.png
Intermediate process to generate the center and plane of the 2nd circle:

Calculate the 2nd circle plane by moving the first circle plane along the xaxis by an amount = first radius

GCalculate the 2nd circle radius by multiplying the first radius by 1.2

EADS-McNeel 070.png
EADS-McNeel 071.png

Sphere with bounds tutorial

Use the 4-step process to draw a sphere with a radius between 2 and 6. If input is less than 2, then set the radius to 2, and if input radius is greater than 6, set the radius to 6. Use a number slider to input the radius and set between 0 and 10 to test. Make sure your solution is well organized, color-coded and labeled properly

Chapter Two: Introduction to Data Structures

Chapter Three: Advanced Data Structures