CadQuery & build123d Parts
CadQuery and build123d support in Printago lets you create parametric 3D models using Python. Write real Python code to generate models, define customizable parameters, and leverage a rich ecosystem of engineering libraries -- all without leaving Printago.
What are CadQuery and build123d?
CadQuery and build123d are Python libraries for creating precise, parametric 3D CAD models using code. Both are built on the OpenCASCADE (OCCT) geometry kernel -- the same kernel used by FreeCAD and other professional CAD tools -- giving you access to BREP (boundary representation) modeling with fillets, chamfers, lofts, sweeps, booleans, and more.
| CadQuery | build123d | |
|---|---|---|
| Style | Fluent/chained API | Context managers (builder pattern) |
| Maturity | Established, large community | Newer, rapidly growing |
| Best for | Quick one-liners, chained operations | Complex assemblies, cleaner code structure |
Printago supports both -- your script's imports determine which library is used. If Printago detects a build123d import (import build123d, from build123d import ..., or import build123d as ...), it uses build123d. Otherwise, it defaults to CadQuery.
How Scripts Work
A Printago CadQuery/build123d script is a standard Python file with two conventions:
- Define a
paramsdictionary at the top with default values - Set
resultto your final geometry
That's it. Your script works identically on your local machine and in Printago.
CadQuery Example
import cadquery as cq
params = {
"width": 30,
"depth": 20,
"height": 15,
"fillet": 3,
}
# Optional: UI hints for Printago (ignored when running locally)
printago = {
"width": {"min": 10, "max": 100, "label": "Width (mm)"},
"depth": {"min": 10, "max": 100, "label": "Depth (mm)"},
"height": {"min": 5, "max": 50, "label": "Height (mm)"},
"fillet": {"min": 0, "max": 10, "label": "Corner Radius"},
}
box = cq.Workplane("XY").box(params["width"], params["depth"], params["height"])
if params["fillet"] > 0:
box = box.edges("|Z").fillet(params["fillet"])
result = box
build123d Example
from build123d import *
params = {
"width": 30,
"depth": 20,
"height": 15,
"fillet": 3,
}
printago = {
"width": {"min": 10, "max": 100, "label": "Width (mm)"},
"depth": {"min": 10, "max": 100, "label": "Depth (mm)"},
"height": {"min": 5, "max": 50, "label": "Height (mm)"},
"fillet": {"min": 0, "max": 10, "label": "Corner Radius"},
}
box = Box(params["width"], params["depth"], params["height"])
if params["fillet"] > 0:
box = fillet(box.edges().filter_by(Axis.Z), radius=params["fillet"])
result = box
Parameters
When you upload a script, Printago automatically detects the params dictionary and creates editable fields in the UI. Users can override any parameter value without touching the code.
- Numbers become numeric input fields
- Strings become text fields
- Booleans become toggles
Place your params dictionary near the top of the file, after any imports but before geometry code.
Parameter UI Hints (printago dict)
You can optionally add a printago dictionary to control how parameters appear in the Printago UI. This dict is parsed by Printago when you upload the script but is ignored when running locally, so your script stays fully portable.
import cadquery as cq
params = {
"width": 30,
"depth": 20,
"height": 15,
"fillet": 3,
"style": "rounded",
}
printago = {
"width": {"min": 10, "max": 100, "description": "Box width in mm"},
"depth": {"min": 10, "max": 100, "description": "Box depth in mm"},
"height": {"min": 5, "max": 50, "label": "Height (mm)"},
"fillet": {"min": 0, "max": 10, "label": "Corner Radius"},
"style": {"options": ["rounded", "chamfered", "sharp"]},
}
Each key in the printago dict matches a key in params and supports these fields:
| Field | Type | Description |
|---|---|---|
min | number | Minimum allowed value (number parameters only) |
max | number | Maximum allowed value (number parameters only) |
description | string | Description shown in the UI for this parameter |
label | string | Short label for the parameter (used if description is not set) |
options | array | List of allowed values -- converts the field to a dropdown |
The printago dict is completely optional. Without it, Printago still generates UI controls from params -- you just won't have min/max constraints, descriptions, or dropdowns.
Multi-Color Assemblies
Both CadQuery and build123d support multi-color output, just like ColorSCAD. Each unique color in your script becomes a separate material slot, so you can assign different filaments to each color in the Printago UI.
If you use a local viewer like OCP CAD Viewer for model development, you can structure your script so it works both locally and in Printago. The try/except block for ocp_vscode is safely ignored in Printago's sandbox, so you can develop and preview with the same script you upload.
CadQuery
Build a cq.Assembly and assign colors with cq.Color():
import cadquery as cq
try:
from ocp_vscode import show
except ImportError:
show = None
params = {"size": 20}
body = cq.Workplane("XY").box(params["size"], params["size"], params["size"])
accent = cq.Workplane("XY").sphere(params["size"] * 0.3)
assy = cq.Assembly()
assy.add(body, color=cq.Color("blue"))
assy.add(accent, color=cq.Color("red"))
result = assy
if show:
show(result)
Colors cascade through nested assemblies -- a parent color applies to all children unless overridden:
assy = cq.Assembly()
sub = cq.Assembly()
sub.add(part_a) # inherits "blue" from parent
sub.add(part_b, color=cq.Color("red")) # overrides to red
assy.add(sub, color=cq.Color("blue"))
result = assy
Multi-color output requires returning a cq.Assembly. A single Workplane or shape produces single-color output.
build123d
Assign .color to individual shapes and set result to a list:
from build123d import *
try:
from ocp_vscode import show
except ImportError:
show = None
params = {"size": 20}
body = Box(params["size"], params["size"], params["size"])
body.color = Color("blue")
accent = Sphere(params["size"] * 0.3)
accent.color = Color("red")
result = [body, accent]
if show:
show(*result)
Multi-color output requires setting result to a list of shapes with .color set. Do not use Compound -- wrapping shapes in a Compound loses the per-shape color information and produces single-color output. A single shape (not in a list) also produces single-color output.
How Colors Are Processed
- Parts with the same color are merged into a single STL file
- Parts with no color are grouped together as a single uncolored file
- Each unique color becomes a material slot in the Printago UI, where you can assign filaments to each slot
- CadQuery: use a
cq.Assemblywithcolor=cq.Color(...)on each.add()call. Child nodes inherit their parent's color unless they specify their own. - build123d: set
.color = Color(...)on individual shapes and return them as a list. Color is per-shape -- there is no inheritance.
Supported Color Formats
Both cq.Color() (CadQuery) and Color() (build123d) accept the same formats:
# Named colors (CSS/X11 color names)
cq.Color("red") # CadQuery
Color("red") # build123d
# RGB floats (0.0 - 1.0)
cq.Color(1.0, 0.0, 0.0) # CadQuery
Color(0.39, 0.58, 0.93) # build123d
Color(0.2, 0.8, 0.4, 0.5) # with alpha (4th value)
# Hex integer (build123d only)
Color(0x4683CE)
Execution Environment
Scripts run in a secure sandboxed environment with:
- No network access -- scripts cannot make HTTP requests or download files
- No disk access -- scripts cannot read or write files outside the sandbox
- Resource limits -- 2 GB memory, 5-minute time limit
- Python 3.11 with the full standard library available
You can import any pre-installed library using standard Python import statements. See the Libraries page for the full list.
Local Development
Since Printago scripts are standard Python, you can develop and test them on your local machine before uploading.
Setup
pip install cadquery build123d
To include all the same libraries available in Printago:
pip install cadquery build123d bd-warehouse trimesh shapely scipy svgpathtools numpy-stl
Viewing Models
For real-time 3D preview while you code, we recommend one of:
- OCP CAD Viewer -- VS Code extension with inline 3D preview for both CadQuery and build123d
- CQ-editor -- Standalone GUI editor for CadQuery
Exporting STL Locally
To export your model to STL for local testing:
# CadQuery
import cadquery as cq
# ... your model code ...
cq.exporters.export(result, "model.stl")
# build123d
from build123d import export_stl
# ... your model code ...
export_stl(result, "model.stl")
Keep your params dictionary and result assignment in your script even during local development. This ensures your script works in both environments without modification.
Best Practices
Script Structure
- Place
paramsnear the top of your file, after imports but before geometry code - Use descriptive parameter names (
wall_thicknessnotwt) - Provide sensible defaults that produce a valid model
- Always set
resultto your final geometry
Performance
- CadQuery and build123d share the same OCCT kernel -- both produce identical geometry quality
- Minimize the number of boolean operations (unions, cuts) where possible
- For complex models, build up geometry incrementally rather than creating large compound operations
- Test your script's full parameter range to ensure it produces valid geometry at all values
3D Printing Considerations
- Design with your printer's tolerances in mind (typically 0.1-0.2mm for FDM)
- Use the pre-installed bd_warehouse library for standard threads, fasteners, and mechanical features instead of modeling them from scratch
- Consider wall thickness and overhang angles when designing parametric ranges
Using Existing CadQuery Scripts
The CadQuery examples gallery has dozens of ready-made scripts you can adapt for Printago. To make any existing CadQuery script work as a parametric Printago part:
- Keep the imports -- your
import cadquery as cq(or any other imports) stay as-is - Add a
paramsdict -- extract any hardcoded values you want to be configurable into aparamsdictionary near the top, after imports - Optionally add a
printagodict -- add min/max constraints, labels, or dropdown options for a better UI experience - Read values from
params-- replace hardcoded values withparams["key"]references - Set
result-- assign your final geometry toresultat the end of the script
For example, if an existing script has:
import cadquery as cq
width = 30
height = 15
box = cq.Workplane("XY").box(width, width, height)
You would adapt it by adding params and printago after the imports, replacing the hardcoded values with params lookups, and assigning result at the end -- the rest of the script stays unchanged:
import cadquery as cq
params = {
"width": 30,
"height": 15,
}
printago = {
"width": {"min": 5, "max": 100, "label": "Width (mm)"},
"height": {"min": 5, "max": 50, "label": "Height (mm)"},
}
width = params["width"]
height = params["height"]
box = cq.Workplane("XY").box(width, width, height)
result = box
Resources
- CadQuery Documentation
- CadQuery Examples -- ready-made scripts to adapt for Printago
- build123d Documentation
- Pre-installed Libraries
Need help? Join our Discord community for support and tips!