diff --git a/bottlecaps.scad b/bottlecaps.scad index fb83a0f..6a79ca2 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -9,7 +9,8 @@ include <threading.scad> include <knurling.scad> - +include <structs.scad> +include <rounding.scad> // Section: PCO-1810 Bottle Threading @@ -53,7 +54,7 @@ module pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) tamper_base_h = 14.10; threadbase_d = 24.51; thread_pitch = 3.18; - thread_angle = 20; + flank_angle = 20; thread_od = 27.43; lip_d = 25.07; lip_h = 1.70; @@ -113,7 +114,7 @@ module pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) d=threadbase_d-0.1, pitch=thread_pitch, thread_depth=thread_h+0.1, - thread_angle=thread_angle, + flank_angle=flank_angle, twist=810, higbee=thread_h*2, anchor=TOP @@ -164,7 +165,7 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) cap_id = 28.58; tamper_ring_h = 14.10; thread_pitch = 3.18; - thread_angle = 20; + flank_angle = 20; thread_od = cap_id; thread_depth = 1.6; @@ -192,7 +193,7 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) } up(wall) cyl(d=cap_id, h=tamper_ring_h+wall, anchor=BOTTOM); } - up(wall+2) thread_helix(d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, thread_angle=thread_angle, twist=810, higbee=thread_depth, internal=true, anchor=BOTTOM); + up(wall+2) thread_helix(d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, flank_angle=flank_angle, twist=810, higbee=thread_depth, internal=true, anchor=BOTTOM); } children(); } @@ -246,7 +247,7 @@ module pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) tamper_divot_r = 1.08; threadbase_d = 24.20; thread_pitch = 2.70; - thread_angle = 15; + flank_angle = 15; thread_od = 27.4; lip_d = 25.07; lip_h = 1.70; @@ -306,7 +307,7 @@ module pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) d=threadbase_d-0.1, pitch=thread_pitch, thread_depth=thread_h+0.1, - thread_angle=thread_angle, + flank_angle=flank_angle, twist=650, higbee=thread_h*2, anchor=TOP @@ -376,7 +377,7 @@ module pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) } up(wall) cyl(d=28.58, h=11.2+wall, anchor=BOTTOM); } - up(wall+2) thread_helix(d=25.5, pitch=2.7, thread_depth=1.6, thread_angle=15, twist=650, higbee=1.6, internal=true, anchor=BOTTOM); + up(wall+2) thread_helix(d=25.5, pitch=2.7, thread_depth=1.6, flank_angle=15, twist=650, higbee=1.6, internal=true, anchor=BOTTOM); } children(); } @@ -428,7 +429,7 @@ module generic_bottle_neck( neck_d = neck_d; supp_d = max(neck_d, support_d); thread_pitch = pitch; - thread_angle = 15; + flank_angle = 15; diamMagMult = neck_d / 26.19; heightMagMult = height / 17.00; @@ -478,7 +479,7 @@ module generic_bottle_neck( d = threadbase_d - 0.1 * diamMagMult, pitch = thread_pitch, thread_depth = thread_h + 0.1 * diamMagMult, - thread_angle = thread_angle, + flank_angle = flank_angle, twist = 360 * (height - pitch - lip_roundover_r) * .6167 / pitch, higbee = thread_h * 2, anchor = TOP @@ -527,7 +528,7 @@ function generic_bottle_neck( // thread_od = Outer diameter of the threads in mm. // tolerance = Extra space to add to the outer diameter of threads and neck in mm. Applied to radius. // neck_od = Outer diameter of neck in mm. -// thread_angle = Angle of taper on threads. +// flank_angle = Angle of taper on threads. // pitch = Thread pitch in mm. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` @@ -545,7 +546,7 @@ module generic_bottle_cap( thread_od = 28.58, tolerance = .2, neck_od = 25.5, - thread_angle = 15, + flank_angle = 15, pitch = 4, anchor = BOTTOM, spin = 0, @@ -587,7 +588,7 @@ module generic_bottle_cap( } difference(){ up(wall + pitch / 2) { - thread_helix(d = neckOuterDTol, pitch = pitch, thread_depth = threadDepth, thread_angle = thread_angle, twist = 360 * ((height - pitch) / pitch), higbee = threadDepth, internal = true, anchor = BOTTOM); + thread_helix(d = neckOuterDTol, pitch = pitch, thread_depth = threadDepth, flank_angle = flank_angle, twist = 360 * ((height - pitch) / pitch), higbee = threadDepth, internal = true, anchor = BOTTOM); } } } @@ -598,7 +599,7 @@ module generic_bottle_cap( function generic_bottle_cap( wall, texture, height, thread_od, tolerance, - neck_od, thread_angle, pitch, + neck_od, flank_angle, pitch, anchor, spin, orient ) = no_function("generic_bottle_cap"); @@ -689,7 +690,7 @@ module bottle_adapter_neck_to_cap( thread_od = cap_thread_od, tolerance = tolerance, neck_od = cap_neck_od, - thread_angle = cap_thread_taper, + flank_angle = cap_thread_taper, orient = DOWN, pitch = cap_thread_pitch ); @@ -948,4 +949,216 @@ function bottle_adapter_neck_to_neck( +// Section: SPI Bottle Threading + + +// Module: sp_neck() +// Usage: +// sp_neck(diam, type, wall|id, [style], [bead], [anchor], [spin], [orient]) +// Description: +// Make a SPI (Society of Plastics Industry) threaded bottle neck. You must +// supply the nominal outer diameter of the threads and the thread type, one of +// 400, 410 and 415. The 400 type neck has 360 degrees of thread, the 410 +// neck has 540 degrees of thread, and the 415 neck has 720 degrees of thread. +// You can also choose between the L style thread, which is symmetric and +// the M style thread, which is an asymmetric buttress thread. You can +// specify the wall thickness (measured from the base of the threads) or +// the inner diameter, and you can specify an optional bead at the base of the threads. +// Arguments: +// diam = nominal outer diameter of threads +// type = thread type, one of 400, 410 and 415 +// wall = wall thickness +// --- +// id = inner diameter +// style = Either "L" or "M" to specify the thread style. Default: "L" +// bead = if true apply a bad to the neck. Default: false +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// Examples: +// sp_neck(48,400,2); +// sp_neck(48,400,2,bead=true); +// sp_neck(22,410,2); +// sp_neck(22,410,2,bead=true); +// sp_neck(28,415,id=20,style="M"); +// sp_neck(13,415,wall=1,style="M",bead=true); + + +// Thread specs from https://www.isbt.com/threadspecs-downloads.asp + +_sp_specs = [ + [400, //diam T I H S tpi + [[ 18, [ 17.68, 8.26, 9.42, 0.94, 8]], + [ 20, [ 19.69, 10.26, 9.42, 0.94, 8]], + [ 22, [ 21.69, 12.27, 9.42, 0.94, 8]], + [ 24, [ 23.67, 13.11, 10.16, 1.17, 8]], + [ 28, [ 27.38, 15.60, 10.16, 1.17, 6]], + [ 30, [ 28.37, 16.59, 10.24, 1.17, 6]], + [ 33, [ 31.83, 20.09, 10.24, 1.17, 6]], + [ 35, [ 34.34, 22.23, 10.24, 1.17, 6]], + [ 38, [ 37.19, 25.07, 10.24, 1.17, 6]], + [ 40, [ 39.75, 27.71, 10.24, 1.17, 6]], + [ 43, [ 41.63, 29.59, 10.24, 1.17, 6]], + [ 45, [ 43.82, 31.78, 10.24, 1.17, 6]], + [ 48, [ 47.12, 35.08, 10.24, 1.17, 6]], + [ 51, [ 49.56, 37.57, 10.36, 1.17, 6]], + [ 53, [ 52.07, 40.08, 10.36, 1.17, 6]], + [ 58, [ 56.06, 44.07, 10.36, 1.17, 6]], + [ 60, [ 59.06, 47.07, 10.36, 1.17, 6]], + [ 63, [ 62.08, 50.09, 10.36, 1.17, 6]], + [ 66, [ 65.07, 53.09, 10.36, 1.17, 6]], + [ 70, [ 69.06, 57.07, 10.36, 1.17, 6]], + [ 75, [ 73.56, 61.57, 10.36, 1.17, 6]], + [ 77, [ 76.66, 64.67, 12.37, 1.52, 6]], + [ 83, [ 82.58, 69.93, 12.37, 1.52, 5]], + [ 89, [ 88.75, 74.12, 13.59, 1.52, 5]], + [100, [ 99.57, 84.94, 15.16, 1.52, 5]], + [110, [109.58, 94.92, 15.16, 1.52, 5]], + [120, [119.56,104.93, 17.40, 1.52, 5]], + ]], + [410, //diam T I H S tpi L W + [[ 18, [ 17.68, 8.26, 13.28, 0.94, 8, 9.17, 2.13]], + [ 20, [ 19.59, 10.26, 14.07, 0.94, 8, 9.17, 2.13]], + [ 22, [ 21.69, 12.27, 14.86, 0.94, 8, 9.55, 2.13]], + [ 24, [ 23.67, 13.11, 16.41, 1.17, 8, 11.10, 2.13]], + [ 28, [ 27.38, 15.60, 17.98, 1.17, 6, 11.76, 2.39]], + ]], + [415, //diam T I H S tpi L W + [[ 13, [ 12.90, 5.54, 11.48, 0.94,12, 7.77, 1.14]], + [ 15, [ 14.61, 6.55, 14.15, 0.94,12, 8.84, 1.14]], + [ 18, [ 17.68, 8.26, 15.67, 0.94, 8, 10.90, 2.13]], + [ 20, [ 19.69, 10.26, 18.85, 0.94, 8, 11.58, 2.13]], + [ 22, [ 21.69, 12.27, 21.26, 0.94, 8, 13.87, 2.13]], + [ 24, [ 23.67, 13.11, 24.31, 1.17, 8, 14.25, 2.13]], + [ 28, [ 27.38, 15.60, 27.48, 1.17, 6, 16.64, 2.39]], + [ 33, [ 31.83, 20.09, 32.36, 1.17, 6, 19.61, 2.39]], + ]] +]; + +_sp_twist = [ [400, 360], + [410, 540], + [415, 720] + ]; + + +// profile data: tpi, total width, depth, +_sp_thread_width= [ + [5, 3.05], + [6, 2.39], + [8, 2.13], + [12, 1.14], // But note style M is different + ]; + + +function _sp_thread_profile(tpi, a, S, style) = + let( + pitch = 1/tpi*INCH, + cL = a*(1-1/sqrt(3)), + cM = (1-tan(10))*a/2, + // SP specified roundings for the thread profile have special case for tpi=12 + roundings = style=="L" && tpi < 12 ? 0.5 + : style=="M" && tpi < 12 ? [0.25, 0.25, 0.75, 0.75] + : style=="L" ? [0.38, 0.13, 0.13, 0.38] + : /* style=="M" */ [0.25, 0.25, 0.2, 0.5], + path = style=="L" + ? round_corners([[-1/2*pitch,-a/2], + [-a/2,-a/2], + [-cL/2,0], + [cL/2,0], + [a/2,-a/2], + [1/2*pitch,-a/2]], radius=roundings, closed=false,$fn=24) + : round_corners( + [[-1/2*pitch,-a/2], + [-a/2, -a/2], + [-cM, 0], + [0,0], + [a/2,-a/2], + [1/2*pitch,-a/2]], radius=roundings, closed=false, $fn=24) + ) + // Shift so that the profile is S mm from the right end to create proper length S top gap + select(right(-a/2+1/2-S,p=path),1,-2)/pitch; + + +function sp_neck(diam,type,wall,id,style="L",bead=false, anchor, spin, orient) = no_function("sp_neck"); +module sp_neck(diam,type,wall,id,style="L",bead=false, anchor, spin, orient) +{ + assert(num_defined([wall,id])==1, "Must define exactly one of wall and id"); + + table = struct_val(_sp_specs,type); + dum1=assert(is_def(table),"Unknown SP closure type. Type must be one of 400, 410, or 415"); + entry = struct_val(table, diam); + dum2=assert(is_def(entry), str("Unknown closure nominal diameter. Allowed diameters for SP",type,": ",struct_keys(table))) + assert(style=="L" || style=="M", "style must be \"L\" or \"M\""); + + T = entry[0]; + I = entry[1]; + H = entry[2]; + S = entry[3]; + tpi = entry[4]; + a = (style=="M" && tpi==12) ? 1.3 : struct_val(_sp_thread_width,tpi); + + twist = struct_val(_sp_twist, type); + + profile = _sp_thread_profile(tpi,a,S,style); + + depth = a/2; + higlen = 2*a; + higang = higlen / ((T-2*depth)*PI) * 360; + + beadmax = type==400 ? (T/2-depth)+depth*1.25 + : diam <=15 ? (T-.15)/2 : (T-.05)/2; + + W = type==400 ? a*1.5 // arbitrary decision for type 400 + : entry[6]; // specified width for 410 and 415 + + beadpts = [ + [0,-W/2], + each arc(16, points = [[T/2-depth, -W/2], + [beadmax, 0], + [T/2-depth, W/2]]), + [0,W/2] + ]; + + isect400 = [for(seg=pair(beadpts)) let(segisect = line_segment_intersection([[T/2,0],[T/2,1]] , seg)) if (is_def(segisect)) segisect.y]; + + extra_bot = type==400 && bead ? -min(subindex(beadpts,1))+max(isect400) : 0; + bead_shift = type==400 ? H+max(isect400) : entry[5]+W/2; // entry[5] is L + + attachable(anchor,spin,orient,r=bead ? beadmax : T/2, l=H+extra_bot){ + up((H+extra_bot)/2){ + difference(){ + union(){ + thread_helix(d=T-.01, profile=profile, pitch = INCH/tpi, twist=twist+2*higang, higbee=higlen, anchor=TOP); + cylinder(d=T-depth*2,l=H,anchor=TOP); + if (bead) + down(bead_shift) + rotate_extrude() + polygon(beadpts); + } + up(.5)cyl(d=is_def(id) ? id : T-a-2*wall, l=H-extra_bot+1, anchor=TOP); + } + } + children(); + } +} + + +// Function: sp_diameter() +// Usage: +// true_diam = sp_diameter(diam,type) +// Description: +// Returns the actual base diameter (root of the threads) for a SPI plastic bottle neck given the nominal diameter and type number (400, 410, 415). +function sp_diameter(diam,type) = + let( + table = struct_val(_sp_specs,type) + ) + assert(is_def(table),"Unknown SP closure type. Type must be one of 400, 410, or 415") + let( + entry = struct_val(table, diam) + ) + assert(is_def(entry), str("Unknown closure nominal diameter. Allowed diameters for SP",type,": ",struct_keys(table))) + entry[0]; + + + // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/paths.scad b/paths.scad index 31f963e..79d10ed 100644 --- a/paths.scad +++ b/paths.scad @@ -1030,8 +1030,12 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) { // Module: spiral_sweep() // Description: -// Takes a closed 2D polygon path, centered on the XY plane, and sweeps/extrudes it along a 3D spiral path. -// of a given radius, height and twist. +// Takes a closed 2D polygon path, centered on the XY plane, and sweeps/extrudes it along a 3D spiral path +// of a given radius, height and twist. The origin in the profile traces out the helix of the specified radius. +// If twist is positive the path will be right-handed; if twist is negative the path will be left-handed. +// . +// Higbee specifies tapering applied to the ends of the extrusion and is given as the linear distance +// over which to taper. // Arguments: // poly = Array of points of a polygon path, to be extruded. // h = height of the spiral to extrude along. @@ -1040,6 +1044,9 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) { // --- // d = Diameter of the spiral to extrude along. // higbee = Length to taper thread ends over. +// higbee1 = Taper length at start +// higbee2 = Taper length at end +// internal = direction to taper the threads with higbee. If true threads taper outward; if false they taper inward. Default: false // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` @@ -1047,57 +1054,67 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) { // Example: // poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]]; // spiral_sweep(poly, h=200, r=50, twist=1080, $fn=36); -module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, anchor, spin=0, orient=UP) { - poly = path3d(poly); +module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal=false, anchor, spin=0, orient=UP) { + higsample = 10; // Oversample factor for higbee tapering + dummy1=assert(is_num(twist) && twist != 0); + bounds = pointlist_bounds(poly); + yctr = (bounds[0].y+bounds[1].y)/2; + xmin = bounds[0].x; + xmax = bounds[1].x; + poly = path3d(clockwise_polygon(poly)); anchor = get_anchor(anchor,center,BOT,BOT); r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=50); r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=50); sides = segs(max(r1,r2)); - steps = ceil(sides*(twist/360)); + dir = sign(twist); + ang_step = 360/sides*dir; + anglist = [for(ang = [0:ang_step:twist-EPSILON]) ang, + twist]; higbee1 = first_defined([higbee1, higbee, 0]); higbee2 = first_defined([higbee2, higbee, 0]); higang1 = 360 * higbee1 / (2 * r1 * PI); higang2 = 360 * higbee2 / (2 * r2 * PI); - higsteps1 = ceil(higang1/360*sides); - higsteps2 = ceil(higang2/360*sides); - assert(higang1 < twist/2); - assert(higang2 < twist/2); - - function higsize(a) = lookup(a,[ - [-0.001, 0], - for (x=[0.125:0.125:1]) [ x*higang1, pow(x,1/2)], - for (x=[0.125:0.125:1]) [twist-x*higang2, pow(x,1/2)], - [twist+0.001, 0] - ]); - - us = [ - for (i=[0:higsteps1/10:higsteps1]) i, - for (i=[higsteps1+1:1:steps-higsteps2-1]) i, - for (i=[steps-higsteps2:higsteps2/10:steps]) i, - ]; - zang = atan2(r2-r1,h); + dummy2=assert(higbee1>=0 && higbee2>=0) + assert(higang1 < dir*twist/2,"Higbee1 is more than half the threads") + assert(higang2 < dir*twist/2,"Higbee2 is more than half the threads"); + function polygon_r(N,theta) = + let( alpha = 360/N ) + cos(alpha/2)/(cos(posmod(theta,alpha)-alpha/2)); + higofs = pow(0.05,2); // Smallest hig scale is the square root of this value + function taperfunc(x) = sqrt((1-higofs)*x+higofs); + interp_ang = [ + for(i=idx(anglist,e=-2)) + each lerpn(anglist[i],anglist[i+1], + (higang1>0 && higang1>dir*anglist[i+1] + || (higang2>0 && higang2>dir*(twist-anglist[i]))) ? ceil((anglist[i+1]-anglist[i])/ang_step*higsample) + : 1, + endpoint=false), + last(anglist) + ]; + skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h)); points = [ - for (p = us) let ( - u = p / steps, - a = twist * u, - hsc = higsize(a), + for (a = interp_ang) let ( + hsc = dir*a<higang1 ? taperfunc(dir*a/higang1) + : dir*(twist-a)<higang2 ? taperfunc(dir*(twist-a)/higang2) + : 1, + u = a/twist, r = lerp(r1,r2,u), - mat = affine3d_zrot(a) * - affine3d_translate([r, 0, h * (u-0.5)]) * - affine3d_xrot(90) * - affine3d_skew_xz(xa=zang) * - affine3d_scale([hsc,lerp(hsc,1,0.25),1]), + mat = affine3d_zrot(a) + * affine3d_translate([polygon_r(sides,a)*r, 0, h * (u-0.5)]) + * affine3d_xrot(90) + * skewmat + * scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]), pts = apply(mat, poly) ) pts ]; vnf = vnf_vertex_array( - points, col_wrap=true, caps=true, reverse=true, - style=(abs(higbee1)+abs(higbee2))>0? "quincunx" : "alt" + points, col_wrap=true, caps=true, reverse=dir>0?true:false, + style=higbee1>0 || higbee2>0 ? "quincunx" : "alt" ); attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { - vnf_polyhedron(vnf, convexity=2*twist/360); + vnf_polyhedron(vnf, convexity=ceil(2*dir*twist/360)); children(); } } diff --git a/screws.scad b/screws.scad index b66674d..9a0e9a7 100644 --- a/screws.scad +++ b/screws.scad @@ -837,18 +837,18 @@ module screw_head(screw_info,details=false) { // anchor = anchor relative to the shaft of the screw // anchor_head = anchor relative to the screw head // Example(Med): Selected UTS (English) screws -// $fn=32; -// xdistribute(spacing=8){ -// screw("#6", length=12); -// screw("#6-32", head="button", drive="torx",length=12); -// screw("#6-32,3/4", head="hex"); -// screw("#6", thread="fine", head="fillister",length=12, drive="phillips"); -// screw("#6", head="flat small",length=12,drive="slot"); -// screw("#6-32", head="flat large", length=12, drive="torx"); -// screw("#6-32", head="flat undercut",length=12); -// screw("#6-24", head="socket",length=12); // Non-standard threading -// screw("#6-32", drive="hex", drive_size=1.5, length=12); -// } + $fn=32; + xdistribute(spacing=8){ + screw("#6", length=12); + screw("#6-32", head="button", drive="torx",length=12); + screw("#6-32,3/4", head="hex"); + screw("#6", thread="fine", head="fillister",length=12, drive="phillips"); + screw("#6", head="flat small",length=12,drive="slot"); + screw("#6-32", head="flat large", length=12, drive="torx"); + screw("#6-32", head="flat undercut",length=12); + screw("#6-24", head="socket",length=12); // Non-standard threading + screw("#6-32", drive="hex", drive_size=1.5, length=12); + } // Example(Med): A few examples of ISO (metric) screws // $fn=32; // xdistribute(spacing=8){ @@ -1243,38 +1243,30 @@ function thread_specification(screw_spec, internal=false, tolerance=undef) = function _thread_profile(thread) = let( pitch = struct_val(thread,"pitch"), - basicrad = struct_val(thread,"basic")/2, meanpitchrad = mean(struct_val(thread,"d_pitch"))/2, meanminorrad = mean(struct_val(thread,"d_minor"))/2, meanmajorrad = mean(struct_val(thread,"d_major"))/2, - depth = (meanmajorrad-meanminorrad)/pitch, - crestwidth = (pitch/2 - 2*(meanmajorrad-meanpitchrad)/sqrt(3))/pitch - + depth = meanmajorrad-meanminorrad, + crestwidth = pitch/2 - 2*(meanmajorrad-meanpitchrad)/sqrt(3) ) + [ + [-depth/sqrt(3)-crestwidth/2, -depth], + [ -crestwidth/2, 0], + [ crestwidth/2, 0], + [ depth/sqrt(3)+crestwidth/2, -depth] + ]/pitch; + + +/* Old non-centered profile + [ [-1/2,-depth], [depth/sqrt(3)-1/2,0], [depth/sqrt(3)+crestwidth-1/2, 0], [crestwidth + 2*depth/sqrt(3)-1/2,-depth] - ]; - -function _thread_profile_e(thread) = - let( - pitch = struct_val(thread,"pitch"), - basicrad = struct_val(thread,"basic")/2, - meanpitchrad = mean(struct_val(thread,"d_pitch"))/2, - meanminorrad = mean(struct_val(thread,"d_minor"))/2, - meanmajorrad = mean(struct_val(thread,"d_major"))/2, - depth = (meanmajorrad-meanminorrad)/pitch, - crestwidth = (pitch/2 - 2*(meanmajorrad-meanpitchrad)/sqrt(3))/pitch - - ) - [ - [-1/2,-1], // -1 instead of -depth? - [depth/sqrt(3)-1/2,0], - [depth/sqrt(3)+crestwidth-1/2, 0], - [crestwidth + 2*depth/sqrt(3)-1/2,-1] - ]; + ] + ; +*/ module _rod(spec, length, tolerance, orient=UP, spin=0, anchor=CENTER) @@ -1283,11 +1275,19 @@ module _rod(spec, length, tolerance, orient=UP, spin=0, anchor=CENTER) echo(d_major_mean = mean(struct_val(threadspec, "d_major"))); echo(bolt_profile=_thread_profile(threadspec)); - trapezoidal_threaded_rod( d=mean(struct_val(threadspec, "d_major")), + threaded_rod([mean(struct_val(threadspec, "d_minor")), + mean(struct_val(threadspec, "d_pitch")), + mean(struct_val(threadspec, "d_major"))], + pitch = struct_val(threadspec, "pitch"), + l=length, left_handed=false, + bevel=false, orient=orient, anchor=anchor, spin=spin); + /* + generic_threaded_rod( d=mean(struct_val(threadspec, "d_major")), l=length, pitch = struct_val(threadspec, "pitch"), profile = _thread_profile(threadspec),left_handed=false, bevel=false, orient=orient, anchor=anchor, spin=spin); + */ } @@ -1352,7 +1352,7 @@ module nut(name, diameter, thickness, thread="coarse", oversize=0, spec, toleran threadspec = thread_specification(spec, internal=true, tolerance=tolerance); echo(threadspec=threadspec,"for nut threads"); echo(nut_minor_diam = mean(struct_val(threadspec,"d_minor"))); - trapezoidal_threaded_nut( + generic_threaded_nut( od=diameter, id=mean(struct_val(threadspec, "d_major")), h=thickness, pitch=struct_val(threadspec, "pitch"), profile=_thread_profile(threadspec), diff --git a/threading.scad b/threading.scad index 8ad15ba..9856717 100644 --- a/threading.scad +++ b/threading.scad @@ -7,18 +7,1121 @@ ////////////////////////////////////////////////////////////////////// +// Section: Standard (UTS/ISO) Threading + +// Module: threaded_rod() +// Description: +// Constructs a standard ISO (metric) or UTS (English) threaded rod. These threads are close to triangular, +// with a 60 degree thread angle. You can give the outer diameter and get the "basic form" or you can +// set d to a triplet [d_min, d_pitch, d_major] where are parameters determined by the ISO and UTS specifications +// that define clearance sizing for the threading. See screws.scad for how to make screws +// using the specification parameters. +// Arguments: +// d = Outer diameter of threaded rod, or a triplet of [d_min, d_pitch, d_major]. +// l = length of threaded rod. +// pitch = Length between threads. +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// internal = If true, make this a mask for making internal threads. +// d1 = Bottom outside diameter of threads. +// d2 = Top outside diameter of threads. +// higbee = Length to taper thread ends over. Default: 0 +// higbee1 = Length to taper bottom thread end over. +// higbee2 = Length to taper top thread end over. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Example(2D): +// projection(cut=true) +// threaded_rod(d=10, l=15, pitch=1.5, orient=BACK); +// Examples(Med): +// threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1); +// threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1); +module threaded_rod( + d, l, pitch, + left_handed=false, + bevel,bevel1,bevel2, + internal=false, + d1, d2, + higbee, higbee1, higbee2, + anchor, spin, orient +) { + dummy1= + assert(all_positive(pitch)) + assert(all_positive(d)) + assert(all_positive(l)); + basic = is_num(d) || is_undef(d) || is_def(d1) || is_def(d2); + dummy2 = assert(basic || is_vector(d,3)); + depth = basic ? cos(30) * 5/8 + : (d[2] - d[0])/2/pitch; + crestwidth = basic ? 1/8 : 1/2 - (d[2]-d[1])/sqrt(3)/pitch; + profile = [ + [-depth/sqrt(3)-crestwidth/2, -depth], + [ -crestwidth/2, 0], + [ crestwidth/2, 0], + [ depth/sqrt(3)+crestwidth/2, -depth] + ]; + oprofile = internal? [ + [-6/16, -depth], + [-1/16, 0], + [-1/32, 0.02], + [ 1/32, 0.02], + [ 1/16, 0], + [ 6/16, -depth] + ] : [ + [-7/16, -depth*1.07], + [-6/16, -depth], + [-1/16, 0], + [ 1/16, 0], + [ 6/16, -depth], + [ 7/16, -depth*1.07] + ]; + generic_threaded_rod( + d=basic ? d : d[2], d1=d1, d2=d2, l=l, + pitch=pitch, + profile=profile, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + internal=internal, + higbee=higbee, + higbee1=higbee1, + higbee2=higbee2, + anchor=anchor, + spin=spin, + orient=orient + ) children(); +} + + + +// Module: threaded_nut() +// Description: +// Constructs a hex nut for an ISO (metric) or UTS (English) threaded rod. +// Arguments: +// od = diameter of the nut. +// id = diameter of threaded rod to screw onto. +// h = height/thickness of nut. +// pitch = Length between threads. +// --- +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Examples(Med): +// threaded_nut(od=16, id=8, h=8, pitch=1.25, $slop=0.05, $fa=1, $fs=1); +// threaded_nut(od=16, id=8, h=8, pitch=1.25, left_handed=true, bevel=true, $slop=0.1, $fa=1, $fs=1); +module threaded_nut( + od, id, h, + pitch, left_handed=false, bevel, bevel1, bevel2, + anchor, spin, orient +) { + depth = pitch * cos(30) * 5/8; + profile = [ + [-6/16, -depth/pitch], + [-1/16, 0], + [-1/32, 0.02], + [ 1/32, 0.02], + [ 1/16, 0], + [ 6/16, -depth/pitch] + ]; + generic_threaded_nut( + od=od, id=id, h=h, + pitch=pitch, + profile=profile, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + anchor=anchor, spin=spin, + orient=orient + ) children(); +} + +// Section: Trapezoidal Threading + + +// Module: trapezoidal_threaded_rod() +// Description: +// Constructs a threaded rod with a symmetric trapezoidal thread. Trapezoidal threads are used for lead screws because +// they are one of the strongest symmetric profiles. This tooth shape is stronger than a similarly +// sized square thread becuase of its wider base. However, it does place a radial load on the nut, unlike the square thread. +// For loads in only one direction the asymmetric buttress thread profile can bear greater loads. +// . +// By default produces the nominal dimensions +// for metric trapezoidal threads: a thread angle of 30 degrees and a depth set to half the pitch. +// You can also specify your own trapezoid parameters. For ACME threads see acme_threaded_rod(). +// Figure(2D,Med): +// pa_delta = tan(15)/4; +// rr1 = -1/2; +// z1 = 1/4-pa_delta; +// z2 = 1/4+pa_delta; +// profile = [ +// [-z2, rr1], +// [-z1, 0], +// [ z1, 0], +// [ z2, rr1], +// ]; +// fullprofile = 50*left(1/2,p=concat(profile, right(1, p=profile))); +// stroke(fullprofile,width=1); +// dir = fullprofile[2]-fullprofile[3]; +// dir2 = fullprofile[5]-fullprofile[4]; +// curve = arc(32,angle=[75,105],r=67.5); +// avgpt = mean([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2]); +// color("red"){ +// stroke([fullprofile[2]+.1*dir, fullprofile[2]+.4*dir], width=1); +// stroke([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2], width=1); +// stroke(move(-curve[0]+avgpt,p=curve), width=1,endcaps="arrow2"); +// back(10)text("thread",size=4,halign="center"); +// back(3)text("angle",size=4,halign="center"); +// } +// Arguments: +// d = Outer diameter of threaded rod. +// l = Length of threaded rod. +// pitch = Thread spacing. +// thread_angle = Angle between two thread faces. Default: 30 +// thread_depth = Depth of threads. Default: pitch/2 +// --- +// left_handed = If true, create left-handed threads. Default: false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// starts = The number of lead starts. Default: 1 +// internal = If true, make this a mask for making internal threads. Default: false +// d1 = Bottom outside diameter of threads. +// d2 = Top outside diameter of threads. +// higbee = Length to taper thread ends over. Default: 0 (No higbee thread tapering) +// higbee1 = Length to taper bottom thread end over. +// higbee2 = Length to taper top thread end over. +// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Example(2D): +// projection(cut=true) +// trapezoidal_threaded_rod(d=10, l=15, pitch=2, orient=BACK); +// Examples(Med): +// trapezoidal_threaded_rod(d=10, l=40, pitch=2, $fn=32); // Standard metric threading +// trapezoidal_threaded_rod(d=10, l=17, pitch=2, higbee=25, $fn=32); // Standard metric threading +// trapezoidal_threaded_rod(d=10, l=17, pitch=2, bevel=true, $fn=32); // Standard metric threading +// trapezoidal_threaded_rod(d=10, l=30, pitch=2, left_handed=true, $fa=1, $fs=1); // Standard metric threading +// trapezoidal_threaded_rod(d=10, l=40, pitch=3, left_handed=true, starts=3, $fn=36); +// trapezoidal_threaded_rod(l=25, d=10, pitch=2, starts=3, $fa=1, $fs=1, orient=RIGHT, anchor=BOTTOM); +// trapezoidal_threaded_rod(d=60, l=16, pitch=8, thread_depth=3, thread_angle=90, left_handed=true, $fa=2, $fs=2); +// trapezoidal_threaded_rod(d=60, l=16, pitch=8, thread_depth=3, thread_angle=90, left_handed=true, starts=4, $fa=2, $fs=2); +// trapezoidal_threaded_rod(d=16, l=40, pitch=2, thread_angle=60); +// trapezoidal_threaded_rod(d=25, l=40, pitch=10, thread_depth=8/3, thread_angle=100, starts=4, center=false, $fa=2, $fs=2); +// trapezoidal_threaded_rod(d=50, l=35, pitch=8, thread_angle=60, starts=11, higbee=10,$fn=120); +// Example(Med): Using as a Mask to Make Internal Threads +// bottom_half() difference() { +// cube(50, center=true); +// trapezoidal_threaded_rod(d=40, l=51, pitch=5, thread_angle=30, internal=true, orient=RIGHT, $fn=36); +// } +module trapezoidal_threaded_rod( + d, l, pitch, + thread_angle=30, + thread_depth=undef, + left_handed=false, + bevel,bevel1,bevel2, + starts=1, + internal=false, + higbee, higbee1, higbee2,d1,d2, + center, anchor, spin, orient +) { + dummy0 = assert(all_positive(pitch)); + dummy1 = assert(thread_angle>=0 && thread_angle<180); + depth = first_defined([thread_depth, pitch/2]); + pa_delta = 0.5*depth*tan(thread_angle/2) / pitch; + dummy2 = assert(pa_delta<1/4, "Specified thread geometry is impossible"); + rr1 = -depth/pitch; + z1 = 1/4-pa_delta; + z2 = 1/4+pa_delta; + profile = [ + [-z2, rr1], + [-z1, 0], + [ z1, 0], + [ z2, rr1], + ]; + generic_threaded_rod(d=d,l=l,pitch=pitch,profile=profile, + left_handed=left_handed,bevel=bevel,bevel1=bevel1,bevel2=bevel2,starts=starts,internal=internal,d1=d1,d2=d2, + higbee=higbee,higbee1=higbee1,higbee2=higbee2,center=center,anchor=anchor,spin=spin,orient=orient) + children(); +} + + +// Module: trapezoidal_threaded_nut() +// Description: +// Constructs a hex nut for a symmetric trapzoidal threaded rod. +// By default produces the nominal dimensions +// for metric trapezoidal threads: a thread angle of 30 degrees and a depth set to half the pitch. +// You can also specify your own trapezoid parameters. For ACME threads see acme_threaded_nut(). +// Arguments: +// od = diameter of the nut. +// id = diameter of threaded rod to screw onto. +// h = height/thickness of nut. +// pitch = Thread spacing. +// thread_angle = Angle between two thread faces. Default: 30 +// thread_depth = Depth of the threads. Default: pitch/2 +// --- +// left_handed = if true, create left-handed threads. Default = false +// starts = The number of lead starts. Default = 1 +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Examples(Med): +// trapezoidal_threaded_nut(od=16, id=8, h=8, pitch=2, $slop=0.1, anchor=UP); +// trapezoidal_threaded_nut(od=16, id=8, h=8, pitch=2, bevel=true, $slop=0.05, anchor=UP); +// trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, $slop=0.1, left_handed=true); +// trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, starts=3, $fa=1, $fs=1, $slop=0.15); +module trapezoidal_threaded_nut( + od, + id, + h, + pitch, + thread_angle=30, + thread_depth, + left_handed=false, + starts=1, + bevel,bevel1,bevel2, + id1,id2, + anchor, spin, orient +) { + dummy1 = assert(pitch>0 && thread_angle>=0 && thread_angle<180); + depth = first_defined([thread_depth, pitch/2]); + pa_delta = 0.5*depth*tan(thread_angle/2) / pitch; + dummy2 = assert(pa_delta<1/4, "Specified thread geometry is impossible"); + rr1 = -depth/pitch; + z1 = 1/4-pa_delta; + z2 = 1/4+pa_delta; + profile = [ + [-z2, rr1], + [-z1, 0], + [ z1, 0], + [ z2, rr1], + ]; + generic_threaded_nut(od=od,id=id,h=h,pitch=pitch,profile=profile,id1=id1,id2=id2, + left_handed=left_handed,bevel=bevel,bevel1=bevel1,bevel2=bevel2,starts=starts, + anchor=anchor,spin=spin,orient=orient) + children(); +} + + +// Module: acme_threaded_rod() +// Description: +// Constructs an ACME trapezoidal threaded screw rod. This form has a 29 degree thread angle with a +// symmetric trapezoidal thread. +// Arguments: +// d = Outer diameter of threaded rod. +// l = length of threaded rod. +// tpi = threads per inch. +// --- +// pitch = thread spacing (alternative to tpi) +// starts = The number of lead starts. Default = 1 +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// internal = If true, this is a mask for making internal threads. +// higbee = Length to taper thread ends over. Default: 0 (No higbee thread tapering) +// higbee1 = Length to taper bottom thread end over. +// higbee2 = Length to taper top thread end over. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Example(2D): +// projection(cut=true) +// acme_threaded_rod(d=10, l=15, pitch=2, orient=BACK); +// Examples(Med): +// acme_threaded_rod(d=3/8*INCH, l=20, pitch=1/8*INCH, $fn=32); +// acme_threaded_rod(d=10, l=30, pitch=2, starts=3, $fa=1, $fs=1); +module acme_threaded_rod( + d, l, tpi, pitch, + starts=1, + left_handed=false, + bevel,bevel1,bevel2, + internal=false, + higbee, higbee1, higbee2, + anchor, spin, orient +) { + dummy = assert(num_defined([pitch,tpi])==1,"Must give exactly one of pitch and tpi"); + pitch = is_undef(pitch) ? INCH/tpi : pitch; + trapezoidal_threaded_rod( + d=d, l=l, pitch=pitch, + thread_angle=29, + thread_depth=pitch/2, + starts=starts, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + internal=internal, + anchor=anchor, + spin=spin, + orient=orient + ) children(); +} + + + +// Module: acme_threaded_nut() +// Description: +// Constructs a hex nut for an ACME threaded screw rod. +// Arguments: +// od = diameter of the nut. +// id = diameter of threaded rod to screw onto. +// h = height/thickness of nut. +// tpi = threads per inch +// --- +// pitch = Thread spacing (alternative to tpi) +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Examples(Med): +// acme_threaded_nut(od=16, id=3/8*INCH, h=8, tpi=8, $slop=0.05); +// acme_threaded_nut(od=16, id=1/2*INCH, h=10, tpi=12, starts=3, $slop=0.1, $fa=1, $fs=1); +module acme_threaded_nut( + od, id, h, tpi, pitch, + starts=1, + left_handed=false, + bevel,bevel1,bevel2, + anchor, spin, orient +) { + dummy = assert(num_defined([pitch,tpi])==1,"Must give exactly one of pitch and tpi"); + pitch = is_undef(pitch) ? INCH/tpi : pitch; + dummy2=assert(is_num(pitch) && pitch>0); + trapezoidal_threaded_nut( + od=od, id=id, h=h, pitch=pitch, + thread_depth = pitch/2, + thread_angle=29, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + starts=starts, + anchor=anchor, + spin=spin, + orient=orient + ) children(); +} + + + + +// Section: Pipe Threading + +// Module: npt_threaded_rod() +// Description: +// Constructs a standard NPT pipe end threading. If `internal=true`, creates a mask for making +// internal pipe threads. Tapers smaller upwards if `internal=false`. Tapers smaller downwards +// if `internal=true`. If `hollow=true` and `internal=false`, then the pipe threads will be +// hollowed out into a pipe with the apropriate internal diameter. +// Arguments: +// size = NPT standard pipe size in inches. 1/16", 1/8", 1/4", 3/8", 1/2", 3/4", 1", 1+1/4", 1+1/2", or 2". Default: 1/2" +// --- +// left_handed = If true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// hollow = If true, create a pipe with the correct internal diameter. +// internal = If true, make this a mask for making internal threads. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.p +// Example(2D): The straight gray rectangle reveals the tapered threads. +// projection(cut=true) npt_threaded_rod(size=1/4, orient=BACK); +// right(.533*INCH/2) color("gray") rect([2,0.5946*INCH],anchor=LEFT); +// Examples(Med): +// npt_threaded_rod(size=3/8, $fn=72); +// npt_threaded_rod(size=1/2, $fn=72, bevel=true); +// npt_threaded_rod(size=1/2, left_handed=true, $fn=72); +// npt_threaded_rod(size=3/4, hollow=true, $fn=96); +// Example: +// diff("remove"){ +// cuboid([40,40,40]) +// tags("remove"){ +// up(.01)position(TOP) +// npt_threaded_rod(size=3/4, $fn=96, internal=true, $slop=0.1, anchor=TOP); +// cyl(d=3/4*INCH, l=42, $fn=32); +// } +// } +module npt_threaded_rod( + size=1/2, + left_handed=false, + bevel,bevel1,bevel2, + hollow=false, + internal=false, + anchor, spin, orient +) { + assert(is_finite(size)); + assert(is_bool(left_handed)); + assert(is_undef(bevel) || is_bool(bevel)); + assert(is_bool(hollow)); + assert(is_bool(internal)); + assert(!(internal&&hollow), "Cannot created a hollow internal threads mask."); + info_table = [ + // Size len OD TPI + [ 1/16, [ 0.3896, 0.308, 27 ]], + [ 1/8, [ 0.3924, 0.401, 27 ]], + [ 1/4, [ 0.5946, 0.533, 18 ]], + [ 3/8, [ 0.6006, 0.668, 18 ]], + [ 1/2, [ 0.7815, 0.832, 14 ]], + [ 3/4, [ 0.7935, 1.043, 14 ]], + [ 1, [ 0.9845, 1.305, 11.5]], + [ 1+1/4, [ 1.0085, 1.649, 11.5]], + [ 1+1/2, [ 1.0252, 1.888, 11.5]], + [ 2, [ 1.0582, 2.362, 11.5]], + ]; + info = [for (data=info_table) if(approx(size,data[0])) data[1]][0]; + dummy1 = assert(is_def(info), "Unsupported NPT size. Try one of 1/16, 1/8, 1/4, 3/8, 1/2, 3/4, 1, 1+1/4, 1+1/2, 2"); + l = INCH * info[0]; + d = INCH * info[1]; + pitch = INCH / info[2]; + rr = d/2; + rr2 = rr - l/32; + r1 = internal? rr2 : rr; + r2 = internal? rr : rr2; + depth = pitch * cos(30) * 5/8; + profile = internal? [ + [-6/16, -depth/pitch], + [-1/16, 0], + [-1/32, 0.02], + [ 1/32, 0.02], + [ 1/16, 0], + [ 6/16, -depth/pitch] + ] : [ + [-7/16, -depth/pitch*1.07], + [-6/16, -depth/pitch], + [-1/16, 0], + [ 1/16, 0], + [ 6/16, -depth/pitch], + [ 7/16, -depth/pitch*1.07] + ]; + attachable(anchor,spin,orient, l=l, r1=r1, r2=r2) { + difference() { + generic_threaded_rod( + d1=2*r1, d2=2*r2, l=l, + pitch=pitch, + profile=profile, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + internal=internal, + higbee=r1*PI/2 + ); + if (hollow) cylinder(l=l+1, d=size*INCH, center=true); + else nil(); + } + children(); + } +} + + + +// Section: Buttress Threading + +// Module: buttress_threaded_rod() +// Description: +// Constructs a simple buttress threaded rod with a 45 degree angle. The buttress thread or sawtooth thread has low friction and high loading +// in one direction at the cost of higher friction and inferior loading in the other direction. Buttress threads are sometimes used on +// vises, which are loaded only in one direction. +// Arguments: +// d = Outer diameter of threaded rod. +// l = length of threaded rod. +// pitch = Thread spacing. +// --- +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// internal = If true, this is a mask for making internal threads. +// higbee = Length to taper thread ends over. Default: 0 +// higbee1 = Length to taper bottom thread end over. +// higbee2 = Length to taper top thread end over. +// d1 = Bottom outside diameter of threads. +// d2 = Top outside diameter of threads. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Example(2D): +// projection(cut=true) +// buttress_threaded_rod(d=10, l=15, pitch=2, orient=BACK); +// Examples(Med): +// buttress_threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1); +// buttress_threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1); +module buttress_threaded_rod( + d=10, l=100, pitch=2, + left_handed=false, + bevel,bevel1,bevel2, + internal=false, + higbee=0, + higbee1, + higbee2, + d1,d2, + anchor, spin, orient +) { + depth = pitch * 3/4; + profile = [ + [ -7/16, -0.75], + [ 5/16, 0], + [ 7/16, 0], + [ 7/16, -0.75], + [ 1/ 2, -0.77], + ]; + generic_threaded_rod( + d=d, l=l, pitch=pitch, + profile=profile, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + internal=internal, + higbee=higbee, + higbee1=higbee1, + higbee2=higbee2, + d1=d1,d2=d2, + anchor=anchor, + spin=spin, + orient=orient + ) children(); +} + + + +// Module: buttress_threaded_nut() +// Description: +// Constructs a hex nut for a simple buttress threaded screw rod. +// Arguments: +// od = diameter of the nut. +// id = diameter of threaded rod to screw onto. +// h = height/thickness of nut. +// pitch = Thread spacing. +// --- +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Examples(Med): +// buttress_threaded_nut(od=16, id=8, h=8, pitch=1.25, left_handed=true, $slop=0.05, $fa=1, $fs=1); +module buttress_threaded_nut( + od=16, id=10, h=10, + pitch=2, left_handed=false, + bevel,bevel1,bevel2, + anchor, spin, orient +) { + depth = pitch * 3/4; + profile = [ + [ -7/16, -0.75], + [ 5/16, 0], + [ 7/16, 0], + [ 7/16, -0.75], + [ 1/ 2, -0.77], + ]; + generic_threaded_nut( + od=od, id=id, h=h, + pitch=pitch, + profile=profile, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + anchor=anchor, spin=spin, + orient=orient + ) children(); +} + + + +// Section: Square Threading + +// Module: square_threaded_rod() +// Description: +// Constructs a square profile threaded screw rod. The greatest advantage of square threads is that they have the least friction and a much higher intrinsic efficiency than trapezoidal threads. +// They produce no radial load on the nut. However, square threads cannot carry as much load as trapezoidal threads. +// Arguments: +// d = Outer diameter of threaded rod. +// l = length of threaded rod. +// pitch = Thread spacing. +// --- +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// starts = The number of lead starts. Default = 1 +// internal = If true, this is a mask for making internal threads. +// higbee = Length to taper thread ends over. Default: 0 +// higbee1 = Length to taper bottom thread end over. +// higbee2 = Length to taper top thread end over. +// d1 = Bottom outside diameter of threads. +// d2 = Top outside diameter of threads. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Example(2D): +// projection(cut=true) +// square_threaded_rod(d=10, l=15, pitch=2, orient=BACK); +// Examples(Med): +// square_threaded_rod(d=10, l=20, pitch=2, starts=2, $fn=32); +module square_threaded_rod( + d, l, pitch, + left_handed=false, + bevel,bevel1,bevel2, + starts=1, + internal=false, + higbee=0, higbee1, higbee2, + d1,d2, + anchor, spin, orient +) { + trapezoidal_threaded_rod( + d=d, l=l, pitch=pitch, + thread_angle=0.1, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + starts=starts, + internal=internal, + higbee=higbee, + higbee1=higbee1, + higbee2=higbee2, + d1=d1, + d2=d2, + anchor=anchor, + spin=spin, + orient=orient + ) children(); +} + + + +// Module: square_threaded_nut() +// Description: +// Constructs a hex nut for a square profile threaded screw rod. +// Arguments: +// od = diameter of the nut. +// id = diameter of threaded rod to screw onto. +// h = height/thickness of nut. +// pitch = Length between threads. +// --- +// left_handed = if true, create left-handed threads. Default = false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// starts = The number of lead starts. Default = 1 +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Examples(Med): +// square_threaded_nut(od=16, id=10, h=10, pitch=2, starts=2, $slop=0.1, $fn=32); +module square_threaded_nut( + od, id, h, + pitch, + left_handed=false, + bevel,bevel1,bevel2, + starts=1, + anchor, spin, orient +) { + assert(is_num(pitch) && pitch>0) + trapezoidal_threaded_nut( + od=od, id=id, h=h, pitch=pitch, + thread_angle=0, + left_handed=left_handed, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + starts=starts, + anchor=anchor, + spin=spin, + orient=orient + ) children(); +} + + +// Section: Ball Screws + +// Module: ball_screw_rod() +// Description: +// Constructs a ball screw rod. This type of rod is used with ball bearings. +// Arguments: +// d = Outer diameter of threaded rod. +// l = length of threaded rod. +// pitch = Thread spacing. Also, the diameter of the ball bearings used. +// ball_diam = The diameter of the ball bearings to use with this ball screw. +// ball_arc = The arc portion that should touch the ball bearings. Default: 120 degrees. +// --- +// left_handed = if true, create left-handed threads. Default = false +// starts = The number of lead starts. Default = 1 +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// internal = If true, make this a mask for making internal threads. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Example(2D): Thread Profile, ball_diam=4, ball_arc=100 +// projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=4, ball_arc=100, orient=BACK); +// Example(2D): Thread Profile, ball_diam=4, ball_arc=120 +// projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=4, ball_arc=120, orient=BACK); +// Example(2D): Thread Profile, ball_diam=3, ball_arc=120 +// projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=3, ball_arc=120, orient=BACK); +// Examples(Med): +// ball_screw_rod(d=15, l=20, pitch=8, ball_diam=5, ball_arc=120, $fa=1, $fs=1); +// ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, $fa=1, $fs=1); +// ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, left_handed=true, $fa=1, $fs=1); +module ball_screw_rod( + d, l, pitch, + ball_diam=5, ball_arc=100, + starts=1, + left_handed=false, + internal=false, + bevel,bevel1,bevel2, + anchor, spin, orient +) { + n = ceil(segs(ball_diam/2)*ball_arc/2/360); + depth = ball_diam * (1-cos(ball_arc/2))/2; + cpy = ball_diam/2/pitch*cos(ball_arc/2); + profile = [ + each arc(N=n, d=ball_diam/pitch, cp=[-0.5,cpy], start=270, angle=ball_arc/2), + each arc(N=n, d=ball_diam/pitch, cp=[+0.5,cpy], start=270-ball_arc/2, angle=ball_arc/2) + ]; + generic_threaded_rod( + d=d, l=l, pitch=pitch, + profile=profile, + left_handed=left_handed, + starts=starts, + bevel=bevel,bevel1=bevel1,bevel2=bevel2, + internal=internal, + higbee=0, + anchor=anchor, + spin=spin, + orient=orient + ) children(); +} + + + // Section: Generic Threading +// Module: generic_threaded_rod() +// Description: +// Constructs a generic threaded rod using an arbitrary thread profile that you supply. The rod can be tapered (e.g. for pipe threads). +// For specific thread types use other modules that supply the appropriate profile. +// . +// You give the profile as a 2D path that will be scaled by the pitch to produce the final thread shape. The profile X values +// must be between -1/2 and 1/2. The Y=0 point will align with the specified rod diameter, so generally you want a Y value of zero at the peak (which +// makes your specified diameter the outer diameter of the threads). +// The value in the valleys of the thread should then be `-depth/pitch` due to the scaling by the thread pitch. The segment between the end +// of one thread and the start of the next is added automatically, so you should not have the path start and end at equivalent points (X = ±1/2 with the same Y value). +// Generally you should center the profile horizontally in the interval [-1/2, 1/2]. +// . +// If internal is true then produce a thread mask to difference from an object. +// When internal is true the rod diameter is enlarged to correct for the polygonal nature of circles to ensure that the internal diameter is the specified size. +// The diameter is also increased by `4 * $slop` to create clearance for threading by allowing a `2 * $slop` gap on each side. +// If bevel is set to true and internal is false then the ends of the rod will be beveled. When bevel is true and internal is true the ends of the rod will +// be filled in so that the rod mask will create a bevel when subtracted from an object. The bevel is at 45 deg and is the depth of the threads. +// . +// Higbee specifies tapering of the thread ends to make screws easier to start. Specify the number of degrees for the taper. Higbee +// only works for external threads. It is ignored if internal is true. +// Arguments: +// d = Outer diameter of threaded rod. +// l = Length of threaded rod. +// pitch = Thread spacing. +// profile = A 2D path giving the shape of a thread +// --- +// left_handed = If true, create left-handed threads. Default: false +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// starts = The number of lead starts. Default: 1 +// internal = If true, make this a mask for making internal threads. Default: false +// d1 = Bottom outside diameter of threads. +// d2 = Top outside diameter of threads. +// higbee = Angle to taper thread ends over. Default: 0 (No higbee thread tapering) +// higbee1 = Angle to taper bottom thread end over. +// higbee2 = Angle to taper top thread end over. +// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +// Example(2DMed): Example Tooth Profile +// pitch = 2; +// depth = pitch * cos(30) * 5/8; +// profile = [ +// [-7/16, -depth/pitch*1.07], +// [-6/16, -depth/pitch], +// [-1/16, 0], +// [ 1/16, 0], +// [ 6/16, -depth/pitch], +// [ 7/16, -depth/pitch*1.07] +// ]; +// stroke(profile, width=0.02); +// Example: +// pitch = 2; +// depth = pitch * cos(30) * 5/8; +// profile = [ +// [-7/16, -depth/pitch*1.07], +// [-6/16, -depth/pitch], +// [-1/16, 0], +// [ 1/16, 0], +// [ 6/16, -depth/pitch], +// [ 7/16, -depth/pitch*1.07] +// ]; +// generic_threaded_rod(d=10, l=40, pitch=2, profile=profile); +module generic_threaded_rod( + d, l, pitch, profile, + left_handed=false, + bevel, + bevel1, bevel2, + starts=1, + internal=false, + d1, d2, + higbee, higbee1, higbee2, + center, anchor, spin, orient +) { + dummy0 = + assert(all_positive(pitch)) + assert(all_positive(l)) + assert(is_path(profile)) + assert(is_bool(left_handed)); + bevel1 = first_defined([bevel1,bevel,false]); + bevel2 = first_defined([bevel2,bevel,false]); + r1 = get_radius(d1=d1, d=d); + r2 = get_radius(d1=d2, d=d); + sides = quantup(segs(max(r1,r2)), starts); + rsc = internal? (1/cos(180/sides)) : 1; + islop = internal? 2*$slop : 0; + _r1 = r1 * rsc + islop; + _r2 = r2 * rsc + islop; + threads = quantup(l/pitch+2,1); // Was quantup(1/pitch+2,2*starts); + dir = left_handed? -1 : 1; + twist = 360 * l / pitch / starts; + higang1 = first_defined([higbee1, higbee, 0]); + higang2 = first_defined([higbee2, higbee, 0]); + assert(higang1 < twist/2); + assert(higang2 < twist/2); + prof3d = path3d(profile); + pdepth = -min(subindex(profile,1)); + pmax = pitch * max(subindex(profile,1)); + rmax = max(_r1,_r2)+pmax; + depth = pdepth * pitch; + dummy1 = assert(_r1>depth && _r2>depth, "Screw profile deeper than rod radius"); + map_threads = right((_r1 + _r2) / 2) // Shift profile out to thread radius + * affine3d_skew(sxz=(_r2-_r1)/l) // Skew correction for tapered threads + * affine3d_frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis + * scale(pitch); // scale profile by pitch + hig_table = [ + [-twist/2-0.0001, 0], + [-twist/2+higang1, 1], + [ twist/2-higang2, 1], + [ twist/2+0.0001, 0], + ]; + start_steps = sides / starts; + thread_verts = [ + // Outer loop constructs a vertical column of the screw at each angle + // covering 1/starts * 360 degrees of the cylinder. + for (step = [0:1:start_steps]) let( + ang = 360 * step/sides, + dz = step / start_steps, // z offset for threads at this angle + rot_prof = zrot(ang*dir)*map_threads, // Rotate profile to correct angular location + full_profile = [ // profile for the entire rod + for (thread = [-threads/2:1:threads/2-1]) let( + tang = (thread/starts) * 360 + ang, + hsc = internal? 1 + : (higang1==0 && tang<=0)? 1 + : (higang2==0 && tang>=0)? 1 + : lookup(tang, hig_table), + higscale = yscale(hsc,cp = -pdepth) // Scale for higbee + ) + // The right movement finds the position of the thread along + // what will be the z axis after the profile is mapped to 3d + each apply(right(dz + thread) * higscale, prof3d) + ] + ) [ + [0, 0, -l/2-pitch], + each apply(rot_prof , full_profile), + [0, 0, +l/2+pitch] + ] + ]; + + style = higang1>0 || higang2>0 ? "quincunx" : "min_edge"; + + thread_vnfs = vnf_merge([ + // Main thread faces + for (i=[0:1:starts-1]) + zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style=style)), + // Top closing face(s) of thread + for (i=[0:1:starts-1]) let( + rmat = zrot(i*360/starts), + pts = deduplicate(list_head(thread_verts[0], len(prof3d)+1)), + faces = [for (i=idx(pts,e=-2)) left_handed ? [0, i, i+1] : [0, i+1, i]] + ) [apply(rmat,pts), faces], + // Bottom closing face(s) of thread + for (i=[0:1:starts-1]) let( + rmat = zrot(i*360/starts), + pts = deduplicate(list_tail(last(thread_verts), -len(prof3d)-2)), + faces = [for (i=idx(pts,e=-2)) left_handed ? [len(pts)-1, i+1, i] : [len(pts)-1, i, i+1]] + ) [apply(rmat,pts), faces] + ]); + + slope = (_r1-_r2)/l; + maxlen = 2*pitch; + + anchor = get_anchor(anchor, center, BOT, CENTER); + attachable(anchor,spin,orient, r1=_r1, r2=_r2, l=l) { + union(){ + + // This method is faster but more complex code and it produces green tops + difference() { + vnf_polyhedron(vnf_quantize(thread_vnfs),convexity=10); + + if (!internal){ + if (bevel1 || bevel2) + rotate_extrude(){ + if (bevel2) polygon([[ 0, l/2], + [_r2+pmax-depth, l/2], + [_r2+pmax+slope*depth, l/2-depth], + [ rmax+1, l/2-depth], + [rmax+1, l/2+maxlen], + [ 0, l/2+maxlen]]); + if (bevel1) polygon([[ 0,-l/2], + [_r1+pmax-depth, -l/2], + [_r1+pmax-slope*depth, -l/2+depth], + [ rmax+1, -l/2+depth], + [rmax+1, -l/2-maxlen], + [ 0, -l/2-maxlen]]); + } + } + if (!bevel1 || internal) + down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP); + if (!bevel2 || internal) + up(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=BOTTOM); + } + +/* intersection(){ + //vnf_validate(vnf_quantize(thread_vnfs), size=0.1); + vnf_polyhedron(vnf_quantize(thread_vnfs), convexity=10); + cyl(l=l, r1=_r1+pmax, r2=_r2+pmax, chamfer1=bevel1?depth:undef, chamfer2=bevel2?depth:undef); + }*/ + + // Add bevel for internal thread mask + if (internal) { + if (bevel1) + down(l/2+.001)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM); + if (bevel2) + up(l/2+.001)cyl(l=depth, r2=_r2+pmax, r1=_r2+pmax+slope*depth-depth,anchor=TOP); + } + } + children(); + } +} + + + +// Module: generic_threaded_nut() +// Description: +// Constructs a hexagonal nut for an generic threaded rod using a user-supplied thread profile. +// See generic_threaded_rod for details on the profile specification. +// Arguments: +// od = diameter of the nut. +// id = diameter of threaded rod to screw onto. +// h = height/thickness of nut. +// pitch = Thread spacing. +// profile = Thread profile. +// --- +// left_handed = if true, create left-handed threads. Default = false +// starts = The number of lead starts. Default = 1 +// bevel = if true, bevel the thread ends. Default: false +// bevel1 = if true bevel the bottom end. +// bevel2 = if true bevel the top end. +// id1 = inner diameter at the bottom +// id2 = inner diameter at the top +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads. +module generic_threaded_nut( + od, + id, + h, + pitch, + profile, + left_handed=false, + starts=1, + bevel,bevel1,bevel2, + id1,id2, + anchor, spin, orient +) { + extra = 0.01; + id1 = first_defined([id1,id]); + id2 = first_defined([id2,id]); + assert(is_def(id1) && is_def(id2), "Must specify inner diameter of nut"); + slope = (id2-id1)/h; + full_id1 = id1-slope*extra/2; + full_id2 = id2+slope*extra/2; + bevel1 = first_defined([bevel1,bevel,false]); + bevel2 = first_defined([bevel2,bevel,false]); + dummy1 = assert(is_num(pitch) && pitch>0); + depth = -pitch*min(subindex(profile,1)); + attachable(anchor,spin,orient, size=[od/cos(30),od,h]) { + difference() { + cyl(d=od/cos(30), h=h, center=true, $fn=6,chamfer1=bevel1?depth:undef,chamfer2=bevel2?depth:undef); + generic_threaded_rod( + d1=full_id1,d2=full_id2, + l=h+extra, + pitch=pitch, + profile=profile, + left_handed=left_handed, + starts=starts, + internal=true, + bevel1=bevel1,bevel2=bevel2 + ); + } + children(); + } +} + + // Module: thread_helix() // Usage: -// thread_helix(d, pitch, thread_depth, [thread_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]); +// thread_helix(d, pitch, [thread_depth], [flank_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]); // Description: -// Creates a helical thread with optional end tapering. +// Creates a right-handed helical thread with optional end tapering. Unlike generic_threaded_rod, this module just generates the thread, and +// you specify the angle of thread you want, which makes it easy to put complete threads onto a longer shaft. It also makes a more finely +// divided taper at the thread ends. However, it takes about twice as long to render compared to generic_threaded_rod. +// . +// You can specify a thread_depth and flank_angle, in which +// case you get a symmetric trapezoidal thread, whose inner diameter (the base of the threads for external threading) +// is d (so the total diameter will be d + thread_depth). This differs from the threaded_rod modules, where the specified +// diameter is the outer diameter. +// Alternatively you can give a profile, following the same rules as for general_threaded_rod. +// The Y=0 point will align with the specified diameter, and the profile should +// range in X from -1/2 to 1/2. You cannot specify both the profile and the thread_depth or flank_angle. +// . +// Unlike generic_threaded_rod, when internal=true this module generates the threads, not a thread mask. +// The profile needs to be inverted to produce the proper thread form. If you use the built-in trapezoidal +// thread you get the inverted thread, designed so that the inner diameter is d. With adequate clearance +// this thread will mate with the thread that uses the same parameters but has internal=false. Note that +// unlike the threaded_rod modules, thread_helix does not adjust the diameter for faceting, nor does it +// subtract any $slop for clearance. +// . +// Higbee specifies tapering applied to the ends of the threads and is given as the linear distance +// over which to taper. Tapering works on both internal and external threads. // Arguments: // d = Inside base diameter of threads. Default: 10 // pitch = Distance between threads. Default: 2mm/thread // thread_depth = Depth of threads from top to bottom. -// thread_angle = Angle of the thread faces. Default: 15 degrees. +// flank_angle = Angle of thread faces to plane perpendicular to screw. Default: 15 degrees. // twist = Number of degrees to rotate thread around. Default: 720 degrees. // --- // profile = If an asymmetrical thread profile is needed, it can be specified here. @@ -43,39 +1146,70 @@ // [ 6/16, 0 ], // ]; // stroke(profile, width=0.02); +// Figure(2D,Med): +// pa_delta = tan(15)/4; +// rr1 = -1/2; +// z1 = 1/4-pa_delta; +// z2 = 1/4+pa_delta; +// profile = [ +// [-z2, rr1], +// [-z1, 0], +// [ z1, 0], +// [ z2, rr1], +// ]; +// fullprofile = 50*left(1/2,p=concat(profile, right(1, p=profile))); +// stroke(fullprofile,width=1); +// dir = fullprofile[2]-fullprofile[3]; +// dir2 = fullprofile[5]-fullprofile[4]; +// curve = arc(15,angle=[75,87],r=40 /*67.5*/); +// avgpt = mean([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2]); +// color("red"){ +// stroke([fullprofile[4]+[0,1], fullprofile[4]+[0,37]], width=1); +// stroke([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2], width=1); +// stroke(move(-curve[0]+avgpt,p=curve), width=0.71,endcaps="arrow2"); +// right(14)back(19)text("flank",size=4,halign="center"); +// right(14)back(14)text("angle",size=4,halign="center"); +// } // Example: -// thread_helix(d=10, pitch=2, thread_depth=0.75, thread_angle=15, twist=900, $fn=72); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=900, $fn=72); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=900, higbee=1, $fn=72); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=720, higbee=2, internal=true, $fn=72); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=360, left_handed=true, higbee=1, $fn=36); module thread_helix( - d, pitch=2, thread_depth, thread_angle=15, twist=720, + d, pitch, thread_depth, flank_angle, twist=720, profile, starts=1, left_handed=false, internal=false, d1, d2, higbee, higbee1, higbee2, anchor, spin, orient ) { + dummy1=assert(is_undef(profile) || !any_defined([thread_depth, flank_angle]),"Cannot give thread_depth or flank_angle with a profile"); h = pitch*starts*twist/360; r1 = get_radius(d1=d1, d=d, dflt=10); r2 = get_radius(d1=d2, d=d, dflt=10); - tdp = thread_depth / pitch; - dz = tdp * tan(thread_angle); - cap = (1 - 2*dz)/2; - profile = !is_undef(profile)? profile : ( - internal? [ + profile = is_def(profile) ? profile : + let( + tdp = thread_depth / pitch, + dz = tdp * tan(flank_angle), + cap = (1 - 2*dz)/2 + ) + internal? + [ [-cap/2-dz, tdp], [-cap/2, 0 ], [+cap/2, 0 ], [+cap/2+dz, tdp], - ] : [ + ] + : + [ [+cap/2+dz, 0 ], [+cap/2, tdp], [-cap/2, tdp], [-cap/2-dz, 0 ], - ] - ); + ]; pline = mirror([-1,1], p = profile * pitch); dir = left_handed? -1 : 1; - idir = internal? -1 : 1; attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { zrot_copies(n=starts) { - spiral_sweep(pline, h=h, r1=r1, r2=r2, twist=twist*dir, higbee=higbee, higbee1=higbee1, higbee2=higbee2, anchor=CENTER); + spiral_sweep(pline, h=h, r1=r1, r2=r2, twist=twist*dir, higbee=higbee, higbee1=higbee1, higbee2=higbee2, internal=internal, anchor=CENTER); } children(); } @@ -83,894 +1217,34 @@ module thread_helix( -// Module: trapezoidal_threaded_rod() -// Description: -// Constructs a generic trapezoidal threaded screw rod. This method makes -// much smoother threads than the naive linear_extrude method. -// For metric trapezoidal threads, use thread_angle=15 and thread_depth=pitch/2. -// For ACME threads, use thread_angle=14.5 and thread_depth=pitch/2. -// For square threads, use thread_angle=0 and thread_depth=pitch/2. -// For normal UTS or ISO screw threads, use the `threaded_rod()` module instead to get the correct thread profile. -// For NPT (National Pipe Threading) threads, use the `npt_threaded_rod() module instead. -// Arguments: -// d = Outer diameter of threaded rod. -// l = Length of threaded rod. -// pitch = Length between threads. -// thread_angle = The pressure angle profile angle of the threads. Default = 14.5 degree ACME profile. -// --- -// thread_depth = Depth of the threads. Default=pitch/2 -// left_handed = If true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: true -// starts = The number of lead starts. Default = 1 -// profile = The shape of a thread, if not a symmetric trapezoidal form. Given as a 2D path, where X is between -1/2 and 1/2, representing the pitch distance, and Y is 0 for the peak, and `-depth/pitch` for the valleys. The segment between the end of one thread profile and the start of the next is automatic, so the start and end coordinates should not both be at the same Y at X = ±1/2. This path is scaled up by the pitch size in both dimensions when making the final threading. This overrides the `thread_angle` and `thread_depth` options. -// internal = If true, make this a mask for making internal threads. -// d1 = Bottom outside diameter of threads. -// d2 = Top outside diameter of threads. -// higbee = Length to taper thread ends over. Default: 0 (No higbee thread tapering) -// higbee1 = Length to taper bottom thread end over. -// higbee2 = Length to taper top thread end over. -// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Examples(Med): -// trapezoidal_threaded_rod(d=10, l=40, pitch=2, thread_angle=15, $fn=32); -// trapezoidal_threaded_rod(d=3/8*25.4, l=20, pitch=1/8*25.4, thread_angle=29, $fn=32); -// trapezoidal_threaded_rod(d=60, l=16, pitch=8, thread_depth=3, thread_angle=45, left_handed=true, $fa=2, $fs=2); -// trapezoidal_threaded_rod(d=60, l=16, pitch=8, thread_depth=3, thread_angle=45, left_handed=true, starts=4, $fa=2, $fs=2); -// trapezoidal_threaded_rod(d=16, l=40, pitch=2, thread_angle=30); -// trapezoidal_threaded_rod(d=10, l=40, pitch=3, thread_angle=15, left_handed=true, starts=3, $fn=36); -// trapezoidal_threaded_rod(d=25, l=40, pitch=10, thread_depth=8/3, thread_angle=50, starts=4, center=false, $fa=2, $fs=2); -// trapezoidal_threaded_rod(d=50, l=35, pitch=8, thread_angle=30, starts=3, bevel=true); -// trapezoidal_threaded_rod(l=25, d=10, pitch=2, thread_angle=15, starts=3, $fa=1, $fs=1, orient=RIGHT, anchor=BOTTOM); -// Example(Med): Using as a Mask to Make Internal Threads -// bottom_half() difference() { -// cube(50, center=true); -// trapezoidal_threaded_rod(d=40, l=51, pitch=5, thread_angle=30, internal=true, orient=RIGHT, $fn=36); -// } -// Example(2DMed): Typical Tooth Profile -// pitch = 2; -// depth = pitch * cos(30) * 5/8; -// profile = [ -// [-7/16, -depth/pitch*1.07], -// [-6/16, -depth/pitch], -// [-1/16, 0], -// [ 1/16, 0], -// [ 6/16, -depth/pitch], -// [ 7/16, -depth/pitch*1.07] -// ]; -// stroke(profile, width=0.02); -module trapezoidal_threaded_rod( - d, l=100, pitch=2, - thread_angle=15, - thread_depth=undef, - left_handed=false, - bevel=false, - starts=1, - profile, - internal=false, - d1, d2, - higbee, higbee1, higbee2, - center, anchor, spin, orient -) { - r1 = get_radius(d1=d1, d=d, dflt=10); - r2 = get_radius(d1=d2, d=d, dflt=10); - sides = quantup(segs(max(r1,r2)), starts); - rsc = internal? (1/cos(180/sides)) : 1; - islop = internal? $slop*3 : 0; - _r1 = r1 * rsc + islop; - _r2 = r2 * rsc + islop; - threads = quantup(l/pitch+2, 2*starts); - depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle)); - pa_delta = min(pitch/4-0.01,depth*tan(thread_angle)/2)/pitch; - dir = left_handed? -1 : 1; - twist = 360 * l / pitch / starts; - higang1 = first_defined([higbee1, higbee, 0]); - higang2 = first_defined([higbee2, higbee, 0]); - assert(higang1 < twist/2); - assert(higang2 < twist/2); - - rr1 = -depth/pitch; - z1 = 1/4-pa_delta; - z2 = 1/4+pa_delta; - profile = ( - profile!=undef? profile : [ - [-z2, rr1], - [-z1, 0], - [ z1, 0], - [ z2, rr1], - ] - ); - prof3d = path3d(profile); - higthr1 = ceil(higang1 / 360); - higthr2 = ceil(higang2 / 360); - pdepth = -min(subindex(profile,1)); - dummy1 = assert(_r1>pdepth) assert(_r2>pdepth); - skew_mat = affine3d_skew(sxz=(_r2-_r1)/l); - side_mat = affine3d_xrot(90) * - affine3d_mirror([-1,1,0]) * - affine3d_scale([1,1,1] * pitch); - hig_table = [ - [-twist, 0], - [-twist/2-0.00001, 0], - [-twist/2+higang1, 1], - [+twist/2-higang2, 1], - [+twist/2+0.00001, 0], - [+twist, 0], - ]; - start_steps = floor(sides / starts); - thread_verts = [ - for (step = [0:1:start_steps]) let( - ang = 360 * step/sides, - dz = pitch * step / start_steps, - mat1 = affine3d_zrot(ang*dir), - mat2 = affine3d_translate([(_r1 + _r2) / 2 - pdepth*pitch, 0, 0]) * - skew_mat * - affine3d_translate([0, 0, dz]), - prof = apply(side_mat, [ - for (thread = [-threads/2:1:threads/2-1]) let( - tang = (thread/starts) * 360 + ang, - hsc = internal? 1 : - (higang1==0 && tang<=0)? 1 : - (higang2==0 && tang>=0)? 1 : - lookup(tang, hig_table), - mat3 = affine3d_translate([thread, 0, 0]) * - affine3d_scale([1, hsc, 1]) * - affine3d_translate([0,pdepth,0]) - ) each apply(mat3, prof3d) - ]) - - - ) [ - [0, 0, -l/2-pitch], - each apply(mat1*mat2, prof), - [0, 0, +l/2+pitch] - ] - ]; - thread_vnfs = vnf_merge([ - for (i=[0:1:starts-1]) - zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style="min_edge")), - for (i=[0:1:starts-1]) let( - rmat = zrot(i*360/starts), - pts = deduplicate(list_head(thread_verts[0], len(prof3d)+1)), - faces = [for (i=idx(pts,e=-2)) [0, i+1, i]], - rfaces = left_handed? [for (x=faces) reverse(x)] : faces - ) [apply(rmat,pts), rfaces], - for (i=[0:1:starts-1]) let( - rmat = zrot(i*360/starts), - pts = deduplicate(list_tail(last(thread_verts), -len(prof3d)-2)), - faces = [for (i=idx(pts,e=-2)) [len(pts)-1, i, i+1]], - rfaces = left_handed? [for (x=faces) reverse(x)] : faces - ) [apply(rmat,pts), rfaces] - ]); - - anchor = get_anchor(anchor, center, BOT, CENTER); - attachable(anchor,spin,orient, r1=_r1, r2=_r2, l=l) { - intersection() { - //vnf_validate(vnf_quantize(thread_vnfs), size=0.1); - vnf_polyhedron(vnf_quantize(thread_vnfs), convexity=10); - if (bevel) { - cyl(l=l, r1=_r1, r2=_r2, chamfer=depth); - } else { - cyl(l=l, r1=_r1, r2=_r2); - } - } - children(); - } -} - - -// Module: trapezoidal_threaded_nut() -// Description: -// Constructs a hex nut for a threaded screw rod. This method makes -// much smoother threads than the naive linear_extrude method. -// For metric screw threads, use thread_angle=30 and leave out thread_depth argument. -// For SAE screw threads, use thread_angle=30 and leave out thread_depth argument. -// For metric trapezoidal threads, use thread_angle=15 and thread_depth=pitch/2. -// For ACME threads, use thread_angle=14.5 and thread_depth=pitch/2. -// For square threads, use thread_angle=0 and thread_depth=pitch/2. -// Arguments: -// od = diameter of the nut. -// id = diameter of threaded rod to screw onto. -// h = height/thickness of nut. -// pitch = Length between threads. -// thread_depth = Depth of the threads. Default=pitch/2. -// thread_angle = The pressure angle profile angle of the threads. Default = 14.5 degree ACME profile. -// left_handed = if true, create left-handed threads. Default = false -// starts = The number of lead starts. Default = 1 -// bevel = if true, bevel the thread ends. Default: true -// profile = The shape of a thread, if not a symmetric trapezoidal form. Given as a 2D path, where X is between -1/2 and 1/2, representing the pitch distance, and Y is 0 for the peak, and `-depth/pitch` for the valleys. The segment between the end of one thread profile and the start of the next is automatic, so the start and end coordinates should not both be at the same Y at X = ±1/2. This path is scaled up by the pitch size in both dimensions when making the final threading. This overrides the `thread_angle` and `thread_depth` options. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Examples(Med): -// trapezoidal_threaded_nut(od=16, id=8, h=8, pitch=2, $slop=0.2, anchor=UP); -// trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, $slop=0.2, left_handed=true); -// trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, thread_angle=15, starts=3, $fa=1, $fs=1); -module trapezoidal_threaded_nut( - od=17.4, - id=10, - h=10, - pitch=2, - thread_depth=undef, - thread_angle=15, - profile=undef, - left_handed=false, - starts=1, - bevel=true, - anchor, spin, orient -) { - depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle)); - attachable(anchor,spin,orient, size=[od/cos(30),od,h]) { - difference() { - cylinder(d=od/cos(30), h=h, center=true, $fn=6); - trapezoidal_threaded_rod( - d=id, - l=h+1, - pitch=pitch, - thread_depth=depth, - thread_angle=thread_angle, - profile=profile, - left_handed=left_handed, - starts=starts, - internal=true - ); - if (bevel) { - zflip_copy() { - down(h/2+0.01) { - cylinder(r1=id/2+$slop, r2=id/2+$slop-depth, h=depth, center=false); - } - } - } - } - children(); - } -} - - -// Section: Triangular Threading - -// Module: threaded_rod() -// Description: -// Constructs a standard metric or UTS threaded screw rod. This method -// makes much smoother threads than the naive linear_extrude method. -// Arguments: -// d = Outer diameter of threaded rod. -// l = length of threaded rod. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// internal = If true, make this a mask for making internal threads. -// d1 = Bottom outside diameter of threads. -// d2 = Top outside diameter of threads. -// higbee = Length to taper thread ends over. Default: 0 -// higbee1 = Length to taper bottom thread end over. -// higbee2 = Length to taper top thread end over. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Example(2D): -// projection(cut=true) -// threaded_rod(d=10, l=15, pitch=2, orient=BACK); -// Examples(Med): -// threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1); -// threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1); -module threaded_rod( - d, l=100, pitch=2, - left_handed=false, - bevel=false, - internal=false, - d1, d2, - higbee, higbee1, higbee2, - anchor, spin, orient -) { - _r1 = get_radius(d1=d1, d=d, dflt=10); - _r2 = get_radius(d1=d2, d=d, dflt=10); - depth = pitch * cos(30) * 5/8; - profile = internal? [ - [-6/16, -depth/pitch], - [-1/16, 0], - [-1/32, 0.02], - [ 1/32, 0.02], - [ 1/16, 0], - [ 6/16, -depth/pitch] - ] : [ - [-7/16, -depth/pitch*1.07], - [-6/16, -depth/pitch], - [-1/16, 0], - [ 1/16, 0], - [ 6/16, -depth/pitch], - [ 7/16, -depth/pitch*1.07] - ]; - trapezoidal_threaded_rod( - d=d, d1=d1, d2=d2, l=l, - pitch=pitch, - thread_depth=depth, - thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - internal=internal, - higbee=higbee, - higbee1=higbee1, - higbee2=higbee2, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - - -// Module: threaded_nut() -// Description: -// Constructs a hex nut for a metric or UTS threaded screw rod. This method -// makes much smoother threads than the naive linear_extrude method. -// Arguments: -// od = diameter of the nut. -// id = diameter of threaded rod to screw onto. -// h = height/thickness of nut. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Examples(Med): -// threaded_nut(od=16, id=8, h=8, pitch=1.25, left_handed=true, $slop=0.2, $fa=1, $fs=1); -module threaded_nut( - od=16, id=10, h=10, - pitch=2, left_handed=false, bevel=false, - anchor, spin, orient -) { - depth = pitch * cos(30) * 5/8; - profile = [ - [-6/16, -depth/pitch], - [-1/16, 0], - [-1/32, 0.02], - [ 1/32, 0.02], - [ 1/16, 0], - [ 6/16, -depth/pitch] - ]; - trapezoidal_threaded_nut( - od=od, id=id, h=h, - pitch=pitch, thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - anchor=anchor, spin=spin, - orient=orient - ) children(); -} - - -// Section: Pipe Threading - -// Module: npt_threaded_rod() -// Description: -// Constructs a standard NPT pipe end threading. If `internal=true`, creates a mask for making -// internal pipe threads. Tapers smaller upwards if `internal=false`. Tapers smaller downwards -// if `internal=true`. If `hollow=true` and `internal=false`, then the pipe threads will be -// hollowed out into a pipe with the apropriate internal diameter. -// Arguments: -// size = NPT standard pipe size in inches. 1/16", 1/8", 1/4", 3/8", 1/2", 3/4", 1", 1+1/4", 1+1/2", or 2". Default: 1/2" -// left_handed = If true, create left-handed threads. Default = false -// bevel = If true, bevel the thread ends. Default: false -// hollow = If true, create a pipe with the correct internal diameter. -// internal = If true, make this a mask for making internal threads. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Example(2D): -// projection(cut=true) npt_threaded_rod(size=1/4, orient=BACK); -// Examples(Med): -// npt_threaded_rod(size=3/8, $fn=72); -// npt_threaded_rod(size=1/2, $fn=72); -// npt_threaded_rod(size=1/2, left_handed=true, $fn=72); -module npt_threaded_rod( - size=1/2, - left_handed=false, - bevel=false, - hollow=false, - internal=false, - anchor, spin, orient -) { - assert(is_finite(size)); - assert(is_bool(left_handed)); - assert(is_bool(bevel)); - assert(is_bool(hollow)); - assert(is_bool(internal)); - assert(!(internal&&hollow), "Cannot created a hollow internal threads mask."); - info_table = [ - // Size OD len TPI - [ 1/16, [ 0.3896, 0.308, 27 ]], - [ 1/8, [ 0.3924, 0.401, 27 ]], - [ 1/4, [ 0.5946, 0.533, 18 ]], - [ 3/8, [ 0.6006, 0.668, 18 ]], - [ 1/2, [ 0.7815, 0.832, 14 ]], - [ 3/4, [ 0.7935, 1.043, 14 ]], - [ 1, [ 0.9845, 1.305, 11.5]], - [ 1+1/4, [ 1.0085, 1.649, 11.5]], - [ 1+1/2, [ 1.0252, 1.888, 11.5]], - [ 2, [ 1.0582, 2.362, 11.5]], - ]; - info = [for (data=info_table) if(approx(size,data[0])) data[1]][0]; - dummy1 = assert(is_def(info), "Unsupported NPT size. Try one of 1/16, 1/8, 1/4, 3/8, 1/2, 3/4, 1, 1+1/4, 1+1/2, 2"); - l = 25.4 * info[0]; - d = 25.4 * info[1]; - pitch = 25.4 / info[2]; - rr = get_radius(d=d, dflt=0.84 * 25.4 / 2); - rr2 = rr - l/32; - r1 = internal? rr2 : rr; - r2 = internal? rr : rr2; - depth = pitch * cos(30) * 5/8; - profile = internal? [ - [-6/16, -depth/pitch], - [-1/16, 0], - [-1/32, 0.02], - [ 1/32, 0.02], - [ 1/16, 0], - [ 6/16, -depth/pitch] - ] : [ - [-7/16, -depth/pitch*1.07], - [-6/16, -depth/pitch], - [-1/16, 0], - [ 1/16, 0], - [ 6/16, -depth/pitch], - [ 7/16, -depth/pitch*1.07] - ]; - attachable(anchor,spin,orient, l=l, r1=r1, r2=r2) { - difference() { - trapezoidal_threaded_rod( - d1=2*r1, d2=2*r2, l=l, - pitch=pitch, - thread_depth=depth, - thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - internal=internal, - higbee=r1*PI/2 - ); - if (hollow) { - cylinder(l=l+1, d=size*INCH, center=true); - } else nil(); - } - children(); - } -} - - - -// Section: Buttress Threading - -// Module: buttress_threaded_rod() -// Description: -// Constructs a simple buttress threaded screw rod. This method -// makes much smoother threads than the naive linear_extrude method. -// Arguments: -// d = Outer diameter of threaded rod. -// l = length of threaded rod. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// internal = If true, this is a mask for making internal threads. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Example(2D): -// projection(cut=true) -// buttress_threaded_rod(d=10, l=15, pitch=2, orient=BACK); -// Examples(Med): -// buttress_threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1); -// buttress_threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1); -module buttress_threaded_rod( - d=10, l=100, pitch=2, - left_handed=false, - bevel=false, - internal=false, - anchor, spin, orient -) { - depth = pitch * 3/4; - profile = [ - [ -7/16, -0.75], - [ 5/16, 0], - [ 7/16, 0], - [ 7/16, -0.75], - [ 1/ 2, -0.77], - ]; - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_depth=depth, - thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - - -// Module: buttress_threaded_nut() -// Description: -// Constructs a hex nut for a simple buttress threaded screw rod. This method -// makes much smoother threads than the naive linear_extrude method. -// Arguments: -// od = diameter of the nut. -// id = diameter of threaded rod to screw onto. -// h = height/thickness of nut. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Examples(Med): -// buttress_threaded_nut(od=16, id=8, h=8, pitch=1.25, left_handed=true, $slop=0.2, $fa=1, $fs=1); -module buttress_threaded_nut( - od=16, id=10, h=10, - pitch=2, left_handed=false, - bevel=false, - anchor, spin, orient -) { - depth = pitch * 3/4; - profile = [ - [ -7/16, -0.75], - [ 5/16, 0], - [ 7/16, 0], - [ 7/16, -0.75], - [ 1/ 2, -0.77], - ]; - trapezoidal_threaded_nut( - od=od, id=id, h=h, - pitch=pitch, thread_angle=30, - profile=profile, - thread_depth=pitch*3*sqrt(3)/8, - left_handed=left_handed, - bevel=bevel, - anchor=anchor, spin=spin, - orient=orient - ) children(); -} - - -// Section: Metric Trapezoidal Threading - -// Module: metric_trapezoidal_threaded_rod() -// Description: -// Constructs a metric trapezoidal threaded screw rod. This method makes much -// smoother threads than the naive linear_extrude method. -// Arguments: -// d = Outer diameter of threaded rod. -// l = length of threaded rod. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// starts = The number of lead starts. Default = 1 -// internal = If true, this is a mask for making internal threads. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Example(2D): -// projection(cut=true) -// metric_trapezoidal_threaded_rod(d=10, l=15, pitch=2, orient=BACK); -// Examples(Med): -// metric_trapezoidal_threaded_rod(d=10, l=30, pitch=2, left_handed=true, $fa=1, $fs=1); -module metric_trapezoidal_threaded_rod( - d=10, l=100, pitch=2, - left_handed=false, - starts=1, - bevel=false, - internal=false, - anchor, spin, orient -) { - trapezoidal_threaded_rod( - d=d, l=l, - pitch=pitch, - thread_angle=15, - left_handed=left_handed, - starts=starts, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - - -// Module: metric_trapezoidal_threaded_nut() -// Description: -// Constructs a hex nut for a metric trapezoidal threaded screw rod. This method -// makes much smoother threads than the naive linear_extrude method. -// Arguments: -// od = diameter of the nut. -// id = diameter of threaded rod to screw onto. -// h = height/thickness of nut. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// starts = The number of lead starts. Default = 1 -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Examples(Med): -// metric_trapezoidal_threaded_nut(od=16, id=10, h=10, pitch=2, left_handed=true, bevel=true, $fa=1, $fs=1); -module metric_trapezoidal_threaded_nut( - od=17.4, id=10.5, h=10, - pitch=3.175, - starts=1, - left_handed=false, - bevel=false, - anchor, spin, orient -) { - trapezoidal_threaded_nut( - od=od, id=id, h=h, - pitch=pitch, thread_angle=15, - left_handed=left_handed, - starts=starts, - bevel=bevel, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - -// Section: ACME Trapezoidal Threading - -// Module: acme_threaded_rod() -// Description: -// Constructs an ACME trapezoidal threaded screw rod. This method makes -// much smoother threads than the naive linear_extrude method. -// Arguments: -// d = Outer diameter of threaded rod. -// l = length of threaded rod. -// pitch = Length between threads. -// thread_depth = Depth of the threads. Default = pitch/2 -// thread_angle = The pressure angle profile angle of the threads. Default = 14.5 degrees -// starts = The number of lead starts. Default = 1 -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// internal = If true, this is a mask for making internal threads. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Example(2D): -// projection(cut=true) -// acme_threaded_rod(d=10, l=15, pitch=2, orient=BACK); -// Examples(Med): -// acme_threaded_rod(d=3/8*25.4, l=20, pitch=1/8*25.4, $fn=32); -// acme_threaded_rod(d=10, l=30, pitch=2, starts=3, $fa=1, $fs=1); -module acme_threaded_rod( - d=10, l=100, pitch=2, - thread_angle=14.5, - thread_depth=undef, - starts=1, - left_handed=false, - bevel=false, - internal=false, - anchor, spin, orient -) { - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_angle=thread_angle, - thread_depth=thread_depth, - starts=starts, - left_handed=left_handed, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - - -// Module: acme_threaded_nut() -// Description: -// Constructs a hex nut for an ACME threaded screw rod. This method makes -// much smoother threads than the naive linear_extrude method. -// Arguments: -// od = diameter of the nut. -// id = diameter of threaded rod to screw onto. -// h = height/thickness of nut. -// pitch = Length between threads. -// thread_depth = Depth of the threads. Default=pitch/2 -// thread_angle = The pressure angle profile angle of the threads. Default = 14.5 degree ACME profile. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Examples(Med): -// acme_threaded_nut(od=16, id=3/8*25.4, h=8, pitch=1/8*25.4, $slop=0.2); -// acme_threaded_nut(od=16, id=10, h=10, pitch=2, starts=3, $slop=0.2, $fa=1, $fs=1); -module acme_threaded_nut( - od, id, h, pitch, - thread_angle=14.5, - thread_depth=undef, - starts=1, - left_handed=false, - bevel=false, - anchor, spin, orient -) { - trapezoidal_threaded_nut( - od=od, id=id, h=h, pitch=pitch, - thread_depth=thread_depth, - thread_angle=thread_angle, - left_handed=left_handed, - bevel=bevel, - starts=starts, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - -// Section: Square Threading - -// Module: square_threaded_rod() -// Description: -// Constructs a square profile threaded screw rod. This method makes -// much smoother threads than the naive linear_extrude method. -// Arguments: -// d = Outer diameter of threaded rod. -// l = length of threaded rod. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// starts = The number of lead starts. Default = 1 -// internal = If true, this is a mask for making internal threads. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Example(2D): -// projection(cut=true) -// square_threaded_rod(d=10, l=15, pitch=2, orient=BACK); -// Examples(Med): -// square_threaded_rod(d=10, l=20, pitch=2, starts=2, $fn=32); -module square_threaded_rod( - d=10, l=100, pitch=2, - left_handed=false, - bevel=false, - starts=1, - internal=false, - anchor, spin, orient -) { - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_angle=0.1, - left_handed=left_handed, - bevel=bevel, - starts=starts, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - - -// Module: square_threaded_nut() -// Description: -// Constructs a hex nut for a square profile threaded screw rod. This method -// makes much smoother threads than the naive linear_extrude method. -// Arguments: -// od = diameter of the nut. -// id = diameter of threaded rod to screw onto. -// h = height/thickness of nut. -// pitch = Length between threads. -// left_handed = if true, create left-handed threads. Default = false -// bevel = if true, bevel the thread ends. Default: false -// starts = The number of lead starts. Default = 1 -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Examples(Med): -// square_threaded_nut(od=16, id=10, h=10, pitch=2, starts=2, $slop=0.15, $fn=32); -module square_threaded_nut( - od=17.4, id=10.5, h=10, - pitch=3.175, - left_handed=false, - bevel=false, - starts=1, - anchor, spin, orient -) { - trapezoidal_threaded_nut( - od=od, id=id, h=h, pitch=pitch, - thread_angle=0, - left_handed=left_handed, - bevel=bevel, - starts=starts, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - -// Section: Ball Screws - -// Module: ball_screw_rod() -// Description: -// Constructs a ball screw rod. This method makes much smoother threads than the naive linear_extrude method. -// Arguments: -// d = Outer diameter of threaded rod. -// l = length of threaded rod. -// pitch = Length between threads. Also, the diameter of the ball bearings used. -// ball_diam = The diameter of the ball bearings to use with this ball screw. -// ball_arc = The arc portion that should touch the ball bearings. Default: 120 degrees. -// left_handed = if true, create left-handed threads. Default = false -// starts = The number of lead starts. Default = 1 -// bevel = if true, bevel the thread ends. Default: false -// internal = If true, make this a mask for making internal threads. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The printer-specific slop value to make parts fit just right. -// Example(2D): Thread Profile, ball_diam=4, ball_arc=100 -// projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=4, ball_arc=100, orient=BACK); -// Example(2D): Thread Profile, ball_diam=4, ball_arc=120 -// projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=4, ball_arc=120, orient=BACK); -// Example(2D): Thread Profile, ball_diam=3, ball_arc=120 -// projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=3, ball_arc=120, orient=BACK); -// Examples(Med): -// ball_screw_rod(d=15, l=20, pitch=8, ball_diam=5, ball_arc=120, $fa=1, $fs=1); -// ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, $fa=1, $fs=1); -// ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, left_handed=true, $fa=1, $fs=1); -module ball_screw_rod( - d=10, l=100, pitch=2, starts=1, - ball_diam=5, ball_arc=100, - left_handed=false, - internal=false, - bevel=false, - anchor, spin, orient -) { - n = ceil(segs(ball_diam/2)*ball_arc/2/360); - depth = ball_diam * (1-cos(ball_arc/2))/2; - cpy = ball_diam/2/pitch*cos(ball_arc/2); - profile = [ - each arc(N=n, d=ball_diam/pitch, cp=[-0.5,cpy], start=270, angle=ball_arc/2), - each arc(N=n, d=ball_diam/pitch, cp=[+0.5,cpy], start=270-ball_arc/2, angle=ball_arc/2) - ]; - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_depth=depth, - thread_angle=90-ball_arc/2, - profile=profile, - left_handed=left_handed, - starts=starts, - bevel=bevel, - internal=internal, - higbee=0, - anchor=anchor, - spin=spin, - orient=orient - ) children(); -} - - - // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap + +// Changes +// internal: can set bevel to true and get non-garbage result +// bevel is always set by thread depth +// acme takes tpi +// square threads are at angle 0 +// added generic_threaded_{rod,nut} +// eliminated metric_trapezoidal_* +// cleaned up matrices some in generic_threaded_rod +// threaded_rod can produce spec-true ISO/UTS profile with a triplet input for the diameter. +// Added bevel1 and bevel2 to all modules. Made default uniformly false for every case instead of +// sometimes true, sometimes false +// Profiles that go over zero are not clipped, and bevels are based on actual profile top, not nominal +// When bevel is given to nuts it bevels the outside of the nut by thread depth +// higbee looks best with quincunx, but it's more expensive. Select quincunx when higbee is used, min_edge otherwise +// Current code uses difference to remove excess length in the rod. This gives faster renders at the cost +// of more complex code and green top/bottom surfaces. +// Changed slop to 4 * $slop. I got good results printing with $slop=0.05 with this setting. +// Don't generate excess threads when starts>1, and don't force threads to be even +// +// Fixed higbee in spiral_sweep for properly centered scaling and for staying on the internal/external base of threads +// Fixed bug in spiral_sweep where two segments were missing if higbee is zero +// Fixed faceting bugs in spiral_sweep where segments weren't aligned with requested number of segments, and higbee +// would pull away from the cylinder by using a higher count and following a true circle +// +// Questions +// Should nut modules take d1/d2 for tapered nuts? +// +// Need explanation of what exactly the diff is between threaded_rod and helix_threads. +// Higbee is different, angle in one and length in another. Need to reconcile diff --git a/vnf.scad b/vnf.scad index 85cd4d4..77e08e7 100644 --- a/vnf.scad +++ b/vnf.scad @@ -216,7 +216,7 @@ function vnf_triangulate(vnf) = // Function: vnf_vertex_array() // Usage: -// vnf = vnf_vertex_array(points, [caps], [cap1], [cap2], [reverse], [col_wrap], [row_wrap], [vnf]); +// vnf = vnf_vertex_array(points, [caps], [cap1], [cap2], [style], [reverse], [col_wrap], [row_wrap], [vnf]); // Description: // Creates a VNF structure from a vertex list, by dividing the vertices into columns and rows, // adding faces to tile the surface. You can optionally have faces added to wrap the last column