[User]
I'd like to design a fan holder for cooling a 3D printer bed, and I need it to be parametric so I can easily adjust it for different fan sizes and mounting options. I've seen some designs that seem to work well, but I need something I can customize.

Specifically, I need the design to accommodate several different fan sizes (40mm, 45mm, 50mm, 60mm, 70mm and 80mm). The fan will be held in a frame which will attach to the printer bed, and I'd like to be able to specify the internal width and depth of this frame, as well as its thickness. There also needs to be a snap mechanism for attaching the frame to the printer bed, and that should be parameterizable as well.  I need to be able to adjust the internal dimensions to have some tolerance to make sure things fit properly.

Ideally, it should include an optional mounting for a small PCB, like a speed controller, with holes that I can adjust the diameter and position of.

The design should include a frame that holds the fan, a connection to the printer bed that is held together using a snap and the option for a PCB board mounting.

It would be great if you could generate the OpenSCAD code for this. I don't need any external libraries, just standard OpenSCAD functionality.

[Assistant]
BedCoolerFan.scad:
```
//// Version: OpenSCAD 2015.03-2
//// Date: 2021.4.27
//// File: BedCoolerFan.scad
//// Author: Kijja Chaovanakij

/*[smoothness and tolerance]*/
//curve smoothness
smooth=48; //[48:low, 60:medium, 72:high]
$fn=smooth;
sm=min(max(smooth*2/3, 36), 60);
//internal dimention tolerance
i_tol=0.5;

/*[holder]*/
//holder internal width
holder_iwid=20;
//holder internal depth
holder_idep=20;
//holder thickness
holder_thk=3;
//snap size
snap_size=2.8;

/*[fan]*/
//fan size
fan_size=4; //[1:40, 2:45, 3:50, 4:60, 5:70, 6:80]

/*[pcb board]*/
//pcb hole diameter(option for speed control board)
pcb_hole_dia=2.7;
//pcb hole margin(option for speed control board)
pcb_hmargin=2.8;

/*[hidden]*/
h_iwid=holder_iwid+i_tol;
h_idep=holder_idep+i_tol;
pcb_hdia=pcb_hole_dia+i_tol;

//============ main ============//

difference(){
  frame_holder();
  pcb_hole(); //(option for speed control board)
}//d
fan_holder();

module pcb_hole(){ //(option for speed control board)
  $fn=sm; ep=0.01;
  fan=fan_params[fan_size];
  translate([h_iwid/2-ep, 0, -h_idep/2-holder_thk])
    rotate([0, 90, 0])
    linear_extrude(holder_thk+ep*2)
    for (i=[(h_idep-pcb_hmargin-pcb_hdia)/2, -(h_idep-pcb_hmargin-pcb_hdia)/2])
    for (j=[fan[0]/2-pcb_hmargin-pcb_hdia/2, -fan[0]/2+pcb_hmargin+pcb_hdia/2])
      translate([i, j])
        circle(d=pcb_hdia);
}//m
//

module fan_holder(){
  $fn=sm;
  rounding=1.8;
  fan=fan_params[fan_size];
  translate([-h_iwid/2, 0])
    rotate([0, -90, 0])
    linear_extrude(holder_thk)
    difference(){
      offset(r=rounding)
      offset(delta=-rounding)
      difference(){
        translate([fan[0]/2-holder_thk/2, 0])
          square([fan[0]+holder_thk, fan[0]], true);
        translate([fan[0]/2, 0])
          circle(d=fan[0]-2, $fn=smooth*3);
        translate([fan[0]/2-fan[1]/2+fan[2], -fan[0]/2])
          square([fan[0]+holder_thk, fan[0]]);
      }//d
      for (j=[fan[1]/2, -fan[1]/2])
        translate([fan[0]/2-fan[1]/2, j])
          circle(d=fan[2]);
    }//d
}//m
//

module frame_holder(){
  fan=fan_params[fan_size];
  rotate([90, 0, 0])
  linear_extrude(fan[0], center=true)
  union(){
    difference(){
      translate([0, -(h_idep+holder_thk+snap_size)/2])
        offset(r=holder_thk)
        offset(delta=-holder_thk)
          square([h_iwid+holder_thk*2, h_idep+holder_thk+snap_size], true);
      translate([0, -holder_thk-(h_idep+snap_size)/2])
        square([h_iwid, h_idep+snap_size], true);
    }//d
    for(i=[h_idep/2, -h_idep/2])
      translate([i, -holder_thk-h_idep-snap_size/2])
        circle(d=snap_size, $fn=sm);
  }//u
}//m
//

fan_params =
  [["size", "hole_c2c", "hole_dia"],
   [40,     32,         3.4],
   [45,     37,         4.3],
   [50,     40,         4.3],
   [60,     50,         4.3],
   [70,     61.5,       4.3],
   [80,     72,         4.3],
  ];

```


[User]
Hey, I need a parametric OpenSCAD design for a drill template for guitar pedal enclosures, specifically for PedalPCB layouts. I've been using this script called PedalPCB-Drill-Template.scad, and it's pretty close, but I'd like to break it down into a more flexible, library-like structure, and make it easier to add and modify new enclosure types and hole patterns. Also, I'd like to add support for non-rectangular enclosures and not be limited to predefined patterns of holes (basically make hole placement as free as possible).

Here's how I imagine it working:

1.  **Enclosure Definitions:** I'd like a module to define enclosures, including their shape (not just rectangular!), dimensions (width, height, depth or other appropriate parameters for a given shape), and a bevel or radius value for the edges. These should be stored in a way that I can easily add to them.

2.  **Hole Definitions:** I need a module to define holes, including their x/y (or more general coordinate system) locations relative to the enclosure origin, and the hole diameter. It should be very easy to attach holes to specific enclosures.

3.  **Drill Template Generation:** I’d need a module that accepts an enclosure definition and a list of hole definitions, and generates a drill template. The template would have a base with a thickness parameter and optionally include a bevel on its edges. The holes should be created by subtracting cylinders of the correct diameter from the base.

4.  **Library-Like Structure:** I envision this being set up in a way that I can add new enclosure types and new hole arrangements very easily. Perhaps this could involve using lists or dictionaries to store the information.

5.  **Non-Rectangular Enclosures:** It's important for it to support shapes other than just rectangles, ideally including circles or other polygons, and maybe even imported shapes from SVG if that's possible.

I think a good starting point might be to use the `minkowski()` operator for the bevel and the `difference()` operator to create the holes. For the actual shapes I'm not sure which would be best for the base shape. I'm flexible on the internal implementation as long as the result can generate accurate drill templates with very flexible definitions. No specific external libraries are required, but if a simple one makes the design easier, feel free to use it. If you're thinking about using `linear_extrude`, make sure you think of how that would work for shapes not based on 2D polygons.

Essentially, I want a set of well-organized, modular, and reusable OpenSCAD files to handle generating drill templates, and I want to move beyond the limits of just rectangular enclosures with pre-defined layouts. Can you help me with that?

[Assistant]
PedalPCB-Drill-Template.scad:
```
// What size enclosure
enclosure = "1590B"; // [125B:125B, 1590B:1590B]
// Which hole pattern to use
pattern = "back"; // [top:Top (knobs/switches), back:Back (jacks)]

/* [Enclosure] */
top_dimensions = [
    ["125B", [65, 120]],
    ["1590B", [60, 112]],
];

back_dimensions = [
    ["125B", [67, 36]],
    ["1590B", [61, 27]],
];

bevel = 2;

/* [Template] */
top_holes = [
    ["125B", [[-20.32, 38.1],[-16.5, 38.1],[0, 38.1],[16.5, 38.1],[20.32, 38.1],
              [0, 25.4],
              [-20.32, 12.7],[-16.5, 12.7],[0, 12.7],[16.5, 12.7],[20.32, 12.7],
              [-15.24,-27.3],[0, -27.3],[15.24, -27.3],
              [-15.24,-45.1],[0, -45.1],[15.24, -45.1]]],
    ["1590B", [[-20.32, 34.1],[-16.5, 34.1],[0, 34.1],[16.5, 34.1],[20.32, 34.1],
               [0, 21.4],
               [-20.32, 8.7],[-16.5, 8.7],[0, 8.7],[16.5, 8.7],[20.32, 8.7],
               [-15.24, -23.3],[0, -23.3],[15.24, -23.3],
               [-15.24, -41.1],[0, -41.1],[15.24, -41.1]]],
];

back_holes = [
    ["125B", [[-15.2, 5.75],[15.2, 5.75],
              [0, -4.4]]],
    ["1590B", [[-13.2, -3.5],[13.2, -3.5],
               [0, 3.9]]],
];

// Radius of the holes
punch = 1.5;
// Thickness of the template face/walls
thickness = 2;

function selector(item, struct) = struct[search([item], struct)[0]][1];

if (pattern == "top") {
	dimensions = selector(enclosure, top_dimensions);
	holes = selector(enclosure, top_holes);
    DrillTemplate(dimensions[0], dimensions[1], holes);
} else if (pattern == "back") {
	dimensions = selector(enclosure, back_dimensions);
	holes = selector(enclosure, back_holes);
    DrillTemplate(dimensions[0], dimensions[1], holes);
}

module DrillTemplate(w, h, holes, min_top=0, min_bottom=0) {
  difference() {
    translate([0, 0, 2 * thickness]) {
      difference() {
    	cube([w + thickness, h + thickness, 4 * thickness],   center=true);
	    for (hole = holes) {
          translate([hole[0], hole[1], -4 * thickness]) {
            cylinder(5 * thickness, punch, punch, $fn=32);
          }
	    }
      }
    }
    translate([0, 0, 3 * thickness]) {
      minkowski() {
        cube([w - 2 * bevel, h - 2 * bevel, 4 * thickness - 2 * bevel], center=true);
        sphere(bevel, $fn=32);
      }
    }
  }
}

```


[User]
Hey, I'm looking to create some inserts for the grip holes on euro containers to keep mice out. I've seen a design that works well, and I'd like to make my own parametric version so I can tweak it for different container brands.

Essentially, the insert needs to fill the rectangular grip hole with rounded corners, and then have a sort of collar around the edge that snaps into place on the inside of the grip hole. The existing design has a fairly complex profile, but I'm not sure how much of it is truly necessary. Here's what I know from the example I have:

*   The main body of the insert is essentially a rounded rectangle with a width (`insert_width`), height (`insert_height`), depth (`insert_depth`), and corner radius (`insert_corner_radius`).
*   The collar has a defined width (`insert_collar`) and thickness (which is defined as `base_thickness` in the given file).
*   The main body of the insert is hollow, with a wall thickness (`wall_thickness`).
*   There is a kind of profile to the insert, which looks to be designed to have a portion of the interior insert area be the same profile as the hole itself. This profile is defined as 5 points describing a polygon that's then extruded (the points are `[[0,0],[insert_depth,0],[insert_depth,8],[6,insert_height],[0,insert_height]]`).

It would be ideal to have this all configurable through variables. I'm thinking I'd want the following parameters:

*   `insert_width`: Width of the main rectangular insert.
*   `insert_height`: Height of the main rectangular insert.
*  `insert_depth`: Depth of the main insert, as well as the length the profile is extruded to.
*   `insert_corner_radius`: Radius of the rounded corners.
*   `insert_collar`: Width of the collar that snaps into the grip hole.
*  `base_thickness`: Thickness of the collar.
*   `wall_thickness`: Thickness of the walls of the hollow insert body.
*   `profile_points`: List of coordinates that form the shape that is extruded to form the main insert. It is important that the origin of this profile is at the same point as the origin of the main body of the insert.

Could you help me create an OpenSCAD file or files that defines this design parametrically? I'm not worried about additional features like chamfers, etc. right now -- just the basic insert. I don't have any specific requirements regarding external libraries or similar, so anything you think is appropriate should work for me. I'm expecting the output to be an STL that I can slice for printing, so I don't need help generating any intermediate file types.

I think this approach could work well: generate the base of the insert with a hollow rounded rectangle, generate the profile extrusion, intersect the two, and then add the collar to the bottom.

[Assistant]
euro_box_grip_insert.scad:
```
/*
Euro Box Grip Insert

Closes the grip holes.

Author: Joerg Pressel
Created: 05/02/2021
License: Creative Commons - Attribution - Non-Commercial - Share Alike

Changes:
05/02/2021 Changed insert_collar to the actual collar width (was: double the collar), increased collar to 2.5mm

*/

insert_width = 138;
insert_height = 29.2;
insert_depth = 16.4;
insert_corner_radius = 3.1;
insert_collar = 2.5;
wall_thickness = 2;
base_thickness = 1.5;

/* [Hidden] */

$fn = 90;

function getpoints(w, h=0) = h ? [ [0,0], [w,0], [0,h], [w,h] ] : [ [0,0], [w,0] ];

module cylinders(w, h, radius, height, center=false){
    points = getpoints(w, h);
    xoff = center ? -w / 2: radius;
    yoff = center ? -h / 2: radius;
    translate([xoff, yoff]) {
        for (p = points){
            translate(p) cylinder(r=radius, h=height);
        }
    }
}

module rounded(w, h, radius, height, center=false, truedimension=true){
    d = truedimension ? radius * 2 : 0;
    hull() {
        cylinders(w - d, max(h - d, 0), radius, height, center);
    }
}

module hollow_rounded(w, h, radius, height, thickness, center=false, truedimension=true){
    difference(){
        rounded(w, h, radius, height, center, truedimension);
        off = center ? 0 : thickness;
        minus = truedimension ? thickness * 2 : 0;
        translate([off, off, 0]) rounded(w-minus, h-minus, radius - thickness, height, center, truedimension);
    }
}

module profile(){
    polygon(points = [[0,0],[insert_depth,0],[insert_depth,8],[6,insert_height],[0,insert_height]], paths=[[0,1,2,3,4]]);
}

rotate([0,180,0]) union(){
    intersection(){
        hollow_rounded(insert_width, insert_height, insert_corner_radius, insert_depth, wall_thickness);
        rotate([0,90,0]) translate([-insert_depth,0,0]) linear_extrude(insert_width) profile();
    }
    translate([-insert_collar,-insert_collar,insert_depth]) rounded(insert_width+insert_collar*2, insert_height+insert_collar*2, insert_corner_radius, base_thickness);
    rotate([0,-90,0]) translate([0,wall_thickness,-insert_width+wall_thickness]) linear_extrude(insert_width-2*wall_thickness) polygon(points = [[0,0],[10-wall_thickness,0],[wall_thickness/2,8-wall_thickness],[0,8-wall_thickness]], paths=[[0,1,2,3]]);
}

```


[User]
Hey, I'm looking to design a customizable ring calendar for medication bottles, and I'd love some help creating the OpenSCAD files for it.

The basic idea is to have two rings: an inner ring that fits snugly around the bottle neck, and an outer ring that rotates around the inner ring and clicks into place. Here's a breakdown of what I need:

**Inner Ring:**

*   **Diameter:** Needs to be parametrically set, so I can change the bottle neck size it's designed for. Let's call this `Bottle_Diameter`.
*   **Height:** Should have a label area height that can be adjusted, let's call this `Label_Height`. The total height should have some additional height on both ends and that should be parametrically set using an additional parameter.
*   **Click Mechanism:**  Have little cylinders (pegs) evenly spaced around the outer edge that the outer ring clicks into. The number of these should be adjustable (`Click_Count`).
*   **Split:** I'd like an option to split this ring for easier install (`Split_Inner_Ring`).
*   **Text:** I need to be able to add embossed or inset labels at a given height on the side of this ring. These labels should also be parametricaly set. I'll need a parameter for the `Label_Count`, as well as for the label texts themselves. I'd also like to set the `Label_Text_size` parameter.
*   **Tolerance:** I need a `Tolerance` value to give spacing between the rings.

**Outer Ring:**

*   **Diameter:** Should be just larger than the inner ring so it can rotate. I think using the inner ring's radius + `Tolerance` + additional parametrically set value will work best.
*   **Height:** The outer ring should have an adjustable height too, separate from the inner ring's height. Let's call this `outer_h`.
*   **Click Mechanism:** Should have an indention into the ring so that it can click into the pegs on the inner ring.
*   **Split:** An option to split the outer ring to make it easier to put on (`Split_Outer_Ring`).
*    **Grip:** I'd like to have grip pegs around the outside of this ring to make it easier to turn. Let's have a `grip_count` parameter for these.
*  **Handle:** I would like to add a small handle on the outside of the ring, and I'd like the dimensions to be parametrically set

**General:**

*   **Parametric:**  Everything should be parametrically set, so I can adjust ring diameter, height, number of clicks, label count, label texts, text size, tolerance between the rings, the height of the rings, etc.
*   **Text Style:** I need an option to choose `Label_Text_Style` as either "Embossed" or "Inset" for the text labels.

I'm not sure what the best libraries are for this, but I think it should use at least the linear_extrude, text, cylinder, rotate, translate, cube, and difference functions in vanilla OpenSCAD.

Could you please create the necessary OpenSCAD file(s) for this? I'm new to OpenSCAD, so a well-commented file would be very helpful!

[Assistant]
ring-calendar.scad:
```
// Diameter of bottle ring will fit.
Bottle_Diameter = 40;
bottle_r = Bottle_Diameter / 2;

// Number of positions outer ring can click into.
Click_Count = 63;
grip_count = Click_Count;

// Space between rings.
Tolerance = 0.35;

// Height of label area.
Label_Height = 10;

// Text size for labels around the right.
Label_Text_size = 6;

// Text offset. Use negative value to inset text.
Label_Text_Style = "Embossed"; //["Embossed","Inset"]

// Number of labels around ring.
Label_Count = 7; //[0:12]

label_1 = "Sun";
label_2 = "Mon";
label_3 = "Tue";
label_4 = "Wed";
label_5 = "Thu";
label_6 = "Fri";
label_7 = "Sat";
label_8 = "";
label_9 = "";
label_10 = "";
label_11 = "";
label_12 = "";

// Split inner ring for easier install.
Split_Inner_Ring = false;

// Split outerClick_Count ring for easier install.
Split_Outer_Ring = true;

// Inner ring variables.
inner_h = Label_Height + 13;
inner_r = bottle_r + 3;

// Outer ring variables
outer_h = 12 + 0;
outer_r = inner_r + Tolerance + 3;

// Inner ring
translate([0, 0, 0]){
  difference () {
    union() {
      difference() {
        union() {
          difference() {
            cylinder(inner_h, inner_r, inner_r, false, $fn=100);
            for (i = [1:Click_Count])  {
              translate([inner_r * cos(i * (360/Click_Count)), inner_r * sin(i * (360/Click_Count)), -1]) {
                cylinder(13.2, 1, 1, false, $fn=30);
              }
            }
          }
          translate([0, 0, 3]){
            cylinder(3, inner_r - 2, inner_r + 1, false, $fn=100);
          }
          translate([0, 0, 6]){
            cylinder(3, inner_r + 1, inner_r - 2, false, $fn=100);
          }
          translate([0, 0, Label_Height + 13]){
            cylinder(1.5, inner_r, bottle_r + 1.5, false, $fn=100);
          }
        }
        translate([0, 0, -1]){
          cylinder(inner_h + 5, bottle_r, bottle_r, false, $fn=100);
        }
        if (Label_Count > 0) {
          for (i = [1:Label_Count])  {
            rotate((360 * i/Label_Count), [0, 0, 1]) {
              translate([-bottle_r - 1.5, 0, 14]){
                rotate(-90, [0, 1, 0]) {
                  rotate(-90, [0, 0, 1]) {
                    translate([-25, -1, 0.5]){
                      cube([50, Label_Height + 2, 1.5], false);
                    }
                  }
                }
              }
            }
          }
        }
      }
      if (Label_Count > 0) {
        for (i = [1:Label_Count])  {
          rotate((360 * i/Label_Count), [0, 0, 1]) {
            if (Label_Text_Style == "Embossed") {
              translate([-bottle_r - 1.5, 0, 14]){
                rotate(-90, [0, 1, 0]) {
                  rotate(-90, [0, 0, 1]) {
                    if (i == 1) {
                      linear_extrude(1.5)text(label_1, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 2) {
                      linear_extrude(1.5)text(label_2, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 3) {
                      linear_extrude(1.5)text(label_3, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 4) {
                      linear_extrude(1.5)text(label_4, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 5) {
                      linear_extrude(1.5)text(label_5, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 6) {
                      linear_extrude(1.5)text(label_6, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 7) {
                      linear_extrude(1.5)text(label_7, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 8) {
                      linear_extrude(1.5)text(label_8, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 9) {
                      linear_extrude(1.5)text(label_9, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 10) {
                      linear_extrude(1.5)text(label_10, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 11) {
                      linear_extrude(1.5)text(label_11, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                    else if (i == 12) {
                      linear_extrude(1.5)text(label_12, size=Label_Text_size, halign="center", font="Helvetica");
                    }
                  }
                }
              }
            }
          }
        }
      }
    }

    if (Label_Text_Style == "Inset") {
      if (Label_Count > 0) {
        for (i = [1:Label_Count])  {
          rotate((360 * i/Label_Count), [0, 0, 1]) {
            translate([-bottle_r - 1, 0, 14]){
              rotate(-90, [0, 1, 0]) {
                rotate(-90, [0, 0, 1]) {
                  if (i == 1) {
                    linear_extrude(1.5)text(label_1, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 2) {
                    linear_extrude(1.5)text(label_2, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 3) {
                    linear_extrude(1.5)text(label_3, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 4) {
                    linear_extrude(1.5)text(label_4, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 5) {
                    linear_extrude(1.5)text(label_5, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 6) {
                    linear_extrude(1.5)text(label_6, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 7) {
                    linear_extrude(1.5)text(label_7, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 8) {
                    linear_extrude(1.5)text(label_8, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 9) {
                    linear_extrude(1.5)text(label_9, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 10) {
                    linear_extrude(1.5)text(label_10, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 11) {
                    linear_extrude(1.5)text(label_11, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                  else if (i == 12) {
                    linear_extrude(1.5)text(label_12, size=Label_Text_size, halign="center", font="Helvetica");
                  }
                }
              }
            }
          }
        }
      }
    }

    if (Split_Inner_Ring) {
      translate([50, 0, 0]){
        cube([100, 0.5, 100], true);
      }
    }
  }
}

// Outer ring
translate([0, 0, 0]){
  difference() {
    union() {
      difference() {
        union() {
          cylinder(outer_h, outer_r, outer_r, false, $fn=100);
        }
        translate([0, 0, -1]){
          cylinder(outer_h + 2, inner_r + Tolerance, inner_r + Tolerance, false, $fn=100);
        }
      }
      if (Split_Outer_Ring) {
         rotate((360/Click_Count), [0, 0, 1]) {
          translate([inner_r + Tolerance, 0, 6]){
            rotate(30, [0, 0, 1]) {
              cylinder(12, 0.75, 0.75, true, $fn=6);
            }
          }
        }
        rotate(-(360/Click_Count), [0, 0, 1]) {
          translate([inner_r + Tolerance, 0, 6]){
            rotate(30, [0, 0, 1]) {
              cylinder(12, 0.75, 0.75, true, $fn=6);
            }
          }
        }
      } else {
         translate([inner_r + Tolerance, 0, 6]){
          rotate(30, [0, 0, 1]) {
            cylinder(12, 1, 1, true, $fn=6);
          }
        }
      }
    }
    translate([0, 0, 3]){
      cylinder(3, inner_r + Tolerance - 2, inner_r + Tolerance + 1, false, $fn=100);
    }
    translate([0, 0, 6]){
      cylinder(3, inner_r + Tolerance + 1, inner_r + Tolerance - 2, false, $fn=100);
    }
    for (i = [1:grip_count])  {
      translate([(outer_r + 1) * cos(i * (360/grip_count)), (outer_r + 1) * sin(i * (360/grip_count)), -1]) {
        if (i > (grip_count / 2) + 1 || i < (grip_count / 2) - 1 ) {
          cylinder(14, 2, 2, false, $fn=30);
        }
      }
    }
    if (Split_Outer_Ring) {
      translate([50, 0, 0]){
        cube([100, 0.5, 100], true);
      }
    }
    rotate(-90, [0, 1, 0]) {
      translate([6, 0, bottle_r]){
        cylinder(10, 2.5, 2.5, false, $fn=3);
      }
    }
  }
}

```


[User]
I need an OpenSCAD design for a customizable H-shaped clip, primarily intended for mounting LED strips to walls. I've already got a basic version (attached as 'H-Clip.scad'), but I want to be able to adjust it parametrically and include features for more robust mounting.

Specifically, the clip needs three main sections: a central bar, and two side arms extending outwards. The central bar should have adjustable height, width, and thickness. The side arms should also have their own individual height, width, and thickness parameters, and be able to independently adjust how far left or right they extend from the central bar. It also needs to have an additional mounting element so that the clips can be screwed in or attached with something like double-sided tape.

Additionally, I would like to include two angled supports that connect the top and bottom of the side arms to the top and bottom of the central section. The angle of these should be fixed at 45 degrees, relative to the z-axis, and the thickness should be adjustable.

The clip should also include a central mounting hole in the center bar and mounting holes on each arm. All of these holes should have adjustable diameters and be able to be disabled.

I'm new to OpenSCAD, so I'd appreciate it if you could provide the entire code and I do not have any of the OpenSCAD libraries you might include.

[Assistant]
H-Clip.scad:
```
// thickness of the angles
Thickness_Angle=10; // [0:0.1:20]
/* [Global] */

/* [Hidden] */

/* [Left] */
// thickness of the left arm
Thickness_Left=10; // [1:0.1:20]
// height of the left arm
Height_Left=30; // [1:0.1:100]
// width of the left arm
Width_Left=3; // [1:0.1:100]

/* [Center] */
// thickness of the center arm
Thickness_Center=10; // [1:0.1:20]
// height of the center arm
Height_Center=4; // [1:0.1:100]
// width of the center arm
Width_Center=43; // [1:0.1:100]

/* [Right] */
// thickness of the right arm
Thickness_Right=10; // [1:0.1:20]
// height of the right arm
Height_Right=30; // [1:0.1:100]
// width of the right arm
Width_Right=4; // [1:0.1:100]

translate([-(Width_Center/2+Width_Left/2),0,0]) cube([Width_Left,Thickness_Left, Height_Left], center=true);
cube([Width_Center,Thickness_Center, Height_Center], center=true);

translate([Width_Center/2+Width_Right/2,0,0]) cube([Width_Right,Thickness_Right, Height_Right], center=true);

intersection() {
    union() {
        translate([Width_Center/2+Width_Right/2,0,0]) rotate([0,45,0]) cube(Thickness_Angle, center=true);
        translate([-(Width_Center/2+Width_Right/2),0,0]) rotate([0,-45,0]) cube(Thickness_Angle, center=true);
    }
    union() {
        translate([0,0,(max(Height_Left,Height_Right)-Height_Center)/4+Height_Center/2]) cube([Width_Center,Thickness_Center, (max(Height_Left,Height_Right)-Height_Center)/2], center=true);
        translate([0,0,-((max(Height_Left,Height_Right)-Height_Center)/4+Height_Center/2)]) cube([Width_Center,Thickness_Center, (max(Height_Left,Height_Right)-Height_Center)/2], center=true);
    }

}

```


[User]
I need a parametric OpenSCAD design for a spool adapter. It should fit inside a filament spool and attach to a standard 3D printer filament holder. The key parameters need to be customizable so that it will be adaptable to different spools. I need to be able to control the diameter of the hole in the spool (where this adapter goes), the thickness of the spool itself (so I know how long the adapter needs to be), and the diameter of the axle on my spool holder.

The basic shape I'm envisioning is a central cylinder that fits snugly on the spool holder axle, then arms that radiate outward to grab the inside of the spool. The arms should be angled so the adapter sits concentrically. The arms should also have rounded ends to make insertion easier and to prevent damage to the spool. I would like the option to create a single adapter, or one that uses two opposing pieces if that is better for the overall design.

I need to control the following parameters:

*   `spool_hole`:  The diameter of the spool's center hole.
*   `height`: The thickness of the spool, and thus the height/length of the adapter.
*   `arbor`: The diameter of the spool holder's axle.
*   `arm_thickness`: The thickness of the supporting arms.
*   `arm_width`: The width of the supporting arms.
* `arm_end_radius`: The radius of the rounded end of the arms
*   `num_arms`: The number of arms. Should be evenly spaced.
*   `two_part`: A boolean to toggle between a single long adapter and two opposing shorter ones

It would also be helpful if it used $fn for controlling the smoothness of the circular shapes.

I'd like to be able to load this into the OpenSCAD customizer and change these settings.
I'm not sure how to handle the tolerances for the printer, but maybe we can add a small fudge factor that I can adjust.

[Assistant]
Custom_spool_hub.scad:
```
//spool hole diameater
spool_hole=78;
//adapter height
height=20;
//spool holder arbor hole diameter
arbor=6.9;
//hub wall thickness
thick=4;
//curve smoothness
$fn=100;

hole=spool_hole/2;
rod=arbor/2;
arc=(hole+rod)/2;
arc2=(hole+rod+thick)/2;

//spool holder's axel hub
difference() {
    union() {
        cylinder(r=rod+thick,h=height);
        //adapter arms to fit spool hole

        for(a=[0:120:240]) {
            rotate(a)
            arm();
        }
    }
    translate([0,0,-0.5])
    cylinder(r=rod,h=height+1);
    translate([0,0,-0.1])
    cylinder(r2=rod,r1=rod+.5,h=3);
    translate([0,0,height-2.9])
    cylinder(r1=rod,r2=rod+.5,h=3);
}

//end spool hub adapter

module arm() {
    //long arms
    difference() {
        translate([arc2-rod-thick,0,0])
        cylinder(r=arc2,height);

        translate([arc-rod-1,0,-.5])
        cylinder(r=arc-1,h=height+1);

        translate([-rod-thick,0,-.5])
            cube([hole+rod+thick,arc2,height+1]);
    }
    //arm tips
    translate([hole-1,0,0]) {
        cylinder(r=2,h=height,$fn=12);
        cylinder(r=5,h=2,$fn=24);
            translate([0,0,height])
        cylinder(r1=2,r2=1,h=2);
    }
}

```


[User]
Hey, I'm looking to create a threaded cap in OpenSCAD, and I need a bit of help getting the design right. I've seen some code examples, but it's a little complex and I'm hoping you can assist me in generating a more flexible and user-friendly version.

I'd like the cap to be parametric with the following features:

*   **Thread Customization:**
    *   Thread pitch (distance between threads)
    *   Thread angle (the angle of the thread profile)
    *   Thread depth
    *   Number of thread starts (like a multi-lead screw)
*   **Cap Dimensions:**
    *   Inner diameter at the closed end of the cap
    *   Inner diameter at the open end of the cap
    *   Overall height of the cap (excluding the roof/top)
    *   Cap border width (the thickness of the cap wall)
    *   Cap roof thickness
*   **Grip Features:**
    *   Number of grips around the circumference
    *   Grip to non-grip ratio (controls the width of the grip vs. the spacing between grips)
    *   Total height of the grips (including the roof)
*   **Thread Generation:**
    *   I'd like to use the `metric_thread` module from Dan Kirshner's thread library. So, make sure to include that in the design.

I'd like the code to be organized well, and I'd prefer to be able to easily adjust the cap by tweaking parameters. I'd also like the design to be in the correct printing orientation for a 3D printer, so it should be rotated 180 degrees when it's rendered so it sits correctly on a print bed.

Let me know if you need any more information!

[Assistant]
threaded-round-cap.scad:
```
// Copyright 2021 Daniel Starke
// Licensed under CC BY-NC-SA 4.0 (https://creativecommons.org/licenses/by-nc-sa/4.0/)
// Note: Set "Turn off rendering at [100000] elements" in Edit/Preferences/Advanced.

// Thread pitch.
P_THREAD = 4.3; // [0:0.1:10]
// Thread angle.
A_THREAD = 0; // [0:1:45]
// Thread depth.
D_THREAD = 0; // [0:0.1:5]
// Thread starts.
N_THREAD = 1; // [1:1:8]
// Cap closed end inner diameter.
D_CLOSED = 32; // [3:0.1:200]
// Cap open end inner diameter.
D_OPEN = 36; // [3:0.1:200]
// Cap height (excluding roof).
H_CAP = 18; // [0:0.1:200]
// Cap border width.
B_CAP = 1.3; // [0.5:0.1:10]
// Cap roof thickness.
T_CAP = 2; // [0.5:0.1:10]
// Grip count.
N_GRIP = 16; // [0:1:360]
// Grip/non-grip ratio (0.5 means same width, 1 means only grips).
R_GRIP = 0.5; // [0:0.05:1]
// Grip total height (including roof).
H_GRIP = 13; // [0:0.1:200]
/* [Hidden] */
// Epsilon for mathematic rounding compensation.
EPS = 0.01;
// Number of fragments.
$fn = 100;

// Grip groove arc length.
AL_GRIP = (PI * (D_OPEN + (2 * B_CAP))) / (N_GRIP / R_GRIP);
// Grip groove outer border offset.
O_GRIP = (pow(AL_GRIP / 2, 2) / (B_CAP / 2));
// Grip groove diameter.
D_GRIP = (B_CAP / 2) + O_GRIP;

assert(H_GRIP <= H_CAP, "Grip height is too large for the given cap height.");

module groove(n) {
	rotate([0, 0, n * 360 / N_GRIP])
	union() {
		hull() {
			translate([(D_OPEN / 2) + ((D_GRIP + B_CAP) / 2), 0, -EPS])
			cylinder(h = H_GRIP - B_CAP + EPS, d = D_GRIP);
			translate([(D_OPEN / 2) + B_CAP, -AL_GRIP / 2, H_GRIP])
			cube([AL_GRIP, AL_GRIP, 0.000001]);
		}
	};
}

// In printing orientation. Rotate by 180° for real orientation.
translate([0, 0, T_CAP]) difference() {
	union() {
		// Outer shell.
		cylinder(h = H_CAP, d = D_OPEN + (2 * B_CAP));
		// Cap roof.
		hull() rotate_extrude(angle = 360, convexity = 2)
		translate([(D_OPEN / 2) + B_CAP - T_CAP, 0, 0])
		difference() {
			circle(T_CAP);
			translate([-T_CAP - EPS, EPS, 0]) square([2 * (T_CAP + EPS), T_CAP + EPS]);
		}
	}
	// Inner thread.
	metric_thread(diameter = D_OPEN, length = H_CAP + EPS, pitch = P_THREAD, internal = true, n_starts = N_THREAD, square = (A_THREAD <= 0), angle = A_THREAD, thread_size = P_THREAD);
	// Inner hole.
	cylinder(h = H_CAP + EPS, d1 = D_CLOSED, d2 = D_OPEN);
	// Grip grooves.
	if (N_GRIP > 0) {
		for (n = [1:1:N_GRIP]) translate([0, 0, -B_CAP]) groove(n);
	}
}

/*
 * ISO-standard metric threads, following this specification:
 *          http://en.wikipedia.org/wiki/ISO_metric_screw_thread
 *
 * Copyright 2020 Dan Kirshner - dan_kirshner@yahoo.com
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * See <http://www.gnu.org/licenses/>.
 *
 * Version 2.5.  2020-04-11  Leadin option works for internal threads.
 * Version 2.4.  2019-07-14  Add test option - do not render threads.
 * Version 2.3.  2017-08-31  Default for leadin: 0 (best for internal threads).
 * Version 2.2.  2017-01-01  Correction for angle; leadfac option.  (Thanks to
 *                           Andrew Allen <a2intl@gmail.com>.)
 * Version 2.1.  2016-12-04  Chamfer bottom end (low-z); leadin option.
 * Version 2.0.  2016-11-05  Backwards compatibility (earlier OpenSCAD) fixes.
 * Version 1.9.  2016-07-03  Option: tapered.
 * Version 1.8.  2016-01-08  Option: (non-standard) angle.
 * Version 1.7.  2015-11-28  Larger x-increment - for small-diameters.
 * Version 1.6.  2015-09-01  Options: square threads, rectangular threads.
 * Version 1.5.  2015-06-12  Options: thread_size, groove.
 * Version 1.4.  2014-10-17  Use "faces" instead of "triangles" for polyhedron
 * Version 1.3.  2013-12-01  Correct loop over turns -- don't have early cut-off
 * Version 1.2.  2012-09-09  Use discrete polyhedra rather than linear_extrude ()
 * Version 1.1.  2012-09-07  Corrected to right-hand threads!
 */

// Examples.
//
// Standard M8 x 1.
// metric_thread (diameter=8, pitch=1, length=4);

// Square thread.
// metric_thread (diameter=8, pitch=1, length=4, square=true);

// Non-standard: long pitch, same thread size.
//metric_thread (diameter=8, pitch=4, length=4, thread_size=1, groove=true);

// Non-standard: 20 mm diameter, long pitch, square "trough" width 3 mm,
// depth 1 mm.
//metric_thread (diameter=20, pitch=8, length=16, square=true, thread_size=6,
//               groove=true, rectangle=0.333);

// English: 1/4 x 20.
//english_thread (diameter=1/4, threads_per_inch=20, length=1);

// Tapered.  Example -- pipe size 3/4" -- per:
// http://www.engineeringtoolbox.com/npt-national-pipe-taper-threads-d_750.html
// english_thread (diameter=1.05, threads_per_inch=14, length=3/4, taper=1/16);

// Thread for mounting on Rohloff hub.
//difference () {
//   cylinder (r=20, h=10, $fn=100);
//
//   metric_thread (diameter=34, pitch=1, length=10, internal=true, n_starts=6);
//}

// ----------------------------------------------------------------------------
function segments (diameter) = min (50, max (ceil (diameter*6), 25));

// ----------------------------------------------------------------------------
// diameter -    outside diameter of threads in mm. Default: 8.
// pitch    -    thread axial "travel" per turn in mm.  Default: 1.
// length   -    overall axial length of thread in mm.  Default: 1.
// internal -    true = clearances for internal thread (e.g., a nut).
//               false = clearances for external thread (e.g., a bolt).
//               (Internal threads should be "cut out" from a solid using
//               difference ()).  Default: false.
// n_starts -    Number of thread starts (e.g., DNA, a "double helix," has
//               n_starts=2).  See wikipedia Screw_thread.  Default: 1.
// thread_size - (non-standard) axial width of a single thread "V" - independent
//               of pitch.  Default: same as pitch.
// groove      - (non-standard) true = subtract inverted "V" from cylinder
//                (rather thanadd protruding "V" to cylinder).  Default: false.
// square      - true = square threads (per
//               https://en.wikipedia.org/wiki/Square_thread_form).  Default:
//               false.
// rectangle   - (non-standard) "Rectangular" thread - ratio depth/(axial) width
//               Default: 0 (standard "v" thread).
// angle       - (non-standard) angle (deg) of thread side from perpendicular to
//               axis (default = standard = 30 degrees).
// taper       - diameter change per length (National Pipe Thread/ANSI B1.20.1
//               is 1" diameter per 16" length). Taper decreases from 'diameter'
//               as z increases.  Default: 0 (no taper).
// leadin      - 0 (default): no chamfer; 1: chamfer (45 degree) at max-z end;
//               2: chamfer at both ends, 3: chamfer at z=0 end.
// leadfac     - scale of leadin chamfer length (default: 1.0 = 1/2 thread).
// test        - true = do not render threads (just draw "blank" cylinder).
//               Default: false (draw threads).
module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1,
                      thread_size=-1, groove=false, square=false, rectangle=0,
                      angle=30, taper=0, leadin=0, leadfac=1.0, test=false)
{
   // thread_size: size of thread "V" different than travel per turn (pitch).
   // Default: same as pitch.
   local_thread_size = thread_size == -1 ? pitch : thread_size;
   local_rectangle = rectangle ? rectangle : 1;

   n_segments = segments (diameter);
   h = (test && ! internal) ? 0 : (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size / (2 * tan(angle));

   h_fac1 = (square || rectangle) ? 0.90 : 0.625;

   // External thread includes additional relief.
   h_fac2 = (square || rectangle) ? 0.95 : 5.3/8;

   tapered_diameter = diameter - length*taper;

   difference () {
      union () {
         if (! groove) {
            if (! test) {
               metric_thread_turns (diameter, pitch, length, internal, n_starts,
                                    local_thread_size, groove, square, rectangle, angle,
                                    taper);
            }
         }

         difference () {

            // Solid center, including Dmin truncation.
            if (groove) {
               cylinder (r1=diameter/2, r2=tapered_diameter/2,
                         h=length, $fn=n_segments);
            } else if (internal) {
               cylinder (r1=diameter/2 - h*h_fac1, r2=tapered_diameter/2 - h*h_fac1,
                         h=length, $fn=n_segments);
            } else {

               // External thread.
               cylinder (r1=diameter/2 - h*h_fac2, r2=tapered_diameter/2 - h*h_fac2,
                         h=length, $fn=n_segments);
            }

            if (groove) {
               if (! test) {
                  metric_thread_turns (diameter, pitch, length, internal, n_starts,
                                       local_thread_size, groove, square, rectangle,
                                       angle, taper);
               }
            }
         }

         // Internal thread lead-in: take away from external solid.
         if (internal) {

            // "Negative chamfer" z=0 end if leadin is 2 or 3.
            if (leadin == 2 || leadin == 3) {
               cylinder (r1=diameter/2, r2=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
                         $fn=n_segments);
            }

            // "Negative chamfer" z-max end if leadin is 1 or 2.
            if (leadin == 1 || leadin == 2) {
               translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) {
                  cylinder (r1=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
                            r2=tapered_diameter/2,
                            $fn=n_segments);
               }
            }
         }
      }

      if (! internal) {

         // Chamfer z=0 end if leadin is 2 or 3.
         if (leadin == 2 || leadin == 3) {
            difference () {
               cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);

               cylinder (r2=diameter/2, r1=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
                         $fn=n_segments);
            }
         }

         // Chamfer z-max end if leadin is 1 or 2.
         if (leadin == 1 || leadin == 2) {
            translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) {
               difference () {
                  cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);

                  cylinder (r1=tapered_diameter/2, r2=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
                            $fn=n_segments);
               }
            }
         }
      }
   }
}

// ----------------------------------------------------------------------------
// Input units in inches.
// Note: units of measure in drawing are mm!
module english_thread (diameter=0.25, threads_per_inch=20, length=1,
                      internal=false, n_starts=1, thread_size=-1, groove=false,
                      square=false, rectangle=0, angle=30, taper=0, leadin=0,
                      leadfac=1.0, test=false)
{
   // Convert to mm.
   mm_diameter = diameter*25.4;
   mm_pitch = (1.0/threads_per_inch)*25.4;
   mm_length = length*25.4;

   echo (str ("mm_diameter: ", mm_diameter));
   echo (str ("mm_pitch: ", mm_pitch));
   echo (str ("mm_length: ", mm_length));
   metric_thread (mm_diameter, mm_pitch, mm_length, internal, n_starts,
                  thread_size, groove, square, rectangle, angle, taper, leadin,
                  leadfac, test);
}

// ----------------------------------------------------------------------------
module metric_thread_turns (diameter, pitch, length, internal, n_starts,
                            thread_size, groove, square, rectangle, angle,
                            taper)
{
   // Number of turns needed.
   n_turns = floor (length/pitch);

   intersection () {

      // Start one below z = 0.  Gives an extra turn at each end.
      for (i=[-1*n_starts : n_turns+1]) {
         translate ([0, 0, i*pitch]) {
            metric_thread_turn (diameter, pitch, internal, n_starts,
                                thread_size, groove, square, rectangle, angle,
                                taper, i*pitch);
         }
      }

      // Cut to length.
      translate ([0, 0, length/2]) {
         cube ([diameter*3, diameter*3, length], center=true);
      }
   }
}

// ----------------------------------------------------------------------------
module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size,
                           groove, square, rectangle, angle, taper, z)
{
   n_segments = segments (diameter);
   fraction_circle = 1.0/n_segments;
   for (i=[0 : n_segments-1]) {
      rotate ([0, 0, i*360*fraction_circle]) {
         translate ([0, 0, i*n_starts*pitch*fraction_circle]) {
            //current_diameter = diameter - taper*(z + i*n_starts*pitch*fraction_circle);
            thread_polyhedron ((diameter - taper*(z + i*n_starts*pitch*fraction_circle))/2,
                               pitch, internal, n_starts, thread_size, groove,
                               square, rectangle, angle);
         }
      }
   }
}

// ----------------------------------------------------------------------------
module thread_polyhedron (radius, pitch, internal, n_starts, thread_size,
                          groove, square, rectangle, angle)
{
   n_segments = segments (radius*2);
   fraction_circle = 1.0/n_segments;

   local_rectangle = rectangle ? rectangle : 1;

   h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size / (2 * tan(angle));
   outer_r = radius + (internal ? h/20 : 0); // Adds internal relief.
   //echo (str ("outer_r: ", outer_r));

   // A little extra on square thread -- make sure overlaps cylinder.
   h_fac1 = (square || rectangle) ? 1.1 : 0.875;
   inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with
                                // cylinder.

   translate_y = groove ? outer_r + inner_r : 0;
   reflect_x   = groove ? 1 : 0;

   // Make these just slightly bigger (keep in proportion) so polyhedra will
   // overlap.
   x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02;
   x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02;
   z_incr = n_starts * pitch * fraction_circle * 1.005;

   /*
    (angles x0 and x3 inner are actually 60 deg)

                          /\  (x2_inner, z2_inner) [2]
                         /  \
   (x3_inner, z3_inner) /    \
                  [3]   \     \
                        |\     \ (x2_outer, z2_outer) [6]
                        | \    /
                        |  \  /|
             z          |[7]\/ / (x1_outer, z1_outer) [5]
             |          |   | /
             |   x      |   |/
             |  /       |   / (x0_outer, z0_outer) [4]
             | /        |  /     (behind: (x1_inner, z1_inner) [1]
             |/         | /
    y________|          |/
   (r)                  / (x0_inner, z0_inner) [0]

   */

   x1_outer = outer_r * fraction_circle * 2 * PI;

   z0_outer = (outer_r - inner_r) * tan(angle);
   //echo (str ("z0_outer: ", z0_outer));

   //polygon ([[inner_r, 0], [outer_r, z0_outer],
   //        [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]);
   z1_outer = z0_outer + z_incr;

   // Give internal square threads some clearance in the z direction, too.
   bottom = internal ? 0.235 : 0.25;
   top    = internal ? 0.765 : 0.75;

   translate ([0, translate_y, 0]) {
      mirror ([reflect_x, 0, 0]) {

         if (square || rectangle) {

            // Rule for face ordering: look at polyhedron from outside: points must
            // be in clockwise order.
            polyhedron (
               points = [
                         [-x_incr_inner/2, -inner_r, bottom*thread_size],         // [0]
                         [x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1]
                         [x_incr_inner/2, -inner_r, top*thread_size + z_incr],    // [2]
                         [-x_incr_inner/2, -inner_r, top*thread_size],            // [3]

                         [-x_incr_outer/2, -outer_r, bottom*thread_size],         // [4]
                         [x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5]
                         [x_incr_outer/2, -outer_r, top*thread_size + z_incr],    // [6]
                         [-x_incr_outer/2, -outer_r, top*thread_size]             // [7]
                        ],

               faces = [
                         [0, 3, 7, 4],  // This-side trapezoid

                         [1, 5, 6, 2],  // Back-side trapezoid

                         [0, 1, 2, 3],  // Inner rectangle

                         [4, 7, 6, 5],  // Outer rectangle

                         // These are not planar, so do with separate triangles.
                         [7, 2, 6],     // Upper rectangle, bottom
                         [7, 3, 2],     // Upper rectangle, top

                         [0, 5, 1],     // Lower rectangle, bottom
                         [0, 4, 5]      // Lower rectangle, top
                        ]
            );
         } else {

            // Rule for face ordering: look at polyhedron from outside: points must
            // be in clockwise order.
            polyhedron (
               points = [
                         [-x_incr_inner/2, -inner_r, 0],                        // [0]
                         [x_incr_inner/2, -inner_r, z_incr],                    // [1]
                         [x_incr_inner/2, -inner_r, thread_size + z_incr],      // [2]
                         [-x_incr_inner/2, -inner_r, thread_size],              // [3]

                         [-x_incr_outer/2, -outer_r, z0_outer],                 // [4]
                         [x_incr_outer/2, -outer_r, z0_outer + z_incr],         // [5]
                         [x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6]
                         [-x_incr_outer/2, -outer_r, thread_size - z0_outer]    // [7]
                        ],

               faces = [
                         [0, 3, 7, 4],  // This-side trapezoid

                         [1, 5, 6, 2],  // Back-side trapezoid

                         [0, 1, 2, 3],  // Inner rectangle

                         [4, 7, 6, 5],  // Outer rectangle

                         // These are not planar, so do with separate triangles.
                         [7, 2, 6],     // Upper rectangle, bottom
                         [7, 3, 2],     // Upper rectangle, top

                         [0, 5, 1],     // Lower rectangle, bottom
                         [0, 4, 5]      // Lower rectangle, top
                        ]
            );
         }
      }
   }
}
```


[User]
Hey, I'm looking to create a customizable, rugged-style box with latches, and I need some help with OpenSCAD. I've been playing with an existing design called "Penguin Case" and I like how it's structured, but I'd prefer to build it from the ground up for my own purposes.

Essentially, I need a box that has two main halves, a top and bottom, that can be joined. I'd like to control the interior dimensions of the box, specifically the height, width, and depth of the cavity. I also need to specify the height at which the two halves meet, and the overall thickness of the seal area where the two halves mate. The design should accommodate either a flat mating surface or one with a square channel for an o-ring type seal, and the number of latches, either one or two. I also want control over the radius of the inside edges of the box.

A few more things: I want to be able to specify how far the latches stick out from the box and the thickness of the box's walls. I need the ability to define the vertical distance between mounting holes on the latches, and their inner width to match certain clasps I have already. Finally, I would like some way to have ribs on the outside to add strength.

For libraries, I'd appreciate it if you could incorporate a library for the build plate that is used in the Penguin Case as it seems to be used for display purposes only, as well as a way to specify the printer size for which the project is intended. I would prefer it if you could write the code with the expectation that I will only want to render one of the halves of the box, or both of the halves, arranged in a way that they could be 3D printed on a single print bed at the same time. Also, I'd like the rendering for the top half to have a feature like `box_top_printable` in the Penguin Case, where the top is essentially flipped in the Z-axis so that it could be printed with the inside facing up.

I would like the project to be well commented and easy to understand.

[Assistant]
penguin_case.scad:
```
// Penguin Case - customizable "rugged" box
// Christopher Bero <bigbero@gmail.com>

// Only for Thingiverse Customizer
use <utils/build_plate.scad>

/* [Basic] */

part = "both"; // [top:Top Half, bottom: Bottom Half, both: Top and Bottom arranged]

// Box dimensions dictate the internal volume.

// Box's cavity height (z)
box_height = 50;
// Box's cavity width (x)
box_width = 100;
// Box's cavity depth (y)
box_depth = 40;
// Height (z) of seam between bottom and top halves
seal_height = 35;
// Number of latches
latch_num = 2; // [1, 2]
// Style of seal surface
seal_type = "flat"; // [channel_square:Square Channel,flat:Flat]

/* [Advanced] */

//
// OpenSCAD settings
//

// Min angle. OpenSCAD default is 12.
$fa = 6;
// Min face size. OpenSCAD default is 2.
$fs = 0.5;

// Inside radius of box edges
inner_radius = 5;
// Height (z) of seal
seal_thickness = 10;
// Distance latch protrudes from exterior of box
latch_outset = 10;
// Wall thickness
wall = 2;
// Distance between latch mounting holes. 1 inch matches existing clasp designs.
latch_height = 25.4;
// Inner latch width, matches existing designs.
latch_width = 12.7;

// Validate variables
assert(box_height > 0);
assert(box_width > 0);
assert(box_depth > 0);
assert(seal_height > 0);

// Exterior wall dimensions
ext_w = box_width + (wall*2);
ext_d = box_depth + (wall*2);
ext_h = box_height + (wall*2);

// Render Thingiverse Customizer build plate
//for display only, doesn't contribute to final object
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]

//when Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 200; //[100:400]

//when Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 180; //[100:400]

build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

// Creates rounded cube for rough shape of case.
module box_core() {
	difference() {
		translate([0, 0, (ext_h/2)]) {
			minkowski() {
				cube([ext_w-(inner_radius*2), ext_d-(inner_radius*2), ext_h-(inner_radius)], center=true);
				sphere(inner_radius);
			}
		}
		translate([0, 0, -(inner_radius/2)])
			cube([ext_w*2, ext_d*2, inner_radius], center = true);
		translate([0, 0, (box_height + (wall*2)) + (inner_radius/2)])
			cube([box_width*2, box_depth*2, inner_radius], center=true);
	}
}

module box_seal() {
	c_h = 2; // cylinder height - minkowski helper variable
	union() {
		box_core();
		translate([0, 0, seal_height]) {
			hull() {
				minkowski() {
					cube([ext_w-(inner_radius*2), ext_d-(inner_radius*2), seal_thickness - c_h], center=true);
					cylinder(c_h, r=(inner_radius + wall), center=true);
				}
				minkowski() {
					cube([ext_w-(inner_radius*2), ext_d-(inner_radius*2), (seal_thickness + (wall * 2) - c_h)], center=true);
					cylinder(c_h, r=(inner_radius), center=true);
				}
			}
		}
	}
}

module box_ribs() {
	w = (box_width+(wall*2));
	h = (box_height + (wall*2));
	union() {
		box_seal();
		for (i = [-1:2:1]) {
			translate([0, (-1*i)*(box_depth/4), h/2]) {
				hull() {
					cube([w + (wall*2), wall, h - (wall*2)], center=true);
					cube([w, wall*2, h], center=true);
				}
			}
		}
	}
}

module latch(x_offset = 0) {
	h = box_height + (wall*2);
	translate([x_offset, 0, 0]) {
		for (i = [-1:2:1]) {
		translate([((latch_width/2)+(wall+0.5))*i, 0, 0]) { // mirror left/right
			difference() {
				translate([0 ,0, h/2])
					hull() { // Latch rib
						cube([wall*2, box_depth + latch_outset, h], center=true);
						cube([wall, box_depth + (latch_outset*2), h-latch_outset], center=true);
					}
				for (r = [0:1]) { // Latch mounting holes
					rotate([0, 0, 180*r]) // mirror front/back of case
						translate([0, ((box_depth+(latch_outset*2))/2)-((3/2)+2), seal_height])
							for (y = [-1:2:1]) {
								translate([0, 0, (latch_height/2)*y]) // mirror bottom/top
									rotate([0, 90, 0])
										cylinder(h=wall*3, d=3, center=true);
							}
				}
				for (d = [-1:2:1]) { // Latch hinge cutouts
					translate([0, ((box_depth+latch_outset*2)/2)*d, seal_height])
						rotate([45, 0, 0])
							cube([wall*4, wall, wall], center=true);
				}
			}
		}
		}
	}
}

module box_latches() {
	union() {
		box_ribs();
		if (latch_num == 1) {
			latch(0);
		}
		else {
			latch(-(box_width/4));
			latch(box_width/4);
		}
	}
}

module box_cavity() {
	difference() {
		box_latches();
		translate([0, 0, (box_height/2)+wall]) {
			minkowski() {
				cube([box_width-(inner_radius*2), box_depth-(inner_radius*2), box_height-(inner_radius*2)], center=true);
				sphere(r=inner_radius);
			}
		}
	}
}

module box_bottom() {
	difference() {
		box_cavity();
		translate([0, 0, (box_height/2)+seal_height])
			cube([box_width*2, box_depth*2, box_height], center=true);
	}
}

module box_top() {
	difference() {
		box_cavity();
		translate([0, 0, -(box_height/2)+seal_height])
			cube([box_width*2, box_depth*2, box_height], center=true);
	}
}

module box_bottom_oring() {
	difference() {
		box_bottom();
		translate([0, 0, seal_height + (seal_thickness/4)])
		difference() {
			minkowski() {
				cylinder_h = 2;
				cube([box_width-(inner_radius*2), box_depth-(inner_radius*2), seal_thickness - cylinder_h], center=true);
				cylinder(cylinder_h, r=(inner_radius + (wall*2) - 1), center=true);
			}
			minkowski() {
				cylinder_h = 2;
				cube([box_width-(inner_radius*2), box_depth-(inner_radius*2), seal_thickness - cylinder_h], center=true);
				cylinder(cylinder_h, r=(inner_radius+1), center=true);
			}
		}
	}
}

module box_top_oring() {
	union() {
		box_top();
		translate([0, 0, seal_height])
		difference() {
			minkowski() {
				cylinder_h = 1;
				cube([box_width-(inner_radius*2), box_depth-(inner_radius*2), (seal_thickness/4) - cylinder_h], center=true);
				cylinder(cylinder_h, r=(inner_radius + (wall*2) - 1.25), center=true);
			}
			minkowski() {
				cylinder_h = 2;
				cube([box_width-(inner_radius*2), box_depth-(inner_radius*2), (seal_thickness/4) - cylinder_h + 0.1], center=true);
				cylinder(cylinder_h, r=(inner_radius+1.25), center=true);
			}
		}
	}
}

module box_top_printable() {
		rotate([180, 0, 0]) {
			translate([0, 0, -ext_h])
				children();
		}
}

module box_arrangement() {
	translate([-((box_width/2)+10), 0, 0]) {
		if (seal_type == "flat") {
			box_bottom();
		}
		if (seal_type == "channel_square") {
			box_bottom_oring();
		}
	}
	translate([(box_width/2)+10, 0, 0]) {
		box_top_printable() {
			if (seal_type == "flat") {
				box_top();
			}
			if (seal_type == "channel_square") {
				box_top_oring();
			}
		}
	}
}

module box_customizer() {
	if (part == "top") {
		box_top_printable() {
			if (seal_type == "flat") {
				box_top();
			}
			if (seal_type == "channel_square") {
				box_top_oring();
			}
		}
	}
	if (part == "bottom") {
		if (seal_type == "flat") {
			box_bottom();
		}
		if (seal_type == "channel_square") {
			box_bottom_oring();
		}
	}
	if (part == "both") {
		box_arrangement();
	}
}

//box_core();
//box_seal();
//box_ribs();
//box_latches();
//box_cavity();
//box_bottom();
//box_top();
//box_bottom_oring();
//box_top_oring();
//box_arrangement();
box_customizer();

```


[User]
Okay, I've got this idea for a parametric crystal generator in OpenSCAD based on Big Clive's video about making custom crystal lamps. I'd like to be able to create all sorts of crystal shapes, from simple quartz-like ones to more complex, brilliant-cut diamonds. I've got some specific controls in mind, which I'll describe below.

Here are the main features I want to control:

**Overall Size:**

*   `size`: The overall height of the crystal, excluding any rims, in millimeters. This should be a variable I can adjust (e.g., from 10mm to 300mm).
*   `base`: The outer diameter of the base of the crystal that will sit on the print bed in millimeters.
*   `btm_rim`: A boolean to add a rim on the bottom.
*   `rim_height`: The height of the bottom rim in millimeters.
*  `top_rim`: A boolean to add a rim on the top.
*  `top_rim_diameter`: The outer diameter of the top rim in millimeters.
*  `top_rim_height`: The height of the top rim in millimeters.

**Shape of the Crystal:**

*   `scaling_top`: A percentage value to control the relative height of the top girdle (where the top part of the crystal transitions to the middle), with 100 being the full height and lower values making it shorter.
*   `scaling_btm`: A similar percentage value for the bottom girdle, allowing me to adjust the height of the lower transition, and has to be less than or equal to the top.
*   `scaling_top_width`: Relative width of the top girdle, expressed as a percentage. This will control the horizontal size of the top transition relative to the rest of the shape. It only needs to be used if the `scaling_top` is different from the `scaling_btm`.
*   `scaling_btm_width`: Relative width of the bottom girdle, as a percentage, controlling how wide it is. Has to be greater than the `base`.

**Faceting:**

*   `gem_facets`: The number of facets around the crystal, like for a cut gemstone.
*   `scaling_table`: A percentage value to control the size of the flat table facet at the top of the crystal.
*   `brilliant_cut`: A boolean to enable a more detailed, brilliant-cut diamond shape. This would add more facets to the crown and pavilion of the diamond.
*   `brillian_crown_main_facet_angle`: An angle parameter, from 0 to 50 degrees, that dictates how steep the main facet cut of the brilliant-cut crown will be.

**Alternative Spherical Top:**

*   `spherical`: A boolean to create a spherical top on the crystal rather than facets. This will be an alternative to faceting the top.

**Hollowing Option:**

*  `hollow`: A boolean to create a hollow core. This will reduce material usage and allow the creation of crystal lamp shades.
*   `wall`: The thickness of the walls of the hollowed crystal in millimeters.

**Other Features:**

*   `xray`: A boolean to cut away half of the model for x-ray view.
*   `circle_fragments`: Number of fragments used to draw the top and bottom rim, must be higher than 3, a higher value results in a smoother model.

I'd like the code to handle all the necessary calculations, like the positions of the girdles, the slopes of the facets, and everything needed to produce a correct 3D model.

I think this should cover most of it. Let me know if you have any questions! I'm hoping this will give me a lot of flexibility in making my own unique crystal designs. I don't anticipate using any external libraries.

[Assistant]
diamond.scad:
```
// a crystal/diamond generator based on the idea and script from:
// bigclivedotcom "Make EPIC custom crystal lamps" video
// see https://youtu.be/HZKu9QMN5xw
// I expanded and merged the capabilities, shapes and configuration.
// It is easy now to print all kinds of chrystals and even really preatty diamonds.

/* [size] */
// height of gem excluding rims in mm
size = 100; // [10:300]
// outer diameter of base touching bed in mm
base = 40.1;
// put a rim on bottom
btm_rim = true;
// height of rim at base in mm
rim_height = 3.5;
// put a rim on top
top_rim = false;
// outer diameter of top rim in mm
top_rim_diameter = 40.1;
// height of rim at top in mm
top_rim_height = 3.5;

/* [shape] */
// relative height of top girdle
scaling_top = 50; //[1:100]
// relative height of bottom girdle (<= top)
scaling_btm = 45; //[0:100]
// relative width of top girdle (if scaling top != btm)
scaling_top_width = 100; //[1:300]
// relative width of bottom girdle (> base)
scaling_btm_width = 100; //[0:300]

/* [faceting] */
// Number of facets
gem_facets = 8; // [3:100]
// Ratio of top to cut of to create table
scaling_table = 50; //[0:100]
// brilliant diamond cut (more details and polygons)
brilliant_cut = false;
// 0 = square
brillian_crown_main_facet_angle = 15; //[0:50]

/* [sphere] */
// create a spherical top instead of a faceted gem
spherical = false;

/* [hollow print] */
//create hollow core
hollow = false;
//wall thickness (default 0.5 mm)
wall = 0.5;

/* [other] */
//x-ray (half gem)
xray = false;
//rim facets
circle_fragments = 100; //[3:200]

/* [Hidden] */

// calculate a lot of helper variables
rim = btm_rim ? rim_height : 0;
gem_height_top = size+rim;
model_height = top_rim ? gem_height_top+top_rim_height : gem_height_top;
// girdle height/width table cut compensations
top_girdle_height_base = size*pct(scaling_top);
table_cut_height = (size-top_girdle_height_base)*pct(scaling_table);
table_cut_multi = 1+table_cut_height/(size-table_cut_height);
effective_size = size*table_cut_multi;
// radius
r_base = base / 2;
// bottom z gets infinite if bottom is smaller than base
r_bottom_temp = pct(scaling_btm_width)*effective_size/2;
r_bottom = r_bottom_temp <= r_base ? r_base+1 : r_bottom_temp;
// top radius is bottom radius for single girdle
r_top = scaling_btm >= scaling_top ? r_bottom : pct(scaling_top_width)*effective_size/2;
// absolute internal top z of model
top_z = effective_size+rim; // add rim as offset
// girdle positions and width
girdle_height_top = top_z-effective_size*ipct(scaling_top);
girdle_width_top = 2*r_top;
scaling_btm_fixed = scaling_btm > scaling_top ? scaling_top : scaling_btm;
girdle_height_btm = top_z-effective_size*ipct(scaling_btm_fixed);
girdle_width_btm = 2*r_bottom;
// table cut
// height between top girdle and top of gem
top_height = gem_height_top-girdle_height_top;
table_x = ((top_height+girdle_height_top) - top_z) / slope(r_top, girdle_height_top, 0, top_z);
// outer shape as points
p_rim_top = [top_rim_diameter/2, 0, gem_height_top+top_rim_height];
p_table = [table_x, 0, gem_height_top];
p_mid_top = [r_top, 0, girdle_height_top];
p_mid_btm = [r_bottom, 0, girdle_height_btm];
// brilliant cut needs table for math
brilliant = brilliant_cut && scaling_table > 0;
facets = spherical ? circle_fragments : gem_facets;
// target z for bottom cone
bottom_z = rim - (r_base*(girdle_height_btm-rim))/(r_bottom-r_base);
// brilliant bottom z offset
brillant_z = !brilliant ? 0
        : intersectXZ(p_mid_btm,
                mid(rotateZ(mid(p_mid_btm, [0, 0, bottom_z]), 360/facets/2),
                    rotateZ(mid(p_mid_btm, [0, 0, bottom_z]), -360/facets/2)),
                [0,0,0], [0,0,1]).z;

/*
    create a more detailed crown imitating a brillant cut diamond
*/
module brilliant_top() {
    // calculate the simple koordinates of the cuts
    mid = mid([r_top, 0, girdle_height_top], [table_x, 0, gem_height_top]);
    sqr_t = [table_x, 0, gem_height_top];
    sqr_b = [r_top, 0, girdle_height_top];
    tr_lb = rotateZ(sqr_b, 360/facets/2);
    tr_rb = [tr_lb.x, -tr_lb.y, tr_lb.z];
    tr_ltx = rotateZ(sqr_t, 360/facets);
    tr_lt = mid(sqr_t, tr_ltx);
    tr_rt = [tr_lt.x, -tr_lt.y, tr_lt.z];
    // now calculate point where the line from mid intersects the next half-facet plane
    // https://math.stackexchange.com/a/100447
    o = mid;
    // rotate Y-direction up 15° for kite shape
    d = rotateV([0,-10,0], brillian_crown_main_facet_angle, sqr_t-mid);
    n = plane([0,0,0], [0,0,10], tr_lb);
    // t = (a.x*n.x+a.z*n.z+a.y*n.y-n.x*o.x-n.z*o.z-n.y*o.y)/(d.x*n.x+d.z*n.z+d.y*n.y);
    // removed zeros
    t = (-n.x*o.x-n.z*o.z-n.y*o.y)/(d.x*n.x+d.z*n.z+d.y*n.y);
    sqr_l = [o.x+d.x*t, o.y+d.y*t, o.z+d.z*t];
    sqr_r = [sqr_l.x, -sqr_l.y, sqr_l.z];

    // duplication rotation of the facet
    rotation(facets)
    polyhedron(
        points=[ [0,0,girdle_height_top], tr_lb, sqr_b, tr_rb, // 0,1,2,3
                sqr_l, sqr_r, // 4,5
                [0,0,gem_height_top], tr_lt, sqr_t, tr_rt ], // 6,7,8,9
        faces=[ [0,1,2,3],[9,8,7,6], // bottom, top
                [6,7,4,1,0], [0,3,5,9,6], // left, right
                [8,5,2,4], // front rhombus
                [4,2,1], [5,3,2], [7,8,4], [8,9,5]] // front 4 triangles
    );
}

/*
    create a more detailed pavilion imitating a brillant cut diamond
*/
module brillant_bottom_cut() {
    mid = mid(p_mid_btm, [0, 0, bottom_z]);
    sqr_t = p_mid_btm;
    sqr_b = [0, 0, bottom_z];
    sqr_l = rotateZ(mid, 360/facets/2);
    sqr_r = [sqr_l.x, -sqr_l.y, sqr_l.z];
    out = [mid.x*2, mid.y, 0];
    mid2 = mid(sqr_l, sqr_r);
    sqr_b2 = intersectXZ(sqr_t, mid2, [0,0,0], [0,0,1]);
    sqr_b2l = [sqr_b2.x, sqr_l.y, sqr_b2.z];
    sqr_b2r = [sqr_b2.x, sqr_r.y, sqr_b2.z];

    rotation(facets)
    polyhedron(
        points=[ sqr_b2l, sqr_l, sqr_t, sqr_r, out, sqr_b2r ],
        faces=[ [5,3,2,1,0], [0,1,4], [1,2,4], [2,3,4], [3,5,4], [5,0,4]]
    );
    cylinder(h=sqr_b2.z, d1=r_bottom, d2=r_bottom, $fn=facets);
}

module top_rim_body() {
    if (top_rim && top_rim_height > 0 && top_rim_diameter > 0) {
        top_dia = top_rim_diameter > girdle_width_top ? girdle_width_top : top_rim_diameter;
        translate([0, 0, girdle_height_top])
        cylinder(h=model_height-girdle_height_top, d1=top_dia, d2=top_dia, $fn=circle_fragments);
    }
}

module btm_rim_body() {
    if (rim > 0) {
        cylinder(h=girdle_height_btm, d1=base, d2=base, $fn=circle_fragments);
    }
}

/*
    combine the parts to create a solid gem/chrystal
*/
module body() {
    difference() {
        union() {
            // upper part
            if (spherical) {
                difference() {
                    translate([0, 0, girdle_height_top])
                    sphere(r_top, $fn=facets);
                    translate([0, 0, girdle_height_top-r_top])
                    cube(r_top*2,center=true);
                }
            }
            else {
                if (brilliant) {
                    brilliant_top();
                }
                else {
                    translate([0, 0, girdle_height_top])
                    cylinder(h=top_height, d1=girdle_width_top, d2=table_x*2, $fn=facets);
                }
            }

            lower_facets = brilliant ? facets*2 : facets;
            // middle part
            if (scaling_btm < scaling_top) {
                mid_height = girdle_height_top-girdle_height_btm;
                translate([0, 0, girdle_height_btm])
                cylinder(h=mid_height, d1=girdle_width_btm, d2=girdle_width_top, $fn=lower_facets);
            }
            // lower part
            translate([0, 0, bottom_z])
            cylinder(h=girdle_height_btm-bottom_z, d1=0, d2=girdle_width_btm, $fn=lower_facets);
        }

        // cut out brillant pavilion facets
        if (brilliant) {
            brillant_bottom_cut();
        }
    }
}

/*
    create copy of model for acurate hollow core
*/
module core() {
    effective_btm_z = brilliant ? brillant_z : bottom_z;
    line_top = p_table-p_mid_top;
    top_offset = normalize([-line_top.z, 0, line_top.x])*wall;
    core_top_start = top_offset+p_mid_top;
    core_top_end = top_offset+p_table;

    line_mid = p_mid_top-p_mid_btm;
    mid_offset = normalize([-line_mid.z, 0, line_mid.x])*wall;
    core_mid_start = mid_offset+p_mid_btm;
    core_mid_end = mid_offset+p_mid_top;

    line_btm = p_mid_top-[0, 0, effective_btm_z];
    btm_offset = normalize([-line_btm.z, 0, line_btm.x])*wall;
    core_btm_start = btm_offset+[0, 0, effective_btm_z];
    core_btm_end = btm_offset+p_mid_btm;

    merged_top = core_top_end.x > 0
            ? intersectXZ(core_top_end, core_top_start, [0,0,gem_height_top-wall], [1,0,gem_height_top-wall])
            : intersectXZ(core_top_end, core_top_start, [0,0,0], [0,0,1]);
    merged_mid_top_pre = scaling_btm < scaling_top
            ? intersectXZ(core_top_end, core_top_start, core_mid_end, core_mid_start)
            : intersectXZ(core_top_end, core_top_start, core_btm_end, core_btm_start);
    merged_mid_btm_pre =  scaling_btm < scaling_top
            ? intersectXZ(core_btm_end, core_btm_start, core_mid_end, core_mid_start)
            : merged_mid_top_pre;
    merged_mid_top = merged_mid_btm_pre.z > merged_mid_top_pre.z
            ? intersectXZ(core_top_end, core_top_start, core_btm_end, core_btm_start)
            : merged_mid_top_pre;
    merged_mid_btm = merged_mid_btm_pre.z > merged_mid_top_pre.z ? merged_mid_top : merged_mid_btm_pre;
    merged_btm = intersectXZ(core_btm_end, core_btm_start, [0,0,0], [0,0,1]);

    // top part
    if (spherical) {
        difference() {
            translate([0, 0, merged_mid_top.z])
            sphere(merged_mid_top.x, $fn=facets);
            translate([0, 0, merged_mid_top.z-merged_mid_top.x])
            cube(merged_mid_top.x*2,center=true);
        }
    }
    else {
        translate([0, 0, merged_mid_top.z])
        cylinder(h=merged_top.z-merged_mid_top.z, d1=max(0, merged_mid_top.x*2), d2=merged_top.x*2, $fn=facets);
    }

    lower_facets = brilliant ? facets*2 : facets;
    // middle part
    if (scaling_btm < scaling_top) {
        translate([0, 0, merged_mid_btm.z])
        cylinder(h=merged_mid_top.z-merged_mid_btm.z, d1=merged_mid_btm.x*2, d2=merged_mid_top.x*2, $fn=lower_facets);
    }

    // lower part
    translate([0, 0, merged_btm.z])
    cylinder(h=merged_mid_btm.z-merged_btm.z, d1=0, d2=merged_mid_btm.x*2, $fn=lower_facets);

    // top rim
    if (top_rim && top_rim_height > 0 && top_rim_diameter > 0) {
        body_top_dia = top_rim_diameter > girdle_width_top ? girdle_width_top : top_rim_diameter;
        top_dia = body_top_dia-2*wall;
        translate([0, 0, merged_mid_top.z])
        cylinder(h=p_rim_top.z-merged_mid_top.z+1, d1=top_dia, d2=top_dia, $fn=circle_fragments);
    }

    // bottom rim
    if (rim > 0) {
        btm_dia = base-2*wall;
        translate([0, 0, -1])
        cylinder(h=merged_mid_btm.z+2, d1=btm_dia, d2=btm_dia, $fn=circle_fragments);
    }
}

/*
    rotation+creation of children around z axis
*/
module rotation(num=8, pathRadius=20) {
    for (azimuth = [0:360/num:359])  {
        rotate([0, 0, azimuth])
            children();
    }
}

/*
    create a transparent debug sphere at point p with color c
*/
module debugPoint(p, c) {
    echo(c, p);
    %translate(p) color(c,0.3) sphere(0.5);
}

/*
    main build: combine everything
*/
difference() {
    union() {
        top_rim_body();
        body();
        btm_rim_body();
    }

    if (hollow) {
        // cut out hollow core
        core();
    }

    max_width = max(girdle_width_top, girdle_width_btm);
    // clean below z=0
    translate([max_width/2, -max_width/2, 0])
    rotate([0, 180, 0])
    cube([max_width, max_width, 2^32]);

    if (xray) {
        translate([0, -max_width/2, top_z/2-1])
        cube([max_width, max_width, top_z+2], center=true);
    }
}

// print 3d-print hints
echo("=== DONE ===");
echo("top slope degree:", angle(r_top, girdle_height_top, 0, top_z));
echo("middle slope degree:", angle(r_bottom, girdle_height_btm, r_top, girdle_height_top));
echo("bottom slope degree:", angle(0, bottom_z, r_bottom, girdle_height_btm));
echo("=== HAVE FUN ===");
echo("Coffee me @ paypal.me/daviddaviddavid ");

/*
    helper function
*/
// slope between two 2d points
function slope(x1, y1, x2, y2) = (y2-y1)/(x2-x1);
// angle between two 2d points
function angle(x1, y1, x2, y2) = 90-abs(atan(slope(x1, y1, x2, y2)));
// distance between two points or origin and a point
function distance(a, b=[0,0,0]) = sqrt(pow(a.x-b.x, 2)+pow(a.y-b.y, 2)+pow((a.z?a.z:0)-(b.z?b.z:0), 2));
// normalize vector
function normalize(a) = concat([a.x/distance(a), a.y/distance(a)], len(a) < 3 ? [] : [a.z/distance(a)]);
// cross product of 3d vectors
function xproduct(a, b) = [
    a.y*b.z-a.z*b.y,
    a.z*b.x-a.x*b.z,
    a.x*b.y-a.y*b.x
];
// rotate vector (point) around origin in direction b
function rotateV(a, theta, b) = cos(theta) * a + sin(theta) * xproduct(normalize(xproduct(a, b)), a);
// rotate clockwise around z axis
function rotateZ(a, theta) = rotateV([a.x, a.y, 0], theta, [a.y, -a.x, 0])+[0, 0, a.z];
// calculate intersection point of two 2d lines
function intersect2d(a1, a2, b3, b4) = [
    ((a1.x*a2.y-a1.y*a2.x)*(b3.x-b4.x)-(a1.x-a2.x)*(b3.x*b4.y-b3.y*b4.x))/((a1.x-a2.x)*(b3.y-b4.y)-(a1.y-a2.y)*(b3.x-b4.x)),
    ((a1.x*a2.y-a1.y*a2.x)*(b3.y-b4.y)-(a1.y-a2.y)*(b3.x*b4.y-b3.y*b4.x))/((a1.x-a2.x)*(b3.y-b4.y)-(a1.y-a2.y)*(b3.x-b4.x))
];
function xzV2(v) = [v.x, v.z];
function v2x0z(v) = [v.x, 0, v.y];
// intersect 3d lines 2D on X-Z plane
function intersectXZ(a1, a2, b3, b4) = v2x0z(intersect2d(xzV2(a1), xzV2(a2), xzV2(b3), xzV2(b4)));

// middle point between two points
function mid(a, b) = (a+b)/2;
// normal vector of plane
function plane(a, b, c) = xproduct(a-b, b-c);
// decimal as percent
function pct(value) = value/100;
// decimal as inverse percent
function ipct(value) = pct(100-value);
```


[User]
Hey, I'm looking to get a parametric drill bit holder designed in OpenSCAD. I've seen a few online, and I'd like one similar, but I want the design to be flexible and customizable, and I don't want to have to rely on Thingiverse's Customizer.

Essentially, I need a rectangular block with hexagonal holes to hold drill bits, and also space for small cylindrical magnets to help keep the bits in place. Here are the key parameters I'd like to control:

*   **Number of Bits:** The number of bits the holder should accommodate.
*   **Holder Height:** The overall height of the rectangular block, this is a rough dimension.
*   **Bit Lip Height:** The height of a lip or raised area on the bottom of the block to keep the bits from falling out.
*   **Magnet Diameter:** The diameter of the magnets, and also a bit of play for the magnet holder.
*   **Magnet Thickness:** The thickness of the magnets to be used.

The holes for the bits should be hexagonal to match typical drill bit shafts. Also, the magnets should be placed next to each bit hole, to help hold the bits in place. The magnet recesses should be centered in their positions. I think a good way to achieve a sturdy design is to use a Minkowski sum with a sphere on a box that is used as the main body of the holder.

I'm hoping for a design where everything is parametric, and easily adjusted. I'm also a beginner with OpenSCAD, so I'm hoping for clear and concise code. No need to use external libraries if it's not necessary. I'd prefer to have the design in a single .scad file, if possible.

[Assistant]
drill_bitholder.scad:
```
/* [Bits] */
// Number of Bits
bitnumber = 6;
// Height of the holder
height = 17;
// Height of the lip holding the bits in place
holdheight = 6;
/* [Magnets] */
// Diameter of the magnet (add a bit of play)
magdiameter = 3;
// Thickness of the magnet
magthickness = 2;

innerdistance = 2*3.4;
depth = max(4, 6.8+(magthickness+1)-4);
$fn=50*1;

difference() {

	minkowski() {
	  cube([depth,10*bitnumber,height-4]);
	  sphere(r=2);
	}
	for (i = [1:bitnumber]) {
		bit(0, i*10-5);
	}

	for (i = [1:bitnumber]) {
		translate([innerdistance/2+magthickness/2-0.01,(i-0.5)*10,height-magdiameter-4]) rotate([0,90,0]) cylinder(d=magdiameter, h=magthickness, center=true);
	}

	translate([-depth,-2,holdheight+2]) cube([depth, 12*bitnumber,height-holdheight]);

}

module bit(x,y) {
	translate([0,0,12.5]) linear_extrude(height=25,center=true) hexagon(3.4,x,y);
}

module hexagon(r,x,y){
polygon(points=[[(r+x),(r*(tan(30)))+y],
                [x,(r*(2/sqrt(3)))+y],
                [-r+x,(r*(tan(30)))+y],
                [-r+x,-(r*(tan(30)))+y],
                [x,-(r*(2/sqrt(3)))+y],
                [r+x,-(r*(tan(30)))+y]]);

 }
```


[User]
I'm looking for a parametric OpenSCAD design for a bit storage tray, something that can be either wall-mounted or used on a workbench. I need it to hold standard hex screwdriver bits at an angle, not straight up.

Here's what I have in mind:

*   **Basic Structure:** The tray should be a rectangular base with hexagonal cutouts to hold the bits. The cutouts should be angled to make it easier to grab the bits.
*   **Parameters:** I'd like to be able to control the following through parameters:
    *   `numberx`: The number of bits in the X direction.
    *   `numbery`: The number of bits in the Y direction.
    *   `bitheight`: The height of the bit to be held (this affects the cutout depth).
    *   `wall`: The thickness of the wall around each cutout, and also the thickness of the base.
    *   `angle`: The angle at which the bits are held (0 being straight up).
    *   `squeeze`: This parameter would move the bit holders closer together.
    *   `border`: A boolean (1 or 0) to specify if there should be a border around the entire tray (necessary for adding screw holes)
    *   `screwdiameter`: The diameter of the screw holes, when `border` is enabled
*   **Cutout Design:** The bit holders should be hexagonal, matching the shape of the bits, with the correct dimensions for standard 1/4" hex bits.
*   **Screw Holes:** If the `border` parameter is set to 1, then there should be screw holes in each corner of the border.
*   **Libraries:** I am expecting that the code will use the standard OpenSCAD library for `linear_extrude`, `translate`, `rotate`, `difference`, `union`, `cube`, `cylinder`, and `polygon` primitives.

I think that covers everything, let me know if there are any questions. I'm looking forward to seeing what you come up with.

[Assistant]
bitstoragewall_angled.scad:
```
$fn=30*1;
// Amount of bits in X direction
numberx = 5;
// Amount of bits in Y direction
numbery = 3;
// Amount of bits in Y direction
bitheight = 9;
// Wall thickness
wall = 1;
// Angle to hold the bits (0 = upright)
angle = 25;
// Moves the bits closer together
squeeze = 0;
// Should there be a border around the tray? (necessary for screws)
border = 1;
// Diameter of the screw holes
screwdiameter = 4;

outerradius = (6.8 + 2* wall) / sqrt(3);
dx = (6.8+2*wall);
dy = (sin(angle)*(bitheight+wall)+cos(angle)*outerradius*2) - squeeze;

thickness = sqrt((outerradius*outerradius*4) - (cos(angle)*outerradius*2)*(cos(angle)*outerradius*2))/2;

difference() {

	union() {

		translate([-0.5*dx - (border*dx),-0.5*dy - (border*dx),-thickness])
			cube([numberx*dx + (border*dx*2), numbery*dy + (border*dx*2),wall+thickness]);

		for (xi = [0:numberx-1]) {
			for (yi = [0:numbery-1]) {
				translate([0,yi*dy,0]) rotate([angle,0,0]) bitshell(xi*dx, 0);
			}
		}

	}

	for (xi = [0:numberx-1]) {
		for (yi = [0:numbery-1]) {
			translate([0,yi*dy,0]) rotate([angle,0,0]) bit(xi*dx, 0);
		}
	}

	translate([-0.5*(dx)-border*dx*0.5,-0.5*(dy)-border*dx*0.5,-5*thickness])
      cylinder(h=10*thickness, d=screwdiameter);

	translate([(numberx-0.5)*(dx)+border*dx*0.5,-0.5*(dy)-border*dx*0.5,-5*thickness])
      cylinder(h=10*thickness, d=screwdiameter);

	translate([-0.5*(dx)-border*dx*0.5,(numbery-0.5)*(dy)+border*dx*0.5,-5*thickness])
      cylinder(h=10*thickness, d=screwdiameter);

	translate([(numberx-0.5)*(dx)+border*dx*0.5,(numbery-0.5)*(dy)+border*dx*0.5,-5*thickness])
      cylinder(h=10*thickness, d=screwdiameter);

}

module bitholder(x,y) {
	rotate([angle,0,0])
	difference() {
		bitshell(x,y);
		bit(x,y);
	}
}

module bit(x,y) {
	translate([0,0,bitheight/2+wall+0.01]) linear_extrude(height=bitheight,center=true) hexagon(3.4,x,y);
}

module bitshell(x,y) {
	translate([0,0,(bitheight+wall)/2]) linear_extrude(height=bitheight+wall,center=true) hexagon(3.4+wall,x,y);
}

module hexagon(r,x,y){
polygon(points=[[(r+x),(r*(tan(30)))+y],
                [x,(r*(2/sqrt(3)))+y],
                [-r+x,(r*(tan(30)))+y],
                [-r+x,-(r*(tan(30)))+y],
                [x,-(r*(2/sqrt(3)))+y],
                [r+x,-(r*(tan(30)))+y]]);

 }

```