[User]
I'm looking to generate a variety of fan covers using OpenSCAD, and I want them to be highly customizable. I really liked the look of the "Customizable Fan Cover" on Thingiverse by mightynozzle, but I'd like to request a version of it that I can use as a base for my own designs.

Here's what I need:

1.  **Fan Size Presets:** I'd like to be able to select from a list of common fan sizes (25mm, 30mm, 40mm, 50mm, 60mm, 70mm, 80mm, 92mm, 120mm, 140mm), or choose a "custom" option and enter the specific dimensions. For the presets, use these screw hole distances: 25mm fan: 20, 30mm fan: 24, 40mm fan: 32, 50mm fan: 40, 60mm fan: 50, 70mm fan: 61.5, 80mm fan: 71.5, 92mm fan: 82.5, 120mm fan: 105, 140mm fan: 126, all in millimeters. Also use these screw hole diameters: 25mm fan: 2.9, all others: 3.3, except for 50mm and larger, which should be 4.4, all in millimeters. Finally use these cover heights: 25mm fan: 2, 30mm fan: 2.5, 40mm fan: 2.7, 50mm fan: 2.9, 60mm fan: 3, 70mm fan: 3, 80mm fan: 3.2, 92mm fan: 3.5, 120mm fan: 4, 140mm fan: 4.5, all in millimeters. For grill pattern heights, use: 25mm fan: 1, 30mm fan: 1.1, 40mm fan: 1.2, 50mm fan: 1.3, 60mm fan: 1.3, 70mm fan: 1.4, 80mm fan: 1.5, 92mm fan: 1.6, 120mm fan: 1.8, 140mm fan: 2, all in millimeters.

2.  **Frame Options:** I need a way to switch between a "full" frame and a "reduced" frame. The "reduced" frame should remove the large corner sections of the frame and leave only a small frame around the edges. The full frame should be a complete circle outline.

3.  **Minimum Border Size:** This should be a parameter that I can set, and the generated design should adhere to this border size. This should be in millimeters, and I would prefer it if it defaults to 1.92.

4.  **Grill Patterns:** I'd like a variety of grill patterns to choose from, specifically: "honeycomb", "grid", "line", "triangle", "crosshair", "square", "dot", and "aperture".

5.  **Grill Rotation:** I want to be able to rotate the pattern around the center of the cover by a specific angle. I would like to specify this angle as an integer between 0 and 360, and I want the UI to display a step value of 5 so it is easy to select angles in steps of 5 degrees.

6.  **Line Size:** I need to be able to control the thickness of the lines for the grill patterns. This should also be in millimeters, and I would like a default of 0.96. I would like it if this was a step size of 0.02 to allow precise control of the line width.

7.  **Line Space:** I want to control the space between lines (or the size of the shapes, in the case of the dot and aperture pattern). This should be in millimeters, and I would like a default of 6 and a step size of 0.1. If I am using the aperture pattern, this value should control the size of the center octagon.

8. **Screw Hole Chamfer:** I need to choose between no chamfer, top chamfer, bottom chamfer, and top and bottom chamfered screw holes.

9.  **Rounded Corners:** I want the ability to turn rounded corners on or off. When on, the radius of the rounded corners should be calculated based on the screw hole distance.

10. **Crosshair/Square Support Lines:** For the "crosshair" and "square" patterns, I want to control the number of support lines that run through the shape. This should default to 2 and be an integer step size of 1.

11. **Custom Fan Settings:** If the preset isn't an option, I need to be able to set the following parameters manually: cover size, screw hole diameter, screw hole distance, cover height, and grill pattern height, all in millimeters.

I'd prefer it if the code did not use any external libraries. Could you generate the necessary OpenSCAD files? I think I would prefer if the main part of the code was all in a single file, but if it makes sense to break it out, I'm happy for you to do that.

[Assistant]
customizable_fan_cover.scad:
```
/*
 * Customizable Fan Cover - https://www.thingiverse.com/thing:2802474
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2018-02-22
 * updated 2018-03-27
 * version v1.2
 *
 * Changelog
 * --------------
 * v1.2:
 *      - better quality of rounded corners and crosshair pattern
 * v1.1:
 *      - added support line option for crosshair and square pattern
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - Non-Commercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

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

/* [Global Settings] */

// Choose between presets of known and common fan sizes.
fan_preset = "40"; //[25:25mm,30:30mm,40:40mm,50:50mm,60:60mm,70:70mm,80:80mm,92:92mm,120:120mm,140:140mm,custom:Custom Fan Settings]

// If you want save to save about 40% filament and about 20% printing time, you can choose the reduced frame option.
frame_option = "reduced"; //[full,reduced]

// Minimum Border Size. I recommend to use two/four/six... times of your line width setting of your slicer. Simplify3D uses 0.48mm line width by default if you are using a 0.4mm nozzle. Cura uses 0.4mm line width.
min_border_size_in_millimeter = 1.92;

// Choose between a grill pattern
grill_pattern = "honeycomb"; //[honeycomb,grid,line,triangle,crosshair,square,dot,aperture]

// Set the angle of the pattern around the center of the cover.
grill_pattern_rotation = 0; //[0:5:360]

// Size of the pattern lines. I recommend to use two/four/six... times of your line width setting of your slicer. Simplify3D uses 0.48mm line width by default if you are using a 0.4mm nozzle. Cura uses 0.4mm line width.
line_size_in_millimter = 0.96; //[0.3:0.02:4]

// Space between two lines of your pattern. If you choose aperture pattern, this value set the size of the center octagon.
line_space_in_millimeter = 6; //[1:0.1:50]

// If you need countersunk holes, you can activate it here. Bottom = first layer. Top = last layer.
screw_hole_chamfer = "no"; //[no,top,bottom,top_and_bottom]

// Activate or deactivate rounded corners. The radius will be automatically calculated.
rounded_corners = "yes"; //[yes,no]

// number of straight lines supporting the crosshair or square pattern.
number_of_support_lines = 2;  //[1:1:36]

/* [Custom Fan Settings] */

// Cover size. E.g. 80 for a 80mm Fan
cover_size_in_millimeter = 40;

// E.g. 2.9 for M2.5, 3.3 for M3, 4.4 for M4
screw_hole_diameter_in_millimeter = 3.3;

// 25mm fan: 20 | 30mm fan: 24 | 40mm fan: 32 | 50mm fan: 40 | 60mm fan: 50 | 70mm fan: 61.5 | 80mm fan: 71.5 | 92mm fan: 82.5 | 120mm fan: 105 | 140mm fan: 126
screw_hole_distance_in_millimeter = 32;

// Height of the outer frame of the cover
cover_height_in_millimeter = 3;

// Height of the pattern inside the cover frame
grill_pattern_height_in_millimeter = 1;

/* [Hidden] */
min_border_size = min_border_size_in_millimeter;
line_size = line_size_in_millimter;
line_space = line_space_in_millimeter;

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

if(fan_preset == "25") {
    fan_cover(cover_size = 25, screw_hole_dia = 2.9, screw_hole_distance = 20, cover_h = 2, grill_pattern_h = 1);
}
if(fan_preset == "30") {
    fan_cover(cover_size = 30, screw_hole_dia = 3.3, screw_hole_distance = 24, cover_h = 2.5, grill_pattern_h = 1.1);
}
if(fan_preset == "40") {
    fan_cover(cover_size = 40, screw_hole_dia = 3.3, screw_hole_distance = 32, cover_h = 2.7, grill_pattern_h = 1.2);
}
if(fan_preset == "50") {
    fan_cover(cover_size = 50, screw_hole_dia = 4.4, screw_hole_distance = 40, cover_h = 2.9, grill_pattern_h = 1.3);
}
if(fan_preset == "60") {
    fan_cover(cover_size = 60, screw_hole_dia = 4.4, screw_hole_distance = 50, cover_h = 3, grill_pattern_h = 1.3);
}
if(fan_preset == "70") {
    fan_cover(cover_size = 70, screw_hole_dia = 4.4, screw_hole_distance = 61.5, cover_h = 3, grill_pattern_h = 1.4);
}
if(fan_preset == "80") {
    fan_cover(cover_size = 80, screw_hole_dia = 4.4, screw_hole_distance = 71.5, cover_h = 3.2, grill_pattern_h = 1.5);
}
if(fan_preset == "92") {
    fan_cover(cover_size = 92, screw_hole_dia = 4.4, screw_hole_distance = 82.5, cover_h = 3.5, grill_pattern_h = 1.6);
}
if(fan_preset == "120") {
    fan_cover(cover_size = 120, screw_hole_dia = 4.4, screw_hole_distance = 105, cover_h = 4, grill_pattern_h = 1.8);
}
if(fan_preset == "140") {
    fan_cover(cover_size = 140, screw_hole_dia = 4.4, screw_hole_distance = 126, cover_h = 4.5, grill_pattern_h = 2.0);
}
if(fan_preset == "custom") {
    fan_cover(cover_size = cover_size_in_millimeter, screw_hole_dia = screw_hole_diameter_in_millimeter, screw_hole_distance = screw_hole_distance_in_millimeter, cover_h = cover_height_in_millimeter, grill_pattern_h = grill_pattern_height_in_millimeter);
}

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

module fan_cover(cover_size, screw_hole_dia, screw_hole_distance, cover_h, grill_pattern_h) {
    corner_size = cover_size - screw_hole_distance;
    corner_r = rounded_corners == "yes" ? corner_size / 2 : 0;
    screw_pos = (cover_size - corner_size) / 2;
    color("DodgerBlue") difference() {
        union() {
            linear_extrude(height = cover_h, convexity = 20) {
                difference() {
                    offset(r=corner_r, $fn = ceil(corner_r * 8)) {
                        offset(r=-corner_r) {
                            square([cover_size, cover_size], center = true);
                        }
                    }
                    if(frame_option == "reduced") {
                        offset(r=corner_r, $fn = ceil(corner_r * 8)) {
                            offset(r=-corner_r) {
                                square([cover_size - min_border_size*2, cover_size - min_border_size*2], center = true);
                            }
                        }
                    }
                    if(frame_option == "full") {
                        circle(d = cover_size - min_border_size * 2, $fn = cover_size);
                    }
                }
                if(frame_option == "reduced") {
                    for(y = [-1:2:1]) {
                        for(x = [-1:2:1]) {
                            translate([screw_pos * x, screw_pos * y, -2]) {
                                circle(d = corner_size, $fn = ceil(corner_r * 8));
                            }
                        }
                    }
                }
            }
            linear_extrude(height = grill_pattern_h, convexity = 20) {
                intersection() {
                    offset(r=corner_r, $fn = ceil(corner_r * 8)) {
                        offset(r=-corner_r) {
                            square([cover_size, cover_size], center = true);
                        }
                    }
                    rotate(grill_pattern_rotation) {
                        if(grill_pattern == "honeycomb") {
                            honeycomb_pattern(cover_size, line_size, line_space);
                        }
                        if(grill_pattern == "grid") {
                            grid_pattern(cover_size, line_size, line_space);
                        }
                        if(grill_pattern == "line") {
                            line_pattern(cover_size, line_size, line_space);
                        }
                        if(grill_pattern == "triangle") {
                            triangle_pattern(cover_size, line_size, line_space);
                        }
                        if(grill_pattern == "crosshair") {
                            crosshair_pattern(cover_size, line_size, line_space);
                        }
                        if(grill_pattern == "square") {
                            square_pattern(cover_size, line_size, line_space);
                        }
                        if(grill_pattern == "dot") {
                            dot_pattern(cover_size, line_size, line_space);
                        }
                        if(grill_pattern == "aperture") {
                            aperture_pattern(cover_size, line_size, line_space);
                        }

                    }
                }
            }
        }
        for(y = [-1:2:1]) {
            for(x = [-1:2:1]) {
                translate([screw_pos * x, screw_pos * y, -2]) {
                    screw_hole(cover_h, screw_hole_dia);
                }
            }
        }
    }
}

module screw_hole(cover_h, screw_hole_dia) {
    cylinder(h = cover_h + 4, d = screw_hole_dia, $fn = 16);
    if(screw_hole_chamfer == "bottom" || screw_hole_chamfer == "top_and_bottom") {
        translate([0, 0, 2.9 - screw_hole_dia]) {
            cylinder(h = screw_hole_dia, d1 = screw_hole_dia * 4, d2 = screw_hole_dia);
        }
    }
    if(screw_hole_chamfer == "top" || screw_hole_chamfer == "top_and_bottom") {
        translate([0, 0, cover_h + screw_hole_dia/4]) {
            cylinder(h = screw_hole_dia, d2 = screw_hole_dia * 4, d1 = screw_hole_dia);
        }
    }
}

module grid_pattern(size, line_size, line_space) {
    num = ceil(size / (line_size + line_space) * 1.42);
    for(x = [floor(-num / 2) : ceil(num / 2)]) {
        translate([x * (line_size + line_space), 0]) {
            square([line_size, num *(line_size + line_space)], center=true);
        }
        rotate(90) {
            translate([x * (line_size + line_space), 0]) {
                square([line_size, num *(line_size + line_space)], center = true);
            }
        }
    }
}

module triangle_pattern(size, line_size, line_space) {
    num = ceil(size / (line_size + line_space) * 1.42);
    for(x = [floor(-num / 2):ceil(num / 2)]) {
        translate([x * (line_size + line_space), 0]) {
            square([line_size, num *(line_size + line_space)], center = true);
        }
        rotate(60) {
            translate([x * (line_size + line_space), 0]) {
                square([line_size, num *(line_size + line_space)], center = true);
            }
        }
        rotate(120) {
            translate([x * (line_size + line_space), 0]) {
                square([line_size, num *(line_size + line_space)], center = true);
            }
        }
    }
}

module line_pattern(size, line_size, line_space) {
    num = ceil(size / (line_size + line_space)*1.42);
    for(x = [floor(-num / 2):ceil(num / 2)]) {
        translate([x * (line_size + line_space), 0]) {
            square([line_size, num *(line_size + line_space)], center=true);
        }
    }
}

module crosshair_pattern(size, line_size, line_space) {
    line = (line_size + line_space) * 2;
    num = ceil(size / line * 1.42);
    for(n = [1:num]) {
        difference() {
            circle(d = n * line + line_size * 2, $fn = ceil(n * line + line_size * 2));
            circle(d = n * line, $fn = ceil(n * line + line_size * 2));
        }
    }
    for(rot=[0:90 / number_of_support_lines * 2:180]) {
        rotate(rot + 45) square([size * 2, line_size], center = true);
    }
}

module square_pattern(size, line_size, line_space) {
    line = (line_size + line_space) * 2;
    num = ceil(size / line * 1.42);
    for(n = [1:num]) {
        difference() {
            square([n * line + line_size * 2, n * line + line_size * 2], center = true);
            square([n * line, n * line], center = true);
        }
    }
    for(rot=[0:90 / number_of_support_lines * 2:180]) {
        rotate(rot + 45) square([size * 2, line_size], center = true);
    }
}

module honeycomb_pattern(size, line_size, line_space) {
    min_rad = (line_space / 2 * sqrt(3)) / 2 + line_size / 2;
    y_offset = sqrt(min_rad * min_rad * 4 - min_rad * min_rad);
    num_x = ceil(size / min_rad / 2) * 1.42;
    num_y = ceil(size / y_offset) * 1.42;
    difference() {
        square([size * 1.42, size * 1.42], center = true);
        for(y = [floor(-num_y / 2) : ceil(num_y / 2)]) {
            odd = (y % 2 == 0) ? 0 : min_rad;
            for(x = [floor(-num_x / 2) : ceil(num_x / 2)]) {
                translate([x * min_rad * 2 + odd, y * y_offset]) {
                    rotate(30) {
                        circle(d=line_space, $fn=6);
                    }
                }
            }
        }
    }
}

module dot_pattern(size, line_size, line_space) {
    rad = line_space / 2;
    y_offset = sqrt((rad + line_size / 2) * (rad + line_size / 2) * 4 - (rad + line_size / 2) * (rad + line_size / 2));
    num_x = ceil(size / rad / 2) * 1.42;
    num_y = ceil(size / y_offset) * 1.42;
    difference() {
        square([size * 1.42, size * 1.42], center = true);
        for(y = [floor(-num_y / 2) : ceil(num_y / 2)]) {
            odd = (y % 2 == 0) ? 0 : rad + line_size / 2;
            for(x = [floor(-num_x / 2) : ceil(num_x / 2)]) {
                translate([x * (rad + line_size / 2) * 2 + odd, y * y_offset]) {
                    rotate(30) {
                        circle(d=line_space);
                    }
                }
            }
        }
    }
}

module aperture_pattern(size, line_size, line_space) {
    circle(d = line_space, $fn = 8);
    for(rot = [1:2:15]) {
        rotate(360 / 16 * rot) {
            translate([line_space / 2 * cos(360 / 16) - line_size, -line_size]) {
                square([line_size, size]);
            }
        }
    }
}

```


[User]
Hi! I'm looking to create some custom spacers for my 3D printer. I've got an existing design I've hacked together, but it's not very configurable, and I'd like something that's easier to customize and generate different versions of.

Essentially, I need a spacer that will sit between the printer's base and the bottom plate, raising it up. The basic shape should be a sort of thick, flat octagon, with a hole in the center for an M3 screw. The height of the spacer will need to be adjustable but should default to 16mm for my case. The hole needs to be slightly larger than the screw diameter, maybe 3.6mm. It's currently a circle, but I think a polygon that is a rounded regular octagon would be best.

I'd also like the option to add "wings" to this base octagon, essentially rectangular extensions that make it easier to grab and hold onto during assembly. These wings would extend out from the sides of the central octagon, and should probably be a configurable length. I want to be able to choose whether to have 0, 1, or 2 wings. When 2 wings are selected, they should be on opposite sides.

So, here are the basic parameters I'm looking for:

*   `screw_diameter`: Diameter of the screw hole (default: 3.6mm)
*   `spacer_height`: Height of the spacer (default: 16mm)
*   `wing_length`: Length of the wings from the center of the octagon (default: 12mm)
*   `number_of_wings`: Number of wings (0, 1, or 2)
*   `fn` the $fn value to use for making the octagon. Let's say default to 16, with a parameter.

I was thinking it would be best if the design starts with rotating a rectangle to create the octagon (I think I did this in my hacked together version), then have the wings be added as needed.  I'd like the edges to be rounded, not sharp. Can you generate an OpenSCAD file that includes these parameters and generates the spacer? Ideally, you could also include a usage example where I can create the octagon spacer with zero or two wings. I would be using this with the MCAD library, I think. Thanks!

[Assistant]
_p_spacer.scad:
```
//print time on the 20mm risers was /insane/ so I'm going for something simpler
//that's right, M3 washers

//sized for M3 screws
screw = 3.6; //.6 for slop
// a 5015 blower fan is, as you might expect, 15mm thick
height = 16;
//length of wings (measured from center)
wing = 12;
//number of wings
sides = 2; //[0,1,2]
$fn = 1*8;

union(){
    rotate_extrude(angle=360)
    translate ([0.5*screw, 0])
    square ([screw, height]);

    if (sides > 1){
        translate ([-(0.5*screw)-wing, -screw, 0])
        cube ([wing, 2*screw, height]);
    }

    if (sides > 0){
        translate ([0.5*screw, -screw, 0])
        cube ([wing, 2*screw, height]);
    }
}

```


[User]
I need a customizable connector for two tubes, like for a bicycle frame. I want to be able to specify the diameter of each tube, the distance between the tubes, and the angle of connection between the two tubes. The connector should have a solid body with hollowed tubes and slots on each side.

Here's the breakdown of the parameters I need to control:

*   **tube_1_diameter**: Diameter of the first tube (in mm, range of 2.0 to 25.0).
*   **tube_2_diameter**: Diameter of the second tube (in mm, range of 2.0 to 25.0).
*   **extra_length**:  Additional distance between the tubes (in mm, range of 0 to 100, defaults to 0). This is in addition to the space required to put the two tubes next to each other.
*   **angle**:  Angle between the two tubes (in degrees, range of 0 to 90, defaults to 0, representing parallel tubes).

The connector needs to be designed with a bit of tolerance and a border for strength. I'd prefer that be handled internally in the code rather than having to calculate each one. A tolerance of .2mm and a border of 2mm should be fine.

The connector itself should essentially be a hull of two cylinders with the appropriate tube-shaped holes inside, as well as slots along the length of each tube. The tubes should have some extra length to ensure the connection is secure. The slots should cut down to the tube opening.

Could you generate the OpenSCAD code for this? I don't need any external libraries.

[Assistant]
customizable_tubes_connector.scad:
```
// (mm)
tube_1_diameter = 8.8; //[2.0:25.0]
// (mm)
tube_2_diameter = 6.8; //[2.0:25.0]
// between the tubes (mm)
extra_length = 0; //[0:100]
// formed by the tubes (degrees)
angle = 0; //[0:90]

// Two tube conector
//$fn=30*1;
tolerance = 0.2*1;
border = 2*1;
d1 = tube_1_diameter+tolerance+border; // cylinder 1
d2 = tube_2_diameter+tolerance+border;
h1 = d2;
h2 = d1;
slot1 = d1/2;
slot2 = d2/2;

distance_from_centers = d1/2+d2/2+extra_length;

difference()
{
    // main block
    hull()
    {
        cylinder(d=d1,h=h1,center=true);
        translate([distance_from_centers,0,0])
            rotate([angle,0,0])
            cylinder(d=d2,h=h2,center=true);
    }

    // holes
    cylinder(d=d1-border,h=h1*2,center=true);
    translate([distance_from_centers,0,0])
        rotate([angle,0,0])
        cylinder(d=d2-border,h=h2*2,center=true);

    // slots
    translate([-d1/2,0,0])
        cube([d1,slot1,h1*2],center=true);
    translate([d2+d1/2+extra_length,0,0])
        rotate([angle,0,0])
        cube([d2,slot2,h2*2],center=true);
}

// draw tubes only
%cylinder(d=d1-border,h=h1*3,center=true);
%translate([distance_from_centers,0,0])
        rotate([angle,0,0])
        cylinder(d=d2-border,h=h2*3,center=true);

```


[User]
Hey, I'm looking to create a set of ring gauges, and I'd like some help creating the OpenSCAD files for that. I want to be able to specify the ring size (US sizing) and have the code generate the appropriate gauge.

Here's what I'm thinking:

*   **Ring Size Input:** I need a parameter called `ringSize` that can be set to values between 0 and 16, with increments of 0.25.
*   **Ring Shape:** The main part of the gauge should be a ring shape with a small "tail" extending out the side, and a small hole in the tail for attaching to a string. The cross-section should be a rectangle.
    *   The overall shape should consist of a cylinder with a diameter specified by a conversion from ring size to diameter (using the equation provided below).
    *   The tail will be a rectangular solid that extends from the main ring and has a small hole.
    *   The main ring and the tail should be combined into one object, using `difference()` to remove the interior of the ring and the small tail hole.
    *   There should be a rounded corner near the tail hole using a `difference()` of a cube and a cylinder.
*   **Ring Size Markings:** I need the ring size number to be inset on the side of each gauge using `linear_extrude()` of a `text()` object. If the size is an integer, it should display with ".00" on the end; otherwise, it should display the value directly.
*   **Fillets:** On the inner curves, add small fillets for comfort.
*   **Diameter Calculation:** The inner diameter of the ring should be calculated from the US ring size using the formula: `innerDiameter = 0.8128 * ringSize + 11.63`. The outer diameter should be `innerDiameter + 6`.
*   **Units:** I would like the units to be in millimeters.

Could you generate the necessary OpenSCAD files for me? I'm not using any special libraries, so it should all be standard OpenSCAD code.

[Assistant]
ringGuage.scad:
```
ringSize=0; // [0:0.25:16]

module ring(message,outerDiameter,innerDiameter,height)
{
    letterHeight=0.5;

    // major ring and triangle corner
	module shapeA() {
		difference() {
            union() {
                // corner
                translate([outerDiameter+7,0,0])
                    cube([16,8,height],true,$fn=90);
                // outer ring
                cylinder(height,outerDiameter,outerDiameter,true,$fn=90);
            }
            // interior hole
            cylinder(height*2,innerDiameter,innerDiameter,true,$fn=90);
            // tiny hole for management
			translate([outerDiameter+11,0,0])
				cylinder(height*2,2,2,true,$fn=45/2);
		}
	}

    // rounded corner near the small hole
	module roundedCorner() {
		difference() {
			translate([outerDiameter,outerDiameter,0])
				cube([8,8,height*2],true);
			translate([outerDiameter-4,outerDiameter-4,0])
				cylinder(height*2,4,4,true,$fn=90);
		}
	}

    // text on the side
    module letters() {
        translate([outerDiameter-14,outerDiameter-letterHeight/2,-(height-1)/2])
        rotate([90,0,180])
            linear_extrude(letterHeight)
                text(message,
                    font="Liberation Sans",
                    size=height-1,
                    halign="right");
    }

    // fillets on interior curve
    module fillets() {
        color([1,0,0])
            cylinder(height*2,innerDiameter-height-1.5,innerDiameter+height-1.5,true);
        color([1,0,0])
            rotate([0,180,0])
                cylinder(height*2,innerDiameter-height-1.5,innerDiameter+height-1.5,true);
    }

    // put the various pieces together
	module finalAssembly() {
        difference() {
            shapeA();
            letters();
            fillets();
        }
	}

    // do it!
    finalAssembly();
}

module ringMaker(ringSize) {
	function stod(s)= 0.8128 * s + 11.63;

    d = stod(ringSize)/2;
    if((ringSize%1)==0) {
        label = str(ringSize,".00");
        ring(label,d+3,d,4);
    } else {
        label = str(ringSize);
        ring(label,d+3,d,4);
    }
}

ringMaker(ringSize);

```


[User]
Hey, I'm looking to design a customizable parts box, and I'd like to use OpenSCAD for it. I've been messing around with a few designs but haven't gotten exactly what I need, so I'm hoping you can help.

Essentially, I need a box that can hold drawers, with each drawer having compartments. I want to be able to control the size of the compartments, the number of compartments per drawer, and the number of drawers per box. I'd also like to be able to stack the boxes if I need to.

Here are some specific features I'm looking for, broken down into sections:

**Basic Setup:**

*   **Compartment Size:** I need to be able to specify the width, depth, and height of a single compartment in a drawer.
*   **Compartment Count:** I need to specify how many compartments are next to each other in a row (X count) and how many are behind each other in a column (Y count).
*   **Drawers Per Area:** The number of drawers stacked vertically in each section of the box.

**Drawer Options:**

*   **Handle:** I want to be able to choose whether a handle is printed in place on the drawer itself or as a separate piece. I'd like to have some control over the size and shape of that handle, or choose for it to automatically size itself.
*  **Drawer Stoppers:** It would be really handy to add drawer stoppers to prevent the drawers from sliding out.
*   **Label Holder:** I need options for label holders, including a slide-in frame, a simple canvas, or none at all.
*   **Handle Sizing:** As noted above, either manual size or auto-size based on the drawer. Manual includes the width, height, depth, and thickness of the handle.

**Stacking Options:**

*   **Stacking Holes:** I need to be able to choose whether to add holes on the right, left, top, and/or bottom of the box for connectors to enable stacking.

**Multiple Drawer Areas:**

*   I'd like to also be able to specify the number of areas with drawers next to each other (X count) and on top of each other (Y count). This would allow a larger single unit with multiple box sections.

**Fine Tuning:**

*   **Printer Settings:** I need to be able to specify the layer width and layer height I use to print, so those can be taken into account for spacing calculations and such.
*   **Spacing:**
    *   I would like a "drawerSpacer" parameter to control space between the drawer and the box itself.
    *   I'd like "connectorPadding" to specify padding for connectors.
     *   I need a "connectorSpacing" parameter to control space for connectors to be inserted.
*   **Smoothing:** I'd like "smoothRadius" and "smoothQuality" to control the smoothing of corners.
*   **Label Holder:**
    * "labelHolderSpace" should specify the gap for label insertion.
     * "labelHolderEdgeWidth" should specify the size of edges.
*  **Drawer Stopper Size:** Should also be controllable with `drawerStopperSize`.

I'm hoping for a parametric design, so I can tweak all these values and generate different configurations. It seems like a modular approach would be best, where we have modules for the Box, Drawer, Handle, and Connectors, and label holders. I'm not planning to use any of the existing libraries but let me know if you think there's a need.

Let me know if you have questions!

[Assistant]
PartBox.scad:
```
//Created by Bram Vaessen 2017

//Which one would you like to see?
part = "box"; //[box:The Box, drawer:Drawer, emptyDrawer:Drawer without compartments/dividers (optional), handle:Handle for Drawer (optional), connector:Connector for Stacking (optional)]

/* [Basic Setup] */
//The width of a single compartment in a drawer
compWidth=20;
//The depth of a single compartment in a drawer
compDepth=25;
//The height of a single compartment in a drawer
compHeight=15;

//The number of compartments in a drawer next to each other
compXCount=3;
//The number of compartments in a drawer behind each other
compYCount=2;
//Number of drawers on top of each other
drawersPerArea=3;

/* [Drawer Options] */
//should the handle be printed in-place on the drawer (requires good bridging!)
attachHandle=1; //[1:Yes, 0:No]
//Should the drawers have stoppers?
drawerStopper=1; //[1:Yes, 0:No]
//The type of holder for a label (slide-in-frame, simple canvas, or none)
labelOption = "Frame"; //[Frame, Simple, None]
//Size the handle automatically? (ignores options below)
handleAutoSize=1; //[1:Yes, 0:No]
//the width of the handle (for non-auto-sized)
handleWidth=25;
//the height of the handle (for non-auto-sized)
handleHeight=12;
//the depth of the handle (for non-auto-sized)
handleDepth=8;
//the thickness of the handle (for non-auto-sized)
handleThickness=1.5;

/* [Stacking Options] */
//Add holes for stacking on the right side
stackRight=1; //[1:Yes, 0:No]
//Add holes for stacking on the left side
stackLeft=1; //[1:Yes, 0:No]
//Add holes for stacking on the top side
stackTop=1; //[1:Yes, 0:No]
//Add holes for stacking on the bottom side
stackBottom=1; //[1:Yes, 0:No]

/* [Multiple Drawer Areas] */
//The number of areas with drawers next to each other
areasXCount=1;
//The number of areas with drawers on top of each other
areasYCount=1;

/* [Fine Tuning] */
//the width of a single layer of your 3d printer (0.48 is normal for 0.4 nozzle)
layerWidth=0.48;
//the layer height that you use to print
layerHeight=0.2;
//space between drawer and box, applied to box, increase this if it the drawer doesn' fit
drawerSpacer=0.25;
//the amount of smoothing to do on the corners, don' make too large
smoothRadius=1.5;
//the quality of the smoothing, higher=slower
smoothQuality=32;

//the size of the gap to insert a label
labelHolderSpace = 0.75;
//the size of the edge of the label holder
labelHolderEdgeWidth = 2.5;

//extra amount of space between boxes for stacking, applied to connector
connectorPadding = 0.2;
//extra room for the connector for stacking, applied to connector
connectorSpacing = 0.3;

//the size of the drawer stopper
drawerStopperSize=1.25;

print_part();

module print_part()
{
	if (part == "box")
    	Box();
	else if (part == "drawer")
		Drawer();
	else if (part == "connector")
		rotate([0,90,0]) Connector();
    else if (part == "handle")
        rotate([-90,0,0]) Handle();
    else if (part == "emptyDrawer")
        Drawer(empty=true);

}

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

drawOutsideThick=layerWidth*3;
drawInsideThick=layerWidth*2;
drawBottomThick=CorrectedHeight(drawOutsideThick);

drawInsideLower=CorrectedHeight(1);

boxOutSideThick=1.5+layerWidth*4;
echo("layerwidth*5",layerWidth*5);
echo("layerwidth*6",layerWidth*6);
echo("layerwidth*7",layerWidth*7);

echo("1.5+layerwidth*2",1.5+layerWidth*2);
echo("1.5+layerwidth*3",1.5+layerWidth*3);
echo("1.5+layerwidth*4",1.5+layerWidth*4);

echo("boxOutSideThick",boxOutSideThick);

boxInsideThick=layerWidth*3;
boxBackThick=CorrectedHeight(boxOutSideThick);

drawXInsideCount = compXCount-1;
drawYInsideCount = compYCount-1;

drawWidth = compWidth*compXCount + drawXInsideCount*drawInsideThick + drawOutsideThick*2;
drawDepth = compDepth*compYCount + drawYInsideCount*drawInsideThick + drawOutsideThick*2;
drawHeight = compHeight + drawBottomThick;

drawXStart = -drawWidth/2 + drawOutsideThick + compWidth/2;
drawXStep = compWidth + drawInsideThick;

drawYStart = -drawDepth/2 + drawOutsideThick + compDepth/2;
drawYStep = compDepth + drawInsideThick;

////

areaWidth = drawWidth+drawerSpacer*2;
areaHeight = drawersPerArea*drawHeight + (drawersPerArea+1)*drawerSpacer + (drawersPerArea-1)*drawerSpacer;

boxXInsideCount = areasXCount-1;
boxYInsideCount = areasYCount-1;

boxWidth = areaWidth*areasXCount +  boxXInsideCount*boxInsideThick + boxOutSideThick*2;
boxHeight = areaHeight*areasYCount + boxYInsideCount*boxInsideThick + boxOutSideThick*2;
boxDepth = drawDepth + drawerSpacer + boxBackThick;

boxXStart = -boxWidth/2 + boxOutSideThick + areaWidth/2;
boxXStep = areaWidth + boxInsideThick;

boxYStart = -boxHeight/2 + boxOutSideThick + areaHeight/2;
boxYStep = areaHeight + boxInsideThick;

///

slidercut = sqrt(pow(drawBottomThick,2)/2)*1.5;

function CorrectedHeight(height) = ceil(height/layerHeight)*layerHeight;

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

module Connector()
{
    linear_extrude(height=boxDepth/3*2-1)
    offset(r=-connectorSpacing/2) union()
    {
        square([layerWidth*2+connectorPadding, boxOutSideThick], center=true);
        for (x=[0,1]) mirror([x,0,0]) translate([connectorPadding/2,0,0])
            polygon(points=[[layerWidth,boxOutSideThick/2],
                            [3*layerWidth,boxOutSideThick],
                            [3*layerWidth,-boxOutSideThick],
                            [layerWidth,-boxOutSideThick/2]]);

    }

}

module ConnectorHole()
{
    linear_extrude(height=boxDepth/3*2+1)
    union()
    {
        square([layerWidth*2, boxOutSideThick], center=true);
        for (x=[0,1]) mirror([x,0,0])
            polygon(points=[[layerWidth,boxOutSideThick/2],
                            [3*layerWidth,boxOutSideThick],
                            [3*layerWidth,-boxOutSideThick],
                            [layerWidth,-boxOutSideThick/2]]);
    }
}

module LabelHolder(width, height, opening)
{
    if (labelOption=="Frame")
        LabelHolderFrame(width, height, opening);
    else if (labelOption=="Simple")
        LabelHolderSimple(width, height);
}

module LabelHolderSimple(width, height)
{
    difference()
    {
        translate([0,-layerWidth/2,0]) CenterCube([width,layerWidth,height]);
        translate([0,0,height/2])
            cube([width-labelHolderEdgeWidth, layerWidth*3, height - labelHolderEdgeWidth],
                  center=true);
    }

}

module LabelHolderFrame(width, height)
{
    outideDepth = layerWidth*2;
    totalDepth = layerWidth*2 + labelHolderSpace;

    difference()
    {
        translate([0,-totalDepth/2,0]) CenterCube([width,totalDepth,height]);
        translate([0,0,labelHolderEdgeWidth])
            CenterCube([width-labelHolderEdgeWidth*2, totalDepth*3, height ]);
        translate([0,0,height/2])
            cube([width-labelHolderEdgeWidth, labelHolderSpace*3, height+1 ], center=true);
    }

    rotate([0,90,180])
        Triangle(totalDepth,totalDepth, width);

}

module Box()
{
    difference()
    {
        SmoothCube([boxWidth, boxHeight, boxDepth]);
        for (x=[0:areasXCount-1]) for (y=[0:areasYCount-1])
        {
            translate([boxXStart+x*boxXStep, boxYStart+y*boxYStep-drawerSpacer,0])
            {
                translate([0,0,boxBackThick+boxDepth/2])
                    rotate([-90,0,0]) translate([0,0,-areaHeight/2])
                        SmoothCube([areaWidth, boxDepth+drawerSpacer, areaHeight]);

                if (drawerStopper)
                    for (x2=[0,1]) mirror([x2,0,0]) for (y=[0:drawersPerArea-1])
                        translate([areaWidth/2,
                               -areaHeight/2 + drawerSpacer + y*(drawHeight+drawerSpacer),
                                boxBackThick])
                            translate([-0.01,drawHeight-1.25,0]) cube([1.5, 1.5, drawDepth-1]);

            }
        }

        //connector holes for stacking
        if (boxWidth>=30)
        {
            for (x=[0,1]) for (y=[0,1]) mirror([x,0,0]) mirror([0,y,0])
                if ((stackTop && y==0) || (stackBottom && y==1))
                    translate([boxWidth/2-10, boxHeight/2, -1]) rotate([0,0,90])
                        ConnectorHole();
        }
        else
        {
            for (y=[0,1])  mirror([0,y,0])
                if ((stackTop && y==0) || (stackBottom && y==1))
                    translate([0, boxHeight/2, -1]) rotate([0,0,90])
                        ConnectorHole();
        }
        if (boxHeight>=30)
        {
            for (x=[0,1]) for (y=[0,1]) mirror([x,0,0]) mirror([0,y,0])
                if ((stackRight && x==0) || (stackLeft && x==1))
                    translate([boxWidth/2, boxHeight/2-10, -1])
                        ConnectorHole();
        }
        else
        {
            for (x=[0,1]) mirror([x,0,0])
                if ((stackRight && x==0) || (stackLeft && x==1))
                    translate([boxWidth/2, 0, -1])
                        ConnectorHole();
        }

    }

    for (x=[0:areasXCount-1]) for (y=[0:areasYCount-1])
    {
        for (x2=[0,1]) mirror([x2,0,0])
        {
            translate([boxXStart+x*boxXStep, boxYStart+y*boxYStep,boxBackThick])
            {
                if (drawersPerArea>1)
                    for (y=[1:drawersPerArea-1])
                        translate([areaWidth/2+0.01,
                                   -areaHeight/2 + drawerSpacer + y*(drawHeight+drawerSpacer*2),0])
                            Slider();

            }
        }

    }
}

module Slider()
{
    diag = slidercut-drawerSpacer;
    dist = sqrt(pow(diag,2)*2);

    linear_extrude(height=drawDepth-smoothRadius)
        polygon(points=[[0,0], [-dist,0], [0,dist]]);
}

module Drawer(empty=false)
{
    difference()
    {
        SmoothCube([drawWidth, drawDepth, drawHeight]);
        if (empty)
        {
            translate([0, 0, drawBottomThick])
                SmoothCube([drawWidth-drawOutsideThick*2, drawDepth-drawOutsideThick*2, compHeight+1]);
        }
        else
        {
            for (x=[0:compXCount-1]) for (y=[0:compYCount-1])
            {
                translate([drawXStart+x*drawXStep, drawYStart+y*drawYStep, drawBottomThick])
                {
                    SmoothCube([compWidth, compDepth, compHeight+1]);
                }
            }
            translate([0,0,drawHeight-drawInsideLower])
                SmoothCube([drawWidth-drawOutsideThick*2, drawDepth-drawOutsideThick*2, compHeight+1]);
        }
        for (x=[0,1]) mirror([x,0,0])
            translate([drawWidth/2,smoothRadius,0]) rotate([0,45,0])
                cube([slidercut*2, drawDepth, slidercut*4], center=true);

        if (!attachHandle)
            translate([0,-drawDepth/2,drawHeight/2])
                minkowski()
                {
                    Handle();
                    cube([0.25,0.25,0.25],center=true);
                }

    }

    if (attachHandle)
        translate([0,-drawDepth/2+0.01,drawHeight/2]) Handle();

    if (drawerStopper)
        for (x=[0,1]) mirror([x,0,0])
            translate([drawWidth/2-0.01,
                   drawDepth/2-smoothRadius-drawOutsideThick/2,
                   drawHeight])
                Nook();

    if (handleAutoSize)
    {
        handleWidthA = drawWidth*0.33;
        lhPos = ((drawWidth/2-smoothRadius) + handleWidthA/2+smoothRadius)/2;
        lhSpace = (drawWidth/2-smoothRadius*1.5) - (handleWidthA/2+smoothRadius*1.5);
        for (x=[-1,1])
            translate([lhPos*x, -drawDepth/2+0.01, smoothRadius*2.5])
                LabelHolder(lhSpace, drawHeight - smoothRadius*3, x);
    }
    else
    {
        lhPos = ((drawWidth/2-smoothRadius) + handleWidth/2+smoothRadius)/2;
        lhSpace = (drawWidth/2-smoothRadius*1.5) - (handleWidth/2+smoothRadius*1.5);
        for (x=[-1,1])
            translate([lhPos*x, -drawDepth/2+0.01, smoothRadius*2.5])
                LabelHolder(lhSpace, drawHeight - smoothRadius*3, x);
    }

}

module Nook()
{
    layers = 1 / layerHeight;

    length = drawerStopperSize*3;
    middle1 = 1.5 *drawerStopperSize;
    middle2 = 2.5 *drawerStopperSize;
    for (i=[1:layers])
        let (perc = min(1,i/(layers*3/5)))
        translate([0,0,-1+(i-1)*layerHeight])
            linear_extrude(height=layerHeight)
                #polygon(points=[[0,0], [0,-length],
                                 [drawerStopperSize*perc, -middle2],
                                 [drawerStopperSize*perc, -middle1]]);
}

module Handle()
{
    if (handleAutoSize)
        HandleAutoSize();
    else HandleManual();

}

module HandleAutoSize()
{
    handleWidthA = drawWidth*0.3;
    handleThicknessA = sqrt(handleWidthA)/3;
    handleHeightA = drawHeight * 0.75;
    handleDepthA = handleHeightA * 0.6;

    for (x=[0,1]) mirror([x,0,0])
    translate([handleWidthA/2-handleThicknessA,0]) rotate([90,0,90])
        linear_extrude(height=handleThicknessA)
            polygon(points=[[0,-handleHeightA/2], [-handleDepthA,0],[0,handleHeightA/2]]);

    a = atan(handleDepthA / (handleHeightA/2));
    a2 = 90-a;

    d = handleThicknessA / sin(a);
    d2 = handleThicknessA / sin(a2);

    rotate([90,0,90])
        linear_extrude(height=handleWidthA-0.01, center=true)
            polygon(points=[[-handleDepthA,0],[0,handleHeightA/2],[0,handleHeightA/2-d],
                         [-handleDepthA+layerWidth*2,0]]);
}

module HandleManual()
{
    for (x=[0,1]) mirror([x,0,0])
    translate([handleWidth/2-handleThickness,0]) rotate([90,0,90])
        linear_extrude(height=handleThickness)
            polygon(points=[[0,-handleHeight/2], [-handleDepth,0],[0,handleHeight/2]]);

    a = atan(handleDepth / (handleHeight/2));
    a2 = 90-a;

    d = handleThickness / sin(a);
    d2 = handleThickness / sin(a2);

    rotate([90,0,90])
        linear_extrude(height=handleWidth-0.01, center=true)
            polygon(points=[[-handleDepth,0],[0,handleHeight/2],[0,handleHeight/2-d],
                         [-handleDepth+layerWidth*2,0]]);
}

module CenterCube(size)
{
    echo(size);
    translate([0,0,size[2]/2])
        cube(size=size,center=true);
}

module Triangle(width, height, depth)
{
    linear_extrude(height=depth, center=true)
        polygon(points=[[0,0], [width,0], [0,height]]);
}

module SmoothCube(size)
{
    hull()
    {
        for (x=[-1,1]) for (y=[-1,1])
        {
            translate([x*(size[0]/2-smoothRadius), y*(size[1]/2-smoothRadius), smoothRadius])
                sphere(r=smoothRadius, $fn=smoothQuality);
        }
        for (x=[-1,1]) for (y=[-1,1])
        {
            translate([x*(size[0]/2-smoothRadius), y*(size[1]/2-smoothRadius), size[2]-smoothRadius])
                cylinder(r=smoothRadius, h=smoothRadius, $fn=smoothQuality);
        }

    }
}

```


[User]
I need a parametric OpenSCAD design for a bracket that adapts different G9 light sockets to various mounting configurations. I'm trying to make my own version of something like this Thingiverse design: [https://www.thingiverse.com/thing:2656745](https://www.thingiverse.com/thing:2656745) but with more flexibility.

The bracket should consist of:

1.  **A top disc:** This will interface with the G9 socket itself. It should be a hollow cylinder with configurable outside and inside diameters and thickness.
2.  **Top tabs:** These will extend from the top disc and have screw holes to secure the socket. The position and spacing of these screw holes need to be parametric.  They should also have configurable width and thickness
3.  **Vertical arms:** These connect the top disc/tabs to the bottom mounting area.  The horizontal distance between the arms should be configurable. They need to have a thickness parameter as well as a height parameter.
4. **Bottom tabs**: These are for mounting the whole adapter to a surface. They should have mounting holes.  The horizontal distance between holes needs to be configurable. They also need to have width and thickness.

I want the design to be fully parametric so I can adjust all dimensions such as:

*   Overall thickness of the disc, arms, and tabs.
*   Overall height of the bracket.
*   Width of the tabs.
*   Outside and inside diameters of the top disc.
*   Diameter and spacing of the top mounting holes (for the socket).
*   Horizontal distance between the vertical arms.
*   Diameter and spacing of the bottom mounting holes.

I’d like the code to use `$fn = 200` to ensure the circular features look smooth. Also, include a parameter to scale the entire design, `Global_Scale`. And a tiny value parameter for offsetting elements slightly, `EPS`, to prevent issues with coincident faces when making subtractions.

Please provide me with all the necessary `.scad` files, and I prefer that each module be in the same `.scad` file.  Please do not use any external libraries, I would like it all contained in this one file.

[Assistant]
Base_Adapter_v2.0.scad:
```
//Base adapter for different G9 Sockets
//by: Brett Mattas
//date: February 24, 2018
//units: mm
//description:
//G9 Sockets I bought for lithophane lamp:
//https://www.thingiverse.com/thing:2656745
//don't fit on this base. Also have wires coming out
//of the bottom. This is an adapter bracket.
//version 2.0:
//Changed to make more customizable

/////////////////////////////////////////////
/////////  PARAMETERS ///////////////////////
/////////////////////////////////////////////

//Overall
Thickness = 3; //Disc, arm and tab thickness
Height = 15; //Height
TabWidth = 8; //Tab side dimension

//Top disc
TopOutD = 30; //Top disc outside diamter
TopInD = 22; //Top disc inside diamter

//Top Tabs
TopHoleD = 3.5; //Hole diameter for light socket screws
TopHoleS = 12.93; //Spacing of top holes

//Vertical arms
VertArmOut = 28; //Out-out dimension of the vertical arms

//Bottom tabs
BotHoleD = 4; //Bottom hole diameter
BotHoleS = 16.43; //Bottom hole spacing

//Global Parameters
Global_Scale = 1;
$fn = 200;
EPS = 0.01; //Small number to avoid coincident faces

/////////////////////////////////////////////
/////////  RENDERS //////////////////////////
/////////////////////////////////////////////

//Driver

scale(Global_Scale){

    Bracket();

}
/////////////////////////////////////////////
/////////  FUNCTIONS ////////////////////////
/////////////////////////////////////////////

/////////////////////////////////////////////
/////////  MODULES //////////////////////////
/////////////////////////////////////////////

//Main Module
module Bracket()
{

    //Elements added
    union(){

        //Top cylinder
        Disc(TopOutD, TopInD, Thickness);

        //Top hole tabs
        rotate([0, 0, 90]){
            TopTab(TopOutD, TopInD, TopHoleS, TabWidth, Thickness, TopHoleD);
            mirror([1, 0, 0])
                TopTab(TopOutD, TopInD, TopHoleS, TabWidth, Thickness, TopHoleD);
        }

        //Vertical arms
        TabArm(TopInD, VertArmOut, TabWidth, Thickness, Height);
        mirror([1, 0, 0])
            TabArm(TopInD, VertArmOut, TabWidth, Thickness, Height);

        //Bottom tabs
        BotTab(VertArmOut, BotHoleS, TabWidth, Thickness, Height, BotHoleD);
        mirror([1, 0, 0])
            BotTab(VertArmOut, BotHoleS, TabWidth, Thickness, Height, BotHoleD);

    }

}

module Disc(OutD, InD, T){
    //Hollow disc
    difference(){
        cylinder(r=OutD/2, h=T, center=false);
        translate([0, 0, -EPS])
            cylinder(r=InD/2, h=T+2*EPS, center=false);
    }
}

module TabArm(In, Out, W, T, H){
    //Arm for tabs
    //Should be outside of the inside D
    union(){
        translate([Out/2, -W/2, -H+T])
            cube([T, W, H], center=false);
        translate([In/2, -W/2, 0])
            cube([(Out-In)/2, W, T], center=false);
    }
}

module BotTab(Out, HoleS, W, T, H, HoleD){
    //Bottom tab
    if(HoleS > Out){
        //Holes are outside the vertical tab
        difference(){
            translate([Out/2, -W/2, -H+T])
                cube([(HoleS/2 + W/2 - Out/2), W, T], center=false);
            translate([HoleS/2, 0, -H+T-EPS])
                cylinder(r=HoleD/2, h=T+2*EPS, center=false);
        }
    } else {
        //Holes are inside the vertical tab
        difference(){
            translate([HoleS/2-W/2, -W/2, -H+T])
                cube([(Out/2 - HoleS/2 + W/2 + T), W, T], center=false);
            translate([HoleS/2, 0, -H+T-EPS])
                cylinder(r=HoleD/2, h=T+2*EPS, center=false);
        }
    }

}

module TopTab(Out, In, HoleS, W, T, HoleD){
    //Top tabs
    if(HoleS > Out){
        //Holes are outside the vertical tab
        difference(){
            translate([In/2, -W/2, 0])
                cube([(HoleS/2 + W/2 - In/2), W, T], center=false);
            translate([HoleS/2, 0, -EPS])
                cylinder(r=HoleD/2, h=T+2*EPS, center=false);
        }
    } else {
        //Holes are inside the vertical tab
        difference(){
            translate([HoleS/2-W/2, -W/2, 0])
                cube([(In/2 - HoleS/2 + W/2), W, T], center=false);
            translate([HoleS/2, 0, -EPS])
                cylinder(r=HoleD/2, h=T+2*EPS, center=false);
        }
    }
}

```


[User]
Hey there, I'm looking for help creating a customizable flower pot generator in OpenSCAD. I've seen a few, but I'd like one with specific features and parameters. I'd like the generated SCAD to allow users to customize the following:

*   **Base Diameter:** The diameter of the pot's base, measured from the outer edge. I'd need to make sure this is constrained to between 10mm and 350mm.
*   **Pot Height:** The total height of the pot, from the bottom to the top of the rim. This should also have a minimum and maximum range (8mm to 350mm).
*   **Pot Side Angle:** The angle of the pot sides from vertical. The user should be able to set this anywhere from 0 to 30 degrees.
*   **Rim Side Angle:** Similar to the pot side angle, this should be the angle of the rim from vertical. This value should never exceed the Pot Side Angle. A range of 0-30 degrees should be fine for the user control.
*   **Rim Height:** The height of the rim above the main pot body, not including any rounding. It should be limited to something less than the overall pot height, maybe 6mm less at the max. A range of 0-200mm should be enough. Setting this to 0 should give a rounded-over edge with no lip.
*   **Rim Thickness:** The thickness of the rim. The user control can be from 0 to 10mm.
*   **Drain Hole:** A simple yes/no option for a drain hole in the bottom. If yes, the diameter should be calculated based on the base diameter.
*   **Pot Risers:** Another yes/no option for risers on the inside of the pot's bottom. These risers should be disabled if there's a drain hole.
*   **Sidewall Thickness:** The thickness of the pot's walls. A value of 0 should mean the program should calculate the thickness based on the base diameter using some kind of lookup table, or algorithm, maybe with values for wall thickness from a min of 1.25mm to a max of 5mm . A direct user control can range from 1 to 6mm.
*   **Base Thickness:** The thickness of the pot's base, also with a default (0) meaning the program should calculate it based on the diameter or the sidewall thickness (a bit thicker than the walls) User control range can be 1-7mm.

I think that's all. The generated pot should include a fillet under the rim, to allow printing without supports. I want the code to be well-commented so I can understand what's happening. I'd prefer it not use any external libraries, just plain OpenSCAD.

Thanks in advance!

[Assistant]
Customizer_Flower_Pot_-_RC2.scad:
```
// Flower Pot Customizer   by Robert Wallace - Mar 2018

// All values in milimeters
// Diameter of base - minimum is set later to 10, maximum to 350
// (mm) measured from outer edge (min 10mm, max 350mm)
base_diameter = 70;

// Height of the pot - min is set later to 10, max to 350
// (mm) measured from bottom to top (min 10mm, max 350mm)
height_of_pot = 80;

// Angle of pot sides -
// (deg) Number of degrees past vertical
pot_side_angle = 10;   //[0:30]

// Angle of rim - -
// (deg) Number of degrees past vertical. Not > Pot Side Angle
rim_side_angle = 5;   //[0:30]

// Height of rim -
// (mm) excluding round-over : '0' gives round rim : S/B << Height of Pot
height_of_rim = 16;   //[0:200]

// Rim thickness -
// (mm) [0-10] '0' gives no rim
rim_thickness = 2;

// Drain hole in the bottom? (usually "Yes" for pots)(cancels any risers)
drain_hole = "Yes";  //[Yes,No]

// Note: Drain hole == "Yes" turns off Risers.
// Risers on inside-bottom to let pot drain? (usually "Yes" for saucers)
pot_risers = "No"; //[Yes,No]

// (mm) Thickness of the side walls [0-6.0] - if 0, software calculates based on diameter
sidewall_thickness = 0;

// (mm) Thickness of the base [0-7.0] - if 0, software calculates based on diameter
base_thickness = 0;

module fake_news()  // to stop customizer from further customizing
{
}

// The sanity ranges.  Any user input outside the min or max is changed to the min or max.
//  These were chosen based on expected use and build sizes.  If you have a huge print volume,
//  feel free to change these to suit you.  No guarantees, however.

min_dia = 10;   // the minimum diameter of the pot's base
max_dia = 350;  // the maximum diameter of the pot's base
min_ht = 8;     // the minimum height of the pot (smaller sizes normally used to make saucers)
max_ht  = 350;  // the maximum height of the pot
min_wthk = 1.0; // the minimum side wall thickness
max_wthk = 6.0; // the maximum side wall thickness
min_bthk = 1.0; // the minimum base thickness
max_bthk = 7.0; // the maximum base thickness
min_rthk = 0;   // the minimum rim thickness (0=no rim)
max_rthk = 10;  // the maximum rim thickness

// There's a lot of flailing around past this point - I reworked the algorithms
//  a half-dozen times, so there's old cruft here and there.  I intend to
//  clean it up one more time and eliminate a few intermediate variables
//  that ended up being used only once.  Even if only used once,
//  I may use a variable to hold the results of long calculations just
//  so I don't have to insert the long calculation in an argument list and
//  break lines all over the place.
//    ( I like a clean, definite right margin in my code. )

$fn = 400;  // Very smooth, but a little slower to generate

// lookup table relating base radius to wall thickness
//  (not quite linear and sets minimums and maximums)
// note: the radius==300 lookup is never reached since software limits radius to 150
wt_tab = [[0,1.25],[10,1.5],[30,2],[60,2.5],[75,3],[100,3.5],[150,4],[300,5]];

// To eliminate the need for supports, build a fillet to connect
//  the bottom of the rim to the side of the pot. The next value is
//  the angle (as measured from noon) of the edge of the fillet.
//  (55 degrees is near the limit of what an fdm printer can print unsupported.)
fill_ang = 55;  // angle of the fillet.

// convert & correct the user's customizer inputs and assign them to variables used internally.
dr_hole = (drain_hole == "Yes") ? 1 : 0;
add_risers = (pot_risers == "Yes") ? 1 : 0;

number_of_risers = 5;  // auto-arranged around the center

// the pot side angle   (note: range is specified in Customizer)
pot_ang = pot_side_angle;// pot sides angle (measured from noon, clockwise) max 43.5 deg
              //  but the fillet starts looking pretty obnoxious after 20 degrees or so
              //  fyi: can't be >= fill_ang or things blow up.

// the rim side angle - check for sanity (note: range is specified in Customizer)
rim_ang = (rim_side_angle <= pot_side_angle &&   // is rim angle <= pot angle ?
           rim_thickness > 0) ?                  // and is user want's a rim?
            rim_side_angle :                     // yes - use rim angle (degrees past vertical)
            pot_side_angle;                      // no - set to pot angle
                                                 // note: can't be < 0 or > pot_ang

// limit the xtremes of user's pot height to program's specified limits
abs_pot_ht = (height_of_pot < min_ht) ? // spec'd height < minimum height?
              min_ht :                  // set to minimum height
             (height_of_pot > max_ht) ? // otherwise check to see if height > maximum height
              max_ht :                  // if it is, set to maximum height
              height_of_pot;            // otherwise use the user's specified height
                                  // this is the pot height before the rounded rim top
                                  //  is lowered to match angle of pot's top edge

pot_ht = abs_pot_ht - 1.4;  // when the rim round-over is set, it's lowered some to edge-match

// limit the extremes of user's base diameter to spec'd limits
//  and convert diameter to radius)
base_rad = (base_diameter/2 < 5) ? 5 : (base_diameter/2 > max_dia/2) ? max_dia/2 : base_diameter/2;

// adjust user-specified wall, base, and rim thicknesses to be within the specified limits
wall_thk = (sidewall_thickness == 0 ) ? lookup(base_rad, wt_tab) :  // set to 0? then lookup value
           (sidewall_thickness <= min_wthk) ? min_wthk :            //  < min, set to min
           (sidewall_thickness >= max_wthk) ? max_wthk :            //  > max, set to max
            sidewall_thickness;                                     // all OK - use it

base_thk = (base_thickness == 0) ? wall_thk + 1 :        // set to 0? make it 1mm thicker than walls
           (base_thickness <= min_bthk) ? min_bthk :     // else check for extremes and limit them
           (base_thickness >= max_bthk) ? max_bthk :
            base_thickness;

user_rim_thk = (rim_thickness < min_rthk) ? min_rthk :  // check for extremes
              (rim_thickness > max_rthk) ? max_rthk :
              rim_thickness;

tot_rim_thk = user_rim_thk + wall_thk;   // the total thickness of the rim (incl wall thickness)

// Some sanity settings and programmatically determined settings :

// rim_ht must be some value less than the height of the pot or things blow up
// - lets say 6mm less, but check the output carefully
rim_ht = (height_of_rim < (abs_pot_ht - 6)) ?  // how tall (above zero) is the flat top of the rim
    height_of_rim : abs_pot_ht - 6;          // ( 0 gives a tot_rim_thk cylinder rim)

// Scale the drain hole radius based on pot base radius except min is 2.5 and max is 12
drain_r = (base_rad / 7 < 2.5) ? 2.5 : (base_rad / 7 > 12) ? 12 :base_rad / 7;

bchamf = .8;  // bottom chamfer is 45-degrees -- intended to smooth any elephant-foot on bottom,
              //  this number is how far up the side of the pot the chamfer starts
              //  (larger on bigger pots?)

// Calculate how far out on X axis the top edges (inner and outer) of the pot are
//  based on the angle of the wall (pot_ang).
in_dx = tan(pot_ang) * (pot_ht - base_thk);  // inner edge: from top of base to pot_ht
in_dx2 = tan(pot_ang) * (pot_ht);            // outer edge: from bottom of base to pot_ht
out_dx = tan(rim_ang) * rim_ht;              // how far left to move the rim's top circle

// Building this as a spun polygon has its advantages and its disadvantages.
// One problem is maintaining the specified wall thickness when drawing an outline
//  as the angle of the wall changes instead of just rotating a pre-built rectangle.
//  (Another is rounding over your edges using circles and polygons - seen later.)

// To maintain the wall thickness (cross section) when we're drawing the wall shape at an angle,
//  the x points (x1 and x2) don't give us wall_thick -- that's actually (x1 and x3).  They
//  give us the hypotenuse of a right triangle. (crudely illustrated in fixed font, below.)
//  You have to calculate the distance x1-x2 based on wall_thick (x1-x3)
//
//         x1 ---------------- x2
//           /               /
//          /               / x3
//         /               /

// Actual value of x1-x2 needed to achieve specified wall thickness (x1-x3)
true_x = wall_thk / sin(90-pot_ang);

// Print out the final values used to make the shape.  Customizer won't display output from echo.
echo ("Base Diameter = ",base_diameter);
echo ("Pot Height = ", height_of_pot);
echo ("Pot Side Angle = ", pot_side_angle);
echo ("Rim Side Angle = ", rim_side_angle);
echo ("Rim Thickness = ", rim_thickness, "final rim thickness = ", user_rim_thk);
echo ("Rim Height = ", height_of_rim);
echo ("Drain Hole = ", drain_hole);
echo ("Pot Risers = ", pot_risers);
echo ("Sidewall Thickness = ", sidewall_thickness, "final sidewall thickness = ", wall_thk);
echo ("Base Thickness = ", base_thickness, "final basel thickness = ", base_thk);

// Build the shape polygon and spin it to create the finished pot/saucer/etc.

rotate_extrude()  // comment out rotate_extrude() to see the polygon that will be rotated
{
// everything beyond this point is 2-dimensional
difference()  // lop off any protrusions on the bottom from the silly fillet algorithm
{
union()
{
polygon([
        [(drain_r + base_thk/2) * dr_hole,0],           // origin at drain hole and y==0
        [(drain_r + base_thk/2) * dr_hole,base_thk],    // up to the top of the base
        [base_rad-true_x, base_thk],                    // out to the inside edge of the wall
        [base_rad-true_x + in_dx, pot_ht],              // up to the inner topedge
        [base_rad-true_x + in_dx + true_x, pot_ht],     // out to the outer topedge
// the next two points chop a 45-degree chunk off the pointed edge
//  -- the next-best thing to an actual round-over, and indistinguishable from an
//  actual rounded edge when you only have two or three printed layers to define it.
        [base_rad - (in_dx2 - in_dx) + bchamf/tan(90-pot_ang),bchamf],  // down to start of chamfer
        [base_rad - (in_dx2 - in_dx) + bchamf/tan(90-pot_ang) - bchamf, 0], // in and down to y==0

        [base_rad - (in_dx2 - in_dx), 0],      //
        [(drain_r + base_thk/2) * dr_hole,0]
       ]);

// put a half-circle at the edge of the drain hole to give a rounded inner edge when rotated
translate([drain_r + base_thk/2,base_thk/2])
difference()
{
circle(d=base_thk);
translate([0,-(base_thk+1)/2])
 square([base_thk+1, base_thk+1]);  // remove unseen portion: at low diameters it could protrude
}  // end difference

// The rounded top of the rim has to be re-positioned a bit to account for
//  the angle of the sides of the pot.  The center of the circle forming the top of
//  the rim must remain perpendicular to the side of the pot so there won't be a visible
//  hump on the inside edge.  Wouldn't be a problem if it was placed before angling the sides.
//  Hindsight.

// the exact (x) of the top-inner edge of the pot
pot_top_inner_x = base_rad-true_x + in_dx;
// the exact (x) of the top-center edge of the pot
pot_top_center_x = pot_top_inner_x + true_x/2;

// Calculate x and y offset used to lower the top circle (that creates the top rim's round-over)
// so that its rim is flush with the inside edge of the pot

py = (tot_rim_thk)/2 * cos(90-pot_ang);  // how far down to move the center point of the circle
px = tan(pot_ang) * py;                  // how far in to move the center point

// Need to find the diameter of a circle such that the chord after it has been lowered will be exactly 'true_x' long, thus covering exactly the top of the pot edge

// Since the required new radius is the hypotenuse of a right angle formed by the constrained radius (true_x/2) and the vertical amount that the circle was dropped into the leg (py), pythagorus gives us the length of the hypotenuse: hyp**2 = sqrt ( sum of squares of other two sides )  -- about now, I'm questioning my decision to spin a 2D shape...

needed_radius = sqrt(pow((user_rim_thk + true_x)/2,2) + pow(py,2));

// create the rim by placing two circles at the rim angle and hulling them

hull()
{
// top circle - set on top edge then pulled down and back a little based on angle of the wall
//  This lines up circle's perimeter with top inner edge of pot wall
tmp_x_off = pot_top_inner_x + needed_radius;
translate([pot_top_center_x + user_rim_thk/2 , pot_ht-py])
  circle(r=needed_radius);

// bottom circle x offset - to put perimeter on the inside edge of the pot
offset_x = tan(pot_ang) * rim_ht;

// bottom circle
translate([pot_top_center_x
           + user_rim_thk/2
           + needed_radius - tot_rim_thk/2
          - out_dx,
        pot_ht-py-rim_ht])

   circle(d=tot_rim_thk);

}  // end hull for rim

// Here's the maximum overhang fillet (see 'fill_ang' at the top)
//  [This one was a bear to figure out - ended up having to calculate
//   the (x,y) point where a line tangent to the rim (the fillet) meets
//   another line (the outside of the pot). I can barely remember any
//   high-school geometry as it is.  Please forgive the obvious comments
//   as I document what I calculated so I can remember it next week.]

mo_x = abs(tot_rim_thk/2 * sin(270 - fill_ang)); // intermediate calculations to help find pt on rim
mo_y = abs(tot_rim_thk/2 * cos(270 - fill_ang));

// here's the tangent point where the fill_ang slope meets the bottom rim's circle
// x coord of tangent point on circle
tx =  (pot_top_center_x + user_rim_thk/2 + needed_radius - tot_rim_thk/2 - out_dx) + mo_x;
// y coord of tangent point on circle
ty = (pot_ht - py - rim_ht) - mo_y;

// Need to find a point along the outer edge of the pot
//  that will give the polygon a 'fill_ang' angle
//  to the x-axis

// Let's try something - trig and algebra - to find out where the overhang line
//  meets the outside edge of the pot.

// Need to use y=mx+b for both lines.

// First, the slope of the overhang (the 'm' term)
ov_slope = 1/tan(fill_ang);

// Use the slope to find the y-intercept (the b in y=mx+b)
// To do that, first find the value of x when the line crosses x-axis (y == 0)
base_x = ty / tan(90-fill_ang);
base_x_pt = tx - base_x;

// Now, determine the y-intercept
ov_b = ty - (ov_slope * tx);

// Our overhang equation is now y = (1/tan(fill_ang) * tx + (ty - (ov_slope * tx))
//   or... y-point = (ov_slope * x) + ov_b

// Now do the same for the pot's outer wall
// The bottom pt of the outer edge is  "base_rad - (in_dx2 - in_dx), 0"
// The top pt of the outer edge is "base_rad-true_x + in_dx + true_x, pot_ht"
pot_tox = base_rad-true_x + in_dx + true_x;
pot_box = base_rad - (in_dx2 - in_dx);
pot_slope = (pot_ang == 0) ? 1/tan(.00000000001) : 1/tan(pot_ang);
pot_b = pot_ht - (pot_slope * (base_rad-true_x + in_dx + true_x));

// now have slope and b for both the overhang and the pot side
// lets see where they intersect each other
// (ov_slope * x) + ov_b = (pot_slope * x) + pot_b
// solve for x:
//  (ov_slope * x) - (pot_slope * x) = pot_b - ov_b
//   (ov_slope - pot_slope) * x = pot_b -ov_b
// x = (pot_b - ov_b) / (ov_slope - pot_slope)

// Calculate the point where this actually turns out to be
pot_intrcpt_x = (pot_b - ov_b) / (ov_slope - pot_slope);
pot_intrcpt_y = (pot_slope * pot_intrcpt_x) + pot_b;

// draw the overhang protector triangle in position under the rim
polygon([[tx, ty],[pot_intrcpt_x, pot_intrcpt_y], [pot_tox, pot_ht]]);

}  // end union of all 2-D elements

// the square to lop off protrusions from the no-support fillet
//  (at certain extreme pot side angles, the y-intercept, above,  is < 0)
translate([-150,-300])
 square([300,  300]);
}  // end difference() to lop off rogue protrusions
} // end rotate_extrude()

// add some risers (if this is made without a drain hole)
// -- needs more parameterization - gets screwey at small base diameters
if(!dr_hole)
{

if(add_risers)
 {
 for(i=[0:360/number_of_risers:359])
  {
  //echo(i=i);
  translate([0,0,base_thk])  // how high the risers above 0
  rotate([90,0,i])
   translate([0,0, drain_r+1.5])   // how far out the risers are from the center
    union()
     {  // make the 'cylinder' 3/4 the size of th base thickness
     cylinder(r=base_thk - base_thk/4, h= base_rad - 2*wall_thk - drain_r - 5, $fn=6);
     translate([0,0,0]) sphere(r=base_thk - base_thk/4, $fn=6);
     translate([0,0,base_rad - 2*wall_thk - drain_r - 5]) sphere(r=base_thk - base_thk/4, $fn=6);
     }
  }  // end for loop
 } // end if(add_risers)
} // end if(!dr_hole)

```


[User]
Hi! I'm looking to design a customizable shoulder stock for things like cameras, binoculars, and spotting scopes. I've got a basic idea and some specific parts in mind.

The core of the design is a tube that will rest against the shoulder, and this tube needs to be customizable. I'd like to specify the total length, the maximum diameter, the amount of taper at the ends, and the length of the tapered sections. It also needs a specified wall thickness.

Then, I need a thumbwheel that can be used to tighten a screw. This thumbwheel should have a knurled edge for better grip, and it needs to have a hole for an M3 screw. I need to be able to specify the dimensions of this thumbwheel so that I can make sure it works properly.

I would like the design to be parametric so that I can easily change these dimensions and generate different versions for various scopes and other devices. This should all be done in OpenSCAD, and I'd prefer to use the standard built-in libraries where possible.

So to be clear, here are the main features I need:
*   A customizable tube (adjustable length, diameter, taper, and wall thickness).
*   A customizable thumbwheel for M3 screws.
*   Everything should be parametric so dimensions can be easily tweaked.

I'm also planning on using some additional hardware such as 1/4" tripod screw, M6 x 30 screw, M6 hex nut, and 2 M3 x 20 screws and nuts, but those can be attached separately so we don't have to worry about those parts for this project.

Thanks!

[Assistant]
Shoulder_Stock_Tube.scad:
```
// Total Length
l = 150;

// Maximum Diameter
diameter = 25.5;

// Taper (gets subtracted from Maximum Diameter to yield Diameter at Ends)
taper = 1.5;

// Length of Tapered Section (should be smaller than half the Total Length)
taper_length = 10;

// Wall Thickness
wall_thickness = 1.6;

// DOUBLE Wall Thickness
double_thickness = 2 * wall_thickness;

//Number of Facets
$fn = 128;

difference ()
{
    union ()
    {
        cylinder (d2 = diameter, d1 = diameter - taper, h = taper_length);

        translate ([0, 0, taper_length])
            cylinder (d = diameter, h = l - 2 * taper_length);

        translate ([0, 0, l - taper_length])
            cylinder (d1 = diameter, d2 = diameter - taper, h = taper_length);
    }

    union ()
    {
        translate ([0, 0, double_thickness/2])
            cylinder (d2 = diameter - double_thickness, d1 = diameter - taper - double_thickness, h = taper_length - double_thickness/2);

        translate ([0, 0, taper_length])
            cylinder (d = diameter - double_thickness, h = l - 2 * taper_length);

        translate ([0, 0, l - taper_length])
            cylinder (d1 = diameter - double_thickness, d2 = diameter - double_thickness, h = taper_length - double_thickness/2);
    }
}
```
Thumbwheel_M3.scad:
```
epsilon = 0.01;

$fn = 32;

//rotate ([180, 0, 0])
screwgrip ();

module screwgrip ()
{
    difference ()
    {
        union ()
        {
            cylinder (d1 = 8, d2 = 8, h = 3);

            hull ()
            {
                translate ([0, 0, 3])
                cylinder (d1 = 8, d2 = 8, h = epsilon);

                translate ([0, 0, 6])
                cylinder (d = 12, h = 2, $fn = 6);
            }

            hull ()
            {
                translate ([0, 0, 6])
                cylinder (d = 12, h = 2, $fn = 6);

                translate ([0, 0, 8])
                cylinder (d = 8, h = 2, $fn = 6);

            }
        }

        epzstep (1)
        cylinder (d = 4, h = 10);

        translate ([0, 0, 7])
        cylinder (d = 6, h = 10);

    }
}

module epzstep (n)
{
    translate ([0, 0, -n * epsilon])
    children ();
}
```


[User]
I'm looking to get a parametric OpenSCAD design for a hotshoe lens cap holder. I have a design I made a while back, and I’d like to recreate it with a bit more flexibility. I'd like to be able to customize it based on the diameter of my lens cap, and I want to be able to optionally have a section that covers the camera's pop-up flash.

The design has two main parts. First, there's a cylindrical part that fits over the lens cap. It’s basically a short cylinder with a stepped inner diameter to snugly hold the cap. The second part is a hotshoe adapter that slides into the camera's hotshoe. This part is basically a rectangular prism, with a slightly thinner portion that fits into the shoe itself.

I need these parameters:

*   **`Diameter`**: This is the inner diameter of the cylindrical portion, matching the lens cap size. It should be adjustable within a reasonable range, say, 40mm to 100mm.
*   **`CoverFlash`**: A boolean flag (0 or 1) to determine whether the hotshoe adapter should have a section that extends over the pop-up flash. This section would be needed only on larger lens diameters, like 67mm and up.
*  **`ShoeThickness`**: This should be a parameter but it is less likely to be edited by the user, let's set this to 1.6 as a default.

The design should be such that it can be printed upright without needing support material, using a brim to help with adhesion.

Also, I'd like to have a second part that acts like a lens cap sling. It should have a similar cylindrical structure, but instead of a hotshoe adapter it should have an adapter that is similar to a standard clip for a sling.

Can you generate the necessary OpenSCAD files for these designs?

[Assistant]
LensCapHolder_v0.7.scad:
```
/* [Global] */

// Inner diameter (40: SELP1640, 52: Sigma 30mm F1.4 DC DN C, 67: Samyang/Rokinon 12mm f2 NCS CS)
Diameter = 40; // [40:100]

// Cover the pop-up flash (makes sense above 67mm)
CoverFlash = 0; // [1:true, 0:false]

/* [Hidden] */

ShoeThickness = 1.6;

LensCap(Diameter, ShoeThickness, CoverFlash);

module LensCapSling(diameter)
{
    difference()
    {
        union()
        {
            SlingAdapter(diameter);
            translate([0,diameter/15,0]) LensCylinder(diameter, 4);
        }
        SlingCropCubes(diameter);
    }
}

module LensCap(diameter, shoeThickness, coverFlash)
{
    rotate([90, 0, 0])
    {
        difference()
        {
            union()
            {
                HotShoeAdapter(diameter, shoeThickness, coverFlash);
                LensCylinder(diameter, 3);
            }
            CropCubes(diameter);
        }
    }
}

module LensCylinder(innerDiameter, rimHeight)
{
    outerDiameter = innerDiameter + 3.0;
    difference ()
    {
        totalHeight = rimHeight + 1.15;
        translate([0,innerDiameter/3,totalHeight/2]) cylinder(h=totalHeight,d=outerDiameter, center = true);
        union()
        {
            translate([0,innerDiameter/3,rimHeight/4+1.25-0.05]) cylinder(h=rimHeight/2, d1=innerDiameter, d2=innerDiameter+0.8, center = true);
            translate([0,innerDiameter/3,rimHeight/4*3+1.25-0.06]) cylinder(h=rimHeight/2, d1=innerDiameter+0.8, d2=innerDiameter, center = true);
        }
    }
}

module HotShoeAdapter(capDiameter, shoeThickness, coverFlash)
{
    union()
    {
        if (coverFlash == 1)
        {
            x = capDiameter / 2 - (9 + 25.4);
            translate([x,7.5,-1]) cube([12.5,15,2], center = true);
            translate([x,7.75,-2-shoeThickness/2]) cube([18,15.5,shoeThickness], center = true);
        }
        else
        {
            x = capDiameter / 2 - (9 + 2.4);
            translate([x,7.5,-1]) cube([12.5,15,2], center = true);
            translate([x,7.75,-2-shoeThickness/2]) cube([18,15.5,shoeThickness], center = true);
        }
    }
}

module SlingAdapter(capDiameter)
{
    difference()
    {
        x = capDiameter;
        translate([0,x/2,-2]) cube([12.4,x,4], center = true);
        translate([0,x/2+0.1,-1.6]) cube([10,x+0.2,2.4], center = true);
    }
}

module CropCubes(lensDiameter)
{
    // if the cap is bigger than 100mm it may result in strange forms
    cubeSide = 200;
    union()
    {
        translate([0, cubeSide/2 + lensDiameter*0.5, 0]) cube(cubeSide, center = true);
        translate([0, -cubeSide/2, 0]) cube(cubeSide, center = true);
    }
 }

module SlingCropCubes(lensDiameter)
{
    cubeSide = 200;
    union()
    {
        translate([0, cubeSide/2 + lensDiameter*0.8, 0]) cube(cubeSide, center = true);
        translate([0, -cubeSide/2, 0]) cube(cubeSide, center = true);
    }
}
```


[User]
I'd like to design a parametric tray generator in OpenSCAD. The idea is to create trays for things like hard drives, SD cards, or really anything, where each compartment can have a different width and depth. I'd like to be able to choose between full-height walls or low walls, and with the low walls I need to be able to specify their height, percentage of the tray depth they cover, and whether the low walls are centered or on one edge.

Specifically, I'd need these parameters to be customizable:

*   `wall_thickness`: The thickness of all walls.
*   `walls_height`: The full height of the walls (if not using lower walls).
*   `tray_depth`: The depth of each tray compartment. This should either be a single value for all compartments, or a list allowing different depths per compartment.
*   `tray_widths`: A list that specifies the internal width of each tray compartment.
*   `low_walls`: A string to enable or disable lower walls ("yes" or "no").
*   `low_walls_loc`: If using low walls, this should be a string ("center" or "edge") to specify the low wall location.
*   `side_walls_height`: If using low walls, this is the height of the low walls.
*   `low_walls_percentage`: If using low walls, this specifies the percentage of the depth that the low walls should cover (e.g., 0.85 would be 85%).

The core module should be called `tray_factory`, taking all the above parameters. Internally, it should call either a `multitray` module to create the trays with full-height walls, or a `multitray_low` module when low walls are requested.

The `multitray` module should take `wall`, `height`, `deepness` (a list of depths, one for each tray), and `tray_widths` as input. It needs to iterate through the widths list, placing a tray for each value in the list, each adjacent to one another. `deepness` should be a list of values.  `deepness` is the same as the `tray_depths` parameter given to tray_factory.  It should create a standard tray using a module called `tray` which take `wall`, `height`, `dw` (width), and `dd` (depth) as parameters.

The `multitray_low` module should take the same input parameters as `multitray`, plus `innerheight`, `innercut` (percentage), and `low_walls_loc`. It should build a regular `multitray` first, and then make a long box to cut the `multitray` in such a way that low walls will remain.

I'd also like you to write helper functions called `sumv` to return the sum of values in a vector, and `minv` to return the minimum value in a vector.

I don't need any special libraries for this.

[Assistant]
multitray.scad:
```
/**
* Tray generator - an OpenSCAN module
* @author itzco
*
* This library creates multiple kinds of trays.
* It was originally created to create trays of multiple widths, so it remains
* faithful to that principle by getting an array of tray_widths, instead of
* number of arrays.
* You can create:
* tray: single trays
* multitray: A group of joined trays
* multitray_low: A group of joined trays with a lower wall at center or edge
*
* Other options:
* Trays of multiple width and depth
*/

// Wall thickness in mm
wall_thickness = 2.5; // [0.5:0.5:10]

// Walls height in mm (if using 2 heights, this one is the highest)
walls_height = 12; // [1:150]

// Tray depth in mm OR array of tray_depths for multisizes
tray_depth = 110; // [6:200]

// Thickness of each disk / object
tray_widths = [ 10,10,10,10,10,10,10,10,10,10];
//[22.5,15.5,15.5];

// ALL FOLLOWING VARS ARE FOR LOW WALLS. IGNORE low_walls = "no";

// Use low walls on the side?
low_walls = "yes"; //[yes, no]

// Low walls location?
low_walls_loc = "center"; // [center, edge]

// Height of side lower walls
side_walls_height = 5; //[1:120]

// Percentage or low walls (L
low_walls_percentage = 0.85; // [0.1:0.1:1.0]

tray_factory(
   wall_thickness,
   walls_height,
   tray_depth,
   tray_widths,
   low_walls,
   low_walls_loc,
   side_walls_height,
   low_walls_percentage);

module tray_factory(
   wall_thickness = 0.5,        // [0.5:0.5:10]
   walls_height = 15,           // [1:150]
   tray_depth = 140,            // [6:200]  OR pass an array for irregular sizes (must be the same size as widths)
   tray_widths = [ 35,35,35,35],// Array of internal width

   // From here only define if you want lower walls in center or one side
   low_walls = "no", //[yes, no]
   low_walls_loc = "center", // [center, edge]
   side_walls_height = 5, //[1:120]
   low_walls_percentage = 0.80 // [0.1:0.1:1.0]
) {
   tray_depths =  (len(tray_depth) == undef) ?
               [ for (a = [1: len(tray_widths)]) tray_depth]
                  :
               tray_depth;

   if (low_walls == "no") {
      multitray(
           wall_thickness,
           walls_height,
           tray_depths,
           tray_widths);
   } else {
      multitray_low(
           wall_thickness,
           walls_height,
           tray_depths,
           tray_widths,
           side_walls_height,
           low_walls_percentage,
           low_walls_loc
      );
   }
}

// hard disk holder
module tray(
   wall=3,
   height=20,
   dw=20,
   dd=80
) {
   wall2 = wall*2;
   difference() {
      cube( [dw + wall2, dd + wall2, height]);
      translate([wall,wall,wall])
         cube( [dw, dd, height-wall+1]);
   }
}

// sumv function by https://www.thingiverse.com/MichaelAtOz/about
// https://www.thingiverse.com/thing:70745
function sumv(v,i,s=0) = (i==s ? v[i] : v[i] + sumv(v,i-1,s));
// Find minimum value in vector (proudly self-made :))
function minv(v,i) = ( i==0 ? v[i]  :  ( v[i] < minv(v,i-1) ? v[i] : minv(v,i-1) )  );

module multitray(
   wall = 3,
   height = 20,
   deepness = [80,80,80],
   tray_widths = [20,24,26]
) {
   union(){
      for (j = [0:  len(tray_widths)-1]) {
         move = sumv(tray_widths,j) - tray_widths[j] ;
         walls = wall * j;
         //echo(tray_widths[j], move, tray_widths[j]);

         translate([ move + walls,0,0])
            tray(wall, height, tray_widths[j], deepness[j]);
      }
   }
}
module multitray_low(
   wall = 3,
   height = 20,
   depth = [80,80,80],
   tray_widths = [20,24,26],
   innerheight=10,
   innercut = 0.9, // percentage
   low_walls_loc = "center"
) {

      hole_width = sumv(tray_widths, len(tray_widths)-1) + wall * (len(tray_widths)+1);
      hole_to_end = low_walls_loc == "center" ?
         (((1 - innercut) / 2) * depth[0]) + wall :
         -1;
      min_depth = minv(depth, len(depth)-1);
      difference(){
         multitray(wall,height,depth, tray_widths);
         translate([-1,hole_to_end, innerheight])
            color("red") cube([hole_width+2, min_depth * innercut, (height-innerheight)+1]);
      }
   }

```


[User]
I'm looking to create a parametric OpenSCAD model for a front panel with rounded corners and mounting holes. I'd like to be able to customize the panel's dimensions, corner radius, thickness, and the type and placement of screw holes at the corners.

Specifically, I need to be able to set the following parameters:

*   **Panel Dimensions:** Width, Depth, and Thickness.
*   **Corner Radius:** A single value for the radius of all four rounded corners.
*   **Screw Holes:**
    *   Option to choose between **flat** (cylindrical) or **tapered** screw holes.
    *   **Flat Screw Holes:** Radius of the cylinder, and an offset distance from the edge of the panel for these holes.
    *   **Tapered Screw Holes:** Radius of the top and bottom of the taper, and offset distance from the edge of the panel for these.

The design should start with a hull of four cylinders to create the rounded corners, and then subtract screw holes as requested. I would prefer a single .scad file that contains everything in it, please. The `difference()` and `hull()` commands should be used as indicated. I'd also like you to include module definitions for creating corner cylinders, flat screw holes and tapered screw holes, and please use vector arrays to manage the location of corner cylinders and screw holes.

[Assistant]
Rounded_Corner_Flat_Panel.scad:
```
$fn = 128;

/*
		*********************************
		*** PANEL CREATION PARAMETERS ***
		*********************************
*/

PANEL_CORNER_RADIUS = 5;
PANEL_WIDTH = 120;
PANEL_DEPTH = 80;
PANEL_THICKNESS = 3;

/*
		***************************************
		*** SCREW HOLE CONTROL & GENERATION ***
		***************************************
*/

// Change the values in this section to control the overall apperance of the panel.

CREATE_FLAT_SCREW_HOLES = false;
CREATE_TAPER_SCREW_HOLES = true;

FLAT_SCREW_HOLE_RADIUS = 3;
FLAT_SCREW_HOLE_OFFSET_FROM_EDGE = 3;

TAPER_TOP_HOLE_RADIUS = 6;
TAPER_BOTTOM_HOLE_RADIUS = 3;
TAPER_HOLE_FROM_EDGE = 3;

// Make it so!
difference() {
// Cylinder Corner co-ordinates vectors
	PANEL_CORNER_VECTORS = [[PANEL_CORNER_RADIUS, PANEL_CORNER_RADIUS, 0],
													[PANEL_CORNER_RADIUS, PANEL_DEPTH - PANEL_CORNER_RADIUS, 0],
													[PANEL_WIDTH - PANEL_CORNER_RADIUS, PANEL_CORNER_RADIUS, 0],
													[PANEL_WIDTH - PANEL_CORNER_RADIUS, PANEL_DEPTH - PANEL_CORNER_RADIUS, 0]];

	hull()
	{
		// Create the 4 cylinders for each corner
		translate(PANEL_CORNER_VECTORS[0]) CreateCornerCylinder(PANEL_CORNER_RADIUS, PANEL_THICKNESS);
		translate(PANEL_CORNER_VECTORS[1]) CreateCornerCylinder(PANEL_CORNER_RADIUS, PANEL_THICKNESS);
		translate(PANEL_CORNER_VECTORS[2]) CreateCornerCylinder(PANEL_CORNER_RADIUS, PANEL_THICKNESS);
		translate(PANEL_CORNER_VECTORS[3]) CreateCornerCylinder(PANEL_CORNER_RADIUS, PANEL_THICKNESS);
	}

	/* *** Create Screw Holes in Corners of Panel? *** */
	if (CREATE_FLAT_SCREW_HOLES)
	{
		CreateFlatScrewHolesInPanel(FLAT_SCREW_HOLE_RADIUS, FLAT_SCREW_HOLE_OFFSET_FROM_EDGE);
	}
	else if (CREATE_TAPER_SCREW_HOLES)
	{
		CreateTaperScrewHolesInPanel(TAPER_TOP_HOLE_RADIUS, TAPER_BOTTOM_HOLE_RADIUS, TAPER_HOLE_FROM_EDGE);
	}
}	// difference()

/*
		***************************************
		*** Code Modules for Panel Creation ***
		***************************************
*/

module CreateCornerCylinder(radius)
{
	// This module is used in two instances:
	// 		1 - Creation of the Panel's outer hull.
	//		2 - Creation of the flat screw holes.
	cylinder(r = radius, h = PANEL_THICKNESS);
}	// CreateCornerCylinder()

module CreateTaperScrewHoles(topRadius, bottomRadius)
{
	// This module is used to create a tapered screw hole.
	cylinder(r1 = bottomRadius, r2 = topRadius, h = PANEL_THICKNESS);
}	// CreateTaperScrewHoles()

module CreateFlatScrewHolesInPanel(screwHoleRadius, holeXYOffset)
{
	// Calculate the required vectors for hole placements.
	leftHolesOffset = FLAT_SCREW_HOLE_OFFSET_FROM_EDGE + holeXYOffset + screwHoleRadius / 2;
	rightHolesOffset = PANEL_WIDTH - screwHoleRadius - TAPER_HOLE_FROM_EDGE;

	SCREW_HOLE_VECTORS = [[leftHolesOffset, leftHolesOffset, 0],
												[leftHolesOffset, PANEL_DEPTH - screwHoleRadius - holeXYOffset, 0],
												[rightHolesOffset, PANEL_DEPTH - screwHoleRadius - holeXYOffset, 0],
												[rightHolesOffset, FLAT_SCREW_HOLE_OFFSET_FROM_EDGE + holeXYOffset + screwHoleRadius / 2, 0]];

	// Create the Flat Screw Holes
	translate(SCREW_HOLE_VECTORS[0]) CreateCornerCylinder(screwHoleRadius);
	translate(SCREW_HOLE_VECTORS[1]) CreateCornerCylinder(screwHoleRadius);
	translate(SCREW_HOLE_VECTORS[2]) CreateCornerCylinder(screwHoleRadius);
	translate(SCREW_HOLE_VECTORS[3]) CreateCornerCylinder(screwHoleRadius);
}	// CreateFlatScrewHoles()

module CreateTaperScrewHolesInPanel(topHoleR, botHoleR, holeXYOffset)
{
	// Calculate the required vectors for hole placements.
	leftHolesOffset = TAPER_HOLE_FROM_EDGE + holeXYOffset + topHoleR / 2;
	rightHolesOffset = PANEL_WIDTH - topHoleR - TAPER_HOLE_FROM_EDGE;

	SCREW_HOLE_VECTORS = [[leftHolesOffset, leftHolesOffset, 0],
												[leftHolesOffset, PANEL_DEPTH - topHoleR - holeXYOffset, 0],
												[rightHolesOffset, PANEL_DEPTH - topHoleR - holeXYOffset, 0],
												[rightHolesOffset, TAPER_HOLE_FROM_EDGE + holeXYOffset + topHoleR / 2, 0]];

	// Create Tapered Screw Holes
	translate(SCREW_HOLE_VECTORS[0]) CreateTaperScrewHoles(TAPER_TOP_HOLE_RADIUS, TAPER_BOTTOM_HOLE_RADIUS);
	translate(SCREW_HOLE_VECTORS[1]) CreateTaperScrewHoles(TAPER_TOP_HOLE_RADIUS, TAPER_BOTTOM_HOLE_RADIUS);
	translate(SCREW_HOLE_VECTORS[2]) CreateTaperScrewHoles(TAPER_TOP_HOLE_RADIUS, TAPER_BOTTOM_HOLE_RADIUS);
	translate(SCREW_HOLE_VECTORS[3]) CreateTaperScrewHoles(TAPER_TOP_HOLE_RADIUS, TAPER_BOTTOM_HOLE_RADIUS);
}	// CreateTaperScrewHoles()
```


[User]
I'm looking to create a parametric gyroscopic keyring, similar to the one I saw on Thingiverse by gianfranco (thing:1307100), but with more flexibility for customization. I want to be able to control the number of rings, the spacing between them, the thickness of the rings, and the overall size.

Here's a breakdown of what I need:

*   **Rings:** I need a series of concentric rings, starting with a center ring or ball. The innermost ring should have a customizable diameter.
*   **Center Hole:** The innermost ring should have an optional hole through the center - so if I don't want the hole, it can be a solid ball.
*   **Ring Width:**  I need to be able to set the width of each ring wall.
*   **Ring Spacing:**  I should be able to specify the gap between each ring.
*   **Number of Rings:** The total number of rings needs to be adjustable.
*   **Height:** I need to be able to set the overall height of the gyro.
*   **Keyring:** I'd like an optional keyring attachment on the outermost ring that I can toggle on or off.
*   **Keyring Orientation:** The keyring should be able to be either vertical or horizontal.
*   **Keyring Size/Shape:** The keyring needs adjustable outer and inner diameters. I'd also like to be able to scale it elliptically, if possible.
*   **Keyring Offset:**  I need to be able to position the keyring slightly inside or outside the outer edge of the outer ring.
*   **Preview:** It would be awesome if there's a way to generate a center-sliced preview to see how the internal structure looks, maybe with some transparency.

I'm looking for a parametric design using OpenSCAD, and I do not have a library preference. Please generate the necessary .scad file or files that will allow for all of these parameters.

[Assistant]
gyroRing.scad:
```
/**
 * This is a parametric version of the excellent Gyroscopic Keyring by
 * gianfranco found here:
 * https://www.thingiverse.com/thing:1307100
 *
 * Thanks gianfranco!
 *
 * I wanted to be able to experiment with number of rings, spacing between
 * rings, sizes, etc. so created this parametric version that allows the
 * customizer in OpenSCAD or the Thingiverse customizer to be used to alter the
 * model.
 *
 * Author : Tom Coetser (Fitzterra) <fitzterra@icave.net>
 *
 * License:
 * This work is licensed under the Creative Commons Attribution 3.0 Unported
 * License. To view a copy of this license, visit
 * http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
 * Commons, PO Box 1866, Mountain View, CA 94042, USA.
 **/

/* [ Generator options ] */
// Rouded surfaces resolution.
$fn = 100;
// Create a center sliced sample view?
sliceCenter = false;

/* [ Gyro parameters ] */
// Diameter for innermost ring or ball.
innerDia = 16;
// Size for hole in inner most ring. Zero will leave it as a hollow ball.
centerHole=4.0;
// Width for ring walls at ring equators.
ringW = 2.0;
// Spacing between rings.
gap = 0.5;
// Number of rings, including center ring or ball.
rings = 6;
// Height for the gyro.
height = 18.0;
// Calculate the Outer Diameter
outerDia = (innerDia/2+ringW + (rings-1)*(gap+ringW))*2;

/* [ Key ring parameters ] */
// Add a key ring?
keyRing = true;
// Keyring orientation?
krOrient = "v"; //[v:Vertical, h:Horizontal]
// Width for keyring - also affects outer diameter
krOuterDia = 9;
// Inner diameter for keyring
krInnerDia = 5;
// Elliptical scaling
krEliptical = 0.5; //[0.1:0.1:1])
// Optional offset (+ or -) for the keyring from the outer Gyro edge.
krOffs = -3;

/**
 * Creates a ball shape with a non solid inside, or with a solid inside and
 * a vertical hole through the middle.
 *
 * param od: Outside diameter
 * param t: Wall thickness for the ball if ctr is non-zero.
 * param ctr: If non-zero, the inside is solid, but a center hole at this
 *            diameter is created in Z direction. If zero, the inside is empty
 *            or hollow.
 **/
module Ball(od, t, ctr=0) {
    difference() {
        // The main solid sphere
        sphere(d=od);
        // Hollow it out or drill a central hole?
        if (ctr==0)
            // We hollow it out like a ball
            sphere(d=od-2*t);
        else
            // We drill a central vertical hole
            cylinder(d=ctr, h=od+0.3, center=true);
    }
}

/**
 * Generates the key ring bit to attach to the outer most ring.
 *
 * @param od: The diameter for the ring used to for the key ring. This affects
 *        both the outer diameter of the ring, as well as the size of the ring
 *        - just try it :-)
 * @param id: Inner diameter of the ring - this is the inner diameter if the
 *        ring is perfectly round and not ellipsed. If it is elliptically
 *        scaled, the true inner diameter would be slightly larger.
 * @param offs: The keyring center would be placed on the outer edge of the outer
 *        gyro ring. This parameter allows it to be offset in or out from the
 *        outer edge.
 * @param fitOD: The outer diameter of the outer gyro ring the keyring will be
 *        fitted to. This is to cut the excess off for a perfect fit.
 * @param orient: Orientation of the keyring: "v" for vertical or "h" for
 *        horizontal.
 **/
module KeyRing(od=krOuterDia, id=krInnerDia, offs=krOffs, fitOD=outerDia, orient=krOrient) {
    // Rotate the final ring 90° around the x axis if needed
    rotate([orient=="v"?90:0, 0, 0])
    // Create the keyring
    difference() {
        // Move it in X if there is an offset
        translate([offs, 0, 0])
            // Use rotate extrude to create a toroid type shape from a possibly
            // elliptical circle.
            rotate_extrude(convexity=10)
                translate([od/2+id/2, 0, 0,])
                    scale([krEliptical, 1, 1])
                        circle(d=od);
        // Use a Ball() shape to cut the part that will be thrown away off the
        // keyring.
        translate([-fitOD/2, 0, 0])
            Ball(fitOD, 1, 0.1);
    }
}

/**
 * Generates the gyro fitted rings based on the parameters.
 *
 * @param id: Inner ring diameter - this is the base size determining the final
 *        object size.
 * @param wt: The width for the ring walls. This translates to the skin thickness
 *        for the ball that then gets cuts. This will be the width of the ring
 *        when cut through at the equator, but obviously would result in
 *        thicker walls when sliced horizontally further away from the center.
 * @param gap: The gap between rings.
 * @param c: The number of rings to generate
 * @param h: The final height for the gyro rings
 * @param ch: If not 0, the central ball used to form the central ring will be
 *        solid with a vertical hole through the middle with diameter of this
 *        parameter. If 0, then the central ball will be hollow with no holes
 *        (unless the height causes a cut through it) in it.
 **/
module GyroRings(id=innerDia, wt=ringW, g=gap, c=rings, h=height, ch=centerHole) {
    od = outerDia;
    difference() {
        // Combine a series of concentric balls and the keyring as one
        union() {
            for (n=[0:c-1]) {
                // Generate concentric balls for each ring
                Ball((id/2+wt + n*(g+wt))*2, wt, n==0?ch:0);
            }
            // Add the keyring if required.
            if(keyRing)
                translate([outerDia/2, 0, 0])
                    KeyRing();
        }
        // Cut the top and bottom to leave the desired height
        translate([0, 0, h/2+od/2])
            cube([od*2, od+0.1, od], center=true);
        translate([0, 0, -h/2-od/2])
            cube([od*2, od+0.1, od], center=true);
    }
}

/**
 * Generates the gyro rings object based on the global parameters.
 **/
module Generate() {
    // Make it a transparent yellow if we're showing a sliced sample
    color(sliceCenter?[255/255, 255/255, 0/255, 0.3]:false)
    difference() {
        // Generate the gyro rings
        GyroRings();
        // Slice in half if we're showing a sliced sample
        if (sliceCenter)
            translate([0, 0, height/2+1])
                cube([outerDia*2, outerDia*2, height+2], center=true);
    }
}

Generate();

```