diff --git a/nhf/touhou/shiki_eiki/crown.py b/nhf/touhou/shiki_eiki/crown.py index 7e8f7ad..1896adf 100644 --- a/nhf/touhou/shiki_eiki/crown.py +++ b/nhf/touhou/shiki_eiki/crown.py @@ -1,10 +1,17 @@ -import math -from dataclasses import dataclass, field -import cadquery as Cq from nhf import Material, Role from nhf.build import Model, target, assembly, TargetKind import nhf.utils +import math +from dataclasses import dataclass, field +from enum import Enum +import cadquery as Cq + +class AttachPoint(Enum): + DOVETAIL_IN = 1 + DOVETAIL_OUT = 2 + NONE = 3 + @dataclass class Crown(Model): @@ -24,6 +31,7 @@ class Crown(Model): side_guard_channel_height: float = 10 side_guard_hole_height: float = 15.0 side_guard_hole_diam: float = 1.5 + side_guard_dovetail_height: float = 30.0 material: Material = Material.METAL_BRASS material_side: Material = Material.PLASTIC_PLA @@ -365,36 +373,36 @@ class Crown(Model): dx = self.side_guard_thickness / 2 wire = Cq.Wire.makePolygon([ (dx * 0.5, 0), - (dx * 0.8, dx), - (-dx * 0.8, dx), + (dx * 0.7, dx), + (-dx * 0.7, dx), (-dx * 0.5, 0), ], close=True) return Cq.Solid.extrudeLinear( wire, [], - (0,0,self.height/3), - ) + (0,0,dx + self.side_guard_dovetail_height), + ).moved((0, 0, -dx)) - def side_guard(self, attach_left: bool = True, attach_right: bool = False) -> Cq.Workplane: + def side_guard(self, attach_left: AttachPoint, attach_right: AttachPoint) -> Cq.Workplane: """ Constructs the side guard using a cone. Via Gauss's Theorema Egregium, the surface of the cone can be deformed into a plane. """ - angle = 360 / 5 + angle_span = 360 / 5 outer = Cq.Solid.makeCone( radius1=self.radius_lower + self.side_guard_thickness, radius2=self.radius_upper + self.side_guard_thickness, height=self.height, - angleDegrees=angle, + angleDegrees=angle_span, ) inner = Cq.Solid.makeCone( radius1=self.radius_lower, radius2=self.radius_upper, height=self.height, - angleDegrees=angle, + angleDegrees=angle_span, ) - shell = (outer - inner).rotate((0,0,0), (0,0,1), -angle/2) - dx = math.sin(math.radians(angle / 2)) * (self.radius_middle + self.side_guard_thickness) + shell = (outer - inner).rotate((0,0,0), (0,0,1), -angle_span/2) + dx = math.sin(math.radians(angle_span / 2)) * (self.radius_middle + self.side_guard_thickness) profile = ( Cq.Workplane('YZ') .polyline([ @@ -408,16 +416,16 @@ class Crown(Model): .extrude(self.radius_upper + self.side_guard_thickness) .val() ) - channel = ( - Cq.Solid.makeCylinder( - radius=self.side_guard_channel_radius + 1.0, - height=self.side_guard_channel_height, - ) - Cq.Solid.makeCylinder( - radius=self.side_guard_channel_radius, - height=self.side_guard_channel_height, - ) - ) - result = shell * profile - channel + #channel = ( + # Cq.Solid.makeCylinder( + # radius=self.side_guard_channel_radius + 1.0, + # height=self.side_guard_channel_height, + # ) - Cq.Solid.makeCylinder( + # radius=self.side_guard_channel_radius, + # height=self.side_guard_channel_height, + # ) + #) + result = shell * profile# - channel for i in [-2, -1, 0, 1, 2]: phi = i * (math.pi / 14) hole = Cq.Solid.makeCylinder( @@ -431,10 +439,45 @@ class Crown(Model): radius_attach = self.radius_lower + self.side_guard_thickness / 2 # tilt the dovetail by radius differential angle_tilt = math.degrees(math.atan2(self.radius_middle - self.radius_lower, self.height / 2)) - print(angle_tilt) dovetail = self.side_guard_dovetail() - loc_dovetail = Cq.Location.rot2d(angle / 2) * Cq.Location(radius_attach, 0, 0, 0, angle_tilt, 0) * Cq.Location.rot2d(180) - return result - dovetail.moved(loc_dovetail) + loc_dovetail_left = Cq.Location.rot2d(angle_span / 2) * Cq.Location(radius_attach, 0, 0, 0, angle_tilt, 0) + loc_dovetail_right = Cq.Location.rot2d(-angle_span / 2) * Cq.Location(radius_attach, 0, 0, 0, angle_tilt, 0) + + match attach_left: + case AttachPoint.DOVETAIL_IN: + loc_dovetail_left *= Cq.Location.rot2d(180) + result = result - dovetail.moved(loc_dovetail_left) + case AttachPoint.DOVETAIL_OUT: + result = result + dovetail.moved(loc_dovetail_left) + case AttachPoint.NONE: + pass + match attach_right: + case AttachPoint.DOVETAIL_IN: + result = result - dovetail.moved(loc_dovetail_right) + case AttachPoint.DOVETAIL_OUT: + loc_dovetail_right *= Cq.Location.rot2d(180) + result = result + dovetail.moved(loc_dovetail_right) + case AttachPoint.NONE: + pass + # Remove parts below the horizontal + cut_h = self.radius_lower + result -= Cq.Solid.makeCylinder( + radius=self.radius_lower + self.side_guard_thickness, + height=cut_h).moved((0,0,-cut_h)) + return result + + @target(name="side_guard_2") + def side_guard_2(self) -> Cq.Workplane: + return self.side_guard( + attach_left=AttachPoint.DOVETAIL_OUT, + attach_right=AttachPoint.DOVETAIL_IN, + ) + @target(name="side_guard_3") + def side_guard_3(self) -> Cq.Workplane: + return self.side_guard( + attach_left=AttachPoint.DOVETAIL_IN, + attach_right=AttachPoint.DOVETAIL_IN, + ) def front_surrogate(self) -> Cq.Workplane: """ @@ -478,7 +521,7 @@ class Crown(Model): """ New assembly using conformal mapping on the cone. """ - side_guard = self.side_guard() + side_guard = self.side_guard_2() a = Cq.Assembly() for i in range(1,5): a = a.addS(