diff --git a/nhf/touhou/houjuu_nue/electronics.py b/nhf/touhou/houjuu_nue/electronics.py index b3d6724..b5cc1aa 100644 --- a/nhf/touhou/houjuu_nue/electronics.py +++ b/nhf/touhou/houjuu_nue/electronics.py @@ -129,6 +129,7 @@ class LinearActuator(Item): combine='cut', ) ) + back.faces(">X").tag("dir") back.copyWorkplane(Cq.Workplane('XZ')).tagPlane('conn') result = ( Cq.Assembly() diff --git a/nhf/touhou/houjuu_nue/wing.py b/nhf/touhou/houjuu_nue/wing.py index 81831eb..1024e1a 100644 --- a/nhf/touhou/houjuu_nue/wing.py +++ b/nhf/touhou/houjuu_nue/wing.py @@ -170,23 +170,21 @@ class WingProfile(Model): def shoulder_height(self) -> float: return self.shoulder_joint.height - def outer_profile_s0(self) -> Cq.Sketch: + def outer_profile_s0(self) -> Cq.Edge: """ The outer boundary of s0 top/bottom slots """ tip_x = self.shoulder_tip_x tip_y = self.shoulder_tip_y - return ( - Cq.Sketch() - .spline([ - (0, 0), - (-30.0, 80.0), - (tip_x, tip_y) - ]) - #.segment( - # (tip_x, tip_y), - # (tip_x - 10, tip_y), - #) + return Cq.Edge.makeSpline( + [ + Cq.Vector(*p) + for p in [ + (0, 0), + (-30.0, 80.0), + (tip_x, tip_y), + ] + ] ) def inner_profile_s0(self) -> Cq.Edge: """ @@ -199,18 +197,17 @@ class WingProfile(Model): dx1 = self.shoulder_base_bezier_x dy1 = self.shoulder_base_bezier_y sw = self.shoulder_width + points = [ + (tip_x, tip_y - sw), + (tip_x + dx2, tip_y - sw + dy2), + (-self.base_width + dx1, dy1), + (-self.base_width, 0), + ] return Cq.Edge.makeBezier( - [ - Cq.Vector(*p) - for p in [ - (tip_x, tip_y - sw), - (tip_x + dx2, tip_y - sw + dy2), - (-self.base_width + dx1, dy1), - (-self.base_width, 0), - ] - ] + [Cq.Vector(x, y) for x, y in points] ) + @property def shoulder_angle_neutral(self) -> float: """ @@ -224,24 +221,16 @@ class WingProfile(Model): def profile_s0(self, top: bool = True) -> Cq.Sketch: tip_x = self.shoulder_tip_x tip_y = self.shoulder_tip_y - dx2 = self.shoulder_tip_bezier_x - dy2 = self.shoulder_tip_bezier_y - dx1 = self.shoulder_base_bezier_x - dy1 = self.shoulder_base_bezier_y sw = self.shoulder_width sketch = ( - self.outer_profile_s0() + Cq.Sketch() + .edge(self.outer_profile_s0()) .segment((-self.base_width, 0), (0, 0)) .segment( (tip_x, tip_y), (tip_x, tip_y - sw), ) - .bezier([ - (tip_x, tip_y - sw), - (tip_x + dx2, tip_y - sw + dy2), - (-self.base_width + dx1, dy1), - (-self.base_width, 0), - ]) + .edge(self.inner_profile_s0()) .assemble() .push([self.shoulder_axle_loc.to2d_pos()]) .circle(self.shoulder_joint.radius, mode='a') @@ -265,7 +254,7 @@ class WingProfile(Model): def outer_shell_s0(self) -> Cq.Workplane: t = self.panel_thickness_s0 - profile = Cq.Wire.assembleEdges(self.outer_profile_s0().edges().vals()) + profile = self.outer_profile_s0() result = ( Cq.Workplane('XZ') .rect(t, self.root_height + t*2, centered=(False, False)) @@ -277,17 +266,35 @@ class WingProfile(Model): return result def inner_shell_s0(self) -> Cq.Workplane: t = self.panel_thickness_s0 - #profile = Cq.Wire.assembleEdges(self.inner_profile_s0()) + profile = self.inner_profile_s0() result = ( Cq.Workplane('XZ') + .moveTo(-t, 0) .rect(t, self.root_height + t*2, centered=(False, False)) - .sweep(self.inner_profile_s0()) + .sweep(profile, normal=(0,-1,0)) ) plane = result.copyWorkplane(Cq.Workplane('XZ')) - plane.moveTo(t, 0).tagPlane("bot") - plane.moveTo(t, self.root_height + t*2).tagPlane("top") + plane.moveTo(0, 0).tagPlane("bot") + plane.moveTo(0, self.root_height + t*2).tagPlane("top") return result + @target(name="profile-s0-outer-shell", kind=TargetKind.DXF) + def outer_shell_s0_profile(self) -> Cq.Sketch: + """ + This part should be laser cut and then bent on a falsework to create the required shape. + """ + length = self.outer_profile_s0().Length() + height = self.root_height + self.panel_thickness_s0 * 2 + return Cq.Sketch().rect(length, height) + @target(name="profile-s0-inner-shell", kind=TargetKind.DXF) + def inner_shell_s0_profile(self) -> Cq.Sketch: + """ + This part should be laser cut and then bent on a falsework to create the required shape. + """ + length = self.inner_profile_s0().Length() + height = self.root_height + self.panel_thickness_s0 * 2 + return Cq.Sketch().rect(length, height) + @submodel(name="spacer-s0-shoulder-inner") def spacer_s0_shoulder(self, left: bool=True) -> MountingBox: """ @@ -331,7 +338,7 @@ class WingProfile(Model): ) @submodel(name="spacer-s0-shoulder-outer") def spacer_s0_shoulder_outer(self) -> MountingBox: - return self.spacer_s0_shoulder_inner(left=False) + return self.spacer_s0_shoulder(left=False) @submodel(name="spacer-s0-base") def spacer_s0_base(self) -> MountingBox: @@ -428,7 +435,7 @@ class WingProfile(Model): ("shoulder_right", self.shoulder_axle_loc * axle_rotate * self.shoulder_joint.parent_lip_loc(left=False)), ("shoulder_act", - self.shoulder_axle_loc * axle_rotate * Cq.Location.from2d(120, -40)), + self.shoulder_axle_loc * axle_rotate * Cq.Location.from2d(120, -40, -30)), ("base", Cq.Location.from2d(base_dx, base_dy, 90)), ("electronic_mount", Cq.Location.from2d(-35, 65, 60)), ] @@ -436,11 +443,11 @@ class WingProfile(Model): self.profile_s0(top=top), self.panel_thickness_s0, tags, - reverse=not top, + reverse=top, ) - h = self.panel_thickness if top else 0 + h = 0 if top else self.panel_thickness_s0 result.copyWorkplane(Cq.Workplane('XZ')).moveTo(0, h).tagPlane("corner") - result.copyWorkplane(Cq.Workplane('XZ')).moveTo(-self.base_width, h).tagPlane("corner_left") + result.copyWorkplane(Cq.Workplane('XZ')).moveTo(-self.base_width, self.panel_thickness_s0 - h).tagPlane("corner_left") return result @assembly() @@ -449,9 +456,9 @@ class WingProfile(Model): ignore_electronics: bool=False) -> Cq.Assembly: result = ( Cq.Assembly() - .addS(self.surface_s0(top=True), name="bot", + .addS(self.surface_s0(top=False), name="bot", material=self.mat_panel, role=self.role_panel) - .addS(self.surface_s0(top=False), name="top", + .addS(self.surface_s0(top=True), name="top", material=self.mat_panel, role=self.role_panel, loc=Cq.Location((0, 0, self.root_height + self.panel_thickness))) .constrain("bot", "Fixed") @@ -462,10 +469,10 @@ class WingProfile(Model): material=self.mat_panel, role=self.role_panel) .constrain("bot?corner", "outer_shell?bot", "Plane", param=0) .constrain("top?corner", "outer_shell?top", "Plane", param=0) - #.addS(self.inner_shell_s0(), name="inner_shell", - # material=self.mat_panel, role=self.role_panel) - #.constrain("bot?corner_left", "inner_shell?bot", "Point") - #.constrain("top?corner_left", "inner_shell?top", "Point") + .addS(self.inner_shell_s0(), name="inner_shell", + material=self.mat_panel, role=self.role_panel) + .constrain("bot?corner_left", "inner_shell?bot", "Plane", param=0) + .constrain("top?corner_left", "inner_shell?top", "Plane", param=0) ) for o, tag in [ (self.spacer_s0_shoulder(left=True).generate(), "shoulder_left"), @@ -1122,6 +1129,9 @@ class WingProfile(Model): "Plane", param=0) .constrain("s0/shoulder_act?conn0", f"{tag_bracket}?conn_side", "Plane") + # Directional constraints should be provided by the line + .constrain(f"{tag_bracket}?conn_mid", "s0/shoulder_act?top", "Axis", param=0) + .constrain(f"{tag_act}/back?dir", "s0/shoulder_act?conn0", "Axis", param=0) ) if "root" in parts: result.addS(self.root_joint.assembly(