ADR-017: Parametric CAD System

Status

Accepted

Context

The Amalgam requires a parametric CAD system that supports: - Customizable Dimensions: Variable build volumes, rod lengths, bed sizes - Code-First Approach: Version control, diff-friendly, scriptable - Precision Geometry: NURBS/BREP for accurate manufacturing - Parametric Flexibility: Easy dimension changes without manual remodeling - Integration: Works with Python ecosystem (scripts, NumPy, APIs) - Professional Output: STEP files for CNC, STL for 3D printing

In 2026, several CAD paradigms exist with different trade-offs: 1. Visual/Drag-Drop: TinkerCAD, Fusion 360 2. GUI Parametric: FreeCAD, SolidWorks 3. Scripted CSG: OpenSCAD 4. Scripted BREP: build123d

Each has different capabilities for parametric hardware design.

Decision

We adopt build123d as the core parametric CAD system.

CAD Paradigm Comparison

Feature TinkerCAD FreeCAD (GUI) OpenSCAD build123d
Core Paradigm Simple CSG (Visual) BREP (Feature Tree) Scripted CSG Scripted BREP
Language None (Drag/Drop) Python (Optional) OpenSCAD (Domain) Python (Native)
Geometry Meshes Precise NURBS Meshes/CSG Precise NURBS/STEP
Selection Mouse Mouse (Unstable) Manual Math Smart Selectors
Version Control Poor Difficult (Binary) Excellent (Text) Excellent (Python)
Parametric Limited Good Excellent Excellent
Learning Curve Low High Medium Medium

Why build123d

1. Object Awareness (BREP Advantage) - Unlike OpenSCAD’s CSG, build123d understands topology - Can find faces, edges, and vertices programmatically - Example: “Find top-most face and fillet all edges parallel to X-axis” - If part grows/shrinks, script still finds correct features - OpenSCAD requires manual coordinate tracking that breaks on size changes

2. Native Python Power - No proprietary CAD language to learn - Full Python ecosystem: NumPy for math, requests for APIs, logging for debugging - pip install infrastructure for extensions - Standard Python tooling: pytest, black, ruff, mypy - Familiar to developers, steep learning curve only for CAD concepts

3. “Builder” vs “Algebra” Modes - with BuildPart() as part: context mimics human building workflow - “I am working on this face, now sketching here” - More readable than functional CSG languages - Supports both algebraic (Box() - Cylinder()) and builder (build123d)

4. Professional Output - Exports true STEP files (industry standard for CNC, manufacturing) - STL export with high-quality mesh generation - BREP kernel (OpenCascade) provides precise geometry - OpenSCAD largely stuck in STL world (mesh approximations)

5. Fillet/Chamfer Support - OpenSCAD: Mathematically exhausting for complex edges - FreeCAD: Fillets work but can break on topology changes - build123d: Smart selectors + BREP make fillets reliable - Can fillet specific edges based on orientation, length, etc.

Why Alternatives Were Rejected

OpenSCAD (The “Old Guard” of Code-CAD) - Math Tax: Must track coordinates manually (no object awareness) - Fillet/Chamfer Nightmare: Complex geometry is mathematically exhausting - Non-Standard Language: Proprietary functional language with no ecosystem - Mesh-Only Output: Lacks STEP export for professional manufacturing - Slow Rendering: CSG evaluation is slower than BREP

FreeCAD & GUI Systems - Topological Naming Problem: Internal face names “shuffle” on dimension changes - Breaking features: Hole suddenly on wrong side after early dimension change - Hard to Automate: Python API bolted onto GUI-first workflow - GUI Dependency: Requires graphical environment (not server-friendly) - Binary Files: Poor version control, diff-friendly text not available

TinkerCAD & Visual Systems - No Version Control: Binary files, impossible to diff - No Parametric: Manual remodeling for dimension changes - Poor Precision: Mesh-based approximation vs NURBS - No Automation: Drag-drop cannot be scripted - Limited Ecosystem: No integration with Python tooling

Integration with Quarto (ADR-018)

Literate CAD: - build123d is Python, Quarto executes Python cells - Can embed CAD code directly in documentation - Automated rendering: Generate part views during doc build - Diagrams stay synchronized with code

Single Source of Truth: - CAD code and documentation in same ecosystem - Change dimension → regenerate STLs → regenerate docs → all synchronized - No manual screenshot taking or image updating

Consequences

Benefits

  • Parametric Power: Infinite permutations, single codebase
  • Python Ecosystem: NumPy, pytest, black, ruff, mypy, requests, logging
  • Object Awareness: Smart selectors for reliable geometry operations
  • Version Control: Text files, diff-friendly, git-friendly
  • Professional Output: STEP files for CNC, STL for 3D printing
  • Automatable: CLI-first, no GUI dependency
  • Community: Growing Python CAD community, not niche OpenSCAD ecosystem

Trade-offs

  • Learning Curve: Requires learning build123d API + CAD concepts
  • Python Required: Users need Python environment to generate parts
  • Preview Limited: No visual editor (though OCP-VSCode provides some visualization)
  • Performance: BREP operations can be slower than simple CSG
  • Ecosystem: Smaller than OpenSCAD community (but growing)

What This Enables

  • Infinite Customization: User changes one dimension, entire machine regenerates
  • Automated Workflows: CI/CD for CAD validation, doc generation
  • Web UI: Server-side part generation for non-technical users
  • Literate Documentation: CAD code embedded in Quarto docs (ADR-018)
  • Integration: NumPy for complex geometry, APIs for pulling data

What This Replaces

  • Static STL repositories (manual management, limited permutations)
  • OpenSCAD scripts (object awareness, Python ecosystem)
  • FreeCAD files (topological naming, GUI dependency)
  • Visual CAD (no version control, no automation)

Implementation Notes

build123d Project Structure

cad/
├── include/              # Shared components (brackets, pucks)
│   ├── corner_components.py
│   ├── puck_components.py
│   └── ...
├── parts/                # Individual parts
│   ├── corner_standard.py
│   ├── bed_spider.py
│   └── ...
├── config.py.example      # Reference configuration
├── config.py             # User configuration (gitignored)
├── build.sh               # Build script (STL generation)
├── configure.py           # Configuration wizard
└── .venv/                # Python virtual environment

Code Style

# Import pattern (parts/)
import sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))

from include.corner_components import make_standard_corner

# Always use config values (no fallbacks)
from config import CORNER_SIZE, M12_FIT_DIA

def main():
    from build123d import export_stl as export_stl_func
    corner = make_standard_corner(
        corner_size=CORNER_SIZE,
        m12_fit_dia=M12_FIT_DIA,
    )
    export_stl_func(corner, "stl/filename.stl")

Smart Selectors Example

# Find all faces normal to Z-axis
top_faces = part.faces().filter_by_position(Axis.Z, position=0)

# Find edges parallel to X and longer than 10mm
x_parallel_edges = part.edges().filter_by_direction(Axis.X)
x_parallel_long = x_parallel_edges.filter_by_length(min=10)

# Fillet all matching edges
part = part.fillet(x_parallel_long, radius=2)

Builder Context Example

with BuildPart() as corner:
    # Sketch on XY plane
    Box(50, 50, 20)
    # Select top face
    top = faces().sort_by(Axis.Z).last
    with BuildSketch(top.workplane()):
        Circle(10)
    extrude(amount=5)

Version Control Best Practices

  • Text Files: build123d scripts are plain text, git-friendly
  • Diff-Friendly: Changes show line-by-line, not binary diffs
  • Review: Can review PRs with git diff
  • Blame: Can trace design decisions with git blame
  • CI/CD: Lint (ruff), typecheck (mypy), test (pytest) on PRs

References

  • docs/reference/ai-conversations/why-build123d.md: Complete CAD discussion
  • docs/adr/018-documentation-system.md: Quarto documentation system
  • build123d Documentation: build123d.io
  • OpenCascade (BREP Kernel): opencascade.com
  • OCP-VSCode Preview: OCVSCode

Evolution Notes

This ADR establishes build123d as the core CAD system. Future CAD technologies will be evaluated against: - Python native (not proprietary language) - BREP with object awareness (not CSG only) - STEP export capability (professional manufacturing) - Smart selectors for reliable geometry operations - Python ecosystem integration (NumPy, APIs, etc.)

Alternative emerging technologies: - CadQuery (older version of build123d) - PythonOCC (lower-level OpenCascade bindings) - CQ-Editor (build123d GUI, but CLI-first preferred)