[User]
I'm looking to create a customizable system of interlocking LEGO display pieces, and I'd like to use OpenSCAD to generate the models. I need three main types of pieces: full hexagons, half-hexagons, and veneer pieces that can be used to cover the edges.

Here are the details for each type:

**Full Hexagons (Honeycomb):**

*   I need to be able to generate a grid of hexagons, specified by the number of rows and columns.
*   The even-numbered columns should be able to be adjusted; I need options to make them taller, shorter, shifted up, or shifted down relative to odd-numbered columns.
*   The internal walls of the honeycomb should be optionally removable for a more open display, which I'll call "hollowcomb."
*   For hollowcomb models, I want an option to have a single horizontal bridge spanning the lowest level across the model, but only in "shifted down" or "taller" modes for the even columns.
*   I need two types of hexes: normal and hex with headroom, the latter adding space within the hexagon for taller items.
*   The interior height, depth, and the presence and location of a LEGO baseplate recess should be configurable, including a hole for pushing out the baseplate.
*   I need an option to add a full back panel to the hexagon with the option for a keyhole mount.
*   Each hexagon will need configurable top, top-right, bottom-right, bottom, bottom-left, and top-left connection points, each of which can be either a rail or slot or none.
*  A "slot gap" parameter should control the tightness of slot/rail connections.

**Half-Hexagons:**

*   These should be halves of the full hexagon, either the top or bottom half, as needed.
*   They should otherwise have all the same connection, baseplate, and back panel options as the full hexagons.

**Veneers:**

*   These are flat, rectangular pieces that fit the edges of the full hexes and can also have connectors.
*   The veneers need a configuration for how far they extend relative to the edge; options should be: short, one extended side, and double extension (extending the most).
*   They need to allow for either a rail or slot connector along the connecting edge.

**General Requirements:**

*   All dimensions should be parametric.
*   I'd prefer to keep it simple and not use too many libraries if possible, though I understand that some may be necessary. If you do use a library, please make sure to specify which one and which specific modules you're using, and which modules are needed from each of those files (e.g. `use <BOSL/transforms.scad>`).
*   I'd prefer the code to be clear and well-commented, making it easy for me to understand and modify.
*   The code needs to be able to scale up or down based on the required size. I'm going to be printing these with FDM.

Can you generate an OpenSCAD file (or a set of files, if that makes more sense) that implements these requirements?

[Assistant]
lego_hex_display_customizer.scad:
```
/* [Output Type] */

output_type = 0; // [0: hex/honeycomb, 1: half-hex, 2: veneer]

/* [Veneer Options] */

veneer_connector = 0; // [0: rails, 1: slots]
veneer_length = 0; // [0: short, 1: one extended side, 2: double extension]

/* [Half Hex Options] */

which_half = 0; // [0: top, 1: bottom]

/* [Hex/Honeycomb Options] */

// Number of columns
num_cols = 1;
// Number of rows
num_rows = 1;

//Whether the even columns should be taller, shorter, shifted up, or shifted down
even_column_mode = 4; // [1: taller, 2: shorter, 3: shift up, 4: shift_down]

// If hollow, the interior walls of the honeycomb will be removed.
hollow = 0; //[1: hollow, 0: full honeycomb]

// Only for hollow models: if bridge is selected, a single long bridge will be made across the lowest level. (Only use with ("shifted down" or "taller" modes)
bridge = 0; // [0: no bridge, 1: bridge]

// Type of hex to create
hex_type = 1; // [1:Normal Hex,2:Hex with Headroom]

// Height needed on the inside of the hexagon (mm)
total_interior_height = 60; // [50:200]

// Depth of the hexagon, front to back (mm)
hexagon_depth = 32; // [16:50]

// Recessed LEGO base?
LEGO_base = 1; // [1:Yes,0:No]

// LEGO recess location
LEGO_base_location = 1; // [1:Near Front,0:Center,-1:Near Back]

// Hole for pushing LEGO baseplate out (only with baseplate)?
baseplate_hole = 1; // [1:Yes,0:No]

// Back Type
back_type = 0; // [0:None,1:Full Back]

// Mount Type (only with full back)
mount_type = 1; // [0:None,1:Keyhole Mount]

/* [Rails and Slots] */

// Connector type - top
top_connector = -1; // [-1:Slot,0:None,1:Rail]
// Connector type - top right
top_right_connector = 1; // [-1:Slot,0:None,1:Rail]
// Connector type - bottom right
bottom_right_connector = -1; // [-1:Slot,0:None,1:Rail]
// Connector type - bottom
bottom_connector = 1; // [-1:Slot,0:None,1:Rail]
// Connector type - bottom left
bottom_left_connector = -1; // [-1:Slot,0:None,1:Rail]
// Connector type - top left
top_left_connector = 1; // [-1:Slot,0:None,1:Rail]

// Fit (Tight is 0.15 mm, Loose is 0.3 mm)
slot_gap = 0.3; // [0.15:Tight,0.3:Loose]

/* [Hidden] */

$fn = 30;
legoUnit = 1.6*5;
plateH = 3.2;

totH = total_interior_height;

baseD = hexagon_depth; //4*legoUnit;
t = plateH+1.6;

baseW = totH/cos(30)/2+t;
echo(baseW);
echo(2*(baseW-t)*cos(30));

module hex(h,d) {
    cylinder(h,d1=d,d2=d,$fn=6);
}

module cell() {
    difference() {
        hex(baseD,baseW*2);
        translate([0,0,-1]) hex(baseD+2,baseW*2-2*t);
    }
}

module keyhole() {
    h1 = 5;
    h2 = 9;
    hlen = 10;
    translate([0,0,-1]) hull() {
        cylinder(totH,d1=h1,d2=h1);
        translate([0,hlen,0]) cylinder(totH,d1=h1,d2=h1);
    }
    translate([0,0,-1]) cylinder(totH,d1=h2,d2=h2);
}

module partial_back(tt) {
    difference() {
        hex(tt,baseW*2-2*t);
        translate([-baseD-10,-2*baseD-13,-1]) cube([2*baseD+20,2*baseD+20,baseD+2]);
        keyhole();

    }
}

module fullBack(tt) {
    hex(tt,baseW*2-2*t);
}

module slider_old() {
    linear_extrude(baseD*1) {
        translate([-0.8,0,0]) square([1.6,1.6+1.6]);
        translate([-2,1.6,0]) square([4,1.6]);
    }
}

module slider() {
    linear_extrude(baseD*1) {
        translate([-0.8,-0.5,0]) square([1.6,1.6+1.6+0.5]);
        translate([-2,1.6,0]) square([4,1.6]);
    }
}

module slot() {
    // delta of 0.15 = original
    // delta of 0.3 = loose fit
    translate([0,0.2,-1]) linear_extrude(baseD+2) offset(delta=slot_gap) rotate([0,0,180]) {
        translate([-0.8,0,0]) square([1.6,1.6+1.6]);
        translate([-2,1.6,0]) square([4,1.6]);
    }
}

module stud() {
    cylinder(1.6,d1=4.8,d2=4.8,$fn=30);
}

module stud3x4() {
    translate([-legoUnit*1.5,-legoUnit*1,0]) {
        for (i=[0,3]) {
            for (j=[1:1]) {
                translate([i*legoUnit,j*legoUnit,0]) stud();
            }
        }
    }
}

cutoutW = 4*legoUnit+0.3;
cutoutD = 3*legoUnit+0.3;
cutoutH = plateH;

module plateCutout() {
    difference() {
        translate([-cutoutW/2,0,0]) cube([cutoutW,cutoutH+10,cutoutD]);
        // Studs
        if (LEGO_base == 1) translate([0,-0.1,cutoutD/2]) rotate([-90,0,0]) stud3x4();
    }
    // extra space at the sides
    translate([-cutoutW/2 - 2, cutoutH, 1]) minkowski() {
        cube([cutoutW + 4, 6, cutoutD - 2]);
        rotate([-90, 0, 0]) cylinder(5, r = 1);
    }
    // hole for pushing plate out
    if (baseplate_hole == 1) if (LEGO_base == 1) translate([0,5,cutoutD/2]) rotate([90,0,0]) cylinder(20,d1=5,d2=5,$fn=30);
}

module add_bridge(width){
  difference(){
  union(){
  children();
  translate([-baseW/2+t*sin(30),-baseW*cos(30),0]) cube([width, t*cos(30), baseD]);
  }
  if (LEGO_base == 1){
    translate([-baseW/2+width/2, -totH/2-plateH,LEGO_base_location*(baseD/2-cutoutD/2-legoUnit/3)+baseD/2-cutoutD/2]) plateCutout();
  }
  }
}

module display() {

    difference() {
        cell();

        // cutout for base
        if (LEGO_base == 1) translate([0,-totH/2-plateH,LEGO_base_location*(baseD/2-cutoutD/2-legoUnit/3)+baseD/2-cutoutD/2]) plateCutout();

        // slots
        if (top_connector == -1) {
            rotate([0,0,60*0]) {
                translate([baseW/6,baseW*cos(30),-0.15]) slot();
                translate([-baseW/6,baseW*cos(30),-0.15]) slot();
            }
        }
        if (top_right_connector == -1) {
            rotate([0,0,60*1]) {
                translate([baseW/6,baseW*cos(30),-0.15]) slot();
                translate([-baseW/6,baseW*cos(30),-0.15]) slot();
            }
        }
        if (bottom_right_connector == -1) {
            rotate([0,0,60*2]) {
                translate([baseW/6,baseW*cos(30),-0.15]) slot();
                translate([-baseW/6,baseW*cos(30),-0.15]) slot();
            }
        }
        if (bottom_connector == -1) {
            rotate([0,0,60*3]) {
                translate([baseW/6,baseW*cos(30),-0.15]) slot();
                translate([-baseW/6,baseW*cos(30),-0.15]) slot();
            }
        }
        if (bottom_left_connector == -1) {
            rotate([0,0,60*4]) {
                translate([baseW/6,baseW*cos(30),-0.15]) slot();
                translate([-baseW/6,baseW*cos(30),-0.15]) slot();
            }
        }
        if (top_left_connector == -1) {
            rotate([0,0,60*5]) {
                translate([baseW/6,baseW*cos(30),-0.15]) slot();
                translate([-baseW/6,baseW*cos(30),-0.15]) slot();
            }
        }
    }

    // rails
    if (top_connector == 1) {
        rotate([0,0,60*0]) {
            translate([baseW/6,baseW*cos(30),0]) slider();
            translate([-baseW/6,baseW*cos(30),0]) slider();
        }
    }
    if (top_right_connector == 1) {
        rotate([0,0,60*1]) {
            translate([baseW/6,baseW*cos(30),0]) slider();
            translate([-baseW/6,baseW*cos(30),0]) slider();
        }
    }
    if (bottom_right_connector == 1) {
        rotate([0,0,60*2]) {
            translate([baseW/6,baseW*cos(30),0]) slider();
            translate([-baseW/6,baseW*cos(30),0]) slider();
        }
    }
    if (bottom_connector == 1) {
        rotate([0,0,60*3]) {
            translate([baseW/6,baseW*cos(30),0]) slider();
            translate([-baseW/6,baseW*cos(30),0]) slider();
        }
    }
    if (bottom_left_connector == 1) {
        rotate([0,0,60*4]) {
            translate([baseW/6,baseW*cos(30),0]) slider();
            translate([-baseW/6,baseW*cos(30),0]) slider();
        }
    }
    if (top_left_connector == 1) {
        rotate([0,0,60*5]) {
            translate([baseW/6,baseW*cos(30),0]) slider();
            translate([-baseW/6,baseW*cos(30),0]) slider();
        }
    }

    // full back
    if (back_type == 1) difference() {
        fullBack(2);
        // keyhole mount
        if (mount_type == 1) translate([0,totH*0.4-10,-1]) keyhole();
    }

}

module half_hex(){
  difference() {
    display();

    translate([-50, 0, -1])
    if (which_half) {
    cube([100, 50, hexagon_depth + 2]);
    } else {
    mirror([0,1,0])
    cube([100, 50, hexagon_depth + 2]);
    }
  }
}

module full_cell(){

  module recess() {
      translate([0, 0 , hexagon_depth/2]) minkowski() {
          rotate([90, 0, 0]) cylinder(0.001, r = 10);
          //cube([baseD - 5, 0.0001, hexagon_depth - 10]);
          sphere(r = 1.0);
      }
  }

  if (hex_type == 2) {
      // hex with headroom v2
      tt = t*sin(60);
      echo(tt - 1.6 + 0.2 + slot_gap);
      difference() {
          display();
          translate([0, total_interior_height / 2, 0]) recess();
      }
  }
}

module slot_plug(connector_type){
y_trans = connector_type == -1 ? (baseW)*cos(30) : (baseW+t)*cos(30);
translate([-baseW/2,(baseW)*cos(30),0])
cube([baseW,t*cos(30),baseD]);
}

module hollow(){
  translate([0,0, 3*back_type - 1]) {
    linear_extrude(baseD + 2) polygon(points = [[baseW-t, 0],[-baseW+t, 0], [-baseW/2+2*t*sin(30),baseW*cos(30)],[-baseW+t, 2*baseW*cos(30)],[baseW-t, 2*baseW*cos(30)],[baseW/2-2*t*sin(30),baseW*cos(30)]]);
  }
}

module veneer(){

  module flat() {
    left_shift = veneer_length == 2 ? baseW/2+t*sin(30) : baseW/2+t*sin(30)-t;
    right_shift = veneer_length == 0 ? baseW/2+t*sin(30)-t :baseW/2+t*sin(30);
    difference(){
      union(){
        translate([-baseW/2-t,0,0])
        cube([baseW+2*t,t*cos(30),baseD]);
      }
      translate([left_shift,0,-0.5])
      rotate([0,0,-30])
      cube([2*t,2*t,baseD+1]);

      mirror([1,0,0])
      translate([right_shift,0,-0.5])
      rotate([0,0,-30])
      cube([2*t,2*t,baseD+1]);
    }
  }

  if (veneer_connector == 0){ // rails
    flat();
    translate([baseW/6,t*cos(30),0])
    slider();
    translate([-baseW/6,t*cos(30),0])
    slider();
  } else {

    difference() {
    flat();
    translate([baseW/6,t*cos(30),0])
    slot();
    translate([-baseW/6,t*cos(30),0])
    slot();
    }
  }

}

module honeycomb(){
  union(){
    for (i = [0:1:num_cols-1]){
      col_shift = 1.5*baseW;
      row_shift = 2*baseW*cos(30);
      max_rows = i%2 ? even_col_rows : num_rows;
      vert_shift = i%2 ? even_col_offset : 0;
      for (j = [0:1:max_rows-1]){
        translate([i*col_shift,(j+vert_shift)*row_shift,0]) display();
      }
    }
    at_honeycomb_interior_edges(){
      slot_plug();
    }
  }
}

module at_honeycomb_interior_intersections(){
    col_shift = 1.5*baseW;
    row_shift = 2*baseW*cos(30);
    for (i = [1:1:num_cols-1]){
      max_rows = i%2 ? even_col_rows : num_rows;
      vert_shift = i%2 ? even_col_offset : 0;
      neighbor_min = i%2 ? 0: even_col_offset;
      neighbor_max = !(i%2) ? even_col_rows + even_col_offset: num_rows;
      for (j = [0:1:max_rows-1]){
      echo(i, j);
      //left vertex
      if (j + vert_shift > neighbor_min &&
          j + vert_shift + 0.5 < neighbor_max)
      translate([-baseW+i*col_shift, (j+vert_shift)*row_shift,0])
        children();

      //bottom left vertex
      if (j > 0 &&
          j + vert_shift <= neighbor_max)
      color("green")
      translate([-baseW/2+i*col_shift, (j+vert_shift-0.5)*row_shift,0])
        children();
    }
  }
}

module at_honeycomb_interior_edges(){
  union(){
    col_shift = 1.5*baseW;
    row_shift = 2*baseW*cos(30);
    for (i = [0:1:num_cols-1]){
      max_rows = i%2 ? even_col_rows : num_rows;
      vert_shift = i%2 ? even_col_offset : 0;
      neighbor_min = i%2 ? 0: even_col_offset;
      neighbor_max = !(i%2) ? even_col_rows + even_col_offset: num_rows;
      for (j = [0:1:max_rows-1]){
        translate([i*col_shift,(j+vert_shift)*row_shift,0]) {
          //bottom-left edge
          if (i > 0 && j + vert_shift >= neighbor_min) rotate([0,0,120]) {
            bump = bottom_left_connector == -1? -t*cos(30) : 0;
            translate([0,bump])
            children();
            }
          //top-left edge
          if (i>0 && (j+vert_shift+0.5) < neighbor_max) rotate([0,0,60]) {
            bump = top_left_connector == -1? t*cos(30) : 0;
            translate([0,bump])
            children();
            }
          //bottom_edge
          if (j > 0) rotate([0,0,180]){
            bump = bottom_connector == -1? t*cos(30) : 0;
            translate([0,bump])
            children();
            }
        }
      }
    }
  }

}

even_col_rows = even_column_mode == 1? num_rows + 1 : even_column_mode == 2 ? num_rows - 1 : num_rows;
even_col_offset = even_column_mode == 1 || even_column_mode == 4 ? -0.5 : 0.5;
max_offset = even_column_mode == 1 || even_column_mode == 4 ? num_rows - 0.5 : num_rows - 1;

module hollowcomb(){
  difference(){
    honeycomb();
    at_honeycomb_interior_edges(){
      hollow();
    }
    at_honeycomb_interior_intersections(){
      translate([0,0, 3*back_type - 1])
        cylinder(h = baseD+2, r = t);
    }
  }
}

if (output_type == 0){ //honeycomb
  if (hollow) {
//    if (bridge) {
//      w = (floor(num_cols/2) * 3 + num_cols % 2) * baseW - 2*t*sin(30);
//      add_bridge(w) {
//      hollowcomb();
//      }
//    } else {
      hollowcomb();
//    }
  } else {
      honeycomb();
  }
} else if (output_type == 1){ // half_hex
  half_hex();
} else if (output_type == 2){ // veneer
  veneer();
//  plateCutout();
}
```


[User]
I'd like to design a custom tape measure roller, similar to a simple hand-cranked spool, but designed to be 3D-printed as a single, assembled unit. I'm picturing a central spindle that holds the tape, and an outer shell that the spindle rotates within. I need this to be parametric so I can adjust it for different sized tapes.

Specifically, I need to be able to control the following dimensions:
*   **Tape Tab Parameters:**
    *   `tab_length`: The length of the metal tab at the end of the measuring tape.
    *   `tab_thickness`: The thickness of the metal tab.
    *   `tape_width`: The width of the tape itself.
*   **Tape Parameters:**
    *   `tape_thickness`: The thickness of the tape.
    *   `tape_length`: The total length of the tape, including the leader portion beyond the measurements.
*   **Spindle Parameters:**
    *   `spindle_r`: The radius of the central spindle.
    *   `handle_r`: The radius of the handle.
    *   `handle_h`: The height of the handle
*   **Assembly/Tolerance:**
    *   `gap`: A small gap between the spindle and the shell to allow for smooth rotation after printing.
*   **Shell Parameters:**
    *   `base_thickness`: The thickness of the base of the shell.
    *   `wall_thickness`: The thickness of the outer wall of the shell.

I want the design to ensure that there is enough space for the tape to wind around the spindle, and that it's easy to print as a single part with no support. I am thinking that there needs to be some method for attaching the end of the tape. I'd like for the spindle to have a slit to hold the tape tab. And I also would like for the bottom of the shell and spindle to have a design that fits together, like stacked cylinders with different radii, to help ensure that the assembly is aligned. Finally, I want the handle to be easily grippable and smooth, so maybe a handle on a cylindrical shaft ending in a sphere.

Could you generate an OpenSCAD file for me? I'd really appreciate it! I don't need any external libraries for this, just the core OpenSCAD language.

[Assistant]
tape_measure_roller.scad:
```
/* [Tape Measure Parameters] */
// How long, in mm, the metal tab at the end of the tape is.
tab_length = 10;
// How thick (front to back) the tab is.
tab_thickness = 1.1;
// How wide is the tape in mm?
tape_width=20; // [5:1:40]

// Roughly how thick is the tape? (This is used to calculate how much space is left around the spindle for the tape to wind in.
tape_thickness = 0.4;

// How long is the tape, inclusive of any length after the markings stop.
tape_length=3150; // [100:10:6000]

/* [Spindle Parameters] */
spindle_r = 14;
handle_r = 3;
handle_h = 15;

// How much play to give the spindle. Not bad to keep this at ~ your nozzle bore
gap = 0.4;

/* [Shell Parameters] */
base_thickness = 3.0;
wall_thickness = 1.5;

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

tape_r = sqrt((tape_length*tape_thickness)/PI + spindle_r*spindle_r) + 0.5;

module spindle(){
translate([0,0, base_thickness]) {
  difference(){
  cylinder(h=tape_width+1, r=spindle_r);
  translate([0,spindle_r-tab_length,-1])
  cube([tab_thickness,tab_length+2,tape_width+3]);
  }

  translate([(spindle_r-handle_r-1), 0, 0]){

    cylinder(h = tape_width + 1 + handle_h - handle_r,r = handle_r);
    translate([0,0, tape_width + 1 + handle_h - handle_r]) sphere(r=handle_r);
  }
  }
  cylinder(h = base_thickness, r=spindle_r-base_thickness/3);
  cylinder(h = base_thickness/3, r1 = spindle_r, r2 = spindle_r- base_thickness/3);
  translate([0,0,2*base_thickness/3])
  cylinder(h = base_thickness/3, r2 = spindle_r, r1 = spindle_r- base_thickness/3);

}
spindle();

module shell(){
difference(){
  cylinder(h = tape_width+1+base_thickness + gap, r = tape_r + wall_thickness);
  translate([0,0,base_thickness]){
    cylinder(h = tape_width+2, r=tape_r);
    translate([-spindle_r,0,0])
    cube([spindle_r*2,tape_r*2,tape_width+2]);
  }
  translate([0,0,-0.1])
  cylinder(h = base_thickness + gap+0.1, r=spindle_r - base_thickness/3 + gap);
  translate([0,0,-0.1])
  cylinder(h = 0.1 + base_thickness/3, r2 = spindle_r - base_thickness/3 + gap, r1 = spindle_r + gap + 0.1);
  translate([0,0,2*base_thickness/3])
  cylinder(h = 0.1+base_thickness/3, r1 = spindle_r - base_thickness/3 + gap + 0.1, r2 = spindle_r + gap);

}
}

shell();

```


[User]
Hey, I'm looking to create a customizable wire spool holder for my Gridfinity setup, and I'd like to use OpenSCAD to make it fully parametric. I’ve seen a few designs out there, but I want to create a really flexible one from scratch.

Basically, I need a design that can hold different sizes of wire spools, and I'd like to be able to specify the overall size of the holder in terms of Gridfinity units (like 1x1, 1x2, 2x2, etc.). The core of the design should be a spindle that the spool sits on, and I'd like to control the following parameters:

*   **Spindle Diameter:** The diameter of the spindle that goes into the center of the spool.
*   **Spindle Height:** The overall height of the spindle. I'd like presets for single-height and double-height spools, but also a custom value for other heights.
*  **Gridfinity Base Size:** How many gridfinity units the design takes up along the X & Y axis (e.g. 1x1, 2x1, 2x2, etc.)
*   **Tolerance:**  A tolerance value to account for printer accuracy.
*   **Peg Diameter:** The diameter of the peg that protrudes from the bottom of the spindle (to allow stacking).
*   **Peg Internal Diameter:** The diameter of the hole that will be inside of the peg on the bottom.
*   **Peg Internal Height:** How tall the inner cylinder is on the bottom peg.
*   **Peg Height:** The total peg height.
*   **Peg Chamfer:** A chamfer value for the inner peg.
*   **Fillet Radius:** The radius of the fillets of the triangle that makes the base of the spindle.

I'd like the spindle to have a triangular cross-section with rounded corners, with the triangle being inscribed in a circle with the `spindle_diameter`. Also, I'd like to include Gridfinity magnet holes and screw holes for stacking (like M3). I’m thinking the magnet holes should be the standard 6mm diameter and 2mm deep.

So, could you generate the OpenSCAD files (or maybe a single file if that's easier) to do this? I'm thinking of something with a modular design. Ideally, I’d like to be able to change the Gridfinity size, spindle, and peg parameters, as well as the tolerance. I think that should cover the basics of it. I would prefer if you do not use any external libraries such as BOSL, MCAD, or BOLTS.

[Assistant]
spindle.scad:
```
//This will be the diameter of the spindle
spindle_diameter = 25.45;
//This will be the tolerance between parts, set it to your printers tolerance lever (default = 0.2)
tolerance = 0.2;
//Select the spindle height for the spindle or use custom to use the size specified by spindle_height
spindle_size_preset = 0; //[0:Single, 1:Double, 2:Custom]
//Set a custom spindle height (only works if spindle_size_preset is set to custom)
spindle_height = 10.10;

//These are the constants not to be changed unless needed
/* [Hidden] */
SINGLE_HEIGHT_SPINDLE = 30.3;
DOUBLE_HEIGHT_SPINDLE = 72.3;
SPINDLE_FILLET_RADIUS = 5;
PEG_DIAMETER = 7.9;
PEG_INNER_DIAMETER = 7.5;
PEG_INTERNAL_HEIGHT = 5;
PEG_HEIGHT = 2;
PEG_CHAMFER = 0.5;

module makePart(spindle_height) {
    module equ_triangle(side_length,corner_radius,triangle_height) {

        translate([0,corner_radius,0]) {
            hull() {
                cylinder(r=corner_radius,h=triangle_height, $fn=100);
                rotate([0,0,60])translate([(side_length-corner_radius*2),0,0])cylinder(r=corner_radius,h=triangle_height, $fn=100);
                rotate([0,0,120])translate([(side_length-corner_radius*2),0,0])cylinder(r=corner_radius,h=triangle_height, $fn=100);
            }
        }
    }

    //remove the peg from the bottom
    {
        inscribed_length = 2*((spindle_diameter-tolerance)/2)*cos(30);
        echo("The inscribed triangle side length will be: ", inscribed_length);
        difference() {
        equ_triangle(inscribed_length, SPINDLE_FILLET_RADIUS, spindle_height);
            translate([0,((spindle_diameter-tolerance)/2-0.775),0]) {
                cylinder(d=PEG_INNER_DIAMETER, h=PEG_INTERNAL_HEIGHT, $fn=100);
                translate([0,0,PEG_INTERNAL_HEIGHT]) {
                    cylinder(d1=PEG_INNER_DIAMETER, d2=0, h=PEG_INTERNAL_HEIGHT, $fn=100);
                }
            }
        }

        translate([0,((spindle_diameter-tolerance)/2-0.775),spindle_height]) {
            cylinder(d=PEG_DIAMETER - tolerance, h=PEG_HEIGHT, $fn=100);
        }

            translate([15,PEG_DIAMETER/2,0]) {
            cylinder(d=PEG_DIAMETER - tolerance, h=PEG_HEIGHT, $fn=100);
            translate([0,0,PEG_HEIGHT]) {
                cylinder(d=(PEG_INNER_DIAMETER - tolerance), h=PEG_HEIGHT, $fn=100);
                translate([0,0,PEG_HEIGHT]) {
                cylinder(d1=(PEG_INNER_DIAMETER - tolerance), d2=(PEG_INNER_DIAMETER - tolerance - PEG_CHAMFER), h=PEG_HEIGHT, $fn=100);
                }
             }
        }
    }
}

//Set the spindle heigh if a preset is used
if (spindle_size_preset == 0) {
    makePart(SINGLE_HEIGHT_SPINDLE);
}
else if (spindle_size_preset == 1) {
    makePart(DOUBLE_HEIGHT_SPINDLE);
}
else {
    makePart(spindle_height);
}
```


[User]
I'm looking to create a vacuum cleaner attachment with a customizable design, and I'd like it to be parametric so I can adjust the dimensions to fit different hoses and accessories. I need to be able to specify the inner diameter of the wide end, the outer diameter of the narrow end, and the lengths of each of the three sections: the wide end, the narrow end, and the middle transition section. Also, a wall thickness parameter is important, and I’d like to specify a fillet radius for the outer edges.

I also want the option to include a honeycomb grid at the narrow end to prevent sucking up small parts. I need to be able to control the honeycomb grid's size (number of cells), the cell diameter, the wall width of the honeycomb cells, and the height of the cells, too. I'd like a simple yes/no toggle for whether to include the honeycomb grid at all. If the user disables the honeycomb, then the object will be a simple hose adapter with no grid.

It needs to be easy to customize all of these parameters using sliders. Can you generate the necessary OpenSCAD code for me? I don't have any existing code, so this needs to be a complete solution. I do not need any external libraries, either.

[Assistant]
VacuumGuard-v1.1.scad:
```
/* Vacuum Guard / Hose Adapter
   by Lyl3

Create a vacuum cleaner attachment with a honeycomb grid barrier to prevent
small parts from being suck into the vacuum cleaner. Can also create a model
without the grid, for making hose adapters.

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

V0.1 - basic functioning code
V0.2 - added the honeycomb
V0.3 - reversed the vertical placement of the parts so that the honeycomb is at the bottom
V0.4 - Added parameters for each of the segments lengths
V0.5 - Cleaned up, sanitized, and provided descriptions for the parameters
V0.6 - added a parameter for the diameter of the narrow end
V0.7 - made the honeycomb grid optional
V0.8 - increased diameter of minkowski sphere to better ensure the model is at Z=0
V1.0 - ready for publication
V1.1 - fixed so honeycomb won't be created higher than the length of that end
*/

/* [Basic Parameters] */

// Enter the inner diameter of the wide end of the model (mm).
Inner_wide_diameter = 31; // [10:0.1:100]

// Enter the outer diameter of the narrow end of the model (mm). If this is not a size adapter, it should probably be slightly less than inner diameter of the wide end for a good fit.
Outer_narrow_diameter = 30.3;  // [10:0.1:100]

// Enter the length of the wide end (mm).
Length_wide_end = 25; // [4:0.1:100]

// Enter the length of the narrow end (mm).
Length_narrow_end = 10; // [4:0.1:100]

// Enter the length of the middle section (mm).
Length_middle_section = 25.0; // [2:0.1:100]

// Enter the wall thickness (mm).
Wall_thickness = 1.2; // [0.4:0.1:5]

// Enter the fillet radius (mm). This value should not be greater than the wall thickness, and if it is then the wall thickness will be used as the radius.
Fillet_radius = 1.0; // [0.5:0.1:2]

filletRadius = min(Fillet_radius,Wall_thickness);

Wall_width = Wall_thickness - filletRadius*2;
filletHeight = filletRadius+0.1;

/* [Honeycomb Parameters] */

// Create a honeygrid as a barrier for small parts?

Make_honeycomb = 1; // [0:no, 1:yes ]

// Enter the number of honeycomb cells on each side of the honeycomb grid. This should be a large enough number that it extends all the way around the perimeter of the narrow end.
Honeycomb_grid_size=6; // [5:20]

// Enter the diameter of the honeycomb cells (mm).
Honeycomb_diameter=4; // [2:0.1:10]

// Enter the width of the honeycomb cell walls (mm).
Honeycomb_wall_width=0.4; // [0.4:0.1:5]

// Enter the height of the honeycomb cells (mm).
Honeycomb_height = 4; // [3:10]

/* [Hidden] */

$fn=100;
fudge = 0.03;
innerWideDiameter = Inner_wide_diameter;
outerNarrowDiameter = Outer_narrow_diameter;
makeHoneycomb = (Make_honeycomb==0)?false:true;
honeycombHeight = Honeycomb_height;

  union () {

    // Make the lower part with the outer diameter as specified
    color("green") difference () {
      union() {
        translate ([0, 0, filletHeight])
          cylinder (d=outerNarrowDiameter, h=Length_narrow_end-filletHeight);
        translate ([0, 0, filletRadius])
        minkowski () {
          cylinder (d=outerNarrowDiameter-2*filletRadius, h=filletHeight);
          sphere (r=filletRadius,$fn=40);
        }
      }
      translate ([0, 0,-fudge/2]) cylinder (d=outerNarrowDiameter-2*Wall_thickness, h=Length_narrow_end+fudge);
    }

    // Make the transition part between the two diameters
    translate ([0, 0, Length_narrow_end])
    color("lightgreen") difference () {
      cylinder (d1=outerNarrowDiameter, d2=innerWideDiameter+2*Wall_thickness, h=Length_middle_section);
      translate ([0, 0, -fudge/2]) cylinder (d1=outerNarrowDiameter-2*Wall_thickness, d2=innerWideDiameter, h=Length_middle_section+fudge);
    }

    // Make the upper part with the inner diameter as specified
    translate ([0,0, Length_narrow_end+Length_middle_section])
    color("green") difference () {
      union() {
        translate ([0, 0, 0])
          cylinder (d=innerWideDiameter+2*Wall_thickness, h=Length_wide_end-filletHeight);
        translate ([0, 0, Length_wide_end-filletHeight-filletRadius])
        minkowski () {
          cylinder (d=innerWideDiameter+2*Wall_thickness-2*filletRadius, h=filletHeight);
          sphere (r=filletRadius,$fn=40);
        }
      }
      translate ([0, 0, -fudge/2]) cylinder (d=innerWideDiameter, h=Length_wide_end+fudge);

    }
    // Make the honeycomb barrier
    if (makeHoneycomb) {
      color("gold") intersection() {
        cylinder(h=min(honeycombHeight,Length_narrow_end),d=outerNarrowDiameter-2*Wall_thickness+fudge);
        linear_extrude(honeycombHeight)
          hexGrid(gridSize=Honeycomb_grid_size, diameter=Honeycomb_diameter, wallWidth=Honeycomb_wall_width);
      }
    }
  }

module hexCell (xNum=0, yNum=0, diameter=10, wallWidth=1, isFilled=false) {
  // Spacing is based on minimal diameter
  XDistance=(diameter+wallWidth);
  YDistance = XDistance * sqrt(3)/2;
//  echo (diameter,XDistance,YDistance);

  translate([(xNum+(abs(yNum)%2)*0.5)*XDistance,yNum*YDistance,0])
  rotate([0,0,30]) difference() {
    circle(d=(diameter+2*wallWidth)*2/sqrt(3),$fn=6);
    if (isFilled) {} else circle(d=diameter*2/sqrt(3),$fn=6);
  }
}

module hexGrid (gridSize=2, diameter, wallWidth, isFilled) {
  length = gridSize - 1;
  for (yHex=[-length:length]) {
    for (xHex=[-length+floor(abs(yHex)*0.5):length-ceil(abs(yHex)*0.5)]) {
      hexCell (xNum=xHex, yNum=yHex, diameter=diameter, wallWidth=wallWidth, isFilled=isFilled);
    }
  }
}

```


[User]
I want to create a customizable mustache comb using OpenSCAD. I need it to be parametric so I can adjust different features. Here's what I'm thinking:

**Overall Structure:**

*   The comb will have a main body with teeth for combing, a handle, and a profile section connecting them.
*   There should be a way to switch between three different main body profiles. The first profile should be "none" which means there is no additional profile or shape. The second profile should be a "wing" shaped profile that makes the connection between the handle and the teeth have a smooth curved shape. Finally, the last profile should be "full_3d", which means the entire comb is a 3D shape rather than an extrusion of a 2D shape.
*   The comb body can have three main 2D shapes: a bow shape, a rectangle, or a rounded rectangle. These will affect how the comb looks, especially when there is no profile applied.
*   I also need to add a wedge shape to the back of the handle section. There should be two options for this. One is "teeth" which adds a wedge with jagged edges to the back of the handle and one is "full", which creates a full wedge. Both of these are 2D shapes that are extruded.

**Parameters:**

*   `max_height`: The maximum height of the comb in millimeters.
*   `min_height`: The minimum height of the wedge at the thinnest part in millimeters.
*   `teeth_depth`: The depth of the teeth section in millimeters.
*   `handle_depth`: The depth of the handle in millimeters.
*  `handle_teeth_gap`: The gap between the teeth section and the handle section in millimeters.
*   `tooth_width`: The width of each tooth in millimeters.
*   `tooth_gap`: The gap between two teeth in millimeters.
*   `teeth_side_margin`: The margin between the teeth section and the outline in millimeters.
*   `number_of_teeth`: The number of teeth.
*   `corner_radius`: Radius of the corners (only used with the rounded rectangle shape). This cannot be larger than the `teeth_side_margin`. And corner should not be possible to select when using a rectangle shape.
*   `emboss_text`: A boolean to control whether the text is embossed or debossed.
*   `personal_text`: The text to be displayed on the comb.
*   `text_teeth_margin`: The margin between the teeth section and the text in millimeters.
*   `font_size`: The font size of the text.
*   `font_family`: A font family to use (e.g., "Arial", "Helvetica", "lemon_milk").
*   `quality`: A number that controls the number of segments to use for circles and curves. Higher values will result in higher quality.

**Specific Details:**

*   The teeth should be created by using `square` and `circle` primitives. Make sure the ends of each tooth have a rounded profile with a circle primitive.
*   The text should be added to the body, away from the teeth. Use the `text()` module and apply `linear_extrude`. The location should be based on the `text_teeth_margin` parameter.
*   The overall length of the comb should be calculated using a combination of parameters (`teeth_depth`, `handle_depth`, `handle_teeth_gap`).
*   The overall width of the comb should be calculated using a combination of parameters (`teeth_side_margin`, `number_of_teeth`, `tooth_width`, `tooth_gap`).
*   Use linear extrusions to create the main body, teeth, and text.
*   For the "full_3d" profile, create the body using a combination of `hull` and `difference` operators. Use `bow_3d` to create the main shape.
*   Use `polygon` for the wedge profiles.
*   Use `offset` to create the chamfers for the rounded rectangle.
*   Use `resize` to control circle sizes when creating the oval shapes.

I'm envisioning this as a single .scad file. Let me know if you have any questions. I expect that I should be able to use this code directly.

[Assistant]
Captain_mustache_customizable_comb.scad:
```
// Choose between these profiles
profile = "none"; // [none,wing,full_3d]

// Choose between these wedge profiles
wedge_profile = "teeth"; // [none,teeth,full]

// Choose between these shapes
shape = "bow"; // [bow,rectangle,rounded_rectangle]

// Max height in Millimeter
max_height = 6;

// Min height in Millimeter
min_height = 1;

// The depth of the teeth section in Millimeter
teeth_depth = 20;

// The depth of the handle in Millimeter
handle_depth = 25;

// Gap between teeth_depth and handle_depth in Millimeter.
handle_teeth_gap = 5;

// The width of each tooth in Millimeter
tooth_width = 1;

// The gap between two teeth in Millimeter
tooth_gap = 1.5;

// The margin between the teeth section and the outline in Millimeter
teeth_side_margin = 5;

// Number of teeth.
number_of_teeth = 60; //[1:200]

// Radius of the corner in Millimeter. Can't be more than teeth_side_margin! Corner can't be used together with rectangle shape!
corner_radius = 8;

// Control whether the text is embossed or debossed
emboss_text = true;

// Type in your personal text.
personal_text = "CAPTAIN MUSTACHE";

// The margin between the teeth section and the text in Millimeter
text_teeth_margin = 2;

// The font size
font_size = 9;  //[4:0.5:20]

// You can choose a font
font_family = "lemon_milk";

// You can reduce the quality to 8-16 to create a low poly effect.
quality = 32; //[1:200]

/* [Hidden] */
$fn = quality;
extra1 = 1;
extra2 = extra1 * 2;
total_depth = teeth_depth + handle_teeth_gap + handle_depth;
total_width = teeth_side_margin * 2 + number_of_teeth * (tooth_width + tooth_gap) + tooth_gap;
corner = corner_radius < teeth_side_margin ? corner_radius : teeth_side_margin;

echo(total_width);
echo(total_depth);

color("LightSeaGreen") comb();

module comb() {
    difference() {
        translate([0, total_depth - handle_depth, 0]) {
            rotate([0, 0, 270]) {
                translate([0, 0, 0]) {
                    if(profile == "wing") {
                        wing_profile_comb();
                    } else if(profile == "full_3d") {
                        full_3d_profile_comb();
                    } else if(profile == "none") {
                        no_profile_comb();
                    }
                }
            }
        }

        translate([total_width / 2 - 1, teeth_depth + 2 + text_teeth_margin, max_height / 2]) {
            if (emboss_text) {

                linear_extrude(height = - 2, convexity = 20) {
                    text(text = personal_text, font = font_family, size = font_size, halign = "center", valign = "bottom", spacing = 0.96);
                }
            } else {
                linear_extrude(height = max_height + 2, convexity = 20) {
                    text(text = personal_text, font = font_family, size = font_size, halign = "center", valign = "bottom", spacing = 0.96);
                }

            }
        }

        translate([total_width + extra1, handle_teeth_gap + teeth_depth, 0]) {
            rotate([270, 180, 90]) {
                linear_extrude(height = total_width + extra2, convexity = 50) {
                    if(wedge_profile == "teeth") {
                        teeth_wedge_profile_2d();
                    } else if(wedge_profile == "full") {
                        full_wedge_profile_2d();
                    }
                }
            }
        }
    }
}

module no_profile_comb() {
    linear_extrude(height = max_height, convexity=50) {
        comb_2d();
    }
}

module wing_profile_comb() {
   intersection()  {
        no_profile_comb();
        translate([handle_teeth_gap, total_width, 0]) {
            rotate([90, 0, 0]) {
                linear_extrude(height = total_width, convexity=3) {
                    wing_profile_2d();
                }
            }
        }
    }
}

module full_3d_profile_comb() {
    difference() {
        union() {
            translate([0, total_width / 2, 0]) {
                bow_3d(handle_depth, total_width, max_height);

            }
            teeth_section_3d();
        }
        translate([handle_teeth_gap, teeth_side_margin, -extra1]) {
            cube([teeth_depth + extra1, total_width - teeth_side_margin * 2, max_height + extra2]);
        }

    }
    intersection() {
        teeth_section_3d();
        translate([handle_teeth_gap, teeth_side_margin + tooth_gap, 0]) {
            teeth_3d();
        }
    }
}

module comb_2d() {
    if(shape == "bow") {
        translate([0, total_width / 2]) {
            bow_2d();
        }
    } else if(shape == "rectangle") {
        translate([-handle_depth, 0]) {
            square([handle_depth, total_width]);
        }
    } else if(shape == "rounded_rectangle") {
        translate([-handle_depth, 0]) {
            chamfer(r = corner) {
                square([handle_depth, total_width]);
            }
            translate([handle_depth - corner, 0]) {
                square([corner, total_width]);
            }
        }
    }

    difference() {
        union() {
            if(shape == "rectangle") {
                square([teeth_depth + handle_teeth_gap, total_width]);
            } else {
                chamfer(r = corner) {
                    square([teeth_depth + handle_teeth_gap, total_width]);
                }
                square([corner, total_width]);
            }
        }
        translate([handle_teeth_gap, teeth_side_margin]) {
            square([teeth_depth + extra1, total_width - teeth_side_margin * 2]);
        }
    }

    translate([handle_teeth_gap, teeth_side_margin + tooth_gap]) {
        teeth_2d();
    }
}

module teeth_section_3d() {
    hull() {
        translate([0, total_width / 2, 0]) {
            rotate([0, 0, 180]) {
                bow_3d(handle_teeth_gap + teeth_depth, total_width, max_height);
            }
        }
        if(shape == "rectangle") {
            translate([handle_teeth_gap + teeth_depth - extra1, 0, 0]) {
                cube([extra1,extra1,.2]);
            }

            translate([handle_teeth_gap + teeth_depth - extra1, total_width - extra1, 0]) {
                cube([extra1,extra1,.2]);
            }
        } else {
            translate([handle_teeth_gap + teeth_depth - corner, corner, 0]) {
                cylinder(r = corner, h = .2);
            }
            translate([handle_teeth_gap + teeth_depth - corner, total_width - corner, 0]) {
                cylinder(r = corner, h = .2);
            }
        }
    }
}

module bow_2d() {
    difference() {
        oval_2d(handle_depth * 2, total_width);
        translate([0, -total_width / 2 - extra1]) {
            square([handle_depth + extra1, total_width + extra2]);
        }
    }
}

module bow_3d(depth, width, height) {
    difference() {
        oval_3d(depth * 2, width, 2 * height);
        translate([0, -width / 2 - extra1, - height - extra1]) {
            cube([depth + extra1, width + extra2, 2 * height + extra2]);
        }
        translate([-depth, -width / 2 - extra1, -height - extra1]) {
            cube([depth + extra2, width + extra2, height + extra1]);
        }
    }
}

module teeth_2d() {
    for(tooth = [0:number_of_teeth - 1]) {
        translate([0, (tooth_gap + tooth_width) * tooth]) {
            square([teeth_depth - tooth_width / 2, tooth_width]);
            translate([teeth_depth - tooth_width / 2, tooth_width / 2]) {
                circle(r = tooth_width / 2, $fn=12);
            }
            translate([0, tooth_width / 2]) {
                circle(r = (tooth_width + tooth_gap - .1) / 2, $fn = 4);
            }
        }
    }
}

module teeth_3d() {
    translate([0, 0, -1]) {
        linear_extrude(height = max_height + 2, convexity=50) {
            teeth_2d();
        }
    }
}

module wing_profile_2d() {
    difference() {
        union() {
            difference() {
                oval_2d((total_depth - teeth_depth) * 2, max_height * 2);
                translate([0, -max_height]) {
                    square([total_depth - teeth_depth, max_height * 2]);
                }
            }
            difference() {
                oval_2d(teeth_depth * 2, max_height * 2);
                translate([-teeth_depth, -max_height]) {
                    square([teeth_depth, max_height * 2]);
                }
            }
        }
        translate([-teeth_depth -handle_teeth_gap - extra1, -max_height - extra1]) {
            square([total_depth + extra2, max_height + extra1]);
        }
    }
}

module teeth_wedge_profile_2d() {
    polygon(points=[[handle_teeth_gap, max_height], [handle_teeth_gap, max_height + extra1], [handle_teeth_gap + teeth_depth + extra1, max_height + extra1], [handle_teeth_gap + teeth_depth + extra1, min_height], [handle_teeth_gap + teeth_depth, min_height]]);
}

module full_wedge_profile_2d() {
    pos=-handle_depth - handle_teeth_gap;
    polygon(points=[[-handle_depth, max_height], [-handle_depth, max_height + extra1], [ teeth_depth + handle_teeth_gap + extra1, max_height + extra1], [teeth_depth + handle_teeth_gap + extra1, min_height], [pos + total_depth + handle_teeth_gap, min_height]]);
}

module oval_2d(d1, d2) {
    resize([d1, d2]) {
        circle($fn = quality);
    }
}

module oval_3d(d1, d2, h) {
    resize([d1, d2, h]) {
        sphere($fn = quality);
    }
}

module chamfer(r) {
    offset(r = r) {
        offset(delta = -r) {
            children();
        }
    }
}
```


[User]
I'm looking to generate an OpenSCAD model for a customizable wrench. I need it to be parametric, allowing me to adjust several key dimensions and features. I'd like to specify:

*   **Tool Height:** The overall thickness or height of the wrench.
*   **Object Diameter:** The diameter of the object the wrench is intended to grip. This will define the inner circle of the wrench's grip.
*   **Tolerance:** A small amount of clearance added to the grip, likely to allow for printing variation.
*   **Number of Segments:** The number of segments to use when creating circles.
*   **Tooth Shape:**  The shape of the teeth on the inner circumference of the wrench, allowing for round, square, rectangular, hexagonal, or triangular shapes.
*   **Number of Teeth:** How many teeth are around the gripping surface.
*   **Tooth Depth:** (Square teeth only) How far the square teeth should project inward.
*   **Tooth Width:** How wide the teeth should be.
*   **Fillet Radius:** The size of the fillets that are added at various points on the wrench.
*   **Print Diameter Placement:** To have the option of printing the specified diameter of the object the wrench is sized for, either on the surface, subsurface or none.

Additionally, I want the design to include the following:

*   The wrench should have a handle that extends from the main circular grip part. The length and width of this handle should be calculated and configurable based on the object diameter and wrench thickness.
*   The handle should have fillets on the edges that meet the main circle.
*   I would like the ability to print the diameter size on the wrench body, using the 'printDiameter' setting, on surface or sub surface.

I am not planning to use any external libraries, just built-in OpenSCAD features.

Could you generate the necessary OpenSCAD code for me?

[Assistant]
wrench.scad:
```
// ToolHeight
toolHeight  = 12.5;             // [5:0.1:35]

// Largest Diameter of the object. (square => diagonal)
diameterObject=26.7;            // [5:0.1:70]

// Tolerance (half Nozzle diameter printer)
tolerance=0.2;           // [0.05:0.01:0.40]

// Number of Segments in a circle
segments = 100;                 // [50:1:150]

// Toothe shape: round, square
toothShape="Cylinder";             // [Cylinder,Rectangular,Square,Hexagon, Triangle]

// Serrated edge, number of teeth, all around
noTeeth = 80;                   // [3:1:256]

// Tooth depth (square only)
depth_Tooth = 2;                 // [0.2:0.1:10]

// Tooth width
width_Tooth = 0.8;               // [0.2:0.1:5]

// Fillet radius
filletRadius = 2;               // [0:0.2:5]

// Diameter printed on surface
printDiameter = "OnSurface";    // [OnSurface,None,SubSurface]

module customizerBreak(){};     // None tunable parameters from here on.

depthTooth = depth_Tooth + tolerance;
widthTooth = width_Tooth + 2*tolerance;

radius = (diameterObject / 2);
scaleXyPercentage = diameterObject / 26.0 ;

maxThickness = 12;
minThickness = 8;
thickness = min( maxThickness, minThickness + ( toothShape == "Cylinder" || toothShape == "Rectangular" ? depthTooth : 0));

innerRadius = radius + tolerance;
outerRadius = radius + thickness;

legX = min(40 * scaleXyPercentage, 50);   // Maximal 50
legY = 3 * thickness;
legCut = thickness;
fontSize = 5 * thickness/maxThickness;

halfToolHeight = 0.5 * toolHeight;
halfLegX = 0.5 * legX;
halfLegY = 0.5 * legY;

echo("");
echo(str("scaleXyPercentage ",scaleXyPercentage));
echo(str("fontSize ",fontSize));
echo(str("depthTooth ",depthTooth));
echo(str("widthTooth ",widthTooth));
wrench();
// circularFillet(innerRadius,180, "True");
// translate([0,0,-toolHeight/2 + filletRadius])circularFillet(innerRadius,180,"True");

echo("");

module wrench(){
    difference(){
        union(){
            difference(){
                cylinder(h=toolHeight, r=outerRadius, center=true, $fn=segments);
                translate([0,0,toolHeight/2 - filletRadius])circularFillet(outerRadius,0);
                translate([0,0,-toolHeight/2 + filletRadius])circularFillet(outerRadius,180);
            }
            difference(){
                translate([legX*0.5 + innerRadius,0,0]) cube([legX,legY,toolHeight], center=true);

                translate([innerRadius,      +halfLegY-filletRadius, -halfToolHeight+filletRadius]) linearFillet(0,90);
                translate([legX+innerRadius, -halfLegY+filletRadius, -halfToolHeight+filletRadius]) linearFillet(180,90);
                translate([innerRadius,      +halfLegY-filletRadius, +halfToolHeight-filletRadius]) linearFillet(90,0,90);
                translate([legX+innerRadius, -halfLegY+filletRadius, +halfToolHeight-filletRadius]) linearFillet(90,0,270);

                translate([legX+innerRadius-filletRadius, +halfLegY-filletRadius, -halfToolHeight]) linearFillet(0,0);
                translate([legX+innerRadius-filletRadius, -halfLegY+filletRadius, -halfToolHeight]) linearFillet(0,0,270);

                translate([legX+innerRadius-filletRadius, +halfLegX, +halfToolHeight-filletRadius]) linearFillet(90,0);
                translate([legX+innerRadius-filletRadius, -halfLegX, -halfToolHeight+filletRadius]) linearFillet(-90,0,0);
            }
            addText(inUnion = "True");  // When OnSurface
        }
        if (toothShape=="Cylinder" || toothShape=="Rectangular"){
            translate([0,0,-0.5]) cylinder(h=toolHeight+2, r=innerRadius, center=true, $fn=segments);
            serratedEdge();
            translate([0,0,-toolHeight/2 + filletRadius])circularFillet(innerRadius,180,"True");
        } else if (toothShape=="Square") {
            xy = sqrt(2.0) * innerRadius;
            rotate([0,0,45]) cube([xy, xy, toolHeight+1], center=true);
        } else if (toothShape=="Hexagon") {
            rotate([0,0,60]) cylinder(h=toolHeight+1, r=innerRadius, center=true, $fn=6);
        } else if (toothShape=="Triangle") {
            rotate([0,0,60]) cylinder(h=toolHeight+1, r=innerRadius, center=true, $fn=3);
        }
        translate([legX*0.5 +innerRadius,0,-0.5]) cube([legX+innerRadius,legCut,toolHeight+2], center=true);
        addText(inUnion = "False");    // When SubSurface
    }
}

module addText(inUnion){
    textHeight = 1.0;
    if ((printDiameter != "None") &&
        ((printDiameter == "OnSurface" && inUnion == "True") ||
         (printDiameter == "SubSurface" && inUnion == "False")) ){

        translate([innerRadius, 0.6*legCut, halfToolHeight - 0.5 * textHeight])
            color("black")
                linear_extrude(textHeight, center=false, convexity=10, twist=0, slices=20, scale=1.0, $fn=segments)
                    text(str("   ",chr(216)," ",diameterObject), size=fontSize, font="Arial:style=Bold");
    }
}

module serratedEdge(){
    stepDegrees = 360 / noTeeth;
    circumferenceObject = 2 * innerRadius * PI;
    circumferenceSteps = noTeeth*(widthTooth+0.2) + widthTooth;

    startAt = 90 - 0.5 * stepDegrees;  // start at the x-axis with optimal fit.
    stopAt  = startAt + 360;
    echo(str("circumferenceObject ",circumferenceObject));
    echo(str("circumferenceSteps ",circumferenceSteps));

    color((circumferenceSteps > circumferenceObject) ? "red" : "green"){
        for(i=[startAt:stepDegrees:stopAt]){
            angle = i % 360;
            x = innerRadius * sin(angle);
            y = innerRadius * cos(angle);
            translate([x,y,0])
                if (toothShape=="Cylinder"){
                    cylinder(h=toolHeight+1, d=widthTooth, center=true, $fn=segments);
                } else {
                    rotate([0,0,-angle]) cube([widthTooth,2*depthTooth,toolHeight+1], center=true);
                }
        }
    }
}

module circularFillet(diameter, angleX, inner="False", angleY=0, angleZ=0){
    radiusFillet = (inner == "False") ? -filletRadius : -0.1;
    rotate([angleX,angleY,angleZ])
        rotate_extrude(angle=360, convexity=10,  $fn=segments)
            translate([diameter + radiusFillet,0,0])
                    difference(){
                        square(filletRadius+0.2);
                        if (inner == "False"){
                            circle(filletRadius, $fn=segments);
                        } else {
                            translate([filletRadius+0.2,0,0])circle(filletRadius, $fn=segments);
                        }
                    }
}

module linearFillet(angleX, angleY=0, angleZ=0){
    rotate([angleX,angleY,angleZ])
        linear_extrude(legX, center=false, convexity=10, twist=0, slices=20, scale=1.0, $fn=segments)
            difference(){
                square(filletRadius+0.01);
                circle(filletRadius, $fn=segments);
            }
}

```


[User]
I'd like to create a parametric ruler that I can print in multiple colors using a filament swap. The design should have the following characteristics:

*   **Overall Shape:** The ruler should be a rectangular prism with a beveled edge. The main body should be a specified width, depth, and base thickness, with the beveled edge having a minimum thickness.
*   **Units:** I'd like to be able to switch between metric (millimeters) and imperial (inches) units. In metric mode, the primary divisions will be in centimeters, with half-cm and mm marks as well. For inches, the divisions will be in fractions of an inch (specify the number of subdivisions) (e.g., 1/12 or 1/8).
*   **Markings:**
    *   There should be a series of tick marks along the top edge, with varying lengths and heights to indicate different unit increments (e.g., mm, half-cm, cm in metric; fractions of inch in imperial).
    *   The tick marks should be extruded above the surface so that they can be printed in different colors from the main body of the ruler. The height of these marks should be a parametric value.
    *   For each centimeter (or inch) there should be a numeric label extruded above the surface as well, to indicate the value of that division.
*   **Custom Label:** I want to include a customizable text label on the ruler, also extruded for a different color. This label should be configurable with its font, size, spacing and text.
*   **Parameters:** The design should be fully parametric, allowing me to specify:
    *   Ruler width (mm or lines)
    *   Ruler depth (mm)
    *   Ruler base thickness (mm)
    *   Minimum thickness at the beveled edge (mm)
    *   Font size for numeric labels
    *   Inch mode (boolean)
    *   Relative heights of the different markings (mm) and text elements, such as 1cm marks, half-cm marks, 1mm marks, digits and the custom label.
    *  Subdivisions of an inch in imperial mode.
    *   Custom text for the label
    *   Space between letters in the custom label
    *   Font size for the custom label
    *   Font name and style for the custom label
    *   I'd like to be able to specify how many tick marks are to be considered bold and of which type (e.g. a long mark on every 5 cm, an intermediate mark on 2.5 and a small mark on each cm).
*   **Implementation:** I’d like the design to be implemented using OpenSCAD.

I think it would be good for the main structure of the ruler (without tick marks or text) to be generated in one part of the design, and the tick marks and text as separate modules. This way they could be positioned properly in a later stage.

Could you please generate the OpenSCAD file(s) to accomplish this?

[Assistant]
ruler.scad:
```
/* [Ruler dimensions (mm or lines)] */

// ruler width
ww=155; // [55:10:305]

// ruler depth
dd=35;

// ruler base thickness
hh=1.2;

// thickness near the edge
minh=0.4;

// digits size
font_size=5;

// inch mode
inch=false;

/* [Relative height of marks/labels above top surface for 2nd color] */

// 1cm marks extra height
extra_h_1cm = 0.6;

// half-cm marks extra height
extra_h_5mm = 0.4;

// 1mm marks extra height
extra_h_1mm = 0.2;

// digits extra height
extra_h_digits = 0.6;

// your label extra height
extra_h_label = 0.3;

/* [Your custom label] */

// label
custom_text = "Don't worry, I can print a new one!";

// space between letters
text_spacing = 0.94;

// font size
label_font_size=5.5;

// font name
label_font_name = "Times New Roman:style=Italic";

/* [Hidden] */
mils=[
	// [width, length, height]
	[0.4, 3, hh+extra_h_1mm],
	[0.6, 5, hh+extra_h_5mm],
	[0.7, 6, hh+extra_h_5mm],
	[0.8, 7.5, hh+extra_h_1cm]

]; offset_from_edge=0.2;

// inch subdivisions
inch_divisions=12;

// bold marks
bolddivs = !inch ? ([5, PI, 10]) : [2, 6, inch_divisions];

dtop=22; // depth of the top flat face
$fn=40; // label detail

maxh = max([extra_h_1cm, extra_h_5mm, extra_h_1mm, extra_h_digits, extra_h_label]) + hh;

module meat(params) {
	w = params[0];
	d = params[1];
	h = params[2];
	label_text = params[3];
	part = params[4];

	mm_per_division = !inch? 1 : (25.4/inch_divisions);
	divs = !inch? 10 : inch_divisions;

function magnitude(x) =
	x%bolddivs[2]==0 ? 3 :
	(
		x%bolddivs[1]==0 ? 2 :
 		(
 			x%bolddivs[0]==0 ? 1 : 0
 		)
	);
//	let(n=len(bolddivs))
//	let(tmp=[for(i=0, cur=n-1; (i<n) && ((x%bolddivs[cur])!=0); i=i+1, cur=cur-1) cur ])
//	n-len(tmp);

	if (part!=2)
	rotate([0,0,90]) rotate([90,0,0]) linear_extrude(w*mm_per_division)
		polygon([[0,0],[d,0],[d,minh], [d-dtop/2,h],[dtop/2,h],[0,minh]]);

	intersection() {
		translate([0,0,part==2 ? h : 0])
			cube([w*mm_per_division,d, part==0 ? maxh : h]);

union(){
	for(i=[0:w]) {
		j = magnitude(i);
		for(k=[0,1])
			translate([i*mm_per_division-mils[j][0]/2,k*(d-mils[j][1])+offset_from_edge*(1-2*k),0])
				cube(mils[j]);
		if (j==3) {
			for(k=[0,1])
			translate([i*mm_per_division+ (i==0?1.5:0), k==0?mils[3][1]+1:d-mils[3][1]-1,0])
				linear_extrude(h+extra_h_digits)
				text(str( i/divs ), size=font_size, halign="center", valign = k==0?"bottom":"top" );
		}
	}

	if (part!=1)
	translate([w/2 * mm_per_division,d/2-font_size/10,0])
		linear_extrude(h+extra_h_label)
			text(label_text, size=label_font_size, font = label_font_name, halign="center", spacing=text_spacing, valign="center");
}
}

}

module main() {
p=0;// 0 - both, 1 - lower, 2 - upper
samples = [
[ww, dd, hh, custom_text, 0],
];

i=0;
meat(samples[i]);

}

main();
```


[User]
Hi, I'm looking for an OpenSCAD design for a customizable cable and rod holder. I need it to be flat, and I'd like to be able to adjust several parameters.

Specifically, I need to control:

*   The number of sections (slots) for holding cables or rods.
*   The overall height of the holder.
*   The distance between each of the sections.
*   The distance of each section's opening from the edge of the holder.
*   The rounding radius of the base edges.
*   Minimum values for the holder's width and length (so it doesn't get too small).
*   The width of each slot.
*   The width of the space where the cable or rod is actually held within the slot.
*   The height of each slot.
*   The width of the slot's opening at the top.
*   The height of the slot's smoothing wall (if any).
*   The offset of screw holes from the base edge.
*   The screw head diameter.
*   The screw leg diameter.
*   The screw head height.
*   How much the screw head should drop into the material for a flush fit.

Also, I'd like to be able to select between showing just a cross-section, the ready-to-print model, or a top view.

I don't need any fancy libraries, just the standard OpenSCAD functionality would be great. The output should also have comments explaining each parameter like in the example. I think that's everything. Thanks!

[Assistant]
customizable-cable-rod-holder.scad:
```
// Customizable cables/rods FLAT organizer/holder
// by torwan @ February 10th, 2024

// Description / Hints:
// - See more details on https://www.thingiverse.com/thing:6478394
// - All dimensions in [mm].
// - Overall width and length of a holder are auto-calculated. You
//   can override them by specifying minimum values that are higher
//   than auto-calculated ones.
// - To generate STL file, render the object using F6 and save it
//   using F7.
// - Print up-side-down (without supports).
// - $fn controls number of a circle fragments. I recommend to use
//   $fn = 50 or more.

// ------------ Supporting parameters ------------
/* [Supporting parameters] */
$fn = 50;
cc = 0.01;

// ----------- Model general parameters ----------
/* [Model general parameters] */

// Number of sections
no_sections = 3;

// Holder height
holder_height = 4;

// Distance between sections
dist_sections = 2;

// Distance of section opening from holder edge (does not include rounded part)
dist_w_edge = 1;

// Rounding radius
rounding_radius = 1.0;

// Minimum holder width
minimum_holder_width = 0;

// Minimum holder length
minimum_holder_length = 0;

// --------------- Slot dimensions ---------------
/* [Slot dimensions] */

// Slot width
slot_width = 13;

// Amount of space for an object to hold
object_space_width = 7;

// Slot height
slot_height = 2.4;

// Slot top opening width
slot_opening_width = 3.5;

// Slot smoothing wall height
slot_smoothing_wall = 0.6;

// ----------- Screw-holes dimensions ------------
/* [Screw-holes dimensions] */

// Distance of a screw hole from the edge of the base
screw_offset = 5.0;

// Screw head diameter
screw_head_dia = 6.4;

// Screw leg diameter (make it 0.1-0.2 bigger than real)
screw_leg_dia = 3.3;

// Screw head height
screw_head_h = 3;

// How much a screw has to drop into material (hiding head)
screw_head_drop = 0.2;

// ------------- Helping parameters --------------
// (do not modify below parameters)
top_far = object_space_width/2 + slot_opening_width;
bottom_close = top_far - holder_height;
slot_roof_h = holder_height - slot_height;

no_sections_c = (no_sections >=1) ? no_sections : 1;

required_holder_length = (no_sections_c+1) * dist_sections
                         + no_sections_c * (2*top_far)
                         + 2*screw_offset
                         + 2*screw_head_dia/2;
required_holder_width = slot_width + 2*dist_w_edge + 2*rounding_radius;
holder_length = (required_holder_length>minimum_holder_length) ?
                 required_holder_length :
                 minimum_holder_length;
ext_length_split = (minimum_holder_length>required_holder_length) ?
                    (minimum_holder_length-required_holder_length)/2 : 0;
holder_width = (required_holder_width>minimum_holder_width) ?
                required_holder_width :
                minimum_holder_width;
1st_section_start = screw_offset
                  + screw_head_dia/2
                  + dist_sections
                  + top_far;
section_jump = 2* top_far + dist_sections;

echo(str("holder_width: ", holder_width));
echo(str("holder_length: ", holder_length));

// --------------- GENERATION AREA ---------------
/* [GENERATION AREA] */
// 0 - cross-secion, 1 - model ready2print, 2 - model top view
what_to_show = 1; //[0, 1, 2]

create_holder(what_to_show);

// ------------------ MODULES --------------------
// (you do not need to touch anything below unless you
// know what you do :) )
module create_holder(wts = 0)
{
    flip = (wts == 1) ? 180 : 0;
    rotate([flip, 0, 0])
        difference()
        {
            // Base body
            base_body();

            // Mounting screw slots
            for(off = [screw_offset, holder_length-screw_offset])
                translate([off, holder_width/2, holder_height+cc])
                    rotate([180, 0, 0])
                        screw_nest(head_dia = screw_head_dia,
                                    head_drop = screw_head_drop,
                                    screw_head_h = screw_head_h,
                                    total_length = 10,
                                    screw_leg_dia = screw_leg_dia);

            // Deploy section one after another
            for(idx = [0:1:(no_sections_c-1)])
                translate([1st_section_start+ext_length_split+idx*section_jump,
                           slot_width/2+holder_width/2,
                           0])
                    rotate([90, 0, 0]) make_cut();

            // Cut off half to present cross section
            if (wts == 0)
                translate([-cc/2, holder_width/2, -cc/2])
                    cube([holder_length+cc, holder_width+cc, holder_height+cc]);
        }
}

module base_body()
{
    difference()
    {
        translate([rounding_radius, rounding_radius, rounding_radius-holder_height])
            minkowski()
            {
                cube([holder_length-2*rounding_radius,
                      holder_width-2*rounding_radius,
                      2*holder_height-2*rounding_radius]);
                sphere(rounding_radius);
            }
        translate([-cc/2, -cc/2, -holder_height-cc])
            cube([holder_length+cc, holder_width+cc, holder_height+cc]);
    }
}

module make_cut_half()
{
    translate([0, 0, -cc])
        linear_extrude(slot_width)
            polygon([[0, -cc],
                     [bottom_close, -cc],
                     [top_far, holder_height+cc],
                     [object_space_width/2, holder_height+cc],
                     [object_space_width/2, holder_height-slot_smoothing_wall],
                     [object_space_width/2-slot_roof_h, slot_height],
                     [0, slot_height]
                     ]);
}

module make_cut()
{
    make_cut_half();
    mirror([1, 0, 0])
        make_cut_half();
}

module screw_nest(head_dia = 6,
                  head_drop = 1,
                  screw_head_h = 3,
                  total_length = 10,
                  screw_leg_dia = 2) // Template for space for a mounting screw
{
    rotate_extrude(angle = 360)
        polygon([[0, 0],
                 [head_dia/2, -cc],
                 [head_dia/2, head_drop],
                 [screw_leg_dia/2, head_drop + screw_head_h],
                 [screw_leg_dia/2, total_length+cc],
                 [0, total_length+cc]
                ]);
}
```


[User]
I'm looking to create a parametric LED strip connector in OpenSCAD, similar to the one I've seen described online, but I need it designed from the ground up so that I can easily customize it.

Essentially, I want a connector that can quickly attach wires to an LED strip without soldering. It should have two main variations: a split design with snap-fit connectors and a single-body design. The design needs to be adjustable for different strip widths, the length of the contact area, the overall connector length, and the number of pins needed. It should also handle different wire gauges, accommodate for insulation thickness, and allow adjustments for strip thickness and how tightly the strip is held in place. Finally, the design needs to handle snap fit tolerances.

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

*   **Model Type:**  Should be able to choose between "Split" (with snap connectors), "Solid" (single body), or "Both" (generating both types).
*   **Strip Width:**  The width of the LED strip, likely around 10mm, but needs to be parametric.
*   **Lead Length:** The length of the copper trace contact area - how much of the LED strip the connector covers.
*   **Connector Length:** The total length of the connector.
*   **Number of Pins:**  The number of wire connections, I want to be able to choose between 2, 3, and 4 to start.
*   **Wire Gauge:** The gauge of the wire used, which will determine the wire diameter.
*   **Insulation Thickness:** The thickness of the wire insulation.
*   **Strip Thickness:** The thickness of the LED strip itself.
*   **Strip Squeeze:** How tightly the connector squeezes the LED strip.
*   **Wall Thickness:** The thickness of the connector's walls.
*   **Snap Connector Width:** Width of latch connectors in split design
*   **Snap Connector Thickness:** Thickness of latch connector shaft in split design.
*   **Snap Connector Depth:** How far the latching mechanism sticks out in split design.
*   **Snap Connector Height:** Height of the latching mechanism in split design.
*  **Snap Connector Tolerance:** Tolerance for snap connectors in split design.

I'd like the design to use faceted cylinders, and the number of facets should be parametric, but a reasonable default (e.g. 99) is fine.  I need the design to be fairly tolerant, and I'm planning to start with a 21 gauge wire, so a default of 1.99mm for the insulator thickness seems good.  I think a default strip thickness of 0.4mm and squeeze of 0.15mm will be useful, as will a wall thickness of 3mm.  Snap connector defaults should start at 5mm width, 1.3mm shaft thickness, depth of 0.5mm, height of 2.3mm, and a tolerance of 0.2mm.

I'd like to rely on standard libraries where possible, so please use `BOSL` or `BOSL2` if that seems useful to you, but don't feel obligated to use them if not appropriate.

The connector should have cutouts to allow the wire to be inserted and make good contact with the copper traces on the LED strip. It should also keep the LED strip aligned and provide some clamping force.

Could you generate the necessary OpenSCAD files for this parametric design?

[Assistant]
led-strip-connector.scad:
```
/* [Details] */
// Split, snap-fit or one-piece?
model="Split"; // [Split, Solid, Both]
// Width of the LED strip
strip_width=10;
// This is how much of the LED strip can be covered by the connector.  This could be just the copper traces, or up to any component that would interfere with closing the connector, such as a resister, capacitor, or the actual LED.
lead_len = 3.6;
// How long the connector should be
length = 12;
// How many pins does the connector have?  See script to add additional options or tweak spacing of contact leads.
pins=3;//[3, 4]
// Wire gauge.  Go smaller to add some tolerance as required by your printer.
gauge=21;
// Thickness of the wire including insulating layer, and some "tolerance"
insul=1.99;
// How much space to give for the thickness of the LED strip
strip_thk = 0.4;
// How tightly to squeeze the strip and wire together
strip_squueze = 0.15;
// Wall thickness of the connector
thk=3;
// How many facets in a circle.  As we are dealing with some very small bits, we don't need a large number here.
$fn=99;

/*[Snap Connectors] */
// Width of latch connectors
conw = 5.0;
// Thickness of latch connector shaft
conthk = 1.3;
// Extent to which latching mechanism sticks out
cond = 0.5;
// Height of above latching mechanism
conh = 2.3;
// Tolerance of snap connector
contol = 0.2;

/*[Hidden] */
// Centre of each copper trace on the led strip, indexed by number of pins.  Change this to add support for different numbers of connectors, or different width strips - these are for 10mm wide strips.
centres=[[0], [0, 0], [2.5, 5, 7.5], [1.3, 3.8, 6.25, 8.7]];
// Overall width of device
wid = strip_width + 2 * (thk + conthk);
// Height of everything
height = 2*thk + insul + 2;
// wall thickness includes basic thickness plus room for snap conectors
sidewall = thk + conthk;
// Wire diameter (mm)
wire_d = wire_diam(gauge);
echo(gauge, "gauge wire is ", wire_d, "mm thick");

// Quick test to compare wire guage formula against published wire gauge charts
/*for (i = [12 : 2 : 30]) {
    echo( i, "gauge wire is ", wire_diam(i), "thick");
}*/

if (model != "Solid") {// Split or Both
    rotate([0, -180*(1-$t), 0])
        translate([0, 0, -height*(1-$t)])
            top();
    translate([5*(1-$t), 0, 0])
        bottom();
}

if (model != "Split") { // Solid or Both
    yDelta = (model == "Both" ? length + 9 : 0);
    translate([0, yDelta, 0]) {
        top(false);
        bottom(false);
    }
}

module top(split = true) {
    difference() {
        box();
        difference() {
        // Remove bottom half, except a bit where exposed wire will be
            translate([-1, -1, -1])
                cube([wid + 2, length + 2, height/2 + 1]);
            // platform around expose wires
            sep(-0.2, -0.2, strip_thk*0);
        }
    }
    if (split) {
        // Snap Connectors
        rotate([0, 180, 270])
            translate([0, 0, -height/2])
                latch();

        rotate([0, 180, 270])
            translate([length - conw, 0, -height/2])
                latch();

        rotate([0, 180, 90])
            translate([-conw, -wid, -height/2])
                latch();

        rotate([0, 180, 90])
            translate([-length , -wid, -height/2])
                latch();
    }
}

module bottom(split = true) {
    difference() {
        box();
        // Remove top half
        translate([-1, -1, height/2])
            cube([wid + 2, length + 2, height/2 + 1]);
        sep(0, 0, strip_thk);

        if (split) {
            rotate([0, 180, 270])
                translate([0 - 0.01, 0, -height/2])
                    latch(contol);

            rotate([0, 180, 270])
                translate([length - conw - contol + 0.01, 0, -height/2])
                    latch(contol);

            rotate([0, 180, 90])
                translate([-conw-contol+0.01, -wid, -height/2])
                    latch(contol);

            rotate([0, 180, 90])
                translate([-length , -wid, -height/2])
                    latch(contol);
        }
    }
}

// Basic connector unit
module box() {
    difference() {
        // Overall box from which we cut away bits
        cube([wid, length, height]);
        // Removec space for wire and insulator
        for (i = [1 : pins]) {
            translate([sidewall + centres[pins-1][i-1], 0, 0])
                wire();
        }
        // LED Strip
   //     sep();
        // clear space atop wire
        for (i = [1 : pins]) {
            translate([sidewall + centres[pins-1][i-1] - wire_d/2, length-lead_len,  height/2 - wire_d/2])
                cube([wire_d, lead_len + 0.01, wire_d/2  +0.01]);
        }
    }
    /*for (i = [1 : pins]) {
       % translate([sidewall + centres[pins-1][i-1], 0, 0])
            wire(true);
    }*/
}

// Wire with insulation
module wire(core = false) {
    // core wire
    translate([0, length+1, height/2])
        rotate([90, 0, 0])
            cylinder(h = length+2, d = wire_d);
    if (!core) {
    //insulation
    translate([0, length-lead_len, height/2])
        rotate([90, 0, 0])
            cylinder(h = length - lead_len + 0.01, d = insul);
    }
}

// delta: how much to exclude from perfect mating with other side (e.g. led strip thickness and some tolerance and allowance for wire to contact led strip
// hh: height addition to ensure an F5 rendering comes out clean (for bottom half)
module sep(xtol = 0.0, ytol = 0.0, zDelta = 0) {
    // Wire separators
    translate([sidewall - xtol,   length - lead_len - ytol,   height/2 - wire_d/2 - zDelta + strip_squueze])
        cube([strip_width + 2 * xtol, lead_len+ytol+0.01, strip_thk + wire_d/2 - strip_squueze + 0.01]);
}

module latch(tol = 0) {
    cube([conw+tol, conthk+tol, height/2+(tol > 0 ? 0.01 : 0)]);
    rotate([0, 270, 0])
        translate([height/2 - conh - tol, conthk+tol, -conw-tol])
            linear_extrude(conw + 2*tol)
                polygon([[0, 0], [0, cond+tol], [conh + tol, 0]]);
}

// Determine wire diameter based on gauge
function wire_diam(gauge) = 0.127 * 92 ^ ((36 - gauge)/39);

```


[User]
I'm looking to create some labels for my storage bins, which are those plastic ones with the rectangular holes in them, kind of like fake rattan. I'd like to be able to customize the label's size, text, and how it attaches to the bin, using OpenSCAD.

Specifically, I need to define a few things:

First, I need to specify the dimensions of the bin's holes and their spacing.  So, I'll need to input the distance between the holes horizontally, the distance between the holes vertically, the height of the holes, the width of the holes, and the thickness of the bin's wall.

Second, for the label itself, I need parameters to control the label thickness and how far the label extends past the attachment points on each side. There should be separate controls for top, bottom, left, and right overhangs.

Third, there needs to be a way to define some text that will be printed on the front of the label, and an option to scale that text. It would also be helpful to have an option for text on the back of the label, though it doesn't need to be visible.

Fourth, for the attachment mechanism, I want to use a snap fit design. The key parameters here should be the thickness of the "posts" on the side of the snap, the amount of overhang in the snap mechanism (more overhang = tighter fit), the depth of the groove, and the clearance between the side of the hole and the snap closure. The snap should be designed so it can be made to fit into a vertical hole or horizontal hole.

Could you generate an OpenSCAD file (or files) that allows me to define all of this? I'm not really planning on making a label that will be removed and reattached often, since the snaps will be printed in the z direction, so they won't be super strong. I would also like to avoid using any libraries in this design.

[Assistant]
basketlabels_generic.scad:
```
/* [Dimensions - measure these on your basket] */

// How many cells should the label cover horizontally [mm]?
distance_between_holes_w = 60;
// How many cells should the label cover vertically [mm]?
distance_between_holes_h = 30;
// How tall are the holes in your basket [mm]?
hole_height = 3;
// How wide are the holes in your basket [mm]?
hole_width = 6;
// How thick is the wall of your basket [mm]?
wall_thickness = 1;

/* [Label parameters] */
// How thick should the facing plate of the label be [mm]?
label_thickness = 1; // [0.5:0.5:5]

// How far should the label extend beyond the attachment points at the top [mm]?
overhang_top = 3; // [0:10]
// How far should the label extend beyond the attachment points on the left [mm]?
overhang_left = 3; // [0:10]
// How far should the label extend beyond the attachment points on the right [mm]?
overhang_right = 3; // [0:10]
// How far should the label extend beyond the attachment points at the bottom [mm]?
overhang_bottom = 3; // [0:10]

/* [Text] */
// Text on the label
text_front = "";
// Factor by which to scale the text on the front
text_front_size = 1; // [0.25:0.25:5]
// Text on the back of the label - won't be visible, can be used to tell different sizes apart
text_back = "";

/* [Parameters for snap attachment - leave on default unless you have problems] */
// Thickness of the "posts" on each side of the snap mechanism
snap_thickness = 1.2;
// Amount of overhang in the snap mechanism. More = tighter hold, but harder to insert.
snap_overhang = 0.2;
// Depth of the groove in the snap mechanism
snap_depth = 0.6;
// Clearance between the side of the hole and the snap closure
snap_side_clearance = 0.5;

module snap_closure ( w, d, h, post_thickness, overhang, extra_depth ) {
    cyl_radius = overhang + post_thickness;
    cyl_offset = cyl_radius * (1 - cos(asin(post_thickness / cyl_radius)));
    chamfer_radius = h/2 - post_thickness;
    difference() {
        union() {
            translate([0, d/2 + cyl_radius/2, 0]) cube([w, d + cyl_radius, h], true);
            hull() {
                translate([0, d + cyl_radius - cyl_offset, h/2 - post_thickness]){
                    rotate([0, 90, 0]) {
                        cylinder(w, cyl_radius, cyl_radius, true, $fn=100);
                    }
                }
                translate([0, d + cyl_radius - cyl_offset, -h/2 + post_thickness]){
                    rotate([0, 90, 0]) {
                        cylinder(w, cyl_radius, cyl_radius, true, $fn=100);
                    }
                }
            }
        }
        translate([0, d/2 + cyl_radius + (d - extra_depth) + chamfer_radius, 0]) {
            cube([w*2, d + cyl_radius*2, h - 2* post_thickness], true);
        }
        translate([0, d - extra_depth + chamfer_radius, 0]){
            rotate([0, 90, 0]) {
                cylinder(w*2, chamfer_radius, chamfer_radius, true, $fn=100);
            }
        }
    }
}

total_w = distance_between_holes_w + 2 * hole_width;
total_h = distance_between_holes_h + 2 * hole_height;

rotate([90, 0, 0]) {
    difference() {
        translate([(overhang_right - overhang_left)/2, -label_thickness/2, (overhang_top - overhang_bottom)/2]) cube([total_w + overhang_left + overhang_right, label_thickness, total_h + overhang_top + overhang_bottom], true);
        translate([0, 0.5-label_thickness, 0]) rotate([90, 0, 0]) scale([text_front_size, text_front_size, 1]) linear_extrude(0.6) text(text_front, halign = "center", valign = "center");
    }

    rotate([90, 0, 180]) linear_extrude(0.5) text(text_back, halign = "center", valign = "center");

     for(i = [ [  1,  1],
               [ 1, -1],
               [-1, -1],
               [ -1, 1] ])
    {
        translate([i[0] * (total_w - hole_width)/2, 0, i[1] * (total_h - hole_height)/2]) {
            if (hole_width >= hole_height) {
                snap_closure(hole_width - 2*snap_side_clearance, wall_thickness, hole_height, snap_thickness, snap_overhang, snap_depth);
            }
            else {
                rotate([0, 90, 0]) {
                    snap_closure(hole_height - 2*snap_side_clearance, wall_thickness, hole_width, snap_thickness, snap_overhang, snap_depth);
                }
            }
        }
    }
}
```


[User]
I'm looking for an OpenSCAD design for a customizable bracket to hold various boxy objects, like power supplies or small enclosures. I found some designs that are similar, and I'd like to be able to adjust all the parameters to fit different devices.

Here's what I need:

*   **Basic Shape:** The bracket should consist of a base, an "upper arm", a "lower arm", and a "finger" at the end. The base attaches to a surface, then the upper arm extends out, the lower arm comes down, and the finger comes back in to hold the object.
*   **Parametric Dimensions:** I need to be able to adjust the lengths and angles of the arms and finger. I also need to adjust the width of the bracket, its thickness, the width of the object the bracket is intended to hold, and some "play" to make it easy to slide the object in, so the calculations are done for a snug fit.
*   **Base Options:** The base should be configurable. It can either be flat, or arched, with a hole for a screw to secure the bracket. If it's arched, I need to control the radius of the arch.
*   **End Stop Option:** I'd like the option to add an end-stop on the upper arm, in case I'm mounting an object vertically, so it won't slide past the bracket. It should also be configurable in terms of its length and height.
*   **Screw Holes:** I need to be able to specify the diameter of the screw head and screw leg. I need to be able to drop the screw head down into the bracket, so it sits flush, but can also have the head sit on top of the base. And I need to be able to configure a second screw hole, which may be useful for a wider bracket to keep it attached securely. It'd also be useful to be able to turn off the second screw if I don't need it.
*   **Output Control:** I'd like an option to show the bracket on its own, or to show it holding a representative box with dimensions printed on it. I'd also like it to report some of the internal dimensions it calculates, so I can check them.

I don't need any libraries to do this, I'd like to keep it all basic. I want all dimensions to be in millimeters.

[Assistant]
customizable-box-psu-power-adapter-holder.scad:
```
/* Customizable box/psu/power adapter bracket/holder
   written by torwan @ February 18th, 2024

   Design concept by bekudrei/bequ3:
   https://www.thingiverse.com/thing:4977737
   https://www.printables.com/model/77598-fasta-parametric-mounting-bracket

   Description / Hints:
   - See more details on https://www.thingiverse.com/thing:6490337
   - All dimensions in [mm].
   - Use "what_to_show" parameter to control what to generate.
   - Use "bracket_base_type" parameter to switch between holder base
     types (flat vs arched with one screw)
   - Use "endstop_enabler" parameter to add or remove an end-stop.
     It can be useful when you mount your object vertically.
   - To generate STL file, render the object using F6 and save it
     using F7.
   - $fn controls number of a circle fragments. I recommend to use
     $fn = 50 or more.
   - Explanation of the design segments (this is being drawn and then
     mirrored to create a complete bracket):

                     shoulder
                     --------
                     |
         upper_arm   |
                     |
                     /
         lower_arm  /
                   /__ finger

Parameter values that will work with my Sonoff DualR3 Enclosure:
 bracket_base_type = 0, bracket_width = 10, bracket_thickness = 3,
 box_width = 60, box_play = 0.5, upper_arm_l = 20, upper_arm_ang = -2,
 lower_arm_l = 20, lower_arm_ang = 3, finger_l = 10, finger_ang = -135,
 end_stop_h = 5, end_stop_th = 3, end_stop_lf = 0.6

Parameter values that will work with HP Zbook TPN-CA11 150W AC/DC adapter:
 bracket_base_type = 0, bracket_width = 15, bracket_thickness = 3,
 box_width = 66.3, box_play = 0.2, upper_arm_l = 12, upper_arm_ang = -2,
 lower_arm_l = 18, lower_arm_ang = 3, finger_l = 10, finger_ang = -130

Parameter values that will work with HP HSTNN-CA40 45W AC/DC adapter:
 bracket_base_type = 0, bracket_width = 12, bracket_thickness = 3,
 box_width = 40, box_play = 0.5, upper_arm_l = 17.5, upper_arm_ang = -2,
 lower_arm_l = 18, lower_arm_ang = 3, finger_l = 10, finger_ang = -130
*/

// ----------- Model general parameters ----------
/* [Model general parameters] */

// 0 - show a holder with a box & text, 1 - show only a holder (to print)
what_to_show = 0; //[0, 1]

// Bracket type, 0 - straight base, 1 - arched base with one screw hole
bracket_base_type = 0; //[0, 1]

// End-stop activation
endstop_enabler = 0; //[0, 1]

// Bracket width
bracket_width = 12.0;

// Bracket thickness
bracket_thickness = 3.0;

// Bracket rounding radius
rounding_radius = 0.5;

// Your box width
box_width = 40.0;

// Play for your box width play
box_play = 0.5;

// Upper arm length
upper_arm_l = 17.5;

// Upper arm angle
upper_arm_ang = -2;

// Lower arm length
lower_arm_l = 18.0;

// Lower arm angle
lower_arm_ang = 3;

// Bottom finger length
finger_l = 10.0;

// Bottom finger angle
finger_ang = -130;

// Base radius (for arched base, when bracket_base_type == 1)
base_radius = 400;

// End stop height
end_stop_h = 5.0;

// End stop thickness
end_stop_th = 3.0;

// End stop width (as fraction of upper arm length)
end_stop_lf = 0.6;

// ----------- Screw-holes dimensions ------------
/* [Screw-holes dimensions] */

// Distance between screws (zero means one screw hole)
screws_distance = 20;

// Screw head diameter
screw_head_dia = 6.4;

// Screw leg diameter (make it 0.1-0.2 bigger than real)
screw_leg_dia = 3.3;

// Screw head height
screw_head_h = 2.6;

// How much a screw has to drop into material (hiding head)
screw_head_drop = 0.2;

// ------------ Supporting parameters ------------
/* [Supporting parameters] */
// Number of a circle fragments
$fn = 50;

// ------------- Helping parameters --------------
// (do not modify below parameters)
cc = 0.01;

shoulder_l = box_width/2 + box_play/2 + bracket_thickness/2;

ua_lx = sin(upper_arm_ang) * upper_arm_l;
ua_ly = cos(upper_arm_ang) * upper_arm_l;

la_lx = sin(lower_arm_ang) * lower_arm_l;
la_ly = cos(lower_arm_ang) * lower_arm_l;

finger_lx = sin(finger_ang) * finger_l;
finger_ly = cos(finger_ang) * finger_l;

box_height = ua_ly + la_ly + finger_ly - bracket_thickness;

angle_arched = (shoulder_l*360) / (2 * PI * base_radius);

shoulder_lx = (bracket_base_type == 0)
            ? (shoulder_l)
            : (sin(angle_arched)*base_radius);

shoulder_ly = (bracket_base_type == 0)
            ? 0
            : base_radius - (cos(angle_arched)*base_radius);

finger_support_dist = (box_width/2)
                     -(shoulder_lx+ua_lx+la_lx+finger_lx);

screws_distance_corr = (bracket_base_type == 0)
                     ? screws_distance
                     : 0;

end_stop_l = end_stop_lf * upper_arm_l;
end_stop = (endstop_enabler == 0) ? false : true;

box_width_rounded = round(10*box_width)/10;
box_height_rounded = round(10*box_height)/10;
finger_support_dist_rounded = round(10*finger_support_dist)/10;
echo(str("Box width: ", box_width_rounded));
echo(str("Box hength: ", box_height_rounded));
echo(str("Finger support distance: ", finger_support_dist_rounded));

// --------------- GENERATION AREA ---------------
/* [GENERATION AREA] */

show_holder(wts = what_to_show);

// ------------------ MODULES --------------------
// (you do not need to touch anything below unless you
// know what you do :) )

module show_holder(wts = 0)
{
    create_holder();
    mirror([1, 0, 0]) create_holder();

    if (wts == 0)
    {
        // Hold object
        color("YellowGreen", alpha = 0.75)
            translate([-box_width/2, bracket_thickness/2, 0])
                cube([box_width,
                      box_height,
                      bracket_width], center = false);
        // Text with width, height and finger support distance
        translate([0, -15, 0])
            color("YellowGreen")
                text(str("Suitable object WIDTH: ", box_width_rounded),
                     size = 8,
                     halign = "center");
         translate([0, -30, 0])
            color("YellowGreen")
                text(str("Suitable object HEIGHT: ", box_height_rounded),
                     size = 8,
                     halign = "center");
         translate([0, -45, 0])
            color("YellowGreen")
                text(str("Finger support distance: ",
                     finger_support_dist_rounded),
                     size = 8,
                     halign = "center");
    }
}

module create_holder()
{
    difference()
    {
        union()
        {
            // Shoulder
            if (bracket_base_type == 0)
                make_straight(shoulder_lx, 90);
            else
                make_arched(rad = base_radius, ang = angle_arched);

            // 1st joint
            translate([shoulder_lx, -shoulder_ly, 0])
                make_joint();

            // Upper arm
            translate([shoulder_lx, -shoulder_ly, 0])
                make_straight(upper_arm_l, upper_arm_ang, es = end_stop);

            // 2nd joint
            translate([shoulder_lx+ua_lx, -shoulder_ly+ua_ly, 0])
                make_joint();

            // Lower arm
            translate([shoulder_lx+ua_lx,
                      -shoulder_ly+ua_ly,
                      0])
                    make_straight(lower_arm_l, lower_arm_ang);

            // 3nd joint
            translate([shoulder_lx+ua_lx+la_lx, -shoulder_ly+ua_ly+la_ly, 0])
                make_joint();

            // Finger
            translate([shoulder_lx+ua_lx+la_lx,
                      -shoulder_ly+ua_ly+la_ly,
                      0])
                    make_straight(finger_l, finger_ang);

            // Finger tip
            translate([shoulder_lx+ua_lx+la_lx+finger_lx,
                       -shoulder_ly+ua_ly+la_ly+finger_ly,
                       0])
                make_joint();
        } // end of union

        // Screw hole
        translate([screws_distance_corr/2, bracket_thickness/2, bracket_width/2])
            rotate([90, 0, 0])
                screw_nest(head_dia = screw_head_dia,
                      head_drop = screw_head_drop,
                      screw_head_h = screw_head_h,
                      total_length = bracket_thickness+cc,
                      screw_leg_dia = screw_leg_dia);
        }
}

module make_straight(len = 0, ang = 0, es = false)
{
        rotate([90, 0, 180-ang])
        {
            translate([-bracket_thickness/2, 0, 0])
                linear_extrude(len)
                    bracket_blueprint();
            if (es == true)
                translate([bracket_thickness/2-rounding_radius,
                           0,
                           -end_stop_l/2+len/2])
                    create_endstop();
        }
}

module make_arched(rad = 0, ang = 0)
{
    mirror([0, 1, 0])
        rotate([0, 0, 90])
                translate([rad, 0, 0])
                    rotate_extrude(angle = ang, $fn = base_radius)
                        translate([-rad-bracket_thickness/2, 0, 0])
                            bracket_blueprint();
}

module make_joint()
{
    translate([0, 0, rounding_radius])
        minkowski()
        {
            cylinder(d = (bracket_thickness-2*rounding_radius),
                     h = bracket_width-2*rounding_radius);
            sphere(r = rounding_radius);
        }
}

module create_endstop()
{   rotate([90, 0, 180])
        difference()
        {
            translate([-end_stop_h,
                       rounding_radius,
                       rounding_radius])
                minkowski()
                {
                    cube([2*end_stop_h-2*rounding_radius,
                          end_stop_l-2*rounding_radius,
                          end_stop_th-2*rounding_radius]);
                    sphere(r = rounding_radius);
                }
            translate([-cc/2, -cc/2, -cc/2])
                cube([end_stop_h+cc, end_stop_l+cc, end_stop_th+cc]);
        }
}

module bracket_blueprint()
{
    translate([rounding_radius, rounding_radius, 0])
        offset(r = rounding_radius)
            square([bracket_thickness-2*rounding_radius,
                    bracket_width-2*rounding_radius]);
}

module screw_nest(head_dia = 6,
                  head_drop = 1,
                  screw_head_h = 3,
                  total_length = 10,
                  screw_leg_dia = 2) // Template for space for a mounting screw
{
    rotate_extrude(angle = 360)
        polygon([[0, 0],
                 [head_dia/2, -cc],
                 [head_dia/2, head_drop],
                 [screw_leg_dia/2, head_drop + screw_head_h],
                 [screw_leg_dia/2, total_length+cc],
                 [0, total_length+cc]
                ]);
}
```