[User]
I need an OpenSCAD design for small, customizable containers. I'd like to be able to specify the overall length, width, and height of the container. The containers should have rounded corners on the inside bottom to make it easier to retrieve small parts, like screws or washers. I'll also need to be able to specify the wall thickness.

Specifically, I'm looking for two versions: a single compartment container and a double compartment container where a divider separates the space into two equal parts. I need the design to be parametric, so I can easily change these dimensions. I'm planning on 3D printing these.

[Assistant]
containers.scad:
```
/*
 * Variables for Customizer
 * ==================================================================
 *
 */

// of the container (outside dimension in mm)
Length = 32;

// of the container (outside dimension in mm)
Width = 54;

// of the container (outside dimension in mm)
Height = 70;

// Number of compatments (single or double)
Type = "single"; //[single,double]

//-----------------
/* [AdditionalSettings] */

// if the walls (in mm)
Thickness = 1.55;

module container(length, width, height, wall_thickness)
{
	difference()
	{
		union()
		{
			minkowski()
			{
				translate([0, 0, height/2+10/2])
					cube([width-2*6, length-2*6, height+10-2*6], center = true);

				sphere(r = 6, $fn = 80);
			}

			translate([0, 0, -30])
			minkowski()
			{
				translate([0, 0, height/2+10/2+1+wall_thickness])
					cube([width-wall_thickness*2-2*(6-wall_thickness), length-wall_thickness*2-2*(6-wall_thickness), height+10-2*(6-wall_thickness)], center = true);

				sphere(r = (6-wall_thickness), $fn = 80);
			}

		}
		union()
		{
			translate([0, 0, 100/2+height])
				cube([width+2, length+2, 100], center = true);

			translate([0, 0, -100/2])
				cube([width+2, length+2, 100], center = true);

			minkowski()
			{
				translate([0, 0, height/2+10/2+wall_thickness])
					cube([width-wall_thickness*2-2*(6-wall_thickness), length-wall_thickness*2-2*(6-wall_thickness), height+10-2*(6-wall_thickness)], center = true);

				sphere(r = (6-wall_thickness), $fn = 80);
			}

		}
	}
}

module container_double(length, width, height, wall_thickness)
{
	difference()
	{
		union()
		{
			minkowski()
			{
				translate([0, 0, height/2+10/2])
					cube([width-2*6, length-2*6, height+10-2*6], center = true);

				sphere(r = 6, $fn = 80);
			}

			translate([0, 0, -30])
			minkowski()
			{
				translate([0, 0, height/2+10/2+1+wall_thickness])
					cube([width-wall_thickness*2-2*(6-wall_thickness), length-wall_thickness*2-2*(6-wall_thickness), height+10-2*(6-wall_thickness)], center = true);

				sphere(r = (6-wall_thickness), $fn = 80);
			}

		}
		union()
		{
			translate([0, 0, 100/2+height])
				cube([width+2, length+2, 100], center = true);

			translate([0, 0, -100/2])
				cube([width+2, length+2, 100], center = true);

			minkowski()
			{
				translate([0, length/4-wall_thickness/4, height/2+10/2+wall_thickness])
					cube([width-wall_thickness*2-2*(6-wall_thickness), (length-wall_thickness*3)/2-2*(6-wall_thickness), height+10-2*(6-wall_thickness)], center = true);

				sphere(r = (6-wall_thickness), $fn = 80);
			}

			minkowski()
			{
				translate([0, -length/4+wall_thickness/4, height/2+10/2+wall_thickness])
					cube([width-wall_thickness*2-2*(6-wall_thickness), (length-wall_thickness*3)/2-2*(6-wall_thickness), height+10-2*(6-wall_thickness)], center = true);

				sphere(r = (6-wall_thickness), $fn = 80);
			}

		}
	}
}

if(Type == "single")
{
    container(Length, Width, Height, Thickness);
}

if(Type == "double")
{
    container_double(Length, Width, Height, Thickness);
}

```


[User]
Okay, I'm looking to design a set of gears using OpenSCAD, and I'd like them to be easily customizable. I've seen some implementations that use the "modulus" approach rather than specifying pitch directly, and I think that's what I want. It seems simpler for prototyping.

Specifically, I need a way to create involute spur gears, and I want to be able to control these parameters:

*   **Modulus:**  The key parameter, essentially the ratio of tooth count to diameter. I need to set this.
*   **Tooth Count:** Obviously, the number of teeth.
*   **Gear Thickness:** The width of the gear.
*   **Center Hole Diameter:** The diameter of the bore in the center of the gear.
*   **Gear Twist:** I'd like the option to twist the teeth along the z-axis, so it becomes a helical gear.
*   **Hidden Teeth:**  The ability to create a partial gear by hiding some teeth.
*  **Tooth Pressure Angle:** To control the shape of the tooth profile. I want to be able to set standard angles, 14.5, 20, and 28 degrees.
*   **Rim Size:** How thick the rim of the gear needs to be.
*   **Number of Spokes:** The number of spokes connecting the rim to the hub. I want to also be able to remove all of the spokes for a solid gear.
*   **Keyway Height and Width:** To create a keyway for securing the gear to a shaft.
*   **Center Bore Shape:** The shape of the center hole, options should be round, hexagon, or square.
*   **Hub Height:** To extend the hub beyond the gear's thickness.
*   **Hub Diameter:** The outer diameter of the hub.

I'm not looking to use any existing libraries, but it would be great if the code was well organized, perhaps with helper functions, and easy to understand. I'm familiar with basic OpenSCAD concepts, so the code doesn't need to be super heavily commented, but something reasonable is preferable.

Could you generate the necessary OpenSCAD file(s) for me? I'd like to be able to simply change those parameters to generate different gears as needed.

[Assistant]
Gear2.scad:
```
///////////////////////////////////////////////////////////////////////////////////////////
// Public Domain Modulus Gear OpenSCAD code
// version 1.0
// This code defines a gear based on its modulus rather than pitch in mm,
// Modulus is the ration of tooth count:diameter, effectively the pitch of the gear is
// modulus * pi
// ie, a gear of mod=1 has a tooth pitch of 3.1415926 mm.
//
// The advantage of using modulus rather than pitch to define a gear is that it is very easy to
// prototype - for example, a gear of mod = 1 will have a diamter that matches the tooth count.
// with mod = 2, radius = tooth count.
//
// This code is based on the poblic domain Parametric Involute Spur Gear code by Leemon Baird
//found at http://www.thingiverse.com/thing:5505
//////////////////////////////////////////////////////////////////////////////////////////////
//
// Since the original file was public domain, this file is public domain as well.
// Use it for any purpose, including commercial applications.  Attribution would be nice,
// but is not required.  There is no warranty of any kind, including its correctness,
// usefulness, or safety.
//
//////////////////////////////////////////////////////////////////////////////////////////////
// An involute spur gear based on Modulus, with reasonable defaults for all the parameters.
// Normally, you should just choose the first 4 parameters, and let the rest be default values.
// Meshing gears must match mod, pressure_angle, and twist. Geras should be separated by the
// sum of their radii, where gear radius = mod * tooth count.
//////////////////////////////////////////////////////////////////////////////////////////////

/* [Variables] */
Modulus				= 1		;//[0.1:0.1:3]
Tooth_Count 		= 20	;//[6:1:500]
Gear_Thickness 		= 6 	;//[0.1:0.1:30]
Center_Hole_Diameter	= 4		;//[0.1:0.1:20]
Gear_Twist			= 0  	;//[0:0.5:360]
Hidden_Teeth		= 0    	;//[0:1:20]
Tooth_Pressure_Angle= 20	;//[14.5, 20, 28,]
Rim_Size 			= 5		;//[1:0.1:10]
Number_of_Spokes 	= 4		;//[1:1:10]
Keyway_Height		= 0		;//[0:0.1:10]
Keyway_Width		= 0		;//[0:0.1:8]
Center_Bore_Shape	= 4	;//[100:Round, 6:Hexagon, 4:Square]
/* [hidden] */
Hub_Height 		= 0 	;//[0:0.1:30]
Hub_Diameter			= 12	;//[6:0.5:40]

// Call the modgear module to create the gear using the parameters define above
// If using this as a library, you can delete this, or put a * before modgear to hide it.
modgear (
	mod    			= Modulus,    //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
	number_of_teeth = Tooth_Count,   //total number of teeth around the entire perimeter
	thickness       = Gear_Thickness,    //thickness of gear in mm
	hole_diameter   = Center_Hole_Diameter,    //diameter of the hole in the center, in mm
	twist           = Gear_Twist,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	teeth_to_hide   = Hidden_Teeth,    //number of teeth to delete to make this only a fraction of a circle
	pressure_angle  = Tooth_Pressure_Angle,   //Controls how straight or bulged the tooth sides are. In degrees.
	clearance       = 0.0,  //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
	backlash        = 0.0,  //gap between two meshing teeth, in the direction along the circumference of the pitch circle
	rim				= Rim_Size , 	//thickness of rim supporting the gear teeth
	hub_diameter	= Hub_Diameter,	//diameter of hub
	Hub_Height	    = Hub_Height , 	//extension of hub beyond gear thickness
	spoke_num 		= Number_of_Spokes ,	//number of spokes
	keyway			= [Keyway_Height,Keyway_Width],
    hole_polygon    = Center_Bore_Shape

);

//these 4 functions are used by modgear
function polar(r,theta)   = r*[sin(theta), cos(theta)];                            //convert polar to cartesian coordinates
function iang(r1,r2)      = sqrt((r2/r1)*(r2/r1) - 1)/PI*180 - acos(r1/r2); //unwind a string this many degrees to go from radius r1 to radius r2
function q7(f,r,b,r2,t,s) = q6(b,s,t,(1-f)*max(b,r)+f*r2);                         //radius a fraction f up the curved side of the tooth
function q6(b,s,t,d)      = polar(d,s*(iang(b,d)+t));                              //point at radius d on the involute curve

//*****************************************************

module modgear (
	mod    			= 1,    //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
	number_of_teeth = 15,   //total number of teeth around the entire perimeter
	thickness       = 6,    //thickness of gear in mm
	hole_diameter   = 3,    //diameter of the hole in the center, in mm
	twist           = 0,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	teeth_to_hide   = 0,    //number of teeth to delete to make this only a fraction of a circle
	pressure_angle  = 20,   //Controls how straight or bulged the tooth sides are. In degrees.
	clearance       = 0.0,  //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
	backlash        = 0.0,  //gap between two meshing teeth, in the direction along the circumference of the pitch circle
	rim				= 5 , 	//thickness of rim supporting the gear teeth
	hub_diameter	= 6,	//diameter of hub
	Hub_Height	= 0 , 	//extension of hub beyond gear thickness
	spoke_num 		= 4 ,	//number of spokes
	keyway			= [0,0],
    hole_polygon    = 100

) {
	pi = PI;
	mm_per_tooth = mod * pi;
	number_of_teeth = floor (abs(number_of_teeth));
	keycut = [ keyway[0] +hole_diameter/2 , keyway[1], 2*(thickness + Hub_Height)];
	if (teeth_to_hide > number_of_teeth) {teeth_to_hide = 0;}
	p  = mod * number_of_teeth/ 2; //radius of pitch circle
	c  = p + mod - clearance ;     //radius of outer circle
	b  = p*cos(pressure_angle);    //radius of base circle
	r  = p-(c-p)-clearance ;       //radius of root circle
	t  = mm_per_tooth/2-backlash/2;                //tooth thickness at pitch circle
	k  = -iang(b, p) - t/2/p/pi*180; {             //angle to where involute meets base circle on each side of tooth
		difference(){

            union() {
				for (i = [0:number_of_teeth - teeth_to_hide-1] )
					rotate([0,0,i*360/number_of_teeth])
						linear_extrude(height = thickness,  convexity = 10, twist = twist)
							polygon(
								points=[
									//[0, -hole_diameter/10],
									polar(r, -181/number_of_teeth),
									polar(r, r<b ? k : -180/number_of_teeth),
									q7(0/5,r,b,c,k, 1),q7(1/5,r,b,c,k, 1),q7(2/5,r,b,c,k, 1),q7(3/5,r,b,c,k, 1),q7(4/5,r,b,c,k, 1),q7(5/5,r,b,c,k, 1),
									q7(5/5,r,b,c,k,-1),q7(4/5,r,b,c,k,-1),q7(3/5,r,b,c,k,-1),q7(2/5,r,b,c,k,-1),q7(1/5,r,b,c,k,-1),q7(0/5,r,b,c,k,-1),
									polar(r, r<b ? -k : 180/number_of_teeth),
									polar(r, 181/number_of_teeth)
								]//,
								//paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]]
							);
				difference(){
					cylinder(h=thickness, r=r,  $fn=60);
					translate ([0,0,-0.001]) cylinder(h=2*thickness+1, r=r-rim,  $fn=60);
				}
				cylinder (h=thickness + Hub_Height,r=hub_diameter/2,$fn=60);		// hub

				for (i = [0:spoke_num - 1] )		//position the spokes
					rotate([0,0,i*360/spoke_num])
						translate ([-rim/2,0,0])
							cube ([rim,r-1,thickness]);

			}
			translate ([0,0,-0.001])cylinder (h=2*(thickness + Hub_Height),r=hole_diameter/2,$fn=hole_polygon);
			translate ([((keyway[0]+hole_diameter/2)/2),0,(thickness + Hub_Height-1)]) cube (keycut, center = true);
		}

	}
};

```


[User]
Hi, I'm looking to design a parametric device stand using OpenSCAD. I have a few devices of varying thicknesses, and I need the stand to be customizable.

Specifically, I'd like the following features:

*   **Adjustable Stand Width:** I need to be able to set the overall width of the stand.
*   **Adjustable Base Height:** I need to be able to control the height of the base on which the devices sit.
*   **Adjustable Divider Height:** I need to set the height of the dividers that separate the devices.
*   **Customizable Device Slots:** I need to be able to specify the number of devices the stand should hold (up to 10) and the thickness of each of those devices individually, so the slots fit the devices snugly.
*   **Device Spacing:** I need to be able to add a small tolerance/gap between the devices to accommodate slight variations in the devices and make them easier to remove.
*   **Fillets:** I want to add some fillets/rounded edges to the design to give it a smoother look:
    *   Fillets around the base of the stand.
    *   Fillets at the top edges of the dividers.
    *   Fillets at either end of the stand base.
*   **Optional Hexagonal Cutout Pattern:** I'd love to have the ability to add a hexagonal cutout pattern along the dividers for aesthetics. It should be customizable:
    *   I should be able to turn the pattern on and off.
    *   I need to set the distance between the hexagons.
    *   I should set the radius of the hexagons.
    *   I need to set the distance between the pattern and the edge of the divider (an offset).
    *   And, I need to be able to slightly offset the pattern horizontally and vertically so that it doesn't just align with the grid of the divider.

I think that covers everything. I'm happy to get a first pass at it, and then request changes. I'm expecting one or more `.scad` files in response. I'm also hoping you can handle the required math and logic to ensure that things line up correctly, and include appropriate use of `hull` and `difference` for things like the fillets and cutouts. I don't need you to use any external libraries like MCAD or BOSL.

[Assistant]
device_holder.scad:
```
/* [General] */
Stand_width = 70.01;
Stand_feet_length = 9.01;
Stand_base_height = 7.01;
Divider_thickness = 7.01;
Divider_height = 35.01;
// Extra spacing between devices
Device_tolerance = 0.2;
Device_1_thickness = 15.01;
Device_2 = true;
Device_2_thickness = 15.01;
Device_3 = true;
Device_3_thickness = 15.01;
Device_4 = false;
Device_4_thickness = 15.01;
Device_5 = false;
Device_5_thickness = 15.01;
Device_6 = false;
Device_6_thickness = 15.01;
Device_7 = false;
Device_7_thickness = 15.01;
Device_8 = false;
Device_8_thickness = 15.01;
Device_9 = false;
Device_9_thickness = 15.01;
Device_10 = false;
Device_10_thickness = 15.01;

/* [Hex pattern] */
Hex_on = false;
// Distance between hexagons
Hex_gap = 1.5;
// Radius of hexagons
Hex_radius = 5.5;
// Distance between pattern and edge of divider
Pattern_offset = 3.01;

/* [Fillets] */
// Fillet around the base of the stand
Base_fillet_radius = 8.01;
// Fillet at top of dividers
Divider_fillet_radius = 5.01;
// Fillet at either end of stand
Feet_fillet_radius = 5.01;

/* [Advanced] */
// Horizontal offset
Hex_x_offset = 0.01;
// Vertical offset
Hex_y_offset = 0.01;

/* [Hidden] */
$fn=80;
eps=0.01;
rows_needed = Divider_height / (Hex_radius + Hex_gap) + 1;
cols_needed = Stand_width / (Hex_radius + Hex_gap) + 1;

Device_thicknesses_1 = Device_2 ? concat(Device_1_thickness, Device_2_thickness) : Device_1_thickness;
Device_thicknesses_2 = Device_3 ? concat(Device_thicknesses_1, Device_3_thickness) : Device_thicknesses_1;
Device_thicknesses_3 = Device_4 ? concat(Device_thicknesses_2, Device_4_thickness) : Device_thicknesses_2;
Device_thicknesses_4 = Device_5 ? concat(Device_thicknesses_3, Device_5_thickness) : Device_thicknesses_3;
Device_thicknesses_5 = Device_6 ? concat(Device_thicknesses_4, Device_6_thickness) : Device_thicknesses_4;
Device_thicknesses_6 = Device_7 ? concat(Device_thicknesses_5, Device_7_thickness) : Device_thicknesses_5;
Device_thicknesses_7 = Device_8 ? concat(Device_thicknesses_6, Device_8_thickness) : Device_thicknesses_6;
Device_thicknesses_8 = Device_9 ? concat(Device_thicknesses_7, Device_9_thickness) : Device_thicknesses_7;
Device_thicknesses_9 = Device_10 ? concat(Device_thicknesses_8, Device_10_thickness) : Device_thicknesses_8;
Device_thicknesses = Device_thicknesses_9;

function add(v, i = 0, r = 0) = i < len(v) ? add(v, i + 1, r + v[i]) : r;
stand_length = add(Device_thicknesses)
    + (Divider_thickness + Device_tolerance * 2) * len(Device_thicknesses) + Divider_thickness
    + 2 * Stand_feet_length;
// Create dividers
distance = concat([Stand_feet_length], Device_thicknesses);
distances = [for (a=0, b=distance[0]; a < len(distance); a= a+1, b=b+(distance[a]==undef?0:distance[a])) b];

translate([-Stand_width/2,-stand_length/2,0]) {
    difference() {
        union() {
            // Create rounded base
            hull() {
                translate([Base_fillet_radius, Base_fillet_radius, 0]) {
                    cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                }
                translate([Stand_width-Base_fillet_radius, Base_fillet_radius, 0]) {
                    cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                }
                translate([Base_fillet_radius, stand_length-Base_fillet_radius, 0]) {
                    cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                }
                translate([Stand_width-Base_fillet_radius, stand_length-Base_fillet_radius, 0]) {
                    cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                }
            }

            for (i = [0:len(Device_thicknesses)]) {
                intersection() {
                    hull() {
                        y_off = distances[i] + Divider_thickness * i + Device_tolerance * (2 * i);
                        translate([Divider_fillet_radius, y_off+Divider_thickness, Divider_height+Stand_base_height-Divider_fillet_radius]) {
                            rotate(a=[90, 0, 0]) {
                                cylinder(Divider_thickness, Divider_fillet_radius, Divider_fillet_radius);
                            }
                        }
                        translate([Stand_width-Divider_fillet_radius, y_off+Divider_thickness, Divider_height+Stand_base_height-Divider_fillet_radius]) {
                            rotate(a=[90, 0, 0]) {
                                cylinder(Divider_thickness, Divider_fillet_radius, Divider_fillet_radius);
                            }
                        }
                        translate([0, y_off, Stand_base_height]) {
                            cube(Divider_thickness);
                        }
                        translate([Stand_width-Divider_thickness, y_off, Stand_base_height]) {
                            cube(Divider_thickness);
                        }
                    }

                    // extended version of the base to get curve on fillet
                    hull() {
                        translate([Base_fillet_radius, Base_fillet_radius, Stand_base_height]) {
                            cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Stand_width-Base_fillet_radius, Base_fillet_radius, Stand_base_height]) {
                            cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Base_fillet_radius, stand_length-Base_fillet_radius, Stand_base_height]) {
                            cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Stand_width-Base_fillet_radius, stand_length-Base_fillet_radius, Stand_base_height]) {
                            cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                        }
                    }
                }
            }

            difference() {
                // Curved rectangle to subtract from
                intersection() {
                    // Rectangle to intersect with
                    translate([0, Stand_feet_length-Feet_fillet_radius-eps, Stand_base_height]) {
                        cube([Stand_width, Feet_fillet_radius, Feet_fillet_radius]);
                    }

                    // extended version of the base to get curve on fillet
                    hull() {
                        translate([Base_fillet_radius, Base_fillet_radius, Stand_base_height]) {
                            cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Stand_width-Base_fillet_radius, Base_fillet_radius, Stand_base_height]) {
                            cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Base_fillet_radius, stand_length-Base_fillet_radius, Stand_base_height]) {
                            cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Stand_width-Base_fillet_radius, stand_length-Base_fillet_radius, Stand_base_height]) {
                            cylinder(Stand_base_height, Base_fillet_radius, Base_fillet_radius);
                        }
                    }
                }

                translate([-2, Stand_feet_length-Feet_fillet_radius, Stand_base_height+Feet_fillet_radius]) {
                    rotate(a=[0, 90, 0]) {
                        cylinder(Stand_width+4, Feet_fillet_radius, Feet_fillet_radius);
                    }
                }
            }

            difference() {
                // Curved rectangle to subtract from
                intersection() {
                    translate([0, stand_length-Stand_feet_length-eps, Stand_base_height]) {
                        cube([Stand_width, Feet_fillet_radius, Feet_fillet_radius]);
                    }

                    hull() {
                        translate([Base_fillet_radius, Base_fillet_radius, Stand_base_height]) {
                            cylinder(Feet_fillet_radius, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Stand_width-Base_fillet_radius, Base_fillet_radius, Stand_base_height]) {
                            cylinder(Feet_fillet_radius, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Base_fillet_radius, stand_length-Base_fillet_radius, Stand_base_height]) {
                            cylinder(Feet_fillet_radius, Base_fillet_radius, Base_fillet_radius);
                        }
                        translate([Stand_width-Base_fillet_radius, stand_length-Base_fillet_radius, Stand_base_height]) {
                            cylinder(Feet_fillet_radius, Base_fillet_radius, Base_fillet_radius);
                        }
                    }
                }

                // fillet-radius sized cylinder to subtract to form fillet
                translate([-2, stand_length-(Stand_feet_length-Feet_fillet_radius), Stand_base_height+Feet_fillet_radius]) {
                    rotate(a=[0, 90, 0]) {
                        cylinder(Stand_width+4, Feet_fillet_radius, Feet_fillet_radius);
                    }
                }
            }
        }

        if (Hex_on) {
            // Hex pattern
            intersection() {
                for (i=[0:rows_needed-1]) {
                    for (j=[0:cols_needed]) {
                        if (j % 2 != 0 || j != cols_needed) {
                            x_off = j * ((3 * Hex_radius / 2) + Hex_gap * (sqrt(3)/2)) + Hex_x_offset;
                            y_off = (j % 2 == 0) ? i * (Hex_radius*sqrt(3)+Hex_gap) + Hex_y_offset: -((Hex_radius * sqrt(3) / 2) + Hex_gap/2) + i * (Hex_radius*sqrt(3)+Hex_gap) + Hex_y_offset;
                            rotate([270, 0, 0]) {
                                linear_extrude(height=stand_length + 2, twist=0) {
                                    translate([x_off,-y_off]) circle(Hex_radius,$fn=6);
                                }
                            }
                        }
                    }
                }

                for (i = [0:len(Device_thicknesses)]) {
                    intersection() {
                        hull() {
                            y_off = distances[i] + Divider_thickness * i + Device_tolerance * (2 * i);
                            translate([Divider_fillet_radius, y_off+Divider_thickness, Divider_height+Stand_base_height-Divider_fillet_radius]) {
                                rotate(a=[90, 0, 0]) {
                                    cylinder(Divider_thickness, Divider_fillet_radius-Pattern_offset, Divider_fillet_radius-Pattern_offset);
                                }
                            }
                            translate([Stand_width-Divider_fillet_radius, y_off+Divider_thickness, Divider_height+Stand_base_height-Divider_fillet_radius]) {
                                rotate(a=[90, 0, 0]) {
                                    cylinder(Divider_thickness, Divider_fillet_radius-Pattern_offset, Divider_fillet_radius-Pattern_offset);
                                }
                            }
                            translate([Pattern_offset, y_off, Stand_base_height + Feet_fillet_radius]) {
                                cube(Divider_thickness);
                            }
                            translate([Stand_width-Divider_thickness-Pattern_offset, y_off, Stand_base_height+Feet_fillet_radius]) {
                                cube(Divider_thickness);
                            }
                        }

                        // extended version of the base to get curve on fillet
                        hull() {
                            translate([Base_fillet_radius, Base_fillet_radius+Pattern_offset, Stand_base_height]) {
                                cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                            }
                            translate([Stand_width-Base_fillet_radius, Base_fillet_radius+Pattern_offset, Stand_base_height]) {
                                cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                            }
                            translate([Base_fillet_radius, stand_length-Base_fillet_radius-Pattern_offset, Stand_base_height]) {
                                cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                            }
                            translate([Stand_width-Base_fillet_radius, stand_length-Base_fillet_radius-Pattern_offset, Stand_base_height]) {
                                cylinder(Divider_height, Base_fillet_radius, Base_fillet_radius);
                            }
                        }
                    }
                }
            }
        }
    }
}
```


[User]
I'd like to get a parametric motorcycle helmet stand designed in OpenSCAD. I've got a rough idea of what I want, but I need it to be adjustable.

The helmet rests upside down on the stand. The stand itself should have a base with three legs, and the top of the stand should be shaped to receive the helmet. I'm envisioning a sort of bowl shape, not a flat surface, that will hold the helmet.

Here are the key things I need to be able to customize:
*   **`dome_width`**: This is the full width of the helmet, side to side. This needs to be adjustable, probably with a range of around 210 to 280 mm.
*   **`minthick`**: This is the minimum thickness of the base; it determines how far off the table the helmet sits. Let's make this adjustable between 6 and 20 mm.

I'm picturing a design where the main part of the stand is essentially a body of revolution, with the three legs coming off of it. The legs should be angled, not straight down, and I'd like to be able to adjust that angle, even if it's an "advanced" parameter. The leg ends should be rounded. I'd also like a small bevel where the bowl starts to rise up from the base - another parameter if possible.

For calculating the helmet's shape, it's close to an ellipsoid. I'm assuming a cephalic index of 0.83, which is a decent average, but it would be nice if you could use that to define the dome's length as a function of `dome_width`.

I'm not sure how to best describe the shape, other than it's like a bowl. Perhaps the top edge isn't a perfect bowl. It should be a connected surface. The easiest way I can think to describe it is like you're taking an upside-down helmet and carving the shape out of a solid base with legs.

I'd like all of this designed parametrically in OpenSCAD, please. Could you also incorporate the `geodesic_hemisphere.scad` file, which I'm including as it will ensure that the "bowl" of the helmet stand has even faces?

[Assistant]
helmetstand.scad:
```
/*
Motorcycle helmet stand
by Alex Matulich
June 2024

On Printables: https://www.printables.com/model/909807
On Thingiverse: https://www.thingiverse.com/thing:6658298

This is a simple but elegant stand for a helmet, which stands upside-down in the stand. The helmet is assumed to be an ellipsoid dome with a different axis lengths in all three dimensions.
*/

// ---------- customizable parameters

// helmet dome full width (side to side)
dome_width = 210; //[210:280]

// minimum base thickness (helmet offset from table)
minthick = 12; //[6:20]

// ---------- advanced parameters ----------

module dummy(){}    // force customizer to stop here

// skull length/breadth proportion -  https://en.wikipedia.org/wiki/Cephalic_index
// use the high end of average range, since helmets tend to be rounder than heads
cephalic_index = 0.83;
// angle of side legs (0=parallel to main leg)
legangle = 50;
// leg end quarter-curve steps
lsteps = 48;
// bevel size
bevel = 3;

// ---------- initialize ----------

// max radial length of stand leg at base
leglen = dome_width/3;
// diameter of end of stand leg at base
legdia = dome_width/9;
// outward offset of top edge relative to bottom edge
bottomoffset = dome_width/25;

rwid = dome_width/2;  // helmet dome width radius
dhlen = rwid / cephalic_index; // helmet dome length radius
rdep = rwid;    // helmet dome vertical radius

scl = [dhlen/rwid, 1, rdep/rwid];
bzcirc = 0.55228475;    // parameter for bezier curve circle approximation
rleg = 0.5*legdia;  // radius of end of leg
hlegang = 90-0.5*legangle;  // orientation of angled legs
baseleglen = leglen - rleg; // leg base length excluding end radius

// ---------- render ----------

difference() {
    baseshape();
    translate([0,0,rdep+minthick])
        rotate([180,0,0]) scale(scl)
            geodesic_hemisphere(r=rwid, $fs=2);
}

// ---------- modules ----------

// Basic stand before helmet dome is carved out of it.
// Top surface isn't a bowl-shape, but simply triangulated from edges.
// Carving out the helmet dome makes it bowl-shaped.
module baseshape() {
    stack = [
        legprofile(ofst=-bevel, z=0),
        legprofile(ofst=0, z=bevel),
        legprofilesphere(ofst=bottomoffset, sphofst=bevel),
        legprofilesphere(ofst=bottomoffset-bevel, sphofst=-bevel)
    ];
    polyhedron_stack(stack);
}

// Build a polyhedron object from a stack of polygons. It is assumed that each polygon has [x,y,z] coordinates as its vertices, and the ordering of vertices follows the right-hand-rule with respect to the direction of propagation of each successive polygon.
module polyhedron_stack(stack) {
    nz = len(stack); // number of z layers
    np = len(stack[0]); // number of polygon vertices
    facets = [
        // close first opening
        [ for(j=[0:np-1]) j ],
        // define quads for polyhedron body
        for(i=[0:nz-2])
            for(j=[0:np-1]) let(k1=i*np+j, k4=i*np+((j+1)%np), k2=k1+np, k3=k4+np)
                [k1, k2, k3, k4],
        // close last opening
        [ for(j=[np*nz-1:-1:np*nz-np]) j ]
    ];
    polyhedron(flatten(stack), facets, convexity=10);
}

// ---------- functions ----------

// flatten an array of arrays
function flatten(l) = [ for (a = l) for (b = a) b ] ;

// project the leg profile onto the underside of a sphere scaled to helmet proportions
function legprofilesphere(ofst=0, sphofst=0) = let(
    lp = legprofile(ofst),
    nlp = len(lp)-1,
    rsph = rwid+sphofst
) [
    for(i=[0:nlp]) let(
        x = lp[i].x/scl[0],
        y = lp[i].y,
        d = sqrt(x*x+y*y)
        ) [ lp[i].x, y, (rsph-sqrt(rsph*rsph-d*d))*scl[2] + minthick-sphofst ]
];

function legprofile(ofst=0, z=undef, bzpts=false) = let(
    r = rleg+ofst,
    llen = baseleglen,
    tlen = llen+r,
    cl = cos(legangle),
    sl = sin(legangle),
    ca = cos(hlegang),
    sa = sin(hlegang),
    l2 = llen-bzcirc*r,//0.75*llen,
    bzp = [
        // right leg top
        [[tlen,0], [tlen,bzcirc*r], [llen+bzcirc*r,r], [llen,r]],
        let(c=[r*ca,r*sa], d=min(0.4*llen, pdist(c, [r/ca,0])))
            [[llen,r], [l2,r], [c.x+d*cos(hlegang-90),c.y+d*sin(hlegang-90)], c],
        // left top leg
        let(c=[r*ca,r*sa], d=min(0.4*llen, pdist(c, [r/ca,0])))
            [c, [c.x+d*cos(hlegang+90),c.y+d*sin(hlegang+90)], prot([-l2,r],-legangle), prot([-llen,r],-legangle)],
        [prot([-llen,r],-legangle), prot([-llen-bzcirc*r,r],-legangle), prot([-tlen,bzcirc*r],-legangle), [-tlen*cl,tlen*sl]],
        [[-tlen*cl,tlen*sl], prot([-tlen,-bzcirc*r],-legangle), prot([-llen-bzcirc*r,-r],-legangle), prot([-llen,-r],-legangle)],
        [prot([-llen,-r],-legangle), prot([-l2,-r],-legangle), [-r,min(0.4*llen,r*tan(legangle))], [-r,0]],
        // left bottom leg
        [[-r,0], [-r,-min(0.4*llen,r*tan(legangle))], prot([-l2,r],legangle), prot([-llen,r],legangle)],
        [prot([-llen,r],legangle), prot([-llen-bzcirc*r,r],legangle), prot([-tlen,bzcirc*r],legangle), [-tlen*cl,-tlen*sl]],
        [[-tlen*cl,-tlen*sl], prot([-tlen,-bzcirc*r],legangle), prot([-llen-bzcirc*r,-r],legangle), prot([-llen,-r],legangle)],
        let(c=[r*ca,-r*sa], d=min(0.4*llen, pdist(c, [r/ca,0])))
            [prot([-llen,-r],legangle), prot([-l2,-r],legangle), [c.x+d*cos(-hlegang-90),c.y+d*sin(-hlegang-90)], c],
        // right leg bottom
        let(c=[r*ca,-r*sa], d=min(0.4*llen, pdist(c, [r/ca,0])))
            [c, [c.x+d*cos(-hlegang+90),c.y+d*sin(-hlegang+90)], [l2,-r], [llen,-r]],
        [[llen,-r], [llen+bzcirc*r,-r], [tlen,-bzcirc*r], [tlen,0]]
    ],
    stepsize = 1/lsteps
) bzpts ? flatten(bzp) : // for displaying control points
    [
        for(n=[0:len(bzp)-1])
            for(t=[0:stepsize:0.9999]) let(p=bezier_point2d(t, bzp[n]))
                z==undef ? p : [p.x, p.y, z]
    ];

function prot(p, angle) = let(ca = cos(angle), sa=sin(angle))
    [p.x*ca - p.y*sa, p.x*sa + p.y*ca];

function pdist(p1, p2) = sqrt((p1.x-p2.x)^2+(p1.y-p2.y)^2);

// ---------- Bezier cubic curve

// quarter circle: [0,0], [0.52284,0], [1, 1-0.52284], [1,1]

// get an x,y coordinate of a cubic Bezier curve given 4 x,y control points given in the array p[][]
function bezier_point2d(t, p) = [ // t=0...1, p=array of 4 [x,y] control points
    bezier_coordinate(t, p[0][0],p[1][0],p[2][0],p[3][0]),
    bezier_coordinate(t, p[0][1],p[1][1],p[2][1],p[3][1])
];

// return coordinate of Bezier curve at location t in the interval 0...1
// must be called separately to pass x-, y-, (and z-) coordinate coefficients
function bezier_coordinate(t, n0, n1, n2, n3) = let(t2=t*t, tm=1-t, tm2=tm*tm)
    n0*tm2*tm + 3*n1*t*tm2 + 3*n2*t2 * (1-t) + n3*t2*t;

//=========================================
// Octsphere.scad
// included here for Thingiverse Customizer
//=========================================
/*
Geodesic Hemisphere
by Alex Matulich, April 2021
Requires OpenSCAD version 2019.05 or greater.

On Thingiverse: https://www.thingiverse.com/thing:4816358
On Prusaprinters: https://www.prusaprinters.org/prints/129119-geodesic-hemisphere-with-regular-polygon-equator

February 2022:
* Simplifications and improvements made throughout the code by Benjamin Blankertz, particularly a better approach to finding the closest root polygon match to any $fn value requested for the hemisphere.
* Root pyramid can now have up to 7 sides (was 6), giving a few more equatorial polygon possibilities.

Subdivision functions are taken from Geodesic Sphere (icosahedral sphere) by Jamie Kawabata - https://www.thingiverse.com/thing:1484333

USAGE
-----
To use this, simply put this file in the directory where you keep .scad files, and include this line in your other .scad files:

use <geodesic_hemisphere.scad>

Use it exactly like you would use the OpenSCAD sphere() primitive, with the same parameters.

EXPLANATION
-----------
This is a hemisphere with evenly-distributed faces, and unlike an icosahedral sphere, this hemisphere is rendered so that the equator is a regular polygon that can match up with other regular polygon shapes (cylinders etc.).

The default sphere in OpenSCAD is rendered as a circle rotated around a diameter. This results in a globe-shape with longitude and latitude edges, with many wasted facets concentrated at the poles.

To get a sphere with more uniformly-spaced facets, one can subdivide the triangular faces of an icosahedron into smaller faces projected onto a circumscribed sphere. This has three problems:

1. An icosahedron doesn't have an equator, so it is difficult to match another polygonal shape to it.
2. The cross-section of a regular icosahedron can be a regular decagon (orient it with a vertex at each pole and cut through an equatorial plane), but the diameter is slightly smaller than the largest diameter of the icosahedron.
3. An icosahedron is limited in the number of sides available in the polygon equator if you cut it in half. At a minimum, an icosahedron cut in half has a 10-sided equator. Subdividing it, you can get 20 sides, 40 sides, 80 sides... 5*2^(levels+1) sides, where levels is the number of subdivision iterations (with levels=0 being the original icosahedron).

This model fixes all problems for a hemisphere, which is normally what one wants to use when matching to some other shape like a cylinder. It subdivides a pyramid (3, 4, 5, 6, or 7 sided), which results in the base of the hemisphere being a regular polygon of the specified diameter of the hemisphere. The polygon can also have a larger variety of sides: 3*2^levels, 4*2^levels, 5*2^levels, 6*2^levels or 7*2^levels. This means the number of polygon sides possible is 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, etc. (one should never need to go higher than 192 or so).
*/

// ---------- generate hemisphere ----------

module geodesic_hemisphere(r=-1, d=-1) {
    rad = r > 0 ? r : d > 0 ? d/2 : 1; //radius=1 by default
    fn = $fn ? $fn : max(360/$fa, 2*PI*rad/$fs);

    // given fn, figure out best root polygon and number of levels to subdivide
    log2 = log(2.0);
    logfn = log(fn);
    minpoly = fn < 6 ? 3 : 4; // can use 3-sided if fn<=6
    maxpoly = fn > 6 ? 7 : 6;
    // pn[]=possible subdivision levels for polygon having number of sides = npoly * 2^pn
    pn = [ for(i=[minpoly:maxpoly]) (logfn-log(i))/log2 ];

    // how far the provided number of vertices will deviate from fn (bb)
    dpn = [ for(i=[0:len(pn)-1]) each
            [ abs((minpoly+i)*2^floor(pn[i]) - fn),
              abs((minpoly+i)*2^ceil(pn[i]) - fn) ] ];
    minidx = floor(argmin(dpn)/2); // get closest match

    npoly = minidx + minpoly; // sides of root polygon
    levels = floor(pn[minidx]) + argmin(dpn)%2; // subdivision levels
    //echo(requested = fn, got = npoly*2^levels, npoly = npoly);

    pyramid = [ // define a pyramid with npoly sides
        // vertices
        [ [0,0,1],         // north pole
          let(a=360/npoly) // equator
            for(i=[0:npoly-1]) [ cos(i*a), sin(i*a), 0]
        ],
        // faces
        [ [0,1,npoly], for(i=[1:npoly-1]) [0,i+1,i] ]
    ];

    // subdivide the pyramid
    subdiv_pyramid = multi_subdiv_pf(pyramid, levels);
    // create base vertices and append them
    basepts = let(lv=npoly*pow(2,levels)) [for(n=[0:lv]) let(t=n*360/lv) [cos(t), sin(t), 0]];
    hpts = concat(subdiv_pyramid[0], basepts);
    // create base face and add it
    baseface = [ let(lv=npoly*pow(2,levels),
                     pstart=len(hpts)-lv)
                 [ for(n=[0:lv-1]) pstart+n ]
               ];
    hfcs = concat(subdiv_pyramid[1], baseface);
    // render the cap
    scale(rad) polyhedron(points=hpts, faces=hfcs);

    // ========== functions ==========
    // taken from https://www.thingiverse.com/thing:1484333 by Jamie Kawabata

    // return the index of the minimum value in an array
    function argmin(v, k=0, mem=[-1, ceil(1/0)]) =
        (k == len(v)) ? mem[0] :
            v[k] < mem[1] ? argmin(v, k+1, [k, v[k]]) :
            argmin(v, k+1, mem);

    // polyhedron subdivision functions

    // given two 3D points on the unit sphere, find the half-way point on the great circle (euclidean midpoint renormalized to be 1 unit away from origin)

    function midpt(p1, p2) =
        let(mid = 0.5 * (p1 +p2))
        mid / norm(mid);

    // given a "struct" where pf[0] is vertices and pf[1] is faces, subdivide all faces into 4 faces by dividing each edge in half along a great circle (midpt function) and returns a struct of the same format, i.e. pf[0] is a (larger) list of vertices and pf[1] is a larger list of faces.

    function subdivpf(pf) =
        let (p=pf[0], faces=pf[1])
        [ // for each face, output six points
            [ for (f=faces)
                let (p0 = p[f[0]], p1 = p[f[1]], p2=p[f[2]])
                each
                [ p0, p1, p2, midpt(p0, p1), midpt(p1, p2), midpt(p0, p2) ]
            ],
        // now, again for each face, output four faces that connect those six points
            [ for (i=[0:len(faces)-1])
                let (base = 6*i)  // points generated in multiples of 6
                each
                [[base, base+3, base+5],
                 [base+3, base+1, base+4],
                 [base+5, base+4, base+2],
                 [base+3, base+4, base+5]
                ]
            ]
        ];

    // recursive wrapper for subdivpf that subdivides "levels" times
    function multi_subdiv_pf(pf, levels) =
        levels == 0 ? pf :
        multi_subdiv_pf(subdivpf(pf), levels-1);
}

```


[User]
I'm looking to create a parametric velocity stack for computer fans, and I'd like to have it generated in OpenSCAD. I've seen some examples, but I'd like one that's a bit more customizable. I need it to be able to adapt to different fan sizes, specifically with a mounting plate that matches standard fan dimensions.

Here are the core features I'm looking for:

*   **Parametric Fan Plate Size:** I need to be able to specify the fan mounting plate size, ideally from a list of standard sizes like 40mm, 50mm, 60mm, 70mm, 80mm, 92mm, 120mm, 140mm, 200mm, and 220mm. The mounting holes for the fan should be properly spaced based on the chosen plate size using a lookup.
*   **Inlet and Outlet Diameters:** I need parameters to control the diameter of the wider inlet and the narrower outlet where the stack meets the fan. The outlet diameter should be a bit smaller than the fan size.
*   **Stack Height:** I'd like a parameter for the overall height of the velocity stack.
*   **Center Cone Diameter:** There should be a parameter to set the diameter of the center hub of the fan which is present in the design as a central cone.
*   **Number of Fins:** I need a parameter to control the number of support fins inside the stack, and I'd like these fins to have variable thickness.
*   **Wall Thickness:** I need a parameter to control the wall thickness of the stack itself.
*   **Curve Smoothness:** I'd like a `$fn` parameter to control the smoothness of the curves, probably a relatively high number like 180.
*   **Mounting Hole Diameter:** I need a parameter to set the diameter of the mounting holes for attaching the stack to the fan.
*   **Corner Bezel:** I'd like a radius parameter to create rounded corners on the mounting plate.

The stack itself should have an elliptical profile that transitions between the inlet and outlet diameters. I'd like to have a cone with a supported core in the center. The fins need to provide support and also have variable thicknesses at several points. The mounting plate needs to have the proper spacing for the holes, and should be a specified thickness.

I'm really hoping the design avoids any crazy overhangs that might require lots of supports when printing, but supports will be needed.

I'd like the code to be well-structured and use modules where appropriate.

Also, could you use lookup functions and conditional statements to handle the fan mounting hole spacing based on the selected plate size? If I enter an invalid plate size, it should give me an error message and use a default spacing value.

I'm not really looking for any particular libraries to be used in the generation of the code. Could you also generate STL outputs for each file you generate so that I have a quick way to check them?

Thanks!

[Assistant]
velocitystack.scad:
```
// Basic parameters

// mounting plate should match fan size in mm.
plate_size = 120; // [40,50,60,70,80,92,120,140,200,220]
// size of the wider opening
inlet_diameter = 150;
// size where it meets the fan inlet (should be a few mm smaller than the fan size)
outlet_diameter = 118;
// recommend a number between 1/2 and 1 times the fan size.
stack_height = 60;
// diameter of the center hub of the fan.
cone_d = 40;
// recommend using an odd number that is different from and not a factor of or divisible by the number of fan blades or the number of stator blades. More is stronger and quieter, fewer has better airflow.
num_fins = 3;
// use the single wall thickness you intend to slice/print with for the thinnest and strongest possible fins.
fin_thickness = 0.38;
// smoothness of curves.
$fn = 180;

// Advanced Parameters

/* [Hidden] */
// 2 is a nice number, maybe don't mess with this unless you have to
wall_thickness = 2;
// curve radius for the corners of the mounting plate.
bezel = 5;
 // diameter of the mounting holes
mounting_hole_d = 4.1;

// Calculated values
inlet_radius = outlet_diameter / 2;
outlet_radius = inlet_diameter / 2;
ellipse_height = stack_height * 2;
ellipse_width = inlet_diameter - outlet_diameter;
fin_height = cone_d/2;

// Assign the mounting hole spacing based on the plate_size using lookup function
function get_mounting_hole_spacing(size) =
    lookup(size, [
        [40, 32],
        [50, 40],
        [60, 50],
        [70, 60],
        [80, 71.5],
        [92, 82.5],
        [120, 105],
        [140, 124.5],
        [200, 154],
        [220, 170]
    ]);

mounting_hole_spacing = get_mounting_hole_spacing(plate_size);

// Check if the plate_size is valid
if (mounting_hole_spacing == undef) {
    echo("Error: Invalid plate_size. Using default value of 105.");
    mounting_hole_spacing = 105;
}

// create elliptical stack profile
module velocity_stack_profile() {
    difference() {
        intersection() {
            translate([ellipse_width/2, 0, 0]) {
                difference() {
                    resize([ellipse_width, ellipse_height]) circle(r = outlet_radius);
                    resize([ellipse_width - wall_thickness*2, ellipse_height - wall_thickness*2]) circle(r = outlet_radius - wall_thickness);
                }
            }
            square([ellipse_width/2, ellipse_height]);
        }
        translate([ellipse_width/2 + 1/sqrt(2)*wall_thickness/2, ellipse_height/2 - sqrt(2)*wall_thickness, 0]) rotate([0, 0, 60]) square(wall_thickness*2, center = true);
    }
}

// make holes
module mounting_hole() {
    cylinder(d = mounting_hole_d, h = wall_thickness*3, center = true);
}

// main module
module velocity_stack() {
    rotate_extrude() translate([inlet_radius, 0, 0]) velocity_stack_profile();

    difference() {
        minkowski() {
            cube([plate_size - 2*bezel, plate_size - 2*bezel, wall_thickness], center = true);
            cylinder(r = bezel, h = 0.1, center = true);
        }
        cylinder(d = outlet_diameter, h = wall_thickness*3, center = true);

        // Place mounting holes based on mounting_hole_spacing
        for (x = [-1, 1], y = [-1, 1]) {
            translate([x * mounting_hole_spacing/2, y * mounting_hole_spacing/2, 0]) mounting_hole();
        }
    }

    // Center cone with supported core
    rotate_extrude() translate([-cone_d/2, 0, 0]) velocity_stack_profile();
    translate([0, 0, stack_height/2]) {
        cylinder(h = stack_height, d = cone_d - (inlet_diameter - outlet_diameter), center = true);
        translate([0, 0, -wall_thickness/2]) cylinder(h = stack_height - wall_thickness, d = cone_d - (inlet_diameter - outlet_diameter) + wall_thickness, center = true);
    }

    // Support fins
    for (i = [0 : num_fins - 1]) {
        rotate(i * 360 / num_fins) {
            translate([0, inlet_radius/2, fin_height/2]) cube([fin_thickness, inlet_radius + 2*wall_thickness, fin_height], center = true);
            translate([0, inlet_radius/2, 3*fin_height/5]) {
                cube([fin_thickness*2, inlet_radius + 2*wall_thickness, 2*fin_height/3], center = true);
                cube([fin_thickness*3, inlet_radius + 2*wall_thickness, 2*fin_height/5], center = true);
            }
        }
    }
}

// Render the velocity stack
velocity_stack();

```


[User] I'd like to design a printable bearing, and I have some specific parameters in mind that I want to be able to easily adjust. The basic design consists of an outer race, an inner race, and a series of rollers in between. I'd like the ability to control the inner diameter, outer diameter, overall height of the bearing, and the wall thickness. Also, I need to specify the gap between the rollers and the races. I want to make sure that the rollers are generated using a proper calculation to determine how many fit into the given space. The races will consist of a series of flat and angled faces and be created using cylinders and subtractions. I'd like an OpenSCAD file that creates this bearing using parameters. Also, I'd like to define those parameters when I use the module. For instance, I'd like to say `printedbearing(diameter_in=8, diameter_out=22, height=7, wall_width=0.79);` and have those values used when creating the bearing. I'd like this module to be called `printedbearing()`. Can you create this for me?

[Assistant]
printedbearing.scad:
```
// Printed Bearing
// by Radus 2018
// http://vk.com/linuxbashev

// Parameters
diameter_in=8;   // Inner Diameter
diameter_out=22; // Outer Diameter
height=7;        // Height
wall_width=0.79; // Wall Width

printedbearing(diameter_in,diameter_out,height, wall_width);  // 608

// Examples
//translate([0,0,0])  printedbearing(3,10,4, 0.52);  // 623
//translate([20,0,0]) printedbearing(4,13,5, 0.65);  // 624
//translate([-20,0,0]) printedbearing(5,16,5, 0.81); // 625
//translate([0,20,0]) printedbearing(6,19,6, 0.76);  // 626
//translate([0,-20,0]) printedbearing(7,22,7, 0.83); // 627
//translate([25,25,0]) printedbearing(8,24,8, 0.79);   // 628
//translate([25,-25,0]) printedbearing(8,22,7, 0.79);  // 608
//translate([-25,-25,0]) printedbearing(9,26,8, 1.11); // 629
//translate([-25,25,0]) printedbearing(10,26,8, 1.07); // 6000
//translate([0,0,0]) printedbearing(20,42,12, 1.64); // 6004
//translate([0,0,0]) printedbearing(100,150,24, 2.06); // 6020

function catb(cata,catang)=cata/cos(catang)*sin(catang);

module printedbearing(pdi=8, pdo=22, ph=7, pw=1){

do=pdo;
di=pdi;
h=ph;
w2=pw;
w1=w2;

zz=0.15;             // gap between rollers and base

dr=do/2-di/2-w1*2-w2*2-zz*2;

h2=catb(w1,45);     // angle inside rollers
//h3=(dr-h2*2)/1.5;
//h1=(h-h3-h2*2)/2;
h1=w1;
h3=h-h1*2-h2*2;

n=floor(2*PI*(di/2+(do/2-di/2)/2)/(dr+w1*2));
//echo(n);
sph=0.15;
spw=0.6;
$fn=64;

module bout(){
difference(){
union(){
translate([0,0,h/2]) cylinder(h,do/2,do/2,true);
} // un

translate([0,0,h1/2-1/2]) cylinder(h1+1.1,do/2-w1-w2,do/2-w1-w2,true);
translate([0,0,h1+h2/2]) cylinder(h2,do/2-w1-w2,do/2-w2,true);
translate([0,0,h1+h2+h3/2]) cylinder(h3+0.02,do/2-w2,do/2-w2,true);
translate([0,0,h1+h2+h3+h2/2]) cylinder(h2,do/2-w2,do/2-w2-w1,true);
translate([0,0,h1+h2+h3+h2+h1/2+1/2]) cylinder(h1+1.1,do/2-w2-w1,do/2-w2-w1,true);
} // df
} // mod

module bin(){
difference(){
union(){
translate([0,0,h1/2]) cylinder(h1,di/2+w2+w1,di/2+w2+w1,true);
translate([0,0,h1+h2/2]) cylinder(h2,di/2+w2+w1,di/2+w2,true);
translate([0,0,h1+h2+h3/2]) cylinder(h3,di/2+w2,di/2+w2,true);
translate([0,0,h1+h2+h3+h2/2]) cylinder(h2,di/2+w2,di/2+w2+w1,true);
translate([0,0,h1+h2+h3+h2+h1/2]) cylinder(h1,di/2+w2+w1,di/2+w2+w1,true);
} // un

translate([0,0,h/2]) cylinder(h+1,di/2,di/2,true);
} // df
} // mod

module rol(){
union(){
translate([0,0,h1/2]) cylinder(h1,dr/2,dr/2,true);
translate([0,0,h1+h2/2]) cylinder(h2,dr/2,dr/2+w1,true);
translate([0,0,h1+h2+h3/2]) cylinder(h3,dr/2+w1,dr/2+w1,true);
translate([0,0,h1+h2+h3+h2/2]) cylinder(h2,dr/2+w1,dr/2,true);
translate([0,0,h1+h2+h3+h2+h1/2]) cylinder(h1,dr/2,dr/2,true);
} // un
} // mod

module sp(){
difference(){
translate([0,0,sph/2]) cylinder(sph, di/2+(do/2-di/2)/2+spw/2, di/2+(do/2-di/2)/2+spw/2,true);
translate([0,0,sph/2]) cylinder(sph+1, di/2+(do/2-di/2)/2-spw/2, di/2+(do/2-di/2)/2-spw/2,true);
} // df
} // mod sp

difference(){
union(){
bout();
bin();
for (r=[0:n-1]) rotate([0,0,360/n*r])
translate([di/2+(do/2-di/2)/2,0,0]) rol();
sp();
} //un
//cube([100,100,100]);
} // df

} // mod prinbe

```


[User]
Hey, I'm looking to generate some model rocket nose cones in OpenSCAD, and I want something pretty flexible. I've seen a few examples, but they don't quite give me the control I need.

Here's what I'm thinking:

I need a main file, let's call it `rocket_nose_cone.scad`.  This file should define all the key parameters that control the cone's shape and fit. I want to be able to easily change:

*   **Inner Diameter:** This is the diameter of the rocket body tube the cone will slide into.
*   **Outer Diameter:**  This is the outer diameter of the rocket body tube; the base of the cone should match this.
*   **Cone Length:** How long the pointy part of the nose cone is.
*   **Cone Shape:** Here's where it gets interesting, I want to be able to select from a few different shapes:
    *   **Parabolic:**  A standard parabolic shape.
    *   **Power Series:** This would use a power series to define the curve - I should be able to adjust the power.
    *   **Haack:**  A Sears-Haack body, with a control for the "C" parameter.
    *   **Default:** I'd also like a reasonable default that is a sort of squashed sphere shape.
*   **Adapter Length:**  The length of the part that goes inside the rocket body tube.
*   **Recovery Hole Size:**  I need an oval-ish hole on the adapter section for the recovery system. I need to control both X and Y dimensions of that hole.
*   **Recovery Rod Girth:** How thick the part of the recovery system that goes through the hole will be.

I also need to make sure that the recovery rod hole has a bit of extra material around it, so there is a solid connection between the main adapter and the recovery rod.

I'd like to make sure the code uses the following libraries:
* `MCAD`

Could you generate the OpenSCAD code for me? I don't need any special features, just a clean and easy to use script to start generating some nose cones. Thanks!

[Assistant]
model_rocket_nose_cone.scad:
```
// Model rocket nose cone by Lutz Paelke (lpaelke)
// CC BY-NC-SA 3.0
// Model modified by John Hawley

// change fill% of print to alter weight

// Inner diameter of the rocket body
Diameter_In=25.65;

// Outer diameter of the rocket body
Diameter_Out=28.72;

radius_in=Diameter_In/2;	// inside radius of rocket tube, should be >= 7
radius_out=Diameter_Out/2;	// ouside radius of rocket tube / nose cone, should be > radius_in

// Length of nose cone
Length=150;

// How many sides to render for the cone
Cone_Render_Resolution=60;

// Enter "parabolic", "power_series", "haack" or "<blank>" to default to Lutz Paelke (lpaelke) cone design
Cone_Shape="";

// Length of the adapter section (insertion to rocket body)
Adaptor_Length=20;
variable_adapter_Length=Adaptor_Length-2-2-2; // 9.25

// Recovery hole size (Y Axis)
Recovery_Hole_Size_Y=6;
// Recovery hole size (X Axis)
Recovery_Hole_Size_X=10; // (radius_in*2) - (radius_in*2)/3;
// Width/Strength of the recovery rod
Recovery_Rod_Girth=3;

union(){
    // Fastner section
    difference(){
        difference() {
        //     cylinder(h=15, r1=radius_in-taper, r2=radius_in,$fn=60);
            union(){
                translate ([0,0,variable_adapter_Length+4]) linear_extrude(2){
                    circle(r=radius_in-1);
                }
                translate ([0,0,variable_adapter_Length+2]) linear_extrude(2){
                    circle(r=radius_in);
                }
                translate ([0,0,2]) linear_extrude(variable_adapter_Length){
                    circle(r=radius_in-1);
                }
                linear_extrude(2){
                    circle(r=radius_in);
                }
            }
            translate ([-Recovery_Hole_Size_X/2,-Recovery_Hole_Size_Y/2,-1]) cube(size = [Recovery_Hole_Size_X,Recovery_Hole_Size_Y,2]);
            translate ([0,Recovery_Hole_Size_Y/2,1]) rotate ([90,0,0]) cylinder(h=Recovery_Hole_Size_Y,r=Recovery_Hole_Size_X/2,$fn=60);
        }

  }

    difference() {
        translate([0,Recovery_Hole_Size_Y/2,2]) rotate([90,0,0]) cylinder(h=Recovery_Hole_Size_Y,r=Recovery_Rod_Girth/2,$fn=60);
        translate ([0,0,-29]) cylinder(h=30,r=radius_in,$fn=60); // Saftey cynlinder to cut off bottom
    }
    translate([-Recovery_Rod_Girth/2,Recovery_Hole_Size_Y/2,0]) rotate([90,0,0]) cube(size = [Recovery_Rod_Girth,2,Recovery_Hole_Size_Y]);

    // Cone section
    if (Cone_Shape == "parabolic") {
        translate([0,0,Adaptor_Length]) cone_parabolic(R = radius_out, L = Length, K = 1, s = Cone_Render_Resolution);
    }
    if (Cone_Shape == "power_series") {
        translate([0,0,Adaptor_Length]) cone_power_series(n = 0.5, R = radius_out, L = Length, s = Cone_Render_Resolution);
    }

    if (Cone_Shape == "haack") {
        translate([0,0,Adaptor_Length]) cone_haack(C = 0.3333, R = radius_out, L = Length, s = Cone_Render_Resolution);
    }
    if (Cone_Shape != "parabolic" && Cone_Shape != "power_series" && Cone_Shape != "haack") {
        translate ([0,0,Adaptor_Length]) scale([1,1,Length/radius_out]) difference() {
            sphere(r = radius_out, $fn = Cone_Render_Resolution); // Adjust the sphere radius
            translate ([0,0,-radius_out/2]) cube(size=[2*radius_out,2*radius_out,radius_out],center=true);
        }
    }
}

// Thank you: ggoss (https://www.thingiverse.com/thing:2004511)
// For translating the math into OpenSCAD!
// Nose Cone Design: https://en.wikipedia.org/wiki/Nose_cone_design

module cone_haack(C = 0, R = 5, L = 10, s = 500){

// SEARS-HAACK BODY NOSE CONE:
//
// Parameters:
// C = 1/3: LV-Haack (minimizes supersonic drag for a given L & V)
// C = 0: LD-Haack (minimizes supersonic drag for a given L & D), also referred to as Von Kármán
//
// Formulae (radians):
// theta = acos(1 - (2 * x / L));
// y = (R / sqrt(PI)) * sqrt(theta - (sin(2 * theta) / 2) + C * pow(sin(theta),3));

echo(str("SEARS-HAACK BODY NOSE CONE"));
echo(str("C = ", C));
echo(str("R = ", R));
echo(str("L = ", L));
echo(str("s = ", s));

TORAD = PI/180;
TODEG = 180/PI;

inc = 1/s;

rotate_extrude(convexity = 10, $fn = s)
for (i = [1 : s]){
    x_last = L * (i - 1) * inc;
    x = L * i * inc;

    theta_last = TORAD * acos((1 - (2 * x_last/L)));
    y_last = (R/sqrt(PI)) * sqrt(theta_last - (sin(TODEG * (2*theta_last))/2) + C * pow(sin(TODEG * theta_last), 3));

    theta = TORAD * acos(1 - (2 * x/L));
    y = (R/sqrt(PI)) * sqrt(theta - (sin(TODEG * (2 * theta)) / 2) + C * pow(sin(TODEG * theta), 3));

    rotate([0, 0, -90]) polygon(points = [[x_last - L, 0], [x - L, 0], [x - L, y], [x_last - L, y_last]], convexity = 10);
}
}

module cone_parabolic(R = 5, L = 10, K = 0.5, s = 500){
// PARABOLIC NOSE CONE
//
// Formula: y = R * ((2 * (x / L)) - (K * pow((x / L),2)) / (2 - K);
//
// Parameters:
// K = 0 for cone
// K = 0.5 for 1/2 parabola
// K = 0.75 for 3/4 parabola
// K = 1 for full parabola

echo(str("PARABOLIC NOSE CONE"));
echo(str("R = ", R));
echo(str("L = ", L));
echo(str("K = ", K));
echo(str("s = ", s));

if (K >= 0 && K <= 1){

    inc = 1/s;

    rotate_extrude(convexity = 10, $fn = s)
    for (i = [1 : s]){

        x_last = L * (i - 1) * inc;
        x = L * i * inc;

        y_last = R * ((2 * ((x_last)/L)) - (K * pow(((x_last)/L), 2))) / (2 - K);
        y = R * ((2 * (x/L)) - (K * pow((x/L), 2))) / (2 - K);

        polygon(points = [[y_last, 0], [y, 0], [y, L - x], [y_last, L - x_last]], convexity = 10);
    }
} else echo(str("ERROR: K = ", K, ", but K must fall between 0 and 1 (inclusive)."));
}

module cone_power_series(n = 0.5, R = 5, L = 10, s = 500){
// POWER SERIES NOSE CONE:
//
// Formula: y = R * pow((x / L), n) for 0 <= n <= 1
//
// Parameters:
// n = 1 for a cone
// n = 0.75 for a 3/4 power
// n = 0.5 for a 1/2 power (parabola)
// n = 0 for a cylinder

echo(str("POWER SERIES NOSE CONE"));
echo(str("n = ", n));
echo(str("R = ", R));
echo(str("L = ", L));
echo(str("s = ", s));

inc = 1/s;

rotate_extrude(convexity = 10, $fn = s)
for (i = [1 : s]){

    x_last = L * (i - 1) * inc;
    x = L * i * inc;

    y_last = R * pow((x_last/L), n);
    y = R * pow((x/L), n);

    rotate([0, 0, 90]) polygon(points = [[0,y_last],[0,y],[L-x,y],[L-x_last,y_last]], convexity = 10);
}
}

```


[User]
I need a parametric OpenSCAD design for a small disc-like object, something that would be useful as an anti-warp tab when 3D printing. It should have adjustable radius and height. I'd like the code to be well-commented and use modules to keep things organized.

I'd also like to be able to create an array of these discs arranged in a square or rectangular grid, with adjustable spacing between them. This should be a separate module or function so I can choose to make just one disc or several. The number of rows and columns in the grid should be adjustable, and the spacing between them should be too.

Finally, I'd also like to add an option to put a small hole through the center of each disc. The hole should also have a user-adjustable diameter.

So, to summarize, I need:

*   A basic disc module with adjustable radius and height.
*   A grid array module that uses the disc module, with adjustable spacing, number of rows, and columns.
*   An option to add a hole to the center of each disc, with an adjustable diameter.

All parameters should be user-adjustable via the customizer, and the code should be commented and use modules. Could you please also include a small example of how to use all of this?

I'd prefer the code to be fairly simple, using basic OpenSCAD features rather than advanced libraries, if possible.

[Assistant]
disk-customizer.scad:
```
$fn=30;

//step sliders for numbers (see https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Customizer#parameters)
diskR=7.5; //[0:0.5:100]
diskH=1.5; //[0:0.5:100]

cylinder(h = diskH, r = diskR);
```


[User]
Hey, I'm looking to create a customizable dice tray in OpenSCAD. I have a pretty clear idea of what I want, and I'd like it to be parametric so I can adjust the size and some other features.

Essentially, I'm envisioning a tray with a main rolling area and then a series of stepped "shelves" to hold dice.

Here’s what I'm thinking in terms of parameters:

*   **Rolling Area:**
    *   `Roll_Area_Depth`:  How deep the rolling area is from front to back.
    *   `Width`: The total width of the rolling area and shelves from left to right.
*   **Dice Shelves:**
    *   `Dice_Shelf_Depth`: How deep each of the dice shelves are, front to back.
    *   `Dice_Shelf_Count`: How many dice shelves there are.
*   **Walls:**
    *   `Wall_Height_Max`: The height of the wall around the *back* of the rolling area. This should be the highest wall.
    *  `Wall_Height_Min`: The height of the wall around the front of the tray and the dice shelves. This should be the lowest wall.
    *   `Wall_Interior_Slope`: The angle of the wall's interior surface, in degrees. 90 degrees would be vertical. This slope should apply to the walls around the rolling area *and* the walls surrounding the shelves.
*   **Other Thicknesses:**
    *   `Floor_Thickness`: The thickness of the base of the tray.
    *   `Divider_Thickness`: The thickness of the dividers between the rolling area and each dice shelf.

I’d also like the following details:

*   The tray should have smooth, rounded corners. 64 `$fn` should do the trick.
*   The walls should have a minimum thickness (`wallThicknessMin`) which I think 2mm sounds good.
*   There should be a small bezel on the exterior top edge of the walls (`wallBezel`) - maybe around 1mm.
*   The wall base width is calculated as `wallThicknessMin + (Wall_Height_Max * tan(90-Wall_Interior_Slope))`.
*   The total depth of the shelves will be `(Dice_Shelf_Depth * Dice_Shelf_Count) + (Divider_Thickness * Dice_Shelf_Count)`.
*   The overall interior depth should be `Roll_Area_Depth + totalDiceShelfDepth`.
*   The divider width will be `Width + (wallBaseWidth*2) - (wallBezel*2)`.

For the overall design, the tray should have walls that slope down from the back of the rolling area to the front and along the shelf sections. I think the simplest way to get this effect is to do a linear extrude for the front and back walls, and then use a `rotate_extrude` for the corners and a `hull` of two linear extrudes with the 2d_trayProfile for the sloping sides, where the `height` is the only difference between them. And then use another `linear_extrude` of that profile for the straight sides. The floor should be a `linear_extrude` of a `square` centered on the x and y. Dividers should be a `linear_extrude` of a `square` centered on the x.

The 2d profile should be created using a `polygon` with the points calculated using the parameters. It should handle different heights.

I don’t need to have any external library dependencies to do this.

I think that covers everything. Let me know if you have any questions!

[Assistant]
Small_Dice_Tray.scad:
```
////////////////////////////////////////////////
// User-defined Parameters
////////////////////////////////////////////////

/* [Rolling and Shelf area] */
//How deep, back to front, the rolling area is
Roll_Area_Depth = 100;
//How deep, back to front, the a dice shelf is
Dice_Shelf_Depth = 18;
//How wide, left to right, the tray's rolling and shelf areas are
Width = 130;
//How many dice shelfs there are
Dice_Shelf_Count = 3; //[0:10]

/* [Wall shape specifics] */
//Height, bottom to top, of the wall around the rolling area
Wall_Height_Max = 30;
//Height, bottom to top, of the wall around the dice shelves or front of tray
Wall_Height_Min = 4;
//Angle of the wall's interior surface in degrees. 90 is vertical
Wall_Interior_Slope = 66; //[45:90]

/* [Other surface thickness] */
//Thickness of the base of the tray. Should be less than or equal to Wall Height Min
Floor_Thickness = 2;
//Thickness of the dividers between roll area and dice shelfs
Divider_Thickness = 2;

//No more params in customizer
module endParams(){} endParams();

////////////////////////////////////////////////
// Hard-coded Parameters
////////////////////////////////////////////////
//Detail on corners and slopes
$fn=64;

//Thinnest part of wall
wallThicknessMin = 2;
//How much bezel to apply to wall
wallBezel = 1;
//How wide the base is at its widest point
//Calculated using the angle selected and how far out it comes from at the base
wallBaseWidth = wallThicknessMin + (Wall_Height_Max * tan(90-Wall_Interior_Slope));

//Depth of the dice shelfs and separator walls
totalDiceShelfDepth = (Dice_Shelf_Depth*Dice_Shelf_Count) + (Divider_Thickness*Dice_Shelf_Count);
//Overall depth (y) of the board's interior
totalInteriorDepth = Roll_Area_Depth + totalDiceShelfDepth;

//How wide the divider is
dividerWidth = Width + (wallBaseWidth*2) - (wallBezel*2);

////////////////////////////////////////////////
// Logic - what gets made
////////////////////////////////////////////////

union(){
    part_wall_front();
    part_wall_back();
    part_wall_corners();
    part_wall_sides();
    part_floor();
    part_dividers();
}

////////////////////////////////////////////////
// Parts - Models or parts of models
////////////////////////////////////////////////

// Wall part (front)
module part_wall_front(){
    translate([0,-totalInteriorDepth/2-wallBaseWidth,0]) //position y
    translate([-Width/2,0,0]) //center x
    rotate([90,0,90])
    linear_extrude(Width)
    2d_trayProfile(Wall_Height_Min);
}
// Wall part (back)
module part_wall_back(){
    translate([0,totalInteriorDepth/2+wallBaseWidth,0]) //position y
    translate([Width/2,0,0]) //center x
    rotate([90,0,-90])
    linear_extrude(Width)
    2d_trayProfile(Wall_Height_Max);
}
// Wall part (corners)
module part_wall_corners(){
    //Front corners
    3d_cornerPiece(Wall_Height_Min);
    mirror([1,0,0])
    3d_cornerPiece(Wall_Height_Min);
    //Back corners
    rotate([0,0,180]){
        3d_cornerPiece(Wall_Height_Max);
        mirror([1,0,0])
        3d_cornerPiece(Wall_Height_Max);
    }
}
// Wall part (left and right sides)
module part_wall_sides(){
    //left sloped
    translate([-Width/2 - wallBaseWidth,0,0])
    translate([0,totalInteriorDepth/2,0]) //center y
    rotate([90,0,0])
    3d_slopingWallSide();
    //right sloped
    mirror([1,0,0])
    translate([-Width/2 - wallBaseWidth,0,0])
    translate([0,totalInteriorDepth/2,0]) //center y
    rotate([90,0,0])
    3d_slopingWallSide();

    //left straight
    translate([-Width/2 - wallBaseWidth,0,0]) //set x
    translate([0,totalDiceShelfDepth - totalInteriorDepth/2,0]) //set y
    rotate([90,0,0])
    linear_extrude(totalDiceShelfDepth)
    2d_trayProfile(Wall_Height_Min);
    //right straight
    mirror([1,0,0])
    translate([-Width/2 - wallBaseWidth,0,0]) //set x
    translate([0,totalDiceShelfDepth - totalInteriorDepth/2,0]) //set y
    rotate([90,0,0])
    linear_extrude(totalDiceShelfDepth)
    2d_trayProfile(Wall_Height_Min);
}

// Floor
module part_floor(){
    linear_extrude(Floor_Thickness)
    square([Width, totalInteriorDepth], center=true);
}

// Dividers between rolling area and dice shelves
module part_dividers(){
    for (i=[0:Dice_Shelf_Count-1]){
        translate([0,(Dice_Shelf_Depth + Divider_Thickness) * i,0]) //set y for subsiquent dividers
        translate([0,-totalInteriorDepth/2 + Dice_Shelf_Depth,0]) //set y to the location of the first divider
        translate([-dividerWidth/2,0,0]) //center x
        linear_extrude(Wall_Height_Min + Floor_Thickness)
        square([dividerWidth,Divider_Thickness]);
    }
}

////////////////////////////////////////////////
// Helpers - functions or modules to make things easier
////////////////////////////////////////////////

// Create a shape for the walls with dynamic height
// Base width is always the same. It is based off the max height and slope
// Points of the shape start at 0,0 then go clockwise
module 2d_trayProfile(height){
    //Calculate dynamic width at top
    widthOffsetAtTop = height * tan(90-Wall_Interior_Slope);
    widthAtTop = wallBaseWidth - widthOffsetAtTop;
    //Create shape
    polygon([
        [0            , 0                                   ],
        [0            , height + Floor_Thickness - wallBezel],
        [wallBezel    , height + Floor_Thickness            ],
        [widthAtTop   , height + Floor_Thickness            ],
        [wallBaseWidth, Floor_Thickness                     ],
        [wallBaseWidth, 0                                   ]
    ]);
}

// Create a wall corner piece in the front-left position
module 3d_cornerPiece(height){
    translate([-Width/2,-totalInteriorDepth/2,0]) //position to front-left corner
    rotate_extrude(angle=90)
    translate([-wallBaseWidth-0.0001,0,0]) //position for rotate extrude
    2d_trayProfile(height);

    //note: The 0.0001 is there because a polygon aligned on 1 side to the x axis when rotating caused the shape to not compile correctly, leaving empty corners. By removing it a tiny amount from the line, I avoid the bug but the corners still come out identical for our purposes.abs

    //This appearsto be fixed in a beta build as on 6/24/24, but I wanted anyone to be able to use it.
}

// Create an unpositioned side wall piece that transitions height from back to front
module 3d_slopingWallSide(){
    //Amount height will change between tallest point and lowest
    totalHeightDelta = Wall_Height_Max - Wall_Height_Min;
    // How many sections to break the transition into
    steps = $fn;
    // Amount of sin wave per step
    stepPerLoop = 180/steps;
    // Length of each section
    wallLengthPerStep = Roll_Area_Depth/steps;
    // Loop i times (steps) and create each section of the transition
    // We loop -stepPerLoop times because each section connects to the next. So the second to last section reaches the end
    for (currentAngle = [0:stepPerLoop:180-stepPerLoop]){
        //index # of loop
        i = currentAngle / stepPerLoop;
        //This step's height
        heightDelta = ((totalHeightDelta*sin(currentAngle+90)) + totalHeightDelta) / 2 + Wall_Height_Min;
        //Next step's height
        nextHeightDelta = ((totalHeightDelta*sin((currentAngle+stepPerLoop)+90)) + totalHeightDelta) / 2 + Wall_Height_Min;
        //Create a section that transitions this step's height to the next step's height
        translate([0,0,wallLengthPerStep * i])
        hull(){
            //bigger, this loop's section
            linear_extrude(0.0001)
            2d_trayProfile(heightDelta);
            //smaller, next loop's section
            translate([0,0,wallLengthPerStep])
            linear_extrude(0.0001)
            2d_trayProfile(nextHeightDelta);
        }
    }
}

```


[User]
Hi, I'd like to create a parametric OpenSCAD model to generate a printable spool of filament. I've been trying to make my own "quantum" filament, where the two halves of the filament have different colors, and I think a parametric model would be the best way to create and test that.

Basically, the model should generate a long, spiraling strand of filament within a given circular area. The main parameters I want are:

*   **`diameter`**: The diameter of the circle that contains the spiral. This represents the print area, and the spiral will fit entirely within this circle.
*   **`length`**: The total length of the resulting filament strand, in millimeters.
*   **`fd`**: The diameter of the filament itself. I'll be using either 1.75mm or 2.85mm.
*   **`space`**: The space between each round of the spiral in millimeters.
*   **`roundness`**: This will control how many segments are used to make the circle of the filament cross-section. I want to be able to specify this as `"draft"`, `"low"`, `"good"`, `"best"`, or `"overkill"`, and these should correspond to increasing number of segments.
*   **`spiral_smoothness`**: Similar to `roundness`, but this will control the number of segments when creating the spiral path. This should also have the same options as `roundness`.
*   **`parts`**: This will select which portion of the filament cross-section will be used. I want options for `"full"`, `"inside"`, `"outside"`, `"top"`, `"bottom"`, `"inside top"`, `"inside bottom"`, `"outside top"`, and `"outside bottom"`. This allows me to make different types of filaments, like a single stripe on the side.

The model should generate a single object representing the spiraled filament. I would like the filament to be constructed using a `rotate_extrude` operation of a circle representing the filament's diameter, in which the radius of rotation changes according to the position of the filament. I expect to use `intersection()` to limit the total area to within the given `diameter` constraint.

I've provided the definitions I use for the different quality keywords and a way to convert them to numbers. I also included a transformation of the cross-section depending on the value of the `parts` variable. This logic should probably be converted into functions in the SCAD file.

When using Customizer and requesting too much length, I've seen a small circle appear in the center to indicate that the filament wouldn't fit. I'd like this same kind of indicator.

I am not sure what, if any, libraries are needed for this; please let me know if there are any OpenSCAD libraries that could help with this project.

[Assistant]
filament.scad:
```
// Diameter of the outside loop (smaller dimension of print area)
diameter = 200;

// Total length of resulting filament strand, in mm
length = 5000;

// Diameter of the generated filament
fd = 1.75; // [1.75,2.85]

// Space between rounds of the filament strand, in mm
space=1.00; // [0.1:0.1:4.0]

// The roundness of the cross-section of the filament. Choose "good" unless you have issues when slicing.
roundness = "good"; // [draft, low, good, best, overkill]

// The smoothness of the spiral; choose "good" unless you experience issues when printing with the resulting filament.
spiral_smoothness = "good"; // [draft, low, good, best, overkill]

// Which part of the filament to generate:
parts = "full"; // [full, inside, outside, top, bottom, inside top, inside bottom, outside top, outside bottom]

// now convert those friendly numbers in useful ones, and define functions

max_width = diameter-fd/2;

// map quality keywords to numbers
function values(kw) =
  kw == "draft"    ? 0 :
  kw == "low"      ? 1 :
  kw == "good"     ? 2 :
  kw == "best"     ? 3 :
  kw == "overkill" ? 4 :
  undef;

// map parts keywords to transformations on the intersectiong square
function part_trans(kw) =
  kw == "inside"         ? -fd*[3.0, 1.5, 0.0] :
  kw == "outside"        ? -fd*[0.0, 1.5, 0.0] :
  kw == "top"            ? -fd*[1.5, 0.0, 0.0] :
  kw == "bottom"         ? -fd*[1.5, 3.0, 0.0] :
  kw == "inside top"     ? -fd*[3.0, 0.0, 0.0] :
  kw == "inside bottom"  ? -fd*[3.0, 3.0, 0.0] :
  kw == "outside top"    ? -fd*[0.0, 0.0, 0.0] :
  kw == "outside bottom" ? -fd*[0.0, 3.0, 0.0] :
  undef;

// filament cross-section quality
cir_q = 6*(values(roundness)+1);

// filament spiral smoothness quality
spi_q = 16*pow(2,values(spiral_smoothness));

// per-quarter radius shrinkage
pqs = (space+fd)/4;

// radius of first quarter
r = max_width/2;

// length of the first quarter
fql = PI*r/2;

// per-quarter length shrinkage in mm
pqls = PI*pqs/2;

// maximum quarters (minimum 10mm radius)
max_qs = floor((max_width/2-10)/pqs);

// how many quarters to make up the required length
function length_to_quarters(length, cl=0, i=0) =
  i >= max_qs ?
    [max_qs, cl] :
  cl+fql-(i*pqls)>=length ?
    [i + (length-cl)/(fql-(i*pqls)), length]
  : length_to_quarters(length, cl+fql-(i*pqls), i+1);

// how many quarters to generate
qs_and_l = length_to_quarters(length);
if (qs_and_l[1] < length) {
  echo(actual_length=qs_and_l[1]);
  cylinder(h=0.01,r=8);
 }
qs = qs_and_l[0];

// last quarter may be partial, how many degrees are remaining
rdeg = qs*90%90;

translate([0,0,fd/2]) for(q=[0:ceil(qs-1)]){
  rotate([0,0,q%4*90]){ // rotate the quarter turn into place
    translate([pqs/2,-pqs/2,0]){ // move the quarter so it fits once rotated
      intersection(){
        if(version_num() <= 20150399){ // rotate_extrude(angle) unsupported
          translate([0,0,-fd]) if(qs-q>=1){
            cube(diameter/2+fd);
          } else intersection(){
            cube(diameter/2+fd);
            rotate([0,0,rdeg-90]) cube(diameter/2+fd);
          }
        }
        rotate_extrude(angle=(qs-q<1?rdeg:90),$fn=spi_q){ // if on last segment, shorten as needed
          translate([max_width/2-q*pqs,0,0]){ // diameter of extrusion
            if(!part_trans(parts)){
              circle(d=fd,$fn=cir_q);
            } else{
              intersection(){ translate(part_trans(parts)) square(fd*3); circle(d=fd,$fn=cir_q); }
            }
          }
        }
      }
    }
  }
}
```


[User]
Hi! I'm looking to design a customizable measuring spoon, specifically for coffee, and I'd like to use OpenSCAD for that. I've seen some examples online, but I'd like to create my own from scratch.

Here's what I'm thinking:

**Overall Shape:**

*   The spoon should have an oval-shaped bowl and a handle.
*   The handle should transition smoothly from the bowl and have a flat, slightly tapered shape for a comfortable grip.
*   I want the spoon to be print-in-place, so it should be one solid piece when printed.

**Spoon Bowl (Oval Part):**

*   I need to be able to set the *internal* diameter and depth of the spoon bowl, allowing the actual outer dimensions to be slightly larger based on wall thickness.
*   I'd like to specify the wall thickness as well, so I can make it more or less durable.

**Handle:**

*   The handle should be flat, relatively thin, and connect smoothly to the oval bowl.
*   It should have a specified width and length, and a slight taper toward the end (so the handle is slightly narrower at its tip).
*   I'd like to define the width of the handle at the base where it joins the spoon, and have the tip width be calculated as a fraction of that base width (e.g. a third less).
*   I would like the handle to have a cutout or some sort of indented profile along the length to make it easier to hold and also to add some visual appeal. I'd like that cutout to be parallel to the outside edges of the handle and to follow the taper so that it looks consistent.
*   I need the walls of the spoon to continue into the handle.
*   I need to be able to add text to the handle, and control the font size, spacing, and the text itself. I'd like the text to be on a raised surface on the handle.

**Customization:**

*   All of the key dimensions (spoon internal diameter, depth, wall thickness, handle width, handle length, font size, font spacing, and text) should be parameters that I can easily change.

**Technical Details:**

*   I’d like to do the oval bowl by doing a rotated extrusion of an oval shape. I'd like to create that oval shape by subtracting two circles and then doing some boolean subtractions of rectangles.
*   I need a way to create the indented polygon on the handle from a set of points.

Can you generate an OpenSCAD file or files to create this design? I don't need a library, I just want the core OpenSCAD code for the spoon and handle. I don't expect you to use external libraries such as MCAD. I want to be able to adjust these dimensions to create spoons of varying sizes, and I'd like the text to be a customizable feature.

[Assistant]
CoffeeSpoon.scad:
```
$fa = 1;
$fs = 0.4;

/* [SPOON] */
// Thikness of walls (mm)
walls = 2;
// Internal diameter of the spoon (mm)
spoonDiameter = 30;
// Internal Depth of the spoon (mm)
spoonDepth = 46;

/* [HANDLE] */
// Width of the Handle (mm). The closest size to the spoon
handleWidth = 12;
// Length of the handle (mm)
handleLength = 50;

/* [TEXT CUSTOMIZATION] */
// Text to include in the handle. If text dosn't fit increse handle length or reduce Font Size & Font Spacing
textOnHandle = "Coffee";
// Font Size
fontSize = 11;
// Font Spacing
fontSpacing = 1;

module spoon(){
    rotate_extrude(angle = 360){
        difference(){
            //OVALO
            difference(){
                resize([outerDiameter,outerDepth]) circle(d=20);
                resize([spoonDiameter,spoonDepth]) circle(d=20);
            }
            translate([0, -outerDepth/2, 0])
                square([outerDiameter, outerDepth], center=true);

            translate([outerDiameter/2, 0, 0])
                square([outerDiameter, outerDepth], center=true);
        }
    }
}

module handle(){
    polygonPoints = [
        // PERIMETRO
        [0, -handleWidth/2],
        [walls, 0],
        [0, handleWidth/2],
        [handleLength, (handleWidth/1.33)/2],
        [handleLength, -(handleWidth/1.33)/2],

        // HUECO
        [walls, (-handleWidth/2)+walls],
        [walls*1.5, 0],
        [walls, (handleWidth/2)-walls],
        [handleLength-walls, ((handleWidth/1.33)/2)-walls],
        [handleLength-walls, (-(handleWidth/1.33)/2)+walls]
    ];
    translate([spoonDiameter/2, 0, 0])
        linear_extrude(walls){
                polygon(polygonPoints, [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]);
                difference(){
                    polygon([
                        [0, -handleWidth/2],
                        [walls, 0],
                        [0, handleWidth/2],
                        [-outerDiameter/2, outerDiameter/3],
                        [-outerDiameter/2, -outerDiameter/3]
                    ]);
                    translate([-outerDiameter/2, 0, 0])
                        circle(d=outerDiameter);
                }
        }
    translate([spoonDiameter/2, 0, 0])
        linear_extrude(walls/2){
            polygon([
                [walls, (-handleWidth/2)+walls],
                [walls*1.5, 0],
                [walls, (handleWidth/2)-walls],
                [handleLength-walls, ((handleWidth/1.33)/2)-walls],
                [handleLength-walls, (-(handleWidth/1.33)/2)+walls]
            ]);
        }

    // TEXTO
    translate([(spoonDiameter/2)+(handleLength/2), 0, 0])
        linear_extrude(walls+0.8){
            text(textOnHandle, size=fontSize, font="Brush Script MT:style=Italic", halign="center", valign="center", spacing=fontSpacing);
        }
}

outerDiameter = spoonDiameter + (walls*2);
outerDepth = spoonDepth + walls;
spoon();
handle();
```


[User]
I'm looking for a parametric OpenSCAD design for a cable comb. I want to be able to customize the number of slots, the dimensions of the slots themselves, the spacing between the slots, and how it mounts to a surface.

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

*   **Main Body:**
    *   `NumberOfSlots`: The number of cable slots in the comb. I'd like to be able to specify between 1 and 8 slots, in whole number increments.
    *   `MountWallHeight`: The height of the wall that will be used to mount the comb. There should be a lower limit imposed by the rounding (see below).
    *   `CableWallHeight`: The height of the wall for each cable slot. There should be a lower limit imposed by the rounding (see below).
    *   `Thickness`:  The thickness of the material used to make the comb (i.e., wall thickness).
    *   `InnerDepth`: The distance between the mount wall and the cable slot wall.
*   **Mount Holes:**
    *   `NumMountHoles`:  The number of mounting holes. I want to be able to specify none, one, or two holes. If one hole is chosen, it should be centered. If two holes are chosen, the code should try to maximize the distance between the holes, while also ensuring they aren't too close to the edges of the comb, and that they don't interfere with the slots.
    *   `MountHoleType`: The type of mounting hole. I want to be able to choose a simple through-hole, a counterbore, a countersink, or both a counterbore and a countersink.
    *   `MountThroughHoleDiameter`: The diameter of the main through-hole for the mounting screw.
    *   `MountCounterHoleDiameter`: The diameter of the counterbore or countersink.
    *   `MountCounterBoreDepth`: The depth of the counterbore.
    *   `MountHoleTolerance`: Tolerance for the mounting holes; this will allow the code to generate slots instead of holes for mounting. This tolerance will be multiplied by the through hole diameter to make the slot width.
*   **Slot Parameters:**
    *   `SlotDistance`: The distance between each cable slot tooth. The spacing must be large enough to allow for the slot's rounded corners.
    *   `CableSlotSizes_1_to_4`: The width of the cable inlets for slots 1 to 4.
    *   `CableSlotDiameters_1_to_4`: The diameter of the plug rest area for slots 1 to 4.
    *   `CableSlotSizes_5_to_8`: The width of the cable inlets for slots 5 to 8.
    *   `CableSlotDiameters_5_to_8`: The diameter of the plug rest area for slots 5 to 8.
*   **Precision:**
    *   I'd like to set `$fn` for increased resolution for curves (at least 120).

The code should include safety checks:
*   The width of a slot should never exceed the inner depth minus some distance for rounding.
*   The diameter of a slot's termination should never be greater than the allowed width or the raw diameter.

The comb should have rounded edges/corners for the teeth and the mounting wall. I'd like to use a variable to adjust the rounding, something like `RoundCornerRadius`.

Also, I'd like the design to use the BOSL2 library if possible, as I'm familiar with it. Specifically, I'd need `rounding_edge_mask()` from that library.

I think that's everything. Let me know if you have any questions.

[Assistant]
CableComb.scad:
```
// This work is licensed under CC BY-SA 4.0. To view a copy of this license,
// visit https://creativecommons.org/licenses/by-sa/4.0/

include <BOSL2/std.scad>

/* [Main Body] */

NumberOfSlots = 4; // [1:1:8]

// The minimum is limited by the rounding
MountWallHeight = 14; // [0:50]

// The minimum is limited by the rounding
CableWallHeight = 5; // [0:50]

// wall thickness
Thickness = 3; // [1:.5:100]

// distance between mount wall and cable wall
InnerDepth = 14; // [10:50]

/* [Mount Holes] */

// holes should be opposite of a slot
NumMountHoles = 2; // [0:none,1:one,2:two]

// mounting drill hole diameter

MountHoleType = 1; //[0:through,1:counterbore,2:countersink,3:both]

// through hole diameter
MountThroughHoleDiameter = 3.5; // [0:.5:10]
// counterhole/countersink diameter
MountCounterHoleDiameter = 7; // [0:.5:10]
// counterhole/countersink depth
MountCounterBoreDepth = 1.5; // [0:.5:10]
// slot hole tolerance (multiple of diameter)
MountHoleTolerance = 0.5; // [0:0.1:3]

/* [Slot Parameters] */

// Tooth width between cable slot
SlotDistance = 5; // [4:50]

// cable inlet widths 1..4
CableSlotSizes_1_to_4 = [ 5, 5, 5, 5 ]; // [.5:.5:100]
// plug rest area diameters 1..4
CableSlotDiameters_1_to_4 = [ 6, 6, 6, 6 ]; // [.5:.5:100]
// cable inlets widths 5..8
CableSlotSizes_5_to_8 = [ 5, 5, 5, 5 ]; // [.5:.5:100]
// plug rest area diameters 5..8
CableSlotDiameters_5_to_8 = [ 6, 6, 6, 6 ]; // [.5:.5:100]

/* [Precision] */
$fn = 120;

/* [Hidden] */
Overlap = .01; // to avoid z-fighting
MountHoleMinEdgeDistance = 2;
RoundCornerRadius = 2;
// the width of the slot cannot be wider then the inner distance
// otherwise the rounding of the slot would weaken the structure
MaxSlotWidth = InnerDepth - 2 * RoundCornerRadius;

function GetRawSlotWidth(i) = (i < 4) ? CableSlotSizes_1_to_4[i] : CableSlotSizes_5_to_8[i - 4];
function GetLimitedSlotWidth(i) = min(GetRawSlotWidth(i), MaxSlotWidth);
function GetRawSlotDiameter(i) = (i < 4) ? CableSlotDiameters_1_to_4[i] : CableSlotDiameters_5_to_8[i - 4];
function GetSlotDistance() = max(SlotDistance, RoundCornerRadius * 2);
function SumUnitLen(nNumUnits) = (nNumUnits <= 0)
                                     ? 0
                                     : (SumUnitLen(nNumUnits - 1) + GetLimitedSlotWidth(nNumUnits - 1) + SlotDistance);
OverallWidth = SlotDistance + SumUnitLen(NumberOfSlots);

function GetMountWallHeight() = max(MountWallHeight, Thickness + 2 * RoundCornerRadius);
echo(OverallWidth = OverallWidth);
module Tooth(_length, _height, _offset)
{
	height = max(_height, Thickness + 2 * RoundCornerRadius);
	translate([ _offset, 0, 0 ])
	difference()
	{
		hull()
		{
			cube([ _length, Thickness, 0.01 ]);
			translate([ RoundCornerRadius, 0, height - RoundCornerRadius ])
			rotate([ -90, 0, 0 ])
			{
				cylinder(r = RoundCornerRadius, h = Thickness);
				translate([ _length - 2 * RoundCornerRadius, 0, 0 ])
				cylinder(r = RoundCornerRadius, h = Thickness);
			}
		}
		translate([ 0, 0, -RoundCornerRadius ])
		cube(size = [ _length, Thickness, RoundCornerRadius ]);
	}
}

module MountWall()
{
	translate([ 0, Thickness + InnerDepth, 0 ])
	{
		Tooth(_length = OverallWidth, _height = GetMountWallHeight(), _offset = 0);
		translate([ 0, 0, Thickness ])
		rounding_edge_mask(l = OverallWidth, r = RoundCornerRadius, anchor = BOTTOM, spin = [ 0, -90, 180 ], );
	}
}

module GroundPlate()
{
	cube(size = [ OverallWidth, InnerDepth + 2 * Thickness, Thickness ]);
}

module Teeth()
{
	for (i = [0:NumberOfSlots])
	{
		len = GetSlotDistance();
		offset = SumUnitLen(i);
		translate([ offset, Thickness, Thickness ])
		rounding_edge_mask(l = len, r = RoundCornerRadius, anchor = TOP, spin = [ 0, -90, 0 ], );
		Tooth(_length = len, _height = CableWallHeight, _offset = offset);
	}
}

// This function does no sanity checks; it just creates the parts.
// The cable rest hole center is positioned to the middle between mount wall and cable wall.
module Slot(_width, _diameter, _offset = 0)
{
	depth = Thickness + InnerDepth / 2;
	// create slot
	translate([ _offset - _width / 2, -Overlap, -Overlap ])
	cube([ _width, depth + Overlap, Thickness + 2 * Overlap ]);
	// create slot drill hole at the end of the slot
	translate([ _offset, depth, -Overlap ])
	cylinder(d = _diameter, h = Thickness + 2 * Overlap);
}

module Slots()
{
	for (i = [0:NumberOfSlots - 1])
	{
		//slot width
		width = GetLimitedSlotWidth(i);
		if (width < GetRawSlotWidth(i))
		{
			echo("");
			echo("###########################################");
			echo(str("##### WARNING: CableSlotSize[", i, "] has been reduced to ", width, ". Increase 'InnerDepth'!"));
			echo("###########################################");
			echo("");
		}
		offset = SumUnitLen(i + 1) - width / 2;

		//limit the slot termination diameter
		diameter = min(MaxSlotWidth,max(width, min(width + 2 * GetSlotDistance() / 3, GetRawSlotDiameter(i))));
		if (diameter < GetRawSlotDiameter(i))
		{
			echo("");
			echo("###########################################");
			echo(str("##### WARNING: CableSlotDiameter[", i, "] has been reduced to ", diameter, ". Increase 'InnerDepth or SlotDistance'!"));
			echo("###########################################");
			echo("");
		}

		Slot(_width = width, _offset = offset, _diameter = diameter);
	}
}

module SlotHole(d = 1, h = 1, d2, dx = 0, dy = 0)
{
	hull()
	{
		translate([ -dx, -dy, 0 ])
		cylinder(d = d, d2 = d2, h = h);
		translate([ +dx, +dy, 0 ])
		cylinder(d = d, d2 = d2, h = h);
	}
}

module MountHole(_offset, _xSlotFactor = 0, _zSlotFactor = 0)
{
	dx = (_xSlotFactor * MountThroughHoleDiameter) / 2;
	dz = (_zSlotFactor * MountThroughHoleDiameter) / 2;
	translate([ _offset, InnerDepth + Thickness, (GetMountWallHeight() + Thickness + RoundCornerRadius) / 2 ])
	rotate([ -90, 0, 0 ])
	{
		translate([ 0, 0, -Overlap ])
		{
			// through hole
			SlotHole(d = MountThroughHoleDiameter, h = Thickness + 2 * Overlap, dx = dx, dy = dz);

			// counterbore
			if (MountHoleType > 0)
			{
				if (MountHoleType == 1 || MountHoleType == 3)
				{ // counterbore
					SlotHole(d = MountCounterHoleDiameter, h = MountCounterBoreDepth + Overlap, dx = dx, dy = dz);
				}
				if (MountHoleType == 2 || MountHoleType == 3)
				{ // countersink
					translate([ 0, 0, MountHoleType == 2 ? 0 : MountCounterBoreDepth ])
					SlotHole(d = MountCounterHoleDiameter, d2 = 0, h = MountCounterHoleDiameter / 2 + Overlap, dx = dx,
					         dy = dz);
				}
			}
			// break through the rounding
			translate([ 0, 0, -RoundCornerRadius - Overlap ])
			SlotHole(d = GetMountHolesDiameter(), h = RoundCornerRadius + 2 * Overlap, dx = dx, dy = dz);
		}
	}
}

function GetMountHolesDiameter() = (MountHoleType > 0) ? (max(MountThroughHoleDiameter, MountCounterHoleDiameter))
                                                       : (MountThroughHoleDiameter);
function IsSpaceForTwoMountHoles() = OverallWidth >= 3 * MountHoleMinEdgeDistance + 2 * GetMountHolesDiameter();

// recursive function to choose the best bore hole distance from the outer
// edges out of a given vector of possible distances
// optimization criterion: maximum space between the bore holes
function GetMaximumEdgeDistance(v, n) = ((OverallWidth - 2 * (v[n] + GetMountHolesDiameter())) > v[n])
                                            ? (v[n])
                                            : (n > 0 ? GetMaximumEdgeDistance(v, n - 1) : v[n]);

module MountHoles()
{
	style = NumMountHoles;
	if (style == 1 || !IsSpaceForTwoMountHoles())
	{
		MountHole(OverallWidth / 2);
	}
	else if (style == 2)
	{
		aryPossibleEdgeDistances = [ MountHoleMinEdgeDistance, GetSlotDistance() / 2, GetSlotDistance() ];
		dist = GetMaximumEdgeDistance(aryPossibleEdgeDistances, len(aryPossibleEdgeDistances) - 1);
		CenterEdgeDistance = dist + GetMountHolesDiameter() / 2;

		MountHole(_offset = CenterEdgeDistance, _xSlotFactor = MountHoleTolerance);
		MountHole(OverallWidth - CenterEdgeDistance, _zSlotFactor = MountHoleTolerance);
	}
}

difference()
{
	union()
	{
		MountWall();
		GroundPlate();
		Teeth();
	}
	union()
	{
		Slots();
		MountHoles();
	}
}

```