[User]
Okay, I'd like to create a customized stick bowl design, but much larger and more complex than the basic one I've seen. I'd like to be able to control a lot of parameters, so I need a parametric OpenSCAD file that can handle it.

Here's what I'm thinking for features and parameters:

**Basic Structure:**

*   **Stick Shape:** I want to be able to choose the basic form of the sticks used to construct the bowl, with options for `beam`, `cylinder`, `hexagon`, and `triangle`.
*   **Stick Size:**  A parameter called `size` to control the dimensions of the basic stick cross section. I want to specify a single value that changes the radius if cylinder, or side length if hexagon or triangle, or width and depth if beam.
*   **Stick Scaling:** A parameter `scale_second_dir` that allows scaling of sticks in the second dimension, perpendicular to its length. This allows for things like flattened sticks (scale < 1) or thicker sticks (scale > 1).
*   **Dual Extrusion Control:** I want to be able to use the design for dual extrusion, with two distinct parts that can be different materials or colors.
    *   A parameter `dual_pat` to switch between having the two parts be alternating "rings" or alternating even/odd "sticks" within a ring. The options would be `ring` and `even_odd`.
    *   A `part` parameter with options for `both`, `bothorig`, `first`, and `second`. `first` and `second` control which of the two extrusion parts is displayed or rendered. `both` will display them together, with the first part scaled by `scalefirst` and `second` not scaled. The `bothorig` should display both parts without the scaling.
    *   A parameter `scalefirst` to fine-tune the scale of the first dual extrusion part.
    *   A `showcolor` parameter (with `yes` or `no`) that controls whether the different parts of dual extrusion are shown in different colors. This is just for preview, I would set to `no` before generating an STL file.

*   **Starting Radius:** A parameter `r0` to control the starting radius at the bottom.
*   **Number of Sticks:** A parameter `nrsticks` which determines how many sticks are used per ring, and thus the spacing between them.  The `grad_per_stick` value can be derived from `360/nrsticks`, but could be useful as an intermediate for other purposes.

**Bowl/Base:**

*   **Bottom:** A parameter `add_bottom` that can be "yes" or "no", controlling whether a base is added to the bowl.
*   **Bottom Height:** If `add_bottom` is set to "yes", a parameter `bottom_height` will set the height of the base cylinder.

**Rings:**

*   I want to be able to define up to 12 rings, each with their own parameters. For each ring (numbered 01 through 12):
    *   A parameter to set the radius `[ring number]_radius` which if `0` means this ring is not generated.
    *   A parameter to set the height `[ring number]_height`.
    *   A parameter to set the stick length extension at each end of each stick on that ring `[ring number]_extend`. This should make the sticks extend beyond the radius of the ring itself, and should influence how it connects to the next ring.
    *   A parameter to skew or rotate each ring `[ring number]_skew`. This rotates the whole ring around the z-axis.

**General Constraints:**

*   **Maximum Height:** A parameter `max_height` to define the maximum height of the generated object. The object should be clipped at this height.
*   **Maximum Width:** A parameter `max_width` to define the maximum width of the generated object. The object should be clipped at this width.

**Libraries:**

*   I'll need the `MCAD/regular_shapes.scad` library for the `hexagon` and `triangle` shapes, as well as the `utils/build_plate.scad` to display build plate for preview.

I think that covers it. Let me know if you have any questions!

[Assistant]
stickbowlXL_v03.scad:
```
// Licence: Creative Commons, Attribution
// Created: 16-02-2013 by bmcage http://www.thingiverse.com/bmcage

// An attempt at customizable math art: Stick Bowl

//Make it a bowl or not
add_bottom = "no";  //[yes, no]
//If a bowl, size of the bottom
bottom_height = 2; //[1:20]

//Form of the sticks to use
base_form = "beam";  // [beam, cylinder, hexagon, triangle]
//Size of the sticks
size = 4; //[1:20]
//Scaling in second direction of sticks
scale_second_dir = 1; // [0.1, 0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,2,3,4,5]

//dualstrusion pattern. Every ring another color, or even/odd coloring in a ring
dual_pat = "even_odd";  // [ring, even_odd]

//what part to show. Both, first dualstusion, or second dualstrusion, and finally bothorig which drops the scalefirst parameter.
part = "both";     // [both, bothorig, first, second]

//Paremeter to beat openscad dualstrusion generation into submission. In essense, part 'first' is scaled with this value, and 'second' part not. This removes the problem of differences of parts which share surfaces. If you did not understand this, don't worry, just leave it alone :-)
scalefirst = 1.005; //
//showcolor when both parts are shown. This gives a nice view of the final result, but requires more computing. Set to no before producing an stl!
showcolor = "no";   // [yes, no]
//start radius
r0 = 5; //[2:200]

// number of sticks per ring
nrsticks = 18; //[4:50]
grad_per_stick = 360 / nrsticks;

// radius of the first ring
01_radius = 50; // [0:200]
// height if the first ring
01_height = 0;  // [0:300]
// extend beyond radius of first sticks
01_extend = 1;  //[0:100]
//how skewed (rotated) first ring is in degrees
01_skew = 0;    //[-170:170]

// radius of the second ring (0 for no ring)
02_radius = 50; // [0:200]
// height if the second ring
02_height = 120;  // [0:300]
// extend beyond radius of second sticks
02_extend = 1;  //[0:100]
//how skewed (rotated) second ring is in degrees
02_skew = 0;    //[-170:170]

// radius of the third ring (0 for no ring)
03_radius = 60; // [0:200]
// height if the third ring
03_height = 40;  // [0:300]
// extend beyond radius of third sticks
03_extend = 1;  //[0:100]
//how skewed (rotated) third ring is in degrees
03_skew = 0;    //[-170:170]

// radius of the fourth ring (0 for no ring)
04_radius = 30; // [0:200]
// height if the fourth ring
04_height = 1;  // [0:300]
// extend beyond radius of fourth sticks
04_extend = 1;  //[0:100]
//how skewed (rotated) first ring is in degrees
04_skew = 0;    //[-170:170]

// radius of the fifth ring (0 for no ring)
05_radius = 0; // [0:200]
// height if the fifth ring
05_height = 20;  // [0:300]
// extend beyond radius of fifth sticks
05_extend = 2;  //[0:100]
//how skewed (rotated) fifth ring is in degrees
05_skew = -27;    //[-170:170]

// radius of the sixth ring (0 for no ring)
06_radius = 0; // [0:200]
// height if the sixth ring
06_height = 40;  // [0:300]
// extend beyond radius of sixth sticks
06_extend = 5;  //[0:100]
//how skewed (rotated) sixth ring is in degrees
06_skew = 90;    //[-170:170]

// radius of the 7th ring (0 for no ring)
07_radius = 0; // [0:200]
// height if the 7th ring
07_height = 40;  // [0:300]
// extend beyond radius of 7th sticks
07_extend = 5;  //[0:100]
//how skewed (rotated) 7th ring is in degrees
07_skew = 90;    //[-170:170]

// radius of the 8th ring (0 for no ring)
08_radius = 0; // [0:200]
// height if the 8th ring
08_height = 40;  // [0:300]
// extend beyond radius of 8th sticks
08_extend = 5;  //[0:100]
//how skewed (rotated) 8th ring is in degrees
08_skew = 90;    //[-170:170]

// radius of the 9th ring (0 for no ring)
09_radius = 0; // [0:200]
// height if the 9th ring
09_height = 40;  // [0:300]
// extend beyond radius of 9th sticks
09_extend = 5;  //[0:100]
//how skewed (rotated) 9th ring is in degrees
09_skew = 90;    //[-170:170]

// radius of the 10th ring (0 for no ring)
10_radius = 0; // [0:200]
// height if the 10th ring
10_height = 40;  // [0:300]
// extend beyond radius of 10th sticks
10_extend = 5;  //[0:100]
//how skewed (rotated) 10th ring is in degrees
10_skew = 90;    //[-170:170]

// radius of the 11th ring (0 for no ring)
11_radius = 0; // [0:200]
// height if the 11th ring
11_height = 40;  // [0:300]
// extend beyond radius of 11th sticks
11_extend = 5;  //[0:100]
//how skewed (rotated) 11th ring is in degrees
11_skew = 90;    //[-170:170]

// radius of the 12th ring (0 for no ring)
12_radius = 0; // [0:200]
// height if the 12th ring
12_height = 40;  // [0:300]
// extend beyond radius of 12th sticks
12_extend = 5;  //[0:100]
//how skewed (rotated) 12th ring is in degrees
12_skew = 90;    //[-170:170]

//maximum height of the object (object cut there)
max_height = 150; // [100:400]
//maximum width of the object (object cut there)
max_width = 145;  // [100:400]

use <MCAD/regular_shapes.scad>;
use <utils/build_plate.scad>;

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

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

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

build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

bottomstart = -bottom_height; //-first_extend*first_radius/sqrt(pow(first_height,2)+pow(first_radius,2));

module baseform(length){
  scale([1,scale_second_dir,1]){
  if (base_form == "beam") {
    translate([-size/2, -size/2, 0]) cube(size=[size, size, length]);
  }
  if (base_form == "cylinder") {
    cylinder(r=size, h=length, $fn=20);
  }
  if (base_form == "hexagon") {
    linear_extrude(height=length) hexagon(size);
  }
  if (base_form == "triangle") {
    linear_extrude(height=length) triangle(size);
  }
  }
}

module ringbaseform(r1, h1, skew1, r2, h2, skew2, extend){
  // a form with correct length if first ring is at r1, h1, and
  // second at r2, h2, and extend is extend
  //
  // P = (r1, 0, h1), Q = (r2 cos(skew2), r2 sin(skew2), h2)
  // dir_new = (P-Q) / norm(P-Q) = (a,b,c)
  // rotate to new direction, euler angles
  // th_x = atan2(b,c); th_y = atan(-a, sqrt(b*b+c*c)) ; th_z = 0
  rotate([0,0,skew1])
  translate([r1, 0, h1])
  help_ringbaseform(r2*cos(-skew2)-r1, r2*sin(-skew2), h2-h1, extend);
}

module help_ringbaseform(a, b, c, extend)
{
  rotate([atan2(b/sqrt(pow(a, 2) + pow(b, 2) + pow(c, 2)),
                c/sqrt(pow(a, 2) + pow(b, 2) + pow(c, 2))), 0, 0])
  rotate([0, atan2(a/sqrt(pow(a, 2) + pow(b, 2) + pow(c, 2)),
     sqrt(b*b/(pow(a, 2) + pow(b, 2) + pow(c, 2))
          +c*c/(pow(a, 2) + pow(b, 2) + pow(c, 2)))), 0])
  translate([0,0,-extend])
  baseform(sqrt(pow(a, 2) + pow(b, 2)
                    + pow(c, 2)) + 2*extend);
}

module ring(ringnr, curpart){
if (curpart == "both" || (dual_pat == "ring" && curpart == "first" && ringnr%2 == 0)
   ||  (dual_pat == "ring" && curpart == "second" && ringnr%2 == 1)
   ||  (dual_pat == "even_odd"))
{
  union() {
    for (i = [0:nrsticks]) {
     if (curpart == "both" || dual_pat == "ring" ||
         (dual_pat == "even_odd" && curpart == "first" && i%2 == 0) ||
         (dual_pat == "even_odd" && curpart == "second" && i%2 == 1)
        ) {
      rotate([0,0,i*grad_per_stick]) {
        if (ringnr == 1) {
          ringbaseform(r0, 0, 0, 01_radius, 01_height,
                       01_skew,  01_extend);}
        if (ringnr == 2){
          ringbaseform(01_radius, 01_height, 01_skew,
                       02_radius, 02_height, 02_skew,
							  02_extend);}
        if (ringnr == 3)
          ringbaseform(02_radius, 02_height,
                       01_skew + 02_skew,
                       03_radius, 03_height, 03_skew,
							  03_extend);
        if (ringnr == 4)
          ringbaseform(03_radius, 03_height,
							  01_skew + 02_skew + 03_skew,
                       04_radius, 04_height, 04_skew,
							  04_extend);
        if (ringnr == 5)
          ringbaseform(04_radius, 04_height,
  01_skew + 02_skew + 03_skew + 04_skew,
                       05_radius, 05_height, 05_skew,
							  05_extend);
        if (ringnr == 6)
          ringbaseform(05_radius, 05_height,
  01_skew + 02_skew + 03_skew + 04_skew + 05_skew,
                       06_radius, 06_height, 06_skew,
							  06_extend);
        if (ringnr == 7)
          ringbaseform(06_radius, 06_height,
  01_skew + 02_skew + 03_skew + 04_skew + 05_skew + 06_skew,
                       07_radius, 07_height, 07_skew,
							  07_extend);
        if (ringnr == 8)
          ringbaseform(07_radius, 07_height,
  01_skew + 02_skew + 03_skew + 04_skew + 05_skew + 06_skew + 07_skew,
                       08_radius, 08_height, 08_skew,
							  08_extend);
        if (ringnr == 9)
          ringbaseform(08_radius, 08_height,
  01_skew + 02_skew + 03_skew + 04_skew + 05_skew + 06_skew + 07_skew + 08_skew,
                       09_radius, 09_height, 09_skew,
							  09_extend);
        if (ringnr == 10)
          ringbaseform(09_radius, 09_height,
  01_skew + 02_skew + 03_skew + 04_skew + 05_skew + 06_skew + 07_skew + 08_skew + 09_skew,
                       10_radius, 10_height, 10_skew,
							  10_extend);
        if (ringnr == 11)
          ringbaseform(10_radius, 10_height,
  01_skew + 02_skew + 03_skew + 04_skew + 05_skew + 06_skew + 07_skew + 08_skew + 09_skew + 10_skew,
                       11_radius, 11_height, 11_skew,
							  11_extend);
        if (ringnr == 12)
          ringbaseform(11_radius, 11_height,
  01_skew + 02_skew + 03_skew + 04_skew + 05_skew + 06_skew + 07_skew + 08_skew + 09_skew + 10_skew + 11_skew,
                       12_radius, 12_height, 12_skew,
							  12_extend);
       }
      }
    }
  }
}
}

module bottom(){
  if (add_bottom == "yes") {
    translate([0,0,bottomstart])
      cylinder(r = r0+size, h=bottom_height);
  }
}

module bowl(curpart){
union() {
  if (curpart == "both" || curpart == "first") {
    bottom();
    }
  ring(1, curpart);
  if (02_radius > 0) {
    ring(2, curpart);}
  if (03_radius > 0) {
    ring(3, curpart);}
  if (04_radius > 0) {
    ring(4, curpart);}
  if (05_radius > 0) {
    ring(5, curpart);}
  if (06_radius > 0) {
    ring(6, curpart);}
  if (07_radius > 0) {
    ring(7, curpart);}
  if (08_radius > 0) {
    ring(8, curpart);}
  if (09_radius > 0) {
    ring(9, curpart);}
  if (10_radius > 0) {
    ring(10, curpart);}
  if (11_radius > 0) {
    ring(11, curpart);}
  if (12_radius > 0) {
    ring(12, curpart);}
}
}

intersection(){
  translate([0,0,-bottomstart]) {
    if (part == "bothorig") {
      if (showcolor == "no") {
        bowl(part);
        } else {
        color("DarkSalmon")
        difference() {
          bowl("second");
          bowl("first");
          }
        color("Orange")
        bowl("first");
        }
      }
    if (part == "both") {
      if (showcolor == "no") {
        //bowl(both);
          scale([scalefirst,scalefirst,scalefirst]) bowl("first");
          bowl("second");
        } else {
        color("DarkSalmon")
        difference() {
          bowl("second");
          scale([scalefirst,scalefirst,scalefirst]) bowl("first");
          }
        color("Orange")
        scale([scalefirst,scalefirst,scalefirst]) bowl("first");
        }
      }
    if (part == "first") {
      color("Orange")
      scale([scalefirst,scalefirst,scalefirst]) bowl(part);
      }
    if (part == "second") {
      color("DarkSalmon")
      difference() {
        bowl("second");
        render()
        scale([scalefirst,scalefirst,scalefirst]) bowl("first");
        }
      }
    }
  translate([-max_width/2,-max_width/2,0])
    cube(size=[max_width,max_width,max_height]);
}

```


[User]
Okay, I'm looking to create a customizable test print for evaluating overhang performance. I've seen some existing designs, but I need something a bit more flexible.

Essentially, I want to generate a series of angled overhangs, either supported on both sides (bridged) or unsupported (free floating), or both. I need to be able to control the following parameters:

*   **Test Type:** I need to be able to choose whether the test consists of "bridged" overhangs, "floating" overhangs, or "both". If I choose "both", I want a mirrored print, one side bridged, one side floating.
*   **Start Angle:**  The angle of the first overhang in the series, relative to the horizontal plane.
*   **End Angle:** The angle of the last overhang in the series.
*   **Angle Increment:** The angular difference between each consecutive overhang in the test series.
*   **Overhang Width:**  The width of each individual overhang.
*   **Overhang Height:** The height of each individual overhang.
*   **Font Size Percentage:** The size of the label numbers as a percentage of the width.
*   **Spine Width:**  The width of the spine that supports the overhangs. This is in the y-direction when viewing the model in a default view in OpenSCAD.

The design should consist of a spine, onto which the angled overhangs are printed, incrementing from the start angle to the end angle. For bridged overhangs, there will be a support beam across each of the angles. The overhangs themselves should be triangular, with the angled side providing the overhang being tested. I'd like to also be able to print the test with angle labels. I'd like the font to be 'orbitron.dxf' which should be a file within the Write.scad library. If the label placement is set to offset, the numbers should be placed on the overhang itself otherwise, on the spine.

Can you write me a parametric OpenSCAD file that does this? I'd prefer to keep the parametric definitions at the top of the file, the modules separate, and the main module that handles the parametric values and calls the other modules to build the test to be at the bottom of the file. The label generation should use the `Write.scad` library. I want it to import the `Write.scad` library using `use <write/Write.scad>`.

[Assistant]
overhang_test_print_v1-0.scad:
```
use <write/Write.scad>

// style of test print
_type = "both"; //[bridged, floating, both]

// start angle
_start = 15;

// end angle
_end = 45;

// angle increment between samples
_increment = 5;

// sample width
_width = 7.0; // [1:20]

// height
_height = 6.0; // [1:20]

_fontSizePercent = 65; // [0:100]

_spineWidth = 2; // [0:10]

function getLength(height,angle) = height/tan(angle);

module overhangTest(bridged, start, end, increment, width, height, spine, gap, fontSize, showLabels = true, offsetLabels = true) {
	num = (end-start)/increment + 1;
	length = width * num + gap * (num+1);
	fontFraction = fontSize/100;
	fontDepth = 0.5;
	step = width+gap;
	eps = 0.1;

	difference() {
		union() {
			translate([spine/2, -length/2, height/2]) {
				cube(size=[spine+eps,length,height], center=true);
			}

			for (angle = [start:increment:end]) {
				assign(index = (angle-start)/increment + 1) {
					translate([spine,-(step*index - width/2),0])
					rotate([90,0,0])
					linear_extrude(height = bridged ? width+eps : width, center = true, convexity = 2) {
						polygon(points=[[0,0],[getLength(height,angle),height],[0,height]], paths=[[0,1,2]]);
					}
				}
			}

			// beams
			if (bridged) {
				for (index = [0:num]) {
					translate([0,-step*(index) - gap,0]) {
						cube(size=[getLength(height,start + increment * max(0,(index-1))) + spine, gap, height]);
					}
				}
			}
		}

		// labels
		if (showLabels) {
			for (angle = [start:increment:end]) {
				assign(y=step * ((angle-start)/increment +1)) {
					translate([offsetLabels ? width/4 : 0,-(y - width/2 + width * fontFraction/2),height-fontDepth]) {
						write(str(angle), t=fontDepth+eps, h=width*fontFraction, font="write/orbitron.dxf", space=1.2);
					}
				}
			}
		}
	}
}

module make(type, start, end, increment, width, height, fontSize, spineWidth) {
	gap = 1.0;

	if (_type == "floating") {
		overhangTest(false, start, end, increment, width, height, spineWidth, gap, fontSize);
	} else if (_type == "bridged") {
		overhangTest(true, start, end, increment, width, height, spineWidth, gap, fontSize);
	} else {
		overhangTest(true, start, end, increment, width, height, spineWidth, gap, fontSize, true, false);
		mirror([1,0,0]) {
			overhangTest(false, start, end, increment, width, height, spineWidth, gap, fontSize, false);
		}
	}
}

make(_type, _start, _end, _increment, _width, _height, _fontSizePercent, _spineWidth);

```


[User]
Okay, I'm looking to create some custom word glasses, like the ones described here. I want a design that I can easily customize with different words, and that uses a filament hinge.

Here are my specific requirements:

**Word Customization:**

*   The design should allow for a word of 1 to 4 letters to be displayed on the lenses.
*   I need to be able to specify the word as a parameter in the OpenSCAD file.
*   Each letter should be individually horizontally adjustable by a parameter. These shift values could be positive or negative. This needs to be independent for each letter.

**Part Selection:**

*   I need to be able to choose to print only the frame, only the arms, or both the frame and arms. This should also be a configurable parameter.

**Hinge:**

*   The hinge should be designed to use standard filament as a pin.
*   The diameter of the filament used should be a configurable parameter, so I can adjust it to match the filament I'm using.

**General Geometry:**

*   The overall style should match the provided example: rounded eye pieces, with a connected bridge, and arms.
*   The shape of the frame itself should be parameterised; I want to be able to adjust the size of the lenses and their relative positions.
*   The frame should have a nose piece.
*   The design should include a hinge receiver on the frame and a hinge on the arms that works as a simple pin join.

**Libraries:**

*   The design should use the `Write.scad` library for creating the text, and I would like this to be an attached file as part of this response.

**Output:**

*   The final output should be an OpenSCAD file, including the `Write.scad` library, that I can open, customize, and generate a 3D printable file from.

Essentially, I want to take the design from the provided description, but make it fully configurable and parametric. I want to be able to change the word, the letter positions, the hinge size, the eye piece size and position, and choose which parts to print. Is this something you can help me with?

[Assistant]
WordGlasses-3.0.scad:
```
use <write/Write.scad>

//The word to write (1 to 4 characters)
word="IZZY";

// Which one would you like to see?
part = "frame"; // [both:Arms and frame,frame:Only the frame,arms:Only the arms]

// Shift letter 1 by?
letter_1_shift = 0;

// Shift letter 2 by?
letter_2_shift = 0;

// Shift letter 3 by?
letter_3_shift = 0;

// Shift letter 4 by?
letter_4_shift = 0;

//Hinge filament diameter?
hinge_diameter = 1.77;

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

print_part();

wordlength = len(word);

module print_part() {
	if (part == "frame") {
		only_frame();
	} else if (part == "arms") {
		only_arms();
	} else if (part == "both") {
		build_print();
	} else {
		build_print();
	}
}

module only_arms() {
	//place the two arms
	translate([0,4,0]) arm();
	translate([0,-4,0]) mirror([0,1,0]) arm();
}

module only_frame() {
	union() {
		//place the glasses frame
		place_frame();
		//place the words
		place_words();
	}
}

module build_print() {
	only_frame();
	//place the two arms
	translate([0,35,0]) arm();
	translate([0,-50,0]) mirror([0,1,0]) arm();
}

//creates a quarter circle centered at 0,0 and in the positive quadrant
module quarter_arc(radius) {
intersection() {
	square([radius,radius]);
	circle(r = radius, $fn=50); //bottom left
}
}

//creates the shape of the eye
module eye_shape(size) {
	//join lots of circles and quarter_circles together
	hull() {
		//top left
		translate([size*15, 0, 0]) circle(r = size*15, $fn=50);
		//rounded top
		//translate([size*33, size*-30, 0]) rotate(a=[0,0,45]) quarter_arc(size*47);
		//top right
		translate([size*53, 0, 0]) circle(r = size*15, $fn=50);
		//bottom left
		translate([size*23, size*-9.5, 0]) rotate(a=[0,0,180]) quarter_arc(size*23);
		//rounded bottom
		//translate([size*30, size*-8, 0]) rotate(a=[0,0,240]) quarter_arc(size*25);
		// bottom right
		translate([size*43, size*-9.5, 0]) rotate(a=[0,0,270]) quarter_arc(size*23);
	}
}

//defines the nose piece
module nose_piece() {
	//width of 3
	linear_extrude(height=3) {
		//it's a hull of 3 arcs
		hull() {
		quarter_arc(6, $fn=50);
		rotate([0,0,300]) quarter_arc(6, $fn=50);
		translate([0, -12, 0]) rotate([0,0,270]) quarter_arc(3, $fn=50);
		}
	}
}

//defines the letters
module words(l_ltr1, l_ltr2, r_ltr1, r_ltr2, l_trns1, l_trns2, r_trns1, r_trns2) {
	union() {
	translate([155,1.5,0]) mirror([1,0,0]) {
	//4 letter word, trans 9 and 95
		translate([l_trns1,-32,0]) scale([0.82,1,1]) write(l_ltr1,t=3,h=43);
		translate([l_trns2,-32,0]) scale([0.82,1,1]) write(l_ltr2,t=3,h=43);
		translate([r_trns1,-32,0]) scale([0.82,1,1]) write(r_ltr1,t=3,h=43);
		translate([r_trns2,-32,0]) scale([0.82,1,1]) write(r_ltr2,t=3,h=43);
	}
	}
}

module place_words() {

	s1 = letter_1_shift;
	s2 = letter_2_shift;
	s3 = letter_3_shift;
	s4 = letter_4_shift;

	intersection() {
		translate([0,0,-1]) linear_extrude(height=5) {
			union() {
				eye_shape(0.97);
				translate([150,0,0]) mirror([1, 0, 0])	eye_shape(0.97);
			}
		}

		if (wordlength == 1) {
			words(word[0],"","","",25+s1,0,0,0);
		} else if (wordlength == 2) {
			words(word[0],word[1],"","",25+s1,109+s2,0,0);
		} else if (wordlength == 3) {
			words(word[0],word[1],word[2],"",15+s1,40+s2,109+s3,0);
		} else if (wordlength == 4) {
			words(word[0],word[1],word[2], word[3],15+s1,40+s2,98+s3,123+s4);
		} else {
			words("F","A","I","L", 15, 40, 98, 123);
		}
	}
}

module place_frame() {
	//join the two halves of the frame together
	union() {
		frame_half();
		translate([150,0,0]) mirror([1, 0, 0]) frame_half();
	}
}

// pieces together a half-frame
module frame_half() {
	//subtract the bridge curves
	difference() {
		//the glasses including nose piece and hinge piece
		union() {
			//extrude the main glasses frame
			linear_extrude(height=3) {
				difference() {
					// a big eye shape is made, and squares for the bridge and hinge join added
					union() {
						eye_shape(1);
						//bridge
						translate([50, -1, 0]) square([25, 15.5]);
						//hinge
						translate([-5, -8, 0]) square([9, 10]);
					}
					// we subtract a slightly scaled eye shape. Offset needs to be calculated if scale (or glasses dimensions) change.
					translate([4.5,-1 ,0]) eye_shape(0.88);
				}
			}
			//place the nose piece
			translate([68,-3,3]) rotate(a=[0, 270, 347]) nose_piece();
			//place the hinge
			//note: x angle may not be 0. Adjust to get flat lenses on head. Adjust translate Y to correct for movement in rot.
			translate([-5,-5.5,2.99]) frame_hinge_recep();
			//place the stiffening support
			//translate([62,9,3]) rotate([0,90,0]) cylinder(h=15,r=2.5, $fn=50);
		}

		//the shape of the bridge curve (below and above bridge)
		linear_extrude(height=20) {
			union() {
				translate([74.5, 72, 10]) circle(r = 60);
				translate([74.5, -3.4, 10]) {
					hull() {
						rotate([0,0,45]) quarter_arc(8);
						translate([-4.5,-16,0]) circle(5);
					}
				}
			}
		}
	}
}

//draws the body of the arm
module arm_body() {
union() {
linear_extrude(height=3) union() {
	hull() {
		square([2,10]);
		translate([5,0,0]) square([2,10]);
	}
	hull() {
		translate([5,0,0]) square([2,10]);
		translate([92,7,0]) square([2,8]);
	}
	hull() {
		translate([92,7,0]) square([2,8]);
		translate([130,22,0]) square([2,8]);
	}
}

}
}

module frame_hinge_recep() {
	rotate([90,0,0]) {
		translate([0,0,0]) hinge_support(hinge_diameter);
		translate([0,0,-7.5]) hinge_support(hinge_diameter);
	}
}
module hinge_support(hinge_size) {
	linear_extrude(height=2.5) {
	difference() {
	union() {
		square([4,8]);
		square([9,4.5]);
		translate([5.5,4.5]) circle(r=3.5,$fn=50);
		polygon(points=[[0,8],[4,8],[0,8.75]], paths=[[0,1,2]]);
	}
	translate([5.5,4.5]) union() {
		circle(r=hinge_size/2, $fn=50);
		rotate([0,0,30]) intersection() {
				quarter_arc(6);
				rotate([0,0,26]) quarter_arc(6);
			}
		//translate([-hinge_size/2,0,0]) square([hinge_size/2,7]);
	}
	}
	}
}

module arm_hinge(hinge_size) {
	linear_extrude(height=4.5) {
		difference() {
			union() {
				square([8,5.5]);
				translate([4.5,5.5]) circle(r=3.5,$fn=50);
			}
			translate([4.5,5.5]) union() {
				circle(r=hinge_size/2, $fn=50);
				rotate([0,0,32]) intersection() {
				rotate([0,0,0]) quarter_arc(6);
				rotate([0,0,25]) quarter_arc(6);
			}
			}
		}

	}

}

module arm() {
	union() {
		arm_body();
		translate([0.05,2.75,0]) rotate([270,180,0]) arm_hinge(hinge_diameter);
	}
}

```


[User]
Okay, here's what I'm looking to create. I want a parametric OpenSCAD file that generates molds for casting different shapes, primarily using cement, plaster, or wax. I want to be able to customize the final shape of the cast object, the mold's dimensions, and a few other features.

Here's a breakdown of the key parameters and functionalities I need:

**Core Functionality:**

1.  **Mold Types:** I need the option to choose between several different basic mold shapes. Specifically, I want:
    *   **Two Radius Cylinder:**  A cylinder that tapers, with a different radius at the top and bottom.
    *   **Cylinder:** A standard cylinder.
    *   **Cube:** A standard cube.
    *   **Sphere:** A sphere.
    *   **Dodecahedron:** A 12-sided polyhedron.

2.  **Insert/Core:** For all mold types except sphere and dodecahedron, I need the ability to include an insert (a core) that creates the hollow space where the material will be poured. This should be a separate object that fits inside the main mold body. There should be a parameter to toggle the display of this insert.
    *   **Printable Insert:** The insert should be designed such that it can be printed separately.
    *   **Insert Cutout:** The insert itself should have a "cutout" section to allow for more precise casting. This would be a smaller insert nested within the main insert.
    *   **Plier Lip**: The insert should also have an element designed for removal via pliers.

3.  **Display Options:**
    *   **Cutout View:** I need a "cutout" view option that displays only one half of the mold to allow for better visualization of the internal geometry.
    *   **Print Entire Model:** This option should print both halves of the mold.
    *   **Print Insert Only:** This option should print only the insert, or default to the entire model if an insert is not applicable.

4.  **Dimensions:**
    *   **Material Height:** The overall height of the final cast object. For sphere, the height is based on the width.
    *   **Material Width:** The width of the final cast object.
    *   **Bottom Material Width:** For the two-radius cylinder only, this is the width of the cylinder's base.
    *   **Cement Thickness:** The desired thickness of the cement/plaster/wax material, affecting the size of the insert.
    *   **Mold Thickness:** The thickness of the mold material itself, affecting the overall size of the mold.

5. **Resolution:** Allow control over the fineness of circular shapes, which can be controlled with the `$fn` variable.

6. **Text:**
    *   **Show Text:** An option to include text on the mold.
    *   **Text Content:** A parameter to set the text to be displayed.
    *   **Letter Spacing:** An adjustable spacing between letters.
    *   **Letter Height:** The height of the text.
    *   **Font Choice:** Allow a selection of fonts using the `write/Letters.dxf`, `write/BlackRose.dxf`, `write/orbitron.dxf`, `write/knewave.dxf` and `write/braille.dxf` files.
    *   **Invert Text:** An option to indent or raise the text on the mold.

**Libraries:**

I will be using the library located within the `write/` folder, so do not include the `write/` prefix in the code, for example, just `Letters.dxf`.

**Specific Requirements:**

*   The OpenSCAD file should be well-commented, making it easy to understand and modify.
*   The code should be organized logically, with variables defined clearly at the beginning of the file.
*   The code should be parametric, meaning that changing one of the variables will change the overall geometry of the model.
*   The code should make use of modules for different parts of the design.
* The model should be split into two halves.
* The insert should be designed to have a plier lip for easier removal.

Basically, I'd like to have a robust, flexible tool for generating different mold designs with a good amount of customization. I'm okay with basic shapes for now, but I plan to expand this project over time.

Let me know if you need any more clarification.

[Assistant]
CementMold_v5.scad:
```
use <write/Write.scad>

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

//Type of model
moldType = 0; //[0:Two Radius Cylinder, 1:Cylinder, 2:Cube, 3:Sphere, 4:Dodecahedron]

//Print mold insert, the "Sphere" & "Dodecahedron" models do not have an insert, but you can always print an insert separately.
reqInsert = 1; //[0:Don't want insert, 1:Display insert]

//Show a cutout version of your model, good for perspective, switch to "Print Entire Model" or "Print Insert Only" to print working parts of a mold.  If you try to Print Insert Only with non-insert models the default display will be the entire model.
displayCutout = 1; //[1:Display Cutout, 0:Print Entire Model, 2:Print Insert Only]

//Height of your model in mm's, if choosing a sphere, height is calculated based on the sphere's width.
materialHeight = 65;

//Width of your finished material in mm's (Final Product)
materialWidth = 50;

//Bottom Cylinder thickness in mm's, only used with "Two Radius Cylinder" model.
bottomMaterialWidth = 30;

//Desired thickness of media (Cement, Plaster, Wax...) in mm's. Changes the size of the insert.
cementThickness = 5;

//Desired thickness of model (How thick do you want it) in mm's.
moldThickness = 5;

//Resolution of build
resolution = 25;

//Show text below. Still working on getting letters on the "Two Radius Cylinder" model.  Not available on the "Dodecahedron" model.
showText = 0; //[0:No, 1:Yes]

//Something to say?
writeSomething = "Howdy From TX";

//Letter spacing, default = 1.1
mySpace = 1.1;

//Letter height, default = 4
myHeight = 4;

//Font Choice
myFont = "write/Letters.dxf"; //["write/Letters.dxf":Letters, "write/BlackRose.dxf":Black Rose, "write/orbitron.dxf":Orbitron, "write/knewave.dxf":Knewave, "write/braille.dxf":Braille]

//Invert Text in mold (Embed)
invertText = 0; //[0:No, 1:Yes]

//Lots of variables... more for my sanity and to keep everything clean

function radius(diameter) = diameter / 2;

//Outer cube measurements
cubeMoldThickness = materialWidth + (moldThickness * 2);
cubeHeight = materialHeight + moldThickness;

//Outer Insert measurements
insertHeight = materialHeight - cementThickness + 1;
insertBotRad = radius(bottomMaterialWidth) - cementThickness;
insertTopRad = radius(materialWidth) - cementThickness;

//Insert cutout measurements
insertCutThickness = insertTopRad / 4;
insertCutHeight = insertHeight - insertCutThickness + 1;
insertCutBotRad = insertBotRad - insertCutThickness;
insertCutTopRad = insertTopRad - insertCutThickness;

//moving the insert away from the model
moveInsertXY = insertTopRad / 2;
moveInsertCutXY = insertCutTopRad / 2;
moveInsertCutZ = insertCutThickness;

/*
//Not in use: working on text for two radius cylinder
function findC(a,b) = sqrt((pow(a,2) + pow(b,2)));  //a^2 + b^2 = c^2... findC
function findDeg(a,o) = atan(a/o);  //find the arc tangent (for degrees) of an angle
textAngleDoubleRad = findDeg(materialWidth - bottomMaterialWidth, materialHeight);
textHeightFromTop = materialHeight / 2;
*/

//This is the cube that cuts the model in half
cutSize = cubeMoldThickness * 2;
cutMove = cutSize / 2;

//Movement of model cutouts to fit in the middle of the mold
moveCubeXY = cubeMoldThickness / 2;
moveCubeZ = cubeHeight / 2;
movePolyZ = moldThickness;
moveCubePolyZ = (materialHeight / 2) + moldThickness;

//Insert plier lip
plierLip = insertHeight / 2;
plierRad = insertTopRad / 4;

module drawMold(){
	difference(){
		if(moldType == 0){
			difference(){
				translate([0,0,moveCubeZ])
					cube(size = [cubeMoldThickness, cubeMoldThickness, cubeHeight], center = true);
				translate([0,0,movePolyZ])
						cylinder(h = materialHeight + 1, r1 = radius(bottomMaterialWidth), r2 = radius(materialWidth), $fn = resolution, center = false);
			}
		}else if(moldType == 1){
			difference(){
				translate([0,0,moveCubeZ])
					cube(size = [cubeMoldThickness, cubeMoldThickness, cubeHeight], center = true);
				translate([0,0,movePolyZ])
					cylinder(h = materialHeight + 1, r = radius(materialWidth), $fn = resolution, center = false);
			}
		}else if(moldType == 2){
			difference(){
				translate([0,0,moveCubeZ])
					cube(size = [cubeMoldThickness, cubeMoldThickness, cubeHeight], center = true);
				translate([0,0,moveCubePolyZ])
					cube(size = [materialWidth, materialWidth, materialHeight + 1], center = true);
			}
		}else if(moldType == 3){
			difference(){
				translate([0,0,moveCubeXY])
					cube(size = [cubeMoldThickness, cubeMoldThickness, cubeMoldThickness], center = true);
				union(){
					translate([0,0,moveCubeXY])
						sphere(r = radius(materialWidth), $fn = resolution);
					translate([0,0,moveCubeXY])
						cylinder(h = materialWidth, r1 = radius(materialWidth) / 4, r2 = radius(materialWidth) / 2, $fn = resolution, center = false);
				}
			}
		}else if(moldType == 4){
			difference(){
				translate([0,0,moveCubeZ])
					cube(size = [cubeMoldThickness, cubeMoldThickness, materialWidth], center = true);
				translate([0,0,moveCubeZ + 2.5])
					scale([materialWidth - moldThickness,materialWidth - moldThickness,materialWidth - moldThickness + 1]){
						intersection(){
							cube([2,2,1], center = true);
						intersection_for(i=[0:4]){
							rotate([0,0,72*i]) rotate([116.565,0,0])
								cube([2,2,1], center = true);
						}
					}
				}
			}
		}
		translate([-cutMove,0,cutMove/2])
			cube(size = cutSize, center = true);
	}
}

module drawInnerMold(){
		union(){
			if(moldType == 0){
				difference(){
					cylinder(h = insertHeight, r1 = insertBotRad, r2 = insertTopRad, $fn = resolution, center = false);
					translate([0,0,insertCutThickness])
						cylinder(h = insertCutHeight, r1 = insertCutBotRad, r2 = insertCutTopRad, center = false);
				}
			}else if(moldType == 1){
				difference(){
					cylinder(h = insertHeight, r = insertTopRad, $fn = resolution, center = false);
					translate([0,0,insertCutThickness])
						cylinder(h = insertCutHeight, r = insertCutTopRad, center = false);
				}
			}else if(moldType == 2){
				difference(){
					translate([-moveInsertXY,-moveInsertXY, 0])
						cube(size = [insertTopRad, insertTopRad, insertHeight], center = false);
					translate([-moveInsertCutXY,-moveInsertCutXY, moveInsertCutZ])
						cube(size = [insertCutTopRad, insertCutTopRad, insertCutHeight], center = false);
				}
			}
			if(moldType < 3){
				translate([0,0,moveInsertCutZ])
					cylinder(h = plierLip, r = plierRad, center = false);
			}else	if(displayCutout == 2){
				if(moldType == 4){
					//Default Display
					translate([5,0,0]) drawDodecahedron();
					translate([-5,0,0]) mirror([1,0,0]) drawDodecahedron();
				}else{
					//Default Display
					translate([5,0,0]) drawMold();
					translate([-5,0,0]) mirror([1,0,0]) drawMold();
				}
			}
		}
}

module drawDodecahedron(){
	difference(){
		difference(){
			translate([0,0,moveCubeZ])
				cube(size = [cubeMoldThickness, cubeMoldThickness, materialWidth], center = true);
			translate([0,0,moveCubeZ + 2.5])
				scale([materialWidth - moldThickness,materialWidth - moldThickness,materialWidth - moldThickness + 1]){
					intersection(){
						cube([2,2,1], center = true);
						intersection_for(i=[0:4]){
							rotate([0,0,72*i]) rotate([116.565,0,0])
								cube([2,2,1], center = true);
						}
					}
				}
		}
		translate([-cutMove,0,cutMove/2])
			cube(size = cutSize, center = true);
	}
}

module writeMe(){
	if(moldType == 1){
		translate([-5,0,0]) rotate([0,0,270])
				writecylinder(writeSomething,[0,0,materialHeight / 2],radius(materialWidth),materialHeight,font=myFont,h=myHeight,space=mySpace,t=2,center=true);
	}else if(moldType == 2){
		translate([-5,0,0])
			writecube(writeSomething,[0,0,materialHeight / 2],[materialWidth,materialWidth,materialHeight],font=myFont,h=myHeight,space=mySpace,t=2,face="left");
	}else if(moldType == 3){
		translate([-5,0,materialWidth]) rotate([0,0,270]) rotate([0,180,0])
			writesphere(writeSomething,[0,0,materialWidth / 2],radius(materialWidth),font=myFont,h=myHeight,space=mySpace,t=4);
	}
}

//Final Display
if(displayCutout == 0){
	if(showText == 1){
		if(invertText == 1){
			difference(){
				translate([-5,0,0]) mirror([1,0,0])	drawMold();
				writeMe();
			}
			translate([5,0,0]) drawMold();
		}else{
			writeMe();
			translate([5,0,0]) drawMold();
			translate([-5,0,0]) mirror([1,0,0])	drawMold();
		}
	}else{
		translate([5,0,0]) drawMold();
		translate([-5,0,0]) mirror([1,0,0])	drawMold();
	}
	if(reqInsert == 1){
		translate([(cubeMoldThickness / 2) + insertTopRad + 10,0,0]) drawInnerMold();
	}
}else if(displayCutout == 1){
	if(showText == 1){
		if(invertText == 1){
			difference(){
				translate([-5,0,0]) mirror([1,0,0])	drawMold();
				writeMe();
			}
		}else{
			writeMe();
			translate([-5,0,0]) mirror([1,0,0])	drawMold();
		}
	}else{
		translate([-5,0,0]) mirror([1,0,0])	drawMold();
	}
	if(reqInsert == 1){
		translate([(cubeMoldThickness / 2) + insertTopRad + 10,0,0]) drawInnerMold();
	}
}else if(displayCutout == 2){
	drawInnerMold();
}
```


[User]
Okay, so I'm looking to get a custom skateboard riser designed, and I want a parametric OpenSCAD file so I can tweak it myself. I've been using a program that someone else wrote, but it's a bit clunky and I want something a bit cleaner, plus I'm hoping for a few more options.

Here's what I need it to do:

**Core Functionality:**

*   **Truck Hole Patterns:** The design needs to accommodate both "Old School" and "New School" truck mounting hole patterns.
*   **Dimensions:** I need to be able to specify:
    *   Riser width (in inches)
    *   Riser length (in inches)
    *   Minimum riser height (in inches) - basically the thickness of the flat part of the riser
    *   Riser angle (in degrees, from 0 to 20 degrees).
*   **Text:** I'd like to be able to add custom text to the back of the riser.
*   **Text Customization:** I need to be able to control:
    *   The font of the text (I'd like at least a few to choose from, maybe a basic one, a more futuristic one, something bold, and something like Braille for fun).
    *   The text height (in millimeters).
*   **Units:** It would be nice if the input could be primarily in inches, and the OpenSCAD program does the conversion to metric.
*   **Rounding:** The riser should have rounded edges/corners, just like a normal one.
*   **Build Plate:** It would be helpful if there was a display of the build plate, I know in the example file there was functionality to select between common print bed sizes.

**Libraries:**

*   I know the existing code used `MCAD/boxes.scad` for the basic box shapes, and `utils/build_plate.scad` for the build plate indicator and `write/Write.scad` for the text feature, so please include those as well.

**Things That Could Be Improved From the Existing Code:**

*   The text placement in the existing code is a bit of a mess, it feels like there's some unnecessary trigonometry going on.  I think it's trying to account for the angled surface but it's calculating a weird offset. It also flips to a different placement for small angles which I don't want. It should simply be on the board-side surface, centered horizontally, and set vertically in a way that it's above the main board facing portion of the riser by just a few millimeters.
*   I'd really like the text to not print upside down without having to use an extra step before printing.
*   I'd like the option of cutouts within the riser, which I know are not part of the original design, but a hollowed-out honeycomb or similar design would be cool, and I'd like to control how many cutouts there are.
*  The existing code uses a generic number for rounding. If possible it should use a variable, so I can make it as sharp or as rounded as I want.

**In Summary:**

I'm looking for a parametric OpenSCAD file that lets me create custom skateboard risers with adjustable dimensions, angles, text, and hole patterns. The code should be well-organized, use the specified libraries, fix the text placement issues I mentioned, and give the ability to specify edge rounding and cutout number. Please output the entire `Custom_Skateboard_Riser.scad` file.

[Assistant]
Customizable_Skatebaord_Riser.scad:
```
// preview[view:north east, tilt:top diagonal]
//This program allows for the production of easily customizable risers for skateboards.
//Created By: Matthew Kelly
//Date:2-18-13

//This section is for Library Callouts
use <MCAD/boxes.scad>
use <utils/build_plate.scad>
use <write/Write.scad>

//This section askes the user for the information necessary to generate a custom skateboard riser

//Select the the desired truck hole pattern.
Hole_Pattern="Old School";  //[Old School, New School]

//Enter your desired riser width here in inches.
Riser_Width=2.5;

//Enter your desired riser length here in inches.
Riser_Length=3.25;

//Select the desired minimum riser height.
Riser_Height=.5;  //[0.125:1/8", 0.25:1/4", 0.375:3/8", 0.5:1/2", 0.625:5/8", 0.75:3/4", 0.875:7/8", 1:1"]

//Select the desired riser angle.
Riser_Angle=10;  //[0:20]

//Enter your custom text here.  (Not recommended for 0.125" Risers) (Note riser prints upside down)
Custom_Text="RELENTLESS";

//Select Text Font
Font = "write/orbitron.dxf";//["write/Letters.dxf":Basic,"write/orbitron.dxf":Futuristic,"write/braille.dxf":Braille, "write/knewave.dxf":Retro]

//Enter Text Height in millimeters.
Text_Height=6;

//This section is for constants.
Hole_Size_Standard=0.219
*1;  //7/32" Diameter
Hole_Width_Old_School=41.28*1;  //1-5/8"
Hole_Length_Old_School=63.5*1;  //2-1/2"
Hole_Width_New_School=41.28*1;  //1-5/8"
Hole_Length_New_School=53.98*1;  //2-1/8"

//This section carries out simple standard to metric conversions of the inputs where necessary.

Riser_Height_Metric=Riser_Height*25.4;
Hole_Size_Metric=Hole_Size_Standard*25.4;
Truck_Width_Metric=Riser_Width*25.4;
Truck_Length_Metric=Riser_Length*25.4;

//This section carries out calculations for the riser's general geometry.

Board_Side_Length=Truck_Length_Metric/cos(Riser_Angle); //This is the length of the board-side of the riser.
Angled_Thickness=sin(Riser_Angle)*Board_Side_Length;  //This is the thickness of the angles section of the riser.
Angled_Height=Truck_Length_Metric*sin(Riser_Angle);

//This section builds the riser

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

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

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

build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

if (Hole_Pattern == "Old School")
{
translate([0,0, Riser_Height_Metric])

union()
{

	difference()
	{

		union()
		{

			difference()
			{
				translate([0,0,0])
				rotate([Riser_Angle,0,0])
				roundedBox([Truck_Width_Metric, Truck_Length_Metric, Angled_Thickness], 10, true);  //Angled Portion

			translate([0,0,-50])cube(size = [100,100,100], center = true); //Blank off the angled portion of the riser.
			}

		translate([0,0,-Riser_Height_Metric/2])roundedBox([Truck_Width_Metric, Board_Side_Length, Riser_Height_Metric], 			10, true);  //Add riser height
		}

	rotate([Riser_Angle,0,0])translate([Hole_Width_Old_School/2,Hole_Length_Old_School/2,-100]) cylinder(h = 200, 				r1 = Hole_Size_Metric/2, r2 = Hole_Size_Metric/2, center = false, $fn=100);

	rotate([Riser_Angle,0,0])translate([-Hole_Width_Old_School/2,Hole_Length_Old_School/2,-100]) cylinder(h = 200,				 r1 = Hole_Size_Metric/2, r2 = Hole_Size_Metric/2, center = false, $fn=100);

	rotate([Riser_Angle,0,0])translate([Hole_Width_Old_School/2,-Hole_Length_Old_School/2,-100]) cylinder(h = 200, 				r1 = Hole_Size_Metric/2, r2 = Hole_Size_Metric/2, center = false, $fn=100);

	rotate([Riser_Angle,0,0])translate([-Hole_Width_Old_School/2,-Hole_Length_Old_School/2,-100]) cylinder(h = 200, 				r1 = Hole_Size_Metric/2, r2 =Hole_Size_Metric/2, center = false, $fn=100);
	}
if (Riser_Angle>4)
{
translate([0,Board_Side_Length/2-(cos(180-90-Riser_Angle)*(Angled_Thickness/2)),Riser_Height+(sin(180-90-Riser_Angle)*(Angled_Thickness/2))])
rotate(90-Riser_Angle,[-1,0,0]) // rotate around the x axis
rotate(90,[0,0,0]) // rotate around the y axis
write(Custom_Text,t=1.5,h=Text_Height*1,center=true, font=Font);
}
else
{
translate([0,Board_Side_Length/2-(cos(180-90-Riser_Angle)*(Angled_Thickness/2)), -Riser_Height_Metric/2])
rotate(90-Riser_Angle,[-1,0,0]) // rotate around the x axis
rotate(90,[0,0,0]) // rotate around the y axis
write(Custom_Text,t=1.5,h=Text_Height*1,center=true, font=Font);
}

}
}
else
{
translate([0,0, Riser_Height_Metric])

union()
{

	difference()
	{

		union()
		{

			difference()
			{
				translate([0,0,0])
				rotate([Riser_Angle,0,0])
				roundedBox([Truck_Width_Metric, Truck_Length_Metric, Angled_Thickness], 10, true);  //Angled Portion

			translate([0,0,-50])cube(size = [100,100,100], center = true); //Blank off the angled portion of the riser.
			}

		translate([0,0,-Riser_Height_Metric/2])roundedBox([Truck_Width_Metric, Board_Side_Length, Riser_Height_Metric], 			10, true);  //Add riser height
		}

	rotate([Riser_Angle,0,0])translate([Hole_Width_New_School/2,Hole_Length_New_School/2,-100]) cylinder(h = 200, 				r1 = Hole_Size_Metric/2, r2 = Hole_Size_Metric/2, center = false, $fn=100);

	rotate([Riser_Angle,0,0])translate([-Hole_Width_New_School/2,Hole_Length_New_School/2,-100]) cylinder(h = 200,				 r1 = Hole_Size_Metric/2, r2 = Hole_Size_Metric/2, center = false, $fn=100);

	rotate([Riser_Angle,0,0])translate([Hole_Width_New_School/2,-Hole_Length_New_School/2,-100]) cylinder(h = 200, 				r1 = Hole_Size_Metric/2, r2 = Hole_Size_Metric/2, center = false, $fn=100);

	rotate([Riser_Angle,0,0])translate([-Hole_Width_New_School/2,-Hole_Length_New_School/2,-100]) cylinder(h = 200, 				r1 = Hole_Size_Metric/2, r2 =Hole_Size_Metric/2, center = false, $fn=100);
	}
if (Riser_Angle>3)
{
translate([0,Board_Side_Length/2-(cos(180-90-Riser_Angle)*(Angled_Thickness/2)),Riser_Height_Metric/2])
rotate(90-Riser_Angle,[-1,0,0]) // rotate around the x axis
rotate(90,[0,0,0]) // rotate around the y axis
write(Custom_Text,t=1.5,h=Text_Height*1,center=true, font=Font);
}
else
{
translate([0,Board_Side_Length/2-(cos(180-90-Riser_Angle)*(Angled_Thickness/2)), -Riser_Height_Metric/2])
rotate(90-Riser_Angle,[-1,0,0]) // rotate around the x axis
rotate(90,[0,0,0]) // rotate around the y axis
write(Custom_Text,t=1.5,h=Text_Height*1,center=true, font=Font);
}

}
}
```


[User]
Okay, I'm looking for a way to generate a flexible, interlocking scale-like structure that I can 3D print, something like a modular armor or impact vest. I've got an existing OpenSCAD file that kind of does this, but it's not as configurable as I'd like, and it's a bit messy.

Here’s a breakdown of what I'm trying to achieve, and what I need to be able to control:

**Overall Structure:**

*   I want a grid of interlocking scales. These scales should be connected such that when printed in a flattened state they can expand in the other two directions when bent. Think of a flexible sheet that can expand and contract somewhat.
*   The basic shape of each scale should be a kind of rounded, almost fish-scale looking shape, with some interlocking male/female connectors. The connectors should be designed so they stay together, but provide some flex.
*   The scales should ideally be printed all connected in a flat configuration but be able to expand into a flexible structure in other dimensions.
*   I want the design to be parametric; I should be able to change the size, number of rows/columns, scale, and overall dimensions relatively easily.

**Key Parameters to Control:**

*   **`rows`**: The number of rows of scales in the grid.
*   **`columns`**: The number of columns of scales in the grid.
*   **`scale`**: A global scaling factor that resizes individual scales (e.g., 1=small, 2=larger, etc.)
*    **`polygon_sides`**: The number of sides used to construct the rounded geometry.
*   **`scale_length`**: The overall length of the individual scales (a kind of base measure for the others, perhaps the longest dimension)
*   **`scale_thickness`**: The thickness of the scale itself.
*   **`overlap_x`, `overlap_y`, `overlap_z`**: Control how much the scales overlap in each direction, to customize how the scales flex and are connected.
*   **`male_ball_radius`**: The radius of the ball part of the male connector.
*   **`male_neck_width`**:  The width of the 'neck' of the male connector.
*   **`female_track_height`**: The height of the track in the female connector where the male connector slides.
*   **`female_gap_variance`**: A small variable to customize the fitting between the male and female connectors (a gap or overlap adjustment).
*   **`print_mode`**:  A boolean, perhaps 1 or 0, that allows switching between the flattened printing configuration and the designed expanded configuration.

**Interlocking/Connectors:**

*   The male connectors should be roughly spherical or hemispherical and should connect into tracks on the adjacent scales
*   The connections should be robust but provide enough clearance to allow the scales to articulate and move relative to one another.
*   I would like the ability to offset the horizontal connectors slightly so that the vertical scales can have a 'half-step' shift for better flexibility, kind of like the scales of a snake.
*   I should be able to control the location of the connectors within each scale (front, back, left, right), and how many there are per scale
*   The male and female connector should be clearly separated so they are easily visible, and not 'hidden' from easy adjustment

**Additional Notes:**

*   The current code has a few hardcoded numbers for offsets, etc. I'd prefer that those be configurable variables also, or better yet, a calculation based on the scale or other core variables.
*   I'm not too attached to the exact shape of the scale, so feel free to change that if it would make the design simpler or better. I just need it to be rounded and have the general aesthetic of a fish scale.
*   It'd be great if the code was well-commented so I can follow along with how it's built, and easily make changes.

So, could you design an OpenSCAD file that meets these requirements? I am not using any specific libraries. I've attached my current file as a reference.

[Assistant]
fishscale_20.scad:
```
/*
*Name: Fish scale impact vest
*Author: Andrew Ireland
*Date: 7-Mar-2013
*Description: Prints the parts of a fish scale impact vest in one moving piece.
			   The number of rows, columns and a few other details can be customised.
*/

// preview[view:south, tilt:side]
//Customisable parameters

//Columns to print
g_cols=3;//[1:20]

//Rows to print
g_rows=4;//[1:20]

//Number of faces
g_polygons=20;//[1:50]

//Print Mode - Collapses all scales for printing
g_print_mode=1;  //[0:Yes,1:No]

//Resizes individual scales (1 = small, 10 = large)
g_scale=2;//[1:10]

//Ignore this variable
g_length=80;//[80:80]
//Ignore this variable
g_thickness=3;//[3:3]
//Ignore this variable
g_fishScale_overlap_x=15;//[15:15]
//Ignore this variable
g_fishScale_overlap_y=-16;//[-16:-16]
//Ignore this variable
g_fishScale_overlap_z=9.5;//[9.5:9.5]
//Ignore this variable
g_male_ball_radius=3.5;//[3.5:3.5]
//Ignore this variable
g_male_neck_width=2.5;//[2.5:2.5]
//Ignore this variable
g_female_track_height=4; //[4:4]
//Ignore this variable
g_female_gap_variance=0.5;//[0.5:0.5]

scale(g_scale * 1/10)
{
	if ( g_print_mode == 0 )
	{
		translate([(g_length*2+g_fishScale_overlap_x)*(g_cols-1)/2, 0,g_length]) rotate([-90,0,180]) fishScaleMultiple();
	} else {
		translate([-(g_length*2+g_fishScale_overlap_x)*(g_cols-1)/2, 0,0])	rotate([90,0,0]) fishScaleMultiple();
	}
}

//############################################################################################################
module fishScaleMultiple()
{

	for(row=[g_rows-1:0])
	{

		for(col=[g_cols-1:0])
		{
			if((row+1)%2==0)
			{
				if (col!=g_cols-1){
					translate([(g_length*2+g_fishScale_overlap_x*2)*col+g_length+g_fishScale_overlap_x, (g_length+g_fishScale_overlap_y)*row*g_print_mode, g_fishScale_overlap_z*row]) fishScaleComplete();
				}
			} else {
				translate([(g_length*2+g_fishScale_overlap_x*2)*col, (g_length+g_fishScale_overlap_y)*row*g_print_mode, g_fishScale_overlap_z*row]) fishScaleComplete();
			}

		}

	}
}

//############################################################################################################
module fishScaleComplete()
{
	female_offset_y=g_fishScale_overlap_y+40;//14;
	female_offset_z=4.5;
	difference()
	{
		union()
		{
			difference()
			{
				union()
				{
					fishScale7();
					translate([0,g_length-15,-g_male_ball_radius +  2.8 + g_fishScale_overlap_z*2 ]) rotate([0,90,0]) male();

					//Left
					translate([0.4*g_length+g_fishScale_overlap_x+1 ,g_length-(g_male_ball_radius+25),-g_male_ball_radius + 2.5 + g_fishScale_overlap_z]) rotate([0,90,0]) male();
					//Right
					translate([-0.4*g_length-g_fishScale_overlap_x-1 ,g_length-(g_male_ball_radius+25),-g_male_ball_radius + 2.5 + g_fishScale_overlap_z]) rotate([0,90,0]) male();
				} //union 1

				translate([0,0,-g_length*2]) cylinder(r=g_length*3, h=g_length*2);
				translate([0,g_length/2+g_fishScale_overlap_y-female_offset_y,g_male_ball_radius-female_offset_z]) rotate([180,0,90]) femaleCutAway();
			} //difference 1

			translate([0,g_length/2+g_fishScale_overlap_y-female_offset_y,g_male_ball_radius-female_offset_z]) rotate([180,0,90])  femaleMid();

			//Left
			translate([0.4*g_length+g_fishScale_overlap_x,g_length/2+g_fishScale_overlap_y-female_offset_y*.2,g_male_ball_radius-female_offset_z]) rotate([180,0,90]) femaleSide();

			//Right
			translate([-0.4*g_length-g_fishScale_overlap_x,g_length/2+g_fishScale_overlap_y-female_offset_y*.2,g_male_ball_radius-female_offset_z]) rotate([180,0,90]) femaleSide();
		}//union 2

		//Left difference
		translate([0.4*g_length+g_fishScale_overlap_x,g_length/2+g_fishScale_overlap_y-female_offset_y*.2,g_male_ball_radius-female_offset_z]) rotate([180,0,90]) femaleCutAwaySide();

		//Right difference
		translate([-0.4*g_length-g_fishScale_overlap_x,g_length/2+g_fishScale_overlap_y-female_offset_y*.2,g_male_ball_radius-female_offset_z]) rotate([180,0,90]) femaleCutAwaySide();

		//Make top edge flat for printing
		translate([15,g_length+14, 0]) rotate([90,0,0]) cube([g_length*3,g_length*3,30], center=true);

	}//difference 2
}

//############################################################################################################
module femaleSide()
{
	track_length=g_length*0.85;
	track_height=g_female_track_height;
	gap_variance=g_female_gap_variance;

	difference()
	{

		hull()
		{
			translate([track_length/2-g_male_ball_radius/2,0,0]) cylinder(r1=g_male_ball_radius+gap_variance*30, r2=g_male_ball_radius*.09, h=track_height*2, center=true, $fn=g_polygons);
			translate([-track_length/2+g_male_ball_radius/2,0,0]) cylinder(r1=g_male_ball_radius+gap_variance*30, r2=g_male_ball_radius*.09, h=track_height*2, center=true, $fn=g_polygons);
		}

		cube([track_length+20, g_male_neck_width*2+gap_variance*3,track_height+30], center=true);
		femaleCutAwaySide(track_height, gap_variance);
		translate([0,0,-track_height*2])	cube([g_length*3, g_length*3, track_height*3], center=true);
	}
}

module femaleCutAwaySide()
{
	track_length=g_length*0.87;
	track_height=g_female_track_height;
	gap_variance=g_female_gap_variance;

	rotate([0,90,0]) cylinder(r=g_male_ball_radius+gap_variance/2, h=track_length-g_male_ball_radius, center=true, $fn=g_polygons);
	translate([track_length/2-g_male_ball_radius/2,0,0]) sphere(r=g_male_ball_radius+gap_variance/2, h=g_length-g_male_ball_radius, center=true, $fn=g_polygons);
	translate([-track_length/2+g_male_ball_radius/2,0,0]) sphere(r=g_male_ball_radius+gap_variance/2, h=g_length-g_male_ball_radius, center=true, $fn=g_polygons);
}

//############################################################################################################
module femaleMid()
{
	track_length=2*g_length*0.85;
	track_height=g_female_track_height;
	gap_variance=g_female_gap_variance;

	difference()
	{
		hull()
		{
			translate([track_length/2-g_male_ball_radius/2,0,0]) cylinder(r1=g_male_ball_radius+gap_variance*30, r2=g_male_ball_radius*.09, h=track_height*2, center=true, $fn=g_polygons);
			translate([-track_length/2+g_male_ball_radius/2,0,0]) cylinder(r1=g_male_ball_radius+gap_variance*30, r2=g_male_ball_radius*.09, h=track_height*2, center=true, $fn=g_polygons);
		}

		cube([track_length+20, g_male_neck_width*2+gap_variance*3,track_height+30], center=true);
		femaleCutAway(track_height, gap_variance);
		translate([0,0,-track_height*2])	cube([g_length*3, g_length*3, track_height*3], center=true);
	}
}

module femaleCutAway()
{
	track_length=2*g_length*0.87;
	track_height=g_female_track_height;
	gap_variance=g_female_gap_variance;

	rotate([0,90,0]) cylinder(r=g_male_ball_radius+gap_variance/2, h=track_length-g_male_ball_radius, center=true, $fn=g_polygons);
	translate([track_length/2-g_male_ball_radius/2,0,0]) sphere(r=g_male_ball_radius+gap_variance/2, h=g_length-g_male_ball_radius, center=true, $fn=g_polygons);
	translate([-track_length/2+g_male_ball_radius/2,0,0]) sphere(r=g_male_ball_radius+gap_variance/2, h=g_length-g_male_ball_radius, center=true, $fn=g_polygons);
}

//############################################################################################################
module male()
{
	male_shaft_height=14.9;
	cube_width = male_shaft_height*2.8;
	ball_radius_spacer=0.1;

	difference()
	{
		translate([cube_width*.6,0,0]) rotate([45,90,0]) cube([g_male_neck_width,cube_width,cube_width], center=true);

	 	translate([-g_male_ball_radius-ball_radius_spacer,0,0]) rotate([0,90,0]) cube([g_male_neck_width*2,g_male_ball_radius*3,g_male_ball_radius], center=true);
	}

 	sphere(r=g_male_ball_radius-ball_radius_spacer, $fn=g_polygons, center=true, $fn=g_polygons);

}

//############################################################################################################
module fishScale7()
{
	offset = 5;
	tip_offset = g_fishScale_overlap_x-7;
	thickness_offset = 4;

	difference()
	{
		hull()
		{
			translate([0,0,-thickness_offset*7.5]) sphere(r=thickness_offset*10, center=true, $fn=g_polygons);
			translate([15,g_length*.2, 0]) cylinder(r=g_length*.8,h=g_thickness, center=true, $fn=g_polygons);
			translate([-15,g_length*.2, 0]) cylinder(r=g_length*.8,h=g_thickness, center=true, $fn=g_polygons);
			translate([0,-g_length/2, 0]) cylinder(r=g_length/2,h=g_thickness, center=true, $fn=g_polygons);
		} //hull
		translate([g_length+tip_offset, -g_length-offset, 0]) cylinder(r1=g_length, r2=g_length*0.8, h=g_thickness+thickness_offset, center=true, $fn=g_polygons);
		translate([-g_length-tip_offset, -g_length-offset, 0]) cylinder(r1=g_length, r2=g_length*0.8, h=g_thickness+thickness_offset, center=true, $fn=g_polygons);
	} //difference

}

//############################################################################################################

function s(i) = sin(i);
function c(i) = cos(i);
```


[User]
Okay, I'm looking to create a parametric Archimedean spiral generator in OpenSCAD, and I'd like it to be flexible enough to make things like split rings or just interesting spiral shapes. I've seen some examples online, but I want to be able to control it with parameters.

Here's what I'd like the script to do:

*   **Spiral Shape:** Generate an Archimedean spiral, where the distance between each loop (the pitch) is constant.
*   **Parameters:**
    *   `revolutions`: The number of full turns the spiral makes around its center. This should be a floating-point number so I can have partial revolutions.
    *   `diameter`: The overall diameter of the spiral.
    *   `thickness`: The thickness of each "ring" of the spiral.
    *   `height`: The height of the spiral (for 3D extrusion).
    *   `gap`: The space between each adjacent ring of the spiral.
    *   `resolution`: The number of segments used to create each ring in the spiral, which should affect the smoothness of the curves.
*   **Functionality:**
    *   The spiral should be extruded along the height parameter.
    *   I want to control the number of resolutions around the circle using the `resolution` parameter, and the spiral should be constructed from linear segments, not a curve.
    *   The script should handle cases where the requested number of revolutions exceeds the physical limits based on the diameter, thickness, and gap, clamping it to a maximum sensible value. I'd prefer to do this clamping in the module, not the parameters themselves, so that the parameters still contain the input value.
*   **Library Usage:** Please include the `build_plate()` from the `utils` library to provide context for the spiral. (I understand that I'll need to have the `utils` library available to use it.)
*   **Output:**  The final output should be a single, solid 3D object (a single `union()` of the spiral segments is probably the easiest way to achieve this).

Basically, I'd like a reusable module that I can plug different parameter values into to generate different spiral shapes.

[Assistant]
spiral_v1-0.scad:
```
use <utils/build_plate.scad>

// number of revolutions around the circle
_revolutions = 2.0;

// outside diameter of the spiral
_diameter = 30; //[5:300]

// thickness of each ring of the spiral
_thickness = 2.0;

// height of the spiral
_height = 3;

// gap between rings of the spiral
_gap = 0.3;

// number of sections around the circle
_resolution = 60; // [3:180]

function radiusAt(radius, angle, pitch) = radius - pitch * (angle/360);

module spiral(revolutions, diameter, thickness, height, gap, resolution) {
	eps = 0.2;
	radius = diameter/2;
	revolutions = min(revolutions, radius/(thickness+gap)-eps);
	pitch = thickness + gap;
	angleStep = 360/round(resolution);

	union()
	for (angle = [0:angleStep:360*revolutions-angleStep]) {
		assign(	r0 = radiusAt(radius, angle, pitch),
					r1 = radiusAt(radius, angle+angleStep, pitch),
					r2 = radiusAt(radius-thickness, angle+angleStep, pitch),
					r3 = radiusAt(radius-thickness, angle, pitch)
					) {
			linear_extrude(height = height, slices=1) {
					polygon(	points=[	[r0*sin(angle),r0*cos(angle)],
											[r1*sin(angle+angleStep),r1*cos(angle+angleStep)],
											[r2*sin(angle+angleStep+eps),r2*cos(angle+angleStep+eps)],
											[r3*sin(angle-eps),r3*cos(angle-eps)] ],
								paths=[[0,1,2,3]]);
			}
		}

	}

}

build_plate(0);
spiral(_revolutions, _diameter, _thickness, _height, _gap, _resolution);
```


[User]
Okay, I need a custom die generator, something a little more complex than just your standard six-sided cube. I'd like to be able to specify the number of sides, the depth of the faces, and how deep the numbers are engraved into the surface. I've already got a pretty decent starting point from something I found online (I've included the `ndie.scad` file), but it's not quite perfect.

Here's a breakdown of what I'm looking for:

**Core Functionality:**

*   **Variable Sides:** The key feature is that the die should be configurable for any number of sides. This should be a simple parameter input. I also want to be able to chose a radius of the sphere.
*   **Engraved Numbers:** Each face should have a number on it, indicating the face. Ideally, these numbers should be generated automatically for any number of sides. I want to be able to adjust the size of the numbers as well as the depth that they are engraved.
*   **Face Depth:** I need to be able to control how deep each face is inset into the sphere. It should be a percentage of some sort of calculated maximum depth.
*   **Text Style:** I'd like the ability to select from a few fonts. I've already seen a handful used, things like Orbitron, Knewave, and BlackRose. I think it's important to be able to select these.
*  **Underlining Sixes/Nines:** I've seen this in the existing code where if you only have sixes and nines in the text, you underline it so it's clear which is which.

**Specific Parameters (and how I'd expect to use them):**

*   **`SIDES`:** Integer - the number of sides/faces on the die (e.g., 6, 11, 23, etc.)
*   **`HEIGHT`:** Integer - the overall height (and diameter) of the spherical die. I'd rather this be a parameter than the radius directly.
*   **`FACE_DEPTH`:**  Integer - percentage (0-100) of the maximum possible face depth (sagitta).
*   **`FONT_DEPTH`:** Integer - percentage (0-100) of the maximum possible face depth to use for the text engravings.
*   **`FONT_SIZE`:** Integer - size of the engraved numbers.
*   **`FONT`:** String (enum) - the font to be used. Specifically, I'd like the options from the example: "write/Letters.dxf", "write/orbitron.dxf", "write/knewave.dxf", "write/BlackRose.dxf", and "write/braille.dxf"
*   **`RESOLUTION`:** Integer - resolution of the sphere mesh, for rendering speed. 100, 250, and 400 are what I'm seeing.
*  **`RADIUS`:** Calculated from `HEIGHT/2` internally and is not an input to the user

**Additional Requirements/Clarifications:**

*   **Units:**  I'm using millimeters (mm) as the base unit.
*   **Library:** It looks like the existing design is using a `Write.scad` library for the font handling. I'd want to make sure that is included, but don't include it in the response. I'm not concerned if it's using the DXF fonts, but I just don't want to have to redo the letters.
*   **Output:** The final output should be a 3D model of the die that can be exported as STL for 3D printing.
*   **Golden Spiral:** The example I've included uses a golden selection spiral to align the faces. I like this, but if there's a better way to do it, I'm open to suggestions. It looks like the faces are rotated so that face 1 is always 'z-down'.

**My Goal:**

The primary reason for this is to be able to create a die with any number of sides for tabletop games, and to be able to customize the look, depth, and fonts.

Can you help me create this OpenSCAD design?

[Assistant]
ndie.scad:
```
include <write/Write.scad>

SIDES = 11;
//in mm
HEIGHT = 40;
//percent
FACE_DEPTH = 30;//[0:100]
//percent
FONT_DEPTH = 20;//[0:100]
//in mm
FONT_SIZE = 6;
//style
FONT = "write/Letters.dxf";//["write/Letters.dxf":Standard,"write/orbitron.dxf":Orbitron,"write/knewave.dxf":Knewave,"write/BlackRose.dxf":BlackRose,"write/braille.dxf":Braille]
RESOLUTION = 250;//[100:Low,250:Medium,400:High]

// radius of sphere
RADIUS = HEIGHT/2;

// approx depth of arc on sphere
SAGITTA_MAX = RADIUS-RADIUS*sin(90 - acos(1-2/SIDES));

// dot product of vector with z unit vector
function dot_z(x,y,z) = acos(z/sqrt(x*x + y*y + z*z));

// cross product of vector with z unit vector
function cross_z(x,y,z) = [y,-x,0];

/*
 * Wrapper for write that will disambiguate strings with only sixes or nines
 * by underlining the entire string
 */
module safe_write(word, t, h, center, font) {
	write(word, t=t, h=h, center=true, font=FONT);

	// only if string is all sixes or nines
	six_search = search("6", word);
	nine_search = search("9", word);
	if ((len(six_search) + len(nine_search)) == len(word))
	{
		translate([0,-6*h/10,0]) cube(size = [len(word)*(6*h/10),h/10,t], center = true);
	}
}

/*
 * Face number pair
 */
module face_pair(n, radius, face_depth, font_depth)
{
	translate([0,0,-2*(radius-face_depth)])
	{
		cube(2*radius, center=true);
	}
	translate([0,0,radius-font_depth/2])
	{
		safe_write(str(n), t=font_depth, h=FONT_SIZE, center=true, font=FONT);
	}
}

/*
 * N-sided face face number pairs
 *
 * Golden selection spiral adopted from:
 * http://www.xsi-blog.com/archives/115
 */
module n_face_pairs(n, radius, face_depth, font_depth)
{
	inc = 180 * (3 - sqrt(5));
	off = 2.0 / n;

	for (k = [0:n-1])
	{
		assign (y = k * off - 1 + (off / 2))
		{
			assign(r = sqrt(1 - y*y), phi = k * inc)
			{

				assign(x=cos(phi)*r, z=sin(phi)*r)
				{
					// rotate by angle of dot product about the cross product vector
					// to align z unit vector to direction of given vector
					rotate(dot_z(x,y,z), cross_z(x,y,z))
					{
						face_pair(k+1, radius, face_depth, font_depth);
					}
				}
			}
		}
	}
}

/*
 * Rotate golden selection spiral where k=0, so that face of side-1 is
 * always z-down.
 */
module n_face_pairs_up(n, radius, face_depth, font_depth, resolution)
{
	x = sqrt( 2/n - 1/(n*n) );
	y = 1/n - 1;
	z = 0;
	// rotate by angle of dot product about the cross product vector
	// to align z unit vector to direction of given vector
	rotate(dot_z(x,y,z) + 180, cross_z(x,y,z)) {
		n_face_pairs(n, radius, face_depth, font_depth);
	}
}

module n_die(n, radius, face_depth, font_depth, resolution)
{
	difference()
	{
		sphere(radius, $fn=resolution);

		// special handeling for low values of n
		if (n == 2)
		{
			face_pair(1, radius, face_depth, font_depth);
			rotate(180, [1,0,0]) face_pair(2, radius, face_depth, font_depth);

		}
		else if (n == 4)
		{
			face_pair(1, radius, face_depth, font_depth);
			rotate(110, [1,0,0]) face_pair(2, radius, face_depth, font_depth);
			rotate(120, [0,0,1]) rotate(110, [1,0,0]) face_pair(3, radius, face_depth, font_depth);
			rotate(240, [0,0,1]) rotate(110, [1,0,0]) face_pair(4, radius, face_depth, font_depth);

		}
		else if (n == 6)
		{
			face_pair(1, radius, face_depth, font_depth);
			rotate(90, [1,0,0]) face_pair(2, radius, face_depth, font_depth);
			rotate(180, [1,0,0]) face_pair(6, radius, face_depth, font_depth);
			rotate(270, [1,0,0]) face_pair(5, radius, face_depth, font_depth);
			rotate(90, [0,1,0]) face_pair(3, radius, face_depth, font_depth);
			rotate(270, [0,1,0]) face_pair(4, radius, face_depth, font_depth);

		// else perform golden selection spiral
		} else {
			n_face_pairs_up(n, radius, face_depth, font_depth);
		}
	}
}

n_die(SIDES, RADIUS, FACE_DEPTH/100 * SAGITTA_MAX, FONT_DEPTH/25 * SAGITTA_MAX, RESOLUTION);

```


[User]
Okay, I want to make a personalized heart ornament, like a tag or something. I'd like to be able to set the initials, the height of the heart, and the height of the letters. The heart itself should be made up of two intersecting cylinders. The letters should be extruded on top of the heart.

I need it all to be parametric, so I can easily change the size later. The letters should be a specific font – it's called "Spiff Sans" – and I've included the code for that, so it doesn't need to be generated.

Specifically:

*   **Initials:** A text input field where I can enter two letters.
*   **Heart Height:** A number input field, defaulting to 3mm, allowing values from 3mm to 10mm.
*   **Letter Height:** A number input field, defaulting to 1mm, allowing values from 1mm to 5mm.
*   **Heart Shape:** The base heart shape should be created by the intersection of two cylinders that are scaled to be eliptical, with one rotated by a variable that is set to 90 degrees.
*   **Letter Placement:** The letters should be placed on top of the heart, rotated 135 degrees and a translation along the x-axis
*   **Font:** I'm providing all of the code for the "Spiff Sans" font as a series of `spiffsans_` modules; I don't need you to generate that.

Could you generate an OpenSCAD file for this?

[Assistant]
initialheart.scad:
```
// Initialed Heart Ornament
// By Jake Poznanski
// preview[view:north east, tilt:top]

//Enter your two letter initials
initials = "AJ";

//The height of the main heart body in mm
height = 3; // [3:10]

//The height of the letters themselves
letter_height = 1; // [1:5]

//Internal variables
heartAngle = 90*1;
size = 14*1;
ratio = 48/28;
elipseSize = [size * ratio,size,height];

//Importing this module directly for Makerbot customizer script
// Spiff Sans
// Author: Stuart P. Bentley <stuart@testtrack4.com>

module spiffsans_puts(letters, count, offsets)
{
  for(i=[0:count-1])
  {
    translate([offsets[i],0,0])
    {
      spiffsans_lookup_draw(letters[i]);
    }
  }
}

module spiffsans_lookup_draw(char)
{
  if(char==" ") {} //silly lookup, space can't be printed
  else if(char =="!") spiffsans_exclaim();
  else if(char =="-") spiffsans_hyphen();
  else if(char ==".") spiffsans_period();
  else if(char =="0") spiffsans_digit_0();
  else if(char =="1") spiffsans_digit_1();
  else if(char =="2") spiffsans_digit_2();
  else if(char =="3") spiffsans_digit_3();
  else if(char =="4") spiffsans_digit_4();
  else if(char =="5") spiffsans_digit_5();
  else if(char =="6") spiffsans_digit_6();
  else if(char =="7") spiffsans_digit_7();
  else if(char =="8") spiffsans_digit_8();
  else if(char =="9") spiffsans_digit_9();
  else if(char ==":") spiffsans_colon();
  else if(char =="?") spiffsans_question();
  else if(char =="A" || char == "a") spiffsans_capital_a();
  else if(char =="B" || char == "b") spiffsans_capital_b();
  else if(char =="C" || char == "c") spiffsans_capital_c();
  else if(char =="D" || char == "d") spiffsans_capital_d();
  else if(char =="E" || char == "e") spiffsans_capital_e();
  else if(char =="F" || char == "f") spiffsans_capital_f();
  else if(char =="G" || char == "g") spiffsans_capital_g();
  else if(char =="H" || char == "h") spiffsans_capital_h();
  else if(char =="I" || char == "i") spiffsans_capital_i();
  else if(char =="J" || char == "j") spiffsans_capital_j();
  else if(char =="K" || char == "k") spiffsans_capital_k();
  else if(char =="L" || char == "l") spiffsans_capital_l();
  else if(char =="M" || char == "m") spiffsans_capital_m();
  else if(char =="N" || char == "n") spiffsans_capital_n();
  else if(char =="O" || char == "o") spiffsans_capital_o();
  else if(char =="P" || char == "p") spiffsans_capital_p();
  else if(char =="Q" || char == "q") spiffsans_capital_q();
  else if(char =="R" || char == "r") spiffsans_capital_r();
  else if(char =="S" || char == "s") spiffsans_capital_s();
  else if(char =="T" || char == "t") spiffsans_capital_t();
  else if(char =="U" || char == "u") spiffsans_capital_u();
  else if(char =="V" || char == "v") spiffsans_capital_v();
  else if(char =="W" || char == "w") spiffsans_capital_w();
  else if(char =="X" || char == "x") spiffsans_capital_x();
  else if(char =="Y" || char == "y") spiffsans_capital_y();
  else if(char =="Z" || char == "z") spiffsans_capital_z();
  else echo(str("Unknown character: ", char));
}

module spiffsans_exclaim()
{
  polygon(points=[[0,3],[0,10],[2,10],[2,3],[0,0],[0,2],[2,2],[2,0]],paths=[[0,1,2,3],[4,5,6,7]]);
}

module spiffsans_hyphen()
{
  polygon(points=[[0,4],[0,6],[4,6],[4,4]],paths=[[0,1,2,3]]);
}

module spiffsans_period()
{
  polygon(points=[[0,0],[0,2],[2,2],[2,0]],paths=[[0,1,2,3]]);
}

module spiffsans_digit_0()
{
  polygon(points=[[5,0],[1,0],[0,2],[0,8],[1,10],[5,10],[6,8],[6,2],[2,8],[4,8],[2,5],[2,2],[4,5],[4,2]],paths=[[0,1,2,3,4,5,6,7],[8,9,10],[11,12,13]]);
}

module spiffsans_digit_1()
{
  polygon(points=[[0,0],[0,2],[2,2],[2,7],[0,7],[2,10],[4,10],[4,2],[5,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

module spiffsans_digit_2()
{
  polygon(points=[[0,0],[0,2],[4,6],[4,8],[2,8],[2,6],[0,6],[0,8],[1,10],[5,10],[6,8],[6,5.5],[2.5,2],[6,2],[6,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]]);
}

module spiffsans_digit_3()
{
  polygon(points=[[1,0],[0,2],[0,4],[2,4],[2,2],[4,2],[4,4],[3,4],[3,6],[4,6],[4,8],[2,8],[2,6],[0,6],[0,8],[1,10],[5,10],[6,8],[6,6],[5.5,5],[6,4],[6,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]]);
}

module spiffsans_digit_4()
{
  polygon(points=[[4,0],[4,4],[0,4],[0,10],[2,10],[2,6],[4,6],[4,10],[6,10],[6,0]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

module spiffsans_digit_5()
{
  polygon(points=[[0,0],[0,2],[4,2],[4.5,3],[4,4],[0,4],[0,10],[6,10],[6,8],[2,8],[2,6],[5,6],[6,4],[6,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]]);
}

module spiffsans_digit_6()
{
  polygon(points=[[1,0],[0,2],[0,8],[1,10],[5,10],[5.5,8],[2,8],[2,6],[5,6],[6,4],[6,2],[5,0],[2,2],[2,4],[4,4],[4,2]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11],[12,13,14,15]]);
}

module spiffsans_digit_7()
{
  polygon(points=[[1,0],[1,3],[3,8],[0,8],[0,10],[5,10],[5,8],[3,3],[3,0]],paths=[[0,1,2,3,4,5,6,7,8]]);
}

module spiffsans_digit_8()
{
  polygon(points=[[1,0],[0,2],[0,4],[0.5,5],[0,6],[0,8],[1,10],[5,10],[6,8],[6,6],[5.5,5],[6,4],[6,2],[5,0],[2,6],[2,8],[4,8],[4,6],[2,2],[2,4],[4,4],[4,2]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13],[14,15,16,17],[18,19,20,21]]);
}

module spiffsans_digit_9()
{
  polygon(points=[[1,0],[0,2],[4,2],[4,4],[1,4],[0,6],[0,8],[1,10],[5,10],[6,8],[6,2],[5,0],[2,6],[2,8],[4,8],[4,6]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11],[12,13,14,15]]);
}

module spiffsans_colon()
{
  polygon(points=[[0,6],[0,8],[2,8],[2,6],[0,2],[0,4],[2,4],[2,2]],paths=[[0,1,2,3],[4,5,6,7]]);
}

module spiffsans_question()
{
  polygon(points=[[2,3],[4,6],[4,8],[2,8],[2,6],[0,6],[0,8],[2,10],[4,10],[6,8],[6,6],[4,3],[2,0],[2,2],[4,2],[4,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11],[12,13,14,15]]);
}

module spiffsans_capital_a()
{
  polygon(points=[[0,0],[0,8],[1,10],[5,10],[6,8],[6,0],[4,0],[4,4],[2,4],[2,0],[2,6],[2,8],[4,8],[4,6]],paths=[[0,1,2,3,4,5,6,7,8,9],[10,11,12,13]]);
}

module spiffsans_capital_b()
{
  polygon(points=[[0,0],[0,10],[5,10],[6,8],[6,6],[5.5,5],[6,4],[6,2],[5,0],[2,6],[2,8],[4,8],[4,6],[2,2],[2,4],[4,4],[4,2]],paths=[[0,1,2,3,4,5,6,7,8],[9,10,11,12],[13,14,15,16]]);
}

module spiffsans_capital_c()
{
  polygon(points=[[6,0],[1,0],[0,2],[0,8],[1,10],[6,10],[6,8],[2,8],[2,2],[6,2]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

module spiffsans_capital_d()
{
  polygon(points=[[0,0],[0,10],[5,10],[6,8],[6,2],[5,0],[2,2],[2,8],[4,8],[4,2]],paths=[[0,1,2,3,4,5],[6,7,8,9]]);
}

module spiffsans_capital_e()
{
  polygon(points=[[0,0],[0,10],[5,10],[5,8],[2,8],[2,6],[4,6],[4,4],[2,4],[2,2],[5,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11]]);
}

module spiffsans_capital_f()
{
  polygon(points=[[0,0],[0,10],[5,10],[5,8],[2,8],[2,6],[4,6],[4,4],[2,4],[2,0]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

module spiffsans_capital_g()
{
  polygon(points=[[6,0],[1,0],[0,2],[0,8],[1,10],[5.5,10],[6,8],[2,8],[2,2],[4,2],[4,6],[6,6]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11]]);
}

module spiffsans_capital_h()
{
  polygon(points=[[0,0],[0,10],[2,10],[2,6],[4,6],[4,10],[6,10],[6,0],[4,0],[4,4],[2,4],[2,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11]]);
}

module spiffsans_capital_i()
{
  polygon(points=[[0,0],[0,2],[1.5,2],[1.5,8],[0,8],[0,10],[5,10],[5,8],[3.5,8],[3.5,2],[5,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11]]);
}

module spiffsans_capital_j()
{
  polygon(points=[[0.5,0],[0,1],[0,4],[2,4],[2,2],[4,2],[4,8],[0,8],[0,10],[6,10],[6,1],[5.5,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11]]);
}

module spiffsans_capital_k()
{
  polygon(points=[[0,0],[0,10],[2,10],[2,6],[4,6],[4,10],[6,10],[6,6],[5,5],[6,4],[6,0],[4,0],[4,4],[2,4],[2,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]]);
}

module spiffsans_capital_l()
{
  polygon(points=[[0,0],[0,10],[2,10],[2,2],[5,2],[5,0]],paths=[[0,1,2,3,4,5]]);
}

module spiffsans_capital_m()
{
  polygon(points=[[0,0],[0,8],[1,10],[9,10],[10,8],[10,0],[8,0],[8,8],[6,8],[6,0],[4,0],[4,8],[2,8],[2,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13]]);
}

module spiffsans_capital_n()
{
  polygon(points=[[0,0],[0,8],[1,10],[5,10],[6,8],[6,0],[4,0],[4,8],[2,8],[2,0]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

module spiffsans_capital_o()
{
  polygon(points=[[5,0],[1,0],[0,2],[0,8],[1,10],[5,10],[6,8],[6,2],[2,2],[2,8],[4,8],[4,2]],paths=[[0,1,2,3,4,5,6,7],[8,9,10,11]]);
}

module spiffsans_capital_p()
{
  polygon(points=[[0,0],[0,10],[5,10],[6,8],[6,6],[5,4],[2,4],[2,0],[2,6],[2,8],[4,8],[4,6]],paths=[[0,1,2,3,4,5,6,7],[8,9,10,11]]);
}

module spiffsans_capital_q()
{
  polygon(points=[[1,0],[0,2],[0,8],[1,10],[5,10],[6,8],[6,0],[2,2],[2,8],[4,8],[4,2]],paths=[[0,1,2,3,4,5,6],[7,8,9,10]]);
}

module spiffsans_capital_r()
{
  polygon(points=[[0,0],[0,10],[5,10],[6,8],[6,6],[5.5,5],[6,4],[6,0],[4,0],[4,4],[2,4],[2,0],[2,6],[2,8],[4,8],[4,6]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11],[12,13,14,15]]);
}

module spiffsans_capital_s()
{
  polygon(points=[[1,0],[0,2],[0,3],[2,3],[2,2],[4,2],[4,4],[1,4],[0,5],[0,8],[1,10],[5,10],[6,8],[6,7],[4,7],[4,8],[2,8],[2,6],[5,6],[6,5],[6,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]]);
}

module spiffsans_capital_t()
{
  polygon(points=[[2,0],[2,8],[0,8],[0,10],[6,10],[6,8],[4,8],[4,0]],paths=[[0,1,2,3,4,5,6,7]]);
}

module spiffsans_capital_u()
{
  polygon(points=[[1,0],[0,2],[0,10],[2,10],[2,2],[4,2],[4,10],[6,10],[6,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

module spiffsans_capital_v()
{
  polygon(points=[[2,0],[0,4],[0,10],[2,10],[2,4],[4,4],[4,10],[6,10],[6,4],[4,0]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

module spiffsans_capital_w()
{
  polygon(points=[[9,0],[1,0],[0,2],[0,10],[2,10],[2,2],[4,2],[4,10],[6,10],[6,2],[8,2],[8,10],[10,10],[10,2]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13]]);
}

module spiffsans_capital_x()
{
  polygon(points=[[0,0],[0,4],[1,5],[0,6],[0,10],[2,10],[2,6],[4,6],[4,10],[6,10],[6,6],[5,5],[6,4],[6,0],[4,0],[4,4],[2,4],[2,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]]);
}

module spiffsans_capital_y()
{
  polygon(points=[[2,0],[2,4],[1,4],[0,6],[0,10],[2,10],[2,6],[4,6],[4,10],[6,10],[6,6],[5,4],[4,4],[4,0]],paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13]]);
}

module spiffsans_capital_z()
{
  polygon(points=[[0,0],[0,2],[3,8],[0,8],[0,10],[5,10],[5,8],[2,2],[5,2],[5,0]],paths=[[0,1,2,3,4,5,6,7,8,9]]);
}

union()
{

translate([0,0,height])
scale([size/10,size/10,1])
linear_extrude(height=letter_height)
rotate(heartAngle+(90-heartAngle)/2+45)
translate([-2.0*(size/10)-5, -size/20,0])
spiffsans_puts(initials, 2, [0,10]);

union(){
intersection(){

union(){
scale(elipseSize)
cylinder(h = 1, r = 1,$fn=60);

rotate(heartAngle)
scale(elipseSize)
cylinder(h = 1, r = 1,$fn=60);
}

rotate(heartAngle+(90-heartAngle)/2+45)
translate([-elipseSize.x,0,-height/2])
cube(size = elipseSize*2);
}

intersection(){

intersection(){
scale(elipseSize)
cylinder(h = 1, r = 1,$fn=60);

rotate(heartAngle)
scale(elipseSize)
cylinder(h = 1, r = 1,$fn=60);
}

rotate(heartAngle+(90-heartAngle)/2+45)
translate([-elipseSize.x,-elipseSize.x,-height/2])
cube(size = elipseSize*2);
}
}

}

```


[User]
Okay, I need a parametric terminal block generator in OpenSCAD. I'd like it to be configurable for the number of terminals, the screw size for each terminal, and the overall dimensions of the block. Here are the details:

**Basic Terminal Block:**

*   **Number of Terminals:** This should be a configurable integer.
*   **Terminal Screw Hole:** This should have a configurable diameter, and should be a hole right through the block.
*   **Terminal Height:** The height of each terminal block, should be configurable.
*   **Terminal Width:** The width of each individual terminal, should be configurable.
*   **Block Width:** The width of the main block, should be configurable.
*   **Terminal Divider:** The width of the space/divider between the terminals, should be configurable.
*   **Nut Thickness:** The thickness of the nut that the screw will thread into, should be configurable.
*   **Clearance:** A configurable clearance value, for nut holes and screw holes, to account for printing tolerances.

**Mounting Tabs:**

*   **Mounting Tabs:** A boolean (yes/no) option to include mounting tabs on either end of the block.
*   **Mount Tab Length:** Configurable length of the mounting tabs.
*   **Mount Tab Height:** Configurable height of the mounting tabs.
*  **Mount Hole Diameter:** Configurable diameter for the mounting holes in the tabs.

**Nut/Bolt Options:**

*   **Nut/Bolt Type:** This should be a selection between three options:
    *   **Enclosed Nuts:** The design will include recessed hex pockets for nuts to be inserted *during* the print (so the print has to be paused to insert them). The nuts will be captive in the print.
    *   **Slotted Nuts:** The design will include a slot so a nut can be inserted *after* the print. These would be a recessed hole for the nut, and a channel wide enough to slide the nut into place.
    *   **Bolted:** The design should include a larger recessed hole and clearance for a bolt head to be inserted from below *after* the print, the bolt will thread into the printed plastic.

**Additional Considerations:**

*   The nut height will be calculated automatically based on whether enclosed, slotted, or bolted is selected, based on the `TerminalHeight` and `NutThickness` parameters. For Enclosed, the nut height should sit 2mm from the top. For slotted it should sit 2mm from the top and for bolted it should be the same as the nut thickness.
*  All cylinders for holes should be made with `$fn=50`. All nut hexes should be made with `$fn=6`.
*  The clearance should be applied to all appropriate holes.
*   The design should be generated using loops so multiple terminals are created automatically.
*   The code should be well-commented and easy to understand.
*   I am not using any libraries for this code (BOSL, MCAD, etc.).

Could you generate an OpenSCAD file based on these requirements?

[Assistant]
TerminalBlock.scad:
```
//
// Terminal Block
//
//

//How many terminals in the block
NoTerminals = 3; // [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
// Diameter of screw in terminal
TerminalHoleSize = 4;
//Terminal Height in mm
TerminalHeight = 8;
//Width of each terminal in mm
TerminalWidth = 12;
//Block Width in mm
BlockWidth = 14;
//Terminal Divider width in mm
TerminalDivider = 2.4;
// Nut Thickness
NutThickness = 4;
// Tabs required
ReqTabs = "Yes"; //[Yes,No]
//Mounting tab length
MountTabLength = 8;
//Mounting tab Height
MountTabHeight = 8;
//Mount tab hole diameter in mm
MountHole = 4;
//Clearence
Clearence = 0.5;
//Are the nuts Enclosed (nuts added during build - print must be paused) or Slotted (nuts added after build) or Bolted that is Hex head bolt inserted post print.
Enclose =1; //[1:Enclosed, 0: Slotted, 2:Bolted]

//Height of the top of the nut above the base
NutHeight = Enclose > 1 ? NutThickness : TerminalHeight - 2;
//echo (NutHeight);

for (i =[ 0:NoTerminals - 1]) {
	translate ([0,(TerminalWidth + TerminalDivider) * i ,0])
		terminal() ;
	}

translate ([- TerminalDivider ,- TerminalDivider , 0])
		cube([BlockWidth + TerminalDivider * 2,
				TerminalDivider ,
				TerminalHeight + TerminalDivider]);
if (ReqTabs == "Yes") {
	translate ([0,-TerminalDivider,0]) mirror ([0,1,0]) tab() ;
	translate ([0,(TerminalWidth + TerminalDivider) * NoTerminals,0]) tab() ;
	}

module tab() {
	difference () {
		translate ([MountHole /2, 0,0])
			minkowski() {
				cube([BlockWidth - MountHole,
					MountTabLength ,
					MountTabHeight ]);
				cylinder ( r= (MountHole + Clearence )/2 , h =.01 , $fn=50);
			}
		translate ([BlockWidth /2, MountTabLength /2, -1]) cylinder ( r= (MountHole + Clearence )/2 , h = MountTabHeight + 2, $fn=50);
	}
}

module terminal() {
difference() {
	union() {
		cube([BlockWidth,TerminalWidth,TerminalHeight]);
		translate ([- TerminalDivider , TerminalWidth , 0])
				cube([BlockWidth + (TerminalDivider * 2),
					TerminalDivider + 0.01 ,
					TerminalHeight + TerminalDivider]);
	}
	translate ([BlockWidth / 2,
				TerminalWidth / 2,
				NutHeight  + .5])
			cylinder ( r= (TerminalHoleSize + Clearence )/2 ,
				h = TerminalHeight, $fn=50);

	translate ([BlockWidth /2,
		TerminalWidth /2,  -1])
		cylinder ( r= ( TerminalHoleSize + Clearence ) / 2 ,
		h = NutHeight - (NutThickness / 2) + 1, $fn=50);

	translate ([BlockWidth /2,
				TerminalWidth /2,
				NutHeight - NutThickness ])
			cylinder ( r= TerminalHoleSize + ( Clearence ) , h = NutThickness , $fn=6);
	if (Enclose == 0) {
		translate ([BlockWidth /2 + .1,
					(TerminalWidth  - (TerminalHoleSize + Clearence) * 1.8) / 2 ,
					NutHeight - NutThickness  ])
			cube([ BlockWidth /2	, (TerminalHoleSize + Clearence) * 1.8, NutThickness]);
		}
	}
}
```