Forráskód Böngészése

h2n: wind screen support with three rings, add code for splitting the microphone stalk and capsule array mount from the bridge so that the capsule array can be separated from the the recorder body without making the whole system too big - includes threads for 8 pin mini-XLR connectors that couple the bridge to the stalk and array

Fernando Pablo Lopez-Lezcano 9 éve
szülő
commit
362edf83c1
2 módosított fájl, 659 hozzáadás és 2 törlés
  1. 281 2
      TinySpHEAR_H2N_common.scad
  2. 378 0
      threads.scad

+ 281 - 2
TinySpHEAR_H2N_common.scad

@@ -28,6 +28,10 @@ include <SpHEAR_common.scad>;
 include <TinySpHEAR_common.scad>;
 include <TinySpHEAR_mount_pcb.scad>;
 
+// http://dkprojects.net/openscad-threads/
+// used for the threads of the mini-xlm connectors
+include <threads.scad>;
+
 // outline of H2N front
 // include<h2n/H2N_front_outline.scad>;
 // outline of H2N top
@@ -608,6 +612,253 @@ module h2n_bridge(width, base_height, stalk_height, radius, col_wall, stalk_wall
     }
 }
 
+
+// a bridge that includes an 8 pin mini-xlr connector
+
+thread_diam = 10.0;
+thread_pitch = 0.75;
+thread_height = 6.0;
+thread_depth = 0.614 * thread_pitch;
+conn_height = 25;
+conn_shoulder = 1.25;
+
+module h2n_mini_xlr_thread(diam, pitch, height) {
+    metric_thread (diameter=diam, pitch=pitch, length=height, internal=true);
+}
+
+module h2n_bridge_connector(width, base_height, stalk_height, radius, col_wall, stalk_wall, wall) {
+    difference() {
+        union() {
+            h2n_bridge_core(width, base_height, stalk_height, radius, col_wall, wall, 0);
+            // connector
+            translate([0, 0, base_height]) {
+                difference() {
+                    cylinder(r = thread_diam/2 + stalk_wall, h = conn_height, $fn = facets);
+                    // remove the thread
+                    translate([0, 0, conn_height - thread_height - conn_shoulder]) {
+                        rotate([0, 0, 90]) {
+                            h2n_mini_xlr_thread(thread_diam, thread_pitch, thread_height);
+                        }
+                    }
+                    // remove excess above the thread
+                    translate([0, 0, conn_height - conn_shoulder]) {
+                        cylinder(r = thread_diam/2 + thread_depth, h = conn_shoulder, $fn = facets);
+                    }
+                    // make hole below the thread
+                    cylinder(r = thread_diam/2, h = conn_height - thread_height - conn_shoulder, $fn = facets);
+                    // remove stalk segment that is inside the bridge
+                    translate([width / 2, 0, -1.0]) {
+                        rotate([0, -90, 0]) {
+                            semi_cylinder(r = thread_diam/2 + stalk_wall, h = width);
+                        }
+                    }
+                }
+            }
+            // small supports for the screws that attach to the columns
+            for (m = [0, 1]) mirror([m, 0, 0]) {
+                translate([-(width / 2 + radius + 2 * wall), 0, radius]) {
+                    rotate([0, 90, 0]) {
+                        cylinder(r = 1.5, h = 2 * wall, $fn = facets);
+                    }
+                }
+            }
+        }
+        h2n_bridge_core(width, base_height, stalk_height, radius, col_wall, wall, -wall);
+        // make a hole in the bridge for the cables
+        cylinder(r = thread_diam/2, h = base_height + radius + 2 * wall, $fn = facets);
+        // cut a window for the XY and MS leds
+        translate([0, h2n_bridge_circle_max, 0]) {
+            rotate([90, 0, 0]) cylinder(r = 2.0, h = h2n_bridge_circle_max * 2, $fn = facets);
+        }
+        // holes for screws that attach to the columns
+        translate([-(width + 3 * radius + 2 * wall) / 2, 0, radius]) {
+            rotate([0, 90, 0]) {
+                cylinder(r = 0.5, h = width + 4 * radius, $fn = facets);
+            }
+        }
+    }
+}
+
+module h2n_connector_to_array_mount(width, stalk_wall) {
+    stalk_height = 40;
+    // the threaded bottom
+    difference() {
+        cylinder(r = thread_diam/2 + stalk_wall, h = conn_height, $fn = facets);
+        // remove the thread
+        translate([0, 0, conn_shoulder]) {
+            rotate([0, 0, 90]) {
+                h2n_mini_xlr_thread(thread_diam, thread_pitch, thread_height);
+            }
+        }
+        // remove excess above the thread
+        translate([0, 0, conn_shoulder + thread_height]) {
+            cylinder(r = thread_diam/2, h = conn_height, $fn = facets);
+        }
+        // make hole below the thread
+        cylinder(r = thread_diam/2, h = conn_shoulder, $fn = facets);
+    }
+    // conical stalk to the capsule array mount
+    translate([0, 0, conn_height]) {
+        difference() {
+            cylinder(r1 = thread_diam/2 + stalk_wall, r2 = mount_radius + stalk_wall, h = stalk_height, $fn = facets);
+            cylinder(r1 = thread_diam/2, r2 = mount_radius, h = stalk_height, $fn = facets);
+        }
+    }
+    // capsule array mount with the right orientation (left front top = #1)0
+    rotate([0, 0, -90]) translate([0, 0, conn_height + stalk_height]) {
+        tetra_capsule_array_mount([-45, 135], mount_radius, stalk_wall);
+    }
+}
+
+// components for a wind screen cage
+
+module h2n_wind_screen_old(width) {
+    *for (m = [0, 1]) mirror([m, 0, 0]) {
+        translate([width/2, 0, 0]) {
+            cube([2, 2, 90]);
+        }
+    }
+    for (m = [0, 1]) mirror([0, m, 0]) {
+        translate([0, 10, 0]) {
+            rotate([0, 90, 90]) {
+                half_rounded_box(2, 130, h2n_width_m/2 + mount_radius + h2n_col_wall, 25, 2);
+            }
+        }
+    }
+    translate([0, 0, 100]) {
+        rounded_box(2, h2n_width_m/2 + mount_radius + h2n_col_wall, 30, 25, 2);
+    }
+}
+
+// wind screen support modules
+
+module h2n_wind_screen_vertical_ring(diameter, thickness, collar_height) {
+    radius = diameter / 2;
+    w = thickness;
+    // vertical main ring with four slots and half collar
+    difference() {
+        union() {
+            translate([-w, 0, w/2]) {
+                rotate([0, 90, 0]) {
+                    tube(radius, radius - w, w);
+                    // side locks
+                    for (r = [0, 1]) rotate([0, 0, 90 * r]) {
+                        for (m = [0, 1]) mirror([0, m, 0]) {
+                            translate([-w*1.5, radius - 2*w, 0]) {
+                                cube([w*3, w*1.5, w]);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        // cut slots for vertical rings
+        tube(radius + w, radius - w, w);
+        translate([0, -w/2, w/2]) {
+            rotate([0, 90, 90]) {
+                tube(radius + w, radius - w, w);
+            }
+        }
+        // remove lower cylinder
+        translate([0, 0, -radius]) {
+            cylinder(r = h2n_mount_radius + h2n_col_wall, h = 3 * w, $fn = facets);
+        }
+    }
+    // cylindrical attachment to mount
+    difference() {
+        union() {
+            translate([0, 0, -(radius + 5)]) {
+                tube(h2n_mount_radius + h2n_col_wall, h2n_mount_radius + wall, collar_height);
+            }
+            // mount for bottom screws
+            for (m = [0, 1]) mirror([0, m, 0]) translate([-w, h2n_mount_radius + 2 * wall + 2.0, -(radius)]) {
+                minkowski() {
+                    rotate([0, 90, 0]) {
+                        cylinder(r = 2.0, h = w, $fn = facets);
+                    }
+                    translate([0, -2, -2]) {
+                        cube([w, 1.5 * w, 4]);
+                    }
+                }
+            }
+        }
+        // remove half the tube
+        translate([0, -2 * (h2n_mount_radius + h2n_col_wall), -(radius + collar_height / 2)]) {
+            cube([h2n_mount_radius + h2n_col_wall, 4 * (h2n_mount_radius + h2n_col_wall), collar_height]);
+        }
+        // make holes for screws
+        for (m = [0, 1]) mirror([0, m, 0]) translate([-w, h2n_mount_radius + 2 * wall + 2.0, -(radius)]) {
+            rotate([0, 90, 0]) {
+                cylinder(r = 1.0, h = w, $fn = facets);
+            }
+        }
+        // remove inside mounts
+        translate([0, 0, -(radius + 5)]) {
+            cylinder(r = h2n_mount_radius + wall, h = collar_height, $fn = facets);
+        }
+    }
+}
+
+module h2n_wind_screen_horizontal_ring(diameter, thickness) {
+    radius = diameter / 2;
+    w = thickness;
+    // horizontal ring with two slots
+    difference() {
+        union() {
+            tube(radius, radius - w, w);
+            // side lock supports
+            rotate([0, 0, 90]) {
+               for (m = [0, 1]) mirror([0, m, 0]) {
+                    translate([-w*1.5, radius - 2*w, 0]) {
+                        cube([w*3, w*1.5, w]);
+                    }
+                }
+            }
+            // stops to assemble into vertical ring
+            for (m = [0, 1]) mirror([0, m, 0]) translate([-2*w, radius - w, w]) {
+                cube([w, w, w]);
+            }
+        }
+        // cut slot for vertical secondary ring
+        translate([0, -w/2, w/2]) {
+            rotate([0, 90, 90]) {
+                tube(radius + w, radius - w, w);
+            }
+        }
+        // make into an arc (1/2 of the ring)
+        translate([0, -diameter/2, 0]) {
+            cube([diameter, diameter, w]);
+        }
+   }
+}
+
+module h2n_wind_screen_vertical_aux_ring(diameter, thickness) {
+    radius = diameter / 2;
+    w = thickness;
+    difference() {
+        union() {
+            translate([0, -w/2, w/2]) {
+                rotate([0, 90, 90]) {
+                    tube(radius, radius - w, w);
+                }
+            }
+            // stop to assemble into vertical ring
+            rotate([90, 0, 0]) translate([-2*w, radius - w/2, w/2]) {
+                cube([w, w, w]);
+            }
+        }
+        // remove lower cylinder
+        translate([0, 0, -radius]) {
+            cylinder(r = h2n_mount_radius + h2n_col_wall, h = 3 * w, $fn = facets);
+        }
+        // make into an arc (1/2 of the ring)
+        rotate([0, 90, 90]) translate([-(diameter/2 + w/2), -diameter, -w]) {
+            cube([diameter, diameter, 2*w]);
+        }
+    }
+}
+
+
 // render the whole thing
 
 difference() {
@@ -624,13 +875,40 @@ union() {
                h2n_bridge_base_height, h2n_bridge_stalk_height, mount_radius, h2n_col_wall, h2n_stalk_wall, wall);
 }
 
+*translate([0, 0, h2n_column_height]) {
+    h2n_bridge_connector((h2n_width_m / 2 + mount_radius - h2n_col_wall - h2n_column_height * tan(h2n_col_tilt)) * 2,
+               h2n_bridge_base_height, h2n_bridge_stalk_height, mount_radius, h2n_col_wall, h2n_stalk_wall, wall);
+}
+
+translate([0, 0, h2n_column_height + 40]) {
+    h2n_connector_to_array_mount((h2n_width_m / 2 + mount_radius - h2n_col_wall - h2n_column_height * tan(h2n_col_tilt)) * 2,
+            h2n_stalk_wall);
+}
+
 *rotate([0, 0, 90]) {
     render_tetra_capsule_array(h2n_column_height + h2n_bridge_base_height + h2n_bridge_stalk_height + 26);
 }
 
+translate([0, 0, 100]) {
+    *h2n_wind_screen_vertical_ring(h2n_width_m, 1.6, 10.0);
+    *rotate([0, 0, 180]) h2n_wind_screen_vertical_ring(h2n_width_m, 1.6, 10.0);
+    *h2n_wind_screen_horizontal_ring(h2n_width_m, 1.6);
+    *h2n_wind_screen_vertical_aux_ring(h2n_width_m, 1.6);
 }
-*translate([-40, -50, 0]) {
-    cube([80, 50, 80]);
+
+*rotate([0, 90, 0])
+    h2n_wind_screen_vertical_ring(h2n_width_m, 1.6, 10.0);
+
+
+*h2n_wind_screen_horizontal_ring(h2n_width_m, 1.6);
+
+
+*rotate([90, 180, 0])
+    h2n_wind_screen_vertical_aux_ring(h2n_width_m, 1.6);
+
+}
+translate([-40, -50, 0]) {
+    cube([80, 50, 150]);
 }
 }
 
@@ -644,6 +922,7 @@ union() {
 *translate([-(h2n_width_m - 0.1) / 2, - 0.5, 0]) {
     cube([h2n_width_m - 0.1, 1, 1]);
 }
+
 *translate([-0.5, -(h2n_depth_m - 0.1) / 2, 0]) {
     cube([1, h2n_depth_m - 0.1, 1]);
 }

+ 378 - 0
threads.scad

@@ -0,0 +1,378 @@
+/*
+ * ISO-standard metric threads, following this specification:
+ *          http://en.wikipedia.org/wiki/ISO_metric_screw_thread
+ * http://dkprojects.net/openscad-threads/
+ *
+ * Copyright 2016 Dan Kirshner - dan_kirshner@yahoo.com
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * See <http://www.gnu.org/licenses/>.
+ *
+ * Version 2.2.  2017-01-01  Correction for angle; leadfac option.  (Thanks to
+ *                           Andrew Allen <a2intl@gmail.com>.)
+ * Version 2.1.  2016-12-04  Chamfer bottom end (low-z); leadin option.
+ * Version 2.0.  2016-11-05  Backwards compatibility (earlier OpenSCAD) fixes.
+ * Version 1.9.  2016-07-03  Option: tapered.
+ * Version 1.8.  2016-01-08  Option: (non-standard) angle.
+ * Version 1.7.  2015-11-28  Larger x-increment - for small-diameters.
+ * Version 1.6.  2015-09-01  Options: square threads, rectangular threads.
+ * Version 1.5.  2015-06-12  Options: thread_size, groove.
+ * Version 1.4.  2014-10-17  Use "faces" instead of "triangles" for polyhedron
+ * Version 1.3.  2013-12-01  Correct loop over turns -- don't have early cut-off
+ * Version 1.2.  2012-09-09  Use discrete polyhedra rather than linear_extrude ()
+ * Version 1.1.  2012-09-07  Corrected to right-hand threads!
+ */
+
+// Examples.
+//
+// Standard M8 x 1.
+*difference() {
+    diam = 10.0;
+    cylinder(r = 11.5/2, h = 8, $fn = 128);
+    translate([0, 0, 6]) cylinder(r = diam/2, h = 10, $fn = 128);
+    translate([0, 0, 1.25]) metric_thread (diameter=diam, pitch=0.75, length=6, internal=true);
+    translate([0, 0, 0]) cylinder(r = diam/2, h = 1.25, $fn = 128);
+}
+
+// Square thread.
+// metric_thread (diameter=8, pitch=1, length=4, square=true);
+
+// Non-standard: long pitch, same thread size.
+//metric_thread (diameter=8, pitch=4, length=4, thread_size=1, groove=true);
+
+// Non-standard: 20 mm diameter, long pitch, square "trough" width 3 mm,
+// depth 1 mm.
+//metric_thread (diameter=20, pitch=8, length=16, square=true, thread_size=6,
+//               groove=true, rectangle=0.333);
+
+// English: 1/4 x 20.
+//english_thread (diameter=1/4, threads_per_inch=20, length=1);
+
+// Tapered.  Example -- pipe size 3/4" -- per:
+// http://www.engineeringtoolbox.com/npt-national-pipe-taper-threads-d_750.html
+// english_thread (diameter=1.05, threads_per_inch=14, length=3/4, taper=1/16);
+
+// Thread for mounting on Rohloff hub.
+//difference () {
+//   cylinder (r=20, h=10, $fn=100);
+//
+//   metric_thread (diameter=34, pitch=1, length=10, internal=true, n_starts=6);
+//}
+
+
+// ----------------------------------------------------------------------------
+function segments (diameter) = min (50, ceil (diameter*6));
+
+
+// ----------------------------------------------------------------------------
+// diameter -    outside diameter of threads in mm. Default: 8.
+// pitch    -    thread axial "travel" per turn in mm.  Default: 1.
+// length   -    overall axial length of thread in mm.  Default: 1.
+// internal -    true = clearances for internal thread (e.g., a nut).
+//               false = clearances for external thread (e.g., a bolt).
+//               (Internal threads should be "cut out" from a solid using
+//               difference ()).
+// n_starts -    Number of thread starts (e.g., DNA, a "double helix," has
+//               n_starts=2).  See wikipedia Screw_thread.
+// thread_size - (non-standard) axial width of a single thread "V" - independent
+//               of pitch.  Default: same as pitch.
+// groove      - (non-standard) subtract inverted "V" from cylinder (rather than
+//               add protruding "V" to cylinder).
+// square      - Square threads (per
+//               https://en.wikipedia.org/wiki/Square_thread_form).
+// rectangle   - (non-standard) "Rectangular" thread - ratio depth/(axial) width
+//               Default: 1 (square).
+// angle       - (non-standard) angle (deg) of thread side from perpendicular to
+//               axis (default = standard = 30 degrees).
+// taper       - diameter change per length (National Pipe Thread/ANSI B1.20.1
+//               is 1" diameter per 16" length). Taper decreases from 'diameter'
+//               as z increases.
+// leadin      - 1 (default): chamfer (45 degree) at max-z end; 0: no chamfer;
+//               2: chamfer at both ends, 3: chamfer at z=0 end.
+// leadfac     - scale of leadin chamfer (default: 1.0 = 1/2 thread).
+module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1,
+                      thread_size=-1, groove=false, square=false, rectangle=0,
+                      angle=30, taper=0, leadin=1, leadfac=1.0)
+{
+   // thread_size: size of thread "V" different than travel per turn (pitch).
+   // Default: same as pitch.
+   local_thread_size = thread_size == -1 ? pitch : thread_size;
+   local_rectangle = rectangle ? rectangle : 1;
+
+   n_segments = segments (diameter);
+   h = (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size / (2 * tan(angle));
+
+   h_fac1 = (square || rectangle) ? 0.90 : 0.625;
+
+   // External thread includes additional relief.
+   h_fac2 = (square || rectangle) ? 0.95 : 5.3/8;
+
+   tapered_diameter = diameter - length*taper;
+
+   difference () {
+      union () {
+         if (! groove) {
+            metric_thread_turns (diameter, pitch, length, internal, n_starts,
+                                 local_thread_size, groove, square, rectangle, angle,
+                                 taper);
+         }
+
+         difference () {
+
+            // Solid center, including Dmin truncation.
+            if (groove) {
+               cylinder (r1=diameter/2, r2=tapered_diameter/2,
+                         h=length, $fn=n_segments);
+            } else if (internal) {
+               cylinder (r1=diameter/2 - h*h_fac1, r2=tapered_diameter/2 - h*h_fac1,
+                         h=length, $fn=n_segments);
+            } else {
+
+               // External thread.
+               cylinder (r1=diameter/2 - h*h_fac2, r2=tapered_diameter/2 - h*h_fac2,
+                         h=length, $fn=n_segments);
+            }
+
+            if (groove) {
+               metric_thread_turns (diameter, pitch, length, internal, n_starts,
+                                    local_thread_size, groove, square, rectangle,
+                                    angle, taper);
+            }
+         }
+      }
+
+      // chamfer z=0 end if leadin is 2 or 3
+      if (leadin == 2 || leadin == 3) {
+         difference () {
+            cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);
+
+            cylinder (r2=diameter/2, r1=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
+                      $fn=n_segments);
+         }
+      }
+
+      // chamfer z-max end if leadin is 1 or 2.
+      if (leadin == 1 || leadin == 2) {
+         translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) {
+            difference () {
+               cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);
+               cylinder (r1=tapered_diameter/2, r2=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
+                         $fn=n_segments);
+            }
+         }
+      }
+   }
+}
+
+
+// ----------------------------------------------------------------------------
+// Input units in inches.
+// Note: units of measure in drawing are mm!
+module english_thread (diameter=0.25, threads_per_inch=20, length=1,
+                      internal=false, n_starts=1, thread_size=-1, groove=false,
+                      square=false, rectangle=0, angle=30, taper=0, leadin=1)
+{
+   // Convert to mm.
+   mm_diameter = diameter*25.4;
+   mm_pitch = (1.0/threads_per_inch)*25.4;
+   mm_length = length*25.4;
+
+   echo (str ("mm_diameter: ", mm_diameter));
+   echo (str ("mm_pitch: ", mm_pitch));
+   echo (str ("mm_length: ", mm_length));
+   metric_thread (mm_diameter, mm_pitch, mm_length, internal, n_starts,
+                  thread_size, groove, square, rectangle, angle, taper, leadin);
+}
+
+// ----------------------------------------------------------------------------
+module metric_thread_turns (diameter, pitch, length, internal, n_starts,
+                            thread_size, groove, square, rectangle, angle,
+                            taper)
+{
+   // Number of turns needed.
+   n_turns = floor (length/pitch);
+
+   intersection () {
+
+      // Start one below z = 0.  Gives an extra turn at each end.
+      for (i=[-1*n_starts : n_turns+1]) {
+         translate ([0, 0, i*pitch]) {
+            metric_thread_turn (diameter, pitch, internal, n_starts,
+                                thread_size, groove, square, rectangle, angle,
+                                taper, i*pitch);
+         }
+      }
+
+      // Cut to length.
+      translate ([0, 0, length/2]) {
+         cube ([diameter*3, diameter*3, length], center=true);
+      }
+   }
+}
+
+
+// ----------------------------------------------------------------------------
+module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size,
+                           groove, square, rectangle, angle, taper, z)
+{
+   n_segments = segments (diameter);
+   fraction_circle = 1.0/n_segments;
+   for (i=[0 : n_segments-1]) {
+      rotate ([0, 0, i*360*fraction_circle]) {
+         translate ([0, 0, i*n_starts*pitch*fraction_circle]) {
+            //current_diameter = diameter - taper*(z + i*n_starts*pitch*fraction_circle);
+            thread_polyhedron ((diameter - taper*(z + i*n_starts*pitch*fraction_circle))/2,
+                               pitch, internal, n_starts, thread_size, groove,
+                               square, rectangle, angle);
+         }
+      }
+   }
+}
+
+
+// ----------------------------------------------------------------------------
+module thread_polyhedron (radius, pitch, internal, n_starts, thread_size,
+                          groove, square, rectangle, angle)
+{
+   n_segments = segments (radius*2);
+   fraction_circle = 1.0/n_segments;
+
+   local_rectangle = rectangle ? rectangle : 1;
+
+   h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size / (2 * tan(angle));
+   outer_r = radius + (internal ? h/20 : 0); // Adds internal relief.
+   //echo (str ("outer_r: ", outer_r));
+
+   // A little extra on square thread -- make sure overlaps cylinder.
+   h_fac1 = (square || rectangle) ? 1.1 : 0.875;
+   inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with
+                                // cylinder.
+
+   translate_y = groove ? outer_r + inner_r : 0;
+   reflect_x   = groove ? 1 : 0;
+
+   // Make these just slightly bigger (keep in proportion) so polyhedra will
+   // overlap.
+   x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02;
+   x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02;
+   z_incr = n_starts * pitch * fraction_circle * 1.005;
+
+   /*
+    (angles x0 and x3 inner are actually 60 deg)
+
+                          /\  (x2_inner, z2_inner) [2]
+                         /  \
+   (x3_inner, z3_inner) /    \
+                  [3]   \     \
+                        |\     \ (x2_outer, z2_outer) [6]
+                        | \    /
+                        |  \  /|
+             z          |[7]\/ / (x1_outer, z1_outer) [5]
+             |          |   | /
+             |   x      |   |/
+             |  /       |   / (x0_outer, z0_outer) [4]
+             | /        |  /     (behind: (x1_inner, z1_inner) [1]
+             |/         | /
+    y________|          |/
+   (r)                  / (x0_inner, z0_inner) [0]
+
+   */
+
+   x1_outer = outer_r * fraction_circle * 2 * PI;
+
+   z0_outer = (outer_r - inner_r) * tan(angle);
+   //echo (str ("z0_outer: ", z0_outer));
+
+   //polygon ([[inner_r, 0], [outer_r, z0_outer],
+   //        [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]);
+   z1_outer = z0_outer + z_incr;
+
+   // Give internal square threads some clearance in the z direction, too.
+   bottom = internal ? 0.235 : 0.25;
+   top    = internal ? 0.765 : 0.75;
+
+   translate ([0, translate_y, 0]) {
+      mirror ([reflect_x, 0, 0]) {
+
+         if (square || rectangle) {
+
+            // Rule for face ordering: look at polyhedron from outside: points must
+            // be in clockwise order.
+            polyhedron (
+               points = [
+                         [-x_incr_inner/2, -inner_r, bottom*thread_size],         // [0]
+                         [x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1]
+                         [x_incr_inner/2, -inner_r, top*thread_size + z_incr],    // [2]
+                         [-x_incr_inner/2, -inner_r, top*thread_size],            // [3]
+
+                         [-x_incr_outer/2, -outer_r, bottom*thread_size],         // [4]
+                         [x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5]
+                         [x_incr_outer/2, -outer_r, top*thread_size + z_incr],    // [6]
+                         [-x_incr_outer/2, -outer_r, top*thread_size]             // [7]
+                        ],
+
+               faces = [
+                         [0, 3, 7, 4],  // This-side trapezoid
+
+                         [1, 5, 6, 2],  // Back-side trapezoid
+
+                         [0, 1, 2, 3],  // Inner rectangle
+
+                         [4, 7, 6, 5],  // Outer rectangle
+
+                         // These are not planar, so do with separate triangles.
+                         [7, 2, 6],     // Upper rectangle, bottom
+                         [7, 3, 2],     // Upper rectangle, top
+
+                         [0, 5, 1],     // Lower rectangle, bottom
+                         [0, 4, 5]      // Lower rectangle, top
+                        ]
+            );
+         } else {
+
+            // Rule for face ordering: look at polyhedron from outside: points must
+            // be in clockwise order.
+            polyhedron (
+               points = [
+                         [-x_incr_inner/2, -inner_r, 0],                        // [0]
+                         [x_incr_inner/2, -inner_r, z_incr],                    // [1]
+                         [x_incr_inner/2, -inner_r, thread_size + z_incr],      // [2]
+                         [-x_incr_inner/2, -inner_r, thread_size],              // [3]
+
+                         [-x_incr_outer/2, -outer_r, z0_outer],                 // [4]
+                         [x_incr_outer/2, -outer_r, z0_outer + z_incr],         // [5]
+                         [x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6]
+                         [-x_incr_outer/2, -outer_r, thread_size - z0_outer]    // [7]
+                        ],
+
+               faces = [
+                         [0, 3, 7, 4],  // This-side trapezoid
+
+                         [1, 5, 6, 2],  // Back-side trapezoid
+
+                         [0, 1, 2, 3],  // Inner rectangle
+
+                         [4, 7, 6, 5],  // Outer rectangle
+
+                         // These are not planar, so do with separate triangles.
+                         [7, 2, 6],     // Upper rectangle, bottom
+                         [7, 3, 2],     // Upper rectangle, top
+
+                         [0, 5, 1],     // Lower rectangle, bottom
+                         [0, 4, 5]      // Lower rectangle, top
+                        ]
+            );
+         }
+      }
+   }
+}
+
+