From ce0b4e9d323dea11be48f6845dfa1b1afa7e5df7 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sun, 22 Aug 2021 21:53:08 -0400
Subject: [PATCH 01/19]    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

---
 paths.scad     |   19 +-
 screws.scad    |   50 +--
 threading.scad | 1162 +++++++++++++++++++++++++++++-------------------
 vnf.scad       |    2 +-
 4 files changed, 734 insertions(+), 499 deletions(-)

diff --git a/paths.scad b/paths.scad
index 31f963e..8522a7a 100644
--- a/paths.scad
+++ b/paths.scad
@@ -1040,6 +1040,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.  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,7 +1050,11 @@ 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) {
+module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal=false, anchor, spin=0, orient=UP) {
+    bounds = pointlist_bounds(poly);
+    yctr = (bounds[0].y+bounds[1].y)/2;
+    xmin = bounds[0].x;
+    xmax = bounds[1].x;
     poly = path3d(poly);
     anchor = get_anchor(anchor,center,BOT,BOT);
     r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=50);
@@ -1062,7 +1069,6 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     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)],
@@ -1071,9 +1077,11 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     ]);
 
     us = [
-        for (i=[0:higsteps1/10:higsteps1]) i,
+        0, 
+        for (i=[higsteps1/10:higsteps1/10:higsteps1]) i,
         for (i=[higsteps1+1:1:steps-higsteps2-1]) i,
-        for (i=[steps-higsteps2:higsteps2/10:steps]) i,
+        for (i=[steps-higsteps2:higsteps2/10:steps-higsteps2/10]) i,
+        steps                                               
     ];
     zang = atan2(r2-r1,h);
     points = [
@@ -1086,7 +1094,8 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
                 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]),
+                //affine3d_scale([hsc,lerp(hsc,1,0.25),1]),
+                scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]),
             pts = apply(mat, poly)
         ) pts
     ];
diff --git a/screws.scad b/screws.scad
index b66674d..2da1ee3 100644
--- a/screws.scad
+++ b/screws.scad
@@ -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 bccf180..4f9fee9 100644
--- a/threading.scad
+++ b/threading.scad
@@ -9,6 +9,283 @@
 
 // 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 value is 0 at the peak and, due to scaling by the pitch, `-depth/pitch` in the valleys.  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);
+    echo(threads=threads);
+    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");
+    skew_mat = affine3d_skew(sxz=(_r2-_r1)/l);
+    map3d_and_scale = 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,           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 = [
+         // Outer loop constructs a vertical column of the screw at each angle
+         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)            // Rotate profile to correct angular location
+                        * right((_r1 + _r2) / 2) // Shift profile out to thread radius
+                        * skew_mat               // Skew correction for tapered threads
+                        * map3d_and_scale,       // Map profile into 3d
+             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
+                     //,eff=echo(tang=tang, twist=twist)
+                 )
+                 // 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([
+        for (i=[0:1:starts-1])
+            zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style=style)),
+        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]
+    ]);
+
+    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_validate(vnf_quantize(thread_vnfs), size=0.1);
+              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)
+                    down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
+                  if (!bevel2)
+                    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 threads
+          if (internal) {
+            if (bevel1)
+              down(l/2)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
+            if (bevel2)
+              up(l/2)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;
+    echo(id1=full_id1,id2=full_id2);
+    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=]);
@@ -75,35 +352,60 @@ module thread_helix(
     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();
     }
 }
 
 
-
 // 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.
+//   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 = Length between threads.
-//   thread_angle = The pressure angle profile angle of the threads.  Default = 14.5 degree ACME profile.
+//   pitch = Thread spacing. 
+//   thread_angle = Angle between two thread faces.  Default: 30
+//   thread_depth = Depth of threads.  Default: pitch/2
 //   ---
-//   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.
+//   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)
@@ -113,233 +415,243 @@ module thread_helix(
 //   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);
+//   $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);
 //   }
-// 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,
+    d, l, pitch,
+    thread_angle=30,
     thread_depth=undef,
     left_handed=false,
-    bevel=false,
+    bevel,bevel1,bevel2,
     starts=1,
-    profile,
     internal=false,
-    d1, d2,
-    higbee, higbee1, higbee2,
+    higbee, higbee1, higbee2,d1,d2,
     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);
-
+    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 = (
-        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();
-    }
+    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 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.
+//   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 = 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.
+//   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: 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.
+//   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 to make parts fit just right.
+//   $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.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);
+//   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=17.4,
-    id=10,
-    h=10,
-    pitch=2,
-    thread_depth=undef,
-    thread_angle=15,
-    profile=undef,
+    od,
+    id,
+    h,
+    pitch,
+    thread_angle=30,
+    thread_depth,
     left_handed=false,
     starts=1,
-    bevel=true,
+    bevel,bevel1,bevel2,
+    id1,id2,
     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();
-    }
+    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();
 }
 
 
-// Section: Triangular Threading
+
+
+// Section: ACME Trapezoidal Threading
+
+// 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: Standard (UTS/ISO) 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.
+//   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.
+//   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.
@@ -349,48 +661,58 @@ module trapezoidal_threaded_nut(
 //   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.
+//   $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=2, orient=BACK);
+//       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=100, pitch=2,
+    d, l, pitch,
     left_handed=false,
-    bevel=false,
+    bevel,bevel1,bevel2,
     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],
+    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/pitch]
+        [ 6/16, -depth]
     ] : [
-        [-7/16, -depth/pitch*1.07],
-        [-6/16, -depth/pitch],
+        [-7/16, -depth*1.07],
+        [-6/16, -depth],
         [-1/16,  0],
         [ 1/16,  0],
-        [ 6/16, -depth/pitch],
-        [ 7/16, -depth/pitch*1.07]
+        [ 6/16, -depth],
+        [ 7/16, -depth*1.07]
     ];
-    trapezoidal_threaded_rod(
-        d=d, d1=d1, d2=d2, l=l,
+    generic_threaded_rod(
+        d=basic ? d : d[2], d1=d1, d2=d2, l=l,
         pitch=pitch,
-        thread_depth=depth,
-        thread_angle=30,
         profile=profile,
         left_handed=left_handed,
-        bevel=bevel,
+        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
         internal=internal,
         higbee=higbee,
         higbee1=higbee1,
@@ -405,24 +727,27 @@ module threaded_rod(
 
 // 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.
+//   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 to make parts fit just right.
+//   $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, left_handed=true, $slop=0.2, $fa=1, $fs=1);
+//   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=16, id=10, h=10,
-    pitch=2, left_handed=false, bevel=false,
+    od, id, h,
+    pitch, left_handed=false, bevel, bevel1, bevel2, 
     anchor, spin, orient
 ) {
     depth = pitch * cos(30) * 5/8;
@@ -434,12 +759,12 @@ module threaded_nut(
         [ 1/16,  0],
         [ 6/16, -depth/pitch]
     ];
-    trapezoidal_threaded_nut(
+    generic_threaded_nut(
         od=od, id=id, h=h,
-        pitch=pitch, thread_angle=30,
+        pitch=pitch,
         profile=profile,
         left_handed=left_handed,
-        bevel=bevel,
+        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
         anchor=anchor, spin=spin,
         orient=orient
     ) children();
@@ -456,36 +781,50 @@ module threaded_nut(
 //   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
+//   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 to make parts fit just right.
-// Example(2D):
+//   $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);
+//   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=false,
+    bevel,bevel1,bevel2,
     hollow=false,
     internal=false,
     anchor, spin, orient
 ) {
     assert(is_finite(size));
     assert(is_bool(left_handed));
-    assert(is_bool(bevel));
+    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    OD      len    TPI
+        // 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  ]],
@@ -499,10 +838,10 @@ module npt_threaded_rod(
     ];
     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);
+    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;
@@ -524,20 +863,17 @@ module npt_threaded_rod(
     ];
     attachable(anchor,spin,orient, l=l, r1=r1, r2=r2) {
         difference() {
-            trapezoidal_threaded_rod(
+            generic_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,
+                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();
+            if (hollow) cylinder(l=l+1, d=size*INCH, center=true);
+            else nil();
         }
         children();
     }
@@ -549,19 +885,28 @@ module npt_threaded_rod(
 
 // Module: buttress_threaded_rod()
 // Description:
-//   Constructs a simple buttress threaded screw rod.  This method
-//   makes much smoother threads than the naive linear_extrude method.
+//   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 = Length between threads.
+//   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 to make parts fit just right.
+//   $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);
@@ -571,8 +916,12 @@ module npt_threaded_rod(
 module buttress_threaded_rod(
     d=10, l=100, pitch=2,
     left_handed=false,
-    bevel=false,
+    bevel,bevel1,bevel2,
     internal=false,
+    higbee=0,
+    higbee1,
+    higbee2,
+    d1,d2,
     anchor, spin, orient
 ) {
     depth = pitch * 3/4;
@@ -583,14 +932,16 @@ module buttress_threaded_rod(
         [  7/16, -0.75],
         [  1/ 2, -0.77],
     ];
-    trapezoidal_threaded_rod(
+    generic_threaded_rod(
         d=d, l=l, pitch=pitch,
-        thread_depth=depth,
-        thread_angle=30,
-        profile=profile,
+        profile=profile, 
         left_handed=left_handed,
-        bevel=bevel,
+        bevel=bevel,beve1=bevel1,bevel2=bevel2,
         internal=internal,
+        higbee=higbee,
+        higbee1=higbee1,
+        higbee2=higbee2,
+        d1=d1,d2=d2,
         anchor=anchor,
         spin=spin,
         orient=orient
@@ -601,25 +952,27 @@ module buttress_threaded_rod(
 
 // 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.
+//   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 = Length between threads.
+//   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 to make parts fit just right.
+//   $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.2, $fa=1, $fs=1);
+//   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=false,
+    bevel,bevel1,bevel2,
     anchor, spin, orient
 ) {
     depth = pitch * 3/4;
@@ -630,238 +983,72 @@ module buttress_threaded_nut(
         [  7/16, -0.75],
         [  1/ 2, -0.77],
     ];
-    trapezoidal_threaded_nut(
+    generic_threaded_nut(
         od=od, id=id, h=h,
-        pitch=pitch, thread_angle=30,
+        pitch=pitch,
         profile=profile,
-        thread_depth=pitch*3*sqrt(3)/8,
         left_handed=left_handed,
-        bevel=bevel,
+        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
         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.
+//   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 = Length between threads.
+//   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 to make parts fit just right.
+//   $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=10, l=100, pitch=2,
+    d, l, pitch,
     left_handed=false,
-    bevel=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,
+        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
@@ -872,35 +1059,38 @@ module square_threaded_rod(
 
 // 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.
+//   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 to make parts fit just right.
+//   $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.15, $fn=32);
+//   square_threaded_nut(od=16, id=10, h=10, pitch=2, starts=2, $slop=0.1, $fn=32);
 module square_threaded_nut(
-    od=17.4, id=10.5, h=10,
-    pitch=3.175,
+    od, id, h,
+    pitch,
     left_handed=false,
-    bevel=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,
+        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
         starts=starts,
         anchor=anchor,
         spin=spin,
@@ -913,21 +1103,24 @@ module square_threaded_nut(
 
 // Module: ball_screw_rod()
 // Description:
-//   Constructs a ball screw rod.  This method makes much smoother threads than the naive linear_extrude method.
+//   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 = Length between threads.  Also, the diameter of the ball bearings used.
+//   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 to make parts fit just right.
+//   $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
@@ -939,11 +1132,12 @@ module square_threaded_nut(
 //   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,
+    d, l, pitch, 
     ball_diam=5, ball_arc=100,
+    starts=1,
     left_handed=false,
     internal=false,
-    bevel=false,
+    bevel,bevel1,bevel2,
     anchor, spin, orient
 ) {
     n = ceil(segs(ball_diam/2)*ball_arc/2/360);
@@ -953,14 +1147,12 @@ module ball_screw_rod(
         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(
+    generic_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,
+        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
         internal=internal,
         higbee=0,
         anchor=anchor,
@@ -972,3 +1164,37 @@ module ball_screw_rod(
 
 
 // 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
+//
+//   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
+//   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.  
+
+
+// Questions:
+//   Existing code intersection with cylinder, so any profile over 0 gets removed.  Also---exact match intersection?  Better
+//      to intersect with a cube that is larger than the screw?  Cheaper....
+//   higbee can look bad due to edge issues in the VNF.  Possibly "convex" style is better.  "quincunx" may be best, but costly.
+//      buttress_threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1,higbee=15, $fn=32);
+//      Use quincunx when higbee is requested but min_edge otherwise?  
+//   Should nut modules take d1/d2 for tapered nuts?
+
+// Run time checks.  No difference for preview, but switching intersection to cube reduced render in the test from 11s to 8s.
+// Switching to doing the intersection with vnf_halfspace made preview slower, 3.8s instead of 1.6s but render faster (4s instead of 8s)
+// Using custom masks for beveling reduces render to 8s.  
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

From f3abf7f79c73d6f39a53a95ae9fc70c54090244d Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sun, 22 Aug 2021 22:08:45 -0400
Subject: [PATCH 02/19] testing git auth

---
 threading.scad | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/threading.scad b/threading.scad
index 4f9fee9..258965f 100644
--- a/threading.scad
+++ b/threading.scad
@@ -1177,15 +1177,16 @@ module ball_screw_rod(
 //   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
-//
-//   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
 //   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.  
-
+//   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
+//   
 
 // Questions:
 //   Existing code intersection with cylinder, so any profile over 0 gets removed.  Also---exact match intersection?  Better

From 6e0b966b72e154e814499a50e01f3c00bf6ab1a7 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sun, 22 Aug 2021 22:09:34 -0400
Subject: [PATCH 03/19] continued git auth testing

---
 threading.scad | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/threading.scad b/threading.scad
index 258965f..1466f46 100644
--- a/threading.scad
+++ b/threading.scad
@@ -1184,8 +1184,8 @@ module ball_screw_rod(
 //   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 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
 //   
 
 // Questions:

From 6da612f7e6ae2ed1f87c2b654aed2dc893cac15a Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Mon, 23 Aug 2021 23:03:25 -0400
Subject: [PATCH 04/19] Fixed higbee=0 problem in spiral_sweep, cleaned up
 thread_helix.  Made spiral sweep force its input to be clockwise to avoid
 reversed polyhedra.

Changed thread_angle in thread_helix to flank_angle, which is the
correct term for this angle, and then propagated change in
bottlecap.scad.
---
 bottlecaps.scad | 28 ++++++++++++++--------------
 paths.scad      | 29 ++++++++++++++++++-----------
 threading.scad  | 46 ++++++++++++++++++++++++++++------------------
 3 files changed, 60 insertions(+), 43 deletions(-)

diff --git a/bottlecaps.scad b/bottlecaps.scad
index fb83a0f..89bd3e9 100644
--- a/bottlecaps.scad
+++ b/bottlecaps.scad
@@ -53,7 +53,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 +113,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 +164,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 +192,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 +246,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 +306,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 +376,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 +428,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 +478,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 +527,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 +545,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 +587,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 +598,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 +689,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
                 );
diff --git a/paths.scad b/paths.scad
index 8522a7a..d43640f 100644
--- a/paths.scad
+++ b/paths.scad
@@ -1030,8 +1030,11 @@ 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 right-handed 3D spiral path
+//   of a given radius, height and twist.  The origin in the profile traces out the helix of the specified radius.  
+//   .
+//   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.
@@ -1055,7 +1058,7 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     yctr = (bounds[0].y+bounds[1].y)/2;
     xmin = bounds[0].x;
     xmax = bounds[1].x;
-    poly = path3d(poly);
+    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);
@@ -1063,17 +1066,19 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     steps = ceil(sides*(twist/360));
     higbee1 = first_defined([higbee1, higbee, 0]);
     higbee2 = first_defined([higbee2, higbee, 0]);
+    assert(higbee1>=0 && higbee2>=0);
     higang1 = 360 * higbee1 / (2 * r1 * PI);
     higang2 = 360 * higbee2 / (2 * r2 * PI);
     higsteps1 = ceil(higang1/360*sides);
     higsteps2 = ceil(higang2/360*sides);
+    assert(twist>0);
     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]
+        [-0.001,  higang1>0?0:1],
+        if (higang1>0) for (x=[0.125:0.125:1]) [      x*higang1, pow(x,1/2)],
+        if (higang2>0) for (x=[0.125:0.125:1]) [twist-x*higang2, pow(x,1/2)],
+        [twist+0.001,   higang2>0?0:1]
     ]);
 
     us = [
@@ -1089,11 +1094,13 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
             u = p / steps,
             a = twist * u,
             hsc = higsize(a),
+            
             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_skew_xz(xa=zang) *
                 //affine3d_scale([hsc,lerp(hsc,1,0.25),1]),
                 scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]),
             pts = apply(mat, poly)
@@ -1101,12 +1108,12 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     ];
 
     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=true, 
+        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*twist/360));
         children();
     }
 }
diff --git a/threading.scad b/threading.scad
index 1466f46..a39624e 100644
--- a/threading.scad
+++ b/threading.scad
@@ -15,7 +15,9 @@
 //   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 value is 0 at the peak and, due to scaling by the pitch, `-depth/pitch` in the valleys.  The segment between the end
+//   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].
 //   .
@@ -99,7 +101,6 @@ module generic_threaded_rod(
     _r1 = r1 * rsc + islop;
     _r2 = r2 * rsc + islop;
     threads = quantup(l/pitch+2,1); // Was quantup(1/pitch+2,2*starts);
-    echo(threads=threads);
     dir = left_handed? -1 : 1;
     twist = 360 * l / pitch / starts;
     higang1 = first_defined([higbee1, higbee, 0]);
@@ -141,7 +142,6 @@ module generic_threaded_rod(
                          : (higang2==0 && tang>=0)? 1
                          : lookup(tang, hig_table),
                      higscale = yscale(hsc,cp = -pdepth)  // Scale for higbee
-                     //,eff=echo(tang=tang, twist=twist)
                  )
                  // The right movement finds the position of the thread along
                  // what will be the z axis after the profile is mapped to 3d                                           
@@ -261,7 +261,6 @@ module generic_threaded_nut(
     slope = (id2-id1)/h;
     full_id1 = id1-slope*extra/2;
     full_id2 = id2+slope*extra/2;
-    echo(id1=full_id1,id2=full_id2);
     bevel1 = first_defined([bevel1,bevel,false]);
     bevel2 = first_defined([bevel2,bevel,false]);
     dummy1 = assert(is_num(pitch) && pitch>0);
@@ -288,14 +287,21 @@ module generic_threaded_nut(
 
 // 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.  You can specify a thread_depth and flank_angle, in which
+//   case you get a symmetric trapezoidal thread, whose base is at the diameter (so the total diameter will be d + thread_depth).  
+//   Atlernatively 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.  
+//   .
+//   Higbee specifies tapering applied to the ends of the extrusion and is given as the linear distance
+//   over which to taper.  
 // 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.
@@ -321,35 +327,39 @@ module generic_threaded_nut(
 //   ];
 //   stroke(profile, width=0.02);
 // 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);
 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, internal=internal, anchor=CENTER);

From 3c3e911db9f99b6b46ae4f885e334caa030df97e Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Tue, 24 Aug 2021 18:00:36 -0400
Subject: [PATCH 05/19] Changed order for docs purposes in threading.scad.
 Added SPI threading to bottlecaps.scad.

---
 bottlecaps.scad | 215 ++++++++++-
 threading.scad  | 964 ++++++++++++++++++++++++------------------------
 2 files changed, 696 insertions(+), 483 deletions(-)

diff --git a/bottlecaps.scad b/bottlecaps.scad
index 89bd3e9..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
 
@@ -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/threading.scad b/threading.scad
index a39624e..a383898 100644
--- a/threading.scad
+++ b/threading.scad
@@ -7,368 +7,144 @@
 //////////////////////////////////////////////////////////////////////
 
 
-// Section: Generic Threading
+// Section: Standard (UTS/ISO) Threading
 
-// Module: generic_threaded_rod()
+// Module: 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.
+//   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.
-//   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
+//   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. 
-//   starts = The number of lead starts.  Default: 1
-//   internal = If true, make this a mask for making internal threads.  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 = 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");
-    skew_mat = affine3d_skew(sxz=(_r2-_r1)/l);
-    map3d_and_scale = 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,           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 = [
-         // Outer loop constructs a vertical column of the screw at each angle
-         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)            // Rotate profile to correct angular location
-                        * right((_r1 + _r2) / 2) // Shift profile out to thread radius
-                        * skew_mat               // Skew correction for tapered threads
-                        * map3d_and_scale,       // Map profile into 3d
-             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([
-        for (i=[0:1:starts-1])
-            zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style=style)),
-        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]
-    ]);
-
-    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_validate(vnf_quantize(thread_vnfs), size=0.1);
-              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)
-                    down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
-                  if (!bevel2)
-                    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 threads
-          if (internal) {
-            if (bevel1)
-              down(l/2)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
-            if (bevel2)
-              up(l/2)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], [flank_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]);
-// Description:
-//   Creates a right-handed helical thread with optional end tapering.  You can specify a thread_depth and flank_angle, in which
-//   case you get a symmetric trapezoidal thread, whose base is at the diameter (so the total diameter will be d + thread_depth).  
-//   Atlernatively 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.  
-//   .
-//   Higbee specifies tapering applied to the ends of the extrusion and is given as the linear distance
-//   over which to taper.  
-// 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.
-//   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.
-//   starts = The number of thread starts.  Default: 1
-//   left_handed = If true, thread has a left-handed winding.
-//   internal = If true, invert threads for internal threading.
-//   d1 = Bottom inside base diameter of threads.
-//   d2 = Top inside base 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`
-// Example(2DMed): Typical Tooth Profile
-//   pitch = 2;
-//   depth = pitch * cos(30) * 5/8;
-//   profile = [
-//       [-6/16, 0           ],
-//       [-1/16, depth/pitch ],
-//       [ 1/16, depth/pitch ],
-//       [ 6/16, 0           ],
-//   ];
-//   stroke(profile, width=0.02);
-// Example:
-//   thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=900, $fn=72);
-module thread_helix(
-    d, pitch, thread_depth, flank_angle, twist=720,
-    profile, starts=1, left_handed=false, internal=false,
-    d1, d2, higbee, higbee1, higbee2,
+//   $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(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);
-    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;
-    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, internal=internal, anchor=CENTER);
-        }
-        children();
-    }
+    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
@@ -539,10 +315,6 @@ module trapezoidal_threaded_nut(
 }
 
 
-
-
-// Section: ACME Trapezoidal Threading
-
 // Module: acme_threaded_rod()
 // Description:
 //   Constructs an ACME trapezoidal threaded screw rod.  This form has a 29 degree thread angle with a
@@ -645,141 +417,6 @@ module acme_threaded_nut(
 
 
 
-// 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: Pipe Threading
 
@@ -1173,6 +810,369 @@ module ball_screw_rod(
 
 
 
+// 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");
+    skew_mat = affine3d_skew(sxz=(_r2-_r1)/l);
+    map3d_and_scale = 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,           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 = [
+         // Outer loop constructs a vertical column of the screw at each angle
+         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)            // Rotate profile to correct angular location
+                        * right((_r1 + _r2) / 2) // Shift profile out to thread radius
+                        * skew_mat               // Skew correction for tapered threads
+                        * map3d_and_scale,       // Map profile into 3d
+             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([
+        for (i=[0:1:starts-1])
+            zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style=style)),
+        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]
+    ]);
+
+    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_validate(vnf_quantize(thread_vnfs), size=0.1);
+              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)
+                    down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
+                  if (!bevel2)
+                    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 threads
+          if (internal) {
+            if (bevel1)
+              down(l/2)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
+            if (bevel2)
+              up(l/2)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], [flank_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]);
+// Description:
+//   Creates a right-handed helical thread with optional end tapering.  You can specify a thread_depth and flank_angle, in which
+//   case you get a symmetric trapezoidal thread, whose base is at the diameter (so the total diameter will be d + thread_depth).  
+//   Atlernatively 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.  
+//   .
+//   Higbee specifies tapering applied to the ends of the extrusion and is given as the linear distance
+//   over which to taper.  
+// 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.
+//   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.
+//   starts = The number of thread starts.  Default: 1
+//   left_handed = If true, thread has a left-handed winding.
+//   internal = If true, invert threads for internal threading.
+//   d1 = Bottom inside base diameter of threads.
+//   d2 = Top inside base 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`
+// Example(2DMed): Typical Tooth Profile
+//   pitch = 2;
+//   depth = pitch * cos(30) * 5/8;
+//   profile = [
+//       [-6/16, 0           ],
+//       [-1/16, depth/pitch ],
+//       [ 1/16, depth/pitch ],
+//       [ 6/16, 0           ],
+//   ];
+//   stroke(profile, width=0.02);
+// Example:
+//   thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=900, $fn=72);
+module thread_helix(
+    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);
+    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;
+    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, internal=internal, anchor=CENTER);
+        }
+        children();
+    }
+}
+
+
+
 // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
 
 // Changes

From 8b23eb4951892bee6cb53eaf099ceefe1df3794c Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Tue, 24 Aug 2021 22:14:54 -0400
Subject: [PATCH 06/19] Fixed facet sampling bugs in spiral_sweep

---
 paths.scad     | 47 +++++++++++++++++++++++++----------------------
 threading.scad | 18 ++++++------------
 2 files changed, 31 insertions(+), 34 deletions(-)

diff --git a/paths.scad b/paths.scad
index d43640f..ee5acf2 100644
--- a/paths.scad
+++ b/paths.scad
@@ -1054,6 +1054,8 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
 //   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, internal=false, anchor, spin=0, orient=UP) {
+    higsample = 10;         // Oversample factor for higbee tapering
+    dummy1=assert(twist>0);
     bounds = pointlist_bounds(poly);
     yctr = (bounds[0].y+bounds[1].y)/2;
     xmin = bounds[0].x;
@@ -1063,17 +1065,19 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     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));
+    ang_step = 360/sides;
+    anglist = [for(ang = [0:ang_step:twist-EPSILON]) ang,
+               twist];
     higbee1 = first_defined([higbee1, higbee, 0]);
     higbee2 = first_defined([higbee2, higbee, 0]);
-    assert(higbee1>=0 && higbee2>=0);
     higang1 = 360 * higbee1 / (2 * r1 * PI);
     higang2 = 360 * higbee2 / (2 * r2 * PI);
-    higsteps1 = ceil(higang1/360*sides);
-    higsteps2 = ceil(higang2/360*sides);
-    assert(twist>0);
-    assert(higang1 < twist/2);
-    assert(higang2 < twist/2);
+    dummy2=assert(higbee1>=0 && higbee2>=0)
+           assert(higang1 < twist/2)
+           assert(higang2 < twist/2);
+    function polygon_r(N,theta) =
+        let( alpha = 360/N )
+        cos(alpha/2)/(cos(posmod(theta,alpha)-alpha/2));
     function higsize(a) = lookup(a,[
         [-0.001,  higang1>0?0:1],
         if (higang1>0) for (x=[0.125:0.125:1]) [      x*higang1, pow(x,1/2)],
@@ -1081,26 +1085,25 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
         [twist+0.001,   higang2>0?0:1]
     ]);
 
-    us = [
-        0, 
-        for (i=[higsteps1/10:higsteps1/10:higsteps1]) i,
-        for (i=[higsteps1+1:1:steps-higsteps2-1]) i,
-        for (i=[steps-higsteps2:higsteps2/10:steps-higsteps2/10]) i,
-        steps                                               
-    ];
-    zang = atan2(r2-r1,h);
+    interp_ang = [
+                  for(i=idx(anglist,e=-2))
+                      each lerpn(anglist[i],anglist[i+1],
+                                 (higang1<anglist[i+1] || (twist-higang2>anglist[i])) ? ceil((anglist[i+1]-anglist[i])/ang_step*higsample)
+                                                                                      : 1,
+                                 endpoint=false),
+                  last(anglist)
+                 ];
+    echo(interp_ang=interp_ang,twist=twist);
+    skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h));
     points = [
-        for (p = us) let (
-            u = p / steps,
-            a = twist * u,
+        for (a = interp_ang) let (
             hsc = higsize(a),
-            
+            u = a/twist,
             r = lerp(r1,r2,u),
-            
             mat = affine3d_zrot(a) *
-                affine3d_translate([r, 0, h * (u-0.5)]) *
+                affine3d_translate([polygon_r(sides,a)*r, 0, h * (u-0.5)]) *
                 affine3d_xrot(90) *
-                affine3d_skew_xz(xa=zang) *
+                skewmat *
                 //affine3d_scale([hsc,lerp(hsc,1,0.25),1]),
                 scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]),
             pts = apply(mat, poly)
diff --git a/threading.scad b/threading.scad
index a383898..096bd3f 100644
--- a/threading.scad
+++ b/threading.scad
@@ -1087,7 +1087,6 @@ module generic_threaded_nut(
 }
 
 
-
 // Module: thread_helix()
 // Usage:
 //   thread_helix(d, pitch, [thread_depth], [flank_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]);
@@ -1196,16 +1195,11 @@ module thread_helix(
 //
 //   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:
-//   Existing code intersection with cylinder, so any profile over 0 gets removed.  Also---exact match intersection?  Better
-//      to intersect with a cube that is larger than the screw?  Cheaper....
-//   higbee can look bad due to edge issues in the VNF.  Possibly "convex" style is better.  "quincunx" may be best, but costly.
-//      buttress_threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1,higbee=15, $fn=32);
-//      Use quincunx when higbee is requested but min_edge otherwise?  
 //   Should nut modules take d1/d2 for tapered nuts?
-
-// Run time checks.  No difference for preview, but switching intersection to cube reduced render in the test from 11s to 8s.
-// Switching to doing the intersection with vnf_halfspace made preview slower, 3.8s instead of 1.6s but render faster (4s instead of 8s)
-// Using custom masks for beveling reduces render to 8s.  
+//
+// 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

From c0bf42836dade58446b8e604a8388d3cbcf055f3 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Tue, 24 Aug 2021 22:21:16 -0400
Subject: [PATCH 07/19] remove debug echo

---
 paths.scad | 1 -
 1 file changed, 1 deletion(-)

diff --git a/paths.scad b/paths.scad
index ee5acf2..c661f62 100644
--- a/paths.scad
+++ b/paths.scad
@@ -1093,7 +1093,6 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
                                  endpoint=false),
                   last(anglist)
                  ];
-    echo(interp_ang=interp_ang,twist=twist);
     skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h));
     points = [
         for (a = interp_ang) let (

From bfaf6990bc523e240967cf8fd6b4123519393527 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Thu, 26 Aug 2021 19:27:36 -0400
Subject: [PATCH 08/19] doc tweaks

---
 threading.scad | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/threading.scad b/threading.scad
index 096bd3f..2a7215c 100644
--- a/threading.scad
+++ b/threading.scad
@@ -927,9 +927,10 @@ module generic_threaded_rod(
         [+twist/2+0.00001, 0],
         [+twist,           0],
     ];
-    start_steps = floor(sides / starts);
+    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
@@ -998,7 +999,6 @@ module generic_threaded_rod(
                     down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
                   if (!bevel2)
                     up(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=BOTTOM);
-                  
               }
           }
 
@@ -1091,13 +1091,17 @@ module generic_threaded_nut(
 // Usage:
 //   thread_helix(d, pitch, [thread_depth], [flank_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]);
 // Description:
-//   Creates a right-handed helical thread with optional end tapering.  You can specify a thread_depth and flank_angle, in which
+//   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, if you use it for large length of thread it is much slower than generic_threaded_rod.
+//   .
+//   You can specify a thread_depth and flank_angle, in which
 //   case you get a symmetric trapezoidal thread, whose base is at the diameter (so the total diameter will be d + thread_depth).  
 //   Atlernatively 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.  
 //   .
-//   Higbee specifies tapering applied to the ends of the extrusion and is given as the linear distance
+//   Higbee specifies tapering applied to the ends of the threads and is given as the linear distance
 //   over which to taper.  
 // Arguments:
 //   d = Inside base diameter of threads.  Default: 10

From 33c63eb7b3a5b3180483060bcc6f9eab83c7a819 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Fri, 27 Aug 2021 22:21:33 -0400
Subject: [PATCH 09/19] doc tweak

---
 threading.scad | 1 +
 1 file changed, 1 insertion(+)

diff --git a/threading.scad b/threading.scad
index 2a7215c..7da9cdf 100644
--- a/threading.scad
+++ b/threading.scad
@@ -1206,4 +1206,5 @@ module thread_helix(
 //   Should nut modules take d1/d2 for tapered nuts?
 //
 // Need explanation of what exactly the diff is between threaded_rod and helix_threads.
+//    Kinda added this
 // Higbee is different, angle in one and length in another.  Need to reconcile

From 8abdd31bdf219c20793b7291ac6192bf2b252699 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Fri, 27 Aug 2021 22:24:36 -0400
Subject: [PATCH 10/19] correct the corrupt file

---
 threading.scad | 1270 ++++++++++++++++++------------------------------
 1 file changed, 481 insertions(+), 789 deletions(-)

diff --git a/threading.scad b/threading.scad
index dcebda5..8ad15ba 100644
--- a/threading.scad
+++ b/threading.scad
@@ -7,191 +7,103 @@
 //////////////////////////////////////////////////////////////////////
 
 
-// Section: Standard (UTS/ISO) Threading
+// Section: Generic Threading
 
-// Module: threaded_rod()
+// Module: thread_helix()
+// Usage:
+//   thread_helix(d, pitch, thread_depth, [thread_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]);
 // 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.  
+//   Creates a helical thread with optional end tapering.
 // 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.
+//   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.
+//   twist = Number of degrees to rotate thread around.  Default: 720 degrees.
+//   ---
+//   profile = If an asymmetrical thread profile is needed, it can be specified here.
+//   starts = The number of thread starts.  Default: 1
+//   left_handed = If true, thread has a left-handed winding.
+//   internal = If true, invert threads for internal threading.
+//   d1 = Bottom inside base diameter of threads.
+//   d2 = Top inside base 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,
+// Example(2DMed): Typical Tooth Profile
+//   pitch = 2;
+//   depth = pitch * cos(30) * 5/8;
+//   profile = [
+//       [-6/16, 0           ],
+//       [-1/16, depth/pitch ],
+//       [ 1/16, depth/pitch ],
+//       [ 6/16, 0           ],
+//   ];
+//   stroke(profile, width=0.02);
+// Example:
+//   thread_helix(d=10, pitch=2, thread_depth=0.75, thread_angle=15, twist=900, $fn=72);
+module thread_helix(
+    d, pitch=2, thread_depth, thread_angle=15, twist=720,
+    profile, starts=1, left_handed=false, 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();
+    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? [
+            [-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);
+        }
+        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");
-//  }
+//   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 = Thread spacing. 
-//   thread_angle = Angle between two thread faces.  Default: 30
-//   thread_depth = Depth of threads.  Default: pitch/2
+//   pitch = Length between threads.
+//   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
-//   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
+//   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)
@@ -201,43 +113,64 @@ module threaded_nut(
 //   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);
+//   $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, pitch,
-    thread_angle=30,
+    d, l=100, pitch=2,
+    thread_angle=15,
     thread_depth=undef,
     left_handed=false,
-    bevel,bevel1,bevel2,
+    bevel=false,
     starts=1,
+    profile,
     internal=false,
-    higbee, higbee1, higbee2,d1,d2,
+    d1, d2,
+    higbee, higbee1, higbee2,
     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");
+    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;
@@ -330,113 +263,140 @@ module trapezoidal_threaded_rod(
 
 // 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(). 
+//   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 = Thread spacing.
-//   thread_angle = Angle between two thread faces.  Default: 30
-//   thread_depth = Depth of the threads.  Default: pitch/2
-//   ---
+//   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: false
-//   bevel1 = if true bevel the bottom end.
-//   bevel2 = if true bevel the top end. 
+//   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, which adds clearance (`4*$slop`) to internal threads.
+//   $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.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);
+//   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,
-    id,
-    h,
-    pitch,
-    thread_angle=30,
-    thread_depth,
+    od=17.4,
+    id=10,
+    h=10,
+    pitch=2,
+    thread_depth=undef,
+    thread_angle=15,
+    profile=undef,
     left_handed=false,
     starts=1,
-    bevel,bevel1,bevel2,
-    id1,id2,
+    bevel=true,
     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();
+    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();
+    }
 }
 
 
-// Module: acme_threaded_rod()
+// Section: Triangular Threading
+
+// Module: threaded_rod()
 // Description:
-//   Constructs an ACME trapezoidal threaded screw rod.  This form has a 29 degree thread angle with a
-//   symmetric trapezoidal thread.  
+//   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.
-//   tpi = threads per inch.
-//   ---
-//   pitch = thread spacing (alternative to tpi)
-//   starts = The number of lead starts.  Default = 1
+//   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, this is a mask for making internal threads.
-//   higbee = Length to taper thread ends over.  Default: 0 (No higbee thread tapering)
+//   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.
+//   $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);
+//       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,
+//   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,bevel1,bevel2,
+    bevel=false,
     internal=false,
+    d1, d2,
     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;
+    _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, l=l, pitch=pitch,
-        thread_angle=29,
-        thread_depth=pitch/2,
-        starts=starts,
+        d=d, d1=d1, d2=d2, l=l,
+        pitch=pitch,
+        thread_depth=depth,
+        thread_angle=30,
+        profile=profile,
         left_handed=left_handed,
-        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
+        bevel=bevel,
         internal=internal,
+        higbee=higbee,
+        higbee1=higbee1,
+        higbee2=higbee2,
         anchor=anchor,
         spin=spin,
         orient=orient
@@ -445,53 +405,49 @@ module acme_threaded_rod(
 
 
 
-// Module: acme_threaded_nut()
+// Module: threaded_nut()
 // Description:
-//   Constructs a hex nut for an ACME threaded screw rod. 
+//   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.
-//   tpi = threads per inch
-//   ---
-//   pitch = Thread spacing (alternative to tpi)
+//   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.
+//   $slop = The printer-specific slop value to make parts fit just right.
 // 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,
+//   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
 ) {
-    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);
+    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_depth = pitch/2, 
-        thread_angle=29,
+        od=od, id=id, h=h,
+        pitch=pitch, thread_angle=30,
+        profile=profile,
         left_handed=left_handed,
-        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
-        starts=starts,
-        anchor=anchor,
-        spin=spin,
+        bevel=bevel,
+        anchor=anchor, spin=spin,
         orient=orient
     ) children();
 }
 
 
-
-
 // Section: Pipe Threading
 
 // Module: npt_threaded_rod()
@@ -502,50 +458,36 @@ module acme_threaded_nut(
 //   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. 
+//   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, which adds clearance (`4*$slop`) to internal threads.p
-// Example(2D): The straight gray rectangle reveals the tapered threads.  
+//   $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);
-//   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, $fn=72);
 //   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,
+    bevel=false,
     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(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
+        // 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  ]],
@@ -559,10 +501,10 @@ module npt_threaded_rod(
     ];
     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;
+    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;
@@ -584,17 +526,20 @@ module npt_threaded_rod(
     ];
     attachable(anchor,spin,orient, l=l, r1=r1, r2=r2) {
         difference() {
-            generic_threaded_rod(
+            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,bevel1=bevel1,bevel2=bevel2,
+                bevel=bevel,
                 internal=internal,
                 higbee=r1*PI/2
             );
-            if (hollow) cylinder(l=l+1, d=size*INCH, center=true);
-            else nil();
+            if (hollow) {
+                cylinder(l=l+1, d=size*INCH, center=true);
+            } else nil();
         }
         children();
     }
@@ -606,28 +551,19 @@ module npt_threaded_rod(
 
 // 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.  
+//   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 = Thread spacing.
-//   ---
+//   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, 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.
+//   $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);
@@ -637,12 +573,8 @@ module npt_threaded_rod(
 module buttress_threaded_rod(
     d=10, l=100, pitch=2,
     left_handed=false,
-    bevel,bevel1,bevel2,
+    bevel=false,
     internal=false,
-    higbee=0,
-    higbee1,
-    higbee2,
-    d1,d2,
     anchor, spin, orient
 ) {
     depth = pitch * 3/4;
@@ -653,16 +585,14 @@ module buttress_threaded_rod(
         [  7/16, -0.75],
         [  1/ 2, -0.77],
     ];
-    generic_threaded_rod(
+    trapezoidal_threaded_rod(
         d=d, l=l, pitch=pitch,
-        profile=profile, 
+        thread_depth=depth,
+        thread_angle=30,
+        profile=profile,
         left_handed=left_handed,
-        bevel=bevel,beve1=bevel1,bevel2=bevel2,
+        bevel=bevel,
         internal=internal,
-        higbee=higbee,
-        higbee1=higbee1,
-        higbee2=higbee2,
-        d1=d1,d2=d2,
         anchor=anchor,
         spin=spin,
         orient=orient
@@ -673,27 +603,25 @@ module buttress_threaded_rod(
 
 // Module: buttress_threaded_nut()
 // Description:
-//   Constructs a hex nut for a simple buttress threaded screw rod.  
+//   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 = Thread spacing. 
-//   ---
+//   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.
+//   $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.05, $fa=1, $fs=1);
+//   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,bevel1,bevel2,
+    bevel=false,
     anchor, spin, orient
 ) {
     depth = pitch * 3/4;
@@ -704,72 +632,238 @@ module buttress_threaded_nut(
         [  7/16, -0.75],
         [  1/ 2, -0.77],
     ];
-    generic_threaded_nut(
+    trapezoidal_threaded_nut(
         od=od, id=id, h=h,
-        pitch=pitch,
+        pitch=pitch, thread_angle=30,
         profile=profile,
+        thread_depth=pitch*3*sqrt(3)/8,
         left_handed=left_handed,
-        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
+        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.  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. 
+//   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 = Thread spacing.
-//   ---
+//   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
 //   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.
+//   $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, l, pitch,
+    d=10, l=100, pitch=2,
     left_handed=false,
-    bevel,bevel1,bevel2,
+    bevel=false,
     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,
+        bevel=bevel,
         starts=starts,
         internal=internal,
-        higbee=higbee,
-        higbee1=higbee1,
-        higbee2=higbee2,
-        d1=d1,
-        d2=d2,
         anchor=anchor,
         spin=spin,
         orient=orient
@@ -780,38 +874,35 @@ module square_threaded_rod(
 
 // Module: square_threaded_nut()
 // Description:
-//   Constructs a hex nut for a square profile threaded screw rod.  
+//   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
-//   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.
+//   $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.1, $fn=32);
+//   square_threaded_nut(od=16, id=10, h=10, pitch=2, starts=2, $slop=0.15, $fn=32);
 module square_threaded_nut(
-    od, id, h,
-    pitch,
+    od=17.4, id=10.5, h=10,
+    pitch=3.175,
     left_handed=false,
-    bevel,bevel1,bevel2,
+    bevel=false,
     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,
+        bevel=bevel,
         starts=starts,
         anchor=anchor,
         spin=spin,
@@ -824,24 +915,21 @@ module square_threaded_nut(
 
 // Module: ball_screw_rod()
 // Description:
-//   Constructs a ball screw rod.  This type of rod is used with ball bearings.  
+//   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 = Thread spacing. Also, the diameter of the ball bearings used.
+//   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
-//   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.
+//   $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
@@ -853,12 +941,11 @@ module square_threaded_nut(
 //   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, 
+    d=10, l=100, pitch=2, starts=1,
     ball_diam=5, ball_arc=100,
-    starts=1,
     left_handed=false,
     internal=false,
-    bevel,bevel1,bevel2,
+    bevel=false,
     anchor, spin, orient
 ) {
     n = ceil(segs(ball_diam/2)*ball_arc/2/360);
@@ -868,12 +955,14 @@ module ball_screw_rod(
         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(
+    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,bevel1=bevel1,bevel2=bevel2,
+        bevel=bevel,
         internal=internal,
         higbee=0,
         anchor=anchor,
@@ -884,401 +973,4 @@ module ball_screw_rod(
 
 
 
-// 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");
-    skew_mat = affine3d_skew(sxz=(_r2-_r1)/l);
-    map3d_and_scale = 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,           0],
-        [-twist/2-0.00001, 0],
-        [-twist/2+higang1, 1],
-        [+twist/2-higang2, 1],
-        [+twist/2+0.00001, 0],
-        [+twist,           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)            // Rotate profile to correct angular location
-                        * right((_r1 + _r2) / 2) // Shift profile out to thread radius
-                        * skew_mat               // Skew correction for tapered threads
-                        * map3d_and_scale,       // Map profile into 3d
-             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([
-        for (i=[0:1:starts-1])
-            zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style=style)),
-        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]
-    ]);
-
-    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_validate(vnf_quantize(thread_vnfs), size=0.1);
-              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)
-                    down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
-                  if (!bevel2)
-                    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 threads
-          if (internal) {
-            if (bevel1)
-              down(l/2)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
-            if (bevel2)
-              up(l/2)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], [flank_angle], [twist], [profile=], [left_handed=], [higbee=], [internal=]);
-// Description:
-//   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, if you use it for large length of thread it is much slower than generic_threaded_rod.
-//   .
-//   You can specify a thread_depth and flank_angle, in which
-//   case you get a symmetric trapezoidal thread, whose base is at the diameter (so the total diameter will be d + thread_depth).  
-//   Atlernatively 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.  
-//   .
-//   Higbee specifies tapering applied to the ends of the threads and is given as the linear distance
-//   over which to taper.  
-// 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.
-//   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.
-//   starts = The number of thread starts.  Default: 1
-//   left_handed = If true, thread has a left-handed winding.
-//   internal = If true, invert threads for internal threading.
-//   d1 = Bottom inside base diameter of threads.
-//   d2 = Top inside base 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`
-// Example(2DMed): Typical Tooth Profile
-//   pitch = 2;
-//   depth = pitch * cos(30) * 5/8;
-//   profile = [
-//       [-6/16, 0           ],
-//       [-1/16, depth/pitch ],
-//       [ 1/16, depth/pitch ],
-//       [ 6/16, 0           ],
-//   ];
-//   stroke(profile, width=0.02);
-// Example:
-//   thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=900, $fn=72);
-module thread_helix(
-    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);
-    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;
-    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, internal=internal, anchor=CENTER);
-        }
-        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.
-//    Kinda added this
-// Higbee is different, angle in one and length in another.  Need to reconcile

From 98e05731dae738700f7f0776800a20c4a01e2e4a Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Fri, 27 Aug 2021 23:08:09 -0400
Subject: [PATCH 11/19] Corruption fix, take 2

---
 screws.scad    |    2 +-
 threading.scad | 2043 +++++++++++++++++++++++++++---------------------
 2 files changed, 1139 insertions(+), 906 deletions(-)

diff --git a/screws.scad b/screws.scad
index 2da1ee3..e26a370 100644
--- a/screws.scad
+++ b/screws.scad
@@ -839,7 +839,7 @@ module screw_head(screw_info,details=false) {
 // Example(Med): Selected UTS (English) screws
 //   $fn=32;
 //   xdistribute(spacing=8){
-//     screw("#6", length=12);
+     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");
diff --git a/threading.scad b/threading.scad
index 8ad15ba..2a7215c 100644
--- a/threading.scad
+++ b/threading.scad
@@ -7,18 +7,1107 @@
 //////////////////////////////////////////////////////////////////////
 
 
+// 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,beve1=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");
+    skew_mat = affine3d_skew(sxz=(_r2-_r1)/l);
+    map3d_and_scale = 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,           0],
+        [-twist/2-0.00001, 0],
+        [-twist/2+higang1, 1],
+        [+twist/2-higang2, 1],
+        [+twist/2+0.00001, 0],
+        [+twist,           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)            // Rotate profile to correct angular location
+                        * right((_r1 + _r2) / 2) // Shift profile out to thread radius
+                        * skew_mat               // Skew correction for tapered threads
+                        * map3d_and_scale,       // Map profile into 3d
+             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([
+        for (i=[0:1:starts-1])
+            zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style=style)),
+        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]
+    ]);
+
+    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_validate(vnf_quantize(thread_vnfs), size=0.1);
+              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)
+                    down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
+                  if (!bevel2)
+                    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 threads
+          if (internal) {
+            if (bevel1)
+              down(l/2)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
+            if (bevel2)
+              up(l/2)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, if you use it for large length of thread it is much slower than generic_threaded_rod.
+//   .
+//   You can specify a thread_depth and flank_angle, in which
+//   case you get a symmetric trapezoidal thread, whose base is at the diameter (so the total diameter will be d + thread_depth).  
+//   Atlernatively 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.  
+//   .
+//   Higbee specifies tapering applied to the ends of the threads and is given as the linear distance
+//   over which to taper.  
 // 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.
@@ -44,38 +1133,42 @@
 //   ];
 //   stroke(profile, width=0.02);
 // 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);
 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 +1176,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

From a966002256dc2f91bb3951e5c9764d384d3734ec Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Fri, 27 Aug 2021 23:15:17 -0400
Subject: [PATCH 12/19] comment example

---
 screws.scad | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/screws.scad b/screws.scad
index e26a370..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){
+   $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);
-//   }
+     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){

From a5a2dc926cce82b808bf7dd1c2053a7ffc7b309e Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Fri, 27 Aug 2021 23:24:02 -0400
Subject: [PATCH 13/19] doc indentation fix

---
 threading.scad | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/threading.scad b/threading.scad
index 2a7215c..0914927 100644
--- a/threading.scad
+++ b/threading.scad
@@ -166,19 +166,19 @@ module threaded_nut(
 //               [ 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"){
+//   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.

From dbe7a58a748fb686ee40ce57f7e5f1c83666c97d Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Fri, 27 Aug 2021 23:36:53 -0400
Subject: [PATCH 14/19] bevel typo fix, doc fix

---
 threading.scad | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/threading.scad b/threading.scad
index 0914927..d4ca4cc 100644
--- a/threading.scad
+++ b/threading.scad
@@ -559,7 +559,7 @@ module npt_threaded_rod(
 //       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);
+   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,
@@ -583,7 +583,7 @@ module buttress_threaded_rod(
         d=d, l=l, pitch=pitch,
         profile=profile, 
         left_handed=left_handed,
-        bevel=bevel,beve1=bevel1,bevel2=bevel2,
+        bevel=bevel,bevel1=bevel1,bevel2=bevel2,
         internal=internal,
         higbee=higbee,
         higbee1=higbee1,
@@ -1202,7 +1202,7 @@ module thread_helix(
 //   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:
+// Questions
 //   Should nut modules take d1/d2 for tapered nuts?
 //
 // Need explanation of what exactly the diff is between threaded_rod and helix_threads.

From 352436938f1a98b50b3210e0f0b398248d5a6245 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Fri, 27 Aug 2021 23:49:41 -0400
Subject: [PATCH 15/19] fix comment

---
 threading.scad | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/threading.scad b/threading.scad
index d4ca4cc..0a2cf8b 100644
--- a/threading.scad
+++ b/threading.scad
@@ -559,7 +559,7 @@ module npt_threaded_rod(
 //       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);
+//   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,

From 6ef2f712550c8fa9c715fda5419e4ebce2e5ff79 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sat, 28 Aug 2021 08:57:09 -0400
Subject: [PATCH 16/19] improved thread_helix docs fixed bugs in spiral_sweep:
 wrong sampling rate, left_handed broken

---
 paths.scad     | 28 +++++++++++++++-------------
 threading.scad | 18 +++++++++++++++---
 2 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/paths.scad b/paths.scad
index c661f62..30ec5eb 100644
--- a/paths.scad
+++ b/paths.scad
@@ -1055,7 +1055,7 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
 //   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, internal=false, anchor, spin=0, orient=UP) {
     higsample = 10;         // Oversample factor for higbee tapering
-    dummy1=assert(twist>0);
+//    dummy1=assert(twist>0);
     bounds = pointlist_bounds(poly);
     yctr = (bounds[0].y+bounds[1].y)/2;
     xmin = bounds[0].x;
@@ -1065,7 +1065,8 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     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));
-    ang_step = 360/sides;
+    dir = sign(twist);
+    ang_step = 360/sides*dir;
     anglist = [for(ang = [0:ang_step:twist-EPSILON]) ang,
                twist];
     higbee1 = first_defined([higbee1, higbee, 0]);
@@ -1073,23 +1074,24 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     higang1 = 360 * higbee1 / (2 * r1 * PI);
     higang2 = 360 * higbee2 / (2 * r2 * PI);
     dummy2=assert(higbee1>=0 && higbee2>=0)
-           assert(higang1 < twist/2)
-           assert(higang2 < twist/2);
+           assert(higang1 < dir*twist/2)
+           assert(higang2 < dir*twist/2);
     function polygon_r(N,theta) =
         let( alpha = 360/N )
         cos(alpha/2)/(cos(posmod(theta,alpha)-alpha/2));
     function higsize(a) = lookup(a,[
-        [-0.001,  higang1>0?0:1],
-        if (higang1>0) for (x=[0.125:0.125:1]) [      x*higang1, pow(x,1/2)],
-        if (higang2>0) for (x=[0.125:0.125:1]) [twist-x*higang2, pow(x,1/2)],
-        [twist+0.001,   higang2>0?0:1]
+        [-0.001*dir,  higang1>0?0:1],
+        if (higang1>0) for (x=[0.125:0.125:1]) [      dir*x*higang1, pow(x,1/2)],
+        if (higang2>0) for (x=[0.125:0.125:1]) [twist-dir*x*higang2, pow(x,1/2)],
+        [twist+dir*0.001,   higang2>0?0:1]
     ]);
-
+    
     interp_ang = [
                   for(i=idx(anglist,e=-2))
                       each lerpn(anglist[i],anglist[i+1],
-                                 (higang1<anglist[i+1] || (twist-higang2>anglist[i])) ? ceil((anglist[i+1]-anglist[i])/ang_step*higsample)
-                                                                                      : 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)
                  ];
@@ -1110,12 +1112,12 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     ];
 
     vnf = vnf_vertex_array(
-        points, col_wrap=true, caps=true, reverse=true, 
+        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=ceil(2*twist/360));
+        vnf_polyhedron(vnf, convexity=ceil(2*dir*twist/360));
         children();
     }
 }
diff --git a/threading.scad b/threading.scad
index 0a2cf8b..1f8bab1 100644
--- a/threading.scad
+++ b/threading.scad
@@ -1093,14 +1093,23 @@ module generic_threaded_nut(
 // Description:
 //   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, if you use it for large length of thread it is much slower than generic_threaded_rod.
+//   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 base is at the diameter (so the total diameter will be d + thread_depth).  
-//   Atlernatively you can give a profile, following the same rules as for general_threaded_rod.
+//   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.  
 // Arguments:
@@ -1134,6 +1143,9 @@ module generic_threaded_nut(
 //   stroke(profile, width=0.02);
 // Example:
 //   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, thread_depth, flank_angle, twist=720,
     profile, starts=1, left_handed=false, internal=false,

From 110dc09cb7ef3afb2a6f1cb15303f3eb5c125bf4 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sat, 28 Aug 2021 10:14:29 -0400
Subject: [PATCH 17/19] Smoother higbee for spiral_sweep (don't linear
 interpolate square root)

---
 paths.scad | 29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

diff --git a/paths.scad b/paths.scad
index 30ec5eb..47ebf1b 100644
--- a/paths.scad
+++ b/paths.scad
@@ -1074,18 +1074,14 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     higang1 = 360 * higbee1 / (2 * r1 * PI);
     higang2 = 360 * higbee2 / (2 * r2 * PI);
     dummy2=assert(higbee1>=0 && higbee2>=0)
-           assert(higang1 < dir*twist/2)
-           assert(higang2 < dir*twist/2);
+           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));
-    function higsize(a) = lookup(a,[
-        [-0.001*dir,  higang1>0?0:1],
-        if (higang1>0) for (x=[0.125:0.125:1]) [      dir*x*higang1, pow(x,1/2)],
-        if (higang2>0) for (x=[0.125:0.125:1]) [twist-dir*x*higang2, pow(x,1/2)],
-        [twist+dir*0.001,   higang2>0?0:1]
-    ]);
-    
+
+    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],
@@ -1098,15 +1094,16 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h));
     points = [
         for (a = interp_ang) let (
-            hsc = higsize(a),
+            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([polygon_r(sides,a)*r, 0, h * (u-0.5)]) *
-                affine3d_xrot(90) *
-                skewmat *
-                //affine3d_scale([hsc,lerp(hsc,1,0.25),1]),
-                scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]),
+            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
     ];

From 1f7d17eab63453289a9d8744762051f0a9cf27ae Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sat, 28 Aug 2021 11:37:09 -0400
Subject: [PATCH 18/19] doc fix for spiral_sweep

---
 paths.scad | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/paths.scad b/paths.scad
index 47ebf1b..79d10ed 100644
--- a/paths.scad
+++ b/paths.scad
@@ -1030,8 +1030,9 @@ 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 right-handed 3D spiral path
-//   of a given radius, height and twist.  The origin in the profile traces out the helix of the specified radius.  
+//   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.  
@@ -1045,7 +1046,7 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
 //   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.  Default: false
+//   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`
@@ -1055,7 +1056,7 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
 //   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, internal=false, anchor, spin=0, orient=UP) {
     higsample = 10;         // Oversample factor for higbee tapering
-//    dummy1=assert(twist>0);
+    dummy1=assert(is_num(twist) && twist != 0);
     bounds = pointlist_bounds(poly);
     yctr = (bounds[0].y+bounds[1].y)/2;
     xmin = bounds[0].x;
@@ -1079,7 +1080,6 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
     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 = [

From c4c9b6cd93b27b5dbed09e5770788a87e0947a44 Mon Sep 17 00:00:00 2001
From: Adrian Mariano <avm4@cornell.edu>
Date: Sat, 28 Aug 2021 19:06:47 -0400
Subject: [PATCH 19/19] Fixed some issues with internal threading. Tightening
 code slightly for generic threaded rod Added figure showing flank angle

---
 threading.scad | 91 +++++++++++++++++++++++++++++++++-----------------
 1 file changed, 60 insertions(+), 31 deletions(-)

diff --git a/threading.scad b/threading.scad
index 1f8bab1..9856717 100644
--- a/threading.scad
+++ b/threading.scad
@@ -916,16 +916,15 @@ module generic_threaded_rod(
     rmax = max(_r1,_r2)+pmax;
     depth = pdepth * pitch;
     dummy1 = assert(_r1>depth && _r2>depth, "Screw profile deeper than rod radius");
-    skew_mat = affine3d_skew(sxz=(_r2-_r1)/l);
-    map3d_and_scale = 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
+    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,           0],
-        [-twist/2-0.00001, 0],
+        [-twist/2-0.0001, 0],
         [-twist/2+higang1, 1],
-        [+twist/2-higang2, 1],
-        [+twist/2+0.00001, 0],
-        [+twist,           0],
+        [ twist/2-higang2, 1],
+        [ twist/2+0.0001, 0],
     ];
     start_steps = sides / starts;
     thread_verts = [
@@ -934,10 +933,7 @@ module generic_threaded_rod(
          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)            // Rotate profile to correct angular location
-                        * right((_r1 + _r2) / 2) // Shift profile out to thread radius
-                        * skew_mat               // Skew correction for tapered threads
-                        * map3d_and_scale,       // Map profile into 3d
+             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,
@@ -961,20 +957,21 @@ module generic_threaded_rod(
     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)) [0, i+1, i]],
-            rfaces = left_handed? [for (x=faces) reverse(x)] : faces
-        ) [apply(rmat,pts), rfaces],
+            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)) [len(pts)-1, i, i+1]],
-            rfaces = left_handed? [for (x=faces) reverse(x)] : faces
-        ) [apply(rmat,pts), rfaces]
+            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;
@@ -986,35 +983,43 @@ module generic_threaded_rod(
 
           // This method is faster but more complex code and it produces green tops
           difference() {
-              //vnf_validate(vnf_quantize(thread_vnfs), size=0.1);
-              vnf_polyhedron(vnf_quantize(thread_vnfs), convexity=10);
+              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 (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)
-                    down(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=TOP);                     
-                  if (!bevel2)
-                    up(l/2) cuboid([2*rmax+1,2*rmax+1, maxlen], anchor=BOTTOM);
               }
+              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 threads
+          // Add bevel for internal thread mask
           if (internal) {
             if (bevel1)
-              down(l/2)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
+              down(l/2+.001)cyl(l=depth, r1=_r1+pmax, r2=_r1+pmax-slope*depth-depth,anchor=BOTTOM);
             if (bevel2)
-              up(l/2)cyl(l=depth, r2=_r2+pmax, r1=_r2+pmax+slope*depth-depth,anchor=TOP);
+              up(l/2+.001)cyl(l=depth, r2=_r2+pmax, r1=_r2+pmax+slope*depth-depth,anchor=TOP);
           }
         }
         children();
@@ -1111,7 +1116,7 @@ module generic_threaded_nut(
 //   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.  
+//   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
@@ -1141,6 +1146,30 @@ module generic_threaded_nut(
 //       [ 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, 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);