diff --git a/parametric_cad/primitives/cylinder.py b/parametric_cad/primitives/cylinder.py new file mode 100644 index 0000000..4731cd9 --- /dev/null +++ b/parametric_cad/primitives/cylinder.py @@ -0,0 +1,18 @@ +import trimesh + +class Cylinder: + def __init__(self, radius, height, sections=32): + self.radius = radius + self.height = height + self.sections = sections + self._position = (0, 0, 0) + + def at(self, x, y, z): + self._position = (x, y, z) + return self + + def mesh(self): + cyl = trimesh.creation.cylinder(radius=self.radius, height=self.height, + sections=self.sections) + cyl.apply_translation(self._position) + return cyl diff --git a/parametric_cad/primitives/sphere.py b/parametric_cad/primitives/sphere.py new file mode 100644 index 0000000..96b73bc --- /dev/null +++ b/parametric_cad/primitives/sphere.py @@ -0,0 +1,17 @@ +import trimesh + +class Sphere: + def __init__(self, radius, subdivisions=3): + self.radius = radius + self.subdivisions = subdivisions + self._position = (0, 0, 0) + + def at(self, x, y, z): + self._position = (x, y, z) + return self + + def mesh(self): + sph = trimesh.creation.icosphere(subdivisions=self.subdivisions, + radius=self.radius) + sph.apply_translation(self._position) + return sph diff --git a/tests/test_export.py b/tests/test_export.py index ea3d686..d8dd6fd 100644 --- a/tests/test_export.py +++ b/tests/test_export.py @@ -2,6 +2,8 @@ import os import pytest import trimesh from parametric_cad.primitives.box import Box +from parametric_cad.primitives.cylinder import Cylinder +from parametric_cad.primitives.sphere import Sphere from parametric_cad.export.stl import STLExporter @@ -11,3 +13,16 @@ def test_stl_exporter(tmp_path): path = exporter.export_mesh(box, "test_box", preview=False) assert os.path.isfile(path) assert path == str(tmp_path / "test_box.stl") + + +def test_ascii_stl_multiple_objects(tmp_path): + exporter = STLExporter(output_dir=tmp_path, binary=False) + box = Box(1.0, 2.0, 1.0) + cyl = Cylinder(radius=0.5, height=1.0) + sph = Sphere(radius=0.25) + path = exporter.export_meshes([box, cyl, sph], "combo", preview=False) + assert os.path.isfile(path) + assert path == str(tmp_path / "combo.stl") + with open(path, "r", encoding="utf-8") as f: + first_line = f.readline().strip() + assert first_line.startswith("solid") diff --git a/tests/test_primitives.py b/tests/test_primitives.py index e25f248..20e8cd7 100644 --- a/tests/test_primitives.py +++ b/tests/test_primitives.py @@ -5,6 +5,8 @@ from math import cos, pi from parametric_cad.primitives.box import Box from parametric_cad.primitives.gear import SpurGear +from parametric_cad.primitives.cylinder import Cylinder +from parametric_cad.primitives.sphere import Sphere def test_box_mesh_extents_and_position(): @@ -21,3 +23,14 @@ def test_spur_gear_diameters(): assert gear.pitch_diameter == pytest.approx(20.0) expected_base = gear.pitch_diameter * cos(20 * pi / 180) assert gear.base_diameter == pytest.approx(expected_base) + + +def test_cylinder_and_sphere_meshes(): + cyl = Cylinder(radius=1.0, height=2.0).at(0.5, 0.5, 0) + sph = Sphere(radius=1.0).at(-0.5, -0.5, 0) + cyl_mesh = cyl.mesh() + sph_mesh = sph.mesh() + assert isinstance(cyl_mesh, trimesh.Trimesh) + assert isinstance(sph_mesh, trimesh.Trimesh) + assert cyl_mesh.is_watertight + assert sph_mesh.is_watertight