[User]
I'm looking to create a customizable pipe collar that can be printed in two halves and assembled around an existing pipe. I'd like to be able to specify the diameter of the pipe it should fit, the width of the collar itself, and the height of the collar. I want the collar to have interlocking grips to hold the two halves together, and ideally, it should be designed so that it can be easily adjusted for different pipe sizes.

The collar should be a cylindrical shape that surrounds the pipe. The width parameter controls how far the collar extends outwards from the pipe, and the height controls the thickness. The grips should be created by cutting out a shape on one side of each half cylinder, and insetting a matching interlocking piece on the other side of each half cylinder (so that the left-hand grip matches the right-hand grip of the other half) and they should be triangular in profile.

I also would like to have it be a puzzle style fit with a male and female grip (so two of these parts can be put together) and it should use a cylinder for the main part. I'd also like the grips to be placed near the outer edge of the collar.

Can you generate an OpenSCAD file for this? I don't need any specific OpenSCAD libraries for this.

[Assistant]
pipeCollarV3.scad:
```
pipeDiameter = 105;
collarWidth = 20; // [0:80]
collarHeight = 10; // [0:80]

/* [Hidden] */
pipeRadius = pipeDiameter / 2;
totalWidth = pipeDiameter + (collarWidth * 2);
gripSize = collarWidth * 0.5;
gripOffset = collarWidth * 0.2;
tolerance = 0.6;
$fn = 75;

difference() {
  cylinder(collarHeight, pipeRadius + collarWidth, pipeRadius, true);

  union(){
    cylinder(collarHeight * 2, pipeRadius, pipeRadius, true); // pipe
    translate([gripSize + totalWidth / 2, totalWidth / 2, 0])cube([totalWidth, totalWidth, collarHeight], true);
    translate([totalWidth / 2, -totalWidth / 2, 0])cube([totalWidth, totalWidth, collarHeight], true);
    // Grips
    translate([-gripSize / 2, -pipeRadius - gripOffset, 0])cylinder(collarHeight, gripSize + tolerance, gripSize + tolerance, $fn = 3, true);
      translate([-tolerance/2-gripSize, -gripSize/2-pipeRadius, -collarHeight/2])cube([gripSize+tolerance, gripSize/2+pipeRadius, collarHeight]); // small piece next to left grip
    translate([gripSize / 2, pipeRadius - gripOffset + collarWidth, 0])cylinder(collarHeight, gripSize, gripSize, $fn = 3, true);
  }
}

```


[User]
Hi, I'm looking to create a parametric phone holder that attaches to the top of a laptop screen. I've seen a design that does this, and it seems pretty simple. I want it to be adjustable for different phone thicknesses and laptop screen thicknesses.

Essentially, the design consists of a vertical back piece that rests against the back of the laptop screen, a horizontal bar that sits above the screen, and then two small tabs that extend upward from that horizontal bar. One tab is meant to sit against the laptop screen, and the other sits against the phone, creating a space for the phone to sit on top.

Here are some specific parameters I'd like to be able to control:

*   **Thickness of the bracket** (all parts of the bracket will be this thickness)
*   **Width of the bracket** (the dimension that is parallel to the screen edge)
*   **Thickness of the phone** (this affects the size of the phone holding space)
*   **Thickness of the laptop screen** (this also affects the size of the phone holding space)
*   **Overall height of the back piece**
*   **Length of the screen-side tab**
*   **Length of the phone-side tab**
*   **Distance from the bottom of the bracket to the horizontal bar**

I'd also like a way to set the curve resolution, since I'll be using cylinders for rounded edges.

I'm hoping to get this as an OpenSCAD design, so a .scad file would be great. Can you help with that?

[Assistant]
Laptop_Phone_Holder.scad:
```
// Laptop Phone Holder.scad by Finn Margrie Jones Sept 2020

// @potatoad  _     _
//           /o\---/o\
//         ,'    |    `.
//        |      |      |
//        <`-----------'>
//       / `. `-----' ,' \
//      /    `-------'    \
//     :  |   _______   |  :
//     |  `.,'       `.,'  |
//    ,`.   \    o    /   ,'.
//   /   `.  `.     ,'  ,'   \
// ^^^^----^^^^-----^^^^----^^^^

// Thickness of bracket
thickness = 3;

// Width of each bracket
width = 10;

// Thickness of phone
phoneSize = 10;

// Tickness of laptop screen
screenSize = 7;

// Overall height
backLength = 82;

// Length of the tab that goes against the screen
screentTab = 21;

// Length of the tab that goes against the phone
phoneTab = 11;

// Distance from the bottom of the bracket to the horizontal bar
offsetFromBtm = 50;

// Curve resolution, higher is more detailed
$fn = 32;

echo(crossWidth);

union(){
    // Back
    union(){
        translate([0,thickness/2,0]) cube([thickness,backLength-thickness,width]);

        translate([thickness/2,thickness/2,0]){
            cylinder(r=thickness/2,h=width);
        }

        translate([thickness/2,backLength-(thickness/2),0]){
            cylinder(r=thickness/2,h=width);
        }
    }

    // Cross piece
    translate([0,offsetFromBtm,0]){
        if (phoneSize > screenSize){
            cube([phoneSize + thickness,thickness,width]);
        } else {
            cube([screenSize + thickness,thickness,width]);
        }
    }

    // Screen tab
    union(){
        translate([screenSize+thickness,offsetFromBtm-screentTab+(thickness/2),0]){
            cube([thickness,screentTab+thickness-(thickness/2),width]);
        }

        translate([screenSize+thickness+thickness/2,offsetFromBtm-screentTab+(thickness/2),0]){
            cylinder(r=thickness/2,h=width);
        }
    }

    // Phone tab
    union(){
        translate([phoneSize+thickness,offsetFromBtm,0]){
            cube([thickness,phoneTab+(thickness/2),width]);
        }

        translate([phoneSize+thickness*2-(thickness/2),offsetFromBtm+phoneTab+(thickness/2),0]){
            cylinder(r=thickness/2,h=width);
        }
    }
}
```


[User]
Hi, I'm looking to create a customizable surprise egg design in OpenSCAD. I've seen some cool ones online but want something fully parametric and under a free license. I've done some basic OpenSCAD but would like a design that I can easily tweak, and I'm not sure where to start.

The design should essentially be a hollow egg that splits into two halves with a hinge and a latch mechanism. I'd like to be able to control things like the egg's width and height, how much of the bottom is truncated (flattened), the latch size, the hinge size, and the tolerances between parts for printing.

Specifically, here's what I have in mind for parameters, which are all in millimeters unless otherwise stated:

*   `egg_w`: The width of the egg.
*   `egg_h`: The height of the egg before truncation.
*   `off`: The amount to truncate or flatten the bottom.
*   `l`: The size (length or height) of the latch.
*  `lw`: The width of the latch
*   `m`: The offset of the latch from the top of the egg.
*   `n`: The width of the notch that the latch engages with.
*   `r`: The radius of the hinge.
*   `p`: The radius of the hinge pin.
*   `w`: The width of a hinge segment.
*   `tol`: The tolerance between the two halves of the egg, as well as hinge and latch tolerances (or potentially separate parameters if that is easier).
*   `$fn`:  The number of segments used for curves (as I think I'll be using `rotate_extrude` for the egg shape).

The design should also include a feature to add internal supports so that the "payload" or space inside is bridged across the width.  The design will include a midline cut and an offset cut so that we can remove the middle section to create a gap between the two halves. In addition, there should be some geometry overlap in each half along the midline so that they fit well. I'm not entirely sure how to achieve some of the cut sections (like the midline and offset cuts) so that they align perfectly when assembled, and I’m wondering if this will involve using  `intersection` and `difference` operations.

I'd like it to be flexible enough so I can use it to create just the hollow egg or a solid blank egg, which can be customized later via boolean operations to hold a specific object inside.

I think that the `MCAD/regular_shapes.scad` library might be useful for creating the egg outline and the ellipse shape.

Could you provide me with a set of OpenSCAD files to do this?

[Assistant]
surprise_egg.scad:
```
// OpenSCAD Customizable Surprise Egg
// (c) 2020, tmackay
//
// Licensed under a Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license, http://creativecommons.org/licenses/by-sa/4.0.

// Which part to generate
part="hollow"; // [hollow:Hollow,blank:Blank]
// egg width
egg_w=33;
// egg height (before truncated)
egg_h=45;
// truncate bottom
off=3;
// latch size
l=3;
// latch width
lw=4;
// latch offset (from top)
m=8;
// notch width
n=4;
// hinge radius
r=3.4;
// hinge pin radius
p=1.2;
// hinge segment width
w=3.2;
// distance between parts
tol=0.4; // TODO: separate parameter for shell thickness
// curve segments
$fn=96;
// geometry overlap
AT=1/64;
ST=2*AT;

difference(){
    egg(egg_w,egg_h,0,off); // outer shell
    //translate([0,-egg_w/2,0])cube([egg_w,egg_w,egg_h]); // temporary section
    difference(){ // midline cut
        translate([-egg_w/2,-tol/2,2*r-3*tol])cube([egg_w,tol,egg_h]);
        egg(egg_w,egg_h,2*tol+tol,off); // inner shell
    }
    intersection(){ // offset cut and latch
        difference(){
            union(){
                translate([-egg_w/2,tol,2*r-3*tol])cube([egg_w,tol,egg_h]);
                translate([lw/2,tol,egg_h-m])mirror([1,1,0])mirror([0,-1,1])linear_extrude(lw)polygon(points=[
                    [0,-tol],[4+tol,4],[4+tol,6+tol],[2-tol,6+tol],[2-tol,5+tol],[0,5+tol]
                ]*l/6);
            }
            translate([lw/2-tol,tol,egg_h-m])mirror([1,1,0])mirror([0,-1,1])linear_extrude(lw-2*tol)
                polygon(points=[[-(tol+AT)*6/l,5],[2,5],[2,6],[4,6],[4,4],-(tol+AT)*[1,1]*6/l]*l/6);
        }
        egg(egg_w,egg_h,tol+tol,off); // middle shell
    }
    intersection(){ // join cuts
        translate([-egg_w/2,-tol/2,2*r-3*tol])cube([egg_w,2.5*tol,egg_h]);
        difference(){
            egg(egg_w,egg_h,tol+tol,off);
            egg(egg_w,egg_h,2*tol+tol,off);
        }
    }
    difference(){ // notch
        translate([-egg_w/2,2.5*tol/2-tol/2,egg_h])scale([egg_h/egg_w,1,1])
            rotate([0,45,0])cube([egg_w,2.5*tol,n],center=true);
        egg(egg_w,egg_h,2*tol+tol,off);
    }
    difference(){ // hinge
        translate([0,3*tol/2,r-2*tol])mirror([1,1,0])mirror([0,1,1])mir()difference(){
            union(){
                for(i=[1:3])translate([0,0,i*w-tol/2-w/2])cylinder(r=r,h=tol);
                for(i=[0:3])translate([0,0,i*w-w/2])difference(){h=i<3?w:egg_w/2-2.5*w;
                    cylinder(r=r,h=h);
                    cylinder(r=r-tol,h=h);
                    translate([0,-r,0])mirror([i%2,0,0])cube([2*r,2*r,h]);
                }
                translate([0,0,w/2])cylinder(r=p+tol,h=w);
            }
            translate([0,0,w/2-tol/2-AT])cylinder(r=p,h=w+tol+ST);
        }
        difference(){
            translate([-egg_w/2,-tol/2+tol,2*r-4*tol])cube([egg_w,2.5*tol-tol,egg_h]);
            egg(egg_w,egg_h,tol+tol,off); // middle shell
        }

    }
    if(part=="hollow")difference(){ // payload
        egg(egg_w,egg_h,5*tol,off); // inside shell
        translate([-egg_w/2,-egg_w/2,egg_h-m-2*tol])cube([egg_w,egg_w,m]);
        translate([-egg_w/2,3*tol/2,r-2*tol])mirror([1,1,0])mirror([0,1,1])cylinder(r=r+4*tol,h=egg_w);
        for(i=[-8:8])translate([0,3*tol/2+i*egg_w/16,egg_h-m-2*tol])
            cube([egg_w,egg_w/16-AT,tol],center=true); // bridge helper
    }
}

module mir(){
    children();
    mirror([0,0,1])children();
}

module egg(width, length, tol, off){
    rotate_extrude()intersection(){
        translate([0,tol-off])egg_outline(width-2*tol,length-2*tol);
        translate([0,tol])square([egg_w,egg_h]);
    }
}

//use <MCAD/regular_shapes.scad>
// The ratio of length and width is about 1.39 for a real egg
module egg_outline(width, length){
    translate([0, width/2, 0]) union(){
        rotate([0, 0, 180]) difference(){
            ellipse(width, 2*length-width);
            translate([-length/2, 0, 0]) square(length);
        }
        circle(r=width/2);
    }
}

module ellipse(width, height) {
  scale([1, height/width, 1]) circle(r=width/2);
}
```


[User]
Okay, I'm looking to create a customizable surprise egg, similar to the toy ones, but with a focus on being fully parametric and open-source. I want to be able to adjust the overall size, the latch mechanism, the hinge, and the internal space. The main goal is to have a design I can easily modify for different sizes and for different items I might want to put inside. The existing design I have is in OpenSCAD, and I want a new design that achieves a similar effect but is completely of your creation and will not be derived from existing work.

Here are the key aspects I'd like to control:

*   **Egg Dimensions:**
    *   `egg_w`: The width of the egg.
    *   `egg_h`: The height of the egg (before any truncation).
    *   `off`: The amount to truncate the bottom of the egg, making it sit flat.

*   **Latch Mechanism:**
    *   `l`: The size of the latch.
    *   `lw`: The width of the latch.
    *   `m`: The offset of the latch from the top of the egg.
    *   `n`: Width of the notch that the latch engages.

*   **Hinge:**
    *   `r`: The radius of the hinge.
    *   `p`: The radius of the hinge pin.
    *   `w`: The width of the hinge segments.

*   **Clearances:**
    *   `tol`: The tolerance between the egg halves (and for the hinge pin). This is critical for print-in-place functionality.
    *   `layer_h`: The layer height used for vertical clearances. This needs to be considered for the latch mechanism to work well with 3D prints.

*   **Other:**
    *   `$fn`: The number of segments for curves - I'd like this to be adjustable as usual.
    *   `part`: A variable to choose between a hollow egg ("hollow") or a blank solid egg ("blank"). I would like to use the blank version for designing a custom insert, so the inside should be solid and easy to cut away from.
    *  A small amount of overlap using `AT` and `ST` for subtractive operations.

The existing design makes use of the `rotate_extrude()` and `linear_extrude()` functions, `difference()`, `intersection()`, and `union()` operators, as well as `mirror()`, `translate()`, `rotate()`, and `scale()`. Also the `circle()`, `square()`, and `polygon()` primitives. I don't mind if you use some of the same functions, but I'd like the overall implementation to be unique.

The egg outline is defined with a rotated difference of an ellipse and square and then unioned with a circle to give a sort of egg profile, and this works quite well, I'd like to retain this profile.

I'm hoping for a design that prints well without supports, relying on bridging for the hollow part.  I'd like all code to be in one file, unless you believe the file is large enough that it would help to split up the modules into a separate file, in that case use `use` or `include`. In that case, please make sure the main file is called "surprise_egg.scad" and that the library file is named something intuitive like "egg_modules.scad". I don't anticipate using any external libraries (like MCAD) for this.

[Assistant]
surprise_egg.scad:
```
// OpenSCAD Customizable Surprise Egg
// (c) 2020, tmackay
//
// Licensed under a Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license, http://creativecommons.org/licenses/by-sa/4.0.

// Which part to generate
part="hollow"; // [hollow:Hollow,blank:Blank]
// egg width
egg_w=33;
// egg height (before truncated)
egg_h=45;
// truncate bottom
off=3;
// latch size
l=3;
// latch width
lw=4;
// latch offset (from top)
m=8;
// notch width
n=4;
// hinge radius
r=3.4;
// hinge pin radius
p=1.2;
// hinge segment width
w=3.2;
// distance between parts
tol=0.4; // TODO: separate parameter for shell thickness
// layer height used for vertical clearances
layer_h=0.2;
// curve segments
$fn=96;
// geometry overlap
AT=1/64;
ST=2*AT;

difference(){
    egg(egg_w,egg_h,0,off); // outer shell
    //translate([0,-egg_w/2,0])cube([egg_w,egg_w,egg_h]); // temporary section
    difference(){ // midline cut
        translate([-egg_w/2,-tol/2,2*r-3*tol])cube([egg_w,tol,egg_h]);
        egg(egg_w,egg_h,2*tol+tol,off); // inner shell
    }
    intersection(){ // offset cut and latch
        difference(){
            union(){
                translate([-egg_w/2,tol,2*r-3*tol])cube([egg_w,tol,egg_h]);
                translate([lw/2,tol,egg_h-m])mirror([1,1,0])mirror([0,-1,1])linear_extrude(lw)polygon(points=[
                    [0,-layer_h],[5*l/6+tol,2*l/3],[5*l/6+tol,l+layer_h],[l/2-tol,l+layer_h],[l/2-tol,5*l/6+layer_h],[0,5*l/6+layer_h]
                ]);
            }
            translate([lw/2-tol,tol,egg_h-m])mirror([1,1,0])mirror([0,-1,1])linear_extrude(lw-2*tol)
                polygon(points=[[-(tol+AT)*6/l,5],[3,5],[3,6],[5,6],[5,4],-[(tol+AT)*6,(layer_h+AT)*6]/l]*l/6);
        }
        egg(egg_w,egg_h,tol+tol,off); // middle shell
    }
    intersection(){ // join cuts
        translate([-egg_w/2,-tol/2,2*r-3*tol])cube([egg_w,2.5*tol,egg_h]);
        difference(){
            egg(egg_w,egg_h,tol+tol,off);
            egg(egg_w,egg_h,2*tol+tol,off);
        }
    }
    difference(){ // notch
        translate([-egg_w/2,2.5*tol/2-tol/2,egg_h])scale([egg_h/egg_w,1,1])
            rotate([0,45,0])cube([egg_w,2.5*tol,n],center=true);
        egg(egg_w,egg_h,2*tol+tol,off);
    }
    difference(){ // hinge
        translate([0,3*tol/2,r-2*tol])mirror([1,1,0])mirror([0,1,1])mir()difference(){
            union(){
                for(i=[1:3])translate([0,0,i*w-tol/2-w/2])cylinder(r=r,h=tol);
                for(i=[0:3])translate([0,0,i*w-w/2])difference(){h=i<3?w:egg_w/2-2.5*w;
                    cylinder(r=r,h=h);
                    cylinder(r=r-tol,h=h);
                    translate([0,-r,0])mirror([i%2,0,0])cube([2*r,2*r,h]);
                }
                translate([0,0,w/2])cylinder(r=p+tol,h=w);
            }
            translate([0,0,w/2-tol/2-AT])cylinder(r=p,h=w+tol+ST);
        }
        difference(){
            translate([-egg_w/2,-tol/2+tol,2*r-4*tol])cube([egg_w,2.5*tol-tol,egg_h]);
            egg(egg_w,egg_h,tol+tol,off); // middle shell
        }

    }
    if(part=="hollow")difference(){ // payload
        egg(egg_w,egg_h,5*tol,off); // inside shell
        translate([egg_w/2,3*tol/2,0])mirror([1,1,0])mirror([0,1,-1])linear_extrude(egg_w)
            polygon(points=[[-m-5*tol,egg_h],[m+5*tol,egg_h],[3*tol,egg_h-m-2*tol],[-3*tol,egg_h-m-2*tol]]);
        translate([-egg_w/2,3*tol/2,r-2*tol])mirror([1,1,0])mirror([0,1,1])cylinder(r=r+4*tol,h=egg_w);
        //translate([-egg_w/2,-egg_w/2,egg_h-m-2*tol])cube([egg_w,egg_w,m]);
        //for(i=[-8:8])translate([0,3*tol/2+i*egg_w/16,egg_h-m-2*tol])
        //    cube([egg_w,egg_w/16-AT,tol],center=true); // bridge helper
    }
}

module mir(){
    children();
    mirror([0,0,1])children();
}

module egg(width, length, tol, off){
    rotate_extrude()intersection(){
        translate([0,tol-off])egg_outline(width-2*tol,length-2*tol);
        translate([0,tol])square([egg_w,egg_h]);
    }
}

//use <MCAD/regular_shapes.scad>
// The ratio of length and width is about 1.39 for a real egg
module egg_outline(width, length){
    translate([0, width/2, 0]) union(){
        rotate([0, 0, 180]) difference(){
            ellipse(width, 2*length-width);
            translate([-length/2, 0, 0]) square(length);
        }
        circle(r=width/2);
    }
}

module ellipse(width, height) {
  scale([1, height/width, 1]) circle(r=width/2);
}
```


[User]
I'd like to generate a customizable LEGO Duplo brick with text on each of its sides, including the top. The brick should be parametric, allowing me to adjust its dimensions, stud presence, and text properties.

Specifically, here's what I need:

*   **Brick Dimensions:** I need to be able to set the length and width of the brick in studs (X\_size and Y\_size), and the height as a multiple of the standard brick height (Height\_factor).

*   **Studs:** I need a toggle to enable or disable studs on the top of the brick (Studs). If studs are enabled, they should be properly placed. When adding text to the top, studs should be disabled.

*   **Internal Structure:** The brick should have the standard internal cylinder structure for compatibility with other Duplo bricks. The internal cylinder structure should scale with the size of the brick.

*   **Support Ridges:** There should be an option to include extended triangular support ridges on the inside of the brick (Extended\_Ridges). These should be generated in a manner consistent with Duplo bricks.

*   **Text on All Sides:** I need to be able to add text to each of the six sides of the brick: top, front, back, left, and right. For each side's text, I need to be able to control:
    *   The text itself (e.g., `top_text = "Top"`)
    *   The font (e.g., `top_font = "Arial"`)
    *   The font size (e.g., `top_letter_size = 12`)
    *   The text height/thickness (e.g., `top_letter_height = 0.8`)
    *   The scaling behavior ("both", "x", "y")
    *   A border around the text that adjusts the sizing. For top and bottom this will be (top_borderX and top_borderY) and for the front back, left and right it will be (F/B/LH/RH\_borderW and F/B/LH/RH\_borderH)

*   **Parametric Text Placement:** The text on each side should be automatically centered and scaled to fit within the available space, while respecting the specified border.

*   **Default Values:** Please include reasonable default values for all parameters, so I don't have to set everything initially.

I think this is a pretty standard structure, I'm not looking for advanced features here. I'm not using any libraries that I am aware of. Could you generate an OpenSCAD file for this?

[Assistant]
duplo_generator_with_text.scad:
```
//
// LEGO DUPLO BRICK GENERATOR
// Remix: with Text
//
// Author: Emanuele Iannone (emanuele@fondani.it)
// Remix: Daniel Rusch
//
// Url: https://www.thingiverse.com/thing:3113862
// Remix-URL: https://www.thingiverse.com/thing:4590556

/* [Main] */

// Size along X axis (in studs)
X_size = 6;

// Size along Y axis (in studs)
Y_size = 2;

// (1 = standard brick height)
Height_factor = 1;

// Enable or disable studs
Studs = 0; // [0:No studs, 1:With studs]

// Add extended triangular support ridges
Extended_Ridges = 1; // [0:No extended ridges, 1:Add extended ridges]

/* [Advanced] */
// Size of a single stud (in mm)
base_unit = 15.75;
// Standard height of a block
std_height = 19.25;
// Vertical walls thickness
wall_thickness = 1.29;
// Top surface thickness
roof_thickness = 1.4;
// Stud outer radius
stud_radius = 4.7;
// Internal cylinder outer radius
cyl_radius = 6.6;
// Internal cylinder thickness
cyl_thickness = 1;
// Ridge thickness
ridge_thickness = 1;
// Ridge extension (control grip)
ridge_width = 3.1;

// Test and Textsetting added by CAD_Daniel 07.09.2020
// Text on Top
top_text = "Top"; // Text on Top
top_letter_size = 8; // Fontsize
top_font = "Ethnocentric"; // Font
top_letter_height = 0.6; // letter height
top_scale = "both"; // x y both
top_borderX = 15; // Border around the text X direction
top_borderY = 10; // Border around the text Y direction

// Text on Front
F_text = "Front"; // Text on Front
F_letter_size = 14; // Fontsize
F_font = "Arial"; // Font
F_letter_height = 0.8; // letter height
F_scale = "both"; // w h both
F_borderW = 15; // Border around the text width
F_borderH = 5; // Border around the text height

// Text on Front
B_text = "Back"; // Text on Front
B_letter_size = 13; // Fontsize
B_font = "Times New Roman"; // Font
B_letter_height = 0.8; // Letter letter height
B_scale = "both"; // w h both
B_borderW = 5; // Border around the text widht
B_borderH = 5; // Border around the text height

// Text on Left Side
LH_text = "Left"; // Text on Left Side
LH_letter_size = 10; // Fontsize
LH_font = "Blackout"; // Font
LH_letter_height = 0.8; // Letter letter height
LH_scale = "both"; // w h both
LH_borderW = 0; // Border around the text width
LH_borderH = 0; // Border around the text height

// Text on Right Side
RH_text = "Right"; // Text on Right Side
RH_letter_size = 11; // Fontsize
RH_font = "Verdana"; // Font
RH_letter_height = 0.8; // Letter letter height
RH_scale = "w"; // w h both
RH_borderW = 5; // Border around the text width
RH_borderH = 5; // Border around the text height

brick(X_size, Y_size, Height_factor, Studs);

module brick(nx, ny, heightfact, studs=true){
    height = heightfact * std_height;
    dx = nx * base_unit;
    dy = ny * base_unit;
    union() {
        // Shell
        difference() {
            cube([dx, dy, height]);
            translate(v=[wall_thickness, wall_thickness, -1]) {
                cube([dx - (wall_thickness * 2), dy - (wall_thickness * 2), height - roof_thickness + 1]);
            }
        }

        // Studs
        if (studs) for (r=[0:nx-1])for (c=[0:ny-1]){
            stud((base_unit/2) + r * base_unit, base_unit/2 + c * base_unit, height, stud_radius);
        }

        // Internal cylinders
        if (nx > 1 && ny > 1) for(r=[1:nx-1]) for(c=[1:ny-1]) {
            internal_cyl(base_unit * r, base_unit * c, height - roof_thickness + 0.1);
        }

        // Ridges
        ridgeset(0, 0, 0, nx, height);
        ridgeset(dx, dy, 180, nx, height);
        ridgeset(0, dy, -90, ny, height);
        ridgeset(dx, 0, 90, ny, height);

        // Top Text
        if (len(top_text)>0){
        translate(v=[
            X_size*base_unit/2,
            Y_size*base_unit/2,
            height]){
                if (top_scale == "both") {
                    color("gray")
                    resize(newsize=[X_size*base_unit-top_borderX,Y_size*base_unit-top_borderY,RH_letter_height])
                    letter(top_text,top_letter_size,top_font,top_letter_height);
                    }
                else if (top_scale == "x") {
                    color("gray")
                    resize(newsize=[X_size*base_unit-top_borderX,0,RH_letter_height], auto=true)
                    letter(top_text,top_letter_size,top_font,top_letter_height);
                    }
                else if (top_scale == "y") {
                    color("gray")
                    resize(newsize=[0,Y_size*base_unit-top_borderY,RH_letter_height], auto=true)
                    letter(top_text,top_letter_size,top_font,top_letter_height);
                    }
                else {
                    color("gray")
                    letter(top_text,top_letter_size,top_font,top_letter_height);
                    }
                }
            }

        // Front Text
        if (len(F_text)>0){
        translate(v=[
            X_size*base_unit/2,
            0,
            height/2]){
                if (F_scale == "both") {
                    rotate(a=[90,0,0])
                    color("green")
                    resize(newsize=[X_size*base_unit-F_borderW,height-F_borderH,F_letter_height], auto=true)
                    letter(F_text,F_letter_size,F_font,F_letter_height);}
                else if (F_scale == "w") {
                    rotate(a=[90,0,0])
                    color("red")
                    resize(newsize=[X_size*base_unit-F_borderW,0,F_letter_height], auto=true)
                    letter(F_text,F_letter_size,F_font,F_letter_height);}
                else if (F_scale == "h") {
                    rotate(a=[90,0,0])
                    color("green")
                    resize(newsize=[0,height-F_borderH,F_letter_height], auto=true)
                    letter(F_text,F_letter_size,F_font,F_letter_height);}
                else {
                    rotate(a=[90,0,0])
                    color("green")
                    letter(F_text,F_letter_size,F_font,F_letter_height);}
                }
        }
        // Back Text
        if (len(B_text)>0){
        translate(v=[
            X_size*base_unit/2,
            Y_size*base_unit,
            height/2]){
                if (B_scale == "both") {
                    rotate(a=[90,0,180])
                    color("red")
                    resize(newsize=[X_size*base_unit-B_borderW,height-B_borderH,B_letter_height], auto=true)
                    letter(B_text,B_letter_size,B_font,B_letter_height);}
                else if (B_scale == "w") {
                    rotate(a=[90,0,180])
                    color("red")
                    resize(newsize=[X_size*base_unit-B_borderW,0,B_letter_height], auto=true)
                    letter(B_text,B_letter_size,B_font,B_letter_height);}
                else if (B_scale == "h") {
                    rotate(a=[90,0,180])
                    color("red")
                    resize(newsize=[0,height-B_borderH,B_letter_height], auto=true)
                    letter(B_text,B_letter_size,B_font,B_letter_height);}
                else {
                    rotate(a=[90,0,180])
                    color("red")
                    letter(B_text,B_letter_size,B_font,B_letter_height);}
                }
        }
        // Left Text
        if (len(LH_text)>0){
        translate(v=[
            0,
            Y_size*base_unit/2,
            Height_factor * std_height/2]){
                if (LH_scale == "both") {
                    rotate(a=[90,0,270])
                    color("white")
                    resize(newsize=[Y_size*base_unit-LH_borderW,height-LH_borderH,LH_letter_height], auto=true)
                    letter(LH_text,LH_letter_size,LH_font,LH_letter_height);}
                else if (LH_scale == "w") {
                    rotate(a=[90,0,270])
                    color("white")
                    resize(newsize=[Y_size*base_unit-LH_borderW,0,LH_letter_height], auto=true)
                    letter(LH_text,LH_letter_size,LH_font,LH_letter_height);}
                else if (LH_scale == "h") {
                    rotate(a=[90,0,270])
                    color("white")
                    resize(newsize=[0,height-LH_borderH,LH_letter_height], auto=true)
                    letter(LH_text,LH_letter_size,LH_font,LH_letter_height);}
                else {
                    rotate(a=[90,0,270])
                    color("white")
                    letter(LH_text,LH_letter_size,LH_font,LH_letter_height);}
                }
        }
        // Right Text
        if (len(RH_text)>0){
        translate(v=[
            X_size*base_unit,
            Y_size*base_unit/2,
            height/2]){
                if (RH_scale == "both") {
                    rotate(a=[90,0,90])
                    color("blue")
                    resize(newsize=[Y_size*base_unit-RH_borderW,height-RH_borderH,RH_letter_height], auto=true)
                    letter(RH_text,RH_letter_size,RH_font,RH_letter_height);}
                else if (RH_scale == "w") {
                    rotate(a=[90,0,90])
                    color("blue")
                    resize(newsize=[Y_size*base_unit-RH_borderW,0,RH_letter_height], auto=true)
                    letter(RH_text,RH_letter_size,RH_font,RH_letter_height);}
                else if (RH_scale == "h") {
                    rotate(a=[90,0,90])
                    color("blue")
                    resize(newsize=[0,height-RH_borderH,RH_letter_height], auto=true)
                    letter(RH_text,RH_letter_size,RH_font,RH_letter_height);}
                else {
                    rotate(a=[90,0,90])
                    color("blue")
                    letter(RH_text,RH_letter_size,RH_font,RH_letter_height);}
                }
        }
    }
}

// letter(text, size, font, height/thickness)
module letter(l,s,f,h) {
	linear_extrude(height = h) {
		text(l, size = s, font = f, halign = "center", valign = "center", $fn = 16);
	}
}

module stud(x, y, z, r){
  translate(v=[x, y, 1.875 + z]){
    difference(){
       cylinder(h=3.75, r=r, center=true, $fn=100);
       cylinder(h=4, r=r-1.5, center=true, $fn=100);
    }
  }
}

module internal_cyl(x, y, h) {
    translate(v=[x, y, 0]) difference() {
        union() {
            cylinder(h=h, r=cyl_radius, $fn=100);
            translate([0,0,h]) {
                rotate([-90,0,45]) linear_extrude(height = ridge_thickness, center=true)
                    polygon([[-base_unit*0.7,0],[base_unit*0.7,0],[0,14]]);
                rotate([-90,0,-45]) linear_extrude(height = ridge_thickness, center=true)
                    polygon([[-base_unit*0.7,0],[base_unit*0.7,0],[0,14]]);
            }
        }
        cylinder(h=h*3, r=cyl_radius-cyl_thickness, center=true, $fn=100);
        cube(size=[cyl_radius*2, 1, 10], center=true);
        cube(size=[1, cyl_radius*2, 10], center=true);
    }
}

module ridgeset(x, y, angle, n, height){
    translate([x, y, 0]) rotate([0, 0, angle]) {
        for (i=[1:2*n-1]){
            // Ridge
            translate([(i*base_unit - ridge_thickness)/2,0,0]) rotate([90,0,90]) {
                if (Extended_Ridges) {
                    linear_extrude(height = ridge_thickness, center=true)
                        polygon([[0,height - std_height],[0,height],[10,height]]);
                }
                if (i % 2) cube([ridge_width, height, ridge_thickness]);
            }
        }
    }
}
```


[User]
Hi, I'm looking to design a parametric document camera mount for a laptop, similar to the one described in the text you have. I want to be able to attach it to the top of a laptop screen and have a mirror that sits at an angle to act as a document camera, using the laptop's built-in webcam.

I need the design to be customizable, specifically around the following parameters:

*   **Mirror Dimensions:** I need to be able to specify the width, height, and thickness of the mirror I'm using. I anticipate these values are in millimeters, but make sure the units are clear in any output.
*   **Laptop Monitor Thickness:** The design needs to securely clip onto various laptop monitor thicknesses. There should be an adjustable parameter for the maximum monitor thickness that the mount can accommodate, and ideally another that allows me to specify a variance or minimum thickness for the clamping.
*   **Interference Fit:**  There will be parts that snap together, so I'd like a parameter to control the tightness of these fits – basically a small adjustment factor to get the perfect snap.
*   **Vertical Mirror Offset:** There should be a parameter for adjusting the vertical position of the mirror relative to the monitor bezel, in case my mirror sits too high or too low.
*   **Shell Thickness:** The general wall thickness of the print, for strength considerations.
*   **Mirror Bezel Thickness:** The amount of material that surrounds the mirror's edges.
*   **Axle Radius:** The radius of the circular pieces that provide the pivot mechanism.
*   **Fillet Radius:**  The radius of any filleted (rounded) edges on the design.
*   **Nub Diameter and Scale:** Dimensions for small nubs used to help the rotational part snap into place.

I'd like the OpenSCAD design to generate two main parts: a 'back' clip that attaches to the monitor, and a 'front' piece that holds the mirror and pivots on the 'back' piece.  Ideally, I'd like a test piece that lets me measure monitor lid thickness, like the `MonitorLidGauge.scad` mentioned. It would also be useful to have an option to set a parameter to generate just the mirror holder itself, rather than the whole unit. It should also be able to easily switch to use a value for mirror dimensions and offset as a command-line argument instead of a customizer option.

I don't need any specific library dependencies, just vanilla OpenSCAD code. Can you generate this for me? I'd like a good starting point for my project, and I can always refine later.

[Assistant]
DocuCam.scad:
```
// Adjustable mirror for laptop camera as document camera
// Copyright Michael K Johnson
// https://creativecommons.org/licenses/by/4.0/ CC BY 4.0

/* [Mirror Measurement] */

// Measure your mirror in one dimension, add about 1mm for clearance
mirror_width = 51.8;
// Measure your mirror in the other dimension, add about 1mm for clearance
mirror_height = 51.8;
// Measure actual mirror thickness, but at least 0.3 for common slicer configuration
mirror_thickness = 1; // [0.3:5]

/* [Computer measurement] */
// Maximum supported monitor thickness; intended to work with up to 2mm thinner monitor lids
monitor_thickness = 7;

/* [Adjustments] */

// If the prints are too hard to snap together at all, or if adjustment is too stiff, increase this; if the adjustment is too loose to hold, decrease it
interference = .15; // [0:0.5]

// If the mirror is too far down, move it up by adding to this offset in mm; if it is too far up, this can be a negative number
monitor_bezel_offset = 0;

/* [Hidden] */
// If you need to adjust these parameters, you probably need to be in OpenSCAD not customizer, and be reading the code

monitor_variance = 2; // monitor_thickness - monitor_variance is minimum supported thickness
monitor_back_height = 30;
shell = 2;
mirror_bezel = 5;
axle_radius = 10;
fillet_r = 1;

nub_d = 10;
nub_scale = 0.75;

total_y = mirror_width + 2*mirror_bezel;
camera_width = mirror_width - 2* mirror_bezel; // cutout for camera to see through
back_x = monitor_thickness + shell + monitor_variance + axle_radius;
total_front_thickness = shell + mirror_thickness;
inside_mirror_offset = sqrt( 2*axle_radius*(total_front_thickness)
                             - pow(total_front_thickness, 2)) + shell;
front_x = mirror_height + mirror_bezel;

pivot_h = mirror_bezel;
pivot_offset = camera_width/2 + pivot_h/2;
tang_w = 3 * pivot_h;
tang_thickness = 3;
tang_pressure = 1; // extra distance beyond monitor_variance thinnest supported bezel for positive pressure
tang_foot = 4;

$fn=60;

module nubs(extra=0) {
    // Snap to hold the rotating parts concentric
    for (i=[-1, 1]) {
        translate([0, i*mirror_width/2, axle_radius-shell/2])
            scale([1, nub_scale, 1]) sphere(d=nub_d+extra);
    }
}

module lid_proxy(increment) {
    translate([-(axle_radius+monitor_thickness), -150, shell/2+increment])
        cube([monitor_thickness, 300, 200]);
}

module back() {
    bezel_z = axle_radius+monitor_bezel_offset;
    //%lid_proxy(bezel_z);
    difference() {
        union() {
            // bottom plate
            translate([-back_x/2, 0, 0])
                cube([back_x, total_y, shell], center=true);
            // monitor back flange
            translate([shell/2-back_x, 0, shell])
                cube([shell, total_y, shell*2], center=true);
            for (i=[-1, 1]) {
                // monitor offset
                translate([-axle_radius+shell, i*(pivot_offset+pivot_h/2), shell/2+bezel_z/2])
                    cube([axle_radius, pivot_h*2, bezel_z], center=true);
                y = i*pivot_offset;
                hull() {
                    // axle body
                    translate([0, y, axle_radius-shell/2]) rotate([90, 0, 0])
                        cylinder(r=axle_radius, h=pivot_h, center=true);
                    translate([-(axle_radius-fillet_r), y, axle_radius*2.25-2*fillet_r]) rotate([90, 0, 0])
                        cylinder(r=fillet_r, h=pivot_h, $fn=15, center=true);
                }
                // screen holder bezel tangs
                t = tang_thickness;
                x = monitor_variance+tang_pressure-(axle_radius+monitor_thickness+t/2);
                hull() {
                    // top of bezel tang foot
                    translate([x, y, bezel_z+axle_radius+tang_foot]) rotate([90, 0, 0])
                        cylinder(d=t, h=tang_w, $fn=15, center=true);
                    // middle of bezel tang
                    translate([x, y, bezel_z+axle_radius-tang_foot]) rotate([90, 0, 0])
                        cylinder(d=t, h=tang_w, $fn=15, center=true);
                }
                hull() {
                    // middle of bezel tang
                    translate([x, y, bezel_z+axle_radius-tang_foot]) rotate([90, 0, 0])
                        cylinder(d=t, h=tang_w, $fn=15, center=true);
                    // bottom of bezel tang
                    translate([-back_x, y, t/2-shell/2]) rotate([90, 0, 0])
                        cylinder(d=t, h=tang_w, $fn=15, center=true);
                }
            }
        }
        // remove space for camera from axle
        translate([0, 0, axle_radius-shell/2]) rotate([90, 0, 0])
            cylinder(r=axle_radius+interference, h=camera_width, center=true);
        // remove front axle interference fillet
        for (i=[-1, 1]) {
            translate([0, i*(total_y/2 - mirror_bezel/2 + .1), axle_radius-shell/2]) rotate([90, 0, 0])
                cylinder(r=axle_radius+interference, h=mirror_bezel+.2, center=true);
            // remove useless fillet
            translate([0, i*(total_y/2), 0])
                cube([axle_radius* .7, mirror_bezel*2, shell*2], center=true);

        }
        // remove useless fillet
        cube([inside_mirror_offset*2, camera_width, shell*2], center=true);
    }
    nubs();
}

module front() {
    difference() {
        union() {
            // base
            translate([front_x/2 + inside_mirror_offset, 0, mirror_thickness/2])
                cube([front_x, total_y, shell+mirror_thickness], center=true);
            // axles
            for (i=[-1, 1]) {
                y = i*(total_y/2 - mirror_bezel/2);
                // axle
                translate([0, y, axle_radius-shell/2]) rotate([90, 0, 0])
                    cylinder(r=axle_radius, h=mirror_bezel, center=true);
                hull() {
                    // attach
                    translate([axle_radius*2, y, 0]) rotate([90, 0, 0])
                        cylinder(d=shell, h=pivot_h, center=true, $fn=12);
                    translate([axle_radius-shell/2, y, axle_radius-shell/2]) rotate([90, 0, 0])
                        cylinder(d=shell, h=pivot_h, center=true, $fn=12);
                    translate([shell, y, axle_radius/2]) rotate([90, 0, 0])
                        cylinder(d=shell, h=pivot_h, center=true, $fn=12);
                    translate([shell, y, axle_radius/2]) rotate([90, 0, 0])
                        cylinder(d=shell, h=pivot_h, center=true, $fn=12);
                    translate([mirror_bezel + shell*2, y, 0]) rotate([90, 0, 0])
                        cylinder(d=shell, h=pivot_h, center=true, $fn=12);
                }
            }
        }
        // remove space for nubs
        nubs(interference*2);
        // remove space for mirror
        translate([inside_mirror_offset, -mirror_width/2, shell/2])
           cube([mirror_height, mirror_width, mirror_thickness*2]);
    }
}

module set(offset=0) {
    translate([0, 0, shell/2]) {
        translate([-offset, 0, 0]) back();
        translate([offset, 0, 0]) front();
    }
}

//set();
set(axle_radius+2);
```
MonitorLidGauge.scad:
```
// Monitor lid gauge
// Copyright Michael K Johnson
// https://creativecommons.org/licenses/by/4.0/ CC BY 4.0

difference() {
    step=6;
    start=2;
    end=8;
    cube([end+step*1.6, (2+end-start)*step, 1.5]);
    for(x=[start:end]) {
        n=1+x-start;
        translate([step, step*n, -.1]) cube([x, step, 2]);
        translate([step*0.8, step*n, 0.9]) linear_extrude(1) text(str(x), size=step-1, halign="right");
    }
}
```


[User]
I'm looking to create a parametric design for 3D printing that will act as a connector for wooden barbecue skewers, allowing me to build small trellises for my wife's plants. I envision these connectors as clips that snap onto the skewers.

Here are the key requirements:

*   **Parametric Design:** I need to be able to easily adjust the design based on skewer diameter, shell thickness of the connector, and the length of the connector.
*   **Basic Clip Shape:** The core of the design should be a clip that can accommodate two perpendicular skewers.
*   **Adjustable Clip Dimensions:** The clips should have a circular channel to fit the skewer, with a specified thickness for the clip material.
*   **"Extra Hole" Feature:** I'd like to include an optional extra hole, positioned on the side, that would allow a skewer to protrude at an angle. This feature should be togglable on or off.
*   **Symmetrical Design:** The overall design should be symmetrical so that orientation doesn't matter when assembling the trellis.
*   **Two-Clip Element:** I'd like to have a module that combines two clips into a single part, with the clips positioned on opposite sides and oriented perpendicular to each other. This module should have the same parameters as the individual clips.
*   **Optional Spherical End Caps:** I'd like to include the option for the clips to have rounded ends, creating a smoother, more finished look. This can be a simple spherical shape for the end.
*   **Units:** Everything should be designed using millimeters (mm).
*   **OpenSCAD Libraries:** I would prefer if you *didn't* rely on external libraries, keeping things as simple as possible.

The final design should consist of a set of OpenSCAD modules to create single clips, a clip element consisting of two clips, an extra hole, and the spherical end caps. The parameters should be straightforward to change: skewer diameter, shell thickness, length of the clip, and a toggle for the extra hole.

I'm excited to see what you come up with!

[Assistant]
BBQ-Stick-Fence-Trellis-v5-Thingiverse.scad:
```
//All values in millimeters (mm)

inner_diameter = 3.2;
shell_thickness = 0.8;
inner_radius = inner_diameter/2;
outer_radius = inner_radius+shell_thickness;
length = 40;
show_extra_hole = "yes";//[yes,no]

module clip (){

difference(){

cylinder (r=outer_radius,h=length,$fn=50,center=true);
cylinder (r=inner_radius,h=length,$fn=50,center=true);

translate ([0,-(outer_radius+(inner_radius*0.75)),0]){
cube ([(outer_radius*2),(outer_radius*2),length],center=true);
}

}

difference (){

rotate ([0,90,0]){

difference (){
cube ([(shell_thickness),((outer_radius*2)+(shell_thickness)),length],center=true);
translate ([0,-(outer_radius+(inner_radius*0.75)),0]){
cube ([(outer_radius*2),(outer_radius*2),length],center=true);
}

}

}

cylinder (r=outer_radius,h=length,$fn=50,center=true);

}

}

module clipx2 (){

translate ([0,-outer_radius,0]){
clip ();
}
translate ([0,outer_radius,0]){
rotate ([180,90,0]) clip ();
}

}

//work in progress
*difference(){
difference(){
cylinder (r=outer_radius,h=(outer_radius+(inner_radius*0.75))*2,center=true,$fn=50);
cylinder (r=inner_radius,h=(outer_radius+(inner_radius*0.75))*2,center=true,$fn=50);
}
translate ([0,-(outer_radius+(inner_radius*0.75)),0]){
cube ([(outer_radius*2),(outer_radius*2),(outer_radius+(inner_radius*0.75))*2],center=true);
}
}

module extra_hole () {
translate([(outer_radius*2)-shell_thickness,0,(outer_radius*2)-shell_thickness])
rotate([90,215,0])
difference(){
difference(){
cylinder (r=outer_radius,h=(outer_radius+(inner_radius*0.75))*2,center=true,$fn=50);
cylinder (r=inner_radius,h=(outer_radius+(inner_radius*0.75))*2,center=true,$fn=50);
}
translate ([0,-(outer_radius+(inner_radius*0.75)),0]){
cube ([(outer_radius*2),(outer_radius*2),(outer_radius+(inner_radius*0.75))*2],center=true);
}
}

}

module completed (){
clipx2 ();
if (show_extra_hole == "yes") {
extra_hole ();
}
}

rotate ([90,0,0]){
union(){

intersection(){

completed ();

sphere (length/2, $fn=50);

}
}
}
```


[User]
I'm looking to design a parametric spool holder for my workbench. I'd like it to hold multiple small spools, things like wire, solder, and so on. The holder needs to be stable just from its own weight and friction on the workbench, so it shouldn't require any mounting hardware.

Here's what I envision:

*   **Multiple Spools:** I need the ability to specify how many spools the holder will hold.
*   **Spool Dimensions:**  I'll need to set the width and diameter of the spools, as these will vary.
*   **Spacing:**  The holder needs to have some margin between the spools and the holder walls, and also between each spool. I'd like to set these independently.
*   **Cage/Retainer:** The spools need some sort of cage or retainer on the sides, I'd like to control its size.
*   **Eye Hole:** There should be an eye hole on the front to thread the wire, and I'd like to specify its height and size.
*   **Thickness:** I need to be able to adjust the wall thickness, of the base, sides, and retaining features.
*   **Fillets:**  There should be fillets on the corners of the side pieces, and the fillet radius should be adjustable.
*  **Bridge Height:** There should be a bridge at the bottom of the side walls that can have its height controlled by a parameter.

I'd like the design to use OpenSCAD, and the design should be parametric so I can easily adjust these dimensions. I'd prefer if the design was done without relying on external libraries, except the `my_sphere.scad` library. I can share a library file if that helps, but it should be something simple like a sphere primitive to use.

[Assistant]
Spool_holders.scad:
```
use <lib/my_sphere.scad>

/* [Spool parameters] */
spool_count    =    5; // [1:50]
spool_width    =   46; // [10:0.1:300]
spool_diameter =   92; // [10:0.1:400]
margin         =  0.5; // [0:0.1:10]
spool_cage     =    7; // [0:50]
eye_height     = 12.4; // [0:0.1:200]
eye_size       = 1.25; // [0.5:0.05:30]
wall_thickness =    3; // [0.1:0.05:10]
fillet_radius  =    7; // [0:0.1:10]
bridge_height  =    5; // [1:10]

pitch = spool_width + margin * 2 + wall_thickness;

module mockup_spool() {
    %render() {
        translate([wall_thickness + margin, wall_thickness + margin + spool_diameter / 2, wall_thickness + spool_diameter / 2])
        rotate([-90, -90, -90])
        cylinder(d = spool_diameter, h = 2, $fn = 96);

        translate([wall_thickness + margin + spool_width - 2, wall_thickness + margin + spool_diameter / 2, wall_thickness + spool_diameter / 2])
        rotate([-90, -90, -90])
        cylinder(d = spool_diameter, h = 2, $fn = 96);

        translate([wall_thickness + margin, wall_thickness + margin + spool_diameter / 2, wall_thickness + spool_diameter / 2])
        rotate([-90, -90, -90])
        cylinder(d = spool_diameter / 3 + 2, h = spool_width, $fn = 96);
    }
}

module fillet() {
    *rotate([-90, -90, -90])
    translate([-fillet_radius, -fillet_radius, 0])
    difference() {
        cube([fillet_radius, fillet_radius, wall_thickness]);
        translate([0, 0, -1])
        cylinder(r = fillet_radius, h = wall_thickness + 2, $fn = $preview ? 16 : 36);
    }
}

module side_cage(slant = false) {
    translate([0, 0, -0.0001]) {
        intersection() {
            rotate([-90, -90, -90])
            cylinder(r = spool_cage, h = wall_thickness, $fn = $preview ? 16 : 48);

            translate([-1, 0, 0])
            cube([wall_thickness + 2, spool_cage + 1, spool_cage + 1]);
        }

        if (slant) {
            xy = spool_cage * sqrt(0.5);

            rotate([-90, -90, -90])
            mirror([0, 1, 0])
            linear_extrude(wall_thickness)
            polygon([[0, 0], [xy, xy], [0, 2 * xy]]);
        }
    }
}

module bottom() {
    difference() {
        cube([margin * 2 + spool_width    + wall_thickness * 2,
              margin * 2 + spool_diameter + wall_thickness * 2,
              wall_thickness]);

        translate([wall_thickness * 2, wall_thickness * 2, -1])
        cube([margin * 2 - wall_thickness * 2 + spool_width,
              margin + spool_diameter / 2 - wall_thickness * 2,
              wall_thickness + 2]);

        translate([wall_thickness * 2, wall_thickness * 2 + margin + spool_diameter / 2, -1])
        cube([margin * 2 - wall_thickness * 2 + spool_width,
              margin + spool_diameter / 2 - wall_thickness * 2,
              wall_thickness + 2]);
    }
}

module front_back() {
    difference() {
        cube([spool_width + margin * 2 + wall_thickness * 2, wall_thickness, spool_diameter / 2 + margin + wall_thickness]);

        translate([wall_thickness * 2, -1, wall_thickness * 2])
        cube([spool_width + margin * 2 - wall_thickness * 2, wall_thickness + 2, spool_diameter / 2 + margin - wall_thickness * 3]);
    }
}

module side_panel() {
    difference() {
        cube([wall_thickness, margin * 2 + spool_diameter + wall_thickness * 2, spool_diameter / 2 + margin + wall_thickness]);

        translate([-1, wall_thickness * 2, wall_thickness * 2])
        cube([wall_thickness + 2, margin * 2 + spool_diameter - wall_thickness * 2, spool_diameter / 2 + margin - wall_thickness + 1]);
    }

    translate([0, wall_thickness * 2, wall_thickness * 2])
    rotate([90, 0, 0])
    fillet();

    translate([0, margin * 2 + spool_diameter, wall_thickness * 2])
    rotate([180, 0, 0])
    fillet();

    translate([0, wall_thickness * 2, wall_thickness + margin + spool_diameter / 2])
    rotate([270, 0, 0])
    side_cage(true);

    translate([0, margin * 2 + spool_diameter, wall_thickness + margin + spool_diameter / 2])
    mirror([0, 1, 0])
    rotate([270, 0, 0])
    side_cage(true);

    translate([0, wall_thickness + margin + spool_diameter / 2, wall_thickness * 2]) {
        side_cage();
        mirror([0, 1, 0])
        side_cage();
    }
}

module spool_box() {
    bottom();

    front_back();
    translate([0, wall_thickness + spool_diameter + margin * 2, 0])
    front_back();

    side_panel();
    translate([wall_thickness + margin * 2 + spool_width, 0, 0])
    side_panel();

    // Eye hole
    translate([wall_thickness + margin + spool_width / 2, 0, eye_height]) {
        difference() {
            union() {
                translate([-wall_thickness - eye_size / 2, 0, -eye_height])
                cube([eye_size + wall_thickness * 2, wall_thickness, eye_height]);

                rotate([-90, 90, 0])
                cylinder(d = eye_size + wall_thickness * 2, h = wall_thickness, $fn = $preview ? 12 : 36);
            }

            translate([0, -1, 0])
            rotate([-90, 90, 0])
            cylinder(r = eye_size / 2 + wall_thickness / 2, h = wall_thickness + 2, $fn = 6);
        }
    }

    // Eye torus
    translate([wall_thickness + spool_width / 2 + margin, wall_thickness / 2, eye_height])
    rotate([-90, 90, 0])
    torus(inner = eye_size / 2, minor = wall_thickness * 0.4999, major_segments = 6, minor_segments = $preview ? 16 : 32);
}

module handle() {
    translate([0, 0, wall_thickness + margin + spool_diameter / 2 - bridge_height])
    cube([wall_thickness, wall_thickness * 2 + margin * 2 + spool_diameter, bridge_height]);

    translate([0, wall_thickness / 2 + margin + spool_diameter / 2, 0])
    cube([wall_thickness, wall_thickness, wall_thickness + margin + spool_diameter / 2]);
 }

for (i = [0:spool_count - 1]) {
    translate([pitch * i, 0, 0]) {
        spool_box();
        mockup_spool();
    }
}

handle();

translate([spool_count * (wall_thickness + margin * 2 + spool_width) + wall_thickness, wall_thickness * 2 + margin * 2 + spool_diameter, 0])
rotate([0, 0, 180])
handle();

```


[User]
I'm looking to create a parametric hexagon grid generator in OpenSCAD, similar to the one I've seen, but I'd like it to be a bit more flexible. I'm attaching the original script for reference, but I'd like you to generate new code. The main things I need are:

*   **Adjustable hexagon size:** I want to control the length of the sides of the hexagons.
*   **Adjustable grid spacing:** Both the spacing between the hexagons horizontally and vertically should be adjustable.
*   **Grid dimensions:** I need to be able to specify how many hexagons there are in the X and Y directions.
*   **Grid plate thickness:** The base plate under the hexagons should have a variable thickness.
*   **Full hexes only:** I want full hexagons, no partial ones on the edges.
*   **Customizable base plate:** I want the baseplate to extend beyond the hexagons; that is, there should be a border around the entire array of hexagons, and I'd like to be able to control how far that border extends.
*   **Rounded corners on the baseplate:** It would be nice if the baseplate had rounded corners.

Essentially, I want a function where I can specify these parameters and get a nice, clean hexagon grid object with a base plate, ready for 3D printing. I do not need any customizer functionality.

I'm not familiar with all the OpenSCAD libraries, but please don't assume I want to use any. I am open to using a well-documented library if you believe it will reduce code complexity. Please don't introduce any new functionality, such as the option to use different shapes, but please include inline documentation as you see fit. Thanks!

[Assistant]
hex_mesh_1.scad:
```
//
//  Hexagonal grid generator
//  Joris Bots - August 2020
//

/* [Global] */
// length of a hexagon side
s = 6;   //[1:20]
// distance between hexagons (grid width )
d = 2;   //[0.5:10]
// number of hexagons in x direction (even numbers only!)
x = 20;  //[2:100]
// number of hexagons in y direction (even numbers only!)
y = 30;  //[2:100]
// height of the grid plate
h = 5;   //[1:10]

difference(){
translate([-s-(hexvspace()), -(hexheight()/2)-hexhspace(),0])
    cube([hexhspace()+x*hexhdist()+s/2,
          (hexvspace()+hexheight()/2)+hexvspace()/2+((hexheight() + hexvspace() ) * y/2),h]);

    for (j = [1:2:y]){
        for (i = [1:2:x]){
            translate([(s+(s/2)+hexhspace())*(i-1),(hexheight()/2+d/2)*j])hex(s);
            translate([(s+(s/2)+hexhspace())*i,(hexheight()/2+d/2)*(j-1)])hex(s);
        }
    }
}

module hex(l){
    $fn = 6;
    linear_extrude(h)circle(l);
}

function hexheight() = sqrt(3)*(s);       // vertical height of a hex
function hexvspace() = d;                 // vertical space between two hex
function hexhspace() = sqrt(3)*d/2;       // horizontal space between two hex
function hexhdist()  = 1.5*s+hexhspace(); // horizontal distance between two hex centres

```


[User]
Okay, I'm looking to create a parametric box design using OpenSCAD, and I'd like it to be flexible enough to be cut on a laser cutter or CNC router, or even a Cricut machine with a knife blade. It should be a lidded box with a double-layered construction, so the walls and top/bottom are all made of two pieces each - an inner and outer layer.

Here’s a breakdown of what I need:

**Overall Box Structure:**

*   The box should be a simple rectangular prism shape.
*   It needs a separate lid that fits snugly on top.
*   The box walls and lid should have this double-layered design, giving each surface an inner and outer component.

**Parametric Dimensions & Controls:**

*   **Box Dimensions:** I want to control the width (x), depth (y), and height (z) of the *inner dimensions* of the box. These should all be in millimeters.
*   **Material Thickness:** There needs to be a variable for the material thickness.
*   **Top Lid Relief:** I need to adjust a "relief" value. This should allow me to adjust how much bigger the lid is compared to the top edges of the box's base, for a slightly loose fit of the lid.
*   **Spacing:** I need a parameter for the spacing between different parts during layout.
*   **Border:** A border size value that can add a consistent border to the outer pieces.
*   **Ornaments:** I want an optional set of decorative ornaments on the outer pieces (sides and lid). I need to set a size, spacing between the ornaments and the option to turn them off entirely.
*   **Text:** I want to be able to put some text on the lid. I need to specify the text.

**Ornaments Specifically:**

*   I want to choose from a range of shapes for the ornaments like circles, wedges, diamonds, stars, hexagons, hearts, squares, and maybe a pirate skull with crossbones.
*   The ornaments should be cut out from the outer parts of the design, not just embossed, and should be slightly larger than the material thickness to enable the tool to cut it.

**Layout & Export:**

*   The output should be laid out as a flat 2D projection so that it can be exported as SVG. It should be able to be opened in Inkscape or other software for laser cutting, CNC or Cricut.
*   Each part should be output as a separate piece with correct parameters in the OpenSCAD file, no grouping, please.

**Libraries:**

*   I’m not sure if it's needed but please use the BOSL2 library if you think it's appropriate for the task.

**Additional Notes:**

*   Please include a calculated variable to determine the maximum width of the layout. I also want to include that width in the echo statement so I can be sure it will fit the material I intend to use.
* I don't expect to print this, only export to vector format (SVG) so keep that in mind.

I’m aiming for something that's both functional and has some aesthetic options. Let me know if you have any questions.

[Assistant]
cricutv7.scad:
```
/* [Box Dimensions] */
//Box width in mm
x=125; //[0:1:500]
//Box depth in mm
y=59; //[0:1:500]
//Box heigh in mm
z=30;  //[0:1:500]
/* [Layout control] */
//Loosenss of top in mm
relief=0.5; //[0.1:.1:5.0]
//Material thickness in mm
thickness=1.5; //[0.1:.1:10.0]
//Gap between pieces in mm
spacing=2.0;  //[0.1:.1:10.0]
/* [Customize appearance] */
//Side frame border size in mm
border=8;
//Diameter of ornamentation in mm
bling_size=11;
//Space between ornaments in mm
bling_spacing=7;
//Choose your onrnamet shape
bling=20;  //[1:none, 3:wedge, 4:diamond, 5:star, 6:hex, 7:heart, 8:square, 9:pirate, 20:circle]
//Text on box top
top_text="CRICUT";  /* the text that will appear on the box top */

wide=max(x+8*thickness+2*relief,y+4*thickness+2*relief+spacing+y+6*thickness+2*relief);
echo();
echo(str("      Max width is " ,floor(wide*100+.5)/100, " mm or ", floor(wide/.254+.5)/100," inches <======"));
echo();
//translate([0,0,-2])cube([11*25.4,17*25.4,1]);
projection()
layout();

module heart() {
   scale([0.828427124,0.906163678,1])translate([0,-0.103553391,0]){
      translate([.5,.5,0])
         cylinder(thickness+2,sqrt(2)/2,sqrt(2)/2,$fn=50,center=true);
      translate([-.5,.5,0])
         cylinder(thickness+2,sqrt(2)/2,sqrt(2)/2,$fn=50,center=true);
      cylinder(thickness+2,1,1,$fn=4,center=true);
   }
}

module star() {
translate([0,-0.097886967,0])for(th=[18:72:360])
   rotate([0,0,th])
      scale([1,0.562777422,1])
         translate([.3,0,0])cylinder(thickness+2,.7,.7,$fn=3,center=true);
}

module square() {
   rotate([0,0,45])cylinder(thickness+2,sqrt(2),sqrt(2),$fn=4,center=true);
}

module pirate() {
   minkowski() {
      scale([1.15,1,1])cylinder(.01,.2,.2,center=true,$fn=20);
      skull();
   }
   bone2();
   mirror()bone2();
}

module skull() {
   intersection()
      {
      translate([0,.3,0])cylinder(1,.5,.5,center=true,$fn=50);
      translate([0,.9,0])rotate([0,0,45])cube(1.5,center=true);
      translate([0,.8,0])cube(1.5,center=true);
      }
   cube([.1,.92,1],center=true);

}
 module bone2() {
    minkowski() {
    scale([1.15,1,1])cylinder(.01,.1,.1,center=true,$fn=20);
    bone();
    }
}
 module bone() {
    translate([.1,-.5,0])rotate([0,0,23])cube([1.2,.01,1],center=true);
    translate([.1,-.5,0])rotate([0,0,23])translate([-.6,0,0])rotate([0,0,45])translate([-.1,0,0])cube([.2,.01,1],center=true);
    translate([.1,-.5,0])rotate([0,0,23])translate([-.6,0,0])rotate([0,0,-45])translate([-.1,0,0])cube([.2,.01,1],center=true);
    translate([.1,-.5,0])rotate([0,0,23])translate([.6,0,0])rotate([0,0,135])translate([-.1,0,0])cube([.2,.01,1],center=true);
    translate([.1,-.5,0])rotate([0,0,23])translate([.6,0,0])rotate([0,0,-135])translate([-.1,0,0])cube([.2,.01,1],center=true);
 }

module bling(n) {
   if(n==1) {}
   if(n==5) {
      scale(bling_size/2)star();
   }
   if(n==7) {
      scale(bling_size/2)heart();
   }
   if(n==8) {
      scale(bling_size/2)square();
   }
   if(n==9) {
      scale(bling_size/2)pirate();
   }
   if(n!=1&&n!=5&&n!=7&&n!=8&&n!=9) {
      cylinder(thickness+2,bling_size/2,bling_size/2,$fn=n);
   }
}

module layout() {

/* Outer lid top with text */
difference() {
   cube([x+8*thickness+2*relief,y+8*thickness+2*relief,thickness]);
   translate([border,border,-1])cube([x+8*thickness+2*relief-2*border,y+8*thickness+2*relief-2*border,2*thickness+2]);
}
translate([(x+8*thickness+2*relief-2*border)/2.04+border,(y+8*thickness+2*relief-2*border)/2.05+border,0])scale([.95,1.05,1])resize([x+8*thickness+2*relief-2*border,y+8*thickness+2*relief-2*border,thickness])linear_extrude(1)text(top_text,font = "Consolas Sans:style=Bold ",$fn=100,halign="center",valign="center");

/* Inner lid top */
translate([0,y+8*thickness+2*relief+spacing,0])cube([x+6*thickness+2*relief,y+6*thickness+2*relief,thickness]);

/* Inner bottom */
translate([0,4*relief+2*spacing+14*thickness+2*y,0])cube([x,y,thickness]);

/* Outer bottom */
translate([0,4*relief+3*spacing+14*thickness+3*y,0])cube([x+2*thickness,y+2*thickness,thickness]);

/* Outer long top side*/
difference() {
   translate([0,4*relief+4*spacing+16*thickness+4*y,0])cube([x+8*thickness+2*relief,z/2+thickness,thickness]);
   k1=floor((x+8*thickness+2*relief-bling_spacing)/(bling_size+bling_spacing)+.5);
   q1=(x+8*thickness+2*relief-(k1*(bling_size+bling_spacing)-bling_spacing))/2;
   for(i=[0:1:k1-1]){
      translate([q1+bling_size/2+(bling_size+bling_spacing)*i,4*relief+4*spacing+17*thickness+4*y+z/4,-1])bling(bling);
   }
}

/* Inner long top side */
translate([0,4*relief+5*spacing+17*thickness+4*y+z/2,0])cube([x+6*thickness+2*relief,z/2,thickness]);

/* Outer long bottom side */
difference() {
   translate([0,4*relief+6*spacing+17*thickness+4*y+z,0])cube([x+4*thickness,z+2*thickness,thickness]);
   translate([border,border+4*relief+6*spacing+17*thickness+4*y+z,-1])cube([x+4*thickness-2*border,z+2*thickness-2*border,2*thickness]);
}

/* Inner long bottom side */
translate([0,4*relief+7*spacing+19*thickness+4*y+2*z,0])cube([x+2*thickness,z+thickness,thickness]);

/* Outer long top side */
difference() {
   translate([0,4*relief+8*spacing+20*thickness+4*y+3*z,0])cube([x+8*thickness+2*relief,z/2+thickness,thickness]);
   k2=floor((x+8*thickness+2*relief-bling_spacing)/(bling_size+bling_spacing)+.5);
   q2=(x+8*thickness+2*relief-(k2*(bling_size+bling_spacing)-bling_spacing))/2;
   for(i=[0:1:k2-1]){
      translate([q2+bling_size/2+(bling_size+bling_spacing)*i,4*relief+8*spacing+21*thickness+4*y+3*z+z/4,-1])bling(bling);
   }
}

/* Inner long top side */
translate([0,4*relief+9*spacing+21*thickness+4*y+3.5*z,0])cube([x+6*thickness+2*relief,z/2,thickness]);

/* Outer long bottom side*/
difference() {
   translate([0,4*relief+10*spacing+21*thickness+4*y+4*z,0])cube([x+4*thickness,z+2*thickness,thickness]);
   translate([border,border+4*relief+10*spacing+21*thickness+4*y+4*z,-1])cube([x+4*thickness-2*border,z+2*thickness-2*border,2*thickness]);
}

/* Inner long bottom side */
translate([0,4*relief+11*spacing+23*thickness+4*y+5*z,0])cube([x+2*thickness,z+thickness,thickness]);

/* Inner short bottom side */
translate([0,4*relief+12*spacing+24*thickness+4*y+6*z,0])cube([y,z+thickness,thickness]);

/* (to the right) Outer short bottom side */
difference(){
   translate([y+spacing,4*relief+12*spacing+24*thickness+4*y+6*z,0])cube([y+2*thickness,z+2*thickness,thickness]);
   translate([border+y+spacing-thickness,border+4*relief+12*spacing+24*thickness+4*y+6*z,-1])cube([y+4*thickness-2*border,z+2*thickness-2*border,2*thickness]);
}

/* Inner sort top side */
translate([0,4*relief+13*spacing+26*thickness+4*y+7*z,0])
   cube([y+4*thickness+2*relief,z/2,thickness]);

/* (to the right) Outer short top side */
difference() {
   translate([y+4*thickness+2*relief+spacing,4*relief+13*spacing+26*thickness+4*y+7*z,0])cube([y+6*thickness+2*relief,z/2+thickness,thickness]);
   k3=floor((y+6*thickness+2*relief-bling_spacing)/(bling_size+bling_spacing)+.5);
   q3=(y+6*thickness+2*relief-(k3*(bling_size+bling_spacing)-bling_spacing))/2;
   for(i=[0:1:k3-1]){
      translate([q3+bling_size/2+(bling_size+bling_spacing)*i+y+4*thickness+2*relief+spacing,4*relief+13*spacing+27*thickness+4*y+7*z+z/4,-1])bling(bling);
   }
}

/* Inner short bottom side */
translate([0,4*relief+14*spacing+27*thickness+4*y+7.5*z,0])cube([y,z+thickness,thickness]);

/* (to the right) Outer short bottom side*/
difference(){
   translate([y+spacing,4*relief+14*spacing+27*thickness+4*y+7.5*z,0])cube([y+2*thickness,z+2*thickness,thickness]);
   translate([border+y+spacing-thickness,border+4*relief+14*spacing+27*thickness+4*y+7.5*z,-1])cube([y+4*thickness-2*border,z+2*thickness-2*border,2*thickness]);
}

/* Inner short top side */
translate([0,4*relief+15*spacing+29*thickness+4*y+8.5*z,0])
   cube([y+4*thickness+2*relief,z/2,thickness]);

/* (to the right) Outer short top side */
difference() {
   translate([y+4*thickness+2*relief+spacing,4*relief+15*spacing+29*thickness+4*y+8.5*z,0])cube([y+6*thickness+2*relief,z/2+thickness,thickness]);
   k4=floor((y+6*thickness+2*relief-bling_spacing)/(bling_size+bling_spacing)+.5);
   q4=(y+6*thickness+2*relief-(k4*(bling_size+bling_spacing)-bling_spacing))/2;
   for(i=[0:1:k4-1]){
      translate([q4+bling_size/2+(bling_size+bling_spacing)*i+y+4*thickness+2*relief+spacing, 4*relief+15*spacing+30*thickness+4*y+8.5*z+z/4,-1])bling(bling);
   }
}
}
```


[User]
I'd like to create a custom Hartmann mask for my telescope, and I need a parametric OpenSCAD design to generate the STL. I've used similar tools before but haven't tried to design my own mask in OpenSCAD yet.

The basic idea is a cylinder with a central hole, representing the telescope tube opening, and then a layer on top with two or three smaller holes arranged symmetrically around the center.

Here are the parameters I need:

*   **`mask_diameter`:** This is the inner diameter of the mask and should be adjustable, ideally with a minimum of 10 and a maximum of 1000 in increments of 0.1. Let's say it's currently 204.
*   **`mask_height`:** The height of the tube-like part of the mask. This can vary between 10 and 100, also in increments of 0.1. Set it to 10 for now.
*   **`walls_thickness`:** The thickness of all the walls of the mask. Allow values between 1 and 10, in increments of 0.1. I'll use 1.5 initially.
*   **`use_triangles`:** A boolean variable to switch between circular holes and triangular holes in the top layer. I want this to default to false, meaning circular holes.
*   **`inner_holder`:** A boolean to control whether an inner cylindrical holder is created or not. This defaults to true, meaning that the mask is set to fit inside a tube.

For quality, I need:

*   **`fragments`:** This will control how many segments are used to draw circles and cylinders. I'd like this to go from 1 to 100; let's start at 50.

The smaller holes are positioned a fixed distance from the center and their diameter and distance should change relative to the mask diameter. I think a percentage of the mask diameter will be the best approach.
I'm envisioning that, internally, `holes_percentage` will be calculated using a percentage, starting at 3.5. The `holes_count` will determine the number of holes, initially three. `holes_diameter` will be the mask diameter divided by the percentage, `holes_angle` will divide 360 degrees by the hole count, and `holes_radius_from_center` will be mask diameter divided by the same percentage. The outer `diameter` will be the mask diameter plus twice the wall thickness.

The main part should be a difference operation between the outer cylinder and the inner cylinder, and if `inner_holder` is true, I'd like to include two overlapping cubes for an insert shape.

The top layer should also use a difference operation, using the main cylinder, and then a loop to create the smaller holes, taking into account the `use_triangles` variable.

Finally, I want the entire mask rotated by 180 degrees around the X axis so it's in the correct orientation for printing and telescope installation.
Also, include a `triangle` module.

I think that's everything. Let me know if you have any questions!

[Assistant]
Hartmann_mask.scad:
```
/* [Main] */

// Inner diameter of mask
mask_diameter = 204; // [10:0.1:1000]

// Height of mask holder
mask_height = 10; // [10:0.1:100]

// All walls thickness
walls_thickness = 1.5; // [1:0.1:10]

// Switch to triangles instead of circles
use_triangles = false;

// Smaller mask inserted inside tube
inner_holder = true;

/* [Quality] */

// Quality of print
fragments = 50; // [1:100]

/* [Hidden] */
holes_percentage = 3.5;
holes_count = 3;
holes_diameter = mask_diameter / holes_percentage;
holes_angle = 360 / holes_count;
holes_radius_from_center = mask_diameter / holes_percentage;
diameter = mask_diameter + 2 * walls_thickness;

rotate([180,0,0]) {
    // holder
    difference() {
        cylinder (h = mask_height, d = diameter, center = true, $fn = fragments);
        cylinder (h = mask_height + 1, d = mask_diameter, center = true, $fn = fragments);
        if (inner_holder) {
            cube (size = [diameter+1,diameter/1.75,mask_height], center = true);
            rotate ([0,0,90])
            cube (size = [diameter+1,diameter/1.75,mask_height], center = true);
       }
   }

    translate([0,0,mask_height / 2.0]) {
        difference() {
            // main cylinder
            cylinder (h = walls_thickness, d = diameter, center = true, $fn = fragments);

            // holes
            for (i = [1:holes_count]) {
                translate([holes_radius_from_center * cos( i * holes_angle),holes_radius_from_center * sin(i * holes_angle),0])
                if (use_triangles) {
                    triangle (h = walls_thickness + 1, d=holes_diameter, center = true);
                } else {
                    cylinder (h = walls_thickness + 1, d=holes_diameter, center = true, $fn = fragments);
                }
            }
        }
    }
}

module triangle(h, d, center) {
    cylinder (h = h, d=d, center = center, $fn=3);
}

```


[User]
I need a replacement lid for my coffee machine. It's a rectangular shape with rounded ends and a lip that fits down into the machine. I'd like the design to be parametric so I can adjust the size if needed.

Here are the dimensions I'm working with:
*   The main body of the lid, where it sits flush, should have a length of 183mm (let's call this `sizeX`).
*   The width, where it's a circular arc, should be 95mm (`sizeY`).
*   The lip or "throat" needs a wall thickness of 2.5mm (`wallThickness`).
*   The throat should extend down a height of 15mm (`wallHeight`).
*   And the top of the lid itself should be 3mm thick (`topThickness`).

I'd like the final shape to be achieved by extruding a rounded rectangle, then subtracting a smaller extrusion within that to create the lip, so basically a difference operation. The outside of the lip should follow the rounded rectangle shape, and the inner subtraction should also follow the rounded rectangle shape.

Could you generate the OpenSCAD file for this design? I'm not familiar with MCAD or BOSL, so please stick with vanilla OpenSCAD if possible. I don't need special rounding operations beyond the standard `circle()` functionality, so a `circle()` followed by a resize should be fine. Also, I'd like to have the `$fn` be a variable, initialized to 100 for the rendering. So I'd like to see `$fn = 100;` declared in the code.

[Assistant]
coffee-machine-lid.scad:
```
// width
sizeX = 183;
// height
sizeY = 95;
// wall thickness of the throat surrounding the coffee machine
wallThickness = 2.5;
// height of the throat surrounding the coffee machine
wallHeight = 15;
// thickness of the top plate
topThickness = 3;

/* [Hidden] */
$fn = 100;

difference() {
    // the body
    linear_extrude(height = wallHeight + topThickness)
        resize([sizeX + wallThickness*2, 0]) circle(d=sizeY + wallThickness*2);

    // the hole
    linear_extrude(height = wallHeight)
        resize([sizeX, 0]) circle(d=sizeY);
}

```


[User]
Hey, I'm looking to design a bracket for my Ender 3 extruder motor so I can mount it on its side. Currently, the filament path is bumping into the z-axis leadscrew when I add a PTFE tube to the input side. I'd like to be able to use a tube coupler without interference. The existing design I have is pretty close but I'm hoping you can generate a parametric design so I can tweak it.

Here are the parameters I have in mind:

*   **wall**: Thickness of the bracket walls (currently 3mm)
*   **tol**: Tolerance for the split in the bracket (currently 0.4mm)
*   **layer_h**: Layer height for vertical clearances (currently 0.2mm)
*   **motor_w**: Width of the stepper motor (currently 42.3mm)
*   **chamf_w**: Diagonal width of the stator chamfer (currently 52mm)
*   **chamf_h**: Height of the stator (currently 21mm)
*   **collar**: Diameter of the hole for the motor's metal bracket (currently 22mm)
*   **circlip_o**: Offset for the circlip (metal bracket thickness; currently 2.8mm)
*   **circlip_h**: Thickness of the circlip (currently 3mm)
*   **circlip_w**: Width of the circlip (currently 5mm)
*   **circlip_a**: Angle of the circlip opening (additional to 90 deg; currently 30 deg)
*   **AT**: Geometry overlap (currently 1/64 mm)
*   **ST**: Geometry overlap (currently 2\*AT)
*   **fn**: Curve resolution ($fn) (currently 96)

The basic design is a bracket that grips the stepper motor, specifically engaging with its chamfered edges. It also needs to incorporate a circular cutout with an integrated circlip feature to hold it in place. This circlip should have an angled opening. Finally, the bracket should be split down one side to allow it to be clamped onto the motor. The split should have a configurable tolerance.

It needs to use standard OpenSCAD primitives and not require any additional libraries. It's pretty simple in the end, mostly intersecting and subtracting cubes and cylinders.

Could you generate the OpenSCAD code for this? I'd really like the design to be flexible for future modifications.

[Assistant]
Stepper_ClipOn.scad:
```
// OpenSCAD Ender 3 Clip-On Extruder
// (c) 2020, tmackay
//
// Licensed under a Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license, http://creativecommons.org/licenses/by-sa/4.0.

// Thickness of bracket wall
wall=3;
// Tolerance for split
tol=0.4;
// Layer height for vertical clearance
layer_h=0.2;
// Width of stepper motor
motor_w=42.3;
// Diagonal width of stator chamfer
chamf_w=52;
// Height of stator
chamf_h=21;
// Collar diameter (hole in metal bracket)
collar=22;
// Circlip offset (thickness of metal bracket)
circlip_o=2.8;
// Thickness of circlip
circlip_h=3;
// Width of circlip
circlip_w=5;
// Circlip opening angle (additional to 90 degree cutout)
circlip_a=30;
// Geometry overlap
AT=1/64;
ST=2*AT;
// Curve resolution
$fn=96;

// Stepper Holder
difference(){
    union(){
        translate([0,0,chamf_h/2])difference(){
            intersection(){
                cube([motor_w+2*wall,motor_w+2*wall,chamf_h],center=true);
                rotate(45)cube([chamf_w+2*wall,chamf_w+2*wall,chamf_h],center=true);
            }
            intersection(){
                cube([motor_w,motor_w,chamf_h+ST],center=true);
                rotate(45)cube([chamf_w,chamf_w,chamf_h+ST],center=true);
            }
        }
        translate([AT-motor_w/2-wall,0,chamf_h/2])mirror([1,0,1]){
            intersection(){
                union(){
                    cylinder(d=collar,h=circlip_o-layer_h+AT);
                    translate([0,0,circlip_o-layer_h])cylinder(d=collar-circlip_w,h=circlip_h+layer_h+AT);
                    translate([0,0,circlip_o+circlip_h])cylinder(d=collar,h=circlip_h+AT);
                }
                translate([-chamf_h/2,-collar/2,0])cube([chamf_h,collar,circlip_o+2*circlip_h+AT]);
            }
        }
    }
    translate([-(circlip_o+2*circlip_h+wall+motor_w/2+AT),-tol/2,-AT])cube([circlip_o+2*circlip_h+wall+ST,tol,chamf_h+ST]);
}

// Circlip
difference(){
    cylinder(d=collar+circlip_w,h=circlip_h);
    translate([0,0,-AT]){
        cylinder(d=collar-circlip_w,h=circlip_h+ST);
        cube([collar+circlip_w,collar+circlip_w,circlip_h+ST]);
        rotate(circlip_a)cube([collar+circlip_w,collar+circlip_w,circlip_h+ST]);
    }
}
translate([collar/2,0,0])cylinder(d=circlip_w,h=circlip_h);
rotate(circlip_a)translate([0,collar/2,0])cylinder(d=circlip_w,h=circlip_h);

```


[User]
Hi there,

I need an OpenSCAD script to generate a customizable cable hole filler plug for desks and tabletops, and I have a pretty specific idea of what I need. I've seen examples online, but I need something that matches my requirements more closely.

Here's what I need it to do:

The basic plug shape is a cylinder that fits into the hole, with a flange at the top. I need to be able to set the diameter of the hole and the depth of the plug section. I want the script to handle the generation of a "cable cutout" too, and the number of cables as well as cable diameter needs to be customizable.

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

*   **CABLES**: The number of cable cutouts I need. It should be able to handle 0 or more cable cutouts.
*   **CABLE\_DIA**: The diameter of the cable cutouts.
*   **HOLE\_DIA**: The diameter of the hole that the plug fits into.
*   **HOLE\_DEPTH**: The depth of the plug cylinder.
*   **LIP**: The width of the flange that sits on top of the surface.
*   **WALL\_THICK**: The thickness of the cylinder wall.
*   **PLATE\_THICK**: The thickness of the top flange plate.
*   **PLATE\_EDGE**: The style of the flange edge; I want options for "ROUND", "CHAMFERED" and "SQUARE".
*   **INSET**: This one is a bit tricky, as it determines how the top part of the plug is done. I want these options:
    *   "FLUSH TOP": A flat top flange for a smooth surface on the desk.
    *   "BOTTOM": A recessed flange with a filled in bottom, meaning no flat top but rather a bottom section that acts as the new 'top' face.
    *   "FINGERGRIP": Like "BOTTOM", but with a grip bar across the plug like you might find on a sink plug.
    *   "NONE": Just a wall-thickness ring and flange for air circulation, with no top or bottom face.
*   **FINGERGRIP\_WIDTH**: The width of the finger grip bar.
*   **FINGERGRIP\_UNDERSIDE\_RECESS**: This is a boolean to add a cutout under the fingergrip to save printing material.
*  **FLEX\_SLOTS**: The number of vertical slots to allow some flexibility in the plug cylinder. This parameter should not affect "BOTTOM" or "FINGERGRIP" inset styles.
*   **FLEX\_GROOVE**: A boolean to determine if the plug has an additional groove to give even more flex at the base of the flex slots.
*   **TIE\_LOOP**: A boolean, for adding a tie loop.

I'm not worried about libraries - I'm happy to use vanilla OpenSCAD.

Could you generate a script that allows me to control all of those parameters? I also want a few example cases defined so that I can easily see how to use the various features and options. Please provide some examples, one of each of the **INSET** style types and show the **PLATE_EDGE** variations in each. It should also be clear how to use the **TIE_LOOP** and the **FLEX_GROOVE** features. Let's say three examples will cover this. Thanks in advance.

[Assistant]
Desktop_cable_filler_plug.scad:
```
// Desk / Bench top cable hole fascia filler plate plug
// written by Steve Malikoff in Brisbane, Australia 2020

// This script is hereby placed by its author in the Public Domain.
// I do not warrant this (nor the models it generates) as fit-for-purpose for anything.
// Use entirely at your own risk. Enjoy!
// As a courtesy please leave my name as the author of this script.

// Last updated 20200916

CABLES = 1;
CABLE_DIA = 8;

HOLE_DIA = 50;
HOLE_DEPTH = 10;
LIP = 5;
WALL_THICK = 2;

PLATE_THICK = 2;
PLATE_EDGE = "ROUND"; // ["ROUND", "CHAMFERED", "SQUARE"]
INSET = "FLUSH TOP"; // ["FLUSH TOP", "BOTTOM", "FINGERGRIP", "NONE"]

FINGERGRIP_WIDTH = 10;
FINGERGRIP_UNDERSIDE_RECESS = true; // [true, false]

FLEX_SLOTS = 8;
FLEX_GROOVE = false; // [false, true]

TIE_LOOP = false;    // [false, true]

module CustomiserStopper() {};  // ---- Stop Customiser showing variables after this -----
HOLE_RAD = HOLE_DIA/2;

FACETS = 233;
$fn = FACETS;

// Generate the model proper
FillerPlug();

// The complete model
module FillerPlug()
{
    difference()
    {
        FillerPlugSolid();
        Spokes(CABLES)
            CableCutout();
        EndChamfer();
        if (INSET == "FLUSH TOP" || INSET == "NONE")
            FlexSlots(FLEX_SLOTS);
        if (FLEX_GROOVE == true)
            FlexGroove();
        if (INSET == "FINGERGRIP" && FINGERGRIP_UNDERSIDE_RECESS == true)
            FingergripUndersideRecess();
    }
    if (TIE_LOOP == true && INSET != "NONE")
        TieLoop();
}

// The entire model prior to cutting out slots, chamfer, groove etc.
module FillerPlugSolid()
{
    LocatingCylinder();
    translate([0,0,HOLE_DEPTH])
        Plate();
    Spokes(CABLES)
        CableCutoutSurroundingWall();
}

// The body of the plug
module LocatingCylinder()
{
    HOLE_RAD = HOLE_DIA/2;
    difference()
    {
        cylinder(r=HOLE_RAD, h=HOLE_DEPTH);
        cylinder(r=HOLE_RAD - WALL_THICK, h=HOLE_DEPTH);
    }
}

// The flanged plate.
// The options are a flush top surface, an inside bottom surface,
// bottom with fingergrip, or just the edge wall.
module Plate()
{
    if (INSET == "FLUSH TOP")
    {
        PlateEdgeStyle();
        cylinder(r=HOLE_RAD, h=PLATE_THICK);
    }
    else if (INSET == "BOTTOM")
    {
        PlateEdgeStyle();
        translate([0,0,-HOLE_DEPTH])
            cylinder(r=HOLE_RAD, h=PLATE_THICK);
        FLEX_SLOTS = 0;
        TIE_LOOP = false;
    }
    else if (INSET == "FINGERGRIP")
    {
        PlateEdgeStyle();
        translate([0,0,-HOLE_DEPTH])
            cylinder(r=HOLE_RAD, h=PLATE_THICK);
        FLEX_SLOTS = 0;
        difference()
        {
        translate([-HOLE_RAD + WALL_THICK, -FINGERGRIP_WIDTH/2, -HOLE_DEPTH])
            cube([(HOLE_RAD - WALL_THICK)*2, FINGERGRIP_WIDTH, HOLE_DEPTH + PLATE_THICK]);
            /*
            // Transverse string tie hole through fingergrip
            if (TIE_LOOP == true)
                translate([0,FINGERGRIP_WIDTH, -HOLE_DEPTH/2 + PLATE_THICK])
                rotate([90,0,0])
                    cylinder(d=FINGERGRIP_WIDTH*0.6, h=FINGERGRIP_WIDTH*2);
            */
        }
    }
    else if (INSET == "NONE")
        PlateEdgeStyle();
}

// What sort of edge to be placed on the flanged plate
module PlateEdgeStyle()
{
    if (PLATE_EDGE == "SQUARE")
        PlateSquareEdge();
    else if (PLATE_EDGE == "CHAMFERED")
        PlateChamferedEdge();
    else if (PLATE_EDGE == "ROUND")
        PlateRadiusedEdge();
}

module PlateSquareEdge()
{
    difference()
    {
        cylinder(r=HOLE_RAD + LIP, h=PLATE_THICK);
        cylinder(r=HOLE_RAD - WALL_THICK, h=PLATE_THICK);
    }
}

module PlateChamferedEdge()
{
    difference()
    {
        union()
        {
            cylinder(r=HOLE_RAD + LIP, h=PLATE_THICK/2);
            translate([0,0,PLATE_THICK/2])
                cylinder(r1=HOLE_RAD + LIP, r2=HOLE_RAD + LIP - WALL_THICK, h=PLATE_THICK/2);
        }
        cylinder(r=HOLE_RAD - WALL_THICK, h=PLATE_THICK);
    }
}

module PlateRadiusedEdge()
{
    difference()
    {
        union()
        {
            cylinder(r=HOLE_RAD + LIP - PLATE_THICK, h=PLATE_THICK);
            Torus((HOLE_RAD + LIP)*2, PLATE_THICK*2);
        }
        translate([0,0,-PLATE_THICK])
            cylinder(r=HOLE_RAD+LIP, h=PLATE_THICK);
        cylinder(r=HOLE_RAD - WALL_THICK, h=PLATE_THICK);
    }
}

// The chamfer around the bottom to allow easier insertion
module EndChamfer()
{
    difference()
    {
        cylinder(r=HOLE_RAD+WALL_THICK,h=WALL_THICK);
        cylinder(r2=HOLE_RAD+WALL_THICK, r1=HOLE_RAD-WALL_THICK/2, h=WALL_THICK*1.25);
    }
}

// Place the cable cutouts equally spaced around the hole in the tabletop
module CableCutouts(someNumberOfCableCutouts)
{
    Spokes(someNumberOfCableCutouts)
        CableCutout();
}

// A single cable slot
module CableCutout()
{
    translate([HOLE_RAD - CABLE_DIA/2, 0, 0])
        U(CABLE_DIA, 100,HOLE_DEPTH + PLATE_THICK);
}

// Add the backing wall around the cable cutout
module CableCutoutSurroundingWall()
{
    intersection()
    {
        translate([HOLE_RAD - CABLE_DIA/2, 0, 0])
            U(CABLE_DIA + WALL_THICK*2, 100, HOLE_DEPTH + PLATE_THICK);
        cylinder(r=HOLE_RAD, h=HOLE_DEPTH + PLATE_THICK);
    }
}

// Optional: a series of fingers by cutting slots in the body to allow some flexing
module FlexSlots(someNumberOfSlots)
{
    Spokes(someNumberOfSlots)
        translate([HOLE_RAD - WALL_THICK - 1, -WALL_THICK/2, 0])
            cube([HOLE_RAD, WALL_THICK, HOLE_DEPTH * 0.9]);
}

// Optional: a groove around the slot fingers to give even more flex
module FlexGroove()
{
    translate([0,0,HOLE_DEPTH - WALL_THICK*0.75])
    difference()
    {
        cylinder(r=HOLE_RAD, h=WALL_THICK*0.75);
        cylinder(r=HOLE_RAD - WALL_THICK/5, h=WALL_THICK*0.75);
    }
}

// Optional: save filling the grip bar as a solid block, when printing at 100% infill
module FingergripUndersideRecess()
{
    RecessWidth = FINGERGRIP_WIDTH - WALL_THICK*2;

    // No cables (plug mode only): wall-to-wall recess
    if (CABLES == 0)
        translate([-(HOLE_RAD - WALL_THICK),-RecessWidth/2, 0])
            cube([(HOLE_RAD - WALL_THICK)*2, RecessWidth, HOLE_DEPTH]);

    // One cable, or odd number of cables: extend recess closer to opposing wall
    if (CABLES == 1 || CABLES % 2 == 1)
        translate([-(HOLE_RAD - WALL_THICK),-RecessWidth/2, 0])
            cube([HOLE_RAD - WALL_THICK, RecessWidth, HOLE_DEPTH]);

    // Any mumber of cables
    if (CABLES > 0)
        translate([-(HOLE_RAD - CABLE_DIA - WALL_THICK),-RecessWidth/2, 0])
            cube([(HOLE_RAD - CABLE_DIA - WALL_THICK)*2, RecessWidth, HOLE_DEPTH]);
}

// meh. I may do it sometime. Add the Thingiverse Thing# so it can be
// reprinted at a future date. Possibly also the hole dia too.
module EmbossThingiverseNumber()
{
    linear_extrude(WALL_THICK/2)
    text("Thing# TBD", size=4);
}

// optional: add a string tie loop to some of the patterns
module TieLoop()
{
    if (INSET == "BOTTOM")
    {
        y = 90;
    }
    else
    {
        y = 270;
    }
    translate([0,0,WALL_THICK*3])
    rotate([0,90,0])
    {
        difference()
        {
            U(HOLE_DEPTH, HOLE_DEPTH, HOLE_DEPTH/2);
            cylinder(r=WALL_THICK, h=HOLE_DEPTH/2);
        }
    }
}

// -------------------------- UTILITY FUNCTIONS --------------------------
// Generate a torus with:
// majorRadius being the larger value
// minorRadius for the thickness of the rotated circle
module Torus(majorRadius, minorRadius)
{
  halfMinorRadius = minorRadius / 2;
  minorRadiusCentreOffset = majorRadius / 2 - halfMinorRadius;
    rotate_extrude()
        translate([minorRadiusCentreOffset, 0, 0])
            circle(r = halfMinorRadius);
}

// Generate radial spokes of whatever child object you have
module Spokes(n)
{
    segment = 360 / n;
    for (i = [1:n])
    {
        rotate([0, 0, segment * i])
            //rotate([90, 0, 0])    // only if needed
                children();
    }
}

// Generate a U shape for the cable cutout and the tie loop
module U(someDia, someLength, someHeight)
{
    cylinder(d=someDia, h=someHeight);
    translate([0,-someDia/2,0])
        cube([someLength - someDia/2, someDia, someHeight]);
}

```


[User]
Hey, I'm looking for an OpenSCAD design for some mask clips that attach to glasses. I've got a basic idea and some parameters in mind.

Basically, I need a hook that clips onto the arm of glasses and provides a loop or similar for a mask strap to hook onto. It'll need to be parametric for different glasses arm sizes, and I'd like to have an option for rounded edges (basically Minkowski-ing the whole thing).

Here's what I'm thinking in terms of parameters:

*   `height`: Overall height of the clip.
*   `glas_height`: The height of the glasses arm it will clip onto.
*   `glas_width`: The width of the glasses arm.
*  `thick`: The thickness of the main body of the clip, and also the thickness of the hook part.
*   `bottom_thickness`: The thickness of the bottom part of the clip, underneath the glasses arm.
*   `perfect`: A boolean to enable Minkowski on the whole clip for rounded edges.

The clip itself should have a 'U' shape where the glasses arm goes, the bottom part should be flat, and then on the sides, there should be a loop or hook for the mask strap. The ends should have a small fillet on the outside, to ease pressure, but the inner hook part should be sharp to hold firm.

I think the core structure can be done with `cube`s, `linear_extrude`s and `polygon`s, maybe some `cylinder`s. I'll need a `difference` and `union` to make the main body, plus `minkowski` to generate the rounded edge version. Also, don't forget `translate` and `rotate` to position things correctly, and `mirror` to generate the opposite clip. Also, I'll need a small fillet function, maybe using a cylinder.

I'll need to generate two objects, one on either side of the glasses.

Could you write the OpenSCAD code for this, making sure all those parameters are accessible and the Minkowski option is included? I don't need any specific libraries this time.

[Assistant]
model_cust.scad:
```
$fn = 40;
height=6;
glas_height=5;
glas_width=2;
thick=1.2;
bottom_thickness=1.5;
perfect=false;

module clip(){
difference(){
    union(){
        difference(){
            cube([glas_width+2*thick,glas_height+thick,height]);
            translate ([thick,0,0]) cube ([glas_width,glas_height,height]);
        }
        translate ([glas_width+2*thick,glas_height+thick,0]) rotate([0,0,-90]) difference (){

            union(){
                cube([glas_height+thick,2,height]);
                translate ([(glas_height+thick)/4,2,0]) cube([(glas_height+thick)/2,(glas_height+thick)/4,height]);
                translate ([(glas_height+thick)/4,2,0]) cylinder (height,(glas_height+thick)/4,(glas_height+thick)/4);
                translate ([3*(glas_height+thick)/4,2,0]) cylinder (height,(glas_height+thick)/4,(glas_height+thick)/4);
                    }

            translate ([glas_height+thick,0,bottom_thickness])rotate([0,-90,0]) linear_extrude(height=glas_height+thick) polygon (points=[[0,0],[height-bottom_thickness,0],[height-bottom_thickness,4-thick]]);
        }
        linear_extrude(height=height) polygon (points=[[0,0],[thick+glas_width/2-0.5,0],[thick+glas_width/2-0.5,-1],[thick,-1.5],[thick/2,-1.5]]);
        translate([glas_width+2*thick,0,0]) mirror([1,0,0]) linear_extrude(height=height) polygon (points=[[0,0],[thick+glas_width/2-0.5,0],[thick+glas_width/2-0.5,-1],[thick,-1.5],[thick/2,-1.5]]);
    };
fillet();
translate ([glas_width+2*thick,0,bottom_thickness]) mirror([1,0,0]) fillet();
}
};

module fillet(){
    translate ([0,glas_height+thick-1,0]) difference() {
    cube([1,1,height]);
    translate ([1,0,0]) cylinder(height,1,1);
    };
};

module finalclip(){
if (perfect==false) clip();
if (perfect==true) {
    minkowski(){
        clip();
        sphere(0.25);
    }
}
}

finalclip();
translate([-2,0,0]) mirror([1,0,0]) finalclip();

```


[User]
I need an OpenSCAD design for a simple ring that acts as a magnet adapter. Basically, if my magnet is too small for a hole I need to fill, I can print this ring and place it around the magnet, then put it in the hole. The ring should have a cylindrical shape with a specific inner diameter, outer diameter, and height.

I'd like to be able to customize these parameters. Could you create a parametric OpenSCAD design that allows me to specify:

*   `INSIDE_DIAMETER`: the diameter of the hole in the ring (where the magnet goes).
*   `OUTSIDE_DIAMETER`: the outer diameter of the ring.
*   `HEIGHT`: the height of the ring.

The default values should be:

*   `INSIDE_DIAMETER` = 5.5 mm
*   `OUTSIDE_DIAMETER` = 8 mm
*   `HEIGHT` = 2 mm

I don't need anything super fancy, just a plain ring. I also don't need any extra libraries for this one. I want to be able to easily adjust the parameters using the OpenSCAD customizer, so the code should be well organized.

[Assistant]
magnet-adapter.scad:
```
INSIDE_DIAMETER = 5.5; // [1:0.1:10]
OUTSIDE_DIAMETER = 8; // [1:0.1:10]
HEIGHT = 2; // [1:0.1:10]

$fn     = 24 + 0;

difference() {
	cylinder(d = OUTSIDE_DIAMETER, h = HEIGHT);

	translate([0, 0, -1])
		cylinder(d = INSIDE_DIAMETER, h = HEIGHT+2);
}

```