diff --git a/nhf/touhou/shiki_eiki/crown.py b/nhf/touhou/shiki_eiki/crown.py index 590dc0b..3ae1dcc 100644 --- a/nhf/touhou/shiki_eiki/crown.py +++ b/nhf/touhou/shiki_eiki/crown.py @@ -10,9 +10,9 @@ class Crown(Model): facets: int = 5 # Lower circumference - base_circ: float = 570.0 + base_circ: float = 538.0 # Upper circumference - tilt_circ: float = 670.0 + tilt_circ: float = 640.0 height: float = 120.0 margin: float = 10.0 @@ -25,6 +25,7 @@ class Crown(Model): super().__init__(name="crown") assert self.tilt_circ > self.base_circ + assert self.facet_width_upper / 2 > self.height / 2, "Top angle must be > 90 degrees" @property def facet_width_lower(self): @@ -33,8 +34,7 @@ class Crown(Model): def facet_width_upper(self): return self.tilt_circ / self.facets - @target(name="side", kind=TargetKind.DXF) - def profile_side(self) -> Cq.Sketch: + def profile_base(self) -> Cq.Sketch: # Generate the pentagonal shape dx_l = self.facet_width_lower @@ -51,6 +51,35 @@ class Crown(Model): ]) ) + @target(name="side", kind=TargetKind.DXF) + def profile_side(self) -> Cq.Sketch: + dy = self.facet_width_upper * 0.1 + x_side = self.facet_width_upper + y_tip = self.height - self.margin + + eye = ( + Cq.Sketch() + .segment( + (0, y_tip), + (dy, y_tip - dy), + ) + .segment( + (0, y_tip), + (-dy, y_tip - dy), + ) + .bezier([ + (dy, y_tip - dy), + (0, y_tip - dy/2), + (0, y_tip - dy/2), + (-dy, y_tip - dy), + ]) + .assemble() + ) + return ( + self.profile_base() + .boolean(eye, mode='s') + ) + @target(name="dot", kind=TargetKind.DXF) def profile_dot(self) -> Cq.Sketch: return ( @@ -72,7 +101,7 @@ class Crown(Model): ) window_p1 = Cq.Location.from2d( dx_u/2 - self.margin - window_length * 0.4, - dy/2 + self.margin, + dy/2 + self.margin/2, math.degrees(math.atan2(dy/2, -dx_u/2)), ) window_p2 = Cq.Location.from2d( @@ -156,20 +185,44 @@ class Crown(Model): needle_y_top = dy - self.margin needle_y_mid = dy * 0.7 needle_dx = scale_base_x * 2 + y_shoulder = needle_y_mid - z * 2 needle = ( + Cq.Sketch() + .segment( + (0, needle_y_mid), + (z, y_shoulder), + ) + .segment( + (z, y_shoulder), + (z, scale_base_y), + ) + .segment( + (z, scale_base_y), + (-z, scale_base_y), + ) + .segment( + (-z, y_shoulder), + (-z, scale_base_y), + ) + .segment( + (-z, y_shoulder), + (0, needle_y_mid), + ) + .assemble() + ) + z2 = z * 2 + y1 = needle_y_mid + z2 + needle_head = ( Cq.Sketch() .segment( (z, needle_y_mid), - (z, scale_base_y), + (z, y1), ) .segment( - (z, scale_base_y), - (-z, scale_base_y), - ) - .segment( - (-z, scale_base_y), (-z, needle_y_mid), + (-z, y1), ) + # Outer edge .bezier([ (0, needle_y_top), (0, (needle_y_top + needle_y_mid)/2), @@ -182,40 +235,24 @@ class Crown(Model): (-needle_dx, (needle_y_top + needle_y_mid)/2), (-z, needle_y_mid), ]) - .assemble() - ) - z2 = z * 2 - needle_inner = ( - Cq.Sketch() - .segment( - (z2, needle_y_mid - z2), - (-z2, needle_y_mid - z2) - ) - .segment( - (z2, needle_y_mid - z2), - (z2, needle_y_mid + z2), - ) - .segment( - (-z2, needle_y_mid - z2), - (-z2, needle_y_mid + z2), - ) + # Inner edge .bezier([ (0, needle_y_top - z2), (0, (needle_y_top + needle_y_mid)/2), (needle_dx-z2*2, (needle_y_top + needle_y_mid)/2), - (z2, needle_y_mid + z2), + (z, y1), ]) .bezier([ (0, needle_y_top - z2), (0, (needle_y_top + needle_y_mid)/2), (-needle_dx+z2*2, (needle_y_top + needle_y_mid)/2), - (-z2, needle_y_mid + z2), + (-z, y1), ]) .assemble() ) return ( - self.profile_side() + self.profile_base() .boolean(window.moved(window_p1), mode='s') .boolean(window.moved(window_p1.flip_x()), mode='s') .boolean(window.moved(window_p2), mode='s') @@ -224,7 +261,7 @@ class Crown(Model): .boolean(scale_pan.moved(loc_scale_pan2), mode='s') .boolean(scale_body, mode='s') .boolean(needle, mode='s') - .boolean(needle_inner, mode='a') + .boolean(needle_head, mode='s') .clean() )