Imports
# Standard — Rhino commands
import rhinoscriptsyntax as rs

# RhinoCommon geometry kernel
import Rhino.Geometry as rg

# Grasshopper types
import Grasshopper as gh
import ghpythonlib.components as ghcomp

# Math and system
import math
import System.Drawing as sd  # colors

# GHPython template
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
x = ...  # output
Variables & Types
# Naming conventions (Rutten/McNeel style)
bln_closed  = True        # bool
int_count   = 10           # int
dbl_length  = 3.14         # float
str_name    = "column_01"  # str
arr_points  = []             # list
id_curve    = None           # guid

# Type checking
type(x)             # → <class 'int'>
isinstance(x, float) # → True/False

# Type conversion
int(3.9)   # → 3
float(3)   # → 3.0
str(42)    # → "42"
bool(0)    # → False
Operators
Arithmetic
10 + 3   # 13    Addition
10 - 3   # 7     Subtraction
10 * 3   # 30    Multiplication
10 / 3   # 3.333 Division
10 // 3  # 3     Floor division
10 % 3   # 1     Modulo
10 ** 3  # 1000  Power
Comparison & Logic
x == y  x != y  x < y  x > y  x <= y  x >= y
and   or   not   in   is   is not
if / elif / else
if length > 10:
    print("Long")
elif length > 5:
    print("Medium")
else:
    print("Short")

# Inline (ternary)
label = "big" if x > 5 else "small"

# None check (important in Rhino!)
if obj is not None:
    rs.DeleteObject(obj)

# Compound conditions
if x > 0 and y > 0:
    print("First quadrant")
for loops
# range(stop) / range(start, stop, step)
for i in range(10):          # 0..9
    rs.AddPoint([i, 0, 0])

for i in range(0, 20, 2):    # 0,2,4..18
    rs.AddPoint([i, 0, 0])

# Iterate list
for pt in points:
    rs.AddPoint(pt)

# enumerate = index + value
for i, pt in enumerate(points):
    rs.ObjectName(rs.AddPoint(pt), f"pt_{i}")

# zip = parallel lists
for a, b in zip(list_a, list_b):
    rs.AddLine(a, b)

# break / continue
for obj in objects:
    if rs.CurveLength(obj) > 100: break
    if rs.CurveLength(obj) < 0.1: continue
while loops
# Basic while
count = 0
while count < 10:
    rs.AddPoint([count, 0, 0])
    count += 1

# Condition-driven (Rhino example)
goal = target_len * 0.5
while rs.CurveLength(crv) > goal:
    pts = rs.CurvePoints(crv)
    for i, pt in enumerate(pts):
        pts[i] = rs.PointAdd(pt, direction)
    # update curve...

# while True + break
while True:
    val = rs.GetNumber("Radius (0 to quit)")
    if val is None or val == 0: break
    rs.AddCircle([0,0,0], val)
Lists
# Create
pts = []                   # empty
pts = [[0,0,0],[1,1,0]]    # with items

# Access
pts[0]    # first item
pts[-1]   # last item
pts[1:4]  # slice index 1..3
pts[::2]  # every other item

# Modify
pts.append([5,5,0])     # add to end
pts.insert(0, [-1,0,0]) # insert at index
pts.remove([0,0,0])    # remove value
pts.pop(2)              # remove by index
pts.sort()              # sort in-place
pts.reverse()          # reverse
len(pts)                # count

# List comprehension
zs = [pt[2] for pt in pts]
hi = [pt for pt in pts if pt[2] > 5]
Tuples & Dictionaries
# Tuple — immutable
t = (1, 2, 3)
t[0]              # → 1
a, b, c = t       # unpack
color = (255, 0, 0)  # RGB tuple

# Dictionary — key:value
mat = {
    "name":    "concrete",
    "density": 2400,
    "color":   (180,180,180)
}
mat["name"]           # → "concrete"
mat["E"] = 30e9        # add key
"density" in mat       # → True
mat.get("E", 0)         # safe get

for k, v in mat.items():
    print(f"{k}: {v}")
Functions
# Define
def make_tower(base_pt, height, count):
    """Creates stacked circles."""
    step = height / count
    floors = []
    for i in range(count):
        z   = base_pt[2] + i * step
        ctr = [base_pt[0], base_pt[1], z]
        rad = 5 - i * (5 / count)
        floors.append(rs.AddCircle(ctr, rad))
    return floors

# Default arguments
def add_grid(rows=5, cols=5, spacing=2.0):
    pts = []
    for r in range(rows):
        for c in range(cols):
            pts.append([c*spacing, r*spacing, 0])
    return pts

# Call
grid = add_grid(cols=10)
Brackets — When to Use
BracketUseExample
( )Call / Math / Tuplers.AddLine(p1, p2)
[ ]List / Index / Slicepts[0] / [x,y,z]
{ }Dict / Set{"key": val}
f"{ }"f-string embedf"val={x:.2f}"
# ( ) — function call
result = rs.CurveLength(crv_id)

# [ ] — list literal + index
pt = [0, 0, 0]
z  = pt[2]

# { } — dictionary
d = {"a": 1, "b": 2}

# f"{ }" — formatted string
msg = f"Length = {result:.3f} mm"
Rhino — Create Geometry
import rhinoscriptsyntax as rs

# Points
rs.AddPoint([x, y, z])
rs.AddPoints(list_of_pts)

# Curves
rs.AddLine(pt_start, pt_end)
rs.AddPolyline(pts)
rs.AddCurve(pts, degree=3)
rs.AddInterpCurve(pts, degree=3)
rs.AddCircle(center, radius)
rs.AddArc(plane, radius, degrees)
rs.AddEllipse(plane, r_x, r_y)

# Surfaces
rs.AddSphere(center, radius)
rs.AddPlaneSurface(plane, uv, uv)
rs.AddLoftSrf([crv1, crv2, ...])
rs.AddNurbsSurface(size, pts, ...)

# Mesh
rs.AddMesh(vertices, face_indices)
Rhino — Query & Measure
import rhinoscriptsyntax as rs

# Curves
rs.CurveLength(id)
rs.CurveDomain(id)         # [t0, t1]
rs.CurveStartPoint(id)
rs.CurveEndPoint(id)
rs.CurveTangent(id, t)
rs.EvaluateCurve(id, t)    # → point
rs.DivideCurve(id, N)      # N pts
rs.DivideCurveLength(id, d) # by dist

# Surfaces
rs.SurfaceDomain(id, 0)    # U domain
rs.EvaluateSurface(id,u,v) # → point
rs.SurfaceNormal(id,[u,v]) # → vector
rs.SurfaceFrame(id,[u,v])  # → plane
rs.SurfaceClosestPoint(id,pt) # → [u,v]

# General
rs.Distance(pt1, pt2)
rs.BrepClosestPoint(id, pt)
Vector Math
import rhinoscriptsyntax as rs

a = [1,0,0]   b = [0,1,0]

rs.VectorAdd(a, b)          # [1,1,0]
rs.VectorSubtract(a, b)     # [1,-1,0]
rs.VectorScale(a, 5)        # [5,0,0]
rs.VectorUnitize(a)         # unit vector
rs.VectorLength(a)          # magnitude
rs.VectorCrossProduct(a,b)  # [0,0,1]
rs.VectorDotProduct(a,b)    # 0.0
rs.VectorRotate(a,90,[0,0,1]) # rotate
rs.VectorCreate(pt1, pt2)   # pt2-pt1
rs.IsVectorParallelTo(a,b)  # 1/-1/0

# Point arithmetic
rs.PointAdd(pt, vec)
rs.PointSubtract(pt, vec)
Selection & User Input
import rhinoscriptsyntax as rs

# Pick objects
obj  = rs.GetObject("msg", rs.filter.curve)
objs = rs.GetObjects("msg", rs.filter.surface)
srf, uv, pt = rs.GetSurfaceObject("msg")

# Pick point / number / string
pt  = rs.GetPoint("Pick point")
n   = rs.GetNumber("Value", 1.0)
k   = rs.GetInteger("Count", 5)
s   = rs.GetString("Name")

# Dialogs
rs.MessageBox("Done!")
choice = rs.ListBox(items, "title")
color  = rs.GetColor()

# Filter constants
rs.filter.curve      # 4
rs.filter.surface    # 8
rs.filter.mesh       # 32
rs.filter.point      # 1
Object Attributes
import rhinoscriptsyntax as rs

# Name / Layer / Color
rs.ObjectName(id, "ColumnA1")
rs.ObjectLayer(id, "Structure")
rs.ObjectColor(id, (255,0,0))   # RGB

# Layers
rs.AddLayer("Structure::Beams")  # nested
rs.LayerColor("Beams", (0,100,255))

# Delete / select
rs.DeleteObject(id)
rs.DeleteObjects(id_list)
rs.SelectObject(id)
rs.ObjectsByType(rs.filter.curve)

# Type checks
rs.IsCurve(id)
rs.IsCurveClosed(id)
rs.IsBlock(id)
Performance Patterns
import rhinoscriptsyntax as rs

# ✅ Always disable redraw for batch ops
rs.EnableRedraw(False)
results = []
for i in range(500):
    results.append(rs.AddPoint([i,0,0]))
rs.EnableRedraw(True)
rs.ZoomExtents()

# ✅ Batch delete
rs.DeleteObjects(list_of_ids)   # fast
# ❌ not: for id in list: rs.DeleteObject(id)

# ✅ Tolerance-aware checks
tol = rs.UnitAbsoluteTolerance()
if length < tol: print("zero-length")

# ✅ Always check None returns
crv = rs.GetObject("Pick")
if crv is None: exit()   # user hit Esc
Grasshopper Python (GHPython)
# Standard GHPython header
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
import System.Drawing as sd

# Inputs: x, y (from GH wires)
# Outputs: a, b, c (assigned to GH params)

# Point grid output
pts = []
for i in range(rows):
    for j in range(cols):
        pts.append(rg.Point3d(i,j,0))
a = pts    # output list

# Color by value
if length < threshold:
    col = sd.Color.FromArgb(30,100,255)
else:
    col = sd.Color.FromArgb(255,80,30)
b = col    # → GH Custom Preview

# rg vs rs
# rg = geometry objects only (no document)
# rs = document commands (adds to Rhino)
Common Errors & Fixes
ErrorFix
IndentationErrorUse 4 spaces, no tabs
NameErrorVariable not defined yet
TypeError: NoneTypeCheck if obj is None first
IndexErrorlist index out of range → check len()
AttributeErrorWrong type, check isinstance()
Script too slowEnableRedraw(False) in loop
# Safe pattern
obj = rs.GetObject("Select")
if obj is None:
    print("Cancelled")
else:
    # safe to use obj here
    length = rs.CurveLength(obj)
Math Quick Reference
import math

math.pi                # 3.14159…
math.e                 # 2.71828…
math.sqrt(16)          # 4.0
math.pow(2, 10)         # 1024.0
math.sin(math.pi/2)    # 1.0
math.cos(0)             # 1.0
math.tan(math.pi/4)    # 1.0
math.atan2(y, x)        # angle in rad
math.degrees(rad)       # rad → deg
math.radians(deg)       # deg → rad
math.floor(3.9)         # 3
math.ceil(3.1)          # 4
math.fabs(-5)           # 5.0  (abs)
round(3.14159, 2)      # 3.14
min(3,1,4)   max(3,1,4)  # 1, 4
String Methods
s = "  Hello Rhino  "
s.strip()         # "Hello Rhino"
s.upper()         # "  HELLO RHINO  "
s.lower()         # "  hello rhino  "
s.replace("Rhino","GH")
s.split(" ")       # list of words
" | ".join(["a","b"])  # "a | b"
s.find("Rhino")   # index or -1
s.startswith("H") # True/False
s.endswith("o")   # True/False
s.count("o")      # occurrences
"42".zfill(5)     # "00042"
f-strings & Format
n = 3.14159   i = 42
f"Value: {n:.2f}"      # "Value: 3.14"
f"{n:8.2f}"           # "    3.14" (width 8)
f"{i:04d}"            # "0042"
f"{i:b}"              # "101010" (binary)
f"{n:e}"              # "3.14e+00" (sci)
f"{2**10}"            # "1024" (expr)
# String slice
s = "Rhinoceros"
s[:5]   # "Rhino"
s[-1]    # "s"
s[::-1]  # reversed
try / except / finally
try:
    val = int(user_input)
    result = 100 / val
except ValueError:
    print("Not a number!")
except ZeroDivisionError:
    result = 0
except Exception as e:
    print(f"Error: {e}")  # catch-all
else:
    print("No errors!")   # only if no exception
finally:
    file.close()        # always runs

# Raise your own
raise ValueError("Bad input")
File I/O
# Write
with open("data.txt", "w") as f:
    f.write("Hello\n")

# Read all at once
with open("data.txt", "r") as f:
    text = f.read()         # string
    lines = f.readlines()   # list of lines

# JSON
import json
with open("d.json","w") as f:
    json.dump(data, f, indent=2)
with open("d.json","r") as f:
    data = json.load(f)

# Safe load (try/except)
try:
    with open("cfg.txt") as f:
        cfg = f.read()
except FileNotFoundError:
    cfg = ""   # default
lambda / map / filter / sorted
# lambda: inline one-liner function
sq = lambda x: x**2     # sq(5) → 25
mid = lambda a,b: (a+b)/2 # midpoint

# sorted with key
curves_by_len = sorted(
    curves, key=lambda c: rs.CurveLength(c))

longest = max(curves, key=lambda c: rs.CurveLength(c))

# filter — keep matching items
long = list(filter(
    lambda c: rs.CurveLength(c) > 5, curves))

# map — transform all items
lengths = list(map(rs.CurveLength, curves))

# Equivalent list comprehensions
long    = [c for c in curves if rs.CurveLength(c)>5]
lengths = [rs.CurveLength(c) for c in curves]
*args and **kwargs
# *args — any number of positional args
def add_pts(*points):
    for pt in points:
        rs.AddPoint(pt)

add_pts([0,0,0], [1,0,0], [2,0,0])

# **kwargs — keyword arguments as dict
def tag(obj, **attrs):
    if "name"  in attrs: rs.ObjectName(obj, attrs["name"])
    if "color" in attrs: rs.ObjectColor(obj, attrs["color"])

tag(obj, name="Beam_1", color=(255,0,0))

# Unpack list/dict into call
coords = [1,2,3]
rs.AddPoint(*coords)    # splat list
tag(obj, **my_dict)     # splat dict
Generators & yield
# Generator — lazy, memory-efficient
def grid_pts(rows, cols, sp):
    for r in range(rows):
        for c in range(cols):
            yield [c*sp, r*sp, 0]

for pt in grid_pts(10,10,2):
    rs.AddPoint(pt)  # one at a time

# Generator expression  ( ) not [ ]
total = sum(rs.CurveLength(c) for c in curves)
minL  = min(rs.CurveLength(c) for c in curves)

# any / all
any(rs.IsCurveClosed(c) for c in curves)
all(rs.IsCurve(c)       for c in curves)
RhinoCommon (rg.)
import Rhino.Geometry as rg

# Points & Vectors
p = rg.Point3d(0,0,0)
v = rg.Vector3d(1,0,0)
rg.Vector3d.XAxis   # (1,0,0)
rg.Vector3d.CrossProduct(v1,v2)
p.DistanceTo(p2)

# Plane
rg.Plane.WorldXY   # standard XY
rg.Plane(origin, normal)

# NurbsCurve
nc = rg.NurbsCurve.CreateFromPoints(pts, 3)
nc.GetLength()
nc.PointAt(t)
nc.TangentAt(t)

# Transforms
xf = rg.Transform.Translation(5,0,0)
xf = rg.Transform.Rotation(angle, axis, pt)
pt.Transform(xf)

# Brep booleans
rg.Brep.CreateBooleanUnion(breps, tol)
Parametric Design Patterns
# Normalize: map 0→n to 0→1
t = i / (n - 1)           # t ∈ [0,1]

# Linear interpolation (lerp)
val = a + (b - a) * t      # between a and b

# Remap from [src0,src1] to [dst0,dst1]
t = (v - src0) / (src1 - src0)
out = dst0 + t * (dst1 - dst0)

# Attractor influence (inverse distance)
d = rs.Distance(pt, attractor)
scale = 1 / (d + 0.001)   # avoid /0

# Grid to flat index
idx = row * cols + col

# Flat index to row/col
row = idx // cols
col = idx  % cols

# Polar coordinates
x = r * math.cos(angle)
y = r * math.sin(angle)