[User]
I'd like to create a set of parametric parts for building stands out of empty tubes, things like metal tubes I might have lying around. I want to be able to make end pieces (both flat and rounded), as well as middle and corner connectors, and I want to be able to easily attach a camera mount, too. It would be nice to see examples with both action cams and gimbal mounts.

Here are my specific design requests.

First, I need to control the following parameters:

*   `radius`: This is the radius of the *empty* tubes that will fit *inside* the printed parts. This will obviously change depending on what tube I have.
*   `thickness`: This is the thickness of the printed parts themselves.
*   `len`: This is the length of the shaft that accepts the tube - how far it goes inside the connector, essentially.
*   `tubes`: This number applies to the middle and corner connectors and represents how many additional tubes should connect (e.g. a 'T' or a corner with more than two arms).
*   `count`: This specifies how many of each piece I want to generate in a single print.
*   `gimbalDamperDiaDistance`: This parameter would be used when creating a gimbal stand, to define the distance between the midpoints of diagonally opposite damper holes on a gimbal.

I would like the following types of parts:

*   `EndFlat`: A flat end piece for the tube.
*   `EndRound`: An end piece with a rounded, ball-like end.
*   `ConnectorMiddle`: A connector that allows tubes to connect in a line. It should be able to handle 1 or more additional tubes on its side.
*   `ConnectorCorner`: A connector for creating right-angle connections. It also should be able to handle 1 or more additional tubes on its sides.
*   `ConnectorCornerGimbal`: A special corner connector that has an additional hook to attach a gimbal.
*    `ActionCamMount`: A mount specifically for attaching an action camera. This should probably be combined with a `ConnectorMiddle` piece.

I'd also like to see a function that combines the above parts into a complete "ActionCamStand" and a "GimbalStand" example to help visualize how they all fit together.

Finally, please use the `text` and `linear_extrude` functions from OpenSCAD to include some explanatory text in those two example models.

Could you generate the necessary parametric OpenSCAD code for this? I'm not looking for any external libraries for now.

[Assistant]
EmptyTubeStand.scad:
```
// ****************************************************************************
// Customizable parts for a stand from empty tubes
// with additions to create a stand for a camera gimbal or an action cam
// Author: Peter Holzwarth
// ****************************************************************************
// https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language

// Radius of the [empty] tubes that go inside these parts.
radius= 8.1; // [1:100]

// Thickness of these parts
thickness= 1.9; // [1:10]

// Length of the shafts taking the tubes
len= 10; // [1:100]

$fn=50;

// what to print
menu="ShowActionCamStand"; // [EndFlat,EndRound,ConnectorMiddle,ConnectorCorner,ConnectorCornerGimbal,ActionCamMount,ShowActionCamStand,ShowGimbalStand]

// Number of (additional) tubes to attach, only used for ConnectorCorner and ConnectorMiddle
tubes= 2; // [0:5]

// Number of pieces to create
count= 1; // [1:10]

// For a gimbal stand, the distance between two diagonal dampers (mid points)
gimbalDamperDiaDistance= 90.5; // [50:200]
// 2*66;

Menu();
// ShowGimbalStand(71.8);
// ShowActionCamStand(80);

module Menu() {
    if (menu=="EndFlat") {
        EndFlat(count);
    } else if (menu=="EndRound") {
        EndRound(count);
    } else if (menu=="ConnectorMiddle") {
        ConnectorMiddle(tubes, count);
    } else if (menu=="ActionCamMount") {
        translate([0,0,radius+thickness]) rotate([-90,0,0])
            ConnectorMiddle(1, count, "ActionCamMount");
    } else if (menu=="ConnectorCorner") {
        ConnectorCorner(tubes, count);
    } else if (menu=="ConnectorCornerGimbal") {
        ConnectorCornerGimbal(tubes, count);
    } else if (menu=="ShowActionCamStand") {
        ShowActionCamStand(80);
    } else if (menu=="ShowGimbalStand") {
        ShowGimbalStand(gimbalDamperDiaDistance);
    }
}

// 0..4 or 5 for the side parts
module ConnectorMiddle(n, count, extras) {
    for (i=[1:count]) {
        translate([(i-1)*2*(radius+len+0.5),0,radius+thickness]) difference() {
            rot=[0,180,90,270];
            union() {
                for (a=[0:n]) {
                    rotate([0,0,rot[a]]) rotate([0,90,0])
                        cylinder(r=radius+thickness, h=len+radius);
                }
                if ("ActionCamMount"==extras) {
                    translate([0,0,radius+22]) ActionCamRingsFemale(25);
                }
            }
            for (a=[0:n]) {
                rotate([0,0,rot[a]]) translate([radius,0,0]) rotate([0,90,0])
                    cylinder(r=radius, h=len+0.01);
            }
        }
    }
}

module ConnectorCorner(n, count) {
    for (i=[0:count-1]) {
        translate([i*2*(radius+2)+i*len,0,radius+thickness]) {
            ConnectorCornerI(n);
        }
    }
}

// a corner connector with a gimbal hook
module ConnectorCornerGimbal(n, count) {
    for (i=[0:count-1]) {
        translate([i*2*(radius+thickness)+i*(len),-i*(len-10),radius+thickness]) {
            ConnectorCornerI(n, "Gimbal");
        }
    }
}

module ConnectorCornerI(n, extras) {
    rot=[0,0,180,270,90,180];
    difference() {
        union() {
            // Ball(radius, thickness)
            sphere(r=radius+thickness);
            cylinder(r=radius+thickness, h=len+radius);
            if (n >= 2) {
                for (a=[2:n]) {
                    rotate([0,0,rot[a]]) rotate([0,90,0])
                        cylinder(r=radius+thickness, h=len+radius);
                }
            }
            if ("Gimbal"==extras) {
                translate([0,0,-radius-thickness]) {
                    translate([-25,-25,0]) cylinder(r=4,h=5.1);
                    translate([-14.4,-14.4,5]) rotate([90,0,45]) halfRing(15,4);
                }
            }
        }
        translate([0,0,radius+0.01]) cylinder(r=radius, h=len);
        if (n >= 2) {
            for (a=[2:n]) {
                rotate([0,0,rot[a]]) translate([radius+0.01,0,0]) rotate([0,90,0])
                    cylinder(r=radius, h=len+radius+0.01);
            }
        }
    }
}

// End piece with a round end
// i number of piece being created
module EndRound(count) {
    for (i=[0:count-1]) {
        translate([i*2*(radius+thickness+0.5),0,radius+thickness]) {
            ConnectorCornerI(1);
        }
    }
}

// End piece with a flat end
// i number of pieces to create
module EndFlat(count) {
    for (i=[0:count-1]) {
        translate([i*2*(radius+thickness+0.5),0,0]) difference() {
            cylinder(r=radius+thickness, h=len);
            translate([0,0,thickness]) cylinder(r=radius, h=len);
        }
    }
}

module ShowActionCamStand(xy) {
    height= 100;
    for (a=[0:3]) {
        rotate([0,0,a*90]) translate([xy,-xy,0]) {
            translate([0,0,height]) rotate([180,0,0])
                ConnectorCornerI(3);
            color("darkgray") cylinder(r=radius,h=height);
            EndFlat(1);
        }
    }
    for (a=[0:3]) {
        rotate([0,0,a*90]) translate([-xy+radius,-xy,height])
            rotate([0,90,0]) color("darkgray") cylinder(r=radius,h=2*(xy-radius));
    }
    translate([0,-xy-radius-thickness,100]) rotate([-90,0,0])
        ConnectorMiddle(1, 1, "ActionCamMount");
    color("red") translate([0,-xy-35,0]) linear_extrude(1)
        text(str("For illustration only."),
                size=20, valign="bottom", halign="center");
}

module ShowGimbalStand(gimbalDamperDiaDistance) {
    damperRadius= gimbalDamperDiaDistance/2;
    a= sqrt(damperRadius*damperRadius/2);
    xy= a+25; // ring offset

    height= 100;
    for (a=[0:3]) {
        rotate([0,0,a*90]) translate([xy,-xy,0]) {
            translate([0,0,height]) rotate([180,0,0])
                ConnectorCornerI(3, "Gimbal");
            color("darkgray") cylinder(r=radius,h=height);
            EndFlat(1);
        }
    }
    plen= 2*(xy-radius);
    color("red") translate([0,-xy-30,height]) linear_extrude(1)
        text(str("tube len: ", plen, " mm"),
                size=20, valign="bottom", halign="center");
    for (a=[0:3]) {
        rotate([0,0,a*90]) translate([-xy+radius,-xy,height])
            rotate([0,90,0]) color("darkgray") cylinder(r=radius,h=plen);
    }
}

// /////////////////////////////////////////////////////////////
// Helpers

// Ball - a hollow sphere
module Ball(r, th) {
    difference() {
        sphere(r=radius+thickness);
        sphere(r=radius);
    }
}

// ////////////////////////////////////////////////////////////////
// a ring with a radius and a thickness
module ring(r, th) {
    rotate_extrude(convexity = 4, $fn = 100)
        translate([r, 0, 0])
            circle(r = th, $fn = 100);
}

// OpenSCAD before 2016.xx
module halfRing(r, th) {
    difference() {
        ring(r, th);
        translate([-r-th, -r-th, -th]) cube([2*r+2*th, r+th, 2*th]);
    }
}
module halfRing2016(r, th) {
    rotate_extrude(angle=180, convexity = 4, $fn = 100)
        translate([r, 0, 0])
            circle(r = th, $fn = th*20);
}

// ////////////////////////////////////////////////////////////////
// parts for action cam mount
module ActionCamRingsFemale(l, base) {
    translate([-15.5/2,0,0]) {
        if (base) {
            translate([0,-6,-l]) cube([15, 12, 3]);
        }
        for (i= [0, 6.25, 12.5]) {
            translate ([i, 0, 0]) ActionCamOneRing(l);
        }
    }
}

module ActionCamOneRing(l) {
    rotate([90, 0, 90]) difference() {
        hull() {
            cylinder(r=6, h=3);
            translate([-6, -l, 0]) cube([12,1,3]);
        }
        translate([0,0,-0.01]) cylinder(r=3, h=3.02);
    }
}

```


[User]
I'm looking to design a flexible box, something like a cardboard box blank, that I can cut out and fold up. I'd like to be able to control its dimensions, material thickness, and corner radius, as well as the number of "creases" in the folded edges and how they are created. I want to be able to view the 2D layout of the unfolded box.

Here's what I need:

*   **Basic Box Dimensions:**
    *   Internal Length
    *   Internal Depth
    *   Internal Height
*   **Material:**
    *   Thickness (for the material)
    *   Corner Radius
*   **Fold/Crease Parameters:**
    *   A method to choose how the creases are represented (e.g. a series of thin squares, maybe some other way down the line)
    *   Number of creases on each fold
    *   Option to automatically calculate the number of creases based on the corner radius.
*   **2D View:**
    *   The unfolded 2D layout of the box should be displayed as if it were on a flat surface.

I'd like the design to be parametric so I can adjust all of these values. I'm not using any external libraries yet.

[Assistant]
FlexTest.scad:
```
$fn=64/1;
/*
Flexbox
*/

/* [Dimensions intérieures] */
Longueur            = 200;
Profondeur          = 100;
Hauteur             = 80;
/* [Matière et paramètres] */
Epaisseur           = 5.0;
Arrondi             = 20.0;
/* [Pliure] */
Methode             = 1;//[1,2]
Nombre_d_arretes    = 3;
Nombre_automatique  = "Non";//[Oui,Non]

// Variables calculées
RLongueur           = Longueur + Epaisseur*2;
RProfondeur         = Profondeur + Epaisseur*2;
RHauteur            = Hauteur + Epaisseur*2;
SLongueur           = Longueur - Arrondi*2;
SProfondeur         = Profondeur - Arrondi*2;

Longueur_pliure     = Arrondi*3.141592654/2;

vue2D();

module vue2D()
{
    translate([0,-Hauteur-Epaisseur*3])
    mondeplie();

    mabase();

    translate([RLongueur+5,0])
    mabase();
}

module pliure(a)
{
    Longeur_arrete = Longueur_pliure/(a*2+1);
    if(Methode==1)
    {
        for(i=[0:a-1])
        {
            translate([Longeur_arrete+(Longeur_arrete*i*2),0])
            square([Longeur_arrete,Hauteur]);
        }
        for(i=[0:a/2])
        {
            translate([(Longeur_arrete*i*4),0])
            square([Longeur_arrete,Hauteur/10]);
        }
        for(i=[0:ceil(a/2-1)])
        {
            translate([Longeur_arrete*2+(Longeur_arrete*i*4),Hauteur-Hauteur/10])
            square([Longeur_arrete,Hauteur/10]);
        }
    }
    else
    {

    }

}

module mondeplie()
{
for(i=[0:1])
{
    translate([SLongueur/4,Hauteur*i])
    square([SLongueur/4,Epaisseur*2],center=true);

    translate([SLongueur/2+Longueur_pliure+SProfondeur/2,Hauteur*i])
    square([SProfondeur/4,Epaisseur*2],center=true);

    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur/2,Hauteur*i])
    square([SLongueur/4,Epaisseur*2],center=true);

    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur+Longueur_pliure+SProfondeur/2,Hauteur*i])
    square([SProfondeur/4,Epaisseur*2],center=true);

    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur/4,Hauteur*i])
    square([SLongueur/4,Epaisseur*2],center=true);

}

    square([SLongueur/2,Hauteur]);
    translate([SLongueur/2,0])
    color("white")
    if(Nombre_automatique=="Non")
    {
        pliure(Nombre_d_arretes);
    }
    else
    {
        pliure(floor(Arrondi/2));
    }

    translate([SLongueur/2+Longueur_pliure,0])
    square([SProfondeur,Hauteur]);

    translate([SLongueur/2+Longueur_pliure+SProfondeur,0])
    color("white")
    if(Nombre_automatique=="Non")
    {
        pliure(Nombre_d_arretes);
    }
    else
    {
        pliure(floor(Arrondi/2));
    }

    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure,0])
    square([SLongueur,Hauteur]);

    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur,0])
    color("white")
    if(Nombre_automatique=="Non")
    {
        pliure(Nombre_d_arretes);
    }
    else
    {
        pliure(floor(Arrondi/2));
    }
    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur+Longueur_pliure,0])
    square([SProfondeur,Hauteur]);

    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur+Longueur_pliure+SProfondeur,0])
    color("white")
    if(Nombre_automatique=="Non")
    {
        pliure(Nombre_d_arretes);
    }
    else
    {
        pliure(floor(Arrondi/2));
    }
    translate([SLongueur/2+Longueur_pliure+SProfondeur+Longueur_pliure+SLongueur+Longueur_pliure+SProfondeur+Longueur_pliure,0])
        square([SLongueur/2,Hauteur]);
}
module mabase()
{
    difference()
    {
        hull()
        {
            translate([Arrondi,Arrondi])
            circle(r=Arrondi);

            translate([Longueur-Arrondi+Epaisseur*2,Arrondi])
            circle(r=Arrondi);

            translate([Arrondi,Profondeur-Arrondi+Epaisseur*2])
            circle(r=Arrondi);

            translate([Longueur-Arrondi+Epaisseur*2,Profondeur-Arrondi+Epaisseur*2])
            circle(r=Arrondi);
        }

        translate([Arrondi+SLongueur/4,0])
        square([SLongueur/4,Epaisseur*2],center=true);

        translate([Arrondi+SLongueur-SLongueur/4,0])
        square([SLongueur/4,Epaisseur*2],center=true);

        translate([0,RProfondeur/2])
        square([Epaisseur*2,SProfondeur/4],center=true);

        translate([RLongueur,RProfondeur/2])
        square([Epaisseur*2,SProfondeur/4],center=true);

        translate([RLongueur/2,RProfondeur])
        square([SLongueur/4,Epaisseur*2],center=true);
    }
}
```


[User]
I'm looking for an OpenSCAD design for a customizable spool hub, something that can accommodate spools of slightly different inner diameters by having springy "vanes" that flex and grip the spool. I need a few key features and parameters to adjust, and would like to use the BOSL2 library for the parametric design.

Here’s what I’m imagining:

**Core Structure:**

*   A central bore for the hub to attach to something – this diameter should be adjustable.
*   A raised center portion that’s slightly above the flexible vanes. I need to be able to control this height difference.
*   The flexible “vanes” are what will grip the spool. I need to be able to control:
    *   The number of vanes
    *   The width of each vane (as a multiple of the extrusion width).
    *   The height of the vanes.
    *   The maximum angle the vane extends to (this limits the degree of flex and ensures there’s room for compression).
    *   The fillet radius where the vane meets the main hub body; this should be scalable, like, a multiple of the vane width.

**Rim (Outer Edge):**

*   A rim around the outside of the vanes. It's needed to limit the amount of expansion the vanes can do. This should be adjustable by:
    *   The width of the rim.
    *   The height of the rim.
    *   The angular width of each rim segment.

**Optional Bearing Seat:**

*   There should be an option to add a bearing seat. This would need parameters for:
    *   The width of the seat ring.
    *   The height of the seat ring.
   This ring will be flat and not contoured to the bearing's outer race shape.

**Printer-Related Parameters:**

*   The extrusion width.
*   The layer height.
*  The circular resolution (segments per 360 degrees) for smooth curves.

**Overall Design:**
*   The main diameter of the hub (where the outer edges of the vanes are) should be adjustable - this is what will be sized to be slightly larger than the spools it needs to grip.
*   The hub should also have all its parameters exposed via customizer.
*   The root fillet (where the vane connects to the base) and rim fillets should be done properly, with smooth, rounded transitions, and should have their radii exposed as parameters.
*   The hub should be generated as a single object without extra parts.

I'm okay with having to import the BOSL2 library and I'm expecting that the parameters will be exposed via customizer. The design should be relatively simple and easy to print, optimized for perimeter-only printing for the flexible vanes and should not contain any unneccesary details.

[Assistant]
spiral-hub.scad:
```
// by Martin Muehlhaeuser Twitter: @mediaing
// rev2: fixed missing path in vane profile
// rev3: exported root fillet parameter
// rev4: added bearing seat

/* [main parameters] */
// Maximum diameter of the hub in units. Make this 5-10% bigger than the largest spool bore for ideal tension.
maxDia = 58;
// Diameter of the center bore in units.

centerBoreDia = 8.3;
// Height by which the center ring is raised above the vanes in units.
centerExtrusion = 1; //[0:10]

/* [vanes] */
// Number of vanes.
vaneCount = 6;
// Vane width in X times extrusion width.
vaneWidth =  4;//[3:5]
// Height of the vanes in units.
vaneHeight = 6;
// The angle the vane extends to. To large and there is no room for compression of the spring left.
maxVaneAngle = 115;//[90:150]
// Fillet radius between the back of the vane and the hub. As x times vaneWidth.
filletRadiusScale = 1.4; //[1.0:0.1:4.0]

/* [rim] */
// Width of the rim
rimWidth = 4; //[3:7]
// Rim height in X times layerHeight.
rimHeight = 4;//[2:6]
// Width of the rim segment in degrees.
rimAngularWidth = 50;//[30:90]

/* [bearing seat] */
addSeat = "no"; //[yes, no]
// Width of the seat ring in units.
seatWidth = 2;
// Height of the seat ring in X times layerHeight.
seatHeight = 4; //[2:6]

/* [printer Parameters] */
extrusionWidth = 0.4;
layerHeight = 0.3;
// circular resolution in segments per 360 Degrees.
resolution = 360;

/* [Hidden] */
rVane = ((maxDia - centerBoreDia) / 4);
rRim = rVane + rimWidth;
rimFilletRadius = rimWidth/2.5;
_vaneWidth = extrusionWidth * vaneWidth;
_rimHeight = layerHeight * rimHeight;
_seatHeight = layerHeight * seatHeight;

// rotate and instacnce children
module rotateCopy(deltaAngle = 4)
{
    children();
    rotate(deltaAngle, [0,0,1]) children();
}

// calculates a point on the circle at the specified angle and adds an offset length
function pointOnCircleOffset(radius=1, angle=0, circumferenceOffset=0) = (
     let(offsetAngle = (circumferenceOffset * 180 / ((radius-circumferenceOffset) * PI) ) )
     [sin(angle+offsetAngle) * (radius), cos(angle+offsetAngle) * (radius)]
);

// calculate a point on a circle
function pointOnCircle(radius=1, center=[0,0], angle) = (
     [sin(angle) * (radius) + center[0], cos(angle) * (radius) + center[1]]
);

// offset angle by length on circumference with given radius
function offsetAngle(baseAngle=0, radius=1, circumferenceOffset=0) =
    (baseAngle + (circumferenceOffset * 180 / ((radius-circumferenceOffset) * PI)));

function ring(d1=1, d2=2, step=0.8) = concat (
    [[0, d2/2]],
    [for (a=[0:step:360]) pointOnCircle(d2/2, [0,0], a)],
    [[0, d2/2]],
    [[0, d1/2]],
    [for (a=[0:step:360]) pointOnCircle(d1/2, [0,0], a)],
    [[0, d1/2]]
);

// calculate position of the root fillet between vane and hub
function calcFilletCenter(a=5, b=12.5, c=17.5, fr=1) = (
    let(h = sqrt(-pow(a,4) + 2 * pow(a,2)*pow(b,2) + 2*pow(a,2)*pow(c,2)
                 - pow(b,4) + 2*pow(b,2)*pow(c,2) - pow(c,4)) / (2*c) )
    [-sqrt(pow(b,2) - pow(h,2)), h]
);

// create the vane profile
function vaneProfile(radius=1.0, maxAngle=120, width=5, step=0.8) = (
    let(maxVaneAngle = offsetAngle(maxAngle, radius-width/2, -width/2))
    let(roundoverCenter = pointOnCircleOffset(radius-width/2, maxAngle, -width/2))
    let(filletRadius = width*filletRadiusScale)
    let(filletCenter =
            calcFilletCenter(a=(centerBoreDia/2) + width + filletRadius,
            b=radius+filletRadius,
            c=radius + (centerBoreDia/2)),
            fr=filletRadius )
    let(maxFilletAngle = acos(filletCenter[1]/((centerBoreDia/2) + width +filletRadius)) )
    let(minFilletAngle = acos(filletCenter[1]/(radius+filletRadius)) )
    concat(
        [[-radius+width, 0], [-radius, 0]],
        [for (a=[90:-step:90-asin(filletCenter[1]/((centerBoreDia/2) + width +filletRadius))])
                                     pointOnCircle(centerBoreDia/2, [-radius-centerBoreDia/2,0], a) ],
        [for (a=[180+maxFilletAngle:-step:180-minFilletAngle])
                                     pointOnCircle(filletRadius, filletCenter, a)], // root fillet
        [for (a=[-90+asin(filletCenter[1]/(radius+filletRadius)):step:maxVaneAngle])
                                     pointOnCircle(radius, [0,0], a) ], // outer circle
        [for (a=[maxAngle:step:maxAngle+180]) pointOnCircle(width/2, roundoverCenter, a)], // radius
        [for (a=[maxVaneAngle:-step:-90]) pointOnCircle(radius-width, [0,0], a)] // inner circle
    )
);

// create the rim segmennt profile
function rimProfile(radius=1.0, minAngle=80, maxAngle=120, rimWidth=45, vaneWidth=1, filletRadius=1, step=0.5) = (
    let(outerMinAngle = offsetAngle(minAngle, radius, filletRadius) )
    let(outerMaxAngle = offsetAngle(maxAngle, radius, -filletRadius) )
    let(innerFilletCenter = pointOnCircleOffset(radius-rimWidth+filletRadius, minAngle, -filletRadius))
    let(upperOuterFilletCenter = pointOnCircleOffset(radius-filletRadius, minAngle, filletRadius))
    let(lowerOuterFilletCenter = pointOnCircleOffset(radius-filletRadius, maxAngle, -filletRadius))

    concat(
        [pointOnCircleOffset(radius-rimWidth-vaneWidth/2, minAngle, -filletRadius) ],
        [for (a=[minAngle+180:-step:minAngle+90]) pointOnCircle(filletRadius, innerFilletCenter, a) ],
        [for (a=[minAngle-90:step:minAngle]) pointOnCircle(filletRadius, upperOuterFilletCenter, a) ],
        [for (a=[outerMinAngle:step:outerMaxAngle]) pointOnCircle(radius, [0,0], a) ],
        [for (a=[maxAngle:step:maxAngle+90]) pointOnCircle(filletRadius, lowerOuterFilletCenter, a) ],
        [pointOnCircle(radius-rimWidth-vaneWidth/2, [0,0], maxAngle )],
        [for (a=[outerMaxAngle:-step:outerMinAngle]) pointOnCircle(radius-rimWidth-vaneWidth/2, [0,0], a) ]
    )
);

module hub() {
    union() {
        difference() {
           cylinder(   h = vaneHeight + centerExtrusion,
                       d1 = centerBoreDia + _vaneWidth*2, d2 = centerBoreDia + _vaneWidth*2,
                      center = false, $fn = resolution);
           cylinder(   h = vaneHeight*4 + centerExtrusion,
                       d1 = centerBoreDia, d2 = centerBoreDia,
                       center = true, $fn = resolution);
        }
        if(addSeat == "yes") {
            linear_extrude(height=_seatHeight, center=false, $fn = resolution)
                polygon(points = ring(d1=centerBoreDia, d2=centerBoreDia-2*seatWidth, step=360/resolution));
        }
        for(i=[1:1:vaneCount]) {
            rotateCopy(i*(360/vaneCount))

                translate([rVane + centerBoreDia/2, 0,0]) {

                    linear_extrude(height=vaneHeight, center=false, $fn = resolution)
                        polygon(points = vaneProfile(radius = rVane,
                                                     maxAngle=maxVaneAngle,
                                                     width=_vaneWidth,
                                                     step=360/resolution));

                    linear_extrude(height=_rimHeight, center=false, $fn = resolution)
                        polygon(points = rimProfile(
                                                radius = rRim,
                                                minAngle=maxVaneAngle-rimAngularWidth,
                                                maxAngle=maxVaneAngle,
                                                rimWidth=rimWidth,
                                                vaneWidth=_vaneWidth,
                                                filletRadius=rimFilletRadius,
                                                step=360/resolution
                                        )
                        );
                }
           }
    }
}

hub();

```


[User]
I'm looking to design a bracket to hold a Nintendo Switch console, similar to the one I've described in the provided SCAD file.  I'd like a parametric design, so I can adjust various dimensions and angles.

Essentially, I need two mirrored support brackets. Each bracket consists of a main beam with a "Joyrail" (a rail designed to fit the Switch's joystick keyway) on one side.  The beam has angled feet at each end for stability. I need to be able to control the following parameters:

*   **Thickness:** The overall printed thickness of the support beams.
*   **Beamwidth:** The width of the support beams.
*   **Beamlength:** The length of the main beam section.
*   **Topangle:** The angle of the top foot relative to the main beam.
*   **Bottomangle:** The angle of the bottom foot relative to the main beam.
*   **Supports:** A binary switch to include small support blocks under the Joyrail.

The "Joyrail" module should have its own fixed dimensions for the keyway and overall width as described in the file. This should fit the Nintendo switch joysticks properly. The support feet should have a smoothly clipped end to provide stability.

Also, the design should include punchouts or cutaways where the angled feet meet the main beam to save on material and reduce print time. These cutaways should be parameterized to account for the thickness and clipping angle of the feet.  I'd like to be able to define the clipangle separately.

The design should be generated using only standard OpenSCAD modules and avoid any external libraries. Could you provide me with a `.scad` file that allows me to generate this design? I want to be able to customize these parameters from the main file.

[Assistant]
Switch.scad:
```
$fn = 360;
/*[Nintendo Switch Support bracket parameters]*/
// (mm) Printed thickness of the bracket support beams (not including rail)
thickness = 4;
// (mm) Width of the support beams
beamwidth = 15;
// (mm) Length of the main beam
beamlength = 130;
// Angle of the top bracket
topangle = 45;
// Angle of the bottom bracket
bottomangle = 70;
// (0/!0) Include supports for bridging (omit these if you can reliably print support structures or use a support material with your slicer/printer)
supports = 0;

dual_switch_supports(thickness, beamwidth, beamlength, topangle, bottomangle, supports);

// Mount rail to fit the Nintendo Switch joystick keyway
module Joyrail(s, l)
{
	w = 9;
	//l = 50;
	h = 2.5;
	t = 1.5;
	gap = 1.0;
	key = h - gap;
	//translate([0,0,(h/4) + (h/2)])
	union() {
		translate([0,0,h/2])	// main support
			cube([w-h, l, h], true);
		translate([0,0,h - (key/2)])	// key plate
			cube([w, l, key], true);
		if(s != 0)
		{
			// Some little support blocks that should be bridgable on most printers
			translate([0,-(l - t)/2,h/2])	// end supports
				cube([w, t, h], true);
			translate([0, (l - t)/2,h/2])
				cube([w, t, h], true);
		}
	}
}

// Main bar of the support
module Bar(thickness, length, width, keylen, printsupports)
{
	w = width;
	h = thickness;
	l = length;
	translate([0,0,h/2])
		cube([w, l, h], true);
	translate([0,0, h])
		Joyrail(printsupports, keylen);
}

// A nicely shaped bar that makes up the feet of the support bracket (or an appropriate punchout)
module SupportBar(thickness, length, width, clipangle)
{
  w = width;
	h = thickness;
	l = length;

	//clipangle = 15;//atan(4.5/14);
	offset = width;
	cliplen = l * 1.5;
	union()
	{
		difference()
		{

			cube([w, l, h], true);

			translate([0, (l/2) + offset , 0])
				rotate([0,0,-clipangle])
					translate([0, -cliplen/2, 0])
						cube([w, cliplen, h+1], true);
		}
		translate([0, -length/2, 0])
			cylinder(h = thickness, d = width, center = true);
  }
}

// Pattern to make up a single support bracket (mirror it for the other side)
module SwitchSupport(thickness, barwidth, barlength, topangle, bottomangle, printsupports)
{
	length = barlength - barwidth;
	feet = 70;
	top = topangle;
	bottom = bottomangle;
	width = barwidth;
	clipangle = 15;
	punchdiff = 2 * ((thickness / sin(clipangle)) - (thickness));
	keylen = 2* (82 - (barlength / 2));
	difference()
	{
		union()
		{
			// mounting beam
			Bar(thickness, length, width, keylen, printsupports);

			// top foot
			translate([0, length/2, thickness/2])
				rotate([0,0,top])
					translate([0, -feet/2,0])
						mirror([0,1,0])
							SupportBar(thickness, feet, width, clipangle);
			// bottom foot
			translate([0, -length/2, thickness/2])
				rotate([0,0, 360 - bottom])
					translate([0, feet/2,0])
						SupportBar(thickness, feet, width, clipangle);
    }

		// punchouts
			translate([0, length/2, thickness/2])
				rotate([0,0,top])
					translate([0, -feet/2,0])
						mirror([0,1,0])
							translate([0, -(punchdiff)/2, 0])
								SupportBar(thickness + 1, feet - punchdiff, width - (thickness * 2), clipangle);

			translate([0, -length/2, thickness/2])
				rotate([0,0, 360 - bottom])
					translate([0, feet/2,0])
						translate([0, -(punchdiff)/2, 0])
							SupportBar(thickness + 1, feet - punchdiff, width - (thickness * 2), clipangle);
  }
}

// A pair of matched support brackets
module dual_switch_supports(thickness, barwidth, barlength, topangle, bottomangle, printsupports)
{
translate([ ((barwidth / 2) + 2),0,0])
	SwitchSupport(thickness, barwidth, barlength, topangle, bottomangle, printsupports);
translate([-((barwidth / 2) + 2),0,0])
	mirror([1,0,0])
		SwitchSupport(thickness, barwidth, barlength, topangle, bottomangle, printsupports);
}

```


[User]
I'd like to design a parametric token or coin storage container, and I'd like to be able to customize it in OpenSCAD. It should have a sleeve and a sliding tray inside, and the sleeve should have a locking tab that snaps into the tray to keep it closed. There should also be a viewing window on the side of the sleeve so I can see what's inside. I'd also like the option to add a pull ring to the back of the tray, which could also be used to attach a lanyard. I want to be able to specify how many sides the container has, defaulting to a hexagon, but with the option to have 10, 14, 18, etc. sides. I want to be able to specify things like the diameter and thickness of the tokens I'm storing, how many I want to hold, and if the tokens nest together, how much they nest. I’d also like to be able to override the length if I need to. I need to control the tolerances to allow the parts to move correctly, as well as the wall thickness for both the sleeve and the tray. Include a build plate visualization tool using a library.

Could you please create the necessary OpenSCAD files for this, including the main design file and a build plate utility? I'm hoping for something fully parametric, like the one I saw that was described as:

*  `token_tube.scad` by mildmojo

I believe that design used these libraries:
*  `utils/build_plate.scad`

Can you also make sure the locking tab is adjustable in size and the window is customizable? I'd really appreciate it!

[Assistant]
token_tube_1.2_customizable.scad:
```
/* token_tube.scad
 * by mildmojo
 * https://www.thingiverse.com/thing:2335826
 * CC-BY-NC
 *
 * Tray and tube sleeve designed for holding game tokens or coins. Length and
 * diameter can be adjusted or calculated from your token size and the number
 * you'd like to store. A locking tab lets the tray and sleeve snap closed.
 *
 * Original settings for my purposes were:
 *   token_dia=24
 *   token_thickness=3 // 3 for damage, 4 for status
 *   token_nesting_depth=0.5
 *
 * This design is licensed under a Creative Commons Attribution-NonCommercial
 * 4.0 International (CC BY-NC 4.0) license. If you'd like a commercial license,
 * get in touch!
 * https://creativecommons.org/licenses/by-nc/4.0/
 *
 * v1.1 - Fixes end cap bottom cutoff for small parts
 *        Fixes part layout for small parts
 *        Calculates end cap size based on tube diameter
 * v1.1 - Adds Customizer build plate viz, converts toggle variables to "yes/no"
 *        because it looks better in customized object descriptions, and fixes
 *        ghost visualization of tokens when nesting depth is > 0.
 * v1.0 - Original version
 */

use <utils/build_plate.scad>

// preview[view:south west, tilt:top diagonal]

/* [General] */

// PRIMARY CONFIGURATION
// =====================

// Object layout; to see how the parts fit together, true for printing.
layout = 1; //[1:For printing, 0:Assembly preview]

// Include the sleeve in the parts to print?
print_tube = "yes"; //[yes, no]

// Include the tray in the parts to print?
print_tray = "yes"; //[yes, no]

// Should the bottom of the sleeve be open or closed? (Default: Closed)
tube_end_type = "closed"; //[open, closed]

// Add a ring to the back of the tray as a handle or for a lanyard? (Default: No)
tray_pull_ring = "no"; //[yes, no]

// Hex is sexy. How many sides should container have? (Multiples of 4 plus 2)
tube_side_count = 6; //[6 : 4 : 30]

// Token or coin diameter (mm) (diameter of largest circle to fit in box)
token_dia = 25.4; // 25.4mm == 1" US quarter

// Token or coin thickness (mm).
token_thickness = 1.75; // 1.75mm US quarter

// If your tokens nest together (a stack is shorter than count*thickness), enter nesting depth here (mm).
token_nesting_depth = 0;

// Number of tokens it should hold. May want to increase by 1 to make it easier to get tokens in/out.
token_count = 40; // 40 quarters in ten dollars

// OVERRIDE: Interior length of token tray, 10mm minimum. (mm) (Default: 0, calculate from token thickness & count)
override_interior_length = 0;

// OPTIONS
// =======

// Viewing window slot width (mm). May need to resize if you change `token_dia` or `tube_side_count`.
tube_window_width = 5;

// Distance from tube top/bottom where window slot begins (mm).
tube_window_margin = 5.5;

// Tube wall thickness (mm).
tube_wall_thickness = 2;

// Tray wall thickness as a multiple of tube wall thickness
sled_wall_multiple = 1; //[0.25 : 0.25 : 2]

/* [Tolerances] */

// Tolerance between the tube and the sled (mm). Adjust until sled slides freely.
tube_tolerance = 0.7;

// Tolerance between tokens and sled/tube (mm). Adjust until sled + tokens slide freely in tube.
token_tolerance = 1.75;

// Tolerance for the snap-shut latch tab (mm). Increase to make it easier to open and close.
latch_tolerance = 0.5;

/* [Build plate visualization] */

// For display only, doesn't contribute to final object
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
// When Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 150; //[100:400]
// When Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 150; //[100:400]
build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

/* [Hidden] */
// NO ADJUSTMENT BELOW THIS LINE PROBABLY
// ======================================

for_printing = layout == 1;

sled_wall_thickness = tube_wall_thickness * sled_wall_multiple;

// Tokens may have a depression for nesting, so N-1 tokens are actually
// (thickness - nesting) thick each. In a stack, only the last token can
// actually be considered full thickness.
nested_stack_length = (token_thickness - token_nesting_depth) * (token_count - 1) + token_thickness;
token_stack_length = override_interior_length > 0 ? override_interior_length : nested_stack_length;
sled_length = token_stack_length + 2*sled_wall_thickness;
tube_length = sled_length - sled_wall_thickness + tube_tolerance + tube_wall_thickness;

tube_dia = token_dia + 2*token_tolerance + 2*sled_wall_thickness + 2*tube_tolerance + 2*tube_wall_thickness;
sled_dia = token_dia + 2*token_tolerance + 2*sled_wall_thickness;

// Angle to twist the tube so a flat face is parallel to the X axis.
// 4n+2 sides are fine, others have to be rotated by 45/m, where m is sides/4
tube_twist_angle = (tube_side_count - 2) % 4 == 0 ? 0 : 45 / (tube_side_count / 4);

// Diameters from face to face vs. point to point
tube_flat_to_flat_dia = 2 * (tube_dia/2) * cos(360/tube_side_count/2);
sled_flat_to_flat_dia = 2 * (sled_dia/2) * cos(360/tube_side_count/2);

// Relief for nested tokens that have a cone protrusion in the center
token_cone_hole_dia = 6;

// Latch feature dimensions
latch_width = (sled_dia * PI) / 6 * 0.6;
latch_depth = 1.4;
latch_end_dist = 5;
latch_fudge = 0.5;

// Pull ring feature dimensions
ring_dia = 0.75*token_dia;
ring_width = sled_wall_thickness;

sled_cap_dia = 1.15*tube_dia;
sled_cap_rounding_rad = 1;

// Fudge factor to allow `difference()` to punch through surfaces w/o z-fighting.
diff_fudge = 0.1;

all_parts();

module all_parts() {
  if (for_printing) {
    if (print_tube == "yes") {
      tube();
    }

    if (print_tray == "yes") {
      translate([-1.25*tube_dia,0,0])
        sled();
    }
  } else {
    %tube();
    translate([0, sled_flat_to_flat_dia/2, tube_wall_thickness + tube_tolerance])
      rotate([90,0,0])
        sled();
  }
}

module tube() {
    difference() {
      rotate([0,0,tube_twist_angle])
        cylinder(d=tube_dia, h=tube_length, $fn=tube_side_count);
      // Hollow out the middle
      rotate([0,0,tube_twist_angle])
        translate([0,0,tube_end_type == "closed" ? tube_wall_thickness : -diff_fudge])
          cylinder(d=tube_dia - 2*tube_wall_thickness, h=2*tube_length, $fn=tube_side_count);

      // Window
      translate([-tube_window_width/2, -tube_flat_to_flat_dia/2 - diff_fudge, 0]) {
        // Center slot
        translate([0,0,tube_window_margin + tube_window_width/2])
          cube([tube_window_width, 2*tube_wall_thickness + diff_fudge, tube_length - 2*tube_window_margin - tube_window_width]);
        translate([tube_window_width/2,0,0]) {
          // Rounded bottom
          translate([0,0,tube_window_margin + tube_window_width/2])
            rotate([-90,0,0])
              cylinder(d=tube_window_width, h=2*tube_wall_thickness + diff_fudge, $fn=20);
          // Rounded top
          translate([0,0,tube_length - tube_window_margin - tube_window_width/2])
            rotate([-90,0,0])
              cylinder(d=tube_window_width, h=2*tube_wall_thickness + diff_fudge, $fn=20);
        }

      }
    }

    latch_bump_width = 0.8*latch_width;
    latch_ramp_length = 4*latch_depth;

    translate([-latch_bump_width/2, sled_flat_to_flat_dia/2 + tube_tolerance + latch_fudge, tube_wall_thickness + tube_tolerance + latch_end_dist + latch_ramp_length])
      rotate([0,90,0])
        latch_bump(latch_bump_width, latch_ramp_length);
}

module sled() {
  translate([0,0,sled_flat_to_flat_dia/2])
    rotate([-90,0,0])
      difference() {
        union() {
          cylinder(d=sled_dia, h=sled_length, $fn=tube_side_count);
          // Sled end cap
          translate([0, 0, sled_length - sled_wall_thickness + sled_cap_rounding_rad])
            minkowski() {
              cylinder(d=sled_cap_dia - 2*sled_cap_rounding_rad, h=sled_wall_thickness - sled_cap_rounding_rad, $fn=tube_side_count);
              sphere(r=1, $fn=20);
            }

          // Visual aid, show transparent imaginary tokens in preview.
          if (override_interior_length == 0)
            translate([0,0,sled_wall_thickness])
              ghost_tokens(token_count, token_dia, token_thickness);

          // Add a pull ring to the back of the tray?
          if (tray_pull_ring == "yes")
            translate([0, 0, sled_length])
              pull_ring();
        }

        // Cut off end cap excess
        translate([-(tube_dia + 6)/2,sled_flat_to_flat_dia/2, -1])
          cube([tube_dia + 6, tube_dia, sled_length + 2]);

        // Hollow it out
        translate([0,0,sled_wall_thickness])
          cylinder(d=sled_dia - 2*sled_wall_thickness, h=sled_length - 2*sled_wall_thickness, $fn=tube_side_count);

        // Cut off top half to make tube into tray
        translate([-sled_dia/2,-sled_dia,-diff_fudge])
          cube([sled_dia, sled_dia, sled_length - sled_wall_thickness - 2 + diff_fudge]);

        // Hole for token centering protrusion
        translate([0,0,-diff_fudge])
        cylinder(d=token_cone_hole_dia, h=sled_wall_thickness + 2*diff_fudge, $fn=20);

        // Locking slot
        translate([-latch_width/2, sled_flat_to_flat_dia/2 + latch_depth/1.5 + latch_fudge, latch_end_dist + 4*latch_depth])
          rotate([0,90,0])
            latch_bump(latch_width, 4*latch_depth);
      }
}

module pull_ring() {
  rounding_rad = sled_cap_rounding_rad;
  overhang_angle = 50;
  overhang_sub_width = token_dia + 2*sled_wall_thickness;
  overhang_sub_depth = token_dia;
  overhang_sub_length = 2*token_dia;

  translate([0, -ring_width, 0])
  difference() {
    // Embed a cylinder in the sled cap, oriented vertically from the sled bottom.
    minkowski() {
      translate([0, sled_dia/2 + rounding_rad - tube_wall_thickness, 0])
        rotate([90,0,0])
          cylinder(d=ring_dia + 2*sled_wall_thickness - 2*rounding_rad, h=sled_dia/2 + 2*ring_width - 2*rounding_rad - tube_wall_thickness, $fn=60);
      sphere(r=rounding_rad, $fn=20);
    }

    // Delete half the cylinder that sticks into the end cap.
    translate([-token_dia,-token_dia,-2*token_dia])
      cube([2*token_dia, 2*token_dia, 2*token_dia]);

    // Hollow out the cylinder.
    rotate([90,0,0])
      cylinder(d=ring_dia, h=3*token_dia, center=true);

    // Cut off the bottom of the cylinder at an angle to allow it to print w/o support.
    translate([-overhang_sub_width/2, -0, -overhang_sub_length + ring_dia/2 + sled_wall_thickness])
      translate([0, 0, overhang_sub_length]) // change axis of rotation
        rotate([overhang_angle,0,0])
          translate([0, 0, -overhang_sub_length]) // translate back to original position after rotation
            cube([overhang_sub_width, overhang_sub_depth, overhang_sub_length]);
  }
}

module latch_bump(latch_bump_width, latch_ramp_length) {
  linear_extrude(height=latch_bump_width)
    polygon(points=[[0,0], [latch_ramp_length, -latch_depth - 0.5 - latch_fudge], [2*latch_ramp_length, 0]]);
}

module ghost_tokens(count, dia, thickness) {
  for (i = [0 : count - 1]) {
    translate([0, 0, i * (thickness - token_nesting_depth)])
      %cylinder(d1=dia, d2=0.95*dia, h=thickness, $fn=40);
  }
}

```


[User]
I'd like a custom key holder designed in OpenSCAD. It should consist of two main parts: a bottom plate and a top plate, which will be fastened together with screws.

The bottom plate should be a rectangular solid with a key stop, which is essentially another rectangular solid sticking out. The key stop needs to be on the side of the bottom plate that the keys will sit on. The bottom plate should also have screw holes. The screw holes should be slightly smaller than the screw, so the screws can self-tap into the plastic. I want the edges of the bottom plate to be chamfered.

The top plate should mirror the bottom plate in terms of size and have corresponding screw holes, but the screw holes should be designed for the bolt heads to sit flush. Also, the top plate should have a thumb hole that is a polygon (you choose the number of sides). The edges of the top plate should be chamfered as well.

Here are some specific parameters to use initially but please make sure they are all easily adjustable later on in the customizer. The overall length of the holder should be 79.375mm and the width should be 31.75mm. The thickness of each plate should be 6.35mm. The key stop should be 35mm long, 6.35mm wide, and 1.905mm thick. The screws I plan to use have a diameter of 4.318mm, and I want the screw holes to be slightly smaller for self-tapping. The bolt length I need will be around 7.35mm (Thickness+1mm). The head of the bolt is 8mm in diameter and 4.75mm in thickness. The screw holes should be offset 6.35mm from the sides. I want the thumb hole to be 50.8mm in diameter. I would like an 8-sided polygon for the thumb hole initially but make it customizable. The chamfer should be 45 degrees, and 10mm wide.

I'm not using any external libraries right now but if something is needed let me know, I haven't had to include anything previously in my designs. I'm anticipating a design made of two separate modules, one for the bottom piece, and one for the top piece.

[Assistant]
keyHolder02.scad:
```
/***********************************************************************
Name ......... : keyHolder.scad
Description....: Parametric Key Holder
Author ....... : Garrett Melenka
Version ...... : V1.0 2017/05/21
Licence ...... : GPL
***********************************************************************/

//Length of Key Holder
Length = 79.375;
//Width of Key Holder
Width = 31.75;

//Thickness of Holder Plates
Thickness = 6.350;

//Measured Thickness of Keys
keyThickness = 1.905;

//Length of Spacer to stop keys
keySpacerLength = 35;
//Width of key spacer
keySpaceWidth = 6.35;

//Measured bolt diameter- undersize so bolt with thread into hole
boltDiameter = 4.318;
//Measured length of bolt
boltLength = Thickness+1;
//Measured diameter of bolt head
boltHeadDiameter= 8.0;
//Measured bolt head thickness
boltHeadThickness = 4.75;
//Offset of bolts from sides of hey holder
boltOffset = 6.35;

//Diameter of thumb hole
thumbHoleDiameter = 50.8;
//Change number of facets for key thumb hole
thumHoleNumSides = 8; //[3:1:100]

//Chamfer Angle
chamferAngle=45;
//Chamfer Width
chamferWidth = 10;

module keyHolderBottom()
{

    difference(){
    union(){
    cube([Length, Width, Thickness], center = true);

    translate([0,Width*0.5-keySpaceWidth*0.5,Thickness*0.5+keyThickness*0.5]){
    cube([keySpacerLength, keySpaceWidth, keyThickness], center = true);
    }
    }

    translate([Length*0.5-boltOffset,0,0])
    {
    cylinder(r = boltDiameter*0.5, h = boltLength, center=true, $fn=100);
    }

    translate([-Length*0.5+boltOffset,0,0])
    {
    cylinder(r = boltDiameter*0.5, h = boltLength, center=true, $fn=100);
    }

    translate([Length*0.5, Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    translate([-Length*0.5, Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    translate([-Length*0.5, -Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    translate([Length*0.5, -Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    }

}

module keyHolderTop()
{

    difference(){
    cube([Length, Width, Thickness], center = true);

    translate([Length*0.5-boltOffset,0,0])
    {
    cylinder(r = boltDiameter*0.5, h = boltLength, center=true, $fn=100);
    }

    translate([Length*0.5-boltOffset,0,Thickness-boltHeadThickness])
    {
    cylinder(r = boltHeadDiameter*0.5, h = boltHeadThickness, center=true, $fn=100);
    }

    translate([-Length*0.5+boltOffset,0,Thickness-boltHeadThickness])
    {
    cylinder(r = boltHeadDiameter*0.5, h = boltHeadThickness, center=true, $fn=100);
    }

    translate([-Length*0.5+boltOffset,0,0])
    {
    cylinder(r = boltDiameter*0.5, h = boltLength, center=true, $fn=100);
    }

    translate([Length*0.5, Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    translate([-Length*0.5, Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    translate([-Length*0.5, -Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    translate([Length*0.5, -Width*0.5,0]){
    rotate([0,0,chamferAngle]){
    cube([chamferWidth, chamferWidth, Thickness+1], center= true);
    }
    }

    translate([0,-Width*0.5,0]){
    cylinder(r=thumbHoleDiameter*0.5, h=Thickness+1, center=true, $fn=thumHoleNumSides);
    }

    }

}

keyHolderBottom();

translate([0, Width*1.75, 0]){
keyHolderTop();
}

```


[User]
Hey, I'm looking to create some customizable miniature highway signs, kind of like those old Burma-Shave signs or a church hymn board, but with a few more text lines. I've seen some that have the speed limit on one line, and then a couple lines of text underneath, like old "Highway Hymns" signs. I'd like to be able to customize the text for each sign, the font size, and also have the option to print just the signs, or the signs with a post, or even just the sign posts separately.

Here's what I'm thinking for the basic structure:

*   **Signs:** I need six signs total. They should be rectangular and have a small post connector on each side. The signs themselves should be separate from the text so I can easily print them in two colors - the sign in one, and the text in another. The text should be recessed into the sign. I'd like to be able to specify up to three lines of text for each sign (except the first, which would only have one), with independent size control for each line. The first sign should have only one line of text in the center.
*   **Signposts:** I'd like two signpost options, tall and short. These should be simple, upright posts with a crossbar at the bottom for stability. The signs should be designed to press-fit onto the posts without glue.

I need the whole thing to be parametric, using OpenSCAD so I can customize it easily. It would also be cool to be able to preview all the signs and posts in a single view before printing, so I can verify how they will all look assembled.

I'd like to use the "Oswald-Regular" font.

Can you help me create the OpenSCAD code for this? I'm not sure about using any external libraries, I think this is simple enough for vanilla OpenSCAD.

[Assistant]
Customizable_Miniature_Highway_Hymns_Signs.scad:
```
// Customizable Miniature Highway Hymns Signs
// by RiverRatDC 2017
//
// Remix of http://www.thingiverse.com/thing:2289808
// by RiverRatDC 2017
//
// "Customizable Miniature Highway Hymns Signs" is licensed under a
// Creative Commons Attribution-NonCommercial 3.0 Unported License.
//

// Select the part you'd like to print:
part="Preview"; //[Preview, Signs, Tall Signpost, Short Signpost]

/* [Sign 1 Text] */

//Sign 1 Text
line1 = "HIGHWAY HYMNS";
//Sign 1 Text Size
line1_text_size = 6.5; //[4,4.5,5,5.5,6,6.5]

/* [Sign 2 Text] */

//Sign 2 Line 1 Text
2line1 = "55 |";
//Sign 2 Line 1 Text Size
2line1_text_size = 9; //[7,8,9]
//Sign 2 Line 2 Text
2line2 = "'GOD WILL TAKE";
//Sign 2 Line 2 Text Size
2line2_text_size = 5; //[4,4.5,5,5.5,6,6.5]
//Sign 2 Line 3 Text
2line3 = "CARE OF YOU'";
//Sign 2 Line 3 Text Size
2line3_text_size = 5; //[4,4.5,5,5.5,6,6.5]

/* [Sign 3 Text] */

//Sign 3 Line 1 Text
3line1 = "75 |";
//Sign 3 Line 1 Text Size
3line1_text_size = 9; //[7,8,9]
//Sign 3 Line 2 Text
3line2 = "'NEARER MY GOD";
//Sign 3 Line 2 Text Size
3line2_text_size = 4.5; //[4,4.5,5,5.5,6,6.5]
//Sign 3 Line 3 Text
3line3 = "TO THEE'";
//Sign 3 Line 3 Text Size
3line3_text_size = 5; //[4,4.5,5,5.5,6,6.5]

/* [Sign 4 Text] */

//Sign 4 Line 1 Text
4line1 = "85 |";
//Sign 4 Line 1 Text Size
4line1_text_size = 9; //[7,8,9]
//Sign 4 Line 2 Text
4line2 = "'THIS WORLD IS";
//Sign 4 Line 2 Text Size
4line2_text_size = 5; //[4,4.5,5,5.5,6,6.5]
//Sign 4 Line 3 Text
4line3 = "NOT MY HOME'";
//Sign 4 Line 3 Text Size
4line3_text_size = 5; //[4,4.5,5,5.5,6,6.5]

/* [Sign 5 Text] */

//Sign 5 Line 1 Text
5line1 = "95 |";
//Sign 5 Line 1 Text Size
5line1_text_size = 9; //[7,8,9]
//Sign 5 Line 2 Text
5line2 = "'LORD, I'M";
//Sign 5 Line 2 Text Size
5line2_text_size = 5; //[4,4.5,5,5.5,6,6.5]
//Sign 5 Line 3 Text
5line3 = "COMING HOME'";
//Sign 5 Line 3 Text Size
5line3_text_size = 5; //[4,4.5,5,5.5,6,6.5]

/* [Sign 6 Text] */

//Sign 6 Line 1 Text
6line1 = "100|";
//Sign 6 Line 1 Text Size
6line1_text_size = 8; //[7,7.5,8,8.5,9]
//Sign 6 Line 2 Text
6line2 = "'PRECIOUS";
//Sign 6 Line 2 Text Size
6line2_text_size = 5; //[4,4.5,5,5.5,6,6.5]
//Sign 6 Line 3 Text
6line3 = "MEMORIES'";
//Sign 6 Line 3 Text Size
6line3_text_size = 5; //[4,4.5,5,5.5,6,6.5]

/* [Hidden] */

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

print_part();

  /////////////
 // Modules //
/////////////

module print_part() {
    if (part == "Preview") {
		one_piece();
    } else if (part == "Signs") {
		signs();
    } else if (part == "Tall Signpost") {
		tall_signpost();
	} else if (part == "Short Signpost") {
		short_signpost();
	}
}
module one_piece(){
    color("black") Sign();
	color("black") translate([0,-28.0,0]) Sign();
	color("black") translate([0,-28.0*2,0]) Sign();
	color("black") translate([0,-28.0*3,0]) Sign();
	color("black") translate([0,-28.0*4,0]) Sign();
    color("black") translate([0,-28.0*5,0]) Sign();
	color("white") signpost();
	color("white") translate([0,0,1]) SignWords1(line1);
	color("white") translate([0,-28.0,1]) SignWords2(2line1,2line2,2line3);
	color("white") translate([0,-28.0*2,1]) SignWords3(3line1,3line2,3line3);
	color("white") translate([0,-28.0*3,1]) SignWords4(4line1,4line2,4line3);
	color("white") translate([0,-28.0*4,1]) SignWords5(5line1,5line2,5line3);
	color("white") translate([0,-28.0*5,1]) SignWords6(6line1,6line2,6line3);
}
module signs(){
	color("black") Sign();
	color("black") translate([0,-28.0,0]) Sign();
	color("black") translate([0,-28.0*2,0]) Sign();
	color("black") translate([0,-28.0*3,0]) Sign();
	color("black") translate([0,-28.0*4,0]) Sign();
    color("black") translate([0,-28.0*5,0]) Sign();
	color("white") translate([0,0,1]) SignWords1(line1);
	color("white") translate([0,-28.0,1]) SignWords2(2line1,2line2,2line3);
	color("white") translate([0,-28.0*2,1]) SignWords3(3line1,3line2,3line3);
	color("white") translate([0,-28.0*3,1]) SignWords4(4line1,4line2,4line3);
	color("white") translate([0,-28.0*4,1]) SignWords5(5line1,5line2,5line3);
	color("white") translate([0,-28.0*5,1]) SignWords6(6line1,6line2,6line3);
}
module signpost(){
	translate([-20,-75,-.76]) cube([3, 175, 2], center=true);
	translate([20,-75,-.76]) cube([3, 175, 2], center=true);
	translate([0,-161,6.25]) cube([65, 3, 16], center=true);
}
module tall_signpost(){
	translate([-20,-75,0]) cube([3, 175, 3], center=true);
	translate([20,-75,0]) cube([3, 175, 3], center=true);
	translate([0,-161,6.25]) cube([65, 3, 15.5], center=true);
}
module short_signpost(){
	translate([-20,-72,0]) cube([3, 50, 3], center=true);
	translate([20,-72,0]) cube([3, 50, 3], center=true);
	translate([0,-98,6.25]) cube([65, 3, 15.5], center=true);
}
module Sign(){
    difference(){
		color("black") cube([65, 25, 3.5], center=true);
        translate([-20,0,-1]) cube([3, 28, 2], center=true);
	    translate([20,0,-1]) cube([3, 28, 2], center=true);
    }
}
module SignWords1(sign_text){
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([0,0,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text, size=line1_text_size, font="Oswald-Regular", halign="center", valign="center");
		}
	}
}
module SignWords2(sign_text1,sign_text2,sign_text3){
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([-21,0,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text1, size=2line1_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text2, size=2line2_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,-4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text3, size=2line3_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
}
module SignWords3(sign_text1,sign_text2,sign_text3){
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([-21,0,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text1, size=3line1_text_size, font="Oswald-Regular", halign="center", valign="center");
		}
	}
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text2, size=3line2_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,-4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text3, size=3line3_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
}
module SignWords4(sign_text1,sign_text2,sign_text3){
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([-21,0,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text1, size=4line1_text_size, font="Oswald-Regular", halign="center", valign="center");
		}
	}
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text2, size=4line2_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,-4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text3, size=4line3_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
}
module SignWords5(sign_text1,sign_text2,sign_text3){
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([-21,0,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text1, size=5line1_text_size, font="Oswald-Regular", halign="center", valign="center");
		}
	}
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text2, size=5line2_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,-4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text3, size=5line3_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
}
module SignWords6(sign_text1,sign_text2,sign_text3){
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([-21,0,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text1, size=6line1_text_size, font="Oswald-Regular", halign="center", valign="center");
		}
	}
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text2, size=6line2_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
	intersection(){
		color("black") cube([65, 25, 3.5], center=true);
		translate([9.5,-4,3]) linear_extrude(height=5, center=true, convexity=10, twist=0) {
		text(sign_text3, size=6line3_text_size, font="Oswald-Regular", halign="center", valign="center");
        }
    }
}

```


[User]
I need a parametric OpenSCAD design for a spring guide for a 3D printer bed. The primary function is to keep the bed springs from moving around, and to slightly increase their tension. I'd like to be able to adjust the following parameters:

*   `tolerance`:  This controls the gap between the guide and the spring itself, to account for printer tolerances.
*   `inner_dia`: This is the inner diameter of the guide, which should match the outer diameter of the springs I'm using.
*   `bold_dia`: The diameter of the bolt that passes through the spring, which the guide also needs to accommodate.
*   `height`: The overall height of the guide.
*   `thickness`: The wall thickness of the guide.

The guide should be a hollow cylinder with a hole for the bolt in the center, and the internal space for the spring. The space for the spring should be slightly larger than the `inner_dia` by the `tolerance` value. I don't need any complex features, just a simple, functional guide. Could you provide the OpenSCAD code for this? Please ensure it is parametric so I can adjust those values. I also need the code to use the `$fn` variable for circle smoothness.

[Assistant]
spring_guide.scad:
```
// adjust this depend of how precise your printer is
tolerance = 0.4;

// inner diameter of the spring guide, == your spring diameter
inner_dia = 5;

// diameter of the bold
bold_dia = 2.8;

// height of the spring guide
height = 6;

// wall thickness of the guide
thickness = 2;

$fn=50;

difference() {
    // define some value
    cylinder(height, d = inner_dia + thickness);
    translate([0, 0, thickness])
        cylinder(height - thickness, d = inner_dia + tolerance);
    cylinder(height, d = bold_dia + tolerance + 0.1);
}
```


[User]
Hey, I'm looking to get a custom case designed for an X-Acto knife, and I'd like to use OpenSCAD so I can easily tweak it later.

Essentially, it's a two-part design - a base and a cover. The base needs to have a cavity for the knife body and a separate slot for the blade. The cover should fit snugly over the base. I want to use rounded corners for both parts, and I'd like the option to adjust the wall thickness and the clearance between the base and the cover.

Here are some specific dimensions and details:

*   The internal length of the base (including the blade slot) should be adjustable via a parameter, let's call it `inner_length`.
*   The internal width should also be parameterizable via `inner_width`.
*   The internal height of the case, named `inner_height` should also be a parameter.
*   There should be a parameter, `wall`, controlling the wall thickness, which should be consistent across all walls.
*   I need a clearance parameter, `clearance` for printer tolerance, which adjusts the fit between base and cover.
*   There needs to be an external step on the bottom of the base, defined by a parameter named `step_height`.
*   I want to control the corner radius with a parameter named `corner`.
*   The blade slot should be adjustable by length via `slot_length`.
*   The blade slot's width should be adjustable by parameter `slot_width`.

The base should have the step on its exterior bottom, and the main cavity for the knife, along with the blade slot. The cover should fit down over the base, with the step on the base acting as a stop.

I'd prefer to use the `minkowski()` operation to create the rounded corners, as I saw in an example and it's pretty cool. I'd like to see functions named `rounded_cube` (which should take the length, width, height and corner radius as arguments), `base()` and `cover()` modules to define the main components. The final part should be a `main()` module to output the combined base and cover, with a translation so they're not overlapping. Please take into account that there will be multiple clearances for printing, so don't forget to add them on the relevant positions.

Also, for the rounding radius calculations, I think we'll need to use a value named `eps1` to do a little bit of "fudging" to avoid errors. If it helps, `eps1` should equal some small number such as 0.01.

I'd like to use standard OpenSCAD functions and no external libraries if possible. Could you generate that for me?

[Assistant]
xacto_box.scad:
```
// X-Acto knife case.

// Roundness resolution.
$fn = 90;

eps1 = 0 + 00.01;
eps2 = 2*eps1;

// Base inner length, including the blade slot.
inner_length = 153;

// Base inner width
inner_width = 12;

// Base inner height
inner_height = 12;

// Wall thickness
wall = 1.6;

// Space betwene parts to compensate for printer tolernace.
clearance = 0.3;

// The height of the bottom step outside the base.
step_height = 3;

// External corner radius.
corner = 4;

// Blade slot length, should be longer than the visible length fo the blade.
slot_length = 28;

// Width of blade slot.
slot_width = 3;

// Centered at x=0, y=0. On z=0 plane.
module rounded_cube(xlen, ylen, zlen, r) {
  translate([r-xlen/2, r-ylen/2, 0]) minkowski() {
    cube([xlen-2*r, ylen-2*r, zlen/2]);
    cylinder(r=r, h=zlen/2);
 }
}

module base() {
  difference() {
    // Add solid body
    union() {
      rounded_cube(
         inner_length + 4*wall + 2*clearance,
         inner_width + 4*wall + 2*clearance,
         step_height,
         corner);

       rounded_cube(
          inner_length + 2*wall,
          inner_width + 2*wall,
          inner_height + wall,
          max(eps1, corner - wall - clearance));
    }

    // Remove cavity
    translate([slot_length/2, 0, wall])
    rounded_cube(
        inner_length - slot_length,
        inner_width,
        inner_height + eps1,
        max(eps1, corner - wall - clearance - wall));

    // Remove blade slot
    translate([-inner_length/2, -slot_width/2, wall])
        cube([slot_length+eps1, slot_width, inner_height+eps1]);
  }
}

module cover() {
  difference() {
    rounded_cube(
       inner_length + 4*wall + 2*clearance,
       inner_width + 4*wall + 2*clearance,
       inner_height + 2*wall - step_height,
       corner);

    translate([0, 0, wall])
    rounded_cube(
       inner_length + 2*wall + 2*clearance,
       inner_width + 2*wall + 2*clearance,
       inner_height + eps1,
       max(eps1, corner - wall - clearance));
  }
}

module main() {
  translate([0, 40, 0]) base();
  cover();
}

main();

```


[User]
I'd like to design a reducing coupling, something that looks like a cylinder with a smaller diameter cylinder cut out of the middle, and then a smaller cylinder cut out of the bottom of that. I'm thinking the outside should be a cylinder, then a smaller-diameter cylinder that goes partway in, and finally another even-smaller cylinder goes the rest of the way in.

I need to be able to control the following parameters:

*   **Outer Diameter:** The diameter of the main cylinder (the outside of the coupling).
*   **Height:** The overall height of the coupling.
*   **Cut Diameter:** The diameter of the first inner cylinder (the one that goes partway in).
*   **Inner Diameter:** The diameter of the final, smallest, inner cylinder.
*   **Inner Depth:** The depth of the smallest inner cylinder.

I'd like to control the number of facets used to create each circle/cylinder. Also, I would like to be able to override the default setting. I am not sure exactly how best to handle that part.

I would also like to create multiple instances of this coupling; that is, I want to be able to specify a number of couplings and have them be laid out in a single row on the X axis. I don't need to control their separation distance, I think three times the outer diameter is good for separation.

Finally, as a bit of a bonus, I would like to be able to put a flat section on one side of the main cylinder using a cube. I want to specify that flat section as a cube that's centered on the vertical axis and is on the side of the cylinder. I'd like it to be some fraction of the overall height. I am thinking like, one quarter.

I'm envisioning using the OpenSCAD customizer for all this, so I want all of these options to be available there. I would like to use as few libraries as possible, preferably just the basic OpenSCAD functionality.

[Assistant]
rcouple.scad:
```
/* [Main Dimensions] */
// Outside diameter (mm)
out_dia=50;
// Height (mm)
height=45;
// Cut diameter (mm)
cut_dia=30;

/* [Inner Dimensions] */
// Inner diameter (mm)
in_dia=21;
// Depth (mm)
in_depth=10; // [5,10,15,20,25]
/* [Options] */
//  Line segments for circles (3 for triangles, 6 for hex, etc.)
FN=100; // [3:1:25]
// Override line segement count (any positive value)
OFN=0;

$fn=OFN?OFN:FN;

/* [Hidden] */
Num_copies=2;
// Num_copies=1+0; // stop customizer from picking this up

for (i=[1:Num_copies])
translate([(i-1)*out_dia*3,0,0])
difference()
{
  cylinder(center=false,r=out_dia/2, h=height);
   translate([0,0,-1])
     cylinder(center=false,r=cut_dia/2,h=1+height-in_depth);
   translate([0,0,,height-in_depth])
     cylinder(center=false,r=in_dia/2,h=1+in_depth);
}/* [Main Dimensions] */
// Outside diameter (mm)
out_dia=50;
// Height (mm)
height=45;
// Cut diameter (mm)
cut_dia=30;

/* [Inner Dimensions] */
// Inner diameter (mm)
in_dia=21;
// Depth (mm)
in_depth=10; // [5,10,15,20,25]
/* [Options] */
// Line segments for circles (3 for triangles, 6 for hex, etc.)
FN=100; // [3:1:25]
// Override line segement count (any positive value)
OFN=0;

$fn=OFN?OFN:FN;

/* [Hidden] */
Num_copies=2;
// Num_copies=1+0; // stop customizer from picking this up

for (i=[1:Num_copies])
  translate([(i-1)*out_dia*3,0,0])
    difference()
    {
      union()
      {
      cylinder(center=false,r=out_dia/2, h=height);
      translate([-out_dia/2,-out_dia/2,height/2-height/8])
      cube([out_dia,out_dia,height/4]);
     }
  translate([0,0,-1])
  cylinder(center=false,r=cut_dia/2,h=1+height-in_depth);
  translate([0,0,,height-in_depth])
  cylinder(center=false,r=in_dia/2,h=1+in_depth);
  }
```


[User]
I'm looking to design a small, cylindrical piggy bank specifically for safely disposing of used X-Acto blades, particularly the #2 size and smaller.  I want it to have a narrow slot for inserting the blades, and ideally, some sort of internal guard to make it difficult to remove them after insertion. It would also be great if it had an option for a magnet to be included mid-print to further secure the blades.

Here are the specific dimensions and features I'm thinking of:

*   **Overall Shape:** Cylindrical with rounded edges on the top and bottom.
*   **External Diameter:** Around 40mm
*   **External Height:** Approximately 62mm
*   **Wall Thickness:** A consistent 2mm
*   **Rounded Edges:** Use a fillet of 3mm on the cylinder faces
*   **Slot Opening:**  A slot that is 15mm long and 3mm wide.
*   **Internal Guard:**  A small guard inside the slot, extending about 5mm in height.
*   **Magnet Option:** I'd like to be able to pause the print and insert a small magnet halfway through the print, if desired, to hold the blades inside.

I'd like to be able to customize these parameters in the OpenSCAD file. I will be printing this with PLA, so keep that in mind with respect to the model's design. The printing orientation should have the slot at the bottom.

I am imagining using the standard `hull()` and `minkowski()` functions in OpenSCAD. No need to use any custom libraries. Can you generate the OpenSCAD file for this design, taking into account the parameters specified above?

[Assistant]
blade_bank.scad:
```
// A piggy bank for used X-Acto blades #2 and smaller.

eps1 =  0+0.01;
eps2 = 2*eps1;

// Roundness
$fn=60;

// External diameter
d = 40;

// External height.
h = 62;

// Rounding on cylinder faces.
filet = 3;

// Thickness of all walls.
wall = 2;

// Length of the slot opening.
slot_length = 15;

// Width of the slot opening.
slot_width = 3;

// Length of the internal slot guard.
guard_height = 5;

// Set to 1 for debugging, 0 for printing.
debugging = 0;

module elongated_cylinder(d, h, l) {
  hull() {
    dy = max(0, (l-d)/2);
    translate([0, dy, 0]) cylinder(d=d, h=h);
    translate([0, -dy, 0]) cylinder(d=d, h=h);
  }
}

module rounded_cylinder(d, h, r) {
  translate([0, 0, r])
  minkowski()
  {
  cylinder(d=d-2*r, h=h-2*r);
    sphere(r=r);
  }
}

module main() {
  difference() {
    union() {
    difference() {
      // Add external cylinder
      rounded_cylinder(d, h, filet);
      // Remove internal cavity
      translate([0, 0, wall])
        rounded_cylinder(d-2*wall, h-2*wall, filet);
    }
    // Add internal slot guard
    translate([0, 0, wall-eps1])
        elongated_cylinder(slot_width+2*wall, guard_height, slot_length+2*wall);
  }
    translate([0, 0, -eps1])
      elongated_cylinder(
          slot_width,
          wall+eps2+guard_height,
          slot_length);
  }
}

//translate([0, 0, h])
//rotate([0, 180, 0])
render(1)
rotate([0, 0, 180])
difference() {
  main();
  if (debugging != 0) {
    translate([-100, 0, -100]) cube([200, 200, 200]);
  }
}

```


[User]
I'd like to design a model of a ship, specifically something reminiscent of the Boston Tea Party ships, but with a space on the back to add some text. I've seen a couple of designs I like, and I'd like you to help me make my own parametric version in OpenSCAD.

Here’s what I’m thinking:

**Overall Design:**

*   It should be a ship model, generally following the style of a classic sailing ship - kind of like a simplified galleon or brigantine, but not too detailed.
*   The ship should have a hull, masts, and sails. I'd like the hull to be rounded on the bottom.
*   There needs to be a large, relatively flat area on the back of the ship's hull where text can be displayed. This text area should be customizable; ideally, we could adjust the spacing, size, font, and what is written.
*   I also want to include a way to add some crates - I'll call these "cargo" - around the ship as a separate optional design element or as a separate model entirely; I'll be using these for tabletop gaming purposes in the same scale range as the ship, and I'd like them to be small and relatively numerous. I might want to adjust their number and spacing too.

**Specific Features and Parameters:**

*   **Ship Size:** The overall size of the ship should be easily adjustable using a single parameter.
*   **Hull:**
    *   The hull should have a rounded bottom and a flat back for the text.
    *   There should be parameters to control the length, width, and height, as well as the overall curvature of the hull.
*   **Masts and Sails:**
    *   The number and height of masts should be adjustable.
    *   The size of the sails should scale with the mast height.
    *   I’d like an option for the masts to have angled support struts or a more simplified support structure if angled supports are too complex. There should be an option for no supports as well.
*  **Text:**
    * The text should be added to the flat back of the ship and should be easily customizable.
    *   It should be possible to adjust the size of the text, font, vertical spacing between lines, and the total number of text lines.
    *  There should be an offset parameter so I can make the text a certain distance from the back of the hull.
*   **Cargo:**
    *   The crates should be a simple cube-like shape, maybe with some basic surface texture.
    *   The size of individual crates and the total number of crates should be adjustable, and I need a spacing parameter too.
*   **Units:** Everything should be designed to be easily printable without supports, so we need to take overhangs into account (with angled struts).

**Libraries:**

I'd like you to use the `build_plate` module in my `utils/build_plate.scad` file, which I will include as a separate file, and can add to the root directory for the project.

**Output:**

I'd like the output to be modular, with a main function that allows me to choose if I render the ship, the cargo crates, or both. I’d also like it to be easy to use a single parameter to control the size of everything (with different scale parameters for things that are meant to be proportional to other design elements). So I’m envisioning a single .scad file for the ship, plus a separate one for the crates.

[Assistant]
tea_ship.scad:
```
//
// Boston Tea Party Memorial Ship Model
// version 1.0   5/28/2017
// (DaoZenCiaoYen @ Thingiverse)
//
// Includes (modified) Pirate Ship by MakerBlock
// https://www.thingiverse.com/thing:12856
//
// Imports Crate prop for DnD by Sinar
// https://www.thingiverse.com/thing:45107
//

st=2; // support type 0=none, 1=sails, 2=angled
show_text=true;
model=1; // 1=ship, 2=crates

oy=-68; // text offset to back of ship
ls=5.3; // line spacing
tz=6;   // top z
ts=4.5; // text size

t = [ // Massachusetts state motto
    ["Ense petit",0,oy,tz],
    ["placidam",0,oy,tz-ls],
    ["sub",0,oy,tz-ls*2],
    ["libertate",0,oy,tz-ls*3],
    ["quietem",0,oy,tz-ls*4]];

use <utils/build_plate.scad> ;
//for display only, doesn't contribute to final object
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
//when Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 120.65; //[100:400]
//when Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 120.65; //[100:400]
show_build_plate=true;

main();

module main()
{
    if (model==1)
        scale(1.05) rotate(45) translate([0,0,31]) tea_ship();
    else
        crates();
}

module crates()
{
    nx=5; // number in x direction
    ny=nx; // number in y direction
    s=10; // spacing
    cs=0.4; // crate scale

    for ( x=[-s*nx/2+s/2:s:s*nx/2-1] )
        for (y=[-s*ny/2+s/2:s:s*ny/2-1] )
            translate([x,y,0]) crate(cs);
}

module crate(cs) {
    // crate.STL below from
    // Crate prop for DnD by Sinar
    // https://www.thingiverse.com/thing:45107
    scale(cs) import("crate.STL");
}

module tea_ship()
{
    difference() {
        scale(2) translate([0,-6.7,0]) ship();
        if (show_text)
            words();
    }
}

module words() {
    for (i= [0:len(t)-1])
        line(t[i][0],t[i][1],t[i][2],t[i][3]);
}

module line(t,x,y,z)
{
  translate([x,y,z]) rotate([90,0,0]) linear_extrude(3)
    text(t,ts,font = "Liberation Sans:style=Bold",
      halign="center",valign="center");
}

//
// From OpenSCAD Pirate Ship by MakerBlock
// https://www.thingiverse.com/thing:12856
//

th = 0.6;
shipscale=60;
fn=32;

module ship(size=shipscale)
{
sails(size);
hull(size);
rudder(size);
bowsprit(size);
//% translate([0,0,size/4]) cube(size, center=true);
}

module rudder(rh)
{
//intersection()
//    {
    translate([0,rh*-24.9/64,rh*-10.14/64]) cube([th*2,rh*5/32,rh*13/64], center=true);
//    translate([0,rh*-33/64,rh*-1/64]) rotate([0,90,0]) cylinder(r=rh*8/32,h=th,$fn=fn);
//    }
}

module bowsprit(bs)
{
translate([0,bs*36/64,bs*3/64]) rotate([290,0,0])
intersection()
    {
    scale([0.5,1,1]) cylinder(r1=bs*8/64,r2=th,h=bs/4,$fn=fn/4,center=true);
    translate([0,bs/2,0]) cube(bs,center=true);
    }
}

module hull(hl)
{
difference()
{
scale([1.5,1,1.2])
difference()
    {
    translate([0,hl*-1/32,-hl*3/32]) union()
        {
        scale([0.7,2.1,1]) translate([0,0,-hl/12])
            cylinder(r1=hl/6, r2=hl/4, h=hl/6, $fn=fn,center=true);

        difference()
            {
            scale([0.7,2.1,1]) translate([0,0,hl/12])
                cylinder(r=hl/4, h=hl/6, $fn=fn,center=true);
            difference()
                {
                scale([0.7,2.1,1]) translate([0,0,hl/12+th/4])
                    cylinder(r=(hl/4) -th, h=hl/6, $fn=fn,center=true);
                translate([0,-hl*13/32,hl/32]) cube([hl,hl/4,hl/4],center=true);
                translate([0,-hl*12/32,-hl*2/32]) cube([hl,hl/4,hl/4],center=true);
                translate([0,hl*14/32,-hl*2/32]) cube([hl,hl/4,hl/4],center=true);
                translate([0,-hl*15/32,hl/32]) cube([hl,hl/8,hl/2],center=true);
                }
            translate([0,hl/4.5,hl/1.7]) cube(hl, center=true);
            translate([0,hl*1/32,hl*10/32]) cube(hl/2, center=true);
            }
        }
    translate([0,-hl*17/32,hl/32]) cube([hl,hl/8,hl/2],center=true);
    };
translate([0,0,-hl/3.1]) cube([hl,hl,hl/8],center=true);
}
}

module sails(mh)
{
translate([0,0,mh*-4/62])
    {
    // crows nest
    translate([0,0,mh*23.5/32])
        {
            difference() {
            cylinder(r1=mh*1.8/64,r2=mh*3/64,h=mh*4/64,$fn=fn/2,center=true);
//    translate([0,0,mh*53/64])
            translate([0,0,mh/54])
            cylinder(r1=th/2,r2=mh*2/64,h=mh*3/64,$fn=fn/2,center=true);
            }
        }
    // front sail
    translate([0,mh/4.4,0]) rigging(mh*5/6);
    translate([0,0,0]) rigging(mh);
    translate([0,-mh/4.4,0]) rigging(mh*4.8/6);
    *translate([0,mh*15/32,mh*8/32]) rotate([205,0,0]) scale([1,1,2]) rotate([0,90,0])
        cylinder(r=mh/5,h=th,$fn=3,center=true);
    //center support
    translate([0,mh*-1/32,-mh*2/32]) cube([th*3,mh*13/16,mh/8],center=true);
    }
}

module rigging(mh=30)
{
// mast poles
translate([0,0,-mh/10]) cylinder(r=mh/36,h=mh*10.1/12,$fn=fn/2);
// angled supports
if (st==2)
{
    translate([0,0,-mh/18]) angled_support(mh/2);
}
rotate([0,-90,0])
translate([mh/2,0,0]) union()
    {
    *translate([0,-mh/64,0]) intersection()
        {
        difference()
            {
            scale([1,0.67,1]) translate([-mh/6,0,0])
                cylinder(r=mh/1.5,h=th,$fn=3,center=true);
            translate([0,-mh*5/6,0]) rotate([0,0,27.5]) scale([2,1,1])
                cylinder(r=mh/1.5,h=th+1,$fn=fn,center=true);
            }
        translate([0,-mh*1/1.9,0]) rotate([0,0,-27.5]) scale([2,1,1])
            cylinder(r=mh/1.5,h=th+1,$fn=fn,center=true);
        }
    translate([0,mh/16,0]) mast(mh);
    }
}

module angled_support(mh=30)
{
    intersection() {
        union() {
            translate([0,0,0]) rotate([45,0,0]) cube([mh/12,mh/3,mh/3],center=true);
            translate([0,0,0]) rotate([45,0,90]) cube([mh/12,mh/3,mh/3],center=true);
        }
        translate([0,0,mh/12]) cube([mh/2,mh/2,mh/6],center=true);
    }
}

module mast(mh=30)
{
translate([-mh/14,0,0])
scale([0.7,1,1])
{
    // bottom
    translate([-mh/3.75,0,0]) sail(mh/4);
    translate([mh/8,-th*1.5,0]) sail(mh/8);
    translate([mh*5/15.5,-th*2,0]) sail(mh/16);
    // sail type support
    if (st==1)
        translate([-mh/1.408,-mh/30,0]) sail(mh/6.4);
}
}

module sail(size)
{
intersection()
    {
    scale([1.5,1,1]) translate([0,-size,0]) difference()
        {
        cylinder(r=size, h=size*5/2,$fn=fn,center=true);
        cylinder(r=size-th, h=size*5/2+1,$fn=fn,center=true);
        }
    cube(size*2.2,center=true);
	translate([size*2,0,0]) scale([2,1,1])
        rotate([0,45,0]) cube(size*3,center=true);
    translate([size/3,0,0]) scale([2,1,1]) rotate([90,0,0])
        cylinder(r=size*1,h=size*2,$fn=fn,center=true);
    }
}

if (show_build_plate)
{
  build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);
}

```


[User]
I'm looking for a parametric OpenSCAD design for a gyroscope toy, similar to the one I have, but I'd like to request a new implementation.  I want to be able to control several aspects of it through parameters.

First, it should be made of several nested rings. I need to be able to set:

*   The inner radius of the smallest ring.
*   The height of all the rings.
*   The number of rings.
*   The width of each ring.
*   The clearance between the rings.

Also important are a few advanced settings:

*   The clearance in the joints, to account for machine inaccuracies.
*   The option to choose between cylindrical rings and spherical sections (which is a slow option, I know).
*   The joint depth as a percentage of the ring width.
*   The angle offset between the joints.  (90 would be the classic gimbal).
*   The number of segments for curves - this should be exposed, but set to a reasonable default, like 100.

The design should calculate the spacing between rings, the cone radius for joints and the pin height. It should render using `convexity=2` and `difference()`, with the ring created and holes and pins for the joints created separately.  The design needs functions for `cone`, `ring`, and `moveCone`, as well.  The `moveCone` function needs to make sure to duplicate the cones in 180-degree rotations.

Could you generate the OpenSCAD file for this, please?

[Assistant]
gyro.scad:
```
/* [Basic] */
//basic parameters
//Inner radius of smallest ring
innerRingRadius = 20; // [0:0.5:150]
innerR = innerRingRadius;
//Height of rings
height = 4;
//# of rings
number = 3; // [2:1:10]
//Width of each ring
ringWidth = 4; // [3:0.25:8]
//Clearance between rings
ringClearance = 2; // [0.25:0.25:5]

/* [Advanced] */
//advanced parameters
//Clearance in joints, increase on bad machine
jointClearance = 0.2; // [0.1:0.025:0.5]
//Should rings be cylinders or sections of spheres (very slow)?
ringType = 0; // [0:Cylinders, 1:Spherical Sections]
sphericalRings = ringType == 1;
//Joint depth as % of ring width
jointOverlap = 75; // [50:5:90]
overlap = jointOverlap / 100;
//angle difference between each joint (90 is classic gimbal)
angleOffset = 90;
//# of segments (more is much slower)
$fn = 100;

//calculated parameters
spacing = ringWidth + ringClearance;
coneR = 0.9 * height / 2; //target cone radius
pinH = ringWidth*overlap + ringClearance - jointClearance;

for (i = [0:number-1]) {
	r = innerR + spacing*i;
	jointPoint = r + ringWidth*(1-overlap);
	render(convexity=2) difference() {
		ring(r);
		//make hole on all but last segment
		if (i != number-1)
			moveCone(jointPoint, angleOffset*i) cone(coneR/overlap,ringWidth);
	}
	//make pin on all but first segment
	if (i != 0)
		moveCone(jointPoint-spacing+jointClearance, angleOffset*(i-1)) cone(coneR, pinH);
}

module cone(r, h) {
	translate([h,0,0])
	rotate([0,-90,0])
	cylinder(h=h, r1=r, r2=0);
}

module ring(r) {
	if (sphericalRings) {
		intersection() {
			difference() {
				sphere(r + ringWidth);
				sphere(r);
			}
			cylinder(height, r=r+ringWidth+1, center=true);
		}
	} else {
		translate([0,0,-height/2]) linear_extrude(height) difference() {
			circle(r + ringWidth);
			circle(r);
		}
	}
}
module moveCone(r, theta) {
	for (i = [0,180]) {
		rotate([0,0,theta+i])
		translate([r,0,0])
		children();
	}
}
```


[User]
I'd like to create a custom fidget spinner, and I want it to be parametric so I can easily change things. It should have three arms, and each arm should have some text on it. I need the option to choose between generating the entire spinner with text, just the base spinner without text, or just the text parts for a dual-extrusion print.

Specifically, I want these features:

*   **Build Mode:** I need a variable called `BUILD` which can be set to `"SINGLE"` for a single-extrusion print (spinner and text together), `"TEXT"` for just the text parts, or `"BASE"` for just the base spinner without text.
*   **Text Customization:** I need three variables called `WORDS1`, `WORDS2`, and `WORDS3`, each holding a string of text. Each string should appear on one of the three arms of the spinner. The font should be changeable using a variable `FONT`, and I'd like it to work with fonts from fonts.google.com.  I need a variable called `TWEAKHEIGHT` to adjust the text height. I need `TWEAKX`, `TWEAKY`, and `TWEAKZ` variables to let me adjust the text position in each direction.
*   **Spinner Dimensions:** The spinner should have a central hole for a standard 608zz bearing (roughly 22.5mm diameter) and three outer holes. I want to be able to adjust the hole diameters using a variable called `TWEAKHOLE`, so I can make them slightly bigger or smaller.
*   **Spinner Base:** The base structure should have three arms with cutouts for some kind of visual design, maybe some triangles. I also need an outer ring to help reinforce the bearings.
*  **Color**: The text should always be red

I'm expecting it to be all in OpenSCAD, and I'm not familiar with any libraries, so please write it from scratch using only built in modules.

Could you please generate the code for this?

[Assistant]
customMini.scad:
```

// Choose "SINGLE" for single extruder, "TEXT" for the text-only part of a dual-extrusion and "BASE" for the base-only part of a dual extrusion.
BUILD = "SINGLE"; // [SINGLE, TEXT, BASE]
//BUILD = "TEXT";
//BUILD = "BASE";

// Each line will appear on a separate arm of the spinner
WORDS1 = "CUSTOM";
WORDS2 = "TRIPLE";
WORDS3 = "SPINNER";

// Any fonts from fonts.google.com should work, just be sure to type the name exactly
FONT = "Arial Rounded MT Bold";

// Adjust the height of the text if needed
TWEAKHEIGHT = 0;

// Adjust how much the text sticks out
TWEAKX = 0;

// Move text to the left or right
TWEAKY = 0;

// Move text up or down
TWEAKZ = 0;

// Make the holes bigger (e.g. 0.1) or smaller (e.g. -0.1)
TWEAKHOLE = 0;

/* Created by Edwin Donnelly, MD, PhD. May 6, 2017 */

if (BUILD == "BASE")
{
   difference()
   {
       spinner();
       addText();
   }

}
else if (BUILD == "TEXT")
{
       addText();
}
else
{
 spinner();
 addText();
}

module addText()
{
    caption(WORDS1);
    rotate([0,0,120]) caption(WORDS2);
    rotate([0,0,-120]) caption(WORDS3);
}

module base()
{
  rod();
  rotate([0,0,120]) rod();
  rotate([0,0,-120]) rod();
  outers();
}

module spinner()
{
   difference()
   {
    base();
    holes();
   }
}

module holes()
{
    holeC();
    holeP();
    rotate([00,0,120]) holeP();
    rotate([00,0,-120]) holeP();
}

module holeC()
{
    cylinder(d=22.5+TWEAKHOLE, h=10, $fn=200, center=true);
}

module holeP()
{
    translate([0,30,0]) holeC();
}

module outerHC()
{
    cylinder(d=29, h=7, $fn=200, center=true);
}
module outerHP()
{
    translate([0,30,0]) outerHC();
}
module outers()
{
    outerHC();
    outerHP();
    rotate([00,0,120]) outerHP();
    rotate([00,0,-120]) outerHP();
}

module rod()
{
    difference()
    {
      translate([7,15,0]) cube([15,30,7], center=true);
      translate([10,18,0]) rotate([0,0,180]) triangle();
    }
}

module triangle()
{
    POINT0 = [0,0,-7];
    POINT1 = [6.06,3.5,-7];
    POINT2 = [0,7,-7];

    POINT3 = [0,0,7];
    POINT4 = [6.06,3.5,7];
    POINT5 = [0,7,7];

    polyhedron (
      points = [POINT0, POINT1, POINT2, POINT3, POINT4, POINT5],
      faces = [
         [0, 1, 2],
         [1,3,4,2],
         [3,1,0,5],
         [2,4,5,0],
         [3, 5, 4] ] );
}

module caption(WORDS)
{
   color("red")
    translate([13+TWEAKX,-3+TWEAKY,-3+TWEAKZ])
    rotate([90,0,90])
     linear_extrude(height=2 )
      text(WORDS, size= 6+TWEAKHEIGHT, font=FONT);
}

```