from copy import deepcopy
from dataclasses import dataclass, field
from math import degrees, radians, sin, tan
from typing import List
from taperable_helix import Helix, HelixLocation
[docs]@dataclass
class HelicalThread(Helix):
"""
A set of fields used to represent a helical thread and passed as
the parameter to `helical_thread`.
Control of the size and spacing of the thread using the various
fields in Helix and those below.
"""
angle_degs: float = 45
"""angle in degrees"""
major_cutoff: float = 0
"""Size of of flat at the major diameter"""
minor_cutoff: float = 0
"""Size of flat at the minor diameter"""
ext_clearance: float = 0.1
"""External clearance between external and internal threads"""
thread_overlap: float = 0.001
"""
Amount to overlap threads with the core so the union of core and
threads is a manifold
"""
[docs]@dataclass
class ThreadHelixes:
"""
The helixes returned by helical_thread` that represents the internal
thread, prefixed with `int_` and the external thread, prefixed with `ext_`.
"""
ht: HelicalThread
"""The basic Dimensions of the helixes"""
int_helix_radius: float = 0
"""The internal thread radius"""
int_helixes: List[HelixLocation] = field(default_factory=list)
"""List of the internal helix locations"""
ext_helix_radius: float = 0
"""The external thread radius"""
ext_helixes: List[HelixLocation] = field(default_factory=list)
"""List of the external helix locations"""
[docs]def helical_thread(ht: HelicalThread) -> ThreadHelixes:
"""
Given HelicalThread compute the internal and external
helixes thread and returning them in ThreadHelixes.
int_hexlix_radius, int_helixes, ext_helix_radius and ext_helixes.
The helixes are an array of HelixLocations that define the helixes of
the thread. If minor_cutoff is 0 then the thread will be triangular
and the length of the {int|ext}_helixes 3. if minor_cutoff > 0 then
the thread will be a trapezoid with the length of the {int|ext}_helixes
will be 4.
:param ht: The basic dimensions of the helicla thread
:returns: internal and external helixes necessary to use taperable-helix
"""
# print(
# f"helical_thread:+ height={height:.3f} pitch={pitch:.3f} angle_degs={angle_degs:.3f}"
# )
# print(
# f"helical_thread: inset={inset:.3f} ext_clearance={ext_clearance} taper_rpos={taper_rpos:.3f}"
# )
# print(
# f"helical_thread: major_cutoff={major_cutoff} minor_cutoff={minor_cutoff} thread_overlap={thread_overlap:.3f} "
# )
# print(
# f"helical_thread: first_t={first_t} last_t={last_t} "
# )
result: ThreadHelixes = ThreadHelixes(ht)
angle_radians: float = radians(ht.angle_degs)
tan_hangle: float = tan(angle_radians / 2)
sin_hangle: float = sin(angle_radians / 2)
tip_to_major_cutoff: float = ((ht.pitch - ht.major_cutoff) / 2) / tan_hangle
tip_to_minor_cutoff: float = (ht.minor_cutoff / 2) / tan_hangle
# print(
# f"helical_thread: tip_to_major_cutoff={tip_to_major_cutoff:.3f} tip_to_minor_cutoff={tip_to_minor_cutoff:.3f}"
# )
int_thread_depth: float = tip_to_major_cutoff - tip_to_minor_cutoff
# print(f"helical_thread: int_thread_depth={int_thread_depth}")
thread_overlap_vert_adj: float = ht.thread_overlap * tan_hangle
thread_half_height_at_helix_radius: float = (
(ht.pitch - ht.major_cutoff) / 2
) + thread_overlap_vert_adj
thread_half_height_at_opposite_helix_radius: float = ht.minor_cutoff / 2
# print(
# f"thh_at_r={thread_half_height_at_helix_radius} thh_at_or={thread_half_height_at_opposite_helix_radius} td={int_thread_depth}"
# )
# Internal thread have helix thread radisu
result.int_helix_radius = ht.radius
result.int_helixes = []
# print(f"result.int_helix_radius={result.int_helix_radius}")
hl = HelixLocation(
radius=result.int_helix_radius + ht.thread_overlap,
horz_offset=0,
vert_offset=-thread_half_height_at_helix_radius,
)
result.int_helixes.append(hl)
hl = HelixLocation(
radius=result.int_helix_radius + ht.thread_overlap,
horz_offset=0,
vert_offset=+thread_half_height_at_helix_radius,
)
result.int_helixes.append(hl)
hl = HelixLocation(
radius=result.int_helix_radius,
horz_offset=-int_thread_depth,
vert_offset=+thread_half_height_at_opposite_helix_radius,
)
result.int_helixes.append(hl)
if ht.minor_cutoff > 0:
hl = HelixLocation(
radius=result.int_helix_radius,
horz_offset=-int_thread_depth,
vert_offset=-thread_half_height_at_opposite_helix_radius,
)
result.int_helixes.append(hl)
# Use ext_clearance to calcuate external thread values
# hyp is the hypothense of the trinagle formed by a radial
# line, the tip of the internal thread and the tip of the
# external thread.
hyp: float = ht.ext_clearance / sin_hangle
# ext_vert_adj is the amount to ajdust verticaly the helix
ext_vert_adj: float = (hyp - ht.ext_clearance) * tan_hangle
# print(f"hyp={hyp} ext_vert_adj={ext_vert_adj}")
# External thread have the helix on the minor side and
# so we subtract the int_thread_depth and ext_clearance from ht.radius
result.ext_helix_radius = ht.radius - int_thread_depth - ht.ext_clearance
# print(
# f"result.ext_helix_radius={ht.ext_helix_radius} td={int_thread_depth} ec={ht.ext_clearance}"
# )
ext_thread_half_height_at_ext_helix_radius: float = (
(ht.pitch - ht.minor_cutoff) / 2
) - ext_vert_adj
ext_thread_half_height_at_ext_helix_radius_plus_tova: float = (
ext_thread_half_height_at_ext_helix_radius + thread_overlap_vert_adj
)
# When major cutoff becomes smaller than the exter_vert_adj then the
# external thread will only be three points and we set
# ext_thrad_half_height_at_opposite_ext_helix_radius # to 0 and
# compute the thread depth. Under these circumstances the clearance
# from the external tip to internal core will be close to ext_clearance
# or greater. See test_thread.py or test_thread_new.py.
ext_thread_half_height_at_opposite_ext_helix_radius: float = (
ht.major_cutoff / 2
) - ext_vert_adj
ext_thread_depth: float = int_thread_depth
if ext_thread_half_height_at_opposite_ext_helix_radius < 0:
ext_thread_half_height_at_opposite_ext_helix_radius = 0
ext_thread_depth = ext_thread_half_height_at_ext_helix_radius / tan_hangle
# print(
# f"ext_thread_depth={ext_thread_depth} ext_thh_at_ehr={ext_thread_half_height_at_ext_helix_radius} ext_thh_at_ehr_plus_tovo={ext_thread_half_height_at_ext_helix_radius_plus_tova} ext_thh_at_oehr={ext_thread_half_height_at_opposite_ext_helix_radius}"
# )
result.ext_helixes = []
hl = HelixLocation(
radius=result.ext_helix_radius - ht.thread_overlap,
horz_offset=0,
vert_offset=-ext_thread_half_height_at_ext_helix_radius_plus_tova,
)
result.ext_helixes.append(hl)
hl = HelixLocation(
radius=result.ext_helix_radius - ht.thread_overlap,
horz_offset=0,
vert_offset=+ext_thread_half_height_at_ext_helix_radius_plus_tova,
)
result.ext_helixes.append(hl)
hl = HelixLocation(
radius=result.ext_helix_radius,
horz_offset=ext_thread_depth,
vert_offset=+ext_thread_half_height_at_opposite_ext_helix_radius,
)
result.ext_helixes.append(hl)
if ext_thread_half_height_at_opposite_ext_helix_radius > 0:
hl = HelixLocation(
radius=result.ext_helix_radius,
horz_offset=ext_thread_depth,
vert_offset=-ext_thread_half_height_at_opposite_ext_helix_radius,
)
result.ext_helixes.append(hl)
return result