[User]
I'd like to request an OpenSCAD design for a parametric kitchen knife sheath. I've seen some existing designs, but I'd like a fresh one tailored to my specific needs.

Essentially, I need a sheath that snugly fits a kitchen knife. I'd like the design to be fully parametric, driven by variables for knife dimensions. Here’s what I envision:

*   **Knife Length:** A parameter to set the blade length, measured from the handle to the tip.
*   **Max Blade Width:** The maximum width of the blade.
*   **Blade Thickness:** The maximum thickness of the blade.
*   **Wall Thickness:** The thickness of the sheath wall itself.
*   **Windows:** An option to include windows in the sheath, allowing a visual check of the blade without fully removing the knife. I'd like to control the number of windows. The windows should alternate sides for easy cleaning.
*   **Holding Ridges:** An optional set of ridges inside the sheath to hold the knife more securely. These would be small, semi-circular ridges.
*   **Grip Cutoff Angle:** The angle of the sheath opening relative to the vertical axis of the blade. It should support both positive and negative angles, allowing the opening to either flare out or taper inward.
*   **All measurements should be in millimeters.**

The sheath should be generated using a difference of two objects – an outer shell representing the sheath and an inner blade-shaped void representing the space for the knife. The design should be suitable for 3D printing.

I don't have a specific preference for libraries, but I'm open to suggestions if they simplify the design process, for instance for the hull operation. I'd like the overall shape to be smooth and functional, without any unnecessary complexities.

Could you create the OpenSCAD files needed for this design?

[Assistant]
Parametric_Kitchen_Knife_Sheath_V5.05.scad:
```
// parametric knife sheath
// original by Steve Pendergrast (vorpal)
// https://www.thingiverse.com/thing:125109
//
// remix v5.05 by BikeCyclist: Output rotated by 180 degrees to work around a suspected display bug in Thingiverse customizer - https://www.thingiverse.com/thing:4418138
// remix v5.04 by BikeCyclist: Default grip cutoff angle changed to something Thingiverse customizer can handle - https://www.thingiverse.com/thing:4418138
// remix v5.01 by BikeCyclist: Now supports negative grip cutoff angles
// remix v4 by BikeCyclist: Bugfix for better walls with windows at high grip cutoff angles
// remix v3 by BikeCyclist: Bugfix (windows in thick walls) as suggested by Laurie, improved use of thingiverse customizer interface, better walls with windows at high grip cutoff angles
// remix v2 by BikeCyclist: Parametric wall thickness, shorter ridges, better symmetry
// remix v1 by BikeCyclist: Introducing holding ridge and oblique cut-off at grip
// https://www.thingiverse.com/thing:2856699

// length: the length of the blade from handle to tip
// maxwidth: the maximum width of the blade
// tipwidth:  the width near the tip, zero means it comes to a point
// thickness: maximum thickness of the blade
// has_windows:  0 = do not put in windows to show the blade and simplify cleaning
// has_ridges: 0 = do not add ridges to the sheath to hold the knife by friction
// grip_cutoff_angle:  the angle of the mouth of the sheet at the grip (off the vertical)
//
// all measurements are in mm

//CUSTOMIZER VARIABLES

//	Knife length, mm
knife_length = 115;	//	[25:350]

//	Knife Max Blade width, mm
knife_width = 18;	//	[10:100]

//	Knife thickness, mm
knife_thickness = 1.5;

//	Wall thickness, mm
wall_thickness = 0.5;

// Show knife through windows?
has_windows = 1; // [0:No, 1:Yes]

// Number of windows per side
number_of_windows = 10; // [1:25]

// Sheath has ridges to hold the knife more securely?
has_ridges = 0; // [0:No, 1:Yes]

// Angle of the sheath at the grip (0 = vertical) [-75:75]
grip_cutoff_angle = -20;

//CUSTOMIZER VARIABLES END

$fn = 80*1;  // while debugging reduce this to 20 to save time

wall = 2*wall_thickness;
pad = 2*1;

translate ([0, 0, knife_width + 2 * wall + pad])
    rotate ([0, 180, 180])
        parasheath (length = knife_length, maxwidth = knife_width, thickness = knife_thickness, windows = has_windows);

module parasheath(length=200, maxwidth=25, thickness=2, windows=1)
{
    difference()
    {
        union ()
        {
            sheathwid = maxwidth + pad + 2 * wall;
            extralen = (grip_cutoff_angle<0?sheathwid * tan (-grip_cutoff_angle):0);
            sheathlen = length + wall + pad + extralen;

            translate ([0, -extralen, 0])
            difference()
            {
                // first make an oversized blade, oversized by 2 times the wall thickness

                blade(len = sheathlen, maxwid = sheathwid, thick = thickness + 2 * wall);

                translate([0, 0, wall])
                blade(len = length + 0.1 + pad + extralen, maxwid = maxwidth + pad, thick = thickness);

            }

            if (has_ridges)
            for (i = [-1, 1])
                translate([0, 0, wall + knife_width/2 + pad/2])
                    translate ([i * knife_thickness/2, 0, 0])
                        rotate ([-90, 0, 0])
                            scale ([1, 2, 1])
                                cylinder (d = knife_thickness / 2, h = length/8);

        }

        if (windows)
            intersection ()
            {
                {
                    straight_length = length - maxwidth * sin (grip_cutoff_angle);
                    for (j = [-1, 1])
                        for (i = [1 : number_of_windows])
                            translate([j * (wall + thickness), maxwidth * sin (grip_cutoff_angle) + straight_length/number_of_windows * i + straight_length/(4 * number_of_windows) * j, maxwidth/2 + wall + pad/2])
                                    cube([2 * (wall + thickness), straight_length/(2 * number_of_windows), maxwidth + pad], center = true);
                }

                translate([0, 0, wall])
                    blade(len = length + 0.1 + pad - wall, maxwid = maxwidth + pad, thick = 2 * thickness + 4 * wall);
            }

        rotate ([-grip_cutoff_angle, 0, 0])
            translate([0, -1000, 0])
                    cube([2000,2000,2000], center=true);
    }

    /* Ensure windows don't cut through the mouth edges of the sheath */
    if (windows)
        difference ()
        {
            maxwid = maxwidth + pad + 2 * wall;

            hull ()
            {
                rotate ([-grip_cutoff_angle, 0, 0])
                    translate([0, 0, 0.5 * maxwid / cos (grip_cutoff_angle)])
                        cube ([thickness + 2 * wall, 0.01, maxwid / cos (grip_cutoff_angle)], center = true);

                translate([0, wall / cos (grip_cutoff_angle), 0])
                    rotate ([-grip_cutoff_angle, 0, 0])
                        translate([0, 0, 0.5 * maxwid / cos (grip_cutoff_angle)])
                            cube ([thickness + 2 * wall, 0.01, maxwid / cos (grip_cutoff_angle)], center = true);
            }

            hull ()
            {
                translate([0, -wall / cos (grip_cutoff_angle), 0])
                    rotate ([-grip_cutoff_angle, 0, 0])
                        translate([0, 0, 0.5 * maxwid / cos (grip_cutoff_angle)])
                            cube ([thickness, 0.01, (maxwidth + pad) / cos (grip_cutoff_angle)], center = true);

                translate([0, 2 * wall / cos (grip_cutoff_angle), 0])
                    rotate ([-grip_cutoff_angle, 0, 0])
                        translate([0, 0, 0.5 * maxwid / cos (grip_cutoff_angle)])
                            cube ([thickness, 0.01, (maxwidth + pad) / cos (grip_cutoff_angle)], center = true);
            }
        }
}

module blade (len = 200, maxwid = 25, tipwid = 5, thick = 2)
{
    translate ([-thick/2, 0, 0])
        union() {
            intersection() {
                    // first make a cube
                    cube([thick, len, maxwid]);

                    translate([0,len-maxwid,maxwid])
                        rotate([0,90,0])
                            cylinder(r=maxwid,h=thick*3,center=true);
            }

            cube([thick,len-maxwid,maxwid]);
        }
}

```


[User]
I'd like to create a set of customizable parts: nuts, bolts, washers, and threaded rods, with a particular focus on a knurled screw head option, which should be highly customizable.

For the nuts, bolts, and rods, I need to be able to set the thread's outer diameter, step/pitch, and step shape. I'd also like to specify the length of the threaded section and whether to include a countersink at the ends, and if so, how large the countersink should be. For bolts, I need the ability to set the non-threaded section length, and its diameter - the diameter should also have some options, specifically the inner and outer diameter of the thread.

The bolts themselves should be highly customizable in terms of head type. I need options for a hex head, a standard socket cap, a button socket cap, a countersunk socket cap, and the knurled head I mentioned earlier. For the hex head, I need control over the number of facets, and for the socket types (including the knurled head), I need to specify the diameter and height of the head. When using the socket head types, I need options for the drive type such as socket, phillips, or slot, and be able to set the diameter, width (for slot and phillips), and slot depth. I would also like an option for a textured surface for the socket head.

For the nuts, I need a "normal" hex nut and a wingnut option. For both, I need to set the diameter and height. The wingnut should have the wing radius defined relative to the nut height via a ratio. I also need to be able to set the thread's parameters, similar to the bolts.

Washers should have settings for inner and outer diameter, as well as thickness.

The knurling on the screw head is very important, and needs several customizable parameters including knurl width, height, depth, and the level of smoothness at the ends and surface.

Finally, I need to control the resolution of the generated parts.

The design should rely on the `polyScrewThread.scad` library for threads. Can you generate the OpenSCAD files for this?

[Assistant]
Nut_Job.scad:
```
/* 'Nut Job' nut, bolt, washer and threaded rod factory by Mike Thompson 1/12/2013, Thingiverse: mike_linus
 *
 * Licensing: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Australia License.
 * Further information is available here - http://creativecommons.org/licenses/by-nc-sa/3.0/au/deed.en_GB
 *
 * v2 8/12/2013 - added socket head types
 * v3 2/11/2014 - adjusted wing nut algorithm for better behaviour with unusual nut sizes and added ISO262 metric references
 * v4 31/12/2014 - added optional texture to socket heads, added ability to change the number of facets for a hex head
 * and adjusted wingnut base level on certain nut sizes
 * v5 11/1/2015 - added phillips and slot drive types and improved texture handling
 * v6 21/2/2015 - added wing ratio to wingnuts
 * v7 6/3/2016 - added extended options to control number of facets on nuts, square sockets (or any number of facets) and socket depth control
 * v8 1/1/2017 - modified library code to remove dependence on deprecated 'assign' statement
 *
 * This script generates nuts, bolts, washers and threaded rod using the library
 * script: polyScrewThead.scad (modified/updated version polyScrewThread_r1.scad)
 * http://www.thingiverse.com/thing:8796, CC Public Domain
 *
 * Defaults are for a 8mm diameter bolts, rod, matching nuts and wing nuts that work well together
 * without cleanup or modification. Some default parameters such as the nut outer diameter are deliberately
 * altered to produce a snug fit that can still be hand tightened. This may need to be altered
 * depending on individual printer variances, slicing tools, filament etc. Suggest printing a matching
 * bolt and nut and adjusting as necessary.  Note: slow print speeds, low temperatures and solid
 * fill are recommended for best results.
 */

/* [Component Type] */

type						    = "bolt";//[nut,bolt,rod,washer]

/* [Bolt and Rod Options] */

//Head type - Hex, Socket Cap, Button Socket Cap or Countersunk Socket Cap (ignored for Rod)
head_type              			= "knurl";//[hex,socket,button,countersunk,knurl]
//Drive type - Socket, Phillips, Slot (ignored for Hex head type and Rod)
drive_type              		= "socket";//[socket,phillips,slot]
//Distance between flats for the hex head or diameter for socket or button head (ignored for Rod)
head_diameter    				= 12;
//Height of the head (ignored for Rod)
head_height  					= 10;
//Diameter of drive type (ignored for Hex head and Rod)
drive_diameter					= 5;
//Width of slot aperture for phillips or slot drive types
slot_width					    = 1;
//Depth of slot aperture for slot drive type
slot_depth 					    = 2;
//Surface texture (socket head only)
texture                         = "exclude";//[include,exclude]
//Outer diameter of the thread
thread_outer_diameter           = 6.35;
//Thread step or Pitch (2mm works well for most applications ref. ISO262: M3=0.5,M4=0.7,M5=0.8,M6=1,M8=1.25,M10=1.5)
thread_step    					= 1.27;
//Step shape degrees (45 degrees is optimised for most printers ref. ISO262: 30 degrees)
step_shape_degrees 				= 30;
//Length of the threaded section
thread_length  					= 6;
//Countersink in both ends
countersink  					= 2;
//Length of the non-threaded section
non_thread_length				= 4;
//Diameter for the non-threaded section (-1: Same as inner diameter of the thread, 0: Same as outer diameter of the thread, value: The given value)
non_thread_diameter				= 5;

/* [Nut Options] */

//Type: Normal or WingNut
nut_type	                    = "normal";//[normal,wingnut]
//Distance between flats for the hex nut
nut_diameter    				= 12;
//Height of the nut
nut_height	  				    = 6;
//Outer diameter of the bolt thread to match (usually set about 1mm larger than bolt diameter to allow easy fit - adjust to personal preferences)
nut_thread_outer_diameter     	= 9;
//Thread step or Pitch (2mm works well for most applications ref. ISO262: M3=0.5,M4=0.7,M5=0.8,M6=1,M8=1.25,M10=1.5)
nut_thread_step    				= 2;
//Step shape degrees (45 degrees is optimised for most printers ref. ISO262: 30 degrees)
nut_step_shape_degrees 			= 45;
//Wing radius ratio.  The proportional radius of the wing on the wing nut compared to the nut height value (default = 1)
wing_ratio                      = 1;
wing_radius=wing_ratio * nut_height;

/* [Washer Options] */

//Inner Diameter (suggest making diameter slightly larger than bolt diameter)
inner_diameter					= 8;
//Outer Diameter
outer_diameter					= 14;
//Thickness
thickness					    = 2;

/* [Knurl Settings] */

knurlWidth = 3;
knurlHeight = 2;
knurlDepth = 0.5;
knurlEndSmooth = 2;
knurlSurfaceSmooth = 0;

/* [Extended Options] */

//Number of facets for hex head type or nut. Default is 6 for standard hex head and nut
facets                          = 6;
//Number of facets for hole in socket head. Default is 6 for standard hex socket
socket_facets                   = 6;
//Depth of hole in socket head. Default is 3.5
socket_depth                    = 3.5;
//Resolution (lower values for higher resolution, but may slow rendering)
resolution    					= 0.5;
nut_resolution    				= resolution;

//Hex Bolt
if (type=="bolt" && head_type=="hex")
{
	hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,head_height,non_thread_length,non_thread_diameter);
}

//Rod
if (type=="rod")
{
	hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,0,non_thread_length,non_thread_diameter);
}

//Hex Nut (normally slightly larger outer diameter to fit on bolt correctly)
if (type=="nut" && nut_type=="normal")
{
	hex_nut(nut_diameter,nut_height,nut_thread_step,nut_step_shape_degrees,nut_thread_outer_diameter,nut_resolution);
}

//Wing Nut variation of hex nut. Cylinders added to each side of nut for easy turning - ideal for quick release applications
if (type=="nut" && nut_type=="wingnut")
{
	rotate([0,0,30])hex_nut(nut_diameter,nut_height,nut_thread_step,nut_step_shape_degrees,nut_thread_outer_diameter,nut_resolution); //nut
	translate([(nut_diameter/2)+wing_radius-1,1.5,wing_radius/2+1])rotate([90,0,0])wing(); //attach wing
	mirror(1,0,0)translate([(nut_diameter/2)+wing_radius-1,1.5,wing_radius/2+1])rotate([90,0,0])wing(); //attach wing
}

module wing()
{
	difference()
	{
		cylinder(r=wing_radius,h=3,$fn=64); //cylinder
		union()
		{
			translate([-wing_radius,-wing_radius-1,-0.5])cube([wing_radius*2,wing_radius/2,wing_radius*2]); //remove overhang so flush with base of nut
			rotate([0,0,90])translate([-wing_radius,wing_radius-1,-0.5])cube([wing_radius*2,wing_radius/2,wing_radius*2]); //remove overhangs so flush with side of nut
		}
	}
}

//Washer
if (type=="washer")
{
	difference()
	{
		cylinder(r=outer_diameter/2,h=thickness,$fn=100);
		translate([0,0,-0.1])cylinder(r=inner_diameter/2,h=thickness+0.2,$fn=100);
	}
}

//Socket Head Bolt
if (type=="bolt" && head_type!="hex")
{
	socket_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,head_height,non_thread_length,non_thread_diameter);
}

module phillips_base()
{
	linear_extrude(slot_width)polygon(points=[[0,0],[(drive_diameter-slot_width)/2,9/5*(drive_diameter-slot_width)/2],[(drive_diameter+slot_width)/2,9/5*(drive_diameter-slot_width)/2],[drive_diameter,0]]);
	translate([(drive_diameter-slot_width)/2,0,(drive_diameter+slot_width)/2])rotate([0,90,0])linear_extrude(slot_width)polygon(points=[[0,0],[(drive_diameter-slot_width)/2,9/5*(drive_diameter-slot_width)/2],[(drive_diameter+slot_width)/2,9/5*(drive_diameter-slot_width)/2],[drive_diameter,0]]);
}

module phillips_fillet()
{
	union()
	{
		translate([-(drive_diameter-slot_width)/2-(slot_width/2),slot_width/2,0])rotate([90,0,0])phillips_base();
		translate([0,0,9/5*(drive_diameter-slot_width)/2])union()
		{
			inner_curve();
			rotate([0,0,90])inner_curve();
			rotate([0,0,180])inner_curve();
			rotate([0,0,270])inner_curve();
		}
	}
}

module inner_curve()
{
	translate([slot_width/2,-slot_width/2,0])rotate([0,90,0])linear_fillet(9/5*(drive_diameter-slot_width)/2,drive_diameter/10);
}

//basic 2d profile used for fillet shape
module profile(radius)
{
  difference()
  {
    square(radius);
    circle(r=radius);
  }
}

//linear fillet for use along straight edges
module linear_fillet(length,profile_radius)
{
	translate([0,-profile_radius,profile_radius])rotate([0,90,0])linear_extrude(height=length,convexity=10)profile(profile_radius);
}

module phillips_drive()
{
	intersection()
	{
		phillips_fillet();
		cylinder(9/5*(drive_diameter-slot_width)/2,drive_diameter/2+(slot_width/2),slot_width/2);
	}
}

module socket_screw(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);
	$fn=60;

	difference()
	{
    		union()
    		{
                if (head_type=="knurl")
				{
                    knurled_cyl(hg, df,
                    knurlWidth, knurlHeight, knurlDepth,
                    knurlEndSmooth, knurlSurfaceSmooth);
				}

        		if (head_type=="socket")
				{
					socket_head(hg,df);
				}

        		if (head_type=="button")
				{
					button_head(hg,df);
				}

        		if (head_type=="countersunk")
				{
					countersunk_head(hg,df);
				}

        		translate([0,0,hg])
        		if ( ntl == 0 )
        		{
          		cylinder(h=0.01, r=ntr, center=true);
        		}
        		else
        		{
            		if ( ntd == -1 )
            		{
                		cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            		}
            		else if ( ntd == 0 )
            		{
                		union()
                		{
                    		cylinder(h=ntl-st/2,r=od/2, $fn=floor(od*PI/rs), center=false);
                    		translate([0,0,ntl-st/2])
                    		cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                		}
            		}
            		else
            		{
                		cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            		}
        		}
        		translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    		}
		//create opening for specific drive type
		if (drive_type=="socket")
		{
			cylinder(r=drive_diameter/2,h=socket_depth,$fn=socket_facets); //socket
			#translate([0,0,socket_depth])cylinder(r1=drive_diameter/2,r2=0,h=drive_diameter/3,$fn=socket_facets); //socket tapers at base to allow printing without bridging and improve socket grip
		}
		else
		{
			if (drive_type=="phillips")
			{
				translate([0,0,-0.001])phillips_drive();
			}
			else //slot
			{
				translate([-(drive_diameter)/2,slot_width/2,-0.001])rotate([90,0,0])cube([drive_diameter,slot_depth,slot_width]);
			}
		}
	}
}

module knurled_cyl(chg, cod, cwd, csh, cdp, fsh, smt)
{
    cord=(cod+cdp+cdp*smt/100)/2;
    cird=cord-cdp;
    cfn=round(2*cird*PI/cwd);
    clf=360/cfn;
    crn=ceil(chg/csh);

    intersection()
    {
        shape(fsh, cird, cord-cdp*smt/100, cfn*4, chg);

        translate([0,0,-(crn*csh-chg)/2])
          knurled_finish(cord, cird, clf, csh, cfn, crn);
    }
}

module shape(hsh, ird, ord, fn4, hg)
{
        union()
        {
            cylinder(h=hsh, r1=ird, r2=ord, $fn=fn4, center=false);

            translate([0,0,hsh-0.002])
              cylinder(h=hg-2*hsh+0.004, r=ord, $fn=fn4, center=false);

            translate([0,0,hg-hsh])
              cylinder(h=hsh, r1=ord, r2=ird, $fn=fn4, center=false);
        }

}

module knurled_finish(ord, ird, lf, sh, fn, rn)
{
    for(j=[0:rn-1])
    assign(h0=sh*j, h1=sh*(j+1/2), h2=sh*(j+1))
    {
        for(i=[0:fn-1])
        assign(lf0=lf*i, lf1=lf*(i+1/2), lf2=lf*(i+1))
        {
            polyhedron(
                points=[
                     [ 0,0,h0],
                     [ ord*cos(lf0), ord*sin(lf0), h0],
                     [ ird*cos(lf1), ird*sin(lf1), h0],
                     [ ord*cos(lf2), ord*sin(lf2), h0],

                     [ ird*cos(lf0), ird*sin(lf0), h1],
                     [ ord*cos(lf1), ord*sin(lf1), h1],
                     [ ird*cos(lf2), ird*sin(lf2), h1],

                     [ 0,0,h2],
                     [ ord*cos(lf0), ord*sin(lf0), h2],
                     [ ird*cos(lf1), ird*sin(lf1), h2],
                     [ ord*cos(lf2), ord*sin(lf2), h2]
                    ],
                triangles=[
                     [0,1,2],[2,3,0],
                     [1,0,4],[4,0,7],[7,8,4],
                     [8,7,9],[10,9,7],
                     [10,7,6],[6,7,0],[3,6,0],
                     [2,1,4],[3,2,6],[10,6,9],[8,9,4],
                     [4,5,2],[2,5,6],[6,5,9],[9,5,4]
                    ],
                convexity=5);
         }
    }
}

module socket_head(hg,df)
{
	texture_points=2*PI*(head_diameter/2);
	texture_offset=head_diameter/18;
	texture_radius=head_diameter/24;

	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   	cylinder(h=hg, r=rd0, $fn=60, center=false);
		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}

	if (texture=="include") //add texture to socket head. Adjust texture density and size using texture variables above
	{
		for (i= [1:texture_points])
		{
			translate([cos(360/texture_points*i)*(head_diameter/2+texture_offset), sin(360/texture_points*i)*(head_diameter/2+texture_offset), 1 ])
			rotate([0,0,360/texture_points*i])cylinder(r=texture_radius,h=head_height*0.6,$fn=3);
		}
	}

}

module button_head(hg,df)
{
	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   	cylinder(h=hg, r1=drive_diameter/2 + 1, r2=rd0, $fn=60, center=false);
		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}
}

module countersunk_head(hg,df)
{
	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   cylinder(h=hg, r1=rd0, r2=thread_outer_diameter/2-0.5, $fn=60, center=false);

		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}
}

/* Library included below to allow customizer functionality
 *
 * polyScrewThread_r1.scad    by aubenc @ Thingiverse
 *
 * Modified by mike_mattala @ Thingiverse 1/1/2017 to remove deprecated assign
 *
 * This script contains the library modules that can be used to generate
 * threaded rods, screws and nuts.
 *
 * http://www.thingiverse.com/thing:8796
 *
 * CC Public Domain
 */

module screw_thread(od,st,lf0,lt,rs,cs)
{
    or=od/2;
    ir=or-st/2*cos(lf0)/sin(lf0);
    pf=2*PI*or;
    sn=floor(pf/rs);
    lfxy=360/sn;
    ttn=round(lt/st+1);
    zt=st/sn;

    intersection()
    {
        if (cs >= -1)
        {
           thread_shape(cs,lt,or,ir,sn,st);
        }

        full_thread(ttn,st,sn,zt,lfxy,or,ir);
    }
}

module hex_nut(df,hg,sth,clf,cod,crs)
{

    difference()
    {
        hex_head(hg,df);

        hex_countersink_ends(sth/2,cod,clf,crs,hg);

        screw_thread(cod,sth,clf,hg,crs,-2);
    }
}

module hex_screw(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);

    union()
    {
        hex_head(hg,df);

        translate([0,0,hg])
        if ( ntl == 0 )
        {
            cylinder(h=0.01, r=ntr, center=true);
        }
        else
        {
            if ( ntd == -1 )
            {
                cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            }
            else if ( ntd == 0 )
            {
                union()
                {
                    cylinder(h=ntl-st/2,
                             r=od/2, $fn=floor(od*PI/rs), center=false);

                    translate([0,0,ntl-st/2])
                    cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                }
            }
            else
            {
                cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            }
        }

        translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    }
}

module hex_screw_0(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);

    union()
    {
        hex_head_0(hg,df);

        translate([0,0,hg])
        if ( ntl == 0 )
        {
            cylinder(h=0.01, r=ntr, center=true);
        }
        else
        {
            if ( ntd == -1 )
            {
                cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            }
            else if ( ntd == 0 )
            {
                union()
                {
                    cylinder(h=ntl-st/2,
                             r=od/2, $fn=floor(od*PI/rs), center=false);

                    translate([0,0,ntl-st/2])
                    cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                }
            }
            else
            {
                cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            }
        }

        translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    }
}

module thread_shape(cs,lt,or,ir,sn,st)
{
    if ( cs == 0 )
    {
        cylinder(h=lt, r=or, $fn=sn, center=false);
    }
    else
    {
        union()
        {
            translate([0,0,st/2])
              cylinder(h=lt-st+0.005, r=or, $fn=sn, center=false);

            if ( cs == -1 || cs == 2 )
            {
                cylinder(h=st/2, r1=ir, r2=or, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }

            translate([0,0,lt-st/2])
            if ( cs == 1 || cs == 2 )
            {
                  cylinder(h=st/2, r1=or, r2=ir, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }
        }
    }
}

module full_thread(ttn,st,sn,zt,lfxy,or,ir)
{
  if(ir >= 0.2)
  {
    for(i=[0:ttn-1])
    {
        for(j=[0:sn-1])
        {
			pt = [[0,0,i*st-st],
                 [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt-st       ],
                 [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt-st   ],
				 [0,0,i*st],
                 [or*cos(j*lfxy),     or*sin(j*lfxy),     i*st+j*zt-st/2     ],
                 [or*cos((j+1)*lfxy), or*sin((j+1)*lfxy), i*st+(j+1)*zt-st/2 ],
                 [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt          ],
                 [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt      ],
                 [0,0,i*st+st]];

            polyhedron(points=pt,faces=[[1,0,3],[1,3,6],[6,3,8],[1,6,4], //changed triangles to faces (to be deprecated)
										[0,1,2],[1,4,2],[2,4,5],[5,4,6],[5,6,7],[7,6,8],
										[7,8,3],[0,2,3],[3,2,7],[7,2,5]	]);
        }
    }
  }
  else
  {
    echo("Step Degrees too agresive, the thread will not be made!!");
    echo("Try to increase de value for the degrees and/or...");
    echo(" decrease the pitch value and/or...");
    echo(" increase the outer diameter value.");
  }
}

module hex_head(hg,df)
{
	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   cylinder(h=hg, r=rd0, $fn=facets, center=false);

		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}
}

module hex_head_0(hg,df)
{
    cylinder(h=hg, r=df/2/sin(60), $fn=6, center=false);
}

module hex_countersink_ends(chg,cod,clf,crs,hg)
{
    translate([0,0,-0.1])
    cylinder(h=chg+0.01,
             r1=cod/2,
             r2=cod/2-(chg+0.1)*cos(clf)/sin(clf),
             $fn=floor(cod*PI/crs), center=false);

    translate([0,0,hg-chg+0.1])
    cylinder(h=chg+0.01,
             r1=cod/2-(chg+0.1)*cos(clf)/sin(clf),
             r2=cod/2,
             $fn=floor(cod*PI/crs), center=false);
}
```


[User]
Hey, I'm looking to get some OpenSCAD code generated for a bolt hole pattern template. I've got some specific needs and I'd like it to be parametric so I can adjust things easily later.

Basically, I need a template with four bolt holes arranged in a rectangle. I need to be able to define:

*   The diameter of the bolt holes.
*   The on-center width between the two bolt holes along the short side.
*   The on-center length between the two bolt holes along the long side.

Then, I want to add these features, all as options:

*   A border around the whole template, so it's not just the holes right at the edge.
*   The template thickness.
*   Optional clamp tabs, two total: one on a long side and one on a short side
*  An optional edge guide. This is an offset fence to help line the template up. I want to be able to specify:
    *   Whether the guide is on the short or long side.
    *   The distance from the center of the bolt holes to the edge of the guide.
    *  The height of the edge guide.

I'm thinking something pretty simple in terms of the geometry of the template, basically just a rectangle with holes and the edge guide if it's enabled, plus the optional clamp tabs. I would like it to be all within one .scad file; no need to separate it into more files than that. I'm hoping the code can use the `BOSL2` library for ease of use. It would be great if the code was well-commented to make it easier for me to understand and modify later! I'm also not too good with coding, so if it could be intuitive, that'd be a huge help. Let me know if you have any other questions or if something is unclear. Thanks!

[Assistant]
BoltHolePattern.scad:
```
//Bolt Hole Pattern
//by Sithrazer
//https://www.thingiverse.com/Sithrazer
//https://github.com/Sithrazer/3DPrintingProjects
//License: CC BY-NC-SA
//https://creativecommons.org/licenses/by-nc-sa/4.0/

/*[ALL DIMENSIONS IN MILLIMETERS]*/
//1 inch equals 25.4 mm
one_inch=25.4;
/*[Bolt Pattern]*/
//Hole Diameter
diameter=9.5; // 3/8in
//Width On-Center
width=44.5; // ~1 3/4in
//Length On-Center
length=74.6; // ~2 15/16in

/*[Template Features]*/
//Border Around Features
border=3; //[2:8]
//Template Thickness
thick=2; //[2:20]
//Clamp Tabs
tabs=0;//[0:False,1:True]

/*[Edge Guide]*/
//Edge Guide
guide=0;//[0:False,1:True]
//Edge Guide Side
guide_side=0;//[0:Short Side,1:Long Side]
//Edge Guide Distance On-Center
guide_dist=10;
//Edge Guide Height
guide_height=10; //[2:20]

//Useful calculations
//total length including all features
t_length=length+diameter+border*2;
//total width including all features
t_width=width+diameter+border*2;
radius=diameter/2;

//Assemble Template Basics
make_plate(thick, border, t_width, t_length);
translate([length/2,width/2,0])
    make_hole(thick, border, radius);
translate([-(length/2),width/2,0])
    make_hole(thick, border, radius);
translate([length/2,-(width/2),0])
    make_hole(thick, border, radius);
translate([-(length/2),-(width/2),0])
    make_hole(thick, border, radius);

//Assemble Clamp Tabs
if (tabs==1){
    //long side tab
    translate([0,-(t_width/8)+t_width/2,0])
    cube([length/2, t_width/4,thick],center=true);
    //short side tab
    translate([-(t_length/8)+t_length/2,0])
    cube([t_length/4, width/2,thick],center=true);
}

//Assemble Edge Guides
if((guide==1)&&(guide_side==0)){
    make_guide(thick, border, width-diameter, guide_dist, guide_height, length);
}
if((guide==1)&&(guide_side==1)){
    rotate([0,0,90])
    make_guide(thick, border, length-diameter, guide_dist, guide_height, width);
}

module make_plate(thick, border, width, length){
    difference(){
        cube([length, width, thick],center=true);
        cube([length-border*2, width-border*2, thick+1],center=true);
    }
}

module make_hole(thick, border, radius){
    difference(){
        cylinder(thick,r=radius+border,center=true);
        cylinder(thick+1,r=radius,center=true);
    }
}

module make_guide(thick, border, width, guide_dist, guide_height, to_center){
    translate([to_center/2,0,-(thick/2)])
    union(){
        //Assemble stop
        translate([guide_dist,-(width/2),0])
        cube([border,width,guide_height+thick],center=false);
        //Assemble bracket
        translate([0,-(width/2),0]){
            difference(){
                cube([guide_dist,width,thick]);
                translate([border,border,-1])
                cube([guide_dist-border*2,width-border*2,thick+2]);
            }
        }
    }
}

```


[User]
I need an OpenSCAD file, or maybe a few files if that's better, to generate a bed level test print. It should be customizable with the following parameters:

*   `bedX` and `bedY`:  These define the dimensions of the print bed.
*   `squareSize`: The size of each individual test square.
*   `spacing`: The distance between squares and also between the squares and the edges of the bed.
*   `nbSquaresX` and `nbSquaresY`: The number of squares along the X and Y axes. The code should automatically reduce this number if the squares plus the spacing would exceed the bed size.
*   `contour`: A boolean to control whether or not to print a contour/skirt around the squares.
*   `contourOffset`: The distance between the edge of the bed and the contour/skirt, if the contour is enabled.
*   `height`: The height of the squares and the contour.
*  `width`: The thickness of the contour.

The file(s) should create a grid of squares on the bed. If `contour` is true, then a rectangular contour should be printed around the grid of squares. If the requested number of squares with the specified spacing will not fit on the bed, the script should automatically reduce the number of squares to the maximum that will fit. The squares, and the contour if active, should be centered on the bed.

I'm not too familiar with OpenSCAD, so it would be great if the code is easy to understand and well commented. I don't need any external libraries or files beyond the base OpenSCAD functionality.

[Assistant]
level_test.scad:
```
bedX = 235;
bedY = 235;
squareSize = 25;
spacing = 10;
nbSquaresX = 3;
nbSquaresY = 3;
contour = true;
contourOffset = 7;
height = 0.2;
width = 0.4;

if(contour)
{
    difference() {
        cube([bedX-2*contourOffset,bedY-2*contourOffset,height], center = true);
        cube([bedX-2*contourOffset-2*width,bedY-2*contourOffset-2*width,height+0.1], center = true);
    }
}

if( bedX-((2+nbSquaresX-1)*spacing)-(nbSquaresX*squareSize) < 0)
{
    nbSquaresX = floor((bedX-spacing)/(squareSize+spacing));
    if( bedY-((2+nbSquaresY-1)*spacing)-(nbSquaresY*squareSize) < 0)
    {
        nbSquaresY = floor((bedY-spacing)/(squareSize+spacing));
        for(iX = [(-bedX/2)+spacing+(squareSize/2) : (bedX-squareSize-2*spacing)/(nbSquaresX-1) : (bedX/2)-spacing-(squareSize/2)])
        {
            for(iY = [(-bedY/2)+spacing+(squareSize/2) : (bedY-squareSize-2*spacing)/(nbSquaresY-1) : (bedY/2)-spacing-(squareSize/2)])
            translate([iX,iY,0])  cube([squareSize,squareSize,height], center = true);
        }
    }
    else
    {
        for(iX = [(-bedX/2)+spacing+(squareSize/2) : (bedX-squareSize-2*spacing)/(nbSquaresX-1) : (bedX/2)-spacing-(squareSize/2)])
        {
            for(iY = [(-bedY/2)+spacing+(squareSize/2) : (bedY-squareSize-2*spacing)/(nbSquaresY-1) : (bedY/2)-spacing-(squareSize/2)])
            translate([iX,iY,0])  cube([squareSize,squareSize,height], center = true);
        }
    }

}
else if( bedY-((2+nbSquaresY-1)*spacing)-(nbSquaresY*squareSize) < 0)
{
    nbSquaresY = floor((bedY-spacing)/(squareSize+spacing));
    if( bedX-((2+nbSquaresX-1)*spacing)-(nbSquaresX*squareSize) < 0)
    {
        nbSquaresX = floor((bedX-spacing)/(squareSize+spacing));
        for(iX = [(-bedX/2)+spacing+(squareSize/2) : (bedX-squareSize-2*spacing)/(nbSquaresX-1) : (bedX/2)-spacing-(squareSize/2)])
        {
            for(iY = [(-bedY/2)+spacing+(squareSize/2) : (bedY-squareSize-2*spacing)/(nbSquaresY-1) : (bedY/2)-spacing-(squareSize/2)])
    translate([iX,iY,0])  cube([squareSize,squareSize,height], center = true);
        }
    }
    else
    {
        for(iX = [(-bedX/2)+spacing+(squareSize/2) : (bedX-squareSize-2*spacing)/(nbSquaresX-1) : (bedX/2)-spacing-(squareSize/2)])
        {
            for(iY = [(-bedY/2)+spacing+(squareSize/2) : (bedY-squareSize-2*spacing)/(nbSquaresY-1) : (bedY/2)-spacing-(squareSize/2)])
            translate([iX,iY,0])  cube([squareSize,squareSize,height], center = true);
        }
    }
}
else
{
    for(iX = [(-bedX/2)+spacing+(squareSize/2) : (bedX-squareSize-2*spacing)/(nbSquaresX-1) : (bedX/2)-spacing-(squareSize/2)])
    {
        for(iY = [(-bedY/2)+spacing+(squareSize/2) : (bedY-squareSize-2*spacing)/(nbSquaresY-1) : (bedY/2)-spacing-(squareSize/2)])
        translate([iX,iY,0])  cube([squareSize,squareSize,height], center = true);
    }
}
```


[User]
I'm looking to create a highly customizable Arca-Swiss compatible quick release plate. I'd like a parametric design in OpenSCAD.

Here are the parameters I need to be able to control:

*   **Plate Length:** The length of the plate, adjustable in millimeters (e.g., from 30mm up to 300mm).
*   **Top Grid:** A boolean to toggle a grid pattern on the top surface of the plate.
*   **Grid Resolution:** If the top grid is enabled, this would control how many grid lines are present per mm, maybe around 5 lines per mm.
*   **Rounded Corners:**  A boolean to toggle if the corners of the plate should be rounded.
*   **End Stops:** Boolean for whether there should be end stops on the plate.

For mounting, I need the following options:

*   **Mounting Hole Diameter:** Choose between 1/4" or 3/8" screw mounting holes.
*   **Mounting Design:** This should be a dropdown with the following options:
    *   Single Slit
    *   Double Slit
    *   Double Slit Mixed (one 1/4" and one 3/8" slit)
    *   Center Hole
    *   Center Hole with Double Slits
    *   Cheese Plate (a pattern of mounting holes)
    *   Vertical Slits
    *   Custom Example (which includes a mix of different features)

I also would like a detail setting that controls `$fn`. I think `$fn=60` would work fine for my needs.

Could you generate the necessary OpenSCAD code that lets me specify all of the parameters listed above? I don't think I need any specific external libraries for this.

[Assistant]
parametric_arca_swiss_plate.scad:
```
/*[ Plate Parameter ]*/

//length of the plate in mm (38mm for a square plate)
length = 100;//[30:300]

//should there be an grid pattern on top?
top_grid = false;

//how many grid lines per mm?
grid_resolution = 5;

//should there be round corners?
round_corners = true;

//should there be end stops?
end_stops = true;

/*[ Mounting Parameter ]*/
//what screws do you want to mount on the plate: 1/4" or 3/8"?
hole_diameter = 1;//[1:1/4,2:3/8]

//select the mounting design:
hole_design = 5; // [1:Single slit, 2:Double slit, 3:Double slit mixed,4: Center hole, 5: Center hole double slit, 6: Cheese plate, 7: Vertical Slits, 8: Custom example]

/*[ Render Settings ]*/
//detail
$fn = 60;

//------------------ NOTES ------------------
//if you want a really simple plate for other projects use simpleplate(L);
//simpleplate(50);

//for custom mounting designs just place your own slit and hole functions

//render:
plate();

//------------------ Modules ------------------
module plate(){
    difference(){
        union(){
            if(round_corners){
                minkowski(){
                    translate([0,-38/2,0])cube([length-10,38-10,5]);
                    translate([5,5,0])cylinder(5,d=10);
                }
            } else {
                translate([0,-38/2,0])cube([length,38,10]);
            }
            if(end_stops){
                translate([3.25,0,-1])cylinder(6,d=5.5);
                translate([length-3.25,0,-1])cylinder(6,d=5.5);
            }
        }
        translate([6,-38/2+5,-1.01])cube([length-12,38-10,6.5]);

        translate([-.01,32/2,5])rotate([-45,0,0])cube([length+.02,10,10]);
        translate([-.01,-32/2,5])rotate([135,0,0])cube([length+.02,10,10]);

        if(top_grid){
            translate([length/2,0,10]){
                rotate([0,0,45])grid_lines();
                rotate([0,0,-45])grid_lines();
            }
        }
        mounting_holes();
}}

module grid_lines(){
    for (i = [0:grid_resolution:(length+38)*0.35]) {
        translate([0,i,0])rotate([45,0,0])cube([length*1.8,0.6,0.6],true);
        translate([0,-i,0])rotate([45,0,0])cube([length*1.8,0.6,0.6],true);
    }
}

module mounting_holes(){
    if(hole_design == 1){ //single slit
        translate([15,0,0])slit(length-15*2);
    } else if (hole_design == 2){ //double slit
        if(length >= 45){
            translate([length-15,0,0])rotate([0,0,180])slit(length/2-15*1.5);
            translate([15,0,0])slit(length/2-15*1.5);
        } else {
            echo("for this design we need at least a length of 45mm");
            translate([15,0,0])slit(length-15*2);
        }
    } else if (hole_design == 3){ //double slit mixed
        if(length >= 45){
            translate([length-15,0,0])rotate([0,0,180])slit_1_4(length/2-15*1.5);
            translate([15,0,0])slit_3_8(length/2-15*1.5);
        } else {
            echo("for this design we need at least a length of 45mm");
            translate([15,0,0])slit(length-15*2);
        }
    } else if (hole_design == 4){ // center hole
        translate([length/2,0,0])slit(0);
    } else if (hole_design == 5){ // center hole double slit
        if(length >= 53){
            translate([length/2,0,0])slit(0);
            translate([length-15,0,0])rotate([0,0,180])slit(length/2-15*1.75);
            translate([15,0,0])slit(length/2-15*1.75);
        } else {
            echo("for this design we need at least a length of 53mm");
            translate([15,0,0])slit(length-15*2);
        }
    } else if (hole_design == 6){ //cheese plate
        if (hole_diameter == 1){
            cheese(7);
        } else {
            cheese(11);
        }
    } else if (hole_design == 7){ //vertical slits
        if (hole_diameter == 1){
            for(i = [0:10:length/2-15-((length/2-15)%10)]) {
                translate([length/2+i,0,0])vertical_slit();
                translate([length/2-i,0,0])vertical_slit();
            }
        } else {
            for(i = [0:12:length/2-15-((length/2-15)%12)]) {
                translate([length/2+i,0,0])vertical_slit();
                translate([length/2-i,0,0])vertical_slit();
            }
        }
    } else if (hole_design == 8){ //custom example
        if(length >= 82){
            custom_example();
        } else {
            echo("for this design we need at least a length of 82mm");
        }
    }
}

module slit(L){
    if (hole_diameter == 1){
        slit_1_4(L);
    } else {
        slit_3_8(L);
    }
}

module vertical_slit(){
    if (hole_diameter == 1){
        vertical_slit_1_4();
    } else {
        vertical_slit_3_8();
    }
}

module slit_1_4(L){
    translate([0,-5/2,0])cube([L,5,12]);
    translate([0,0,0])cylinder(12,d = 6);
    translate([L,0,0])cylinder(12,d = 5);

    translate([0,-7/2,9])cube([L,7,2]);
    translate([0,0,9])cylinder(2,d = 7);
    translate([L,0,9])cylinder(2,d = 7);
}

module slit_3_8(L){
    translate([0,-6/2,0])cube([L,6,12]);
    translate([0,0,0])cylinder(12,d = 9);
    translate([L,0,0])cylinder(12,d = 6);

    translate([0,-10.5/2,9])cube([L,10.5,2]);
    translate([0,0,9])cylinder(2,d = 10.5);
    translate([L,0,9])cylinder(2,d = 10.5);
}

module vertical_slit_1_4(){
    rotate([0,0,90])translate([-8/2,0,0])slit_1_4(8);
}

module vertical_slit_3_8(){
    rotate([0,0,90])translate([-8/2,0,0])slit_3_8(8);
}

module cheese(S){
    for(i = [0:S:length/2-15-((length/2-15)%S)]) {
        if (i%2 == 0){
            translate([length/2+i,0,0])slit(0);
            translate([length/2-i,0,0])slit(0);
        } else {
            translate([length/2+i,-5.5,0])slit(0);
            translate([length/2+i,5.5,0])slit(0);

            translate([length/2-i,-5.5,0])slit(0);
            translate([length/2-i,5.5,0])slit(0);
        }
    }
}

module custom_example(){
        //Places a 3/8" hole in the middle of the plate by making a 0mm long slit
        translate([length/2,0,0])slit_3_8(0);

        //Places a 1/4" slit 15mm to the top of the middle with a length reaching to 15mm before the end of the plate
        translate([length/2+15,0,0])slit_1_4(length/2-15*2);

        //Places two 1/4" vertical slits 15 and 25mm under the middle
        translate([length/2-15,0,0])vertical_slit_1_4();
        translate([length/2-25,0,0])rotate([0,0,180])vertical_slit_1_4();

}

module simpleplate(L){
    difference(){
        translate([0,-38/2,0])cube([L,38,10]);
        translate([-.01,32/2,5])rotate([-45,0,0])cube([L+.02,10,10]);
        translate([-.01,-32/2,5])rotate([135,0,0])cube([L+.02,10,10]);
    }
}

```


[User]
Hey, I'm looking to generate a parametric Arca Swiss plate, but I need some specific features and flexibility.

I want a rectangular plate with Arca Swiss dovetails on all four sides. The base plate should be 38mm wide, and I want to be able to control the length as a parameter.  I'd also like to be able to specify a few mounting options in the center of the plate - either a single slit or a center hole. I also need to specify the screw size for the mounting holes, either 1/4" or 3/8".

I'd like a few optional features as well: rounded corners for a smoother look, end stops on the rail for preventing sliding, and a grid pattern on the top surface. The grid should be defined by a grid resolution, meaning grid lines per millimeter.

Can you generate the OpenSCAD code for this? I do not need the code to have any external dependencies or external libraries beyond what comes standard with openscad. I need the full code for all required files. I'd like to specify the following:

*   **Plate Dimensions:**
    *   `plate_width` = 38mm (fixed)
    *   `plate_length`: User-defined parameter
    *   `plate_thickness`: User-defined parameter (default to 10mm)

*   **Arca Swiss Dovetail:**
    *   Standard 45-degree dovetails on all four sides. The dovetail dimensions should be consistent across all edges.

*   **Mounting:**
    *   `hole_diameter`: User-selectable option: 1 for 1/4", 2 for 3/8"
    *   `hole_design`: User-selectable option: 1 for single slit, 2 for center hole
    *   Centered mounting hole/slit based on the chosen design

*   **Optional Features:**
    *   `round_corners`: Boolean (default to `false`)
    *   `end_stops`: Boolean (default to `false`)
    *   `top_grid`: Boolean (default to `false`)
    *  `grid_resolution`: User defined float (default to 5) - grid lines per mm.

I'd like the user to be able to specify all of these parameters and I would like the OpenSCAD file to default to the given default values.

Could you handle that? Thanks!

[Assistant]
parametric_arca_swiss_plate_fourway.scad:
```
/*[ Plate Parameter ]*/

//should there be an grid pattern on top?
top_grid = false;

//how many grid lines per mm?
grid_resolution = 5;

//should there be round corners?
round_corners = true;

//should there be end stops?
end_stops = false;

/*[ Mounting Parameter ]*/
//what screws do you want to mount on the plate: 1/4" or 3/8"?
hole_diameter = 1;//[1:1/4,2:3/8]

//select the mounting design:
hole_design = 2; // [1:Single slit, 2:Center hole]

/*[ Render Settings ]*/
//detail
$fn = 60;

//------------------ NOTES ------------------
//if you want a really simple plate for other projects use simpleplate();
//simpleplate();

//render:
plate();

//------------------ Modules ------------------
module plate(){
    difference(){
        union(){
            if(round_corners){
                minkowski(){
                    translate([0,-38/2,0])cube([38-10,38-10,5]);
                    translate([5,5,0])cylinder(5,d=10);
                }
            } else {
                translate([0,-38/2,0])cube([38,38,10]);
            }
            if(end_stops){
                translate([3.25,0,-1])cylinder(6,d=5.5);
                translate([38-3.25,0,-1])cylinder(6,d=5.5);
            }
        }
        translate([6,-38/2+5,-1.01])cube([38-12,38-10,6.5]);

        translate([-.01,32/2,5])rotate([-45,0,0])cube([38+.02,10,10]);
        translate([-.01,-32/2,5])rotate([135,0,0])cube([38+.02,10,10]);
        translate([3,-38/2,5])rotate([-45,0,90])cube([38+.02,10,10]);
        translate([35,38/2,5])rotate([-45,0,-90])cube([38+.02,10,10]);
        if(top_grid){
            translate([38/2,0,10]){
                rotate([0,0,45])grid_lines();
                rotate([0,0,-45])grid_lines();
            }
        }
        mounting_holes();
}}

module grid_lines(){
    for (i = [0:grid_resolution:(38+38)*0.35]) {
        translate([0,i,0])rotate([45,0,0])cube([38*1.8,0.6,0.6],true);
        translate([0,-i,0])rotate([45,0,0])cube([38*1.8,0.6,0.6],true);
    }
}

module mounting_holes(){
    if(hole_design == 1){ //single slit
        translate([19,0,0]) vertical_slit();
    } else if (hole_design == 2){ //center hole
        translate([19,0,0]) slit(0);
    }
}

module slit(L){
    if (hole_diameter == 1){
        slit_1_4(L);
    } else {
        slit_3_8(L);
    }
}

module vertical_slit(){
    if (hole_diameter == 1){
        vertical_slit_1_4();
    } else {
        vertical_slit_3_8();
    }
}

module slit_1_4(L){
    translate([0,-5/2,0])cube([L,5,12]);
    translate([0,0,0])cylinder(12,d = 6);
    translate([L,0,0])cylinder(12,d = 5);

    translate([0,-7/2,9])cube([L,7,2]);
    translate([0,0,9])cylinder(2,d = 7);
    translate([L,0,9])cylinder(2,d = 7);
}

module slit_3_8(L){
    translate([0,-6/2,0])cube([L,6,12]);
    translate([0,0,0])cylinder(12,d = 9);
    translate([L,0,0])cylinder(12,d = 6);

    translate([0,-10.5/2,9])cube([L,10.5,2]);
    translate([0,0,9])cylinder(2,d = 10.5);
    translate([L,0,9])cylinder(2,d = 10.5);
}

module vertical_slit_1_4(){
    rotate([0,0,90])translate([-8/2,0,0])slit_1_4(8);
}

module vertical_slit_3_8(){
    rotate([0,0,90])translate([-8/2,0,0])slit_3_8(8);
}

module simpleplate(){
    difference(){
        translate([0,-38/2,0])cube([38,38,10]);
        translate([-.01,32/2,5])rotate([-45,0,0])cube([38+.02,10,10]);
        translate([-.01,-32/2,5])rotate([135,0,0])cube([38+.02,10,10]);
        translate([3,-38/2,5])rotate([-45,0,90])cube([38+.02,10,10]);
        translate([35,38/2,5])rotate([-45,0,-90])cube([38+.02,10,10]);
    }
}

```


[User]
Hey, I'm looking to design an enclosure for my ESP8266 Wemos D1 Mini board, and I'd like it to have some built-in support for Dupont connectors. I saw a design online that was pretty close to what I wanted, but I'd like to make some adjustments, and start from scratch in case some of the modeling was off.

Here's what I need:

*   **Enclosure Type**: It should be a two-part enclosure, a base and a lid/top that snap together or are held with friction/tight tolerances. I like the idea that the base has a slightly higher wall, and the top lid sort of caps it, but we can explore other ways of having the top lid fit.

*   **ESP8266 Mounting**: The base needs to have a cutout that fits the Wemos D1 Mini snugly, with a little bit of tolerance for printing. The board should sit flush with the top of the base.

*   **Dupont Connector Support:** This is key, I want holders that support the Dupont connectors when plugged into the board's pins. I'd like the support to be part of the base enclosure, so they provide extra stability and minimize the risk of them coming loose. I think the design I saw used a set of rectangular extrusions that sit flush with the connector, that would be ideal. The connectors are in the standard row spacing of the Wemos D1 mini board, which is 2.54 mm.

*   **USB Opening:** The top/lid needs to have a rectangular opening for the USB port on the Wemos D1 Mini, as well as all the pin headers on both sides of the board.

*   **Parametric Design**: I would like all the critical dimensions, such as the overall size of the case, the thickness of the walls, the dimensions for the ESP8266 cutout, and the size of the Dupont connector holders to be adjustable via variables. I want to be able to tweak things if needed.

*   **Tolerance Variable**: I'd also like to have a tolerance variable so that I can easily adjust the clearance between the base and the lid, to have a tight fit.

*   **Rounded corners:** A nice touch would be rounded corners or chamfered edges on the case, but this isn't an essential requirement.

I'd like this to be written in OpenSCAD, and I would appreciate it if you could use comments to explain the different parts of the code. It doesn't really matter to me if the code is in one file or multiple files, as long as it is easy to understand and easy to edit. I don't need any particular libraries for the design.

Thanks in advance for your help with this!

[Assistant]
ESP8266_Wemos_D1_Mini_Enclosure_with_Dupont_Holders_-_Version_4.scad:
```
// ESP8266 Wemos D1 Mini Enclosure with Dupont Holders - Version 5
// ===============================================================
//
// Date   : 23-06-2020
// Version: 4
// License: Creative Commons - BY NC SA
// Author : Erikjvl
// Shared : https://www.thingiverse.com/prototype-improve-share

/*[ESP8266 Wemos D1 Mini Enclosure with Dupont Holders]*/
//3D printing tolerance.
tolerance = 0.2; //[0.1:0.05:0.5]

//Show part(s)
part = 0; //[0:Full assembly on print bed,1:Base (holder),2:Lid/Top,-1:Show assembled assembly,-2:Cross section]
//Enf of customizer

//Constants
BPP = 25.4/10; // Breadboard pin pitch distance is 1/10 inch, in mm.
$fn = $preview ? 36 : 90;

// Make ESP8266 Wemos D1 Mini Box with Dupont Holder
// The program
if (part == -2 && $preview) {
    // Show opened assembled assembly.
    difference(){
        union(){
            base();
            top();
        }
        color("Blue") translate([16,7,-5]) rotate([0,0,45]) cube([100,100,100]);
    }
} else if (part == -1 && $preview) {
    // Show assembled assembly.
    base();
    top();
} else if (part == 0) {
    // Translate and rotate are for right print bed location only.
    translate([0, 0.3 + tolerance/2, 0]) base();
    translate([0, -5, 15.75+tolerance])
        rotate([180,0,0])
            top();
} else if (part == 1) {
     base();
} else if (part == 2) {
    // Show the cover top (rotated for optimal 3D printing).
     rotate([180,0,0]) top();
}
//End of program

// ------------------------------------------------
// Modules
// ------------------------------------------------

module base(){
    mirror([0,0,0]) halfbase();
    mirror([1,0,0]) halfbase();
}

module halfbase(){
    difference(){
        union(){
            halfboxshap(h=3, margin=tolerance);
            translate([(3.5*BPP)-tolerance,(2.5*BPP)-tolerance,0]) cube([
               2*BPP + 2*tolerance,
               9*BPP + 2*tolerance,
               10]);
        }
        translate([4*BPP-tolerance,3*BPP-tolerance,-0.5]) cube([
           BPP + 2*tolerance,
           8*BPP + 2*tolerance,
           11]);
        translate([6.5*BPP+tolerance, 4*BPP,5]) sphere($fn=36,  d=6.5);
        translate([6.5*BPP+tolerance,10*BPP,5]) sphere($fn=36,  d=6.5);
        translate([3.5*BPP-0.61,2*BPP+tolerance,10-BPP+0.01]) cube([1.5*BPP, 1.5*BPP, BPP]);
    }
}

module halfbasecut(h){
    difference(){
        halfboxshap(h=h, margin=2*tolerance);
        translate([6.5*BPP+tolerance,4*BPP,5]) sphere(d=6.5-tolerance);
        translate([6.5*BPP+tolerance,10*BPP,5]) sphere(d=6.5-tolerance);
    }
}

module halfboxshap(h=3, margin=0) {
    hull(){
        translate([0,-margin,0]) cube([1, 14*BPP + 2*margin, h]);
        translate([5.5*BPP-1, 1,0]) cylinder(h=h, r=1+margin);
        translate([5.5*BPP-1, 13*BPP-1,0]) cylinder(h=h, r=1+margin);
        translate([4.5*BPP-1, 14*BPP-1,0]) cylinder(h=h, r=1+margin);
    }
}

module top(){
    mirror([0,0,0]) halftop();
    mirror([1,0,0]) halftop();
}

module halftop(){
    difference(){
        halfboxshap(h=15.75+tolerance, margin=1.2+2*tolerance);
        translate([0,0,-0.01]) halfbasecut(h=14.75+tolerance);

        //USB connector opening
        translate([0,-8,7.2]) cube([3.9,20,3.2]);
    }
}

// End of modules
// End of file
```


[User]
I'm looking to design a parametric battery holder for cylindrical cells, and I want it to be modular so I can easily combine multiple units. I have some specific requirements:

First, the holder needs to be customizable for different cell sizes. I'd like to define the cell diameter as a parameter, along with the number of rows and columns of cells in a single holder unit. I'll also need to set the height of the holder, and the spacing between the cells. For example, I might want a 4x4 holder for 20.42mm cells.

Second, I want to include dove tail joints on the sides of the holder units so that I can connect them to other units. These joints should have adjustable parameters for things like the dovetail max and min width, the depth of the dovetail, and the width and depth of the cutout.

Third, I'd like to have some chamfering at the bottom of the holder to deal with elephant foot issues when 3D printing, and I also need to include holes at the intersection points between cells, as well as along the edges of each holder, for fastening. The holes themselves should have a definable diameter. I will want to be able to disable both the chamfer and dove tails, depending on the needs of each print.

Finally, the design should have some additional thickness on the outer walls to allow for the dove tails to be formed. This needs to be handled programmatically rather than by manually adjusting the dimensions.

Could you generate the OpenSCAD code for this? I think I'd like to avoid using external libraries if possible.

[Assistant]
pack.scad:
```
// Cell Diameter. Common settings: 20.4 for 20700; 18.4 for 18650
cell = 20.42;
// Number of Rows
rows = 4; // [0:10]
// Number of Columns
cols = 4; // [0:10]
// Holder Height
height = 9;
// Space betwen Cells
space = 2;
// generate dove tails
dove = true;
// anti elephant foot champer
champer = true;
// connecting hole diameter
hole = 4.6;

// hidden
// additional thickness to outermost walls to create space for dove tails
wall_add = 1;
cubeSize = cell+space;
clipFix = 0.2;

// number of faces in circle
$fn = 48;

// about dovetails
dove_max_width = 5.1;
dove_min_width = 3.4;
dove_depth = 2;
dove_cutout_width = 3;
dove_cutout_depth = 0.6;

// rotate to the best print orientation and translate for presentation
translate([0, (cols+1)*cubeSize, height/2])
rotate([180,0,0])
// actual creation of holder
{
    difference()
    {
        translate([(rows+1)*cubeSize/2, (cols+1)*cubeSize/2, 0])
        cube(size = [cubeSize*rows+space+wall_add*2, cubeSize*cols+space+wall_add*2, height], center = true);

        if (champer) {
            // 45 degree champer to make sure elephant foot won't interfere when putting multiple pieces together
            // bottom champer
            translate([(rows+1)*cubeSize/2, (cols+1)*cubeSize/2 - cols/2*cubeSize - sqrt(pow(height/2, 2)+pow(height/2, 2)), 0])
            rotate([-45,0,0])
            cube(size = [cubeSize*rows+space+wall_add*2, space, height*3], center = true);
            // top champer
            translate([(rows+1)*cubeSize/2, (cols+1)*cubeSize/2 + cols/2*cubeSize + sqrt(pow(height/2, 2)+pow(height/2, 2)), 0])
            rotate([45,0,0])
            cube(size = [cubeSize*rows+space+wall_add*2, space, height*3], center = true);
            // left champer
            translate([(rows+1)*cubeSize/2 - rows/2*cubeSize - sqrt(pow(height/2, 2)+pow(height/2, 2)), (cols+1)*cubeSize/2, 0])
        rotate([0,45,0])
        cube(size = [space, cubeSize*cols+space+wall_add*2, height*3], center = true);
            // right champer
            translate([(rows+1)*cubeSize/2 + rows/2*cubeSize + sqrt(pow(height/2, 2)+pow(height/2, 2)), (cols+1)*cubeSize/2, 0])
        rotate([0,-45,0])
        cube(size = [space, cubeSize*cols+space+wall_add*2, height*3], center = true);
        }

        for ( i = [1 : cols] )
        {
            translate([0,i*cubeSize,0])
            for ( ii = [1 : rows] )
            {
                translate([ii*cubeSize,0,0])

                union()
                {
                    translate([0,0,height/2]) cube(size = [cell/2,cubeSize+3+wall_add*2,4], center = true);
                    translate([0,0,height/2]) cube(size = [cubeSize+3+wall_add*2,cell/2,4], center = true);
                    translate([0,0,-height/2-1.9]) cylinder(h = height, r=cell/2);
                }
            }
        }

        if (rows > 1 && cols > 1) {
            // holes at intersections
            for ( i = [1 : cols-1] )
            {
                translate([0,i*cubeSize,0])
                for ( ii = [1 : rows-1] )
                {
                    translate([ii*cubeSize,0,0])
                        translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cylinder(h = height+clipFix, d=hole);
                }
            }
        }

        if (rows > 1) {
            // holes at outmost walls (for connecting multiple pieces together)
            // bottom side
            translate([0,space,0])
            for ( ii = [1 : rows-1] )
            {
                translate([ii*cubeSize,0,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cylinder(h = height+clipFix, d=hole);
                translate([ii*cubeSize,-hole/2-wall_add/2,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole, hole+wall_add, hole], center=true);
                translate([ii*cubeSize,-hole/2-wall_add/2,height])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole, hole+wall_add, hole], center=true);
            }
            // top side
            translate([0,cols*cubeSize-space,0])
            for ( ii = [1 : rows-1] )
            {
                translate([ii*cubeSize,0,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cylinder(h = height+clipFix, d=hole);
                translate([ii*cubeSize,hole/2+wall_add/2,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole, hole+wall_add, hole], center=true);
                translate([ii*cubeSize,hole/2+wall_add/2,height])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole, hole+wall_add, hole], center=true);
            }
        }
        if (cols > 1) {
            // left side
            for ( ii = [1 : cols-1] )
            {
                translate([space,ii*cubeSize,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cylinder(h = height+clipFix, d=hole);
                translate([space-hole/2-wall_add/2,ii*cubeSize,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole+wall_add, hole, hole], center=true);
                translate([space-hole/2-wall_add/2,ii*cubeSize,height])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole+wall_add, hole, hole], center=true);
            }
            // right side
            for ( ii = [1 : cols-1] )
            {
                translate([rows*cubeSize-space,ii*cubeSize,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cylinder(h = height+clipFix, d=hole);
                translate([rows*cubeSize-space+hole/2+wall_add/2,ii*cubeSize,0])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole+wall_add, hole, hole], center=true);
                translate([rows*cubeSize-space+hole/2+wall_add/2,ii*cubeSize,height])
                    translate([cell/2+space/2,cell/2+space/2,-height/2-clipFix/2]) cube([hole+wall_add, hole, hole], center=true);
            }
        }

        // female dovetails
        if (dove) {
            for ( ii = [1 : rows] )
            {
                translate([0,cols*cubeSize+wall_add*2+space,0])
                translate([ii*cubeSize-cubeSize/4,cell/2-wall_add,0])
                rotate([0, 0, 180])
                translate([0,0,-height/2])
                female_dovetail_negative(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height, clearance=0.3);
                //
                translate([0,cols*cubeSize+wall_add*2+space,0])
                translate([ii*cubeSize+cubeSize/4,cell/2-wall_add,0])
                rotate([0, 0, 180])
                translate([0,0,-height/2])
                female_dovetail_negative(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height, clearance=0.3);
            }
            for ( ii = [1 : cols] )
            {
                translate([rows*cubeSize+wall_add*2+space,0,0])
                translate([cell/2-wall_add,ii*cubeSize-cubeSize/4,0])
                rotate([0, 0, 90])
                translate([0,0,-height/2])
                female_dovetail_negative(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height, clearance=0.3);
                //
                translate([rows*cubeSize+wall_add*2+space,0,0])
                translate([cell/2-wall_add,ii*cubeSize+cubeSize/4,0])
                rotate([0, 0, 90])
                translate([0,0,-height/2])
                female_dovetail_negative(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height, clearance=0.3);
            }
        }
    }

    // male dovetails
    if (dove) {
        for ( ii = [1 : rows] )
        {
            translate([ii*cubeSize+cubeSize/4,cell/2-wall_add,0])
            rotate([0, 0, 180])
            translate([0,0,-height/2])
            male_dovetail(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height-2, cutout_width=dove_cutout_width, cutout_depth=dove_cutout_depth);
            //
            translate([ii*cubeSize-cubeSize/4,cell/2-wall_add,0])
            rotate([0, 0, 180])
            translate([0,0,-height/2])
            male_dovetail(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height-2, cutout_width=dove_cutout_width, cutout_depth=dove_cutout_depth);
        }
        for ( ii = [1 : cols] )
        {
            translate([cell/2-wall_add,ii*cubeSize+cubeSize/4,0])
            rotate([0, 0, 90])
            translate([0,0,-height/2])
            male_dovetail(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height-2, cutout_width=dove_cutout_width, cutout_depth=dove_cutout_depth);
            //
            translate([cell/2-wall_add,ii*cubeSize-cubeSize/4,0])
            rotate([0, 0, 90])
            translate([0,0,-height/2])
            male_dovetail(max_width=dove_max_width, min_width=dove_min_width, depth=dove_depth, height=height-2, cutout_width=dove_cutout_width, cutout_depth=dove_cutout_depth);
        }
    }
}

module female_dovetail_negative(max_width=11, min_width=5, depth=5, height=30, clearance=0.25) {
	union() {
		translate([0,-0.001,-0.05])
			dovetail_3d(max_width+clearance,min_width+clearance,depth,height+0.1);
			translate([-(max_width+clearance)/2, depth-0.002,-0.5])
				cube([max_width+clearance,clearance/2,height+1]);
	}
}

module female_dovetail(max_width=11, min_width=5, depth=5, height=30, block_width=15, block_depth=9, clearance=0.25) {
		difference() {
			translate([-block_width/2,0,0]) cube([block_width, block_depth, height]);
			female_dovetail_negative(max_width, min_width, depth, height, clearance);
		}
}

module male_dovetail(max_width=11, min_width=5, depth=5, height=30, cutout_width=5, cutout_depth=3.5) {
	difference() {
		dovetail_3d(max_width,min_width,depth,height);
		translate([0.001,depth+0.001,-0.05])
			dovetail_cutout(cutout_width, cutout_depth, height+0.1);
	}
}

module dovetail_3d(max_width=11, min_width=5, depth=5, height=30) {
	linear_extrude(height=height, convexity=2)
		dovetail_2d(max_width,min_width,depth);
}

module dovetail_2d(max_width=11, min_width=5, depth=5) {
	angle=atan((max_width/2-min_width/2)/depth);
	polygon(paths=[[0,1,2,3,0]], points=[[-min_width/2,0], [-max_width/2,depth], [max_width/2, depth], [min_width/2,0]]);
}

module dovetail_cutout(width=5, depth=4, height=30) {
	translate([0,-depth+width/2,0])
		union() {
			translate([-width/2,0,0])
				cube([width,depth-width/2,height]);
			difference() {
				cylinder(r=width/2, h=height, $fs=0.25);
				translate([-width/2-0.05,0.05,-0.05]) cube([width+0.1,width+0.1,height+0.1]);
			}
		}
}
```


[User]
Hi, I'm looking to create a modular mold box for silicone casting, and I need help generating some OpenSCAD code. The idea is to have adjustable sides so I can easily change the size of the mold.

The basic concept is a bracket with two legs forming an L shape. One leg will be longer than the other. I need to be able to print multiple of these brackets and assemble them to create a rectangular mold box. The brackets should connect to each other using a sliding rail system. I'm aiming for a design that allows for adjustable mold sizes by sliding the brackets along each other.

Here are the specific parameters I have in mind:

*   **L1**: Short leg length (e.g., 50mm)
*   **L2**: Long leg length (e.g., 180mm)
*   **T**: Wall thickness (e.g., 3.6mm)
*   **H**: Wall height (e.g., 40mm)
*   **G**: Tolerance gap for the sliding rail (e.g., 0.1mm)

The rail should be on the shorter leg, and it should allow two brackets to slide against each other smoothly.

I'd like two different versions of the bracket. The first version is a simple L-shape, and it should have a sliding rail on the short leg so the bracket can connect to another bracket along its short leg. The second version is also an L shape, but the short leg will have a rail that is in three parts. It will have a solid bar in the center of the short leg, with recessed rails above and below on that bar, where the mating bracket's bar will slide in and create a sort of three-sided channel.

Ideally, the code should be parametric so I can easily adjust these dimensions later.

I would need the code to generate two .scad files, one for each bracket design. I'm hoping this project will help me make reusable molds, so a sturdy design is important. I would like the code to use the BOSL2 library. Can you help with this?

[Assistant]
molding-bracket.scad:
```
/*
// (c) Csaba Laszlo Bacskay
// csaba@mynergy.com
*/
$fn=50;
//Short leg length
L1=50;
//Long leg length
L2=180;
//Wall thickness
T=3.6;
//Wall height
H=40;
//Rail Enable
R=1;
/*
Tolerance gap in units(mm),
gap on both sides of the rail
*/
//Tolerance gap
G=.1;

//module for rendering one bracket
module Bracket(x,y,z) {
translate([x,y,z]) {
    cube([L2,T,H]);
    cube([T,L1,H]);
    if (R==1) {
      difference() {
        translate([-2*T,0,-T]) cube([T*3,L1,T*2]);
        union() {
            translate([-T-2*G,0,0]) cube([T+2*G,L1,T]);
            translate([-2*T,T,-T]) cube([2*T,L1-3*T,T]);
           }//eof union
        }//eof difference
    }//eof if
}//eof translate
}//eof Bracket

//Render 1 bracket at center position
Bracket(0,0,0);
```


[User]
Hey, I'm looking to create a customizable, print-in-place tray for my pet's cage, and I think OpenSCAD would be perfect for this. I've seen some designs that get close, but they aren't quite what I'm looking for.

Specifically, I need a tray that can attach to the cage bars using twist locks, so the whole thing is removable. I'd like it to be parametric, so I can easily adjust it to different cage sizes. Here's what I'm picturing:

**Overall Structure:**

*   **Tray Body:** A rectangular tray with a base and walls.
*   **Guard:** A raised section of the wall on the back of the tray to prevent food from being pushed out.
*   **Perch:** A small perch attached to the tray near the top of the guard, so my pet can sit there.
*   **Twist Locks:** Two twist lock mechanisms on the back of the tray to attach to the cage bars. These need to be print-in-place - that is, fully assembled and functional without needing to be assembled after printing.

**Parameters (that I want to be able to adjust):**

*   `width`: The overall width of the tray.
*   `height`: The height of the tray base.
*   `depth`: The depth of the tray base.
*   `wall_thickness`: The thickness of the tray walls and guard.
*   `tessellation_tolerance`: The gap between the moving parts of the locks to ensure smooth movement.
*   `bar_gap`: The size of the gap between the cage bars.
*  `bar_thickness`: The thickness of the cage bars themselves.
*   `nudge_amount`: An adjustment value for fine-tuning the position of the twist locks, in case they don't align correctly without adjustment.
*   `guard_height`: The height of the guard.
*   `perch_width`: The width of the perch.

**Twist Lock Mechanism Details:**

*   The locks should consist of a primary cylinder that fits into the cage bar gap, and a secondary cylinder that is thicker to secure the tray to the bar.
*   The mechanism should rotate freely around an axis with appropriate gaps for print in place.
*   There should be an axle extending into the lock body with a small gap so that it rotates freely.

**Additional Considerations:**

*   I'd like to be able to output a test object that just displays the alignment of the twist lock mechanisms. This way, I can ensure the `bar_gap` and `bar_thickness` settings are correct before printing the whole tray. So an output variable that toggles between "tray" and "alignment test" would be really useful.
*   I'm aiming for a design that's quick to print and strong.
*   I plan on using a food-safe filament like PLA, but it might be useful to account for some over-extrusion that would happen if I tried to make it water-tight for a bath. I think that'd need to increase the tessellation tolerance.
*   I want to make sure the locking mechanisms have good tolerances so that they move freely but still keep the tray firmly attached.

Could you generate the OpenSCAD code that does all of this? I think we'll probably need to use the built-in OpenSCAD `minkowski`, `hull`, and `linear_extrude` modules, so I don't think we will need any libraries. Let me know if you have any questions.

[Assistant]
tray_customizer.scad:
```
// print the alignment test before printing the tray. This will validate the tray will correctly align with the bars of your cage.
output = "tray"; // [alignment test,tray]

width  = 120;
height = 20;
depth  = 70;

wall_thickness         = 3;
// the size of the gap between moving parts. Increaing this can help if parts are seized.
tessellation_tolerance = 0.2;

// the size of the gap between bars (gap only)
bar_gap       = 10;
// the thickness of the wire bars
bar_thickness = 1.7;
// if the catches don't both align with the bars this can be used to adjust their position
nudge_amount = 0;

guard_height = 25;
perch_width  = 10;

/* [Hidden] */
first_offset            = -bar_gap * 2.5 - bar_thickness * 2 - nudge_amount / 2;
lock_offsets            = [first_offset, first_offset + bar_gap * 5 + bar_thickness * 4 + nudge_amount];
lock_primary_diameter   = bar_gap - tessellation_tolerance * 2;
lock_secondary_diameter = lock_primary_diameter + 5;

$fa = 5;
$fn = 360 / $fa;

if(output == "tray")
    build();
else
    test_spacing();

module test_spacing()
{
    cube([width, 5, 2], orient_x = ORIENT_CENTRE);
    for(offset = lock_offsets)
        translate([offset, 0, 0])
            cube([lock_primary_diameter, 5, 2], orient_x = ORIENT_CENTRE, orient_y = ORIENT_NEGATIVE);
}

module test_lock()
{
    intersection()
    {
        build();
        translate([-44.5, -13, 0])
            cube([45, 23, 20]);
    }
}

module build()
{
    difference()
    {
        union()
        {
            difference()
            {
                union()
                {
                    cube([width, depth, height], orient_x = ORIENT_CENTRE);
                    cube([width, wall_thickness, height + guard_height], orient_x = ORIENT_CENTRE);
                    translate([0, wall_thickness, height])
                        rotate([-90, 0, 0])
                        transform_hull(translate = [0, 0, depth - wall_thickness], hull_easing = EASE_OUT)
                        {
                            square([width, guard_height], orient_x = ORIENT_CENTRE, orient_y = ORIENT_NEGATIVE);
                            square([width, 0.01], orient_x = ORIENT_CENTRE, orient_y = ORIENT_NEGATIVE);
                        }
                    translate([0, depth, 0])
                    {
                        translate([0, perch_width / 2 - wall_thickness, height])
                            intersection()
                            {
                                rotate([0, 90, 0])
                                    cylinder(d = perch_width, h = width, orient_z = ORIENT_CENTRE);
                                cylinder(d = 999, h = 999);
                            }
                        hull()
                        {
                            translate([0, 0, height])
                                cube([width, perch_width - wall_thickness, 0.01], orient_z = ORIENT_NEGATIVE, orient_x = ORIENT_CENTRE);
                            cube([width, 0.01, 0.01], orient_x = ORIENT_CENTRE);
                        }
                    }
                }
                translate([0, wall_thickness , wall_thickness])
                    cube([width - wall_thickness * 2, depth - wall_thickness * 2, 999], orient_x = ORIENT_CENTRE);
            }
            intersection()
            {
                for(offset = lock_offsets)
                    translate([offset, wall_thickness, bar_gap / 2 - tessellation_tolerance])
                        rotate([-90, 0, 0])
                        cylinder(d = lock_secondary_diameter + tessellation_tolerance * 2 + wall_thickness * 2, h = wall_thickness * 2 + tessellation_tolerance * 2);
                cylinder(d = 999, h = 999);
            }
        }
        for(offset = lock_offsets)
            translate([offset, 0, bar_gap / 2 - tessellation_tolerance])
            {
                rotate([-90, 0, 0])
                {
                    translate([0, 0, wall_thickness])
                        cylinder(d = lock_secondary_diameter + tessellation_tolerance * 2, h = wall_thickness + tessellation_tolerance * 2);
                    translate([0, 0, -0.01])
                        cylinder(d = bar_gap + tessellation_tolerance * 2, h = wall_thickness + 0.02);
                }
                translate([0, -0.01, 0])
                    cube([bar_gap * sin(45), wall_thickness + 0.02, bar_gap / 2 + tessellation_tolerance], orient_x = ORIENT_CENTRE, orient_z = ORIENT_NEGATIVE);
            }
    }

    for(offset = lock_offsets)
        translate([offset, -bar_thickness, 0])
            lock();
}

module lock()
{
    width            = bar_gap * 3 + bar_thickness * 4 + wall_thickness * 2;
    axle_trim_amount = 1;
    corner_radius = 2;
    difference()
    {
        intersection()
        {
            translate([0, corner_radius, 0])
                cube([width, 10 + corner_radius, lock_primary_diameter], orient_x = ORIENT_CENTRE, orient_y = ORIENT_NEGATIVE, cr = corner_radius);
            cube(999, orient = [ORIENT_CENTRE, ORIENT_NEGATIVE, ORIENT_CENTRE]);
        }
        bar_insets();
        mirror([1, 0, 0])
            bar_insets();
    }
    intersection()
    {
        rotate([-90, 0, 0])
            cylinder(d = lock_primary_diameter, h = bar_thickness + wall_thickness + tessellation_tolerance, orient_y = ORIENT_NEGATIVE);
        cube([999, 999, lock_primary_diameter - axle_trim_amount], orient_x = ORIENT_CENTRE);
    }
    intersection()
    {
        translate([0, bar_thickness + wall_thickness + tessellation_tolerance, bar_gap / 2 - tessellation_tolerance])
            rotate([-90, 0, 0])
            cylinder(d = lock_secondary_diameter, h = wall_thickness);
        union()
            cylinder(d = 999, h = lock_primary_diameter + (lock_secondary_diameter - lock_primary_diameter) / 2 - axle_trim_amount);
    }

    module bar_insets()
    {
        translate([bar_gap / 2 + bar_thickness / 2, bar_thickness / 2, 0])
        {
            cylinder(d = bar_thickness + tessellation_tolerance * 4, h = 999, orient_z = ORIENT_CENTRE);
            translate([bar_gap + bar_thickness, 0, 0])
                cylinder(d = bar_thickness + tessellation_tolerance * 4, h = 999, orient_z = ORIENT_CENTRE);
        }
    }
}

module cube(
    size     = [1, 1, 1],
    center   = false,
    orient   = undef,
    orient_x = undef,
    orient_y = undef,
    orient_z = undef,
    cr       = undef,
    cd       = undef
)
{
    cr = cr == undef && cd == undef ?
        0 :
        cr == undef ?
            cd / 2 :
            cr;

    size          = is_float(size) ? [ size, size, size ] : size;
    adjusted_size = [ for(s = size) s - cr * 2 ];
    orient        = center ? [ORIENT_CENTRE, ORIENT_CENTRE, ORIENT_CENTRE] : (orient == undef ? [orient_x, orient_y, orient_z] : orient);

    offset_x = (
        orient[0] == ORIENT_POSITIVE || orient[0] == ORIENT_DEFAULT ?
            0 :
            orient[0] == ORIENT_CENTRE ?
                -size[0] / 2 :
                -size[0]
    );
    offset_y = (
        orient[1] == ORIENT_POSITIVE || orient[1] == ORIENT_DEFAULT ?
            0 :
            orient[1] == ORIENT_CENTRE ?
                -size[1] / 2 :
                -size[1]
    );
    offset_z = (
        orient[2] == ORIENT_POSITIVE || orient[2] == ORIENT_DEFAULT ?
            0 :
            orient[2] == ORIENT_CENTRE ?
                -size[2] / 2 :
                -size[2]
    );

    if(cr > 0)
    {
        if(adjusted_size[0] > 0 && adjusted_size[1] > 0 && adjusted_size[2] > 0)
        {
            translate([offset_x, offset_y, offset_z])
                minkowski()
                {
                    translate([cr, cr, cr])
                        cube(adjusted_size);
                    sphere(r = cr);
                }
        }
    }
    else
    {
        translate([offset_x, offset_y, offset_z])
            cube(size);
    }

    module cube(size)
    {
        linear_extrude(height = size[2])
            square([size[0], size[1]]);
    }
}

module cylinder(
    h        = 1,
    r1       = 1,
    r2       = 1,
    r        = undef,
    d1       = undef,
    d2       = undef,
    d        = undef,
    center   = false,
    cr       = undef,
    cd       = undef,
    orient   = undef,
    orient_x = undef,
    orient_y = undef,
    orient_z = undef
)
{
    cr = cr == undef && cd == undef ?
        0 :
        cr == undef ?
            cd / 2 :
            cr;

    r1     = d1 == undef ? (d == undef ? (r == undef ? r1 : r) : d / 2) : d1 / 2;
    r2     = d2 == undef ? (d == undef ? (r == undef ? r2 : r) : d / 2) : d2 / 2;
    max_r  = max(r1, r2);
    orient = center ? [ORIENT_CENTRE, ORIENT_CENTRE, ORIENT_CENTRE] : (orient == undef ? [orient_x, orient_y, orient_z] : orient);

    offset_x = (
        orient[0] == ORIENT_POSITIVE ?
            max_r :
            orient[0] == ORIENT_CENTRE || orient[0] == ORIENT_DEFAULT ?
                0 :
                -max_r
    );
    offset_y = (
        orient[1] == ORIENT_POSITIVE ?
            max_r :
            orient[1] == ORIENT_CENTRE || orient[1] == ORIENT_DEFAULT ?
                0 :
                -max_r
    );
    offset_z = (
        orient[2] == ORIENT_POSITIVE || orient[2] == ORIENT_DEFAULT ?
            0 :
            orient[2] == ORIENT_CENTRE ?
                -h / 2 :
                -h
    );

    if(cr > 0)
    {
        adjusted_r1 = r1 - cr;
        adjusted_r2 = r2 - cr;
        adjusted_h  = h - cr * 2;

        if(adjusted_r1 > 0 && adjusted_r2 > 0 && adjusted_h > 0)
        {
            translate([offset_x, offset_y, offset_z])
                minkowski()
                {
                    translate([0, 0, cr])
                        cylinder(r1 = adjusted_r1, r2 = adjusted_r2, h = adjusted_h);
                    sphere(r = cr);
                }
        }
    }
    else
    {
        translate([offset_x, offset_y, offset_z])
            cylinder(r1 = r1, r2 = r2, h = h);
    }

    module cylinder(r1, r2, h)
    {
        if(r1 >= r2)
        {
            linear_extrude(height = h, slices = 1, scale = r2 / r1)
                circle(r = r1);
        }
        else
        {
            translate([0, 0, h])
                mirror([0, 0, 1])
                linear_extrude(height = h, slices = 1, scale = r1 / r2)
                circle(r = r2);
        }
    }
}

module sphere(
    r        = 1,
    d        = undef,
    orient   = undef,
    orient_x = undef,
    orient_y = undef,
    orient_z = undef,
)
{
    r      = d == undef ? r : d / 2;
    orient = orient == undef ? [orient_x, orient_y, orient_z] : orient;

    offset_x = (
        orient[0] == ORIENT_POSITIVE ?
            r :
            orient[0] == ORIENT_CENTRE || orient[0] == ORIENT_DEFAULT ?
                0 :
                -r
    );
    offset_y = (
        orient[1] == ORIENT_POSITIVE ?
            r :
            orient[1] == ORIENT_CENTRE || orient[1] == ORIENT_DEFAULT ?
                0 :
                -r
    );
    offset_z = (
        orient[2] == ORIENT_POSITIVE || orient[2] == ORIENT_DEFAULT ?
            r :
            orient[2] == ORIENT_CENTRE ?
                0 :
                -r
    );

    translate([0, 0, -r - 1])
    rotate_extrude()
        translate([0, r + 1])
        difference()
        {
            rotate([0, 0, 180])circle(r = r);
            translate([0, -r - 1, 0])
                square(r * 2 + 2);
        }
}
function is_array(v) = !(v>="") && len(v) != undef;
function is_boolean(v) = (v==true || v==false);
function is_float(v) = v+0 != undef;
function is_definite_float(v) = v==v && (v+1)!=undef && (v+1)!=v;
function is_range(v) = !(v>="") && len(v)==undef && v[0]!=undef;
function is_string(v) = v >= "";
function type_of(v) = v>="" ? "string" :
                     len(v) != undef ? "array" :
                     v+0 != undef ? "float" :
                     v[0] != undef ? "range" :
                     v == false || v == true ? "boolean" :
                     v == undef ? "undef" :
                     "unknown";

ORIENT_POSITIVE = 0;
ORIENT_NEGATIVE = 1;
ORIENT_CENTRE   = 2;
ORIENT_CENTER   = ORIENT_CENTRE;
ORIENT_DEFAULT  = undef;

module square(
    size     = [1, 1],
    center   = false,
    cr       = undef,
    cd       = undef,
    orient   = undef,
    orient_x = undef,
    orient_y = undef
)
{
    cr = cr == undef && cd == undef ?
        0 :
        cr == undef ?
            cd / 2 :
            cr;

    size          = is_float(size) ? [ size, size ] : size;
    adjusted_size = [ for(s = size) s - cr * 2 ];
    orient        = center ? [ORIENT_CENTRE, ORIENT_CENTRE] : (orient == undef ? [orient_x, orient_y] : orient);

    offset_x = (
        orient[0] == ORIENT_POSITIVE || orient[0] == ORIENT_DEFAULT ?
            0 :
            orient[0] == ORIENT_CENTRE ?
                -size[0] / 2 :
                -size[0]
    );
    offset_y = (
        orient[1] == ORIENT_POSITIVE || orient[1] == ORIENT_DEFAULT ?
            0 :
            orient[1] == ORIENT_CENTRE ?
                -size[1] / 2 :
                -size[1]
    );

    if(cr > 0)
    {
        if(adjusted_size[0] > 0 && adjusted_size[1] > 0)
        {
            translate([offset_x, offset_y])
                minkowski()
                {
                    translate([cr, cr, cr])
                        square(adjusted_size);
                    circle(r = cr);
                }
        }
        else
        {
            log_warn("Square too small for corner radius, geometry will not be rendered.");
        }
    }
    else
    {
        translate([offset_x, offset_y])
            square(size);
    }

    module square(size)
    {
        polygon(points = [
            [0, 0],
            [size[0], 0],
            [size[0], size[1]],
            [0, size[1]]
        ]);
    }
}

module circle(
    r        = 1,
    d        = undef,
    orient   = undef,
    orient_x = undef,
    orient_y = undef
)
{
    r      = d == undef ? r : d / 2;
    orient = orient == undef ? [orient_x, orient_y] : orient;

    offset_x = (
        orient[0] == ORIENT_POSITIVE ?
            r :
            orient[0] == ORIENT_CENTRE || orient[0] == ORIENT_DEFAULT ?
                -0 :
                -r
    );
    offset_y = (
        orient[1] == ORIENT_POSITIVE ?
            r :
            orient[1] == ORIENT_CENTRE || orient[1] == ORIENT_DEFAULT ?
                -0 :
                -r
    );

    num_fragments = $fn > 0 ?
        $fn >= 3 ? $fn : 3 :
        ceil(max(min(360 / $fa, r * 2 * PI / $fs), 5));
    a_step = 360 / num_fragments;

    translate([offset_x, offset_y])
        polygon(points = [ for(a = [0 : a_step : 360 - a_step]) [ r * cos(a), r * sin(a) ]]);
}
module transform_hull(
    translate = [0, 0, 0],
    rotate = [0, 0, 0],
    scale = [1, 1, 1],
    hull_easing = EASE_LINEAR,
    hull_easing_exponent = 2,
    translate_easing = EASE_LINEAR,
    translate_easing_exponent = 2,
    rotate_easing = EASE_LINEAR,
    rotate_easing_exponent = 2,
    scale_easing = EASE_LINEAR,
    scale_easing_exponent = 2,
    debug = false
)
{
    if($children >= 2)
    {
        start_translate = [0, 0, 0];
        start_rotate    = [0, 0, 0];
        start_scale     = [1, 1, 1];

        $fn = ($fn == undef || $fn == 0) ?
            floor(360 / $fa) :
            floor($fn) < 1 ?
                1 :
                floor($fn);

        if(debug)
        {
            start_colour = [0.3, 0.3, 1, 0.5];
            end_colour   = [0.5, 1, 0.5, 0.5];

            for(i = [0 : $fn - 1])
                color(lerp_vector(start_colour, end_colour, i / $fn))
                    translate(ease(start_translate, translate, i / $fn, translate_easing, translate_easing_exponent))
                    rotate(ease(start_rotate, rotate, i / $fn, rotate_easing, rotate_easing_exponent))
                    scale(ease(start_scale, scale, i / $fn, scale_easing, scale_easing_exponent))
                    linear_extrude(height = 0.0001)
                    morph_2d(ease(0, 1, i / $fn, hull_easing, hull_easing_exponent))
                    {
                        children(0);
                        children(1);
                    }
            color(end_colour)
                translate(ease(start_translate, translate, 1, translate_easing, translate_easing_exponent))
                rotate(ease(start_rotate, rotate, 1, rotate_easing, rotate_easing_exponent))
                scale(ease(start_scale, scale, 1, scale_easing, scale_easing_exponent))
                translate([0, 0, -0.0001])
                linear_extrude(height = 0.0001)
                morph_2d(ease(0, 1, 1, hull_easing, hull_easing_exponent))
                {
                    children(0);
                    children(1);
                }
        }
        else
        {
            for(i = [0 : $fn - 1])
                hull()
                {
                    translate(ease(start_translate, translate, i / $fn, translate_easing, translate_easing_exponent))
                        rotate(ease(start_rotate, rotate, i / $fn, rotate_easing, rotate_easing_exponent))
                        scale(ease(start_scale, scale, i / $fn, scale_easing, scale_easing_exponent))
                        linear_extrude(height = 0.0001)
                        morph_2d(ease(0, 1, i / $fn, hull_easing, hull_easing_exponent))
                        {
                            children(0);
                            children(1);
                        }
                    translate(ease(start_translate, translate, (i + 1) / $fn, translate_easing, translate_easing_exponent))
                        rotate(ease(start_rotate, rotate, (i + 1) / $fn, rotate_easing, rotate_easing_exponent))
                        scale(ease(start_scale, scale, (i + 1) / $fn, scale_easing, scale_easing_exponent))
                        translate([0, 0, -0.0001])
                        linear_extrude(height = 0.0001)
                        morph_2d(ease(0, 1, (i + 1) / $fn, hull_easing, hull_easing_exponent))
                        {
                            children(0);
                            children(1);
                        }
                }
        }
    }
    else if($children == 1)
    {
        children(0);
    }
}

module morph_2d(amount = 0.5)
{
    if($children >= 2)
    {
        projection(cut = true)
            translate([0, 0, -amount])
            hull()
            {
                linear_extrude(height = 0.0001)
                    children(0);
                translate([0, 0, 1])
                    linear_extrude(height = 0.0001)
                    children(1);
            }
    }
    else if($children == 1)
    {
        children(0);
    }
}
EASE_IN     = 0;
EASE_OUT    = 1;
EASE_IN_OUT = 2;
EASE_LINEAR = 3;
EASE_OUT_IN = 4;

function ease(a, b, proportion, type = EASE_LINEAR, exponent = 2) =
    type == EASE_LINEAR ?
        ease_linear(a, b, proportion) :
        type == EASE_OUT ?
            ease_out(a, b, proportion, exponent) :
            type == EASE_IN ?
                ease_in(a, b, proportion, exponent) :
                type == EASE_IN_OUT ?
                    ease_in_out(a, b, proportion, exponent) :
                    ease_out_in(a, b, proportion, exponent);

function ease_in(a, b, proportion, exponent = 2) = (b - a) * pow(clamp(proportion, 0, 1), exponent) + a;
function ease_out(a, b, proportion, exponent = 2) = (b - a) * (1 - pow(1 - clamp(proportion, 0, 1), exponent)) + a;

function ease_in_out(a, b, proportion, exponent = 2) =
    proportion < 0.5 ?
        ease_in(a, ((b - a) / 2 + a), proportion * 2, exponent) :
        ease_out(((b - a) / 2 + a), b, proportion * 2 - 1, exponent);

function ease_out_in(a, b, proportion, exponent = 2) =
    proportion >= 0.5 ?
        ease_in(((b - a) / 2 + a), b, proportion * 2 - 1, exponent) :
        ease_out(a, ((b - a) / 2 + a), proportion * 2, exponent);

function ease_linear(a, b, proportion) = lerp(a, b, proportion);
function clamp(value, minimum, maximum) = max(min(value, maximum), minimum);
function lerp(start, end, amount) = start + (end - start) * amount;
function lerp_vector(start, end, amount) =
    [ for(i = [0 : len(start) - 1]) lerp(start[i], end[i], clamp(amount, 0, 1)) ];

```