diff --git a/nhf/touhou/houjuu_nue/__init__.py b/nhf/touhou/houjuu_nue/__init__.py index a514bd8..844ce48 100644 --- a/nhf/touhou/houjuu_nue/__init__.py +++ b/nhf/touhou/houjuu_nue/__init__.py @@ -38,6 +38,7 @@ from nhf.parts.handle import Handle, BayonetMount import nhf.touhou.houjuu_nue.wing as MW import nhf.touhou.houjuu_nue.trident as MT import nhf.touhou.houjuu_nue.joints as MJ +import nhf.touhou.houjuu_nue.harness as MH import nhf.utils @dataclass @@ -50,44 +51,16 @@ class Parameters(Model): panel_thickness: float = 25.4 / 16 # Harness - harness_thickness: float = 25.4 / 8 - harness_width: float = 300 - harness_height: float = 400 - harness_fillet: float = 10 - - harness_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 + harness: MH.Harness = field(default_factory=lambda: MH.Harness()) hs_hirth_joint: HirthJoint = field(default_factory=lambda: HirthJoint( radius=30, radius_inner=20, tooth_height=10, - base_height=5 + base_height=5, + n_tooth=24 )) - # Wing root properties - # - # The Houjuu-Scarlett joint mechanism at the base of the wing - 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 - wing_profile: MW.WingProfile = field(default_factory=lambda: MW.WingProfile( shoulder_height=100.0, elbow_height=110.0, @@ -135,7 +108,8 @@ class Parameters(Model): def __post_init__(self): super().__init__(name="houjuu-nue") - assert self.wing_root_radius > self.hs_hirth_joint.radius,\ + self.harness.hs_hirth_joint = self.hs_hirth_joint + assert self.wing_root_radius > self.hs_hirth_joint.radius, \ "Wing root must be large enough to accomodate joint" assert self.wing_s1_shoulder_spacer_hole_dist > self.wing_s1_spacer_hole_diam, \ "Spacer holes are too close to each other" @@ -157,150 +131,19 @@ class Parameters(Model): result = result.faces(">Z").hole(self.trident_terminal_hole_diam, depth=h) return result - - def harness_profile(self) -> Cq.Sketch: - """ - Creates the harness shape - """ - w, h = self.harness_width / 2, self.harness_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.harness_fillet) - ) - for tag, x, y in self.harness_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 - @target(name="harness", kind=TargetKind.DXF) - def harness(self) -> Cq.Shape: - """ - Creates the harness shape - """ - result = ( - Cq.Workplane('XZ') - .placeSketch(self.harness_profile()) - .extrude(self.harness_thickness) - ) - result.faces(">Y").tag("mount") - plane = result.faces(">Y").workplane() - for tag, x, y in self.harness_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) - .circle(1, forConstruction='True') - .edges() - .tag(f"{tag}_{i}") - ) - return result + def harness_profile(self) -> Cq.Sketch: + return self.harness.profile() - 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), - ] + def harness_surface(self) -> Cq.Workplane: + return self.harness.surface() - @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 - .pushPoints([(px, py)]) - .circle(1, forConstruction='True') - .edges() - .tag(f"h{i}") - ) - result = ( - result - .faces(">Z") - .workplane() - .union(hirth, tol=0.1) - .clean() - ) - result = ( - result.faces(" Cq.Workplane: + return self.harness.hs_joint_parent() @assembly() def harness_assembly(self) -> Cq.Assembly: - harness = self.harness() - result = ( - Cq.Assembly() - .add(harness, name="base", color=Material.WOOD_BIRCH.color) - .constrain("base", "Fixed") - ) - for name in ["l1", "l2", "l3", "r1", "r2", "r3"]: - j = self.hs_joint_parent() - ( - result - .add(j, name=name, color=Role.PARENT.color) - .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 + return self.harness.assembly() #@target(name="wing/joining-plate", kind=TargetKind.DXF) #def joining_plate(self) -> Cq.Workplane: diff --git a/nhf/touhou/houjuu_nue/harness.py b/nhf/touhou/houjuu_nue/harness.py new file mode 100644 index 0000000..3df4cf8 --- /dev/null +++ b/nhf/touhou/houjuu_nue/harness.py @@ -0,0 +1,170 @@ +from dataclasses import dataclass, field +import cadquery as Cq +from nhf.parts.joints import HirthJoint +from nhf import Material, Role +import nhf.utils + +@dataclass +class Harness: + 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 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), + ] + + 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() + .add(harness, name="base", color=Material.WOOD_BIRCH.color) + .constrain("base", "Fixed") + ) + for name in ["l1", "l2", "l3", "r1", "r2", "r3"]: + j = self.hs_joint_parent() + ( + result + .add(j, name=name, color=Role.PARENT.color) + #.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