from dataclasses import dataclass, field import cadquery as Cq from nhf.parts.joints import HirthJoint from nhf import Material, Role from nhf.build import Model, TargetKind, target, assembly import nhf.utils @dataclass class Harness(Model): thickness: float = 25.4 / 8 width: float = 300.0 height: float = 400.0 fillet: float = 10.0 wing_base_pos: list[tuple[str, float, float]] = field(default_factory=lambda: [ ("r1", 70, 150), ("l1", -70, 150), ("r2", 100, 0), ("l2", -100, 0), ("r3", 70, -150), ("l3", -70, -150), ]) # Holes drilled onto harness for attachment with HS joint harness_to_root_conn_diam: float = 6 hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint( radius=30, radius_inner=20, tooth_height=10, base_height=5 )) hs_joint_base_width: float = 85 hs_joint_base_thickness: float = 10 hs_joint_corner_fillet: float = 5 hs_joint_corner_cbore_diam: float = 12 hs_joint_corner_cbore_depth: float = 2 hs_joint_corner_inset: float = 12 hs_joint_axis_diam: float = 12 hs_joint_axis_cbore_diam: float = 20 hs_joint_axis_cbore_depth: float = 3 def __post_init__(self): super().__init__(name="harness") @target(name="profile", kind=TargetKind.DXF) def profile(self) -> Cq.Sketch: """ Creates the harness shape """ w, h = self.width / 2, self.height / 2 sketch = ( Cq.Sketch() .polygon([ (0.7 * w, h), (w, 0), (0.7 * w, -h), (0.7 * -w, -h), (-w, 0), (0.7 * -w, h), ]) #.rect(self.harness_width, self.harness_height) .vertices() .fillet(self.fillet) ) for tag, x, y in self.wing_base_pos: conn = [(px + x, py + y) for px, py in self.hs_joint_harness_conn()] sketch = ( sketch .push(conn) .tag(tag) .circle(self.harness_to_root_conn_diam / 2, mode='s') .reset() ) return sketch def surface(self) -> Cq.Workplane: """ Creates the harness shape """ result = ( Cq.Workplane('XZ') .placeSketch(self.profile()) .extrude(self.thickness) ) result.faces(">Y").tag("mount") plane = result.faces(">Y").workplane() for tag, x, y in self.wing_base_pos: conn = [(px + x, py + y) for px, py in self.hs_joint_harness_conn()] for i, (px, py) in enumerate(conn): plane.moveTo(px, py).tagPoint(f"{tag}_{i}") return result def hs_joint_harness_conn(self) -> list[tuple[int, int]]: """ Generates a set of points corresponding to the connectorss """ dx = self.hs_joint_base_width / 2 - self.hs_joint_corner_inset return [ (dx, dx), (dx, -dx), (-dx, -dx), (-dx, dx), ] @target(name="hs-joint-parent") def hs_joint_parent(self): """ Parent part of the Houjuu-Scarlett joint, which is composed of a Hirth coupling, a cylindrical base, and a mounting base. """ hirth = self.hs_hirth_joint.generate() conn = self.hs_joint_harness_conn() result = ( Cq.Workplane('XY') .box( self.hs_joint_base_width, self.hs_joint_base_width, self.hs_joint_base_thickness, centered=(True, True, False)) .translate((0, 0, -self.hs_joint_base_thickness)) .edges("|Z") .fillet(self.hs_joint_corner_fillet) .faces(">Z") .workplane() .pushPoints(conn) .cboreHole( diameter=self.harness_to_root_conn_diam, cboreDiameter=self.hs_joint_corner_cbore_diam, cboreDepth=self.hs_joint_corner_cbore_depth) ) # Creates a plane parallel to the holes but shifted to the base plane = result.faces(">Z").workplane(offset=-self.hs_joint_base_thickness) for i, (px, py) in enumerate(conn): plane.moveTo(px, py).tagPoint(f"h{i}") result = ( result .faces(">Z") .workplane() .union(hirth, tol=0.1) .clean() ) result = ( result.faces(" Cq.Assembly: harness = self.surface() result = ( Cq.Assembly() .addS(harness, name="base", material=Material.WOOD_BIRCH, role=Role.STRUCTURE) .constrain("base", "Fixed") ) for name in ["l1", "l2", "l3", "r1", "r2", "r3"]: j = self.hs_joint_parent() ( result .addS(j, name=name, role=Role.PARENT, material=Material.PLASTIC_PLA) #.constrain("base?mount", f"{name}?base", "Axis") ) for i in range(4): result.constrain(f"base?{name}_{i}", f"{name}?h{i}", "Point") result.solve() return result