Created
October 17, 2022 19:13
-
-
Save bernhard-42/c9e5349a68970e9bc79612abadcbb097 to your computer and use it in GitHub Desktop.
build123d separation of concerns
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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