Skip to content

Instantly share code, notes, and snippets.

@bernhard-42
Created October 17, 2022 19:13
Show Gist options
  • Save bernhard-42/c9e5349a68970e9bc79612abadcbb097 to your computer and use it in GitHub Desktop.
Save bernhard-42/c9e5349a68970e9bc79612abadcbb097 to your computer and use it in GitHub Desktop.
build123d separation of concerns
import cadquery as cq
from build123d import *
from typing import Union
from build123d.build_common import LocationList
VectorLike = Union[Vector, tuple[float, float], tuple[float, float, float]]
RotationLike = Union[tuple[float, float, float], Rotation]
class SolidType:
...
class SketchType:
...
class OperationsType:
def get_pending(self):
context = BuildPart._get_context()
faces = context.pending_faces
face_planes = context.pending_face_planes
context.pending_faces = []
context.pending_face_planes = []
return zip(faces, face_planes)
# Modify BuildPart
def _place_part(self, obj, rotation, mode):
if isinstance(obj, SolidType):
rotate = Rotation(*rotation) if isinstance(rotation, tuple) else rotation
new_solids = [
obj.shape.moved(location * rotate)
for location in LocationList._get_context().locations
]
elif isinstance(obj, OperationsType):
new_solids = obj.solids
BuildPart._get_context()._add_to_context(*new_solids, mode=mode)
return Compound.make_compound(new_solids)
def _add_part(self, obj, rotation:RotationLike = (0, 0, 0)):
return self._place(obj, rotation, Mode.ADD)
def _subtract_part(self, obj, rotation:RotationLike = (0, 0, 0)):
return self._place(obj, rotation, Mode.SUBTRACT)
def _intersect_part(self, obj, rotation:RotationLike = (0, 0, 0)):
return self._place(obj, rotation, Mode.INTERSECT)
# Modify BuildSketch
def _place_sketch(self, face, rotation, mode):
face = face.face.locate(
Location((0, 0, 0), (0, 0, 1), rotation) * Location(face.origin)
)
new_faces = [
face.moved(location)
for location in LocationList._get_context().local_locations
]
context = BuildSketch._get_context()
for face in new_faces:
context._add_to_context(face, mode=mode)
return Compound.make_compound(new_faces)
def _add_sketch(self, obj, rotation:float=0):
return self._place(obj, rotation, Mode.ADD)
def _subtract_sketch(self, obj, rotation:float=0):
return self._place(obj, rotation, Mode.SUBTRACT)
def _intersect_sketch(self, obj, rotation:float=0):
return self._place(obj, rotation, Mode.INTERSECT)
# Monkey Patching
BuildPart._place = _place_part
BuildPart.add = _add_part
BuildPart.subtract = _subtract_part
BuildPart.intersect = _intersect_part
BuildSketch._place = _place_sketch
BuildSketch.add = _add_sketch
BuildSketch.subtract = _subtract_sketch
BuildSketch.intersect = _intersect_sketch
# Shapes
class NewBox(SolidType):
def __init__(self, length: float, width: float, height: float, centered: tuple[bool, bool, bool] = (True, True, True)):
self.length = length
self.width = width
self.height = height
self.origin = Vector(
-length / 2 if centered[0] else 0,
-width / 2 if centered[1] else 0,
-height / 2 if centered[2] else 0,
)
self.z_dir = Vector(0, 0, 1)
self.shape = Solid.make_box(length, width, height, self.origin, self.z_dir)
class NewCylinder(SolidType):
def __init__(self, radius: float, height: float, arc_size: float = 360, centered: tuple[bool, bool, bool] = (True, True, True)):
self.radius = radius
self.height = height
self.arc_size = arc_size
self.origin = Vector(
0 if centered[0] else radius,
0 if centered[1] else radius,
-height / 2 if centered[2] else 0,
)
self.z_dir = Vector(0, 0, 1)
self.shape = Solid.make_cylinder(radius, height, self.origin, self.z_dir, arc_size)
# Sketches
class NewEllipse(SketchType):
def __init__(
self,
x_radius: float,
y_radius: float,
centered: tuple[bool, bool] = (True, True),
):
self.x_radius = x_radius
self.y_radius = y_radius
self.centered = centered
self.face = Face.make_from_wires(
Wire.make_ellipse(
x_radius,
y_radius,
center=Vector(),
normal=Vector(0, 0, 1),
x_dir=Vector(1, 0, 0),
rotation_angle=0,
)
)
self.origin = Vector(
0 if centered[0] else x_radius / 2,
0 if centered[1] else y_radius / 2,
)
# Operations
class NewExtrude(OperationsType):
def __init__(
self,
amount: float = None,
both: bool = False,
taper: float = 0.0
):
self.amount = amount
self.both = both
self.taper = taper
new_solids: list[Solid] = []
pending_faces = self.get_pending()
for face, plane in pending_faces:
for direction in [1, -1] if both else [1]:
new_solids.append(
Solid.extrude_linear(
face,
plane.z_dir * amount * direction,
taper,
)
)
self.solids = new_solids
with BuildPart() as b:
p = b.add(NewBox(1,2,3))
b.subtract(NewBox(3, 0.5, 4), rotation=(5,0,0))
f = p.faces().sort_by(Axis.Z)
with Workplanes(f[0], f[-1]):
b.intersect(NewCylinder(0.8, 1))
f = b.faces().sort_by(Axis.Z)
with Workplanes(f[0], f[1], f[-2], f[-1]):
with BuildSketch() as s:
s.add(NewEllipse(0.3, 0.15))
s.subtract(NewEllipse(0.25, 0.1))
b.add(NewExtrude(amount=0.5))
b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment