[User]
I'd like to create a parametric checklist that I can 3D print, with sliding indicators for each line item. I'd like to be able to customize a number of aspects of the checklist, so I'm hoping you can generate some OpenSCAD code for me.

Here's a breakdown of what I'm looking for:

**Core Structure:**
*   The checklist will be made of several parts: a base plate (both top and bottom), slide locks, name plates, and the sliders themselves.
*   The base plate will have a number of rows, each with a name plate and a slider.
*   The overall design should allow for the name plates to be swapped out (if using the locks), but glue can be an alternative.
*   The design should be parametric, allowing me to adjust the dimensions and other properties in OpenSCAD.

**Base Plate Customization:**
*   **Thickness:** The base plate thickness should be adjustable, based on a multiple of the layer height (e.g. something like `T_BASE = 0.2; // [0.1:0.05:3]`).
*   **Extra Space:** An extra space value can be used for the base plate (i.e. `T_EXTRA = 1; // [0:1:5]`).
*   **Line Count:** The number of checklist rows/lines should be customizable (e.g., `C_LINE = 1; // [1:1:50]`).
*   **Line Spacing:** The height between each checklist line should be adjustable (e.g., `H_SPACE = 10; // [5:0.5:50]`).
*   **Hole Diameters:** I need options for middle holes and corner holes with adjustable diameters (e.g. `D_MIDDLE = 3; // [0:0.1:10]` and `D_CORNER = 3; // [0:0.1:10]`). These holes would be for mounting.
*   **Corner Radius:** The corners of the base plate should be able to have a customizable radius (e.g. `R_CORNER_XY = 3; // [0.1:0.1:10]`).
*  **Lock System:** I'd like to have an option to include locks to hold the base plates together or just use glue. The width and height of the locks should be adjustable based on the base plate thickness and wall thickness (e.g. `W_LOCK = 6; // [0:"No Locks but Glue",3,5,6,7,8]` and `H_LOCK = 6; // [2:1:20]`).

**Name Plate Customization:**
*   **Text:** The name plate should have customizable text (e.g. `T_PRINT = "Do";`).
*   **Text Style:** The text should be able to be printed embossed, engraved, or outlined (e.g., `S_TEXT = 1; // [1:"Embossed",2:"Engraved",3:"Outlined"]`).
* **Font size**: Should be adjustable and should be used for line spacing calculation (e.g. `H_FONT = 10; // [5:0.5:30]`).

**Slider Customization:**
*   **Knob Width:** The width of the slider knob should be adjustable (e.g., `W_KNOB = 2; // [1:1:10]`).
* **Sliding Overhang**: An overhang for the slider should be customizable and based on the wall thickness (e.g. `S_OVERHANG = 3; // [1:0.5:10]`).

**General Settings:**
*   **Line Width:** The width of the visual line on the name plate should be adjustable (e.g., `W_LINE = 20; // [5:1:300]`).
*  **Spacing Width:** Spacing width for elements on the plate should be adjustable (e.g. `W_SPACE = 10; // [5:0.5:50]`).
*   **Nozzle Diameter:** This should be configurable as this value will determine the wall thickness (e.g., `D_NOZZLE = 0.4; // [0.1:0.1:1]`).
*   **XY Tolerance:** A printing tolerance for X/Y dimensions should be included (e.g., `T_XY = 0.15; // [0:0.05:1]`).

**Other Considerations:**
*   The code should use a module called `rounded2d` for corner rounding as well as `roundedStroke` for rounded rectangle strokes
*   I'd also like a `printText` module.

I think that covers it. I'm hoping to get a well-structured, parametric OpenSCAD file (or multiple files) that allows me to generate all the needed parts for this checklist. Thanks!

[Assistant]
checklist.scad:
```
// Copyright 2021 Daniel Starke
// Licensed under CC BY-NC-SA 4.0 (https://creativecommons.org/licenses/by-nc-sa/4.0/)

/* [Base Settings] */
// Generate this part of the checklist.
PART = 0; // [0:"Base Plate Bottom",1:"Base Plate Top",2:"Base Plate Lock",3:"Base Plate Locks",4:"Name Plate",5:"Slider"]
// Base thickness (multiple of layer height) [mm].
T_BASE = 0.2; // [0.1:0.05:3]
// Slide extra space (multiplies with T_BASE).
T_EXTRA = 1; // [0:1:5]
// Font height [mm].
H_FONT = 10; // [5:0.5:30]
// Line width [mm].
W_LINE = 20; // [5:1:300]
// Spacing width [mm].
W_SPACE = 10; // [5:0.5:50]
// Base plate lock width (multiplies with T_WALL [derived from D_NOZZLE]).
W_LOCK = 6; // [0:"No Locks but Glue",3,5,6,7,8]
// Base plate lock height (multiples of T_BASE)
H_LOCK = 6; // [2:1:20]
// Sliding overhang (multiplies with T_WALL [derived from D_NOZZLE]).
S_OVERHANG = 3; // [1:0.5:10]

/* [Base Plate Settings] */
// Line count.
C_LINE = 1; // [1:1:50]
// Spacing height [mm].
H_SPACE = 10; // [5:0.5:50]
// Middle hole diameters [mm].
D_MIDDLE = 3; // [0:0.1:10]
// Corner hole diameters [mm].
D_CORNER = 3; // [0:0.1:10]
// Corner radius on X/Y axis [mm].
R_CORNER_XY = 3; // [0.1:0.1:10]

/* [Name Plate Settings] */
// Printed text.
T_PRINT = "Do";
// Text style.
S_TEXT = 1; // [1:"Embossed",2:"Engraved",3:"Outlined"]

/* [Slide Settings] */
// Slide knob width [mm].
W_KNOB = 2; // [1:1:10]

/* [Printing Settings] */
// Nozzle diameter [mm].
D_NOZZLE = 0.4; // [0.1:0.1:1]
// XY printing tolerance.
T_XY = 0.15; // [0:0.05:1]

/* [Hidden] */
// Wall thickness. This should be 5% larger than the nozzle diameter.
T_WALL = D_NOZZLE * 1.05;
// Overhang thickness for slides.
T_OVERHANG = T_WALL * S_OVERHANG;
// Lock overhang [mm].
O_LOCK = 0.2;
// Epsilon for mathematic rounding compensation [mm].
EPS = 0.01;
// Number of fragments.
$fn = 100;

function y(i) = (i * H_SPACE) + ((i - 1) * H_FONT);
function x(i) = W_SPACE + (((W - (3 * W_SPACE)) / (2 * (C_LOCK - 1))) * (i - 1));
function tXy(disabled) = disabled ? 0 : T_XY;
function eps(enabled) = enabled ? EPS : 0;

// Extra thickness according to the currently selected part.
T_EXTRA2 = (PART == 0) ? T_EXTRA : 0;

// Total board width.
W = W_LINE + (2 * H_FONT) + W_KNOB + T_OVERHANG + (3 * W_SPACE) + (2 * T_WALL);
// Total board height.
H = (C_LINE * H_FONT) + ((C_LINE + 1) * H_SPACE);
// Total board depth.
D = (6 + T_EXTRA2) * T_BASE;
// Hole cylinder offset of the depth to adjust center positioning.
D_OFF = (D + EPS) / 2;
// Number of horizontal locks per line (half width; one every 20mm).
C_LOCK = ceil((W - (3 * W_SPACE)) / 40) + 1;

echo(str("Board dimensions: ", W, "x", H, "x", D, "mm"));
assert(W_SPACE > ((2 * T_OVERHANG) + T_WALL + (2 * T_XY)), "Spacing width is too small for the used sliding overhang.");
assert((W_SPACE + (2 * T_WALL)) > D_CORNER, "Spacing width is too small for the used corner hole diameter.");
assert((H_SPACE + (2 * T_WALL)) > D_CORNER, "Spacing height is too small for the used corner hole diameter.");
assert((H_SPACE + (2 * T_WALL)) > D_MIDDLE, "Spacing height is too small for the used middle hole diameter.");

module rounded2d(r = 0) {
	offset(r = r) offset(r = -r) children();
}

module roundedStroke(w, r) {
	union() {
		translate([r, 0, 0]) square([w, 2 * r]);
		translate([r, r, 0]) circle(r);
		translate([r + w, r, 0]) circle(r);
	}
}

module printText(z, h) {
	translate([W_SPACE, 0.25 * H_FONT, z])
	linear_extrude(height = h)
	text(T_PRINT, size = 0.5 * H_FONT, font = "Liberation Sans", halign = "left", valign = "baseline");
}

module namePlate(cutOut = false) {
	difference() {
		union() {
			if (cutOut == true) {
				// Base plate.
				translate([W_SPACE, 0, 0])
				cube([W_LINE + eps(cutOut) - tXy(cutOut), H_FONT - (2 * tXy(cutOut)), D + (2 * eps(cutOut))]);
				// Sliding.
				translate([0, -T_OVERHANG, 2 * T_BASE])
				cube([W_LINE + W_SPACE + T_OVERHANG + eps(cutOut) - tXy(cutOut), H_FONT + (2 * T_OVERHANG) - (2 * tXy(cutOut)), (2 + T_EXTRA2) * T_BASE]);
			} else {
				// Base plate.
				translate([W_SPACE - tXy(cutOut), 0, 0])
				cube([W_LINE + eps(cutOut) - tXy(cutOut), H_FONT - (2 * tXy(cutOut)), (4 + T_EXTRA) * T_BASE]);
				// Sliding.
				translate([0, -T_OVERHANG, 0])
				cube([W_LINE + W_SPACE + T_OVERHANG + eps(cutOut) - tXy(cutOut), H_FONT + (2 * T_OVERHANG) - (2 * tXy(cutOut)), (2 + T_EXTRA2) * T_BASE]);
			}
			// Lock.
			if (cutOut == true && W_LOCK > 0) {
				translate([W_SPACE / 2, H_FONT / 2, (D + (2 * EPS)) / 2])
				cube([(T_BASE * H_LOCK) + (2 * T_XY), (T_WALL * W_LOCK) + (2 * T_XY), D + (2 * eps(cutOut))], center = true);
			}
			// Embossed text.
			if (cutOut == false) {
				if (S_TEXT == 1) {
					printText((4 + T_EXTRA) * T_BASE, 2 * T_BASE);
				} else if (S_TEXT == 3) {
					printText((4 + T_EXTRA) * T_BASE, T_BASE);
				}
			}
		}
		if (cutOut == false) {
			// Engraved text.
			if (S_TEXT == 2) {
				printText(((2 + T_EXTRA) * T_BASE) + EPS, (2 * T_BASE) + EPS);
			} else if (S_TEXT == 3) {
				printText((3 + T_EXTRA) * T_BASE, T_BASE);
			}
			// Lock.
			if (W_LOCK > 0) {
				translate([W_SPACE / 2, H_FONT / 2, ((D + (2 * EPS)) / 2) - EPS])
				cube([(T_BASE * H_LOCK) + (2 * T_XY), (T_WALL * W_LOCK) + (2 * T_XY), D + (2 * EPS)], center = true);
			}
		}
	}
}

module basePlate() {
	difference() {
		// Base contour.
		linear_extrude(height = D) rounded2d(R_CORNER_XY) square([W, H]);
		// Middle holes.
		if (D_MIDDLE > 0) {
			translate([W / 2, H_SPACE / 2, D_OFF]) cylinder(h = D + (2 * EPS), d = D_MIDDLE + (2 * T_XY), center = true);
			translate([W / 2, H - (H_SPACE / 2), D_OFF]) cylinder(h = D + (2 * EPS), d = D_MIDDLE + (2 * T_XY), center = true);
		}
		// Corner holes.
		if (D_CORNER > 0) {
			translate([W_SPACE / 2, H_SPACE / 2, D_OFF]) cylinder(h = D + (2 * EPS), d = D_CORNER + (2 * T_XY), center = true);
			translate([W - (W_SPACE / 2), H_SPACE / 2, D_OFF]) cylinder(h = D + (2 * EPS), d = D_CORNER + (2 * T_XY), center = true);
			translate([W_SPACE / 2, H - (H_SPACE / 2), D_OFF]) cylinder(h = D + (2 * EPS), d = D_CORNER + (2 * T_XY), center = true);
			translate([W - (W_SPACE / 2), H - (H_SPACE / 2), D_OFF]) cylinder(h = D + (2 * EPS), d = D_CORNER + (2 * T_XY), center = true);
		}
		for (i = [1:1:C_LINE]) {
			// Visible text cutout.
			translate([-EPS, y(i), -EPS]) namePlate(true);

			// Visible slide cutout.
			translate([(2 * W_SPACE) + W_LINE + H_FONT, y(i) - T_XY, 2 * T_BASE])
			linear_extrude(height = D - (2 * T_BASE) + EPS, convexity = 11) roundedStroke(W_KNOB + T_OVERHANG, (H_FONT / 2) + T_XY);
			// Sliding cutout.
			translate([(2 * W_SPACE) + W_LINE - T_OVERHANG, y(i) - T_OVERHANG - T_XY, 2 * T_BASE])
			linear_extrude(height = (2 + T_EXTRA2) * T_BASE, convexity = 11) roundedStroke(H_FONT + W_KNOB + T_OVERHANG, (H_FONT / 2) + T_OVERHANG + T_XY);
			// Bottom state cutout.
			translate([(2 * W_SPACE) + W_LINE + (1.5 * H_FONT) + W_KNOB + T_OVERHANG, y(i) + (H_FONT / 2), T_BASE])
			linear_extrude(height = T_BASE + EPS, convexity = 11) circle((H_FONT / 2) - (2 * T_WALL));

			// Lock cutout.
			if (W_LOCK > 0) {
				for (j = [1:1:C_LOCK]) {
					// Left side.
					translate([x(j), y(i) + H_FONT + T_OVERHANG + (3 * T_WALL) + 2 * T_XY, D_OFF])
					cube([(W_LOCK * T_WALL) + (2 * T_XY), (T_BASE * H_LOCK) + (2 * T_XY), D + (2 * EPS)], center = true);
					// Right side.
					translate([x(j) + ((W - (3 * W_SPACE)) / 2) + W_SPACE, y(i) + H_FONT + T_OVERHANG + (3 * T_WALL) + 2 * T_XY, D_OFF])
					cube([(W_LOCK * T_WALL) + (2 * T_XY), (T_BASE * H_LOCK) + (2 * T_XY), D + (2 * EPS)], center = true);
				}
			}
		}
		// Lock cutout (below bottom line).
		if (W_LOCK > 0) {
			for (j = [1:1:C_LOCK]) {
				// Left side.
				translate([x(j), y(1) - T_OVERHANG - (3 * T_WALL) - 2 * T_XY, D_OFF])
				cube([(W_LOCK * T_WALL) + (2 * T_XY), (T_BASE * H_LOCK) + (2 * T_XY), D + (2 * EPS)], center = true);
				// Right side.
				translate([x(j) + ((W - (3 * W_SPACE)) / 2) + W_SPACE, y(1) - T_OVERHANG - (3 * T_WALL) - 2 * T_XY, D_OFF])
				cube([(W_LOCK * T_WALL) + (2 * T_XY), (T_BASE * H_LOCK) + (2 * T_XY), D + (2 * EPS)], center = true);
			}
		}
	}
}

module plateLock() {
	linear_extrude(height = T_BASE * H_LOCK, convexity = 11)
	translate([O_LOCK, 0, 0])
	polygon([
		[-O_LOCK, 0],
		[-O_LOCK, (2 * T_WALL)],
		[T_WALL - O_LOCK, (2 * T_WALL)],
		[0, (2 * T_WALL)],
		[0, (2 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[-O_LOCK, (2 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[-O_LOCK, T_WALL + (2 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[0, (4 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[(2 * T_WALL), (4 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[(2 * T_WALL), (2 * T_WALL)],
		[(W_LOCK * T_WALL) - (2 * T_WALL), (2 * T_WALL)],
		[(W_LOCK * T_WALL) - (2 * T_WALL), (4 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[(W_LOCK * T_WALL), (4 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[(W_LOCK * T_WALL) + O_LOCK, T_WALL + (2 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[(W_LOCK * T_WALL) + O_LOCK, (2 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[(W_LOCK * T_WALL), (2 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)],
		[(W_LOCK * T_WALL), (2 * T_WALL)],
		[(W_LOCK * T_WALL) + O_LOCK, (2 * T_WALL)],
		[(W_LOCK * T_WALL) + O_LOCK, 0]
	]);
}

module slider() {
	difference() {
		union() {
			// Base.
			linear_extrude(height = 2 * T_BASE, convexity = 11) roundedStroke(W_KNOB + T_OVERHANG, (H_FONT / 2) + T_OVERHANG - T_XY);
			// Knob.
			translate([(H_FONT / 2) + (2 * T_OVERHANG) - T_XY + W_KNOB, (H_FONT / 2) + T_OVERHANG - T_XY, 0])
			linear_extrude(height = 8 * T_BASE, convexity = 11) intersection() {
				circle(r = (H_FONT / 2) - T_XY);
				translate([H_FONT - (2 * T_XY) - W_KNOB, 0, 0]) circle(r = (H_FONT / 2) - T_XY);
				translate([(H_FONT / 2) - T_XY - (W_KNOB / 2), 0, 0]) circle(r = (H_FONT / 4) - (T_XY / 2));
			}
		}
		// Top state cutout.
		translate([(H_FONT / 2) + (3 * T_OVERHANG / 2) - T_XY, (H_FONT / 2) + T_OVERHANG - T_XY, T_BASE])
		linear_extrude(height = T_BASE + EPS, convexity = 11) circle((H_FONT / 2) - (2 * T_WALL));
	}
}

// Part selector.
if (PART == 0) {
	// Base Plate Bottom
	intersection() {
		basePlate();
		translate([-EPS, -EPS, -EPS]) cube([W + (2 * EPS), H + (2 * EPS), ((3 + T_EXTRA) * T_BASE) + EPS]);
	}
} else if (PART == 1) {
	// Base Plate Top
	translate([0, 0, 6 * T_BASE]) mirror([0, 0, 1]) // mirror for correct print orientation
	intersection() {
		basePlate();
		translate([-EPS, -EPS, 3 * T_BASE]) cube([W + (2 * EPS), H + (2 * EPS), (3 * T_BASE) + EPS]);
	}
} else if (PART == 2) {
	// Base Plate Lock
	if (W_LOCK > 0) plateLock();
} else if (PART == 3) {
	// Base Plate Locks
	if (W_LOCK > 0) {
		for (i = [1:1:C_LINE]) {
			translate([0, 2 * (i - 1) * ((6 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)), 0]) {
				// Name plate lock.
				plateLock();
				// Line locks.
				for (n = [0:1:1]) {
					for (k = [0:1:1]) {
						translate([x(k * C_LOCK) + W_LOCK + (2 * (O_LOCK + T_WALL)), n * ((6 * T_WALL) + ((6 + T_EXTRA) * T_BASE) + (2 * T_XY)), 0]) {
							for (j = [1:1:C_LOCK]) {
								translate([x(j), 0, 0])
								plateLock();
							}
							translate([x(1), 0, 0])
							cube([x(C_LOCK) - x(1), 2 * T_WALL, T_BASE * H_LOCK], center = false);
						}
					}
				}
			}
		}
	}
} else if (PART == 4) {
	// Name Plate
	translate([0, T_OVERHANG, 0]) namePlate();
} else if (PART == 5) {
	// Slider
	slider();
}

```


[User]
I'd like to design a customizable coin holder that I can use for jewelry or keychains. I have an example design, but I want to be able to change more parameters and have it be a little more flexible.

Here are the basic features:

*   **Coin Cavity:** The holder needs a circular recess to hold a coin or similar object. I need to be able to set the diameter and thickness of the coin. I want a little extra clearance built in for the coin (maybe a tolerance setting).

*   **Frame:** The coin cavity should have a surrounding frame. I need to be able to set the width and thickness of the frame edge. I would also like a "shoulder" inside the frame to support the coin - which should also have settable width and thickness.

*   **Hanger/Flank:** The design needs some way to attach it to a chain or keyring. I'd like to use a rounded bar, or "flank", with a hole in it for this purpose. I need to be able to set the diameter of the hole, and the thickness of the flank, and possibly how far the flank extends out from the frame. It would be nice if I could add more than one flank, maybe like 2 or 3 or 4 around the circumference of the frame. I think I'd like them to all be on the same plane, and I'd like to be able to set the angle at which they stick out from the edge (e.g. straight out, or at a 45 degree angle, etc).

*   **Rounded Edges:** I'd also like to round off all the edges nicely, using a radius that I can adjust.

Could you provide an OpenSCAD file (or multiple files if that makes sense) that accomplishes this? I'd like to be able to adjust all the parameters I've mentioned. I'd prefer to work without having to install any extra libraries.

[Assistant]
CoinHolder.scad:
```
//// Version: OpenSCAD 2015.03-2
//// Date: 2019.9.29
//// File: CoinHolder.scad
//// Author: Kijja Chaovanakij

//first layer=0.2 mm.
//other layer=0.15 mm.

/*[smoothness and tolerance]*/
//number of fragments
smooth=48; //[48:low, 72:medium, 100:high, 180:quality]
//tolerance[0.5]
tol=0.5; //[0.3:0.05:1.0]

/*[coin]*/
//coin diameter[25]
coin_dia=25; //[10:0.1:100]
//coin thickness[1.8]
coin_thi=1.8; //[0.8:0.1:20]

/*[frame]*/
//edge width[3.0]
edge_wid=3.0; //[2.8:0.1:10]
//top edge thickness[1.2]
top_edge_thi=1.2; //[0.8:0.1:10]
//shouder width[1.0]
shouder_wid=1.0; //[0.8:0.1:20]
//shouder thickness[1.25]
shouder_thi=1.25; //[0.8:0.1:10]

/*[flank]*/
//flank hole diameter[1.6]
hole_dia=1.6; //[1.0:0.1:10]
//flank thickness[2.3]
flank_thi=2.3; //[0.8:0.1:10]

/*[hidden]*/
$fn=smooth;
minsm=min(max(smooth*2/3, 36), 48);
frame_rad=coin_dia/2+edge_wid;
edge_thi=shouder_thi+coin_thi+top_edge_thi;
rounding=0.95;

//============ main ============//
difference(){
  union(){
//make frame
    rotate_extrude()
    hull(){
      for (i=[coin_dia/2-shouder_wid+rounding, frame_rad-rounding])
        translate([i, rounding, 0])
          circle(r=rounding, $fn=minsm);
      for (i=[coin_dia/2+rounding, frame_rad-rounding])
        translate([i, edge_thi-rounding, 0])
          circle(r=rounding, $fn=minsm);
    }//h
//make flank
    hull(){
      translate([frame_rad+rounding+hole_dia/2, 0, 0])
        end_round_cylin(flank_thi, hole_dia+rounding, rounding);
      translate([(frame_rad-rounding*2)*cos(45), (coin_dia/2+edge_wid-rounding*2)*sin(45), 0])
        end_round_cylin(flank_thi, rounding*2, rounding);
    }//h
    hull(){
      translate([frame_rad+rounding+hole_dia/2, 0, 0])
        end_round_cylin(flank_thi, hole_dia+rounding, rounding);
      translate([(frame_rad-rounding*2)*cos(-45), (coin_dia/2+edge_wid-rounding*2)*sin(-45), 0])
        end_round_cylin(flank_thi, rounding*2, rounding);
    }//h
    translate([frame_rad, 0, 0])
      cylinder(h=rounding, r=rounding*2, $fn=minsm);
  }//u
//make coin room
  translate([0, 0, shouder_thi])
    linear_extrude(edge_thi)
      circle(d=coin_dia+tol);
//make flank hole
  translate([frame_rad+rounding+hole_dia/2, 0, -0.1])
    linear_extrude(edge_thi)
      circle(d=hole_dia, $fn=minsm);
}//d
//

//end_round_cylin();
module end_round_cylin(hi=7, rad=3, rounding=1){
  $fn=minsm;
	rotate_extrude()
  hull(){
      translate([rad-rounding, rounding])
       circle(rounding);
      translate([rad-rounding, hi-rounding])
        circle(rounding);
      translate([0, 0])
        square([0.1, hi]);
  }//h
}//m
//

```


[User]
Hey, I'm looking for an OpenSCAD design for a customizable base plate, kind of like the one I saw on Thingiverse for the Dr!ft car stand. It should have a rectangular base with a recessed area and some text on top. I want to be able to change the text and have it automatically resize to fit the plate. The plate itself should also be configurable, with parameters for the length, width, and height. I'll need to control the depth of the recessed area and the height of the text. I want to make it so that the text and recessed area will be different colors for a two color print, so having the text and the recessed part be separate, even if they share a similar shape, would be very useful. The base plate should have rounded corners to match the original one.

I'd like to use the "Euro Plate" font, which I can download from the link in the original description. It would be great if the code could handle that. Also, I'd really like to keep it as simple as possible, so no need to incorporate external libraries. Could you help me generate a SCAD file for this?

[Assistant]
base-plate.scad:
```
echo(version=version());

text = "A - TF 999";

height = 3;
leght = 80;
width = 17.5;
cornerHeight = 1.4;
corner = 1;
textHeight = cornerHeight;
textPadding = 1.6;

translate([0,0,height / 2]){
    difference(){
        cube([leght,18,height], center = true);
        translate([0,0, height / 2 - cornerHeight / 2])
        cube([leght- 2 * corner,width-2*corner,cornerHeight], center = true);
    }
    translate([0,0, height / 2 - textHeight])
    t(text, s = 60, spacing = 1.05, height=textHeight);
}

module t(t, s = 60, style = "", spacing = 1, height=height) {
    linear_extrude(height)
      resize([leght - 2 * (corner + textPadding),0,0], true) text(t, size = s,
           spacing=spacing,
           halign="center",
           valign="center",
           font = str("Euro Plate", style),
           $fn = 16);
}

module green() color([157/255,203/255,81/255]) children();
module corn() color([249/255,210/255,44/255]) children();
module black() color([0, 0, 0]) children();
```


[User]
Hey, I'm looking to generate a parametric battery holder for 18650 cells, and I need an OpenSCAD file. I have some specific requirements.

First, the holder should accommodate a matrix of cells, so I need to be able to specify the number of rows and columns. Let's call those `pack_x` and `pack_y`, respectively. I need to set the cell diameter to `cell_dia`, and the thickness of the rim around each cell to `cell_rim_thiknes`. There should be a support under each cell, with a height of `cell_rim_hight`.  The depth of the cell holder should be `cell_holder_hight`.

Also, I need to include tabs for connecting wires, with a specified `tab_width` and `tab_thickness`. And finally, can you make the cylinder's circle have 48 sides, that's `$fn=48`.

The battery cells need to be held in a hexagonal grid pattern. This means that odd rows should be offset by half a cell width.  Also, I want there to be fill material in the gaps between the cells, to make it a solid block. There also needs to be support material at the edge of the structure. And lastly, I want to be able to include the tab wires along the rows and diagonals.

I've seen some code for this before, and it used a module called `cell_holder` to make the individual cell holders by using the `difference()` function of two cylinders, with a third cylinder subtracted out of the base. It also used a `fill()` module, which used the `intersection()` function of three `ring()` modules.

I don't think I need any external libraries for this. Can you help me create the OpenSCAD file?

[Assistant]
18650.scad:
```
// number of rows
pack_x = 3; // [1:20]
//number of columns
pack_y = 3; // [1:20]
//Cell diameter
cell_dia = 18.8;
//thickness around cell
cell_rim_thiknes = 0.8;
//hight of support under cell
cell_rim_hight = 2.5;
//cell depth
cell_holder_hight = 8;
//tab wire width
tab_width = 7;
//tab wire thickness
tab_thickness = 1.5;
// number of faces in circle
$fn=48;

//CUSTOMIZER VARIABLES END
/*[hidden]*/

x_offset = (cell_dia+cell_rim_thiknes)/2;
y_offset = 0;
module cell_holder(){
    difference(){
        cylinder(r = (cell_dia/2)+cell_rim_thiknes,h = cell_holder_hight);
        translate([0,0,cell_rim_hight]){
                cylinder(r = cell_dia/2, h =cell_holder_hight);
            }
        translate([0,0,-1]){
            cylinder(r = (cell_dia/2-cell_rim_thiknes), h = cell_holder_hight+2);
        }
    }
}

module x_row(){
    for(i=[0 : pack_x-1]){
        translate([(cell_dia+cell_rim_thiknes)*i,0,0]){
            cell_holder();
            if(i != pack_x-1){
                fill();
                mirror([0,1,0]) fill();
            }
        }
    }
}
module x_tab(){
    translate([0,-tab_width/2,cell_rim_hight-tab_thickness])
    cube(size = [((pack_x-1) * cell_dia)+(pack_x-1)*cell_rim_thiknes, tab_width,cell_holder_hight]);
}
module y_tab_l(){
    translate([(cell_dia+cell_rim_thiknes)*0.25,((cell_dia+cell_rim_thiknes)/4)*1.732,(cell_holder_hight/2)+cell_rim_hight-tab_thickness]){
        rotate(a=-60, v=[0,0,1])
            cube([cell_dia+cell_rim_thiknes, tab_width, cell_holder_hight], center = true);
    }
}

module y_tab_r(){
    translate([(cell_dia+cell_rim_thiknes)*0.25,((cell_dia+cell_rim_thiknes)/4)*1.732,(cell_holder_hight/2)+cell_rim_hight-tab_thickness]){
        rotate(a=60, v=[0,0,1])
            cube([cell_dia+cell_rim_thiknes, tab_width, cell_holder_hight], center = true);
    }
}

module ring(offset){
    difference(){
        translate([0,0,-(offset*0.5)]){
            cylinder(r = ((cell_dia/2)+cell_rim_thiknes)*1.5,h = cell_holder_hight+offset);
        }
        translate([0,0,-(offset*0.5)-1]){
            cylinder(r = ((cell_dia/2)+cell_rim_thiknes)*0.95,h = cell_holder_hight+offset+2);
        }
    }
}
module fill(){
    intersection() {
        ring(0);
        translate([cell_dia+cell_rim_thiknes,0,0]) ring(1);
        translate([(cell_dia+cell_rim_thiknes)/2,(((cell_dia+cell_rim_thiknes)/2)*1.732),0])ring(2);
    }
}

difference(){
    union(){
        // cell holders
        for(j = [0 : pack_y-1]){
            y_offset = (((cell_dia+cell_rim_thiknes)/2)*1.732*j);
            if((j%2) == 0){
                translate([0,y_offset,0]) x_row();
            }else{
                translate([x_offset,y_offset,0]) x_row();
            }
        }
        if(pack_y > 1){
            // outside fill
            for(j = [0 : pack_y-2]){
                y_offset = (((cell_dia+cell_rim_thiknes)/2)*1.732*j);
                if((j%2) != 0){
                    translate([(cell_dia+cell_rim_thiknes)/2,y_offset,0])
                    rotate(a=120, v=[0,0,1])
                    fill();
                    translate([((cell_dia+cell_rim_thiknes)/2)+(cell_dia+cell_rim_thiknes)*(pack_x-1),y_offset,0])
                    rotate(a=60, v=[0,0,1])
                    fill();
                }else{
                    translate([0,y_offset,0])
                    rotate(a=60, v=[0,0,1])
                    fill();
                    translate([(cell_dia+cell_rim_thiknes)*(pack_x-1),y_offset,0])
                    fill();
                }
            }
        }
    }

    // slots for tabwires
    if(tab_thickness != 0){
        for(j = [0 : pack_y-1]){
            y_offset = (((cell_dia+cell_rim_thiknes)/2)*1.732*j);
            if((j%2) == 0){
                translate([0,y_offset,0]) x_tab();
            }else{
                translate([x_offset,y_offset,0]) x_tab();
            }
        }
    //}
        if(pack_y > 1){
            for(j = [0 : pack_y-2]){
                for(k = [0 : pack_x-1]){
                    translate([(cell_dia+cell_rim_thiknes)*k,(((cell_dia+cell_rim_thiknes)/2)*1.732*j),0])
                    if((j%2) == 0){
                        y_tab_r();
                    }else{
                        y_tab_l();
                    }
                }
            }
        }
    }
}

```


[User]
Hey there, I'm looking to generate some vintage-style knobs for my projects and I stumbled across your existing design, VintageKnobA.scad. It's pretty much exactly what I need, but I'd like to make a version that's fully parametric from scratch, and I'm hoping you can help with that!

I really like the overall shape, the indented sides, and the options for shafts.  Here's what I'm thinking for the design, based on what I see in your code and would want in a new one:

**Main Body:**

*   **Diameter:** The base diameter of the knob (excluding the bottom skirt/flange).
*   **Height:** The total height of the knob body.
*   **Taper:** The taper from the base to the top, a value between 0.6 and 1.0 (1.0 being no taper).
*   **Indent Count:** Number of vertical indents (grips) on the side.
*   **Hollow Underside:** A boolean to enable/disable hollowing out the underside, with a warning that supports might be needed when printing.
*   **Indicator:** Boolean or enum (if it's easier to create an enum) for having indicators. It can be split into three booleans (or an enum):
    *  **IndicatorTop:** Boolean for a small dot indicator on the top rim.
    *  **IndicatorBottom:** Boolean for a small dot indicator on the bottom (flange).
    * **IndicatorLine:** Boolean for a vertical line indicator on the body and horizontal line on the top rim.

**Shaft:**

*   **Shaft Type:** An enum for the following shaft types: "D", "Knurled", "DwithSetScrew", "RoundWithSetScrew", and "None".
*   **Shaft Depth:** Depth (length) of the shaft.
*  **ShaftOffset:** X/Y offset to adjust hole size to compensate for shrinkage of printed parts.
*   **D-Shaft Details:**
    *   **DShaftMajorDiameter:** The major diameter of the D-shaft.
    *   **DShaftMinorDiameter:** The minor diameter of the D-shaft.
*   **Knurled Shaft Details:**
    *   **KnurledShaftMajorDiameter:** The major diameter of the knurled shaft.
    *   **KnurlCount:** The number of knurls on the shaft.
*   **Set Screw Details:**
    *   **SetScrewHoleDiameter:** Diameter for the set screw hole.
*   **Shaft Rotation Angle:** Angle to rotate the shaft around its axis.
*   **ShaftExtraClearanceHeight:** Extra clearance around the shank up to a specified height.

**Cap:**

*   **CapIndicator:** Enum with options "None", "ShortLine", "LongLine", "Dot" for an indicator on the cap.

**Hidden:**
   * These should be internally defined and not exposed to the end user: `lineDepth`, `capWidthFactor`, `e`, and `in`.

I'm hoping that you can generate one or more .scad files that will do this. I don't need any external libraries. Thanks so much!

[Assistant]
VintageKnobA.scad:
```
/* [Main] */

// Select which part to render. "Both" will render knob + cap as one integrated part.
Render = "Knob"; // ["Knob", "Cap", "Both"]

/* [Knob settings] */

// Main body diameter (at bottom, excluding skirt)
Diameter = 22;
// Total height
Height = 15;
// Number of indents (grips)
IndentCount = 7; // [3:11]
// Taper (top diameter/bottom diameter). 1.0=no taper 0.6=heavy taper
Taper = 0.95; // [0.6:0.01:1.0]

// Hollow out underside (likely requires supports/raft)
HollowUnderside = false;

// Indicator dot (top)
IndicatorTop = true;
// Indicator dot (bottom)
IndicatorBottom = true;
// Vertical indicator line
IndicatorLine = false;

/* [Shaft settings] */

// Potentiometer/encoder shaft type
ShaftType = "D"; // ["D", "Knurled", "DwithSetScrew", "RoundWithSetScrew", "None"]
// Depth (length) of shaft.
ShaftDepth = 11; // [0.0:0.01:50.0]
// X/Y Offset to compensate for shrinking holes when printing. Increase if too tight. Decrease if too loose.
ShaftOffset = 0.3; // [-1.0:0.01:1.0]

// Shaft diameter (D-shaft) ->O<-
DShaftMajorDiameter = 6.0; // [3.0:0.01:15.0]
// Minor diameter (across cut) (D-shaft) ->D<-
DShaftMinorDiameter = 4.2; // [3.0:0.01:15.0]

// Major diameter (knurled shaft)
KnurledShaftMajorDiameter = 6.0; // [3.0:0.01:15.0]
// Number of serrations/knurls per recolution (knurled shaft)
KnurlCount = 18;

// Hole diameter for self-tapping set screw (if applicable)
SetScrewHoleDiameter = 25.4 * 0.14; // set screw diameters: #4 ~ 0.11", #6 ~ 0.14"

// Rotate the shaft around its axis so the indicator lines up the right way (if needed).
ShaftRotationAngle = 0; // [0:1:360]

// Extra clearance around the shank up to this height (likely requires supports)
ShaftExtraClearanceHeight = 0;

/* [Cap settings] */

// Type of indicator (if any) to render on the cap
CapIndicator = "None"; // ["None", "ShortLine", "LongLine", "Dot"]

/* [Hidden] */

// Constants
lineDepth = 0.5;
capWidthFactor = 0.8;
e=0.001; // error term (e.g. for nudging coincident surfaces for CSG)
in=25.4; // in => mm

// Slant angle for a cylinder with h and diameters d1 and d2
function Slant(h, d1, d2) = atan(0.5*(d1-d2)/h);

// Diameter of a slanted cylinder at height pos (lerp)
function DiameterAt(h, d1, d2, pos) = d1 - (d1-d2)*(pos/h);

module Cone(h, d) {
  cylinder(h=h, d1=d, d2=0);
}

module Tube(h, d1, d2) {
  difference(){
    cylinder(h=h, d=max(d1,d2));
    translate([0,0,-e]) cylinder(h=h+2*e, d=min(d1,d2));
  };
}

// Cylinder with bevel along top edge
module BeveledCylinder(h, d1, d2, bevel, bevelBottom=false) {
  r2 = d2*0.5;
  intersection() {
    cylinder(h=h, d1=d1, d2=d2);
    translate([0,0,-bevel])
      Cone(h=h+r2, d=2*r2*(1+h/r2));
    if (bevelBottom) {
      translate([0,0,-r2+bevel])
        cylinder(h=h+r2, d1=0, d2=d1*(1+h/r2));}
  };
}

// Knurled shaft.
// 6mm major diameter, 18 knurls, fits most pots.
module KnurledShaft(h, clearance=0, flare=0, d=6, count=18) {
  spacing = (d-2*clearance) * PI/count * 0.7;
  difference() {
    BeveledCylinder(h-clearance, d+2*clearance, d+2*clearance, 0.5);
    SquareSerrations(h=h, d1=d+2*clearance, d2=d+2*clearance, fromz=0, toz=h,
      count=18, gapPct=30, rotationDeg=45, slant=0);
  }
  if (flare>0) Cone(h=d/2+flare*2, d=d+flare*2);
}

module DShaft(h, flare=0, overcutHeight=2, d=6.0, dmin=4.2, clearance=0) {
  difference() {
    cylinder(h=h, d=d+2*clearance);
    translate([d/2-(d-dmin)+clearance,-d/2-clearance,-e]) cube([d,d+2*clearance,h+2*e]);
  };
  if (flare > 0) Cone(h = d/2+flare, d=d+2*flare);
  if (overcutHeight > 0)
  {
      cylinder(h=overcutHeight, d=d*1.1);
      translate([0,0,overcutHeight]) Cone(d=d*1.1, h=d*0.3);
  }
}

// Stronger thin-wall d-shaft (hopefully more flexible for a better fit)
// d = shaft diameter ->O<-
// dmin = minor diameter (across cut) ->D<-
module DShaft5(h, d=5.9, dmin=4.3) {
    wallThickness = 0.8;
    outerCylinderD = d + 2*wallThickness + 1.4;
    flatXpos = (d/2) - (d-dmin);

    difference() {
        cylinder(h=h, d=outerCylinderD);
        translate([0,0,-e]) {
            difference() {
                Tube(h=h, d1=d + 2*wallThickness, d2=d);
                translate([-d-wallThickness, -wallThickness/2, 0]) cube([d+wallThickness,wallThickness,h]);
        }};
        translate([flatXpos, -d*0.45, 0])  // TODO: Adjust based on dMin
            cube([wallThickness, d*0.9, h]); // TODO: Adjust based on dMin
    }
}

module Shaft() {
    translate([0,0,-e]) {
        if (ShaftType == "D") {
            DShaft5(h=ShaftDepth,
                d=DShaftMajorDiameter+ShaftOffset,
                dmin=DShaftMinorDiameter+ShaftOffset);
        }
        else if (ShaftType == "Knurled") {
            KnurledShaft(h=ShaftDepth,
                clearance=ShaftOffset,
                d=KnurledShaftMajorDiameter,
                count=KnurlCount);
        }
        else if (ShaftType == "DwithSetScrew") {
          DShaft(h=ShaftDepth, d=DShaftMajorDiameter,
            dmin=DShaftMinorDiameter, clearance=ShaftOffset);
        }
        else if (ShaftType == "RoundWithSetScrew") {
          cylinder(h=ShaftDepth, d=DShaftMajorDiameter+ShaftOffset);
        }

        if (ShaftType=="DwithSetScrew" || ShaftType=="RoundWithSetScrew") {
          translate([0,0,DShaftMajorDiameter])
            rotate([0,90,0])
              cylinder(h=1000, d=SetScrewHoleDiameter, $fn=$fn/2);
        }
    };
}

// Cylinder with bevel along top edge
module BeveledCylinder2(h, d1, d2, bevelx, bevelz) {
  difference() {
    cylinder(h=h, d1=d1, d2=d2);
    translate([0,0,h+e])
      rotate_extrude()
        translate([d2/2-bevelx+e,0,0])
          polygon([[0,0],[bevelx,-bevelz]*2,[bevelx,0]*2]);
  };
}

module SquareSerrations(h, d1, d2, fromz, toz, offset=0, count, gapPct=50, rotationDeg, slant) {
  baseD = DiameterAt(h, d1, d2, fromz);
  side = baseD*PI / count * (100-gapPct)/100;
  for (i=[0:count-1])
      rotate([0,0,360/count*i])
        translate([baseD/2+offset, 0, fromz])
          rotate([0, -slant, 0])
            rotate([0,0,rotationDeg])
              translate([-side/2, -side/2, 0])
                cube([side,side,toz-fromz]);
}

// Generates vertical serrations/indents based on count,
// Indents the circle with radius r by count circles
// centered distancePct % off the main circle,
// with radius r2Pct % of the main radius.
// To be useful: r2Pct > distancePct
module Serrations2(h, d1, d2, fromz, toz, count, distancePct, r2Pct, rotationDeg=0, capTop=false, capBottom=false) {
    baseD = DiameterAt(h, d1, d2, fromz);
    distance = 0.01*(baseD/2)*(100+distancePct);
    r2 = 0.01*(baseD/2)*r2Pct; // radius of serrations
    slantDeg = Slant(h,d1,d2);
    for (i=[0:count-1])
      rotate([0,0,360/count*i])
        translate([distance, 0, fromz])
          rotate([0, -slantDeg, 0])
            rotate([0,0,rotationDeg]) {
              cylinder(h=toz-fromz, d=r2*2);
              if (capBottom) { translate([0,0,0]) sphere(r=r2); }
              if (capTop) { translate([0,0,toz-fromz]) sphere(r=r2); }}
}

module Flange(h1, h2, d1, d2, capAt=0) {
  cylinder(d=d1, h=h1);
  intersection() {
    translate([0,0,h1]) cylinder(h=h2, d1=d1, d2=d2);
    if (capAt > 0) cylinder(h=capAt, d=d1);
  }
}

module SerrationBevels(z, d, count, distancePct, r2Pct, bevel) {
  distance = 0.01*d/2*(100+distancePct);
  dS = 0.01*d*r2Pct; // diameter of serrations
  h = dS*2;
  for (i=[0:count-1])
    rotate([0,0,360/count*i])
      translate([distance, 0, z-dS/2-bevel])
        cylinder(h=h, r1=0, r2=h);
}

module VintageKnobA(h, d, indentCount, taper) {
  rimHeight = d/50;
  h2 = h-rimHeight; // top of vertical sides
  d2 = d*taper; // top diameter (adjust for more/less tapering)
  dCap = d2*capWidthFactor; // inner top (cap) diameter
  dFlange = d*1.11; // flange outer diameter
  sm = d/100; // smoothing (rounding) of features on main body

  difference() {
    union() {
      difference() {
        union() {
          // Main body + rounding
          BeveledCylinder2(h=h2, d1=d, d2=d2, bevelx=sm, bevelz=sm, $fn=$fn*2);

          // Rim
          translate([0,0,h2])
            cylinder(h=rimHeight, d1=d2-sm*2, d2=dCap);
        };

        // Indents (grips) + rounding
        Serrations2(h=h, d1=d, d2=d2, fromz=0, toz=h+1, distancePct=80, // indents
          count=indentCount, r2Pct=89, $fn=$fn*2);
        SerrationBevels(z=h2, d=d2, count=indentCount, distancePct=80, r2Pct=89, bevel=sm);

        if (IndicatorLine) {
          lineWidth = d*0.06;
          // Vertical indicator line
          lineAngle = Slant(h2, d, d2);
          translate([-d/2-lineDepth,-lineWidth/2,0])
            rotate([0,lineAngle,0])
              cube([lineDepth*2,lineWidth,h]);
          // Horizontal indicator line
          hLineAngle = atan2(rimHeight, (d2-dCap)/2-sm);
          translate([-d2/2+sm,-lineWidth/2,h2-lineDepth])
            rotate([0,-hLineAngle,0])
              cube([d2/2+sm,lineWidth,lineDepth*2]);
        }
      };

      // Bottom flange (skirt)
      Flange(h1=d*0.05, h2=d/2, d1=dFlange, d2=0, $fn=$fn*2);
    };

    // Hollow bottom
    if (HollowUnderside)
      translate([0,0,-2*e])
        cylinder(h=(dFlange-d)/2, d1=dFlange-1, d2=d-1);

    // Shaft
    rotate([0,0,ShaftRotationAngle])
      translate([0,0,-2*e]) Shaft();
    // TODO: unified diameter
    translate([0,0,-e])
      cylinder(h=ShaftExtraClearanceHeight, d=DShaftMajorDiameter*1.4);

    // Hole for cap
    translate([0,0,h-1]) cylinder(d=dCap, h=20); // cutout for cap

    // Top (rim) dot
    topDotRadius = 0.1+0.2*(d2-dCap);
    topDotOffset = (d2-0.5*(d2-dCap))/2;
    if (IndicatorTop)
      translate([-topDotOffset,0,h2+0.5*rimHeight+0.5*topDotRadius])
        sphere(r=topDotRadius, $fn=$fn/3);

    // Bottom (flange) dot
    bottomDotRadius = 0.05+0.3*(dFlange-d);
    bottomDotOffset = dFlange/2 - (dFlange-d)/4;
    if (IndicatorBottom)
      translate([-bottomDotOffset,0,d*0.05+(dFlange-d)/4+bottomDotRadius/4])
        sphere(r=bottomDotRadius, $fn=$fn/3);
  }
}

module VintageKnobAcap(d, taper) {
  d2 = d*taper; // top diameter (adjust for more/less tapering)
  dCap = d2*capWidthFactor; // inner top (cap) diameter
  lineWidth = d*0.06;
  difference() {
    cylinder(d=dCap, h=1);
    if (CapIndicator == "ShortLine") {
      translate([-dCap*0.5, -lineWidth/2, 1-lineDepth])
        cube([dCap*0.2,lineWidth,lineDepth*2]);
    }
    if (CapIndicator == "LongLine") {
      translate([-dCap*0.5, -lineWidth/2, 1-lineDepth])
        cube([dCap*0.4,lineWidth,lineDepth*2]);
    }
    if (CapIndicator == "Dot") {
      translate([-dCap*0.39,0,1-lineDepth])
        cylinder(h=lineDepth*2, d=lineWidth*1.4);
    }
  }
}

$fn = $preview ? 20 : 50;

if (Render == "Knob" || Render == "Both")
  VintageKnobA(h=Height, d=Diameter, indentCount=IndentCount, taper=Taper);
if (Render == "Cap")
  VintageKnobAcap(d=Diameter-0.5, taper=Taper);
if (Render == "Both") {
  translate([0,0,Height-1])
    VintageKnobAcap(d=Diameter, taper=Taper);
}

```


[User]
Hey, I'm looking to generate a customizable battery dispenser that I can 3D print. I'd like it to be wall-mountable and have individual columns for different battery types. I've got some ideas on how I want it to work, but I'm not great at coding, especially not in OpenSCAD.

Here's what I'm thinking:

*   **Battery Types:** I need to be able to specify which battery types to include, something like a list of strings that can be "AAA", "AA", "C", "D", and "9V". It should generate a column for each type in the list.

*   **Height:** The overall height of the dispenser should be customizable.

*   **Mounting:** I want it to have screw holes for wall mounting. I need to be able to set the screw shaft diameter and the screw head diameter. If either of these is zero, the screw holes shouldn't be added.

*   **Labels:** I'd like to have small plaques above each column to label what type of battery goes there, with inset text for each label. The height of those plaques should be customizable and the text inset depth should also be customizable (and if I set the plaque height to zero, it should not add the plaque).

*   **Wall Thickness:** There needs to be a customizable wall thickness for the whole thing.

*   **Spacing:** The inside of the columns need to have a little extra space beyond the battery dimensions so the batteries slide down nicely.  I need to be able to control this spacing.

*   **Reference Models:** It would be great if I could optionally generate models of the batteries themselves next to the dispenser so that I can visually check spacing and size during design.

*   **Quality:** I'd like to have a parameter that controls the rendering quality in OpenSCAD, something like "draft" (faster) and "final" (better).

The design should use a library to handle the different battery types and their specific dimensions.  I think it'd be good if it were easily extensible to other battery types down the road.

So basically, I'm looking for an OpenSCAD file that uses a library to do this. It should be parametric using all the things above so I can adjust the variables. The library itself needs to provide the dimensions of each type of battery, so I need that too.  I'm hoping you can help me out!

[Assistant]
battery_dispenser.scad:
```
// Customizable battery dispenser
//
// Generates a wall-mounted battery storage and dispenser device.
// The batteries this dispenser will work with can be customized as well as many
// other aspects.
//
// Share and enjoy!
//
// 27 Mar 2021 - Brad Kartchner - V 1.0

/* [Basic Parameters] */
// A list of the batteries to generate for ("AAA", "AA", "C", "D", and "9V")
Battery_List = ["AAA", "AAA", "AAA", "AA", "AA", "AA", "9V", "9V"];

// The height of the holder
Holder_Height = 147.01;

// The diameter of the shafts of the screws used to secure the battery holder to the wall (0 for no screws)
Screw_Shaft_Diameter = 5.01;

// The diameter of the heads of the screws used to secure the battery holder to the wall (0 for no screws)
Screw_Head_Diameter = 9.38;

/* [Advanced Parameters] */
// The name font
Name_Font = "Courier 10 Pitch:style=Bold";

// The height of the name plaques at the top of each column (0 for no plaque)
Name_Plaque_Height = 3.501;

// The amount to inset the name text
Name_Inset = 0.201;

// The thickness of the walls in the model
Wall_Thickness = 1.601;

// Extra padding spacing to ensure the batteries slide down the tubes nicely
Slide_Space = 0.161;

// Generate reference models for the batteries (true or false)?
Generate_Reference_Hardware = true;

// The quality to generate the model at ("draft" is faster but "final" looks better)
Quality = "final"; // ["draft", "final"]

include<battery_library.scad>

module Generate()
{
    // Generate the first battery column
    Generate_BatteryColumn(Battery_List[0]);

    // Generate the remaining battery columns
    if (len(Battery_List) > 1)
    {
        for (i = [1: len(Battery_List) - 1])
        {
            // Calculate the x-axis position of this column
            widths = [ for (j = [0: i-1]) ColumnOuterWidth(Battery_List[j]) - Wall_Thickness ];
            x_offset = SumVector(widths);
            translate([x_offset, 0, 0])
                Generate_BatteryColumn(Battery_List[i]);
        }
    }

    if (Screw_Shaft_Diameter > 0 && Screw_Head_Diameter > 0)
    {
        GenerateScrewTabs();
    }
}

module Generate_BatteryColumn(battery_name)
{
    if (BatteryLib_Type(battery_name) == "tube")
    {
        Generate_TubeBatteryColumn(battery_name);
    }
    else if (BatteryLib_Type(battery_name) == "rectangle")
    {
        Generate_RectangleBatteryColumn(battery_name);
    }

    if (Generate_Reference_Hardware)
    {
        x_offset = ColumnOuterWidth(battery_name)/2;
        y_offset = BatteryLib_Height(battery_name) + Wall_Thickness;
        z_bottom = BatteryLib_Width(battery_name)/2 + Wall_Thickness;
        for (z_offset = [0: BatteryLib_Width(battery_name) : Holder_Height - BatteryLib_Width(battery_name)])
        {
            translate([x_offset, y_offset, z_bottom + z_offset])
            rotate([90, 90, 0])
                %BatteryLib_GenerateBattery(battery_name);
        }
    }
}

module Generate_TubeBatteryColumn(battery_name)
{
    difference()
    {
        // Create a solid column to carve out
        column_width = ColumnOuterWidth(battery_name);
        column_depth = Holder_Depth;
        column_height = Holder_Height;
        cube([column_width, column_depth, column_height]);

        // Hollow out the column to make space for the batteries
        hollow_width = column_width - Wall_Thickness*2;
        hollow_depth = BatteryLib_Height(battery_name) + Slide_Space*2;
        hollow_height = column_height;
        hollow_x_offset = Wall_Thickness;
        hollow_y_offset = Wall_Thickness;
        hollow_z_offset = Wall_Thickness;
        translate([hollow_x_offset, hollow_y_offset, hollow_z_offset + hollow_width/2])
        {
            cube([hollow_width, hollow_depth, hollow_height]);

            // Round the bottom of the hollow space
            // This also cuts out the opening for the bottom-most battery to be removed
            translate([hollow_width/2, -Wall_Thickness - iota, 0])
            rotate([-90, 0, 0])
                cylinder(d=hollow_width, hollow_depth + Wall_Thickness + iota);
        }

        // Cut out a slot running down the front of the column
        slot_width = BatteryLib_AnodeDiameter(battery_name) + Slide_Space*2;
        slot_height = column_height - Wall_Thickness*4 - Name_Plaque_Height;
        slot_depth = Wall_Thickness + iota*2;
        slot_x_offset = column_width/2 - slot_width/2;
        slot_y_offset = -iota;
        slot_z_offset = Wall_Thickness;
        translate([slot_x_offset, slot_y_offset, slot_z_offset])
        {
            cube([slot_width, slot_depth, slot_height - slot_width/2]);

            // Round the top of the slot
            translate([slot_width/2, 0, slot_height - slot_width/2])
            rotate([-90, 0, 0])
                cylinder(d=slot_width, Wall_Thickness + iota*2);
        }

        // Create an access opening at the bottom front of the column
        access_width = hollow_width;
        access_depth = column_depth/3;
        access_height = Wall_Thickness + iota*2 + hollow_width/2;
        access_x_offset = Wall_Thickness;
        access_y_offset = -iota;
        access_z_offset = -iota;
        translate([access_x_offset, access_y_offset, access_z_offset])
        {
            cube([access_width, access_depth - access_width/2, access_height]);

            // Round the back end of the access opening
            translate([access_width/2, access_depth - access_width/2, 0])
                cylinder(d=access_width, access_height);
        }

        // Generate the name plaque at the top of the column
        if (Name_Plaque_Height > 0)
        {
            name_x_offset = column_width/2;
            name_y_offset = Name_Inset;
            name_z_offset = column_height - Wall_Thickness - Name_Plaque_Height/2;
            translate([name_x_offset, name_y_offset, name_z_offset])
            rotate([90, 0, 0])
            linear_extrude(Name_Inset + iota)
            resize([0, Name_Plaque_Height, 0], auto=true)
                text(battery_name, 10, Name_Font, halign="center", valign="center");
        }
    }
}

module Generate_RectangleBatteryColumn(battery_name)
{
    difference()
    {
        // Create a solid column to carve out
        column_width = ColumnOuterWidth(battery_name);
        column_depth = Holder_Depth;
        column_height = Holder_Height;
        cube([column_width, column_depth, column_height]);

        // Hollow out the column to make space for the batteries
        hollow_width = column_width - Wall_Thickness*2;
        hollow_depth = BatteryLib_Height(battery_name) + Slide_Space*2;
        hollow_height = column_height;
        hollow_x_offset = Wall_Thickness;
        hollow_y_offset = Wall_Thickness;
        hollow_z_offset = Wall_Thickness;
        translate([hollow_x_offset, hollow_y_offset, hollow_z_offset])
            cube([hollow_width, hollow_depth, hollow_height]);

        // Cut out the opening for the bottom-most battery to be removed
        opening_width = BatteryLib_BodyLength(battery_name) + Slide_Space*2;
        opening_depth = Wall_Thickness + iota*2;
        opening_height = BatteryLib_BodyWidth(battery_name) + Slide_Space*2;
        opening_x_offset = Wall_Thickness;
        opening_y_offset = -iota;
        opening_z_offset = Wall_Thickness;
        translate([opening_x_offset, opening_y_offset, opening_z_offset])
        {
            cube([opening_width, opening_depth, opening_height]);

            // Round the top of the opening
            translate([opening_width/2, 0, opening_height])
            rotate([-90, 0, 0])
                cylinder(d=opening_width, opening_depth);
        }

        // Cut out a slot running down the front of the column
        slot_width = max(BatteryLib_AnodeDiameter(battery_name), BatteryLib_CathodeDiameter(battery_name)) + Slide_Space*2;
        slot_height = column_height - Wall_Thickness*4 - Name_Plaque_Height;
        slot_depth = Wall_Thickness + iota*2;
        slot_x_offset = column_width/2 - slot_width/2;
        slot_y_offset = -iota;
        slot_z_offset = Wall_Thickness;
        translate([slot_x_offset, slot_y_offset, slot_z_offset])
        {
            cube([slot_width, slot_depth, slot_height - slot_width/2]);

            // Round the top of the slot
            translate([slot_width/2, 0, slot_height - slot_width/2])
            rotate([-90, 0, 0])
                cylinder(d=slot_width, Wall_Thickness + iota*2);
        }

        // Create an access opening at the bottom front of the column
        access_width = hollow_width;
        access_depth = column_depth/3;
        access_height = Wall_Thickness + iota*2 + hollow_width/2;
        access_x_offset = Wall_Thickness;
        access_y_offset = -iota;
        access_z_offset = -iota;
        translate([access_x_offset, access_y_offset, access_z_offset])
        {
            cube([access_width, access_depth - access_width/2, access_height]);

            // Round the back end of the access opening
            translate([access_width/2, access_depth - access_width/2, 0])
                cylinder(d=access_width, access_height);
        }

        // Generate the name plaque at the top of the column
        if (Name_Plaque_Height > 0)
        {
            name_x_offset = column_width/2;
            name_y_offset = Name_Inset;
            name_z_offset = column_height - Wall_Thickness - Name_Plaque_Height/2;
            translate([name_x_offset, name_y_offset, name_z_offset])
            rotate([90, 0, 0])
            linear_extrude(Name_Inset + iota)
            resize([0, Name_Plaque_Height, 0], auto=true)
                text(battery_name, 10, Name_Font, halign="center", valign="center");
        }
    }
}

module GenerateScrewTabs()
{
    GenerateScrewTab();

    translate([Holder_Width, 0, 0])
    mirror([1, 0, 0])
        GenerateScrewTab();
}

module GenerateScrewTab()
{
    difference()
    {
        // Create the basic rectangular tab
        tab_width = Screw_Head_Diameter + Wall_Thickness*2 + Slide_Space*2;
        tab_height = Holder_Height;
        tab_depth = Wall_Thickness;
        tab_x_offset = -tab_width;
        tab_y_offset = Holder_Depth - Wall_Thickness;
        tab_z_offset = 0;
        translate([tab_x_offset, tab_y_offset, tab_z_offset])
            cube([tab_width, tab_depth, tab_height]);

        // Drill out the screw slots
        shaft_hole_diameter = Screw_Shaft_Diameter + Slide_Space*2;
        head_hole_diameter = Screw_Head_Diameter + Slide_Space*2;
        hole_depth = tab_depth + iota*2;
        hole_distance = shaft_hole_diameter*2;
        hole_x_offset = -tab_width/2;
        hole_y_offset = tab_y_offset - iota;
        for (hole_z_offset = [tab_width/2 + hole_distance, tab_height - tab_width/2])
        {
            translate([hole_x_offset, hole_y_offset, hole_z_offset])
            {
                // Create a hole wide enough for the screw shaft
                rotate([-90, 0, 0])
                    cylinder(d=shaft_hole_diameter, hole_depth);

                // Create a hole wide enough for the screw head
                translate([0, 0, -hole_distance])
                rotate([-90, 0, 0])
                    cylinder(d=head_hole_diameter, hole_depth);

                // Create a slot connecting the two holes
                translate([-shaft_hole_diameter/2, 0, -hole_distance])
                    cube([shaft_hole_diameter, hole_depth, hole_distance]);
            }
        }
    }
}

function ColumnOuterWidth(battery_name) =
    BatteryLib_BodyLength(battery_name) + Slide_Space*2 + Wall_Thickness*2;

function SumVector(v, i = 0) =
    (i < len(v) - 1) ?
        v[i] + SumVector(v, i + 1) :
        v[i];

$fn = Quality == "final" ? 128 : 32;
iota = 0.001;

BatteryLib_Heights = [for (battery_name = Battery_List) BatteryLib_Height(battery_name) ];
Holder_Depth = max(BatteryLib_Heights) + Wall_Thickness*2 + Slide_Space*2;
Column_Widths = [for (battery_name = Battery_List) ColumnOuterWidth(battery_name) ];
Holder_Width = SumVector(Column_Widths) - Wall_Thickness * (len(Battery_List) - 1);

Generate();

```
battery_library.scad:
```
// Battery modeling library
//
// Simplifies (for me, anyway) generating models of and for common battery types.
//
// Share and enjoy!
//
// 27 Mar 2021 - Brad Kartchner - V 1.0
//  Supports "AAA", "AA", "C", "D", and "9V" batteries.
//  Could be easily extended (and probably should be) for coin cell batteries.

IDX_NAME = 0;
IDX_TYPE = IDX_NAME + 1;

// Tube battery dimensions
//
// |<-->| Body Diameter
//  >||<- Anode Diameter
//  _--_  === Anode Height
// |    |  ^
// |    |  |
// |    | Body Height
// |    |  |
// |____|  V
//   --   === Cathode Height
//  >||<- Cathode Diameter
IDX_TUBE_BODY_DIAMETER = IDX_TYPE + 1;
IDX_TUBE_BODY_HEIGHT = IDX_TUBE_BODY_DIAMETER + 1;
IDX_TUBE_CATHODE_DIAMETER = IDX_TUBE_BODY_HEIGHT + 1;
IDX_TUBE_CATHODE_HEIGHT = IDX_TUBE_CATHODE_DIAMETER + 1;
IDX_TUBE_ANODE_DIAMETER = IDX_TUBE_CATHODE_HEIGHT + 1;
IDX_TUBE_ANODE_HEIGHT = IDX_TUBE_ANODE_DIAMETER + 1;

// Rectangular battery Round_Battery_Dimensions
// ...well, really, just 9v batteries...
//
// Cathode Diameter
//  |
//  >||< >||<- Anode Diameter
//  _--___--_ ==== Terminal Height
// |         |  ^
// |         |  |
// |         | Body Height
// |         |  |
// |_________|  V
// |<------->|<- Body Width
//
//  __--__
// |      |
// |      |
// |      |
// |      |
// |______|
// |<---->|<- Body Length
IDX_RECT_BODY_WIDTH = IDX_TYPE + 1;
IDX_RECT_BODY_LENGTH = IDX_RECT_BODY_WIDTH + 1;
IDX_RECT_BODY_HEIGHT = IDX_RECT_BODY_LENGTH + 1;
IDX_RECT_CATHODE_DIAMETER = IDX_RECT_BODY_HEIGHT + 1;
IDX_RECT_CATHODE_HEIGHT = IDX_RECT_CATHODE_DIAMETER + 1;
IDX_RECT_ANODE_DIAMETER = IDX_RECT_CATHODE_HEIGHT + 1;
IDX_RECT_ANODE_HEIGHT = IDX_RECT_ANODE_DIAMETER + 1;
IDX_RECT_TERMINAL_DISTANCE = IDX_RECT_ANODE_HEIGHT + 1;

// These battery dimensions are based mostly on dimensions found in Wikipedia
// articles for each battery, along with some actual measurements.  They're
// accurate for the batteries I have, but may need some tweaking to account
// for variations between manufacturers.
BatteryLib_Dimensions =
[
    [
        "AAA",
        "tube",
        10.50,  // Body diameter
        43.50,  // Body height
         6.30,  // Cathode diameter
         0.40,  // Cathode height
         3.80,  // Anode diameter
         0.80,  // Anode height
    ],

    [
        "AA",
        "tube",
        14.50,  // Body diameter
        49.50,  // Body height
         9.10,  // Cathode diameter
         0.40,  // Cathode height
         5.50,  // Anode diameter
         1.00,  // Anode height
    ],

    [
        "C",
        "tube",
        26.20,  // Body diameter
        47.60,  // Body height
        18.00,  // Cathode diameter
         0.40,  // Cathode height
         6.00,  // Anode diameter
         2.00,  // Anode height
    ],

    [
        "D",
        "tube",
        34.20,  // Body diameter
        59.60,  // Body height
        18.00,  // Cathode diameter
         0.40,  // Cathode height
         9.50,  // Anode diameter
         1.50,  // Anode height
    ],

    [
        "9V",
        "rectangle",
        26.50,  // Body Width
        17.50,  // Body Length
        46.40,  // Body height
         8.52,  // Cathode diameter
         2.10,  // Cathode height
         5.75,  // Anode diameter
         2.10,  // Anode height
        12.70,  // terminal distance
    ],
];

// Generate a model of a specified battery
// Supported battery names
// ["AAA", "AA", "C", "D", "9V"]
module BatteryLib_GenerateBattery(battery_name)
{
    if (BatteryLib_Type(battery_name) == "tube")
        BatteryLib_GenerateTubeBattery(battery_name);
    else if (BatteryLib_Type(battery_name) == "rectangle")
        BatteryLib_GenerateRectangleBattery(battery_name);
    else
        assert(false, str(battery_name, " type ", BatteryLib_Type(battery_name), " is not a recognized battery type"));
}

// Generate a specified "tube" style battery
module BatteryLib_GenerateTubeBattery(battery_name)
{
    if (BatteryLib_Type(battery_name) != "tube")
        assert(false, str(battery_name, " is not a tube battery"));

    body_diameter = BatteryLib_BodyDiameter(battery_name);
    body_height = BatteryLib_BodyHeight(battery_name);
    cathode_diameter = BatteryLib_CathodeDiameter(battery_name);
    cathode_height = BatteryLib_CathodeHeight(battery_name);
    anode_diameter = BatteryLib_AnodeDiameter(battery_name);
    anode_height = BatteryLib_AnodeHeight(battery_name);

    translate([0, 0, cathode_height])
    {
        cylinder(d=body_diameter, body_height);
        translate([0, 0, body_height])
            cylinder(d=anode_diameter, anode_height);
        translate([0, 0, -cathode_height])
            cylinder(d=cathode_diameter, cathode_height);
    }
}

// Generate a specified "rectangle" battery
module BatteryLib_GenerateRectangleBattery(battery_name)
{
    if (BatteryLib_Type(battery_name) != "rectangle")
        assert(false, str(battery_name, " is not a rectangle battery"));

    body_width = BatteryLib_BodyWidth(battery_name);
    body_length = BatteryLib_BodyLength(battery_name);
    body_height = BatteryLib_BodyHeight(battery_name);
    cathode_diameter = BatteryLib_CathodeDiameter(battery_name);
    cathode_height = BatteryLib_CathodeHeight(battery_name);
    anode_diameter = BatteryLib_AnodeDiameter(battery_name);
    anode_height = BatteryLib_AnodeHeight(battery_name);
    terminal_distance = BatteryLib_TerminalDistance(battery_name);

    translate([-body_width/2, -body_length/2, 0])
        cube([body_width, body_length, body_height]);
    translate([-terminal_distance/2, 0, body_height])
        cylinder(d=anode_diameter, anode_height);
    translate([terminal_distance/2, 0, body_height])
        cylinder(d=cathode_diameter, cathode_height);
}

// Retrieve the parameters specific to a specified battery
function RetrieveBatteryParameters(battery_name) =
	BatteryLib_Dimensions [search([battery_name], BatteryLib_Dimensions) [0] ];

// Retrieve the type of a specified battery
function BatteryLib_Type(battery_name) =
    RetrieveBatteryParameters(battery_name) [IDX_TYPE];

// Retrieve the body diameter of a specified battery
// This really only applies to tube batteries
// For rectangle batteries, the larger of the width and length dimensions is
// returned
function BatteryLib_BodyDiameter(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_BODY_DIAMETER] :
        max(BatteryLib_BodyWidth(battery_name), BatteryLib_Length(battery_name));

// Retrieve the diameter of a specified battery
// This is just syntactic sugar for the previous function
function BatteryLib_Diameter(battery_name) =
    BatteryLib_BodyDiameter(battery_name);

// Retrieve the width of a specified battery
function BatteryLib_BodyWidth(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_BODY_DIAMETER] :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_BODY_WIDTH];

// Retrieve the width of a specified battery
// This is just syntactic sugar for the previous function
function BatteryLib_Width(battery_name) =
    BatteryLib_BodyWidth(battery_name);

// Retrieve the length (in the y dimension) of a specified battery
function BatteryLib_BodyLength(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_BODY_DIAMETER] :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_BODY_LENGTH];

// Retrieve the length of a specified battery
// This is just syntactic sugar for the previous function
function BatteryLib_Length(battery_name) =
    BatteryLib_BodyLength(battery_name);

// Retrieve the body height (in the z dimension) of a specified battery
function BatteryLib_BodyHeight(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_BODY_HEIGHT] :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_BODY_HEIGHT];

// Retrieve the total height of a specified battery (including the anode and
// cathode)
function BatteryLib_Height(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        BatteryLib_BodyHeight(battery_name) + BatteryLib_AnodeHeight(battery_name) + BatteryLib_CathodeHeight(battery_name) :
        BatteryLib_BodyHeight(battery_name) + max(BatteryLib_AnodeHeight(battery_name), BatteryLib_CathodeHeight(battery_name));

// Retrieve the diameter of the cathode of a specified battery
function BatteryLib_CathodeDiameter(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_CATHODE_DIAMETER] :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_CATHODE_DIAMETER];

// Retrieve the height of the cathode of a specified battery
function BatteryLib_CathodeHeight(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_CATHODE_HEIGHT] :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_CATHODE_HEIGHT];

// Retrieve the diameter of the anode of a specified battery
function BatteryLib_AnodeDiameter(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_ANODE_DIAMETER] :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_ANODE_DIAMETER];

// Retrieve the height of the anode of a specified battery
function BatteryLib_AnodeHeight(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        RetrieveBatteryParameters(battery_name) [IDX_TUBE_ANODE_HEIGHT] :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_ANODE_HEIGHT];

// Retrieve the horizontal distance between the anode and cathode of a
// specified battery
// This really only applies to rectangle (e.g. 9V) batteries
// For tube batteries, this just returns the body height as a sort of sane
// alternative
function BatteryLib_TerminalDistance(battery_name) =
    BatteryLib_Type(battery_name) == "tube" ?
        BatteryLib_Body_Height(battery_name) :
        RetrieveBatteryParameters(battery_name) [IDX_RECT_TERMINAL_DISTANCE];

// Retrieve the dimensions of the dimensions of a cube completely enveloping
// a specified battery [x, y, z]
function BatteryLib_Envelope(battery_name) =
    [BatteryLib_Width(battery_name), BatteryLib_Length(battery_name), BatteryLib_Height(battery_name)];

```


[User]
I need an OpenSCAD file for a simple LED spacer. It should be a cylinder with two holes for the LED leads and a cutout at the sides for the PCB board, like a saddle. Here's what I need it to do:

*   **Shape:** The main body should be a cylinder.
*   **Holes:** It needs two cylindrical holes for the LED leads. These holes should be on opposite sides of the center of the main cylinder.
*   **Cutout:** It should have a cutout on both sides, like a saddle shape. This cutout should go up to the center of the main cylinder so that the top of the PCB board can be flush with the center of the spacer.
*   **Parameters:** I need to be able to customize the following:
    *   `height`: The height of the cylinder.
    *   `diameter`: The overall diameter of the cylinder.
    *   `holesize`: The diameter of the holes for the LED leads.
    *   `pitch`: The distance between the centers of the two holes.
*   **Default values:** The default values should be `height = 10`, `holesize = 1`, `diameter = 6`, and `pitch = 2.54`.
*   **Libraries:** I don't think I need any special libraries for this, but if you think it'll make it easier, feel free.

I also need it to be clear which parameters to adjust so that it is easier to use in a slicer. For example, I should be able to lower the Z position of the spacer in the slicer to easily adjust the final height of the spacer when printed.

[Assistant]
Round_LED_Spacer.scad:
```
$fn=50;
height = 10;
holesize = 1;
diameter = 6;
pitch = 2.54;
difference(){
    cylinder(h=height,r=diameter/2);
translate([pitch/2,0,-0.1])cylinder(h=height+0.2,r=holesize/2);
translate([-pitch/2,0,-0.1])cylinder(h=height+0.2,r=holesize/2);
translate([pitch/2,-holesize/2,-0.1])cube([(diameter/2)-pitch/2,holesize,height+0.2],center=false);
rotate(180)translate([pitch/2,-holesize/2,-0.1])cube([(diameter/2)-pitch/2,holesize,height+0.2],center=false);
}
```


[User]
I'd like to design a customizable hook for 3D printing. I have some specific parameters in mind.

First, for material properties, I need to be able to set the `Material_Thickness` in mm and the `Corner_Radius_Percentage` as a percentage of the hook width.

For the hook itself, I want to define the `Hook_Inside_Diameter` (also in mm) and the `Hook_Width`.  The hook lip should have two options for shape: `Straight` or `Curved` (0 or 1).

Finally, for the mounting screw, I need to set the `Screw_Hole_Diameter`, `Screw_Head_Diameter`, and `Screw_Head_Countersink_Angle` (in degrees).

Could you generate an OpenSCAD file that takes all of these parameters as inputs to generate the hook? Please use these exact parameter names, as I'll need to change them easily. I'd like to be able to specify things like material thickness, hook dimensions, and screw hole dimensions. I’m envisioning a hook with a circular profile and a flat back for mounting, with the screw holes on the back. I want to be able to adjust the corners to be rounded or sharp, based on that percentage. Make sure the screw diameter affects the minimum width of the hook, too. If the screw head diameter plus twice the material thickness is bigger than the specified `Hook_Width`, then the width of the hook needs to be adjusted to accommodate it.

Also, please include the use of the OpenSCAD built-in modules and do not rely on any external libraries or extensions, even if they are commonly used like MCAD or BOSL.

[Assistant]
codeandmake.com_Hook_v1.1.scad:
```
/*
 * Copyright 2021 Code and Make (codeandmake.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Hook by Code and Make (https://codeandmake.com/)
 *
 * https://codeandmake.com/post/fully-customizable-hook
 *
 * Hook v1.1 (30 March 2021)
 */

/* [Material] */

// Thickness of the material
Material_Thickness = 3; // [1:1:10]

// The size of the corner radius as a percentage
Corner_Radius_Percentage = 50; // [0:1:100]

/* [Hook] */

// The inside diameter of the hook
Hook_Inside_Diameter = 20; // [10:0.1:50]

// The width of the hook (unless the screw size commands larger)
Hook_Width = 15;  // [5:0.1:50]

// The shape of the hook lip
Hook_Lip_Shape = 0; // [0:Straight, 1:Curved]

/* [Screw] */

// The diameter of the screw hole
Screw_Hole_Diameter = 4; // [2:0.1:10]

// The diameter of the screw head
Screw_Head_Diameter = 8; // [2:0.1:15]

// Screw countersink angle (82 and 90 are common)
Screw_Head_Countersink_Angle = 90; // [0:0.1:180]

module hook() {
  screwDiameter = max(Screw_Hole_Diameter, Screw_Head_Diameter);

  // given hook width or width based on screw diameter
  hookWidth = max(Hook_Width, screwDiameter + (Material_Thickness * 2));

  cornerRadius = (hookWidth / 2) * (Corner_Radius_Percentage / 100);

  $fn = 60;

  module ringProfile() {
    translate([Hook_Inside_Diameter / 2, 0, 0]) {
      square([Material_Thickness, hookWidth]);
    }
  }

  module ring(angle) {
    rotate([0, 0, -angle]) {
      translate([0, 0, -hookWidth / 2]) {
        rotate_extrude(convexity = 10, angle = angle) {
          ringProfile();
        }
      }
    }
  }

  module corner(height) {
   if(cornerRadius) {
      intersection() {
        translate([-hookWidth / 2, 0, 0]) {
          cube(size=[hookWidth, hookWidth / 2, height]);
        }

        hull() {
          translate([(-hookWidth / 2) + cornerRadius, (hookWidth / 2) - cornerRadius, 0]) {
            cylinder(r = cornerRadius, h = height);
          }

          translate([(hookWidth / 2) - cornerRadius, (hookWidth / 2) - cornerRadius, 0]) {
            cylinder(r = cornerRadius, h = height);
          }

          translate([-hookWidth / 2, -1, 0]) {
            cube(size=[hookWidth, 1, height]);
          }
        }
      }
    } else {
      translate([-hookWidth / 2, 0, 0]) {
        cube(size=[hookWidth, hookWidth / 2, height]);
      }
    }
  }

  module hookSection() {
    ring(180);

    // straight
    if (Hook_Lip_Shape == 0) {
      translate([(Hook_Inside_Diameter / 2), 0, 0]) {
        rotate([0, 90, 0]) {
          corner(Material_Thickness);
        }
      }
    }

    // curved
    if (Hook_Lip_Shape == 1) {
      intersection() {
        rotate([0, 0, 180]) {
          ring(180);
        }

        rotate([0, 90, 0]) {
          corner((Hook_Inside_Diameter + (Material_Thickness * 2)) / 2);
        }
      }
    }
  }

  module supportSection() {
    translate([0, 0, -hookWidth / 2]) {
      difference() {
        rotate([0, 0, 180]) {
          cube([(Hook_Inside_Diameter / 2) + Material_Thickness, Hook_Inside_Diameter + Material_Thickness, hookWidth]);
        }

        translate([0, 0, -0.5]) {
          cylinder(d = Hook_Inside_Diameter + (Material_Thickness * 2), h = hookWidth + 1, center = false);

          translate([0, -(Hook_Inside_Diameter + Material_Thickness), 0]) {
            cylinder(d = Hook_Inside_Diameter, h = hookWidth + 1, center = false);
          }
        }
      }
    }
  }

  module backPanel() {
    translate([-Hook_Inside_Diameter / 2, 0, 0]) {
      rotate([0, -90, 0]) {
        hull() {
          translate([0, (hookWidth / 2) + Material_Thickness + (screwDiameter / 2), 0]) {
            corner(Material_Thickness);
          }

          translate([0, -(Hook_Inside_Diameter + Material_Thickness), 0]) {
            mirror([0, 1, 0]) {
              corner(Material_Thickness);
            }
          }
        }
      }
    }
  }

  module screwHole() {
    translate([0, 0, -0.5]) {
      cylinder(d = (Screw_Head_Countersink_Angle ? Screw_Hole_Diameter : screwDiameter), h = Material_Thickness + 1);
    }

    if(Screw_Head_Countersink_Angle) {
      countersinkHeight = tan((180 - (Screw_Head_Countersink_Angle)) / 2) * (Screw_Head_Diameter / 2);
      translate([0, 0, Material_Thickness - countersinkHeight]) {
        cylinder(d2 = Screw_Head_Diameter, d1 = 0, h = countersinkHeight);
      }
    }

    translate([0, 0, Material_Thickness - 0.005]) {
      cylinder(d = screwDiameter, h = Hook_Inside_Diameter / 2);
    }
  }

  difference() {
    union() {
      hookSection();
      supportSection();
      backPanel();
    }

    translate([-((Hook_Inside_Diameter / 2) + Material_Thickness), 0, 0]) {
      translate([0, (hookWidth / 2) + Material_Thickness + (screwDiameter / 2), 0]) {
        rotate([0, 90, 0]) {
          screwHole();
        }
      }

      translate([0, -(Hook_Inside_Diameter + Material_Thickness), 0]) {
        rotate([0, 90, 0]) {
          screwHole();
        }
      }
    }
  }
}

hook();

```


[User]
I'm looking for an OpenSCAD design for a customizable boat rail base. I need it to be parametric so I can easily adjust the dimensions.

Here are the key features I need:

**Material:**

*   `Material_Thickness`: I need to be able to set the thickness of the material used for the base and tube.

**Tube:**

*   `Tube_Profile`: I need to be able to choose between a round or rectangular tube profile.
*   `Round_Tube_Inside_Diameter`: If I choose a round tube, I need to specify the inside diameter.
*   `Rectangular_Tube_Inside_Width`: If I choose a rectangular tube, I need to specify its inside width (Y axis).
*   `Rectangular_Tube_Inside_Height`: Also for a rectangular tube, I need to specify its inside height (X axis).
*   `Min_Tube_Length`: The minimum length of the tube on the shortest side.
*   `Tube_Angle`: The angle at which the tube is tilted relative to the base.
*   `Tube_Bevel_Percentage`: I need to be able to bevel the end of the tube, with a percentage determining the bevel's depth.
*   `Grub_Screw_Hole_Diameter`:  I need a hole for a grub screw, and I need to be able to set its diameter. (This hole shouldn't be threaded).

**Base:**

*   `Base_Type`: I need to be able to choose between a round or rectangular base shape.
*   `Screw_Hole_Diameter`:  I need to be able to set the diameter of the screw holes used to mount the base.
*   `Screw_Head_Diameter`: I also need to set the diameter of the screw heads.
*  `Screw_Head_Countersink_Angle`: I need to be able to specify the countersink angle for the screw heads
*   `Screw_Padding`: The minimum distance between the screw holes and the edge of the base and the tube.
*   `Base_Bevel_Percentage`: I need to be able to bevel the edge of the base, similar to the tube, using a percentage.

I would like the design to be reasonably smooth, so please use a decent `$fn` value. I also want it to have good preview performance, which means different `$fn` values for rendering and previewing.

I don't have any particular preference regarding libraries, but please make sure the code is easy to read and understand. Could you provide a single .scad file for this design?

[Assistant]
codeandmake.com_Boat_Rail_Base_v1.4.scad:
```
/*
 * Copyright 2021 Code and Make (codeandmake.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Boat Rail Base by Code and Make (https://codeandmake.com/)
 *
 * https://codeandmake.com/post/fully-customizable-boat-rail-base
 *
 * Boat Rail Base v1.4 (25 April 2021)
 */

/* [Material] */

// Thickness of the material
Material_Thickness = 3; // [2:0.5:5]

/* [Tube] */

// The shape of the tube
Tube_Profile = 1; // [1:Round, 2:Rectangular]

// The inside diameter of the round tube
Round_Tube_Inside_Diameter = 22.5; // [5:0.001:50]

// The inside width (Y axis) of the rectangular tube
Rectangular_Tube_Inside_Width = 22.5; // [5:0.001:50]

// The inside height (X axis) of the rectangular tube
Rectangular_Tube_Inside_Height = 22.5; // [5:0.001:50]

// The length of the tube on the shortest side
Min_Tube_Length = 25.4; // [5:0.001:50]

// The angle to tilt the tube
Tube_Angle = 45; // [45:1:90]

// The amount to bevel the end of the tube (as a percentage)
Tube_Bevel_Percentage = 100; // [0:1:100]

// Grub screw hole diameter. Not threaded. Will require manual tapping.
Grub_Screw_Hole_Diameter = 3; // [0:0.1:8]

/* [Base] */

// The type of base (round or rectangular)
Base_Type = 1; // [1:Round, 2:Rectangular]

// The diameter of the screw hole
Screw_Hole_Diameter = 4; // [2:0.1:10]

// The diameter of the screw head
Screw_Head_Diameter = 8; // [3:0.1:20]

// Screw countersink angle (82 and 90 are common)
Screw_Head_Countersink_Angle = 90; // [0:0.1:180]

// The minimum amount of padding around each screw
Screw_Padding = 1; // [0:0.1:20]

// The amount to bevel the edge of the base (as a percentage)
Base_Bevel_Percentage = 100; // [0:1:100]

module boatRailBase() {
  $fn = 60;
  fnHigh = ($preview ? 60 : 200);

  screwDiameter = max(Screw_Hole_Diameter, Screw_Head_Diameter);
  baseBevelRadius = Material_Thickness * (Base_Bevel_Percentage / 100);
  tubeBevelRadius = Material_Thickness * (Tube_Bevel_Percentage / 100) / 2;

  plateRadiusX = (Tube_Profile == 1 ? Round_Tube_Inside_Diameter : Rectangular_Tube_Inside_Height) / 2;
  plateRadiusY = (Tube_Profile == 1 ? Round_Tube_Inside_Diameter : Rectangular_Tube_Inside_Width) / 2;
  roundPlateRadius = max(
    ((plateRadiusX + Material_Thickness) / cos(90 - Tube_Angle)) + screwDiameter + Screw_Padding + max(Material_Thickness, Screw_Padding),
    (plateRadiusY + Material_Thickness) + screwDiameter + Screw_Padding + max(Material_Thickness, Screw_Padding),
    (Tube_Profile == 2 ? sqrt(pow((plateRadiusX + Material_Thickness) / cos(90 - Tube_Angle), 2) + pow((plateRadiusY + Material_Thickness), 2)) + Material_Thickness : 0)
  );

  rectangularPlateRadiusX = max(((plateRadiusX + (Material_Thickness)) / cos(90 - Tube_Angle)) +
    Material_Thickness,
    screwDiameter + (Screw_Padding / 2) + max(Material_Thickness, Screw_Padding));
  rectangularPlateRadiusY = plateRadiusY + (Material_Thickness) +
    screwDiameter + Screw_Padding + max(Material_Thickness, Screw_Padding);
  tubeLength = (plateRadiusX * tan(90 - Tube_Angle)) + Min_Tube_Length;

  module roundBasePlate() {
    translate([0, 0, -Material_Thickness]) {
      union() {
        cylinder(r = roundPlateRadius, h = Material_Thickness - baseBevelRadius, $fn = fnHigh);
        translate([0, 0, Material_Thickness - baseBevelRadius]) {
          intersection() {
            cylinder(r = roundPlateRadius, h = Material_Thickness, $fn = fnHigh);
            hull() {
              rotate_extrude(convexity=10, $fn = fnHigh) {
                translate([roundPlateRadius - baseBevelRadius, 0, 0]) {
                  circle(r=baseBevelRadius);
                }
              }
            }
          }
        }
      }
    }
  }

  module rectangularBasePlateRoundedCorner() {
    translate([rectangularPlateRadiusX - baseBevelRadius, rectangularPlateRadiusY - baseBevelRadius, -baseBevelRadius]) {
      intersection() {
        union() {
          sphere(r = baseBevelRadius, $fn = fnHigh);
          translate([0, 0, -Material_Thickness + baseBevelRadius]) {
            cylinder(r = baseBevelRadius, h = Material_Thickness - baseBevelRadius, $fn = fnHigh);
          }
        }
        translate([0, 0, -Material_Thickness + baseBevelRadius]) {
          cube(size=[baseBevelRadius, baseBevelRadius, Material_Thickness]);
        }
      }
    }
  }

  module rectangularBasePlate() {
    if(baseBevelRadius) {
      hull() {
        hull() {
          rectangularBasePlateRoundedCorner();

          mirror([0, 1, 0]) {
            rectangularBasePlateRoundedCorner();
          }
        }

        hull() {
          mirror([1, 0, 0]) {
            rectangularBasePlateRoundedCorner();

            mirror([0, 1, 0]) {
              rectangularBasePlateRoundedCorner();
            }
          }
        }
      }
    } else {
      translate([0, 0, -Material_Thickness / 2]) {
        cube(size=[rectangularPlateRadiusX * 2, rectangularPlateRadiusY * 2, Material_Thickness], center = true);
      }
    }
  }

  module roundTube() {
    intersection() {
      cylinder(r=(tubeLength * sin(90 - Tube_Angle)) + (((Round_Tube_Inside_Diameter / 2) + Material_Thickness) * cos(90 - Tube_Angle)),
        h=(tubeLength * cos(90 - Tube_Angle)) + (((Round_Tube_Inside_Diameter / 2) + Material_Thickness) * sin(90 - Tube_Angle)),
        $fn = fnHigh);

      rotate([0, 90 - Tube_Angle, 0]) {
        difference() {
          union() {
            translate([0, 0, -tubeBevelRadius]) {
              difference() {
                cylinder(r = (Round_Tube_Inside_Diameter / 2) + Material_Thickness, h = (tubeLength * 2), center = true, $fn = fnHigh);
                cylinder(r = (Round_Tube_Inside_Diameter / 2), h = (tubeLength * 2) + 0.1, center = true, $fn = fnHigh);
              }
            }

            translate([0, 0, tubeLength - tubeBevelRadius]) {
              intersection() {
                cylinder(r = (Round_Tube_Inside_Diameter / 2) + Material_Thickness, h = Material_Thickness / 2, $fn = fnHigh);
                rotate_extrude(convexity=10, $fn = fnHigh) {
                  translate([(Round_Tube_Inside_Diameter / 2) + tubeBevelRadius, 0, 0]) {
                    hull() {
                      circle(r=tubeBevelRadius);
                      translate([Material_Thickness - (tubeBevelRadius * 2), 0, 0]) {
                        circle(r=tubeBevelRadius);
                      }
                    }
                  }
                }
              }
            }
          }

          translate([0, 0, (tubeLength / 2) - (Material_Thickness / 4)]) {
            rotate([0, -90, 0]) {
              cylinder(d = Grub_Screw_Hole_Diameter, h = (Round_Tube_Inside_Diameter / 2) + Material_Thickness + 0.1);
            }
          }
        }
      }
    }
  }

  module rectangularTubeBevelEdge(length, offset) {
    rotate([90, 0, 0]) {
      linear_extrude(height=length, center=true, convexity=10) {
        translate([offset + tubeBevelRadius, 0, 0]) {
          hull() {
            circle(r=tubeBevelRadius);
            translate([Material_Thickness - (tubeBevelRadius * 2), 0, 0]) {
              circle(r=tubeBevelRadius);
            }
          }
        }
      }
    }
  }

  module rectangularTubeBevelCorner() {
    translate([Rectangular_Tube_Inside_Height / 2, Rectangular_Tube_Inside_Width / 2, 0]) {
      intersection() {
        cube(size=[Material_Thickness, Material_Thickness, Material_Thickness]);
        rotate_extrude(convexity=10, $fn = fnHigh) {
          // + 0.0001 is needed to avoid errors in OpenSCAD
          translate([tubeBevelRadius + 0.0001, 0, 0]) {
            hull() {
              circle(r=tubeBevelRadius);
              translate([Material_Thickness - (tubeBevelRadius * 2), 0, 0]) {
                circle(r=tubeBevelRadius);
              }
            }
          }
        }
      }
    }
  }

  module rectangularTubeBevel() {
    rectangularTubeBevelEdge(Rectangular_Tube_Inside_Width, Rectangular_Tube_Inside_Height / 2);

    mirror([1, 0, 0]) {
      rectangularTubeBevelEdge(Rectangular_Tube_Inside_Width, Rectangular_Tube_Inside_Height / 2);
    }

    rotate([0, 0, -90]) {
      rectangularTubeBevelEdge(Rectangular_Tube_Inside_Height, Rectangular_Tube_Inside_Width / 2);
    }

    rotate([0, 0, 90]) {
      rectangularTubeBevelEdge(Rectangular_Tube_Inside_Height, Rectangular_Tube_Inside_Width / 2);
    }

    rectangularTubeBevelCorner();

    mirror([1, 0, 0]) {
      rectangularTubeBevelCorner();
    }

    mirror([0, 1, 0]) {
      rectangularTubeBevelCorner();
    }

    mirror([1, 0, 0]) {
      mirror([0, 1, 0]) {
        rectangularTubeBevelCorner();
      }
    }
  }

  module rectangularTube() {
    intersection() {
      tubeWidth = Rectangular_Tube_Inside_Width + (Material_Thickness * 2);
      tubeHeight = ((tubeLength * sin(90 - Tube_Angle)) + (((Rectangular_Tube_Inside_Height / 2) + Material_Thickness) * cos(90 - Tube_Angle))) * 2;

      translate([- tubeHeight / 2, - tubeWidth / 2, 0]) {
        cube([tubeHeight,
        tubeWidth,
        (tubeLength * cos(90 - Tube_Angle)) + (((Rectangular_Tube_Inside_Height / 2) + Material_Thickness) * sin(90 - Tube_Angle))]);
      }

      rotate([0, 90 - Tube_Angle, 0]) {
        difference() {
          union() {
            translate([0, 0, -tubeBevelRadius]) {
              difference() {
                union() {
                  hull() {
                    translate([Rectangular_Tube_Inside_Height / 2, Rectangular_Tube_Inside_Width / 2, 0]) {
                      cylinder(r=Material_Thickness, h=(tubeLength * 2), center=true);
                    }
                    translate([Rectangular_Tube_Inside_Height / 2, -Rectangular_Tube_Inside_Width / 2, 0]) {
                      cylinder(r=Material_Thickness, h=(tubeLength * 2), center=true);
                    }
                    translate([-Rectangular_Tube_Inside_Height / 2, Rectangular_Tube_Inside_Width / 2, 0]) {
                      cylinder(r=Material_Thickness, h=(tubeLength * 2), center=true);
                    }
                    translate([-Rectangular_Tube_Inside_Height / 2, -Rectangular_Tube_Inside_Width / 2, 0]) {
                      cylinder(r=Material_Thickness, h=(tubeLength * 2), center=true);
                    }
                  }
                }

                cube(size=[(Rectangular_Tube_Inside_Height), (Rectangular_Tube_Inside_Width), (tubeLength * 2) + 0.1], center= true, $fn = fnHigh);
              }
            }

            translate([0, 0, tubeLength - tubeBevelRadius]) {
              intersection() {
                translate([-(Rectangular_Tube_Inside_Height + (Material_Thickness * 2)) / 2, -(Rectangular_Tube_Inside_Width + (Material_Thickness * 2)) / 2, 0]) {
                  cube([Rectangular_Tube_Inside_Height + (Material_Thickness * 2), Rectangular_Tube_Inside_Width + (Material_Thickness * 2), Material_Thickness / 2], $fn = fnHigh);
                }
                rectangularTubeBevel();
              }
            }
          }

          translate([0, 0, (tubeLength / 2) - (Material_Thickness / 4)]) {
            rotate([0, -90, 0]) {
              cylinder(d = Grub_Screw_Hole_Diameter, h = (Rectangular_Tube_Inside_Height / 2) + Material_Thickness + 0.1);
            }
          }
        }
      }
    }
  }

  module baseHole() {
    if(Tube_Profile == 1) {
      rotate([0, 90 - Tube_Angle, 0]) {
        cylinder(d = Round_Tube_Inside_Diameter,
        h = (((tubeLength + Material_Thickness) * cos(90 - Tube_Angle)) + (((Round_Tube_Inside_Diameter / 2) + Material_Thickness) * sin(90 - Tube_Angle)) * 2) + 0.1,
        center = true, $fn = fnHigh);
      }
    }

    if(Tube_Profile == 2) {
      rotate([0, 90 - Tube_Angle, 0]) {
        cube(size=[Rectangular_Tube_Inside_Height, Rectangular_Tube_Inside_Width,
        (((tubeLength + Material_Thickness) * cos(90 - Tube_Angle)) + (((Round_Tube_Inside_Diameter / 2) + Material_Thickness) * sin(90 - Tube_Angle)) * 2) + 0.1],
        center=true);
      }
    }

  }

  module screwHole() {
    translate([0, 0, -Material_Thickness]) {
      translate([0, 0, -0.5]) {
        cylinder(d = (Screw_Head_Countersink_Angle ? Screw_Hole_Diameter : screwDiameter), h = Material_Thickness + 1);
      }

      if(Screw_Head_Countersink_Angle) {
        countersinkHeight = tan((180 - (Screw_Head_Countersink_Angle)) / 2) * (Screw_Head_Diameter / 2);
        translate([0, 0, Material_Thickness - countersinkHeight]) {
          cylinder(d2 = Screw_Head_Diameter, d1 = 0, h = countersinkHeight);
        }
      }

      translate([0, 0, Material_Thickness - 0.005]) {
        cylinder(d = screwDiameter, h = 1);
      }
    }
  }

  module roundBase() {
    maxRadius = roundPlateRadius - max(Material_Thickness, Screw_Padding) - (screwDiameter / 2);
    minRadiusX = ((plateRadiusX + Material_Thickness) / cos(90 - Tube_Angle)) + Screw_Padding + (screwDiameter / 2);
    minRadiusY = plateRadiusY + Material_Thickness + Screw_Padding + (screwDiameter / 2);

    difference() {
      roundBasePlate();

      baseHole();

      translate([0, (maxRadius + minRadiusY) / 2, 0]) {
        screwHole();
      }

      translate([0, -(maxRadius + minRadiusY) / 2, 0]) {
        screwHole();
      }

      translate([-(maxRadius + minRadiusX) / 2, 0, 0]) {
        screwHole();
      }

      if(Tube_Angle == 90) {
        translate([(maxRadius + minRadiusX) / 2, 0, 0]) {
          screwHole();
        }
      }
    }
  }

  module rectangularBase() {
    x = rectangularPlateRadiusX - (screwDiameter / 2) - max(Material_Thickness, Screw_Padding);
    y = rectangularPlateRadiusY - (screwDiameter / 2) - max(Material_Thickness, Screw_Padding);

    difference() {
      rectangularBasePlate();

      baseHole();

      translate([x, y, 0]) {
        screwHole();
      }

      translate([-x, y, 0]) {
        screwHole();
      }

      translate([x, -y, 0]) {
        screwHole();
      }

      translate([-x, -y, 0]) {
        screwHole();
      }
    }
  }

  translate([0, 0, Material_Thickness]) {
    if(Base_Type == 1) {
      roundBase();
    }
    if(Base_Type == 2) {
      rectangularBase();
    }

    if(Tube_Profile == 1) {
      roundTube();
    }
    if(Tube_Profile == 2) {
      rectangularTube();
    }
  }
}

boatRailBase();
```


[User]
I'm looking for an OpenSCAD design for a parametric filament guide, something that I can customize for different printer setups. I want to be able to adjust the following:

*   **Jaw Dimensions:** I need to control the x, y, and z sizes of the jaw, as well as its thickness. Also, a lip around the jaw opening would be nice, and I need to be able to adjust the lip size or turn it off entirely.

*   **Filament Size:**  The guide needs to accommodate different filament diameters.

*   **Arm Geometry:** I want to control the length of the arm in both x and y directions. I also want the ability to choose the arm's position: straight, going off to the left, or going off to the right.

*   **Smoothness:** Ideally, I'd like to control the smoothness of curves via a parameter that adjusts the `$fn` variable.

Essentially, I want a general-purpose filament guide generator that I can tweak to fit my printer needs. Could you provide the OpenSCAD code for such a design?

[Assistant]
FilamentGuide.scad:
```
//// Version: OpenSCAD 2015.03-2
//// Date: 2021.2.12
//// File: FilamentGuide.scad
//// Author: Kijja Chaovanakij

/*[smoothness]*/
//smoothness
smooth=48; //[low:48, medium:60, high:72]
$fn=smooth;
sm=min(max(smooth*2/3, 24), 40);

/*[filament]*/
//filament diameter[1.75]
fil_dia=1.75;

/*[jaw]*/
//jaw x size[15](effective if bigger than hole_edia)
jaw_xsize=22;
//jaw y size and tolerance[20+0.2]
jaw_ys=20.2;
//jaw z size[20]
jaw_zs=20;
//jaw thickness[4]
jaw_thk=4;
//lip size[0.75]
lip=0.75; //[0, 0.25, 0.5, 0.75, 1]

/*[arm]*/
//arm y offset[15](effective if bigger than hole_edia)
arm_yoff=20; //[15:1:150]
//arm x offset[15](effective if bigger than arm_rad)
arm_xoff=20; //[0:1:150]
//arm position
arm_pos=1; //[0:straight, 1:left, 2:rigth]

/*[hidden]*/
rounding=1.0;
hole_thk=jaw_thk+rounding*2;
hole_dia=round(fil_dia+5);
hole_edia=hole_dia+jaw_thk*2;
  echo("hole_edia", hole_edia);
jaw_xs=max(hole_edia, jaw_xsize);
arm_thk=jaw_thk+rounding;
arm_radius=5;
arm_rad=hole_edia/2+arm_radius;
  echo("arm_rad", arm_rad);
arm_ylen=max(jaw_thk+arm_radius+1, arm_yoff-hole_edia/2);
  echo("arm_ylen", arm_ylen);
arm_xlen=max(arm_xoff-arm_rad, arm_xoff-hole_edia-arm_radius);
  echo("arm_xlen", arm_xlen);

//============ main ============//

color("gold"){
jaw();
if (arm_pos==0)
  st_arm();
else if (arm_pos==1)
  mirror([1, 0, 0])
  right_arm();
else if (arm_pos==2)
  right_arm();
}//c
//

module jaw(){
  difference(){
    translate([0, 0, rounding])
      minkowski(){
        linear_extrude(jaw_thk+jaw_zs+lip*2-rounding*2)
        offset(jaw_thk/2-rounding)
          square([jaw_xs-jaw_thk, jaw_ys+jaw_thk*2-jaw_thk], true);
        sphere(r=rounding, $fn=sm);
      }//m
    translate([0, 0, jaw_thk])
      linear_extrude(jaw_zs+lip*2+0.01)
      square([jaw_xs+0.01, jaw_ys], true);
  }//d
  if (lip>0)
    for (i=[0, 1])
      mirror([0, i, 0])
      hull(){
        for (i=[-(jaw_xs/2-rounding-lip*2), (jaw_xs/2-rounding-lip*2)])
        translate([i, jaw_ys/2, jaw_thk+jaw_zs+lip])
          sphere(r=lip, $fn=sm);
      }//h
}//m
//

module st_arm(){
  translate([0, -jaw_ys/2, arm_thk/2])
      rotate([90, 0, 0])
      offset_rect(hi=arm_ylen);
  translate([0, -(jaw_ys/2+arm_ylen), 0])
      fil_hole();
}//m
//

module right_arm(){
  if (arm_xoff>=arm_rad){
    translate([0, -jaw_ys/2, arm_thk/2])
      rotate([90, 0, 0])
      offset_rect(hi=arm_ylen-arm_radius);
    translate([-arm_rad, -(jaw_ys/2+arm_ylen-arm_radius), arm_thk/2])
      rt_profile();
    translate([-arm_rad, -(jaw_ys/2+arm_ylen-arm_radius)-arm_rad, arm_thk/2])
      rotate([-90, 0, 90])
      offset_rect(hi=arm_xlen);
    translate([-arm_rad-arm_xlen, -(jaw_ys/2+arm_ylen-arm_radius)-arm_rad, 0])
      rotate([0, 0, -90])
      fil_hole();
  }//i
}//m
//

//fil_hole();
module fil_hole(){
  difference(){
    hull(){
      translate([0, 0, arm_thk/2])
        rotate([90, 0, 0])
        offset_rect();
      translate([0, -hole_edia/2, 0])
        round_edge_ring(hi=arm_thk, e_rad=hole_edia/2, i_rad=0, rounding=rounding);
    }//h
    translate([0, -hole_edia/2, -0.01])
      linear_extrude(arm_thk+0.02)
      circle(r=hole_dia/2+rounding);
  }//d
  translate([0, -hole_edia/2, 0])
    round_edge_ring(hi=arm_thk+1.5, e_rad=hole_edia/2, i_rad=hole_dia/2, rounding=rounding);
}//m
//

//round_edge_ring();
module round_edge_ring(hi=7, e_rad=10, i_rad=4, rounding=1){
  rotate_extrude()
  minkowski(){
    translate([i_rad+rounding, rounding])
    square([e_rad-i_rad-rounding*2, hi-rounding*2], false);
    circle(r=rounding, $fn=sm);
  }//m
}//m
//

//rt_profile();
module rt_profile(dx=arm_rad, angle1=0, angle2=-90, sector=smooth/4){
  i_angle=(angle2-angle1)/sector;
  for (i=[0:1:sector-1])
    hull(){
      rotate([0, 0, angle1+i*i_angle])
      translate([dx, 0, 0])
        rotate([90, 0, 0])
        offset_rect();
      rotate([0, 0, angle1+(i+1)*i_angle])
      translate([dx, 0])
        rotate([90, 0, 0])
        offset_rect();
    }//h
}//m
//

//offset_rect();
module offset_rect(hi=0.01, xs=hole_edia, ys=arm_thk, rad=rounding){
  $fn=sm;
  linear_extrude(hi)
  offset(rad)
    square([xs-rad*2, ys-rad*2], true);
}//m
//
```