mirror of
https://github.com/johndoe6345789/bamboogenerator.git
synced 2026-04-24 13:24:54 +00:00
Centralize trimesh import
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
import logging
|
||||
|
||||
logging.basicConfig(filename='install_debug.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
@@ -15,15 +15,15 @@ def configure_trimesh_scad():
|
||||
print("Configuring trimesh to use OpenSCAD engine...")
|
||||
logging.info("Checking trimesh module availability")
|
||||
|
||||
# Check if trimesh is available
|
||||
try:
|
||||
import trimesh
|
||||
logging.info("trimesh module found")
|
||||
print("trimesh module found.")
|
||||
except ImportError:
|
||||
logging.error("trimesh module not found")
|
||||
print("trimesh module not found! Please ensure it is installed via 'pip install trimesh'.")
|
||||
sys.exit(1)
|
||||
# Check if trimesh (the mesh backend) is available
|
||||
try:
|
||||
_ = tm.__version__
|
||||
logging.info("trimesh module found")
|
||||
print("trimesh module found.")
|
||||
except Exception:
|
||||
logging.error("trimesh module not found")
|
||||
print("trimesh module not found! Please ensure it is installed via 'pip install trimesh'.")
|
||||
sys.exit(1)
|
||||
|
||||
# Check if OpenSCAD executable exists
|
||||
logging.debug(f"Verifying OpenSCAD existence at {openscad_path}")
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"""Consolidated parametric_cad package."""
|
||||
|
||||
from .core import tm
|
||||
from .primitives.box import Box
|
||||
from .primitives.gear import SpurGear
|
||||
from .mechanisms.butthinge import ButtHinge
|
||||
from .export.stl import STLExporter
|
||||
|
||||
__all__ = ["Box", "SpurGear", "ButtHinge", "STLExporter"]
|
||||
__all__ = ["tm", "Box", "SpurGear", "ButtHinge", "STLExporter"]
|
||||
|
||||
15
parametric_cad/core.py
Normal file
15
parametric_cad/core.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""Backend access for parametric_cad.
|
||||
|
||||
This module centralizes interaction with the mesh backend so other
|
||||
parts of the package do not need to import the backend directly.
|
||||
Currently :mod:`trimesh` provides all geometry functionality, but this
|
||||
wrapper allows the backend to be swapped or mocked easily.
|
||||
"""
|
||||
|
||||
import trimesh as _trimesh
|
||||
|
||||
# Public alias so that other modules can use the backend without
|
||||
# importing ``trimesh`` themselves.
|
||||
tm = _trimesh
|
||||
|
||||
__all__ = ["tm"]
|
||||
@@ -1,6 +1,5 @@
|
||||
from parametric_cad.primitives.box import Box
|
||||
from parametric_cad.export.stl import STLExporter
|
||||
import trimesh
|
||||
|
||||
outer = Box(100, 60, 40).at(0, 0, 0)
|
||||
inner = Box(90, 50, 30).at(5, 5, 5)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import trimesh
|
||||
import trimesh.exchange.stl
|
||||
from parametric_cad.core import tm
|
||||
import os
|
||||
import logging
|
||||
import numpy as np
|
||||
@@ -18,7 +17,7 @@ class STLExporter:
|
||||
os.makedirs(self.output_dir, exist_ok=True)
|
||||
|
||||
def _ensure_mesh(self, obj):
|
||||
if isinstance(obj, trimesh.Trimesh):
|
||||
if isinstance(obj, tm.Trimesh):
|
||||
return obj
|
||||
if hasattr(obj, "mesh"):
|
||||
m = obj.mesh
|
||||
@@ -31,7 +30,7 @@ class STLExporter:
|
||||
|
||||
def export_meshes(self, objs, base_filename, timestamp=False, preview=True):
|
||||
meshes = [self._ensure_mesh(o) for o in objs]
|
||||
combined = trimesh.util.concatenate(meshes)
|
||||
combined = tm.util.concatenate(meshes)
|
||||
if not combined.is_watertight or combined.vertices.shape[0] == 0:
|
||||
repaired = combined.fill_holes()
|
||||
if repaired is not False:
|
||||
@@ -47,7 +46,7 @@ class STLExporter:
|
||||
if self.binary:
|
||||
combined.export(path, file_type="stl")
|
||||
else:
|
||||
text = trimesh.exchange.stl.export_stl_ascii(combined)
|
||||
text = tm.exchange.stl.export_stl_ascii(combined)
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
logging.info(f"Exported STL to {path}")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import trimesh
|
||||
import numpy as np
|
||||
from parametric_cad.export.stl import STLExporter
|
||||
from parametric_cad.core import tm
|
||||
import numpy as np
|
||||
from parametric_cad.export.stl import STLExporter
|
||||
|
||||
class ButtHinge:
|
||||
def __init__(self, leaf_length=50.0, leaf_width=25.0, leaf_thickness=2.0, knuckles=5, pin_diameter=3.0):
|
||||
@@ -23,7 +23,7 @@ class ButtHinge:
|
||||
self.exporter = STLExporter()
|
||||
|
||||
def _create_hinge(self):
|
||||
"""Create a trimesh object representing the butt hinge."""
|
||||
"""Create a trimesh object representing the butt hinge."""
|
||||
# Create two leaves
|
||||
leaf_vertices = np.array([
|
||||
[0, 0, 0], [self.leaf_length, 0, 0], [self.leaf_length, self.leaf_width, 0],
|
||||
@@ -35,7 +35,7 @@ class ButtHinge:
|
||||
[0, 1, 5], [0, 5, 4], [2, 3, 7], [2, 7, 6],
|
||||
[0, 3, 7], [0, 7, 4], [1, 2, 6], [1, 6, 5]
|
||||
])
|
||||
leaf1 = trimesh.Trimesh(vertices=leaf_vertices, faces=leaf_faces, process=False)
|
||||
leaf1 = tm.Trimesh(vertices=leaf_vertices, faces=leaf_faces, process=False)
|
||||
|
||||
# Second leaf, offset for hinge alignment
|
||||
leaf2 = leaf1.copy()
|
||||
@@ -46,7 +46,7 @@ class ButtHinge:
|
||||
knuckle_spacing = self.leaf_length / (self.knuckles + 1)
|
||||
for i in range(self.knuckles):
|
||||
z_pos = knuckle_spacing * (i + 1)
|
||||
knuckle = trimesh.creation.cylinder(
|
||||
knuckle = tm.creation.cylinder(
|
||||
radius=self.pin_diameter / 2,
|
||||
height=self.leaf_thickness + 0.1, # Slight overlap for union
|
||||
sections=16
|
||||
@@ -55,7 +55,7 @@ class ButtHinge:
|
||||
knuckle_meshes.append(knuckle)
|
||||
|
||||
# Combine leaves and knuckles
|
||||
hinge = trimesh.util.concatenate([leaf1, leaf2] + knuckle_meshes)
|
||||
hinge = tm.util.concatenate([leaf1, leaf2] + knuckle_meshes)
|
||||
return hinge
|
||||
|
||||
def mesh(self):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
|
||||
class Box:
|
||||
def __init__(self, width, depth, height):
|
||||
@@ -12,6 +12,6 @@ class Box:
|
||||
return self
|
||||
|
||||
def mesh(self):
|
||||
box = trimesh.creation.box(extents=(self.width, self.depth, self.height))
|
||||
box = tm.creation.box(extents=(self.width, self.depth, self.height))
|
||||
box.apply_translation(self._position)
|
||||
return box
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
|
||||
class Cylinder:
|
||||
def __init__(self, radius, height, sections=32):
|
||||
@@ -12,7 +12,7 @@ class Cylinder:
|
||||
return self
|
||||
|
||||
def mesh(self):
|
||||
cyl = trimesh.creation.cylinder(radius=self.radius, height=self.height,
|
||||
sections=self.sections)
|
||||
cyl = tm.creation.cylinder(radius=self.radius, height=self.height,
|
||||
sections=self.sections)
|
||||
cyl.apply_translation(self._position)
|
||||
return cyl
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import numpy as np
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
from shapely.geometry import Polygon
|
||||
from math import pi, sin, cos, tan
|
||||
import logging
|
||||
@@ -75,21 +75,21 @@ class SpurGear:
|
||||
polygon = polygon.buffer(0)
|
||||
logging.warning("Tooth polygon was invalid, repaired with buffer")
|
||||
|
||||
tooth_mesh = trimesh.creation.extrude_polygon(polygon, self.width, engine='triangle')
|
||||
tooth_mesh = tm.creation.extrude_polygon(polygon, self.width, engine='triangle')
|
||||
logging.debug(f"Extruded tooth mesh with {len(tooth_mesh.vertices)} vertices")
|
||||
|
||||
all_teeth = []
|
||||
for i in range(self.teeth):
|
||||
angle = 2 * pi * i / self.teeth
|
||||
rot = trimesh.transformations.rotation_matrix(angle, [0, 0, 1])
|
||||
rot = tm.transformations.rotation_matrix(angle, [0, 0, 1])
|
||||
rotated_tooth = tooth_mesh.copy().apply_transform(rot)
|
||||
all_teeth.append(rotated_tooth)
|
||||
logging.debug(f"Added tooth {i+1}/{self.teeth}")
|
||||
|
||||
gear_body = trimesh.util.concatenate(all_teeth)
|
||||
gear_body = tm.util.concatenate(all_teeth)
|
||||
logging.debug(f"Combined {self.teeth} teeth into gear body with {len(gear_body.vertices)} vertices")
|
||||
|
||||
bore = trimesh.creation.cylinder(radius=self.bore_diameter / 2, height=self.width + 0.1)
|
||||
bore = tm.creation.cylinder(radius=self.bore_diameter / 2, height=self.width + 0.1)
|
||||
bore.apply_translation([0, 0, self.width / 2])
|
||||
try:
|
||||
gear = gear_body.difference(bore, engine='scad')
|
||||
@@ -107,7 +107,7 @@ class SpurGear:
|
||||
angle = 2 * pi * i / self.hole_count
|
||||
x = cos(angle) * self.hole_radius
|
||||
y = sin(angle) * self.hole_radius
|
||||
hole = trimesh.creation.cylinder(radius=self.hole_diameter / 2, height=self.width + 0.1)
|
||||
hole = tm.creation.cylinder(radius=self.hole_diameter / 2, height=self.width + 0.1)
|
||||
hole.apply_translation([x, y, self.width / 2])
|
||||
if not hole.is_volume:
|
||||
hole = hole.convex_hull
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
|
||||
class Sphere:
|
||||
def __init__(self, radius, subdivisions=3):
|
||||
@@ -11,7 +11,7 @@ class Sphere:
|
||||
return self
|
||||
|
||||
def mesh(self):
|
||||
sph = trimesh.creation.icosphere(subdivisions=self.subdivisions,
|
||||
radius=self.radius)
|
||||
sph = tm.creation.icosphere(subdivisions=self.subdivisions,
|
||||
radius=self.radius)
|
||||
sph.apply_translation(self._position)
|
||||
return sph
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import os
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
from parametric_cad.primitives.box import Box
|
||||
from parametric_cad.primitives.cylinder import Cylinder
|
||||
from parametric_cad.primitives.sphere import Sphere
|
||||
@@ -14,14 +14,14 @@ def test_stl_exporter(tmp_path):
|
||||
assert path == str(tmp_path / "test_box.stl")
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
contents = f.read()
|
||||
mesh = trimesh.util.concatenate([box.mesh()])
|
||||
mesh = tm.util.concatenate([box.mesh()])
|
||||
if not mesh.is_watertight or mesh.vertices.shape[0] == 0:
|
||||
repaired = mesh.fill_holes()
|
||||
if repaired is not False:
|
||||
mesh = repaired
|
||||
else:
|
||||
mesh = mesh.convex_hull
|
||||
expected = trimesh.exchange.stl.export_stl_ascii(mesh)
|
||||
expected = tm.exchange.stl.export_stl_ascii(mesh)
|
||||
assert contents == expected
|
||||
|
||||
|
||||
@@ -35,12 +35,12 @@ def test_ascii_stl_multiple_objects(tmp_path):
|
||||
assert path == str(tmp_path / "combo.stl")
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
contents = f.read()
|
||||
mesh = trimesh.util.concatenate([box.mesh(), cyl.mesh(), sph.mesh()])
|
||||
mesh = tm.util.concatenate([box.mesh(), cyl.mesh(), sph.mesh()])
|
||||
if not mesh.is_watertight or mesh.vertices.shape[0] == 0:
|
||||
repaired = mesh.fill_holes()
|
||||
if repaired is not False:
|
||||
mesh = repaired
|
||||
else:
|
||||
mesh = mesh.convex_hull
|
||||
expected = trimesh.exchange.stl.export_stl_ascii(mesh)
|
||||
expected = tm.exchange.stl.export_stl_ascii(mesh)
|
||||
assert contents == expected
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
|
||||
from parametric_cad.mechanisms.butthinge import ButtHinge
|
||||
|
||||
@@ -8,7 +8,7 @@ from parametric_cad.mechanisms.butthinge import ButtHinge
|
||||
def test_butthinge_mesh_and_translation():
|
||||
hinge = ButtHinge()
|
||||
mesh = hinge.mesh()
|
||||
assert isinstance(mesh, trimesh.Trimesh)
|
||||
assert isinstance(mesh, tm.Trimesh)
|
||||
original_centroid = mesh.centroid.copy()
|
||||
hinge.at(1.0, 2.0, 3.0)
|
||||
translated_centroid = hinge.mesh().centroid
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
import trimesh
|
||||
from parametric_cad.core import tm
|
||||
from math import cos, pi
|
||||
|
||||
from parametric_cad.primitives.box import Box
|
||||
@@ -30,7 +30,7 @@ def test_cylinder_and_sphere_meshes():
|
||||
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 isinstance(cyl_mesh, tm.Trimesh)
|
||||
assert isinstance(sph_mesh, tm.Trimesh)
|
||||
assert cyl_mesh.is_watertight
|
||||
assert sph_mesh.is_watertight
|
||||
|
||||
Reference in New Issue
Block a user