From 01092713cb63fa30d92b7f548ac6a39e29f10f76 Mon Sep 17 00:00:00 2001
From: Garth Minette <gminette@gmail.com>
Date: Thu, 4 Feb 2021 05:39:00 -0800
Subject: [PATCH] VNF re-re-write of trapezoidal_threaded_rod()

---
 threading.scad | 147 ++++++++++++++++++++++++++-----------------------
 version.scad   |   2 +-
 vnf.scad       |   8 +--
 3 files changed, 83 insertions(+), 74 deletions(-)

diff --git a/threading.scad b/threading.scad
index 51c8821..ff5ee96 100644
--- a/threading.scad
+++ b/threading.scad
@@ -151,96 +151,105 @@ module trapezoidal_threaded_rod(
     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) + $slop*3) : 1;
-    threads = ceil(l/pitch/starts) + 2;
-    ll = threads * pitch * starts;
+    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;
-    _higbee1 = first_defined([higbee1, higbee, 0]);
-    _higbee2 = first_defined([higbee2, higbee, 0]);
-    higang1 = 360 * _higbee1 / (2 * PI * _r1);
-    higang2 = 360 * _higbee2 / (2 * PI * _r2);
+    higang1 = first_defined([higbee1, higbee, 0]);
+    higang2 = first_defined([higbee2, higbee, 0]);
     assert(higang1 < twist/2);
     assert(higang2 < twist/2);
 
-    higstart = twist/2 + 360/starts/4;
-    higbee_table = [
-        [-higstart*2,       0.01],
-        [-higstart-0.001,   0.01],
-        [-higstart+higang1, 1   ],
-        [+higstart-higang2, 1   ],
-        [+higstart+0.001,   0.01],
-        [+higstart*2,       0.01]
-    ];
-
-    r1 = -depth/pitch;
+    rr1 = -depth/pitch;
     z1 = 1/4-pa_delta;
     z2 = 1/4+pa_delta;
-    profile = pitch * (
+    profile = (
         profile!=undef? profile : [
-            [-z2, r1],
+            [-z2, rr1],
             [-z1,  0],
             [ z1,  0],
-            [ z2, r1],
+            [ z2, rr1],
         ]
     );
+    prof3d = path3d(profile);
+    higthr1 = ceil(higang1 / 360);
+    higthr2 = ceil(higang2 / 360);
     pdepth = -min(subindex(profile,1));
-    eprofile = [
-        each move([0,pdepth], p=profile),
-        move([pitch,pdepth], p=profile[0]),
+    dummy1 = assert(_r1>2*pdepth) assert(_r2>2*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],
     ];
-    angstep = 360 / sides;
-    angsteps = ceil(sides * (twist / 360 + 2));
-    zang = atan2(_r2-_r1,l);
+    start_steps = floor(sides / starts);
     thread_verts = [
-        [for (i = idx(eprofile)) [0,0,-ll/2]],
-        for (thread = [0:1:threads-1], side=[0:1:sides-1]) let(
-            ang = ((thread - threads/2) + (side / sides)) * 360,
-            u = ang / twist,
-            r = lerp(_r1, _r2, u) * rsc,
-            hsc = higbee1==0 && higbee2==0? 1 : lookup(ang, higbee_table),
-            mat = affine3d_zrot(ang*dir) *
-                affine3d_translate([r-pdepth*pitch, 0, l*u]) *
-                affine3d_xrot(90) *
-                affine3d_skew_xz(xa=zang) * 
-                affine3d_mirror([-1,1]) *
-                affine3d_scale([1,hsc,1]),
-            pts = apply(mat, path3d(eprofile))
-        ) pts,
-        [for (x = eprofile) [0,0,+ll/2]],
-    ];
-    thread_vnf = vnf_vertex_array(thread_verts, reverse=left_handed);
-    eplen = len(eprofile);
-    vlen = len(thread_vnf[0]);
-    thread_vnf2 = [
-        concat(thread_vnf[0], [[0,0,-ll/2], [0,0,+ll/2]]),
-        concat(thread_vnf[1], [
-            for (i = [0:1:sides/starts]) each
-            left_handed? [
-                [eplen*(i+1), eplen*i, vlen],
-                [vlen-eplen*(i+1)-1, vlen-eplen*(i+0)-1, vlen+1]
-            ] : [
-                [eplen*i, eplen*(i+1), vlen],
-                [vlen-eplen*(i+0)-1, vlen-eplen*(i+1)-1, vlen+1]
-            ]
-        ])
+        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 =
+                        abs(tang) > twist/2? 0 :
+                        (higang1==0 && higang2==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 (start = [0:1:starts-1]) zrot(start*360/starts, p=thread_vnf2)
-    ], cleanup=true);
+        for (i=[0:1:starts-1])
+            zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed)),
+        for (i=[0:1:starts-1]) let(
+            rmat = zrot(i*360/starts),
+            pts = deduplicate(select(thread_verts[0], 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(select(last(thread_verts), -len(prof3d)-2, -1)),
+            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) {
-        difference() {
-            vnf_polyhedron(thread_vnfs, convexity=10);
-            zcopies(l+4*pitch*starts)
-                cylinder(h=4*pitch*starts, r=2*max(_r1,_r2)+1, center=true);
-            if (bevel)
-                cylinder_mask(r1=_r1, r2=_r2, l=l+0.01, chamfer=depth);
+        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();
     }
diff --git a/version.scad b/version.scad
index 5d74464..aa90602 100644
--- a/version.scad
+++ b/version.scad
@@ -6,7 +6,7 @@
 //////////////////////////////////////////////////////////////////////
 
 
-BOSL_VERSION = [2,0,561];
+BOSL_VERSION = [2,0,562];
 
 
 // Section: BOSL Library Version Functions
diff --git a/vnf.scad b/vnf.scad
index 9341661..9118ee3 100644
--- a/vnf.scad
+++ b/vnf.scad
@@ -608,7 +608,7 @@ function vnf_bend(vnf,r,d,axis="Z") =
 // Usage: As Function
 //   fails = vnf_validate(vnf);
 // Usage: As Module
-//   vnf_validate(vnf);
+//   vnf_validate(vnf, <size>);
 // Description:
 //   When called as a function, returns a list of non-manifold errors with the given VNF.
 //   Each error has the format `[ERR_OR_WARN,CODE,MESG,POINTS,COLOR]`.
@@ -855,16 +855,16 @@ module vnf_validate(vnf, size=1, show_warns=true, check_isects=false) {
         echo(str(typ, " ", err, " (", clr ,"): ", msg, " at ", pts));
         color(clr) {
             if (len(pts)==2) {
-                stroke(pts, width=size);
+                stroke(pts, width=size, closed=true, endcaps="butt", hull=false, $fn=8);
             } else if (len(pts)>2) {
-                stroke(pts, width=size, closed=true);
+                stroke(pts, width=size, closed=true, hull=false, $fn=8);
                 polyhedron(pts,[[for (i=idx(pts)) i]]);
             } else {
                 move_copies(pts) sphere(d=size*3, $fn=18);
             }
         }
     }
-    color([0.5,0.5,0.5,0.5]) vnf_polyhedron(vnf);
+    color([0.5,0.5,0.5,0.67]) vnf_polyhedron(vnf);
 }
 
 // Section: VNF transformations