[User]
Hey, I'm looking to create a parametric ambigram generator in OpenSCAD, kind of like the one I saw on Thingiverse, but I want to make sure it's flexible and customizable.

Essentially, I want to be able to input two words or phrases of equal length, and have the script generate a 3D model where those words appear to be readable depending on the viewing angle. I found a basic script that handles this, but I'd like something more robust and parametric.

Here are the key features I would need:

*   **Input words:** The user needs to be able to enter two text strings, and the script should check that they're the same length. If they're not, the script should provide an error.
*   **Letter thickness:** A parameter to control the thickness of the extruded letters.
*   **Letter spacing:**  A parameter to set the spacing between each letter.
*   **X and Y offset:** Parameters to control the amount of horizontal and vertical shift needed to make the letters overlap correctly to form the illusion.
*   **Length offset:** A parameter to control the extension of the extruded letters at the 45 degree angle, to ensure the words fully overlap.
*   **Preview/Final Modes:** Like in the example I saw, I'd like a mode to quickly preview the text and offsets (without intersecting the letters), and another mode to generate the final, merged, stl.
*   **Font selection**: It would be nice to select which font to use.
*   **Rotation Angle**: An additional option to rotate the letters by a certain degree, in addition to the 45 degree angle. This will make other ambigrams possible.
*   **Customization:** It would be very useful to be able to set the letter size.
*   **Error Handling:** If the user enters empty strings, or different lengths, the generator should output a helpful error message.

I am not interested in support for complex kerning, multiple different font types in one object, or multiple lines of text. It would be awesome if the design included comments explaining how to adjust parameters for best results.

For libraries, I'm open to any you feel would make this task easier - I'm especially familiar with BOSL, so I'd be happy to see that used, but I am also okay with using other libraries (like BOSL2, dotSCAD, or MCAD). I'd like to keep it to one or two well-established libraries for now if possible.

Can you help me put something like this together?

[Assistant]
ambigram_generator.scad:
```
/*

    Ambigram Generator

    Author: Dustin Westaby
    Date: June 16, 2022

    Instructions:
    - Set mode=0 and enter your words
    - Press F5 to preview. Rotate the view to test the illusion.
    - Adjust the offsets so the letters fully overlap
    - Set mode=1 then press F6 to render
    - Press F7 to export STL

    Add a plate in your slicer or here for a finished look.

*/

mode = 1; // 0 = TEST (F5), 1 = FINAL (F6)

/* ************* */
/* Customizer */
/* ************* */

// Names MUST have the same number of letters
// Use filler characters like "|"
name_one = ["Y","O","U","R"];
name_two = ["N","A","M","E"];

/* ************* */
/* Math stuff */
/* ************* */

letter_thickness = 10;
letter_spacing = 14;

total_length = 25*len(name_one);

//TBD: replace with triangle math, sohcahtoa
x_offset = -5;
y_offset = 7;
length_offset = 5;

/* ************* */
/* Draw the thing */
/* ************* */

if(mode==1)
{
    for (idx = [ 0 : len(name_one) - 1 ] )
    {
        intersection()
        {
            translate([idx*letter_spacing+x_offset,y_offset,0])
            rotate([90,0,45])
            linear_extrude(height = letter_thickness+length_offset) text(name_one[idx]);

            translate([idx*letter_spacing,letter_thickness,0]) rotate([90,0,0])
            linear_extrude(height = letter_thickness) text(name_two[idx]);
        }
    }
}
else
{
    for (idx = [ 0 : len(name_one) - 1 ] )
    {
        translate([idx*letter_spacing+x_offset,y_offset,0])
        rotate([90,0,45])
        linear_extrude(height = letter_thickness+length_offset) text(name_one[idx]);

        translate([idx*letter_spacing,letter_thickness,0]) rotate([90,0,0])
        linear_extrude(height = letter_thickness) text(name_two[idx]);
    }
}
```


[User]
Hey there, I'm looking to get a parametric buckle designed in OpenSCAD. I've seen some neat designs out there, and I want something with a low profile, easy to print, and sewable. I've got a basic idea in mind but I need your help to generate the code.

I'm thinking of a two-part buckle - a "plug" and a "socket". The plug would slide into the socket and then a small catch would secure them together. I need it to be parametric in terms of the width of the fabric that passes through it, the thickness of the fabric and the material, as well as the overall thickness of structural elements.

Here’s a breakdown of what I need:

**Overall Structure:**

*   Two main parts: a "Plug" and a "Socket."
*   The plug should slide into the socket.
*   A small catch on the socket will engage with a cavity on the plug to secure the connection.
*   Should have a flat/low profile
*   Be designed to be sewn onto a fabric material.
*   Designed to print flat without supports.

**Parameters:**

*   `fabricWidth`: Width of the fabric that will pass through the loop of the buckle.
*   `fabricThk`: Thickness of the fabric.
*   `THK`: Thickness of the structural elements of the buckle.
*   `clr`: Clearance between parts.
*   `overlap`: Catch overlap, controlling how far the catch engages
*   `spring`: Strength of the spring (related to the catch)
*   `part`: a string allowing the user to render just a single part or the whole assembly. This should be able to render the full "Assembly", just the "Plug", or just the "Socket", or to render both "Plug" and "Socket" side-by-side as a "Print" layout.

**Plug specifics:**

*   A loop for the fabric to pass through.
*   A sliding body that engages the socket.
*   A cavity for the catch to secure itself.
*   Rounded edges/chamfers as necessary for smooth operation.

**Socket specifics:**

*   A receiving body for the plug.
*   A loop for the fabric to pass through.
*   A small catch that engages with the plug.
*   A mechanism that allows the catch to spring back into place. (I'm thinking of a flexure).
*   Rounded edges/chamfers as necessary for smooth operation.

**Sewability:**
*   There should be a flat area, without any intersecting or overlapping geometry, surrounding the loop that can be used for sewing the part to the fabric.

**Additional Notes:**

*   I'd like to be able to render either the full assembly, or just the plug or socket individually for testing.
*   I’d like some flexibility on the catch design - I'm thinking a small ramp might be necessary for easier engagement.
*   I'd like the catch/spring mechanism to be relatively robust, so it doesn't break easily.

I don't need it to be super fancy. Just a functional, parametric buckle that I can use for my projects. Let me know what you think, and what you need from me! And can you make the design such that the catch will be printed on top of the main body and that the spring mechanism is a cantilever beam? I don't have any existing code to share with you, so you'll be starting from scratch, but I'm excited to see what you come up with.

I don't have any existing libraries I need you to use for this project.

[Assistant]
YK42_Buckle.scad:
```
//experiment with split button shape
//think about alternate button types
//test some extreme values

/* [Body] */

// - Width of the fabric that should pass through the loop
fabricWidth = 60;

// - Thickness of the fabric
fabricThk = 3;

// - Thickness of structural elements
THK = 4;

// - Clearance between parts
clr = 0.3;

// - Catch overlap
overlap = 0.7;

// - Spring strength
spring = 2;

/* [Display options] */
// - part to render
part = "Print"; //[Assembly:Assembly, Print:Print, Plug:Plug, Socket:Socket]

/* [hidden] */
shoulder = 4;
chamferTB = THK/2;
chamferLR = THK;

XBuckleBlock = fabricThk+THK*2;
YBuckleBlock = fabricWidth+THK*2;
ZBuckleBlock = THK*2;

buttonX = THK;
buttonXPosition = XBuckleBlock;
buttonY = buttonX*2+clr;
ZCatch = overlap+clr;
ZCatchCav = ZCatch+clr*.5;
YCatch = buttonY+2*THK;
YCatchCav = YCatch+4*clr;

plugWidth = fabricWidth-2*(0+clr);
plugHeith = THK*2;
plugLenth = buttonXPosition+buttonX+clr+1+chamferTB-XBuckleBlock; //THK*2;

/* [modelling] */
print_part();
module print_part() {
    if(part=="Assembly") {Plug(); Socket();}
    else if(part=="Print") Print();
    else if(part=="Plug") Plug();
    else if(part=="Socket") translate([plugLenth+clr*3, 0, THK+clr]) Socket();
    else text(size=30,str("ER3: invalid value for 'part'",part));
}
//

module Plug() {
    difference() {
        union() {
            //buckle body
            BuckleLoop();
            //clip profile
            intersection() {
                //body
                translate([XBuckleBlock+plugLenth/2, 0, plugHeith/2])  cube([plugLenth, plugWidth, plugHeith],center=true);
                //shoulder chamfers
                translate([0,0,plugHeith+plugWidth/2-shoulder]) rotate([180+45,0]) cube(99);
                //top/bottom front chamfers
                translate([0,0,plugHeith/2]) rotate([90,0]) cylinder(r=XBuckleBlock+plugLenth+plugHeith/2-chamferTB, h=99, $fn=4, center=true);
//                //left/right front chamfers
//                translate([XBuckleBlock+plugLenth+plugWidth/2-chamferLR, 0, -0.01]) rotate([0,0,135]) cube(99);
            }
        }
        //catch cavity
        translate([buttonXPosition+buttonX/2, 0]) cube([buttonX+2*clr, YCatchCav, ZCatchCav*2], center=true);
        //slot for button
        translate([buttonXPosition-clr, -buttonY/2-clr, -0.01]) cube([99, buttonY+2*clr, 99]);
        //LR chamfers
        translate([XBuckleBlock+plugLenth-chamferLR, -plugWidth/2, -0.01]) rotate([0,0,-60]) cube(99);
    }
}
//

module Socket() {
    difference() {
        union() {
            //socket profile
            x0=0; x1=x0+plugWidth/2-shoulder/2+clr; x2=x0+plugWidth/2+clr; x3=x2+THK;
            y2=0; y1=y2-clr; y0=y1-spring; y3=y2+plugHeith-shoulder; y4=plugHeith-shoulder/2; y5=plugHeith;
            p=[ [x0 ,y0 ],
                [x3 ,y0 ],
                [x3 ,y5 ],
                [x1 ,y5 ],
                [x1 ,y4 ],
                [x2 ,y3 ],
                [x2 ,y1 ],
                [x0 ,y1 ]];
            translate([XBuckleBlock+0.01,0,0]) rotate([90,0,90])
            linear_extrude(plugLenth+clr) union() {polygon(p); mirror([1,0]) polygon(p);}

            //buckle
            translate([plugLenth+clr+XBuckleBlock*2,0, y0]) rotate([0,0,180]) BuckleLoop();

            //button
            translate([buttonXPosition, -buttonY/2, y0]) cube([buttonX,buttonY,-y0+y5+2]);

            //button backing
            difference() {
                translate([buttonXPosition+buttonX+clr, -buttonY/2, y0]) cube([plugLenth-buttonX-clr,buttonY,-y0+y5]);
                translate([XBuckleBlock+plugLenth, -buttonY/2-0.01, y5-chamferTB]) rotate([0,-45,0]) cube(99);
            }

            //catch
            translate([buttonXPosition, -YCatch/2, y1]) difference() {
                cube([buttonX, YCatch,ZCatch]);
                rotate([0,-20])cube(99);
            };
        }

        //'spring' features
        translate([buttonXPosition+buttonX+clr/2, 0]) cube([clr,plugWidth, plugHeith],center=true);
        translate([buttonXPosition-clr/2, 0]) cube([clr,plugWidth, plugHeith],center=true);

        //button split
        translate([buttonXPosition+buttonX/2, 0]) cube([buttonX+0.01, clr, spring*2.01],center=true);
    }
}
//

module BuckleLoop() {
    x1=THK; x2=x1+fabricThk;
    y1=fabricWidth/2; y2=-y1;
    p =[[x1,y1],[x2,y1],[x2,y2],[x1,y2]];
    $fn=20;
    difference () {
        linear_extrude(ZBuckleBlock) difference() {
            offset(r=THK) polygon(p);
            offset(r=0) polygon(p);
        }
        translate([0, 0, ZBuckleBlock]) cube([THK*2.01, fabricWidth, THK*2], center=true);
    }
    translate([THK/2, 0, ZBuckleBlock-THK]) rotate([90,0]) cylinder(d=THK, h=fabricWidth, center=true);
    translate([XBuckleBlock-THK,-YBuckleBlock/2]) cube([THK, YBuckleBlock, ZBuckleBlock]);
}

module Cosssection() {intersection() {
    translate([0,0,-10]) cube([XBuckleBlock+10-T+clr+buttonX/2, 99, 99]);
    union() {
        Plug();
        Socket();
    }
}}

module chamferTBTool(size=1) {
    scale(size) union() {
        translate([0,0,1]) rotate([0,0,45])linear_extrude(0.5,scale=0) square(1,center=true);
        translate([0,0,1]) rotate([180,0,45]) linear_extrude(0.5,scale=0) square(1,center=true);
    }
}

module Print() {
    Plug();
    translate([plugLenth+clr*3, 0, spring+clr]) Socket();
}
```


[User]
I'm looking to create a customizable sign with multiple lines of text, similar to a license plate. I've seen some designs, but they don't quite do what I need. I want to be able to set the text, font, font size, style (bold, italic, regular), vertical position, and letter spacing for each line independently. I'd like to be able to have four lines of text, positioned top, upper-middle, lower-middle, and bottom.

The base should be a rectangular plate with rounded corners, and I'd like to have some mounting holes, one near each corner.  I want to be able to adjust the overall dimensions of the plate, the corner radius, and the mounting hole diameter. The plate should have two layers with the top layer slightly recessed with a different thickness.

I need this to be a parametric design, so I can easily adjust all these things through variables. I'm using OpenSCAD, and if possible please use the MCAD library since I'm already familiar with `boxes.scad`.

[Assistant]
LicencePlate_DualLine.scad:
```
//V1.0 Initial release
//V1.1 Moved holes to align with Muzz64's plate.
//Thinned the plate out a few mm
//V1.2 Added multiple lines
//V1.3 Allowed different fonts, size, etc on different lines
//V1.4 Updated list of fonts - borrwed from https://www.thingiverse.com/thing:739573
// - a down stripped 'main' font list with 250 fonts(many Thanks to tbuser for giving me the list(http://www.thingiverse.com/download:1335652) of the installed fonts)
//V1.5 Minor updates: Default font set to a built in one. Default font size reset to a more manageable size.
//V1.6 The top and bottom lines can be moved vertically through a small range. And....It doesn't work. NFI why.
//V1.7 Think I fixed the issue above (had the slider the wrong way around).
//Fork to SSZ Canada at 1.7
//vSSZ1 - Adjusted text sliders to allow more movement and added second middle line of text

use <MCAD/boxes.scad>

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

//Top Line
    TopLineText = "Top Line";
//Upper Middle Line
    MidLine1Text = "Upper Middle";
//Lower Middle Line
    MidLine2Text = "Lower Middle";
//Line4
    BottomLineText = "Bottom Line";

//CUSTOMIZER VARIABLES END

/* [Text options Top Line] */
fontTop="Liberation Sans"; //[Abril Fatface,Aclonica,Acme,Advent Pro,Akronim,Aladin,Alegreya,Alegreya Sans,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Amaranth,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arvo,Asap,Astloch,Asul,Averia Libre,Averia Serif Libre,Bangers,Basic,Belgrano,Berkshire Swash,Bigshot One,Bilbo Swash Caps,Black Ops One,Bonbon,Bowlby One SC,Brawler,Bubblegum Sans,Butterfly Kids,Cabin Condensed,Caesar Dressing,Cagliostro,Calligraffitti,Capriola,Carter One,Changa One,Chango,Chelsea Market,Cherry Cream Soda,Chewy,Chicle,Chivo,Clicker Script,Coming Soon,Concert One,Condiment,Cookie,Courgette,Covered By Your Grace,Crimson Text,Dancing Script,DejaVu Sans,DejaVu Serif,Dhurjati,Doppio One,Dosis,Droid Sans,Eagle Lake,Electrolize,Emilys Candy,Encode Sans,Encode Sans Compressed,Euphoria Script,Exo,Exo 2,Faster One,Federo,Finger Paint,Fjord One,Fontdiner Swanky,Freckle Face,Fruktur,Gabriela,Geo,Germania One,Give You Glory,Gloria Hallelujah,Goudy Bookletter 1911,Graduate,Grand Hotel,Great Vibes,Griffy,Hanalei Fill,Happy Monkey,Henny Penny,Hind,IM Fell English SC,Indie Flower,Irish Grover,Italianno,Jacques Francois Shadow,Jolly Lodger,Josefin Slab,Joti One,Judson,Just Another Hand,Kalam,Kameron,Karma,Kavoon,Knewave,Kranky,Kristi,Laila,Lakki Reddy,Lancelot,Lato,Leckerli One,Ledger,Lekton,Lemon One,Liberation Sans,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Lobster,Lobster Two,Londrina Outline,Londrina Shadow,Londrina Solid,Lora,Love Ya Like A Sister,Loved by the King,Lovers Quarrel,Luckiest Guy,Lusitana,Macondo,Macondo Swash Caps,Mako,Marck Script,Margarine,Marko One,Maven Pro,McLaren,MedievalSharp,Merienda One,Merriweather,Mervale Script,Metal Mania,Metrophobic,Milonga,Miltonian Tattoo,Miss Fajardose,Molle,Montez,Montserrat,Mr De Haviland,Mystery Quest,Neucha,New Rocker,Niconne,Nosifer,Nothing You Could Do,Noto Sans Oriya,Noto Serif,Nova Square,Nunito,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Over the Rainbow,Overlock,Oxygen,Pacifico,Paprika,Parisienne,Patrick Hand SC,Paytone One,Peralta,Permanent Marker,Piedra,Pirata One,Play,Playball,Poetsen One,Poppins,Press Start 2P,Princess Sofia,PT Mono,Qwigley,Racing Sans One,Raleway,Rancho,Ranga,Ribeye,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rubik One,Sail,Salsa,Sansita One,Sarina,Satisfy,Schoolbell,Seaweed Script,Sevillana,Shadows Into Light,Shadows Into Light Two,Share,Six Caps,Skranji,Source Sans Pro,Spicy Rice,Stardos Stencil,Stoke,Syncopate,Teko,Terminal Dosis,The Girl Next Door,Tillana,Timmana,Titillium Web,Ubuntu,Ultra,Underdog,UnifrakturCook,UnifrakturMaguntia,Vampiro One,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,Waiting for the Sunrise,Wallpoet,Wellfleet,Wendy One,Yellowtail,Yesteryear,Zeyada, License Plate, Unica One]

styleTop="Bold"; //[Regular, Bold, Italic]

fontstyleTop = str(fontTop,":",styleTop);

//Height of characters [mm]
fontsizeTop=6;  //[5:9]

//Top Text Position
TopY = 20; //[12:21]

letter_spacingTop = 1; //[-1:5]
letterspacingTop = 1.0+letter_spacingTop/10;

/* [Text options Middle Line] */
fontMid="Liberation Sans"; //[Abril Fatface,Aclonica,Acme,Advent Pro,Akronim,Aladin,Alegreya,Alegreya Sans,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Amaranth,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arvo,Asap,Astloch,Asul,Averia Libre,Averia Serif Libre,Bangers,Basic,Belgrano,Berkshire Swash,Bigshot One,Bilbo Swash Caps,Black Ops One,Bonbon,Bowlby One SC,Brawler,Bubblegum Sans,Butterfly Kids,Cabin Condensed,Caesar Dressing,Cagliostro,Calligraffitti,Capriola,Carter One,Changa One,Chango,Chelsea Market,Cherry Cream Soda,Chewy,Chicle,Chivo,Clicker Script,Coming Soon,Concert One,Condiment,Cookie,Courgette,Covered By Your Grace,Crimson Text,Dancing Script,DejaVu Sans,DejaVu Serif,Dhurjati,Doppio One,Dosis,Droid Sans,Eagle Lake,Electrolize,Emilys Candy,Encode Sans,Encode Sans Compressed,Euphoria Script,Exo,Exo 2,Faster One,Federo,Finger Paint,Fjord One,Fontdiner Swanky,Freckle Face,Fruktur,Gabriela,Geo,Germania One,Give You Glory,Gloria Hallelujah,Goudy Bookletter 1911,Graduate,Grand Hotel,Great Vibes,Griffy,Hanalei Fill,Happy Monkey,Henny Penny,Hind,IM Fell English SC,Indie Flower,Irish Grover,Italianno,Jacques Francois Shadow,Jolly Lodger,Josefin Slab,Joti One,Judson,Just Another Hand,Kalam,Kameron,Karma,Kavoon,Knewave,Kranky,Kristi,Laila,Lakki Reddy,Lancelot,Lato,Leckerli One,Ledger,Lekton,Lemon One,Liberation Sans,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Lobster,Lobster Two,Londrina Outline,Londrina Shadow,Londrina Solid,Lora,Love Ya Like A Sister,Loved by the King,Lovers Quarrel,Luckiest Guy,Lusitana,Macondo,Macondo Swash Caps,Mako,Marck Script,Margarine,Marko One,Maven Pro,McLaren,MedievalSharp,Merienda One,Merriweather,Mervale Script,Metal Mania,Metrophobic,Milonga,Miltonian Tattoo,Miss Fajardose,Molle,Montez,Montserrat,Mr De Haviland,Mystery Quest,Neucha,New Rocker,Niconne,Nosifer,Nothing You Could Do,Noto Sans Oriya,Noto Serif,Nova Square,Nunito,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Over the Rainbow,Overlock,Oxygen,Pacifico,Paprika,Parisienne,Patrick Hand SC,Paytone One,Peralta,Permanent Marker,Piedra,Pirata One,Play,Playball,Poetsen One,Poppins,Press Start 2P,Princess Sofia,PT Mono,Qwigley,Racing Sans One,Raleway,Rancho,Ranga,Ribeye,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rubik One,Sail,Salsa,Sansita One,Sarina,Satisfy,Schoolbell,Seaweed Script,Sevillana,Shadows Into Light,Shadows Into Light Two,Share,Six Caps,Skranji,Source Sans Pro,Spicy Rice,Stardos Stencil,Stoke,Syncopate,Teko,Terminal Dosis,The Girl Next Door,Tillana,Timmana,Titillium Web,Ubuntu,Ultra,Underdog,UnifrakturCook,UnifrakturMaguntia,Vampiro One,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,Waiting for the Sunrise,Wallpoet,Wellfleet,Wendy One,Yellowtail,Yesteryear,Zeyada, License Plate, Unica One]

styleMid="Bold"; //[Regular, Bold, Italic]

fontstyleMid = str(fontMid,":",styleMid);

//Height of characters [mm]
fontsizeMid=8;  //[5:16]

letter_spacingMid = 1; //[-1:5]
letterspacingMid = 1.0+letter_spacingMid/10;

MidLine1Position=5; //[-8:8]

/* [Text options Second Middle Line] */
fontMid2="Liberation Sans"; //[Abril Fatface,Aclonica,Acme,Advent Pro,Akronim,Aladin,Alegreya,Alegreya Sans,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Amaranth,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arvo,Asap,Astloch,Asul,Averia Libre,Averia Serif Libre,Bangers,Basic,Belgrano,Berkshire Swash,Bigshot One,Bilbo Swash Caps,Black Ops One,Bonbon,Bowlby One SC,Brawler,Bubblegum Sans,Butterfly Kids,Cabin Condensed,Caesar Dressing,Cagliostro,Calligraffitti,Capriola,Carter One,Changa One,Chango,Chelsea Market,Cherry Cream Soda,Chewy,Chicle,Chivo,Clicker Script,Coming Soon,Concert One,Condiment,Cookie,Courgette,Covered By Your Grace,Crimson Text,Dancing Script,DejaVu Sans,DejaVu Serif,Dhurjati,Doppio One,Dosis,Droid Sans,Eagle Lake,Electrolize,Emilys Candy,Encode Sans,Encode Sans Compressed,Euphoria Script,Exo,Exo 2,Faster One,Federo,Finger Paint,Fjord One,Fontdiner Swanky,Freckle Face,Fruktur,Gabriela,Geo,Germania One,Give You Glory,Gloria Hallelujah,Goudy Bookletter 1911,Graduate,Grand Hotel,Great Vibes,Griffy,Hanalei Fill,Happy Monkey,Henny Penny,Hind,IM Fell English SC,Indie Flower,Irish Grover,Italianno,Jacques Francois Shadow,Jolly Lodger,Josefin Slab,Joti One,Judson,Just Another Hand,Kalam,Kameron,Karma,Kavoon,Knewave,Kranky,Kristi,Laila,Lakki Reddy,Lancelot,Lato,Leckerli One,Ledger,Lekton,Lemon One,Liberation Sans,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Lobster,Lobster Two,Londrina Outline,Londrina Shadow,Londrina Solid,Lora,Love Ya Like A Sister,Loved by the King,Lovers Quarrel,Luckiest Guy,Lusitana,Macondo,Macondo Swash Caps,Mako,Marck Script,Margarine,Marko One,Maven Pro,McLaren,MedievalSharp,Merienda One,Merriweather,Mervale Script,Metal Mania,Metrophobic,Milonga,Miltonian Tattoo,Miss Fajardose,Molle,Montez,Montserrat,Mr De Haviland,Mystery Quest,Neucha,New Rocker,Niconne,Nosifer,Nothing You Could Do,Noto Sans Oriya,Noto Serif,Nova Square,Nunito,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Over the Rainbow,Overlock,Oxygen,Pacifico,Paprika,Parisienne,Patrick Hand SC,Paytone One,Peralta,Permanent Marker,Piedra,Pirata One,Play,Playball,Poetsen One,Poppins,Press Start 2P,Princess Sofia,PT Mono,Qwigley,Racing Sans One,Raleway,Rancho,Ranga,Ribeye,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rubik One,Sail,Salsa,Sansita One,Sarina,Satisfy,Schoolbell,Seaweed Script,Sevillana,Shadows Into Light,Shadows Into Light Two,Share,Six Caps,Skranji,Source Sans Pro,Spicy Rice,Stardos Stencil,Stoke,Syncopate,Teko,Terminal Dosis,The Girl Next Door,Tillana,Timmana,Titillium Web,Ubuntu,Ultra,Underdog,UnifrakturCook,UnifrakturMaguntia,Vampiro One,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,Waiting for the Sunrise,Wallpoet,Wellfleet,Wendy One,Yellowtail,Yesteryear,Zeyada, License Plate, Unica One]

styleMid2="Bold"; //[Regular, Bold, Italic]

fontstyleMid2 = str(fontMid2,":",styleMid2);

//Height of characters [mm]
fontsizeMid2=8;  //[5:16]

letter_spacingMid2 = 1; //[-1:5]
letterspacingMid2 = 1.0+letter_spacingMid2/10;

MidLine2Position=-5; //[-8:8]

/* [Text options Bottom Line] */
fontBot="Liberation Sans"; //[Abril Fatface,Aclonica,Acme,Advent Pro,Akronim,Aladin,Alegreya,Alegreya Sans,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Amaranth,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arvo,Asap,Astloch,Asul,Averia Libre,Averia Serif Libre,Bangers,Basic,Belgrano,Berkshire Swash,Bigshot One,Bilbo Swash Caps,Black Ops One,Bonbon,Bowlby One SC,Brawler,Bubblegum Sans,Butterfly Kids,Cabin Condensed,Caesar Dressing,Cagliostro,Calligraffitti,Capriola,Carter One,Changa One,Chango,Chelsea Market,Cherry Cream Soda,Chewy,Chicle,Chivo,Clicker Script,Coming Soon,Concert One,Condiment,Cookie,Courgette,Covered By Your Grace,Crimson Text,Dancing Script,DejaVu Sans,DejaVu Serif,Dhurjati,Doppio One,Dosis,Droid Sans,Eagle Lake,Electrolize,Emilys Candy,Encode Sans,Encode Sans Compressed,Euphoria Script,Exo,Exo 2,Faster One,Federo,Finger Paint,Fjord One,Fontdiner Swanky,Freckle Face,Fruktur,Gabriela,Geo,Germania One,Give You Glory,Gloria Hallelujah,Goudy Bookletter 1911,Graduate,Grand Hotel,Great Vibes,Griffy,Hanalei Fill,Happy Monkey,Henny Penny,Hind,IM Fell English SC,Indie Flower,Irish Grover,Italianno,Jacques Francois Shadow,Jolly Lodger,Josefin Slab,Joti One,Judson,Just Another Hand,Kalam,Kameron,Karma,Kavoon,Knewave,Kranky,Kristi,Laila,Lakki Reddy,Lancelot,Lato,Leckerli One,Ledger,Lekton,Lemon One,Liberation Sans,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Lobster,Lobster Two,Londrina Outline,Londrina Shadow,Londrina Solid,Lora,Love Ya Like A Sister,Loved by the King,Lovers Quarrel,Luckiest Guy,Lusitana,Macondo,Macondo Swash Caps,Mako,Marck Script,Margarine,Marko One,Maven Pro,McLaren,MedievalSharp,Merienda One,Merriweather,Mervale Script,Metal Mania,Metrophobic,Milonga,Miltonian Tattoo,Miss Fajardose,Molle,Montez,Montserrat,Mr De Haviland,Mystery Quest,Neucha,New Rocker,Niconne,Nosifer,Nothing You Could Do,Noto Sans Oriya,Noto Serif,Nova Square,Nunito,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Over the Rainbow,Overlock,Oxygen,Pacifico,Paprika,Parisienne,Patrick Hand SC,Paytone One,Peralta,Permanent Marker,Piedra,Pirata One,Play,Playball,Poetsen One,Poppins,Press Start 2P,Princess Sofia,PT Mono,Qwigley,Racing Sans One,Raleway,Rancho,Ranga,Ribeye,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rubik One,Sail,Salsa,Sansita One,Sarina,Satisfy,Schoolbell,Seaweed Script,Sevillana,Shadows Into Light,Shadows Into Light Two,Share,Six Caps,Skranji,Source Sans Pro,Spicy Rice,Stardos Stencil,Stoke,Syncopate,Teko,Terminal Dosis,The Girl Next Door,Tillana,Timmana,Titillium Web,Ubuntu,Ultra,Underdog,UnifrakturCook,UnifrakturMaguntia,Vampiro One,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,Waiting for the Sunrise,Wallpoet,Wellfleet,Wendy One,Yellowtail,Yesteryear,Zeyada, License Plate, Unica One]

styleBot="Bold"; //[Regular, Bold, Italic]

fontstyleBot = str(fontBot,":",styleBot);

//Height of characters [mm]
fontsizeBot=6;  //[2:9]

//Bottom Text Position
BotY=-13; //[-17:-6]

letter_spacingBot = 1; //[-1:5]
letterspacingBot = 1.0+letter_spacingBot/10;

difference (){
    $fn=100;
    roundedBox([94,47,3],4,true);
    translate([0,0,1.5]) roundedBox([89,42,3],3,true);
    translate([-30,17,-5]) cylinder(h=10, r=2);
    translate([-30,-17,-5]) cylinder(h=10, r=2);
    translate([30,17,-5]) cylinder(h=10, r=2);
    translate([30,-17,-5]) cylinder(h=10, r=2);
}
translate([0,TopY,-1]){
    $fn=100;
    linear_extrude(2.5) text(TopLineText, fontsizeTop, font = fontstyleTop, valign = "top", halign = "center", spacing = letterspacingTop);
}
translate([0,MidLine1Position,-1]){
    $fn=100;
    linear_extrude(2.5) text(MidLine1Text, fontsizeMid, font = fontstyleMid, valign = "center", halign = "center", spacing = letterspacingMid);

}
translate([0,MidLine2Position,-1]){
    $fn=100;
    linear_extrude(2.5) text(MidLine2Text, fontsizeMid2, font = fontstyleMid, valign = "center", halign = "center", spacing = letterspacingMid);

}
translate([0,BotY,-1]){
    linear_extrude(2.5) text(BottomLineText, fontsizeBot, font = fontstyleBot, valign = "top", halign = "center", spacing = letterspacingBot);
}

```


[User]
I'm looking for an OpenSCAD design for a customizable universal cuff, something that can hold utensils or other objects. I want to be able to adjust the size of both the cuff itself (like the wristband part) and the diameter of the hole that will hold the object.

I need to be able to specify several parameters:

*   **Cuff Inner Diameter:** The diameter of the wrist opening.
*   **Cuff Width:** The width of the wristband itself.
*  **Cuff Thickness:** The thickness of the wristband itself.
*   **Hole Diameter:** The diameter of the hole for the utensil/object.
*   **Hole Depth:** The depth of the hole.

The cuff should be roughly cylindrical, though I wouldn't mind if you made it slightly oval shaped to fit more comfortably, if that's something that's easy. The hole should be centered on the face of the cuff, perpendicular to the cuff cylinder. It should also be cylindrical. I'd like the main body of the cuff to be a solid print; I don't need it to be printed with any internal support structure.

I'm not sure if it matters, but I'd like to use the `MCAD` library for any helper functions that might be useful, but I'm not sure if you'd need it. I am using OpenSCAD 2021.01.

[Assistant]
Customizable_Universal_Cuff.scad:
```
translate([20,0,0]) import("/Users/natalieslemp/Downloads/Universal Cuff-2.stl");
detail = 4;

scale_factor = 1.0;

layer_height = 0.2;
nozzle_size = 0.4;

bottom_height = 1 * layer_height;
fill_height= 2 * layer_height;
top_height = 3 * layer_height;
line_width = 1 * nozzle_size;
antenna_width = 2 * nozzle_size;
extra_outline_width = 2 * nozzle_size;

#difference()for(i=[1]) rotate([0,90,360]) translate([-12.5,-20.32,42]) cylinder(d=15.88,h=20,$fn=32, center=true);
mirror([-35,0,0]) #difference()for(i=[1]) rotate([0,90,360]) translate([-12.5,-20.32,23]) cylinder(d=15.88,h=20,$fn=32, center=true);
```


[User]
Hey, I'm looking to design some custom Gridfinity bins, and I'd like them to be parametric. I've seen some existing designs, but they don't quite fit my needs, so I'd like to start from scratch.

Here's what I'm thinking:

**Overall Structure:**

*   The core of the design should be based on the Gridfinity standard. I'd need to be able to specify the width and depth in grid units, where one unit is 42mm. I need it to use the standard 7mm unit height as well, and the height of the box should be variable based on the number of those units stacked.
*   I'd also like to be able to choose how many chambers the bin is divided into. There should be dividing walls, and the walls must go all the way from the floor to the top of the main wall.
*   The main part of the design should be a module I can call, similar to the provided `basic_cup` module.

**Customization:**

*   I need a way to optionally include holes for magnets on the bottom. These should be sized for press-fit 6mm diameter magnets, with some amount of adjustment possible for the hole diameter, because different printers may make the fit a bit different. I need to specify the height for the magnets as well. I'm thinking something like 3mm height should work okay. I saw a cone shape at the bottom of those magnet holes in some designs, I think that's a great idea.
*   I need to optionally include a label shelf on the front side of the container. It should be a half-cylinder that extends out, and can be used to add a printed label.
*   I also really like the idea of a finger slide on the bottom to help remove small parts. I'd like to make this optional too.

**Modules/Libraries:**

*   I believe the design should use a similar pattern as the example in the description, using a `grid_block` to form the base and `partitioned_cavity` or similar to form the interior.
*   I think all the `gridcopy`, `cornercopy`, `pad_oversize` and other supporting modules should probably be defined somewhere and then included. So that the module can be called from a top-level definition, and the various modules are still available.
*   I don't need any external libraries (like BOSL), but you should make sure the code is well-formatted and readable.
    I'm not sure how feasible some of the design is, but I'm willing to try something out.
    Can you generate the necessary OpenSCAD files and the core module?

[Assistant]
gridfinity_basic_cup.scad:
```

/* [Basics] */

// Width of the box
width = 1; //[1:20]

// Depth of the box
depth = 1; //[1:20]

// Height of the box in units of of 7mm
// Normally a full size box is 6 units high
height = 6; //[2:24]

// Number of chambers
chambers = 1; //[1:20]

// Should there be a shelf for a label
label = true; //[true, false]

// Finger slides at the bottom to help get small parts out
finger_slide = true; //[true, false]

/* [Magnets] */

// Should there be magnet holes?
magnets = true; //[true, false]

// Height of magnets in mm
magnet_height = 3; // [2, 3]

// Tune the diameter of magnet holes (in mm) for good press-fit tightness.  6.1mm usually works well
magnet_diameter = 6.1; //[5.9:0.02:6.3]

basic_cup(depth, width, height, chambers=chambers);

module basic_cup(num_x=1, num_y=1, num_z=2, bottom_holes=0, chambers=1) {
  difference() {
    grid_block(num_x, num_y, num_z, magnets ? magnet_height : 0);
    color("red") partitioned_cavity(num_x, num_y, num_z, chambers);
  }
}

module partitioned_cavity(num_x=2, num_y=1, num_z=2, chambers=3) {
  // cavity with removed segments so that we leave dividing walls behind
  gp = gridfinity_pitch;
  outer_wall_th = 1.8;  // cavity is this far away from the 42mm 'ideal' block
  inner_wall_th =1.2;

  bar_d = 1.2;
  zpoint = gridfinity_zpitch*num_z;

  yz = [[ (num_y-0.5)*gridfinity_pitch-14, zpoint-bar_d/2 ],
    [ (num_y-0.5)*gridfinity_pitch, zpoint-bar_d/2 ],
    [ (num_y-0.5)*gridfinity_pitch, zpoint-bar_d/2-10.18 ]
  ];

  cavity_xsize = gp*num_x-2*outer_wall_th;
  chamber_pitch = (cavity_xsize+inner_wall_th)/chambers;  // period of repeating walls

  difference() {
    basic_cavity(num_x, num_y, num_z);

    if (chambers >= 2) {
      for (i=[1:chambers-1]) {
        translate([-gp/2+outer_wall_th-inner_wall_th+i*chamber_pitch, -gp/2, 0])
        cube([inner_wall_th, gp*num_y, gridfinity_zpitch*(num_z+1)]);
      }
    }
    // this is the label
    if (label) {
      hull() for (i=[0,1, 2])
      translate([-gridfinity_pitch/2, yz[i][0], yz[i][1]])
      rotate([0, 90, 0]) cylinder(d=bar_d, h=num_x*gridfinity_pitch, $fn=24);
    }
  }
}

module basic_cavity(num_x=2, num_y=1, num_z=2) {
  eps = 0.1;
  q = 1.65;
  q2 = 2.15;
  zpoint = gridfinity_zpitch*num_z-1.2;
  facets = 13;

  difference() {
    union() {
      // cut downward from base
      hull() cornercopy(17, num_x, num_y) {
        translate([0, 0, zpoint-eps]) cylinder(d=2.3, h=1.2+2*eps, $fn=24);
      }

      hull() cornercopy(17, num_x, num_y) {
        // create bevels below the lip
        translate([0, 0, zpoint-0.1]) cylinder(d=2.3, h=0.1, $fn=24);
        translate([0, 0, zpoint-q-q2]) cylinder(d=2.3+2*q, h=q2, $fn=32);
        // create rounded bottom of bowl (8.3 is high enough to not expose gaps)
        translate([0, 0, 8.3]) sphere(d=2.3+2*q, $fn=32);
      }
    }

    // cut away from the negative to leave behind wall to make it easier to remove piece
    pivot_z = 11.6+2;
    pivot_y = -12+2;

    // rounded inside bottom
    if(finger_slide){
    for (ai=[0:facets-1]) translate([0, pivot_y, pivot_z])
      rotate([90*ai/(facets-1), 0, 0]) translate([0, -pivot_y, -pivot_z])
      translate([-gridfinity_pitch/2, -10-17-1.15, 0])
      cube([gridfinity_pitch*num_x, 10, gridfinity_zpitch*num_z+5]);
    }
  }
}
gridfinity_pitch = 42;
gridfinity_zpitch = 7;
gridfinity_clearance = 0.5;  // each bin is undersize by this much

// basic block with cutout in top to be stackable, optional holes in bottom
// start with this and begin 'carving'
module grid_block(num_x=1, num_y=1, num_z=2, bot_hole_depth=3.1, center=false) {
  bot_hole_extra = 0.1;
  corner_radius = 3.75;
  outer_size = gridfinity_pitch - gridfinity_clearance;  // typically 41.5
  block_corner_position = outer_size/2 - corner_radius;  // need not match center of pad corners

  totalht=gridfinity_zpitch*num_z+3.75;
  translate( center ? [-(num_x-1)*gridfinity_pitch/2, -(num_y-1)*gridfinity_pitch/2, 0] : [0, 0, 0] )
  difference() {
    intersection() {
      union() {
        // grid of pads
        gridcopy(num_x, num_y) pad_oversize();
        // main body will be cut down afterward
        translate([-gridfinity_pitch/2, -gridfinity_pitch/2, 5])
        cube([gridfinity_pitch*num_x, gridfinity_pitch*num_y, totalht-5]);
      }

      // crop with outer cylinders
      translate([0, 0, -0.1])
      hull()
      cornercopy(block_corner_position, num_x, num_y)
      cylinder(r=corner_radius, h=totalht+0.2, $fn=44);
    }

    // remove top so XxY can fit on top
      color("blue")
      translate([0, 0, gridfinity_zpitch*num_z])
      pad_oversize(num_x, num_y, 1);

    // add holes in bottom pads if requested
    if (bot_hole_depth > 0) {
      gridcopy(num_x, num_y) bottomholes(bot_hole_depth + bot_hole_extra);
    }
  }
}

// holes in the bottom of the pads (slightly short to allow deeper
module bottomholes(depth=3.1) {
  magnet_position = 13;
  magnet_od = magnet_diameter;
  magnet_thickness = depth;
  eps = 0.1;  // differences are annoying with coincident faces
  if (depth > 0) {
    cornercopy(magnet_position) {
      translate([0, 0, -eps])
            hull() {
                cylinder(d=magnet_od, h=magnet_thickness+eps, $fn=41);
                translate([0, 0, magnet_thickness]) cylinder(d1=magnet_od, d2=magnet_od/2, h=magnet_od/4, $fn=42);
            }
      cylinder(d=1, h=depth, $fn=28);
    }
  }
}

// unit pad slightly oversize at the top to be trimmed or joined with other feet or the rest of the model
// also useful as cutouts for stacking
module pad_oversize(num_x=1, num_y=1, margins=0) {
  pad_corner_position = 17; // must be 17 to be compatible
  bevel1_top = 0.8;     // z of top of bottom-most bevel (bottom of bevel is at z=0)
  bevel2_bottom = 2.6;  // z of bottom of second bevel
  bevel2_top = 5;       // z of top of second bevel
  bonus_ht = 0.2;       // extra height (and radius) on second bevel

  // female parts are a bit oversize for a nicer fit
  radialgap = margins ? 0.25 : 0;  // oversize cylinders for a bit of clearance
  axialdown = margins ? 0.1 : 0;   // a tiny bit of axial clearance present in Zack's design

  translate([0, 0, -axialdown])
  difference() {
    union() {
      hull() cornercopy(pad_corner_position, num_x, num_y) {
        cylinder(d=1.6+2*radialgap, h=0.1, $fn=24);
        translate([0, 0, bevel1_top]) cylinder(d=3.2+2*radialgap, h=1.9, $fn=32);
      }

      hull() cornercopy(pad_corner_position, num_x, num_y) {
        translate([0, 0, bevel2_bottom])
        cylinder(d1=3.2+2*radialgap, d2=7.5+0.5+2*radialgap+2*bonus_ht, h=bevel2_top-bevel2_bottom+bonus_ht, $fn=32);
      }
    }

    // cut off bottom if we're going to go negative
    if (margins) {
      translate([-gridfinity_pitch/2, -gridfinity_pitch/2, 0])
      cube([gridfinity_pitch*num_x, gridfinity_pitch*num_y, axialdown]);
    }
  }
}

// similar to quadtranslate but expands to extremities of a block
module cornercopy(r, num_x=1, num_y=1) {
  for (xx=[-r, gridfinity_pitch*(num_x-1)+r]) for (yy=[-r, gridfinity_pitch*(num_y-1)+r])
    translate([xx, yy, 0]) children();
}

// make repeated copies of something(s) at the gridfinity spacing of 42mm
module gridcopy(num_x, num_y) {
  for (xi=[1:num_x]) for (yi=[1:num_y]) translate([gridfinity_pitch*(xi-1), gridfinity_pitch*(yi-1), 0]) children();
}

```


[User]
I'd like to generate some cartridge cases, and I'm hoping you can help me with the OpenSCAD code. I need a parametric design that can handle different types of cartridges and different array configurations.

Here's what I'm looking for:

*   **Cartridge Types:** I need to support these cartridge types, each with specific dimensions (you can assume I'll provide the dimensions): NES, SNES, N64, DS/2DS/3DS, Switch, GameBoy, PSP/UMD, VIC20, C64, Atari 800, Atari 2600, Atari 5200, SD, microSD, and Memory Stick Duo.
*   **Array Configuration:** I want to be able to specify the number of rows and columns for the case.
*   **Lid Option:** I need an option to include a lid or not. The lid should fit snugly on top of the base and should be generated as a separate object.
*   **Parametric Design:** All dimensions (width, depth, height, thickness, tolerance, and corner radius) should be parametric.
*   **Rounded Corners:** I would like the edges of the case and the lid to be rounded with a specified radius.
*   **Minimalist Design:** The overall design should be as simple and functional as possible, avoiding unnecessary design elements.
*   **Material Thickness and Tolerance:** I need to be able to specify the material thickness and some tolerance for a snug fit.
*   **OpenSCAD:** The code needs to be written in OpenSCAD.

I'd like the output to be a single OpenSCAD file that I can customize with the parameters I mentioned above. Could you write this for me?

[Assistant]
cartridge-case.scad:
```
// SPDX-License-Identifier: CC-BY-NC-SA-4.0

/*
 * Copyright (c) 2022 - Felipe Balbi <felipe@balbi.sh>
 */

/* [Parameters] */
// Number of rows
rows			= 10;

// Number of columns
columns			= 1;

// Generate Lid?
lid			= false;

// Cartridge Type
type			= "NES"; // [ NES, SNES, N64, DS/2DS/3DS, Switch, GameBoy, PSP/UMD, VIC20, C64, Atari 800, Atari 2600, Atari 5200, SD, microSD, Memory Stick Duo ]

/* [Hidden] */
// Number of faces
$fn			= 50;

// Thickness of all walls
material_thickness	= 2;

// Extra slack/tolerance
material_tolerance	= 0.4;

// Corner Radius
material_radius		= 1;

/*		       NAME		Width	Depth	Height	Ratio */
/* Nintendo */
nes_cartridge	    = ["NES",		119.8,	16.7,	134.5,	3/5];
snes_cartridge	    = ["SNES",		135,	20,	86,	3/5];
n64_cartridge	    = ["N64",		116,	18,	75,	3/5];
3ds_cartridge	    = ["DS/2DS/3DS",	32.9,	3.7,	35,	3/5];
switch_cartridge    = ["Switch",	21.4,	3.4,	31.2,	1/2];
gb_cartridge        = ["GameBoy",	57,	7.7,	65,	2/3];

/* Sony */
psp_cartridge	    = ["PSP",		64,	4.2,	65,	3/5];

/* Commodore */
vic20_cartridge	    = ["VIC20",		139,	17,	79,	3/5];
c64_cartridge	    = ["C64",		68.5,	20.1,	88.9,	3/5];

/* Atari */
atari800_cartridge  = ["Atari 800",	66,	22,	77,	3/5];
atari2600_cartridge = ["Atari 2600",	83,	22,	99,	3/5];
atari5200_cartridge = ["Atari 5200",	104,	17,	112,	3/5];

/* Memory Cards */
sd_cartridge        = ["SD",		24,	2.2,	32,	3/4];
microsd_cartridge   = ["microSD",	11,	1,	15,	4/5];
msd_cartridge	    = ["MSD",		20,	1.6,	30.8,	4/5];

function cartridge_name(cart)		= cart[0];
function cartridge_width(cart)		= cart[1];
function cartridge_depth(cart)		= cart[2];
function cartridge_height(cart)		= cart[3];
function cartridge_ratio(cart)		= cart[4];

module rounded_cube(dims = [10, 10, 10, 1]) {
  function get_width(dims)	= dims[0];
  function get_depth(dims)	= dims[1];
  function get_height(dims)	= dims[2];
  function get_radius(dims)	= dims[3];

  width		= get_width(dims);
  depth		= get_depth(dims);
  height	= get_height(dims);
  radius	= get_radius(dims);

  points = [[radius, radius],
	    [width - radius, radius],
	    [radius, depth - radius],
	    [width - radius, depth - radius]];

  hull() {
    for (p = points) {
      translate(p) cylinder(h = height, r = radius);
    }
  }
}

module make_array(dims) {
  function get_rows(dims)	= dims[0];
  function get_cols(dims)	= dims[1];
  function get_width(dims)	= dims[2];
  function get_depth(dims)	= dims[3];
  function get_thickness(dims)	= dims[4];
  function get_tolerance(dims)	= dims[5];

  rows		= get_rows(dims);
  cols		= get_cols(dims);
  width		= get_width(dims);
  depth		= get_depth(dims);
  thickness	= get_thickness(dims);
  tolerance	= get_tolerance(dims);

  for (i = [0:1:cols - 1]) {
    for (j = [0:1:rows - 1]) {
      x = (2 * thickness + width + tolerance) * i + thickness;
      y = (2 * thickness + depth + tolerance) * j + thickness;

      translate([x, y, thickness])
	children();
    }
  }
}

module cartridge_slot(cart) {
  width		= cartridge_width(cart);
  depth		= cartridge_depth(cart);
  height	= cartridge_height(cart);

  total_width	= width + 2 * material_tolerance;
  total_depth	= depth + 2 * material_tolerance;
  total_height	= height;

  rounded_cube([total_width, total_depth, total_height, material_radius]);
}

module cartridge_case_base(rows, cols, cart) {
  width		= cartridge_width(cart);
  depth		= cartridge_depth(cart);
  height	= cartridge_height(cart);
  ratio		= cartridge_ratio(cart);

  total_width	= cols * (width + material_tolerance + 2 * material_thickness);
  total_depth	= rows * (depth + material_tolerance + 2 * material_thickness);
  total_height	= height * ratio;

  echo(str("Total Dimensions: ", total_width, " mm ✕ ", total_depth, " mm ✕ ",
	   total_height, " mm."));

  rounded_cube([total_width, total_depth, total_height, material_radius]);
}

module cartridge_case_lid(rows, cols, cart) {
  name		= cartridge_name(cart);
  width		= cartridge_width(cart);
  depth		= cartridge_depth(cart);
  height	= cartridge_height(cart);
  ratio		= cartridge_ratio(cart);

  total_width	= cols * (width + material_tolerance + 2 * material_thickness)
    + material_tolerance + 2 * material_thickness;

  total_depth	= rows * (depth + material_tolerance + 2 * material_thickness)
    + material_tolerance + 2 * material_thickness;

  total_height	= (height * ratio) + material_thickness + material_tolerance;

  echo(str("Generating lid for ", name, " Cartridge Type"));

  difference() {
    rounded_cube([total_width, total_depth, total_height, material_radius]);

    translate([material_thickness, material_thickness, -material_thickness])
      rounded_cube([total_width - 2 * material_thickness,
		    total_depth - 2 * material_thickness,
		    total_height - material_thickness,
		    material_radius]);
  }
}

module cartridge_case(rows, cols, cart) {
  name		= cartridge_name(cart);
  width		= cartridge_width(cart);
  depth		= cartridge_depth(cart);
  height	= cartridge_height(cart);

  echo(str("Generating case for ", name, " Cartridge Type"));

  total_width	= cols * (width + material_tolerance + 2 * material_thickness)
    + material_tolerance + 2 * material_thickness;

  difference() {
    cartridge_case_base(rows, columns, cart);

    make_array([rows, cols, width, depth, material_thickness, material_tolerance])
      cartridge_slot(cart);
  }

  if (lid) {
    translate([total_width, 0, 0])
      cartridge_case_lid(rows, cols, cart);
  }
}

if (type == "NES")
  cartridge_case(rows, columns, nes_cartridge);
else if (type == "SNES")
  cartridge_case(rows, columns, snes_cartridge);
else if (type == "N64")
  cartridge_case(rows, columns, n64_cartridge);
else if (type == "DS/2DS/3DS")
  cartridge_case(rows, columns, 3ds_cartridge);
else if (type == "Switch")
  cartridge_case(rows, columns, switch_cartridge);
else if (type == "GameBoy")
  cartridge_case(rows, columns, gb_cartridge);
else if (type == "PSP/UMD")
  cartridge_case(rows, columns, psp_cartridge);
else if (type == "VIC20")
  cartridge_case(rows, columns, vic20_cartridge);
else if (type == "C64")
  cartridge_case(rows, columns, c64_cartridge);
else if (type == "Atari 800")
  cartridge_case(rows, columns, atari800_cartridge);
else if (type == "Atari 2600")
  cartridge_case(rows, columns, atari2600_cartridge);
else if (type == "Atari 5200")
  cartridge_case(rows, columns, atari5200_cartridge);
else if (type == "SD")
  cartridge_case(rows, columns, sd_cartridge);
else if (type == "microSD")
  cartridge_case(rows, columns, microsd_cartridge);
else if (type == "Memory Stick Duo")
  cartridge_case(rows, columns, msd_cartridge);

```


[User]
I'd like to create a parametric OpenSCAD design for a horizontal test tube holder box, similar to the one described at codeandmake.com. I need it to hold multiple test tubes horizontally, and also include a lid.

The design should be fully customizable, allowing me to specify things like the test tube diameter, test tube length, the number of tubes it should hold, and the thickness of the material. I would also like to be able to control the gap between the tubes, as well as the gap at the ends of the tubes, the number of rests that support the test tubes, and the size and fit of the lid.

I need two main parts: a base and a lid. The base will have recesses to hold the test tubes, and the lid should fit over the base, ideally with an adjustable gap around its sides. I need control over the lid's height as a percentage of other measurements in the base.

For preview purposes, it would be beneficial to have an option to show test tubes within the base, and also to lift the lid by a percentage of its height, so I can visualize the design better before printing. The final product should be exportable as an .stl file.

I'd like all dimensions to be parametrically defined.

I do not need to use any special libraries.

Let me know if you need more information.

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

/*
 * Horizontal Test Tube Holder Box by Code and Make (https://codeandmake.com/)
 *
 * https://codeandmake.com/post/horizontal-test-tube-holder-box
 *
 * Horizontal Test Tube Holder Box v1.0 (16 July 2022)
 */

 /* [General] */

// Part to display
Part = 0; // [0: Base, 1: Lid, 2: All - Preview Only]

// The thickness of the material
Material_Thickness = 3; // [2:0.1:5]

/* [Base] */

// Diameter of the test tube holder
Test_Tube_Diameter = 17; // [5:1:30]

// Length of the test tube holder
Test_Tube_Length = 151; // [50:1:200]

// Number of test tubes
Test_Tubes = 3; // [1:1:10]

// Gap at either end of the test tube(s)
Test_Tube_Gap = 5; // [0:1:100]

// Number of rests
Rests = 3; // [2:1:10]

/* [Lid] */

// Gap around all sides of the lid
Lid_Gap = 1; // [0.5:0.1:5]

// Height of the lid as a percentage
Lid_Height_Percent = 25; // [1:1:100]

/* [Preview] */

// Show test tubes in base (preview only)
Show_Test_Tubes = 1; // [0: No, 1: Yes]

// Amount to lift the lid (preview only)
Lift_Lid_Percent = 0; // [1:1:500]

module testTubeHolderBox() {
  $fn = 60;

  module lid() {
    linear_extrude(height = Material_Thickness, convexity = 10) {
      lidProfile();
    }

    linear_extrude(height = Material_Thickness + ((Lid_Height_Percent / 100) * ((Material_Thickness * 3) + Test_Tube_Diameter)), convexity = 10) {
      lidWallProfile();
    }
  }

  module lidWallProfile() {
    difference() {
      offset(r = (Material_Thickness * 2) + Lid_Gap) {
        baseProfile();
      }

      lidProfile();
    }
  }

  module lidProfile() {
    offset(r = Material_Thickness + Lid_Gap) {
      baseProfile();
    }
  }

  module base() {
    linear_extrude(height = Material_Thickness, convexity = 10) {
      baseProfile();
    }

    linear_extrude(height = (Material_Thickness * 3) + Test_Tube_Diameter, convexity = 10) {
      baseWallProfile();
    }

    translate([Material_Thickness, Material_Thickness, 0]) {
      for (i=[0:Test_Tubes - 1]) {
        translate([Test_Tube_Gap, i * (Test_Tube_Diameter + (Material_Thickness * 2)), Material_Thickness]) {
          testTubeRest();
        }
      }
    }
  }

  module baseWallProfile() {
    difference() {
      offset(r = Material_Thickness) {
        baseProfile();
      }

      baseProfile();
    }
  }

  module baseProfile() {
    translate([Material_Thickness, Material_Thickness, 0]) {
      square([Test_Tube_Length + (Test_Tube_Gap * 2), Test_Tubes * (Test_Tube_Diameter + (Material_Thickness * 2))]);
    }
  }

  module testTubeRest() {
    gap = (Test_Tube_Length / Rests);

    for (i=[0:Rests - 1]) {
      translate([(gap / 2) + (i * gap), 0, 0]) {
        rotate([90, 0, 90]) {
          linear_extrude(height = Material_Thickness, center = true, convexity = 10) {
            restProfile();
          }
        }
      }
    }
  }

  module restProfile() {
    square([Test_Tube_Diameter + (Material_Thickness * 2), Material_Thickness / 2]);

    square([Material_Thickness / 2, (Test_Tube_Diameter / 2) + (Material_Thickness)]);

    translate([Test_Tube_Diameter + (Material_Thickness * 2) - (Material_Thickness / 2), 0, 0]) {
      square([Material_Thickness / 2, (Test_Tube_Diameter / 2) + (Material_Thickness)]);
    }

    offset(r = Material_Thickness / 2) {
      offset(r = -Material_Thickness / 2) {
        difference() {
          square([Test_Tube_Diameter + (Material_Thickness * 2), (Test_Tube_Diameter / 2) + (Material_Thickness)]);

          translate([(Test_Tube_Diameter / 2) + Material_Thickness, (Test_Tube_Diameter / 2) + (Material_Thickness), 0]) {
            circle(d = Test_Tube_Diameter);
          }
        }
      }
    }
  }

  module testTube() {
    translate([0, 0, Test_Tube_Diameter / 2]) {
      difference() {
        union() {
          difference() {
            sphere(d = Test_Tube_Diameter);
            cylinder(d = Test_Tube_Diameter, h = (Test_Tube_Diameter / 2));
          }
          cylinder(d = Test_Tube_Diameter, h = Test_Tube_Length - (Test_Tube_Diameter / 2));
        }

        sphere(d = Test_Tube_Diameter - 1);
        cylinder(d = Test_Tube_Diameter - 1, h = Test_Tube_Length - (Test_Tube_Diameter / 2) + 1);
      }
    }
  }

  module testTubes() {
    if (Show_Test_Tubes) {
      for (i=[0:Test_Tubes - 1]) {
        translate([0, i * (Test_Tube_Diameter + (Material_Thickness * 2)), 0]) {
          translate([Material_Thickness + Test_Tube_Gap, (Material_Thickness * 2) + (Test_Tube_Diameter / 2), (Material_Thickness * 2) + (Test_Tube_Diameter / 2)]) {
            rotate([0, 90, 0]) {
              %testTube();
            }
          }
        }
      }
    }
  }

  if (Part == 0) {
    base();
    testTubes();
  } else if (Part == 1) {
    translate([Material_Thickness + Lid_Gap, Material_Thickness + Lid_Gap, 0]) {
      lid();
    }
  } else if (Part == 2 && $preview) {
    base();
    testTubes();
    translate([0, 0, (Lift_Lid_Percent / 100) * ((Material_Thickness * 3) + Test_Tube_Diameter)]) {
      translate([0, 0, ((Material_Thickness * 4) + Test_Tube_Diameter)]) {
        mirror([0, 0, 1]) {
          lid();
        }
      }
    }
  }
}

testTubeHolderBox();

echo("\r\tThis design is completely free and shared under a permissive license.\r\tYour support is hugely appreciated: codeandmake.com/support\r");

```


[User]
Hi, I'm looking for an OpenSCAD design for a customizable bin. I want it to be parametric, meaning I can easily change the dimensions.

Here's what I need:

*   **Basic Shape:** A rectangular bin.
*   **Dimensions:** I need to be able to specify the width, depth, and height of the bin. I also need to be able to specify whether I'm providing the *outside* dimensions or the *inside* dimensions, and have the script calculate the other.
*   **Wall & Floor Thickness:** I need to be able to adjust the thickness of both the walls and the floor.
*   **Corner Options:**  I'd like three options for the corners: square, rounded, or chamfered. If rounded, I need to control the radius; if chamfered, I need to control the inset amount.
*   **Side Holes:** I need the ability to add holes to the sides. There should be options for no hole, a round hole, or a diamond-shaped hole. I need to be able to set the size (diameter for a round hole, edge length for the diamond), and choose whether the hole is centered on the side or placed a specified distance from the top.
*   **Floor Options:** I need three options for the floor: a full floor, a floor with a hole in the middle, or no floor. If there's a hole, I need to be able to specify how much it is inset from the sides (both width and depth).
*   **Libraries:** I expect that you'll need to use some OpenSCAD libraries; I know that for the rounding and chamfering we'll want `BOSL2/std.scad`, for the cuboid command.

I think that covers it. Let me know if you have any questions, or if that's not clear.

[Assistant]
Customizer_for_Bin_v0.18.scad:
```
/*	Title:	Customizer for Bin
*
*	Version:	v0.18
*
*	Author:	Cygnyet
*
*	Date:		2024-09-01
*
*	GitHub: 	https://github.com/cygnyet/_________
*
*	Thingiverse:	https://www.thingiverse.com/thing:5435387
*
*	Keywords:	part bin trashcan pencil holder
*
*/
;

/*	VERSION HISTORY
		- v0.12	Initial Version
		- v0.13	Added round corner using roundedcube(), in include <roundedcube.scad>
		- v0.14	Added vertical hole offset
		- v0.16	Added chamfer corner, 01_Select_Specified_Bin_Dimensions_AS logic,
					Increased parameters to 2 decimal places
		- v0.17	Added "No Floor" and "Floor Hole" capabilities.
					Reorganized the customizer parameters and refactored code including using functions for
					a more flexible design.
					Added covers used in "https://www.thingiverse.com/thing:6690247" (Travel Boggle-Like Letter-Cube Word Game):
						01 TB Out-Set Extension 	=> 03b_Travel_Boggle-Like_Letter-Cube_Word_Game___Shaking_Cover_Top.stl
						02 TB In-Set Cover 			=> 03a_Travel_Boggle-Like_Letter-Cube_Word_Game___Shaking_Cover_Bottom.stl
						03 TB Out-Set Cover 			=> 04_Travel_Boggle-Like_Letter-Cube_Word_Game___Playing_Cover.stl
		- v0.18	Fixed defects in v0.17 when using Corner Type = Round Corner and Floor Type = No Floor or Floor With Hole
							required changing from roundedcube(), in include <roundedcube.scad>, to cuboid, in include <BOSL2/std.scad>
*/
;

/* TODO
		- ________
*/
;

	//CUSTOMIZER VARIABLES

/* [Select if Specified Bin Dimensions are "Outside Dimensions" or "Inside Dimensions"] */
	01_Select_Specified_Bin_Dimensions_AS = "Outside Dimensions";	// [Outside Dimensions, Inside Dimensions]

/* [Specified Bin Dimensions (mm)] */

		//Left to Right (X axis)	original default = 160
	02_Specified_Bin_Width		= 100;	//.01

		//Front to Back (Y axis)	orignal default = 145
	03_Specified_Bin_Depth		= 100;	//.01

   	//Bottom to Top (Z axis)	original default = 175
	04_Specified_Bin_Height		= 100;	//.01

	Specified_Bin_Dimensions = [02_Specified_Bin_Width, 03_Specified_Bin_Depth, 04_Specified_Bin_Height];

/* [Wall and Floor Thickness (mm)] */
		//Sides
	05_Wall_Thickness=3;		//.01
		//Bottom
	06_Floor_Thickness=3;	//.01

/* [Corner Options (mm)] */
	07_Corner_Type = "Square Corner";		// [Square Corner, Round Corner, Chamfer Corner]

	08_Round_Corner_Radius = 20;	//.01

	09_Chamfer_Inset = 5;	//.01

/* [Side Options (mm)] */

	10_Side_Hole_Type = "No Side Hole";		// [Diamond Side Hole, Round Side Hole, No Side Hole]

		//Size = diameter of Round Side Hole or length of one Diamond Side Hole edge
	11_Side_Hole_Size = 4.50;	//.01

	12_Center_The_Side_Hole = true;

//	Applies only if "Center The Side Hole" is false (unchecked)
	13_Side_Hole_Distance_From_Top_IF_NOT_Centered = 30;			//.01

/* [Floor Options (mm)] */
	14_Floor_Type = "Full Floor";			// [Full Floor, Floor With Hole, No Floor]

//	Applies only if Floor Type = "Floor With Hole"
	15_Floor_Hole_Width_Inset = 0;		//.01
//	Applies only if Floor Type = "Floor With Hole"
	16_Floor_Hole_Depth_Inset = 0;		//.01

/* [Hidden] */
$fn = 40;		//default $fn = 40 but can set to $fn = 10 for quicker rendering during prototyping

Xw = 0;	//Vector index for X axis (width)
Yd = 1;	//Vector index for Y axis (depth)
Zh = 2;	//Vector index for Z axis (height)

eps = 0.02;			//small epsilon to ensure extension of the InnerCube along the Z axis
Hole_Depth = 05_Wall_Thickness;

	//SCAD CODE

	//Includes (for a list of OpenSCAD libraries, see https://openscad.org/libraries.html)
include <roundedcube.scad>						//See https://danielupshaw.com/openscad-rounded-corners	Note:  No longer used
include <smooth-prim/smooth_prim.scad>		//See https://github.com/rcolyer/smooth-prim					Note:  Note currently used
include <BOSL2/std.scad>						//See https://github.com/BelfrySCAD/BOSL2

{	//Functions (return values)

function calculateOutsideDimensions
	(Specified_Bin_Dimensions = [
												02_Specified_Bin_Width,
												03_Specified_Bin_Depth,
												04_Specified_Bin_Height
										]) =
		(01_Select_Specified_Bin_Dimensions_AS == "Outside Dimensions")
	? 	Specified_Bin_Dimensions		//Specified_Bin_Dimensions are the Outside Dimensions
		//Calculate Inside Dimensions
	:	[
			02_Specified_Bin_Width+(2*05_Wall_Thickness),
			03_Specified_Bin_Depth+(2*05_Wall_Thickness),
			04_Specified_Bin_Height+(1*06_Floor_Thickness)
		];

Outside_Bin_Dimensions = calculateOutsideDimensions();

function calculateInsideDimensions
	(Specified_Bin_Dimensions = 	[
												02_Specified_Bin_Width,
												03_Specified_Bin_Depth,
												04_Specified_Bin_Height
											]) =
		(01_Select_Specified_Bin_Dimensions_AS == "Outside Dimensions")
		//Calculate Outside Dimensions
	? 	[
			02_Specified_Bin_Width-(2*05_Wall_Thickness),
			03_Specified_Bin_Depth-(2*05_Wall_Thickness),
			04_Specified_Bin_Height-(1*06_Floor_Thickness)
		]
	: 	Specified_Bin_Dimensions;	//Specified_Bin_Dimensions are the Inside Dimensions

Inside_Bin_Dimensions = calculateInsideDimensions();

//Calculate how much to extend InnerCube along negative Z axis to achieve a No Floor bin
function calculateNoFloorDimension
	() =
		(14_Floor_Type == "No Floor")
	? 	06_Floor_Thickness + eps
	: 	0;

InnerCube_Extension = calculateNoFloorDimension();

//Calculate dimensions of the floor hole, give the floor hole inset

function calculateFloorHoleWidth
	() =
		(14_Floor_Type == "Floor With Hole")
	? 	Inside_Bin_Dimensions[Xw] - (2 * 15_Floor_Hole_Width_Inset)
	: 	0;

Floor_Hole_Width = calculateFloorHoleWidth();

function calculateFloorHoleDepth
	() =
		(14_Floor_Type == "Floor With Hole")
	? 	Inside_Bin_Dimensions[Yd] - (2 * 16_Floor_Hole_Depth_Inset)
	: 	0;

Floor_Hole_Depth = calculateFloorHoleDepth();

}
;

{	//Other Initializations	(used by the echoDim module to display individual bin dimensions)

OutsideWidth = Outside_Bin_Dimensions[Xw];
OutsideDepth = Outside_Bin_Dimensions[Yd];
OutsideHeight = Outside_Bin_Dimensions[Zh];

InsideWidth = Inside_Bin_Dimensions[Xw];
InsideDepth = Inside_Bin_Dimensions[Yd];
InsideHeight = Inside_Bin_Dimensions[Zh];

}
;

	//Echo modules

module echoDim() {
echo ("------------------------------------------------------------------");
echo (01_Select_Specified_Bin_Dimensions_AS = 01_Select_Specified_Bin_Dimensions_AS);
echo (Specified_Bin_Dimensions = Specified_Bin_Dimensions);
echo ("------------------------------------------------------------------");
echo (Outside_Bin_Dimensions = Outside_Bin_Dimensions);
echo (OutsideWidth = OutsideWidth);
echo (OutsideDepth = OutsideDepth);
echo (OutsideHeight = OutsideHeight);
echo ("------------------------------------------------------------------");
echo (Inside_Bin_Dimensions = Inside_Bin_Dimensions);
echo (InsideWidth = InsideWidth);
echo (InsideDepth = InsideDepth);
echo (InsideHeight = InsideHeight);
echo ("------------------------------------------------------------------");
echo (InnerCube_Extension = InnerCube_Extension);
echo ("------------------------------------------------------------------");
echo (Floor_Hole_Width=Floor_Hole_Width);
echo (Floor_Hole_Depth=Floor_Hole_Depth);
echo ("------------------------------------------------------------------");
}
;
echoDim();

	//Utility modules

module CutCubeCustom(size=[160,145,175], insetby=09_Chamfer_Inset) {
  size = is_num(size) ? [size, size, size] : size;

						//  X,			Y,				Z
  CutCorner(insetby, [0,			0,				0			])	//Front Left Bottom	= origin
//CutCorner(insetby, [0,			0,				size[Zh]	])	//Front Left Top		= origin + Z			//NOT USED
  CutCorner(insetby, [0,			size[Yd],	0			])	//Back Left Bottom	= orIgin + Y
//CutCorner(insetby, [0,			size[Yd],	size[Zh]	])	//Back Left Top		= origin + (Y,Z)		//NOT USED
  CutCorner(insetby, [size[Xw],	0,				0			])	//Front Right Bottom	= origin + (X)
//CutCorner(insetby, [size[Xw],	0,				size[Zh]	])	//Front Right Top		= origin + (X, Z)		//NOT USED
  CutCorner(insetby, [size[Xw],	size[Yd],	0			])	//Back Right Bottom	= origin + (X, Y)
//CutCorner(insetby, [size[Xw],	size[Yd],	size[Zh]	])	//Back Right Top		= origin + (X, Y, Z)	//NOT USED
  cube(size);
}
;

	//Object modules

module OuterCube(){		//Using vector Outside_Bin_Dimensions

	color("yellow")
	if (07_Corner_Type == "Round Corner")
	{
		cuboid(
			Outside_Bin_Dimensions,
			anchor = FRONT+LEFT+BOTTOM,
			rounding = 08_Round_Corner_Radius,
			edges = "Z"
		);
	}
	else if (07_Corner_Type == "Square Corner")
	{
		cube(
			Outside_Bin_Dimensions,
			center = false
		);
	}
	else if (07_Corner_Type == "Chamfer Corner")
	{
		CutCubeCustom(
			Outside_Bin_Dimensions
		);
	}
}
;

module InnerCube(){		//Using vector Inside_Bin_Dimensions

	translate([05_Wall_Thickness,05_Wall_Thickness,06_Floor_Thickness - InnerCube_Extension ])
		color("red")
	if (07_Corner_Type == "Round Corner")
	{
		cuboid(
			[
				Inside_Bin_Dimensions[Xw],
				Inside_Bin_Dimensions[Yd],
				Inside_Bin_Dimensions[Zh] + eps + InnerCube_Extension
			],
			anchor = FRONT+LEFT+BOTTOM,
			rounding = 08_Round_Corner_Radius,
			edges = "Z"
		);
	}
	else if (07_Corner_Type == "Square Corner")
	{
		cube(
			[
				Inside_Bin_Dimensions[Xw],
				Inside_Bin_Dimensions[Yd],
				Inside_Bin_Dimensions[Zh] + eps + InnerCube_Extension
			],
			center = false
		);
	}
	else if (07_Corner_Type == "Chamfer Corner")
	{
		CutCubeCustom(
			[
				Inside_Bin_Dimensions[Xw],
				Inside_Bin_Dimensions[Yd],
				Inside_Bin_Dimensions[Zh] + eps + InnerCube_Extension
			]
		);
	}
}
;

module FloorHoleCube(){		/*	Creates the cube that generates the hole in the floor
										with the "Floor With Hole" Floor Type */

	translate(
		[05_Wall_Thickness+15_Floor_Hole_Width_Inset,
		05_Wall_Thickness+16_Floor_Hole_Depth_Inset,
		- eps])
		color("purple")

		cube(
			[Floor_Hole_Width, Floor_Hole_Depth, Inside_Bin_Dimensions[Zh] - eps - 06_Floor_Thickness],
			center = false
		);
}
;

module RoundHoleCylinder(){	//On -y face (from origin)
	HoleZDistanceFromOrigin = (OutsideHeight/2)-13_Side_Hole_Distance_From_Top_IF_NOT_Centered;
	if (12_Center_The_Side_Hole)
	{
	translate([OutsideWidth/2,-((InsideDepth/2)+05_Wall_Thickness/2)+OutsideDepth/2,OutsideHeight/2])
		rotate([90,0,0])
			color("green")
			cylinder(h=Hole_Depth,
						d=11_Side_Hole_Size,
						center=true);
	}
	else
	{
	translate([OutsideWidth/2,-((InsideDepth/2)+05_Wall_Thickness/2)+OutsideDepth/2,HoleZDistanceFromOrigin])
		rotate([90,0,0])
			color("green")
			cylinder(h=Hole_Depth,
						d=11_Side_Hole_Size,
						center=true);
	}
}
;

module DiamondHoleCuboid(){	//On -y face (from origin)
	HoleZDistanceFromOrigin = (OutsideHeight/2)-13_Side_Hole_Distance_From_Top_IF_NOT_Centered;
	if (12_Center_The_Side_Hole)
	{
	translate([OutsideWidth/2,-((InsideDepth/2)+05_Wall_Thickness/2)+OutsideDepth/2,OutsideHeight/2])
		rotate([0,45,0])
		rotate([90,0,0])
			color("white")
			cube([11_Side_Hole_Size,
					11_Side_Hole_Size,
					Hole_Depth],
					center=true);
	}
	else
	{
	translate([OutsideWidth/2,-((InsideDepth/2)+05_Wall_Thickness/2)+OutsideDepth/2,HoleZDistanceFromOrigin])
		rotate([0,45,0])
		rotate([90,0,0])
			color("white")
			cube([11_Side_Hole_Size,
					11_Side_Hole_Size,
					Hole_Depth],
					center=true);
	}
}
;

	//Transformations
difference(){
	OuterCube();
	InnerCube();

	if (14_Floor_Type == "Floor With Hole") {FloorHoleCube();}

	if (10_Side_Hole_Type == "Diamond Side Hole") {DiamondHoleCuboid();}
	else if(10_Side_Hole_Type == "Round Side Hole") {RoundHoleCylinder();}
	else {}
}

```


[User]
I'd like to create a modular desk drawer system using OpenSCAD, and I need some help designing the files. I'm envisioning a set of drawers that fit into a casing, with both the drawers and casing being parameterized. I'd like to be able to adjust the inner dimensions of the drawers, and have the casing automatically adjust to fit them. I’m thinking that I can print the casing and drawers separately, and then assemble them.

Here’s how I want it to work:

**Drawers:**

*   I need to define the *inner* width, height, and depth of the drawers.
*   The drawer walls should have a defined thickness.
*   There should be a front panel that extends the height of the drawer wall.
*   There should be a simple, angled handle cut out of the front face on each side of the drawer. The handle should be created using a `difference()`. I’d like to avoid having to make this cut out in another file, or make it a separate function.
*   I want the drawer module to be printable, and I should be able to print multiple copies of the drawer, since they will all be the same.

**Casing:**

*   The casing should accommodate a specified number of drawers.
*   The casing should have a defined wall thickness.
*   The casing should have a bottom and a top, with side walls and a back stabilizer, but should be open in the back between the stabilizers to save on print material.
*   The casing’s dimensions should be calculated based on the drawer dimensions, the casing wall thickness, and a printing tolerance value to give some clearance so that the drawers slide in smoothly.

**Parameters:**

I want to have the following parameters that I can easily change:

*   `dw`: Drawer inner width
*   `dh`: Drawer inner height
*   `dd`: Drawer inner depth
*   `dt`: Drawer wall thickness
*   `cd`: Number of drawers in the casing
*   `ct`: Casing wall thickness
*   `pt`: Printing tolerance

**Output**

I would like two separate files to print the casing and the drawer. Ideally, there should be a `main` module for each file, but it's not absolutely necessary.

Let me know if you need any other details, and thanks in advance for your help!

[Assistant]
desk_drawers.scad:
```
/* [drawer] */
// inner width
dw = 170;
// inner height
dh = 25;
// inner depth
dd = 90;
// walls thickness
dt = 2;

/* [ casing ] */
// Amount of drawers
cd = 4;
// walls tickness
ct = 3.8;

/* [ print settings ] */
// printing tolerance
pt = 0.5;
// What to print
to_print = "casing"; // [casing, drawer]

casing_height = dh+dt+ct+pt;
casing_width = dw+ct*3+pt*2;
casing_depth = dd+ct+pt;

if (to_print == "drawer") {
  drawer();
}

if (to_print == "casing") {
  for (i=[1:1:cd]) {
    translate([0,0,ct+casing_height*(i-1)]) {
      translate([ct+pt,0,0]) %drawer();
      casing();
    }
  }
  cube([casing_width,casing_depth+ct,ct]);
  translate([0,0,casing_height*cd+ct])cube([casing_width,casing_depth+ct,ct]);
}

module casing () {
  // left side
  union() {
    cube([ct, casing_depth, casing_height]);
    translate([ct,dt+pt,dh+dt+pt*2]) cube([ct, dd+ct-dt, ct-pt]);
    translate([0,casing_depth,0]) cube([ct*2-pt, ct, casing_height]);
  }

  // right side
  translate([dw+ct*2+pt*2,0,0]) {
    cube([ct, casing_depth, casing_height]);
    translate([-ct,dt+pt,dh+dt+pt*2]) cube([ct, dd+ct-dt, ct-pt]);
    translate([-ct+pt,casing_depth,0]) cube([ct*2-pt, ct, casing_height]);
  }

  // back stabilizer
  translate([ct*2-pt,casing_depth,dh-dt-pt-(casing_height-ct)/2])
    cube([dw-ct+pt*4,ct,ct*2]);
}

module drawer () {
  w = dw+dt*2;
  h = dh+dt;
  d = dd+dt*2;

  hw = w/32;
  hh = (h+ct)/2;

  difference() {
    union() {
      cube([w, d, h]); // outer perimeter
      cube([w, dt, h+ct]); // front panel
      d();
    }
    translate([dt,dt,dt]) cube([dw,dd,dh]);
  }

  module d () {
      translate([(w-hw)/2,0,h/6]) {
        difference() {
          rotate([45,0,0]) cube([hw,hh,hh]);
          translate([0,-hh-hh/2,0]) cube([hw,hh,hh]);
        }
      }
  }
}
```


[User]
I'd like to request a parametric design for stackable storage caddies, kind of like the ones on codeandmake.com. I'm looking for something that's fully customizable, including the internal dimensions, wall thickness, corner radius, and patterns on the sides and base. I'd also like to be able to add a lid with a lip and recess to enable stacking.

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

*   **General Dimensions:**
    *   Inside Length, Width, and Height, as well as the inner corner radius.
    *   Overall material thickness for the base and walls.
*   **Front Options:**
    *   A solid front, a rectangular cutout of variable width and height (as percentages), or a pattern on the front face.
*   **Bottom, Sides and Rear Options:**
    *   Solid, or a pattern (holes, lines, triangles, diamonds, circles, hexagons, stars, or hearts).
*   **Lip:**
    *   Option to add a lip and a corresponding recess on the lid for stacking. I'd need to control the gap between lip and recess.
*   **Pattern:**
    *   Type of pattern (as listed above).
    *   Padding around the pattern elements.
    *   Width of the individual pattern elements.
    *   Minimum material thickness to use for the pattern.

I'd also like the design to be optimized for 3D printing without supports where possible.

It would also be really useful if, as part of the output, I could get dimensions displayed in the console similar to codeandmake.com. Things like the outside length, width, height, and then specifically the lip and recess dimensions.

Finally, if possible, could you include an option to preview the parts split apart for visual inspection prior to export, similar to how the codeandmake example allows?

I don't have any specific library preferences; just use whatever you think is most appropriate. Thanks!

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

/*
 * Stackable Storage Caddies by Code and Make (https://codeandmake.com/)
 *
 * https://codeandmake.com/post/stackable-storage-caddies
 *
 * Stackable Storage Caddies v1.4 (5 August 2022)
 */

/* [General] */

// Part to display
Part = 1; // [1: Container, 2: Lid, 3: Container Corner Section - For Testing, 4: Lid Corner Section - For Testing, 5: All - Preview Only]

// Inner length
Inside_Length = 150; // [10:1:300]

// Inner width
Inside_Width = 120; // [10:1:300]

// Inner height
Inside_Height = 60; // [10:1:300]

// Inner corner radius
Inside_Corner_Radius = 10; // [0:1:20]

// Front type
Front = 1; // [0:Solid, 1: Cutout, 2: Pattern]

// Bottom type
Bottom = 1; // [0:Solid, 1: Pattern]

// Side type
Sides = 1; // [0:Solid, 1: Pattern]

// Rear type
Rear = 1; // [0:Solid, 1: Pattern]

// Thickness of the walls and base
Material_Thickness = 4; // [2.5:0.1:10]

/* [Cutout] */

// Width of front cutout as a percentage
Front_Cutout_Width_Percent = 90; // [0:1:100]

// Width of front cutout as a percentage
Front_Cutout_Height_Percent = 90; // [0:1:100]

/* [Lip] */

// Should a lip and lip recess be added
Lip = 1; // [0: No, 1: Yes]

// Gap between the lip and the recess
Lip_Gap = 0.6; // [0.1:0.1:1]

/* [Pattern] */

// Type of pattern
Pattern_Type = 6; // [0: None, 1: Hole, 2: Lines, 3: Triangles, 4: Diamonds, 5: Circles, 6: Hexagons, 7: Stars, 8: Hearts]

// Amount of padding around the pattern
Pattern_Padding = 0; // [0:1:100]

// The width of the pattern elements
Pattern_Width = 5; // [1:1:100]

// Minimum pattern material thickness
Pattern_Material_Thickness = 2; // [2:0.1:10]

/* [Preview] */

// Amount to split the parts (preview only)
Split_Parts_In_Preview = 10; // [0:1:100]

module caddy() {
  $fn = 120;

  insideCornerRadius = min(Inside_Corner_Radius, (Inside_Length / 2) - 0.01, (Inside_Width / 2) - 0.01, (Inside_Height / 2) - 0.01);
  lipHeight = (Material_Thickness / 2);
  lipRecessHeight = min(lipHeight + 1, Material_Thickness - 1);
  lipThickness = (Material_Thickness / 2);

  module insideProfile() {
    translate([Material_Thickness, Material_Thickness, 0]) {
      offset(r = insideCornerRadius) {
        offset(delta = -insideCornerRadius) {
          square(size=[Inside_Width, Inside_Length]);
        }
      }
    }
  }

  module outsideProfile() {
    offset(r = Material_Thickness) {
      insideProfile();
    }
  }

  module wallProfile() {
    difference() {
      outsideProfile();
      insideProfile();
    }
  }

  module lipProfile(outsidePad, insidePad) {
    difference() {
      offset(r = lipThickness + (outsidePad != undef ? outsidePad : 0)) {
        insideProfile();
      }

      offset(r = -(insidePad != undef ? insidePad : 0)) {
        insideProfile();
      }
    }
  }

  module linesPatternProfile(width, length, lineWidth, materialThickness) {
    longestSide = max(width, length);
    hypotenuse = sqrt(pow(longestSide, 2) + pow(longestSide, 2));

    module lines(start) {
      for (i=[start:ceil(longestSide / (lineWidth + materialThickness))]) {
        translate([(i * (lineWidth + materialThickness)), 0, 0]) {
          square(size=[lineWidth, hypotenuse], center = true);
        }
      }
    }

    translate([width / 2, length / 2, 0]) {
      rotate([0, 0, -45]) {
        lines(0);

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

  module triangle(altitude) {
    radius = altitude * 2 / 3;
    translate([0, (radius / 2) - (altitude / 2), 0]) {
      rotate([0, 0, 90]) {
        circle(r = radius, $fn = 3);
      }
    }
  }

  module trianglePatternProfile(width, length, altitude, materialThickness) {
    edge = (2 / sqrt(3)) * altitude;
    xBaseOffset = ((edge / 2) + materialThickness) * 2;
    yBaseOffset = altitude + materialThickness;
    xIterations = ceil(width / xBaseOffset);
    yIterations = ceil(length / yBaseOffset);
    totalWidth = xBaseOffset * xIterations;
    totalLength = yBaseOffset * yIterations;

    translate([-((totalWidth - width) / 2), -((totalLength - length) / 2), 0]) {
      for (x=[0:xIterations]) {
        for (y=[0:yIterations]) {
          translate([xBaseOffset * x, yBaseOffset * y, 0]) {
            mirror([0, (y % 2 == 0 ? 0 : 1), 0]) {
              triangle(altitude);
            }

            translate([(edge / 2) + materialThickness, 0, 0]) {
              mirror([0, (y % 2 == 0 ? 1 : 0), 0]) {
                triangle(altitude);
              }
            }
          }
        }
      }
    }
  }

  module diamondPatternProfile(width, length, diamondWidth, materialThickness) {
    intersection() {
      linesPatternProfile(width, length, diamondWidth, materialThickness);

      translate([width, 0, 0]) {
        mirror([1, 0, 0]) {
          linesPatternProfile(width, length, diamondWidth, materialThickness);
        }
      }
    }
  }

  module circlePatternProfile(width, length, diameter, materialThickness) {
    baseOffset = (diameter + materialThickness);
    xIterations = ceil(width / (diameter + materialThickness));
    yIterations = ceil(length / (diameter + materialThickness));
    totalWidth = baseOffset * xIterations;
    totalLength = baseOffset * yIterations;
    // base facets on circumference, clamp between 20 and 60
    fn = ceil(min(max(diameter * PI, 20), 60));

    translate([-((totalWidth - width) / 2), -((totalLength - length) / 2), 0]) {
      for (x=[0:xIterations]) {
        for (y=[0:yIterations]) {
          translate([x * (diameter + materialThickness), y * (diameter + materialThickness), 0]) {
            circle(d = diameter, $fn = fn);
          }
        }
      }
    }
  }

  module hexPatternProfile(width, length, diameter, materialThickness) {
    radius = diameter / 2;
    apothem = radius * cos(180 / 6);

    xBaseOffset = ((radius * 1.5) + (materialThickness)) * 2;
    yBaseOffset = (apothem * 2) + materialThickness;
    xIterations = ceil(width / xBaseOffset);
    yIterations = ceil(length / yBaseOffset);
    totalWidth = xBaseOffset * xIterations;
    totalLength = yBaseOffset * yIterations;

    translate([-((totalWidth - width) / 2), -((totalLength - length) / 2), 0]) {
      for (x=[0:xIterations]) {
        for (y=[0:yIterations]) {
          translate([xBaseOffset * x, yBaseOffset * y, 0]) {
            circle(r = radius, $fn = 6);

            translate([(radius * 1.5) + materialThickness, apothem + (materialThickness / 2), 0]) {
              circle(r = radius, $fn = 6);
            }
          }
        }
      }
    }
  }

  module starPatternProfile(width, length, diameter, materialThickness) {
    baseOffset = (diameter + materialThickness);
    xIterations = ceil(width / (diameter + materialThickness));
    yIterations = ceil(length / (diameter + materialThickness));
    totalWidth = baseOffset * xIterations;
    totalLength = baseOffset * yIterations;
    altitude = (diameter / 4) + (diameter / 2);

    translate([-((totalWidth - width) / 2), -((totalLength - length) / 2), 0]) {
      for (x=[0:xIterations]) {
        for (y=[0:yIterations]) {
          translate([x * (diameter + materialThickness), y * (diameter + materialThickness), 0]) {
            translate([0, (diameter / 2) - (altitude / 2), 0]) {
              triangle(altitude);
            }

            mirror([0, 1, 0]) {
              translate([0, (diameter / 2) - (altitude / 2), 0]) {
                triangle(altitude);
              }
            }
          }
        }
      }
    }
  }

  module heart(edge, altitude) {
    topDiameter = ((edge / 2) + altitude - (sqrt(pow(edge / 2, 2) + pow(altitude, 2))));
    bottomDiameter = topDiameter * 2 / 3;
    topTranslateX = ((sqrt(3) / 2) * topDiameter) - edge / 2;
    topTranslateY = (topDiameter / 2) - (altitude / 2);
    bottomTranslateY = altitude / 2;

    // base facets on altitude, clamp between 20 and 60
    fn = ceil(min(max(altitude * PI, 20), 60));

    hull() {
      translate([-topTranslateX, topTranslateY, 0]) {
        circle(d = topDiameter, $fn = fn);
      }

      translate([0, bottomTranslateY - bottomDiameter, 0]) {
        circle(d = bottomDiameter, $fn = fn);
      }
    }

    hull() {
      translate([topTranslateX, topTranslateY, 0]) {
        circle(d = topDiameter, $fn = fn);
      }

      translate([0, bottomTranslateY - bottomDiameter, 0]) {
        circle(d = bottomDiameter, $fn = fn);
      }
    }
  }

  module heartPatternProfile(width, length, altitude, materialThickness) {
    edge = (2 / sqrt(3)) * altitude;
    xBaseOffset = ((edge / 2) + materialThickness) * 2;
    yBaseOffset = altitude + materialThickness;
    xIterations = ceil(width / xBaseOffset);
    yIterations = ceil(length / yBaseOffset);
    totalWidth = xBaseOffset * xIterations;
    totalLength = yBaseOffset * yIterations;

    translate([-((totalWidth - width) / 2), -((totalLength - length) / 2), 0]) {
      for (x=[0:xIterations]) {
        for (y=[0:yIterations]) {
          translate([xBaseOffset * x, yBaseOffset * y, 0]) {
            mirror([0, (y % 2 == 0 ? 0 : 1), 0]) {
              heart(edge, altitude);
            }

            translate([(edge / 2) + materialThickness, 0, 0]) {
              mirror([0, (y % 2 == 0 ? 1 : 0), 0]) {
                heart(edge, altitude);
              }
            }
          }
        }
      }
    }
  }

  module patternMask(width, length) {
    if (Pattern_Type == 1) {
      // hole
      square(size=[width, length]);
    }
    else if (Pattern_Type == 2) {
      // lines
      linesPatternProfile(width, length, Pattern_Width, Pattern_Material_Thickness);
    }
    else if (Pattern_Type == 3) {
      // triangles
      trianglePatternProfile(width, length, Pattern_Width, Pattern_Material_Thickness);
    }
    else if (Pattern_Type == 4) {
      // diamonds
      diamondPatternProfile(width, length, Pattern_Width, Pattern_Material_Thickness);
    }
    else if (Pattern_Type == 5) {
      // circles
      circlePatternProfile(width, length, Pattern_Width, Pattern_Material_Thickness);
    }
    else if (Pattern_Type == 6) {
      // hexagons
      hexPatternProfile(width, length, Pattern_Width, Pattern_Material_Thickness);
    }
    else if (Pattern_Type == 7) {
      // stars
      starPatternProfile(width, length, Pattern_Width, Pattern_Material_Thickness);
    }
    else if (Pattern_Type == 8) {
      // hearts
      heartPatternProfile(width, length, Pattern_Width, Pattern_Material_Thickness);
    }
  }

  module bottom() {
    linear_extrude(height = Material_Thickness, convexity = 10) {
      outsideProfile();
    }
  }

  module containerBody() {
    bottom();

    // walls
    linear_extrude(height = Material_Thickness + Inside_Height, convexity = 10) {
      wallProfile();
    }

    // lip
    if (Lip) {
      linear_extrude(height = Material_Thickness + Inside_Height + lipHeight, convexity = 10) {
        lipProfile();
      }
    }
  }

  module frontCutout() {
    width = (Inside_Width - (2 * insideCornerRadius)) * (Front_Cutout_Width_Percent / 100);
    height = (Inside_Height + lipHeight) * (Front_Cutout_Height_Percent / 100);
    roundingRadius = min(width / 2, height / 2, insideCornerRadius) - 0.00001;
    depth = Material_Thickness + 1;
    lipRoundingRadius = min(lipHeight, (Inside_Width - width) / 2, height - roundingRadius);

    translate([Material_Thickness + (Inside_Width / 2), depth, Material_Thickness + Inside_Height + lipHeight - height]) {
      rotate([90, 0, 0]) {
        linear_extrude(height = depth + 1, convexity = 10) {
          translate([-width / 2, 0, 0]) {
            offset(r = roundingRadius) {
              offset(r = -roundingRadius) {
                square(size=[width, height + roundingRadius + 1]);
              }
            }

            translate([-lipRoundingRadius, height - lipRoundingRadius, 0]) {
              difference() {
                square(size=[lipRoundingRadius, lipRoundingRadius + 1]);
                circle(r=lipRoundingRadius);
              }
            }

            translate([width + lipRoundingRadius, height - lipRoundingRadius, 0]) {
              mirror([1, 0, 0]) {
                difference() {
                  square(size=[lipRoundingRadius, lipRoundingRadius + 1]);
                  circle(r=lipRoundingRadius);
                }
              }
            }
          }
        }
      }
    }
  }

  module body(part) {
    difference() {
      if (part == 1) {
        containerBody();
      } else if (part == 2) {
        bottom();
      }

      // lip recess (underneath)
      if (Lip) {
        translate([0, 0, -0.01]) {
          linear_extrude(height = lipRecessHeight + 0.01, convexity = 10) {
            lipProfile(Lip_Gap / 2, Lip_Gap / 2);
          }
        }
      }

      // bottom pattern
      if (Bottom == 1) {
        translate([0, 0, -0.5]) {
          linear_extrude(height = Material_Thickness + 1, convexity = 10) {
            intersection() {
              translate([Material_Thickness, Material_Thickness, 0]) {
                patternMask(Inside_Width, Inside_Length);
              }

              offset(r = -Material_Thickness - Pattern_Padding) {
                insideProfile();
              }
            }
          }
        }
      }

      if (part != 2) {
        if (Front == 1) {
          frontCutout();
        } else if (Front == 2) {
          translate([Material_Thickness, Material_Thickness + 0.5, Material_Thickness]) {
            rotate([90, 0, 0]) {
              linear_extrude(height = Material_Thickness + 1, convexity = 10) {
                intersection() {
                  patternMask(Inside_Width, Inside_Height);
                  patternWidth = Inside_Width - (insideCornerRadius * 2);
                  patternHeight = Inside_Height;

                  translate([insideCornerRadius, 0, 0]) {
                    offset(r = -Material_Thickness - Pattern_Padding) {
                      offset(r = insideCornerRadius) {
                        offset(delta = -insideCornerRadius) {
                        square([patternWidth, patternHeight]);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        // side pattern
        if (Sides == 1) {
          translate([-0.5, Material_Thickness, Material_Thickness]) {
            rotate([90, 0, 90]) {
              linear_extrude(height = Inside_Width + (Material_Thickness * 2) + 1, convexity = 10) {
                intersection() {
                  patternMask(Inside_Length, Inside_Height);
                  patternWidth = Inside_Length - (insideCornerRadius * 2);
                  patternHeight = Inside_Height;

                  translate([insideCornerRadius, 0, 0]) {
                    offset(r = -Material_Thickness - Pattern_Padding) {
                      offset(r = insideCornerRadius) {
                        offset(delta = -insideCornerRadius) {
                        square([patternWidth, patternHeight]);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        // rear pattern
        if (Rear == 1) {
          translate([Material_Thickness, Inside_Length + (Material_Thickness * 2) + 0.5, Material_Thickness]) {
            rotate([90, 0, 0]) {
              linear_extrude(height = Material_Thickness + 1, convexity = 10) {
                intersection() {
                  patternMask(Inside_Width, Inside_Height);
                  patternWidth = Inside_Width - (insideCornerRadius * 2);
                  patternHeight = Inside_Height;

                  translate([insideCornerRadius, 0, 0]) {
                    offset(r = -Material_Thickness - Pattern_Padding) {
                      offset(r = insideCornerRadius) {
                        offset(delta = -insideCornerRadius) {
                        square([patternWidth, patternHeight]);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }

  module containerPart() {
    body(1);
  }

  module lidPart() {
    translate([0, 0, Material_Thickness]) {
      translate([Inside_Width + (Material_Thickness * 2), 0, 0]) {
        rotate([0, 180, 0]) {
          body(2);
        }
      }
    }
  }

  if (Part != 5) {
    if (Part == 1) {
      containerPart();

      echo("Container dimensions:");

      echo(str("\tOutside length: ", Inside_Length + (Material_Thickness * 2), "mm"));
      echo(str("\tOutside width: ", Inside_Width + (Material_Thickness * 2), "mm"));
      echo(str("\tOutside height: ", Inside_Height + Material_Thickness + lipHeight, "mm"));

      echo(str("\tLip width: ", Material_Thickness / 2, "mm"));
      echo(str("\tLip height: ", lipHeight, "mm"));

      echo(str("\tLip recess width: ", (Material_Thickness / 2) + Lip_Gap, "mm"));
      echo(str("\tLip recess height: ", lipRecessHeight, "mm"));
    } else if (Part == 2) {
      lidPart();

      echo("Lid dimensions:");

      echo(str("\tOutside length: ", Inside_Length + (Material_Thickness * 2), "mm"));
      echo(str("\tOutside width: ", Inside_Width + (Material_Thickness * 2), "mm"));
      echo(str("\tOutside height: ", Material_Thickness, "mm"));

      echo(str("\tLip recess width: ", (Material_Thickness / 2) + Lip_Gap, "mm"));
      echo(str("\tLip recess height: ", lipRecessHeight, "mm"));
    } else if (Part == 3) {
      intersection() {
        containerPart();
        cube([Inside_Corner_Radius + (Material_Thickness * 1.5), Inside_Corner_Radius + (Material_Thickness * 1.5), Inside_Height + Material_Thickness + lipHeight]);
      }
    } else if (Part == 4) {
      intersection() {
        lidPart();
        cube([Inside_Corner_Radius + (Material_Thickness * 1.5), Inside_Corner_Radius + (Material_Thickness * 1.5), Material_Thickness]);
      }
    }
  } else {
    if ($preview) {
      %body(1);

      translate([0, 0, Material_Thickness + Inside_Height + Split_Parts_In_Preview]) {
        %body(1);

        translate([0, 0, Material_Thickness + Inside_Height + Split_Parts_In_Preview]) {
          %body(2);
        }
      }
    }
  }
}

caddy();

echo("\r\tThis design is completely free and shared under a permissive license.\r\tYour support is hugely appreciated: codeandmake.com/support\r");
```


[User]
Hey there, I'm looking to create a custom detergent cup holder that I can 3D print. I've got a few different detergent bottles, and the valve cap sizes vary quite a bit, so I need something parametric. I want to be able to easily adjust the diameter of the ring that grips the bottle, the diameter of the cup that holds the detergent, and the height of the cup itself.

Specifically, I envision a design with a ring that fits around the dispenser valve cap, a small platform extending out from that ring, and a cylindrical cup attached to the bottom of that platform. The cup is where the detergent will collect. I also want some flaring support between the ring and the platform and then the platform and the cup. I'd like a way to optionally generate just the ring, to test fit on the bottle before printing the whole thing.

I'd like the following to be customizable:

*   **`ring_diameter`**: The diameter of the inner ring that grips the bottle cap.
*   **`cup_diameter`**: The diameter of the detergent cup.
*   **`cup_height`**: The height of the detergent cup.
*  **`test_ring_only`**: A boolean value. Setting this to true should only generate the ring itself for test fitting on the bottle.

I don't really care about the wall thicknesses so much as making sure everything is solid for 3d printing and I don't mind tweaking it myself later. Let me know what you think and if you can help me generate a suitable OpenSCAD file or files.

[Assistant]
Detergent_Cup_Holder.scad:
```
ring_diameter   = 43.5;     // Diamater of the valve cap. 43.5 Fits Costco Kirkland Signature Bottles.
cup_diameter    = 64;       // Diameter of cup (determines base width)
cup_height      = 60;       // Height of the cup
test_ring_only  = 0;        // Set to "1" if you just want to create a test ring to test fitment on the bottle

// Most of this you should be able to leave alone
ring_thickness  = 4;
ring_depth      = 2;
cup_clearance   = 5;

$fn             = 64;

//rotate([90,0,0])
difference() {
    union() {
        cylinder(h=ring_depth,d=ring_diameter+ring_thickness);
        if (test_ring_only == 0){
            translate([-(ring_diameter+ring_thickness)/2,-(ring_diameter+ring_thickness)/2,0])
              cube([ring_diameter+ring_thickness,(ring_diameter+ring_thickness)/2,ring_depth]);
            color("red") translate([-(ring_diameter+ring_thickness)/2,-((ring_diameter+ring_thickness)/2+cup_height+cup_clearance),0])
              cube([ring_diameter+ring_thickness,cup_height+cup_clearance,ring_depth]);
            translate([0,-(cup_height+cup_clearance+(ring_diameter+ring_thickness)/2),(cup_diameter+ring_thickness)/2+ring_depth]) rotate([90,0,0])
              difference(){
                union(){
                    cylinder(h=ring_depth*2,d=cup_diameter+ring_thickness*2);
                    translate([-(cup_diameter/2+ring_thickness),-(cup_diameter/2+ring_thickness),0])
                      cube([cup_diameter+ring_thickness*2,cup_diameter/2+ring_thickness,ring_depth*2]);
                }
                cylinder(h=ring_depth,d=cup_diameter+ring_thickness);
              }
           back_flaring_r();
           mirror([1,0,0]) back_flaring_r();
           support_flaring_r();
           mirror([1,0,0]) support_flaring_r();
        }
    }
    cylinder(h=ring_depth,d=ring_diameter);
}

module back_flaring_r(){
    flaring_height = ring_diameter/2+ring_thickness+cup_height+cup_clearance;
    union(){
        translate([(ring_diameter+ring_thickness)/2,-flaring_height,0]) color("blue")
          cube([(cup_diameter-ring_diameter)/2+ring_depth,flaring_height/2+ring_thickness/2,ring_depth]);
        translate([(ring_diameter+ring_thickness)/2,-flaring_height/2+ring_thickness/2,0]) color("green")
          linear_extrude(ring_depth)
            polygon( points=[[0,0],[0,flaring_height/2-ring_thickness/2],[(cup_diameter-ring_diameter)/2+ring_depth,0]] );
    }
}

module support_flaring_r(){
    back_flaring_height = ring_diameter/2+ring_thickness+cup_height+cup_clearance;
    union(){
        translate([cup_diameter/2+ring_thickness, -back_flaring_height+ring_thickness/2, ring_depth]) color("orange")
          rotate([0,-90,0]) linear_extrude(ring_depth)
            polygon( points=[[0,0],[0,back_flaring_height/2],[cup_diameter/2+ring_depth,0]] );
    }
}
```