[User]
Hey, I'm trying to design some gears and I found this OpenSCAD code online (attached). It's pretty good but a bit hard to tweak. I'd like to be able to create gears with a lot of different options, like the attached code has, but in a way that's a little easier to understand and use.

Specifically, I need to be able to specify the following:

*   **Modulus:** This is the core parameter, it defines the gear's size in relation to its teeth.
*   **Number of Teeth:** The total number of teeth on the gear.
*   **Gear Thickness:** How thick the gear is.
*   **Center Hole Diameter:** The diameter of the hole in the center of the gear.
*   **Gear Twist:** How much the teeth twist from the bottom to the top. This is for making helical gears (or screws).
*   **Hidden Teeth:** The number of teeth to remove, creating a partial gear.
*   **Tooth Pressure Angle:** The angle that determines how straight or bulged the tooth sides are. Common values are 14.5, 20, and 28 degrees.
*   **Rim Size:** The thickness of the rim that supports the teeth.
*    **Hub Diameter:** The diameter of a raised hub in the center of the gear.
*   **Hub Height:**  The height of the hub extending from the gear face.
*   **Number of Spokes:** The number of spokes connecting the hub to the rim.
*   **Keyway Dimensions:** A keyway height and width that can be set to zero if there's no keyway.
*   **Center Bore Shape:** I'd like to choose whether the center hole is round, a hexagon, or a square.

It would be fantastic if the code was well-commented and easy to modify later. I'm not using any special OpenSCAD libraries, so it would be good if all the code was in one file, or at least very simple includes.

[Assistant]
ModulusGear.scad:
```
///////////////////////////////////////////////////////////////////////////////////////////
// Public Domain Modulus Gear OpenSCAD code
// version 1.0
// This code defines a gear based on its modulus rather than pitch in mm,
// Modulus is the ration of tooth count:diameter, effectively the pitch of the gear is
// modulus * pi
// ie, a gear of mod=1 has a tooth pitch of 3.1415926 mm.
//
// The advantage of using modulus rather than pitch to define a gear is that it is very easy to
// prototype - for example, a gear of mod = 1 will have a diamter that matches the tooth count.
// with mod = 2, radius = tooth count.
//
// This code is based on the poblic domain Parametric Involute Spur Gear code by Leemon Baird
//found at http://www.thingiverse.com/thing:5505
//////////////////////////////////////////////////////////////////////////////////////////////
//
// Since the original file was public domain, this file is public domain as well.
// Use it for any purpose, including commercial applications.  Attribution would be nice,
// but is not required.  There is no warranty of any kind, including its correctness,
// usefulness, or safety.
//
//////////////////////////////////////////////////////////////////////////////////////////////
// An involute spur gear based on Modulus, with reasonable defaults for all the parameters.
// Normally, you should just choose the first 4 parameters, and let the rest be default values.
// Meshing gears must match mod, pressure_angle, and twist. Geras should be separated by the
// sum of their radii, where gear radius = mod * tooth count.
//////////////////////////////////////////////////////////////////////////////////////////////

Modulus				= 1		;//[0.1:0.1:3]
Tooth_Count 		= 20	;//[6:1:500]
Gear_Thickness 		= 6 	;//[0.1:0.1:30]
Center_Hole_Diameter	= 4		;//[0.1:0.1:20]
Gear_Twist			= 0  	;//[0:0.5:360]
Hidden_Teeth		= 0    	;//[0:1:20]
Tooth_Pressure_Angle= 20	;//[14.5, 20, 28,]
Rim_Size 			= 5		;//[1:0.1:10]
Hub_Diameter			= 12	;//[6:0.5:40]
Hub_Height 		= 1 	;//[0:0.1:30]
Number_of_Spokes 	= 4		;//[1:1:10]
Keyway_Height		= 0		;//[0:0.1:10]
Keyway_Width		= 0		;//[0:0.1:8]
Center_Bore_Shape	= 100	;//[100:Round, 6:Hexagon, 4:Square]

// Call the modgear module to create the gear using the parameters define above
// If using this as a library, you can delete this, or put a * before modgear to hide it.
modgear (
	mod    			= Modulus,    //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
	number_of_teeth = Tooth_Count,   //total number of teeth around the entire perimeter
	thickness       = Gear_Thickness,    //thickness of gear in mm
	hole_diameter   = Center_Hole_Diameter,    //diameter of the hole in the center, in mm
	twist           = Gear_Twist,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	teeth_to_hide   = Hidden_Teeth,    //number of teeth to delete to make this only a fraction of a circle
	pressure_angle  = Tooth_Pressure_Angle,   //Controls how straight or bulged the tooth sides are. In degrees.
	clearance       = 0.0,  //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
	backlash        = 0.0,  //gap between two meshing teeth, in the direction along the circumference of the pitch circle
	rim				= Rim_Size , 	//thickness of rim supporting the gear teeth
	hub_diameter	= Hub_Diameter,	//diameter of hub
	Hub_Height	    = Hub_Height , 	//extension of hub beyond gear thickness
	spoke_num 		= Number_of_Spokes ,	//number of spokes
	keyway			= [Keyway_Height,Keyway_Width],
    hole_polygon    = Center_Bore_Shape

);

//these 4 functions are used by modgear
function polar(r,theta)   = r*[sin(theta), cos(theta)];                            //convert polar to cartesian coordinates
function iang(r1,r2)      = sqrt((r2/r1)*(r2/r1) - 1)/PI*180 - acos(r1/r2); //unwind a string this many degrees to go from radius r1 to radius r2
function q7(f,r,b,r2,t,s) = q6(b,s,t,(1-f)*max(b,r)+f*r2);                         //radius a fraction f up the curved side of the tooth
function q6(b,s,t,d)      = polar(d,s*(iang(b,d)+t));                              //point at radius d on the involute curve

//*****************************************************

module modgear (
	mod    			= 1,    //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
	number_of_teeth = 15,   //total number of teeth around the entire perimeter
	thickness       = 6,    //thickness of gear in mm
	hole_diameter   = 3,    //diameter of the hole in the center, in mm
	twist           = 0,    //teeth rotate this many degrees from bottom of gear to top.  360 makes the gear a screw with each thread going around once
	teeth_to_hide   = 0,    //number of teeth to delete to make this only a fraction of a circle
	pressure_angle  = 20,   //Controls how straight or bulged the tooth sides are. In degrees.
	clearance       = 0.0,  //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
	backlash        = 0.0,  //gap between two meshing teeth, in the direction along the circumference of the pitch circle
	rim				= 5 , 	//thickness of rim supporting the gear teeth
	hub_diameter	= 6,	//diameter of hub
	Hub_Height	= 0 , 	//extension of hub beyond gear thickness
	spoke_num 		= 4 ,	//number of spokes
	keyway			= [0,0],
    hole_polygon    = 100

) {
	pi = PI;
	mm_per_tooth = mod * pi;
	number_of_teeth = floor (abs(number_of_teeth));
	keycut = [ keyway[0] +hole_diameter/2 , keyway[1], 2*(thickness + Hub_Height)];
	if (teeth_to_hide > number_of_teeth) {teeth_to_hide = 0;}
	p  = mod * number_of_teeth/ 2; //radius of pitch circle
	c  = p + mod - clearance ;     //radius of outer circle
	b  = p*cos(pressure_angle);    //radius of base circle
	r  = p-(c-p)-clearance ;       //radius of root circle
	t  = mm_per_tooth/2-backlash/2;                //tooth thickness at pitch circle
	k  = -iang(b, p) - t/2/p/pi*180; {             //angle to where involute meets base circle on each side of tooth
		difference(){

            union() {
				for (i = [0:number_of_teeth - teeth_to_hide-1] )
					rotate([0,0,i*360/number_of_teeth])
						linear_extrude(height = thickness,  convexity = 10, twist = twist)
							polygon(
								points=[
									//[0, -hole_diameter/10],
									polar(r, -181/number_of_teeth),
									polar(r, r<b ? k : -180/number_of_teeth),
									q7(0/5,r,b,c,k, 1),q7(1/5,r,b,c,k, 1),q7(2/5,r,b,c,k, 1),q7(3/5,r,b,c,k, 1),q7(4/5,r,b,c,k, 1),q7(5/5,r,b,c,k, 1),
									q7(5/5,r,b,c,k,-1),q7(4/5,r,b,c,k,-1),q7(3/5,r,b,c,k,-1),q7(2/5,r,b,c,k,-1),q7(1/5,r,b,c,k,-1),q7(0/5,r,b,c,k,-1),
									polar(r, r<b ? -k : 180/number_of_teeth),
									polar(r, 181/number_of_teeth)
								]//,
								//paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]]
							);
				difference(){
					cylinder(h=thickness, r=r,  $fn=60);
					translate ([0,0,-0.001]) cylinder(h=2*thickness+1, r=r-rim,  $fn=60);
				}
				cylinder (h=thickness + Hub_Height,r=hub_diameter/2,$fn=60);		// hub

				for (i = [0:spoke_num - 1] )		//position the spokes
					rotate([0,0,i*360/spoke_num])
						translate ([-rim/2,0,0])
							cube ([rim,r-1,thickness]);

			}
			translate ([0,0,-0.001])cylinder (h=2*(thickness + Hub_Height),r=hole_diameter/2,$fn=hole_polygon);
			translate ([((keyway[0]+hole_diameter/2)/2),0,(thickness + Hub_Height-1)]) cube (keycut, center = true);
		}

	}
};

```


[User]
Hi there! I'm looking for an OpenSCAD design that lets me create custom PCB mounting feet. I've got a few different needs, and I'd like the design to be parametric so I can easily adjust things like the standoff height, leg length, etc.

Basically, I need three types of mounting options:

1.  **Vertical Standoffs:** These are just cylindrical posts that raise the PCB up from the surface. I need to be able to adjust the height of the standoff, the diameter of the post, and the diameter of the screw hole inside. I'd like the screw hole to have an option for either a flat bottom or a tapered countersink. There should also be a snap fit feature at the top for holding the PCB. I need to be able to adjust the PCB hole size and the retainer flange thickness and height. I also need to control the split/gap size of the snap fit feature.

2.  **L-Shaped Legs:**  These are like the vertical standoffs, but they have a horizontal leg extending out to the side. I'd need to be able to adjust the height of the vertical portion, the length of the horizontal leg, the diameter of the base at the end of the leg, and the diameter of the screw hole at the end of the leg. This screw hole should also have the same option for a flat bottom or a tapered countersink. The snap fit mentioned above should be used on the vertical portion, as well.

3. **Combined Stand/Leg:**  A combination of both of the above features. A leg with an angled base, and vertical stand/snap feature.

I’d like to be able to switch between these three types of mounts using a variable.  Also, I'd need to be able to set the diameter of the screw holes and the height, width, and length of each part.

I'd like to be able to set $fn for print resolution.

It seems like this will take a lot of code, but I'm also planning to use this for some different board mountings, so being able to change the dimensions is essential. Can you generate a modular and flexible OpenSCAD file (or files) that can do this for me? I don't need a library, just the base design file. I think that's everything - let me know if you have any questions!

[Assistant]
pcb_screw_feet.scad:
```
/** PCB Mounts **/

/* [Mount Ty[pe] */
//Mount type; Stand = vertical stand, Leg = L-shaped leg, Combo...
type="stand";//[stand,leg,combo]

/* [Vertical standoff dimensions] */
// Height of PCB from surface
pcb_height=10;//.1
// Diameter of PCB support post
support_diameter=6;//.1
// Diameter of screw hole in vertical standoff
support_hole=2;
//Leg screw hole type
support_hole_type="taper";//[taper,flat]

/* [Leg dimensions] */
// Height of the leg
leg_height=4;
// Length of the leg
leg_len=16;
// Diameter at end of leg
leg_diam=12;
// Diameter of screw hole in leg end
leg_hole_diam=4;
//Leg screw hole type
leg_hole_type="taper";//[taper,flat]

/* [Stand: hole and retainer dimensions] */
// Diameter of the hole in the PCB
pcb_hole=3.8;//.1
// PCB board thickness
pcb_thickness=3.2;//.1
// Extra retainer thickness at board
flange=0.35;
// Retainer height
flange_height=6;
// Retainer split thickness
split=1;//.1

$fn=($preview ? 64 : 128);

if (type=="leg") {
    difference() {
        leg();
        // Minus: screw-hole for stand-off
        translate([0,0,-1]) {
            cylinder(d=support_hole, h=pcb_height+2);
            if (support_hole_type == "taper") {
                translate([0, 0, pcb_height])
                    cylinder(h=2, r1=support_hole/2, r2=support_hole*1.5);
            }
        }
    }
} else if (type=="stand") {
    union() {
        //leg();
        snap_fit();
        // stand base
        cylinder(d=support_diameter,h=pcb_height);// This is the base
    }
} else {
    union() {
        leg();
        snap_fit();
    }
}

module leg() {
    difference() {
        CubePoints = [
          [  0,  -support_diameter/2,  0 ], //0
          [ leg_len,  -leg_diam/2,  0 ], //1
          [ leg_len,  leg_diam/2,  0 ],  //2
          [  0,  support_diameter/2,  0 ],  //3
          [  0,  -support_diameter/2,  leg_height ], //4
          [ leg_len,  -leg_diam/2,  leg_height ], //5
          [ leg_len,  leg_diam/2,  leg_height ],  //6
          [  0,  support_diameter/2,  leg_height  ]  //7
        ];

        CubeFaces = [
          [0,1,2,3],  // bottom
          [4,5,1,0],  // front
          [7,6,5,4],  // top
          [5,6,2,1],  // right
          [6,7,3,2],  // back
          [7,4,0,3]   // left
        ];

        union() {
            // Vertical standoff
            cylinder(d=support_diameter, h=pcb_height);
            // Leg
            polyhedron(CubePoints, CubeFaces);
            translate([leg_len,0,0])
            cylinder(d=leg_diam, h=leg_height);
        }
        // Minus screw hole for leg
        translate([leg_len,0,-1]) {
            cylinder(d=leg_hole_diam, h=leg_height+2);
            if (leg_hole_type == "taper") {
                translate([0, 0, leg_height])
                cylinder(h=2, r1=leg_hole_diam/2, r2=leg_hole_diam*1.5);
            }
        }
    }
}

module snap_fit() {
    difference() {
        union() {
            // Snap fit bit above the PCB
            translate([0, 0, pcb_height + pcb_thickness])
            intersection() {
                cylinder(d1=pcb_hole+flange*2, d2=2, h=flange_height);
                cube([pcb_hole, pcb_hole+flange*2, flange_height*2], center=true);
            }
            // post which sit in PCB hole
            cylinder(d=pcb_hole,h=pcb_height + pcb_thickness);  // Shaft up to flange
        }
        // Cut out the split bit
        translate([0,0,pcb_height+(pcb_thickness + flange_height)])
            cube([support_diameter+2, split, flange_height*2+pcb_thickness*1.7], center=true);
    }
}

```


[User]
Hey, I'm looking to get a handlebar riser designed for my Veloretti electric bike. I've seen a few online, but I'd like something I can customize myself.

Basically, it needs to be a hollow cylinder. The outer diameter should be 46mm, and the inner one should be 29mm. I'd like to be able to adjust the height, from 1mm up to 30mm. I also want to control the level of detail so it appears smooth. I'm thinking a value around 100 percent would be great for a smooth print, but I'd like to be able to adjust that too. The cylinder should be centered, with the inner cylinder being taller than the outer one by about 2mm. It should work as a single OpenSCAD file.

Could you generate that for me? Thanks!

[Assistant]
veloretti_handlebar_riser.scad:
```
/* [Settings] */

// How tall should the handlebar riser be in millimeters
Height = 20; // [1:30]

// Detail percentage
Detail = 100; // [6:100]

difference ()
{
    cylinder(Height, d = 46, $fn = Detail * 3.6, center=true);
    cylinder(Height + 2, d = 29, $fn = Detail * 3.6, center=true);
}
```


[User]
Hi, I'm looking to create a bandsaw insert for my Grizzly 14" bandsaw and I need an OpenSCAD file to generate the model. I've made a few of these before but I'd like to create a parametric model that I can adjust to make different inserts in the future.

The insert needs to be composed of two stacked cylinders. The top cylinder will be slightly larger in diameter and a little bit thicker than the bottom cylinder. I need to be able to control these diameters and thicknesses through the parameters. I need a small key on the outside of the top circle as well to keep the insert from spinning in the saw. I would also like a hole for easier removal from the bandsaw. Finally, I need to be able to create a semi-circular recess on the bottom of the bottom circle. I want the model to have a cutout for the blade with both a straight cut and an area for the blade to turn.

Here are the parameters that should be adjustable:
*   **Top Circle:**
    *   `top_thickness`: Thickness of the top circle.
    *   `top_diameter`: Diameter of the top circle.
*   **Bottom Circle:**
    *   `bottom_thickness`: Thickness of the bottom circle.
    *   `bottom_diameter`: Diameter of the bottom circle.
*   **Bottom Inset:**
    *   `bottom_inset_diameter`: Diameter of the semi-circular inset on the bottom circle (0 for none).
*   **Key:**
    *   `key`: Diameter of the bump for the key (0 for none).
*   **Removal Hole:**
    *   `hole`: Diameter of the removal hole (0 for none).
*   **Blade Cutout:**
    *   `blade_width_allowance`: Clearance width for the blade.
    *   `side_offset`: Y offset direction of the cutaway for the blade (0 for center).
    *   `front_back_offset`: X offset direction of the cutaway for the blade (0 for back of the blade at center).
*   **Blade Box:**
    *   `blade_box_width`: Width of the cutaway for the turning of the blade.
    *   `blade_box_length`: Length of the cutaway for the turning of the blade.
    *
Also, it would be great if you could include a JSON file with the settings I use for my Grizzly 14" bandsaw.

Could you create the OpenSCAD files for me, please? I think it would be useful if you included some comments in the file, as well, so that I know what each section does.

[Assistant]
bandsaw_insert.scad:
```
/* [Plate Layout] */
//Thickness of top circle
top_thickness=4.7; //[0.1:0.1:10]
//Diameter of top circle (entire plate)
top_diameter=70; //[0.1:0.1:100]
//Thickness of bottom circle
bottom_thickness=4;  //[0.1:0.1:10]
//Diameter of bottom circle
bottom_diameter=55;  //[0.1:0.1:100]
//Diameter of bottom semi-circle inset (zero for none)
bottom_inset_diameter=25;  //[0.1:0.1:100]

//Diameter of bump for key (zero for none)
key=3.5; //[0.1:0.1:10]

//Size of removal hole (zero for none)
hole=8; //[0.1:0.1:10]

/* [Blade Dimensions] */
//Clearance width for blade (with tooth splay!)
blade_width_allowance=1.5; //[0.1:0.1:10]
//Y Offset direction (R+ L-) of cutaway for blade (side-to-side, 0=Center)
side_offset=0; //[-30:0.1:30]
//X Offset direction of cutaway for blade (front-to-back, 0=Back of blade at Center)
front_back_offset=10; //[-5:0.1:50]
/* [Blade Box] */
//Width of cutaway for turning of blade
blade_box_width=4; //[0.1:0.1:30]
//Length of cutaway for turning of blade
blade_box_length=20; //[0.1:0.1:30]

/* [Hidden] */
$fn=300; //resolution of circles

difference(){
difference(){
difference(){
difference(){
union(){
    //Top
cylinder(top_thickness, d=top_diameter);
    //Bottom
translate([0,0,top_thickness]) cylinder(bottom_thickness, d=bottom_diameter);
    //Key
translate([(top_diameter/2),0,0]) cylinder(top_thickness, d=key);
}
//Blade slot
translate ([side_offset, (top_diameter/4)-(front_back_offset/2), (top_thickness+bottom_thickness)/2]) cube(size=[blade_width_allowance, top_diameter/2+front_back_offset, top_thickness+bottom_thickness+0.1], center=true);
}
//Blade Box hole
translate ([(-blade_box_width/2)+side_offset,-front_back_offset,-0.05]) cube(size=[blade_box_width,blade_box_length,top_thickness+bottom_thickness+0.1], center=false);
}
//Removal Hole
translate([(top_diameter/4),0,-0.05]) cylinder(top_thickness+bottom_thickness+0.1, d=hole);
}
//Bottom Inset
translate([-((top_diameter-bottom_diameter)+bottom_inset_diameter/2),0,top_thickness+0.001]) cylinder(bottom_thickness+0.1, d=bottom_inset_diameter);
}
```


[User]
Hey there, I'm looking to get some OpenSCAD files generated for some power supply end caps, and I need them to be parametric so I can adjust them to fit different sizes later.

Here's the deal: I have a 12v power supply that I want to put these end caps on, and I need two different types: a front cap and a rear cap.

**Dimensions and Basic Structure:**

The power supply is a rectangular box, and I need the end caps to wrap around the sides a little bit. So, I'll need parameters for things like:

*   `power_width`: The width of the power supply.
*   `power_height`: The height of the power supply.
*   `cap_wall`: The thickness of the cap walls.
*   `cap_wrap_depth`:  How much the front cap should wrap around the sides of the supply.
*   `front_space`: How much the front cap should extend beyond the front of the power supply.
*   `rear_cap_depth`: The depth of the rear cap.
*   `powercon_start`: The distance from the left edge of the supply to the start of the power connector area.
*   `powercon_width`: The width of the power connector area.
*   `powercon_height`: The height of the power connector area.
*   `wire_height`: The vertical distance from the table/base to the bottom of the wire connectors.

The front cap will obviously have a void that fits snugly around the power supply. The rear cap can be more basic, or even minimalistic, it's just there to support the back of the supply. So, I'd like an option to enable a minimal rear cap, using less material.  `rear_cap_minimal` .

**Front Cap Options:**

I also need the front cap to have a few options, which depend on the type of power supply. I'd like to be able to specify the following:

*   `type`:  Whether it's a "front" or "rear" cap being generated.
*   `style`: Whether the front cap is a `single` piece, or `split` in half, for easier installation.
*   `connect_type`: If it's a `split` case, whether to use `slide-on` or `snap-fit` connectors.
*  `input`: If the style is `split`, which side the high voltage input is on (`left` or `right`).

For the split cap version I need it to be split horizontally. If using `snap-fit` I need `snap_width` and `snap_height`, and I need to generate the connecting parts to match up. The height at which the model is split is calculated based on `cap_wall`, `wire_height`, and `powercon_height`.

I need the option for a power switch hole on the front cap:

*   `power_switch`: Boolean to enable or disable the power switch hole
*   `power_switch_width`: Width of the switch hole
*    `power_switch_depth`: Depth of the switch hole

I would also like options for indicator lights:

*   `front_indicator`: Boolean for an indicator hole on the front.
*   `led_height`: Distance from the base to the indicator light.
*   `top_indicator`: Boolean for an indicator light on top.
*   `led_dist`: Distance from the left edge to the center of the LED.
*   `led_depth`: The depth from the front edge to the center of the LED.

**Additional Details:**

I'd like a few extra features for safety and convenience:

*   `defense_wall`: Boolean for a barrier between high and low-voltage.
*   `defense_wall_start`: Distance from the left edge to the wall.
*   `defense_wall_width`: The width of this wall.
*   `support_wall`: Boolean to create support walls around the low-voltage wire exit.
*   `support_lip`: Boolean to create a rounded lip on the front for additional wire support, `support_lip_diam` defines the lip diameter.

Finally I also need the option for grill tabs, which should fit into ventilation holes on the supply itself for extra hold

*   `grill_tabs`: Boolean to enable or disable the tabs.
*   `grill_tab_count`: The number of grill tabs
*   `grill_tab_first`: Distance from the front edge to the first tab.
*   `grill_tab_spacer`: Space between tabs.
*   `grill_tab_height`: The height of the tab protrusion
*   `grill_tab_width`: The width of the tab
*    `grill_tab_depth`: The depth the tab sticks out
*    `grill_tab_bottom`: Distance from the base to the bottom of the tab.

**Libraries:**

I'll need the `chamfer.scad` library for creating the chamfered edges on the block. I believe I've seen that on Thingiverse, and GitHub. I do not have any other libraries I'd like included.

Could you provide the necessary `.scad` files for this? I'd appreciate it! Let me know if you need any further details.

[Assistant]
HT_Light_PowerCap-mod.scad:
```
// HT Lighting 12v Power End Caps
// The_Redcoat 2020-08

// Uses two functions from Chamfered primitives for OpenSCAD v1.0
// from: https://www.thingiverse.com/thing:1305888
//       https://github.com/SebiTimeWaster/Chamfers-for-OpenSCAD

include <chamfer/chamfer.scad>;

/* [Model Type] */
type="front";//[front,rear]
style="single";//[split, single]
connect_type="snap-fit";//[slide-on, snap-fit]

/* [Power Supply Dimensions] */
//Power supply width
power_width = 113.4;
//Power supply height
power_height = 49.4;
// Start of power connectors - distance from outer-left side of case to left side of first power connector; used for hole in front for wires
powercon_start=20;//.1
// Width of power connectors - also used for the hole in front for wires
powercon_width=85;//.1
// Height of the hole for the power connection hole
powercon_height=6.5;
// Vertical distance from table to bottom of wire connector - used for the hole in front for wires.
wire_height=14;//.1

/* [Exterior dimensions, Wall Thickness] */
// Wall thickness
cap_wall = 2.5;
cap_width = power_width + (2 * cap_wall);
cap_height = power_height + (2 * cap_wall);
// Depth of the power supply to be covered by the front cap
cap_wrap_depth=22;//.1
//How much to stick out past the end of the power supply
front_space=10;
// Depth of the rear cap
rear_cap_depth = 18;
// Save on plastic and print time by creating a minimalistic rear cap
rear_cap_minimal=true;

/* [Front cap options] */
// Which side is the high-voltage input on (for split model type only)
input="left";//[left,right]
// Enable hole for a power switch?
power_switch=true;
// Width of power switch hole
power_switch_width=12.8;
// Depth of power switch hole
power_switch_depth=19.6;
// Width of snap fit connector
snap_width=8;//.1
// Height of snap fit connector
snap_height=5;//.1

// Depth of the front cap
cap_depth = cap_wrap_depth + front_space + cap_wall;//34;

/* [Indicator LED] */
// Provide a power indicator light hole in the front
front_indicator=false;
// Distance from table to led, used for the front indicator hole
led_height=14;//.1
// Provide a power indicator light hole at the top
top_indicator=false;
// Distance from outer-left side of case to centre of LED, for either indicator
led_dist=7;//.1
// Distance from front of power supply to centre of LED, for indicator
led_depth=9.9;

/* [Defence Wall] */
//Barrier between high-voltage input and outputs
defense_wall=true;
// Distance from outer-left side of case to left side of defence wall
defense_wall_start=76.2;
// Width of defence wall
defense_wall_width=1.5;//.1

/* [ Wire supports] */
//Supports for outputs
support_wall=true;
//If a rounded lip should protrude from the case front
support_lip=true;
// Diameter of support lip
support_lip_diam=6;//.1

/* [ Grill tabs ] */
// Enable grill tabs - protrusions which snap into case grill holes on the sides for extra support
grill_tabs=true;
// Number of tabs to produce
grill_tab_count=3;
// First tab start from front of case
grill_tab_first=5;//.1
// Spacer widtth between tabs
grill_tab_spacer=4;//.1
// Height of tab protrusion
grill_tab_height= 18;//.1
// Width of grill tabs
grill_tab_width=3;//.1
// Depth - how much the tab sticks out from the surface, and into the case
grill_tab_depth=0.6;//.1
// Bottom of grill tab, from table
grill_tab_bottom=16;//.1

// This is the height at which to cut the model in 2, for the snap-fit model type
slice_height = cap_wall + wire_height + powercon_height;

/* [Circular Quality] */
// Quality of shapes, bigger number = more surfaces. 45 good for final, 10 for debug. Actually for this model it impacts just the power cable void quality, and cable lip
$fn=45;

module rear_cap()
{
    difference()
    {
        rear_cap_block();
        rear_cap_void();
    }
}

// This produces the front cap model
module front_cap(extras=true)
{
    difference() {
        cap_block();
        cap_void();
        if (style == "split")
            power_cable_void();
        output_power_void();
        if (front_indicator)
            front_indicator_void();
        if (top_indicator)
            top_indicator_void();
color("red")        if (power_switch)
            power_switch_hole();
    }
    if (extras) {
        if (defense_wall)
            live_defense_wall();
        if (support_wall)
            low_support_walls();
        if (support_lip)
            wire_support_lip();
    }
    if (grill_tabs)
        grill_tabs();
}

// This produces the reat cap model
module rear_cap_void()
{
    translate([cap_wall,cap_wall,cap_wall])
        chamferCube([power_width, cap_depth, power_height],
      [[1, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]], 1.5);
    if (rear_cap_minimal) {
        translate([cap_wall+10, -1, cap_wall])
            cube([power_width-20, cap_depth+2, power_height+cap_wall+11]);
    }
}

module cap_void()
{
    translate([cap_wall,cap_wall,cap_wall])
    chamferCube([power_width, cap_depth, power_height],
      [[1, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]], 1.5);
}

module power_cable_void()
{
    x_offset = (input == "left" ? powercon_start : power_width  - cap_wall*2);
    translate([x_offset, cap_wall+0.1, cap_wall+wire_height])
    rotate([90,0,0])
    scale([1,0.5,1])
    cylinder(h=cap_wall+0.2, r1=12.3/2, r2=12.3/2, center=false);
}

module output_power_void()
{
    if (style == "split") {
        x_offset = (input == "left" ? 42 : 23);
        translate([x_offset, cap_wall+0.1,12])
        rotate([90,0,0])
        cube([40,15,cap_wall+0.2]);
    } else if (style == "single") {
        translate([powercon_start, cap_wall+0.1, wire_height + cap_wall])
        rotate([90,0,0])
        cube([powercon_width, powercon_height, cap_wall+0.2]);
    }
}

// For a 1.5mm fiber optic to poke through, it was kida crap and not needed
// if you print this cap in a translucent filament
module front_indicator_void()
{
    fiber_dia = 1.5;
    translate([led_dist, cap_wall/2, led_height])
    rotate([90,0,0])
    cylinder(h=cap_wall+0.2, r=fiber_dia/2, center=true);
}

// For a 1.5mm fiber optic to poke through, it was kida crap and not needed
// if you print this cap in a translucent filament
module top_indicator_void()
{
    fiber_dia = 1.5;
    translate([led_dist, led_depth, power_height+cap_wall*3/2])
    rotate([0,0,0])
    cylinder(h=cap_wall+1, r1=fiber_dia/2, r2=fiber_dia/2, center=true);
}

module grill_tabs() {
    fudge = 0.1; // fudge factor for widths of grill tabs - shrink by this much to ensure a fit into power supply grill holes
    for (i = [1 : grill_tab_count]) {
        // Left
        translate([cap_wall,
            cap_wall + front_space + grill_tab_first + (i-1)*(grill_tab_spacer + grill_tab_width) + fudge/2,
            grill_tab_bottom])
        cube([grill_tab_depth, grill_tab_width-fudge, grill_tab_height]);
        // Right
        translate([power_width + cap_wall - grill_tab_depth,
            cap_wall + front_space + grill_tab_first + (i-1)*(grill_tab_spacer + grill_tab_width) + fudge/2,
            grill_tab_bottom])
        cube([grill_tab_depth, grill_tab_width-fudge, grill_tab_height]);
    }
}

module power_switch_hole() {
    translate([powercon_start + powercon_width - power_switch_width/2, cap_depth/2, cap_height-cap_wall/2]) rotate([0, 180, 0])
    linear_extrude(cap_wall, center = true, convexity = 4, scale=1.2) square([power_switch_width, power_switch_depth], center=true);
}

// External lip on front face for additional wire support
module wire_support_lip() {
    lr = support_lip_diam/2;
    translate([powercon_start, 0, wire_height + cap_wall - lr])
    rotate([0,90,0])
    difference() {
        cylinder(h=powercon_width, r=lr);
        translate([-lr, 0, 0])
        cube([lr*2, lr, powercon_width+1], center=false);
    }
}

// Overkill, not needed
module live_defense_wall()
{
    translate([defense_wall_start, 0, wire_height + cap_wall])
    cube([defense_wall_width, front_space+cap_wall, powercon_height]);
}

// These box in the opening for the low voltage wiring (on 3 sides)
module low_support_walls()
{
    if (style == "split") {
        x_offset = (input == "left" ? 42 : 23);
        translate([x_offset,cap_wall,12-2])
        cube([40,front_space,2]);

        translate([x_offset-2,cap_wall,12-2])
        cube([2,front_space,15+2]);

        translate([x_offset-2+40+2,cap_wall,12-2])
        cube([2,front_space,15+2]);
    } else {
        // bottom
        translate([powercon_start, cap_wall, wire_height + cap_wall - 2])
        cube([powercon_width, front_space,2]);
        // left
        translate([powercon_start-2,cap_wall,wire_height + cap_wall-2])
        cube([2, front_space, powercon_height+2]);
        // right
        translate([powercon_start-2+powercon_width+2,cap_wall,wire_height + cap_wall-2])
        cube([2, front_space, powercon_height+2]);
    }
}

// Snap-fit connectors, used internally for the "snap-fit" model type, to help hold things together when split in half
module snap_connect(x, y, z, h=5, w=8, thk=1.2, rot=true) {
    // basic shaft part
    translate([x-thk, y, z])
        cube([thk, w, h]);
    // Triangular snap-fit bit itself
    translate([x+(rot ?  -1.8 : 1.8-thk), rot ? y + w: y, z+h])
        rotate([0,0,rot ? 270 : 90])
            prism(w, 1.8, 2.6);
}

module snap_hole(x, y, z, h=snap_height, w=snap_width, thk=1.2, rot=true, fudge=0.1) {
    // basic shaft part
    translate([x-(rot ? (thk+fudge) : (grill_tab_depth+fudge)), y-fudge*2, z])
        cube([thk+fudge*2 + grill_tab_depth, w + fudge*4, h+fudge*2]);
    // Triangular snap-fit bit itself
    translate([x - (rot ?  2 : grill_tab_depth + 1), y - fudge*8, z+h])
        cube([2 + grill_tab_depth + 1, w + fudge*16, 3]);
}

module cap_block()
{
    // Chamfered cap box. Weird boolean arrays are for each face edge, on or off
    // [x-orbit][y-orbit][z-orbit] and some trial and error
    chamferCube([cap_width, cap_depth, cap_height],
      [[1, 0, 0, 1], [1, 1, 1, 1], [1, 1, 0, 0]], 1.5);
}

module rear_cap_block()
{
    // Chamfered cap box. Weird boolean arrays are for each face edge, on or off
    // [x-orbit][y-orbit][z-orbit] and some trial and error
    chamferCube([cap_width, rear_cap_depth, cap_height],
      [[1, 0, 0, 1], [1, 1, 1, 1], [1, 1, 0, 0]], 1.5);
}

  module prism(l, w, h) {
      polyhedron(//pt 0        1        2        3        4        5
              points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
              faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]
              );
}

//RENDER OPTIONS: front_cap or rear_cap
if (type == "front") {
    thickness = 1.2;
    //split_line =
    difference() {
        // Bottom-half for snap-fit, whole thing otherwise
        front_cap();
        // For snap-fit, cut off the top, we will redraw the top next in this case (see below)
        if (connect_type == "snap-fit") {
            translate([-1, -1, slice_height])
                cube([cap_width+2, cap_depth+2, cap_height+1]);
        }
    }
    if (connect_type == "snap-fit") {
        snap_indent=9;
        // Create snap connectors on the bottom portion
        // left rear
        snap_connect(cap_wall, cap_depth-snap_indent, slice_height, thk=thickness, rot=true);
        // left front
        snap_connect(cap_wall, cap_wall+4, slice_height, thk=thickness, rot=true);
        // right reat
        snap_connect(cap_width-cap_wall+thickness, cap_depth-snap_indent, slice_height, thk=thickness, rot=false);
        // right front
        snap_connect(cap_width-cap_wall+thickness, cap_wall+4, slice_height, thk=thickness, rot=false);
        // Redraw the whole thing at an elevation, cutting off the bottom this time
        translate([0,0,cap_height+2])
//color("red")                snap_hole(cap_wall, cap_depth-snap_indent, slice_height, thk=thickness, rot=true);
            difference() {
                front_cap(false);
                // This time, leave space for the snap-connectors in the top portion
                snap_hole(cap_wall, cap_depth-snap_indent, slice_height, thk=thickness, rot=true);
                snap_hole(cap_wall, cap_wall+4, slice_height, thk=thickness, rot=true);
                snap_hole(cap_width-cap_wall, cap_depth-snap_indent, slice_height, thk=thickness, rot=false);
                snap_hole(cap_width-cap_wall, cap_wall+4, slice_height, thk=thickness, rot=false);

                translate([-1, -1, -1])
                    cube([cap_width+2, cap_depth+2, slice_height+1]);
            }
    }
} else if (type == "rear")
    rear_cap();
else {
    //linear_extrude(height = 5, center = true, convexity = 10, twist = -fanrot, slices = 20, scale = 1.0, $fn = 16)
prism(5, 1.8, 2.6);
    }

```


[User]
Hey, I'm looking for an OpenSCAD design for a customizable Dupont pin connector holder. I'd like it to be a two-part, snap-together design, similar to the ones I've seen on Thingiverse. The basic idea is that the two halves will clamp onto the wires connected to the pins.

Specifically, I need to be able to specify:

*   Whether to print just the base, just the lid, or both parts.
*   The number of pins in a row, from 1 to 20.
*   The number of rows, either 1 or 2 (so, a single row, or a double row of pins).
*   An adjustment for the pin header position (some headers aren't all exactly the same length, and sometimes you need them to stick out a little bit to get clearance).

I'm going to be printing these in either PLA+ or PETG, so I don't need anything too exotic. I'm thinking the design should incorporate rounded edges for a cleaner look and to aid in printing without supports. I'd also like the lid to have some side clips that match up with notches on the base so that the two parts clip together securely. There also need to be wire notches for each pin on both the base and an internal brace within the base, and similarly on the lid.

I think I can handle the slicing settings myself, but please make sure the code is well-commented so I can understand what's going on. For any advanced curves, please use a high fragment count.

I'd also like to use the `BOSL2` library for the rounding and shape creation primitives, if possible. And the BOSL2 library should be included at the beginning of any generated files.

Thanks!

[Assistant]
Dupont_Holder_23.scad:
```
/*
   Dupont 0.1" Pin Holder - Customizable Size
   ----------------------
   G. A. Tielke, March 2023

   Creates a two-part snap-together holder for Dupont pin connectors.
   Pins are spaced 0.1" (2.54mm) apart, either M or F type.
   Print two parts which snap together over the wires.

   This design was based on:
        Connector housing Dupont 8Pin
		by NerdCorner February 06, 2023
        https://www.thingiverse.com/thing:5835440

   "NerdCorner" has several varieties available, along with a web
   blog and much more information. Be sure to check it out.

   The original design was for 8 pins in a single row only.
   This customizer allows 1 to 20 pins, in one or two rows.
   The pin header location may be adjusted further in or out
   from the holder. (Not all pin headers are the same length;
   also allows pin headers to stick out by a few mm if needed
   for clearance.)

>Specify these settings:
 -Print either one or both of the two sections
 -The number of pins across (default of 8)
 -The number of pins thick (1 or 2)
 -In/out adjustment of header position

>SLICING: I used Sunlu PLA+ with these settings:
  220 degrees C, print bed at 70 degrees C.
  Layer Height=0.2 (can use 0.12 for better quality)
  Speed=50 (or slower), first few layers at 25
  Infill=100%
  Supports: None
  Adhesion: None or Raft

May use PETG for a tougher result:
  240 degrees C, print bed at 80 degrees C.
==========================================
*/

/* [Print Controls] */
// 0=Print Base, 1=Print Lid, 2=PRINT BOTH:
print_section = 2; //[0:1:2]
// Number of pins across:
pins_wide = 8; // [1:1:20]
// Number of pins tall:
pins_tall = 1; // [1:1:2]
// Adjust pin location vertically:
pin_adjust = 0.0; // [-2.0:0.5:4.0]

/* [Hidden] */
SS=32; // Improves all curved edges

/* NEW */
p = pins_wide;
//t = pins_tall - 1; // 0 or 1
pmm = p * 2.54; // Width of the pins themselves in mm
tmm = (pins_tall - 1) * 2.54; // Added thickness in mm
cmm = pin_adjust; // Adjustment amount in mm
tot_width = pmm + 3.82;

module RoundedRectangle(Width, Length, Height, Radius) {
/*
	Create a rectangular box with rounded corners.
	Top and bottom sides are flat.
*/
	translate([Radius,Radius,0]) hull() {
		for(i=[0:1]) {
		for(j=[0:1]) {
			translate([i*(Width-2*Radius),j*(Length-2*Radius),0])
				cylinder(r=Radius, h=Height, center=false,
				$fn = SS);
		}} // End both For loops
	}
};

module RoundedRhombus(Width, Length, Height, Rad) {
/*
	Create a rectangular box with all edges rounded,
	including the top and bottom edges.
*/
	translate([Rad,Rad,Rad]) hull() {
		// Repeat for four corners, two in each direction:
		for(i=[0:1]) {
		for(j=[0:1]) {
			// Translate so spheres are just inside each corner:
			translate([i*(Width-2*Rad),j*(Length-2*Rad),0]) {
				// Sphere for bottom of each corner:
				sphere(r=Rad,$fn = SS);
				// Sphere for top of each corner:
				translate([0,0,Height-2*Rad]) sphere(r=Rad, $fn=SS);
			}
		}} // End both For loops
	}
};

/*
================================================================
================================================================
================================================================
	Begin creating the BASE section:
    This is the slightly smaller portion, which the TOP section
      will snap over.
--Create the body of the Base:  */
if (print_section != 1) union() {
	difference() {
		union() {
			// Body with rounded corners:
			RoundedRhombus(tot_width, 25, 7, 1.3/2);

		} // End of Add section

		// SUBTRACT THESE:
		union() {
			// Chop off the top, to leave a flat top to the Base:
			translate([0,0,4+tmm]) cube([pmm+4,26,4]);

			// Scoop out the interior of the Base:
			translate([1.72,0,1.46]) cube([pmm+0.5,23,6]);

			// Add notches for wires, at top of Base:
			translate([2.44+.75,25.1,2.0]) {
				for(n=[1:p]) { // For each pin
					translate([(n-1)*2.54,0,0.75]) {
						// Remove rounded bottom of notches:
						rotate([90,0,0])
							cylinder(r=0.75, h=2.2, center=false,
							$fn = SS);
						// Remove vertical part of notch:
						translate([-.75,-3,0])
							cube([1.5,4,1.5+tmm]);
						// Remove two bevels at top of notch:
						translate([0.2,-3,1.5+tmm]) rotate([0,45,0])
							cube([.8,3,0.8]);
						translate([-1.3,-3,1.5+tmm]) rotate([0,45,0])
							cube([.8,3,0.8]);
						};
				};
			};

			// Subtract rectangular window:
			translate([tot_width/4,2,0]) cube([tot_width/2,3,3]);

			// Subtract side notches for snap-on lid:
			w1=pmm + 3.32; // Width distance to other side
			for(ss=[0,1]) { // Two sides of Base
			for(nn=[0:2]) { // Three pairs of notches
				// Remove vertical part of side notch:
				translate([ss*w1,1.4+8*nn,0])
					cube([0.5,5.2,4.1+tmm]);
				// Remove bevel at top of side notch:
				translate([-.2+ss*(w1-.4),1.4+8*nn,4+tmm])
					rotate([0,45,0]) cube([1,5.2,1]);
			} // end of for ss
			} // end of for nn

		} // End of Subtract section for BASE
	} // End of Difference for BASE

	// Add interior brace/wire guide to BASE:
	difference() {
		translate([1,14-cmm,0]) cube([pmm+1.8,1,2.46+tmm]);
		// Add notches for wires to interior guide:
		translate([2.44+.75,15.1-cmm,2.46]) {
			for(n=[1:p]) { // For each pin
				translate([(n-1)*2.54,0,0]) {
					// Rounded bottom of guides:
					rotate([90,0,0])
						cylinder(r=0.75, h=1.2, center=false,
						$fn = SS);
					// Vertical part of guides:
					translate([-0.75,-1.3,0]) cube([1.5,1.5,.2+tmm]);
					// Remove two bevels at top of each guide:
					translate([0.2,-3,.2+tmm]) rotate([0,45,0])
						cube([.8,3,0.8]);
					translate([-1.3,-3,.2+tmm]) rotate([0,45,0])
						cube([.8,3,0.8]);
					};
			};
		};
	};

}; // END OF BASE SECTION

/*
//	================================================================

	Begin creating the TOP section:
    This will snap-fit over the Base section.
--Create the body of the Lid:   */
if (print_section != 0) translate([0,30,0]) union() {
	difference() {
		union() {
			// Body with rounded corners:
			RoundedRhombus(pmm+3.88, 25, 3, 1.3/2);
		}

		// SUBTRACT THESE:
		union() {
			// Chop off the top, to leave a flat top to the Base:
			translate([0,0,1.5]) cube([pmm+4,26,4]);

			// Subtract rectangular window:
			translate([tot_width/4,2,0]) cube([tot_width/2,3,3]);
		}
	};

	// ADD Clamping bars to lid:
	translate([2.5,25-3.3,0]) cube([pmm-1.17,1.2,2.1]);
	translate([1,14-cmm,0]) cube([pmm+1.8,1,2.1]);

	// ADD IN SIDE CLIP PARTS TO THE LID:
	for(s=[0:1]) { // For each of two sides
	for(c=[0:2]) { // For three pairs of clips
		translate([-1.5+s*(pmm+3.88+3),(c*8)+2+s*5,0])
		rotate([0,0,s*180]) difference() {
			union() {
				// Add arms to sides of clip:
				difference() {
					RoundedRhombus(3,5,5.6+tmm,.5);
					translate([0,1,0]) cube([1.5,3,7]);
				};
				// Add center body of clip:
				translate([1,4,0]) rotate([90,0,0])
					RoundedRectangle(1.3,6.3+tmm,3,.65);
			};
			// Subtract inside face of rounded clip parts:
			translate([2,0,0]) cube([2,5,5.6+tmm]);
			// Subtract an angled wedge at top of clip:
			translate([2.3,0,5.6+tmm]) rotate([0,-28,0])
				cube([.7,5.2,.7]);
		};
	} // end for s
	} // end for c

} // END OF TOP SECTION

```


[User]
I'd like a design for a customizable nameplate with curved, 3D letters. The nameplate should have a rectangular bar as its base, with the text extruded in a curved arc on top. I need to be able to control the text, the bar's length, width, and thickness, the curve of the letters, and the size of the letters.

Here's a breakdown of what I'm looking for:

*   **Text:** The user should be able to specify any text string.
*   **Bar:**
    *   The bar is a rectangular prism.
    *   The user should control the length, width, and thickness of the bar.
    *   The bar should have beveled edges on the top front and back. The user should be able to specify the bevel face width.
*   **Letter Curve:**
    *   The letters should be extruded along an arc, creating a curved 3D effect.
    *   The user should be able to control the angle of this curve. Think of it like a slice of a cylinder, where the user defines the arc of the slice.
*   **Letter Placement:** The letters should appear centered along the length of the bar.
*   **Letter Size:** The user should be able to control the font size of the letters.
*   **Font:** The user should be able to select a font.

I think it would be helpful to make sure the design takes into account that OpenSCAD doesn't reliably do `rotate_extrude` for less than 360 degrees. I saw one example where someone used `linear_extrude` and rotated that for an equivalent effect, maybe that approach would work here.

To summarize the parameters:
* `the_text` (string): Text for the nameplate
* `bar_length` (number): Length of the bar
* `bar_width` (number): Width of the bar
* `bar_thickness` (number): Thickness of the bar
* `bevel_face` (number): Width of the bevel face
* `extrude_angle` (number): Angle of the extrusion sweep.
* `forward_offset` (number): amount to move the bar to help centering the letters, this can be a fixed value.
* `fontSize` (number): Size of the letters
* `font` (string): Name of the font

I don't have any specific needs beyond the built-in OpenSCAD libraries so you don't need to worry about dependencies.

[Assistant]
name_block2.scad:
```
// Name plate with curved letters
// David O'Connor david@lizardstation.com
// 27 Feb 2023
//
// 9 July 2023  Edited to work with Thingiverse's Customizer, which won't do "rotate_extrude"
// for angles other than 360 degrees.
//

// Your text
the_text = "Your Text Here";

// Length of the bar
bar_length = 120;

// Width of the bar
bar_width = 17;

// Thickness of the bar
bar_thickness = 4;

// Face width of the top front and back edges of the bar
bevel_face = 1.6;

// Angle of the extrusion sweep
extrude_angle = 20; // [0:1:90]

// Amount to move the bar forwards to center the letters properly
forward_offset = 3;

// Size of the letters
fontSize = 12;

// Small distance for preventing CSG Ambiguities
eps = 0.1;

// Number of steps per 360 degree sweep.  Make smaller if you get a rendering error.
fn = 50;

// Your Font
font = "Berlin Sans FB";

rotate([180, 0, 180]) {
    rotate([90, 0, -90]) {
        translate([0.5 * fontSize, 0, 0]) {
            intersection() {
                rotate_extrude(angle=360, $fn = fn) {
                    translate([-fontSize, 0, 0])rotate([0, 0, 90])  {
                        text(the_text, font=font, size = fontSize, halign="center");
                    }
                }
                letter_limit();
            }
        }
    }
    b = bar_width;
    c = bar_thickness;

    translate([-0.5 * bar_length, forward_offset + 0.5 * b, 0.5 * c - eps]) {
        beveled_box([bar_length, b, c], bevel_face);
    }
}

// Create the volume that defines the extent for the rotate_extrude() text.
module letter_limit() {
    linear_extrude(bar_length, center = true) {
        polygon([[0, 0], [-100, 0], [-100 * cos(extrude_angle), -100 * sin(extrude_angle) ]]);
    }
}

module beveled_box(size = [1, 1, 1], bevel = 0.1) {
    a = size[0];
    b = size[1];
    c = size[2];
    bv = bevel * sqrt(0.5);
    P = [
        [b/2 - bv, -c/2],
        [b/2, -c/2 + bv],
        [b/2, c/2],
        [b/2 - bv, c/2],
        [-b/2 + bv, c/2],
        [-b/2, c/2],
        [-b/2, -c/2 + bv],
        [-b/2 + bv, -c/2]
    ];
    rotate([90, 0, 90])
    linear_extrude(a) {
        polygon(P);
    }
}
```


[User]
I'd like to create a phone holder that uses gears to adjust the side clamps. It should have a base frame, side clamps that move in and out, a bottom clamp, gears to drive the side clamps, and pins to hold the gears in place. I'd like the design to be parametric, so I can adjust the dimensions to fit different phones.

Here's what I'm thinking:

**Overall Structure:**
*   There should be a main frame structure that everything attaches to.
*   Two side clamps should move horizontally, driven by gears.
*   A bottom clamp should move vertically.
*   Gears on the side move racks connected to the side clamps to adjust the grip.
*   The gears should be attached to the frame with pins.
*   The design should be easy to modify with parameters in the customizer.
*   I'd like a simple "mount base" option for attaching to other mounts.

**Specific Components and Requirements:**

*   **Frame:**
    *   A rigid frame using rails for the clamps to slide on.
    *   Horizontal rails for the side clamps, with cutouts to avoid interference.
    *   Vertical rails for the bottom clamp.
     *   A mount base should be an option, for easier attachment to existing mounts. It should be a simple rectangular base.
*   **Clamps:**
    *   Side clamps should have a curved shape to grip the phone securely.
    *   Bottom clamp should have a similar curve but be wider.
    *   The bottom clamp should also have a USB cutout.
 *  The clamps should use a rack and pinion system for smooth movement.
*   **Gears:**
    *   Gears should drive the side clamp racks.
     *   The gears should be able to rotate freely, but stay attached via pins.
    *   I'd like the gear and rack module to use the MCAD library.
    *   The gears need to have a way of being pinned to the frame, for example using an axle.
*   **Pins:**
    *   Pins should hold the gears in place.
    *   They need a head so they do not fall out.
    *   They need to have a little tolerance so they can be easily inserted.
*   **Parameters:**
    *   `neutral_width`: The width of the phone holder in neutral position
    *  `neutral_height`:  The height of the phone holder in neutral position.
    *   `vert_clamp_offset`: vertical offset of the side clamps.
    *   `clamp_depth`: The depth of the clamp.
    *   `clamp_thickness`: The thickness of the clamp's material.
    *   `clamp_overhang`: How much the clamp extends over the base.
    *   `bottom_clamp_width`: The width of the bottom clamp.
    *   `side_clamp_heigth`: The height of the side clamps.
    *   `gear_pitch`: Pitch of the gears.
    *   `gear_teeth`: Number of teeth of the gears.
    *   `rack_teeth`: Number of teeth of the racks.
    *  `include_mount_base`: 0 (false) or 1 (true) to enable a base for mount.
    *  `mount_base_depth`: how deep is the mount base.
 *  `frame_thickness`: thickness of the base frame.

**Library Dependencies:**

*   I'd like to use the MCAD library for the rack and pinion gears and for some of the math functions.

**Additional Notes:**
*   It would be great to have an option to visualize different parts of the design separately or together in OpenSCAD using a `part` variable, like in the example code.
*   I'd like a variable `pitch_offset` to move the bottom clamp up and down for animation purposes, like the code does, so please include that in the design.
*   Please consider printing tolerances, so the fit is good.

Could you provide me with the OpenSCAD code for this design?

[Assistant]
phoneholder.scad:
```
part = 0; // [0:all, 1:frame, 2:clamps, 3:gears, 4:pins, 5:(just for view) composed]

include_mount_base = 0; // [0:false, 1:true]

/* [Clamp positions] */

neutral_width = 77;

neutral_height = 25;

vert_clamp_offset = -9;

/* [Clamp dimensions] */

clamp_depth = 12;

clamp_thickness = 1.5;

clamp_overhang = 1;

bottom_clamp_width = 40;

side_clamp_heigth = 30;

/* [Hidden] */

// For animation
minimal_pitch_offset = - 8.2;
maximal_pitch_offset = 9;
pitch_offset = sin($t * 360) * (maximal_pitch_offset - minimal_pitch_offset) / 2 + (maximal_pitch_offset +
    minimal_pitch_offset) / 2;
//pitch_offset = -8.2;

$fa = 1;
$fs = $preview ? 1 : 1; // : 0.5;
X = [1, 0, 0];
Y = [0, 1, 0];
Z = [0, 0, 1];

axel_radius = 3;
pin_head_radius = 5;
pin_head_thickness = 1;
pin_tolerance = 0.3;
rail_tolerance = 0.2;

gear_thickness = 3;
gear_x_offset = 18;
gear_y_offset = neutral_height;

gear_pitch = 5.2; // mm / tooth
gear_teeth = 16;
gear_pie_angle = 148;

rack_teeth = 4;
rack_length = rack_teeth * gear_pitch;
rack_width = 5;

rail_radius = 2;
rail_offset = 3;

frame_thickness = 3;

// to align the gears with the side rack
teeth_offset = (gear_teeth % 4) * gear_pitch / 4;

// Adjust this with a custom mount to lift the frame in the 'all' layout
mount_base_depth = 10;

//****** What to display or export ******//

if (part == 0 || part == 1) {
    lift = include_mount_base == 1 ? mount_base_depth : rail_offset + rail_radius * 2;
    translate([0, 35, lift]) frame();
}
if (part == 0 || part == 2) {
    translate(Z * (rail_radius * 2 - 0.6)) {
        translate(Y * pitch_offset) bottom_clamp();
        side_clamp();
        mirror(X) side_clamp();
    }
}
if (part == 0 || part == 3) {
    translate(Y * - 5) {
        translate(X * 3) gear();
        translate(X * - 3) mirror(X) gear();
    }
}
if (part == 0 || part == 4) {
    translate([- gear_x_offset + pin_head_radius + 2, - gear_y_offset - pin_head_radius - 5, gear_thickness])mirror(Z)
        pin();
    mirror(X) translate([- gear_x_offset + pin_head_radius + 2, - gear_y_offset - pin_head_radius - 5, gear_thickness])
        mirror(Z)pin();
}
if (part == 5) {
    // Show some or all elements composed together
    rotate(X * 90) composit(
    clamps = true,
    gears = true,
    frame = true,
    pins = true);
}

module composit(frame = true, clamps = true, gears = true, pins = true) {
    if (gears) color("white") {
        gear();
        mirror(X) gear();
    }
    if (clamps) color("green") {
        translate(Y * pitch_offset) bottom_clamp();
        side_clamp();
        mirror(X) side_clamp();
    }
    if (frame) {
        frame();
    }
    if (pins) {
        pin();
        mirror(X) pin();
    }
}

// Modify this to make a custom mount
module mountbase() {
    // The Z position from which it is save to attach a mount without disrupting the rails
    z_offset = - rail_offset - rail_radius;

    if (include_mount_base == 1) {
        translate(Y * 25) hull() {
            translate([0, 0, z_offset - 1])
                rounded_plate([rail_offset + rail_radius * 2 - 1, 15, 1], 1);

            translate([0, 0, - mount_base_depth])
                rounded_plate([20, 20, 1], 1);
        }
    }
}

module frame() {
    vlength = rack_length * 1.3;
    hlength = gear_x_offset * 3.4;
    y1 = gear_y_offset + gear_x_offset + rail_offset;
    y2 = gear_y_offset + gear_x_offset - rail_offset;
    // vertical rail
    translate(Y * (y2 - vlength / 2)) rail(length = vlength, male = false);
    // complete the joint with the horizontal rail
    extension = 10;
    translate([0, gear_x_offset + gear_y_offset - extension / 2, - rail_radius])rotate(X * - 90)
        difference() {
            cylinder_tube(height = extension, radius = rail_offset + rail_radius, wall = 2, center = true);
            translate(Y * - 2)cube([20, rail_offset * 2 + rail_radius * 2, 20], center = true);
        }

    // horizontal rail with cutout in the middle
    difference() {
        translate([0, gear_y_offset + gear_x_offset, 0]) rotate(Z * 90)
            rail(length = hlength, male = false);
        translate([- rail_offset, y2 - rail_radius - 0.1, - rail_radius * 2 - 0.1])
            cube([rail_offset * 2, rail_radius * 2 + 0.2, rail_radius * 2 + 0.2]);
    }

    module gear_base() {
        module connector() {
            translate([- gear_x_offset + rail_offset, - 2])
                square([gear_x_offset - pin_head_radius - 2, 4]);
        }
        translate([gear_x_offset, gear_y_offset, - rail_radius * 2]) {
            linear_extrude(height = rail_radius * 2) {
                difference() {
                    circle(r = pin_head_radius + 2);
                    circle(r = axel_radius);
                }
                connector();
                rotate(- 90) connector();
            }
        }
    }
    gear_base();
    mirror(X) gear_base();

    color("red") mountbase();
}

module side_clamp() {
    translate(X * pitch_offset) {
        translate([gear_x_offset + teeth_offset, gear_y_offset + gear_x_offset, 0])
            rack(cp = gear_pitch, N = rack_teeth, width = gear_thickness, thickness = rack_width);
        translate([neutral_width / 2, gear_y_offset + gear_x_offset + vert_clamp_offset, gear_thickness])
            rotate(X * 90)
                linear_extrude(height = side_clamp_heigth, center = true) phoneclamp_shape();
        hull() {
            translate([neutral_width / 2 - 2, gear_y_offset + gear_x_offset - 15 + vert_clamp_offset,
                    gear_thickness - clamp_thickness]) cube([0.1, 30, clamp_thickness]);
            translate([gear_x_offset + rack_length / 2, gear_y_offset + gear_x_offset - rack_width / 2, 0]) cube([2,
                rack_width,
                gear_thickness]);
        }
        translate([gear_x_offset + teeth_offset, gear_y_offset + gear_x_offset, 0]) rotate(Z * 90)
            rail(length = rack_length * 3 / 4, male = true);
    }
}

module bottom_clamp() {
    rail_depth = 3; // sink the rack to cutout the rail in the gears

    module bottom_clamp() {
        module usb_cutout() {
            d = clamp_depth + clamp_thickness * 2;
            usb_w = 14;
            usb_h = 8;
            translate([0, 3, gear_thickness - clamp_thickness + d / 2])
                rotate(X * 90) rounded_plate([usb_w, usb_h, 6], 2);
        }
        difference() {
            translate([0, 0, gear_thickness])rotate([90, 0, - 90])
                linear_extrude(height = bottom_clamp_width, center = true, convexity = 4)
                    phoneclamp_shape();
            usb_cutout();
        }
    }

    module rack_side() {
        rack(cp = gear_pitch, N = rack_teeth, width = gear_thickness, thickness = rack_width);
    }

    translate(Y * gear_y_offset)rotate(Z * - 90) {
        rack_side();
        mirror(Y) rack_side();
    }
    bottom_clamp();
    hull() {
        translate([- 15, 2, gear_thickness - clamp_thickness]) cube([30, 0.1, clamp_thickness]);
        translate([- rack_width / 2, gear_y_offset - rack_length / 2 - 1, 0]) cube([rack_width, 2,
            gear_thickness]);
    }
    // Make the rail part a bit longer to prevent it from moving up so much that the
    // sides wil detach from the gear
    extra_length = 4;
    translate(Y * gear_y_offset)rail(length = rack_length * 3 / 4 + extra_length, male = true);
}

module rail(length, male = false) {
    tolerance = male ? rail_tolerance : 0;
    module rail_side() {
        translate([rail_offset, 0, - rail_radius]) {
            rotate(X * - 90)
                cylinder(h = length + tolerance * 2, r = rail_radius + tolerance, center = true);
            if (!male) {
                translate(Y * - length / 2) sphere(r = rail_radius);
                translate(Y * length / 2) sphere(r = rail_radius);
            }
        }
    }
    module rail_base() {
        difference() {
            translate(Z * - rail_radius)rotate(X * - 90)
                cylinder_tube(height = length, radius = rail_offset + rail_radius, wall = 2.5, center = true);
            translate([0, 0, rail_offset / 2 + rail_radius / 2 + 0.05 - rail_radius])
                cube([rail_offset * 2 + rail_radius * 2, length + 0.2, rail_offset + rail_radius + 0.1],
                center = true);
        }
    }
    if (male) {
        difference() {
            translate([0, 0, - rail_radius + 0.4])
                cube([rack_width, length, 2 * rail_radius - 0.4], center = true);
            rail_side();
            mirror(X) rail_side();
        }
    } else {
        rail_base();
        rail_side();
        mirror(X) rail_side();
    }
}

module gear() {
    translate([gear_x_offset, gear_y_offset, 0]) {
        rotate(Z * pitch_offset / 5.2 / gear_teeth * - 360) {
            intersection() {
                difference() {
                    pinion(cp = gear_pitch, N = gear_teeth, width = gear_thickness, shaft_diameter = 5);
                    translate(Z * - 0.1)
                        cylinder(h = gear_thickness + 0.2, r = axel_radius);
                    translate(Z * (gear_thickness - pin_head_thickness))
                        cylinder(h = pin_head_thickness + 0.1, r = pin_head_radius);
                }
                translate(Z * - 0.1) linear_extrude(height = gear_thickness + 0.2) {
                    circle(r = pin_head_radius + 2);
                    //                        rotate(Z * 45) translate(X * - gear_x_offset) square([gear_x_offset * 2, gear_x_offset]);
                    rotate(- gear_pie_angle / 2 - 45)
                        difference() {
                            circle(r = gear_x_offset);
                            translate([- gear_x_offset, 0]) square(gear_x_offset * 2);
                            rotate(- 180 + gear_pie_angle)square(gear_x_offset);
                        }
                }
            }
        }
    }
}

module pin() {
    clip_length = 2;
    length = gear_thickness + rail_radius * 2 + clip_length;
    module cutout() {
        translate(Z * (rail_radius + clip_length / 2 - 0.1))
            cube([axel_radius * 2, 1, rail_radius * 2 + clip_length], center = true);
    }
    module bump() {
        translate([axel_radius - 0.6, 0, 1.0]) sphere(r = 1);
    }
    color("dimgray") translate([gear_x_offset, gear_y_offset, - length + gear_thickness]) {
        difference() {
            union() {
                cylinder(h = length, r = axel_radius - pin_tolerance);
                translate(Z * (length - pin_head_thickness))
                    cylinder(h = pin_head_thickness, r = pin_head_radius - pin_tolerance);
            }
            translate(Z * - 0.1)cylinder(h = length + 0.2, r = 1.5);
            cutout();
            rotate(Z * 90)cutout();
        }
        for (i = [0:4]) {
            rotate(Z * (45 + i * 90)) bump();
        }
    }
}

// 2D shape for the phone clamp
module phoneclamp_shape() {
    outer = clamp_depth + clamp_thickness * 2;
    roundness = 2;
    module shape(inner = false) {
        height = inner ? outer - clamp_thickness * 2 : outer;
        offset = inner ? clamp_thickness : 0;
        translate([- offset, offset])square([clamp_overhang + clamp_thickness, height]);
        translate([clamp_overhang + clamp_thickness - offset, outer / 2]) scale([roundness / (height / 2), 1]) circle(d
        = height);
    }
    module edge() {
        scale([0.5, 1])circle(d = clamp_thickness, $fn = 10);
    }

    translate([- clamp_overhang - roundness, - clamp_thickness]) {
        difference() {
            shape();
            shape(inner = true);
            translate([- 0.1, - 0.1])square([clamp_overhang + 0.1, clamp_thickness + 0.2]);
        }
        translate([clamp_overhang, clamp_thickness / 2]) edge();
        translate([0, outer - clamp_thickness / 2]) edge();
    }
}

// box with (only) rounded side edges. Centered in XY plane but not in Z.
module rounded_plate(size, radius) {
    dx = size[0] / 2 - radius;
    dy = size[1] / 2 - radius;
    h = size[2];
    hull() {
        translate([- dx, - dy, 0])cylinder(r = radius, h = h);
        translate([dx, - dy, 0])cylinder(r = radius, h = h);
        translate([dx, dy, 0])cylinder(r = radius, h = h);
        translate([- dx, dy, 0])cylinder(r = radius, h = h);
    }
}

//**** The following are generic library functions ****/

// From MCAD/general/constants.scad (https://github.com/openscad/MCAD/tree/dev)
const_pi = 3.14159265358979323846264338327950288419716939937510;

// From MCAD/shapes/3Dshapes.scad (https://github.com/openscad/MCAD/tree/dev)

//Tubifies any regular prism
module tubify(radius, wall)
{
    difference()
        {
            children(0);
            translate([0, 0, - 0.1]) scale([(radius - wall) / radius, (radius - wall) / radius, 2]) children(0);
        }
}

module cylinder_tube(height, radius, wall, center = false)
{
    tubify(radius, wall)
    cylinder(h = height, r = radius, center = center);
}

// From MCAD/gears/rack_and_pinion.scad (https://github.com/openscad/MCAD/tree/dev)

module rack(cp, N, width, thickness) {
    // cp = circular pitch - for rack = pitch in mm/per tooth
    // N = number of teeth
    // width = width of rack
    // thickness = thickness of support under teeth (0 for no support)

    a = 1.0 * cp / const_pi; // addendum (also known as "module")
    d = 1.1 * cp / const_pi; // dedendum (this is set by a standard)
    height = (d + a);

    // find the tangent of pressure angle once
    tanPA = tan(20);
    // length of bottom and top horizontal segments of tooth profile
    botL = (cp / 2 - 2 * d * tanPA);
    topL = (cp / 2 - 2 * a * tanPA);

    slantLng = tanPA * height;
    realBase = 2 * slantLng + topL;

    offset = topL + botL + 2 * slantLng;
    length = (realBase + botL) * N;

    supportSize = width;
    translate([botL / 2, 0, 0])
        rotate([90, 0, 0]) {
            translate([0, supportSize / 2, 0]) {
                union() {
                    cube(size = [length, width, thickness], center = true);
                    for (i = [0:N - 1]) {
                        translate([i * offset - length / 2 + realBase / 2, 0, thickness / 2]) {
                            trapezoid([topL, supportSize], [realBase, supportSize], height);
                        }
                    }
                }
            }
        }
}

module pinion(cp, N, width, shaft_diameter, backlash = 0) {
    // cp= circular pitch - for pinion pitch in mm/per as measured along the ptich radius (~1/2 way up tooth)
    // N= number of teeth
    // width= width of the gear
    // shaft_diameter= diameter of hole for shaft
    // backlash - I think this is just a bodge for making things fit when printed but I never tested it

    if (cp == false && diametral_pitch == false)
        echo("MCAD ERROR: pinion module needs either a diametral_pitch or cp");

    //Convert diametrial pitch to our native circular pitch
    cp = (cp != false?cp:180 / diametral_pitch);

    // Pitch diameter: Diameter of pitch circle.
    pitch_diameter = N * cp / const_pi;
    pitch_radius = pitch_diameter / 2;
    echo("Teeth:", N, " Pitch radius:", pitch_radius);
    // Base Circle
    base_radius = pitch_radius * cos(20);

    // Addendum: Radial distance from pitch circle to outside circle.
    addendum = cp / const_pi;

    //Outer Circle
    outer_radius = pitch_radius + addendum;

    // Dedendum: Radial distance from pitch circle to root diameter
    dedendum = addendum * 1.1;

    // Root diameter: Diameter of bottom of tooth spaces.
    root_radius = pitch_radius - dedendum;

    backlash_angle = backlash / pitch_radius * 180 / const_pi;
    half_thick_angle = (360 / N - backlash_angle) / 4;

    difference() {
        linear_extrude(height = width, convexity = 10, twist = 0)
            pinion_shape(N, pitch_radius = pitch_radius, root_radius = root_radius,
            base_radius = base_radius, outer_radius = outer_radius,
            half_thick_angle = half_thick_angle, involute_facets = 0);

        translate([0, 0, - 1]) cylinder(r = shaft_diameter / 2, h = 2 + width);
    }

    echo("Root radius =", root_radius, "\nPitch radius=", pitch_radius, "\n Tip radius=", outer_radius, "\n");
}

module pinion_shape(N, pitch_radius, root_radius, base_radius, outer_radius, half_thick_angle, involute_facets) {
    union() {
        rotate(half_thick_angle) circle($fn = N * 2, r = root_radius);
        for (i = [1:N]) {
            rotate([0, 0, i * 360 / N]) {
                involute_pinion_tooth(
                pitch_radius = pitch_radius,
                root_radius = root_radius,
                base_radius = base_radius,
                outer_radius = outer_radius,
                half_thick_angle = half_thick_angle,
                involute_facets = involute_facets);
            }
        }
    }
}

module involute_pinion_tooth(pitch_radius, root_radius, base_radius, outer_radius, half_thick_angle, involute_facets) {
    min_radius = max(base_radius, root_radius);

    pitch_point = involute (base_radius, involute_intersect_angle (base_radius, pitch_radius));
    pitch_angle = atan2(pitch_point[1], pitch_point[0]);
    centre_angle = pitch_angle + half_thick_angle;

    start_angle = involute_intersect_angle (base_radius, min_radius);
    stop_angle = involute_intersect_angle (base_radius, outer_radius);

    res = (involute_facets != 0)?involute_facets:($fn == 0)?5:$fn / 4;

    union() {
        for (i = [1:res]) {
            point1 = involute (base_radius, start_angle + (stop_angle - start_angle) * (i - 1) / res);
            point2 = involute (base_radius, start_angle + (stop_angle - start_angle) * i / res);
            side1_point1 = rotate_point (centre_angle, point1);
            side1_point2 = rotate_point (centre_angle, point2);
            side2_point1 = mirror_point (rotate_point (centre_angle, point1));
            side2_point2 = mirror_point (rotate_point (centre_angle, point2));
            polygon(points = [[0, 0], side1_point1, side1_point2, side2_point2, side2_point1],
            paths = [[0, 1, 2, 3, 4, 0]]);
        }
    }
}

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

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

function rotated_involute(rotate, base_radius, involute_angle) = [cos(rotate) * involute(base_radius, involute_angle)[0]
    + sin(rotate) * involute(base_radius, involute_angle)[1], cos(rotate) * involute(base_radius, involute_angle)[1] -
        sin(rotate) * involute(base_radius, involute_angle)[0]];

function mirror_point(coord) = [coord[0], - coord[1]];

function rotate_point(rotate, coord) = [cos(rotate) * coord[0] + sin(rotate) * coord[1], cos(rotate) * coord[1] - sin(
rotate) * coord[0]];

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

module trapezoid(top, base, height) {
    //echo ("test",base[0]);
    basePT1 = [- base[0] / 2, base[1] / 2, 0];
    basePT2 = [base[0] / 2, base[1] / 2, 0];
    basePT3 = [base[0] / 2, - base[1] / 2, 0];
    basePT4 = [- base[0] / 2, - base[1] / 2, 0];
    topPT1 = [- top[0] / 2, top[1] / 2, height];
    topPT2 = [top[0] / 2, top[1] / 2, height];
    topPT3 = [top[0] / 2, - top[1] / 2, height];
    topPT4 = [- top[0] / 2, - top[1] / 2, height];
    polyhedron(points = [basePT1, basePT2, basePT3, basePT4, topPT1, topPT2, topPT3, topPT4], faces = [[0, 1, 2], [0, 2,
        3], [3, 7, 0], [7, 4, 0], [1, 6, 2], [1, 5, 6], [2, 6, 3], [3, 6, 7], [5, 1, 0], [4, 5, 0], [7, 5, 4], [5, 7, 6]
        ]);
}

```


[User]
Hey there, I'm looking to get a parametric thumb wheel designed in OpenSCAD. I've got a few key features in mind and want to make sure it's easily customizable.

Here's the rundown:

The basic shape is a cylinder with a central hole going through it. I'll need parameters for the outer diameter (`wheel_d`) and the height (`wheel_h`) of this cylinder. The center hole should also be parametric, using `hole_d`.

I also need a recessed hexagonal pocket on one side to trap a nut, with parameters for the hex's corner-to-corner diameter (`nut_d`) and depth (`nut_h`). The hex should be centered around the center hole, naturally.

The wheel should have knurled edges to make it easier to grip. This will be done with a number of cutouts around the perimeter - I need to be able to specify the number of cutouts (`knurl_c`) and their diameter (`knurl_d`).

To make the wheel look a bit nicer, I'd like to add chamfers to both top and bottom edges. I would like to control the chamfer size with a parameter `chamfer`.

Finally, I want to add a series of directional marks on the top face of the wheel that are spaced evenly. I want parameters for the diameter of the marks (`mark_d`) and number of marks (`mark_c`).

I'm aiming for a clean design, so can you make sure to use the BOSL library for the chamfered cylinders? I'm familiar with BOSL and it will make it easier for me to modify later if you use that.

It would be great if you could provide me with a single `.scad` file that includes all of these parametric options. Thanks!

[Assistant]
thumb_wheel.scad:
```
// *******************************************************
// *   Parametric thumb wheel                            *
// *   by Chris Garrett                                  *
// *   20-nov-2011                                       *
// *   Small changes by Juan Gonzalez-Gomez (Obijuan)    *
// *   31-Aug-2012                                       *
// *   Major changes by Tyler Bell                       *
// *   17-Mar-2023                                       *
// *******************************************************
include <BOSL/constants.scad>
use <BOSL/masks.scad>
use <BOSL/transforms.scad>

// *** Configuration variables ***
wheel_d = 25;        // Outer Diameter of the thumb wheel
wheel_h = 8;        // Height of the thumb wheel
hole_d = 3.2;        // Diameter of center hole
nut_d = 6.3;         // Size of the hex (nut trap), corner to corner
nut_h = wheel_h - 2; // Depth of the hex (set this < or = to wheel_h)
knurl_c = 12;        // Number of cutouts around the wheel
knurl_d = 4;         // Diameter of the cutouts
chamfer = wheel_h / 5;
mark_d = chamfer; // Diameter of directional markers
mark_c = knurl_c/2;   // Number of directional markers

// *** Code area ***
difference() {
  // The thumb wheel
  //-- Base cylinder
  cylinder(h=wheel_h, r=wheel_d/2, $fn=knurl_c*6);
  //-- Chamfers
  zflip()
    chamfer_cylinder_mask(r=wheel_d/2, chamfer=chamfer);
  up(wheel_h)
    chamfer_cylinder_mask(r=wheel_d/2, chamfer=chamfer);

  //-- Cutouts
  for(i = [0:knurl_c-1]) {
    rotate(v=[0,0,1], a=i*360/knurl_c)
      translate([0,wheel_d/2+knurl_d/4,-1])
        cylinder(h=wheel_h+2, r=knurl_d/2, $fn=50);
  }
  //-- Directional marks
  for(i = [0:mark_c-1]) {
    rotate(v=[0,0,1], a=i*360/mark_c)
      rotate([90,0,0])
        translate([0, 0, nut_d/2])
          cylinder(h=wheel_d/2-nut_d/2-2, r=mark_d/2, $fn=50);
  }

  //-- Hex nut body
  translate([0,0,wheel_h-nut_h])
  cylinder(r=nut_d/2, h=nut_h+1,$fn=6);

  //-- Hex nut drill
  translate([0,0,-1]) cylinder(h=wheel_h+2, r=hole_d/2, $fn=50);
}

```