[User]
I'd like to create a parametric template for measuring diameters, kind of like a go/no-go gauge, but with a few different sizes built in. The template should be a flat, rectangular piece with circular cutouts of varying sizes. I need to be able to easily adjust all the dimensions, both of the main body and the circles.

Specifically, the main body should be a rectangle, and I'd like to control its width and height. I also need to set the thickness, but that's probably a constant. There should be five circular cutouts, each with a different diameter and a different location on the template, with the ability to control all of their diameters and x/y locations.

I need the following parameters:

*   `plate_width`: Width of the rectangular body.
*   `plate_height`: Height of the rectangular body.
*   `plate_thickness`: Thickness of the rectangular body. (Assume a constant value, like 6mm or 8mm - whichever is more appropriate)
*  `cutout1_diameter`: Diameter of the first cutout.
*  `cutout1_x`: X position of the center of the first cutout.
*  `cutout1_y`: Y position of the center of the first cutout.
* `cutout2_diameter`: Diameter of the second cutout.
* `cutout2_x`: X position of the center of the second cutout.
* `cutout2_y`: Y position of the center of the second cutout.
* `cutout3_diameter`: Diameter of the third cutout.
* `cutout3_x`: X position of the center of the third cutout.
* `cutout3_y`: Y position of the center of the third cutout.
* `cutout4_diameter`: Diameter of the fourth cutout.
* `cutout4_x`: X position of the center of the fourth cutout.
* `cutout4_y`: Y position of the center of the fourth cutout.
* `cutout5_diameter`: Diameter of the fifth cutout.
* `cutout5_x`: X position of the center of the fifth cutout.
* `cutout5_y`: Y position of the center of the fifth cutout.

In addition, I'd like to add text labels for each cutout, specifying the cutout's diameter. The text labels should be near each cutout. They should also be created as a difference, so they're cut out of the template as well. These labels should be automatically positioned relative to their respective cutouts, as well as rotated to follow the contour of the plate, as in the example file.

I'm thinking the simplest approach would be to start with a basic cube, then use difference operations to cut out each of the five circles and text labels. I’m not looking for anything fancy – just a basic OpenSCAD file that I can customize. I don't need any external libraries or anything like that.

[Assistant]
FragmentTemplate.scad:
```
D1=57;
D2=76;
D3=122;
D4=152;
D5=85;
W=100;
H=120;

//End Variables
difference(){
Core();
Core2();

}

        translate([-W/4,H/3.5,0])linear_extrude(3)text(str(D1));

        translate([W/4-8,H/3.5,0])linear_extrude(3)text(str(D2));

        translate([10,0+12,0])rotate([0,0,-90])linear_extrude(3)text(str(D3));

        translate([-10,0-12,0])rotate([0,0,90])linear_extrude(3)text(str(D4));

        translate([0+8,-H/3.5+8,0])rotate([0,0,180])linear_extrude(3)text(str(D5));

module Core(){
   difference(){
    union(){
            cube([W,H,6],center=true);
    }
    translate([-W/4,H/2+(D1/3),0])cylinder(d=D1,center=true,h=10,$fn=64);

    translate([W/4,H/2+(D2/2.5),0])cylinder(d=D2,center=true,h=10,$fn=64);

    translate([W/2+D3/3,0/2.5,0])cylinder(d=D3,center=true,h=10,$fn=64);

    translate([-W/2-D4/3,0/2.5,0])cylinder(d=D4,center=true,h=10,$fn=64);

    translate([0,-H/2-(D5/3),0])cylinder(d=D5,center=true,h=10,$fn=64);

}
}
module Core2(){
   difference(){
    union(){
            translate([0,0,3])cube([W-2,H-2,5],center=true);
    }
    translate([-W/4,H/2+(D1/3),0])cylinder(d=D1+2,center=true,h=10,$fn=64);

    translate([W/4,H/2+(D2/2.5),0])cylinder(d=D2+2,center=true,h=10,$fn=64);

    translate([W/2+D3/3,0/2.5,0])cylinder(d=D3+2,center=true,h=10,$fn=64);

    translate([-W/2-D4/3,0/2.5,0])cylinder(d=D4+2,center=true,h=10,$fn=64);

    translate([0,-H/2-(D5/3),0])cylinder(d=D5+2,center=true,h=10,$fn=64);

}
}
```


[User]
Hi, I'm looking to create an enclosure for an in-wall junction box using OpenSCAD. I've seen a design that's pretty close to what I need, but it's got some issues in the customizer, and I'd like to have a more robust parametric version.

Essentially, I need a design that consists of a base plate, a box that mounts to it, and a lid for the box. The base plate should be larger than the box and have some mounting holes for attaching it to the wall or the box to the plate, and the box sits on the base plate. The lid needs to be sized to fit neatly inside the box. I'm thinking the shapes can all be based off an intersection of a circle and a square, as this example does.

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

*   `PLATE_THICKNESS`: The thickness of the base plate.
*   `LID_THICKNESS`: The thickness of the lid.
*   `WALL_THICKNESS`: The thickness of the walls of the box.
*   `BOX_HEIGHT`: The height of the box.
*   `USE_MOUNTHOLE_MODULE`:  This one is a bit tricky, as I see you have a few options. I'd like to have it available, but let's start with just one mount hole style for now, but let me be able to switch between the styles, let's just start with the mounting style that's using the `up_gehaeuse_mounting_hole.dxf` file.

The base plate should have an outer shape formed by intersecting a circle and a square, then have a hole in the middle that is also formed by intersecting a circle and a square. I need mounting holes placed around the perimeter of that central cut-out that's on the base plate, which I guess are also on the perimeter of the box once it's attached. I'm also including the dxf for the hole I'd like to use.

The box will be a hollow extrusion formed by the same intersection of circle and square, with some offsets for wall thickness. The lid will be a smaller extrusion based on the interior dimensions of the box.

Can you generate the necessary OpenSCAD code for this?
I'm using OpenSCAD, so standard OpenSCAD libraries will be fine.
I do have this .dxf file called `up_gehaeuse_mounting_hole.dxf`, and it will be in the same folder as the OpenSCAD file.

[Assistant]
up_gehaeuse.scad:
```
PLATE_THICKNESS = 2.0;
LID_THICKNESS = 1.2;
WALL_THICKNESS = 1.2;
BOX_HEIGHT = 50;
USE_MOUNTHOLE_MODULE = 3;

$fn=0;
$fa=0.5;
$fs=0.5;

module dose() {
    difference() {
        union(){
            linear_extrude(BOX_HEIGHT-(LID_THICKNESS/2.0)){
                intersection() {
                    circle(d=58);
                    square([50,50], center=true);
                }
            }
        }
        union(){
            translate([0,0,-.1])
            linear_extrude(BOX_HEIGHT+0.2){
                intersection() {
                    circle(d=58-(2*WALL_THICKNESS));
                    square([50-(2*WALL_THICKNESS), 50-(2*WALL_THICKNESS)], center=true);
                }
            }
        }
    }
}

module mount_hole1() {
    rotate([0,0,-15]) {
        translate([0,0,1]) {
            union() {
                rotate_extrude(angle=30){
                    translate([30,0,0]) {
                        square([3,2], center=true);
                    }
                }
                translate([30,0,-1]) {
                    cylinder(d=3,h=2);
                }

                rotate([0,0,30]) {
                    translate([30,0,-1]) {
                        cylinder(d=6,h=2);
                    }
                }
            }
        }
    }
}

module mount_hole2() {
    translate([30,0,-0.1]) {
        linear_extrude(PLATE_THICKNESS+0.2) {
            import("up_gehaeuse_mounting_hole.dxf");
        }
    }
}

module mount_hole3() {
    linear_extrude(PLATE_THICKNESS+0.2) {
        rotate([0,0,-15]) {
            intersection(){
                difference() {
                    circle(r=31.5);
                    circle(r=28.5);
                }
                polygon([[0,0],[40,0],[40,20],[0,0]]);
            }
            translate([30,0,0]) {
                circle(d=3);
            }
            rotate([0,0,30]) {
                translate([30,0,0]) {
                    circle(d=6);
                }
            }
        }
    }
}

module mount_holes() {
    for (i=[0:90:360]) {
        rotate([0,0,i]) {
            if (USE_MOUNTHOLE_MODULE == 1) {
                mount_hole1();
            }
            if (USE_MOUNTHOLE_MODULE == 2) {
                mount_hole2();
            }
            if (USE_MOUNTHOLE_MODULE == 3) {
                mount_hole3();
            }
        }
    }
}

module plate() {
    linear_extrude(PLATE_THICKNESS){
        difference(){
            intersection(){
                square([70.5,70.5], center=true);
                circle(d=90);
            }
            intersection(){
                circle(d=58);
                square([50,50], center=true);
            }
        }
    }
}

module deckel(){
    linear_extrude(LID_THICKNESS / 2.0){
        intersection() {
            circle(d=58);
            square([50,50], center=true);
        }
    }
    linear_extrude(LID_THICKNESS){
        intersection() {
            circle(d=58-(2*WALL_THICKNESS));
            square([50-(2*WALL_THICKNESS),50-(2*WALL_THICKNESS)], center=true);
        }
    }
}

module dose_komplett() {
    difference(){
        plate();
        translate([0,0,-.1]) {
            mount_holes();
        }
    }
    dose();
}

dose_komplett();
translate([70,0,0]) {
    deckel();
}

```


[User]
I'd like to design a customizable pill box, and I need some help with the OpenSCAD code. I want it to be roughly the size of a credit card as a default, with a shallow box and wide cells for easy access.

Here are the basic parameters I have in mind:

*   **Overall Box Dimensions:**
    *   `boxX`: Width of the box (default 54mm)
    *   `boxY`: Length of the box (default 85.4mm)
    *   `boxZ`: Height of the box (default 14mm)

*   **Box Wall Thickness:**
    *   `box_floor`: Thickness of the box floor (default 2mm)
    *   `box_wall`: Thickness of the outer walls (default 2.4mm)
    *   `inner_wall`: Thickness of the inner cell dividers (default 1mm)

*   **Lid Parameters:**
    *   `lid_thick`: Thickness of the lid (default 2mm)
    *   `lid_tol`: Tolerance for the lid fit (default 0.5mm)

*   **Cell Parameters:**
    *   `cell_divider`: Boolean for whether to include cell dividers (0 for no, 1 for yes)
    *   `n_cellY`: Number of cells along the Y axis (default 4)
    *   `cellX_wid`: Width of the cell in the X direction (default 12mm)
    *   `cellZ_space`: Space between the cell divider and the lid (default 2mm)

The box and lid should have rounded corners. I think a `minkowski` sum with a sphere for the rounding would be good. The lid should fit into the box with the given tolerance. There should also be a simple lock mechanism that's a semi-circle extrusion, with a corresponding cut-out on the lid. Finally, the cell dividers should extend up from the floor but not all the way to the lid.

I'm thinking the design should have the option of generating just the box, just the lid, or both. This can be achieved with a `part` variable, where:
    * `part = 1`:  generates only the box
    * `part = 2`:  generates only the lid
    * `part = 3`:  generates both the box and the lid

I'd like to ensure the design is parametric, so that I can easily change these values to create pill boxes of different sizes and cell configurations. I don't want to use any external libraries if possible, but I would like it to render with good smoothness, which I think can be achieved by setting `$fn` to 32 or higher (as a parameter of the design).

Can you create an OpenSCAD file that implements this design?

[Assistant]
PillBox.scad:
```
// File: PillBox.scad
// Version: OpenSCAD 2015.03-2
// Author: Kijja Chaovanakij
// Date: 2018.09.3
//
// defalt box size equals credit card size.
// Date: 2020.07.14
// Revise-1
// Rewrite for faster rendering

//smoothness
$fn=32; //[32:low,40:medium,48:high]
//generate parts
part=3; //[1:box,2:lid,3:both]

/*[Box]*/
//external box size
boxX=54; //54
boxY=85.4; //85.4
boxZ=14; //14
//thickness of the box
box_floor=2; //2
box_wall=2.4; //2.4
inner_wall=1.0; //1.0

/*[lid]*/
//thickness of the lid
lid_thick=2; //2
//lid & box fit tolerance
lid_tol=0.5; //0.5

/*[Cell]*/
//cell divider
cell_divider=1; //[0:no,1:yes]
//number of cell y
n_cellY=4; //[0:1:5]
//width of cell x [0 to boxX/2]
cellX_wid=12;
//space between cell and lid[2]
cellZ_space=2;

/*[Hidden]*/
cellY_wid=(boxY-box_wall*2-inner_wall*(n_cellY-1))/n_cellY+inner_wall;

//======== main ========//
if (part==1 || part==3)
  translate([-boxX*1.1, 0, 0])
    box();
if (part==2 || part==3)
  color("Gold")
  translate([boxX*0.1, 0, 0])
    lid();

//box();
module box(){
  color("SkyBlue")
  difference(){
    round_cube(hi=boxZ, xwid=boxX, ywid=boxY, rad=box_wall);
    translate([box_wall, box_wall, box_floor])
      round_cube(hi=boxZ, xwid=boxX-box_wall*2, ywid=boxY-box_wall*2, rad=box_wall/2);
    translate([box_wall/2, -box_wall/2, boxZ-lid_thick])
      frustum_cube(hi=lid_thick,
                   baseX=boxX-box_wall,
                   baseY=boxY,
                   base_rad=2,
                   topX=boxX-box_wall*2,
                   topY=boxY-box_wall,
                   top_rad=1);
  }//d
  color("Gold")
  translate([boxX/2, box_wall*3/4, boxZ-lid_thick])
    lock();
  color("Tan")
  if (cell_divider==1)
    cell();
}//m
//

//lid();
module lid(hi=lid_thick,
           baseX=boxX-box_wall-lid_tol,
           baseY=boxY-box_wall/2-lid_tol,
           base_rad=2,
           topX=boxX-box_wall*2-lid_tol,
           topY=boxY-box_wall*1.5-lid_tol,
           top_rad=1){
  difference(){
    frustum_cube(hi, baseX, baseY, base_rad, topX, topY, top_rad);
    translate([baseX/2, box_wall*3/4, 0])
      lock(sizeX=boxX/2+lid_tol, dia=box_wall/2+lid_tol/2);
  }//d
  translate([baseX/2, baseY/6, hi])
      lock(sizeX=boxX/4, dia=box_wall/2);
}//m
//

//round_cube();
module round_cube(hi=4, xwid=10, ywid=15, rad=1){
  translate([xwid/2, ywid/2, hi/2])
  minkowski(){
    cube([xwid-rad*2, ywid-rad*2, hi-rad*2], true);
    sphere(r=rad);
  }//m
}//m
//

//frustum_cube();
module frustum_cube(hi=2, baseX=20, baseY=30, base_rad=2, topX=16, topY=26, top_rad=1){
  ep=0.001;
  translate([baseX/2, baseY/2, 0])
  hull(){
    linear_extrude(ep)
    minkowski(){
      circle(r=base_rad);
      square([baseX-base_rad*2, baseY-base_rad*2], true);
    }//m
    translate([0, 0, hi-ep])
    linear_extrude(ep)
    minkowski(){
      circle(r=top_rad);
      square([topX-top_rad*2, topY-top_rad*2], true);
    }//m
  }//h
}//m
//

//lock();
module lock(sizeX=boxX/2, dia=box_wall/2){
  rotate([0, 90, 0])
  translate([0, 0, -sizeX/2])
  linear_extrude(sizeX)
    circle(d=dia);
}//m
//

module cell(){
  if (cellX_wid>0)
    translate([box_wall+cellX_wid, box_wall, box_floor])
      cube([inner_wall, boxY-box_wall*2, boxZ-box_floor-lid_thick-cellZ_space]);
//cell y
  translate([box_wall+cellX_wid, box_wall-inner_wall, box_floor])
    for (i=[1:1:n_cellY-1])
      translate([0, i*cellY_wid, 0])
        cube([boxX-box_wall*2-cellX_wid, inner_wall, boxZ-box_floor-lid_thick-cellZ_space]);
}//m
//
```


[User]
Hi! I'm looking for some help designing a parametric system for LEGO sorting tray dividers, and I think OpenSCAD would be the best way to go about it.

Basically, I want to be able to generate dividers for the LEGO sorting trays that come with a bunch of their education kits. The trays are all different sizes, but I want the code to be able to handle all of them. I've seen the tray numbers are #45499, #45497, #45498, and #9840; but, specifically, the design should be able to generate the following tray sizes (Width, Depth, Height):

*   A: 79, 134, 28.5
*   B: 87.5, 103, 28.5
*   C: 91.5, 103, 28.5
*   D: 79, 134, 28.5
*   E: 40, 122, 28.5
*   F: 42, 122, 23
*   G: 87, 76, 28.5
*   H: 83, 80, 28.5
*   I: 83, 76, 28.5
*   J: 46, 55, 23
*   K: 35.5, 55, 23
*   L: 46, 55, 23
*   M: 35.5, 55, 23

I'd like to select a tray by a simple letter identifier ('A', 'B', 'C'...) instead of having to input all three dimensions manually each time.

The dividers themselves will either run along the width or depth of the tray, and I want to be able to specify their positions as a percentage of the tray's width or depth. For example, a divider at 0.5 along the width would divide the tray in half horizontally, and two dividers at 0.25 and 0.75 along the depth would split it into 4 equal depth sections. I'd also want to have partial dividers that don't run the full length or depth, where I can specify the position, start, and end points.

A few more things:
*   I want a perimeter option that includes the tray walls to help keep dividers in the tray when it's removed. This should be an on/off toggle.
*   There should be an option to connect the ends of the dividers at their start and end points (where they meet other dividers and the tray walls), which should also be on/off
*   I'd like to be able to add some little "anti-crack" supports at the ends of dividers, to prevent them from splitting during cooling - and I want to be able to control the size of these supports.
*   I'd like to set the thickness of the walls and dividers, as well as the bottom thickness. These should all be parametric.

I think that's everything. Can you generate the necessary OpenSCAD files for this? I don't need any specific libraries - just vanilla OpenSCAD is fine. Thanks!

[Assistant]
Parametric_LEGO_Sorting_Tray_Divider_V1.5.scad:
```
//LEGO sorting tray (#45499) divider
//
//
//  AAAAAAA CCCCCCC KKK JJJJ  IIIIII
//  AAAAAAA CCCCCCC KKK JJJJ  IIIIII
//  AAAAAAA CCCCCCC KKK JJJJ  IIIIII
//  AAAAAAA CCCCCCC LLLL MMM  IIIIII
//  AAAAAAA CCCCCCC LLLL MMM  HHHHHH
//  AAAAAAA  DDDDDD LLLL MMM  HHHHHH
//  BBBBBBBB DDDDDD EEEE FFFF HHHHHH
//  BBBBBBBB DDDDDD EEEE FFFF HHHHHH
//  BBBBBBBB DDDDDD EEEE FFFF GGGGGG
//  BBBBBBBB DDDDDD EEEE FFFF GGGGGG
//  BBBBBBBB DDDDDD EEEE FFFF GGGGGG
//
//
// Conventions: Width = along X-Axis
//              Depth = along Y-Axis
//              Height = along Z-Axis
//

/*  */

//Which tray would you like to make?
Tray = "A"; //[A,B,C,D,E,F,G,H,I,J,K,L,M]

//Perimeters keep parts in the divider when it is removed from the LEGO sorting tray
Include_Perimeter = "no"; //[yes,no]

//Adds perimeter walls only between dividers. Helps with rigidity on thin or long dividers.
Connect_Dividers_At_Ends = "no"; //[yes,no]

//Adds a short perimeter (multiple of wall/divider thickness) at divider ends to prevent cracking during part cooling
AntiCrack_Supports = 1.5; // [1:0.25:3.0]

//in millimeters
Perimeter_And_Divider_Width = 2.0; //[0.5:0.25:3.0]
Thickness = Perimeter_And_Divider_Width * 1;

//in millimeters
Bottom_Thickness = 1.0; //[0.5:0.25:3.0]
BotThick = Bottom_Thickness * 1;

//Tray dividers
//
// Wdividers split the tray along its width (dividers parallel to Y-Axis)
// Ddividers split the tray along its depth (dividers parallel to X-Axis)
//
// The position of the divider is a % of the tray width/depth, so a value
// of 0.50 would divide the tray into two equal pieces
//
// Examples:
//
// Divide the tray into 2 equal halves along its depth
// Ddividers = [0.5];
//
// Divide the tray into 4 equal parts along its width
// Wdividers = [0.25, 0.50, 0.75];
//
// Divide the tray into 4 equal quarters
// Wdividers = [0.5];
// Ddividers = [0.5];
//

//Places dividers along tray width (parallel to Y-axis) Ex: [0.5] splits in half, [0.333, 0.666] splits in thirds
Wdividers = [0.5];

//Places dividers along tray depth (parallel to X-axis) Ex: [0.25, 0.50, 0.75] makes 3 dividers, splitting tray into 4 equal sections
Ddividers = [0.333, 0.666];

//Partial tray dividers
//
// Similar to Wdividers/Ddividers, but the start/end points of the divider must also be specified
// This creates a divider that doesn't span the entire width/depth of the tray
//
// PWdiv splits the tray along its width (dividers parallel to Y-Axis)
// PDdiv splits the tray along its depth (dividers parallel to X-Axis)
//
// PWdiv = [ [Position, Start, End] ];
// PWdiv = [ [Position1, Start1, End1], [Position2, Start2, End2] ];
//
//       Position = position of the divider as a % of the tray width
//       Start = where the divider begins as a % of the tray depth
//       End = where the divider ends as a % of the tray depth
//
// Examples:
//
// Divide the tray into 2 equal halves along its depth
// (Same result as Ddividers = [0.5];)
// PDdiv = [ [0.5, 0.0, 1.0] ];
//
// Make a "T"-shaped divider with the vertical of the "T" centered on the
// width of the box:
// Ddividers = [0.5];
// PWdiv = [ [0.5, 0, 0.5] ]; (divider starts at 50% width, goes from 0% to 50% depth)
//
// Make a "U" shaped divider 1/3 of the tray width and 1/2 of the tray depth
// that is centered on the width of the tray
// PWdiv = [[0.333, 0.5, 1.0], [0.666, 0.5, 1.0]]; (two dividers at 33.3% and 66.6% of tray width)
// PDdiv = [[0.5, 0.333, 0.666]]; (divider at 50% of tray depth)
//

//Partial Width dividers.Ex: [ [0.333, 0, 0.5], [0.666, 0, 0.5] ] creates two dividers to split a tray into thirds, but the dividers only extend across half of the tray depth. See thing details for a detailed explanation. General format: [ [div1position, start, end], [div2position, start, end], [div3position, start, end] ]
PWdiv = [];

//Partial Depth dividers. Ex: [ [0.5, 0.25, 0.75] ] will split a tray's depth in half, but only from 25% to 75% of the tray's width. See thing details for a detailed explanation. General format: [ [div1position, start, end], [div2position, start, end], [div3position, start, end] ]
PDdiv = [];

/* [Hidden] */
Perimeter = (Include_Perimeter == "yes") ? true : false;
Connect_Ends = (Connect_Dividers_At_Ends == "yes") ? true : false;

//Physical dimensions of the LEGO Sorting Tray
Angle = 5;          //Angle of tray walls from vertical
TopR = 10;          //Corner radius at top of tray
BotR = 7;           //Corner radius at bottom tray
Delta = 0.001;      //A minimum thickness to use with the hull() function

$fn = 50;

TrayNames = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"];
TraySizes = [
    [79, 134, 28.5],    //A
    [87.5, 103, 28.5],  //B
    [91.5, 103, 28.5],  //C
    [79, 134, 28.5],    //D
    [40, 122, 28.5],    //E
    [42, 122, 23],      //F
    [87, 76, 28.5],     //G
    [83, 80, 28.5],     //H
    [83, 76, 28.5],     //I
    [46, 55, 23],       //J
    [35.5, 55, 23],     //K
    [46, 55, 23],       //L
    [35.5, 55, 23]      //M
];

for(t = [0:len(TrayNames)]) {
    if (Tray == TrayNames[t]) {

        echo("Creating tray of size: ", Width, Depth, Height);

        Width = TraySizes[t][0];
        Depth = TraySizes[t][1];
        Height = TraySizes[t][2];
        Shrink = Height * tan(Angle);

       intersection() {
            union() {

                //Include tray walls
                if (Perimeter) {
                    difference() {
                        hull() {
                            translate([0,0,Height]) rBox(TopR, Width, Depth, Delta);
                            translate([Shrink, Shrink]) rBox(BotR, Width-2*Shrink, Depth-2*Shrink, Delta);
                        }
                        hull() {
                            translate([Thickness,Thickness,Height]) rBox(TopR, Width-2*Thickness, Depth-2*Thickness, Delta);
                            translate([Shrink+Thickness, Shrink+Thickness, BotThick]) rBox(BotR, Width-2*Shrink-2*Thickness, Depth-2*Shrink-2*Thickness, Delta);
                        }
                    }
                }

                //No tray walls, only bottom
                else {
                    translate([Shrink, Shrink, 0]) rBox(BotR, Width - 2*Shrink, Depth - 2*Shrink, BotThick);
                }

                //Width dividers
                for(div=Wdividers) {
                    translate([div*Width-Thickness/2,0,0])
                    hull() {
                        //Top of divider
                        translate([0,0,Height]) linear_extrude(height=Delta) square([Thickness, Depth]);

                        //Bottom of divider
                        translate([0, Shrink, 0]) linear_extrude(height=Delta) square([Thickness, Depth-2*Shrink]);
                    }
                }

                //Depth dividers
                for(div=Ddividers) {
                    translate([0, div*Depth-Thickness/2])
                    hull() {
                        //Top of divider
                        translate([0,0,Height]) linear_extrude(height=Delta) square([Width, Thickness]);

                        //Bottom of divider
                        translate([Shrink, 0, 0]) linear_extrude(height=Delta) square([Width-2*Shrink, Thickness]);
                    }
                }

                //Partial width dividers
                for(div=PWdiv) {
                    translate([div[0]*Width-Thickness/2, 0, 0])
                    hull() {
                        //Top of divider
                        translate([0, div[1]*Depth - Thickness/2,Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                        translate([0, div[2]*Depth - Thickness/2,Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);

                        //Bottom of divider
                        translate([0, div[1]*Depth - Thickness/2, 0]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                        translate([0, div[2]*Depth - Thickness/2, 0]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                    }
                }

                //Partial depth dividers
                for(div=PDdiv) {
                    translate([0, div[0]*Depth-Thickness/2, 0])
                    hull() {
                        //Top of divider
                        translate([div[1]*Width - Thickness/2, 0, Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                        translate([div[2]*Width - Thickness/2, 0, Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);

                        //Bottom of divider
                        translate([div[1]*Width - Thickness/2, 0, 0]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                        translate([div[2]*Width - Thickness/2, 0, 0]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                    }
                }

                //AntiCrack Supports
                if (AntiCrack_Supports != 0) {

                    //AntiCrack_Supports on Wdividers
                    for(div=Wdividers) {
                        translate([div*Width - AntiCrack_Supports*Thickness/2, 0, 0])
                        hull() {
                            translate([0, 0, Height]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                            translate([0, Shrink, 0]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                        }
                        translate([div*Width - AntiCrack_Supports*Thickness/2, 0, 0])
                        hull() {
                            translate([0, Depth-Thickness, Height]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                            translate([0, Depth-Shrink-Thickness, 0]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                        }
                    }

                    //AntiCrack_Supports on Ddividers
                    for(div=Ddividers) {
                        translate([0, div*Depth - AntiCrack_Supports*Thickness/2, 0])
                        hull() {
                            translate([0, 0, Height]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                            translate([Shrink, 0, 0]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                        }
                        translate([0, div*Depth - AntiCrack_Supports*Thickness/2, 0])
                        hull() {
                            translate([Width-Thickness, 0, Height]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                            translate([Width-Shrink-Thickness, 0, 0]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                        }
                    }

                    //AntiCrack_Supports on PWdiv
                    for(div=PWdiv) {
                        translate([div[0]*Width-AntiCrack_Supports*Thickness/2, 0, 0]) {
                            if (div[1]==0 || div[2]==0) {
                                hull() {
                                    translate([0, 0, Height]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                    translate([0, Shrink, 0]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                }
                            }
                            if (div[1]==1 || div[2]==1) {
                                hull() {
                                    translate([0, Depth-Thickness, Height]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                    translate([0, Depth-Shrink-Thickness, 0]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                }
                            }
                        }
                    }

                    //AntiCrack_Supports on PDdiv
                    for(div=PDdiv) {
                        translate([0, div[0]*Depth - AntiCrack_Supports*Thickness/2, 0]) {
                            if (div[1]==0 || div[2]==0) {
                                hull() {
                                    translate([0, 0, Height]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                    translate([Shrink, 0, 0]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                }
                            }
                            if (div[1]==1 || div[2]==1) {
                                hull() {
                                    translate([Width-Thickness, 0, Height]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                    translate([Width-Shrink-Thickness, 0, 0]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                }
                            }
                        }
                    }
                } //AntiCrack_Supports

                //Connect dividers at their ends
                if (Connect_Ends) {

                    //Supports for width dividers
                    hull() {
                        for(div=Wdividers) {
                            translate([div*Width-Thickness/2, 0, Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                            translate([div*Width-Thickness/2, Shrink]) linear_extrude(height=Delta) square([Thickness,Thickness]);
                        }
                        for(div=PWdiv) {
                            translate([div[0]*Width-AntiCrack_Supports*Thickness/2, 0, 0]) {
                                if (div[1]==0 || div[2]==0) {
                                    translate([0, 0, Height]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                    translate([0, Shrink, 0]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                }
                            }
                        }
                    }
                    hull() {
                        for(div=Wdividers) {
                            translate([div*Width-Thickness/2, Depth-Thickness, Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                            translate([div*Width-Thickness/2, Depth-Shrink-Thickness]) linear_extrude(height=Delta) square([Thickness,Thickness]);
                        }
                        for(div=PWdiv) {
                            translate([div[0]*Width-AntiCrack_Supports*Thickness/2, 0, 0]) {
                                if (div[1]==1 || div[2]==1) {
                                    hull() {
                                        translate([0, Depth-Thickness, Height]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                        translate([0, Depth-Shrink-Thickness, 0]) linear_extrude(height=Delta) square([AntiCrack_Supports*Thickness, Thickness]);
                                    }
                                }
                            }
                        }
                    }

                    //Supports for depth dividers
                    hull() {
                        for(div=Ddividers) {
                            translate([0, div*Depth-Thickness/2, Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                            translate([Shrink, div*Depth-Thickness/2]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                        }
                        for(div=PDdiv) {
                            translate([0, div[0]*Depth - AntiCrack_Supports*Thickness/2, 0]) {
                                if (div[1]==0 || div[2]==0) {
                                    translate([0, 0, Height]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                    translate([Shrink, 0, 0]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                }
                            }
                        }
                    }
                    hull() {
                        for(div=Ddividers) {
                            translate([Width-Thickness, div*Depth-Thickness/2, Height]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                            translate([Width-Thickness-Shrink, div*Depth-Thickness/2]) linear_extrude(height=Delta) square([Thickness, Thickness]);
                        }
                        for(div=PDdiv) {
                            translate([0, div[0]*Depth - AntiCrack_Supports*Thickness/2, 0]) {
                                if (div[1]==1 || div[2]==1) {
                                    translate([Width-Thickness, 0, Height]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                    translate([Width-Shrink-Thickness, 0, 0]) linear_extrude(height=Delta) square([Thickness, AntiCrack_Supports*Thickness]);
                                }
                            }
                        }
                    }
                } //Connect Dividers
            } //union

            //Outline of tray compartment - used to cut off parts of dividers that extend beyond the tray boundaries
            hull() {
                translate([0,0,Height]) rBox(TopR, Width, Depth, Delta);
                translate([Shrink, Shrink]) rBox(BotR, Width-2*Shrink, Depth-2*Shrink, Delta);
            }

        } //intersection
    } // Tray Name
}

/* rBox -

   Create a cube of size [Width, Depth, Height]
   with corners on the X/Y plane rounded to Radius

*/
module rBox(Radius=1, Width=10, Depth=10, Height=10) {
    translate([Radius, Radius, 0])
        linear_extrude(height=Height) hull() {
            circle(r=Radius);
            translate([Width - 2*Radius, 0]) circle(r=Radius);
            translate([0, Depth - 2*Radius]) circle(r=Radius);
            translate([Width-2*Radius, Depth-2*Radius]) circle(r=Radius);
    }
}

```


[User]
I'd like a customizable nameplate generator in OpenSCAD, similar to the "Chamfered_text_plate_V7.scad" design I saw online. I want to be able to specify the text, the font, the height of the letters, and the overall size. It needs to have a chamfered look, and I'd like to have options for creating a solid tag, a container that the letters stand up out of, or a hollow version of the text.

The core of this design will center around each character of the given text, which should be extruded individually based on the width of the character, and the total width should be cumulative, and based on the specific letter. For example, letter "i" will be narrow, and letter "w" will be wide, for example. I'd like to make sure kerning can be accounted for, as well, so I should be able to make the letters closer or farther apart. I'd also like to have a little rim around each letter, as a sort of backplane/outline, but also have it be an option. Lastly, I'd want to be able to scale the whole thing easily.

I'm not sure about libraries, but the original SCAD file used some font related features, and extruding functions and linear extrusion. Hopefully, you can figure it out!

[Assistant]
Chamfered_text_plate_V7.scad:
```
//Set the text to render
textstring="Swirly";

//Font name. In the list is first a selection of bold fonts. After the dashed line you get a list of thinner fonts, which are not really recommended.
fontname="Henny Penny"; //[Vivian,Holtwood One SC,Alegreya Sans SC Black,Alfa Slab One,Asset,Black Ops One,Bowlby One,Bowlby One SC,Chango,Cinzel Decorative Black,Coustard,Elsie Black,Elsie Swash Caps Black,Erica One,Exo2 Black,Fascinate,Lobster,PassionOne,Plaster,Poller One,Rammetto One,Ruslan Display,Sarpanch Black,Sigmar One,Sonsie One,Titan One,Ultra,Hanalei,---------,ABeeZee,Abel,Abril Fatface,Aclonica,Acme,Actor,Adamina,Advent Pro,Aguafina Script,Akronim,Aladin,Aldrich,Alef,Alex Brush,Alice,Alike,Alike Angular,Allan,Allerta,Allerta Stencil,Allura,Almendra,Almendra Display,Almendra SC,Amarante,Amaranth,Amatic SC,Amethysta,Amiri,Anaheim,Andada,Andika,Angkor,Annie Use Your Telescope,Anonymous Pro,Antic,Antic Didone,Antic Slab,Anton,Arapey,Arbutus,Arbutus Slab,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arizonia,Armata,Artifika,Arvo,Asap,Asset,Astloch,Asul,Atomic Age,Aubrey,Audiowide,Autour One,Average,Average Sans,Averia Gruesa Libre,Averia Libre,Averia Sans Libre,Averia Serif Libre,Bad Script,Balthazar,Bangers,Basic,Battambang,Baumans,Bayon,Belgrano,Belleza,BenchNine,Bentham,Berkshire Swash,Bevan,Bigelow Rules,Bigshot One,Bilbo,Bilbo Swash Caps,Bitter,Black Ops One,Bokor,Bonbon,Boogaloo,Bowlby One,Bowlby One SC,Brawler,Bree Serif,Bubblegum Sans,Bubbler One,Buda,Buenard,Butcherman,Butterfly Kids,Cabin,Cabin Condensed,Cabin Sketch,Caesar Dressing,Cagliostro,Calligraffitti,Cambay,Cambo,Candal,Cantarell,Cantata One,Cantora One,Capriola,Cardo,Carme,Carrois Gothic,Carrois Gothic SC,Carter One,Caudex,Cedarville Cursive,Ceviche One,Changa One,Chango,Chau Philomene One,Chela One,Chelsea Market,Chenla,Cherry Cream Soda,Cherry Swash,Chewy,Chicle,Chivo,Cinzel,Cinzel Decorative,Clicker Script,Coda,Coda Caption,Codystar,Combo,Comfortaa,Coming Soon,Concert One,Condiment,Content,Contrail One,Convergence,Cookie,Copse,Corben,Courgette,Cousine,Coustard,Covered By Your Grace,Crafty Girls,Creepster,Crete Round,Crimson Text,Croissant One,Crushed,Cuprum,Cutive,Cutive Mono,Damion,Dancing Script,Dangrek,Dawning of a New Day,Days One,Dekko,Delius,Delius Swash Caps,Delius Unicase,Della Respira,Denk One,Devonshire,Dhurjati,Didact Gothic,Diplomata,Diplomata SC,Domine,Donegal One,Doppio One,Dorsa,Dosis,Dr Sugiyama,Droid Sans,Droid Sans Mono,Droid Serif,Duru Sans,Dynalight,Eagle Lake,Eater,EB Garamond,Economica,Ek Mukta,Electrolize,Elsie,Elsie Swash Caps,Emblema One,Emilys Candy,Engagement,Englebert,Enriqueta,Erica One,Esteban,Euphoria Script,Ewert,Exo,Exo 2,Expletus Sans,Fanwood Text,Fascinate,Fascinate Inline,Faster One,Fasthand,Fauna One,Federant,Federo,Felipa,Fenix,Finger Paint,Fira Mono,Fira Sans,Fjalla One,Fjord One,Flamenco,Flavors,Fondamento,Fontdiner Swanky,Forum,Francois One,Freckle Face,Fredericka the Great,Fredoka One,Freehand,Fresca,Frijole,Fruktur,Fugaz One,Gabriela,Gafata,Galdeano,Galindo,Gentium Basic,Gentium Book Basic,Geo,Geostar,Geostar Fill,Germania One,GFS Didot,GFS Neohellenic,Gidugu,Gilda Display,Give You Glory,Glass Antiqua,Glegoo,Gloria Hallelujah,Goblin One,Gochi Hand,Gorditas,Goudy Bookletter 1911,Graduate,Grand Hotel,Gravitas One,Great Vibes,Griffy,Gruppo,Gudea,Gurajada,Habibi,Halant,Hammersmith One,Hanalei,Hanalei Fill,Handlee,Hanuman,Happy Monkey,Headland One,Henny Penny,Herr Von Muellerhoff,Hind,Homemade Apple,Homenaje,Iceberg,Iceland,IM Fell Double Pica,IM Fell Double Pica SC,IM Fell DW Pica,IM Fell DW Pica SC,IM Fell English,IM Fell English SC,IM Fell French Canon,IM Fell French Canon SC,IM Fell Great Primer,IM Fell Great Primer SC,Imprima,Inconsolata,Inder,Indie Flower,Inika,Irish Grover,Istok Web,Italiana,Italianno,Jacques Francois,Jacques Francois Shadow,Jim Nightshade,Jockey One,Jolly Lodger,Josefin Sans,Josefin Slab,Joti One,Judson,Julee,Julius Sans One,Junge,Jura,Just Another Hand,Just Me Again Down Here,Kalam,Kameron,Kantumruy,Karla,Karma,Kaushan Script,Kavoon,Kdam Thmor,Keania One,Kelly Slab,Kenia,Khand,Khmer,Khula,Kite One,Knewave,Kotta One,Koulen,Kranky,Kreon,Kristi,Krona One,La Belle Aurore,Laila,Lakki Reddy,Lancelot,Lateef,Lato,League Script,Leckerli One,Ledger,Lekton,Lemon,Liberation Sans,Libre Baskerville,Life Savers,Lilita One,Lily Script One,Limelight,Linden Hill,Lobster,Lobster Two,Londrina Outline,Londrina Shadow,Londrina Sketch,Londrina Solid,Lora,Love Ya Like A Sister,Loved by the King,Lovers Quarrel,Luckiest Guy,Lusitana,Lustria,Macondo,Macondo Swash Caps,Magra,Maiden Orange,Mako,Mallanna,Mandali,Marcellus,Marcellus SC,Marck Script,Margarine,Marko One,Marmelad,Martel Sans,Marvel,Mate,Mate SC,Maven Pro,McLaren,Meddon,MedievalSharp,Medula One,Megrim,Meie Script,Merienda,Merienda One,Merriweather,Merriweather Sans,Metal,Metal Mania,Metamorphous,Metrophobic,Michroma,Milonga,Miltonian,Miltonian Tattoo,Miniver,Miss Fajardose,Modak,Modern Antiqua,Molengo,Molle,Monda,Monofett,Monoton,Monsieur La Doulaise,Montaga,Montez,Montserrat,Montserrat Alternates,Montserrat Subrayada,Moul,Moulpali,Mountains of Christmas,Mouse Memoirs,Mr Bedfort,Mr Dafoe,Mr De Haviland,Mrs Saint Delafield,Mrs Sheppards,Muli,Mystery Quest,Neucha,Neuton,New Rocker,News Cycle,Niconne,Nixie One,Nobile,Nokora,Norican,Nosifer,Nothing You Could Do,Noticia Text,Noto Sans,Noto Serif,Nova Cut,Nova Flat,Nova Mono,Nova Oval,Nova Round,Nova Script,Nova Slim,Nova Square,NTR,Numans,Nunito,Odor Mean Chey,Offside,Old Standard TT,Oldenburg,Oleo Script,Oleo Script Swash Caps,Open Sans,Open Sans Condensed,Oranienbaum,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Over the Rainbow,Overlock,Overlock SC,Ovo,Oxygen,Oxygen Mono,Pacifico,Paprika,Parisienne,Passero One,Passion One,Pathway Gothic One,Patrick Hand,Patrick Hand SC,Patua One,Paytone One,Peddana,Peralta,Permanent Marker,Petit Formal Script,Petrona,Philosopher,Piedra,Pinyon Script,Pirata One,Plaster,Play,Playball,Playfair Display,Playfair Display SC,Podkova,Poiret One,Poller One,Poly,Pompiere,Pontano Sans,Port Lligat Sans,Port Lligat Slab,Prata,Preahvihear,Press Start 2P,Princess Sofia,Prociono,Prosto One,PT Mono,PT Sans,PT Sans Caption,PT Sans Narrow,PT Serif,PT Serif Caption,Puritan,Purple Purse,Quando,Quantico,Quattrocento,Quattrocento Sans,Questrial,Quicksand,Quintessential,Qwigley,Racing Sans One,Radley,Rajdhani,Raleway,Raleway Dots,Ramabhadra,Ramaraja,Rambla,Rammetto One,Ranchers,Rancho,Ranga,Rationale,Ravi Prakash,Redressed,Reenie Beanie,Revalia,Ribeye,Ribeye Marrow,Righteous,Risque,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rokkitt,Romanesco,Ropa Sans,Rosario,Rosarivo,Rouge Script,Rozha One,Rubik Mono One,Rubik One,Ruda,Rufina,Ruge Boogie,Ruluko,Rum Raisin,Ruslan Display,Russo One,Ruthie,Rye,Sacramento,Sail,Salsa,Sanchez,Sancreek,Sansita One,Sarina,Sarpanch,Satisfy,Scada,Scheherazade,Schoolbell,Seaweed Script,Sevillana,Seymour One,Shadows Into Light,Shadows Into Light Two,Shanti,Share,Share Tech,Share Tech Mono,Shojumaru,Short Stack,Siemreap,Sigmar One,Signika,Signika Negative,Simonetta,Sintony,Sirin Stencil,Six Caps,Skranji,Slabo 13px,Slabo 27px,Slackey,Smokum,Smythe,Sniglet,Snippet,Snowburst One,Sofadi One,Sofia,Sonsie One,Sorts Mill Goudy,Source Code Pro,Source Sans Pro,Source Serif Pro,Special Elite,Spicy Rice,Spinnaker,Spirax,Squada One,Sree Krushnadevaraya,Stalemate,Stalinist One,Stardos Stencil,Stint Ultra Condensed,Stint Ultra Expanded,Stoke,Strait,Sue Ellen Francisco,Sunshiney,Supermercado One,Suranna,Suravaram,Suwannaphum,Swanky and Moo Moo,Syncopate,Tangerine,Taprom,Tauri,Teko,Telex,Tenali Ramakrishna,Tenor Sans,Text Me One,The Girl Next Door,Tienne,Timmana,Tinos,Titan One,Titillium Web,Trade Winds,Trocchi,Trochut,Trykker,Tulpen One,Ubuntu,Ubuntu Condensed,Ubuntu Mono,Ultra,Uncial Antiqua,Underdog,Unica One,UnifrakturCook,UnifrakturMaguntia,Unkempt,Unlock,Unna,Vampiro One,Varela,Varela Round,Vast Shadow,Vesper Libre,Vibur,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,VT323,Waiting for the Sunrise,Wallpoet,Walter Turncoat,Warnes,Wellfleet,Wendy One,Wire One,Yanone Kaffeesatz,Yellowtail,Yeseva One,Yesteryear,Zeyada]

//type
type="tag"; //[container, tag, hollow]

//where to extrude
shiftit=0.3;

//scaling up
scale=1.7;

//Text height (extrusion) in mm
textheight=6;

//Set font size in mm
textsize=10;

//wall thickness
wall=1;

//text kerning - in font size
width_factor=.9;

//rim to combine the text
rim=0.5;

//close gaps e.g. to the dot of an i...
delta=0.5;

/* [Hidden] */
//-------------------------------------------------------------------------------
function is_monospaced() =
    (fontname=="Cutive Mono" || fontname=="Droid Sans Mono" ||fontname=="Fira Mono" ||fontname=="Nova Mono" ||fontname=="PT Mono" ||fontname=="Rubik Mono" ||fontname=="Share Tech Mono" ||fontname=="Ubuntu Mono" || fontname=="Oxygen Mono" )? 1 : 0;

echo (is_monospaced()) ;
width_big=textsize*width_factor;
width_small=textsize*width_factor*(is_monospaced()==1?1:.75);
width_small_half=width_small*(is_monospaced()==1?1:.5);
width_tiny=textsize*width_factor*(is_monospaced()==1?1:.25);
width_tiny_half=width_tiny*(is_monospaced()==1?1:.5);
echo (width_big, width_small, width_tiny);

function is_narrow_letter(c) =
    (c=="t" ||c=="f" || c=="l" || c=="i" || c=="j") ? 1 : 0;
function is_space(c) =
    (c=="." ||c==";" || c==" ") ? 1 : 0;
function width(c,d) =
    (is_narrow_letter(c)==1 && is_narrow_letter(d)==1) ? width_small_half :
    (is_narrow_letter(c)==1 || is_narrow_letter(d)==1) ? width_small :
    (is_space(c)==1 && is_space(d)==1) ? width_tiny_half :
    (is_space(c)==1 || is_space(d)==1) ? width_tiny :
    width_big;

function widthfunction(i) = width(textstring[i-1],textstring[i-2]);
function totalwidthfunction(x) = x < 1 ? 0 : widthfunction(x) + totalwidthfunction(x - 1);
//input = [1, 3, 5, 8];
width = [for (a = [ 1 : len(textstring) ]) totalwidthfunction(a) ];
echo(width); // ECHO: [1, 6, 15, 36]

dummy_cutoutlen=len(textstring)*textsize;

/*
Chamfered Text
Version 7, September 2018
Written by MC Geisler (mcgenki at gmail dot com)

A text for your customization...
Have fun!

License: Attribution 4.0 International (CC BY 4.0)

You are free to:
    Share - copy and redistribute the material in any medium or format
    Adapt - remix, transform, and build upon the material
    for any purpose, even commercially.
*/

module writetext(textstr, sizeit)
{
    text(textstr,size=sizeit*.9,font=fontname,halign="center",valign="baseline");
}

module RiseText(textstr, textsize,textheight)
{
    difference()
    {
        union()
        {
            for(i=[0:1:len(textstr)-1])
            {
                //extruded text
                translate([width[i],0,0])
                    linear_extrude(height=textheight,convexity = 32, scale = scale)
                        translate([0,-textsize*shiftit,0])
                            writetext(textstr[i], textsize);

                //backplane
                if(type=="tag")
                {
                    translate([width[i],0,textheight-0*wall])
                        linear_extrude(height=wall,convexity = 32)
                            scale([scale,scale,1])
                                translate([0,-textsize*shiftit,0])
                                    offset(delta=-delta)
                                        offset(delta=delta+rim)
                                            writetext(textstr[i], textsize);

                    translate([width[i],0,textheight-0*wall])
                        linear_extrude(height=wall,convexity = 32)
                                    scale([scale,scale,1])
                                       translate([0,-textsize*shiftit,0])
                                            offset(delta=rim)
                                                writetext(textstr[i], textsize);
                }
            }

        }

        if (type=="container")
        {
            translate([0,0,wall])
                for(i=[0:1:len(textstr)-1])
                {
                    translate([width[i],0,0])
                        linear_extrude(height=textheight,convexity = 32, scale = scale)
                            translate([0,-textsize*shiftit,0])
                                offset(delta=-wall)
                                    writetext(textstr[i], textsize);
                }
        }

        if (type=="hollow")
        scale([1,1,1])
            translate([0,0,-wall])
                for(i=[0:1:len(textstr)-1])
                {
                    translate([width[i],0,0])
                        linear_extrude(height=textheight,convexity = 32, scale = 0.3)
                            translate([0,-textsize*shiftit,0])
                               //offset(delta=-wall)
                                    writetext(textstr[i], textsize);
                }

    }
}

if (type=="tag" || type=="hollow")
{
   scale([1,1,-1])
        RiseText(textstring,textsize,textheight);
}
else
{
    RiseText(textstring,textsize,textheight);
}

```


[User]
I'm looking to create a parametric card box with a sliding lid, and I'd like it to be fully customizable. I want the box to hold standard playing cards, and I need to be able to specify the card dimensions, the number of stacks of cards the box should hold, and the wall thickness of the box. I'd also like to add text to both the lid and the bottom of the box, with control over the text string, size, font, and the text direction (horizontal or vertical). The lid should slide in and be held by friction using wedge-shaped channels, and I think it should look as close to a single, unified piece as possible - I'm thinking that mitered edges would help with that.

Ideally, I'd like to be able to generate just the top (lid), just the bottom, or both.

Could you generate the OpenSCAD files for this design? I don't have any existing code other than the above description, so I'll need the whole thing from scratch. I’d like to be able to modify the box with the Customizer, if possible.

[Assistant]
parametric_card_box_with_sliding_lid.scad:
```
/*********************************************************************
* Parametric Card Box with Sliding Lid
*
* This is a simple box to hold playing cards.  The box has a sliding
* lid that uses friction to hold fairly tight.  The shared edges of
* the parts are mitered.
*********************************************************************/

/*********************************************************************
* Parameters
*********************************************************************/

/* [Build] */
generateTop    = "true"; //[true, false]
generateBottom = "true"; //[true, false]

/* [Card] */
cardWidth   = 64; /* Dimension of the "draw" side of the card */
cardLength  = 89;
stackHeight = 17;

/* [Box] */
wallThickness  = 3;
numberOfStacks = 3;
margin         = 0.5; /* Defines shell of extra space around the card stack */

/* [Text] */

/* Direction of text */
textInWidthDirection = "true"; //[true, false]

/* Lid text parameters */
addLidText  = "true"; //[true, false]
lidText     = "ABC";
lidTextSize = 20; /* mm */
lidTextFont = "Times";

/* Bottom text parameters */
addBottomText  = "true"; //[true, false]
bottomText     = "ABC";
bottomTextSize = 20; /* mm */
bottomTextFont = "Times";

/* [Hidden] */
/*********************************************************************
* Intermediate Calculations
*********************************************************************/

/* Calculate the interior size for compartment to hold 1 card stack */
compartmentInteriorSize = [cardWidth,cardLength,stackHeight] + margin*[2,2,2];

/* Calculate the total outside size of the box */
outsideSize = [ numberOfStacks*compartmentInteriorSize[0] + (numberOfStacks+1)*wallThickness,  compartmentInteriorSize[1] + 2*wallThickness, compartmentInteriorSize[2] + 2*wallThickness];

/*********************************************************************
* Generate 3D Model
*********************************************************************/

translate([outsideSize[0],0,outsideSize[2]])
if (generateTop == "true") {
    rotate([0,180,0])
    top();
}

if (generateTop == "true" && generateBottom == "true") {
    if (outsideSize[0] < outsideSize[1]) {
    translate([outsideSize[0]+5,0,0])
    bottom();
    }
    else {
        translate([0,outsideSize[1]+5,0])
        bottom();
    }
}
else if (generateBottom == "true") {
    bottom();
}

/*********************************************************************
* Modules
*********************************************************************/

/* Sliding lid for the top of the box */
module top() {

    difference() {
        union() {
            /************************************
            * Vertical part of the sliding lid
            ************************************/

            translate([wallThickness,0,wallThickness])
            cube([outsideSize[0]-2*wallThickness, wallThickness, outsideSize[2]-wallThickness]);

            translate([wallThickness,0,wallThickness])
            rotate([0,90,0])
            partSharedEdge(outsideSize[0]-2*wallThickness);

            translate([wallThickness,0,wallThickness])
            rotate([0,0,90])
            partSharedEdge(outsideSize[2]-wallThickness);

            translate([outsideSize[0]-wallThickness,0,wallThickness])
            rotate([0,0,0])
            partSharedEdge(outsideSize[2]-wallThickness);

            translate([wallThickness,0,wallThickness])
            euler321Rotate([90,180,0])
            partSharedCorner();

            translate([outsideSize[0]-wallThickness,0,wallThickness])
            mirror([1,0,0])
            euler321Rotate([90,180,0])
            partSharedCorner();

            /************************************
            * Horizontal part of the sliding lid
            ************************************/

            translate([wallThickness,0,outsideSize[2]-wallThickness])
            cube([outsideSize[0]-2*wallThickness, outsideSize[1]-wallThickness, wallThickness]);

            translate([wallThickness,0,outsideSize[2]])
            rotate([270,90,0])
            partSharedEdge(outsideSize[1]-wallThickness);

            translate([outsideSize[0]-wallThickness,0,outsideSize[2]])
            mirror([1,0,0])
            rotate([270,90,0])
            partSharedEdge(outsideSize[1]-wallThickness);

            translate([wallThickness,outsideSize[1]-wallThickness,outsideSize[2]])
            rotate([0,90,0])
            partSharedEdge(outsideSize[0]-2*wallThickness);

            translate([outsideSize[0]-wallThickness,outsideSize[1]-wallThickness,outsideSize[2]])
            rotate([0,90,90])
            partSharedCorner();

            translate([wallThickness,outsideSize[1]-wallThickness,outsideSize[2]])
            mirror([1,0,0])
            rotate([0,90,90])
            partSharedCorner();

            /************************************
            * Channel lock for the sliding lid
            ************************************/

            translate([wallThickness,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
            rotate([90,-90,0])
            lidLock(outsideSize[1]-wallThickness);

            translate([outsideSize[0]-wallThickness,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
            rotate([90,0,0])
            lidLock(outsideSize[1]-wallThickness);

            translate([outsideSize[0]-wallThickness,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
            mirror([0,1,0])
            rotate([90,0,270])
            lidLock(outsideSize[0]-2*wallThickness);

            translate([wallThickness,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
            mirror([0,0,1])
            rotate([0,90,180])
            lidLockCorner();

            translate([outsideSize[0]-wallThickness,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
            mirror([1,0,0])
            mirror([0,0,1])
            rotate([0,90,180])
            lidLockCorner();
        }

        /************************************
        * Add text to the lid
        ************************************/

        if (addLidText == "true") {
            translate([0.5*outsideSize[0],0.5*outsideSize[1],outsideSize[2]-0.5])
            if (textInWidthDirection == "true") {
                writeText(lidText, lidTextSize, lidTextFont, 1.0);
            }
            else {
                rotate([0,0,270])
                writeText(lidText, lidTextSize, lidTextFont, 1.0);
            }
        }
    }
}

module bottom() {
    difference() {
        union() {
            translate([0, wallThickness, 0])
            cube([outsideSize[0], outsideSize[1]-wallThickness, outsideSize[2]-wallThickness]);

            translate([0,wallThickness,0])
            euler321Rotate([0,90,180])
            partSharedEdge(outsideSize[0]);

            translate([0,outsideSize[1],outsideSize[2]-wallThickness])
            euler321Rotate([0,90,180])
            partSharedEdge(outsideSize[0]);

            translate([0,wallThickness,0])
            rotate([0,0,-90])
            partSharedEdge(outsideSize[2]-wallThickness);

            translate([outsideSize[0],wallThickness,0])
            rotate([0,0,180])
            partSharedEdge(outsideSize[2]-wallThickness);

            translate([0,outsideSize[1],outsideSize[2]-wallThickness])
            rotate([90,0,0])
            partSharedEdge(outsideSize[1]-wallThickness);

            translate([outsideSize[0],outsideSize[1],outsideSize[2]-wallThickness])
            mirror([1,0,0])
            rotate([90,0,0])
            partSharedEdge(outsideSize[1]-wallThickness);

            translate([0,wallThickness,outsideSize[2]-wallThickness])
            partSharedCorner();

            translate([outsideSize[0],wallThickness,outsideSize[2]-wallThickness])
            mirror([1,0,0])
            partSharedCorner();
        }

        /* Remove channels for the lid lock to slide in to */
        /* Y direction, left side */
        translate([wallThickness,outsideSize[1]-wallThickness+0.001,outsideSize[2]-wallThickness])
        rotate([90,-90,0])
        lidLock(outsideSize[1]-wallThickness);
        /* Y direction, right side */
        translate([outsideSize[0]-wallThickness,outsideSize[1]-wallThickness+0.001,outsideSize[2]-wallThickness])
        rotate([90,0,0])
        lidLock(outsideSize[1]-wallThickness);
        /* X direction */
        translate([outsideSize[0]-wallThickness+0.001,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
        mirror([0,1,0])
        rotate([90,0,270])
        lidLock(outsideSize[0]-2*wallThickness+0.002);

        translate([wallThickness,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
        mirror([0,0,1])
        rotate([0,90,180])
        lidLockCorner();

        translate([outsideSize[0]-wallThickness,outsideSize[1]-wallThickness,outsideSize[2]-wallThickness])
        mirror([1,0,0])
        mirror([0,0,1])
        rotate([0,90,180])
        lidLockCorner();

        /* Remove space for interior card stack compartment */
        for (i = [1:numberOfStacks]) {
            xPosition = i*wallThickness + compartmentInteriorSize[0]*(i-1);
            cubePosition = [xPosition, wallThickness, wallThickness];
            translate(cubePosition-[0,1,0]) cube(compartmentInteriorSize + [0,1,wallThickness+1]);
        }

        /************************************
        * Add text to the bottom of the box
        ************************************/

        if (addBottomText == "true") {
            translate([0.5*outsideSize[0],0.5*outsideSize[1],-0.5])
            mirror([1,0,0])
            if (textInWidthDirection == "true") {
                writeText(bottomText, bottomTextSize, bottomTextFont, 1.0);
            }
            else {
                rotate([0,0,90])
                writeText(bottomText, bottomTextSize, bottomTextFont, 1.0);
            }
        }
    }
}

module lidLock(length = 1) {
    camfer(0.5*wallThickness, length);
}

module partSharedEdge(length = 1) {
    camfer(wallThickness, length);
}

module camfer(edgeWidth = 1, edgeLength = 1) {
    scale([edgeWidth, edgeWidth, edgeLength])
    linear_extrude(1) polygon([[0,0],[0,1],[1,0]]);
}

module partSharedCorner() {
    scale([wallThickness, wallThickness, wallThickness])
    openCornerCamfer();
}

module lidLockCorner() {
    scale([0.5*wallThickness, 0.5*wallThickness, 0.5*wallThickness])
    openCornerCamfer();
}

module openCornerCamfer() {
    intersection() {
        rotate([0,0,-90]) linear_extrude(1) polygon([[0,0],[0,1],[1,0]]);
        translate([0,0,0]) rotate([90,0,0]) linear_extrude(1) polygon([[0,0],[0,1],[1,0]]);
    }
}

module euler123Rotate(a = [0,0,0]) {
    rotate([   0,    0, a[2]])
    rotate([   0, a[1],    0])
    rotate([a[0],    0,    0])
    children();
}

module euler321Rotate(a = [0,0,0]) {
    rotate([a[2],    0,    0])
    rotate([   0, a[1],    0])
    rotate([   0,    0, a[0]])
    children();
}

module writeText(textString, textSize, textFont, textHeight)
{
    linear_extrude(textHeight)
    text(textString, textSize, textFont, halign = "center", valign = "center");
}
```


[User]
Hey, I'm looking to create a customizable rod clamp in OpenSCAD, and I've got a pretty clear idea of what I need. I want to be able to adjust a bunch of parameters to fit different rods and screw sizes.

Specifically, I need the clamp to be cylindrical overall, but with a hole for the rod, of course.  I want to control the diameter of the rod that the clamp will hold, as well as the outside diameter of the clamp itself and its height. Importantly, I also want to be able to specify a *different* diameter for the output side of the clamp, making it suitable for use as an adapter. And, while we're talking about rods, it would be great if I could specify either a round hole, or a hole with one or two flat faces on it -- and I'd need to be able to specify the size of those faces. I need that for both the input and output shaft holes.

On the clamp, I'd like to be able to choose how many screws to use for clamping, between zero and four, and also be able to specify the screw size as either M2, M2.5, M3, M3.5, M4, or M5. The design needs to take into account the screw diameter, the washer diameter, and the nut thickness so that the screws can be countersunk. Additionally, the length of the screws should be configurable. The screws should be equally spaced around the circumference and also spaced vertically based on the total height of the clamp.

Lastly, I need some options for how the clamp can open. The default should be a simple split along one side that allows for clamping. But also, I need an option to have a wider opening that allows the clamp to be slid on from the side (without having to put the rod through the hole). And I’d like an additional option where there's a wedge-shaped cut in the opening, as well as a final option to have a double-sided clamp (two pieces). For the double-sided option, I'd need a configurable gap between the two clamp halves.

I think that's about it. I need all this in one or more OpenSCAD files, and I'd like to be able to call these functions to create various clamps, so it would be great if the design was parametric. Thanks in advance for your help!

[Assistant]
Rod_Clamp.scad:
```
// --------------------------------+---------------------------------------
// Title:        Parametric Rod Clamp
// Version:      1.0.0
// Release Date: 2018-08-01 (ISO)
// Author:       Nathan Brown
// --------------------------------+---------------------------------------
//
// Description:
//
// - Parametric rod clamp with options
//
// - Clamp for variable sized rods with various options to make the clamp more functional.
//
//   Print Recomendations:
//   * Use a higher perimiter count such as four or six.
//   * Radial infill may allow higher clamping force.
//   * Increase the top or bottom layers for any surface that will be exposed to force.
//   * Have fun! (required)
//
//
//   Features
//   * Variable Shaft & Clamp Size
//   * Variable Screw Size & Count
//   * Slide-on Option
//
// Release Notes:
//
//
// - Version 1.0
//   * First release with all the basic features to make a clamp
//
//

////////////////////////////////////////////////////////////////
// Constants

// Used to hide constant from Thingiverse.
C_CONSTANT = 0 + 0;

//Clamping split size for shafts radius
C_CLAMP_SIZE_1 = 1.0 + C_CONSTANT;
C_CLAMP_SIZE_5 = 1.5 + C_CONSTANT;
C_CLAMP_SIZE_20 = 2.0 + C_CONSTANT;

//M2 -> M5 Metric Screw Properties (Hole Sizes and Washer Diameters
C_M2_SIZE = 2.1 + C_CONSTANT;
C_M2_WASHER_DIAMETER = 5 + C_CONSTANT;
C_M2_WASHER_THICK = 0.35 + C_CONSTANT;
C_M2_NUT_THICK = 1.6 + C_CONSTANT;

C_M2p5_SIZE = 2.65 + C_CONSTANT;
C_M2p5_WASHER_DIAMETER = 5 + C_CONSTANT;
C_M2p5_WASHER_THICK = 0.55 + C_CONSTANT;
C_M2p5_NUT_THICK = 2 + C_CONSTANT;

C_M3_SIZE = 3.15 + C_CONSTANT;
C_M3_WASHER_DIAMETER = 7 + C_CONSTANT;
C_M3_WASHER_THICK = 0.55 + C_CONSTANT;
C_M3_NUT_THICK = 2.4 + C_CONSTANT;

C_M3p5_SIZE = 3.7 + C_CONSTANT;
C_M3p5_WASHER_DIAMETER = 8 + C_CONSTANT;
C_M3p5_WASHER_THICK = 0.55 + C_CONSTANT;
C_M3p5_NUT_THICK = 2.8 + C_CONSTANT;

C_M4_SIZE = 4.2 + C_CONSTANT;
C_M4_WASHER_DIAMETER = 9 + C_CONSTANT;
C_M4_WASHER_THICK = 0.9 + C_CONSTANT;
C_M4_NUT_THICK = 3.2 + C_CONSTANT;

C_M5_SIZE = 5.25 + C_CONSTANT;
C_M5_WASHER_DIAMETER = 10 + C_CONSTANT;
C_M5_WASHER_THICK = 1.1 + C_CONSTANT;
C_M5_NUT_THICK = 4.7 + C_CONSTANT;

////////////////////////////////////////////////////////////////
// Thingiverse Parameters

/* [Options] */

//Diameter (mm) of the shaft.
shaft_diameter           = 8.0;

//Input Shaft Mode
shaft_mode              = 0;  // [ 0:Round, 1:Faced, 2:Double Faced ]

//Input Shaft Face Size (mm) (if faced)
shaft_face_size         = 1;

//Diameter (mm) of the output shoft
output_shaft_diameter    = 8.0;

//Output Shaft Mode
output_shaft_mode       = 0;  // [ 0:Round, 1:Faced, 2:Double Faced ]

//Output Shaft Face Size (mm) (if faced)
output_shaft_face_size   = 0;

//Outside Clamp Diameter (mm).  Mininum value of
clamp_diameter          = 30;

//Height of the clamp (mm)
clamp_height            = 20;

//Allows the clamp to be slid on from the side
clamp_slide_mode          = 0;  // [ 0:Off, 1:Slide On, 2:Slide with Wedge, 3:Double Sided ]

//Space between double sided clamp halves (only for Double Sided Clamp Style)
clamp_gap                 = 1;

//Number of clamping screws
screw_count            = 2;  // [ 0:None, 1:One, 2:Two, 3:Three, 4:Four ]

//Screw Size
screw_size             = 3;  // [ 2:M2, 2.5:M2.5, 3:M3, 3.5:M3.5, 4:M4, 5:M5 ]

//Screw Length (mm).  Actual length, will allow for washer(1) and nut(1).  Recommend at least Clamp Diameter * 0.5
screw_length            = 20;

//Model resolution.  Should be at least 64, but 96 will give you better circle.
resolution              = 96;

////////////////////////////////////////////////////////////////
// Module Variables

m_resolution = resolution;

m_clamp_radius = clamp_diameter / 2;
m_shaft_radius = shaft_diameter / 2;
m_shaft_radius_bottom = output_shaft_diameter / 2;

m_clamp_height = clamp_height;

m_clamp_gap = clamp_gap > m_shaft_radius ? m_shaft_radius : clamp_gap;

m_screw_size = screw_size + 0;

m_screw_radius = MX_ScrewHole(screw_size) / 2;

m_screw_length = screw_length < shaft_diameter ? shaft_diameter : (screw_length > clamp_diameter ? clamp_diameter : screw_length);

//Actual working length of the screw = Total Length - One Nut & One Washer
m_screw_working_length = m_screw_length - MX_ScrewWasherPlusNutHeight(screw_size);

//Countersing hole size
m_screw_washer_radius = MX_ScrewWasherHole(screw_size) / 2 + 0.1;

//Height of a single screw position
m_screw_height = (m_screw_washer_radius * 2) + 1;

//Offset from the center of the clamp for the screws
m_screw_x_offset = m_clamp_radius * 0.666666666;

//Screw count Calculation
m_screw_count = ((screw_count * m_screw_height) > m_clamp_height) ? floor(m_clamp_height / m_screw_height) : screw_count;

//Total Height of all Screws
m_screw_all_height = m_screw_height * m_screw_count;
m_screw_offset = (m_clamp_height - m_screw_all_height) / (m_screw_count + 1);

m_screw_starting_offset = (m_clamp_height * -0.5);

//Diameter to cut any when cutting through the entire clamp
m_cut_diameter = clamp_diameter + 1;

m_cut_wedge_extent = m_shaft_radius * 0.7 > m_clamp_radius ? m_clamp_radius - 1 : m_shaft_radius * 0.7;

////////////////////////////////////////////////////////////////
// Main

main();

// Main
module main() {

    //Set Resolution
    $fn = m_resolution;

    difference()
    {
        //Main Clamp Body
        clamp();

        screwHoles(m_screw_starting_offset, m_screw_x_offset);

        if(clamp_slide_mode == 3)
        {
            screwHoles(m_screw_starting_offset, -m_screw_x_offset);
        }

    };

    //Slide with Wedge
    if(clamp_slide_mode == 2){

        translate([m_clamp_radius + 2 , 0, 0]) {

            difference(){
                intersection() {
                    clampBody();

                    clampCutSlideWithBlock(clamp_height, m_cut_wedge_extent - 0.2, 0.2);
                };

                //Cut Screw Holes Normal Position
                screwHoles(m_screw_starting_offset, m_screw_x_offset);

                //Then cut a second set to allow for the piece to wedge in to clamp
                screwHoles(m_screw_starting_offset, m_screw_x_offset + 0.3);
            }
        };

    }

}

////////////////////////////////////////////////////////////////
// Modules

module clamp() {

    if(clamp_slide_mode == 0) {
        //No slide on - just clamp
        difference() {
            clampBody();

            //Cut out the slide on portion
            clampCutNormal();
        };

    } else if(clamp_slide_mode == 1 || clamp_slide_mode == 2) {
        //Slit for Clamping or Slide on Option

        difference() {
            clampBody();

            //Cut out the slide on portion
            clampCutSlideNormal();

            //Cut out the Insert Block
            if(clamp_slide_mode == 2) {
                clampCutSlideWithBlock(clamp_height + 1, m_cut_wedge_extent, 0);
            }
        }
    } else if(clamp_slide_mode == 3) {
      //Double Sided

        //First make one side
        difference() {
            clampBody();

            clampCutDoubleSided();
        };

        //Then make the second side
        intersection(){
            clampBody();

            translate([0, m_clamp_gap, 0]){
                clampCutDoubleSided();
            }

        }

    } else {
        //Normal Single Cut
        clampCutNormal();
    }

}

module clampBody(){

    //Main Clamp Body
    difference() {
        //Main
        cylinder(h = m_clamp_height, r = m_clamp_radius, center = true);

        //Center Hole - top
        shaft(m_shaft_radius, 0, shaft_mode, shaft_face_size, 0);
        //cylinder(h = m_clamp_height + 1, r = m_shaft_radius, center = true);

        //Center Hole - bottom
        shaft(m_shaft_radius_bottom, 1, output_shaft_mode, output_shaft_face_size, 0);
        ///cylinder(h = (m_clamp_height / 2) + 1, r = m_shaft_radius_bottom, center = false);

    };

}

/*
 @radius = shaft radius
 @bottom = 0 top, 1 bottom
 @mode = 0 round, 1 faced, 2 double faced, 3 keyed
 @fDepth = Feature Depth (face depth or Key height)
 @fWidth = Feature Width (key width)
*/
module shaft(radius, top, mode, fDepth, fWidth){

    holeHeight = (m_clamp_height / 2) + 1;

    translate([0, 0, (top * -1) * holeHeight]){
        difference() {
            //Center Hole - top
            cylinder(h = holeHeight, r = radius, center = false);

            if(mode == 1){
                //Single face
                linear_extrude(height = holeHeight, center = false) {
                    polygon(points = [
                        [-radius - fDepth,-radius], [-radius - fDepth, radius],
                        [-radius + fDepth, radius], [-radius + fDepth, -radius] ]);

                }

            }else if(mode == 2){
                //Double Face (90deg offset)
                linear_extrude(height = holeHeight, center = false) {
                    polygon(points = [
                        [-radius - fDepth,-radius], [-radius - fDepth, radius],
                        [-radius + fDepth, radius], [-radius + fDepth, -radius] ]);

                }

                linear_extrude(height = holeHeight, center = false) {
                    polygon(points = [
                        [-radius, -radius - fDepth], [radius, -radius - fDepth],
                        [radius, -radius + fDepth], [-radius, -radius + fDepth] ]);

                }
            }

        }
    }

}

//Cut for Normal Clamp
module clampCutNormal(){

    //Vary the clamping slit by the size of the shaft
    i_cut_extent = m_shaft_radius >= 20 ? C_CLAMP_SIZE_20 :
        (m_shaft_radius > 5.0 ? C_CLAMP_SIZE_5 : C_CLAMP_SIZE_1 );

    //Taper the center to allow clamping
    linear_extrude(height = m_clamp_height + 1, center = true) {
        polygon(points = [
            [0,0], [ m_clamp_radius, -i_cut_extent ], [ m_clamp_radius, i_cut_extent ] ]);

        polygon(points = [
            [m_clamp_radius, 0], [ 0, 0.5 ], [ 0, -0.5 ] ]);
    }

}

//Cut out Slide on Clamp
module clampCutSlideNormal() {

    //Regular Slide on without any clamping block
    linear_extrude(height = m_clamp_height + 1, center = true) {
        //Primary rectangle
        polygon(points = [
            [0, m_shaft_radius], [0, -m_shaft_radius],
            [m_cut_diameter, -m_shaft_radius ], [ m_cut_diameter, m_shaft_radius ]
            ]);

    }
}

//Cut out for Slide on with extra clamping block
module clampCutSlideWithBlock(extrudeHeight, wedgeExtent, startingX) {

    i_wedge_radius = m_clamp_radius + 1;

    linear_extrude(height = extrudeHeight, center = true) {

        //Angle cut for wedge
        polygon(points = [
            [startingX, m_shaft_radius + wedgeExtent], [startingX, -m_shaft_radius - wedgeExtent],
            [i_wedge_radius, -m_shaft_radius ], [ i_wedge_radius, m_shaft_radius ]
            ]);

    }

}

module clampCutDoubleSided(){

    //m_cut_diameter
    m_cut_height = m_clamp_height * 0.5 + 1;

    i_height_vert = m_clamp_height * 0.5 - 4;

     rotate([0,90,0]){
        linear_extrude(height = clamp_diameter + 1, center = true) {
            polygon(points = [
                [ m_cut_height, 0],
                [ m_cut_height - 4, 0 ],
                [ 0 + 2 , m_clamp_radius * 0.2 ] ,
                [ 0 - 2 , m_clamp_radius * -0.2],
                [ -m_cut_height + 4, 0 ],
                [ -m_cut_height, 0],
                [ -m_cut_height,  m_clamp_radius + 1],
                [ m_cut_height,  m_clamp_radius + 1]
                ]);
        };

    }

}

module screwHoles(screwStartingOffset, xOffset){

    for (i = [1 : m_screw_count])
    {

        //Cut Screw Holes & Countersink
        screwHole(xOffset, screwStartingOffset
            + (i - 1) * (m_screw_height)
            + (m_screw_height) * 0.5
            + i * m_screw_offset);

    }

}

module screwHole(offsetX, offsetZ){

    //X
    //m_clamp_radius * (2/3)

    //Screw Hole
    translate([offsetX, 0, offsetZ])
    {
        rotate([90, 0, 0]){
            cylinder(h = m_clamp_radius * 2 + 1, r = m_screw_radius, center = true);
        }
    }

    //Screw Countersink Hole
    screwHoleCountersink (offsetX, offsetZ);
    mirror([0, 1, 0])
    {
        screwHoleCountersink (offsetX, offsetZ);
    }

}

module screwHoleCountersink(offsetX, offsetZ){

    //Screw Countersink Hole
    translate([offsetX, m_clamp_radius / 2 +  m_screw_working_length * 0.5, offsetZ])
    {
        rotate([90, 0, 0]){
            cylinder(h = m_clamp_radius, r = m_screw_washer_radius, center = true);
        }
    }

};

////////////////////////////////////////////////////////////////
// Functions and other Lookups

function MX_ScrewHole(x) =
    x == 2 ? C_M2_SIZE
    : x == 2.5 ? C_M2p5_SIZE
    : x == 3 ? C_M3_SIZE
    : x == 3.5 ? C_M3p5_SIZE
    : x == 4 ? C_M4_SIZE
    : C_M5_SIZE;

function MX_ScrewWasherHole(x) =
    x == 2 ? C_M2_WASHER_DIAMETER
    : x == 2.5 ? C_M2p5_WASHER_DIAMETER
    : x == 3 ? C_M3_WASHER_DIAMETER
    : x == 3.5 ? C_M3p5_WASHER_DIAMETER
    : x == 4 ? C_M4_WASHER_DIAMETER
    : C_M5_WASHER_DIAMETER;

function MX_ScrewWasherPlusNutHeight(x) =
    x == 2 ? C_M2_WASHER_THICK + C_M2_NUT_THICK
    : x == 2.5 ? C_M2p5_WASHER_THICK + C_M2p5_NUT_THICK
    : x == 3 ? C_M3_WASHER_THICK + C_M3_NUT_THICK
    : x == 3.5 ? C_M3p5_WASHER_THICK + C_M3p5_NUT_THICK
    : x == 4 ? C_M4_WASHER_THICK + C_M4_NUT_THICK
    : C_M5_WASHER_THICK + C_M5_NUT_THICK;

```


[User]
I'd like a parametric OpenSCAD design for a glass cutting compass. I want to be able to adjust the dimensions of all the parts through parameters in the file. Here's what I'm thinking:

The compass needs a base or foot that will stick to the glass. This should be a circular part with a hole for a screw and a recess for a nut on the bottom. There also should be a small circular lip on the bottom to help glue it to the glass. The user needs to be able to specify the diameter and thickness of the foot, as well as the hole and screw diameters.

There's a slider that holds the glass cutter and moves along two rods. This slider will need to be able to hold a cutter of adjustable dimensions, with a through hole for the cutter blade and another hole for a roller. The cutter itself will be fixed using two screws, one to hold the cutter in place and the other to set the depth/height. The dimensions of these screw holes should be adjustable. The slider needs two through holes for the rods to pass through, and dimensions for those need to be configurable as well.

Finally, there is a carousel, which holds the rods and connects to the foot. This part has a central hole to fix it to the foot, and two side holes for the rods. The carousel also needs to be connected to the foot with a screw.

I would like the following parameters:

*   `cutter_depth`: Depth of the hole for the cutter head
*   `cutter_width`: Width of the hole for the cutter head
*   `cutter_roller_depth`: Depth of the hole for the roller of the cutter
*   `cutter_roller_width`: Width of the hole for the roller of the cutter
*   `cutter_clearence`: Cutter hole clearance
*   `compass_rods_diameter`: Diameter of main horizontal rods
*   `foot_thickness`: Thickness of the foot
*   `foot_screw_border`: Border distance of the screw on the foot
*   `foot_diameter`: Diameter of the foot
*   `foot_height`: Height of the foot
*   `foot_screw_diameter`: Diameter of the foot screw hole
*   `foot_nut_size`: Nut wrench size for the foot screw
*   `foot_nut_height`: Height of the foot nut recess
*   `carousel_wall`: Minimum wall thickness of the carousel
*  `slider_screw`: Size of cutter holding screws
*   `slider_nut_size`: Size of the nut wrench for the cutter holding nut
*   `slider_nut_height`: Height of cutter holding nut recess
*   `slider_wall`: Minimum wall thickness of the slider
*   `slider_thick_wall`: Minimum wall thickness for loaded walls of the slider
*   `hole_clearence`: Round hole clearance
* `layer_height`: Thickness of the print surface above the nut of the foot
*   `chamfer`: Size of the chamfer for edges

I'd also like the option to render each part separately ("print", "slider", "carousel", and "foot"), or render all of them together for printing purposes using a `part` variable which defaults to `print`.

I want to use the standard OpenSCAD library, so I don't need you to include anything.

[Assistant]
compass.scad:
```
part = "print"; // [print,slider,carousel,foot]

// Depth of the hole for the cutter head
cutter_depth=3.1;
// Width of the hole for the cutter head
cutter_width=20.3;
// Depth of the hole for the roller of the cutter
cutter_roller_depth=5;
// Width of the hole for the roller of the cutter
cutter_roller_width=16;
// Cutter hole clearence
cutter_clearence = 0.15;

// Diameter of main horizontal rods
compass_rods_diameter=5;

foot_thickness = 3;
foot_screw_border = 3;
foot_diameter = 35;
foot_height = 10;
foot_screw_diameter = 5;
// Nut wrench size
foot_nut_size = 8;
foot_nut_height = 5;

// Minimum wall thickness
carousel_wall=3;

// Cutter holding screws size
slider_screw = 4;
// Cutter holding nut wrench size
slider_nut_size = 7;
slider_nut_height = 3.2;
// Minimum wall thickness
slider_wall=3;
// Minimum wall thickness for loaded wall
slider_thick_wall=5;

// Round holes clearence
hole_clearence = 0.2;
// Thickness of printing helper surface above the nut of the foot
layer_height = 0.2;
$chamfer = 0.5;

/* [Hidden] */

$fn = 64;

axis_distance=cutter_width/2+cutter_clearence+slider_wall+hole_clearence+compass_rods_diameter/2;

module chamfered_cylinder(d=1,h=1,center=false)
{
    translate([0,0,center?0:h/2]) {
        hull() {
            cylinder(d=d,h=h-2*$chamfer,center=true);
            cylinder(d=d-2*$chamfer,h=h,center=true);
        }
    }
}

module chamfered_cube(size=[1,1,1],center=false)
{
    translate(center?[0,0,0]:size/2) {
        hull() {
            cube([size.x,size.y-2*$chamfer,size.z-2*$chamfer],center=true);
            cube([size.x-2*$chamfer,size.y-2*$chamfer,size.z],center=true);
            cube([size.x-2*$chamfer,size.y,size.z-2*$chamfer],center=true);
        }
    }
}

module reflect(v)
{
    children();
    mirror(v)
        children();
}

function nut_dia(nut_size) = 2*nut_size/sqrt(3);

module foot()
{
    difference() {
        union() {
            chamfered_cylinder(d=foot_diameter,h=foot_thickness);
            chamfered_cylinder(d=foot_screw_diameter+2*hole_clearence+2*foot_screw_border,h=foot_height);
            cylinder(d=foot_diameter-2*$chamfer,h=foot_height-$chamfer);
        }

        rotate_extrude()
            translate([foot_diameter/2-$chamfer,foot_height-$chamfer])
                resize([2*(foot_diameter/2-foot_screw_border-foot_screw_diameter/2-hole_clearence-$chamfer),2*(foot_height-foot_thickness-$chamfer)])
                    circle(r=foot_height-foot_thickness-$chamfer);

        translate([0,0,-0.001])
            cylinder(d=nut_dia(foot_nut_size)+2*hole_clearence,h=foot_nut_height,$fn=6);
        translate([0,0,foot_nut_height+layer_height])
            cylinder(d=foot_screw_diameter+2*hole_clearence,h=foot_height-foot_nut_height+layer_height+0.001);
    }
}

carousel_zsize=2*carousel_wall+compass_rods_diameter+2*hole_clearence;
carousel_xsize=2*carousel_wall+foot_screw_diameter+2*hole_clearence;
carousel_ysize=carousel_zsize+2*axis_distance;

module carousel()
{
    translate([0,0,carousel_zsize/2]) {
        difference() {
            hull() {
                reflect([0,1,0])
                    translate([0,axis_distance,0])
                        rotate([0,90,0])
                            chamfered_cylinder(d=carousel_zsize,h=carousel_xsize,center=true);
                translate([0,0,-carousel_zsize/4])
                    chamfered_cube([carousel_xsize,carousel_ysize,carousel_zsize/2],center=true);
            }

            cylinder(d=foot_screw_diameter+2*hole_clearence,h=carousel_zsize+0.002,center=true);
            reflect([0,1,0])
                translate([0,axis_distance,0])
                    rotate([0,90,0])
                        cylinder(d=compass_rods_diameter+2*hole_clearence,h=carousel_xsize+0.002,center=true);
        }
    }
}

slider_zsize=2*slider_wall+compass_rods_diameter+2*hole_clearence+slider_screw+2*hole_clearence;
slider_ysize=4*slider_wall+2*compass_rods_diameter+4*hole_clearence+cutter_width+2*cutter_clearence;
slider_xsize=2*slider_thick_wall+cutter_depth+cutter_roller_depth+2*cutter_clearence+slider_nut_height;

module slider()
{
    translate([0,0,slider_zsize/2]) {
        difference() {
            chamfered_cube([slider_xsize,slider_ysize,slider_zsize],center=true);

            // Cutter hole
            translate([(cutter_depth+2*cutter_clearence)/2-slider_xsize/2+slider_thick_wall+cutter_roller_depth,0,0])
                cube([cutter_depth+2*cutter_clearence,cutter_width+2*cutter_clearence,slider_zsize+0.002],center=true);
            // Cutter roller hole
            translate([(cutter_roller_depth+2*cutter_clearence)/2-slider_xsize/2+slider_thick_wall,0,0])
                cube([cutter_roller_depth+2*cutter_clearence,cutter_roller_width+2*cutter_clearence,slider_zsize+0.002],center=true);

            // X Clamp screw
            translate([slider_xsize/2+0.001,0,0])
                rotate([0,-90,0])
                    cylinder(d=slider_screw+2*hole_clearence,h=slider_nut_height+slider_thick_wall+0.002);
            // X clamp nut
            translate([slider_xsize/2-slider_thick_wall,0,0])
                rotate([0,-90,0])
                    rotate(30)
                        cylinder(d=nut_dia(slider_nut_size)+2*hole_clearence,h=slider_nut_height+0.001,$fn=6);

            // Clamp screw
            translate([(cutter_depth+2*cutter_clearence)/2-slider_xsize/2+slider_thick_wall+cutter_roller_depth,-slider_ysize/2-0.001,slider_zsize/2-slider_wall-hole_clearence-slider_screw/2])
                rotate([-90,0,0])
                    cylinder(d=slider_screw+2*hole_clearence,h=compass_rods_diameter+2*hole_clearence+2*slider_wall+0.002);
            // Clamp nut
            translate([(cutter_depth+2*cutter_clearence)/2-slider_xsize/2+slider_thick_wall+cutter_roller_depth,-slider_ysize/2+compass_rods_diameter+2*hole_clearence+2*slider_wall-slider_nut_height,slider_zsize/2-slider_wall-hole_clearence-slider_screw/2])
                rotate([-90,0,0])
                    cylinder(d=nut_dia(slider_nut_size)+2*hole_clearence,h=2*slider_nut_height+(cutter_width-cutter_roller_width)/2,$fn=6);

            // Axis
            reflect([0,1,0])
                translate([0,slider_ysize/2-slider_wall-hole_clearence-compass_rods_diameter/2,-slider_zsize/2+slider_wall+hole_clearence+compass_rods_diameter/2])
                    rotate([0,90,0])
                        cylinder(d=compass_rods_diameter+2*hole_clearence,h=slider_xsize+0.002,center=true);
        }
    }
}

module print()
{
    foot();
    translate([foot_diameter/2+3+slider_xsize/2,0,0])
        slider();
    translate([-foot_diameter/2-3-carousel_xsize/2,0,0])
        carousel();
}

if (part == "print")
    print();
else if (part == "slider")
    slider();
else if (part == "carousel")
    carousel();
else if (part == "foot")
    foot();

```


[User]
Hey, I'm trying to design a split nut for a hot tub heater element.  I have a design in mind, and I'd like to create it using OpenSCAD, but I need some help creating the code.

Basically, the idea is to have two halves that clamp onto the heater tube. It needs to have threads on the inside to screw onto the heater element tube, a section to clamp onto the heater itself, and then a way to secure the two halves together with a bolt/nut.

Here are the specific parameters I have in mind. I need them to be adjustable using variables so I can use the design for other heaters as well.

*   **Heater Dimensions:**
    *   `heater_id`: Inner diameter of the heater tube (e.g., 58mm)
    *   `heater_od`: Outer diameter of the heater tube (e.g., 85mm)
    *   `heater_length`: Length of the heater tube section to grip (e.g., 10mm)

*   **Thread Dimensions:**
    *   `thread_id`: Inner diameter of the threads (e.g., 79mm). This will screw onto the heater tube.
    *   `thread_od`: Outer diameter of the threaded section (e.g., 85mm)
    *   `thread_length`: Length of the threaded section (e.g., 25mm)
    *   `thread_pitch`: Pitch of the threads (e.g., 15.3/5 mm, assuming it's a metric thread)

*   **Connector Dimensions (for clamping the two halves):**
    *  `connector_dia`: Diameter of the cylindrical section of the connector (e.g., 15mm)
    * `screw_dia`: Diameter of the screw hole (e.g. 6.5mm)
    *   `connector_length`: Length of the connector (e.g., 15mm)
    *   `nut_dia`: Diameter of the nut cavity (e.g., 13mm)
    *   `nut_thickness`: Thickness of the nut cavity (e.g., 3mm)

I'd like the design to create a `left_half()` and a `right_half()` module that each create half of the split nut. These modules should each call a `complete_part()` module which does all the complex work. The `complete_part()` module itself will call `thread_part()`, `heater_part()`, and two instances of the `connector_part()` modules to create the main body. Finally the `thread_part()` needs to call functions/modules from the included `simplethreads.scad` library. The threads should be internal threads, obviously, to screw onto the heater element. I need it to be easy to modify the threads, so the library code needs to be used.

Can you generate the OpenSCAD code for this?

[Assistant]
simplethreads.scad:
```
// Metric Screw Thread Library
// by Maximilian Karl <karlma@in.tum.de> (2014)
// modified the api to create internal threads, smooth the start and
// stop threads, and use imperial units:
//
//
//
// use module thread_imperial(pitch, majorD, stepAngle, numRotations, tol, internal)
//         or thread(pitch, majorD, stepAngle, numRotations, tol, internal)
// with the parameters:
// pitch        - screw thread pitch
// majorD       - screw thread major diameter
// step         - step size in degrees (36 gives ten steps per rotation)
// numRotations - the number of full rotations of the thread
// tol          - (optional parameter for internal threads) the amount to increase the
//                 thread size in mm.Default is 0
// internal     - (optional parameter for internal threads) - this can be set true or
//                false.  Default is true
//
// Performance improvements by Volker Schuller

root3 = sqrt(3);

//-----------------------------------------------------------------------------------------------
//EXAMPLES:
//example: this creates a 3/8-inch 16TPI bolt using metric units
//thread(1.5875, 9.5250, 12, 12);

//the same thread using imperial units
//thread_imperial(1/16,3/8,12,12);

//an internal thread that will accomodate the two examples above
//translate([15, 0, 0]) {
//    difference() {
//        cylinder(r = 8, h = 8);
//        translate([0, 0, -1.5875 / 2])
//        thread(1.5875, 9.5250, 12, 8, 0.2, true);
//    }
//}
//------------------------------------------------------------------------------------------------

//Creates a thread cross section starting with an equilateral triangle
//and removing the point.  Internal threads (for creating nuts) will
//have a non-zero tolerance value which enlarges the triangle to
//accomodate a bolt of the same size
function screwthread_triangle(P, tol) =
let (a = P + 2 * tol,
    h = root3 * a / 2,
    y = P / 16 + tol / root3,
    x1 = root3 * P / 16 - P * h / a + tol * 2,
    x2 = root3 / 16 * P - h + tol * 2)
        [[tol, -y], [tol, y], [x1, P / 2], [x2, P / 2], [x2, -P / 2 + 0.00001], [x1, -P / 2 + 0.00001]];

//Creates a polyhedron by placing the triangle points along the thread and connecting them with faces
module threadPolyhedron(P, D_maj, step, rotations, tol) {
    t = screwthread_triangle(P, tol);
    n = floor(360 / step) * rotations;
    points = [for (i = [0: step: 360 * rotations], p = t)
        let (d = p[0] + D_maj / 2)
        [d * cos(i), d * sin(i), p[1] + i / 360 * P]
    ];
    faces = concat([[0, 5, 4], [1, 3, 2], [0, 3, 1], [0, 4, 3],
        [6 * n, 6 * n + 4, 6 * n + 5],[6 * n + 1, 6 * n + 2, 6 * n + 3],
        [6 * n, 6 * n + 1, 6 * n + 3],[6 * n, 6 * n + 3, 6 * n + 4]],
        [for (i = [0: n - 1], j = [0: 5])[i * 6 + j, i * 6 + 1 + j, i * 6 + 6 + j]],
        [for (i = [0: n - 1], j = [0: 4])[i * 6 + 1 + j, i * 6 + 7 + j, i * 6 + 6 + j]],
        [for (i = [0: n - 1])[i * 6 + 6, i * 6 + 5, i * 6]]);
    polyhedron(points = points, faces = faces);
}

//creates a thread using inches as units (tol is still in mm)
module thread_imperial(pitch, majorD, stepAngle, numRotations, tol = 0, internal = false) {
    p = pitch * 25.4;
    d = majorD * 25.4;
    thread(p, d, stepAngle, numRotations, tol);
}

//creates a thread using mm as units
module thread(P, D, step, rotations, tol = 0, internal = false) {
    // added parameter "rotations"
    // as proposed by user bluecamel
    D_min = D - 5 * root3 / 8 * P;
    if (internal == false) {
        difference() {
            threadPolyhedron(P, D, step, rotations, 0);

            //The first and last portion of external threads (for making bolts)
            //are tapered at a 20 degree angle for an easier fit
            translate([D / 2 - P / 2, 0, -P / 2])
            rotate(-20, [0, 0, 1]) translate([0, -P * 5, 0])
            cube([P, P * 10, P]);

            translate([D / 2 - P / 2, 0, (rotations - 0.5) * P])
            rotate(20, [0, 0, 1]) translate([0, -P * 5, 0])
            cube([P, P * 10, P]);
        }
    } else {
        threadPolyhedron(P, D + tol, step, rotations, tol);
    }
    //make the cylinder a little larger if this is to be an internal thread
    translate([0, 0, (rotations * 0.5) * P])
    if (internal == false) {
        cylinder(r = D_min / 2, h = (rotations + 1) * P, $fn = 360 / step, center = true);
    } else {
        cylinder(r = D_min / 2 + tol, h = (rotations + 1) * P, $fn = 360 / step, center = true);
    }
}
```
spa_heater_split_nut.scad:
```
heater_id=58;
heater_od=85;
heater_length=10;
thread_id=79;
thread_od=85;
thread_length=25;
thread_pitch=15.3/5;
connector_dia=15;
screw_dia=6.5;
connector_length=15;
nut_dia=13;
nut_thickness=3;
$fn=96;

translate([-5,0,0])
left_half();

translate([5,0,0])
right_half();

module thread_part(){
include <simplethreads.scad>;
difference(){
cylinder(d=thread_od,h=thread_length);
thread(thread_pitch,thread_id,12,8,0,true);
}
}

module heater_part(){
difference(){
cylinder(d=heater_od,h=heater_length);
cylinder(d=heater_id,h=heater_length);
}
}

module connector_part(){
difference(){
union(){
cylinder(d=connector_dia,h=connector_length);
translate([-connector_dia/2,-connector_dia/2,0])
cube([connector_dia/2,connector_dia,connector_length]);
}
cylinder(d=screw_dia,h=connector_length);
rotate([0,0,360/12])
cylinder(d=nut_dia,h=nut_thickness,$fn=6);
}
}

module complete_part(){
thread_part();
translate([0,0,thread_length])
heater_part();

translate([0,0,(heater_length+thread_length)/2])
rotate([90,0,0])
translate([thread_od/2+connector_dia/2-2,0,-connector_length/2])
connector_part();

rotate([0,0,180])
translate([0,0,(heater_length+thread_length)/2])
rotate([270,0,0])
translate([thread_od/2+connector_dia/2-2,0,-connector_length/2])
connector_part();
}

module left_half(){
difference(){
rotate([0,0,90])
complete_part();
translate([0,-200,-1])
cube([200,400,200]);
}
}

module right_half(){
difference(){
rotate([0,0,90])
complete_part();
translate([-200,-200,-1])
cube([200,400,200]);
}
}
```