[User]
Okay, so I'm trying to design a pill cutter in OpenSCAD, and I'm running into some issues with the current design. I've got a basic structure working, but it's not quite cutting the pill properly, and I'd really like a more robust, parametric design.

Here's a breakdown of what I need, and some of the issues I'm having:

**Core Functionality:**

*   **Pill Cavity:** The design needs a cavity to hold the pill. This cavity should accommodate pills of varying sizes. I need to define the pill diameter (`pd`), the total pill height which includes convex curve (`pt`), and the cylindrical height of the pill, which doesn't include convex curve (`pt2`). The design should calculate the size of the convex curve from these.

*   **Cutting Mechanism:** The cutting action is achieved by pressing the pill into a sharp edge in a "top" part, which is mirrored in the bottom part to hold pill. Currently it uses some cubes for this which is not perfect. I need a proper cutting edge shape, perhaps a rounded edge.

*   **Two-Part Design:** The cutter is composed of a top and bottom part. Both have similar outer shape, and both have a cavity for pill. The top part has an extrusion to press on pill, and bottom is just a simple flat base that support the pill.

*   **Parametric:** I want the design to be completely parametric, meaning I can easily adjust pill size parameters and the whole cutter scales automatically.

**Specific Parameters I Need:**

*   `pd`: Pill diameter.
*   `pt`: Total pill height which includes convex curve.
*    `pt2`: Cylindrical pill height (without convex curve).

*   `bh`: Base height of each part. It could be linked to `pt` value somehow.
*   Possibly the size of the cutting edge.

**Problems with my current design:**

*   **Convex curve calculation:** I'm not sure if my current calculation for the sphere sizes for the pill cavity is correct. It looks okay but the geometry can be improved.
*   **Cutting edge**: The cutting edge implemented with cubes is not very smooth, and the shape could be better.
*   **Parametric scaling**: The size of the posts are not scaling with other parameters.
*   **Overall robustness**: The model doesn't feel very solid. I'd like some nice, chamfered edges on the outside and the model needs to hold tightly.

**Desired Improvements:**

*   **Proper Spherical Cavity:** Create a smooth, mathematically accurate spherical cavity for the pill based on the `pd`, `pt`, and `pt2` parameters.
*   **Clean Cutting Edge:** Implement a better cutting edge that is not just cubes.
*   **Robust Design:** Ensure the overall design is solid and fits together nicely, perhaps with registration features to keep the top and bottom aligned.
*   **Chamfers/Fillets:** Add chamfers to the exterior edges to make the model look and feel nicer.
*   **Parametric Posts/Alignment Features**: Make the posts or other alignment features scale with the overall size of the cutter.
*   **Optional Locking Feature**: Include or have option to include a simple locking mechanism to keep it closed during storage, perhaps using pegs.
*   **Clearance** Add a clearance between the top and bottom for easy assembling.

**Specific OpenSCAD Library:**
*    I want the code to utilize the `BOSL2` library for things like parametric shapes, chamfers, and registration features. If there is a better option please use that.

Could you please provide me with an OpenSCAD file that implements this? I'm looking for something well-structured and easy to customize. Also, I'm okay with not having a locking mechanism initially, and can always add one later.

[Assistant]
pillcutter2.scad:
```
//Pill diameter
pd=9.6;//

//Pill hight1
pt=4.2;//

//Pill hight2
pt2=2.6;//

//Parts sellect
part="top";//[pill,top,bottom]

pc=(pt-pt2)/2;//Convex value
sp=pd/(tan(2*atan(2*pc/pd))*2)+pd;// convex diameter

//Base hight
bh=pt/2+1;

//echo(sp);// convex diameter

module pill()
{
rotate([0,0,0])intersection(){
cylinder(h=10,r=pd/2+0.5,center=true,$fn=48);
translate([0,0,-sp+(pt2-0.5)])sphere(r=sp+0.5,center=true,$fn=96);
translate([0,0,sp-(pt2-0.5)])sphere(r=sp+0.5,center=true,$fn=96);
								}
}

module post()
{
translate([21,21,bh/2])cube(size=[5,5,bh*2], center=true);
translate([-21,21,bh/2])cube(size=[5,5,bh*2], center=true);
translate([-21,-21,bh/2])cube(size=[5,5,bh*2], center=true);
translate([21,-21,bh/2])cube(size=[5,5,bh*2], center=true);
}

union(){
if(part=="pill") {
		translate([0,0,0])pill();
				 }
		else if (part=="bottom"){
union(){
difference(){
cube(size=[46,46,bh], center=true);
translate([0,0,pt/2])pill();
translate([-0.4,-20.5,-1])cube(size=[0.8,41,20], center=false);
translate([-20.5,-0.4,-1])cube(size=[41,0.8,20], center=false);
			}
post();
		}
					}
				else if (part=="top"){
translate([0,0,0]){
difference(){
		cube(size=[44,44,bh], center=true);
		translate([0,0,pt/2])pill();
		translate([-0.4,-20.5,-10])cube(size=[0.8,41,20], center=false);
		translate([-20.5,-0.4,-10])cube(size=[41,0.8,20], center=false);
		translate([0,0,-4])rotate([0,45,0])cube(size=[5,41,5], center=true);
		translate([0,0,-4])rotate([45,0,0])cube(size=[41,5,5], center=true);
		translate([0,0,-pt])scale([0.95,0.95,2])post();

			}
		}
					}
					}

```


[User]
Okay, I'm looking to design a parametric clip for securing a build platform, and I've seen some examples that get me close to what I need, but I'd like to make some changes and have it be more flexible.

Essentially, the clip needs to grasp a platform from the top and bottom, using either a round or a square style of grip. The existing example I've seen uses a "spine" with two "grips" on either side that grab the platform. I'm thinking of a design where the user can control the following parameters:

*   **`style`**: A string, either `"round"` or `"square"`, to choose the grip style.
*   **`platformGap`**: The height of the gap between the two grips that the platform fits into. This is a critical dimension.
*   **`length`**: The overall length of the clip (including the grip areas and the central spine).
*   **`wallThickness`**: The thickness of the clip material, which impacts its strength.
*  **`diameter`**: The diameter of the round grip, or the size of the square grip.

For the "round" style, the existing examples are pretty close. The grip is formed using an arc that wraps around the platform edge, with a hinged portion that opens to accept the platform. I’d like to be able to adjust:
* the curvature of the grip portion
* the hinge angle
* the clip angle

For the "square" style grip, it should have a rectangular central spine with two opposing grips on each side. These grips will be semi-cylindrical with a small, almost-rectangular "prong" section that actually contacts the platform. I need to be able to set the following:
* The radius of the semi-cylindrical section.
* The radius of the tips of the prongs.
* The depth of the prongs
* The space between the spine and the grip.

So, to sum up, I'd like a single OpenSCAD file with these parameters, switching between a "round" style and a "square" style of clip, with the above mentioned adjustable values. The general structure of the file should be like this:

```openscad
_style = "square"; // [round, square]

// height of the platform (use a slightly smaller value, about 0.2mm to make the clip tight)
_platformGap = 6.8;

// length of the clip
_length = 16.0;

// thickness of the clip.
_wallThickness = 1.0;

// size of the top of the clip
_diameter = 2.2;

////////////////////////////////////////////////////////////////////////
// Round clip parameters
_curvature = 25;
_clipAngle = 120;
_hingeAngle = 280;

////////////////////////////////////////////////////////////////////////
// Square clip parameters
_clipDepth = 1.0;
_space = 0.6;

// function to build and return the clip
module make($fn = 120) {
    if (_style == "round") {
      // round clip code
    } else {
      // square clip code
    }
}

make();
```
I am not using any external libraries for this project. Could you provide an OpenSCAD file that implements all of this functionality?

[Assistant]
dual_platform_clips_v1-0.scad:
```

_style = "square"; // [round, square]

// height of the platform (use a slightly smaller value, about 0.2mm to make the clip tight)
_platformGap = 6.8;

// length of the clip
_length = 16.0; //[4:50]

// thickness of the clip.
_wallThickness = 1.0;

// size of the top of the clip
_diameter = 2.2;

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

// only works from [0 to 180] degrees, runs clockwise from 12 o'clock
module smallArc(radius0, radius1, angle, depth) {
	thickness = radius1 - radius0;
	eps = 0.01;

	union() {
		difference() {
			// full arc
			cylinder(r=radius1, h=depth, center=true);
			cylinder(r=radius0, h=depth+2*eps, center=true);

			for(z=[0, 180 - angle]) {
				rotate([0,0,z])
				translate([-radius1,0,0])
				cube(size = [radius1*2, radius1*2, depth+eps], center=true);
			}
		}
	}
}

// arc from [0 to 360] degrees with rounded ends, runs clockwise from 12 o'clock, rounded tips not included in angle computation
module roundArc(radius0, radius1, angle, depth) {
	thickness = radius1 - radius0;
	cutAngle = 360-angle;
	centerRadius = (radius0 + radius1)/2.0;
	union() {
		if (angle > 180) {
			smallArc(radius0, radius1, 180, depth);
			rotate([0,0,-180]) {
				smallArc(radius0, radius1, angle-180, depth);
			}
		} else {
			smallArc(radius0, radius1, angle, depth);
		}

		// round ends
		for(t = [ [0,centerRadius,0],
					 [centerRadius * -sin(cutAngle), centerRadius * cos(cutAngle),0] ]) {
			translate(t) {
				cylinder(r=thickness/2, h=depth, center=true);
			}
		}
	}
}

module clip(arcRadius, angle, height, outerThickness, hingeRadius, hingeAngle, clipAngle, hingeGap) {
	ir = arcRadius;
	or = ir + outerThickness;
	clipRadius = hingeRadius;
	hx = (ir-hingeRadius)*sin(angle/2);
	hy = (ir-hingeRadius)*cos(angle/2);
	cx = (ir-clipRadius)*sin(angle/2);
	cy = (ir-clipRadius)*cos(angle/2);

	union() {

		difference() {
			//	spine
			rotate([0,0,angle/2]) {
				roundArc(ir, or, angle, height);
			}
		}

		// hinge
		translate([-hx, hy, 0])
		rotate([0,0,angle/2+hingeAngle]) {
			roundArc(hingeRadius, hingeRadius+outerThickness, hingeAngle, height);
		}

		// clip
		translate([cx, cy, 0])
		rotate([0,0,-angle/2]) {
			roundArc(clipRadius, clipRadius+outerThickness, clipAngle, height);
		}
	}
}

// reuse code from the arc clip
module buildRoundClip(gap, width, wall, diameter) {
	curvature = 25;
	clipAngle = 120;
	hingeAngle = 280 - wall*10;
	length = gap + diameter/3 + wall; // approximate
	radius = length / (2 *(curvature/100));
	angle = 2 * asin(length/(2*radius));

	translate([0,-radius,width/2]) {
		clip(radius, angle, width, wall, diameter/2, hingeAngle, clipAngle);
	}
}
////////////////////////////////////////////////////////////////////////

module buildSquareClip(gap, width, wall, diameter) {
	radius = diameter/2;
	tipRadius = diameter * 0.3;
	clipDepth = 1.0; // depth of the clip prongs
	space = 0.6; // space between clip and spine
	eps = 0.1;
	for (y = [-1,1]) {
		hull() {
			translate([0,y*(gap/2 + radius),0]) {
				cylinder(r=radius, h=width);
			}
			translate([-diameter*clipDepth,y*(gap/2 + tipRadius),0]) {
				cylinder(r=tipRadius, h=width);
			}
		}

		translate([(radius+space)/2, y*(gap/2 + diameter - wall/2), width/2]) {
			cube(size=[radius + space + eps, wall, width], center=true);
		}
	}
	translate([radius + space + wall/2, 0, width/2]) {
		cube(size=[wall, gap + diameter*2, width], center=true);
	}
}

module make($fn = 120) {
	if (_style == "round") {
		buildRoundClip(_platformGap, _length, _wallThickness, _diameter);
	} else {
		buildSquareClip(_platformGap, _length, _wallThickness, _diameter);
	}
}

make();
```


[User]
Okay, I'm looking to design a non-sliding roller bearing that can be printed in one piece, similar to the one I found on Thingiverse (thing:170691). I'm trying to improve upon that design, as I had some issues with my test print.

Here are the key features and goals for the bearing:

*   **Non-Sliding Action:** The core idea is to have rollers that roll on other rollers, minimizing sliding friction. This is achieved with a central set of rollers that allow the inner and outer rollers to roll, not slide.
*   **One-Piece Print:** I need to be able to print the entire bearing in a single print.
*   **Radial and Axial Load:** The bearing should be designed to handle both radial and axial forces.
*   **Reduced Friction:** The main goal is to reduce friction compared to a traditional roller bearing with a cage.
*   **Preload Consideration:** I understand that a preload might be beneficial, but I'd like to explore options that don't require post-assembly splitting of the inner or center pieces, at least initially.  I'm open to that if it’s a necessary design approach to achieve the desired performance.

The previous design had the following problems, which need to be addressed:

1.  **Fusing:** The outer rollers fused so hard to the outer ring that the ring broke during attempted separation. We need to ensure sufficient clearance to avoid this problem
2.  **Loose Inner Rollers:** The inner rollers were too loose and could almost fall out.  We need to improve the stability of the inner rollers
3.  **Non-Round Rollers:**  The small diameter rollers didn't print round enough to roll smoothly even under load. This might mean we need to adjust the size or number of rollers or how we generate the geometry.
4.  **Clearances:** We need to ensure that all the components have enough, but not too much, clearance. In my old file, `clr` variable was used, but there's no explanation on what the clearance values represent, or why they are set where they are. Can you please explain what all clearances need to be.

Here's a breakdown of the parameters I'd like to control:

*   **Outer Diameter (dout):** The outermost diameter of the bearing.
*   **Inner Diameter (dda):** The diameter of the inner race.
*   **Bearing Height (h0):** The overall height of the bearing.
*   **Number of Rollers (nroller):**  The number of rollers in each row.
*   **Roller Gap (fgap):** The factor that determines the gap between successive layers of rollers. This seems key in getting the balance right between sizes of different rollers.
*   **Clearance (clr):** The clearance to ensure things don’t fuse together and can roll. It is not clear how to calculate clearances, so the assistant should determine how to make them such that they ensure rolling while also having minimal space between the parts.
*   **Resolution ($fs and $fa):** Control resolution for smooth curves.

I would like the OpenSCAD code to be parametric. In particular, can you generate the code such that it supports the following:

*   A `part` variable, which allows me to render different parts of the bearing. The variable can take the values "whole", "mainrolls", and "rest", in accordance with the previous design.
*   The code should use `hull()` to create the slanted top and bottom retainers, but ensure that there is clearance between the retainers and the adjacent surfaces.
*   The code should define a module `cyl(rr,hh,outward,clr,dr,dh,s)` as in the previous design and utilize it to create the bearing’s components, including rollers, and also the inner and outer races.
*   The code should utilize `difference()` operations in the `whole` module to remove the central hexagon and the excess material to generate the inner and outer races.

I would like to utilize the following libraries for this design, as I am familiar with them:
*   BOSL2

Can you generate the OpenSCAD file for me with these parameters and considerations? I'm ready for an improved and robust design!

[Assistant]
Noslide-Rollerbearing.scad:
```
/*
2013 Lukas Süss aka mechadense
Nonsliding Roller Bearing
license: CC-By

Thingiverse Description:

This is a roller bearing that can be printed in one piece.

The special thing about it:
It has less sliding friction than a roller bearing WITH cage.
This is archived through the central rolls.
Those let the inner and outer rolls roll on rolls.
This bearing can can take radial and axial force.

It should run under (axial) load so that rolling is ensured !!
A preload would work to but than a zero clearnce print with post assembly is required.
(split the ring or better the center piece in two for that)

A very little unavoidable friction occurs at the slanted top & bottom retainers but this should'nt hurt.
(gears also have a tiny friction component)

It was inspired by this thing:
http://www.thingiverse.com/thing:55515
Which was inspired by emmets popular gear bearing.
http://www.thingiverse.com/thing:53451

Thingiverse Instructions:

There are a lot of parameter combinations that do not work.
Change one at a time.
Low roller nubers lead to the necessity of high bearing diameters.
High roller numers make the rollers small and lead to the necessity of high center piece diameters
( 8 to 12 rollers are good I think)

//#################################
//all main radii starting from center: rra; rr1; rr2; rr3; rrb; rout
*/

// for a dual color print
part = "whole"; // [whole,mainrolls,rest]

//#################
function p2(x) = pow(x,2); // shorthand
function p3(x) = pow(x,3); // shorthand
function p4(x) = pow(x,4); // shorthand
pi = 3.141592653*1;
phi = 1.61803*1;

// clearing
clr = 0.15;
// resolution: minimal segment length
$fs = 0.15;
// resolution: maximal segment angle
$fa = 3;
eps = 0.05*1;
//##################

// number of rollers (when decreasing increase fgap)
nroller = 8;

alpha = 360/nroller; a = alpha;
sa = sin(a); ca = cos(a);
ca2 = cos(a/2); sa2 = sin(a/2);

// outermost diameter of bearing (choose big enough for the rollers)
dout = 64;
rout = dout/2;
// diameter of the inner piece
dda = 20;
rra = dda/2;
// height of bearing
h0 = 12;
// hexagon width
w = 6.7;

// determines gap between innermost roller shell good: 1.414
f = 1.414;

fact = (1/2*f) * 2*pi/(2*nroller);
// r1 = 2*(rra + r1)*pi / (2*nroller)*f; ==>
r1 = rra*fact/(1-fact);
rr1 = rra+r1; // starting point for calculation

// !!! gap increasement factor (choose so that inner & outer rollers have similar size)
fgap = 1.8;
gap1 = 2*(rra+r1)*sin(alpha/2)-2*r1;
gap2 = gap1*fgap;
gap3 = gap2*fgap;

//echo("r1",r1);
//echo("rr1",rr1);
//echo("gap1 between r1 rollers is:",gap1);

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

// approach: choose the next radius such that the gap between r2 rolls is choosable.
// the placing radius of second roller row calculates to: (make sketch)
// rr2 = [(rra+r1)*cos(alpha/2)] + sqrt[(r1+r2)^2-((rra+r1)*sin(alpha/2))^2] // ****
// We demand that the gap between the r2 rollers should be the same as the one between the r1 ones:
// r2 + gap/2 = rr2*sin(alpha/2);
// solve for r2 in a Comp.Alg.Syst. =>

r2 = 1/2*1/(p2(sa2)-1)*(-sqrt(p2(gap2*sa2)-4*gap2*r1*p2(sa2)-2*gap2*rr1*sa*p2(sa2)+4*p2(r1*sa2)+4*r1*rr1*sa*p2(sa2))-2*r1*p2(sa2)-2*rr1*sa2*ca2+gap2);
rr2 = (rr1*cos(alpha/2)) + sqrt(p2(r1+r2)-p2(rr1*sin(alpha/2))); // ****

r3 = 1/2*1/(p2(sa2)-1)*(-sqrt(p2(gap3*sa2)-4*gap3*r2*p2(sa2)-2*gap3*rr2*sa*p2(sa2)+4*p2(r2*sa2)+4*r2*rr2*sa*p2(sa2))-2*r2*p2(sa2)-2*rr2*sa2*ca2+gap3);
rr3 = (rr2*cos(alpha/2)) + sqrt(p2(r2+r3)-p2(rr2*sin(alpha/2))); // ****

rrb = rr3+r3; // outer rolling diameter

// cut a quater away
debug = false;

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

if(debug==true)
{
  difference()
  {
    whole();
    translate([0,0,-50])
    cube([100,100,100]);
    #translate([0,50,50]) cune([10,10,10]);
  }
}
else
{
  whole();
}

module whole()
{
  if(part == "whole" || part == "mainrolls")
  {
    color("grey")
    for(i=[0:nroller-1]) // inner rollers
    {
      rotate(alpha*i,[0,0,1])
      translate([rr1,0,0]) //cylinder(r=r1,h=h0);
        cyl(r1,h0,true,clr/2);
    }
    color("grey")
    for(i=[0:nroller-1]) // outer rollers
    {
      rotate(alpha*i,[0,0,1])
      translate([rr3,0,0]) //cylinder(r=r3,h=h0);
        cyl(r3,h0,true,clr/2);
    }
  }

  if(part == "whole" || part == "rest")
  {
    for(i=[0:nroller-1]) // middle rollers
    {
      rotate(alpha*i+alpha/2,[0,0,1])
      translate([rr2,0,0]) //cylinder(r=r2,h=h0);
        cyl(r2,h0,false,clr/2);
    }

    // inner piece
    difference()
    {
      //cylinder(r=rra,h=h0);
      cyl(rra,h0-eps,false,clr/2);
      translate([0,0,-h0*0.1])
      cylinder(r=w/2/cos(30),h=h0*1.2,$fn=6);
    }

    // outer ring
    difference()
    {
      cylinder(r=rout,h=h0-eps);
      translate([0,0,-eps])
      // cylinder(r=rrb,h=h0+2*eps);
        cyl(rrb,h0+2*eps,true,clr/2);
    }
  }

} // end whole

//cyl(20,20);

module ring(rr,hh,outward=true,clr=0,dr=1,dh=1,s=1) {} // TO DEFINE!!

module cyl(rr,hh,outward=true,clr=0,dr=1,dh=1,s=1)
{
  if(outward==true)
  {
    cylinder(r=rr-dr-clr,h=hh);
    hull()
    {
      translate([0,0,s+dh]) cylinder(r=rr-clr,h=hh-2*s-2*dh);
      translate([0,0,s]) cylinder(r=rr-dr-clr,h=hh-2*s);
    }
  }
  else
  {
    cylinder(r=rr-clr,h=hh);
    hull()
    {
      cylinder(r=rr+dr-clr,h=s);
      cylinder(r=rr-clr,h=s+dh);
    }
    translate([0,0,hh]) scale([1,1,-1])
    hull()
    {
      cylinder(r=rr+dr-clr,h=s);
      cylinder(r=rr-clr,h=s+dh);
    }
  }
}

```


[User]
Okay, I'm looking to design a shoulder strap attachment for my 3D printer, so I can carry it around like a backpack. I have a lasercut wood frame printer, and I'd like to screw these attachments onto the front of the printer frame, both top and potentially bottom, then run a shoulder strap through them.

The current design I'm looking at is okay, but it’s not quite as parametric as I'd like it. Here's a breakdown of what I need to be adjustable:

*   **Frame Dimensions:** I need to be able to specify the thickness of the wood my printer frame is made of (`twood`) and the height of the wood frame where the attachments will mount (`hfront`). I also need to be able to control how far down the hook part of the piece goes inward (`hback`).
*   **Attachment Size:** The main body of the attachment needs adjustable thickness (`t`).  The overall height of the attachment is derived from the strap width and `t`, but should be able to adjust the total height `h` if needed. The width of the shoulder strap itself (`strapwidth`) should be a variable as well.
*   **Strap Guides:** The rounded ends where the straps connect should have an adjustable diameter (`dstrapround`) and a gap (`tstrapgap`) for the strap itself.
*   **Mounting:** There should be mounting holes included. Their diameter (`dbore`) should be adjustable. The code shouldn't just assume the holes are there, but rather offer a flag to turn them on/off.
*   **Edge Rounding:** All edges should have rounded corners and chamfers. The sizes of these roundings and chamfers (`b` and `b2`, respectively) should also be adjustable.

I like the way the current design uses `hull()` to create rounded shapes and the `r2box` module, but it's a bit clunky with all the offsets, and I want to see if it could be made more elegant.

Specifically, I would like:

*   A single module to generate the main body of the attachment, based off an adjustable rectangle, but with the rounded edges at all the outer edges.
*   A clear method of placing the strap guide at either end that can also add the rounded ends and the connecting section, rather than having to copy/paste transforms and then perform a hull.
*   Clear variables for adjusting the locations of the screw holes.
*   The ability to specify whether or not to close the strap loop, or leave it fully open for attaching different shoulder strap connectors.
*   I'd prefer the code be easier to read, ideally using some modules for repeated sub-sections of the design.

Can you create a parametric OpenSCAD file that incorporates these changes? I'm happy to continue refining it, but this should be a much better starting point.

[Assistant]
Printer-Shoulder-Strap-Carrying-Aid.scad:
```
/*
2013 Lukas Süss aka mechadense

Printer Shoulder Strap Carrying Aid
or
Printer as Backpack

Tired of carrying your printer with one hand?
Convert your lasercutted wood printer in a backpack.
Screw two of these onto the upper front frame of your printer and add two shoulder straps.

1) Measure your printers upper frontframe dimensions.
2) Print two or four pieces with high infill.
3) screw-mount two of them onto your printers upper front frame
   and optionally two on your printers lower front frame.
4) Attatch shoulder straps.
5) Take your printer on a stroll.

Note:
1) The standard parameters are for an ultimaker (top & bottom) and a belt of 24mm width.
2) Some back cushioning might be needed.

*/

$fa=5*1;
$fs=0.25*1;

// edge roundings
b = 2;
// side edge breakings
b2 = 1.5;

// length of hook downward portion (how far wood goes down inward)
hback = 4;
// total hight of printer wood front
hfront = 32;
// main piece wall thickness
t = 5;
// thickness of the wood of your printer
twood = 6;
// with of your sholderstrap
strapwidth = 24;

// total piece height
h = strapwidth+2*t;

// diameter of the rounded ends for the straps
dstrapround = 8;
rstrapround=dstrapround/2;

// channel thickness for shoulder strap to go through
tstrapgap = 2;
// diameter of screws
dbore = 3.1;

hole = false;
f = 0.61803*1;
s = 0*1;
//######################
/*
twood=6; hback=4; hfront=35; // ultimaker top
twood=6; hback=33; hfront=33; // ultimaker bottom
*/

difference()
{
  hull() // main body
  {
    r2box(b,b,hfront+t,2*t+twood,h-2*b2);
    r2box(b,b,hfront+t-2*b,2*t+twood-2*b,h);
  }
  // slot cut
  translate([t,0,0]) cube([hfront+t,twood,h*1.2],center=true);

  // back cutoff
  translate([t+hback,t,0]) cube([hfront+t,twood+t*2,h*1.2],center=true);

  translate([hfront/2-h/5,0,+(h/2-h/4)]) rotate(90,[1,0,0]) cylinder(r=dbore/2,h=100,center=true);
  translate([hfront/2-h/5,0,-(h/2-h/4)]) rotate(90,[1,0,0]) cylinder(r=dbore/2,h=100,center=true);
  //translate([hfront/2-h/5*2,0,0]) rotate(90,[1,0,0]) cylinder(r=dbore/2,h=100,center=true);
}

  // strap rounding ends
  translate([-(hfront+t)/2+rstrapround +s,-rstrapround-twood/2-t-tstrapgap,0])
    softend();
  translate([+(hfront+t)/2-dstrapround/2 -h/2 ,-dstrapround/2-twood/2-t-tstrapgap,0])
    scale([-1,1,1]) softend();

  if(hole==false) // closing
  {
    hull()
    {
      translate([-(hfront+t)/2+rstrapround +s,-rstrapround-twood/2-t-tstrapgap,0])
        conn();
      translate([+(hfront+t)/2-dstrapround/2 -h/2 ,-dstrapround/2-twood/2-t-tstrapgap,0])
         scale([-1,1,1]) conn();
    }
  }

  // sideward loop connectors
  hull()
  {
    translate([-(hfront+t)/2+rstrapround +s,-twood/2,h/2-t/2]) rotate(90,[1,0,0])
      cylinder(r=t/2,h=t+tstrapgap+t/2);
    translate([(+(hfront+t)/2-dstrapround/2 -h/2),-twood/2,h/2-t/2]) rotate(90,[1,0,0])
      cylinder(r=t/2,h=t+tstrapgap+t/2);
  }
  hull()
  {
    translate([-(hfront+t)/2+rstrapround +s,-twood/2,-(h/2-t/2)]) rotate(90,[1,0,0])
      cylinder(r=t/2,h=t+tstrapgap+t/2);
    translate([(+(hfront+t)/2-dstrapround/2 -h/2),-twood/2,-(h/2-t/2)]) rotate(90,[1,0,0])
      cylinder(r=t/2,h=t+tstrapgap+t/2);
  }

module softend(b=b2)
{
  hull()
  {
    cylinder(r=rstrapround-b,h=h,center=true);
    cylinder(r=rstrapround,h=h-2*b,center=true);

    translate([dstrapround*f/2,-t/2+rstrapround,0]) cube([dstrapround*f,t,h-2*b],center=true);
    translate([dstrapround*f/2,-t/2+rstrapround,0]) cube([dstrapround*f,t-2*b,h],center=true);
  }
}
module conn(b=b2)
{
  hull()
  {
    translate([dstrapround*1.20/2,-t/2+rstrapround,0]) cube([dstrapround*1.20,t,h-2*b],center=true);
    translate([dstrapround*1.20/2,-t/2+rstrapround,0]) cube([dstrapround*1.20,t-2*b,h],center=true);
  }
}

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

module r2box(rx,ry,x,y,z)
{
  union()
  {
     cube([x,y-2*ry,z],true);
     cube([x-2*rx,y,z],true);
     translate([x/2-rx,y/2-ry,0])
       scale([1,ry/rx,1]) cylinder(r=rx,h=z,center=true);
     translate([-x/2+rx,y/2-ry,0])
       scale([1,ry/rx,1]) cylinder(r=rx,h=z,center=true);
     translate([-x/2+rx,-y/2+ry,0])
       scale([1,ry/rx,1]) cylinder(r=rx,h=z,center=true);
     translate([x/2-rx,-y/2+ry,0])
       scale([1,ry/rx,1]) cylinder(r=rx,h=z,center=true);
  }
}
```


[User]
Okay, I'm looking to design a customizable planetary gear bearing, and I've got a starting point, but I need some improvements and a more flexible design. I want it to be fully parametric so I can easily tweak the dimensions and features for different applications. I've attached a .scad file (bearing-mod-2.scad) that shows the basic structure.

Here's what I'm looking for:

**Core Features:**

*   **Planetary Gear Bearing:** The design should be a planetary gearset with a central sun gear, multiple planet gears, and an outer ring gear.
*   **Post-Assembly:** The key is that this should be designed to be assembled *after* printing. This means parts shouldn't be so tight they require force to assemble. The existing design uses a `tol` parameter but it's a bit basic and could probably use some more smart design to improve assembly and functionality.
*   **Parametric Design:** Everything must be controlled by parameters at the top of the file. I want to be able to change things like the outer diameter of the ring, the thickness, the number of planets, the number of teeth on the gears, the pressure angle, and the helix angle, just to name a few.
*   **Clearances:** The current design uses a simple `tol` variable. I'd like a more sophisticated system for clearance that might use separate tolerances for the gear teeth, the planet holes, and the sun gear axle hole.

**Specific Requirements/Improvements:**

1.  **Outer Ring:**
    *   The outer ring should have a customizable outer diameter (`D`) and thickness (`T`).
    *   It needs an internal herringbone gear.
    *   Add an option for text or a logo on the ring's outer face.
2.  **Planet Gears:**
    *   The number of planets (`number_of_planets`) and the number of teeth on each planet (`number_of_teeth_on_planets`) should be adjustable.
    *   They should have herringbone gears.
    *   The holes for the planet axles (`dplanethole`) need to be adjustable.
    *   Add a clearance parameter for the planet gear holes.
3.  **Sun Gear:**
    *   The sun gear should have a calculated number of teeth based on the number of planets and their teeth. The current code does that somewhat, but perhaps a more robust calculation is needed.
    *   It should have herringbone teeth.
    *   The axle hole (`daxle`) needs to be adjustable, and should have its own tolerance parameter.
    *   I'd like the option to split the sun gear into two halves with a screw attachment method. The code has this, but it could use a better design to make that actually work with screws.
4.  **Gear Generation:**
    *   The current design includes `herringbone` and `gear` modules, but it would be helpful to be able to choose different types of gears (spur, helical etc), if possible. If not, the existing gear code could be generalized for higher performance.
    *   The `depth_ratio` should be exposed as a parameter, or potentially be removed and controlled more directly through other parameters.
5.  **Print Plate:**
    *   I like the idea of having a `print_plate` option that lays out the parts for printing, with optional shifts for the planet gears. The current implementation is pretty basic. Improve the layout and allow for more flexibility in arranging parts.
6.  **Performance:**
    *   The file I provided uses `$fa` and `$fs`. I think the design could be optimized for better printability without losing detail, if possible. I'm not sure of the best parameters here though, so it would be helpful if these were documented.
7.  **Libraries:**
     * I'd like to make sure I'm able to use some well established libraries in OpenSCAD. I'd like to use the `MCAD` and `BOLTS` libraries, if possible.

**Output:**

I need a single, well-commented, parametric OpenSCAD file that includes all these features.  Please make sure the file is well organized.

Essentially, I need a very customizable, robust planetary gear bearing that's easy to assemble after printing.  I'm looking for this to be a more mature version of what I've already got.

[Assistant]
bearing-mod-2.scad:
```
// Planetary gear bearing (customizable)

// outer diameter of ring
D=51.7;
// thickness
T=15;
// clearance (tight fit for post assembly)
tol=0.0;
number_of_planets=4;
number_of_teeth_on_planets=7;
approximate_number_of_teeth_on_sun=9;
// pressure angle
P=45;//[30:60]
// number of teeth to twist across
nTwist=1;
// axle hole diameter
daxle = 6;
// diameter of holes in planets
dplanethole = 3;

DR=0.5*1;// maximum depth ratio of teeth

m=round(number_of_planets);
np=round(number_of_teeth_on_planets);
ns1=approximate_number_of_teeth_on_sun;
k1=round(2/m*(ns1+np));
k= k1*m%2!=0 ? k1+1 : k1;
ns=k*m/2-np;
echo(ns);
nr=ns+2*np;
pitchD=0.89*D/(1+min(PI/(2*nr*tan(P)),PI*DR/nr)); // mod: 0.9->0.89 slightly sturdier
pitch=pitchD*PI/nr;
echo(pitch);
helix_angle=atan(2*nTwist*pitch/T);
echo(helix_angle);

phi=$t*360/m;

$fa = 4*1; $fs = 0.15*1; // mod

part = "explode_demo"; // [whole,ring,planet,sun,sunhalve_leftscrew,sunhalve_rightscrew,explode_demo,print_plate]

test = (part == "whole") || (part == "print_plate");

print_plate_planet_shift = 3;

planetshift = (part != "print_plate") ? 0 : -print_plate_planet_shift; // a bit of a hack

translate([0,0,T/2]){
    if( test==true || part == "ring"  || part == "explode_demo")
	difference(){
		cylinder(r=D/2,h=T,center=true,$fn=100);
		herringbone(nr,pitch,P,DR,-tol,helix_angle,T+0.2);
		difference(){
			translate([0,-D/2,0])rotate([90,0,0])monogram(h=10);
			cylinder(r=D/2-0.25,h=T+2,center=true,$fn=100);
		}
	}
    if( part == "whole" || part == "sun" && part != "explode_demo") { sun(); }
    if( test == true || part == "explode_demo")
	{
		color("orange")
		for(i=[1:m])rotate([0,0,i*360/m+phi])translate([pitchD/2*(ns+np)/nr+planetshift,0,0])
			rotate([0,0,i*ns/m*360/np-phi*(ns+np)/np-phi])
				difference()
				{
					herringbone(np,pitch,P,DR,tol,helix_angle,T);
					translate([0,0,-T*0.1])
					cylinder(r=dplanethole/2,h=T*1.2);
				}
	}
    if(part == "planet" || part == "explode_demo") // ??
	{
		#for(i=[1:1])rotate([0,0,i*360/m+phi])translate([pitchD/2*(ns+np)/nr,0,0])
			rotate([0,0,i*ns/m*360/np-phi*(ns+np)/np-phi])
				difference()
				{
					herringbone(np,pitch,P,DR,tol,helix_angle,T);
					translate([0,0,-T*0.1])
					cylinder(r=dplanethole/2,h=T*1.2);
				}
	}

    if(part == "sunhalve_leftscrew" || part == "explode_demo") { sunhalve(); }
    if(part == "sunhalve_rightscrew" || part == "explode_demo")
	{	translate([0,0,T*2]) mirror([1,1,0]) sunhalve(); }

    if(part =="print_plate")
    {
		translate([-D/2*1.5,0,0]) mirror([1,1,0]) sunhalve();
		translate([+D/2*1.5,0,0]) sunhalve(); // 1.5 might be too less in some cases
    }
}

module sunhalve()
{
	difference(){
		sun();
		translate([0,0,T/4+1])
			cylinder(r=D/2,h=T/2+2,center=true,$fn=32);
	}
}

module sun()
{
	rotate([0,0,(np+1)*180/ns+phi*(ns+np)*2/ns])
	difference(){
		mirror([0,1,0])
			herringbone(ns,pitch,P,DR,tol,helix_angle,T);
		cylinder(r=daxle/2,h=T+1,center=true,$fn=32);
	}
}

module rack(
	number_of_teeth=15,
	circular_pitch=10,
	pressure_angle=28,
	helix_angle=0,
	clearance=0,
	gear_thickness=5,
	flat=false){
addendum=circular_pitch/(4*tan(pressure_angle));

flat_extrude(h=gear_thickness,flat=flat)translate([0,-clearance*cos(pressure_angle)/2])
	union(){
		translate([0,-0.5-addendum])square([number_of_teeth*circular_pitch,1],center=true);
		for(i=[1:number_of_teeth])
			translate([circular_pitch*(i-number_of_teeth/2-0.5),0])
			polygon(points=[[-circular_pitch/2,-addendum],[circular_pitch/2,-addendum],[0,addendum]]);
	}
}

module monogram(h=1)
linear_extrude(height=h,center=true)
translate(-[3,2.5])union(){
	difference(){
		square([4,5]);
		translate([1,1])square([2,3]);
	}
	square([6,1]);
	translate([0,2])square([2,1]);
}

module herringbone(
	number_of_teeth=15,
	circular_pitch=10,
	pressure_angle=28,
	depth_ratio=1,
	clearance=0,
	helix_angle=0,
	gear_thickness=5){
union(){
	gear(number_of_teeth,
		circular_pitch,
		pressure_angle,
		depth_ratio,
		clearance,
		helix_angle,
		gear_thickness/2);
	mirror([0,0,1])
		gear(number_of_teeth,
			circular_pitch,
			pressure_angle,
			depth_ratio,
			clearance,
			helix_angle,
			gear_thickness/2);
}}

module gear (
	number_of_teeth=15,
	circular_pitch=10,
	pressure_angle=28,
	depth_ratio=1,
	clearance=0,
	helix_angle=0,
	gear_thickness=5,
	flat=false){
pitch_radius = number_of_teeth*circular_pitch/(2*PI);
twist=tan(helix_angle)*gear_thickness/pitch_radius*180/PI;

flat_extrude(h=gear_thickness,twist=twist,flat=flat)
	gear2D (
		number_of_teeth,
		circular_pitch,
		pressure_angle,
		depth_ratio,
		clearance);
}

module flat_extrude(h,twist,flat){
	if(flat==false)
		linear_extrude(height=h,twist=twist,slices=twist/6)child(0);
	else
		child(0);
}

module gear2D (
	number_of_teeth,
	circular_pitch,
	pressure_angle,
	depth_ratio,
	clearance){
pitch_radius = number_of_teeth*circular_pitch/(2*PI);
base_radius = pitch_radius*cos(pressure_angle);
depth=circular_pitch/(2*tan(pressure_angle));
outer_radius = clearance<0 ? pitch_radius+depth/2-clearance : pitch_radius+depth/2;
root_radius1 = pitch_radius-depth/2-clearance/2;
root_radius = (clearance<0 && root_radius1<base_radius) ? base_radius : root_radius1;
backlash_angle = clearance/(pitch_radius*cos(pressure_angle)) * 180 / PI;
half_thick_angle = 90/number_of_teeth - backlash_angle/2;
pitch_point = involute (base_radius, involute_intersect_angle (base_radius, pitch_radius));
pitch_angle = atan2 (pitch_point[1], pitch_point[0]);
min_radius = max (base_radius,root_radius);

intersection(){
	rotate(90/number_of_teeth)
		circle($fn=number_of_teeth*3,r=pitch_radius+depth_ratio*circular_pitch/2-clearance/2);
	union(){
		rotate(90/number_of_teeth)
			circle($fn=number_of_teeth*2,r=max(root_radius,pitch_radius-depth_ratio*circular_pitch/2-clearance/2));
		for (i = [1:number_of_teeth])rotate(i*360/number_of_teeth){
			halftooth (
				pitch_angle,
				base_radius,
				min_radius,
				outer_radius,
				half_thick_angle);
			mirror([0,1])halftooth (
				pitch_angle,
				base_radius,
				min_radius,
				outer_radius,
				half_thick_angle);
		}
	}
}}

module halftooth (
	pitch_angle,
	base_radius,
	min_radius,
	outer_radius,
	half_thick_angle){
index=[0,1,2,3,4,5];
start_angle = max(involute_intersect_angle (base_radius, min_radius)-5,0);
stop_angle = involute_intersect_angle (base_radius, outer_radius);
angle=index*(stop_angle-start_angle)/index[len(index)-1];
p=[[0,0],
	involute(base_radius,angle[0]+start_angle),
	involute(base_radius,angle[1]+start_angle),
	involute(base_radius,angle[2]+start_angle),
	involute(base_radius,angle[3]+start_angle),
	involute(base_radius,angle[4]+start_angle),
	involute(base_radius,angle[5]+start_angle)];

difference(){
	rotate(-pitch_angle-half_thick_angle)polygon(points=p);
	square(2*outer_radius);
}}

// Mathematical Functions
//===============

// Finds the angle of the involute about the base radius at the given distance (radius) from it's center.
//source: http://www.mathhelpforum.com/math-help/geometry/136011-circle-involute-solving-y-any-given-x.html

function involute_intersect_angle (base_radius, radius) = sqrt (pow (radius/base_radius, 2) - 1) * 180 / PI;

// Calculate the involute position for a given base radius and involute angle.

function involute (base_radius, involute_angle) =
[
	base_radius*(cos (involute_angle) + involute_angle*PI/180*sin (involute_angle)),
	base_radius*(sin (involute_angle) - involute_angle*PI/180*cos (involute_angle))
];

```


[User]
Okay, I'm trying to make a customizable template for a 3D printer, and I'd like to use OpenSCAD. I have a basic template that works, but it's not as flexible as I need. I want to be able to select different shapes, adjust the dimensions, and the overall thickness easily, plus add a couple of small handles.

The basic idea is a 2D template with a constant thickness, and it should have two small cylindrical handles attached at the sides. I need to be able to select from three template shapes:

1.  **A simple strip**:  This is just a rectangle. I want to control its length and width. The current code uses a `height` parameter for the length, but that's not super intuitive. I'd rather call that `length` instead. I also want to be able to control its width with a `width` parameter.
2.  **An arrow**: This starts with a rectangular strip like the one above, and then has a triangular arrow at one end. I should be able to control the width and length of the rectangular strip part, and the arrow section should dynamically adjust based on the width of the base rectangle, maybe making it 1.5x the base rectangle's width.
3.  **A heart**: This is a heart shape, which should be controlled by a single `width` parameter to determine its overall size. It's constructed from a rotated square and two intersecting cylinders.

For all three shapes, I'd like:

*   A `thickness` parameter that controls the thickness of the template.
*   Two cylindrical handles attached to the sides. The handle diameter should also be parameterizable as `handleDiameter`. The handles should stick out on both sides, and the length of the handles (along the Z-axis) should scale based on thickness, maybe 3x the `thickness`.
*   I'd like a small amount of overlap (using a `fudge` parameter) when generating the arrow part to ensure there are no gaps between arrow and strip.
*   I'm using the `build_plate.scad` library to make sure my designs fit, so I'd like that incorporated.

Also, let's get rid of the somewhat confusing `$fn = 50*1;` variable. I'm not sure why that's there. I would prefer to explicitly control `$fn` when I need to control the resolution, instead of setting it directly in the design.

I need the code to use parameters like:
* `templateShape` (integer 1, 2, or 3 for strip, arrow, or heart respectively)
* `length` for the strip and arrow shapes
* `width` for all shapes
* `thickness` for all shapes
* `handleDiameter` for the handles

I also want to keep the `build_plate_selector`, `build_plate_manual_x`, and `build_plate_manual_y` options from my current code. I'm not sure if `fudge` is used in a good way currently, so maybe consider how it could be used better or removed as well if it's not that helpful.

[Assistant]
template.scad:
```
// Joe Stubbs
// March 1 2013
// http://www.thingiverse.com/thing:56183

use <utils/build_plate.scad>

$fn = 50*1;

//Select the basic shape for your template
Template_Shape = 2; // [1:Strip,2:Arrow,3:Heart]

//Select the basic width for your design
Width = 20; //[10:30]

//Select the basic height for your design (not used with the heart shape)
Height = 50; //[10:80]

//Select the thickness for the template
Thickness = 4; //[3:8]

strip_x=Height;
strip_y=Width;
strip_z=Thickness;

handle_d=5*1;
handle_r=handle_d/2;
handle_z=strip_z*3;

handle_x = handle_d;

arrow_y = strip_y*1.5;

fudge=0.05*1;

shape = Template_Shape;

//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);

if (shape == 1) landingStrip();
if (shape == 2) arrowStrip();
if (shape == 3) heartStrip();

module landingStrip() {
	translate([0,0,strip_z/2]) {
		cube ([strip_x,strip_y,strip_z],center=true);
		translate ([handle_x,0,handle_z/2])
			cylinder(r=handle_r,h=handle_z,center=true);
		translate ([-handle_x,0,handle_z/2])
			cylinder(r=handle_r,h=handle_z,center=true);
	}
}

module arrowStrip() {
	translate([0,0,strip_z/2]) {
		cube ([strip_x,strip_y,strip_z],center=true);

		translate([strip_x/2,0,0])
			difference() {
				rotate(45,[0,0,1])
					cube ([arrow_y,arrow_y,strip_z],center=true);
				translate([-arrow_y,0,0])
					cube ([arrow_y*2,arrow_y*2,strip_z+fudge],center=true);
			}

		translate ([handle_x,0,handle_z/2])
			cylinder(r=handle_r,h=handle_z,center=true);
		translate ([-handle_x,0,handle_z/2])
			cylinder(r=handle_r,h=handle_z,center=true);
	}
}

module heartStrip() {
	translate([0,0,strip_z/2]) {
		rotate(45,[0,0,1])
			cube ([strip_y,strip_y,strip_z],center=true);

		translate ([-strip_y/3.14,-strip_y/3.14,0])
		cylinder(r=strip_y/2,h=strip_z,center=true);

		translate ([-strip_y/3.14,strip_y/3.14,0])
		cylinder(r=strip_y/2,h=strip_z,center=true);

		translate ([handle_x,0,handle_z/2])
			cylinder(r=handle_r,h=handle_z,center=true);
		translate ([-handle_x,0,handle_z/2])
			cylinder(r=handle_r,h=handle_z,center=true);

	}
}
```


[User]
Okay, I have this idea for some modular shelving based on Tetris shapes and I'm hoping you can help me create an OpenSCAD design. I've already played around with a basic version (I can share that if it helps!), but I need something more parametric and robust.

Here's what I'm imagining:

**Core Concept:**

*   **Tetris Shapes:** The shelving units will be based on the standard Tetris shapes: I, J, L, O, S, Z, and T.
*   **Modular Design:** Each shape should be a standalone unit that can be connected to others using some kind of connector system. This needs to be customizable.

**Key Parameters I want to Control:**

*   **Shape Selection:** The user should be able to select which Tetris shape to generate (I, J, L, O, S, Z, or T).
*   **Grid Size:**  The size of the base unit "grid square" of the Tetris shape, in millimeters.
*   **Wall Thickness:** The thickness of the walls of each shape, also in millimeters.
*   **Depth:** The depth of the shelving unit (how far it extends out from the wall), in millimeters.
*   **Back Wall:** A boolean option to include a back wall on each shelving unit.
*   **Back Wall Grid:** A boolean option to add a grid pattern to the back of the shelving unit for aesthetics/strength.
*   **Connectors:**
    *   **Connector Depth:** The depth of the connectors that fit into the shelving units.
    *   **Connector Width:** The width of the connectors.
    *   **Connector Clearance:**  Clearance around the connectors for ease of fit (needs to be adjustable for different printer tolerances).
    *   **Connector Shape:** Option for circle or square connectors.
    *   **Number of Connectors:** Ability to specify how many of each connector type are generated (4-slot, 3-slot, straight 2-slot, and corner 2-slot).
    *   **Connector Layout:** Connectors should be laid out in a reasonable way, possibly in a grid, so they are easy to print.

**Other Considerations:**

*   **Build Plate Visualization:** I'd like a way to visualize the build plate size and dimensions so I can see how the shapes and connectors will fit. It would be cool to have some common printer presets for this or even be able to define manual build plate dimensions
*   **Coloring:** It might be nice to have the shapes be colored based on the standard Tetris shape colors for easier visualization in the preview (I=Cyan, J=Blue, L=Orange, O=Yellow, S=Lime, Z=Red, T=Magenta).
*   **Library:** It looks like I am already using a build_plate.scad file, so please use that if it's possible.

**My Goals**

*   **Parametric Design:** Everything should be adjustable via parameters.
*   **Ease of Use:** I should be able to easily select a shape, adjust sizes, and generate the STL files I need for 3D printing.
*   **Functional and Printable:** The design should be strong enough for shelving while being printable without excessive supports.

Could you help me develop a parametric OpenSCAD file that achieves these goals? I want to be able to generate both individual Tetris shapes and a set of connectors.

[Assistant]
tetrisShelves.scad:
```
use <utils/build_plate.scad>

// Select a shape to make
shape = "L"; // [I,J,L,O,S,Z,T]
// Size of each grid square in mm
grid_size = 50;
// Thickness of walls in mm
wall_thickness = 2;
// Depth of shape in mm
depth = 50;
// Do you want a back wall on the shape?
back_wall = "yes";//[yes,no]
// Do you want a grid pattern on the back of the shape?
grid_pattern = "yes";//[yes,no]
// Do you want to build a shape?
want_shape = "yes";//[yes,no]

// Depth of connectors in mm
connector_depth = 15;
// Width of connectors in mm
connector_width = 15;
// Clearance between connectors and shapes in mm? Increase to make looser fits, decrease to make tighter fits.
connector_clearance = 0.25;
// Circle or square connectors?
connector_shape = "square";//[circle,square]
// How many 4 slot connectors to layout?
number_of_4_slot_connectors = 0;//[0:12]
// How many 3 slot connectors to layout?
number_of_3_slot_connectors = 0;//[0:12]
// How many straight slot connectors to layout?
number_of_2_slot_straight_connectors = 0;//[0:12]
// How many corner slot connectors to layout?
number_of_2_slot_corner_connectors = 0;//[0:12]

//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);

connector_diameter = connector_width;
numConnectors = number_of_4_slot_connectors+number_of_3_slot_connectors+number_of_2_slot_straight_connectors+number_of_2_slot_corner_connectors;
CC = connector_clearance;
off = wall_thickness/grid_size;
backWallOffset = back_wall == "yes" ? 1 : -1;

// preview[view:south east,tilt:top]
if (want_shape == "yes") makeShape(shape);
translate([-grid_size,grid_size*1.5,0])
makeConnectors();

module makeConnectors(){
	if (numConnectors > 0){
		for (i = [0:numConnectors-1]){
			makeConnectorX(i);
		}
	}
}

module makeConnectorX(i){
	color("Gray")
	translate([grid_size*(i%4),grid_size*round(i/4-0.49),0])
	if (number_of_4_slot_connectors > i) make4Connector();
	else if (number_of_4_slot_connectors + number_of_3_slot_connectors > i) make3Connector();
	else if (number_of_4_slot_connectors + number_of_3_slot_connectors + number_of_2_slot_straight_connectors > i) make2ConnectorA();
	else make2ConnectorB();
}

module connectorBase(){
	if (connector_shape == "circle") cylinder(h=connector_depth,r=connector_diameter/2);
	else{
		translate([-connector_diameter/2,-connector_diameter/2,0])
		cube(size = [connector_diameter,connector_diameter,connector_depth]);
	}
}

module make4Connector(){
	difference(){
		connectorBase();
		translate([0,0,wall_thickness*2+connector_depth/2])
		cube(size = [connector_diameter*1.1,wall_thickness*2+CC,connector_depth],center = true);
		translate([0,0,wall_thickness*2+connector_depth/2])
		cube(size = [wall_thickness*2+CC,connector_diameter*1.1,connector_depth],center = true);
	}
}

module make3Connector(){
	difference(){
		connectorBase();
		translate([0,0,wall_thickness*2+connector_depth/2])
		cube(size = [connector_diameter*1.1,wall_thickness*2+CC,connector_depth],center = true);
		translate([-wall_thickness-CC/2,0,wall_thickness*2])
		cube(size = [wall_thickness*2+CC,connector_diameter*.55,connector_depth]);
	}
}

module make2ConnectorA(){
	difference(){
		connectorBase();
		translate([0,0,wall_thickness*2+connector_depth/2])
		cube(size = [connector_diameter*1.1,wall_thickness*2+CC,connector_depth],center = true);
	}
}

module make2ConnectorB(){
	difference(){
		connectorBase();
		translate([-wall_thickness-CC/2,-wall_thickness-CC/2,wall_thickness*2])
		cube(size = [connector_diameter*1,wall_thickness*2+CC,connector_depth]);
		translate([-wall_thickness-CC/2,0,wall_thickness*2])
		cube(size = [wall_thickness*2+CC,connector_diameter*.55,connector_depth]);
	}
}

module makeShape(shape){
	color(shape == "L" ? "Orange" : shape == "J" ? "Blue" : shape == "O" ? "Yellow" : shape == "I" ? "Cyan" : shape == "S" ? "Lime" : shape == "Z" ? "Red" : "Magenta")
	difference(){
		linear_extrude(height=depth, convexity = 10)
		scale([grid_size,grid_size,1])
		if (shape == "L") L();
		else if (shape == "J") J();
		else if (shape == "O") O();
		else if (shape == "I") I();
		else if (shape == "S") S();
		else if (shape == "Z") Z();
		else if (shape == "T") T();

		difference(){
			translate([0,0,backWallOffset*wall_thickness])
			linear_extrude(height=depth*2, convexity = 10)
			scale([grid_size,grid_size,1])
			if (shape == "L") offsetL();
			else if (shape == "J") offsetJ();
			else if (shape == "O") offsetO();
			else if (shape == "I") offsetI();
			else if (shape == "S") offsetS();
			else if (shape == "Z") offsetZ();
			else if (shape == "T") offsetT();
			if (grid_pattern == "yes"){
				translate([0,0,wall_thickness])
				cuttingGrid();
			}
		}
		if (grid_pattern == "yes") cuttingGrid();
	}
}

module cuttingGrid(){
	translate([0,-grid_size*.5,0])
	rotate([0,0,90])
	rotate([90,0,0])
	translate([0,0,-grid_size*1.55])
	linear_extrude(height=grid_size*4.1, convexity = 10)
	scale([grid_size,grid_size,1])
	polygon(points = [ [-1,-1],[-1,-.35],[-.5,.15],[0,-.35],[.5,.15],[1,-.35],[1.5,.15],[2,-.35],[2,-1]],paths = [[0,1,2,3,4,5,6,7,8]]);

	rotate([90,0,0])
	translate([0,0,-grid_size*1.05])
	linear_extrude(height=grid_size*2.1, convexity = 10)
	scale([grid_size,grid_size,1])
	polygon(points = [ [-2,-1],[-2,-.35],[-1.5,.15],[-1,-.35],[-.5,.15],[0,-.35],[.5,.15],[1,-.35],[1.5,.15],[2,-.35],[2.5,.15],[3,-.35],[3,-1]],paths = [[0,1,2,3,4,5,6,7,8,9,10,11,12]]);
}

module L() {
	color("Orange")
	polygon(points = [ [-1.5,-1],[1.5,-1],[1.5,1],[.5,1],[.5,0],[-1.5,0]],paths = [[0,1,2,3,4,5]]);
}

module J() {
	color("Blue")
	polygon(points = [ [-1.5,-1],[1.5,-1],[1.5,0],[-.5,0],[-.5,1],[-1.5,1]],paths = [[0,1,2,3,4,5]]);
}

module O() {
	color("Yellow")
	polygon(points = [ [-.5,-1],[1.5,-1],[1.5,1],[-.5,1]],paths = [[0,1,2,3]]);
}

module I() {
	color("Cyan")
	polygon(points = [ [-1.5,0],[2.5,0],[2.5,1],[-1.5,1]],paths = [[0,1,2,3]]);
}

module S() {
	color("Lime")
	polygon(points = [ [-1.5,-1],[.5,-1],[.5,0],[1.5,0],[1.5,1],[-.5,1],[-.5,0],[-1.5,0]],paths = [[0,1,2,3,4,5,6,7]]);
}

module Z() {
	color("Red")
	polygon(points = [ [1.5,-1],[-.5,-1],[-.5,0],[-1.5,0],[-1.5,1],[.5,1],[.5,0],[1.5,0]],paths = [[0,1,2,3,4,5,6,7]]);
}

module T() {
	color("Magenta")
	polygon(points = [ [-1.5,-1],[1.5,-1],[1.5,0],[.5,0],[.5,1],[-.5,1],[-.5,0],[-1.5,0]],paths = [[0,1,2,3,4,5,6,7]]);
}

module offsetL() {
	color("Orange")
	polygon(points = [ [-1.5+off,-1+off],[1.5-off,-1+off],[1.5-off,1-off],[.5+off,1-off],[.5+off,0-off],[-1.5+off,0-off]],paths = [[0,1,2,3,4,5]]);
}

module offsetJ() {
	color("Blue")
	polygon(points = [ [-1.5+off,-1+off],[1.5-off,-1+off],[1.5-off,0-off],[-.5-off,0-off],[-.5-off,1-off],[-1.5+off,1-off]],paths = [[0,1,2,3,4,5]]);
}

module offsetO() {
	color("Yellow")
	polygon(points = [ [-.5+off,-1+off],[1.5-off,-1+off],[1.5-off,1-off],[-.5+off,1-off]],paths = [[0,1,2,3]]);
}

module offsetI() {
	color("Cyan")
	polygon(points = [ [-1.5+off,off],[2.5-off,off],[2.5-off,1-off],[-1.5+off,1-off]],paths = [[0,1,2,3]]);
}

module offsetS() {
	color("Lime")
	polygon(points = [ [-1.5+off,-1+off],[.5-off,-1+off],[.5-off,0+off],[1.5-off,0+off],[1.5-off,1-off],[-.5+off,1-off],[-.5+off,0-off],[-1.5+off,0-off]],paths = [[0,1,2,3,4,5,6,7]]);
}

module offsetZ() {
	color("Red")
	polygon(points = [ [1.5-off,-1+off],[-.5+off,-1+off],[-.5+off,0+off],[-1.5+off,0+off],[-1.5+off,1-off],[.5-off,1-off],[.5-off,0-off],[1.5-off,0-off]],paths = [[0,1,2,3,4,5,6,7]]);
}

module offsetT() {
	color("Magenta")
	polygon(points = [ [-1.5+off,-1+off],[1.5-off,-1+off],[1.5-off,0-off],[.5-off,0-off],[.5-off,1-off],[-.5+off,1-off],[-.5+off,0-off],[-1.5+off,0-off]],paths = [[0,1,2,3,4,5,6,7]]);
}
```


[User]
Okay, I'm looking to create a customizable necktie design with a two-tone look, but without needing a dual extruder printer. I'd like the tie to have a main body and then separate pattern pieces that attach to it, which is how I'll achieve the two-tone look. I also want it to be parametric so that I can easily adjust the dimensions and pattern.

Here's what I'm envisioning:

**Overall Tie:**

*   **Main Tie Body:** This will be the base of the tie, with a defined length and width. I need to be able to control the overall length of the tie, the maximum width of the tie, and the thickness of the material.
*   **Knot:** The top portion of the tie that goes around the neck should be adjustable in width at the top and bottom, the height of the knot, and the thickness.
*   **Pattern Pieces:** These are separate pieces that attach to the main tie body. I need to be able to enable or disable them and control their thickness, shape, and rotation.
*   **Attachment:** The pattern pieces should attach to the main body via small notches or pegs.

**Specific Parameters I Need to Control:**

*   **Tie Body:**
    *   `tie_thickness`: The thickness of the main tie body.
    *   `tie_height`: The length of the main tie body below the knot.
    *   `tie_width`: The maximum width of the tie.
*   **Knot:**
    *   `knot_width`: The width of the knot at the base.
    *  `knot_width_top`: The width of the knot at the top.
    *   `knot_height`: The height of the knot.
    *   `knot_thickness`: The thickness of the knot.
*   **Pattern Pieces:**
    *   `pattern_yes_or_no`: A boolean to enable or disable the pattern pieces.
    *   `pattern_thickness`: The thickness of the pattern pieces.
    *   `sides`: The number of sides for the pattern shapes (e.g., 3 for a triangle, 6 for a hexagon, 30 for circle etc.).
    *   `radius`: The radius or size of the pattern shapes.
	* `polygon_edge_thickness`: The thickness of the edges of the polygon shapes.
	* `overlap_factor`: How much the shapes will overlap eachother as a percentage.
    *   `polygon_rotation`: An angle to rotate the pattern shapes.
	*   `stretch_off_on`: A boolean to turn off or on the stretch effect.
	*	`polygon_stretch`: Percentage to stretch the pattern pieces.
	*	`distort_off_on`: A boolean to turn off or on the distort effect.
	*	`distort_value`: The value of the distort effect.

**Other Design Considerations:**

*   I'd like the tie to be printable in sections or layers, and to be flexible enough once printed, so that it can be worn comfortably.
*   The pattern pieces should fit snugly and securely into the main tie body.
*   I'd need to generate five separate STL files; one for the main body, and four for the different pattern pieces.

Could you help me create a parametric OpenSCAD file for this design? I don't need any additional libraries, just base OpenSCAD functionality.

[Assistant]
Customizable_Necktie_R2.scad:
```
//preview[view:north,tilt:bottom]

//░░▒▒▓▓██████████████████████████ Set all the other paramaters, then use these next 3 options to get the STLS. Turn off preview and then Create STL under "Tie Body" and then "Pattern Pieces". Customizer will generate 5 STLS at once for each.  __________________________________
S_E_L_E_C_T_O_R=0;//[0:0]//break
//
preview_or_generate_Stl=1; //[1:Preview All (I'M NOT DONE DESIGNING!),0:One piece at a time (I'M READY TO GET THE STLS!)]
body_or_pattern=1;//[1:Tie Body, 2:Pattern Pieces]
part=1; // [1:Bottom, 2:Next to Bottom, 3:Next to Top, 4:Top, 5:Knot]

//░░▒▒▓▓██████████████████████████
B_O_D_Y=0;//[0:0]//break
//Z axis material thickness
tie_thickness=1.8; //[1.2:Flimsy 1.2mm,1.5:Thin 1.5mm,1.8:Moderate 1.8mm,2.1:Moderate Plus 2.1mm,2.4:Sturdy 2.4mm]
//Length of main tie body from knot down.
tie_height=450;  // [325:600]
//Maximum width. 45=very skinny, 70=modern narrow, 95=classic wide
tie_width =80; //[45:100]//

//░░▒▒▓▓██████████████████████████
K_N_O_T=0;//[0:0]//break
knot_width=30; //[30:40]
knot_width_top=40;//[30:60]
knot_height=35;//[35:50]
//Z axis material thickness
knot_thickness=5;//[3:15]

//░░▒▒▓▓██████████████████████████ Because of how the pattern gets split into 4 pieces, watch out for "island" pieces (i.e. not contiguous with the rest of the design). __________________________________
P_A_T_T_E_R_N=0;//[0:0]//break
//This adds the notches on the main tie body that the pattern pieces snap into. They're not needed if you don't want a pattern.
pattern_yes_or_no=1; //[1:I want to add the pattern pieces, 0:I want a solid color tie]
//Z axis material thickness
pattern_thickness=2.1;//[1.8:Moderate 1.8mm,2.1:Sturdy 2.1mm]
sides=6;//[3:Triangle (3-sided),4:Square (4-sided),5:Pentagon (5-sided),6:Hexagon (6-sided),7:Heptagon (7-sided),8:Octogon (8-sided),9:Nonagon (9-sided),10:Decagon (10-sided),11:Hendecagon (11-sided),12:Dodecagon (12-sided),30:Circle (3w0-sided)]
radius=15;//[2:25]
polygon_edge_thickness=2;//[1.0:10.0]
overlap_factor=50; //[0:100]
polygon_rotation=50;//[0:120]
stretch_off_on=0;//[0:OFF, 1:ON]
//% X stretch.
polygon_stretch=0;//[-20:100]
distort_off_on=0; //[0:OFF, 1:ON]
distort_value=1;//[1:20]
//░░▒▒▓▓██████████████████████████
E_N_D=0;//[0:0]//break

iflub=.05+0;

module SELECTION()
intersection(){

		union()
				{translate([0,iflub,0])cube([110,tie_height/4-.5,20]);
				 translate([-265+(2-body_or_pattern)*265,-.25*tie_height*(part-1)-5,-iflub])
					cube([370,tie_height+knot_height+10,20*preview_or_generate_Stl]);
				translate([-140+(2-body_or_pattern)*265+(.5*tie_width),.75*tie_height-.25*tie_height*(part-1),-6-6*(1-preview_or_generate_Stl)])
					scale([2,2,1])
						cylinder(h=6,r=42);}

		union()
				{MAINTIE();
				DESIGN();
				translate([-140+(2-body_or_pattern)*265+(.5*tie_width),.75*tie_height+25-.25*tie_height*(part-1),-5])
				scale([2,2,1])
					if (preview_or_generate_Stl==1)
						{
						HONEYCOMB(60,60,1,radius,overlap_factor,polygon_edge_thickness,sides,polygon_stretch,distort_value,distort_off_on);
						}

				}
			}
				SELECTION();

module MAINTIE()

translate([-250*(body_or_pattern-1),-.25*tie_height*(part-1),0])
{
difference()
		{
			union()
			{

				//body
				linear_extrude(height = tie_thickness)polygon([[0,tie_width/2],[tie_width/2,0],[tie_width,tie_width/2],[(tie_width-knot_width)/2+knot_width,tie_height],
				[(tie_width-knot_width)/2,tie_height]],[[0,1,2,3,4,5]]);

				//knot
				translate([.5*tie_width-.5*knot_width,(tie_height)+1.5,0])
				linear_extrude(height =knot_thickness)polygon([[0,0],[knot_width,0],[knot_width+.5*(knot_width_top-knot_width),knot_height],
				[-.5*(knot_width_top-knot_width),knot_height]],[[0,1,2,3,4]]);

					//knot connector nubs
					// bottom
							translate([(tie_width/2)-15,tie_height+5.9,knot_thickness-iflub])
							{
								rotate([90,0,90])
								{
									linear_extrude(height = 30)polygon([[0,3.3],[.9,0],[2.3,0],[2.3,1.3],[3.0,2.7]],[[0,1,2,3,4,5]]);
									linear_extrude(height = 30)polygon([[6.9,0],[9.6,0],[9.6,3.2],[5.5,3.2]],[[0,1,2,3,4]]);
								}
							}
					//R
							translate([(tie_width/2)+9.58,tie_height+knot_height-19,knot_thickness-iflub])
							{
								rotate([90,0,180])
								{
									linear_extrude(height = 20)polygon([[0,3.3],[.9,0],[2.3,0],[2.3,1.3],[3.0,2.7]],[[0,1,2,3,4,5]]);
									linear_extrude(height = 20)polygon([[6.9,0],[9.6,0],[9.6,3.2],[5.5,3.2]],[[0,1,2,3,4]]);
								}
							}
					//L
							translate([(tie_width/2)-9.58,tie_height+knot_height+1,knot_thickness-iflub])
							{
								rotate([90,0,0])
								{
									linear_extrude(height = 20)polygon([[0,3.3],[.9,0],[2.3,0],[2.3,1.3],[3.0,2.7]],[[0,1,2,3,4,5]]);
									linear_extrude(height = 20)polygon([[6.9,0],[9.6,0],[9.6,3.2],[5.5,3.2]],[[0,1,2,3,4]]);
								}
							}

				//body connector nubs top
			for(i = [ [((.25*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width)+17,(tie_height)/4-5.5,tie_thickness-iflub],
        			       [ tie_width-(.25*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)/4-5.5,tie_thickness-iflub],
					[((.5*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width)+17,(tie_height)/2-5.1,tie_thickness-iflub],
					[ tie_width-(.5*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)/2-5.1,tie_thickness-iflub],
					[((.75*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width)+17,(tie_height)/1.333-4.8,tie_thickness-iflub],
					[ tie_width-(.75*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)/1.333-4.8,tie_thickness-iflub],
					[tie_width/2,tie_height-4.4,tie_thickness-iflub],
					[tie_width/2+15,tie_height-4.4,tie_thickness-iflub]])
						{
							translate(i)
							{
								rotate([90,0,270])
								{
									linear_extrude(height = 15)polygon([[0,3.3],[.9,0],[2.3,0],[2.3,1.3],[3.0,2.7]],[[0,1,2,3,4,5]]);
									linear_extrude(height = 15)polygon([[6.9,0],[9.6,0],[9.6,3.2],[5.5,3.2]],[[0,1,2,3,4]]);
								}
							}
						}

				//body connector nubs bottom
			for(i = [ [((.25*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width)+2,(tie_height)/4+4.7,tie_thickness-iflub],
        			      [ tie_width-(.25*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-17,(tie_height)/4+4.7,tie_thickness-iflub],
					[((.5*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width)+2,(tie_height)/2+5.1,tie_thickness-iflub],
					[ tie_width-(.5*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-17,(tie_height)/2+5.1,tie_thickness-iflub],
					[((.75*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width)+2,(tie_height)/1.333+5.4,tie_thickness-iflub],
					[ tie_width-(.75*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-17,(tie_height)/1.333+5.4,tie_thickness-iflub]])
						{
							translate(i)
							{
								rotate([90,0,90])
								{
									linear_extrude(height = 15)polygon([[0,3.3],[.9,0],[2.3,0],[2.3,1.3],[3.0,2.7]],[[0,1,2,3,4,5]]);
									linear_extrude(height = 15)polygon([[6.9,0],[9.6,0],[9.6,3.2],[5.5,3.2]],[[0,1,2,3,4]]);
								}
							}
						}

			}

			union()
			{TIEBREAKS(tie_thickness);

			if (pattern_yes_or_no==1)
				{

			//TIENOTCH R
				for (i=[
        			      [ tie_width-(.15*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.15,0],
					[ tie_width-(.3*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.3,0],
					[ tie_width-(.42*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.42,0],
					[ tie_width-(.55*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.55,0],
					[ tie_width-(.67*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.67,0],
					[ tie_width-(.8*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.8,0],
					[ tie_width-(.92*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.92,0]])

					{translate(i)

						{rotate([0,0,atan((.5*(tie_width-knot_width))/(tie_height-0.5*tie_width))])
							translate([0,-.5,9.9-10*(pattern_yes_or_no)])cube([2,11,tie_thickness*1.1]);
						}
					}

			//TIENOTCH L
				for (i = [
					[((.15*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.15,0],
					[((.3*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.3,0],
					[((.42*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.42,0],
					[((.55*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.55,0],
					[((.67*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.67,0],
					[((.8*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.8,0],
					[((.92*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.92,0]])

					{translate(i)
						{rotate([0,0,180-atan((.5*(tie_width-knot_width))/(tie_height-0.5*tie_width))])
							translate([-2+iflub,-10.5,-.1])cube([2,11,tie_thickness*1.1]);
						}
					}

			//BOTTOM 45 L
				for (i = [
					[.5*tie_width-1.4,0,0]])
					{translate(i)
						{rotate([0,0,-135])
							translate([-2.9,-10-.25*tie_width,-.1])cube([2,11,tie_thickness*1.1]);
						}
					}

			//BOTTOM 45 R
				for (i = [
					[.5*tie_width-1.4,00]])
					{translate(i)
						{rotate([0,0,135])
							translate([-1,-12-.255*tie_width,-.1])cube([2,11,tie_thickness*1.1]);
						}
					}
			//KNOT L

							translate([.5*tie_width-.5*knot_width-.25*(knot_width_top-knot_width)+1,tie_height+.5*knot_height+1.5,.55*knot_thickness])
							rotate([0,0,atan((.5*(knot_width_top-knot_width))/(knot_height))])translate([0,2,0])cube([2,11,knot_thickness*1.2],center=true);

			//KNOT R

							translate([.5*tie_width+.5*knot_width+.25*(knot_width_top-knot_width)-1,tie_height+.5*knot_height+1.5,.55*knot_thickness])
							rotate([0,0,-atan((.5*(knot_width_top-knot_width))/(knot_height))])translate([0,2,0])cube([2,11,knot_thickness*1.2],center=true);
				}

			}
		}
}

module DESIGN()
	translate([250-((body_or_pattern-1)*250),-.25*tie_height*(part-1),0])
	{
	difference()
		{
			union()
			{
				intersection()
				{
					union()
					{
						//knot
						translate([.5*tie_width-.5*knot_width,(tie_height)+1.5,-iflub])
						linear_extrude(height =pattern_thickness-2*iflub)polygon([[0,0],[knot_width,0],[knot_width+.5*(knot_width_top-knot_width),knot_height],
						[-.5*(knot_width_top-knot_width),knot_height]],[[0,1,2,3,4]]);

						//body
						translate([0,0,-iflub])linear_extrude(height = pattern_thickness-2*iflub)polygon([[0,tie_width/2],[tie_width/2,0],[tie_width,tie_width/2],[(tie_width-knot_width)/2+knot_width,tie_height],
						[(tie_width-knot_width)/2,tie_height]],[[0,1,2,3,4,5]]);
					}

					union()
					{translate([tie_width/2,(tie_height+knot_height)/2,0])
					if ((body_or_pattern+preview_or_generate_Stl)>1)
						{
					HONEYCOMB(tie_width,tie_height+knot_height,9,radius,overlap_factor,polygon_edge_thickness,sides,polygon_stretch,distort_value,distort_off_on);
						}

					}
				}

				BODY_EDGE();
				KNOT_EDGE();

				//PATTERNCLIP R

				for (i=[
        			      [ tie_width-(.15*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.15,0],
					[ tie_width-(.3*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.3,0],
					[ tie_width-(.42*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.42,0],
					[ tie_width-(.55*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.55,0],
					[ tie_width-(.67*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.67,0],
					[ tie_width-(.8*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.8,0],
					[ tie_width-(.92*tie_height-0.5*tie_width)/(2*tie_height-tie_width)*(tie_width-knot_width)-2,(tie_height)*.92,0]])

					{translate(i)

						{rotate([0,0,atan((.5*(tie_width-knot_width))/(tie_height-0.5*tie_width))])
							translate([2,0,pattern_thickness])PATTERNCLIP();
						}
					}

				//PATTERNCLIP L

				for (i = [
					[((.15*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.15,0],
					[((.3*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.3,0],
					[((.42*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.42,0],
					[((.55*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.55,0],
					[((.67*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.67,0],
					[((.8*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.8,0],
					[((.92*tie_height-0.5*tie_width)/(2*tie_height-tie_width))*(tie_width-knot_width),(tie_height)*.92,0]])

					{translate(i)

						{rotate([0,0,180-atan((.5*(tie_width-knot_width))/(tie_height-0.5*tie_width))])
							translate([0,-10,pattern_thickness])PATTERNCLIP();
						}
					}

				//PATTERNCLIP BOTTOM 45 L
				for (i = [
					[.5*tie_width-1.4,0,0]])
					{translate(i)
						{rotate([0,0,225])
							translate([-1,-10-.25*tie_width,pattern_thickness])PATTERNCLIP();
						}
					}

				//PATTERNCLIP BOTTOM 45 R
				for (i = [
					[.5*tie_width-1.4,0]])
					{translate(i)
						{rotate([0,0,135])
							translate([-1,-2-.255*tie_width,pattern_thickness])rotate([0,0,180])PATTERNCLIP();
						}
					}

				//PATTERNCLIP KNOT L

						rotate([0,0,0])
							translate([.5*tie_width-.5*knot_width-.25*(knot_width_top-knot_width),tie_height+.5*knot_height+1.5,pattern_thickness-iflub])
							rotate([0,0,atan((.5*(knot_width_top-knot_width))/(knot_height))])translate([0,2,0])PATTERNCLIPKNOT();

				//PATTERNCLIP KNOT R

						rotate([0,0,0])
							translate([.5*tie_width+.5*knot_width+.25*(knot_width_top-knot_width),tie_height+.5*knot_height+1.5,pattern_thickness-iflub])
							rotate([0,0,180-atan((.5*(knot_width_top-knot_width))/(knot_height))])translate([0,-2,0])PATTERNCLIPKNOT();

			}

			union()
			{TIEBREAKS(pattern_thickness);
			}
		}
	}

			module TIEBREAKS(thickness){
			for (i=[[0,-.25+(tie_height-4.5)/4,-iflub],[0,-.25+(tie_height-1.5)/2,-iflub],[0,-.25+ .75*(tie_height-.5),-iflub]])
			{
			translate(i)
				cube([tie_width,1.,1.1*thickness]);
			}
			}

			module BODY_EDGE(){
			linear_extrude(height = pattern_thickness+iflub)polygon([
			[0,tie_width/2],
			[tie_width/2,0],
			[tie_width,tie_width/2],
			[(tie_width-knot_width)/2+knot_width,tie_height],
			[(tie_width-knot_width)/2+knot_width-2,tie_height],
			[tie_width-2,tie_width/2+1],
			[tie_width/2,2.8],
			[2,tie_width/2+1],
			[(tie_width-knot_width)/2+2,tie_height],
			[(tie_width-knot_width)/2,tie_height]],
			[[0,1,2,3,4,5,6,7,8,9,10]]);}

			module KNOT_EDGE(){
			translate([.5*tie_width-.5*knot_width,(tie_height)+1.5,0]){
			linear_extrude(height =pattern_thickness+1.1*iflub)polygon([
			[0,0],
			[knot_width,0],
			[knot_width+.5*(knot_width_top-knot_width),knot_height],
			[-.5*(knot_width_top-knot_width),knot_height],
			[2,2],
			[knot_width-2,2],
			[knot_width-2*((1.05*knot_width_top)/knot_height)+.5*(knot_width_top-knot_width),knot_height-2],
			[-.5*(knot_width_top-knot_width)+2*((1.05*knot_width_top)/knot_height),knot_height-2],],
			paths=[[0,1,2,3,0],[4,5,6,7]]);}}

			module HONEYCOMB(w,l,h,r,rmod,th,sides,stretch,distort,distortyn){

			columns = l/(r*3)+1;

			rows = w/(r*sqrt(3)/2*2)+2;

			translate([-w/1.3,l/1.8,pattern_thickness+iflub])
				rotate([0,0,-90])
					for(i = [0:rows]){

					translate([0,r*sqrt(3)/2*i*2,0])
						scale([pow(1+i/(21-distort),distortyn),1,1])
							for(i = [0:columns]){
								translate([r*i*3,0,0])
									for(i = [0:1]){
										translate([r*1.5*i,r*sqrt(3)/2*i,0])
											rotate([0,0,polygon_rotation])
											difference(){
										if(sides < 5){
											scale([1,pow((stretch+100)/100,stretch_off_on),1])cylinder(h=2*pattern_thickness, r = r+th+(r*rmod/50), center = true, $fn = sides);
										} else {
											scale([1,pow((stretch+100)/100,stretch_off_on),1])cylinder(h=2*pattern_thickness, r = r+(r*rmod/50), center = true, $fn = sides);
										}
										scale([1,pow((stretch+100)/100,stretch_off_on),1])cylinder(h=2.05*pattern_thickness, r = r-th+(r*rmod/50), center = true, $fn = sides);
									}
								}
							}
						}
					}

			module PATTERNCLIP(){
			translate([0,0,-iflub])rotate([90,0,180])linear_extrude(height = 10)polygon
			([[0,0],[1.8,0],[2,tie_thickness-.2],[2.7,tie_thickness+.6],[1.8,tie_thickness+1.3],[0,tie_thickness+1.3]],[[0,1,2,3,4,5]]);}

			module PATTERNCLIPKNOT(){
			translate([0,0,-iflub])rotate([90,0,0])linear_extrude(height = 10,center=true)polygon
			([[0,0],[1.8,0],[2,knot_thickness-.2],[2.7,knot_thickness+.6],[1.8,knot_thickness+1.3],[0,knot_thickness+1.3]],[[0,1,2,3,4,5]]);}

//___________________________________

```


[User]
Okay, I'm looking to create a custom tool tray, and I need an OpenSCAD file that can handle a few specific things. I'd like to have several rows for holding different sized tools, and be able to adjust the number of tools, the diameter of the holes, and the shape of the holes in each row. I want to be able to choose from round, square, and hexagonal holes.

Specifically, I'd like these parameters:

*   **Number of tools per row:** Should be adjustable per row.
*   **Hole diameter:** Adjustable per row.
*   **Hole shape:** Should allow me to select between round, square, and hexagon, adjustable per row.
*   **Tray height:** The overall height of the tray.
*   **Hole taper:** Allow for a taper in the holes, and be adjustable.
*   **Printing Orientation:** A way to flip the tray for printing orientation. I want to make sure I can print it face down.
*   **Tray thickness:** The thickness of the main tray, which should be adjustable, but perhaps not presented to the user.
*   **Leg thickness**: The thickness of the supporting legs, and likewise may not need to be presented to the user.
*   **Margin:** A consistent margin around the tool holes. Not adjustable directly, but would be good to be consistent.

It's important that the tray's overall dimensions are calculated based on the tool rows I define, and that each row is properly spaced, taking into consideration the hole diameter and some spacing factor between the holes. I am using the MCAD library, so please include that in the code. Specifically, the `regular_shapes.scad` file within that library will be required for the hexagon holes.

I would also like some finger-like cutouts around the top of each hole to make it easier to grab the tools. I have no explicit desire to specify the "finger" thickness, however I would like to know that it's an adjustable parameter. I think it would also make sense to add some rounding to the corners to get rid of sharp corners.

The overall design is essentially a flat tray with legs. The legs should not interfere with any of the holes.

[Assistant]
Custom_Tool_Tray.scad:
```
//4 Row Parametric Tool Tray
//Created by Jon Hollander, 3/1/2013

use <MCAD/regular_shapes.scad>

//Tool Row 1

number_of_tools_in_row_1 = 3;
row_1_hole_diameter = 26;
row_1_hole_shape = 0; //[0:Round,1:Square, 2: Hex]

//Tool Row 2

number_of_tools_in_row_2 = 4;
row_2_hole_diameter = 15;
row_2_hole_shape = 1; //[0:Round,1:Square, 2: Hex]

//Tool Row 3

number_of_tools_in_row_3 = 5;
row_3_hole_diameter = 10;
row_3_hole_shape = 0; //[0:Round,1:Square, 2: Hex]

//Tool Row 4

number_of_tools_in_row_4 = 5;
row_4_hole_diameter = 10;
row_4_hole_shape = 2; //[0:Round,1:Square, 2: Hex]

// Other User Options

tray_height = 25;

hole_taper = 4;

turn_upside_down_for_printing = 0; //[0:No, 180:Yes]

//Constant Parameters

tray_thickness = 5+0; //Add zero to parameter so Thingiverse customizer excludes this from user form.

leg_thickness = 2+0;

y_margin = 5+0;

//Global Derived Variables

//Set hole_dia to 0 if row is empty.
	r1_temp = (number_of_tools_in_row_1 == 0 ? 0:row_1_hole_diameter);

	r2_temp = (number_of_tools_in_row_2 == 0 ? 0:row_2_hole_diameter);

	r3_temp = (number_of_tools_in_row_3 == 0 ? 0:row_3_hole_diameter);

	r4_temp = (number_of_tools_in_row_4 == 0 ? 0:row_4_hole_diameter);

	tray_width = max(
			r1_temp*(1.5*number_of_tools_in_row_1+1),
			max(r2_temp*(1.5*number_of_tools_in_row_2+1),
				max(	r3_temp*(1.5*number_of_tools_in_row_3+1),
					r4_temp*(1.5*number_of_tools_in_row_4+1))));

	tray_depth = 1.5*(r1_temp+r2_temp+r3_temp+r4_temp)+2*y_margin;

	max_hole = max(r1_temp,
					max(r2_temp,
						max(r3_temp,r4_temp)));

	corner_width = .1*tray_width;
	corner_depth = .1*tray_depth;
	corner_taper = .1+0;

//Draw stuff!
generate_tool_tray();

module generate_tool_tray(){
	translate([0,0,tray_thickness/2])
	rotate([turn_upside_down_for_printing, 0,0])

	union(){

		// Draw legs first so operations don't cut into the top tray
		tray_legs();

		//Top tray

		tray_top();

	}

}

module tray_top(){
	union(){
		difference(){
					cube(size=[tray_width,tray_depth,tray_thickness], center = true);
					tool_hole_row_1();
					tool_hole_row_2();
					tool_hole_row_3();
					tool_hole_row_4();
				}
		tool_finger_row_1();
		tool_finger_row_2();
		tool_finger_row_3();
		tool_finger_row_4();
	}
}

module tool_hole_row_1(){
	tool_space_1 = tray_width/(2*number_of_tools_in_row_1+1);
	tool_y_1 = tray_depth/2-.5*r1_temp-y_margin;

	if(number_of_tools_in_row_1 != 0){
		tool_holes_row(-tray_width/2+1.5*tool_space_1,
						tool_y_1,
						tray_thickness,
						row_1_hole_diameter,
						tool_space_1,
				 		number_of_tools_in_row_1,
						row_1_hole_shape);
	}
}

module tool_finger_row_1(){
	tool_space_1 = tray_width/(2*number_of_tools_in_row_1+1);
	tool_y_1 = tray_depth/2-.5*r1_temp-y_margin;

	if(number_of_tools_in_row_1 != 0){
		tool_fingers_row(-tray_width/2+1.5*tool_space_1,
						tool_y_1,
						tray_thickness,
						row_1_hole_diameter,
						tool_space_1,
				 		number_of_tools_in_row_1,
						row_1_hole_shape);
	}
}

module tool_hole_row_2(){
	tool_space_2 = tray_width/(2*number_of_tools_in_row_2+1);
	tool_y_2 = tray_depth/2-1.5*r1_temp-.5*r2_temp-y_margin;

	if(number_of_tools_in_row_2 != 0){
		tool_holes_row(-tray_width/2+1.5*tool_space_2,
						tool_y_2,
						tray_thickness,
						row_2_hole_diameter,
						tool_space_2,
				 		number_of_tools_in_row_2,
						row_2_hole_shape);
	}
}

module tool_finger_row_2(){
	tool_space_2 = tray_width/(2*number_of_tools_in_row_2+1);
	tool_y_2 = tray_depth/2-1.5*r1_temp-.5*r2_temp-y_margin;

	if(number_of_tools_in_row_2 != 0){
		tool_fingers_row(-tray_width/2+1.5*tool_space_2,
						tool_y_2,
						tray_thickness,
						row_2_hole_diameter,
						tool_space_2,
				 		number_of_tools_in_row_2,
						row_2_hole_shape);
	}
}

module tool_hole_row_3(){
	tool_space_3 = tray_width/(2*number_of_tools_in_row_3+1);
	tool_y_3 = tray_depth/2-1.5*r1_temp-1.5*r2_temp-.5*r3_temp-y_margin;

	if(number_of_tools_in_row_3 != 0){
		tool_holes_row(-tray_width/2+1.5*tool_space_3,
					tool_y_3,
					tray_thickness,
					row_3_hole_diameter,
					tool_space_3,
			 		number_of_tools_in_row_3,
					row_3_hole_shape);
	}
}

module tool_finger_row_3(){
	tool_space_3 = tray_width/(2*number_of_tools_in_row_3+1);
	tool_y_3 = tray_depth/2-1.5*r1_temp-1.5*r2_temp-.5*r3_temp-y_margin;

	if(number_of_tools_in_row_3 != 0){
		tool_fingers_row(-tray_width/2+1.5*tool_space_3,
					tool_y_3,
					tray_thickness,
					row_3_hole_diameter,
					tool_space_3,
			 		number_of_tools_in_row_3,
					row_3_hole_shape);
	}
}

module tool_hole_row_4(){
	tool_space_4 = tray_width/(2*number_of_tools_in_row_4+1);
	tool_y_4 = tray_depth/2-1.5*r1_temp-1.5*r2_temp-1.5*r3_temp-.5*r4_temp-y_margin;

	if(number_of_tools_in_row_4 != 0){
		tool_holes_row(-tray_width/2+1.5*tool_space_4,
					tool_y_4,
					tray_thickness,
					row_4_hole_diameter,
					tool_space_4,
			 		number_of_tools_in_row_4,
					row_4_hole_shape);
	}
}

module tool_finger_row_4(){
	tool_space_4 = tray_width/(2*number_of_tools_in_row_4+1);
	tool_y_4 = tray_depth/2-1.5*r1_temp-1.5*r2_temp-1.5*r3_temp-.5*r4_temp-y_margin;

	if(number_of_tools_in_row_4 != 0){
		tool_fingers_row(-tray_width/2+1.5*tool_space_4,
					tool_y_4,
					tray_thickness,
					row_4_hole_diameter,
					tool_space_4,
			 		number_of_tools_in_row_4,
					row_4_hole_shape);
	}
}

module tool_holes_row(row_x_start, row_y, tray_thk, hole_dia, hole_space, num_holes, hole_type = 0 ){

	//h1 = tray_thk/(hole_taper/2)*hole_dia/2;
//	h2 = h1 + 1;
//	dia2 = h2*hole_dia/h1;

	for (tool_index = [0:num_holes-1]){
		if(hole_type == 0){
			translate([row_x_start+tool_index*2*hole_space,row_y,-tray_thk/2-10])
				cylinder(h=tray_thk+20, r=hole_dia/2,$fs=.1);
		}

		if(hole_type == 1){
			translate([row_x_start+tool_index*2*hole_space,row_y,0])
				cube(center=true, size = [hole_dia, hole_dia, tray_thk+2]);
		}

		if(hole_type == 2){
			translate([row_x_start+tool_index*2*hole_space,row_y,-tray_thk/2-1])
			hexagon_prism(tray_thk+2, hole_dia/2);
		}
	}
}

module tool_fingers_row(row_x_start, row_y, tray_thk, hole_dia, hole_space, num_holes, hole_type = 0,finger_thk=2){

	for (tool_index = [0:num_holes-1]){
		translate([row_x_start+tool_index*2*hole_space,row_y,-tray_thk/2]) rotate([180, 0, 0]) tool_finger(hole_type, hole_dia, hole_taper, tray_thk, finger_thk);
	}
}

module tool_finger(hole_type=2, hole_dia = 4, hole_taper = 2, tray_thk=5, finger_thk=2){

	if (hole_type == 0){
			//Round Holes
			//Pyramid height = tray_thk*(dia/2+finger_thk)/(hole_taper)
			//This ensures that pyramid has width of dia-hole_taper at height of tray_thk

			difference(){
				cylinder(r1=hole_dia/2+finger_thk, r2=hole_dia/2+finger_thk-hole_taper/2,h=tray_thk,$fs=.1);
				translate([0,0,-.1]) cylinder(r1=hole_dia/2, r2=hole_dia/2-hole_taper/2,h=tray_thk+.2,$fs=.1);
				translate([hole_dia/2+finger_thk,0,-.1]) cube([1.5*(2*finger_thk+hole_taper),.4*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);
				translate([-hole_dia/2-finger_thk,0,-.1]) cube([1.5*(2*finger_thk+hole_taper),.4*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);
				translate([0, hole_dia/2+finger_thk,-.1]) cube([.4*(2*finger_thk+hole_taper),1.5*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);
				translate([0, -hole_dia/2-finger_thk,-.1]) cube([.4*(2*finger_thk+hole_taper),1.5*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);
			}
 	}

	if (hole_type == 1){
		//Square Holes
		//Pyramid height = tray_thk*(dia/2+finger_thk)/(hole_taper)
		//This ensures that pyramid has width of dia-hole_taper at height of tray_thk

		difference(){
			square_pyramid((hole_dia+2*finger_thk), (hole_dia+2*finger_thk),tray_thk*(hole_dia/2+finger_thk)/(hole_taper/2));
			translate([0,0,-.1]) square_pyramid(hole_dia, hole_dia, tray_thk*(hole_dia/2+finger_thk)/(hole_taper/2)+.1);
			translate([0,0,tray_thk+.5*tray_thk*(hole_dia/2+finger_thk)]) cube([hole_dia+2*finger_thk,hole_dia+2*finger_thk,tray_thk*(hole_dia/2+finger_thk)], center=true);
			translate([hole_dia/2+finger_thk,0,-.1]) cube([2*(2*finger_thk+hole_taper),.3*(hole_dia/2+finger_thk),2*tray_thk+.4], center=true);
			translate([-hole_dia/2-finger_thk,0,-.1]) cube([2*(2*finger_thk+hole_taper),.3*(hole_dia/2+finger_thk),2*tray_thk+.4], center=true);
			translate([0, hole_dia/2+finger_thk,-.1]) cube([.3*(hole_dia/2+finger_thk),2*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);
			translate([0, -hole_dia/2-finger_thk,-.1]) cube([.3*(hole_dia/2+finger_thk),2*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);

		}

	}

	if (hole_type == 2){
		difference(){
			cylinder(r1=hole_dia/2+finger_thk, r2=hole_dia/2+finger_thk-hole_taper/2,h=tray_thk,$fs=.1);
			translate([0,0,-.1]) cylinder(r1=hole_dia/2, r2=hole_dia/2-hole_taper/2,h=tray_thk+.2,$fs=.1);
			translate([hole_dia/2+finger_thk,0,-.1]) cube([2*(2*finger_thk+hole_taper),.3*(hole_dia/2+finger_thk),2*tray_thk+.4], center=true);
			translate([-hole_dia/2-finger_thk,0,-.1]) cube([2*(2*finger_thk+hole_taper),.3*(hole_dia/2+finger_thk),2*tray_thk+.4], center=true);
			translate([0, hole_dia/2+finger_thk,-.1]) cube([.3*(hole_dia/2+finger_thk),2*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);
			translate([0, -hole_dia/2-finger_thk,-.1]) cube([.3*(hole_dia/2+finger_thk),2*(2*finger_thk+hole_taper),2*tray_thk+.4], center=true);

			//hull(){
//				hexagon_prism(.1,hole_dia/2+finger_thk);
//				translate([0,0,tray_thk-.1]) hexagon_prism(.1, hole_dia/2+finger_thk-hole_taper/2);}
//			translate([0,0,-.2])
//				hull(){
//					hexagon_prism(.1,hole_dia/2);
//					translate([0,0,tray_thk]) hexagon_prism(.3, .9*(hole_dia/2-hole_taper/2));}

		}

	}

}

module tray_legs(){
	difference(){
		translate([0,0,-tray_height/2-tray_thickness/2])
			cube(size=[tray_width,tray_depth,tray_height], center = true);

		//Center Cutouts
		translate([0,0,-tray_height/2-tray_thickness/2])
			cube(size=[tray_width-2*leg_thickness,tray_depth-2*leg_thickness,tray_height+2], center = true);

		//Leg Cutouts
		translate([0, tray_depth/2+1,-tray_height-tray_thickness/2-1]) rotate([90,0,0])	linear_extrude(height = tray_depth+2) trapezoid(tray_width-2*corner_width, tray_height+2, corner_taper);

		translate([-tray_width/2-1,0,-tray_height-tray_thickness/2-1]) rotate([90,0,90])	linear_extrude(height = tray_width+2) trapezoid(tray_depth-2*corner_depth, tray_height+2, corner_taper);

	}

}

module trapezoid(twidth, theight, taper){
	polygon(points = [ 	[-twidth/2,0],
						[twidth/2,0],
						[(1-taper)*(twidth/2),theight],
						[(1-taper)*(-twidth/2),theight]],

			paths =	[	[0,1,2,3]]
		);
}

```


[User]
Okay, I need a parametric spool holder designed in OpenSCAD. I'm tired of my filament getting tangled and causing failed prints! I've got some specific requirements and a basic idea of what I'm after, so hopefully you can help.

Essentially, the design should consist of four printable parts: two legs and two centering pulleys that hold the spool. The legs will support the spool and have bearing holders. The centering pulleys will fit into the spool's center hole and keep it aligned, they should also accommodate different sized spool holes to some extent. The whole thing should be assembled using a threaded rod and nuts.

Here are the main parameters I need to be able to adjust:

*   **Spool Dimensions:**
    *   `Min_Spool_Hole_Diameter`: The smallest diameter of the spool's center hole. I need this to be able to grip spools that have a small center.
    *   `Max_Spool_Hole_Diameter`: The largest diameter of the spool's center hole. This lets us accommodate variations in spools.
    *   `Spool_Diameter`: The overall diameter of the spool. This is used mainly for leg spacing, so it doesn't have to be *that* precise.
    *   `Under_Spool_Clearance`: The amount of space under the spool so it doesn't hit the table.

*   **Axle and Fasteners:**
    *   `Axle_Diameter`: The diameter of the threaded rod. I'm using M8, so this should be set accordingly.
    *   `Axle_Clearance`: The amount of extra space around the axle to ensure it moves freely.
    *   `Nut_Size`: The outer diameter (across flats) of the nut.
    *   `Nut_width`: The thickness of the nut.

*   **Flanges:**
    *   `Flange_Width`: The thickness of the flange on the centering pulley.

*   **Bearings:**
    *   `Bearing_Outer_Diameter`: The outer diameter of the bearing that will fit into the leg.
    *   `Bearing_Width`: The thickness of the bearing.

The design should use a difference operation to make a nut-shaped hole in each of the centering pulleys. The legs should have a cutout to fit the bearings. And the legs should include feet that can be printed without needing supports.

I'd like the design to use the `rotate` and `translate` transformation modules for positioning things, and I'd like to use named variables throughout to make it easier to read and modify. Please make sure to use comments in the OpenSCAD code so I can understand what's going on.

I think that's everything... Let me know if you need more info. Thanks! Oh, and I don't need any libraries other than OpenSCAD's built-in modules for now.

[Assistant]
spool_holder.scad:
```
// Copyright (c) 2013 by Jean-Louis Paquelin (jlp6k).
// This work is licensed under the Creative Commons Attribution
// Partage dans les Mêmes Conditions 3.0 France License.
// To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/fr/
// CC-BY-SA

// Created 2013-02-22
// Last modified 2013-03-02

// dimensions are set in millimeters

Render = 2; // [0:centering_pulley, 1: leg, 2:setup]

Min_Spool_Hole_Diameter = 27;
Max_Spool_Hole_Diameter = 37;
Spool_Diameter = 160;
Under_Spool_Clearance = 20;

Axle_Diameter = 8;
Axle_Clearance = 1;

Nut_Size = 14.62;
Nut_width = 6.26;

Flange_Width = 10;

Bearing_Outer_Diameter = 22;
Bearing_Width = 7;

if(Render == 0)
    centering_pulley(Min_Spool_Hole_Diameter, Max_Spool_Hole_Diameter, Axle_Diameter, Nut_Size, Nut_width, Flange_Width);
else if(Render == 1)
    leg(Spool_Diameter, Axle_Diameter, Bearing_Outer_Diameter, Bearing_Width);
else {
    // complete setup
    rotate([0, -90, 45]) {
        color("grey")
        union() {
            // spool
            cylinder(h = Spool_Diameter / 2, r = Spool_Diameter / 2, center = true);

            // axle
            cylinder(h = Spool_Diameter * 1.5, r = Axle_Diameter / 2, center = true);

            // nuts
            translate([0, 0, (-Spool_Diameter / 3.5) - (Max_Spool_Hole_Diameter - Min_Spool_Hole_Diameter + Flange_Width - Nut_width) / 2])
                rotate([0, 0, 45])
                    cylinder(h = Nut_width + 0.1, r = Nut_Size / 2, center = true, $fn = 6);
            translate([0, 0, (Spool_Diameter / 3.5) + (Max_Spool_Hole_Diameter - Min_Spool_Hole_Diameter + Flange_Width - Nut_width) / 2])
                rotate([0, 0, 30])
                    cylinder(h = Nut_width + 0.1, r = Nut_Size / 2, center = true, $fn = 6);

            // bearings
            translate([0, 0, Spool_Diameter / 2])
                cylinder(h = Bearing_Width + 0.1, r = Bearing_Outer_Diameter / 2, center = true);
            translate([0, 0, -Spool_Diameter / 2])
                cylinder(h = Bearing_Width + 0.1, r = Bearing_Outer_Diameter / 2, center = true);
        }
        translate([0, 0, -Spool_Diameter / 3.5])
            rotate([0, 0, 45])
                centering_pulley(Min_Spool_Hole_Diameter, Max_Spool_Hole_Diameter, Axle_Diameter, Nut_Size, Nut_width, Flange_Width);
        translate([0, 0, Spool_Diameter / 3.5])
            rotate([180, 0, 30])
                centering_pulley(Min_Spool_Hole_Diameter, Max_Spool_Hole_Diameter, Axle_Diameter, Nut_Size, Nut_width, Flange_Width);
        translate([-((Spool_Diameter / 2) + Under_Spool_Clearance) / 2, 0, Spool_Diameter / 2])
            leg(Spool_Diameter, Axle_Diameter, Bearing_Outer_Diameter, Bearing_Width);
        translate([-((Spool_Diameter / 2) + Under_Spool_Clearance) / 2, 0, -Spool_Diameter / 2])
            leg(Spool_Diameter, Axle_Diameter, Bearing_Outer_Diameter, Bearing_Width);
    }
}

module leg(Spool_Diameter, Axle_Diameter, Bearing_Outer_Diameter, Bearing_Width) {
    leg_height = (Spool_Diameter / 2) + Under_Spool_Clearance;
    bearing_holder_radius = (Bearing_Outer_Diameter / 2) + Bearing_Width;

    difference() {
        union() {
            // leg base
            difference() {
                cube([leg_height, bearing_holder_radius * 2, Bearing_Width], center = true);
                cube([leg_height - 2 * Bearing_Width, (bearing_holder_radius  - Bearing_Width) * 2, Bearing_Width + 1], center = true);
            }

            // bearing holder
            translate([leg_height / 2, 0, 0])
                cylinder(h = Bearing_Width, r = (Bearing_Outer_Diameter / 2) + Bearing_Width, center = true, $fn = 100);

            // feet
            translate([-leg_height / 4, bearing_holder_radius * 1.9, 0])
                foot(leg_height / 2, bearing_holder_radius * 2, Bearing_Width);
            translate([-leg_height / 4, -bearing_holder_radius * 1.9, 0])
                rotate([180, 0, 0])
                    foot(leg_height / 2, bearing_holder_radius * 2, Bearing_Width);
        }

        // bearing hole
        translate([leg_height / 2, 0, 0])
            cylinder(h = Bearing_Width + 1, r = Bearing_Outer_Diameter / 2, center = true, $fn = 100);
    }
}

module foot(x, y, z) {
    inner_radius = x - z;

    scale([1, y / x, 1])
    difference() {
        difference() {
            translate([-x / 2, -x / 2, 0])
                 cylinder(h = z, r = x, center = true, $fn = 100);
            translate([-x - x / 2, -x / 2, 0])
                cube([x * 2, x *  2, z + 1], center = true);
            translate([-x / 2, -x - x / 2, 0])
                cube([x * 2, x *  2, z + 1], center = true);
        }

    scale([1, 1, 1.1])
        difference() {
            translate([-x / 2, -x / 2, 0])
                 cylinder(h = z, r = inner_radius, center = true, $fn = 100);
            translate([-inner_radius - x / 2 + z, -x / 2, 0])
                cube([inner_radius * 2, inner_radius *  2, z + 1], center = true);
            translate([-x / 2, -inner_radius - x / 2, 0])
                cube([inner_radius * 2, inner_radius *  2, z + 1], center = true);
        }
    }
}

module centering_pulley(Min_Spool_Hole_Diameter, Max_Spool_Hole_Diameter, Axle_Diameter, Nut_Size, Nut_width) {
    centering_pulley_width = Max_Spool_Hole_Diameter - Min_Spool_Hole_Diameter;
    flange_radius = (Max_Spool_Hole_Diameter + Flange_Width) / 2;
    hole_extension = 1;

    difference() {
        translate([0, 0, Flange_Width / 2])
            union() {
                // auto centering pulley base
                cylinder(h = centering_pulley_width, r1 = Max_Spool_Hole_Diameter / 2, r2 = Min_Spool_Hole_Diameter / 2, center = true, $fn = 100);

                // flange
                translate([0, 0, -(centering_pulley_width + Flange_Width) / 2])
                    cylinder(h = Flange_Width, r = flange_radius, center = true, $fn = 6);
            }

        // nut hole
        translate([0, 0, -(centering_pulley_width + Flange_Width - Nut_width + hole_extension) / 2])
            cylinder(h = Nut_width + hole_extension, r = Nut_Size / 2, center = true, $fn = 6);

        // axle hole
        cylinder(h = centering_pulley_width + Flange_Width + hole_extension, r = (Axle_Diameter + Axle_Clearance) / 2, center = true, $fn = 100);
    }
}

```