[User]
I'd like to create a set of radius gauges, with both inner and outer radius measurement capability. The gauges should be designed to fit inside a box for organization. I've got a basic idea of how I want them to look, but I need help generating the OpenSCAD code.

Here's a breakdown of what I'm envisioning:

**Gauges:**

*   Each gauge will have a specific radius it measures.
*   The gauge should have a slot to measure an *outer* radius.
*   The gauge will also have a circular cutout on the other side to measure an *inner* radius.
*   The gauges will have a thickness that can be adjusted.
*   Each gauge needs to be labeled with the radius it measures. The text label should be extruded for better visibility.
*   The labeling text should get smaller as the radius gets larger.

**Box:**

*   The box needs to hold all the gauges.
*   It should have slots or cutouts to hold each gauge individually. The box should have depth to fully contain the gauges, so it is more like a case than a tray.
*   The dimensions of the slots need to be adjusted to the different sizes of the gauges, using the radius as the main size parameter.
*   The slots should be arranged so they are neatly lined up.
*   I need the box to be designed so that it's easy to 3D print.
*   I need the box design to allow for custom thickness and to be sized based on the smallest radius and largest radius gauges that are being used.

**Parameters:**

I'd like the following parameters to be customizable:

*   `from`: The radius of the smallest gauge.
*   `to`: The radius of the largest gauge.
*   `thickness`: The thickness of the gauges.
*   `boxHeight`: The height of the box.

I'm aiming to have this all generated as a single, or a series of easily managed, OpenSCAD file(s). I'm not sure if I should be using any specific libraries here like BOSL, but if there is something you think would make this a better design, I'm open to using them.

[Assistant]
RadiusGauge.scad:
```
from = 3;
to = 20;
thickness = 2;
boxHeight = 35;

module createGauge(radius) {
    $fn = 64;
	length = 40;
	difference() {
		union() {
			translate([-length/2+radius, 0, 0]) cylinder(h=thickness, r=radius);
			translate([-length/2+radius, -radius, 0]) cube([length-radius/2, radius*2, thickness]);
		}
		translate([length/2+radius/2, radius/2, 0]) cylinder(h=thickness, r=radius);
		translate([length/2-radius/2, radius/2, 0]) cube([radius, radius, 3]);

		// text
		if(radius <10) translate([-length/2+5, -(radius*1.5)/2, thickness/2]) linear_extrude(height=thickness) text(str(radius), size=radius*1.5);
		else 					 translate([-length/2+5, -(radius*0.75)/2, thickness/2]) linear_extrude(height=thickness) text(str(radius), size=radius*0.75);
	}
}

module createBox() {
    $fn = 64;
	spacing = 0.75;

	difference() {
		// huelle
		hull() {
			translate([0, 2, boxHeight/2]) cube([(from)*2+spacing+4, thickness+spacing+4, boxHeight], center=true);
			translate([0, 2+(to-from)*(thickness+2), boxHeight/2]) cube([(to)*2+spacing+4, thickness+spacing+4, boxHeight], center=true);
		}

		// aussparungen
		for(i=[0:(to-from)]) {
			translate([0, 2+i*(thickness+2), 22]) cube([(from+i)*2+spacing, thickness+spacing, 40], center=true);
		}
	}
}

// generate gauges
y=0;
for(i=[from:to]) {
    $fn = 64;
	y = i+i*i;
	translate([0, y, 0]) createGauge(i);

	//y = -thickness-1+(i)*(1+thickness);
	//translate([0, -5+y, 32]) rotate([90, -90, 0]) createGauge(i);
}

// generate box
translate([-60, 0, 0]) createBox();
```


[User]
I'm looking to create a customizable sponge holder for the kitchen, and I'd like it to be parametric so I can easily adjust it. I want to be able to specify the number of sponge holders, their individual dimensions, and the spacing between them. I've got a pretty detailed idea of how it should work.

Basically, the design will have a base with 1 to 3 sponge holders attached. Each sponge holder should have a main support that rises from the base and a curved arm that holds the sponge. The base is a simple flat rectangle. The arms should be rotatable based on user input. Each sponge holder should have a number of holes cut into the base and sponge support. The holes on the sponge support should be stretched to allow for better drainage. Finally, I want each sponge holder to have a small support at the bottom to keep the sponge from sliding off.

Here's a breakdown of the parameters I'd like to control:

**Overall Structure:**

*   `spongecount`:  An integer to specify how many sponge holders (1, 2, or 3) are attached to the base.
*   `basethickness`: The thickness of the base.
*   `spongespacer`: The space between each sponge holder unit.

**Individual Sponge Holder Dimensions (each with a 1, 2, or 3 suffix):**

*   `sponge[1-3]depth`: The depth of the sponge holder (front to back).
*   `sponge[1-3]angle`: The angle of the sponge holder arm.
*   `sponge[1-3]thickness`: The thickness of the curved arm.
*   `sponge[1-3]height`: The height of the curved arm.
*   `sponge[1-3]width`: The width of the main body where the sponge sits.

**Hole Parameters:**

*   `vertholewidth`: The width of the ventilation holes in the base and sponge supports.
*    `vertholewidthspacer`: The spacing between each hole in the depth direction.
*   `vertholeheight`: The height of the ventilation holes.
*   `vertholeheightspacer`: The spacing between each hole in the height direction.
*   `vertholeedge`: The minimum spacing between the edges and the first hole.
*  `spongeholeratio`: A ratio to stretch the holes in the support for better drainage

**Sponge Support Dimensions:**
* `sponge[1-3]supportwidth`: The width of the bottom support for the sponge.
* `sponge[1-3]supportheight`: The height of the bottom support for the sponge.
* `sponge[1-3]supportdepth`: The depth of the bottom support for the sponge.
* `sponge[1-3]supporthole`: The displacement of the support relative to the sponge holder.

**Calculated Parameters:**

I'd like these to be calculated automatically based on the above parameters. I'm including them here just for clarity.

*   `sponge[1-3]adj`: The horizontal adjustment of the arm. (cos(angle) * height)
*   `width[1-3]`: The total width of each individual sponge holder unit (including the base and sponge spacer) ((adj\*2) + width+spongespacer/2).
*  `depth` The maximum depth of the sponges (max(sponge1depth,sponge2depth,sponge3depth)).

I think I've included everything, but let me know if I missed anything.  I do not need any external libraries for this project.

[Assistant]
parametric_sponge_holder.scad:
```
$fn=360/15;
spongecount=2;
sponge1depth=70;
sponge2depth=70;
sponge3depth=100;
sponge1angle=80;
sponge2angle=80;
sponge3angle=80;
sponge1thickness=2.5;
sponge2thickness=2.5;
sponge3thickness=2.5;
sponge1height=70;
sponge2height=70;
sponge3height=58;
sponge1width=20;
sponge2width=20;
sponge3width=20;
spongespacer=7;
basethickness=2.5;
depth=max(sponge1depth,sponge2depth,sponge3depth);
sponge1adj=(cos(sponge1angle)* sponge1height);
sponge2adj=(cos(sponge2angle)* sponge2height);
sponge3adj=(cos(sponge3angle)* sponge3height);
width1=(sponge1adj*2)+sponge1width+spongespacer/2;
width2=(sponge2adj*2)+sponge2width+spongespacer/2;
width3=(sponge3adj*2)+sponge3width+spongespacer/2;
vertholewidth=15;
vertholewidthspacer=2;
vertholewidthspace=vertholewidth+vertholewidthspacer;
vertholeheight=4;
vertholeheightspacer=4;
vertholeheightspace=vertholeheight+vertholeheightspacer;
vertholeedge=4;
sponge1supportwidth=sponge1width+sponge1thickness;
sponge2supportwidth=sponge2width+sponge2thickness;
sponge3supportwidth=sponge3width+sponge3thickness;
sponge1supportheight=4;
sponge2supportheight=4;
sponge3supportheight=4;
sponge1supportdepth=2;
sponge2supportdepth=2;
sponge3supportdepth=2;
sponge1supporthole=3;
sponge2supporthole=3;
sponge3supporthole=3;
spongeholeratio=3;

module spongearm (move,spongeadj,spongespacer,spongethickness,spongedepth, spongeheight,spongeangle){
    rotate([0,spongeangle,0]) hull(){
        cube([spongethickness,1,1], center=true);
        translate([0,spongedepth-1,0]) cube([spongethickness,1,1], center=true);
        translate([0,0,spongeheight-spongethickness/2]) sphere(d=spongethickness, center=true);
        translate([0,spongedepth-spongethickness/2,spongeheight-spongethickness/2]) sphere(d=spongethickness, center=true);
    }
}

module spongeholder(move,spongespacer,basewidth,spongedepth,basethickness,spongeadj,spongeangle,spongethickness,spongeheight,spongewidth,spongesupportheight,spongesupportwidth,spongesupportdepth,spongesupporthole) {
    union(){
        difference() {
            union(){
                translate([move+spongeadj+spongespacer/4,.5,basethickness/3]) spongearm(move,spongeadj,spongespacer,spongethickness,spongedepth,spongeheight,spongeangle-90);
                translate([move+spongeadj+spongewidth+spongespacer/4,.5,basethickness/3])spongearm(move+spongewidth,spongeadj,spongespacer,spongethickness,spongedepth,spongeheight,10);
                translate([move,0,0]) cube([basewidth,spongedepth,basethickness]);
                translate([move+basewidth/2,spongesupportdepth/2,basethickness+spongesupportheight/2+spongesupporthole]) cube([spongesupportwidth,spongesupportdepth,spongesupportheight], center=true);
            }
            translate([move,0,-spongeheight]) cube([basewidth,spongedepth,spongeheight]);

            numiverthole=floor((spongeheight-vertholeedge-basethickness)/vertholeheightspace)+1;
            for (i=[spongeheight-numiverthole*vertholeheightspace-((spongeheight-numiverthole*vertholeheightspace)/2)+vertholeedge*2:vertholeheightspace:numiverthole*vertholeheightspace]){     //for (i=[3+basethickness: 7 : spongeheight]){

                numjverthole=floor((spongedepth-vertholeedge*2)/vertholewidthspace)+1;
                for (j=[(spongedepth-vertholeedge*2-(numjverthole-1)*vertholewidthspace )-((spongedepth-numjverthole*vertholewidthspace)/2)+vertholewidthspacer/2+vertholeheight/2:vertholewidthspace:numjverthole*vertholewidthspace]) {
                    echo("J");
                    echo(i,j,spongedepth,numjverthole,vertholeedge,vertholewidthspace,spongedepth-vertholeedge*2,(spongedepth-vertholeedge*2)/vertholewidthspace );

                    if((i+vertholeedge+vertholeheight<=spongeheight)) {
                        if(j+vertholewidth<=spongedepth){
                            echo("make");
                            translate([move+basewidth/2,j,i]) {
                                hull(){
                                    rotate([0,90,0]) cylinder(d=vertholeheight,h=basewidth, center=true);
                                    translate([0,vertholewidth-vertholeheight,0]) rotate([0,90,0]) cylinder(d=vertholeheight, h=basewidth, center=true);
                                }
                            }
                            translate([move+spongeadj+spongewidth/2+spongespacer/4,j,0]) {
                                //need to fix scale here to use variables
                                scale([spongeholeratio,1,1])
                                hull(){
                                    cylinder(d=vertholeheight,h=basewidth, center=true);
                                    translate([0,vertholewidth-vertholeheight,0]) cylinder(d=vertholeheight, h=basewidth, center=true);
                                }
                            }

                        }
                    }
                }
            }
        }
        translate([move,spongedepth/4,0]) scale([1,1.5,1]) rotate([0,90,0]) cylinder(r=basethickness,h=basethickness);
        translate([move+basewidth-basethickness,spongedepth/4,0]) scale([1,1.5,1]) rotate([0,90,0]) cylinder(r=basethickness,h=basethickness);
        translate([move,spongedepth/4*3,0]) scale([1,1.5,1]) rotate([0,90,0]) cylinder(r=basethickness,h=basethickness);
        translate([move+basewidth-basethickness,spongedepth/4*3,0]) scale([1,1.5,1]) rotate([0,90,0]) cylinder(r=basethickness,h=basethickness);
    }
}

module makesponge(){
    union(){
        spongeholder(0,spongespacer,width1,sponge1depth,basethickness,sponge1adj,sponge1angle,sponge1thickness,sponge1height,sponge1width,sponge1supportheight,sponge1supportwidth,sponge1supportdepth,sponge1supporthole);
        if (spongecount>1) {
            spongeholder(width1,spongespacer,width2,sponge2depth,basethickness,sponge2adj,sponge2angle,sponge2thickness,sponge2height,sponge2width,sponge2supportheight,sponge2supportwidth,sponge2supportdepth,sponge2supporthole);
        }
        if (spongecount>2){
            spongeholder(width1+width2,spongespacer,width3,sponge3depth,basethickness,sponge3adj,sponge3angle,sponge3thickness,sponge3height,sponge3width,sponge3supportheight,sponge3supportwidth,sponge3supportdepth,sponge3supporthole);
        }
    }
}

makesponge();

```


[User]
I'd like to create a customizable origami-style howling wolf design in OpenSCAD. I've seen an example online, and I want to create something similar but with some flexibility.

Here are the parameters I want to control:

*   **Overall Size:** I need to be able to specify the maximum dimension of the design (either width or height). The other dimension should scale proportionally to maintain the aspect ratio.
*   **Model Height:** I need to set the thickness of the extruded design, so I can make it anything from a thin sticker-like shape to a thicker model.
*   **Outline Thickness:** I need to define the thickness of the outer border/outline of the wolf's shape.
*   **Inner Line Thickness:** I need to define the thickness of the inner lines/cuts that define the wolf's origami folds.
*   **Inner Line Roundness:** The ends of the inner lines need to be rounded, and I need to be able to control the radius of these rounded ends.
*   **Flip Option:**  I need the ability to mirror the design horizontally.

The design itself is a 2D outline of a howling wolf. The outer shape of the wolf is defined by a series of points (A1 through A33). The inner 'cuts' are defined by polygons made up of these points, which are identified as polygons C1 through C15. There is one extra inner point, B1, that is used as part of some of these polygons.

Essentially, I want to provide these parameters and have the code generate an extruded 2D model. The design should automatically scale and generate the inner and outer shapes accordingly.

I think I need to use `polygon`, `offset`, `linear_extrude`, `resize`, and `mirror` in the code. Also I think the `difference()` function is needed to take away the inner parts. And I'm expecting the output to be a single .scad file.

[Assistant]
customizable_origami_howling_wolf.scad:
```
/*
 * Customizable Origami - Howling Wolf - https://www.thingiverse.com/thing:2650270
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-11-16
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

// preview[view:north, tilt:top]

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.4; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.7; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.3; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [268, 265];
A2 = [274, 254];
A3 = [293, 280];
A4 = [287, 244];
A5 = [290, 235];
A6 = [294, 226];
A7 = [306, 222];
A8 = [311, 231];
A9 = [350, 305];
A10 = [405, 345];
A11 = [372, 345];
A12 = [378, 356];
A13 = [406, 431];
A14 = [639, 497];
A15 = [729, 595];
A16 = [732, 689];
A17 = [684, 664];
A18 = [658, 602];
A19 = [643, 635];
A20 = [657, 668];
A21 = [646, 765];
A22 = [608, 765];
A23 = [623, 735];
A24 = [622, 679];
A25 = [561, 585];
A26 = [444, 600];
A27 = [440, 642];
A28 = [421, 765];
A29 = [384, 765];
A30 = [402, 736];
A31 = [389, 660];
A32 = [330, 577];
A33 = [298, 438];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33];

// Inner Points
B1 = [620, 523];

// Polygons
C1 = [A6, A7, A8, A5];
C2 = [A33, A1, A2, A3, A4, A5, A8, A9, A11, A12];
C3 = [A9, A10, A11];
C4 = [A33, A12, A13, A32];
C5 = [A13, A26, A32];
C6 = [A13, A14, B1, A25, A26];
C7 = [A14, A15, A17, A18, B1];
C8 = [A15, A16, A17];
C9 = [B1, A18, A19, A25];
C10 = [A19, A20, A24, A25];
C11 = [A24, A20, A21, A23];
C12 = [A21, A22, A23];
C13 = [A26, A27, A31, A32];
C14 = [A31, A27, A28, A30];
C15 = [A28, A29, A30];

cut_polygons = [C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15];

min_x = A1[0];
min_y = A7[1];
max_x = A16[0];
max_y = A28[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
Okay, I'm looking to design a customizable slew bearing, and I'd like to use OpenSCAD. I really like the features and overall concept of the design I saw on Thingiverse (the one by christianp007), but I want to make it from scratch with my own parameters so I can fully customize it.

Here's what I'm after:

**Overall Structure:**

The design should consist of three main parts: an inner ring (with a top and bottom part), an outer ring (also with a top and bottom part, or potentially a single outer part that can be glued), and rollers. The rollers should sit between the inner and outer rings, allowing rotation.

**Key Parameters (I want to control these):**

*   **`SHOW_PART`**: A variable that lets me select which part to render (e.g., "ALL", "INNER_BOTTOM", "OUTER_TOP", "ROLLER", etc.). This helps generate STL files of each part individually as OpenSCAD doesn't support multi-file STL output.
*   **`quality`**: Controls the `$fn` value, affecting the smoothness of the curves. Higher numbers mean more time to render.
*   **`nozzle`**: The nozzle width used for printing, to help set tolerances.
*   **`outerDiameter`**: The outer diameter of the bearing.
*   **`innerDiameter`**: The inner diameter of the bearing.  This can be 0 if I want a solid center.
*   **`height`**: The height of the bearing, indirectly defining roller diameter and height.
*   **`config_opening_between_inner_outer`**: Space between inner and outer rings. Should default to auto calculation based on roller height but allow manual override.
*   **`tolerance`**: Main tolerance value.
*   **`toleranceRollerDiameter`**: Tolerance specifically for the roller diameter.
*   **`threadTolerance`**: Tolerance for threading.
*   **`rollerChamferFactorPercent`**: Chamfer factor as a percentage of roller height.
*   **`rollerChamferAmountFactorPercent`**: Chamfer amount as a percentage of roller diameter.
*   **`rollerPositionPercent`**: Defines how close to the inner or outer ring the rollers are positioned.
*    **`config_threadThickness`**: Thread thickness, should default to nozzle width, but allow override.
*   **`createThreadsForOuterRing`**: Boolean, true if the outer ring should have threads; false means it has to be glued or printed as one piece.
*   **`posInnerThread`**: Position of the inner thread (0=inner diameter, 100=close to rollers).
*   **`posOuterThread`**: Position of the outer thread (0=close to rollers, 100=outer diameter).

**Specific Design Features:**

*   **Threads:** I'd like threads integrated into both the inner and outer ring parts. The threads in the outer ring should allow it to be assembled in two parts and screwed together.
*   **Rollers:** The rollers should be cylindrical, with chamfered edges on both the top and bottom. The amount and height of the chamfer should be customizable with parameters listed above.
*   **Automatic Calculations:** Some parameters, like the roller diameter and the space between inner and outer rings, should ideally be calculated automatically, based on the `height` and `tolerance` settings, but also allowing me to override with other parameters.
*   **Tolerances:** The tolerance values should be applied intelligently to ensure that the parts fit together correctly. In particular the roller tolerance is quite critical.
*   **Rotation:** The top parts of the inner and outer ring must be rotated for printability.
*   **Error checking:** Some basic error checking and debugging echo statements should also be included, like those in the original design.

**Libraries:**

I'll need functions for creating the threads and for creating a smooth sweep of the thread profile, so I'll need the "Threading.scad" and "Naca_sweep.scad" functions, so please copy that code over too. I'm not including the library directly.

It would be great if the code is well-commented. Thanks!

[Assistant]
slew_bearing.scad:
```
/* [Main] */
// which parts to render - use one at a time to generate printable stl files (sadly OpenSCAD cannot generate multiple stl files at once)
SHOW_PART="ALL"; // [ALL,INNER_BOTTOM,INNER_TOP,OUTER_BOTTOM,OUTER_TOP,OUTER_BOTH,ROLLER]
// larger values increase calculation time considerably, choose small values for rapid prototyping
quality=90; // [30:20:150]
// print width, as configured in your slicer (use nozzle width if not configurable)
nozzle=0.42;
// outer diameter of bearing
outerDiameter=49;
// inner diameter of bearing, may be 0
innerDiameter=8;
// height of bearing, will indirectly define roller diameter and heigth
height=15;

// space between outer and inner, not very critical, will be limited to half roller height; 0=automatic
config_opening_between_inner_outer=0; // [0:0.5:10]

/* [Tolerances] */
// main tolerance setting
tolerance=0.2;
// tolerance for roller diameter, critical to optimize this for quality of bearing
toleranceRollerDiameter=0.03;
// tolerance of the threading
threadTolerance=0.35;

/* [Roller] */
// chamfer factor in percent of roller height, 100=completely chamfer top and bottom half
rollerChamferFactorPercent=30; // [10:5:100]
// how much chamfer is applied, in percent of diameter, 100=chamfer down to 0
rollerChamferAmountFactorPercent=20;
// the roller position, larger values can fit more rollers, extreme values limit thickness of outer/inner walls to a minimum; 0=as close as possible to inner bearing diameter, 100=as close as possible to outer bearing diameter
rollerPositionPercent=80; // [0:5:100]

/* [Threads ]*/
// thread thickness, must be at least nozzle width; 0=automatic
config_threadThickness=0; // [0:0.1:5]
// if the outer ring should have threads, if false you have to print the outer ring as one piece or glue it togehter. Saves space for threads so outer bearing diameter can be smaller.
createThreadsForOuterRing=true; // [true,false]
// position of inner thread; 0=as close as possible to inner hole, 100=as close as possible to rollers
posInnerThread=80; // [0:10:100]
// position of outer thread; 0=as close as possible to rollers, 100=as close as possible to outer bearing diameter
posOuterThread=80; // [0:10:100]

/* [Hidden] */
$fn=quality;

rollerChamferFactor=rollerChamferFactorPercent/100/2;
chamferAmountFactor=rollerChamferAmountFactorPercent/100;

threadThickness=max(config_threadThickness,nozzle);

c=sqrt(2*height/2*height/2); // pythagoras triangle

rollerHeight=c-2*tolerance;

threadWallThickness=2*nozzle+2*threadThickness;
singleThreadSpace=4*nozzle+2*threadThickness+2*threadTolerance;
numThreads=createThreadsForOuterRing ? 2 : 1;
totalThreadSpace=numThreads*singleThreadSpace+(createThreadsForOuterRing?0:2*nozzle);

tmp_opening_between_inner_outer=config_opening_between_inner_outer==0 ? rollerHeight/4 : min(config_opening_between_inner_outer,rollerHeight/2);
opening_between_inner_outer=max(tmp_opening_between_inner_outer,2*tolerance);

// available horizontal space to wiggle with
horizontalRollerSpace=sqrt(2*c*c); // pythagoras triangle, will currently always result in value of variable 'height'
availableHorizontalWiggleSpace=outerDiameter-2*horizontalRollerSpace-totalThreadSpace-innerDiameter;
echo("availableHorizontalWiggleSpace",availableHorizontalWiggleSpace);
innerExtraWallWiggleRoom=availableHorizontalWiggleSpace*(rollerPositionPercent/100);
innerExtraWall=singleThreadSpace/2+innerExtraWallWiggleRoom/2;
outerExtraWallWiggleRoom=availableHorizontalWiggleSpace*(1-(rollerPositionPercent/100));
outerExtraWall=(outerDiameter-innerDiameter)/2-innerExtraWall-horizontalRollerSpace;
echo("outerExtraWall",outerExtraWall);

// --- BEGIN roller calculation ---
middleRollerCircleDiameter=innerDiameter+2*innerExtraWall+height;
middleRollerCircleCircumference = 3.141592*middleRollerCircleDiameter;

middleRollerDiameter=c-toleranceRollerDiameter;
innerRollerDiameter=middleRollerDiameter;
outerRollerDiameter=middleRollerDiameter;
rollerDiameterDifference=outerRollerDiameter-innerRollerDiameter;

spaceAtInnerWallForRollers=3.141592*(middleRollerCircleDiameter-(c/4));
spaceForNumRollers=spaceAtInnerWallForRollers/middleRollerDiameter;
// --- END roller calculation ---

innerThreadDiameter=innerDiameter+threadWallThickness+(innerExtraWallWiggleRoom*(posInnerThread/100));
outerThreadDiameter=outerDiameter-threadWallThickness-(outerExtraWallWiggleRoom*(1-(posOuterThread/100)));

OVERLAP=0.001;

echo("rollerHeight", rollerHeight);
echo("middleRollerDiameter",middleRollerDiameter);
echo("spaceForNumRollers",spaceForNumRollers);

// top parts must be rotated so large side is on bottom
rotate_top_to_make_printable=(len(search("_TOP",SHOW_PART))>=4) ? 180 : 0;

color([0.5,0.5,0.5])
rotate([rotate_top_to_make_printable,0,0])
{
    if (showpart("OUTER_BOTTOM") || SHOW_PART=="OUTER_BOTH"){
        outer0();
    }
    if (showpart("OUTER_TOP") || SHOW_PART=="OUTER_BOTH"){
        outer1();
    }
    if (showpart("INNER_BOTTOM")){
        inner0();
    }
    if (showpart("INNER_TOP")){
        inner1();
    }
}
color([0.3,0.4,1])
{
    if (showpart("ROLLER")){
        if (SHOW_PART=="ALL"){
            renderRollerInBearing();
        } else {
            roller();
        }
    }
}

function showpart(partId) = SHOW_PART=="ALL" ||  SHOW_PART==partId;

module renderRollerInBearing(){
    numRollers=floor(spaceForNumRollers);

    for (i=[0:numRollers-1]){
        rotationDirection=(i%2==0) ? 1 : -1;

        degrees_per_roller=360/numRollers;
        maxPosFromCenter=middleRollerCircleDiameter/2;
        translate([cos(i*degrees_per_roller)*maxPosFromCenter,sin(i*degrees_per_roller)*maxPosFromCenter,height/2])
        rotate([rotationDirection*-sin(i*degrees_per_roller)*45,rotationDirection*cos(i*degrees_per_roller)*45,0])
        translate([0,0,-rollerHeight/2])
        roller();
    }

}

module outer0(){
    difference(){
        half(false,outerExtraWall);

        if (createThreadsForOuterRing){
            translate([0,0,-OVERLAP/2])
            difference(){
                cylinder(d=outerDiameter+1,h=height/2+OVERLAP);
                translate([0,0,-OVERLAP/2])
                cylinder(d=outerThreadDiameter-2*threadThickness-0.3,h=height/2+2*OVERLAP);
            }
        }
    }

    if (createThreadsForOuterRing){
        translate([0,0,+OVERLAP/2])
        thread(height/2-OVERLAP,outerThreadDiameter,threadThickness,1.6,true,false);
    }
}

module outer1(){
    difference(){
        translate([0,0,height])
        rotate([180,0,0])
        half(false,outerExtraWall);
    }

    if (createThreadsForOuterRing){
        union(){
            translate([0,0,+OVERLAP/2])
            thread(height/2-OVERLAP,outerThreadDiameter+2*threadTolerance,threadThickness,1.6,false);

            translate([0,0,-OVERLAP/2])
            difference(){
                cylinder(d=outerDiameter,h=height/2+OVERLAP);
                translate([0,0,-OVERLAP/2])
                cylinder(d=outerThreadDiameter+2*threadThickness+0.3,h=height/2+2*OVERLAP);
            }
        }
    }
}

module inner0(){
    difference(){
        union(){
            hull(){
                half(true,innerExtraWall);
            }
            translate([0,0,height/2-OVERLAP])
            thread(height/2,innerThreadDiameter,threadThickness,1.6,true);
        }

        cylinder(d=innerDiameter+OVERLAP/2,h=2*height+1);
    }
}

module inner1(){
    difference(){
        union(){
            difference(){
                translate([0,0,height])
                rotate([180,0,0])
                hull(){
                    half(true,innerExtraWall);
                }

                translate([0,0,height/2-0.5])
                cylinder(d=innerThreadDiameter+2*threadThickness,h=height/2+1);
            }

            translate([0,0,height/2-OVERLAP/2])
            thread(height/2-OVERLAP,innerThreadDiameter+2*threadTolerance,threadThickness,1.6,false);
        }
//        cylinder(d=innerDiameter+OVERLAP/2,h=2*height+1);
    }
}

module roller(){
    chamferHeight=rollerHeight*rollerChamferFactor;
    chamferDiameterDifference=rollerChamferFactor*rollerDiameterDifference;
    chamferAmount=chamferAmountFactor*outerRollerDiameter+rollerChamferFactor*rollerDiameterDifference;

    difference(){
        cylinder(d1=outerRollerDiameter,d2=innerRollerDiameter,h=rollerHeight);

        translate([0,0,-OVERLAP])
        cylinder(d=outerRollerDiameter+1,h=chamferHeight+OVERLAP);

        translate([0,0,rollerHeight-chamferHeight])
        cylinder(d=outerRollerDiameter+1,h=chamferHeight+OVERLAP);
    }

    d2=outerRollerDiameter-chamferDiameterDifference;
    cylinder(d1=d2-chamferAmount,d2=d2,h=chamferHeight);

    d1=innerRollerDiameter+rollerChamferFactor*rollerDiameterDifference;
    translate([0,0,rollerHeight-chamferHeight])
    cylinder(d1=innerRollerDiameter+rollerChamferFactor*rollerDiameterDifference,d2=d1-chamferAmount,h=chamferHeight);
}

module half(inner=false, extraWall){
    xTranslate=inner ? -height/2-innerDiameter/2-innerExtraWall : innerDiameter/2+innerExtraWall+height/2;
    rotate_extrude()
    translate([xTranslate,0,0])
    difference(){
        union(){
            translate([0,+OVERLAP/2,0])
            square(height/2+OVERLAP);
            translate([height/2,0,0])
            square([extraWall,height/2]);
        }

        translate([0,height/2,0])
        rotate([0,0,-45])
        square(c, center=true);

        square([opening_between_inner_outer/2,height/2]);
    }
}

module thread(height,diameter,threadThickness,pitch, outer=true, full=true){
    // 20 mm produces 0.95mm thread thickness at pitch 1.6
    scaleFactor=(1/threadThickness)*(pitch/1.6);
    generateDiameter=scaleFactor*diameter;

    difference(){
        scale([1/scaleFactor,1/scaleFactor,1])
        rotate([0,0,360/(2*$fn)])
        translate([0,0,-pitch])
        {
         if (outer){
            threading(pitch=pitch, d=generateDiameter, windings = 1.5+height/pitch, angle = 40, full=full);
         } else {
            Threading(pitch=pitch, d=generateDiameter, windings = 2.5+height/pitch, angle = 40);
         }
        }

        translate([0,0,-(pitch+OVERLAP)/2-1/2])
        cylinder(d=diameter*2,h=pitch+OVERLAP+1,center=true);

        translate([0,0,height+pitch/2+OVERLAP/2+1/2])
        cylinder(d=diameter*2,h=pitch+OVERLAP+1,center=true);
    }

}

// =============================================
// copied from libraries Threading.scad and Naca_sweep.sad
// https://www.thingiverse.com/thing:1659079/
// created by Rudolf Huttary
// removed len(<vector>) to remove OpenScad Warnings
// =============================================

module Threading(D = 0, pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=$fn)
{
  R = D==0?d/2+2*pitch/PI:D/2;
  translate([0,0,-pitch])
  difference()
  {
    translate([0,0,pitch])
    cylinder (r=R, h =pitch*(windings-helices));
    threading(pitch, d, windings, helices, angle, steps, true);
  }
}

module threading(pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=$fn, full = false)
{  // tricky: glue two 180° pieces together to get a proper manifold
  r = d/2;
  Steps = steps/2;
  Pitch = pitch*helices;
  if(full) cylinder(r = r-.5-pitch/PI, h=pitch*(windings+helices), $fn=steps);
  sweep(gen_dat());   // half screw
  rotate([0, 0, 180]) translate([0, 0, Pitch/2])
  sweep(gen_dat());   // half screw

  function gen_dat() = let(ang = 180, bar = R_(180, -90, 0, Ty_(-r+.5, vec3D(pitch/PI*Rack(windings, angle)))))
        [for (i=[0:Steps]) Tz_(i/2/Steps*Pitch, Rz_(i*ang/Steps, bar))];

  function Rack(w, angle) =
     concat([[0, 2]], [for (i=[0:windings-1], j=[0:3])
     let(t = [ [0, 1], [2*tan(angle/2), -1], [PI/2, -1], [2*tan(angle/2)+PI/2, 1]])
        [t[j][0]+i*PI, t[j][1]]], [[w*PI, 1], [w*PI, 2]]);
}

// generate polyhedron from multiple airfoil_datasets
// dat - vec of vec1, with vec1 = simple polygon like airfoil_data, > 3 points per dataset expected
module sweep(dat, convexity = 5, showslices = false, plaincaps = true)
{
  n = len(dat);     // # datasets
  l = len(dat[0]);  // points per dataset
  if(l<=3)
    echo("ERROR: sweep() expects more than 3 points per dataset");
  else
  {
    if(n==1)
      polyhedron(points = dat[0], faces = [count(l-1, 0)]);
    else{
    first = plaincaps?[count(l-1, 0)]:
            faces_polygon(l, true);  // triangulate first dataset
    last = plaincaps?[count((n-1)*l,(n)*l-1)]:
            faces_shift((n-2)*l, faces_polygon(l, false)); // triangulate last dataset
    if (showslices)
      for(i=[0:n-1])
        sweep([dat[i]]);
    else
      if (n<2)  // this case is also used recursively for showslices
        polyhedron(points = flat(), faces  = last, convexity = 5);
      else
      {
        polyhedron(points = flat(),
                   faces  = concat(first, last, faces_sweep(l,n)), convexity = 5);
      }
    }
  }
  function count(a, b) = let(st = (a<b?1:-1))[for (i=[a:st:b]) i];
  function faces_shift(d, dat) = [for (i=[0:len(dat)-1]) dat[i] + [d, d, d]];
  function flat() = [for (i=[0:n-1], j=[0:l-1]) dat[i][j]];
}

function del(A, n) = [for(i=[0:len(A)-1]) if (n!=i)A[i]];

//// composition stuff for polyhedron
  function faces_sweep(l, n=1) = let(M = n*l)
      concat([[0,l,l-1]],   // first face
      [for (i=[0:l*(n-1)-2], j = [0,1])
        j==0? [i, i+1, (i+l)] : [i+1, (i+l+1), i+l]],
      [[n*l-1, (n-1)*l-1, (n-1)*l]]) // last face
      ;

  function faces_polygon(l, first = true) = let(odd = (l%2==1), d=first?0:l)
    let(res = odd?concat([[d,d+1,d+l-1]],
      [for (i=[1:(l-3)/2], j=[0,1])(j==0)?[d+i,d+i+1,d+l-i]:[d+i+1,d+l-i-1, d+l-i]]):
      [for (i=[0:(l-4)/2], j=[0,1])(j==0)?[d+i,d+i+1,d+l-i-1]:[d+i+1,d+l-i-2, d+l-i-1]])
    first?facerev(res):res;

  function facerev(dat) = [for (i=[0:len(dat)-1]) [dat[i][0],dat[i][2],dat[i][1]]];

//// vector and vector set operation stuff ///////////////////////
//// Expand 2D vector into 3D
function vec3D(v, z=0) = [for(i = [0:len(v)-1])
  len(v[i])==2?[v[i][0], v[i][1], z]:v[i]+[0, 0, z]];

// Translation - 1D, 2D, 3D point vector //////////////////////////
// vector along all axes
function T_(x=0, y=0, z=0, v) = let(x_ = [x, y, z])
  [for (i=[0:len(v)-1]) T__(x_[0], x_[1], x_[2], p=v[i])];
/// vector along one axis
function Tx_(x=0, v) = T_(x=x, v=v);
function Ty_(y=0, v) = T_(y=y, v=v);
function Tz_(z=0, v) = T_(z=z, v=v);
/// point along all axes 1D, 2D, 3D allowed
function T__(x=0, y=0, z=0, p) = len(p)==3?p+[x, y, z]:len(p)==2?p+[x, y]:p+x;

//// Rotation - 2D, 3D point vector ///////////////////////////////////
// vector around all axes
function R_(x=0, y=0, z=0, v) =             // 2D vectors allowed
  let(x_ = [x, y, z])
  len(v[0])==3?Rx_(x_[0], Ry_(x_[1], Rz_(x_[2], v))):
  [for(i = [0:len(v)-1]) rot(x_[2], v[i])];
// vector around one axis
function Rx_(w, A) = A*[[1, 0, 0], [0, cos(w), sin(w)], [0, -sin(w), cos(w)]];
function Ry_(w, A) = A*[[cos(w), 0, sin(w)], [0, 1, 0], [-sin(w), 0, cos(w)]];
function Rz_(w, A) = A*[[cos(w), sin(w), 0], [-sin(w), cos(w), 0], [0, 0, 1]];

//// Scale - 2D, 3D point vector ///////////////////////////////////
// vector along all axes
function S_(x=1, y=1, z=1, v) =
  [for (i=[0:len(v)-1]) S__(x,y,z, v[i])];
// vector along one axis
function Sx_(x=0, v) = S_(x=x, v=v);
function Sy_(y=0, v) = S_(y=y, v=v);
function Sz_(z=0, v) = S_(z=z, v=v);
// single point in 2D
function S__(x=1, y=1, z=1, p) =
  len(p)==3?[p[0]*x, p[1]*y, p[2]*z]:len(p)==2?[p[0]*x+p[1]*y]:[p[0]*x];
```


[User]
I need an OpenSCAD design for a parametric battery holder. It should be able to model AA, AAA, C, and D batteries, and also allow for custom sizes. The design should include:

*   A cylindrical body with a specified diameter, height, and wall thickness.
*   A small cylindrical pin on top, with its own specified diameter and height.
*   Holes for wires to make electrical connections to both the positive and negative ends of the battery.
*   An option to add zip tie holders, defined by a width and an offset from the top/bottom of the battery.

For the battery dimensions, I want to use the following as defaults based on common battery sizes, but make all of these user-customizable:

*   **D Battery:** Diameter 34.2 mm, height 61.5 mm (including a 1.5 mm pin), wall thickness 1.5 mm, pin diameter 9.5 mm, wire hole diameter 1.2 mm.
*   **C Battery:** Diameter 26.2 mm, height 50.0 mm (including a 1.5 mm pin), wall thickness 1.5 mm, pin diameter 7.5 mm, wire hole diameter 1.2 mm.
*   **AA Battery:** Diameter 14.5 mm, height 50.5 mm (including a 1.0 mm pin), wall thickness 1.2 mm, pin diameter 5.5 mm, wire hole diameter 1.2 mm.
*   **AAA Battery:** Diameter 10.5 mm, height 44.5 mm (including a 0.8 mm pin), wall thickness 1.0 mm, pin diameter 3.8 mm, wire hole diameter 1.2 mm.

The zip tie holders should be rectangular cutouts.

I'd like to be able to select the battery type from a dropdown, and be able to turn the zip tie holder on or off. If turned on, then the width and offset parameters should become active.

It should be easy to call this module with all the different battery parameters (including custom parameters). Could you also provide a demo example with all the batteries side by side?

I think that's all.

[Assistant]
battery.scad:
```
////////////////////////////////////////////////////////////////////////////////
//
// Dummy Battery
//
////////////////////////////////////////////////////////////////////////////////

// battery type (for the 'Generic' type set the values in the 'Generic Battery' section below)
battery_type    = "AA" ; // [AAA,AA,C,D,Generic]

// add zip tie holder
zip_tie = "On" ; // [On,Off]

/* [Settings for ZipTie (mm)] */
// width of zip tie hole
zipTieWidth = 8 ;
// offset from top/bottom
zipTieOffset = 10 ;

/* [Settings for Generic Battery (mm)] */
// diameter of the body
body_diameter   = 14.5 ; // [8:0.1:40]
// height of body plus pin
body_height     = 50.5 ; // [35:0.1:60]
// width of the body wall
body_wall_width =  1.2 ; // [0.7:0.1:5]
// diameter of the pin
pin_diameter    =  5.5 ; // [3:0.1:12]
// height of the pin
pin_height      =  1.0 ; // [0.7:0.1:2.5]
// diameter of the holes for the wire
wire_diameter   =  1.2 ; // [0.7:0.1:2]

////////////////////////////////////////////////////////////////////////////////

/* [Hidden] */

// column index into Batt
cName        = 0 ; // name
cDiameter    = 1 ; // body diameter
cHeight      = 2 ; // body height (incl pin height)
cWidth       = 3 ; // wall width
cPinDiameter = 4 ; // pin diameter
cPinHeight   = 5 ; // pin width
cWire        = 6 ; // wire diameter

/*
  Dimensions:

  D   http://data.energizer.com/pdfs/e95.pdf
      http://professional.duracell.com/downloads/datasheets/product/Industrial/Industrial%20D.pdf
  C   http://data.energizer.com/pdfs/e93.pdf
      http://professional.duracell.com/downloads/datasheets/product/Industrial/Industrial%20C.pdf
  AA  http://data.energizer.com/pdfs/e91.pdf
      http://professional.duracell.com/downloads/datasheets/product/Industrial/Industrial%20AA.pdf
  AAA http://data.energizer.com/pdfs/l92.pdf
      http://professional.duracell.com/downloads/datasheets/product/Industrial/Industrial%20AAA.pdf
*/

// Note: Battery length is the height of the battery body and plus the pin height.
Batt =
[ // name, diam, heig, wid, pinD, pinH, wire
  [ "D"  , 34.2, 61.5, 1.5,  9.5,  1.5, 1.2 ],
  [ "C"  , 26.2, 50.0, 1.5,  7.5,  1.5, 1.2 ],
  [ "AA" , 14.5, 50.5, 1.2,  5.5,  1.0, 1.2 ],
  [ "AAA", 10.5, 44.5, 1.0,  3.8,  0.8, 1.2 ]
] ;

$fa =  5 ;
$fs =  0.4 ;

////////////////////////////////////////////////////////////////////////////////

module battery(batt, zipTieWidth=0, zipTieOffset=0)
{
  radius = batt[cDiameter] / 2 ;
  height = batt[cHeight] - batt[cPinHeight] ;

  difference()
  {
    union()
    {
      cylinder(h=height, r=radius); // outer
      translate([0,0,height-0.1])
      {
	cylinder(h=batt[cPinHeight]+0.1, d=batt[cPinDiameter]) ; // pin on top
      }
    }
    color("lightgreen")
    translate([0, 0, batt[cWidth]]) { cylinder(h=height-2*batt[cWidth], r=radius-batt[cWidth]) ; }  // inner

    // (+) wire
    color("red")
    {
      offsetTop1 = (batt[cPinDiameter] + 2*batt[cWire]) / 2 ;
      offsetTop2 = batt[cDiameter]/2 - batt[cWidth] - 0.5*batt[cWire] ;
      offsetTop = offsetTop1 < offsetTop2 ? offsetTop1 : offsetTop2 ;
      translate([ offsetTop, 0,height-1.5*batt[cWidth]]) { cylinder(h=2*batt[cWidth]+batt[cPinHeight], d=batt[cWire]) ; }
      translate([-offsetTop, 0,height-1.5*batt[cWidth]]) { cylinder(h=2*batt[cWidth]+batt[cPinHeight], d=batt[cWire]) ; }
    }

    // (-) wire
    color("black")
    {
      offsetBottom = (batt[cDiameter]/2 - batt[cWidth]) * 2 / 3 ;
      translate([ offsetBottom, 0, -batt[cWidth]/2]) { cylinder(h=2*batt[cWidth], d=batt[cWire]) ; }
      translate([-offsetBottom, 0, -batt[cWidth]/2]) { cylinder(h=2*batt[cWidth], d=batt[cWire]) ; }
      translate([ 0, offsetBottom, -batt[cWidth]/2]) { cylinder(h=2*batt[cWidth], d=batt[cWire]) ; }
      translate([ 0,-offsetBottom, -batt[cWidth]/2]) { cylinder(h=2*batt[cWidth], d=batt[cWire]) ; }
    }

    color("orange")
    hull() // cut-out
    {
      translate([-radius, radius, radius+2*batt[cWidth]])
        rotate([90,0,0])
        cylinder(h=2*radius, r=radius) ;
      translate([-radius, radius, height-radius-2*batt[cWidth]])
        rotate([90,0,0])
        cylinder(h=2*radius, r=radius) ;
    }
  }

  if (zipTieWidth > 0)
  {
    intersection()
    {
      cylinder(h=height, r=radius);

      color("lightblue")
      difference()
      {
        dL = zipTieOffset ;
        cL = zipTieWidth ;

        w = batt[cWidth] ;
        l = cL + 2*w ;
        h = radius ;

        union()
        {
          translate([h/2+w, 0, l/2+dL]) cube([h, 2*w, l], center=true) ;
          translate([h/2+w, 0, batt[cHeight]-l/2-dL]) cube([h, 2*w, l], center=true) ;
        }

        union()
        {
          translate([3*w, 0, l/2+dL]) cube([2*w, 3*w, cL], center=true) ;
          translate([3*w, 0, batt[cHeight]-l/2-dL]) cube([2*w, 3*w, cL], center=true) ;
        }
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

module batteryType(battery_type, body_diameter, body_height, body_wall_width, pin_diameter, pin_height, wire_diameter, zipTieWidth, zipTieOffset)
{
  idx = search([battery_type], Batt) ;
  if (idx != [[]])
  {
    battery(Batt[idx[0]], zipTieWidth, zipTieOffset) ;
  }
  else if (battery_type == "Demo")
  {
    iD   = search(["D"  ], Batt) ;   rD   = Batt[iD  [0]][cDiameter] / 2 ;
    iC   = search(["C"  ], Batt) ;   rC   = Batt[iC  [0]][cDiameter] / 2 ;
    iAA  = search(["AA" ], Batt) ;   rAA  = Batt[iAA [0]][cDiameter] / 2 ;
    iAAA = search(["AAA"], Batt) ;   rAAA = Batt[iAAA[0]][cDiameter] / 2 ;

    dD   =   rD                              ; translate([-rD  , dD  , 0]) batteryType("D"  , zipTieWidth=10, zipTieOffset=10) ;
    dC   = 2*rD +   rC                  + 10 ; translate([-rC  , dC  , 0]) batteryType("C"  ) ;
    dAA  = 2*rD + 2*rC +   rAA          + 20 ; translate([-rAA , dAA , 0]) batteryType("AA" , zipTieWidth=8, zipTieOffset=6) ;
    dAAA = 2*rD + 2*rC + 2*rAA +   rAAA + 30 ; translate([-rAAA, dAAA, 0]) batteryType("AAA") ;
  }
  else
  {
    battery(["Generic", body_diameter, body_height, body_wall_width, pin_diameter, pin_height, wire_diameter]) ;
  }
}

////////////////////////////////////////////////////////////////////////////////

ztw = (zip_tie == "On") ? zipTieWidth  : 0 ;
zto = (zip_tie == "On") ? zipTieOffset : 0 ;

rotate([0,0,180])
rotate([0, 90, 0])
batteryType(battery_type, body_diameter, body_height, body_wall_width, pin_diameter, pin_height, wire_diameter, ztw, zto) ;

////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////

```


[User]
Hey, I'm looking to get a cable protector designed in OpenSCAD, something similar to what I have already but with a few specific requirements and a more robust approach to the math.

I'm providing the existing code as a starting point, though I don't need it to be a direct translation (though it might help as a reference for the basic design). The core concept is a modular cable protector that can flex, made up of repeating "links." Here's a summary of what I'm looking for:

**Core Design:**

*   **Modular Links:** The protector should be made of individual link pieces that can connect together to follow a curved path.
*   **Channel:** Each link should have a channel or groove to contain the cable. The channel needs to have a configurable width and height.
*   **Ramped Approach:** The entry/exit to the channel should be ramped, meeting the ground at a specified approach angle (like a speed bump). This angle is pretty key for ADA compliance.
*   **Connector System:** The links should connect via a robust mechanism -- something that makes a smooth, low-profile join. I'm not sure exactly what that would look like best, maybe a rounded-edge cup and ball style joint? Maybe something else if you have a good idea.
*   **Parametric Design:** It needs to be fully parametric so all the critical dimensions (channel width, channel height, approach angle, bridge thickness, part tolerance) can be easily adjusted. This is critical.
*   **Three Parts:** I'd like to be able to print three distinct parts - the "base" (which is the ramped part that also includes the channel), the "connector" (the part that provides the joining mechanism for links), and an assembly version where both are present.
*   **Robust Math:** I noticed that the existing code had some stability issues with very low approach angles, and I would like a more mathematically sound approach that can handle a wide range of angle values.  Specifically, please ensure the masked cylinder used to create the ramps is fully contained so that it does not have a negative radius or otherwise break the math.

**Specific Parameters (should be customizable):**

*   `approach_angle`: Angle of approach of the ramp. 12 degrees is ideal to stay ADA-compliant, but allow for higher values.
*   `channel_height`: Height of the cable channel.
*   `channel_width`: Width of the cable channel.
*   `bridge_thickness`: Thickness of the material over the cable channel itself.
*   `tolerance`: Tolerance for the connecting parts.

**Additional Notes:**

*   I'm hoping for a design that prioritizes both functionality and aesthetics; it should look clean and well-engineered.
*   I'd like to get the design in multiple separate `.scad` files: one for the base, one for the connector, and one for the "both" assembly.  I do not need to use any libraries.

Would you be able to create an OpenSCAD design based on these specifications?

[Assistant]
CM_link.scad:
```
// "Linkable" Floor Cable Protector
// variables for channel height and width.
// ADA compliance requires steepest approach to be 12 deg.  This determines width.

// Angle of attack, ADA requires steepest approach to be 12 degrees.  Also determines width.
AA=30; //[10,12,15,20,25,30]
// Height of channel, helps determine external height and width of chain
channel_height= 13; // [1:50]
// Width of channel, use narrower than a maximum (as determined by channel_height)
channel_width= 13; // [1:50]
// Thickness of bridge
bridge=2; //[1:5]
// Tolerance for mating parts
tolx=0.25; // [0:0.1:3]
// Which parts to print?
part = "both"; // [first:disk,second:linker,both:both]

print_part();

module print_part() {
	if (part == "first") {
		circle();
	} else if (part == "second") {
		link();
	} else if (part == "both") {
		both();
	} else {
		both();
	}
}

$fa=1;

// Perform cylinder calculations
cyl_rad=((channel_height+bridge)*cos(AA))/(1-cos(AA));
overlook=cyl_rad*sin(AA);

module track(){
    difference(){
        translate([0,0,-(cyl_rad-channel_height-bridge)])rotate([0,90,0])cylinder(h=overlook*2,r=cyl_rad);
        union(){
            translate([-tolx,-cyl_rad,-2*cyl_rad])cube([2*overlook+2*tolx,2*cyl_rad+tolx,2*cyl_rad]);
            translate([-tolx,-channel_width/2,-tolx])cube([2*overlook+2*tolx,channel_width,channel_height+tolx]);
        }
    }
}

module circle(){
    intersection(){
        track();
        translate([overlook,0,-tolx])cylinder(h=overlook,r=overlook);
   }
}

module cup(){
    difference(){
        track();
        union(){
            translate([1.5*overlook+2*tolx,0,-tolx])cylinder(h=overlook,r=overlook+tolx);
            translate([1*overlook+channel_width,-overlook-channel_width,-tolx])cube([2*(overlook+channel_width),2*(overlook+channel_width),overlook]);
        }
    }
}

module link(){
    translate([-overlook/2-3*bridge,0,0]) union(){
        cup();
        mirror([1,0,0])cup();}
}

module both(){
    circle();
    link();
}

```


[User]
I'm looking to create a design similar to the "Trapped Sphere" model I saw on Thingiverse, but I need a version that is entirely parametric and customizable through OpenSCAD. It's got a rounded cube with a sphere trapped inside.

Here's the breakdown of the key features and parameters:

1.  **Overall Scale:** I need a global scale factor to easily adjust the entire model's size proportionally.

2.  **Resolution:** I'd like to control the smoothness of the curves, using the `$fn` variable.

3.  **Cube:**
    *   The base shape is a cube.
    *   I need a `cubeSize` parameter to adjust the cube's dimensions.
    *   The edges of the cube should be rounded with a `rds` (round radius) parameter.

4.  **Holes:**
    *   There should be three intersecting cylindrical holes that pass through the center of the cube.
    *   The diameter of these holes should be controlled by a `hole` parameter.

5.  **Trapped Sphere:**
    *   There should be a sphere inside the cube.
    *   The sphere's size should be controlled by a `ball` parameter.
    *   The sphere should be free to move within the cube, not fused in any way to the cube.
    *   The sphere should be inserted into the cube via a larger hole to allow its placement before it is "trapped" - this hole should have its diameter controlled by a `sphereHole` parameter.

I'd like the output to be an OpenSCAD file (or files, if it makes sense to use modules in separate files). The colors are not critical. It's important to have all the parameters defined in a way that is easily customizable from within the OpenSCAD customizer. I don't have any files I can provide; I am expecting you to generate all the necessary code. Thanks!

[Assistant]
CustomizableTrappedSphereR.scad:
```

/* [Trapped Sphere] */

// Scales the whole thing by number
Scale = 1;

// Resolution
$fn = 100;

// Cube
cubeSize = 20;

// Round radius
rds=1;

// Hole diameter (doesn't work if too small)
hole = 13.5;

// ball size
ball = 20;

// Sphere Hole size
sphereHole = 22;
// Rounded Cube Module //
// Created by @skitcher //

  module rCube(x,y,z,rd,fn){
    $fn=fn;

    hull(){
        translate([rd,rd,rd]) sphere(r=rd);
        translate([x-rd,rd,rd]) sphere(r=rd);
        translate([rd,y-rd,rd]) sphere(r=rd);
        translate([rd,rd,z-rd]) sphere(r=rd);

        translate([x-rd,y-rd,rd]) sphere(r=rd);
        translate([x-rd,rd,z-rd]) sphere(r=rd);
        translate([x-rd,y-rd,z-rd]) sphere(r=rd);

        translate([rd,y-rd,z-rd]) sphere(r=rd);
    }
}

module TrappedCube(){
    //Cube with holes
    difference(){
        //cube
        color("lightgrey") rCube(cubeSize*Scale,cubeSize*Scale,cubeSize*Scale,rds,$fn);

        //cylinder 1
        color("lightgreen") translate([cubeSize/2*Scale, cubeSize/2*Scale, -1]) cylinder(r=hole/2*Scale, h=cubeSize*Scale+2);

        //cylinder 2
       color("lightgreen")  translate([cubeSize/2*Scale, -1, cubeSize/2*Scale]) rotate([-90, 0, 0]) cylinder(r=hole/2*Scale, h=cubeSize*Scale+2);

        //cylinder 3
         color("lightgreen") translate([-1, cubeSize/2*Scale, cubeSize/2*Scale]) rotate([0, 90, 0]) cylinder(r=hole/2*Scale, h=cubeSize*Scale+2);

        //sphereHole
        color("lightblue") translate([cubeSize/2*Scale, cubeSize/2*Scale, cubeSize/2*Scale]) sphere(sphereHole/2*Scale);
    }

    //The ball
    color("white") translate([cubeSize/2*Scale, cubeSize/2*Scale, cubeSize/2*Scale]) sphere(ball/2*Scale);
}

TrappedCube();
```


[User]
Hey! I'd love to create a customizable origami dove similar to the one described on Thingiverse, but I'd like to make a few alterations and ensure it's completely parametric. I need a design that I can control through parameters for size, thickness, and the like.

Essentially, I'm looking for a 2D design of a dove that I can adjust using OpenSCAD. The core shape is defined by an outer outline and some inner cutouts, just like in the provided description. I want to be able to change the following:

*   **Overall Size:** I need a `max_size` parameter to control the longest dimension of the design. The other dimension should scale proportionally.
*   **Model Height:** A `model_height` parameter to set the thickness of the extruded 2D shape.
*   **Outline Thickness:** I need a way to control the thickness of the outer edge with an `outline_size` parameter.
*   **Inner Cutout Thickness:** I also need to control the thickness of the lines in the inner cutouts with an `inline_size` parameter.
*   **Inner Cutout Edge Radius:** For a softer look, I'd like to control the roundness of the inner cutout corners with an `inline_edge_radius` parameter.
*   **Flip Option:** I need a `flip_model` parameter to mirror the design.

I'm providing the existing coordinates in the attached files. These define the outer polygon and the inner cut polygons, and I need to make sure that the relative placement of the cutouts is properly maintained through all of the various parameter changes. I think the existing design already makes good use of the `offset` operation. I'm looking for the AI to generate the whole SCAD code, not just parts of it.

I'm not really worried about the exact values for each of those parameters initially. I just need the code to exist such that I can change the value of the parameters to get the exact desired output. The actual values are going to be dependent on material availability.

Thanks in advance!

[Assistant]
customizable_origami_dove.scad:
```
/*
 * Customizable Origami - Dove - https://www.thingiverse.com/thing:2651960
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-11-17
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

// preview[view:north, tilt:top]

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.8; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.7; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.9; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [262, 398];
A2 = [298, 365];
A3 = [338, 358];
A4 = [395, 403];
A5 = [455, 328];
A6 = [651, 319];
A7 = [482, 463];
A8 = [515, 591];
A9 = [416, 686];
A10 = [432, 526];
A11 = [344, 472];
A12 = [311, 398];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12];

// Inner Points

// Polygons
C1 = [A1, A2, A12];
C2 = [A3, A4, A11, A12, A2];
C3 = [A4, A5, A7, A10, A11];
C4 = [A5, A6, A7];
C5 = [A7, A8, A9, A10];

cut_polygons = [C1, C2, C3, C4, C5];

min_x = A1[0];
min_y = A6[1];
max_x = A6[0];
max_y = A9[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User] I'd like to create a customizable 2D origami-style Christmas tree for 3D printing, similar to the one described in the Thingiverse link. I want to be able to control its overall size, height, and the thickness of both the outer and inner outlines. Additionally, I need to be able to specify the roundness of the inner outline edges and flip the model if necessary. The design uses a series of points and polygons to define the tree's shape, so I'd expect that to be handled programmatically. Please generate the necessary OpenSCAD files. I don't have a preference for any external libraries, so use whichever would be best suited for this project.

[Assistant]
customizable_origami_christmas_tree.scad:
```
/*
 * Customizable Origami - Christmas Tree - https://www.thingiverse.com/thing:2654198
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-11-18
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

// preview[view:north, tilt:top]

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.5; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.7; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.1; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [679, 24];
A2 = [797, 369];
A3 = [780, 368];
A4 = [756, 363];
A5 = [907, 672];
A6 = [883, 672];
A7 = [832, 672];
A8 = [1018, 969];
A9 = [981, 971];
A10 = [989, 983];
A11 = [914, 982];
A12 = [1126, 1265];
A13 = [1068, 1267];
A14 = [1090, 1299];
A15 = [1003, 1293];
A16 = [1233, 1557];
A17 = [1151, 1558];
A18 = [1194, 1615];
A19 = [1106, 1607];
A20 = [1339, 1845];
A21 = [1221, 1847];
A22 = [1300, 1942];
A23 = [1009, 1896];
A24 = [1063, 2026];
A25 = [690, 1846];
A26 = [312, 2026];
A27 = [360, 1898];
A28 = [70, 1944];
A29 = [147, 1846];
A30 = [29, 1844];
A31 = [261, 1610];
A32 = [176, 1620];
A33 = [216, 1558];
A34 = [134, 1557];
A35 = [362, 1293];
A36 = [276, 1299];
A37 = [299, 1266];
A38 = [239, 1265];
A39 = [452, 981];
A40 = [384, 983];
A41 = [391, 970];
A42 = [354, 968];
A43 = [529, 672];
A44 = [486, 672];
A45 = [460, 670];
A46 = [604, 368];
A47 = [585, 369];
A48 = [571, 369];

outline = [A1, /*A2, A3, A4,*/ A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, /*A46, A47, A48*/];

// Inner Points
B1 = [378, 1670];
B2 = [484, 1627];
B3 = [349, 1600];
B4 = [523, 1306];
B5 = [447, 1325];
B6 = [419, 1289];
B7 = [485, 983];
B8 = [508, 994];
B9 = [564, 986];
B10 = [607, 669];
B11 = [552, 672];
B12 = [611, 366];
B13 = [634, 360];
B14 = [645, 367];
B15 = [720, 364];
B16 = [742, 364];
B17 = [761, 673];
B18 = [800, 674];
B19 = [814, 674];
B20 = [572, 674];
B21 = [881, 976];
B22 = [863, 995];
B23 = [802, 990];
B24 = [926, 1323];
B25 = [836, 1305];
B26 = [949, 1286];
B27 = [992, 1667];
B28 = [1019, 1597];
B29 = [883, 1630];

// Polygons
C1 = [A1, A5, A6];
C2 = [A1, A6, A7, B19, A9, A10, A11, B21, A13, A14, A15, B26, A17, A18, A19, B28, A21, A22, A23, B29, B27, B25, B24, B23, B22, B17, B18];
C3 = [A1, B18, B17, B22, B23, B24, B25, B27, B29, A23, A24, A25];
C4 = [A7, A8, A9, B19];
C5 = [B21, A11, A12, A13];
C6 = [A15, A16, A17, B26];
C7 = [A19, A20, A21, B28];
C8 = [B2, A27, A28, A29, B3, A31, A32, A33, B6, A35, A36, A37, B7, A39, A40, A41, B11, A43, A44, A1, B20, B10, B8, B9, B5, B4, B1];
C9 = [A29, A30, A31, B3];
C10 = [A34, A33, B6, A35];
C11 = [A38, A37, B7, A39];
C12 = [A41, A42, A43, B11];
C13 = [A1, A44, A45];
C14 = [A25, A26, A27, B2, B1, B4, B5, B9, B8, B10, B20, A1];

cut_polygons = [C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14];

min_x = A30[0];
min_y = A1[1];
max_x = A20[0];
max_y = A24[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
I'd like to design some wine charms using OpenSCAD. I've got a basic idea in mind but need some help with the code.

Essentially, I want to create circular charms with a personalized name tag attached. The charm itself should have a ring shape with a small tab that connects it to the text portion. I'd like to be able to input a list of names, and the script should automatically arrange these charms in a grid layout.

Here are the key parameters I'd like to control:

*   **`name_input`**: A comma-separated string of names to be used on the charms.
*   **`font`**:  The font to use for the names. I'd like to be able to specify both the font family (like Arial or Helvetica) and variations such as "Bold Italic" if supported.
*   **`charm_thickness`**: The overall thickness of the charm.
*   **`text_size`**: The size of the text on the name tag.
*   **`hoop_size`**: The diameter of the ring portion of the charm.
*  **`x_multiple`**: A multiplier to adjust the width of the charms to prevent overlap. This is especially important for longer names and needs to adjust the overall width of the name tag.
*   **`split_character`**:  The character to use when splitting the `name_input` string, typically a comma.
*   **`thickness_ratio`**: The ratio between the thickness of the base ring and the thickness of the name part and tab. This will allow them to be at slightly different heights.
*  **`outline_ratio`**: The ratio to determine the size of the outline or offset around the text in the name tag, I'd like this to be a sort of backing plate.
*   **`detail_size`**: The size of small details like the thickness of the ring and the end nubs on the ring.
*   **`y_space`**: The spacing between the rows of the grid layout.

The design should include:

*   A ring-shaped "hoop" with a small tab extending inwards for attaching to the text. This ring should be smooth (a simple torus) and have small nubs on the ends.
*  A name-tag portion with the name, also have a slightly larger "backing plate" behind the text that extends slightly.
*  The charms should be arranged in a grid format on the output.
*   The code needs to handle any length of names and arrange them correctly on the grid.

I'm not too familiar with some of the advanced techniques, so I’d like the code to be well-commented and easy to follow. I would like the files to be broken up into modules so that they can be easily reused.

Also, I'm hoping you can avoid using any third-party libraries like MCAD, BOSL, etc. The goal is to keep the solution simple and self-contained using just the core OpenSCAD language. I'm not sure if that makes things harder, but I appreciate it.

[Assistant]
wine-charms.scad:
```
// (C) Paul Oswald 2017
//	v1.0 First release

// Makerbot Customizer
// preview[view:south, tilt:top diagonal]

// Convert an array of strings into an array of string lengths
function lengths(v) = [for( i=v ) len(i)];

// Return a substring
function substr(string, start, end, p="") =
    (start >= end || start >= len(string))
        ? p
        : substr(string, start + 1, end, str(p, string[start]));

// Split string on match_value and return an array
function split(match_value, string, p=[]) =
    let(x = search(match_value, string))
    x == []
        ? concat(p, string)
        : let(i=x[0], l=substr(string, 0, i), r=substr(string, i+1, len(string)))
                split(match_value, r, concat(p, l));

// A comma-separated list of names or words to use on the tags
name_input = "Artistic,Clever,Playful,Reliable,Practical,Inventive,Sexy,Rational,Beautiful,Handsome,Ambitious,Athletic";

// Select from basic fonts such as Helvetica or Google fonts (http://www.google.com/fonts). Font variations can be selected by adding a style after the font (example: "Liberation Sans:style=Bold Italic" would make it bold and italic)
font = "Arial Unicode MS";

// Z height above the print bed. This affects the number of layers.
charm_thickness = 2;

// Text size (height above the baseline) 10 would give 1 cm high capital letters
text_size = 5; // [1:1:20]

// Diameter of the hoop
hoop_size = 20; // [5:0.5:200]

// The width of the charms depends on the text length and the letters in the word. This can't be calculated directly. Use the following to adjust to make sure tags do not touch each other when using your chosen font.
x_multiple = 1.5; // [0.1:0.05:5]

// The character to use for splitting the name_input string
split_character = ",";

// Ratio between lower and upper pieces
thickness_ratio = 0.5; // [0:0.1:1]

// Ratio of text size to outline
outline_ratio = 0.2; // [0.1:0.1:1]

// Size of some smaller details like the hoop thickness
detail_size = 1.5; // [1:0.1:10]

// y-spacing between charms
y_space = 2;

/* [Hidden] */

names = split(split_character, name_input);

// calculate a width constant based on the longest name
name_lengths = lengths(names);
max_name_length = max(name_lengths);
echo("Longest name", max_name_length);

y_spacing = max(text_size + 2 * (text_size * outline_ratio), hoop_size) + y_space; // to get correct spacing between tags
x_spacing = hoop_size + text_size * (max_name_length / x_multiple);

// Rendering facets
$fn = 32;

module ring(inner_radius, outer_radius, thickness) {
    linear_extrude(height=thickness * thickness_ratio) {
        difference() {
            circle(r=outer_radius);
            circle(r=inner_radius);
        }
    }
}

// The name-tag part
module nametag(height, thickness, name) {
    linear_extrude(height=thickness * thickness_ratio) {
        offset (r=text_size * outline_ratio) text(name, font=font, size=text_size, valign="center");
    }
    color("DarkOrange") {
        linear_extrude(height=thickness) {
            text(name, font=font, size=text_size, valign="center");
        }
    }
}

module hoop(height, thickness) {
    offset_radius = detail_size;

    // create a tab to ensure a solid connection with the text
    width = text_size; // how far into the text should it go?
    tab_width = width + 2 * offset_radius;
    translate([tab_width / 2, 0 ,0]) {
        linear_extrude(height=thickness * thickness_ratio) {
            offset(r=2) {
                square([width, text_size/3], center=true);
            }
        }
    }

    ring_r = height/2;
    translate([-ring_r + offset_radius, 0, 0]) {
        union() {
            render(convexity = 10) // fixes a bug in display of the next difference command
            difference() {
                ring(ring_r - offset_radius, ring_r, thickness);
                linear_extrude(height=thickness * thickness_ratio) {
                    square([9999, offset_radius], center=true);
                }
            }
            // make end nubs
            translate([-ring_r + offset_radius/2, offset_radius * 1.25, 0]) {
                linear_extrude(height=thickness * thickness_ratio) {
                    circle(r=offset_radius);
                }
            }
            translate([-ring_r + offset_radius/2, -offset_radius * 1.25, 0]) {
                linear_extrude(height=thickness * thickness_ratio) {
                    circle(r=offset_radius);
                }
            }
        }
    }
}

// A single wine glass tag
module charm(height, thickness, name) {
    nametag(height, thickness, name);
    hoop(hoop_size, thickness);
}

// figure out how many columns we need to make a grid
divider = ceil(sqrt(len(names)));
echo("Names", len(names));
echo("Columns", divider);
echo("---");

for (i = [0 : len(names) - 1]) {
    name = names[i];
    curr_column = floor(i / divider);
    curr_row = i % divider;
    echo(name, curr_column, curr_row);
    translate([curr_column * x_spacing, curr_row * y_spacing, 0]) {
        charm(text_size, charm_thickness, name);
    }
}

```


[User]
Hi, I'd like to design a parametric ink cartridge holder, specifically for Lamy or Parker cartridges. It should have a cylindrical shape for each cartridge, and the number of cartridges it holds should be customizable. I'd like the design to have a base with individual cutouts for each cartridge, and a separate top that fits over the base. Each cutout should have a 'window' or gap on the side of the cylinder so you can easily see if a cartridge is in the chamber without taking the top off.

For the base, I envision a series of connected, individual cylindrical cutouts, each designed to hold one cartridge. Each cylinder should have a cutout on the side, so we can see inside to check if a cartridge is present. For the top, I’d like a series of matching cylindrical shapes that can fit snugly into the base's cutouts, creating a cover. The cylinders in the top and base shouldn't be the same diameter - the top’s cylinders should have a slightly smaller diameter so that it can be pushed into the base.

Could you create a parametric OpenSCAD file for this? I'd like to be able to specify the number of cartridges the holder should accommodate. No specific libraries need to be used.

[Assistant]
Lamy_Ink_Cartridge_Container_parametric.scad:
```
/* ********************
//
// Lamy Ink Cartridge Holder - Container z.B. für Lamy Tintenpatronen
// CC-BY-SA November 2017 by ohuf@Thingiverse
//
// www.thingiverse.com/thing:2654631

// Version 2 - 2017-11-17
//
//
// License: CC-BY-SA
// read all about it here: http://creativecommons.org/licenses/by-sa/4.0/
// ********************** */

/* [Anzahl] */
//Anzahl der Kammern || Number of cartridge chambers
amount=2;	// [1:30]

/* [Hidden] */
$fn=150;
ix=0.005;	// This is just some static value to beautify the cutout

sl=0.3;		// static value to widen concave walls

//// === Unterteil: ===
for(a = [1:amount] )
{
	translate([a*10.3, 0, 0])
	bottom_cutout_round();
}
//// === Deckel: ===
translate([0, 15, 0])
for(a = [1:amount] )
{
	translate([a*10.3, 0, 0])
	top_round();
}

//// ================================
//// runde Variante (Unterteil):
module bottom_cutout_round(){
	difference(){
		difference(){
			color("red")
			union(){
			translate([0,0,0])
				cylinder(d=9, h=60);
				cylinder(d=11, h=53);
			}

			translate([0, 0, 1])
			union(){
				cylinder(d=7.7, h=59 + ix );
			}
		}

		//// Cutout im Body:
		union(){
		translate([0,0,45])
			rotate([90,0,0])
				cylinder(d=5, h=40, center=true);
		translate([0,0,25])
			rotate([90,0,0])
				cylinder(d=5, h=40, center=true);
		translate([-2.5,-20,25])
			cube([5,40,20]);
		}
	}
}

//// Runder Deckel:
module top_round(){
	adj2=0.15;
	difference(){
		color("green")
		cylinder(d=11, h=22);
		translate([0,0,1.0])
			cylinder(d=9.0+adj2, h=21+ix);
	}
}

```


[User]
I'd like to create a customizable 2D Moravian star design in OpenSCAD, similar to the one I saw on Thingiverse ([https://www.thingiverse.com/thing:2655524](https://www.thingiverse.com/thing:2655524)).

The design should be parametric, allowing me to adjust:

*   **Overall Size:** The maximum size (longest side) of the star, with the other side resizing proportionally to maintain the aspect ratio. I'd like to set this in millimeters, let's say with a default of 120mm.
*   **Height:** The thickness or height of the extruded model, also in millimeters, with a default of 5mm.
*   **Outline Thickness:** The thickness of the outer border of the star, with a default of 1.4mm.
*  **Inner Outline Thickness:**  The thickness of the inner lines that form the star pattern, with a default of 0.7mm.
*  **Inner Outline Rounding:** The radius of the rounding on the ends of the inner outline segments, with a default of 0.1mm.
*   **Flip Option:** A toggle to flip the design horizontally (mirror along the Y axis).

The star's basic geometry is defined by a series of points (A1-A40) for the outer shape, and other points (B1-B12) for the inner shapes. These points are used to define polygons which are then used for creating the shape. I think that the core logic of offsetting and generating the outer and inner outlines should be separated into a function so I could adjust that easier for different designs in the future.

Could you create the OpenSCAD code that achieves this, and uses the listed variables? I won't be using any external libraries here. I would like that the model should automatically rescale according to the longest side.

[Assistant]
customizable_origami_moravian_star.scad:
```
/*
 * Customizable Origami - Moravian Star - https://www.thingiverse.com/thing:2655524
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-11-19
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

// preview[view:north, tilt:top]

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.4; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.7; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.1; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [136, 161];
A2 = [325, 307];
A3 = [280, 211];
A4 = [458, 405];
A5 = [496, 15];
A6 = [535, 390];
A7 = [712, 197];
A8 = [660, 309];
A9 = [855, 154];
A10 = [675, 376];
A11 = [786, 336];
A12 = [641, 459];
A13 = [783, 453];
A14 = [732, 472];
A15 = [983, 499];
A16 = [735, 534];
A17 = [828, 569];
A18 = [627, 554];
A19 = [708, 632];
A20 = [659, 619];
A21 = [831, 824];
A22 = [685, 718];
A23 = [770, 871];
A24 = [533, 610];
A25 = [496, 949];
A26 = [459, 610];
A27 = [227, 867];
A28 = [311, 716];
A29 = [165, 824];
A30 = [329, 626];
A31 = [282, 644];
A32 = [369, 555];
A33 = [163, 575];
A34 = [251, 539];
A35 = [11, 518];
A36 = [276, 475];
A37 = [214, 454];
A38 = [357, 460];
A39 = [217, 331];
A40 = [306, 366];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40];

// Inner Points
B1 = [496, 244];
B2 = [496, 476];
B3 = [559, 536];
B4 = [434, 536];
B5 = [388, 487];
B6 = [454, 419];
B7 = [540, 419];
B8 = [591, 553];
B9 = [401, 554];
B10 = [496, 710];
B11 = [496, 873];
B12 = [609, 488];

// Polygons
C1 = [A5, A6, B7, B1, B6, A4];
C2 = [A24, A25, A26, B10];
C3 = [A26, B11, B10];
C4 = [A24, B11, B10];
C5 = [B10, A26, B4, B2];
C6 = [A24, B3, B2, B10];
C7 = [B1, B7, B2];
C8 = [B2, B6, B1];
C9 = [A6, A7, A8];
C10 = [A6, A8, A9, B7];
C11 = [A9, A10, B7];
C12 = [B7, A10, A11, B2];
C13 = [B2, A11, A12, B12, B3];
C14 = [A12, A13, A14];
C15 = [A12, A14, A15, B12];
C16 = [B12, A15, A16];
C17 = [B12, A16, A17, B3];
C18 = [B3, A17, A18, B8];
C19 = [A18, A19, A20, B8];
C20 = [B8, A20, A21];
C21 = [B8, A22, A21];
C22 = [A22, A23, B3, B8];
C23 = [B3, A23, A24];
C24 = [A26, A27, B4];
C25 = [A27, A28, B9, B4];
C26 = [B9, A28, A29];
C27 = [A29, A30, B9];
C28 = [A30, A31, A32, B9];
C29 = [B4, B9, A32, A33];
C30 = [A33, A34, B5, B4];
C31 = [A34, A35, B5];
C32 = [A35, A36, A38, B5];
C33 = [A36, A37, A38];
C34 = [A39, A38, B5, B4, B2];
C35 = [A39, A40, B6, B2];
C36 = [A40, A1, B6];
C37 = [A1, A2, B6];
C38 = [A3, A2, B6, A4];

cut_polygons = [C1, C2, /*C3, C4,*/ C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C18, C19, C20, C21, C22, C23, C24, C25, C26, C27, C28, C29, C30, C31, C32, C33, C34, C35, C36, C37, C38];

min_x = A35[0];
min_y = A5[1];
max_x = A15[0];
max_y = A25[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
Hey there, I'm looking to get a custom filament spool holder designed in OpenSCAD. I currently have a simple design that works for eSun spools with 5mm bearings and an M5 bolt, but I'd like to make it parametric so that it's easier to adjust for different spools and such. My current design is pretty basic and just involves a cylinder with two bearing holes and a center hole through the middle for the M5 bolt. I've attached my current design for reference, but I'd like to get a new parametric version created from scratch. It uses a couple of cylinders and differences to create the needed shape. The design currently includes a brim, so please carry that over.

My design uses the following dimensions:

*   **Brim Radius:** (Added to the spool hole radius) 2.5mm
*   **Brim Thickness/Height:** 2mm
*   **Spool Hole Radius:** 26.8mm
*   **Spool Hole Thickness/Depth:** 9mm
*   **Bearing Outer Radius:** 5.10mm
*   **Bearing Thickness:** 5mm
*   **Center Hole Size:** 3.5mm (for an M5 bolt)

I'm thinking the following parameters would be useful for the design:

*   `brim_radius`: The additional radius for the brim
*   `brim_thickness`: The thickness/height of the brim
*   `spool_hole_radius`: The radius of the spool hole
*   `spool_hole_thickness`: The thickness/depth of the spool hole
*   `bearing_outer_radius`: The outer radius of the bearing hole
*   `bearing_thickness`: The thickness of the bearing
*   `center_hole_size`: The size of the center bolt hole

I'd like for it to be a single piece that can be printed directly. Could you generate the OpenSCAD files for that? Also, I think I'd like to include some comments so I can easily understand it later, if that's okay.

[Assistant]
d_bot_spool_holder.scad:
```
//Spinder Holder w/ Bearings
//Default settings for eSun spools

//Number of Faces/How Smooth
$fn=360; //[15:360]

//Brim Radius
brim_radius = 2.5;
//Brim Thickness/Height
brim_thickness = 2;

//Spool Hole Radius
spool_hole_radius = 26.8;
//Spool Hole Thickness/Depth
spool_hole_thickness = 9;

//Bearing Insert Radius
bearing_outer_radius = 5.10;
//Bearing Insert Thickness
bearing_thickness = 5;

//Center Hole Size
center_hole_size=3.5; //Will fit an M5

module bearing_hole() {
    cylinder(h=(bearing_thickness+1),
            r=bearing_outer_radius);
}

module spool_holder() {
    cylinder(h=(brim_thickness+spool_hole_thickness),
        r=spool_hole_radius);
}

module brim() {
    cylinder(h=brim_thickness,
            r=(brim_radius+spool_hole_radius));
}

difference() {
    union() {
        spool_holder();
        brim();
    };

    translate([0,0,-1]) {
        bearing_hole();
    }

    translate([0,0,((brim_thickness+spool_hole_thickness)-bearing_thickness)]) {
        bearing_hole();
    }

    translate([0,0,1]) {
        cylinder(h=brim_thickness+spool_hole_thickness+1,
            r=center_hole_size);
    }
}

```


[User]
I need a parametric OpenSCAD design for a meat tenderizer head that can be attached to a hammer. I'm envisioning something like an accessory that fits over the existing hammer head.

Here's what I'm looking for in terms of parameters:

*   **`hammer_head_diameter`**: The diameter of the hammer head it will attach to (e.g., 26mm for an Ikea Fixa hammer).
*   **`hammer_head_length`**: The length of the hammer head.
*   **`wall_thickness`**: The thickness of the material making up the tenderizer.
*   **`spike_length`**: The length of the spikes on the tenderizer.
*   **`spikes_per_circle`**: The number of spikes in the outer circle.

The design should have a cylindrical base that fits snugly around the hammer head, with a lip extending past the end of the hammer to prevent the hammer from sliding out of the attachment. There should be two circles of spikes, one larger than the other. The smaller one should be on the interior, and if there isn't room for it, it doesn't have to be included. The base should be hollow on the inside to fit around the hammer head.

I'm not looking for a fancy design, just something functional.

I'm not sure which OpenSCAD libraries would be helpful, but if you have some advice for best practices there, I'm all ears! Also, I would appreciate if you keep it all in one .scad file if possible.

[Assistant]
meat_tenderizer.scad:
```
hammer_head_diameter = 26;
hammer_head_length = 15;
wall_thickness = 2; // [0:0.1:5]
spike_length = 3.4; // [0:0.1:10]
spikes_per_circle = 12; // [4:20]

/* [Hidden] */
item_diameter = hammer_head_diameter+wall_thickness*2;
$fn=128;

// Hammer base
translate([0,0,-hammer_head_length-wall_thickness])
    difference(){
        cylinder(d=item_diameter, h=hammer_head_length+wall_thickness);
        cylinder(d1=hammer_head_diameter*.95, d2=hammer_head_diameter, h=hammer_head_length*2, center=true);
        for (i=[1:3])
            rotate([0,0,i*(360/3)])
                cube([wall_thickness*2, hammer_head_diameter*2, hammer_head_length*2], center=true);
}

module spike(size) { cylinder(r1=size, r2=0, h=size, $fn=4); }

// Central spike
spike(spike_length);

// Inner spike circle, if there's a place for it
if (item_diameter > spike_length*8.5)
    for (i=[0:spikes_per_circle/2])
        rotate([0,0,i*(360/spikes_per_circle*2)])
            translate([item_diameter/4-spike_length/2,0,0])
                rotate([0,0,-i*(360/spikes_per_circle*2)])
                    spike(spike_length);

// Outer spike circle
for (i=[0:spikes_per_circle])
    rotate([0,0,i*(360/spikes_per_circle)])
        translate([item_diameter/2-spike_length, 0, 0])
            rotate([0,0,-i*(360/spikes_per_circle)])
                spike(spike_length);

```


[User]
I need a configurable power strip mount, something that can be screwed underneath a desk. I'm envisioning it will cover one end of the power strip, so I'll need to print two.

Here's what I'm thinking for the design:

**Basic Shape:**
*   The main part should be a box that fits over the end of the power strip, covering it partially. I need to be able to set the width and height of the power strip, and how much it's covered (the 'overlay' amount).
*   The mount should have a wall thickness parameter.

**Cable Management:**
*   I need a cable outlet on the bottom to guide the power cord. This should be an adjustable diameter. Also, the vertical offset of where the cable starts should be configurable.
*   I'd also like optional cable holders, one on the left and one on the right, to help manage cables plugged into the power strip. These should have a thickness, distance from the main body, and height.

**Cutouts for the Power Strip:**
*   There should be an optional round cutout, likely for the first power outlet. I need to be able to set its diameter and offset from the end of the socket.
*   Also, an optional rectangular cutout, for a power switch, with its width and offset settable.

**Mounting:**
*   Screw holes will be needed on both sides of the mount for attaching under a desk. I need to be able to set the diameter for the screw holes.

**Parameters:**
Here's a list of all the parameters I envision, just to make it super clear:
*   `socket_width`: Width of the power strip socket.
*   `socket_height`: Height of the power strip socket.
*   `overlay`: How much of the socket to cover.
*   `cable_holder_right`: Boolean for if the right cable holder is present.
*  `cable_holder_left`: Boolean for if the left cable holder is present.
*   `cable_holder_thickness`: Thickness of the cable holder.
*   `cable_holder_width`: Distance of the cable holder from the mount's main body.
*   `outlet_cut_round_offset`: Offset of the round outlet cut.
*   `outlet_cut_diameter`: Diameter of the round outlet cut.
*   `switch_cut_rect_offset`: Offset of the rectangular switch cut.
*   `switch_cut_rect_width`: Width of the rectangular switch cut.
*   `cable_top_offset`: Distance from the bottom of the socket to the cable outlet's top.
*   `cable_diameter`: Diameter of the cable outlet.
*   `thickness`: Wall thickness of the mount.
*   `screw_diameter`: Diameter of the screw holes.

I think that covers everything. Can you generate the OpenSCAD file(s)? I don't need to use any external libraries.

[Assistant]
Power_Strip_Mount.scad:
```
/* [Hidden] */
$fs=0.1;

/* [Global] */
// The width of your socket
socket_width=50;

// The height of your socket
socket_height=41;

// How far should your socket be covered in total?
overlay=20;

/* [Cable holder] */
// Cableholder on the left ?
cable_holder_right = true;

// Cable holder on th eright?
cable_holder_left = true;

// Thickness of the Cable holder
cable_holder_thickness = 6;

// Distance to cable holder
cable_holder_width = 10;

/* [Plug cuts] */
// Offset from the end of the socket to the beginning of the round cut
outlet_cut_round_offset=9;

// Diameter of the round cut (0=disabled) (Schuko=41)
outlet_cut_diameter=40;

// Offset from the end of the socket to the beginning of the rectangular cut
switch_cut_rect_offset=0;

// Diameter of the rectangular cut (0=disabled)
switch_cut_rect_width=0;

/* [Cable outlet cuts] */

// Distance from the bottom of the socket to the top of the cable
cable_top_offset=32;

// Diameter of the cable cut
cable_diameter=22;

/* [Other] */
// Wall thickness
thickness=3;

// Diameter of the screw holde
screw_diameter = 4;

outerwidth=socket_width+2*thickness;
outerheight=socket_height+thickness;
outerdepth=overlay+thickness;

cableholderheight = outerheight-cable_holder_width-thickness;

render () {
difference () {
    cube([outerdepth,outerwidth,outerheight]);
    translate([thickness,thickness,0])
        cube([overlay,socket_width,socket_height]);

    // cable outlet
    hull()
    {
        translate ([0,outerwidth/2,cable_top_offset-cable_diameter/2])
            rotate ([0,90,0])
                cylinder (h=thickness*2,r=cable_diameter/2,center=false);

        translate ([0,thickness+cable_diameter/2,0])
            rotate ([0,90,0])
                cylinder (h=thickness*2,r=cable_diameter/2,center=false);

        translate ([0,outerwidth-(thickness+cable_diameter/2),0])
            rotate ([0,90,0])
                cylinder (h=thickness*2,r=cable_diameter/2);

    }

    // socketcut round
    translate ([outlet_cut_round_offset+thickness+outlet_cut_diameter/2,outerwidth/2,outerheight-thickness])
        cylinder (h=thickness*2,r=outlet_cut_diameter/2);

    // socketcut rectangular
    translate ([switch_cut_rect_offset+thickness,(outerwidth-switch_cut_rect_width)/2,outerheight-thickness])
        cube ([overlay,switch_cut_rect_width,thickness*2]);
}

//srewfix right
translate([0,outerwidth],0)
    screwfix(outerdepth,thickness,screw_diameter);

//screwfix left
mirror([0,1,0])
    screwfix(outerdepth,thickness,screw_diameter);

//cableholder right
if (cable_holder_right)
{
translate([0,outerwidth,outerheight])
    cableholder(cable_holder_width,cable_holder_thickness,
    cableholderheight,
    outerdepth,
    screw_diameter*2.5);    // change this factor to adjust the screw driver width
}

//cableholder left
if (cable_holder_left)
{
    translate([0,0,outerheight])
        mirror([0,1,0])
        cableholder(cable_holder_width,cable_holder_thickness,
        cableholderheight,
        outerdepth,
        screw_diameter*2.5);    // change this factor to adjust the screw driver width
}
}

module cableholder (
    width,
    cthickness,
    height,
    depth,
    dia)
{
    outerradius = width/2+cthickness;

    // Point zero at top left corner
    difference(){
        union() {
            // top cylinder
            translate ([0,width/2,(width/2+cthickness)*-1])
                rotate ([0,90,0])
                    cylinder (h=depth,r=outerradius);

            // fill corner on cylinder
            translate ([0,0,(width/2+cthickness)*-1])
                cube ([depth,width/2,outerradius]);

            // main bar
            translate ([0,0,cthickness/2-height])
                cube ([depth,width+cthickness,height-outerradius-cthickness/2]);

            // main bar lower rounding
            translate ([0,width+cthickness/2,cthickness/2-height])
                rotate ([0,90,0])
                    cylinder (h=depth,r=cthickness/2);

            // corner out main bar rounding
            translate([0,width,height*-1])
                cube([depth,cthickness/2,cthickness/2]);
        }

        //top inner rounding
        translate ([0,width/2,(width/2+cthickness)*-1])
            rotate ([0,90,0])
                cylinder (h=depth*2,r=width/2);
        // main inner space
        translate ([0,0,(height+width)*-1])
            cube ([depth,width,height+width/2-cthickness]);

        // screwslot
        translate ([(depth)/2, max(width/2,dia/2),height*-1])
                cylinder (h=height,r=dia/2);

        // remove overhanging cylinder
        translate([0,outerradius*-1,outerradius*-2])
            cube ([depth,outerradius,outerradius*2]);
    }
}

module screwfix(width,height,diameter) {
    length=3*diameter;
    radius=min(length,width/2);
    difference(){
        hull() {
            cube([width,length-radius,height]);

            translate ([width-radius,length-radius,0])
                cylinder (h=height,r=radius);
            translate ([radius,length-radius,0])
                cylinder (h=height,r=radius);
       };
       // screwholes
       translate ([(width)/2,length/2,0])
            cylinder(h=height,r=diameter/2);

       // remove leftover from rounding
       translate ([0,radius*-1,0])
            cube([width,radius,height]);
    }
}

```