CadQuery high-level design — top-down architecture
Purpose
Prove decomposition, proportions, and placement before any detailed work. Output is an architectural baseline the bottom-up phase replaces part-by-part while keeping names, hierarchy, and common.py dimensions.
Authoritative rules
Follow leo-monolith/resources/functions/system-messages/cad-generation.txt — High-Level Design section.
Required outputs (two files)
1. common.py (create first)
- Coordinate convention block (
Origin,+X/+Y/+Z, floor/table Z). - Envelope parameters only — one variable per envelope dimension (mm).
# DECOMPOSITIONcomment table — every part: function name, sub-assembly, L×W×H, origin rule, mating partners, OTS flag.MATE_*constants for interfaces that Phase B must honor (hole PCD, bore Ø, stack heights, snap ledge size). Example:
# MATE: lower_housing ↔ upper_housing — six M3 snaps on 140×100 mm rectangle
M3_SNAP_PCD_X = 140.0
M3_SNAP_PCD_Y = 100.0
M3_SNAP_COUNT = 6
No geometry in common.py.
2. high_level_assembly.py
importenvelope values fromcommon.py.- One primitive per part:
.box(),.cylinder(), or single.extrude()— no booleans. - Nested
cq.Assemblytree matching the final product (same sub-assembly names as future*_assembly.pyfiles). result = assyat top level.
DO include
- Correct envelope dimensions and relative placement.
- Sub-assembly grouping (≤ ~6 top-level groups).
- Function-named variables (
drive_shaft, notpart1). - One-line
# VENDOR — <category>, <envelope size>comments for OTS parts (full BOM text waits for DFM).
DO NOT include
Holes, fillets, chamfers, ribs, shells, fastener zones, press-fit spigots, snap beams, counterbores, engraved text, or procurement-grade vendor comments.
Hollow housings → solid block envelope. Brackets with gussets → plain plate envelope.
Placement rules (critical for good results)
Floor-standing / table-top parts
CadQuery .box() is centered on XY by default; control Z with centered tuple:
from common import HOUSING_W, HOUSING_D, LOWER_H
# Bottom face at Z=0, grows upward — NO extra Location Z offset
lower_housing = cq.Workplane("XY").box(
HOUSING_W, HOUSING_D, LOWER_H,
centered=(True, True, False),
)
base_assy.add(lower_housing, name="lower_housing",
loc=cq.Location(cq.Vector(0, 0, 0)))
# ❌ WRONG — double-counts height if box is already bottom-anchored
loc=cq.Location(cq.Vector(0, 0, LOWER_H / 2))
Stacked mates
Place child so anchor face touches anchor face:
# Column sits on base top (Z = BASE_TOP_Z)
column_assy.add(
column,
name="column",
loc=cq.Location(cq.Vector(0, 0, BASE_TOP_Z)), # column also bottom-anchored
)
Define BASE_TOP_Z in common.py, not magic numbers in the assembly file.
Symmetric repeats
Derive from pattern constants in common.py:
for i, angle in enumerate((0, 90, 180, 270)):
x = (PCD / 2) * math.cos(math.radians(angle))
y = (PCD / 2) * math.sin(math.radians(angle))
base_assy.add(foot, name=f"foot_{i}", loc=cq.Location(cq.Vector(x, y, -FOOT_H)))
Prefer constraints when testing a single mate pair
For one critical coaxial stack, a miniature constrain().solve() prototype is allowed in comments or a scratch block — but high-level default is explicit loc= from common.py math.
File template
import cadquery as cq
import math
from common import (
HOUSING_W, HOUSING_D, LOWER_H, BASE_TOP_Z,
# ... all envelope dims
)
lower_housing = cq.Workplane("XY").box(HOUSING_W, HOUSING_D, LOWER_H, centered=(True, True, False))
column = cq.Workplane("XY").box(COL_W, COL_D, COL_H, centered=(True, True, False))
base_assy = cq.Assembly(name="base")
base_assy.add(lower_housing, name="lower_housing", loc=cq.Location(cq.Vector(0, 0, 0)))
column_assy = cq.Assembly(name="column")
column_assy.add(column, name="column", loc=cq.Location(cq.Vector(0, 0, BASE_TOP_Z)))
assy = cq.Assembly(name="product_name")
assy.add(base_assy)
assy.add(column_assy)
result = assy
Workflow
- Write
common.py(convention + decomposition comment + parameters +MATE_*). - Write
high_level_assembly.py(primitives + nested assemblies). - Smoke:
python -c "import runpy; runpy.run_path('high_level_assembly.py')"— must not raise. - Invoke
cadquery-render; orchestrator reads PNGs. - Present decomposition table + ask user to confirm proportions.
- Iterate envelopes in
common.py/ placements until confirmed. - Hand off frozen
common.py+high_level_assembly.pyto Phase B.
Hard rules
- One primitive per part — no booleans.
- No holes, fillets, shells.
- Nested assemblies by function — do not flatten 20 parts on root.
- Top-level
result = assy. - All shared dims in
common.py— not duplicated inhigh_level_assembly.py. - Sub-assembly names must match what Phase B will generate (
base_assembly→base_assembly.py).
When to stop
User confirmed decomposition and proportions. Do not start functional parts until the orchestrator receives that confirmation.