[User]
Hey, I'm looking to get a parametric OpenSCAD design for a linear bushing that uses PTFE tubing as the bearing surface. I've seen a couple of designs on Thingiverse, but I want a fresh start and something a bit more tailored to my needs, plus I want to make sure it's well-documented, and that it's generated using modern OpenSCAD practices.

Basically, I need a cylindrical bushing with grooves inside to hold lengths of PTFE tubing. These tubes should be arranged in a circular pattern inside the bushing and be slightly twisted along the length of the bushing. I'd like to be able to control several parameters like:

*   **Inner Diameter:** The diameter of the rod that will slide inside the bushing.
*   **Outer Diameter:** The diameter of the bushing for mounting.
*   **Bushing Length:** The overall length of the bushing.
*   **Number of PTFE Tubes:** The number of PTFE tubes used in the design.
*   **PTFE Twist:** The amount of twist per cm along the bushing length.
*   **PTFE Outer Diameter:** The outer diameter of the PTFE tubing.
*   **PTFE Inner Diameter:** The inner diameter of the PTFE tubing.
*   **PTFE Overlap:** How much the PTFE tubing should protrude into the inner diameter of the bushing.
*   **PTFE Half Cut:** A boolean to optionally cut the tubes in half using a square cut.
*  **Cutting Tool:** A boolean to optionally show a separate cutting tool, which will be a cylinder with a slot that will match the selected PTFE tube parameters, for assisting in cutting the tubing.
* **Bushing End Cap**: A boolean to optionally include a flange on both ends of the bushing.

I'd also like to be able to easily adjust the tolerances for printing. Can you provide an OpenSCAD file with all these parameters? It would be great if the file was well-commented to help me understand what's going on, and that it used modern OpenSCAD practices (e.g. modules instead of functions, if you think that's best). I also have no idea how to use the libraries, so if you want to include them, make sure I can understand which specific libraries I need and that I don't have to go hunting for individual .scad files within the libraries. The libraries themselves must be included and must not be in separate files or require manual inclusion of multiple files or subdirectories.

Thanks!

[Assistant]
parametric_linear_ptfe_bushing_V2_cutter.scad:
```
// parametric linear PTFE bushing by Bradley Muller - http://www.thingiverse.com/papsnoek/
// 2017-06-16 V2

//Based of the design of parametric linear bushing by Dennis Hofmann - http://www.thingiverse.com/sasdensas/

// Use the outer diameter of the rod + tolerance value of your printer. Do some test prints before print a whole set of bushings. Best way in my case is to choose the bushing where can push it in with some effort on the rod and slide it by hand without any tools. Then slide it back and forth along the rod, until the bushing has the expected behavior. Standard values: 6.00 for LM6(L)UU, 8.00 (default 8.15) for LM8(L)UU, 10.00 for LM10(L)UU, 12.00 for LM12(L)UU, 16.00 for LM16UU, ...
inner_diameter = 8.15;

// Use the inner diameter of housing where the bushing sits + tolerance value of your printer. Depends on the filament you use and the total wall thickness of the bushings, the bushing shouldn't fit to tight in the housing. Standard values: 12.0 for LM6(L)UU, 15.0 (default) for LM8(L)UU, 19.0 for LM10(L)UU, 21.0 for LM12(L)UU, 28.0 for LM16UU, ...
outer_diameter = 15.0;

// Standard values: 19.0 for LM6UU, 24.0 for LM8UU, 29.0 for LM10UU, 30.0 for LM12UU, 35.0 for LM6LUU, 37.0 for LM16UU, 45.0 for LM8LUU, 55.0 for LM10LUU, 57.0 for LM12LUU, ...
bushing_length = 24.0;

// Choose a number of tubes to use
number_of_PTFE = 7; //[3:20]

// Choose how much twist to use.
PTFE_twist_per_cm = 30;

//This will be the outer diameter of the PTFE tube used + tolerance.
PTFE_outer_diameter = 4.8;

//This will be the Inner diameter of the PTFE tube used + tolerance.
PTFE_inner_diameter = 2;

//This will be the about of PTFE Tube that "Sticks out" from the printed bushing on the inside.
PTFE_overlap=1;

//If you use small enough diameter PTFE tube, you might get away with not cutting the PTFE in half
PTFE_half=true; // [true: false]

//create cutting to to slice PTFE in half.
show_cutting_tool=true; // [true: false]

//Experimental for now. Not used or tested. Problem is there is no way to insert the tubing!
bushing_end_cap=false;

//Calculated Values used in Script
PTFE_twist = PTFE_twist_per_cm * bushing_length/10;
e=0.2;
inner_diameter_actual=inner_diameter+PTFE_overlap;
ptfe_place=(inner_diameter_actual/2)+(PTFE_outer_diameter/2)-(PTFE_overlap/2);

//Bushing Outer Cylinder
module shell() {
    union(){
        linear_extrude(bushing_length) {
            difference() {
                circle(d = outer_diameter, center = true, $fn=100);
                circle(d = inner_diameter_actual, center = true, $fn=100);
            }
        }
        if (bushing_end_cap){
                translate([0, 0, bushing_length]){
                linear_extrude(2) {
                    difference() {
                        circle(d = outer_diameter, center = true, $fn=100);
                        circle(d = inner_diameter, center = true, $fn=100);
                    }
                }

            }
        }
         if (bushing_end_cap){
                translate([0, 0, -2]){
                linear_extrude(2) {
                    difference() {
                        circle(d = outer_diameter, center = true, $fn=100);
                        circle(d = inner_diameter, center = true, $fn=100);
                    }
                }

            }
        }
    }
}

module ptfe_tube() {
    if (PTFE_half) {
        difference(){
            circle(d = PTFE_outer_diameter, center = true, $fn=100);
            circle(d = PTFE_inner_diameter, center = true, $fn=100);
            translate([0,-PTFE_outer_diameter,0])square(2*PTFE_outer_diameter);
        }
    }
    else {
        circle(d = PTFE_outer_diameter, center = true, $fn=100);
    }

}

module ptfe_tubes() {
    linear_extrude(bushing_length, twist = PTFE_twist) {
        for(ptfe_tube = [0 : number_of_PTFE])
            rotate([0, 0, 360 / number_of_PTFE * ptfe_tube]) {
                translate([ptfe_place, 0, 0]) {
                    ptfe_tube();
                }
            }
    }
}

//Bushing
difference(){
    shell();
    ptfe_tubes();
}

//Cutting tool
module ptfe_cutting_tool(){
    //ptfe tube
    union(){
        union(){
            translate([0, 0, 4]) {
                difference(){
                linear_extrude(bushing_length) {
                    difference(){
                        circle(d = PTFE_outer_diameter+5, center = true, $fn=100);
                        square([PTFE_outer_diameter+6, 0.8], center = true);
                    }
                }
                linear_extrude(bushing_length) {
                    circle(d = PTFE_outer_diameter-0.6, center = true, $fn=100);
                }
            }
            linear_extrude(2) {
                circle(d = PTFE_inner_diameter-0.5, center = true, $fn=100);
            }
        }
        }
        linear_extrude(4){
            circle(d = PTFE_outer_diameter+5, center = true, $fn=100);
        }
    }
}

if (show_cutting_tool) {
    translate([outer_diameter, 0, 0]) ptfe_cutting_tool() ;
}

```
parametric_linear_ptfe_bushing_V2.scad:
```
// parametric linear PTFE bushing by Bradley Muller - http://www.thingiverse.com/papsnoek/
// 2017-06-16 V2

//Based of the design of parametric linear bushing by Dennis Hofmann - http://www.thingiverse.com/sasdensas/

// Use the outer diameter of the rod + tolerance value of your printer. Do some test prints before print a whole set of bushings. Best way in my case is to choose the bushing where can push it in with some effort on the rod and slide it by hand without any tools. Then slide it back and forth along the rod, until the bushing has the expected behavior. Standard values: 6.00 for LM6(L)UU, 8.00 (default 8.15) for LM8(L)UU, 10.00 for LM10(L)UU, 12.00 for LM12(L)UU, 16.00 for LM16UU, ...
inner_diameter = 8.15;

// Use the inner diameter of housing where the bushing sits + tolerance value of your printer. Depends on the filament you use and the total wall thickness of the bushings, the bushing shouldn't fit to tight in the housing. Standard values: 12.0 for LM6(L)UU, 15.0 (default) for LM8(L)UU, 19.0 for LM10(L)UU, 21.0 for LM12(L)UU, 28.0 for LM16UU, ...
outer_diameter = 15.0;

// Standard values: 19.0 for LM6UU, 24.0 for LM8UU, 29.0 for LM10UU, 30.0 for LM12UU, 35.0 for LM6LUU, 37.0 for LM16UU, 45.0 for LM8LUU, 55.0 for LM10LUU, 57.0 for LM12LUU, ...
bushing_length = 24.0;

// Choose a number of tubes to use
number_of_PTFE = 7; //[3:20]

// Choose how much twist to use.
PTFE_twist_per_cm = 30;

//This will be the outer diameter of the PTFE tube used + tolerance.
PTFE_outer_diameter = 4.8;

//This will be the Inner diameter of the PTFE tube used + tolerance.
PTFE_inner_diameter = 2;

//This will be the about of PTFE Tube that "Sticks out" from the printed bushing on the inside.
PTFE_overlap=1;

//If you use small enough diameter PTFE tube, you might get away with not cutting the PTFE in half
PTFE_half=true; // [true: false]

//create cutting to to slice PTFE in half.
show_cutting_tool=false; // [true: false]

//Experimental for now. Not used or tested. Problem is there is no way to insert the tubing!
bushing_end_cap=false;

//Calculated Values used in Script
PTFE_twist = PTFE_twist_per_cm * bushing_length/10;
e=0.2;
inner_diameter_actual=inner_diameter+PTFE_overlap;
ptfe_place=(inner_diameter_actual/2)+(PTFE_outer_diameter/2)-(PTFE_overlap/2);

//Bushing Outer Cylinder
module shell() {
    union(){
        linear_extrude(bushing_length) {
            difference() {
                circle(d = outer_diameter, center = true, $fn=100);
                circle(d = inner_diameter_actual, center = true, $fn=100);
            }
        }
        if (bushing_end_cap){
                translate([0, 0, bushing_length]){
                linear_extrude(2) {
                    difference() {
                        circle(d = outer_diameter, center = true, $fn=100);
                        circle(d = inner_diameter, center = true, $fn=100);
                    }
                }

            }
        }
         if (bushing_end_cap){
                translate([0, 0, -2]){
                linear_extrude(2) {
                    difference() {
                        circle(d = outer_diameter, center = true, $fn=100);
                        circle(d = inner_diameter, center = true, $fn=100);
                    }
                }

            }
        }
    }
}

module ptfe_tube() {
    if (PTFE_half) {
        difference(){
            circle(d = PTFE_outer_diameter, center = true, $fn=100);
            circle(d = PTFE_inner_diameter, center = true, $fn=100);
            translate([0,-PTFE_outer_diameter,0])square(2*PTFE_outer_diameter);
        }
    }
    else {
        circle(d = PTFE_outer_diameter, center = true, $fn=100);
    }

}

module ptfe_tubes() {
    linear_extrude(bushing_length, twist = PTFE_twist) {
        for(ptfe_tube = [0 : number_of_PTFE])
            rotate([0, 0, 360 / number_of_PTFE * ptfe_tube]) {
                translate([ptfe_place, 0, 0]) {
                    ptfe_tube();
                }
            }
    }
}

//Bushing
difference(){
    shell();
    ptfe_tubes();
}

//Cutting tool
module ptfe_cutting_tool(){
    //ptfe tube
    union(){
        union(){
            translate([0, 0, 4]) {
                difference(){
                linear_extrude(bushing_length) {
                    difference(){
                        circle(d = PTFE_outer_diameter+5, center = true, $fn=100);
                        square([PTFE_outer_diameter+6, 0.8], center = true);
                    }
                }
                linear_extrude(bushing_length) {
                    circle(d = PTFE_outer_diameter-0.6, center = true, $fn=100);
                }
            }
            linear_extrude(2) {
                circle(d = PTFE_inner_diameter-0.5, center = true, $fn=100);
            }
        }
        }
        linear_extrude(4){
            circle(d = PTFE_outer_diameter+5, center = true, $fn=100);
        }
    }
}

if (show_cutting_tool) {
    translate([outer_diameter, 0, 0]) ptfe_cutting_tool() ;
}

```


[User]
I'd like to design a parametric soap box, something that can hold a bar of soap and also act as a soap dish. I need it to be customizable for different soap sizes. I'm envisioning a two-part design: a base/case and a top.

Here are the basic parameters I'm thinking of:

*   **`soap_length`**, **`soap_width`**, **`soap_height`**: These will define the internal dimensions of the soap box to fit different soap bars.
*   **`wall_thickness`**: The thickness of the walls of the box.
*   **`space`**: The amount of space left around the soap inside the box.
*   **`wave_width`**: This will control the width of the ridges on the bottom of the case, meant to let the water drain and air to circulate.
*   **`top_holes`**: The number of drainage holes or slits on the top of the box.
*   **`string_diameter`**: The diameter of optional holes to pass a string through the box. If set to zero, then these holes are disabled.
*   **`part`**: This will control whether the code generates only the case, the top, or both parts of the soapbox.

I need two modules: one called `case()` for the base and one called `top()` for the lid.

The `case()` module needs to:

*   Create a box with dimensions based on the soap dimensions, wall thickness, and space parameters.
*   Subtract a cavity from the box to hold the soap.
*   Include an optional string hole on both sides of the box.
*   Add wave-like ridges on the bottom to keep the soap from sitting in water.

The `top()` module should:

*   Create a box that fits over the case, with a lip that fits just inside the walls of the case.
*   Include drainage holes on top. These holes should be elongated slits, evenly spaced across the top.

The main module should instantiate `case()` and `top()` depending on the value of the `part` parameter.

I will need to be able to set `$fs` to a value like `0.5` as well.

I'd like the code to be in a single OpenSCAD file, please.

[Assistant]
soap_box.scad:
```
part = "both"; // [case:Case only,top:Top only,both:Case and top]

// In mm
soap_length = 85; // [40:1:120]
// In mm
soap_width = 55; // [20:1:100]
// In mm
soap_height = 20; // [10:1:60]

// Number of holes on the top
top_holes = 8; // [2:2:10]
// In mm, 0 to disable
string_diameter = 4; // [0:1:10]

// In mm
wall_thickness = 4.8; // [2:0.1:15]
// Space left around the soap, in mm
space = 4; //[1:0.1:20]
// Width of the ridges at the bottom, in mm
wave_width = 2; // [1:1:10]

/* [HIDDEN]*/
$fs=0.5;

module case(soap_length,soap_width,soap_height,wall_thickness,space,wave_width,string_diameter)
{
	length = soap_length + wall_thickness*2 + space*2;
	width = soap_width + wall_thickness*2 + space*2;
	height = soap_height + wall_thickness*2 + space + wave_width/2;

	inside_length = soap_length + space*2;
	inside_width = soap_width  + space*2;
	inside_height = soap_height + space*2;

	top_height = soap_height/2;

	/* MAIN SHAPE */
	union()
	{
		difference()
		{
			cube([width,length,height]);
			translate([wall_thickness,wall_thickness,wall_thickness])
				cube([inside_width,inside_length,inside_height+wall_thickness*2+space*2]);

			/* String holes */
			if ( string_diameter > 0 )
			{
				translate([width/2+wall_thickness,wall_thickness*2,height/2+top_height/2-string_diameter/2])
					rotate([90,0,0])
					cylinder(d=string_diameter,h=wall_thickness*4);
				translate([width/2-wall_thickness,wall_thickness*2,height/2+top_height/2-string_diameter/2])
					rotate([90,0,0])
					cylinder(d=string_diameter,h=wall_thickness*4);
			}
		}
		difference()
		{
			translate([0,0,height]) cube([wall_thickness/2,length,top_height]);
			translate([wall_thickness/2,-wall_thickness,height+top_height/2-wall_thickness/4])
				rotate([0,45,0])
				translate([-wall_thickness/4,0,0])
				cube([wall_thickness/4,inside_length+wall_thickness*1.5,wall_thickness/4]);
		}
		difference()
		{
			translate([width-wall_thickness/2,0,height]) cube([wall_thickness/2,length,top_height]);
			translate([width-wall_thickness/2,-wall_thickness,height+top_height/2-wall_thickness/4])
				rotate([0,45,0])
				translate([-wall_thickness/4,0,0])
				cube([wall_thickness/4,inside_length+wall_thickness*1.5,wall_thickness/4]);
		}
		translate([0,length-wall_thickness/2,height])
			cube([width,wall_thickness/2,top_height]);
	}

	/* RIDGES IN THE BOTTOM */
	translate([0,wave_width*0.25,0])
	{
		for(y=[length/2:-wave_width-wave_width/2:wall_thickness+space-wave_width*1.5])
		{
			hull()
			{
				translate([wall_thickness+space,y+wave_width/2,wall_thickness])
					sphere(d=wave_width);
				translate([wall_thickness+space+soap_width,y+wave_width/2,wall_thickness])
					sphere(d=wave_width);
			}
		}
		for(y=[length/2+wave_width+wave_width/2:wave_width+wave_width/2:wall_thickness+space+soap_length-wave_width/2])
		{
			hull()
			{
				translate([wall_thickness+space,y+wave_width/2,wall_thickness])
					sphere(d=wave_width);
				translate([wall_thickness+space+soap_width,y+wave_width/2,wall_thickness])
					sphere(d=wave_width);
			}
		}
	}
}

module top(soap_length,soap_width,soap_height,wall_thickness,space,wave_width,top_holes)
{
	length = soap_length + wall_thickness*1.5 + space*2;
	width = soap_width + wall_thickness + space*2;
	height = soap_height/2;

	inside_length = length - wall_thickness*1.5;
	inside_width = width - wall_thickness;
	inside_height = soap_height + space*2;

	/* MAIN SHAPE */
	difference()
	{
		cube([width,length,height]);
		translate([wall_thickness/2,wall_thickness,-wall_thickness])
			cube([inside_width,inside_length,height+wall_thickness*2]);
	}

	/* HOLES */
	for(y = [wall_thickness:inside_length/top_holes*2:inside_length])
	{
		translate([wall_thickness/2,y,0])
		{
			hull()
			{
				cube([inside_width-wall_thickness,inside_length/top_holes,1]);
				translate([-wall_thickness/2,0,height-1])
					cube([wall_thickness/2,inside_length/top_holes,1]);
			}

			translate([0,inside_length/top_holes,0]) hull()
			{
				translate([wall_thickness,0,0])
					cube([inside_width-wall_thickness,inside_length/top_holes,1]);
				translate([inside_width,0,height-1])
					cube([wall_thickness/2,inside_length/top_holes,1]);
			}
		}
	}

	translate([0,0,height/2-wall_thickness/4])
		rotate([0,45,0])
		translate([-wall_thickness/4,0,0])
		cube([wall_thickness/4,length-wall_thickness,wall_thickness/4]);
	translate([width,0,height/2-wall_thickness/4])
		rotate([0,45,0])
		translate([-wall_thickness/4,0,0])
		cube([wall_thickness/4,length-wall_thickness,wall_thickness/4]);
}

if ( part == "case" )
{
	color("LightBlue")
		case(soap_length,soap_width,soap_height,wall_thickness,space,wave_width,string_diameter);
}
else if (part == "top" )
{
	color("Ivory")
		top(soap_length,soap_width,soap_height,wall_thickness,space,wave_width,top_holes);
}
else
{
	color("LightBlue")
		case(soap_length,soap_width,soap_height,wall_thickness,space,wave_width,string_diameter);
	color("Ivory")
		translate([soap_width+wall_thickness*4+space*2,0,0])
		top(soap_length,soap_width,soap_height,wall_thickness,space,wave_width,top_holes);
}
```


[User]
Okay, I need an OpenSCAD design for a solar filter attachment for my binoculars.  I've got a specific size in mind, and I want to be able to adjust it.  The design will be in three parts: a holder that slides onto the binocular tube, a ring to secure the filter material, and a cover to protect the filter when not in use.

Specifically, I need the following to be adjustable parameters:

*   **`diameter`**:  The outside diameter of my binocular tube in millimeters. I need the holder to fit snugly. Let's start with 63.5mm.
*   **`wall_thickness`**: The thickness of all the walls of the 3D printed parts. I want this to be a multiple of my nozzle width, so let's use 1.2 mm as a starting value.
*   **`holder_annulus_width`**: The width of the ring on the holder that keeps the filter from falling through. I'm thinking 10 mm to start.
*  **`ring_annulus_width`**: The width of the ring that holds the filter in from the inside; needs to be slightly smaller than `holder_annulus_width`. Let's try 9 mm to start.
*   **`holder_depth`**: The depth of the holder that slides onto the binocular tube. Let's start with 10 mm.
*  **`cover_depth`**: The depth of the cover. Start with 7 mm.
*   **`cover_gap`**: The gap between the cover and holder to ensure smooth sliding. A small gap like 0.25mm would work.
*   **`part_spacing`**: The space between parts when printing them all at the same time; something like 3mm.

The holder should have a cylindrical part that slides onto the binocular tube with the inner diameter equal to my `diameter` and the outer diameter equal to `diameter + 2 * wall_thickness`. It should also have an annulus at one end, with an inner diameter of `diameter - 2 * holder_annulus_width` and the same outer diameter as the cylindrical part.

The ring should also be a thin cylinder with an outer diameter matching the `diameter` and an inner diameter of `diameter - ring_annulus_width * 2`.

The cover should be a cylindrical cap that slides over the holder, with an inner diameter equal to `diameter + 2 * wall_thickness + 2 * cover_gap` and an outer diameter of `cover_inner_diameter + 2 * wall_thickness`.

I want to be able to specify that I only want to generate the code for a single part, so I'd need a `part` variable that can be "holder", "ring", "cover", or "all" to generate one piece or all of the pieces at once.  When "all" is chosen, I want the parts laid out next to each other to be printed at the same time. I need the parts spaced out by the parameter I mentioned above, `part_spacing`. The `cover` should be positioned at `[-spacing * sqrt(3) / 4, spacing / 2, 0]`, the `holder` at `[-spacing * sqrt(3) / 4, -spacing / 2, 0]` and the `ring` at `[spacing * sqrt(3) / 4, 0, 0]`.

I think that's all the info. Can you generate the OpenSCAD file(s)? I'd prefer if the code used standard OpenSCAD primitives; no need to use any libraries.

[Assistant]
solar-filter.scad:
```
part = "all"; // [holder:Holder Only,ring:Ring Only,cover:Cover Only,all:Holder Ring and Cover]

// outside diameter in mm of one binocular tube
diameter=63.5;

// thickness of the walls in mm (for best results, make this a multiple of your nozzle width)
wall_thickness = 1.2;

// width of the ring of material on the holder that prevents the filter from falling through
holder_annulus_width = 10;

//width of the ring that sandwiches the filter from the inside (should be slightly smaller than holder_annulus_width)
ring_annulus_width = 9;

// depth of the filter holder that slides over the binoculars
holder_depth = 10;

// depth of the cover that slides over the holder
cover_depth = 7;

// amount of gap between the cover and the holder
cover_gap = 0.25;

// the distance between parts when printed together
part_spacing = 3;

/* [Hidden] */
cover_inner_diameter = diameter + 2 * wall_thickness + 2 * cover_gap;
cover_outer_diameter = cover_inner_diameter + 2 * wall_thickness;

module cover() {
    union() {
        translate([0, 0, wall_thickness / 2]) {
            cylinder(h = wall_thickness,
                     d = cover_outer_diameter,
                     center = true,
                     $fn=200);
        }
        translate([0 , 0, wall_thickness + cover_depth / 2]) {
            difference() {
                cylinder(h = cover_depth,
                         d = cover_outer_diameter,
                         center = true,
                         $fn = 200);
                cylinder(h = cover_depth + 1,
                         d = cover_inner_diameter,
                         center = true,
                         $fn = 200);
            }
        }
    }
}

module holder() {
    union() {
        translate([0, 0, wall_thickness / 2]) {
            difference() {
                cylinder(h = wall_thickness,
                         d = diameter + 2 * wall_thickness,
                         center = true,
                         $fn = 200);
                cylinder(h = wall_thickness + 1,
                         d = diameter - 2 * holder_annulus_width,
                         center = true,
                         $fn = 200);
            }
        }
        translate([0, 0, holder_depth / 2 + wall_thickness]) {
            difference() {
                cylinder(h = holder_depth,
                         d = diameter + 2 * wall_thickness,
                         center = true,
                         $fn = 200);
                cylinder(h = holder_depth + 1,
                         d = diameter,
                         center = true,
                         $fn = 200);
            }
        }
    }
}

module ring() {
    union() {
         translate([0, 0, wall_thickness / 2]) {
            difference() {
                cylinder(h = wall_thickness,
                         d = diameter,
                         center = true,
                         $fn = 200);
                cylinder(h = wall_thickness + 1,
                         d = diameter - ring_annulus_width * 2,
                         center = true,
                         $fn = 200);
            }
        }
    }
}

// Create one STL file at a time
if (part == "cover") {
    cover();
} else if (part == "holder") {
    holder();
} else if (part == "ring") {
    ring();
} else if (part == "all") {
    spacing = cover_outer_diameter + part_spacing;

    translate([-spacing * sqrt(3) / 4, spacing / 2, 0]) cover();
    translate([-spacing * sqrt(3) / 4, -spacing / 2, 0]) holder();
    translate([spacing * sqrt(3) / 4, 0, 0]) ring();
}

```


[User]
Hey, I'm looking to design a truss structure in OpenSCAD, and I'd like to make it highly parametric. I have some basic ideas and parameters in mind, but I need help fleshing out the code.

Here's what I'm envisioning:

*   **Overall Structure:** The truss should be a rectangular prism shape. I want to control the length and width independently. Also, I'd like the height of the truss members themselves to be a parameter.
*   **Truss Members:**  The truss will consist of a lattice of beams. I want to be able to control the thickness of these beams independently from the overall member height.
*   **Connections:** I need to design this to be easily connected together, so I want holes at the corners of the rectangular truss at the same height as the structure which protrude to allow multiple trusses to connect to each other. I also want the diameter and length of these holes to be configurable. It should make space for a small bolt.
*   **Lattice Pattern:** The pattern inside should be configurable in two ways: I want to be able to switch between having diagonal and horizontal struts. It would be fantastic if I can toggle these on and off, even both at the same time. I also want to make sure all the truss beams have the same thickness and height as a parameter, while the height of the support structure is a separate parameter.
*   **Parametric control:** I'd like to have parameters for the following:
    *   Overall Length (`l`)
    *   Overall Width (`w`)
    *   Height of the truss members (`h`)
    *   Thickness of the truss beam itself (`s`)
    *   Hole radius (`holer`)
    *   Hole Length (`holesl`)
    *   Offset of hole from corner (`holeo`)
    *   Horizontal bars boolean `horizontal`
    *   Diagonal bars boolean `diagonal`
*   **Units:** I'm using millimeters for everything.

I'm not using any specific libraries, just base OpenSCAD functionality. It needs to use a few `for` loops.

Could you generate the OpenSCAD code that implements these features? I'd love to see it broken into modules so it's easy to read and modify later, and some comments would help too. It should have an output for each individual truss section.

[Assistant]
truss.scad:
```
//Length
l=400;
//Width
w=100;
////Length
//l=rnd(20,175);
////Width
//w=rnd(10,20);
n=floor(l/w);

//Wall
wall=5;
//Beam
beam=5.1;
//Horizontal bars
horizontal=1;//[0,1]
//Diagonal bars
diagonal=1;//[0,1]
// Connecting holes radius
holer=6;
// Connecting holes length
holesl=30;
// Connecting holes offset from corner: holer+x
holeo=3;
w2=l/n;
////Wall over width
//wall=rnd(10,50);
////Beam
//beam=rnd(10,50);
//// connecting holes radius
//holer=rnd(0.5,w/15);
//// connecting holes length
//holesl=rnd(1,10);
h=wall;
s=beam;
echo(l,w,n,h,s);
//h=w/rnd(10,40);
//s=h*rnd(0.3,2);

rotate([0,-90])color("lightgray"){
rotate([000,0,0])translate ([0,0,-w*0.5+h*0.5]) t();
 rotate([90,0,0])translate ([0,0,-w*0.5+h*0.5]) t();
 rotate([180,0,0])translate ([0,0,-w*0.5+h*0.5]) t();
 rotate([270,0,0])translate ([0,0,-w*0.5+h*0.5])  t();
 crossbeam();
 holes();
translate([l ,0,0])mirror([1,0,0]) holes();

}
 module crossbeam(){
 rotate([0,90,0])linear_extrude(l )difference(){
square(w-h*2,center=true);
offset(max(0,2*s-h ),$fn=20)offset(-max(0,2*s-h ))square(w-h*2,center=true);}

}

module holes(){
hole=holer+h+holeo;
 rotate([0,90,0])linear_extrude(s+h ){

 difference(){
offset(-max(0,2*s-h ) )offset(max(0,2*s-h ),$fn=20) union(){
square(w ,center=true);
difference(){
union(){
hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}
mirror()hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}
mirror([1,1,0])hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}
mirror([0,1,0])hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}

 }

translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([-(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([-(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer,$fn=20);}}

offset(max(0,2*s-h ),$fn=20)offset(-max(0,2*s-h ))difference(){
square(w-h*2,center=true);
union(){
hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}
mirror()hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}
mirror([1,1,0])hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}
mirror([0,1,0])hull(){
 translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
 translate([(w*0.5  -h),(w*0.5  -h)]) square(h);
}

 }

}
translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([-(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([-(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer,$fn=20);}

}

 rotate([0,90,0])linear_extrude(holesl )difference() {union(){
translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
translate([-(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer+h,$fn=20);
translate([(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer+h,$fn=20);
translate([-(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer+h,$fn=20);

}

union(){
translate([(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([-(w*0.5- hole -h),(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer,$fn=20);
translate([-(w*0.5- hole -h),-(w*0.5- hole -h)]) circle(holer,$fn=20);}
}
}

module t(){

 linear_extrude(h,center=true){truss();}

}

function clamp(  b = 0, c = 1,a) = min(max(abs(a), b), c);
module truss(){

intersection(){
 translate([0,-w/2])square([l,w]);

offset(-s,$fn=20)
offset(s)
for(a=[-w2:w2:l+w2]){

if (horizontal==1)hull() {
translate([clamp(s,l-s,a),abs(a/w2)%(2)*w-w*0.5])circle(s);
translate([clamp(s,l-s,a),abs(b/w2)%(2)*w-w*0.5])circle(s);
}

b=a+w2;
if (diagonal==1)hull() {
translate([clamp(0,l,a),abs(a/w2)%(2)*w-w*0.5])circle(s);
translate([clamp(0,l,b),abs(b/w2)%(2)*w-w*0.5])circle(s);
}
c=b+w2;
hull() {
translate([a,abs(a/w2)%(2)*w-w*0.5])circle(s*2);
translate([c,abs(c/w2)%(2)*w-w*0.5])circle(s*2);
}
}
}}

function rnd(a = 1, b = 0, s = []) =
s == [] ?
(rands(min(a, b), max(   a, b), 1)[0])
:
(rands(min(a, b), max(a, b), 1, s)[0])
;
```


[User]
Hi there, I'm looking to generate a parametric OpenSCAD file for a flexible XT60 connector cap. I have some measurements already that I'd like to parameterize.

Here's what I'm thinking: The cap is basically a rectangular box with some internal cutouts to fit the XT60 connector. I need to control the overall width of the XT60 connector itself, as well as the length of a central section and also the total length. Also needed is the width of the "top" cutout, as well as inner and outer heights. The cap should also have a bottom section or skirt and a wall thickness, each controllable. Finally, there's a guide "tab" which has an offset, length, and width that I would like to be parametric as well.

Here are the specific dimensions as I have them now, but I'd like to make these variables:

*   `width = 6.5;` (width of the internal XT60 connector part)
*   `length1 = 12;` (length of the main internal cavity)
*   `lengthTotal = 14.4;` (total length of the cap)
*   `widthTop = 2;` (width of the top cut out)

*   `innerHeight = 9.5;` (height of the main internal cavity)
*  `innerHeight1 = 8.3;` (height of another inner cavity)

*   `bottom = 1.2;` (height of the bottom "skirt" of the cap)
*   `skirt = 1.2;` (additional skirt width on the sides)
*   `walls = 1.2;` (thickness of the walls)

*   `xOffsetGuide = 6.25;` (offset of the guide from the side)
*   `guideLength = 1.2;` (length of the guide)
*   `guideWidth = 1.5;` (width of the guide)

The cap should have a few cutouts. It has a main rectangular cavity, a second shorter cavity that is a little taller and on top of that is a final cutout that is rectangular but only .1mm thick to make the thin portion at the top of the cap. There should also be a cutout for the guide on the side. The guide itself will be a solid tab sticking out of the cavity.

I think that's all of the dimensions. I don't need any libraries, I think the standard OpenSCAD primitives should be fine. Thanks in advance!

[Assistant]
xt60_cap.scad:
```
width = 6.5;
length1 = 12;
lengthTotal = 14.4;
widthTop = 2;

innerHeight = 9.5;
innerHeight1 = 8.3;

bottom = 1.2;
skirt = 1.2;
walls = 1.2;

xOffsetGuide = 6.25;
guideLength = 1.2;
guideWidth = 1.5;

/* [Hidden] */
outerHeight = bottom + innerHeight;
outerWidth = width + walls * 2;
ratio = outerWidth / width;

outerWidthTop = widthTop * ratio;

outerLength1 = length1 + walls;

outerBottomLength1 = outerLength1 + skirt;
outerBottomWidth = outerWidth + skirt * 2;
outerBottomWidthTop = outerWidthTop * ratio;

hull() {
    translate([-skirt, -skirt, 0])
        cube([outerBottomWidth, outerBottomLength1, bottom]);

    translate([(outerWidth - outerBottomWidthTop) / 2, lengthTotal + walls * 2 + skirt - 0.1, 0])
        cube([outerBottomWidthTop, 0.1, bottom]);
}

translate([0, 0, bottom]) {
    difference() {
        hull() {
            cube([outerWidth, outerLength1, innerHeight]);

            translate([(outerWidth - outerWidthTop) / 2, lengthTotal + walls * 2 - 0.1, 0])
                cube([outerWidthTop, 0.1, innerHeight]);
        }

        translate([walls, walls, bottom + innerHeight - innerHeight1]) {
            hull() {
                cube([width, length1, innerHeight]);

                translate([(width - widthTop) / 2, lengthTotal - 0.1, 0])
                    cube([widthTop, 0.1, innerHeight]);
            }
        }

        translate([walls, walls + xOffsetGuide - guideWidth / 2, 0])
            cube([width -walls, guideWidth * 2, innerHeight]);
    }

    translate([walls + width - guideLength, walls + xOffsetGuide, 0]) {
        cube([guideLength, guideWidth, innerHeight]);
    }
}
```


[User]
I need to generate some parametric mounting clips for a sign I'm making. I'm using a CNC to cut a wooden sign that will be attached to the side of a mailbox post, and I think these clips would work great.

I've been working with OpenSCAD, and I'd like to have three main types of clips:

1.  **Bottom/Top Clips:** These would essentially be 'C' shaped brackets that slide over the wood, with a screw hole for mounting.
2.  **Corner Clips:** Similar to the bottom/top clips, but they would wrap around the corner of two pieces of wood.
3.  **Side Clips:** These would be longer brackets designed to hold two pieces of wood together side-by-side. These can have one or two screw holes.

I need to be able to control a few things with parameters:

*   **Wood Thickness (Wd\_Thk):** This will be the thickness of the wood I'm mounting to. I'd like to set this in millimeters, and have the brackets fit loosely. I'm starting with 20mm as my default, but I'd like to be able to change this for different lumber.
*   **Plastic Thickness (Thk):** This is the thickness of the plastic I'm printing for the clip itself, in mm.
*   **Screw Hole Extension Length (E\_Len):** This is how far the mounting section of the clip extends out from the main part in mm.
*   **Screw Hole Diameter (Hole\_OD):** The diameter of the screw hole.
*   **Screw Head Diameter (Head\_OD):** The diameter of the screw head, which would be used to cut a recess in the weld area to allow the screw to sit flush.
*   **Weld Diameter (Weld\_OD):** The diameter of the fillet weld area where the clip connects to the mounting portion.

For the **Bottom/Top Clips** specifically:

*   **Height (BT\_Ht):** The height of the "C" bracket in mm.
*   **Length Over Wood (BT\_Len):** How far the clip extends along the wood in mm.
*   **Screw Hole Y Offset (BT\_Hole\_Y\_Offset):** How far from the edge of the clip the screw hole is, in mm.

For the **Corner Clips**:

*   **Base Thickness (Base\_Thk):** The thickness of a base that adds support to the clip, in mm.
*   **Height (C\_Ht):** The height of the corner bracket in mm.
*  **Length Over Wood (C_Len):** How far the clip extends along the wood in mm.
*   **Screw Hole Y Offset (C\_Hole\_Y\_Offset):** How far from the edge of the clip the screw hole is, in mm.
*   **Wood Drill Hole (Wd\_Hole\_ON):** An option to enable or disable a hole in the wood facing part of the clip.

For the **Side Clips**:

*   **Height (S\_Ht):** The height of the side bracket in mm.
*   **Length Over Wood (S\_Len):** How far the clip extends along the wood in mm.
*   **Screw Hole Y Offset (S\_Hole\_Y\_Offset):** How far from the edge of the clip the screw hole(s) are, in mm.
*   **Hole Quantity (Hole\_Qty):** This is either 1 or 2 holes.
*   **Hole Spacing (Hole\_Spacing):** If two holes are specified, the spacing between them.

I'd also like to add a **Middle Clip**, a double-sided C bracket, for holding two pieces of wood together side-by-side.

* **Height (M\_Ht):** The height of the C bracket.
* **Length Over Wood (M\_Len):** How far the clip extends along the wood.
* **Screw Hole Y Offset (M\_Hole\_Y\_Offset):** How far from the edge of the clip the screw hole is.

Finally, I want to add a chamfer edge on the exterior part of the brackets.

*   **Edge Chamfer Radius (E\_Chamf\_Rad):** The radius of the edge chamfer, which should show only 1mm of chamfer when at 3.

I think that covers all of it. Can you generate OpenSCAD files for this? I'm not using any special libraries, so I think everything can be done with base SCAD functionality.

[Assistant]
SignClips_Rev28.scad:
```
//Plastic Clips to hold a Sign in Place
//7/4/2017  By: David Bunch
//
//3 Types of clips, Bottom & Top, Corner and Side Clips to hole 2 separate signs together
//
/* [Main Variables] */
//1. Type of Clip
Clip_Type = 0;          //[0:BotTopClip, 1:CornerClip, 2:Side Clip, 3:Middle Clip, 4:All 3 Clips]

//2. Thickness of Wood
Wd_Thk = 20;            //[6:.5:40]

//3: Thickness of Plastic parts
Thk = 4;                //[1:.2:6]

//4. Len where screw connects to Mount
E_Len = 12;             //[12:1:24]

//5. Diameter of Holes
Hole_OD = 4.5;          //[3:.5:6]

//6. Diameter of Screw head, used to Cut out some of Weld
Head_OD = 10;           //[6:.5:12]

//7. Diameter of Fillet Weld to use
Weld_OD = 10;            //[4:.5:10]

/* [Bottom & Top Clip Variables] */

//8. Height of Bottom & Top Clips
BT_Ht = 7;            //[8:1:38]

//9. Length of Bottom & Top Clip over Wood
BT_Len = 20;             //[12:1:30]

//10. Hole Offset for Bottom & Top Clips
BT_Hole_Y_Offset = 6.5;    //[6:.5:16]

/* [Corner Clip Variables] */

//11. Base Thickness
Base_Thk = 3;           //[0:.5:8]

//12. Height of Corner Clips
C_Ht = 12;             //[8:1:38]

//13. Length of Corner Clip over Wood
C_Len = 20;             //[12:1:30]

//14. Hole Offset for Corner Clips
C_Hole_Y_Offset = 8;    //[6:.5:16]

//15. Drill Hole where connects to Sign
Wd_Hole_ON = 1;         //[0:OFF, 1:ON]

/* [Side Clip Variables] */

//16. Height of Side Clips
S_Ht = 25;             //[8:1:38]

//17. Length of Side Clip over Wood
S_Len = 19.5;             //[12:1:30]

//18. Hole Offset Side Clips
S_Hole_Y_Offset = 8;     //[6:.5:16]

//19. 1 or 2 holes on Side Clip
Hole_Qty = 2;           //[1:2]

//20. Hole Spacing for 2 holes on Side clip
Hole_Spacing = 19;      //[8:1:24]

/* [Middle Clip Variables] */

//21. Height of Bottom & Top Clips
M_Ht = 12;            //[7:1:38]

//22. Length of Bottom & Top Clip over Wood
M_Len = 20;             //[12:1:30]

//23. Hole Offset for Bottom & Top Clips
M_Hole_Y_Offset = 6;    //[6:.5:16]

//24. Edge Chamfer (6 should show only 1mm)
E_Chamf_Rad = 3;        //[0:.5:8]

/* [Hidden] */

//These Variables are for Visualizing Assembly of Parts
Draw_Type = 0;          //0:Draw Printable Part, 1:Draw Assembly
Bot_Len = 400.05;       //15.75" Length of Bottom Wood Sign
Top_Len = 577.85;       //22.75" Length of Top Wood Sign
Wid = 139.7;            //5.5" Width of Wood Signs
Cut_X_offset = 25.4;
Cut_Y_offset = 60;
Cut_Z_Depth = 2;

E_Chamf = E_Chamf_Rad * 2;
Cut_Wid = Wid - (Cut_X_offset * 2);
Cut_BotLen = Bot_Len - (Cut_Y_offset * 2);
Cut_TopLen = Top_Len - (Cut_Y_offset * 2);

E_Len2 = E_Len / 2;
Tot_Thk = Wd_Thk + Thk + Thk;   //Total Thickness of Plastic Clip where it slides over Wood
BT_Tot_Ht = BT_Ht + (E_Chamf * 1);
BT_Ht2 =  BT_Tot_Ht / 2;
BT_Tot_Len = E_Len + Thk + BT_Len;        //Total Width of Bottom or Top Plastic Clip
BT_Chamf_Res = (round(((BT_Tot_Ht  * 3.14) / 4) / 1) *  4);

C_Ht2 =  (C_Ht + Base_Thk) / 2;
C_Tot_Len = E_Len + Thk + C_Len;        //Total Length of Plastic Clip
C_Tot_Ht = C_Ht + (E_Chamf * 1);
C_Chamf_Res = (round(((C_Tot_Ht  * 3.14) / 4) / 1) *  4);
C_Chamf_OD = C_Tot_Ht + Base_Thk;
C_Chamf_Rad = C_Chamf_OD / 2;

S_Ht2 =  S_Ht / 2;
Hole_Space2 = Hole_Spacing / 2;
echo(E_Len = E_Len);
echo(S_Len = S_Len);
echo(Thk = Thk);
S_Tot_Len = E_Len + Thk + S_Len;        //Total Length of Plastic Clip
S_Tot_Ht = S_Ht + (E_Chamf * 1);
S_Chamf_Res = (round(((12  * 3.14) / 4) / 1) *  4);

M_Tot_Len = E_Len + Thk + BT_Len;        //Total Width of Bottom or Top Plastic Clip
M_Tot_Ht = M_Ht + (E_Chamf * 1);
M_Ht2 =  M_Tot_Ht / 2;
M_Chamf_Res = (round(((M_Tot_Ht  * 3.14) / 4) / 1) *  4);

echo(Tot_Thk = Tot_Thk);
echo(BT_Chamf_Res = BT_Chamf_Res);
echo(BT_Tot_Len = BT_Tot_Len);
echo(S_Chamf_Res = S_Chamf_Res);
echo(S_Tot_Len = S_Tot_Len);
echo(C_Tot_Ht = C_Tot_Ht);
echo(S_Tot_Ht = S_Tot_Ht);

module ChamEdge(CH_Tot_Ht = BT_Tot_Ht, CH_Chamf_Res = BT_Chamf_Res)
{
    rotate([0,90,0])
    translate([0,0,-Thk-1])
    difference()
    {
        translate([-CH_Tot_Ht,0,0])
        cube([CH_Tot_Ht * 2,CH_Tot_Ht,Tot_Thk + 2]);
        translate([0,0,-1])
        cylinder(d=CH_Tot_Ht,h=Tot_Thk + 4,$fn=CH_Chamf_Res);
    }
}
module ChamFlat(CH_Tot_Len = BT_Tot_Len, CH_Ht2 = BT_Ht2,
                CH_Tot_Ht = BT_Tot_Ht, CH_Chamf_Res = BT_Chamf_Res)
{
    translate([-Thk+E_Chamf_Rad,BT_Tot_Len-BT_Ht2,BT_Ht2])
    rotate([0,-90,0])
    difference()
    {
        cylinder(d=CH_Tot_Ht+2, h=E_Chamf_Rad+.01, $fn=CH_Chamf_Res);
        translate([0,0,-.002])
        cylinder(d1=CH_Tot_Ht, d2=CH_Tot_Ht - (E_Chamf_Rad*2), h=E_Chamf_Rad+.004, $fn=CH_Chamf_Res);
        translate([-CH_Tot_Ht, -CH_Tot_Ht,-1])
        cube([CH_Tot_Ht*2, CH_Tot_Ht, E_Chamf_Rad+2]);
    }
}
//echo("BT_Tot_Ht - (E_Chamf_Rad*2) = ",BT_Tot_Ht - (E_Chamf_Rad*2));
//%ChamFlat(BT_Tot_Len, BT_Ht2, BT_Tot_Ht, BT_Chamf_Res);

module ChamFlatSide()
{
    for (m = [0,1])
    {
        translate([0,0,m * S_Tot_Ht])
        mirror([0,0,m])
        {
            translate([-Thk+E_Chamf_Rad,S_Tot_Len-6,S_Tot_Ht-6])
            rotate([0,-90,0])
            difference()
            {
                cylinder(d=14,h=E_Chamf_Rad+.01, $fn=S_Chamf_Res);
                translate([0,0,-.002])
                cylinder(d1=12, d2=12 - (E_Chamf_Rad*2), h=E_Chamf_Rad+.02, $fn=S_Chamf_Res);
                translate([-8, -16,-1])
                cube([16, 16, E_Chamf_Rad+2]);
                translate([-16, -1,-1])
                cube([16, 16, E_Chamf_Rad+2]);
            }
            translate([-Thk,0,0])
            rotate([-90,0,0])
            cylinder(d=E_Chamf, h=S_Tot_Len,$fn = 4);
        }
    }
    translate([-Thk,S_Tot_Len,5])
    cylinder(d=E_Chamf, h=S_Tot_Ht,$fn = 4);
}
module Corner_ChamEdge()
{
    rotate([0,90,0])
    difference()
    {
        translate([-C_Tot_Ht/2-2,0,0])
        cube([C_Tot_Ht+4,C_Chamf_OD,Tot_Thk + 2]);
        translate([0,0,-1])
        cylinder(d=C_Chamf_OD,h=Tot_Thk + 4,$fn=C_Chamf_Res);
    }
}
module ChamSideClip()
{
    translate([0,0,-Thk-1])
    for (m = [0,1])
    {
        translate([0,0,m * (S_Tot_Ht+10)])
        mirror([0,0,m])
        {
            translate([-Thk+E_Chamf_Rad,S_Tot_Len-6,S_Tot_Ht-1])
            rotate([0,-90,0])
            translate([0,0,-Tot_Thk+2])
            difference()
            {
                cylinder(d=24,h=Tot_Thk+2, $fn=S_Chamf_Res);
                translate([0,0,-1])
                cylinder(d=12,h=Tot_Thk+4, $fn=S_Chamf_Res);
                translate([-13, -16,-1])
                cube([26, 16, Tot_Thk+4]);
                translate([-16, -1,-1])
                cube([16, 16, Tot_Thk+4]);
            }
        }
    }
}

module ChamMid()
{
    rotate([0,90,0])
    translate([0,0,-Thk-1])
    difference()
    {
        translate([-M_Ht,0,0])
        cube([M_Ht * 2,M_Ht,Tot_Thk + 2]);
        translate([0,0,-1])
        cylinder(d=M_Ht,h=Tot_Thk + 4,$fn=M_Chamf_Res);
    }
}
module WeldCorner(Ht = 12, Add_Thk = Base_Thk)
{
    difference()
    {
        cylinder(d=Weld_OD,h=Ht + Add_Thk,$fn=4);
        translate([-Weld_OD,0,-1])
        cube([Weld_OD * 2,Weld_OD,Ht + Add_Thk+2]);
        translate([0,-Weld_OD,-1])
        cube([Weld_OD,Weld_OD * 2,Ht + Add_Thk+2]);
    }
}
module Base_Cubes(BC_Tot_Ht = BT_Tot_Ht, BC_Len = BT_Len)
{
    translate([-Thk,E_Len,0])
    cube([Tot_Thk,Thk,BC_Tot_Ht]);
    translate([-Thk,E_Len + Thk,0])
    cube([Thk,BC_Len,BC_Tot_Ht]);
    translate([Wd_Thk,0,0])
    cube([Thk,E_Len + Thk + BC_Len,BC_Tot_Ht]);
}
module CornerClip()
{
    difference()
    {
        union()
        {
            Base_Cubes(C_Chamf_OD, C_Len);
            translate([Wd_Thk,E_Len,0])
            WeldCorner(C_Tot_Ht,Base_Thk);
            if (Base_Thk > 0)
            {
                translate([-Thk,E_Len,0])
                cube([Tot_Thk,Thk + C_Len - C_Chamf_Rad,Base_Thk]);
            }
        }
        difference()
        {
            translate([-Thk-1,C_Tot_Len-C_Chamf_Rad,C_Ht2+3])
            Corner_ChamEdge();
//            translate([0,E_Len,-1])
//            cube([Wd_Thk,Thk + C_Len,C_Ht + Base_Thk + 2]);  //In case we have a Base
        }
        if (Base_Thk > 0)
        {
            translate([0,E_Len + Thk + C_Len - C_Chamf_Rad,Base_Thk])
            rotate([0,90,0])
            cylinder(d=Base_Thk,h=Wd_Thk,$fn=4);
        }
        translate([Tot_Thk,C_Chamf_Rad,C_Ht2+3])
        rotate([0,0,180])
        Corner_ChamEdge();
        translate([0,E_Len / 2,C_Ht2 + Base_Thk])
        rotate([0,90,0])
        cylinder(d=Hole_OD,h=30,$fn=24);
        translate([0,E_Len / 2,C_Ht2 + Base_Thk])
        rotate([0,90,0])
        cylinder(d=Head_OD,h=Wd_Thk,$fn=32);

        if (Wd_Hole_ON == 1)
        {
            translate([0,C_Tot_Len - C_Hole_Y_Offset,C_Ht2 + Base_Thk+E_Chamf/2])
            rotate([0,90,0])
            cylinder(d=Hole_OD,h=10,center=true,$fn=24);
        }
        if (E_Chamf > 0)
        {
//Chamfer clip to edge
        for (z = [0,C_Tot_Ht+Base_Thk])
        {
            translate([-Thk,0,z])
            rotate([-90,0,0])
            cylinder(d=E_Chamf,h=C_Tot_Len,$fn=4);
        }
            translate([0,-3,0])
            ChamFlat(C_Tot_Len, C_Ht2, C_Tot_Ht+Base_Thk, C_Chamf_Res);
        }
    }
}

module BotTopClip()
{
    difference()
    {
        union()
        {
            Base_Cubes(BT_Tot_Ht, BT_Len);
            translate([Wd_Thk,E_Len,0])
            WeldCorner(BT_Tot_Ht,0);
        }
        translate([0,BT_Tot_Len - BT_Ht2,BT_Ht2])
        ChamEdge(BT_Tot_Ht,BT_Chamf_Res);
        translate([0,BT_Ht2,BT_Ht2])
        mirror([0,1,0])
        ChamEdge(BT_Tot_Ht,BT_Chamf_Res);
        translate([0,E_Len / 2,BT_Ht2])
        rotate([0,90,0])
        cylinder(d=Hole_OD,h=30,$fn=24);
        translate([0,E_Len / 2,BT_Ht2])
        rotate([0,90,0])
        cylinder(d=Head_OD,h=Wd_Thk,$fn=32);

        translate([0,BT_Tot_Len - BT_Hole_Y_Offset,BT_Ht2])
        rotate([0,90,0])
        cylinder(d=Hole_OD,h=10,center=true,$fn=24);
//Chamfer clip to edge
        for (z = [0,BT_Tot_Ht])
        {
            translate([-Thk,0,z])
            rotate([-90,0,0])
            cylinder(d=E_Chamf,h=BT_Tot_Len,$fn=4);
        }
        if (E_Chamf > 0)
        {
            ChamFlat(BT_Tot_Len, BT_Ht2, BT_Tot_Ht, BT_Chamf_Res);
        }
    }
}
module Mid_Clip()
{
    difference()
    {
        for (y = [0,1])
        {
            translate([0,y*12,0])
            mirror([0,y,0])
            union()
            {
                Base_Cubes(M_Tot_Ht, M_Len);
                translate([Wd_Thk,E_Len,0])
                WeldCorner(M_Tot_Ht,0);
            }
        }
        for (y = [0,1])
        {
            translate([0,y*12,0])
            mirror([0,y,0])
            {
                translate([0,M_Tot_Len - M_Ht2,M_Ht2])
                ChamEdge(M_Tot_Ht,M_Chamf_Res);
                translate([0,M_Tot_Len - M_Hole_Y_Offset,M_Ht2])
                rotate([0,90,0])
                cylinder(d=Hole_OD,h=10,center=true,$fn=24);
            }
        }
        translate([0,E_Len / 2,M_Ht2])
        rotate([0,90,0])
        cylinder(d=Hole_OD,h=30,$fn=24);
        translate([0,E_Len / 2,M_Ht2])
        rotate([0,90,0])
        cylinder(d=Head_OD,h=Wd_Thk,$fn=32);
        if (E_Chamf > 0)
        {
            for (m = [0,1])
            {
                translate([0,m*E_Len,0])
                mirror([0,m,0])
                translate([-Thk + E_Chamf_Rad,M_Tot_Len-M_Ht2,M_Ht2])
                rotate([0,-90,0])
                difference()
                {
                    cylinder(d=M_Tot_Ht+2, h=E_Chamf_Rad+.01, $fn=M_Chamf_Res);
                    translate([0,0,-.002])
                    cylinder(d1=M_Tot_Ht, d2=M_Tot_Ht - (E_Chamf_Rad*2), h=E_Chamf_Rad+.004, $fn=M_Chamf_Res);
                    translate([-M_Tot_Ht, -M_Tot_Ht,-1])
                    cube([M_Tot_Ht*2, M_Tot_Ht, E_Chamf_Rad+2]);
                }
            }
            for (z = [0,M_Tot_Ht])
            {
                translate([-Thk,0,z])
                rotate([-90,0,0])
                cylinder(d=E_Chamf,h=M_Tot_Len*2,center=true,$fn=4);
            }
        }
    }
}
module SideClip()
{
    difference()
    {
        union()
        {
            Base_Cubes(S_Tot_Ht, S_Len);
            translate([Wd_Thk,E_Len,0])
            WeldCorner(S_Tot_Ht,0);
        }
        ChamSideClip();
        translate([0,S_Tot_Len,0])
        mirror([0,1,0])
        ChamSideClip();
        translate([0,E_Len / 2,S_Tot_Ht/2])
        rotate([0,90,0])
        cylinder(d=Hole_OD,h=30,$fn=24);
        translate([0,E_Len / 2,S_Tot_Ht/2])
        rotate([0,90,0])
        cylinder(d=Head_OD,h=Wd_Thk,$fn=32);

        if (Hole_Qty == 1)
        {
            translate([0,S_Tot_Len-S_Hole_Y_Offset,S_Tot_Ht])
            rotate([0,90,0])
            cylinder(d=Hole_OD,h=10,center=true,$fn=24);
        } else
        {
            translate([0,S_Tot_Len-S_Hole_Y_Offset,(S_Tot_Ht / 2) - Hole_Space2])
            rotate([0,90,0])
            cylinder(d=Hole_OD,h=10,center=true,$fn=24);
            translate([0,S_Tot_Len-S_Hole_Y_Offset,(S_Tot_Ht / 2) + Hole_Space2])
            rotate([0,90,0])
            cylinder(d=Hole_OD,h=10,center=true,$fn=24);
        }
        if (E_Chamf > 0)
        {
            ChamFlatSide();
        }
    }
}
module BotTopClip_x2()
{
    BotTopClip();
    translate([(Tot_Thk / 2) - Thk,E_Len / 2,0])
    rotate([0,0,180])
    BotTopClip();
}
module CornerClip_x2()
{
    CornerClip();
    translate([0,Tot_Thk - Thk,0])
    rotate([0,0,90])
    mirror([1,0,0])
    CornerClip();
}
module SideClip_x2()
{
    SideClip();
    translate([(Tot_Thk / 2) - Thk,E_Len / 2,0])
    rotate([0,0,180])
    SideClip();
}
module MidClip_x2()
{
    Mid_Clip();
    translate([Tot_Thk + 6,0,0])
    Mid_Clip();
}
module Assembly()
{
    difference()
    {
        cube([Wid,Bot_Len,Wd_Thk]);
        translate([Cut_X_offset,Cut_Y_offset,Wd_Thk - Cut_Z_Depth])
        cube([Cut_Wid,Cut_BotLen,Thk]);
    }
    translate([0,Bot_Len,0])
    difference()
    {
        cube([Wid,Top_Len,Wd_Thk]);
        translate([Cut_X_offset,Cut_Y_offset,Wd_Thk - Cut_Z_Depth])
        cube([Cut_Wid,Cut_TopLen,Thk]);
        translate([-1,0,Wd_Thk])
        rotate([0,90,0])
        cylinder(d=2,h=Wid+2,$fn=4);        //Add Crease to see separate Woods
    }

    translate([-12-Thk,15+Bot_Len,Wd_Thk])
    rotate([0,0,-90])
    rotate([0,90,0])
    color("red",.5)
    %SideClip();
    translate([12+Thk+Wid,Bot_Len-15,Wd_Thk])
    rotate([0,0,90])
    rotate([0,90,0])
    color("red",.5)
    %SideClip();
    if (Clip_Type == 1)
    {
        translate([-Thk,-E_Len-Thk,Wd_Thk])
        rotate([0,90,0])
        color("red",.5)
        %CornerClip();
        translate([Wid+Thk,-E_Len-Thk,Wd_Thk])
        mirror([1,0,0])
        rotate([0,90,0])
        color("red",.5)
        %CornerClip();
        translate([0,Bot_Len+Top_Len,0])
        mirror([0,1,0])
        {
            translate([-Thk,-E_Len-Thk,Wd_Thk])
            rotate([0,90,0])
            color("red",.5)
            %CornerClip();
            translate([Wid+Thk,-E_Len-Thk,Wd_Thk])
            mirror([1,0,0])
            rotate([0,90,0])
            color("red",.5)
            %CornerClip();
        }
    } else
    {
        translate([Wid/4,-E_Len - Thk,Wd_Thk])
        rotate([0,90,0])
        color("red",.5)
        %BotTopClip();
        translate([Wid * .75,-E_Len - Thk,Wd_Thk])
        rotate([0,90,0])
        color("red",.5)
        %BotTopClip();
        translate([0,Bot_Len+Top_Len,0])
        mirror([0,1,0])
        {
            translate([Wid/4,-E_Len - Thk,Wd_Thk])
            rotate([0,90,0])
            color("red",.5)
            %BotTopClip();
            translate([Wid * .75,-E_Len - Thk,Wd_Thk])
            rotate([0,90,0])
            color("red",.5)
            %BotTopClip();
        }
    }
}

if (Draw_Type == 1)
{
    Assembly();
} else
{
    if (Clip_Type == 0)
    {
        BotTopClip_x2();
    } else if (Clip_Type == 1)
    {
        CornerClip_x2();
    } else if (Clip_Type == 2)
    {
        SideClip_x2();
    } else if (Clip_Type == 3)
    {
        MidClip_x2();
    }  else if (Clip_Type == 4)
    {
        BotTopClip_x2();
        translate([-E_Len-Thk-Thk,E_Len,0])
        rotate([0,0,90])
        CornerClip_x2();
        translate([Tot_Thk+14,0,0])
        SideClip_x2();
        translate([0,6,0])
        for (m = [0,1])
        {
            mirror([0,m,0])
            translate([0,Tot_Thk+12,0])
            rotate([0,0,90])
            Mid_Clip();
        }
    }
}

```


[User]
Hi, I'm looking to create a customizable name tag using OpenSCAD. I'd like it to be parametric, so I can easily adjust the dimensions and text.

Here are the key features I need:

*   **Basic Shape:** A rounded rectangular tag with a hole for attaching it to things.
*   **Dimensions:** I'd like to control the width, depth, and height of the tag using variables. I need to be able to set the hole diameter and corner radius as well.
*   **Text:** The tag should display text. I need the ability to control whether the text is embossed (raised) or engraved (cut into the tag). The number of text lines, font size, vertical offset, spacing between lines, and text height should be adjustable.
*   **Text Customization:** For each line of text, I'd like to control:
    *   The actual text string
    *   The font (I am fine if you just include Tahoma and Arial, but if there are more, that's okay)
    *   A horizontal offset

I'd also want error checking so that the corner radius cannot be greater than or equal to half of the tag depth.

I'd prefer a single SCAD file to define this, if possible. I'm not looking to use any external libraries for this particular project. It would be awesome if the rendering was quick too. Could you generate the necessary OpenSCAD code?

[Assistant]
Name_Tag_v2.scad:
```
/* [Hidden] */
// Value to keep display manifold when using difference()
m = .015;
// Resolution
$fn = 100;

/* [Nametag] */
// X Axis
tagWidth = 45; // [10:100]
// Y Axis
tagDepth = 15; // [10:60]
// Z Axis
tagHeight = 2; // [1:10]
// Hole Diameter
holeD = 5; // [1:10]
// Corner Radius
cornerRadius = 4; // [0:10]

/* [Text] */
// Emboss or Engrave
textType = "Emboss"; // [Emboss,Engrave]
// Number of lines to display
lines = 1;
// Font Size
textS = 5;
// Y Axis Offset
textYOffset = 10; // [1:60]
// Y Axis Spacing
textYSpacing = 10; // [1:20]
// Z Axis
textHeight = 1; // [1:20]

/* [Text - Line 1] */
// Text
line1T = "First Name";
// Font
line1FN = "Tahoma"; // [Tahoma,Arial]
// X Offset
line1XO = 0;

/* [Text - Line 2] */
// Text
line2T = "";
// Font
line2FN = "Tahoma"; // [Tahoma,Arial]
// X Offset
line2XO = 0;

/* [Text - Line 3] */
// Text
line3T = "";
// Font
line3FN = "Tahoma"; // [Tahoma,Arial]
// X Offset
line3XO = 0;

/* [Text - Line 4] */
// Text
line4T = "";
// Font
line4FN = "Tahoma"; // [Tahoma,Arial]
// X Offset
line4XO = 0;

/* [Text - Line 5] */
// Text
line5T = "";
// Font
line5FN = "Tahoma"; // [Tahoma,Arial]
// X Offset
line5XO = 0;

// Leftmost X Coordinate
xStart = (tagWidth / 2) - (holeD * 2);

// Error Checking
// Corner Radius can not be >= (tagDepth / 2)
cornerR = cornerRadius >= tagDepth / 2 ? (tagDepth / 2) - 1 : cornerRadius;

// textList
// Load the array to be used to display the text.
// - Text is the text to be diplayed for the current line
// - Font Name is the name of the font for the current line
// - X Offset for the text on the current line
textList = [
["Text", "Font Name", "X Offset"],
[line1T, line1FN, line1XO],
[line2T, line2FN, line2XO],
[line3T, line3FN, line3XO],
[line4T, line4FN, line4XO],
[line5T, line5FN, line5XO],
];

// If "Emboss" place the name object on top of the tag
if(textType=="Emboss")
{
nameTag();
translate([0,0,tagHeight])
color("Teal")
textObject();
}
// If not "Emboss" subtract the name object from the tag
else
{
difference()
{
nameTag();
translate([0,0,tagHeight-(textHeight+(m/2))])
textObject();
} // End difference()
} // End if()

// nameTag()
// Create a raised rounded rectangle with a hole in it.
// 1. Draw a polygon compensating for the width and height the minkowski() function will add:
// (tagWidth - (cornerR 2))
// x (tagDepth - (cornerR 2))
// x tagHeight
// 2. minkowski() the polygon with a flattened sphere:
// XY Plane Radius = cornerR
// Z Radius = .01 (Flat for all intents and purposes)
// 3. difference a cylinder from the resulting construct
// Offset = holeD / 2
// Height = tagHeight + m
// Diameter = HoleD
// 4. raise the whole construct to sit on the XY plane
//
// Minkowski() adds the dimensions of two 3D objects. It is useful on simple shapes but is very processor intensive when rendering.
module nameTag()
{
// Compute the tag width and depth (see item 1)
tagW = tagWidth - (cornerR/2);
tagD = tagDepth - (cornerR/2);

// Compute the offset for the hole from the end of the tag. (see item 3)
holeXOffset = (tagWidth / 2) - holeD;

translate([0,0,tagHeight/2]) // 4
    difference() // 3
    {
        minkowski() // 2
        {
            cube([tagW,tagD,tagHeight],center=true); // 1
            cylinder(r=cornerR,h=.01,center=true);
        } // End minkowski()
        translate([-holeXOffset,0,0])
            cylinder(d=holeD,h=tagHeight+m,center=true);
    } // End difference()
} // End nameTag()

// textObject()
// Generate the text object by stepping through the textList array
// 1. For each line compute or assign the following variables:
// lineT = the text to be displayed for current line
// fontN = the font name for the text on the current line
// xOffset = calculate the X Offset for the text by adding the user defined X Offset for the current line to the leftmost X Coordinate
// yOffset = calculate the Y Offset for the text by multiplying the current line number by the space between lines and adding the product to the Text Y Offset defined by the user for the current line.
// 2. Then position and write the text
module textObject()
{
linear_extrude(textHeight+m)
for(i=[1:lines])
{
// 1.
lineT = textList[i][0];
fontN = textList[i][1];
xOffset = textList[i][2] - xStart;
yOffset = textYOffset - (i * textYSpacing);

        // 2.
        translate([xOffset,yOffset,0])
            text(lineT,size=textS,font=fontN,halign="left",valign="center");
    } // End for(i)
} // End textObject()
```


[User]
I'd like to design a configurable angle bracket for joining aluminum extrusions, and I need it to be parametric. It should let me specify the lengths of the two arms (X and Y), the overall width of the bracket, the thickness of the material, the angle between the arms, and the size of the screw holes for attaching the bracket. I'd also like to have some webbing between the arms to add strength. The amount of webbing should be configurable both in thickness and in how far it extends along the arms.

For the screw holes, I'd like the spacing and placement to be configurable, and I also want them to be countersunk, not just cylinders. I also want to be able to specify which screws I'm using for those countersunk holes (for example, an M4 screw). It would be really nice if the design automatically handled notching the opposite arm for tool access when the angle is acute. I'm envisioning something that has multiple screw locations, perhaps with even spacing, so I can choose where I'd like to screw it to. The design should also account for the screw head when calculating if the part is properly notch for tool access.

I think the overall design could be more modular if the screw hole design and webbing design were in their own module; that way, if I want to use this for a different situation, it might be easier to copy and paste the code.

I'd like the AI to generate the scad files for me. I'm looking for a standalone file without needing any external libraries. I think the best way to do this would be to create a module for creating the main part and one for each feature.

[Assistant]
Customizable_Angle_Bracket.scad:
```
//preview[view:east,tilt:top]

/*Customizer Variables*/

// The length of the X arm in mm
x_length = 20; //[20,40,60,80]

// The length of the Y arm in mm
y_length = 20; //[20,40,60,80]

// The width of the part
width = 20; //[20,40,60,80]

// The thickness of the arms in mm
z_length = 3;

// The angle of the bracket
angle = 90.0; //[0:1:180]

// The thickness of the webbing as a fraction of the z_height
web_thickness = 0.5; // [0.05:0.05:1]

// The fraction of the x or y length the webbing extends to.
web_length = 0.75; // [0.05:0.05:1]

// Screw size in mm
screw_size = 4;

hole_size = screw_size + screw_size/10 + 0.4;

print_part();

module print_part() {

  {translate([-width/2, -y_length/2,0])
    difference ()
    {
      union ()
      {
        // extrude webbing
        {translate([z_length * web_thickness,0,0])
          rotate([0,-90,0])
            linear_extrude(height = z_length * web_thickness, center = false)
             polygon([[z_length, (z_length / sin(angle)) + (z_length / tan(angle))],
                [z_length, (y_length - 0) * web_length],
                [sin(angle) * x_length * web_length - cos(angle) * z_length,
                 cos(angle) * x_length * web_length + sin(angle) * z_length]]);}

         {translate([width ,0,0])
           rotate([0,-90,0])
             linear_extrude(height = z_length * web_thickness, center = false)
             polygon([[z_length, (z_length / sin(angle)) + (z_length / tan(angle))],
                [z_length, (y_length - 0) * web_length],
                [sin(angle) * x_length * web_length - cos(angle) * z_length,
                 cos(angle) * x_length * web_length + sin(angle) * z_length]]);}

             /*
             polygon([[0,0], [0,y_length * web_length],
                 [sin(angle)*x_length * web_length, cos(angle) * x_length * web_length]]);}*/

         {translate([width,0,0])
           rotate([0,-90,0])
             linear_extrude(height = width, center = false)
             polygon([[0,0],[0,y_length],
                 [z_length,y_length],
                 [z_length, (z_length / sin(angle)) + (z_length / tan(angle))],
                 [sin(angle) * x_length - cos(angle) * z_length, cos(angle) * x_length + sin(angle) * z_length],
                 [sin(angle) * x_length, cos(angle) * x_length]]);}}

         // drill bottom hole
         {
             for(stepy=[10:20:70])
             for(stepw=[10:20:70])
              if(stepy < y_length && stepw < width)
           translate([stepw, stepy, -0.10])
           cylinder(80 + 0.2, hole_size * 0.5, hole_size * 0.5, false, $fn=20);}

         // drill top hole
         {translate([0, sin(angle) * z_length, -cos(angle) * z_length])
           rotate([angle,0,0])
           { for(stepy=[10:20:70])
             for(stepw=[10:20:70])
              if(stepy < x_length && stepw < width)
           translate([stepw, stepy, z_length - y_length ])
             cylinder(80 + 0.2, hole_size * 0.5, hole_size * 0.5, false, $fn=20);}}}}}

```


[User]
Hey, I'm looking to create some tool trays for my workbench, and I'd like them to be 3D printable using OpenSCAD. I've got some ideas for different shapes and want to make them parametric so I can easily tweak them.

I need three tray designs:

1.  **Cylinder Tray:** A simple cylindrical tray with a flat bottom. I'd like to control the overall height, the radius, the radius of the bottom curve, and the wall thickness, as well as a printer tolerance setting. It needs a few holes cut out for mounting it to a vertical surface. The location of those holes doesn't matter as long as they're evenly spaced around the cylinder. Also, I need to be able to render it with either a single wall (vase mode) or with the specified wall thickness.

2.  **Single-Sided Tray:** This one is a long, rectangular tray with rounded ends. I want to specify its height, length, width, end radius, printer tolerance and wall thickness (for normal mode). I also need a couple of holes cut out to mount this to a flat surface. As with the cylinder tray, the location doesn't matter. It should also have the vase mode / wall thickness choice.

3.  **Angle Tray:** This is like two rectangular trays joined at a right angle to make a corner piece. I need to control its height, length and width for each side, the radius of the rounded corners, a corner offset to control how deep the two sides are, the printer tolerance, and the wall thickness (for normal mode). It also needs holes for mounting, and should also have a vase mode or wall thickness option. Again, location doesn't matter.

For all of these, I'd like to be able to set a general "printer tolerance" that affects all dimensions slightly to allow for the inherent inaccuracy in 3d printing, and I want a setting to change the definition for the final render. I also want to be able to switch between normal mode (adjustable wall thickness) and vase mode (single wall) for each tray.

Also, I'm thinking a small radius on the bottom edges of each tray would be nice. Let's make that a parameter too.

Could you generate the OpenSCAD files for these? I don't need any specific libraries beyond the built-in features of OpenSCAD. It would also be great if the code was well-commented! Thanks!

[Assistant]
ToolTray_001_Customizer.scad:
```
/* [General] */
//Choose the tray you want to render
part=0; //[0:Cylinder Tray,1:Single Sided Tray,2:Angle Tray]
//Space between parts (printer tolerence)
tol=0.3;//[0:0.1:0.6]
//Definition
def=60; //[60:Low,90:Medium,120:High]

//Bottom Part Radius
b_rad=1.5;//[0:0.5:2.5]

//Print mode
mode=0;//[0:Vase Mode,1:Normal Mode]

/* [Cylinder Tray Parameters] */
//Tray height
ct_h=100;//[20:1:200]
//Tray radius
ct_r=25;//[20:1:100]

//Wall thickness (Normal Mode)
ct_wt=1.8;//[0.5:0.1:4]

/* [Single Sided Tray Parameters] */
//Tray height
sst_h=22;//[20:1:200]
//Tray length
sst_l=120;//[30:1:200]
//Tray width
sst_w=40;//[20:1:200]
//Tray radius (can generate issues! start with low value then increase it)
sst_r=20;//[5:1:100]

//Wall thickness (Normal Mode)
sst_wt=1.8;//[0.5:0.1:4]

/* [Angle Tray Parameters] */
//Tray height
at_h=30;//[20:1:200]
//Tray length
at_l=200;//[30:1:300]
//Tray width
at_w=200;//[20:1:300]
//Tray radius
at_r=10;//[5:1:100]
//Corner offset
at_co=35;//[0:1:70]

//Wall thickness (Normal Mode)
at_wt=1.8;//[0.5:0.1:4]

////////////////////
////////////////////
$fn=def;
module Cylinder_Tray(a) {
    difference() {
        union() {
            difference() {
                translate([0,0,b_rad]) union() {
                    cylinder(d=2*ct_r,h=ct_h-b_rad);
                    hull() {rotate_extrude() translate([ct_r-b_rad,0,0]) circle(d=2*b_rad);}
                }
                translate([-10,-ct_r-2,-1]) hull() {
                    translate([0.5,9.5,0]) cylinder(d=1+2*tol,h=ct_h-17);
                    translate([5,3.5,0]) cylinder(d=1+2*tol,h=ct_h-9);
                    translate([19.5,9.5,0]) cylinder(d=1+2*tol,h=ct_h-17);
                    translate([15,3.5,0]) cylinder(d=1+2*tol,h=ct_h-9);
                }
                translate([-15,-ct_r-3+tol,-1]) cube([30,5,ct_h+2]);
            }
        }
        if(a==1) {
            difference() {
                translate([0,0,b_rad+ct_wt]) union() {
                    cylinder(d=2*ct_r-2*ct_wt,h=ct_h-b_rad);
                    hull() {rotate_extrude() translate([ct_r-b_rad-ct_wt,0,ct_wt]) circle(d=2*b_rad);}
                }
                translate([-10,-ct_r-2,-1]) hull() {
                    translate([0.5,9.5,0]) cylinder(d=1+2*tol+2*ct_wt,h=ct_h-17+ct_wt);
                    translate([5,3.5,0]) cylinder(d=1+2*tol+2*ct_wt,h=ct_h-9+ct_wt);
                    translate([19.5,9.5,0]) cylinder(d=1+2*tol+2*ct_wt,h=ct_h-17+ct_wt);
                    translate([15,3.5,0]) cylinder(d=1+2*tol+2*ct_wt,h=ct_h-9+ct_wt);
                }
                translate([-15,-ct_r-3+tol+ct_wt,-1]) cube([30,5,ct_h+2]);
            }
        }
        else{}
    }
}

module SingleSided_Tray(a) {
    difference() {
        translate([0,0,b_rad]) union() {
            hull() {
                for(i=[5,sst_l-5]) translate([i,5,-sst_h/2]) {
                    cylinder(d=10,h=sst_h-b_rad);
                    rotate_extrude() translate([5-b_rad,0,0]) circle(d=2*b_rad);
                }
                for(i=[sst_r,sst_l-sst_r]) translate([i,sst_w-sst_r,-sst_h/2]) difference() {
                    union() {
                        cylinder(d=2*sst_r,h=sst_h-b_rad);
                        rotate_extrude() translate([sst_r-b_rad,0,0]) circle(d=2*b_rad);
                    }
                    translate([0,-105,0]) cube(210,center=true);
                }
            }
        }
        if (sst_l<=60) {
            translate([sst_l/2-10,-4-tol,-sst_h/2-1]) hull() {
                translate([0.5,9.5,0]) cylinder(d=1+2*tol,h=12+(sst_h-20)/2);
                translate([5,3.5,0]) cylinder(d=1+2*tol,h=20+(sst_h-20)/2);
                translate([19.5,9.5,0]) cylinder(d=1+2*tol,h=12+(sst_h-20)/2);
                translate([15,3.5,0]) cylinder(d=1+2*tol,h=20+(sst_h-20)/2);
            }
        }
        else {
            for(i=[5,sst_l-25]) translate([i,-4-tol,-sst_h/2-1]) hull() {
                translate([0.5,9.5,0]) cylinder(d=1+2*tol,h=12+(sst_h-20)/2);
                translate([5,3.5,0]) cylinder(d=1+2*tol,h=20+(sst_h-20)/2);
                translate([19.5,9.5,0]) cylinder(d=1+2*tol,h=12+(sst_h-20)/2);
                translate([15,3.5,0]) cylinder(d=1+2*tol,h=20+(sst_h-20)/2);
            }
        }

        //Mode
        if(a==1) {
            difference() {
                translate([0,0,b_rad+sst_wt]) union() {
                    hull() {
                        for(i=[5+sst_wt,sst_l-5-sst_wt]) translate([i,5+sst_wt,-sst_h/2]) {
                            cylinder(d=10,h=sst_h-b_rad);
                            rotate_extrude() translate([5-b_rad,0,0]) circle(d=2*b_rad);
                        }
                        for(i=[sst_r,sst_l-sst_r]) translate([i,sst_w-sst_r,-sst_h/2]) difference() {
                            union() {
                                cylinder(d=2*sst_r-2*sst_wt,h=sst_h-b_rad);
                                rotate_extrude() translate([sst_r-b_rad-sst_wt,0,0]) circle(d=2*b_rad);
                            }
                            translate([0,-105,0]) cube(210,center=true);
                        }
                    }
                }
                if (sst_l<=60) {
                    translate([sst_l/2-10,-4-tol,-sst_h/2-1]) hull() {
                        translate([0.5,9.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=12+(sst_h-20)/2+sst_wt);
                        translate([5,3.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=20+(sst_h-20)/2+sst_wt);
                        translate([19.5,9.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=12+(sst_h-20)/2+sst_wt);
                        translate([15,3.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=20+(sst_h-20)/2+sst_wt);
                    }
                }
                else {
                    for(i=[5,sst_l-25]) translate([i,-4-tol,-sst_h/2-1]) hull() {
                        translate([0.5,9.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=12+(sst_h-20)/2+sst_wt);
                        translate([5,3.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=20+(sst_h-20)/2+sst_wt);
                        translate([19.5,9.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=12+(sst_h-20)/2+sst_wt);
                        translate([15,3.5,0]) cylinder(d=1+2*tol+2*sst_wt,h=20+(sst_h-20)/2+sst_wt);
                    }
                }
            }
        }
        else {}
    }
}
module Angle_Tray(a) {
    difference() {
        translate([0,0,-at_h/2+b_rad]) hull() {
            for(i=[at_r+at_co,at_w-at_r]) translate([at_r,i,0]) {
                cylinder(d=2*at_r,h=at_h);
                rotate_extrude() translate([at_r-b_rad,0,0]) circle(r=b_rad);
            }
            for(i=[at_r+at_co,at_l-at_r]) translate([i,at_r,0]) {
                cylinder(d=2*at_r,h=at_h);
                rotate_extrude() translate([at_r-b_rad,0,0]) circle(r=b_rad);
            }
        }
        if (at_l-at_co<=60) {
            translate([at_co+(at_l-at_co)/2-10,-4-tol,-at_h/2-1]) hull() {
                translate([0.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([5,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
                translate([19.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([15,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
            }
        }
        else {
            for(i=[at_r+at_co+5,at_l-at_r-25]) translate([i,-4-tol,-at_h/2-1]) hull() {
                translate([0.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([5,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
                translate([19.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([15,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
            }
        }
        if (at_w-at_co<=60) {
            translate([-4-tol,at_co+(at_w-at_co)/2+10,-at_h/2-1]) rotate([0,0,-90]) hull() {
                translate([0.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([5,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
                translate([19.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([15,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
            }
        }
        else {
            for(i=[at_r+at_co+25,at_w-at_r-5]) translate([-4-tol,i,-at_h/2-1]) rotate([0,0,-90]) hull() {
                translate([0.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([5,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
                translate([19.5,9.5,0]) cylinder(d=1+2*tol,h=12+(at_h-20)/2);
                translate([15,3.5,0]) cylinder(d=1+2*tol,h=20+(at_h-20)/2);
            }
        }

        //Mode
        if(a==1) {
            difference() {
                translate([0,0,-at_h/2+b_rad+at_wt]) hull() {
                    for(i=[at_r+at_co,at_w-at_r]) translate([at_r,i,0]) {
                        cylinder(d=2*at_r-2*at_wt,h=at_h);
                        rotate_extrude() translate([at_r-b_rad-at_wt,0,0]) circle(r=b_rad);
                    }
                    for(i=[at_r+at_co,at_l-at_r]) translate([i,at_r,0]) {
                        cylinder(d=2*at_r-2*at_wt,h=at_h);
                        rotate_extrude() translate([at_r-b_rad-at_wt,0,0]) circle(r=b_rad);
                    }
                }
                if (at_l-at_co<=60) {
                    translate([at_co+(at_l-at_co)/2-10,-4-tol,-at_h/2-1]) hull() {
                        translate([0.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([5,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                        translate([19.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([15,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                    }
                }
                else {
                    for(i=[at_r+at_co+5,at_l-at_r-25]) translate([i,-4-tol,-at_h/2-1]) hull() {
                        translate([0.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([5,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                        translate([19.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([15,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                    }
                }
                if (at_w-at_co<=60) {
                    translate([-4-tol,at_co+(at_w-at_co)/2+10,-at_h/2-1]) rotate([0,0,-90]) hull() {
                        translate([0.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([5,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                        translate([19.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([15,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                    }
                }
                else {
                    for(i=[at_r+at_co+25,at_w-at_r-5]) translate([-4-tol,i,-at_h/2-1]) rotate([0,0,-90]) hull() {
                        translate([0.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([5,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                        translate([19.5,9.5,0]) cylinder(d=1+2*tol+2*at_wt,h=12+(at_h-20)/2+at_wt);
                        translate([15,3.5,0]) cylinder(d=1+2*tol+2*at_wt,h=20+(at_h-20)/2+at_wt);
                    }
                }
            }
        }
        else {}
    }
}

module Render(a){
    if(a==0) {Cylinder_Tray(mode);}
    else if(a==1) {SingleSided_Tray(mode);}
    else {Angle_Tray(mode);}
}

Render(part);
```


[User]
I'd like to design a customizable stand for e-cigarette bottles and an iStick. I've got some parameters in mind, so I'll lay them out.

**General Layout:**

*   I need a grid of bottle holders, arranged in rows and columns. I'd like to be able to set the number of rows and columns using variables.
*   There should be a space for an iStick (e-cig battery) if desired, adjacent to the bottle grid. This should be toggleable.
*   The whole thing should sit on a base that I can customize.
*   All units are in millimeters.

**Bottle Holder Settings:**

*   Bottle diameter is adjustable.
*   The spacing between bottle holders, and between bottle holders and the stick holder, should be adjustable.
*   I'd like the ability to round the bottom edges of the bottle holes.

**iStick Holder Settings:**

*   I need to be able to toggle whether the iStick holder is present or not.
*   The orientation of the iStick holder (north-south or east-west) should be adjustable with a variable.
*   I'd like to set the length and thickness of the stick holder, plus an extra amount of surrounding space.
*   Also include a slot for the USB connector. The USB slot should be an option on either the left or right side. I need the length of this slot, and how far up it begins from the base of the iStick holder, to be settable.

**Base/Tray Settings:**

*   I need to control the overall thickness of the base and the roundness of its edges.
*   I'd like to be able to add borders around the grid.

**Other Settings:**

*   I need a tolerance setting to enlarge the holes to compensate for 3D printing tolerances, to ensure the bottles and iStick fit properly.
*   I'd like the ability to output a thin test slice to verify dimensions easily, which would need to be switchable on or off.

I'm expecting this to be a single SCAD file, using the standard OpenSCAD library. I'd like all of these parameters to be editable through OpenSCAD's customizer.

[Assistant]
istick_standc.scad:
```
// All dimension are in mm

/* [Basic settings] */
rows = 3;              // [1:1:12]
cols = 3;               // [1:1:12]

//  Enlarge all holes by this value. 1mm suggested for around 10mm diameter, more for lower diameter less for higher
ht = 0.8;               // [0.5:0.1:2]

// thin slice draft just to check for holes, stick & usb dimensions
fingerprint= 0;       // [0: Normal, 1: FingerPrint]
slice_thick = 0.2;      // Thick of the test slice

// ABS/PLA thickness at end of holes
back_thick = 0.5;    // [0.1:0.1:0.3]
holes_depth = 10;   // [5:1:25]

/* [Tray settings (mm)] */
tray_round = 5;         // [0:1:20]
tray_thick = holes_depth + back_thick;      // Tray thickness
tray_x_border = 0;        // [0:1:50]
tray_y_border = 0;        // [0:1:50]

/* [Bottles settings (mm)] */
// 10.4 AAA battery
// 21.2 10ml istick bottle
bottle_diameter = 18;    // [10:0.1:60]
bottle_round_end = 0;

//  Interval between bottles / bottles & stick
bottle_intvl = 1;             // [0:1:10]

/* [IStick Settings (mm)] */
cig_holder = 0;             // [0: No, 1: Yes]
cig_orientation = 0;        // [0: North-South, 1: East-West]

//cigarette hole length
cig_hlength = 45.1;         // [10:0.1:60]

// cigarette hole thickness
cig_thicks = 23.1;              // [10:0.1:40]

// Additionnal space around stick
cig_add_space = 0;        // [0:1:30]

/* [Usb Connector */
// usb room
cig_usb_hwidth = 12;       // [5:1:20]

// Start height of usb connector
cig_usb_starty = 6;         // [0:1:10]

//  Usb hole on the left
cig_usb_left=0;             // [0:No, 1:Yes]

// Usb hole on the right
cig_usb_right=1;           // [0:No, 1:Yes]

/* [Hidden] */
// Do not edit below values
// (Unless for fun :-D)
// --------------------------------
$fn=96;             // [32:1:128]
cig_thick = cig_thicks+ht;
cig_usb_width = cig_usb_hwidth+ht;
cig_usb_start = cig_usb_starty-ht;
bottle_d = bottle_diameter + ht;
cig_length = cig_hlength+ht;
cig_rotate = 1 * cig_holder * cig_orientation;

x1 = rows * bottle_d + (rows -1) * bottle_intvl + tray_x_border*2;
y1 = cols * bottle_d+ (cols-1) * bottle_intvl + tray_y_border*2;
y2 = y1 + (bottle_intvl + cig_thick)*cig_holder;
y3 = y2 + cig_rotate * (cig_length - cig_thick)+cig_add_space*cig_holder;

xstart = (bottle_d+bottle_intvl)*(rows-1)/2;
ystart = (bottle_d+bottle_intvl)*(cols-1)/2+tray_y_border;

cig_li = cig_length-cig_thick;
// cig_usb_x = (cig_thick/2-cig_length)/2-tray_round;
cig_usb_x = 1000;

// Clipping (should be little greater than the tray dimension
hx = 1.1*(x1+tray_round*2);
hy = 1.1*(y3+tray_round*2);

difference()
{
    base();
    holes();

    // slice
    if (fingerprint)
    {
        z_slice=cig_usb_start+bottle_round_end*bottle_d/2;
        z_slize = tray_thick;

        translate([-hx/2, -hy/2, -0.01])
        cube([hx, hy, z_slice-slice_thick, ], center=false);
        //translate([0,0,250+back_thick+1+z_slice]) cube([hx, hy, 500], center=true);
    }
}

module base()
{
    // Pavé support cig + flacons
    union()
    {
        difference()
        {
            translate([0, 0, tray_thick/2])
            {
                cube(size=[x1,y3,tray_thick], center=true);
                translate([0,0,tray_thick/2-tray_round])
                {
                    translate([-x1/2,0,0])
                        rotate([90,0,0]) cylinder(h=y3, r=tray_round,center=true);
                    translate([x1/2,0,0])
                        rotate([90,0,0]) cylinder(h=y3, r=tray_round,center=true);
                    translate([0, y3/2, 0])
                        rotate([0, 90, 0]) cylinder(h=x1, r=tray_round, center=true);
                    translate([0, -y3/2, 0])
                        rotate([0, 90, 0]) cylinder(h=x1, r=tray_round, center=true);
                    for(xs = [-x1/2 : x1 : x1/2])
                        for(ys = [-y3/2 : y3 : y3/2])
                            translate([xs,ys,0])
                                sphere(r=tray_round);
                }
            }

            // Clip
            translate([0,0, -500])
                cube([hx, hy, 1000], center = true);
        }

        hc = tray_thick-tray_round;
        if (hc>0)
        {
            translate([0, 0, hc/2])
                cube(size=[x1+2*tray_round,y3, hc], center=true);
            translate([0, 0, hc/2])
                cube(size=[x1,y3+2*tray_round, hc], center=true);
            for(xs = [-x1/2 : x1 : x1/2])
                for(ys = [-y3/2 : y3 : y3/2])
                    translate([xs,ys,0])
                        cylinder(h=hc, r=tray_round);
        }
    }
}

module holes()
{
    translate([0,tray_y_border,back_thick])
    union()
    {
        // Holes for bottles
        translate([0,(y3-y1)/2,0])
            for(nx = [0 : 1 : rows-1 ])
                for(nb = [0:1:cols-1])
                    translate([-xstart+nx*(bottle_d+bottle_intvl), -ystart+nb*(bottle_d+bottle_intvl), bottle_round_end*bottle_d/2])
                    {
                        cylinder(h = holes_depth+1, d=bottle_d, center=false);
                        if (bottle_round_end)
                        {
                            sphere(d=bottle_d);
                        }
                    }

        if (cig_holder)
        {
            // Hole for istick
            translate([0, (cig_thick-y2)/2, 0])
            {
                rotate([0,0,cig_rotate*90])
                {
                    translate([0,0,holes_depth/2])
                        cube(size=[cig_length-cig_thick, cig_thick, holes_depth+0.01], center=true);
                    translate([cig_li/2, 0, ])
                        cylinder(h = holes_depth+1, d=cig_thick, center=false);
                    translate([-cig_li/2, 0, ])
                        cylinder(h = holes_depth+1, d=cig_thick, center=false);

                    translate([0, 0, cig_usb_start+50])
                    {
                        if (cig_usb_left)
                            translate([1000,0,0]) cube([2000, cig_usb_width, 100], center=true);
                        if (cig_usb_right)
                            translate([-1000,0,0]) cube([2000, cig_usb_width, 100], center=true);
                    }
                }
            }
        }
    }
}

```


[User]
Okay, I'd like to request a design for a parametric solder fume extractor in OpenSCAD. It should be based on the Hakko FA-400 design, allowing for both vertical and horizontal orientations.

Here are the key parameters that need to be configurable:

*   **`fan_w`**: Width of one side of the fan (e.g., 120mm).
*   **`filter_w`**: Width of the filter. It should default to `fan_w + 10`.
*   **`filter_t`**: Thickness of the filter (e.g., 9mm).
*   **`wall`**: Wall thickness of the printed parts (e.g., 1.2mm).
*   **`fan_r`**: Radius of the fan aperture in the grill (e.g., `fan_w / 2.1`).
*   **`fan_hole_spacing`**: Distance between screw holes on the fan, center-to-center. Should offer common sizes like 32mm, 40mm, 50mm, 60mm, 72mm, 83mm, 105mm, and 124.5mm as presets, corresponding to 40mm, 50mm, 60mm, 70mm, 80mm, 92mm, 120mm, and 140mm fans respectively.
*   **`fan_hole_r`**: Radius of the fan screw holes (e.g., 2mm).
*   **`angle`**: Tilt angle of the housing (e.g., 20 degrees).
*   **`corner`**: Size of the filter retention corners (e.g., 10mm).
*   **`print_grate`**: A boolean (0 or 1) to control rendering the secondary grate (defaults to 0).
*   **`print_extractor`**: A boolean (0 or 1) to control rendering the extractor itself (defaults to 1).

The design should include:

1.  A main body that holds the filter, with an opening for the fan.
2.  Corners for holding the filter grating in place.
3.  A grille on the fan side of the filter.
4.  An angled housing for vertical/horizontal orientation. This angled piece should have a filtered air intake if oriented horizontally.
5.  A filter grate on the intake side, optionally rendered based on the `print_grate` parameter.

The filter width should be `fan_w+10` by default. Please note that filter size will need to be cut to fit.

I'd also like the OpenSCAD code to include a `grating` module, which takes width, height, thickness, number of holes in x and y, and grate wall thickness as parameters. It should create a grating using differences with cubes.

Finally, could you please make sure that it's easy to switch between fan sizes by selecting a `fan_hole_spacing` from the provided presets?

I'd like you to generate all the necessary .scad files. I do not expect any external libraries, this should be entirely self-contained.

[Assistant]
extractor_modified.scad:
```
// Solder Fume Extractor - Originally by Nirav Patel <http://eclecti.cc>
// Modified for parameterization by Rob Street
//
// This is a parametric solder fume extractor, designed to be used either
// vertically to pull in a larger volume of air or horizontally to have
// stronger suction.  It is a blatant rip off of a Hakko FA-400.
// In the default configuration, it works with a 120mm fan and the filters
// used by the Hakko and similar commercial fume extractors.
//
// This work is licensed under a Creative Commons Attribution 3.0 Unported License.

//All measures are in mm
//Width of 1 side of the fan. Default 120
fan_w = 120;
//Filter Width. Default 130 (size of Haako filter)
filter_w = fan_w+10;
//Filter Thickness. Default 9 (size of Haako filter)
filter_t = 9;
//Wall thickness. Default 1.2 (3*.4mm nozzle diameter)
wall = 1.2;
//Radius of the fan aperture in grill
fan_r = fan_w/2.1;
//Distance between screw holes Center-Center. Default 105
fan_hole_spacing = 105; // [32:32mm for 40mm fan,40:40mm for 50mm fan, 50:50mm for 60mm fan, 60:60mm for 70mm fan, 72:72mm for 80mm fan, 83:83mm for 92mm fan, 105:105mm for 120mm fan, 124.5:124.5mm for 140mm fan]
fan_hole = fan_hole_spacing/2;
//     *****Common fan sizes*****
//    40mm fan size -> 32mm between screw holes
//    50mm fan size -> 40mm between screw holes
//    60mm fan size -> 50mm between screw holes
//    70mm fan size -> 60mm between screw holes
//    80mm fan size -> 72mm between screw holes
//    92mm fan size -> 83mm between screw holes
//   120mm fan size -> 105mm between screw holes
//   140mm fan size -> 124.5 mm between screw holes
//Screw hole radius. Default 2
fan_hole_r = 2;
//Tilt angle. Default 20
angle = 20;
//Size of the filter retention corners. Default 10
corner = 10;
//Should we render the secondary grate? Default 0
print_grate = 0;
//should we render the extractor? Default 1
print_extractor = 1;

// grating used to hold both sides of the filter
module grating(w, h, t, holes_x, holes_y, grate_wall) {
    translate([-w/2, -h/2, 0]) {
        difference() {
            cube([w, h, t]);
            for(x = [grate_wall : (w-grate_wall)/holes_x : w-grate_wall]) {
                for(y = [grate_wall : (h-grate_wall)/holes_y : h-grate_wall]) {
                    translate([x, y, 0])
                        cube([(w-grate_wall)/holes_x-grate_wall, (h-grate_wall)/holes_y-grate_wall, t]);
                }
            }
        }
    }
}

if(print_extractor == 1){
    union() {

        difference() {
            // main body of filter holder
            translate([-filter_w/2-wall, -filter_w/2-wall, 0])
                cube([filter_w+wall*2, filter_w+wall*2, filter_t+wall*3]);
            difference() {
                // filter area
                translate([-filter_w/2, -filter_w/2, wall])
                    cube([filter_w, filter_w, filter_t+wall*2]);
                // cut the corners that'll hold the filter grate in place
                for (x = [-1, 1])
                    for (y = [-1, 1])
                        translate([filter_w/2*x, filter_w/2*y, filter_t+wall*2])
                            rotate([0, 0, 45])
                                translate([-corner, -corner, 0])
                                    cube([corner*2, corner*2, wall]);
            }
            // fan air hole
            intersection() {
                translate([-fan_w/2, -fan_w/2, 0])
                    cube([fan_w, fan_w, wall]);
                cylinder(h = wall, r = fan_r);
            }
            // fan bolt holes
            for (x = [-1, 1])
                for (y = [-1, 1])
                    translate([x*fan_hole, y*fan_hole, 0])
                        cylinder(h = wall, r = fan_hole_r);
        }
        // grating on the fan side of the filter
        difference() {
            grating(fan_w+2, fan_w+2, wall, 6, 6, wall);
            // fan bolt holes again to ensure the grate doesn't block the holes
            for (x = [-1, 1])
                for (y = [-1, 1])
                    translate([x*fan_hole, y*fan_hole, 0])
                        cylinder(h = wall, r = fan_hole_r);
        }
        // the angled housing
        intersection() {
            translate([-filter_w/2-wall, -filter_w/2-wall, filter_t+wall*3]) {
                rotate([-angle, 0, 0]) {
                    union() {
                        difference() {
                            union() {
                                cube([filter_w+wall*2, (filter_w+wall*2)*cos(angle), (filter_w+wall*2)*sin(angle)]);
                                // extra little angled feet
                                for (x = [0, filter_w+wall])
                                    translate([x, 0, -(filter_t+wall*3)/cos(angle)])
                                        cube([wall, (filter_w+wall*2)*cos(angle), (filter_t+wall*3)/cos(angle)]);
                            }
                            // hollow the inside
                            translate([wall, wall, 0])
                               cube([filter_w, filter_w, (filter_w+wall*2)*sin(angle)]);
                            // make an opening for the grating
                            rotate([90, 0, 0])
                                translate([wall, 0, -2*wall])
                                    cube([filter_w, (filter_w+wall*2)*sin(angle)*0.75, 3*wall]);
                        }
                        // the air intake for horizontal orientation
                        rotate([90, 0, 0])
                            translate([filter_w/2+wall, (filter_w+wall*2)*sin(angle)*0.75/2, -wall])
                                grating(filter_w, (filter_w+wall*2)*sin(angle)*0.75, wall, 3, 1, 4);
                    }
                }
            }
            // cut off the bits that are outside our working area
            translate([-filter_w/2-wall, -filter_w/2-wall-50, 0])
                cube([filter_w+wall*2, filter_w+wall*2+50, filter_t+wall*3+(filter_w+wall*2)*tan(angle)]);
        }
    }
}

// the filter grate on the intake side
if(print_grate == 1){
    translate([filter_w+wall+30, 0, 0])
        grating(filter_w-2, filter_w-corner*sqrt(2), wall, 6, 6, wall);
}
```