From 2ccb4160dbadef97ec0cc5fe9d151fc3bc7ddc5c Mon Sep 17 00:00:00 2001 From: Leni Aniva Date: Thu, 1 Aug 2024 22:38:45 -0700 Subject: [PATCH] feat: Use double layer for electronic mount --- nhf/parts/box.py | 12 ++++++++-- nhf/parts/fasteners.py | 14 +++++++++-- nhf/touhou/houjuu_nue/electronics.py | 9 +++++++ nhf/touhou/houjuu_nue/wing.py | 36 +++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/nhf/parts/box.py b/nhf/parts/box.py index 69a40f8..5fbf21e 100644 --- a/nhf/parts/box.py +++ b/nhf/parts/box.py @@ -33,12 +33,20 @@ class Hole: y: float = 0.0 diam: Optional[float] = None tag: Optional[str] = None + face: Optional[Cq.Face] = None @property def rev_tag(self) -> str: assert self.tag is not None return self.tag + "_rev" + def cutting_geometry(self, default_diam: Optional[float]=None) -> Cq.Face: + if self.face is not None: + return self.face + diam = self.diam if self.diam is not None else default_diam + assert diam is not None + return Cq.Face.makeFromWires(Cq.Wire.makeCircle(diam/2, Cq.Vector(), Cq.Vector(0,0,1))) + @dataclass class MountingBox(Model): """ @@ -84,8 +92,8 @@ class MountingBox(Model): .rect(self.length, self.width) ) for hole in self.holes: - diam = hole.diam if hole.diam else self.hole_diam - result.push([(hole.x, hole.y)]).circle(diam / 2, mode='s') + face = hole.cutting_geometry(default_diam=self.hole_diam) + result.push([(hole.x, hole.y)]).each(lambda l:face.moved(l), mode='s') if self.profile_callback: result = self.profile_callback(result) return result diff --git a/nhf/parts/fasteners.py b/nhf/parts/fasteners.py index 9dfd20f..02bd598 100644 --- a/nhf/parts/fasteners.py +++ b/nhf/parts/fasteners.py @@ -103,12 +103,15 @@ class HexNut(Item): def role(self) -> Role: return Role.CONNECTION + @property + def radius(self) -> float: + return self.width / math.sqrt(3) + def generate(self) -> Cq.Workplane: - r = self.width / math.sqrt(3) result = ( Cq.Workplane("XY") .sketch() - .regularPolygon(r=r, n=6) + .regularPolygon(r=self.radius, n=6) .circle(r=self.diam_thread/2, mode='s') .finalize() .extrude(self.thickness) @@ -117,3 +120,10 @@ class HexNut(Item): result.faces(">Z").tag("top") result.copyWorkplane(Cq.Workplane('XY')).tagPlane("dirX", direction="+X") return result + + def cutting_face(self) -> Cq.Face: + return ( + Cq.Sketch() + .regularPolygon(r=self.radius, n=6) + ._faces + ) diff --git a/nhf/touhou/houjuu_nue/electronics.py b/nhf/touhou/houjuu_nue/electronics.py index f8ea616..d29fd17 100644 --- a/nhf/touhou/houjuu_nue/electronics.py +++ b/nhf/touhou/houjuu_nue/electronics.py @@ -335,6 +335,15 @@ LINEAR_ACTUATOR_BRACKET = MountingBracket() BATTERY_BOX = BatteryBox18650() +# Acrylic hex nut +ELECTRONIC_MOUNT_HEXNUT = HexNut( + mass=0.8, + diam_thread=4, + pitch=0.7, + thickness=3.57, + width=6.81, +) + @dataclass(kw_only=True) class Flexor: """ diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index f8cfa9d..03867f4 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -18,7 +18,8 @@ from nhf.touhou.houjuu_nue.electronics import ( LINEAR_ACTUATOR_10, LINEAR_ACTUATOR_21, LINEAR_ACTUATOR_50, - ElectronicBoard + ElectronicBoard, + ELECTRONIC_MOUNT_HEXNUT, ) import nhf.utils @@ -327,6 +328,9 @@ class WingProfile(Model): ) @submodel(name="spacer-s0-electronic") def spacer_s0_electronic_mount(self) -> MountingBox: + """ + This one has circular holes for the screws + """ return MountingBox( holes=self.electronic_board.mount_holes, hole_diam=self.electronic_board.mount_hole_diam, @@ -337,6 +341,26 @@ class WingProfile(Model): flip_y=False,#self.flip, generate_reverse_tags=True, ) + @submodel(name="spacer-s0-electronic2") + def spacer_s0_electronic_mount2(self) -> MountingBox: + """ + This one has hexagonal holes + """ + face = ELECTRONIC_MOUNT_HEXNUT.cutting_face() + holes = [ + Hole(x=h.x, y=h.y, face=face, tag=h.tag) + for h in self.electronic_board.mount_holes + ] + return MountingBox( + holes=holes, + hole_diam=self.electronic_board.mount_hole_diam, + length=self.root_height, + width=self.electronic_board.width, + centred=(True, True), + thickness=self.spacer_thickness, + flip_y=False,#self.flip, + generate_reverse_tags=True, + ) @submodel(name="spacer-s0-shoulder-act") def spacer_s0_shoulder_act(self) -> MountingBox: dx = self.shoulder_joint.draft_height @@ -424,6 +448,16 @@ class WingProfile(Model): .constrain(f"{tag}?{top_tag}", f"top?{tag}", "Plane") .constrain(f"{tag}?dir", f"top?{tag}_dir", "Axis") ) + result.addS( + self.spacer_s0_electronic_mount2().generate(), + name="electronic_mount2", + role=Role.STRUCTURE | Role.CONNECTION, + material=self.mat_bracket) + for hole in self.electronic_board.mount_holes: + result.constrain( + f"electronic_mount?{hole.tag}", + f"electronic_mount2?{hole.tag}_rev", + "Plane") if not ignore_electronics: result.add(self.electronic_board.assembly(), name="electronic_board") for hole in self.electronic_board.mount_holes: