1//////////////////////////////////////////////////////////////////////
   2// LibFile: threading.scad
   3//   Provides generic threading support and specialized support for standard triangular (UTS/ISO) threading,
   4//   trapezoidal threading (ACME), pipe threading, buttress threading, square threading and ball screws.  
   5// Includes:
   6//   include <BOSL2/std.scad>
   7//   include <BOSL2/threading.scad>
   8// FileGroup: Threaded Parts
   9// FileSummary: Various types of threaded rods and nuts.
  10//////////////////////////////////////////////////////////////////////
  11
  12// Section: Standard (UTS/ISO) Threading
  13
  14// Module: threaded_rod()
  15// Usage:
  16//   threaded_rod(d, l|length, pitch, [internal=], ...) [ATTACHMENTS];
  17// Description:
  18//   Constructs a standard ISO (metric) or UTS (English) threaded rod.  These threads are close to triangular,
  19//   with a 60 degree thread angle.  You can give the outer diameter and get the "basic form" or you can
  20//   set d to a triplet [d_min, d_pitch, d_major] where are parameters determined by the ISO and UTS specifications
  21//   that define clearance sizing for the threading.  See screws.scad for how to make screws
  22//   using the specification parameters.  
  23// Arguments:
  24//   d = Outer diameter of threaded rod, or a triplet of [d_min, d_pitch, d_major]. 
  25//   l / length = length of threaded rod.
  26//   pitch = Length between threads.
  27//   ---
  28//   left_handed = if true, create left-handed threads.  Default = false
  29//   starts = The number of lead starts.  Default: 1
  30//   bevel = if true, bevel the thread ends.  Default: false
  31//   bevel1 = if true bevel the bottom end.
  32//   bevel2 = if true bevel the top end.
  33//   internal = If true, make this a mask for making internal threads.
  34//   d1 = Bottom outside diameter of threads.
  35//   d2 = Top outside diameter of threads.
  36//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
  37//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
  38//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
  39//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
  40//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
  41//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
  42//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
  43// Example(2D):
  44//   projection(cut=true)
  45//       threaded_rod(d=10, l=15, pitch=1.5, orient=BACK);
  46// Examples(Med):
  47//   threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1);
  48//   threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1);
  49//   threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1, bevel=true);
  50//   rot(90)threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1, higbee=true);
  51// Example: Diamond threading where both left-handed and right-handed nuts travel (in the same direction) on the threaded rod:
  52//   $slop = 0.075;
  53//   d = 3/8*INCH;
  54//   pitch = 1/16*INCH;
  55//   starts=3;
  56//   xdistribute(19){
  57//       intersection(){
  58//         threaded_rod(l=40, pitch=pitch, d=d,starts=starts,anchor=BOTTOM);
  59//         threaded_rod(l=40, pitch=pitch, d=d, left_handed=true,starts=starts,anchor=BOTTOM);
  60//       }
  61//       threaded_nut(nutwidth=4.5/8*INCH,id=d,h=3/8*INCH,pitch=pitch,starts=starts,anchor=BOTTOM);
  62//       threaded_nut(nutwidth=4.5/8*INCH,id=d,h=3/8*INCH,pitch=pitch,starts=starts,left_handed=true,anchor=BOTTOM);
  63//   }
  64function threaded_rod(
  65    d, l, pitch,
  66    left_handed=false,
  67    bevel,bevel1,bevel2,starts=1,
  68    internal=false,
  69    d1, d2,
  70    higbee, higbee1, higbee2,
  71    anchor, spin, orient
  72) = no_function("threaded_rod");
  73
  74module threaded_rod(
  75    d, l, pitch,
  76    left_handed=false,
  77    bevel,bevel1,bevel2,starts=1,
  78    internal=false,
  79    d1, d2, length, 
  80    higbee, higbee1, higbee2,
  81    anchor, spin, orient
  82) {
  83    dummy1=
  84      assert(all_positive(pitch))
  85      assert(all_positive(d))
  86      assert(all_positive(l));
  87    basic = is_num(d) || is_undef(d) || is_def(d1) || is_def(d2);
  88    dummy2 = assert(basic || is_vector(d,3));
  89    depth = basic ? cos(30) * 5/8
  90                  : (d[2] - d[0])/2/pitch;
  91    crestwidth = basic ? 1/8 : 1/2 - (d[2]-d[1])/sqrt(3)/pitch;
  92    profile =    [
  93                  [-depth/sqrt(3)-crestwidth/2, -depth],
  94                  [              -crestwidth/2,      0],
  95                  [               crestwidth/2,      0],
  96                  [ depth/sqrt(3)+crestwidth/2, -depth]
  97                 ];
  98    oprofile = internal? [
  99        [-6/16, -depth],
 100        [-1/16,  0],
 101        [-1/32,  0.02],
 102        [ 1/32,  0.02],
 103        [ 1/16,  0],
 104        [ 6/16, -depth]
 105    ] : [
 106        [-7/16, -depth*1.07],
 107        [-6/16, -depth],
 108        [-1/16,  0],
 109        [ 1/16,  0],
 110        [ 6/16, -depth],
 111        [ 7/16, -depth*1.07]
 112    ];
 113    generic_threaded_rod(
 114        d=basic ? d : d[2], d1=d1, d2=d2, l=l,
 115        pitch=pitch,
 116        profile=profile,starts=starts,
 117        left_handed=left_handed,
 118        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
 119        internal=internal, length=length, 
 120        higbee=higbee,
 121        higbee1=higbee1,
 122        higbee2=higbee2,
 123        anchor=anchor,
 124        spin=spin,
 125        orient=orient
 126    ) children();
 127}
 128
 129
 130
 131// Module: threaded_nut()
 132// Usage:
 133//   threaded_nut(nutwidth, id, h|height|thickness, pitch,...) [ATTACHMENTS];
 134// Description:
 135//   Constructs a hex nut or square nut for an ISO (metric) or UTS (English) threaded rod. 
 136// Arguments:
 137//   nutwidth = flat to flat width of nut
 138//   id = diameter of threaded rod to screw onto.
 139//   h / height / thickness = height/thickness of nut.
 140//   pitch = Distance between threads, or zero for no threads. 
 141//   ---
 142//   shape = specifies shape of nut, either "hex" or "square".  Default: "hex"
 143//   left_handed = if true, create left-handed threads.  Default = false
 144//   starts = The number of lead starts.  Default: 1
 145//   bevel = if true, bevel the outside of the nut.  Default: true for hex nuts, false for square nuts
 146//   bevel1 = if true, bevel the outside of the nut bottom.
 147//   bevel2 = if true, bevel the outside of the nut top. 
 148//   bevang = set the angle for the outside nut bevel.  Default: 30
 149//   ibevel = if true, bevel the inside (the hole).   Default: true
 150//   ibevel1 = if true bevel the inside, bottom end.
 151//   ibevel2 = if true bevel the inside, top end.
 152//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 153//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 154//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 155//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 156//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 157//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 158//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 159// Examples(Med):
 160//   threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25, $slop=0.05, $fa=1, $fs=1);
 161//   threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25, left_handed=true, bevel=false, $slop=0.1, $fa=1, $fs=1);
 162//   threaded_nut(shape="square", nutwidth=16, id=8, h=8, pitch=1.25, $slop=0.1, $fa=1, $fs=1);
 163//   threaded_nut(shape="square", nutwidth=16, id=8, h=8, pitch=1.25, bevel2=true, $slop=0.1, $fa=1, $fs=1);
 164//   rot(90)threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25,higbee=true, $slop=0.1, $fa=1, $fs=1);
 165function threaded_nut(
 166    nutwidth, id, h,
 167    pitch, starts=1, shape="hex", left_handed=false, bevel, bevel1, bevel2, id1,id2,
 168    ibevel1, ibevel2, ibevel, bevang=30, thickness, height,     
 169    anchor, spin, orient
 170)=no_function("threaded_nut");
 171module threaded_nut(
 172    nutwidth, id, h,
 173    pitch, starts=1, shape="hex", left_handed=false, bevel, bevel1, bevel2, id1,id2,
 174    ibevel1, ibevel2, ibevel, bevang=30, thickness, height,
 175    higbee, higbee1, higbee2,
 176    anchor, spin, orient
 177) {
 178    dummy1=
 179          assert(all_nonnegative(pitch), "Nut pitch must be nonnegative")
 180          assert(all_positive(id), "Nut inner diameter must be positive")
 181          assert(all_positive(h),"Nut thickness must be positive");
 182    basic = is_num(id) || is_undef(id) || is_def(id1) || is_def(id2);
 183    dummy2 = assert(basic || is_vector(id,3));
 184    depth = basic ? cos(30) * 5/8
 185                  : (id[2] - id[0])/2/pitch;
 186    crestwidth = basic ? 1/8 : 1/2 - (id[2]-id[1])/sqrt(3)/pitch;
 187    profile =    [
 188                  [-depth/sqrt(3)-crestwidth/2, -depth],
 189                  [              -crestwidth/2,      0],
 190                  [               crestwidth/2,      0],
 191                  [ depth/sqrt(3)+crestwidth/2, -depth]
 192                 ];
 193    oprofile = [
 194        [-6/16, -depth/pitch],
 195        [-1/16,  0],
 196        [-1/32,  0.02],
 197        [ 1/32,  0.02],
 198        [ 1/16,  0],
 199        [ 6/16, -depth/pitch]
 200    ];
 201    generic_threaded_nut(
 202        nutwidth=nutwidth,
 203        id=basic ? id : id[2], id1=id1, id2=id2,
 204        h=h,
 205        pitch=pitch,
 206        profile=profile,starts=starts,shape=shape, 
 207        left_handed=left_handed,
 208        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
 209        ibevel1=ibevel1, ibevel2=ibevel2, ibevel=ibevel,
 210        height=height, thickness=thickness, bevang=bevang,
 211        higbee=higbee, higbee1=higbee1, higbee2=higbee2,
 212        anchor=anchor, spin=spin,
 213        orient=orient
 214    ) children();
 215}
 216
 217// Section: Trapezoidal Threading
 218
 219
 220// Module: trapezoidal_threaded_rod()
 221// Usage:
 222//   trapezoidal_threaded_rod(d, l|length, pitch, [thread_angle], [thread_depth], [internal=], ...) [ATTACHMENTS];
 223// Description:
 224//   Constructs a threaded rod with a symmetric trapezoidal thread.  Trapezoidal threads are used for lead screws because
 225//   they are one of the strongest symmetric profiles.  This tooth shape is stronger than a similarly
 226//   sized square thread becuase of its wider base.  However, it does place a radial load on the nut, unlike the square thread.
 227//   For loads in only one direction the asymmetric buttress thread profile can bear greater loads.  
 228//   .
 229//   By default produces the nominal dimensions
 230//   for metric trapezoidal threads: a thread angle of 30 degrees and a depth set to half the pitch.
 231//   You can also specify your own trapezoid parameters.  For ACME threads see acme_threaded_rod().
 232// Figure(2D,Med,NoAxes):
 233//   pa_delta = tan(15)/4;
 234//   rr1 = -1/2;
 235//   z1 = 1/4-pa_delta;
 236//   z2 = 1/4+pa_delta;
 237//   profile = [
 238//               [-z2, rr1],
 239//               [-z1,  0],
 240//               [ z1,  0],
 241//               [ z2, rr1],
 242//             ];
 243//   fullprofile = 50*left(1/2,p=concat(profile, right(1, p=profile)));
 244//   stroke(fullprofile,width=1);
 245//   dir = fullprofile[2]-fullprofile[3];
 246//   dir2 = fullprofile[5]-fullprofile[4];
 247//   curve = arc(32,angle=[75,105],r=67.5);
 248//   avgpt = mean([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2]);
 249//   color("red"){
 250//    stroke([fullprofile[2]+.1*dir, fullprofile[2]+.4*dir], width=1);
 251//    stroke([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2], width=1);
 252//    stroke(move(-curve[0]+avgpt,p=curve), width=1,endcaps="arrow2");
 253//    back(10)text("thread",size=4,halign="center");
 254//    back(3)text("angle",size=4,halign="center");
 255//   }
 256// Arguments:
 257//   d = Outer diameter of threaded rod.
 258//   l / length = Length of threaded rod.
 259//   pitch = Thread spacing. 
 260//   thread_angle = Angle between two thread faces.  Default: 30
 261//   thread_depth = Depth of threads.  Default: pitch/2
 262//   ---
 263//   left_handed = If true, create left-handed threads.  Default: false
 264//   starts = The number of lead starts.  Default: 1
 265//   bevel = if true, bevel the thread ends.  Default: false
 266//   bevel1 = if true bevel the bottom end.
 267//   bevel2 = if true bevel the top end. 
 268//   internal = If true, make this a mask for making internal threads.  Default: false
 269//   d1 = Bottom outside diameter of threads.
 270//   d2 = Top outside diameter of threads.
 271//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 272//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 273//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 274//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 275//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 276//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 277//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 278// Example(2D):
 279//   projection(cut=true)
 280//       trapezoidal_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
 281// Examples(Med): 
 282//   trapezoidal_threaded_rod(d=10, l=40, pitch=2, $fn=32);  // Standard metric threading
 283//   rot(-65)trapezoidal_threaded_rod(d=10, l=17, pitch=2, higbee=25, $fn=32);  // Standard metric threading
 284//   trapezoidal_threaded_rod(d=10, l=17, pitch=2, bevel=true, $fn=32);  // Standard metric threading
 285//   trapezoidal_threaded_rod(d=10, l=30, pitch=2, left_handed=true, $fa=1, $fs=1);  // Standard metric threading
 286//   trapezoidal_threaded_rod(d=10, l=40, pitch=3, left_handed=true, starts=3, $fn=36);
 287//   trapezoidal_threaded_rod(l=25, d=10, pitch=2, starts=3, $fa=1, $fs=1, bevel=true, orient=RIGHT, anchor=BOTTOM);
 288//   trapezoidal_threaded_rod(d=60, l=16, pitch=8, thread_depth=3, thread_angle=90, left_handed=true, $fa=2, $fs=2);
 289//   trapezoidal_threaded_rod(d=60, l=16, pitch=8, thread_depth=3, thread_angle=90, left_handed=true, starts=4, $fa=2, $fs=2);
 290//   trapezoidal_threaded_rod(d=16, l=40, pitch=2, thread_angle=60);
 291//   trapezoidal_threaded_rod(d=25, l=40, pitch=10, thread_depth=8/3, thread_angle=100, starts=4, anchor=BOT, $fa=2, $fs=2);
 292//   trapezoidal_threaded_rod(d=50, l=35, pitch=8, thread_angle=60, starts=11, higbee=true,$fn=120);
 293// Example(Med): Using as a Mask to Make Internal Threads
 294//   bottom_half() difference() {
 295//       cube(50, center=true);
 296//       trapezoidal_threaded_rod(d=40, l=51, pitch=5, thread_angle=30, internal=true, bevel=true, orient=RIGHT, $fn=36);
 297//   }
 298function trapezoidal_threaded_rod(
 299    d, l, pitch,
 300    thread_angle=30,
 301    thread_depth=undef,
 302    left_handed=false,
 303    bevel,bevel1,bevel2,
 304    starts=1, length,
 305    internal=false,
 306    higbee, higbee1, higbee2,d1,d2,
 307    anchor, spin, orient
 308) = no_function("trapezoidal_threaded_rod");
 309module trapezoidal_threaded_rod(
 310    d, l, pitch,
 311    thread_angle=30,
 312    thread_depth=undef,
 313    left_handed=false,
 314    bevel,bevel1,bevel2,
 315    starts=1, length,
 316    internal=false,
 317    higbee, higbee1, higbee2,d1,d2,
 318    anchor, spin, orient
 319) {
 320    dummy0 = assert(all_positive(pitch));
 321    dummy1 = assert(thread_angle>=0 && thread_angle<180);
 322    depth = first_defined([thread_depth, pitch/2]);
 323    pa_delta = 0.5*depth*tan(thread_angle/2) / pitch;
 324    dummy2 = assert(pa_delta<1/4, "Specified thread geometry is impossible");
 325    rr1 = -depth/pitch;
 326    z1 = 1/4-pa_delta;
 327    z2 = 1/4+pa_delta;
 328    profile = [
 329               [-z2, rr1],
 330               [-z1,  0],
 331               [ z1,  0],
 332               [ z2, rr1],
 333              ];
 334    generic_threaded_rod(d=d,l=l,pitch=pitch,profile=profile,
 335                         left_handed=left_handed,bevel=bevel,bevel1=bevel1,bevel2=bevel2,starts=starts,internal=internal,d1=d1,d2=d2,
 336                         higbee=higbee,higbee1=higbee1,higbee2=higbee2,anchor=anchor,spin=spin,orient=orient,length=length)
 337      children();
 338}
 339
 340
 341// Module: trapezoidal_threaded_nut()
 342// Usage:
 343//   trapezoidal_threaded_nut(nutwidth, id, h|height|thickness, pitch, [thread_angle], [thread_depth], ...) [ATTACHMENTS];
 344// Description:
 345//   Constructs a hex nut or square nut for a symmetric trapzoidal threaded rod.
 346//   By default produces the nominal dimensions
 347//   for metric trapezoidal threads: a thread angle of 30 degrees and a depth set to half the pitch.
 348//   You can also specify your own trapezoid parameters.  For ACME threads see acme_threaded_nut(). 
 349// Arguments:
 350//   nutwidth = flat to flat width of nut
 351//   id = diameter of threaded rod to screw onto.
 352//   h / height / thickness = height/thickness of nut.
 353//   pitch = Thread spacing.
 354//   thread_angle = Angle between two thread faces.  Default: 30
 355//   thread_depth = Depth of the threads.  Default: pitch/2
 356//   ---
 357//   shape = specifies shape of nut, either "hex" or "square".  Default: "hex"
 358//   left_handed = if true, create left-handed threads.  Default = false
 359//   starts = The number of lead starts.  Default = 1
 360//   bevel = if true, bevel the outside of the nut.  Default: true for hex nuts, false for square nuts
 361//   bevel1 = if true, bevel the outside of the nut bottom.
 362//   bevel2 = if true, bevel the outside of the nut top. 
 363//   bevang = set the angle for the outside nut bevel.  Default: 30
 364//   ibevel = if true, bevel the inside (the hole).   Default: true
 365//   ibevel1 = if true bevel the inside, bottom end.
 366//   ibevel2 = if true bevel the inside, top end.
 367//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 368//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 369//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 370//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 371//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 372//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 373//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 374// Examples(Med):
 375//   trapezoidal_threaded_nut(nutwidth=16, id=8, h=8, pitch=2, $slop=0.1, anchor=UP);
 376//   trapezoidal_threaded_nut(nutwidth=16, id=8, h=8, pitch=2, bevel=true, $slop=0.05, anchor=UP);
 377//   trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=2, $slop=0.1, left_handed=true);
 378//   trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=2, starts=3, $fa=1, $fs=1, $slop=0.15);
 379//   trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=2, starts=3, $fa=1, $fs=1, $slop=0.15, higbee=true);
 380//   trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=0, $slop=0.2);   // No threads
 381function trapezoidal_threaded_nut(
 382    nutwidth,
 383    id,
 384    h,
 385    pitch,
 386    thread_angle=30,
 387    thread_depth, shape="hex",
 388    left_handed=false,
 389    starts=1,
 390    bevel,bevel1,bevel2,bevang=30,
 391    ibevel1,ibevel2,ibevel,
 392    thickness,height,
 393    id1,id2,
 394    higbee,higbee1,higbee2,
 395    anchor, spin, orient
 396) = no_function("trapezoidal_threaded_nut");
 397module trapezoidal_threaded_nut(
 398    nutwidth,
 399    id,
 400    h,
 401    pitch,
 402    thread_angle=30,
 403    thread_depth, shape="hex",
 404    left_handed=false,
 405    starts=1,
 406    bevel,bevel1,bevel2,bevang=30,
 407    ibevel1,ibevel2,ibevel,
 408    thickness,height,
 409    id1,id2,
 410    higbee,higbee1,higbee2,
 411    anchor, spin, orient
 412) {
 413    dummy1 = assert(is_num(pitch) && pitch>=0 && thread_angle>=0 && thread_angle<180);
 414    depth = first_defined([thread_depth, pitch/2]);
 415    pa_delta = 0.5*depth*tan(thread_angle/2) / pitch;
 416    dummy2 = assert(pitch==0 || pa_delta<1/4, "Specified thread geometry is impossible");
 417    rr1 = -depth/pitch;
 418    z1 = 1/4-pa_delta;
 419    z2 = 1/4+pa_delta;
 420    profile = [
 421               [-z2, rr1],
 422               [-z1,  0],
 423               [ z1,  0],
 424               [ z2, rr1],
 425              ];
 426    generic_threaded_nut(nutwidth=nutwidth,id=id,h=h,pitch=pitch,profile=profile,id1=id1,id2=id2,
 427                         shape=shape,left_handed=left_handed,bevel=bevel,bevel1=bevel1,bevel2=bevel2,starts=starts,
 428                         ibevel=ibevel,ibevel1=ibevel1,ibevel2=ibevel2,bevang=bevang,height=height,thickness=thickness,
 429                         higbee=higbee, higbee1=higbee1, higbee2=higbee2,
 430                         anchor=anchor,spin=spin,orient=orient)
 431      children();
 432}
 433
 434
 435// Module: acme_threaded_rod()
 436// Usage:
 437//   acme_threaded_rod(d, l|length, tpi|pitch=, [internal=], ...) [ATTACHMENTS];
 438// Description:
 439//   Constructs an ACME trapezoidal threaded screw rod.  This form has a 29 degree thread angle with a
 440//   symmetric trapezoidal thread.  
 441// Arguments:
 442//   d = Outer diameter of threaded rod.
 443//   l / length = length of threaded rod.
 444//   tpi = threads per inch.
 445//   ---
 446//   pitch = thread spacing (alternative to tpi)
 447//   starts = The number of lead starts.  Default = 1
 448//   left_handed = if true, create left-handed threads.  Default = false
 449//   bevel = if true, bevel the thread ends.  Default: false
 450//   bevel1 = if true bevel the bottom end.
 451//   bevel2 = if true bevel the top end. 
 452//   internal = If true, this is a mask for making internal threads.
 453//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 454//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 455//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 456//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 457//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 458//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 459//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 460// Example(2D):
 461//   projection(cut=true)
 462//       acme_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
 463// Examples(Med):
 464//   acme_threaded_rod(d=3/8*INCH, l=20, pitch=1/8*INCH, $fn=32);
 465//   acme_threaded_rod(d=10, l=30, pitch=2, starts=3, $fa=1, $fs=1);
 466function acme_threaded_rod(
 467    d, l, tpi, pitch,
 468    starts=1,
 469    left_handed=false,
 470    bevel,bevel1,bevel2,
 471    internal=false, length, 
 472    higbee, higbee1, higbee2,
 473    anchor, spin, orient
 474) = no_function("acme_threaded_rod");
 475module acme_threaded_rod(
 476    d, l, tpi, pitch,
 477    starts=1,
 478    left_handed=false,
 479    bevel,bevel1,bevel2,
 480    internal=false, length, 
 481    higbee, higbee1, higbee2,
 482    anchor, spin, orient
 483) {
 484    dummy = assert(num_defined([pitch,tpi])==1,"Must give exactly one of pitch and tpi");
 485    pitch = is_undef(pitch) ? INCH/tpi : pitch;
 486    trapezoidal_threaded_rod(
 487        d=d, l=l, pitch=pitch,
 488        thread_angle=29,
 489        thread_depth=pitch/2,
 490        starts=starts,
 491        left_handed=left_handed,
 492        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
 493        internal=internal, length=length,
 494        higbee=higbee,
 495        anchor=anchor,
 496        spin=spin,
 497        orient=orient
 498    ) children();
 499}
 500
 501
 502
 503// Module: acme_threaded_nut()
 504// Usage:
 505//   acme_threaded_nut(nutwidth, id, h|height|thickness, tpi|pitch=, [shape=], ...) [ATTACHMENTS];
 506// Description:
 507//   Constructs a hexagonal or square nut for an ACME threaded screw rod. 
 508// Arguments:
 509//   nutwidth = flat to flat width of nut. 
 510//   id = diameter of threaded rod to screw onto.
 511//   h / height / thickness = height/thickness of nut.
 512//   tpi = threads per inch
 513//   ---
 514//   pitch = Thread spacing (alternative to tpi)
 515//   shape = specifies shape of nut, either "hex" or "square".  Default: "hex"
 516//   left_handed = if true, create left-handed threads.  Default = false
 517//   starts = Number of lead starts.  Default: 1
 518//   bevel = if true, bevel the outside of the nut.  Default: true for hex nuts, false for square nuts
 519//   bevel1 = if true, bevel the outside of the nut bottom.
 520//   bevel2 = if true, bevel the outside of the nut top. 
 521//   bevang = set the angle for the outside nut bevel.  Default: 30
 522//   ibevel = if true, bevel the inside (the hole).   Default: true
 523//   ibevel1 = if true bevel the inside, bottom end.
 524//   ibevel2 = if true bevel the inside, top end.
 525//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 526//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 527//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 528//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 529//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 530//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 531//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 532// Examples(Med):
 533//   acme_threaded_nut(nutwidth=16, id=3/8*INCH, h=8, tpi=8, $slop=0.05);
 534//   acme_threaded_nut(nutwidth=16, id=1/2*INCH, h=10, tpi=12, starts=3, $slop=0.1, $fa=1, $fs=1);
 535//   acme_threaded_nut(nutwidth=16, id=1/2*INCH, h=10, tpi=12, starts=3, $slop=0.1, $fa=1, $fs=1,ibevel=false,higbee=true);
 536function acme_threaded_nut(
 537    nutwidth, id, h, tpi, pitch,
 538    starts=1,
 539    left_handed=false,shape="hex",
 540    bevel,bevel1,bevel2,bevang=30,
 541    ibevel,ibevel1,ibevel2,
 542    height,thickness,
 543    higbee,higbee1,higbee2,
 544    anchor, spin, orient
 545) = no_function("acme_threaded_nut");
 546module acme_threaded_nut(
 547    nutwidth, id, h, tpi, pitch,
 548    starts=1,
 549    left_handed=false,shape="hex",
 550    bevel,bevel1,bevel2,bevang=30,
 551    ibevel,ibevel1,ibevel2,
 552    height,thickness,
 553    higbee,higbee1,higbee2,
 554    anchor, spin, orient
 555) {
 556    dummy = assert(num_defined([pitch,tpi])==1,"Must give exactly one of pitch and tpi");
 557    pitch = is_undef(pitch) ? INCH/tpi : pitch;
 558    dummy2=assert(is_num(pitch) && pitch>=0);
 559    trapezoidal_threaded_nut(
 560        nutwidth=nutwidth, id=id, h=h, pitch=pitch,
 561        thread_depth = pitch/2, 
 562        thread_angle=29,shape=shape, 
 563        left_handed=left_handed,
 564        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
 565        ibevel=ibevel,ibevel1=ibevel1,ibevel2=ibevel2,
 566        height=height,thickness=thickness,
 567        starts=starts,
 568        higbee=higbee, higbee1=higbee1, higbee2=higbee2,
 569        anchor=anchor,
 570        spin=spin,
 571        orient=orient
 572    ) children();
 573}
 574
 575
 576
 577
 578// Section: Pipe Threading
 579
 580// Module: npt_threaded_rod()
 581// Usage:
 582//   npt_threaded_rod(size, [internal=], ...) [ATTACHMENTS];
 583// Description:
 584//   Constructs a standard NPT pipe end threading. If `internal=true`, creates a mask for making
 585//   internal pipe threads.  Tapers smaller upwards if `internal=false`.  Tapers smaller downwards
 586//   if `internal=true`.  If `hollow=true` and `internal=false`, then the pipe threads will be
 587//   hollowed out into a pipe with the apropriate internal diameter.
 588// Arguments:
 589//   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"
 590//   ---
 591//   left_handed = If true, create left-handed threads.  Default = false
 592//   bevel = if true, bevel the thread ends.  Default: false
 593//   bevel1 = if true bevel the bottom end.
 594//   bevel2 = if true bevel the top end. 
 595//   hollow = If true, create a pipe with the correct internal diameter.
 596//   internal = If true, make this a mask for making internal threads.
 597//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 598//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 599//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 600//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 601// Example(2D): The straight gray rectangle reveals the tapered threads.  
 602//   projection(cut=true) npt_threaded_rod(size=1/4, orient=BACK);
 603//   right(.533*INCH/2) color("gray") rect([2,0.5946*INCH],anchor=LEFT);
 604// Examples(Med):
 605//   npt_threaded_rod(size=3/8, $fn=72);
 606//   npt_threaded_rod(size=1/2, $fn=72, bevel=true);
 607//   npt_threaded_rod(size=1/2, left_handed=true, $fn=72);
 608//   npt_threaded_rod(size=3/4, hollow=true, $fn=96);
 609// Example:
 610//   diff("remove"){
 611//      cuboid([40,40,40])
 612//      tag("remove"){
 613//        up(.01)position(TOP)
 614//            npt_threaded_rod(size=3/4, $fn=96, internal=true, $slop=0.1, anchor=TOP);
 615//        cyl(d=3/4*INCH, l=42, $fn=32);
 616//      }
 617//   }
 618function npt_threaded_rod(
 619    size=1/2,
 620    left_handed=false,
 621    bevel,bevel1,bevel2,
 622    hollow=false,
 623    internal=false,
 624    anchor, spin, orient
 625)=no_function("npt_threaded_rod");
 626module npt_threaded_rod(
 627    size=1/2,
 628    left_handed=false,
 629    bevel,bevel1,bevel2,
 630    hollow=false,
 631    internal=false,
 632    anchor, spin, orient
 633) {
 634    assert(is_finite(size));
 635    assert(is_bool(left_handed));
 636    assert(is_undef(bevel) || is_bool(bevel));
 637    assert(is_bool(hollow));
 638    assert(is_bool(internal));
 639    assert(!(internal&&hollow), "Cannot created a hollow internal threads mask.");
 640    info_table = [
 641        // Size    len      OD    TPI
 642        [ 1/16,  [ 0.3896, 0.308, 27  ]],
 643        [ 1/8,   [ 0.3924, 0.401, 27  ]],
 644        [ 1/4,   [ 0.5946, 0.533, 18  ]],
 645        [ 3/8,   [ 0.6006, 0.668, 18  ]],
 646        [ 1/2,   [ 0.7815, 0.832, 14  ]],
 647        [ 3/4,   [ 0.7935, 1.043, 14  ]],
 648        [ 1,     [ 0.9845, 1.305, 11.5]],
 649        [ 1+1/4, [ 1.0085, 1.649, 11.5]],
 650        [ 1+1/2, [ 1.0252, 1.888, 11.5]],
 651        [ 2,     [ 1.0582, 2.362, 11.5]],
 652    ];
 653    info = [for (data=info_table) if(approx(size,data[0])) data[1]][0];
 654    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");
 655    l = INCH * info[0];
 656    d = INCH * info[1];
 657    pitch = INCH / info[2];
 658    rr = d/2;
 659    rr2 = rr - l/32;
 660    r1 = internal? rr2 : rr;
 661    r2 = internal? rr : rr2;
 662    depth = pitch * cos(30) * 5/8;
 663    profile = internal? [
 664        [-6/16, -depth/pitch],
 665        [-1/16,  0],
 666        [-1/32,  0.02],
 667        [ 1/32,  0.02],
 668        [ 1/16,  0],
 669        [ 6/16, -depth/pitch]
 670    ] : [
 671        [-7/16, -depth/pitch*1.07],
 672        [-6/16, -depth/pitch],
 673        [-1/16,  0],
 674        [ 1/16,  0],
 675        [ 6/16, -depth/pitch],
 676        [ 7/16, -depth/pitch*1.07]
 677    ];
 678    attachable(anchor,spin,orient, l=l, r1=r1, r2=r2) {
 679        difference() {
 680            generic_threaded_rod(
 681                d1=2*r1, d2=2*r2, l=l,
 682                pitch=pitch,
 683                profile=profile,
 684                left_handed=left_handed,
 685                bevel=bevel,bevel1=bevel1,bevel2=bevel2,
 686                internal=internal,
 687                higbee=true
 688            );
 689            if (hollow) cylinder(l=l+1, d=size*INCH, center=true);
 690        }
 691        children();
 692    }
 693}
 694
 695
 696
 697// Section: Buttress Threading
 698
 699// Module: buttress_threaded_rod()
 700// Usage:
 701//   buttress_threaded_rod(d, l|length, pitch, [internal=], ...) [ATTACHMENTS];
 702// Description:
 703//   Constructs a simple buttress threaded rod with a 45 degree angle.  The buttress thread or sawtooth thread has low friction and high loading
 704//   in one direction at the cost of higher friction and inferior loading in the other direction.  Buttress threads are sometimes used on
 705//   vises, which are loaded only in one direction.  
 706// Arguments:
 707//   d = Outer diameter of threaded rod.
 708//   l / length = length of threaded rod.
 709//   pitch = Thread spacing.
 710//   ---
 711//   left_handed = if true, create left-handed threads.  Default = false
 712//   starts = Number of lead starts.  Default: 1
 713//   bevel = if true, bevel the thread ends.  Default: false
 714//   bevel1 = if true bevel the bottom end.
 715//   bevel2 = if true bevel the top end. 
 716//   internal = If true, this is a mask for making internal threads.
 717//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 718//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 719//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 720//   d1 = Bottom outside diameter of threads.
 721//   d2 = Top outside diameter of threads.
 722//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 723//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 724//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 725//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 726// Example(2D):
 727//   projection(cut=true)
 728//       buttress_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
 729// Examples(Med):
 730//   buttress_threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1);
 731//   buttress_threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1);
 732function buttress_threaded_rod(
 733    d, l, pitch,
 734    left_handed=false,
 735    bevel,bevel1,bevel2,
 736    internal=false,
 737    higbee, higbee1, higbee2,
 738    d1,d2,starts=1,length, 
 739    anchor, spin, orient
 740) = no_function("buttress_threaded_rod");
 741module buttress_threaded_rod(
 742    d, l, pitch,
 743    left_handed=false,
 744    bevel,bevel1,bevel2,
 745    internal=false,
 746    higbee,higbee1,higbee2,
 747    d1,d2,starts=1,length, 
 748    anchor, spin, orient
 749) {
 750    depth = pitch * 3/4;
 751    profile = [
 752        [ -7/16, -0.75],
 753        [  5/16,  0],
 754        [  7/16,  0],
 755        [  7/16, -0.75],
 756        [  1/ 2, -0.77],
 757    ];
 758    higbee2 = !internal || (!higbee && !higbee2) ? higbee2
 759            : let (higval = first_defined([higbee2,higbee]))
 760              is_num(higval) ? higval + 270
 761            : 270;
 762    generic_threaded_rod(
 763        d=d, l=l, pitch=pitch,
 764        profile=profile, 
 765        left_handed=left_handed,
 766        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
 767        internal=internal,
 768        higbee=higbee,
 769        higbee1=higbee1,
 770        higbee2=higbee2,
 771        d1=d1,d2=d2,
 772        anchor=anchor,length=length, 
 773        spin=spin,starts=starts,
 774        orient=orient
 775    ) children();
 776}
 777
 778
 779
 780// Module: buttress_threaded_nut()
 781// Usage:
 782//   buttress_threaded_nut(nutwidth, id, h|height|thickness, pitch, ...) [ATTACHMENTS];
 783// Description:
 784//   Constructs a hexagonal or square nut for a simple buttress threaded screw rod.  
 785// Arguments:
 786//   nutwidth = diameter of the nut.
 787//   id = diameter of threaded rod to screw onto.
 788//   h = height/thickness of nut.
 789//   pitch = Thread spacing. 
 790//   ---
 791//   shape = specifies shape of nut, either "hex" or "square".  Default: "hex"
 792//   left_handed = if true, create left-handed threads.  Default = false
 793//   starts = The number of lead starts.  Default: 1
 794//   bevel = if true, bevel the outside of the nut.  Default: true for hex nuts, false for square nuts
 795//   bevel1 = if true, bevel the outside of the nut bottom.
 796//   bevel2 = if true, bevel the outside of the nut top. 
 797//   bevang = set the angle for the outside nut bevel.  Default: 30
 798//   ibevel = if true, bevel the inside (the hole).   Default: true
 799//   ibevel1 = if true bevel the inside, bottom end.
 800//   ibevel2 = if true bevel the inside, top end.
 801//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 802//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 803//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 804//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 805//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 806//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 807//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 808// Examples(Med):
 809//   buttress_threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25, left_handed=true, $slop=0.05, $fa=1, $fs=1);
 810function buttress_threaded_nut(
 811    nutwidth, id, h,
 812    pitch, shape="hex", left_handed=false,
 813    bevel,bevel1,bevel2,bevang=30,starts=1,
 814    ibevel,ibevel1,ibevel2,height,thickness,
 815    higbee,higbee1,higbee2,
 816    anchor, spin, orient
 817) = no_function("buttress_threaded_nut");
 818module buttress_threaded_nut(
 819    nutwidth, id, h,
 820    pitch, shape="hex", left_handed=false,
 821    bevel,bevel1,bevel2,bevang=30,starts=1,
 822    ibevel,ibevel1,ibevel2,height,thickness,
 823    higbee,higbee1,higbee2,
 824    anchor, spin, orient
 825) {
 826    depth = pitch * 3/4;
 827    profile = [
 828        [ -7/16, -0.75],
 829        [  5/16,  0],
 830        [  7/16,  0],
 831        [  7/16, -0.75],
 832        [  1/ 2, -0.77],
 833    ];
 834    higbee2 = !higbee && !higbee2 ? higbee2
 835            : let (higval = first_defined([higbee2,higbee]))
 836              is_num(higval) ? higval + 270
 837            : 270;
 838    generic_threaded_nut(
 839        nutwidth=nutwidth, id=id, h=h,
 840        pitch=pitch,
 841        profile=profile,
 842        shape=shape,
 843        left_handed=left_handed,starts=starts,
 844        bevel=bevel,bevel1=bevel1,bevel2=bevel2,bevang=bevang,
 845        ibevel=ibevel,ibevel1=ibevel1,ibevel2=ibevel2,
 846        higbee=higbee, higbee1=higbee1, higbee2=higbee2,
 847        anchor=anchor, spin=spin, height=height, thickness=thickness, 
 848        orient=orient
 849    ) children();
 850}
 851
 852
 853
 854// Section: Square Threading
 855
 856// Module: square_threaded_rod()
 857// Usage:
 858//   square_threaded_rod(d, l|length, pitch, [internal=], ...) [ATTACHMENTS];
 859// Description:
 860//   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.
 861//   They produce no radial load on the nut.  However, square threads cannot carry as much load as trapezoidal threads. 
 862// Arguments:
 863//   d = Outer diameter of threaded rod.
 864//   l / length = length of threaded rod.
 865//   pitch = Thread spacing.
 866//   ---
 867//   left_handed = if true, create left-handed threads.  Default = false
 868//   starts = The number of lead starts.  Default = 1
 869//   bevel = if true, bevel the thread ends.  Default: false
 870//   bevel1 = if true bevel the bottom end.
 871//   bevel2 = if true bevel the top end. 
 872//   internal = If true, this is a mask for making internal threads.
 873//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 874//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 875//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 876//   d1 = Bottom outside diameter of threads.
 877//   d2 = Top outside diameter of threads.
 878//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 879//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 880//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 881//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 882// Example(2D):
 883//   projection(cut=true)
 884//       square_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
 885// Examples(Med):
 886//   square_threaded_rod(d=10, l=20, pitch=2, starts=2, $fn=32);
 887function square_threaded_rod(
 888    d, l, pitch,
 889    left_handed=false,
 890    bevel,bevel1,bevel2,
 891    starts=1,
 892    internal=false,
 893    higbee, higbee1, higbee2,
 894    d1,d2,
 895    anchor, spin, orient
 896) = no_function("square_threaded_rod");
 897module square_threaded_rod(
 898    d, l, pitch,
 899    left_handed=false,
 900    bevel,bevel1,bevel2,
 901    starts=1,
 902    internal=false,
 903    higbee=0, higbee1, higbee2,
 904    d1,d2,length,
 905    anchor, spin, orient
 906) {
 907    trapezoidal_threaded_rod(
 908        d=d, l=l, pitch=pitch,
 909        thread_angle=0.1,
 910        left_handed=left_handed,
 911        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
 912        starts=starts,
 913        internal=internal,
 914        higbee=higbee,
 915        higbee1=higbee1,
 916        higbee2=higbee2,
 917        d1=d1,
 918        d2=d2,
 919        length=length,
 920        anchor=anchor,
 921        spin=spin,
 922        orient=orient
 923    ) children();
 924}
 925
 926
 927
 928// Module: square_threaded_nut()
 929// Usage:
 930//   square_threaded_nut(nutwidth, id, h|height|thickness, pitch, ...) [ATTACHMENTS];
 931// Description:
 932//   Constructs a hexagonal or square nut for a square profile threaded screw rod.  
 933// Arguments:
 934//   nutwidth = diameter of the nut.
 935//   id = diameter of threaded rod to screw onto.
 936//   h / height / thickness = height/thickness of nut.
 937//   pitch = Length between threads.
 938//   ---
 939//   shape = specifies shape of nut, either "hex" or "square".  Default: "hex"
 940//   left_handed = if true, create left-handed threads.  Default = false
 941//   starts = The number of lead starts.  Default = 1
 942//   bevel = if true, bevel the outside of the nut.  Default: true for hex nuts, false for square nuts
 943//   bevel1 = if true, bevel the outside of the nut bottom.
 944//   bevel2 = if true, bevel the outside of the nut top. 
 945//   bevang = set the angle for the outside nut bevel.  Default: 30
 946//   ibevel = if true, bevel the inside (the hole).   Default: true
 947//   ibevel1 = if true bevel the inside, bottom end.
 948//   ibevel2 = if true bevel the inside, top end.
 949//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
 950//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
 951//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
 952//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 953//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 954//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 955//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
 956// Examples(Med):
 957//   square_threaded_nut(nutwidth=16, id=10, h=10, pitch=2, starts=2, $slop=0.1, $fn=32);
 958function square_threaded_nut(
 959    nutwidth, id, h,
 960    pitch,
 961    left_handed=false,
 962    bevel,bevel1,bevel2,bevang=30,
 963    ibevel,ibevel1,ibevel2,
 964    height,thickness,    
 965    starts=1,
 966    higbee,higbee1,higbee2,
 967    anchor, spin, orient
 968) = no_function("square_threaded_nut");
 969module square_threaded_nut(
 970    nutwidth, id, h,
 971    pitch,
 972    left_handed=false,
 973    bevel,bevel1,bevel2,bevang=30,
 974    ibevel,ibevel1,ibevel2,
 975    height,thickness,    
 976    higbee,higbee1,higbee2,
 977    starts=1,
 978    anchor, spin, orient
 979) {
 980    assert(is_num(pitch) && pitch>=0)
 981    trapezoidal_threaded_nut(
 982        nutwidth=nutwidth, id=id, h=h, pitch=pitch,
 983        thread_angle=0,
 984        left_handed=left_handed,
 985        bevel=bevel,bevel1=bevel1,bevel2=bevel2, bevang=bevang,
 986        ibevel=ibevel, ibevel1=ibevel1, ibevel2=ibevel2,
 987        height=height,thickness=thickness,
 988        starts=starts,
 989        higbee=higbee, higbee1=higbee1, higbee2=higbee2,
 990        anchor=anchor,
 991        spin=spin,
 992        orient=orient
 993    ) children();
 994}
 995
 996
 997// Section: Ball Screws
 998
 999// Module: ball_screw_rod()
1000// Usage:
1001//   ball_screw_rod(d, l|length, pitch, [ball_diam], [ball_arc], [internal=], ...) [ATTACHMENTS];
1002// Description:
1003//   Constructs a ball screw rod.  This type of rod is used with ball bearings.  
1004// Arguments:
1005//   d = Outer diameter of threaded rod.
1006//   l / length = length of threaded rod.
1007//   pitch = Thread spacing. Also, the diameter of the ball bearings used.
1008//   ball_diam = The diameter of the ball bearings to use with this ball screw.
1009//   ball_arc = The arc portion that should touch the ball bearings. Default: 120 degrees.
1010//   ---
1011//   left_handed = if true, create left-handed threads.  Default = false
1012//   starts = The number of lead starts.  Default = 1
1013//   bevel = if true, bevel the thread ends.  Default: false
1014//   bevel1 = if true bevel the bottom end.
1015//   bevel2 = if true bevel the top end. 
1016//   internal = If true, make this a mask for making internal threads.
1017//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
1018//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
1019//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
1020//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
1021// Example(2D): Thread Profile, ball_diam=4, ball_arc=100
1022//   projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=4, ball_arc=100, orient=BACK, $fn=24);
1023// Example(2D): Thread Profile, ball_diam=4, ball_arc=120
1024//   projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=4, ball_arc=120, orient=BACK, $fn=24);
1025// Example(2D): Thread Profile, ball_diam=3, ball_arc=120
1026//   projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=3, ball_arc=120, orient=BACK, $fn=24);
1027// Examples(Med):
1028//   ball_screw_rod(d=15, l=20, pitch=8, ball_diam=5, ball_arc=120, $fa=1, $fs=0.5);
1029//   ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, $fa=1, $fs=0.5);
1030//   ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, left_handed=true, $fa=1, $fs=0.5);
1031function ball_screw_rod(
1032    d, l, pitch, 
1033    ball_diam=5, ball_arc=100,
1034    starts=1,
1035    left_handed=false,
1036    internal=false,
1037    bevel,bevel1,bevel2, length, 
1038    anchor, spin, orient
1039) = no_function("ball_screw_rod");
1040module ball_screw_rod(
1041    d, l, pitch, 
1042    ball_diam=5, ball_arc=100,
1043    starts=1,
1044    left_handed=false,
1045    internal=false,
1046    bevel,bevel1,bevel2, length, 
1047    anchor, spin, orient
1048) {
1049    n = max(3,ceil(segs(ball_diam/2)*ball_arc/2/360));
1050    depth = ball_diam * (1-cos(ball_arc/2))/2;
1051    cpy = ball_diam/2/pitch*cos(ball_arc/2);
1052    profile = [
1053        each arc(n=n, d=ball_diam/pitch, cp=[-0.5,cpy], start=270, angle=ball_arc/2),
1054        each arc(n=n, d=ball_diam/pitch, cp=[+0.5,cpy], start=270-ball_arc/2, angle=ball_arc/2)
1055    ];
1056    generic_threaded_rod(
1057        d=d, l=l, pitch=pitch,
1058        profile=profile,
1059        left_handed=left_handed,
1060        starts=starts,
1061        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
1062        internal=internal,
1063        higbee=0, length=length, 
1064        anchor=anchor,
1065        spin=spin,
1066        orient=orient
1067    ) children();
1068}
1069
1070
1071
1072// Section: Generic Threading
1073
1074// Module: generic_threaded_rod()
1075// Usage:
1076//   generic_threaded_rod(d, l|length, pitch, profile, [internal=], ...) [ATTACHMENTS];
1077// Description:
1078//   Constructs a generic threaded rod using an arbitrary thread profile that you supply.  The rod can be tapered (e.g. for pipe threads).
1079//   For specific thread types use other modules that supply the appropriate profile.
1080//   .
1081//   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
1082//   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
1083//   makes your specified diameter the outer diameter of the threads).  
1084//   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
1085//   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).
1086//   Generally you should center the profile horizontally in the interval [-1/2, 1/2].
1087//   .
1088//   If internal is true then produce a thread mask to difference from an object.
1089//   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.
1090//   The diameter is also increased by `4 * $slop` to create clearance for threading by allowing a `2 * $slop` gap on each side. 
1091//   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
1092//   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.
1093//   .
1094//   Higbee or blunt start threading specifies that the thread ends abruptly at its full width instead of running off the end of the shaft and leaving a sharp edged partial
1095//   thread at the end of the screw.  This makes screws easier to start and
1096//   prevents cross threading.  If you set `higbee=true` then the blunt start applies to both ends.  The blunt start cuts the thread end in a single facet, 
1097//   so if you use lots of facets it will be close to perpendicular to the screw surface, but if you use fewer facets, it will be a more sloped cut.  
1098//   The place to cut the threads is calculated to try to leave a 1/4 thread gap from the end of the screw, but depending on your profile, you may
1099//   wish to adjust this.  If you set higbee to a numerical value it will be added to the computed higbee angle, so a positive value will cut the thread back farther
1100//   giving more space at the end.  Higbee works on both internal and external threads.  
1101// Arguments:
1102//   d = Outer diameter of threaded rod.
1103//   l / length = Length of threaded rod.
1104//   pitch = Thread spacing.
1105//   profile = A 2D path giving the shape of a thread
1106//   ---
1107//   left_handed = If true, create left-handed threads.  Default: false
1108//   starts = The number of lead starts.  Default: 1
1109//   bevel = if true, bevel the thread ends.  Default: false
1110//   bevel1 = if true bevel the bottom end.
1111//   bevel2 = if true bevel the top end. 
1112//   internal = If true, make this a mask for making internal threads.  Default: false
1113//   d1 = Bottom outside diameter of threads.
1114//   d2 = Top outside diameter of threads.
1115//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
1116//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
1117//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
1118//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
1119//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
1120//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
1121//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
1122// Example(2DMed): Example Tooth Profile
1123//   pitch = 2;
1124//   depth = pitch * cos(30) * 5/8;
1125//   profile = [
1126//       [-7/16, -depth/pitch*1.07],
1127//       [-6/16, -depth/pitch],
1128//       [-1/16,  0],
1129//       [ 1/16,  0],
1130//       [ 6/16, -depth/pitch],
1131//       [ 7/16, -depth/pitch*1.07]
1132//   ];
1133//   stroke(profile, width=0.02);
1134// Example:
1135//   pitch = 2;
1136//   depth = pitch * cos(30) * 5/8;
1137//   profile = [
1138//       [-7/16, -depth/pitch*1.07],
1139//       [-6/16, -depth/pitch],
1140//       [-1/16,  0],
1141//       [ 1/16,  0],
1142//       [ 6/16, -depth/pitch],
1143//       [ 7/16, -depth/pitch*1.07]
1144//   ];
1145//   generic_threaded_rod(d=10, l=40, pitch=2, profile=profile);
1146function generic_threaded_rod(
1147    d, l, pitch, profile,
1148    left_handed=false,
1149    bevel,
1150    bevel1, bevel2, 
1151    starts=1,
1152    internal=false,
1153    d1, d2, length,
1154    higbee, higbee1, higbee2,
1155    anchor, spin, orient
1156) = no_function("generic_threaded_rod");
1157module generic_threaded_rod(
1158    d, l, pitch, profile,
1159    left_handed=false,
1160    bevel,
1161    bevel1, bevel2, 
1162    starts=1,
1163    internal=false,
1164    d1, d2, length,
1165    higbee, higbee1, higbee2,
1166    anchor, spin, orient
1167) {
1168    l = one_defined([l,length],"l,length");
1169    bevel1 = first_defined([bevel1,bevel,false]);
1170    bevel2 = first_defined([bevel2,bevel,false]);
1171    thigbee1 = first_defined([higbee1,higbee,false]);
1172    thigbee2 = first_defined([higbee2,higbee,false]);
1173    // Zero higbee should be treated as "true", default angle, but it tests as false so adjust
1174    higbee1 = thigbee1==0 ? true : thigbee1;
1175    higbee2 = thigbee2==0 ? true : thigbee2;
1176    dummy0 = 
1177      assert(all_positive([pitch]),"Thread pitch must be a positive value")
1178      assert(all_positive([l]),"Length must be a postive value")
1179      assert(is_path(profile),"Profile must be a path")
1180      assert(is_finite(higbee1) || is_bool(higbee1), str("higbee",is_undef(higbee)?"1":""," must be boolean or a number"))
1181      assert(is_finite(higbee2) || is_bool(higbee2), str("higbee",is_undef(higbee)?"1":""," must be boolean or a number"))
1182      assert(is_bool(left_handed));
1183    r1 = get_radius(d1=d1, d=d);
1184    r2 = get_radius(d1=d2, d=d);
1185    sides = quantup(segs(max(r1,r2)), starts);
1186    rsc = internal? (1/cos(180/sides)) : 1;
1187    islop = internal? 2*get_slop() : 0;
1188    _r1 = r1 * rsc + islop;
1189    _r2 = r2 * rsc + islop;
1190    threads = quantup(l/pitch+2,1); // Was quantup(1/pitch+2,2*starts);
1191    dir = left_handed? -1 : 1;
1192    twist = 360 * l / pitch / starts;
1193    profile = !internal ? profile
1194            : [
1195                 for(entry=profile) if (entry.x>=0) [entry.x-1/2,entry.y], 
1196                 for(entry=profile) if (entry.x<0) [entry.x+1/2,entry.y]
1197              ];
1198    gap = 0.25;
1199    thread_minx = min(column(profile,0));
1200    thread_maxx = max(column(profile,0));
1201    // Compute higbee cut angles, or set to large negative value if higbee is not enabled
1202    higang1 = !higbee1 ? -1000
1203                       : (180+(gap-(thread_minx+.5))*360)/starts + (is_num(higbee1) ? higbee1 : 0);
1204    higang2 = !higbee2 ? -1000
1205                       : (180+(gap-(.5-thread_maxx))*360)/starts + (is_num(higbee2) ? higbee2 : 0);
1206    prof3d = path3d(profile);
1207    pdepth = -min(column(profile,1));
1208    pmax = pitch * max(column(profile,1));
1209    rmax = max(_r1,_r2)+pmax;
1210    depth = pdepth * pitch;
1211    dummy1 = assert(_r1>depth && _r2>depth, "Screw profile deeper than rod radius");
1212    map_threads = right((_r1 + _r2) / 2)                   // Shift profile out to thread radius
1213                * affine3d_skew(sxz=(_r2-_r1)/l)           // Skew correction for tapered threads
1214                * frame_map(x=[0,0,1], y=[1,0,0])          // Map profile to 3d, parallel to z axis
1215                * scale(pitch);                            // scale profile by pitch
1216    start_steps = sides / starts;
1217    thread_verts = [
1218         // Outer loop constructs a vertical column of the screw at each angle
1219         // covering 1/starts * 360 degrees of the cylinder.  
1220         for (step = [0:1:start_steps]) let(
1221             ang = 360 * step/sides,
1222             dz = step / start_steps,    // z offset for threads at this angle
1223             rot_prof = zrot(ang*dir)*map_threads,   // Rotate profile to correct angular location
1224             full_profile =  [   // profile for the entire rod
1225                 for (thread = [-threads/2:1:threads/2-1]) let(
1226                     tang = (thread/starts) * 360 + ang,
1227                     adjusted_prof3d = tang < -twist/2+higang1 || tang > twist/2-higang2 
1228                                           ? [for(v=prof3d) [v.x,internal?pmax/pitch:-pdepth,v.z]] 
1229                                     : prof3d
1230                 )
1231                 // The right movement finds the position of the thread along
1232                 // what will be the z axis after the profile is mapped to 3d                                           
1233                 each apply(right(dz + thread) , adjusted_prof3d)
1234             ]
1235         ) [
1236             [0, 0, -l/2-pitch],
1237             each apply(rot_prof , full_profile),
1238             [0, 0, +l/2+pitch]
1239         ]
1240    ];
1241    style=internal?"concave":"convex";
1242    
1243    thread_vnfs = vnf_join([
1244        // Main thread faces
1245        for (i=[0:1:starts-1])
1246            zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style=style)),
1247        // Top closing face(s) of thread                                 
1248        for (i=[0:1:starts-1]) let(
1249            rmat = zrot(i*360/starts),
1250            pts = deduplicate(list_head(thread_verts[0], len(prof3d)+1)),
1251            faces = [for (i=idx(pts,e=-2)) left_handed ? [0, i, i+1] : [0, i+1, i]]
1252        ) [apply(rmat,pts), faces],
1253        // Bottom closing face(s) of thread                                 
1254        for (i=[0:1:starts-1]) let(
1255            rmat = zrot(i*360/starts),
1256            pts = deduplicate(list_tail(last(thread_verts), -len(prof3d)-2)),
1257            faces = [for (i=idx(pts,e=-2)) left_handed ? [len(pts)-1, i+1, i] : [len(pts)-1, i, i+1]]
1258        ) [apply(rmat,pts), faces]
1259    ]);
1260
1261    slope = (_r1-_r2)/l;
1262    maxlen = 2*pitch;
1263
1264    attachable(anchor,spin,orient, r1=_r1, r2=_r2, l=l) {
1265        union(){
1266
1267          // This method is faster but more complex code and it produces green tops
1268          difference() {
1269              vnf_polyhedron(vnf_quantize(thread_vnfs),convexity=10);
1270
1271              if (!internal){
1272                  if (bevel1 || bevel2)
1273                      rotate_extrude(){
1274                         if (bevel2) polygon([[             0, l/2],
1275                                              [_r2+pmax-depth, l/2],
1276                                              [_r2+pmax+slope*depth, l/2-depth],
1277                                              [              rmax+1, l/2-depth],
1278                                              [rmax+1, l/2+maxlen],
1279                                              [     0, l/2+maxlen]]);
1280                         if (bevel1) polygon([[             0,-l/2],
1281                                              [_r1+pmax-depth, -l/2],
1282                                              [_r1+pmax-slope*depth, -l/2+depth],
1283                                              [              rmax+1, -l/2+depth],
1284                                              [rmax+1, -l/2-maxlen],
1285                                              [     0, -l/2-maxlen]]);
1286                      }
1287              }
1288              if (!bevel1 || internal)
1289                  down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
1290              if (!bevel2 || internal)
1291                  up(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=BOTTOM);
1292          }
1293
1294          /*  // slower, simpler approach for beveling
1295          intersection(){
1296              //vnf_validate(vnf_quantize(thread_vnfs), size=0.1);
1297              vnf_polyhedron(vnf_quantize(thread_vnfs), convexity=10);
1298              cyl(l=l, r1=_r1+pmax, r2=_r2+pmax, chamfer1=bevel1?depth:undef, chamfer2=bevel2?depth:undef);                  
1299          }
1300          */
1301
1302          // Add bevel for internal thread mask
1303          if (internal) {
1304            if (bevel1)
1305              down(l/2+.001)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
1306            if (bevel2)
1307              up(l/2+.001)cyl(l=depth, r2=_r2+pmax, r1=_r2+pmax+slope*depth-depth,anchor=TOP);
1308          }
1309        }
1310        children();
1311    }
1312}
1313
1314
1315
1316// Module: generic_threaded_nut()
1317// Usage:
1318//   generic_threaded_nut(nutwidth, id, h|height|thickness, pitch, profile, [$slop], ...) [ATTACHMENTS];
1319// Description:
1320//   Constructs a hexagonal or square nut for an generic threaded rod using a user-supplied thread profile.
1321//   See {{generic_threaded_rod()}} for details on the profile specification.  
1322// Arguments:
1323//   nutwidth = outer dimension of nut from flat to flat. 
1324//   id = diameter of threaded rod to screw onto.
1325//   h / height / thickness = height/thickness of nut.
1326//   pitch = Thread spacing.
1327//   profile = Thread profile.
1328//   ---
1329//   shape = specifies shape of nut, either "hex" or "square".  Default: "hex"
1330//   left_handed = if true, create left-handed threads.  Default = false
1331//   starts = The number of lead starts.  Default = 1
1332//   bevel = if true, bevel the outside of the nut.  Default: true for hex nuts, false for square nuts
1333//   bevel1 = if true, bevel the outside of the nut bottom.
1334//   bevel2 = if true, bevel the outside of the nut top. 
1335//   bevang = set the angle for the outside nut bevel.  Default: 30
1336//   ibevel = if true, bevel the inside (the hole).   Default: true
1337//   ibevel1 = if true bevel the inside, bottom end.
1338//   ibevel2 = if true bevel the inside, top end.
1339//   higbee = If true apply higbee thread truncation at both ends, or set to an angle to adjust higbee cut point.  Default: false
1340//   higbee1 = If true apply higbee thread truncation at bottom end, or set to an angle to adjust higbee cut point.
1341//   higbee2 = If true apply higbee thread truncation at top end, or set to an angle to adjust higbee cut point.
1342//   id1 = inner diameter at the bottom
1343//   id2 = inner diameter at the top
1344//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
1345//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
1346//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
1347//   $slop = The printer-specific slop value, which adds clearance (`4*$slop`) to internal threads.
1348function generic_threaded_nut(
1349    nutwidth,
1350    id,
1351    h,
1352    pitch,
1353    profile,
1354    shape="hex",
1355    left_handed=false,
1356    starts=1,
1357    bevel,bevel1,bevel2,bevang=30,
1358    ibevel, ibevel1, ibevel2,
1359    id1,id2, height, thickness, 
1360    higbee,higbee1,higbee2,
1361    anchor, spin, orient
1362) = no_function("generic_threaded_nut");
1363module generic_threaded_nut(
1364    nutwidth,
1365    id,
1366    h,
1367    pitch,
1368    profile,
1369    shape="hex",
1370    left_handed=false,
1371    starts=1,
1372    bevel,bevel1,bevel2,bevang=30,
1373    ibevel, ibevel1, ibevel2,
1374    id1,id2, height, thickness, 
1375    higbee,higbee1,higbee2,
1376    anchor, spin, orient
1377) {
1378    
1379    extra = 0.01;
1380    id1 = first_defined([id1,id]);
1381    id2 = first_defined([id2,id]);
1382    h = one_defined([h,height,thickness],"h,height,thickness");
1383    dummyA = assert(is_num(pitch) && pitch>=0, "pitch must be a nonnegative number")
1384             assert(is_num(h) && h>0, "height/thickness must be a positive number")
1385             assert(in_list(shape,["square","hex"]), "shape must be \"hex\" or \"square\"")
1386             assert(all_positive([id1,id2]), "Inner diameter(s) of nut must be positive number(s)");
1387    slope = (id2-id1)/h;
1388    full_id1 = id1-slope*extra/2;
1389    full_id2 = id2+slope*extra/2;
1390    ibevel1 = first_defined([ibevel1,ibevel,true]);
1391    ibevel2 = first_defined([ibevel2,ibevel,true]);
1392    bevel1 = first_defined([bevel1,bevel,shape=="hex"?true:false]);
1393    bevel2 = first_defined([bevel2,bevel,shape=="hex"?true:false]);
1394    depth = -pitch*min(column(profile,1));
1395    IBEV=0.05;
1396    vnf = linear_sweep(hexagon(id=nutwidth), height=h, center=true);
1397    attachable(anchor,spin,orient, size=shape=="square" ? [nutwidth,nutwidth,h] : undef, vnf=shape=="hex" ? vnf : undef) {
1398        difference() {
1399            _nutshape(nutwidth,h, shape,bevel1,bevel2);
1400            if (pitch==0) 
1401               cyl(l=h+extra, d1=full_id1+4*get_slop(), d2=full_id2+4*get_slop(), chamfer1=ibevel1?-IBEV*full_id1:undef, chamfer2=ibevel2?-IBEV*full_id2:undef);
1402            else
1403               generic_threaded_rod(
1404                     d1=full_id1,d2=full_id2,
1405                     l=h+extra,
1406                     pitch=pitch,
1407                     profile=profile,
1408                     left_handed=left_handed,
1409                     starts=starts,
1410                     internal=true,
1411                     bevel1=ibevel1,bevel2=ibevel2,
1412                     higbee=higbee, higbee1=higbee1, higbee2=higbee2
1413                );
1414        }
1415        children();
1416    }
1417}
1418
1419
1420module _nutshape(nutwidth, h, shape, bevel1, bevel2)
1421{
1422   bevel_d=0.9;
1423   intersection(){
1424       if (shape=="hex")
1425         cyl(d=nutwidth, circum=true, $fn=6, l=h, chamfer1=bevel1?0:nutwidth*.01, chamfer2=bevel2?0:nutwidth*.01);
1426        //vnf_polyhedron(vnf);
1427       else
1428         cuboid([nutwidth,nutwidth,h],chamfer=nutwidth*.01, except=[if (bevel1) BOT, if(bevel2) TOP]);
1429       fn = quantup(segs(r=nutwidth/2),shape=="hex"?6:4);
1430       d = shape=="hex" ? 2*nutwidth/sqrt(3) : sqrt(2)*nutwidth;
1431       chamfsize = (d-nutwidth)/2/bevel_d;
1432       cyl(d=d*.99,h=h+.01,realign=true,circum=true,$fn=fn,chamfer1=bevel1?chamfsize:0,chamfer2=bevel2?chamfsize:0,chamfang=30);
1433   }
1434}
1435
1436
1437// Module: thread_helix()
1438// Usage:
1439//   thread_helix(d, pitch, [thread_depth], [flank_angle], [turns], [profile=], [left_handed=], [higbee=], [internal=]);
1440// Description:
1441//   Creates a right-handed helical thread with optional end tapering.  Unlike
1442//   {{generic_threaded_rod()}, this module just generates the thread, and you specify the total
1443//   angle of threading that you want, which makes it easy to put complete threads onto a longer
1444//   shaft.  It also optionally makes a finely divided taper at the thread ends.  However, it takes
1445//   2-3 times as long to render compared to {{generic_threaded_rod()}}.  This module was designed
1446//   to handle threads found in plastic and glass bottles.
1447//   .
1448//   You can specify a thread_depth and flank_angle, in which case you get a symmetric trapezoidal
1449//   thread, whose inner diameter (the base of the threads for external threading) is d (so the
1450//   total diameter will be d + thread_depth).  This differs from the threaded_rod modules, where
1451//   the specified diameter is the outer diameter.  Alternatively you can give a profile, following
1452//   the same rules as for general_threaded_rod.  The Y=0 point will align with the specified
1453//   diameter, and the profile should range in X from -1/2 to 1/2.  You cannot specify both the
1454//   profile and the thread_depth or flank_angle.
1455//   .
1456//   Unlike {{generic_threaded_rod()}, when internal=true this module generates the threads, not a thread mask.
1457//   The profile needs to be inverted to produce the proper thread form.  If you use the built-in trapezoidal
1458//   thread you get the inverted thread, designed so that the inner diameter is d.  If you supply a custom profile
1459//   you must invert it yourself to get internal threads.  With adequate clearance
1460//   this thread will mate with the thread that uses the same parameters but has internal=false.  Note that
1461//   unlike the threaded_rod modules, thread_helix does not adjust the diameter for faceting, nor does it
1462//   subtract any $slop for clearance.  
1463//   .
1464//   The taper options specify tapering at of the threads at each end, and is given as the linear distance
1465//   over which to taper.  If taper is positive the threads are lengthened by the specified distance; if taper
1466//   is negative, the taper is included in the thread length specified by `turns`.  Tapering works on both internal and external threads.  
1467// Arguments:
1468//   d = Inside base diameter of threads.  Default: 10
1469//   pitch = Distance between threads.  Default: 2
1470//   thread_depth = Depth of threads from top to bottom.
1471//   flank_angle = Angle of thread faces to plane perpendicular to screw.  Default: 15 degrees.
1472//   turns = Number of revolutions to rotate thread around. 
1473//   ---
1474//   profile = If an asymmetrical thread profile is needed, it can be specified here.
1475//   starts = The number of thread starts.  Default: 1
1476//   left_handed = If true, thread has a left-handed winding.
1477//   internal = if true make internal threads.  The only effect this has is to change how the threads taper if tapering is selected. When true, threads taper towards the outside; when false, they taper towards the inside.  Default: false
1478//   d1 = Bottom inside base diameter of threads.
1479//   d2 = Top inside base diameter of threads.
1480//   taper = Length of tapers for thread ends.  Positive to add taper to threads, negative to taper within specified length.  Default: 0
1481//   taper1 = Length of taper for bottom thread end
1482//   taper2 = Length of taper for top thread end
1483//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
1484//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
1485//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
1486// Example(2DMed): Typical Tooth Profile
1487//   pitch = 2;
1488//   depth = pitch * cos(30) * 5/8;
1489//   profile = [
1490//       [-6/16, 0           ],
1491//       [-1/16, depth/pitch ],
1492//       [ 1/16, depth/pitch ],
1493//       [ 6/16, 0           ],
1494//   ];
1495//   stroke(profile, width=0.02);
1496// Figure(2D,Med):
1497//   pa_delta = tan(15)/4;
1498//      rr1 = -1/2;
1499//      z1 = 1/4-pa_delta;
1500//      z2 = 1/4+pa_delta;
1501//      profile = [
1502//                  [-z2, rr1],
1503//                  [-z1,  0],
1504//                  [ z1,  0],
1505//                  [ z2, rr1],
1506//                ];
1507//      fullprofile = 50*left(1/2,p=concat(profile, right(1, p=profile)));
1508//      stroke(fullprofile,width=1);
1509//      dir = fullprofile[2]-fullprofile[3];
1510//      dir2 = fullprofile[5]-fullprofile[4];
1511//      curve = arc(15,angle=[75,87],r=40 /*67.5*/);
1512//      avgpt = mean([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2]);
1513//      color("red"){
1514//       stroke([fullprofile[4]+[0,1], fullprofile[4]+[0,37]], width=1);
1515//       stroke([fullprofile[5]+.1*dir2, fullprofile[5]+.4*dir2], width=1);
1516//       stroke(move(-curve[0]+avgpt,p=curve), width=0.71,endcaps="arrow2");
1517//       right(14)back(19)text("flank",size=4,halign="center");
1518//       right(14)back(14)text("angle",size=4,halign="center");
1519//      }
1520// Examples:
1521//   thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=2.5, $fn=72);
1522//   thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=2.5, taper=1, $fn=72);
1523//   thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=2, taper=2, internal=true, $fn=72);
1524//   thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=1, left_handed=true, taper=1, $fn=36);
1525function thread_helix(
1526    d, pitch, thread_depth, flank_angle, turns,
1527    profile, starts=1, left_handed=false, internal=false,
1528    d1, d2, taper, taper1, taper2,
1529    anchor, spin, orient
1530) = no_function("thread_helix");
1531module thread_helix(
1532    d, pitch, thread_depth, flank_angle, turns=2,
1533    profile, starts=1, left_handed=false, internal=false,
1534    d1, d2, taper, taper1, taper2,
1535    anchor, spin, orient
1536) {
1537    dummy1=assert(is_undef(profile) || !any_defined([thread_depth, flank_angle]),"Cannot give thread_depth or flank_angle with a profile");
1538    h = pitch*starts*turns;
1539    r1 = get_radius(d1=d1, d=d, dflt=10);
1540    r2 = get_radius(d1=d2, d=d, dflt=10);
1541    profile = is_def(profile) ? profile :
1542        let(
1543            tdp = thread_depth / pitch,
1544            dz = tdp * tan(flank_angle),
1545            cap = (1 - 2*dz)/2
1546        )
1547        internal?
1548          [
1549            [-cap/2-dz, tdp],
1550            [-cap/2,    0  ],
1551            [+cap/2,    0  ],
1552            [+cap/2+dz, tdp],
1553          ]
1554        :
1555          [
1556            [+cap/2+dz, 0  ],
1557            [+cap/2,    tdp],
1558            [-cap/2,    tdp],
1559            [-cap/2-dz, 0  ],
1560          ];
1561    pline = mirror([-1,1],  p = profile * pitch);
1562    dir = left_handed? -1 : 1;
1563    attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) {
1564        zrot_copies(n=starts) {
1565            spiral_sweep(pline, h=h, r1=r1, r2=r2, turns=turns*dir, taper=taper, taper1=taper1, taper2=taper2, internal=internal, anchor=CENTER);
1566        }
1567        children();
1568    }
1569}
1570
1571
1572
1573// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
1574
1575// Questions
1576//   Should nut modules take d1/d2 for tapered nuts?
1577//
1578// Need explanation of what exactly the diff is between threaded_rod and helix_threads.
1579// Higbee is different, angle in one and length in another.  Need to reconcile