[User]
Okay, I'd like to design a parametric key generator that can handle both 5-cylinder house keys and 4-cylinder padlock keys. I want to be able to customize several aspects of the key.

Here are the key parameters I'd like to control:

1.  **Key Type:** I need a dropdown to select whether to create a "5 Cylinder House Key" or a "4 Cylinder Pad Lock".

2.  **Key Header Style:** I want a dropdown to choose between a "Straight Header" or a "Round Header". The round header's shape will depend on the "Polygon Count" setting.

3.  **Print Quality / Polygon Count:** This should be a dropdown or slider to control the polygon count used for creating the round header, ranging from 4 to at least 32 (e.g., options like 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 24, 28, 32). This impacts the roundness of the key header.

4.  **Key Cutting Code:** This should be a text field where I can enter the cut code for the key. The code should be a string of digits (0-9), and the length will be determined by the type of key (5 digits for house key, 4 for padlock key). If left blank, a random key cut should be generated.

5.  **Custom Key Text:** This should be a text field for additional custom text that will print on the key in place of or in addition to the key cut code.

The design should also include these features, which shouldn't need to be user-configurable parameters but should still be controllable by the parameters above:

*   **Build Plate:** It needs to use the `build_plate.scad` library (I have that file already, so you don't need to include it) to show a build plate for a Replicator 2, Replicator, Thingomatic or manual dimensions.
*   **Text Printing:** It needs to use the `Write.scad` library (I have that file too) to print text on the key.
*   The key blades should have front, back, and top slots/grooves cut into them. These will be different for the house and padlock keys
*   The tip of the key blade should have a custom cut shape that will be different for the house and padlock keys
*   The key pin cuts should be along the top of the key blade with a step value determined by dividing the key top slot height by 10
*   The design should automatically adjust the cylinder positions, lengths, heights, and widths based on the type of key selected (house or padlock).
*   There should be a key ring hole in the header and its location should be adjustable based on whether or not it's a round or straight key
*   There should be a printed 'B' for the author on the back of the key, and its placement will also depend on key type and key header style.

Essentially, I want a single parametric OpenSCAD file that handles all of this. I am using `build_plate.scad` and `Write.scad`.

[Assistant]
House_key_vBelfry_068.scad:
```
// Filename: House_key_vBelfry_068.scad
// Author: Belfry ( John Gardner - JGPC )
// Date: 02/11/2013
// Phone: 901-366-9606
// Email: belfry6050@jgpc.net
// Web: www.thingiverse.com/belfry

// Project: A parametric key generator

// Supported types (so far ) - ( 5 cylinder house key and 4 cylinder padlock )

// Creates a customized key.

// use <MCAD/boxes.scad>
use <utils/build_plate.scad>
use <write/Write.scad>
//use <Write.scad>

// ----- start of 'customizer' user options -----

//: Select the TYPE OF KEY to make
type_of_key = 0; //[0:5 Cylinder House Key,1:4 Cylinder Pad Lock]
echo( "type_of_key" , type_of_key );

//: Select the KEY HEADER style to make
style_of_key = 1; //[0:Straight Header,1:Round (set by polygon count) Header]
echo ( "style_of_key" , style_of_key );

//: Print Quality-used by $fn parameter(s)
polygon_count = 5;	// [4,5,6,7,8,9,10,11,12,16,20,24,28,32]
echo( "Print Quality / Polygon Count" , polygon_count );

//: Key Cutting Code (0-9 for each cylinder) leave blank for random key
keycutcode = "";
echo ( "keycutcode" , keycutcode );

//: Custom text to print in place of the key code
custom_key_text ="";
echo ( "custom_key_text" , custom_key_text );

// ----- end of 'customizer' user options -----
dummycall();	// make sure no more stuff shows up in 'customizer' (?)

//---[ Build Plate ]----------------------------------------------------------

//: for display only, doesn't contribute to final object
build_plate_selector = 2; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
//: when Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 100 + 0;
//: when Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 100 + 0;

build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

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

// Key Cutting Code (0-9 for each cylinder)

//keycutcode = "8866";	//	silver padlock
//keycutcode = "6644";	//	silver padlock
//keycutcode = "7755";	 //	silver padlock
//keycutcode = "9977";	//	silver padlock
//keycutcode = "3277";

//keycutcode = "19909";
//keycutcode = "33626";

//keycutcode = "";
//keycutcode = "00000"; // should create a 'blank' key???
//keycutcode = "99999"; // should create a 'bump' key???
//key_cut_code = keycutcode;	// use this for the user entered value
//key_cut_code = random_key;	// comment this line out - for 'user entered' values to work

//key_cut_code = "000007777123499999";

//----- start of program 'constant' declarations -----

// define a value for 'pi'
pi_value = 3.14159 *1;	// pi
echo ( "Pi" , pi_value );

// define a 'fudge factor' ( see best practices - http://customizer.makerbot.com/best_practices )
fudge_factor = 0 + 0.001;
echo ( "fudge_factor" , fudge_factor );

// define a maximum cylinder count
maxcylindercount = 10+0;
echo( "maxcylindercount" , maxcylindercount );

// define an array of key cylinder counts
keycylindercount = [5,4];
echo ( "keycylindercount" , keycylindercount );

// Number of Cylinders
number_cylinders = keycylindercount[ type_of_key ];
echo ( "number_cylinders" , number_cylinders );

// define an array of print layer thickness
layerthickness = [0.30,0.30];
echo ( "layerthickness" , layerthickness );

// print layer thickness
layer_thickness = layerthickness[ type_of_key ];
echo ( "layer_thickness" , layer_thickness );

// define an array of key blade lengths (x-dimension)
keybladelength = [28.50,18.50];
echo ( "keybladelength" , keybladelength );

// Length of the Blade ( shoulder to tip ) (x-dimension)
blade_length = keybladelength[ type_of_key ];
echo ( "blade_length" , blade_length );

// define an array of key blade heights (y-dimension)
keybladeheight = [8.30,7.25];
echo ( "keybladeheight" , keybladeheight );

// Height of the Blade (y-dimension)
blade_height = keybladeheight[ type_of_key ];
echo ( "blade_height" , blade_height );

// define an array of key blade widths (z-dimension)
keybladewidth = [(7*layer_thickness),(7*layer_thickness)];	//	(2.1mm)-seven layers thick at 0.3mm thickness
echo ( "keybladewidth" , keybladewidth );

// Width of the Blade (z-dimension)
blade_width = keybladewidth[ type_of_key ];
echo ( "blade_width" , blade_width );

// define an array of height/size values for printing text
texthsize = [5.5,4.5];
echo( "texthsize" , texthsize );

// Height/Size of printed text ( 'write.scad' value )
text_h_size = texthsize[ type_of_key ];
echo ( "text_h_size" , text_h_size );

// Number Lookup Array
number_lookup = str( "0123456789" , "" );
echo( "number_lookup" ,  number_lookup );

// Array of random key pin STEP values ( 0 to 9.9 for each cylinder )
random_key_array = rands( 0 , 9.9 , maxcylindercount + 1 );
//random_key_array = rands( 0 , 9.9 , number_cylinders + 1 );
echo( "random_key_array" , random_key_array );

// 10 'charcter' random number
random_key = str( floor( random_key_array[1] ) , floor( random_key_array[2] ) , floor( random_key_array[3] ) , floor( random_key_array[4] ) , floor( random_key_array[5] ) , floor( random_key_array[6] ) , floor( random_key_array[7] ) , floor( random_key_array[8] ) , floor( random_key_array[9] ) , floor( random_key_array[10] ) );
echo ( "random_key: " , random_key );

// Key Cutting Code (0-9 for each cylinder)
key_cut_code = str(keycutcode,random_key);	// random_key used if keycutcode is blank
echo ( "key_cut_code" , key_cut_code );

// --- front slot definitions------------------------------------

// define an array of front slot heights
frontslotheight = [1.65,0.00];
echo ( "frontslotheight" , frontslotheight );

// define an array of front slot y offset values
frontslotyoffset = [0.60,0.00];
echo ( "frontslotyoffset" , frontslotyoffset );

// define an array of front slot z offset values
frontslotzoffset = [(3*layer_thickness),0.00];	//	0.90mm-at 0.30mm
echo ( "frontslotzoffset" , frontslotzoffset );

// Key Front Side Slot Height
key_fslot_height = frontslotheight[ type_of_key ];
echo ( "key_fslot_height" , key_fslot_height );

// Key Front Side Slot Y Offset
key_fslot_yoffset = frontslotyoffset[ type_of_key ];
echo ( "key_fslot_yoffset" , key_fslot_yoffset );

// Key Front Side Slot Z Offset
key_fslot_zoffset = frontslotzoffset[ type_of_key ];
echo ( "key_fslot_zoffset" , key_fslot_zoffset );

// --- back slot definitions------------------------------------

// define an array of back slot heights
backslotheight = [1.35,1.35];
echo ( "backslotheight" , backslotheight );

// define an array of back slot y offset values
backslotyoffset = [2.75,1.00];
echo ( "backslotyoffset" , backslotyoffset );

// define an array of back slot z offset values
backslotzoffset = [-(2*layer_thickness),-(2*layer_thickness)];	// 0.60mm - at 0.30mm
echo ( "backslotzoffset" , backslotzoffset );

// define an array of back slot angle cut values
backslotangle = [210,210];
echo ( "backslotangle" , backslotangle );

// Key Back Side Slot Height
key_bslot_height = backslotheight[ type_of_key ];
echo ( "key_bslot_height" , key_bslot_height );

// Key Back Side Slot Y Offset
key_bslot_yoffset = backslotyoffset[ type_of_key ];
echo ( "key_bslot_yoffset" , key_bslot_yoffset );

// Key Back Side Slot Z Offset
key_bslot_zoffset = backslotzoffset[ type_of_key ];
echo ( "key_bslot_zoffset" , key_bslot_zoffset );

// Key Back Side Angle Value
key_bslot_angle = backslotangle[ type_of_key ];
echo ( "key_bslot_angle" , key_bslot_angle );

// --- top slot definitions------------------------------------

// define an array of top slot heights
topslotheight = [3.50,3.50];
echo ( "topslotheight" , topslotheight );

// define an array of top slot z offset values
topslotzoffset = [(4*layer_thickness),(3*layer_thickness)];	//	1.20mm-at 0.30mm
echo ( "topslotzoffset" , topslotzoffset );

// Key Top Slot Height
key_tslot_height = topslotheight[ type_of_key ];
echo ( "key_tslot_height" , key_tslot_height );

// Key Top Slot Z Offset
key_tslot_zoffset = topslotzoffset[ type_of_key ];
echo ( "key_tslot_zoffset" , key_tslot_zoffset );

// define an array of top slot angle cut values
topslotangle = [30,30];
echo ( "topslotangle" , topslotangle );

// Key Top Slot Angle Value
key_tslot_angle = topslotangle[ type_of_key ];
echo ( "key_tslot_angle" , key_tslot_angle );

// --- tip of key definitions - top ------------------------------------

// define an array of tip of the key - top angle rotation values
keytiptopangle = [58,50];
echo ( "keytiptopangle" , keytiptopangle );

// key tip - top angle rotation value
key_tip_topangle = keytiptopangle[ type_of_key ];
echo ( "key_tip_topangle" , key_tip_topangle );

// define an array of tip of the key - top y offset values
keytiptopyoffset = [(key_bslot_yoffset+key_bslot_height),(blade_height-key_tslot_height)];
echo( "keytiptopyoffset" , keytiptopyoffset );

// key tip - top y offset value
key_tip_topyoffset = keytiptopyoffset[ type_of_key ];
echo ( "key_tip_topyoffset" , key_tip_topyoffset );

// --- tip of key definitions - bottom ------------------------------------

// define an array of tip of the key - bottom angle rotation values
keytipbotangle = [225,225];
echo ( "keytipbotangle" , keytipbotangle );

// key tip - bottom angle rotation value
key_tip_botangle = keytipbotangle[ type_of_key ];
echo ( "key_tip_botangle" , key_tip_botangle );

// define an array of tip of the key - bottom y offset values
keytipbotyoffset = [key_bslot_yoffset,(blade_height/2)];
echo( "keytipbotyoffset" , keytipbotyoffset );

// key tip - bottom y offset value
key_tip_botyoffset = keytipbotyoffset[ type_of_key ];
echo ( "key_tip_botyoffset" , key_tip_botyoffset );

// --- cylinder definitions --------------------------------------

// define an array of 1st cylinder offset values
firstcyloffset = [6.00,5.00];
echo ( "firstcyloffset" , firstcyloffset );

//: Offset to 1st Cylinder ( shoulder to center )
first_offset = firstcyloffset[ type_of_key ];
echo ( "first_offset" , first_offset );

// define an array of distance between cylinder values
cylseparation = [4.00,3.00];
echo ( "cylseparation" , cylseparation );

// Distance Between Cylinders ( center to center )
cylinder_separation = cylseparation[ type_of_key ];
echo ( "cylinder_separation" , cylinder_separation );

// define an array of width of cylinder pin values
cylwidth = [2.00,1.00];
echo ( "cylwidth" , cylwidth );

// Width of individual cylinder pins
key_cyl_width = cylwidth[ type_of_key ];
echo ( "key_cyl_width" , key_cyl_width );

// define an array of cylinder pin STEP values
pinstep = [(key_tslot_height / 10), (key_tslot_height / 10) ];
echo ( "pinstep" , pinstep );

// Cylinder pin STEP value
pin_step = pinstep[ type_of_key ];
echo( "pin_step" , pin_step );

// define an array of straight key header lengths
straightlength = [27,22];	// mm
echo ( "straightlength" , straightlength );

// define the straight key length
straight_length = straightlength[ type_of_key ];
echo ( "straight_length" , straight_length );

//short_side = ( ( blade_width - key_tslot_yoffset ) / tan(60) );
//echo ( "short side" , short_side );

// define an array of how many layers thick to print the text
layersthick = [3,3];	//	number of layers thick
echo ( "layersthick" , layersthick );

// number of layers thick ( for printed text )
layers_thick = layersthick[ type_of_key ];
echo ( "layers_thick" , layers_thick );

// define an array of text separation values (mm)
textseparation = [3.8,3.5];	//	distance between text (mm)
echo ( "textseparation" , textseparation );

// distance between characters (mm)
text_separation = textseparation[ type_of_key ];
echo ( "text_separation" , text_separation );

// define an array of key ring hole radius values (mm)
keyringradius = [4.5,4.0];	//	radius of the hole (mm)
echo ( "keyringradius" , keyringradius );

// radius for the key ring hole
key_ring_radius = keyringradius[ type_of_key ];
echo ( "key_ring_radius" , key_ring_radius );

// define an array of key head diameter values (mm)
keyheaddiameter = [25,20];	//	diameter of the key head
echo ( "keyheaddiameter" , keyheaddiameter );

// diameter for the key head
key_head_diameter = keyheaddiameter[ type_of_key ];
echo ( "key_head_diameter" , key_head_diameter );

// ---[ Start of program ]-------------------------------------------------------
echo ( "start of the program" );
{

difference()
{
union()
{

create_key_blade();
create_key_shoulder();
create_key_header();

}	//	end of union

create_key_ringhole();

create_belfry_B();

} // end of difference

} // end of program

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

echo ("end of program" );

// ---[ End of program ]------------------------------------------------------

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

// ---[ Start of module definitions ] ---

// ----- cut the main key slots (millings/grooves) module ---------------------
module cut_slots()
{
echo ( "cut the main key slots module" );

// ----- front slot setup -----
echo ( "front slot setup" );
front_slot();

// ----- back slot setup -----
echo ( "back slot setup" );
back_slot();

// ----- top slot setup -----
echo ( "top slot setup" );
top_slot();

}	//	end of module

// ----- front slot setup module ---------------------------------------------
module front_slot()
{
echo ( "front slot setup module" );
translate([-1,key_fslot_yoffset,key_fslot_zoffset]) cube( [ blade_length + 2 , key_fslot_height , blade_width ] );
}	//	end of module

// ----- back slot setup module ----------------------------------------------
module back_slot()
{
echo ( "back slot setup module" );
translate([-1,key_bslot_yoffset,key_bslot_zoffset])
{
union()
{

cube( [ blade_length + 2 , key_bslot_height , blade_width ] );

if ( key_bslot_angle != 0 )
{
translate([0,key_bslot_height,blade_width]) rotate( [key_bslot_angle,0,0] ) cube( [ blade_length + 2 , key_bslot_height , blade_width * 2 ] );
}	//	end of if
}	// end of union
}	// end of translate

}	//	end of module

// ----- top slot setup module ------------------------------------------------
module top_slot()
{
echo ( "top slot setup module" );
translate( [-1,blade_height-key_tslot_height,key_tslot_zoffset] )
{
union()
{

rotate( [key_tslot_angle,0,0] ) cube( [ blade_length + 2 , blade_height , blade_width * 2 ] );
cube( [ blade_length + 2 , blade_height  , blade_width * 2 ] );

}	// end of union
}	// end of translate
}	// end of module

// ----- cut off the tip of the blade -------------------------------------------
module cut_the_tip()
{
echo ( "cut off the tip of the blade module" );
union()
{
translate( [blade_length,key_tip_topyoffset,-1] ) rotate( [0,0,key_tip_topangle] ) cube( blade_height );
translate( [blade_length,key_tip_botyoffset,-1] ) rotate( [0,0,key_tip_botangle] ) cube( blade_height );
}	// end of union
}	//	end of module

// ----- cylinder cutting loop ---------------------------------------------------
module cut_cylinders( total_cylinders = 1 )
{
echo ( "start of cylinder cutting loop for" , total_cylinders , "cylinders" );
for ( cyl_loop = [1 : total_cylinders] )
{

	echo(str("cylinder:" , cyl_loop , " - key_cut_code:" , key_cut_code[cyl_loop-1]));

	pin_position( what_str_digit = key_cut_code[cyl_loop-1] , what_cyl = cyl_loop );

}  // end of loop

}	// end of module

// --- Key Cylinder Cutout ---------------------------------------------------
module key_cyl_cutout( cylinder_number = 1 )
{
echo ("Key Cylinder Cutout Shape for Cylinder:" , cylinder_number );

union()
{
translate([(first_offset-(key_cyl_width/2)) + ((cylinder_number-1)*cylinder_separation) ,blade_height,-1]) cube([key_cyl_width,blade_height,blade_width+2 ]);

translate([(first_offset-(key_cyl_width/2)) + ((cylinder_number-1)*cylinder_separation),blade_height,-1]) rotate( [0 , 0 , 45] )cube([blade_height,blade_height,blade_width+2]);

translate([(first_offset+(key_cyl_width/2)) + ((cylinder_number-1)*cylinder_separation),blade_height,-1]) rotate( [0 , 0 , 45] )cube([blade_height,blade_height,blade_width+2]);

} // end of union
}

// --- Set the position of a cylinder pin ---------------------------------------------
module pin_position( what_str_digit = "0" , what_cyl = 1 )
{

echo( "Cylinder: " , what_cyl , "Digit: " , what_str_digit );

//for ( value_lookup = [0:9] )
for ( value_lookup = [1:9] )
{

if ( what_str_digit == number_lookup[value_lookup] )
{
echo( "match found: " , value_lookup , what_str_digit );

translate([0,(-1*(value_lookup*pin_step)),0]) key_cyl_cutout(cylinder_number=what_cyl);

}	// end of if

}	// end of for loop

}	// end of module

//-------------------------------------------------------------------------
module straight_key_header( sheader_length = 25 , sheader_text = "0123456789" )
{
echo ( "sheader_length" , sheader_length );
echo ( "sheader_text" , sheader_text );

translate([(-1*sheader_length)+fudge_factor,0,0])
{
union()
{

// ----- basic straight key head setup -----
cube( [ sheader_length , blade_height  , blade_width ] );	// main key header

// ----- print the text ------------------------------------------------------

for ( sdigit_loop = [0:number_cylinders-1] )
{
	echo ( "sdigit_loop" , sdigit_loop , "text" , sheader_text[sdigit_loop] );

	translate([(text_separation+(text_separation*sdigit_loop)),1,blade_width-fudge_factor]) write(sheader_text[sdigit_loop],h=text_h_size,t=(layer_thickness*layers_thick));

}	//	end of for loop

}	//	end of union

}	// end of translate

}	// end of module

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

//-------------------------------------------------------------------------
module round_key_header( rheader_diameter = 25 , rheader_text = "0123456789" )
{
echo ( "rheader_diameter" , rheader_diameter );
echo ( "rheader_text" , rheader_text );

translate([(-1*rheader_diameter)+fudge_factor,0,0])
{

translate([(rheader_diameter/2),(blade_height/2),0]) cylinder( h=blade_width , r=rheader_diameter/2 , center = false , $fn=polygon_count);	//	round header for key

if ( polygon_count > 4 )
{

// let's rotate the text
rotate([0,0,90]) union()
{

// ----- add the 'key code' ------------------------------------------------------
for ( rdigit_loop = [0:number_cylinders-1] )
{
	echo ( "rdigit_loop" , rdigit_loop , "text" , rheader_text[rdigit_loop] );

	translate([(text_separation*(rdigit_loop+1))+(blade_height/2)-(rheader_diameter/2),-(rheader_diameter/2)-(text_h_size),blade_width-fudge_factor]) write(rheader_text[rdigit_loop],h=text_h_size,t=(layer_thickness*layers_thick));

}	//	end of for loop

}	// end of translate

}	//	end of union

}	//	end of polygon_count

}	// end of module

// ----- create text to be printed module-----------------------------------------------
module create_text( text_to_print="Belfry 6050" )
{

echo ( "text_to_print" , text_to_print );

write(text_to_print,h=text_h_size,t=(layer_thickness*layers_thick));

}	//	end of module

//----- dummycall module -------------------------------------------------------
module dummycall()
{
echo ( "dummycall" );
}

//----- 5 cylinder house key shoulder ------------------------------------------
module five_cyl_housekey_shoulder()
{
translate([fudge_factor-blade_height,-(blade_height*0.25),0])
cube([blade_height,(blade_height*1.5),blade_width]);
}

//----- 4 cylinder padlock key shoulder ----------------------------------------
module four_cyl_padlock_shoulder()
{
translate([fudge_factor-blade_height,0,0])
cube([blade_height,(blade_height*1.25),blade_width]);
}

//----- create a 'key blade' ------------------------------------------------------
module create_key_blade()
{

echo ( "start of the create_key_blade module" );

echo ( "start of the difference function" );
difference()
{

// ----- main blade setup -----
echo ( "main blade setup" );
cube( [ blade_length , blade_height , blade_width ] );	// main key blade

// ----- cut the main key slots -----
echo ( "cut the main key slots" );
cut_slots()

// ----- cut off the tip of the blade -----
echo ( "cut off the tip of the blade" );
cut_the_tip();

// ----- cylinder cutting loop ---------------------------------------------------
echo ( "start cutting" , number_cylinders , "cylinders" );
cut_cylinders( total_cylinders = number_cylinders );

} // end of difference

}	//	end of module

//----- create a 'shoulder' for the key ------------------------------------------
module create_key_shoulder()
{
echo ( "start of create_key_shoulder module" );

if ( style_of_key > 0 )
{
	if ( type_of_key == 0 )
		five_cyl_housekey_shoulder();

	if ( type_of_key == 1 )
		four_cyl_padlock_shoulder();

}	//	end of if
}	//	end of module

//------- create the cylinder used to cut a hole in the key ----------------
module create_key_ring_hole()
{
echo( "key_ring_radius for cylinder" , key_ring_radius );
if ( style_of_key > 0 )
{
	// create the cylinder used for cutting a hole for a key ring
	cylinder( h=blade_width*3 , r=key_ring_radius , center = true , $fn=polygon_count);	//	hole for key ring

}	//	end of if
}	//	end of module

//------ create a key header object ----------------------------------------
module create_key_header()
{

if ( style_of_key == 0 )
{
// create a basic 'straight' key header
straight_key_header(sheader_length=straight_length,sheader_text=str(custom_key_text,key_cut_code));
}	// end of if

if ( style_of_key == 1 )
{
// create a 'round' key header
round_key_header(rheader_diameter=key_head_diameter,rheader_text=str(custom_key_text,key_cut_code));
}	// end of if

}	//	end of module

//-------------------------------------------------------------------------
//------- put a hole in the key header for a key chain ring ----------------
module create_key_ringhole()
{
if ( style_of_key > 0 )
{
if ( polygon_count == 5 )
{

	if ( type_of_key == 0 )
{
	translate([(-1*(key_head_diameter))+(key_ring_radius*2)-2,(blade_height/2),0]) create_key_ring_hole();
}
else
{
	translate([((-1*(key_head_diameter))+ key_ring_radius + 2 ),(blade_height/2),0]) create_key_ring_hole();

}	//	end of if
}
else
{
	translate([((-1*(key_head_diameter))+ key_ring_radius + 2 ),(blade_height/2),0]) create_key_ring_hole();

}	//	end of if

}	//	end of if

}	//	end of module

//----- create the Belfry 'B' on the back of the key ----------------------
module create_belfry_B()
{

//----- straight key headers -----
//	straight key header - ( style_of_key == 0 )
if ( style_of_key == 0 )	//	straight key header
{

	translate([-15,0.5,(layer_thickness*3)]) rotate([0,180,270])
{

// print the Belfry 'B' on the back

	if (type_of_key == 0 )	//	5 cyl house key
{
		write("B",h=11,t=layer_thickness*4);
}	//	end of if

	if (type_of_key == 1)	//	4 cyl padlock
{
		write("B",h=10,t=layer_thickness*4);
}	//	end of if

}	//	end of translate

}	//	end of if - ( style_of_key == 0 )

//----- round key headers -----
//	round key header - ( style_of_key == 1 )
if ( style_of_key == 1 )	//	round key header
{

	if ( type_of_key == 0 )	//	5 cyl house key
{
// print the Belfry 'B' on the back
		translate([-1.25,7,(layer_thickness*3)]) rotate([0,180,90]) write("B",h=11,t=(layer_thickness*4));

}	// end of if - type_of_key

	if ( type_of_key == 1 )	//	4 cyl padlock
{
// print the Belfry 'B' on the back
		translate([-0.75,6,(layer_thickness*3)]) rotate([0,180,90]) write("B",h=8,t=(layer_thickness*4));
}	//	end of if - ( type_of_key == 1 )

}	//	end of if - ( round key headers )

}	//	end of module

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

```


[User]
Okay, I'd like a parametric OpenSCAD file for a wall-mounted hook, inspired by the Muuto Dots coat hooks but with a few specific design choices and adjustable parameters.

Here's the general idea:

The hook should have a rounded, disk-like outer part connected to a cylindrical shaft that goes to the wall. The outer disk has a smooth, rounded edge, almost like a flattened doughnut. I'd like to adjust the following:

*   **`thickness`**: The thickness of the outer disk part. I'm thinking a range between 10mm and 40mm.
*   **`wall_distance`**: The distance the back of the outer disk sits from the wall.  Also, a range of 10mm to 40mm.
*   **`roundness`**: How much the outer edge of the disk is rounded. Range from 0mm (a sharp edge) to 50mm.
*   **`Diameter`**: The overall diameter of the outer disk. Let's make the range from 70mm to 150mm.
*   **`Diameter2`**: The diameter of the cylindrical shaft that goes back to the wall. This should be smaller than the main diameter minus the roundness and needs to account for screws too. I'll need a minimum of 36mm and going up to 100mm.
*  **`screw_distance`**: The distance between the center of the screw holes. I'll specify this to 20mm initially.
* **`screw_hole_size`**: The diameter of the screw head I want to be recessed inside a hole. Let's use 8mm initially.
*  **`$fn`**: The number of segments for circles, currently I use `$fn=100`.

I want the back part of the hook to be a simple cylinder. The connection between the main disk and the cylinder should be a smooth transition.

For mounting, I need two countersunk screw holes in the back cylinder. The current design uses `polyhedron` with `points` and `triangles` and it works, but it’s really not elegant. It would be great if we could make the screw holes more “refined”, but still allow for adjustment using `screw_distance` and `screw_hole_size`. Perhaps using a more standard OpenSCAD technique for creating countersunk screw holes?

Ideally, I'd like to avoid using libraries like BOSL or similar if that’s reasonably possible given the requirements. If it's really necessary, please let me know why and which library you are using.

[Assistant]
Coat_hook.scad:
```
// How thick the outer part will be
thickness=25; // [10:40]
// How far the outer part will be from the wall
wall_distance=25; // [10:40]
// How rounded the outer edge is
roundness=25; // [0:50]
// Diameter of outer part
Diameter=80; // [70:150]
//The diameter of the shaft towards the wall (should be smaller than outer diameter - roundness, but higher than screw distance + 2*screw hole size)
Diameter2=36; // [36:100]
// The distance between the screws, from left side to left side
screw_distance=20;
//The diameter of the screw head
screw_hole_size=8;
// How fine the round parts are
$fn=100;

s=screw_hole_size*1.53;
r1=Diameter/2;

translate([0,0,thickness/2])
cylinder(h=thickness,r=r1-roundness,center=true);

translate([0,0,thickness/2])
rotate_extrude(convexity=10)
translate([r1-roundness,0,0])
scale([roundness/thickness,1,1])
circle(thickness/2,center=true);

difference(){
translate([0,0,thickness+wall_distance/2])
cylinder(h=wall_distance,r=Diameter2/2,center=true);
translate([-s,-screw_distance/2,thickness+wall_distance-s/4])
polyhedron(
	points=[[s,s,0],[s,-s,0],[-s,0,0],[s,0,s/2]],
	triangles=[[0,2,1],[0,1,3],[2,0,3],[1,2,3]]);

translate([-s,screw_distance/2,thickness+wall_distance-s/4])
polyhedron(
	points=[[s,s,0],[s,-s,0],[-s,0,0],[s,0,s/2]],
	triangles=[[0,2,1],[0,1,3],[2,0,3],[1,2,3]]);
}

difference(){
translate([0,0,thickness+wall_distance/2])
cylinder(h=wall_distance,r=Diameter2/2,center=true);
translate([0,0,thickness+wall_distance/2])
cylinder(h=wall_distance+1,r=Diameter2/2-1,center=true);}
```


[User]
Okay, I want to design a custom bracelet using OpenSCAD, but I want it to be flexible and customizable through a few key parameters. I was looking at a design online (shape_bracelet.scad) that is close, but I need to make it my own.

Here's what I'm looking for:

**Overall Structure:**

The bracelet should be built from repeating shapes arranged in a circular pattern to form a band. I want to control the number of rows (or layers) of these shapes, their overlap, whether the edges of the bracelet are outlined, and the thickness of the lines/shapes themselves. I also need the option to make the band hollow or solid.

**Specific Parameters (and ranges):**

1.  **`bracelet_size`:** This is the inner diameter of the bracelet. I need a good range of sizes to select from, not necessarily specific ones in the other script. Think something like a number in millimeters, perhaps from 30mm to 90mm.
2.  **`width_in_millimeters`**: The width of the bracelet surface. Let's say we'll allow this to be between 8mm and 30mm.
3.  **`shape`**: This determines the basic shape used to make up the bracelet. I'd like to keep the options from the other design, which include Triangle (3), Square (4), Pentagon (5), Hexagon (6), Heptagon (7), Octagon (8), Nonagon (9), Decagon (10), and Circle (20)
4.  **`Stacked`**: This controls the number of rows of shapes (single, double, triple, etc.). Please allow me to specify any whole number starting at 1.
5.  **`Overlap`**: Controls how much the shapes overlap. I'd like this to be a value, where 1 is just touching, greater than 1 means they overlap, and less than 1 means there is a space. We'll use a value range between 0.5 and 4.
6.  **`Outlined`**: A boolean (0 or 1) to determine if the bracelet edges are outlined or not.
7. **`Line_thinkness`**: How thick the lines are in millimeters (1 to 4).
8.  **`Hollow_braclet_band`**: A boolean to determine if the bracelet band is hollow or solid (0 for solid, 1 for hollow).

**Functionality:**

*   The core of the bracelet should be built using a `for` loop to repeat shapes around the circumference, and another `for` loop or `if` condition to stack the shapes to the correct `Stacked` number.
*   There should be an optional outline around the edges if `Outlined` is set to 1
* The hollow bracelet should have a specific inner radius for making sure its not too small or too big.

**Libraries:**

I don't need any specific libraries, just the basic OpenSCAD functionality. Please avoid using any libraries like MCAD, BOSL, BOSL2, dotSCAD, NopSCADlib, or BOLTS.

Essentially, I need a flexible bracelet generator that I can customize with these parameters.

[Assistant]
shape_bracelet.scad:
```
// Joe Stubbs
// February 24 2012
// http://www.thingiverse.com/thing:53816

use <utils/build_plate.scad>

$fn=20*1;

//pick from the list of available bracelet sizes
bracelet_size = 65; //[35:0-6 Mos,39:6-12 Mos,44:12-24 Mos,48:2-5 Yrs,52:6-8 Yrs,57:9-13 Yrs,62:Women's Petite,65:Women's Small,68:Women's Medium,75:Women's Large,80:Women's Plus,86:Women's Ankle,70:Men's Small,73:Men's Medium,78:Men's Large,83:Men's Plus]

//width of the bracelet surface
width_in_millimeters = 20; // [8:30]

// What basic shape do you wish to make you bracelet out of
shape = 20;  //[3:Triangle,4:Square,5:Pentagon,6:Hexagon,7:Heptagon,8:Octagon,9:Nonagon,10:Decagon,20:Circle]

//How many rows of shapes would you like?
Stacked = 3; // [1:Single, 2:Double, 3:Triple]

//The overlap between the shapes.  Note: If the shapes do not overlap you must add the outline for the bracelet to hold together
Overlap = 0.75; // [0.5:Spaced, 0.75:Small Spaces, 1:Just Touching, 1.25:Small, 1.5:Medium, 1.75:Larger, 2:Double,3:Tripple, 4:Quadrupal]

//Would you like the bracelet edges outlined.  This may be required if your shapes are not touching based on previous selections.
Outlined = 0; // [1:Yes,0:No]

//How thick would you like the lines to be?
Line_thinkness = 2; //[1:1mm Thin,2:2mm Normal,3:3mm Normal,4:4mm Thick]

//Do you want the bracelet to be hollow?
Hollow_braclet_band=1; // [1:Yes,0:No]

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

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

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

build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

// adjust the inside diameter depending if the bracelet is hollow or not.
inside_width = bracelet_size+(Hollow_braclet_band*(width_in_millimeters/3)); // x
inside_height = inside_width * 55 / 65; // y

thick = Line_thinkness;
shape_width = Line_thinkness;

outside_width = inside_width + (thick) / 2 * 2; // x
outside_height = inside_height + thick + thick; //y

width = width_in_millimeters;

number_of_shapes_height = Stacked;

circumferance = outside_height*3.1415;

echo("minimum_number_of_shapes :", minimum_number_of_shapes );

fudge = 0.05*1;

ratio = Overlap;
number_of_shapes = (round(circumferance/(width/round(number_of_shapes_height/2))*ratio/2)+1)*2; // Round(/2)*2 to get an even number all the time!
rotate_angle = 360/number_of_shapes;

width_of_ring = width/3;

shape_length = outside_height+width_of_ring*2+2; //just needs to be larger then any braclet you might make

/*
difference() {
bracelet_space ();
		cube([100,10,100],center=true);
}
*/

translate (v=[0, 0,(width/2)])
scale (v=[outside_width/outside_height,1,1])
intersection() {
	bracelet_allshapes();
	bracelet_space ();
}

module bracelet_space () {

if (Hollow_braclet_band==0) {
	difference () {
	// full shape

		cylinder(r = outside_height/2, h = width, center = true);

	// inside cutout
			cylinder(r = inside_height/2, h = width+fudge, center = true);

	}
} else {

difference () {
	rotate_extrude(convexity = 10)
		translate([(outside_height/2+inside_height/2)/2, 0, 0])
				scale (v=[1,width/width_of_ring,1])
					circle(r = width_of_ring/2, $fn = 20, center= true);

	rotate_extrude(convexity = 10)
		translate([(outside_height/2+inside_height/2)/2, 0, 0])
				scale (v=[1,width/width_of_ring,1])
					circle(r = width_of_ring/2-thick, $fn = 20,center= true);
}

}

}

module bracelet_allshapes () {

	for (h = [1:number_of_shapes_height]) {
		if (number_of_shapes_height==1)
			bracelet_spin();

		if (number_of_shapes_height==2) {
			if (h==1)
				translate (v=[0, 0,width/6-shape_width/4])
					bracelet_spin();
			if (h==2)
				rotate (a=rotate_angle/2,v=[0,0,1])
				translate (v=[0, 0,-width/6+shape_width/4])
					bracelet_spin();
		}

		if (number_of_shapes_height==3) {
			if (h==1)
				translate (v=[0, 0,width/2/2-shape_width/4])
					bracelet_spin();
			if (h==2)
				rotate (a=rotate_angle/2,v=[0,0,1])
					bracelet_spin();
			if (h==3)
				translate (v=[0, 0,-width/2/2+shape_width/4])
					bracelet_spin();
		}

	}

	if (Outlined ==1)
		bracelet_outline ();
}

module bracelet_spin () {

	for (i = [1:number_of_shapes/2]){
		//echo("i:", i);
		rotate (a=rotate_angle*i,v=[0,0,1])
			bracelet_shape ();
	}
}

module bracelet_shape () {
	rotate (a=90,v=[1,0,0])
{
		if (number_of_shapes_height==1) {
		difference() {
			cylinder(r = width/2, h = shape_length, center = true, $fn=shape);
			cylinder(r = width/2-shape_width, h = shape_length+fudge, center = true, $fn=shape);
			}
		}

		if (number_of_shapes_height==2) {
		difference() {
			cylinder(r = width/3, h = shape_length, center = true, $fn=shape);
			cylinder(r = width/3-shape_width, h = shape_length+fudge, center = true, $fn=shape);
			}
		}

		if (number_of_shapes_height==3) {
			difference() {
			cylinder(r = width/2/2, h = shape_length, center = true, $fn=shape);
			cylinder(r = width/2/2-shape_width, h = shape_length+fudge, center = true, $fn=shape);
			}
		}

	}

}

module bracelet_outline () {

	// full shape
		translate (v=[0, 0,width/2-shape_width])
		cylinder(r = outside_height/2, h = shape_width*2, center = true);

	// full shape
		translate (v=[0, 0,-width/2+shape_width])
		cylinder(r = outside_height/2, h = shape_width, center = true);

/*
	if (number_of_shapes_height==2) {//  && ratio < 1.25) {
			cylinder(r = outside_height/2, h = shape_width, center = true);
	}

	if (number_of_shapes_height==3 ) {// && ratio < 1.75) {
		translate (v=[0, 0,width/6-shape_width/2])
			cylinder(r = outside_height/2, h = shape_width, center = true);
		translate (v=[0, 0,-width/6+shape_width/2])
			cylinder(r = outside_height/2, h = shape_width, center = true);
	}
*/

}

```


[User]
Okay, I'm looking to design a filament holder that sits on top of two crossbars and interfaces with a spindle from another design. I have a pretty good starting point using a file I found online, `filament-holder.scad`, but I need it to be more flexible and customizable.

Here's what I have in mind, and what I'd like to be able to control with parameters:

**General Structure:**

The design consists of three main parts: a bottom bar, a top bar, and two retaining caps.  The bottom and top bars are essentially identical except for the pegs on the top and the peg holes on the bottom for connecting to the retaining caps. The design uses a horizontal cross bar which is meant to be 8mm diameter (parameterized later) and two crossbars parallel to that are meant to be 6mm in diameter(parameterized later).

**Key Parameters I Need:**

1.  **`crossbar_diameter`**: The diameter of the horizontal threaded rod that goes through the top and bottom bars. This is currently hardcoded to 8mm.
2.  **`crossbar_spacing`**:  The center-to-center distance between the two crossbars. This is currently hardcoded to 58mm.
3.  **`vertical_bar_diameter`**: The diameter of the vertical rods that connect the top and bottom bars together. Currently hardcoded to 6mm.
4.  **`peg_tolerance`**:  The percentage adjustment for the pegs/holes used to secure the retaining cap.  For example, a `peg_tolerance` of 5 would mean peg holes are 5% larger and pegs are 5% smaller.
5. **`bar_width`**: The width of the main body of the top and bottom bars. Currently this appears to be `crossbar_diameter * 2`
6. **`bar_height`**: The height of the main body of the top and bottom bars.  Currently this appears to be `crossbar_diameter * 2`
7. **`cap_height`**: The height of the retaining cap that holds the filament spool in place. Currently this appears to be `vertical_bar_diameter * .25`
8.  **`peg_height`**: The length of the retaining cap's pegs. Currently appears to be `crossbar_diameter/2`

**Functionality and Changes:**

*   The design should use the `build_plate` module from `utils/build_plate.scad` to define the print area, as the existing design does. I don't need you to provide the code for `build_plate.scad`, just include the use statement.
*   The top and bottom bars should be identical aside from the peg/hole differences. I'd like to have a single module for the bar, and then call it twice with different rotation parameters, and a different translate for the hole or peg locations.
*   The retaining caps need to connect using pegs that should have a variable tolerance relative to the holes on the top bar.
*   I'd like to be able to specify which part of the assembly (or all) to render.  Specifically,  "bottom", "top", "cap", or "all" just like the original code.
*   I want the bars to have rounded edges on the ends, as the current code does.
*   I do not want the holes for the horizontal crossbar in the center to use a `hull()`, this can just be a simple cylinder subtraction.

**Output:**

I want a single OpenSCAD file that can produce all of the parts based on parameters.

**Library Requirements:**

*   I expect to see a `use <utils/build_plate.scad>;` line, just like the existing code. Please make sure to include this.

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

[Assistant]
filament-holder.scad:
```
// Copyright (c) 2013, Jacob Helwig <jacob@technosorcery.net>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

use <utils/build_plate.scad>;

// Which build plate do you want?
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual plate size]
// Manual x dimension in mm:
build_plate_manual_x = 200; //[100:400]
// Manual y dimension in mm:
build_plate_manual_y = 200; //[100:400]
// Pegs minus this percent, and peg holes plus this percent
peg_tolerance = 5;

build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

// Choose a piece:
part = "all"; // [bottom:Bottom bar,top:Top bar,cap:Retaining cap,all:All pieces]

// Diameter of top threaded cross-bar in mm.
crossbar_diameter = 8;
// Distance between top cross-bars (Center to center) in mm.
crossbar_spacing = 58;
// Diameter of rod to connect top & bottom bars.
vertical_bar_diameter = 6;

print_part();

module print_part() {
    if (part == "bottom") {
        translate([0,0,crossbar_diameter]) bottom_bar();
    } else if (part == "top") {
        translate([0,0,crossbar_diameter]) top_bar();
    } else if (part == "cap") {
        retaining_cap();
    } else if (part == "all") {
        all_parts();
    } else {
        all_parts();
    }
}

module bottom_bar() {
    translate([0,0,crossbar_diameter]) rotate([90,0,0]) {
        difference() {
            union() {
                cube(
                    size = [
                        crossbar_spacing + vertical_bar_diameter*4,
                        crossbar_diameter * 2,
                        crossbar_diameter * 2
                    ],
                    center = true
                );
                translate(v = [(crossbar_spacing + vertical_bar_diameter*4)/2, 0, 0]) rounded_end();
                translate(v = [-(crossbar_spacing + vertical_bar_diameter*4)/2, 0, 0]) rounded_end();
            }

            hull() {
                translate(v = [crossbar_spacing/2,0,0]) bar_hole(crossbar_diameter);
                translate(v = [crossbar_spacing/2,0,-crossbar_diameter]) bar_hole(crossbar_diameter);
            }
            hull() {
                translate(v = [-crossbar_spacing/2,0,0]) bar_hole(crossbar_diameter);
                translate(v = [-crossbar_spacing/2,0,-crossbar_diameter]) bar_hole(crossbar_diameter);
            }

            rotate([90,0,0]) translate(v = [(crossbar_spacing/2) + (crossbar_diameter + vertical_bar_diameter/2),0,0]) bar_hole(vertical_bar_diameter);
            rotate([90,0,0]) translate(v = [-((crossbar_spacing/2) + (crossbar_diameter + vertical_bar_diameter/2)),0,0]) bar_hole(vertical_bar_diameter);
        }
    }
}

module top_bar() {
    translate([0,0,crossbar_diameter]) rotate([-90,0,0]) {
        difference() {
            union() {
                cube(
                    size = [
                        crossbar_spacing + vertical_bar_diameter*4,
                        crossbar_diameter * 2,
                        crossbar_diameter * 2
                    ],
                    center = true
                );
                translate(v = [(crossbar_spacing + vertical_bar_diameter*4)/2, 0, 0]) rounded_end();
                translate(v = [-(crossbar_spacing + vertical_bar_diameter*4)/2, 0, 0]) rounded_end();
            }

            rotate([90,0,0]) translate(v = [(crossbar_spacing/2) + (crossbar_diameter + vertical_bar_diameter/2),0,0]) bar_hole(vertical_bar_diameter);
            rotate([90,0,0]) translate(v = [-((crossbar_spacing/2) + (crossbar_diameter + vertical_bar_diameter/2)),0,0]) bar_hole(vertical_bar_diameter);

            hull() {
                bar_hole(vertical_bar_diameter);
                translate(v = [0,0,crossbar_diameter]) bar_hole(vertical_bar_diameter);
            }
            translate(v = [0,-crossbar_diameter,0]) cap_holes(1 + peg_tolerance / 100.0);
        }
    }
}

module retaining_cap() {
    translate([0,0,(vertical_bar_diameter*.25)/2]) rotate([90,0,0]) union() {
        difference() {
            hull() {
                translate([vertical_bar_diameter*2,0,0]) rotate([90,0,0]) cylinder(
                    h = vertical_bar_diameter * .25,
                    r = vertical_bar_diameter,
                    center = true
                );
                translate([-vertical_bar_diameter*2,0,0]) rotate([90,0,0]) cylinder(
                    h = vertical_bar_diameter * .25,
                    r = vertical_bar_diameter,
                    center = true
                );
            }
            hull() {
                bar_hole(vertical_bar_diameter);
                translate(v = [0,0,crossbar_diameter]) bar_hole(vertical_bar_diameter);
            }
        }
        translate(v = [0,vertical_bar_diameter * (.25/3) + crossbar_diameter/4,0]) cap_pegs(1 - peg_tolerance / 100.0);
    }
}

module all_parts() {
    translate([0,-crossbar_diameter*4,0]) retaining_cap();
    translate([0,0,0]) top_bar();
    translate([0,crossbar_diameter*4,0]) bottom_bar();
}

module cap_pegs(tolerance) {
    translate(v = [vertical_bar_diameter*2, 0, 0]) scale([tolerance,tolerance,tolerance]) cap_peg();
    translate(v = [-vertical_bar_diameter*2, 0, 0]) scale([tolerance,tolerance,tolerance]) cap_peg();
}

module cap_peg() {
    rotate([90,0,0]) cylinder(
        h = crossbar_diameter/2,
        r = vertical_bar_diameter / 2,
        center = true
    );
}

module cap_holes(tolerance) {
    translate(v = [vertical_bar_diameter*2, 0, 0])  scale([tolerance,tolerance,tolerance]) cap_hole();
    translate(v = [-vertical_bar_diameter*2, 0, 0]) scale([tolerance,tolerance,tolerance]) cap_hole();
}

module cap_hole() {
    rotate([90,0,0]) cylinder(
        h = crossbar_diameter,
        r = vertical_bar_diameter / 2,
        center = true
    );
}

module bar_hole(diameter) {
    rotate([90,90,0]) cylinder(
        h = crossbar_diameter * 3,
        r = diameter/2,
        center = true
    );
}

module rounded_end() {
    rotate([90,90,0]) cylinder(
        h = crossbar_diameter * 2,
        r = crossbar_diameter,
        center = true
    );
}

```


[User]
Okay, I'm looking to design a specialized bearing for 3D printing, and I've got a really interesting idea based on planetary gearing. I've included a description and an example OpenSCAD file that does exactly what I want, but I'd like the design parameterized and made more robust in a new file.

Essentially, this bearing uses herringbone gears as rollers within a ring, and everything is 3D printed as a single piece, pre-assembled. It avoids using traditional balls or needles which can be problematic for 3D printing. The herringbone nature allows for both radial and thrust loading, since the planets can't slide out axially, and the gearing keeps the rollers perfectly spaced, eliminating the need for a cage.

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

*   **Outer Diameter (D):** The overall diameter of the bearing ring.
*   **Thickness (T):**  The axial thickness of the bearing.
*   **Clearance (tol):** The gap between the gears to ensure smooth operation (important for 3D printing tolerances).
*   **Number of Planets (number_of_planets):**  The quantity of gear rollers.
*   **Number of Teeth on Planets (number_of_teeth_on_planets):** The number of teeth on each gear roller.
*   **Approximate Number of Teeth on Sun (approximate_number_of_teeth_on_sun):** This value helps calculate a better value for teeth on sun that fits.
*   **Pressure Angle (P):**  The pressure angle for the gear teeth.
*   **Number of Teeth to Twist Across (nTwist):** Controls the helix angle of the herringbone teeth.
*   **Width of Hexagonal Hole (w):** Size of the optional central hole to drive the sun gear (for testing purposes, not required).
*  **Maximum Depth Ratio of Teeth (DR):** The depth ratio controls the teeth depth.

Here's what I'd like the design to achieve and what I need in the code:

1.  **Automatic Calculation of Sun Gear Teeth:** Instead of just an approximate number of teeth on the sun gear, the code needs to calculate an exact number of teeth that ensures the planets mesh properly. The current code does this, but it may be nice to make it more robust. Specifically, it needs to:
    *   Calculate *k1* (initially rounded), then calculate *k*.
    *   Calculate *ns* (actual number of teeth on sun gear) from *k*.
    *   Calculate *nr* (number of teeth on the outer ring) from *ns* and number of teeth on the planets (*np*).
2.  **Gear Geometry Calculation:** All gear parameters, such as pitch diameter, circular pitch, and helix angle, need to be calculated from the input parameters above. The current code does this well, so please maintain it.
3.  **Herringbone Gears:** The gear rollers (planets) and the inner and outer gears should use a herringbone gear design. This is critical for the functionality.
4.  **Central Hexagonal Hole (Optional):**  The inner sun gear should have a central hexagonal hole, but this is optional and should have a parameter controlling whether to generate it.
5. **Monogram:** I would like to keep a simple monogram on the side of the outer ring, but I would like to remove it as an option.
6.  **Correcting Involute math:** the current version only creates an arc for the gears, it should create a full involute.

**I'd like the design to be:**
*   Completely parametric, such that the user can change any of the above values in the OpenSCAD customizer and get a fully functional design.
*   Well-structured, following best practices for OpenSCAD.

**Libraries:**

I don't believe any external libraries are required.

Could you generate the OpenSCAD code for this bearing design?

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

// outer diameter of ring
D=51.7;
// thickness
T=15;
// clearance
tol=0.15;
number_of_planets=5;
number_of_teeth_on_planets=7;
approximate_number_of_teeth_on_sun=9;
// pressure angle
P=45;//[30:60]
// number of teeth to twist across
nTwist=1;
// width of hexagonal hole
w=6.7;

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

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

phi=$t*360/m;

translate([0,0,T/2]){
	difference(){
		cylinder(r=D/2,h=T,center=true,$fn=100);
		herringbone(nr,pitch,P,DR,-tol,helix_angle,T+0.2);
		difference(){
			translate([0,-D/2,0])rotate([90,0,0])monogram(h=10);
			cylinder(r=D/2-0.25,h=T+2,center=true,$fn=100);
		}
	}
	rotate([0,0,(np+1)*180/ns+phi*(ns+np)*2/ns])
	difference(){
		mirror([0,1,0])
			herringbone(ns,pitch,P,DR,tol,helix_angle,T);
		cylinder(r=w/sqrt(3),h=T+1,center=true,$fn=6);
	}
	for(i=[1:m])rotate([0,0,i*360/m+phi])translate([pitchD/2*(ns+np)/nr,0,0])
		rotate([0,0,i*ns/m*360/np-phi*(ns+np)/np-phi])
			herringbone(np,pitch,P,DR,tol,helix_angle,T);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

```


[User]
Okay, I'd like to design a chainmail bracelet, but I need some help with the OpenSCAD code. I'm looking for something that's fully parametric, where I can easily control the length and width, as well as the ring thickness and spacing.

Here's what I have in mind:

*   **Bracelet Structure:** The bracelet should be made of interconnected, slightly angled, rectangular links, like a simplified chainmail. Each link should pass through the adjacent links in a repeating pattern, such as this: "/ / / \\ \\ \\"

*   **Parameters:** I need to be able to control the following parameters:
    *   `Length`: The total length of the bracelet in millimeters.
    *   `Width`: The number of links across the width of the bracelet.
    *   `Thickness`: The thickness of the material used for the rings.
    *   `Separation`: The sideways separation between the rings
    *   `Rotation`: The number of degrees to rotate the bracelet on the build platform
    *    `build_plate_selector` and the manual `build_plate_manual_x` and `build_plate_manual_y` for different printer dimensions.

*   **Clasp:** I'd like a simple clasp mechanism at each end of the bracelet. One side should have a protruding tab, and the other side a corresponding slot/opening to fit the tab. The clasp should be easily integrated with the bracelet and adjust to the width parameters.

*   **Build Platform Considerations:** The design should include the `build_plate` function from the `utils/build_plate.scad` file, which allows me to set printer dimensions.

*   **Customizability:** It should be straightforward to change the scale of the entire bracelet if needed.

I'm hoping that the OpenSCAD code would automatically calculate the number of links based on the overall length and the size of individual links. Also I would like the option to rotate the object to make the print longer on smaller print plates.

Could you generate the OpenSCAD code for this? I'd like it to be as clean and easy to understand as possible.

[Assistant]
ChainmailBracelet21.scad:
```
// Chainmail Bracelet
// Pasutx 23.2.2013

use <utils/build_plate.scad>

//: The total length of the bracelet in mm:
Length= 200;	// [30:300]
//: Number of links next to each other:
Width= 2;	// [2:6]
//: The number of degrees to rotate object to fit long objects on build platform
Rotation=45;	// [0,27,45]
//: strength of rings in mm
Thickness=2; // [2,2.5,3]
//: sideways separation between rings
Separation=0.66;  // [0.5,0.66,1,1.5,2]
//for display only, doesn't contribute to final object
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
//when Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 200; //[100:400]
//when Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 200; //[100:400]
build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

// Calulate number of links
Scale=1.1*1;
maxx = round(Length/Scale/6.2-1.5);
maxy = Width;
Xw=Thickness;  		// width of vertical elements in x direction
Yw=Thickness;   		// width of vertical elements in y direction

// Main program
// The translate operation puts the bracelet in the middle of the build platform
 Bracelet();

module Bracelet()
{
	rotate([0,0,Rotation]) translate([-0.5*Length+3.5,Width*-0.5*(5*Yw+4*Separation),0])  scale([Scale,Scale,Scale])
		union()
		{
			band();
			clasp("left");
			translate([(maxx-1)*7+6,maxy*4*(Yw+Separation)+2*Yw+Separation,0])
				rotate([0,0,180])
					clasp("right");
		}
}

module clasp(Type)
{
	difference()
	{
		// base
		translate([-9,Yw+Separation,0])
			cube(size=[11,maxy*4*(Separation+Yw)-Separation,3],center=false);
		// slit
		if (Type=="left")
		{
			translate([-1.5,Yw+Separation-0.1,-0.1])
				scale([1,1,1.1]) rotate([-90,180,0])
					linear_extrude(height = (maxy)*(4*Separation+4*Yw)-Separation-3, center = false, convexity = 3)
						polygon(points=[[-0.5,0],[3,0],[4,1.5],[3,3],[-0.5,3],[0.5,1.5]] );
			translate([-1.5,Yw+Separation-0.1,-0.1])
				scale([1,1,1.1]) rotate([-90,180,0])
					linear_extrude(height = (maxy)*(4*Separation+4*Yw)-Separation-3, center = false, convexity = 3)
						polygon(points=[[5.2,3],[6.2,1.5],[5.2,0],[8,0],[8,3],[0,3]] );
		}
		else // (Type=="right")
		{
			translate([-1.5,Yw+Separation-0.1,-0.1])
				scale([1.2,1,1.1]) rotate([-90,180,0])
					linear_extrude(height = (maxy)*(4*Separation+4*Yw)-Separation-3, center = false, convexity = 3)
						polygon(points=[[0,0],[3,0],[2,1.5],[3,3],[0,3],[-1,1.5]] );
			translate([-1.5,Yw+Separation-0.1,-0.1])
				scale([1.2,1,1.1]) rotate([-90,180,0])
					linear_extrude(height = (maxy)*(4*Separation+4*Yw)-Separation-3, center = false, convexity = 3)
						polygon(points=[[5,0],[4,1.5],[5,3],[20,3],[20,0]] );
		}
		// shorten for counter piece
		translate([-10,0,-0.1])
			cube(size=[10,Yw+Separation-0.1+3+Separation,5],center=false);
	}
}

module band()
{
	for (y = [0:maxy-1])
	{
		for (x = [0:maxx-1])
		{
			// right leaning links
			if (x!=maxx-1)
			{
				if (y!=0)
					translate([x*7,y*(4*Separation+4*Yw),0]) link();
				else
					translate([x*7,y*(4*Separation+4*Yw),0]) half_link();
			}
			// left leaning links
			if (x!=0)
			{
				if (y!=maxy-1)
					translate([x*7+6,y*(4*Separation+4*Yw)+(5*Separation+6*Yw),0]) rotate([0,0,180]) link();
				else
					translate([x*7+6,y*(4*Separation+4*Yw)+(5*Separation+6*Yw),0]) rotate([0,0,180]) half_link();
			}
		}
	}
}

module link()
{
	difference()
	{
		translate([2.1,0,0]) rotate([0,-40,0]) translate([-Xw,0,0])
			cube(size=[13,4*Yw+3*Separation,Xw],center=false);
		// through holes
		translate([0,0,0.26]) rotate([0,-40,0]) translate([Yw,Yw,-2.5*Xw])
			cube(size=[8,2*Yw+3*Separation,10],center=false);
		translate([0,Yw,1.2])
			cube(size=[12,2*Yw+3*Separation,5],center=false);
		// remove bottom
		translate([-1,-1,-4]) cube(size=[10,20,4],center=false);
		translate([-9.75,-1,-1]) cube(size=[10,20,5],center=false);
		// remove top
		translate([-1,-1,7.6]) cube(size=[13,20,4],center=false);
		translate([9.2,-1,-1]) cube(size=[10,20,15],center=false);
	}
}

module half_link()
{
	translate([0,Yw+Separation,0]) difference()
	{
		translate([2.1,0,0]) rotate([0,-40,0]) translate([-Xw,0,0])
			cube(size=[13,3*Yw+2*Separation,Xw],center=false);
		// through holes
		translate([0,0,0.26]) rotate([0,-40,0]) translate([Yw,Yw,-2.5*Xw])
			cube(size=[8,1*Yw+2*Separation,10],center=false);
		translate([0,Yw,1.2])
			cube(size=[12,1*Yw+2*Separation,5],center=false);
		// remove bottom
		translate([-1,-1,-4]) cube(size=[10,20,4],center=false);
		translate([-9.75,-1,-1]) cube(size=[10,20,5],center=false);
		// remove top
		translate([-1,-1,7.6]) cube(size=[13,20,4],center=false);
		translate([9.2,-1,-1]) cube(size=[10,20,15],center=false);
	}
}

```


[User]
Okay, I'd like to design a custom jewelry box using OpenSCAD. I want a parametric design so I can easily change things like the size, shape, and filigree pattern.

Here's what I'm looking for:

**Basic Shape:**

*   The box should be able to have a variety of base shapes. I'd like options for:
    *   A heart shape.
    *   A 6-pointed star.
    *   A cross shape.
    *   N-sided regular polygons (from 3 sides to 20 sides).
    *   Circular (should approximate a circle up to 102 sides).
    *   N-sided "racetracks" (basically a rectangle with semi-circles on the ends) using the n-sided polygon for the semi-circles (from 103 sides to 199 sides)
    *   Ovaloids, a rounded rectangle shape (from 203 to 299) using the specified number of sides to round corners
	*   Rounded Rectangles with progressively rounded corners (from 300-350). Should range from square rectangle to nearly circular corners
*   I need to be able to control the overall size of the box using a single parameter. The script should scale the base shape so the largest dimension of the base shape is the specified dimension.
*   I need a parameter to set the height of the box (interior height).
*   I need a parameter to control the thickness of the walls, top, and bottom of the box.

**Filigree Top:**

*   The top of the box should have a filigree design.
*   I want a parameter to choose the filigree type, with the options:
    *   Solid (no filigree, just a solid top).
    *   Linear (a grid-like pattern).
    *   Radial (a pattern emanating from the center).
    *   Circular (a circular pattern).
*   I need a parameter to control the basic shape of the filigree pattern, for example a triangle, circle, rectangle, etc.
*   I need a parameter to adjust the line thickness of the filigree pattern.
*   I need a parameter to rotate the filigree pattern.
*   I need a parameter to control the radius of the pattern.
*   I need two "magic number" parameters to further customize the filigree pattern, their interaction creates the unique filigree shapes.
*   I also need parameters to shift the filigree pattern vertically and horizontally to better center the design

**Insert:**

*   I'd like to be able to generate an optional insert that fits snugly behind the filigree on the top.
*   The insert should be the same shape as the interior of the box.
*   The insert should have a small height so it can be printed separately and easily installed.

**Printing Considerations:**

*   I need a parameter for the printer extrusion width, primarily to make the top fit correctly.
*   I need a parameter for the printer layer height, primarily to ensure the top can be pressed all the way down on the box.

**Build Plate:**

*   I would like a way to show a build plate for visualization.
*   I would like to be able to select a build plate with options for a Replicator 2, Replicator, Thingomatic, or Manual, for setting build plate size manually.
*   If I select Manual I need to be able to set the width and depth manually.

**General:**

*   The design should be fully parametric, so all dimensions can be adjusted using variables.
*   I'd like to be able to select which part of the design is generated (the entire assembly, just the body, just the top, or just the insert) for printing purposes.
*   I need the `build_plate.scad` library provided by Makerbot included for the build plate.

I think that covers everything. Please generate the OpenSCAD code for this.

[Assistant]
CustJewelryBox.scad:
```
/*ToDo
1. Customizer Variables
2. Build Flow
*/

//Author: Bennett Marks
//Date: 23-FEB-2013
//Copyright: This autoscad script is made available under Creative Commons
//           You are free to use this work in any manner you desire provided
//           you acknowledge the author and origination

/************ This is a customizable jewelry/gift box with filigree top
 The shape is customizable, as is the filigree design in the top
Optionally you can print out an insert plate that fits behind the filigree. It should be printed
in a different color. This will seal the box but allow the pattern to be prominent.

This script may be edited directly, but is written to run with the Makerbot "Customizer". When run with
 "Customizer", you may change appropriate parameters without the need to edit the script. This makes it
 easy to play with different box types, patterns and inserts
**************/

//*******************************Customizer parameters

//Customizer Control - You may display the whole assembly or generate each part individually. Note: The top is inverted for printing.
part = "show_all"; //[show_all:Display assembly,body:Box Body STL,top: Box Top STL,insert:Top Insert STL]

//Choose the shape of your container.0=heart, 1=6-sided star, 2=cross, 3-20=n sided, 30-102=circular 103-199=n-sided racetracks 203-299=ovaloids 300-350=rounded rectangles
box_shape=0; //[0:350]

//Set the height of the container in mm
box_height=30; //[10:200]

//Scale the approximate size of largest dimension of the container in mm
box_size=100; //[10:200]

//Set the thickness of bottom, sides and top of box in mm
box_wall_thickness=2; //[1:6]

//Pick a type of pattern for your Filigree top (solid means no filigree)
filigree_type=2; //[0:Solid,1:Linear,2:Radial,3:Circular]

//Pick the pattern shape. Each choice generates a different pattern.
filigree_pattern_shape=1; //[0:60]

//Choose the thickness of the pattern lines - 2-5 tend to look nice
filigree_pattern_thickness=3; //[1:30]

//Rotate the pattern for unique shapes and repetition
filigree_pattern_rotation=10; //[0:359]

//Overlap the pattern centers and size. 1.5 to 20.0 generate interesting results.
filigree_pattern_radius=2.3; //

//Put in a Pattern Magic number #1 a float between 0.01-100 (The magic #'s interact.)
filigree_magic_one=4.0;	//

//Put in another Pattern Magic number #2 a float between 0.01-100
filigree_magic_two=0.5;	//

//pattern vertical centering - allows you to move the pattern around vertically
filigree_vertical_centering=0; //[-75:75]

//pattern horizontal centering - allows you to move the pattern around horizontally
filigree_horizontal_centering=0; //[-75:75]

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

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

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

//width of extrusion for print - you shouldn't need to tweak this unless the box top is too loose or too tight
print_extrusion_width=.39; //

//Layer height - typical layer height - you shouldn't need to tweak this unless the top doesn't press all the way down
print_extrusion_height=.25; //

//*******************************Script Parameters
//  For the Print
Pextrwid=print_extrusion_width;	//standard for .35 nozzle (you only need to change this if the box top is too loose or too tight)
Pextrhgt=print_extrusion_height;	//typical extrusion layer height ( shouldn't need to change this unless top doesn't press all the way down)

//  For the Box
Bshape=box_shape;     			//0=heart, 1=6-sided star, 2=cross, 3-20=n sided, 30-102=circular
								//103-199=n-sided racetracks 203-299=ovaloids 300-399=rounded rectangles
Bheight=box_height;				// interior box height (total height = Bheight+2*Wall height [top & bottom]) [1-150]
Bradius=box_size/2;				// radius of bounding circle of box (overall size) [10-100]
Bwall=box_wall_thickness;		// wall thickness (multiple of extrusion width may result in best print)[.5-6]

//  For the Filigree
Ftype=filigree_type;		//0=solid 1=linear, 2=radial 3=circular
Frotation=filigree_pattern_rotation;	//pattern rotation [0.0 - 359.9]
Fradius=filigree_pattern_radius;	//pattern radius [0.1-100]
Fouterrad=filigree_magic_one;	//pattern outer radius   overlap = outer/inner [.01-100]
Finnerrad=filigree_magic_two;	//pattern inner radius   [.01-100]
Fshape=filigree_pattern_shape;		//pattern shape (0,1 are interesting special cases) [0-100]
Fthickness=filigree_pattern_thickness;	//pattern line thickness[.5-25]

FcenterUpDown=filigree_vertical_centering;		//-75 to +75 allows moving the pattern around up and down for centering
FcenterLeftRight=filigree_horizontal_centering;		//-75 to +75 allows moving the pattern around left and right for centering

use <utils/build_plate.scad> //This is the Buildplate library provided by Makerbot
//Shape Modules
module JStar(rad,hgt)
{
	union()
	{
		cylinder(r=rad,h=hgt,$fn=3);
		rotate([0,0,180])cylinder(r=rad,h=hgt,$fn=3);
	}
}
module Cross(offset,hgt)
{
	union()
	{
		cube([4*Bradius/3-offset,2*Bradius/3-offset,hgt]);
		translate([Bradius/3,-Bradius,0])
			cube([2*Bradius/3-offset,2*Bradius-offset,hgt]);
	}
}
module Heart(size,hgt)
{

	scale([size/29,size/29,1])union()
	{
		hull(){translate([3,0,.5*hgt])rotate([0,0,45])cube([18,18,hgt],center=true);
		translate([-3,-6,0])cylinder(r=9,h=hgt,$fn=40);}
		hull(){translate([3,0,.5*hgt])rotate([0,0,45])cube([18,18,hgt],center=true);
		translate([-3,6,0])cylinder(r=9,h=hgt,$fn=40);}
	}

}
module Racetrack(rad,hgt)
{
	hull()
	{
		rotate([0,0,180])cylinder(r=rad, h=hgt, $fn=Bshape-100);
		translate([Bradius,0,0])cylinder(r=rad, h=hgt, $fn=Bshape-100);
	}
}
module Ovaloid(offset,hgt)
{
	hull()
	{
		rotate([0,0,180])cylinder(r=Bradius/4+offset, h=hgt, $fn=Bshape-200);
		translate([3*Bradius/4,0,0])cylinder(r=Bradius/2+offset, h=hgt, $fn=Bshape-200);
		translate([3*Bradius/2,0,0])cylinder(r=Bradius/4+offset, h=hgt, $fn=Bshape-200);
	}
}
/* PIZZELLE
This routine generates infinite interesting circular and rotated patterns
	edge=sharpness of edges 3=very sharp 30=very round
	rad=radius of total pattern
	r1=radius of inner loop
	r2=radius of outer loop
	rep=repetition in degrees (e.g. 10=every 10 degrees, or 36 times around the circle)
	rot=rotation around the pattern 360=all the way around 180=halfway
*/
module pizzelle(edge,rad,r1,r2,rep,rot,th,hgt)
{
	union()
	{
	for(pat=[0:rep:rot])
	{
		if(edge<3)
		{
			rotate([0,0,pat])difference()
			{

				hull()
				{
					cylinder(h=hgt,r=th+r1, center=true, $fn=3);
					translate([rad,0,0])cylinder(h=hgt,r=th+r2, center=true, $fn=3);
				}
				hull()
				{
					cylinder(h=hgt+1,r=r1, center=true, $fn=3);
					translate([rad,0,0])cylinder(h=hgt+1,r=r2, center=true, $fn=3);
				}
			}
		} else
		{
			rotate([0,0,pat])difference()
			{

				hull()
				{
					cylinder(h=hgt,r=th+r1, center=true, $fn=edge);
					translate([rad,0,0])cylinder(h=hgt,r=th+r2, center=true, $fn=edge);
				}
				hull()
				{
					cylinder(h=hgt+1,r=r1, center=true, $fn=edge);
					translate([rad,0,0])cylinder(h=hgt+1,r=r2, center=true, $fn=edge);
				}
			}
		}
	}
	}
}
/* HONEYCOMB
This routine generates infinite interesting gridded patterns
		w=width of the gridding area
		l=length of the gridding area
		rot=rotation variable
		rad=radius variable
		rmod=radius modifier
		th=base line thickness
		sides=number of side to pattern 0,1,2 are interesting
Based on routine from Makerbot Customizable IPhone scad
*/
module honeycomb(w,l,hgt,rot,rad,rmod,th,sides) //th=thickness
{

	columns = l/(rad*3);
	rows = w/((rad*sqrt(3)/2)*2)+1;
	translate([-w/2,l/2,0])
	{
		rotate([0,0,-90])
		{
			for(i = [0:rows])
			{

				translate([0,rad*sqrt(3)/2*i*2,0])
				{
					//scale([1*(1+(i/10)),1,1])
					for(i = [0:columns])
					{
						translate([rad*i*3,0,0])
						{
							for(i = [0:1]){
								translate([rad*1.5*i,rad*sqrt(3)/2*i,0])
								{
									rotate([0,0,rot])
									//sides=0 pointy chevron, sides=1 use hull, sides=2 use rectangle
									if(sides == 0)
									{
										difference()
										{
											hull()
											{
												cube([2*(rad+th),rad+th,hgt], center=true, $fn=40);
												rotate([0,0,rot])translate([10*th,0,0])cube([2*(rad+th),rad+th,hgt], center=true, $fn=40);
											}
											hull()
											{
												cube([2*rad,rad,hgt+1], center=true, $fn=40);
												rotate([0,0,rot])translate([10*th,0,0])cube([2*rad,rad,hgt+1], center=true, $fn=40);
											}
										}
									}
									else if(sides == 1)
									{
										difference()
										{
											hull()
											{
												cylinder(h=hgt,r=rad/10+th+(rad*rmod/50), center=true, $fn=40);
												rotate([0,0,rot])translate([10*th,0,0])cylinder(h=hgt,r=th+rad/10+(rad*rmod/50), center=true, $fn=40);
											}
											hull()
											{
												cylinder(h=hgt+1,r=rad/10+(rad*rmod/50), center=true, $fn=40);
												rotate([0,0,rot])translate([10*th,0,0])cylinder(h=hgt+1,r=rad/10+(rad*rmod/50), center=true, $fn=40);
											}
										}
									}
									else if (sides == 2)
									{
										difference()
										{
											cube([2*(rad+th),rad+th,hgt],center=true);
											cube([2*rad,rad,hgt+1],center=true);
										}
									}
									else
									{
										difference()
										{
											if(sides < 5){
												cylinder(h = hgt, r = rad+th+(rad*rmod/50), center = true, $fn = sides);
											} else {
												cylinder(h = hgt, r = rad+(rad*rmod/50), center = true, $fn = sides);
											}
											cylinder(h = hgt+1, r = rad-th+(rad*rmod/50), center = true, $fn = sides);
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

module filigree()
{
	if(Ftype==1)
	{
		translate([FcenterLeftRight,FcenterUpDown,Bwall])honeycomb(200,200,2*Bwall,Frotation,3.5+Fradius,Fouterrad/Finnerrad,Fthickness,Fshape);
	}
	else if(Ftype==2)
	{
		translate([FcenterLeftRight,FcenterUpDown,Bwall])pizzelle(Fshape,Fradius*20,Fouterrad,Finnerrad,Frotation,360,Fthickness,2*Bwall);
	}
	else if(Ftype==3)
	{
		translate([FcenterLeftRight,FcenterUpDown,Bwall])pizzelle(Fshape,Fradius*20,Finnerrad,Fouterrad,Frotation,360,Fthickness,2*Bwall);
	}
	else
		translate([-100,-100,Bwall])cube([200,200,Bwall]); //solid top
}

module makeBox()
{
	color("Gold")
	//Make the box
	if (Bshape == 0) //Heart Box
	{
		difference()
		{
			Heart(Bradius*2,Bheight);
			translate([0,0,Bwall])Heart(2*(Bradius-Bwall), Bheight);
		}
	} else if (Bshape == 1) //Jewish Star Box
	{
		difference()
		{
			JStar(Bradius+2*Bwall,Bheight);
			translate([0,0,Bwall]) JStar(Bradius,Bheight+100);
		}
	} else if (Bshape == 2) //Cross Box
	{
		difference()
		{
			Cross(0,Bheight);
			translate([Bwall,Bwall,Bwall])Cross(2*Bwall,Bheight);
		}
	} else if (Bshape >102 && Bshape <200 ) //Racetrack Box
	{
		difference()
		{
			Racetrack(Bradius/2+Bwall,Bheight);
			translate([0,0,Bwall])Racetrack(Bradius/2,Bheight);
		}
	} else if (Bshape >202 && Bshape <300 ) //Ovaloid Box
	{
		difference()
		{
			Ovaloid(Bwall,Bheight);
			translate([0,0,Bwall])Ovaloid(0,Bheight);
		}
	} else if (Bshape == 300) //Squared Rectangle Box
	{
		difference()
		{
			cube([2*Bradius,Bradius,Bheight]);
			translate([Bwall,Bwall,Bwall])cube([2*(Bradius-Bwall),Bradius-2*Bwall,Bheight]);
		}
	}else if (Bshape >300 && Bshape <400 ) //Progressively rounded corner rectangle box
	{
		if(Bshape-300 < Bradius/2)
		{
			difference()
			{
				minkowski()
				{
					cube([2*Bradius+2*Bwall-2*(Bshape-300),Bradius+2*Bwall-2*(Bshape-300),.001]);
					cylinder(r=(Bshape-300), h=Bheight, $fn=40);
				}
				translate([Bwall,Bwall,Bwall])minkowski()
				{
					cube([2*Bradius-2*(Bshape-300),Bradius-2*(Bshape-300),.001]);
					cylinder(r=(Bshape-300), h=Bheight,$fn=40);
				}
			}
		}
		else
		{
			difference()
			{
				minkowski()
				{
					cube([Bradius+2*Bwall,2*Bwall,.001]);
					cylinder(r=Bradius/2, h=Bheight);
				}
				translate([Bwall,Bwall,Bwall])minkowski()
				{
					cube([Bradius,.001,.001]);
					cylinder(r=Bradius/2, h=Bheight);
				}
			}
		}
	}
	else	// N-sided box
	{
		difference()
		{
			cylinder(r=Bradius+Bwall, h=Bheight,$fn=Bshape);
			translate([0,0,Bwall])cylinder(r=Bradius, h=Bheight+100,$fn=Bshape);
		}
	}
}
module makeTop()
{
	color("IndianRed")
	if (Bshape == 0) //Heart Top
	{
		union()
		{
			translate([0,0,Bwall])
			union()
			{
				intersection()
				{
					translate([0,0,-Bwall])filigree();
					Heart(2*(Bradius-Bwall),Bheight+100);
				}
				difference()
				{
					Heart(2*Bradius,Bwall);
					translate([0,0,-1])Heart(2*(Bradius-2*Bwall),Bwall+2);
				}
			}
			difference()
			{
				Heart(2*(Bradius-Bwall-Pextrwid),Bwall);
				translate([0,0,-1])Heart(2*(Bradius-2*Bwall),Bwall+2);
			}
		}
	} else if (Bshape == 1)	//Jewish Star Top
	{
		union()
		{
			translate([0,0,Bwall])
			union()
			{
				intersection()
				{
					translate([0,0,-Bwall])filigree();
					JStar(Bradius,Bheight+100);
				}
				difference()
				{
					JStar(Bradius+2*Bwall,Bwall);
					translate([0,0,-1])JStar(Bradius-Bwall,Bwall+2);
				}
			}
			difference()
			{
				JStar(Bradius-2*Pextrwid,Bwall);
				translate([0,0,-1])JStar(Bradius-Bwall,Bwall+2);
			}
		}
	} else if (Bshape == 2) //Cross Top
	{
		union()
		{
			translate([0,0,Bwall])intersection()
			{
				translate([2*Bradius/3,Bradius/3,0])filigree();
				translate([2*Bwall,2*Bwall,0])Cross(4*Bwall,Bwall);
			}
			translate([0,0,Bwall])difference()
			{
				union()
				{
					Cross(0,Bwall);
					translate([Bwall,Bwall,-Bwall])Cross(2*(Bwall+Pextrwid),Bwall);
				}
				translate([2*Bwall,2*Bwall,-3])Cross(4*Bwall,Bwall+4);
			}
		}
	}else if (Bshape >102 && Bshape <200 ) //Racetrack shape top
	{
		union()
		{
			intersection()
			{
				translate([Bradius/2,0,0])filigree();
				Racetrack(Bradius/2-Bwall,Bwall);
			}
			difference()
			{
				union()
				{
					Racetrack(Bradius/2+Bwall,Bwall);
					translate([0,0,-Bwall])Racetrack(Bradius/2-Pextrwid,2*Bwall);
				}
				translate([0,0,2*-Bwall])Racetrack(Bradius/2-Bwall,5*Bwall);
			}
		}
	} else if (Bshape >202 && Bshape <300 ) //Ovaloid top
	{
		union()
		{
			intersection()
			{
				translate([3*Bradius/4,0,0])filigree();
				Ovaloid(Bwall,Bwall);
			}
			difference()
			{
				union()
				{
					Ovaloid(Bwall,Bwall);
					translate([0,0,-Bwall])Ovaloid(-Pextrwid,2*Bwall);
				}
				translate([0,0,2*-Bwall])Ovaloid(-Bwall,5*Bwall);
			}
		}
	} else if (Bshape == 300) //Rectangular top
	{
		union()
		{
			translate([0,0,Bwall])
			{
				union()
				{
					intersection()
					{
						translate([Bradius,Bradius/2,-Bwall])filigree();
						translate([2*Bwall,2*Bwall,0])cube([2*Bradius-4*Bwall,Bradius-4*Bwall,Bwall]);
					}
					difference()
					{
						cube([2*Bradius,Bradius,Bwall]);
						translate([2*Bwall,2*Bwall,-1])cube([2*Bradius-4*Bwall,Bradius-4*Bwall,Bwall+2]);
					}
				}
			}
			difference()
			{
				translate([Bwall,Bwall,0])cube([2*Bradius-2*(Bwall+Pextrwid),Bradius-2*(Bwall+Pextrwid),Bwall]);
				translate([2*Bwall,2*Bwall,-1])cube([2*Bradius-4*Bwall,Bradius-4*Bwall,Bwall+2]);
			}
		}
	}
	else if (Bshape >300 && Bshape <400 ) //Rounded Rectangle top
	{
		if(Bshape-300 < Bradius/2)
		{
			union()
			{
				intersection()
				{
					translate([Bradius-(Bshape-300),Bradius/2-(Bshape-300),0])filigree();
					translate([2*Bwall,2*Bwall,0])minkowski()
					{
						cube([2*Bradius-2*Bwall-2*(Bshape-300),Bradius-2*Bwall-2*(Bshape-300),.001]);
						cylinder(r=(Bshape-300), h=Bwall, $fn=40);
					}
				}
				difference()
				{
					union()
					{
						minkowski()
						{
							cube([2*Bradius+2*Bwall-2*(Bshape-300),Bradius+2*Bwall-2*(Bshape-300),.001]);
							cylinder(r=(Bshape-300), h=Bwall, $fn=40);
						}
						translate([Bwall,Bwall,-Bwall])minkowski()
						{
							cube([2*Bradius-2*(Bshape-300),Bradius-2*(Bshape-300),.001]);
							cylinder(r=(Bshape-300)-Pextrwid, h=2*Bwall, $fn=40);
						}
					}
					translate([2*Bwall,2*Bwall,-2*Bwall])minkowski()
					{
						cube([2*Bradius-2*Bwall-2*(Bshape-300),Bradius-2*Bwall-2*(Bshape-300),.001]);
						cylinder(r=(Bshape-300), h=5*Bwall, $fn=40);
					}
				}
			}
		}else
		{
			union()
			{
				intersection()
				{
					translate([Bradius/2,0,0])filigree();
					minkowski()
					{
						cube([Bradius+2*Bwall,2*Bwall,.001]);
						cylinder(r=Bradius/2, h=Bwall);
					}
				}
				difference()
				{
					union()
					{
						minkowski()
						{
							cube([Bradius+2*Bwall,2*Bwall,.001]);
							cylinder(r=Bradius/2, h=Bwall);
						}
						translate([Bwall,Bwall,-Bwall])minkowski()
						{
							cube([Bradius,.001,.001]);
							cylinder(r=Bradius/2-Pextrwid, h=2*Bwall);
						}
					}
					translate([Bwall,Bwall,-2*Bwall])minkowski()
					{
						cube([Bradius,.001,.001]);
						cylinder(r=Bradius/2-Bwall, h=5*Bwall);
					}
				}
			}
		}
	}else
	{
		union() // N-sided cylinder top
		{
			intersection()
			{
				filigree();
				translate([0,0,Bwall])cylinder(r=Bradius-Bwall+.5, h=Bwall,$fn=Bshape);
			}
		}
		difference()
		{
			union()
			{
				translate([0,0,Bwall])cylinder(r=Bradius+Bwall, h=Bwall,$fn=Bshape);
				difference()
				{
					cylinder(r=Bradius-Pextrwid, h=Bwall, $fn=Bshape);
					translate([0,0,-1])cylinder(r=Bradius-Bwall, h=Bwall+1,$fn=Bshape);
				}
			}
			cylinder(r=Bradius-Bwall, h=Bwall+10,$fn=Bshape);
		}

	}
}
module makeInsert()
{
	color("YellowGreen")
	if      (Bshape == 0)Heart(2*(Bradius-2*Bwall-Pextrwid),3*Pextrhgt);
	else if (Bshape == 1)JStar(Bradius-Bwall-2*Pextrwid,3*Pextrhgt);
	else if (Bshape == 2)translate([2*Bwall,2*Bwall,0])Cross(4*Bwall-2*Pextrwid,3*Pextrhgt);
	else if (Bshape >102 && Bshape <200 )Racetrack(Bradius/2-Bwall-Pextrwid,3*Pextrhgt);
	else if (Bshape >202 && Bshape <300 )Ovaloid(-Bwall-Pextrwid,3*Pextrhgt);
	else if (Bshape == 300)translate([2*Bwall,2*Bwall,0])cube([2*Bradius-4*Bwall-2*Pextrwid,Bradius-4*Bwall,3*Pextrhgt]);
	else if (Bshape >300 && Bshape <400 ) //rounded rectangle insert
	{
		if(Bshape-300 < Bradius/2)
		{
			translate([2*Bwall,2*Bwall,-2*Bwall])minkowski()
			{
				cube([2*Bradius-2*Bwall-2*(Bshape-300),Bradius-2*Bwall-2*(Bshape-300)-2*Pextrwid,.001]);
				cylinder(r=(Bshape-300), h=3*Pextrhgt, $fn=40);
			}
		}
		else
		{
					translate([Bwall,Bwall,-2*Bwall])minkowski()
					{
						cube([Bradius-2*Pextrwid,.001,.001]);
						cylinder(r=Bradius/2-Bwall, h=3*Pextrhgt);
					}
		}
	}else cylinder(r=Bradius-Bwall-2*Pextrwid, h=3*Pextrhgt,$fn=Bshape);
}
module showBuildPlate()
{
	build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);
}
module showAll()
{
	showBuildPlate();
	makeBox();
	translate([0,0,Bheight+16])makeTop();
	translate([0,0,Bheight+8])makeInsert();
}
module printTop()
{
	showBuildPlate();
	translate([0,0,2*Bwall])rotate([180,0,0])makeTop();
}
module printInsert()
{
	showBuildPlate();
	makeInsert();
}
module printBox()
{
	showBuildPlate();
	makeBox();
}

if(part == "show_all")
	showAll();
else if (part == "body")
	printBox();
else if (part == "top")
	printTop();
else if (part == "insert")
	printInsert();

```


[User]
Okay, I need a new parametric OpenSCAD design for an X-axis belt tensioner for my Printrbot. I really like the design in printrbot-x-tensioner.scad, but I need it to be more adjustable and flexible, and want to be able to tune the design to new belts and printer parameters without having to dig deep into the math.

Specifically, here's what I need:

**Core Functionality:**

*   **Belt Gripping:** Like the existing design, the tensioner needs a top piece with teeth or gaps that grip the belt. The depth, width, and spacing of these gaps should be parameters.
*   **Tensioning Mechanism:** It should use a screw to push the belt gripping top piece away from a fixed bottom piece, allowing for belt tension adjustment. The screw can be the same type as the original.
*   **Bottom Mount:** The bottom piece needs to attach to the existing printrbot X-axis mounting point. I want parameters for the mounting point dimensions, including width, height, and depth. It also needs a through hole or nut trap for securing the screw that does the actual tensioning.
*   **Material Thickness:** Everything needs to have adjustable wall thicknesses, including for the base/bottom, supporting features, and the belt grippers.

**Specific Parameters and Adjustability:**

1.  **Belt Parameters:**
    *   `belt_width`: The width of the belt.
    *   `belt_thickness`: The thickness of the belt.
    *   `belt_gap_depth`: Depth of the teeth/gaps in the belt gripper.
    *   `belt_gap_width`: Width of the teeth/gaps in the belt gripper.
    *   `belt_gap_spacing`: Spacing between the teeth/gaps in the belt gripper.
2.  **Mounting Point Parameters:**
    *   `mount_width`: The width of the existing printer mounting point.
    *   `mount_height`: The height of the existing printer mounting point.
    *   `mount_depth`: The depth of the existing printer mounting point.
    *   `mount_inner_square`: Size of the rectangular cut-out on the mounting point.
3.  **Wall Thickness Parameters:**
    *   `wall_thickness`: Standard wall thickness of the main structure.
    *  `thin_wall_thickness`: Thickness of the thinner walls or supports.
4.  **Screw/Nut Parameters:**
    *   `screw_hole_diameter`: Diameter of the screw hole.
    *   `nut_short_width`: Width of the nut (measured from flat sides).
    *   `nut_height`: Height of the nut.
5. **Tension Screw Placement:** An adjustable parameter that determines the placement of the tension screw along the bottom piece. This should probably be relative to the center of the tensioner, and should be a distance along the `BOTTOM_WIDTH` parameter, something like `tension_screw_offset`.

**Additional Notes/Requests:**

*   I want to use the BOSL2 library for any standard shapes, cylinders, and such. Please make use of BOSL2 modules wherever appropriate.
*   I'd like the top and bottom pieces to be separate modules so they're easy to adjust and potentially use in other projects.
*   Please include modules that will render the pieces for printing, preferably oriented in the most printable position with no special supports required.
*   I would like to have a module that renders the pieces as they're assembled so I can preview how it all fits together.
*   Add comments in the SCAD file that explain what each parameter does, and where they're used in the design, so it's easy for me to understand what I'm changing.
*   The file should be parametric enough to handle different sized belts, mounting points, and screw/nut sizes with ease.

I think that's everything. I'm looking forward to seeing what you come up with!

[Assistant]
printrbot-x-tensioner.scad:
```
// author: clayb.rpi@gmail.com
// date: 2013-02-20
// units: mm
//
// description: A X-axis tensioner for the original printed printrbot.
//
// extra hardware needed:
//   - 2 M4 machine screws + nuts (JIS)
//   - 1 small ziptie

/*******************
 * measured values *
 *******************/

// How wide the belt is
BELT_WIDTH = 7;

// The thickness of the belt.
BELT_THICKNESS = 3;

// The depth of the gaps in the belt.
BELT_GAP_DEPTH = 1.1;

// The width of the gaps in the belt.
BELT_GAP_WIDTH = 2;

// The distance between repeated features in the belt.
BELT_GAP_SPACING = 5;

// How wide the mounting point is.
MOUNT_WIDTH = 17.75;

// How deep the mounting point is (only used for visualization).
MOUNT_DEPTH = 20;

// How high the mounting point is.
MOUNT_HEIGHT = 6;

// The side-size of the inner mounting point sqare (only used for visualization).
MOUNT_INNER_SQUARE = 7;

// How thick the walls should be.
WALL_THICKNESS = 3;

// How thick any bracing walls should be.
THIN_WALL_THICKNESS = 2;

// The diameter of the screws to be used.
SCREW_HOLE_DIAMETER= 3;

// The width of the nuts to be used as measured from flat sides.
NUT_SHORT_WIDTH = 5.5;

// The height of the nuts to be used.
NUT_HEIGHT = 2.14;

/*********************
 * calculated values *
 *********************/
$fn=24 + 0;

TOP_PART_WIDTH = max(NUT_SHORT_WIDTH + THIN_WALL_THICKNESS, WALL_THICKNESS);
TOP_PART_HEIGHT = 2 * BELT_WIDTH;

BOTTOM_HEIGHT = MOUNT_HEIGHT + BELT_THICKNESS + 3 * WALL_THICKNESS + THIN_WALL_THICKNESS;
BOTTOM_WIDTH = max(
    /* mount is the limiting factor */ MOUNT_WIDTH + 2 * WALL_THICKNESS + NUT_HEIGHT,
    /* if we have thick belts */ TOP_PART_WIDTH + 2 * BELT_THICKNESS + 4 * WALL_THICKNESS);
BOTTOM_DEPTH = BELT_WIDTH + 2 * WALL_THICKNESS;

/*
 * A small piece to actually grip the belt for tensioning.
 */
module top_piece() {
  difference() {
    union() {
      cube([BELT_WIDTH, TOP_PART_WIDTH, TOP_PART_HEIGHT]);
      for (h = [0 : BELT_GAP_SPACING : 2 * BELT_WIDTH - BELT_GAP_WIDTH]) {
        translate([0, -BELT_GAP_DEPTH, h])
        cube([BELT_WIDTH, TOP_PART_WIDTH + 2 * BELT_GAP_DEPTH, BELT_GAP_WIDTH]);
      }
    }

    // cut out slots for the tensioner nut and screw.
    translate([BELT_WIDTH / 2, TOP_PART_WIDTH / 2, -1])
    nut_hole(h = NUT_HEIGHT + 1);

    translate([BELT_WIDTH / 2, TOP_PART_WIDTH / 2, -1])
    cylinder(r = SCREW_HOLE_DIAMETER / 2, h = TOP_PART_HEIGHT + 2);
  }
}

/*
 * The bottom bracket piece of the tensioner.
 */
module bottom_piece() {
  difference() {
    union() {
      // bottom side.
      cube([BOTTOM_DEPTH, BOTTOM_WIDTH, WALL_THICKNESS]);

      // support beam.
      translate([0, 0, WALL_THICKNESS + MOUNT_HEIGHT])
      cube([BOTTOM_DEPTH, BOTTOM_WIDTH, THIN_WALL_THICKNESS]);

      // fill in the area around the mounting point.
      if (BOTTOM_WIDTH - 2 * WALL_THICKNESS - MOUNT_WIDTH -0.001 > NUT_HEIGHT) {
        translate([0, WALL_THICKNESS + NUT_HEIGHT + MOUNT_WIDTH, WALL_THICKNESS])
        cube([BOTTOM_DEPTH, BOTTOM_WIDTH - 2 * WALL_THICKNESS - MOUNT_WIDTH - NUT_HEIGHT, MOUNT_HEIGHT]);
      }

      // sides.
      cube([BOTTOM_DEPTH, WALL_THICKNESS, BOTTOM_HEIGHT]);

      translate([0, BOTTOM_WIDTH - WALL_THICKNESS])
      cube([BOTTOM_DEPTH, WALL_THICKNESS, BOTTOM_HEIGHT]);

      // Rounded areas for the bet interface.
      translate([0, WALL_THICKNESS, BOTTOM_HEIGHT - WALL_THICKNESS])
      rotate([0, 90, 0])
      cylinder(r=WALL_THICKNESS, h=BOTTOM_DEPTH);

      translate([0, BOTTOM_WIDTH - WALL_THICKNESS, BOTTOM_HEIGHT - WALL_THICKNESS])
      rotate([0, 90, 0])
      cylinder(r=WALL_THICKNESS, h=BOTTOM_DEPTH);
    }

    // cut out a hole for the belt.
    translate([WALL_THICKNESS, -1, WALL_THICKNESS + MOUNT_HEIGHT + THIN_WALL_THICKNESS])
    cube([BELT_WIDTH, BOTTOM_WIDTH + 2, BELT_THICKNESS]);

    // cut out a hole for the bracing screw.
    translate([BOTTOM_DEPTH / 2, -1, WALL_THICKNESS + MOUNT_HEIGHT / 2])
    rotate([-90, 0, 0])
    cylinder(r=SCREW_HOLE_DIAMETER / 2, h=WALL_THICKNESS + 2);

    translate([BOTTOM_DEPTH / 2, WALL_THICKNESS - THIN_WALL_THICKNESS, WALL_THICKNESS + MOUNT_HEIGHT / 2])
    rotate([-90, 0, 0])
    nut_hole(h=2 * NUT_HEIGHT);

    // cut out a hole for the tensioning screw.
    translate([BOTTOM_DEPTH / 2, BOTTOM_WIDTH / 2, WALL_THICKNESS + MOUNT_HEIGHT - 1])
    cylinder(r=SCREW_HOLE_DIAMETER / 2, h=THIN_WALL_THICKNESS + 2);
  }
}

/*
 * A representation of the mounting point on the printrbot for this part.
 */
module mounting_point() {
  difference() {
  	cube([MOUNT_DEPTH, MOUNT_WIDTH, MOUNT_HEIGHT]);

    translate([MOUNT_DEPTH - MOUNT_INNER_SQUARE - 6, (MOUNT_WIDTH - MOUNT_INNER_SQUARE) / 2, -1])
    cube([MOUNT_INNER_SQUARE, MOUNT_INNER_SQUARE, MOUNT_HEIGHT +  2]);
  }
}

/*
 * A cutaway for a nut as measured from the flat sides.
 */
module nut_hole(short_width=NUT_SHORT_WIDTH, h=NUT_HEIGHT) {
  translate([0, 0, h / 2])
  union() {
    for(r = [0, 120, 240]) {
      rotate([0, 0, r])
      cube([(short_width / 2) / cos(30), short_width, h], center=true);
    }
  }
}

/*
 * A module to render all of the pieces in their assembled form.
 */
module pieces_arranged_for_display() {
  bottom_piece();

  translate([WALL_THICKNESS, (BOTTOM_WIDTH - TOP_PART_WIDTH) / 2,
             WALL_THICKNESS + MOUNT_HEIGHT + THIN_WALL_THICKNESS + BELT_THICKNESS])
  top_piece();

  %
  // Visualize the mounting point on the printrbot.
  translate([BOTTOM_DEPTH - MOUNT_DEPTH, WALL_THICKNESS + NUT_HEIGHT, WALL_THICKNESS])
  mounting_point();
}

/*
 * A module to render all of the pieces in their printable form.
 */
module pieces_arranged_for_printing() {
  rotate([0, -90, 0])
  bottom_piece();

  translate([-BOTTOM_HEIGHT, (BOTTOM_WIDTH - TOP_PART_WIDTH) / 2, 0])
  rotate([0, -90, 0])
  top_piece();
}

// pieces_arranged_for_display();
pieces_arranged_for_printing();

```