[User]
Okay, I'd like a parametric phone case generator in OpenSCAD. I need it to be highly customizable.

First, the basic phone dimensions should be configurable, including:
- phone width (x)
- phone length (y)
- phone thickness (z)
- phone corner radius

The case itself should also have adjustable parameters, like:
- case back cover thickness
- case wall thickness
- case front lip size
- a rounding radius between the back cover and case wall

Next, the case needs to have several configurable slots and openings. I need an ear socket with selectable side (top, bottom, left, or right) and adjustable:
- ear socket diameter
- ear socket x offset from the phone's left edge
- ear socket y offset from the phone's bottom edge
- ear socket z offset from the phone's back cover

I also need:
- Two slots on the back cover, with individual x and y sizes and x and y offsets from the phone's left/bottom edges
- Two slots on the top edge, with individual x and z sizes and x offsets from the phone's left edge and z offsets from back cover.
- Two slots on the left edge, with individual y and z sizes and y offsets from the phone's bottom edge and z offsets from back cover.
- Two slots on the right edge, with individual y and z sizes and y offsets from the phone's bottom edge and z offsets from back cover.
- Three slots on the bottom edge, with individual x and z sizes and x offsets from the phone's left edge and z offsets from back cover.

Additionally, I would like:
- An option to cut array holes on the back cover
- An option to include text on the back cover, with configurable text, x and y location factors.
- The ability to round the corners of the back cover.
- A feature to test print part of the case, with a configurable factor for the location of the test print (top or bottom)

I am using the default OpenSCAD library.

[Assistant]
PhoneCaseR2.scad:
```
////----PhoneCase.scad----
////----OpenSCAD 2015.03-2----
////----2018.7.30----
////----By Kijja Chaovanakij----

////----feature: 1-customizable phone size (x / y / z / corner radius), 2-ear socket with selectable side, 3-two slots on back cover, 4-two slots on top, left, and right side, 5-three slots on bottom side, 6-array holes on back cover, 7-text on back cover, 8-round corner on back cover, 9-test print some part of the case.

/* [var] */
//number of fragments[30=low:40=medium:60=high]
$fn=30;
//fit between case and phone[0.25]
fit_tol=0.25;
//adjust slot clearance[0.8]
slot_cl_adj=0.8;
//adjust the depth of slot, for the slot close to the phone's round corner[0.25-2.00]
slot_dep_adj=0.25;
//cut array holes on the back cover[0=no:1=yes]
array_hole=0;
//text chars on the back cover[""=no text]
chars="Mupa Academy";
//x factor of the text location[-0.3 to 0.3]
text_xlocat_factor=0;
//y factor of the text location[-0.45 to 0.45]
text_ylocat_factor=-0.35;
//factor of the test print part[-0.5 to 0.5 =top or bottom part:0=no test print part]
test_print=0;

/* [phone dimension var] */
//phone width[72.36]
phone_wid=72.36;
//phone length[143.82]
phone_len=143.82;
//phone thickness[9.1]
phone_thk=9.1;
//phone corner radius[5.0]
phone_rad=5.0;

/* [case var] */
//case's back cover thickness[1.2]
case_thk=1.2;
//case's wall thickness[1.0]
case_wall_thk=1.0;
//case's front lip size[1.2]
case_lip=1.2;
//inner rounding radius between back cover and case wall[1.2]
rounding=1.2;

/* [ear socket var] */
//side of ear socket[1=top:2=bottom:3=left:4=right]
es_side=1;
//ear socket diameter[3.6]
es_dia=3.6;
//phone's left edge to ear socket's edge[41.5]
es_xoffset=41.5;
//phone's bottom edge to ear socket's edge[143.8]
es_yoffset=143.8;
//phone's back cover to ear socket's edge[1.3]
es_zoffset=1.3;

/* [back cover var] */
//number of back cover slot[0-2]
bc_num=2;
//back cover slot 1 x size[9.0]
bc1_xsize=9;
//back cover slot 1 y size[17.5]
bc1_ysize=17.5;
//back cover slot 1 x offset from left edge[54.5]
bc1_xoffset=55.0;
//back cover slot 1 y offset from bottom edge[122.8]
bc1_yoffset=122.8;
//back cover slot 2 x size[14.4]
bc2_xsize=14.4;
//back cover slot 2 y size[2.0]
bc2_ysize=2.0;
//back cover slot 2 x offset from left edge[35.0]
bc2_xoffset=35.0;
//back cover slot 2 y offset from bottom edge[12.0]
bc2_yoffset=12.0;

/* [top edge var] */
//number of top edge slot[0-2]
te_num=0;
//top edge slot 1 x size
te1_xsize=4;
//top edge slot 1 z size
te1_zsize=1.4;
//top edge slot 1 x offset from left edge
te1_xoffset=24;
//top edge slot 1 z offset from back cover
te1_zoffset=4;
//top edge slot 2 x size
te2_xsize=4;
//top edge slot 2 z size
te2_zsize=1.4;
//top edge slot 2 x offset from left edge
te2_xoffset=54;
//top edge slot 2 z offset from back cover
te2_zoffset=4;

/* [bottom edge var] */
//number of bottom edge slot[0-3]
be_num=1;
//bottom edge slot 1 x size[12]
be1_xsize=12;
//bottom edge slot 1 z size[2.5]
be1_zsize=2.5;
//bottom edge slot 1 x offset from left edge[53]
be1_xoffset=53;
//bottom edge slot 1 z offset from back cover[3.3]
be1_zoffset=3.3;
//bottom edge slot 2 x size
be2_xsize=12;
//bottom edge slot 2 z size
be2_zsize=2.5;
//bottom edge slot 2 x offset from left edge
be2_xoffset=8;
//bottom edge slot 2 z offset from back cover
be2_zoffset=3.2;
//bottom edge slot 3 x size
be3_xsize=15;
//bottom edge slot 3 z size
be3_zsize=3.6;
//bottom edge slot 3 x offset from left edge
be3_xoffset=28.2;
//bottom edge slot 3 z offset from back cover
be3_zoffset=2.5;

/* [left edge var] */
//number of left edge slot[0-2]
le_num=1;
//left edge slot 1 y size[18.8]
le1_ysize=18.8;
//left edge slot 1 z size[1.3]
le1_zsize=1.3;
//left edge slot 1 y offset from bottom edge[105]
le1_yoffset=105;
//left edge slot 1 z offset from back cover[4.5]
le1_zoffset=4.5;
//left edge slot 2 y size
le2_ysize=4;
//left edge slot 2 z size
le2_zsize=1.4;
//left edge slot 2 y offset from bottom edge
le2_yoffset=24;
//left edge slot 2 z offset from back cover
le2_zoffset=4;

/* [right edge var] */
//number of right edge slot[0-2]
re_num=1;
//right edge slot 1 y size[9.9]
re1_ysize=9.9;
//right edge slot 1 z size[1.3]
re1_zsize=1.3;
//right edge slot 1 y offset from bottom edge[111]
re1_yoffset=111;
//right edge slot 1 z offset from back cover[4.5]
re1_zoffset=4.5;
//right edge slot 2 y size
re2_ysize=4;
//right edge slot 2 z size
re2_zsize=1.4;
//right edge slot 2 y offset from bottom edge
re2_yoffset=24;
//right edge slot 2 z offset from back cover
re2_zoffset=4;

////----translate var----
//center to center of x corner radius
c2c_xwid=phone_wid-phone_rad*2;
//center to center of y corner radius
c2c_ywid=phone_len-phone_rad*2;
//case hight
case_hi=phone_thk+case_thk+case_lip/2;
//top edge slot 1 y offset from bottom edge
te1_yoffset=phone_len;
//top edge slot 2 y offset from bottom edge
te2_yoffset=phone_len;
//right edge slot 1 x offset from left edge
re1_xoffset=phone_wid;
//right edge slot 2 x offset from left edge
re2_xoffset=phone_wid;

////----main----
difference()
{
union(){
  difference(){
    translate([phone_rad,phone_rad,-case_thk])
      hull(){
        for (i=[0,c2c_xwid])
          for (j=[0,c2c_ywid])
            translate([i,j,0])
              roundCylinder(
                phone_thk+case_thk+case_lip/2,
                phone_rad+fit_tol+case_wall_thk,
                rounding+case_wall_thk);
      }

    //cut inside case
    translate([phone_rad,phone_rad,0])
      color("orange")
      hull(){
        for (i=[0,c2c_xwid])
          for (j=[0,c2c_ywid])
            translate([i,j,0])
              roundCylinder(
                phone_thk+case_lip,
                phone_rad+fit_tol,
                rounding);
      }

    //cut ear jack hole
    ear_jack();

    //cut back cover slot
    back_cover();

    //cut top edge slot
    top_edge();

    //cut bottom edge slot
    bottom_edge();

    //cut left edge slot
    left_edge();

    //cut right edge slot
    right_edge();

    //cut back cover array hole
    if (array_hole==1)
    translate([
        phone_wid/2,
        phone_len/2,
        -case_thk-0.1])
      for (i=[-phone_wid/3:phone_wid/6:phone_wid/3])
        for (j=[
            -phone_len/4:
            phone_len/8:
            phone_len/4])
          translate([i,j-3,0])
            scale([1,1.5,1])
              cylinder(2,phone_wid/19,phone_wid/19);

    //cut back cover text
    color("red")
      translate([
          phone_wid/2-phone_wid*text_xlocat_factor,
          phone_len/2+phone_len*text_ylocat_factor,
          -case_thk/2])
        rotate([0,180,0])
          linear_extrude(
              height=case_thk,
              //center=true,
              convexity=10)
            text(
              chars,
              size=phone_wid/10,
              font="LiberationMono-Bold",
              halign="center",
              valign="center");
  }

  //add top edge lip
  translate([
      phone_rad+c2c_xwid/2,
      phone_len+fit_tol,
      phone_thk+case_lip/2])
    rotate([0,0,180])
      resize ([c2c_xwid*2/3,0,0])
        lip();

  //add bottom edge lip
  translate([
      phone_rad+c2c_xwid/2,
      -fit_tol,
      phone_thk+case_lip/2])
    resize ([c2c_xwid*2/3,0,0])
      lip();

  //add left edge lip
  translate([
      -fit_tol,
      phone_rad+c2c_ywid/4,
      phone_thk+case_lip/2])
    rotate([0,0,-90])
      resize ([c2c_ywid/3,0,0])
        lip();
  translate([
      -fit_tol,
      phone_rad+c2c_ywid*3/4,
      phone_thk+case_lip/2])
    rotate([0,0,-90])
      resize ([c2c_ywid/3,0,0])
        lip();

  //add right edge lip
  translate([
      phone_wid+fit_tol,
      phone_rad+c2c_ywid/4,
      phone_thk+case_lip/2])
    rotate([0,0,90])
      resize ([c2c_ywid/3,0,0])
        lip();
  translate([
      phone_wid+fit_tol,
      phone_rad+c2c_ywid*3/4,
      phone_thk+case_lip/2])
    rotate([0,0,90])
      resize ([c2c_ywid/3,0,0])
        lip();
}
////----end main----

//cut test print
if (test_print!=0)
  translate([
      phone_wid/2,
      phone_len/2-phone_len*test_print,
      phone_thk/2])
    cube([
      phone_wid+4,
      phone_len+4,
      phone_thk+4],
      true);
}
//

module lip(){
translate([0,case_lip/2,-case_lip/2])
rotate([-45,0,0])
  difference(){
    rotate([45,0,0])
      cube([1,case_lip,case_lip],true);
    translate([-1,0,-1])
      cube([2,2,2]);
  }
}
//

module right_edge(){
if (re_num>=1)
  translate([
    re1_xoffset+case_wall_thk/2+fit_tol*1,
    re1_yoffset+re1_ysize/2,
    re1_zoffset+re1_zsize/2])
  rotate([0,-90,0])
  xe_slot(
    re1_ysize,
    re1_zsize,
    case_wall_thk+slot_dep_adj);

if (re_num==2)
  translate([
    re2_xoffset+case_wall_thk/2+fit_tol*1,
    re2_yoffset+re2_ysize/2,
    re2_zoffset+re2_zsize/2])
  rotate([0,-90,0])
  xe_slot(
    re2_ysize,
    re1_zsize,
    case_wall_thk+slot_dep_adj);
}
//

module left_edge(){
//left edge slot 1 x offset from left edge
le1_xoffset=0;
//left edge slot 2 x offset from left edge
le2_xoffset=0;

if (le_num>=1)
  translate([
    le1_xoffset-case_wall_thk/2-fit_tol*1,
    le1_yoffset+le1_ysize/2,
    le1_zoffset+le1_zsize/2])
  rotate([0,90,0])
  xe_slot(
    le1_ysize,
    le1_zsize,
    case_wall_thk+slot_dep_adj);

if (le_num==2)
  translate([
    le2_xoffset-case_wall_thk/2-fit_tol*1,
    le2_yoffset+le2_ysize/2,
    le2_zoffset+le2_zsize/2])
  rotate([0,90,0])
  xe_slot(
    le2_ysize,
    le1_zsize,
    case_wall_thk+slot_dep_adj);
}
//

module xe_slot(ysize,zsize,thick){
hull(){
  for (i=[-ysize/2+zsize/2,ysize/2-zsize/2])
    translate([0,i,0])
    cylinder(
      thick,
      zsize/2+slot_cl_adj*3.2,
      zsize/2+slot_cl_adj*1.6,
      true);
 }
}
//

module bottom_edge(){
//bottom edge slot 1 y offset from bottom edge
be1_yoffset=0;
//bottom edge slot 2 y offset from bottom edge
be2_yoffset=0;
//bottom edge slot 3 y offset from bottom edge
be3_yoffset=0;

if (be_num>=1)
  translate([
    be1_xoffset+be1_xsize/2,
    be1_yoffset-case_wall_thk/2-fit_tol*1,
    be1_zoffset+be1_zsize/2])
  rotate([-90,0,0])
  ye_slot(
    be1_xsize,
    be1_zsize,
    case_wall_thk+slot_dep_adj);

if (be_num>=2)
  translate([
    be2_xoffset+be2_xsize/2,
    be2_yoffset-case_wall_thk/2-fit_tol*1,
    be2_zoffset+be2_zsize/2])
  rotate([-90,0,0])
  ye_slot(
    be2_xsize,
    be2_zsize,
    case_wall_thk+slot_dep_adj);

if (be_num==3)
  translate([
    be3_xoffset+be3_xsize/2,
    be3_yoffset-case_wall_thk/2-fit_tol*1,
    be3_zoffset+be3_zsize/2])
  rotate([-90,0,0])
  ye_slot(
    be3_xsize,
    be3_zsize,
    case_wall_thk+slot_dep_adj);
}
//

module top_edge(){
if (te_num>=1)
  translate([
    te1_xoffset+te1_xsize/2,
    te1_yoffset+case_wall_thk/2+fit_tol*1,
    te1_zoffset+te1_zsize/2])
  rotate([90,0,0])
  ye_slot(
    te1_xsize,
    te1_zsize,
    case_wall_thk+slot_dep_adj);

if (te_num==2)
  translate([
    te2_xoffset+te2_xsize/2,
    te2_yoffset+case_wall_thk/2+fit_tol*1,
    te2_zoffset+te2_zsize/2])
  rotate([90,0,0])
  ye_slot(
    te2_xsize,
    te2_zsize,
    case_wall_thk+slot_dep_adj);
}
//

module ye_slot(xsize,zsize,thick){
hull(){
 for (i=[-xsize/2+zsize/2,xsize/2-zsize/2])
   translate([i,0,0])
   cylinder(
     thick,
     zsize/2+slot_cl_adj*3.2,
     zsize/2+slot_cl_adj*1.6,
     true);
 }
}
//

module back_cover(){
if (bc_num>=1)
  translate([
    bc1_xoffset+bc1_xsize/2,
    bc1_yoffset+bc1_ysize/2,
    -case_thk/2])
  bc_slot(
    bc1_xsize,
    bc1_ysize,
    case_thk+slot_dep_adj);

if (bc_num==2)
  translate([
    bc2_xoffset+bc2_xsize/2,
    bc2_yoffset+bc2_ysize/2,
    -case_thk/2])
  bc_slot(
    bc2_xsize,
    bc2_ysize,
    case_thk+slot_dep_adj);
}
//

module bc_slot(xsize,ysize,thick){
  if (xsize >= ysize)
    hull(){
      for (i=[-xsize/2+ysize/2,xsize/2-ysize/2])
        translate([i,0,0])
        cylinder(
          thick,
          ysize/2+slot_cl_adj,
          ysize/2+slot_cl_adj,
          true);
    }
  else
    hull(){
      for (i=[-ysize/2+xsize/2,ysize/2-xsize/2])
        translate([0,i,0])
        cylinder(
          thick,
          xsize/2+slot_cl_adj,
          xsize/2+slot_cl_adj,
          true);
    }
}
//

module ear_jack(){
if (es_side==1){
  translate([
    es_xoffset+es_dia/2,
    es_yoffset+case_wall_thk/2+fit_tol*1,
    es_zoffset+es_dia/2])
  rotate([90,0,0])
  cylinder(
    case_wall_thk+slot_dep_adj,
    es_dia/2+slot_cl_adj*2.5,
    es_dia/2+slot_cl_adj,
    true);
}
if (es_side==2)
  translate([
    es_xoffset+es_dia/2,
    es_yoffset-case_wall_thk/2-fit_tol*1,
    es_zoffset+es_dia/2])
  rotate([-90,0,0])
  cylinder(
    case_wall_thk+slot_dep_adj,
    es_dia/2+slot_cl_adj*2.5,
    es_dia/2+slot_cl_adj,
    true);

if (es_side==3)
  translate([
    es_xoffset-case_wall_thk/2-fit_tol*1,
    es_yoffset+es_dia/2,
    es_zoffset+es_dia/2])
  rotate([0,90,0])
  cylinder(
    case_wall_thk+slot_dep_adj,
    es_dia/2+slot_cl_adj*2.5,
    es_dia/2+slot_cl_adj,
    true);

if (es_side==4)
  translate([
    es_xoffset+case_wall_thk/2+fit_tol*1,
    es_yoffset+es_dia/2,
    es_zoffset+es_dia/2])
  rotate([0,-90,0])
  cylinder(
    case_wall_thk+slot_dep_adj,
    es_dia/2+slot_cl_adj*2.5,
    es_dia/2+slot_cl_adj,
    true);
}
//

module roundCylinder(height,radius,rounding){
  hull(){
    translate([0,0,height-rounding])
    cylinder(r=radius,h=rounding);

    translate([0,0,rounding])
    rotate_extrude(convexity=10)
      translate([radius-rounding,0,0])
      circle(rounding);
  }
}
//
```


[User]
Hey, I'm looking to design a parametric clip to hold a caliper, and I have some specific ideas. I've got a previous design that I'm using as a base, and I'd like to rebuild it from scratch using a parametric approach.

Basically, the clip has a rectangular base with two "arms" that extend upwards. These arms will hold the caliper. The arms have a rounded, "filleted" look at the corners where they join the base. I need to be able to control the following dimensions of the clip:

*   `length_1`: The length of the main base part
*   `length_2`: The length of the 'arm' sticking up from the base (from the corner junction)
*   `Thickness`: The thickness of the clip material (all parts have the same thickness)
*   `height`: The overall height of the clip (the extruded height of the 2D shape)
*    `hole_dia`: The diameter of the screw/mounting holes
*    `standoff`: The amount the main base is inset, that the 'arms' are positioned from, this is to make the main body 'thinner' than the main base

Also, I need two mounting holes on the top face, one at roughly 1/4 the height, and the other at 3/4 the height. These holes should have a chamfer to allow screws to sit flush. And I also need to be able to fillet the outside edges of the base and arms where they join together.

For the fillets and to achieve some of the shapes I would like to use the offset() function in openscad.

I think this would make it very flexible. Can you generate OpenSCAD code for this? I don't need any libraries other than what comes with openscad, I don't use any special libraries.

[Assistant]
Parametric_clip_caliper.scad:
```

length_1=17;
length_2=3.6;
Thickness=0.9;
height=35;
hole_dia=4;
standoff=3;

module round2d(OR=3,IR=1){
    offset(OR)offset(-IR-OR)offset(IR)children();
}

$fn=100;
module clip(){
linear_extrude(height=height){
    fillet=Thickness/2.85;
round2d(OR=fillet,IR=fillet){
translate([-length_1/2-Thickness,0,0]) square([length_1+Thickness+Thickness,Thickness],false);
translate([length_1/2,-length_2,0]) square([Thickness,length_2],false);
translate([-length_1/2-Thickness,-length_2,0]) square([Thickness,length_2],false);
translate([-length_1/2-Thickness,-length_2-Thickness,0]) square([Thickness*1.5,Thickness],false);
translate([+length_1/2-Thickness/2,-length_2-Thickness,0]) square([Thickness*1.5,Thickness],false);

 translate([-(length_1*0.8/2),0,0]) square([length_1*0.8,standoff]);

}
}
}

difference(){
clip();

    rotate([90,0,0]) translate([0,height/4,-Thickness*37-Thickness/2]) cylinder(  Thickness*38, d1=hole_dia,  d2=hole_dia  );
rotate([90,0,0]) translate([0,height/4,-hole_dia/2.39]) cylinder(  hole_dia/2.38, d1=hole_dia,  d2=hole_dia*2  );

    rotate([90,0,0]) translate([0,height/2+height/4,-Thickness*37-Thickness/2]) cylinder(  Thickness*38, d1=hole_dia,  d2=hole_dia  );
rotate([90,0,0]) translate([0,height/2+height/4,-hole_dia/2.39]) cylinder(  hole_dia/2.38, d1=hole_dia,  d2=hole_dia*2  );

}

```


[User]
Hi! I'm looking to generate a custom guitar top nut in OpenSCAD, and I'd like it to be pretty flexible. I've seen some designs out there, but I need something that can handle a few different scenarios.

Here's what I'm thinking:

1.  **Parametric Design:** I want to be able to easily adjust things like the nut's length, width, and the radius of the fretboard it's meant for. The fretboard radius is important; I'd like the string slots to follow the curvature. I also need to specify the number of strings.

2.  **String Slots:** The slots need to be cut for the actual diameters of the strings and I need to be able to set the height of each string from the base of the nut. This means being able to set the distance from the bottom of the nut to the *bottom* of each string slot. I also want the option to adjust the height of individual strings if needed.

3.  **Slot Shape:** I'd like the choice between V-shaped slots or straight slots for the strings.

4.  **String Spacing:** I need two different options for spacing the strings:
    *   **Proportional:** Where the open space between strings is proportional based on the total length and number of strings.
    *   **Equal:** Where each slot is equally spaced from one another.

5.  **Paired Strings:** It needs to work for a 12-string guitar too. This means being able to specify if the strings are to be paired, and to set the spacing between the paired strings. For paired strings with proportional spacing, the main (even) strings should be spaced proportionally and the slave (odd) strings would be placed to make the space between the strings in the pair equal to the specified paired string distance.

6.  **Edge Distance:** I want to control the distance from the edge of the nut to the outermost edge of the first and last strings.

7.  **String Diameters:** I need to be able to set individual diameters for each string. It should assume that any unused strings have a diameter of 0.

8.  **Individual Adjustments:** For each string I need to be able to adjust two parameters:
    *   **Height Adjust:** A value that offsets the calculated height of the string.
    *   **Position Adjust:** A value that offsets the calculated position of the string slot horizontally.

9.  **Chamfered Edges:** The nut should have nicely chamfered edges at the outer limits of the strings.

Could you create an OpenSCAD design that can handle these parameters? It looks like you will need to use the `minkowski()` function to create the v-shaped slots, and you may need `rotate_extrude` to make the base shape. I'm looking forward to seeing what you come up with!

[Assistant]
TopNut.scad:
```
/*
Awesome customizable top nut uses true fretboard radius, true string diameter grooves and true string height setting.
It is also possible to adjust each string height if necessary.

Updates:
    2022-07-18: Increased $fn to 160 from 80, lower it if your computer struggles, improved chamferCorner function
    2020-04-25: Better calculation for paired strings
    2020-05-01: Added proportional spacing rule and position adjustment for individual strings

Written by Torkel Borjeson 2018-08-02 <torkelb@gmail.com>
*/
echo(version=version());
$fn = 160*1; // To hide from Customizer

function isOdd(x) = (x%2) == 1;

// Use identical distances inside two adjacent strings, if not the strings are placed on equal distance from each other
spacingMode = "Proportional"; //["Proportional", "Equal"]

// Length of nut in direction of strings
nutLength = 4.8;

// Width of nut
nutWidth = 42.4;

// Radius of first fret, normally 12"
nutRadius = 304.8;

// V-shaped slots or straight slots
stringSlotShape = "v-shaped"; //["v-shaped", "straight"]

// Distance from bottom of nut to bottom of strings. Measure nut slot height to fret board, add fret thickness and add extra clearance of about 0.75mm
stringHeight = 7.8;

// Total number of strings
numberOfStrings = 6; //[2:12]

// If true the algorithm will pair strings like on a 12-string guitar
pairStrings = false;

// For proportional spacing this is the open space between two paired strings, same for all pairs. For equal spacing this is the distance between two paired string slots
pairedStringsDistance = 2;

// Distance from edge of nut to outermost edge of first string
edgeDistanceToFirstString = 3;

// Distance from edge of nut to outermost edge of last string
edgeDistanceToLastString = 3;

// Diameters of used strings. Important: Set width of unused strings to 0 to not confuse algorithm to calculate thickest string
string1Diameter = 0.35;
string2Diameter = 0.45;
string3Diameter = 0.65;
string4Diameter = 0.85;
string5Diameter = 1.10;
string6Diameter = 1.40;
string7Diameter = 0.0;
string8Diameter = 0.0;
string9Diameter = 0.0;
string10Diameter = 0.0;
string11Diameter = 0.0;
string12Diameter = 0.0;
stringDiameters = [
    string1Diameter,
    string2Diameter,
    string3Diameter,
    string4Diameter,
    string5Diameter,
    string6Diameter,
    string7Diameter,
    string8Diameter,
    string9Diameter,
    string10Diameter,
    string11Diameter,
    string12Diameter];

// This way we can handle both right and left handed nuts
thickestString = max(stringDiameters);
stringDiameterSum =
    string1Diameter +
    string2Diameter +
    string3Diameter +
    string4Diameter +
    string5Diameter +
    string6Diameter +
    string7Diameter +
    string8Diameter +
    string9Diameter +
    string10Diameter +
    string11Diameter +
    string12Diameter;

// These are used for paired strings using proprtional rule since the algorithm lays out the even strings proportionally
evenStringsDiameterSum =
    string2Diameter +
    string4Diameter +
    string6Diameter +
    string8Diameter +
    string10Diameter +
    string12Diameter;

// Height adjustment for individual strings, positive value heightens string
string1HeightAdjust = 0.0;
string2HeightAdjust = 0.0;
string3HeightAdjust = 0.0;
string4HeightAdjust = 0.0;
string5HeightAdjust = 0.0;
string6HeightAdjust = 0.0;
string7HeightAdjust = 0.0;
string8HeightAdjust = 0.0;
string9HeightAdjust = 0.0;
string10HeightAdjust = 0.0;
string11HeightAdjust = 0.0;
string12HeightAdjust = 0.0;

stringHeightAdjust = [
    string1HeightAdjust,
    string2HeightAdjust,
    string3HeightAdjust,
    string4HeightAdjust,
    string5HeightAdjust,
    string6HeightAdjust,
    string7HeightAdjust,
    string8HeightAdjust,
    string9HeightAdjust,
    string10HeightAdjust,
    string11HeightAdjust,
    string12HeightAdjust];

// Position adjustment for individual strings
string1PositionAdjust = 0;
string2PositionAdjust = 0;
string3PositionAdjust = 0;
string4PositionAdjust = 0;
string5PositionAdjust = 0;
string6PositionAdjust = 0;
string7PositionAdjust = 0;
string8PositionAdjust = 0;
string9PositionAdjust = 0;
string10PositionAdjust = 0;
string11PositionAdjust = 0;
string12PositionAdjust = 0;

stringPositionAdjust = [
    string1PositionAdjust,
    string2PositionAdjust,
    string3PositionAdjust,
    string4PositionAdjust,
    string5PositionAdjust,
    string6PositionAdjust,
    string7PositionAdjust,
    string8PositionAdjust,
    string9PositionAdjust,
    string10PositionAdjust,
    string11PositionAdjust,
    string12PositionAdjust];

nutHeight = stringHeight + thickestString;

// Space between strings, for paired strings it's the space between the main(upper) strings in each pair excluding the slave strings
spaceBetweenStrings =  pairStrings ? (nutWidth-edgeDistanceToFirstString - pairedStringsDistance -string1Diameter/2 -edgeDistanceToLastString-evenStringsDiameterSum)/(numberOfStrings/2-1) : (nutWidth-edgeDistanceToFirstString-edgeDistanceToLastString-stringDiameterSum)/(numberOfStrings-1);

function calcStringPosition(previousStringPosition, previousStringDiameter,
    stringNumber,
    pairStrings) = !pairStrings ?
    // Normal strings
    previousStringPosition +  previousStringDiameter/2 +
    spaceBetweenStrings +
    stringDiameters[stringNumber-1]/2 :
    // Paired strings
    isOdd(stringNumber) ?
    // Slave string
    // previous string is a main string
    previousStringPosition +
    previousStringDiameter/2 +
    spaceBetweenStrings -
    pairedStringsDistance -
    stringDiameters[stringNumber-1]/2 :
    // Main string
    previousStringPosition +
    previousStringDiameter/2 +
    pairedStringsDistance +
    stringDiameters[stringNumber-1]/2;

// Calculate string positions
string1Position = -nutWidth/2 + edgeDistanceToFirstString + string1Diameter/2;
string2Position = calcStringPosition(string1Position, string1Diameter,
    2, pairStrings);
string3Position = calcStringPosition(string2Position, string2Diameter,
    3, pairStrings);
string4Position = calcStringPosition(string3Position, string3Diameter,
    4, pairStrings);
string5Position = calcStringPosition(string4Position, string4Diameter,
    5, pairStrings);
string6Position = calcStringPosition(string5Position, string5Diameter,
    6, pairStrings);
string7Position = calcStringPosition(string6Position, string6Diameter,
    7, pairStrings);
string8Position = calcStringPosition(string7Position, string7Diameter,
    8, pairStrings);
string9Position = calcStringPosition(string8Position, string8Diameter,
    9, pairStrings);
string10Position = calcStringPosition(string9Position, string9Diameter,
    10, pairStrings);
string11Position = calcStringPosition(string10Position, string10Diameter,
    11, pairStrings);
string12Position = calcStringPosition(string11Position, string11Diameter,
    12, pairStrings);

// Store the positions in an array
stringPosition = [
    string1Position,
    string2Position,
    string3Position,
    string4Position,
    string5Position,
    string6Position,
    string7Position,
    string8Position,
    string9Position,
    string10Position,
    string11Position,
    string12Position];

//////////////////////////////////////////
// Non proportional position algorithms
//////////////////////////////////////////
firstStringToLastStringDistance = nutWidth - edgeDistanceToFirstString - edgeDistanceToLastString - string1Diameter/2 - stringDiameters[numberOfStrings-1];
stringSpacing = firstStringToLastStringDistance/(numberOfStrings-1);
string1XOffset = (nutWidth - firstStringToLastStringDistance)/2;
string1Pos = -nutWidth/2 + string1XOffset;

// First pair of strings get group number 1, second pair get group number 2 and so forth
function getGroupNumber(stringNumber) =
floor((stringNumber+1)/2);

// Returns position for middle of string group
function getGroupPosition(stringNumber) =
string1Pos + pairedStringsDistance/2 + (getGroupNumber(stringNumber)-1)*pairedGroupsDistance;

// Return offset from middle of a string group to selected string
function getPairedStringOffset(stringNumber) =
isOdd(stringNumber) ? -pairedStringsDistance/2 : pairedStringsDistance/2;

// Return x-position of string
function getXPosition(stringNumber, pairStrings) =
(!pairStrings) ?
// normal strings
string1Pos+stringSpacing*(stringNumber-1) :
// paired strings
getGroupPosition(stringNumber) + getPairedStringOffset(stringNumber);

// Radius of torus
R = nutRadius + nutHeight;

// Angle of slot horizontally
stringAngle = 10;

// V-shaped slot
module vSlot(diameter) {
    rotate([stringAngle, 0, 0]) {
        minkowski() {
            cylinder(d=diameter, h=nutLength, center=true);
            resize([10,20])
                rotate([0,0,45])
                    cube([10, 10, nutLength], center=false);
        }
    }
}

// Straight slot
module straightSlot(diameter) {
    rotate([stringAngle, 0, 0]) {
        cylinder(d=diameter, h=nutLength, center=true);
        translate([0,nutHeight/2, 0])               cube([diameter, nutHeight, nutLength], center=true);
    }
}

// Make slot for a string
module slot(stringNumber) {
    xPos = (spacingMode == "Proportional" ? stringPosition[stringNumber-1] : getXPosition(stringNumber, pairStrings)) + stringPositionAdjust[stringNumber-1];
    echo(string = stringNumber, position = xPos);
    // Compensate so all strings will get same height to fret board
    heightCompensation = thickestString - stringDiameters[stringNumber-1];
    yPos = sqrt(R*R-xPos*xPos)-stringDiameters[stringNumber-1]/2 - heightCompensation + stringHeightAdjust[stringNumber-1];
    translate([xPos, yPos, 0])
        if (stringSlotShape == "v-shaped") {
            vSlot(stringDiameters[stringNumber-1]);
        } else {
            straightSlot(stringDiameters[stringNumber-1]);
        }
}

// Main torus that later will be cut to size
module torus() {
color("Ivory")
    rotate_extrude()
        translate([nutRadius, 0, 0])
            resize([nutHeight*2,nutLength*2])
                circle(d=20);
}

module delimiterBottom() {
    // Bottom part
    cube([nutRadius*2, nutRadius*2, 40], center=true);
}

module delimiterSide() {
    // Side delimiter
    translate([nutWidth/2+nutRadius, 0, 0])
        cube([nutRadius*2, nutRadius*3, 50], center=true);
}

module delimiterSplit() {
    // Remove lower part of ellipse
    translate([-nutRadius, 0, -30])
        cube([nutRadius*2, nutRadius*2, 30], center=false);
}

module hollowCylinder(innerRadius, outerRadius, length) {
    color("pink") {
        difference() {
            cylinder(r=innerRadius*2, h=length, center=false);
            translate([0,0,-length])
                cylinder(r=innerRadius, h=length*3, center=false);
        }
    }
}

module chamferCorner(innerRadius) {

    xCenter = -nutWidth/2+innerRadius;
    yCenter = sqrt(R*R-xCenter*xCenter)-innerRadius;
    translate([xCenter, yCenter, -nutLength]) {
        difference() {
            hollowCylinder(innerRadius, innerRadius*2, nutLength*2);
        color("gray") {
            translate([0,0,-nutLength])
              cube([2*innerRadius, 2*innerRadius, nutLength*4], center=false);
        }
        rotate([180,90,90]) {
            translate([-3*nutLength,-2*innerRadius,0])
            color("green") {
                cube([nutLength*4, 4*innerRadius, 2*innerRadius], center=false);
            }
            }
        }
    }
}

module removeTheRest() {
    translate([0, -nutRadius, 0])
        cube([nutRadius*2, nutRadius*2, 60], center=true);
}

// The nut is created by first constructing the torus and then all other elements that are used to remove stuff from the torus
module Nut() {
    rotate([90,0,0]) {
        translate([0, -nutRadius, 0]) {
            difference() {
                torus();
                delimiterBottom();
                delimiterSide();
                rotate([0,0,180])
                    delimiterSide();
                delimiterSplit();
                removeTheRest();
                chamferCorner(edgeDistanceToFirstString);
                rotate([0, 180, 0]) {
                    chamferCorner(edgeDistanceToLastString);
                }
                for (i=[1:numberOfStrings])
                    slot(i);
            }
        }
    }
}

Nut();

```


[User]
I'd like to create a customizable bracelet using OpenSCAD, kind of like the one in that thingiverse link, but with a few changes and improvements. I need a way to specify the bracelet's circumference, width, and thickness, along with the rounding radius for the side edges. Also, the text should be customizable, including the font (using Google fonts), text height, a small X and Y offset, and even a Z offset to push it deeper. I need a way to control the text's scaling/flare and width.

I want the option to have the text either engraved onto the surface, cut through the bracelet, or, what I'm calling, "hanging" between borders.  If it's hanging, I need to be able to control the border size. Also, I need to control the ends.  Specifically, I need an option to use simple ends or ones suitable for attaching clasps.  For the clasps ends, we will need customizable length, width and thickness.

To sum it up, I need a parametric bracelet design with customizable:

*   **Bracelet Dimensions:** Circumference, Width, Thickness, Edge Rounding
*   **Text:** String, Font (Google Fonts), Height, X/Y/Z Offset, Size, Flare, Width Scaling
*   **Text Mode:** Engraved, Cut-Through, or "Hanging" with border size
*   **End Style:** Simple or Clasp-compatible, with length, width, and thickness for clasp-ends
*   **Closure Method:** If the ends are simple, create holes to use pins, thread, etc...
*   **File:** Single OpenSCAD file.

[Assistant]
Braccialetto_con_scritta_v2a.scad:
```
Bracelet_circumference=180;
Bracelet_width=20;
Bracelet_thickness=3;
Bracelet_side_rounding=2;

Text="MY BRACELET";
Font="Handlee";
Text_height=4;
Text_x_offset=0;
Text_y_offset=-0.25;
Text_z_offset=-1.5;
Font_size=14;
Flare_text=1.05;
Scale_text_width=1;

//Text hanging between borders
Only_border=1;
Border_size=4;

//Type of closure: 0=original    1=for end-clasps
Ends_style=1; //([0:1])
Ends_width=8;
Ends_length=5;
Ends_thickness=3.5;

$fn=72;

step=Bracelet_circumference/len(Text);

difference()
    {
    translate([0,0,0])
        hull()
            {
                translate([(Bracelet_circumference/2-Bracelet_side_rounding),(Bracelet_width/2-Bracelet_side_rounding),0])
                 rotate([0,0,0])
                  sphere(r=Bracelet_side_rounding,center=true);
                    translate([(Bracelet_circumference/2-Bracelet_side_rounding),-(Bracelet_width/2-Bracelet_side_rounding),0])
                 rotate([0,0,0])
                  sphere(r=Bracelet_side_rounding,center=true);
                    translate([-(Bracelet_circumference/2-Bracelet_side_rounding),(Bracelet_width/2-Bracelet_side_rounding),0])
                 rotate([0,0,0])
                  sphere(r=Bracelet_side_rounding,center=true);
                    translate([-(Bracelet_circumference/2-Bracelet_side_rounding),-(Bracelet_width/2-Bracelet_side_rounding),0])
                 rotate([0,0,0])
                  sphere(r=Bracelet_side_rounding,center=true);
                }
if(Only_border==0)    {
    scale([Scale_text_width,1,1])
    translate([Text_x_offset,Text_y_offset,Text_z_offset])
     linear_extrude(height=Text_height,scale=Flare_text)
       text(Text,font=Font,valign="center",halign="center",size=Font_size);
    }

    if(Only_border==1)
    {
      cube([Bracelet_circumference-Border_size*2-20,Bracelet_width-Border_size*2,Bracelet_thickness*2],center=true);
    }

//Zona agganci
if(Ends_style==0)
   {
    translate([Bracelet_circumference/2-4,4,0])
            cube([10,4,Bracelet_thickness*2],center=true);
    translate([Bracelet_circumference/2-4,-4,0])
            cube([10,4,Bracelet_thickness*2],center=true);

    translate([-Bracelet_circumference/2+4,5,0])
            cube([10,4,Bracelet_thickness*2],center=true);
    translate([-Bracelet_circumference/2+4,14,0])
            cube([10,4,Bracelet_thickness*2],center=true);
    translate([-Bracelet_circumference/2+4,-4,0])
            cube([10,4,Bracelet_thickness*2],center=true);
   }
if(Ends_style==1)
   {
    translate([Bracelet_circumference/2,0,0])
            cube([Ends_length*2,Bracelet_width,Bracelet_thickness*2],center=true);
    translate([-Bracelet_circumference/2,0,0])
            cube([Ends_length*2,Bracelet_width,Bracelet_thickness*2],center=true);
   }
//
        translate([0,0,5+Bracelet_thickness/2])
            cube([Bracelet_circumference*2,Bracelet_width*2,10],center=true);
                translate([0,0,-5-Bracelet_thickness/2])
            cube([Bracelet_circumference*2,Bracelet_width*2,10],center=true);

        translate([Bracelet_circumference/2+Bracelet_thickness,0,0])
            cube([Bracelet_thickness*2,Bracelet_width*2,10],center=true);
        translate([-Bracelet_circumference/2-Bracelet_thickness,0,0])
            cube([Bracelet_thickness*2,Bracelet_width*2,10],center=true);

//Fori fissaggio
if(Ends_style==0)
   {
    translate([-Bracelet_circumference/2+6,0,0])
      rotate([90,0,0])
         cylinder(r=0.75,h=Bracelet_width*2,center=true);
    translate([Bracelet_circumference/2-6,0,0])
      rotate([90,0,0])
         cylinder(r=0.75,h=Bracelet_width*2,center=true);
    translate([-Bracelet_circumference/2+2,0,0])
      rotate([90,0,0])
         cylinder(r=0.75,h=Bracelet_width*2,center=true);
    translate([Bracelet_circumference/2-2,0,0])
      rotate([90,0,0])
         cylinder(r=0.75,h=Bracelet_width*2,center=true);
      }
    }

    if(Only_border==1)
    {
    scale([Scale_text_width,1,1])
    translate([Text_x_offset,Text_y_offset,Text_z_offset])
     linear_extrude(height=Text_height,scale=Flare_text)
       text(Text,font=Font,valign="center",halign="center",size=Font_size);
    }

if(Ends_style==1)
   {
    translate([Bracelet_circumference/2-Ends_length/2,0,(Ends_thickness-Bracelet_thickness)/2])
            cube([Ends_length,Ends_width,Ends_thickness],center=true);
    translate([-Bracelet_circumference/2+Ends_length/2,0,(Ends_thickness-Bracelet_thickness)/2])
            cube([Ends_length,Ends_width,Ends_thickness],center=true);
   }

```


[User]
I'd like to design a parametric wire holder for 3D printing, but it needs to be a bit more sophisticated than just a simple clip. I need it to hold multiple wires, with a configurable spacing between them, and I'd like the option for a mounting plate with screw holes.

Here are the details:

*   **Multiple Wire Holders:** The design should hold a configurable number of individual wire holders. I need a parameter for the number of holders.
*   **Holder Spacing:** There should be a configurable gap between each wire holder. This gap can be positive for space or negative for overlap.
*   **Individual Holder Shape:** Each wire holder should be a rounded shape with a gap in the center for the wire to pass through.  Think of a nearly complete circle with a narrow cut out of the top edge of the circle.
*   **Mounting Plate (Optional):** I'd like an option for a mounting plate to be added to the assembly. This would be a flat base that extends beyond the first and last wire holder and includes holes for mounting screws. The size of the plate and the size of the screw holes needs to be configurable.

I also need a way to smooth out the edges of the wire holder. It should be configurable so I can disable smoothing if needed. It might be nice to have a preview option that doesn't use the smoothing function to speed up initial rendering.

I’m thinking this design will be entirely built from scratch - don't worry about trying to modify any existing code.

I'm planning to use OpenSCAD for this, so the output will be a parametric SCAD file. Please make sure it's easy to adjust all of the parameters I've outlined, and I'm not using any libraries. I'd prefer everything be contained in a single file.

[Assistant]
wireholder.scad:
```
// https://gist.github.com/dustin/0b48e7e7b7d318752acccaaaf4f590a5
// https://gist.github.com/dustin/80645ad3078394fbd784d2a226bc386f (multi)
$fa = 2 * 1;
$fs = 0.3 * 1;

// Smooth = 1 smooths out all the edges (and takes a lot more time).
smooth = 1; // [0:no, 1:yes]
// How many wires would you like to hold?
num_holders = 2;
// How large of a gap would you like between the holders? (negative causes overlap)
holder_gap = -5;
// How large (diameter) of a mounting plate do you want?  (0 disables)
mount_plate = 8;
// How large of a hole would you like for the mounting screws?
mount_hole_size = 1.5;

module line(points=[], width=1) {
    for(i = [1:len(points)-1]) {
        hull() {
            translate([points[i-1][0], points[i-1][1]]) circle(d=width);
            translate([points[i][0], points[i][1]]) circle(d=width);
        }
    }
}

module cableroute(length, width, thickness) {
    line([for(i = [0 : 20 : 360]) [i / (360/length), width * sin(i)]], width=thickness);
}

module wireholder(width, gap) {
    difference() {
        translate([width/2, 0, 0]) sphere(d=width);
        linear_extrude(width/2 + .1) cableroute(width, 3.5, gap);
        translate([0, -width/2, -width/2-2]) cube([width, width, width/2]);
        translate([0, -width/4, 0]) cube([width, width/2, gap+.5]);
    }
}

module smoothedHolder(width, gap) {
    if (!$preview && smooth > 0) {
        minkowski() {
            wireholder(width, gap);
            sphere(d=smooth);
        }
    } else {
        wireholder(width, gap);
    }
}

module multiHolder(width, gap) {
    for (i = [0:num_holders-1]) {
        translate([0, (width+holder_gap)*i, 0])
            smoothedHolder(width, gap);
    }
}

module mountPlate(width, mountPlateSize) {
    w = width + holder_gap;
    leftPoint = - (width / 2) - (mountPlateSize/2);
    rightPoint = (w * (num_holders-1)) + (width / 2) + (mountPlateSize / 2);
    difference() {
        hull() {
            translate([width/2, leftPoint, -2]) {
                cylinder(d=mountPlateSize, h=2);
            }
            translate([width/2, rightPoint, -2]) {
                cylinder(d=mountPlateSize, h=2);
            }
        }
        translate([width/2, leftPoint, -2.1]) {
            cylinder(d=mount_hole_size, h=3);
        }
        translate([width/2, rightPoint, -2.1]) {
            cylinder(d=mount_hole_size, h=3);
        }
    }
}

// These aren't parameters.
width = 25 + 0;
gap = 5 + 0;
multiHolder(width, gap);
if (mount_plate > 0) {
    mountPlate(width, mount_plate);
}

```


[User]
I'm looking to get a tool holder designed for my Ender 3 3D printer. It should mount onto the extruded aluminum profile, specifically the top bar behind the right-hand vertical brace, above the power supply.

I'd like two main versions: a smaller one that holds all the tools except the spatula, and a larger one that includes a spot for the spatula. It should be parametric, so I can tweak things like the clearance around the tools, the fit on the aluminum profile, and the corner radius. I want to be able to adjust these things through variables at the top of the file. Also, it'd be nice if there was an option to generate a smaller "profile only" piece that I can print to quickly test the fit on the printer's aluminum profile. This will help me dial in the `profile_clearance` variable.

The smaller holder should have spots for:
*   A set of hex keys (diameters of 4.5, 3.5, 3.0, 2.5, and 2.0 mm)
*   A small screwdriver/nut driver (with a 2.5mm diameter shaft and 11.5mm diameter handle)
*   Two small spanners (widths of 9.5mm and 20mm, thickness of 2mm)
*   A slot for the cutters (8mm wide and 22mm long)

The larger version should have all of that, plus:
*   A holder for the spatula, that has a slot of 38mm width and 1mm thickness on one side, and 24mm width and 8mm thickness on the other side of the slot and a "hole" with a 20mm width and 15mm depth at the bottom.

I'm thinking the main body of the holder should be about 40mm wide and the depths vary depending on whether it's the small or large variant, maybe 26mm deep for the small version, and 42mm deep for the large. Also, the main holder should be 5mm thick. The aluminum profile it's mounting on has a general size of 20mm, and an edge thickness of 2mm. I'd also like to round off the top of the holder using `corner_radius`.

I'd like to use `hull()` to join together parts of the holder and `difference()` to cut out the necessary tool holes, and any other basic OpenSCAD functions. No libraries need to be used.

[Assistant]
toolholder.scad:
```
// ////////////////////////////////////////////////////////////
//
// Ender 3 Tool Holder
//
// A simple tool holder that mounts onto the extruded aluminium
// profile, fits neatly to the top bar behind the right hand
// vertical brace, above the power supply.
//
// Copyright 2018 Kevin F. Quinn
//
// This work is licensed under the Creative Commons
// Attribution-ShareAlike 4.0 International License. To view a
// copy of the license, visit
// https://creativecommons.org/licenses/by-sa/4.0/
//
// History
// 2018-08-05 1.0 Initial publication

/* Printing notes
 * --------------
 *
 * First, refine the profile_clearance value to get a nice snug
 * fit for the extrusion profile mating section. Select the
 * "profile only" piece to print and check quickly before
 * committing to printing the holder.
 *
 * The "small" variant holds all the supplied tools apart from
 * the spatula.
 *
 * The "large" variant also has capacity for the spatula.
 *
 * In both cases, print on its side with the profile face
 * sitting on the bed - no supports needed.
 */

// Select which part to generate - which kind of holder, or the profile-only test part
part=0; // [0:small - without spatula, 1:large - with spatula, 2:profile only]
// Clearance distance around the various tool holes
tool_clearance=0.3;
// Clearance distance for the extrusion profile - print the "profile only" piece to test quickly
profile_clearance=0.2;
// Corner radius of top part
corner_radius=2.0;
// Amount of extrusion profile to remove to accommodate the end cap (or set to 0 and just turn the end cap...)
holder_cap_size=0;

/* [Hidden] */

$fn=30;

PART_SMALL=0;
PART_LARGE=1;
PART_PROFILE=2;

keys_d=[4.5,3.5,3.0,2.5,2.0];
keys_size=1.0;
driver_dia=2.5;
driver_size=11.5;
spanners_w=[9.5,20];
spanner_d=2.0;
spatula_w1=38;
spatula_w2=24;
spatula_d1=1.0;
spatula_d2=8.0;
spatula_size_w=20;
spatula_size_d=15;
cutters_w=8.0;
cutters_d=22.0;

holder_w=40;
holder_d=[26,42,40];
holder_t=5;

roundness=0.2;

profile_size=20.0;
profile_edge_thickness=2.0;

c=profile_clearance;
c2=c*sqrt(2);

profile=[
  [0.0, -profile_edge_thickness],
  [0.0,0.0],
  [5.5,0.0],
  [7.0,1.5],
  [4.5,1.5],
  [7.0,4.0],
  [13.0,4.0],
  [15.5,1.5],
  [13.0,1.5],
  [14.5,0.0],
  [20.0,0.0],
  [20.0, -profile_edge_thickness]
];

offset=[
  [0.0,0.0],
  [0.0,-c],
  [c2,-c],
  [c2,c],
  [c2,c],
  [c2,-c],
  [-c2,-c],
  [-c2,c],
  [-c2,c],
  [-c2,-c],
  [0.0,-c],
  [0.0,0.0]
];

profile1=[
  for (p=[0:1:len(profile)-1]) [profile[p][0]+offset[p][0],profile[p][1]+offset[p][1]]
];

sp=0.1;
/*
5.5  1.5  6  1.5  5.5
______         _______
|   __\       /__    | 1.5
|   \2.5      2.5/   |
| |\ \          / /| | 2.5
\ | \ \________/ / | /
 \|  \    6     /  |/
...
 */

module slot_profile(h,x=0) {
  //color("green") linear_extrude(height=2) polygon(profile0);
  translate([0,0,profile_size])
  rotate(a=-90,v=[1,0,0])
  rotate(a=90,v=[0,0,1])
  translate([0,profile_edge_thickness-roundness,0])
  difference() {
    linear_extrude(height=h)
    offset(r=roundness) offset(delta=-roundness)
    //offset(r=-roundness) offset(delta=roundness)
    polygon(profile1);
    translate([0,-roundness,-sp])
    cube([profile_size,profile_size,x+sp]);
  }
}

function sum(n,s,k)=(n>0)?sum(n-1,s+k[n],k):(s+k[0]);

module blank_toolholder(depth) {
  slot_profile(holder_w,holder_cap_size);
  // main plate
  hull() {
    translate([0,0,profile_size-holder_t])
    cube([profile_edge_thickness,holder_w,holder_t]);
    for (y=[corner_radius,holder_w-corner_radius])
    translate([depth-corner_radius,y,profile_size-holder_t])
    cylinder(r=corner_radius,h=holder_t);
  }
  // Support brace underneath
  hull() {
    for (z=[roundness,profile_size-holder_t])
    for (y=[holder_w/4,holder_w*3/4])
    translate([0,y,z]) sphere(r=roundness);
    for (y=[holder_w*3/7,holder_w*4/7])
    translate([depth/3,y,profile_size-holder_t]) sphere(r=roundness);
  }
}

module hex_hole(d,l) {
  cylinder(d=d+tool_clearance*2,h=profile_size+sp*2);
  translate([0,0,profile_size])
  rotate(a=90,v=[0,1,0])
  cylinder(d=d+tool_clearance*2,h=2.5+d/2+sp);
}

module spatula_hole() {
  d=spatula_d1+tool_clearance*2;
  translate([-spatula_w1/2-tool_clearance,-spatula_d1/2-tool_clearance,-sp])
  cube([spatula_w1+tool_clearance*2,spatula_d1+tool_clearance*2,profile_size+sp*2]);
  hull() {
    translate([-spatula_w2/2-tool_clearance-d/2,0-sp])
    cylinder(d=d,h=profile_size+sp*2);
    translate([-spatula_w2/2-tool_clearance+spatula_d2/2-d/2,spatula_d2/2+tool_clearance-d/2,-sp])
    cylinder(d=d,h=profile_size+sp*2);
    translate([-spatula_w2/2-tool_clearance+spatula_d2/2-d/2,-spatula_d2/2-tool_clearance+d/2,-sp])
    cylinder(d=d,h=profile_size+sp*2);
    translate([spatula_w2/2+tool_clearance+d/2,0,-sp])
    cylinder(d=d,h=profile_size+sp*2);
    translate([spatula_w2/2+tool_clearance-spatula_d2/2+d/2,spatula_d2/2+tool_clearance-d/2,-sp])
    cylinder(d=d,h=profile_size+sp*2);
    translate([spatula_w2/2+tool_clearance-spatula_d2/2+d/2,-spatula_d2/2-tool_clearance+d/2,-sp])
    cylinder(d=d,h=profile_size+sp*2);
  }
  translate([0,0,profile_size-tool_clearance])
  scale([1.0,spatula_size_d/spatula_size_w,1.0])
  cylinder(d=spatula_size_w+tool_clearance*2,h=tool_clearance+sp);
}

module toolholder() {
  difference() {
    blank_toolholder(depth=holder_d[part]);
    // Hex key slots
    if (part==PART_SMALL) {
      // Along back edge
      for (key=[0:1:4]) {
        translate([holder_d[part]-2.5-keys_d[key]/2,holder_w-sum(key,0,keys_d)-key*2,-sp])
        hex_hole(d=keys_d[key]);
      }
    } else {
      // Along right hand side edge
      for (key=[0:1:4]) {
        translate([holder_d[part]-(sum(key,0,keys_d)+key*2)-spatula_size_d-profile_edge_thickness,2.5+keys_d[key]/2,-sp])
        rotate(a=-90,v=[0,0,1])
        hex_hole(d=keys_d[key]);
      }
    }

    // screw driver hole
    translate([driver_size/2+profile_edge_thickness/2,
               ((part==PART_LARGE)?holder_w/2:holder_w-driver_size/2-profile_edge_thickness),-sp]) {
      cylinder(d=driver_dia+tool_clearance*2,h=profile_size+sp*2);
      translate([0,0,profile_size+sp-tool_clearance])
      cylinder(d=driver_size+tool_clearance*2,h=tool_clearance+sp);
    }

    // Spanners
    if (part==PART_SMALL) {
      for (spanner=[0:1:1]) {
        translate([driver_size+(spanner_d+2)*spanner-tool_clearance,cutters_w+4-tool_clearance,-sp])
        cube([spanner_d+tool_clearance*2,spanners_w[spanner]+tool_clearance*2,profile_size+sp*2]);
      }
    } else {
      translate([driver_size,(holder_w-(profile_edge_thickness*2+cutters_w+spanner_d)),-sp])
      rotate(a=-30,v=[0,0,1])
      cube([spanners_w[0]+tool_clearance*2,spanner_d+tool_clearance*2,profile_size+sp*2]);

      translate([profile_edge_thickness*2,(max(keys_d)+profile_edge_thickness),-sp])
      rotate(a=30,v=[0,0,1])
      cube([spanners_w[1]+tool_clearance*2,spanner_d+tool_clearance*2,profile_size+sp*2]);
    }

    // Cutters
    translate([((part==PART_LARGE)?(holder_d[part]-spatula_size_d)/2-cutters_d/2:holder_d[part]/2-cutters_d/2),
    ((part==PART_LARGE)?(holder_w-profile_edge_thickness-cutters_w):profile_edge_thickness),-sp])
    cube([cutters_d,cutters_w,profile_size+sp*2]);

    // Spatula - only on large version
    if (part==PART_LARGE) {
      translate([holder_d[part]-spatula_size_d/2-profile_edge_thickness,holder_w/2,0])
      rotate(a=70,v=[0,0,1])
      spatula_hole();
    }

  }
}

if (part==PART_PROFILE) {
  rotate(a=90,v=[1,0,0])
  slot_profile(10);
} else {
  rotate(a=90,v=[1,0,0])
  toolholder();
}

```


[User]
Hey, I'm looking to create a crown design that's fully customizable. I've seen a few examples but want to make one that I can adjust the size, number of spikes, and the like. I need the design to be parametric so I can easily change these dimensions and have the crown update accordingly.

Specifically, I'd like to control:

*   The upper diameter of the crown's base.
*   The lower diameter of the crown's base.
*   The overall height of the crown.
*   The height of the arch created by the spikes, or the point at which the spikes begin to rise.
*   The number of spikes around the crown.
*   The diameter of the spherical balls at the tips of the spikes.
*   The wall thickness of the crown base.
* The resolution or quality of the curve rendering of the crown, where I can specify the fragment number ($fn).

Could you generate the OpenSCAD code for this design? I'd like it to be a single `.scad` file.

[Assistant]
Crown.scad:
```
// Crown
// author: Benedict Endemann
// https://www.thingiverse.com/thing:3037001

////////// Thingiverse Customizer //////////
upper_diameter = 125; // [25:1:250]
lower_diameter = 110; // [25:1:250]
height = 50; // [25:0.5:100]
arch_height = 32.5; // [25:0.5:100]
number_of_spikes = 9; // [3:1:15]
spike_ball_diameter = 7; // [0.1:0.1:20]
wall_thickness = 2.4; // [0.1:0.1:20]
set_fn_value = 90; // [15:1:360]
$fn = set_fn_value;
////////// Thingiverse Customizer //////////

crown(
    upper_diameter = upper_diameter,
    lower_diameter = lower_diameter,
    wall_thickness = wall_thickness,
    height         = height,
    arch_height    = arch_height,
    spike_number   = number_of_spikes,
    ball_diameter  = spike_ball_diameter
);

module crown(
    upper_diameter,
    lower_diameter,
    wall_thickness,
    height,
    arch_height,
    spike_number,
    ball_diameter
) {

    upper_diameter  = upper_diameter != undef ?
                        abs(upper_diameter) :
                        lower_diameter != undef ?
                            lower_diameter :
                            100;
    lower_diameter  = lower_diameter != undef ?
                        abs(lower_diameter) :
                        upper_diameter / 10 * 8;
    wall_thickness  = wall_thickness != undef ?
                        abs(wall_thickness) :
                        upper_diameter / 10;
    height          = height != undef ?
                        abs(height) :
                        upper_diameter / 3 * 2;
    arch_height     = arch_height != undef ?
                        max(min(arch_height, height), 0) :
                        height / 2;
    spike_number    = spike_number != undef ?
                        max(spike_number, 3) :
                        5;
    ball_diameter   = ball_diameter != undef ?
                        abs(ball_diameter) :
                        0;

    difference() {

        // base cylinder
        cylinder(
            d1 = lower_diameter,
            d2 = upper_diameter,
            h  = height
        );

        // make hollow cylinder
        cylinder(
            d1 = lower_diameter * 2 - upper_diameter - wall_thickness * 2,
            d2 = upper_diameter - wall_thickness * 2,
            h  = height * 2,
            center = true
        );

        // shape spikes
        for (i = [0 : spike_number]) {

            rotate([
                0,
                270,
                360 / spike_number * i
            ])
                translate([
                    height,
                    0,
                    0
                ])
                    resize([
                        (height - arch_height) * 2,
                        0,
                        0
                    ])
                        cylinder(
                            d = (upper_diameter - wall_thickness * 2) * sin(360 / spike_number / 2),
                            h = upper_diameter
                        );
        }
    }

    // add spike balls
    for (i = [0 : spike_number]) {

        rotate([
            0,
            0,
            360 / spike_number * (i + 1 / 2 * (spike_number + 1) % 2)
        ])
            translate([
                (upper_diameter - wall_thickness) / 2,
                0,
                height
            ])
               sphere(
                    d   = ball_diameter,
                    $fn = $fn / 3
                );
    }
}
```


[User]
I need a parametric OpenSCAD file (or files if necessary) to generate a customizable grid. I should be able to specify whether the grid is round or rectangular. For a rectangular grid, I need to specify the length and width, as well as the thickness. For a round grid, I need to specify the radius and the thickness, and also the total width (same as dimensions for the rectangular one). There needs to be a parameter for the width of each strand in the grid. I also want to specify the number of strands (based on the length measurement, so either the diameter or the dimension_x). Also there should be a scale variable to scale the entire object in x.

Finally, I want a parameter to create a center hole, which won't be scaled and can be 0 to disable. It should have a diameter. I want to be able to specify the diameter for this.

[Assistant]
Griglia_parametrica.scad:
```
//Grate type   0=round     1=rectangular
Grate_type=0;
//Grate length
Dimension_x=63;
//Grate width
Dimension_y=60;
//Grate thicknesstranslate([0,50,0])
Dimension_z=2;
//Grate radius if type=0
Radius=50;
//Width of each strand of the grid
Strand_width=1;
//Number of stands (based on Dimension_x measure)
Strand_number_x=15;
//Scale
Scale=1;
//Center hole (0 to disable) - No scaling
Center_hole_diameter=10;

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

step=(Dimension_x)/Strand_number_x;
$fn=36;

difference()
   {
   scale([Scale,1,1])
   union()
   {
   intersection()
      {
      union()
         {
         if(Grate_type==0)
            {
            for(a=[0:step:Radius*3])
            translate([-Radius*1.5+a-Strand_width/1.5,0,0])
               cube([Strand_width,Radius*3,Dimension_z],center=true);
            for(a=[0:step:Radius*3])
            translate([0,-Radius*1.5+a-Strand_width/1.5,0])
               cube([Radius*3,Strand_width,Dimension_z],center=true);
            }
         if(Grate_type==1)
            {
            for(a=[0:step:Dimension_x])
            translate([-Dimension_x/2+a,0,0])
               cube([Strand_width,Dimension_y,Dimension_z],center=true);
            for(a=[0:step:Dimension_y])
            translate([0,-Dimension_y/2+a+Strand_width/2,0])
               cube([Dimension_x,Strand_width,Dimension_z],center=true);
            }
         }

      if(Grate_type==0)
         {
         cylinder(r=Radius,h=Dimension_z,center=true);
         }

      if(Grate_type==1)
         {
              cube([Dimension_x,Dimension_y,Dimension_z],center=true);
         }
      }
      if(Grate_type==0)
      difference()
         {
              cylinder(r=Radius,h=Dimension_z,center=true);
               cylinder(r=Radius-Strand_width,h=Dimension_z+2,center=true);
         }

      if(Grate_type==1)
      difference()
         {
              cube([Dimension_x,Dimension_y,Dimension_z],center=true);
               cube([Dimension_x-Strand_width*2,Dimension_y-Strand_width*2,Dimension_z+2],center=true);
         }

   }
   cylinder(r=Center_hole_diameter,h=Dimension_z+2,center=true);

   }
   if(Center_hole_diameter!=0)
   difference()
      {
           cylinder(r=Center_hole_diameter+Strand_width,h=Dimension_z,center=true);
            cylinder(r=Center_hole_diameter,h=Dimension_z+2,center=true);
      }
```


[User]
Hey, I'm looking to create a rattle design for 3D printing, similar to the one I saw on Thingiverse (thing:3037626). I need it to be parametric, so I can easily adjust the size and other features.

Here's what I'm thinking:

*   **Overall Shape:** It should be a roughly spherical shape, but flattened at the bottom for better bed adhesion during printing. The top should have a hole for accessing the pea inside.
*   **Parameters:**
    *   `diameter`:  The main diameter of the sphere.
    *   `height_factor`: A multiplier to adjust the overall height relative to the diameter.
    *   `wall_thickness`: The thickness of the rattle's wall.
    *   `set_fn_value`:  The `$fn` value for controlling the smoothness of the curves.
*   **Internal Features:**
    *   There should be a small cylindrical "pea holder" inside near the bottom, and a separate "pea" that sits inside it.
*   **Hole:** A hole in the top should go through to the internal cavity.

I need a single .scad file that generates this rattle with all these parameters, I would like to be able to set all of the parameters as variables at the beginning of the file.

Can you help me generate this OpenSCAD code?

[Assistant]
Rattle.scad:
```
// Rattle
// author: Benedict Endemann
// https://www.thingiverse.com/thing:3037626

////////// Thingiverse Customizer //////////
diameter = 30; // [25:1:150]
height_factor = 1.25; // [0.5:0.05:2]
wall_thickness = 0.8; // [0.1:0.1:4]
set_fn_value = 90; // [15:1:360]
$fn = set_fn_value;
////////// Thingiverse Customizer //////////

rattle(
    diameter       = diameter,
    height_factor  = height_factor,
    wall_thickness = wall_thickness
);

module rattle(
    diameter       = 30,
    height_factor  = 1.25,
    wall_thickness = 0.8
) {
    translate([
        0,
        0,
        diameter * height_factor / 2
    ])
        difference() {

            hull() {

                // basic sphere
                resize([
                    diameter,
                    diameter,
                    diameter * height_factor
                ])
                    sphere(
                        d = diameter
                    );

                // cylinder as socket
                translate([
                    0,
                    0,
                    -diameter * height_factor / 2
                ])
                    cylinder(
                        d = diameter / 2,
                        h = diameter * height_factor / 2
                    );
            }

            // remove inside of sphere
            resize([
                diameter - wall_thickness * 2,
                diameter - wall_thickness * 2,
                diameter * height_factor - wall_thickness * 2
            ])
                sphere(
                    d = diameter
                );

            // make hole in top
           cylinder(
                d = diameter / 3 / 2,
                h = diameter * height_factor
            );
        }

    // add pea holder
    cylinder(
        d = diameter / 10,
        h = diameter * height_factor / 15 + diameter / 3 / 2
    );

    // add pea
    translate([
        0,
        0,
        diameter * height_factor / 15 + diameter / 3 / 2
    ])
        sphere(
            d = diameter / 3
        );
}
```


[User]
I'm looking to get a parametric OpenSCAD model made for a holder, specifically for computer RAM or memory sticks. I often have old RAM lying around after upgrades, and I'd like to have a way to store them neatly.

The design should be a rectangular box with slots cut into it to hold the memory sticks. The user should be able to configure:

*   **Number of rows**: How many memory sticks will be stored side by side.
*   **Number of columns**: How many memory sticks will be stored front to back.
*   **Wall thickness**: The thickness of the walls of the holder and the dividers between slots.
*   **Corner radius**: The radius of the rounded corners on the outer edge of the holder.
*   **Slot width**: The width of a slot to hold a memory stick.
*   **Slot length**: The length of a slot to hold a memory stick.
*   **Holder height**: The overall height of the holder.

The holder should have a solid base, and the slots should go most of the way down, leaving a small base at the bottom. It needs to be fully parametric, so I can change any of those variables and the model will update properly. The design should use `minkowski` for rounded corners, and all dimensions are in millimeters. I don't need any specific libraries, just the core OpenSCAD language.

[Assistant]
MemoryHolder.scad:
```
rows = 6;
columns = 1;
wallThickness = 5;
cornerRadius = 3;
slotWidth = 7.1;
slotLength = 133.5;
holderHeight = 20;

$fn = 100;

module drawBase(width, length, height) {
    translate([0, 0, height / 2])
    union() {
        minkowski() {
            cube([width - cornerRadius * 2, length - cornerRadius * 2, height - cornerRadius * 2], center=true);
            sphere(cornerRadius);
        }
        translate([0, 0, -height / 2 + cornerRadius / 2])
        minkowski() {
            cube([width - cornerRadius * 2, length - cornerRadius * 2, cornerRadius], center=true);
            cylinder(cornerRadius, cornerRadius);
        }
    }
}

module drawHolder(rows, columns, wallThickness, cornerRadius, slotWidth, slotLength, holderHeight) {
    cutWidth = rows * (slotWidth + wallThickness) - wallThickness;
    cutLength = columns * (slotLength + wallThickness) - wallThickness;
    cutHeight = holderHeight - wallThickness;

    baseWidth = rows * (slotWidth + wallThickness) + wallThickness;
    baseLength = columns * (slotLength + wallThickness) + wallThickness;

    difference() {
        drawBase(baseWidth, baseLength, holderHeight);
        translate([-cutWidth / 2, -cutLength / 2, wallThickness])
        union() {
            for (column = [1:columns]) {
                for (row = [1:rows]) {
                    translate([(row - 1) * (slotWidth + wallThickness), (column - 1) * (slotLength + wallThickness), 0])
                    cube([slotWidth, slotLength, cutHeight]);
                }
            }
        }
    }
}

drawHolder(rows, columns, wallThickness, cornerRadius, slotWidth, slotLength, holderHeight);
```


[User]
Hey, I'm looking to create a parametric OpenSCAD design for a nut trap, specifically for use with an MPCNC. I've got a good idea of the basic structure and some specific dimensions in mind, and I'd like something that's as flexible as possible. I'm hoping you can help me create a reusable, parametric set of OpenSCAD files.

Here's what I'm thinking:

The core design should revolve around creating a "nut trap" that is inserted into a circular conduit. This is used to hold a nut in place for bolt attachment. I'd need the design to:

1.  Be fully parametric, so I can adjust everything, from conduit size to nut dimensions and hole locations.
2.  Be able to handle different conduit diameters and wall thicknesses, since those will vary based on the MPCNC build.  I'm particularly interested in 23.5, 25, and 25.4 mm outer diameters, and I'll need to specify the wall thickness.
3.  Have two nut trap/bolt hole positions along the length of the conduit. The distance from the bottom of the part to the center of the first hole/nut trap needs to be customizable, and the same goes for the distance between the two holes. There should also be some clearance at the top, to account for part fit.
4. The holes should be sized for M4 bolts, but the diameter for the bolt holes and hex nut pockets should be configurable.
5. The nut pockets should have a slightly chamfered edge where the nut goes in (to make inserting them easier, ideally it should use standard hex chamfer). I'd like to be able to define the height of the nut, and the chamfer as well.
6. Allow for the orientation of the hex nut pocket to be rotated 0 or 30 degrees.
7. Be easy to import into FreeCAD (and then export to a STEP file), so it should use `$fn = 0` for smoother curves and $fa/$fs values that will give a decent amount of resolution, while also generating a reasonably sized file.

I would like the overall structure to be modular. I'm imagining at least these modules:

*   `ConduitInsert()`: This should be the base module for the part that fits inside the conduit. It'll take the conduit diameter, wall thickness, and length of the part as parameters. It should be a cylindrical shape with material removed to create a "D" profile.
*   `HoleAndNutTrap()`: This module would generate the hole for the bolt and the nut trap. It would take parameters for the hole diameter, nut size, nut height, nut chamfer, and nut orientation.
*   `Thing()`: This module would combine the `ConduitInsert` with the number of `HoleAndNutTrap` modules (in my case two, but this could be a variable later on), positioned at the right distances.
*  There should be an outer module that allows the whole thing to be rendered.

I'd like to include some variables outside of any modules so they can be easily adjusted in the customizer. Things like:

*   `conduit_outer_diameter`
*   `conduit_wall_thickness`
*   `first_hole_position`
*   `hole_distance`
*   `top_clearance`
*   `hole_diameter`
*   `nut_size`
*   `nut_height`
*   `nut_orientation`

I'm not sure if it's best to use libraries for some of this. It's probably best to create it from scratch, I think that would make it the most customizable.

Could you help me design this in OpenSCAD?

Oh, and when you send over the code, please don't include the rendered result, just the OpenSCAD files. Thanks!

[Assistant]
NutTrap.scad:
```
// MPCNC tool mount nut trap
// https://www.thingiverse.com/thing:3038722
// Robbert Klarenbeek
//
// Remixed from https://www.thingiverse.com/thing:907882 (by GeoDave)
//
// - Uses standard spacings as shown on MPCNC assembly page
// - Built for 25mm conduit (international "F" version)
// - Uses $fn = 0 in order to create smooth arcs when imported
//   into FreeCAD, which in turn generates a smooth STEP export

// Size of the conduit used / MPCNC version
conduit_outer_diameter = 25; // [23.5:Version C (23.5mm), 25:Version F (25mm), 25.4:Version J (25.4mm)]
// How thick the wall of your conduit is
conduit_wall_thickness = 1.5;
// How far from the bottom the 1st hole will be
first_hole_position = 22.5;
// The distance between the 1st and 2nd hole
hole_distance = 75;
// Added space above the 2nd hole
top_clearance = 8;
// Diameter of the holes (for M4, use 4mm)
hole_diameter = 4;
// Distance between 2 parallel sides of the hex shape (for M4, use 7.2mm)
nut_size = 7.2;
// Height of your nut (for M4 nyloc nut, use 4.7mm)
nut_height = 4.7;
// Orientation of the hex shape
nut_orientation = 0; // [0:Parallel to long edge, 30:Parallel to short edge]

// Create anonymous scope to hide from customizer
{
    $fn = 0; // Keep at 0 for smooth FreeCAD import / STEP output
    $fa = 2;
    $fs = 0.1;
    cut_margin = 1;
    second_hole_position = first_hole_position + hole_distance;
    length = second_hole_position + top_clearance;
    diameter = conduit_outer_diameter - 2 * conduit_wall_thickness;
    radius_margin = diameter / 2 + cut_margin;
    nut_diameter = nut_size * 2 / sqrt(3);
    nut_chamfer_height = 1;
    nut_chamfer_diameter = nut_diameter - 2;
}

module ConduitInsert() {
    difference()
    {
        rotate([0, 90, 0])
            cylinder(d = diameter, h = length);
        translate([-cut_margin, -radius_margin, -radius_margin])
            cube([length + cut_margin * 2, radius_margin * 2, radius_margin]);
    }
}

module HoleAndNutTrap() {
    rotate([0, 0, nut_orientation]) translate([0, 0, -cut_margin]) {
        cylinder(d = hole_diameter, h = radius_margin + cut_margin);
        cylinder(d = nut_diameter, h = nut_height + cut_margin, $fn = 6);
        translate([0, 0, nut_height + cut_margin - 0.01])
            cylinder(d1 = nut_diameter, d2 = nut_chamfer_diameter, h = nut_chamfer_height, $fn = 6);
    }
}

module Thing() {
    // Using nested difference() to prevent FreeCAD import from crashing
    difference() {
        difference() {
            ConduitInsert();
            translate([first_hole_position, 0, 0])
                HoleAndNutTrap();
        }
        translate([second_hole_position, 0, 0])
            HoleAndNutTrap();
    }
}

Thing();

```


[User]
I need a parametric drill guide for the MPCNC nut trap. It should be designed for 25mm conduit, which is the "F" version. The guide will have two drill holes, and their positions and spacing are critical. The first hole should be 22.5mm from the bottom edge of the guide, and the distance between the first and second hole should be 75mm. I'll need to adjust the drill hole diameter to suit various tap sizes.

The guide needs to fit snugly around the 25mm conduit with a wall thickness of 5mm and needs a cutout that accommodates the conduit as well as provide a flat surface for clamping. The drill holes should also be surrounded by some extra clearance, say 6mm on each side.

I'd like this to be created as an OpenSCAD file that allows me to customize the following parameters:

*   `conduit_outer_diameter`: This should default to 25mm but allow for other sizes like 23.5mm (C) or 25.4mm (J).
*   `first_hole_position`:  Default to 22.5mm.
*   `hole_distance`: Default to 75mm.
*   `wall_thickness`: Default to 5mm.
*   `drill_hole_diameter`: This needs to be configurable.
*   `drill_hole_clearance`: Default to 6mm.

Please ensure the code is well-commented and uses smooth arcs or circles for the cuts when generating the STEP files. I plan on importing the design into FreeCAD, so keeping the `$fn` low is important, as well as a small `$fa` and `$fs` as it appears these also help with generating cleaner geometry when exporting.

[Assistant]
DrillGuide.scad:
```
// MPCNC nut trap drill guide
// https://www.thingiverse.com/thing:3038859
// Robbert Klarenbeek
//
// Remixed from https://www.thingiverse.com/thing:910707 (by GeoDave)
//
// - Uses standard spacings as shown on MPCNC assembly page
// - Built for 25mm conduit (international "F" version)
// - Uses $fn = 0 in order to create smooth arcs when imported
//   into FreeCAD, which in turn generates a smooth STEP export

// Size of the conduit used / MPCNC version
conduit_outer_diameter = 25; // [23.5:Version C (23.5mm), 25:Version F (25mm), 25.4:Version J (25.4mm)]
// How far from the bottom the 1st hole will be
first_hole_position = 22.5;
// The distance between the 1st and 2nd hole
hole_distance = 75;
// Thickness of the walls holding the conduit
wall_thickness = 5;
// Diameter of the drill holes
drill_hole_diameter = 4.2;
// Space on both sides of the drill holes
drill_hole_clearance = 6;

// Create anonymous scope to hide from customizer
{
    $fn = 0; // Keep at 0 for smooth FreeCAD import / STEP output
    $fa = 2;
    $fs = 0.1;

    cut_margin = 1;

    second_hole_position = first_hole_position + hole_distance;
    length = second_hole_position + drill_hole_clearance;
    width = conduit_outer_diameter + 2 * wall_thickness;
    height = conduit_outer_diameter / 2 + wall_thickness;
    cut_height = height + cut_margin * 2;

    first_cut_offset = -cut_margin;
    first_cut_length = first_hole_position - drill_hole_clearance + cut_margin;
    second_cut_offset = first_hole_position + drill_hole_clearance;
    second_cut_length = hole_distance - drill_hole_clearance * 2;
}

module Holder() {
    difference() {
        translate([0, -width / 2, 0])
            cube([length, width, height]);
        rotate([0, 90, 0]) translate([-height, 0, -cut_margin])
            cylinder(d = conduit_outer_diameter, h = length + cut_margin * 2);

        Trim();
    }
    translate([-wall_thickness, -width / 2, 0])
        cube([wall_thickness, width, wall_thickness + conduit_outer_diameter / 4]);
}

module Trim() {
    translate([first_cut_offset, -conduit_outer_diameter / 2, -cut_margin])
        cube([first_cut_length, conduit_outer_diameter, cut_height]);

    translate([second_cut_offset, -conduit_outer_diameter / 2, -cut_margin])
        cube([second_cut_length, conduit_outer_diameter, cut_height]);

    translate([first_hole_position, -(width + cut_margin * 2) / 2, wall_thickness + 2])
        cube([hole_distance, width + cut_margin * 2, cut_height]);
}

module DrillHole() {
    translate([0, 0, -cut_margin])
        cylinder(d = drill_hole_diameter, h = cut_height);
}

module Thing() {
    difference() {
        Holder();
        translate([first_hole_position, 0, 0])
            DrillHole();
        translate([second_hole_position, 0, 0])
            DrillHole();
    }
}

Thing();

```


[User]
I need a parametric OpenSCAD design for a multi-column card box, and I'd like it to be pretty flexible. I've got some code I've been playing with, but I'm not very good at OpenSCAD and I think it can be improved. It should generate either the box itself (the "caddy"), the lid, or both.

Here's what I have in mind:

*   **Multi-Column Layout:** The box should be able to have multiple columns, each with a potentially different number of compartments.  Each compartment in a column will have a specific width, thickness (how deep it is) and height.

*   **Compartment Definition:** Compartments should be defined by a list of lists (like `compartments` in the provided code) where each sub-list represents a column and each element in that sub-list is a list defining `[width, thickness, height, alignment]`.  Alignment will be a way to make compartments aligned to the left, right, or centered for cases where the columns might be shorter than their max width.  (like compartments[r][i][3])

*   **Parametric Dimensions:**
    *   I need to be able to set a `minimum_box_width`, and if my compartments don't add up to that wide, the box should use it. Otherwise, the box width should equal to the total of the maximum widths of each column added.
    *   Similarly, a `minimum_box_length` parameter.
    *   There should be a `divider_thickness` parameter for the walls between the compartments. It should auto-adjust to fill gaps in the column if the columns aren't the same length.
    *   A `divider_height` for the height of the compartment dividers.
    *   `compartment_width` and `compartment_height` parameters are there for convenience, in case all compartments are of a fixed size, but should not conflict with the list of lists definition mentioned above.
    *   `wall_thickness_minimum` parameter.
    *   `cover_top_clearance` value is for the distance between the top of the caddy and the inside of the cover.
    *   `cover_side_clearance` for additional space for the sides of the cover and lip.
    *   A `lip_height` for the height of the lip around the top of the caddy.
    *   `lip_thickness` for the thickness of the lip.
    *   A list of radii `fingers` for the finger grooves, one per column.

*   **Lip Option:** It needs a `caddy_lip` option, with a "yes" or "no" option.

*   **Output Options:** The output should be selectable, to create:
    *   Only the "caddy" (the main body of the box),
    *   Only the "cover" (the lid),
    *   "both" the caddy and the cover.

*   **Error Checking:** While I expect to enter valid numbers, some error handling for sanity checks would be nice.

I think that covers the main stuff. I'm not sure if you need to use any libraries to get this to work, so I'm leaving that up to you. Let me know if you need any additional information!

[Assistant]
cardbox_super.scad:
```
/***
  Multi-Column Card Box generator
  Based on https://www.thingiverse.com/thing:2120898

  Version 1.0
    Initial Release
  Version 1.1
    Auto-compute divider thickness, center partial fills
*/

outputs = "caddy"; // [cover:Cover Only,caddy:Caddy Only,both:Caddy and Cover]

// A value of 0 will use the widest compartment width as the overall width of the box
minimum_box_width=0;
// A value of 0 will use the combined thickness of all the compartments and appropriate dividers.
minimum_box_length=0;
// Thickness of the dividers separating the individual pockets
divider_thickness=1.5;
// This will determine how high the body walls will go. This value must be lower than the compartment height.
divider_height=60;
// Use compartment_width in the pocket array if compartments are all the same size
compartment_width=66;
// Use compartment_height in the pocket array if compartments are all the same size
compartment_height=91;
// The thickness of each wall (Not counting the lip thickness)
wall_thickness_minimum=1.5;

// Amount of height to add to the top cover
cover_top_clearance=1;
// Amount of material to add to the sides of the cover's sides and lip thickness
cover_side_clearance=.3;

// Serves as the stopper for the cover, preventing the cover from squishing the top of the compartments
caddy_lip = "yes"; // [no:no, yes:yes]

// Height of the lip. A higher value means less material needed for the cover
lip_height=10;

// Thickness of the caddy lip. Also determines the thickness of the cover walls.
lip_thickness = 2;

el_alamein = [
  [
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 5, compartment_height],
    [ compartment_width, 7, compartment_height],

    [ compartment_width, 3, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 5, compartment_height],
    [ compartment_width, 4, compartment_height],

    [ compartment_width, 22, compartment_height],
    [ compartment_width, 3, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 4, compartment_height],

    [ compartment_width, 6, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 4, compartment_height],
    [ compartment_width, 16, compartment_height],
  ]
];

star_realms_frontiers=[
  // divider_height = 60
  // compartment_width = 66
  // compartment_height = 91
  // lip_height = 10
  // caddy_lip = "yes"
  // cover_side_clearance= 0.3
  [
    // event
    [compartment_width,  8, compartment_height],
    //explorers
    [compartment_width, 10, compartment_height, -1],
    //main
    [compartment_width, 75, compartment_height],
    //expansion
    [compartment_width, 30, compartment_height],
    //rules/misc
    [compartment_width, 8, compartment_height],
    [compartment_width, 9, compartment_height],
  ],[
    [10,  140, compartment_height]
  ],[
    //players X 6
    [compartment_width, 6, compartment_height, -1],
    [compartment_width, 6, compartment_height,  1],
    [compartment_width, 6, compartment_height, -1],
    [compartment_width, 6, compartment_height,  1],
    [compartment_width, 6, compartment_height, -1],
    [compartment_width, 6, compartment_height,  1],

    //Command Decks x 11
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
    [compartment_width,  8, compartment_height],
  ]
];

// A vector of pockets defined by [width,thickness,height, alignment(<0:left , 0:center, >0:right)]
n_pockets=[  [    [compartment_width,  8, compartment_height],    [compartment_width, 10, compartment_height, -1],    [compartment_width, 75, compartment_height], [ compartment_width, 30, compartment_height],   [compartment_width, 8, compartment_height],    [compartment_width, 9, compartment_height],  ],[    [10,  140, compartment_height]  ],[    [compartment_width, 6, compartment_height, -1],    [compartment_width, 6, compartment_height,  1],    [compartment_width, 6, compartment_height, -1],    [compartment_width, 6, compartment_height,  1],    [compartment_width, 6, compartment_height, -1],    [compartment_width, 6, compartment_height,  1],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],    [compartment_width,  8, compartment_height],  ]];

// A vector of finger grooves radii for each column.  0 for no groove
fingers = [ 18, 0, 18 ];

compartments = n_pockets ? n_pockets : star_realms_frontiers;
rows = len(compartments);

function getAxisValuesFromVectorArray(v, a) =
    [ for (i = [0: len(v) - 1]) v[i][a] ];

function addToVector(v, s) =
    [ for (i = [0: len(v) - 1]) v[i] + s ];

function sumPrevious(v, i, s=0) =
    (i==s ? v[i] : v[i] + sumPrevious(v, i-1, s));

function getOffsets(v,s=0)=
    [ for (i = [0 : len(v)]) i==0 ? 0 : sumPrevious(v, i-1)+s ];

echo (compartments);
yValues = [for (i = [0:1 : rows-1]) getAxisValuesFromVectorArray(compartments[i], 1) ];
yValuesWithDividerThickness = [for (i = [0 : rows-1]) addToVector(yValues[i], divider_thickness) ];
offsets = [for (i = [0:1 : rows-1]) getOffsets(yValuesWithDividerThickness[i]) ];

echo ("Row ");
echo (yValues);
echo (yValuesWithDividerThickness);
echo ("Offsets:");
echo (offsets);

boxWidth_side = [for (i = [0:1: rows-1]) max(getAxisValuesFromVectorArray(compartments[i], 0)) ];
boxLength_side = [for (i = [0:1 : rows-1]) max(offsets[i][len(offsets[i])-2] + yValues[i][len(yValues[i])-1]) ];

boxWidth = max(sumPrevious(boxWidth_side, rows-1, 0), minimum_box_width);
boxLength = rows > 1 ? max(boxLength_side) : max(boxLength_side[0], minimum_box_length);

function computeDivider(r) =
    max(divider_thickness, (boxLength - boxLength_side[r] + divider_thickness * len(compartments[r]) ) / len(n_pockets[r]));

function initial_offset(r) =
    (computeDivider(r)/2 - divider_thickness) < 0 ? 0 : computeDivider(r)/2 - divider_thickness;

function rc_getOffsets(v, r, s=0) =
    [ for (i = [0 : len(v)]) i==0 ? initial_offset(r): sumPrevious(v, i-1)+initial_offset(r) ];

rc_yValuesWithDividerThickness = [for (i = [0 : rows-1]) addToVector(yValues[i], computeDivider(i)) ];
echo (rc_yValuesWithDividerThickness);
rc_offsets = [for (i = [0:1 : rows-1]) rc_getOffsets( rc_yValuesWithDividerThickness[i], i) ];
echo ("Recalculated Offsets:");
echo (rc_offsets);

echo(boxWidth);
echo(boxLength);

boxBody = [
    boxWidth + wall_thickness_minimum*(rows+2) + wall_thickness_minimum * (rows+2),
    boxLength + wall_thickness_minimum*2,
    divider_height
];

echo("Box Size: ", boxBody);

boxBody_side = [ for (i = [0:1 : rows-1]) [boxWidth_side[i] + wall_thickness_minimum, boxLength_side[i], divider_height] ];
compartmentBlockWidth = [ for (i = [0:1 : rows-1]) max(getAxisValuesFromVectorArray(compartments[i], 0)) ];
compartmentBlockDepth = [ for (i = [0:1 : rows-1]) boxLength ];
compartmentBlockHeight = [ for (i = [0:1 : rows-1]) max(getAxisValuesFromVectorArray(compartments[i], 2)) ];

bodyXoffset = [ for (r = [0:1:rows-1]) sumPrevious(boxBody_side, r, 0) - boxBody_side[r]];
echo("BodyXoffset: ",bodyXoffset);

// ** lip **
lip = [
    boxBody[0] + lip_thickness*2 + cover_side_clearance*2,
    boxBody[1] + lip_thickness*2 + cover_side_clearance*2,
    lip_height,
];
lip_offset = lip_thickness + cover_side_clearance;

// ** cover **
cover = [
    boxBody[0] + lip_thickness*2 + cover_side_clearance*2,
    boxBody[1] + lip_thickness*2 + cover_side_clearance*2,
    compartmentBlockHeight[0] + lip_thickness + wall_thickness_minimum-lip_height +cover_top_clearance
];

// ** cover cavity **
coverCavity = [
    cover[0] - lip_thickness*2,
    cover[1] - lip_thickness*2,
    cover[2] - cover_top_clearance
];

module body(){
    union(){
      difference(){
        union(){ //mainbox
          translate([lip_offset,lip_offset,0])
           cube(size=boxBody);
          if(caddy_lip=="yes"){
            translate ([0,0,0])
             //color([0,0,1])
              cube(size=lip);
          };
        };
        for (r = [0:1: rows-1])
          union(){
            translate([ bodyXoffset[r][0] + boxBody_side[r][0]/2 + (wall_thickness_minimum*(r+1)) + lip_offset*2, lip_offset*2, 0 ])
            for (i=[0:1:len(compartments[r])-1])
                translate([
                    -compartments[r][i][0]/2 +
                    // ** stagger **
                    (
                        compartments[r][i][3]<0?
                         (compartments[r][i][0] - boxBody_side[r][0])/2 +wall_thickness_minimum:
                        compartments[r][i][3]>0?
                        -(compartments[r][i][0] - boxBody_side[r][0])/2 -wall_thickness_minimum
                        :0
                    ),
                    rc_offsets[r][i],
                    wall_thickness_minimum
                ])
                //color([1,0,1])
                cube( size=[
                    compartments[r][i][0],
                    compartments[r][i][1],
                    compartmentBlockHeight[r]
                ]);
          }
        for (r = [0:1: rows-1])
        // ** Finger Groove
          if (fingers[r] > 0) {
            translate( [bodyXoffset[r][0] + boxBody_side[r][0]/2 + wall_thickness_minimum*r + lip_offset*2, lip_thickness, divider_height] )
            rotate([270,0,0])
                cylinder(h = coverCavity[1], r=fingers[r], center = false, $fn=300);
        };
      }
    }
}

module cover(){
    translate ([0,0,cover[2]+lip_height])
    rotate([0,180,0])
    difference(){
        // Cover Body
        translate([
            -(cover[0] )/2,
            -cover[1]/2,
            lip_height
        ])
            cube(cover);
        // Inner Cavity
        translate([
            -coverCavity[0]/2,
            -coverCavity[1]/2,
            lip_height-1
        ])
            cube(coverCavity);
    }
    echo(cover);
}

print_part();
module print_part() {
	if (outputs == "cover") {
        cover();
	} else if (outputs == "caddy") {
        translate([-boxBody[0]/2, -boxBody[1]/2, 0])
        body();
	} else if (outputs == "both") {
        translate ([-boxBody[0] - 10 -lip_thickness, -boxBody[1]/2, 0])
            body();
        translate ([cover[0]/2+10,0,0])
            cover();
	} else {
		both();
	}
}

```


[User]
I need an OpenSCAD design for a parametric mount to hold a cooling fan to an extruder motor. I've got some specific needs for this, and it's a bit more complex than just a simple bracket.

Here's what I need:

*   **Fan Support:**
    *   The mount needs to support 40mm, 50mm, and 60mm fans, and the user should be able to select between these.
    *   The fan thickness should also be configurable, and allow options like 10mm, 12mm, 15mm, 20mm, and 25mm.
    *   The design must include a circular guard around the fan for safety. The fan itself should be a complete and fully-modeled circular object with support for the required screws.
*   **Bolt Mounting:**
    *   The mount needs to attach to the extruder motor using four bolts.
    *   I need to be able to configure the bolt diameter, with common options being 2.7mm (M3) and 3.8mm (M4).
    *   There should be an option to set the bolt hole clearance.
    *   The X-axis distances between bolts should be configurable individually, specifically the distance between bolts 1 & 2, 2 & 3, and 3 & 4, using center-to-center measurements. I would expect the 1-2 distance to be equal to the 3-4 distance.
*   **Vertical Plate:**
    *   The design needs a vertical plate that provides the mounting points for the bolts.
    *   I need to set the X offset between the left or right edge of the vertical plate and the edge of the bolt holes.
    *   The Z offset between the bolt hole center and the bottom of the fan needs to be adjustable.
*   **Horizontal Plate:**
    *   The mount also needs a horizontal plate that connects to the vertical plate, supporting the fan.
    *   I need to be able to set the Y offset between the inner edge of the vertical plate and the edge of the fan.
    *   There should be an X offset factor that moves the horizontal plate in the X direction based on the fan size. This factor would be a percentage of the fan radius.

*   **Print Test:**
    *   A flag variable should exist, and if set to '1', it will only render a section of the vertical plate, just enough to test the accuracy of the bolt holes, which will save on print time. If set to '0', it will render the whole mount.

*   **User Interface**
     * All parameters should have user-friendly names and a reasonable range of options.
     * The code should be properly commented so that I can easily understand it.

Could you generate an OpenSCAD file for this? I don't need any external libraries, this can all be done with OpenSCAD's built in modules.

[Assistant]
ExtrudeFan.scad:
```
////----ExtrudeFan.scad----
////----OpenSCAD 2015.03-2----
////----2018.8.7----
////----By Kijja Chaovanakij----

/* [var] */
//number of fragments[30=low:40=medium:60=high]
$fn=60;
//print test the exact location of bolt hole
print_test=0; //[0:false,1:true]

/* [fan var] */
//fan size[50]
fan_size=50; //[40,50,60]
//fan thickness[15]
fan_thi=15; //[10,12,15,20,25]

/* [bolt var] */
//bolt diameter[3.8]
bolt_dia=3.8; //[2.7:M3,3.8:M4]
//bolt hole clearance[0.3]
bolt_hole_cl=0.3;
//X width between bolt 1 center to bolt 2 center[18]
bolt1_2_c2c=18;
//X width between bolt 2 center to bolt 3 center[14]
bolt2_3_c2c=14;
//X width between bolt 3 center to bolt 4 center[18]
bolt3_4_c2c=18;

/* [vertical plate var] */
//x offset between left or right edge and bolt edge[3]
vplate_lre_xoffset=3; //[3:0.5:5]
//z offset between bolt hole center and bottom of fan[8]
vplate_te_zoffset=8; //[6:1:12]

/* [horizontal plate var] */
//y offset between inner verical plate and fan edge[11]
hplate_vf_yoffset=11; //[8:1:20]
//x offset factor between middle of verical plate and center of fan[-0.5 to 0.5:0=no offset]
hplate_xoffset_factor=0.5; //[-0.6:0.1:0.6]

////----translate var----
vplate_c2c=bolt1_2_c2c+bolt2_3_c2c+bolt3_4_c2c;

////----main----
color("orange"){
  vertical_plate();
  horizontal_plate();
}//c
////----end main----

module horizontal_plate(){
  plate_thi=2;
  if (print_test==0){
    translate([
      0,
      plate_thi/2,
      vplate_te_zoffset+fan_thi+plate_thi/2]){

      hull(){
        cube([
          vplate_c2c+bolt_dia+vplate_lre_xoffset*2,
          0.01,
          plate_thi],
          true);
        translate([
          fan_size*hplate_xoffset_factor,
          -hplate_vf_yoffset,
          0])
        rotate([0,0,2.5])
          cube([fan_size*0.85,0.01,plate_thi],true);
      }//h

      translate([
        fan_size*hplate_xoffset_factor,
        -hplate_vf_yoffset-fan_size/2+1,
        -plate_thi/2])
      fan_guard(fan_size,plate_thi);
    }//t
  }//i
}//m
//

module vertical_plate(){
  plate_thi=2;
  rotate([90,0,0])
  difference(){
    hull(){
      tetra_cylinder(
        vplate_c2c,
        0,
        plate_thi,
        bolt_dia/2+vplate_lre_xoffset,
        true);

      if (print_test==0)
      translate([
        0,
        (vplate_te_zoffset+fan_thi+plate_thi)/2,    0])
      cube([
        vplate_c2c+bolt_dia+vplate_lre_xoffset*2,
        vplate_te_zoffset+fan_thi+plate_thi,
        plate_thi],
        true);
    }//h

    for (i=[
      -bolt1_2_c2c-bolt2_3_c2c/2,
      -bolt2_3_c2c/2,
      bolt2_3_c2c/2,
      bolt3_4_c2c+bolt2_3_c2c/2])
    translate([i,0,0])
    cylinder(
      plate_thi+0.02,
      bolt_dia/2+bolt_hole_cl,
      bolt_dia/2+bolt_hole_cl,
      true);
  }//d
}//m
//

module fan_guard(fan_size,fan_thi){
  if (fan_size==60){
    fan_bolt_hole_dia=4.2;
    fan_hub_dia=33;
    fan_e2e=45.9;
    fan(
      fan_size,
      fan_thi,
      fan_bolt_hole_dia,
      fan_hub_dia,
      fan_e2e);
  }//i

  else if (fan_size==50){
    fan_bolt_hole_dia=4.2;
    fan_hub_dia=27;
    fan_e2e=35.9;
    fan(
      fan_size,
      fan_thi,
      fan_bolt_hole_dia,
      fan_hub_dia,
      fan_e2e);
  }//e

  else if (fan_size==40){
    fan_bolt_hole_dia=3.3;
    fan_hub_dia=26;
    fan_e2e=28.6;
    fan(
      fan_size,
      fan_thi,
      fan_bolt_hole_dia,
      fan_hub_dia,
      fan_e2e);
  }//e
}//m
//

module fan(fan_size,fan_thi,fan_bolt_hole_dia,fan_hub_dia,fan_e2e){
  fan_c2c=fan_e2e+fan_bolt_hole_dia;
  fan_bolt_offset_rad=fan_size/2-fan_c2c/2;
  difference(){
    hull(){
      tetra_cylinder(
        fan_c2c,
        fan_c2c,
        fan_thi,
        fan_bolt_offset_rad,
        false);
    }//h

    translate([0,0,-0.01]){
      tetra_cylinder(
        fan_c2c,
        fan_c2c,
        fan_thi+0.02,
        fan_bolt_hole_dia/2,
        false);
      cylinder(
        fan_thi+0.02,
        fan_size/2-1.2,
        fan_size/2-1.2,
        false);
    }//t
  }//d

  cylinder(
    fan_thi,
    fan_hub_dia/2,
    fan_hub_dia/2,
    false);
  for (i=[45:90:315])
    translate([
      (fan_size/4+fan_hub_dia/4)*cos(i),
      (fan_size/4+fan_hub_dia/4)*sin(i),
      fan_thi/2])
    rotate([0,0,i])
    cube([
      fan_size/2-fan_hub_dia/2+1,
      fan_bolt_hole_dia,
      fan_thi],
      true);
}//m
//

module tetra_cylinder(xwid,ywid,height,rad,truth){
  for (i=[-xwid/2,xwid/2])
  for (j=[-ywid/2,ywid/2])
    translate([i,j,0])
    cylinder(h=height,r=rad,r=rad,center=truth);
}//m
//

```