[User]
Hey there, I'm looking to create a customizable cartridge holder for my old video game cartridges. I've seen a few designs online, but none quite fit my needs, so I was hoping you could help me generate some OpenSCAD code for this.

Essentially, I need a holder that can store a variable number of cartridges, with the option to angle them for better display. I’d also like to control the wall thickness and spacing between cartridges. The design should be parametric so I can easily adapt it for different cartridge sizes (like Game Boy and Game Gear) and make it less wasteful on material where support isn't crucial.

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

**Core Features:**

*   **Number of Cartridges:** I need a parameter to specify how many cartridges the holder should accommodate.
*   **Cartridge Angle:** There should be an adjustable angle for the cartridges to lean back (or forward, or be straight!).
*   **Wall Thickness:** I need control over the outer wall thickness, as well as the base wall thickness (or height).
*   **Separator Width:** There should be a parameter to set the width of the separators between the cartridges.
*   **Holder Height:** A total height parameter.
*   **Cutout/Material Reduction:** I'd like an option to cut out some material between the slots, with a parameter to control how much is removed, and a separate crossbeam parameter to control crossbeam width. Also a parameter for the length at which partitions are introduced
*   **Cartridge Dimensions:** Parameters for the width and depth of the cartridges, so I can use different dimensions for GameBoy, GameGear, etc.

**Specifics:**

*   I need it to cut out the cartridge slots at the specified angle.
*   I want to have the material cut from the bottom as well, for a thinner overall look.
*   The holder should have some bottom ribs for rigidity, which can be controlled.

**Examples (as reference):**

For Nintendo Game Boy cartridges *with* protective hulls, I'd use:
    *   `cartridge_width = 65.0;`
    *   `cartridge_depth = 13.5;`

For Game Boy cartridges *without* the hulls:
    *   `cartridge_width = 57.5;`
    *   `cartridge_depth =  8.0;`

For SEGA Game Gear cartridges *with* protective hulls:
    *   `cartridge_width = 78.5;`
    *   `cartridge_depth = 15.5;`
    *   `holder_height = 20.0;`
    *   `outer_wall_width = 4;`

And for Game Gear cartridges *without* the hulls:
    *   `cartridge_width = 67.5;`
    *   `cartridge_depth =  8.5;`
    *   `separator_width =  3.0;`

I think the easiest way to specify the different options would be to provide comments so that I can simply uncomment a line and use it.

I'm hoping this is possible, I think it'd be very useful for organizing these old games. It would be great if the file could work in the Thingiverse customizer too. If you think this would be easier in multiple files I'm okay with that! I'm not using any libraries like BOSL or anything.

[Assistant]
universal_cart_holder.scad:
```
/*
 * Universal cartridge holder.
 *
 * (c)Tempura San 2020, Some Rights Reserved
 * This work is licensed under a Creative Commons Attribution 4.0 International License.
 */

// The parameters are prepared to work with the MakerBot Customizer.
// see http://customizer.makerbot.com/docs

/* [Common holder settings] */
// The angle of the individual cartridges in degrees.
cartridge_angle = 10; // [0:20]
// Number of cartridges to be fitted in the holder.
number_of_cartridges = 5;
// The outer/perimeter wall width. [mm]
outer_wall_width = 2;
// The bottom wall height. [mm]
bottom_wall_height = 2;
// The width of the separator between the cartridges. [mm]
separator_width = 2;
// The total height of the holder. [mm]
holder_height = 10;
// The ratio defining the cutout area in between the holder tabs (set to '0' for no cutout).
cutout_ratio = 0.75; // [0:0.05:0.75]
// The width of the crossbeam inserted in the cutout area. [mm]
crossbeam_width =  7;
// The minimum available length before a partition is divided. [mm]
minimum_crossbeam_length = 30;

/* [Cartridge dimensions] */
// The cartridge's width. [mm]
cartridge_width = 65.0;
// The cartridge's depth. [mm]
cartridge_depth = 13.5;

// Tested cart configurations
// uncomment the respective cartridge_width/cartridge_depth lines to enable

// Nintendo Game Boy - cartridge with protective hull
//cartridge_width = 65.0;
//cartridge_depth = 13.5;

// Nintendo Game Boy - cartridge only
//cartridge_width = 57.5;
//cartridge_depth =  8.0;

// SEGA Game Gear - cartridge with protective hull
//cartridge_width = 78.5;
//cartridge_depth = 15.5;
//holder_height = 20.0;
//outer_wall_width = 4;

// SEGA Game Gear - cartridge only
//cartridge_width = 67.5;
//cartridge_depth =  8.5;
//separator_width =  3.0;

union() {
    difference() {
        bcube_x = cartridge_width + 2*outer_wall_width;
        bcube_y = cartridge_depth * number_of_cartridges + separator_width * (number_of_cartridges-1) + 2*outer_wall_width;
        bcube_z = holder_height;

        // holder base cube
        cube([bcube_x, bcube_y, bcube_z]);

        // cartridge cutout
        for(i = [0 : number_of_cartridges-1])
            translate([outer_wall_width, outer_wall_width + i * (cartridge_depth + separator_width), bottom_wall_height])
                rotate([cartridge_angle, 0, 0])
                cube([cartridge_width, cartridge_depth, holder_height*2]); // use x2 to ensure the cut goes over the top

        // inner cutout
        translate([outer_wall_width + cartridge_width * (1-cutout_ratio) / 2, outer_wall_width, bottom_wall_height])
            cube([cartridge_width * cutout_ratio, number_of_cartridges * cartridge_depth + (number_of_cartridges-1) * separator_width, holder_height]);

        // bottom cutout
        // calculate bottom area (use 4x to inset by outer_wall_width)
        bott_x = bcube_x - 4*outer_wall_width - cartridge_width * (1-cutout_ratio);
        bott_y = bcube_y - 4*outer_wall_width;
        translate([2*outer_wall_width + cartridge_width * (1-cutout_ratio)/2, 2*outer_wall_width, 0])
            cube([bott_x, bott_y, bottom_wall_height]);
    }

    // bottom ribs to add some more rigidity and for cosmetics
    bott_x = cartridge_width*cutout_ratio - 2*outer_wall_width;
    bott_y = cartridge_depth*number_of_cartridges + separator_width*(number_of_cartridges-1) - 2*outer_wall_width;
    if(bott_y > minimum_crossbeam_length) {
        translate([outer_wall_width + cartridge_width/2 - crossbeam_width/2, 2*outer_wall_width,0])
            cube([crossbeam_width, bott_y, bottom_wall_height]);

        num_reps = ceil(bott_y / minimum_crossbeam_length);
        for(i = [1 : num_reps - 1]) {
            translate([2*outer_wall_width + cartridge_width*(1-cutout_ratio)/2, bott_y / num_reps * i - crossbeam_width/2 + 2*outer_wall_width, 0])
                cube([bott_x, crossbeam_width, bottom_wall_height]);
        }
    }
}

```


[User]
I'd like to design a hole plug, but I'm having trouble getting all the features I want in one design. I need something that can be customized, and I'm thinking OpenSCAD might be the best way.

The basic idea is a cylindrical plug with a lip. I'd like to be able to specify the main cylinder's diameter, how thick the lip should be (both radial and axial thickness), the length of the cylindrical part, and how much, if any, taper it should have. The lip should have a smooth transition from the cylinder and a chamfered edge, preferably.

I'm imagining two separate modules for this: one for the plug itself, and another for the chamfered lip. I'd like them to be separate, so the lip can be used with other shapes if needed.

Specifically for the plug, it'll have the main cylinder, a small lip that's thicker than the cylinder, and then the main cylinder part. I’d like to specify the diameter of the plug, the radial thickness of the lip, and the length of the plug part, the plug part’s axial thickness. I'd also like to be able to specify the amount of taper along the length of the cylindrical plug, expressed in tenths of a millimeter.

For the chamfered lip, I'd like it to be a cylinder, with an angled face on the inner and outer edges. I need to specify the overall diameter of the chamfered section, the diameter of the hole, and how axially thick it should be.

Finally, I want to be able to render this at reasonable quality, so I’d appreciate it if the code was parameterized with `$fa` and `$fs` for minimum angle and minimum size. I don’t need any special libraries, just standard OpenSCAD functionality.

Could you provide the OpenSCAD code for this?

[Assistant]
hole_plug.scad:
```
/* [Hole Plug] */
// Cylinder diameter (mm)
diameter=32;

// Lip thickness (mm)
lip = 8; // [5:15]

// Cylinder thickness (mm)
thickness=1; // [1:3]

// Cylinder length (mm)
length=25;

// Cylinder tapering (tenth of mm)
taper=0; // [0:10]

/* [Rendering] */

// Minimum angle
$fa = 4;

// Minimum size
$fs = 1;

module HoleCap(d=30, lip=10, thick=1, depth=20)
{
    bottom_diameter = d + lip*2;

    cylinder(h=1, d=bottom_diameter);

    translate([0, 0, 1 - 0.1])
        cylinder(
            h=1.6, d1=bottom_diameter,
            d2=bottom_diameter - thick * 2 * 1.5);

    translate([0, 0, 2.5 - 0.1])
        difference()
        {
            cylinder(h=depth + 0.1, d1=d, d2=d-taper/10);
            translate([0, 0, -1])
                cylinder(h=depth+2, d1=d-thick * 2, d2=d-taper/10-thick*2);
        }
}

HoleCap(d = diameter, lip = lip, thick = thickness, depth = length);
```
hole_plug_hollow.scad:
```
// Hole diameter (mm)
hole_diameter = 20;

// Lip size (mm)
lip = 10;

// Depth size (mm)
depth = 10;

// Thickness (mm)
thickness = 1;

// Top tickness (mm)
top_tickness = 2;

// Taper (tenth of mm)
taper_size = 0; // [0:10]
taper = taper_size / 10;

/* [Rendering] */

// Minimum angle
$fa = 2;

// Minimum size
$fs = 0.2;

module ChamferedCyl(d=30, hole=10, h=2)
{
    difference()
    {
        union()
        {
            cylinder(h=h, d=d - h*2);

            intersection()
            {
                cylinder(h=h, d=d);

                rotate_extrude(angle=360)
                translate([d/2 - h,0,0])
                circle(d=h*2);
            }
        }

        translate([0, 0, -0.5])
        cylinder(h=h+1, d=hole + h*2 - 0.001);
    }

    intersection()
    {
        cylinder(h=h, d=hole + h*2);

        rotate_extrude(angle=360)
        translate([hole/2 + h,0,0])
        circle(d=h*2);
    }
}

module Pipe(d1=20, d2=25, h=20, thick=1)
{
    difference()
    {
        cylinder(d1=d1, d2=d2, h=h+0.001);

        translate([0, 0, -0.2])
        cylinder(d1=d1 - thick*2, d2=d2-thickness*2, h=h+0.4);
    }
}

Pipe(d1=hole_diameter - taper, d2=hole_diameter, h=depth + 0.001, thick=thickness);

translate([0, 0, depth])
ChamferedCyl(d=hole_diameter+lip*2, hole=hole_diameter-thickness*2, h=top_tickness);

```


[User]
I'm looking to create a keyguard for a Chromebook keyboard to help with accessibility. I have some specific measurements for my Chromebook, and I'd like to be able to customize the design easily using parameters. I've seen a design that seems close to what I want, and I'd like to be able to create something similar using OpenSCAD.

The basic idea is a raised perimeter around the keyboard, with optional tabs for securing it to the Chromebook. I'd also like the option to include dividers between the rows of keys, and the option to add key separators within those rows too. The design should be parametric so I can easily change the dimensions to fit different keyboards.

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

**Keyguard Perimeter:**

*   `keyboard_width`: The width of the keyboard area.
*   `keyboard_height`: The height of the keyboard area.
*   `key_depth`: How deep the keys sit below the keyguard surface.
*   `keyguard_rise`: The height the keyguard perimeter rises above the keyboard surface.
*   `perimeter_width`: The thickness of the keyguard perimeter walls.
*   `left_side_gap`:  A gap between the left side of the keyboard and the perimeter.
*  `keyboard_corner_radius`:  The radius of the corners of the main keyguard rectangle.
*   `angled_perimeter`: A boolean toggle for an angled keyguard wall or an even wall.

**Tabs:**

*   `upper_tab_width`: The width of the tabs on the upper edge.
*   `upper_tab_depth`: The depth the tabs extend beyond the keyguard.
*   `upper_tab_inset`: How far inset the upper tabs are from the left and right edges.
*   `upper_tab_thickness`: The thickness of the upper tab, which should default to match the keyguard rise but be customizable.
*   `lower_tab_width`: The width of the tabs on the lower edge.
*  `lower_tab_depth`: The depth the tabs extend beyond the keyguard.
*   `lower_tab_inset`: How far inset the lower tabs are from the left and right edges.
*   `lower_tab_thickness`: The thickness of the lower tab, which should default to match the keyguard rise but be customizable.
*   `tab_corner_radius`: The radius of the corners on the tabs.

**Separators:**

*   `row_separator_width`: The thickness of the horizontal separators between rows.
*   `key_separator_width`: The thickness of the vertical separators between keys.

**Row Separators:**

*   `show_row_separators`: A boolean toggle to show or hide the horizontal row separators.
*   `ctrl_row_height`: The vertical height of the control key row.
*   `shift_row_height`: The vertical height of the shift key row.
*   `search_row_height`: The vertical height of the search key row.
*   `tab_row_height`: The vertical height of the tab key row.
*   `number_row_height`: The vertical height of the number key row.

**Key Separators within Rows (each with a boolean toggle):**

*   `show_ctrl_row_key_separators` and control-row specific key locations (e.g. `ctrl_alt_separator`, `alt_spacebar_separator` etc.)
*   `show_shift_row_key_separators` and shift-row specific key locations (e.g. `shift_z_separator`, `z_x_separator` etc.)
*   `show_search_row_key_separators` and search-row specific key locations (e.g. `search_a_separator`, `a_s_separator` etc.)
*   `show_tab_row_key_separators` and tab-row specific key locations (e.g. `tab_q_separator`, `q_w_separator` etc.)
*   `show_number_row_key_separators` and number-row specific key locations (e.g. `tilde_1_separator`, `1_2_separator` etc.)
*  `show_esc_row_key_separators` and esc-row specific key locations (e.g. `esc_previous_separator`, `previous_next_separator` etc.)

**Arrow Key Mask:**

*   `show_arrow_key_mask`: Boolean toggle to show a mask around the arrow keys.
*   `arrow_key_mask_height`: The height of the mask.

I think that's everything. I'm not sure what libraries would be most helpful for this, but any helper functions that would make it easier to get the final result would be great.

[Assistant]
chromebook_keyguard.scad:
```
// Written by Volksswitch <www.volksswitch.org>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to the
// public domain worldwide. This software is distributed without any
// warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//------------------------------------------------------------------
// User Inputs
//------------------------------------------------------------------

/*[Keyguard Perimeter]*/
keyboard_width="272";
keyboard_height="103";
key_depth="1.8";
keyguard_rise="0.5";
perimeter_width="2";
left_side_gap="0";
keyboard_corner_radius="2";
angled_perimeter = "yes"; //[yes,no]

/*[Tabs]*/
upper_tab_width="20";
upper_tab_depth="10";
upper_tab_inset="5";
upper_tab_thickness="0";
lower_tab_width="60";
lower_tab_depth="10";
lower_tab_inset="10";
lower_tab_thickness="0";
tab_corner_radius="3";

/*[Separator Sizes]*/
row_separator_width="2";
key_separator_width="2";

/*[Row Separators]*/
show_row_separators="no"; //[yes,no]
ctrl_row_height="20";
shift_row_height="38";
search_row_height="56";
tab_row_height="74";
number_row_height="92";

/*[ctrl row key separators]*/
show_ctrl_row_key_separators="no"; //[yes,no]
ctrl_alt_separator="38";
alt_spacebar_separator="78";
spacebar_alt_separator="173";
alt_ctrl_separator="192";
ctrl_left_arrow_separator="211";
left_arrow_up_down_arrows_separator="232";
up_down_arrows_right_arrow_separator="253";

/*[shift row key separators]*/
show_shift_row_key_separators="no"; //[yes,no]
shift_z_separator="40";
z_x_separator="59";
x_c_separator="78";
c_v_separator="97";
v_b_separator="116";
b_n_separator="135";
n_m_separator="154";
m_comma_separator="173";
comma_period_separator_separator="192";
period_question_mark_separator="211";
question_mark_shift_separator="230";

/*[search row key separators]*/
show_search_row_key_separators="no"; //[yes,no]
search_a_separator="30";
a_s_separator="49";
s_d_separator="68";
d_f_separator="87";
f_g_separator="107";
g_h_separator="125";
h_j_separator="144";
j_k_separator="163";
k_l_separator="182";
l_colon_separator="201";
colon_quote_separator="220";
quote_enter_separator="239";

/*[tab row key separators]*/
show_tab_row_key_separators="no"; //[yes,no]
tab_q_separator="27";
q_w_separator="45";
w_e_separator="65";
e_r_separator="83";
r_t_separator="102";
t_y_separator="122";
y_u_separator="141";
u_i_separator="160";
i_o_separator="178";
o_p_separator="198";
p_left_bracket_separator="217";
left_bracket_right_bracket_separator="235";
right_bracket_slash_separator="255";

/*[number row key separators]*/
show_number_row_key_separators="no"; //[yes,no]
tilde_1_separator="17";
1_2_separator="36";
2_3_separator="55";
3_4_separator="74";
4_5_separator="93";
5_6_separator="112";
6_7_separator="131";
7_8_separator="150";
8_9_separator="169";
9_0_separator="188";
0_hyphen_separator="207";
hyphen_plus_separator="226";
plus_backspace_separator="245";

/*[esc row key separators]*/
show_esc_row_key_separators="no"; //[yes,no]
esc_previous_separator="21";
previous_next_separator="44";
next_reload_separator="67";
reload_full_screen_separator="90";
full_screen_next_tab_separator="113";
next_tab_fade_separator="136";
fade_brighter_separator="159";
brighter_mute_separator="182";
mute_quieter_separator="205";
quieter_louder_separator="228";
louder_power_separator="251";

/*[Arrow Keys]*/
show_arrow_key_mask="no"; //[yes,no]
arrow_key_mask_height="10";

/*[Hidden]*/
//convert to floating point numbers
keyboard_width_float = atof(keyboard_width);
keyboard_height_float = atof(keyboard_height);
key_depth_float = atof(key_depth);
keyguard_rise_float = atof(keyguard_rise);
perimeter_width_float = atof(perimeter_width);
left_side_gap_float = atof(left_side_gap);
keyboard_corner_radius_float = atof(keyboard_corner_radius);

upper_tab_width_float = atof(upper_tab_width);
upper_tab_depth_float = atof(upper_tab_depth);
upper_tab_inset_float = atof(upper_tab_inset);
upper_tab_thickness_float = atof(upper_tab_thickness);
lower_tab_width_float = atof(lower_tab_width);
lower_tab_depth_float = atof(lower_tab_depth);
lower_tab_inset_float = atof(lower_tab_inset);
lower_tab_thickness_float = atof(lower_tab_thickness);

u_tab_thickness = (upper_tab_thickness_float != 0) ? min(key_depth_float+keyguard_rise_float,upper_tab_thickness_float) : keyguard_rise_float;
l_tab_thickness = (lower_tab_thickness_float != 0) ? min(key_depth_float+keyguard_rise_float,lower_tab_thickness_float) : keyguard_rise_float;

tab_corner_radius_float = atof(tab_corner_radius);

row_separator_thickness_float = atof(row_separator_width);
key_separator_width_float = atof(key_separator_width);

ctrl_row_height_float = atof(ctrl_row_height);
shift_row_height_float = atof(shift_row_height);
search_row_height_float = atof(search_row_height);
tab_row_height_float = atof(tab_row_height);
number_row_height_float = atof(number_row_height);

s1 = [[atof(ctrl_alt_separator)],[atof(alt_spacebar_separator)],[atof(spacebar_alt_separator)],[atof(alt_ctrl_separator)],[atof(ctrl_left_arrow_separator)],[atof(left_arrow_up_down_arrows_separator)],[atof(up_down_arrows_right_arrow_separator)]];

s2 = [[atof(shift_z_separator)],[atof(z_x_separator)],[atof(x_c_separator)],[atof(c_v_separator)],[atof(v_b_separator)],[atof(b_n_separator)],[atof(n_m_separator)],[atof(m_comma_separator)],[atof(comma_period_separator_separator)],[atof(period_question_mark_separator)],[atof(question_mark_shift_separator)]];

s3 = [[atof(search_a_separator)],[atof(a_s_separator)],[atof(s_d_separator)],[atof(d_f_separator)],[atof(f_g_separator)],[atof(g_h_separator)],[atof(h_j_separator)],[atof(j_k_separator)],[atof(k_l_separator)],[atof(l_colon_separator)],[atof(colon_quote_separator)],[atof(quote_enter_separator)]];

s4 = [[atof(tab_q_separator)],[atof(q_w_separator)],[atof(w_e_separator)],[atof(e_r_separator)],[atof(r_t_separator)],[atof(t_y_separator)],[atof(y_u_separator)],[atof(u_i_separator)],[atof(i_o_separator)],[atof(o_p_separator)],[atof(p_left_bracket_separator)],[atof(left_bracket_right_bracket_separator)],[atof(right_bracket_slash_separator)]];

s5 = [[atof(tilde_1_separator)],[atof(1_2_separator)],[atof(2_3_separator)],[atof(3_4_separator)],[atof(4_5_separator)],[atof(5_6_separator)],[atof(6_7_separator)],[atof(7_8_separator)],[atof(8_9_separator)],[atof(9_0_separator)],[atof(0_hyphen_separator)],[atof(hyphen_plus_separator)],[atof(plus_backspace_separator)]];

s6 = [[atof(esc_previous_separator)],[atof(previous_next_separator)],[atof(next_reload_separator)],[atof(reload_full_screen_separator)],[atof(full_screen_next_tab_separator)],[atof(next_tab_fade_separator)],[atof(fade_brighter_separator)],[atof(brighter_mute_separator)],[atof(mute_quieter_separator)],[atof(quieter_louder_separator)],[atof(louder_power_separator)]];

separator_thickness = key_depth_float+keyguard_rise_float;

arrow_key_mask_height_float = atof(arrow_key_mask_height);

$fn=40;
fudge=0.005;

// ----------------------Main-----------------------------
//construct keyguard perimeter
translate([keyboard_width_float/2,keyboard_height_float/2,0])
difference(){
	hull(){
		translate([0,0,0])
		linear_extrude(height=keyguard_rise_float)
		offset(delta=perimeter_width_float)
		offset(r=keyboard_corner_radius_float)
		square([keyboard_width_float-keyboard_corner_radius_float*2, keyboard_height_float-keyboard_corner_radius_float*2],center=true);

		if (angled_perimeter=="yes"){
			translate([0,0,keyguard_rise_float])
			linear_extrude(height=key_depth_float)
			offset(r=keyboard_corner_radius_float)
			square([keyboard_width_float-keyboard_corner_radius_float*2, keyboard_height_float-keyboard_corner_radius_float*2],center=true);
		}
		else{
			translate([0,0,keyguard_rise_float])
			linear_extrude(height=key_depth_float)
			offset(delta=perimeter_width_float)
			offset(r=keyboard_corner_radius_float)
			square([keyboard_width_float-keyboard_corner_radius_float*2, keyboard_height_float-keyboard_corner_radius_float*2],center=true);
		}
	}

	translate([0,0,-fudge])
	linear_extrude(height=key_depth_float+keyguard_rise_float+fudge*2)
	offset(r=keyboard_corner_radius_float)
	square([keyboard_width_float-keyboard_corner_radius_float*2, keyboard_height_float-keyboard_corner_radius_float*2],center=true);
}

//add tabs
//add upper tabs
echo(upper_tab_thickness=upper_tab_thickness);
// translate([upper_tab_inset_float+upper_tab_width_float/2-perimeter_width_float,
		// keyboard_height_float+perimeter_width_float,0])
translate([upper_tab_inset_float+upper_tab_width_float/2-perimeter_width_float,keyboard_height_float,0])
linear_extrude(height=u_tab_thickness)
add_tab("top",upper_tab_width_float,upper_tab_depth_float+perimeter_width_float,tab_corner_radius_float);

translate([keyboard_width_float+perimeter_width_float-upper_tab_inset_float-upper_tab_width_float/2,
		keyboard_height_float,0])
linear_extrude(height=u_tab_thickness)
add_tab("top",upper_tab_width_float,upper_tab_depth_float+perimeter_width_float,tab_corner_radius_float);

//add lower tabs
translate([lower_tab_inset_float+lower_tab_width_float/2,fudge,0])
linear_extrude(height=l_tab_thickness)
add_tab("bottom",lower_tab_width_float,lower_tab_depth_float+perimeter_width_float,tab_corner_radius_float);

translate([keyboard_width_float+perimeter_width_float-lower_tab_inset_float-lower_tab_width_float/2,fudge,0])
linear_extrude(height=l_tab_thickness)
add_tab("bottom",lower_tab_width_float,lower_tab_depth_float+perimeter_width_float,tab_corner_radius_float);

//add horizontal row separators
if (show_row_separators=="yes"){
	translate([0,ctrl_row_height_float-row_separator_thickness_float/2,0])
	cube([keyboard_width_float,row_separator_thickness_float,separator_thickness]);

	translate([0,shift_row_height_float-row_separator_thickness_float/2,0])
	cube([keyboard_width_float,row_separator_thickness_float,separator_thickness]);

	translate([0,search_row_height_float-row_separator_thickness_float/2,0])
	cube([keyboard_width_float,row_separator_thickness_float,separator_thickness]);

	translate([0,tab_row_height_float-row_separator_thickness_float/2,0])
	cube([keyboard_width_float,row_separator_thickness_float,separator_thickness]);

	translate([0,number_row_height_float-row_separator_thickness_float/2,0])
	cube([keyboard_width_float,row_separator_thickness_float,separator_thickness]);

}

//add key separators

if (show_row_separators=="yes" && show_ctrl_row_key_separators=="yes"){
	for(i=[0:len(s1)-1]){
		horiz_dist=keyboard_width_float-s1[i][0]-key_separator_width_float/2-left_side_gap_float;
		previous_row_height=0;
		seperator_length=ctrl_row_height_float-previous_row_height;
		translate([horiz_dist,previous_row_height,0])
		cube([key_separator_width_float,seperator_length,separator_thickness]);
	}
}

if (show_row_separators=="yes" && show_shift_row_key_separators=="yes"){
	for(i=[0:len(s2)-1]){
		horiz_dist=keyboard_width_float-s2[i][0]-key_separator_width_float/2-left_side_gap_float;
		previous_row_height=ctrl_row_height_float;
		seperator_length=shift_row_height_float-previous_row_height;
		translate([horiz_dist,previous_row_height,0])
		cube([key_separator_width_float,seperator_length,separator_thickness]);
	}
}

if (show_row_separators=="yes" && show_search_row_key_separators=="yes"){
	for(i=[0:len(s3)-1]){
		horiz_dist=keyboard_width_float-s3[i][0]-key_separator_width_float/2-left_side_gap_float;
		previous_row_height=shift_row_height_float;
		seperator_length=search_row_height_float-previous_row_height;
		translate([horiz_dist,previous_row_height,0])
		cube([key_separator_width_float,seperator_length,separator_thickness]);
	}
}

if (show_row_separators=="yes" && show_tab_row_key_separators=="yes"){
	for(i=[0:len(s4)-1]){
		horiz_dist=keyboard_width_float-s4[i][0]-key_separator_width_float/2-left_side_gap_float;
		previous_row_height=search_row_height_float;
		seperator_length=tab_row_height_float-previous_row_height;
		translate([horiz_dist,previous_row_height,0])
		cube([key_separator_width_float,seperator_length,separator_thickness]);
	}
}

if (show_row_separators=="yes" && show_number_row_key_separators=="yes"){
	for(i=[0:len(s5)-1]){
		horiz_dist=keyboard_width_float-s5[i][0]-key_separator_width_float/2-left_side_gap_float;
		previous_row_height=tab_row_height_float;
		seperator_length=number_row_height_float-previous_row_height;
		translate([horiz_dist,previous_row_height,0])
		cube([key_separator_width_float,seperator_length,separator_thickness]);
	}
}

if (show_row_separators=="yes" && show_esc_row_key_separators=="yes"){
	for(i=[0:len(s6)-1]){
		horiz_dist=keyboard_width_float-s6[i][0]-key_separator_width_float/2-left_side_gap_float;
		previous_row_height=number_row_height_float;
		seperator_length=keyboard_height_float-previous_row_height;
		translate([horiz_dist,previous_row_height,0])
		cube([key_separator_width_float,seperator_length,separator_thickness]);
	}
}

if (show_row_separators=="yes" && show_arrow_key_mask=="yes"){
	edge1 = atof(up_down_arrows_right_arrow_separator)+left_side_gap_float;
	edge2 = atof(left_arrow_up_down_arrows_separator)+left_side_gap_float;
	edge3 = atof(ctrl_left_arrow_separator)+left_side_gap_float;

	mask1_width = keyboard_width_float - edge1;
	translate([keyboard_width_float-keyboard_width_float,ctrl_row_height_float-arrow_key_mask_height_float,0])
	cube([mask1_width,arrow_key_mask_height_float,separator_thickness]);

	mask2_width = edge2 - edge3;
	translate([keyboard_width_float-edge2,ctrl_row_height_float-arrow_key_mask_height_float,0])
	cube([mask2_width,arrow_key_mask_height_float,separator_thickness]);
}

// ----------------------End of Main-----------------------------

module add_tab(type, tab_width, tab_height, corner_radius){
		if (type=="top"){
			translate([0,-fudge])
			if (tab_width > 0 && tab_height > 0 && corner_radius > 0){
				translate([-tab_width/2+corner_radius,0])
				square([tab_width-corner_radius*2,tab_height]);
				translate([-tab_width/2,0])
				square([tab_width,tab_height-corner_radius]);
				translate([-tab_width/2+corner_radius,tab_height-corner_radius])
				circle(r=corner_radius);
				translate([tab_width/2-corner_radius,tab_height-corner_radius])
				circle(r=corner_radius);
			}
		}
		else if (type=="bottom"){
			translate([0,fudge])
			if (tab_width > 0 && tab_height > 0 && corner_radius > 0){
				translate([-tab_width/2+corner_radius,-tab_height])
				square([tab_width-corner_radius*2,tab_height]);
				translate([-tab_width/2,-tab_height+corner_radius])
				square([tab_width,tab_height-corner_radius]);
				translate([-tab_width/2+corner_radius,-tab_height+corner_radius])
				circle(r=corner_radius);
				translate([tab_width/2-corner_radius,-tab_height+corner_radius])
				circle(r=corner_radius);
			}
		}
}

// Jesse Campbell
// www.jbcse.com
// OpenSCAD ascii string to number conversion function atof
// scientific notation support added by Alexander Pruss (thingiverse user arpruss)
// atoi and substr are from http://www.thingiverse.com/roipoussiere

function atoi(str, base=10, i=0, nb=0) =
	i == len(str) ? (str[0] == "-" ? -nb : nb) :
	i == 0 && str[0] == "-" ? atoi(str, base, 1) :
	atoi(str, base, i + 1,
		nb + search(str[i], "0123456789ABCDEF")[0] * pow(base, len(str) - i - 1));

function substr(str, pos=0, len=-1, substr="") =
	len == 0 ? substr :
	len == -1 ? substr(str, pos, len(str)-pos, substr) :
	substr(str, pos+1, len-1, str(substr, str[pos]));

function atof(str) = len(str) == 0 ? 0 : let( expon1 = search("e", str), expon = len(expon1) ? expon1 : search("E", str)) len(expon) ? atof(substr(str,pos=0,len=expon[0])) * pow(10, atoi(substr(str,pos=expon[0]+1))) : let( multiplyBy = (str[0] == "-") ? -1 : 1, str = (str[0] == "-" || str[0] == "+") ? substr(str, 1, len(str)-1) : str, decimal = search(".", str), beforeDecimal = decimal == [] ? str : substr(str, 0, decimal[0]), afterDecimal = decimal == [] ? "0" : substr(str, decimal[0]+1) ) (multiplyBy * (atoi(beforeDecimal) + atoi(afterDecimal)/pow(10,len(afterDecimal))));

```


[User]
I want to create a set of parametric cable runners for 3D printing. The basic idea is square tubes with different shapes: straight, T-junction, and 90-degree curves. I need the OpenSCAD code to be fully parametric, so I can easily adjust the dimensions and number of each type of runner.

Specifically, I need the following:

*   **Basic tube:** A square tube with a cut along one side. This cut allows cables to be inserted or removed and also accommodates a screw for mounting. This should be a module called `makeTube(length)`.
*   **T-tube:** A T-shaped tube using the basic tube module. The T-section should also have a cut along one side to be consistent with the straight tubes. The module should be called `tube_t(length)`.
*   **Curved tube:** A 90-degree curved tube, essentially two straight tubes connected by a curved section, with a cut on the bottom of each straight section and on the outer edge of the curve.  There should be an option for the cut to be on the *inner* side of the curve instead of the bottom. This is important for different routing needs. The module should be called `tube_90(length, cutLeft)`.
*   **Build plate layout:** I need to be able to specify how many of each of these types of tubes I want to generate. They should be laid out in rows with an offset between the rows for each set of tubes so they don't overlap.
*   **Rounding:** I'd like an option to add a slight rounding to the edges of all the tubes to improve their aesthetics. This is to be controlled with a boolean variable and uses a minkowski operation. This should be wrapped in a function called `applyRounded()`.

Here's a breakdown of the parameters I need:

*   `internal_space`: The size of the internal square space inside the tube.
*   `thickness`: The thickness of the tube walls.
*   `length`: The base length for the straight sections.
*   `rounded`: A boolean to enable edge rounding.
*  `line_tubes`: The number of straight tubes.
*  `t_tubes`: The number of T-tubes.
*  `curved_tubes`: The number of curved tubes with standard cut.
*  `curved_tubes_with_left_cut`: The number of curved tubes with the inner cut.
*   `$fn`: For curvature, the number of segments (higher number means smoother).

The output should be an OpenSCAD file that generates the desired number of each type of tube in a row. I don't need any special library, just the standard OpenSCAD modules.

[Assistant]
Tubos.scad:
```
/* [Shape] */
// Internal square space for each tube
internal_space = 15;
// Perimeter thickness for each tube
thickness  = 0.9;
// Tube length, halved for curved tubes
length = 50;
// Increase thickness by 0.5mm and is slow
rounded = false;

/* [Build plate] */
line_tubes = 1; // [0,1,2,3,4]
t_tubes = 1; // [0,1,2,3,4]
curved_tubes = 1; // [0,1,2,3,4]
curved_tubes_with_left_cut = 1; // [0,1,2,3,4]

/* [Rendering] */
// The number of faces to draw for the curved shape
$fn = 120; // [30, 60, 90, 120]

/* [Hidden] */
cutw = internal_space/2;

// makeTube creates a simple tube using the specified length
// and global thickness.
module makeTube(length = 50) {
    w = internal_space + 2*thickness;
    difference() {
        cube([w, length, w]);
        translate([thickness, -1, thickness])
            cube([internal_space, length+2, internal_space]);
    }
}

// tube creates a tube shape with the specified length and
module tube(length=50, cut=true) {
    if (cut) {
        difference() {
            makeTube(length);
            // Corte traseiro para passar fios
            translate([cutw/2+thickness, -1, -1])
                cube([cutw, length+2, 2*thickness+2]);
        }
    } else {
        makeTube(length);
    }
}

// tube_t makes a T shaped tube
module tube_t(length = 50) {
    y = length/2 - internal_space/2;
    l = cutw;
    difference() {
        tube(length=length);
        translate([internal_space/2, y, -1])
            cube([internal_space, internal_space, internal_space+thickness+1]);
    }
    translate([internal_space + cutw + thickness*2, y-thickness, 0])
        rotate([0, 0, 90])
        tube(length=l);

    innerCorner = (internal_space - cutw)/2;
    translate([internal_space-cutw/2+thickness, length/2-innerCorner*2, 0])
        cube([innerCorner+thickness, innerCorner, thickness]);
    translate([internal_space-cutw/2+thickness, length/2+innerCorner, 0])
        cube([innerCorner+thickness, innerCorner, thickness]);
}

// tube_90 makes a curved tube with 90 degrees rotation. length is halved
// and cutLeft, if specified, switches the cut from bottom to the inner of the
// curve.
module tube_90(length = 50, cutLeft=false) {
    c1 = length/2;
    difference() {
        $v = cutLeft ? [length+thickness, length/2+thickness+2, cutw] : [0, 0, 0];
        union() {
            // tubes laterais
            tube(length=c1, cut=!cutLeft);
            translate([c1+internal_space+thickness, c1-thickness, 0])
                rotate([0, 0, 90])
                tube(length=c1, cut=!cutLeft);

            // Cantoneira
            difference() {
                translate([internal_space+thickness*2, c1-thickness, 0])
                    rotate([0, 0, 90])
                    rotate_extrude(angle=360)
                    difference() {
                        square(internal_space + 2*thickness);
                        translate([thickness, thickness, 0]) square(internal_space);
                        translate([cutw/2 + thickness, 0, 0]) square(cutw);
                    }
                translate([0,0,-1])
                    cube([length, c1, internal_space+thickness*2+2]);
                translate([internal_space+thickness*2,0,-1])
                    cube([length, length, internal_space+thickness*2+2]);
            }
        }
        translate([thickness*2, -1, cutw/2+thickness])
            cube($v);
    }
}

module applyRounded() {
    if (rounded) {
        minkowski() {
            children();
            sphere(0.25, $fn=6);
        }
    } else {
        children();
    }
}

// Draw all shapes on build plate

offSet = thickness*2 + internal_space + 10;

if(line_tubes > 0) {
    for (i=[0:line_tubes-1]) {
        color("white")
            translate([i*offSet, 0, 0])
            applyRounded()
            tube(length = length);
    }
}

if(t_tubes > 0) {
    for (i=[0:t_tubes-1]) {
        color("white")
            translate([i*offSet, length+20, 0])
            applyRounded()
            tube_t(length = length);
    }
}

if(curved_tubes > 0) {
    for(i=[0:curved_tubes-1]) {
        $x = line_tubes*offSet + 10 + 2 * i * offSet;
        color("white")
            translate([$x, 0, 0])
            applyRounded()
            tube_90(length=length);
    }
}

if(curved_tubes_with_left_cut>0) {
    for(i=[0:curved_tubes_with_left_cut-1]) {
        $x = t_tubes*offSet + 10 + 2 * i * offSet;
        color("white")
            translate([$x, length+20, 0])
            applyRounded()
            tube_90(length=length, cutLeft = true);
    }
}
```


[User]
Okay, I'm looking to create a customizable plant pot label, and I'd like to be able to adjust various aspects through parameters. I'd like it to have two lines of text, a main body with rounded corners and a recessed area for the text, and a stem to stick it into the soil.

Here's a breakdown of what I need:

**General Parameters:**

*   `topLine`: String - Text for the top line.
*   `secondLine`: String - Text for the second line.
*   `font`: String - Font to use for the text. (Let's go with the default font for now, and I can specify it later if required)
*   `height`: Number - Overall height of the main label body.
*   `width`: Number - Overall width of the main label body.
*   `thickness`: Number - Overall thickness of the label.
*   `inset`: Number - Depth of the recess for the text.
*  `radius`: Number - Radius of the rounded corners of the main body.
*   `lip`: Number - Width of the lip (the flat part around the recess).
*   `stemLength`: Number - Length of the stem.
*   `stemWidth`: Number - Width of the stem.
*   `stemThickness`: Number - Thickness of the stem.
*   `$fn`: Number - This should control the roundness/smoothness of the curves.

**Design Details:**

1.  **Main Body:**
    *   The main body should be a rectangle with rounded corners based on the `radius` parameter.
    *   It should have a recessed area inside it, created by offsetting the rounded rectangle inwards by half of `lip`. This creates a lip/border.
    *   The recess should have a depth of `inset`.
    *   The `thickness` defines the overall thickness of the main body.
2.  **Text:**
    *   The two lines of text, specified by `topLine` and `secondLine`, should be centered within the recessed area.
    *   They should be extruded by a depth equal to the `inset` parameter.
    *   The top line should be slightly above the vertical center, and the second line slightly below.
3.  **Stem:**
    *   The stem should be attached to the bottom-center of the main label.
    *   It should be a rectangular cuboid of the defined `stemLength`, `stemWidth`, and `stemThickness`.
    *   I would also like the stem to have a little angled barb at the bottom to help it stay in the soil. This barb can be a small cube rotated by 45 degrees, attached to the bottom of the main stem.

I think it would be best to organize this into a single .scad file with all the parameters at the top, and all the shapes being combined in the main module. No extra libraries should be necessary for this basic design.

Could you generate the OpenSCAD code for this?

[Assistant]
plant2.scad:
```
// @potatoad  _     _
//           /o\---/o\
//         ,'    |    `.
//        |      |      |
//        <`-----------'>
//       / `. `-----' ,' \
//      /    `-------'    \
//     :  |   _______   |  :
//     |  `.,'       `.,'  |
//    ,`.   \    o    /   ,'.
//   /   `.  `.     ,'  ,'   \
// ^^^^----^^^^-----^^^^----^^^^

//Top line of text
topLine = "Snake Plant";
//Bottom line of text
secondLine = "Dracaena trifasciata";

//Font
font = "";

//Height of label
height = 40;
//Width of label
width = 120;
//Overall thickness of label
thickness = 3;
// Depth of recess
inset = 2;
//Roundness of corners
radius = 10;
$fn = 30;
//Width of lip
lip = 6;

//Length of stem
stemLength = 50;
//Width of stem
stemWidth = 10;
//Thickness of the stem
stemThickness = 2;

//Main body
translate([radius,radius]){
    union(){
        translate([0,0,thickness-inset]){
            linear_extrude(inset)
            difference(){
                roundedRec();
                offset(r=-lip/2){
                    roundedRec();
                }
            }
        }
        linear_extrude(thickness-inset)
            roundedRec();
    }
}

//Text and stem
translate([width/2,height/2]){
    //Top line of text
    translate([0,height/7.5,thickness-inset])
    linear_extrude(inset)
        text(topLine, height/3, font, halign="center", valign="center");

    //Second line of text
    translate([0,height/-4,thickness-inset])
        linear_extrude(inset)
            text(secondLine, height/6, font, halign="center", valign="center");
    //Stem
    translate([0,(-stemLength/2)-height/2, stemThickness/2])
        union(){
            cube([stemWidth,stemLength,stemThickness],true);
            translate([0,(-stemLength/2)])
                rotate([0,0,45])
                    cube([stemWidth/sqrt(2),stemWidth/sqrt(2),stemThickness],true);
    }
}

//Create a rounded rectangle with the radii of $radius
module roundedRec(){
    minkowski(){
        square([width-(2*radius),height-(2*radius)]);
        circle(r=radius);
    }
}
```
plant3.scad:
```
// @potatoad  _     _
//           /o\---/o\
//         ,'    |    `.
//        |      |      |
//        <`-----------'>
//       / `. `-----' ,' \
//      /    `-------'    \
//     :  |   _______   |  :
//     |  `.,'       `.,'  |
//    ,`.   \    o    /   ,'.
//   /   `.  `.     ,'  ,'   \
// ^^^^----^^^^-----^^^^----^^^^

//Top line of text
topLine = "Snake Plant";
//Bottom line of text
secondLine = "Dracaena trifasciata";

//Font
font = "";

//Height of label
height = 40;
//Width of label
width = 120;
//Overall thickness of label
thickness = 3;
// Depth of recess
inset = 2;
//Roundness of corners
radius = 10;
$fn = 30;
//Width of lip
lip = 10;

//Length of stem
stemLength = 50;
//Width of stem
stemWidth = 10;
//Thickness of the stem
stemThickness = 2;

union(){
    //Main body
    translate([radius,radius]){
        union(){
            translate([0,0,thickness-inset]){
                linear_extrude(inset)
                difference(){
                    roundedRec();
                    offset(r=-lip/2){
                        roundedRec();
                    }
                }
            }
            linear_extrude(thickness-inset)
                roundedRec();
        }
    }

    //Text and stem
    translate([width/2,height/2]){
        //Top line of text
        translate([0,height/7.5,thickness-inset])
        linear_extrude(inset)
            text(topLine, height/3, font, halign="center", valign="center");

        //Second line of text
        translate([0,height/-4,thickness-inset])
            linear_extrude(inset)
                text(secondLine, height/6, font, halign="center", valign="center");
        //Stem
        translate([0,(-stemLength/2)+(lip/2)-height/2, stemThickness/2])
            union(){
                cube([stemWidth,stemLength,stemThickness],true);
                translate([0,(-stemLength/2)])
                    rotate([0,0,45])
                        cube([stemWidth/sqrt(2),stemWidth/sqrt(2),stemThickness],true);
        }
    }
}

//Create a rounded rectangle with the radii of $radius
module roundedRec(){
    minkowski(){
        square([width-(2*radius),height-(2*radius)]);
        circle(r=radius);
    }
}
```
plant.scad:
```
//Top line of text
topLine = "Snake Plant";
//Bottom line of text
secondLine = "Dracaena trifasciata";

//Font
font = "Arroyo";

//Height of label
height = 40;
//Width of label
width = 95;
//Overall thickness of label
thickness = 3;
// Depth of recess
inset = 2;
//Roundness of corners
radius = 3;
$fn = 30;

//Length of stem
stemLength = 50;
//Width of stem
stemWidth = 10;
//Thickness of the stem
stemThickness = 2;

//Body of Label
difference(){
    translate([0,0,thickness/2])
        union(){
            cube([width,height-radius,thickness],true);
            cube([width-radius,height,thickness],true);
            translate([(width/2)-(radius/2),(height/2)-(radius/2)])
                cylinder(h = thickness, d = radius, center=true);
            translate([(-width/2)+(radius/2),(height/2)-(radius/2)])
                cylinder(h = thickness, d = radius, center=true);
            translate([(width/2)-(radius/2),(-height/2)+(radius/2)])
                cylinder(h = thickness, d = radius, center=true);
            translate([(-width/2)+(radius/2),(-height/2)+(radius/2)])
                cylinder(h = thickness, d = radius, center=true);
        }
    translate([0,0,thickness])
        cube([width-radius, height-radius, inset], true);
}

//Stem
translate([0,(-stemLength/2)-height/2, stemThickness/2])
    union(){
        cube([stemWidth,stemLength,stemThickness],true);
        translate([0,(-stemLength/2)])
            rotate([0,0,45])
                cube([stemWidth/sqrt(2),stemWidth/sqrt(2),stemThickness],true);
    }

//Top line of text
translate([0,height/7.5,thickness-inset])
linear_extrude(inset)
    text(topLine, height/3, font, halign="center", valign="center");

//Second line of text
translate([0,height/-4,thickness-inset])
    linear_extrude(inset)
        text(secondLine, height/6, font, halign="center", valign="center");
```


[User]
I'd like to design a custom stamp, and I need help generating the OpenSCAD code. Here's what I'm looking for:

The stamp should have a rectangular base with a raised, engraved text area for the actual stamping. The text should be centered on the base and recessed into it. I need to be able to easily change the text itself. Also, I'd like a handle on the back for easier use. The handle should be cylindrical with a spherical endcap, and there should be a small sphere on the top of the handle as a visual indicator of the "top" of the stamp.

Here's a breakdown of the dimensions and features I'm envisioning:

*   **Base:**
    *   Rectangular prism shape.
    *   The length should be adjustable and configurable, let's call that 'length', which affects the length of the stamp and the length of the engraved text.
    *   The width is 'w', which should also be configurable.
    *   It will have a small lip around it's bottom face (like a picture frame) with a depth of 1, so we can extrude a smaller rectangle from the bigger one.

*   **Text:**
    *   Recessed into the base, with a depth of 1 unit.
    *   The text itself should be a configurable variable, let's call that 'text'.
    *   The text should be centered on the base.

*   **Handle:**
    *   Cylindrical body extending from the back of the base, the length should be configured by "handleHeight"
    *   Spherical end cap on the very end of the cylinder.
    *   A small sphere on top of the handle for visual indication of the top.

*   **Parameters:** I need to be able to change these parameters:
    *   `length` (length of the stamp and text)
    *   `w` (width of the stamp)
    *   `text` (the text to be engraved)
    *   `handleHeight` (length of the cylinder handle)

Can you generate the OpenSCAD code for this design?

[Assistant]
stempelharry.scad:
```

length = 60;
w = 15;
text="donermitsauce";

union(){
union() {

    translate([0,0,-6])
    cube([length+(w/2), w, 6]);
    //color("white")
    rotate(a=180,v=[0,1,0])
        translate([-w/4-length, w / 2, -1])
            linear_extrude(1, convexity = 4)
                resize([length, 0], auto = true)
                    text(text, valign = "center");
}

difference(){
    cube([length+(w/2), w, 1]);
    translate([0.8,0.8,0])
    cube([length+(w/2)-1.6, w-1.6, 2]);
}
translate([(length+w/2)/2,w/2,-41])
cylinder(40,w/2,w/2-3);

translate([(length+w/2)/2,w/2,-41])

difference(){
    sphere(w/2);
    translate([0,0,w/2])
    cube([w,w,w], center=true);

}
 translate([(length+w/2)/2,w-0.5,-41])
sphere(2);
}
```


[User]
Okay, I'm looking to design a customizable phone stand with a built-in charging cable holder. I've been messing around with OpenSCAD, but I'd really appreciate if you could handle the heavy lifting for this. I'd like a design that accounts for phone cases and isn't specific to one model of phone.  I also have some additional notes about the design that I'll outline below.

Here are the key parameters that I'd want to be able to control:

**Plug Measurements:** These control the dimensions of the charging plug area. I'm thinking of it in three parts: the plug interface, the plug itself, and the collar.

*   **plug interface** (the actual metal part of the plug):
    *   `plugInterfaceDepth`: Thickness of the charging tip.
    *   `plugInterfaceWidth`: Width of the charging tip.
    *   `plugInterfaceHeight`: Length of the charging tip.
*   **plug** (the block around the plug interface):
    *   `plugWidth`: Width of the plug block.
    *   `plugDepth`: Depth of the plug block.
    *   `plugHeight`: Height of the plug block.
*   **collar** (the thicker part of the cable near the flexible cord):
    *   `collarDiameter`: Diameter of the collar.
    *   `collarHeight`: Height of the collar.
    *   `cordDiameter`: Diameter of the cord itself.
*   `standCordCurveRadius`:  The radius of the curve for the charging cable exiting the bottom of the stand (this will determine how far the stand needs to extend beyond the phone, and avoid a sharp bend in the cable).

**Phone Measurements:**  These measurements describe the phone itself (including any case).

*   `phoneInterfaceDepth`: Depth from the front of the phone to the charging port.
*   `phonePlugIntrusionHeight`: How far the plug needs to intrude into the case (or into the phone itself, if no case is used).  This is like the case thickness at the charge port.
*   `phoneDepth`: Thickness of the phone.
*   `phoneWidth`: Width of the phone.

**Stand Measurements:** These are the dimensions for the stand itself.

*   `standPhoneCoverHeight`: Height of the phone slot (how high the sides of the stand come up on the phone).  This can be shorter than the total height of the phone.
*   `standThickness`: Thickness of the material used for the stand.
*   `standLip`:  How much the stand extends over the front face of the phone (ideally covering the case, but not the screen)
*  `tiltAngle`: How much to tilt the phone back from vertical when it's on the stand

**Design Notes/Invariants:**

*   The charging plug should be centered horizontally on the bottom edge of the phone.
*   The slot for the cable should always cut all the way through the base.
*   The base's thickness should always match the thickness of the phone holding body of the stand (the part the phone rests in).
*   The base should extend so that the top of the phone-holding section is centered over the base.
*   The phone covering section should have sides and a back, and be able to be eliminated entirely if `standPhoneCoverHeight` is set to `0`.
*   All corners should be rounded using the same radius as the thickness of the phone-holding part of the stand.

I would like three SCAD files total:
1.  The phone stand itself.
2.  A plug sizer tool to get the fit of the plug area correct.
3.  A phone sizer tool to get the fit of the phone area correct.

I'm not using any libraries that I know of, so you don't need to worry about including any.

Thank you so much!

[Assistant]
phone_stand.scad:
```
//Hello World!
$fn=100.0;
plugInterfaceDepth=2.35;
plugInterfaceWidth=8.2;
plugInterfaceHeight=6.6;
plugWidth=11.25;
plugDepth=6.8;
plugHeight=18.62;
collarDiameter=5.359999999999999;
collarHeight=6.0;
cordDiameter=3.55;
phoneInterfaceDepth=4.39;
phonePlugIntrusionHeight=3.3;
phoneDepth=14.7;
phoneWidth=79.4;
standCordCurveRadius=15.0;
standPhoneCoverHeight=50.0;
standThickness=2.0;
standLip=4.5;
tiltAngle=10.0;
union(){
  translate([0.0,0.0,standThickness]){
    rotate([0.0, (-tiltAngle), 0.0]){
      translate([((phoneDepth-phoneInterfaceDepth)-(plugInterfaceDepth/2.0)),0.0,(((plugHeight+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0))]){
        difference(){
          minkowski(){
            translate([(((-phoneDepth)+phoneInterfaceDepth)+(plugInterfaceDepth/2.0)),((-phoneWidth)/2.0),(((((phonePlugIntrusionHeight-plugHeight)-collarHeight)-standCordCurveRadius)-phonePlugIntrusionHeight)-(cordDiameter/2.0))]){
              cube([phoneDepth, phoneWidth, (((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0))]);
            }
            sphere(standThickness);
          }
          union(){
            translate([(((-phoneDepth)+(plugInterfaceDepth/2.0))+phoneInterfaceDepth),((-phoneWidth)/2.0),(-phonePlugIntrusionHeight)]){
              cube([phoneDepth, phoneWidth, standPhoneCoverHeight]);
            }
            translate([(-phoneDepth),(-phoneWidth),((-phonePlugIntrusionHeight)+standPhoneCoverHeight)]){
              cube([(2.0*phoneDepth), (2.0*phoneWidth), (2.0*standPhoneCoverHeight)]);
            }
            translate([0.0,(((-phoneWidth)/2.0)+standLip),((-phonePlugIntrusionHeight)+standLip)]){
              cube([phoneDepth, (phoneWidth-(2.0*standLip)), (2.0*standPhoneCoverHeight)]);
            }
          }
          union(){
            translate([((-plugInterfaceDepth)/2.0),((-plugInterfaceWidth)/2.0),0.0]){
              cube([plugInterfaceDepth, plugInterfaceWidth, plugInterfaceHeight]);
            }
            translate([((-plugDepth)/2.0),((-plugWidth)/2.0),(-plugHeight)]){
              cube([plugDepth, plugWidth, plugHeight]);
            }
            translate([0.0,0.0,((-plugHeight)-collarHeight)]){
              cylinder(collarHeight, (collarDiameter/2.0), (collarDiameter/2.0));
            }
            translate([((-cordDiameter)/2.0),((-cordDiameter)/2.0),((-plugHeight)-collarHeight)]){
              cube([(2.0*phoneDepth), cordDiameter, (2.0*standPhoneCoverHeight)]);
            }
            translate([(-2.0*phoneDepth),((-cordDiameter)/2.0),(((-2.0*plugHeight)-(2.0*collarHeight))-(2.0*standCordCurveRadius))]){
              cube([(4.0*phoneDepth), cordDiameter, ((plugHeight+collarHeight)+(2.0*standCordCurveRadius))]);
            }
          }
        }
      }
    }
  }
  difference(){
    union(){
      minkowski(){
        translate([0.0,((-phoneWidth)/2.0),standThickness]){
          cube([(phoneDepth*cos(tiltAngle)), phoneWidth, (phoneDepth*sin(tiltAngle))]);
        }
        sphere(standThickness);
      }
      minkowski(){
        translate([((-2.0*(((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0)))*sin(tiltAngle)),((-phoneWidth)/2.0),standThickness]){
          cube([((2.0*(((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0)))*sin(tiltAngle)), phoneWidth, phoneDepth]);
        }
        sphere(standThickness);
      }
    }
    translate([-100.0,((-cordDiameter)/2.0),0.0]){
      cube([200.0, cordDiameter, (phoneDepth+(2.0*standThickness))]);
    }
  }
}
//Goodbye World!

```
plug_sizer.scad:
```
//Hello World!
$fn=100.0;
plugInterfaceDepth=2.35;
plugInterfaceWidth=8.2;
plugInterfaceHeight=6.6;
plugWidth=11.25;
plugDepth=6.8;
plugHeight=18.62;
collarDiameter=5.359999999999999;
collarHeight=6.0;
cordDiameter=3.55;
phoneInterfaceDepth=4.39;
phonePlugIntrusionHeight=3.3;
phoneDepth=14.7;
phoneWidth=79.4;
standCordCurveRadius=15.0;
standPhoneCoverHeight=50.0;
standThickness=2.0;
standLip=4.5;
tiltAngle=0.0;
intersection(){
  union(){
    translate([0.0,0.0,standThickness]){
      rotate([0.0, (-tiltAngle), 0.0]){
        translate([((phoneDepth-phoneInterfaceDepth)-(plugInterfaceDepth/2.0)),0.0,(((plugHeight+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0))]){
          difference(){
            minkowski(){
              translate([(((-phoneDepth)+phoneInterfaceDepth)+(plugInterfaceDepth/2.0)),((-phoneWidth)/2.0),(((((phonePlugIntrusionHeight-plugHeight)-collarHeight)-standCordCurveRadius)-phonePlugIntrusionHeight)-(cordDiameter/2.0))]){
                cube([phoneDepth, phoneWidth, (((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0))]);
              }
              sphere(standThickness);
            }
            union(){
              translate([(((-phoneDepth)+(plugInterfaceDepth/2.0))+phoneInterfaceDepth),((-phoneWidth)/2.0),(-phonePlugIntrusionHeight)]){
                cube([phoneDepth, phoneWidth, standPhoneCoverHeight]);
              }
              translate([(-phoneDepth),(-phoneWidth),((-phonePlugIntrusionHeight)+standPhoneCoverHeight)]){
                cube([(2.0*phoneDepth), (2.0*phoneWidth), (2.0*standPhoneCoverHeight)]);
              }
              translate([0.0,(((-phoneWidth)/2.0)+standLip),((-phonePlugIntrusionHeight)+standLip)]){
                cube([phoneDepth, (phoneWidth-(2.0*standLip)), (2.0*standPhoneCoverHeight)]);
              }
            }
            union(){
              translate([((-plugInterfaceDepth)/2.0),((-plugInterfaceWidth)/2.0),0.0]){
                cube([plugInterfaceDepth, plugInterfaceWidth, plugInterfaceHeight]);
              }
              translate([((-plugDepth)/2.0),((-plugWidth)/2.0),(-plugHeight)]){
                cube([plugDepth, plugWidth, plugHeight]);
              }
              translate([0.0,0.0,((-plugHeight)-collarHeight)]){
                cylinder(collarHeight, (collarDiameter/2.0), (collarDiameter/2.0));
              }
              translate([((-cordDiameter)/2.0),((-cordDiameter)/2.0),((-plugHeight)-collarHeight)]){
                cube([(2.0*phoneDepth), cordDiameter, (2.0*standPhoneCoverHeight)]);
              }
              translate([(-2.0*phoneDepth),((-cordDiameter)/2.0),(((-2.0*plugHeight)-(2.0*collarHeight))-(2.0*standCordCurveRadius))]){
                cube([(4.0*phoneDepth), cordDiameter, ((plugHeight+collarHeight)+(2.0*standCordCurveRadius))]);
              }
            }
          }
        }
      }
    }
    difference(){
      union(){
        minkowski(){
          translate([0.0,((-phoneWidth)/2.0),standThickness]){
            cube([(phoneDepth*cos(tiltAngle)), phoneWidth, (phoneDepth*sin(tiltAngle))]);
          }
          sphere(standThickness);
        }
        minkowski(){
          translate([((-2.0*(((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0)))*sin(tiltAngle)),((-phoneWidth)/2.0),standThickness]){
            cube([((2.0*(((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0)))*sin(tiltAngle)), phoneWidth, phoneDepth]);
          }
          sphere(standThickness);
        }
      }
      translate([-100.0,((-cordDiameter)/2.0),0.0]){
        cube([200.0, cordDiameter, (phoneDepth+(2.0*standThickness))]);
      }
    }
  }
  translate([(((phoneDepth-phoneInterfaceDepth)-(plugDepth/2.0))-2.0),(((-plugWidth)/2.0)-1.0),((standCordCurveRadius+(2.0*standThickness))-1.0)]){
    cube([(plugDepth+2.0), (plugWidth+2.0), (((collarHeight+plugHeight)-phonePlugIntrusionHeight)+2.0)]);
  }
}
//Goodbye World!

```
phone_sizer.scad:
```
//Hello World!
$fn=100.0;
plugInterfaceDepth=2.35;
plugInterfaceWidth=8.2;
plugInterfaceHeight=6.6;
plugWidth=11.25;
plugDepth=6.8;
plugHeight=18.62;
collarDiameter=5.359999999999999;
collarHeight=6.0;
cordDiameter=3.55;
phoneInterfaceDepth=4.39;
phonePlugIntrusionHeight=3.3;
phoneDepth=14.7;
phoneWidth=79.4;
standCordCurveRadius=15.0;
standPhoneCoverHeight=50.0;
standThickness=2.0;
standLip=4.5;
tiltAngle=0.0;
intersection(){
  union(){
    translate([0.0,0.0,standThickness]){
      rotate([0.0, (-tiltAngle), 0.0]){
        translate([((phoneDepth-phoneInterfaceDepth)-(plugInterfaceDepth/2.0)),0.0,(((plugHeight+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0))]){
          difference(){
            minkowski(){
              translate([(((-phoneDepth)+phoneInterfaceDepth)+(plugInterfaceDepth/2.0)),((-phoneWidth)/2.0),(((((phonePlugIntrusionHeight-plugHeight)-collarHeight)-standCordCurveRadius)-phonePlugIntrusionHeight)-(cordDiameter/2.0))]){
                cube([phoneDepth, phoneWidth, (((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0))]);
              }
              sphere(standThickness);
            }
            union(){
              translate([(((-phoneDepth)+(plugInterfaceDepth/2.0))+phoneInterfaceDepth),((-phoneWidth)/2.0),(-phonePlugIntrusionHeight)]){
                cube([phoneDepth, phoneWidth, standPhoneCoverHeight]);
              }
              translate([(-phoneDepth),(-phoneWidth),((-phonePlugIntrusionHeight)+standPhoneCoverHeight)]){
                cube([(2.0*phoneDepth), (2.0*phoneWidth), (2.0*standPhoneCoverHeight)]);
              }
              translate([0.0,(((-phoneWidth)/2.0)+standLip),((-phonePlugIntrusionHeight)+standLip)]){
                cube([phoneDepth, (phoneWidth-(2.0*standLip)), (2.0*standPhoneCoverHeight)]);
              }
            }
            union(){
              translate([((-plugInterfaceDepth)/2.0),((-plugInterfaceWidth)/2.0),0.0]){
                cube([plugInterfaceDepth, plugInterfaceWidth, plugInterfaceHeight]);
              }
              translate([((-plugDepth)/2.0),((-plugWidth)/2.0),(-plugHeight)]){
                cube([plugDepth, plugWidth, plugHeight]);
              }
              translate([0.0,0.0,((-plugHeight)-collarHeight)]){
                cylinder(collarHeight, (collarDiameter/2.0), (collarDiameter/2.0));
              }
              translate([((-cordDiameter)/2.0),((-cordDiameter)/2.0),((-plugHeight)-collarHeight)]){
                cube([(2.0*phoneDepth), cordDiameter, (2.0*standPhoneCoverHeight)]);
              }
              translate([(-2.0*phoneDepth),((-cordDiameter)/2.0),(((-2.0*plugHeight)-(2.0*collarHeight))-(2.0*standCordCurveRadius))]){
                cube([(4.0*phoneDepth), cordDiameter, ((plugHeight+collarHeight)+(2.0*standCordCurveRadius))]);
              }
            }
          }
        }
      }
    }
    difference(){
      union(){
        minkowski(){
          translate([0.0,((-phoneWidth)/2.0),standThickness]){
            cube([(phoneDepth*cos(tiltAngle)), phoneWidth, (phoneDepth*sin(tiltAngle))]);
          }
          sphere(standThickness);
        }
        minkowski(){
          translate([((-2.0*(((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0)))*sin(tiltAngle)),((-phoneWidth)/2.0),standThickness]){
            cube([((2.0*(((((standPhoneCoverHeight-phonePlugIntrusionHeight)+plugHeight)+collarHeight)+standCordCurveRadius)+(cordDiameter/2.0)))*sin(tiltAngle)), phoneWidth, phoneDepth]);
          }
          sphere(standThickness);
        }
      }
      translate([-100.0,((-cordDiameter)/2.0),0.0]){
        cube([200.0, cordDiameter, (phoneDepth+(2.0*standThickness))]);
      }
    }
  }
  translate([(-phoneWidth),(-phoneWidth),((((((standThickness+standCordCurveRadius)+2.0)+collarHeight)+plugHeight)-plugInterfaceDepth)+standLip)]){
    cube([(2.0*phoneWidth), (2.0*phoneWidth), 10.0]);
  }
}
//Goodbye World!

```


[User]
Hey, I'm looking to design an Abloy-style key generator in OpenSCAD. I've been experimenting with some code, and I think I have a handle on the basic logic, but I'd like you to create a more robust, parametric design.

Essentially, the key has a cylindrical body with a series of cuts along one side that correspond to the pin depths in an Abloy lock. The depths of these cuts are controlled by a list, where each number represents the angle to rotate the cut. A "0" cut is the deepest cut and thus the cylinder is not cut at all at that point. The deeper the cut, the larger the angle is, up to 5 (the shallowest cut) or whatever max depth I specify.

Here's the specific functionality I need:

1.  **Cut List:** The design should take a list (e.g., `cuts = [0, 5, 3, 3, 0, 1, 3, 0, 4]`) as an input parameter. Each element in the list represents the cut depth, with a numerical value. I want it to be easy to adjust the maximum cut depth via a variable.
2.  **Cylindrical Key Body:** The base of the key should be a cylinder, with adjustable diameter and length.
3.  **Cut Generation:**  For each element in the `cuts` list, a rectangular cut (a cuboid) should be created along the cylinder. Each cut should be positioned along the length of the cylinder, with each cut having a unique index. The "depth" of each cut should correspond to the angle of the cuboid as described above. A "0" cut is where no material is removed.
4.  **Mirroring:** The key needs to be mirrored along its central axis to have cuts on either side.
5.  **Key Bow:**  The key needs a "bow" or handle at one end. It should have the general shape I have in mind - a sort of rounded shape with a hole in the middle. It would be helpful if this could be customized too.
6.  **FDM Printing Compensation:**  I want a boolean option to compensate for FDM printing inaccuracies, which will reduce the size of the overall key. This compensation value should be adjustable, probably around .1 to .5mm. The option should shrink the overall key.
7.  **Parameterization:** All dimensions (cylinder diameter, cylinder length, cut dimensions, bow dimensions, hole diameter, and all relevant sizes) should be controlled by parameters.
8.  **Optional Bow Hole:** I want a parameter to control whether the bow has a hole or not.

Could you generate OpenSCAD files to achieve this design? I'm not using any particular libraries right now, so I don't need anything fancy like BOSL or similar libraries.

[Assistant]
abloy_key_to_code.scad:
```
//List the cuts from tip to bow.  A first 0 is implicit.
cuts = [0,5,3,3,0,1,3,0,4];

// Make the version for FDM/FFF printing?
fdmable = 1; //[1:yes,0:no]

// How much should I shrink the key to compensate for FDM surface roughness/inaccuracy
fdm_size_compensate = 0.3;

if (fdmable) {
    difference() {
        translate([0,0,2.5])
        rotate([0,90,0])
        union() {
            bow(hole = false);
            key(cuts,compensation=fdm_size_compensate);
        }
    translate([0,0,-50])cube(100,center=true);
    }
} else {
    bow();
    key(cuts);
}

module key(cuts,compensation=0) {
    mirror([1,0,0])
    translate([0,-30,0])
    difference() {
        rotate([-90,0,0])cylinder(d=6-compensation,h=30,$fn=35);
        translate([0,0,-50])cube(100,center=true);
        for (ii= [0:(len(cuts)-1)]) {
            rotate([0,90-cuts[ii]*18,0])
            translate([0,3+2*ii,0]) cube([20,2.5,20]);
        }
    };
}

module bow(hole = true) {
    difference() {
        hull(){
          translate([0,21,0]) cylinder(d=3,h=3,$fn=30);
          translate([-7,16,0]) cylinder(d=3,h=3,$fn=30);
          translate([7,16,0]) cylinder(d=3,h=3,$fn=30);
          translate([-1.5,0,0]) cylinder(d=3,h=3,$fn=30);
          translate([1.5,0,0]) cylinder(d=3,h=3,$fn=30);
        }
        if (hole) {
          translate([0,14,-1]) rotate([0,0,90])
            cylinder(h=5,d=11,$fn=5);
        }
    }
}
```


[User]
Okay, I've been playing around with OpenSCAD and I need to create a really flexible tee fitting, or actually, something that can be a tee, a corner, a straight connector, or even a stopper. I've seen some designs that are close, but I need more control and more flexibility than those offer.

My main goal is to have a single module that can generate a fitting with 1 to 6 pipes. Here's how I envision the functionality:

**Core Parameters:**

*   **Common Inner Diameter (CD):** A global setting for the inner diameter of all the pipes to ensure a consistent connection.
*   **Number of Circle Fragments (fn):** For faster rendering during design, I want to set the circle detail via `$fn`.
*   **Rounded Borders (RB):** A simple "yes/no" option to round the outer edges of the pipes.
*   **4th Pipe (4P):** A "yes/no" toggle for including a fourth pipe, extending opposite the base pipe.
*   **5th & 6th Pipes (56P):** A "yes/no" to enable two additional pipes, perpendicular to the XY-plane and on opposite sides of the existing center.

**Individual Pipe Parameters:**

I want each pipe to have its own set of parameters, specifically:

*   **Outer Diameter (DX_):** Where '\_' represents pipe number.
*   **Inner Diameter (BX_):** Where '\_' represents pipe number.
*   **Length (LX_):** Where '\_' represents pipe number.
*   **Insert Length (LX_ _):** Where '\_' represents pipe number; for how far the pipe inserts into the main body.
*   **Rotation (AR_ , AL_):**
    *   Each of the three main pipes needs to rotate around the X and then the Z axis. For the base pipe, it will always be a rotation around Y of 90 deg.
    *   For the right pipe, we need X rotation (AR1) and Z rotation (AR2).
    *   For the left pipe, we need X rotation (AL1) and Z rotation (AL2).
    * The additional pipes (4,5,6) need only one rotation around X; this is a fixed value in the model.

**Additional Features:**

*   **Show Directions (showdir):** A "yes/no" toggle for displaying text labels indicating the direction of each pipe (Base, Left, Right, and 4th). I can see that you've used `linear_extrude()` for the text, so that's a great option.
*   **Test (test):** A "yes/no" toggle for visualizing the internal structure of the fitting. In my example, the inside was shown via the use of a cube at certain points in the model.
* **Central Sphere:** To ensure connections with a good base, for the main 3 pipes (base, left and right) I need to have a central sphere with a diameter equal to the largest of the 3 pipes.

**Usage Notes:**

I understand that if I want just a simple Tee, I should leave the 4th, 5th, and 6th pipe parameters set to 'no'. For a corner or straight connector, I would rotate the left and right pipes to be in the same relative position (or close) and leave the 4, 5, and 6th pipes to no. If the left and right are set to the base, I have a closed stopper. Also, as the 4-5-6 pipes share diameters and lengths, I will be doing these pipes first, before adapting the 1-2-3 pipes.

I know that I need to avoid conflicting dimensions, so I'll make sure my lengths and diameters don't cause interference. I'll be adjusting each pipe's dimensions carefully one at a time.

I'm looking forward to seeing how you solve this! I don't need a lot of complicated comments in the code itself, just something well-organized and efficient.

[Assistant]
Tee.scad:
```
// My tee
// phildc 2020
// Use openscad customizer

// General revision

// Add optional 4th pipe ?

/* [General parameters] */
//Common inner diameter of connection
CD=10;
//Rounded borders
RB="yes";       // [yes,no]
//4th pipe
4P="no";       // [yes,no]
//5th and 6th pipe
56P="no";       // [yes,no]

//Show directions
showdir="no";  // [no,yes]

//Examine inside
test="no";      // [no,yes]

/* [Base pipe (X axis)] */
//Outer diameter
DX1=15;
//Inner diameter
BX1=12;
//Length
LX1=20;
//Insert length
LX11=10;

/* [Right pipe] */
//Rotate around X-axis 0= Z-dir -90=flat
AR1=-90;
//Rotate around Z-axis
AR2=0;
//Outer diameter
DX2=15;
//Inner diameter
BX2=12;
//Length
LX2=20;
//Insert length
LX22=10;

/* [Left pipe] */
//Rotate around X-axis 0= Z-dir 90=flat
AL1=90;
//Rotate around Z-axis
AL2=0;
//Outer diameter
DX3=15;
//Inner diameter
BX3=12;
//Length
LX3=20;
//Insert length
LX33=10;

/* [additional pipes (4,5,6) pipe] */
//Outer diameter
DX4=15;
//Inner diameter
BX4=12;
//Length
LX4=20;
//Insert length
LX44=10;

// any more ?

module dummy() {}   // end of parameters

// ==========================================

echo("");
echo("Using the CUSTOMIZER, discover the parameters:");
echo("Carefully change ONE parameter at a time, and CHECK the results,");
echo("maybe using the TEST parameter to look inside the model");
echo("");
echo("Then try your own values.If in doubt, go back (to the default values?)");
echo("Enjoy !");
echo("");

difference() {

// First do the full plain part  =========
union() {  // Ensure good connection point

 if(DX1>=DX3) {
  sphere(d=DX1,$fn=240);
 }
 if(DX3>=DX2) {
  sphere(d=DX3,$fn=240);
 }
 if(DX2>=DX1) {
  sphere(d=DX2,$fn=240);
 }

rotate([0,90,0])   // transverse pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=120);
    if(RB=="yes"){
    rotate_extrude(convexity = 10,$fn=120)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = 120);
    }
  }   // union

//rotate([AR1,AR2,0])    // right pipe*****
rotate([AR1,0,AR2])    // right pipe*****
  union(){
    cylinder(d=DX2,h=LX2,$fn=120);
     if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX2/2-((DX2-BX2)/4), LX2, 0])
       circle(r = (DX2-BX2)/4, $fn = 120);
     }
   }  // union

//rotate([AL1,AL2,0])     // left pipe
rotate([AL1,0,AL2])     // left pipe
  union(){
   translate([0,0,0])
     cylinder(d=DX3,h=LX3,$fn=120);
        if(RB=="yes"){

   rotate_extrude(convexity = 10,$fn=120)
     translate([DX3/2-((DX3-BX3)/4), LX3, 0])
       circle(r = (DX3-BX3)/4, $fn = 120);
        }
  }  // union

if(4P=="yes") {
 rotate([0,-90,0])   // 4th prolongs transverse
  union() {
    cylinder(d=DX4,h=LX4,$fn=120);
      if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX4/2-((DX4-BX4)/4), LX4, 0])
       circle(r = (DX4-BX4)/4, $fn = 120);
      }
  }   // union

}

if(56P=="yes") {
 rotate([0,0,0])   // 4th prolongs transverse
  union() {
    cylinder(d=DX4,h=LX4,$fn=120);
      if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX4/2-((DX4-BX4)/4), LX4, 0])
       circle(r = (DX4-BX4)/4, $fn = 120);
      }
  }   // union
 rotate([180,0,0])   // 4th prolongs transverse
  union() {
    cylinder(d=DX4,h=LX4,$fn=120);
      if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX4/2-((DX4-BX4)/4), LX4, 0])
       circle(r = (DX4-BX4)/4, $fn = 120);
      }
  }   // union

}

 }   // union of pipes and central sphere
//===============================================

// now the borings ======================

 sphere(d=CD,$fn=120); // central space

 rotate([0,90,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX1,h=LX1+2,$fn=120);
  translate([0,0,-1])
     cylinder(d=CD,h=LX1+2,$fn=120);
  }  // union

rotate([AR1,0,AR2])    // bore right  ****
  union() {
  translate([0,0,LX2-LX22])
     cylinder(d=BX2,h=LX2+2,$fn=120);
  translate([0,0,-1])
     cylinder(d=CD,h=LX2+2,$fn=120);

  }

rotate([AL1,0,AL2])    // bore left
  union() {
  translate([0,0,LX3-LX33])
     cylinder(d=BX3,h=LX33+2,$fn=120);
  translate([0,0,-1])
     cylinder(d=CD,h=LX3+2,$fn=120);

 }

if(4P=="yes") {
   rotate([0,-90,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX4,h=LX4+2,$fn=120);
  translate([0,0,-1])
     cylinder(d=CD,h=LX4+2,$fn=120);
  }  // union

}

if(56P=="yes") {
   rotate([0,0,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX4,h=LX4+2,$fn=120);
  translate([0,0,-1])
     cylinder(d=CD,h=LX4+2,$fn=120);
  }  // union
   rotate([180,0,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX4,h=LX4+2,$fn=120);
  translate([0,0,-1])
     cylinder(d=CD,h=LX4+2,$fn=120);
  }  // union

}

// Test option to look inside
if(test=="yes")  {
  echo("");
  echo(" ***    ***   TEST parameter is ON !  ***   ***");
  echo("");

  translate([-50,-50,0])
    cube([150,100,50]);
}
if(showdir=="yes")  {
  echo("");
  echo(" ***    ***   SHOWDIR parameter is ON !  ***   ***");
  echo("");

}
 }   // difference

if(showdir=="yes")  {
  color("Blue",1.0) {
    translate([50,0,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("Base",size=3,$fn=120);
    }   // color
  color("Red",1.0) {
    translate([0,-50,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("Left",size=3,$fn=120);
    }   // color

  color("Green",1.0) {
    translate([0,50,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("Right",size=3,$fn=120);
    }   // color

  color("Black",0.6) {
    translate([-50,0,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("4th",size=3,$fn=120);
    }   // color

}
```
Construction-check.scad:
```
// multi example - check construction

// Warning:

//UNION function does not maintain holes!

import("Corner4.stl");

color("red") {

translate([80,0,0])
  rotate([90,0,180])
    import("Corner4.stl");
}
color("green") {

translate([80,80,0])
  rotate([90,0,270])
    import("Corner4.stl");
}
color("blue") {

translate([0,80,0])
  rotate([90,0,0])
    import("Corner4.stl");
}

color("grey") {
  translate([20,0,0])
    rotate([0,90,0])
      cylinder(d=12,h=60,$fn=20);
  translate([0,20,0])
    rotate([0,90,90])
      cylinder(d=12,h=60,$fn=20);
  translate([80,80,0])
    rotate([0,90,180])
      cylinder(d=12,h=60,$fn=20);
  translate([80,80,0])
    rotate([0,90,270])
      cylinder(d=12,h=60,$fn=20);

  translate([0,0,0])
    rotate([0,0,0])
      cylinder(d=12,h=60,$fn=20);
  translate([0,20,80])
    rotate([-90,0,0])
      cylinder(d=12,h=60,$fn=20);
}

color("purple") {

translate([0,0,80])
  rotate([180,0,90])
    import("Corner4.stl");
}
```
TeeV5.scad:
```
// My tee
// phildc 2020
// Use openscad customizer

// 2020  03  04  V5

/* [General parameters] */
//Common inner diameter of connection
CD=10;
//Nbr of circle fragments ($fn)
fn=120;         // [20, 60, 120, 240]
//Rounded borders
RB="yes";       // [yes,no]
//4th pipe
4P="no";       // [yes,no]
//5th and 6th pipe
56P="no";       // [yes,no]

//Show directions
showdir="no";  // [no,yes]

//Examine inside
test="no";      // [no,yes]

/* [Base pipe (X axis)] */
//Outer diameter
DX1=15;
//Inner diameter
BX1=12;
//Length
LX1=20;
//Insert length
LX11=10;

/* [Right pipe] */
//Rotate around X-axis 0= Z-dir -90=flat
AR1=-90;
//Rotate around Z-axis
AR2=0;
//Outer diameter
DX2=15;
//Inner diameter
BX2=12;
//Length
LX2=20;
//Insert length
LX22=10;

/* [Left pipe] */
//Rotate around X-axis 0= Z-dir 90=flat
AL1=90;
//Rotate around Z-axis
AL2=0;
//Outer diameter
DX3=15;
//Inner diameter
BX3=12;
//Length
LX3=20;
//Insert length
LX33=10;

/* [additional pipes (4,5,6) pipe] */
//Outer diameter
DX4=15;
//Inner diameter
BX4=12;
//Length
LX4=20;
//Insert length
LX44=10;

// any more ?

module dummy() {}   // end of parameters

// ==========================================

echo("");
echo("Using the CUSTOMIZER, discover the parameters:");
echo("Carefully change ONE parameter at a time, and CHECK the results,");
echo("maybe using the TEST parameter to look inside the model");
echo("");
echo("Then try your own values.If in doubt, go back (to the default values?)");
echo("Enjoy !");
echo("");

difference() {

// First do the full plain part  =========
union() {  // Ensure good connection point

 if(DX1>=DX3) {
  sphere(d=DX1,$fn=fn);
 }
 if(DX3>=DX2) {
  sphere(d=DX3,$fn=fn);
 }
 if(DX2>=DX1) {
  sphere(d=DX2,$fn=fn);
 }

rotate([0,90,0])   // transverse pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=fn);
    if(RB=="yes"){
    rotate_extrude(convexity = 10,$fn=fn)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = fn);
    }
  }   // union

//rotate([AR1,AR2,0])    // right pipe*****
rotate([AR1,0,AR2])    // right pipe*****
  union(){
    cylinder(d=DX2,h=LX2,$fn=fn);
     if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=fn)
     translate([DX2/2-((DX2-BX2)/4), LX2, 0])
       circle(r = (DX2-BX2)/4, $fn = fn);
     }
   }  // union

//rotate([AL1,AL2,0])     // left pipe
rotate([AL1,0,AL2])     // left pipe
  union(){
   translate([0,0,0])
     cylinder(d=DX3,h=LX3,$fn=fn);
        if(RB=="yes"){

   rotate_extrude(convexity = 10,$fn=fn)
     translate([DX3/2-((DX3-BX3)/4), LX3, 0])
       circle(r = (DX3-BX3)/4, $fn = fn);
        }
  }  // union

if(4P=="yes") {
 rotate([0,-90,0])   // 4th prolongs transverse
  union() {
    cylinder(d=DX4,h=LX4,$fn=fn);
      if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=fn)
     translate([DX4/2-((DX4-BX4)/4), LX4, 0])
       circle(r = (DX4-BX4)/4, $fn = fn);
      }
  }   // union

}

if(56P=="yes") {
 rotate([0,0,0])   // 4th prolongs transverse
  union() {
    cylinder(d=DX4,h=LX4,$fn=fn);
      if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=fn)
     translate([DX4/2-((DX4-BX4)/4), LX4, 0])
       circle(r = (DX4-BX4)/4, $fn = fn);
      }
  }   // union
 rotate([180,0,0])   // 4th prolongs transverse
  union() {
    cylinder(d=DX4,h=LX4,$fn=120);
      if(RB=="yes"){

    rotate_extrude(convexity = 10,$fn=fn)
     translate([DX4/2-((DX4-BX4)/4), LX4, 0])
       circle(r = (DX4-BX4)/4, $fn = fn);
      }
  }   // union

}

 }   // union of pipes and central sphere
//===============================================

// now the borings ======================

 sphere(d=CD,$fn=fn); // central space

 rotate([0,90,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX1,h=LX1+2,$fn=fn);
  translate([0,0,-1])
     cylinder(d=CD,h=LX1+2,$fn=fn);
  }  // union

rotate([AR1,0,AR2])    // bore right  ****
  union() {
  translate([0,0,LX2-LX22])
     cylinder(d=BX2,h=LX2+2,$fn=fn);
  translate([0,0,-1])
     cylinder(d=CD,h=LX2+2,$fn=fn);

  }

rotate([AL1,0,AL2])    // bore left
  union() {
  translate([0,0,LX3-LX33])
     cylinder(d=BX3,h=LX33+2,$fn=fn);
  translate([0,0,-1])
     cylinder(d=CD,h=LX3+2,$fn=fn);

 }

if(4P=="yes") {
   rotate([0,-90,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX4,h=LX4+2,$fn=fn);
  translate([0,0,-1])
     cylinder(d=CD,h=LX4+2,$fn=fn);
  }  // union

}

if(56P=="yes") {
   rotate([0,0,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX4,h=LX4+2,$fn=fn);
  translate([0,0,-1])
     cylinder(d=CD,h=LX4+2,$fn=fn);
  }  // union
   rotate([180,0,0])   // transverse bore
  union() {
  translate([0,0,LX1-LX11])
     cylinder(d=BX4,h=LX4+2,$fn=fn);
  translate([0,0,-1])
     cylinder(d=CD,h=LX4+2,$fn=fn);
  }  // union

}

// Test option to look inside
if(test=="yes")  {
  echo("");
  echo(" ***    ***   TEST parameter is ON !  ***   ***");
  echo("");

  translate([-50,-50,0])
    cube([150,100,50]);
}
if(showdir=="yes")  {
  echo("");
  echo(" ***    ***   SHOWDIR parameter is ON !  ***   ***");
  echo("");

}
 }   // difference

if(showdir=="yes")  {
  color("Blue",1.0) {
    translate([50,0,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("Base",size=3,$fn=60);
    }   // color
  color("Red",1.0) {
    translate([0,-50,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("Left",size=3,$fn=60);
    }   // color

  color("Green",1.0) {
    translate([0,50,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("Right",size=3,$fn=60);
    }   // color

  color("Black",0.6) {
    translate([-50,0,2])
      rotate([90,0,45])  // set vertical
         linear_extrude(0.5)
         text("4th",size=3,$fn=60);
    }   // color

}
```


[User]
Hey, I'm looking to create some T-slot profiles and corner brackets in OpenSCAD, similar to what I've seen other folks do. I want to be able to specify the T-slot size, length, and whether I want a single profile or multiple profiles grouped together. For the corner brackets, I need them to match the T-slot sizes I choose.

Here’s a more detailed breakdown of what I’m hoping to achieve:

**T-Slot Profiles:**

*   I need to be able to select between a few standard sizes, specifically 2020, 2525, and 3030 T-slot profiles. I need these sizes to match the standard dimensions.
*   I need to be able to specify the length of the T-slot extrusion. This should be a simple linear extrusion of the profile.
*   I also want the option to generate multiple T-slot profiles together. I need to be able to choose how many profiles are generated, including single, double, triple, and quadruple configurations. These should be joined together as a single object.

**Corner Brackets:**

*   I need corner brackets that correspond to the same sizes as the T-slots (2020, 2525, and 3030).
*   The corner brackets should have holes for mounting screws. I'd like the size and placement of these holes to be appropriate for each respective T-slot size.

Ideally, I'd like to control all of these options via variables in the OpenSCAD file. So, something like:

```
// T-slot Parameters
t_select = 1; // 1=Tslot2020, 2=Tslot2525, 3=Tslot3030
tslot_len = 60; // Length of the t-slot
t_set = 1; // 1=single, 2=double, 3=triple, 4=quadruple

// Corner Bracket Parameters
cb_select = 1; // 1=bracket2020, 2=bracket2525, 3=bracket3030
```

I don't need any fancy rendering or anything. Just a clean model of the T-slots and brackets. I'm using OpenSCAD version 2015.03-2. I'm not using any custom libraries. Could you generate this for me?

[Assistant]
UTslot.scad:
```
//// File: UTslot.scad
//// Version: OpenSCAD 2015.03-2
//// Author: Kijja Chaovanakij
//// Date: 2020.2.29
//// revise(1): change internal radius, add Tslot2525
//// Date: 2020.3.05
//// revise(2): Add a set of t-slots
//// Date: 2020.3.07

//[var]//
//parts to generate
part=0; //[0:demo, 1:tslot, 2:corner bracket]
//Tslot size
t_select=1; //[1:Tslot2020, 2:Tslot2525, 3:Tslot3030]
//Tslot length
tslot_len=60; //[10:5:200]
//Tslot set
t_set=1; //[1:single, 2:double, 3:triple, 4:quadruple]
//The size of the corner bracket
cb_select=3; //[1:bracket2020, 2:bracket2525, 3:bracket3030]

//// main
if (part==0){
  color("Pink")
  translate([-35, 20, 0])
    single_profile(3, 45);
  color("LightGreen")
  translate([0, 17.5, 0])
    single_profile(2, 40);
  color("PaleTurquoise")
  translate([30, 15, 0])
    single_profile(1, 38);
  color("HotPink")
  translate([-35, -5, 0])
    corner_bracket(3);
  color("GreenYellow")
  translate([0, -5, 0])
    corner_bracket(2);
  color("Aqua")
  translate([30, -5, 0])
    corner_bracket(1);
}//i
if (part==1)
  tslot_extrude(size=t_select, hi=tslot_len, pile=t_set);
if (part==2)
  corner_bracket(size=cb_select);
//// end

module corner_bracket(size=cb_select){
  sc=cb_params[size];
  $fn=24;
  ep=0.002;
  translate([sc[2]/2, 0, 0])
  rotate([90,0,-90])
  difference(){
    union(){
      difference(){
        cube([sc[1], sc[1], sc[2]]);
        translate([sc[1], sc[1], sc[2]/2])
        rotate([0, 0, 45])
          cube([sc[1]*sqrt(2), sc[1]*sqrt(2), sc[2]+ep], true);
      }//d
      difference(){
        cube([sc[1], sc[1], sc[2]]);
        translate([sc[5], sc[5], -ep])
          cube([sc[1], sc[1], sc[2]+ep*2]);
      }//d
    }//u
    translate([sc[3], sc[3], sc[4]])
      cube([sc[1], sc[1], sc[2]-sc[4]*2]);
    translate([sc[1]/2+sc[3]/2, -ep, sc[2]/2])
    rotate([-90, 0, 0])
      double_cylin(sc[3]+ep*2, sc[6], sc[7]);
    translate([-ep, sc[1]/2+sc[3]/2, sc[2]/2])
    rotate([90, 0, 90])
      double_cylin(sc[3]+ep*2, sc[6], sc[7]);
  }//d
}//m
//

module tslot_extrude(size=t_select, hi=tslot_len, pile=t_set){
if (pile==1)
  single_profile(size, hi);
if (pile>=2)
  multi_profile(size, hi, pile);
}//m
//

module single_profile(size=t_select, hi=tslot_len){
  st=tslot_params[size];
  $fn=24;
  if (size>=1 && size<=3)
  linear_extrude(height=hi)
  difference(){
    minkowski(){
      circle(r=st[7]);
      square(st[1]-st[7]*2, true);
    }//m
    circle(d=st[6]);
    for (i=[0:90:270])
      rotate([0, 0, i])
        tslot_profile(size);
  }//d
}//m
//

module multi_profile(size=t_select, hi=tslot_len, pile=t_set){
  st=tslot_params[size];
  $fn=24;
  if (size>=1 && size<=3)
  if (pile>=2)
  linear_extrude(height=hi)
  difference(){
    minkowski(){
      circle(r=st[7]);
      square([st[1]*pile-st[7]*2, st[1]-st[7]*2], true);
    }//m
    for (i=[-(pile-1)/2+1/2:1:(pile-1)/2])
      translate([i*st[1], 0])
        inner_profile(size=size);
    for (i=[-pile/2+1/2:1:pile/2]){
      translate([i*st[1], 0])
        circle(d=st[6]);
      for (j=[0,180])
        translate([i*st[1], 0])
          rotate([0, 0, j])
            tslot_profile(size=size);
    }//f
    translate([(pile-1)*st[1]/2, 0])
      rotate([0, 0, 270])
        tslot_profile(size=size);
    mirror([1, 0, 0])
    translate([(pile-1)*st[1]/2, 0])
      rotate([0, 0, 270])
        tslot_profile(size=size);
  }//d
}//m
//

module tslot_profile(size=t_select){
  st=tslot_params[size];
  $fn=24;
  ep=0.002;
  fill_rad=st[3]/4;
  tdep=(st[5]-st[4])/2;
  tinner_rad=(st[1]/2-st[2]/2-st[3]-tdep)/3;
  if (size>=1 && size<=3)
  union(){
    minkowski(){
      circle(r=tinner_rad);
      hull(){
        translate([0, st[2]/2+tinner_rad])
          square([st[4]-tinner_rad*2, ep], true);
        translate([0, st[2]/2+tdep+tinner_rad])
          square([st[5]-tinner_rad*2, ep], true);
        translate([0, st[1]/2-st[3]-tinner_rad])
          square([st[5]-tinner_rad*2, ep], true);
      }//h
    }//m
    translate([-st[4]/2, st[1]/2-st[3]])
      square(st[4]);
    translate([st[4]/2, st[1]/2-st[3]])
      fillet(fill_rad);
    mirror([1, 0, 0])
    translate([st[4]/2, st[1]/2-st[3]])
      fillet(fill_rad);
    translate([st[4]/2, st[1]/2])
    rotate([0, 0, -90])
      fillet(fill_rad);
    mirror([1, 0, 0])
    translate([st[4]/2, st[1]/2])
    rotate([0, 0, -90])
      fillet(fill_rad);
  }//u
}//m
//

module inner_profile(size=t_select){
  st=tslot_params[size];
  $fn=24;
  ep=0.002;
  fill_rad=st[3]/4;
  tinner_rad=(st[1]/2-st[2]/2-st[3]-(st[5]-st[4])/2)/3;
  tinner_dep=(st[1]-st[2])/2-st[3];
  if (size>=1 && size<=3)
  union(){
    minkowski(){
      circle(r=tinner_rad);
      hull(){
        translate([st[1]/2-st[2]/2-tinner_rad, 0])
          square([ep, st[4]-tinner_rad*2], true);
        mirror([1, 0, 0])
        translate([st[1]/2-st[2]/2-tinner_rad, 0])
          square([ep, st[4]-tinner_rad*2], true);
        translate([st[3]-tinner_rad, 0])
          square([ep, st[4]+tinner_dep*2-tinner_rad*2], true);
        mirror([1, 0, 0])
        translate([st[3]-tinner_rad, 0])
          square([ep, st[4]+tinner_dep*2-tinner_rad*2], true);
      }//h
    }//m
  }//u
}//m
//

module double_cylin(hi=10, dia=6, e2e=12){
  linear_extrude(height=hi)
  hull(){
    for (i=[(e2e-dia)/2, -(e2e-dia)/2])
      translate([i, 0, 0])
        circle(d=dia);
  }//h
}//m
//

module fillet(rad=1){
	difference(){
		square(rad+0.002);
		translate([rad, rad])
      circle(r=rad);
	}//d
}//m
//

//// Do not change these parameters unless necessary.
cb_params =
 [[ "name", //0
    "cb_size", //1
    "cb_wid", //2
    "cb_thi", //3
    "cb_side_thi", //4
    "cb_side_hi", //5
    "cb_hole_dia", //6
    "cb_slot_len"], //7
  [ "cbracket2020", 28, 20, 3.5, 3, 6, 6, 10],
  [ "cbracket2525", 30, 24, 4.0, 3, 7, 6, 12],
  [ "cbracket3030", 35, 28, 4.5, 3, 7, 7, 16],
 ]; //end

tslot_params =
 [[ "name", //0
    "tframe_size", //1
    "tinner_size", //2
    "twall_thi", //3
    "tslot_wid", //4
    "tmax_inner_wid", //5
    "thole_dia", //6
    "tcorner_rad"], //7
  [ "tslot2020", 20, 7.8,  1.8, 6.2, 11.0, 5.0, 1.5],
  [ "tslot2525", 25, 9.8,  2.0, 6.2, 15.0, 5.0, 1.5],
  [ "tslot3030", 30, 11.6, 2.2, 8.2, 16.5, 6.8, 2.5],
 ]; //end

```