[User]
Okay, I'm looking to design a customizable PVC pipe mount in OpenSCAD, and I'd like the model to be parametric. I need a design that will allow me to easily mount a PVC pipe to a flat surface, and also easily insert and remove that pipe.

Here's what I'm envisioning:

*   **Pipe Mount Base:** A cylindrical base that will be screwed into a flat surface.
*   **Pipe Shelf:** A circular "shelf" that extends from the top of the base, designed to cradle the PVC pipe. This shelf should have a diameter slightly larger than the PVC pipe's outer diameter to allow for easy placement. The shelf should be a simple, flat extension, without a lip.
*   **Screw Holes:** Recessed screw holes around the base to allow for flat mounting. The screw holes should be counterbored to recess the screw head. I’d like to be able to configure the number of screw holes and screw sizes.
*   **Customization:** I need to be able to adjust several key dimensions:
    *   The nominal outer diameter of the PVC pipe.
    *   A "fudge factor" to add to the pipe radius for a looser fit.
    *   The length of the pipe shelf.
    *   The thickness of the shelf itself.
    *   The height of the base of the mount.
    *   The diameter of the mounting screws.
    *   The diameter of the screw heads.
    *   The height of the screw heads.
    *   The number of screw holes to include.

I'd prefer not to have any extra features, like fillets or chamfers on the edges of the mount or shelf unless needed. Simple geometric shapes should be sufficient for this design. Also, I want to be able to adjust the rendering quality.

I need you to generate the OpenSCAD code. I don't have any other specific preference for libraries, but the design should be clear and easy to understand, using comments to make things clear. Please use reasonable defaults for values like the fudge factor, shelf thickness and length, and screw sizes so that it is functional out of the box.

[Assistant]
32mmPvcPipeMount.scad:
```
pipe_od=32;         // Nominal OD of the pipe (mm)
fudge_factor=0.5;   // Add this much to radius for "fudge factor" (mm)
shelf_l=10;         // Length of the "shelf" for the pipe (mm)
shelf_t=4;          // "Thickness" of the flat area around the pipe mount (mm)

mount_h=7;          // Height of the mount base (mm)
screw_dia=4;        // Diameter of the mounting screws (mm)
screw_head_dia=11;   // Diameter of the screw head (mm)
screw_head_h=5;     // Height of the screw head (mm)

holes = 3;          // Number of screw holes

_od = pipe_od + fudge_factor;
_tot_od = _od + (2*screw_dia) + (2*screw_head_dia);

assert(mount_h > screw_head_h, "ERROR: mount height must be at least the screw head height");

// Rendering values
$fa=1;
//$fs=2;    // Drafting quality
$fs=0.5;    // Finish quality

difference () {

    // The "mount itself"
    union() {
        // Full outer mount
        cylinder(d=_tot_od, h=mount_h);

        // Pipe Shelf
        translate([0, 0, mount_h]) {
            cylinder(h=shelf_l, d1=_tot_od, d2=_od+shelf_t);
        }
    }

    // Pipe Cutouts
    // Top "drop in" area
    translate ([-_od/2, 0, mount_h]) {
        cube([_od, _tot_od, shelf_l+1]);
    }
    // Bottom cylinder
    translate ([0, 0, mount_h]) {
        cylinder(d=_od, h=shelf_l+1);
    }

    // Screw holes
    for (i=[0:holes-1]) {
        rotate(a=i*(360/holes)) {
            c = (_tot_od + _od) / 2;
            translate([0, c/2, 0]) {
                opph = mount_h + shelf_l + 1; // Opportunistic full height

                // Screw Hole
                cylinder(d=screw_dia + 0.1, h=opph);

                // Screw Head Hole
                head_height = mount_h - screw_head_h;
                translate([0, 0, head_height]) {
                    cylinder(d=screw_head_dia + 0.1, h=opph);
                }
            }
        }
    }
}
```


[User]
I need a parametric OpenSCAD design for a fence post cap. I've got a few specific requirements, so I'll try to be as clear as possible.

First, the basic cap should be a box that fits snugly over a fence post. I need to be able to adjust the *inside* dimensions of this box, specifically the width and depth, so it can accommodate different sizes of posts. The cap also needs a variable inside height. The walls should be a configurable thickness all the way around, including the top.

I would like to have a bevel on the top of the cap and for the angle of the bevel in both the X and Y directions to be adjustable.

The design should be able to optionally cut out the back of the cap and cut out portions of the left and right sides. When cutting out the left and right sides, I want to specify the height and the distance from the top of the cap to where the cutout starts, so I can get the exact shape I need.

I also need the ability to add side supports to the left and right sides of the cap. These would protrude outwards from the sides. The width of these side supports and their distance from the top of the cap should both be adjustable as well.

Finally, I'd like to have the option of adding a sphere on top of the cap as a decorative finial. I need to be able to adjust the radius of this sphere. And, below the sphere I'd like some simple decorative banding that is also parametrically adjustable.

I need the ability to add nail holes around the bottom of the cap for attaching it to the post and for this to be configurable (i.e., I can turn it off or set the size).

To recap, here's what I'd like to be able to customize:

*   Post width (front)
*   Post width (side)
*   Cap inside height
*   Wall thickness
*   Top thickness
*   Top bevel angle (X direction)
*   Option to remove back of the cap
*   Option to cut out sections of the sides, along with the height of the cutout and its distance from the top
*   Option to add side supports, along with the width of the supports and their distance from the top
*   Sphere finial radius (0 for no finial)
*   Nail hole diameter (0 for no holes)
*   Roundness of the sphere and nail holes

I don't have a preference for libraries, but I would prefer that any used be common libraries with good documentation. Thanks in advance!

[Assistant]
fence-post-cap.scad:
```
// all measurements in mm
//
// Note for wood, a 4" post is actual width ~3.5" (89mm), and a 2" post is actual ~1.5" (38mm)
//
post_width_front = 92;
post_width_side = 92;
cap_inside_height = 60;

open_top = 0;
open_rear = 0;
open_sides_height = 0;      // 0 for no open left & right sides, 38mm for 2x4 attached to side
open_sides_mm_from_top = 0; // measured from inside top of post to the top of the side opening

side_supports_width = 20;   // protrusion in mm; 0 for no supports protruding from left & right sides
side_supports_mm_from_top = 38;  // measured from inside top of post to the top of the side support

sphere_finial_radius = 40;   // 0 for no sphere finial on top

side_wall_thickness = 5;
top_thickness = 6;          // add extra thickness for bridging; doesn't include addl thickness from beveled area

top_bevel_angle_x = 15;     // set to 0 for no beveled surface on top of the cap
nail_hole_diameter = 1.5;   // set to 0 to delete nail holes on side

$fn = 72;                   // roundness of sphere, nail holes

total_cap_width_x = post_width_front + side_wall_thickness*2;  // doesn't include side supports
total_cap_width_y = post_width_side + side_wall_thickness*2;

beveled_hat_height = tan(top_bevel_angle_x) * total_cap_width_x/2;
echo("beveled_hat_height=",beveled_hat_height);
top_bevel_angle_y = atan(beveled_hat_height / (total_cap_width_y/2));
echo("top_bevel_angle_y=",top_bevel_angle_y);

cap_height_without_hat = cap_inside_height + top_thickness;
total_cap_height =  cap_height_without_hat + beveled_hat_height;
bevel_cutter_size = total_cap_height*total_cap_height;      // arbitrarily big enough to handle large bevel

module post_cap() {
    difference() {
        union () {
            // start with a solid block
            translate([0,0,total_cap_height/2])
                cube([total_cap_width_x,total_cap_width_y,total_cap_height],true);

            // add side supports
            if (side_supports_width) {
                translate([total_cap_width_x/2,0,
                           cap_inside_height-side_supports_mm_from_top])
                    rotate([-90,0,0])
                        linear_extrude(height=post_width_side, center=true)
                            polygon(points=[[0,0],[side_supports_width,0],[0,side_supports_width]]);

                translate([-total_cap_width_x/2,0,
                           cap_inside_height-side_supports_mm_from_top])
                    rotate([-90,90,0])
                        linear_extrude(height=post_width_side, center=true)
                            polygon(points=[[0,0],[side_supports_width,0],[0,side_supports_width]]);
            }
        }

        // bevel top if needed
        if (top_bevel_angle_x) {
            for (i=[0:1]) {
                translate([0,0,total_cap_height])
                    rotate([0,top_bevel_angle_x,180*i])
                        translate([0,0,bevel_cutter_size/2])
                            cube(bevel_cutter_size,true);
                translate([0,0,total_cap_height])
                    rotate([0,top_bevel_angle_y,180*i+90])
                        translate([0,0,bevel_cutter_size/2])
                            cube(bevel_cutter_size,true);
            }
        }

        // cut center out
        translate([0,0,cap_inside_height/2])
            cube([post_width_front,post_width_side,open_top?bevel_cutter_size:cap_inside_height],true);

        // remove rear side?
        if (open_rear)
            translate([0,side_wall_thickness,cap_inside_height/2])
                cube([post_width_front,post_width_side,open_top?bevel_cutter_size:cap_inside_height],true);

        // notch out sides?
        if (open_sides_height)
            translate([0,side_wall_thickness,cap_inside_height/2+(cap_inside_height-open_sides_height)/2])
                cube([total_cap_width_x,total_cap_width_y,open_sides_height],true);

        // cut holes for screws/nails
        translate([0,open_rear?total_cap_width_y/4:0,cap_height_without_hat/4]) rotate([0,90,0])
            cylinder(total_cap_width_x+side_supports_width*2,nail_hole_diameter,nail_hole_diameter,true);
        translate([0,0,cap_height_without_hat/5]) rotate([90,0,0])
            cylinder(total_cap_width_y,nail_hole_diameter,nail_hole_diameter,true);
    }
}

module finial() {
    // sphere
    z = (sphere_finial_radius + beveled_hat_height)/7;

    translate([0,0,cap_height_without_hat+z*6.5+sphere_finial_radius])
        sphere(sphere_finial_radius);

    // bands below sphere
    translate([0,0,cap_height_without_hat])
        rotate_extrude()
            polygon( points=[[0,0],
                             [sphere_finial_radius/2+post_width_side/4,     0],
                             [sphere_finial_radius/2+post_width_side/4.3,   beveled_hat_height],
                             [sphere_finial_radius/2,   beveled_hat_height + z*1.2],
                             [sphere_finial_radius/2,   beveled_hat_height + z*2],
                             [sphere_finial_radius*4/5, beveled_hat_height + z*3.5],
                             [sphere_finial_radius*3/4, beveled_hat_height + z*4],
                             [sphere_finial_radius/2,   beveled_hat_height + z*5],
                             [sphere_finial_radius/2,   beveled_hat_height + z*7],
                             [0,                        beveled_hat_height + z*7]] );
}

union() {
    post_cap();

    if (sphere_finial_radius)
       finial();
}

```


[User]
I'd like to create a parametric fin alignment guide for model rockets using OpenSCAD, similar to the one described in that thread on The Rocketry Forum. I want to be able to adjust all sorts of things, like the diameter of the body tube, the number of fins, the fin thickness, and the overall span and height of the guide.

I really like the idea of having a flange at the tips of the fin guides, so I'd like that as an option, maybe with an adjustable thickness.  And maybe the web between the guide sections could be thickened, but that should be optional too since it might get in the way sometimes.

The core concept is a cylinder matching the rocket body diameter, with guide arms extending out at the appropriate spacing, with slots for the fins to fit into.  The guides should have a foot at the base for stability.

Here are the specific parameters I want to be able to control:

*   **Body Tube Diameter (Outer Diameter):** This should be a dropdown selection with preset values for common rocket body tube sizes, like BT-5, BT-20, BT-50, etc, and maybe a few LOC and G12 sizes. I've provided a pretty extensive list of potential values below. I'd also like the option to specify a custom value too, but we should start with the provided list. I'm not including any body tubes over 4" in diameter (or 103.5mm) in this list.

    *   13.8 mm (BT-5)
    *   18.7 mm (BT-20)
    *   24.8 mm (BT-50)
    *   29.9 mm
    *   30.7 mm
    *  32.4 mm
    *   31.8 mm
    *   33.7 mm (BT-55)
    *   41.5 mm
    *  41.0 mm
    * 40.6 mm
    *  41.8 mm
    *   40.5 mm
    *   41.6 mm (BT-60)
    *   56.3 mm (BT-70)
    *   66.0 mm (BT-80)
    *   66.8 mm
    *   67.8 mm
    *   74.4 mm
    *   78.7 mm
    *  80.3 mm
    * 78.7 mm
    * 78.7 mm
    *  79.8 mm
    *   101.6 mm
    * 102.1 mm
    * 102.0 mm
    *   103.5 mm
*   **Guide-to-Tube Clearance:** This is the space between the inside of the guide and the outside of the tube, so the guide slides on easily. Let's make it adjustable from 0 to 1mm, with 0.1mm increments.
*   **Number of Fins:** 1 to 6 fins, as an integer value.
*   **Fin Thickness:** Adjustable from 0.5mm to 10mm, with 0.1mm increments.
*   **Guide Span:** The length of the guide arm from the tube, adjustable from 5mm to 100mm, in 0.5mm increments.
*   **Guide Height:**  The height of the fin guide walls, adjustable from 5mm to 100mm, in 0.5mm increments.
*   **Guide Wall Thickness:** Adjustable from 1mm to 10mm, in 0.2mm increments.
*   **Guide Foot Thickness:** Adjustable from 1mm to 10mm, in 0.2mm increments.
*   **Guide Foot Width:** Adjustable from 1mm to 10mm, in 0.2mm increments.
*   **Include End Flange:** A boolean option for whether or not to include the flange at the fin tip
*   **End Foot Thickness:** The thickness of the flange at the fin tip, adjustable from 1mm to 10mm, in 0.2mm increments.

I would like a single SCAD file that includes all of this. I don't anticipate using any specialized libraries, so I believe the design should be fairly straightforward. I expect to be able to use the OpenSCAD customizer with this.

Could you write the OpenSCAD code for this?

[Assistant]
Fin_Guide.scad:
```
/*

Fin positioning guides like this are not new, but it was done in a nice printed impelementation
by Dave Juliano in this thread on TRF:
https://www.rocketryforum.com/threads/3d-printer-plunge.146985/page-4

I liked it, so I stole it, with the OpenSCAD Customizer implementation here.

Usage is pretty much self-explanitory, and the best way to figure it out is just to play around
with the slider controls a bit.  See the blurb below, if you haven't used the Customizer before.

One difference I've made from the original is the addition of an optional flange at the 'fin tips'
of the guide, where I think there might be a weakness.  Also, since the sections are only held
together by a small web, I've included the option to thicken this web, though it might interfere
with the placement of the guide.

=============================================================================================
=================================  OpenSCAD Customizer ======================================
This file has been created with the intention to use the OpenSCAD 'Customizer', which allows
the use of Windows-style selection tools to set parameters of the component.  The use of this
feature requires the following steps:

1.  You must be using the OpenScad 'Development Snapshot' version, available at:
    http://www.openscad.org/downloads.html
    Scroll down to Development Snapshots,  use version OperSCAD 2018.09.05 or later.

2.  Open this file with OpenSCAD.

3.  In the OpenSCAD [Edit] menu, select [Preferences] then open tab [Features], check
    [Customizer], then close the window when the check is shown.

4.  In the OpenSCAD [View] menu, uncheck the option [Hide Customizer].  The Customizer
    window should open at the right side of the screen.

5.  At the top of the Customizer window, check 'Automatic Preview' and select 'Show Details'
    in the dropdown box.

6.  If you are comfortable with the instructions above, then also in the [View] menu, you can
	check the option [Hide editor], which will allow the full screen to view the model.  You
	can also check to [Hide Console].

*/

/* [Fin Guide Parameters] */
// Body Tube Diameter
//Tube_Diameter_mm = 25;

Tube_Diameter_OD_mm=24.8; // [0:use specified value, 13.8:BT5>>>>>>>OD>13.8mm>>0.544in, 18.7:BT20>>>>>>OD>18.7mm>>0.735in, 24.8:BT50>>>>>>OD>24.8mm>>0.976in, 29.9:29mm>>>>>>OD>29.9mm>>1.176in, 30.7:29mmTW>>>>OD>30.7mm>>1.210in, 32.4:29mmBlue>>OD>32.4mm>>1.276in, 31.8:29mmG12>>>OD>31.8mm>>1.250in, 33.7:BT55>>>>>>OD>33.7mm>>1.325in, 41.5:38mmLOC>>>OD>41.5mm>>1.635in, 41.0:38mmBlue>>OD>41.0mm>>1.616in, 40.6:38mmG12TW>OD>40.6mm>>1.600in, 41.8:38mmG12FW>OD>41.8mm>>1.645in, 40.5:40mm>>>>>>OD>40.5mm>>1.595in, 41.6:BT60>>>>>>OD>41.6mm>>1.637in, 56.3:BT70>>>>>>OD>56.3mm>>2.217in, 66.0:BT80>>>>>>OD>66.0mm>>2.600in, 66.8:LOC66mm>>>OD>66.8mm>>2.630in, 67.8:66mmBlue>>OD>67.8mm>>2.671in, 74.4:74mmTW>>>>ID>74.4mm>>2.930in, 78.7:LOC74mm>>>OD>78.7mm>>3.100in, 80.3:LOCM75mm>>OD>80.3mm>>3.162in, 78.7:G12TW>>>>>OD>78.7mm<<3.098in, 78.7:75mmBlue>>OD>78.7mm>>3.100in, 79.8:G12FFW>>>>OD>79.8mm>>3.141in, 101.6:LOC98mm>>>OD>101.6mm>4.000in, 102.1:LOCM98mm>>OD>102.1mm>4.020in, 102.0:98mmBlue>>OD>102.0mm>4.014in, 103.5:G12FFW>>>>OD>103.5mm>4.076in]

// limit to 4" diameter
//, 140.7:LOC137mm>>0D>140.7mm>5.540in, 140.2:136mmBlue>OD>140.2mm>5.518in, //141.3:G12FFW>>>>OD>141.3mm>5.562in, 154.4:152mmBlue>OD>154.4mm>6.079in, //157.2:G12FFW>>>>OD>157.2mm>6.188in, 195.0:LOC191mm>>OD>195.0mm>7.675in, //196.6:G12FFW>>>>OD>196.6mm>7.740in, 203.2:G12FFW>>>>OD>203.2mm>8.000in]

// Guide-to-Tube Clearance
Tube_Clearance_mm = 0.4; // [0:0.1:1.0]

Number_of_Fins = 3; // [1,2,3,4,5,6]

Fin_Thickness_mm = 3; // [0.5:0.1:10.0]

Guide_Span_mm = 25; // [5:0.5:100.0]

Guide_Height_mm = 15; // [5:0.5:100.0]

Guide_Wall_Thickness_mm = 2; // [1:0.2:10.0]

Guide_Foot_Thickness_mm = 2; // [1:0.2:10.0]

Guide_Foot_Width_mm = 4; // [1:0.2:10.0]
//
Include_End_Flange = true;
End_Foot_Thickness_mm = 4; // [1:0.2:10.0]

/* [Hidden] */
$fn = 180;
Guide_Tube_ID_mm = Tube_Diameter_OD_mm + 2*Tube_Clearance_mm;

difference() {
	union () {
		// basic tube
		cylinder (h = Guide_Height_mm, d = Guide_Tube_ID_mm + 2*Guide_Wall_Thickness_mm);
		// foot cylinder
		cylinder (h = Guide_Foot_Thickness_mm,
			d = Guide_Tube_ID_mm + 2*Guide_Wall_Thickness_mm + 2*Guide_Foot_Width_mm);
		for (j = [0 : 360/Number_of_Fins : 360-360/Number_of_Fins]) {
			rotate (a = [0, 0, j]) {
				// fin walls
				hull () {
					translate ([Guide_Span_mm/2, 0, Guide_Height_mm/4])
						cube (size = [Guide_Span_mm, Fin_Thickness_mm + 2*Guide_Wall_Thickness_mm,
							Guide_Height_mm/2], center = true);
					translate ([Guide_Span_mm - Guide_Foot_Width_mm/2, 0,
						Guide_Height_mm - Guide_Foot_Width_mm/2])
						rotate (a = [90, 0, 0])
						cylinder (h = Fin_Thickness_mm + 2*Guide_Wall_Thickness_mm,
							d = Guide_Foot_Width_mm, center = true);
					translate ([0, 0, Guide_Height_mm - Guide_Foot_Width_mm/2])
						rotate (a = [90, 0, 0])
						cylinder (h = Fin_Thickness_mm + 2*Guide_Wall_Thickness_mm,
							d = Guide_Foot_Width_mm, center = true);
				}
				// fin foot
				hull () {
					translate ([Guide_Span_mm/2 + Guide_Foot_Width_mm/2, 0, Guide_Foot_Thickness_mm/2])
						cube (size = [Guide_Span_mm,
							Fin_Thickness_mm + 2*Guide_Wall_Thickness_mm + 2*Guide_Foot_Width_mm, Guide_Foot_Thickness_mm],
							center = true);
					translate ([Guide_Span_mm + Guide_Foot_Width_mm - Guide_Foot_Width_mm/4,
						Fin_Thickness_mm/2 + Guide_Wall_Thickness_mm + Guide_Foot_Width_mm -
						Guide_Foot_Width_mm/2, 0])
						cylinder (h = Guide_Foot_Thickness_mm, d = Guide_Foot_Width_mm);
					translate ([Guide_Span_mm + Guide_Foot_Width_mm - Guide_Foot_Width_mm/4,
						-(Fin_Thickness_mm/2 + Guide_Wall_Thickness_mm + Guide_Foot_Width_mm -
						Guide_Foot_Width_mm/2), 0])
						cylinder (h = Guide_Foot_Thickness_mm, d = Guide_Foot_Width_mm);
				}

				// end web
				if (Include_End_Flange == true ) {
					difference() {
						hull () {
							translate ([Guide_Span_mm + Guide_Foot_Width_mm - Guide_Foot_Width_mm/4,
								Fin_Thickness_mm/2 + Guide_Wall_Thickness_mm + Guide_Foot_Width_mm -
								Guide_Foot_Width_mm/2, 0])
								cylinder (h = Guide_Foot_Thickness_mm, d = Guide_Foot_Width_mm);
							translate ([Guide_Span_mm + Guide_Foot_Width_mm - Guide_Foot_Width_mm/4,
								-(Fin_Thickness_mm/2 + Guide_Wall_Thickness_mm + Guide_Foot_Width_mm -
								Guide_Foot_Width_mm/2), 0])
								cylinder (h = Guide_Foot_Thickness_mm, d = Guide_Foot_Width_mm);
							translate ([Guide_Span_mm - Guide_Foot_Width_mm/2, 0,
								Guide_Height_mm - Guide_Foot_Width_mm/2])
								rotate (a = [90, 0, 0])
								cylinder (h = Fin_Thickness_mm + 2*Guide_Wall_Thickness_mm,
									d = Guide_Foot_Width_mm, center = true);
						}
						translate ([Guide_Span_mm, 0, Guide_Height_mm/2+End_Foot_Thickness_mm])
							cube (size = [Guide_Span_mm, Fin_Thickness_mm, Guide_Height_mm],
							center = true);
					}
				}
			}
		}
	}
	// subtract center tube
	cylinder (h = 3*Guide_Height_mm, d = Guide_Tube_ID_mm, center = true);
	// subtract fins
	for (j = [0 : 360/Number_of_Fins : 360-360/Number_of_Fins]) {
		rotate (a = [0, 0, j]) {
			translate ([Guide_Span_mm/2 + 0.01, 0, 0])
			cube (size = [Guide_Span_mm, Fin_Thickness_mm, 3*Guide_Height_mm], center = true);
		}
	}
}

```


[User]
Okay, I'm looking to create a configurable rail button standoff for model rockets, something like the one described in the attached file. I need it to be parametric, so I can adjust it for different rocket body tubes, button sizes, and standoff heights. I'd also like to be able to add some aerodynamic features.

Here's what I'm thinking, and what I need to be able to customize:

*   **Body Tube Diameter:**  I need to be able to specify the diameter of the body tube that the standoff will attach to. It would be really helpful if I could use a dropdown list of common sizes, like the attached file includes, instead of typing in a number each time.  The list should include common BT series tubes, from BT5 to BT80, and a few typical 29mm, 38mm, 66mm, 75mm, and 98mm tubes, maybe with options for LOC or G12 tubes. (I can provide specific diameters for different tubes, if necessary).
*   **Standoff Height:** I need a parameter for the standoff height, which is the height at which the rail button will sit above the body tube. I need to be able to specify this as a number in mm.
*  **Standoff Base Diameter:** A parameter for the standoff base diameter, that is, the width of the base where the standoff meets the body tube.  Also in mm.
*   **Standoff Top Diameter:** A parameter for the diameter of the top of the standoff, that is, the surface the rail button will mount to. In mm as well.
*  **Center Drill Diameter:** A parameter that specifies the diameter of the hole that goes vertically through the standoff for mounting a screw or bolt. Also in mm.
*   **Smoothness:** I'd like a parameter to control the smoothness of the curves for the model. I would like this to work via the `$fn` parameter of the built-in OpenSCAD primitives.
*   **Aerodynamic Option:** A boolean checkbox to add an aerodynamic 'tail' to the standoff.
*   **AeroFactor:** If the AeroButton option is selected, a number to control the length of the 'tail'.
*   **Aft Slant:** If the AeroButton option is selected, a number (in degrees) to control how far down the top of the 'tail' slants.
*   **Standoff Base Extension:**  A parameter to allow the base of the standoff to extend down to meet the tube smoothly, rather than having a sharp edge. I think a positive number of mm would be appropriate here.
*   **Standoff Base Elliptical Factor:** A number that controls how much the base of the standoff is extended in the fore-aft direction, without increasing its width. This parameter will multiply the width of the base along the long dimension. A value of 1 would mean no extension, and values greater than one would create an ellipse.
*   **Rail Button Recess:** A checkbox to create a recessed area in the top of the standoff for the rail button to sit in.
*   **Button Base Diameter:** If the rail button recess option is selected, a number for the diameter of this recess.
*   **Button Recess Depth:** If the rail button recess option is selected, a number for the depth of the recess.

I want the OpenSCAD code to output all of the dimensions to the console, similar to the included file, in mm and inches.

I don't think I'll need any external libraries for this, so it should be fairly straightforward. I am hoping to use the customizer features in OpenSCAD.

Could you generate the OpenSCAD code for this?

[Assistant]
Aero_Button_Standoff.scad:
```
// Note: Before attempting to use, see the section below on using the OpenSCAD Customizer.
/*===========================================================================================
                      CONFIGURABLE RAIL BUTTON STANDOFFS
	                       Gary A. Crowell Sr.  (VCP)

* Produces a conformal Rail Button Standoff for a range of button sizes, standoff height, and
	body tube diameters.

* The Rail Button Standoff is defined by:
	> The diameter of the tube it conformally fits.
        . Common body tube sizes between BT20 and 98mm are available.
	> The standoff height, above the surface of the body tube.  (Note that this is the height at
	  which the button sits, even if it is recessed.)
	> The diameter of the standoff base, at the surface of the body tube.
	> The diameter of the top of the standoff, i.e., at the base of the rail button.
	> The diameter of the drill/screw hold vertically through the center of the standoff.
	> Depending upon the speed of your computer, you might want to start with a low smoothness
	  level, like 60. Once you have the shape you want, increase the smoothness to 200 or more,
	  but it might take ~30 seconds to render the final shape.
	> A conical or aerodynamic standoff is selected by the AeroButton checkbox.
	> The AeroFactor setting determines the length of the 'tail'.
	> The AftSlant setting slants the 'deck' aft of the standoff downwards for a more aerodynamic
	  shape.  (I wish it could be rounded better, but, another time.)
	> The Standoff Base Extension extends the base of the standoff down to meet the tube, to
	  avoid a vertical edge at the base.  This does not affect the standoff height.
	> The Standoff Base Elliptical factor extends the base of the standoff fore and aft without
	  increasing its width.
	> Rail Button Recess allows the base of the rail button to be recessed into the top of the
	  standoff.  Recess Diameter is the diameter of the cavity, which should be smaller than the
	  top of the standoff.
	> Rail Button Recess Depth is how far the button is recessed into the top of the standoff.
	  Note that the bottom of the recess is at the standoff height.

* If you enable the console window (i.e., uncheck the 'hide console' box under the 'view' menu
	item) all of the standoff dimensions will be displayed there. (You may have to scroll that
	window to find them.)

=============================================================================================
=================================  OpenSCAD Customizer ======================================
This file has been created with the intention to use the OpenSCAD 'Customizer', which allows
the use of Windows-style selection tools to set parameters of the component.  The use of this
feature requires the following steps:

1.  You must be using the OpenScad 'Development Snapshot' version, available at:
    http://www.openscad.org/downloads.html
    Scroll down to Development Snapshots,  use version OperSCAD 2018.09.05 or later.

2.  Open this file with OpenSCAD.

3.  In the OpenSCAD [Edit] menu, select [Preferences] then open tab [Features], check
    [Customizer], then close the window when the check is shown.

4.  In the OpenSCAD [View] menu, uncheck the option [Hide Customizer].  The Customizer
    window should open at the right side of the screen.

5.  At the top of the Customizer window, check 'Automatic Preview' and select 'Show Details'
    in the dropdown box.

6.  If you are comfortable with the instructions above, then also in the [View] menu, you can
	check the option [Hide editor], which will allow the full screen to view the model.  You
	can also check to [Hide Console].

*/

/* [Tube Diameter] */
//Select Body Tube Diameter
Body_Tube=0.976; // [0.544:BT5>>>>>>>OD>13.8mm>>0.544in, 0.736:BT20>>>>>>>OD>18.7mm>>0.736in,  0.976:BT50>>>>>>OD>24.8mm>>0.976in, 1.175:29mm>>>>>>OD>29.9mm>>1.176in,  1.210:29mmTW>>>>OD>30.7mm>>1.210in, 1.276:29mmBlue>>>OD>32.4mm>>1.276in,  1.250:29mmG12>>>OD>31.8mm>>1.250in, 1.325:BT55>>>>>>>OD>33.7mm>>1.325in,  1.635:38mmLOC>>>OD>41.5mm>>1.635in, 1.616:38mmBlue>>>OD>41.0mm>>1.616in,  1.600:38mmG12TW>OD>40.6mm>>1.600in, 1.645:38mmG12FW>>OD>41.8mm>>1.645in,  1.595:40mm>>>>>>OD>40.5mm>>1.595in, 1.637:BT60>>>>>>>OD>41.6mm>>1.637in,  2.217:BT70>>>>>>>OD>56.3mm>>2.217in, 2.600:BT80>>>>>>>OD>66.0mm>>2.600in,  2.63:66mmLOC>>>>OD>66.8mm>>2.630in, 2.671:66mmBlue>>>OD>67.8mm>>2.671in,  3.000:74mmTW>>>>OD>76.2mm>>3.000in, 3.089:3.00TWFG>>>OD>78.5mm>>3.089in,  3.098:G12TW>>>>>OD>78.7mm>>3.098in, 3.100:3.00LOC>>>>OD>78.7mm>>3.100in,  3.100:75mmBlue>>OD>78.7mm>>3.100in, 3.141:3.00G12FW>>OD>79.8mm>>3.141in,  3.162:75mmLOC>>>OD>80.3mm>>3.162in, 4.000:98mmLOC>>>OD>101.6mm>>4.000in, 4.014:98mmBlue>>>OD>102.0mm>>4.014in, 4.020:98mmLOCMM>OD>102.1mm>>4.020in, 4.035:98mmTWFG>>>OD>102.5mm>>4.035in, 4.076:4.00G12FW>>OD>103.5mm>>4.076in]

/* [Standoff Basic Dimensions] */

StandOffHeight_mm = 10; // [0.5:0.25:50]
StandOffBaseDia_mm = 20; // [10:0.25:50]
StandOffTopDia_mm = 10; // [5:0.25:30]

//Center Drill Diameter
CenterDrillDia_mm = 3; // [1:0.25:5]

Smoothness = 60; // [30:15:360]

/* [Standoff Advanced Dimensions] */

// Add an aerodynamic tail to the standoff
AeroButton = false;

// Length of aerodynamic tail
AeroFactor = 3; // [0.25:0.25:5]

// Downward slant of aft deck
AftSlantDegree = 20; // [0:1:40]

// Base extension downward to meet tube
StandOffBaseExtension_mm = 0; // [0:0.25:10]

// Base fore-aft elliptical extension
StandOffBaseEllipticalFactor = 1; // [1:0.1:3]

/* [Rail Button Recess Dimensions] */
ButtonBaseDia_mm = 10; // [5:0.1:25]
ButtonRecessDepth_mm = 0; // [0:0.1:5]

/* [Hidden] */

TubeDia = Body_Tube*25.4;

difference() {
	union () {
		// standoff frustrum
		translate ([0, 0, -StandOffBaseExtension_mm]) {
			cylinder (h = StandOffHeight_mm + StandOffBaseExtension_mm + ButtonRecessDepth_mm,
				d1= StandOffBaseDia_mm, d2 = StandOffTopDia_mm, $fn = Smoothness/4);
			hull () {
				translate ([0, 0, StandOffHeight_mm + StandOffBaseExtension_mm + ButtonRecessDepth_mm])
				cylinder (h = 0.01, d = StandOffTopDia_mm, $fn = Smoothness/4);
				scale (v = [StandOffBaseEllipticalFactor, 1, 1])
					cylinder (h = 0.01, d = StandOffBaseDia_mm, $fn = Smoothness/4);
			}
		}
		// base extension down to meet tube
		translate ([0, 0, -TubeDia/4 - StandOffBaseExtension_mm])
			scale (v = [StandOffBaseEllipticalFactor, 1, 1])
			cylinder (h = TubeDia/4, d = StandOffBaseDia_mm, $fn = Smoothness/4);
	}
	// center drill
	translate ([0, 0, -TubeDia/2])
		cylinder (h = StandOffHeight_mm + 1 + TubeDia/2, d = CenterDrillDia_mm, $fn = 30);
	// cut for conformal tube
	translate ([0, 0, -TubeDia/2]) rotate (a = [0, 90, 0])
		cylinder (h = 20*StandOffBaseDia_mm, d = TubeDia, center = true, $fn = Smoothness);
	// button recess
	translate ([0, 0, StandOffHeight_mm])
		cylinder (h = 4*ButtonRecessDepth_mm, d = ButtonBaseDia_mm, $fn = 120);
}

if (AeroButton==true) {
	Aero ();
	mirror ([0, 1, 0]) Aero ();
}

module Aero () {
	difference() {
		hull () {
			// standoff frustrum
			translate ([0, 0, -StandOffBaseExtension_mm])
				cylinder (h = StandOffHeight_mm + StandOffBaseExtension_mm + ButtonRecessDepth_mm,
					d1= StandOffBaseDia_mm, d2 = StandOffTopDia_mm, $fn = Smoothness/4);
				//hull () {
				//	cylinder (h = 0.01, d = StandOffTopDia_mm, $fn = Smoothness/4);
				//	scale (v = [StandOffBaseEllipticalFactor, 1, 1])
				//		cylinder (h = 0.01, d = StandOffBaseDia_mm, $fn = Smoothness/4);
				//}
			// base extension down to meet tube
			translate ([0, 0, -TubeDia/4 - StandOffBaseExtension_mm])
				scale (v = [StandOffBaseEllipticalFactor, 1, 1])
				cylinder (h = TubeDia/4, d = StandOffBaseDia_mm, $fn = Smoothness/4);
			// aero disc at base
			translate ([0, AeroFactor*StandOffBaseDia_mm - StandOffBaseDia_mm/2, -StandOffBaseExtension_mm])
				rotate_extrude(convexity = 10, $fn = Smoothness) translate([AeroFactor*StandOffBaseDia_mm, 0, 0])
				circle(r = .001);
			// aero disc below base
			translate ([0, AeroFactor*StandOffBaseDia_mm - StandOffBaseDia_mm/2, -TubeDia/2 - StandOffBaseExtension_mm])
				rotate_extrude(convexity = 10, $fn = Smoothness) translate([AeroFactor*StandOffBaseDia_mm, 0, 0])
				circle(r = .001);
			// aero disc at top
			rotate (a = [0, -AftSlantDegree, 0])
				translate ([0, AeroFactor*StandOffTopDia_mm - StandOffTopDia_mm/2,
				StandOffHeight_mm + ButtonRecessDepth_mm])
				rotate_extrude(convexity = 10, $fn = Smoothness) translate([AeroFactor*StandOffTopDia_mm, 0, 0])
				circle(r = .001);
		}
		// slice off the front and sides of aero discs
		translate ([250, 0, 0]) cube (size = [500, 500, 500], center = true);
		translate ([0, 250, 0]) cube (size = [500, 500, 500], center = true);
		// flatten the top
		translate ([0, 0, 250 + StandOffHeight_mm + ButtonRecessDepth_mm]) cube (size = [500, 500, 500], center = true);
		// center drill
		translate ([0, 0, -TubeDia/2])
			cylinder (h = StandOffHeight_mm + 1 + TubeDia, d = CenterDrillDia_mm, $fn = 30);
		// cut for conformal tube
		translate ([0, 0, -TubeDia/2]) rotate (a = [0, 90, 0])
			cylinder (h = 20*StandOffBaseDia_mm, d = TubeDia, center = true, $fn = Smoothness);
		// button recess
		translate ([0, 0, StandOffHeight_mm])
			cylinder (h = 2*ButtonRecessDepth_mm, d = ButtonBaseDia_mm, $fn = 120);
	}
}

echo ( );
echo ("All Dimensions in mm, in" );
echo ("Body Tube Diameter", TubeDia, "mm ", Body_Tube, "in." );
echo ("Standoff Height", StandOffHeight_mm,"mm ", StandOffHeight_mm/25.4, "in." );
echo ("Standoff Base Diameter", StandOffBaseDia_mm,"mm ", StandOffBaseDia_mm/25.4, "in." );
echo ("Standoff Top Diameter", StandOffTopDia_mm,"mm ", StandOffTopDia_mm/25.4, "in." );
echo ("Center Dril Diameter", CenterDrillDia_mm,"mm ", CenterDrillDia_mm/25.4, "in." );
if (AeroButton==true) {
	echo ("Aero Factor", AeroFactor);
	echo ("Aft Deck Slant", AftSlantDegree, "degrees" );
}
echo ("Standoff Base Extension", StandOffBaseExtension_mm, "mm ");
echo ("Standoff Base Fore-Aft Extension Factor", StandOffBaseEllipticalFactor);
if (ButtonRecessDepth_mm > 0) {
	echo ("Button Recess Diameter", ButtonBaseDia_mm, "mm ");
	echo ("Button Recess Depth", ButtonRecessDepth_mm, "mm ");
}
echo ( );

```


[User]
I'm looking for a way to generate backplates for mounting rail buttons or guides inside model rocket body tubes. I want these backplates to conform to the inner curvature of the tube and have options for either a simple drill hole or a recessed area for a nut. I'd also like to be able to configure the thickness, slopes, and width of the backplate. It'd be ideal if I could specify a spacing for two holes or nuts, for rail guides with two mounting points.

Specifically, here's what I need to be able to control:

*   **Body Tube Inner Diameter:**  This is the diameter of the rocket body tube the backplate will fit inside.
*   **Nut/Drill Dimensions:**
    *   **Nut Diameter (Flat-to-Flat):** The size of the nut, measured across the flats. If I just want a drill hole, I should be able to set this to zero.
    *   **Nut Thickness:** The thickness of the nut.
    *   **Nut Standoff Distance:** How far the nut (or drill hole) should be from the inner tube surface. This will contribute to the overall plate thickness.
    *   **Drill Diameter:** The diameter of the drill hole.
    *   **Drill Spacing:** If I need two mounting points, this would be the distance between the two drill holes or nuts. If only one, this should be zero.
*   **Backplate Dimensions:**
    *   **Forward Slope Length:** The length of the slope leading up to the nut or drill hole.
    *   **Aft Slope Length:**  The length of the slope trailing from the nut or drill hole.
    *   **Aft Slope Trim:**  A way to trim the very end of the aft slope to avoid thin, hard-to-print edges.
    *   **Width Trim:**  A way to adjust the overall width of the backplate.

I'd like the design to be parametric, so I can easily adjust all these parameters to generate different backplates for various rocket sizes and rail guide configurations. It would be great if the OpenSCAD file included an echo statement that outputs all of the input parameters at the end. I am not familiar with any OpenSCAD libraries, so I would appreciate it if the design could be implemented using only the standard built-in functions.

[Assistant]
Config_Backplate.scad:
```
// Note: Before attempting to use, see the section below on using the OpenSCAD Customizer.
/*===========================================================================================
                   CONFIGURABLE RAIL BUTTON BACKPLATE/NUTPLATE
	                       Gary A. Crowell Sr.  (VCP)

* Produces a conformal tube interior backplate for mounting rail buttons or guides.

* The plate can contain just a drill hole sized to be tapped or self-tapped with a screw,
	or, a nut-sized well that can be configured to fit either a press fitted or glued nut.
	The drill hole and nut sizes are totally configurable.  Note that the Nut Diameter is
	measured from flat-to-flat.  If only a drill hole is desired, set the Nut Diameter to
	zero.

* The overall thickness of the plate is determined by the Nut Thickness and the Nut Standoff
	Distance.  Use these to adjust thickness even if the Nut Diameter is zero and all you
	want are drill holes.

* The slope of the backplate leading from the aft edge to the nut, can be adjusted as required
	to create a slope that won't interfere with the recovery system deployment.  In addition,
	since a gradual slope might produce a thin, hard to print and flush glue edge, the trailing
	edge can be trimmed to eliminate this edge.

* The forward slope leading up to the nut can be adjusted independently, since it is unlikely
	to require a gradual slope like the trailing edge.  This also makes it possible to use this
	edge as the print base, so little or no support will be required.

* The overall width can also be trimmed with a slope.

* By specifying a 'drill spacing' greater than zero, two in-line nuts (or drills) can be
	placed on the same backplate.

* Input parameters are echoed in the console window.

=============================================================================================
=================================  OpenSCAD Customizer ======================================
This file has been created with the intention to use the OpenSCAD 'Customizer', which allows
the use of Windows-style selection tools to set parameters of the component.  The use of this
feature requires the following steps:

1.  You must be using the OpenScad 'Development Snapshot' version, available at:
    http://www.openscad.org/downloads.html
    Scroll down to Development Snapshots,  use version OperSCAD 2018.09.05 or later.

2.  Open this file with OpenSCAD.

3.  In the OpenSCAD [Edit] menu, select [Preferences] then open tab [Features], check
    [Customizer], then close the window when the check is shown.

4.  In the OpenSCAD [View] menu, uncheck the option [Hide Customizer].  The Customizer
    window should open at the right side of the screen.

5.  At the top of the Customizer window, check 'Automatic Preview' and select 'Show Details'
    in the dropdown box.

6.  If you are comfortable with the instructions above, then also in the [View] menu, you can
	check the option [Hide editor], which will allow the full screen to view the model.  You
	can also check to [Hide Console].

*/

/* [Tube Inner Diameter] */
//Select Body Tube Inner Diameter
Body_Tube=0.976; // [0.518:BT5>>>>>>>ID>13.2mm>>0.518in, 0.710:BT20>>>>>>>ID>18.0mm>>0.710in,  0.950:BT50>>>>>>ID>24.1mm>>0.950in, 1.140:29mm>>>>>>ID>29.0mm>>1.140in,  1.140:29mmTW>>>>ID>29.0mm>>1.140in, 1.136:29mmBlue>>>ID>28.9mm>>1.136in,  1.139:29mmG12>>>ID>28.9mm>>1.139in, 1.283:BT55>>>>>>ID>32.6mm>>1.283in,  1.525:38mmLOC>>>ID>38.7mm>>1.525in, 1.504:38mmBlue>>>ID>38.2mm>>1.504in,  1.520:38mmG12TW>>ID>38.6mm>>1.520in, 1.518:38mmG12FW>>ID>38.6mm>>1.518in,  1.575:40mm>>>>>>ID>40.0mm>>1.575in, 1.595:BT60>>>>>>>ID>40.5mm>>1.595in,  2.180:BT70>>>>>>>ID>55.4mm>>2.180in, 2.558:BT80>>>>>>>ID>65.0mm>>2.558in,  2.560:66mmLOC>>>>ID>65.0mm>>2.560in, 2.551:66mmBlue>>>>ID>64.8mm>>2.551in,  2.930:74mmTW>>>>ID>74.4mm>>2.930in, 3.000:3.00TWFG>>>ID>76.2mm>>3.000in,  3.000:3.00LOC>>>>ID>76.2mm>>3.000in,  3.000:75mmBlue>>>>ID>76.2mm>>3.000in, 3.000:3.00G12FW>>>ID>76.2mm>>3.000in,  3.000:75mmLOCMM>>ID>>76.2mm>>3.000in, 3.900:98mmLOC>>>>ID>99.1mm>>3.900in, 3.900:98mmBlue>>>>ID>99.1mm>>3.900in, 3.900:98mmLOCMM>>ID>99.1mm>>3.900in, 3.890:98mmTWFG>>>ID>98.8mm>>3.890in, 3.892:4.00G12FW>>>ID>98.9mm>>3.892in]

/* [Nut/Drill Dimensions] */

// Nut Diameter (Face-to-Face)
NutDia_mm = 6; // [1:0.2:10]
// Nut Thickness
NutThickness_mm = 2.8; // [1:0.20:10]
// Nut Standoff Distance from Inner Tube Surface
NutStandoff_mm = 0.4; // [0:0.2:10]
// Drill Diameter
DrillDia_mm = 3.00; // [1:0.25:5]
// Drill Spacing if than one nut or drill hole
DrillSpacing_mm = 10.00; // [0.0:0.2:100]

/* [Backplate Dimensions] */

// Forward Slope Length
ForeSlopeLength_mm = 5; // [1:0.2:20]
// Aft Slope Length
AftSlopeLength_mm = 15; // [1:0.2:40]
// Aft Slope Trim
AftTrim_mm = 4; // [1:0.2:10]
// Top Width
//MinimumWidth_mm = 8; // [5:0.2:10]
// Side Width
WidthTrim = 40; // [5:0.5:100]

/* [Hidden] */

Thickness = NutThickness_mm + NutStandoff_mm;
NutDiaP2P = sqrt ( pow (0.577 * NutDia_mm/2, 2) + pow (NutDia_mm/2, 2)) * 2;
OverallLength_mm = ForeSlopeLength_mm + AftSlopeLength_mm - AftTrim_mm + NutDiaP2P;
TubeDia = Body_Tube*25.4;

difference() { // to subtract the drill(s), nut well(s), and aft trim
	intersection () {  // include only what is within the tube
		hull () {
			// center section nutwidth
			translate ([0, 0, Thickness/2])
				cube (size = [NutDiaP2P + DrillSpacing_mm, 1000, Thickness], center = true);
			// center section drillwidth
			translate ([0, 0, Thickness/2])
				cube (size = [DrillDia_mm + DrillSpacing_mm, 1000, Thickness], center = true);
			// forward edge
			translate ([ForeSlopeLength_mm + NutDiaP2P/2 + DrillSpacing_mm/2, 0, 0]) rotate (a = [90, 0, 0])
				cylinder (h = 1000, d = 0.01, center = true, $fn = 6);
			// aft edge
			translate ([-AftSlopeLength_mm - NutDiaP2P/2 - DrillSpacing_mm/2, 0, 0]) rotate (a = [90, 0, 0])
				cylinder (h = 1000, d = 0.01, center = true, $fn = 6);
		}
		hull () {
			// width edges
			translate ([0, WidthTrim/2, 0]) rotate (a = [0, 90, 0])
				cylinder (h = 1000, d = 0.01, center = true, $f = 6);
			translate ([0, -WidthTrim/2, 0]) rotate (a = [0, 90, 0])
				cylinder (h = 1000, d = 0.01, center = true, $f = 6);
			// center
			translate ([0, 0, Thickness/2])
				cube (size = [1000, NutDiaP2P, Thickness], center = true);
		}
		// body tube interior diameter
		translate ([0, 0, TubeDia/2]) rotate (a = [0, 90, 0])
			cylinder (h = 1000, d = TubeDia, center = true, $fn = 180);
	}
	// drill(s), nut well(s), and aft trim
	// Forward Nut Well
	translate ([DrillSpacing_mm/2, 0, NutStandoff_mm]) cylinder (h = 1000, d = NutDiaP2P, $fn = 6);
	// Forward Drill
	translate ([-DrillSpacing_mm/2, 0, NutStandoff_mm]) cylinder (h = 1000, d = NutDiaP2P, $fn = 6);
	// Aft Nut Well
	translate ([DrillSpacing_mm/2, 0, -1]) cylinder (h = 1000, d = DrillDia_mm, $fn = 20);
	// Aft Drill
	translate ([-DrillSpacing_mm/2, 0, -1]) cylinder (h = 1000, d = DrillDia_mm, $fn = 20);
	// Aft Trim
	translate ([-AftSlopeLength_mm - NutDiaP2P/2 - DrillSpacing_mm/2 + AftTrim_mm, 0, 0])
		rotate (a = [0, -35, 0]) translate ([0, 0, 50])
		cube (size = [100, 100, 100], center = true);
}

echo ( );
echo ("All Dimensions in mm, in" );
echo ("Body Tube Inner Diameter", TubeDia, "mm ", Body_Tube, "in." );
echo ("Drill Diameter", DrillDia_mm,"mm ", DrillDia_mm/25.4, "in." );
echo ("Nut F2F Diameter", NutDia_mm,"mm ", NutDia_mm/25.4, "in." );
if (DrillSpacing_mm > 0) {
	echo ("Drill Spacing Distance", DrillSpacing_mm,"mm ", DrillSpacing_mm/25.4, "in." );
}
echo ("Forward Slope Length", ForeSlopeLength_mm,"mm ", ForeSlopeLength_mm/25.4, "in." );
echo ("Aft Slope Length", AftSlopeLength_mm,"mm ", AftSlopeLength_mm/25.4, "in." );
if (AftTrim_mm > 0) {
	echo ("Aft Slope Trim", -AftTrim_mm,"mm ", -AftTrim_mm/25.4, "in." );
}
echo ("Overall Length", OverallLength_mm,"mm ", OverallLength_mm/25.4, "in." );
echo ( );

```


[User]
Hey, I'm looking to get a parametric OpenSCAD design for a headphone holder, similar to the one I've described. I've included the basic concept in the file "HeadphoneHolderV1.scad", but it's a bit messy, and I want something more flexible and easier to adjust.

Here's what I need:

1.  **Main Base/Clamp:**
    *   It should have a C-clamp design to attach to a tabletop.
    *   The clamp needs to be able to handle tabletops up to 30mm thick (I'd like to be able to adjust this parameter).
    *   There should be a main support arm extending outwards from the clamp.
    *   The overall length of the support arm (from the edge of the clamp to the end where the headphones rest) should be adjustable.
    *   The support arm should have a curved shape with an adjustable curve radius.
    *  The base needs to have a hole for a screw to secure it to the tabletop, and this should also be parametric.
2.  **Screw:**
    *   I need a matching screw designed to secure the base to the table, with parametric diameter, thread pitch, head size, and length, using an included module or library.
    *   Ideally, the screw can be generated using the same parameters as the screw hole on the base.

3. **Parametric Design:**
    *   Everything should be parametric, so I can easily adjust dimensions like the tabletop thickness, arm length, arm curve, screw size, etc.
    *   I want to be able to control the overall height of the screw from the bottom of the base, and the hole depth.

4.  **Libraries:**
    *  The design should use the `polyScrewThread` library for creating the screw threads, which I've included a modified version of, as well.

I want separate files for the base/clamp and screw, but both files should use a shared set of parametric values.

Let me know if you need more information.

[Assistant]
HeadphoneHolderV1.scad:
```

pieceType = "base"; // [base,screw, screwSmall]

cylinder_1= 30;
cylinder_2= 32;

//translate([25,0,25-10])rotate([-90,0,0])cylinder(150,cylinder_1,cylinder_1,$fn=100);

if(pieceType=="base")
{
    rotate([0,90,0])difference()
    {
    intersection()
    {
        translate([25,0,25])rotate([-90,0,0])cylinder(150,cylinder_1,cylinder_1,$fn=100);

    union()
    {
        intersection()
        {
            difference()
            {
                // arm
                translate([0,50,0])cube([50,80,50]);
                union()
                {

                    // hole bottom
                    translate([0,55,0])cube([50,130,30]);

                    // hole top
                    //translate([0,55,40])cube([50,130,10]);
                }
            }
            translate([25,55,25-10])rotate([-90,0,0])cylinder(150,cylinder_2,cylinder_2,$fn=100);
        }

        difference()
        {
            // base
            intersection()
            {
                cube([50,55,50]);
                //translate([25,50,25-10])rotate([-90,0,0])cylinder(150,cylinder_1,cylinder_1,$fn=100);
            }
            union()
            {
                // hole screw
                translate([0,0,13])cube([50,45,27]);

            }

        }
        translate([0,130,30])cube([50,5,22]);
    }
    }

    // schraubenloch
    translate([25,25,-20])hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,0,non_thread_length,non_thread_diameter);

    }
}

if(pieceType=="screw")
{
    // Schraube
    translate([25,25,-20])hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,0,non_thread_length,non_thread_diameter);
}

if(pieceType=="screwSmall")
{
    // Schraube
    translate([25,25,-20])hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,0,non_thread_length,non_thread_diameter);
}

/* 'Nut Job' nut, bolt, washer and threaded rod factory by Mike Thompson 1/12/2013, Thingiverse: mike_linus
 *
 * Licensing: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Australia License.
 * Further information is available here - http://creativecommons.org/licenses/by-nc-sa/3.0/au/deed.en_GB
 *
 * v2 8/12/2013 - added socket head types
 * v3 2/11/2014 - adjusted wing nut algorithm for better behaviour with unusual nut sizes and added ISO262 metric references
 * v4 31/12/2014 - added optional texture to socket heads, added ability to change the number of facets for a hex head
 * and adjusted wingnut base level on certain nut sizes
 * v5 11/1/2015 - added phillips and slot drive types and improved texture handling
 * v6 21/2/2015 - added wing ratio to wingnuts
 * v7 6/3/2016 - added extended options to control number of facets on nuts, square sockets (or any number of facets) and socket depth control
 * v8 1/1/2017 - modified library code to remove dependence on deprecated 'assign' statement
 *
 * This script generates nuts, bolts, washers and threaded rod using the library
 * script: polyScrewThead.scad (modified/updated version polyScrewThread_r1.scad)
 * http://www.thingiverse.com/thing:8796, CC Public Domain
 *
 * Defaults are for a 8mm diameter bolts, rod, matching nuts and wing nuts that work well together
 * without cleanup or modification. Some default parameters such as the nut outer diameter are deliberately
 * altered to produce a snug fit that can still be hand tightened. This may need to be altered
 * depending on individual printer variances, slicing tools, filament etc. Suggest printing a matching
 * bolt and nut and adjusting as necessary.  Note: slow print speeds, low temperatures and solid
 * fill are recommended for best results.
 */

/* [Component Type] */
//type						    = "bolt";//[nut,bolt,rod,washer]

/* [Bolt and Rod Options] */

//Head type - Hex, Socket Cap, Button Socket Cap or Countersunk Socket Cap (ignored for Rod)
head_type              			= "hex";//[hex,socket,button,countersunk]
//Drive type - Socket, Phillips, Slot (ignored for Hex head type and Rod)
drive_type              		= "socket";//[socket,phillips,slot]
//Distance between flats for the hex head or diameter for socket or button head (ignored for Rod)
head_diameter    				= 12;
//Height of the head (ignored for Rod)
head_height  					= 5;
//Diameter of drive type (ignored for Hex head and Rod)
drive_diameter					= 5;
//Width of slot aperture for phillips or slot drive types
slot_width					    = 1;
//Depth of slot aperture for slot drive type
slot_depth 					    = 2;
//Surface texture (socket head only)
texture                         = "exclude";//[include,exclude]
//Outer diameter of the thread
thread_outer_diameter           = 30;
//Thread step or Pitch (2mm works well for most applications ref. ISO262: M3=0.5,M4=0.7,M5=0.8,M6=1,M8=1.25,M10=1.5)
thread_step    					= 6;
//Step shape degrees (45 degrees is optimised for most printers ref. ISO262: 30 degrees)
step_shape_degrees 				= 50;
//Length of the threaded section
thread_length  					= 25;
//Countersink in both ends
countersink  					= 2;
//Length of the non-threaded section
non_thread_length				= 10;
//Diameter for the non-threaded section (-1: Same as inner diameter of the thread, 0: Same as outer diameter of the thread, value: The given value)
non_thread_diameter				= 40;

/* [Nut Options] */

//Type: Normal or WingNut
nut_type	                    = "normal";//[normal,wingnut]
//Distance between flats for the hex nut
nut_diameter    				= 12;
//Height of the nut
nut_height	  				    = 6;
//Outer diameter of the bolt thread to match (usually set about 1mm larger than bolt diameter to allow easy fit - adjust to personal preferences)
nut_thread_outer_diameter     	= 9;
//Thread step or Pitch (2mm works well for most applications ref. ISO262: M3=0.5,M4=0.7,M5=0.8,M6=1,M8=1.25,M10=1.5)
nut_thread_step    				= 6;
//Step shape degrees (45 degrees is optimised for most printers ref. ISO262: 30 degrees)
nut_step_shape_degrees 			= 50;
//Wing radius ratio.  The proportional radius of the wing on the wing nut compared to the nut height value (default = 1)
wing_ratio                      = 1;
wing_radius=wing_ratio * nut_height;

/* [Washer Options] */

//Inner Diameter (suggest making diameter slightly larger than bolt diameter)
inner_diameter					= 8;
//Outer Diameter
outer_diameter					= 14;
//Thickness
thickness					    = 2;

/* [Extended Options] */

//Number of facets for hex head type or nut. Default is 6 for standard hex head and nut
facets                          = 6;
//Number of facets for hole in socket head. Default is 6 for standard hex socket
socket_facets                   = 6;
//Depth of hole in socket head. Default is 3.5
socket_depth                    = 3.5;
//Resolution (lower values for higher resolution, but may slow rendering)
resolution    					= 0.5;
nut_resolution    				= resolution;

//Hex Bolt
if (type=="bolt" && head_type=="hex")
{
	hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,head_height,non_thread_length,non_thread_diameter);
}

//Rod
if (type=="rod")
{
	hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,0,non_thread_length,non_thread_diameter);
}

//Hex Nut (normally slightly larger outer diameter to fit on bolt correctly)
if (type=="nut" && nut_type=="normal")
{
	hex_nut(nut_diameter,nut_height,nut_thread_step,nut_step_shape_degrees,nut_thread_outer_diameter,nut_resolution);
}

//Wing Nut variation of hex nut. Cylinders added to each side of nut for easy turning - ideal for quick release applications
if (type=="nut" && nut_type=="wingnut")
{
	rotate([0,0,30])hex_nut(nut_diameter,nut_height,nut_thread_step,nut_step_shape_degrees,nut_thread_outer_diameter,nut_resolution); //nut
	translate([(nut_diameter/2)+wing_radius-1,1.5,wing_radius/2+1])rotate([90,0,0])wing(); //attach wing
	mirror(1,0,0)translate([(nut_diameter/2)+wing_radius-1,1.5,wing_radius/2+1])rotate([90,0,0])wing(); //attach wing
}

module wing()
{
	difference()
	{
		cylinder(r=wing_radius,h=3,$fn=64); //cylinder
		union()
		{
			translate([-wing_radius,-wing_radius-1,-0.5])cube([wing_radius*2,wing_radius/2,wing_radius*2]); //remove overhang so flush with base of nut
			rotate([0,0,90])translate([-wing_radius,wing_radius-1,-0.5])cube([wing_radius*2,wing_radius/2,wing_radius*2]); //remove overhangs so flush with side of nut
		}
	}
}

//Washer
if (type=="washer")
{
	difference()
	{
		cylinder(r=outer_diameter/2,h=thickness,$fn=100);
		translate([0,0,-0.1])cylinder(r=inner_diameter/2,h=thickness+0.2,$fn=100);
	}
}

//Socket Head Bolt
if (type=="bolt" && head_type!="hex")
{
	socket_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,head_height,non_thread_length,non_thread_diameter);
}

module phillips_base()
{
	linear_extrude(slot_width)polygon(points=[[0,0],[(drive_diameter-slot_width)/2,9/5*(drive_diameter-slot_width)/2],[(drive_diameter+slot_width)/2,9/5*(drive_diameter-slot_width)/2],[drive_diameter,0]]);
	translate([(drive_diameter-slot_width)/2,0,(drive_diameter+slot_width)/2])rotate([0,90,0])linear_extrude(slot_width)polygon(points=[[0,0],[(drive_diameter-slot_width)/2,9/5*(drive_diameter-slot_width)/2],[(drive_diameter+slot_width)/2,9/5*(drive_diameter-slot_width)/2],[drive_diameter,0]]);
}

module phillips_fillet()
{
	union()
	{
		translate([-(drive_diameter-slot_width)/2-(slot_width/2),slot_width/2,0])rotate([90,0,0])phillips_base();
		translate([0,0,9/5*(drive_diameter-slot_width)/2])union()
		{
			inner_curve();
			rotate([0,0,90])inner_curve();
			rotate([0,0,180])inner_curve();
			rotate([0,0,270])inner_curve();
		}
	}
}

module inner_curve()
{
	translate([slot_width/2,-slot_width/2,0])rotate([0,90,0])linear_fillet(9/5*(drive_diameter-slot_width)/2,drive_diameter/10);
}

//basic 2d profile used for fillet shape
module profile(radius)
{
  difference()
  {
    square(radius);
    circle(r=radius);
  }
}

//linear fillet for use along straight edges
module linear_fillet(length,profile_radius)
{
	translate([0,-profile_radius,profile_radius])rotate([0,90,0])linear_extrude(height=length,convexity=10)profile(profile_radius);
}

module phillips_drive()
{
	intersection()
	{
		phillips_fillet();
		cylinder(9/5*(drive_diameter-slot_width)/2,drive_diameter/2+(slot_width/2),slot_width/2);
	}
}

module socket_screw(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);
	$fn=60;

	difference()
	{
    		union()
    		{
        		if (head_type=="socket")
				{
					socket_head(hg,df);
				}

        		if (head_type=="button")
				{
					button_head(hg,df);
				}

        		if (head_type=="countersunk")
				{
					countersunk_head(hg,df);
				}

        		translate([0,0,hg])
        		if ( ntl == 0 )
        		{
          		cylinder(h=0.01, r=ntr, center=true);
        		}
        		else
        		{
            		if ( ntd == -1 )
            		{
                		cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            		}
            		else if ( ntd == 0 )
            		{
                		union()
                		{
                    		cylinder(h=ntl-st/2,r=od/2, $fn=floor(od*PI/rs), center=false);
                    		translate([0,0,ntl-st/2])
                    		cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                		}
            		}
            		else
            		{
                		cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            		}
        		}
        		translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    		}
		//create opening for specific drive type
		if (drive_type=="socket")
		{
			cylinder(r=drive_diameter/2,h=socket_depth,$fn=socket_facets); //socket
			#translate([0,0,socket_depth])cylinder(r1=drive_diameter/2,r2=0,h=drive_diameter/3,$fn=socket_facets); //socket tapers at base to allow printing without bridging and improve socket grip
		}
		else
		{
			if (drive_type=="phillips")
			{
				translate([0,0,-0.001])phillips_drive();
			}
			else //slot
			{
				translate([-(drive_diameter)/2,slot_width/2,-0.001])rotate([90,0,0])cube([drive_diameter,slot_depth,slot_width]);
			}
		}
	}
}

module socket_head(hg,df)
{
	texture_points=2*PI*(head_diameter/2);
	texture_offset=head_diameter/18;
	texture_radius=head_diameter/24;

	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   	cylinder(h=hg, r=rd0, $fn=60, center=false);
		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}

	if (texture=="include") //add texture to socket head. Adjust texture density and size using texture variables above
	{
		for (i= [1:texture_points])
		{
			translate([cos(360/texture_points*i)*(head_diameter/2+texture_offset), sin(360/texture_points*i)*(head_diameter/2+texture_offset), 1 ])
			rotate([0,0,360/texture_points*i])cylinder(r=texture_radius,h=head_height*0.6,$fn=3);
		}
	}

}

module button_head(hg,df)
{
	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   	cylinder(h=hg, r1=drive_diameter/2 + 1, r2=rd0, $fn=60, center=false);
		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}
}

module countersunk_head(hg,df)
{
	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   cylinder(h=hg, r1=rd0, r2=thread_outer_diameter/2-0.5, $fn=60, center=false);

		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}
}

/* Library included below to allow customizer functionality
 *
 * polyScrewThread_r1.scad    by aubenc @ Thingiverse
 *
 * Modified by mike_mattala @ Thingiverse 1/1/2017 to remove deprecated assign
 *
 * This script contains the library modules that can be used to generate
 * threaded rods, screws and nuts.
 *
 * http://www.thingiverse.com/thing:8796
 *
 * CC Public Domain
 */

module screw_thread(od,st,lf0,lt,rs,cs)
{
    or=od/2;
    ir=or-st/2*cos(lf0)/sin(lf0);
    pf=2*PI*or;
    sn=floor(pf/rs);
    lfxy=360/sn;
    ttn=round(lt/st+1);
    zt=st/sn;

    intersection()
    {
        if (cs >= -1)
        {
           thread_shape(cs,lt,or,ir,sn,st);
        }

        full_thread(ttn,st,sn,zt,lfxy,or,ir);
    }
}

module hex_nut(df,hg,sth,clf,cod,crs)
{

    difference()
    {
        hex_head(hg,df);

        hex_countersink_ends(sth/2,cod,clf,crs,hg);

        screw_thread(cod,sth,clf,hg,crs,-2);
    }
}

module hex_screw(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);

    union()
    {
        hex_head(hg,df);

        translate([0,0,hg])
        if ( ntl == 0 )
        {
            cylinder(h=0.01, r=ntr, center=true);
        }
        else
        {
            if ( ntd == -1 )
            {
                cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            }
            else if ( ntd == 0 )
            {
                union()
                {
                    cylinder(h=ntl-st/2,
                             r=od/2, $fn=floor(od*PI/rs), center=false);

                    translate([0,0,ntl-st/2])
                    cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                }
            }
            else
            {
                cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            }
        }

        translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    }
}

module hex_screw_0(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);

    union()
    {
        hex_head_0(hg,df);

        translate([0,0,hg])
        if ( ntl == 0 )
        {
            cylinder(h=0.01, r=ntr, center=true);
        }
        else
        {
            if ( ntd == -1 )
            {
                cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            }
            else if ( ntd == 0 )
            {
                union()
                {
                    cylinder(h=ntl-st/2,
                             r=od/2, $fn=floor(od*PI/rs), center=false);

                    translate([0,0,ntl-st/2])
                    cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                }
            }
            else
            {
                cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            }
        }

        translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    }
}

module thread_shape(cs,lt,or,ir,sn,st)
{
    if ( cs == 0 )
    {
        cylinder(h=lt, r=or, $fn=sn, center=false);
    }
    else
    {
        union()
        {
            translate([0,0,st/2])
              cylinder(h=lt-st+0.005, r=or, $fn=sn, center=false);

            if ( cs == -1 || cs == 2 )
            {
                cylinder(h=st/2, r1=ir, r2=or, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }

            translate([0,0,lt-st/2])
            if ( cs == 1 || cs == 2 )
            {
                  cylinder(h=st/2, r1=or, r2=ir, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }
        }
    }
}

module full_thread(ttn,st,sn,zt,lfxy,or,ir)
{
  if(ir >= 0.2)
  {
    for(i=[0:ttn-1])
    {
        for(j=[0:sn-1])
        {
			pt = [[0,0,i*st-st],
                 [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt-st       ],
                 [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt-st   ],
				 [0,0,i*st],
                 [or*cos(j*lfxy),     or*sin(j*lfxy),     i*st+j*zt-st/2     ],
                 [or*cos((j+1)*lfxy), or*sin((j+1)*lfxy), i*st+(j+1)*zt-st/2 ],
                 [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt          ],
                 [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt      ],
                 [0,0,i*st+st]];

            polyhedron(points=pt,faces=[[1,0,3],[1,3,6],[6,3,8],[1,6,4], //changed triangles to faces (to be deprecated)
										[0,1,2],[1,4,2],[2,4,5],[5,4,6],[5,6,7],[7,6,8],
										[7,8,3],[0,2,3],[3,2,7],[7,2,5]	]);
        }
    }
  }
  else
  {
    echo("Step Degrees too agresive, the thread will not be made!!");
    echo("Try to increase de value for the degrees and/or...");
    echo(" decrease the pitch value and/or...");
    echo(" increase the outer diameter value.");
  }
}

module hex_head(hg,df)
{
	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   cylinder(h=hg, r=rd0, $fn=facets, center=false);

		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}
}

module hex_head_0(hg,df)
{
    cylinder(h=hg, r=df/2/sin(60), $fn=6, center=false);
}

module hex_countersink_ends(chg,cod,clf,crs,hg)
{
    translate([0,0,-0.1])
    cylinder(h=chg+0.01,
             r1=cod/2,
             r2=cod/2-(chg+0.1)*cos(clf)/sin(clf),
             $fn=floor(cod*PI/crs), center=false);

    translate([0,0,hg-chg+0.1])
    cylinder(h=chg+0.01,
             r1=cod/2-(chg+0.1)*cos(clf)/sin(clf),
             r2=cod/2,
             $fn=floor(cod*PI/crs), center=false);
}
```


[User]
I'm looking for a parametric ear saver design for face masks, and I want it to be highly customizable. I'd like to be able to control the overall length, the width of the band, how many hooks are on each side, and the spacing between those hooks. I also want a central oval-shaped section that sits behind the head. I want to be able to control the size of that oval, both the overall dimensions and the size of the hole in the middle. The user should have the option to fill the oval, so it doesn't have a hole in the center. Finally, I need to be able to rotate the arms outwards from the oval center, and control the angle of that rotation. I’d like the code to be set up so that users can adjust those parameters via the Customizer.

I'm not familiar with OpenSCAD libraries, so I'm not sure what to ask for there. Could you please generate the necessary .scad files to do this?

[Assistant]
EarSaverV2.scad:
```
//-------------------------------------------------------------------------------------
// Ear Saver
//
// This OpenScad file creates an Ear Saver for Masks. I'm hoping this will be useful
// for people who find the multitudes of Ear Savers too uncomfortable to use.
//
// My inspiration for creating this design was that I could not find anything like
// it on the web. I mean there are no OpenScad versions.
//
// Author:  Andre Pruitt
// Date:    4/18/2020
// Version: 2.0
//-------------------------------------------------------------------------------------

// Over all length in mm
Length = 150; // [50:300]

// Width of the band in mm
Width =  10; // [1:13]

// Number of hooks per arm
Hook_Count =  4; // [1:10]

// Distance between each hook
Hook_Spacing = 10; // [8:20]

/* [Oval Dimensions] */
// Length of the central oval  in mm. Best if set to Lenth/2
Oval_Lenth =  54; // [10:150]

// Height of the central oval in mm. Best if greater than Width * 2
Oval_Height =  25; // [1:100]

// The ratio between the outer part of the oval and the inner hole.
Ratio = .79; // [0.6:0.01:0.87]

// Should the oval have a whole in its middle
Fill = false;

/* [Arm Rotation] */
// The amount each arm is rotated by in degrees
Rotation =   6; // [0:35]

/* [Render Quality] */
// The quality of the renderings
$fn = 200;

LegLength = Length/2.0 - Oval_Lenth / 2.0 * Ratio + 2;

//------------------------------------------------------------------------------
// This module will rotate around a point of origin. It does this by first
// moving the origin point, rotating around that point and then moving back
// to where it was before.
//
// The angles parameter is a vector of angles of degrees [X, Y, Z]
// The orig parameter is a position vector [X, Y, Z] of the origin to rotate around
//------------------------------------------------------------------------------
module rotateAround(angles, orig)
{
    translate(orig)
    rotate(angles)
    translate(-orig)
    children();
}

//-------------------------------------------------------------------------------------
// hook(x,y) - This module draws a single hook. It takes ab x offset and a y offset
//-------------------------------------------------------------------------------------
module hook(aXOffset = 35.3, aYOffset = 0)
{
  translate([aXOffset, aYOffset, 0])
  {
    translate([0,     9.8, 0]) cylinder(d = 5.5, h = 2);
    translate([0,    -9.8, 0]) cylinder(d = 5.5, h = 2);
    translate([-0.2, -9.8, 0]) cube([3, 20, 1]);
  }
}

//-------------------------------------------------------------------------------------
// Draw the center portion of the Ear Saver. The outer dimensions are defined
// by the Oval_Lenth and Oval_Height. The innder part of the ponytail hole
// is Ratio times the size of outer dimensions. You can change this if
// you want more or less plastic.
//-------------------------------------------------------------------------------------
difference()
{
  // Outer oval
  resize([Oval_Lenth, Oval_Height, 1]) cylinder(d=Oval_Lenth, h=1);

  // Remove the inner oval
  if(Fill == false)
  {
    translate([0, 0, -0.01])
      resize([Oval_Lenth * Ratio, Oval_Height * 0.625,])
        cylinder(d=Oval_Lenth * Ratio, h= 3);
  }
}

//-------------------------------------------------------------------------------------
// Draw Right Leg and rotate it. The leg consists of the hooks and a
// bar that stretches from the Ponytail holder to the last hook.
//-------------------------------------------------------------------------------------
rotateAround(Rotation, [Oval_Lenth / 2.0 * Ratio, 0])
{
  // Space the hooks along the arm
  for (tIndex = [0: Hook_Count-1])
  {
    hook(Length/2.0 - Hook_Spacing * tIndex);
  }

  // Draw the body of the arm
  translate([ Oval_Lenth / 2.0 * Ratio, -Width/2, 0])
     cube([LegLength, Width, 1]);
}

//-------------------------------------------------------------------------------------
// Draw Left Leg and rotate it. The leg consists of the hooks and a
// bar that stretches from the Ponytail holder to the last hook.
//-------------------------------------------------------------------------------------
rotateAround(-Rotation, [-Oval_Lenth / 2.0 * Ratio, 0])
{
  // Draw the hooks along the arm
  for (tIndex = [0: Hook_Count - 1])
  {
    rotate([0, 0, 180])
      hook(Length/2.0 - Hook_Spacing * tIndex);
  }

  // Draw the body of the arm
  translate([-LegLength - Oval_Lenth / 2.0 * Ratio, -Width/2, 0])
    cube([LegLength, Width, 1]);
}

```


[User]
I need a parametric OpenSCAD design for a static mixer and nozzle, specifically for injection mortar cartridges like the ones from fischer FIS. I'd like it to have a few key features and be customizable.

First, the design should include three main parts: a threaded connector to attach to the cartridge, a static mixer tube, and a nozzle. I want to be able to generate these all together or independently. So, I'll need a variable for showing/hiding the connector, mixer and nozzle. I'd also like an option to split the mixer and nozzle into two parts each. This would be for easier printing on smaller printers.

For the connector, it should have a threaded section, a straight section, and a transition to the mixer tube. The dimensions of these need to be variable, including things like the connector's bottom height, the straight section's height and diameter, and the thread diameter and height. Please use standard metric threads.

The mixer tube itself should have variable height and inner diameter, as well as a wall thickness parameter. Inside the tube, I need a series of alternating mixer elements. These elements should also have variable height and thickness. It should also have a threaded section at the top to connect to the nozzle, with variable height and diameter.

The nozzle should have a variable height and outer diameter, as well as a variable wall thickness. The inner diameter should be calculated from the outer diameter and wall thickness. If we split the nozzle into two parts, it should have a threaded connector similar to the mixer.

Finally, I'd like a cap as well, to seal the connector when not in use. It should attach to the bottom connector and also have some customizable features.

I want to be able to cutaway a quarter section of the parts for debugging purposes, so a boolean `cutaway` variable would be helpful for that.

I'd like all the values to be easily changeable. Please use the `threads.scad` library for generating the threads.

[Assistant]
mixer.scad:
```
//Static Mixer and Nozzle for Injection Mortar
//designed by iggy@yggi.de
//last changes 2020-04-21
//released under CC-BY

use <threads.scad>;

/* [Hidden] */
eps=0.1;
$fn=32;

/* [Main] */
//cut away a quarter of the parts, mainly for debugging
cutaway=false;
//generate the cap
show_cap=false;
//generate the mixer
show_mixer=true;
//generate the nozzle
show_nozzle=true;
//split the nozzle and mixer into two parts
two_part=true;

/* [Connector] */
bottom_h=24;
bottom_straight_h=6;
bottom_straight_d=19;
bottom_d=26;
thread_h=13;
thread_d=24;

/* [Mixer] */
//height of the mixer tube
tube_h=90;
//inner diameter of the mixer tube
tube_id=12;
//wall thickness
tube_wall=1.2;
tube_od=tube_id+2*tube_wall;
//height of a mixer element
mixer_h=6;
//thickness of a mixer element
mixer_wall=1.2;
//height of the threading at the top of the mixer
thread2_h=13;
thread2_d=22;//tube_id+2*tube_wall+4;

/* [Nozzle] */
//height of the nozzle (insertable)
tube2_h=90;
//outer diameter of the nozzle
tube2_od=7.5;
//wall thickness
tube2_wall=1.2;
tube2_id=tube2_od-2*tube2_wall;

module mixer_inner(){
    for(iz=[0:tube_h/mixer_h-1]) let(dz=iz*mixer_h, dir=iz%2) translate([0,0,dz]) rotate([0,0,90*dir]) mirror([dir,0,0]) linear_extrude(height=mixer_h,twist=180,$fn=64) square([tube_id+1,mixer_wall],center=true);
    }

module tube(){
    difference(){
        union(){
            cylinder(d=tube_od,h=tube_h);
            if(two_part) {
                translate([0,0,tube_h-thread2_h]) metric_thread(diameter=thread2_d,length=thread2_h,pitch=3,leadin=1);
                translate([0,0,tube_h-thread2_h-10]) cylinder(d1=tube_od,d2=thread2_d,h=10);
            }
        }
        translate([0,0,-eps]) cylinder(d=tube_id,h=tube_h+2*eps);
    }
}

module bottom(top_id){
    difference(){
        union(){
            cylinder(d=bottom_d,h=thread_h,$fn=64);
            translate([0,0,thread_h]) cylinder(d1=bottom_d,d2=bottom_straight_d+2*tube_wall,h=bottom_straight_h);
            translate([0,0,thread_h+bottom_straight_h]) cylinder(d1=bottom_straight_d+2*tube_wall,d2=top_id+2*tube_wall,h=bottom_h-thread_h-bottom_straight_h,$fn=64);
            }
        if(top_id>0) translate([0,0,thread_h+bottom_straight_h-eps]) cylinder(d1=bottom_straight_d,d2=top_id,h=bottom_h-thread_h-bottom_straight_h+2*eps);
        translate([0,0,thread_h-4*eps]) cylinder(d1=thread_d,d2=bottom_straight_d,h=bottom_straight_h+5*eps);

        translate([0,0,-eps]) metric_thread (diameter=thread_d, pitch=3, length=thread_h+2*eps,internal=true,leadin=0);
    }
}

module tube2(){
    difference(){
        union(){
            cylinder(d=tube2_od,h=tube2_h,$fn=32);
            if(two_part)cylinder(d1=tube2_id+2*tube_wall,d2=tube2_od,h=4);
            else cylinder(d1=tube_od,d2=tube2_od,h=10);
        }
        translate([0,0,-eps]) cylinder(d=tube2_id,h=tube2_h+2*eps,$fn=32);
        if(!two_part) cylinder(d1=tube_id,d2=tube2_id,h=10);
    }
}

module mixer(){
    mixer_inner();
    tube();
    translate([0,0,-bottom_h]) bottom(tube_id);
}

module nozzle(){
    tube2();
    if(two_part) translate([0,0,-bottom_h]) bottom(tube2_id);
}

module cap(){
    top_h=5;
    translate([0,0,-bottom_h]) bottom(0);
    //cylinder(d1=bottom_d-5,d2=bottom_d-15,h=top_h);
    translate([0,0,-bottom_h+thread_h+bottom_straight_h+2*eps]) intersection(){
        union() for(irz=[0,1]) rotate([0,0,90*irz]) translate([0,0,top_h/2]) cube([bottom_d,3,top_h],center=true);
        cylinder(d1=bottom_straight_d+2*tube_wall,d2=bottom_straight_d+2*tube_wall-10,h=top_h);
    }
}

if(show_cap) difference(){

    translate([-30,0,bottom_h]) cap();
    if(cutaway) translate([-30,0,-eps]) cube(200);
}

if(show_nozzle) {
    if(two_part) {
        difference(){
            translate([30,0,bottom_h]) nozzle();
            if(cutaway) translate([30,0,-eps]) cube(200);
        }
    } else {
        difference(){
            translate([0,0,tube_h+bottom_h]) nozzle();
            if(cutaway) translate([0,0,tube_h-eps]) cube(200);
        }
    }
}
if(show_mixer) difference(){
    translate([0,0,bottom_h]) mixer();
    if(cutaway) translate([0,0,-eps]) cube(200);
}
```
threads.scad:
```
/*
 * ISO-standard metric threads, following this specification:
 *          http://en.wikipedia.org/wiki/ISO_metric_screw_thread
 *
 * Copyright 2017 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.3.  2017-08-31  Default for leadin: 0 (best for internal threads).
 * 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.
// metric_thread (diameter=8, pitch=1, length=4);

// 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      - 0 (default): no chamfer; 1: chamfer (45 degree) at max-z end;
//               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=0, 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=0,
                      leadfac=1.0)
{
   // 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,
                  leadfac);
}

// ----------------------------------------------------------------------------
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
                        ]
            );
         }
      }
   }
}

```