首页 > 其他分享 >Thread

Thread

时间:2024-04-14 18:01:10浏览次数:22  
标签:Thread thread self radius pitch threads cq

螺纹基础知识(五要素)

螺纹包括五个要素:牙型、公称直径、线数、螺距(或导程)和旋向。
  1. 牙型

 
  1. 直径

螺纹有大径(d、D)、中径(d2、D2)、小径(d1、D1),在表示螺纹时采用的是公称直径,公称直径是代表螺纹尺寸的直径。普通螺纹的公称直径就是大径。
  1. 线数

沿一条螺旋线形成的螺纹称为单线螺纹,沿轴向等距分布的两条或两条以上的螺旋线形成的螺纹称为多线螺纹。
  1. 螺距导程

      螺距(p)是相邻两牙在中径线上对应两点间的轴向距离。   导程(ph)是同一条螺旋线上的相邻两牙在中径线上对应两点间的轴向距离。   单线螺纹时,导程=螺距;多线螺纹时,导程=螺距×线数
  1. 旋向

      顺时针旋转时旋入的螺纹称为右旋螺纹;   逆时针旋转时旋入的螺纹称为左旋螺纹。
https://zhuanlan.zhihu.com/p/687901073

class Thread(Solid):

继承solid大类
  1. 参数:
  • apex_radius: 螺纹尖端的半径 外螺纹最大半径 内螺纹最小半径
  • apex_width: 螺纹宽基底的半径
  • root_radius: 螺纹宽基底的半径
  • root_width: 螺纹基底宽度
  • pitch: 360°螺纹旋转的长度
  • length: 螺纹的端到端长度
  • apex_offset: 不对称螺纹顶点偏移量,默认为0.0
  • hand: 旋转方向,默认为"right"
  • taper_angle: 锥形螺纹的角度,默认为None
  • end_finishes: 每个端部的轮廓,可以是:
    • "raw" - 未完成的,通常会导致螺纹延伸到z=0以下或z=length以上
    • "fade" - 螺纹高度在90°的弧度上降为零(或1/4螺距)
    • "square" - 通过z=0或z=length平面剪切
    • "chamfer" - 圆锥形端部,有助于螺栓对准螺母
      • 默认为("raw","raw")。 simple: 停止在螺纹计算阶段,不创建螺纹,默认为False。

def fade_helix(self, t: float, apex: bool, vertical_displacement: float)

用于创建螺旋形螺纹的衰减尖端,一般为螺距的1/4  
def fade_helix(
        self, t: float, apex: bool, vertical_displacement: float
) -> Tuple[float, float, float]:
    """A helical function used to create the faded tips of threads that spirals
    self.tooth_height in self.pitch/4"""
if self.external:
        radius = (
            self.apex_radius - sin(t * pi / 2) * self.tooth_height
            if apex
            else self.root_radius
        )
    else:
        radius = (
            self.apex_radius + sin(t * pi / 2) * self.tooth_height
            if apex
            else self.root_radius
        )

    z_pos = t * self.pitch / 4 + t * vertical_displacement
    x_pos = radius * cos(t * pi / 2)
    y_pos = radius * sin(t * pi / 2)
    return (x_pos, y_pos, z_pos)

def cq_object(self)

@property
def cq_object(self):
    """A cadquery Solid thread as defined by class attributes"""
warn("cq_object will be deprecated.", DeprecationWarning, stacklevel=2)
    return Solid(self.wrapped)
   

全部代码


class Thread(Solid):
     """
    螺旋螺纹 Helical thread
    最通用的螺纹类,用于构建所有其他类型的螺纹。
    创建具有给定根部和顶部半径的右旋或左旋螺旋螺纹。

    参数:
        apex_radius: 螺纹尖端的半径。
        apex_width: 螺纹宽基底的半径。
        root_radius: 螺纹宽基底的半径。
        root_width: 螺纹基底宽度。
        pitch: 360°螺纹旋转的长度。
        length: 螺纹的端到端长度。
        apex_offset: 不对称螺纹顶点偏移量,默认为0.0。
        hand: 旋转方向,默认为"right"。
        taper_angle: 锥形螺纹的角度,默认为None。
        end_finishes: 每个端部的轮廓,可以是:
            "raw" - 未完成的,通常会导致螺纹延伸到z=0以下或z=length以上
            "fade" - 螺纹高度在90°的弧度上降为零(或1/4螺距)
            "square" - 通过z=0或z=length平面剪切
            "chamfer" - 圆锥形端部,有助于螺栓对准螺母
            默认为("raw","raw")。
        simple: 停止在螺纹计算阶段,不创建螺纹,默认为False。

    异常:
        ValueError: 如果end_finishes不在["raw", "square", "fade", "chamfer"]中:
    """

def fade_helix(
        self, t: float, apex: bool, vertical_displacement: float
    ) -> Tuple[float, float, float]:
        """A helical function used to create the faded tips of threads that spirals
        self.tooth_height in self.pitch/4"""
if self.external:
            radius = (
                self.apex_radius - sin(t * pi / 2) * self.tooth_height
                if apex
                else self.root_radius
            )
        else:
            radius = (
                self.apex_radius + sin(t * pi / 2) * self.tooth_height
                if apex
                else self.root_radius
            )

        z_pos = t * self.pitch / 4 + t * vertical_displacement
        x_pos = radius * cos(t * pi / 2)
        y_pos = radius * sin(t * pi / 2)
        return (x_pos, y_pos, z_pos)

    @property
    def cq_object(self):
        """A cadquery Solid thread as defined by class attributes"""
warn("cq_object will be deprecated.", DeprecationWarning, stacklevel=2)
        return Solid(self.wrapped)

    def __init__(
        self,
        apex_radius: float,
        apex_width: float,
        root_radius: float,
        root_width: float,
        pitch: float,
        length: float,
        apex_offset: float = 0.0,
        hand: Literal["right", "left"] = "right",
        taper_angle: Optional[float] = None,
        end_finishes: Tuple[
            Literal["raw", "square", "fade", "chamfer"],
            Literal["raw", "square", "fade", "chamfer"],
        ] = ("raw", "raw"),
        simple: bool = False,
    ):
        """Store the parameters and create the thread object"""
for finish in end_finishes:
            if finish not in ["raw", "square", "fade", "chamfer"]:
                raise ValueError(
                    'end_finishes invalid, must be tuple() of "raw, square, taper, or chamfer"'
                )
        self.external = apex_radius > root_radius
        self.apex_radius = apex_radius
        self.apex_width = apex_width
        # Unfortunately, when creating "fade" ends inaccuracies in parametric curve calculations
# can result in a gap which causes the OCCT core to fail when combining with other
# object (like the core of the thread). To avoid this, subtract (or add) a fudge factor
# to the root radius to make it small enough to intersect the given radii.
self.root_radius = root_radius - (0.001 if self.external else -0.001)
        self.root_width = root_width
        self.pitch = pitch
        self.length = length
        self.apex_offset = apex_offset
        self.right_hand = hand == "right"
        self.end_finishes = end_finishes
        self.tooth_height = abs(self.apex_radius - self.root_radius)
        self.taper = 360 if taper_angle is None else taper_angle
        self.simple = simple

        if not simple:
            # Create base cylindrical thread
number_faded_ends = self.end_finishes.count("fade")
            cylindrical_thread_length = self.length + self.pitch * (
                1 - 1 * number_faded_ends
            )
            if self.end_finishes[0] == "fade":
                cylindrical_thread_displacement = self.pitch / 2
            else:
                cylindrical_thread_displacement = -self.pitch / 2

            # Either create a cylindrical thread for further processing
# or create a cylindrical thread segment with faded ends
if number_faded_ends == 0:
                cq_object = self.make_thread_solid(cylindrical_thread_length).translate(
                    (0, 0, cylindrical_thread_displacement)
                )
            else:
                cq_object = self.make_thread_with_faded_ends(
                    number_faded_ends,
                    cylindrical_thread_length,
                    cylindrical_thread_displacement,
                )

            # Square off ends if requested
cq_object = self.square_off_ends(cq_object)
            # Chamfer ends if requested
cq_object = self.chamfer_ends(cq_object)
            if isinstance(cq_object, Compound) and len(cq_object.Solids()) == 1:
                super().__init__(cq_object.Solids()[0].wrapped)
            else:
                super().__init__(cq_object.wrapped)
        else:
            # Initialize with a valid shape then nullify
super().__init__(Solid.makeBox(1, 1, 1).wrapped)
            self.wrapped = TopoDS_Shape()

    def make_thread_with_faded_ends(
        self,
        number_faded_ends,
        cylindrical_thread_length,
        cylindrical_thread_displacement,
    ):
        """Build the thread object from cylindrical thread faces and
        faded ends faces"""
(thread_faces, end_faces) = self.make_thread_faces(cylindrical_thread_length)

        # Need to operate on each face below
thread_faces = [
            f.translate((0, 0, cylindrical_thread_displacement)) for f in thread_faces
        ]
        end_faces = [
            f.translate((0, 0, cylindrical_thread_displacement)) for f in end_faces
        ]
        cylindrical_thread_angle = (
            (360 if self.right_hand else -360) * cylindrical_thread_length / self.pitch
        )
        (fade_faces, _fade_ends) = self.make_thread_faces(
            self.pitch / 4, fade_helix=True
        )
        if not self.right_hand:
            fade_faces = [f.mirror("XZ") for f in fade_faces]

        if self.end_finishes[0] == "fade":
            # If the thread is asymmetric the bottom fade end needs to be recreated as
# no amount of flipping or rotating can generate the shape
if self.apex_offset != 0:
                (fade_faces_bottom, _fade_ends) = self.make_thread_faces(
                    self.pitch / 4, fade_helix=True, asymmetric_flip=True
                )
                if not self.right_hand:
                    fade_faces_bottom = [f.mirror("XZ") for f in fade_faces_bottom]
            else:
                fade_faces_bottom = fade_faces
            fade_faces_bottom = [
                f.mirror("XZ").mirror("XY").translate(cq.Vector(0, 0, self.pitch / 2))
                for f in fade_faces_bottom
            ]
        if self.end_finishes[1] == "fade":
            fade_faces_top = [
                f.translate(
                    cq.Vector(
                        0,
                        0,
                        cylindrical_thread_length + cylindrical_thread_displacement,
                    )
                ).rotate((0, 0, 0), (0, 0, 1), cylindrical_thread_angle)
                for f in fade_faces
            ]
        if number_faded_ends == 2:
            thread_shell = cq.Shell.makeShell(
                thread_faces + fade_faces_bottom + fade_faces_top
            )
        elif self.end_finishes[0] == "fade":
            thread_shell = cq.Shell.makeShell(
                thread_faces + fade_faces_bottom + [end_faces[1]]
            )
        else:
            thread_shell = cq.Shell.makeShell(
                thread_faces + fade_faces_top + [end_faces[0]]
            )
        return cq.Solid.makeSolid(thread_shell)

    def square_off_ends(self, cq_object: Solid):
        """Square off the ends of the thread"""

squared = cq_object
        if self.end_finishes.count("square") != 0:
            # Note: box_size must be > max(apex,root) radius or the core doesn't cut correctly
half_box_size = 2 * max(self.apex_radius, self.root_radius)
            box_size = 2 * half_box_size
            cutter = cq.Solid.makeBox(
                length=box_size,
                width=box_size,
                height=self.length,
                pnt=cq.Vector(-half_box_size, -half_box_size, -self.length),
            )
            for i in range(2):
                if self.end_finishes[i] == "square":
                    squared = cq_object.cut(
                        cutter.translate(cq.Vector(0, 0, 2 * i * self.length))
                    )
        return squared

    def chamfer_ends(self, cq_object: Solid):
        """Chamfer the ends of the thread"""

chamfered = cq_object
        if self.end_finishes.count("chamfer") != 0:
            cutter = (
                cq.Workplane("XY")
                .circle(self.root_radius)
                .circle(self.apex_radius)
                .extrude(self.length)
            )
            face_selectors = ["<Z", ">Z"]
            edge_radius_selector = 1 if self.apex_radius > self.root_radius else 0
            for i in range(2):
                if self.end_finishes[i] == "chamfer":
                    cutter = (
                        cutter.faces(face_selectors[i])
                        .edges(cq.selectors.RadiusNthSelector(edge_radius_selector))
                        .chamfer(self.tooth_height * 0.5, self.tooth_height * 0.75)
                    )
            chamfered = cq_object.intersect(cutter.val())
        return chamfered

    def make_thread_faces(
        self, length: float, fade_helix: bool = False, asymmetric_flip: bool = False
    ) -> Tuple[List[cq.Face]]:
        """Create the thread object from basic CadQuery objects

        This method creates three types of thread objects:
        1. cylindrical - i.e. following a simple helix
        2. tapered - i.e. following a conical helix
        3. faded - cylindrical but spiralling towards the root in 90°

        After testing many alternatives (sweep, extrude with rotation, etc.) the
        following algorithm was found to be the fastest and most reliable:
        a. first create all the edges - helical, linear or parametric
        b. create either 5 or 6 faces from the edges (faded needs 5)
        c. create a shell from the faces
        d. create a solid from the shell
        """
local_apex_offset = -self.apex_offset if asymmetric_flip else self.apex_offset
        apex_helix_wires = [
            cq.Workplane("XY")
            .parametricCurve(
                lambda t: self.fade_helix(t, apex=True, vertical_displacement=0)
            )
            .val()
            .translate((0, 0, i * self.apex_width + local_apex_offset))
            if fade_helix
            else cq.Wire.makeHelix(
                pitch=self.pitch,
                height=length,
                radius=self.apex_radius,
                angle=self.taper,
                lefthand=not self.right_hand,
            ).translate((0, 0, i * self.apex_width + local_apex_offset))
            for i in [-0.5, 0.5]
        ]
        assert apex_helix_wires[0].isValid()
        root_helix_wires = [
            cq.Workplane("XY")
            .parametricCurve(
                lambda t: self.fade_helix(
                    t,
                    apex=False,
                    vertical_displacement=-i * (self.root_width - self.apex_width),
                )
            )
            .val()
            .translate((0, 0, i * self.root_width))
            if fade_helix
            else cq.Wire.makeHelix(
                pitch=self.pitch,
                height=length,
                radius=self.root_radius,
                angle=self.taper,
                lefthand=not self.right_hand,
            ).translate((0, 0, i * self.root_width))
            for i in [-0.5, 0.5]
        ]
        # When creating a cylindrical or tapered thread two end faces are required
# to enclose the thread object, while faded thread only has one end face
end_caps = [0] if fade_helix else [0, 1]
        end_cap_wires = [
            cq.Wire.makePolygon(
                [
                    apex_helix_wires[0].positionAt(i),
                    apex_helix_wires[1].positionAt(i),
                    root_helix_wires[1].positionAt(i),
                    root_helix_wires[0].positionAt(i),
                    apex_helix_wires[0].positionAt(i),
                ]
            )
            for i in end_caps
        ]
        thread_faces = [
            cq.Face.makeRuledSurface(apex_helix_wires[0], apex_helix_wires[1]),
            cq.Face.makeRuledSurface(apex_helix_wires[1], root_helix_wires[1]),
            cq.Face.makeRuledSurface(root_helix_wires[1], root_helix_wires[0]),
            cq.Face.makeRuledSurface(root_helix_wires[0], apex_helix_wires[0]),
        ]
        end_faces = [cq.Face.makeFromWires(end_cap_wires[i]) for i in end_caps]
        return (thread_faces, end_faces)

    def make_thread_solid(
        self,
        length: float,
        fade_helix: bool = False,
    ) -> cq.Solid:
        """Create a solid object by first creating the faces"""
(thread_faces, end_faces) = self.make_thread_faces(length, fade_helix)

        thread_shell = cq.Shell.makeShell(thread_faces + end_faces)
        thread_solid = cq.Solid.makeSolid(thread_shell)
        return thread_solid

demo1

代码

from cadquery import *
from math import *

def helix(r0,r_eps,p,h,d=0,frac=1e-1):
   
    def func(t):
       
        if t>frac and t<1-frac:
            z = h*t + d
            r = r0+r_eps
        elif t<=frac:
            z = h*t + d*sin(pi/2 *t/frac)
            r = r0 + r_eps*sin(pi/2 *t/frac)
        else:
            z = h*t - d*sin(2*pi - pi/2*(1-t)/frac)
            r = r0 - r_eps*sin(2*pi - pi/2*(1-t)/frac)
           
        x = r*sin(-2*pi/(p/h)*t)
        y = r*cos(2*pi/(p/h)*t)
       
        return x,y,z
   
    return func

def thread(radius, pitch, height, d, radius_eps, aspect= 10):
   
    e1_bottom = (cq.Workplane("XY")
        .parametricCurve(helix(radius,0,pitch,height,-d)).val()
    )
    e1_top = (cq.Workplane("XY")
        .parametricCurve(helix(radius,0,pitch,height,d)).val()
    )
   
    e2_bottom = (cq.Workplane("XY")
        .parametricCurve(helix(radius,radius_eps,pitch,height,-d/aspect)).val()
    )
    e2_top = (cq.Workplane("XY")
        .parametricCurve(helix(radius,radius_eps,pitch,height,d/aspect)).val()
    )
   
    f1 = Face.makeRuledSurface(e1_bottom, e1_top)
    f2 = Face.makeRuledSurface(e2_bottom, e2_top)
    f3 = Face.makeRuledSurface(e1_bottom, e2_bottom)
    f4 = Face.makeRuledSurface(e1_top, e2_top)
   
    sh = Shell.makeShell([f1,f2,f3,f4])
    rv = Solid.makeSolid(sh)
   
    return rv


radius = 4
pitch = 2
height = 4
d = pitch/4
radius_eps = 0.5
eps=1e-3

core = cq.Workplane("XY",origin=(0,0,-d)).circle(radius-1-eps).circle(radius+eps).extrude(height+1.75*d)
th1 = thread(radius,pitch,height,d,radius_eps)
th2  =thread(radius-1,pitch,height,d,-radius_eps)

res = core.union(Compound.makeCompound([th1,th2]))

show_object(res)

代码解释

helix 函数 这个函数用于生成一个螺旋线(helix)的参数方程。螺旋线是通过圆半径、螺距、高度等参数定义的三维曲线。  
  • r0, r_eps: 控制螺旋线半径的参数。
  • p: 螺距,即螺旋线在一个完整周期内沿轴向的移动距离。
  • h: 螺旋线的总高度。
  • d: 控制螺旋线在起始和结束位置的偏移量。
  • frac: 控制螺旋线在起始和结束位置过渡段的长度比例。
 

thread 函数

  这个函数利用 helix 函数生成的螺旋线创建一个螺纹的3D模型。  
  • radius, pitch, height: 分别代表螺纹的基本半径、螺距和高度。
  • d: 控制螺纹在起始和结束位置的偏移量。
  • radius_eps: 控制螺纹半径的扩展量,用于创建螺纹的外形。
  • aspect: 控制螺纹在顶部和底部的过渡形状。
 

创建螺纹模型

  代码的最后部分使用 thread 函数创建了两个螺纹模型(th1th2),并将它们与一个中心的圆柱体(core)合并,形成最终的3D模型。  
  • radius, pitch, height, d, radius_eps: 这些参数定义了螺纹的基本形状和尺寸。
  • core: 一个圆柱体,作为螺纹模型的中心部分。
  • th1th2: 通过 thread 函数创建的两个螺纹模型,分别位于圆柱体的外侧和内侧。
  • res: 最终的3D模型,通过合并 coreth1th2 得到。
  总的来说,这段代码展示了如何使用 cadquery 库通过参数化的方法创建具有特定螺纹形状的3D模型。这种方法非常适合于需要精确控制几何形状的CAD设计任务。

demo2

https://sourceforge.net/p/nl10/code/HEAD/tree/cq-code/common/metric_threads.py
male   = external_metric_thread(3.0, 0.5, 4.0, z_start= -0.85,top_lead_in=True)
female = internal_metric_thread(3.0, 0.5, 1.5,  bottom_chamfer=True, base_tube_od= 4.5)

代码

# Copyright (c) 2020-2024, Nerius Anthony Landys.  All rights reserved.
#   neri-engineering 'at' protonmail.com
#   https://svn.code.sf.net/p/nl10/code/cq-code/common/metric_threads.py
#
# Simple code example to create meshing M3x0.5 threads:
###############################################################################
#
#     male   = external_metric_thread(3.0, 0.5, 4.0, z_start= -0.85,
#                                     top_lead_in=True)
#
#     # Please note that the female thread is meant for a hole which has
#     # radius equal to metric_thread_major_radius(3.0, 0.5, internal=True),
#     # which is in fact very slightly larger than a 3.0 diameter hole.
#
#     female = internal_metric_thread(3.0, 0.5, 1.5,
#                                     bottom_chamfer=True, base_tube_od= 4.5)
#
###############################################################################
# Left hand threads can be created by employing one of the "mirror" operations.
# Thanks for taking the time to understand and use this code!

import math
import cadquery as cq

###############################################################################
# The functions which have names preceded by '__' are not meant to be called
# externally; the remaining functions are written with the intention that they
# will be called by external code.  The first section of code consists of
# lightweight helper functions; the meat and potatoes of this library is last.
###############################################################################

# Return value is in degrees, and currently it's fixed at 30.  Essentially this
# results in a typical 60 degree equilateral triangle cutting bit for threads.
def metric_thread_angle():
    return 30

# Helper func. to make code more intuitive and succinct.  Degrees --> radians.
def __deg2rad(degrees):
    return degrees * math.pi / 180

# In the absence of flat thread valley and flattened thread tip, returns the
# amount by which the thread "triangle" protrudes outwards (radially) from base
# cylinder in the case of external thread, or the amount by which the thread
# "triangle" protrudes inwards from base tube in the case of internal thread.
def metric_thread_perfect_height(pitch):
    return pitch / (2 * math.tan(__deg2rad(metric_thread_angle())))

# Up the radii of internal (female) thread in order to provide a little bit of
# wiggle room around male thread.  Right now input parameter 'diameter' is
# ignored.  This function is only used for internal/female threads.  Currently
# there is no practical way to adjust the male/female thread clearance besides
# to manually edit this function.  This design route was chosen for the sake of
# code simplicity.
def __metric_thread_internal_radius_increase(diameter, pitch):
    return 0.1 * metric_thread_perfect_height(pitch)

# Returns the major radius of thread, which is always the greater of the two.
def metric_thread_major_radius(diameter, pitch, internal=False):
    return (__metric_thread_internal_radius_increase(diameter, pitch) if
            internal else 0.0) + (diameter / 2)

# What portion of the total pitch is taken up by the angled thread section (and
# not the squared off valley and tip).  The remaining portion (1 minus ratio)
# will be divided equally between the flattened valley and flattened tip.
def __metric_thread_effective_ratio():
    return 0.7

# Returns the minor radius of thread, which is always the lesser of the two.
def metric_thread_minor_radius(diameter, pitch, internal=False):
    return (metric_thread_major_radius(diameter, pitch, internal)
            - (__metric_thread_effective_ratio() *
               metric_thread_perfect_height(pitch)))

# What the major radius would be if the cuts were perfectly triangular, without
# flat spots in the valleys and without flattened tips.
def metric_thread_perfect_major_radius(diameter, pitch, internal=False):
    return (metric_thread_major_radius(diameter, pitch, internal)
            + ((1.0 - __metric_thread_effective_ratio()) *
               metric_thread_perfect_height(pitch) / 2))

# What the minor radius would be if the cuts were perfectly triangular, without
# flat spots in the valleys and without flattened tips.
def metric_thread_perfect_minor_radius(diameter, pitch, internal=False):
    return (metric_thread_perfect_major_radius(diameter, pitch, internal)
            - metric_thread_perfect_height(pitch))

# Returns the lead-in and/or chamfer distance along the z axis of rotation.
# The lead-in/chamfer only depends on the pitch and is made with the same angle
# as the thread, that being 30 degrees offset from radial.
def metric_thread_lead_in(pitch, internal=False):
    return (math.tan(__deg2rad(metric_thread_angle()))
            * (metric_thread_major_radius(256.0, pitch, internal)
               - metric_thread_minor_radius(256.0, pitch, internal)))

# Returns the width of the flat spot in thread valley of a standard thread.
# This is also equal to the width of the flat spot on thread tip, on a standard
# thread.
def metric_thread_relief(pitch):
    return (1.0 - __metric_thread_effective_ratio()) * pitch / 2


###############################################################################
# A few words on modules external_metric_thread() and internal_metric_thread().
# The parameter 'z_start' is added as a convenience in order to make the male
# and female threads align perfectly.  When male and female threads are created
# having the same diameter, pitch, and n_starts (usually 1), then so long as
# they are not translated or rotated (or so long as they are subjected to the
# same exact translation and rotation), they will intermesh perfectly,
# regardless of the value of 'z_start' used on each.  This is in order that
# assemblies be able to depict perfectly aligning threads.

# Generates threads with base cylinder unless 'base_cylinder' is overridden.
# Please note that 'use_epsilon' is activated by default, which causes a slight
# budge in the minor radius, inwards, so that overlaps would be created with
# inner cylinders.  (Does not affect thread profile outside of cylinder.)
###############################################################################
def external_metric_thread(diameter,  # Required parameter, e.g. 3.0 for M3x0.5
                           pitch,     # Required parameter, e.g. 0.5 for M3x0.5
                           length,    # Required parameter, e.g. 2.0
                           z_start=0.0,
                           n_starts=1,
                           bottom_lead_in=False, # Lead-in is at same angle as
                           top_lead_in   =False, # thread, namely 30 degrees.
                           bottom_relief=False, # Add relief groove to start or
                           top_relief   =False, # end of threads (shorten).
                           force_outer_radius=-1.0, # Set close to diameter/2.
                           use_epsilon=True, # For inner cylinder overlap.
                           base_cylinder=True, # Whether to include base cyl.
                           cyl_extend_bottom=-1.0,
                           cyl_extend_top=-1.0,
                           envelope=False): # Draw only envelope, don't cut.

    cyl_extend_bottom = max(0.0, cyl_extend_bottom)
    cyl_extend_top    = max(0.0, cyl_extend_top)

    z_off             = (1.0 - __metric_thread_effective_ratio()) * pitch / 4
    t_start           = z_start
    t_length          = length
    if bottom_relief:
        t_start       = t_start  + (2 * z_off)
        t_length      = t_length - (2 * z_off)
    if top_relief:
        t_length      = t_length - (2 * z_off)
    outer_r           = (force_outer_radius if (force_outer_radius > 0.0) else
                         metric_thread_major_radius(diameter,pitch))
    inner_r           = metric_thread_minor_radius(diameter,pitch)
    epsilon           = 0
    inner_r_adj       = inner_r
    inner_z_budge     = 0
    if use_epsilon:
        epsilon       = (z_off/3) / math.tan(__deg2rad(metric_thread_angle()))
        inner_r_adj   = inner_r - epsilon
        inner_z_budge = math.tan(__deg2rad(metric_thread_angle())) * epsilon

    if envelope:
        threads = cq.Workplane("XZ")
        threads = threads.moveTo(inner_r_adj, -pitch)
        threads = threads.lineTo(outer_r, -pitch)
        threads = threads.lineTo(outer_r, t_length + pitch)
        threads = threads.lineTo(inner_r_adj, t_length + pitch)
        threads = threads.close()
        threads = threads.revolve()

    else: # Not envelope, cut the threads.
        wire = cq.Wire.makeHelix(pitch=pitch*n_starts,
                                 height=t_length+pitch,
                                 radius=inner_r)
        wire = wire.translate((0,0,-pitch/2))
        wire = wire.rotate(startVector=(0,0,0), endVector=(0,0,1),
                           angleDegrees=360*(-pitch/2)/(pitch*n_starts))
        d_mid = ((metric_thread_major_radius(diameter,pitch) - outer_r)
                 * math.tan(__deg2rad(metric_thread_angle())))
        thread = cq.Workplane("XZ")
        thread = thread.moveTo(inner_r_adj, -pitch/2 + z_off - inner_z_budge)
        thread = thread.lineTo(outer_r, -(z_off + d_mid))
        thread = thread.lineTo(outer_r, z_off + d_mid)
        thread = thread.lineTo(inner_r_adj, pitch/2 - z_off + inner_z_budge)
        thread = thread.close()
        thread = thread.sweep(wire, isFrenet=True)
        threads = thread
        for addl_start in range(1, n_starts):
            thread = thread.rotate(axisStartPoint=(0,0,0),
                                   axisEndPoint=(0,0,1),
                                   angleDegrees=360/n_starts)
            threads = threads.union(thread)

    square_shave = cq.Workplane("XY")
    square_shave = square_shave.box(length=outer_r*3, width=outer_r*3,
                                    height=pitch*2, centered=True)
    square_shave = square_shave.translate((0,0,-pitch)) # Because centered.
    # Always cut the top and bottom square.  Otherwise things don't play nice.
    threads = threads.cut(square_shave)

    if bottom_lead_in:
        delta_r = outer_r - inner_r
        rise = math.tan(__deg2rad(metric_thread_angle())) * delta_r
        lead_in = cq.Workplane("XZ")
        lead_in = lead_in.moveTo(inner_r - delta_r, -rise)
        lead_in = lead_in.lineTo(outer_r + delta_r, 2 * rise)
        lead_in = lead_in.lineTo(outer_r + delta_r, -pitch - rise)
        lead_in = lead_in.lineTo(inner_r - delta_r, -pitch - rise)
        lead_in = lead_in.close()
        lead_in = lead_in.revolve()
        threads = threads.cut(lead_in)

    # This was originally a workaround to the anomalous B-rep computation where
    # the top of base cylinder is flush with top of threads, without the use of
    # lead-in.  It turns out that preferring the use of the 'render_cyl_early'
    # strategy alleviates other problems as well.
    render_cyl_early = (base_cylinder and ((not top_relief) and
                                           (not (cyl_extend_top > 0.0)) and
                                           (not envelope)))
    render_cyl_late = (base_cylinder and (not render_cyl_early))
    if render_cyl_early:
        cyl = cq.Workplane("XY")
        cyl = cyl.circle(radius=inner_r)
        cyl = cyl.extrude(until=length+pitch+cyl_extend_bottom)
        # Make rotation of cylinder consistent with non-workaround case.
        cyl = cyl.rotate(axisStartPoint=(0,0,0), axisEndPoint=(0,0,1),
                         angleDegrees=-(360*t_start/(pitch*n_starts)))
        cyl = cyl.translate((0,0,-t_start+(z_start-cyl_extend_bottom)))
        threads = threads.union(cyl)

    # Next, make cuts at the top.
    square_shave = square_shave.translate((0,0,pitch*2+t_length))
    threads = threads.cut(square_shave)

    if top_lead_in:
        delta_r = outer_r - inner_r
        rise = math.tan(__deg2rad(metric_thread_angle())) * delta_r
        lead_in = cq.Workplane("XZ")
        lead_in = lead_in.moveTo(inner_r - delta_r, t_length + rise)
        lead_in = lead_in.lineTo(outer_r + delta_r, t_length - (2 * rise))
        lead_in = lead_in.lineTo(outer_r + delta_r, t_length + pitch + rise)
        lead_in = lead_in.lineTo(inner_r - delta_r, t_length + pitch + rise)
        lead_in = lead_in.close()
        lead_in = lead_in.revolve()
        threads = threads.cut(lead_in)

    # Place the threads into position.
    threads = threads.translate((0,0,t_start))
    if (not envelope):
        threads = threads.rotate(axisStartPoint=(0,0,0), axisEndPoint=(0,0,1),
                                 angleDegrees=360*t_start/(pitch*n_starts))

    if render_cyl_late:
        cyl = cq.Workplane("XY")
        cyl = cyl.circle(radius=inner_r)
        cyl = cyl.extrude(until=length+cyl_extend_bottom+cyl_extend_top)
        cyl = cyl.translate((0,0,z_start-cyl_extend_bottom))
        threads = threads.union(cyl)

    return threads


###############################################################################
# Generates female threads without a base tube, unless 'base_tube_od' is set to
# something which is sufficiently greater than 'diameter' parameter.  Please
# note that 'use_epsilon' is activated by default, which causes a slight budge
# in the major radius, outwards, so that overlaps would be created with outer
# tubes.  (Does not affect thread profile inside of tube or beyond extents.)
###############################################################################
def internal_metric_thread(diameter,  # Required parameter, e.g. 3.0 for M3x0.5
                           pitch,     # Required parameter, e.g. 0.5 for M3x0.5
                           length,    # Required parameter, e.g. 2.0.
                           z_start=0.0,
                           n_starts=1,
                           bottom_chamfer=False, # Chamfer is at same angle as
                           top_chamfer   =False, # thread, namely 30 degrees.
                           bottom_relief=False, # Add relief groove to start or
                           top_relief   =False, # end of threads (shorten).
                           use_epsilon=True, # For outer cylinder overlap.
                           # The base tube outer diameter must be sufficiently
                           # large for tube to be rendered.  Otherwise ignored.
                           base_tube_od=-1.0,
                           tube_extend_bottom=-1.0,
                           tube_extend_top=-1.0,
                           envelope=False): # Draw only envelope, don't cut.

    tube_extend_bottom = max(0.0, tube_extend_bottom)
    tube_extend_top    = max(0.0, tube_extend_top)

    z_off              = (1.0 - __metric_thread_effective_ratio()) * pitch / 4
    t_start            = z_start
    t_length           = length
    if bottom_relief:
        t_start        = t_start  + (2 * z_off)
        t_length       = t_length - (2 * z_off)
    if top_relief:
        t_length       = t_length - (2 * z_off)
    outer_r            = metric_thread_major_radius(diameter,pitch,
                                                    internal=True)
    inner_r            = metric_thread_minor_radius(diameter,pitch,
                                                    internal=True)
    epsilon            = 0
    outer_r_adj        = outer_r
    outer_z_budge      = 0
    if use_epsilon:
        # High values of 'epsilon' sometimes cause entire starts to disappear.
        epsilon        = (z_off/5) / math.tan(__deg2rad(metric_thread_angle()))
        outer_r_adj    = outer_r + epsilon
        outer_z_budge  = math.tan(__deg2rad(metric_thread_angle())) * epsilon

    if envelope:
        threads = cq.Workplane("XZ")
        threads = threads.moveTo(outer_r_adj, -pitch)
        threads = threads.lineTo(inner_r, -pitch)
        threads = threads.lineTo(inner_r, t_length + pitch)
        threads = threads.lineTo(outer_r_adj, t_length + pitch)
        threads = threads.close()
        threads = threads.revolve()

    else: # Not envelope, cut the threads.
        wire = cq.Wire.makeHelix(pitch=pitch*n_starts,
                                 height=t_length+pitch,
                                 radius=inner_r)
        wire = wire.translate((0,0,-pitch/2))
        wire = wire.rotate(startVector=(0,0,0), endVector=(0,0,1),
                           angleDegrees=360*(-pitch/2)/(pitch*n_starts))
        thread = cq.Workplane("XZ")
        thread = thread.moveTo(outer_r_adj, -pitch/2 + z_off - outer_z_budge)
        thread = thread.lineTo(inner_r, -z_off)
        thread = thread.lineTo(inner_r, z_off)
        thread = thread.lineTo(outer_r_adj, pitch/2 - z_off + outer_z_budge)
        thread = thread.close()
        thread = thread.sweep(wire, isFrenet=True)
        threads = thread
        for addl_start in range(1, n_starts):
            thread = thread.rotate(axisStartPoint=(0,0,0),
                                   axisEndPoint=(0,0,1),
                                   angleDegrees=360/n_starts)
            threads = threads.union(thread)
        # Rotate so that the external threads would align.
        threads = threads.rotate(axisStartPoint=(0,0,0), axisEndPoint=(0,0,1),
                                 angleDegrees=180/n_starts)

    square_len = max(outer_r*3, base_tube_od*1.125)
    square_shave = cq.Workplane("XY")
    square_shave = square_shave.box(length=square_len, width=square_len,
                                    height=pitch*2, centered=True)
    square_shave = square_shave.translate((0,0,-pitch)) # Because centered.
    # Always cut the top and bottom square.  Otherwise things don't play nice.
    threads = threads.cut(square_shave)

    if bottom_chamfer:
        delta_r = outer_r - inner_r
        rise = math.tan(__deg2rad(metric_thread_angle())) * delta_r
        chamfer = cq.Workplane("XZ")
        chamfer = chamfer.moveTo(inner_r - delta_r, 2 * rise)
        chamfer = chamfer.lineTo(outer_r + delta_r, -rise)
        chamfer = chamfer.lineTo(outer_r + delta_r, -pitch - rise)
        chamfer = chamfer.lineTo(inner_r - delta_r, -pitch - rise)
        chamfer = chamfer.close()
        chamfer = chamfer.revolve()
        threads = threads.cut(chamfer)

    # This was originally a workaround to the anomalous B-rep computation where
    # the top of base tube is flush with top of threads w/o the use of chamfer.
    # This is now being made consistent with the 'render_cyl_early' strategy in
    # external_metric_thread() whereby we prefer the "render early" plan of
    # action even in cases where a top chamfer or lead-in is used.
    render_tube_early = ((base_tube_od > (outer_r * 2)) and
                         (not top_relief) and
                         (not (tube_extend_top > 0.0)) and
                         (not envelope))
    render_tube_late = ((base_tube_od > (outer_r * 2)) and
                        (not render_tube_early))
    if render_tube_early:
        tube = cq.Workplane("XY")
        tube = tube.circle(radius=base_tube_od/2)
        tube = tube.circle(radius=outer_r)
        tube = tube.extrude(until=length+pitch+tube_extend_bottom)
        # Make rotation of cylinder consistent with non-workaround case.
        tube = tube.rotate(axisStartPoint=(0,0,0), axisEndPoint=(0,0,1),
                           angleDegrees=-(360*t_start/(pitch*n_starts)))
        tube = tube.translate((0,0,-t_start+(z_start-tube_extend_bottom)))
        threads = threads.union(tube)

    # Next, make cuts at the top.
    square_shave = square_shave.translate((0,0,pitch*2+t_length))
    threads = threads.cut(square_shave)

    if top_chamfer:
        delta_r = outer_r - inner_r
        rise = math.tan(__deg2rad(metric_thread_angle())) * delta_r
        chamfer = cq.Workplane("XZ")
        chamfer = chamfer.moveTo(inner_r - delta_r, t_length - (2 * rise))
        chamfer = chamfer.lineTo(outer_r + delta_r, t_length + rise)
        chamfer = chamfer.lineTo(outer_r + delta_r, t_length + pitch + rise)
        chamfer = chamfer.lineTo(inner_r - delta_r, t_length + pitch + rise)
        chamfer = chamfer.close()
        chamfer = chamfer.revolve()
        threads = threads.cut(chamfer)

    # Place the threads into position.
    threads = threads.translate((0,0,t_start))
    if (not envelope):
        threads = threads.rotate(axisStartPoint=(0,0,0), axisEndPoint=(0,0,1),
                                 angleDegrees=360*t_start/(pitch*n_starts))

    if render_tube_late:
        tube = cq.Workplane("XY")
        tube = tube.circle(radius=base_tube_od/2)
        tube = tube.circle(radius=outer_r)
        tube = tube.extrude(until=length+tube_extend_bottom+tube_extend_top)
        tube = tube.translate((0,0,z_start-tube_extend_bottom))
        threads = threads.union(tube)

    return threads

解释

# 版权声明和作者信息
# 本代码用于演示如何创建匹配的M3x0.5螺纹:
###############################################################################

# 生成外部螺纹的示例
#     male   = external_metric_thread(3.0, 0.5, 4.0, z_start= -0.85,
#                                     top_lead_in=True)

# 生成内部螺纹的示例,适用于直径稍大于3.0mm的孔
#     female = internal_metric_thread(3.0, 0.5, 1.5,
#                                     bottom_chamfer=True, base_tube_od= 4.5)

###############################################################################

# 通过镜像操作创建左旋螺纹
# 代码使用说明

import math
import cadquery as cq

###############################################################################

# 以下函数以'__'开头的不建议外部调用;其他函数则是为了被外部代码调用而编写的。
# 代码的第一部分是一些轻量级的辅助函数;库的核心功能位于最后。

# 返回螺纹角度,固定为30度,代表典型的60度等边三角形切削螺纹。
def metric_thread_angle():
    return 30

# 辅助函数,将度数转换为弧度。
def __deg2rad(degrees):
    return degrees * math.pi / 180

# 返回螺纹"三角形"从基础圆柱体向外(对于外螺纹)或向内(对于内螺纹)突出的高度。
def metric_thread_perfect_height(pitch):
    return pitch / (2 * math.tan(__deg2rad(metric_thread_angle())))

# 为内螺纹增加半径,提供一些间隙。
def __metric_thread_internal_radius_increase(diameter, pitch):
    return 0.1 * metric_thread_perfect_height(pitch)

# 返回螺纹的主半径,总是较大的那个。
def metric_thread_major_radius(diameter, pitch, internal=False):
    return (__metric_thread_internal_radius_increase(diameter, pitch) if
            internal else 0.0) + (diameter / 2)

# 返回螺纹的次半径,总是较小的那个。
def metric_thread_minor_radius(diameter, pitch, internal=False):
    return (metric_thread_major_radius(diameter, pitch, internal)
            - (__metric_thread_effective_ratio() *
               metric_thread_perfect_height(pitch)))

# 创建外部螺纹的函数,除非覆盖'base_cylinder'参数,否则会生成基础圆柱体。
def external_metric_thread(diameter, pitch, length, z_start=0.0, ...):
    # 函数实现...

# 创建内部螺纹的函数,除非设置了'base_tube_od'参数,否则不会生成基础管。
def internal_metric_thread(diameter, pitch, length, z_start=0.0, ...):
    # 函数实现...

demo3(cq绘制螺旋修饰线+solid)

import cadquery as cq
from cadquery import Workplane, Assembly
from cadquery.vis import show_object

wire = cq.Wire.makeHelix(pitch=0.5, height=30, radius=3, center=(0,0,-15))

s = Workplane(origin=(0,0,0)).cylinder(30,3)

result = Assembly(s)
result1= result.add(wire)

show_object(result1)
import cadquery as cq
from cadquery import Workplane, Assembly
from cadquery.occ_impl.exporters import export
from cadquery.vis import show_object

wire = cq.Wire.makeHelix(pitch=0.5, height=30, radius=3, center=(0,0,-15))

s = Workplane(origin=(0,0,0)).cylinder(30,3)

result = s.add(wire)

export(result, 'output.step')
show_object(result)
 

标签:Thread,thread,self,radius,pitch,threads,cq
From: https://www.cnblogs.com/arwen-xu/p/18134452

相关文章

  • 字节面试:ThreadLocal内存泄漏,怎么破?什么是 ITL、TTL、FTL?
    文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+涨薪必备免费赠送:《尼恩技术圣经+高并发系列PDF》,帮你实现技术自由,完成职业升级,薪......
  • 通过实例学C#之Thread类
    构造函数Thread(ThreadStart)该构造函数接受一个不带参数的方法。staticvoidMain(string[]args){Threadt1=newThread(newThreadStart(ThreadMethod1));t1.Start();Console.ReadKey();}staticvoidThreadMethod1(){for(inti=0;i<5......
  • ThreadPoolExecutor线程池解析
    ThreadPoolExecutor线程池解析一、ThreadPoolExecutor常见参数jdk中Executors提供了几种常用的线程池,底层都是ThreadPoolExecutor。publicThreadPoolExecutor(intcorePoolSize,//核心线程数intmaximumPoolSize,//最大线程数......
  • 一文详解ThreadLocal与线程间的数据传递
    一.ThreadLocalThreadLocal在并发编程中比较常用,在诸多中间件的源码中都能看到它的身影。对于ThreadLocal的说明先来一段官方解释:ThreadLocal提供的是一种线程局部变量。这些变量不同于其它变量的点在于每个线程在获取变量的时候,都拥有它自己相对独立的变量副本。ThreadLo......
  • JUC:ThreadPoolExecutor线程池的使用方法
    文章目录ThreadPoolExecutor线程池状态构造方法Executors工厂方法newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutor提交任务方法关闭任务方法ThreadPoolExecutor线程池状态线程池用高三位表示状态,第一位为符号位。TERMINATED>TIDYING>STOP>......
  • PySide2-QThread创建、终止、暂停、继续、延时功能实现
    程序实现了一个能够显示0-99数字循环进度的功能,并提供了进度查看、暂停、继续及终止操作。importsysimporttimefromPySide2.QtCoreimport(QObject,QThread,Qt,Signal,QTimer,QCoreApplication,QEventLoop,Slot,)fromPy......
  • 01Thread
    前言准备再好好总结一下线程。1.概念并行(parallel):同一时间,多个线程/进程同时执行。多线程的目的就是为了并行,充分利用cpu多个核心,提高程序性能。线程(threading):线程是操作系统能够进行运算调度的最小单位,是进程的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个......
  • new Object()和new Thread()
    newObject()过程如下:JVM分配一块内存M在内存M上初始化该对象将内存M的地址赋值给引用变量obj创建线程的过程如下:JVM为一个线程栈分配内存,该栈为每个线程方法调用保存一个栈帧每一栈帧由一个局部变量数组、返回值、操作数堆栈和常量池组成每个线程获得一个程序计......
  • Java:多线程-继承Thread类
    在Java中,通过继承Thread类是实现多线程的一种方式。这种方式允许你创建自己的线程类,并定义线程执行的具体内容。以下是关于继承Thread类的详细讲解:继承Thread类的步骤创建线程类:创建一个继承自Thread的子类。重写run方法:在子类中重写run方法,定义线程执行的任务。run方法是......
  • Rust Thread Adventure
    HelloeveryoneandwelcometothewonderfulworldofRustMultithreading!Today,we'regoingtoexplorethisexcitingareatogether.Areyouready?Buckleupandlet'sgo!InRust,threadsarelikethesuperheroesofyourprogram.Theycanperfo......