[User]
Hey there! I'm looking for some help designing a parametric phone case in OpenSCAD. I've got a pretty good idea of what I want, and I've even got some measurements handy. I'm hoping you can help me generate the SCAD code.

Basically, I need a case that fits snugly around my phone. I want to be able to specify things like the phone's dimensions (length, width, height), the radius of the corners, and the thickness of the case itself. I'd also like to include cutouts for things like the camera, flashlight, fingerprint sensor, volume buttons, power button, USB port, speakers, and even a headphone jack if needed.

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

**Phone Dimensions:**
*   `LENGTH`:  The length of my phone in millimeters.
*   `WIDTH`: The width of my phone in millimeters.
*   `HEIGHT`: The height/thickness of my phone in millimeters.
*   `CORNER_RADIUS`: The radius of the rounded corners of the phone. If the corners are square, this would be 0.

**Camera:**
*   `CAMERA_HOLE`: Yes or no, whether there should be a camera cutout.
*   `CAMERA_LEFT_TO_RIGHT_SIZE`: The horizontal size of the camera cutout in millimeters.
*  `CAMERA_LEFT_OFFSET`: Distance from the left edge of the phone to the *center* of the camera cutout.
*   `CAMERA_TOP_TO_BOTTOM_SIZE`: The vertical size of the camera cutout in millimeters.
*   `CAMERA_TOP_OFFSET`: Distance from the top edge of the phone to the *center* of the camera cutout.
*   `CAMERA_RADIUS`: The radius of the camera cutout's rounded corners.

**Flashlight:**
*   `FLASHLIGHT_HOLE`: Yes or no, whether there should be a flashlight cutout.
*   `FLASHLIGHT_LEFT_TO_RIGHT_SIZE`: The horizontal size of the flashlight cutout in millimeters.
*  `FLASHLIGHT_LEFT_OFFSET`: Distance from the left edge of the phone to the *center* of the flashlight cutout.
*   `FLASHLIGHT_TOP_TO_BOTTOM_SIZE`: The vertical size of the flashlight cutout in millimeters.
*   `FLASHLIGHT_TOP_OFFSET`: Distance from the top edge of the phone to the *center* of the flashlight cutout.
*   `FLASHLIGHT_RADIUS`: The radius of the flashlight cutout's rounded corners.

**Fingerprint Sensor:**
*  `FINGERPRINT_HOLE`: Yes or no, whether there should be a fingerprint sensor cutout.
*   `FINGERPRINT_LEFT_TO_RIGHT_SIZE`: The horizontal size of the fingerprint sensor cutout in millimeters.
*   `FINGERPRINT_LEFT_OFFSET`: Distance from the left edge of the phone to the *center* of the fingerprint cutout.
*   `FINGERPRINT_TOP_TO_BOTTOM_SIZE`: The vertical size of the fingerprint sensor cutout in millimeters.
*  `FINGERPRINT_TOP_OFFSET`: Distance from the top edge of the phone to the *center* of the fingerprint cutout.
*   `FINGERPRINT_RADIUS`: The radius of the fingerprint sensor cutout's rounded corners.

**Buttons and Ports (Volume, Power, USB, Speakers, Headphone Jack):**
*   `*_BUTTON_HOLE`/`*_PLUG_HOLE`/`*_JACK_HOLE`: Yes or no, whether there should be a cutout for the specific item. (e.g. `VOLUME_BUTTON_HOLE`, `USB_PLUG_HOLE`)
*   `*_BUTTON_SIDE`/`*_PLUG_SIDE`/`*_JACK_SIDE`:  Which side of the phone the cutout is on ("Right", "Left", "Top", "Bottom").
*   `*_BUTTON_SHAPE`/`*_PLUG_SHAPE`/`*_JACK_SHAPE`:  The shape of the cutout ("Roundy Rectangle", "Round").
*   `*_BUTTON_SIZE`/`*_PLUG_SIZE`/`*_JACK_SIZE`: The size of the cutout (a single number, as it'll be a rectangle or circle/cylinder).
*   `*_BUTTON_OFFSET`/`*_PLUG_OFFSET`/`*_JACK_OFFSET`:  The distance from the edge of the phone to the *center* of the cutout. This should be from the left edge for "Top" or "Bottom" sides and the top edge for "Right" or "Left" sides.

**Case Settings:**
*  `BASEPLATE_THICKNESS`: The thickness of the bottom part of the case (the "baseplate").
*   `WALL_THICKNESS`:  The thickness of the walls around the phone.
*   `WALL_TOP_TO_BOTTOM_LENGHT`: How far the walls extend vertically, as a percentage of the phone's width.
*  `WALL_LEFT_TO_RIGHT_LENGHT`: How far the walls extend horizontally, as a percentage of the phone's length.
*   `HOLDER_WIDTH`: How much the holders overlap the phone.
*   `HOLDER_HEIGHT`: The thickness/height of the holders.

**Fillets:**
*   `UPPER_FILLETS`:  "yes" or "no", whether to add fillets on the upper side to ease printing.
*   `LOWER_FILLETS`: "yes" or "no", whether to add fillets on the lower side for stability.

**Smoother Settings:**
*   `SMOOTHER`:  An integer to control the case's smoothness (0 for none, 1 for "Roundy").
*   `S_RADIUS`:  The radius for the smoother.

I'd prefer not to use any external libraries if possible, I'd like all of the code to be included in a single `.scad` file if possible. If you do need to use a library, please note that and make sure that is included in the response.

I think that covers everything. Let me know if you need any clarifications! I'm excited to see what you come up with.

[Assistant]
phone_cover.scad:
```
//**************Constants********************
/* [Global] */

/* [Phone] */
// of your phone (mm)
LENGTH = 152.25;
// of your phone (mm)
WIDTH = 76.25;
// of your phone (mm)
HEIGHT = 8.3;
// of yout phone, Imagine the phone's round edge as a full circle and meassure its radius! If the edges are square type 0! (mm)
CORNER_RADIUS = 9;

/* [Camera] */

//Do you have a Backside camera?
CAMERA_HOLE = "yes"; // [yes,no]
//Meassure the camera from left to right! (mm)
CAMERA_LEFT_TO_RIGHT_SIZE = 15.0;
//Meassure the distance from the left edge of the phone to the middle of your camera! (mm)
CAMERA_LEFT_OFFSET = 38.125;
//Meassure the camera from bottom to top! (mm)
CAMERA_TOP_TO_BOTTOM_SIZE = 25.0;
//Meassure the distance from the top edge of the phone to the middle of your camera! (mm)
CAMERA_TOP_OFFSET = 23.4;
//Meassure the radius of the camera (like phones corner)! (mm)
CAMERA_RADIUS = 7.5;

/* [Flashlight] */

//Do you have a flashlight at the back?
FLASHLIGHT_HOLE = "yes"; // [yes,no]
//Meassure the flashlight from left to right! (mm)
FLASHLIGHT_LEFT_TO_RIGHT_SIZE = 5.0;
//Meassure the distance from the left edge of the phone to the middle of your flashlight! (mm)
FLASHLIGHT_LEFT_OFFSET = 50.7;
//Meassure the flashlight from bottom to top! (mm)
FLASHLIGHT_TOP_TO_BOTTOM_SIZE = 5.0;
//Meassure the distance from the top edge of the phone to the middle of your flashlight! (mm)
FLASHLIGHT_TOP_OFFSET = 18.3;
//Meassure the radius of the flashlight (like phones corner)! (mm)
FLASHLIGHT_RADIUS = 2.5;

/* [Fingerprint Sensor] */

//Do you have a fingerprint sensor at the back?
FINGERPRINT_HOLE = "yes"; // [yes,no]
//Meassure the fingerprint sensor from left to right! (mm)
FINGERPRINT_LEFT_TO_RIGHT_SIZE = 15;
//Meassure the distance from the left edge of the phone to the middle of your fingerprint sensor! (mm)
FINGERPRINT_LEFT_OFFSET = 38.125;
//Meassure the fingerprint sensor from bottom to top! (mm)
FINGERPRINT_TOP_TO_BOTTOM_SIZE = 15;
//Meassure the distance from the top edge of the phone to the middle of your fingerprint sensor! (mm)
FINGERPRINT_TOP_OFFSET = 41.5;
//Meassure the radius of the fingerprint sensor (like phones corner)! (mm)
FINGERPRINT_RADIUS = 7.25;
;

/* [Volume Button] */

//Do you have a volume button?
VOLUME_BUTTON_HOLE = "yes"; // [yes,no]
//On which side of your phone is it?
VOLUME_BUTTON_SIDE = "Right"; // [Right,Left,Top,Bottom]
//Which Shape do you want the hole to be?
VOLUME_BUTTON_SHAPE = "Roundy Rectangle"; // [Roundy Rectangle,Round]
//What size should the hole have?
VOLUME_BUTTON_SIZE = 20;
//Type in the offset (middle point) of your Hole from left (if the side is Top or Bottom) or top (if the side is Left or Right)
VOLUME_BUTTON_OFFSET = 29.5;

/* [Power Button] */

//Do you have a power button?
POWER_BUTTON_HOLE = "yes"; // [yes,no]
//On which side of your phone is it?
POWER_BUTTON_SIDE = "Right"; // [Right,Left,Top,Bottom]
//Which Shape do you want the hole to be?
POWER_BUTTON_SHAPE = "Roundy Rectangle"; // [Roundy Rectangle,Round]
//What size should the hole have?
POWER_BUTTON_SIZE = 9.5;
//Type in the offset (middle point) of your Hole from left (if the side is Top or Bottom) or top (if the side is Left or Right)
POWER_BUTTON_OFFSET = 52;

/* [USB Plug] */

//Do you have a USB plug?
USB_PLUG_HOLE = "yes"; // [yes,no]
//On which side of your phone is it?
USB_PLUG_SIDE = "Bottom"; // [Right,Left,Top,Bottom]
//Which Shape do you want the hole to be?
USB_PLUG_SHAPE = "Roundy Rectangle"; // [Roundy Rectangle,Round]
//What size should the hole have?
USB_PLUG_SIZE = 12;
//Type in the offset (middle point) of your Hole from left (if the side is Top or Bottom) or top (if the side is Left or Right)
USB_PLUG_OFFSET = 38.125;

/* [Speaker 1] */

//Do you have a Speaker?
SPEAKER_1_HOLE = "yes"; // [yes,no]
//On which side of your phone is it?
SPEAKER_1_SIDE = "Bottom"; // [Right,Left,Top,Bottom]
//Which Shape do you want the hole to be?
SPEAKER_1_SHAPE = "Roundy Rectangle"; // [Roundy Rectangle,Round]
//What size should the hole have?
SPEAKER_1_SIZE = 13;
//Type in the offset (middle point) of your Hole from left (if the side is Top or Bottom) or top (if the side is Left or Right)
SPEAKER_1_OFFSET = 19.5;

/* [Speaker 2] */

//Do you have a second Speaker?
SPEAKER_2_HOLE = "yes"; // [yes,no]
//On which side of your phone is it?
SPEAKER_2_SIDE = "Bottom"; // [Right,Left,Top,Bottom]
//Which Shape do you want the hole to be?
SPEAKER_2_SHAPE = "Roundy Rectangle"; // [Roundy Rectangle,Round]
//What size should the hole have?
SPEAKER_2_SIZE = 13;
//Type in the offset (middle point) of your Hole from left (if the side is Top or Bottom) or top (if the side is Left or Right)
SPEAKER_2_OFFSET = 57.5;

/* [Headphone Jack] */

//Do you have a Headphone jack?
HEADPHONE_JACK_HOLE = "yes"; // [yes,no]
//On which side of your phone is it?
HEADPHONE_JACK_SIDE = "Top"; // [Right,Left,Top,Bottom]
//Which Shape do you want the hole to be?
HEADPHONE_JACK_SHAPE = "Round"; // [Roundy Rectangle,Round]
//What size should the hole have?
HEADPHONE_JACK_SIZE = 0;
//Type in the offset (middle point) of your Hole from left (if the side is Top or Bottom) or top (if the side is Left or Right)
HEADPHONE_JACK_OFFSET = 19;

/* [Cover Settings] */

//Type in the thickness of the cover's groud plate! (mm)
BASEPLATE_THICKNESS = 1;
//Type in the thickness of the cover's walls around the phone! (mm)
WALL_THICKNESS = 2;
//Type in the length of the cover's walls around the phone! (%)
WALL_TOP_TO_BOTTOM_LENGHT = 100; //[0:100]
//Type in the length of the cover's walls around the phone! (%)
WALL_LEFT_TO_RIGHT_LENGHT = 100; //[0:100]
//Type in how much the holders should overlap your phone! (mm)
HOLDER_WIDTH = 1;
//Type in the thickness of the holders! (mm)
HOLDER_HEIGHT = 1;

/* [Fillets] */

//Do you want to use Inner Fillets on the upper side to make printing easier?
UPPER_FILLETS = "yes"; // [yes,no]

//Do you want to use Inner Fillets on the lower side to improve stability?
LOWER_FILLETS = "yes"; // [yes,no]

/* [Smoother Settings] */

//Which smoother do you want to use? A Smoother makes your cover less square and look more fancy. (Known to be buggy and slow at the moment, will be rewritten soon ;) )
SMOOTHER = 1; // [0:none,1:Roundy]
//How round do you want it to be?
S_RADIUS = 8; //[8:70]

/* [Hidden] */

//****************Variables******************
OVERTHICKNESS = 3;
H_OVERTHICKNESS = 4;
ABS_LENGTH = LENGTH + 2*WALL_THICKNESS;
ABS_WIDTH = WIDTH + 2*WALL_THICKNESS;
ABS_HEIGHT = HEIGHT + BASEPLATE_THICKNESS + HOLDER_HEIGHT;
OUTER_CORNER_RADIUS = CORNER_RADIUS+WALL_THICKNESS;
//Add some squareness to it?
S_INFILL = 0; //[0:300]
//If your phone isn't too square, you can leaf this standard value!
LOWER_FILLET_RADIUS = HOLDER_WIDTH;
//If your phone isn't too square, you can leaf this standard value!
UPPER_FILLET_RADIUS = HOLDER_WIDTH;
//****************Geometry*******************

$fa=0.5; $fs=0.5;

Cover();

//WallHole(side = "Right", shape="Roundy Rectangle", off = 0, size = 33);
//Hole(xoff = 0, yoff = 0, xsize = 25, ysize = 15, radius = 7.5);
//****************Modules********************

//Main Modules
module Cover()
{
    difference()
    {
        union()
        {
            Baseplate();
            Walls(size_l= WALL_TOP_TO_BOTTOM_LENGHT, size_w = WALL_LEFT_TO_RIGHT_LENGHT,smoother = SMOOTHER);
            //HolderCorners();
            //HolderWalls();
        }
        if(CAMERA_HOLE == "yes")
        {
            Hole(yoff = CAMERA_LEFT_OFFSET, xoff = CAMERA_TOP_OFFSET, xsize = CAMERA_TOP_TO_BOTTOM_SIZE, ysize = CAMERA_LEFT_TO_RIGHT_SIZE, radius = CAMERA_RADIUS);
        }
        if(FLASHLIGHT_HOLE == "yes")
        {
            Hole(yoff = FLASHLIGHT_LEFT_OFFSET, xoff = FLASHLIGHT_TOP_OFFSET, xsize = FLASHLIGHT_TOP_TO_BOTTOM_SIZE, ysize = FLASHLIGHT_LEFT_TO_RIGHT_SIZE, radius = FLASHLIGHT_RADIUS);
        }
        if(FINGERPRINT_HOLE == "yes")
        {
            Hole(yoff = FINGERPRINT_LEFT_OFFSET, xoff = FINGERPRINT_TOP_OFFSET, xsize = FINGERPRINT_TOP_TO_BOTTOM_SIZE, ysize = FINGERPRINT_LEFT_TO_RIGHT_SIZE, radius = FINGERPRINT_RADIUS);
        }
        if(VOLUME_BUTTON_HOLE == "yes")
        {
            WallHole(side = VOLUME_BUTTON_SIDE, shape = VOLUME_BUTTON_SHAPE, off = VOLUME_BUTTON_OFFSET, size = VOLUME_BUTTON_SIZE);
        }
        if(POWER_BUTTON_HOLE == "yes")
        {
            WallHole(side = POWER_BUTTON_SIDE, shape = POWER_BUTTON_SHAPE, off = POWER_BUTTON_OFFSET, size = POWER_BUTTON_SIZE);
        }
        if(USB_PLUG_HOLE == "yes")
        {
            WallHole(side = USB_PLUG_SIDE, shape = USB_PLUG_SHAPE, off = USB_PLUG_OFFSET, size = USB_PLUG_SIZE);
        }
        if(SPEAKER_1_HOLE == "yes")
        {
            WallHole(side = SPEAKER_1_SIDE, shape = SPEAKER_1_SHAPE, off = SPEAKER_1_OFFSET, size = SPEAKER_1_SIZE);
        }
        if(SPEAKER_2_HOLE == "yes")
        {
            WallHole(side = SPEAKER_2_SIDE, shape = SPEAKER_2_SHAPE, off = SPEAKER_2_OFFSET, size = SPEAKER_2_SIZE);
        }
        if(HEADPHONE_JACK_HOLE == "yes")
        {
            WallHole(side = HEADPHONE_JACK_SIDE, shape = HEADPHONE_JACK_SHAPE, off = HEADPHONE_JACK_OFFSET, size = HEADPHONE_JACK_SIZE);
        }
    }
}

module Hole(xoff = 0, yoff = 0, xsize = 20, ysize = 20, radius = 10)
{
    translate([LENGTH/2-xoff-xsize/2+radius,-WIDTH/2+yoff-ysize/2+radius,-BASEPLATE_THICKNESS*H_OVERTHICKNESS/2])
    {
        if (radius == 0) //rect
        {
            cube([xsize-2*radius,ysize-2*radius,BASEPLATE_THICKNESS*H_OVERTHICKNESS]);
        }
        else if(radius*2 < xsize && radius*2 < ysize) //rounded rect
        {
            minkowski()
            {
                cube([xsize-2*radius,ysize-2*radius,BASEPLATE_THICKNESS*H_OVERTHICKNESS/2]);
                cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS/2,r = radius);
            }
        }
        else if(radius*2 == xsize && radius*2 < ysize) //connected cyls
        {
            hull()
            {
                translate([0,ysize-2*radius,0]) {cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS,r = radius);}
                cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS,r = radius);
            }
        }
        else if(radius*2 == ysize && radius*2 < xsize) //connected cyls
        {
            hull()
            {
                translate([xsize-2*radius,0,0]) {cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS,r = radius);}
                cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS,r = radius);
            }
        }
        else if(radius*2 == ysize && radius*2 == xsize) //circle
        {
            cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS,r = radius);
        }
        else
        {
            echo ("ERROR when making hole, 2*radius has to be <= both sizes");
        }
    }
}

module WallHole(side = "Right", shape="Round", off = 10, size = 20)
{
    if(shape=="Roundy Rectangle") //rounded rect
    {
        if(side == "Top")
        {
            translate([LENGTH/2,WIDTH/2-off,BASEPLATE_THICKNESS+HEIGHT/2]) rotate([0,270,0])
            {
                translate([0,0,0]) minkowski()
                {
                    cube([HEIGHT/2,size-HEIGHT/2,WALL_THICKNESS*OVERTHICKNESS/2],true);
                    cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS/2,r = HEIGHT/4);
                }
            }

        }
        if(side == "Bottom")
        {
            translate([-LENGTH/2,WIDTH/2-off,BASEPLATE_THICKNESS+HEIGHT/2]) rotate([0,270,0])
            {
                translate([0,0,0]) minkowski()
                {
                    cube([HEIGHT/2,size-HEIGHT/2,WALL_THICKNESS*OVERTHICKNESS/2],true);
                    cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS/2,r = HEIGHT/4);
                }
            }
        }
        if(side == "Left")
        {
            translate([LENGTH/2-off+size/2-HEIGHT/4,WIDTH/2+(WALL_THICKNESS),BASEPLATE_THICKNESS+HEIGHT/4]) rotate([0,270,90])
            {
                translate([0,0,0]) minkowski()
                {
                    cube([HEIGHT/2,size-HEIGHT/2,WALL_THICKNESS*OVERTHICKNESS/2]);
                    cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS/2,r = HEIGHT/4);
                }
            }
        }
        if(side == "Right")
        {
            translate([LENGTH/2-off+size/2-HEIGHT/4,-(WIDTH/2)+(WALL_THICKNESS),BASEPLATE_THICKNESS+(HEIGHT/4)]) rotate([0,270,90])
            {
                translate([0,0,0]) minkowski()
                {
                    cube([HEIGHT/2,size-HEIGHT/2,WALL_THICKNESS*OVERTHICKNESS/2]);
                    cylinder(h = BASEPLATE_THICKNESS*H_OVERTHICKNESS/2,r = HEIGHT/4);
                }
            }
        }
    }
    else if(shape=="Round") //circle
    {
        if(side == "Top")
        {
            translate([LENGTH/2+(WALL_THICKNESS*OVERTHICKNESS)/2+1,WIDTH/2-off,BASEPLATE_THICKNESS+HEIGHT/2]) rotate([0,270,0])
            {
                translate([0,0,0]) {cylinder(h = WALL_THICKNESS*OVERTHICKNESS,r = HEIGHT/2);}

            }
        }
        if(side == "Bottom")
        {
            translate([-(LENGTH/2-(WALL_THICKNESS*OVERTHICKNESS)/2+1),WIDTH/2-off,BASEPLATE_THICKNESS+HEIGHT/2]) rotate([0,270,0])
            {
                translate([0,0,0]) {cylinder(h = WALL_THICKNESS*OVERTHICKNESS,r = HEIGHT/2);}

            }
        }
        if(side == "Left")
        {
            translate([LENGTH/2-off,WIDTH/2+(WALL_THICKNESS*OVERTHICKNESS)/2+1,BASEPLATE_THICKNESS+HEIGHT/2]) rotate([0,270,90])
            {
                translate([0,0,0]) {cylinder(h = WALL_THICKNESS*OVERTHICKNESS,r = HEIGHT/2);}

            }
        }
        if(side == "Right")
        {
            translate([LENGTH/2-off,-(WIDTH/2-(WALL_THICKNESS*OVERTHICKNESS)/2+1),BASEPLATE_THICKNESS+HEIGHT/2]) rotate([0,270,90])
            {
                translate([0,0,0]) {cylinder(h = WALL_THICKNESS*OVERTHICKNESS,r = HEIGHT/2);}
            }
        }
    }
}

module Baseplate()
{
    translate([-(LENGTH-2*CORNER_RADIUS)/2,-(WIDTH-2*CORNER_RADIUS)/2,0])
    minkowski()
    {
        cube([LENGTH-2*CORNER_RADIUS, WIDTH-2*CORNER_RADIUS,BASEPLATE_THICKNESS/2]);
        cylinder(h = BASEPLATE_THICKNESS/2,r = CORNER_RADIUS);
    }
}
module WallCorners(smoother = 1)
{
    translate([0,0,0])
    {
        translate([(LENGTH+(2*WALL_THICKNESS))/2,(WIDTH+(2*WALL_THICKNESS))/2,0])
        rotate([0,0,180])
        {
            curve(smoother);
        }
        translate([-(LENGTH+(2*WALL_THICKNESS))/2,(WIDTH+(2*WALL_THICKNESS))/2,0])
        rotate([0,0,270])
        {
            curve(smoother);
        }
        translate([-(LENGTH+(2*WALL_THICKNESS))/2,-(WIDTH+(2*WALL_THICKNESS))/2,0])
        rotate([0,0,0])
        {
            curve(smoother);
        }
        translate([(LENGTH+(2*WALL_THICKNESS))/2,-(WIDTH+(2*WALL_THICKNESS))/2,0])
        rotate([0,0,90])
        {
            curve(smoother);
        }
    }
}

module Walls(size_l=17, size_w=20, smoother = 1)
{
    translate([0,0,0])
    {
        //Top
        translate([LENGTH/2+WALL_THICKNESS/2,0,0])  difference()
            {
                translate([WALL_THICKNESS/2,WIDTH/2-CORNER_RADIUS,0]) rotate([0,270,90]) linear_extrude(height = WIDTH-(2*CORNER_RADIUS))
                {
                    ShapeSmoother(smoother);
                }
                translate([0,0,BASEPLATE_THICKNESS]) linear_extrude(height = ABS_HEIGHT)
                {
                    translate([0,0,BASEPLATE_THICKNESS])
                    {square([WALL_THICKNESS*2+HOLDER_WIDTH, (WIDTH-(2*CORNER_RADIUS))*(100-size_w)/100], center = true);}
                }
        }
        //Bottom
        translate([-(LENGTH/2+WALL_THICKNESS/2),0,0]) difference()
            {
                translate([-WALL_THICKNESS/2,-WIDTH/2+CORNER_RADIUS,0]) rotate([0,270,270]) linear_extrude(height = WIDTH-(2*CORNER_RADIUS))
                {
                    ShapeSmoother(smoother);
                }
                translate([0,0,BASEPLATE_THICKNESS]) linear_extrude(height = ABS_HEIGHT)
                {
                    translate([0,0,BASEPLATE_THICKNESS])
                    {square([WALL_THICKNESS*2+HOLDER_WIDTH, (WIDTH-(2*CORNER_RADIUS))*(100-size_w)/100], center = true);}
                }
        }
        //Right
        translate([0,(WIDTH/2+WALL_THICKNESS/2),0])
        {
            difference()
            {
                translate([-(LENGTH-(2*CORNER_RADIUS))/2,WALL_THICKNESS/2,0]) rotate([0,270,180]) linear_extrude(height = LENGTH-(2*CORNER_RADIUS))
                {
                    ShapeSmoother(smoother);
                }
                translate([0,0,BASEPLATE_THICKNESS]) linear_extrude(height = ABS_HEIGHT)
                {
                    translate([0,0,BASEPLATE_THICKNESS])
                    {square([(LENGTH-(2*CORNER_RADIUS))*(100-size_l)/100,WALL_THICKNESS*2+HOLDER_WIDTH], center = true);}
                }
            }
        }
        //Left
        translate([0,-(WIDTH/2+WALL_THICKNESS/2),0])
        {
            difference()
            {
                translate([(LENGTH-(2*CORNER_RADIUS))/2,-WALL_THICKNESS/2,0]) rotate([0,270,0]) linear_extrude(height = LENGTH-(2*CORNER_RADIUS))
                {
                    ShapeSmoother(smoother);
                }
                translate([0,0,BASEPLATE_THICKNESS]) linear_extrude(height = ABS_HEIGHT)
                {

                    {square([(LENGTH-(2*CORNER_RADIUS))*(100-size_l)/100,WALL_THICKNESS*2+HOLDER_WIDTH], center = true);}
                }
            }
        }
    }
    WallCorners(smoother );
}

//Helper Modules

module WallShape ()
{

    union()
    {
        //WALL
        square([ABS_HEIGHT,WALL_THICKNESS]);
        //Holder
        translate ([ABS_HEIGHT-HOLDER_HEIGHT,0,0]) {square([HOLDER_HEIGHT,HOLDER_WIDTH+WALL_THICKNESS]);}
        //Upper fillet
        if(UPPER_FILLETS == "yes")
        {
            translate([ABS_HEIGHT-HOLDER_HEIGHT-UPPER_FILLET_RADIUS,UPPER_FILLET_RADIUS+WALL_THICKNESS,0])
            rotate([0,0,270])
            difference()
            {
                square([UPPER_FILLET_RADIUS,UPPER_FILLET_RADIUS]);
                circle(UPPER_FILLET_RADIUS);
            }
        }
        if(LOWER_FILLETS == "yes")
        {
            translate([LOWER_FILLET_RADIUS+BASEPLATE_THICKNESS,LOWER_FILLET_RADIUS+WALL_THICKNESS,0])
            rotate([0,0,180])
            difference()
            {
                square([LOWER_FILLET_RADIUS,LOWER_FILLET_RADIUS]);
                circle(LOWER_FILLET_RADIUS);
            }
        }
    }

}

module ShapeSmoother(smoother = 1)
{

        intersection()
        {
            if(smoother == 1)
            {
                translate([ABS_HEIGHT/2,S_RADIUS])
                {circle(S_RADIUS);}
            }
            WallShape ();
        }

}

module curve(smoother = 1)
{
    translate([OUTER_CORNER_RADIUS,OUTER_CORNER_RADIUS,0])rotate([0,0,90])
    difference()
    {
        rotate_extrude()translate([WALL_THICKNESS+CORNER_RADIUS,0,0]) rotate([0,0,90])
        {
            difference()
            {

                ShapeSmoother(smoother);
                translate([-ABS_HEIGHT*OVERTHICKNESS/4,WALL_THICKNESS+CORNER_RADIUS])
                {

                     square([ABS_HEIGHT*OVERTHICKNESS,WALL_THICKNESS+HOLDER_WIDTH]);
                }
            }
        }
        translate([0,0,-(ABS_HEIGHT*OVERTHICKNESS)/2]) linear_extrude(height=ABS_HEIGHT*OVERTHICKNESS)
        {
            polygon(points=[
            [0,0],
            [0,OUTER_CORNER_RADIUS*OVERTHICKNESS],
            [OUTER_CORNER_RADIUS*OVERTHICKNESS,OUTER_CORNER_RADIUS*OVERTHICKNESS],
            [OUTER_CORNER_RADIUS*OVERTHICKNESS,-OUTER_CORNER_RADIUS*OVERTHICKNESS],
            [-OUTER_CORNER_RADIUS*OVERTHICKNESS,-OUTER_CORNER_RADIUS*OVERTHICKNESS],
            [-OUTER_CORNER_RADIUS*OVERTHICKNESS,0]]);
        }
    }
}

```


[User]
I'd like to design a pipe clamp that can hold fabric or other materials onto a pipe. It needs to be parametric so I can easily adjust it for different pipe sizes and clamp needs.

Specifically, I need to control:

*   **Inner Diameter (D_internal):** This is the diameter of the pipe that the clamp will fit around. It should be adjustable, maybe with a default around 25mm and adjustable from 8mm to 50mm.
*   **Thickness (thickness):** The thickness of the clamp material itself. I'm thinking a default of 3mm, adjustable from 3 to 10mm.
*   **Clamp Height (H_clamp):** The height of the clamp, which I think should start at 8mm and go from 5 to 20mm.
*   **Clamp Angle (angle):** The angle of the clamping arms, adjustable between 30 and 80 degrees. A default around 60 sounds good.

The clamp should consist of two parts that almost completely encircle the pipe. Then, there should be two cylinders attached to each side at the edges. These cylinders will be used to hold the fabric that is to be clamped.

Please generate an OpenSCAD file that will generate the clamp according to these parameters. I don't want to use any external libraries.

[Assistant]
PipeClamp2.scad:
```
D_internal=25;//[8:0.1:50]
thickness=3;//[3:0.5:10]
H_clamp=8;//[5:0.5:20]
angle=60;//[30:5:80]

/* [Hidden] */
R_internal=D_internal/2;

Rext=R_internal+thickness;
radius_cylinder=thickness/2 + thickness/10;

H_clamp=8;
//25,16,19,22
$fn=50;

difference()
{
    cylinder(r=Rext,h=H_clamp);
    translate ([0,0,-0.5])    cylinder(r=R_internal,H_clamp+1);
    hull()
    {
        rotate([0,0,0])translate ([0,0,-0.5]) cylinder(r=1,h=H_clamp+1);
        rotate([0,0,angle])translate ([(R_internal+thickness)*20,0,-0.5]) cylinder(r=1,h=H_clamp+1);
        rotate([0,0,-angle])translate ([(R_internal+thickness)*20,0,-0.5]) cylinder(r=1,h=H_clamp+1);
    }
}

rotate([0,0,-angle])translate ([R_internal+thickness/2,0,0]) cylinder(r=radius_cylinder,h=H_clamp);
rotate([0,0,+angle])translate ([R_internal+thickness/2,0,0]) cylinder(r=radius_cylinder,h=H_clamp);

```


[User]
I'd like to create a customizable capsule with a screw-on lid, suitable for 3D printing. I've seen a design on Thingiverse that's close to what I want, but I'd like to have my own version. The capsule should have two main parts that screw together. I need to be able to easily adjust the overall length, the outside diameter, the wall thickness, and the tolerance of the screw thread. Additionally, I'd like to be able to specify the number of faces to control the roundness of the capsule.

Specifically, the design should have these features:

*   Two separate parts: Part 1 (the main body) and Part 2 (the screw-on lid).
*   A screw thread for joining the two parts. The screw thread should have customizable tolerance for 3D printing.
*   A cylindrical main body with rounded ends.
*   Customizable length of the assembled capsule.
*   Customizable outer diameter.
*   Customizable wall thickness.
*   Customizable number of faces (resolution) for roundness.
*   A small loop on the lid for attaching a keychain.

I'm not too concerned about the specific screw profile, but it should be robust enough for repeated use. I’d like a simple, clean design for the screw threads, and it needs to work with the provided tolerance setting.

Could you generate a parametric OpenSCAD script (or scripts) that achieve this? Make sure to parameterize all of the adjustable settings mentioned above. You can use any libraries you need, but please list them. If you provide multiple files, please explain the purpose of each file. I assume it will be a single file though. The files need to be executable when I open it with OpenSCAD.

[Assistant]
simpleCapsule.scad:
```
/*
 Customizable simple capsule script
 https://www.thingiverse.com/thing:2562388
 CC Public Domain

 simpleCapsule.scad by Johann517 @ Thingiverse
 https://www.thingiverse.com/johann517/about
*/

// Select part and change settings. Then click CREATE THING for part#1 and part#2
Part=1;// [1: Part#1, 2: Part#2]
// 3D Printer tolerance for screw connection in [mm], 1 mm is a good start
Tolerance=1; // [0.0:0.1:2.0]
// Length of both parts joined together in [mm]
Length=90;   // [20:200]
// outside diameter of capsule in [mm]
Diameter=30; // [10:100]
// Wall strength in [mm]
Wallsize=1;  // [0.7:0.1:2]
// Number of faces [Number]
Faces=50;    // [6:50]

/* [Hidden] */

// customizer to internal
show=Part;
o=Length;    // length together in [mm]
d=Diameter;  // diameter in [mm]
s=Wallsize;  // wall thickness in [mm]
g=Tolerance; // printer tolerance in [mm]
n=Faces;     // faces - resolution

// internals
j=d/4; // auto
w=7;   // screw height
h=o-d-1-j/2;
cut=0; // show inside

$fn=50;

difference(){
union(){
// top
if (show==1||show==0)
difference(){
 union(){
 hull(){
  cylinder(d=d,h=w);
  difference() {
   translate([0,0,h/2])
    sphere(d=d,$fn=n);
   translate([0,0,h/2-d])
    cube([d,d,2*d],center=true);
  }
  translate([0,0,h/2+d/2])
   rotate([90,0,0])
    cylinder(d=j*2,h=j,center=true);
  }
 }
 translate([0,0,-0.1])
  screw_thread(d-s-g,w/2,55,w,PI/2,2);
 hull(){
  difference(){
   translate([0,0,h/2-1.5])
    sphere(d=d-2*s,$fn=n);
   translate([0,0,-d/2+w])
    cube([d,d,d],center=true);
  }
  translate([0,0,w-.1])
   cylinder(d=d-2*s-g,h=.1);
 }
 translate([0,0,h/2+d/2+j/4])
  rotate([90,0,0])
   cylinder(d=j/1.6,h=d,center=true);
 translate([0,0,-0.5])
  cylinder(d1=d-s,d2=d-3*s,h=1);
 translate([0,0,-d])
  cube([d,d,2*d],center=true);
}

// bottom
if (show==2||show==0)
translate([0,0,(w-1)/2*show])
rotate([90*show,0,270])
difference(){
 union(){
  hull(){
   translate([0,0,-1])
    cylinder(d=d,h=1);
   difference(){
    translate([0,0,-h/2])
     sphere(d=d,$fn=n);
    translate([0,0,d/2])
     cube([d,d,d],center=true);
   }
  }
  cylinder(d1=d,d2=d-s-g,h=.5);
  translate([0,0,-1])
   screw_thread(d-s-2*g,w/2,55,w,PI/2,2);
 }
 hull(){
  difference() {
   translate([0,0,-h/2+s])
    sphere(d=d-2*s,$fn=n);
   translate([0,0,d/2-2])
    cube([d,d,d],center=true);
  }
  translate([0,0,-2.5])
   cylinder(d=d-w/2-2*s-2*g,h=1.5);
 }
 translate([0,0,-2])
  cylinder(d=d-w/2-2*s-2*g,h=w+2);
}
}
if(cut==1)
translate([0,-o,-o])
 cube([2*o,2*o,2*o]);
}

/*
 *    polyScrewThread_r1.scad    by aubenc @ Thingiverse
 *
 * This script contains the library modules that can be used to generate
 * threaded rods, screws and nuts.
 *
 * http://www.thingiverse.com/thing:8796
 *
 * CC Public Domain
 */
module screw_thread(od,st,lf0,lt,rs,cs)
{
    or=od/2;
    ir=or-st/2*cos(lf0)/sin(lf0);
    pf=2*PI*or;
    sn=2*floor(pf/rs);
    lfxy=360/sn;
    ttn=round(lt/st+1);
    zt=st/sn;

    intersection()
    {
        if (cs >= -1)
        {
           thread_shape(cs,lt,or,ir,sn,st);
        }

        full_thread(ttn,st,sn,zt,lfxy,or,ir);
    }
}
module thread_shape(cs,lt,or,ir,sn,st)
{
    if ( cs == 0 )
    {
        cylinder(h=lt, r=or, $fn=sn, center=false);
    }
    else
    {
        union()
        {
            translate([0,0,st/2])
              cylinder(h=lt-st+0.005, r=or, $fn=sn, center=false);

            if ( cs == -1 || cs == 2 )
            {
                cylinder(h=st/2, r1=ir, r2=or, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }

            translate([0,0,lt-st/2])
            if ( cs == 1 || cs == 2 )
            {
                  cylinder(h=st/2, r1=or, r2=ir, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }
        }
    }
}

module full_thread(ttn,st,sn,zt,lfxy,or,ir)
{
  if(ir >= 0.2)
  {
    for(i=[0:ttn-1])
    {
        for(j=[0:sn-1])
			assign( pt = [	[0,                  0,                  i*st-st            ],
                        [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt-st       ],
                        [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt-st   ],
								[0,0,i*st],
                        [or*cos(j*lfxy),     or*sin(j*lfxy),     i*st+j*zt-st/2     ],
                        [or*cos((j+1)*lfxy), or*sin((j+1)*lfxy), i*st+(j+1)*zt-st/2 ],
                        [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt          ],
                        [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt      ],
                        [0,                  0,                  i*st+st            ]	])
        {
            polyhedron(points=pt,
              		  triangles=[	[1,0,3],[1,3,6],[6,3,8],[1,6,4],
											[0,1,2],[1,4,2],[2,4,5],[5,4,6],[5,6,7],[7,6,8],
											[7,8,3],[0,2,3],[3,2,7],[7,2,5]	]);
        }
    }
  }
}

```


[User]
I'd like to design a rolling tray using OpenSCAD. It should be a very simple, two-part design that slides together. I need it to be parametric so I can adjust the size for different rolling papers and adjust the thickness and tolerances.

Here's what I'm thinking:

*   **Two Identical Pieces:** The tray will consist of two identical pieces that are mirror images of each other (or one rotated copy of the other).
*   **Sliding Interlock:** The two pieces should slide together to form the completed tray. I imagine it like a shallow trough or channel.
*   **Parametric Paper Size:** I need to be able to easily adjust the overall size of the tray based on standard rolling paper sizes. I’d like to be able to choose from options like “King”, “King Slim”, “Double Wide”, “1 1/2 inch”, “1 1/4 inch”, and “Single Wide”.
*   **Parametric Thickness:** I also want a parameter to control the thickness of the material the tray is made from.
*   **Parametric Tolerance:** Crucially, I need to be able to set a tolerance value to adjust how tight the fit is between the two pieces.
*   **Rounded Corners:** I would like the corners of the tray to be rounded, and I want to be able to control the corner radius.

The design should be pretty basic, and I'm picturing the overall shape as being a rounded rectangle with a channel in it that would allow the pieces to slide together when assembled.

Could you help me generate the necessary OpenSCAD code for this? I'm not using any special libraries so it should all be from the core OpenSCAD feature set.

[Assistant]
pane_v1.scad:
```
$fa=1; $fs=0.5;
// Show assembled
preview = true; // [true:"Yes",false:"No"]
// Rolling paper size
paper_size = "King Slim"; // [King,King Slim,Double Wide,1 1/2 inch,1 1/4 inch,Single Wide]
// Splint thickness in mm
thickness = 1.6; // [0.4,0.8,1.2,1.6,2]
// Fitting tolerance in mm
tolerance = 0.2; // [0,0.1,0.2,0.3,0.4,0.5]
// corner radius in mm
corner_rad = 2; // [0.1:Square, 1:Small, 2:Medium, 4:Large]

if(paper_size == "King"){
    paper = [110,54];
    Render(paper, preview);
}
else if(paper_size == "King Slim"){
    paper = [110,44];
    Render(paper, preview);
}
else if(paper_size == "Double Wide"){
    paper = [78,88];
    Render(paper, preview);
}
else if(paper_size == "1 1/2 inch"){
    paper = [78,60];
    Render(paper, preview);
}
else if(paper_size == "1 1/4 inch"){
    paper = [78,44];
    Render(paper, preview);
}
else if(paper_size == "Single Wide"){
    paper = [70,36];
    Render(paper, preview);
}

module Render(paper, assembled){
    if(assembled){
        Assembled(paper[0], paper[1], thickness, corner_rad, tolerance);
    }
    else{
        Pane(paper[0], paper[1], thickness, corner_rad, tolerance);
    }
}

module Pane(xdim, ydim, zdim, rdim, tolerance){
    difference(){
        hull(){
            translate([rdim,      rdim,      0])cylinder(r=rdim, h=zdim);
            translate([xdim-rdim, rdim,      0])cylinder(r=rdim, h=zdim);
            translate([xdim-rdim, ydim-rdim, 0])cylinder(r=rdim, h=zdim);
            translate([rdim,      ydim-rdim, 0])cylinder(r=rdim, h=zdim);
        }
        translate([-0.1,ydim/2-(zdim+tolerance)/2,-0.1])cube([xdim/2+tolerance+0.1, zdim+tolerance, zdim+0.2]);
    }
}

module Assembled(xdim, ydim, zdim, rdim, tolerance){
    rotate([45,0,0]){
        Pane(xdim, ydim, zdim, rdim, tolerance);
        %translate([xdim,(ydim-thickness)/2,(-ydim+thickness)/2])rotate([-90,180,0])Pane(xdim, ydim, zdim, rdim, tolerance);
    }
}
```


[User]
I'd like a parametric OpenSCAD design for a bottle or container holder. It should arrange the containers in a hexagonal grid. I want to be able to specify the number of rows and columns, the diameter of the individual holding cylinders, the gap between those cylinders, and the overall height of the holder. Also, if the rows are offset, I'd like an option to have the offset rows have one fewer columns. This helps make it more of a rectangular shape overall. Finally, it would be nice to control the thickness of the outer ring on the cylinders, scaled as a percentage of the gap between cylinders. I'd like the design to include a module that does the hexagonal packing, which will generate the positions for the bottle-holding cylinders.

I'm thinking I'd want one file containing the `HexPack` module and another for the `BottleHolder` module that uses the `HexPack` module. This should not use any additional libraries. Could you please create this for me?

[Assistant]
BottleHolder.scad:
```

/* [Hidden] */
inches = 25.4;
$fs = 1.0;

/* [Bottle Holder] */
rows = 3;
columns = 4;

// The diameter of the holes that will hold bottles.
diameter = 31.75;

// How much spacing is between the edges of the holes.
// If your bottle has lids, don't forget to account for the lid overhang!
gap = 5.0;

// How tall the bottle holder will be.
height = 6.5;

// If the offset rows should have one fewer columns.
// This allows for the whole shape to fit nicely in a rectangle.
smaller_offsets = 0; // [0,1]

// The thickness of the outer loops as a percent of the inner gap.
padding_scale = 1.0; // [1:0.05:2]

// Things I have found useful:
// Film Canisters:
//   diameter = 1.25 * inches
// Medicine Bottles:
//   diameter = 1.25 * inches
//   gap >= 0.45 * inches
// Ikea IHARDIG Spice Jars:
//   diameter = 65.5
// AA Battery Holder:
//   diameter = 14.75
// AAA Battery Holder:
//   diameter = 10.5

function int(bool) = (bool ? 1 : 0);

module HexPack(rows, columns, spacing, smaller_offsets = false) {
    // rows             How many rows of duplicated children.
    // columns          How many columns of duplicated children.
    // spacing          How far the children should be spaced from each other.
    // smaller_offsets  If the offset rows should have one fewer columns.
    //                  This allows for the whole shape to fit nicely in a rectangle.

    // Assuming the objects are hexagonally packed,
    //   this is how far their center points are from each other.
    // We can find the y spacing of the center points using the Pythagorean theorem because
    //   the center points between three adjacent circles will form an equilateral triangle.
    // Shortcut:
    //   The side length is half the hypotenuse, so...
    //   sqrt(hypot^2 - side^2)
    //   => sqrt((side * 2)^2 - side^2)
    //   => sqrt(4 * side^2 - side^2)
    //   => sqrt(3 * side^2)
    //   => sqrt(3) * side
    hex_x_spacing = spacing;
    hex_y_spacing = sqrt(3) * (spacing / 2);

    for ( row = [0 : max(1, rows - 1)] ) {
        is_offset = ( row % 2 );
        offset_shrinkage = int(smaller_offsets) * is_offset;

        // Offset rows are translated over one half a spacing.
        x_offset = (is_offset * (hex_x_spacing / 2));
        y_offset = (row * hex_y_spacing);

        for ( column = [0 : max(0, columns - offset_shrinkage - 1)] ) {
            cur_x_offset = x_offset + (column * hex_x_spacing);
            translate([cur_x_offset, y_offset, 0]) children();
        }
    }
}

module BottleHolder(rows, columns, diameter, gap, height, padding_scale = 1.0, smaller_offsets = false) {
    // rows             Number of rows of holes.
    // columns          Number of columns of holes.
    // diameter         The diameter of the holes that will hold bottles.
    // gap              How much spacing is between the edges of the holes.
    //                  If your bottles have lids, don't forget to account for them!
    // height           How tall the bottle holder will be.
    // padding_scale    The thickness of the outer loops as a percent of the inner gap.
    // smaller_offsets  If the offset rows should have one fewer columns.
    //                  This allows for the whole shape to fit nicely in a rectangle.

    outer_diameter = (diameter + gap);

    difference() {
        union() {
            // Structural cylinders that holes will be cut from.
            HexPack(rows, columns, outer_diameter, smaller_offsets)
                cylinder(height, d = diameter + (2 * gap * padding_scale));

            // Filling any gaps between the inner cylinders.
            // Try making this transparent (with %) to see why it's necessary.
            offset_shrinkage = int(smaller_offsets) * (outer_diameter / 2);
            translate([diameter / 4, 0, 0])
                cube([
                    (columns - 1) * outer_diameter - offset_shrinkage,
                    (rows - 1) * sqrt(3) * (outer_diameter / 2),
                    height
                ]);
        }

        // Cutting holes in the structural cylinders to make loops
        HexPack(rows, columns, outer_diameter, smaller_offsets)
            cylinder(height, d = diameter);
    }
}

// preview[view:south, tilt:top]
BottleHolder(
    rows     = rows,
    columns  = columns,
    diameter = diameter,
    height   = height,
    gap      = gap,
    smaller_offsets = smaller_offsets,
    padding_scale   = padding_scale
);

```


[User]
I need an OpenSCAD design for a press-fit cap with an optional guard. The cap itself should fit tightly onto a tube of a specified outer diameter, and the guard is a flexible appendage that can be used to keep the cap from being lost.

Here are the parameters I need to be able to control:

*   **TubeOuterDiameter**: The outer diameter of the tube the cap will fit onto.
*   **CapHeight**: The overall height of the cap.
*   **CapThickness**: The thickness of the cap walls.
*   **Facets**: The number of facets used to render the circular parts of the design.
*   **Tolerance**:  A tolerance value to control the press-fit (how tightly the cap fits on the tube).
*   **GuardLength**:  The length of the guard.
*   **GuardWidth**: The width of the guard.
*   **GuardInnerDiameter**: The inner diameter of the guard, which allows it to attach to objects of varying sizes. It should be independent of the tube's diameter.
*   **UseGuard**: A boolean (0 or 1) to toggle whether or not the guard is included in the design.

The design should create a cap that is essentially a cylinder with a lip at the bottom that ensures that the cap fits properly on the tube. The guard should be a rectangular extrusion attached to the cap with a circular part to wrap around an attachment point. The entire cap needs to be parametric such that it can be adapted to different applications.

I'd prefer this design to be created from scratch. Also, the design needs to be in a single .scad file.

[Assistant]
Cap.scad:
```
/*[Test tube cap generator]*/
// By MagoNegro OCT-2017

// Tube outer diameter:
TubeOuterDiameter=15.6;
// Cap height:
CapHeight=12;
// Cap thickness:
CapThickness=3;
// Facets:
Facets=60; // [4:100]
// Tolerance (Typ. 0.2mm)
Tolerance = 0.2;
// Guard's lenght (Choose yours...):
GuardLength = 30.0;
// Guard's width (Choose yours...):
GuardWidth = 5.0;
// Guard's inner diameter (Typ. same of tube's):
GuardInnerDiameter = 25.0;
// Make guard or not (0..1)
UseGuard=1;

/* [Hidden] */
CapIntR=(TubeOuterDiameter/2)+Tolerance;
CapOutR=CapIntR+CapThickness-Tolerance;

GuardIntR=(GuardInnerDiameter/2)+Tolerance;
GuardOutR=GuardIntR+CapThickness-Tolerance;

module MakeCap()
{
    difference()
    {
        cylinder(h = CapHeight,
            r = CapOutR,
            center = false,
            $fn=Facets);
        // Hole
        translate([0,0,CapThickness])
            cylinder(h = CapHeight,
                r = CapIntR,
                center = false,
                $fn=Facets);
        translate([0,0,CapHeight-CapOutR+CapThickness/2])
            difference()
            {
            cylinder(h=CapOutR,
                r1=0,
                r2=CapOutR+Tolerance,
                $fn=Facets);
            translate([0,0,-CapThickness])
                cylinder(h=CapOutR,
                    r=CapOutR+Tolerance*2,
                    $fn=Facets);
            }
    }
}

module MakeGuard()
{
    GR=CapOutR+GuardLength;
    difference()
    {
        union()
        {
            translate([-GuardWidth/2,0,0])
                cube([GuardWidth,GR,CapThickness-Tolerance]);
            translate([0,GR+GuardOutR-CapThickness,0])
                    cylinder(h=CapThickness*2,
                        r=GuardOutR,
                        $fn=Facets);
        }
        translate([0,GR+GuardOutR-CapThickness,-CapThickness/2])
                cylinder(h=CapThickness*3,
                    r=GuardIntR,
                    $fn=Facets);
    }
}

// Main
union()
    {
    MakeCap();
    if (UseGuard!=0) MakeGuard();
    }

```


[User]
I need a parametric OpenSCAD design for a tablet mount that attaches to a DJI Spark remote controller. The mount should consist of a "phone plate" that fits into the remote, spacers, and two pieces that form a "tab plate" that holds the tablet. The design should be customizable based on the dimensions of my tablet.

Here are the key features and parameters:

*   **Tablet Dimensions:** I need to specify the tablet's height, width, and thickness. Also, the corner rounding radius and the frame rounding from the display to the sides should be adjustable. I'd like to refer to these as `tabHeight`, `tabWidth`, `tabThick`, `tabRoundings`, and `tabFrameRoundings`.
*   **Phone Plate:** This is the part that fits into the remote controller. I'll need to specify its dimensions, as well, perhaps using parameters like `phonePlateHeight`, `phonePlateWidth`, and `phonePlateThickness`, though I'm open to you deciding on what makes the most sense. It should also have carved-out spaces, which I will need to specify with parameters (perhaps `carveHeight`, `carveWidth`, `carveThickness`).
*   **Spacers:** These connect the phone plate and the tab plate. The length of these spacers should be a parameter (`spacerLen`).
*   **Tab Plate:** This consists of two parts, a "left" and a "right" piece, that combine to hold the tablet. The left and right pieces should be mirror images of each other, or at least derived from a shared module.
*   **Assembly:** The final design should allow for the use of M3 screws for assembly, with appropriate screw holes and countersunk recesses.

The OpenSCAD design should allow me to select between generating only the Phone Plate, only the Spacers, only the Tab Plate Left piece, or only the Tab Plate Right piece, or some other combination if you think it would be valuable. This functionality can be achieved by use of a variable for a menu (`menu`). The tab plate should have a small text label on it with the name of the tab (parameter `tabName`).

I do not have any existing OpenSCAD code for you to refer to so, please generate all files from scratch. I do not need any libraries for this design besides the built-in OpenSCAD functions.

[Assistant]
TabMount.scad:
```
// ****************************************************************************
// Customizable Tablet Mount for DJI Spark remote controller
// Author: Peter Holzwarth
// ****************************************************************************
// https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language

$fn=50;

menu= "Phone Plate"; // [Phone Plate,Spacers,Tab Plate Left,Tab Plate Right]

// Name of the tab/ phablet - written on the upper plate
tabName= "Tab Name";
// Height (shorter side)
tabHeight= 126;
// Width (longer side)
tabWidth= 214;
// Thickness
tabThick= 8;
// Radius of corner roundings
tabRoundings= 9;
// Frame rounding from display to sides
tabFrameRoundings= 2;

// Length of the spacers between the plates
spacerLen= 60;

// make thingiverse customizer ignore these definitions:
function foo(x)= x;
// the phone dummy that goes into the Spark remote
phoneDummy= foo(["PhoneDummy", 80, 110, 8, 8, 2]);
// the inner plate of the tab holder - same x and y sizes as the phone dummy!
tabInnerPlate= foo(["tabInnerPlate", 80, 110, 7, 8, 2]);
// space to carve out of the phone dummy
phoneCarve= foo(["Carve out", 70, 50,12, 6, 2]);

// Samsung Galaxy Tab 8
tabGalaxyS8dot9= foo(["Galaxy Tab 8.9", 126, 214, 8, 9, 2]);
// Chuwi Hi 8 Pro
tabChuwiHi8Pro= foo(["Chuwi Hi8 Pro", 124, 212, 10, 9, 2]);

th= 2+0;

Menu();

// The menu for the thingiverse customizer
module Menu() {
    if (menu=="Phone Plate")  {
        PhonePlate(phoneDummy);
    } else if (menu=="Spacers")  {
        Spacers();
    } else if (menu=="Tab Plate Left")  {
        // TabPlateLeft(tabChuwiHi8Pro);
        TabPlateLeft([tabName,tabHeight,tabWidth,tabThick,
                      tabRoundings,tabFrameRoundings]);
    } else if (menu=="Tab Plate Right")  {
        // TabPlateRight(tabChuwiHi8Pro);
        TabPlateRight([tabName,tabHeight,tabWidth,tabThick,
                       tabRoundings,tabFrameRoundings]);
    }
}

// the complete upper plate - only for large 3D printers
// TabPlate(tabChuwiHi8Pro);

module TabPlateLeft(tab) {
    difference() {
        TabPlatePart(tab);
        tabLRSpaces(tab);
    }
}

module TabPlateRight(tab) {
    difference() {
        translate([tab[2]+2*th,tab[1]+2*th,0]) rotate([0,0,180]) TabPlatePart(tab);
        tabLRSpaces(tab);
    }
}

module tabLRSpaces(tab) {
    translate([(tab[2]+2*th-tabInnerPlate[2])/2,
               (tab[1]+2*th+tabInnerPlate[1])/2,tabInnerPlate[3]])
      rotate([180,0,0]) {
        TwoClipSpaces(tabInnerPlate);
        translate([tabInnerPlate[2],0,0]) mirror([1,0,0])
            TwoClipSpaces(tabInnerPlate, 2);
    }
    translate([th+tab[2]/2,th+tab[1]/2,tabInnerPlate[3]-0.5])
        linear_extrude(1)
            text(tab[0], size=8, valign="center", halign="center");
}

// ////////////////////////////////////////////////////////////////
// A half tab plate and brackets
module TabPlatePart(tab) {
    xm= (tab[2]+2*th)/2;
    ym= (tab[1]+2*th)/2;
    y0= (tab[1]+2*th-tabInnerPlate[1])/2;
    ye= (tab[1]+2*th+tabInnerPlate[1])/2;
    difference() {
        TabPlate2(tab);
        // cutting lines
        // segment 1, 3, 5
        translate([xm+5, -0.01, -0.01])
            cube([xm+0.01, tab[1]+2*th+0.02, 5.02+tab[3]+2*th]);
        // segment 2
        translate([xm-5.2, y0+7-0.2, -0.01])
            cube([11, 7+0.4, 5.02+tab[3]+2*th]);
        // segment 4
        translate([xm-5.2, ym-0.2, -0.01])
            cube([11, ye-ym-14+0.4, 5.02+tab[3]+2*th]);
        // segment 6
        translate([xm-5.2, ye-7-0.2, -0.01])
            cube([11, 7.21, 5.02+tab[3]+2*th]);
        // screw thread hole
        translate([xm, y0, tabInnerPlate[3]/2]) rotate([-90,0,0]) cylinder(r=1.6,h=tab[1]);
        // screw head hole
        translate([xm, y0-0.01, tabInnerPlate[3]/2]) rotate([-90,0,0]) cylinder(r=3,h=3);
    }
}

// ////////////////////////////////////////////////////////////////
// The complete tab plate and holder in one piece - only for large 3D printers
module TabPlate(tab) {
    difference() {
        // brackets and inner frame
        TabPlate2(tab);
        translate([(tab[2]+2*th-tabInnerPlate[2])/2,
                   (tab[1]+2*th+tabInnerPlate[1])/2,tabInnerPlate[3]])
            rotate([180,0,0]) {
            // left side clip spaces
            TwoClipSpaces(tabInnerPlate);
            // right side clip spaces
            translate([tabInnerPlate[2],0,0]) mirror([1,0,0]) TwoClipSpaces(tabInnerPlate);
        }
        translate([th+tab[2]/2,th+tab[1]/2,tabInnerPlate[3]-0.5])
            linear_extrude(1)
                text(tab[0], size=8, valign="center", halign="center");
    }
}

// ////////////////////////////////////////////////////////////////
// Tab brackets and inner plate without holes
module TabPlate2(tab) {
    // tab brackets
    TabHolder(tab);
    translate([(tab[2]+2*th-tabInnerPlate[2])/2,
           (tab[1]+2*th+tabInnerPlate[1])/2,tabInnerPlate[3]])
        rotate([180,0,0]) Smartphone(tabInnerPlate);
}

// ////////////////////////////////////////////////////////////////
// The phone-like plate that goes into the Spark remote controller
module PhonePlate(phone) {
    difference() {
        Smartphone(phone);
        TwoClipSpaces(phone);
        ThirdClipSpace(phone);
        translate([phone[2],0,0]) mirror([1,0,0]) {
            TwoClipSpaces(phone);
            ThirdClipSpace(phone);
        }
        translate([(phone[2]-phoneCarve[2])/2,(phone[1]-phoneCarve[1])/2,2])
            Smartphone(phone=phoneCarve);
    }
}

// ////////////////////////////////////////////////////////////////
// Draw space for two spacers
module TwoClipSpaces(phone) {
    translate([0,10+3,phone[3]/2]) {
        ClipSpace(10,15);
    }
    translate([0,phone[1]-10-3,phone[3]/2]) {
        ClipSpace(10,15);
    }
}

// ////////////////////////////////////////////////////////////////
// Draw space for third spacer
module ThirdClipSpace(phone) {
    fy= sqrt(spacerLen*spacerLen-(spacerLen-8)*(spacerLen-8));
    // echo(fy);
    alpha= asin((spacerLen-8)/spacerLen);
    // echo(alpha);
    translate([0,10+3+fy+8,phone[3]/2]) mirror([0,1,0]) {
        ClipSpace(16,15);
        ClipSpace(10,alpha);
    }
}

// ////////////////////////////////////////////////////////////////
// Draw space for one spacer, using a given angle
module ClipSpace(sh,alpha) {
    translate([sh+3,0,0]) {
        rotate([90,-alpha,90]) Spacer(outer=true);
        rotate([90,-90,90]) Spacer(outer=true);
    }
    translate([-0.01,0,0]) {
        rotate([0,90,0]) cylinder(r=1.55,h=25);
        rotate([0,90,0]) cylinder(r=3.2,h=3);
    }
}

// ////////////////////////////////////////////////////////////////
// A set of spacers
module Spacers() {
    for (a= [1:6]) {
        translate([0,10*(a-1),0]) Spacer();
    }
}

// ////////////////////////////////////////////////////////////////
// A single spacer
module Spacer(outer= false) {
    sc= outer ? 1.05 : 1.0;
    translate([0,0,-sc*6/2]) {
        difference() {
            hull() {
                cylinder(r=sc*4,h=sc*6);
                translate([spacerLen,0,0]) cylinder(r=sc*4,h=sc*6);
            }
            if (!outer) {
                translate([0,0,-0.01]) {
                    cylinder(r=1.7,h=6.02);
                    translate([spacerLen,0,0]) cylinder(r=1.7,h=6.02);
                }
            }
        }
    }
}

// ////////////////////////////////////////////////////////////////
// Draw the model of a smartphone
module Smartphone(phone) {
    hull() {
        translate([phone[2]-phone[4],phone[1]-phone[4],0])
            qwheel(phone[4],phone[3],phone[5]);
        translate([phone[2]-phone[4],phone[4],0]) rotate([0,0,-90])
            qwheel(phone[4],phone[3],phone[5]);
        translate([phone[4],phone[4],0]) rotate([0,0,180])
            qwheel(phone[4],phone[3],phone[5]);
        translate([phone[4],phone[1]-phone[4],0]) rotate([0,0,90])
            qwheel(phone[4],phone[3],phone[5]);
    }
}

// ////////////////////////////////////////////////////////////////
// a quater wheel with an outer wheel radius, an outer height and a rounding radius
module qwheel(wr, h, rr, center=false) {
    translate([0,0,center?-h/2:0]) {
        hull() {
            translate([0,0,h-rr]) qwheelBike(wr, rr);
            translate([0,0,rr]) qwheelBike(wr, rr);
        }
    }
}

// doesn't work in thingiverse customizer
module qwheelBike2016(wr, rr) {
    rotate_extrude(convexity= 2,angle=90) translate([wr-rr, 0, 0]) circle(r=rr);
}

module qwheelBike(wr, rr) {
    difference() {
        rotate_extrude(convexity= 2) translate([wr-rr, 0, 0]) circle(r=rr);
        translate([-wr-0.01,-wr-0.01,-rr-0.01]) cube([wr+0.02,2*wr+0.02,2*rr+0.02]);
        translate([-0.01,-wr-0.01,-rr-0.01]) cube([wr+0.02,wr+0.01,2*rr+0.02]);
    }
}

// ////////////
// Holder.scad

bpThick= 5;

module TabHolder(phone=tabGalaxyS8dot9) {
    // the four corners
    HolderCorner(phone);
    translate([phone[2]+2*th,0,0]) rotate([0,0,90])
        HolderCorner(phone);
    translate([0,phone[1]+2*th,0]) rotate([0,0,-90])
        HolderCorner(phone);
    translate([phone[2]+2*th,phone[1]+2*th,0]) rotate([0,0,180])
        HolderCorner(phone);
    difference() {
        union() {
            // the oval rings
            translate([th+5,th+phone[1]/2,0]) rotate([0,0,-90])
                halfOvalRing(phone[1]/2,phone[2]/2,bpThick+th);
            translate([th+phone[2]-5,th+phone[1]/2,0]) rotate([0,0,90])
                halfOvalRing(phone[1]/2,phone[2]/2,bpThick+th);
        }
    }
}

// half an oval ring - a filled ring with a concave inner ring cut out
// hack for Thingiverse customizer, which uses a too old version of OpenSCAD
module halfOvalRing(w, d, h) {
    difference() {
        ovalRing(w, d, h);
        translate([-w-0.01,-d-0.01,-0.01]) cube([2*w+0.02,d+0.02,h+0.02]);
    }
}

// an oval ring - a filled ring with a concave inner ring cut out
module ovalRing(w, d, h, angle=360) {
    difference() {
        scale([1,d/w,1]) fring(w-h/2, h, angle);
        translate([-0.01,-0.01,-0.01]) scale([1,(d-4)/(w-4),1]) difference() {
            cylinderA(w-4, h+0.02, angle);
            translate([0,0,4/2]) ring(w-4, h, angle);
        }
    }
}

module HolderCorner(phone=tabGalaxyS8dot9) {
    difference() {
        hull() {
            // corner
            translate([phone[4]+th,phone[4]+th,0]) rotate([0,0,180])
                qwheel(phone[4]+th,phone[3]+2*th+bpThick,phone[5]);
            // x longhole
            translate([phone[4]+th,phone[5],phone[3]+phone[5]+bpThick])
                rotate([0,90,0]) longHole(radius=phone[5],
                                   length=phone[3]-2*phone[5]+2*th+bpThick, height=5);
            // y longhole
            translate([phone[5],phone[4]+th,phone[3]+phone[5]+bpThick])
                rotate([0,90,90]) longHole(radius=phone[5],
                                    length=phone[3]-2*phone[5]+2*th+bpThick,height=5);
        }
        translate([th,th,th+bpThick]) Smartphone(phone);
    }
}

// ////////////////////////////////////////////////////////////////
// rounded button
// r= outer radius
// h= height
// rr= upper rounding
module buttonRound(r, h, rr) {
    hull() {
        translate([0,0,h-rr]) wheelBike(r, rr);
        cylinder(r=r,h=h-rr);
    }
}

// ////////////////////////////////////////////////////////////////
// a pipe - a hollow cylinder
module pipe(outerRadius, thickness, height) {
    difference() {
        cylinder(h=height, r=outerRadius);
        translate([0, 0, -0.1])
            cylinder(h=height+0.2, r=outerRadius-thickness);
    }
}

// ////////////////////////////////////////////////////////////////
// a ring with a radius and a thickness
// else same as wheelBike
// r - radius
// th - thickness
// angle - 360 or part of ring
module ring(r, th, angle) {
    rotate_extrude(convexity= 4, angle= angle, $fn= 100)
        translate([r, 0, 0])
            circle(r = th/2, $fn = 100);
}

// filled ring
module fring(r, th, angle=360) {
    rotate_extrude(convexity= 4, angle= angle, $fn= 100) {
        translate([r, th/2, 0])
            circle(r = th/2, $fn = 100);
        square([r,th]);
    }
}

// a pie piece - a part of a cylinder
module cylinderA(r, h, angle) {
    rotate_extrude(convexity= 4, angle= angle, $fn= 100)
        square([r,h]);
}

// generic long hole
module longHole(radius, length, height) {
    hull() {
        cylinder(r=radius, h=height);
        translate([length,0,0]) cylinder(r=radius, h=height);
    }
}

```


[User]
I need a parametric OpenSCAD design for a bed slat retainer. I've got some specific dimensions and features in mind. It's essentially a bracket that holds a bed slat, with pegs that insert into holes in the bed frame. The retainer has a curved back and a straight front, with side walls and a rear wall. Here are the details:

**General Shape and Dimensions:**

*   The overall shape is a partial cylinder, with a straight front edge.
*   The *outer* width of the retainer is adjustable via a `width` parameter (default 56.6mm).
*   The *depth* from the front edge to the start of the curve is adjustable via a `depth` parameter (default 24mm).
*   The curve is based on a diameter set by `arc_diameter` (default 137mm), and the chord of the curve is adjustable using `chord_offset` (default 6.1mm).
*   The total height of the retainer will be determined by the bottom panel, slat gap and top panel thicknesses
*   The bottom and top panels are defined by `bottom_panel_thickness` (default 1.5mm) and `top_panel_thickness` (default 1mm), respectively.
*   The walls are defined by `wall_thickness` (default 1mm).
*   The gap where the slat sits has a `slat_height` parameter (default 9mm).
*   There are two circular holes in the rear wall for air escape defined by `rear_hole` (default 4.4mm), with centres separated by `rear_hole_centre_distance` (default 29mm).
*   There are two pegs that protrude from the top of the retainer to attach to the bed frame.

**Peg Details:**

*   The pegs have a main cylinder part with a diameter set by `peg_main_dia` (default 6.2mm) and height by `peg_main_height` (default 4.5mm).
*   The pegs also have a cone section with a top diameter defined by `peg_cone_top_dia` (default 3mm), a bottom diameter defined by `peg_cone_bottom_dia` (default 8.2mm), and a height defined by `peg_cone_height` (default 8mm).
*   The pegs also include a compression slot, that's a rectangular cutout, defined by `peg_cutout_width` (default 2mm), `peg_cutout_height` (default 4.6mm) and `peg_cutout_offset` (default 2.5mm, offset *above* the top panel).
*   The peg centre is offset from the rear wall using `peg_from_rear` (default 12mm).
*   The distance between the two pegs' centres is defined by `peg_centre_distance` (default 36.5mm).

**Other Details:**

*   I'd like to be able to adjust all of these dimensions via parameters in the OpenSCAD file.
*   For printing, the retainer will probably be printed on its side, with pegs being horizontal, but this is not critical to the design.
*   I expect the design to be a single part.

Could you generate the necessary OpenSCAD code for me? I don't think I'll need any particular library for this, but let me know if you think it would be helpful.

[Assistant]
bed-slat-retainer.scad:
```
// Bed slat retainer
// RJL Oct 2017

$fs=0.5+0; // The plus hides from Customizer.
$fa=1+0;
manifold = 0.01+0;

/* [Retainer Values] */
// Width of retainer (exterior).
width = 56.6;
// Depth, not counting arc area
depth = 24;
// Dia. of arc of top/bottom; Changing this will require changing chord_offset and vice versa.
arc_diameter = 137;
// Distance from chord of arc to curve. Use 0 for straight front.
chord_offset = 6.1;
// Top panel has the pegs (but in use is actually the bottom panel!).
top_panel_thickness = 1;
bottom_panel_thickness = 1.5;
// Other walls
wall_thickness = 1;
// Gap where slat goes.
slat_height = 9;
// Rear hole size, presumably for air escape?
rear_hole = 4.4;
// Distance between rear hole centres.
rear_hole_centre_distance = 29;

/* [Pegs} */
// Distance of centre of peg from rear
peg_from_rear = 12;
// Distance between peg centres.
peg_centre_distance = 36.5;
peg_main_dia = 6.2;
peg_main_height = 4.5;
peg_cone_top_dia = 3;
peg_cone_bottom_dia = 8.2;
peg_cone_height = 8;
// The compression slot (cutout) in the pegs.
peg_cutout_width = 2;
peg_cutout_height = 4.6;
peg_cutout_offset = 2.5;    // Bottom of cutout above top panel
// -- End of parameters ---
total_height = bottom_panel_thickness+slat_height+top_panel_thickness;
echo ("Total height: ", total_height);

total_depth = depth+chord_offset; // Just for reference; not used in calculations.
echo ("Total depth: ", total_depth);

// Print on edge ("rear wall") with supports (pegs being horizontal), so rotate in slicer (unless bridging works well).

// --- Modules ---

module panel (thickness) {
    difference(){
    translate ([arc_diameter/2, arc_diameter/2, 0]) cylinder(d=arc_diameter, h=thickness);
    translate ([0, chord_offset , -manifold]) cube([arc_diameter, arc_diameter, thickness+manifold*2]);
    }
    translate ([arc_diameter/2-width/2, chord_offset , 0]) cube ([width, depth, thickness]);
}

module peg () {

    difference () {
    union() {
    cylinder(d=peg_main_dia, h=peg_main_height);
    translate([0, 0, peg_main_height]) cylinder(d1=peg_cone_bottom_dia, d2=peg_cone_top_dia, h=peg_cone_height);
    }
     translate([-peg_cutout_width/2, -peg_cone_bottom_dia/2, peg_cutout_offset]) cube([peg_cutout_width, peg_cone_bottom_dia, peg_cutout_height]);
  }
}

// --- Execute ---

// Bottom panel
panel(bottom_panel_thickness);

// Top panel
translate([0, 0, bottom_panel_thickness+slat_height]) panel(top_panel_thickness);

// Left wall
translate([arc_diameter/2-width/2, chord_offset, bottom_panel_thickness]) cube ([wall_thickness, depth, slat_height]);

// Right wall
translate([arc_diameter/2+width/2-wall_thickness, chord_offset, bottom_panel_thickness]) cube ([wall_thickness, depth, slat_height]);

// Rear wall
difference() {
translate([arc_diameter/2-width/2, chord_offset+depth-wall_thickness, bottom_panel_thickness]) cube ([width, wall_thickness, slat_height]);
//  Rear holes
translate([ arc_diameter/2-rear_hole/2-rear_hole_centre_distance/2, chord_offset+depth-wall_thickness, total_height/2-rear_hole/2]) cube([rear_hole, wall_thickness*2,rear_hole]);
translate([ arc_diameter/2-rear_hole/2+rear_hole_centre_distance/2, chord_offset+depth-wall_thickness, total_height/2-rear_hole/2]) cube([rear_hole, wall_thickness*2,rear_hole]);
}

translate([arc_diameter/2-peg_centre_distance/2, chord_offset+depth-peg_from_rear, total_height]) peg();
translate([arc_diameter/2+peg_centre_distance/2, chord_offset+depth-peg_from_rear, total_height]) peg();

```


[User]
I need a way to generate involute spur gears in OpenSCAD, but I'd like to define them based on their *modulus* instead of pitch.  I've seen some code that does this but I'd rather work from a clean parametric file of my own. Modulus, as I understand it, is the ratio of tooth count to diameter, making prototyping easier.  For example, a gear with a modulus of 1 will have a diameter matching its tooth count.

Specifically, I need a module that will allow me to specify:

*   **Modulus:** This is the key parameter, the ratio of tooth count to diameter.
*   **Tooth Count:**  The number of teeth the gear has.
*   **Gear Thickness:** The thickness of the gear along its axis.
*   **Center Hole Diameter:** The diameter of the central bore for a shaft.
*   **Gear Twist:** The amount of twist (in degrees) from the bottom of the gear to the top, allowing helical gears.
*   **Hidden Teeth:** The number of teeth that are omitted, in order to produce a partial gear section instead of a complete circle.
*   **Tooth Pressure Angle:** The angle that determines the shape of the tooth profile. Standard values are around 14.5, 20, or 28 degrees.
*   **Rim Size:** The radial thickness of the material supporting the gear teeth (between the root circle and the main gear body)
*   **Hub Diameter:** The diameter of the central hub.
*   **Hub Extension:** The amount the hub extends beyond the gear thickness.
*   **Number of Spokes:** The number of spokes connecting the hub to the rim of the gear.
*  **Keyway Height:** The height of a rectangular keyway, extending radially outward from the center hole.
*  **Keyway Width:** The width of the rectangular keyway.
*  **Center Bore Shape:** An option to select the shape of the central bore of the gear: options are round, hexagonal, and square; note that the specified diameter is for corner-to-corner dimensions.

The module should create the involute gear profile, and include the center bore, hub, and spokes as well. I'd like the module to handle cases where `Hidden_Teeth` is greater than the total number of teeth (by setting `Hidden_Teeth` to zero), and to ensure the number of teeth is a whole number.
I'm looking for a clean, parametric approach. I don't expect to be able to make helical gears with this module.

I'm also looking for helper functions like these to calculate the involute shape:

*   `polar(r, theta)` to convert polar to Cartesian coordinates.
*   `iang(r1, r2)` for calculating the involute angle.
*   `q7(f, r, b, r2, t, s)` and `q6(b, s, t, d)` helper functions for generating the tooth profile.

Please provide the OpenSCAD file or files. I do not have any custom libraries.

[Assistant]
ModulusGear.scad:
```
///////////////////////////////////////////////////////////////////////////////////////////
// Public Domain Modulus Gear OpenSCAD code
// version 1.0
// This code defines a gear based on its modulus rather than pitch in mm,
// Modulus is the ration of tooth count:diameter, effectively the pitch of the gear is
// modulus * pi
// ie, a gear of mod=1 has a tooth pitch of 3.1514mm.
//
// The advantage of using modulus rather than pitch to define a gear is that it is very easy to
// prototype - for example, a gear of mod = 1 will have a diamter that matches the tooth count.
// with mod = 2, radius = tooth count.
//
// This code is based on the poblic domain Parametric Involute Spur Gear code by Leemon Baird
//found at http://www.thingiverse.com/thing:5505
//////////////////////////////////////////////////////////////////////////////////////////////
//
// Since the original file was public domain, this file is public domain as well.
// Use it for any purpose, including commercial applications.  Attribution would be nice,
// but is not required.  There is no warranty of any kind, including its correctness,
// usefulness, or safety.
//
//////////////////////////////////////////////////////////////////////////////////////////////
// An involute spur gear based on Modulus, with reasonable defaults for all the parameters.
// Normally, you should just choose the first 4 parameters, and let the rest be default values.
// Meshing gears must match mod, pressure_angle, and twist. Geras should be separated by the
// sum of their radii, where gear radius = mod * tooth count.
//////////////////////////////////////////////////////////////////////////////////////////////

Modulus				= 1		;//[0.1:0.1:3]
Tooth_Count 		= 20	;//[6:1:200]
Gear_Thickness 		= 6 	;//[2:0.1:30]
Center_Hole_Diamter	= 4		;//[0.1:0.1:20]
Gear_Twist			= 0  	;//[0:0.5:360]
Hidden_Teeth		= 0    	;//[0:1:20]
Tooth_Pressure_Angle= 20	;//[14.5, 20, 28,]
Rim_Size 			= 5		;//[1:0.1:10]
Hub_Diamter			= 12	;//[6:0.5:40]
Hub_Extension 		= 1 	;//[0:0.1:30]
Number_of_Spokes 	= 4		;//[1:1:10]
Keyway_Height		= 0		;//[0:0.1:10]
Keyway_width		= 0		;//[0:0.1:8]
Center_Bore_Shape	= 100	;//[100:round, 6:Hexagon, 4:square]

// Call the modgear module to create the gear using the parameters define above
// If using this as a library, you can delete this, or put a * before modgear to hide it.
modgear (
	mod    			= Modulus,    //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
	number_of_teeth = Tooth_Count,   //total number of teeth around the entire perimeter
	thickness       = Gear_Thickness,    //thickness of gear in mm
	hole_diameter   = Center_Hole_Diamter,    //diameter of the hole in the center, in mm
	twist           = Gear_Twist,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	teeth_to_hide   = Hidden_Teeth,    //number of teeth to delete to make this only a fraction of a circle
	pressure_angle  = Tooth_Pressure_Angle,   //Controls how straight or bulged the tooth sides are. In degrees.
	clearance       = 0.0,  //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
	backlash        = 0.0,  //gap between two meshing teeth, in the direction along the circumference of the pitch circle
	rim				= Rim_Size , 	//thickness of rim supporting the gear teeth
	hub_diameter	= Hub_Diamter,	//diameter of hub
	hub_extension	= Hub_Extension , 	//extension of hub beyond gear thickness
	spoke_num 		= Number_of_Spokes ,	//number of spokes
	keyway			= [Keyway_Height,Keyway_width],
    hole_polygon    = Center_Bore_Shape

);

//these 4 functions are used by modgear
function polar(r,theta)   = r*[sin(theta), cos(theta)];                            //convert polar to cartesian coordinates
function iang(r1,r2)      = sqrt((r2/r1)*(r2/r1) - 1)/3.1415926*180 - acos(r1/r2); //unwind a string this many degrees to go from radius r1 to radius r2
function q7(f,r,b,r2,t,s) = q6(b,s,t,(1-f)*max(b,r)+f*r2);                         //radius a fraction f up the curved side of the tooth
function q6(b,s,t,d)      = polar(d,s*(iang(b,d)+t));                              //point at radius d on the involute curve

//*****************************************************

module modgear (
	mod    			= 1,    //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
	number_of_teeth = 15,   //total number of teeth around the entire perimeter
	thickness       = 6,    //thickness of gear in mm
	hole_diameter   = 3,    //diameter of the hole in the center, in mm
	twist           = 0,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	teeth_to_hide   = 0,    //number of teeth to delete to make this only a fraction of a circle
	pressure_angle  = 20,   //Controls how straight or bulged the tooth sides are. In degrees.
	clearance       = 0.0,  //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
	backlash        = 0.0,  //gap between two meshing teeth, in the direction along the circumference of the pitch circle
	rim				= 5 , 	//thickness of rim supporting the gear teeth
	hub_diameter	= 6,	//diameter of hub
	hub_extension	= 0 , 	//extension of hub beyond gear thickness
	spoke_num 		= 4 ,	//number of spokes
	keyway			= [0,0],
    hole_polygon    = 100

) {
	pi = 3.141592653589;
	mm_per_tooth = mod * pi;
	number_of_teeth = floor (abs(number_of_teeth));
	keycut = [ keyway[0] +hole_diameter/2 , keyway[1], 2*(thickness + hub_extension)];
	if (teeth_to_hide > number_of_teeth) {teeth_to_hide = 0;}
	p  = mod * number_of_teeth/ 2; //radius of pitch circle
	c  = p + mod - clearance ;     //radius of outer circle
	b  = p*cos(pressure_angle);    //radius of base circle
	r  = p-(c-p)-clearance ;       //radius of root circle
	t  = mm_per_tooth/2-backlash/2;                //tooth thickness at pitch circle
	k  = -iang(b, p) - t/2/p/pi*180; {             //angle to where involute meets base circle on each side of tooth
		difference(){

            union() {
				for (i = [0:number_of_teeth - teeth_to_hide-1] )
					rotate([0,0,i*360/number_of_teeth])
						linear_extrude(height = thickness,  convexity = 10, twist = twist)
							polygon(
								points=[
									//[0, -hole_diameter/10],
									polar(r, -181/number_of_teeth),
									polar(r, r<b ? k : -180/number_of_teeth),
									q7(0/5,r,b,c,k, 1),q7(1/5,r,b,c,k, 1),q7(2/5,r,b,c,k, 1),q7(3/5,r,b,c,k, 1),q7(4/5,r,b,c,k, 1),q7(5/5,r,b,c,k, 1),
									q7(5/5,r,b,c,k,-1),q7(4/5,r,b,c,k,-1),q7(3/5,r,b,c,k,-1),q7(2/5,r,b,c,k,-1),q7(1/5,r,b,c,k,-1),q7(0/5,r,b,c,k,-1),
									polar(r, r<b ? -k : 180/number_of_teeth),
									polar(r, 181/number_of_teeth)
								]//,
								//paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]]
							);
				difference(){
					cylinder(h=thickness, r=r,  $fn=60);
					translate ([0,0,-2]) cylinder(h=2*thickness+1, r=r-rim,  $fn=60);
				}
				cylinder (h=thickness + hub_extension,r=hub_diameter/2,$fn=60);		// hub

				for (i = [0:spoke_num - 1] )		//position the spokes
					rotate([0,0,i*360/spoke_num])
						translate ([-rim/2,0,0])
							cube ([rim,r-2,thickness]);

				//translate ([0,0,thickness/2]){
				//	cube ([rim,2*r-2,thickness],center=true);
				//	cube ([2*r-2,rim,thickness],center=true);
				//	}

			}
			translate ([0,0,-2])cylinder (h=2*(thickness + hub_extension),r=hole_diameter/2,$fn=hole_polygon);
			translate ([hole_diameter/2,0,(thickness + hub_extension-1)]) cube (keycut, center = true);
		}

	}
};

```


[User]
I need an OpenSCAD design for a wire guiding aperture, like a bezel with holes for wires to pass through. It should be parametric so I can customize it for different numbers of spools and different wire gauges.

Here's how I envision it:

*   **Basic Shape:** A rectangular plate with a series of evenly spaced holes.
*   **Parameters:**
    *   `bezel_height`:  The height of the rectangular plate.
    *   `spools`: The number of wire spools the aperture will accommodate.
    *   `spool_width`: The width allocated to each spool, and thus the spacing between the holes.
    *   `wire_hole_diameter`: The diameter of each wire hole.
    *   `rim`: The width of the solid border (rim) on each side of the plate. These rims are for mounting.
    *   `screw_hole_diameter`: The diameter of the mounting holes in the rims. There will be one on each side.
    *  `gap`:  An extra gap added to the total width, allowing for some free movement.

*   **Functionality:** The wire holes should be centered along the length of the plate, spaced according to `spool_width`. The mounting holes should be centered within the rim on each end of the plate.

I'd like the design to be such that I can easily adjust these parameters, and I don't want to have to calculate the total length myself. It should be handled internally based on `rim`, `spools`, `spool_width`, and `gap`.

I'm not using any specific libraries right now, so just standard OpenSCAD functionality should be fine.

[Assistant]
Wire_Guiding_Aperture.scad:
```
/* **********************
// [EN] Parametric Guiding Bezel for Wire Spool Holder
// [DE] Kabelführung für Spulenhalter (parametrierbar)
//
// www.thingiverse.com/thing:2573424
// CC-BY-SA October 2017 by ohuf@Thingiverse
// Version 2 - 2017-09-07
//
//
// (English text below...)
//
// [DE] Für meinen Spulenhalter (für Elekronikdrähte)
// Habe ich eine Kabelführung in Form einer Lochblende benöigt.
// Die Blende sitzt vor den Spulen (Siehe Fotos). Sie erleichtert das Abrollen
// und hindert die Kabel zurück zu rollen.
//
// Das Design in OpenSCAD ist parametrierbar.
//
//
// -----
//
//
// For my wire spool holder I needed a wire guidance. I designed this "pinhole aperture".
// the bezel sits in front of the wire spools.
// It facilitates grabbing the wire and keeps the it from slipping back.
//
// The OpenSCAD design is parametric: customize your own!!
//
//
// Enjoy, have fun remixing and let me know when you've made one, and what for!
//
//
//
// oli@huf.org 2017-Oct-07
// License: CC-BY-SA
// read all about it here: http://creativecommons.org/licenses/by-sa/4.0/
// ********************** */

/* Size */
// Height of the Bezel || Höhe der Blende
bezel_height = 20;
// Number of spools || Anzahl der Spulen
spools=11; // [1:50]
// Width of each of the spools || Breite der einzelnen Spulen
spool_width=15.0;
// Size of the cable holes. You can widen those later on with a tiny knife, a drill or sharp pliers || Größe der Kabeldurchlässe. Diese können später mit einem Cutter, Bohrer oder einer Pinzette geweitet werden.
wire_hole_diameter = 2;
// a fixed rim on both sides for the screw holes || Rechter und linker Rand für die Befestigungslöcher
rim=9.0;
// width of the screw holes on the rim || Weite der Befestigungslöcher auf dem Rand rechts und links
screw_hole_diameter = 4;
// An additional gap to allow the spools to run freely or to cater for variances in design || Zusätzlicher Abstand, um den Spulen Bewegungsspielraum zu geben, oder um Toleranzen im Design zu kompensieren.
gap=2; //

/* [Hidden] */
$fn=50;
totalx=2*rim + spools*spool_width + gap;
thickness=2;
ix=0.005;

difference(){
cube([totalx, bezel_height, thickness]);
union(){
	translate([rim/2, bezel_height/2, 0])
		cylinder(d=screw_hole_diameter, h=thickness+ix);	// linkes Befestigungsloch
	translate([rim+gap/2-spool_width/2, bezel_height/2, 0])
	for(i=[1:spools]){
		translate([i*15, 0, 0])
		cylinder(d=wire_hole_diameter, h=thickness+ix);
	}
	translate([totalx - rim/2, bezel_height/2, 0])
		cylinder(d=screw_hole_diameter, h=thickness+ix);	// rechtes Befestigungsloch
}
}

```


[User]
I need an OpenSCAD design for a customizable wall plate frame, like for light switches or power sockets. I want to be able to adjust the outer dimensions (width, depth, height) and the rounding radius of the outer edges. Similarly, I need to control the inner dimensions (width, depth, height) and the inner rounding radius.

Also, I want a few options for what's inside the frame. It should be able to be just an empty space, a solid filled plate, or have two different types of "fixing" structures. The first fixing style should create a sort of recessed lip around the inner space, and the second should be a raised lip around the perimeter of the inner space. For both fixing types I also need to be able to specify the width of the fixing.

The design should be fully parametric, so I can tweak all these parameters through customizer. I don't want to have to mess with the code directly. Also, the design should be created from scratch, I don't need a code review or edits to existing code.

Can you do that?

[Assistant]
Frame_v1.3.scad:
```
// ****************************************************************************
// Socket Frame for power sockets, light switches, etc.
// Author: Peter Holzwarth
// ****************************************************************************
// https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language

/* [Outer dimensions] */
// Width of the frame
outerWidth= 68;
// Height of the frame
outerDepth= 68;
// Radius of the rounding
outerRounding= 5;
// Height at the outside
outerHeight= 6;
// Upper side edge rounding
rr2= 2; // [0:5]

/* [Inner Dimensions] */
// Width of the inner space
innerWidth= 50;
// Height of the inner space
innerDepth= 50;
// Radius of the rounding of the inner space
innerRounding=2;
// Height at the inside
innerHeight= 8;
// What to show inside the frame
innerStyle="Fixing 1"; // [Fixing 1,Fixing 2,Empty,Filled]
// Width of inner fixing
innerFixingWidth=0.5;

$fn=100;

Frame();

module Frame() {
    difference() {
        hull() {
            // outer frame
            Button(outerWidth, outerDepth, outerHeight, outerRounding);
            // inner frame
            translate([(outerWidth-innerWidth)/2,(outerDepth-innerDepth)/2,0])
                RoundedBox1Light(innerWidth, innerDepth, innerHeight, innerRounding+0.01);
        }
        if (innerStyle!="Filled") {
            translate([(outerWidth-innerWidth)/2,(outerDepth-innerDepth)/2,-0.01])
                RoundedBox1(innerWidth, innerDepth, innerHeight+5, innerRounding+0.01);
        }
    }
    if (innerStyle=="Fixing 1") {
        translate([(outerWidth-innerWidth)/2-2,(outerDepth-innerDepth)/2-2,-0.01])
            difference() {
                cube([innerWidth+4,innerDepth+4,1]);
                translate([2+innerFixingWidth,2+innerFixingWidth,-0.01])
                    ChamferedCube([innerWidth-2*innerFixingWidth,
                            innerDepth-2*innerFixingWidth,1.1],innerRounding+2);
            }
    } else if (innerStyle=="Fixing 2") {
        translate([(outerWidth-innerWidth)/2,(outerDepth-innerDepth)/2,0])
            difference() {
                RoundedBox1(innerWidth, innerDepth, 1, 0.1);
                translate([innerFixingWidth,innerFixingWidth,-0.01])
                    RoundedBox1(innerWidth-2*innerFixingWidth,
                            innerDepth-2*innerFixingWidth, 1.02, innerRounding);
            }
    }
}

// /////////////////////////////////////////////////////////////////////
// Box with rounded corners at its upper side
module Button(w, d, h, rr) {
    hull() {
        // upperside shape
        translate([0,0,h-2*rr2]) RoundedBox2(w, d, rr);
        // bottom shape
        RoundedBox1(w, d, 1, rr);
    }
}

// /////////////////////////////////////////////////////////////////////
// a box with rounded corners at its sides
// used as the bottom shape
module RoundedBox1(w, d, h, rr) {
    linear_extrude(h) hull() {
        translate([rr,rr,0]) circle(r=rr);
        translate([w-rr,rr,0]) circle(r=rr);
        translate([rr,d-rr,0]) circle(r=rr);
        translate([w-rr,d-rr,0]) circle(r=rr);
    }
}

// only the circles of the rounded box, simplifies hull operation.
module RoundedBox1Light(w, d, h, rr) {
    translate([rr,rr,h-0.01]) cylinder(r=rr, h= 0.01);
    translate([w-rr,rr,h-0.01]) cylinder(r=rr, h= 0.01);
    translate([rr,d-rr,h-0.01]) cylinder(r=rr, h= 0.01);
    translate([w-rr,d-rr,h-0.01]) cylinder(r=rr, h= 0.01);
}

// /////////////////////////////////////////////////////////////////////
// a box with all rounded corners
// used as the top shape
module RoundedBox2(w, d, rr) {
    hull() {
        translate([rr,rr,rr2]) Wheel(rr, rr2);
        translate([w-rr,rr,rr2]) Wheel(rr, rr2);
        translate([rr,d-rr,rr2]) Wheel(rr, rr2);
        translate([w-rr,d-rr,rr2]) Wheel(rr, rr2);
    }
}

// /////////////////////////////////////////////////////////////////////
// a wheel
// different cases to avoid errors in rotate_extrude
module Wheel(wr, rr, seg= 0) {
    if (wr >= 2*rr) {
        rotate_extrude(convexity = 4) translate([wr-rr, 0, 0]) circle(r=rr);
        translate([0,0,-rr]) cylinder(r= wr-rr, h= rr*2);
    } else if (wr > rr) {
		hull() {
			for (a= [0:15:90]) {
				if (seg==0 || seg == 4) translate([(wr-rr)*sin(a), (wr-rr)*cos(a), 0]) sphere(r=rr, $fn=10);
				if (seg==0 || seg == 3) translate([-(wr-rr)*sin(a), (wr-rr)*cos(a), 0]) sphere(r=rr, $fn=10);
				if (seg==0 || seg == 2) translate([(wr-rr)*sin(a), -(wr-rr)*cos(a), 0]) sphere(r=rr, $fn=10);
				if (seg==0 || seg == 1) translate([-(wr-rr)*sin(a), -(wr-rr)*cos(a), 0]) sphere(r=rr, $fn=10);
			}
		}
        translate([0,0,-rr]) cylinder(r= wr-rr, h= rr*2);
    } else {
        sphere(r=rr);
    }
}

// /////////////////////////////////////////////////////////////////////
// a cube with chamfered edges
// dims - dimensions - x, y, z
// ch - chamfering
module ChamferedCube(dims, c) {
    linear_extrude(dims[2])
        polygon([[c,0],[dims[0]-c,0],[dims[0],c],[dims[0],dims[1]-c],
                [dims[0]-c,dims[1]],[c,dims[1]],[0,dims[1]-c],[0,c]],
                [[0,1,2,3,4,5,6,7]], convexity = 4);
}

```


[User]
I'd like to create a bed leveling test print for my 3D printer, and I need an OpenSCAD design for it.  It should print a series of lines at different angles, radiating out from a center point, and each angle should have lines of varying widths. This helps me see if the nozzle is too close or too far from the bed, and if one side of the bed is higher or lower than another.

Here's a breakdown of the parameters I need:

*   **`layerHeight`**:  This is the height of each layer, obviously. I need to be able to set this in millimeters.
*   **`layerWidth`**: This is the width of my nozzle's extrusion. It should also be adjustable in millimeters.
*   **`testLength`**:  The length of each test line. I'd like to control this length in millimeters too.
*  **`distanceFromCenter`**: The distance from the center of the print to the center point of the test lines.  This should also be in mm.
*   **`numberOfSizes`**:  The number of different line widths to print at each angle.

The design should print lines at several angles around a circle.  I think starting at 0 degrees and going up by 45 degree increments is good, so that would be lines at 0, 45, 90, 135, 180, 225, 270, and 315 degrees.

For each of these angles, the lines should start from a distance of `distanceFromCenter` from the center. The lines will be made up of a primary line, followed by progressively smaller lines on either side of that primary line. The largest should be layerWidth * numberOfSizes, and then each smaller line is reduced by a factor of layerWidth. The smallest will be layerWidth. So, if `numberOfSizes` is 4, you'd have 9 lines: one at 4 * layerWidth, and 4 more on each side: 3 * layerWidth, 2 * layerWidth, 1 * layerWidth, and 1 * layerWidth, 2 * layerWidth, 3 * layerWidth. These lines should be positioned directly after each other, in both directions from the first line.

The lines themselves should be rectangular with the height set by `layerHeight` and width controlled by the formula as described before, and a length of `testLength`.

I'd like the code to be well-organized and easy to understand, with descriptive comments where appropriate. I don't need any specific libraries, I think. It should be doable with just OpenSCAD's built-in functions.

[Assistant]
Leveling.scad:
```
// Layer Height
layerHeight = 0.2; // [0.01:0.01:2]

// Width of your nozzle
layerWidth = 0.4; // [0.01:0.01:2]

// Length of test lines
testLength = 10; // [2:100]

// Mid-point Radius of test lines
distanceFromCenter = 40; //

// Number of test sizes per angle
numberOfSizes = 4;

// Outer Lines
for(i = [0:45:315]) {
    linear_extrude(layerHeight)
    rotate([0, 0, i])
    translate([0, distanceFromCenter])
    outerLine();
}

module outerLine() {

    lineSeg(layerWidth * numberOfSizes, testLength);

    c = numberOfSizes + 1;
    for (j = [2:c]) {
        ij = c - j;

        translate([layerWidth * (j - 1) * c, 0])
        lineSeg(layerWidth * ij, testLength);
        translate([-layerWidth * (j - 1) * c, 0])
        lineSeg(layerWidth * ij, testLength);
    }
}

module lineSeg(w, l) {
    translate([-(w/2), 0])
    square([w, l]);
}
```