[User]
Hey, I'm looking to get some custom designs made for a cupholder/organizer thingy for my AtGames Legends Ultimate arcade cabinet. I've got some basic dimensions from existing designs (provided below), but I want something that's completely customizable. I'd like the design to be parametric, so I can adjust things like the cupholder diameter/height, the USB storage slots, and even a back slot, which might be used for storage. Also, I need screw holes to be adjustable so that I can customize it if needed. Here's what I've got so far for dimensions:

*   **Back Panel Length (L1):** 155 mm
*   **Main Cup Width (CW1):** 97 mm
*   **Main Cup Height (CH1):** 86 mm
*   **USB Depth Left (USBD1):** 60 mm
*   **USB Length Left (USBL1):** 37 mm
*  **USB Depth Right (USBD2):** 60 mm
*   **USB Length Right (USBL2):** 37 mm
*  **Back Slot Depth (BS1):** Variable, but start at 0
*   **Back Slot Width (BS2):** Variable, but start at 0
*   **Main Right Round End Offset (LX1):** 15 mm
*   **Main Left Round End Offset (RX1):** 15 mm
*   **Back Wall Thickness (BW1):** 46 mm
*  **Cup Holder Thickness (CT1):** CW1 + 6 mm
*   **Cup Holder Height (CHH1):** CH1 + 6 mm
*   **Screw Head Depth (SDH1):** 46 mm
*   **Set Screw Depth (SHH1):** SDH1 - 10 mm
*   **Screw Head Diameter (SHD1):** 12 mm
*   **Screw Diameter (SD1):** 7 mm

I'd like the design to use OpenSCAD and be pretty modular so I can easily reuse functions. It should include a function to make the rounded ends, a function for the screw holes with the counter sink, a function for the cupholder with the storage and the back slot. I need the back slot parameters to be able to be adjusted to make a place for various storage options. Also, please make sure the code is commented well. I have attached the existing scad files, but I don't expect you to use them except as reference.

Finally, it needs to work with standard M6 screws. Thanks!

[Assistant]
Lengend_USB_cupholder_customizable_v2.scad:
```
//made for Atgames Legends Ultimate arcade cabinet.
//acem77 1/4/2020
// added custom slots for storage or art

//curve resolution
$fn=50;

//Length of back panel
L1 = 155;

//Main Cup width 97
CW1 = 97;
//Main Cup height
CH1 = 86;

//USB depth left
USBD1 = 60;
//USB length
USBL1 = 37;

//USB depth right
USBD2 = 60;
//USB lenght
USBL2  = 37;

//Main Right Round ends bring into center for smaller printer
LX1 = 15;
//Main Left Round ends bring into center for smaller printer
RX1 = 15;

//back wall 24+17+5 leave as is. 46
BW1 = 46;

//cup holder thinkness
CT1 = CW1+6;
//cup holder height
CHH1 = CH1+6;

//screw and head depth
SDH1 = 24+17+5;
//set depth -10
SHH1 = SDH1-10;

//screw head diameter
SHD1 = 12;
//screw diameter
SD1 = 7;

//module plateroundends(){
//difference(){
//translate([0,0,0])
    //d1 = 22*2-(GP1*2), d2 = 22*2-(GP1*2)
//cylinder(h = 10, d1 = BW1*2-(GP1*2), d2 = BW1*2-(GP1*2//), center = true);
//translate([0,CT1/2-(GP1*2),0])
//cube([BW1*2-(GP1*2),BW1*2-(GP1*2),10], center=true);
//}
//}

//plate
//translate([0,42.5,41])
module USB_holder(){
//difference(){
translate([-76,(CT1/2)+.5,46])
rotate([0,180,180])
#cube([USBL1,19,USBD1], center=false);

translate([76,(CT1/2)-18.5,46])
rotate([0,180,00])
#cube([USBL2,19,USBD2], center=false);

    //translate([0,CT1/2+3,0])
//cube([L1,BW1,CHH1], center=true);
//cylinder(h = CHH1, d1 = CT1, d2 = CT1, center = true);

//(-L1/2)+RX1,CT1/2+3+BW1*2/4,0
//translate([(L1/2)-LX1,22/2,0])
//plateroundends();
//translate([(-L1/2)+RX1,22/2,0])
//plateroundends();
}

module roundends(){
difference(){
translate([0,0,0])
cylinder(h = CHH1, d1 = BW1*2, d2 = BW1*2, center = true);
translate([0,BW1*2/2,0])
cube([BW1*2,BW1*2,CHH1], center=true);
}
}

module screwhole(){
union() {
rotate([90,0,0])
translate([0,0,0])
cylinder(h = SDH1, d1 = SD1, d2 = SD1, center = true);
rotate([90,0,0])
translate([0,0,SDH1/2+SHH1/2-SHH1])
cylinder(h = SHH1, d1 = SHD1, d2 = SHD1, center = true);
}
}

//shape of cupe remove
module cupehole(){
union() {
translate([0,0,5])
cylinder(h = CH1, d1 = CW1, d2 = CW1, center = true);
translate([0,-CT1/2,5])
cylinder(h = CH1, d1 = 22, d2 = 22*2, center = true);

translate([135/2,CT1/2+3,CH1/2+SHD1/4-(16+55)])
screwhole ();
translate([-135/2,CT1/2+3,CH1/2+SHD1/4-(16+55)])
screwhole ();

translate([0,69,-8])
rotate([90,0,0])
#cylinder(h = 31, d1 = 68, d2 = 68, center = true);

translate([135/2,69,+5])
rotate([90,0,0])
#cylinder(h = BW1-15, d1 = 40, d2 = 40, center = true);
  translate([-135/2,69,+5])
rotate([90,0,0])
#cylinder(h = BW1-15, d1 = 40, d2 = 40, center = true);

//logo();

//notch cab panel 23x16mm 24/17
translate([0,CT1/2+9+5,CH1/2-5.5])
cube([L1*2,24,17], center=true);
//0,42.5,41
translate([0,0,0])
USB_holder();

}
}

//cupehole();
module logo(){
rotate([0,0,0])
translate([-77,36,45])
scale([.18, .18, 1])
linear_extrude(height =2, center = true, convexity = 10, twist = 0)
import(file = "Sega_logo.svg");

}

//translate([0,0,0])
module cupeoutside(){
//main cup outside
translate([0,CT1/2+3,0])
cube([L1,BW1,CHH1], center=true);
cylinder(h = CHH1, d1 = CT1, d2 = CT1, center = true);

translate([(L1/2)-RX1,CT1/2+3+BW1*2/4,0])
roundends();
translate([(-L1/2)+LX1,CT1/2+3+BW1*2/4,0])
roundends();
}

difference(){
cupeoutside();
translate([0,0,0])
cupehole();

}

//gage blocks
//translate([0,0,6])
//%cube([5,170,80], center=true);

//translate([-135/2,50,2.5])
//%cube([170,170,55], center=true);
```
Lengend_USB_cupholder_customizable_v3.scad:
```
//made for Atgames Legends Ultimate arcade cabinet.
//acem77 1/4/2020
// added custom slots for storage or art

//curve resolution
$fn=50;

//Length of back panel
L1 = 155;

//Main Cup width 97
CW1 = 97;
//Main Cup height
CH1 = 86;

//USB depth left
USBD1 = 60;
//USB length
USBL1 = 37;

//USB depth right
USBD2 = 60;
//USB lenght
USBL2  = 37;

//Back slot no more for BS1 42 and BS2 24
BS1 = 0 ;
BS2 = 0 ;

//Main Right Round ends bring into center for smaller printer
LX1 = 15;
//Main Left Round ends bring into center for smaller printer
RX1 = 15;

//back wall 24+17+5 leave as is.
BW1 = 46;

//cup holder thinkness
CT1 = CW1+6;
//cup holder height
CHH1 = CH1+6;

//screw and head depth
SDH1 = 24+17+5;
//set depth -10
SHH1 = SDH1-10;

//screw head diameter
SHD1 = 12;
//screw diameter
SD1 = 7;

//module plateroundends(){
//difference(){
//translate([0,0,0])
    //d1 = 22*2-(GP1*2), d2 = 22*2-(GP1*2)
//cylinder(h = 10, d1 = BW1*2-(GP1*2), d2 = BW1*2-(GP1*2//), center = true);
//translate([0,CT1/2-(GP1*2),0])
//cube([BW1*2-(GP1*2),BW1*2-(GP1*2),10], center=true);
//}
//}

//plate
//translate([0,42.5,41])
module USB_holder(){
//difference(){
translate([-76,(CT1/2)+.5,46])
rotate([0,180,180])
cube([USBL1,19,USBD1], center=false);

translate([76,(CT1/2)-18.5,46])
rotate([0,180,00])
cube([USBL2,19,USBD2], center=false);

//back side
translate([150,(CT1/2)+2,25])
rotate([0,180,0])
#cube([300,BS2,BS1], center=false);

    //translate([0,CT1/2+3,0])
//cube([L1,BW1,CHH1], center=true);
//cylinder(h = CHH1, d1 = CT1, d2 = CT1, center = true);

//(-L1/2)+RX1,CT1/2+3+BW1*2/4,0
//translate([(L1/2)-LX1,22/2,0])
//plateroundends();
//translate([(-L1/2)+RX1,22/2,0])
//plateroundends();
}

module roundends(){
difference(){
translate([0,0,0])
cylinder(h = CHH1, d1 = BW1*2, d2 = BW1*2, center = true);
translate([0,BW1*2/2,0])
cube([BW1*2,BW1*2,CHH1], center=true);
}
}

module screwhole(){
union() {
rotate([90,0,0])
translate([0,0,0])
cylinder(h = SDH1, d1 = SD1, d2 = SD1, center = true);
rotate([90,0,0])
translate([0,0,SDH1/2+SHH1/2-SHH1])
cylinder(h = SHH1, d1 = SHD1, d2 = SHD1, center = true);
}
}

//shape of cupe remove
module cupehole(){
union() {
translate([0,0,5])
cylinder(h = CH1, d1 = CW1, d2 = CW1, center = true);
translate([0,-CT1/2,5])
cylinder(h = CH1, d1 = 22, d2 = 22*2, center = true);

translate([135/2,CT1/2+3,CH1/2+SHD1/4-(16+55)])
screwhole ();
translate([-135/2,CT1/2+3,CH1/2+SHD1/4-(16+55)])
screwhole ();
//logo();

//notch cab panel 23x16mm 24/17
translate([0,CT1/2+9+5,CH1/2-5.5])
cube([L1*2,24,17], center=true);
//0,42.5,41
translate([0,0,0])
USB_holder();

}
}

//cupehole();
module logo(){
rotate([0,0,0])
translate([-77,36,45])
scale([.18, .18, 1])
linear_extrude(height =2, center = true, convexity = 10, twist = 0)
import(file = "Sega_logo.svg");

}

//translate([0,0,0])
module cupeoutside(){
//main cup outside
translate([0,CT1/2+3,0])
cube([L1,BW1,CHH1], center=true);
cylinder(h = CHH1, d1 = CT1, d2 = CT1, center = true);

translate([(L1/2)-RX1,CT1/2+3+BW1*2/4,0])
roundends();
translate([(-L1/2)+LX1,CT1/2+3+BW1*2/4,0])
roundends();
}

difference(){
cupeoutside();
translate([0,0,0])
cupehole();

}

//gage blocks
//translate([0,0,6])
//%cube([5,170,80], center=true);

//translate([-135/2,50,2.5])
//%cube([170,170,55], center=true);
```


[User]
I'd like a parametric OpenSCAD design for a Raspberry Pi rack mount, optimized for a cluster of Raspberry Pi 4s or 5s with the official PoE hats. It should fit in a standard 19-inch server rack.

Here's what I'm envisioning:

*   **Capacity:** I'd like a parameter to adjust the number of Pis it holds. The default should be 12, which fits nicely in a 2U space with PoE hats. But I also want the option to increase it to 13 (still with PoE hats) or 14 (if I ever remove the hats), so those should be easy options.

*   **Frame:**
    *   The frame will be constructed with long threaded rods running vertically through the corners to hold everything together.
    *   I need a parameter to set the bolt size for these rods. I'll be using #10 rods (2.38125mm diameter) but want to be able to choose between other standard sizes like 1/4 inch (3.175mm) or #12 (2.778125mm) later.
    *   The bolt holes need some fudge factor to make assembly easier (parameter called `bolt_hole_fudge`). 0.2 is a good starting point.
    *   There should also be notches and tabs to stack the frames vertically. I'd like to be able to turn those notches off for one frame that will be next to the ear pieces. There should also be some `notch_fudge` to provide a comfortable fit, 0.3 is a good starting point.

*   **Trays:**
    *   The trays need to slide into the frame.
    *   I need a fudge factor for the tray width to ensure it fits nicely, let's call it `tray_insert_fudge` (0.25 sounds good as a starting point).
    *   The trays should have screw holes for mounting the Pi. I need a `screw_hole_fudge` parameter for these holes (0.15 should work fine) so the screws aren't too tight. I'm using M2.5 screws.
    *   The trays also need a cutout for the SD card, and another for the power/network ports, and should be designed with cutouts for the PoE hats.

*   **Ears:**
    *   The ear pieces will attach to the side of the rack.
    *   They need the same bolt hole parameters as the frames, and I'd like the option to omit the notches for one of the ears.
    *   Also, there should be bolt holes on the ears for mounting to the rack itself. These should be elongated to allow for some slop (parameter is `ear_bolt_stretch`) and should have a radius (parameter is `ear_bolt_size`).
    *   The default is M6, and I'd like them to have some wiggle room, so `ear_bolt_stretch` should be 3.

I think that's it for now. I'm thinking three files: one for the rack frame, one for the tray, and one for the ears, but I'm open to suggestions if you think it would work better some other way. Let me know if you need any clarification.

I don't have any libraries in mind, so you're free to pick the ones you think are best. I'd prefer it if you only relied on the standard libraries, though.

[Assistant]
raspberry-pi-rack-frame.scad:
```
/* [Bolt holes in the corners] */

// Pick the size of the long threaded rods that run through
// the corners and hold everything together.
//
// These are the sizes that are commonly available at
// Home Depot or Lowes:
//
// 1/4 inch: 3.175
// #12     : 2.778125
// #10     : 2.38125 (default and recommended)
//
// Note: you should pick the same size and fudge factor
// for the ear parts.
bolt_size = 2.38125;

// Make the bolt holes this much bigger than the size of the bolt
// to give a more comfortable fit
bolt_hole_fudge = 0.2; // [0:0.25:1]

/* [Notches and tabs for stacking] */

// You can optionally omit the notches for the frame on the end
// that joins with an ear with no tabs. For all of the others
// you should leave this at its default of true.
notches_enabled = true;

// Make the slot this much taller than the tabs
// to give a more comfortable fit.
notch_fudge = 0.3; // [0:0.25:1]

/* [Size] */

// The default is to fit 12 units with PoE hats in a standard rack.
// 12 is a good fit, and it works nicely with many PoE switches, which
// often come with 12 or 24 ports. You can bump it up to 13 units and
// they will still fit nicely with PoE hats, or you can go up to 14
// of you are not using the hats.
number_to_fit = 12; // [12:Relaxed fit, 13:Using PoE, 14:Without PoE hats]

/* [Hidden] */
// make the screw holes this much bigger than the
// actual screw for a more comfortable fit
// (a bigger number here will make the screws fit looser)
screw_hole_fudge = 0.15;

height = (450.85 - 20) / number_to_fit;

width = 82;
length = 90;

// The height of the notches and tabs
notch_height = 2;

// epsilon is used to slightly overlap pieces to help the previewer
epsilon = 0.001;

bolt_radius = bolt_size + bolt_hole_fudge;

outer_wall_thickness = 12;
inner_wall_thickness = 7;
back_wall_thickness = 5;
wall_diff = outer_wall_thickness - inner_wall_thickness;
floor_depth = 3;
tray_depth = 5;
tray_slot_depth = 2.5;
spacer_depth = 3;

sd_window_width = 40;
sd_window_height = height - (tray_depth + floor_depth)*2;
near_pillar_width = 15;
far_pillar_width = 10;
floor_window_width = sd_window_width;
floor_window_border = (width - floor_window_width - outer_wall_thickness*2) / 2;
floor_window_length = length - back_wall_thickness - floor_window_border*2;
port_window_width = length - near_pillar_width - far_pillar_width;
port_window_frame_height = floor_depth + tray_depth + spacer_depth - notch_height;

difference() {
    union() {
        // the main block
        cube([width, length, height]);

        // the tabs
        translate([ wall_diff,
                    near_pillar_width + wall_diff,
                    height - epsilon]) {
            cube([  inner_wall_thickness - 2,
                    length - near_pillar_width - far_pillar_width - wall_diff*2,
                    notch_height + epsilon]);
        }
        translate([ width - outer_wall_thickness + 2,
                    near_pillar_width + wall_diff,
                    height - epsilon]) {
            cube([  inner_wall_thickness - 2,
                    length - near_pillar_width - far_pillar_width - wall_diff*2,
                    notch_height + epsilon]);
        }
    }

    // carve out the interior
    translate([outer_wall_thickness, -epsilon, floor_depth]) {
        cube(  [width - outer_wall_thickness*2,
                length - back_wall_thickness + epsilon,
                height + notch_height - floor_depth + epsilon]);
    }

    // punch a hole for the sd card
    translate([ (width - sd_window_width) / 2,
                length - back_wall_thickness - epsilon,
                tray_depth + floor_depth]) {
        cube([  sd_window_width,
                back_wall_thickness + 2*epsilon,
                height - port_window_frame_height - tray_depth - floor_depth + notch_height]);
    }

    // punch a hole for side port access
    translate([ -epsilon,
                near_pillar_width,
                port_window_frame_height + notch_height]) {
        cube([  width + 2*epsilon,
                port_window_width,
                height - port_window_frame_height*2]);
    }

    // punch a hole in the bottom
    translate([ outer_wall_thickness + floor_window_border,
                floor_window_border,
                -epsilon]) {
        cube([  floor_window_width,
                floor_window_length,
                floor_depth + 2*epsilon]);
    }

    // open a groove that the speaker jack can slide through
    translate([ outer_wall_thickness - 2,
                -epsilon,
                port_window_frame_height + notch_height]) {
        cube([  2 + epsilon,
                near_pillar_width + 2*epsilon,
                8]);
    }

    // soften the leading edge a bit
    translate([ outer_wall_thickness,
                0,
                floor_depth-0.5]) {
        rotate([25,0,0]) {
            cube([  width - outer_wall_thickness*2,
                    2,
                    2]);
        }
    }

    if (notches_enabled) {
        // cut the notches in the bottom
        translate([ -epsilon,
                    near_pillar_width + wall_diff,
                    -epsilon]) {
            cube([  outer_wall_thickness - 2 + epsilon,
                    length - near_pillar_width - far_pillar_width - wall_diff*2,
                    notch_height + notch_fudge + epsilon]);
        }
        translate([ width - outer_wall_thickness + 2,
                    near_pillar_width + wall_diff,
                    -epsilon]) {
            cube([  outer_wall_thickness - 2 + epsilon,
                    length - near_pillar_width - far_pillar_width - wall_diff*2,
                    notch_height + notch_fudge + epsilon]);
        }
    }

    // cut out the sides to make them thinner
    polyhedron(
        points = [
            [ wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon],
            [ wall_diff, near_pillar_width+wall_diff,       height+notch_height+epsilon],
            [ wall_diff, length-far_pillar_width-wall_diff, -epsilon],
            [ wall_diff, near_pillar_width+wall_diff,       -epsilon],
            [-wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon],
            [-wall_diff, near_pillar_width-wall_diff,       height+notch_height+epsilon],
            [-wall_diff, length-far_pillar_width+wall_diff, -epsilon],
            [-wall_diff, near_pillar_width-wall_diff,       -epsilon]
        ],
        faces = [
            [1,0,2,3],
            [4,5,7,6],
            [0,4,6,2],
            [6,7,3,2],
            [7,5,1,3],
            [5,4,0,1]
        ],
        convexity = 10);
    polyhedron(
        points = [
            [width-wall_diff, near_pillar_width+wall_diff,       height+notch_height+epsilon],
            [width-wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon],
            [width-wall_diff, near_pillar_width+wall_diff,       -epsilon],
            [width-wall_diff, length-far_pillar_width-wall_diff, -epsilon],
            [width+wall_diff, near_pillar_width-wall_diff,       height+notch_height+epsilon],
            [width+wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon],
            [width+wall_diff, near_pillar_width-wall_diff,       -epsilon],
            [width+wall_diff, length-far_pillar_width+wall_diff, -epsilon]
        ],
        faces = [
            [1,0,2,3],
            [4,5,7,6],
            [0,4,6,2],
            [6,7,3,2],
            [7,5,1,3],
            [5,4,0,1]
        ],
        convexity = 10);

    // drill the bolt holes
    for (a=[[outer_wall_thickness/2, near_pillar_width/2],
            [outer_wall_thickness/2, length - far_pillar_width/2],
            [width - outer_wall_thickness/2, near_pillar_width/2],
            [width - outer_wall_thickness/2, length - far_pillar_width/2]]) {
        translate([a[0], a[1], -epsilon]) {
            cylinder(   h=height + notch_height + 2*epsilon,
                        r=bolt_radius,
                        center=false,
                        $fn=360);
        }
    }

    // cut the tray insert slots
    // note: the tray will be slightly narrower to
    // give a more comfortable fit. If it is too loose or too tight,
    // adjust the tray.
    for (a=[    outer_wall_thickness,
                width - outer_wall_thickness]) {
        translate([ a,
                    -epsilon,
                    floor_depth + tray_depth/2]) {
            intersection() {
                rotate([0, 45, 0]) {
                    translate([-tray_depth/sqrt(2)/2, 0, -tray_depth/sqrt(2)/2]) {
                        cube([  tray_depth/sqrt(2),
                                length - back_wall_thickness + epsilon,
                                tray_depth/sqrt(2)]);
                    }
                }
                translate([-tray_depth/2+0.5, 0, -tray_depth/2]) {
                    cube([  tray_depth-1,
                            length - back_wall_thickness + epsilon,
                            tray_depth]);
                }
            }
        }
    }
}

```
raspberry-pi-rack-tray.scad:
```
/* [Screw holes for attaching the Raspberry Pi 4] */

// Make the screw holes this much bigger than the
// actual screw for a more comfortable fit
// (a bigger number here will make the screws fit looser)
screw_hole_fudge = 0.15; // [0:0.05:0.5]

/* [Tray width] */

// Make the tray this much narrower on each side
// for a more comfortable fit in the frame
// (a bigger number here will make the fit looser)
tray_insert_fudge = 0.25; // [0:0.05:0.75]

/* [Hidden] */

tray_width = 58;
tray_length = 85;
tray_depth = 5;
tray_lip_overhang = 10;
inner_wall_thickness = 5;
spacer_depth = 3;
spacer_outer_radius = 3;

screw_radius = 1.25 + screw_hole_fudge;
screw_head_radius = 3;
screw_head_depth = 2;
pcb_depth = 2;
spacer_center_width = 49;
spacer_center_length = 58;
spacer_from_edge = (tray_width - spacer_center_width) / 2;

sd_window_width = 40;
floor_window_width = sd_window_width;
floor_window_border = (tray_width - floor_window_width) / 2;
floor_window_length = tray_length - floor_window_border*2 - inner_wall_thickness;

epsilon = 0.001;

difference() {
    union() {
        // the main tray
        intersection() {
            union() {
                translate([tray_insert_fudge, 0, 0]) {
                    cube([  tray_width - 2*tray_insert_fudge,
                            tray_length + tray_lip_overhang,
                            tray_depth]);
                }

                difference() {
                    // the outer part of the curved handle
                    translate([ tray_width/2,
                                -tray_length*0.5 + 7.5,
                                0]) {
                        cylinder(
                            h=tray_depth + spacer_depth + pcb_depth,
                            r=tray_length*1.5,
                            center=false,
                            $fn=360);
                    }

                    // cut away the inside to make it a shell
                    translate([ tray_width/2,
                                -tray_length*0.5 + 5.4,
                                -epsilon]) {
                        cylinder(
                            h=tray_depth + spacer_depth + pcb_depth + 2*epsilon,
                            r=tray_length*1.5,
                            center=false,
                            $fn=360);
                    }
                    translate([ tray_width,
                                -tray_length,
                                -epsilon]) {
                        cube([  2*tray_width,
                                3*tray_length,
                                tray_depth + spacer_depth + pcb_depth + 2*epsilon]);
                    }
                    translate([ -2*tray_width,
                                -tray_length,
                                -epsilon]) {
                        cube([  2*tray_width,
                                3*tray_length,
                                tray_depth + spacer_depth + pcb_depth + 2*epsilon]);
                    }
                }

                // reinforce the curved handle
                translate([epsilon, tray_length, tray_depth]) {
                    rotate([-75, 0, 0]) {
                        cube([tray_width - 2*epsilon, 2*sqrt(2), 6]);
                    }
                }

            }

            // cut away the ears of the tray
            // where they jut through the curved handle
            translate([ tray_width/2,
                        -tray_length*0.5 + 7.5,
                        0]) {
                cylinder(
                    h=tray_depth + spacer_depth + pcb_depth,
                    r=tray_length*1.5,
                    center=false,
                    $fn=360);
            }
        }

        // place the 4 spacers
        for (a=[    [spacer_from_edge,
                        spacer_from_edge],
                    [spacer_from_edge + spacer_center_width,
                        spacer_from_edge],
                    [spacer_from_edge,
                        spacer_from_edge + spacer_center_length],
                    [spacer_from_edge + spacer_center_width,
                        spacer_from_edge + spacer_center_length]]) {
            translate([a[0], a[1], tray_depth-epsilon]) {
                // place the spacer
                cylinder(
                    h=spacer_depth + epsilon,
                    r=spacer_outer_radius,
                    center=false,
                    $fn=360);

                // place a small cone around it
                cylinder(
                    h=2 + epsilon,
                    r1=spacer_outer_radius + 1,
                    r2=spacer_outer_radius,
                    center=false,
                    $fn=360);
            }
        }

        // the tray insert edge tabs
        for (a=[    tray_insert_fudge,
                    tray_width - tray_insert_fudge]) {
            translate([ a,
                        0,
                        tray_depth/2]) {
                intersection() {
                    rotate([0, 45, 0]) {
                        translate([-tray_depth/sqrt(2)/2, 0, -tray_depth/sqrt(2)/2]) {
                            cube([  tray_depth/sqrt(2),
                                    tray_length,
                                    tray_depth/sqrt(2)]);
                        }
                    }
                    translate([-tray_depth/2+0.5, 0, -tray_depth/2]) {
                        cube([  tray_depth-1,
                                tray_length,
                                tray_depth]);
                    }
                }
            }
        }
    }

    // punch a hole in the bottom
    translate([ floor_window_border,
                floor_window_border,
                -epsilon]) {
        cube([  floor_window_width,
                floor_window_length,
                tray_depth + 2*epsilon]);
    }

    // drill the 4 screw holes
    for (a=[    [spacer_from_edge,
                    spacer_from_edge],
                [spacer_from_edge + spacer_center_width,
                    spacer_from_edge],
                [spacer_from_edge,
                    spacer_from_edge + spacer_center_length],
                [spacer_from_edge + spacer_center_width,
                    spacer_from_edge + spacer_center_length]]) {
        translate([a[0], a[1], -epsilon]) {
            // drill the main screw hole
            cylinder(
                h=tray_depth + spacer_depth + 2*epsilon,
                r=screw_radius,
                center=false,
                $fn=360);

            // drill a recessed hole for the screw head
            cylinder(
                h=screw_head_depth + epsilon,
                r=screw_head_radius,
                center=false,
                $fn=360);
        }
    }
}
```
raspberry-pi-rack-ears.scad:
```
/* [Bolt holes in the corners] */

// Pick the size of the long threaded rods that run through
// the corners and hold everything together.
//
// These are the sizes that are commonly available at
// Home Depot or Lowes:
//
// 1/4 inch: 3.175
// #12     : 2.778125
// #10     : 2.38125 (default and recommended)
//
// Note: you should pick the same size and fudge factor
// for the frame parts.
bolt_size = 2.38125;

// Make the bolt holes this much bigger than the size of the bolt
// to give a more comfortable fit
bolt_hole_fudge = 0.2; // [0:0.25:1]

/* [Notches for attaching to the frame parts] */

// You can optionally omit the notches for one ear that will
// join with a frame on the side that has no tabs.
// For the the other you should leave this at its default of true.
notches_enabled = true;

// Make the slot this much taller than the tabs
// to give a more comfortable fit.
notch_fudge = 0.3; // [0:0.25:1]

/* [Bolt holes for mounting to the rack] */

// The size of the bolt holes for mounting to the server rack.
// The default is for M6 bolts.
ear_bolt_size = 3;

// Make the ear bolt holes this much longer than they are wide
// in case the entire assembled rack is not precisely the right
// length.
ear_bolt_stretch = 3;

/* [Hidden] */

width = 82;
length = 90;
height = 5;
frame_width = 15.875;
distance_to_frame = 10;
distance_between_bolt_holes = 15.875 * 2 + 12.7;
ear_height = distance_to_frame + frame_width;

notch_height = 2;
outer_wall_thickness = 12;
inner_wall_thickness = 7;
back_wall_thickness = 5;
wall_diff = outer_wall_thickness - inner_wall_thickness;
floor_depth = 3;
tray_depth = 5;
tray_slot_depth = 2.5;
spacer_depth = 3;
epsilon = 0.001;

sd_window_width = 40;
sd_window_height = height - (tray_depth + floor_depth)*2;
near_pillar_width = 15;
far_pillar_width = 10;
floor_window_width = sd_window_width;
floor_window_border = (width - floor_window_width - outer_wall_thickness*2) / 2;
floor_window_length = length - back_wall_thickness - floor_window_border*2;
port_window_width = length - near_pillar_width - far_pillar_width;
port_window_frame_height = floor_depth + tray_depth + spacer_depth - notch_height;

bolt_radius = bolt_size + bolt_hole_fudge;

ear_bolt_radius = ear_bolt_size + bolt_hole_fudge;
floor_mini_window_width = (floor_window_width - floor_window_border) / 2;
floor_mini_window_length = (floor_window_length - floor_window_border) / 2;

difference() {
    union() {
        // the main tray block
        translate([0, -epsilon, 0]) {
            cube([width, length + epsilon, height]);
        }

        // the ear
        translate([0, -height, 0]) {
            cube([width, height, ear_height]);
        }

        // reinforce the joint
        translate([0, 0, 0]) {
            rotate([45, 0, 0]) {
                cube([width, height-0.5, height-0.5]);
            }
        }

        translate([ (width - distance_between_bolt_holes)/2 - ear_bolt_radius, 0, 0]) {
            rotate([45, 0, 0]) {
                cube([distance_between_bolt_holes + 2*ear_bolt_radius, height+1, height+1]);
            }
        }
    }

    // cut the bolt holes
    for (a=[[outer_wall_thickness/2, near_pillar_width/2],
            [outer_wall_thickness/2, length - far_pillar_width/2],
            [width - outer_wall_thickness/2, near_pillar_width/2],
            [width - outer_wall_thickness/2, length - far_pillar_width/2]]) {
        translate([a[0], a[1], -epsilon]) {
            cylinder(   h=height + 2*epsilon,
                        r=bolt_radius,
                        center=false,
                        $fn=360);
        }
    }

    // make the sides thinner
    polyhedron(
        points = [
            [ wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon],
            [ wall_diff, near_pillar_width+wall_diff,       height+notch_height+epsilon],
            [ wall_diff, length-far_pillar_width-wall_diff, -epsilon],
            [ wall_diff, near_pillar_width+wall_diff,       -epsilon],
            [-wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon],
            [-wall_diff, near_pillar_width-wall_diff,       height+notch_height+epsilon],
            [-wall_diff, length-far_pillar_width+wall_diff, -epsilon],
            [-wall_diff, near_pillar_width-wall_diff,       -epsilon]
        ],
        faces = [
            [1,0,2,3],
            [4,5,7,6],
            [0,4,6,2],
            [6,7,3,2],
            [7,5,1,3],
            [5,4,0,1]
        ],
        convexity = 10);
    polyhedron(
        points = [
            [width-wall_diff, near_pillar_width+wall_diff,       height+notch_height+epsilon],
            [width-wall_diff, length-far_pillar_width-wall_diff, height+notch_height+epsilon],
            [width-wall_diff, near_pillar_width+wall_diff,       -epsilon],
            [width-wall_diff, length-far_pillar_width-wall_diff, -epsilon],
            [width+wall_diff, near_pillar_width-wall_diff,       height+notch_height+epsilon],
            [width+wall_diff, length-far_pillar_width+wall_diff, height+notch_height+epsilon],
            [width+wall_diff, near_pillar_width-wall_diff,       -epsilon],
            [width+wall_diff, length-far_pillar_width+wall_diff, -epsilon]
        ],
        faces = [
            [1,0,2,3],
            [4,5,7,6],
            [0,4,6,2],
            [6,7,3,2],
            [7,5,1,3],
            [5,4,0,1]
        ],
        convexity = 10);

    if (notches_enabled) {
        // cut the notches
        translate([ wall_diff - epsilon,
                    near_pillar_width + wall_diff,
                    -epsilon]) {
            cube([  inner_wall_thickness-2 + epsilon,
                    length - near_pillar_width - far_pillar_width - 2*wall_diff,
                    notch_height + notch_fudge + epsilon]);
        }
        translate([ width - wall_diff - (inner_wall_thickness-2),
                    near_pillar_width + wall_diff,
                    -epsilon]) {
            cube([  inner_wall_thickness-2 + epsilon,
                    length - near_pillar_width - far_pillar_width - 2*wall_diff,
                    notch_height + notch_fudge + epsilon]);
        }
    }

    // cut some mini windows in the floor
    for (a=[[outer_wall_thickness + floor_window_border,
                floor_window_border],
            [width - outer_wall_thickness - floor_window_border - floor_mini_window_width,
                floor_window_border],
            [outer_wall_thickness + floor_window_border,
                2*floor_window_border + floor_mini_window_length],
            [width - outer_wall_thickness - floor_window_border - floor_mini_window_width,
                2*floor_window_border + floor_mini_window_length]]) {
        translate([a[0], a[1], -epsilon]) {
            cube([  floor_mini_window_width,
                    floor_mini_window_length,
                    height + 2*epsilon]);
        }
    }

    // cut the bolt holes on the ear
    for (a=[    width/2 - distance_between_bolt_holes/2,
                width/2 + distance_between_bolt_holes/2]) {
        for (b=[-ear_bolt_stretch/2, ear_bolt_stretch/2]) {
            translate([ a,
                        -height-epsilon,
                        ear_height - frame_width/2 + b]) {
                rotate([-90,0,0]) {
                    cylinder(   h=height + 2*epsilon,
                                r=ear_bolt_radius,
                                center=false,
                                $fn=360);
                }
            }
        }
        translate([ a - ear_bolt_radius,
                    -height-epsilon,
                    ear_height - frame_width/2 - ear_bolt_stretch/2]) {
            cube([  ear_bolt_radius*2,
                    height + 2*epsilon,
                    ear_bolt_stretch]);
        }
    }
}
```


[User]
I want to design a set of coasters that look like element cards from the periodic table, but I need the design to be parametric so I can easily change the element information. Each coaster should be a square with a raised brim to contain spills. The card should display the element's name, symbol, atomic number, and atomic weight.

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

*   **Shape:** A square coaster with a raised brim around the perimeter.
*   **Text:** The element's name should be centered at the bottom of the square. The element's symbol should be large and centered. The atomic number should be in the top left corner, and the atomic weight in the top right. All text should be raised.
*   **Dimensions:**
    *   The square coaster should have a customizable side length (e.g., 3.5 inches).
    *   The brim should be a bit narrower than the square's side length, and it should have a customizable height.
    *   The text height and sizes should also be customizable.
*   **Customizable Values:** I need to be able to easily change the following:
    *   Element name (string)
    *   Element symbol (string)
    *   Atomic number (string)
    *   Atomic weight (string)
    *   Square side length
    *   Brim height
    *   Text height
    *   Text sizes (ideally set per text element).

I'm hoping you can generate a parametric OpenSCAD file (or files) for me that will create this element coaster. I'd like to be able to change the element data and dimensions via variables, so it will be easy to create multiple coasters. Thanks!

[Assistant]
element_coasters.scad:
```
//Creates a periodic table coaster given specified element values.

function inches(val) = val * 25.4; //inch conversion

elem_name = "Manganese";  //customizable parameters
elem_number = "25";
elem_symbol = "Mn";
atomic_weight = "54.94";

module letter(l, s, j) {
  // Use linear_extrude() to make the letters 3D objects
  linear_extrude(height = text_height) {
    text(l, size = s, halign = j, valign = "center", $fn = 16);
  }
}

square_size = inches(3.5);
p = square_size / 2;            //for creating brim
q = p - 1;                      //for creating brim
brim_points = [[p,p],[p,-p],[-p,-p],[-p,p],[q,q],[q,-q],[-q,-q],[-q,q]];
brim_paths = [[0,1,2,3],[4,5,6,7]];
brim_height = 3;
square_height = 3;
text_height = 2.5;

//Value used for placing text
a = square_size / 3;

union() {
    color("gray") cube([square_size, square_size, square_height], center = true);
    letter(str(elem_symbol), 35, "center");
    translate([-37, 32, 0]) letter(str(elem_number), 10, "left");
    translate([37, 32, 0]) letter(str(atomic_weight), 10, "right");
    translate([0, -a, 0]) letter(str(elem_name), 9, "center");
    linear_extrude(height = brim_height) {  //extrude brim
        polygon(brim_points, brim_paths, convexity = 10);
    }
}

```


[User]
I need an OpenSCAD design for a hose adaptor tee. I've found one that's pretty close, but I want to be able to customize all the dimensions, and I'd like it generated from scratch. I need three hose connection points: two in-line, and one coming off the side.

Here's the breakdown of what I need to control parametrically:

*   **Outer Diameters:** I need to be able to specify the outer diameter for each of the three connection points individually. Let's call them `outer_diameter_1` (for one end of the in-line connections), `outer_diameter_2` (for the other end of the in-line connection) and `outer_diameter_3` (for the side connection).
*   **Wall Thicknesses:** I need to be able to set the wall thickness for each of the three connection points. Let's call these `wall_thickness_1`, `wall_thickness_2`, and `wall_thickness_3`, corresponding to the outer diameters.
*   **Barb Sizes:** Each connection point should also have barbs. I need to be able to control the size/depth of these barbs. Let's use `barb_size_1`, `barb_size_2`, and `barb_size_3`. These should be triangles that extend out from the outside diameter.  I want the barbs to have their tips point in the direction of the hose connector, like the example.
*   **Lengths:** I need to control the length of each hose connector section; let's call these `length_1`, `length_2`, and `length_3` for the in-line ends and the side connector respectively.
*   **Middle Length:**  Finally, the length of the tee section between the in-line ends, call it `middle_length`, also needs to be controllable. This length needs to be at least as long as the outer diameter of side connector, `outer_diameter_3`, but might need to be longer.

It'd be great if the design automatically determined which of the in-line connections was wider. I need to be able to specify them individually and not have to worry about setting which was on which end. I'd like the code to use `top_od` and `bottom_od`, as well as associated `wall_thickness`, `barb_size`, and `length` parameters to refer to the two in-line connectors, with `top_` referring to whichever has a larger `outer_diameter`. `bottom_` should refer to the connector with the smaller `outer_diameter`.

For the main body of the tee, the inner diameters should match up to the inner diameters of the hose connectors. The transition from the different diameters should use conical sections and be centered.

I'm hoping for a single .scad file that generates this shape with all these parameters. I also don't need any external libraries for this one. Thanks!

[Assistant]
Hose_Adaptor_Tee.scad:
```
////////////////////////////////////////////////////////
// Created by Paul Tibble - 18/7/19                   //
// https://www.thingiverse.com/Paul_Tibble/about      //
// Please consider tipping, if you find this useful.  //
////////////////////////////////////////////////////////

$fn = 100*1;

// Outer Diameter #1 (top/bottom)
outer_diameter_1 = 15;
// Wall Thickness #1 (top/bottom)
wall_thickness_1 = 2;
// Rib Thickness #1 (top/bottom), set to Zero to remove
barb_size_1 = 0.5;
// Length #1 (top/bottom)
length_1 = 15;
// Outer Diameter #2 (top/bottom)
outer_diameter_2 = 20;
// Wall Thickness #2 (top/bottom)
wall_thickness_2 = 1;
// Rib Thickness #2 (top/bottom), set to Zero to remove
barb_size_2 = 0.5;
// Length #2 (top/bottom)
length_2 = 15;
// Outer Diameter #3 (side)
outer_diameter_3 = 8;
// Wall Thickness #3 (side)
wall_thickness_3 = 1;
// Rib Thickness #3 (side), set to Zero to remove
barb_size_3 = 0.5;
// Length #3 (side)
length_3 = 16;
// Middle Length, cannot be less than Outer Diameter #3 (side)
middle_length = 15;

//do not change these

top_od = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? outer_diameter_1 : outer_diameter_2;
top_wall_thickness = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? wall_thickness_1 : wall_thickness_2;
top_barb = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? barb_size_1 : barb_size_2;
top_length = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? length_1 : length_2;
bottom_od = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? outer_diameter_2 : outer_diameter_1;
bottom_wall_thickness = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? wall_thickness_2 : wall_thickness_1;
bottom_barb = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? barb_size_2 : barb_size_1;
bottom_length = outer_diameter_1 != max(outer_diameter_1,outer_diameter_2) ? length_2 : length_1;
mid_length = max(middle_length,outer_diameter_3);

bottom_id = bottom_od - (bottom_wall_thickness*2);
top_id = top_od - (top_wall_thickness*2);
side_id = outer_diameter_3 - (wall_thickness_3*2);

module create_profile1() {
    //////
    //length 1
    /////
    translate([bottom_id/2,0,0])square([bottom_wall_thickness,bottom_length]);
    //barb 1
    translate([bottom_od/2,0,0])polygon(points=[[0,0],[0,(bottom_length/5)],[bottom_barb,(bottom_length/5)]]);
    //barb 2
    translate([bottom_od/2,bottom_length*0.25,0])polygon(points=[[0,0],[0,(bottom_length/5)],[bottom_barb,(bottom_length/5)]]);
    //barb 3
    translate([bottom_od/2,bottom_length*0.5,0])polygon(points=[[0,0],[0,(bottom_length/5)],[bottom_barb,(bottom_length/5)]]);
}
module create_profile2() {
    //////
    //length 2
    /////
    translate([top_id/2,0,0])square([top_wall_thickness,top_length]);
    //barb 1
    translate([top_od/2,0,0])polygon(points=[[0,0],[0,(top_length/5)],[top_barb,(top_length/5)]]);
    //barb 2
    translate([top_od/2,top_length*0.25,0])polygon(points=[[0,0],[0,(top_length/5)],[top_barb,(top_length/5)]]);
    //barb 3
    translate([top_od/2,top_length*0.5,0])polygon(points=[[0,0],[0,(top_length/5)],[top_barb,(top_length/5)]]);
}
module create_profile3() {
    //////
    //length 3
    /////
    translate([side_id/2,0,0])square([wall_thickness_3,length_3]);
    //barb 1
    translate([outer_diameter_3/2,0,0])polygon(points=[[0,0],[0,(length_3/5)],[barb_size_3,(length_3/5)]]);
    //barb 2
    translate([outer_diameter_3/2,length_3*0.25,0])polygon(points=[[0,0],[0,(length_3/5)],[barb_size_3,(length_3/5)]]);
    //barb 3
    translate([outer_diameter_3/2,length_3*0.5,0])polygon(points=[[0,0],[0,(length_3/5)],[barb_size_3,(length_3/5)]]);
}

//////
//length 1
/////
rotate_extrude(angle = 360, convexity = 10) create_profile1();

////////
// Middle
///////
translate([0,0,bottom_length]){
    difference(){
        union(){
            cylinder(h=mid_length,d1=bottom_od,d2=top_od);
            translate([0,0,mid_length/2]){
                rotate([0,90,0]){
                    cylinder(h=(mid_length+(top_od+bottom_od)/2)/2,d1=min(mid_length,(top_od+bottom_od)/2),d2=outer_diameter_3);
                }
            }
        }
        union(){
            cylinder(d1=bottom_id,d2=top_id,h=mid_length);
            translate([0,0,-bottom_length]){
    cylinder(d=min(bottom_id,top_id),h=bottom_length+mid_length+top_length);
            }
            translate([0,0,mid_length/2]){
                rotate([0,90,0]){
                    cylinder(h=(mid_length+(top_od+bottom_od)/2)/2,d1=max(min(mid_length-wall_thickness_3,(top_id+bottom_id)/2.5),side_id),d2=side_id);
                }
            }
        }
    }

    //////
    //length 2
    /////
    translate([0,0,top_length+mid_length]){
        rotate([0,180,0]){
            rotate_extrude(angle = 360, convexity = 10) create_profile2();
        }
    }

    //////
    //length 3
    /////
    translate([length_3+((top_od+bottom_od)/2+mid_length)/2,0,mid_length/2]){
        rotate([180,90,0]){
            rotate_extrude(angle = 360, convexity = 10) create_profile3();
        }
    }
}
```


[User]
Hey, I'm trying to make a custom tie rod end for a project, and I found this OpenSCAD file online that's pretty close to what I need, but I'd like to make some changes and have a more robust, parametric version. I want to be able to customize the rod size, the screw size (for the ball joint), the engagement length of the rod, the wall thickness, and the tolerance between the ball and the socket. I'd also like a way to adjust the strength of the ball joint connection and the resolution of the curves.

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

**Core Components:**

*   **Rod Holder:** A cylindrical section that holds the rod. I need to be able to define the rod thread diameter, the wall thickness around it, and the length of engagement.
*   **Ball Joint Socket:** A socket that holds the ball, and which has a screw thread on the end. This part needs to be able to have its thread diameter customized and also the wall thickness defined. The socket should also be rotated 90 degrees relative to the rod holder.
*   **Ball:** A sphere with a cylindrical section extending from it to mimic a screw/stud. The sphere part of the ball needs to be able to be customized based on the screw thread diameter, and the wall thickness and a cylinder should be added to the end.

**Parameters to Customize:**

*   `Rod_Thread`: Diameter of the rod thread.
*   `Screw_Thread`: Diameter of the screw thread for the ball joint.
*   `Engagement`: Length of rod engagement.
*   `Wall_Width`: Thickness of the walls in the rod holder and socket.
*   `Tolerance`: Clearance between the ball and socket.
*   `Hold_Multiplier`: This is supposed to adjust how tight the ball joint socket is, I'm not really sure how best to do this.
*   `Resolution`: Resolution of the curves used for cylinders and spheres (or `fn` parameter).

I'd really like this to be in OpenSCAD files. Could you please help me create this? I need to be able to adjust everything using the parameters mentioned. I’m hoping you can generate it so that all the parts can be controlled parametrically. I don't need any libraries beyond the standard OpenSCAD features.

[Assistant]
Nimrod_v1.scad:
```
//Nimrod - A Rod End Creator
//Credit to Ryan Witschger

//Rod thread size?
Rod_Thread = 3.80; // [.05:.05:20]

//Screw thread size (Ball Side)?
Screw_Thread = 3; //   [.05:.05:20]

//Total engadement on the rod in mm
Engagement = 20; // [4:55]

//Desired Wall Thickness in mm?
Wall_Width = 1.2; //[0.5:0.05:6]

//Desired Gap / Tolerance (Printer Capability)?
Tolerance = 0.1; //  [0.05:0.05:0.4]

//Make the connection stronger or more flexible?
Hold_Multiplier = 1.0; // [0.4:0.1:1.7]

Resolution = 72; //[45:"Course", 60:"Low", 72:"Medium", 90:"High"]

Screw_Center = Engagement + (Rod_Thread / 2) +Tolerance + Screw_Thread + (Wall_Width * 2);

//Make the rod holder

difference(){

cylinder(h = (Engagement), d = (Rod_Thread + (Wall_Width * 2)), center = false, $fn=Resolution);

cylinder(h = (Engagement*2), d = (Rod_Thread), center = true, $fn=Resolution);

}

//Add a sphere to connect

translate([0,0,Engagement]) sphere(d = (Rod_Thread + (Wall_Width*2)), $fn=Resolution);

//Make the ball holder

difference() {
    translate([0,0,Screw_Center]) rotate([0,90,0]) cylinder(h = (Screw_Thread + Wall_Width) * Hold_Multiplier, r = (Screw_Thread + (Wall_Width * 2) + Tolerance), center=true, $fn = Resolution);

    translate([0,0,Screw_Center]) rotate([0,90,0]) sphere(r = (Screw_Thread + Wall_Width + Tolerance), $fn = Resolution);
}

//Make the ball
difference() {

    union(){

        translate([0,0,Screw_Center]) rotate([0,90,0]) sphere(r = (Screw_Thread + Wall_Width), $fn = Resolution);

        translate([0,0,Screw_Center]) rotate([0,90,0]) cylinder (h = ((Screw_Thread * 2) + (Wall_Width * 2)), d = (Screw_Thread + Wall_Width), center = true, $fn=Resolution);

    }

    translate([0,0,Screw_Center]) rotate([0,90,0]) cylinder (h = (((Screw_Thread * 2) + 1) + (Wall_Width * 2)), d = (Screw_Thread), center = true, $fn=Resolution);

}

```


[User]
Okay, I need to create a cover for the holes on the DJI Mavic Mini remote controller handles. I already have an existing design (`DJI_right_cover.scad`) that does this, but I want to improve upon it and make it parametric. The design currently includes a text label, a main body that covers the hole and a small connecting piece.

Here's what I'd like the new design to be able to do, with OpenSCAD code:

1.  **Text Customization:** The text on the cover should be customizable through a variable, including font, font size, and the actual text content. The current design has a `content` variable. I would like a `text_string`, `font_name` and `font_size` variable.
2.  **Main Cover Shape:** Instead of being limited to a fixed hull based on cylinders, the main body of the cover should have a general shape that can be defined by a 2D polygon. I need a variable named `cover_points` that is a list of 2D coordinates for the polygon.
3.  **Connector Size and Position:** The small connector piece which extends to the remote controller hole should have its dimensions and position controlled by variables. It's currently a cube with a cylindrical extension, but that could be another 2D shape that is extruded. I would like to have a `connector_points` variable for the 2D points, and a `connector_height` and `connector_x_offset`, and `connector_y_offset` to control the position.
4.  **Extrusion Height:** Everything in the design should have an adjustable extrusion height, allowing for fine-tuning of the thickness of the text and the cover. The current design uses an extrusion height of `2`, and I would like a variable named `extrusion_height`.
5. **Hole Extension:** The cylinder that extends into the hole should also have an adjustable height/depth via `hole_depth` variable, and the same for `hole_diameter`
6. **Handle curvature** In order to accommodate handle curvature, I would like the cover to be slightly curved. Add `curvature` to this and adjust the Z position based on that curvature. The curvature is based on a 100 mm diameter.

I envision the final product being a parametric design with easily modifiable parts. The design should start with the following variables with default values, so that it is still a drop in replacement.

```
text_string = "AEROPIC";
font_name = "Liberation Sans:style=Bold";
font_size = 5;
extrusion_height = 2;

cover_points = [[-13.65, 2.9], [13.65, 2.9], [13.65, -2.9], [-13.65, -2.9]];
connector_points = [[-1.25, -0.5], [1.25, -0.5], [1.25, 0.5], [-1.25, 0.5]];
connector_height = 3;
connector_x_offset = 0;
connector_y_offset = -3.5;
hole_depth = 25;
hole_diameter = 1.5;
curvature = 0;
```

Please provide the OpenSCAD code that implements these features. Assume no existing libraries, but if you require external libraries for some feature, please note which library it is. I would prefer if all the code was in a single file for simplicity.

[Assistant]
DJI_right_cover.scad:
```

// copyright aeropic 2020

// add your text here
content = "AEROPIC";

// hidden
$fn = 100;

module mytext() {

font = "Liberation Sans:style=Bold";

   linear_extrude(height = 2) {
       text(content, font = font, size = min(6,6*5/len(content)), halign ="center", valign ="center");

 }
} // end mytext

module manche() {
  hull() {

      translate([-27.3/2,2.9,0]) cylinder (d = 4,h=2,  center = true);
       translate([27.3/2,2.9,0]) cylinder (d = 4,h=2,  center = true);
      translate([-27.3/2,-2.9,0]) cylinder (d = 4,h=2,  center = true);
       translate([27.3/2,-2.9,0]) cylinder (d = 4,h=2,  center = true);
  } // end hull
  translate([0,-3.5,1.8])cube([25,0.8,3], center = true);
  translate([0,-4,2.5])rotate([-0,90,0])cylinder(d=1.5, h = 25, center = true);

  translate([0,3.5,1.8])cube([25,0.8,3], center = true);
  translate([0,4,2.5])rotate([-0,90,0])cylinder(d=1.5, h = 25, center = true);
}// end manche

difference() {
    manche();
#rotate([180,0,0])mytext();
}

```


[User]
Hi, I'm looking for a parametric OpenSCAD design for a lithophane coaster. I want it to be customizable with different images and various size/thickness parameters.

Essentially, I want to load a square image (ideally it could handle up to 100x100 pixels, or scale the image appropriately if it's not a perfect square). The lithophane effect should be achievable by varying the thickness of the material, where lighter areas of the image would be thinner.

Here are the parameters I’d like to control:
*   `image_file`: The path to the image file (e.g. "my_logo.png").
*   `res`: Resolution of the lithophane (0 for smooth, higher values for sliced). I'd like some kind of linear or layered approach to get the lithophane effect, which would work well for 3D printing.
*  `scribe`: Position of the image. 0 would mean that the image is inscribed (the image is fit inside a circle), 1 would mean that the image is outscribed (the image covers a circumscribed circle).
*  `diam`: Diameter of the coaster.
*   `z`: Total thickness of the coaster.
*   `bordR`: Width of the border around the image.
*   `bordZ`: Height of the border around the image.
*   `pBack`: The percentage of the coaster thickness dedicated to a flat backing.

The design should include a main circular coaster body with an image in the center, a raised border around the image, and a flat back. If `scribe` is set to 0, I want the space outside the image but inside the coaster to be filled in with flat material up to the thickness of the coaster.

I'm hoping to use something like the `surface()` function to handle the image data. I don't think I'm using any particular libraries right now other than what's included in OpenSCAD. Please let me know if you think you can do this.

[Assistant]
Lithopane_Coaster.scad:
```
//lithopane coaster

// preview[view:south]
/* [Image] */

// Load a square image =< 100x100 pixels.(images will be stretched to fit) Simple photos work best. Do not use the Invert checkbox!
image_file = "eggplant.png"; //"image-surface.dat"; // [image_surface:100x100]

// - resolution of lithopane 0-10 (0 is infinte)
res = 0; //[0:10]

// - position of image within coaster (0:inscrbed, 1:outscribed)
scribe = 0;

/* [Adjustments] */
// - Diameter of coaster
diam = 82;

// - thickness of coaster
z = 3;

// - additional border width
bordR = 3;

// - border height
bordZ = 4;

// - backing plate percentage
pBack = 0.33;  //[0.05:0.05:0.8]

/* [Hidden] */
//modify image size to suit in/out-scribed. One term or other=0
imgLen = (scribe)*diam + (1-scribe)*sin(45)*diam;
zBack = z*pBack;
$fn=60;

// Make litho and trim
intersection() {
    //surface
    translate([0,0, zBack])
        resize([imgLen, imgLen, z-zBack])
        Lithopane(res);
    //trim cylinder
    cylinder(d=diam, z*4, center=true);
}
//fill gaps when image is inscribed
if(scribe==0) difference() {
        cylinder(d=diam,z);
        cube(imgLen, center=true);
}

//create border
difference() {
    //outer cylinder
    cylinder(d=diam+2*bordR, bordZ);
    //subtract inner body
    cylinder(d=diam, bordZ*3, center=true);
}

//backing cylinder
cylinder(d=diam, zBack);

module Lithopane(z, res) {
    if(res>0) {
        layerZ = z/res;
        //slice 1 @ z=100
        translate([0,0, z-layerZ])
            linear_extrude(layerZ)
            projection(cut=true)
            translate([0,0,-100+1])
            resize([100,100,100])
            surface(image_file, center=true);

        //slice n
        if(res>=2)
        for(n=[2:res])
        translate([0,0, (n-2)*layerZ])
        linear_extrude(layerZ)
            projection(cut=true)
            translate([0,0,-(n-2)*layerZ-.1])
            resize([100,100,100])
            surface(image_file, center=true);
    }
    else {
        resize([100,100,100])
            surface(image_file, center=true);
    }
}

```


[User]
Hey, I'm looking to create a multi-tiered display stand for minifigures, and I've got some specific parameters in mind. I'd like to be able to customize the stand's width based on the number of minifigures I want it to hold, the height of the steps, the depth of the steps, the thickness of the walls, the number of stairs, and the number of supports underneath. I'd also like an option for a small lip at the front of each step to help prevent figures from sliding off.

Here's a breakdown of the parameters I'm envisioning:

*   **`minifigsacross`**: This will determine how many minifigures wide the stand is, with each minifig roughly needing 34mm of width.
*   **`high`**: This will be the height of each step.
*   **`deep`**: This will be the depth of each step; probably needs to be 25mm by default.
*  **`thick`**: This will be the thickness of the walls/supports. I need to be able to adjust this for structural integrity as needed.
*   **`nostairs`**: This will control how many steps the stand has.
*   **`nosupports`**: This will specify the number of supports underneath the steps (these will probably be necessary for wider stands).
*   **`lip`**: This would be a boolean (1 for yes, 0 for no) to add a lip at the front of each step. The lip should be the same thickness as the walls.

The display should be designed with the assumption that minifigs will be standing on baseplates. If a lip is specified, I'd like the lip to be at the front of each step, and the depth of the step should be increased to accommodate the lip (i.e. lip adds to the depth of the steps).

I think it might be good to have the supports evenly distributed under the structure. I'm imagining the supports being essentially a wall, with the exception that there are openings to reduce the amount of plastic.

Can you generate an OpenSCAD file (or set of files, if you think it's better) based on this description? I don't need to use any specific libraries.

[Assistant]
Lego_deplay_stand.scad:
```
minifigsacross=5; 	// How many minifigs wide
high=20;			// Height of steps
deep=25;			// Depth of steps - should probably be 25
thick=2;			// Thickness of walls - adjust as needed for structural integrity
nostairs=4; 		// How many stairs
nosupports=1; 		// Supports underneath - you'll probably need these if wider than about 5 MFs
lip=1;                    // Lip at front of each step - 1 for yes, 0 for no

topthick = lip==1 ? thick*2 : thick;
actualdeep = lip==1 ? deep+thick : deep;

support=nosupports+1;
sizex=minifigsacross*34;

//Steps +sides
for ( i = [1:nostairs] )
    {
    translate([0,deep*i,0])
        {
        difference()
    	    {
            cube([sizex,actualdeep,high*i]);
    	    for(b=[0:nosupports])
    	    	{
    	        translate([thick+((sizex/support)*b),thick,-topthick]) cube([sizex/support-thick*2,actualdeep*2,high*i]);
    	        translate([thick+((sizex/support)*b),-topthick,-thick-high]) cube([sizex/support-thick*2,actualdeep*2,high*i]);
		}
                //actualdeep = i==nostairs ? deep+thick : deep;
                if(lip==1) translate([0,thick,high*i-thick]) cube([sizex+thick,deep,topthick*2]);

                }
	}
}
```


[User]
Okay, I'm looking to have a set of drawer dividers designed for my Craftsman/Stanley storage bins. I have both the 30-drawer and 39-drawer organizers, and the 39-drawer one has larger drawers as well. I need separate designs for the small and large drawers.

For the small drawer dividers, the dimensions are as follows: the width at the top of the tray should be 51mm, and the width at the bottom of the tray should be 50mm. The divider's height should be 33mm. The thickness of the divider should be 1.5mm, with a corner radius of 2mm, which should be a nice rounded corner. At the top left and right corners, there needs to be a cutout to clear drawer stops; the width of this cutout needs to be 5mm, and the height should also be 5mm. Finally, I'd like to include a "grabber" along the top edge for easier gripping. The grabber should have a width of the top width minus 4 times the cutout width. The grabber height should be 1mm, and the grabber thickness should be 1.5 times the divider thickness.

For the large drawer dividers, the width at the top of the tray should be 109mm, and the width at the bottom of the tray should be 107mm. The divider's height should be 55mm. The thickness should be 1.5mm, and the corner radius, again, should be 2mm for rounded corners. The cutouts at the top should be 8mm wide and 5mm high. The grabber should have a width of the top width minus 4 times the cutout width. Its height should be 1mm, and its thickness should be 1.6 times the divider thickness.

I'd like this to be done in OpenSCAD, and I'd like it to be parametric so I can easily adjust dimensions later on if needed. I think it would be best to use a separate file for the common functions that both small and large use, and I am not sure if OpenSCAD has any useful libraries to use here.

Could you generate the necessary `.scad` files for this?

[Assistant]
craftsman_drawer_divider_small.scad:
```
/*****************************************************************************
 * Craftsman/Stanley Organizer Bin Drawer Dividers
 *
 * This script generates an OpenSCAD rendering of small drawer dividers for
 * the Craftsman storage bins currently sold by Lowe's in 30 and 39 drawer
 * designs:
 *
 * https://www.lowes.com/pd/CRAFTSMAN-Bin-System-30-Compartment-Plastic-Small-Parts-Organizer/1000578345
 * https://www.lowes.com/pd/CRAFTSMAN-Bin-System-39-Compartment-Plastic-Small-Parts-Organizer/1000578341
 *
 * The corresponding Stanley models STST40730 and STST40739 should be the
 * exact same organizer.
 *
 * Note that the 39 drawer model also has large drawers. The large dividers
 * are supported by a separate script included along with this one.
 *
 * Version 1.0 1/6/2020
 *
 * David Morris
 * Copyright (C) 2020
 * Licensed under the Creative Commons - Attribute - Non-Commercial license.
 *****************************************************************************/

dividerWidthTop     = 51;   // divider width at top of tray (trays are usually wider at top)
dividerWidthBottom  = 50;   // divider width at bottom of tray
dividerHeight       = 33;   // divider height from center
dividerThickness    = 1.5;  // thickness of divider
cornerRadius        = 2;    // 0 for square corners; 2 makes a nice round corner
cutoutWidth         = 5;    // Width of cutout at top left/right corners to clear drawer stops
cutoutHeight        = 5;    // Height of cutout at top left/right corners to clear drawer stops

// the "grabber" is a protruding line along the top edge to make gripping the divider easier
grabberWidth        = dividerWidthTop-cutoutWidth*4;
grabberHeight       = 1;
grabberThickness    = dividerThickness * 1.5;

// sanity check on corner radius to ensure a reasonable render
assert(dividerWidthTop/2 > cornerRadius && dividerWidthBottom/2 > cornerRadius && dividerHeight/2 > cornerRadius, "Corner radius must be less than half of divider X/Y dimension!");
assert(cutoutWidth/2 > cornerRadius && cutoutHeight/2 > cornerRadius, "Corner radius must be less than half of cutout X/Y dimension!");

// include ruler to check measurements
// use <ruler.scad>;
// %rotate ([0, 0, 0]) ruler(50);

use <craftsman_drawer_divider_common.scad>;

drawDivider(dividerWidthTop, dividerWidthBottom, dividerHeight, dividerThickness, cornerRadius, cutoutWidth, cutoutHeight, grabberWidth, grabberHeight, grabberThickness);

```
craftsman_drawer_divider_large.scad:
```
/*****************************************************************************
 * Craftsman/Stanley Organizer Bin Drawer Dividers
 *
 * This script generates an OpenSCAD rendering of small drawer dividers for
 * the Craftsman storage bins currently sold by Lowe's in 30 and 39 drawer
 * designs:
 *
 * https://www.lowes.com/pd/CRAFTSMAN-Bin-System-30-Compartment-Plastic-Small-Parts-Organizer/1000578345
 * https://www.lowes.com/pd/CRAFTSMAN-Bin-System-39-Compartment-Plastic-Small-Parts-Organizer/1000578341
 *
 * The corresponding Stanley models STST40730 and STST40739 should be the
 * exact same organizer.
 *
 * Note that this is for the large drawers on 39 drawer model only. The small
 * drawer dividers are supported by a separate script included along with this one.
 *
 * Version 1.0 1/6/2020
 *
 * David Morris
 * Copyright (C) 2020
 * Licensed under the Creative Commons - Attribute - Non-Commercial license.
 *****************************************************************************/

dividerWidthTop     = 109;  // divider width at top of tray (trays are usually wider at top)
dividerWidthBottom  = 107;  // divider width at bottom of tray
dividerHeight       = 55;   // divider height from center
dividerThickness    = 1.5;  // thickness of divider
cornerRadius        = 2;    // 0 for square corners; 2 makes a nice round corner
cutoutWidth         = 8;    // Width of cutout at top left/right corners to clear drawer stops
cutoutHeight        = 5;    // Height of cutout at top left/right corners to clear drawer stops

// the "grabber" is a protruding line along the top edge to make gripping the divider easier
grabberWidth        = dividerWidthTop-cutoutWidth*4;
grabberHeight       = 1;
grabberThickness    = dividerThickness * 1.6;

// sanity check on corner radius to ensure a reasonable render
assert(dividerWidthTop/2 > cornerRadius && dividerWidthBottom/2 > cornerRadius && dividerHeight/2 > cornerRadius, "Corner radius must be less than half of divider X/Y dimension!");
assert(cutoutWidth/2 > cornerRadius && cutoutHeight/2 > cornerRadius, "Corner radius must be less than half of cutout X/Y dimension!");

// include ruler to check measurements
// use <ruler.scad>;
// %rotate ([0, 0, 0]) ruler(50);

use <craftsman_drawer_divider_common.scad>;

drawDivider(dividerWidthTop, dividerWidthBottom, dividerHeight, dividerThickness, cornerRadius, cutoutWidth, cutoutHeight, grabberWidth, grabberHeight, grabberThickness);

```
craftsman_drawer_divider_common.scad:
```
/*****************************************************************************
 * Craftsman/Stanley Organizer Bin Drawer Dividers
 *
 * This script contains common code used by both large and small divider
 * scripts. All scripts should be located in the same folder.
 *
 * Version 1.0 1/6/2020
 *
 * David Morris
 * Copyright (C) 2020
 * Licensed under the Creative Commons - Attribute - Non-Commercial license.
 *****************************************************************************/

// Draw a divider to the specified dimensions
module drawDivider(widthTop, widthBottom, height, thickness, radius, cutoutWidth, cutoutHeight, grabberWidth, grabberHeight, grabberThickness)
{
  union()
  {
    // divider lower body part 1
    translate([0,-cutoutHeight/2,thickness/2])
      drawRectangle(widthTop, widthBottom, height-cutoutHeight, thickness, radius);

    // divider upper body part 2
    translate([0, height/2-cutoutHeight, thickness/2])
      drawRectangle(widthTop-cutoutWidth*2, widthTop-cutoutWidth*2, cutoutHeight*2, thickness, radius);

    // create raised "grabber" at top
    translate([0, height/2-grabberHeight, grabberThickness/2])
      drawRectangle(grabberWidth, grabberWidth, grabberHeight, grabberThickness, 0);
  }
}

// Draw a rectangle with optional rounded corners.
// Allows for different widths at top and bottom since some drawers flare wider at the top
module drawRectangle(widthTop, widthBottom, height, thickness, radius)
{
  // must shrink width and height to account for radius at each corner
  widthTop = widthTop - radius*2;
  widthBottom = widthBottom - radius*2;
  height = height - radius*2;

  seg=120;
  linear_extrude(height=thickness, center=true)
  {
    if ( radius > 0 )
    {
      // rounded corners
      hull()
      {
        translate([-widthTop/2, height/2, 0])
          circle(radius, $fn=seg);
        translate([widthTop/2, height/2, 0])
          circle(radius, $fn=seg);
        translate([widthBottom/2, -height/2, 0])
          circle(radius, $fn=seg);
        translate([-widthBottom/2, -height/2, 0])
          circle(radius, $fn=seg);
      }
    }
    else
    {
      // square corners
      polygon([[-widthTop/2, height/2], [widthTop/2, height/2], [widthBottom/2, -height/2], [-widthBottom/2, -height/2]]);
    }
  }
}

```


[User]
Hey, I'm working on a project where I need some thumbnuts, and I'd like to have a customizable OpenSCAD design. I've got a basic idea of what I want, but I need your help to make it parametric and easy to use.

Essentially, the thumbnut should have a central cylindrical body with a knurled edge for grip and a hexagonal cutout at the base to capture a standard nut. I need to be able to adjust the overall diameter, thickness, the diameter of the center hole, and the size of the nut cavity at the bottom.

For specific parameters, I'd like to have variables for:

*   `diameter`: The overall diameter of the thumbnut (in mm).
*   `thickness`: The overall thickness of the thumbnut (in mm).
*   `hole_diameter`: The diameter of the hole through the center for the screw (in mm).
*   `nut_size`: The flat-to-flat size of the nut to be captured (in mm).
*  `nut_thickness`: The thickness of the captured nut (in mm)
* `knurl_size`: The radius of the cylinders that form the knurl (in mm)
* `knurl_count`: How many knurls to create around the circumference.

I would like the knurl to be generated by a series of cylinders along the outside edges. The knurl should be present both on the top and bottom outer rims of the main body.

Could you generate a parametric OpenSCAD file that allows me to change these values and get a properly sized thumbnut? I’m not looking for any pre-existing code; I want it to be generated from scratch. It would be great if I could use the `BOSL2` library to help with this, particularly the `polygon()` function and any of the `translate_copy()` operations.

[Assistant]
thumb-nut.scad:
```
// Diameter of the thumbnut in inches.
diameter = 0.75;

// Thickness of the thumbnut in inches.
thickness = 0.25;

// Diameter of the hole for the screw to pass through.
hole_diameter = 0.1875;

// Diameter of the captured nut, from flat to flat, in inches.
nut_size = 0.3125;

// Thickness of the caotured nut in inches.
nut_thickness = 0.125;

scale([25.4, 25.4, 25.4]) {
    difference() {
        union() {
            translate([0, 0, 0]) cylinder(r1=((diameter/2) - (thickness/4)), r2=(diameter/2), h=(thickness / 4), $fn=18);
            translate([0, 0, (thickness * 0.25)]) cylinder(r=(diameter/2), h=(thickness / 2), $fn=18);
            translate([0, 0, (thickness * 0.75)]) cylinder(r1=(diameter/2), r2=((diameter/2) - (thickness / 4)), h=(thickness / 4), $fn=18);
        }

        for(i = [0 : 8]) {
            rotate([0, 0, i * 40]) translate([(diameter/2), 0, 0]) rotate([0, 0, 180]) cylinder(r=(thickness / 4), h=thickness, $fn=4);
        }

        cylinder(r=(hole_diameter/2), h=thickness, $fn=360);

        nut_r = sqrt(pow(nut_size / 2, 2) + pow(nut_size / 4, 2));
        #translate([0, 0, (thickness - nut_thickness)]) cylinder(r=nut_r, h=nut_thickness, $fn=6);
    }
}
```


[User]
I'm looking to create a phone mount for my 3D printer, specifically an Ender 3. I want the phone to act as a camera, so I need the design to hold the phone in a landscape orientation, pointing at the print bed. The idea is to mount the phone on the X-axis motor, so it moves up and down with the print head.

I'd like to have the design split into two main parts. There's going to be a "base" that attaches to the motor, and then a "holder" that slides into the base and actually holds the phone. It's important that the phone's camera points at the print, so both pieces should be parametric to adjust for different phone sizes and camera positions.

For the base, it needs to attach securely to the Ender 3 X-motor. I'm envisioning something that clamps around the motor, maybe with a slide-in latch mechanism. For the holder, it should have a cutout that matches the phone's shape, maybe with a slight tolerance for padding if I want to add some soft material. I'd like to have the option to adjust the phone's rotation and position on the holder to dial in the perfect camera angle.

Here are the main parameters I think will be necessary for customization:

*   **Phone dimensions:** Width, height, and depth of the phone.
*   **Camera position:** Distance from the bottom edge of the phone to the camera lens.
*   **Mount offset:** How far the mount is offset from the motor mount and the phone.
*   **Phone rotation:**  Roll (rotation on the y-axis) and yaw (rotation on the z-axis) of the phone. This will be to compensate for the phone not being perfectly square on the holder.
*   **Depth variation:** I'd like to allow for the phone having a curved back, so ideally, I can specify the phone's depth at its edge, and then at a certain distance in towards the phone's center.  The phone depth should gradually change between these two points.

I'd like the OpenSCAD files to use the `MCAD` library, if that's possible. I'd also like to use the `hull()` feature to make sure that the "holder" attaches to the "clip" with adequate strength. It'd be nice to have a small cube or cylinder in the phone model to show where the camera is for easier positioning. Let me know if you have any questions.

[Assistant]
phonecam_ender_mount.scad:
```
// What components to show
render_base = 1;
render_holder = 1;
render_bed = 0;
// Place the phone in the x/y/z planes
// distance from the "bottom" of the phone to the camera lens
//   (whichever side is bottom when placed the the holder)
bottom_to_camera = 72.5/2;
// How far over into the phone is front of motor mount
y_off = -145;
// Phone rotation on y axis (looked at from front)
phone_roll = -8;
// Phone rotation on z axis (looked at from top)
phone_yaw = 190;
// Distance of holder from mount plate
x_off = 30;
// Height adjust (higher will get blocked by cooling on hotend)
z_adj = 10;

// Phone dimensions
// phone horizontal dimension
phone_h = 140.5;
// phone vertical dimension
phone_w = 72.5;
// fudge factor, allows adding some softening material?
depth_fudge = .2;
// MotoX 2014 has a curved back, model this by beveling the back
// set phone_d2 = phone_d1 to remove bevel
// Phone depth at edge (6 for moto x)
phone_d1 = 9 + depth_fudge;
// Phone depth at bevel_w mm into the phone (10.2 for moto x)
phone_d2 = 9 + depth_fudge;
// Width of the bevel (14 for moto x)
bevel_w = 14;

// Adjust virtual hot end x position
hot_x = 70; //117.5 for center

pd1 = phone_d1 + depth_fudge;
pd2 = phone_d2 + depth_fudge;

holder_h = phone_h/2;
holder_w = phone_w/2;
holder_d = pd2 * 2;
holder_th = pd2;
base_th = 4;
base = phone_h + base_th;

z_off = 3+cos(phone_roll)*(-bottom_to_camera-10+z_adj);
plate_w = 20;
screw_c_to_c = 33.7;
upper_screw_hole_x = 7;
upper_screw_hole_y = 8;

dumb_x_arm = 0;

mbw = 10; //motor base width
mbth = 3; //motor base thickness
mbo = 14; //motor base offset
medge = mbw + mbo;

// f means growth amount
module grow(dim, f) {
    translate([-f, -f, -f]) resize([dim[0] + f*2, dim[1] + f*2, dim[2] + f*2])
        children();
}

// X motor, mount point for phone
mx = 41.75;
my = 36.9;
mz = 41.75;
x_motor = [mx, my, mz];
mth = 30.333; // motor mount triangle height
mtang = 36; // motor mount triangle angle
mtth = 3; // motor mount triangle thickness
ff = 0.4; // fudge factor
module ender(){
    bed_h = 0;
    grow(x_motor, ff) cube(x_motor);
    translate([0, my-0.4, mz]) rotate([90,0,0]) linear_extrude(mtth)
        polygon([[0,0], [mx,0], [mx,mth]]);
    // bed
    translate([92,-120,-18.6-5-bed_h]) cube([235, 235, 5]);
    // hotend
    translate([92-42.5/2+hot_x,10-42.5,-11.6]) union(){
        cube([58.4, 42.5, 46.15]);
        translate([28.25,27.8,-7]) cylinder(d2=5.6, d1=0.6, h=7);
    }
}

arm_l = 15;
arm_th = mbth-ff;
module base_square(){
    clip_hyp = (medge)/cos(mtang);
    clip_h = sqrt((pow(clip_hyp,2) + pow(medge,2))
                   - 2*(medge)*clip_hyp*cos(mtang));
    // main bracket around motor
    translate([mbo,-mbth,-mbth]) cube([mbw,my+mbth,mz+(2*mbth)]);
    difference(){
        // upper bracket around triangle
        translate([mbo,my-(2*mbth),mz]) cube([mbw,mbth*2 + mtth,clip_h]);
        translate([0,my-50,mz+mbth]) rotate([0,90-36,0])
            translate([-200,0,0]) cube([200,100,100]);
    }
    if (dumb_x_arm) {
        // x arm to prevent slipping
        translate([mbo, my/2-mbth, mz+ff]) cube([mx-mbo+(arm_th), arm_th, arm_th]);
        translate([mx+ff, my/2-mbth, mz+ff-arm_l])
            cube([arm_th, arm_th, arm_th+arm_l]);
        // chamfer
        translate([mbo+mbw, my/2-mbth-arm_th, mz+ff]) rotate([0,0,45])
        cube([arm_th*2,arm_th*2,arm_th]);
    }
}

module base(){
    difference() {
        base_square();
        if (render_bed) #ender();
        else ender();
        // shave 1 mm off the bottom bracket for fit
        translate([0, my-0.4-1, -5]) cube([mx+.3, 6, 10]);
    }
    translate([mbo, -mbth*2+1, -mbth]) cube([mbw, 2, latch_h]);
    translate([mbo, -mbth*2-3, -mbth]) slide_latch_f();
    // print support
    if (dumb_x_arm) {
        translate([mx+ff-.51, my/2-mbth, mz+ff-arm_l-.2]) rotate([0,0,0]) cube([.51,arm_th,.51]);
        translate([mx+ff-2, my/2-ff, mz+ff-arm_l]) rotate([0,90,0]) cylinder(d1=1,d2=0.5,h=2);
        translate([mx+ff-2, my/2-arm_th-ff, mz+ff-arm_l]) rotate([0,90,0]) cylinder(d1=1,d2=0.5,h=2);
        translate([mx+ff-(mx-mbo), my/2-ff, mz+ff-arm_l]) rotate([0,90,0]) cylinder(d1=1,d2=1,h=mx-mbo-2);
        translate([mx+ff-(mx-mbo), my/2-arm_th-ff, mz+ff-arm_l]) rotate([0,90,0]) cylinder(d1=1,d2=1,h=mx-mbo-2);
        translate([mx-(mx-mbo), my/2-1.7, mz+ff-arm_l]) rotate([0,90,0]) cylinder(d1=5,d2=3,h=1);
    }

}

latch_h = 20;
latch_d = 4;
latch_ff = 0.3;
module slide_latch_f(){
    translate([0,0,latch_d]) linear_extrude(latch_h-latch_d)
        polygon([[0,0], [0,latch_d], [mbw, latch_d], [mbw,0],
                 [mbw/2+1.5,0], [mbw/2+2.5,2], [mbw/2-2.5,2], [mbw/2-1.5,0] ]);
    // angled bottom
    translate([0,0,latch_d]) rotate([0,90,0]) linear_extrude(mbw)
        polygon([[0,0], [latch_d,-latch_d], [latch_d,latch_d], [0,latch_d]]);
    // print support
    #translate([mbw/2-1.5-.1,0,latch_d+1]) cube([3.2,.6,latch_h-latch_d-1]);
}
module slide_latch_m(){
    difference() {
        union() {
            translate([0,0,latch_d]) linear_extrude(latch_h-latch_d)
                polygon([[0,0], [0,latch_d],
     [mbw/2+1.5-latch_ff,latch_d], [mbw/2+2.5-latch_ff-.1,latch_d+2-latch_ff],
     [mbw/2-2.5+latch_ff+.1,latch_d+2-latch_ff], [mbw/2-1.5+latch_ff,latch_d],
                         [mbw, latch_d], [mbw,0]]);
            // angled bottom
            cube([mbw,latch_d,latch_d]);
        }
        rotate([-45,0,0]) translate([-1,0,0]) cube([mbw+2,latch_d,latch_d*4]);
    }
}
//translate([0,20,0])slide_latch_f();
//translate([0,16-latch_ff/2,0])slide_latch_m();

////////////////////////////
// Phone model
////////////////////////////
module phone(){
    difference(){
        cube([phone_w, phone_h, pd1+.01]);
        translate([3,0,0]) rotate([0,0,135]) cube(phone_w);
        translate([3,phone_h,0]) rotate([0,0,135]) cube(phone_w);
    }
    translate([phone_w/2,phone_h/2,pd1]){
        linear_extrude(height=pd2 - pd1,
                       scale=[(phone_w-bevel_w*2)/phone_w, 1], center=false)
        {
            square([phone_w, phone_h], center=true);
        }
    }
    // camera visualization
    if (true) {
        translate([bottom_to_camera,20,0]) cylinder(d=1, h=180);
    }
}

// And the phone holder, modeled around the phone shape
module holder(){
    difference(){
        // Holder block
        union(){
            translate([-holder_th/2, phone_h/4, -pd2/2])
                cube([holder_w, holder_h, holder_d]);
            translate([-holder_th/2, -base_th/2, .01])
                cube([holder_w/2, base, pd1]);
        }
        if (render_bed) #phone();
        else phone();
        // Minus squares for material savings
        translate([-holder_th/2+holder_th, phone_h/4+holder_th, -pd2/2-holder_th])
            cube([holder_w, holder_h-(holder_th*2), holder_d*2]);
    }
}

// Amount to drop the phone to align it
phone_z_to_screw = cos(phone_roll)*phone_w;
screw_rot_y = upper_screw_hole_x - sin(90-25)*pd2;
phone_y_to_screw = cos(phone_yaw)*phone_h - screw_rot_y;
module positioned_holder(){
    translate([x_off, -phone_y_to_screw + y_off, z_off])
    rotate([0, phone_roll, phone_yaw])
    rotate([0, -90, 0])
        holder();
}

module plate_to_holder(){
    hull(){
        rotate([90,0,90]) translate([-10-latch_d,-3,0]) cube([1, latch_h, mbw]);
        translate([x_off, -phone_y_to_screw + y_off, z_off])
            rotate([0, phone_roll, phone_yaw])
            rotate([0, -90, 0])
            translate([0, holder_h/2-3, -holder_d+2])
                cube([holder_th*2, holder_th, holder_d/4]);
    }
    //hull(){
    //    translate([x_off, -phone_y_to_screw + y_off,
    //                -phone_z_to_screw + z_off])
    //        rotate([0, phone_roll, phone_yaw])
    //        rotate([0, -90, 0])
    //        translate([0, holder_h/2-3, -holder_d+3])
    //            cube([holder_th*2, holder_th, holder_d/4]);
    //    #translate([0,-14,-mbth]) cube([mbw,latch_d*2+1,.01]);
    //}
    translate([-.0,-9-latch_d,-3]) slide_latch_m();
}

module phonecam_ender_mount(){
    base();
    translate([mbo,0,0]) plate_to_holder();
    positioned_holder();
}

// rotate for printing as one (lots of support needed)
//rotate([0, -phone_roll, 0]) rotate([0, 0, -phone_yaw]) phonecam_ender_mount();

module split_ph(){
    translate([mbo,0,0]) plate_to_holder();
    positioned_holder();
}

//base();

// Use the entire model to check placement relative to hotend and bed
if (render_bed) {
    phonecam_ender_mount();
} else {
    // Use these to actually print (one at a time)
    if (render_base) translate([40,50,-my/2+mbw/2-.5]) rotate([0,-90,0]) base();
    if (render_holder) rotate([0, -phone_roll, 0]) rotate([0, 0, -phone_yaw]) translate([0,0,holder_h/2-1.4]) split_ph();
}

```


[User]
I'm looking to design a universal joint, and I like the general structure of the "UniversalJoint3.scad" file I found, but I need something more customizable. I'd like a parametric design where I can define the dimensions of the joint, its two sleeves, and the shafts that connect to the sleeves, as well as the bolt holes for connecting the shafts.

Specifically, I need the following parameters to be adjustable:

*   **Joint Parameters:**
    *   `joint_hole`: Diameter of the hole through the joint for the cross pin.
    *   `joint_dia`: Outer diameter of the joint itself.
    *   `joint_len`: Length of the joint (distance between the two cross pin holes)
*   **Sleeve 1 Parameters:**
    *   `shaft_dia1`: Diameter of the shaft that connects to sleeve 1
    *   `sleeve_dia1`: Outer diameter of sleeve 1.
    *   `sleeve_len1`: Length of sleeve 1.
*   **Sleeve 2 Parameters:**
    *   `shaft_dia2`: Diameter of the shaft that connects to sleeve 2
    *   `sleeve_dia2`: Outer diameter of sleeve 2.
    *   `sleeve_len2`: Length of sleeve 2.
*   **Fastener Parameters:**
    *    `fastener_hole`: Diameter of the bolt hole on each sleeve, for connecting the shaft
*  **Clearance Parameter**
     * `cl`: clearance between joint and wings (the part of the sleeve around the joint)
*   **Rotation Parameters**
     * `ry`: rotation around the y-axis for sleeve 2
     * `rz`: rotation around the z-axis for sleeve 1

I'd also like some control over the smoothness of the curves, so I'd need a `$fn` parameter. Additionally, I'd like to be able to hide and display parts with a `part` parameter, similar to the file above. So, `part=0` would display all parts, `part=1` would show only sleeve 1, `part=2` would show only sleeve 2, and `part=3` would show only the central joint.

The design should include the central joint, which has two perpendicular holes for the cross pin, and the two sleeves, which connect to the central joint using a wing structure. Each sleeve should have an outer cylindrical body with a hole that fits the corresponding shaft, as well as a hole for the bolt and the wing structure. The sleeves must be able to rotate about the central joint and fit together properly. The design should have clearance for assembly as well. I think that's all I need to get started. I'm assuming that the design will be using basic OpenSCAD commands and doesn't need any external libraries.

[Assistant]
UniversalJoint3.scad:
```
//// File: UniversalJoint3.scad
//// Version: OpenSCAD 2015.03-2
//// Author: Kijja Chaovanakij
//// Date: 2020-01-26

/*[part]*/
//smooth[48]
$fn=48; //[36, 48, 60]
//clearance between joint and wings[0.4]
cl=0.4; //[0.2:0.1:2]
//display parts
part=0; //[0:all, 1:sleeve1, 2:sleeve2, 3:joint]

/*[joint]*/
//diameter of joint's hole[(M3+loose tolerance)=3.4]
joint_hole=3.4; //[2.8:0.1:4.5]
//outer diameter of joint[10]
joint_dia=10;
//length of the joint(hole to hole)[20]
joint_len=20;

/*[shaft1]*/
//diameter of shaft1[8]
shaft_dia1=8;
//diameter of sleeve1[19]
sleeve_dia1=19;
//length of sleeve1[16]
sleeve_len1=16;

/*[shaft2]*/
//diameter of shaft2[12]
shaft_dia2=12;
//diameter of sleeve2[25]
sleeve_dia2=25;
//length of sleeve2[20]
sleeve_len2=20;

/*[fastener]*/
//diameter of shaft's bolt[M3+tight tolerance=3.1]
fastener_hole=3.1; //[2.8:0.1:4.5]

/*[rotation]*/
//y axis rotation
ry=-15; //[-45:1:45]
//z axis rotation
rz=15; //[-45:1:45]

/*[Hidden]*/
//remove difference effect[0.002]
ep=0.002;
//space between joint and sleeve
sp=1;

//// main
if (part==0 || part==3){
  color("LightPink")
  difference(){
    hull(){
      translate([joint_len/2, 0, 0])
        cylinder( h=joint_dia, d=joint_dia, center=true);
      rotate([90, 0, 0])
      translate([-joint_len/2, 0, 0])
        cylinder( h=joint_dia, d=joint_dia, center=true);
    }//h
    translate([joint_len/2, 0, 0])
      cylinder( h=joint_dia+ep, d=joint_hole, center=true, $fn=24);
    rotate([90, 0, 0])
    translate([-joint_len/2, 0, 0])
      cylinder( h=joint_dia+ep, d=joint_hole, center=true, $fn=24);
  }//d
}//i
//

if (part==0 || part==1){
  color("PaleGreen")
  translate([joint_len/2, 0, 0])
  rotate([0, 0, rz])
    sleeve(sleeve_dia=sleeve_dia1,
           sleeve_length=sleeve_len1,
           shaft_dia=shaft_dia1);
}//i
//

if (part==0 || part==2){
  color("Aquamarine")
  translate([-joint_len/2, 0, 0])
  rotate([90, ry, 0])
  mirror([1, 0, 0])
    sleeve(sleeve_dia=sleeve_dia2,
           sleeve_length=sleeve_len2,
           shaft_dia=shaft_dia2);
}//i
//

//sleeve();
module sleeve(sleeve_dia=sleeve_dia1,
              sleeve_length=sleeve_len1,
              shaft_dia=shaft_dia1){
  difference(){
    union(){
      intersection(){
  //solid wing
        hull(){
          cylinder(h=sleeve_dia, d=joint_dia, center=true, $fn=32);
          translate([sleeve_dia, 0, 0])
            cube([0.001, sleeve_dia, sleeve_dia], true);
        }//h
        rotate([0, 90, 0])
          cylinder(h=sleeve_dia*2, d=sleeve_dia, center=true);
      }//i
  //solid sleeve
      translate([joint_dia/2+sp+sleeve_length/2, 0, 0])
      rotate([0, 90, 0])
        cylinder(h=sleeve_length, d=sleeve_dia, center=true);
    }//u
  //make wing space
    cube([joint_dia+sp*2, sleeve_dia+ep, joint_dia+cl*2], true);
  //make joint hole
    cylinder(h=sleeve_dia+ep, d=joint_hole, center=true, $fn=24);
  //make shaft hole
    translate([joint_dia/2+sp+sleeve_length/2, 0, 0])
    rotate([0, 90, 0])
      cylinder(h=sleeve_length+ep, d=shaft_dia, center=true, $fn=32);
  //make fastener hole
    translate([joint_dia/2+sp+sleeve_length/2, 0, 0])
      cylinder(h=sleeve_dia, d=fastener_hole, center=true, $fn=24);
  }//d
}//m
//

```