[User]
I'm looking to create a plant propagation holder that fits on a glass. I have an existing design in OpenSCAD, but I want to make it more robust and parametric so I can easily adjust it for different glass and stem sizes.

The basic idea is this: there's a main holder piece that sits on the rim of a glass, and two cap pieces that clamp around the stem of the plant. The holder should be adjustable to fit different glass diameters and wall thicknesses. The caps should clamp on the stem and be easily removable once roots grow.

Here's the design I'm aiming for:

*   **Main Holder:**
    *   It should have a circular base that rests on the top edge of a glass, with an inner diameter adjusted for different glass diameters and wall thicknesses.
    *   It should have some kind of clip or indentation to keep it from sliding off the glass.
    *   It should have two slots or grooves on the top to hold the cap pieces. These slots should be slightly raised above the top of the glass, creating a gap.
*   **Cap Pieces (two identical pieces):**
    *   Each cap should be a semicircle or a c-shape.
    *   They should have a hole in the center for the stem, and the hole diameter should be adjustable.
    *   They need to be shaped so that when they are placed into the holder slots, they secure the stem between them.
    * They should "click" into place in the holder piece, but be easy to remove with a slight amount of force.

I would like the whole thing to be parametric with the following parameters:
*   `glass_outer_diameter`: The outer diameter of the glass.
*   `glass_wall_thickness`: The thickness of the glass wall.
*   `cap_diameter`: The diameter of the circle described by two caps.
*   `cap_hole_diameter`: The diameter of the hole through the center of the cap.
* `holder_height`: The height of the main holder piece.
*   `cap_height`: The height of the cap piece.
*   `cap_clip_height`: The height of the cap's clip.
*   `cap_clip_width`: The width of the cap's clip.
* `holder_clip_depth`: The depth of the clip on the holder piece.
* `holder_slot_width`: The width of the slots on the main holder.

I think it would be best to have all of this in a single OpenSCAD file to manage the relationships between the parts. I'm not sure what libraries to use, but it'd be great if you could make this easy to modify.

[Assistant]
Holder.scad:
```
// ----------------------------------------

// Customizable stem holder for plant propagation in a glass by alexandr_mucha is licensed under the Creative Commons - Attribution - Non-Commercial license.

// https://www.thingiverse.com/alexandr_mucha/designs

// Version: 1.3 (1.5.2022) - Increased cap space so that the parts fit together perfectly, without which this was not possible due to the inaccuracy of 3D printing technology

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

// VARIABLES CREATION

    glass_outer_diameter = 74.2;
    glass_wall_thickness = 3.6;
    cap_diameter = 20;
    cap_hole_diameter = 4.4;

// RESOLUTION SET

    $fn=100;

// MODULES CREATION

    module holder(){
        difference(){
            translate([0,0,0])
                cylinder(h = 2, r = glass_outer_diameter/2 + 2, center = true);
            translate([0,0,0])
                cylinder(h = 4, r = glass_outer_diameter/2 + 2 - glass_wall_thickness - 4, center = true);
        }
        difference(){
            translate([0,0,2])
                cylinder(h = 4, r = glass_outer_diameter/2 + 2, center = true);
            translate([0,0,2])
                cylinder(h = 6, r = glass_outer_diameter/2 + 2 - 2, center = true);
        }
        difference(){
            translate([0,0,2])
                cylinder(h = 4, r = glass_outer_diameter/2 + 2 - glass_wall_thickness - 2, center = true);
            translate([0,0,2])
                cylinder(h = 6, r = glass_outer_diameter/2 + 2 - glass_wall_thickness - 4, center = true);
        }
        difference(){
            translate([0,0,0])
                cylinder(h = 2, r = cap_diameter/2 + 2, center = true);
            translate([0,0,0])
                cylinder(h = 4, r = cap_diameter/2 + 0.2, center = true);
        }

        translate([cap_diameter/2,-2,-1])
            cube([glass_outer_diameter/2 + 2 - glass_wall_thickness - cap_diameter/2, 4, 2]);

        rotate(90)
            translate([cap_diameter/2,-2,-1])
                cube([glass_outer_diameter/2 + 2 - glass_wall_thickness - cap_diameter/2, 4, 2]);

        rotate(180)
            translate([cap_diameter/2,-2,-1])
                cube([glass_outer_diameter/2 + 2 - glass_wall_thickness - cap_diameter/2, 4, 2]);

        rotate(270)
            translate([cap_diameter/2,-2,-1])
                cube([glass_outer_diameter/2 + 2 - glass_wall_thickness - cap_diameter/2, 4, 2]);

    }

    module cap(){
        difference(){
            translate([0,0,0])
                cylinder(h = 2, r = (cap_diameter + 4)/2, center = true);
            translate([cap_diameter + 4,0,0])
                cube((cap_diameter + 4) * 2, center = true);
            translate([0,0,0])
                cylinder(h = 4, r = cap_hole_diameter/2, center = true);
        }
        difference(){
            translate([0,0,2])
                cylinder(h = 2, r = cap_diameter/2, center = true);
            translate([cap_diameter,0,2])
                cube((cap_diameter) * 2, center = true);
            translate([0,0,2])
                cylinder(h = 4, r = cap_hole_diameter/2, center = true);
        }

    }

// MODULE GENERATION

    translate([0,0,0])
        holder();
    translate([glass_outer_diameter/2 + cap_diameter/2 + 22,0,0])
        cap();
    translate([glass_outer_diameter/2 + cap_diameter/2 + 24 ,0,0])
        rotate(180)
            cap();
```


[User]
I'm looking to generate some custom switch caps for a DIY flight stick project, specifically for those little micro 2- or 3-way switches. I found a design for the stick I like ([here](https://www.thingiverse.com/thing:5232741 "GRIP TEDAC")), but the switches are a bit tricky. The lever rotates so I can’t just glue a cap on. I need to design a parametric OpenSCAD file (or set of files) that will let me customize these caps with a shaft to fit over the switch, a hole for the switch lever, and optional aprons to help keep the cap from rotating.

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

**General Options**
*   A toggle to enable or disable the hole for the switch lever.
*   Toggles to enable or disable either a rectangular shaft, a round shaft, both, or neither.
*   Toggles to enable or disable rectangular aprons and/or round aprons.

**Base Dimensions**
*   The overall shape of the base should be selectable between rectangular or round.
*   The length and width of the base.
*   The height of the base.

**Corrugation (for grip)**
*   A radius to determine the size of a corrugated arc on top of the base
*   A corrugation depth
*   The number of corrugations around the arc
*   An offset that would effectively allow adding a border to the arc, or inset the corrugations

**Shaft Dimensions**
*   For the round shaft: the diameter and length.
*   For the rectangular shaft: the width (it'll be square) and length.

**Hole Dimensions**
*   The depth of the hole for the switch lever.
*   The diameter of the hole.

**Rectangular Apron Dimensions**
*   The width and height of the aprons.
*   The thickness of the aprons.
*   An offset in the y-direction, to move the aprons around in relation to the base, and also in the z-direction to move them up or down relative to the base.

**Round Apron Dimensions**
*   The diameter of the round aprons.
*   The thickness of the aprons.
*   An offset in the y-direction, to move the aprons around in relation to the base, and also in the z-direction to move them up or down relative to the base.

It would be great if the code was well commented, too! I don't know if it matters, but the libraries I use are pretty basic, mainly just the built-in OpenSCAD functionality. I will not be including or referencing any external libraries. Let me know if I need to provide more detail, and thanks for the help!

[Assistant]
hat_konfigurator.scad:
```
//   Configurator for joystick/hotas switch hats
//   0.7 2022-04-29
//   der.kotti@gmail.com

/* [Enable/Disable features] */
//shall I make a hole?
draw_hole="yes"; // [yes,no]
//shall I make a rectangular shaft?
draw_rect_shaft="yes"; // [yes,no]
//shall I make a round shaft?
draw_round_shaft="yes"; // [yes,no]
//shall I make aprons, usefull as guided rails
draw_rect_aprons="yes"; // [yes,no]
//shall I make round aprons, better for as guided rails, because it rolls
draw_round_aprons="yes"; // [yes,no]

/* [Base] */
base_type="rectangular";//[rectangular,round]
//length of hat
length=25;//[1:50]
//width of hat
width=14;//[1:50]
//height of base
height=6;//[5:20]

/* [Corrugation] */
//radius of corrugation arc
radius=46;//[0:160]
//deepth of corrugation arc (should lower than 'base: height')
deepth=4;//[0:20]
//number of corrugations
numbers_of_corrugations=80;//[0:500]
//offset for corrugation (negative values draw a border)
corrugation_offset=1.0;//[-20:0.1:20]

/* [Shafts] */
//diameter of the round shaft
width_round_shaft=10;//[1:0.1:20]
//length from base bottom to...
length_round_shaft=6;//[1:0.1:50]
//width of the rectangular shaft (square)
width_rect_shaft = 11;//[1:0.1:20]
//length from base bottom to...
length_rect_shaft=3;//[1:0.1:50]

/* [Hole] */
//deepth of hole
deepth_of_hole = 36;//[1:0.1:100]
//diameter of the hole
diameter_of_hole = 3.01;//[1:0.1:20]

/* [Aprons (rectangular)] */
//width should not greater than hat length
width_rect_aprons = 10;//[1:0.1:50]
//length of aprons, from base bottom
height_rect_aprons = 10;//[1:0.1:50]
//thickness of rectangualr aprons
thickness_rect_aprons = 1.5;//[1:0.1:50]
//y offset of aprons, so you can move it a bit around
y_offset_rect_aprons = 1.4;//[1:0.1:20]
//z offset of aprons, so you can move it a bit around
z_offset_rect_aprons = 4.0;//[-20:0.1:20]

/* [Aprons (round)] */
//width of round aprons (diameter)
width_round_aprons=10;//[1:0.1:50]
//thickness of round aprons
thickness_round_aprons = 1.5;//[1:0.1:50]
//y offset of aprons, so you can move it a bit around
y_offset_round_aprons = 1.4;//[1:0.1:20]
//z offset of aprons, so you can move it a bit around
z_offset_round_aprons = -10.0;//[-20:0.1:20]

/* [Hidden] */

//Resolution fs
$fs=0.5;
//Resulution fa
$fa=5;

boatHat();

module boatHat(){
//center object, basebottom as level 0
rotate([0,180,0])
translate ([-length/2,-width/2,-height])
color ("grey")

difference()
{

  union()
  {
    difference()
    {
       union()
       {
       //base
       if (base_type=="rectangular")
       {
        cube ([length,width,height]);
       }else{
         translate ([length/2,width/2,0])
         cylinder (height,length/2,length/2);
       }

        //aprons
        if (draw_rect_aprons == "yes")
        //draw rectangular aprons
        {
          //left
          translate([(length/2) - (width_rect_aprons/2) ,width  - thickness_rect_aprons - y_offset_rect_aprons,height])
          cube ([width_rect_aprons,thickness_rect_aprons,height_rect_aprons]);
          //right
          translate([(length/2) - (width_rect_aprons/2) ,0 + y_offset_rect_aprons,height])
          cube ([width_rect_aprons,thickness_rect_aprons,height_rect_aprons]);
        }
        if(draw_round_aprons == "yes")
        //draw round aprons
        {
          //left
          translate([(length/2)  ,width - y_offset_round_aprons,height-z_offset_round_aprons])
          rotate(a=[90,90,0])
          cylinder (thickness_round_aprons,width_round_aprons/2,width_round_aprons/2);
          //right
          translate([(length/2) ,0 + thickness_round_aprons + y_offset_round_aprons,height-z_offset_round_aprons])
          rotate(a=[90,90,0])
          cylinder (thickness_round_aprons,width_round_aprons/2,width_round_aprons/2);
        }
       }
       //corrugation arc
       translate([length/2 ,width + corrugation_offset,-radius+deepth])
       rotate(a=[90,90,0])
       cylinder(width+(2*corrugation_offset),radius,radius);
       //corrugations
      for(alpha = [0 :(360/numbers_of_corrugations) : 360])
      {
        x = cos(alpha)*radius;
        z = sin(alpha)*radius;
        translate([length/2 ,width + corrugation_offset,-radius+deepth])
        translate([x,0,z])
        rotate(a=[90,90,0])
        cylinder(width+(2*corrugation_offset),1,1);
      }
    }
    //shafts
    if (draw_rect_shaft == "yes")
    {
      //rectangluar shaft
      translate([(length - width_rect_shaft)/2 ,(width-width_rect_shaft) / 2,height])
      cube([width_rect_shaft,width_rect_shaft,length_rect_shaft]);
    }
    if (draw_round_shaft == "yes")
    {
      //round shaft
      translate([length/2 ,width / 2,height])
      cylinder (length_round_shaft,width_round_shaft/2,width_round_shaft/2);
    }
  }
 //hole
 if (draw_hole == "yes")
 {
    translate([length/2 ,width / 2,length_round_shaft-deepth_of_hole+height+0.1])
    cylinder (deepth_of_hole,diameter_of_hole/2,diameter_of_hole/2);
 }
}

}

```


[User]
I need an OpenSCAD design for a customizable wax seal stamp. The core of the design will come from stencil data generated by an external online tool, specifically Benjamin's Stencil-o-Matic. This tool generates a series of polygon coordinates that define the stencil. I'll paste the output from the Stencil-o-Matic into a variable.

The stamp should have a cylindrical base with a handle. I want the user to be able to choose between a "large" and "small" handle size. The "large" handle should be a series of cylinders with varying radii to create a more ergonomic shape. The "small" handle should be a cylinder that transitions into a cuboid and the top should be a cylinder.

The pasted Stencil-o-Matic code defines the pattern on the bottom of the seal. The design should scale this pattern based on a user-defined seal size. Additionally, the user needs to be able to move the seal pattern left/right and up/down with X and Y offset parameters.

I want to include the ability to write "TNH" on the side of the stamp, using the write/Write.scad library. I also want to include a small orientation triangle on the top, using the MCAD/triangles.scad library.

The design should have a quality variable (e.g., 25 for draft, 200 for print) to control the fineness of curves using the $fn parameter.

The Stencil-o-Matic data will be in a variable called `input`, seal size in `seal_size`,  X offset `Move_X` , and Y offset `Move_Y`. The handle size will be selected by the user by choosing from `handle_size = "large"` or `handle_size = "small"`.

The design needs to account for positive and negative (cutout) shapes in the stencil data. The data will be a list of lists, the first of which will be the overall width, height, and depth. Each subsequent list will be a list containing the shape code and a list of polygon coordinates. A shape code of 1 means that this should be extruded and a shape code of 0 means this should be removed.

Could you create these OpenSCAD files for me, including all the required modules and libraries?

[Assistant]
STAMPY_Customizer.scad:
```
// STAMPY - Wax Seal Stamp Cusomizer v1.0
// by TheNewHobbyist 2013 (http://thenewhobbyist.com)
// http://www.thingiverse.com/thing:58612
//
// To use STAMPY first upload an image to Benjamin's (http://www.thingiverse.com/Benjamin)
// Stencil-o-Matic found here: http://chaaawa.com/stencil_factory/
//
// Then paste the resulting code into the "input" field of this thing. After setting the
// resize and move options switch the qualtiy to "Print". Then export an STL and
// print yourself a wax seal stamp!
//
//
// Change Log:
//
// v1.0
// Initial release
//
// v1.1
// ADDED: Small handle size for faster printing
// ADDED: Orientation indicator (triangle)
//
//

// Switch to "Print Quality" before creating STL
quality = 25; // [25:Draft, 200:Print]

// Choose a handle size
handle_size = "large"; // ["large":Large, "small":Small]

// Paste your Sencil-o-Matic code here
input = "default_TNH";

// Resize your seal
seal_size = 10; // [1:50]

// Move seal left/right
Move_X = 0; // [-50:50]

// Move seal up/down
Move_Y = 0; // [-50:50]

// preview[view:north, tilt:bottom]

/////////////////////////
// Beat it Customizer //
///////////////////////

module GoAwayCustomizer(){
}

//////////////////////////////////
// Other variables... Careful! //
////////////////////////////////

use <write/Write.scad>
use <MCAD/triangles.scad>

stencil_thickness = 1.5;
offX = 0;
offY = 0;
margin = 10;
dispo_width = seal_size;

points_array = (input=="default_TNH"? [[133,286, 1],[1,[[234.27,6.02],[231.55,7.45],[227.5,10.88],[222.85,15.67],[218.69,20.35],[214.95,25.17],[211.32,30.24],[208.44,34.98],[206.52,39],[205.31,48.23],[204.14,65.5],[202.43,96.43],[199.95,138.5],[197.65,174.91],[196.37,192.25],[195.44,195.38],[192.95,196],[190.58,196.5],[189.09,197.84],[188.55,199.75],[188.65,201.77],[188.28,204.15],[185.74,207.18],[181.17,210.7],[175.06,214.5],[168.61,218.79],[163.29,223.5],[159.05,228.64],[155.24,233.98],[152.19,239.17],[149.77,243.98],[148.11,250.02],[146.97,258.66],[145.84,267.33],[144.06,272.66],[141.97,276.16],[139.87,279.16],[138.4,281.78],[138.36,284.3],[139.83,286.58],[143.12,288.43],[150.09,289.9],[162.38,291.02],[181.28,291.67],[205.5,291.95],[229.79,291.67],[248.86,291],[261.56,289.87],[267.86,288.28],[270.76,286.27],[271.82,283.7],[271.26,280.36],[268.97,275.96],[266.18,270.14],[264.49,261.76],[263.22,253.76],[262.03,247.53],[260.34,242.46],[257.87,237.27],[254.58,232.07],[250.91,227.01],[246.01,222],[239.74,216.91],[233.89,213.03],[230,210.88],[227.72,209.34],[225.75,206.9],[224.51,204.05],[224,201.5],[223.82,199.39],[223.39,197.58],[222.07,196.41],[219.45,196],[216.94,195.74],[215.57,195.11],[215.61,179.54],[216.5,143.36],[217.84,100.02],[218.98,67.24],[219.99,41.97],[223.5,34.86],[227.01,28.44],[230.51,22.99],[232.97,19.44],[234,17.58],[234.64,16.25],[236.17,13.98],[237.73,11.12],[237.19,8.52],[235.86,6.75],[234.27,6.02]]],[1,[[212.84,76.5],[212.5,59.5],[211.74,69.5],[210.48,90.08],[208.53,125.5],[206.63,159.58],[205.5,176.93],[204.81,181.12],[203.95,183.56],[203.18,185.98],[202.73,190.13],[202.5,195.5],[206.22,195.81],[209.36,195.65],[210.42,193.81],[211.09,177.78],[212.04,142.5],[212.75,102.9],[212.84,76.5]]],[1,[[206,209],[195.82,209.28],[191.5,210],[194.92,210.74],[206,211],[217.08,210.74],[220.5,210],[216.18,209.28],[206,209]]],[1,[[205.86,215.72],[183.79,215.5],[178.37,218.41],[172.58,222.55],[166.23,228.5],[160.29,235.76],[156.27,243.19],[153.68,250.66],[151.96,258.09],[150.89,264.79],[149.83,270.5],[148.37,275.16],[146.32,279.34],[144.69,282.17],[144.25,283.58],[147.3,284.15],[154.07,284.97],[173.01,285.68],[205.29,285.98],[237.8,285.68],[256.79,284.92],[263.77,284.03],[266.92,283.42],[266.23,281.75],[264,278.35],[260.66,273.7],[259.87,264.1],[259.05,256.44],[258.15,251.5],[256.85,248.06],[255.05,244],[252.96,239.81],[250.98,236.07],[248.08,232.07],[243.79,227.28],[238.5,222.61],[233.21,218.93],[227.93,215.94],[205.86,215.72]]],[1,[[156.85,278.42],[154.78,278.35],[153.53,278.95],[153.45,279.83],[154.19,280.46],[157.32,281.08],[163,281.96],[173.19,282.99],[187.17,283.97],[203.88,284.41],[220.67,283.98],[234.91,283.01],[245.5,281.96],[251.42,280.91],[254.43,279.94],[254.97,279.17],[254.93,278.6],[251.4,278.77],[243.5,279.76],[228.64,280.99],[205,281.42],[182.42,281.13],[168.5,280.22],[161.36,279.17],[156.85,278.42]]]]: input);

input_width = points_array[0][0];
input_height= points_array[0][1];
input_depth = points_array[0][2];
sTrace = dispo_width/input_width;
stencil_height = input_height*sTrace + 2*margin;

////////////////////////
// Modules live here //
//////////////////////

module stamp(){
    difference(){
        union(){
            cylinder(h=10,r=15, $fn=quality);
            translate([0,0,10]) cylinder(h=5.8,r=12, $fn=quality);
            translate([0,0,15.8]) cylinder(h=12.8,r1=12, r2=15, $fn=quality);
            translate([0,0,28.6]) cylinder(h=14.9,r1=15, r2=17, $fn=quality);
            translate([0,0,43.5]) cylinder(h=8,r1=17, r2=17, $fn=quality);
            translate([0,0,51.5]) cylinder(h=8,r1=17, r2=14, $fn=quality);
        }
    translate([0,0,58.5]) cylinder(h=10,r1=8, r=40, $fn=quality);
    translate([0,0,40]) write("TNH",h=8,t=1, font="write/orbitron.dxf", center=true );
    translate([0,2,57]) rotate([0,0,225]) triangle(5, 5, 5);
    }
}

module stamp_small(){
    difference(){
        union(){
            cylinder(h=10,r=15, $fn=quality);
            translate([0,0,15]) cube([10,25,15], center=true);
        }
    translate([0,0,58.5]) cylinder(h=10,r1=8, r=40, $fn=quality);
    rotate([0,0,90]) #translate([0,0,15]) write("TNH",h=6,t=1, font="write/orbitron.dxf", center=true );
    translate([0,10,21]) rotate([0,0,225]) triangle(5, 5, 5);
    }
}

module stamp_mask(){
    difference(){
        translate([-100,-100,-.2]) cube([200,200,7]);
        translate([0,0,-.01])cylinder(h=4,r=22);
    }
}

module pattern(){
union(){
    translate([0, 0, stencil_thickness/2])
    translate([offX, offY, -stencil_thickness/2])
    scale([sTrace, -sTrace, 1])
    translate([-200, -150, 0]) {
        union() {
            difference(){
                // Loop through element array
                for (k = [1:len(points_array) -1] ) {
                    // Loop through Points in element if element is 1 so positive, add to extrusion
                    if(points_array[k][0] == 1) {
                        for (i = [1:len(points_array[k]) -1] ) {
                            linear_extrude(height=stencil_thickness*2) {polygon(points_array[k][i]);}
                            }
                        }
                    }
                // Loop through element array
                for (k = [1:len(points_array) -1] ) {
                    // Loop through Points in element if element is 0 so negative, remove from extrusion
                    if(points_array[k][0] == 0) {
                        for (i = [1:len(points_array[k]) -1] ) {
                            linear_extrude(height=stencil_thickness*2) {polygon(points_array[k][i]);}
                            }
                        }
                    }
                }
            }
        }
    }
}

////////////////////
// Stamptown USA //
//////////////////

if (handle_size == "large"){
    difference(){
    stamp();
    translate([Move_X,Move_Y,-2])pattern();
    }
}
else if (handle_size == "small"){
    difference(){
    stamp_small();
    translate([Move_X,Move_Y,-2])pattern();
    }
}

```


[User]
I'm looking to generate custom badges or tags using OpenSCAD, and I'd like to have a good amount of flexibility in their design.

Specifically, I want to be able to control the following:

*   **Overall Shape:**  I'd like a rectangular shape with rounded corners. I need to be able to adjust the overall width and height, as well as the corner radius.  I'd also like to control how rounded (or squared) the individual corners are - not all of them need to be rounded, I may want to selectively have square corners.

*   **Border/Inset:**  I want a border around the edge of the tag. The border should be a defined thickness, and the main body of the tag should be inset from this border creating a sort of 3D effect. Both border thickness and inset depth should be adjustable.

*   **Holes:**  I need to be able to optionally add one or two mounting holes. These should be circular, and I should be able to control their diameter. I need to be able to control the offset from the edges for each hole, independently.

*   **Text:**  I'd like to add multiple lines of text to the tag. I need to be able to control the font, font size, and horizontal and vertical offsets for the entire text block and vertical spacing between lines.

*   **Image:**  I'd like the option to include a single DXF image on the tag.  I should be able to control the image's scale and horizontal and vertical positioning on the tag.

*   **Thickness:** The overall thickness of the badge needs to be adjustable.

*   **Customization:** It would be great if all of the settings above could be adjusted through the OpenSCAD customizer interface with sliders or dropdown menus where appropriate and if the values of all of the parameters were visible in the customizer.

I think that should cover it. Do you think you could provide me with the necessary OpenSCAD code to do all of that? I think that the MCAD library might be a good place to start, as it has some useful polygon manipulation functions.

[Assistant]
Badge.scad:
```
  //-----------------------------//
 // Badge/Tag Generator - V 1.0 //
//-----------------------------//

// Use the OpenSCAD customizer

/* [Facets] */

$fn=20;

/* [Body] */

Border = 4;   // [2:10]
ExtraW = 0;   // [-50:.1:50]
ExtraH = 0;   // [-50:.1:50]
Thick  = 3;   // [3:.1:5]
Inset  = 1;   // [1:.01:2]

/* [Holes] */

Hole1 = true;
Hole2 = true;
HoleD = 3;        // [1:.1:10]
Hole_offset = 0;  // [0:.1:15]

/* [Text] */

XOff   = 4;   // [0:.1:20]
YOff   = 7;   // [0:.1:20]
VSpace = 3;   // [0:10]

str = ["the quick  GGGGGGGGGGGG"];

/* [Font] */

FontSize = 5; // [5:15]
font = "Arial";
//font = "Comic Sans MS";
//font = "MS UI Gothic";
//font = "Script Regular";
//font = "Sagoe UI";
//font = "Terminal";
//font = "Webdings";

/* [Image] */
Image = false;
Scale = 1;    // [.1:.01:5]
im_x  = 0;    // [-50:100]
im_y  = 0;    // [-50:100]
img = 0;      // [0:Bell,1:Butterfly,2:CityWorker,3:Dog,4:Dog1,5:DoggoLove,6:DogPaw,7:Earth,8:Eyes,9:Flower,10:gear,11:girl, 12:IronMan,13:MapleLeaf,14:Minion,15:Mouse,16:owl,17:PaintSplat,18:Panda,19:Sitting,20:ThumbUp,21:YinYang]

 Images = ["Bell.dxf",
           "Butterfly.dxf",
           "CityWorker.dxf",
           "Dog.dxf",
           "Dog1.dxf",
           "DoggoLove.dxf",
           "DogPaw.dxf",
           "Earth.dxf",
           "Eyes.dxf",
           "Flower.dxf",
           "gear.dxf",
           "girl.dxf",
           "IronMan.dxf",
           "MapleLeaf.dxf",
           "Minion.dxf",
           "Mouse.dxf",
           "owl.dxf",
           "PaintSplat.dxf",
           "Panda.dxf",
           "Sitting.dxf",
           "ThumbUp.dxf",
           "YinYang.dxf"];

ns = len(str);
maxx = max([for (i=[0:len(str)-1]) len(str[i]),]);
w = 2*Border + FontSize*(maxx+1) + ExtraW;
h = 2*Border + (FontSize+VSpace) * (ns+0) + ExtraH;

Tag();

//-------------------
// Generate Tag
//
module Tag() {
   difference() {

      linear_extrude(Thick)
      RRect_2d(h,w);

      translate([0,0,Thick-Inset+.01])
      linear_extrude(Inset)
      offset(r=-Border)
      RRect_2d(h,w);

      if (Hole1)
         translate([Hole_offset+Border/2,Hole_offset+Border/2,-.01])
         cylinder(d=HoleD,h=Thick+1);

      if (Hole2)
         translate([w-Hole_offset-Border/2,h-Hole_offset-Border/2,-.01])
         cylinder(d=HoleD,h=Thick+1);

   }

   translate([Border+XOff,(h-FontSize*ns-VSpace*(ns-1))/2])
   linear_extrude(Thick)
   for (a=[0:ns-1])
      translate([0,(len(str)-a-1)*(FontSize+VSpace)])
      text(str[a],size = FontSize,font=font);

   if (Image)
      linear_extrude(Thick)
      translate([w-10+im_x,h-10+im_y])
      scale(Scale)
      import(str("./images/",Images[img]));
}

//-----------------------------------------
// Rounded Rectangle
//
// RRect_2d(h=20,w=40,r=4,FL=[1,0,1,0])
//
// Call with h = height
//           w = width
//           r = corner radius (adjusted w if r > w or h if r > h)
//           FL = corner flags 0 = round !=0 = square
//           $fn = number of rounded sides.  if $fn = 0, sides = 1.
//
module RRect_2d(h=10,w=10,r=7,FL=[1,0,1,0],center=false) {

   r = ((2*r) > h)? h/2: r;
   R = ((2*r) > w)? w/2: r;

   oxy = (center)? [0,0]: [w/2,h/2];

   polygon(concat(CPl(0),CPl(1),CPl(2),CPl(3)));

   function CPl(crnr,Sect) =
                  let(sa = 90*crnr,
                      ns = ($fn == 0)? 1: $fn,
                      stp = 90/ns,
                      Org = [(w/2-R)* sign(cos(sa+45)),(h/2-R)*sign(sin(sa+45))]+oxy
                     )

                     (FL[crnr] == 0)?
                        [ for (a=[sa:stp:90+sa]) R*[cos(a),sin(a)]+Org, ]:
                        [ [w*cos(sa+45),h*sin(sa+45)]*.7071067+oxy ];
}

```


[User]
Hey there, I'm looking for an OpenSCAD design for a parametric fan adapter. I've got a couple of different sized fans and I need to connect them together.

Ideally, I want to be able to specify the size of each fan, and the adapter should generate the correct bolt patterns and screw holes automatically. I'd like to be able to choose between a straight adapter, or an angled adapter. For the angled adapter, I want to be able to specify the angle between the two fan faces.

Here's a more detailed breakdown:

*   **Fan Sizes:** The user should be able to specify the size of two fans, let's call them fan A and fan B. Sizes should be in mm, and I need to handle standard fan sizes from 20mm all the way up to 250mm.
*   **Bolt Patterns:** I want the bolt pattern for each fan to be generated automatically based on the fan size. There is a lookup table in my reference code (which I won't share) that matches fan sizes to the appropriate spacing between holes, if that helps.
*   **Screw Holes:** The design should automatically generate the screw holes for each fan. I'd like control over whether those holes are tapped (for self-tapping screws), exact size (for using a nut on the back side), or through holes. I also need the option to have counterbored pockets for the screw heads for flat-head screws, but I don't need the pockets on the tapped hole type.
*   **Flange Thickness:** It would be great if I could override the flange thickness for each fan. There should also be a minimum screw head pocket thickness, to ensure the pockets don't go all the way through.
*   **Inside Diameter:** The user should be able to override the inside diameter of the adapter. It should have a default value based on the fan size to create a smooth transition between fans.
*   **Angled Adapter:** For an angled adapter, I need to specify the angle between the fan faces and the design should have a smooth, curved transition between them. I'd also like a 'resolution' setting (like `$fn`) just for the arc.
*   **Straight Adapter:** For straight adapters, I need the ability to offset one of the fan flanges in X and Y.
*   **Tunnel Length:** I'd like the user to be able to specify the tunnel length manually, but it should have a sensible default length to transition between fans. For angled adapters it should be at least long enough to ensure there's an arc present with the set angle. And both straight and angled adapters should not default to a 0 length tunnel.

For the angled adapter I'd need to use BOSL2, so make sure to include that in the code. Thanks!

[Assistant]
fan_adapter_thingiverse.scad:
```
// parametric fan size adapter
// b.kenyon.w@gmail.com CC-BY-SA

// edited for thingiverse customizer

// For angled adapters (angle>0), install the BOSL2 library.
// For straight adapters (angle=0), you can just ignore the "can't open" warning.
// https://github.com/revarbat/BOSL2
include <BOSL2/std.scad>;

// fake enums
default = -2; //
auto = -1; //
none = 0; //
thread = 1; //
exact = 2; //
through = 3; //

// Fan size. Outside dimension of the square frame. Usually specify a standard fan size here like "40" or "120". If you want to specify an arbitrary/non-standard size, then you have to supply both *_fan_size and *_bolt_pattern, and may also want to change cowling_thickness or *_inside_diameter, as otherwise the ID will increase with the exterior size. May also want to manually specify *_screw_size, as the automatic screw size will increase at certain threshholds like >40. IE, if you want to use say a 40mm fan, but want the flanges to be 2mm wider, but don't want the ID to be larger, you manually specify, fan_A_size=42, fan_A_inside_diameter=38, fan_A_bolt_pattern=32, fan_A_screw_size=3.
fan_A_size = 60;
// Override bolt pattern spacing: -1=auto  Default is looked up from a table of standard fan sizes.
fan_A_bolt_pattern = -1;
// Override screw hole diameter: -1=auto  0=none  Default is looked up from a table of standard fan size ranges.
fan_A_screw_size = -1;
// Override screw hole type: 1=thread - hole smaller than *_screw_size for thread-forming, and disable pockets.  2=exact - hole exactly *screw_size for manual control.  3=through - hole larger than *_screw_size for pass-through. Default is through if angle=0 and thread if angle>0, and default is angle=0.
fan_A_mount_hole_type = -1;
// Override screw pocket diameter: -1=auto  0=disable  No effect when/where pockets are disabled, ex: _type=thread or angle>0. Default is *_screw_size*2.
fan_A_screw_pocket_diameter = -1;
// Override flange thickness: -1=default  Default is default_flange_thickness.
fan_A_flange_thickness = -1;
// Override inside diameter: -1=auto  Default is *_fan_size-(cowling_thickness*2)
fan_A_inside_diameter = -1;

fan_B_size = 40;
fan_B_bolt_pattern = -1;
fan_B_screw_size = -1;
fan_B_mount_hole_type = -1;
fan_B_screw_pocket_diameter = -1;
fan_B_flange_thickness = -1;
fan_B_inside_diameter = -1;

// creates an angled adapter - angle>0 requires github.com/revarbat/BOSL2 - Screw head pockets are disabled by default when angle>0. To force enable pockets, override the -1/auto value for *_mount_hole_type, ex: "fan_B_mount_hole_type = through; //-1;" would enable pockets for the fan_B flange, and change the hole dimension from being smaller than nominal (for thread-forming) to being larger than nominal (for pass-through).
angle = 0;
// like $fn but just for the main arc of an angled adapter - higher = smoother surface and longer render time
fn = 96;

// default flange thickness - if the flanges were flat, this would need to be more like 3 or 4, but they are not flat
default_flange_thickness = 2;

// For any holes with a screw head pocket enabled, minumum thickness of material under screw heads, regardless of other settings
minimum_screw_flange_thickness = 2;

// tunnel length: -1=auto  Distance between the two flanges, not including the flanges themselves. Default is whatever distance creates a 45 degree cone/funnel between the given fan sizes. For angled adapter, it's the arc length through the center of the bent tube, and may be longer depending on the angle between the flanges, but the minimum arc length is the same as the straght adapter, so that at very shallow angles, the flanges do not come any closer than for a straight adapter. For straight adapter, this may shorten the tunnel to anywhere from default to 0. For angled adapter, this will not shorten less than auto, but may lengthen.
tunnel_length = -1;

// move the fan_A flange off-center - Only for straight adapters. abs(fan_A_size-fan_B_size)/2 makes the 2 flanges exactly flush on one side.
xoffset = 0;
yoffset = 0;

// subtracted (*2) from *_fan_size to determine the default inside diameter
cowling_thickness = 1;

// angled adapter flange back thickness is automatic, but some combinations of options can result in too little, even 0 flange back thickness
minimum_angled_flange_back_thickness = 4;

// tunnel length is automatic, but some combinations of options can result in 0 tunnel length, which is ok in some cases but not always. This is only used if tunnel_length=auto and the auto would come out less than this. If you manually set tunnel_length, even to 0, it is used.
minimum_default_tunnel_length = 4;

// given fan size, return bolt pattern
function fbp(x) =
 x == 20 ? 16 :
 x == 25 ? 20 :
 x == 30 ? 24 :
 x == 35 ? 29 :
 x == 38 ? 32 :
 x == 40 ? 32 :
 x == 45 ? 37 :
 x == 50 ? 40 :
 x == 52 ? 42 :
 x == 60 ? 50 :
 x == 70 ? 61.5 :
 x == 75 ? 67 :
 x == 80 ? 71.5 :
 x == 90 ? 82.5 :  // not a real fan size, just a convenience instead of generating an error when you really meant 92
 x == 92 ? 82.5 :
 x == 120 ? 105 :
 x == 135 ? 122 :  // also 110 & 113.3, but not all 4 corners
// x == 145 ?  :
 x == 140 ? 125 :
 x == 150 ? 122.3 :
 x == 160 ? 138.5 :
// x == 170 ?  :
 x == 176 ? 152.6 :
 x == 180 ? 152.6 :
 x == 190 ? 150.6 : // automotive radiator
 x == 200 ? 154 :
 x == 205 ? 174 :
 x == 225 ? 170 :
 x == 230 ? 170 :
 x == 250 ? 170 :
 0 ;

// given fan size, return bolt size
function fbs(x) =
 x <= 20 ? 2 :
 x <= 35 ? 2.5 :
 x <= 40 ? 3 :
 x == 190 ? 6 :
 x <= 140 ? 4 :
 5 ;

o = 1/128;
//$fn = 72;
$fs = 0.5;
$fa = 1;

// flange inside diameter
fan_A_id =
 fan_A_inside_diameter > auto ? fan_A_inside_diameter :
 fan_A_size - cowling_thickness * 2;
fan_B_id =
 fan_B_inside_diameter > auto ? fan_B_inside_diameter :
 fan_B_size - cowling_thickness * 2;

// mount hole nominal diameter - M3, M4 etc
fan_A_mhnd =
 fan_A_screw_size > auto ? fan_A_screw_size :
 fbs(fan_A_size) ;
fan_B_mhnd =
 fan_B_screw_size > auto ? fan_B_screw_size :
 fbs(fan_B_size) ;

// mount hole type
fan_A_mht =
 fan_A_mount_hole_type > auto ? fan_A_mount_hole_type :
 angle > 0 ? thread :
 through ;
fan_B_mht =
 fan_B_mount_hole_type > auto ? fan_B_mount_hole_type :
 angle > 0 ? thread :
 through ;

// mount hole actual/adjusted diameter - M3 cutting threads, M3 pass through, etc
fan_A_mhad =
 fan_A_mht == exact ? fan_A_mhnd :
 fan_A_mht == thread ? screw_id(fan_A_mhnd) :
 screw_od(fan_A_mhnd) ;
fan_B_mhad =
 fan_B_mht == exact ? fan_B_mhnd :
 fan_B_mht == thread ? screw_id(fan_B_mhnd) :
 screw_od(fan_B_mhnd) ;

echo ("fan_A mount holes: nominal, actual",fan_A_mhnd,fan_A_mhad);
echo ("fan_B mount holes: nominal, actual",fan_B_mhnd,fan_B_mhad);

// mount hole pocket diameter
fan_A_pd =
 fan_A_mht == thread ? 0 :
 fan_A_size == fan_B_size ? 0 :
 fan_A_screw_pocket_diameter > auto ? fan_A_screw_pocket_diameter :
 fan_A_mhnd * 2;
fan_B_pd =
 fan_B_mht == thread ? 0 :
 fan_B_size == fan_A_size ? 0 :
 fan_B_screw_pocket_diameter > auto ? fan_B_screw_pocket_diameter :
 fan_B_mhnd * 2;

 // mount hole bolt pattern
fan_A_bp =
 fan_A_bolt_pattern > auto ? fan_A_bolt_pattern :
 fbp(fan_A_size);
assert(fan_A_bp > 0,"Unrecognized size for fan_A fan. See function fbp() for list of fan sizes.");
fan_B_bp =
 fan_B_bolt_pattern > auto ? fan_B_bolt_pattern :
 fbp(fan_B_size);
assert(fan_B_bp > 0,"Unrecognized size for fan_B fan. See function fbp() for list of fan sizes.");

// flange corner diameter
fan_A_cd = fan_A_size - fan_A_bp;
fan_B_cd = fan_B_size - fan_B_bp;

// flange thickness
fan_A_ft =
 fan_A_flange_thickness > auto ? fan_A_flange_thickness :
 default_flange_thickness ;
fan_B_ft =
 fan_B_flange_thickness > auto ? fan_B_flange_thickness :
 default_flange_thickness ;

// thickness under screw heads
fan_A_sft =
 fan_A_ft > minimum_screw_flange_thickness ? fan_A_ft :
 minimum_screw_flange_thickness ;
fan_B_sft =
 fan_B_ft > minimum_screw_flange_thickness ? fan_B_ft :
 minimum_screw_flange_thickness ;

// tunnel length - default 45 degree cone
def_tl = max(abs((fan_B_size-fan_A_size)/2),minimum_default_tunnel_length);
tl = tunnel_length > auto ? tunnel_length : def_tl;

assert(fan_A_ft+tl+fan_B_ft>0,"At least one of fan_A_flange_thickness, fan_B_flange_thickness, or tunnel_length must be thicker than 0!");

//////////////////////////////////////////////////////////////////////////////////
/// OUTPUT ///////////////////////////////////////////////////////////////////////

if(angle<=0) {
///////////////////////////////////////
////////   straight adapter   /////////

 difference() {
  group () {

   if(fan_A_ft>0) hull() c4(s=fan_A_bp,z=fan_A_ft,d=fan_A_cd); // flange A OD

   translate([0,0,fan_A_ft]) {
    if(tl<def_tl) hull() { // minimum wall when shallow angle would have left none or not enough
     if (fan_A_size>fan_B_size) c4(s=fan_A_bp,z=cowling_thickness,d=fan_A_cd);
     else translate([0,0,tl-cowling_thickness+o]) c4(s=fan_B_bp,z=cowling_thickness,d=fan_B_cd);
    }
    hull() { // normal transition tunnel
     c4(s=fan_A_bp,z=o,d=fan_A_cd,center=true); // fan_A end of transition OD
     translate ([xoffset,yoffset,tl]) c4(s=fan_B_bp,z=o,d=fan_B_cd,center=true); // fan_B end of transition OD
    }
   }

   if(fan_B_ft>0) translate ([xoffset,yoffset,fan_A_ft+tl]) hull() c4(s=fan_B_bp,z=fan_B_ft,d=fan_B_cd); // flange B OD

  }

  group() {
   hl = o+fan_A_ft+tl+fan_B_ft+o; // length to cut a hole through the entire body
   translate([0,0,-o]) cylinder(h=o+fan_A_ft+o,d=fan_A_id); // flange A ID

   translate([0,0,fan_A_ft]) hull() { // transition ID
    cylinder(h=o+o,d=fan_A_id,center=true);
    translate([xoffset,yoffset,tl]) cylinder(h=o+o,d=fan_B_id,center=true);
   }

   translate([xoffset,yoffset,-o+fan_A_ft+tl]) cylinder(h=o+fan_B_ft+o,d=fan_B_id); // fan_A flange ID

   translate([0,0,-o]) {
    c4(s=fan_A_bp,z=hl,d=fan_A_mhad); // fan_A mount holes
    translate([xoffset,yoffset,0]) c4(s=fan_B_bp,z=hl,d=fan_B_mhad); // fan_B mount holes
   }
   if (fan_A_pd > 0) translate([0,0,fan_A_sft+o+o]) c4(s=fan_A_bp,z=hl,d=fan_A_pd); // fan_A pockets
   if (fan_B_pd > 0) translate([xoffset,yoffset,-fan_B_sft-o-o]) c4(s=fan_B_bp,z=hl,d=fan_B_pd); // fan_B pockets
  } // /group

 } // /difference

} else {
/////////////////////////////////////
////////   angled adapter   /////////

 if (fan_A_mhad>fan_A_mhnd) echo("WARNING: Screw head pockets are generally incompatible with an angled adapter. Suggest using fan_A_mount_hole_type=thread to produce a small mount hole that you thread a screw directly into from the fan side.");
 if (fan_B_mhad>fan_B_mhnd) echo("WARNING: Screw head pockets are generally incompatible with an angled adapter. Suggest using fan_B_mount_hole_type=thread to produce a small mount hole that you thread a screw directly into from the fan side.");

 difference() {
  _tl = max(tl,def_tl); // minimum tunnel length def_tl or larger
  _r = max(fan_A_size,fan_B_size)/2; // minimum arc radius = larger fan radius
  _al = PI * _r * (angle/180); // minimum arc length based on angle & minimum arc radius
  al = max(_tl,_al); // arc length = larger of minimum tunnel length or minumim arc length
  r = max(_r,al/(angle*(PI/180))); // radius needed to get desired arc length
  tb = max(def_tl/2,minimum_angled_flange_back_thickness); // flange back thickness
  union () {
   flange(s=fan_A_size,d=0,t=fan_A_ft,b=fan_A_bp,m=fan_A_mhad,pt=fan_A_sft,pd=fan_A_pd,tb=tb); // fan_A flange
   translate([r,0,fan_A_ft]) rotate([0,angle,0]) translate([-r,0,fan_B_ft]) rotate([180,0,0]) flange(s=fan_B_size,d=0,t=fan_B_ft,b=fan_B_bp,m=fan_B_mhad,pt=fan_B_sft,pd=fan_B_pd,tb=tb); // fan_B flange
   translate([0,0,fan_A_ft]) bent_cone(a=angle,r=r,s1=fan_A_size,s2=fan_B_size,fn=fn,e=o); // bent cone OD
  }
  translate([0,0,fan_A_ft]) bent_cone(a=angle,r=r,s1=fan_A_id,s2=fan_B_id,fn=fn,e=max(fan_A_ft,fan_B_ft)+o); // bent cone ID
  // ugh.. for some combinations a little bit of the flange back pokes through the opposite flange face at shallow angles
  _tb = tb+1;
  translate([0,0,-_tb/2+o/2]) cube([fan_A_size+1,fan_A_size+1,_tb],center=true);
  translate([r,0,fan_A_ft]) rotate([0,angle,0]) translate([-r,0,fan_B_ft+_tb/2-o/2]) cube([fan_B_size+1,fan_B_size+1,_tb],center=true);
 }
}

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

// bore diameter for screw to cut threads
function screw_id(x) = round((x-x/15)*10)/10;

// bore diameter for screw to pass through
function screw_od(x) = round((x+x/15)*10)/10;

// 4 cylinders
module c4 (s,z,d,center=false) {
 p = s/2;
 l = [[-p,-p,0],[-p,p,0],[p,p,0],[p,-p,0]];
 for(v=l) translate(v) cylinder(h=z,d=d,center=center);
}

module flange(s=50,d=-1,t=3,b=40,m=3,tb=-1,pt=0,pd=0,center=false) {
 _d = d<0 ? s-2 : d ;
 _z = center ? 0 : t/2 ;
 _tb = tb>auto ? tb : default_flange_thickness ;
 translate([0,0,_z]) {
  difference() {
   hull() {
    translate([0,0,o/2]) c4(s=b,z=t+o,d=s-b,center=true); // main plate
    if (_tb>0) translate([0,0,-_z+t+_tb]) cylinder(d=s-_tb*2,h=o); // extra back side thickness
   }
   group() {
    if(_d>0) cylinder(h=t+1,d=_d,center=true); // main hole
    if(m>0) translate([0,0,-o-_z]) c4(s=b,z=o+t+_tb+o,d=m); // mount holes
    if(pd>0) translate([0,0,-_z+pt]) c4(s=b,z=t+_tb,d=pd); // pockets
   }
  }
 }
}

module bent_cone(a=90,r=-1,s1=10,s2=20,fn=-1,e=0) {
 _r = r<0 ? max(s1,s2)/2 : r;
 sh = circle(d=s1);
 _fn = fn>0 ? fn : $fn>0 ? $fn : 36;
 nv = max(1,round(_fn/(360/a)));
 sc = ((s2/s1)-1)/nv;
 T = [for(i=[0:nv]) yrot(a*i/nv,cp=[_r,0,0])*scale([1+i*sc,1+i*sc,1])];
 sweep(sh,T);
 if (e>0) {
  translate([0,0,-e]) linear_extrude(height=o+e) polygon(sh);
  translate([_r,0,0]) rotate([0,a,0]) translate([-_r,0,-o]) linear_extrude(height=o+e) scale([1+nv*sc,1+nv*sc,1]) polygon(sh);
 }
}

```


[User]
Hi, I'm looking to create a parametric OpenSCAD design for a magnetic pegboard. I'd like it to be customizable in a few ways.

Firstly, the overall dimensions of the pegboard should be adjustable, so I can define the total width and height. Also, I need to be able to control the number of rows and columns of magnet holes – these should always be equal. So I might specify it as a 3x3 or a 5x5 or whatever. The magnets themselves should also have adjustable dimensions, including both cubic and cylindrical shapes, with a tolerance value to allow a slightly larger space. It should be possible to pick whether to use cubes or cylinders.

Secondly, I want a border/separator wall between each "cell" or magnet location. This wall should have its own height and width that are adjustable.  The height of these walls, combined with the main panel thickness, should be used as the total thickness when creating the cavities for the magnets.

Thirdly, the pegboard should include optional mounting holes for bolts. I need to be able to toggle these on or off, and if they are on, I need to be able to define the diameter of the bolt, along with an additional reinforcement ring around the bolt hole and the countersink cone. The mounting holes should be placed equidistant from the four corners of the board.

Finally, I’d like it if the OpenSCAD code was well-commented, and easy to understand. I'm not familiar with any particular libraries, so the more basic the better. But I'm open to using standard ones if needed to make things easier. I'm also not looking for any specific colors on the preview, so the default should be fine.

Thanks!

[Assistant]
Magnetic_Pegboard.scad:
```
Round_Magnets = 0;
Panel_Size = 152.4;                   // total size
Column_Row_Count = 6;                     // array (number of rows/columns)
Panel_Thickness = 2;        // thickness of plate
Seperator_Wall_Height=2;
Seperator_Wall_Width = 2;                 // border divider (cavity divided by array)
Magnet_Size = 5;            // size of magnet
Bolt_holes = 1;
Metric_Bolt_Size = 3;                     // Metric Bolt Size
Metric_Bolt_Reinforcement= 6;                  // Metric bolt reinforcement
Tolerance = 0.25;                  // tolerances for bolt and magnet
tt = Seperator_Wall_Height +Panel_Thickness;
cs = (Panel_Size/Column_Row_Count);               // cell size (total divided by array)
co = cs-Seperator_Wall_Width;                 // cavity offset (cavity divided by border)
mo = (Seperator_Wall_Width+co-(Magnet_Size+Tolerance))/2;     // magnet offset for center align
rmo = cs/2;

color([.72,.72,.72])
translate([-Panel_Size/2,-Panel_Size/2,0])
{
    difference()
    {
        union()
        {
          difference()
            {
                    cube([Panel_Size,Panel_Size,tt]);
                // backboard
                    union()
                    {
                  // cavity
                        for(i=[0:1:Column_Row_Count-1])
                        {
                            for(x=[0:1:Column_Row_Count-1])
                            {
                                translate([Seperator_Wall_Width/2+(cs*i),Seperator_Wall_Width/2+(cs*x),Panel_Thickness])
                                {
                                    cube(size =co);
                                }
                                //magnets
                                if
                                        (Round_Magnets == 0)
                                translate([mo+(cs*i),mo+(cs*x),-1])
                                {

                                        cube([Magnet_Size+Tolerance,Magnet_Size+Tolerance,tt]);
                                }
                                else
                                translate([(cs*i)+rmo,rmo+(cs*x),-1])
                                {
                                        cylinder(d=Magnet_Size+Tolerance,h=tt,$fn=64);
                                }
                            }
                        }

                    };
            };
//M bolt reinforcement
     if(Bolt_holes>=1)
        union()
    {
        translate([cs,cs,0])
            {
                cylinder(d=Metric_Bolt_Size+Metric_Bolt_Reinforcement,h=tt,$fn=64);
            }
        translate([Panel_Size-cs,Panel_Size-cs,0])
            {
                cylinder(d=Metric_Bolt_Size+Metric_Bolt_Reinforcement,h=tt,$fn=64);
            }
        translate([Panel_Size-cs,cs,0])
            {
                cylinder(d=Metric_Bolt_Size+Metric_Bolt_Reinforcement,h=tt,$fn=64);
            }
        translate([cs,Panel_Size-cs,0])
            {
                cylinder(d=Metric_Bolt_Size+Metric_Bolt_Reinforcement,h=tt,$fn=64);
            }
        }
        };
if(Bolt_holes>=1)
    union()
        {
//M bolt holes
        translate([cs,cs,-0.5])
            {
                cylinder(d=Metric_Bolt_Size,h=tt+1,$fn=64);
            }
        translate([Panel_Size-cs,Panel_Size-cs,-0.5])
            {
                cylinder(d=Metric_Bolt_Size,h=tt+1,$fn=64);
            }
        translate([Panel_Size-cs,cs,-0.5])
            {
                cylinder(d=Metric_Bolt_Size,h=tt+1,$fn=64);
            }
        translate([cs,Panel_Size-cs,-0.5])
            {
                cylinder(d=Metric_Bolt_Size,h=tt+1,$fn=64);
            }
//counter-sunk cone
        translate([cs,cs,-0.01])
            {
                cylinder(r1=Metric_Bolt_Size,r2=0,h=Metric_Bolt_Size,$fn=64);
            }
        translate([Panel_Size-cs,Panel_Size-cs,-0.5])
            {
                cylinder(r1=Metric_Bolt_Size,r2=0,h=Metric_Bolt_Size,$fn=64);
            }
        translate([Panel_Size-cs,cs,-0.5])
            {
                cylinder(r1=Metric_Bolt_Size,r2=0,h=Metric_Bolt_Size,$fn=64);
            }
        translate([cs,Panel_Size-cs,-0.5])
            {
                cylinder(r1=Metric_Bolt_Size,r2=0,h=Metric_Bolt_Size,$fn=64);
            }
        };

    }
}

```


[User]
Hey, I'd like a parametric design for a tile bracelet, something similar to the one described in the provided SCAD file, but I need it rebuilt from the ground up with more modularity and potentially some additional features down the line.

Here are the core requirements:

1.  **Tiles:**
    *   Each tile should be a rounded rectangle (or a cube with rounded edges). The user should be able to control the overall `size` (width, depth, height), and `tile_radius` for the rounded corners.
    *   The tiles should be able to have text printed on them. The user should be able to specify the `content` (an array of strings, one string per tile), `font`, `text_size` and `text_height`.
    *   The tiles need holes for the string/elastic. The user should be able to choose how many `holes_count` (0, 1, or 2) the tiles have and the position should automatically adjust.
2.  **Spacers:**
    *   Spacers should be spherical, but scaled on one axis to make them wider. The user should control the `spacer_width`. The spacers should be positioned automatically between the tiles. The user should have an option (`spacers_narrow`) to determine if spacers are narrower than the tile width or equal to the tile width. They will need to also have holes that line up with the holes in the tiles.
3.  **Print Options:**
    *   The user should be able to choose to print only `Tiles`, only `Spacers`, or `All` components.
    *   The number of fragments (`$fn`) should also be adjustable for increased smoothness, and also for decreased render times when prototyping.

4.  **Assembly:** The code does not need to have any assembly logic. I can handle that myself.

I'm thinking a modular approach would be great; having a separate module for the tile, spacer, and overall bracelet assembly would be ideal.

For libraries I would like to keep it simple and just utilize the standard OpenSCAD libraries. No external libraries like MCAD, BOSL, or anything else should be needed. I would like a single .scad file for now.

[Assistant]
tile-bracelet.scad:
```
/*
Tile Bracelet v1.0
by Tamio Patrick Honma <tamio@honma.de> aka IOIO72

For Unicode characters use https://dn-works.com/wp-content/uploads/2020/UFAS-Fonts/Symbola.zip

Recommended print and assembly workflow:
1. Export tiles and spacers as separate parts.
2. You may need to print the tiles with a brim for better hot bed adhesion.
3. Print spacers with a raft for better print hot bed adhesion.
4. Thread a rubber thread up to half of the thread through the upper holes of the tiles and spacers.
5. Then thread the second half of the thread through the lower holes of the tiles and spacers.
6. Thread both ends of the thread through the corresponding holes of the last tile to close the bracelet.
7. Knot the ends of the thread together. Keep in mind that you can still pass your hand through the bracelet.

*/

/* [Basic] */

// Content
content = [" ", "P", "E", "A", "C", "E", " ", "L", "O", "V", "E", " "];

// Font (use menu `Help/Font List`)
font = "System Font:style=Regular";

// Text Size
text_size = 10;

// Text Height;
text_height = 0.8;

// Spacer
spacer_width = 3;

// Holes
holes_count = 2; // [0 : "No", 1 : "One", 2 : "Two"]

/* [Print Parts] */

// Print Parts
parts = "All"; // ["Tiles": "Tiles", "Spacers" : "Spacers (Available when Holes are set)", "All": "All"]

/* [Tile] */

// Width, Depth, Height
size = [10, 15, 5];

// Radius for rounded corners
tile_radius = 2.2;

/* [Advanced] */

// Align spacers narrow
spacers_narrow = true;

// Number of fragments
$fn = 50; // [0:100]

/* [Hidden] */

spacer_padding = spacers_narrow ? size.z + 1 : size.x + spacer_width;

/* Elements */

module hole(s, pos) {
  if(holes_count > 0) {
    translate([0, holes_count == 2 ? pos : 0, 0])
    rotate([0, 90, 0])
    cylinder(s.x + 1, d = s.z / 2, center = true);
  };
};

module tile(s, c, ts, th, r) {
  difference() {
    if (r > 0) {
      minkowski() {
        cube([s.x - r, s.y - r, s.z - 1], center = true);
        cylinder(d = r, center=true);
      };
    } else {
      cube(s, center = true);
    };
    hole(s, s.y / 4);
    hole(s, - s.y / 4);
  };
  translate([0, 0, s.z / 2 + th / 2])
  linear_extrude(th, center = true)
  text(c, ts, font, halign = "center", valign = "center");
};

module spacer(s, sw) {
  difference() {
    scale([1, 1, sw / s.z])
    sphere(d = s.z);
    cylinder(h = s.z + 1, d = s.z / 2, center = true);
  };
};

/* Main */

for(i = [1 : len(content)]) {
  if (parts == "Tiles" || parts == "All") {
    translate([i * (size.x + spacer_width), 0, size.z / 2])
    tile(size, content[i - 1], text_size, text_height, tile_radius);
  };
  if (parts == "Spacers" || parts == "All") {
    if (holes_count > 0) {
      translate([0, size.y, spacer_width / 2 - 0.3]) {
        translate([i * spacer_padding, 0, 0])
        spacer(size, spacer_width);
        if (holes_count == 2) {
          translate([i * spacer_padding, size.z + 2, 0])
          spacer(size, spacer_width);
        };
      };
    };
  };
};

```


[User]
I need an OpenSCAD design for a hose holder, and I'd like it to be fully parametric so I can easily adjust it for different hoses and mounting situations.

Here's a breakdown of what I'm looking for:

**Overall Structure:**

*   The holder should consist of two main parts: a base that can be screwed to a wall or other surface, and a curved "arm" that will cradle the hose.
*   The arm needs to have an opening where the hose can be placed into the holder, this is going to look like a sector of a ring that is open to allow you to place the hose in the holder.
*   The arm should have an adjustable angle, meaning the user can set how far the opening extends around the central point.
*   The arm should have optional rounded ends that are half-cylinders smoothly attached to the ends of the arc.
*   The base should have screw holes for mounting and optionally countersunk areas for wooden screws.

**Specific Parameters:**

*   `tube_diameter`: The inner diameter of the hose the holder is intended to support.
*   `width_holder`: The width of the holder (how far the hose will sit in from the wall).
*   `angle`: The opening angle of the arc of the hose holder (measured in degrees).
*   `round_end`: A boolean flag to enable/disable rounded ends on the arm.
*   `hole_diameter`: The diameter of the mounting holes.
*  `wooden_screw`: a boolean to enable or disable countersunk screw head support.
*   `holder_thick`: The thickness of the arm of the holder.
*   `basement_thick`: The thickness of the base of the holder.

**Mounting:**

*   I want the screw holes to be symmetric on the base, and placed such that they don't interfere with the hose holder itself. The holes should be sized according to the `hole_diameter` parameter.
*   Countersunk support will depend on `wooden_screw` being true or false

**Libraries:**

*   Please make sure you use the BOSL2 library, specifically `std.scad` and `metric_screws.scad`. I want to ensure I can use screws from the library, specifically I want to be able to use metric bolt calls for the countersunk supports.

I'm looking for a robust and flexible design. It's not super important, but having a high `fn` (fragment number) would also help smooth curves.

[Assistant]
hose-holder.scad:
```
include <BOSL2/std.scad>
include <BOSL2/metric_screws.scad>

$fn = 50;
fn = 50;

// inner tube diameter
tube_diameter = 40;
// width holder
width_holder = 20;
// angle for hose
angle = 250;
// if end holder should be sharp or round, add holder_thick to angle
round_end = true;

// hole diameter for screw
hole_diameter = 4;
// added head angle
wooden_screw = true;

// thick of holder and basement
holder_thick = 4;

// basement thick
basement_thick = holder_thick;

/* [Hidden] */
basementX = tube_diameter + 2*holder_thick + 7*hole_diameter;
angles = [0, angle];

// basement
difference() {
    cube([basementX,basement_thick,width_holder], anchor=FRONT);

    if (wooden_screw) {
        right(hole_diameter+tube_diameter/2 + holder_thick+hole_diameter/2)
            back(holder_thick) metric_bolt(headtype="countersunk", size=hole_diameter, l=basement_thick, details=false, phillips="#2",coarse=false, anchor=TOP, orient=BACK);

        left(hole_diameter+tube_diameter/2 + holder_thick+hole_diameter/2)
            back(holder_thick) metric_bolt(headtype="countersunk", size=hole_diameter, l=basement_thick, details=false, phillips="#2",coarse=false, anchor=TOP, orient=BACK);
    }

    right(hole_diameter+tube_diameter/2 + holder_thick+hole_diameter/2)
    xrot(90)
        cylinder(h=holder_thick, d=hole_diameter, anchor=TOP);

    left(hole_diameter+tube_diameter/2 + holder_thick+hole_diameter/2)
    xrot(90)
        cylinder(h=holder_thick, d=hole_diameter, anchor=TOP);

}

// tube holder
down(width_holder/2)
back(tube_diameter/2+holder_thick)
zrot(90 + ((360-angle)/2))
{

    if (round_end)
    {
        round_end_holder(tube_diameter,holder_thick, width_holder, 0);
        round_end_holder(tube_diameter,holder_thick, width_holder, angle);
    }
    linear_extrude(width_holder) arc(tube_diameter/2, angles, holder_thick);
}

module sector(radius, angles, fn = fn) {
    r = radius / cos(180 / fn);
    step = -360 / fn;

    points = concat([[0, 0]],
        [for(a = [angles[0] : step : angles[1] - 360])
            [r * cos(a), r * sin(a)]
        ],
        [[r * cos(angles[1]), r * sin(angles[1])]]
    );

    difference() {
        circle(radius);
        polygon(points);
    }
}

module arc(radius, angles, width = 1, fn = fn) {
    difference() {
        sector(radius + width, angles, fn);
        sector(radius, angles, fn);
    }
}

module round_end_holder(tube_diameter, holder_thick, width_holder, angle) {
    rs = (tube_diameter+(2*holder_thick)-tube_diameter)/4;
    centerRadius = tube_diameter/2 + holder_thick/2;
    translate([centerRadius*cos(angle),centerRadius*sin(angle),0])
        cylinder(r = rs, h = width_holder);
}

```


[User]
Okay, so I'm trying to design a tool for straightening the fins on my car's AC condenser. I've messed around with a design in OpenSCAD already, and I have a working version, but I want something more robust and configurable, ideally with easier handling. I'd like it to be fully parametric so I can easily tweak it for different fin spacing and depths, and maybe even add some features later on.

The basic idea is a comb with teeth that fit between the condenser fins. The teeth need to be spaced properly for the fins. I'm using variables `fins`, `finSpace`, and `finWidth` to control the number of fins, space between the fins, and width of each fin. The existing design uses `combsHeight` to set the depth of the teeth, and `lengthShrinkFactor` is kind of a hack to make it fit my 3D printer, I would like this length to be adjustable without scaling by a factor. Instead I would like the total `length` of the comb to be a configurable parameter.

Here’s a breakdown of the features I'd like:

*   **Parametric Teeth:** The core of the design are the teeth of the comb.  I need to be able to easily specify the number of teeth, the spacing between them, and the thickness of each tooth. I would like the base of the teeth to be a full base instead of individual squares.
*  **Configurable Depth:**  I need to be able to easily control the height of the teeth.
*   **Handle:** The current design uses a base that I just add some material to, but the handle is clunky. I’d like something with a more ergonomic handle. It should be able to be either a fixed, easy to hold handle, or perhaps allow for the addition of other handles, such as an M3 screw mount for a small handle.
*   **Material:** I print this in PLA, but this is just a reference design. I would like to include a parameter to be able to scale all of the features to accommodate the material shrinkage factor.
*   **Rounded edges:** A small fillet on the top edge of the teeth would improve the ease of use.

I'd really like the design to be easy to customize for different fin sizes and materials. Also, I'd like to be able to adjust the length of the comb without needing to adjust the scale of the object. I would also like to be able to adjust the handle without it interfering with the function of the teeth.

I am not using any specific libraries for this design, and it's all basic shapes and transformations. Could you generate the needed OpenSCAD files for this?

[Assistant]
AdjustableFinRepairComb.scad:
```

//user identified parameters
//change these dependant upon your needs
fins = 14;
finSpace = .8;
finWidth = .5;
combsHeight = 4;
lengthShrinkFactor = 1.4;

//code to generate comb

combsLength = fins*(finSpace + finWidth)/lengthShrinkFactor;
scaleFactor = (combsHeight / finSpace);

union(){
for (i = [0:fins]){

    rotate([90,90,0])
    translate([0,i * (finSpace + finWidth),0])
    scale([scaleFactor,1,1])
    cylinder(combsLength,finSpace/2,finSpace/2,$fn=80);

    translate([-finSpace*.5+i*(finSpace+finWidth),-combsLength,-combsHeight/2])
    cube([finSpace,combsLength,combsHeight/2]);
}

 difference(){
    translate([-finSpace/2,0,-combsHeight/2])
    cube([fins*(finSpace + finWidth)+finSpace,combsLength*2,combsHeight*1.2]);

   translate([fins*(finSpace + finWidth)/2,fins*(finSpace + finWidth)/1.5,-combsHeight*.6])
   cylinder(combsHeight*1.4,fins*(finSpace + finWidth)/2.5, fins*(finSpace + finWidth)/2.5,$fn=80);

     translate([-finWidth,0,combsHeight/2])
     rotate([45,0,0])
     cube([fins*(finSpace + finWidth)+(finWidth*2),combsHeight*1.2,combsHeight*1.2]);

 }

}

```


[User]
I'd like to design a parametric connector for stacking IKEA Lack tables, and I'd like to be able to control several aspects of the design. I'm thinking of something similar to the "Lack Stacker" on Thingiverse, but with some added features.

Specifically, I need the connector to:

1.  Securely hold a Lack table leg, using the existing leg's cylindrical shape as an insertion point.
2.  Provide an offset to lift the Lack table above the table below it. I need to adjust this height as a parameter.
3.  Have a cutout to allow the insertion of the existing table top as a second insertion point, this needs to have its own tolerance.
4.  Be able to be optionally configured with holes for mounting casters (small wheels) instead of having a Lack leg inserted at the bottom. These holes need to be parametrically set, for position and diameter.
5.  Optionally, have a hole through the top of the connector to allow screwing the Lack leg from above. This needs to have a diameter big enough to accommodate a screw head and the screw itself.
6.  Have adjustable tolerances for both the Lack leg and table top insertions.
7. Have a specified wall width parameter.

I'm hoping for an OpenSCAD design that lets me control these things:

*   **Wall Width:** Thickness of the walls of the connector.
*   **Table Offset:** Height of the connector from the bottom up to the inserted table top.
*   **Lack Foot Hold Height:** Height of the area that surrounds the inserted leg.
*  **Lack Foot Diameter:**  The diameter of a Lack leg.
*   **Lack Table Diameter:** The diameter of a Lack table top that gets inserted into the base.
*   **Lack Foot Offset:** A small radial offset when inserting the leg.
*   **Lack Foot Tolerance:** Tolerance for the Lack leg insertion.
*   **Lack Table Tolerance:** Tolerance for the Lack table top insertion.
*   **Middle Hole Size:** Diameter of a screw hole in the bottom of the connector.
*   **Holes for Wheels:** Boolean value, determines whether to create holes for mounting casters.
*   **Wheels Hole Offset X/Y:** Offsets for the caster holes in the X and Y directions.
*   **Wheels Hole Size:** Diameter of the caster mounting holes.
*   **Through Screw for Leg:** Boolean value, determines whether to create a through hole for screwing in the leg.
*   **Through Screw Head Diameter:** Diameter of the top hole to accommodate a screw head.
*   **Through Screw Hole Diameter:** Diameter of the through screw hole.
*   **Faces:** Number of faces to use for round objects.

Could you create a parametric OpenSCAD file (or files) that implements this? I would prefer not to use any libraries for this design.

[Assistant]
LackStack.scad:
```
// preview[view:south west, tilt:side]

// Wall width for walls all around in mm
wall_width=2.0;

// Foot height above the table in mm - 0 means the foot will sit on the table with no wall
table_offset=20.0;

// Height of the offset wall around the Lack foot in mm
lack_foot_hold_height=10.0;

// Width of a Lack leg in mm
lack_foot_dia=50.4;

// Thickness of the table plate in mm
lack_table_dia=50.0;

// Offset to the table endge when screwed in
lack_foot_offset=1.8;

// Tolerance in mm you want to insert your foot - depends on your printer calibration
lack_foot_tolerance=0.4;

// Tolerance in mm you want for the table insert - depends on your printer calibration
lack_table_tolerance=0.2;

// Size for the screw hole in mm
middle_hole_size=9.0;

// Select whether you want to have 4 holes to use coaster wheels instead of a Lack leg
holes_for_wheels=0; // [0:false, 1:true]

// Offset for the holes in X direction (in mm)
wheels_hole_offset_x=17.0;

// Offset for the holes in X direction (in mm)
wheels_hole_offset_y=17.0;

// Size for the wheel holes (in mm)
wheels_hole_size=3.0;

// Select whether you want to have a hole through the top section e.g. to screw in the Lack leg
through_screw_for_leg=0; // [0:false, 1:true]

// Top screw hole diameter in mm to compensate for the screw head size.
through_screw_head_dia=9.5;

// Top screw hole diameter in mm for the top screw hole
through_screw_hole_dia=6.0;

// that shall be rendered
faces = 20;

epsilon=0.01;

module lack_cutout() {
    dia=lack_foot_dia+lack_foot_tolerance+wall_width+lack_foot_offset;
    height=lack_table_dia+lack_table_tolerance;
    translate([-epsilon,-epsilon,0])
    cube([dia+epsilon,dia+epsilon,height]);
}

module foot_cutout() {
    height=lack_foot_dia+lack_foot_tolerance;
    translate([0,0,height/2])
    cube([lack_foot_dia+lack_foot_tolerance,lack_foot_dia+lack_foot_tolerance,height], center=true);
}

module bottom_hole_cutout() {
    if((through_screw_for_leg == 1) && (through_screw_head_dia > middle_hole_size)) {
            cylinder(h=2*wall_width+epsilon, d=through_screw_head_dia, center=true);
    }
    else {
        cylinder(h=2*wall_width+epsilon, d=middle_hole_size, center=true);
    }
}

module wheel_holes_cutout() {
    if(holes_for_wheels == 1) {
        translate([wheels_hole_offset_x,wheels_hole_offset_y,0])
        cylinder(h=2*wall_width+epsilon, d=wheels_hole_size, center=true);
        translate([-wheels_hole_offset_x,wheels_hole_offset_y,0])
        cylinder(h=2*wall_width+epsilon, d=wheels_hole_size, center=true);
        translate([wheels_hole_offset_x,-wheels_hole_offset_y,0])
        cylinder(h=2*wall_width+epsilon, d=wheels_hole_size, center=true);
        translate([-wheels_hole_offset_x,-wheels_hole_offset_y,0])
        cylinder(h=2*wall_width+epsilon, d=wheels_hole_size, center=true);
    }
}

module through_hole_cutout() {
    if(through_screw_for_leg == 1) {
        translate([0,0,table_offset/2])
        cylinder(h=table_offset+epsilon, d=through_screw_head_dia, center=true);
        translate([0,0,table_offset/2])
        cylinder(h=table_offset+2*wall_width+epsilon, d=through_screw_hole_dia, center=true);
    }
}

function base_dia() = lack_foot_dia+lack_foot_tolerance+lack_foot_offset+2*wall_width;
function base_height() = table_offset+lack_foot_hold_height+lack_table_dia+lack_table_tolerance+wall_width;

function to_base(x,y) = [x+base_dia()/2-lack_foot_offset/2, y+base_dia()/2-lack_foot_offset/2,0];

module base_cube() {
    cube([base_dia(),base_dia(),base_height()]);
}

module part() {
    difference() {
        base_cube();
        translate([0,0,wall_width])
        lack_cutout();
        translate(to_base(0,0))
        translate([0,0,wall_width+lack_table_dia+lack_table_tolerance+table_offset])
        foot_cutout();
        translate(to_base(0,0))
        bottom_hole_cutout();
        translate(to_base(0,0))
        wheel_holes_cutout();
        translate(to_base(0,0))
        translate([0,0,lack_table_dia+lack_table_tolerance])
        through_hole_cutout();
    }
}

/*
    Final part
    rotate it to a printable version
    make sure to use supports
*/
translate([0,0,base_dia()])
rotate([-90,0,0])
part($fn=faces);
```


[User]
Hey, I'm looking to design a wall-mounted holder for a remote control, and I'd like it to be parametric so I can adjust it for different remotes. I have a rough idea of the features I need, and I'd like to see if you can help me generate the OpenSCAD code.

Here's what I'm thinking:

1.  **Basic Shape:** The holder should have a main compartment to hold the remote, with a slot at the front to easily slide the remote in and out. The main compartment should have rounded corners (like a cylinder with part of the top and bottom cut off).
2.  **Parameters:**
    *   `comp_w`: Width of the main compartment.
    *   `comp_d`: Depth of the main compartment.
    *   `comp_h`: Height of the main compartment.
    *   `slot_w`: Width of the front slot.
    *   `slot_lower`: Height of the lower lip of the front slot.
    *   `corner_r`: Radius of the rounded corners of the main compartment and the slot.
    *   `wall`: Thickness of the compartment walls.
    *   `chamfer`:  A chamfer on the top and bottom edges of the compartment opening (can be 0 for no chamfer).
    *  `mounting_screw_holes_dia`: Diameter of the mounting screw holes
    * `mounting_screw_holes_yesno`: A boolean to toggle mounting screw holes on/off.
    *  `slot_corners_cut`: A boolean to toggle cut corners on the front slot on/off.
    * `detail`: A parameter to control the detail of the rendering
    * `tol`: a tolerance parameter for fit
3.  **Mounting:** It should have optional screw holes for mounting, or I could use double-sided tape if I don't want the holes. The mounting holes should be located in the middle of the back of the holder, left and right.
4. **Cutout corners:** It should be possible to cut off the corners of the cutout section, this makes it easier to insert the remote control. This should be optional.
5.  **Detail:** The detail of the rendering should be able to be increased, this means more polygons and more rendering time.

I don't have any code to start with, so I would need the complete OpenSCAD code for the design. I hope that's clear enough; let me know if you need any more details! I don't need any libraries - just vanilla OpenSCAD.

[Assistant]
remote_holder_updated.scad:
```
//
//  wall holder for aircond remote control
//
//  design by egil kvaleberg, 5 october 2016
//
//  added support for chamfer
//
// Updated by JW693D, June 8 2022
//  Features added:
// - mounting screw holes can be switch off for mounting with double sided tape
//- the corners of the cut-out section can be cut off (or not) to make inserting the remote easier
//- the detail of the render can be chosen. (higher quality at the cost of render time)

// width of compartment
comp_w = 45;
// depth of compartment
comp_d = 25;
// height of compartment
comp_h = 60;
// width of front slot
slot_w = 30;
// height of slot lower lip
slot_lower = 10;
// corner radius of compartment
corner_r = 3.5;
// wall thickness of comparement
wall = 2.4;
// chamfer for top entry and bottom (typical value is 0.6)
chamfer = 0.6; // [0.0:0.1:2.0]
// diameter of mounting screws
mounting_screw_holes_dia = 4.0;
// mounting screws holes yes=1, no =0
mounting_screw_holes_yesno = 1; // [0:1:1]
// cut off the corners of the cutout slot
slot_corners_cut=1; // [0:1:1]

// generic tolerance
tol = 0.3;
d = 1*0.1;
// higher = more detail and more time to render (30=typical)
detail=30; // [5:5:100]

module add() {
    // chamfered bottom section
    if (chamfer > 0) hull() for (sx = [-1,1]) for (sy = [-1,1])
        translate([sx*(comp_d/2-corner_r),sy*(comp_w/2-corner_r),-wall]) {
            cylinder(r1=wall+corner_r-chamfer, r2=wall+corner_r, h=chamfer, $fn=detail);
        }
    // main section
    hull() for (sx = [-1,1]) for (sy = [-1,1])
        translate([sx*(comp_d/2-corner_r),sy*(comp_w/2-corner_r),chamfer-wall]) {
            cylinder(r=wall+corner_r, h=wall+comp_h-chamfer, $fn=detail);
        }
}

module sub() {
    // main compartment
    hull () for (sx = [-1,1]) for (sy = [-1,1])
        translate([sx*(comp_d/2-corner_r),sy*(comp_w/2-corner_r),0]) {
            cylinder(r=corner_r, h=comp_h+d, $fn=detail);
    }
    // main compartment, chamfered section
    if (chamfer > 0) hull () for (sx = [-1,1]) for (sy = [-1,1])
        translate([sx*(comp_d/2-corner_r),sy*(comp_w/2-corner_r),comp_h-chamfer]) {
            translate([0,0,]) cylinder(r1=corner_r, r2=corner_r+chamfer+d, h=chamfer+d, $fn=detail);
    }
    // slot in front
    translate ([0,0,slot_lower+comp_h/2]) rotate([0,-90,0]) hull () for (sx = [-1,1]) for (sy = [-1,1])
        translate([sx*(comp_h/2-corner_r),sy*(slot_w/2-corner_r),0]) {
            cylinder(r=corner_r, h=comp_h+d, $fn=detail);
    }
    // cut off corners
    if (slot_corners_cut>0)
    translate([-comp_d+5,0,comp_h]) {
    rotate ([0,45,90]) cube ([comp_w/sqrt(2),comp_d,comp_w/sqrt(2)],center =true, $fn=detail);
    }
    // mounting holes
    if (mounting_screw_holes_yesno>0)
    translate ([comp_d/2-d,0,comp_h/2]) rotate([0,90,0]) for (sy = [-1,1])
       translate([0,sy*slot_w/4,0]) {
           cylinder(d1=mounting_screw_holes_dia+2*tol+2*wall*0.6, d2=mounting_screw_holes_dia+2*tol, h=d+wall*0.6, $fn=detail);
            cylinder(d=mounting_screw_holes_dia+2*tol, h=d+wall+d, $fn=detail);
    }
}
    // mounting holes
    if (0) color("red") translate ([comp_d/2-d,0,comp_h/2]) rotate([0,90,0]) for (sy = [-1,1])
        translate([0,sy*slot_w/4,0]) {
            cylinder(d=mnt_dia+2*tol, h=d+wall+d, $fn=detail);
    }

    // main structure:
    //union(){
    difference () {
        color("red") add();
        difference () {
            sub();
            //
        }
    }

```


[User]
Hey, I'm looking to get a customizable ceiling hook designed in OpenSCAD. I've got some parameters in mind, and I'd like it to be fairly flexible so I can tweak it later.

The hook needs a cylindrical base, and then a stem rising from the center of the base, followed by the hook itself.

For the base, I'd like to control the:

*   `base_diameter`: obviously the diameter of the cylinder.
*   `base_height`: the height of that cylinder.
*   The base also has an inner, slightly smaller cylinder sitting on top of it, creating a lip.
*   There should also be a rounded edge on the very top-outer perimeter of the base to avoid sharp corners.
*  Finally, I need screw holes (two is fine) with countersunk sections so the screws can sit flush.  I'd need parameters for:
    *   `hole_diameter`: the diameter of the main screw hole
    *   `hole_distance_from_edge`: the distance from the outer edge of the base to the center of the hole.

The stem should just be a simple cylinder, and I'll need a parameter for:

*   `stem_length`: how long the stem should be.
*   `thickness`: used for the stem and also the hook structure.

The hook itself is tricky; It needs to be a bent shape with a consistent thickness.  I want it to essentially be a tube bent around a curve.  I need parameters for:

*   `hook_outer_radius`: the radius of the curve at the outer edge of the hook structure.
*    `hook_angle`: controls the overall bend/circumference of the hook. 360 would result in a closed ring and anything less would be a partial ring.

I think that covers all of it.  I need it to be parametric so I can change all of these easily. Can you generate the OpenSCAD code for this? Oh, and I don't think I'm using any external libraries for this, but it would be good to use `$fa` and `$fs` for smoothness.

[Assistant]
Ceiling_hook.scad:
```
//Siebe Bosch, June 12, 2022
//https://www.thingiverse.com/siebebosch/designs//////////////////
//version: 1.0

//VARIABLES/////////////////////////////////////////////////////////
$fa = 1;    //minimum angle
$fs = 0.2;  //minimum resolution
hook_outer_radius = 12;
thickness = 3.5;
base_diameter = 12;
base_height = 4;
stem_length = 10;
hook_angle = 310;
hole_diameter =1.5;
hole_distance_from_edge = 5;
///////////////////////////////////////////////////////////////

//BASE/////////////////////////////////////////////////////////
difference(){
    union(){
        //base cylinder
        cylinder(base_height,base_diameter,base_diameter);

        //inner cylinder on top of base cylinder
        cylinder(base_height+thickness/2,base_diameter-thickness/2,base_diameter-thickness/2);

        //rounded corner
        translate([0,0,base_height])
        rotate([0,0,90])
        rotate_extrude(angle=360) {
        translate([base_diameter - thickness/2, 0])
            circle(d=thickness);
        }
    };
    //screw holes
    union(){
        translate([-base_diameter + hole_distance_from_edge,0,-1]) cylinder(base_height+20,hole_diameter,hole_diameter);
        translate([-base_diameter + hole_distance_from_edge,0,base_height+thickness/4]) cylinder(thickness,hole_diameter,hole_diameter*2);
        translate([base_diameter - hole_distance_from_edge,0,-1]) cylinder(base_height+20,hole_diameter,hole_diameter);
        translate([base_diameter - hole_distance_from_edge,0,base_height+thickness/4]) cylinder(thickness,hole_diameter,hole_diameter*2);
    }
}
///////////////////////////////////////////////////////////////

//STEM////////////////////////////////////////////////////////
linear_extrude(thickness,0,stem_length,1,1)
circle(d=thickness);
///////////////////////////////////////////////////////////////

//HOOK////////////////////////////////////////////////////////
//base
translate([0,-hook_outer_radius+thickness/2,stem_length])
rotate([-90,0,90])
rotate_extrude(angle=-60) {
 translate([hook_outer_radius - thickness/2, 0])circle(d=thickness);
}

//hook
translate([0,0,stem_length + 2*(sin(60)*(hook_outer_radius-thickness/2))])
rotate([-90,120,90])
rotate_extrude(angle=hook_angle) {
 translate([hook_outer_radius - thickness/2, 0])circle(d=thickness);
}
///////////////////////////////////////////////////////////////

```


[User]
Hey, I'm looking to design a customizable telescoping stick for 3D printing, and I think I need a parametric design in OpenSCAD. I'd like the stick to have multiple segments that slide out from each other and lock into place when fully extended, and I also want the ability to screw additional parts on using threads at either the top or bottom of the stick.

Here are the key features I'm looking for, and how I'd like to be able to customize them:

*   **Base Dimensions:** I want to be able to set the base radius of the smallest segment, the base height of each segment, the number of base segments, and the base thickness of the walls. I should also be able to define the height of a bottom cap on the innermost segment and a corner radius for all edges.
*   **Locking Mechanism:** The stick needs a locking mechanism that engages at the end of each segment's extension. I want to set the locking angle, the lock height, the offset from the segment end, the number of locks per segment, and the lock thickness.
*   **Latching Mechanism:** I want a latching feature as well. I'd like to define its angle, its height, its thickness, and an option to enable the latch in the non-extended/start position. This feature should provide a more secure lock.
*   **Threading:** I need to be able to specify a height, pitch, and depth for both an inner thread at the bottom of the smallest segment and an outer thread at the top of the largest segment. The inner thread will be recessed.
*   **Printing Tolerance:** I'd like to account for printing tolerances by setting both XY and Z tolerances, so that parts will fit together nicely.
*   **Cross-Section View:** I'd like an option to enable a cross-section view of the stick for debugging, and to specify a rotation angle for that view. I'd also like a debug option to extend the inner segments out to see the positions easier.

I think that covers everything. Please make sure to include appropriate assertions to prevent invalid or broken configurations, and also provide some debug output for the final dimensions. I'd like you to use Simon Löffler's `helix_extrude()` function for the threads. I don't need any extra libraries.

[Assistant]
telescope-stick.scad:
```
// Copyright 2022 Daniel Starke
// Licensed under CC BY-NC-SA 4.0 (https://creativecommons.org/licenses/by-nc-sa/4.0/)
// Note: Set "Turn off rendering at [100000] elements" in Edit/Preferences/Advanced if rendering the threads fails.

/* [Base Settings] */
// Base radius of the smallest segment (top view) [mm].
R_BASE = 8.0; // [0.1:0.1:500]
// Base height (front view) [mm].
H_BASE = 120.0; // [0.1:0.1:500]
// Number of base segments.
N_BASE = 2; // [2:1:20]
// Base thickness [mm].
T_BASE = 2; // [0.1:0.1:20]
// Bottom height/thickness within the innermost element [mm].
H_BOTTOM = 1; // [0:0.1:20]
// Corner radius [mm].
R_CORNER = 0.3; // [0.1:0.1:2]

/* [Lock Settings] */
// Lock angle [deg].
A_LOCK = 20; // [1:0.5:60]
// Lock height [mm].
H_LOCK = 10; // [0.1:0.1:100]
// Lock edge offset [mm].
O_LOCK = 10; // [0.1:0.1:100]
// Number of locks.
N_LOCK = 3; // [1:1:3]
// Lock thickness (<T_BASE) [mm].
T_LOCK = 1; // [0.5:0.1:20]

/* [Latch Settings] */
// Latch angle (<A_LOCK) [deg].
A_LATCH = 8; // [0.1:0.1:20]
// Latch height (<H_LOCK) [mm].
H_LATCH = 3; // [0.1:0.1:20]
// Latch thickness (<T_BASE) [mm].
T_LATCH = 0.6; // [0.1:0.1:20]
// Enable latch at non-expanded/start position?
E_START_LATCH = 1; // [0:"No",1:"Yes"]

/* [Threads] */
// Innermost thread height (or 0 if disabled) [mm].
H_INNER_THREAD = 20; // [0:0.1:250]
// Innermost thread pitch [mm].
P_INNER_THREAD = 2; // [1:0.1:25]
// Innermost thread depth [mm].
D_INNER_THREAD = 0.4; // [0.1:0.1:25]
// Outermost thread height (or 0 if disabled) [mm].
H_OUTER_THREAD = 0; // [0:0.1:250]
// Innermost thread pitch [mm].
P_OUTER_THREAD = 2; // [1:0.1:25]
// Innermost thread depth [mm].
D_OUTER_THREAD = 0.4; // [0.1:0.1:25]

/* [Printing Settings] */
// XY printing tolerance [mm].
T_XY = 0.13; // [0:0.01:1]
// Z printing tolerance [mm].
T_Z = 0.04; // [0:0.01:1]

/* [Cross Section] */
// Enable cross section view?
C_ENABLE = 0; // [0:"No",1:"Yes"]
// Cross section rotation angle [deg].
C_ANGLE = 0; // [0:1:360]
// Debug extended position by moving the inner segments out?
D_EXTEND = 0; // [0:"No",1:"Yes"]

/* [Hidden] */
// Epsilon for mathematic rounding compensation [mm].
EPS = 0.01;
// Number of fragments.
$fn = 100;

// Arc length to angle in degrees.
function arcAngle(s, r) = 180 * s / (r * PI);
// Beveled arc angle between r2 and r1 for a 45° bevel and the given arc angle of r2.
function arcBevelBeta(r1, r2, angle) = 180 - (angle / 2) - asin(sin(135) * r1 / r2);
// Beveled arc radius to form a 45° bevel for the given r1, r2 and arc angle.
function arcBevelRadius(r1, r2, angle) = r1 * sin(45) / sin(arcBevelBeta(r1, r2, angle));

echo("########################################")
echo(str("Maximum extend: ", H_BASE + ((N_BASE - 1) * (H_BASE - (2 * O_LOCK) - H_LOCK)), "mm"));
echo(str("Maximum radius: ", N_BASE * (T_BASE) - T_XY + R_BASE, "mm"));
echo("########################################")

assert(H_BASE > (H_LOCK + (2 * O_LOCK) + T_LOCK), "Lock height and offset need to be smaller than base height.");
assert(A_LOCK > (A_LATCH + arcAngle(2 * (T_LATCH + T_XY), R_BASE)), "Lock angle needs to be larger than latch angle.");
assert(H_LOCK > (H_LATCH + (2 * T_BASE)), "Lock height needs to be larger than latch height.");
assert(T_BASE > (T_LOCK + (2 * T_XY)), "Lock thickness needs to be less than the base thickness");
assert(T_LOCK > (T_LATCH + (2 * T_XY)), "Latch thickness needs to be less than the lock thickness");
assert(T_BASE > (2 * R_CORNER), "R_CORNER is too large compared to T_BASE.");
assert(P_INNER_THREAD > ((4 * (D_INNER_THREAD + T_Z))), "Inner thread pitch needs to be at least four times its thread depth.");
assert(P_OUTER_THREAD > ((4 * (D_OUTER_THREAD + T_Z))), "Outer thread pitch needs to be at least four times its thread depth.");
assert(H_INNER_THREAD == 0 || H_INNER_THREAD > ((2 * R_CORNER) + D_INNER_THREAD), "Inner thread height is too small.");
assert(H_OUTER_THREAD == 0 || H_OUTER_THREAD > ((2 * R_CORNER) + D_OUTER_THREAD), "Outer thread height is too small.");
assert(O_LOCK > (H_BOTTOM + T_LOCK + T_Z), "H_BOTTOM is too high and overlaps with the lock.");

// Cutout section for the slide lock.
module LockCut(r, angle) {
	rotate([0, 0, angle - arcAngle(T_XY, r)])
	translate([0, 0, O_LOCK - T_Z])
	for (bottom = [-(T_LOCK / 2)], top = [H_BASE - (2 * O_LOCK) + (T_LOCK / 2) + T_Z])
	union() {
		// vertical shaft
		rotate_extrude(angle = A_LOCK + (2 * arcAngle(T_XY, r)))
		polygon([
			// inner bottom
			[0, bottom],
			// outer bottom
			[r, bottom],
			[r + T_LOCK, bottom + T_LOCK],
			// outer top
			[r + T_LOCK, top - T_LOCK],
			[r, top],
			// inner top
			[0, top]
		]);
		// horizontal shaft
		rotate([0, 0, -A_LOCK])
		rotate_extrude(angle = (2 * A_LOCK) + (2 * arcAngle(T_XY, r)))
		polygon([
			// inner bottom
			[0, top - H_LOCK - T_LOCK - (2 * T_Z)],
			// outer bottom
			[r, top - H_LOCK - T_LOCK - (2 * T_Z)],
			[r + T_LOCK, top - H_LOCK - (2 * T_Z)],
			// outer top
			[r + T_LOCK, top - T_LOCK],
			[r, top],
			// inner top
			[0, top]
		]);
	}
}

// The slide lock section.
module Lock(r, angle) {
	rotate([0, 0, angle + arcAngle(T_XY, r)])
	translate([0, 0, O_LOCK + T_Z])
	for (bottom = [-(T_LOCK / 2)]) {
		// base dilations
		rotate_extrude(angle = A_LOCK - (2 * arcAngle(T_XY, r)))
		polygon([
			// inner bottom
			[r - EPS, bottom],
			// outer bottom
			[r, bottom],
			[r + T_LOCK - T_XY, bottom + T_LOCK],
			// outer top
			[r + T_LOCK - T_XY, bottom + H_LOCK - (2 * T_Z)],
			[r, bottom + H_LOCK + T_LOCK - (2 * T_Z)],
			// inner top
			[r - EPS, bottom + H_LOCK + T_LOCK - (2 * T_Z)]
		]);
	}
}

// Centered arc with 45° bevel.
module BeveledArc(radius, height, angle, depth) {
	intersection() {
		// base part
		translate([0, 0, -EPS])
		rotate([0, 0, -((angle / 2) + EPS)])
		rotate_extrude(angle = angle + (2 * EPS))
		polygon([
			// inner bottom
			[radius, depth],
			// outer bottom
			[radius + depth, 0],
			// outer top
			[radius + depth, height],
			// inner top
			[radius, height - depth]
		]);
		// side bevel
		translate([arcBevelRadius(radius, radius + depth, angle), 0, 0])
		rotate([0, 0, -(180 - arcBevelBeta(radius, radius + depth, angle))])
		rotate_extrude(angle = 2 * (180 - arcBevelBeta(radius, radius + depth, angle)))
		square([radius + depth, height], false);
	}
}

// Inner segment latch inlet.
module InnerLatch(r, angle) {
	rotate([0, 0, angle + (A_LOCK / 2) + arcAngle(T_XY, r)])
	translate([0, 0, O_LOCK + (T_LATCH / 2) + ((H_LOCK - H_LATCH) / 2) - T_Z])
	BeveledArc(
		r + T_LOCK - T_LATCH - T_XY,
		H_LATCH + T_LATCH + (2 * T_Z),
		A_LATCH + arcAngle(2 * T_XY, r),
		T_LATCH + T_XY + EPS
	);
}

// Outer segment latch embossment for lock position.
module OuterLatch(r, angle) {
	rotate([0, 0, angle - (1.5 * A_LOCK)])
	translate([0, 0, H_BASE + (T_LATCH / 2) - O_LOCK - (H_LOCK  + H_LATCH) / 2])
	BeveledArc(
		r + T_LOCK - T_LATCH,
		H_LATCH + T_LATCH,
		A_LATCH,
		T_LATCH + T_XY + EPS
	);
}

// Outer segment latch embossment for start position.
module OuterStartLatch(r, angle) {
	if (E_START_LATCH == 1)
	rotate([0, 0, angle + (A_LOCK / 2)])
	translate([0, 0, O_LOCK + (T_LATCH / 2) + ((H_LOCK - H_LATCH) / 2)])
	BeveledArc(
		r + T_LOCK - T_LATCH,
		H_LATCH + T_LATCH,
		A_LATCH,
		T_LATCH + T_XY + EPS
	);
}

// Innermost segment thread.
module InnerThread(radius, height, pitch, tDepth, tHeight) {
	intersection() {
		// thread with 2 pitch extra height; one in both directions
		translate([0, 0, H_BASE - height - pitch])
		helix_extrude(
			[
				// inner bottom
				[T_XY, tDepth + T_Z],
				// outer bottom
				[tDepth + T_XY, T_Z],
				[tDepth + T_XY + EPS, T_Z],
				// outer top
				[tDepth + T_XY + EPS, tHeight + tDepth - T_Z],
				[tDepth + T_XY, tHeight + tDepth - T_Z],
				// inner top
				[T_XY, tHeight - T_Z]
			],
			radius - T_BASE - tDepth,
			pitch,
			height + (2 * pitch)
		);
		// cut out extra pitch to adjust the height correctly
		for (rInner = R_BASE - T_BASE - R_CORNER - tDepth)
		rotate_extrude(angle = 360)
		polygon([
			// inner bottom
			[0, H_BASE - height],
			// middle bottom
			[R_BASE - tDepth, H_BASE - height],
			// outer bottom
			[R_BASE + R_CORNER + T_XY, H_BASE - height - (tDepth + R_CORNER + T_XY)],
			// outer top
			[R_BASE + R_CORNER + T_XY, H_BASE],
			// middle top
			[R_BASE - tDepth, H_BASE - (tDepth + R_CORNER + T_XY)],
			// inner top
			[0, H_BASE - (R_CORNER + T_XY)]
		]);
	}
}

// Outermost segment thread.
module OuterThread(radius, height, pitch, tDepth, tHeight) {
	intersection() {
		// thread with 2 pitch extra height; one in both directions
		translate([0, 0, -pitch])
		helix_extrude(
			[
				// inner bottom
				[0, T_Z],
				[T_XY + EPS, T_Z],
				// outer bottom
				[tDepth + T_XY + EPS, tDepth + T_Z],
				// outer top
				[tDepth + T_XY + EPS, tHeight - T_Z],
				// inner top
				[T_XY + EPS, tHeight + tDepth - T_Z],
				[0, tHeight + tDepth - T_Z]
			],
			radius - (2 * T_XY) - EPS,
			pitch,
			height + (2 * pitch)
		);
		// cut out extra pitch to adjust the height correctly
		rotate_extrude(angle = 360)
		polygon([
			// inner bottom
			[0, 0],
			// middle bottom
			[radius - R_CORNER - T_XY, 0],
			// outer bottom
			[radius + tDepth - T_XY + EPS, tDepth + R_CORNER + EPS],
			// outer top
			[radius + tDepth - T_XY + EPS, height - tDepth - T_XY - EPS],
			// middle top
			[radius - T_XY, height - T_XY],
			// inner top
			[0, height - T_XY]
		]);
	}
}

// A single telescope stick segment.
module Segment(r, i, isFirst, isLast) {
	rotate([0, 0, ((3 * A_LOCK) + arcAngle(2 * T_XY, R_BASE))])
	union() {
		difference() {
			// base cylinder
			rotate_extrude(angle = 360)
			polygon([
				// inner bottom
				[r - T_BASE + T_XY, R_CORNER],
				[r - T_BASE + R_CORNER + T_XY, 0],
				// outer bottom
				[r - R_CORNER - T_XY, 0],
				[r - T_XY, R_CORNER],
				// outer top
				[r - T_XY, H_BASE - R_CORNER],
				[r - R_CORNER - T_XY, H_BASE],
				// inner top
				[r - T_BASE + R_CORNER + T_XY, H_BASE],
				[r - T_BASE + T_XY, H_BASE - R_CORNER]
			]);
			// lock cut out
			if ( ! isFirst ) {
				for (angle = [0:360/N_LOCK:360]) {
					color("#FF5050")
					difference() {
						LockCut(r - T_BASE, angle + ((i - 1) * 180 / N_LOCK));
						OuterLatch(r - T_BASE, angle + A_LOCK + ((i - 1) * 180 / N_LOCK));
						OuterStartLatch(r - T_BASE, angle + ((i - 1) * 180 / N_LOCK));
					}
				}
			}
		}
		// lock dilation
		if ( ! isLast ) {
			for (angle = [0:360/N_LOCK:360]) {
				color("#5050FF")
				difference() {
					Lock(r - T_XY, angle + (i * 180 / N_LOCK));
					InnerLatch(r - T_XY, angle + (i * 180 / N_LOCK));
				}
			}
		}
		// bottom
		if (isFirst && H_BOTTOM > 0) {
			cylinder(h = H_BOTTOM, r = r - R_CORNER - T_XY - EPS);
		}
		// innermost thread
		if (isFirst && H_INNER_THREAD > 0) {
			InnerThread(r, H_INNER_THREAD, P_INNER_THREAD, D_INNER_THREAD, P_INNER_THREAD / 2);
		}
		// outermost thread
		if (isLast && H_OUTER_THREAD > 0) {
			OuterThread(r, H_OUTER_THREAD, P_OUTER_THREAD, D_OUTER_THREAD, P_OUTER_THREAD / 2);
		}
	}
}

// Expand the given segment if the condition is true.
module ExpandIf(cond, i) {
	if ( cond ) {
		rotate([0, 0, (N_BASE - i) * -A_LOCK])
		translate([0, 0, (N_BASE - i) * (H_BASE - (2 * O_LOCK) - H_LOCK)])
		children();
	} else {
		children();
	}
}

difference() {
	// Final print-in-place telescope stick.
	for (i = [1:1:N_BASE]) {
		ExpandIf(D_EXTEND == 1 && i != N_BASE, i)
		Segment(R_BASE + (i * T_BASE), i, i == 1, i == N_BASE);
	}
	// Cross section cut part.
	if (C_ENABLE == 1)
	color("#FFFFFF")
	rotate([0, 0, C_ANGLE])
	translate([0, -1.5 * (R_BASE + (T_BASE * N_BASE)), -EPS])
	cube([(R_BASE + (T_BASE * N_BASE)) * 3, (R_BASE + (T_BASE * N_BASE)) * 3, H_BASE * N_BASE + 2 * EPS]);
}

/*************************************************
/ Written by Simon Löffler 2020                  /
/ https://www.printables.com/model/101344-openscad-helical-extrude-and-thread-library
/ Licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
/                                                /
/ This module lets you sweep a custom 2D Profile /
/ along a helical path.                          /
/ The profile is an array of points just like a  /
/ polygon() [[x0,z0],[x1,z1],[x2,z2],...] where  /
/ x=0 is at the radius of the helix.             /
/ You can select out of 5 different modes to     /
/ describe the helix.                            /
/                                                /
/ +------+--------+--------+                     /
/ | mode | param1 | param2 |                     /
/ +------+--------+--------+                     /
/ | 1    | pitch  | height |                     /
/ | 2    | pitch  | turns  |                     /
/ | 3    | alpha  | height |                     /
/ | 4    | alpha  | turns  |                     /
/ | 5    | turns  | height |                     /
/ +------+--------+--------+                     /
/ |  alpha is helix angle  |                     /
/ +------------------------+                     /
*************************************************/

module helix_extrude(
	profile,
	radius,
	param1,
	param2,
	mode=1,
	fn=($fn==0?36:$fn)
){
	if(mode<0||mode>5)
		echo(str("ERROR: There is no mode ",mode,"!")
	);

	if(profile==undef||len(profile)<3)
		echo(str("ERROR: profile is missing!")
	);

	if(param1==undef)
		echo(str("ERROR: param1 is missing!")
	);

	if(param2==undef)
		echo(str("ERROR: param2 is missing!")
	);

	pitch=(
		mode==1?param1:(
			mode==2?param1:(
				mode==3?radius*2*tan(param1):(
					mode==4?radius*2*tan(param1):(
						mode==5?param2/param1:(
							undef
						)
					)
				)
			)
		)
	);

	height=(
		mode==1?param2:(
			mode==2?pitch*param2:(
				mode==3?param2:(
					mode==4?pitch*param2:(
						mode==5?param2:(
							undef
						)
					)
				)
			)
		)
	);

	points=[
		for(a=[0:360/fn:height/pitch*360])(
			for(p=[0:len(profile)-1])(
				for(x=[0],y=[x],z=[1])[
					(profile[p][x]+radius)*cos(a),
					(profile[p][y]+radius)*sin(a),
					profile[p][z]+a/360*pitch
				]
			)
		)
	];

	n=len(profile);
	p=len(points);
	faces=[
		[for(point=[0:1:n-1])
			point
		],
		for(point=[0:n:p-n-1])(
			for(side=[0:n-1])(
				for(offset1=[point+side],offset2=[n])[
					offset1,
					offset1+offset2,
					(side==n-1?offset1+1:offset1+offset2+1),
					(side==n-1?offset1-offset2+1:offset1+1)
				]
			)
		),
		[for(point=[p-1:-1:p-n])
			point,
		]
	];

	polyhedron(points,faces,10);
}

```