#4441 Rhino3D, Grasshopper and Ollama-supported Local LLM
#4441 Rhino3D, Grasshopper and Ollama-supported Local LLM
#4441 What could I do withRhino3D, Grasshopper and an Ollama-supported local LLM?
Combining Rhino3D, Grasshopper, and a local Ollama LLM opens up a powerful frontier: Generative Design Engineering. By linking a local, private LLM directly to a parametric CAD environment, you can bypass the cloud, ensure absolute data privacy, and build a closed-loop system where AI interacts directly with 3D geometry and logic.
Because Grasshopper can execute Python scripts (via the CPython component in Rhino 8 or GHPython in Rhino 7), you can stream data back and forth between Ollama's local API endpoints (http://localhost:11434) and your viewport.
Here is a breakdown of what you can build and achieve with this stack, categorized by architectural pattern:
1. Natural Language to Geometry (Semantic Modeling)
Instead of manually dragging sliders and wiring components, you can use the LLM as a translation layer between human intent and parametric execution.
The Workflow: You feed a prompt into an Ollama model (like
llama3ormistral) asking for a specific structure. The LLM returns structured data—such as a JSON object containing coordinates, dimensions, iterations, or vector directions. A lightweight Grasshopper Python component parses this JSON and instantly generates or modifies the geometry.Example: Prompting "Create a structural canopy spanning 20x30 meters with a variable Voronoi perforation that gets denser toward the center support pillars" yields the exact parameters your Grasshopper script needs to rebuild the canopy dynamically.
2. Automated G-Code & Digital Fabrication Auditing
When preparing files for CNC routing, 3D printing, or laser cutting, the LLM can act as an automated, local code auditor.
The Workflow: You can extract raw G-code or toolpath coordinates directly from Grasshopper, format them into a text string, and pass them to a local model engineered for code analysis (like
codellamaordeepseek-coder).Example: The local LLM can parse the toolpath to look for sharp velocity changes, potential tool collisions, or syntax errors based on your specific machine configuration, flagging safety or optimization issues before you send the file to the physical hardware.
3. Real-Time Requirement Mapping & Compliance
If you are designing systems that must adhere to rigid engineering standards, spatial constraints, or building codes, you can turn your LLM into a real-time compliance checker.
The Workflow: You embed your specific engineering constraints, compliance text, or standards directly into a local vector database (or feed them via Retrieval-Augmented Generation / RAG). As you manipulate geometry in Rhino, Grasshopper calculates metrics (e.g., clearance distances, material volumes, structural ratios) and sends them to Ollama.
Example: The LLM evaluates the live CAD data against your reference standards and outputs a real-time compliance report directly into a Rhino text panel, warning you the moment a design boundary is breached.
4. Algorithmic Optimization & "Design Space" Exploration
Traditional Grasshopper optimization engines (like Galapagos or Wallacei) rely on brute-force evolutionary algorithms. An LLM can introduce heuristic, reasoning-driven optimization.
The Workflow: You set up a feedback loop. Grasshopper generates a design variation, runs an analysis (like a structural finite element analysis via Karamba3D or an environmental simulation via Ladybug), and formats the performance metrics into text. The LLM reads the performance data, reasons through the geometric trade-offs, and proposes the next set of parametric adjustments.
Example: The LLM balances a classic design contradiction: reducing material mass while maximizing structural stiffness, steering the parameter sliders more intelligently than a blind random search.
5. Live Interactive Design Journals
You can build a system where the AI acts as a persistent architectural archivist, documenting the why behind your design evolution.
The Workflow: Every time you save a milestone bake or trigger a specific Grasshopper event, Python captures the current parameter states, simulation outputs, and a user note. Ollama summarizes this data into a structured engineering changelog.
Example: This creates an automated, searchable knowledge graph of your entire design process, allowing you to ask a local chatbot later: "Why did we change the lattice density on version 4?" and get an answer derived directly from your past design iterations.
Getting Started: The Tech Connective Tissue
To make this work, you don't need complex plugins. You just need a standard GH Python Script component configured to make a local HTTP POST request:
import json
import urllib.request
# Setup local Ollama endpoint
url = "http://localhost:11434/api/generate"
payload = {
"model": "llama3", # or your preferred local model
"prompt": your_custom_grasshopper_prompt,
"stream": False,
"format": "json" # Forces the LLM to reply in clean, parseable JSON
}
# Send to Ollama and read response
req = urllib.request.Request(
url, data=json.dumps(payload).encode("utf-8"), headers={"Content-Type": "application/json"}
)
with urllib.request.urlopen(req) as response:
res = json.loads(response.read().decode("utf-8"))
llm_output = res["response"]
By parsing llm_output, your Grasshopper canvas gains a private, local reasoning engine capable of driving 3D space.
Let's try and example: Provide a JSON object containing coordinates, dimensions, iterations, or vector directions for "a structural canopy spanning 20x30 meters with a variable Voronoi perforation that gets denser toward the center support pillars"
To process this efficiently in Grasshopper, a local LLM should return a strictly structured JSON object that breaks down the structural bounds, the pillar locations, and the mathematical parameters needed to drive a Voronoi component locally.
Here is how an optimized local model (instructed to output pure JSON) would structure the data for your canopy:
{
"canopy_dimensions": {
"width_x_meters": 20.0,
"length_y_meters": 30.0,
"base_height_z_meters": 5.0,
"total_height_z_meters": 7.5
},
"support_pillars": [
{
"id": "pillar_01",
"coordinate_xyz": [10.0, 10.0, 0.0],
"radius_meters": 1.2,
"influence_radius_meters": 6.0
},
{
"id": "pillar_02",
"coordinate_xyz": [10.0, 20.0, 0.0],
"radius_meters": 1.2,
"influence_radius_meters": 6.0
}
],
"voronoi_generation": {
"total_cell_count": 450,
"population_iterations": 5,
"attractor_logic": {
"type": "inverse_distance_density",
"density_multiplier_at_pillars": 4.5,
"falloff_exponent": 1.8
},
"perforation_scale": {
"min_scale_factor": 0.15,
"max_scale_factor": 0.75,
"note": "Lower scale factor near pillars means smaller openings/higher material density"
}
},
"structural_vectors": {
"primary_load_path": [0.0, 0.0, -1.0],
"vault_curvature_vectors": [
{"axis": "X", "direction": [1.0, 0.0, 0.2], "amplitude": 1.5},
{"axis": "Y", "direction": [0.0, 1.0, 0.1], "amplitude": 0.8}
]
}
}
How to Pipe This into Your Grasshopper Canvas
Once your Python component receives this JSON string from Ollama, you can unpack it and map it directly to native Grasshopper logic:
Boundary & Pillars: Use
canopy_dimensionsto draw the base surface rectangle. Use thesupport_pillarscoordinates to define the exact attractor points where the columns meet the canopy vault.The Voronoi Density Trick: To make the Voronoi pattern denser near the pillars, you use the
attractor_logic. In Python, you can generate a random cloud of points across the 20 x 30 meter surface, but use awhileloop or a rejection-sampling algorithm. If a random point falls close to a pillar coordinate (within theinfluence_radius_meters), it has a much higher probability of being kept. This clusters the Voronoi seed points tightly around the supports.Variable Openings: Pass the distance from each Voronoi cell center to the nearest pillar into a Remap Numbers component. Map those distances to the
min_scale_factorandmax_scale_factor. By scaling the Voronoi cells inward based on this factor, the openings close up near the pillars (providing more structural mass where shear forces are highest) and open up wide further away to let in light.
Would you like the specific Python snippet to parse this structural schema and generate the clustered seed points inside Grasshopper?
Yes
Here is the Python script designed for the GH Python Script component (or the newer CPython component in Rhino 8).
This script accepts the JSON string from Ollama, parses it, and uses rejection sampling to generate a dense cluster of Voronoi seed points around your specified pillar locations.
1. The Grasshopper Setup
Inputs: *
json_string(Set as a single String item containing the JSON from the previous step).Outputs:
canopy_outline(The $20 \times 30$ boundary curve).pillar_points(The center locations of the columns).seed_points(The clustered points to plug directly into the native Grasshopper Voronoi component).
2. The Python Code
import json
import math
import random
import Rhino.Geometry as rg
# 1. Parse the JSON data incoming from Ollama
data = json.loads(json_string)
# Extract Canopy Dimensions
dim_x = data["canopy_dimensions"]["width_x_meters"]
dim_y = data["canopy_dimensions"]["length_y_meters"]
base_z = data["canopy_dimensions"]["base_height_z_meters"]
# Extract Voronoi Logic Parameters
total_target_points = data["voronoi_generation"]["total_cell_count"]
attractor_data = data["voronoi_generation"]["attractor_logic"]
density_mult = attractor_data["density_multiplier_at_pillars"]
falloff = attractor_data["falloff_exponent"]
# 2. Reconstruct Canopy Boundary and Pillars
# Create the base rectangle boundary
plane = rg.Plane(rg.Point3d(0, 0, base_z), rg.Vector3d.ZAxis)
canopy_outline = rg.Rectangle3d(plane, dim_x, dim_y).ToNurbsCurve()
# Parse Pillar coordinates into Rhino Point3d objects
pillar_points = []
pillar_radii = []
for pillar in data["support_pillars"]:
coords = pillar["coordinate_xyz"]
# Ensure points match the canopy base height
pt = rg.Point3d(coords[0], coords[1], base_z)
pillar_points.append(pt)
pillar_radii.append(pillar["influence_radius_meters"])
# 3. Rejection Sampling Loop for Clustered Seed Points
# This creates high density near pillars and lower density further away
seed_points = []
attempts = 0
max_attempts = total_target_points * 100 # Safety break
while len(seed_points) < total_target_points and attempts < max_attempts:
attempts += 1
# Generate a random candidate point within the 20x30 boundary
cand_x = random.uniform(0, dim_x)
cand_y = random.uniform(0, dim_y)
candidate = rg.Point3d(cand_x, cand_y, base_z)
# Find the closest pillar and calculate distance
min_dist = float("inf")
active_influence = 6.0 # fallback Default
for i, p_pt in enumerate(pillar_points):
dist = candidate.DistanceTo(p_pt)
if dist < min_dist:
min_dist = dist
active_influence = pillar_radii[i]
# Calculate acceptance probability based on distance to nearest pillar
if min_dist <= active_influence:
# Normalized distance: 0 at pillar, 1 at edge of influence
t = min_dist / active_influence
# Inverse distance falloff logic dictated by the LLM
prob = 1.0 / math.pow((t * density_mult) + 1.0, falloff)
else:
# Base ambient probability outside of direct pillar influence
prob = 0.05
# Accept or reject the point based on the probability threshold
if random.random() < prob:
seed_points.append(candidate)
3. Completing the Definition in Grasshopper
To finish the variable perforation look without writing more code, wire the outputs of this Python component into native Grasshopper components:
Generate Cells: Plug
seed_pointsinto the Voronoi component'sPoints (P)input. Plugcanopy_outlineinto theBoundary (B)input to clip the edges cleanly.Calculate Distances: Use an Area component to find the center mass of every individual Voronoi cell. Use a Closest Point component to find the distance from each cell center to the nearest
pillar_points.Variable Scale (The Apertures): Wire those distances into a Remap Numbers component. Map the closest distances to a lower value (e.g.,
0.2) and the furthest distances to a higher value (e.g.,0.75).Scale the Cells: Feed those remapped factors into a Scale component to shrink each Voronoi cell about its own center point.
The result is a structurally optimized pattern: tiny, dense perforations near the pillars maintaining high structural mass where shear forces peak, opening up to expansive panels further away.
From <https://gemini.google.com/app/> Google Gemini (3.5 Flash)
Comments
Post a Comment