mirror of
https://github.com/johndoe6345789/bamboogenerator.git
synced 2026-04-24 13:24:54 +00:00
Merge pull request #17 from Rich43/codex/refactor-right-angle-bracket-code-declaratively
Add declarative motor bracket example
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"""Consolidated parametric_cad package."""
|
||||
|
||||
from .core import tm, safe_difference
|
||||
from .core import tm, safe_difference, combine
|
||||
from .primitives.box import Box
|
||||
from .primitives.cylinder import Cylinder
|
||||
from .primitives.gear import SpurGear
|
||||
from .primitives.sprocket import ChainSprocket
|
||||
from .mechanisms.butthinge import ButtHinge
|
||||
@@ -10,7 +11,9 @@ from .export.stl import STLExporter
|
||||
__all__ = [
|
||||
"tm",
|
||||
"safe_difference",
|
||||
"combine",
|
||||
"Box",
|
||||
"Cylinder",
|
||||
"SpurGear",
|
||||
"ChainSprocket",
|
||||
"ButtHinge",
|
||||
|
||||
@@ -7,6 +7,7 @@ wrapper allows the backend to be swapped or mocked easily.
|
||||
"""
|
||||
|
||||
import trimesh as _trimesh
|
||||
from typing import Iterable, Any
|
||||
|
||||
|
||||
def safe_difference(mesh, other, *, engine="scad"):
|
||||
@@ -42,4 +43,21 @@ def safe_difference(mesh, other, *, engine="scad"):
|
||||
# importing ``trimesh`` themselves.
|
||||
tm = _trimesh
|
||||
|
||||
__all__ = ["tm", "safe_difference"]
|
||||
def combine(objects: Iterable[Any]) -> _trimesh.Trimesh:
|
||||
"""Return a union of ``objects``.
|
||||
|
||||
Each object may be a :class:`~trimesh.Trimesh` or have a ``mesh``
|
||||
method returning one.
|
||||
"""
|
||||
meshes = []
|
||||
for obj in objects:
|
||||
if isinstance(obj, _trimesh.Trimesh):
|
||||
meshes.append(obj)
|
||||
elif hasattr(obj, "mesh"):
|
||||
m = obj.mesh
|
||||
meshes.append(m() if callable(m) else m)
|
||||
else:
|
||||
raise TypeError(f"Object {obj!r} cannot be converted to a mesh")
|
||||
return _trimesh.util.concatenate(meshes)
|
||||
|
||||
__all__ = ["tm", "safe_difference", "combine"]
|
||||
|
||||
47
parametric_cad/examples/declarative_motor_bracket.py
Normal file
47
parametric_cad/examples/declarative_motor_bracket.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from math import pi
|
||||
|
||||
from parametric_cad.primitives.box import Box
|
||||
from parametric_cad.primitives.cylinder import Cylinder
|
||||
from parametric_cad.core import combine, safe_difference
|
||||
from parametric_cad.export.stl import STLExporter
|
||||
|
||||
# Basic dimensions for a 540/550 motor bracket
|
||||
BASE_LENGTH = 50.0
|
||||
BASE_WIDTH = 40.0
|
||||
PLATE_HEIGHT = 40.0
|
||||
THICKNESS = 3.0
|
||||
MOTOR_HOLE_SPACING = 25.0
|
||||
MOTOR_HOLE_DIAMETER = 3.2
|
||||
SHAFT_CLEARANCE_DIAMETER = 10.0
|
||||
MOTOR_MOUNT_HEIGHT = 20.0
|
||||
|
||||
# Create the two plates of the bracket
|
||||
base = Box(BASE_LENGTH, BASE_WIDTH, THICKNESS).at(0, 0, 0)
|
||||
plate = Box(BASE_LENGTH, THICKNESS, PLATE_HEIGHT).at(
|
||||
0, BASE_WIDTH - THICKNESS, THICKNESS
|
||||
)
|
||||
|
||||
# Union the plates into a single mesh
|
||||
bracket = combine([base, plate])
|
||||
|
||||
# Define mounting and shaft clearance holes
|
||||
hole_y = BASE_WIDTH - THICKNESS / 2
|
||||
hole_z = THICKNESS + MOTOR_MOUNT_HEIGHT
|
||||
holes = [
|
||||
Cylinder(MOTOR_HOLE_DIAMETER / 2, THICKNESS + 0.2)
|
||||
.rotate([1, 0, 0], pi / 2)
|
||||
.at(BASE_LENGTH / 2 - MOTOR_HOLE_SPACING / 2, hole_y, hole_z),
|
||||
Cylinder(MOTOR_HOLE_DIAMETER / 2, THICKNESS + 0.2)
|
||||
.rotate([1, 0, 0], pi / 2)
|
||||
.at(BASE_LENGTH / 2 + MOTOR_HOLE_SPACING / 2, hole_y, hole_z),
|
||||
Cylinder(SHAFT_CLEARANCE_DIAMETER / 2, THICKNESS + 0.2)
|
||||
.rotate([1, 0, 0], pi / 2)
|
||||
.at(BASE_LENGTH / 2, hole_y, hole_z),
|
||||
]
|
||||
|
||||
# Subtract holes from the bracket body
|
||||
bracket = safe_difference(bracket, [h.mesh() for h in holes])
|
||||
|
||||
# Export result
|
||||
exporter = STLExporter(output_dir="output/declarative_motor_bracket_output")
|
||||
exporter.export_mesh(bracket, "declarative_motor_bracket")
|
||||
@@ -1,18 +1,32 @@
|
||||
from parametric_cad.core import tm
|
||||
from typing import Sequence, Optional
|
||||
|
||||
class Cylinder:
|
||||
def __init__(self, radius, height, sections=32):
|
||||
def __init__(self, radius: float, height: float, sections: int = 32):
|
||||
self.radius = radius
|
||||
self.height = height
|
||||
self.sections = sections
|
||||
self._position = (0, 0, 0)
|
||||
self._position = (0.0, 0.0, 0.0)
|
||||
self._rotation: Optional[Sequence[float]] = None
|
||||
|
||||
def at(self, x, y, z):
|
||||
def at(self, x: float, y: float, z: float) -> "Cylinder":
|
||||
self._position = (x, y, z)
|
||||
return self
|
||||
|
||||
def mesh(self):
|
||||
cyl = tm.creation.cylinder(radius=self.radius, height=self.height,
|
||||
sections=self.sections)
|
||||
def rotate(self, axis: Sequence[float], angle: float) -> "Cylinder":
|
||||
"""Rotate the cylinder around ``axis`` by ``angle`` radians."""
|
||||
self._rotation = (axis, angle)
|
||||
return self
|
||||
|
||||
def mesh(self) -> tm.Trimesh:
|
||||
cyl = tm.creation.cylinder(
|
||||
radius=self.radius,
|
||||
height=self.height,
|
||||
sections=self.sections,
|
||||
)
|
||||
if self._rotation is not None:
|
||||
axis, angle = self._rotation
|
||||
rot = tm.transformations.rotation_matrix(angle, axis)
|
||||
cyl.apply_transform(rot)
|
||||
cyl.apply_translation(self._position)
|
||||
return cyl
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from parametric_cad.core import tm, safe_difference
|
||||
from parametric_cad.core import tm, safe_difference, combine
|
||||
from math import cos, sin, pi
|
||||
|
||||
from parametric_cad.primitives.box import Box
|
||||
@@ -44,6 +44,15 @@ def test_safe_difference_returns_mesh():
|
||||
assert isinstance(result, tm.Trimesh)
|
||||
|
||||
|
||||
def test_combine_and_rotation():
|
||||
box = Box(1.0, 1.0, 1.0)
|
||||
cyl = Cylinder(radius=0.5, height=2.0).rotate([1, 0, 0], pi / 2)
|
||||
combined = combine([box, cyl])
|
||||
assert isinstance(combined, tm.Trimesh)
|
||||
# Cylinder rotated around X should extend its height along Y axis
|
||||
assert combined.extents[1] >= 2.0
|
||||
|
||||
|
||||
def test_chain_sprocket_properties_and_mesh():
|
||||
sprocket = ChainSprocket(pitch=12.7, roller_diameter=7.75, teeth=10)
|
||||
expected_pitch_dia = 2 * sprocket.pitch / (2 * sin(pi / sprocket.teeth))
|
||||
|
||||
Reference in New Issue
Block a user