[User]
Hi, I'm looking to create a parametric pneumatic cylinder design in OpenSCAD. I've seen a few examples, but I need one with specific features and flexibility. I want to be able to easily adjust the bore width and height, the stroke length, wall thickness, piston thickness, and rod diameter. The rod diameter needs to be based on a standard metric screw size, so I'd like that to be taken into account. I need to be able to adjust seal thickness, clamp width, hole spacing for fasteners, the screw hole size (with a small tolerance built in for easy insertion) and whether or not fastener holes are included.

Also, I need options for different tube socket types - specifically "M5", "4mm", "4mmLock", and "4mmLockSupport." I'd also like to set the mounting hole diameter for both the cylinder end and the rod end, and control the nut thickness used to secure the rod and piston, along with extra wall thickness around the rod and mounting holes. There also needs to be an adjustable piston clearance. Finally, it would be great if the output would provide details on the dimensions needed for the rod, fasteners, and seals. I'm looking to generate a few different STL files for various designs and use a parametric seal cutter based on thingiverse id 3020690, so I'm aware that it needs the seal dimensions.

I need to use the `iso_thread` module and related functions for metric threads. It seems like that module might be from a thread library, but I'm not sure which one. I don't want to import the entire library if it's a big one, just the files necessary for those specific functions. It also needs to have the functions for the nut diameter and head diameter calculations.

Could you please generate the OpenSCAD file(s) for this? I want to be able to adjust all of those parameters and generate the different STLs as needed.

[Assistant]
CompactPneumaticCylinder.scad:
```
// Parametric pneumatic cylinder
// Vincent Groenhuis
// September 27, 2018

// Bore width
boreW=16;

// Bore height
boreH=16;

// Cylinder stroke
stroke=30;

// Wall thickness
wall=4;

// Piston thickness
piston=4;

// Rod diameter, must be an existing metric screw diameter
rodD=3;

// Seal thickness
seal=1;

// Width of seal clamp lids in cylinder at bore end.
clampW=3;

// Maximum X hole distance. Actual distance may be smaller depending on cylinder dimensions.
xMaxHoleDist=15;

// Maximum Y hole distance. Actual distance may be smaller depending on cylinder dimensions.
yMaxHoleDist=15;

// Screw hole size
hole=2;

// Screw extra hole size, makes inserting easier
extraHole = 0.15;

// Tube socket type, choose between "M5", "4mm", "4mmLock" or "4mmLockSupport"
socketType = "M5";

// Mounting hole diameter, both for cylinder end and rod end
mountHoleD = 4;

// Nut thickness
nutT = 2;

// Add extra mm's for reinforcements around rod and mount holes
rodHeadExtraWall = 1;

// Piston clearance
clearance = 0.1;

// Enable or disable fastener holes for housing, top and bottom.
useFastenerHoles = true;

// Set to >=50 for STL export
$fn=50;

width=wall+boreW+wall;
length=wall+rodD+seal+piston+seal+rodD+stroke+seal+wall;
rodEndLength=3*rodD;
rodLength=length-wall+rodEndLength;

xHoles = 1+2*ceil((width-wall)/xMaxHoleDist/2-0.5);
yHoles = ceil((length-wall)/yMaxHoleDist);
xHoleSpacing = (width-wall)/xHoles;
yHoleSpacing = (length-wall)/yHoles;

holePos = concat(
   [for(i=[0:xHoles]) [wall/2+(width-wall)*i/xHoles,wall/2]],
   [for(i=[0:xHoles]) [wall/2+(width-wall)*i/xHoles,length-wall/2]],
   [for(i=[1:yHoles-1]) [wall/2,wall/2+(length-wall)*i/yHoles]],
   [for(i=[1:yHoles-1]) [width-wall/2,wall/2+(length-wall)*i/yHoles]]
);

echo(str("Hole spacing: x=",xHoleSpacing,"mm,y=",yHoleSpacing,"mm"));
echo(str("Seal cutter essential parameters: <b>width=",boreW+0.3,", height=",boreH+0.3,", angle=15, holeSize=",rodD,", holePositions=[[0,0,0],[0,1,0],[0,0,0]]</b>"));
echo(str("Rod length: <b>",rodLength," mm</b> with threads: <b>piston: ",(nutT+seal+piston+seal+nutT)," mm, rod end: ",rodEndLength," mm</b>"));
echo(str("Fastener screws: <b>M",hole," x ",boreH+wall+2," mm</b>"));

///////////////// Commands

translate([width,0,wall])rotate([0,180,0])makeBottom();
translate([width+3,0,0])makeHousing();
translate([(width+3)*2,0,0])makeTop();
translate([(width+3)*3,0,0])rotate([-90,0,0])translate([0,-piston,0])makePiston();
translate([(width+3)*3,boreH+2*wall,0])
translate([width/2,boreH/2,0])makeBasicRodHead(rodD=rodD,mountHoleD=mountHoleD,extraWall=rodHeadExtraWall);
translate([(width+3)*4,0,0])
translate([width/2,boreH/2,0])makeCrossNut(d=rodD,D=min(boreW,boreH)-4,t=nutT);
translate([(width+3)*4,boreH+2*wall,0])
translate([width/2,boreH/2,0])makeCrossNut(d=rodD,D=min(boreW,boreH)-4,t=nutT);
if (socketType == "4mmLock" || socketType == "4mmLockSupport") {
  translate([(width+3)*5,0,0])translate([width/2,boreH/2])make4mmRim();
  translate([(width+3)*5,boreH+2*wall,0])translate([width/2,boreH/2])make4mmRim();
}

//////////// Modules and functions

// Makes a rim for 4 mm tubes for use with the 4mm lock socket
module make4mmRim(){
    difference(){
        union(){
            cylinder(h=8,d=5.8);
            translate([-1.5,-1,0])cube([3,8,4.8]);
        }
        translate([0,0,-1])cylinder(h=10,d=3.8);
        translate([-1.3,-4,-1])cube([2.6,4,10]);
        //translate([0,0,5])cylinder(h=4,d=7);
    }
}

// Makes the bottom plate
// Origin is in bottom-left-down corner
module makeBottom(){
  difference(){
    union(){
      cube([width,length,wall]);
      translate([0,0,wall])mirror([0,0,1])plateCornerTriangles();
    }

    makeHolePattern();
  }
}

// Makes the top plate
module makeTop(){
  difference(){
      union(){
        cube([width,length,wall]);
        plateCornerTriangles();
      }
    translate([wall+boreW/2,wall+1,-1])cylinder(h=wall*3/4+1,d=2);
    translate([wall+boreW/2,wall+6,wall*1/4])cylinder(h=wall*3/4+1,d=2);
    translate([wall+boreW/2,wall+6,wall*3/4])cylinder(h=wall*1/4+1,d=4);
    translate([wall+boreW/2-1.25,wall+1,wall*1/4])cube([2.5,6,wall/2]);

    translate([wall+boreW/2,length-wall-2,-1])cylinder(h=wall*3/4+1,d=2);
    translate([wall+boreW/2,length-wall-6,wall*1/4])cylinder(h=wall*3/4+1,d=2);
    translate([wall+boreW/2,length-wall-6,wall*3/4])cylinder(h=wall*1/4+1,d=4);
    translate([wall+boreW/2-1.25,length-wall-7,wall*1/4])cube([2.5,5,wall/2]);

    translate([0,0,-boreH-wall])makeHolePattern();
  }
  translate([wall+boreW/2,wall+6,wall])makeSocket(socketType);
  translate([wall+boreW/2,length-wall-6,wall])makeSocket(socketType);
}

module plateCornerTriangles(){
    difference(){
        rotate([180,0,0])rotate([0,90,0])linear_extrude(width)polygon([[0,0],[wall,0],[0,wall]]);
        translate([wall,-wall-1,-1])cube([boreW,wall+1,wall+2]);
    }
}

module makeHousing(){
  cc=min(mountHoleD+rodHeadExtraWall,boreH/2)*(1-tan(22.5));
  union(){
  difference(){
    cube([width,length,boreH]);
    translate([wall,wall,-1])
      cube([width-2*wall,length-2*wall,boreH+2]);
    translate([0,0,-wall])makeHolePattern();
    translate([0,wall+2,0])makeRod();
  }
  difference(){
      union(){
        translate([wall,length-(wall+seal+0.2+2)])cube([clampW,2,boreH]);
        translate([wall+boreW-clampW,length-(wall+seal+0.2+2)])cube([clampW,2,boreH]);
      }
      translate([width/2,length-(wall+seal+0.2+2+0.5),boreH/2])rotate([-90,0,0])cylinder(h=3,d=min(boreW,boreH)-3);
  }

  // Mount
  difference(){
    translate([0,-mountHoleD*2-rodHeadExtraWall,0])cube([width,mountHoleD*2+rodHeadExtraWall,boreH]);
    translate([wall,-mountHoleD*2-rodHeadExtraWall-1,-1])cube([boreW,mountHoleD*2+rodHeadExtraWall+1,boreH+2]);
    translate([-1,-mountHoleD,boreH/2])rotate([0,90,0])cylinder(width+2,d=mountHoleD);
    // Cut off corners
    translate([0,-mountHoleD*2-rodHeadExtraWall,boreH])rotate([0,90,0])cutCornerShape(h=width+2,d=cc);
    translate([0,-mountHoleD*2-rodHeadExtraWall,0])rotate([90,0,0])rotate([0,90,0])cutCornerShape(h=width+2,d=cc);
    }
  }
}

module cutCornerShape(h,d){
  linear_extrude(height=h)polygon(points=[[-1,-1],[d,-1],[d,0],[0,d],[-1,d]]);
}

// Makes cross-shaped nut that can be spanned by hand
// Outer diameter D, thread diameter d, thickness t
module makeCrossNut(d,D,t){
    difference(){
        cylinder(h=t,d=D);
        for(a=[0:90:270]){
            rotate([0,0,a])translate([D*0.6,0,-1])cylinder(h=t+2,d=D*0.5);
        }
        translate([0,0,-1])iso_thread(m=d,p=iso_pitch_coarse(d),l=t+2,t=0,cap=0);
    }
}

// Makes rod
module makeRod(){
  translate([width/2,0,boreH/2])
    rotate([-90,0,0])
      cylinder(h=length+piston+2,d=rodD);
}

module cylinder_outer(h,d,fn){
   fudge = 1/cos(180/fn);
   cylinder(h=h,d=d*fudge,$fn=fn);
}

module makeHole(h,d,headD,nutD,depth){
    union(){
      cylinder(h=h,d=d);
      translate([0,0,-1])cylinder_outer(h=depth+1,d=nutD,fn=6);
      translate([0,0,h-depth])cylinder_outer(h=depth+1,d=headD,fn=$fn);
    }
}

module makeHolePattern(){
  if(useFastenerHoles){
      union(){
          for (i=[0:len(holePos)-1]) {
              translate([holePos[i][0],holePos[i][1],0])
                makeHole(h=boreH+2*wall,d=hole+extraHole,depth=wall/2,headD=iso_head_diam(hole)+0.1,nutD=iso_nut_diam(hole)+0.1);
          }
      }
  }
}

// Make piston with hole for rod
module makePiston(){
  translate([width/2,piston/2,boreH/2])
  difference(){
      cube([boreW-2*clearance,piston,boreH-2*clearance],center=true);
      translate([0,-piston/2-0.5,0])rotate([-90,0,0])
        iso_thread(m=rodD,p=iso_pitch_coarse(rodD),l=piston+1,t=0,cap=0);
  }
}

// Makes rod head that can be screwed or glued to the rod
// extraWall: additional mm's around the hole for reinforcements
module makeBasicRodHead(rodD,mountHoleD,extraWall){
    holeH=rodD*3;
    difference(){
    union(){
      makeCylinderThread(h=holeH,d=rodD*2+extraWall*2,m=rodD);
      translate([0,0,holeH])cylinder(h=mountHoleD,d=rodD*2+extraWall*2);
      translate([0,0,holeH+mountHoleD+extraWall])rotate([0,90,0])cylinder(h=rodD*2+extraWall*2,d=mountHoleD*2+extraWall*2,center=true);
    }
      translate([0,0,holeH+mountHoleD+extraWall])rotate([0,90,0])cylinder(h=rodD*2+extraWall*2+2,d=mountHoleD,center=true);
    }
}

// Makes 4 mm connection with lock
module plug4mmLock(useSupport){
  // heights:0-4: tube 4-9: gap,9-12: top
difference(){
  cylinder(h=12,d=12);
  translate([0,0,-1])cylinder(h=14,d=3.8);
  translate([0,0,4])cylinder(h=9,d=6.4);
  translate([0,0,3])cylinder(h=1.1,d1=3.8,d2=5);
  translate([-1.5,-1.5,4])cube([10,10,9]);
  translate([-2,-7,4])cube([10,7,5.4]);
}
  if(useSupport)
  difference(){
  intersection(){
    translate([-1.2,-7,4.2])cube([10,5.2,5]);
    cylinder(h=12,d=11);
  }
  cylinder(h=12,d=7);
  }
}

module plug4mmNoLock(){
difference(){
  cylinder(h=5,d=10);
  translate([0,0,-1])cylinder(h=7,d=3.8);
  translate([0,0,4])cylinder(h=1.1,d1=3.8,d2=5);
}
}

module makeSocket(mytype){
  if(mytype=="4mm"){
    plug4mmNoLock();
  } else if(mytype=="4mmLock"){
    plug4mmLock(false);
  } else if(mytype=="4mmLockSupport"){
    plug4mmLock(true);
  }else if(mytype=="M5"){
    makeCylinderThread(m=5,h=5,d=10,t=0);
  }else{
    echo("Use 4mm, 4mmLock, 4mmLockSupport or M5!");
    plug4mmNoLock();
  }
}

// ISO screw thread modules by RevK @TheRealRevK
// https://en.wikipedia.org/wiki/ISO_metric_screw_thread
// Source: https://www.thingiverse.com/thing:2158656
// Modified by Vincent Groenhuis

function iso_pitch_coarse(m)   // Return standard coarse pitch for m value
=lookup(m,[[1,0.25],[1.2,0.25],[1.4,0.3],[1.6,0.35],[1.8,0.35],[2,0.4],[2.5,0.45],[3,0.5],[3.5,0.6],[4,0.7],[5,0.8],[6,1],[7,1],[8,1.25],[10,1.5],[12,1.75],[14,2],[16,2],[18,2.5],[20,2.5],[22,2.5],[24,3],[27,3],[30,3.5],[33,3.5],[36,4],[39,4],[42,4.5],[48,5],[52,5],[56,5.5],[60,5.5],[62,6]]);

function iso_head_diam(m)
=lookup(m,[[2,3.8],[2.5,4.5],[3,5.5],[3.5,6],[4,7],[5,8.5],[6,10],[8,13]]);

// distance across flats
function iso_nut_diam(m)
=lookup(m,[[2,4],[2.5,5],[3,5.5],[3.5,6],[4,7],[5,8],[6,10],[7,11],[8,13]]);

function iso_nut_thickness(m)
=lookup(m,[[2,1.6],[2.5,2],[3,2.4],[3.5,2.8],[4,3.2],[5,4],[6,5],[7,5.5],[8,6.5]]);

module iso_thread(  // Generate ISO / UTS thread, centred 0,0,
    m=20,    // M size, mm, (outer diameter)
    p=0,  // Pitch, mm (0 for standard coarse pitch)
    l=50,   // length
    t=0,    // tolerance to add (for internal thread)
    cap=1,  // capped ends. If uncapped, length is half a turn more top and bottom
)
{
    p=(p?p:iso_pitch_coarse(m));
    r=m/2; // radius
    h=sqrt(3)/2*p;  // height of thread
    fn=round($fn?$fn:36); // number of points per turn
    fa=360/fn; // angle of each point
    n=max(fn+1,round(fn*(l+(cap?p*2:0))/p)+1); // total number of points
    q=min(p/16,t); // thread width adjust
    p1=[for(i=[0:1:n-1])[cos(i*fa)*(r-5*h/8+t),sin(i*fa)*(r-5*h/8+t),i*p/fn+q]];
    p2=[for(i=[0:1:n-1])[cos(i*fa)*(r-5*h/8+t),sin(i*fa)*(r-5*h/8+t),i*p/fn+p/4-q]];
    p3=[for(i=[0:1:n-1])[cos(i*fa)*(r+t),sin(i*fa)*(r+t),i*p/fn+p/2+p/8-p/16-q]];
    p4=[for(i=[0:1:n-1])[cos(i*fa)*(r+t),sin(i*fa)*(r+t),i*p/fn+p/2+p/8+p/16+q]];
    p5=[for(i=[0:1:n-1])[cos(i*fa)*(r-5*h/8+t),sin(i*fa)*(r-5*h/8+t),i*p/fn+p+q]];
    p6=[[0,0,p/2],[0,0,n*p/fn+p/2]];

    t1=[for(i=[0:1:fn-1])[n*5,i,i+1]];
    t2=[[n*5,n,0],[n*5,n*2,n],[n*5,n*3,n*2],[n*5,n*4,n*3]];
    t3=[for(i=[0:1:n-2])[i,i+n,i+1]];
    t4=[for(i=[0:1:n-2])[i+n,i+n+1,i+1]];
    t5=[for(i=[0:1:n-2])[i+n,i+n*2,i+n+1]];
    t6=[for(i=[0:1:n-2])[i+n*2,i+n*2+1,i+n+1]];
    t7=[for(i=[0:1:n-2])[i+n*2,i+n*3,i+n*2+1]];
    t8=[for(i=[0:1:n-2])[i+n*3,i+n*3+1,i+n*2+1]];
    t9=[for(i=[0:1:n-2])[i+n*3,i+n*4,i+n*3+1]];
    t10=[for(i=[0:1:n-2])[i+n*4,i+n*4+1,i+n*3+1]];
    t11=[for(i=[0:1:fn-1])[n*5+1,n*5-i-1,n*5-i-2]];
    t12=[[n*5+1,n*4-1,n*5-1],[n*5+1,n*3-1,n*4-1],[n*5+1,n*2-1,n*3-1],[n*5+1,n-1,n*2-1]];

    intersection()
    {
        translate([0,0,-p/2-(cap?p:0)])
        polyhedron(points=concat(p1,p2,p3,p4,p5,p6),
        faces=concat(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12));
        if(cap)hull()
        { // champhered ends and capped to exact length
            cylinder(r1=r-5*h/8+t,r2=r+t,h=5*h/8*tan(30));
            translate([0,0,l-5*h/8*tan(30)])cylinder(r1=r+t,r2=r-5*h/8+t,h=5*h/8*tan(30));
        }
    }
}

module makeCylinderThread(m,h,d,t=0)
{
    p=iso_pitch_coarse(m);
    difference()
    {
        cylinder(h=h,d=d);
        translate([0,0,-p/2])iso_thread(m=m,p=p,l=h+p,t=t,cap=0);
    }
}

```


[User]
I'm looking to design a door handle system for a Prusa enclosure that uses small, round neodymium magnets to hold the door closed. I need the design to be parametric, so I can adjust it for different sized magnets and thicknesses of the acrylic panels I'm using for the enclosure.

Specifically, I need the following features:

1.  **Magnet Holders:** The design needs to include a "magnet holder" module, which is basically a cube with a cylindrical recess to hold the magnet. The magnet should sit slightly proud of the recess to ensure it makes contact with its counterpart and isn't sunken too deep. I'd also like some extra material around the magnet to hold it securely.
2.  **Door Handles:** I need a "door handle" module that is a composite of *two* magnet holders, arranged side-by-side. This will be attached to the door. There will need to be an additional "counter handle" attached to the frame so that the two magnet holders meet.
3.  **Parametric Design:** The design must be fully parametric based on:
    *   `magnet_diameter`
    *   `magnet_thickness`
    *   `glass_thickness` (or rather, the thickness of the acrylic/plexiglass sheet being used).

4.  **Mounting:** The magnet holders should have provisions for mounting to the enclosure frame. This will likely take the form of screw holes.
5.  **Glass/Acrylic Support:** The design also needs to include a few support pieces to help secure the glass/acrylic panel.
6.  **Handle Shaping:** I'd like the door handle itself to have a slightly ergonomic design, so if you could incorporate a small cutout to make it a little easier to grab, that would be great. Nothing too fancy.

I don't need to specify libraries, but I do need it all written in one or more OpenSCAD files. I think a single file might be best for this project.

[Assistant]
pedhg-v0.3.scad:
```
// configurable stuff

magnet_diameter = 5;    // [1:1:10]
magnet_thickness = 3;   // [1:1:10]
glass_thickness = 3;    // [1:1:10]

/* [Hidden] */

// number of facets
$fn = 50;

t2 = 5;     // extra edging around magnet to hold it in place

// extra space for magnet to stick out from recessed hole, to avoid the magnet from sinking in too deep and not making contact with its counterpart
// also my 3mm magnet seems to only be 2.5mm thick - need to account for this
t3 = 1;

// aliases to simplify formulas

d1 = magnet_diameter + 0.2; // made it a bit bigger - too difficult to rely on stud friction - just use superglue/epoxy
t1 = magnet_thickness;
g1 = glass_thickness;
x2 = (d1+(2*t2));   // thickness of 1 magnet holder
x3 = 2*x2;          // two magnet holders

// this makes a "magnet holder" i.e. a cube with a cylindrical hole large enough to accomodate our magnet
module make_magnet_holder () {

    difference() {

        translate([-(d1+(2*t2))/2, -(d1+(2*t2))/2,0]) cube([d1+(2*t2), d1+(2*t2), t1+t2]);
        translate([0,0,-t3]) cylinder(h=t1, r1=d1/2, r2=d1/2);
    }

} // end module make_magnet_holder

// probably should have named this something else...but its basically the composite of two magnet_holder(s)
module make_door_handle () {

    translate([0,0,(d1+(2*t2))/2])
    rotate([90,0,0]) {
         make_magnet_holder();
    }
    translate([(d1+(2*t2))-0.01,0,(d1+(2*t2))/2])
    rotate([90,0,0]) {
         make_magnet_holder();
    }

} // end module make_door_handle

// use the power of mirror() in a for loop - so that we can copy the objects and position them correctly at the same time :)
for (i=[0,1]) mirror([i,0,0]) {

    // this is the magnet holder for our door handle
    translate([((d1+(2*t2))/2)+1.5,0,0]) make_door_handle();

    // magnet holder on the LACK table
    translate([((d1+(2*t2))/2)+1.5,4,0]) mirror([0,1,0]) make_door_handle();

    // these bits holds the glass in place
    translate([1.5,-(t1+t2),0]) mirror([0,1,0]) cube([x3,25,3]);
    translate([1.5,-(t1+t2+g1+4+0.5),0]) cube([x3,4,d1+(2*t2)]);

    // handle with a small cut out
    translate([1.5,-((2*(t1+t2)+g1)+16),0])
    difference() {
         cube([x3,4,30]);
         translate([(x3/2),8,28]) rotate([70,0,0]) cylinder(h=20,r1=d1*1.25,r2=d1*1.25);

    }

    // screw holes for attaching the magnet holders to the LACK table
    translate([1.5,t1+t2+4,0])
    difference() {
        cube([x3,10,5]);
        translate([x3/4,10/2,-0.1]) cylinder(h=6, r1=2, r2=2);
        translate([x3/4*3,10/2,-0.1]) cylinder(h=6, r1=2, r2=2);
    }
}

```


[User]
I need a design for an inner diameter measuring tool, similar to the one described above. It should have two legs, one fixed and one that pivots. The pivoting leg should have a pointer that indicates the measured diameter. The design should be fully parametric, allowing me to adjust things like the length of the legs, the aperture angle, and the dimensions of various parts.

Specifically, I need the following:

*   **Legs:** Two legs, one fixed and one pivoting. The length of these legs should be adjustable via a parameter. The fixed leg should have a fixed measuring point at its end, while the pivoting leg should have a point that moves as the leg pivots. Both measuring points should be clearly defined and accurate.
*   **Pivoting Mechanism:** A method for the pivoting leg to rotate, with an adjustable angle parameter. I'd like to control this rotation with a threaded rod.
*  **Pointer:** The pivoting leg should have a clear pointer extending from it that can be used to take readings. I want the pointer to have an adjustable length/size.
*  **Threaded Rod:** The mechanism should also include a threaded rod, which pivots and is rotated using a knob, to push on the sliding part that is connected to the pivoted leg. The dimensions of the threaded rod should be adjustable. The rod should be an M4 thread
*   **Body:** A central body that houses the pivoting mechanism and provides a base for the fixed leg.
*   **Customizable Viewing:** I want to be able to select which parts of the design are rendered (e.g., just the legs, just the body, etc.) as well as seeing the whole tool assembled. I'd like to be able to have a single variable to switch between these options.
*   **Units:** The design should be in millimeters.
*   **Labeling:** It would be great if the design could also dynamically generate text showing the minimum, current, and maximum measurable inner diameters based on the given parameters.

I would prefer this design be done using the MCAD library for standard parts like the threaded rod, bolts, and nuts and use standard functions for things like cylinders and cubes. The design should use the "hull()" and "difference()" operators, similar to the example I included, and the math functions of OpenSCAD to compute the inner diameter from the angle of the pivoted leg.

[Assistant]
Innendurchmesser-Lehre.scad:
```
// -----------------------------------------------------
// Innendurchmesser-Lehre
//
//  Detlev Ahlgrimm, 09.2018
// -----------------------------------------------------

// Lenght
lng=120;       // [50:150]

// Aperture Angle
ow=0;         // [0:90]

// What to show
show=0;       // [0:assembled, 1:all part together, 2:lower part, 3:upper part, 4:wheel, 5:pusher]

/* [Hidden] */
$fn=100;
wms=atan(10/lng);         // Winkel zur Messpitze
kms=sqrt(lng*lng+100);    // Laenge Kathete zur Messpitze

// -----------------------------------------------------
//  lg : Laenge Gewinde
//  lm : Laenge Mutter
// -----------------------------------------------------
module SchraubeM4(lg=20, lm=3.1, vu=false) {
  union() {
    cylinder(r=4.2, h=lm, $fn=6);
    translate([0, 0, lm-0.1]) cylinder(d=4.5, h=lg+0.1);
  }
  if(vu) {
    translate([0, 0, lm]) cylinder(r1=4.2, r2=4.5/2, h=4.2-4.5/2, $fn=6);
  }
}

// -----------------------------------------------------
//  Innendurchmesser aus Winkel (gem. Kosinussatz)
// -----------------------------------------------------
function Innendurchmesser(winkel) = sqrt(kms*kms - 2*kms * kms * cos(winkel) + kms*kms);
/*echo("min=", Innendurchmesser(0+2*wms));
echo("cur=", Innendurchmesser(ow+2*wms));
echo("max=", Innendurchmesser(90+2*wms));*/

// -----------------------------------------------------
//  ra  : Aussenradius
//  ri  : Innenradius
//  h   : Hoehe
//  a   : Oeffnungswinkel
// -----------------------------------------------------
module RohrTeil(ra, ri, h, a) {
  da=2*ra;
  difference() {
    cylinder(r=ra, h=h);
    translate([0, 0, -0.1]) cylinder(r=ri, h=h+0.2);
    if(a<=90)       translate([0, 0, -0.1]) linear_extrude(h+0.2) polygon([[0,0], [0,da], [da,da],                                [sin(a)*ra, cos(a)*ra]]);
    else if(a<=180) translate([0, 0, -0.1]) linear_extrude(h+0.2) polygon([[0,0], [0,da], [da,da], [da,-da],                      [sin(a)*ra, cos(a)*ra]]);
    else if(a<=270) translate([0, 0, -0.1]) linear_extrude(h+0.2) polygon([[0,0], [0,da], [da,da], [da,-da], [-da,-da],           [sin(a)*ra, cos(a)*ra]]);
    else            translate([0, 0, -0.1]) linear_extrude(h+0.2) polygon([[0,0], [0,da], [da,da], [da,-da], [-da,-da], [-da,da], [sin(a)*ra, cos(a)*ra]]);
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module Bein1(l=lng) {
  difference() {
    union() {
      cylinder(r=20, h=5);
      translate([15, -5, 0]) cube([l-15-5, 5, 5]);      // das Bein
      hull() {                                          // die Messpitze
        translate([l-5, -5, 0]) linear_extrude(0.25) polygon([[0,0], [4.5,-5], [5,-5], [3,0]]);
        translate([l-5-10, -5, 0]) cube([10, 5, 5]);
      }
      difference() {
        rotate([0, 0, 10]) RohrTeil(42, 19, 5, 200);
        rotate([0, 0, 30]) translate([0, 0, -0.1]) RohrTeil(38, 29, 5.2, 230);
      }
    }
    translate([0, 0, -0.1]) cylinder(r=2.3, h=5.2);     // Mittelloch
    translate([0, 0, 2.5]) cylinder(r=16, h=5);         // Platz fuer Bein2-Zentrum
    translate([-5, 0, 2.5]) cube([21+5, 30, 4]);        // Platz fuer Bein2-Bein
    translate([15, 0-2, 2.5]) rotate([0, 0, 45]) cube([4, 2, 3]);
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module Bein2(l=lng) {
  difference() {
    cylinder(r=15, h=5);
    translate([0, 0, -0.1]) cylinder(r=2.2, h=5.2);
  }
  translate([10, 0, 0]) cube([l-10-5, 5, 5]);                           // Bein
  hull() {                                                              // Messpitze
    translate([l-5, 5, -2.5]) linear_extrude(0.25) polygon([[0,0], [4.5,5], [5,5], [3,0]]);
    translate([l-5-1, 0, -2.5]) cube([1, 5, 7.5]);
    translate([l-5-10, 0, 0]) cube([1, 5, 5]);
  }
  rotate([0, 0, 45]) translate([0, 10, 2.75]) //cube([3, 30, 5-2.75]);  // Zeiger
    linear_extrude(5-2.75) polygon([[-5,0], [5+3,0], [3,15], [3,30], [0,30], [0,15]]);
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module Drehgriff() {
  difference() {
    union() {
      cylinder(r=10, h=5);
      for(a=[0:30:359]) {
        rotate([0, 0, a]) translate([10, 0, 0]) cylinder(r=2.5, h=5);
        //rotate([0, 0, a]) translate([10, 0, 2.5]) sphere(r=3);
      }
    }
    //translate([-20, -20, -10]) cube([40, 40, 10]);
    //translate([-20, -20, 5]) cube([40, 40, 10]);
    translate([0, 0, -0.1]) SchraubeM4(); //cylinder(r=2.2, h=5.2);
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module Schieber() {
  difference() {
    union() {
      rotate([0, 0, 90-20/2]) RohrTeil(38-0.5, 29+0.5, 5, 360-20);
      rotate([0, 0, 90-20/2]) translate([0, 0, 4.75]) RohrTeil(40, 27, 4.25, 360-20);
    }
    translate([-33.5, 0, 9.01]) rotate([180, 0, 30]) SchraubeM4(vu=true);
  }
}
//!difference() { Schieber(); translate([-45, -20, -1]) cube([20, 20, 20]); }

if(show==0) {
  translate([0, 0, -2.5]) Bein1();
  rotate([0, 0, ow]) color("red") Bein2();
  rotate([0, 0, 57]) color("blue") translate([-34, 0, -5-2.5]) Drehgriff();
  rotate([0, 0, 57]) translate([0, 0, -2.5]) Schieber();
  txt=str("min=20, cur=", Innendurchmesser(ow+2*wms), ", max=", Innendurchmesser(90+2*wms));
  translate([0, -40, 0]) linear_extrude(1) text(txt, size=6);
} else if(show==1) {
  Bein1();
  translate([lng, -30, 5]) rotate([0, 180, 0]) Bein2();
  translate([35, -45, 5]) rotate([0, 180, 0]) Drehgriff();
  translate([34+10, -40, 9]) rotate([180, 0, 0]) Schieber();
} else if(show==2) {
  Bein1();
} else if(show==3) {
  translate([0, 0, 5]) rotate([180, 0, 0]) Bein2();
} else if(show==4) {
  translate([0, 0, 5]) rotate([0, 180, 0]) Drehgriff();
} else if(show==5) {
  translate([34, 0, 9]) rotate([180, 0, 0]) Schieber();
}

```


[User]
Hey, I'm looking to design a customizable, stackable resistor storage system, and I'd really like to use OpenSCAD for this. I've seen some cool designs out there, but I need something that’s highly parametric so I can tweak it to my exact needs.

Basically, the system is based on drawers that slide into a box. Here's a breakdown of what I'm aiming for:

**Drawer Parameters:**

*   **Compartment Dimensions:** I want to be able to set the width, depth, and height of individual compartments within the drawers.
*   **Compartment Layout:** I need to control how many compartments are next to each other (X count) and how many are behind each other (Y count) within a single drawer.
*   **Drawer Handle:** I'd like to have a choice of handle styles – a standard handle and a thin handle.
*   **Drawer Shrinkage:** It'd be great to have a setting to slightly reduce the drawer size (width and height, not depth) for easier fit.
*   **Label Frames:** Each drawer needs a recessed area on the front for labels, with customizable depth, spacing from the edges, and thickness for the label itself.

**Box Parameters:**

*   **Box Dimensions:** The overall dimensions of the box should depend on the drawers. However, I also want the ability to set a custom size for the whole box, overriding compartment and drawer sizes.
*   **Drawer Arrangement:** I want to control how many rows and columns of drawers are in the box.
*   **Stacking Connectors:** The boxes need interlocking connectors on sides and top/bottom for stacking. These need to be configurable on each side of the box (top, bottom, left, right).
*  **Wall Mounting:** I'd like an option to add mounting holes to the back of the box for wall mounting.

**General Parameters:**

*   **Tolerances:**  I need individual tolerances for horizontal, vertical, and back spaces between the drawers and the box to control how easily the drawers slide in and out.
*   **Printer Settings:** It's important to factor in layer width and layer height of my printer to ensure proper fit.
*   **Smoothing:** I would like rounded corners on the box and drawers.
*   **Grooves:** I need to be able to add grooves to the left and right of the box, and those grooves will interlock with the connector.

**Specific Requests:**

*   The code should be highly parametric so that I can adjust all of the dimensions and layout.
*   I would like the OpenSCAD code to use some built-in functions instead of relying on libraries so that I have a single file.
* I need a function to generate connector shapes on all sides, not just the top and bottom or left and right.
*   I need to be able to specify connectors per side of the box.
*   Include a module that will print info such as drawer sizes, compartment sizes, and box sizes.

Could you create an OpenSCAD file (or files if it makes sense) based on these requirements?

[Assistant]
ResistorBoxV6.scad:
```
//STACKABLE RESISTOR STORAGE BOX [Customizable]
//Created by Bram Vaessen 2018

// preview[view:south, tilt:top diagonal]

// Which one would you like to see?
part = "single"; //[single:Single Box <no stacking>), drawer: Drawer, customDrawer: Custom Drawer, topLeft:Box Top Left, topMiddle:Box Top Middle, topRight:Box Top Right, middleLeft:Box Middle Left, middleMiddle:Box Middle Middle, middleRight:Box Middle right, bottomLeft:Box Bottom Left, bottomMiddle:Box Bottom Middle, bottomRight:Box Bottom Right,  topSC:Box Top <Single Column Stacking>, middleSC:Box Middle <Signle Column Stacking>, bottomSC:Box Bottom <Signle Column Stacking>, leftSR:Box Left <Single Row Stacking>, middleSR:Box Middle <Single Row Stacking>, rightSR:Box Right <Single Row Stacking>, connector:Connector <for stacking>, info:INFO <Show Box/Drawer/Label sizes>]

/* [Basic Setup] */
//The width (mm) of a single compartment in a drawer (ignored with 'Custom Sized Box')
compartWidth = 20;
//The depth (mm) of a single compartment in a drawer (ignored with 'Custom Sized Box')
compartDepth = 62;
//The height (mm) of a single compartment in a drawer (ignored with 'Custom Sized Box')
compartHeight = 15;

//The number of compartments in the drawer next to each other
compartXCount = 3;
//The numberof compartments in the drawer behind each other
compartYCount = 1;

//The number of drawers on top of each other
drawRows = 4;
//The number of columns of drawers next to eachother
drawCols = 1;

//The style of the handle on the drawers
handleStyle = 0; //[0:Normal, 1:Thin Handle]

//Add holes to the back of the box for mounting it on a wall or panel?
mountHoles=0; //[0:No, 1:Yes]

/* [Custom Drawer] */
//The number of compartments in the drawer next to each other (custom drawer)
cdrawXCount=1;
//The number of compartments in the drawer behind each other (custom drawer)
cdrawYCount=1;

/* [Custom Sized Box] */
//Provide a custom size for the box? Overrides/ignores compartment size!
useCustomBox = 0; //[0:No, 1:Yes]
//the width (mm) of the box
customBoxWidth = 73.06;
//the depth (mm) of the box
customBoxDepth = 67.2;
//the height (mm) of the box
customBoxHeight = 74.64;

/* [Tolerances] */
//the amount of extra space (mm) on the side of the drawers, on each side (reprint box after changing, unless using 'custom sized box', then reprint drawer)
drawHorSpace = 0.25;
//the amount of extra space (mm) on top and bottom of drawers, on each side (reprint box after changing, unless using 'custom sized box', then reprint drawer)
drawVertSpace = 0.35;
//the amount of extra space (mm) on the behind the drawer (reprint box after changing, unless using 'custom sized box', then reprint drawer)
drawBackSpace = 0.2;
//the amount  (mm) to shrink the drawer in height and width to fit more easily (reprint drawer after changing). TOLERANT drawers use 0.2mm.
drawShrink = 0.0;

/* [Your Printer Settings] */
//the layer width that you print with (0.48 is normal for 0.4 nozzle)
layerWidth=0.48;
//the layer height that you print with
layerHeight=0.2;

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

/* [Hidden] */
grooveHeight = 5;
grooveDepth = 1* layerWidth;
smoothRadius=1.5;
smoothQuality=16;

boxColumnSpace = layerWidth*2;

drawDividerWidth = 2*layerWidth;
drawOutsideWidth = 4*layerWidth;
drawBottomHeight = 4*layerHeight;

drawFrontExtraWidth=1;
drawFrontExtraHeight = 2*layerHeight;

drawFrontExtra = max(0, smoothRadius - drawOutsideWidth);

boxOutsideWidth = 5*layerWidth;
boxDividerWidth = 2*layerWidth;
boxBackDepth = (mountHoles==1)?CorrectedHeight(3):CorrectedHeight(1);

//if custom box size, reverse calculations
customBoxColumnWidth = (customBoxWidth +boxOutsideWidth*(drawCols-1) -  boxOutsideWidth + boxDividerWidth)/drawCols;
customBoxDrawSpaceX = customBoxColumnWidth - drawFrontExtraWidth*2 - boxOutsideWidth - boxColumnSpace;

customBoxDrawSpaceY = customBoxDepth - boxBackDepth - drawFrontExtra;
customBoxDrawSpaceZ = (customBoxHeight - boxOutsideWidth*2)/drawRows - boxDividerWidth;

customDrawWidth = customBoxDrawSpaceX - drawHorSpace*2;
customDrawDepth = customBoxDrawSpaceY - drawBackSpace;
customDrawHeight = customBoxDrawSpaceZ - drawVertSpace*2;

customCompartWidth = (customDrawWidth - drawOutsideWidth*2 - drawDividerWidth*(compartXCount-1)) / compartXCount;
customCompartDepth = (customDrawDepth - drawOutsideWidth*2 -drawDividerWidth*(compartYCount-1))/compartYCount;
customCompartHeight = customDrawHeight-drawBottomHeight;

compartWidthF = useCustomBox ? customCompartWidth : compartWidth;
compartDepthF = useCustomBox ? customCompartDepth : compartDepth;
compartHeightF = useCustomBox ? customCompartHeight : compartHeight;

drawWidth = drawOutsideWidth*2 + drawDividerWidth*(compartXCount-1) + compartWidthF*compartXCount;
drawDepth = drawOutsideWidth*2 + drawDividerWidth*(compartYCount-1) + compartDepthF*compartYCount;

//check differences between desired drawer height and corrected drawer height and add to spacer
drawHeight = CorrectedHeightDown(drawBottomHeight + compartHeightF);
drawHeightU = drawBottomHeight + compartHeightF;
drawHeightD = (drawHeightU - drawHeight) / 2;
drawVertSpaceC = drawVertSpace+drawHeightD;

boxDrawSpaceX = drawWidth + drawHorSpace*2;
boxDrawSpaceY = drawDepth + drawBackSpace;
boxDrawSpaceZ = drawHeight + drawVertSpaceC*2;

boxColumnWidth = boxDrawSpaceX + drawFrontExtraWidth*2 + boxOutsideWidth + boxColumnSpace;

boxWidth = boxColumnWidth * drawCols - boxOutsideWidth*(drawCols-1) + boxOutsideWidth - boxDividerWidth;
boxDepth = CorrectedHeight(boxBackDepth + boxDrawSpaceY + drawFrontExtra);
boxHeight = drawRows*(boxDrawSpaceZ + boxDividerWidth)+ boxOutsideWidth*2;

////

drawFrontWidth = drawWidth + drawFrontExtraWidth*2;
drawFrontHeight = drawHeight + drawFrontExtraHeight;

handleWidth = lerp(max(15,drawFrontWidth/3.5), drawFrontWidth/12, handleStyle);
handleHeight1 = min(drawFrontHeight - smoothRadius*2,handleWidth) ;
handleHeight2 = drawFrontHeight - smoothRadius*2;
handleHeight = handleStyle ? handleHeight2 : handleHeight1;

handleDepth = handleHeight*0.5;
handleThickness = layerWidth*4;

labelFrameWidth = (drawFrontWidth - handleWidth - smoothRadius*4)/2;
labelFrameHeight = drawFrontHeight - smoothRadius*2;
labelFrameDist = -drawFrontWidth / 2 + smoothRadius + labelFrameWidth/2;
labelFrameThickness = 2*layerWidth;
labelFrameEdgeSize = 2.5;
labelFrameSpace = 0.8;
labelFrameDepth = labelFrameThickness+labelFrameSpace;

connectorWidth = 4;
connectorMid = 3* layerHeight;
connectorSide = 5*layerHeight;
connectorLength = boxDepth/3*2;
connectorSpacer = 0.35;
connectorDist = 0.5/5*3.25;

stopperDepth = CorrectedHeight(1.2);
stopperSteps = stopperDepth / layerHeight;
stopperHeight = layerWidth*stopperSteps*0.8;
stopperWidth = stopperHeight*1.5;
stopperStart = 2;

mountholeSize1=4;
mountholeSize2=8;
mountholeDepth1 = 2.5;
mountholeDepth2 = 5;

print_part();

module print_part()
{
	if (part == "single")
        rotate([-90,0,0]) Box();
    if (part == "drawer")
        Drawer();
    if (part == "customDrawer")
        CustomDrawer();
    if (part == "topLeft")
        rotate([-90,0,0]) Box(boxRight="connector", boxBottom="connector");
    if (part == "topMiddle")
        rotate([-90,0,0])
            Box(boxLeft="connector", boxRight="connector", boxBottom="connector");
    if (part == "topRight")
        rotate([-90,0,0]) Box(boxLeft="connector", boxBottom="connector");
    if (part == "middleLeft")
        rotate([-90,0,0])
            Box(boxRight="connector", boxBottom="connector", boxTop="connector");
    if (part == "middleMiddle")
        rotate([-90,0,0])
            Box(boxLeft="connector", boxRight="connector",
                boxBottom="connector", boxTop="connector");
    if (part == "middleRight")
        rotate([-90,0,0])
            Box(boxLeft="connector", boxBottom="connector", boxTop="connector");
    if (part == "bottomLeft")
        rotate([-90,0,0]) Box(boxRight="connector", boxTop="connector");
    if (part == "bottomMiddle")
        rotate([-90,0,0])
            Box(boxLeft="connector", boxRight="connector", boxTop="connector");
    if (part == "bottomRight")
        rotate([-90,0,0])
            Box(boxLeft="connector", boxTop="connector");
    if (part == "topSC")
        rotate([-90,0,0]) Box(boxBottom="connector");
    if (part == "middleSC")
        rotate([-90,0,0]) Box(boxBottom="connector", boxTop="connector");
    if (part == "bottomSC")
        rotate([-90,0,0]) Box(boxTop="connector");
    if (part == "leftSR")
        rotate([-90,0,0]) Box(boxRight="connector");
    if (part == "middleSR")
        rotate([-90,0,0]) Box(boxLeft="connector", boxRight="connector");
    if (part == "rightSR")
        rotate([-90,0,0]) Box(boxLeft="connector");
    if (part == "connector")
        rotate([-90,0,0]) Connector();
    if (part == "info")
        Info();
}

module Info()
{

    linear_extrude(height=0.2) union()
    {
        text(str("Box Size (mm): ",boxWidth, ", ",boxDepth,", ", boxHeight));
        translate([0,-20,0])
            text(str("Drawer Size (mm): ", drawWidth,", ", drawDepth,", ",drawHeight));
        translate([0,-40,0])
            text(str("Label Size (mm): ", labelFrameWidth-labelFrameThickness*2-1,", ", labelFrameHeight-labelFrameThickness-1));
        translate([0,-60,0])
            text(str("Compartment Size (mm): ", compartWidthF,", ", compartDepthF,", ",compartHeightF));

    }
}

module Connector()
{
    linear_extrude(height=connectorLength-5)
        polygon(points=[
            [-connectorWidth/2+connectorSpacer, -connectorMid],
            [-connectorWidth/2+connectorSpacer, connectorMid],
            [-connectorWidth/2 -connectorSide +connectorSpacer, connectorMid+connectorSide],
            [connectorWidth/2 + connectorSide -connectorSpacer, connectorMid+connectorSide],
            [connectorWidth/2 - connectorSpacer,  connectorMid],
            [connectorWidth/2 - connectorSpacer, -connectorMid],
            [connectorWidth/2 + connectorSide - connectorSpacer, -connectorMid-connectorSide],
            [-connectorWidth/2 - connectorSide + connectorSpacer, -connectorMid-connectorSide],
            ]);

        translate([0,0,connectorLength-5])
        linear_extrude(height=5, scale=[0.75, 1])
        polygon(points=[
            [-connectorWidth/2+connectorSpacer, -connectorMid],
            [-connectorWidth/2+connectorSpacer, connectorMid],
            [-connectorWidth/2 -connectorSide +connectorSpacer, connectorMid+connectorSide],
            [connectorWidth/2 + connectorSide -connectorSpacer, connectorMid+connectorSide],
            [connectorWidth/2 - connectorSpacer,  connectorMid],
            [connectorWidth/2 - connectorSpacer, -connectorMid],
            [connectorWidth/2 + connectorSide - connectorSpacer, -connectorMid-connectorSide],
            [-connectorWidth/2 - connectorSide + connectorSpacer, -connectorMid-connectorSide],
            ]);

        for (z=[6,connectorLength-6])

        translate([0,0,z])
            linear_extrude(height=1)
                polygon(points=[
                    [-connectorWidth/2+connectorSpacer, -connectorMid],
                    [-connectorWidth/2+connectorSpacer, connectorMid],
                    [-connectorWidth/2 -connectorSide+connectorSpacer/2, connectorMid+connectorSide],
                    [connectorWidth/2 + connectorSide-connectorSpacer/2 , connectorMid+connectorSide],
                    [connectorWidth/2 - connectorSpacer,  connectorMid],
                    [connectorWidth/2 - connectorSpacer, -connectorMid],
                    [connectorWidth/2 + connectorSide -connectorSpacer/2, -connectorMid-connectorSide],
                    [-connectorWidth/2 - connectorSide +connectorSpacer/2, -connectorMid-connectorSide],
                    ]);
}

module MountHole()
{
    translate([0,-0.01,0])
    rotate([-90,0,0])
    {
        cylinder(d=mountholeSize2, d2=mountholeSize1, h = mountholeDepth1, $fn=32);
        translate([0,0,mountholeDepth1])
            cylinder(d=mountholeSize1, h=mountholeDepth2, $fn=32);
    }
}

function CorrectedHeight(height) = ceil(height/layerHeight)*layerHeight;
function CorrectedHeightDown(height) = floor(height/layerHeight)*layerHeight;

module ConnectorCutout()
{

    len = connectorLength + 2;
    linear_extrude(height=len)
        polygon(points=[
            [-connectorWidth/2, -connectorMid],
            [-connectorWidth/2, connectorMid],
            [-connectorWidth/2 -connectorSide, connectorMid+connectorSide],
            [connectorWidth/2 + connectorSide, connectorMid+connectorSide],
            [connectorWidth/2,  connectorMid],
            [connectorWidth/2, -connectorMid],
            [connectorWidth/2 + connectorSide, -connectorMid-connectorSide],
            [-connectorWidth/2 - connectorSide, -connectorMid-connectorSide],
            ]);

        scale([2,1.25,1])
        linear_extrude(height=5, scale=[.5, 1/1.25])
        polygon(points=[
            [-connectorWidth/2, -connectorMid],
            [-connectorWidth/2, connectorMid],
            [-connectorWidth/2 -connectorSide, connectorMid+connectorSide],
            [connectorWidth/2 + connectorSide, connectorMid+connectorSide],
            [connectorWidth/2,  connectorMid],
            [connectorWidth/2, -connectorMid],
            [connectorWidth/2 + connectorSide, -connectorMid-connectorSide],
            [-connectorWidth/2 - connectorSide, -connectorMid-connectorSide],
            ]);

}
//!Grooves(100,100);

module Grooves(width,height)
{
    areaHeight = height*0.85;
    areaWidth = width*0.95;
    holeHeight = .75* grooveHeight;
    h = grooveHeight + holeHeight;
    count = ceil((areaHeight+holeHeight)/h);
    space = (areaHeight - count*grooveHeight) / (count-1);
    startZ = -areaHeight/2 + grooveHeight/2;
    stepZ = space+grooveHeight;

    for (z=[0:count-1]) translate([0,0,startZ + z*stepZ])
    {
        hull()
        {
            for (x=[-1,1]) translate([x*(areaWidth/2 - grooveHeight),0,0])
                rotate([90,0,0])
                    cylinder(d=grooveHeight, h=grooveDepth, center=true, $fn=32);

            for (x=[-1,1]) translate([x*(areaWidth/2 - grooveHeight),0,0])
                rotate([90,0,0])
                    cylinder(d=grooveHeight-grooveDepth*2, h=grooveDepth*2, center=true, $fn=32);
        }

    }
}

module Box(boxLeft="grooves", boxRight="grooves", boxTop="solid", boxBottom="solid")
{
    startX = -boxWidth/2 + boxOutsideWidth + drawFrontExtraWidth+ boxDrawSpaceX/2;
    stepX = boxColumnWidth - boxOutsideWidth;
    startZ = boxOutsideWidth;
    stepZ = boxDrawSpaceZ + boxDividerWidth;
    y= -boxBackDepth - boxDrawSpaceY/2;
    frontPanelWidth = boxDrawSpaceX+drawFrontExtraWidth*2;
    fontPanelHeight = drawRows*(boxDrawSpaceZ+boxDividerWidth);
    stopperY = CorrectedHeight(-boxDepth + smoothRadius + stopperStart);
    frontPanelInset = smoothRadius + drawFrontExtra + labelFrameDepth;
    stopperDist = drawWidth /2 -drawOutsideWidth - stopperWidth;
    insideCutWidth = (stopperDist -stopperWidth) * 2;
    insideCutHeight = stepZ * drawRows - smoothRadius*2;
    difference()
    {
        //main body of the box
        translate([0,0,boxHeight/2]) rotate([90,0,0])
            SmoothCube2([boxWidth, boxHeight, boxDepth]);

        //for each column
        for (x=[0:drawCols-1])
        {
            //cut out the drawer parts
            for (z=[0:drawRows-1])
                translate([startX+x*stepX,y-1,startZ + z*stepZ])
                    SmoothCube([boxDrawSpaceX, boxDrawSpaceY+2, boxDrawSpaceZ]);
            //cut out front part
            translate([startX+x*stepX,,-boxDepth-1,boxOutsideWidth+fontPanelHeight/2])
                SmoothPanel([frontPanelWidth, CorrectedHeight(frontPanelInset)+1, fontPanelHeight]);

            //cut out inside
            translate([startX+x*stepX, -boxBackDepth-smoothRadius,boxOutsideWidth])
                YMinCube([insideCutWidth, boxDepth, insideCutHeight]);

            //cutout mounting holes
            if (mountHoles)
            {
                for (x2=[-1,1])
                    translate([startX+x*stepX + x2*(boxDrawSpaceX/2-mountholeSize2*1.5),
                           -boxBackDepth,
                           startZ + (drawRows-0.5)*stepZ])
                        MountHole();

                translate([startX+x*stepX,
                           -boxBackDepth,
                           startZ + 0.5*stepZ])
                        MountHole();
            }

        }

        //add stuff like connectors
        if (boxLeft=="connector")
            for (z=[-1,1]) translate([-boxWidth/2, 0.0001, boxHeight/2 + z*boxHeight*connectorDist])
                rotate([90,90,0]) ConnectorCutout();
        else if (boxLeft=="grooves")
            translate([-boxWidth/2, -boxDepth/2, boxHeight/2])
                rotate([0,0,90]) Grooves(boxDepth-smoothRadius/2, boxHeight-smoothRadius/2);

        if (boxRight=="connector")
            for (z=[-1,1]) translate([boxWidth/2, 0.0001, boxHeight/2 + z*boxHeight*connectorDist])
                rotate([90,90,0]) ConnectorCutout();
        else if (boxRight=="grooves")
            translate([boxWidth/2, -boxDepth/2, boxHeight/2])
                rotate([0,0,90]) Grooves(boxDepth-smoothRadius/2, boxHeight-smoothRadius/2);

        if (boxBottom=="connector")
            for (x=[-1,1]) translate([x*boxWidth*connectorDist, 0.0001, 0])
                rotate([90,0,0]) ConnectorCutout();

        if (boxTop=="connector")
            for (x=[-1,1]) translate([x*boxWidth*connectorDist, 0.0001, boxHeight])
                rotate([90,0,0]) ConnectorCutout();

    }

    //add stoppers
    for (x=[0:drawCols-1]) for (z=[0:drawRows-1]) for (x2=[-1,1])
        translate([startX+x*stepX + x2*stopperDist,
                   stopperY,
                   startZ + z*stepZ + boxDrawSpaceZ])
            Stopper();

    //*for (x=[0:drawCols-1]) for (z=[0:drawRows-1])
    //    translate([startX+x*stepX,y - drawBackSpace/2 ,startZ + drawVertSpace + z*stepZ])
    //        Drawer();

}

module CustomDrawer()
{
    y = -drawDepth/2 + drawFrontExtra;
    z = drawHeight/2;

    indentWidth = drawWidth - drawOutsideWidth*2;
    indentDepth = drawDepth - drawOutsideWidth*2;

    ccompWidth = (indentWidth - (cdrawXCount-1)*drawDividerWidth) / cdrawXCount;
    ccompDepth = (indentDepth - (cdrawYCount-1)*drawDividerWidth) / cdrawYCount;

    startX = -drawWidth/2 + drawOutsideWidth + ccompWidth/2;
    startY = -drawDepth/2 + drawOutsideWidth + ccompDepth/2;
    stepX = ccompWidth + drawDividerWidth;
    stepY = ccompDepth + drawDividerWidth;

    difference()
    {
        DrawerBase();
        //cut out compartments
        for (x=[0:cdrawXCount-1]) for (y=[0:cdrawYCount-1])
            translate([startX + x*stepX, startY + y*stepY, drawBottomHeight])
                SmoothCube([ccompWidth, ccompDepth, compartHeightF+1]);
        //top indent for dividers
        translate([0,0,drawHeight-stopperHeight])
            SmoothCube([indentWidth, indentDepth, smoothRadius+1]);
    }

    if (handleStyle==0)
        translate([0,y,z]) Handle();
    if (handleStyle==1)
        translate([0,y,0]) HandleThin();
    for (x=[-1,1])
        translate([x*labelFrameDist,y,smoothRadius]) LabelFrame();

}

module Drawer()
{
    y = -drawDepth/2 + drawFrontExtra;
    z = drawHeight/2;

    echo(drawHeight);

    startX = -drawWidth/2 + drawOutsideWidth + compartWidthF/2;
    startY = -drawDepth/2 + drawOutsideWidth + compartDepthF/2;
    stepX = compartWidthF + drawDividerWidth;
    stepY = compartDepthF + drawDividerWidth;
    indentWidth = drawWidth - drawOutsideWidth*2;
    indentDepth = drawDepth - drawOutsideWidth*2;

    difference()
    {
        DrawerBase();
        //cut out compartments
        for (x=[0:compartXCount-1]) for (y=[0:compartYCount-1])
            translate([startX + x*stepX, startY + y*stepY, drawBottomHeight])
                SmoothCube([compartWidthF, compartDepthF, compartHeightF+2]);
        //top indent for dividers
        translate([0,0,drawHeight-stopperHeight])
            SmoothCube([indentWidth, indentDepth, smoothRadius+1]);
    }

    if (handleStyle==0)
        translate([0,y,z]) Handle();
    if (handleStyle==1)
        translate([0,y,0]) HandleThin();
    for (x=[-1,1])
        translate([x*labelFrameDist,y,smoothRadius]) LabelFrame();

}

module DrawerBase()
{
    translate([0,-drawFrontExtra/2,0])
                SmoothCube([drawWidth-drawShrink, drawDepth+drawFrontExtra, drawHeight-drawShrink]);
                //the front panel
            translate([0, -drawDepth/2 -drawFrontExtra , drawHeight/2+drawFrontExtraHeight/2])
                SmoothPanel([drawFrontWidth, smoothRadius, drawFrontHeight]);
}

module Handle()
{
    for (x=[0,1]) mirror([x,0,0])
        translate([handleWidth/2-handleThickness,0]) rotate([90,0,90])
            linear_extrude(height=handleThickness)
            polygon(points=[[.01,-handleHeight/2-.01], [-handleDepth,0],[.01,handleHeight/2+.01]]);

    a = atan(handleDepth / (handleHeight/2));
    a2 = 90-a;

    d = handleThickness / sin(a);
    d2 = handleThickness / sin(a2);

    rotate([90,0,90])
        linear_extrude(height=handleWidth-0.01, center=true)
            polygon(points=[[-handleDepth,0],[.01,handleHeight/2+.01],[.01,handleHeight/2-d],
                         [-handleDepth+layerWidth*2,0]]);
}

//!HandleThin(5);

module HandleThin()
{
    startDiam = handleWidth;
    endDiam = 0.85 * handleWidth;
    middleDiam = 0.4 * handleWidth;
    length = 2 * handleWidth;
    height = handleHeight - endDiam/3;

    difference()
    {
        cylinder(d=startDiam, h=height, $fn=32);
        translate([0,handleWidth+.01,0])
            cube([handleWidth*2, handleWidth*2, height*2.1], center=true);
    }
    difference()
    {
        union()
        {
            translate([0,-length,0]) cylinder(d=endDiam, h=height, $fn=32);
            translate([0,-length/2,0]) CenterCube([middleDiam, length,height]);
        }
        translate([0,-length-endDiam/2-handleWidth*.5,0]) rotate([45,0,0])
            cube([height,height,height*1.5], center=true);
    }

    translate([0,-length,height]) sphere(d=endDiam, $fn=32);

}

module LabelFrame()
{
    depth = labelFrameThickness + labelFrameSpace;

    difference()
    {
        translate([0,-depth/2+.01,0])
            CenterCube([labelFrameWidth, depth+.02, labelFrameHeight]);
        translate([0,0,labelFrameEdgeSize])
            CenterCube([labelFrameWidth-2*labelFrameEdgeSize, depth*3, labelFrameHeight]);
        translate([0,0,labelFrameThickness])
            CenterCube([labelFrameWidth-labelFrameThickness*2, labelFrameSpace*2, labelFrameHeight]);

    }

    for (x=[0,1]) mirror([x,0,0])
    translate([0,-labelFrameSpace,0]) rotate([90,0,0]) linear_extrude(height=labelFrameThickness)
        polygon(points=[[-labelFrameWidth/2+labelFrameEdgeSize-0.01, labelFrameEdgeSize*2],
                        [-labelFrameWidth/2+labelFrameEdgeSize*2, labelFrameEdgeSize],
                        [-labelFrameWidth/2+labelFrameEdgeSize-0.01, labelFrameEdgeSize]]);

    ratio = layerHeight/ layerWidth;

    rotate([90,0,-90]) linear_extrude(height =labelFrameWidth, center=true)
        polygon(points=[[0,0],[depth,0], [0,-depth*ratio*1.5]]);
}

module Stopper()
{
    for (i=[1:stopperSteps])
        translate([0,stopperDepth-(i-1)*layerHeight, 0]) rotate([90,0,0])
            linear_extrude(height=layerHeight)
                polygon(points=[[-stopperWidth*(i/stopperSteps),0],
                                [-stopperWidth/2*(i/stopperSteps),-stopperHeight*(i/stopperSteps)],
                                [stopperWidth/2*(i/stopperSteps),-stopperHeight*(i/stopperSteps)],
                                [stopperWidth*(i/stopperSteps),0]]);

}

module CenterCube(size)
{
    translate([0,0,size[2]/2])
        cube(size=size,center=true);
}

module YMinCube(size)
{
    translate([0,-size[1]/2,0]) CenterCube(size);
}

module SmoothCube(size)
{
    sr2 = smoothRadius*2;
    hull()
    {

        translate([0,0,size[2]/2])
        {
            cube([size[0], size[1]-sr2, size[2]-sr2], center=true);
            cube([size[0]-sr2, size[1], size[2]-sr2], center=true);
            cube([size[0]-sr2, size[1]-sr2, size[2]], center=true);
        }

        for (x=[-1,1]) for (y=[-1,1])
        {
            translate([x*(size[0]/2-smoothRadius), y*(size[1]/2-smoothRadius), smoothRadius])
                sphere(r=smoothRadius, $fn=smoothQuality);
        }
        for (x=[-1,1]) for (y=[-1,1])
        {
            translate([x*(size[0]/2-smoothRadius), y*(size[1]/2-smoothRadius), size[2]-0.01])
                cylinder(r=smoothRadius, h=0.01, $fn=smoothQuality);
        }

    }
}

module SmoothCube2(size)
{
    sr2 = smoothRadius*2;
    hull()
    {

        for (x=[-1,1]) for (y=[-1,1])
        {
            translate([x*(size[0]/2-smoothRadius), y*(size[1]/2-smoothRadius), 0])
                cylinder(r=smoothRadius, h=size[2], $fn=smoothQuality);
        }

    }
}

module SmoothPanel(size)
{
    hull()
    {
        for (x=[-1,1]) for (z=[-1,1])
            translate([x*(size[0]/2-smoothRadius),0,  z*(size[2]/2-smoothRadius)])
                rotate([-90,0,0])
                    cylinder(r=smoothRadius, h=size[1], $fn=smoothQuality);
    }
}

function lerp(v1, v2, a) = (1-a)*v1 + a*v2;

```


[User]
I'm looking to design a custom phone holder for my car, a 2015+ Subaru WRX. I want it to fit snugly into the console bin below the radio and position the phone in front of the gear shifter. I'm thinking it should be a modular design using OpenSCAD so I can easily customize it for different phones.

Here's what I'm envisioning:

**Base:**
*   The base needs to have a somewhat complex shape to fit the console bin.
    *   The side furthest from the driver should be about 158mm wide.
    *   The side facing the driver should be about 140.3mm wide, but with a slight outward bow of 1.8mm at the center.
    *   The depth of the base, from the furthest side to the driver-facing side, should be 106.2mm.
    *   It should have rounded corners with a 5mm radius.
    *   The base should be 10mm thick.
    *   There should also be a peg, 8mm x 8mm, to fit into an existing hole in the console. The peg should be at the far rear of the base and the peg should have a height of approximately 57mm
    *   The base should also include a curve, or 'arch', on the front edge, bowing out at approximately 1.8mm.

**Riser:**
*   A riser needs to attach to the base to lift the phone up.
    *   It should be 5mm thick.
    *   The bottom width (where it meets the base) should span the width of the base, where the rounded corners are.
    *   The top of the riser should be wider - about 150mm.
    * The bottom of the riser should have a length of 45 mm from the point where it attaches to the base to its end. The angle at which the riser is angled towards the driver should be 63.8 degrees, though this can be adjusted within the program.
    *   The height of the lowest top corner of the riser should be about 40.15mm above the base.

**Phone Shelf/Cradle:**
*   At the top of the riser, there should be a shelf to hold the phone.
    *   It should be 15mm deep and 5mm high.
    *   The width should be about 150mm.
    *  It should be filleted to the riser so there aren't any sharp transitions

**General Notes:**
*   All dimensions should be easily adjustable using variables in the OpenSCAD file.
*   I'd like the phone holder to use a cradle design with a small back and sides for security.
*   I want to set parameters for the phone's dimensions (width, height, and thickness) so that I can adjust the cradle to fit different phone models. There also needs to be an additional 'side' thickness parameter to account for phone cases.
*   I'd like to have an adjustable angle parameter for the phone cradle in case I decide I want the phone at an angle.
*   The cradle design should have a rail at the bottom.
*   The fillet from the cradle to the riser should be smooth.

I would like to be able to use the following variables for ease of customization:

*   `FRONT_WIDTH`
*   `BACK_WIDTH`
*   `BASE_DEPTH`
*   `CORNER_RADIUS`
*   `BASE_HEIGHT`
*   `BASE_BOW`
*   `PEG_WIDTH`
*   `PEG_HEIGHT`
*   `RISER_THICKNESS`
*   `RISER_TOP_WIDTH`
*   `RISER_ANGLE`
*  `RISER_LENGTH`
*   `PHONE_WIDTH`
*   `PHONE_HEIGHT`
*   `PHONE_THICKNESS`
*   `PHONE_HOLDER_SIDE`
*   `PHONE_HOLDER_BACK`
*   `PHONE_HOLDER_RAIL`
*  `PHONE_HOLDER_ANGLE`
* `EXTENSION_LENGTH`
* `EXTENSION_ANGLE`

Can you provide me with the OpenSCAD code to generate this phone holder with all of the above parameters? The code must not rely on any libraries except for OpenSCAD's native functionality.

[Assistant]
WRX-Phone-Holder-Remix.scad:
```
/* Rounded corners - 5mm radius
 * side furthest from driver - 158mm
 * side facing driver - 140.3mm wide (1.8mm outward bow)
 * offset 106.2mm from furthest side
 * left and right sides, complete 'square'
 * retaining peg 8mmx8mm
 * thickness of base - 10mm
 * offset to hole in base - 10mm
 *
 * riser - 5mm thick
 * 133.4mm wide at bottom (goes from roundover to roundover)
 * 150mm wide at top.
 * lowest top corner is 40.15 mm above base
 *
 * shelf - 15mm deep, 5mm high, 150mm wide, fillet to riser
 */

FRONT_WIDTH = 140.3;
BACK_WIDTH = 158.0;
BASE_DEPTH = 106.2;
CORNER_RADIUS = 5.0;
BASE_HEIGHT = 10.0;
BASE_BOW = 1.8;
PEG_WIDTH = 8.0;
PEG_HEIGHT = 57.0;

RISER_THICKNESS = 5.0;
RISER_TOP_WIDTH = 150.0;
RISER_ANGLE = 0;
RISER_LENGTH = 45.0;

PHONE_WIDTH = 155.0;
PHONE_HEIGHT = 75.0;
PHONE_THICKNESS = 12.0;
PHONE_HOLDER_SIDE = 2.0;
PHONE_HOLDER_BACK = 5.0;
PHONE_HOLDER_RAIL = 20.0;
PHONE_HOLDER_ANGLE = 40.0;

EXTENSION_LENGTH = 15.0;
EXTENSION_ANGLE = 63.8;

$fn=500;

/* Helper Functions */
function circle_by_three_points(A, B, C) =
let (
  yD_b = C.y - B.y,  xD_b = C.x - B.x,  yD_a = B.y - A.y,
  xD_a = B.x - A.x,  aS = yD_a / xD_a,  bS = yD_b / xD_b,
  cex = (aS * bS * (A.y - C.y) +
  bS * (A.x + B.x) -    aS * (B.x + C.x)) / (2 * (bS - aS)),
  cey = -1 * (cex - (A.x + B.x) / 2) / aS + (A.y + B.y) / 2
)
[cex, cey];

function len3(v) = sqrt(pow(v.x, 2) + pow(v.y, 2));

/* Create the geometry */

/* points in base */
/* 4 corners */
b1 = [(BACK_WIDTH-FRONT_WIDTH)/2,0];
b2 = [(FRONT_WIDTH-((BACK_WIDTH-FRONT_WIDTH)/2)),0];
b3 = [BACK_WIDTH,BASE_DEPTH];
b4 = [0,BASE_DEPTH];
/* furthest point of bowed out edge */
b5 = [((b1.x + b2.x)/2), 0-BASE_BOW];

/* points on the riser */
r1 = [b1.x + CORNER_RADIUS, b1.y];
r2 = [b2.x - CORNER_RADIUS, b2.y];
r_bottom_width = r2.x-r1.x;
echo(r_bottom_width);
r3 = [r2.x + ((RISER_TOP_WIDTH-r_bottom_width)/2.0), r2.y - RISER_LENGTH];
r4 = [r1.x - ((RISER_TOP_WIDTH-r_bottom_width)/2.0), r1.y - RISER_LENGTH];
echo("RISER_TOP_WIDTH = ", RISER_TOP_WIDTH);
echo("Actual top width = ", r3.x-r4.x);
echo(r1, r2, r3, r4);

/* build up the chunks of the thing */
module base(){
  linear_extrude(height=BASE_HEIGHT){
    difference(){
      baseOutline();
      offset(r=CORNER_RADIUS)
        offset(delta=-CORNER_RADIUS)
          offset(delta=-BASE_HEIGHT)
            baseOutline();
    }
  }
}

module baseOutline(){
  /* Create rounded corners with offset - then offset + */
  offset(r=CORNER_RADIUS)
    offset(delta=-CORNER_RADIUS)
      /* Outline of the base */
      union(){
        polygon([b1,b2,b3,b4]);
        baseArch();
      }
}

module baseArch(){
  center = circle_by_three_points(b1, b5, b2);
  radius = len3(b5 - center);
  square_vector = [(3*radius),(center.y-b1.y)*2+2];
  echo("Center = ", center);
  echo("Radius = ", radius);
  echo("square_vector = ", square_vector);
  difference(){
    translate(center) circle(radius);
    translate(center+[0,1]) square(square_vector,center=true);
  }
}

module supportSpike(){
  linear_extrude(height=PEG_HEIGHT)
    intersection(){
      translate(b3-[PEG_WIDTH,PEG_WIDTH]) square(PEG_WIDTH);
      baseOutline();
    }
}

module riser(){
  rotate([-EXTENSION_ANGLE, 0, 0])
    union(){
      linear_extrude(height=RISER_THICKNESS)
        polygon([r1,r2,r3,r4]);
      translate([0,-RISER_LENGTH, RISER_THICKNESS])
        extension();
  }

}

module extension(){
  union(){
      rotate([EXTENSION_ANGLE, 0, 0])
        union(){
          translate([r4.x, -EXTENSION_LENGTH, -RISER_THICKNESS])
            cube([RISER_TOP_WIDTH, EXTENSION_LENGTH, RISER_THICKNESS]);
            translate([(r3.x + r4.x) / 2 - (2 * PHONE_HOLDER_SIDE + PHONE_WIDTH) / 2,
                      - EXTENSION_LENGTH,
                      -RISER_THICKNESS])
              cradle();
    }
  }
}

module cradle(){
  rotate([-PHONE_HOLDER_ANGLE, 0, 0]){
    translate([0, -(PHONE_THICKNESS+PHONE_HOLDER_SIDE+PHONE_HOLDER_BACK), 0]){
      difference(){
          cube([PHONE_WIDTH+2*PHONE_HOLDER_SIDE,
                PHONE_THICKNESS+PHONE_HOLDER_SIDE+PHONE_HOLDER_BACK,
                PHONE_HOLDER_SIDE + PHONE_HEIGHT]);
          union(){
            translate([PHONE_HOLDER_SIDE,
                       PHONE_THICKNESS / 2 + PHONE_HOLDER_SIDE,
                       PHONE_THICKNESS / 2 + PHONE_HOLDER_SIDE])
              rotate([0,90,0])
                cylinder(d=PHONE_THICKNESS,h=PHONE_WIDTH);
            translate([PHONE_HOLDER_SIDE,
                       -PHONE_HOLDER_BACK,
                       PHONE_HOLDER_SIDE + PHONE_THICKNESS / 2])
              cube([PHONE_WIDTH,
                     PHONE_THICKNESS+PHONE_HOLDER_SIDE+PHONE_HOLDER_BACK,
                     2 * PHONE_HEIGHT]);
            translate([-PHONE_HOLDER_SIDE,
                       -PHONE_HOLDER_BACK,
                       PHONE_HOLDER_SIDE+PHONE_THICKNESS / 2 + PHONE_HOLDER_RAIL])
              cube([PHONE_WIDTH * 2,
                    PHONE_THICKNESS + PHONE_HOLDER_SIDE + PHONE_HOLDER_BACK,
                    2 * PHONE_HEIGHT]);
            translate([PHONE_HOLDER_SIDE + PHONE_HOLDER_RAIL,
                       0,
                       PHONE_HOLDER_SIDE + PHONE_THICKNESS / 2])
              cube([PHONE_WIDTH - 2 * PHONE_HOLDER_RAIL,
                    PHONE_THICKNESS + 2 * PHONE_HOLDER_BACK + PHONE_HOLDER_SIDE,
                    PHONE_HEIGHT]);
        }
      }
    }
  }
}

/* Build up the thing from modules */
union(){
  base();
  supportSpike();
  riser();
}

```


[User]
Hey, I'm looking to get a parametric design for a filament spinner, something like a lazy susan for a spool of 3D printer filament. I've got two SCAD files as reference (Filament_Spinner.scad and Filament_Spinner_v2_BEST_SETTINGS.scad) that I've been using, and they're mostly good, but I'd like something more flexible and better organized. I'm hoping we can create a new set of SCAD files that are more modular.

Essentially, it's a two-part design, a base and a top piece, that fit together with ball bearings in between. The base supports the filament spool and the top part sits on the bearings. There's also an optional support stalk to keep the lid of a container from pressing down on the spool.

Here's the breakdown of parameters that I think we need:

*   **General:**
    *   `part_to_generate`: (1=top, 2=bottom, 3=both)
*   **Spool/Hub Related:**
    *   `spool_hole_diameter`:  The inside diameter of the filament spool hole.
    *   `spool_hole_diameter2`: A second diameter for a raised ring on the spinner.
    *   `spool_hole_diameter3`: A third diameter for a raised ring on the spinner.
    *   `hub_nub_slop`: Tolerance between the spinner's hub and the spool hole.
    *   `support_flange_extension`: How far the base's rim extends beyond the spool hole.
*   **Bearing Related:**
    *   `bearing_diameter`: Diameter of the ball bearings to be used.
    *   `bearing_channel_width_slop`: Extra space in the bearing channel beyond the bearing diameter.
    *   `bearing_channel_extra_depth`: How deep the bearing channel should be.
    *   `bearing_tongue_slop`: How much narrower the part that rides on the bearings should be compared to the bearing diameter.
    *   `bearing_loading_well_depth`: How deep the bearing loading well should be.
*   **Slicing/Manufacturing:**
    *   `wall_thickness`: Wall thickness to use.
    *   `part_clearance`: Clearance between the two parts.
*   **Container/Stalk:**
    *   `container_height`: Inside height of the container, used for the stalk.
    *   `stalk_diameter`: Diameter of the support stalk.
    *    `stalk_slop`: Clearance between the stalk and its hole in the spinner.

I'd really like to have the `spinner()` and `base()` modules in separate files. It would also be great if the files used the `BOSL2` library for any helper functions, like centering or using Minkowski sums. I'm hoping this makes it more easily extensible.

The generated parts should be oriented such that when the base piece is sitting correctly, the bearing channel is upwards (to receive the bearings) and the top part will sit upside down on the bearings, with the support flange facing upwards. This is the optimal print orientation for surface quality.

Also, the variable `test` from `Filament_Spinner_v2_BEST_SETTINGS.scad` was a useful way to debug the model, would it be possible to keep that functionality?

[Assistant]
Filament_Spinner.scad:
```
$fn = 96;

part_to_generate = 3; // 1=top, 2=bottom, 3=both

//CUSTOMIZER VARIABLES

spool_hole_diameter = 56 ; // inside diameter of actual spool hole
hub_nub_slop = .5; // how much horizontal space (tolerance) to leave between spinner and spool
support_flange_extension = 7; // how much the rim that supports the spool should overlap with the spool

bearing_diameter = 4.5; // the size of BBs/marbles/etc. that you will be using
bearing_channel_width_slop = .2; // how much wider than the bearing diameter the channel should be
bearing_channel_extra_depth = 5; // how deep to make the groove that the bearings sit in, beyond the size of the bearing
bearing_tongue_slop = 1; // how much narrower than bearing diameter the part that rides on to of the bearings should be
bearing_loading_well_depth = 2; // how much lower the inside of the base is than the outer wall so bearings are easier to load

wall_thickness = 1.2; // your slicer setting; used for parts that only need to be (double) walls

part_clearance = 1.2; // space to leave between the two pieces so they don't rub

container_height = 80; // inside height of the spool container, used for creating support stalk, if set, overall height will equal this
stalk_diameter = 15; // size of the part that sticks up solely to keep the container lid from pressing down on spool
stalk_slop = .5; // how much space to leave between stalk (which is part of the bottom base) and the edges of the hole in the spinning top part

//CUSTOMIZER VARIABLES END

// GENERATED VARS -- DO NOT CHANGE THESE

separation = 2.1* (spool_hole_diameter/2 + support_flange_extension); // how far over to put the second piece in the drawing

base_flange_radius = spool_hole_diameter/2 + support_flange_extension;
base_body_radius = spool_hole_diameter/2 - hub_nub_slop - 2*wall_thickness - part_clearance;
base_channel_inside_of_outer_wall_radius = base_body_radius - 2*wall_thickness;
base_channel_outside_of_inner_wall_radius = base_channel_inside_of_outer_wall_radius - bearing_diameter - bearing_channel_width_slop;

base_flange_height = 2*wall_thickness;
base_body_height = bearing_diameter + bearing_channel_extra_depth;

spinner_flange_radius = base_flange_radius;

spinner_stalk_hole_radius = stalk_diameter/2 + stalk_slop;
spinner_body_radius = spool_hole_diameter/2 - hub_nub_slop;
spinner_body_inside_wall_radius = spinner_body_radius - 2*wall_thickness;

spinner_tongue_outer_wall_diameter = base_channel_inside_of_outer_wall_radius - bearing_tongue_slop;
spinner_tongue_inner_wall_diameter = base_channel_outside_of_inner_wall_radius + bearing_tongue_slop;

spinner_flange_height = base_flange_height;
spinner_top_height = spinner_flange_height;

spinner_body_height = bearing_diameter + bearing_channel_extra_depth - base_flange_height ;
spinner_tongue_height = bearing_channel_extra_depth + part_clearance;

if ( part_to_generate == 1 ) {
    spinner();
} else if ( part_to_generate == 2 ) {
    base();
} else {
    spinner();
    base();
}

module spinner() {

    // SPINNER TOP PIECE -- upside down

    // top layer
    difference () {
        cylinder(h=spinner_top_height, r=spinner_body_radius);
        // hole for stalk
        cylinder(h=spinner_top_height, r=spinner_stalk_hole_radius);
    }

    translate([0,0,spinner_top_height]) {
        // outer wall
        difference () {
            cylinder(h=spinner_body_height, r=spinner_body_radius);
            cylinder(h=spinner_body_height, r=spinner_body_inside_wall_radius);
        }
        // tongue
        difference () {
            cylinder(h=spinner_tongue_height, r=spinner_tongue_outer_wall_diameter);
            cylinder(h=spinner_tongue_height, r=spinner_tongue_inner_wall_diameter);
        }
    }

    // flange
    translate([0,0,spinner_top_height + spinner_body_height]) {
        difference () {
            cylinder(h=spinner_flange_height, r=spinner_flange_radius);
            cylinder(h=spinner_flange_height, r=spinner_body_inside_wall_radius);
        }
    }

}

module base() {

    // BASE BOTTOM PIECE

    translate([separation,0,0]) {

        // bottom layer including flange
        cylinder(h=base_flange_height, r=base_flange_radius);

        translate([0,0,base_flange_height]) {
            difference () {
                // nub main
                cylinder(h=base_body_height, r=base_body_radius );

                // bearing channel
                cylinder(h=base_body_height, r=base_channel_inside_of_outer_wall_radius);
            }

            // inside bearing channel
            cylinder(h=base_body_height - bearing_loading_well_depth, r=base_channel_outside_of_inner_wall_radius);
        }

        // stalk
        cylinder(h=container_height, r=stalk_diameter/2);
    }

}

```
Filament_Spinner_v2_BEST_SETTINGS.scad:
```

/*
v2 adds a 2nd and 3rd spool diameter, fa/fs instead of fn
*/

$fa = .5;
$fs = .5;

part_to_generate = 3; // 1=top, 2=bottom, 3=both

test = 0;

//CUSTOMIZER VARIABLES

spool_hole_diameter = 32 ; // inside diameter of actual spool hole
spool_hole_diameter2 = 42 ; // inside diameter of actual spool hole
spool_hole_diameter3 = 52 ; // inside diameter of actual spool hole

hub_nub_slop = .5; // how much horizontal space (tolerance) to leave between spinner and spool
support_flange_extension = 18; // how much the rim that supports the spool should overlap with the spool

bearing_diameter = 4.5; // the size of BBs/marbles/etc. that you will be using
bearing_channel_width_slop = .3; // how much wider than the bearing diameter the channel should be
bearing_channel_extra_depth = 5; // how deep to make the groove that the bearings sit in, beyond the size of the bearing
bearing_tongue_slop = .6; // how much narrower than bearing diameter the part that rides on top of the bearings should be
bearing_loading_well_depth = 1; // how much lower the inside of the base is than the outer wall so bearings are easier to load

wall_thickness = 1.2; // your slicer setting; used for parts that only need to be (double) walls

part_clearance = 1.2; // space to leave between the two pieces so they don't rub

container_height = 78; // inside height of the spool container, used for creating support stalk, if set, overall height will equal this
stalk_diameter = 8; // size of the part that sticks up solely to keep the container lid from pressing down on spool
stalk_slop = .8; // how much space to leave between stalk (which is part of the bottom base) and the edges of the hole in the spinning top part so it slips over and doesn't rub

//CUSTOMIZER VARIABLES END

// spinner brace hole is 70mm across

// GENERATED VARS -- DO NOT CHANGE THESE

// how far over to put the second piece in the drawing
separation = test == 1 ? 0 : 2.1* (spool_hole_diameter/2 + support_flange_extension);

base_flange_radius = spool_hole_diameter/2 + support_flange_extension;
base_body_radius = spool_hole_diameter/2 - hub_nub_slop - 2*wall_thickness - part_clearance;
base_channel_inside_of_outer_wall_radius = base_body_radius - 2*wall_thickness;
base_channel_outside_of_inner_wall_radius = base_channel_inside_of_outer_wall_radius - bearing_diameter - bearing_channel_width_slop;

base_flange_height = wall_thickness*2;
base_body_height = bearing_diameter + bearing_channel_extra_depth;

spinner_flange_radius = base_flange_radius;

spinner_stalk_hole_radius = stalk_diameter/2 + stalk_slop;
spinner_body_radius = spool_hole_diameter/2 - hub_nub_slop;
spinner_body_radius2 = spool_hole_diameter2/2 - hub_nub_slop;
spinner_body_radius3 = spool_hole_diameter3/2 - hub_nub_slop;
spinner_body_inside_wall_radius = spinner_body_radius - 2*wall_thickness;

spinner_tongue_outer_wall_diameter = base_channel_inside_of_outer_wall_radius - bearing_tongue_slop;
spinner_tongue_inner_wall_diameter = base_channel_outside_of_inner_wall_radius + bearing_tongue_slop;

spinner_flange_height = base_flange_height;
spinner_top_height = spinner_flange_height;

spinner_body_height = bearing_diameter + bearing_channel_extra_depth - base_flange_height ;
spinner_tongue_height = bearing_channel_extra_depth + part_clearance;

if ( part_to_generate == 1 ) {
    spinner();
} else if ( part_to_generate == 2 ) {
    base();
} else if ( test == 1 ) {
    difference() { union() {
        translate([0,0,spinner_flange_height+spinner_body_height+spinner_top_height+base_flange_height+part_clearance]) rotate([180,0,0]) spinner();
        base(); } cube([base_flange_radius,base_flange_radius,container_height]); }
} else {
    spinner();
    base();
}

module spinner() {

    // SPINNER TOP PIECE -- upside down

    // top layer
    difference () {
        cylinder(h=spinner_top_height, r=spinner_body_radius);
        // hole for stalk
        cylinder(h=spinner_top_height, r=spinner_stalk_hole_radius);
    }

    translate([0,0,spinner_top_height]) {
        // outer wall
        difference () {
            union() {
                cylinder(h=spinner_body_height, r=spinner_body_radius);
                translate([0,0,spinner_body_height-2*wall_thickness]) cylinder(h=2*wall_thickness, r=spinner_body_radius2);
                translate([0,0,spinner_body_height-wall_thickness]) cylinder(h=wall_thickness, r=spinner_body_radius3);
            }
            cylinder(h=spinner_body_height, r=spinner_body_inside_wall_radius);
        }
        // tongue
        difference () {
            cylinder(h=spinner_tongue_height, r=spinner_tongue_outer_wall_diameter);
            cylinder(h=spinner_tongue_height, r=spinner_tongue_inner_wall_diameter);
        }
    }

    // flange
    translate([0,0,spinner_top_height + spinner_body_height]) {
        difference () {
            cylinder(h=spinner_flange_height, r=spinner_flange_radius);
            cylinder(h=spinner_flange_height, r=spinner_body_inside_wall_radius);
        }
    }

}

module base() {

    // BASE BOTTOM PIECE

    translate([separation,0,0]) {

        // bottom layer including flange
        cylinder(h=base_flange_height, r=base_flange_radius);

        translate([0,0,base_flange_height]) {
            difference () {
                // nub main
                cylinder(h=base_body_height, r=base_body_radius );

                // bearing channel
                cylinder(h=base_body_height, r=base_channel_inside_of_outer_wall_radius);
            }

            // inside bearing channel
            cylinder(h=base_body_height - bearing_loading_well_depth, r=base_channel_outside_of_inner_wall_radius);
        }

        // stalk
        cylinder(h=container_height, r=stalk_diameter/2);
    }

}

```


[User]
Hey, I'm looking for a design for a modular gate system, primarily for FPV drone racing, but generally useful for any situation needing a quick, reconfigurable structure. I've got some wooden strips that are 10x5mm and I want to connect them with printed corner pieces, forming polygons. These corner pieces also need to have a spot to hold magnets for attaching the gate to a frame.

Specifically, I need two different types of parts: a "corner" piece that joins two strips together at the desired angle, and a "magnet mount" piece that attaches to the side of a strip and holds a magnet. I want to be able to select which piece (or both) is rendered via a variable in the customizer, so I'll have a `part` variable that can be "corner", "magnetMount", or "both".

The corner piece should create an angle that will allow me to create regular polygons with a specified number of sides, defined by a `corners` variable. So, for a hexagon, `corners` would be 6. The wooden strip slots should be based on my 10x5mm strips, but designed to fit snugly using parametric dimensions. The `width` of the cutout will be 10, the `height` will be 5, and the `length` should be 14.

The magnet mount needs to hold a cylindrical magnet of a specified diameter and height. I want to set those with `magnetDiameter` and `magnetHeight`. I'm using 17mm diameter and 3mm height magnets, but I need to be able to change that so it will fit various sized magnets.  The magnet needs to be recessed into the magnet mount and held in place by a surrounding wall. The magnet mount needs to also allow for the 10x5mm wooden strips to pass through the mount.

I would also need to control the wall thickness of both the corner pieces and magnet mounts, so include variables for overall wall thickness `walls` and inner wall thickness `wallsInner` and another thickness for the magnet holder wall `outerWall`.

I'd like to use a `hull()` to smooth the edges of the corner piece where it joins together, so include a `hull` variable as well.

It would be great to use OpenSCAD for this. I do not expect any other libraries except for the `hull()` operation which is built-in. I'm expecting the files will contain full parametric designs for both a corner piece and a magnet holder with all the variables described above. Thanks!

[Assistant]
stick_connector.scad:
```
/* [General] */
// Select the part to be viewed
part = "both"; // [corner: Corner Piece, magnetMount: Magnet Mount, both: Corner and Magnet Mount]

// Amount of Corners
corners = 6;

// Width of the cutout
width = 10;
// Height of the cutout
height = 5;
// Length of the cutout
length= 14;

// Wall thickness
walls = 1.6;
wallsInner = 0.5;
/* [Magnet Holder] */
// Magnet Diameter (I chouse this to be slightly smaller than the actual diameter so the magnet fits snuggly)
magnetDiameter = 16;
// Magnet Height
magnetHeight = 3;
// Thickness of the piece that holds the magnet (it should be thick enough to hold the structure but thin enough to have enough magnetic force)
outerWall = 0.6;

/* [Hidden] */
degrees = 360 / corners;
hull = 0.2;

outerHeight = height + walls * 2;
outerWidth = width + walls * 2;
outerLength = length + wallsInner;

magnetRadius = magnetDiameter / 2;
magnetOuterWidth = width + walls * 2 + magnetHeight + outerWall;
magnetHolderLength = magnetDiameter + walls * 2;

if(part == "corner") {
  corner();
}
if(part == "magnetMount") {
  magnetMount();
}
if(part == "both") {
  corner();
  translate([outerLength - magnetHolderLength, -magnetOuterWidth - 5, 0])
  magnetMount();
}

module magnetMount() {
  difference() {
    cube([magnetHolderLength, magnetOuterWidth, outerHeight]);
    translate([-1, walls, walls])
      cube([magnetHolderLength + 2, width, height]);

    translate([magnetHolderLength / 2, walls * 2 + width, outerHeight / 2])
      rotate([-90,0,0])
        cylinder(r = magnetRadius, h = magnetHeight);
  }
}

module corner() {
  part();
  translate([0, outerWidth, 0])
    rotate([0, 0, 180 - degrees])
      part();

  hull() {
    translate([0, outerWidth, 0])
      rotate([0, 0, 180 - degrees])
        translate([-hull, 0, 0])
          cube([hull, outerWidth, outerHeight]);

    translate([-hull, 0, 0])
      cube([hull, outerWidth, outerHeight]);
  }

  module part() {
    difference() {
      cube([outerLength, outerWidth, outerHeight]);
      translate([wallsInner, walls, walls])
        cube([outerLength, width, height]);
    }
  }
}
```


[User]
I'd like to create a corner protector, ideally designed to be 3D-printed in a flexible material. I need it to be parametric so I can adjust it using the Customizer in OpenSCAD.

The basic shape is like a rounded corner piece. Imagine a block with the corner scooped out, and that scooped-out area is made with rounded edges. I need to control the overall width of the sides (before the corner scoop), the height of the block, the radius of the rounded edge, and the height of the internal hollowed out area.

I'd like the external dimensions to be easy to control. I want to be able to specify the overall width of the arms (the part that goes along each edge before the rounded corner). I'd also need to control the height of the entire protector and the height of the internal "hollow" part where the corner sits. The roundedness of the scoop should also be a parameter, essentially the radius of the spheres used to create the shape. It should be a solid piece, not something with open surfaces.

To clarify the structure: the basic shape is a hull generated using six spheres, three at the top and three at the bottom. Then, two subtractions cut the final shape: a rotated cube to make the corner hollow and another cube to create the inner area which will hold the corner. The final structure should be ready for 3d printing.

Can you generate an OpenSCAD file for this? I would also like the code to use comments so it will be easy to understand. And to clarify I'm aiming for something similar to the provided 'Eckenschutz.scad', but I'd like it to be completely parametric for customisation.

[Assistant]
Eckenschutz.scad:
```
$fn = 64;
radius = 5;
width = 40;
InnerHeight = 16;

difference() {
    hull() {
			translate ([width/2, 0, -InnerHeight/2]) sphere(r=radius);
			translate ([-width/2, 0, -InnerHeight/2]) sphere(r=radius);
			translate ([0, width/2, -InnerHeight/2]) sphere(r=radius);

			translate ([width/2, 0, InnerHeight/2]) sphere(r=radius);
			translate ([-width/2, 0, InnerHeight/2]) sphere(r=radius);
			translate ([0, width/2, InnerHeight/2]) sphere(r=radius);
    }

    rotate([0, 0, 45]) translate([-width/2+width/2*sin(45), -width/2+width/2*sin(45), 0]) cube([width, width, InnerHeight], center=true);
    translate([0, -(width/2)-radius+radius/5*2, 0]) cube([width*1.41, width, InnerHeight+2*radius], center=true);
}

```


[User]
I'm looking to design a cable organizer that I can attach to the underside of a shelf. I've previously tried a design that required passing cables through it, which is annoying when I need to replace it. So, I'd like a modular design that can be attached after the cables are already in place.

Here's what I envision: The organizer will have two main parts - a base that clamps onto the shelf, and a cable-holding section that attaches to the base with screws. The base will have a flat section to rest against the underside of the shelf, and another section perpendicular to that that comes down below the shelf. The cable holding section will have a semi-circular cutout for cables to pass through.

I'd like to make this parametric, so I can easily adjust it for different shelf thicknesses and cable amounts. Here are some starting parameters to use:

*   **Shelf thickness**: 36.5 mm
*   **Length of the top segment (base)**: 20 mm
*   **Radius of the cable passage**: 15 mm
*   **Thickness of the structural elements**: 6 mm
*   **Extrusion height (overall height)**: 10 mm

The screw holes should be designed for standard M3 screws, but I'd like them to be a little oversized to allow some play. And make sure to include some small cuts to allow for a snap fit assembly.

The code should include comments describing how each piece is assembled. I'd prefer for it to not include any library files for now, but if they are needed let me know why.

[Assistant]
Gancio_cavi_assemblabile.scad:
```
$fn=72;

Thickness_of_shelf=36.5;
Length_of_top_segment=20;
Passage_for_cables_radius=15;
Thickness_of_structure=6;
Extrusion_height=10;

difference()
{
union()
    {
cube([Thickness_of_structure,Length_of_top_segment,Extrusion_height]);
translate([Thickness_of_shelf+Thickness_of_structure,0,0])
cube([Thickness_of_structure,Passage_for_cables_radius+Thickness_of_structure,Extrusion_height]);

translate([0,0,0])
rotate([0,0,0])
cube([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2,Thickness_of_structure,Extrusion_height]);

difference()
{
       translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2,Passage_for_cables_radius+Thickness_of_structure,0])
    cylinder(r=Passage_for_cables_radius+Thickness_of_structure,h=Extrusion_height);

       translate([Thickness_of_shelf+Thickness_of_structure*2,-1,-1])
      cube([Passage_for_cables_radius,Passage_for_cables_radius+Thickness_of_structure,Extrusion_height+2]);

}

    }   //Fine union

// Tagli
    translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2,-1,-1])
        cube([0.5,Passage_for_cables_radius,Extrusion_height+2]);
    translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure-1,Passage_for_cables_radius+Thickness_of_structure,-1])
    rotate([0,0,90])
        cube([0.5,Passage_for_cables_radius,Extrusion_height+2]);

//Foro centrale passaggio_cavi
translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2,Passage_for_cables_radius+Thickness_of_structure,-1])
cylinder(r=Passage_for_cables_radius,h=Extrusion_height*3,center=true);

//Fori assemblaggio
translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure-5,Thickness_of_structure/2,Extrusion_height/2])
rotate([0,90,0])
cylinder(r=1.25,h=30,center=true);

translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2+15,Thickness_of_structure/2,Extrusion_height/2])
rotate([0,90,0])
cylinder(r=1.6,h=30,center=true);

translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2+15+3,Thickness_of_structure/2,Extrusion_height/2])
rotate([0,90,0])
cylinder(r=3,h=30,center=true);

translate([Thickness_of_shelf+Thickness_of_structure*2,-Thickness_of_shelf-Thickness_of_structure,0])
union()
rotate([0,0,90])
{
translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2-10,Thickness_of_structure/2,Extrusion_height/2])
rotate([0,90,0])
cylinder(r=1.25,h=30,center=true);

translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2+15,Thickness_of_structure/2,Extrusion_height/2])
rotate([0,90,0])
cylinder(r=1.6,h=30,center=true);

translate([Thickness_of_shelf+Passage_for_cables_radius+Thickness_of_structure*2+15+3,Thickness_of_structure/2,Extrusion_height/2])
rotate([0,90,0])
cylinder(r=3,h=30,center=true);
}
}

//Shelf
*%translate([6.2,6.25,0])
cube([36,100,10]);
```