# Authoring CAD with CadQuery CadQuery is a Python library for creating parametric boundary-representation CAD models. OpenVCAD complements it: CadQuery defines the part boundary, then OpenVCAD attaches continuous attributes such as modulus, density, color, or volume fractions throughout that part's volume. This guide assumes you have completed [Getting Started](getting-started.md) and [Functional grading](gradients.md). It stays with CadQuery's fluent `Workplane` API; see the [CadQuery documentation](https://cadquery.readthedocs.io/en/latest/) for sketches, selectors, assemblies, and advanced operations. ## Install CadQuery CadQuery is optional because it brings a substantial CAD kernel with it: ```bash pip install "OpenVCAD[cad]" ``` The workflows below need only `cadquery`, `pyvcad`, and `pyvcad_rendering`. They do not require `pyvcad-metamaterials`. ## The handoff: closed solids become volumes CadQuery works with boundary representations: vertices form edges, edges form wires, wires form faces, and closed faces form a solid. OpenVCAD needs the last of those for volumetric modeling: its implicit CAD node must be a closed `Solid`, or a compound made entirely of closed solids. A face, shell, wire, or sketch has no unambiguous inside/outside region and cannot be converted into an OpenVCAD volume. `pv.CADModel.from_cadquery(...)` accepts a CadQuery `Workplane`, `Shape`, or selected iterable. It retains the model through an in-memory BREP transfer; no temporary STEP file is written. Call `to_node()` only after the selected result is a closed volume. ```python cad_model = pv.CADModel.from_cadquery(cadquery_part) root = cad_model.to_node(use_fast_mode=True) ``` Use `use_fast_mode=True` for interactive rendering and voxel-style workflows. It discretizes the solid during `prepare()` for faster sampling. Use `False` when exact CAD evaluation is more important than speed. ## Inspect the CadQuery model first CadQuery itself is a library, not a viewer. To inspect a work-in-progress in [CQ-Editor](https://github.com/CadQuery/CQ-editor), append this line after building the part: ```python show_object(cadquery_part) ``` `show_object` is supplied by CQ-Editor's CadQuery execution environment, so do not add it to a normal terminal script. In CQ-Editor it lets you inspect intermediate solids before handing the final closed solid to OpenVCAD. CadQuery documents this script-output convention in its [object-output guide](https://cadquery.readthedocs.io/en/latest/fileformat.html). ## A toy part: parametric mounting plate Start with a workplane, make a base box, select its top face, then add a raised boss and a hole. The parameters below serve two jobs: they size the CAD features and normalize the OpenVCAD modulus field across the plate. ```python import cadquery as cq import pyvcad as pv import pyvcad_rendering as viz plate_length = 48.0 plate_width = 30.0 plate_thickness = 4.0 boss_length = 28.0 boss_width = 14.0 boss_height = 8.0 hole_diameter = 6.0 modulus_min = 800.0 modulus_max = 2800.0 cadquery_part = ( cq.Workplane("XY") .box(plate_length, plate_width, plate_thickness) .faces(">Z") .workplane() .rect(boss_length, boss_width) .extrude(boss_height) .faces(">Z") .workplane() .hole(hole_diameter) ) root = pv.CADModel.from_cadquery(cadquery_part).to_node(use_fast_mode=True) modulus_expr = ( f"{modulus_min} + ({modulus_max} - {modulus_min}) " f"* ((x + {plate_length / 2.0}) / {plate_length})" ) root.set_attribute(pv.DefaultAttributes.MODULUS, pv.FloatAttribute(modulus_expr)) viz.Render(root) ```
OpenVCAD render of a CadQuery mounting plate
CadQuery solid in OpenVCAD
OpenVCAD modulus render of a CadQuery mounting plate
Modulus driven by plate length
The complete script is [`cadquery_parametric_mounting_plate.py`](../../../examples/geometry/cad/cadquery_parametric_mounting_plate.py). ## Practical part: compliant cable clamp This cable clamp starts from two concentric circles extruded into a ring. Subtracting the rectangular opening turns the ring into a closed C-shaped clamp. `cable_radius` and `wall_thickness` determine both the clamp geometry and the span of the stiffness transition. ```python import cadquery as cq import pyvcad as pv import pyvcad_rendering as viz cable_radius = 7.0 wall_thickness = 3.0 clamp_width = 12.0 opening_width = 8.0 opening_depth = 10.0 modulus_min = 450.0 modulus_max = 2200.0 outer_radius = cable_radius + wall_thickness ring = cq.Workplane("XY").circle(outer_radius).circle(cable_radius).extrude(clamp_width) opening = ( cq.Workplane("XY") .center(outer_radius - opening_depth / 2.0, 0.0) .rect(opening_depth, opening_width) .extrude(clamp_width) ) cadquery_part = ring.cut(opening) root = pv.CADModel.from_cadquery(cadquery_part).to_node(use_fast_mode=True) modulus_expr = ( f"{modulus_min} + ({modulus_max} - {modulus_min}) " f"* clamp((x + {outer_radius}) / {2.0 * outer_radius}, 0, 1)" ) root.set_attribute(pv.DefaultAttributes.MODULUS, pv.FloatAttribute(modulus_expr)) viz.Render(root) ```
OpenVCAD render of a CadQuery cable clamp
Closed cable-clamp solid
OpenVCAD modulus render of a CadQuery cable clamp
Soft entry transitioning to a stiff anchor
The complete script is [`cadquery_compliant_cable_clamp.py`](../../../examples/geometry/cad/cadquery_compliant_cable_clamp.py). ## Practical part: equipment bracket For a mounting bracket, build and perforate the base before fusing it with an upright. `upright_height` controls both the upright's geometry and the vertical modulus ramp, providing a simple way to express a stiffer load-bearing arm above a more compliant mounting base. ```python import cadquery as cq import pyvcad as pv import pyvcad_rendering as viz base_length = 64.0 base_width = 36.0 base_thickness = 5.0 upright_height = 48.0 upright_thickness = 6.0 mounting_hole_diameter = 6.0 mounting_hole_spacing = 42.0 modulus_min = 1200.0 modulus_max = 4200.0 base = ( cq.Workplane("XY") .box(base_length, base_width, base_thickness) .faces(">Z") .workplane() .pushPoints([(-mounting_hole_spacing / 2.0, 0.0), (mounting_hole_spacing / 2.0, 0.0)]) .hole(mounting_hole_diameter) ) upright = ( cq.Workplane("XY") .box(upright_thickness, base_width, upright_height) .translate((base_length / 2.0 - upright_thickness / 2.0, 0.0, upright_height / 2.0)) ) cadquery_part = base.union(upright) root = pv.CADModel.from_cadquery(cadquery_part).to_node(use_fast_mode=True) modulus_expr = ( f"{modulus_min} + ({modulus_max} - {modulus_min}) " f"* clamp(z / {upright_height}, 0, 1)" ) root.set_attribute(pv.DefaultAttributes.MODULUS, pv.FloatAttribute(modulus_expr)) viz.Render(root) ```
OpenVCAD render of a CadQuery equipment bracket
Parametric equipment bracket
OpenVCAD modulus render of an equipment bracket
Stiffness increasing toward the upright
The complete script is [`cadquery_graded_equipment_bracket.py`](../../../examples/geometry/cad/cadquery_graded_equipment_bracket.py). ## Import a pre-existing CAD file The same attribute-modeling workflow applies to a closed CAD file received from another authoring tool. [`bracket_import.py`](../../../examples/geometry/cad/bracket_import.py) selects either the bundled STEP or IGES representation with `cad_format`, creates a `pv.CAD` leaf, and applies a modulus ramp along the bracket height. ```python cad_format = "step" # Change to "iges" to load the alternate file. root = pv.CAD(str(model_path), use_fast_mode=True) root.set_attribute( pv.DefaultAttributes.MODULUS, pv.FloatAttribute("900.0 + 1700.0 * clamp(z / 32.0, 0, 1)"), ) ```
OpenVCAD render of an imported bracket
Imported closed bracket
OpenVCAD modulus render of an imported bracket
Attribute field on imported geometry
Run `python scripts/generate_cadquery_guide_assets.py` from the repository root to regenerate every figure in this guide.