[User]
I'd like to create a customizable stamp with replaceable inserts. The design should include a main body that acts as the handle and then an area for the stamp itself. The stamp part should have two lines of text that the user can set. There should also be an option to include a circle around the stamp text.

Specifically, here are the parameters I'd like to control:

*   The text for the first row of the stamp.
*   The text for the second row of the stamp.
*   The height of the text pattern (extrusion height).
*   The distance between the rows of text. If the distance is 0, the text should be centered vertically.
*   The font size for both text rows.
*   A boolean to enable or disable an enclosing circle around the text.
*  The diameter of the entire stamp, and of the outer rim of the stamp, and the base, and the stamp height itself.
*   The precision used to make curved portions of the design.

The stamp handle itself should have a base and a slight shoulder area, and the stamp itself should be slightly proud, in that it's slightly above the handle.

I'd like to use the `regular_shapes.scad` library from MCAD for basic shapes such as cylinders. It might be good to parameterize how high the main stamp and base area are too, as well as those diameters and the height of the whole thing.

I'd like this to be a single .scad file, or multiple files that work together, as is most appropriate.

[Assistant]
Replaceable-stamp.scad:
```
include <MCAD/regular_shapes.scad>

// text of the first row
First_row = "My";

// text of the second row
Second_row = "Stamp";

// height of the pattern
Pattern_height = 1.2;

// distance of the rows of text
Rows_distance = 4;

// size of the font
Font_size = 5;

// decide if you want an additional circle around the text
add_circle = true;

Precision=300;

if (add_circle) {
    difference() {
        translate([0,0,4])
            cylinder(d=27.5,h=Pattern_height, $fn=Precision);
        translate([0,0,4])
            cylinder(d=25.5,h=Pattern_height, $fn=Precision);
    }
}

cylinder(d=29.5,h=1.8, $fn=Precision);
cylinder(d=27.5,h=4, $fn=Precision);
translate([0,Rows_distance,4])
    mirror([-1,0,0])
        linear_extrude(Pattern_height)
            text(text=First_row,size=Font_size,halign="center",valign="center");
translate([0,-Rows_distance,4])
    mirror([-1,0,0])
        linear_extrude(Pattern_height)
            text(Second_row,size=Font_size,halign="center",valign="center");

```


[User]
Hi! I'm looking for an OpenSCAD design for a customizable sponge holder. I want it to have a few key features:

First, I need to be able to specify the dimensions of the sponge itself (width, depth, and height) as parameters. Then, the box should have some clearance around the sponge so it isn't a super tight fit, and I want to be able to control that margin as a parameter as well. I'd also like to round the corners of the box, so a fillet parameter would be great for that.

The box will need to have a bottom, and then some kind of top section which also acts as a grate for the sponge to sit on. I want the grate to be customizable in a couple of ways: I'd like to choose the shape of the openings (rectangle, round, or maybe even honeycomb). I also need to specify the size of those grate openings.

Finally, I would like to specify how the top and bottom parts of the box connect to one another. I was thinking maybe a guide system or maybe just a lip that fits over the bottom, and there also needs to be some tolerance there for easy assembly.

It would be awesome if I could specify the wall thickness for all of these parts and set a roundness parameter for smooth corners. I want the design to be fully parametric.

Could you generate an OpenSCAD file for me? If you need to separate it into multiple files, that would be okay, too, but please use standard OpenSCAD syntax and libraries (like maybe MCAD, BOSL, or BOSL2 if that helps, but I'm not sure if you need any libraries). I'm really excited to see what you come up with! Thanks!

[Assistant]
spongebox.scad:
```
// Subject: Spongebox
//
// This 3D model is published under CC BY-SA 4.0 by Stefan Schönberger <mail@sniner.net>

/* [Sponge dimensions] */

SPONGE_WIDTH        = 60; //[20:120]
SPONGE_DEPTH        = 85; //[20:120]
SPONGE_HEIGHT       = 30; //[20:60]

/* [Box parameters] */

// Space between sponge and box walls
MARGIN              = 3; //[1:10]

// Radius of the corners
FILLET              = 6; //[1:0.1:10]

// Thickness of the box walls
WALL_THICKNESS      = 2.0; //[1.6:0.1:3.0]

// Size of the grate holes
GRATE_OPENING       = 2.5; //[2.0:0.5:5.0]

// Form of the grate holes
GRATE_OPENING_FORM  = "rectangle"; //[rectangle,round,honeycomb]

// I have no idea what it is called in english ;-) Try it out.
INSERTION_FORM = "guides"; //[guides,wall,none]

// Tolerance of this thingy. See above.
INSERTION_TOLERANCE = 0.2; //[0.1:0.1:0.4]

// Determines the smoothness of round objects. Raise to 96 for printing.
ROUNDNESS           = 24; //[24,48,96]

// Which part to display/print or "demo" to give an idea of the spongebox
PART                = "demo"; //[grate,bottom,top,demo,none]

/* [Hidden] */

DRAINING_RACK_HEIGHT = 10;
INSERTION_DEPTH = 2.4;

module copy_mirror(v=[1,0,0])
{
    children();
    mirror(v=v) children();
}

function _hyperbola(x) = sqrt(1.0 + x*x) - 1.0;
function _hyperbola_curve(n, a, s) = [ for (i = [0 : n]) [1.0/n*i, s*_hyperbola(a/n*i)] ];
function _hyperbola_path(n, a) = _hyperbola_curve(n=floor(n), a=a, s=1.0/_hyperbola(a));
function _hyperbola_polygon(n, a, sx=1.0, sy=1.0, ox=0.0, oy=0.0) =
    concat([[0, 0]], [ for (p = _hyperbola_path(n, a)) [sx*p[1]+ox, sy*p[0]+oy] ], [ [0, sy], [0, 0] ]);

module hyperbola(w, h, t, a=1.0, n)
{
    segs = max(is_undef(n) ? $fn : n, 4);
    linear_extrude(t)
    polygon(_hyperbola_polygon(n=segs, a=a, sx=w, sy=h, ox=1));
}

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

module round_cube(width, depth, height, fillet)
{
    linear_extrude(height)
        hull() {
            copy_mirror([0, 1, 0])
            copy_mirror()
            translate([width/2-fillet, depth/2-fillet, 0]) circle(r=fillet);
        }
}

module guide(width, height, thickness=1, down=false)
{
    translate(down ? [0, -thickness/2, height] : [0, thickness/2, 0])
    rotate(down ? [-90, 0, 0] : [90, 0, 0])
    linear_extrude(thickness) hull() copy_mirror() translate([width/2-height, 0, 0])
    intersection() {
        circle(r=height);
        translate([-height-1, 0, 0]) square([2*height+2, height+1], center=false);
    }
}

module half_box(width, depth, height, fillet, thickness)
{
    difference() {
        round_cube(
            width=width,
            depth=depth,
            height=height,
            fillet=fillet
        );
        translate([0, 0, thickness])
        round_cube(
            width=width-2*thickness,
            depth=depth-2*thickness,
            height=height,
            fillet=fillet-thickness
        );
    }
}

module box_walls(width, depth, height, fillet, thickness)
{
    difference() {
        round_cube(
            width=width,
            depth=depth,
            height=height,
            fillet=fillet
        );
        translate([0, 0, -1])
        round_cube(
            width=width-2*thickness,
            depth=depth-2*thickness,
            height=height+2,
            fillet=fillet-thickness
        );
    }
}

module grid(width, depth, dist, center=true)
{
    _w = width;
    _d = is_undef(depth) ? _w : depth;
    _dist = is_undef(dist) ? 1 : dist;
    _n_w = floor(_w/_dist);
    _n_d = floor(_d/_dist);
    _w0 = (_w - _n_w*_dist)/2;
    _d0 = (_d - _n_d*_dist)/2;
    translate(center ? [-_w/2, -_d/2, 0] : [0, 0, 0])
    for (d=[1:_n_d-1]) {
        for (w=[1:_n_w-1]) {
            translate([_w0+w*_dist, _d0+d*_dist, 0]) children();
        }
    }
}

module cutout(width, height, thickness)
{
    translate([0, -thickness/2, 0])
    rotate([-90, 0, 0])
    translate([0, -height-2, 0])
    difference() {
        translate([-width/2, 1, 0])
            cube([width, height+1, thickness]);
        translate([0, 0, -1]) copy_mirror() rotate(90)
            hyperbola(height+1, width/2+0.01, thickness+2);
    }
}

module grate(width, depth, height, fillet, hole, thickness)
{
    difference() {
        half_box(width, depth, height, fillet, thickness);
        grid(width-fillet, depth-fillet, dist=hole+thickness)
            if (GRATE_OPENING_FORM=="round")
                cylinder(d=hole, h=height, center=true);
            else if (GRATE_OPENING_FORM=="honeycomb")
                cylinder(d=hole, h=height, center=true, $fn=6);
            else
                cube([hole, hole, height], center=true);
    }
}

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

module grate_part(facedown=true)
{
    width = SPONGE_WIDTH+2*(MARGIN+WALL_THICKNESS);
    depth = SPONGE_DEPTH+2*(MARGIN+WALL_THICKNESS);
    height = DRAINING_RACK_HEIGHT+WALL_THICKNESS;
    fillet = max(FILLET, WALL_THICKNESS+0.01);
    translate(facedown ? [0, 0, 0] : [0, 0, height])
    rotate(facedown ? 0 : [180, 0, 0])
    difference() {
        grate(
            width = width,
            depth = depth,
            height = height,
            fillet = fillet,
            hole = GRATE_OPENING,
            thickness = WALL_THICKNESS
        );
        if (true) {
            translate([0, 0, height+0.01]) {
                copy_mirror([0, 1, 0])
                translate([0, depth/2-WALL_THICKNESS/2, 0])
                rotate([180, 0, 0])
                cutout(
                    width = width-2*fillet,
                    height = (height-WALL_THICKNESS)*2/3,
                    thickness = WALL_THICKNESS+2
                );
                copy_mirror([1, 0, 0])
                translate([width/2-WALL_THICKNESS/2, 0, 0])
                rotate([180, 0, 90])
                cutout(
                    width = depth-2*fillet,
                    height = (height-WALL_THICKNESS)*2/3,
                    thickness = WALL_THICKNESS+2
                );
            }
        }
    }
}

module bottom_part()
{
    gap = 2*WALL_THICKNESS;
    width = SPONGE_WIDTH+2*(MARGIN+WALL_THICKNESS+gap);
    depth = SPONGE_DEPTH+2*(MARGIN+WALL_THICKNESS+gap);
    height = max(2*DRAINING_RACK_HEIGHT, SPONGE_HEIGHT/3*2);
    difference() {
        half_box(
            width = width,
            depth = depth,
            height = height,
            fillet = FILLET+gap,
            thickness = WALL_THICKNESS
        );
        translate([0, 0, height-INSERTION_DEPTH]) {
            if (INSERTION_FORM=="guides") {
                copy_mirror([1, 0, 0])
                translate([width/2-WALL_THICKNESS+INSERTION_TOLERANCE/2, 0, -INSERTION_TOLERANCE/2])
                rotate(90)
                guide(
                    width = depth-2*(FILLET+gap),
                    height = INSERTION_DEPTH+INSERTION_TOLERANCE/2+0.01,
                    thickness = WALL_THICKNESS,
                    down = true
                );
                copy_mirror([0, 1, 0])
                translate([0, depth/2-WALL_THICKNESS+INSERTION_TOLERANCE/2, -INSERTION_TOLERANCE/2])
                guide(
                    width = width-2*(FILLET+gap),
                    height = INSERTION_DEPTH+INSERTION_TOLERANCE/2+0.01,
                    thickness = WALL_THICKNESS,
                    down = true
                );
            } else if (INSERTION_FORM=="wall") {
                box_walls(
                    width = width - WALL_THICKNESS + INSERTION_TOLERANCE,
                    depth = depth - WALL_THICKNESS + INSERTION_TOLERANCE,
                    height = INSERTION_DEPTH+1,
                    fillet = FILLET+gap - WALL_THICKNESS/2 + INSERTION_TOLERANCE/2,
                    thickness = WALL_THICKNESS
                );
            }
        }
    }
}

module top_part(facedown=true)
{
    gap = 2*WALL_THICKNESS;
    bottom_height = max(2*DRAINING_RACK_HEIGHT, SPONGE_HEIGHT/3*2);
    grate_height = DRAINING_RACK_HEIGHT+WALL_THICKNESS;
    width = SPONGE_WIDTH+2*(MARGIN+WALL_THICKNESS+gap);
    depth = SPONGE_DEPTH+2*(MARGIN+WALL_THICKNESS+gap);
    height = (SPONGE_HEIGHT+grate_height+2*WALL_THICKNESS+MARGIN) - bottom_height;
    translate(facedown ? [0, 0, 0] : [0, 0, height])
    rotate(facedown ? 0 : [180, 0, 0]) {
        difference() {
            half_box(
                width = width,
                depth = depth,
                height = height+(INSERTION_FORM=="wall" ? INSERTION_DEPTH : 0),
                fillet = FILLET+gap,
                thickness = WALL_THICKNESS
            );
            if (INSERTION_FORM=="wall") {
                translate([0, 0, height+INSERTION_TOLERANCE/2])
                box_walls(
                    width = width + WALL_THICKNESS - INSERTION_TOLERANCE,
                    depth = depth + WALL_THICKNESS - INSERTION_TOLERANCE,
                    height = INSERTION_DEPTH+1,
                    fillet = FILLET+gap + WALL_THICKNESS/2 - INSERTION_TOLERANCE/2,
                    thickness = WALL_THICKNESS
                );
            }
        }
        if (INSERTION_FORM=="guides") {
            guide_thickness = WALL_THICKNESS/2 - INSERTION_TOLERANCE/2;
            translate([0, 0, height]) {
                copy_mirror([1, 0, 0])
                translate([width/2-WALL_THICKNESS+guide_thickness/2, 0, 0])
                rotate(90)
                guide(
                    width = depth-2*(FILLET+gap),
                    height = INSERTION_DEPTH,
                    thickness = guide_thickness
                );
                copy_mirror([0, 1, 0])
                translate([0, depth/2-WALL_THICKNESS+guide_thickness/2, 0])
                guide(
                    width = width-2*(FILLET+gap),
                    height = INSERTION_DEPTH,
                    thickness = guide_thickness
                );
            }
        }
    }
}

module sponge()
{
    translate([0, 0, SPONGE_HEIGHT/2])
    cube([SPONGE_WIDTH, SPONGE_DEPTH, SPONGE_HEIGHT], center=true);
}

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

$fn = ROUNDNESS;

if (PART=="grate") {
    grate_part();
} else if (PART=="bottom") {
    bottom_part();
} else if (PART=="top") {
    top_part();
} else if (PART=="demo") {
    bottom_part();
    translate([0, 0, WALL_THICKNESS]) grate_part(facedown=false);
    %translate([0, 0, DRAINING_RACK_HEIGHT+2*WALL_THICKNESS+0.1]) color("green", 0.2) sponge();
    translate([0, 0, SPONGE_HEIGHT*2]) rotate([20, 0, 0]) top_part(facedown=false);
}
```


[User]
Hey, I'm looking to create a parametric base for miniatures, specifically for a game called Gaslands. I need it to be a rounded rectangle, and I'd like to be able to control its length, width, and thickness. It should also have the option for a magnet hole in the center.

So, the parameters should be:

*   `length`: Overall length of the base.
*   `width`: Overall width of the base.
*   `thickness`: Thickness of the base.
*   `radius`: Radius of the rounded corners.
*   `magnet`: Boolean to include a magnet hole (true/false)
*   `mThickness`: Thickness of the magnet hole, if `magnet` is true.
*   `mDiameter`: Diameter of the magnet hole, if `magnet` is true.

I would need all of these to be user-adjustable in the OpenSCAD interface. The base should be a simple rounded rectangle with a circular hole if the magnet option is selected. Could you generate the OpenSCAD code for this? I don't need any extra libraries, just the standard functions.

[Assistant]
Gaslandsbase.scad:
```
/*[ Gaslands Base ]*/
//Length of Base
length=60; // [30:200]

//Width of Base
width=30; // [20:80]

//Thickness of base
thickness=3; //[2:8]

//Corner Radius
radius=1.6; // [0.4:0.4:3.2]

//Magnet Hole
magnet=true;

//Magnet Thickness
mThickness=2;// [1:3]

//Magnet diameter
mDiameter=6;// [4:15]

rlength = length - (radius);
rwidth = width - (radius);

if(magnet){
    difference(){
        roundedRect([rlength,rwidth,thickness],radius, $fn=50);
        translate([0,0,mThickness/2])
        cylinder(h=mThickness,d=mDiameter,center=true,$fn=50);
    }
}else{
    roundedRect([rlength,rwidth,thickness],radius, $fn=50);

}

module roundedRect(size, radius)
{
x = size[0];
y = size[1];
z = size[2];

linear_extrude(height=z)
hull()
{
    // place 4 circles in the corners, with the given radius
    translate([(-x/2)+(radius/2), (-y/2)+(radius/2), 0])
    circle(r=radius);

    translate([(x/2)-(radius/2), (-y/2)+(radius/2), 0])
    circle(r=radius);

    translate([(-x/2)+(radius/2), (y/2)-(radius/2), 0])
    circle(r=radius);

    translate([(x/2)-(radius/2), (y/2)-(radius/2), 0])
    circle(r=radius);
}
}
```


[User]
Hi! I'm looking for help designing a customizable heddle bar for weaving. I've seen some designs online, and I even found an existing .scad file, but I want to make something myself from scratch that's flexible enough for different weaving projects.

The heddle bar should have a main body with alternating deep and shallow grooves that will hold the warp threads. When the bar is rotated, it should raise either the odd or even threads, creating a shed for the shuttle to pass through. There also need to be tabs on the ends for easy rotation.

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

*   **Groove width:** This needs to be adjustable to accommodate different yarn thicknesses. I think a range from 0.5mm to 8mm should work, with 0.1mm increments.
*   **Number of grooves:** I need to set how many warp threads the bar can hold. A range from 3 to 148 slots should be sufficient, in increments of 1.
*   **Shed width:** This is the space between the raised and lowered warp threads, and it's crucial for the shuttle to pass through easily. It should have a range of 3 to 30mm.
*   **Tab length:** This is the length of the tabs on each end used to rotate the bar, with a range of 0 to 30mm with 2mm increments.
*   **Tab thickness:** How thick the end tabs are; a range of 1 to 5mm, in 0.5mm increments, sounds right.
*  **Height:** This will be the height of the main body of the bar. 3 to 12mm should be a good range.
*   **Shallowness:** This parameter controls the curve of the top surface of the heddle bar; the bigger the number, the flatter the belly of the curved top. It needs a range from 1 to 3, in 0.1mm increments.

The heddle bar should have a rounded top to help prevent threads from slipping out of the correct slots. The design should make sure that the deep grooves alternate, such that every other deep groove is on the front side, and the alternating deep grooves are on the back. I want the shallow grooves to do the same, but in an opposite fashion.

I also need it to be parametric, of course, so I can easily change these values. The design should be in OpenSCAD. I don't have any particular libraries I want you to use.

[Assistant]
heddle_bar_customizable.scad:
```
// by KAH in 2020. Updated Jan 2023 to make code more parametric and to work with Thingiverse's customizer tool.
// to adjust for thicker or thinner yarn, play with groove_width; to change the number of strands the heddle bar can accommodate (and therefore its length) play with n_grooves. If you want to modify the design itself, you can also play around with shed_width, tab_length, tab_thickness, height, and shallowness.

groove_width = 2; //[0.5:0.1:8]
// width of the grooves and depth of the shallow grooves. Needs are determined by the thickness of the yarn. 2 mm is good for average knitting yarn.

n_grooves = 20; //[3:1:148]
// number of slots for the warp strands

shed_width = 8; //[3:30]
// the size of the space created between the raised and lowered strands of the warp (the "shed"). A larger shuttle requires a greater shed width. If the shed width was zero, "shallow" and "deep" grooves would be equal in size (and useless).

tab_length = 8; //[0:2:30]
// length of the tabs on the ends, for easy rotation. Bigger thumbs need bigger tabs.

tab_thickness = 2; //[1:0.5:5]
// thickness of the thumb tabs; limited only by sturdiness. I found 2 mm was plenty solid.

height = 6;  //[3:12]
// height of the rectangular prism, not including rounded top

shallowness = 1.14; //[1:0.1:3]
// Curve parameter for rounded top. Set to 1.0 to make the curve as pronounced as possible. Increase to make it more shallow. This belly helps keep the strands from slipping into the wrong grooves when the heddle bar is rotated.

width = shed_width + groove_width * 3; // measurement from front to back of heddle bar.
radius = width / 2  * shallowness; // radius of the cylinder used to make the curved top. larger radius, flatter belly on top.
length = (n_grooves*2 + 3) * groove_width;
cylinder_drop = sqrt(radius*radius - (width/2)*(width/2));

difference(){

// create the rectangular prism with rounded top from which everything else will be subtracted (except the tabs)
    union(){
        cube([length, width, height]);
        intersection(){
            translate([0, width/2, height-cylinder_drop])
            rotate([0, 90, 0])
            cylinder(r=radius, h=length, $fn=160);
            cube([length, width, 4*radius]);
        }
    }

    // odd-numbered deep grooves (cut from front)
    for (i = [groove_width*2:groove_width*4:length-2*groove_width]){
        translate([i, -groove_width*2, -1])
        cube([groove_width, width, width+20]);
    }

    // odd-numbered shallow grooves (cut from back)
    for (i = [groove_width*2:groove_width*4:length-2*groove_width]){
        translate([i, width-groove_width, -1])
        cube([groove_width, width, width+20]);
    }

    // even-numbered deep grooves (cut from back)
    for (i = [groove_width*4:groove_width*4:length-2*groove_width]){
        translate([i, groove_width*2, -1])
        cube([groove_width, width, width+20]);
    }

    // even-numbered shallow grooves (cut from front)
    for (i = [groove_width*4:groove_width*4:length-2*groove_width]){
        translate([i, groove_width-width, -1])
        cube([groove_width, width, width+20]);
    }

    // slices through top
    for (i = [2*groove_width:2*groove_width:length-2*groove_width]){
        translate([i, -1, height-2])
        cube([groove_width, width+2, width+20]);
    }

}

// add the thumb tab at the origin (left)
translate([-tab_length, 0, 0])
cube([tab_length, width, tab_thickness]);

// add the thumb tab at the far end (right)
translate([length, 0, 0])
cube([tab_length, width, tab_thickness]);

```


[User]
I'd like to create a parametric soundbar mounting bracket for my TV, and I've got a basic idea laid out. I'd like it to be designed in OpenSCAD. I need to be able to adjust quite a few things, so it's important that it is fully parametric.

Specifically, I need to be able to specify the depth and height of my soundbar. I also need to define parameters for the maximum thickness of my TV, the depth of its back support area, and whether the back of my TV tapers. If the TV tapers, I'll also need to provide the top thickness of the TV, the taper's start depth, and its overall depth.

For the bracket itself, I need to define its thickness, width, the length of the soundbar's top finger (the part that holds it up), the height of the soundbar's front finger, and the depth of the TV's front finger. I'd like to also have a general corner radius parameter.

I'll also need some advanced options, like the radius of the soundbar's top finger, and the inside angle for that finger.

I am not planning on using any external libraries, so everything should be created from scratch with basic OpenSCAD shapes.

Could you please create the SCAD file for this?

[Assistant]
soundbar_tv_mount.scad:
```
/* [Soundbar Dimensions] */
Soundbar_Depth = 60.2;
Soundbar_Height = 55.2;

/* [TV Dimensions] */
TV_Max_Thickness = 29.5;
TV_Back_Support_Depth = 50;
// Taper settings below will only apply if Has Taper = Yes
Has_Taper = "Yes"; //[Yes, No]
TV_Top_Thickness = 25.5;
TV_Taper_Start_Depth = 2.5;
TV_Taper_Depth = 19.5;

/* [Bracket Options] */
Bracket_Thickness = 5.0;
Bracket_Width = 5.0;
Soundbar_Top_Finger_Length = 22;
Soundbar_Front_Finger_Height = 15;
TV_Front_Finger_Depth = 10;
Corner_Radius = 2.0;

/* [Advanced Options] */
Soundbar_Top_Finger_Radius = 10;
Soundbar_Top_Finger_Inside_Angle = 90; // [0:180]

function a() = Soundbar_Depth;
function b() = Soundbar_Height;
function c() = TV_Top_Thickness;
function d() = TV_Back_Support_Depth;
function e() = TV_Taper_Start_Depth;
function f() = TV_Taper_Depth;
function g() = TV_Max_Thickness;
function h() = Bracket_Thickness; // xy
function i() = Bracket_Width; // z
function j() = Soundbar_Top_Finger_Length;
function k() = Soundbar_Front_Finger_Height;
function l() = TV_Front_Finger_Depth;
function m() = TV_Front_Finger_Nub_Depth;
function n() = Soundbar_Top_Finger_Radius;
function o() = Soundbar_Top_Finger_Inside_Angle;
function r() = TV_Front_Finger_Nub_Radius;
function s() = TV_Finger_Inside_Angle;
function t() = Corner_Radius;

//validation
assert(h()/2 > t(),"Bracket width must be more than 2x Corner Radius!");
assert(j()>=n(),"Soundbar Top Finger Length must be equal to or greater than Soundbar Top Finger Radius!");

// basic shape
color("orange") union() {
    // tv back leg
    hull() {
        translate([g() + h(),0,0]) rounded_prism([h(),h(),i()]);
        translate([g() + h(),d(),0]) rounded_prism([h(),h(),i()]);
        }

    // soundbar bottom span
    hull() {
        translate([0,+d(),0]) rounded_prism([h(),h(),i()]);
        translate([h()+a(),d(),0]) rounded_prism([h(),h(),i()]);
        }

    // soundbar back vertical span
    hull() {
        translate([h()*1+a(),d(),0]) rounded_prism([h(),h(),i()]);
        translate([h()*1+a(),h()+d() + b(),0]) rounded_prism([h(),h(),i()]);
        }

    // soundbar front finger
 /*   hull() {
        sb_ff_y = sin(q()) * (k()+h()/2);
        sb_ff_x = cos(q()) * (k()+h()/2);
        //translate([h()/2 + sb_ff_x,d()+h()/2 + sb_ff_y,0]) cylinder(d=h(),h=i(),$fn=60);
        translate([sb_ff_x,d() + sb_ff_y,0]) rounded_prism([h(),h(),i()]);
        //translate([h()/2,d()+h()/2,0]) cylinder(d=h(),h=i(),$fn=60);
        translate([0,d()+h()/2,0]) cube([h(),h(),i()]);
        } */
         hull() {
        //translate([h()/2 + sb_ff_x,d()+h()/2 + sb_ff_y,0]) cylinder(d=h(),h=i(),$fn=60);
        translate([0,d()+k()+(h()/2),0]) rounded_prism([h(),h(),i()]);
        //translate([h()/2,d()+h()/2,0]) cylinder(d=h(),h=i(),$fn=60);
        translate([0,d()+h()/2,0]) cube([h(),h(),i()]);
        }

    // tv top finger
    hull() {
        translate([0,d(),0]) cube([h(),h(),i()]);
        translate([0,d()-l()-h(),0]) rounded_prism([h(),h(),i()]);
        }

    // soundbar top finger
        /* 1. create top finger using hull
           2. create rounded nub using intersection and soundbar_top_finger_radius
           3. create additional hull top finger to fill in (i.e. small radius and/or long finger)*/

     translate([h()+a()+(h()/2),h()+d()+b()+(h()/2),0]) rotate((90-o()),[0,0,1]) hull() {
        intersection() {
            translate([(-j())+n()-(h()/2),n()-(h()/2),0]) cylinder(r=n(),h=i(),$fn=60); // cylinder to create nub radius
            hull() {
                translate([-j()-(h()/2),-h()/2,0]) rounded_prism([h(),h(),i()]); // left side
                //translate([h()+a() - sb_tf_x,h() + d() + b() + sb_tf_y,0]) rounded_prism([h(),h(),i()]);
                translate([(-h()/2),-h()/2,0]) rounded_prism([h(),h(),i()]); // right side
                }
        }
        translate([(-h()/2),-h()/2,0]) rounded_prism([h(),h(),i()]); // right side

    }

    // taper
    if (Has_Taper=="Yes") {
        hull() {
            translate([c()+h(),d()-e(),0]) cube([g()-c()+h()/2,e(),i()]);
            translate([g()+h(),d()-e()-f()]) cube([h(),h(),i()]);
            }
        }
}

// additional modules
module rounded_square( width, radius_corner ) {
	translate( [ radius_corner, radius_corner, 0 ] )
		minkowski() {
			square( width - 2 * radius_corner );
			circle( radius_corner );
		}
}

module rounded_rectangle(x_len, y_len, radius_corner, radius_elements ) {
	translate( [ radius_corner, radius_corner, 0 ] )
		minkowski() {
			square( x_len - 2 * radius_corner , y_len - 2 * radius_corner );
			circle( radius_corner , $fn = radius_elements);
		}
}

module rounded_prism(size = [0,0,0], radius = t(), radius_elements = 60) {
    linear_extrude(height = size.z) rounded_rectangle(size.x, size.y, radius, radius_elements);
}

```


[User]
Hi, I'm looking for an OpenSCAD design for a customizable fence post cap. I need it to be rectangular, with a flat top that transitions into a pyramid or diamond shape on the top. The cap should have inner dimensions to fit snugly around the post and a user-defined wall thickness. I also need the corners to be rounded both on the outside and inside edges. I would like to also have the option of adding screw holes on two sides of the cap. Could you generate the OpenSCAD file for that?

Specifically:

*   **Shape:** Rectangular base with a flat square on top, transitioning to a pyramid/diamond shape.
*   **Dimensions:**
    *   Inner width A and B (for the post, with tolerance).
    *   Inner height.
    *   Material thickness.
    *   Height of the pyramid/diamond shape.
    *   Size of the flat square at the top of the pyramid.
*   **Rounding:**
    *   Radius for outside corners.
    *   Radius for inside corners.
*   **Screws:**
    *   Option to include screw holes on sides A and B, with user-defined diameters (and the option to set to 0 for none).

I'd like to be able to control the smoothness of the curves (using `$fn`). I don't need any specific library files, just the main .scad file. Thanks!

[Assistant]
postcap1.scad:
```
//By Coat, 08-jul-2018
//Licensed under the Creative Commons - Attribution license.
//Square Fence post cap

//Parameters

/* [Measurements] */
//Innerwidth side A, use some tolerance
inwidthA = 52;
//Innerwidth side B, use some tolerance
inwidthB = 72;
//Innerheight
inheight = 20;
//Thickness material
thickmat = 3;
/* [Top] */
//Height pyramid/diamondshape on top
pyrheight = 10;
//Size of flat square on top
flatsize = 10;
/* [Rounding and smoothnes] */
//Roundingradius corners outside
cro = 3;
//Roundingradius corners inside
cri = 0;
//Smoothness
$fn = 16;
/* [Screwholes] */
//diameter screwhole side A and B (0=none)
dscrewA = 4;
dscrewB = 4;

/* [Hidden] */
wc = 0.01;   //working constant

//main
difference()
{
  translate([0,0,(inheight+thickmat-cro)/2])   //outer
  {
    minkowski()
    {
      hull()
      {
        cube([inwidthA+thickmat*2-cro*2, inwidthB+thickmat*2-cro*2, inheight+thickmat-cro],center = true);
        translate([0,0,(inheight+thickmat)/2+pyrheight-cro/2]) //top flat size Pyramid
          cube([flatsize,flatsize,wc],center=true);
      }
      sphere(r=cro);
    }
  }
  minkowski()    //inner
  {
    cube([inwidthA-cri*2,inwidthB-cri*2, inheight*2],center=true);
    cylinder(r=cri,h=wc);
  }

  if (dscrewA>0)    //screwholes side A
  {
    translate([0,0,inheight/2])
      rotate([0,90,0])
        cylinder(d=dscrewA,h=inwidthA*2,center=true,$fn=20);
  }
  if (dscrewB>0)  //screwholes side B
  {
    translate([0,0,inheight/2])
      rotate([90,0,0])
        cylinder(d=dscrewB, h = inwidthB*2, center=true, $fn=20);
  }
  translate([0,0,-inheight])   //cutting block bottom for minkowski residu
    cube([inwidthA*2,inwidthB*2, inheight*2], center = true);
}
```


[User]
Okay, I'm looking to get a custom stitch spacing wheel generator created. I need something that can create a wheel with evenly spaced spikes around the circumference, primarily for leatherworking.

Here are the main parameters I need to control:

*   **Stitches Per Inch (or cm):** This determines how many spikes are on the wheel per inch or centimeter. I need to be able to specify this as a float with a high degree of precision, like 5.5 stitches per inch, or 2.17 stitches per centimeter, as an example. I'd like to be able to toggle this input between inches and centimeters, so I can switch easily between units.

*   **Axel Diameter:** This is the diameter of the hole in the center of the wheel for an axle.  This needs to be adjustable.

*   **Target Wheel Diameter:** I need to be able to specify a *target* diameter for the wheel. It's fine if the actual diameter needs to adjust slightly to get an exact multiple of the stitch spacing, but it should aim for the number I provide as the closest possible target.

*   **Wheel Thickness:**  The thickness of the wheel itself.

*   **Spike Height:** The height of the spikes.

Here are some of the design considerations:

*   **Spike Placement:** The spikes should lie flat on the bottom side, so I can easily follow patterns and lines. It's important that the points are aligned to the edge so they can follow a guide closely. I don't need a complex point.

*   **Diameter Adjustment:**  The program should calculate the *actual* diameter to ensure the circumference is a multiple of the stitch size.  I'd want to know the calculated circumference so that I can verify it.

*   **Clearance:** The design needs to ensure there's sufficient material around the axle hole to support the spikes. For example, a minimum of around 0.15 inches (or 0.5 cm) from the hole to where the spikes start for structural integrity.

*   **Labeling:**  I'd like the number of stitches per inch (or cm) printed on both the top and bottom of the wheel, so that I can easily see the size of the wheel.  The top should be raised and the bottom should be recessed to enable readability in any circumstance.

*   **Error Checking:**  It should validate the parameters, such as ensuring that the AxelDiameter isn't so large that the spikes don't have enough room to be added. And make sure the spikes are big enough and have enough room on the wheel to be created.

*   **Spike count/angle:** It would be useful if the calculated spike count and the angle of the spike layout was available during the parameter calculation.

*   **Multiple Wheels:** It would be great if it was easy to generate a list of wheel sizes without manual arranging.  Maybe a grid-like method?

I don't need any extra libraries.  I'd like the program to output an STL of the wheel described above, and ideally, a way to generate a series of these wheels with one single run of OpenSCAD.

[Assistant]
StitchSpacingWheel.scad:
```
//                                                   Leathe rstitch spacing wheel
// Creates a wheel with spikes every 1/Xth of an inch.
// The wheel is as close to the requested diameter as possible.  Usually slightly bigger.  Unless you request a diameter who's
// circumference is an integer multiple of the stitch size desired we must find one for you that is an integer multiple.
// For example, a target diameter of 1" has a circumference of 3.14" and 4 spikes per inch requires a circumference that is a
// multiple of 0.25" so a diameter of 1.035" is used to achieve a circumference of 3.25, resulting in a ring of 13 spikes.
// (13 * .25) = 3.25 = (1.035 * 3.14)
// The hole (for an axle) can be any size that leaves at least 0.2" of material to support the spikes.

//                             Parameters

// Determnes how close together spikes are on wheel
StitchesPerInch = 5.5;      // [1:0.01:20]
// Size of hole at center of wheel (inches)
AxelDiameter = 0.25;        // [.01:.01:1]
// Wheel's diameter in inches (roughly)
TargetWheelDiameter = 1;    // [0.5:.01:5]
// You may want a thick or thin wheel
WheelThickness = 0.1;       // [.05:.01:.5]
// For longer or shorter spikes on wheel
SpikeHeight = 0.3;          // [.05:.01:.75]
// Interpret above as CM rather than inches.
CM = 0;                 // [0:Inches, 1:CM]

if(StitchesPerInch<1)
  GenerateAll();
  else
    MAIN(StitchesPerInch,AxelDiameter,TargetWheelDiameter,WheelThickness);

module GenerateAll(){
  List = [9 ,8 ,7 ,6.5 ,6 ,5.5 ,5 ,4.5 ,4.25 ,4 ,3.75 ,3.5 ,3.25 ,3 ,2.9 ,2.8 ,2.7 ,2.6 ,2.5 ,2.4]; //
  Xmax = floor(sqrt(len(List)));
  Ymax = ceil(sqrt(len(List)));
  echo(Count=len(List),Xmax=Xmax,Ymax=Ymax);
  Spacing = 1.05;
  Offset = 0.55;
  for(x=[0:(Xmax)*Ymax<len(List)?Xmax:Xmax-1]){
    for(y=[0:Ymax-1]){
      Item = (x*Ymax)+y;
      echo(x=x,y=y,Item=Item);
      if (Item < len(List))
        // Attempt to array a list of wheels compactly without manual spacing or rotation
        translate([Imperial(x*Spacing+Offset+((y%2)*(Spacing/2))),Imperial(y*(Spacing*0.9)+Offset),0]) MAIN(List[Item]);
    }
  }
}

module MAIN(StitchesPerInch = 2.5, AxelDiameter = 0.25, TargetWheelDiameter = 1, WheelThickness = 0.1) {
  //                        Calculated values
  StitchSize = 1/StitchesPerInch;
  SpikeCount = ceil(Circumference(TargetWheelDiameter)/StitchSize);
  SpikeCircleRadius = StitchSize /(2 * sin(180/SpikeCount));
  HolderDiameter = SpikeCircleRadius*2 - SpikeHeight;
  Degrees = DegreesInCircle()/SpikeCount;

  // Parameter validity checks
  TestValues(AxelDiameter,WheelThickness,SpikeHeight,StitchSize,SpikeCount,HolderDiameter);

  // Rotate item to prevent spike being on an even multiple of 180 so they don't clash so easily in X direction
  rotate([0,0,(Degrees/2)-(180%Degrees)]){
    // Inset spi label on the bottom too
    SubtractSpiLabel(StitchesPerInch,AxelDiameter,WheelThickness) {
    // Build item
      MakeWheel(WheelThickness,HolderDiameter,SpikeCount,AxelDiameter);
      PlotSpikes(HolderDiameter,SpikeCount,SpikeHeight/2) Spike(WheelThickness,SpikeHeight);
      AddSpiLabel(StitchesPerInch,AxelDiameter,WheelThickness);
    }
  }
}
/***********************************
            Functions
************************************/
function Pi() = 3.14159267;  // builtin version doesn't have enough decimal places
function Circumference(Diameter) = Diameter * Pi();
function Diameter(Circumference) = Circumference / Pi();
function Imperial(Inches) = CM?10*Inches:25.4*Inches;
function DegreesInCircle() = 360;

/***********************************
             Modules
************************************/
module AddSpiLabel(SPI,AxelDiameter,WheelThickness) {
  Yval = Imperial(AxelDiameter/2);
  Zval = Imperial(WheelThickness);
    translate([0,Yval,Zval]) LabelText(SPI);
}

module SubtractSpiLabel(SPI,AxelDiameter,WheelThickness){
  Yval = Imperial(AxelDiameter/2);
  difference(){
    children();
    translate([0,Yval,.49])
        rotate([0,180,0]) LabelText(SPI);
  }
}

module LabelText(SPI){
linear_extrude(0.6,false){
    text(str(SPI," "),4,"Courier New:style=Bold",halign="center",valign="bottom",spacing=.6);
    //    text(str(SPI," "),3,"Courier New:style=Bold",halign="right",valign="bottom",spacing=.5);
    //    text("spi",3,"Courier New:style=Bold",halign="left",valign="bottom",spacing=.8);
  }
}

module MakeWheel(Thickness,Diameter,SideCount,AxelDiameter){
  scale(Imperial(1)) {
    difference(){
      cylinder(Thickness,Diameter/2,Diameter/2,$fn=SideCount);
      translate([0,0,-.05]) cylinder(Thickness+.1,AxelDiameter/2,AxelDiameter/2,$fn=100);
    }
  }
}

module TestValues(AxelDiameter,WheelThickness,SpikeHeight
    ,StitchSize,SpikeCount,HolderDiameter) {

  SpikeWheelDiameter = HolderDiameter+SpikeHeight;
  HolderCircumference = Circumference(HolderDiameter);
  HolderDepth = (HolderDiameter-AxelDiameter)/2;
  Degrees = DegreesInCircle()/SpikeCount;

  echo(spi=1/StitchSize
      ,StitchSize=StitchSize
      ,SpikeCount=SpikeCount
      ,HolderDiameter=HolderDiameter
      ,SpikeWheelDiameter=SpikeWheelDiameter
      ,HolderCircumference=HolderCircumference
      ,HolderDepth=HolderDepth
      ,PolygonAngle=Degrees
      ,DifFrom180=(180%Degrees));

  assert(AxelDiameter > 0);
  assert(WheelThickness > 0.05);
  assert(SpikeHeight > 0.05);
  assert(SpikeHeight > WheelThickness);

  if(CM)
    assert(SpikeWheelDiameter >= 1.5,"A bigger value for TargetWheelDiameter is needed");
  else
    assert(SpikeWheelDiameter >= 0.6,"A bigger value for TargetWheelDiameter is needed");

  assert(HolderDepth >= (SpikeHeight/2),str("Hole must leave room for spike inset into ring.
 Hint: Shorten spikes, shrink axel or increase wheel diameter HolderDepth=" ,HolderDepth
          ,",SpikeHeight/2=",SpikeHeight/2));

  if(CM)
    assert(HolderDepth >= 0.5,str("Hole must leave enough material to keep round shape under pressure.  Hint: HolderDepth="
            ,HolderDepth," Try making wheel diameter bigger."));
  else
    assert(HolderDepth >= 0.15,str("Hole must leave enough material to keep round shape under pressure.  Hint: HolderDepth="
            ,HolderDepth," Try making wheel diameter bigger."));

  assert(SpikeCount >= 6,str("Target diameter only holds ",SpikeCount," spikes.  Must hold at least 6 spikes.  Hint: Target diameter "
          ,floor(Diameter(StitchSize*6)*10)/10," or more"));
}

module Spike(WheelThickness,SpikeHeight) {
  SlicerSize = WheelThickness > SpikeHeight ? WheelThickness * 2.1 : SpikeHeight * 2.1;
  SpikeRadius = WheelThickness * 0.9;
  CylindarHeight = SpikeHeight/2;
  PointHeight = SpikeHeight/2;
  scale(Imperial(1)) {
    difference(){
      rotate([0,90,0]){
        translate([0,0,CylindarHeight]) cylinder(PointHeight,SpikeRadius,0,$fn=100);
        cylinder(CylindarHeight,SpikeRadius,SpikeRadius,$fn=100);
      }
      translate([SlicerSize*0.49,0,-SlicerSize*.5])
          cube(SlicerSize,center=true);
    }
  }
}

module PlotSpikes(WheelDiameter,Count,Inset) {
  Diameter = WheelDiameter - (Inset*2);
  function x(r) = Imperial(Diameter*cos(r))/2;
  function y(r) = Imperial(Diameter*sin(r))/2;
  deg = DegreesInCircle()/(Count);
  for(i = [1 : 1 : Count]) {
    r = i*deg;
    //echo(r);
    translate([x(r),y(r)]) rotate([0,0,r]) children();
  }
}

```


[User]
I'd like to create a customizable LEGO brick key fob with text on all four sides, and a ring for attaching to a keychain. I've seen some existing designs, but I want a parametric one where I can control a lot of the dimensions.

Specifically, I need to be able to set the brick's length, width, and height in terms of LEGO studs. It should be possible to toggle studs on the top and under-studs (for skinny bricks) on/off. I'd like the option for both raised and embossed text. I need to be able to set the text for each side (front, back, left, right) separately, with separate scale for each as well.  I need to adjust the diameter of the ring 'pipe' which forms the key fob loop.

I also need some advanced parameters, such as a tolerance to adjust for 3D printing inaccuracies. It would be great if I could select from a range of fonts, and also include the option to specify other fonts from the Google Fonts library. Letter spacing should be configurable, along with a Z-shift for the text and the depth of the text. The resolution of circles should also be an option. I'd like to be able to override the LEGO dimensions in case I want to deviate from standard sizes. Specifically, I'd like to be able to control: unit length, plate height, roof thickness, wall thickness, stud diameter/height, undertube diameters, understud diameter, and the play between bricks.

It'd be helpful if the design used the "Roboto Black" font by default, with a default text size of 5.5mm, and a letter spacing of 1.05. The default text depth should be 0.8mm. I'd also like the tolerance to default to 0.05mm, and the pipe diameter to default to 3mm. The text z-shift should default to 2mm.  The default brick should be 6x2x3. Please ensure the code is well-commented so that it's easy to understand and modify later.

I don't need any specific OpenSCAD libraries beyond the built-in features. Could you generate the necessary OpenSCAD file for this?

[Assistant]
LEGOTextBrickKeyFob-v1p2.scad:
```
/* LEGO Text Brick Key Fob
   by Lyl3

Create LEGO compatible brick key fobs with text engraved on the sides and a ring to attach to a keychain/keyring.

This code is published at:
https://www.thingiverse.com/thing:4633973
licensed under the Creative Commons - Attribution - Share Alike license (CC BY-SA 3.0)

V0.1 - code copied from https://www.thingiverse.com/thing:3867358
V0.2 - added a ring (torus) so that the brick can be attached to a keyring
       changed minimum brick width and length from 1 to 2 and removed the understuds option and code
V0.3 - fine-tuned sizing parameters of the ring
V0.4 - changed minimum brick width and length back to 1 and added back in the understuds option and code
V1.0 - ready for release
V1.1 - added option for raised text in addition to embossed text
V1.2 - fixed the incorrect height of the understuds which were too short to reach the top of the brick
*/

/*
   Set default brick to be #2456 6x2
*/

/* [Brick Parameters] */

// Brick length (specified in number of studs).
brickLength = 6; // [1:1:48]

// Brick width (specified in number of studs).
brickWidth = 2;  // [1:1:48]

// Brick height (specified in LEGO height units: 3 is normal brick height, 1 is plate height).
brickHeight = 3; // [1:1:18]

// Studs on top of the brick?
withStuds = "yes"; // [yes,no]
createStuds = (withStuds=="yes") ? true : false;

// Under-studs on underneath of skinny bricks? Skinny bricks that are only one unit wide have smaller solid cylinders (under-studs) underneath instead of the usual hollow cylinders. These have a tiny footprint and may not stick to the build-plate, so you may want to avoid printing them.
withUnderStuds = "no"; // [yes,no]
createUnderStuds = (withUnderStuds=="yes") ? true : false;

/* [Text Parameters] */

// Raised text instead of embossed text?
raisedText = "no"; // [yes,no]
textRaised = (raisedText=="yes") ? true : false;

frontText = "Text Brick";
frontTextScale = 1.0; // [0:0.01:1]

backText = "";
backTextScale = 1.0; // [0:0.01:1]

leftText = "";
leftTextScale = 1.0; // [0:0.01:1]

rightText = "";
rightTextScale = 1.0; // [0:0.01:1]

/* [Ring parameters] */

// Set the diameter of the ring "pipe" (mm). A larger diameter will make a stronger ring. A smaller diameter will be easier to get on a key ring. This should be set to the minimum of 2 if the brick is only one stud wide.
pipeDiameter = 3; // [2.0:0.1:4.0]
pipeRadius = pipeDiameter/2;

/* [Advanced parameters] */

// Tolerance to account for 3D printing inaccuracies (mm). This decreases the sizes of all vertical surfaces (brick walls, studs, under-tubes, and under-studs) to provide a better fit. It is equivalent to a negative horizontal expansion setting in the Cura slicer. You might want to decrease this for bricks that are only 1 unit wide without under-studs as they will be naturally looser fitting than other bricks. The best setting is printer/filament dependent so I've set the default for my printer on the assumption that it's average.
tolerance = 0.05; // [0:0.01:0.1]

// Some suitable fonts
//fontName = "Arial Black:style=Regular";    // Not available on Thingiverse customizer
//fontName = "Biryani:style=Black";          // Has short numbers
//fontName = "Heebo:style=Black";            // Has tight kerning
//fontName = "Maven Pro:style=Black";        // Has tight spacing on "g"
//fontName = "Nunito Sans:style=Black";      // Has tight kerning
//fontName = "Orbitron:style=Black";         // Has blocky letters
//fontName = "Palanquin Dark:style=Bold";    // Has descended numbers
//fontName = "Raleway:style=Black";          // Has descended numbers
//fontName = "Roboto:style=Black";           // No issues
//fontName = "Yantramanav:style=Black";      // No issues

// Choose from a small selection of similar fonts that should work well in this application (NOTE: Arial is not available on Thingiverse).
fontName = 9; // [2:Biryani Black, 3:Heebo Black, 4:Maven Pro Black, 5:Nunito Sans Black, 6:Orbitron Black, 7:Palanquin Dark Bold, 8:Raleway Black, 9:Roboto Black, 10:Yantramanav Black, 0:other, 1:Arial Black]

// If "other" is selected above, you can specify any font available from the Google fonts repository . See https://fonts.google.com/ for available fonts. For example, specify "Overpass Mono:style=Bold" to allow entering some special symbols like heart, club, etc.
otherFont = "Overpass Mono";

// The base size of the text on all sides (can be scaled down in the text parameters section).
textSize = 5.5; // [1:0.1:10]

// Spacing between letters. (ratio: 1 is normal spacing of the font) It's better to have this a little higher for this application.
letterSpacing = 1.05;  // [0.9:0.01:2.0]

// How much to shift the text in the Z dimension (mm).
textZShift = 2.0;         // [-5:0.1:8]

// The depth of the text (mm). You should probably decrease this from the default of 0.8 if you plan on scaling the model up in your slicer.
textDepth = 0.8;  // [0.2:0.01:1.0]

// Resolution of circles. 50 fragments is more than enough for such a small cylinders unless the model will be later scaled up
numberFragments = 50;                 // [6:1:100]
$fn = numberFragments;

/* [Advanced parameters: Override LEGO dimensions] */

// X and Y dimension of one brick unit (mm)
UNIT_LENGTH = 8; // [0.8:0.01:80]

// plate height (a standard brick is 3 times this height) (mm)
PLATE_HEIGHT = 3.2; // [0.32:0.01:32]

// thickness of brick roof (mm)
ROOF_THICKNESS = 1; // [0.1:0.01:10]

// thickness of brick outer wall as in the original 1.5 mm wall without nubs (mm)
WALL_THICKNESS = 1.5; // [0.15:0.01:15]

// diameter of the studs (solid cylinders) on top of a brick (mm)
STUD_DIAMETER = 4.8; // [0.48:0.01:48]

// height of the studs (solid cylinders) on top of a brick (mm)
STUD_HEIGHT = 1.8; // [0.18:0.01:18]

// outer diameter of the under-tubes (hollow cylinders) on the underside of bricks with length > 1 and width > 1 (mm)
UNDERTUBE_OUTER_DIAMETER = 6.41; // [0.64:0.01:64.1]

// inner diameter of the under-tubes (hollow cylinders) on the underside of bricks with length > 1 and width > 1 (mm)
UNDERTUBE_INNER_DIAMETER = 4.8; // [0.48:0.01:48]

// diameter of the under-studs (solid cylinders) on the underside of bricks with length = 1 or width = 1 (mm)
UNDERSTUD_DIAMETER = 1.6; // [0.16:0.01:16]

// the necessary play between bricks so that they fit together
PLAY = 0.2; // [0.02:0.01:2]

/* [Hidden] */
fudge = 0.01;               // Amount to expand some dimensions to ensure manifold model
ringRadius = brickWidth/2*UNIT_LENGTH-PLAY/2-tolerance;  // Set ring same diameter same as brick width

// This convoluted way of selecting the font from an array was a requirement introduced by a change
// on the Thingiverse customizer that broke the ability to directly select fonts with a style specified.
// This issue was first noticed 2010-02-26 and probably happened within the two months prior to that date.
fontNames = ["other", "Arial Black:style=Regular", "Biryani:style=Black", "Heebo:style=Black", "Maven Pro:style=Black", "Nunito Sans:style=Black", "Orbitron:style=Black", "Palanquin Dark:style=Bold", "Raleway:style=Black", "Roboto:style=Black", "Yantramanav:style=Black"];

selectedFont = (fontName==0) ? otherFont : fontNames[fontName];

textBack = textRaised ? 0 : textDepth;

//
// Make the brick and any text on each of the four sides
//
if(textRaised) {
  union() {
    translate([-brickLength/2*UNIT_LENGTH, -brickWidth/2*UNIT_LENGTH, 0])
      brick (brickLength, brickWidth, brickHeight);
    makeText();
  }
} else {
  difference() {
    translate([-brickLength/2*UNIT_LENGTH, -brickWidth/2*UNIT_LENGTH, 0])
      brick (brickLength, brickWidth, brickHeight);
    makeText();
  }
}

//
// Make the ring to attach to a keychain
//
difference() {
  translate([-brickLength/2*UNIT_LENGTH, 0, pipeRadius*.7 ])
    rotate_extrude(convexity = 10) translate([ringRadius-pipeRadius, 0, 0]) circle(r = pipeRadius , $fn = 100);
  translate([-brickLength/2*UNIT_LENGTH+PLAY/2+tolerance, -brickWidth/2*UNIT_LENGTH, 0 ])
    cube([brickLength*UNIT_LENGTH, brickWidth*UNIT_LENGTH, brickHeight*PLATE_HEIGHT]);
  translate([-brickLength/2*UNIT_LENGTH-ringRadius, -brickWidth/2*UNIT_LENGTH, -ringRadius*2 ])
    cube(ringRadius*2);
}

/*
   Module to make a simple rectangular LEGO compatible brick
   Makes a #3001 4x2 brick by default
*/
module brick(length=4, width=2, height=3) {

  // The brick
  translate([PLAY/2+tolerance, PLAY/2+tolerance, 0])
    difference() {
      cube([length*UNIT_LENGTH-PLAY-2*tolerance, width*UNIT_LENGTH-PLAY-2*tolerance, height*PLATE_HEIGHT]);
      translate([WALL_THICKNESS-2*tolerance, WALL_THICKNESS-2*tolerance, -ROOF_THICKNESS])
        cube([length*UNIT_LENGTH-2*WALL_THICKNESS-PLAY+2*tolerance, width*UNIT_LENGTH-2*WALL_THICKNESS-PLAY+2*tolerance, height*PLATE_HEIGHT]);
    }

  // The studs
  if(createStuds) {
    translate([0,0, height*PLATE_HEIGHT-0.00001])
      for (y = [0:width-1]) {
        for (x = [0:length-1]) {
          translate([(x+0.5)*UNIT_LENGTH, (y+0.5)*UNIT_LENGTH, 0])
            cylinder(h=STUD_HEIGHT, d=STUD_DIAMETER-2*tolerance);
        }
      }
  }

  // The under-tubes (hollow cylinders)
  if (width > 1 && length > 1) {
    for (y = [1:width-1]) {
      for (x = [1:length-1]) {
        difference() {
		  translate([x*UNIT_LENGTH, y*UNIT_LENGTH, 0])
			cylinder(h=height*PLATE_HEIGHT-ROOF_THICKNESS+fudge, d = UNDERTUBE_OUTER_DIAMETER-2*tolerance);
		  translate([x*UNIT_LENGTH, y*UNIT_LENGTH, -fudge])
			cylinder(h=height*PLATE_HEIGHT-ROOF_THICKNESS+fudge, d = UNDERTUBE_INNER_DIAMETER+2*tolerance);
		}
	  }
	}
  }

  // The under-studs (smaller solid cylinders) along the length
  if (width == 1 && length > 1 && createUnderStuds) {
	for (x = [1:length-1]) {
	  translate([x*UNIT_LENGTH, 0.5*UNIT_LENGTH, 0])
	    cylinder(h=height*PLATE_HEIGHT-ROOF_THICKNESS+fudge, d=UNDERSTUD_DIAMETER-2*tolerance);
	}
  }

  // The under-studs (smaller solid cylinders) along the width
  if (length == 1 && width > 1 && createUnderStuds) {
	for (y = [1:width-1]) {
	  translate([0.5*UNIT_LENGTH, y*UNIT_LENGTH, 0])
		cylinder(h=height*PLATE_HEIGHT-ROOF_THICKNESS+fudge, d=UNDERSTUD_DIAMETER-2*tolerance);
	}
  }

}

module makeText () {
  translate([0, -brickWidth/2*UNIT_LENGTH+textBack+PLAY/2+tolerance, textZShift]) rotate([90, 0, 0])
    linear_extrude(textDepth+fudge) text(frontText, font=selectedFont, size=textSize*frontTextScale, halign="center", spacing=letterSpacing);
  translate([0, brickWidth/2*UNIT_LENGTH-textBack-PLAY/2-tolerance, textZShift]) rotate([90, 0, 180])
    linear_extrude(textDepth+fudge) text(backText, font=selectedFont, size=textSize*backTextScale, halign="center", spacing=letterSpacing);
  translate([-brickLength/2*UNIT_LENGTH+textBack+PLAY/2+tolerance, 0, textZShift]) rotate([90, 0, 270])
    linear_extrude(textDepth+fudge) text(leftText, font=selectedFont, size=textSize*leftTextScale, halign="center", spacing=letterSpacing);
  translate([brickLength/2*UNIT_LENGTH-textBack-PLAY/2-tolerance, 0, textZShift]) rotate([90, 0, 90])
    linear_extrude(textDepth+fudge) text(rightText, font=selectedFont, size=textSize*rightTextScale, halign="center", spacing=letterSpacing);
}

```
LEGOTextBrickKeyFob-v1p1.scad:
```
/* LEGO Text Brick Key Fob
   by Lyl3

Create LEGO compatible brick key fobs with text engraved on the sides and a ring to attach to a keychain/keyring.

This code is published at:
https://www.thingiverse.com/thing:4633973
licensed under the Creative Commons - Attribution - Share Alike license (CC BY-SA 3.0)

V0.1 - code copied from https://www.thingiverse.com/thing:3867358
V0.2 - added a ring (torus) so that the brick can be attached to a keyring
       changed minimum brick width and length from 1 to 2 and removed the understuds option and code
V0.3 - fine-tuned sizing parameters of the ring
V0.4 - changed minimum brick width and length back to 1 and added back in the understuds option and code
V1.0 - ready for release
V1.1 - added option for raised text in addition to embossed text
*/

/*
   Set default brick to be #2456 6x2
*/

/* [Brick Parameters] */

// Brick length (specified in number of studs).
brickLength = 6; // [1:1:48]

// Brick width (specified in number of studs).
brickWidth = 2;  // [1:1:48]

// Brick height (specified in LEGO height units: 3 is normal brick height, 1 is plate height).
brickHeight = 3; // [1:1:18]

// Studs on top of the brick?
withStuds = "yes"; // [yes,no]
createStuds = (withStuds=="yes") ? true : false;

// Under-studs on underneath of skinny bricks? Skinny bricks that are only one unit wide have smaller solid cylinders (under-studs) underneath instead of the usual hollow cylinders. These have a tiny footprint and may not stick to the build-plate, so you may want to avoid printing them.
withUnderStuds = "no"; // [yes,no]
createUnderStuds = (withUnderStuds=="yes") ? true : false;

/* [Text Parameters] */

// Raised text instead of embossed text?
raisedText = "no"; // [yes,no]
textRaised = (raisedText=="yes") ? true : false;

frontText = "Text Brick";
frontTextScale = 1.0; // [0:0.01:1]

backText = "";
backTextScale = 1.0; // [0:0.01:1]

leftText = "";
leftTextScale = 1.0; // [0:0.01:1]

rightText = "";
rightTextScale = 1.0; // [0:0.01:1]

/* [Ring parameters] */

// Set the diameter of the ring "pipe" (mm). A larger diameter will make a stronger ring. A smaller diameter will be easier to get on a key ring. This should be set to the minimum of 2 if the brick is only one stud wide.
pipeDiameter = 3; // [2.0:0.1:4.0]
pipeRadius = pipeDiameter/2;

/* [Advanced parameters] */

// Tolerance to account for 3D printing inaccuracies (mm). This decreases the sizes of all vertical surfaces (brick walls, studs, under-tubes, and under-studs) to provide a better fit. It is equivalent to a negative horizontal expansion setting in the Cura slicer. You might want to decrease this for bricks that are only 1 unit wide without under-studs as they will be naturally looser fitting than other bricks. The best setting is printer/filament dependent so I've set the default for my printer on the assumption that it's average.
tolerance = 0.05; // [0:0.01:0.1]

// Some suitable fonts
//fontName = "Arial Black:style=Regular";    // Not available on Thingiverse customizer
//fontName = "Biryani:style=Black";          // Has short numbers
//fontName = "Heebo:style=Black";            // Has tight kerning
//fontName = "Maven Pro:style=Black";        // Has tight spacing on "g"
//fontName = "Nunito Sans:style=Black";      // Has tight kerning
//fontName = "Orbitron:style=Black";         // Has blocky letters
//fontName = "Palanquin Dark:style=Bold";    // Has descended numbers
//fontName = "Raleway:style=Black";          // Has descended numbers
//fontName = "Roboto:style=Black";           // No issues
//fontName = "Yantramanav:style=Black";      // No issues

// Choose from a small selection of similar fonts that should work well in this application (NOTE: Arial is not available on Thingiverse).
fontName = 9; // [2:Biryani Black, 3:Heebo Black, 4:Maven Pro Black, 5:Nunito Sans Black, 6:Orbitron Black, 7:Palanquin Dark Bold, 8:Raleway Black, 9:Roboto Black, 10:Yantramanav Black, 0:other, 1:Arial Black]

// If "other" is selected above, you can specify any font available from the Google fonts repository . See https://fonts.google.com/ for available fonts. For example, specify "Overpass Mono:style=Bold" to allow entering some special symbols like heart, club, etc.
otherFont = "Overpass Mono";

// The base size of the text on all sides (can be scaled down in the text parameters section).
textSize = 5.5; // [1:0.1:10]

// Spacing between letters. (ratio: 1 is normal spacing of the font) It's better to have this a little higher for this application.
letterSpacing = 1.05;  // [0.9:0.01:2.0]

// How much to shift the text in the Z dimension (mm).
textZShift = 2.0;         // [-5:0.1:8]

// The depth of the text (mm). You should probably decrease this from the default of 0.8 if you plan on scaling the model up in your slicer.
textDepth = 0.8;  // [0.2:0.01:1.0]

// Resolution of circles. 50 fragments is more than enough for such a small cylinders unless the model will be later scaled up
numberFragments = 50;                 // [6:1:100]
$fn = numberFragments;

/* [Advanced parameters: Override LEGO dimensions] */

// X and Y dimension of one brick unit (mm)
UNIT_LENGTH = 8; // [0.8:0.01:80]

// plate height (a standard brick is 3 times this height) (mm)
PLATE_HEIGHT = 3.2; // [0.32:0.01:32]

// thickness of brick roof (mm)
ROOF_THICKNESS = 1; // [0.1:0.01:10]

// thickness of brick outer wall as in the original 1.5 mm wall without nubs (mm)
WALL_THICKNESS = 1.5; // [0.15:0.01:15]

// diameter of the studs (solid cylinders) on top of a brick (mm)
STUD_DIAMETER = 4.8; // [0.48:0.01:48]

// height of the studs (solid cylinders) on top of a brick (mm)
STUD_HEIGHT = 1.8; // [0.18:0.01:18]

// outer diameter of the under-tubes (hollow cylinders) on the underside of bricks with length > 1 and width > 1 (mm)
UNDERTUBE_OUTER_DIAMETER = 6.41; // [0.64:0.01:64.1]

// inner diameter of the under-tubes (hollow cylinders) on the underside of bricks with length > 1 and width > 1 (mm)
UNDERTUBE_INNER_DIAMETER = 4.8; // [0.48:0.01:48]

// diameter of the under-studs (solid cylinders) on the underside of bricks with length = 1 or width = 1 (mm)
UNDERSTUD_DIAMETER = 1.6; // [0.16:0.01:16]

// the necessary play between bricks so that they fit together
PLAY = 0.2; // [0.02:0.01:2]

/* [Hidden] */
fudge = 0.01;               // Amount to expand some dimensions to ensure manifold model
ringRadius = brickWidth/2*UNIT_LENGTH-PLAY/2-tolerance;  // Set ring same diameter same as brick width

// This convoluted way of selecting the font from an array was a requirement introduced by a change
// on the Thingiverse customizer that broke the ability to directly select fonts with a style specified.
// This issue was first noticed 2010-02-26 and probably happened within the two months prior to that date.
fontNames = ["other", "Arial Black:style=Regular", "Biryani:style=Black", "Heebo:style=Black", "Maven Pro:style=Black", "Nunito Sans:style=Black", "Orbitron:style=Black", "Palanquin Dark:style=Bold", "Raleway:style=Black", "Roboto:style=Black", "Yantramanav:style=Black"];

selectedFont = (fontName==0) ? otherFont : fontNames[fontName];

textBack = textRaised ? 0 : textDepth;

//
// Make the brick and any text on each of the four sides
//
if(textRaised) {
  union() {
    translate([-brickLength/2*UNIT_LENGTH, -brickWidth/2*UNIT_LENGTH, 0])
      brick (brickLength, brickWidth, brickHeight);
    makeText();
  }
} else {
  difference() {
    translate([-brickLength/2*UNIT_LENGTH, -brickWidth/2*UNIT_LENGTH, 0])
      brick (brickLength, brickWidth, brickHeight);
    makeText();
  }
}

//
// Make the ring to attach to a keychain
//
difference() {
  translate([-brickLength/2*UNIT_LENGTH, 0, pipeRadius*.7 ])
    rotate_extrude(convexity = 10) translate([ringRadius-pipeRadius, 0, 0]) circle(r = pipeRadius , $fn = 100);
  translate([-brickLength/2*UNIT_LENGTH+PLAY/2+tolerance, -brickWidth/2*UNIT_LENGTH, 0 ])
    cube([brickLength*UNIT_LENGTH, brickWidth*UNIT_LENGTH, brickHeight*PLATE_HEIGHT]);
  translate([-brickLength/2*UNIT_LENGTH-ringRadius, -brickWidth/2*UNIT_LENGTH, -ringRadius*2 ])
    cube(ringRadius*2);
}

/*
   Module to make a simple rectangular LEGO compatible brick
   Makes a #3001 4x2 brick by default
*/
module brick(length=4, width=2, height=3) {

  // The brick
  translate([PLAY/2+tolerance, PLAY/2+tolerance, 0])
    difference() {
      cube([length*UNIT_LENGTH-PLAY-2*tolerance, width*UNIT_LENGTH-PLAY-2*tolerance, height*PLATE_HEIGHT]);
      translate([WALL_THICKNESS-2*tolerance, WALL_THICKNESS-2*tolerance, -ROOF_THICKNESS])
        cube([length*UNIT_LENGTH-2*WALL_THICKNESS-PLAY+2*tolerance, width*UNIT_LENGTH-2*WALL_THICKNESS-PLAY+2*tolerance, height*PLATE_HEIGHT]);
    }

  // The studs
  if(createStuds) {
    translate([0,0, height*PLATE_HEIGHT-0.00001])
      for (y = [0:width-1]) {
        for (x = [0:length-1]) {
          translate([(x+0.5)*UNIT_LENGTH, (y+0.5)*UNIT_LENGTH, 0])
            cylinder(h=STUD_HEIGHT, d=STUD_DIAMETER-2*tolerance);
        }
      }
  }

  // The under-tubes (hollow cylinders)
  if (width > 1 && length > 1) {
    for (y = [1:width-1]) {
      for (x = [1:length-1]) {
        difference() {
		  translate([x*UNIT_LENGTH, y*UNIT_LENGTH, 0])
			cylinder(h=height*PLATE_HEIGHT-ROOF_THICKNESS+fudge, d = UNDERTUBE_OUTER_DIAMETER-2*tolerance);
		  translate([x*UNIT_LENGTH, y*UNIT_LENGTH, -fudge])
			cylinder(h=height*PLATE_HEIGHT-ROOF_THICKNESS+fudge, d = UNDERTUBE_INNER_DIAMETER+2*tolerance);
		}
	  }
	}
  }

  // The under-studs (smaller solid cylinders) along the length
  if (width == 1 && length > 1 && createUnderStuds) {
	for (x = [1:length-1]) {
	  translate([x*UNIT_LENGTH, 0.5*UNIT_LENGTH, 0])
	    cylinder(h=height*PLATE_HEIGHT-WALL_THICKNESS, d=UNDERSTUD_DIAMETER-2*tolerance);
	}
  }

  // The under-studs (smaller solid cylinders) along the width
  if (length == 1 && width > 1 && createUnderStuds) {
	for (y = [1:width-1]) {
	  translate([0.5*UNIT_LENGTH, y*UNIT_LENGTH, 0])
		cylinder(h=height*PLATE_HEIGHT-WALL_THICKNESS, d=UNDERSTUD_DIAMETER-2*tolerance);
	}
  }

}

module makeText () {
  translate([0, -brickWidth/2*UNIT_LENGTH+textBack+PLAY/2+tolerance, textZShift]) rotate([90, 0, 0])
    linear_extrude(textDepth+fudge) text(frontText, font=selectedFont, size=textSize*frontTextScale, halign="center", spacing=letterSpacing);
  translate([0, brickWidth/2*UNIT_LENGTH-textBack-PLAY/2-tolerance, textZShift]) rotate([90, 0, 180])
    linear_extrude(textDepth+fudge) text(backText, font=selectedFont, size=textSize*backTextScale, halign="center", spacing=letterSpacing);
  translate([-brickLength/2*UNIT_LENGTH+textBack+PLAY/2+tolerance, 0, textZShift]) rotate([90, 0, 270])
    linear_extrude(textDepth+fudge) text(leftText, font=selectedFont, size=textSize*leftTextScale, halign="center", spacing=letterSpacing);
  translate([brickLength/2*UNIT_LENGTH-textBack-PLAY/2-tolerance, 0, textZShift]) rotate([90, 0, 90])
    linear_extrude(textDepth+fudge) text(rightText, font=selectedFont, size=textSize*rightTextScale, halign="center", spacing=letterSpacing);
}

```


[User]
I need a push grommet, something that can be pressed into a hole to protect cables passing through it. I'd like it to be parametric, so I can easily adjust the size for different holes. It needs a flange on one side to cover the hole and "catches" on the other side to hold it in place.

Here are the parameters I'm thinking of:

*   `hole_diameter`: The diameter of the hole the grommet will be pressed into.
*   `grommet_hole_diameter`: The diameter of the hole in the center of the grommet where the cables pass through.
*   `flange_diameter`: The diameter of the flange that covers the hole. This should be larger than `hole_diameter`.
*   `flange_thickness`: The thickness of the flange.
*   `catch_count`: The number of catches to hold the grommet in place.
*   `catch_height`: The height of the catches. This should correspond to the thickness of the material the hole is in.
*   `catch_thickness`: The thickness of the catches.
*   `catch_lip`: The size of the lip on the inside of the catches that holds them in place.
*  `catch_offset`: The distance the catches are placed from the edge of the `hole_diameter`.

The catches should be evenly spaced around the circumference of the grommet and should be a small, angled lip to hold the grommet in place once it's pushed through the hole. The flange should be a simple circular shape.

Could you generate an OpenSCAD file for me with these parameters? I'm not too familiar with the library situation, so just write the SCAD from scratch, I don't need to include anything pre-existing.

[Assistant]
push_grommet.scad:
```
//Size of the hole to be covered.
hole_diameter=30;

// Size of the gromme hole
grommet_hole_diameter=20;

// Size of the flange that covers the outside of the hole
flange_diameter=34.5;

// Thickness of the flange
flange_thickness=1.5;

// Number of catches
catch_count=6;

// This will be the thickness of the material the hole is in
catch_height=3;

// Theickness of the catches
catch_thickness=1.5;

// Size of the catch lip on the inside of the hole
catch_lip=1;

// How much inside the hole you want the catches to be places
catch_offset=.5;

/* [Hidden] */
$fn=50;
catch_rotation=360/(catch_count*2);

flange(flange_diameter,flange_thickness,grommet_hole_diameter);
for ( i = [0 : catch_count-1] )
{
    rotate([0,0,i*catch_rotation*2])
        catch(hole_diameter,catch_height,catch_thickness,catch_lip,catch_offset,catch_rotation,flange_thickness);
}

module catch(hd,ch,ct,cl,co,cr,ft)
{
    catch_diameter=hd-co;

    difference()
    {
        union()
        {
            translate([0,0,ft])
                cylinder(d = catch_diameter, h = ch+cl*2);
            translate([0,0,ch+ft+cl])
                ring(catch_diameter/2,cl);
        }
        translate([0,0,ft])
            cylinder(d = catch_diameter-ct
        , h = ch+cl*2);
        translate([-catch_diameter/2-cl,0,ft])
          cube([catch_diameter+catch_lip*2, catch_diameter/2+cl, ch+cl*2]);

        rotate([0,0,cr])
            translate([-catch_diameter/2-cl,-1*(catch_diameter/2+cl),ft])
              cube([catch_diameter+cl*2, catch_diameter/2+cl, ch+cl*2]);
    }
}

module flange(fd,ft,ghd)
{
    difference()
    {
        union()
        {
            cylinder(d = fd-ft, h = ft);
            translate([0,0,ft/2])
                ring(fd/2-ft/2,ft/2);
        }
        cylinder(d = ghd+ft, h = ft);
    }
    translate([0,0,ft/2])
        ring(ghd/2+ft/2,ft/2);
}

module ring(r1,r2)
    rotate_extrude( )
       translate([r1,0,0])
           circle(d=2*r2,$fn=50);
```


[User]
I need a parametric design for a push-in cap, kind of like a plug, that fits into a hole. I've got some specific requirements for how it should work.

Basically, there's a main body that's a cylinder, that pushes into the hole, and a flange that sits on top of the material. The cylindrical part has catches that flex inwards as you push it into the hole and then spring outwards to keep it in place.

Here are the parameters I need to control:

*   **`hole_diameter`**: The diameter of the hole the plug needs to fit into.
*   **`flange_diameter`**: The diameter of the flange on the top. This should always be larger than the `hole_diameter`.
*   **`flange_thickness`**: The thickness of the flange part.
*   **`catch_count`**: The number of catches on the cylindrical part. They should be evenly spaced.
*   **`catch_height`**: The height of the catches. This would be the depth they reach into the hole.
*   **`catch_thickness`**: The thickness of each catch.
*  **`catch_lip`**: The thickness of the lip on the inside of the catch that helps retain the plug.
*   **`catch_offset`**: How much smaller the catch cylinder is compared to the hole diameter. This affects the flexibility.

I'd like to be able to specify all of these values in mm. The catches should be created with subtractions, so that there is a single body with flex. And, I think `$fn=50;` for circular parts will be enough for good looking prints. It's probably important to note that all of the dimensions are in mm.

I'm imagining this will probably need to be multiple modules: one for the flange, one for the catch, and one for assembling the overall part. I don't need any special libraries for this, just a basic OpenSCAD file.

[Assistant]
push_cap.scad:
```
//Size of the hole to be covered.
hole_diameter=30;

// Size of the flange that covers the outside of the hole
flange_diameter=34.5;

// Thickness of the flange
flange_thickness=1.5;

// Number of catches
catch_count=6;

// This will be the thickness of the material the hole is in
catch_height=3;

// Theickness of the catches
catch_thickness=1.5;

// Size of the catch lip on the inside of the hole
catch_lip=1;

// How much inside the hole you want the catches to be places
catch_offset=.5;

/* [Hidden] */
$fn=50;
catch_rotation=360/(catch_count*2);

flange(flange_diameter,flange_thickness);
for ( i = [0 : catch_count-1] )
{
    rotate([0,0,i*catch_rotation*2])
    catch(hole_diameter,catch_height,catch_thickness,catch_lip,catch_offset,catch_rotation,flange_thickness);
}

module catch(hd,ch,ct,cl,co,cr,ft)
{
    catch_diameter=hd-co;

    difference()
    {
        union()
        {
            translate([0,0,ft])
                cylinder(d = catch_diameter, h = ch+cl*2);
            translate([0,0,ch+ft+cl])
                ring(catch_diameter/2,cl);
        }
        translate([0,0,ft])
            cylinder(d = catch_diameter-ct
        , h = ch+cl*2);
        translate([-catch_diameter/2-cl,0,ft])
          cube([catch_diameter+catch_lip*2, catch_diameter/2+cl, ch+cl*2]);

        rotate([0,0,cr])
            translate([-catch_diameter/2-cl,-1*(catch_diameter/2+cl),ft])
              cube([catch_diameter+cl*2, catch_diameter/2+cl, ch+cl*2]);
    }
}

module flange(fd,ft)
{
   cylinder(d = fd, h = ft);
}

module ring(r1,r2)
    rotate_extrude( )
       translate([r1,0,0])
           circle(d=2*r2,$fn=50);
```


[User]
I'm looking to create a set of prescription lens inserts for my Oculus Quest (either the original or the Quest 2). I've seen a few designs online, but none quite do what I need. I want something parametric that I can customize for different lens thicknesses, and I'd like the option to include a nose-retaining clip, or to omit it if it might press on my nose. I also want to be able to select which of the four parts (right top, left top, right base, and left base) to output as a single STL, or if I prefer, to output all four in a single operation.

The critical thing is that the design should allow for adjusting the thickness of the lens cavity. I'm farsighted and need a bit of extra space behind the lens. A good starting point would be to allow adjustment from 0 to 6mm in 0.1mm increments.

I'm envisioning that the base pieces should be simple and unmodified, and that the adjustable thickness modification would be applied to the top pieces only. The left pieces can simply be a mirror of the right. I'd like the design to be easily usable with 2x5mm disc magnets.

Can you help me generate a set of OpenSCAD files that does this? I'd prefer it if you used standard OpenSCAD functions as much as possible, but if there are useful libraries that make it easier to design, I'm open to that, so long as I don't have to install too many things. I’ve heard that BOSL is a very good library for OpenSCAD, and that it provides useful functions.

[Assistant]
Quest_Lens_Inserts.scad:
```
//
// Quest_Lens_Inserts.scad
// Parameterized Prescription Lens Inserts for Oculus Quest and Quest 2
// Use the OpenSCAD customizmer to choose a thickness and select options
//
// This 3-file project includes two unmodified STL files from the excellent
// "Oculus Quest Magnetic Prescription Lens Adapter" project by jegstad:
// https://www.thingiverse.com/thing:3659124/
//
// The project sculpts, cuts, and pastes one of those STL files to:
// - Variably increase the thickness of the cavities for the prescrition lenses
//   (Needed when used with prescriptions for mid-to-strong farsightedness.)
// - Optionally sculpt a lens retaining clip that can impinge on the nose
// - Optionally output up to all four of the needed parts into a single STL
//
// Typical usage:
// From the OpenSCAD customizer, respect the Geneva convention and disable nose torture.
// Set extra thickness appropriate for your prescription and the refractive index of your
// lenses: Likely 0 mm for +1 diopter and lower, maybe 1.2 mmm for +2 diopters, and higher
// for higher. Select the parts to print. You need all four for a complete set.
// Press F6 to render the result (be patient) and then F7 to output an STL file for use
// with your slicer.
//
// Released by fyngraf October 2020
// Licensed under Creative Commons - Attribution
// With credit and thanks to jegstad and the developers of OpenSCAD
//

// Compensate for a Protruding Proboscis
Disable_Nose_Torture = true;

// Extra Thickness For Top - mm
Thickness_Delta = 1.2; // [0:0.1:6]

// Include Right Top
Right_Top = true;

// Include Left Top
Left_Top = true;

// Include Right Base
Right_Base = true;

// Include Left Base
Left_Base = true;

/* [Hidden] */

$fn=200;

dx = 28;
dy = -27;

if(Right_Top) translate([-dx,-dy,0])cooked_top_right();

if(Left_Top) translate([dx,-dy,0])mirror([1,0,0])cooked_top_right();

if(Right_Base) translate([-dx,dy,0])raw_base_right();

if(Left_Base) translate([dx,dy,0])mirror([1,0,0])raw_base_right();

module nose_safe_top_right()
{
    difference()
    {
        translate([-179, -181.5, 0])import("v2LensTopRight.stl", convexity=10);

        if(Disable_Nose_Torture)
        {
            translate([24,3.2,1])
            rotate([45,45,0])
            translate([0,5,5])
            cube(size=10, center=true);
        }
    }
}

module raw_base_right()
{
    translate([-70, 0, 0])import("v2LensBaseRight.stl", convexity=10);
}

module top_right_cut_volume()
{
    linear_extrude(height = 20, center = true, convexity = 10)
    rotate([0,0,31])translate([0,22.5,0])polygon(points=[ [-2,0], [2,0], [4.4,8], [-4.4,8] ]);
}

module cooked_top_right()
{
    intersection()
    {
        nose_safe_top_right();
        top_right_cut_volume();
    }

    linear_extrude(height = Thickness_Delta, center = false, convexity = 10)
    projection(cut=true)
    translate([0,0,-0.01])
    difference()
    {
        nose_safe_top_right();
        top_right_cut_volume();
    }

    translate([0,0,Thickness_Delta])
    difference()
    {
        nose_safe_top_right();
        top_right_cut_volume();
    }
}

```


[User]
Okay, I'm looking to create a customizable system of boxes and drawer dividers, kind of like the ones Alexandre Chappel uses. I've got a basic idea from another OpenSCAD file I found but want something more robust and fully parametric from scratch.

Essentially, I need:

*   **A Drawer Module:** This will be the container for everything. I need to specify its *inside* width, length, and height (ID_W, ID_L, ID_H).
*   **A Matrix/Grid System:** Inside the drawer, I want a grid that dictates where boxes can go. I need to control how many grid sections there are in width (MAT_W), length (MAT_L), and height (MAT_H). There needs to be a tolerance (Tol) for space between the grid and any boxes, and the grid itself has a thickness, or really a radius since it's cylindrical (G_rad).
*   **Individual Boxes:** The boxes will fit within the matrix. I need to control the wall thickness (B_WT), the bottom thickness (B_BT), and a radius for rounded corners (B_rad).
*   **Sticker Lip:** Each box should have an optional sticker lip (ST) that can be toggled. If enabled, I should be able to set its height offset (ST_O) from the top of the box, its width (ST_W), and its height (ST_H).
*   **Grid printing options:** I need to be able to print the matrix as a separate grid section for easier installation of the boxes. I should be able to select how many grid sections I want to print for the width (GTP_W) and the length (GTP_L). This must be less or equal than the grid sections count of the matrix system.
*   **Box printing options:** I need to be able to specify which size of box to print, based on the number of matrix units it should take up in width (BTP_W), length (BTP_L), and height (BTP_H). Also, I need to be able to add separators within the box, specifying how many separator "lines" there should be in the width (BTP_SW) and length (BTP_SL) dimensions. And finally, I need to be able to toggle duplication of the sticker lip on all separators or just on the main box (DST).
*   **Preview:** I want the ability to preview just the drawer with the grid, the grid to print, a setup preview showing the drawer with the grid and some boxes (only the first in each row), or just the box to print.

I will need full parametric controls for all of these values. I would also like a $fn parameter for the level of segmentation when using cylinders. I don't require any special libraries. The echo functions in the original code are not needed either.

I'd like a single .scad file to define all of these parameters, modules, and preview options. This is a big one, but I think it'll be a really useful tool. Thanks in advance!

[Assistant]
DrawerBoxesCustomizer_005.scad:
```
///Designed by 1SPIRE, inspired from ALEXENDRE CHAPPEL assortment system boxes///

/* [Drawer Parameters] */
// Inside Drawer Width
ID_W=335;
// Inside Drawer Length
ID_L=430;
// Inside Drawer Height
ID_H=80;

/* [Matrix and Grid Parameters] */
//Matrix Drawer Width
MAT_W=6; //[1:1:20]
//Matrix Drawer Length
MAT_L=8; //[1:1:20]
//Matrix Drawer Height
MAT_H=1; //[1:1:6]
//Tolerance (Space between boxes)
Tol=0.4;
//Grid Tickness (radius value = height = half of the width)
G_rad=2;

/* [Boxes Parameters] */
//Wall Thickness Box
B_WT=1.6;
//Bottom Thickness Box
B_BT=1.25;
//Radius inside box
B_rad=2.5;
//Sticker Lip
ST=1; //[0:No,1:Yes]
//Sticker Lip Height Offset
ST_O=0;
//Sticker Lip Width
ST_W=30;
//Sticker Lip Height
ST_H=9;
//Definition
$fn=60; //[20:120]

/* [Grid to print, see ECHO for the exact size] */
//Number of grid sections on the width, must be < Matrix Drawer Width
GTP_W=3; //[1:1:20]
//Number of grid sections on the length, must be < Matrix Drawer Length
GTP_L=4; //[1:1:20]

/* [Box to print, Size of the box] */
//Grid Inlay
GI=1; //[0:No,1:Yes]
//Coef of matrix along width, must be < Matrix Drawer Width
BTP_W=1; //[1:1:20]
//Coef of matrix along length, must be < Matrix Drawer Length
BTP_L=1; //[1:1:20]
//Coef of matrix along height, must be < Matrix Drawer Height
BTP_H=1; //[1:1:6]

/* [Box to print, Separators] */
//Duplicate sticker lip
DST=0; //[0:No,1:Yes]
//Box separators, Coef of matrix along width, must be < BTP_W
BTP_SW=0; //[0:1:20]
//Box separators, Coef of matrix along length, must be < BTP_L
BTP_SL=0; //[0:1:20]

/* [Preview and Render] */
Preview=2; //[0:Drawer and grid preview,1:Grid to print,2:Setup Preview,3:Box to print]

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

MAT_W_UNIT=ID_W/MAT_W;
MAT_L_UNIT=ID_L/MAT_L;
MAT_H_UNIT=ID_H/MAT_H;
echo("Matrix Width unit=",MAT_W_UNIT);
echo("Matrix Length unit=",MAT_L_UNIT);
echo("Matrix Height unit=",MAT_H_UNIT);
echo("Grid to print Width=",GTP_W*MAT_W_UNIT);
echo("Grid to print Length=",GTP_L*MAT_L_UNIT);

module Drawer() {
    difference() {
        translate([-ID_W/2-10,-ID_L/2-10,-5]) cube([ID_W+20,ID_L+20,ID_H+5]);
        translate([-ID_W/2,-ID_L/2,0]) cube([ID_W,ID_L,ID_H+5]);
    }
}

module GridUnit(orientation,length) {
    if(orientation=="L") {
        difference() {
            rotate([90,0,0]) cylinder(d=2*G_rad,h=length,$fn=4,center=true);
            translate([0,0,-1.5*G_rad]) cube([3*G_rad,length+2,3*G_rad],center=true);
        }
    }

    else if (orientation=="W") {
        difference() {
            rotate([0,90,0]) cylinder(d=2*G_rad,h=length,$fn=4,center=true);
            translate([0,0,-1.5*G_rad]) cube([length+2,3*G_rad,3*G_rad],center=true);
        }
    }
}

module Grid() {
    intersection() {
        union() {
            for(i=[0:1:MAT_W]) translate([i*MAT_W_UNIT-ID_W/2,0,0]) GridUnit("L",ID_L);
            for(i=[0:1:MAT_L]) translate([0,i*MAT_L_UNIT-ID_L/2,0]) GridUnit("W",ID_W);
        }
        cube([ID_W,ID_L,3*G_rad],center=true);
    }
}

module Box(DIV_W,DIV_L,DIV_H) { //DIV=coef matrix (width, length, height)
    difference() {
        hull() {
            for(i=[-1,1],j=[-1,1]) {
                translate([i*(DIV_W*MAT_W_UNIT/2-B_rad-B_WT-Tol/2),j*(DIV_L*MAT_L_UNIT/2-B_rad-B_WT-Tol/2),G_rad+0.2]) cylinder(r=B_rad+B_WT,h=DIV_H*MAT_H_UNIT-G_rad-0.2);
                translate([i*(DIV_W*MAT_W_UNIT/2-B_rad-B_WT-Tol/2-G_rad),j*(DIV_L*MAT_L_UNIT/2-B_rad-B_WT-Tol/2-G_rad),0]) cylinder(r=B_rad+B_WT,h=B_BT);
            }
        }

        if (GI==1) {
            for(i=[1:1:DIV_W-1]) translate([i*MAT_W_UNIT-DIV_W*MAT_W_UNIT/2,0,0]) rotate([90,0,0]) cylinder(d=2*G_rad+0.4,h=DIV_L*MAT_L_UNIT+2,$fn=4,center=true);
            for(i=[1:1:DIV_L-1]) translate([0,i*MAT_L_UNIT-DIV_L*MAT_L_UNIT/2,0]) rotate([0,90,0]) cylinder(d=2*G_rad+0.4,h=DIV_W*MAT_W_UNIT+2,$fn=4,center=true);
        }

        difference() {
            hull() {
                for(i=[-1,1],j=[-1,1]) translate([i*(DIV_W*MAT_W_UNIT/2-B_rad-B_WT-Tol/2),j*(DIV_L*MAT_L_UNIT/2-B_rad-B_WT-Tol/2),G_rad+0.2]) cylinder(r=B_rad,h=DIV_H*MAT_H_UNIT);
                for(i=[-1,1],j=[-1,1]) translate([i*(DIV_W*MAT_W_UNIT/2-B_rad-B_WT-Tol/2-G_rad),j*(DIV_L*MAT_L_UNIT/2-B_rad-B_WT-Tol/2-G_rad),B_BT]) cylinder(r=B_rad,h=DIV_H*MAT_H_UNIT);
            }

            if (GI==1) {
                translate([0,0,0]) {
                    for(i=[1:1:DIV_W-1]) translate([i*MAT_W_UNIT-DIV_W*MAT_W_UNIT/2,0,0]) rotate([90,0,0]) cylinder(d=2*G_rad+0.4+2*B_BT,h=DIV_L*MAT_L_UNIT+2,$fn=4,center=true);
                    for(i=[1:1:DIV_L-1]) translate([0,i*MAT_L_UNIT-DIV_L*MAT_L_UNIT/2,0]) rotate([0,90,0]) cylinder(d=2*G_rad+0.4+2*B_BT,h=DIV_W*MAT_W_UNIT+2,$fn=4,center=true);
                }
            }

            if (ST==1) {
                if (DST==0) {
                    translate([0,-DIV_L*MAT_L_UNIT/2,DIV_H*MAT_H_UNIT]) difference() {
                        hull() {
                                for(i=[-1,1],j=[-B_rad*2,ST_H-B_rad*2+B_WT]) {
                                    translate([i*(ST_W/2-B_rad),j-(-B_rad-Tol/2),-0.5-ST_O]) cylinder(r=B_rad,h=1);
                                    translate([i*(ST_W/2-B_rad),-B_rad*2-(-B_rad-Tol/2),-1.5*ST_H-0.5-ST_O]) cylinder(r=B_rad,h=1);
                                }
                        }
                        translate([-ST_W/2-1,-5,-1.5*ST_H-1-ST_O]) cube([ST_W+2,B_WT+3.5,1.5*ST_H+2]);
                    }
                }
                if (DST==1) {
                        for(i=[0.5:1:BTP_SW+1],j=[0:1:BTP_SL]) translate([i*BTP_W*MAT_W_UNIT/(BTP_SW+1)-BTP_W*MAT_W_UNIT/2,j*BTP_L*MAT_L_UNIT/(BTP_SL+1)-BTP_L*MAT_L_UNIT/2,DIV_H*MAT_H_UNIT]) difference() {
                            hull() {
                                    for(i=[-1,1],j=[-B_rad*2,ST_H-B_rad*2+B_WT]) {
                                        translate([i*(ST_W/2-B_rad),j-(-B_rad-Tol/2),-0.5-ST_O]) cylinder(r=B_rad,h=1);
                                        translate([i*(ST_W/2-B_rad),-B_rad*2-(-B_rad-Tol/2),-1.5*ST_H-0.5-ST_O]) cylinder(r=B_rad,h=1);
                                    }
                            }
                            translate([-ST_W/2-1,-5,-1.5*ST_H-1-ST_O]) cube([ST_W+2,B_WT+3.5,1.5*ST_H+2]);
                        }

                }
            }
            else{}

            for(i=[1:1:BTP_SW]) translate([i*BTP_W*MAT_W_UNIT/(BTP_SW+1)-BTP_W*MAT_W_UNIT/2,0,-ST_O]) cube([B_WT,BTP_L*MAT_L_UNIT,2*MAT_H_UNIT*DIV_H],center=true);
            for(i=[1:1:BTP_SL]) translate([0,i*BTP_L*MAT_L_UNIT/(BTP_SL+1)-BTP_L*MAT_L_UNIT/2,-ST_O]) cube([BTP_W*MAT_W_UNIT,B_WT,2*MAT_H_UNIT*DIV_H],center=true);

        }
    }
}

module GridToPrint(width,length) {
    intersection() {
        union() {
            for(i=[0:1:width]) translate([i*MAT_W_UNIT-width*MAT_W_UNIT/2,0,0]) GridUnit("L",length*MAT_L_UNIT);
            for(i=[0:1:length]) translate([0,i*MAT_L_UNIT-length*MAT_L_UNIT/2,0]) GridUnit("W",width*MAT_W_UNIT);
        }
        cube([width*MAT_W_UNIT,length*MAT_L_UNIT,2*G_rad],center=true);
    }
}

module Rendering(Render) {
    if (Render==0) {
        %Drawer();
        Grid();
    }
    else if (Render==1) {
        GridToPrint(GTP_W,GTP_L);
    }
    else if (Render==2) {
        %Drawer();
        if (GI==1) {translate([0,0,-0.1]) Grid();}
        for(i=[0:BTP_W:MAT_W-BTP_W],j=[0:BTP_L:MAT_L-BTP_L]) translate([-ID_W/2+MAT_W_UNIT/2*BTP_W+i*MAT_W_UNIT,-ID_L/2+MAT_L_UNIT/2*BTP_L+j*MAT_L_UNIT,0]) Box(BTP_W,BTP_L,BTP_H);
    }
    else if (Render==3) {
        Box(BTP_W,BTP_L,BTP_H);
    }
}

Rendering(Preview);

```