Add CadQuery wrappers and update docs

This commit is contained in:
Richard Ward
2025-07-18 15:25:39 +01:00
parent 7dad845cd8
commit dd01670041
8 changed files with 120 additions and 8 deletions

View File

@@ -1,8 +1,11 @@
# bamboogenerator
Python scripts for generating 3D printable models using the
`trimesh` library. The repository contains a consolidated
`parametric_cad` package with example scripts that produce STL files.
Python scripts for generating 3D printable models. The project now
leverages the [CadQuery](https://github.com/CadQuery/cadquery)
library for creating geometry while still providing utilities for
scaffolding generation and printability validation. The repository
contains a consolidated `parametric_cad` package with example scripts
that produce STL files.
## Folder layout
@@ -18,7 +21,9 @@ installer script in the repository root. From the desired project directory run
python install_requirements.py
```
This installs all Python dependencies and attempts to set up `trimesh` for OpenSCAD rendering.
This installs all Python dependencies including CadQuery. It also
configures `trimesh` for OpenSCAD rendering which is still used under
the hood for mesh operations.
## Running the examples
@@ -55,6 +60,31 @@ unioned = combine(boxes)
result = safe_difference(unioned, Cylinder(0.5, 1).mesh())
```
### Using CadQuery
CadQuery models can also be used with the validation and scaffolding
utilities by converting a `Workplane` to a `trimesh` mesh:
```python
import cadquery as cq
from parametric_cad import workplane_to_mesh, generate_scaffolding
wp = cq.Workplane("XY").box(10, 10, 5)
mesh = workplane_to_mesh(wp)
supports = generate_scaffolding(mesh)
```
You can also operate on CadQuery models directly using helper wrappers:
```python
import cadquery as cq
from parametric_cad import generate_scaffolding_from_workplane, PrintabilityValidator
wp = cq.Workplane("XY").box(10, 10, 5)
supports = generate_scaffolding_from_workplane(wp)
errors = PrintabilityValidator().validate_workplane(wp)
```
## Overhang Scaffolding
`generate_scaffolding` creates simple cylindrical supports beneath

View File

@@ -20,8 +20,22 @@ def install_openscad():
def install_python_packages():
logging.debug("Starting Python package installation process")
print("Installing all required Python packages including triangulation engines...")
packages = ["trimesh", "numpy", "matplotlib", "pyglet<2", "networkx", "scipy", "shapely",
"triangle", "mapbox_earcut", "manifold3d", "pillow", "requests", "beautifulsoup4"]
packages = [
"trimesh",
"cadquery",
"numpy",
"matplotlib",
"pyglet<2",
"networkx",
"scipy",
"shapely",
"triangle",
"mapbox_earcut",
"manifold3d",
"pillow",
"requests",
"beautifulsoup4",
]
try:
subprocess.run([sys.executable, "-m", "pip", "install"] + packages, check=True)
logging.info("Python packages installed successfully")

View File

@@ -11,7 +11,8 @@ from .primitives.sphere import Sphere
from .mechanisms.butthinge import ButtHinge
from .export.stl import STLExporter
from .printability import PrintabilityValidator
from .scaffolding import generate_scaffolding
from .scaffolding import generate_scaffolding, generate_scaffolding_from_workplane
from .cadquery_utils import workplane_to_mesh
__all__ = [
"tm",
@@ -28,6 +29,8 @@ __all__ = [
"STLExporter",
"PrintabilityValidator",
"generate_scaffolding",
"generate_scaffolding_from_workplane",
"workplane_to_mesh",
"Polygon",
"Point",
"box",

View File

@@ -0,0 +1,13 @@
import io
import cadquery as cq
from cadquery.occ_impl import exporters
from .core import tm
def workplane_to_mesh(wp: cq.Workplane) -> tm.Trimesh:
"""Convert a CadQuery Workplane to a Trimesh mesh."""
stl = exporters.toString(wp.val(), exporters.ExportTypes.STL)
return tm.load(io.BytesIO(stl.encode("utf-8")), file_type="stl")
__all__ = ["workplane_to_mesh"]

View File

@@ -5,6 +5,8 @@ from pathlib import Path
from typing import List
from .core import tm
import cadquery as cq
from .cadquery_utils import workplane_to_mesh
import numpy as np
@@ -85,5 +87,11 @@ class PrintabilityValidator:
mesh = tm.load(file_path)
return self.validate_mesh(mesh)
def validate_workplane(self, wp: cq.Workplane) -> List[str]:
"""Validate a CadQuery Workplane."""
mesh = workplane_to_mesh(wp)
return self.validate_mesh(mesh)
__all__ = ["PrintabilityValidator"]

View File

@@ -3,6 +3,8 @@ from __future__ import annotations
import numpy as np
from .core import tm
import cadquery as cq
from .cadquery_utils import workplane_to_mesh
def generate_scaffolding(
@@ -65,4 +67,14 @@ def generate_scaffolding(
return tm.util.concatenate(supports)
__all__ = ["generate_scaffolding"]
def generate_scaffolding_from_workplane(
wp: cq.Workplane,
**kwargs,
) -> tm.Trimesh:
"""Convert a Workplane and generate scaffolding."""
mesh = workplane_to_mesh(wp)
return generate_scaffolding(mesh, **kwargs)
__all__ = ["generate_scaffolding", "generate_scaffolding_from_workplane"]

View File

@@ -0,0 +1,10 @@
import cadquery as cq
from parametric_cad.cadquery_utils import workplane_to_mesh
from parametric_cad.core import tm
def test_workplane_to_mesh():
wp = cq.Workplane("XY").box(1, 1, 1)
mesh = workplane_to_mesh(wp)
assert isinstance(mesh, tm.Trimesh)
assert mesh.volume > 0

View File

@@ -0,0 +1,22 @@
import cadquery as cq
from parametric_cad import (
generate_scaffolding_from_workplane,
PrintabilityValidator,
tm,
)
def test_scaffolding_from_workplane():
base = cq.Workplane("XY").box(2, 2, 1)
overhang = cq.Workplane("XY").box(1, 1, 0.5).translate((1.5, 0.5, 1))
wp = base.union(overhang)
scaff = generate_scaffolding_from_workplane(wp)
assert isinstance(scaff, tm.Trimesh)
assert scaff.vertices.shape[0] > 0
def test_validate_workplane():
wp = cq.Workplane("XY").box(10, 10, 10)
validator = PrintabilityValidator()
errors = validator.validate_workplane(wp)
assert errors == []