Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
34 views

RhinoCommon Using the Geometry Namespace with Python v.1.02

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views

RhinoCommon Using the Geometry Namespace with Python v.1.02

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 124

RhinoCommon

Using the Geometry Namespace


with

Python
A Guide to Create Rhinoceros3d Geometry in Grasshopper3d by
Writing Python Code.

Dr. Abdulrahman Ayman

Dr. of Architecture and Design Computing at the Department of Architecture, Faculty of


Engineering, Ain Shams University, Cairo, Egypt
RhinoCommon: Using the Geometry Namespace with Python
A Guide to Create Rhinoceros3d Geometry in Grasshopper3d by Writing Python Code.

Design copyright ©2024 Abdulrahman Ayman


Text copyright ©2024 Abdulrahman Ayman

Abdulrahman Ayman has asserted his right under


the Copyright, Designs and Patent Act 1988 to be
identified as the Author of this work.

All rights reserved. No part of this publication may


be reproduced or transmitted in any form or by any
means, electronic or mechanical, including
photocopy, recording or any information storage
and retrieval system, without prior permission in
writing from the publisher.
Contents
Preface 1
Introduction 3
Introduction To Python 4
Why Python for Architects? 4
Setting Up Python in Rhino and Grasshopper 5
GhPython Component 5
GhPython Script Editor 8
Writing Your First Python Script 14
How This Book is Structured 14
Key Tips for Learning Python 15

Python Data Types 15


Integers and Floats 16
Strings 17
Booleans 18
Type Casting in Python 19
Summary of Python Data Types 19

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

Using Rhino.Geometry Namespace 76


Classes vs Structs vs Enums 77
General Difference between Classes and Structs 77
Class vs. Struct in Python 78
Enums 78

Rhino.Geometry Namespace Objects 79


Point3d Struct 80
Examples of Point3d Struct CONSTRUCTORS 80
Examples of Point3d Struct PROPERTIES 80
Examples of Point3d Struct METHODS 80

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

Intersect Namespace Enums 104


ArcArcIntersection 104
CircleCircleIntersection 105
LineCircleIntersection 105
LineCylinderIntersection 105
LineSphereIntersection 105
PlaneCircleIntersection 105
PlaneSphereIntersection 105
SphereSphereIntersection 106

Rhino.Geometry Enums 106


Loft Type enum 106
BlendContinuity enum 106
CurveKnotStyle enum 106
CurveOffsetCornerStyle enum 107
CurveEnd enum 107
RailType enum 107
BlendType enum 107
PipeCapMode enum 107
MeshPipeCapStyle enum 107

Working with Data Trees in Grasshopper using Python 108


Creating Data Trees 109
Adding Data to Specific Paths 109
Flattening and Grafting 109
Merging and Pruning 110
Useful Data Tree Operations 110
Conclusion 112
Bibliography 115
1 Preface

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.

Who This Book is For

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.

What You'll Learn

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.

Rhino.Geometry Essentials: Explore Rhino’s powerful Geometry module, which includes


classes and structs for creating and manipulating 3D geometry. You'll learn to generate
2 Preface

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.

Why GhPython and Rhino.Geometry?

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.

How to Use This Book

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.

A New Dimension in Design

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.

In the realm of computational design, Python serves as a bridge between conceptual


creativity and technical precision. Its robust libraries and frameworks enable architects to
explore parametric design, optimization, and simulation, empowering them to create
innovative architectural solutions. By harnessing Python, architects can delve deeper into
data-driven design, allowing for the exploration of multiple design alternatives based on
performance criteria and user needs.

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.

Why Python for Architects?


Python is a widely-used, high-level programming language known for its readability,
flexibility, and ease of use. While traditionally associated with fields like data science and
software development, Python has become increasingly popular in architecture. Why?
Because it enables architects to automate repetitive tasks, build custom tools, and create
complex generative designs that would be challenging with manual methods or traditional
CAD tools alone.

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.

Some advantages of using Python in modelling workflows include:

• Automation: Python can automate tedious modeling tasks, from generating


parametric buildings to visualizing the models on viewport and taking control over
working with the document.
• Customization: Build custom scripts that address your specific modelling needs
rather than relying on generic software functions.
5 GhPython Script Editor

• 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.

Setting Up Python in Rhino and Grasshopper


Before we dive into coding, let’s get Python set up in the context of Rhino and Grasshopper.
Here’s how to get started:

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

GhPython Script Editor


The Python Editor Interface: The editor has a simple interface where you can write code, run
it, and see any errors or messages. Familiarize yourself with the editor’s layout, as we’ll be
using it throughout the book.

Double click the GhPython component to open the script editor.


9 GhPython Script Editor

The scripts starts by importing rhinoscriptsyntax module, which is a library of functions


designed to interact with Rhino’s 3D modeling environment. These functions allow users to
perform a wide range of tasks, such as creating geometry, modifying objects, querying the
model, managing layers, and more.

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

Writing Your First Python Script


Let’s write a simple script to get you comfortable with the Python Editor in Grasshopper.

Example: Create a Basic Line

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:

• We import rhinoscriptsyntax, a library in RhinoCommon that provides simple


functions for interacting with Rhino.
• We define two points at specified coordinates and store them as variables.
• We then create a line using rs.AddLine, which takes the two points as arguments.

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.

How This Book is Structured


This book is organized to guide you from Python basics to more advanced applications
relevant to architecture:

• 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.

Key Tips for Learning Python


As you work through this book, here are some tips to help you succeed:

• 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.

Python Data Types


Understanding data types is essential as they form the foundation of programming logic.
Python, like all programming languages, has a range of data types that represent various
forms of information. These types—integers, floats, strings, and booleans—are the building
blocks of programming and serve essential functions in architectural coding.

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:

• Integers: Whole numbers, often used to represent counts or discrete values.


• Floats: Decimal numbers, useful for precise measurements.
• Strings: Sequences of characters, ideal for text labels and identifiers.
• Booleans: Logical values (True or False) that help control decision-making in code.

Each data type will be illustrated with architectural examples, so you can see how these
types apply in practical scenarios.

Integers and Floats


Integers and floats are Python’s numeric data types. They are particularly useful for storing
numerical values like dimensions, angles, and other quantitative attributes commonly used
in architectural design.

Integers

An integer is a whole number, positive or negative, without a decimal point. In an


architectural context, integers are often used to count things (e.g., number of floors,
columns, or windows) or set discrete values.

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

Example: Setting Dimensions with Integers and Floats

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 ("...").

Using Strings for Labels and Names

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.

room_type = "Living Room"

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.

Concatenation: Combine strings with the + operator.

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"

Example: Describing Room Information with Strings

# 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.

Using Booleans in Design Logic

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

Example: Setting Conditions with Booleans

In the following example, we’ll use booleans to check if a room’s width meets the minimum
accessibility requirement.

# Room width in meters


room_width = 1.2
# Minimum accessible width
min_accessible_width = 1.5
# Check if the room is accessible
is_accessible = room_width >= min_accessible_width
print("Is the room accessible?", is_accessible)
19 Python Data Types

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.

Type Casting in Python


Type casting in Python refers to the process of converting one data type into another. This is
a common operation when you need to perform operations on different types of data, or
when you want to ensure that your data is in a suitable format for processing. Python
provides several built-in functions that allow you to convert data types easily.

Implicit vs. Explicit Type Casting

Implicit Type Casting (Automatic Conversion):

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'>

Explicit Type Casting (Manual Conversion):

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'>

Summary of Python Data Types


Here’s a quick summary of the data types we covered:

• 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.

What Are Variables?


In Python, a variable is a name that holds a value, acting like a container to store information
that you’ll need throughout your code. Variables allow you to label data and use it efficiently
without retyping or recalculating values each time you need them. They’re foundational for
any program because they let you manage and update data dynamically. In architectural
programming, variables are useful for storing dimensions, quantities, conditions, and even
entire design parameters that change based on context.

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.

room_height = 3.0 # a float


room_name = "Living Room" # a string
floor_count = 5 # an integer
is_occupied = True # a boolean

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.

room_width = 5.0 # initial width


print("Initial room width:", room_width)
# Update the width
room_width = 6.5
print("Updated room width:", room_width)

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.

Working with Variables in Expressions


Variables can be used in expressions to perform calculations and derive new values. This is
particularly useful for geometric calculations and setting dimensions that depend on other
variables.

Example: Calculating Wall Area

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")

We use the variables wall_width and wall_height in a multiplication expression to calculate


the wall’s area, storing the result in wall_area.

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.

Naming Conventions and Best Practices


When naming variables, it’s essential to follow consistent conventions for readability and to
avoid conflicts with Python’s built-in keywords. Here are some tips:

• 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.

Example of Good Naming Practices

floor_area = 50 # square meters


max_occupancy = 20

This makes the code clear, as each variable’s name directly suggests its purpose.

Example: Combining Variables in a Script


Let’s combine what we’ve learned about variables in a practical example by setting up
variables for a building’s basic properties, calculating a few key values, and printing them.
23 Python Variables

# Basic building properties


building_name = "Community Center"
number_of_floors = 3
floor_height = 3.5 # height of each floor in meters
floor_area = 120 # floor area in square meters
# Calculating total building height and total area
total_height = number_of_floors * floor_height
total_area = number_of_floors * floor_area
# Printing results
print("Building Name:", building_name)
print("Total Height:", total_height, "meters")
print("Total Area:", total_area, "square meters")

In this script, we define variables for building_name, number_of_floors, floor_height, and


floor_area. We use these variables to calculate total_height and total_area, giving a quick
overview of the building’s basic dimensions and scale.

By using variables, we can easily update the building’s properties and recalculate values
without modifying each line individually.

Summary of Python Variables


In this section, we’ve covered how variables help store, update, and calculate information in
Python. To recap:

• 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

Example: Using Arithmetic Operators

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

>= Greater than or equal to


<= Less than or equal to
Example: Using Comparison Operators

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.")

In this example, we use logical operators to validate the dimensions of a room.

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

Example: Using Bitwise Operators

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)

In this example, bitwise operators manipulate the binary representations of integers.

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

room_types = ["living room", "bedroom", "kitchen"]


print("kitchen" in room_types) # Output: True
print("bathroom" not in room_types) # Output: True

In this example, membership operators check if certain room types exist in the room_types
list.
28 Operators in Python

Practical Application: Using Operators in an Architectural Context


Let’s apply these operators to compute some architectural values.

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.

Summary of Operators in Python


In this section, we covered the essential types of operators in Python:

• Arithmetic operators for mathematical calculations.


• Comparison operators for comparing values.
• Logical operators for combining conditions.
• Bitwise operators for binary manipulation.
• Assignment operators for assigning values.
• Identity operators for checking object references.
• Membership operators for testing presence in sequences.

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.

Data Collections in Python


Python’s data collections—lists, tuples, dictionaries, and sets—allow us to organize and
manage groups of data, an essential task in architecture where data is often grouped and
categorized.

• 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

To create a list, enclose items in square brackets [], separated by commas.

Example:

room_heights = [3.0, 2.8, 3.5, 3.2] # heights of different rooms in meters


room_names = ["Kitchen", "Living Room", "Bathroom", "Bedroom"]

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.

Accessing and Modifying List Items

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.

print(room_names[1]) # Output: Living Room


# Updating an item
room_heights[0] = 3.2
print(room_heights) # Output: [3.2, 2.8, 3.5, 3.2]
# Accessing a sublist
rooms_1_to_3_heights = room_heights[0:3]
30 Data Collections in Python

Adding and Removing Items

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"]

Looping Through a List

Lists are often used with loops to process each item.

for room in room_names:


print("Room:", room)

List Comprehension

For ease of code writing, lists could be used with loops in one line.

x = [room for room in room_names]


print(list(x)) # output ['Kitchen', 'Living Room', 'Bathroom', 'Bedroom']
# Or try
squares = [x*x for x in range(1,11)]
print(squares) #output [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# You can also add conditions
squares = [x*x for x in range(1,11) if x%2==0]
print(squares) #output [4, 16, 36, 64, 100]

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 created by placing items in parentheses ().

standard_room_size = (4.0, 3.0) # (width, height)

Tuples could be created by casting other data collections.

standard_room_size_list = [4.0, 3.0]


standard_room_size_tuple = tuple(standard_room_size_list)
print(type(standard_room_size_tuple)) # Output: <class 'tuple'>

Accessing Tuple Items

You access items in a tuple the same way as in a list, by index.

print("Room width:", standard_room_size[0]) # Output: 4.0

Updating Tuple Items

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

standard_room_size_tuple = (4.0, 3.0)


standard_room_size_tuple[0] = 5.0 # Output: TypeError: 'tuple' object does not support item assignment
#try casting
standard_room_size_list = list(standard_room_size_tuple)
standard_room_size_list [0] = 5.0
#convert the list to a tuple
standard_room_size_tuple = tuple(standard_room_size_list)
print(standard_room_size_tuple) # Output: (5.0, 3.0)

Unpacking tuples

Tuple items could be unpacked to different variables.

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]

tup1 = (10,20,30, 40, 50, 60)


x, *y, z = tup1
print ("x: ",x, "y: ", y, "z: ", z) #Output: x: 10 y: [20,30,40,50] z: 60

tup1 = (10,20,30, 40, 50, 60)


*x, y, z = tup1
print ("x: ",x, "y: ", y, "z: ", z) #Output: x: [10,20,30,40] y: 50 z: 60

Looping tuples

Like lists, tuples could be looped through.

# 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

You can join two tuples by adding them together

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

Retrieving item’s index

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)

Counting tuple’s items

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.

#creating empty dictionaries


room_dimensions = dict()
room_dimensions = {}

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.

Accessing and Modifying Dictionary Items

You access dictionary values using keys.

print("Kitchen dimensions:", room_dimensions["Kitchen"]) # Output: Kitchen dimensions: (4.0, 3.0)


34 Data Collections in Python

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.

print("Kitchen dimensions:", room_dimensions["Office"]) # Output: KeyError: 'Office'


print("Kitchen dimensions:", room_dimensions.get("Office")) # Output: KeyError: None
print("Kitchen dimensions:", room_dimensions.get("Office", "Not Found")) # Output: Not Found

Adding Items and Updating a value

dict["key"] = val
room_dimensions["Kitchen"] = (4.5, 3.0)

d1.update(d2)

d1.update([(k1, v1), (k2, v2)])

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

d3 = d1 | d2 # Union of two dictionary objects, retuning new object

d1 |= d2 # Augmented dictionary union operator

Removing Items

del dict['key’]

del dict

item = dict.pop(key) # Remove and return the item with the key

item = dict.popitem() # Remove and return the last item

dict.clear()

Dictionaries view objects

obj = dict.items()

Obj = dict.keys()

Obj = dict.values()

Looping Through a Dictionary

You can loop through a dictionary’s keys and values.

for room, dimensions in room_dimensions.items():


print(f"{room} dimensions are {dimensions}")

for item in dict:

for item in dict.items():

for key, value in dict.items():

for key in dict.keys():

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

#Using the .copy() method


d1 = {"a":11, "b":22, "c":33}
d2 = d1.copy()
d1["b"] = 100
print ("id:", id(d1), "dict: ",d1)
print ("id:", id(d2), "dict: ",d2) #returns an id that is different from 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}
}

for k,v in rooms.items():


print (k, ":", v)

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.

materials_used = {"Concrete", "Steel", "Glass"}

Sets can only contain immutable (hashable) objects including integers, floats, booleans,
strings, tuples, frozen sets, and bytes.

s1 = {1, 2, [3, 4, 5], 3,0, 1, 9}


print (s1) #Output: unhashable type: 'list'

s2 = {“Yassin", {“Architecture":99}}
print (s2) #Output: unhashable type: 'dict'

Accessing items in sets

Sets are unordered collections of data so items inside them are not indexed.

langs = {"C", "C++", "Java", "Python"}


for lang in langs:
print (lang)

Adding and Removing Items

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.

materials = ["Concrete", "Steel", "Glass", "Concrete", "Wood"]


unique_materials = set(materials)
print(unique_materials) # Output: {"Concrete", "Steel", "Glass", "Wood"}

Using the unpacking operator

set1 = {“Wood”, “Concrete”}


set2 = {“Concrete”, “Glass”}
materials_used = {*set1, *set2}

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

set1 = {“Wood”, “Concrete”}


set2 = set1.copy()
set1.add(“Glass”)
print(id(set1))
print(id(set2))
39 Data Collections in Python

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.

obj = array.array(typecode[, initializer])

Type Codes for Arrays

Some common type codes for arrays are:

i: signed integer, f: floating-point, d: double-precision floating-point

import array as arr


# Creating an array of integers
lengths = arr.array('i', [4, 5, 6, 7])
print(lengths) # Output: array('i', [4, 5, 6, 7])

Here, we create an array called lengths with integers representing room lengths.

Accessing and Modifying Array Elements

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.

Common Array Methods

• append(x): Adds an item x to the end of the array.


• insert(i, x): Adds an item x at index i.
• extend(iterable): Adds multiple items from an iterable to the end of the array.
• remove(x): Removes the first occurrence of x from the array.
41 Data Collections in Python

• pop(i): Removes and returns the item at index i.

# Appending a new length


lengths.append(8)
print(lengths) # Output: array('i', [4, 10, 6, 7, 8])

# Extending with multiple lengths


lengths.extend([9, 11])
print(lengths) # Output: array('i', [4, 10, 6, 7, 8, 9, 11])

# 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.

Iterating Over an 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.

# Iterating over lengths and calculating areas (assuming a fixed width of 3)


width = 3
for length in lengths:
area = length * width
print("Area:", area)
# Or
l = len(a)
i = 0
while i<4:
print(lengths[i]*width)
i+=1
# Or
l = len(a)
for i in range(l):
print(lengths[i]*width)

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

import array as arr

a = arr.array('i', [1, 2, 3, 4, 5])


b = a
print (id(a), id(b))

Reversing an array

import array as arr


a = arr.array('i', [10,5,15,4,6,20,9])
b = arr.array('i')
for i in range(len(a)-1, -1, -1):
b.append(a[i])
print (a, b)
# Another way is to cast the array to the list, reverse the list, and then cast it back to an array
a = arr.array('i', [10,5,15,4,6,20,9])
b = a.tolist()
b.reverse()
a = arr.array('i')
a.fromlist(b)

Sorting an array

import array as arr


a = arr.array('i', [10,5,15,4,6,20,9])
for i in range(0, len(a)):
for j in range(i+1, len(a)):
if(a[i] > a[j]):
temp = a[i];
a[i] = a[j];
a[j] = temp;
print (a)
# Or
from array import array as arr
a = arr.array('i', [10,5,15,4,6,20,9])
b=a.tolist()
b.sort()
a = arr.array('i')
a.fromlist(b)
print (a)
# Or use the sorted() method
import array as arr
a = arr.array('i', [31, 4, 5, 6, 9, 10, 15, 20])
a = sorted(a)
print (a)
43 Data Collections in Python

Joining arrays

import array as arr


a = arr.array('i', [10,5,15,4,6,20,9])
b = arr.array('i', [2,7,8,11,3,10])
for i in range(len(b)):
a.append(b[i])
print (a, b)
# Or
from array import array as arr
a = arr.array('i', [10,5,15,4,6,20,9])
b = arr.array('i', [2,7,8,11,3,10])
x=a.tolist()
y=b.tolist()
z=x+y
a=arr.array('i')
a.fromlist(z)
print (a)
# Or
import array as arr
a = arr.array('i', [10,5,15,4,6,20,9])
b = arr.array('i', [2,7,8,11,3,10])
a.extend(b)
print (a)

Arrays vs. Lists

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.

Performance: Arrays perform numerical operations faster due to type uniformity.

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)

Summary of Data Collections in Python


In this section, we covered Python’s primary data collections and how they’re used:

• Lists: Ordered, mutable collections, ideal for storing sequences.


• Tuples: Ordered, immutable collections, suitable for fixed data.
• Dictionaries: Key-value pairs, mutable collections, perfect for labeled information.
• Sets: Unordered, mutablle collections of unique items, useful for eliminating
duplicates.
• Arrays: Ordered, mutable, collections, bette used with numbers

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

Object-Oriented Programming (OOP) is a programming paradigm that organizes code into


objects, which represent real-world entities with attributes (data) and behaviors (methods).
OOP allows you to model complex systems by creating custom data structures that reflect
the architectural components you work with—such as walls, rooms, buildings, and
materials.

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

def move(self, dx, dy, dz):


x, y, z = self.position
self.position = (x+dx, y+dy, z+dz)

Now if you want to add a new object you can just call the function and add the attributes.
46 Object-Oriented Programming

benyamin = Dog(“benyamin”, 1, 1.5, “bones”, (2,2,0))


benyamin.move(1,1,0)

You can even access the objects properties

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.

Attributes and the __init__ Method


Attributes are characteristics of a class. In our Room example, attributes could include
properties like width, height, and name. The __init__ method is a special function called a
constructor that initializes the object’s attributes when the object is created.

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.

Creating an Instance/Object of the Class:


living_room = Room("Living Room", 6.0, 3.2)
print("Room Name:", living_room.name) # Output: Living Room
print("Width:", living_room.width) # Output: 6.0
print("Height:", living_room.height) # Output: 3.2
47 Object-Oriented Programming

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

To you the method, you call it by the object

living_room = Room("Living Room", 6.0, 3.2)


print("Living Room Area:", living_room.area(), "square meters") # Output: 19.2

Here, area is a method that uses self.width and self.height to calculate and return the room’s
area.

Modifying Objects Properties


living_roof.width = 0.8

Deleting Objects Properties


del living_room.width

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.

Making Attributes Private

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.

super().__init__(name) calls the constructor of BuildingComponent to initialize name.

Creating and Using an Inherited Class Instance:

office = Room("Office", 5.0, 3.0)


print("Room Name:", office.name) # Output: Office
print("Room Area:", office.area(), "square meters") # Output: 15.0

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.

Key Features of RhinoCommon Classes:

• Encapsulation: Each class encapsulates specific data (properties) and behavior


(methods) relevant to geometric entities and operations.
50 Object-Oriented Programming

• 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.

Common Classes in RhinoCommon

Some of the key classes in RhinoCommon are:

• Geometry Classes: These classes represent various geometric entities in 2D and 3D


space.
Point3d: Represents a point in 3D space.
Properties: X, Y, Z
Methods: DistanceTo(), Add(), Subtract()
import Rhino.Geometry as rg

point1 = rg.Point3d(1, 2, 3)
point2 = rg.Point3d(4, 5, 6)
distance = point1.DistanceTo(point2)
print(f"Distance: {distance}")

Curve: An abstract class for representing curves. Concrete implementations include


LineCurve, ArcCurve, and NurbsCurve.
Methods: Length, PointAt(), ToNurbsCurve()

line = rg.LineCurve(point1, point2)


print(f"Line Length: {line.Length}") # Output: Line Length: 5.196152422706632

Surface: Represents 2D surfaces, which can be flat or complex shapes.


Subclasses: PlaneSurface, NurbsSurface, RevolutionSurface
Methods: Area(), PointAt(), IsPointOn()
• Model Object Classes: These classes represent objects in the Rhino document.
RhinoObject: The base class for all objects in a Rhino document. It provides common
functionality for all Rhino model objects.
Brep: Represents a boundary representation (BRep) geometry. Useful for defining
complex shapes.
brep = rg.Brep.CreateFromSphere(rg.Sphere(point1, 2))
print(f"Brep Face Count: {brep.Faces.Count}") # Output: Brep Face Count: 1

Mesh: Represents a mesh object. Used for defining and manipulating polygonal
representations of surfaces.
51 Object-Oriented Programming

• Utility Classes: Utility classes in RhinoCommon provide additional functionalities to


enhance geometry manipulation and application logic.
Transform: Represents a geometric transformation (translation, rotation, scaling).
Methods: Translation(), Rotation(), Scaling()
transform = rg.Transform.Translation(10, 5, 0) # Move 10 units in X and 5 in Y
point1.Transform(transform)
print(f"Transformed Point: {point1}") # Output: Transformed Point: (11.0, 7.0, 3.0)

• Custom Classes in RhinoCommon: In addition to the built-in classes, you can


define your own classes that utilize RhinoCommon functionality. This allows you to
create tailored solutions for specific architectural tasks.
class ArchitecturalElement:
def __init__(self, name, geometry):
self.name = name
self.geometry = geometry # This can be a curve, surface, or any RhinoCommon object

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()}"

In this custom class, ArchitecturalElement represents an architectural object with a


name and geometry. The class includes a method to calculate the area if the
geometry is a surface.

Best Practices for Using Classes in RhinoCommon

• Use Inheritance Wisely: Take advantage of inheritance to create specialized classes


that share common behavior. This will make your code cleaner and easier to
maintain.
• Encapsulate Data: Protect the internal state of your classes by using private
attributes and providing public methods (getters/setters) to access or modify them.
• Leverage Polymorphism: Design your methods to accept objects of a base class
type, allowing you to work with multiple derived classes seamlessly.
• Keep Classes Focused: Ensure that each class has a single responsibility, adhering
to the Single Responsibility Principle (SRP) of object-oriented design.
• Document Your Classes: Use docstrings to explain the purpose and usage of your
classes and methods. This is especially useful when collaborating with others or
revisiting your code in the future.
52 Object-Oriented Programming

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.
"""

def __init__(self, name, geometry):


"""
Constructs all the necessary attributes for the ArchitecturalElement object.

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

Summary of Object-Oriented Programming


In this section, we explored the fundamentals of OOP and how it applies to architectural
coding:

• Classes and Attributes define objects with specific characteristics.


• Methods provide functionality for each object.
• Encapsulation organizes and protects data within each object.
• Inheritance promotes code reuse across related classes.
• Polymorphism enables flexible behavior based on context.

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 calculate_area(width, height):


return width * height

Practical Applications: Functions allow us to encapsulate code for repeated calculations


or transformations, such as scaling or rotating geometry in Grasshopper.

Defining and Calling Functions


To define a function, use def followed by the function name and a colon :. The code block
inside the function is indented.

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.

def calculate_wall_area(length, height):


return length * height
# Calling the function with arguments
area = calculate_wall_area(4.0, 2.8)
print("Wall Area:", area, "square meters") # Output: Wall Area: 11.2 square meters

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.

def calculate_volume(length, width, height):


return length * width * height
volume = calculate_volume(4.0, 3.0, 2.8)
print("Volume:", volume, "cubic meters") # Output: Volume: 33.6 cubic meters

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

greetings("Yassin") #Output: Hello Yassin

result = greetings(“Yassin”)
print(result) #Output: None

Passing by reference vs. passing by value


Changing variables inside a function gives this variable a new location with a different id after
calling the function if the variable is immutable. On contrary, if the variable is mutable, the
id of the variable inside the function after editing it remains the same and the original variable
gets modified as well.
55 Functions in Python

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

Types of functions arguments


Functions arguments in python can be introduced in different ways:

• Positional or required arguments


• Keyword arguments
• Default arguments
• Positional-only arguments
• Keyword-only arguments
• Arbitrary or variable-length arguments

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.

def add(a, b):


return a + b

# Calling the function with positional arguments


print(add(5, 3)) # Output: 8

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

def greet(name, message):


return f"{message}, {name}!"
# Using keyword arguments
print(greet(name="Yassin", message="Hello")) # Output: Hello, Yassin!
print(greet(message="Welcome", name="Yassin")) # Output: Welcome, Yassin!

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.

def add(a, b=10):


return a + b
# Calling the function with and without the default arguments
print(add(5)) # Output: 15
print(add(5, 3)) # Output: 8

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.

def divide(a, b, /):


return a / b
# Calling the function
print(divide(10, 2)) # Output: 5.0
print(divide(a=10, b=2)) # Error: can't use keyword arguments for positional-only arguments

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.

def order(item, *, quantity=1):


return f"Order placed for {quantity} {item}(s)"

# Calling the function


print(order("wall", quantity=5)) # Output: Order placed for 5 wall(s)
print(order("door")) # Output: Order placed for 1 door(s)
print(order("wall", 5)) # Error: quantity must be specified as a keyword argument

Python print() function has a keyword only argument with a default value of a space “ “ called
sep.

print ("Hello", "World", sep="-")

print ("Hello", "World", "-")

Arbitrary arguments allow a function to accept a variable number of arguments. Python


provides two ways for this:

• *args: Allows a function to accept any number of positional arguments as a tuple.


57 Functions in Python

• **kwargs: Allows a function to accept any number of keyword arguments as a


dictionary.

# Using *args for variable-length positional arguments


def calculate_total_area(*args):
return sum(args)
total_area = calculate_total_area(10, 12, 15, 20)
print("Total Area:", total_area) # Output: Total Area: 57

# Using **kwargs for variable-length keyword arguments


def describe_person(**kwargs):
return ", ".join(f"{key}: {value}" for key, value in kwargs.items())

print(describe_person(name="Alice", age=30, city="New York"))


# Output: name: Alice, age: 30, city: New York

• 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.

def paint_cost(area, cost_per_square_meter=15):


return area * cost_per_square_meter
# Calling with default cost
cost1 = paint_cost(20)
print("Cost with default rate:", cost1) # Output: Cost with default rate: 300
# Calling with custom cost
cost2 = paint_cost(20, 12)
print("Cost with custom rate:", cost2) # Output: Cost with custom rate: 240
58 Functions in Python

Here, paint_cost has a default cost_per_square_meter of 15. If a different cost is provided, it


overrides 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.

def myfunction(a: int, b: int):


c = a+b
return c

print (myfunction(10,20))

Return data types can have annotations as well.

def myfunction(a: int, b: int) -> int:


c = a+b
return c

you can put any expression which acts as the metadata for the arguments

def total(x : 'marks in Physics', y: 'marks in chemistry'):


return x+y

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(a: "physics", b:"Maths" = 20) -> int:


c = a+b
return c
print (myfunction.__annotations__)

{'a': 'physics', 'b': 'Maths', 'return': <class 'int'>}

def myfunction(*args: "arbitrary args", **kwargs: "arbitrary keyword args") -> int:
pass
print (myfunction.__annotations__)
59 Functions in Python

Scope and Lifetime of Variables


In Python, the scope of a variable determines where it can be accessed. Variables created
inside a function are local to that function, meaning they can’t be accessed outside it.

def room_dimensions():
length = 5
width = 3
return length * width

Print(length) #error length is not accessible outside the function

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

room_dimensions() # Call the function to define `length` globally


print(length) # Now `length` is accessible, Output: 5

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.

You can also use globals() to modify a global variable directly.

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}

In this example, locals() returns a dictionary of variables within local_example(), showing a


and b with their respective values.

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

Types of Functions in Python


Python has four function types.

• 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.

Python Built-in Functions


Python built-in functions are re-defined functions that come with Python. They perform
common tasks and are always available for use. Python has many built-in functions, such as
print(), len(), max(), sum(), etc.

• 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.

The lambda syntax is: lambda arguments : expression

area = lambda length, width: length * width


print("Area:", area(5, 3)) # Output: Area: 15

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)

factorial(6) #output: 720

#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

return 6*5*4*3*2*1 = 720.0


63 Functions in Python

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

Python math Module Functions


The Python math module provides a wide range of mathematical functions that are useful
for various calculations, ranging from basic arithmetic to more complex mathematical
operations. To use the math module, you need to import it into your Python script using the
line import math

No. Function Calculation


1 acos (x) Return the arc cosine of x, in radians.
2 acosh (x) Return the inverse hyperbolic cosine of x.
3 asin Return the arc sine of x, in radians.
4 asinh (x) Return the inverse hyperbolic sine of x.
5 atan Return the arc tangent of x, in radians.
6 atan2 Return atan(y/x), in radians.
7 atanh (x) Return the inverse hyperbolic tangent of x.
8 cbrt (x) Return the cube root of x.
9 cell(x) The ceiling of x: the smallest integer not less than x.
10 comb (x,y) Return the number of ways to choose x items from y iter repetition and without order.
11 copysign(x,y) Return a float with the magnitude of x but the sign of y.
12 cos (x) Return the cosine of x radians.
13 cosh (x) Return the hyperbolic cosine of x.
14 degrees Converts angle x from radians to degrees.
15 dist (x,y) Return the Euclidean distance between two points x and y.
16 e The mathematical constant e = 2.718281..., to available precision.
17 erf (x) Return the error function at x.
18 erfc (x) Return the complementary error function at x.
19 exp (x) Return e raised to the power x, where e = 2.718281...
20 exp2 (x) Return 2 raised to the power x.
21 expm1 (x) Return e raised to the power x, minus 1.

22 fabs(x) The absolute value of x in float

23 factorial(x) Return x factorial as an Integer.

24 floor (x) The floor of x: the largest integer not greater than x.

25 fmod (x,y) Always returns float, similar to x%y

26 frexp (x) Returns the mantissa and exponent for a given number x.

27 fsum (iterable) Sum of all numbers in any iterable, returns float.

28 gamma (x) Return the Gamma function at x..

29 gcd (x,y,z) Return the greatest common divisor of the specified integer arguments.

30 hypot Return the Euclidean norm, sqrt(x*x + y*y).

31 inf A floating-point positive infinity. Equivalent to the output of float('inf").

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.

38 ldexp (x,y) Return x * (2**y). This is the inverse of function frexp().

39 lgamma (x) Return the natural logarithm of the absolute value of the Gamma function at x.

40 log (x) Return the natural logarithm of x (to base e).

41 log10 (x) Return the base-10 logarithm of x.

42 log1p (x) Return the natural logarithm of 1+x (base e).

43 log2 (x) Return the base-2 logarithm of x.


The fractional and integer parts of x in a two-item tuple. Both parts have the same sign as x. The integer part is
44 modf (x)
returned as a float.
45 nan A floating-point "not a number" (NaN) value.

46 nextafter (x,y) Return the next floating-point value after x towards y.

47 perm (x,y) Return the number of ways to choose x items from y items without repetition and with order.

48 pi The mathematical constant π = 3.141592..., to available precision.

49 pow (x,y) Returns x raised to y

50 prod (iterable) Return the product of all the elements in the input iterable.

51 radians Converts angle x from degrees to radians.

52 remainder (x,y) Returns the remainder of x with respect to y

53 sin (x) Return the sine of x radians.

54 sinh (x) Return the inverse hyperbolic sine of x.

55 sqrt (x) Return the square root of x.

56 tan (x) Return the tangent of x radians.

57 tanh (x) Return the hyperbolic tangent of x.

58 tau The mathematical constant τ = 6.283185..., to available precision.

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.

Summary of Functions in Python


In this section, we covered:

• Function basics: defining and calling functions.


• Parameters and return values: customizing function behavior and getting results.
• Default and variable-length arguments: enhancing flexibility.
• Lambda functions: for quick, small functions.
• Scope: understanding where variables live in your code.
66 Control Flow

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.

This section covers:

Conditional statements: Making decisions in code with if, elif, and else.

Loops: Repeating tasks with for and while.

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:

• The if statement checks if the wall is thinner than 0.1 meters.


• The elif statement checks if it’s thicker than 0.5 meters.
• The else statement executes if neither of the above conditions is true.

Nested Conditional Statements


You can nested Conditional Statements inside each other in Python, to decide multiple
checks.

ratio = 65
result = 0

if ratio < 50 and ratio > 45:


result = ratio + (50-ratio)
else:
if ratio > 90:
result = ratio + 0.4*ratio
elif ratio > 80:
result = ratio + 0.3*ratio
elif ratio > 70:
result = ratio + 0.2*ratio
else:
result = ratio + 0.1*ratio

print(result)
68 Control Flow

Logical Operators
You can use logical operators (and, or, not) to combine multiple conditions in a single
statement.

def is_ideal_room(length, width):


if length >= 3 and width >= 3:
return "Room size is ideal."
else:
return "Room size needs adjustment."
print(is_ideal_room(3, 2.5)) # Output: Room size needs adjustment.

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")

process_coordinates((0, 0)) # Output: Origin


process_coordinates((3, 0)) # Output: Point on the X-axis at x=3
process_coordinates((0, 4)) # Output: Point on the Y-axis at y=4
process_coordinates((2, 5)) # Output: Point at coordinates x=2, y=5

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

You can add a guard (if condition) to further refine cases.

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.

Using strings, lists and tuples with For Loops

room_areas = [20, 15, 30, 10]


for area in room_areas:
print("Room Area:", area, "square meters")

This for loop iterates over each item in room_areas, printing each room’s area.

Using range with For Loops

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.

Using dictionaries with For Loops

numbers = {4:"New Cairo", 8:"Heliopolis", 12:"Nasr City"}


print('maximum number of floors could be', end = ': ')

for x in numbers:
print(x, end = ' ')

for x in numbers:
print(x, ":", numbers[x])

for x in numbers.items():
print(x)

for x,y in numbers.items():


print(x, ":", y)

for x in numbers.keys():
print(x, ":", numbers[x])

For loops examples

#Factorial of a number n! = 1 * 2 * 3 * ... * n

factorial = 1
n = 7

for i in range(1, n+1):


factorial = factorial*i

print("The factorial of {} is {}".format(n, factorial))

#Sequence Indexing
#Indices = range(len(sequence))

numbers = [2, 6, 7, 12, 17]


indices = range(len(numbers))

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.

for num in range(7):


print("Iteration no. {}".format(num))
else:
print("for loop is over")
print("end of for loop")

#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.")

#prime number check


n = 13
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
print(f"{n} is not a prime number.")
break
else:
print(f"{n} is a prime number.")

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

while i <= 10:


j = 1
while j <= 10:
k = i * j
print("{:3d}".format(k), end=' ')
j += 1
print()
i += 1
#some output as the last example
75 Control Flow

Loop Control Statements


Python offers control statements to manage loop behavior:

• break: Exits the loop entirely.


• continue: Skips the current iteration and moves to the next.
• pass: Does nothing and acts as a placeholder.

Using break

Use break to exit a loop when a condition is met.

room_areas = [20, 15, 30, 10]


for area in room_areas:
if area > 25:
print("Large room detected. Stopping check.")
break
print("Room Area:", area)

In this example, the loop exits when a room with an area greater than 25 square meters is
found.

Using continue

Use continue to skip the current iteration when a condition is met.

room_areas = [20, 15, 30, 10]


for area in room_areas:
if area < 15:
continue # Skip small rooms
print("Room Area:", area)

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]

for num in numbers:


if num % 2 == 0:
pass # Even number, do nothing for now
else:
print(f"{num} is odd")
# Output
1 is odd
3 is odd
5 is odd

Summary of Control Flow


In this section, we covered the essentials of control flow:

• Conditional statements let you make decisions in your code.


• For and while loops allow for efficient repetition.
• Loop control statements help manage loop execution for complex conditions.

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.

Using Rhino.Geometry Namespace


In order to use RhinoCommon for geometry modelling you will need to import the
Rhino.Geometry namespace by adding to the code: import Rhino.Geometry.

In this case to create a Point3d object you will need to type: a =


Rhino.Geometry.Point3d(1,1,0) or a = Rhino.Geometry.Point3d(x,y,z) if you want to assign
slider to the inputs of the component.

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 vs Structs vs Enums


In programming, classes and structs are both data structures used to encapsulate data and
functionality, but they differ in certain aspects depending on the programming language.
Enums (short for "enumerations") are a way to define a set of named, constant values in
programming.

General Difference between Classes and Structs


In languages like Python, C++, and C#, the distinction between classes and structs varies:

1. Default Access Levels:


In C++: in a class, members (variables and methods) are private by default, meaning
they are accessible only within the class. In a struct, members are public by default,
meaning they are accessible from outside the struct.
In C#: Both class and struct members are private by default, but the key differences
are in behavior and memory allocation (discussed below).
2. Memory Allocation:
Classes are typically allocated on the heap (dynamic memory). This means they can
have flexible memory usage but require garbage collection or manual memory
management.
Structs are typically allocated on the stack (static memory) in languages like C++ and
C#. This makes them more lightweight and faster to access, but they are usually
limited in size because the stack is smaller than the heap.
3. Reference vs. Value Type:
78 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.

Class vs. Struct in Python


Python doesn’t have a traditional struct keyword as in C++ or C#. Instead, Python classes
are used to create objects and can function similarly to both classes and structs from other
languages.

Python supports namedtuples or dataclasses as lightweight alternatives that mimic structs,


mainly used to store simple data without behavior (methods).

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.

Example of an Enum in Python

from enum import Enum


class LoftType(Enum):
Normal = 0
Loose = 1
Tight = 2
Straight = 3
Developable = 4
Uniform = 5

When calling an enum you can just call the ‘EnumClass.Option’ for example
LoftType.Normal.

Rhino.Geometry Namespace Objects


In this section, we present different constructors, properties, and methods for various
geometry classes and structs that relate to architects’ every-day modelling tasks.
80 Rhino.Geometry Namespace Objects

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)

Examples of Point3d Struct PROPERTIES


x_coordinate = my_point.X
my_point = rg.Point3d.Unset
point_at_origin = rg.Point3d.Origin #construct a point at (0,0,0) coordinates

Examples of Point3d Struct METHODS


distance_between_two_pts = my_point.DistanceTo(another_point)
81 Rhino.Geometry Namespace Objects

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)

Examples of Vector3d Struct PROPERTIES


z_dircetion = my_vector.Z
my_vector = rg.Vector3d.Unset
vector_z_axis = rg.Vector3d.ZAxis #construct a vector at (0,0,1) dircetion
vector_length = rg.Vector3d.Length

Examples of Vector3d Struct METHODS


cross_product_vector = rg.Vector3d.CrossProduct(vector_a, vector_b)
reversed_vector_a = rg.Vector3d.Negate(vector_a)
reversed_vector_a = vector_a.Reverse()
perpendicular_vector = rg.Vector3d.PerpendicularTo(vector_a)
unitized_vector_a = vector_a.Unitize()
vector_angle = rg.Vector3d.VectorAngle(vector_a, vector_b)

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

Examples of Plane Struct PROPERTIES


plane_normal_vector3d = my_plane.Normal
plane_origin_point3d = my_plane.Origin
plane_origin_point3d_x_coord = my_plane.OriginX
my_plane = rg.Plane.Unset
my_xy_plane = rg.Plane.WorldXY
my_xz_plane = rg.Plane.WorldZX
my_yz_plane = rg.Plane.WorldYZ
plane_z_axis_vector3d = my_plane.ZAxis
plane_x_axis_vector3d = my_plane.XAxis

Examples of Plane Struct METHODS


closest_point = my_plane.ClosestPoint(point_a)
distance_between_point_and_projection_on_plane = my_plane.DistanceTo(test_point)
flipped_plane = my_plane.Flip()
rotated_plane_about_its_origin = my_plane.Rotate(angle, vector_axis)
rotated_plane = my_plane.Rotate(angle, vector_axis, center_point_of_rotation)
moved_plane = my_plane.Translate(vector3d_of_motion)

Interval Struct
Examples of Interval Struct CONSTRUCTORS

my_interval = rg.Interval(min_value, max_value)

Examples of Interval Struct PROPERTIES


interval_length = my_interval.Length
interval_min_value = my_interval.Min
interval_mid_value = my_interval.Mid
interval_max_value = my_interval.Max
my_interval.T0 = value #change interval lower bound to a certain number
my_interval.T1 = value #change interval upper bound to a certain number
my_interval = rg.Interval.Unset

Examples of Interval Struct METHODS


reversed_interval = my_interval.Reverse() #changes to [-T1, -T0]
swapped_interval = my_interval.Swap() #changes to [T1, T0]

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

Examples of Line Struct PROPERTIES

start_point = line_a.From
end_point = line_a.End
line_length = line_a.Length

Examples of Line Struct METHODS


closest_point = line_a.ClosestPoint(test_point, bool_value_for_limit_to_finite_segment)
shortest_dist_bet_line_and_test_point = line_a.DistanceTo(test_point,
bool_value_for_limit_to_finite_segment)
shortest_dist_bet_line_and_test_point = line_a.MinimumDistanceTo(test_point)
longest_dist_bet_line_and_test_point = line_a.MaximumDistanceTo(test_point)
extended_line = line_a.Extend(start_length, end_length)
filpped_line = line_a.Flip()
point_on_line = line_a.PointAt(parameter_value)
point_at_length = line_a.PointAt(metric_distance)
nurbs_curve_from_line = line_a.ToNurbsCurve() # use to get use of NurbsCurve methods and properties
84 Rhino.Geometry Namespace Objects

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)

Examples of Box Struct PROPERTIES


box_area = my_box.Area
box_center_point = my_box.Center
bounding_box = my_box.BoundingBox
box_orientation_plane = my_box.Plane
box_volume = my_box.Volume
box_x_interval = my_box.X

Examples of Box Struct METHODS


closest_point = my_box.ClosestPoint(point_a)
box_corners_array = my_box.GetCorners()
point_on_box = my_box.PointAt(x_param, y_ param, z_ param) #params from 0 to 1.
brep_from_box = my_box.ToBrep() # use to get use of Brep methods and properties

Sphere Struct
Examples of Sphere Struct CONSTRUCTORS
my_sphere = rg.Sphere(center_point, radius)

Examples of Sphere Struct PROPERTIES


sphere_radius = my_sphere.Eadius
sphere_center_point = my_sphere.Center
bounding_box = my_sphere.BoundingBox
sphere_diameter = my_sphere.Diameter
85 Rhino.Geometry Namespace Objects

Examples of Sphere Struct METHODS


closest_point = my_sphere.ClosestPoint(point_a)
rotated_sphere = my_sphere.Rotate(angle_value, rotation_axis_vector) #rotates around center point
rotated_sphere = my_sphere.Rotate(angle_value, rotation_axis_vector, center_point)
brep_from_sphere = my_sphere.ToBrep() # use to get use of Brep methods and properties

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.

Examples of Curve Class PROPERTIES


Interval = my_curve.Domain
my_curve.Domain = interval_a
start_point = my_curve.PointAtStart
end_point = my_curve.PointAtEnd
86 Rhino.Geometry Namespace Objects

Examples of 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.

Examples of LineCurve Class CONSTRUCTORS


my_line_curve = rg.LineCurve(line_object)
my_line_curve = rg.LineCurve(point_a, point_b)
88 Rhino.Geometry Namespace Objects

Examples of LineCurve Class PROPERTIES


Interval = my_line_curve.Domain
my_line_curve.Domain = interval_a
start_point = my_line_curve.PointAtStart
end_point = my_line_curve.PointAtEnd

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.

Examples of NurbsCurve Class CONSTRUCTORS


NurbsCurve class constructors doesnot have a direct way to create a new Nurbs curve.
Rather, you can create new nurbs curves using the ‘.CreateControlPointCurve’ curve class
methods or with the ‘NurbsCurve’ class methods which start with ‘.CreateFrom’.

Examples of NurbsCurve Class PROPERTIES


Interval = my_nurbs_curve.Domain
my_nurbs_curve.Domain = interval_a
start_point = my_nurbs_curve.PointAtStart
end_point = my_nurbs_curve.PointAtEnd
nurbs_curve_control_point_list = my_nurbs_curve.Points
control_point = nurbs_curve_control_point_list.Item[index]

Examples of NurbsCurve Class METHODS


My_nurbs_curve = rg.NurbsCurve.CreateFromArc(my_arc)
My_nurbs_curve = rg.NurbsCurve.CreateFromCircle(my_circle)
My_nurbs_curve = rg.NurbsCurve.CreateFromEllipse (my_ellipse)
My_nurbs_curve = rg.NurbsCurve.CreateFromLine(my_line)
My_nurbs_curve = rg.NurbsCurve.CreateFromFitPoints (points_list, tolerance, bool_periodic)
My_nurbs_curve = rg.NurbsCurve.CreateParabolaFromPoints (my_start_point, my_inner_point, my_end_point)

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

Examples of Surface Class METHODS


closest_point_params = my_surface.ClosestPoint(point_a)
closest_point = my_surface.PointAt(closest_point_params[0], closest_point_params[1])
my_extrusion = rg.Surface.CreateExtrusion(my_curve, direction_vector3d)
my_extrusion = rg.Surface.CreateExtrusionToPoint(my_curve, my_point)
my_curvature = my_surface.CurvatureAt(param_u, param_v)
surface_domain_at_u = my_surface.Domain(0)
surface_domain_at_v = my_surface.Domain(1)
surface_degree_at_u = my_surface.Degree(0)
surface_degree_at_v = my_surface.Degree(1)
frame_plane_at_param = my_surface.FrameAt(param_u, param_v)
bounding_box = my_surface.GetBoundingBox(plane)
my_interpolated_nurbs_curve_on_surface = my_surface.InterpolatedCurveOnSurface(points_list, tolerance)
normal_vector = my_surface.NormalAt(param_u, param_v)
point_on_surface = my_surface.PointAt(param_u, param_v)
my_rebuilt_surface = my_surface.Rebuild(u_degree, v_degree, u_point_count, v_point_count)
my_rebuilt_surface_on_u_direction = my_surface.RebuildOneDirectuion(0, point_count, LoftType.Normal,
reftit_tolerance)
my_reversed_surface_at_u = my_surface.Reverse(0)
my_reversed_surface_at_v = my_surface.Reverse(1)
my_rotated_surface = my_surface.Rotate(angle_radians, rotation_axis_vector, rotation_center_point)
my_scaled_surface = my_surface.Scale(scale_factor)
my_normalized_u_surface = my_surface.SetDomain(0, my_interval/rg.Interval(0,1))
my_normalized_v_surface = my_surface.SetDomain(1, my_interval/rg.Interval(0,1))
my_split_surface_array_at_u = my_surface.Split(0, param)
my_split_surface_array_at_v = my_surface.Split(1, param)
my_brep_surface = my_surface.ToBrep()
my_nurbs_surface = my_surface.ToNurbsSurface()
my_moved_surface = my_surface.Translate(x, y, z)
my_sub_surface = my_surface.Trim(interval_u, interval_v)
my_increased_u_degree_nurbs_surface = my_ nurbs_surface.IncreaseDegreeU(u_desired_degree)
my_increased_v_degree_nurbs_surface = my_nurbs_surface.IncreaseDegreeV(v_desired_degree)
my_iso_curve_at_u = my_surface.IsoCurve(0, constant_param)
my_iso_curve_at_v = my_surface.IsoCurve(1, constant_param)

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.

Examples of NurbsSurface Class CONSTRUCTORS


NurbsSurface class constructors doesnot have a direct way to create a new Nurbs curve.
Rather, you can create new nurbs surfaces using the ‘.Create’ starting NurbsSurface class
methods.
90 Rhino.Geometry Namespace Objects

Examples of NurbsSurface Class METHODS


my_nurbs_cylinder = rg.NurbsSurface.CreateFromCylinder(my_cylinder)
my_nurbs_sphere = rg.NurbsSurface.CreateFromSphere(my_sphere)
my_nurbs_cone = rg.NurbsSurface.CreateFromCone(my_cone)
my_nurbs_surface = rg.NurbsSurface.CreateFromCorners(point_a, point_b, point_c)
my_nurbs_surface = rg.NurbsSurface.CreateFromCorners(point_a, point_b, point_c, point_d)
my_nurbs_surface = rg.NurbsSurface.CreateFromPlane(my_plane, u_interval, v_interval, u_degree, v_degree,
u_point_count, v_point_count)
my_nurbs_surface = rg.NurbsSurface.CreateFromPoints(points_list, u_count, v_count, u_degree, v_degree)
my_nurbs_surface = rg.NurbsSurface.CreateNetworkSurface(curves_list, continuity_integer, edge_tolerance,
interior_tolerance, angle_tolerance)
my_periodic_nurbs_surface = rg.NurbsSurface.CreatePeriodicSurface(surface, u)
my_revolve_nurbs_surface = rg.NurbsSurface.CreateRailRevolvedSurface(profile_curve, rail_curve,
axis_line, scale_height_bool)
my_ruled_nurbs_surface = rg.NurbsSurface.CreateRuledSurface(curve_a, curve_b)
my_nurbs_surface_through_points = rg.NurbsSurface.CreateThroughPoints(points_list, u_count, v_count,
u_degree, v_degree, u_closed_bool, v_closed_bool)

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.

Examples of Brep Class PROPERTIES


my_brep_edges_list = my_brep.Edges
my_brep_edges_count = my_brep.Edges.Count
my_brep_edge_at_index = my_brep.Edges[index]
my_brep_faces_list = my_brep.Faces
my_brep_face_at_index = my_brep.Faces[index]
my_brep_surfaces_list = my_brep.Surfaces
my_brep_surface_at_index = my_brep.Surfaces[index]
my_brep_vertices_list = my_brep.Vertices
my_brep_vertix_at_index = my_brep.Vertices[index]
91 Rhino.Geometry Namespace Objects

Examples of Brep Class METHODS


my_modified_brep_array = my_brep.Append(my_brep)
my_capped_brep = my_brep.CapPlanarHoles(tolerance)
closest_point = my_brep.ClosestPoint(test_point)
my_baseball_sphere_brep = rg.Brep.CreateBaseballSphere(center_point, radius, tolerance)
my_solid_difference_brep_array = rg.Brep.CreateBooleanDifference(brep_a, brep_b, tolerance)
my_solid_difference_brep_array = rg.Brep.CreateBooleanDifference(brep_a_list, brep_b_list, tolerance)
my_solid_intersection_brep_array = rg.Brep.CreateBooleanIntersection(brep_a, brep_b, tolerance)
my_solid_intersection_brep_array = rg.Brep.CreateBooleanIntersection(brep_a_list, brep_b_list,
tolerance)
my_solid_union_brep_array = rg.Brep.CreateBooleanUnion(brep_a_list, tolerance)
my_split_brep_array = rg.Brep.CreateBooleanSplit(brep_a, brep_b, tolerance)
my_split_brep_array = rg.Brep.CreateBooleanSplit(brep_a_list, brep_b_list, tolerance)
my_contour_lines_array = rg.Brep.CreateContourCurves(my_brep, section_plane)
my_contour_lines_array = rg.Brep.CreateContourCurves(my_brep, start_point, end_point, interval_float)
my_edge_surface_brep = rg.Brep.CreateEdgeSurface(curves_list)
my_edge_filleted_brep_array = rg.Brep.CreateFilletEdges(my_brep, edge_index, start_radii, end_radii,
BlendType.Fillet, RailType.DistanceFromEdge, tolerance)
my_brep_from_box = rg.Brep.CreateFromBox(my_box)
my_brep_from_cone = rg.Brep.CreateFromCone(my_cone)
my_brep_from_cylidner = rg.Brep.CreateFromCylinder(my_cylinder)
my_brep_from_sphere = rg.Brep.CreateFromSphere(my_sphere)
my_brep_from_3_corners = rg.Brep.CreateFromCornerPoints(point_a, point_b, point_c, tolerance)
my_brep_from_4_corners = rg.Brep.CreateFromCornerPoints(point_a, point_b, point_c, point_d, tolerance)
my_lofted_brep_array = rg.Brep.CreateFromLoft(curves_list, unset_point_a, unset_point_b,
LoftType.Normal, closed_bool)
my_revolved_brep = rg.Brep.CreateFromRevSurface(revolved_surface, cap_start_bool, cap_end_bool)
my_brep = rg.Brep.CreateFromSurface(my_surface)
my_sweep_1_brep_array = rg.Brep.CreateFromSweep(rail_curve, shape_curve, closed_bool, tolerance)
my_sweep_1_with_multiple_shapes_brep_array = rg.Brep.CreateFromSweep(rail_curve, shape_curves_list,
closed_bool, tolerance)
my_sweep_2_brep_array = rg.Brep.CreateFromSweep(rail_curve_1, rail_curve_2, shape_curve, closed_bool,
tolerance)
my_sweel_2_with_multiple_shapes_brep_array = rg.Brep.CreateFromSweep(rail_curve_1, rail_curve_2,
shape_curves_list, closed_bool, tolerance)
my_patch_surface_brep = rg.Brep.CreatePatch(geometry_base_list_of_curves, starting_surface, tolerance)
my_patch_surface_brep = rg.Brep.CreatePatch(geometry_base_list_of_curves, u_spans_integer,
v_spans_integer, tolerance)
my_pipe_brep_array = rg.Brep.CreatePipe(rail_curve, radius, local_blending_bool, PipeCapMode.Flat,
fit_rail_bool, absolute_tolerance, angle_tolerance)
my_solid_closed_polysurface_brep_array = rg.Brep.CreateSolid(breps_list, tolerance)
my_double_walled_pipe_brep_array = rg.Brep.CreateThickPipe(rail_curve, radius_a, radius_b,
local_blending_bool, PipeCapMode.Flat, fit_rail_bool, absolute_tolerance, angle_tolerance)
my_duplicate_brep_geometry_base = my_brep.Duplicate()
my_duplicate_brep = my_brep.DuplicateBrep()
my_flipped_brep = my_brep.Flip()
brep_area = my_brep.GetArea(relative_tolerrance, absolute_tolerance)
bounding_box = my_brep.GetBoundingBox(plane)
my_brep_regions_array = my_brep.GetRegions()
brep_volume = my_brep.GetVolume(relative_tolerance, absolute_tolerance)
brep_wireframe_curves_array = my_brep.GetWireframe(density_integer (between-1 and 99))
my_joined_breps = my_brep.Join(brep_b, tolerance, comapct_bool)
my_joined_breps_array = rg.Brep.JoinBreps(brep_list, tolerance, angle_tolerance)
my_edges_joined_brep = my_brep.JoinNakedEdges(tolerance)
my_merged_brep = rg.Brep.MergeBreps(breps_list, tolerance)
my_brep_with_merged_coplanar_faces = my_brep.MergeCoplanarFaces(tolerance, angle_tolerance)
my_brep_with_joined_surfaces = rg.Brep.MergeSurfaces(brep_a, brep_b, tolerance, angle_tolerance)
my_brep_with_joined_surfaces = rg.Brep.MergeSurfaces(surface_a, surface_b, tolerance, angle_tolerance)
my_brep_with_removed_holes = my_brep.RemoveHoles(tolerance)
92 Rhino.Geometry Namespace Objects

my_rotated_brep = my_brep.Rotate(radians_angle, rotation_axis_vector, rotation_center_point)


my_scale_brep = my_brep.Scale(scale_factor)
my_moved_brep = my_brep.Translate(x,y,z)
my_split_brep_array = my_brep.Split(cutter_brep, intersection_tolerance)
my_split_brep_array = my_brep.Split(cutter_breps_list, intersection_tolerance)
my_split_brep_array = my_brep.Split(cutter_curves_list, intersection_tolerance)
my_split_brep_array = my_brep.Split(cutter_geometry_base_list, normal_vector, plan_view_bool,
intersection_tolerance)
my_trimmed_brep_array = my_brep.Trim(cutter_brep, intersection_tolerance)
my_trimmed_brep_array = my_brep.Trim(cutter_plane, intersection_tolerance)

Polyline Class
The polyline class enherits some of its properties and methods from the Point3dList Class
which exists in the Rhino.Collections namespace.

To show polylines on viewport, convert the polyline object to a PolylineCurve object:


my_polyline.ToPolylineCurve()
93 Rhino.Geometry Namespace Objects

Examples of Polyline Class CONSTRUCTORS

my_polyline = rg.Polyline(points_list)

Examples of Polyline Class PROPERTIES


points_count = my_polyline.Count
first_point = my_polyline.First
last_point = my_polyline.Last
point_at_index_2 = my_polyline.Item[2]
my_polyline.item[2] = rg.Point3d(1,1,0)
polyline_length = my_polyline.Length
segments_count = my_polyline.SegmentCount

Examples of Polyline Class METHODS


extended_polyline = my_polyline.Add(my_point3d)
polyline_center_point = my_polyline.CenterPoint()
poly_line_closest_point = my_polyline.ClosestPoint(my_point3d)
my_polyline = rg.Polyline.CreateByJoiningLines(lines_list, tolerance, bool_split_at_intersections)
my_polyline_polygon = rg.Polyline.CreateCircumscribedPolygon(my_circle, sides_conunt)
my_polyline_polygon = rg.Polyline.CreateIncribedPolygon(my_circle, sides_conunt)
my_polyline_polygon = rg.Polyline.CreateStarPolygon(my_circle, radius, corners_count)
my_modified_polyline = my_polyline.Insert(index, my_point)
my_point_at_param = my_polyline.PointAt(param)
my_modified_polyline = my_polyline.RemoveAt(index)
my_reversed_polyline = my_polyline.Reverse()
my_polyline_segment = my_polyline.SegmentAt(index)
my_tangent = my_polyline.TangentAt(param)
my_nurbs_curve = my_polyline.ToNurbsCurve()
my_polyline_curve = my_polyline.ToPolylineCurve()
my_trimmed_polyline = my_polyline.Trim(domain_interval)
my_duplicate_polyline = my_polyline.Duplicate()

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.

Examples of PolylineCurve Class CONSTRUCTORS

my_polyline_curve = rg.PolylineCurve(points_list)

Triangle3d Struct
Examples of Triangle3d Struct CONSTRUCTORS

my_triangle = rg.Triangle3d(point_a, point_b, point_c)


94 Rhino.Geometry Namespace Objects

Examples of Triangle3d Struct PROPERTIES


point_a = my_triangle.A
line_ab = my_triangle.AB
angle_at_a = my_triangle.AngleA
triangle_area = my_triangle.Area
area_center = my_triangle.AreaCenter
circumcenter = my_triangle.Circumcenter
circumcircle = my_triangle.Circumcircle
perpendicular_ab_line = my_triangle.PerpendicularAB

Examples of Triangle3d Struct METHODS


closest_point = my_triangle.ClosestPointOnBoundary(test_point)
point_on_triangle = my_triangle.PointAlongBoundary(param)
my_triangle_to_polyline = my_triangle.ToPolyline()
my_modified_triangle = my_triangle.WithA(new_point_a)

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)

Examples of Arc Struct PROPERTIES


arc_angle = my_arc.Angle
my_arc.Angle = 3.14
arc_center = my_arc.Center
my_arc.Center = my_point
end_point = my_arc.EndPoint
mid_point = my_arc.MidPoint
start_point = my_arc.StartPoint
arc_length = my_arc.Length
arc_radius = my_arc.Radius

Examples of Arc Struct METHODS


closest_point = my_arc.ClosestPoint(point_a)
point_on_arc = my_arc.PointAt(param)
my_reversed_arc = my_arc.Reverse()
my_tangent = my_arc.TangentAt(param)
my_nurbs_curve_arc = my_arc.ToNurbsCurve()
my_trimmed_arc = my_arc.Trim(trimming_domain_interval)
95 Rhino.Geometry Namespace Objects

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.

Examples of ArcCurve Class CONSTRUCTORS


my_arc_curve = rg.ArcCurve(my_circle, start_param, end_param)
my_arc_curve = rg.ArcCurve(my_arc, start_param, end_param)

Examples of ArcCurve Class PROPERTIES


my_arc = my_arc_curve.Arc

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)

Examples of Rectangle3d Struct PROPERTIES


rectangle_area = my_rectangle.Area
rectangle_center_point = my_rectangle.Center
bounding_box = my_rectangle.BoundingBox
rectangle_height = my_rectangle.Height
rectangle_width = my_rectangle.Width
rectangle_plane = my_rectangle.Plane
rectangle_x = my_rectangle.X
my_rectangle.X = 10

Examples of Rectangle3d Struct METHODS


closest_point = my_rectangle.ClosestPoint(point_a)
my_second_corner = my_rectangle.Corner(1)
point_on_rectangle = my_rectangle.PointAt(param)
point_on_rectangle = my_rectangle.PointAt(x,y)
nurbs_curve_rectangle = my_rectangle.ToNurbsCurve()
polyline_rectangle = my_rectangle.ToPolyline()
96 Rhino.Geometry Namespace Objects

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)

Examples of Circle Struct PROPERTIES


circle_center_point = my_circle.Center
bounding_box = my_circle.BoundingBox
circle_diameter = my_circle.Diameter
circle_normal = my_circle.Normal
circle_plane = my_circle.Plane
circle_radius = my_circle.Radius

Examples of Circle Struct METHODS


closest_point = my_circle.ClosestPoint(point_a)
point_on_circle = my_circle.PointAt(param)
my_reversed_circle = my_circle.Reverse()
my_rotated_circle = my_circle.Rotate(angle, axis_vector)
tangent_at_param = my_circle.TangentAt(param)
my_nurbs_curve_circle = my_circle.ToNurbsCurve()
my_moved_circle = my_circle.Translate(translation_vector)

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.

Examples of Mesh Class PROPERTIES


mesh_faces = my_mesh.Faces
mesh_face_1 = mesh_faces.Item(1)
mesh_normals = my_mesh.Normals
mesh_normal_1 = mesh_normals.Item(1)
mesh_vertices = my_mesh.Vertices
mesh_vertex_1 = mesh_vertices.Item(1)

Examples of Mesh Class METHODS


closest_point = my_mesh.ClosestPoint(point_a)
my_extruded_mesh = rg.Mesh.CreateExtrusion(profile_curve, direction_vector3d)
my_box_mesh = rg.Mesh.CreateFromBox(my_box, x_count, y_count, z_count)
my_pipe_mesh = rg.Mesh.CreateFromCurvePipe(my_curve, radius, segments_no., accuracy_integer,
MeshPipeCapStyle.Flat, faceted_bool, intervals_list) #intervals_list could be left None
my_cylinder_mesh = rg.Mesh.CreateFromCylinder(cylinder, vertical_faces_number, around_faces_number,
cap_bottom_bool, cap_top_bool)
my_sphere_mesh = rg.Mesh.CreateFromSphere(my_sphere, x_count, y_count)
my_surface_mesh = rg.Mesh.CreateFromSurface(my_surface)
my_mesh_with_filled_holes = my_mesh.FillHoles()
97 Rhino.Geometry Namespace Objects

Transform Struct
Transform struct is used to generate transformations that could be used on any geometry’s
.Transform() method.

Examples of Transform Struct METHODS


my_moved_object = my_object.Transform(rg.Transform.Translation(x,y,z))
my_moved_object = my_object.Transform(rg.Transform.Translation(my_motion_vector3d))
my_reoriented_object = my_object.Transform(rg.Transform.ChangeBases(plane_a, plane_b))
my_mirrored_object = my_object.Transform(rg.Transform.Mirror(mirror_plane))
my_mirrored_object = my_object.Transform(rg.Transform.Mirror(my_point_on_mirror_plane, normal_vector))
my_projected_object = my_object.Transform(rg.Transform.ProjectAlong(my_plane, direction_vector))
my_rotated_object = my_object.Transform(rg.Transform.Rotation(radians_angle, center_point))
my_rotated_object = my_object.Transform(rg.Transform.Rotation(radians_angle, rotation_axis_vector,
center_point))
my_rotated_object = my_object.Transform(rg.Transform.Rotation(start_direction_vector,
end_direction_vector, center_point))
my_scaled_object = my_object.Transform(rg.Transform.Scale(my_plane, x_factor, y_factor, z_factor))
my_scaled_object = my_object.Transform(rg.Transform.Scale(anchor_point, scale_factor))
98 Rhino.Geometry Namespace Objects

Intersection Class
Intersection class has methods used to solve intersection events between geometries.

Examples of Intersection Class METHODS

arc_arc_intersection_points_list = rg.Intersect.Intersection.ArcArc(arc_a, arc_b)


brep_brep_val, brep_brep_intersect_curves, brep_brep_intersect_points =
rg.Intersect.Intersection.BrepBrep(brep_a, brep_b, tolerance) #brep_brep_val returns true or false
brep_plane_val, brep_plane_intersect_curves, brep_plane_intersect_points =
rg.Intersect.Intersection.BrepPlane(brep_a, plane_a, tolerance) #brep_plane_val returns true or false
brep_surface_val, brep_surface_intersect_curves, brep_surface_intersect_points =
rg.Intersect.Intersection.BrepSurface(brep_a, surface_a, tolerance) #brep_surface_val returns true or
false
circle_circle_intersection_points_list = rg.Intersect.Intersection.CircleCircle(circle_a, circle_b)
curve_brep_val, curve_brep_intersection_params = rg.Intersect.Intersection.CurveBrep(curve_a, brep_a,
tolerance, overlap _tolerance)
curve_curve_intersection_events = rg.Intersect.Intersection.CurveCurve(curve_a, curve_b, tolerance,
overlap_tolerance)
curve_line_intersection_events = rg.Intersect.Intersection.CurveLine(curve_a, line_a, tolerance,
overlap_tolerance)
curve_plane_intersection_events = rg.Intersect.Intersection.CurvePlane(curve_a, plane_a, tolerance)
curve_self_intersection_events = rg.Intersect.Intersection.CurveSelf(curve_a, tolerance)
curve_surface_intersection_events = rg.Intersect.Intersection.CurveSurface(curve_a, surface_a,
tolerance, overlap_tolerance)
line_box_intersect_val, line_box_interval_line_params = rg.Intersect.Intersection.LineBox(line_a, box_a,
tolerance)
line_circle_intersection_points_list = rg.Intersect.Intersection.LineCircle(line_a, circle_a)
line_cylinder_intersect_points = rg.Intersect.Intersection.LineCylinder(line_a, cylinder_a)
line_line_intersect_params = rg.Intersect.Intersection.LineLine(line_a, line_b)
line_plane_intersect_params = rg.Intersect.Intersection.LinePlane(my_line,my_plane)
line_sphere_intersect_points = rg.Intersect.Intersections.LineSphere(my_line, my_sphere)
mesh_line_intersect_points = rg.Intersect.Intersections.MeshLine(my_mesh, my_line)
plane_plane_intersect_line = rg.Intersect.Intersection.PlanePlane(plane_a, plane_b)
plane_plane_plane_intersect_point = rg.Intersect.Intersection.PlanePlanePlane(plane_a, plane_b, plane_c)
plane_circle_intersect_params = rg.Intersect.Intersection.PlaneCircle(plane_a, circle_a)
plane_sphere_intersect_circle = rg.Intersect.Intersection.PlaneSphere(plane_a, sphere_a)
projected_points_to_breps = rg.Intersect.Intersection.ProjectPointsToBreps(breps, points,
direction_vector, tolerance)
sphere_sphere_intersection_circle = rg.Intersect.Intersection.SphereSphere(sphere_a, sphere_b)
surface_surface_curves_and_points = rg.Intersect.Intersection.SurfaceSurface(surface_a, surface_b,
tolerance)

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

Examples of IntersectionEvent Class PROPERTIES


point_of_intersection_on_curve_a = intersection_event.PointA
point_of_intersection_on_curve_b = intersection_event.PointB
param_of_intersection_on_curve_a = intersection_event.ParameterA
param_of_intersection_on_curve_b = intersection_event.ParameterB
check_if_the_event_is_point = intersection_event.IsPoint

Examples of IntersectionEvent Class METHODS


u_param, v_param = intersection_event.SurfacePointParameter() #use with curve|surface intersections

Here is an example of dealing with IntersectionEvent output in case of curve|curve


intersections.

curve_curve_intersection_events = rg.Intersect.Intersection.CurveCurve(curve_a, curve_b, tolerance,


angle_tolerance)
# Initialize a list to hold the intersection points
intersection_points = []
# Loop through the intersection events to extract points
for event in curve_curve_intersection_events:
intersection_points.append(event.PointA) # Add the intersection point

# Output the intersection points


a = intersection_points
100 Rhino.Geometry Namespace Objects

Here is an example of dealing with Intersections output in case of curve|brep intersections.

import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri

intersection_bool, params = ri.Intersection.CurveBrep(curve_a, brep_a, 0.01, 0.01)


# Initialize a list to hold the intersection points
intersection_points = []
# Loop through the resulting params to extract points
for param in params:
intersection_points.append(curve_a.PointAt(param))

# Output the intersection points


a = intersection_points
101 Rhino.Geometry Namespace Objects

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

# Perform the intersection check


intersection_result = ri.Intersection.LineBox(x, y, 0.01)

# Initialize a variable to store the resulting line segment


intersection_line = None
intersection_points = []
# Check if there is an intersection
if intersection_result[0]:
# Get the start and end parameters of the intersection interval
t0 = intersection_result[1].ParameterAt(0) # Start parameter
t1 = intersection_result[1].ParameterAt(1) # End parameter

# Calculate the start and end points of the line segment


start_point = x.PointAt(t0)
end_point = x.PointAt(t1)
intersection_points.append(start_point)
intersection_points.append(end_point)
# Create the line segment between the start and end points
intersection_line = rg.Line(start_point, end_point)

# Output the line segment to the GHPython output


a = intersection_line # Connect this to a 'Line' component in Grasshopper to visualize
b = intersection_points
102 Rhino.Geometry Namespace Objects

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)

# Keep only items not at indices 0, 1, and 3


b = [item for i, item in enumerate(b) if i not in (0, 1, 3)]

To create an intersection between two surfaces, follow this tutorial

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 = []

for object in a[1]:


intersection_curves.append(object)
for object in a[2]:
intersection_points.append(object)
b, c = intersection_curves, intersection_points
103 Rhino.Geometry Namespace Objects

Next, an example of how to project points on breps:

import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri
import System.Collections.Generic as sg

# Create the IEnumerable[Brep] for the Brep collection


breps = sg.List[rg.Brep]()
breps.Add(y)

# Create the IEnumerable[Point3d] for the points collection


points = sg.List[rg.Point3d]()
points.Add(x) # Add the point(s) you want to project

# Perform the projection


a = ri.Intersection.ProjectPointsToBreps(breps, points, rg.Vector3d.ZAxis, 0.01)
104 Rhino.Geometry Namespace Objects

Now, let’s get the intersection points between a plane and a circle:

import Rhino.Geometry as rg
import Rhino.Geometry.Intersect as ri

# Perform the intersection


result = ri.Intersection.PlaneCircle(x, y)

# Initialize an empty list to store the intersection points


intersection_points = []

# Check if the result is a secant (two intersection points)


if result[0] == rg.Intersect.LineCircleIntersection.Secant:
# Use the parameters to find the intersection points on the circle
t0 = result[1] # First parameter
t1 = result[2] # Second parameter

# Calculate the actual points on the circle


point1 = y.PointAt(t0)
point2 = y.PointAt(t1)

# Add the points to the list


intersection_points = [point1, point2]

# Output the points or line segment to the viewport


a = intersection_points # List of points for display

Intersect Namespace Enums


The intersect namespace has a list of enums which can give a good persepective of the result
of some intersections.

ArcArcIntersection
Values Description
0 None Arcs do not intersect
1 Single Arcs touch at one point
105 Rhino.Geometry Namespace Objects

2 Multiple Arcs intersect at two points


3 Overlap Arcs are cocircular and overlap

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.

Loft Type enum


Values Description
0 Normal Uses chord-length parameterization in the loft direction
The surface is allowed to move away from the original curves to make a smoother surface. The
1 Loose
surface control points are created at the same locations as the control points.
The surface sticks closely to the original curves. Uses square root of chord-length
2 Tight
parametrization in the loft direction.
3 Straight The sections between the curves are straight. Also known as ruled surface
4 Developable Obselete, do not use.
5 Uniform Constructs a uniform loft. The object knot vectors are uniform.

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

Working with Data Trees in Grasshopper using


Python
The Grasshopper Software Development Kit (SDK) is a powerful tool that allows developers
and architects to use Grasshopper’s procedural modeling capabilities through scripting.
With the SDK, we can go beyond Grasshopper's built-in components by writing custom code
in languages like Python, C#, and VB.NET, giving us greater flexibility to create and
manipulate complex geometries, workflows, and data structures programmatically.

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.

Creating Data Trees


To create a data tree in Python, we’ll use the Grasshopper.DataTree class from the
Grasshopper SDK. First, let’s import the necessary module:

import Rhino.Geometry as rg
import Grasshopper as gh

# Creating an empty data tree


tree = gh.DataTree[object]()

The DataTree[object] type accepts any object type as an item. This tree is now ready to have
items and paths added to it.

Adding Data to Specific Paths


In a data tree, each branch has a unique path represented by a GH_Path, such as {0;0}, {0;1},
etc. To add data to a specific path, we define the path using a GH_Path object and use the
.Add() method to insert items into the tree at that path.

import Rhino.Geometry as rg
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path

# Initialize the data tree


tree = DataTree[object]()

# Define paths
path1 = GH_Path(0) # Path {0}
path2 = GH_Path(1, 0) # Path {1;0}

# Add data to paths


tree.Add("Item at {0}", path1)
tree.Add("Item at {1;0}", path2)

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 and Grafting


Flattening and grafting are two common operations in Grasshopper that affect data tree
structure.

• 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.

To flatten a data tree:

flattened_tree = tree.Flatten()

To graft a data tree:

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 and Pruning


You may often need to merge multiple data trees or prune branches to remove empty or
unnecessary data.

Merging Trees: You can combine trees by using loops to add branches from one tree to
another.

tree1 = DataTree[object]()
tree2 = DataTree[object]()

# Populate tree1 and tree2 with some items...

# Merge tree2 into tree1


for i in range(tree2.BranchCount):
path = tree2.Path(i)
branch = tree2.Branch(path)
tree1.AddRange(branch, path)

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)

Useful Data Tree Operations


There are a few additional operations that may help in manipulating data trees including
accessing items, branches, paths, etc.
111 Working with Data Trees

• Accessing Branches: Use .Branch(path) to access items at a specific path.

path = GH_Path(0)
branch = tree.Branch(path)
print(branch) # Prints items in the branch at {0}

• Accessing Branch Item: Use branch_name[index] to access items at a specific


branch.

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.

print("Branch Count:", tree.BranchCount)


print("Paths:", tree.Paths)

Example: Creating a Nested Data Tree Structure

This is an example of creating a nested data tree structure with multiple paths and items:

from Grasshopper import DataTree


from Grasshopper.Kernel.Data import GH_Path

tree = DataTree[object]()

# Adding items to various paths in the tree


for i in range(3):
path = GH_Path(i)
for j in range(5):
tree.Add(f"Item {i}-{j}", path)

# Printing out the items in each branch


for i in range(tree.BranchCount):
path = tree.Path(i)
branch = tree.Branch(path)
print(f"Path {path}: {branch}")

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.

The Role of Coding in Architectural Design

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.

By developing code-based workflows, architects can tackle complex problems, iteratively


adjust designs, and create responsive models that adapt to varying parameters. For
example, using RhinoCommon’s classes and methods to generate data-driven forms allows
architects to explore countless iterations that satisfy performance criteria. This approach
fosters a design process where the form evolves naturally from both creative intuition and
logical constraints, creating a harmonious balance between aesthetics and functionality.

Empowering Architects with Parametric Design

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.

This book has covered essential parametric concepts, including:

Geometry Creation and Manipulation: Mastering RhinoCommon's classes, structs, and


enums for creating geometry and transformations allows designers to efficiently build any
complex geometry with code.

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.

Challenges and the Future of Computational Design in Architecture

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.

Toward a Collaborative and Adaptive Design Approach

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

• Lutz, Mark. Learning Python. 5th ed., O'Reilly Media, 2013.


• A comprehensive guide to Python programming, covering core concepts and
advanced topics in detail.
• Zelle, John M. Python Programming: An Introduction to Computer Science. 2nd ed.,
Franklin, Beedle & Associates Inc., 2010.
• Gohlke, Christoph. Python and Rhino for Computational Design. 1st ed., CRC Press,
2020.
• Van Oosterhout, Rob. Python Scripting for Computational Science. Springer, 2014.
• Grün, David. Architectural Design with Rhino and Grasshopper. 1st ed., Wiley, 2020.
• Schodek, Daniel, et al. Digital Design and Manufacturing: CAD/CAM Applications in
Architecture and Design. Wiley, 2011.

Online Resources

• McNeel & Associates. RhinoCommon API Documentation.


https://developer.rhinocommon.com/
• Grasshopper3D. Grasshopper Documentation.
http://www.grasshopper3d.com/page/documentation
• W3Schools. Python Tutorial. https://www.w3schools.com/python/
• Real Python. Python Tutorials and Resources. https://realpython.com/

Articles and Journals

• Gonzalez, Enrique. "The Role of Computational Design in Architectural Practice."


Journal of Architectural Education, vol. 66, no. 2, 2013, pp. 14-21.
• Kolarevic, Branko. "Architecture in the Digital Age: Design and Manufacturing."
International Journal of Architectural Computing, vol. 4, no. 2, 2006, pp. 91-104.
RhinoCommon: Using the Geometry Namespace with Python
A Guide to Create Rhinoceros3d Geometry in Grasshopper3d by Writing Python Code.

Design copyright ©2024 Abdulrahman Ayman


Text copyright ©2024 Abdulrahman Ayman

Abdulrahman Ayman has asserted his right under


the Copyright, Designs and Patent Act 1988 to be
identified as the Author of this work.

All rights reserved. No part of this publication may


be reproduced or transmitted in any form or by any
means, electronic or mechanical, including
photocopy, recording or any information storage
and retrieval system, without prior permission in
writing from the publisher.

You might also like