EssentialAlgorithmsAndDataStrucutres 1stedition PDF
EssentialAlgorithmsAndDataStrucutres 1stedition PDF
EssentialAlgorithmsAndDataStrucutres 1stedition PDF
Rajaa Issa
Robert McNeel & Associates
Essential Algorithms and Data Structures for Computational Design, First edition, by Robert
McNeel & Associates, 2020 is licensed under a Creative Commons Attribution-Share Alike
3.0 United States License.
1
Table of Contents
Preface 4
2
3_1: The Grasshopper data structure 52
3_1_1 Introduction 52
3_1_2 Processing data trees 52
3_1_3 Data tree notation 54
3_2: Generating trees 56
3_3: Tree matching 58
3_4: Traversing trees 61
3_5: Basic tree operations 63
5_3_1: Viewing the tree structure 63
3_5_2: List operations on trees 63
5_3_3: Grafting from lists to a trees 65
5_3_4: Flattening from trees to lists 66
5_3_5: Combining data streams 67
5_3_6: Flipping the data structure 67
5_3_7: Simplifying the data structure 69
3_6: Advanced tree operations 72
3_6_1: Relative items 72
3_6_2: Split trees 77
3_6_3: Path mapper 82
3_7: Advanced data structures tutorials 88
3_7_1: Sloped roof tutorial 88
3_7_2: Diagonal triangles tutorial 91
3_7_3: Zigzag tutorial 92
3_7_4: Weaving tutorial 92
3
Preface
The Essential Algorithms and Data Structures for Computational Design 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 material is 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 beginners 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 www.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.
Rajaa Issa
Robert McNeel & Associates
4
Chapter One: 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 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.
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.
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 require 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.
5
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 purple
for the input, maroon for the key processes and white for the output. We will also group and label
the different parts and adhere to organizing the Grasshopper solutions from left to right.
Example 1-2-1:
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.
Example 1-2-2:
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.
Solution
6
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.
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 time-
consuming. 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:
7
- 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
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.
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).
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.
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.
8
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
Design1. 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:
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.
Step 1: Output:
The sum of the 2 numbers.
Use a Panel to collect the
sum.
1Issa, Essential Mathematics for Computational Design, 4th edition, 2019. Free download of the PDF and
examples: https://www.rhino3d.com/download/rhino/6/essentialmathematics
9
Step 2: Key process:
Addition.
Use the Addition
component that takes 2
numbers and gives the sum.
Step 3: Input:
2 numbers.
Use a Panel to hold the
values of input numbers.
Step 1: Output:
Circle.
Use the Circle parameter to
collect the output.
Step 3: Input:
Use the given input (center
and radius). Feed the radius
to the Circle component.
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
10
origin of the plane.
Step 1: Output:
The line geometry. Use the
Geometry parameter to
collect the output.
Step 3: Input:
Use the given input
(referenced point and 3
coordinates). Feed one point
to one of the ends of the line.
Step 4: Intermediate
process:
Before we can use the
coordinates as a point, we
need to construct a point.
In more complex algorithms, we will need to analyze the problems, investigate possible solutions
and break them down to pieces whenever possible to make it more manageable and readable. We
11
will continue to use the 4-step process and other techniques to solve more complex algorithms
throughout the book.
1_4: Data
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.
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.
12
1_6: 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.
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 Param> Geometry tab in GH.
13
There are other mathematics types that designers do not usually use in 3D modeling, but are very
common in 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.
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.
Grasshopper components internally convert input to suitable types when possible. For example, if
you feed a “text” to 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 case, make sure you use the
intended type to avoid confusion.
14
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.
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
15
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.
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
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.
16
Figure (16): Expression can be set inside the input parameter. Variable “x” refers to the supplied input value
Logical operations are used to create conditional flow 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.
17
1_7_3: 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).
1_7_4: Sorting
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.
1_7_5: Selection
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 List Item component allows selecting elements based on their
indices.
18
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.
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.
1_7_6: Mapping
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.
19
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).
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
20
1-7-1: Flow control tutorial
What is the purpose of the following algorithm? Notate and color code to describe the purpose of each part.
The algorithm has an output that is a sphere, a radius input and some conditional logic to process the radius.
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.
Test with radius > 1
21
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.
Algorithm analysis
Output
List of points
Intermediate processes #1
The input domain is missing and can be
extracted using Bounds component
22
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.
23
1_8_2: 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.
24
Figure (29): Easy to confuse input to operations with poor organization
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
Figure (31): Mismatched data structures of input can cause errors in the output
25
1_8_5: 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.
Figure (33): Poor organization in visual programming make the code hard to read or debug
26
Analyze the question and the flow of the solution
Output
27
Intermediate process to generate the
center and plane of the 1st circle
28
Use the 4-step process to solve the algorithms
Output
The sphere as geometry
Input
1- The radius parameter (0 - 10)
2- The bounds of the radius are 2 & 6
Intermediate processes #1
Construct a selection logic of radii and pattern. The radii is a list of the values from the slider, min and max.
The list of pattern is generated to select the correct radius value
Intermediate processes #2
The selection logic checks if the radius from the slider is between the bounds, then set it to be selected, if less,
then select the min, and if more select the max.
29
Solution
30
5- Remap to 100-200
Solution
31
32
Chapter Two: Introduction to Data Structures
All algorithms involve processing input data to generate a new set of data as output. Data is stored
in well-defined structures to help access and manipulate efficiently. Understanding these structures
is the key for successful algorithmic designs. This chapter includes an in-depth review of the basic
data structures in Grasshopper.
2_1: Overview
Grasshopper has three distinct data structures: single item, list of items and tree of items. GH
components execute differently based on input data structures, and hence it is essential to be fully
aware of the data structure before using. There are tools in GH to help identify the data structure.
Those are Panel and Param Viewer.
Processes in GH execute differently based on the data structure. For example, the Mass Addition
component adds all the numbers in a list and produces a single number, but when operating on a
tree, it produces a list of numbers representing the sum of each branch.
Figure (35): Components execute differently based on the data structures. Result of adding numbers from Figure(34)
The wires connecting the data with components in GH offer additional visual reference to the data
structure. The wire from a single item is a simple line, while the wire connecting a list is drawn as a
33
double line. A wire output from a tree data structure is a dashed double line. This is very useful to
quickly identify the structure of your data.
Figure (36): Generate a list of 8 numbers using the Range component in Grasshopper
The Series component also creates an equally spaced list of numbers, but here you set the
starting number, step size and number of elements.
34
Figure (37): Generate a list of 7 numbers using the Series component in Grasshopper
The Random component is used to create random numbers using a domain and a number of
elements. If you use the same seed, then you always get the same set of random numbers.
Figure (38): Generate a list of numbers using the Random component in Grasshopper
Lists can be the output of some components such as Divide curve (the output includes lists of
points, tangents and parameters). Use the Panel component to preview the values in a list and
Parameter Viewer to examine the data structures.
Figure (39): Divide Curve takes a single input (curve) and generate lists of output
35
Figure (40): Examples of list operations in Grasshopper
Lists can be reversed using the Reverse List component, and sorted using the Sort List
component.
Figure (41): Lists can be reversed or sorted using designated components in Grasshopper
Components such as Cull Patterns and Dispatch allow selecting a subset of the list, or splitting
the list based on a pattern.These components are very commonly used to control data flow and
select a subset of the data.
36
Figure (42): Cull part of a list using components such as Cull Pattern and Dispatch
The Shift List component allows shifting a list by any number of steps. That helps align multiple
lists to match in a particular order.
The Subset component is another example to select part of a list based on a range of indices.
Figure (44): Example to select a subset of the list using a range of indices
37
2_3_1 List operations tutorial
Use the two given lists of points to generate the following images.
38
39
2_4: List matching
When the input is a single item or has equal number of elements in a simple list, it is easy to
imagine how the data is matched. The matching is based on corresponding indices. Let’s use the
Addition component to examine list matching in GH.
Figure (45): Matching equal length lists is based on matching corresponding indices
There are times when input has variable length lists. In this case, GH reuses the last item on the
shorter list and matches it with the next items in the longer list.
Figure (46): The default list matching in Grasshopper reuses the last element of the shorter list
Grasshopper offers alternative ways of data matching: Long, Short and Cross reference that the
user can force to use. The Long matching is the same as the default matching. That is the last
element of the shorter list is repeated to create a matching length.
40
Figure (47): Long list matching is the default matching mode in Grasshopper
The Short list matching truncates the long list to match the length of the short list. All additional
elements are ignored and the resulting list has a length that matches the shorter list.
Figure (48): Short matching of lists omits additional values in longer lists
The Cross Reference matches the first list with each of the elements in the second list. The
resulting list has a length equal to the multiplication product of the length of input lists. Cross
reference is useful when trying to produce all possible combinations of input data. The order of
input affects the order of the result as shown in Figure (49).
41
Figure (49): Cross reference matching creates longer lists to account for all possible permutations
If none of the matching methods produce the desired result, you can explicitly adjust the lists to
match in length based on your requirements. For example, if you like to repeat the shorter list until
it matches the length of the longer list, then you’ll need to create the logic to achieve that as in the
following example.
42
2_4_1 List matching tutorial
Use the input list of 6 numbers to construct the points in the image
Solution
Output:
A list of 6x6x6 = 216 points
constructed from a list of X, Y, Z
coordinates
Key process:
Use the Construct Point
component to generate the list of
points
Input:
Examine input using the
Parameter Viewer and Panel
components.
43
Intermediate process:
Need to find all possible
permutations for the coordinates
to create the cube of 216 points
along all 3 axes
Algorithm analysis
44
1- Divide the line at random
locations
2- Orient to the planes at
locations (line normal to planes)
3- Create the circles (or points
for the profile curve)
4- Select the circles (in order) to
Loft (or Interpolate Curve then
Revolve)
Solution steps
Output:
The surface
Key process:
Use the Loft component to
generate the surface
Input:
Line,
Number of intervals and
Thickness range
45
Problem: the random parameters
are not ordered and hence
produce unordered points. Use
the Sort List component to order
the parameters before feeding
into the Evaluate Curve.
Solution
Construct default GH
matching:
To test the matching, fill the lists
as coordinates to a Construct
Point component and observe
the result.
46
Analysis of GH default
matching:
The last element of shorter lists is
repeated until all lists have the
same length, then elements are
matched by indices
Custom matching:
Use the Repeat component to repeat the elements until match the length of the longest list.
Solution
Algorithm analysis
47
Define the input
L= line geometry on xy-plane
H= height
R= number of runs
J= joint radius
Output:
There are 2 outputs, the
beams as curves (polylines)
and joints as spheres
(surfaces)
Key processes:
Need to create the polylines
for the top, middle, and
bottom beams. Use the
Polyline component with
relevant set of points for each.
Given input:
Four given input: line, number
of runs, height and joint radius
48
Intermediate process #1
Divide the curve with twice the
number of runs. Use Divide
Curve component and
Multiply the number of runs
Intermediate process #2
To create top points, select
every other point from the list
of all divide points, then move
vertically by the height
amount.
Use Cull Pattern component
to select points and Move
component to shift vertically
Intermediate process #3
To create bottom points,
select every other point, in the
invert pattern used to select
top points.
Use Cull Pattern component
to select points (set invert flag
for the pattern input)
Intermediate process #4
To create middle points,
Weave the top and bottom
points.
49
2_5_4: Pearl necklace tutorial
Create a necklace with one big pearl in the middle, and gradually smaller size pearls towards the ends as in the image.
Make number of pearls parametric between 15-25.
Algorithm analysis
Solution steps
Output:
The surfaces
Key process:
Use the Sphere component to
generate the surfaces
Given input:
Necklace curve,
Number of pearls as a parameter
(can be changed by the user)
50
Intermediate process #1:
The Range component creates
equal distances. We need to
change to variable distances and
for that we can use the Graph
Mapper component to control the
spacing.
51
Chapter Three: Advanced Data Structures
This chapter is devoted to the advanced data structure in GH, namely the data trees, and different
ways to generate and manage them. The aim is to start to appreciate when and how to use tree
structures, and best practices to effectively use and manipulate them.
The Parameter Viewer shows each branch address (called “Path”), and the number of elements in
that branch as shown in Figure (52).
52
Figure (52): The Parameter Viewer indicates the path address and the number of elements in each branch
A list of items is typically stored in a tree with one branch. Figure (53). However, the three items
can also be stored in three different branches. Figure (54).
Figure (53): A list is a tree that has one branch with multiple elements
Figure (54): A tree contains any number of branches with any number of elements in each branch
The key to understand Grasshopper data structure is to be able to answer the following question:
What is the significance of storing the 3 numbers in one branch vs 3 branches?
The data structure informs GH components about how to match input values. In other words,
components may process data differently based on their structure. The following example
illustrates how different data structures for the same set of values can affect the result.
53
Figure (55): Organizing same set of value in different data structures affect the output
We will elaborate on data tree matching later, but you can already see that GH components do pay
attention to the data structure and the result can vary considerably based on it. This is one of the
complications inherited in using one unifying data structure in a programming language.
Figure (56): Address of elements include the address of the branch and the index of the element in the branch
Here are a few examples of various trees structures and how they show in the Paramster Viewer
and Panel.
54
Figure (57): Same set of values held in different structures.
Left: 5 trunks (5 trees) with single item in each. Middle: 5 branches out of one trunk (1 tree), and each branch holds a
single item. Right: two trunks (2 trees), the first has 2 branches with the first branching into 3 branches, each holds one
item, the second holds 1 item. The second trunk holds 2 items.
Solution
55
3_2: Generating trees
There are many ways to generate complex data trees. Some explicit, but mostly as a result of
some processes, and this is why you need to always be aware of the data structures of output
before using it as input downstream. It is possible to enter the data and set the data structure
directly inside any Grasshopper parameter. Once set, it is relatively hard to change and therefore is
best suited for a constant input. The following is an example of how to set data tree directly inside a
parameter.
56
There are many components that generate data trees such as Grid and DivideSrf, and others that
combine lists into a tree structure such as Entwine. Also all the components that produce lists can
also create tree if the input is a list. For example, if input more than one curve into the DivideCrv
component, we get a tree of points.
Figure(59): SDivide component takes one input (surface) and outputs a data tree (grid).
All components that generate lists of numbers (such as Range and Series) can also generate
trees when given a list of input.
Figure(60): Entwine component takes any number of lists and combine them into a tree structure.
Perhaps one of the most common cases to generate a tree is when dividing a list of curves to
generate a grid of points. So the input is one list and the output is a tree.
57
Figure(61): Divide component takes any list (curves) and generates a tree structure (grid).
Solution
Discussion: each input point is a single data item that contains 3 numbers (coordinates). We know we would like to
isolate each coordinate into a separate list, then combine them into one data structure. Hence we need to first
deconstruct input points (use Deconstruct of pDecon component), then combine the lists into one structure (use
Entwine component).
58
Match an item with a tree:
Match a shorter list with a tree Match a longer list with a tree
(tree branches longer than the list): (tree branches shorter than the list):
Match 2 trees with same number of branches: Match 2 trees with different number of branches:
59
3_3_1 Tree matching tutorials
Inspect the following 2 number structures, then predict the structure and result of adding them (with default Grasshopper
matching). Verify your answer using Addition components.
Solution
Key solution idea: The two input trees have different number of branches and different number of elements in each
branch. The last branch of the shorter tree is repeated to match the number of branches, then corresponding
branches are matched by repeating the last element of the shorter branch.
60
3_4: Traversing trees
Grasshopper provides components to help extract branches and items from trees. If you have the
path to a branch or to an item, then you can use Branch and Item components. You need to check
the structure of your input so you can supply the correct path.
If you know that your structure might change, or you simply do not want to type the path, you can
extract the path using the Param Viewer and List Item components.
61
Figure (64): Example of how to extract data paths dynamically
Solution
Key solution idea: We can construct a point list using as input 3 lists representing X, Y and Z values. If we can
isolate the 3 branches of the input tree, then we will be able to feed them to the point construction component.
62
important to understand what these operations do, and how they affect the output.
Figure (65): View trees using the Parameter Viewer and the Panel components
Tree information can be extracted using the TStats component. You can extract the list of paths to
all branches, number of elements in each branch and the number of branches.
63
Operations Example of how the list operation apply to trees
List Item
Select items at
specific index in
each branch
List Item
Select multiple
indices to isolate
part of the tree
and perform one
operation on such
as Mass
Addition
Split List
Split the elements
of branches into 2
trees at the
specified index
Shift List
Shifts the
elements of each
branch
64
Cull Pattern
Culls each branch
Figure (67): Grafting a tree create a new branch for each element
It might feel unintuitive to complicate the data structure (from a simple list to a tree), but grafting is
very useful when trying to achieve certain matching. For example if you need to add each element
of one list with all the elements in the second list, then you will need to graft the first list before
inputting to the addition process.
65
Figure (68): Grafting complex trees
Flatten also can handle any complex tree. It takes the branches in order starting with the lowest
index trunk and put all elements in one list.
66
Figure (70): Flattening complex trees
Figure (71): Entwine and Merge components combine lists into trees or bigger lists
67
Figure (72): Flip helps reorganize data trees
If the number of elements in the branches are variable in length, some of the branches in the
flipped tree will have “null” values.
Figure (73): Add “null” when flipping trees with variable length branches
Flipping is one of the operations that cannot handle variable depth branches, simply because there
is no logical solution to flip.
Figure (74): Flip fails when the input tree has variable depth branches
68
5_3_7: Simplifying the data structure
Processing data through multiple components can add unnecessary complexity to the data
structure. The most common form is adding leading or trailing zeros to the paths addresses.
Complex data structures are hard to match. Simplify Tree process helps remove empty branches.
There are other operations such as Clean Tree and Trim Tree to help remove null elements,
empty branches and reduce complexity. It is also possible to extract all branches as separate lists
using Explode Tree operation.
Figure (75): Paths can increase in complexity as more operations are applied to the data. Simplify helps remove empty
branches
Solution
Input curve
Data structure: single item (one
branch and one item in the branch)
69
Create vertical lines at each point.
Data structure: list (one branch with
11 items). Note that the path did not
increase in complexity.
Algorithm analysis
70
For each shutter there are two parts:
the rectangle and the hinge.
Grasshopper implementation
Output
Surface of the shutters
Curves for the frame
Input
4 corner points (and center)
Hinge radius
Rotation parameter
Key processes
71
Intermediate process #1
Intermediate process #2
72
points diagonally. For each point, you connect to another in the +1 branch and +1 index. For
example a point in branch {0}, index [0], connects to the point in branch {1}, index [1].
Figure (76): Relative Item mask {+1}[+1] create positive diagonal connectivity
In Grasshopper, the way you communicate the offset is expressed with an offset string in the
format “{branch offset}[index offset]”. In our example, the string to connect points diagonally is “{+1}
[+1]”. Here is an example that uses relative tree component in Grasshopper. Notice that the relative
item component creates two new trees that correlate in the manner specified in the offset string.
Figure (77): Relative Item mask {+1}[+1] breaks the original tree into 2 new trees with diagonal connectivity
Here is an example implementation in Grasshopper where we define relative items in one tree,
then connect the two resulting trees with lines using the Relative Item component.
73
3_6_1_1 Relative item tutorial #1
Create the pattern shown in the image using a square grid of 7 branches where each branch has11 elements.
Solution
74
Change the offset to {+2}
[+3] to create the second
connections
We showed how to define relative items in one tree, but you can also specify relative items
between 2 trees. You’ll need to pay attention to the data structure of the two input trees and make
sure they are compatible. For example, if you connect each point from the first tree with another
point from a different tree with the same index, but +1 branch, then you can set the offset string to
be {+1}[0].
The input to the Relative Items component is two trees and the output is two trees with
corresponding items according to the offset string.
Figure (80): The offset mask of the Relative Items generates new trees with the desired connections
75
Figure (81): Relative Items implementation in Grasshopper
Solution
Grasshopper definition
76
Cull every other index and keep the
same number of branches. (cull inices
0, 2,...)
Grasshopper definition
{;;} Use curly brackets to enclose the mask for the tree branches.
[] Use square brackets to enclose the mask for the elements (leaves), inside square
brackets. Can omit if select all items or use [*]
* Any number of integers in a path. The asterisk also allows you to include all
branches, no matter what their paths look like
77
6 Any specific integer
(0,2,...) Any integer part of this infinite sequence. Sequences have to be at least two integers
long, and every subsequent integer has to be bigger than the previous one (sorry,
that may be a temporary limitation, don't know yet).
(0,2,...,48) Any integer part of this finite sequence. You can optionally provide a single sequence
limit after the three dots.
!(3,5,...) Any integer not part of this infinite sequence. The sequence doesn't extend to the left,
only towards the right. So this rule would select the numbers 0, 1, 2, 4, 6, 8, 10, 12
and all remaining even numbers.
{ * }[ (0 to 4) or (6,11,41) ] It is possible to combine two or more rules using the boolean and/or operators. The
example selects the first five items in every list of a tree and also the items 7, 12 and
42, then the selection rule
{*} Select all (the whole tree output as positive, and negative tree will be empty)
{ *; (0, 2) }[(1,3,...)] Select elements located at odd indices in the first and third branches
{*; (0,2,...) } [ (1,3,...) ] Select elements located at odd indices in branches located at even indices
{*; (0,2,...) } [(0) or (1,3,...)] Select elements located at odd indices, and index “0”, in branches located at even
indices
One of the common applications that uses split tree functionality is when you have a grid of points
78
that you like to transform a subset of it. When splitting, the structure of the original tree is
preserved, and the elements that are split out are replaced with null. Therefore, when applying
transformation to the split tree, it is easy to recombine back.
Suppose you have a grid with 7 branches and 11 elements in each branch, and you’d like to shift
elements between indices 1-3 and 7-9. You can use the split tree to help isolate the points you
need to move using the mask: {*}[ (1,2,3) or (7,8,9) ], move the positive tree, then recombine back
with the negative tree.
Figure (82): Split tree allows operating on a subset of the tree with the possibility to recombine back
This is the GH definition that does the above using the Split Tree component.
One of the advantages of using Split Tree over relative trees is that the split mask is very versatile
and it is easier to isolate the desired portion of the tree. Also the data structure is preserved across
the negative and positive trees which makes it easy to recombine the elements of the tree after
processing the parts.
79
Solution steps
80
Recombine the middle
part with the rest of the
tree and create
polylines through each
branch elements
Solution
81
Move positive tree vertically
82
Let’s start by familiarizing ourselves with the syntax using built-in mappings inside the Path
Mappers.
In the following example, the input tree has two grids of points (2 trees). The data structure
becomes clear when using a Polyline which creates one polyline through each branch. We will
examine the effect of applying the built-in mapp on the structure and connection of points.
Flatten
Mapping
83
Graft Mapping
Reverse
Mapping
Renumbering
Mapping
84
Solution
To create the vertical connections, you need to create a branch for each 2 corresponding elements across the 2
trees, then use Polyline to connect them
1- Analyze the paths of the trees
2- Come up with a mapping that generates the desired grouping
85
Finally, a Polyline makes the
vertical connections.
Solution
86
The Polyline
component
connects the
elements in
each branch
Regroup the
elements of
corresponding
branches in all
trees using the
Path Mapper
87
Final result
Create all
connections
88
Move every other point in
the Z direction by height
89
Create cross connections
using Flip tree operation for
the bottom and top trees
90
3_7_2: Diagonal triangles tutorial
Given the input grid, use the RelativeItem component to create diagonal triangles
Solution
Algorithm analysis
Grasshopper implementation
91
3_7_3: Zigzag tutorial
Create the structure shown in the image using a base grid as input.
Algorithm analysis
Grasshopper implementation
92
3_7_4: Weaving tutorial
Create flat weaved threads using a rectangular grid as an initial input. Set your desired density and
size. Bonus: Make the weaving go along any surface
Algorithm analysis
Grasshopper implementation
93
Bonus solution
Instead of using the Z-Axis to move points up and down, use the surface normal direction at each point
Note: Make sure the data structure of normals and points match
94