diff --git a/nhf/materials.py b/nhf/materials.py index 0cb266d..863ee3e 100644 --- a/nhf/materials.py +++ b/nhf/materials.py @@ -33,6 +33,7 @@ class Material(Enum): PLASTIC_PLA = 0.5, _color('azure3', 0.6) RESIN_TRANSPARENT = 1.1, _color('cadetblue2', 0.6) ACRYLIC_BLACK = 0.5, _color('gray50', 0.6) + ACRYLIC_TRANSPARENT = 0.5, _color('ghostwhite', 0.5) def __init__(self, density: float, color: Cq.Color): self.density = density diff --git a/nhf/parts/joints.py b/nhf/parts/joints.py index a74b7fe..5990e8a 100644 --- a/nhf/parts/joints.py +++ b/nhf/parts/joints.py @@ -332,6 +332,7 @@ class TorsionJoint: ) def track(self): + # TODO: Cover outer part of track only. Can we do this? groove_profile = ( Cq.Sketch() .circle(self.radius) diff --git a/nhf/touhou/houjuu_nue/__init__.py b/nhf/touhou/houjuu_nue/__init__.py index 32954d1..db534ec 100644 --- a/nhf/touhou/houjuu_nue/__init__.py +++ b/nhf/touhou/houjuu_nue/__init__.py @@ -104,12 +104,16 @@ class Parameters(Model): shoulder_attach_diam: float = 8 """ - Heights for various wing joints, where the numbers start from the first joint. + Heights for various wing joints, where the numbers start from the first + joint. """ wing_s0_thickness: float = 40 wing_s0_height: float = 100 - wing_r1_height: float = 100 - wing_r1_width: float = 400 + + # Length of the spacer + wing_s1_thickness: float = 20 + wing_s1_spacer_thickness: float = 25.4 / 8 + wing_s1_spacer_width: float = 20 trident_handle: Handle = field(default_factory=lambda: Handle( diam=38, @@ -121,6 +125,9 @@ class Parameters(Model): simplify_geometry=False, )) + material_panel: Material = Material.ACRYLIC_TRANSPARENT + material_bracket: Material = Material.ACRYLIC_TRANSPARENT + def __post_init__(self): super().__init__(name="houjuu-nue") assert self.wing_root_radius > self.hs_hirth_joint.radius,\ @@ -258,6 +265,10 @@ class Parameters(Model): result.faces(" Cq.Workplane: + # return self.wing_joining_plate.plate() + @target(name="wing_root") def wing_root(self) -> Cq.Assembly: """ @@ -312,43 +323,42 @@ class Parameters(Model): ) return result - def wing_r1_profile(self) -> Cq.Sketch: - """ - Generates the first wing segment profile, with the wing root pointing in - the positive x axis. - """ - # Depression of the wing middle - bend = 200 - factor = 0.7 + @target(name="wing/s1-spacer", kind=TargetKind.DXF) + def wing_s1_spacer(self) -> Cq.Workplane: result = ( - Cq.Sketch() - .segment((0, 0), (0, self.wing_r1_height)) - .spline([ - (0, self.wing_r1_height), - (0.5 * self.wing_r1_width, self.wing_r1_height - factor * bend), - (self.wing_r1_width, self.wing_r1_height - bend), - ]) - .segment( - (self.wing_r1_width, self.wing_r1_height - bend), - (self.wing_r1_width, -bend), - ) - .spline([ - (self.wing_r1_width, - bend), - (0.5 * self.wing_r1_width, - factor * bend), - (0, 0), - ]) - .assemble() + Cq.Workplane('XZ') + .sketch() + .rect(self.wing_s1_spacer_width, self.wing_s1_thickness) + .finalize() + .extrude(self.wing_s1_spacer_thickness) ) + result.faces("Z").tag("mate2") + result.faces(">Y").tag("dir") return result - def wing_r1(self) -> Cq.Solid: - profile = self.wing_r1_profile() + @target(name="wing/r1s1", kind=TargetKind.DXF) + def wing_r1s1_profile(self) -> Cq.Sketch: + return MW.wing_r1s1_profile() + + def wing_r1s1_panel(self, front=True) -> Cq.Workplane: + profile = self.wing_r1s1_profile() + anchors = [ + ("shoulder_bot", 10, 10), + ("middle", 50, -20), + ("tip", 390, -150), + ] result = ( Cq.Workplane("XY") .placeSketch(profile) .extrude(self.panel_thickness) - .val() ) + plane = result.faces(">Z" if front else " Cq.Assembly: + result = ( + Cq.Assembly() + .add(self.wing_r1s1_panel(front=True), name="panel_front", + color=self.material_panel.color) + .add(self.wing_r1s1_panel(front=False), name="panel_back", + color=self.material_panel.color) + .constrain("panel_front@faces@>Z", "panel_back@faces@ Cq.Assembly: result = ( Cq.Assembly() diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index fe9c9e4..61a535f 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -226,3 +226,34 @@ def wing_root(joint: HirthJoint, loc=Cq.Location((0, 0, -joint.total_height))) ) return result + +def wing_r1s1_profile() -> Cq.Sketch: + """ + Generates the first wing segment profile, with the wing root pointing in + the positive x axis. + """ + # Depression of the wing middle + h = 100 + w = 400 + bend = 200 + factor = 0.7 + result = ( + Cq.Sketch() + .segment((0, 0), (0, h)) + .spline([ + (0, h), + (0.5 * w, h - factor * bend), + (w, h - bend), + ]) + .segment( + (w, h - bend), + (w, -bend), + ) + .spline([ + (w, - bend), + (0.5 * w, - factor * bend), + (0, 0), + ]) + .assemble() + ) + return result diff --git a/nhf/utils.py b/nhf/utils.py index 7cc020d..f375c63 100644 --- a/nhf/utils.py +++ b/nhf/utils.py @@ -6,6 +6,7 @@ Adds the functions to `Cq.Workplane`: 2. `tagPlane` """ import cadquery as Cq +from typing import Union, Tuple def tagPoint(self, tag: str): @@ -18,21 +19,34 @@ def tagPoint(self, tag: str): Cq.Workplane.tagPoint = tagPoint -def tagPlane(self, tag: str, axis='Z'): +def tagPlane(self, tag: str, + direction: Union[str, Cq.Vector, Tuple[float, float, float]] = '+Z'): """ Adds a phantom `Cq.Edge` in the given location which can be referenced in a `Axis`, `Point`, or `Plane` constraint. """ - x, y, z = 0, 0, 0 - if axis in ('z', 'Z'): - z = 1 - elif axis in ('y', 'Y'): - y = 1 - elif axis in ('x', 'X'): - x = 1 + if isinstance(direction, str): + x, y, z = 0, 0, 0 + assert len(direction) == 2 + sign, axis = direction + if axis in ('z', 'Z'): + z = 1 + elif axis in ('y', 'Y'): + y = 1 + elif axis in ('x', 'X'): + x = 1 + else: + assert False, "Axis must be one of x,y,z" + if sign == '+': + sign = 1 + elif sign == '-': + sign = -1 + else: + assert False, "Sign must be one of +/-" + v = Cq.Vector(x, y, z) * sign else: - assert False, "Axis must be one of x,y,z" - edge = Cq.Edge.makeLine((-x, -y, -z), (x, y, z)) + v = Cq.Vector(direction) + edge = Cq.Edge.makeLine(v * (-1), v) self.eachpoint(edge.moved, useLocalCoordinates=True).tag(tag) Cq.Workplane.tagPlane = tagPlane