[User]
I'm looking to create a parametric design for an infinity cube toy in OpenSCAD, and I need a bit of help setting it up. I've seen some existing designs, but I want something fully customizable and with a few specific features.

Here's the breakdown:

**Overall Structure:**

*   The infinity cube is made up of 8 smaller cubes connected by hinges.
*   The hinges are designed to be assembled using a piece of standard 1.75mm filament as an axis, so there needs to be a hole sized correctly for that.
*   The design should be parametric, so I can easily change the overall size, wall thickness, and other dimensions.
*   I want to be able to easily render individual cube pieces, all the cubes together in a flat layout for printing, or a fully assembled layout for visualization.

**Cube Design:**

*   Each individual cube should have a main body with flat faces, with cutouts for the hinges.
*   The design should include fillets where appropriate for strength and aesthetics.
*   The cutouts for the hinges should have a cylindrical part and a square part, with extra clearance around the filament axis for a smooth rotation.
*   There should be a small "mark" on each hinge cutout to help with the initial assembly. This mark could be a small sphere or other shape.
*   Each cube will have two hinge cutouts, one on each of the opposite faces where the hinges would attach.

**Hinge Design:**

*   The hinge should be a separate piece that connects two cubes.
*   It should have cylindrical parts that fit into the cutouts on the cubes.
*   There should be a hole through the hinge for the filament to pass through, again with a bit of extra clearance.
*   The hinge should have a flat middle section that sits against the cube face.

**Parameters (at a minimum):**

*   `CubeSide`: The length of one side of the main cube.
*   `FilDia`: The diameter of the filament used for the hinge axis (should default to 1.75mm).
*   `HingeHole`: The diameter of the hole for the filament in the hinge (should be a little larger than FilDia).
*   `WallThickness`: The thickness of the cube walls.
*   `Gap`: The space between the cubes and hinges to allow for movement.
*  `HoleGap`: The extra space around the filament hole.
* `PartToRender`: A variable controlling which parts to render (e.g., "Assembly", "All", "Cube 1", etc.)

**Functionality:**

*   I'd like to have a `MainCube` module that generates one of the main cube pieces with the hinge cutouts, taking a `type` argument (1-4) to correctly position the hinge cutouts.
*   There should be a `Hinge` module for the hinge piece itself.
*   A `RotatedCube` module that combines a main cube and its hinges in the correct position for a single assembly.
*   I need a main rendering block that uses the `PartToRender` variable to output the requested parts, either a fully assembled cube, all the separate pieces in print orientation, or an individual cube.

I'm hoping you can generate one or more OpenSCAD files to achieve this functionality.  I don't have any specific library requirements beyond those that are built-in, and I'm happy to have the final code be broken up into multiple files if it makes things clearer, but it'd be nice to have a single file for the full assembly that calls the others. Thanks!

[Assistant]
InfinityCube.scad:
```
/**
 * Project Name: Infinity cube
 *
 * Author: Carlos Eduardo Foltran
 * GitHub: https://github.com/Nartlof/InfinityCube
 * Thingiverse: https://www.thingiverse.com/thing:6249758
 * License: Creative Commons CC0 1.0 Universal (CC0 1.0)
 * Description: Infinity cube toy that requires assembly and can be made with mixed collors.
 *
 * Date Created: 2023-09-28
 * Update: 2023-10-01
 * Last Updated: 2023-10-04
 *
 * This OpenSCAD file is provided under the Creative Commons CC0 1.0 Universal (CC0 1.0) License.
 * You are free to use, modify, and distribute this design for any purpose, without any restrictions.
 *
 * For more details about the CC0 license, visit: https://creativecommons.org/publicdomain/zero/1.0/
 */

// Main cube side
CubeSide = 20;
// Filament diameter (use a piece of filament as hinge axis)
FilDia = 1.75;
// Diameter of the hole in the hinges (increase for a looser fit)
HingeHole = 2;
// Wall thickness
WallThickness = 2;
// Gap between cubes
Gap = 0.4;
// Extra space on the holes
HoleGap = 0.4;

// Part to render
PartToRender = "Assembly"; //["Assembly", "All", "All tight", "Cube 1", "Cube 2", "Cube 3", "Cube 4", "Hinge"]

// Create hinge with individual cubes
AddHinge = true;

$fa = ($preview) ? $fa : 2;
$fs = ($preview) ? $fs : .2;

module MainCube(side = CubeSide, wall = WallThickness, hole = HingeHole, filDia = FilDia, gap = Gap, holeGap = HoleGap,
                type = 1)
{
    module mainCube()
    {
        face = side - 2 * fillet;
        hull()
        {
            for (i = [[side, face, face], [face, side, face], [face, face, side]])
                cube(i, center = true);
        }
    }

    // The mark parameter tells if it is the hinge to be assembled first
    module hingeCutOff(mark = false)
    {
        cutOffHeigth = side - 2 * (fillet + wall) + 2 * gap;
        cutOffDiameter = hole + 2 * (wall + gap);
        expandedFilDia = filDia + holeGap;
        translate(v = [ (side - cutOffDiameter) / 2 + gap, 0, cutOffDiameter / 2 - gap - side / 2 ])
            rotate([ -90, 0, 0 ]) translate(v = [ 0, 0, -cutOffHeigth / 2 ])
        {
            // Cuttoff for the fillament inserted as hinge axis
            translate(v = [ 0, 0, -(side - cutOffHeigth - 1) / 2 + fillet ]) union()
            {
                cylinder(h = side + 1, d = expandedFilDia);
                // Putting a truncated part on the end to hold the fillament by friction
                rotate([ 180, 0, 0 ]) difference()
                {
                    cylinder(h = expandedFilDia, d = expandedFilDia, center = false);
                    rotate([ 0, 0, 225 ]) translate(v = [ -expandedFilDia / 2, expandedFilDia / 2, 0 ])
                        rotate([ 45, 0, 0 ]) cube([ expandedFilDia, expandedFilDia, 2 * expandedFilDia ]);
                }
            }
            // Round part
            cylinder(h = cutOffHeigth, d = cutOffDiameter);
            // The marking for assembly
            if (mark)
            {
                rotate([ 0, 0, 225 ]) translate(v = [ cutOffDiameter / 2, 0, cutOffHeigth / 2 ])
                    sphere(d = min(wall, cutOffHeigth));
            }
            // Square part
            for (i = [-1:1])
            {
                rotate([ 0, 0, i * 90 ]) cube(size = [ cutOffDiameter / 2, cutOffDiameter / 2, cutOffHeigth ]);
            }
        }
    }

    // Defining the appropriated fillet for the geometry
    fillet = wall + hole / 2;
    // mainCube();
    color(c = (type == 1) ? "blue" : (type == 2) ? "red" : (type == 3) ? "green" : "yellow") difference()
    {
        mainCube();
        hingeCutOff(mark = true);
        if (type == 1)
        {
            rotate([ 180, 0, 90 ]) hingeCutOff();
        }
        else if (type == 2)
        {
            rotate([ 0, 90, 0 ]) rotate([ 0, 0, 90 ]) hingeCutOff();
        }
        else if (type == 3)
        {
            rotate([ -90, 0, 0 ]) rotate([ 0, 0, 180 ]) hingeCutOff();
        }
        else
        {
            rotate([ 180, 0, 270 ]) hingeCutOff();
        }
    }
}

module Hinge(side = CubeSide, wall = WallThickness, hole = HingeHole, gap = Gap, holeGap = HoleGap)
{
    fillet = wall + hole / 2;
    hingeHeigth = side - 2 * (fillet + wall);
    difference()
    {
        union()
        {
            cube([ 2 * fillet + gap, 2 * fillet, hingeHeigth ], center = true);
            for (i = [ -1, 1 ])
                translate([ i * (2 * fillet + gap) / 2, 0, 0 ])
                    cylinder(h = hingeHeigth, d = 2 * fillet, center = true);
        }
        for (i = [ -1, 1 ])
            translate([
                i * (2 * fillet + gap) / 2,
                0,
            ]) cylinder(h = hingeHeigth + 1, d = hole + holeGap, center = true);
    }
}

// Main part
// This part of the code just put parts on a apropriated position for printing

module RotatedCube(type = 1, addHinge = AddHinge)
{
    Rotations = [ [ 0, -45, 0 ], [ 180, 45, 0 ], [ 0, 135, 180 ], [ 0, -45, 0 ] ];
    translate([ 0, 0, (CubeSide - (WallThickness + HingeHole / 2)) * sqrt(2) / 2 ]) rotate(Rotations[type - 1])
        MainCube(type = type);

    if (addHinge)
    {
        translate(v = [
            Gap + WallThickness + HingeHole / 2 + (CubeSide - WallThickness - HingeHole / 2) * sqrt(2) / 2, 0,
            (CubeSide - 4 * WallThickness - HingeHole) / 2
        ]) rotate([ 0, 0,
                    90 ]) Hinge(side = CubeSide, wall = WallThickness, hole = HingeHole, gap = Gap, holeGap = HoleGap);
    }
}

if (PartToRender == "Assembly")
{

    types = [ 1, 2, 3, 4, 4, 3, 2, 1 ];
    rotations =
        [ [ 0, 0, 0 ], [ 3, 0, 0 ], [ 0, 0, 0 ], [ 2, 0, 1 ], [ 0, 2, 1 ], [ 0, 0, 2 ], [ 3, 0, 2 ], [ 0, 0, 2 ] ];

    for (i = [0:3])
        for (j = [0:1])
        {
            translate(v = [ i, j, 0 ] * (CubeSide + Gap)) rotate(rotations[i + 4 * j] * 90) union()
            {
                MainCube(type = types[i + 4 * j]);
                translate(v = [ (CubeSide + Gap) / 2, 0, -(CubeSide - HingeHole) / 2 + WallThickness ])
                    rotate([ 90, 0, 0 ]) Hinge();
            }
        }
}
else if (PartToRender == "Hinge")
{
    Hinge();
}
else if (PartToRender[0] == "C")
{
    RotatedCube(type = ord(PartToRender[5]) - 48, addHinge = AddHinge);
}
else if (PartToRender == "All tight")
{
    for (i = [0:7])
    {
        translate(v = [

            floor(i / 3) *
                (sqrt(2) * (CubeSide - WallThickness - HingeHole / 2) + 2 * WallThickness + HingeHole + 2 * Gap),
            i % 3 * (CubeSide + WallThickness), 0
        ]) RotatedCube(type = i % 4 + 1);
    }
}
else if (PartToRender == "All")
{
    for (i = [0:1])
        for (j = [0:3])
        {
            translate(v = [
                j * (CubeSide + WallThickness),
                i * (sqrt(2) * (CubeSide - WallThickness - HingeHole / 2) + 2 * WallThickness + HingeHole + 2 * Gap), 0
            ]) rotate([ 0, 0, 90 ]) RotatedCube(type = j + 1);
        }
}
```


[User]
Okay, I'd like to create a parametric design for a shoelace clip, something that lets you quickly loosen shoelaces without untying them. I've got a basic idea of what I want, but need some help making it flexible.

The clip should be essentially a mirrored design that clamps onto a section of shoelace. Here's the breakdown:

*   **Core Shape:**  The main body of each half of the clip should be a mirrored extrusion (using linear_extrude). It should feature a sort of rounded-rectangular shape with a circular cutout for the shoelace. One end should have a "lip" that curves inward towards the shoelace. It should be designed so two mirrored halves can clamp together.

*   **Mirrored Design:** The two halves of the clip are mirror images of each other.

*   **Shoelace Channel:**  There needs to be a channel (created by the circle cutout and lip) where the shoelace sits when the clip is closed.

*   **Text Customization:** Each side of the clip should have embossed text. This needs to be customizable for text, font, size, and embossing depth.

*   **Parametric Needs:**  I want to be able to control the following:
    *   `outer_r`: The outer radius of the main circle making up the clip body.
    *   `inner_r`: The radius of the main shoelace channel.
    *  `inner_r2`: A second, smaller radius used for a cutout near the edge of the clip.
    *   `length`:  The overall length of the clip extrusion.
    *  `angle`: The angle of the cut at the clamping end of the clip.
    *   `width`:  The width of the linear extrusion of each half.
    *   `size`:  The font size for the text.
    *   `emboss`: The depth of the text emboss.
    *   `text1`: The text on one half of the clip.
    *   `text2`: The text on the other half of the clip.
    *   `font`: The font for both texts.
*   **Function:** The design should clamp two shoelace sections together so that the shoelace will not slip out or move on the clip. The design will allow a user to loosen the shoe by sliding the clip along the shoelace, creating slack, rather than fully untying the lace.

I think that about covers it. Can you generate an OpenSCAD file for this? I do not require any libraries.

[Assistant]
shoelace.scad:
```
text1="BART";
text2="LISA";
font="Liberation Sans:style=Bold";
size=4;
emboss=0.6;
outer_r=4.5;
inner_r=2;
inner_r2=1.5;
length=10;
angle=30;
width=5;
$fn=48;

module clip(){
  mirror([1,0,0])translate([length/2,0,0])linear_extrude(width){
    difference(){
      circle(outer_r);
      circle(inner_r);
    }

    translate(-[outer_r+inner_r2,0,0])difference(){
      square([outer_r+inner_r2,outer_r]);
      circle(inner_r2);
      translate([outer_r+inner_r2,0,0])circle(inner_r);
    }

    translate([-length,0,0])difference(){
      circle(outer_r);
      circle(inner_r2);
      square(outer_r);
      rotate(angle-90)square(outer_r);
    }

    translate([-length,0])rotate(angle)translate([0,-(outer_r+inner_r2)/2])
     circle((outer_r-inner_r2)/2);

    translate([-length,inner_r2,0])
     square([length-inner_r,outer_r-inner_r2]);
  }
}

translate([0,outer_r+inner_r2/2,0])difference(){
  clip();
  translate([0,outer_r-emboss,width/2])
   rotate([90,0,180])linear_extrude(emboss+1/64)
    text(text1,halign="center",valign="center",size=size,font=font);
}

rotate(180)translate([0,outer_r+inner_r2/2,0])difference(){
  mirror([1,0,0])clip();
  translate([0,outer_r-emboss,width/2])
   rotate([90,0,180])linear_extrude(emboss+1/64)
    text(text2,halign="center",valign="center",size=size,font=font);
}

```


[User]
I'm looking to create a parametric housing for a few different types of mouse traps, primarily for outdoor use to protect other animals, like birds, from getting caught. I'd like the design to be customizable through OpenSCAD.

Here's the basic idea: the housing needs an entrance tunnel leading to a larger trap room that contains the actual mouse trap. The trap room should be shaped based on the specific trap being used and have a sliding lid that's easy to print.

I'd like to be able to configure the following:

*   **Entrance Tunnel:**
    *   Length: adjustable.
    *   Height: adjustable.
    *   Width: a fixed value, but it should be specified as a constant to avoid magic numbers.
*   **Walls & Edges:**
    *   Thickness: adjustable for all walls, lid edge etc..
    *   Clearance: adjustable, to control how well the lid slides.
*   **Trap Room:**
    *   Shape: I have a few different types of traps I'd like to support:
        *   "Swiss Inno SuperCat": This one needs a "horseshoe" shaped room with a specific length, width, and height.
        *   "HG HorseShoe": Similar to the SuperCat, but with different dimensions. Also a horseshoe shape.
        *   "HG Box": A box-shaped room, again with specific dimensions. It also needs a small fillet radius on the corners of the box's ceiling.
        *   "Windhager SNAP":  A box shape like the HG Box with specific dimensions, and also with a small fillet radius on the corners of the box's ceiling.
    *   Dimensions: Each trap type has a different length, width, and height. These will need to be predefined in the code.
    *   Transition length: the distance between the entrance and the tRoom. Should be tailored for each trap.
*   **Lid:**
    *   The lid should slide in from the back, enclosing both the back of the trap room, and any potential exit from the trap room (so, both 'ends' need to be closeable by the lid).
*   **Display Options:**
    *   I want to be able to display just the housing, just the lid, or both.
    *   I'd like to be able to use a percentage slider to hide parts of the design in the x, y, and z dimensions so I can see inside the model.
*   **Test Printing:**
    *   I need a free-floating cube object, used for test printing and easy debugging. It needs to be toggle-able, positionable (x, y, and z) and a specific size to be bigger than all other objects.

I'd like to have all of these options adjustable through OpenSCAD's customizer interface. So, be sure to use variables that I can change in the interface, and also variables that are constants that I can't change in the interface (for magic numbers etc.). Also, all dimensions should be in mm.

I'm not looking to use any external libraries at this point.

Could you help me design this parametric housing? I'm interested in how you'd structure the code.

[Assistant]
housingMouseTrap.scad:
```
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++ Author: Frans Nijhuis, 13 November 2023 ++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Indentation style: K&R
//
// Programming in OpenScad is simular to eating food containing hairs, sand and stones.
//
// OpenScad claims to be 'unit less' thus nor imperial or metric units.
//   On my PC it turned out to be metric in mm when loaded into FreeCad or Cura.
//   On your PC... who knows, it could be imperial in inches or feet or...
//
// Printer simulator: https://zupfe.velor.ca/
//

// The length of the entrance tunnel to the trap tRoom.
entranceLength = 90;      //[20:0.1:100]

// The height of the entrance tunnel.
entranceHeight = 38;      //[38:0.1:60]

// The thickness of all walls and edge.
wallThickness = 3.0;      //[1:0.1:5]

// The fit clearance between lid and housing.
clearance = 0.28;         //[0.1:0.01:0.4]

// The number of facets used to generate an arc.
resolution = 50;          //[25:1:256]

// The trap room shape: horseshoe or box
trapMake = "Swiss Inno SuperCat";  //[Swiss Inno SuperCat,HG HorseShoe,HG Box,Windhager SNAP]

// Display part Housing, Lid or Housing + Lid ?
displayPart = "Housing + Lid";    //[Housing,Lid,Housing + Lid]

// Show how much % of the length.
showLength = 100;          //[0:1:100]

// Show how much % of the height.
showHeight = 100;         //[0:1:100]

// Show how much % of the width.
showWidth = 100;          //[0:1:100]

// Free floating cutter block. Used for test printing.
showCutter = "False"; // [True,False]
cutX = 0;            //[-200:1:200]
cutY = 0;            //[-200:1:200]
cutZ = 100;          //[-200:1:200]

//------------------------------------------
module customizer_Break(){};  // Variables below this line are constants and NOT customisable.
                              // Its a documented feature of OpenScad...
//------------------------------------------

 // An attempt to prevent magic numbers in the code.
entranceWidth = 36;

// The trap room is the room in which the mouse trap is located. ROOM INTERNAL sizes !!
// Sizes are based on setting/releasing the trapped mouse without removing the trap from the house.
//                [ length, width, height, shape,   radius, transitionLength]
attributeTrap = [ [     67,    56,     61, "HorseShoe",  0,  20],   // 0 Swiss Inno SuperCat
                  [     91.5,  47,     70, "HorseShoe",  0,  35],   // 1 HG HorseShoe
                  [     91.5,  47,     70, "Box",        8,  35],   // 1 HG Box
                  [     91.5,  52,     70, "Box",        8,  35] ]; // 2 Windhager SNAP

// TransitionLength tailored for printing without support.

makerIndex = trapMake == "Swiss Inno SuperCat" ? 0 : trapMake == "HG HorseShoe" ? 1 : trapMake == "HG Box" ? 2 : 3 ;

tRoomLength = attributeTrap[makerIndex][0];
tRoomWidth  = attributeTrap[makerIndex][1];
tRoomHeight = attributeTrap[makerIndex][2];
tRoomShape  = attributeTrap[makerIndex][3];
filletRadiusTRoom = attributeTrap[makerIndex][4];
transitionLength  = attributeTrap[makerIndex][5];
// transitionLength = Distance between entrance and tRoom, SwissInno tailored wrt its outline !!

//------------------------------------------

builder();

//------------------------------------------

module builder(){
    echo();

    // Maximum space to hide with showHeight and showWidth
    hideLength = entranceLength + transitionLength + tRoomLength + 2*wallThickness + clearance;
    hideWidth  = tRoomWidth + 8*wallThickness;
    hideHeight = tRoomHeight + 7*wallThickness;

    if (showCutter == "True"){
        translate([-1.5*wallThickness + cutX, -3*wallThickness + cutY, -wallThickness + cutZ]){
             cube([hideLength, hideWidth, hideHeight], center=false);}
    }
    difference(){
        composer();

        if ( showLength < 100){  // Only when something to hide
            translate([-1.5*wallThickness + hideLength * showLength / 100.0, -3*wallThickness, -wallThickness]){  // showLength
                cube([hideLength, hideWidth, hideHeight], center=false);}
        }

        if ( showHeight < 100){  // Only when something to hide
            translate([-1.5*wallThickness, -3*wallThickness, hideHeight * showHeight / 100.0]){  // showHeight
                cube([hideLength, hideWidth, hideHeight], center=false);}
        }

        if (showWidth < 100) {  // Only when something to hide
            translate([-1.5*wallThickness, -3*wallThickness - hideWidth * showWidth / 100.0, -wallThickness]){  // showWidth
                cube([hideLength, hideWidth, hideHeight], center=false);}
        }

        translate([-1.5*wallThickness + cutX, -3*wallThickness + cutY, -wallThickness + cutZ]){
             cube([hideLength, hideWidth, hideHeight], center=false);}

    }

//    translate([wallThickness, wallThickness, wallThickness]){
//      cube([91.5, 47, 55], center=false);}

    echo();
}

module buildVolume( buildLength = 0, buildWidth  = 0, buildHeight = 0){
    echo(str("Estimateded build volume (length x width x height): ",buildLength," x ",buildWidth," x ",buildHeight, " mm."));
    echo();
}

module composer(){
    // The edge for the 'sliding' lid. The edge is wallThickness + minimum clearance wide.
    minimumClearance = 0.1;  // To give the edge a little extra.

    edge = wallThickness + minimumClearance;
    edgeWidth  = tRoomWidth + 2*wallThickness + 2*edge; // left + right edge
    edgeHeight = tRoomHeight + 2*wallThickness + edge;  // Only top edge

    union(){
        if (displayPart == "Lid"){
            buildVolume( 3*wallThickness, 6*wallThickness + tRoomWidth, 3*wallThickness + tRoomHeight);
            lid(edgeWidth, edgeHeight);

        } else if (displayPart == "Housing"){
            buildVolume( tRoomLength + transitionLength + entranceLength,
                        4*wallThickness + tRoomWidth, 2*wallThickness + tRoomHeight);;
            housing(edgeWidth, edgeHeight);

        } else {
            buildVolume( tRoomLength + transitionLength + entranceLength + wallThickness,
                        6*wallThickness + tRoomWidth, 3*wallThickness + tRoomHeight);
            lid(edgeWidth, edgeHeight);
            housing(edgeWidth, edgeHeight);
        }
    }
}

module tunnelShape (length, width, height, who = "unknown", roomShape = "HorseShoe", filletRadius = filletRadiusTRoom){
    // This is the base shape which is used for all 'parts'.
    if (roomShape == "HorseShoe") {
        difference(){
            union(){
                cube([length, width, height-width/2.0], false);
                translate([0, width/2.0, height-width/2.0])
                    rotate([0, 90, 0])
                            cylinder(h=length, d=width, $fn=resolution, center = false);
            }
            if (width > height) {  // Is the cylinder (d=width) bigger then the height ? Cut it off.
                echo(str("* Flatten cylinder bottom for: '",who,"', width=", width,", height=", height));
                delta = width - height;
                translate([-0.5 * wallThickness, 0, -delta])
                    cube([length + wallThickness, width, delta], false);
            }
        }
    } else {  // "Box"
        union(){
           cube([length, width, height - filletRadius], false);
           translate([0, filletRadius, height - filletRadius])
                  cube([length, width - 2*filletRadius, filletRadius], false);

           translate([0, width - filletRadius, height - filletRadius])
                rotate([0, 90, 0])
                        cylinder(h=length, d= 2 * filletRadius, $fn=resolution, center = false);
           translate([0, filletRadius, height - filletRadius])
                rotate([0, 90, 0])
                        cylinder(h=length, d= 2 * filletRadius, $fn=resolution, center = false);

        }
    }
}

module lid(edgeWidth, edgeHeight){
    // A lid is needed to close the back entrance and the emergency exit (for the trapped mouse).
    translate([-wallThickness - clearance, -2*(wallThickness + clearance), 0]) {
        difference(){
            tunnelShape(3*wallThickness + 2*clearance,     // Outer shape
                        edgeWidth + 2*(wallThickness + clearance),
                        edgeHeight + wallThickness + clearance,
                        "Lid, outer shape",
                        tRoomShape,
                        filletRadiusTRoom + 3*wallThickness + 2*clearance);

            translate( [wallThickness, wallThickness, 0] ){ //Largest cut.
                tunnelShape(wallThickness + 2*clearance,
                            edgeWidth + 2*clearance,
                            edgeHeight + clearance,
                            "Lid, inside cut for edge",
                            tRoomShape,
                            filletRadiusTRoom + 2*wallThickness + clearance);
            }
            translate( [wallThickness, 2*wallThickness, 0] ){  // Smallest cut.
                tunnelShape(4*wallThickness,
                               edgeWidth - 2*wallThickness + 2*clearance,
                               edgeHeight - wallThickness + clearance,
                            "Lid, outeside cut for housing",
                            tRoomShape,
                            filletRadiusTRoom + 2*wallThickness + clearance);
            }
        }
    }
}

module housing(edgeWidth, edgeHeight){
    // For a tigth fit of the trap in the tRoom we need some support beams.
    supBeamWidth = 4;
    supBeamHeight = 2.5;
    supBeamLength = 40;

    doorstepWidth = 2.5;
    doorstepHeight = 11;

    union(){
        difference(){
            outerShape(edgeWidth, edgeHeight);
            translate([0, wallThickness, wallThickness])
                innerShape();
        }

        translate([0, wallThickness, 0]) cube([doorstepWidth, tRoomWidth, doorstepHeight ], center=false); // tRoomStep
        if ( trapMake == "Swiss Inno SuperCat" ) { // Needed because of its external tension spring.

            translate([supBeamHeight, wallThickness, wallThickness])                             // sidesupBeam right
                cube([supBeamLength, supBeamWidth, supBeamHeight ], center=false);
            translate([supBeamHeight, tRoomWidth + wallThickness - supBeamWidth, wallThickness]) // sidesupBeam left
                cube([supBeamLength, supBeamWidth, supBeamHeight ], center=false);
        }
    }
}

module outerShape(edgeWidth, edgeHeight){
    // Hull() bridges gaps but from right side tRoom to right side entrance, which I want to be the left side entrance.
    // A trick to prevent that is to use a 1 unit length entrance which also adds to the hull's wall thickness.
    trickLength = 1;
    hull(){
        tunnelShape(tRoomLength + trickLength, tRoomWidth + 2*wallThickness, tRoomHeight + 2*wallThickness,
                        "outerShape hull begin", tRoomShape, filletRadiusTRoom + 2*wallThickness); // tRoom
        translate ([tRoomLength + transitionLength, (tRoomWidth - entranceWidth)/2, 0])
            tunnelShape( trickLength, entranceWidth + 2*wallThickness, entranceHeight + 2*wallThickness,
                        "outerShape hull end"); // transition trick
    }

    translate ([tRoomLength + transitionLength, (tRoomWidth - entranceWidth)/2.0, 0])
        tunnelShape( entranceLength, entranceWidth + 2*wallThickness, entranceHeight + 2*wallThickness, "outerShape"); // entrance

    translate([0, -(wallThickness + clearance), 0])
        tunnelShape(wallThickness, edgeWidth, edgeHeight, "outerShape edge", tRoomShape, filletRadiusTRoom + 2*wallThickness);  // edge for the lid
}

module innerShape(){
    // The length, on both sides, should be a bit longer to get a proper cut.
    // Hull() bridges gaps but from right side tRoom to right side entrance, which I want to be the left side entrance.
    // A trick to prevent that is to use a 0.01 unit length entrance, not adding to the hull's wall thickness.
    trickLength = 0.01;
    hull(){
        translate([-wallThickness, 0 , 0] )
            tunnelShape(tRoomLength + wallThickness, tRoomWidth, tRoomHeight, "innerShape hull begin", tRoomShape, filletRadiusTRoom + wallThickness);
        translate ([tRoomLength + transitionLength, (tRoomWidth - entranceWidth)/2.0, 0])
            tunnelShape( trickLength, entranceWidth, entranceHeight, "innerShape hull end");
    }
    translate ([tRoomLength + transitionLength, (tRoomWidth - entranceWidth)/2.0, 0])
        tunnelShape( entranceLength + wallThickness, entranceWidth, entranceHeight, "innerShape");
}

```


[User]
I'd like to create a customized soap dispenser based on a skull. I have a soap dispenser bottle (Balea brand, but I want it to be adaptable) and I want to attach a skull to the top so it looks like the soap is coming out of the skull's head. I'd need a parametric OpenSCAD design that lets me adjust things like the skull's size and position, the size and shape of the connection to the soap bottle, and the outlet where the soap comes out of the skull.

Specifically, the basic idea is to take a skull STL file, cut out a hole in it that matches the opening of my soap dispenser, and then add a spout to the skull's top for dispensing. I've got a basic design idea sketched out in the `SoapSkull_V4.scad` file but want to make the design much more robust and customizable.

Here's what I need to be able to control:

*   **Skull:** The design needs to import an STL file for the skull. I should be able to scale and translate the skull's position.
*   **Soap Bottle Connection:** There should be a cylindrical cutout in the base of the skull, which matches the diameter of the soap bottle's neck. I need to be able to adjust the diameter and depth of this cutout and the wall thickness where the skull connects to the bottle.
*   **Dispenser Outlet:** The outlet needs to have a somewhat complex shape: a depressed part matching the soap bottle outlet, an undepressed part, and a spout. The spout needs to be customizable in size and position and consist of a main outlet and a connection to the base part. I need to be able to adjust the heights and diameters of all parts and their positions relative to each other. I need this to be a complete union that can be subtracted from the skull.
*   **Overall Positioning:** I need to be able to adjust the position of the soap bottle within the skull and adjust the height of the entire structure.
*   **Wall Thickness:** I need to be able to control the wall thickness, especially around the soap bottle connection.

I'd really like this to be all controlled by variables in the OpenSCAD file so I don't have to mess with the code each time I need to make an adjustment. It should be clear what each variable does and what it controls.

For the libraries, I think it would be useful to include the `MCAD` library for potentially some helper modules that might make the design cleaner (e.g. `hull`). I am not sure if this would be the best library, please use your judgement.

I am aiming for a user friendly design where each parameter is well documented. Let me know if you have any questions.

[Assistant]
SoapSkull_V4.scad:
```
$fn=32;

wiggle=.2;

shifty=-3;
scaleskull=1.1;

wall=1.5;

//skull
skullfile="skulltop4fix_MeshmixerMakeSolid.stl";

//soap bottle
STL_soapbottle="soapbottle_balea.stl";
center_soapbottle=[5,-301.5,-73];

//neck
d_soapneck=33;
depress=13;
depressstart=-3;
soap_shiftz=-2.5;
//outlet

module neck(delta,depress)
{
    translate([0,0,0])
        cylinder(d=d_soapneck+2*delta,h=depress);
}

module dispenser(height)
{
            translate([0,0,15])
                scale([25,34,height])
                    cylinder(d=1,h=1);
}

module outlet()
{
    translate([0,0,soap_shiftz])
    {
        //depressed
        translate([0,0,depressstart])
            neck(0,depress);

        //undepressed
        translate([0,0,-depress])
            neck(0,depress);

        //top outlet
        hull()
        {
            w_tip=9.5;
            h_tip=4.5;
            translate([0,-35,21-h_tip/2])
                cube([w_tip,.1,h_tip],center=true);
                //sphere(d=9);

            dispenser(10);
        }

        hull()
        {
            dispenser(.1);
            neck(0,depress);
        }

        //thin tube
        cylinder(d=15,h=25);

    }
}

*outlet();

translate(center_soapbottle)
    %import(STL_soapbottle);

difference()
{
    union()
    {
        translate([0,shifty,0])
            scale(scaleskull)
                import(skullfile);
        neck(wall,depress/2);
    }

    outlet();
}

//import(skullfile);

```


[User]
Hey, I'm looking to design a small, hinged box using OpenSCAD. I have some ideas about the general shape and features, and I'd like to make it parametric so I can easily adjust the size later.

Basically, the box should consist of two parts: a lid and a base. Both parts will have the same basic shape - a rectangular prism with rounded edges. The key is that the dimensions I enter should represent the *inside* dimensions of the box. The walls, lid and base will need a given thickness that is added to the outer dimensions. I'd like to control the wall thickness of the lid and base separately as they may need to be different.

Here's the breakdown of the features and parameters I need:

*   **Inside Dimensions:**
    *   `length`: The inside length of the box.
    *   `width`: The inside width of the box.
    *   `lid_depth`: The inside depth of the lid.
    *   `bottom_depth`: The inside depth of the base.

*   **Wall Thicknesses:**
    *   `lid_wall_thickness`: The thickness of the lid's walls, floor and top.
    *   `bottom_wall_thickness`: The thickness of the base's walls and floor.

*   **Rounded Edges:** The outside edges of both the lid and base should be rounded. I'd like the amount of rounding to be a separate parameter:
    * `edge_radius`: The radius of the rounded edges

*   **Hinge Mechanism:**
    *   The lid and base will be joined by a hinge along one of the width edges, and I’ll want two (for smaller box lengths) or four hinges (for larger box lengths) in the same place.
    *   Each hinge needs to have a space for a 3mm nut and a 3mm x 12mm machine screw to act as the pivot. I'd also like to be able to use a printable, friction-fit pivot pin instead of the metal hardware, so please provide that as an option.
    *   The hinges should be robust, and I'd like a small "guide" feature on the lid side of each hinge to help align the lid and base during assembly.

*   **Latching Mechanism:**
    *   I’d like a simple latching mechanism on the opposite edge of the box from the hinges. This should be a friction fit, where one part has a small hook and the other part has a corresponding lip.
    * The latch should be integrated into the main box body and shouldn't require separate screws or glue.

*   **Additional Requirements:**
    * The design should be suitable for 3D printing, and I'd prefer to use standard OpenSCAD modules where possible. No special or extra libraries are needed.
    *   All parameters should be easily adjustable at the top of the file.
    *   I want all the parameters to have default values (make them reasonable box sizes, maybe 50mm for length, 25mm for width, 10mm for lid depth and 20mm for bottom depth, 2mm wall thicknesses, and 3mm for edge radius).
    *   I don't need any internal dividers or other complexities inside the box itself.

Could you generate the OpenSCAD file(s) for me with these features? I'm thinking it will be easier if the lid and base are in separate modules.

[Assistant]
box3.scad:
```
length=50;//[26:120]
width=25;//[9:120]
lid_depth=10;//[5:0.5:60]
bottom_depth=20;//[5:0.5:60]

///////

translate([0,0,length/2+3])
rotate([0,90,0])
box(length,width,lid_depth*2);
module box(ix,iy,iz){
$fn=50;

union(){
difference(){
hull(){//outer shell
for(x=[-ix/2,ix/2])
for(y=[-iy/2,iy/2])
translate([x,y,0])
difference(){
cylinder(r=3,h=iz/2+3);

rotate_extrude()
translate([9,0])
circle(r=8,$fn=4);
}
}
translate([0,0,iz/2+3])//inside cavity
cube([ix,iy,iz],center=true);
}
difference(){//hinges
if(ix<34){
for(i=[-3,-1,1])
translate([i*(4.2)+0.1,iy/2+1,0])
union(){
cube([4,10,iz/2+3]);
translate([0,6,iz/2+3])
rotate([0,90,0])
cylinder(r=4,h=4);
}
}
else {
for(i=[-3,-1,1,3])
translate([i*(4.2)+0.1,iy/2+1,0])
union(){
cube([4,10,iz/2+3]);
translate([0,6,iz/2+3])
rotate([0,90,0])
cylinder(r=4,h=4);
}
}

translate([0,iy/2+7,iz/2+3])
rotate([0,90,0])
cylinder(r=1.8,h=34,center=true);

translate([0,iy/2+11,0])
rotate([0,90,0])
cylinder(r=6,h=34,$fn=4,center=true);

translate([6.6,iy/2+7,iz/2+3])
rotate([0,-90,0])
cylinder(r=2.8/cos(30),h=2.5,$fn=6);
}

difference(){
translate([0.1,-iy/2-6,iz/2-1])
cube([6,3,8]);
translate([3.1,-iy/2-3,iz/2+5])
sphere(r=0.8);

translate([0,-iy/2-3,iz/2+7])
rotate([0,90,0])
cylinder(r=1,h=7,$fn=4);

translate([0,-iy/2-6,iz/2-1])
rotate([0,90,0])
cylinder(r=3,h=7,$fn=4);
}

translate([0.1,-iy/2-3.2,iz/2-0.6])
cube([6,.4,3.6]);
translate([-3.1,-iy/2-3,iz/2+1])
sphere(r=0.8);
}

}

translate([lid_depth+10,0,length/2+3])
rotate([0,90,0])
box2(length,width,bottom_depth*2);
module box2(ix,iy,iz){
$fn=50;

union(){
difference(){
hull(){//outer shell
for(x=[-ix/2,ix/2])
for(y=[-iy/2,iy/2])
translate([x,y,0])
difference(){
cylinder(r=3,h=iz/2+3);

rotate_extrude()
translate([9,0])
circle(r=8,$fn=4);
}
}
translate([0,0,iz/2+3])//inside cavity
cube([ix,iy,iz],center=true);
}
difference(){//hinges
if(ix<34){
for(i=[-3,-1,1])
translate([i*(4.2)+0.1,iy/2+1,0])
union(){
cube([4,10,iz/2+3]);
translate([0,6,iz/2+3])
rotate([0,90,0])
cylinder(r=4,h=4);
}
}
else {
for(i=[-3,-1,1,3])
translate([i*(4.2)+0.1,iy/2+1,0])
union(){
cube([4,10,iz/2+3]);
translate([0,6,iz/2+3])
rotate([0,90,0])
cylinder(r=4,h=4);
}
}

translate([0,iy/2+7,iz/2+3])
rotate([0,90,0])
cylinder(r=1.8,h=34,center=true);

translate([0,iy/2+11,0])
rotate([0,90,0])
cylinder(r=6,h=34,$fn=4,center=true);

translate([6.6,iy/2+7,iz/2+3])
rotate([0,-90,0])
cylinder(r=2.8/cos(30),h=2.5,$fn=6);
}

difference(){
translate([0.1,-iy/2-6,iz/2-1])
cube([6,3,8]);
translate([3.1,-iy/2-3,iz/2+5])
sphere(r=0.8);

translate([0,-iy/2-3,iz/2+7])
rotate([0,90,0])
cylinder(r=1,h=7,$fn=4);

translate([0,-iy/2-6,iz/2-1])
rotate([0,90,0])
cylinder(r=3,h=7,$fn=4);
}

translate([0.1,-iy/2-3.2,iz/2-0.6])
cube([6,.4,3.6]);
translate([-3.1,-iy/2-3,iz/2+1])
sphere(r=0.8);
}

}
```


[User]
I'm looking to create a customizable universal key in OpenSCAD, similar to the design I've seen online. I need it to be parametric, allowing me to adjust the key's shape (triangle, square, or hexagonal), outer diameter, inner diameter (for the chosen shape), and overall length.

Specifically, I'd like to define:

*   `TYPE`: This should be a string parameter allowing me to choose between "triangle", "square", or "hexagonal".
*   `DIAMETER_EXT`: This will be the outer diameter of the key in millimeters.
*   `DIAMETER_INT`: This will be the diameter of the circle that passes through all the points of the inner shape (triangle, square or hexagon) in millimeters. Importantly, it needs to be correctly related to the actual size of the triangle, square or hexagon I want to use - for example a 5mm square key should have DIAMETER_INT=5/cos(PI/4) = 7.07 mm.
*   `LENGTH`: This will be the length of the key shaft, not including any handle/grip.

The key should have a cylindrical shaft with the specified outer diameter and length. The inner shape (triangle, square, or hexagon) should be subtracted from the cylinder to create the key profile. I would also like the key to have a rounded tip and a textured handle with a couple of fixing holes as in the model I've seen. The handle should be integrated into the design, not as a separate piece.

I'd like the design to be robust, with good parametric control. I don't need anything overly fancy, just a functional and well-defined universal key. Can you create the OpenSCAD code for this? I do not have any library dependencies, so no need to worry about including external libraries.

[Assistant]
universal_key.scad:
```
////////////////////////////////////////////////////////////////////////////////
//                              UNIVERSAL KEY                                 //
//                               Version 1.1                                  //
//                           BY JULIEN COPPOLANI                              //
//                        julien.coppolani@gmail.com                          //
////////////////////////////////////////////////////////////////////////////////

// This version has been edited to work with thingiverse customizer

/* [General Settings] */

// IGNORE THIS PARAMETER (IT IS USED FOR CUSTOMIZER TO WORK CORRECTLY)
Test = 0; // [0,1]

// TYPE OF KEY
TYPE="square"; // [triangle,square,hexagonal]

// EXTERNAL DIAMETER OF KEY IN MILLIMETERS
DIAMETER_EXT=9.5;

// DIMENSION OF THE SHAPE (triangle, square or hexagonal) IN MILLIMETERS
DIAMETER_INT=7.3;

// THE DIMENSION OF THE SHAPE CORRESPONDS TO THE DIAMETER
// OF THE CIRCLE THAT PASSES THROUGH ALL POINTS OF THE SHAPE
// EXAMPLE : FOR A 5MM SQUARE KEY, THE DIAMETER_INT WILL BE :
// DIAMETER_INT = 5MM / COS(PI/4) = 7.07MM
// FOR A 6MM SQUARE KEY, THE DIAMETER_INT WILL BE : 8.5MM

// LENGTH OF THE KEY (WITHOUT THE UPPER PART FOR GRIP)
LENGTH=12;

/* [Hidden] */
D=DIAMETER_EXT;
Ep=D/4;

// MAIN
if ( D > DIAMETER_INT && LENGTH >= 0)
{
	difference()
	{
		union()
		{
			cylinder(LENGTH, d=DIAMETER_EXT, $fn=15*D/ln(D));
			translate([0, 0, LENGTH])
				sphere(DIAMETER_EXT/2, $fn=15*D/ln(D));

			// GRIP
			translate([0, 0, LENGTH])
				rotate([90, 0, 0])
					hull()
					{
						translate([-D/3, 0, 0])
							cylinder(Ep, d=D/10, center=true);
						translate([-4*D/5, D, 0])
							cylinder(Ep, d=D, center=true, $fn=15*D/ln(D));
						translate([4*D/5, D, 0])
							cylinder(Ep, d=D, center=true, $fn=15*D/ln(D));
						translate([D/3, 0, 0])
							cylinder(Ep, d=D/10, center=true);
					}
		}
		translate([0, 0, -1])
		rotate([0, 0, 45])
		if (TYPE=="triangle") {
			cylinder(LENGTH, d=DIAMETER_INT, $fn=3);
		}
		else if (TYPE=="square") {
			cylinder(LENGTH, d=DIAMETER_INT, $fn=4);
		}
		else if (TYPE=="hexagonal") {
			cylinder(LENGTH, d=DIAMETER_INT, $fn=6);
		}
		if (LENGTH < D/2)
			translate([0, 0, -D])
				cylinder(D, d=D*1.1);
		// HOLES FOR FIXING
		translate([-4*D/5, 0, LENGTH+D])
			rotate([90, 0, 0])
				cylinder(D/3, d=D/2.5, center=true, $fn=6*D/ln(D));
		translate([4*D/5, 0, LENGTH+D])
			rotate([90, 0, 0])
				cylinder(D/3, d=D/2.5, center=true, $fn=6*D/ln(D));
	}
}
```


[User]
I'm looking to design a fidget toy with a push button that uses magnets to create a repelling force. I'd like a parametric OpenSCAD design that I can customize.

Here's what I have in mind:

**Overall Structure:** The design should consist of three main parts: a shell (the main body), a plunger (the push button), and a cap (that secures the plunger and magnets).

**Key Parameters (that I'd like to easily adjust):**

*   **Generated Part:** A dropdown menu to select which part or parts should be generated: Cap, Shell, Plunger, All-Printable, or All-Preview.
*   **Thread Offset:** The space between threaded parts (for tolerance)
*   **Plunger Offset:** The space between the button and the body (for tolerance)
*   **Plunger Travel:** How far the button depresses (affects magnet travel)
*   **Magnet Shape:** A dropdown menu to select Cylinder or Cube.
*   **Magnet Dimensions:** Width, Depth, and Height of the magnets.

**Detailed Design:**

*   **Shell:**
    *   A cylindrical body with internal threads at one end for the cap, and an internal lip/retainer to hold the plunger in. The bottom of the shell has threads, and there should be a bevel where the body meets the threads to aid printing (avoid overhangs).
*   **Plunger:**
    *   A cylindrical rod that slides inside the shell. It should have a wider head at one end which acts as the push button, and a flange that stops it at the retainer on the shell.  It also has a recess for one magnet.
*   **Cap:**
    *   A threaded cylinder that screws into the bottom of the shell. It contains a magnet recess. The main body of the cap should have a bevel for printability.

**Magnet Considerations:**

*   The magnets should be press-fit or glued into their recesses.
*   The magnets in the plunger and cap should repel each other.

**Additional Notes:**

*   I’d like the ability to easily adjust wall thicknesses of the components.
*   The design should use simple, printable threads without needing any specialized libraries - I've included a function in the attached example that works well, called `printableThread`, and that should work well. Also please be sure to include a chamfering function, `threadChamfer`.
*   Make sure to include an epsilon value for clean cuts with surfaces that would otherwise be perfectly aligned.
*   The OpenSCAD file should have comments and good variable names so I can easily modify parameters.
*   For round ends please use `$fn` set to 64 as the level of detail, and expose that to the user.
*   I’m attaching an example SCAD file that may be useful, as it contains the needed thread and chamfer functions, as well as a good overall structure. Note that these libraries should *not* be included, as you will be writing all of the SCAD files from scratch, including the thread generation functions. I am simply including these to show how the functions should behave.

Could you please provide the OpenSCAD file(s) based on these requirements?

[Assistant]
Magnetic_Button_Fidget_Toy_-_v01.scad:
```
//////////////////////////////////
// Parameters
//////////////////////////////////
/* [Overall paramters] */
//What part or parts are generated
Generated_Part = "All-Printable"; //[Cap, Shell, Plunger, All-Printable, All-Preview]
//Overall_Length = 80;
//Overall_Diameter = 36;
/* [Tolerance adjustments] */
//Space between threaded parts
Thread_Offset = 0.5;
//Space between button and body
Plunger_Offset = 0.25;
//How far the button depresses, which should be as far as your magnet can push it
Plunger_Travel = 6;
/* [Magnet type and size] */
Magnet_Shape = "Cylinder"; //[Cylinder, Cube]
Magnet_Width = 8.15;
Magnet_Depth = 8.15;
Magnet_Height = 1.5;

//////////////////////////////////
// Hard-coded parameters - edit with care
//////////////////////////////////

module noMoreParams(){} noMoreParams();

//Wall thickness
wall_thickness = 2;

//Threads
thread_depth=1;

//How far in from the exterior wall is the plunger. Determines plunger diameter/hole
plungerInsetAmount = ((wall_thickness + Thread_Offset + thread_depth) * 2);

//   Shell, the outer container that holds the plunger
//Body, the tube the plunger goes through, not the retainer or the cap
shell_body_length = 60;
shell_body_diameter_outer = 25;
shell_body_diameter_inner = shell_body_diameter_outer - plungerInsetAmount;
//BevelToThread, measurements for bevel between the body and thread
shell_body_b2t_length = Thread_Offset + thread_depth;
shell_body_b2t_diameter_inner = shell_body_diameter_inner;
shell_body_b2t_diameter_outer = shell_body_b2t_diameter_inner + (shell_body_b2t_length * 2);
//Retainer, the part that keeps the plunger in
shell_r_length = 2;
shell_r_insetForDiameter = shell_r_length*2;
shell_r_base_diameter_outer = shell_body_diameter_outer;
shell_r_top_diameter_outer = shell_body_diameter_outer - shell_r_insetForDiameter;
shell_r_base_diameter_inner = shell_r_base_diameter_outer - plungerInsetAmount;
shell_r_top_diameter_inner = shell_body_diameter_outer - shell_r_insetForDiameter - plungerInsetAmount;
//Thread, the threaded insert for the cap
shell_thread_length = 10;
shell_thread_diameter_outer = shell_body_diameter_outer;
shell_thread_diameter_inner_threadOuter = shell_thread_diameter_outer - (wall_thickness*2);
shell_thread_diameter_inner_threadInner = shell_thread_diameter_inner_threadOuter - (thread_depth*2);

//   Plunger, the button and its tube
//Tube, the part of the plunger that lives in the body
plunger_tube_length = shell_body_length - Plunger_Travel;
plunger_tube_diameter = shell_body_diameter_inner - (Plunger_Offset * 2);
//Retained, the part of the plunger impacting the retainer
plunger_r_length = shell_r_length;
plunger_r_insetForDiameter = plunger_r_length*2;
plunger_r_base_diameter = shell_r_base_diameter_inner - (Plunger_Offset * 2);
plunger_r_top_diameter = shell_r_top_diameter_inner - (Plunger_Offset * 2);
//Button, the tip of the plunger that extends out past the retainer
plunger_button_length = Plunger_Travel + 4;
plunger_button_nib = 2;
plunger_button_lengthMinusNib = plunger_button_length - plunger_button_nib;
plunger_button_diameter = plunger_r_top_diameter;

//   Cap, the part that screws into the bottom of the shell to keep the plunger in
//Body, the part of the cap you hold while screwing it in
cap_body_length = 10;
cap_body_bevel = 2;
cap_body_lengthBeforeBevel = cap_body_length - cap_body_bevel;
cap_body_diameter_outer = shell_body_diameter_outer;
cap_body_diameter_inner = cap_body_diameter_outer - (cap_body_bevel*2);
//Threads, the threaded part of the cap, screws into the bottom of the shell
cap_thread_length = shell_thread_length;
cap_thread_diameter_outer = shell_thread_diameter_inner_threadOuter - (Thread_Offset*2);
cap_thread_diameter_inner = shell_thread_diameter_inner_threadInner - (Thread_Offset*2);

//Level of detail for round endes
$fn=64;
//Offset to produce clean cuts
eps=0.007;

//////////////////////////////////
// Logic - determine what gets built
//////////////////////////////////

//Disabled model parts and create. I toggle these on and off when editing code
//part_shell_body();
//part_plunger_body();
//part_shell_retainer();
//part_plunger_retainer();
//part_plunger_button();
//part_shell_threads();
//part_cap_body();
//part_cap_thread();

//create_shell();
//create_plunger();
//create_cap();

if (Generated_Part == "Cap"){
    create_cap();
}

else if (Generated_Part == "Shell"){
    translate([0,0,shell_thread_length])
    create_shell();
}

else if (Generated_Part == "Plunger"){
    create_plunger();
}

else if (Generated_Part == "All-Printable"){
    distFromCenterStraight = shell_body_diameter_outer/2+2;
    distFromCenterAngled = distFromCenterStraight*sin(45);
    translate([distFromCenterStraight,0,0])
    create_cap();
    translate([-distFromCenterAngled,distFromCenterStraight,0])
    create_plunger();
    translate([-distFromCenterAngled,-distFromCenterStraight,shell_thread_length])
    create_shell();
}

else if (Generated_Part == "All-Preview"){
    create_cap();
    translate([0,0,Plunger_Travel+cap_body_length+cap_thread_length])
    create_plunger();
    translate([0,0,shell_thread_length + cap_body_length + 0.25])
    create_shell();
}

//////////////////////////////////
// Create - generate a whole element of the model
//////////////////////////////////

module create_shell(){
    union(){
        //body
        part_shell_body();
        //retainer
        translate([0,0,shell_body_length])
        part_shell_retainer();
        //threads
        translate([0,0,-shell_thread_length])
        part_shell_threads();
    }
}

module create_plunger(){
    union(){
        //body
        part_plunger_body();
        //retainer
        translate([0,0,plunger_tube_length])
        part_plunger_retainer();
        //button
        translate([0,0,plunger_tube_length + plunger_r_length])
        part_plunger_button();
    }
}

module create_cap(){
    union(){
        //body
        part_cap_body();
        //threads
        translate([0,0,cap_body_length])
        part_cap_thread();
    }
}

//////////////////////////////////
// Parts - elements that make up a piece of a printable part, or sometimes the whole part
//////////////////////////////////

//Shell/Body, plunger goes inside of it
module part_shell_body(){
    difference(){
        //Outer wall
        cylinder(h=shell_body_length, d=shell_body_diameter_outer);

        //Inner cutout
        translate([0,0,-eps/2])
        cylinder(h=shell_body_length+eps, d=shell_body_diameter_inner);

        //Slant up from threads (smooth transition to avoid overhang)
        hull(){
            translate([0,0,shell_body_b2t_length])
            cylinder(h=eps, d=shell_body_b2t_diameter_inner);
            translate([0,0,-eps/2])
            cylinder(h=eps, d=shell_body_b2t_diameter_outer);
        }
    }
}

//Plunger/body, goes inside shell body
module part_plunger_body(){
    difference(){
        //body
        cylinder(h=plunger_tube_length, d=plunger_tube_diameter);
        //magnet cutout
        translate([0,0,-eps])
        resize([Magnet_Width,Magnet_Depth,Magnet_Height])
        if (Magnet_Shape=="Cylinder"){
            cylinder(h=eps, d=eps);
        }
        else if (Magnet_Shape=="Cube") {
            linear_extrude(0.1)
            square(0.1, center=true);
        }
    }
}

//Shell/retainer, keeps the plunger from coming out the top
module part_shell_retainer(){
    difference(){
        hull(){
            translate([0,0,shell_r_length-eps])
            cylinder(h=eps, d=shell_r_top_diameter_outer);
            cylinder(h=eps, d=shell_r_base_diameter_outer);
        }
        hull(){
            translate([0,0,shell_r_length])
            cylinder(h=eps, d=shell_r_top_diameter_inner);
            translate([0,0,-eps])
            cylinder(h=eps, d=shell_r_base_diameter_inner);
        }
    }
}

//Plunger/retainer, plunger part that impacts the shell's retainer and stops the plunger from exiting top
module part_plunger_retainer(){
    hull(){
        translate([0,0,plunger_r_length-eps])
        cylinder(h=eps,d=plunger_r_top_diameter);
        cylinder(h=eps,d=plunger_r_base_diameter);
    }
}

//Plunger/Button, the thing you press on
module part_plunger_button(){
    //nib
    translate([0,0,plunger_button_lengthMinusNib])
    resize([plunger_button_diameter,plunger_button_diameter,plunger_button_nib*2])
    sphere(d=plunger_button_diameter);
    //base
    cylinder(h=plunger_button_lengthMinusNib, d=plunger_button_diameter);
}

//Shell/threads, threaded bit at bottom of shell that receives the cap
module part_shell_threads(){
    difference(){
        //outer wall around thread
        cylinder(h=shell_thread_length, d=shell_thread_diameter_outer);

        //thread cutout
        translate([0,0,-eps/2]){
            printableThread(shell_thread_diameter_inner_threadInner/2, shell_thread_diameter_inner_threadOuter/2, shell_thread_length+eps, twist=-2);
            threadChamfer(shell_thread_diameter_inner_threadInner/2, shell_thread_length+eps, size=wall_thickness/2, edge="bottom", type="add");
            threadChamfer(shell_thread_diameter_inner_threadInner/2, shell_thread_length+eps, size=wall_thickness/2, edge="top", type="add");
        }
    }
}

// Cap/body, the threaded cap's finger hold
module part_cap_body(){
    //body
    hull(){
        //main section of body
        translate([0,0,cap_body_bevel])
        cylinder(h=cap_body_lengthBeforeBevel, d=cap_body_diameter_outer);
        //smaller diameter section which causes a bevel
        cylinder(h=0.1, d=cap_body_diameter_inner);
    }
}

//Cap/thread, threaded part of the cap, with a magnet cutout
module part_cap_thread(){
    difference(){
        //thread
        printableThread(cap_thread_diameter_inner/2, cap_thread_diameter_outer/2, cap_thread_length, twist=-2);
        //chamfer
        threadChamfer(cap_thread_diameter_inner/2, cap_thread_length+eps, size=wall_thickness/2, edge="top", type="subtract");
        //magnet cutout
        translate([0,0,cap_thread_length - Magnet_Height])
        resize([Magnet_Width,Magnet_Depth,Magnet_Height+eps])
        if (Magnet_Shape=="Cylinder"){
            cylinder(h=eps, d=eps);
        }
        else if (Magnet_Shape=="Cube") {
            linear_extrude(0.1)
            square(0.1, center=true);
        }
    }
}

//////////////////////////////////
// X
//////////////////////////////////

///////////////////////////////////////////////////////////
//              Simple Printable Threads
//            Created by MrStump, Aug 2023
///////////////////////////////////////////////////////////
/*
Instructions for use:
    - Copy all of this code (not including the example at the top) into your script
        - You can alternatively use this as a library using the "use" command
    - Use the "printableThread" function to create a threaded shape
    - Optionally, you can use "threadChamfer" to spawn a shape to create chamfers

Notes on printability:
    ~45 degrees is the steepest overhang you want on a model if you want to print with no supports. When making a thread, look at it from the side and make sure you aren't making an angle too steep. If you are, try less threads, less twist/angle, or less difference between your interior and exterior radius.

Notes on tolerance:
    In the example you can see tolerance added to the "socket". Without it, the "perfect fit" would never go together once printed. 0.25-0.5 differnece in the radius measurements between the "socket" and the "screw" is a good general range, but your printer, printer settings, material and more all impact the fit. A test print is always a good idea to find a measurement that works for you.
*/

// Creates a printable thread shape
//         ----REQUIRED PARAMETERS----
// r_int = Distance from point where threads are closest to center
// r_ext = Distance from point where threads are furthest from center
// h     = Height of the threaded shape (overall screw length)
//         ----MUST USE ONE PARAMERS FROM THESE 2----
// twist = Number of 360 twists across the entire length (turns). Negative numbers are "righty tighty"
// angle = Angle of threads. Negative numbers are "righty tighty".
//         ----OPTIONAL PARAMETERS----
// tc    = Number of threads. Even number recommended. Negative #s are "righty tighty"
// dpc   = Dots per circumfrance, how many dots make up the shape
// $fn   = Level of detail (default recommended, or use 32 for better performance)
//
// EXAMPLE:
// printableThread(r_int=40, r_ext=45, twist=-1.5, h=100, tc=6);
module printableThread(r_int, r_ext, h, twist=0, angle=0, tc=2, dpc=100, $fn=64){
    if (twist==0 && angle==0){
        //Makes no screw due to no twist or angle
        echo("\n\n    ATTENTION:\nYou did not set a 'twist' or 'angle' param in printableThread. You must set one.\n\n");
    } else if (twist!=0 && angle!=0){
        //Makes no screw due to both twist and angle being selected
        echo("\n\n    ATTENTION:\nYou set both 'twist' and 'angle' params in printableThread. You must set only one.\n\n");
    } else {
        //Makes screw

        //Determine twist count by twist or angle param
        desiredTwist = twist!=0 ? twist : twistFromAngle(r_ext,h,angle);

        //Calcualte twist
        degreesToTwist = 360 * desiredTwist;

        // Generate threaded shape
        linear_extrude(h, twist=degreesToTwist, $fn=$fn)
            thread2dShape(r_int, r_ext, tc, dpc);
    }
}

// Creates a chamfer to make mating threaded bits easier
//         ----REQUIRED PARAMETERS----
// r_int = Distance from point where threads are closest to center
// h     = Height of the threaded shape (overall screw length)
// size  = How much chamfering to do
//         ----OPTIONAL PARAMETERS----
// angle = Angle of the chamfer (between 0 and 90. Overhangs > 45 may not print without support)
// edge  = "top" or "bottom", for what edge of the screw chamfer aligns to
// type  = "add" or "subtract", for if the chamfer removes from the screw or adds to it
//          - "add" is for when you need to create a threaded socket in another shape
// $fn   = Level of detail (default recommended, or use 32 for better performance)
//
// EXAMPLE:
// threadChamfer(r_int=40, h=100, size=5);
module threadChamfer(r_int, h, size, angle=45, edge="top", type="subtract", $fn=64){
    //Epsilon, used to ensure the difference cleanly cuts paralell surfaces
    eps = 0.007;
    //Extra X added to the chamfer size if subtracting. Lets low poly meshes not clip through
    sizePlus = type=="subtract" ? size*1.2 : size;

    // Create a list of 3 different x/y/z coords for shape creation and position
    // index 0 = translate that changes z
    // index 1 = translate that changes x pos for rotate_extrude
    // index 2 = rotation applied to triangle prior to rotate_extrude
    extrudeAndTranslateData =
        edge=="bottom" && type=="add" ?
            [ [0,0,0], [r_int,0,0], [0,0,0] ] :
        edge=="top" && type=="add" ?
            [ [0,0,h], [r_int,0,0], [180,0,0] ] :
        edge=="bottom" && type=="subtract" ?
            [ [0,0,-eps], [-(r_int+sizePlus),0,0], [0,0,0] ] :
        edge=="top" && type=="subtract" ?
            [ [0,0,h+eps], [-(r_int+sizePlus),0,0], [180,0,0] ] :
        "If you get error in below translate, it is cause of this";

    // Create/position shape
    translate(extrudeAndTranslateData[0])
        rotate_extrude()
        translate(extrudeAndTranslateData[1])
        rotate(extrudeAndTranslateData[2])
        //polygon([[0,0],[0,sin(angle)*size],[cos(angle)*size,0]]);
        polygon([[0,0],[0,sizePlus*tan(angle)],[sizePlus,0]]);
}

// Creates the shape which is extrusion twisted to make threads
//         ----REQUIRED PARAMETERS----
// r_int = Distance from point where threads are closest to center
// r_ext = Distance from point where threads are furthest from center
//         ----OPTIONAL PARAMETERS----
// tc    = Number of threads. Even number recommended. Negative #s are "righty tighty"
// dpc   = Dots per circumfrance, how many dots make up the shape
//
// EXAMPLE:
// thread2dShape(30, 35, tc=6);
module thread2dShape(r_int, r_ext, tc=2, dpc=200){
    //How much threads stick out
    threadLength = r_ext-r_int;
    //Midpoint between the 2 radius measurements
    r_mid = r_int + (threadLength/2);
    //The size of each "step" making the shape
    interval = 1/dpc;

    //Generate list of [X,Y] coords, each acting as a point to make the shape
    pl = [
        for (i=[0:interval:1-interval])[
            (r_mid + (threadLength/2) * sin(tc * i * 360)) * cos(i * 360),
            (r_mid + (threadLength/2) * sin(tc * i * 360)) * sin(i * 360)
        ]
    ];

    //Generate the 2d shape from the list of points
    polygon(points=pl);
}

//Helper function: get twist based on radius, height (length), and desired angle
function twistFromAngle(_r,_h,_a) = _h / ( (2*PI*_r) * tan(_a) );

```


[User]
Hi, I'm looking to get a custom speaker adapter designed in OpenSCAD. I need it to be parametric so I can easily adjust it for different speaker sizes and mounting configurations.

Essentially, it's a round spacer with a central cutout for the tweeter and mounting tabs on either side. The main spacer body should be a cylinder, and then it'll have a circular cutout in the middle. The tabs will also be cylindrical, extending from the main body. The mounting holes on the tabs should be elongated, which is something I know OpenSCAD can handle, I just need the math done. The holes should go all the way through the spacer.

Here are the parameters I'd like to control:

*   `resolution`: A variable to control the smoothness of the curves, like $fn in OpenSCAD. This should be adjustable, with lower settings for faster previews and higher for final renders.
*   `cutout_size`: The diameter of the central tweeter cutout.
*   `spacer_OD`: The outside diameter of the main cylindrical spacer body.
*   `spacer_height`: The height of the main spacer body.
*   `left_tab`: The diameter of the left mounting tab.
*  `left_distance`: The distance from the center of the main spacer to the center of the left tab.
*   `right_tab`: The diameter of the right mounting tab.
*   `right_distance`: The distance from the center of the main spacer to the center of the right tab.
*   `tab_offset`: This will move the tabs offset, perpendicular to the center of the main body.
*  `screw_size`: The diameter of the mounting screw holes.
*   `screw_elongation`: The length of the elongated mounting screw holes.

I'd like the design to use `hull()` for combining the cylinders. I'd also like the screw holes to be a hull of two cylinders, making an oblong hole. Can you help me generate the .scad file(s) for this? It would be great if the main body and tab positions/dimensions could be adjusted easily. I'd like to be able to generate a STL from it.

I don't think I'll need any libraries beyond the basic OpenSCAD functionality.

[Assistant]
Speaker_Spacer_Tweeters.scad:
```
//
// Jon Bourgeois, Tweeter mount adapter
//
//

/* [Resolution] */
// Smoothness of model, set low for editing, set high for printing
resolution = 64;//[64, 128, 256]

/* [Cutout] */
// Tweeter cutout size
cutout_size = 40;

/* [Main Body Diameter & Height] */
// Outside Diameter of main part of spacer
spacer_OD = 69;
spacer_height=5;

/* [Left Side Tab] */
// Diameter of left side mount area
left_tab = 20;
left_distance = 45;

/* [Right Side Tab] */
// Diameter of right side mount area
right_tab = 20;
right_distance = 45;

// Mount tab offset, moves side tabs perpendicular to center
tab_offset =6;

/* [Mounting Holes] */
screw_size = 8;
screw_elongation = 3;   // Creates an elongated screw hole

difference (){
    hull () {
        // spacer body
        //
        translate([0, 0, 0])
            cylinder(h=spacer_height,d=spacer_OD,$fn=resolution);
        translate([-tab_offset, left_distance, 0])
            cylinder(h=spacer_height,d=left_tab,$fn=resolution);
        translate([-tab_offset, -right_distance, 0])
            cylinder(h=spacer_height,d=right_tab,$fn=resolution);
        }
        // Tweeter cutout
        //
        translate([0, 0, -spacer_height])
            #cylinder(h=spacer_height*3,d=cutout_size,$fn=resolution);

        // Mount holes
        //
        // LEFT
    hull (){
        translate([-tab_offset, left_distance, -spacer_height])
            #cylinder(h=spacer_height*3,d=screw_size,$fn=resolution);
        translate([-tab_offset, left_distance-screw_elongation, -spacer_height])
            #cylinder(h=spacer_height*3,d=screw_size,$fn=resolution);
        }
        // RIGHT
    hull (){
        translate([-tab_offset, -right_distance, -spacer_height])
            #cylinder(h=spacer_height*3,d=screw_size,$fn=resolution);
        translate([-tab_offset, -right_distance+screw_elongation, -spacer_height])
            #cylinder(h=spacer_height*3,d=screw_size,$fn=resolution);
        }
}
//
//
//
```


[User]
Okay, I'd like to create a set of parametric OpenSCAD files for generating plant pot liners designed for use with LECA (lightweight expanded clay aggregate). I'd like the design to be quite flexible, allowing for different sized pots.

The primary liner will have a tapered cylindrical shape and be designed with a good number of drainage holes in the bottom and sides. I'm planning on having a main file, let's call it `leca_liner_main.scad`, that will allow me to adjust the key parameters. Here are the key parameters it needs to handle:

*   **`height`**: The overall height of the liner, from the bottom to the top rim in millimeters.
*   **`top_diameter`**: The diameter of the liner at the top in millimeters. I would like this to be slightly smaller than the inner diameter of my pot, which I'll be measuring manually.
*   **`bottom_diameter`**: The diameter of the liner at the bottom in millimeters. This should also be a parameter, even if the pot is a straight cylinder, I usually want a bit of taper.
*   **`thickness`**: The thickness of the liner walls. It should be at least double the external perimeter width I use in my slicer settings.
*   **`hole_radius`**: The radius of the drainage holes. This will need to be adjustable as different leca has different sized aggregate, I also want to be able to adjust it for smaller pots.
*   **`has_tab`**: A boolean (0 or 1) to enable or disable tabs for easier removal.
*   **`tab_width`**: The width of the removal tabs.
*   **`tab_height`**: The height of the removal tabs.
*   **`tab_thickness`**: The thickness of the removal tabs.
*  **`tab_angle`**: The angle at which the tab extends outward from the main cylinder.
*   **`name_label`**: A string, used for a name label that will be printed onto the pot. It'd be useful if this can also include the dimensions of the pot (height, top_diameter, bottom_diameter)

I'd like the holes in the *sides* to be arranged in rows that wrap around the liner. The number of rows should be calculated automatically based on the `height` and the `hole_radius`. I'd also like a set of holes in the bottom of the liner, I want these holes to be slightly offset to pack more holes in without them overlapping too much, I can manage these in a separate function, or module.

I'd also like to add a small module to the bottom of the design that adds a little bit of extra material at the bottom to increase strength. I'd like this to be angled slightly.

I'd like to generate a label along the side of the pot that includes the `name_label`, height, top diameter, and bottom diameter.

Finally, I'd like an option in the main file to make the side holes use a different shape, which can save some filament and rendering time. This can be a boolean called `large`, if this is set to true, make the holes cube-like, and scale them to make them larger. If not, they should be cylinders.

Can you generate the required `.scad` files based on this description, please? I don't need any external libraries (like MCAD or BOSL), just a modular design where I can change the parameters and generate a functional leca liner.

[Assistant]
leca_liner_v2.scad:
```
$fa = 4.5;
$fs = 2.3;

// Instructions to create a pot liner:
//  - set is the height of the liner
//  - set the top_diameter
//  - set the bottom_diameter.

// Troubleshooting:
// If the holes in the side are overlapping, there are a couple things you can try:
//  - try changing the hole_radius to something smaller

// Notes:
//  - It's usually best to make the top_diameter a few mm smaller than the actual diameter
//  - Even if the pot is a cylinder and doesn't taper, set bottom_diameter about 10mm smaller than the top_diameter

height          = 163; // Height in millimeters from the rim of the pot to the bottom of the inside of the pot
top_diameter    = 122; // Top Diameter in millimeters.
bottom_diameter = 92; // Bottom Diameter in millimeters.

name_label      = "barrel";

// This is the tabs for the top to help make the liner easier to remove.
//    I typically melt these with a torch when it's in the pot to wrap them over the edge.
//    They can be pretty fragile and often fall off.
has_tab       = 1;
tab_width     = 10;
tab_height    = 15;
tab_thickness = 2.5;
tab_angle     = 45;

thickness     = 1.6; //<-- Make this param at least 2x the external perimeter width setting from your slicer
hole_radius   = 2.2;//3.7; //<-- might need to be adjusted for smaller pots. 3.7 seems good for standard leca.
num_vert      = (height/(2*hole_radius+1))-2; //<-- number of rows on the side of the liner

large         = false; //<-- experimental, will save filament, but you will need to manually change "hole_radius=2". You will probably also need to change openscad settings to handle more rendered elements (edit->preferences->advanced)

bottom_radius = bottom_diameter/2;
top_radius    = top_diameter/2;

//make horizontal hole first row
module make_horiz_holes_single(hole_radius, height, width, large_liner){
    for(k=[1:12])
    {
        translate([0,0,hole_radius*2+1])
        {
            if (large_liner == false) {
                rotate([90,0,20*k])
                {
                    cylinder($fs=4, width,r=hole_radius, center=true);
                }
            } else {
                rotate([90,0,15*k])
                {
                    scale([3.6,0.8,3]) {
                        cube([hole_radius*2, hole_radius*2, width], center=true);
                    }
                }
            }
        }
    }
}
// Create copies first row of horizontal holes to match the height of the liner
module make_horiz_holes(hole_radius, height, top_radius, large_liner) {
    for(k=[0:num_vert])
    {
        if (large_liner == false) {
            translate([0,0,hole_radius*2.5*k])
            {
                rotate([0,0,10*k])
                {
                    make_horiz_holes_single(hole_radius, height, top_radius*2+50, large_liner);
                }
            }
        } else {
            hole_radius = 2;
            translate([0,0,hole_radius*2.5*k])
            {
                rotate([0,0,5.9*k])
                {
                    make_horiz_holes_single(hole_radius, height, top_radius*2+50, large_liner);
                }
            }
        }
    }
}

// Create the holes in the bottom of the liner based on the size of the bottom radius
module make_vert_holes(hole_radius, bottom_radius) {
    for (j=[1:8]) {
        // Normal Orientation
        rotate([0,0,45*j]) {
            for (i=[0:bottom_radius/13]) {
                translate([i*8,i*8,0])
                {
                    rotate([0,0,20])
                    {
                        cylinder($fs=5, 30,r=hole_radius, center=true);
                    }
                }
            }
        }
        // Offset Orientation
        //   This was added to have more holes in the bottom without overlapping the holes
        rotate([0,0,45*j+22.5]) {
            for (i=[2:bottom_radius/13]) {
                translate([i*8,i*8,0])
                {
                    rotate([0,0,20])
                    {
                        cylinder($fs=5, 30,r=hole_radius, center=true);
                    }
                }
            }
        }
    }
}

module make_outer_shell(top_radius, bottom_radius, height, thickness) {
    difference() {
        difference() {
            union () {
                cylinder(height, r1=bottom_radius, r2=top_radius, center=false);
                make_tab(tab_width, tab_height, tab_thickness, tab_angle, height, top_radius,thickness);
            }
            translate([0,0,thickness]) {
                difference() {
                    cylinder(height, r1=bottom_radius-(thickness), r2=top_radius-(thickness/2), center=false);
                    cylinder(4.9, r1=top_radius, r2=top_radius, center=false);
                }
            }
        }
        // Create angled wall at the bottom to stregthen the liner
        translate ([0,0,thickness]) {
            cylinder(5, r1=bottom_radius-thickness-6, r2=bottom_radius-(thickness));
        }
    }
}

module make_tab (tab_width, tab_height, tab_thickness, tab_angle, height, top_radius, thickness) {
    translate([0,top_radius-tab_thickness/2-thickness/2,height-tab_thickness]) {
        rotate([-tab_angle,0,0]){
            translate([0,0,tab_height/2]){
                cube(size = [tab_width, tab_thickness, tab_height], center=true);
            }
        }
    }
    translate([0,-1*(top_radius-tab_thickness/2-thickness/2),height-tab_thickness]) {
        rotate([tab_angle,0,0]){
            translate([0,0,tab_height/2]){
                cube(size = [tab_width, tab_thickness, tab_height], center=true);
            }
        }
    }
}

module make_label (name_label, top_diameter, bottom_diameter, height, thickness, hole_radius) {
    hole_diameter = hole_radius*2;
    translate([0,hole_diameter,-1]){
         resize([bottom_diameter-thickness*2, hole_diameter*2, 1.3]) {
             linear_extrude(1) {
                 rotate([0,180,0]) {
                     text(name_label, valign="center",halign="center");
                 }
             }
         }
     }
     size_label = str(height,"h ",top_diameter," ",bottom_diameter);
     translate([0,-(hole_diameter),-1]){
         resize([bottom_diameter-thickness*2, hole_diameter*2, 1.3]) {
             linear_extrude(1) {
                 rotate([0,180,0]) {
                     text(size_label, valign="center",halign="center");
                 }
             }
         }
     }

}

difference() {
    //Create the outer shell
    make_outer_shell(top_radius, bottom_radius, height, thickness);
    //Create the vertical and horizontal drainage holes
    union() {
        make_vert_holes(hole_radius, bottom_radius);
        make_horiz_holes(hole_radius-0.1, height, top_radius, large);

    }
    make_label(name_label, top_diameter, bottom_diameter, height, thickness, hole_radius);
}

```


[User]
I'm looking to design a parametric shower hose nozzle that I can 3D print. I have some specific requirements, and I'm hoping you can generate the OpenSCAD code for me.

The nozzle should have a standard 1/2" BSP (British Standard Pipe) thread for connecting to the shower hose. I'd like the code to be flexible enough to allow me to adjust the size of the water outlet (orifice) diameter. I also need a hex-shaped section for tightening with a 13mm wrench. There should be a smooth transition between the threaded part, the hex section, and the tip of the nozzle.

Here's a breakdown:

1.  **Thread:** 1/2" BSP thread. Please use the appropriate dimensions for this thread standard. If possible, try to approximate the thread using a sine wave rather than generating the exact thread geometry, as this will likely be much faster to render. It should have a length of around 10mm.
2.  **Hex Section:** A 13mm hex section for wrench tightening. The hex should have a height of approximately 20mm. The sides of the hex section should be extruded with the correct angle to connect to each vertex, not to the circumscribed circle.
3.  **Transition:** A smooth, tapered transition from the end of the threads to the base of the hex section. This should be a gradual curve or taper, not a sharp step.
4.  **Tip:** The end of the nozzle should be rounded and should begin around 22mm above the top of the hex section. This rounded end should have a diameter of approximately 10mm.
5.  **Orifice:** The outlet should be a hole running through the center of the nozzle, starting from the base and exiting at the tip. The diameter of this orifice should be a parameter that I can easily adjust.
6.  **Units:** Everything should be in millimeters.
7. **Libraries:** Please create a custom library for generating the threads rather than using an existing one, as I'd like the thread approximation to be easily available for future projects as well. I don't think I'll need any other libraries for this project.

I'm planning to print this with a 0.1mm layer height, so keeping the rendering fast is a priority, especially for the threads. Please make sure the code is well-commented and easy for me to understand and modify. I would like for the primary .scad file to include parameters for the orifice diameter, thread length, and thread diameter, as well as parameters for the hex size and height. The dimensions of the tip can be assumed to be constant.

[Assistant]
shower_nozzle.scad:
```
orifice_mm=1.5; //[0.1:0.1:8]

//sine_thread.scad by Ron Butcher (aka. Ming of Mongo),
//with bits borrowed from ISOThread by Trevor Moseley
//
//Thread libs for OpenSCAD are hideously slow.  I love OpenSCAD, but that's the fact.
//But I found that, if you are cool with approximating iso threads with a sine wave,
//you can make very smooth threads really fast just by spinning a circle around.
//
//Actually, you could make near perfect threads by specifying a polygon other than a circle
//with the exact screw cross-section you need, but I leave that as an excersize for those
//who care more than I do.  Circle works for me.
//
//threads module gives a threaded rod
//hex_nut(diameter) does just what you think
//hex_screw(diameter, length) gives an M(diameter) hex head screw with length mm of thread.
//
//defaults get you an M4 x 10mm bolt and an M4 nut.  If you want something non standard,
//you can supply the pitch, head height and diameter, rez(olution) and a scaling factor
//to help make up for the oddities of everyone's individual printers and settings.
//
//Finer layering gets you better results.  At .1mm layer height, my M4 threads kick ass.
//
//This is all metric, but you can translate US diameters and pitch to mm easily enough.

module threads(diameter=4,pitch=undef,length=10,scale=1,rez=20){
	pitch = (pitch!=undef) ? pitch : get_coarse_pitch(diameter);
	twist = length/pitch*360;
	depth=pitch*.6;
	linear_extrude(height = length, center = false, convexity = 10, twist = -twist, $fn = rez)
		translate([depth/2, 0, 0]){
			circle(r = scale*diameter/2-depth/2);
		}
}

/*
module hex_nut(	diameter = 4,
				height = undef,
				span = undef,
				pitch = undef,
				scale = 1,
				rez = 20){
	height = (height!=undef) ? height : hex_nut_hi(diameter);
	span = (span!=undef) ? span : hex_nut_dia(diameter);

	difference(){
		cylinder(h=height,r=span/2, $fn=6); //six sided cylinder
		threads(diameter,pitch,height,scale,rez);
		cylinder(h=diameter/2, r1=diameter/2, r2=0, $fn=rez);
			translate([0,0,height+.0001])
		rotate([180,0,0])
				#cylinder(h=diameter/2, r1=diameter/2, r2=0, $fn=rez);
	}
}

module hex_screw(diameter = 4,
				length = 10,
				height = undef,
				span = undef,
				pitch = undef,
				scale = 1,
				rez = 20){
	height = (height!=undef) ? height : hex_bolt_hi(diameter);
	span = (span!=undef) ? span : hex_bolt_dia(diameter);
	cylinder(h=height,r=span/2,$fn=6); //six sided cylinder
	translate([0,0,height])
		threads(diameter,pitch,length,scale,rez);
}

// Lookups shamelessly stolen from ISOThread.scad by Trevor Moseley
// function for thread pitch
function get_coarse_pitch(dia) = lookup(dia, [
[1,0.25],[1.2,0.25],[1.4,0.3],[1.6,0.35],[1.8,0.35],[2,0.4],[2.5,0.45],[3,0.5],[3.5,0.6],[4,0.7],[5,0.8],[6,1],[7,1],[8,1.25],[10,1.5],[12,1.75],[14,2],[16,2],[18,2.5],[20,2.5],[22,2.5],[24,3],[27,3],[30,3.5],[33,3.5],[36,4],[39,4],[42,4.5],[45,4.5],[48,5],[52,5],[56,5.5],[60,5.5],[64,6],[78,5]]);

// function for hex nut diameter from thread size
function hex_nut_dia(dia) = lookup(dia, [
[3,6.4],[4,8.1],[5,9.2],[6,11.5],[8,16.0],[10,19.6],[12,22.1],[16,27.7],[20,34.6],[24,41.6],[30,53.1],[36,63.5]]);
// function for hex nut height from thread size
function hex_nut_hi(dia) = lookup(dia, [
[3,2.4],[4,3.2],[5,4],[6,3],[8,5],[10,5],[12,10],[16,13],[20,16],[24,19],[30,24],[36,29]]);

// function for hex bolt head diameter from thread size
function hex_bolt_dia(dia) = lookup(dia, [
[3,6.4],[4,8.1],[5,9.2],[6,11.5],[8,14.0],[10,16],[12,22.1],[16,27.7],[20,34.6],[24,41.6],[30,53.1],[36,63.5]]);
// function for hex bolt head height from thread size
function hex_bolt_hi(dia) = lookup(dia, [
[3,2.4],[4,3.2],[5,4],[6,3.5],[8,4.5],[10,5],[12,10],[16,13],[20,16],[24,19],[30,24],[36,29]]);
*/

/*
 * Shower hose nozzle
 * Recommended printing with 0.1mm layer height
 * Random seam placement, no ironing!
 */

wrench_nom = 13;
wrench_ratio = 1.16; //1.1547;
wrench = wrench_ratio * wrench_nom;

difference() {
	union() {
		intersection() {
			//european shower head thread
			threads(diameter=20.4, pitch=1.9, length=10, rez=60);
			cylinder(d1=40, d2=wrench_nom, h=13); //taper
		}

		//wrench and tip
		hull() {
			cylinder(d1=wrench, d2=wrench, h=20, $fn=6);
			translate([0,0,22]) sphere(d=10, $fn=32);
		}

		//transition from thread to wrench
		translate([0,0,10]) hull() {
			cylinder(d1=wrench, d2=wrench, h=3, $fn=6);
			cylinder(d=19, h=0.1, $fn=64);
		}
	}

	//orifice
	union() {
	translate([0,0,-10]) cylinder(d=orifice_mm, h=100, $fn=32);
	translate([0,0,-0.1]) cylinder(d1=9, d2=orifice_mm, h=20, $fn=32);
	}
}
```


[User]
I'd like to design a parametric spoon holder that attaches to the neck of a hot tub chemical container. I have two specific spoon sizes in mind, one for a 2.5ml (1/2 tsp) spoon and another for a 15ml (tablespoon) spoon. I'm using the "Arctic Spa" brand of chemicals, so the container necks are roughly the same size. I've got some information on those containers and how they should fit together with the spoon holder, as well as the spoons that should fit in them.

Here's what I'm looking for in a parametric design:

1.  **Neck Diameter:** I need to be able to adjust the diameter of the opening that fits over the chemical container neck. The "Arctic Spa" brand containers are about 57mm wide, so that should be the default, but being able to adjust it would be very useful.
2.  **Holder Depth:** The depth of the spoon holder should be adjustable, as I expect the spoon handles could come in varying lengths. I need this to be from the top down, and about 150mm deep should be good for the larger spoon.
3.  **Spoon Diameter:** I need to be able to specify the diameter of the spoon well, that should hold the spoon itself. It should be 25mm for the 2.5ml spoon and 36mm for the 15ml spoon.
4.  **Bridging:** If the diameter of the spoon well is too large, then the design should slope the base of the spoon holder to avoid excessive bridging. This can be tuned based on a `MAX_SPAN` parameter that should default to 22mm. If the `SPOON_DIAMETER` is greater than `MAX_SPAN` then the base of the spoon holder should have the sloped feature.
5.  **Neck Clip:** There should be a small lip that clips under the container neck to keep it from sliding off.

I'd like the code to be well-commented. Please provide an OpenSCAD file that I can customize for my needs. Also, please use `$fa` and `$fs` for the circular facets, and make sure they're smoothly rendered. I'd like `$fa` to default to 4 and `$fs` to default to 0.4, for smooth circles.

Let me know if you need any more information.

[Assistant]
arcticspa_chem_spoon_holder.scad:
```
/* This is designed to fit around the neck of a small chemical tub and hold a long spoon
It's sized for "Arctic Spa" brand hot tub chemicals but could easily be adjusted for similar

This one fits a 1/2tsp (2.5ml) spoon, which is perfect for topping off the chlorine levels.
https://www.thingiverse.com/thing:6265462

Which is based on this customizer https://www.thingiverse.com/thing:682146
Note: You will need to specify a longer handle to work with this holder

 Spoon Holder © 2023 by Daniel Urquhart is licensed under Attribution-ShareAlike 4.0 International. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/
*/

NECK_DIAMETER = 57; //width of neck to fit over
DEPTH = 150;         //depth of spoon holder, from the top

//SPOON_DIAMETER = 25; //25 is good for the 2.5ml spoon
SPOON_DIAMETER = 36; //36 is just enough for the 15ml spoon

MAX_SPAN=22; //maximum distance to bridge (if SPOON_DIAMETER is greater than this, slope the base)

//Arc facets - use smoother circles
$fa=4;
$fs=0.4;

difference(){

    cylinder(h=2, d=NECK_DIAMETER+6*2);
    cylinder(h=2, d=NECK_DIAMETER);
}

neck_edge = NECK_DIAMETER/2+6;

translate([neck_edge-2,-6,0])
        cube([10,12,2]);

translate([neck_edge+6,-6,0])
        cube([4,12,15]);

translate([neck_edge+10+SPOON_DIAMETER/2,0,0])scale([1,1.4,1])
difference(){
    t = 0.401*4;
    EXTEND = SPOON_DIAMETER < MAX_SPAN? 0 : (SPOON_DIAMETER-MAX_SPAN)*0.6;
    union(){
        cylinder(h=DEPTH+3, d=SPOON_DIAMETER+t*2);
        if(EXTEND>0){
            translate([0,0,DEPTH+3-0.01])
                cylinder(h=EXTEND, d1=SPOON_DIAMETER+t*2, d2=MAX_SPAN+t*2);
        }
    }

    cylinder(h=DEPTH, d=SPOON_DIAMETER);

    //sloped bottom to reduce bridge
    if(EXTEND>0){
        translate([0,0,DEPTH-0.1])
            cylinder(h=EXTEND, d1=SPOON_DIAMETER, d2=MAX_SPAN);
    }

    hull(){
        translate([-SPOON_DIAMETER/2, -50, -DEPTH^0.6])
            cube([1,100,1]);
        translate([ SPOON_DIAMETER/2+5, -50, 0])
            cube([1,100,DEPTH*.6]);
    }
}

/* VERSION ONE - old

NECK_DIAMETER = 60; //width of neck to fit over
DEPTH = 95;         //depth of spoon holder, from the top

SPOON_DIAMETER = 25;

difference(){

    cylinder(h=3, d=NECK_DIAMETER+2+6*2);
    cylinder(h=3, d=NECK_DIAMETER+2);
}

translate([35,-5,0])
        cube([10,10,3]);

translate([35+6,-5,0])
        cube([4,10,15]);

translate([58,0,0])scale([1,1.4,1])
difference(){

    cylinder(h=DEPTH+3, d=SPOON_DIAMETER+2*2);

    cylinder(h=DEPTH, d=SPOON_DIAMETER);
    hull(){
        translate([-1, -50, 0])
            cube([1,100,1]);
        translate([ SPOON_DIAMETER/2+5, -50, 0])
            cube([1,100,60]);
    }
}
*/
```


[User]
I'm looking to create a custom mask for a camera focusing screen. I have some specific dimensions and features in mind.

The mask itself should be a rectangular plate with a rounded rectangular cutout in the center. I need to be able to specify both the width and height of this center cutout. The outer plate should also have rounded corners. I'd also like to have some text on the plate: one label on the bottom left and another rotated label and a text on the top. Finally I need some small nubs and grips for placement and removal. The nubs go on all four edges and grips on the left and right.

Here are the specific parameters I’d like to control:

*   **`mask_width`**: Width of the central rectangular cutout.
*   **`mask_height`**: Height of the central rectangular cutout.
*   **`mask_name`**: Text label displayed on the bottom left of the plate.
*   **`total_size_w`**: Total width of the outer plate.
*   **`total_size_h`**: Total height of the outer plate.
*   **`thickness`**: Thickness of the mask plate.
*  **`nub_distance`**: Distance from the edge of the plate for the nubs.
*  **`nub_len`**: Length of the nubs.
*  **`nub_radius`**: Radius of the nubs.
* **`cutout_line_thickness`**: Line thickness of the cross-shape in the cutout.
*  **`grip_height`**: Height of the grips.
*  **`grip_width`**: Width of the grips.

The central cutout should be created from a cross shape created by two rounded rectangles where the width and height of each rounded rectangle are not the same. The rounded rectangle corners also need to have a configurable radius.
The plate itself should also have rounded corners.

The nubs are cylindrical pegs that stick out from the bottom surface and need to be placed at the four corners of the plate. Two nubs on the left side should be rotated.
The grips should be small blocks with a rounded top edge and should be placed on the left and right edges of the mask.

Can you generate an OpenSCAD file or files for this? I'm not using any special libraries like MCAD or BOSL so standard OpenSCAD modules should work just fine.

[Assistant]
mask.scad:
```
// Mask cutout width
mask_width = 41; // [5:60]
// Mask cutout height
mask_height = 57; // [5:60]
// Text under the mask
mask_name = "6x4.5";

module __Customizer_Limit__ () {}

// Font: Futura Heavy
total_size_w = 74;
total_size_h = 65;
total_size = 70;
thickness = 0.7;

nub_distance = 50;
nub_len = 2;
nub_radius = 1;

cutout_line_thickness = 1;
grip_height = 0.5;
grip_width = 10;

module rounded_rect(w, h, t, r) {
    minkowski() {
        cube([w - r * 2, h - r * 2, t/2], center=true);
        translate([0, 0, -t/4])
            cylinder(h=t/2, r=r, $fn=30);
    }
}

module nub () {
    rotate([0, 90, 0])
        difference() {
            cylinder(h = nub_len, r = nub_radius, $fn=10);
            translate([10, 0, 0])
                cube([20,20,20], center=true);
        }
}

module cutout_simple (w, h) {
    cube([w, h, 10], true);
}

module cutout_cross (w, h) {
    rounded_rect(w, h, 10, 3);
    rounded_rect(h, w, 10, 3);
}

module cutout_cross_with_lines (w, h) {
    hi = max(w, h);
    lo = min(w, h);
    rounded_rect(lo, lo, 20, 1);

    other = (hi - lo) / 2 - cutout_line_thickness;

    translate([0, lo / 2 + other / 2 + cutout_line_thickness, 0])
        rounded_rect(lo, other, 10, 1);

    translate([0, -(lo / 2 + other / 2 + cutout_line_thickness), 0])
        rounded_rect(lo, other, 10, 1);

    translate([lo / 2 + other / 2 + cutout_line_thickness, 0, 0])
        rounded_rect(other, lo, 10, 1);

    translate([-(lo / 2 + other / 2 + cutout_line_thickness), 0, 0])
        rounded_rect(other, lo, 10, 1);
}

module grip () {
    translate([0, 0, grip_height / 2 - 0.1 + thickness / 2]) {
        cube([2, grip_width, grip_height], true);
        translate([0, 0, grip_height / 2 + 0.25])
            rounded_rect(3, grip_width + 1, 0.5, 0.89);
    }
}

module mask(w, h, name) {
    difference() {
        rounded_rect(total_size_w, total_size_h, thickness, 3);
        cutout_cross_with_lines(w, h);
        ;

        label_left = -total_size_w / 2 + 2;
        label_alignment = "left";
        rotate([0, 0, -90]) {
            if (max(w, h) < 59) {
                translate([total_size_h / 2 - 2, total_size_w / 2 - 6, thickness/2 - 0.23])
                    linear_extrude(height = 20)
                        text("@Ob.skura", font="Futura:style=Medium Italic", size=3, halign="right");
            }
        }

        if (min(w, h) < 45 || max(w, h) < 59) {
            translate([total_size_w / 2 - 1.5, total_size_h / 2 - 7, thickness/2 - 0.23])
                linear_extrude(height = 20)
                    text("RB67", font="Futura:style=Medium Italic", size=4, halign="right");
        }
        translate([label_left, -total_size_h / 2 + 3, thickness/2 - 0.23])
            linear_extrude(height = 20)
                text(name, font="Futura:style=Medium Italic", size=3.5, halign=label_alignment);
    }

    translate([0, 0, -thickness / 2])
    {
        translate([total_size_w / 2, nub_distance / 2, 0])
            nub();
        translate([total_size_w / 2, -nub_distance / 2, 0])
            nub();
        translate([-total_size_w / 2, nub_distance / 2, 0])
            rotate([0, 0, 180])
                nub();
        translate([-total_size_w / 2, -nub_distance / 2, 0])
            rotate([0, 0, 180])
                nub();
    }

    translate([max(w, h) / 2 + 2, 0, -0.1]) grip();
    translate([-max(w, h) / 2 - 2, 0, -0.1]) grip();
}

mask(mask_width, mask_height, mask_name);

```


[User]
I'd like a customizable stamp generator in OpenSCAD. It should allow the user to specify the diameter of the stamp, whether or not to include date markings, the text to be stamped, the font to use for the text and date, the starting year for the year markings, and the thickness of the stamped text, and a size modifier for the text. I'd also like to be able to preview the stamp without the handle attached. It should use the `linear_extrude` and `text` primitives, and make use of `difference`, `cylinder`, `rotate`, `translate`, `cube`, `sphere` and `hull` to generate the shape.

I'm thinking the basic structure should be a cylindrical base, with the text embossed on it. If date markings are enabled, it should have a ring with markings for each month, along with the current year and a few following years. The text should be centered on the stamp, with a modifiable text size. The handle should be created using a hull of two cylinders and spheres, combined with another cylinder and a sphere.

The user should be able to adjust the following via the customizer:
* `Diameter`: The overall diameter of the stamp.
* `AddDateIndicator`: A boolean that toggles the date markings on or off.
* `Text`: The text to be stamped.
* `Font`: The font to be used for both the stamp text and the date markings.
* `StartYear`: The year to start the year marking.
* `TextHeight`: The height of the extruded text and other markings.
* `PreviewWithoutHandle`: Boolean to preview the stamp only (without handle).
* `TextSizeModifier`: A modifier for the text size.

I’d like to control the rendering quality with the `$fn` variable.

The handle should be automatically attached unless it is a preview rendering.

[Assistant]
Stamp.scad:
```
/*
Stamp Creator © 2023 by Michael (chill0r) is licensed under Attribution-ShareAlike 4.0 International. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/
*/

/* [Base Settings] */
Diameter = 50.0; // [20:1:150]
AddDateIndicator = true;
Text = "Hello World!";
Font = "Arial:style=Regular";
StartYear = 23;
TextHeight = 0.5; // [0.1:0.1:2]
/* [Quality] */
$fn=40;
/* [Others] */
PreviewWithoutHandle = true;
TextSizeModifier = 1.00; // [0.5:0.01:2.5]

// Outer marking
difference() {
    cylinder(d=Diameter, h=TextHeight);
    translate([0,0,-0.5])cylinder(d=Diameter-1, h=TextHeight+1);
}

if (AddDateIndicator) {
    difference() {
        cylinder(d=Diameter*0.8, h=TextHeight);
        translate([0,0,-0.5])cylinder(d=Diameter*0.8-1, h=TextHeight+1);
    }

    v = 180/12;
    m = [0,1,2,3,4,5,6,7,8,9,10,11,12];
    // Month seperators
    for (i=[0:1:12]) {
        rotate([0,0,i*v]) translate([Diameter*0.4-0.1,0,0]) cube([Diameter*0.1,0.5,TextHeight]);
    }

    // Months
    for (i=[1:1:12]) {
        rotate([0,0,i*-v+180+v/2+0.5]) translate([Diameter*0.425,0,0]) rotate([0,0,-90]) linear_extrude(TextHeight)text(str(m[i]), halign="center", size=Diameter*0.05, font=Font);
    }

    // Year seperators
    for (i=[0:1:5]) {
        rotate([0,0,(i+3)*v+180+v/2]) translate([Diameter*0.4-0.1,0,0]) cube([Diameter*0.1,0.5,TextHeight]);
    }

    // Years
    for (i=[0:1:4]) {
        rotate([0,0,-(i-8)*-v+0.5]) translate([Diameter*0.47,0,0]) rotate([0,0,90]) linear_extrude(TextHeight)text(str(StartYear+i), halign="center", size=Diameter*0.05, font=Font);
    }
}

translate([-0.5,-Diameter*0.35/len(Text)*TextSizeModifier,0])linear_extrude(TextHeight) text(Text, size=(Diameter*0.7/len(Text)*TextSizeModifier), font=Font, halign="center");

if (!(PreviewWithoutHandle && $preview)) {

    translate([0,0,TextHeight]) {
        hull() {
            cylinder(h=2, d=Diameter*1.05);
            translate([0,0,Diameter*0.25]) sphere(d=Diameter*0.25);
        }
        cylinder(d=Diameter*0.4, h=Diameter*0.5);
        translate([0,0,Diameter*0.7])sphere(d=Diameter*0.75);
    }

}
```