""" This schematics file contains all designs related to tool handles """ from dataclasses import dataclass import cadquery as Cq import nhf.metric_threads as NMt @dataclass(frozen=True) class Handle: """ Characteristic of a tool handle This assumes the handle segment material does not have threads. Each segment attaches to two insertions, which have threads on the inside. A connector has threads on the outside and joints two insertions. Note that all the radial sizes are diameters (in mm). """ # Outer and inner radius for the handle usually come in standard sizes diam: float = 38 diam_inner: float = 33 # Major diameter of the internal threads, following ISO metric screw thread # standard. This determines the wall thickness of the insertion. diam_threading: float = 27.0 thread_pitch: float = 3.0 # Internal cavity diameter. This determines the wall thickness of the connector diam_connector_internal: float = 18.0 # If set to true, do not generate threads simplify_geometry: bool = True # Length for the rim on the female connector rim_length: float = 5 insertion_length: float = 60 # Amount by which the connector goes into the segment connector_length: float = 60 def __post_init__(self): assert self.diam > self.diam_inner, "Material thickness cannot be <= 0" assert self.diam_inner > self.diam_insertion_internal, "Threading radius is too big" assert self.diam_insertion_internal > self.diam_connector_external assert self.diam_connector_external > self.diam_connector_internal, "Internal diameter is too large" assert self.insertion_length > self.rim_length @property def diam_insertion_internal(self): r = NMt.metric_thread_major_radius( self.diam_threading, self.thread_pitch, internal=True) return r * 2 @property def diam_connector_external(self): r = NMt.metric_thread_minor_radius( self.diam_threading, self.thread_pitch) return r * 2 def segment(self, length: float): result = ( Cq.Workplane() .cylinder( radius=self.diam / 2, height=length) ) result.faces("Z").tag("mate2") return result def _external_thread(self): return NMt.external_metric_thread( self.diam_threading, self.thread_pitch, self.insertion_length, top_lead_in=True) def _internal_thread(self): return NMt.internal_metric_thread( self.diam_threading, self.thread_pitch, self.insertion_length) def insertion(self): """ This type of joint is used to connect two handlebar pieces. Each handlebar piece is a tube which cannot be machined, so the joint connects to the handle by glue. Tags: * lip: Co-planar Mates to the rod * mate: Mates to the connector """ result = ( Cq.Workplane('XY') .cylinder( radius=self.diam_inner / 2, height=self.insertion_length - self.rim_length, centered=[True, True, False]) ) result.faces(">Z").tag("rim") if self.rim_length > 0: result = ( result.faces(">Z") .workplane() .circle(self.diam / 2) .extrude(self.rim_length) .faces(">Z") .hole(self.diam_insertion_internal) ) result.faces(">Z").tag("mate") if not self.simplify_geometry: thread = self._internal_thread().val() result = result.union(thread) return result def connector(self, solid: bool = False): """ Tags: * mate{1,2}: Mates to the connector """ result = ( Cq.Workplane('XY') .cylinder( radius=self.diam / 2, height=self.connector_length, ) ) for (tag, selector) in [("mate1", "Z")]: result.faces(selector).tag(tag) result = ( result .faces(selector) .workplane() .circle(self.diam_connector_external / 2) .extrude(self.insertion_length) ) if not solid: result = result.faces(">Z").hole(self.diam_connector_internal) if not self.simplify_geometry: thread = self._external_thread().val() result = ( result .union( thread .moved(Cq.Vector(0, 0, self.connector_length / 2))) .union( thread .rotate((0,0,0), (1,0,0), angleDegrees=90) .moved(Cq.Vector(0, 0, -self.connector_length / 2))) ) return result def one_side_connector(self): result = ( Cq.Workplane('XY') .cylinder( radius=self.diam / 2, height=self.rim_length, ) ) result.faces(">Z").tag("mate") result.faces("Z") .workplane() .circle(self.diam_connector_external / 2) .extrude(self.insertion_length) ) if not self.simplify_geometry: thread = self._external_thread().val() result = ( result .union( thread .moved(Cq.Vector(0, 0, self.connector_length / 2))) ) return result def connector_insertion_assembly(self): connector_color = Cq.Color(0.8,0.8,0.5,0.3) insertion_color = Cq.Color(0.7,0.7,0.7,0.3) result = ( Cq.Assembly() .add(self.connector(), name="c", color=connector_color) .add(self.insertion(), name="i1", color=insertion_color) .add(self.insertion(), name="i2", color=insertion_color) .constrain("c?mate1", "i1?mate", "Plane") .constrain("c?mate2", "i2?mate", "Plane") .solve() ) return result def connector_one_side_insertion_assembly(self): connector_color = Cq.Color(0.8,0.8,0.5,0.3) insertion_color = Cq.Color(0.7,0.7,0.7,0.3) result = ( Cq.Assembly() .add(self.insertion(), name="i", color=connector_color) .add(self.one_side_connector(), name="c", color=insertion_color) .constrain("i?mate", "c?mate", "Plane") .solve() ) return result