[User]
I need an OpenSCAD module to generate standoffs, but I want it to be much more flexible than what I've seen before. I'd like to be able to create single standoffs, arrays of identical standoffs, and arrays of *sample* standoffs, where each standoff in the array has different properties for easy comparison.

Here's a breakdown of the features I need:

**Standoff Properties:**

*   **Shape:** The base of the standoff should be selectable from:
    *   Round (cylinder)
    *   Square (cube)
    *   Hexagonal
*   **Base Height:** Adjustable height of the base.
*   **Base Diameter:** Adjustable diameter/size of the base (diameter for round, side length for square, etc.).
*   **Top Style:** Selectable style of the top section, with options for:
    *   Male (a cylindrical peg)
    *   Snap-In (a peg with a flexible notch for insertion)
    *   Flat (no top section)
    *   Female (a cylindrical hole/cavity)
    *   Hollow (a cylinder with a through hole)
*   **Top Height:** Adjustable height of the top section.
*   **Top Diameter:** Adjustable diameter/size of the top section.
*   **Fillets:** I'd like to be able to specify a fillet radius on the top and bottom edges of the base, and on the top edge of the male and snap-in styles.
*   **Tolerances:** Allow a tolerance value to be specified, which will be applied to diameters for more reliable fits (e.g., hole diameters will be increased, peg diameters will be decreased).
*   **Threaded Options:** I need an option to add threads, which should be adjustable in terms of diameter, pitch, thread length, and type (e.g., metric, imperial). Include an option to create a clearance hole through the standoff.
*   **Hole Patterns:** I need to be able to specify a number of holes on the face(s) of the standoff base, with controls over diameter, spacing, and their arrangement pattern (e.g., circular, grid).

**Array Generation:**

*   **Single:** Generates one standoff with specified properties.
*   **Array-Same:** Generates an array of identical standoffs, with adjustable rows, columns, and X/Y offsets between standoffs.
*   **Array-Samples:** Generates an array of standoffs, where each standoff varies along two selected dimensions. The user should be able to choose which two dimensions to sample from the shape, top style, etc. properties. For example, if the user chooses 'shape' and 'top style', each standoff in the array would have a unique combination of a shape from the list of shapes and a style from the list of top styles.  The user should be able to define the x/y offset between standoffs and to be able to control which dimensions are varied for each axis of the array.

**Additional Features:**

*   **Flanges:** Options for top and/or bottom flanges (including both a flat and a sloped flange).
*   **Support Structure:** An option to generate support structures under the base (e.g., ribs, a thin shell).

**Library Requirements:**

I think I will need the BOSL2 library.

I'd like the module to be well-organized, easily customizable, and clearly documented, with all units in millimeters.

Can you generate the OpenSCAD code?

[Assistant]
StandoffGenerator.scad:
```
// Standoff Generator Module
//
// Author: Steven Nemetz
//
// Current version: https://github.com/snemetz/OpenSCAD-Modules/tree/master/standoffs
// Customizable: http://www.thingiverse.com/thing:1528494
/*
	REVISION HISTORY
	v0.2 Added Array-Same generation
	v0.1 Added hollow style
	Initial code refactored from :
		eriqjo's Standoff Customizer v1.0
			http://www.thingiverse.com/thing:351092
		and Kevin Osborn's Snap in PCB Standoff
*/
// TODO: Future Feature Ideas
//	flanges: top and/or bottom
//	flange styles:
//	bottom styles: support all top styles - rotate 180 and translate top
//	Look at adding features from http://www.thingiverse.com/thing:79944
//		or original http://www.thingiverse.com/thing:44894

//CUSTOMIZER VARIABLES

//Type of standoff(s) to generate
Generate = 3; // [1:Single, 2:Array-Same, 3:Array-Samples]
/* [Body] */
//Choose shape of the main body
Shape = 3; // [1:Round, 2:Square, 3:Hex]
//Select height of the main body,  mm
BaseHeight = 15; // [0:50]
//Select diameter of the main body, mm
BaseDia = 6; // [0:30]
/* [Top] */
//Choose style of the top section
Style = 1; // [1:Male, 2:Snap-In, 3:Flat, 4:Female, 5:Hollow]
//Select height of the top, mm
TopHeight = 4; // [2:20]
//Select diameter of the top, mm
TopDia = 2; // [1:25]
/* [Array Settings] */
//For array: Space between standoffs, X mm
X_Offset = 15; // [2:30]
//For array: Space between standoffs, Y mm
Y_Offset = 10; // [2:30]
//For Array-Same
Columns = 3;
//For Array-Same
Rows = 3;

//CUSTOMIZER VARIABLES END

/* [Hidden] */
// For sample array
Shapes = [1:3]; // All valid shapes
Styles = [1:5]; // All valid styles

RndFrags = 50; // number of facet fragments for round shapes
// Parameters: style, height, diameter, Thread_Height
module standoffTop(style, baseHeight, topHeight, diameter) {
	radius = diameter/2;
	if (style == 1) { //male
		translate([0,0, baseHeight + topHeight/2 - 0.1])
			cylinder(topHeight + 0.1, r = radius, $fn = RndFrags, center = true);
	} else if (style == 2) { // snapin
		// FIX: large top height - this is above base
		notchW = radius/1.5;//width of the flexy notch in terms of peg radius
		translate([0,0, baseHeight - 0.1]) // -2 needs to be a calc
			difference() {
				union() {
					// top standoff
					cylinder(r= radius,h = topHeight+1, $fn = RndFrags); //master peg
					// underside slant - relief for overhang
					translate([0,0,topHeight])
						cylinder(r1=radius,r2= radius+.5, h =1, $fn = RndFrags);
					// top slant - insertion cone
					translate([0,0,topHeight+1])
						cylinder(r1=radius+.5,r2= radius-.25, h =1, $fn = RndFrags);
					} //union
				// Internal cylinder cutout
				cylinder(r= radius-.5, h = topHeight+3, $fn = RndFrags);
				//notch for insertion flex
				translate([-(diameter+2)/2,-notchW/2,-0.1])
					cube([diameter+2,notchW,topHeight+2.2]);
			} //difference
	} else if (style == 3) { // flat - No top
	} else if (style == 4) { // female
		translate([0,0, baseHeight - topHeight/2 + 0.1])
			cylinder(topHeight + 0.1, r = radius, $fn = RndFrags, center = true);
	} else if (style == 5) { // hollow
		translate([0,0, baseHeight/2])
			cylinder(baseHeight + 0.1, r = radius, $fn = RndFrags, center = true);
	}
};
// Parameters: shape, height, diameter
module standoffBase(shape, height, diameter) {
	radius = diameter/2;
	if (shape == 1) { // round
		translate([0, 0, height/2])
			cylinder(height, r = radius, center = true, $fn = RndFrags);
	} else if (shape == 2) { // square
		translate([0,0, height/2])
			cube([diameter, diameter, height], center = true);
	} else if (shape == 3) { // hex
		cylinder(h = height, r = radius, center = false, $fn = 6);
	}
};
// Parameters: shape, style, Body_Diameter, Body_Height, Thread_Height, Thread_Dia
module standoff(shape, baseHeight, baseDiameter, style, topHeight, topDiameter) {
	if (style == 1 || style == 2) { // male or snap-in
		union() {
			standoffBase(shape, baseHeight, baseDiameter);
			standoffTop(style, baseHeight, topHeight, topDiameter);
		}
	} else if (style == 4 || style == 5) { // female or hollow
		difference() {
			standoffBase(shape, baseHeight, baseDiameter);
			standoffTop(style, baseHeight, topHeight, topDiameter);
		}
	} else if (style==3) { // flat - no top
		standoffBase(shape, baseHeight, baseDiameter);
	}
};

function range2vector(r) = [ for (i=r) i];

//
ShapesV = range2vector(Shapes);
StylesV = range2vector(Styles);
if (Generate == 1) { // single
	standoff(Shape,BaseHeight,BaseDia,Style,TopHeight,TopDia);
} else if (Generate == 2) { // array all same
	translate([-(X_Offset * (Rows+1))/2, -(Y_Offset * (Columns+1))/2, 0])
		union(){
			for (j = [1 : Rows]){
				translate([0,j * Y_Offset,0])
					for( i = [1 : Columns]){
						translate([i * X_Offset,0,0])
							standoff(Shape,BaseHeight,BaseDia,Style,TopHeight,TopDia);
					} // for
			} // for
		}; // union
} else if (Generate == 3) { // array Samples
	translate([-(X_Offset * (len(StylesV)+1))/2, -(Y_Offset * (len(ShapesV)+1))/2, 0])
		union(){
			for (shape = Shapes) {     // Rows of styles = row of a shape
				translate([0,shape * Y_Offset,0])
					for( style = Styles) {   // Columns of shapes = column of a style
						translate([style * X_Offset,0,0])
							standoff(shape, BaseHeight, BaseDia, style, TopHeight, TopDia);
					} // for
			} // for
		}; // union
};

// END

```


[User]
I need an OpenSCAD design for a customizable filament holder that can also function as a shelf bracket. It's designed to be used with a single slotted shelf standard, which is a common type of shelving system.

Here's a breakdown of the key features and parameters I'd like to have:

**Structure**

*   **Arm:**
    *   The arm extends horizontally from the shelf standard and supports the filament spool.
    *   It should have an option to be either straight or angled downwards.
    *   The length of the arm should accommodate spools of different widths, with a parameter to control it.
    *   There should be a way to enable/disable this arm.
*   **Back Latch:**
    *   This part hooks onto the shelf standard.
    *   It needs to be sturdy enough to hold the weight of the filament and potentially a shelf.
    *   There should be two "hooks" that fit into the slots of the shelf standard, and the distance between these hooks should be adjustable.
*   **Roll:**
    *   This is the cylindrical part that holds the filament spool.
    *   Its diameter should be slightly less than the hole in a standard filament spool, so it can rotate freely.
    *   It should have an option to enable or disable it.
*   **Spool Carrier/Stopper:**
    *   I need a mechanism to prevent the spool from sliding off the roll. I want to have an option for a spool carrier (a guide for the spool to prevent it from touching the arm or back latch) and stoppers, which will be small protrusions that fit into the roll and keep the spool in place.
*   **Spacer:**
    *   Optionally, a spacer can be included to increase the distance between the filament holder and the wall.
    *   The spacer should have adjustable maximum and minimum heights.

**Parameters**

I want the following parameters to be customizable:

*   **`arm`:** Enable/disable the arm (boolean, default: true)
*   **`roll`:** Enable/disable the roll (boolean, default: false)
*   **`spacer`:** Enable/disable the spacer (boolean, default: false)
*   **`height_backlatch`:** Height of the back latch (default: 65)
*   **`height_arm`:** Height of the arm at the back (default: 30)
*   **`arm_angle`:** Angle of the lower part of the arm (default: 9 degrees)
*   **`distance_hooks`:** Distance between the hooks on the back latch (default: 50)
*   **`diameter_roll`:** Diameter of the roll (default: 50)
*   **`width_roll`:** Width of the spool hole (default: 55)
*   **`thickness`:** Thickness of the arm and other parts (default: 1.8)
*   **`tolerance`:** Tolerance for fitting parts together (default: 0.2)
*   **`spoolcarrier`:** Height of the spool carrier/guide (default: 10)
*   **`spacermax`:** Maximum height of the spacer (default: 4)
*   **`spacermin`:** Minimum height of the spacer (default: 2)

**Additional Notes**

*   The design should be parametric, meaning I can easily change these parameters to adjust the size and features of the filament holder.
*   It should be 3D printable.
*   I'd like the design to be relatively simple and efficient, using basic OpenSCAD shapes and operations as much as possible.
*   I'm using the MCAD library for this project.

I think that covers everything. Can you create a parametric OpenSCAD file or files that meets these requirements?

[Assistant]
Spoolholder_1_2.scad:
```
// My first OpenSCAD experiment
//
// Enable/Disable Arm
arm = "true";    // [true:Enabled,false:Disabled]
// Enable/Disable roll
roll = "";   // [true:Enabled,false:Disabled]
// Enable/Disable spacer
spacer = ""; // [true:Enabled,false:Disabled]

// Height of the standing backpart of the arm
height_backlatch = 65;
// Height of the arm at the backside. Consider spindle diameter. 40mm is a sane value. Backlatch height is not automaticly adjusted.
height_arm = 30; // [200]
// Angle of the lower part of the arm. Set to zero for a straight arm and adjust height with "Height arm".
arm_angle = 9; // [45]
// Distance between the hooks the arm hangs on
distance_hooks = 50; // [200]
// diameter of the holder for the spool. Should be slightly less than the hole in the spool.
diameter_roll = 50; // [100]
// Width of spoolhole. Include a couple of mm margin yourself.
width_roll = 55; // [100]

// Arm thickness. Sturdiness is given by the top part so the default of 1.8 should be enough. Make sure you match the layerheight of the print otherwise you will get tolerance issues.
thickness = 1.8;
// Margin for fitting of parts. Depending on the accuracy of your printer 0.2 or 0.4 are good values.
tolerance=0.2;
// Height of the spoolguider.
spoolcarrier = 10; // [1:100]

//Maximum height of the spacer.
spacermax=4; // [10]
//Height at low end of the spacer.
spacermin=2; // [10]

/* [Hidden] */
cutoff_backlatch = 6;
lengte_arm = 2*width_roll+3*10;
uitsnede_spoolcarrier = -(diameter_roll/2)+spoolcarrier; //0 is 25mm, -25 is 0mm
echo(uitsnede_spoolcarrier=uitsnede_spoolcarrier);
show=true;
//roldraaiing=[-90,-90,0];
roldraaiing=[-90,0,0];
//rolplaatsing = [25,-10,(thickness)/2];
rolplaatsing = [height_backlatch+(diameter_roll/2)+5,0,uitsnede_spoolcarrier];
$fa=0.1;

module haakje(diepte_haakjes = 10,thickness=thickness){
difference(){
color("green"){
    union(){
        translate([0,-diepte_haakjes,0]){
            cube([10,diepte_haakjes*2,thickness]); //staander
        }
// Half boogje
        cylinder(r=diepte_haakjes,h=thickness);
    }}

color("red"){
     translate([-2,-3,-1])
    {
hull(){
    cube([10,3,thickness+2]);
    translate([8,-1,0]){
        cube([8+1,4,thickness+2]);
        }
   }
}}}
} //einde module haakje

module stopper(verhoging=2,thickness=thickness){
    //translate([-12,0,0]){
    union(){intersection(){    cube([10,10,thickness]);
    translate([10,10,0]){
cylinder(r=10,h=thickness);
    }

} translate([10,0,0])
cube([2,10,thickness]);
//}
}}

module stoppergat(thickness=thickness){
    translate([0,0,-tolerance])
    cube([thickness+tolerance,(diameter_roll/2)+2,10+2*tolerance]);
}

// Arm
if ( arm == "true") {
union(){
translate([2,0,0])
difference(){
cube([height_arm,lengte_arm+(10-cutoff_backlatch),thickness]); // armvlak

translate([height_arm,0,-1]){
rotate(arm_angle)
cube([2*height_arm,lengte_arm+50,thickness+2]); // schuine uitsnede
}
}

translate([0,-cutoff_backlatch,0])
difference(){
color("blue"){
    translate([0,0,0]){
        cube([height_backlatch,10,thickness]); //staander
    }
    translate([0,0,0])
    haakje();

    translate([distance_hooks,0,0])
    haakje();
    }
        translate([height_arm,+cutoff_backlatch,-1]){
        cube([height_backlatch-2,12,thickness+2]); //cutout voor staander en onderste haakje
    }
}

translate([-10,width_roll+(10-cutoff_backlatch),0])
//scale([1,-1,1])
stopper();

translate([-10,width_roll+20+(10-cutoff_backlatch),0])
scale([1,-1,1])
stopper();

translate([-10,2*width_roll+20+(10-cutoff_backlatch),0])
//scale([1,-1,1])
stopper();
}
}

//
// Top Cylinder
//
if ( roll == "true") {

translate(rolplaatsing) // lelijke translate van 0,95 omdat cirkel centered is op z as. is thickness- (tolerance/2)
rotate(roldraaiing){
    difference(){
        translate([0,0,2])
        cylinder( r=diameter_roll/2, h=lengte_arm+6);
        {translate([-(diameter_roll/2)-1,uitsnede_spoolcarrier,-1]){
           cube([diameter_roll+2,(diameter_roll)+2,lengte_arm+10+2]);
        }
    translate([-(thickness+tolerance)/2,-(diameter_roll/2)+2,-1]){
        cube([thickness+tolerance,diameter_roll,lengte_arm+2]);}

    translate([-(thickness+tolerance)/2,-(diameter_roll/2)-2,0])
        stoppergat();
    translate([-(thickness+tolerance)/2,-(diameter_roll/2)-2,width_roll+10])
        stoppergat();
    translate([-(thickness+tolerance)/2,-(diameter_roll/2)-2,width_roll+20])
        stoppergat();
    translate([-(thickness+tolerance)/2,-(diameter_roll/2)-2,2*width_roll+30])
        stoppergat();
    }
    }}
}
//
// Spacer
//

if ( spacer == "true") {

translate([height_backlatch+diameter_roll+50,0,0]){
   rotate([0,0,90])
difference(){
    hull(){
cube([10,20+thickness+2*tolerance,spacermax]);
cube([height_backlatch,20+thickness+2*tolerance,spacermin]);
}
translate([6,(10),-1])
{
cube([height_backlatch+2,thickness+2*tolerance,10]);
}}}
  echo("Spacer enabled");}
```


[User]
I need a parametric OpenSCAD design for an atrium drain grate. It should be customizable for different drain sizes and have a few key features.

Here's the basic idea: the drain grate has a top part with spokes, a base that sits on the drain pipe, and a slightly flared solid base. I've got a few dimensions in mind but I want them to be easily changed using variables.

The top part should have a circular rim with spokes going from the outer rim down to the inside of the drain. The rim should have some thickness, and the spokes should be angled. I would also like a smaller center opening.

The base has a lip that extends out from the circular base. I want that lip to be variable, as well as the base height. There's also a solid part extending down from the base, a kind of solid cylinder. The inside of that solid cylinder should be hollow.

I'd like to have the option to change the number of spokes, the diameters of the top and bottom sections, the heights of various parts and the thickness of the grate itself. Basically, everything in the provided `Drain_Grate_custom.scad` file should be customizable.

The final result should be a single, combined, printable mesh. I want to use the standard OpenSCAD libraries, not external ones.

[Assistant]
Drain_Grate_custom.scad:
```
// Thickness of drain cover
thk = 3.75;

// Diameter of top of grate
top_diameter = 58;

// Diameter of bottom of grate
bottom_diameter = 74;

// Grate height
top_height = 30;

// Base height
bottom_height=20;

// Base lip width
base_extension = 8;

// Number of spokes
num_spokes = 24;

$fn = 80;

drain();

// Individual spoke for drain cover (to be repeated and rotated)
module spoke() {
	translate( [bottom_diameter/2, thk/2, 0] )
		rotate( [90, 0, 0] )
			linear_extrude( height=thk )
                // Use -.1 to nudge down
				polygon( points=[ [-thk, -.1],
	 				[-bottom_diameter/2+top_diameter/2-thk/2, top_height-thk],
					[-bottom_diameter/2+top_diameter/2, top_height],
                    // Use -.1 to nudge down
					[0, -.1] ] );
}

// Drain cover
module drain() {

	// create spokes
	for (snum=[1:num_spokes]) {
		rotate( [0, 0, snum*(360/num_spokes)] )
			spoke(top_diameter, bottom_diameter, top_height);
	}

	// top
	num_top_spokes = ceil(num_spokes/2);
	translate( [0, 0, top_height-thk] ) {
		difference() {
			cylinder( d=top_diameter, h=thk );
			cylinder( d=top_diameter-thk*2, h=thk );
		}

		union() {
			for (snum=[1:num_top_spokes]) {
				rotate( [0, 0, snum*(360/num_top_spokes)] )
					translate( [0, -thk/2, 0] )
							cube( [top_diameter/2, thk, thk] );
			}
			cylinder( d=top_diameter/3, h=thk );
		}

	}

	// solid base
	// Note - the 0.1 fudge is to make this 2-Manifold compliant
	translate( [0, 0, -thk-0.1] ) {
		difference() {
			cylinder( d=bottom_diameter+base_extension*2, h=thk+0.1 );
			cylinder( d=bottom_diameter-thk*2, h=thk+0.1 );
		}
	}

	// solid bottom
	translate( [0, 0, -thk-bottom_height] ) {
		difference() {
			cylinder( d=bottom_diameter, h=bottom_height );
			cylinder( d=bottom_diameter-thk*2, h=bottom_height );
		}
	}

}

```


[User]
Hey, I'm looking to create a parametric V-block and clamp for holding cylindrical workpieces, and I'd like the design to be in OpenSCAD. I have some specific ideas in mind, and I'm hoping you can help me put them together into a functional design. I'll describe the various pieces and parameters, and maybe we can make this thing really customizable.

First off, the main V-block should be a rectangular prism with a V-groove cut into the top, centered along its length. The V-groove should have a configurable width and depth, and I'd like to be able to control the width and depth of some small notches in the bottom of the groove as well. Also, the sides of the V-block need to have some notches that will mate with the clamp. I'd like to be able to control the depth, width, and tolerance of these notches too.

For the clamp, I'd like it to be a curved shape that fits over the V-block. It needs to have side walls that overlap with the V-block, and guides that slide into the side notches on the block. The thickness, width, and height of the clamp should be configurable. I also want to have a nut and bolt support section on top of the clamp, with adjustable thickness and width. I need to also specify the diameter and the length of the bolt used to secure the work piece. I need to specify the nut width and thickness also as these values will be used to create the correct space for the nut to sit on the clamp. I want to be able to specify whether the bolt hole will be vertical (for clamping round stock) or at a 45 degree angle (for clamping rectangular stock).

Finally, I'd like to have a bolt knob at the end of the bolt. The diameter and height should be configurable, as well as the number, depth, width, and angle of knurls on the knob.

The whole assembly should be created with customizable parameters for all these dimensions.  I don't need any code to be generated for any libraries, but it might be helpful for you to keep in mind that I'm not using any existing libraries - I need all the code generated for everything. Can you generate a set of parametric OpenSCAD files to realize this design?

[Assistant]
vBlock_v2.scad:
```
/***********************************************************************
Name ......... : vBlock.scad
Description....: Parametric V Block and Clamp for Cylindrical Work
Author ....... : Garrett Melenka
Version ...... : V1.0 2016/04/29
Licence ...... : GPL
***********************************************************************/
//Part Display Options [0:All, 1:V Block, 2: Clamp, 3: Bolt Knob]
part = 0;

//Clamp Option [0: For Clamping Round Materials, 45: For Clamping Rectangular Materials]
clampAngle = 0;

//V Block Dimensions
//Total V Block Length
Length = 75;
//V Block Height
Height = 50;
//V Block Width
Width = 50;
//Width of v Groove
vGrooveWidth = 40;
//v Groove notch depth
vNotchDepth = 2;
//v Groove notch width
vNotchWidth = 2;

//Notches in side of v block to allow for the attachment of v block clamp
sideDepth = 10;
sideWidth = 10;
//Tolerance of side notches to ensure good fit between clamp and v block
clampSideTolerance = 0.1;

//V Block Clamp Dimensions
//Thickness of Clamp
clampThickness = 10;
//Width of Clamp
clampWidth = 18.75;
//Height of Clamp above v block
clampHeight = 35;

//Dimensions of nut/ bolt support on top of v block clamp
//Thickness of Nut support
clampNutSupportThickness = 15;
//Width of nut Support
clampNutSupportWidth = 20;

//Dimensions of Nut and Bolt for securing parts in V block
//Diameter of bolt
boltDiameter = 8.25;
boltLength = 50;

//Width of nut- measure corner to corner distance on nut
nutWidth = 14.75;
//Nut thickness- measure thickness of nut
nutThickness = 7.0;

//Diameter of Bolt Knob
boltKnobDiameter = 20;
//Height of Bolt Knob;
boltKnobLength = 20;
//Number of knurls around Knob Diameter
boltKnobKnurlNum = 15;
//Depth of knurls
boltKnobKnurlDepth = 1;
//Width of knurls
boltKnobKnurlWidth = 1;
//Angle of knurling
boltKnobKnurlAngle = 100;

//********************************************************************************
//V Block
//********************************************************************************
module vBlock()
{

    vSide = vGrooveWidth*cos(45);
    difference(){
    difference(){

        //main body of v block
    cube(size = [Width, Length, Height], center = true);

        //Main Body V
        translate([0,0,Height*0.5]){
            rotate([0, 45, 0]){
                //echo(vSide);
            cube(size = [vSide, Length, vSide], center = true);
            }
        }

        translate([0,0,-Height*0.5]){
            rotate([0, 45, 0]){
                //echo(vSide);
            cube(size = [vSide, Length, vSide], center = true);
            }
        }
    }
    //V Notches
    translate([0,0, Height*0.5 - vSide*cos(45)]){
    cube(size = [vNotchWidth, Length, vNotchDepth], center = true);
    }

    translate([0,0, -(Height*0.5 - vSide*cos(45))]){
    cube(size = [vNotchWidth, Length, vNotchDepth], center = true);
    }

    //Side Grooves to allow for clamp
    translate([Width*0.5, 0, 0]){
    cube(size = [sideDepth, Length, sideWidth], center = true);
    }

    translate([-Width*0.5, 0, 0]){
    cube(size = [sideDepth, Length, sideWidth], center = true);
    }
    }

}

//********************************************************************************
//V Block Clamp
//********************************************************************************
module vClamp(){

    union(){
    difference(){
    difference()
    {
    union(){
    rotate([90,0,0]){
        difference(){
        cylinder(r = (Width+clampThickness)*0.5, h = clampWidth, center = true);

       cylinder(r = Width*0.5, h = Length, center = true);
       translate([0, -Width*0.5, 0]){
        cube(size = [Width*3*0.5, Width*2*0.5, clampWidth], center = true);
       }
        }
    }

    rotate([0,clampAngle,0]){
    translate([0,0, (Width+clampThickness)*0.5]){
    cube(size = [clampNutSupportWidth, clampWidth, clampNutSupportThickness], center = true);
    }
    }
    }
    rotate([0,clampAngle,0]){
    //Bolt Hole
    translate([0,0,(Width + clampThickness)*0.5])
    //translate([0,0,0])
    {
    cylinder(r = boltDiameter*0.5, h =  clampNutSupportThickness*2, center = true);
    }
    //Nut
    translate([0,0, (Width+clampThickness)*0.5+clampNutSupportThickness*0.5-nutThickness*0.5]){
    cylinder(r = nutWidth*0.5, h= nutThickness, center = true, $fn = 6);
    }
    }
    }
    }

    //Left Side Wall
    translate([-0.5*(Width+0.5*(Width+clampThickness-Width)), 0, -clampHeight*0.5]){
    cube(size = [0.5*(Width+clampThickness-Width), clampWidth, clampHeight], center = true);
    }

    //Right Side Wall
    translate([0.5*(Width+0.5*(Width+clampThickness-Width)), 0, -clampHeight*0.5]){
    cube(size = [0.5*(Width+clampThickness-Width), clampWidth, clampHeight], center = true);
    }

    //Right V Block Guide
    translate([Width*0.5-sideWidth*0.25, 0, -clampHeight+sideWidth*0.5]){
    cube(size = [sideDepth*0.5, clampWidth, sideWidth-clampSideTolerance], center = true);
    }
    //Left V Block Guide
    translate([-Width*0.5+sideWidth*0.25, 0, -clampHeight+sideWidth*0.5]){
    cube(size = [sideDepth*0.5, clampWidth, sideWidth-clampSideTolerance], center = true);
    }
    }
}

//********************************************************************************
//Nut which is glued into the V Block Clamp
//********************************************************************************
module nut()
{
    difference(){
    cylinder(r=nutWidth*0.5, h = nutThickness, center = true, $fn=6);

    cylinder(r = boltDiameter*0.5, h=nutThickness, center = true);
    }

}
//********************************************************************************
//Bolt Used to secure round Stock
//********************************************************************************
module bolt()
{

    cylinder(r=boltDiameter*0.5, h = boltLength, center = true);

    translate([0,0,boltLength*0.5]){
    cylinder(r=nutWidth*0.5, h = nutThickness, center = true, $fn=6);
    }

}
//********************************************************************************
//Knob that allows for the bolt to be adjusted
//********************************************************************************
module boltKnob()
{

    if(boltKnobDiameter> nutWidth)
    {

    difference(){
    cylinder(r=boltKnobDiameter*0.5, h=boltKnobLength, center = true);
    translate([0,0,-(boltKnobLength*0.5)+nutThickness*0.5]){
    cylinder(r= nutWidth*0.5, h = nutThickness, center = true, $fn=6);
    }

    //translate([boltKnobDiameter*0.5, 0,0])

    for (i = [1:1:boltKnobKnurlNum])

    rotate([0,0,360*i/boltKnobKnurlNum]){
    {

        linear_extrude(height = boltKnobLength, center = true, twist = boltKnobKnurlAngle){
            translate([boltKnobDiameter*0.5, 0,0]){
        square(size = [boltKnobKnurlDepth,boltKnobKnurlWidth], center = true);
            }
        }

        linear_extrude(height = boltKnobLength, center = true, twist = -boltKnobKnurlAngle){
            translate([boltKnobDiameter*0.5, 0,0]){
        square(size = [boltKnobKnurlDepth,boltKnobKnurlWidth], center = true);
            }
        }
    //cube(size = [2,2, boltKnobLength], center = true);
    }
    }
}
}
    else
    {
        echo("Knob diameter is less than nut width");
    }
}

//********************************************************************************
//Rendering and Display of Parts
//********************************************************************************
$fn = 60;
if (part == 0)
{
//Display V Block
vBlock();

//Display Clamp
translate([0,0,clampHeight-sideWidth*0.5])
{
color("blue")
vClamp();

}

if (clampAngle == 0)
{

   //Display Nut
translate([0,0,clampHeight+clampNutSupportThickness+clampThickness+ nutThickness*0.5]){
color("green")
nut();
}
//Display Bolt
translate([0,0,clampHeight+clampNutSupportThickness+boltLength*0.5]){
color("RosyBrown")
bolt();
}

//Display Bolt Knob
color("Teal")
translate([0,0, clampHeight+clampNutSupportThickness+boltLength]){
boltKnob();
}

//Display Example cylinder clamped using V Block
translate([0,0,Height*0.5+0.5*5]){
rotate([90,0,0])
{
color("red")
    cylinder(r=vGrooveWidth*0.5, h = Length, center = true);
}
}
}
//Display cube stock and clamp with 45 degree angle for nut and bolt
if (clampAngle == 45)
{

    vSide = vGrooveWidth*cos(45);
    translate([0,0, Width*0.5])
    {
    rotate([0,45,0]){
    color("red")
    cube(size = [vSide, Length, vSide], center = true);
    }
    }

}

}
//V-block Rendering Oriented for Printing
else if (part == 1)
{
    translate([0,0,Length*0.5]){
    rotate([90,0,0]){
    vBlock();
    }
    }
}
//Clamp Rending- Oriented for Printing
else if (part == 2)
{
    rotate([90,0,0]){
    vClamp($fn = 50);
    }
}

//Bolt Knob Rending- Oriented for Printing
else if (part == 3)
{

    boltKnob();

}
```


[User]
Hi, I'm looking to create a customizable building block system in OpenSCAD, kind of like a modular construction set. I need to be able to generate blocks of various sizes, with optional holes for bolting things together. The system should be parametric, allowing me to easily adjust dimensions, hole sizes and spacing, and even create flat plates or blocks with large central openings (either circular or rectangular). I've seen some good examples before, but I need to be able to tweak things to suit my needs.

Here's what I'm envisioning:

**Core Block Features:**

*   **Block Size:** I need to specify the base size of a single cubic block (e.g., 0.5 inches or 12.7 mm).
*   **Units:** I need to be able to switch between millimeters and inches.
*   **Holes:**
    *   I need to specify the diameter of the holes. If set to 0, there should be no holes generated.
    *   I need to specify the spacing between the holes. If set to 0, the spacing should be the same as the block size.
    *   Holes need to be generated on all faces of a block.
*   **Block Count:** I need to be able to easily specify the number of blocks along each axis (x, y, z).

**Specific Block Types:**

1.  **Standard Blocks:** This will be the base building block, where I can set the number of blocks in each direction, as mentioned above, and also the hole size and spacing.
2.  **Flat Plates:**  I need to be able to create flat plates of varying thickness and dimensions. The holes should still be an option.
3.  **Blocks with Circular Openings:**  I need to be able to specify the diameter of a central circular opening in a block and an optional margin around that circle, and be able to set the number of blocks in each direction for this block type. This means that the block will have holes except where the circular opening is.
4.  **Blocks with Rectangular Openings:** Similar to the circular opening, but I need to be able to specify the width and height of a central rectangular opening and an optional margin, as well as the block count. Again, the block will have holes except where the rectangular opening is.
5.  **Blocks to Fit Something:** I need a tool that will create a block (or a set of blocks) just big enough to fit an object of specific dimensions, along with an option for the minimum number of blocks along each axis. The holes should be generated everywhere except where the object would go.

I need all of this to be parametric so that I can easily change any one of these dimensions or features in the OpenSCAD file.

Could you help me create the OpenSCAD code for this system? I am not looking to use any libraries like BOSL or MCAD.

[Assistant]
roblocks_base.scad:
```
// Customizable blocks with customizable holes.
//
// The idea here is that you make your own building system for making re-configurable CNC,
// other multi-axis bots, and anything else you can dream up.
//
// Licensed under Creative Commons: Attribution
//		http://creativecommons.org/licenses/by-sa/3.0/
// For attribution you can direct people to the original file on thingiverse:
// 		http://www.thingiverse.com/thing:1532686

// ================ variables

//CUSTOMIZER VARIABLES

/* [Main] */

units=1;// [0:mm,1:inches]

// Size of each cubical block (in selected units)
block_size=0.5;

// (in selected units) if 0, no holes (hint 1/4 inch hole is perfect for 1/4 inch bolt)
hole_dia=0.25;

// (in selected units) if 0, use block_size
hole_spacing=0;

// Fill out the appropriate section after selecting what to generate
generate=0; // [0:blocks,1:flat plate,2:blocks with a circular opening,3:blocks with a rectanglular opening,4:blocks to fit something a certain size]

/* [Blocks] */

// number of blocks x direction
num_x=1;

// number of blocks y direction
num_y=1;

// number of blocks z direction
num_z=1;

/* [Flat Plate] */

// number of blocks x direction
flat_num_x=1;

// number of blocks y direction
flat_num_y=1;

// Thickness of the plate (in selected units)
flat_thickness=0.128;

/* [Blocks w Circlular Opening] */

// number of blocks x direction
d_num_x=5;

// number of blocks y direction
d_num_y=5;

// number of blocks z direction
d_num_z=1;

// diameter of center hole (in units)
diameter=0.866;

// require this much space around center hole (in mm)
diameter_padding=2;

/* [Blocks w Rectangular Opening] */

// number of blocks x direction
r_num_x=3;

// number of blocks y direction
r_num_y=4;

// number of blocks z direction
r_num_z=1;

// x size of the center hole (in units)
rect_x=0.5;

// y size of the center hole (in units)
rect_y=0.99;

// leave a margin of at least this big (in mm) around the opening
rect_margin=0;

/* [Blocks to Fit Something] */

// x size of the thing (in measurements)
x=1;

// y size of the thing (in measurements)
y=1;

// min number of blocks in x direction
min_nx=1;

// min number of blocks in y direction
min_ny=1;

// min number of blocks in z direction
min_nz=1;

/* [Hidden] */

/* pretty standard stuff */
function in2mm(in)=25.4*in;
function mm2in(mm)=mm/25.4;
function u(val)=units==0?val:in2mm(val); // whatever the selected units are in

/* math tools to convert a freeform value into exact blocks */
function roundToNearest(value,step)=round(value/step)*step;
function ceilToNearest(value,step)=ceil(value/step)*step;
function floorToNearest(value,step)=floor(value/step)*step;

/* Some block conversion functions (though they are pretty obvious) */
function actualToblocks(value,blocksize)=value/blocksize;
function blocksToActual(value,blocksize)=value*blocksize;

/*
creates an array of circles

dia - mm
spacing - mm
size - [x,y] mm
maskOffDiameter - do not put any holes inside a circle of this size (z axis only)
maskOffRectangle - [x,y] do not put any holes inside this rectangle (z axis only)

NOTE: this will allow holes right up to the very edge of the mask.  You may want to oversize your mask
by a certain margin to ensure that you don't get tangents.
*/
module circleArray(dia,spacing,size,maskOffDiameter=-1,maskOffRectangle=[-1,-1],hole_offs=[0,0]){
	maskOffDiameter=maskOffDiameter>0?maskOffDiameter+dia/2:maskOffDiameter; // ensure none of hole gets into maskOffDiameter
	union() for(x=[hole_offs[0]:spacing:size[0]/2]){
		for(y=[hole_offs[1]:spacing:size[1]/2]){
			if((maskOffRectangle[0]<=0 || maskOffRectangle[1]<=0 || x-dia/2>=maskOffRectangle[0]/2 || y-dia/2>=maskOffRectangle[1]/2) && (maskOffDiameter<0 || x*x+y*y>maskOffDiameter/2*maskOffDiameter/2)){
				translate([ x, y]) circle(r=dia/2);
				translate([ x,-y]) circle(r=dia/2);
				translate([-x, y]) circle(r=dia/2);
				translate([-x,-y]) circle(r=dia/2);
			}
			/*else
			{
				echo(str("masked off ",x," ",y));
				echo(x-dia/2>=maskOffRectangle[0]/2 , y-dia/2>=maskOffRectangle[1]/2, (maskOffDiameter<0 || x*x+y*y>maskOffDiameter/2*maskOffDiameter/2));
			}*/
		}
	}
}

/*
Creates a (centered) array of cylinders

hole_dia - mm
spacing - mm
size - [x,y] mm
maskOffDiameter - do not put any holes inside a circle of this size (z axis only)
maskOffRectangle - [x,y] do not put any holes inside this rectangle (z axis only)
*/
module cylinderArray(dia,spacing,size,extrude=0,maskOffDiameter=-1,maskOffRectangle=[-1,-1],hole_offs=[0,0]){
	translate([0,0,-extrude/2]) linear_extrude(extrude) circleArray(dia,spacing,size,maskOffDiameter,maskOffRectangle,hole_offs);
}

/*
Drill evenly-spaced holes in all sides of a cube

box - [x,y,z] in mm for the cube
hole_dia - mm
spacing - mm
maskOffDiameter - do not put any holes inside a circle of this size (z axis only)
maskOffRectangle - [x,y] do not put any holes inside this rectangle (z axis only)
*/
module blockHoles(box,hole_dia,hole_spacing,maskOffDiameter=-1,maskOffRectangle=[-1,-1],hole_offs=[0,0,0],$fn=32){
	h=max(box)*2; // keep life simple
	// holes pointing along x axis
	if(box[2]>hole_dia && box[1]>hole_dia) rotate([0,90,0]) {
		cylinderArray(hole_dia,hole_spacing,[box[2],box[1]],h,hole_offs=[hole_offs[2],hole_offs[1]]);
	}
	// holes pointing along y axis
	if(box[0]>hole_dia && box[2]>hole_dia) rotate([90,0,0]) {
		cylinderArray(hole_dia,hole_spacing,[box[0],box[2]],h,hole_offs=[hole_offs[0],hole_offs[2]]);
	}
	// holes pointing along z axis
	if(box[0]>hole_dia && box[1]>hole_dia) {
		cylinderArray(hole_dia,hole_spacing,[box[0],box[1]],h,maskOffDiameter,maskOffRectangle,hole_offs=[hole_offs[0],hole_offs[1]]);
	}
}

/*
Creates a block a given number of blocks on each size

nXnYnZ - [x,y,z] the number of blocks in each direction
blocksize - conversion factor = size of one block
hole_dia - mm
spacing - mm (if unspecified, use blocksize)
maskOffDiameter - do not put any holes inside a circle of this size (z axis only)
maskOffRectangle - [x,y] do not put any holes inside this rectangle (z axis only)
*/
module roBlock(nXnYnZ,blocksize,hole_dia,hole_spacing,maskOffDiameter=-1,maskOffRectangle=[-1,-1]){
	hole_spacing=hole_spacing>0?hole_spacing:blocksize;
	hole_offs=[nXnYnZ[0]%2==0?(blocksize)/2:0,nXnYnZ[1]%2==0?(blocksize)/2:0,nXnYnZ[2]%2==0?(blocksize)/2:0];
	difference(){
		// base
		cube([nXnYnZ[0]*blocksize,nXnYnZ[1]*blocksize,nXnYnZ[2]*blocksize],center=true);
		// screw holes
		if(hole_dia>0){
			blockHoles([nXnYnZ[0]*blocksize,nXnYnZ[1]*blocksize,nXnYnZ[2]*blocksize],hole_dia,hole_spacing,maskOffDiameter,maskOffRectangle,hole_offs);
		}
	}
}

/*
Convenience function to create a flat plate.

nXnY - [x,y] the number of blocks in each direction
blocksize - conversion factor = size of one block
hole_dia - mm (if unspecified, no holes)
hole_spacing - mm (if unspecified, use blocksize)
thickness - mm
maskOffDiameter - do not put any holes inside a circle of this size (z axis)
maskOffRectangle - [x,y] do not put any holes inside this rectangle (z axis)
*/
module roPlate(nXnY,blocksize,hole_dia=0,hole_spacing=0,thickness,maskOffDiameter=-1,maskOffRectangle=[-1,-1]){
	hole_spacing=hole_spacing>0?hole_spacing:blocksize;
	difference(){
		// base
		cube([nXnY[0]*blocksize,nXnY[1]*blocksize,thickness],center=true);
		// screw holes
		if(hole_dia>0){
			translate([nXnY[0]%2==0?-blocksize/2:0,nXnY[1]%2==0?-blocksize/2:0,-thickness/2]) blockHoles([nXnY[0]*blocksize,nXnY[1]*blocksize,0],hole_dia,hole_spacing,maskOffDiameter,maskOffRectangle);
		}
	}
}

/*
creates a block that can accomidate a specific thing
(automatically maskes this area off for holes)

size - [x,y] mm
blocksize - conversion factor = size of one block
hole_dia - mm (if unspecified, no holes)
hole_spacing - mm (if unspecified, use blocksize)
minnXnYnZ - minimum number of blocks in each direction
*/
module blockToAccomodate(size,blocksize,hole_dia=0,hole_spacing=0,minnXnYnZ=[1,1,1]){
	x=max(ceilToNearest(size[0],blocksize)/blocksize,minnXnYnZ[0],1);
	y=max(ceilToNearest(size[1],blocksize)/blocksize,minnXnYnZ[1],1);
	z=max(minnXnYnZ[2],1);
	roBlock([x,y,z],blocksize,hole_dia,hole_spacing,maskOffRectangle=size);
}

if(generate==0){ // blocks
	roBlock([num_x,num_y,num_z],u(block_size),u(hole_dia),u(hole_spacing));
}else if(generate==1){ // flat plate
	roBlock([flat_num_x,flat_num_y],u(block_size),u(hole_dia),u(hole_spacing));
}else if(generate==2){ // blocks with a circular opening
	difference(){
		roBlock([d_num_x,d_num_y,d_num_z],u(block_size),u(hole_dia),u(hole_spacing),u(diameter)+diameter_padding*2);
		translate([0,0,-(d_num_z*u(block_size))]) cylinder(r=u(diameter)/2,h=(d_num_z*u(block_size))*2);
	}
}else if(generate==3){ // blocks with a rectanglular opening
	difference(){
		roBlock([r_num_x,r_num_y,r_num_z],u(block_size),u(hole_dia),u(hole_spacing),[u(rect_x)+rect_margin*2,u(rect_y)+rect_margin*2]);
		cube([u(rect_x),u(rect_y),(r_num_z*u(block_size))*2],center=true);
	}
}else if(generate==4){ // blocks by a certain size
	blockToAccomodate([u(x),u(y)],u(block_size),u(hole_dia),u(hole_spacing),[min_nx,min_ny,min_nz]);
}

```


[User]
Hey, I'm looking for some help creating a set of dominoes using OpenSCAD. I want to be able to customize the dominoes, especially the size and numbers. I'd like to generate either a single domino or the entire set, though I know generating the whole set might take a while.

Here's what I need:

*   **Domino Size:** I need to be able to set the width and thickness of the dominoes. The height should be twice the width.
*   **Corner Rounding:** I want the edges to be rounded with a specified radius.
*   **Number Range:** The dominoes should be able to go up to double twelve. That's 0-0 up to 12-12.
*   **Number Display:**
    *   Use dots for numbers 1 to 9. I need to specify the dot radius.
    *   For numbers 10, 11, and 12, use smaller dots to fit all the dots on the tile. Again, I should be able to specify the radius of these smaller dots.
*   **Single vs. Full Set:** I'd like the option to generate just one domino (specifying the two numbers) or the entire set. If the full set is generated, I don't need it to be laid out for printing; it's fine if they are just all generated and take a while to render.
*   **Library Usage:** Please use the BOSL2 library if possible. I am generally used to it and it has great tools for object placement, rounding, and generally better geometry options.

It would be great if the code was parametric so I could easily adjust these values. Let me know if you have any questions.

[Assistant]
dominos.scad:
```
//==================================
//  Double Twelves Domino Generator
//
//  Written by Andrew Moore, May 2016
//      http://www.thingiverse.com/TacPar
//      For questions, suggestions, or whatever,
//      please PM me through thingiverse.
//
//  This program will generate numbered Domino Tiles
//
//==================================

//Global Constants
//==================================
$fn=30;
inch = 25.4;

//User-selectable Global Variables
//==================================
//double six = 28 tiles
//double nine = 55 tiles
//double twelve = 91 tiles

max_number = 12; //highest domino tile number
first_number = 9;
second_number = 11;
tile_width = 1*inch;
tile_thickness = 3/8*inch;

//this rounds the edges of the tiles.  Smaller number = sharper edges
corner_radius = 1/16*inch;

//Dot size for 1-9
dot_radius = 2;

//Dot size for numbers larger than 9
small_dot_rad = 1.5;

//set to true if you want to wait a really long time for a huge part
print_all = false;

//Calculated Global Values... don't change these
tile_height = 2*tile_width;

module main()
{
    if (print_all)
    {
        for(i=[0:1:max_number])
        {
            for(j=[0:1:max_number])
            {
                if (j>=i)
                {
                    translate([i*1.5*tile_width,j*1.5*tile_height,0]) domino(number1=i, number2=j);
                }
            }
        }
    }else
    {
        domino(number1=first_number, number2=second_number);
    }
}

module domino(number1 = 0, number2 = 0)
{
	difference()
	{
		positives();
		negatives();
	}

	module positives()
	{
		translate([corner_radius,corner_radius,corner_radius]) minkowski()
		{
			cube([tile_width-2*corner_radius,tile_height-2*corner_radius,tile_thickness-2*corner_radius]);
			sphere(r=corner_radius);
		}

	}
	module negatives()
	{
		//cut the center line on the domino
		translate([corner_radius,tile_height/2, tile_thickness])
            rotate([0,90,0])
            cylinder(r=0.5, h=tile_width-2*corner_radius);

		//Place the numbered dots centered on top tile half
		translate([tile_width/2,3/4*tile_height,tile_thickness])
            number_dots(count = number1);

		//Place numbered dots centered on bottom half
		translate([tile_width/2,1/4*tile_height,tile_thickness])
            number_dots(count = number2);

	}

	module number_dots(count = 0)
	{
		//set up margins for dot field
        dot_field_width = tile_width-6*corner_radius;
		dot_field_height = dot_field_width;

		if (count == 1)
		{
			sphere(r=dot_radius);
		}
		if (count == 2)
		{
			translate([-1/3*dot_field_width,1/3*dot_field_width,0]) sphere(r=dot_radius);
			translate([1/3*dot_field_width,-1/3*dot_field_width,0]) sphere(r=dot_radius);
		}
		if (count == 3)
		{
			sphere(r=dot_radius);
			translate([-dot_field_width/2,dot_field_width/2,0]) sphere(r=dot_radius);
			translate([dot_field_width/2,-dot_field_width/2,0]) sphere(r=dot_radius);
		}
		if (count ==4)
		{
			translate([-1/3*dot_field_width,1/3*dot_field_width,0]) sphere(r=dot_radius);
			translate([1/3*dot_field_width,-1/3*dot_field_width,0]) sphere(r=dot_radius);
			translate([-1/3*dot_field_width,-1/3*dot_field_width,0]) sphere(r=dot_radius);
			translate([1/3*dot_field_width,1/3*dot_field_width,0]) sphere(r=dot_radius);
		}
		if (count ==5)
		{
			sphere(r=dot_radius);
			translate([-1/2*dot_field_width,1/2*dot_field_width,0]) sphere(r=dot_radius);
			translate([1/2*dot_field_width,-1/2*dot_field_width,0]) sphere(r=dot_radius);
			translate([-1/2*dot_field_width,-1/2*dot_field_width,0]) sphere(r=dot_radius);
			translate([1/2*dot_field_width,1/2*dot_field_width,0]) sphere(r=dot_radius);
		}
		if (count == 6)
		{
			translate([dot_field_width/2,0,0])sphere(r=dot_radius);
			translate([-dot_field_width/2,0,0])sphere(r=dot_radius);
			translate([dot_field_width/2,dot_field_width/2,0])sphere(r=dot_radius);
			translate([-dot_field_width/2,-dot_field_width/2,0])sphere(r=dot_radius);
			translate([-dot_field_width/2,dot_field_width/2,0]) sphere(r=dot_radius);
			translate([dot_field_width/2,-dot_field_width/2,0]) sphere(r=dot_radius);
		}
		if (count == 7)
		{
			sphere(r=dot_radius);
			translate([dot_field_width/2,0,0])sphere(r=dot_radius);
			translate([-dot_field_width/2,0,0])sphere(r=dot_radius);
			translate([dot_field_width/2,dot_field_width/2,0])sphere(r=dot_radius);
			translate([-dot_field_width/2,-dot_field_width/2,0])sphere(r=dot_radius);
			translate([-dot_field_width/2,dot_field_width/2,0]) sphere(r=dot_radius);
			translate([dot_field_width/2,-dot_field_width/2,0]) sphere(r=dot_radius);
		}
		if (count == 8)
		{
			translate([0,1/3*dot_field_width,0])
                sphere(r=dot_radius);
			translate([0,-1/3*dot_field_width,0])
                sphere(r=dot_radius);
			translate([dot_field_width/2,0,0])
                sphere(r=dot_radius);
			translate([-dot_field_width/2,0,0])
                sphere(r=dot_radius);
			translate([dot_field_width/2,dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([-dot_field_width/2,-dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([-dot_field_width/2,dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([dot_field_width/2,-dot_field_width/2,0])
                sphere(r=dot_radius);
		}
        if (count == 9)
		{
			sphere(r=dot_radius);
			translate([0,-dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([0,dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([dot_field_width/2,0,0])
                sphere(r=dot_radius);
			translate([-dot_field_width/2,0,0])
                sphere(r=dot_radius);
			translate([dot_field_width/2,dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([-dot_field_width/2,-dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([-dot_field_width/2,dot_field_width/2,0])
                sphere(r=dot_radius);
			translate([dot_field_width/2,-dot_field_width/2,0])
                sphere(r=dot_radius);
		}
        if (count == 10)
		{
            translate([dot_field_width/2,0,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,0,0])
                sphere(r=small_dot_rad);
            translate([dot_field_width*3/16,0,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width*3/16,0,0])
                sphere(r=small_dot_rad);

			translate([0,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([0,dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([dot_field_width/2,dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([dot_field_width/2,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
		}
        if (count == 11)
		{
            translate([dot_field_width/2,dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,dot_field_width/2,0])
                sphere(r=small_dot_rad);
            translate([dot_field_width*3/16,dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width*3/16,dot_field_width/2,0])
                sphere(r=small_dot_rad);

            sphere(r=small_dot_rad);
			translate([-dot_field_width/2,0,0])
                sphere(r=small_dot_rad);
			translate([dot_field_width/2,0,0])
                sphere(r=small_dot_rad);

            translate([dot_field_width/2,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
            translate([dot_field_width*3/16,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width*3/16,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
		}
        if (count == 12)
		{
            translate([dot_field_width/2,dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,dot_field_width/2,0])
                sphere(r=small_dot_rad);
            translate([dot_field_width*3/16,dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width*3/16,dot_field_width/2,0])
                sphere(r=small_dot_rad);

            translate([dot_field_width/2,0,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,0,0])
                sphere(r=small_dot_rad);
            translate([dot_field_width*3/16,0,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width*3/16,0,0])
                sphere(r=small_dot_rad);

            translate([dot_field_width/2,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width/2,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
            translate([dot_field_width*3/16,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
			translate([-dot_field_width*3/16,-dot_field_width/2,0])
                sphere(r=small_dot_rad);
		}
	}
}

main();

```


[User]
Hey, I'm looking to design a customizable drawer system for organizing small electronic components. I've been using a slightly modified design but it has some limitations. I'd like to start fresh with a new design that's more flexible and robust.

Basically, I need a system with two main parts: a cabinet and drawers that fit inside it.

**Cabinet:**
*   It should be able to hold multiple drawers, stacked vertically.
*   I need to be able to specify the number of drawers.
*   The cabinet needs to have a base, back and side walls.
*   I need to define the overall dimensions of the cabinet, including height, width, and depth, but they should be calculated based on the dimensions of the drawers.
*   I want to add screw holes on the sides for mounting, and "mouse ear" type tabs for mounting on the top and bottom.

**Drawers:**
*   I need to be able to specify the dimensions of each drawer: height, width, and depth.
*   Each drawer should be divided into a grid of smaller compartments.
*   I need to be able to specify the number of rows and columns within each drawer.
*   The walls of the drawer need to have a specified thickness.
*   The dividers within a drawer should have a different, thinner wall thickness.
*   I would like to have fillets for all edges of the drawer, and inner fillets for all edges of the sub-compartments.
*   I want a small handle on the front of each drawer for pulling it out, and also a small dimple on the sides to act as a stop.
*   I want the option to add text labels on the front of the drawer and have these be either embossed or engraved.
*  I need a way to set the floor thickness of the drawer.

**General Requirements:**

*   All dimensions should be customizable via variables at the top of the file.
*   I want to be able to choose to generate either just the drawer or the cabinet, or both.
*   Clearance between the drawers and the cabinet needs to be adjustable.
*   I want to be able to control the corner radii of the drawers and the dividers.
* I'd like to include an option to turn off the fillets.
*   I'm happy to use the standard OpenSCAD libraries, I don't need to include anything extra.

I'm hoping to get this in a parametric OpenSCAD file, or maybe a few files if that's easier to manage.  Thanks!

[Assistant]
drawers_openscad.scad:
```
/*

Customizable drawer box
Gian Pablo Villamil
August 4, 2014

Marc Bailey
April 30, 2016

v1 first iteration
v2 added drawer handle
v3 added full fillets, tweaked wall thickness and corner handling
v4 added text label
v5 added hex patterning on the sides
v6 added dimple stop to drawers and box
v7 Simplified and added drawer function which takes row & col options
*/

part="drawer"; //[drawer,cabinet]
//part="cabinet"; //[drawer,cabinet]
COLS = 5; // [1,2,3,4,5,6]

// there seems to be a bug when rows > 2
ROWS = 2; // [1,2]

// (top to bottom)
DRAWER_HEIGHT = 15;

// (front to back)
DRAWER_DEPTH = 59;
DRAWER_WIDTH = 80;

// Wall thickness
WALL_THICK = 1.6;

// thickness of dividers between drawer slots
INNER_WALL_THICK = 0.6;

// How many drawers?
NUM_DRAWERS = 5;

/* [Text] */
DRAW_TEXT = "yes"; // [yes,no]

// Label on the drawer
MESSAGE = "TEXT";

// Embossed?
EMBOSSED = "yes"; // [yes,no]

/* [Hidden] */
// Text font
TEXT_FONT = "Archivo Black";

// Text height
TEXT_HEIGHT = 6;

// Text spacing
TEXT_SPACING = 1;

// Shift text to the right (negative goes left)
TEXT_SHIFT_RIGHT = 0;

//Shift text up
TEXT_SHIFT_UP = 2;

// Handle size?
HANDLE_SIZE = 10;

// Corner radius?
DRAWER_CORNER_RAD = 2;

CLEARANCE = 0.25;

// Floor thickness
FLOOR_THICK = 1.2;

// Rounded fillets?
FILLETS = true;

india = 6 ;  // inner diameter for holes in drawer cabinet
spacing = 1.2;  // spacing between holes in drawer cabinet
dia = india + (spacing * 2);

hoff = india+spacing;
voff = sqrt(pow(hoff,2)-pow(hoff/2,2));

$fn = 32;
NOFILLETS = !FILLETS;
WALL_WIDTH = WALL_THICK + 0.001; // Hack to trick Makerware into making the right number of filament widths
INNER_WALL_WIDTH = INNER_WALL_THICK + 0.001; // Hack to trick Makerware into making the right number of filament widths
INNER_FILLET = DRAWER_CORNER_RAD-(WALL_WIDTH/1.25);

BOX_HEIGHT = NUM_DRAWERS*(DRAWER_HEIGHT+CLEARANCE*2+WALL_WIDTH)+WALL_WIDTH;

//BOX_WIDTH = DRAWER_WIDTH+CLEARANCE*2+INNER_WALL_WIDTH*2;
BOX_WIDTH = DRAWER_WIDTH+CLEARANCE*2+WALL_WIDTH*2;
BOX_DEPTH = DRAWER_DEPTH+CLEARANCE+WALL_WIDTH;

thick = WALL_THICK + 0.2;
bumprad = 1;

earthick = 0.6;
earrad = 10 ;

print_part();

module print_part() {
	if (part == "drawer") {
        drawer(COLS,ROWS);
    } else {
		cabinet();
	}
}

module drawer(COLS,ROWS) {
    slot_width = (DRAWER_WIDTH-(COLS-1)*INNER_WALL_WIDTH-2*WALL_WIDTH)/COLS;
    slot_depth = (DRAWER_DEPTH-(ROWS+1)*WALL_WIDTH)/ROWS;

    difference() {
		drawerbase();
    for (col=[0:ROWS-1])
        for (row=[0:COLS-1])
            translate([WALL_WIDTH+slot_width/2+(INNER_WALL_WIDTH+slot_width)*row-DRAWER_WIDTH/2,WALL_WIDTH+slot_depth/2+(WALL_WIDTH+slot_depth)*col-DRAWER_DEPTH/2,FLOOR_THICK+DRAWER_CORNER_RAD/2])
            roundedBox([slot_width,slot_depth,DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);

    }
};

module cabinet() {
	translate([-BOX_WIDTH/2,-BOX_DEPTH/2,0])
	union () {
		difference () {
			cube([BOX_WIDTH,BOX_DEPTH,BOX_HEIGHT]);
			for (i=[0:NUM_DRAWERS-1]) {
				translate([WALL_WIDTH,-1,i*(DRAWER_HEIGHT+CLEARANCE*2+WALL_WIDTH)+WALL_WIDTH])
				cube([DRAWER_WIDTH+CLEARANCE*2,DRAWER_DEPTH+CLEARANCE+1,DRAWER_HEIGHT+CLEARANCE*2]);
			}
		translate([0,BOX_DEPTH+0.1,0])
		holes(BOX_WIDTH,BOX_HEIGHT);
		rotate([0,0,90]) translate([hoff/2,0.1,0]) holes(BOX_DEPTH,BOX_HEIGHT);
		rotate([0,0,90]) translate([hoff/2,-BOX_WIDTH+WALL_WIDTH+0.1,0]) holes(BOX_DEPTH,BOX_HEIGHT);
		}
		for (i=[0:NUM_DRAWERS-1]) {
			translate([BOX_WIDTH/2,DRAWER_CORNER_RAD*2,i*(DRAWER_HEIGHT+CLEARANCE*2+WALL_WIDTH)+WALL_WIDTH-0.1])
			{
				scale([1,1,0.9]) half_sphere(bumprad);
				translate([0,0,DRAWER_HEIGHT+CLEARANCE*2+0.2]) rotate([180,0,0])
				scale([1,1,0.9]) half_sphere(bumprad);
			}
		}

// mouse ears
translate([-earrad/2,BOX_DEPTH,-earrad/2]) rotate([90,0,0]) cylinder(r=earrad,h=earthick);
translate([BOX_WIDTH+earrad/2,BOX_DEPTH,-earrad/2]) rotate([90,0,0]) cylinder(r=earrad,h=earthick);
translate([-earrad/2,BOX_DEPTH,BOX_HEIGHT+earrad/2]) rotate([90,0,0]) cylinder(r=earrad,h=earthick);
translate([BOX_WIDTH+earrad/2,BOX_DEPTH,BOX_HEIGHT+earrad/2]) rotate([90,0,0]) cylinder(r=earrad,earthick);

	}
};

module drawerbase() {
	union() {
		difference() {
			translate([0,0,DRAWER_CORNER_RAD/2]) roundedBox([DRAWER_WIDTH,DRAWER_DEPTH,DRAWER_HEIGHT+DRAWER_CORNER_RAD],DRAWER_CORNER_RAD,NOFILLETS);
			translate([0,0,DRAWER_HEIGHT]) cube([DRAWER_WIDTH+1, DRAWER_DEPTH+1,DRAWER_HEIGHT],center=true);
			translate([0,-DRAWER_DEPTH/2+DRAWER_CORNER_RAD*2,-DRAWER_HEIGHT/2]) scale([1.2,1.2,1.]) sphere(bumprad);
			translate([0,DRAWER_DEPTH/2-DRAWER_CORNER_RAD*2,-DRAWER_HEIGHT/2]) scale([1.2,1.2,1.]) sphere(bumprad);

			if(EMBOSSED == "yes" && DRAW_TEXT=="yes") {
			  translate([TEXT_SHIFT_RIGHT,-DRAWER_DEPTH/2+WALL_WIDTH/4,TEXT_SHIFT_UP]) rotate([90,0,0])linear_extrude(WALL_WIDTH/2)
			    text(MESSAGE,font=TEXT_FONT,size=TEXT_HEIGHT,spacing=TEXT_SPACING,valign="center",halign="center");
			}
		}

   	translate([0,-DRAWER_DEPTH/2,-DRAWER_HEIGHT/2+FLOOR_THICK])
        handle();

	if(EMBOSSED == "no" && DRAW_TEXT=="yes") {
	  translate([TEXT_SHIFT_RIGHT,-DRAWER_DEPTH/2+WALL_WIDTH/4,TEXT_SHIFT_UP]) rotate([90,0,0])linear_extrude(WALL_WIDTH/2) text(MESSAGE,font=TEXT_FONT,size=TEXT_HEIGHT,spacing=TEXT_SPACING,valign="center",halign="center");
	}

	};
};

module handle() {

	difference() {
		roundedBox([DRAWER_WIDTH/4,HANDLE_SIZE*2,FLOOR_THICK*2],2,true);
		translate([0,HANDLE_SIZE,0]) cube([DRAWER_WIDTH/4+1,HANDLE_SIZE*2,FLOOR_THICK*2+1],center=true);
	};

	difference() {
		translate([0,-DRAWER_CORNER_RAD/2,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			cube([DRAWER_WIDTH/4,DRAWER_CORNER_RAD,DRAWER_CORNER_RAD],center=true);
		translate([-DRAWER_WIDTH/8-1,-DRAWER_CORNER_RAD,FLOOR_THICK+DRAWER_CORNER_RAD]) rotate([0,90,0])
			cylinder(h=DRAWER_WIDTH/4+2,r=DRAWER_CORNER_RAD);
		};
    //difference() {
    //    cylinder(h = DRAWER_WIDTH/4, r=HANDLE_SIZE, center = true);
    //    translate([-2,-2,0])   cylinder(h = DRAWER_WIDTH/4+1, r=HANDLE_SIZE, center = true);
    //}

	difference() {
		translate([-DRAWER_WIDTH/8,0,-FLOOR_THICK])
			cube([DRAWER_WIDTH/4,DRAWER_CORNER_RAD,DRAWER_CORNER_RAD],center=false);
		translate([-DRAWER_WIDTH/8-1,DRAWER_CORNER_RAD,-FLOOR_THICK+DRAWER_CORNER_RAD]) rotate([0,90,0])
			cylinder(h=DRAWER_WIDTH/4+2,r=DRAWER_CORNER_RAD);
		};
};

module holes(width,height) {
	ROWS = width / hoff - DRAWER_CORNER_RAD;
	COLS = height / voff - DRAWER_CORNER_RAD;

	translate([hoff*1.3,0,voff*2])
	for (i=[0:COLS-1]) {
		for (j=[0:ROWS-1]){
			translate([j*hoff+i%2*(hoff/2),0,i*voff])
			rotate([90,90,0]) rotate([0,0,0]) cylinder(h=thick,r=india/2,$fn=6);
		}
	}
}

module half_sphere(rad) {
	difference() {
		sphere(rad);
		translate([-rad,-rad,-rad])
		cube([rad*2,rad*2,rad]);
	}
}

module roundedBox(size, radius, sidesonly)
{
  rot = [ [0,0,0], [90,0,90], [90,90,0] ];
  if (sidesonly) {
    cube(size - [2*radius,0,0], true);
    cube(size - [0,2*radius,0], true);
    for (x = [radius-size[0]/2, -radius+size[0]/2],
           y = [radius-size[1]/2, -radius+size[1]/2]) {
      translate([x,y,0]) cylinder(r=radius, h=size[2], center=true);
    }
  }
  else {
    cube([size[0], size[1]-radius*2, size[2]-radius*2], center=true);
    cube([size[0]-radius*2, size[1], size[2]-radius*2], center=true);
    cube([size[0]-radius*2, size[1]-radius*2, size[2]], center=true);

    for (axis = [0:2]) {
      for (x = [radius-size[axis]/2, -radius+size[axis]/2],
             y = [radius-size[(axis+1)%3]/2, -radius+size[(axis+1)%3]/2]) {
        rotate(rot[axis])
          translate([x,y,0])
          cylinder(h=size[(axis+2)%3]-2*radius, r=radius, center=true);
      }
    }
    for (x = [radius-size[0]/2, -radius+size[0]/2],
           y = [radius-size[1]/2, -radius+size[1]/2],
           z = [radius-size[2]/2, -radius+size[2]/2]) {
      translate([x,y,z]) sphere(radius);
    }
  }
}

```


[User]
I'd like to design a corner bracket for use with aluminum extrusions, specifically Misumi/OpenBeam style extrusions, but I want it to be easily customized. I'd like to be able to specify the extrusion width (like switching between 15mm and 20mm) and the bolt hole diameter, so it can be used with M3 or M5 hardware. The bracket itself should have rounded corners and be designed so that the extrusion can be attached on two sides of a corner at a 90-degree angle.

I need it to be parametric, so I can specify things like the extrusion width, bracket thickness, and hole diameter. The current design has the bracket length as 4x the extrusion width, which works for me. It also calculates an internal "square diagonal", which I'm fine with.

The current scad file has some modules I think you could probably build on. The existing modules are `cylinder_outer`, `cylinder_outerdual`, `cylinder_mid`, `createMeniscus`, and `roundCornersCube`. I'm pretty happy with how those work, so keeping them would probably save you some time. And I'd like to keep the `cornerBracket()` module structure, as well.

I envision a single .scad file that handles all of this.

[Assistant]
corner_bracket.scad:
```
// Minimum needed is 0.02 to ensure properly punched holes.
holeCreationOffset=0.05;
//How wide the extrusion is
extrusionWidth=20;//[15,20]
extrusionHeight=extrusionWidth;
//How thick the bracket will be, 3 is the default
bracketThickess=3; //[1:10]
bracketLength=extrusionWidth * 4;
triangle2=bracketLength/2;
// M3 or M5 holes
boltHoleDiameter=3.7;//[3.7,5.68]
boltHoleRadius=boltHoleDiameter/2;
squareDiagonal=(bracketLength*sqrt(2));

echo(squareDiagonal);

module cylinder_outer(height,radius,fn){
   fudge = 1/cos(180/fn);
   cylinder(h=height,r=radius*fudge,$fn=fn, center = true);
}

module cylinder_outerdual(height,radius,radiustwo,fn){
   fudge = 1/cos(180/fn);
   cylinder(h=height,r1=radius*fudge,r2=radiustwo*fudge,$fn=fn, center = true);
}

module cylinder_mid(height,radius,fn){
   fudge = (1+1/cos(180/fn))/2;
   cylinder(h=height,r=radius*fudge,$fn=fn, center = true);
}

module createMeniscus(h,radius) {// This module creates the shape that needs to be substracted from a cube to make its corners rounded.
    difference(){        //This shape is basicly the difference between a quarter of cylinder and a cube
       translate([radius/2+0.1,radius/2+0.1,0]){
          cube([radius+0.2,radius+0.1,h+0.2],center=true);         // All that 0.x numbers are to avoid "ghost boundaries" when substracting
       }

       cylinder(h=h+0.2,r=radius,$fn = 25,center=true);
    }
}

module roundCornersCube(x,y,z,r){  // Now we just substract the shape we have created in the four corners
    difference(){
       cube([x,y,z], center=true);

    translate([x/2-r,y/2-r]){  // We move to the first corner (x,y)
          rotate(0){
             createMeniscus(z,r); // And substract the meniscus
          }
       }
       translate([-x/2+r,y/2-r]){ // To the second corner (-x,y)
          rotate(90){
             createMeniscus(z,r); // But this t�ime we have to rotate the meniscus 90 deg
          }
       }
          translate([-x/2+r,-y/2+r]){ // ...
          rotate(180){
             createMeniscus(z,r);
          }
       }
          translate([x/2-r,-y/2+r]){
          rotate(270){
             createMeniscus(z,r);
          }
       }
    }
}

module cornerBracket(){
    difference (){
        color([0,0,1]) roundCornersCube(bracketLength, bracketLength,bracketThickess, 1);
        translate([extrusionWidth/2,extrusionWidth/2,0]) rotate([0,0,45]) cube([extrusionHeight, extrusionWidth * 2,bracketThickess + 1], center=true);
        translate([squareDiagonal/4,squareDiagonal/4,0])  rotate([0,0,45]) cube([bracketLength, bracketLength*2,bracketThickess + 1], center=true);

        translate(v = [ -(bracketLength/2) + (extrusionWidth/2), -(bracketLength/2) + (extrusionWidth) + extrusionWidth, 0 ]) cylinder_outer(bracketThickess * 2,boltHoleRadius,20);
        translate(v = [ -(bracketLength/2) + (extrusionWidth/2),-(bracketLength/2) + (extrusionWidth * 2)+ extrusionWidth , 0 ]) cylinder_outer(bracketThickess * 2,boltHoleRadius,20);

        translate(v = [ -(bracketLength/2) + (extrusionWidth ) + extrusionWidth,-(bracketLength/2) + (extrusionWidth/2), 0 ]) cylinder_outer(bracketThickess * 2,boltHoleRadius,20);
        translate(v = [ -(bracketLength/2) + (extrusionWidth * 2) + extrusionWidth,-(bracketLength/2) + (extrusionWidth/2), 0 ]) cylinder_outer(bracketThickess * 2,boltHoleRadius,20);
    }

}

cornerBracket();
```


[User]
I'd like to generate an Arduino Uno proto shield, and I want to be able to customize it for my needs. I've got a Thingiverse link for a model that's pretty close to what I want, but I need more flexibility, so I want a parametric design using OpenSCAD.

Specifically, I need to control:

*   **The board height**: I'd like to specify the thickness of the base board.
*   **The overall length**: I need to be able to adjust the board's length to fit my project.
*   **An "enlarge" parameter**: This would add a margin around the edges of the board to help with printing or fit, and it would apply to the base shape only, and not add margins to the hole grids.
*   **The starting grid row**: I should be able to specify if the grid starts at row 0 or row 1.
*   **The grid dimensions**: I need to set the number of rows and columns for the prototyping grid area.
*   **Cable holders**: I need an option to enable or disable cable holders for the pin headers.
*   **Hole shape**: I want to be able to choose between circular and rectangular holes for the through-hole areas (including the pin header holes and prototyping grid). Ideally, the rectangular holes would have 45-degree rotation so they're diamond-shaped.
*   **Legends**: I'd like an option to turn on or off text labels for the pin headers and silkscreen-style text on the board's top surface, as you see in the example Thingiverse model.

I don't want to use any external libraries for this project, just standard OpenSCAD features.

Also, the dimensions for the Arduino Uno board are as follows:
```
  lABx1 = 65;       // 65;
  lABx2 = 68.6;     // 68;    Laenge
  lABx3 = 0;        // 0;
  lABy1 = 0;        // 0;
  lABy2 = 2;        // 2;
  lABy3 = 5;        // 5;
  lABy4 = 37;       // 37;
  lABy5 = 40;       // 40;
  lABy6 = 53.3;     // 53;    Breite
  // Loecher
  lALd  = 3.2;      // 3.2;   Durchmesser
  lALx1 = 15.0;     // 15.0;
  lALx2 = 65.5;     // 65.5;
  lALy1 = 7.0;      // 7.0;
  lALy2 = 35.0;     // 35.0;
  lALy3 = 50.5;     // 50.5;
  lALy4 = 2.54;     // 2.54;  53-50.5
  // Pins
  lAPd  = 1.00+0.2; // V03: 0.2 mm Aufmass
  lAPx  = 2.54;
  lAPx1 = 44.72;    // V03: 1.80=45.72
  lAPx2 = 40.656;   // V03: 1.64=41.656 (-21.844)
  lAPx3 = 62.50;    // V03: 2.50=63.5
  lAPy1 = 2.54;
  lAPy2 = 50.8;
  lAPn1 = 6;
  lAPn2 = 8;
  lAPn3 = 7;
  // Pin-Halter
  lAPHh = 2.40;
  lAPHy = 2.00;
  lAPHa = 0.0;
  lAPHd = 1+0.2;    // V03: 0.2 mm Aufmass
  // Lochraster
  lARd  = 1.00+0.2; // V03: 0.2 mm Aufmass
  lARx  = 2.54;
  lARy  = 2.54;
  lARx1 = 62.5-2.54*pgrid0;
  lARy1 = 2.54*3+(16-pgridy)/2*2.54;
  lARnx = pgridx;
  lARny = pgridy;
```
I think that should be enough to get started! I'm looking forward to what you come up with.

[Assistant]
thing1534620_arduino-uno_circuit-breadboard.scad:
```
/*
 *  arduino-uno-circuit-breadboard.scad
 *  http://www.thingiverse.com/thing:1534620
 *
 *  1.9 mm Hoehe Bord
 *    0.3 mm Nozzle = 0.3 + 8x 0.2 mm
 *    0.4 mm Nozzle = 0.4 + 5x 0.3 mm
 *  2.4 mm Hoehe Halter
 *    0.3 mm Nozzle = 12x 0.2 mm
 *    0.4 mm Nozzle =  8x 0.3 mm
 *
 *  2016-05-02 V0.1 large size 22x16
 *  2016-05-02 V0.2 small size 18x16
 *  2016-05-03 V0.3 Abstand 7/8, Lochgroesse, Extraloch links
 *
 */

height_mm = 1.9; // [1.5:0.1:4.5]
length_mm = 68.8; // [58.3:0.5:68.8]
enlarge_mm = 2; // [0:0.5:15]
grid_0 = 0; // [0:1,1:2,30:disable]
grid_x = 22; // [2:2:22]
grid_y = 16; // [2:2:16]
cable_flag = 1; // [0:disable,1:enable]
hole_flag = 2; // [1:circle,2:rectangle]
legend_flag = 1; // [0:disable,1:enable]

// height_mm = 1.2;
// length_mm = 58.3;
// enlarge_mm = 2;
// grid_0 = 0;
// grid_x = 18;
// grid_y = 16;
// cable_flag = 1;
// hole_flag = 2;
// legend_flag = 0;

arduino_uno_board(
  height_mm,  // Hoehe in mm
  length_mm,  // Laenge in mm
  enlarge_mm, // vergroessern um mm
  grid_0,     // Lochraster beginnen bei Reihe 0 oder 1
  grid_x,     // Lochraster Anzahl x
  grid_y,     // Lochraster Anzahl y
  cable_flag, // Kabelhalter fuer PINs
  hole_flag,  // Lochmuster fuer PINs
  legend_flag // Beschriftung fuer PINs
);

module arduino_uno_board(
    pheight=1.9     // Hoehe
   ,plength=68.8    // Laenge
   ,penlarge=2      // vergroessern
   ,pgrid0=1
   ,pgridx=22
   ,pgridy=16
   ,pcablepin=1   // Halter fuer Kabel
   ,phole=2       // Muster fuer Loecher
   ,plegend=0     // Beschriftung
  ) {
  // Arduino Board
  lABx1 = 65;       // 65;
  lABx2 = 68.6;     // 68;    Laenge
  lABx3 = 0;        // 0;
  lABy1 = 0;        // 0;
  lABy2 = 2;        // 2;
  lABy3 = 5;        // 5;
  lABy4 = 37;       // 37;
  lABy5 = 40;       // 40;
  lABy6 = 53.3;     // 53;    Breite
  // Loecher
  lALd  = 3.2;      // 3.2;   Durchmesser
  lALx1 = 15.0;     // 15.0;
  lALx2 = 65.5;     // 65.5;
  lALy1 = 7.0;      // 7.0;
  lALy2 = 35.0;     // 35.0;
  lALy3 = 50.5;     // 50.5;
  lALy4 = 2.54;     // 2.54;  53-50.5
  echo (str("Ldy: Soll=27.9mm, Ist=",(lALy2-lALy1),"mm"));
  echo (str("Ldx: Soll=50.8mm, Ist=",(lALx2-lALx1),"mm"));
  // Pins
  lAPd  = 1.00+0.2; // V03: 0.2 mm Aufmass
  lAPx  = 2.54;
  lAPx1 = 44.72;    // V03: 1.80=45.72
  lAPx2 = 40.656;   // V03: 1.64=41.656 (-21.844)
  lAPx3 = 62.50;    // V03: 2.50=63.5
  lAPy1 = 2.54;
  lAPy2 = 50.8;
  lAPn1 = 6;
  lAPn2 = 8;
  lAPn3 = 7;
  // Pin-Halter
  lAPHh = 2.40;
  lAPHy = 2.00;
  lAPHa = 0.0;
  lAPHd = 1+0.2;    // V03: 0.2 mm Aufmass
  // Lochraster
  lARd  = 1.00+0.2; // V03: 0.2 mm Aufmass
  lARx  = 2.54;
  lARy  = 2.54;
  lARx1 = 62.5-2.54*pgrid0;
  lARy1 = 2.54*3+(16-pgridy)/2*2.54;
  lARnx = pgridx;
  lARny = pgridy;
  translate([plength-68.8,0,0])
  union() {
    difference() {
      // Arduino Board
      union() {
        translate([penlarge/2,0,0])
        linear_extrude(height=pheight)
        polygon(points=[
         [lABx3-penlarge,lABy1-penlarge/2],
         [lABx1,lABy1-penlarge/2],
         [lABx1,lABy2],
         [lABx2,lABy3],
         [lABx2,lABy4],
         [lABx1,lABy5],
         [lABx1,lABy6+penlarge/2],
         [lABx3-penlarge,lABy6+penlarge/2]
        ]);
        // Lable
        translate([2,7,pheight])
        rotate([0, 0, 90])
        linear_extrude(height=0.6)
        text("http://goo.gl/4p1V8P", size=3, valign="center", halign="left", font=":bold");
        // Lable
        translate([66,10,pheight])
        rotate([0, 0, 90])
        linear_extrude(height=0.6)
        text("UnoPCB", size=4, valign="center", halign="left", font=":bold");
        // Beschriftung
        if (plegend == 1) {
          translate([49,0.8,pheight]) rotate([0, 0, 0])
          linear_extrude(height=0.4)
          text("0  1  2  3  4  5", size=1.1, valign="center", halign="left", font="mono:bold");
          translate([27,0.8,pheight]) rotate([0, 0, 0])
          linear_extrude(height=0.4)
          text("REF RES 3  5 \\GND/ VIN", size=1.1, valign="center", halign="left", font="mono:bold");
          translate([63.2,52.3,pheight]) rotate([0, 0, 180])
          linear_extrude(height=0.4)
          text("0  1  2 ~3  4 ~5 ~6 7", size=1.1, valign="center", halign="left", font="mono:bold");
          translate([39.2,52.3,pheight]) rotate([0, 0, 180])
          linear_extrude(height=0.4)
          text("8 ~9~10~11 12 13 GND AREF", size=1.1, valign="center", halign="left", font="mono:bold");
        }
      }
      // Loecher
      union() {
        translate([lALx1,lALy3,0])
        cylinder(pheight,lALd/2,lALd/2,$fs=0.1);
        translate([lALx1,lALy4,0])
        cylinder(pheight,lALd/2,lALd/2,$fs=0.1);
        translate([lALx2,lALy1,0])
        cylinder(pheight,lALd/2,lALd/2,$fs=0.1);
        translate([lALx2,lALy2,0])
        cylinder(pheight,lALd/2,lALd/2,$fs=0.1);
      }
      if (phole == 1) { // normal
        // Pins
        union() {
          for (x=[1:lAPn1]) {
            color("red")
            translate([lAPx3-lAPx*(x-1),lAPy1,0])
            cylinder(pheight,lAPd/2,lAPd/2,$fs=0.1);
          }
          for (x=[1:lAPn3]) {
            color("blue")
            translate([lAPx1-lAPx*(x-1),lAPy1,0])
            cylinder(pheight,lAPd/2,lAPd/2,$fs=0.1);
          }
          for (x=[1:lAPn2]) {
            color("blue")
            translate([lAPx2-lAPx*(x-1),lAPy2,0])
            cylinder(pheight,lAPd/2,lAPd/2,$fs=0.1);
            color("red")
            translate([lAPx3-lAPx*(x-1),lAPy2,0])
            cylinder(pheight,lAPd/2,lAPd/2,$fs=0.1);
          }
        }
        // Lochraster
        for (x=[1:lARnx]) for (y=[1:lARny]) {
            translate([lARx1-lARx*(x-1),lARy1+lARy*(y-1),0])
            cylinder(pheight,lARd/2,lARd/2,$fs=0.1);
        }
      }
      if (phole == 2) { // optimiert fuer kleinen GCode und schnellen Druck
        // Pins
        union() {
          for (x=[1:lAPn1+1]) {  // V03: Extraloch
            color("red")
            translate([lAPx3-lAPx*(x-1),lAPy1,0]) rotate(45)
            cylinder(pheight,lAPd/2*1.2,lAPd/2*1.2,$fn=4);
          }
          for (x=[1:lAPn3]) {
            color("blue")
            translate([lAPx1-lAPx*(x-1),lAPy1,0]) rotate(45)
            cylinder(pheight,lAPd/2*1.2,lAPd/2*1.2,$fn=4);
          }
          for (x=[1:lAPn2]) {
            color("blue")
            translate([lAPx2-lAPx*(x-1),lAPy2,0]) rotate(45)
            cylinder(pheight,lAPd/2*1.2,lAPd/2*1.2,$fn=4);
            color("red")
            translate([lAPx3-lAPx*(x-1),lAPy2,0]) rotate(45)
            cylinder(pheight,lAPd/2*1.2,lAPd/2*1.2,$fn=4);
          }
        }
        // Lochraster
        for (x=[1:lARnx]) for (y=[1:lARny]) {
            translate([lARx1-lARx*(x-1),lARy1+lARy*(y-1),0])
            rotate(45)
            cylinder(pheight,lARd/2*1.2,lARd/2*1.2,$fn=4);
        }
      }
      // verkleinern
      if (plength < 68.8) {
        translate([-enlarge_mm/2-0.1,-enlarge_mm/2-0.1,-0.1])
        cube([68.8-plength+enlarge_mm+0.2,lABy6+enlarge_mm+0.2,pheight+0.2+10]);
      }

    }
    // Pin-Kabel
    if (pcablepin==1) {
      difference() {
        union() {
          translate([lAPx1-lARx*lAPn3,lAPy1+lAPd/2+lAPHa,0])
          cube([lARx*(lAPn3+1)+.5,lAPHy,pheight+lAPHh]);
          translate([lAPx3-lARx*lAPn1,lAPy1+lAPd/2+lAPHa,0])
          cube([lARx*(lAPn1+1),lAPHy,pheight+lAPHh]);
        }
        for (x=[1:lAPn1]) {
          color("red")
          translate([lAPx3-lAPx*(x-1),lAPy1+lAPd/2+lAPHa,pheight+lAPHh/2-lAPd/2])
          rotate([-90,0,0])
          cylinder(lAPHy,lAPd/2,lAPd/2,$fn=6);
        }
        for (x=[1:lAPn3]) {
          color("blue")
          translate([lAPx1-lAPx*(x-1),lAPy1+lAPd/2+lAPHa,pheight+lAPHh/2-lAPd/2])
          rotate([-90,0,0])
          cylinder(lAPHy,lAPd/2,lAPd/2,$fn=6);
        }
      }
      difference() {
        union() {
          translate([lAPx2-lARx*lAPn2,lAPy2-lAPd/2-lAPHa-lAPHy,0])
          cube([lARx*(lAPn2+1)+.9,lAPHy,pheight+lAPHh]);
          translate([lAPx3-lARx*lAPn2,lAPy2-lAPd/2-lAPHa-lAPHy,0])
          cube([lARx*(lAPn2+1),lAPHy,pheight+lAPHh]);
        }
        for (x=[1:lAPn2]) {
          color("blue")
          translate([lAPx2-lAPx*(x-1),lAPy2-lAPd/2-lAPHa-lAPHy,pheight+lAPHh/2-lAPd/2])
          rotate([-90,0,0])
          cylinder(lAPHy,lAPd/2,lAPd/2,$fn=6);
          color("red")
          translate([lAPx3-lAPx*(x-1),lAPy2-lAPd/2-lAPHa-lAPHy,pheight+lAPHh/2-lAPd/2])
          rotate([-90,0,0])
          cylinder(lAPHy,lAPd/2,lAPd/2,$fn=6);
        }
      }
    }
  }
}
```


[User]
I need an OpenSCAD file for a parametric design that will let me mount a sharpening stone of almost any size onto a Chinese Apex-style knife sharpener. The existing code I've written works for a specific stone size, but I want to make it adaptable to stones of various dimensions.

Here's a breakdown of what I need the design to do:

1. **Stone Dimensions:** The design should be able to accommodate sharpening stones of varying widths, heights, and lengths. I want to be able to input these dimensions as parameters.
2. **Mounting Base:** The design needs to create a base that will hold the stone securely. This base should be adjustable based on the stone's dimensions. The current design has a fixed length based on the original stone the sharpener was designed for. The new design should calculate the necessary length adjustments based on the new stone's dimensions. The current code uses a variable `dist` which is calculated by `(original_stone_l - stone_l)/2 - 3 - 2;` and I want this to be calculated dynamically.
3. **Support Structure:** There are triangular supports on the sides that need to adjust with the stone's dimensions and position. These are generated using `linear_extrude` and `polygon` in the existing code, but they need to be parametric to match any stone size.
4. **Cutout Option:** There's an optional cutout feature in the current design that I want to keep. This is controlled by a variable `cut` that can be set to 1 (On) or 2 (Off). The cutout is generated using a cylinder and should adjust its size and position based on the stone's dimensions.
5. **Original Stone Dimensions:** The length of the original stone that the sharpener was designed for should be a parameter, although it might be able to be fixed in the code if that makes more sense, with a default value of `158`.

Essentially, I want a parametric version of the current design that will automatically adjust all its features based on the dimensions of the new stone.

I am using OpenSCAD 2021.01. I have the MCAD and BOSL2 libraries installed. I expect the AI to use these in the solution.

[Assistant]
sharpener-stone-mount.scad:
```
// preview[view:north west, tilt:top diagonal]

//the width of the new stone
stone_w = 35;//[10:60]

//height of the new stone
stone_h = 23;//[5:60]

//new stone length
stone_l = 98;//[40:148]

//original (old) stone length
original_stone_l = 158;//[120:170]

//Cutout
cut = 1; //[1:On,2:Off]

dist = (original_stone_l - stone_l)/2 - 3 - 2;

translate ([-25 / 2, 0, 0]) {cube ([25, dist, 4], 0);

rotate ([0, 270, 180]){
    linear_extrude(height = 25, center = 0, convexity = 10, twist = 0) polygon(points=[[0,0],[4,0],[0,3]],center = 0);
    }
}

translate ([-25/2, -3, 0]) cube ([1, 3, 4],0);
translate ([-25/2+24, -3, 0]) cube ([1, 3, 4],0);

difference (){

    translate ([- ((stone_w + 3.5) / 2), dist, 0]) {
        cube ([stone_w + 3.5, 10, stone_h + 3.5],0);
    }

    translate ([- (stone_w + 0.5 ) / 2, dist + 2, 1.5]) {
        cube ([stone_w + 0.5, 10, stone_h + 0.5 ],0);
    }

    if (cut == 1){
        translate ([0, dist + 10, -1]) resize([stone_w - 1, 13, 0]) {
            cylinder(stone_h + 5, d = stone_w / 2 );
        }
    }
}

rotate ([0, 270, 180]) translate([4, - dist, - 3]){
    linear_extrude(height = 6, center = 0, convexity = 10, twist = 0) polygon(points=[[0,0],[stone_h - 6, 0],[0, dist]], center=0);
}

```


[User]
Hey, I'm working on a ducted fan design for a quadcopter, and I need some help generating the OpenSCAD code. I've been messing around with some existing code, but I think it'd be easier if I had a parametric design that I could tweak more easily.

Basically, I need to define a ducted fan with the following parameters:

*   **prop\_dia\_upper**: This is the diameter of the propeller itself.
*   **prop\_spacing**: This is the gap between the propeller and the inside wall of the duct.
*   **lip\_radius\_pc**: The radius of the curved lip at the top of the duct, defined as a percentage of the total diameter.
*   **shroud\_lower\_length\_pc**: The length of the straight section of the duct, also defined as a percentage of the total diameter.
*   **duct\_angle**: The angle that the inner duct wall flares outwards.
*   **duct\_resolution**: The resolution or smoothness of the final STL.

I need the design to be parametric so I can adjust these variables.  I also need an option to make the walls of the duct hollow to reduce weight.  It should have a similar structure to the `slice_extruded` module in my example, with `slice_final` and `slice_final_hollow` modules that can be switched out.

I was also thinking it might be helpful to have a basic representation of a motor mount that I could enable/disable. This doesn't need to be super detailed, just something I can use as a reference. Think something along the lines of the motor module I put in my code - just a cylinder with an optional shaft.

I'd really appreciate if you could generate the necessary .scad file(s) for me. I'm not using any external libraries for this, so I don't need any included .scad files from other places.

[Assistant]
Ducted_Fan.scad:
```
/*
Configurable Ducting System for a Quadcopter

    According to the study below (and Richards work) the optimal optimal configuration found experimentally includes a δtip = 0.1%Dt,  rlip = 13%Dt, θd = 10◦ and Ld = 50% to 72%Dt.

    A Quick(ish) Key:
        prop_dia_upper:
            Longest Part of the Propellor.
            IDEAL: Whatever your prop measures.
        prop_spacing:
            The extra space that is added to both sides of the prop_dia_upper variable to give the inside wall of the duct.
            IDEAL: 0.1% of the dia_total, BUT you probably want more tolerance if you're printing/using cheaper props. I've left this as a millimeter value though, not a percentage so that you can define set tolerance.
        dia_total:
            Combines the variables as described above.
            IDEAL: N/A - don't change this unless you're really ripping things to pieces.
        lip_radius_pc:
            Affects the radius of the top curve of the duct, percentage based on the dia_total variable.
            IDEAL: 13%
        shroud_lower_length_pc:
            Affects the length of the duct section, based on the dia_total variable.
            IDEAL: 50 - 70%
        duct_angle:
            The angle off of vertical (Z) by which the inner duct wall pushes outwards.
            IDEAL: 10 degrees
        duct_resolution:
            Affects overall number of slices (the smoothness or quality of the end STL).
            IDEAL: 120. Higher is better, but will take ages if you go too high.

    NOTE ON REDUCING WEIGHT:
        I have a way of reducing weight -- look for the 'slice_extruded' module and replace the 'slice_final' with 'slice_final_hollow' - but be warned, you need to check that the placement doesn't leave you with overly thin walls as it does not always scale well with changes to certain variables.

        You'll want to work with the translate and scale functions to find a good balance for your needs.

        Comment out the 'slice_extruded' and call 'slice_final_hollow' - this will give you a cross section so that you can see what's going on and reduce render times to almost nothing.

*References:
    Based on the following sources by the following:
                https://capolight.wordpress.com/2015/01/14/quadcopter-rotor-duct/
        - Richard.

    HOVER AND WIND-TUNNEL TESTING OF SHROUDED ROTORS FOR IMPROVED MICRO AIR VEHICLE DESIGN
        - Jason L. Pereira Doctor of Philosophy, 2008.

    Design by Harlo,
    02/05/2016.

*/

///USER Variables START

//Propellor Diameter
prop_dia_upper = 123; //[30:0.5:500]
//Spacing between Duct and Prop
prop_spacing = 1.5; //[0.1:0.1:10]
dia_total = prop_dia_upper + (prop_spacing*2);
//Lip Radius Percentage
lip_radius_pc = 0.13; //[0.05:0.01:0.30]

//Length Percentage (Lower Z Height)
shroud_lower_length_pc = 0.50; //[0.10:0.01:0.80]
//Duct Angle (degrees)
duct_angle = 10; //[5:0.5:45]
//Resolution of Duct (> = smoother)
duct_resolution = 200;//[30:1:360]

///USER Variables END

//Internal/Calculated Variables
lip_dia = (dia_total * lip_radius_pc)*2;///We double for diameter to keep things "simple".
shroud_lower_length = shroud_lower_length_pc * dia_total;

//Output of Calculated Variables to User
echo ("Shroud Lower Height = ", shroud_lower_length); //does not include the curved diameter
echo ("Shroud Total Height = ", shroud_lower_length + lip_dia/2);
echo ("Lip Diameter = ", lip_dia); //OUTPUT
echo ("Duct_angle = ", duct_angle);

///RENDER

slice_extruded();

/*
//For Later Testing - based on the rough dimensions of an emax motor
translate([0,0,-12]){
    motor(16.5, 28, 6, 5);
}
*/

/*
//FOR TESTING DIMENSIONS

difference(){
    slice_extruded();
    rotate([90,0,0]){
        cylinder(h = 300, d = 8, center = true, $fn = 20);
    }
}

cylinder (h = 1, d = prop_dia_upper, $fn = 32, center = true);
*/

/*
MAIN BUILD MODULES FOR THE SHROUD/DUCT
*/

module slice_extruded(){
    rotate_extrude(convexity = 10, $fn = duct_resolution){
        translate([dia_total/2,0,0]){
            slice_final();
        }
    }
}

/*
SLICES
    These build the initial housing in tiny slices (in theory..)
*/

module slice_final_hollow(){
difference(){
slice_final();
translate([3.5,-2,0]){
    scale([0.75,0.8,1]){
        slice_final();
    }
}
}
}

module slice_final(){

        slice_leading_edge();
        slice_lower_duct();

}

module slice_lower_duct(){
    //angles
    remain_angle = 180 - 90 - duct_angle;
    echo("Remain_angle = ", remain_angle);

    lower_length = (sin(duct_angle))/(sin(remain_angle)/shroud_lower_length);
    echo("Lower length = ", lower_length);

    //sinA/a = sinB/b = sinC/c
    //sin(remain_angle)/shroud_lower_length = sin(15 degress)/?
    //solving for the lower length of the triangle (x length)

    difference(){
    //Base cube for being cut from.
    translate([lip_dia/2,-shroud_lower_length/2,0]){
        square([lip_dia,shroud_lower_length], center = true);
    }

    //Cutting Triangle
        triangle_points = [[0,0], [0,-shroud_lower_length], [ lower_length, -shroud_lower_length]]; //TEST SO FAR
        polygon(triangle_points);
    }
}

module slice_leading_edge(){
    difference(){
        translate([lip_dia/2,0,0])
        {
            //upper edge
            circle(d = lip_dia, center = true);
        }
        translate([lip_dia/2,-lip_dia/2,0]){
            square(lip_dia, center = true);
        }

    }

}

/*
Motor representation -- rough
    - maybe clean up to use as a library module
*/

module motor(body_height, body_dia, shaft_height, shaft_dia){
    total_height = body_height + shaft_height;
    echo("Motor Total Height = ", total_height);

    translate([0,0,0]){
        cylinder (h = body_height, d = body_dia, $fn = 32, center = true);
        }

    translate([0,0,body_height/2 + shaft_height/2]){
        cylinder (h = shaft_height, d = shaft_dia, center = true, $fn = 20);

        }

    translate([0,0,0]){

    }

}

////David Harlock, 02/05/2016.
```


[User]
Hi, I'm looking to design a wrist rest for my ErgoDox keyboard, and I'd like it to be parametric so I can adjust it to fit other ergonomic keyboards in the future. I'd like to be able to define the width and depth of the palm rest section, and the width and angle of the thumb rest section. I'm picturing a design similar to the one I've attached (wristpad.scad), but want it to be more adaptable.

Specifically, I'd like these parameters to be adjustable:

*   `palm_width`: The width of the main palm rest portion of the pad.
*   `pad_depth`: The overall depth of the wrist rest.
*   `pad_thickness`: The thickness of the wrist rest.
*   `thumb_width`: The width of the thumb rest portion of the pad.
*   `thumb_angle`: The angle of the thumb rest portion relative to the palm rest.

I also really like the rounded edges in the design, so it would be great if you could incorporate a similar fillet function, or equivalent. I don't need any specific libraries, but I would like it to be nicely organized so I can understand the code easily.

I'd also like the output to be a single module that, when called, will create the full wrist rest. So something like `wrist_rest()` and within that module I'd like to see sub-modules `_thumb_rest()` and `_palm_rest()`. I think that would help keep things tidy and easy to adjust later. Can you make that for me? I'd like for this to be a parametric file with a single module for ease of calling.

[Assistant]
wristpad.scad:
```
/* Copyright (c) Bryan Matsuo 2016
 * Use of this model is governed by the Creative Commons - Attribution (CC BY)
 * license.
 * */

wristpad_base();

WristpadDepth=100; // in mm -- Measured from the corner, parallel to side
WristpadThickness=20; // in mm -- Don't include the additional height from rubber feet.

// Private
ErgoDoxAngle=154; // Measured on my acrylic case.
ErgoDoxSideThumb=93; // MUST MATCH THE OTHER SIDE. Not necessarily exact -- will be rounded.
ErgoDoxSideFingers=82.5; // MUST MATCH THE OTHER SIDE.

// The module will use the knowledge that ErgoDoxSideThumb >
// ErgoDoxSideFingers.  This is a fixed property of the PCB (I think -- not
// completely sure about Infinity vs Lite vs standard variants).

module wristpad_base() {
    fillet(r=2.5, $fn=16) {
        union() {
            _wristpad_palm();

            translate([ErgoDoxSideFingers, 0, 0])
                _wristpad_thumb();
        }
    }

    module _wristpad_thumb() {
        intersection() {
            // x value doesn't really matter, it just needs to be larger than
            // the other cube.
            translate([-0.5, 0 ,0])
            cube([2*ErgoDoxSideThumb+0.5, WristpadDepth, WristpadThickness]);

            // y value doesn't matter as long as it is bigger than WristpadDepth.
            rotate([0, 0, 180-ErgoDoxAngle])
            cube([ErgoDoxSideThumb, 2*WristpadDepth, WristpadThickness]);
        }
    }

    module _wristpad_palm() {
        cube([ErgoDoxSideFingers, WristpadDepth, WristpadThickness]);
    }
}

// I found this solution for rounded corners on  /r/openscad.  If you really
// stare at it you can figure out how it works.  It seems like it may not work
// correctly for some crazy geometries.  But for a convex hull the below module
// round corners effectively.
//
// https://www.reddit.com/r/openscad/comments/3edc60/made_a_module_for_generating_rounded_corners/cv1cqub
module fillet(r=1) {
    minkowski() {
        difference() {
            children();
            minkowski() {
                difference() {
                    minkowski() {
                        children();
                        sphere(r=r);
                    }
                    children();
                }
                sphere(r=r);
            }
        }
        sphere(r=r);
    }
}

```