RhinoCommon Using the Geometry Namespace with Python v.1.02
RhinoCommon Using the Geometry Namespace with Python v.1.02
Python
A Guide to Create Rhinoceros3d Geometry in Grasshopper3d by
Writing Python Code.
Python Variables 20
Variables are like labeled containers for storing data values, which we can then use and
modify throughout our code. In architectural scripting, variables allow us to set dynamic
parameters that can easily be adjusted to explore design variations. 20
What Are Variables? 20
Declaring Variables 20
Updating Variables 21
Working with Variables in Expressions 21
Variable Scope 21
Naming Conventions and Best Practices 22
Example: Combining Variables in a Script 22
Summary of Python Variables 23
Operators in Python 23
Arithmetic Operators 24
Comparison Operators 24
Logical Operators 25
Bitwise Operators 25
Assignment Operators 26
Identity Operators 27
Membership Operators 27
Practical Application: Using Operators in an Architectural Context 28
Summary of Operators in Python 28
Data Collections in Python 28
Lists 29
Tuples 31
Sets 37
Arrays 39
Summary of Data Collections in Python 44
Object-Oriented-Programming 44
Defining a Class 46
Attributes and the __init__ Method 46
Creating an Instance/Object of the Class: 46
Methods 47
Modifying Objects Properties 47
Deleting Objects Properties 47
Deleting Objects 47
Encapsulation 47
Inheritance 48
Polymorphism 48
Classes in RhinoCommon 49
Summary of Object-Oriented Programming 53
Functions in Python 53
Defining and Calling Functions 53
Function Parameters 54
Return Values 54
Passing by reference vs. passing by value 54
Types of functions arguments 55
Default Parameters 57
Functions Annotations 58
Scope and Lifetime of Variables 59
Types of Functions in Python 61
Python Built-in Functions 61
Lambda Functions 61
Recursion Functions 62
Python math Module Functions 64
Summary of Functions in Python 65
Control Flow 66
Conditional Statements 66
Nested Conditional Statements 67
Logical Operators 68
Match-Case Statement 68
Loops 70
for-else Loops 72
While Loops 72
While-else Loop 73
Infinite Loops 74
Nested Loops 74
Loop Control Statements 75
Summary of Control Flow 76
Vector3d Struct 81
Examples of Vector3d Struct CONSTRUCTORS 81
Examples of Vector3d Struct PROPERTIES 81
Examples of Vector3d Struct METHODS 81
Plane Struct 81
Examples of Plane Struct CONSTRUCTORS 81
Examples of Plane Struct PROPERTIES 82
Examples of Plane Struct METHODS 82
Interval Struct 82
Examples of Interval Struct CONSTRUCTORS 82
Examples of Interval Struct PROPERTIES 82
Examples of Interval Struct METHODS 82
Line Struct 82
Examples of Line Struct CONSTRUCTORS 82
Examples of Line Struct PROPERTIES 83
Examples of Line Struct METHODS 83
Box Struct 84
Examples of Box Struct CONSTRUCTORS 84
Examples of Box Struct PROPERTIES 84
Examples of Box Struct METHODS 84
Sphere Struct 84
Examples of Sphere Struct CONSTRUCTORS 84
Examples of Sphere Struct PROPERTIES 84
Examples of Sphere Struct METHODS 85
Curve 85
Examples of Curve Class CONSTRUCTORS 85
Examples of Curve Class PROPERTIES 85
Examples of Curve Class METHODS 86
LineCurve Class 87
Examples of LineCurve Class CONSTRUCTORS 87
Examples of LineCurve Class PROPERTIES 88
NurbsCurve Class 88
Examples of NurbsCurve Class CONSTRUCTORS 88
Examples of NurbsCurve Class PROPERTIES 88
Examples of NurbsCurve Class METHODS 88
Surface Class 88
Examples of Surface Class METHODS 89
NurbsSurface Class 89
Examples of NurbsSurface Class CONSTRUCTORS 89
Examples of NurbsSurface Class METHODS 90
Brep Class 90
Examples of Brep Class PROPERTIES 90
Examples of Brep Class METHODS 91
Polyline Class 92
Examples of Polyline Class CONSTRUCTORS 93
Examples of Polyline Class PROPERTIES 93
Examples of Polyline Class METHODS 93
PolylineCurve Class 93
Examples of PolylineCurve Class CONSTRUCTORS 93
Triangle3d Struct 93
Examples of Triangle3d Struct CONSTRUCTORS 93
Examples of Triangle3d Struct PROPERTIES 94
Examples of Triangle3d Struct METHODS 94
Arc Struct 94
Examples of Arc Struct CONSTRUCTORS 94
Examples of Arc Struct PROPERTIES 94
Examples of Arc Struct METHODS 94
ArcCurve Class 95
Examples of ArcCurve Class CONSTRUCTORS 95
Examples of ArcCurve Class PROPERTIES 95
Rectangle3d Struct 95
Examples of Rectangle3d Struct CONSTRUCTORS 95
Examples of Rectangle3d Struct PROPERTIES 95
Examples of Rectangle3d Struct METHODS 95
Circle Struct 96
Examples of Circle Struct CONSTRUCTORS 96
Examples of Circle Struct PROPERTIES 96
Examples of Circle Struct METHODS 96
Mesh Class 96
Examples of Mesh Class PROPERTIES 96
Examples of Mesh Class METHODS 96
Transform Struct 97
Examples of Transform Struct METHODS 97
Intersection Class 98
Examples of Intersection Class METHODS 98
IntersectionEvent Class 98
Examples of IntersectionEvent Class PROPERTIES 99
Examples of IntersectionEvent Class METHODS 99
Preface
Welcome to this comprehensive guide on using GhPython and the Rhino.Geometry
namespace within the Grasshopper3D for Rhinoceros3D environment. This book is written
to guide students of architecture concentration at Ain Shams University with their ‘Principles
of Parametric Design’ course (Fall – 2024) which exhibits a set of lectures about coding
machine learning projects in Python. So, I thought this book would be the best way to guide
the students through the coding learning process.
Later on, I decided to design the book so that it could help architects, designers, and
computational enthusiasts explore and leverage the power of Python scripting within Rhino
and Grasshopper to create advanced parametric designs and automate complex modeling
tasks.
In the world of architectural design and computational modeling, the ability to script custom
behaviors and build dynamic workflows can significantly elevate a designer's capabilities.
GhPython, an add-on for Grasshopper3D, opens up a world of possibilities by allowing users
to script within Grasshopper’s visual programming interface. By integrating Python with
Rhino and Grasshopper, designers can enhance their efficiency, flexibility, and creativity
through tailored scripts and custom components.
This book is tailored for anyone interested in advancing their computational design skills
using Python within Rhino and Grasshopper. Whether you're an architect, designer, engineer,
or student, this book will provide you with a solid foundation in using GhPython for
parametric design. Familiarity with Rhinoceros3D, Grasshopper3d and a basic
understanding of Python programming will be helpful, though each concept is explained
from the ground up.
This book is structured to guide you from the basics of GhPython to advanced topics,
covering the essential concepts and tools needed to work fluently with the Rhino.Geometry
namespace. By following the structure, you’ll gain insight into:
Getting Started with GhPython: Learn where to find and how to set up the GhPython
component within Grasshopper. Understand the structure of inputs and outputs, configure
data types, and use the script editor effectively.
points, vectors, planes, and complex geometric forms, each illustrated with practical
examples.
Classes, Structs, and Enums: Understand the distinctions and applications of classes,
structs, and enums within the Rhino.Geometry library. These data structures provide the
foundation for creating modular, reusable, and readable code in your design workflows.
Practical Examples and Applications: The book includes numerous examples and exercises
demonstrating how to construct points, manipulate vectors, work with planes, define
intervals, and model lines and boxes. Each example is geared towards common tasks in
computational design, showing both the syntax and logic of using Rhino.Geometry.
Advanced Scripting Techniques: As you progress, you'll delve into advanced topics, such as
using custom data types, working with complex structures, and optimizing your scripts for
large-scale parametric models.
Python has become one of the most popular programming languages due to its readability,
versatility, and a vast range of libraries. When paired with Rhino and Grasshopper, Python
scripting transforms from general-purpose programming to a powerful tool for
computational design, providing access to Rhino’s extensive modeling capabilities. The
Rhino.Geometry namespace allows designers to harness this power in a structured and
efficient way, making it possible to create intricate designs, automate repetitive tasks, and
expand Grasshopper's functionality beyond visual programming.
This book is designed as both a tutorial and a reference. You can follow it linearly to build a
complete understanding of GhPython and Rhino.Geometry, or you can dive into specific
chapters and examples to quickly reference particular functions, methods, or structures.
By the end of this book, you’ll be equipped with the skills and confidence to integrate Python
scripting into your design practice effectively. Whether you aim to build custom Grasshopper
components, create sophisticated algorithms for form generation, or automate complex
workflows, GhPython and Rhino.Geometry will open new horizons in computational design.
Let’s dive in and start scripting the future of architecture and design!
3 Introduction
Introduction
In today’s rapidly evolving architectural landscape, the integration of technology into design
practices has become essential. Among the myriad of tools available, Python has emerged
as a powerful programming language that architects can leverage to enhance their
computational design capabilities. Known for its simplicity and versatility, Python allows
architects to automate repetitive tasks, manipulate complex geometries, and develop
custom algorithms, significantly streamlining the design process.
One of the most significant advantages of coding in Python is its seamless integration with
Grasshopper3d, a visual programming language widely used in conjunction with Rhinoceros
3D. Grasshopper3d's node-based interface allows architects to create complex geometric
forms through a visual approach, but when combined with Python scripting, the potential for
customization and functionality is greatly expanded. Python scripts can be used to define
unique functions, process data more efficiently, and execute advanced mathematical
calculations that go beyond the native capabilities of Grasshopper components.
Moreover, the use of Python in Grasshopper3d fosters a more iterative and experimental
design process. Architects can quickly prototype their ideas, modify algorithms, and
visualize the outcomes in real time, leading to a more dynamic interaction with their design
models. This adaptability encourages innovation and exploration, essential qualities in a
field that thrives on creativity.
In conclusion, learning Python not only enhances an architect’s technical skill set but also
enriches the overall design experience. By embracing coding as a fundamental aspect of
computational design, architects can unlock new dimensions of creativity, ultimately
leading to more sophisticated and responsive architectural solutions. As the field continues
to evolve, the synergy between Python programming and Grasshopper modeling will
undoubtedly play a pivotal role in shaping the future of architectural design.
4 Introduction To Python
Introduction To Python
Python is a versatile and beginner-friendly programming language that has made waves in
architecture due to its simplicity and power. For architects, Python unlocks the potential to
automate tasks, create complex geometry, and experiment with generative design—all
without needing to be full-time programmers. In this book, we’ll explore Python’s integration
with Grasshopper and RhinoCommon to build custom tools that can enhance your design
workflow and push creative boundaries.
In this chapter, we’ll set up Python for use within Grasshopper, exploring how to install
necessary plugins and navigate the Python Editor in Grasshopper. You’ll learn the basics of
writing and executing Python code, and we’ll discuss how Python’s syntax and structure
differ from visual programming, making it a valuable addition to an architect's toolkit.
By the end of this chapter, you’ll have a solid foundation to start coding in Python and a clear
understanding of how it can benefit your architectural practice.
For architects, integration of coding with modeling tools opens the door to parametric
design—where design elements are controlled through parameters rather than static
drawings. When combined with Grasshopper and RhinoCommon, Python becomes a
powerful tool that lets you move beyond visual programming into scripting, giving you finer
control over your designs and allowing for more complex operations that visual components
may not easily support.
• Power: Python can help perform large calculations and data manipulations quickly,
with full control over the algorithm you are building using its power control flow tools.
• Complex Geometry: With Python, you can create and manipulate 3D geometry and
develop forms that respond to various parameters and constraints.
This book focuses on using Python for architectural design in Grasshopper, Rhino’s visual
scripting tool, alongside RhinoCommon, Rhino’s API (Application Programming Interface).
We’ll explore fundamental Python concepts and apply them in architectural contexts,
progressively moving from basic coding to more advanced design applications.
Ensure You Have Rhino and Grasshopper: If you don’t already have Rhino installed, you can
download it from Rhino’s website. Grasshopper comes bundled with Rhino 6 and later
versions.
GhPython Component
Rhino uses a plugin called GhPython to enable Python scripting within Grasshopper. To
check if it’s installed, open Grasshopper and look for a Python Script component under the
Maths tab and under the Script category.
GhPython Grasshopper3d component can be found under the Math tab -> Script
components.
Once added, the component will have an orange color indicating a warning that it has no
code to execute yet.
6 GhPython Script Editor
The left part of the component has the inputs and the right part has the outputs as usual
grasshopper3d components.
Right click the inputs to decide whether they contain a single item, a list of item, or a
grasshopper3d data tree.
You can choose the input’s data type by hovering over ‘Type hint’ and choose the right data
type. This step is crucial for your code to run without any type errors.
7 GhPython Script Editor
In GhPython componet, the output “out” gives either the result of any ‘print’ function or
errors statements while the ‘a’ output is the resulting variable from your code.
You can add or remove as many inputs and outputs as you wish. Just zoom in till the small
‘+‘ and ‘–‘ icons appear.
Additionally, you can right click any input or output to rename it.
8 GhPython Script Editor
The ‘as rs’ part assigns an alias, rs, to the rhinoscriptsyntax module. This makes the code
easier to write and read by shortening the module name. So that to make a point for example,
instead of typing rhinoscriptsyntax.AddPoint(0, 0, 0) you can type: rs.AddPoint(0,0,0) and the
code will construct a point object inside Rhino3d viewport.
Write the code and click either “Test” to run the code or “OK” to run the code and close the
script editor.
After finishing your code, you can either save a ‘.gh’ file by clicking on file -> save which will
Test and then save the whole grasshopper3d definition or export the code written inside the
editor to a ‘.py’ file extension which is the extension of python codes. You can import the code
later by choosing ‘Import from’ under file.
10 GhPython Script Editor
If you need to find or search for any item/text inside your code, use find and replace and the
Tools tab.
This will open the ‘Find and Replace’ dialogue box where you can type the text to find and the
text to replace with and then click either find or replace. You can check replace all to replace
all items with one hit.
Additionally, you can tick Match Whole Word under ‘Options’ part to match the text written
in the Find box as a whole or tick Match Case to match the text letters’ cases while searching.
11 GhPython Script Editor
The script editor gives wide optiosn to choose from for the formatting of code. By choosing
‘Font Options’ under ‘Tools’ tab.
You can change the size, style, and font of the code through the ‘Font Options’ dialog box.
12 GhPython Script Editor
The output part in the code editor show if the code has any errors.
If you have errors in your code, the error and its type will show with the line number where
the error exists.
13 GhPython Script Editor
While writing your code, the ‘Help’ part will show information about constructors, properties,
and methods used with the class/struct/enum you are using which can help in recognizing
what to add easily.
14 GhPython Script Editor
In this example, we’ll use Python to create a line between two points. Follow these steps to
try it out:
Double-click the component to open the Python Editor and type the following code inside
the editor:
import rhinoscriptsyntax as rs
# Define two points
start_point = rs.AddPoint(0, 0, 0)
end_point = rs.AddPoint(10, 0, 0)
# Create a line between the points
line = rs.AddLine(start_point, end_point)
a = line
Run the Code: Click “OK” in the Python Editor to run the script. If successful, a line should
appear in your Rhino workspace between the coordinates (0,0,0) and (10,0,0).
In this script:
This example demonstrates how Python can quickly generate geometry in Rhino, and in later
chapters, we’ll build on this foundation to create more complex forms and systems.
• Python Fundamentals: We’ll start by learning the basic building blocks of Python—
data types, variables, and data collections—so you can understand how information
is stored and manipulated.
• Object-Oriented Programming (OOP): In OOP, we’ll learn how classes are a useful
design pattern which allows for organized and reusable code structures to gain
knowledge of how to deal with RhinoCommon API which includes many classes.
15 GhPython Script Editor
• Functions and Control Flow: You’ll learn to write reusable code blocks (functions)
and control the sequence of actions (control flow), enabling responsive, adaptive
design scripts.
• RhinoCommon: We’ll introduce how to use different RhinoCommon Geometry
namespace’s classes, structs, and enums.
• Architectural Applications: Throughout the book, each coding concept will be
illustrated with architectural examples. We’ll build real-world applications like
parametric facades to solidify your understanding of Python in the architectural
context.
• Experiment and Explore: Don’t be afraid to modify the examples. Try changing
parameters, testing different approaches, and observing how they affect the output
in Rhino.
• Understand the Basics First: Mastering the basics of Python will make more
advanced concepts much easier to grasp. Spend time practicing data types,
variables, and control flow.
• Seek Out Resources: Python has a large online community and many resources. If
you run into trouble, sites like Stack Overflow and Rhino’s forums are invaluable as
well as different large language models which could write code including ChatGPT,
Gemini, etc.
• Take Breaks: Coding can be challenging, especially when you’re starting out. If you
feel stuck, take a break and revisit the problem later with a fresh perspective.
By the end of this book, you’ll be well-equipped to use Python in your design work, creating
custom tools, automating workflows, and exploring new design possibilities with parametric
scripting.
In Python, a data type defines the kind of value a variable can hold. Think of data types as
categories of information—numbers, text, or logical conditions—that Python can work with.
In architectural coding, data types allow us to store and manipulate information like
16 Python Data Types
dimensions, material properties, labels, points, curves, surfaces, and conditions that guide
design decisions. Understanding data types is essential, as they form the building blocks for
all the operations and calculations we’ll perform in Python.
In this section, we’ll explore the most common data types in Python:
Each data type will be illustrated with architectural examples, so you can see how these
types apply in practical scenarios.
Integers
Example:
floor_count = 5
Floats
A float, or floating-point number, is a number with a decimal point, allowing for more
precision. Floats are ideal for representing measurements like wall thickness, room
dimensions, and material properties that may not be whole numbers.
wall_thickness = 0.25
Let’s create a simple Python script that uses integers and floats to store and calculate basic
room dimensions.
17 Python Data Types
# Room dimensions
room_length = 5.5 # length in meters (float)
room_width = 4 # width in meters (integer)
room_height = 3.0 # height in meters (float)
# Calculating room volume
room_volume = room_length * room_width * room_height
print("The room volume is:", room_volume, "cubic meters")
In this script we define variables for the room’s length, width, and height, using both integers
and floats.
We calculate the volume by multiplying these values, illustrating how Python can handle
mathematical operations with numeric data types.
Strings
A string is a sequence of characters, used for storing text. Strings are essential in
architectural coding for labeling elements, organizing metadata, and categorizing items
within a design model. In Python, strings are enclosed in either single quotes ('. ..') or double
quotes ("...").
Strings are especially helpful for assigning names or descriptions to different architectural
elements. For instance, you might label rooms, categorize materials, or specify design
stages with strings.
String Operations
Python provides several operations for working with strings, such as concatenation
(combining strings) and slicing (extracting parts of strings). These operations make it easy to
organize and manipulate text-based information in your designs.
material = "Concrete"
strength = "High Strength"
material_description = material + " - " + strength
print(material_description) # Output: "Concrete - High Strength"
String Formatting: Embed variables within a string using f-strings, which make it easy to
construct informative messages.
18 Python Data Types
width = 4.5
message = "The width of the room is {} meters".format(width)
print(message) # Output: "The width of the room is 4.5 meters"
# Room information
room_length = 5.5
room_width = 4
room_height = 3.0
room_name = "Kitchen"
material = "Granite"
dimensions = f"{room_length}m x {room_width}m x {room_height}m"
# Constructing a descriptive message
description = f"The {room_name} has dimensions {dimensions} and is finished with {material}."
print(description)
In this example, we use strings to label the room and its material. An f-string combines text
and variables to produce a descriptive message.
Booleans
Booleans are data types that can only hold two values: True or False. Booleans are
particularly useful for setting conditions and controlling the flow of your code. In
architectural coding, booleans can determine whether an element is included in a design or
if certain criteria are met.
Imagine a scenario where you want to determine if a room is accessible based on its door
width. You can use a boolean to set this condition.
is_accessible = True
is_south_facing = True
In the following example, we’ll use booleans to check if a room’s width meets the minimum
accessibility requirement.
In this script, we set is_accessible to True if the room width meets or exceeds the minimum
requirement, and False otherwise. Booleans can be dynamically updated based on values
in your design, providing flexibility for setting conditional rules.
Python automatically converts one data type to another without the need for explicit
instruction. This usually happens when combining different types of data in expressions. For
example, when an integer and a float are added together, Python will automatically convert
the integer to a float to perform the operation.
a = 5 # int
b = 2.5 # float
result = a + b # Implicit conversion of int to float
print(result) # Output: 7.5
print(type(result)) # Output: <class 'float'>
This is when you explicitly convert one data type to another using built-in functions.
Common functions for type casting include int(), float(), str(), tuple() and list(). You typically
use explicit casting when you need to ensure that your data is in a specific format for further
processing or calculations.
a = "10" # str
b = int(a) # Explicit conversion from string to integer
print(b) # Output: 10
print(type(b)) # Output: <class 'int'>
• Integers: Whole numbers, useful for counts or discrete values (e.g., number of floors).
• Floats: Decimal numbers, ideal for precise measurements (e.g., wall thickness).
• Strings: Text sequences, perfect for labels and identifiers (e.g., room names).
20 Python Variables
• Booleans: Logical values (True or False), used for conditional design rules.
Understanding these data types and how to convert between them is crucial as we’ll use
them to manage, organize, and manipulate data throughout our architectural scripts. With a
solid grasp of these foundational elements, you’re now ready to start using variables in
Python, where we’ll explore how to store, update, and interact with data in flexible ways.
Python Variables
Variables are like labeled containers for storing data values, which we can then use and
modify throughout our code. In architectural scripting, variables allow us to set dynamic
parameters that can easily be adjusted to explore design variations.
Declaring Variables
Declaring a variable in Python is simple. You only need to choose a name for the variable and
assign it a value using the = symbol. Python automatically infers the data type based on the
assigned value.
In this example, room_height is a variable holding a decimal value (float) representing the
height of a room. room_name stores a text value (string) for the room label. floor_count holds
a whole number (integer) representing the number of floors. And is_occupied is a boolean
variable indicating whether the room is currently occupied.
Tip: In Python, variable names should be descriptive, written in lowercase with underscores
between words (e.g., room_height). This naming convention, called “snake_case,” helps
make your code more readable.
21 Python Variables
Updating Variables
One of the powerful features of variables is that they can be updated dynamically, which is
helpful in architectural design as you modify and test different parameters.
In this script, we initially set room_width to 5.0. Later, we update room_width to 6.5. Python
allows variable values to be changed at any point, giving you flexibility as your design
parameters evolve.
Let’s calculate the area of a wall using two variables for width and height.
wall_width = 4.0
wall_height = 3.0
wall_area = wall_width * wall_height
print("Wall area is:", wall_area, "square meters")
This approach lets us quickly adjust dimensions and recalculate area as needed by simply
updating the values of wall_width or wall_height.
Variable Scope
Scope refers to where a variable is accessible in your code. Python uses indentation to
define sections of code, which also determines the scope of variables. There are two main
types of scope:
• Global Scope: Variables defined outside any function are accessible throughout the
code.
• Local Scope: Variables defined within a function are only accessible within that
function.
22 Python Variables
Understanding scope is essential for managing variables in more complex scripts, especially
as you begin writing functions and larger programs.
# Global variable
building_name = "Main Office"
def print_room_info():
# Local variable
room_name = "Conference Room"
print("Building:", building_name)
print("Room:", room_name)
print_room_info()
print(building_name) # This works
# print(room_name) # This would produce an error because room_name is local
In this example, building_name is a global variable and can be accessed anywhere while
room_name is a local variable defined inside print_room_info() and is only accessible within
that function.
• Use Descriptive Names: Choose names that clearly describe the purpose of the
variable (e.g., window_height instead of just h).
• Avoid Keywords: Don’t use Python’s reserved keywords (like print, for, in, int, float,
def) as variable names.
• Follow Naming Conventions: Use snake_case for variable names (wall_width), and
reserve capitalized words for constants (MAX_HEIGHT).
• Avoid Using Magic Numbers: Instead of using arbitrary values directly in
expressions, store them in variables with descriptive names. This practice makes
your code more understandable and easier to update.
This makes the code clear, as each variable’s name directly suggests its purpose.
By using variables, we can easily update the building’s properties and recalculate values
without modifying each line individually.
• Variables are containers for values, helping manage data efficiently in your code.
• Declaring Variables involves giving a name and assigning a value, which Python
automatically categorizes by data type.
• Updating Variables allows for dynamic code, letting you adjust values without
rewriting expressions.
• Using Variables in Expressions supports complex calculations and relationships
between data.
• Scope is crucial for managing where a variable can be accessed.
• Naming Conventions keep your code clean and understandable.
Understanding variables is a vital step toward building more sophisticated scripts that
control and adapt design data. Next, we’ll dive into operators in Python, where we’ll learn
how to operate on your declared variables.
Operators in Python
Operators are special symbols in Python that perform operations on one, two, or three
operands, and they form the foundation of expressions in your code. Understanding
operators is essential for performing calculations, making comparisons, and manipulating
data, all of which are crucial in architectural scripting and design automation. In this section,
we will cover the following types of operators:
24 Operators in Python
• Arithmetic Operators
• Comparison Operators
• Logical Operators
• Bitwise Operators
• Assignment Operators
• Identity Operators
• Membership Operators
Arithmetic Operators
Arithmetic operators are used to perform mathematical calculations. The basic arithmetic
operators in Python include:
Operator Function
+ Addition
- Subtraction
* Multiplication
/ Devision (returns a float)
// Floor Devision (returns an integer)
% Modulus (returns the remainder)
** Exponentiation
length = 5
width = 3
area = length * width
perimeter = 2 * (length + width)
print("Area:", area) # Output: Area: 15
print("Perimeter:", perimeter) # Output: Perimeter: 16
In this example, we calculate the area and perimeter of a rectangle using arithmetic
operators.
Comparison Operators
Comparison operators are used to compare two values. The result of a comparison is a
boolean value (True or False). The common comparison operators are:
Operator Function
== Equal to
!= Not equal to
> Greater than
< Less than
25 Operators in Python
a = 10
b = 15
print(a == b) # Output: False
print(a < b) # Output: True
print(a != b) # Output: True
In this example, various comparison operators are used to compare two numerical values.
Logical Operators
Logical operators combine conditional statements and return boolean results. The primary
logical operators are:
Operator Function
and Returns True if both conditions are true.
or Returns True if at least one condition is true.
not Reverses the boolean value of the condition.
Example: Using Logical Operators
length = 5
width = 10
if length > 0 and width > 0:
print("Dimensions are valid.")
if not (length == 0 or width == 0):
print("Room dimensions are not zero.")
Bitwise Operators
Bitwise operators perform operations on binary representations of integers. The primary
bitwise operators are:
Operator Function
& Bitwise AND
| Bitwise Or
^ Bitwise XOR
~ Bitwise NOT
<< Left Shift
>> Right Shift
26 Operators in Python
x = 5 # 0101 in binary
y = 3 # 0011 in binary
print(x & y) # Output: 1 (0001)
print(x | y) # Output: 7 (0111)
print(x ^ y) # Output: 6 (0110)
Assignment Operators
Assignment operators are used to assign values to variables. The basic assignment operator
is =, but there are also compound assignment operators:
Operator Function
+= Add and assign
-= Subtract and assign
*= Multiply and assign
/= Divide and assign
%= Modulus and assign
Example: Using Assignment Operators
27 Operators in Python
area = 0
length = 5
width = 3
area += length * width # Equivalent to area = area + (length * width)
print("Area after assignment:", area) # Output: Area after assignment: 15
In this example, the += operator adds the calculated area to the existing area value.
Identity Operators
Identity operators are used to check if two variables point to the same object in memory. The
two identity operators are:
Operator Function
is Returns True if both variables point to the same object.
is not Returns True if both variables do not point to the same object.
Example: Using Identity Operators
a = [1, 2, 3]
b = a # b points to the same list as a
print(a is b) # Output: True
print(a is not b) # Output: False
b = a.copy() # b now points to a new list with the same values
print(a is b) # Output: False
In this example, we see how identity operators can determine whether two variables refer to
the same object.
Membership Operators
Membership operators are used to test if a value is present in a sequence, such as a list,
tuple, or string. The two membership operators are:
Operator Function
in Returns True if the value is found in the sequence
not in Returns True if the value is not found in the sequence
Example: Using Membership Operators
In this example, membership operators check if certain room types exist in the room_types
list.
28 Operators in Python
lengths = [4, 5, 6]
widths = [3, 4, 2]
areas = []
# Calculate areas of rooms
for length, width in zip(lengths, widths):
areas.append(length * width)
# Check if any room exceeds a certain area
threshold = 20
for area in areas:
if area > threshold:
print(f"Room area of {area} exceeds the threshold.")
In this example, We calculate the area for multiple rooms and store them in the areas list.
We then check if any room exceeds a specified area threshold using comparison operators.
Understanding and effectively using operators is key to creating dynamic and responsive
scripts in architectural programming. They form the building blocks for expressions that drive
your calculations and logical flows in Python.
• Lists: Lists are ordered collections, great for storing sets of related data, like points
or dimensions. Example: floor_heights = [3.0, 3.5, 4.0] for storing the heights of each
floor in a building.
29 Data Collections in Python
• Tuples: Tuples are similar to lists but are immutable (can’t be modified after
creation). These are helpful for fixed data that shouldn’t change, like origin points in a
model. Example: origin = (0, 0, 0)
• Dictionaries: Dictionaries store data as key-value pairs, useful for attribute-based
organization, like storing material properties for different elements. Example:
material_properties = {"concrete": {"density": 2400, "color": "gray"}}
• Sets: Sets are unordered collections of unique items, helpful for ensuring elements
like point coordinates aren’t repeated. Example:
unique_materials = {"concrete", "steel", "glass"}.
In this section, we’ll dive into each of these collections and explore how they can be applied
in an architectural context.
Lists
A list is an ordered collection of items that can be of any type (integers, floats, strings, even
other lists). Lists are mutable, meaning you can modify them after they’re created. Lists are
ideal for storing items in a sequence, such as a series of measurements or room names.
Creating a List
Example:
In this example, room_heights is a list of numerical values representing the heights of various
rooms while room_names is a list of strings, each representing a room name.
You can access list items by their index (position) and modify them as needed. Indexing in
Python starts at 0, so the first item is at index 0. Additionally, you can access a sublist by
adding a range the sublist method picks objects from i to j-1.
You can add items to a list using the append(), and insert() method, and remove them with
remove(), pop(), or del list[].
# Adding an item
room_names.append("Office")
print(room_names) # Output: ["Kitchen", "Living Room", "Bathroom", "Bedroom", "Office"]
room_names.insert(1, “Mudroom”)
print(room_names) # Output: ["Kitchen", " Mudroom ", "Living Room", "Bathroom", "Bedroom", "Office"]
# Removing an item
room_names.remove("Bathroom")
# or use
room_names.pop(2)
# or use
del room_names[2]
print(room_names) # Output: ["Kitchen", "Living Room", "Bedroom", "Office"]
List Comprehension
For ease of code writing, lists could be used with loops in one line.
Joining Lists
You can join lists to give a new list with same objects of all joined lists.
L1 = [10,20,30,40]
L2 = ['one', 'two', 'three', 'four']
L3 = L1+L2
print("Joined list:", L3)
# Or modify list 1 to have list 2’s objects
L1+=L2
print("Joined list:", L1)
31 Data Collections in Python
Lists Methods
• list.append(obj)
• list.clear( )
• list.copy( )
• list.count(obj)
• list.extend(seq)
• list.index(obj)
• list.insert(index, obj)
• list.pop(obj=list[-1])
• list.remove(obj)
• list.reverse( )
• list.sort([func], reverse)
Tuples
A tuple is similar to a list but immutable, meaning it cannot be modified after creation. Tuples
are useful for fixed collections of items, like dimensions that don’t change (e.g., standard
room sizes).
Creating a Tuple
Tuples are immutable, meaning that it does not support adding or removing items. A
workaround is to cast a tuple into a list, update the list, and then cast it back to a tuple.
32 Data Collections in Python
Unpacking tuples
tup1 = (10,20,30)
x, y, z = tup1
print ("x: ", x, "y: ", "z: ",z) #Output: x: 10 y: 20 z: 30
tup1 = (10,20,30)
x, *y = tup1
print ("x: ", x, "y: ", y) #Output: x: 10 y: [20,30]
Looping tuples
# Example 1:
tup1 = (25, 12, 10, -21, 10, 100)
for num in tup1:
print (num, end = ' ')
# Example 2:
tup1 = (25, 12, 10, -21, 10, 100)
indices = range(len(tup1))
for i in indices:
print ("tup1[{}]: ".format(i), tup1[i])
Joining tuples
tup1 = (10,20,30)
tup2 = (30,40,50)
tup3 = tup1+tup2
print (tup3) #Output: (10,20,30,30,40,50)
33 Data Collections in Python
tup1 = (10,20,30,40,30,30,60)
index_of_30 = tup1.index(30) #Output: 2 (which is the first index of the first 30)
tup1 = (10,20,30,40,30,30,60)
c = tup1.count(30) #Output: 3 (object 30 was repeated 3 times in the tuple)
4.4 Dictionaries
A dictionary is a collection of key-value pairs, where each key maps to a value. Dictionaries
are excellent for storing related information with identifiers, like a room name and its
dimensions. In a dictionary, keys should be immutable data types (numbers, string, or
tuples) while values could be of any data type.
Creating a Dictionary
To create a dictionary, use curly braces {} and separate keys and values with colons.
room_dimensions = {
"Kitchen": (4.0, 3.0),
"Living Room": (6.0, 3.0),
"Bedroom": (5.0, 3.5)
}
#Creating a dict from a list of tuples or a tuple of tuples
d1=dict([('a', 100), ('b', 200)])
d2 = dict((('a', 'one'), ('b', 'two’)))
#Creating a dictionary from Keyword Arguments
d1=dict(a= 100, b=200)
d2 = dict(a='one', b='two')
In this example, each room name (e.g., "Kitchen") is a key, and its dimensions (width, height)
are values stored as tuples.
However, this dict[key] access function raises an error if the key is not valid. Another access
function could be used which is dict.get(key) which returns None if the key is not valid and
and argument could be added to be returned in this case.
dict["key"] = val
room_dimensions["Kitchen"] = (4.5, 3.0)
d1.update(d2)
d1.update(k1=v1, k2=v2)
d3 = {**d1, **d2}
Dictionaries Operators
Python provides a set of operators for manipulating dictionaries including union “|”,
intersection “&”, difference “-“ and symmetric difference “^”. These operators are used with
both dictionaries and sets although the union operator is the one that is mostly used with
dictionaries.
35 Data Collections in Python
Removing Items
del dict['key’]
del dict
item = dict.pop(key) # Remove and return the item with the key
dict.clear()
obj = dict.items()
Obj = dict.keys()
Obj = dict.values()
l = len(dict)
for i in range(l):
print(list(dict.keys())[i])
print(list(dict.values())[i])
Copying dictionaries
Dictionaries are mutable, meaning they refer to the same ‘id’ if modified, or assigned to each
other.
To make a shallow copy of a dictionary, you can use the .copy() method.
36 Data Collections in Python
#Assigning
d1 = {"a":11, "b":22, "c":33}
d2 = d1
d1["b"] = 100
print ("id:", id(d1), "dict: ",d1)
print ("id:", id(d2), "dict: ",d2) #returns the same id as d1
Nested dictionaries
You can create nested dictionaries meaning that a dictionary becomes a dictionary of
dictionaries rather than a dictionary of items.
rooms = {
"Bedroom" : {"Width" : 4, "Length" : 4.5},
"Kitchen" : {" Width " : 3, " Length " : 3.5},
"Bathroom" : {" Width " : 2, " Length " : 3}
}
print(rooms.get(“Bedroom”[“Width”])
obj = rooms[“Bedroom”]
print(obj.get(“Width”))
print(rooms[“Bedroom”].get(“Length”)
Dictionaries Methods
• clear( )
• copy( )
• fromkeys( )
• get(key, default=None)
• has_key(key )
• items( )
• keys( )
• values( )
• pop( )
• popitem( )
• setdefault(key, default=None)
• update(dict2)
37 Data Collections in Python
Sets
A set is a mutable unordered collection of unique items, which means it automatically
removes duplicates. Sets are useful for storing unique values, such as a list of distinct
materials used in a design. A set can have different data types inside it.
Creating a Set
Sets are created using curly braces {} or with the set() function.
Sets can only contain immutable (hashable) objects including integers, floats, booleans,
strings, tuples, frozen sets, and bytes.
s2 = {“Yassin", {“Architecture":99}}
print (s2) #Output: unhashable type: 'dict'
Sets are unordered collections of data so items inside them are not indexed.
You can add new items to a set with add(), update(), and union() methods and remove them
with remove(), discard(), pop(), and clear methods.
# Adding items
materials_used.add("Wood") #add one object
materials_used.update(“Wood”, “Brick”) #add multiple objects
materials_used.union([“Wood”, “Brick”]) #add multiple objects in a list
materials_used.union({“Wood”, “Brick”}) #add multiple objects in another set
# Removing items
materials_used.remove("Glass")
materials_used.discard("Glass")
obj = materials_used.pop() #removes a random object
materials_used.clear() #removes all objects
38 Data Collections in Python
Boolean Operations
set3 = set1.union(set2)
set3 = set1 | set2
set1.update(set2)
set3 = set1.difference(set2)
set3 = set1 - set2
set1.difference_update(set2)
set3 = set1.intersection(set2)
set3 = set1 & set2
set1. intersection_update(set2)
set3 = set1.symmetric_difference(set2)
set3 = set1 ^ set2
set1. symmetric_difference_update(set2)
Using Sets to Remove Duplicates
If you have a list with duplicates, converting it to a set removes any duplicate items.
Copying sets
Sets are mutable objects meaning that modifying them will give the same set with the same
id and changing a set affects the copied set. However, you can create a shallow copy using
the .copy() method to create a new object
Sets Methods
• add( )
• clear( )
• copy( )
• difference( )
• difference_update( )
• discard( )
• intersection( )
• intersection_update( )
• isdisjoint( )
• issubset( )
• pop( )
• remove( )
• symmetric_difference( )
• symmetric_difference_update( )
• union( )
• update( )
Arrays
An array is a data structure that can hold multiple values of the same data type in a single
variable. Unlike lists, which can contain a mix of data types, arrays are optimized for
numerical and homogeneous data types, making them more memory-efficient and faster for
certain operations. Arrays are commonly used in computational design and architectural
calculations that involve large amounts of numeric data, such as coordinates, dimensions,
and geometrical transformations.
While Python doesn’t have a native array type, it provides arrays through the array module.
Additionally, the numpy library offers more powerful array capabilities, allowing for complex
mathematical operations on large datasets. Here, we'll focus on the basics of using arrays
with Python’s built-in array module.
40 Data Collections in Python
Creating Arrays
To use arrays, you need to import the array module. When creating an array, you specify a
type code, which indicates the data type of the array's elements.
Here, we create an array called lengths with integers representing room lengths.
Array elements are accessed using indexing, similar to lists. You can modify an element by
assigning a new value to a specific index.
# Accessing elements
print(lengths[0]) # Output: 4
print(lengths[2:]) #Output: array('i', [6, 7])
# Modifying an element
lengths[1] = 10
print(lengths) # Output: array('i', [4, 10, 6, 7])
In this example, we access the first element and modify the second element of the lengths
array.
Array Methods
Arrays have built-in methods that allow for various operations, such as appending, inserting,
and removing elements.
# Removing an element
lengths.remove(10)
print(lengths) # Output: array('i', [4, 6, 7, 8, 9, 11])
Here, we use append to add a new length, extend to add multiple lengths, and remove to
delete a specific length from the array.
You can use a for loop to iterate through each element of an array, which is particularly useful
for performing calculations on each element.
In this example, we iterate over each length in the lengths array to calculate the area of rooms
with a fixed width.
Copying arrays
Arrays are mutable objects, meaning that modifying an array refers to the same object with
the same id.
42 Data Collections in Python
Reversing an array
Sorting an array
Joining arrays
Although lists are versatile, arrays have distinct advantages in certain scenarios:
Memory Efficiency: Arrays use less memory as they store elements of the same data type.
Homogeneous Data: Arrays are ideal for situations where you know all data will be of the
same type, such as numerical data in architectural models.
However, arrays in Python's array module are more limited than lists in terms of available
methods and flexibility. For advanced numerical work, consider using numpy arrays, which
offer extensive functionality for mathematical operations.
44 Data Collections in Python
Arrays Methods
• array.reverse( )
• array.count(v)
• array.index(v)
• array.fromlist(l)
• array.tofile(f)
import array as arr
f = open('list.txt','wb')
arr.array("i", [10, 20, 30, 40, 50]).tofile(f)
f.close()
• array.fromfile(f, n)
import array as arr
a = arr.array('i', [1, 2, 3, 4, 5])
f = open("list.txt", "rb")
a.fromfile(f, 5)
print (a)
Understanding these data collections will allow you to manage complex datasets, organize
information efficiently, and make your architectural scripts more powerful. Next, we’ll
explore Object-Oriented Programming (OOP), where you’ll learn to create custom data
structures for even greater control over design elements.
Object-Oriented-Programming
Imagine having a database of dogs that you want to keep track of. Hard coding each dog
would take a lot of time and effort if added in the form of variables.
45 Object-Oriented Programming
benyamin_height = 1
benyamin_width = 1.5
benyamin_food = “bones”
roy_height = 0.6
roy_width = 1.2
roy_food = “dry food”
rolex_height = 0.8
rolex_width = 1.4
rolex_food = “meat”
max_height = 1
max_width = 1.5
max_food = “bones”
Now imagine, wanting to express functions that dogs do including moving and eating. Adding
those functions by hard coding would make the code hard to read.
benyamin_height = 1
benyamin_width = 1.5
benyamin_food = “bones”
benyamin_move = benyamin + “1,5,0”
benyamin_eat = prepare_bones + put_bones_in_bowl + benyamins_mouth_open
In Python, OOP is achieved by defining classes. A class serves as a blueprint for creating
objects. Each object created from a class is an instance with its own unique data.
class Dog:
def __init__ (self, name, height, width, food, position):
self.name = name
self.height = height
self.width = width
self.food = food
self.position = position
Now if you want to add a new object you can just call the function and add the attributes.
46 Object-Oriented Programming
print(benyamin.position)
print(benyamin.food)
In this section, we’ll explore the basics of classes, attributes, methods, and how they can be
used to represent architectural entities.
Defining a Class
To create a class, use the class keyword followed by the class name in CamelCase, with the
class definition indented below. By convention, class names start with an uppercase letter.
class Room:
pass # Placeholder, we'll add attributes and methods soon
In this example, Room is an empty class. The pass keyword acts as a placeholder, allowing
the code to run without any errors.
class Room:
def __init__(self, name, width, height):
self.name = name # Attribute for the room's name
self.width = width # Attribute for room width
self.height = height # Attribute for room height
In this example, self represents the instance of the class. It allows us to assign values to the
instance’s attributes.
name, width, and height are passed as parameters to the __init__ method and assigned to
self.name, self.width, and self.height.
Methods
Methods are functions defined within a class that perform actions using the object’s
attributes. In our Room example, a useful method might calculate the room’s area.
class Room:
def __init__(self, name, width, height):
self.name = name
self.width = width
self.height = height
def area(self):
return self.width * self.height # Calculates area
Here, area is a method that uses self.width and self.height to calculate and return the room’s
area.
Deleting Objects
del living_room
Encapsulation
Encapsulation is a core principle of OOP, meaning that each object manages its own data.
By keeping attributes private (or protected), you control how data is accessed and modified.
This ensures data integrity and hides the complexity of the implementation.
You can make an attribute private by prefixing it with an underscore (_), signaling that it
should not be accessed directly outside the class.
48 Object-Oriented Programming
class Room:
def __init__(self, name, width, height):
self.name = name
self._width = width # Private attribute
self._height = height # Private attribute
def area(self):
return self._width * self._height
While Python doesn’t enforce strict privacy, prefixing attributes with an underscore is a
convention that reminds you and others not to access these attributes directly.
Inheritance
Inheritance allows a class to inherit attributes and methods from another class, promoting
code reuse. For example, we might have a base class BuildingComponent with general
attributes, and create a Room class that inherits from it.
class BuildingComponent:
def __init__(self, name):
self.name = name
class Room(BuildingComponent):
def __init__(self, name, width, height):
super().__init__(name) # Inherit name from BuildingComponent
self.width = width
self.height = height
def area(self):
return self.width * self.height
In this example, BuildingComponent is the base class with a single name attribute.
Room inherits from BuildingComponent, allowing it to reuse the name attribute while adding
its own specific attributes and methods.
Polymorphism
Polymorphism in OOP allows methods to perform different tasks depending on the context.
For example, you might have multiple building components (e.g., Room, Wall, Window),
each with its own way of calculating area.
49 Object-Oriented Programming
class BuildingComponent:
def __init__(self, name):
self.name = name
def area(self):
pass # Base method, to be overridden
class Room(BuildingComponent):
def __init__(self, name, width, height):
super().__init__(name)
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Wall(BuildingComponent):
def __init__(self, name, length, height):
super().__init__(name)
self.length = length
self.height = height
def area(self):
return self.length * self.height
Using Polymorphism:
components = [
Room("Living Room", 6.0, 3.2),
Wall("Partition Wall", 4.0, 2.8)
]
for component in components:
print(f"{component.name} Area:", component.area(), "square meters")
In this example, Each subclass (Room and Wall) has its own area method. We use
polymorphism to call area on each component without needing to know its exact class.
Classes in RhinoCommon
In RhinoCommon, many classes represent geometric entities, such as Point3d, Vector3d,
Curve, Surface, and Mesh. Each of these classes has its attributes and methods specific to
its type.
RhinoCommon is the .NET SDK for Rhinoceros 3D, providing access to the core functionality
of the software. It enables developers to create and manipulate complex geometries,
implement custom workflows, and enhance the modeling capabilities of Rhino through
scripting. At the heart of RhinoCommon are classes, which encapsulate properties and
methods to interact with the Rhino environment.
• Inheritance: Many classes are derived from base classes, allowing for shared
behavior and properties while providing specialized functionality.
• Polymorphism: Classes can override methods from base classes to provide custom
behavior while still conforming to the expected interface.
point1 = rg.Point3d(1, 2, 3)
point2 = rg.Point3d(4, 5, 6)
distance = point1.DistanceTo(point2)
print(f"Distance: {distance}")
Mesh: Represents a mesh object. Used for defining and manipulating polygonal
representations of surfaces.
51 Object-Oriented Programming
def area(self):
if isinstance(self.geometry, rg.Surface):
return self.geometry.GetArea()
return 0
def describe(self):
return f"{self.name}: Area = {self.area()}"
class ArchitecturalElement:
"""
A class to represent an architectural element.
Attributes:
----------
name : str
The name of the architectural element.
geometry : Rhino.Geometry
The geometric representation of the element (curve, surface, etc.).
Methods:
-------
area():
Calculates the area of the geometric representation.
describe():
Returns a string description of the architectural element.
"""
Parameters:
----------
name : str
The name of the architectural element.
geometry : Rhino.Geometry
The geometric representation of the element.
"""
self.name = name
self.geometry = geometry
def area(self):
"""
Calculates the area of the geometric representation.
Returns:
-------
float
The area of the geometry if it is a surface, otherwise returns 0.
"""
if isinstance(self.geometry, rg.Surface):
return self.geometry.GetArea()
return 0
def describe(self):
"""
Returns a string description of the architectural element.
Returns:
-------
str
A formatted string containing the name and area of the element.
"""
return f"{self.name}: Area = {self.area()}"
53 Functions in Python
Understanding OOP principles allows you to design flexible, reusable code structures
tailored to architectural needs. Next, we’ll look at Functions in Python, diving into
standalone functions that handle specific tasks and support modular programming.
Functions in Python
Functions are reusable blocks of code designed to perform specific tasks, making scripts
modular and easier to manage. Functions are particularly useful in architectural coding for
performing repetitive tasks, like calculating areas or generating geometry.
Defining Functions: We’ll cover the syntax of defining functions, with parameters and return
statements.
def greet():
print("Hello, Architect!")
# Calling the function
greet() # Output: Hello, Architect!
In this example, greet is a simple function that prints a message when called.
54 Functions in Python
Function Parameters
Parameters are variables passed to a function to customize its behavior. You can pass one
or multiple parameters, allowing the function to operate on different data each time it’s
called.
Here, calculate_wall_area accepts two parameters, length and height, and returns their
product, which represents the area.
Return Values
A function can use the return keyword to send a value back to the caller. This is especially
useful when you need to capture the function’s result for further calculations.
In this example, calculate_volume calculates the volume of a room and returns the result,
which is then stored in volume.
On the other hand, a function may return ‘None’ if no variable comes after the return
keyword.
def greetings(name):
print ("Hello {}".format(name))
return
result = greetings(“Yassin”)
print(result) #Output: None
def testfunction(arg):
print ("ID inside the function:", id(arg))
arg=arg+1
print ("new object after increment", arg, id(arg))
var=10
print ("ID before passing:", id(var))
testfunction(var) #the printed value of var is modified.
print ("value after function call", var) #the value of var remains the same
def testfunction(arg):
arg.append(4)
print ("ID inside the function:", id(arg))
list_a = [1,2,3]
print ("ID before passing:", id(list_a))
testfunction(list_a) #list_a gets modified
Positional arguments are the most common type, where arguments must be provided in the
same order as the parameters are defined. These arguments are required, meaning if they
are not provided, Python will raise an error.
Keyword arguments allow you to specify arguments by name, which makes the code more
readable and flexible. With keyword arguments, you can pass values in any order, as each
argument is specified with the parameter name.
56 Functions in Python
Default arguments are used to assign default values to parameters. If the argument is not
provided when calling the function, the default value is used.
Positional-only arguments are specified by placing a / in the parameter list. Any parameters
listed before / must be passed positionally and cannot be used as keyword arguments. This
is useful when you want certain arguments to only be used in the order they appear.
Keyword-only arguments are specified by placing a * in the parameter list. Any parameters
listed after * must be passed as keyword arguments. This can help prevent mistakes when
passing arguments.
Python print() function has a keyword only argument with a default value of a space “ “ called
sep.
• The argument list begins with the positional-only args, followed by the slash (/)
symbol.
• It is followed by regular positional args that may or may not be called as keyword
arguments.
• Then there may be one or more args with default values.
• Next, arbitrary positional arguments represented by a variable prefixed with single
asterisk, that is treated as tuple. It is the next.
• If the function has any keyword-only arguments, put an asterisk before their names
start. Some of the keyword-only arguments may have a default value.
• Last in the bracket is argument with two asterisks ** to accept arbitrary number of
keyword arguments.
Default Parameters
Default parameters let you define default values for function parameters. If the caller doesn’t
provide a value, the function uses the default.
Functions Annotations
Annotations are any valid Python expressions added to the arguments or return data type.
Simplest example of annotation is to prescribe the data type of the arguments. Annotation
is mentioned as an expression after putting a colon in front of the argument. Annotations are
ignored at runtime.
print (myfunction(10,20))
you can put any expression which acts as the metadata for the arguments
The function in Python is also an object, and one of its attributes is __annotations__. You can
check with dir() function.
print (dir(myfunction))
The __annotations__ attribute itself is a dictionary in which arguments are keys and
annotations their values.
def myfunction(*args: "arbitrary args", **kwargs: "arbitrary keyword args") -> int:
pass
print (myfunction.__annotations__)
59 Functions in Python
def room_dimensions():
length = 5
width = 3
return length * width
In this example, length and width are local variables within room_dimensions, so they only
exist during the function’s execution.
length = 5
width = 3
def room_dimensions():
return length * width
room_dimensions() #Output: 15
def room_dimensions():
global length
length = 5
width = 3
return length * width
In Python, globals() and locals() are built-in functions that provide access to the global and
local symbol tables, respectively. These functions allow you to inspect and manipulate
variables in different scopes within your code.
The globals() function returns a dictionary representing the global symbol table, which
contains all globally defined variables and functions. This dictionary is accessible from
anywhere in your code, and you can read or modify global variables using it.
The locals() function returns a dictionary representing the local symbol table, which
contains all variables defined in the current local scope (e.g., inside a function).
Within a function, locals() shows variables that are local to that function.
In the global scope (outside any function), locals() behaves similarly to globals(), showing
global variables.
60 Functions in Python
# Global variable
x = 10
def print_globals():
print("Global variables:", globals())
print_globals()
#Output:
Global variables: {'__name__': '__main__', '__doc__': None, ..., 'x': 10, 'print_globals': <function
print_globals at 0x7f9b8d3c9dc0>}
globals() returns a dictionary of all global variables, including x and the print_globals
function itself. You can see the x variable with a value of 10 in the dictionary.
def modify_global():
globals()['x'] = 20
modify_global()
print(x) # Output: 20
Here, modify_global() uses globals() to set the value of x to 20. After calling modify_global(),
the value of x is changed globally.
# Local variable
def local_example():
a = 5
b = 10
print("Local variables:", locals())
local_example()
#Output:
Local variables: {'a': 5, 'b': 10}
While you can read values using locals(), modifying the dictionary returned by locals() does
not affect the actual local variables in the current scope. This is mainly useful for inspection
rather than manipulation within functions.
In the global scope (outside of any function), globals() and locals() behave the same way,
returning the same dictionary of global variables.
print("Globals:", globals())
print("Locals:", locals())
This will output the same dictionary of global variables, as the global and local scope are the
same outside any function.
61 Functions in Python
• User-defined functions
• Built-in functions
• Lambda functions
• Recursion functions
User-defined functions were discussed and are functions that developers define using the
def keyword. These functions allow us to encapsulate reusable logic into a single function,
which can then be called multiple times throughout the code.
• abs( )
• max( )
• bool( )
• min( )
• bytes( )
• next( )
• chr( )
• pow( )
• complex( )
• range( )
• float( )
• reversed( )
• format( )
• round( )
• globals( )
• slice( )
• id( )
• sorted( )
• input( )
• str( )
• int( )
• sum( )
• len( )
• tuple( )
• list( )
• type( )
• locals( )
• filter( )
• map( )
Lambda Functions
Lambda functions are small, anonymous functions defined using the lambda keyword. They
are typically used for simple, one-line operations and are often passed as arguments to
higher-order functions (functions that take other functions as parameters). Lambda
functions have a limited scope and can only contain a single expression.
Here, the area lambda function quickly calculates the area by multiplying length and width.
62 Functions in Python
Recursion Functions
Recursive functions are functions that call themselves as part of their execution. They are
commonly used to solve problems that can be broken down into smaller, similar sub-
problems. A base case is essential to avoid infinite recursion.
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n – 1)
#Explanation
Factorial(6)
6 * factorial(6 – 1)
6 * factorial(5)
6 * 5 * factorial(5-1)
6 * 5 * factorial(4)
6 * 5 * 4 * factorial(4-1)
6 * 5 * 4 * factorial(3)
6 * 5 * 4 * 3 * factorial(3-1)
6 * 5 * 4 * 3 * factorial(2)
6 * 5 * 4 * 3 * 2 * factorial(2-1)
6 * 5 * 4 * 3 * 2 * factorial(1)
6 * 5 * 4 * 3 * 2 * 1 * factorial(1-0)
6 * 5 * 4 * 3 * 2 * 1 * factorial(0)
n == 0 → stop
def Recursion(k):
if k > 0:
result = k + Recursion(k - 1)
print(result)
else:
result = 0
return result
print("\n\nRecursion Example Results")
Recursion(6) #output: 1 3 6 10 15 21
#Explanation
Recursion(6):
k = 6, so it calls Recursion(5) and waits for the result.
result = 6 + Recursion(5)
Recursion(5):
k = 5, so it calls Recursion(4) and waits for the result.
result = 5 + Recursion(4)
Recursion(4):
k = 4, so it calls Recursion(3) and waits for the result.
result = 4 + Recursion(3)
Recursion(3):
k = 3, so it calls Recursion(2) and waits for the result.
result = 3 + Recursion(2)
Recursion(2):
k = 2, so it calls Recursion(1) and waits for the result.
result = 2 + Recursion(1)
Recursion(1):
k = 1, so it calls Recursion(0) and waits for the result.
result = 1 + Recursion(0)
Recursion(0):
k = 0, so it hits the base case.
result = 0 and returns 0.
Recursion(1):
result = 1 + 0 = 1
Prints 1
Returns 1
Recursion(2):
result = 2 + 1 = 3
Prints 3
Returns 3
Recursion(3):
result = 3 + 3 = 6
Prints 6
Returns 6
Recursion(4):
result = 4 + 6 = 10
Prints 10
Returns 10
Recursion(5):
result = 5 + 10 = 15
Prints 15
Returns 15
Recursion(6):
result = 6 + 15 = 21
Prints 21
Returns 21
64 Functions in Python
24 floor (x) The floor of x: the largest integer not greater than x.
26 frexp (x) Returns the mantissa and exponent for a given number x.
29 gcd (x,y,z) Return the greatest common divisor of the specified integer arguments.
32 isclose (x,y) Return True if the values x and y are close to each other and False otherwise.
65 Functions in Python
33 isfinite (x) Returns True if neither an infinity nor a NaN, and False otherwise.
34 isinf (x) Return True if x is a positive or negative infinity, and False otherwise.
35 isnan (x) Return True if x is a NaN (not a number), and False otherwise.
36 isqrt (x) Return the integer square root of the nonnegative integer x
37 lcm (x1, x2, ..) Return the least common multiple of the specified integer arguments.
39 lgamma (x) Return the natural logarithm of the absolute value of the Gamma function at x.
47 perm (x,y) Return the number of ways to choose x items from y items without repetition and with order.
50 prod (iterable) Return the product of all the elements in the input iterable.
59 trunc (x) Return x with the fractional part removed, leaving the integer part.
60 ulp Return the value of the least significant bit of the float x.
By mastering functions, you can create modular and reusable code blocks, which is
essential for efficient architectural scripting. Next, we’ll explore control flow.
Control Flow
Control flow determines the sequence in which your code executes. In Python, you can
control the flow of your program using conditional statements, loops, and other structures.
Mastering control flow is essential in scripting for architecture, as it allows you to make
decisions, repeat tasks, and manage complex workflows effectively.
Conditional statements: Making decisions in code with if, elif, and else.
Loop control statements: Controlling loop behavior with break, continue, and pass.
Conditional Statements
Conditional statements let you execute certain sections of code based on whether a
condition is true or false. Python uses if, elif, and else to build these statements.
def wall_thickness_check(thickness):
if thickness < 0.1:
return "Wall is too thin for stability."
elif thickness > 0.5:
return "Wall is thicker than necessary."
else:
return "Wall thickness is appropriate."
print(wall_thickness_check(0.2)) # Output: Wall thickness is appropriate
67 Control Flow
In this example:
ratio = 65
result = 0
print(result)
68 Control Flow
Logical Operators
You can use logical operators (and, or, not) to combine multiple conditions in a single
statement.
Here, ‘and’ ensures that both conditions (length >= 3 and width >= 3) must be true for the
room to be considered ideal.
Match-Case Statement
This feature allows for a more readable and flexible way of handling multiple conditions
based on the structure and content of data. Let's go through the syntax, usage, and
examples. A match statement starts with the match keyword, followed by an expression (the
value you want to match). Inside the match block, case statements define different patterns
to match against the expression.
n = 5
result = ""
match n:
case 0: result = "Saturday"
case 1: result = "Sunday"
case 2: result = "Monday"
case 3: result = "Tuesday"
case 4: result = "Wednesday"
case 5: result = "Thursday"
case 6: result = "Friday"
case _: result = "Invalid day"
print(result)
The example above showes matching literal values which is the most basic form of match
where you compare against specific values.
You can match against structures like tuples, lists, or dictionaries, allowing you to
deconstruct values directly in the case pattern.
69 Control Flow
def process_coordinates(point):
match point:
case (0, 0):
print("Origin")
case (x, 0):
print(f"Point on the X-axis at x={x}")
case (0, y):
print(f"Point on the Y-axis at y={y}")
case (x, y):
print(f"Point at coordinates x={x}, y={y}")
case _:
print("Unknown format")
You can use match with classes, matching based on their attributes. This allows for
matching more complex objects based on their properties.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def process_point(point):
match point:
case Point(x=0, y=0):
print("Origin")
case Point(x=0, y=y):
print(f"Point on the Y-axis at y={y}")
case Point(x=x, y=0):
print(f"Point on the X-axis at x={x}")
case Point(x=x, y=y):
print(f"Point at coordinates x={x}, y={y}")
case _:
print("Unknown point")
point = Point(3, 0)
process_point(point) # Output: Point on the X-axis at x=3
def interest(details):
match details:
case [amount, duration] if amount<10000:
return amount*10*duration/100
case [amount, duration] if amount>=10000:
return amount*15*duration/100
print("Interest = ", interest([5000, 5]))
print("Interest = ", interest([15000, 3]))
70 Control Flow
Loops
Loops allow you to repeat tasks efficiently. Python provides two main loop types:
• For loops: Used to iterate over a sequence, such as a list or range of numbers.
• While loops: Used when you want to repeat a block of code until a specific condition
is no longer true.
For Loops
A for loop is ideal when you know the exact number of iterations or need to process each
item in a sequence.
This for loop iterates over each item in room_areas, printing each room’s area.
range generates a sequence of numbers, commonly used with for loops. It could have 3
syntaxes:
• range(stop)
• range(start, stop, step)
• range(start, stop)
71 Control Flow
for i in range(3):
print("This is repetition number:", i + 1)
Here, range(3) generates the numbers 0, 1, and 2. Each iteration prints the current repetition
number.
for x in numbers:
print(x, end = ' ')
for x in numbers:
print(x, ":", numbers[x])
for x in numbers.items():
print(x)
for x in numbers.keys():
print(x, ":", numbers[x])
factorial = 1
n = 7
#Sequence Indexing
#Indices = range(len(sequence))
for i in indices:
print("index:", i, "number:", numbers[i])
72 Control Flow
for-else Loops
In Python, the for-else loop is a unique construct that combines a for loop with an optional
else clause. The else clause runs if the loop completes without encountering a break
statement. This can be useful when you need to check if a loop finished naturally or was
interrupted.
#searching in a list
numbers = [1, 3, 5, 7, 9]
for num in numbers:
if num == 4:
print("Found 4!")
break
else:
print("4 was not found in the list.")
While Loops
A while loop continues executing as long as a specified condition remains true. Use while
loops when you don’t know in advance how many times a loop should run.
73 Control Flow
total_area = 0
room_areas = [20, 15, 30, 10]
i = 0
while i < len(room_areas):
total_area += room_areas[i]
i += 1
print("Total Area of All Rooms:", total_area, "square meters")
In this example, the while loop adds each room’s area to total_area until all rooms are
processed. i acts as a counter, increasing by 1 in each iteration until it reaches the number
of rooms.
var='0'
while var.isnumeric()==True:
var=input('enter a number..')
if var.isnumeric()==True:
print ("Your input", var)
print ("End of while loop")
While-else Loop
In Python, while-else loops work similarly to for-else loops. The else clause in a while loop
executes only if the while loop completes naturally, without hitting a break statement. If the
loop terminates because of a break, the else clause is skipped.
count=0
while count<5:
count+=1
print ("Iteration no. {}".format(count))
else:
print ("While loop over. Now in else block")
print ("End of while loop")
74 Control Flow
Infinite Loops
An infinite loop is a loop that continues to execute indefinitely because its terminating
condition is never met. This can happen for various reasons, such as the condition always
evaluating to True or not having a break statement to exit the loop. Infinite loops can be
intentional or accidental and are important to understand for both control flow in
programming and for preventing potential errors in code execution.
var = 1
while var == 1 : # This constructs an infinite loop
num = int(input("Enter a number :"))
print ("You entered: ", num)
print ("Good bye!")
Nested Loops
Nested loops are loops that exist within the body of another loop. This means that for every
iteration of the outer loop, the inner loop will execute its full set of iterations. Nested loops
are often used for tasks that require multiple levels of iteration, such as processing multi-
dimensional data structures (like lists of lists or matrices), performing complex calculations,
or generating combinations.
for i in range(1,11):
for j in range(1,11):
k=i*j
print ("{:3d}".format(k), end=' ')
#Output:
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
i = 1
Using break
In this example, the loop exits when a room with an area greater than 25 square meters is
found.
Using continue
Here, continue skips any room with an area smaller than 15 square meters.
Using pass
The pass statement is a null operation or a placeholder that does nothing when executed.
Use it as a placeholder for future code or if you want to create an empty loop.
for i in range(5):
pass # Placeholder for future code
76 Control Flow
numbers = [1, 2, 3, 4, 5]
Mastering control flow helps you build flexible and responsive scripts that adapt to a variety
of architectural data and workflows. This foundational knowledge prepares you for tackling
complex architectural coding challenges, where decision-making and repetition play crucial
roles.
To make the code shorter you can add an alias as seen before by typing: import
Rhino.Geometry as rg.
In this case, to create a Point3d object you will need to type: a = rg.Point3d(x,y,z).
When you start writing your code, a list of classes, structs, and enums will appear which can
be helpful to recognize the names.
77 Rhino.Geometry Namespace
Classes are reference types, meaning they are accessed by reference (a pointer to
the memory address where they are stored). When you assign one class instance to
another, they refer to the same memory location. Any change to one will reflect in the
other.
Structs are value types, meaning they hold their actual data. When you assign one
struct to another, it creates a copy of the data. Changes to one will not affect the
other.
4. Inheritance:
Classes support inheritance (subclassing), which allows for polymorphic behavior
(e.g., creating hierarchies of related classes).
Structs generally do not support inheritance, though they can implement interfaces
in languages like C#.
5. Mutability:
Classes are typically used for mutable objects, meaning their internal state can
change over time.
Structs are often used for immutable data structures or for small, fixed-sized data
types that represent simple values, like coordinates or colors. However, immutability
is not a strict rule, and structs can be mutable too.
Enums
Enums help catch errors at compile time in some languages (e.g., C++), and they make the
code less error-prone by providing predefined options. They are particularly useful when you
have a variable that can only take one out of a small set of possible values, and you want to
make your code more readable and maintainable by using descriptive names for those
values rather than numbers or strings.
In Python, enums are implemented using the enum module, introduced in Python 3.4.
79 Rhino.Geometry Namespace
Each value in an enum has a unique name, which makes the code easier to read and
understand. Additionally, Enum values are constants and cannot be changed once they are
defined. This ensures consistency in your code.
When calling an enum you can just call the ‘EnumClass.Option’ for example
LoftType.Normal.
Point3d Struct
Examples of Point3d Struct CONSTRUCTORS
my_point = rg.Point3d(0, 0, 0)
my_point = rg.Point3d(x, y, z) #add x,y,z as inputs in grasshopper python component
my_point = rg.Point3d(another_point_variable)
Vector3d Struct
Examples of Vector3d Struct CONSTRUCTORS
my_vector = rg.Vector3d(0, 0, 1)
my_vector = rg.Vector3d(x, y, z) #add x,y,z as inputs in grasshopper python component
my_vector = rg.Vector3d(another_vector3d)
my_vector = rg.Vector3d(point3d_variable)
Plane Struct
Examples of Plane Struct CONSTRUCTORS
my_plane = rg.Plane(origin_point, normal_vector3d)
my_plane = rg.Plane(another_plane)
my_plane_from_origin_and_two_vectors = rg.Plane(origin_point, vector3d_x, vector3d_y)
my_plane_from_origin_and_two_points = rg.Plane(origin_point, point_a, point_b)
82 Rhino.Geometry Namespace Objects
Interval Struct
Examples of Interval Struct CONSTRUCTORS
Line Struct
Examples of Line Struct CONSTRUCTORS
my_line = rg.Line(point_a, point_b)
my_line = rg.Line(point_a, vector_a, line_length)
83 Rhino.Geometry Namespace Objects
start_point = line_a.From
end_point = line_a.End
line_length = line_a.Length
Box Struct
Examples of Box Struct CONSTRUCTORS
my_box = rg.Box(base_plane, interval_x, interval_y, interval_z)
my_box_that_contains_defined_points = rg.Box(base_plane, list_of_points)
Sphere Struct
Examples of Sphere Struct CONSTRUCTORS
my_sphere = rg.Sphere(center_point, radius)
Curve
Examples of Curve Class CONSTRUCTORS
Curve class constructors do not have a direct way to create a new curve. Rather, you can
create new curves using the ‘.CreateFrom’ curve class methods.
closest_point_parameter = my_curve.ClosestPoint(test_point)
closest_point = my_curve.PointAt(closest_point_parameter)
my_curve = rg.Curve.CreateArcCornerRectangle(rectangle, radius)
my_curve = rg.Curve.CreateBlendCurve(curve_a, curve_b, BlendContinuity.Poisition/Tangency/Curvature)
my_curve = rg.Curve.CreateControlPointCurve(point_list, degree_integer)
my_filleted_curve = rg.Curve.CreateFilletCornersCurve(curve_a, radius, tolerance, angle_tolerance)
my_interpolate_curve = rg.Curve.CreateInterpolatedCurve(point_list, degree_integer,
CurveKnotStyle.Uniform)
my_mean_curve = rg.Curve.CreateMeanCurve(curve_a, curve_b)
my_periodic_curve = rg.Curve.CreatePeriodicCurve(curve_a, bool_smooth)
my_tween_curves = rg.Curve.CreateTweenCurves(curve_a, curve_b, no_of_curves_int, tolerance)
my_boolean_curves_array = rg.Curve.CreateBooleanDifference(curve_a, curve_b, tolerance)
my_boolean_curves_array = rg.Curve.CreateBooleanDifference(curve_a, subtractors_curves_list, tolerance)
my_intersection_curves_array = rg.Curve.CreateIntersection(curve_a, curve_b, tolerance)
my_curve_boolean_regions = rg.Curve.CreateBooleanRegions(list_curves, plane, combine_regions_bool,
tolerance)
my_region_curves_array = my_curve_boolean_regions.RegionCurves(region_index)
my_boolean_union_curves = rg.Curve.CreateBooleanUnion(curves_list, tolerance)
my_curve_curvature_vector = my_curve.CurvatureAt(parameter)
my_curve_division_parameters_list = my_curve.DivideByCount(segments_count_integer, include_ends_bool)
my_curve_division_parameters_array = my_curve.DivideByLength(segments_length, include_ends_bool,
reverse_bool)
my_curve_equidistant_division_points_array = my_curve.DivideEquidistant(distance_value)
my_extended_curve = my_curve.Extend(extension_value_at_start, extension_value_at_end)
my_frame_plane = my_curve.FrameAt(parameter)
my_curve_length = my_curve.GetLength()
my_perp_frame_plane = my_curve.PerperndicularFrameAt(parameter)
my_perp_frames_planes = my_curve.GetPerpendicularFrames(parameters_list)
my_joined_curves_array = rg.Curve.JoinCurves(curves_list, join_tolerance)
my_length_parameter = my_curve.LengthParameter(segment_length, subdomain_interval)
my_offset_curve_array = my_curve.Offset(plane, distance, tolerance, CurveOffsetCornerStyle.Smooth)
my_offset_curve = my_curve.OffsetNormalToSurface(surface, height)
my_offset_curve_array = my_curve.OffsetOnSurface(surface, distance, fitting_tolerance)
my_point_on_curve = my_curve.PointAt(parameter)
my_point_on_curve = my_curve.PointAtLength(length)
my_point_on_curve = my_curve.PointAtNormalizedLength(length)
my_projected_on_brep_curve_array = rg.Curve.ProjectToBrep(my_curve, my_brep, direction_vector3d,
tolerance)
my_projected_on_brep_curve_array = rg.Curve.ProjectToBrep(my_curve, my_brep_list, direction_vector3d,
tolerance)
my_projected_on_brep_curves_array = rg.Curve.ProjectToBrep(my_curves_list, my_brep_list,
direction_vector3d, tolerance)
my_projected_on_plane_curve = rg.Curve.ProjectToPlane(my_curve, my_plane)
my_rebuilt_nurbs_curve = my_curve.Rebuild(point_count_integer, degree_integer, preserve_tangents_bool)
my_reversed_curve = my_curve.Reverse()
my_rotated_curve = my_curve.Rotate(rotation_angle, rotation_axis_vector3d, rotation_center_point3d)
my_scaled_curve = my_curve.Scale(scale_factor)
my_split_curve_array = my_curve.Split(parameter)
87 Rhino.Geometry Namespace Objects
my_split_curves_array = my_curve.Split(parameters_list)
my_tangent_vector = my_curve.TangentAt(parameter)
my_nurbs_curve_from_curve = my_curve.ToNurbsCurve() # use to get use of Nurbs Curve methods and properties
my_moved_curve = my_curve.Translate(x_ translation, y_ translation, z_ translation)
my_moved_curve = my_curve.Translate(translation_vector_3d)
my_trimmed_curve = my_curve.Trim(CurveEnd.Start/End/Both, trimming_length)
my_trimmed_curve = my_curve.Trim(start_trimming_length, end_trimming_length)
my_text_curve_array = rg.Curve.CreateTextOutlines(‘text’, ‘font’, text_height, text_style_integer,
bool_close_loops, plane, small_caps_scale_float, tolerance)
my_duplicate_curve_geometry_base = my_curve.Duplicate()
LineCurve Class
LineCurve Class inherits the methods of the Curve Class. So, you can use the methods of
the Curve Class with LineCurve objects as well as the properties of the Curve Class.
NurbsCurve Class
Just like the LineCurve Class, the NurbsCurve Class inherits its methods, and properties
from the Curve Class, so all of the Curve Class properties and methods could be used with
NurbsCurve class.
Surface Class
Surface class constructors do not have a direct way to create a new surface. Rather, you can
create new surfaces using the ‘.CreateFrom’ surface class methods.
89 Rhino.Geometry Namespace Objects
NurbsSurface Class
Just like the LineCurve Class, the NurbsSurface Class inherits its methods, and properties
from the Surface Class, so all of the Surface Class properties and methods could be used
with NurbsSurface class.
Brep Class
Brep class constructors do not have a direct way to create a new Brep. Rather, you can create
new breps using the ‘.Create’ Brep class methods.
Polyline Class
The polyline class enherits some of its properties and methods from the Point3dList Class
which exists in the Rhino.Collections namespace.
my_polyline = rg.Polyline(points_list)
PolylineCurve Class
Just like the LineCurve Class, the PolylineCurve Class inherits its methods and properties
from the Curve Class, so all of the Curve Class properties and methods could be used with
PolylineCurve class.
my_polyline_curve = rg.PolylineCurve(points_list)
Triangle3d Struct
Examples of Triangle3d Struct CONSTRUCTORS
Arc Struct
Examples of Arc Struct CONSTRUCTORS
my_arc = rg.Arc(my_circle, angle_in_radians)
my_arc = rg.Arc(my_plane, radius, angle_in_radians)
my_arc = rg.Arc(my_plane, center_point, radius, angle_in_radians)
my_arc = rg.Arc(center_point, radius, angle_in_radians)
my_arc = rg.Arc(start_point, mid_point, end_point)
my_arc = rg.Arc(start_point, tangent_at_start_vector, end_point)
ArcCurve Class
Just like the LineCurve Class, the ArcCurve Class inherits its methods and properties from
the Curve Class, so all of the Curve Class properties and methods could be used with
ArcCurve class.
Rectangle3d Struct
Examples of Rectangle3d Struct CONSTRUCTORS
my_rectangle = rg.Rectangle3d(my_plane, width, height)
my_rectangle = rg.Rectangle3d(my_plane, width_interval, height_interval)
my_rectangle = rg.Rectangle3d(my_plane, corner_point_a, corner_point_b)
Circle Struct
Examples of Circle Struct CONSTRUCTORS
my_circle = rg.Circle(my_plane, radius)
my_circle = rg.Circle(my_plane, center_point, radius)
my_circle = rg.Circle(point_a, point_b, point_c)
Mesh Class
Mesh class constructors do not have a direct way to create a new Mesh. Rather, you can
create new meshes using the ‘.Create’ Mesh class methods.
Transform Struct
Transform struct is used to generate transformations that could be used on any geometry’s
.Transform() method.
Intersection Class
Intersection class has methods used to solve intersection events between geometries.
Generally, the output ‘with red font’ affects how to get the intersection results. val is for if the
intersection is valid. Some intersections result in param which need to be extracted and
used to get points on a curve, some result in curves and points at the same time which needs
to be selected and used to get the actual points and curves. Additionally, some intersections
result in what is called an “intersection event” which we will discuss now.
IntersectionEvent Class
Some intersection class methods return an “IntersectionEvent” type which holds
information for curves intersection events.
99 Rhino.Geometry Namespace Objects
import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri
And then, an example of dealing with Intersections output in case of line|box intersections.
import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri
Next, an example on how to get intersection points between a line and a circle:
import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri
a = ri.Intersection.LineCircle(x, y)
# Convert the result to a list
b = list(a)
import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri
a = ri.Intersection.SurfaceSurface(x, y, 0.001)
# Initialize a lists to hold the intersection points and curves
intersection_curves = []
intersection_points = []
import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri
import System.Collections.Generic as sg
Now, let’s get the intersection points between a plane and a circle:
import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri
ArcArcIntersection
Values Description
0 None Arcs do not intersect
1 Single Arcs touch at one point
105 Rhino.Geometry Namespace Objects
CircleCircleIntersection
Values Description
0 None Circles do not intersect
1 Single Circles touch at one point
2 Multiple Circles intersect at two points
3 Overlap Circles are identical
LineCircleIntersection
Values Description
0 None No intersections
1 Single One intersection
2 Multiple Two intersections
LineCylinderIntersection
Values Description
0 None No intersections
1 Single One intersection
2 Multiple Two intersections
3 Overlap Line lies on cylinder
LineSphereIntersection
Values Description
0 None No intersections
1 Single One intersection
2 Multiple Two intersections
PlaneCircleIntersection
Values Description
No intersections. Either because radius is too small or because circle plane is parallel but not
0 None
coincident with the intersection plane.
1 Tangent Tangent (one point) intersection.
2 Secant Secant (two point) intersection.
3 Parallel Circle and plane are planar but not coincident. Parallel indicates no intersection took place.
4 Coincident Circle and plane are co-planar, they intersect everywhere.
PlaneSphereIntersection
Values Description
106 Rhino.Geometry Namespace Objects
0 None No intersections
1 Point Tangent intersection
2 Circle Circular intersections
SphereSphereIntersection
Values Description
0 None Spheres do not intersect
1 Point Spheres touch at a single point
2 Circle Spheres intersect at a circle
3 Overlap Spheres are identical
Rhino.Geometry Enums
Rhino.Geometry namespace has some enumerations which serve certain methods
arguments as seen previously with different classes and structs.
BlendContinuity enum
Values Description
0 Position G0: The curves or surfaces touch at the join point (position).
1 Tangency G1: The curves or surfaces also share a common tangent direction at the joint point (tangent)
2 Curvature G2: The curves or surfaces also share a common center of curvature at the join point (curvature)
CurveKnotStyle enum
Values Description
0 Uniform Parameter spacing between consecutive knots is 1.0.
1 Chord Chord length spacing, requires degree=3 with CV1 and CVn1 specified.
2 ChordSquareRoot Square root of chord length, requires degree=3 with CV1 and CVn1 specified.
3 UniformPeriodic Periodic with uniform spacing.
4 ChordPeriodic Periodic with chord length spacing.
5 ChordSquareRootPeriodic Periodic with square root of chord length spacing.
107 Rhino.Geometry Namespace Objects
CurveOffsetCornerStyle enum
Values Description
0 None The default value
1 Sharp Offsets and extends curves with a straight line until they intersect
2 Round Offsets and fillets curves with an arc of radius equal to the offset distance
3 Smooth Offsets and connects curves with a smooth (G1 continuity) curve
4 Chamfer Offsets and connects curves with a straight line between their endpoints.
CurveEnd enum
Values Description
0 None Not the start nor the end
1 Start The frontal part of the curve
2 End The tail part of the curve
3 Both Both the start and the end of the curve
RailType enum
Values Description
0 DistanceFromEdge The distance from the edge curves determines the intersection
1 RollingBall The radius of a rolling ball determines the intersection
2 DistanceBetweenRails The distance between the edge rails determines the intersection
BlendType enum
Values Description
0 Chamfer Creates a ruled surface between brep edges with varying chamfer distances
1 Fillet Creates a tangent surface between brep edges with varying radius values
2 Blend Creates a curvature-continuous blend surface between brep edges with varying radius values
PipeCapMode enum
Values Description
0 None No cap
1 Flat Caps with planar surface
2 Round Caps with hemispherical surface
MeshPipeCapStyle enum
Values Description
0 None Capping is skipped. The resulting object will be simpler.
1 Flat A flat surface will cap the pipe
2 Box A simple construction will cap the pipe
3 Dome A meridians-and-parallels hemisphere construction will cap the pipe
108 Working with Data Trees
One of the most critical aspects of working with data in Grasshopper is understanding data
trees. Unlike simple lists or arrays, data trees allow for hierarchical organization, where each
branch can contain multiple items, and each item is organized by path. This structure is
essential in Grasshopper for managing complex sets of geometry or data that naturally group
and branch, such as multi-level lists in architectural forms or multi-stage parameters in
generative design processes.
In this section, we’ll cover how to work with data trees in Grasshopper using Python. Python
provides an accessible way to interact with the Grasshopper SDK, where you can leverage
data trees to organize, flatten, merge, and manipulate data with fine-grained control. This is
particularly useful in cases where standard list or dictionary structures fall short. Here are
some core topics we’ll explore:
• Creating Data Trees: We’ll learn how to initialize and populate DataTree objects in
Python, allowing us to build hierarchical data structures from scratch.
• Paths and Branches: Each branch in a data tree is assigned a path, typically
represented as {0;0}, {0;1}, etc. We’ll see how to create and assign data to these
paths, so you can organize your data in meaningful ways.
• Flattening and Grafting: Flatten and Graft are essential operations in Grasshopper.
Flattening simplifies data structures by merging branches into a single list, while
grafting creates a new branch for each item. We’ll see how to apply these operations
to our data trees in Python.
• Merging and Pruning Trees: Combining multiple data trees and selectively removing
branches are common practices when dealing with complex data structures. We’ll
cover methods to merge trees or filter branches based on specific criteria.
By the end of this section, you’ll understand how to utilize the Grasshopper SDK’s data tree
functions in Python to build advanced, custom workflows that align with your unique design
goals. Working with data trees in Python opens up new possibilities for organizing,
109 Working with Data Trees
manipulating, and managing data structures, making it easier to handle large, complex data
sets in computational design.
import Rhino.Geometry as rg
import Grasshopper as gh
The DataTree[object] type accepts any object type as an item. This tree is now ready to have
items and paths added to it.
import Rhino.Geometry as rg
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
# Define paths
path1 = GH_Path(0) # Path {0}
path2 = GH_Path(1, 0) # Path {1;0}
Each path can contain multiple items, similar to branches on a tree. You can continue adding
items to the same path, or add data to new paths as needed.
• Flattening removes all hierarchy, merging items from all branches into a single
branch at {0}.
110 Working with Data Trees
• Grafting creates a new branch for each individual item, so each item has its own
unique path.
flattened_tree = tree.Flatten()
grafted_tree = tree.Graft()
These methods allow you to control the complexity of the data structure. Flattening is useful
when you need to work with all items as a single list, while grafting is useful when you want
to ensure each item is isolated on its own branch.
Merging Trees: You can combine trees by using loops to add branches from one tree to
another.
tree1 = DataTree[object]()
tree2 = DataTree[object]()
Pruning Trees: Pruning involves removing branches based on specific criteria, such as
empty branches or branches below a certain length.
pruned_tree = DataTree[object]()
for i in range(tree.BranchCount):
path = tree.Path(i)
branch = tree.Branch(path)
if len(branch) > 0:
pruned_tree.AddRange(branch, path)
path = GH_Path(0)
branch = tree.Branch(path)
print(branch) # Prints items in the branch at {0}
path = GH_Path(0)
branch = tree.Branch(path)
item = branch[0]
#Or
item = tree.Branch[0]
• Getting All Paths: Retrieve all paths to see the structure of your data tree.
paths = tree.Paths
for path in paths:
print(path) # Prints each path in the data tree
• Tree Depth and Branch Count: You can use .BranchCount to get the number of
branches and .Paths to view all paths in the tree.
This is an example of creating a nested data tree structure with multiple paths and items:
tree = DataTree[object]()
This code creates a data tree with three branches, where each branch contains five items.
Each item has a unique position within its respective branch, which is accessible via its path.
112 Conclusion
Data trees are an essential part of managing data in Grasshopper, especially in complex
design workflows. By leveraging the Grasshopper SDK in Python, we can build, manipulate,
and organize data trees effectively, providing a higher level of control over our computational
models. This section has covered the core concepts and operations of data trees, including
creation, flattening, grafting, merging, and pruning. Mastering these will allow you to
efficiently handle large data sets and create custom data structures tailored to your specific
design needs.
Conclusion
Throughout this book, we've dived deep into the intersection of coding and design within the
Rhino and Grasshopper environment, focusing on RhinoCommon and its applications in
architecture. From foundational concepts in Python to advanced data structures and
geometric manipulations, our journey has revealed the immense potential of computational
design for architects. By blending creativity with programming, we've gained a unique
perspective on how code can enhance our design capabilities, enabling more precise,
adaptive, and innovative forms.
Coding in architecture isn't merely a tool for automation—it's a means of exploration and
innovation. RhinoCommon, with its vast array of functions and flexibility, provides an
empowering framework for architects to extend beyond the constraints of traditional
modeling. Through coding, we can generate complex forms, simulate behaviors, and test
structural systems with precision and efficiency. RhinoCommon’s integration with
Grasshopper, in particular, offers a seamless environment for experimenting with generative
design, parametric modeling, and data-driven decision-making.
Parametric design has shifted how architects approach form generation. Instead of
manually adjusting dimensions or shapes, we can define relationships and constraints that
guide the form's evolution. The parametric approach in Grasshopper, paired with the
113 Conclusion
RhinoCommon API, empowers architects to focus on the underlying rules that shape their
designs rather than getting lost in repetitive tasks.
Creating Data Trees: By understanding and manipulating data trees, architects can organize
complex data structures that mirror real-world hierarchical systems, such as multi-tiered
design elements or modular construction components.
In this book, we've demonstrated how parametric thinking facilitates a fluid, exploratory
design process, where form generation can be as rigorous as it is artistic. By defining
parameters and relationships, architects are freed to explore the potential of their designs
within a structured framework, allowing both freedom and control.
While coding opens numerous doors, the learning curve and technical complexities pose
challenges for architects new to programming. The intricacies of RhinoCommon and the
syntax of Python require patience and practice. Additionally, blending traditional design
intuition with programming logic can initially feel daunting, especially as architectural
thinking often leans toward the visual and spatial.
Looking forward, computational design will likely become even more integrated into
architectural workflows. As machine learning and artificial intelligence advance, we can
expect tools that offer more predictive modeling, real-time simulations, and even automated
code generation based on design goals. The skills learned here provide a solid foundation for
architects interested in pushing the boundaries of these technologies. Understanding the
core principles of coding and RhinoCommon will make adapting to new tools and
techniques easier as they emerge.
The integration of coding into architectural practice fosters collaboration across disciplines.
Architects can now share code-based models with engineers, planners, and contractors,
creating a more unified design process. RhinoCommon and Grasshopper, with their
interoperability and flexibility, support this collaborative effort, as models can be shared,
adapted, and extended by other professionals involved in the project lifecycle.
114 Conclusion
The adaptability of these computational tools also encourages a responsive design process,
where feedback loops allow architects to test ideas, adjust parameters, and optimize forms
throughout the project timeline. This iterative, data-driven workflow aligns well with the
increasingly sustainable and performance-driven demands of contemporary architecture,
where designs often respond to changing environmental and societal needs.
Final Thoughts
Coding is more than a technical skill—it's a new way of thinking for architects. It enables us
to explore forms and processes that were previously unattainable, enriching the
architectural profession. This book has aimed to equip you with the skills to not only write
code but to think computationally about design. As you continue your journey, embrace
coding as a creative tool, and allow it to broaden your perspective on what’s possible in
architecture.
By mastering RhinoCommon and Python, you’re stepping into a future where architecture is
as much about the precision of logic as it is about the power of imagination. This fusion of
technology and artistry has the potential to redefine the architectural landscape, creating
designs that are not only visually compelling but also intelligent, adaptive, and deeply
integrated with their context. Continue to experiment, iterate, and let coding guide your path
to innovative and resilient architectural solutions.
115 Bibliography
Bibliography
Books
Online Resources