[User]
Okay, I'm looking to generate some custom deck boxes for trading cards, specifically Magic: The Gathering. I've got a design in mind that I'd like to be fully parametric, so I can easily adjust the size for different numbers of sleeved cards, the number and size of magnets, and also add some text.

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

**General Structure:**

*   The deck box should consist of two parts: a main box and a lid.
*   The box and lid should have a slightly interlocking design. The lid should fit snugly over the main box, and I'd like to have a small gap so they can slide together nicely.
*   I'd like to have the option for rounded edges on the exterior of both the box and lid.

**Internal Cavity:**

*   The main internal space for the cards needs to be parametric. I need to be able to set the width, depth, and height of the cavity to fit different numbers of sleeved cards.
*   I want to have a small lip at the front for the top of the cards to rest on, if that makes sense. It adds a bit of a lip on the inside of the box, so that when I close the box it doesn't put a lot of pressure on the card's edges.
*  I need an extra little cavity between the card cavity and the front of the box, so it can be a bit thicker.

**Magnets:**

*   The design should include pockets for round magnets in both the box and the lid.
*   I need to be able to control the number of magnet holes and the number of magnets in each hole (in case I want to use stacked magnets).
*   I need to be able to define the diameter and height of the magnets.
*   I also want to control the spacing between the magnet holes.
*   There should be a small extra offset in the magnet holes to make sure the magnets fit well.
*   I'll need to be able to generate a separate magnet stopper piece (a small cylindrical piece to go behind the magnet in the hole) with the same width as the magnet hole and a height equal to about half the magnets' diameter.

**Text:**

*   I want to be able to add text on the box.
*   The text should be on the front of the box, facing outwards.
*   I should be able to set the font, font size, and the text itself.
*   I'd also like the option for a second line of text, which will be useful for adding names and/or stats.
*   The text should be extruded, so it's raised from the surface.

**Parametric Control:**

*   Everything should be controlled through parameters, so I can easily adjust the dimensions, magnet placement, and text.

**Rendering Options:**

*   I'd like to have options to render just the deck box, just the lid, or just the magnet stopper, in addition to the full assembly.
*   It would be useful if I could also render a version of the deck box with the text facing up so it's easier to see.

**Libraries:**

*   I'd prefer it if you used only built-in OpenSCAD functions where possible. I do, however, need your help with creating rounded corners and for creating polyhedron shapes.

I think that covers it. Please let me know if you have any questions. I'm looking forward to seeing what you come up with!

[Assistant]
magnetic_deck_box_v1.1.scad:
```

/* [Rendering:] */
// Choose what to render
render = "d"; // [d:Deck box, t:Deck box (text view), l:Lid, m:Magnet blocker]

/* [Internal cavity:] */
// Internal cavity width (I've used 55 for 100 sleeved cards)
cardsCavityX = 35;
// Internal cavity depth
cardsCavityY = 67;
// Internal cavity hight
cardsCavityZ = 92;

/* [Magnets:] */
// How many vertical holes for the magnets are in the box and the lid
holesNumber = 1;
// How many magnets are placed in the same hole
magnetNumber = 1;
// Magnet diamiter
magnetX = 10;
// Magnet hight
magnetY = 3;
// How much space between each magnet hole
magnetSpacing = 1;
// Extra spacing in the magnet holes to fit the magnets
magnetOffsetXY = 0.2;

/* [Text:] */

fontName = "Berkshire Swash"; // [Aladin, Amita, Berkshire Swash, Condiment, Charm, Dr Sugiyama, Eagle Lake, Fondamento, Merienda, Merienda One, Niconne, Norican, Quintessential, Redressed, Romanesco, Tapestry, Tillana]

fontSize = 16;
// First line
textString = "Nissa";
// Second line (leave this one blanck to render only one line in the center)
textString2 = "";

module customizer_limit() {}
// =============================================================================
// =============================================================================

$fs = 0.15;

fit = 0.25;

lidHorizontalZ = 2;

magnetZ = magnetX;

magnetHoleX = magnetX + magnetOffsetXY;
magnetHoleY = magnetY + magnetOffsetXY;
magnetHoleZ = 5 + (magnetZ * magnetNumber);
magnetWall = 1;

lidVerticalZ = 5 + magnetHoleZ;

lidVerticalY = magnetHoleY+magnetWall*2;

deckBorder = 3;
deckBorderFront = lidVerticalY*2;
deckBase = 2;

deckX = deckBorder * 2 + cardsCavityX;
deckY = deckBorder + deckBorderFront + cardsCavityY;
deckZ = deckBase + lidHorizontalZ + cardsCavityZ;

lidVerticalX = deckX;

lidHorizontalX = deckX - 2*(deckBorder/2);
lidHorizontalY = deckY - (deckBorder/2); // ok deckBorder perche' tanto e' il back e non il front
lidSlope = deckBorder/2;    // di quanto faccio rientrare i punti superiori
                            // non deve superare deckBorder/2 altrimenti l'apertura e' piu' piccola della cards_cavity

frame = 4;
frameNegativeX = deckX - 2*frame;
frameNegativeZ = deckZ - 2*frame;

extraCavityX = cardsCavityX;
extraCavityY = deckBorderFront - deckBorder;
extraCavityZ = deckZ - lidVerticalZ - lidHorizontalZ - deckBorder -(deckBorderFront - deckBorder);

/*
============================================================================================================
==============================MODULI========================================================================
============================================================================================================
*/

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

module rounded_cube(size = [1, 1, 1], center = true, radius = 1, apply_to = "all") {
	// If single value, convert to [x, y, z] vector
	size = (size[0] == undef) ? [size, size, size] : size;

	translate_min = radius;
	translate_xmax = size[0] - radius;
	translate_ymax = size[1] - radius;
	translate_zmax = size[2] - radius;

	diameter = radius * 2;

	obj_translate = (center == false) ?
		[0, 0, 0] : [
			-(size[0] / 2),
			-(size[1] / 2),
			-(size[2] / 2)
		];

	translate(v = obj_translate) {
		hull() {
			for (translate_x = [translate_min, translate_xmax]) {
				x_at = (translate_x == translate_min) ? "min" : "max";
				for (translate_y = [translate_min, translate_ymax]) {
					y_at = (translate_y == translate_min) ? "min" : "max";
					for (translate_z = [translate_min, translate_zmax]) {
						z_at = (translate_z == translate_min) ? "min" : "max";

						translate(v = [translate_x, translate_y, translate_z])
						if (
							(apply_to == "all") ||
							(apply_to == "xmin" && x_at == "min") || (apply_to == "xmax" && x_at == "max") ||
							(apply_to == "ymin" && y_at == "min") || (apply_to == "ymax" && y_at == "max") ||
							(apply_to == "zmin" && z_at == "min") || (apply_to == "zmax" && z_at == "max")
						) {
							sphere(r = radius);
						} else {
							rotate =
								(apply_to == "xmin" || apply_to == "xmax" || apply_to == "x") ? [0, 90, 0] : (
								(apply_to == "ymin" || apply_to == "ymax" || apply_to == "y") ? [90, 90, 0] :
								[0, 0, 0]
							);
							rotate(a = rotate)
							cylinder(h = diameter, r = radius, center = true);
						}
					}
				}
			}
		}
	}
}
/*
source
https://danielupshaw.com/openscad-rounded-corners/
*/

/*
============================================================================================================
==============================PRINT=========================================================================
============================================================================================================
*/

// TEST UPPER DECK
//difference()
//{
//    deck();
//    translate([0,0,-magnetHoleZ -10])
//    cube([100,100,100], true);
//}

//difference()
//{
//    deck();
//    translate([-50,0,0])
//    cube([100,100,100], true);
//}

//difference()
//{
//    deck();
//    cube([12,100,100], true);
//}

//difference()
//{
//    deck();
//    translate([-40,0,0])
//    cube([100,100,100], true);
//}

//
//deck_magnet_holes();
//lid_magnet_holes();

//difference()
//{
//    deck();
//    translate([0,0,-22])
//    cube([100,100,100], true);
//    translate([0,25,0])
//    cube([100,100,100], true);
//}

//difference()
//{
//    lid();
//    translate([0,25,0])
//    cube([100,100,100], true);
//}

//// TEST TEXT
//difference()
//{
//    deck();
//    translate([0,-16, 0])
//    cube([100,100,100], true);
//}

// TEST cards cavity
//difference()
//{
//    deck();
//    translate([0,0,-magnetHoleZ + 5])
//    cube([100,100,100], true);
//}

if(render == "d")
    deck();
if(render == "t")
    rotate([0,0,180])
    deck();
if(render == "l")
    lid();
if(render == "m")
    magnet_stopper();

/*
============================================================================================================
==============================DECK==========================================================================
============================================================================================================
*/

module cards_cavity()
{
    translate([0, -deckBorder/2 + deckBorderFront/2, deckBase/2 - lidHorizontalZ/2])
        cube([cardsCavityX, cardsCavityY, cardsCavityZ], true);
}

module deck_volume()
{

    rounded_cube([deckX, deckY, deckZ], true);
}

module lid_cavity_horizontal()
{
    // points to be used
    p = [   [lidHorizontalX/2,lidHorizontalY/2,-lidHorizontalZ/2],                      // q 1 p 0
            [-lidHorizontalX/2,lidHorizontalY/2,-lidHorizontalZ/2],                     // q 2 p 1
            [-lidHorizontalX/2,-lidHorizontalY/2,-lidHorizontalZ/2],                    // q 3 p 2
            [lidHorizontalX/2, -lidHorizontalY/2,-lidHorizontalZ/2],                    // q 4 p 3
            [lidHorizontalX/2-lidSlope,lidHorizontalY/2-lidSlope,lidHorizontalZ/2],     // q 1 p 4
            [-lidHorizontalX/2+lidSlope,lidHorizontalY/2-lidSlope,lidHorizontalZ/2],    // q 2 p 5
            [-lidHorizontalX/2+lidSlope,-lidHorizontalY/2,lidHorizontalZ/2],            // q 3 p 6
            [lidHorizontalX/2-lidSlope, -lidHorizontalY/2,lidHorizontalZ/2]  ];         // q 4 p 7

    // faces drawn
    f = [
        [0,1,2]
        ,[2,3,0]
        ,[7,4,3]
        ,[4,0,3]
        ,[4,5,0]
        ,[5,1,0]
        ,[5,6,1]
        ,[6,2,1]
        ,[6,7,2]
        ,[7,3,2]
        ,[4,7,6]
        ,[6,5,4]
    ];

    translate([ 0
                ,-deckBorder/2/2  //divido per 2 tutte le variabili gia' presenti
                ,deckZ/2 - lidHorizontalZ/2
                ])   // non divido lidHorizontalZ per 2 perche' e' appoggiata sul piano xy
     polyhedron(points = p, faces = f);
}
/*
source
https://gusmith.wordpress.com/2015/09/18/simple-tool-for-creating-polyhedrons-in-openscad/
*/

module lid_cavity_vertical()
{
    translate([0
                , lidVerticalY/2 - deckY/2
                , - lidVerticalZ/2 + deckZ/2
    ])
    cube([lidVerticalX, lidVerticalY, lidVerticalZ], true);
}

module deck_magnet_holes_even()
{

    translate ([  magnetHoleX/2 - (holesNumber*magnetHoleX)/2 - (holesNumber-1)*magnetSpacing/2
                , - deckY/2 + magnetHoleY/2 + magnetWall + deckBorderFront/2
                , deckZ/2 - magnetHoleZ/2 //- lidHorizontalZ
                ])

        for ( i = [0:1:holesNumber-1] )
        {
                translate ([i * (magnetHoleX + magnetSpacing), 0, 0])
                    cube([magnetHoleX,magnetHoleY,magnetHoleZ], true);

        }
}

module deck_magnet_holes_odd()
{

    translate ([  0
                , - deckY/2 + magnetHoleY/2 + magnetWall + deckBorderFront/2
                , deckZ/2 - magnetHoleZ/2 //- lidHorizontalZ
                ])

        for ( i = [0:1:holesNumber-1] )
        {

            if(i%2 == 0)
                translate ([(i/2)*(magnetHoleX + magnetSpacing), 0, 0])
                    cube([magnetHoleX,magnetHoleY,magnetHoleZ], true);

            if(i%2 == 1)
                translate ([-(i/2)*(magnetHoleX + magnetSpacing)-(magnetHoleX + magnetSpacing)/2, 0, 0])
                    cube([magnetHoleX,magnetHoleY,magnetHoleZ], true);

        }
}

module deck_text()
{
    rotate([0,90,90])
    translate([0, 0, deckY/2-2-0.1])
    linear_extrude(0.1+0.4)
    text(text = textString, font = fontName, size = fontSize, halign = "center", valign = "center", spacing=1.1, $fn=0.1);
}

module multi_line(lines){
    rotate([0,90,90])
    translate([0, (fontSize+2)/2, deckY/2-2-0.1])
    linear_extrude(0.1+0.4)
    union()
    {
        for (i = [0 : len(lines)-1])
            translate([0 , -i * (fontSize + 2), 0 ])
            text(text = lines[i], font = fontName, size = fontSize, halign = "center", valign = "center", spacing=1.1, $fn=0.1);
    }
}

module prism_cavity()
{

    translate([-cardsCavityX/2, -cardsCavityY/2 - (deckBorderFront+deckBorder)/2 +deckBorder, deckZ/2-(deckBorderFront - deckBorder) - lidVerticalZ - deckBorder])
        prism(cardsCavityX, deckBorderFront - deckBorder +10, deckBorderFront - deckBorder +10);

}

module extra_cavity()
{
    translate([-cardsCavityX/2, -cardsCavityY/2 - (deckBorderFront+deckBorder)/2 +deckBorder, -deckZ/2 + deckBase])
    cube([extraCavityX, extraCavityY, extraCavityZ]);

}

module internal_cavity()
{
    union()
    {
        cards_cavity();
        prism_cavity();
        extra_cavity();
    }
}

module stack_male()
{

translate([deckX/2+1.001-fit/2,0,0])

    difference()
    {

        resize([2-fit, deckY-(frame*2)-fit*4, deckZ-(frame*2)-fit*4])
//        resize([2-fit, deckY-(frame*2)-fit*2, deckZ-(frame*2)-fit*2])
        internal_cavity();

        translate([6,0, -(deckZ-(frame*2))/2])
        rotate([0,-45, 0])
        cube([10,400,10], true);

        translate([6,0, +(deckZ-(frame*2))/2])
        rotate([0,-45, 0])
        cube([10,400,10], true);

    }
}

module stack_female()
{
    translate([-deckX/2+0.999,0,0])

    difference()
    {

        resize([2, deckY-(frame*2), deckZ-(frame*2)])
        internal_cavity();

        translate([6,0, -(deckZ-(frame*2))/2])
        rotate([0,-45, 0])
        cube([10,400,10], true);

        translate([6,0, +(deckZ-(frame*2))/2])
        rotate([0,-45, 0])
        cube([10,400,10], true);

    }
}

module frame_negative()
{

    translate([0,deckY/2-1,0])
    difference()
    {

        cube([deckX-frame*2, 2, deckZ-frame*2], true);

        translate([0,-6, -(deckZ-(frame*2))/2])
        rotate([-45, 0, 0])
        cube([400,10,10], true);

        translate([0,-6, +(deckZ-(frame*2))/2])
        rotate([-45, 0, 0])
        cube([400,10,10], true);

    }
}

module deck()
{
    union()
    {

        difference()
        {
            deck_volume();
            lid_cavity_horizontal();
            lid_cavity_vertical();

            if(holesNumber%2 == 0)
                deck_magnet_holes_even();
            if(holesNumber%2 != 0)
                deck_magnet_holes_odd();

            internal_cavity();

            frame_negative();
            stack_female();
        }

        stack_male();

        if (textString2 == "")
            deck_text();
        else
            multi_line([textString, textString2]);

    }
}

/*
============================================================================================================
==============================LID===========================================================================
============================================================================================================
*/

module lid_horizontal()
{
    // points to be used
    p = [   [lidHorizontalX/2 -fit,lidHorizontalY/2 -fit,-lidHorizontalZ/2],                      // q 1 p 0
            [-lidHorizontalX/2 +fit,lidHorizontalY/2 -fit,-lidHorizontalZ/2],                     // q 2 p 1
            [-lidHorizontalX/2 +fit,-lidHorizontalY/2,-lidHorizontalZ/2],                    // q 3 p 2
            [lidHorizontalX/2 -fit, -lidHorizontalY/2,-lidHorizontalZ/2],                    // q 4 p 3
            [lidHorizontalX/2-lidSlope -fit,lidHorizontalY/2-lidSlope +fit,lidHorizontalZ/2],     // q 1 p 4
            [-lidHorizontalX/2+lidSlope +fit,lidHorizontalY/2-lidSlope -fit,lidHorizontalZ/2],    // q 2 p 5
            [-lidHorizontalX/2+lidSlope +fit,-lidHorizontalY/2,lidHorizontalZ/2],            // q 3 p 6
            [lidHorizontalX/2-lidSlope -fit, -lidHorizontalY/2,lidHorizontalZ/2]  ];         // q 4 p 7

    // faces drawn
    f = [
        [0,1,2]
        ,[2,3,0]
        ,[7,4,3]
        ,[4,0,3]
        ,[4,5,0]
        ,[5,1,0]
        ,[5,6,1]
        ,[6,2,1]
        ,[6,7,2]
        ,[7,3,2]
        ,[4,7,6]
        ,[6,5,4]
    ];

    translate([ 0
                ,-deckBorder/2/2  //divido per 2 tutte le variabili gia' presenti
                ,deckZ/2 - lidHorizontalZ/2
                ])
     polyhedron(points = p, faces = f);
}

module lid_vertical()
{
    translate([0
                , lidVerticalY/2 - deckY/2 -fit/2
                , - lidVerticalZ/2 + deckZ/2 +fit/2
    ])

    union()
    {
        rounded_cube([lidVerticalX - fit, lidVerticalY -fit, lidVerticalZ -fit], true, 1, "zmax");
        rounded_cube([lidVerticalX - fit, lidVerticalY -fit, lidVerticalZ -fit], true, 1, "ymin");
        translate([0, (lidVerticalY-fit)/4, -(lidVerticalZ-fit)/4])
        cube([(lidVerticalX-fit), (lidVerticalY-fit)/2, (lidVerticalZ-fit)/2], true);
    }
}

module cut_lid_horizontal() // serve perche' sporge dal lid vertical quando e' arrotondato
{
    translate([0
                , lidVerticalY/2 - deckY/2 -fit/2
                , - lidVerticalZ/2 + deckZ/2 +fit/2
    ])
    translate([0, -(lidVerticalY-fit)/4, (lidVerticalZ-fit)/4])
    cube([(lidVerticalX-fit), (lidVerticalY-fit)/2, (lidVerticalZ-fit)/2], true);
}

module lid_magnet_holes_even()
{

    translate ([  magnetHoleX/2 - (holesNumber*magnetHoleX)/2 - (holesNumber-1)*magnetSpacing/2
                , - deckY/2 + magnetHoleY/2 + magnetWall -fit/2
                , deckZ/2 - lidVerticalZ + magnetHoleZ/2
                ])

        for ( i = [0:1:holesNumber-1] )
        {
                translate ([i * (magnetHoleX + magnetSpacing), 0, 0])
                    cube([magnetHoleX,magnetHoleY,magnetHoleZ], true);
        }

}

module lid_magnet_holes_odd()
{

    translate ([  0
                , - deckY/2 + magnetHoleY/2 + magnetWall -fit/2
                , deckZ/2 - lidVerticalZ + magnetHoleZ/2
                ])

        for ( i = [0:1:holesNumber-1] )
        {

            if(i%2 == 0)
                translate ([(i/2)*(magnetHoleX + magnetSpacing), 0, 0])
                    cube([magnetHoleX,magnetHoleY,magnetHoleZ], true);

            if(i%2 == 1)
                translate ([-(i/2)*(magnetHoleX + magnetSpacing)-(magnetHoleX + magnetSpacing)/2, 0, 0])
                    cube([magnetHoleX,magnetHoleY,magnetHoleZ], true);

        }

}

module lid()
{
    difference()
    {
        union()
        {
            difference()
            {
                lid_horizontal();
                cut_lid_horizontal();
            }
         lid_vertical();
        }
        if(holesNumber%2 == 0)
            lid_magnet_holes_even();
        if(holesNumber%2 != 0)
            lid_magnet_holes_odd();

    }
}

/*
============================================================================================================
==============================MAGNET STOPPER================================================================
============================================================================================================
*/

module magnet_stopper()
{

 cube([magnetX-0.1, magnetY-0.1, 3], true);

}

```


[User]
Hey, I'm looking to create a customizable Dupont connector housing, similar to the one I found on Thingiverse, but with some specific requirements. I'd like to be able to adjust the number of pins, the thickness of the top and bottom, and have the option to add labels to the individual pins as well as a connector name. I also want the option to add cutouts on the sides to allow the housing to be used with a longer pin header.

Here's a breakdown of what I need:

*   **Number of Pins:** This should be a variable, allowing me to set the number of pins from 2 to 15.
*   **Side Cutouts:** Boolean toggles to enable/disable extra pin cutouts on the left and right sides.
*   **Pin Control:** An array to specify which pins are "active" (have a space for the pin) and which are blocked. This way I can block pins without having to recalculate all other pin numbers.
*   **Pin Labels:** An array to set labels for each pin. The labels should be printed or cut into the housing. I need control over text height, cut depth (if applicable), and font size.
*   **Connector Name:** A variable to specify a name for the connector, also to be printed or cut into the housing. I need control over font size and text position.
*   **Text Alignment:** I'd like the text for the labels to be aligned at either the top or bottom, with the connector name automatically placed on the opposite side.
*   **Top and Bottom Thickness:** Adjustable variables to control the thickness of the top and bottom parts of the housing.
*   **Plug Height:** A variable to control the height of the pin blocking plugs, in case I don't want them to be flush with the top of the housing.
*    **Wire Cutout width** A variable to control the width of the cutout for the wires.
*   **Basic Dimensions:** The core dimensions of a standard Dupont connector pin (width, height, length, etc.) should be used as default values.
*   **Display Options:** I need the ability to choose which parts to display on screen: Top & Bottom, Bottom Only, Top Only, or Text Only.
*   **Export Mode:** I'd like a way to specify if the part is being viewed for modeling or for export to STL. This helps with text orientation.
*   **Section View:** An option to enable a section view for design review.
*   **Libraries:** I don't need any special libraries but, I want to avoid the need to use any additional libraries such as `Chamfers-for-OpenSCAD`.

The goal is to be able to export separate STL files for the top, bottom and text if required. I need to be able to print the pin labels and connector name either as a separate part, or printed directly into the housing. For labels I want the ability to select between text cutouts and just printed text using a thinner layer.

Could you generate the necessary OpenSCAD code to achieve this?

[Assistant]
Dupont_N_pin_v6.scad:
```
/*****************
* Customizable Connector housing Dupont v6
* https://www.thingiverse.com/thing:5615282
*v6 17.11.22 + Добавлена возможность подписывать как отдельные пины, так и название коннектора. 3-х пиновый разъем позволяет сделать подпись 4-5 вполне читаемых символов в ширину, например I2C, OLED, SERVO и так далее.
*v5.1 16.11.22 + Проверка на undef в названиях пинов
*v5 16.11.22 + Печать названия коннектора.
*v4 15.11.22 + Подписи к пинам.
*v3 07.11.22 + Регулировка высоты разделителей(заглушек)пинов.
*v2 07.11.22 - плагин Chamfers-for-OpenSCAD. Версия не требующая надстроек, !!!в Настройщие online не работает!!!
*v1 03.11.22 Dерсия требующая надстройки Chamfers-for-OpenSCAD, в настройщие не работает.
*/
echo(version=version());
/*[Количество пинов / Number of pins]*/
count = 3;//[2:15]

//вырез под дополнительный пин слева
cut_left = true;//[false,true]
//вырез под дополнительный пин справа
cut_right = true;//[false,true]
/*[ Подписи / Signatures ]*/
//Массив включенных пинов, 1 или отсутствует - включено, 0 - заглушено. Указывать через запятую с первого до последнего заглушенного. Пример: [1,0] - заглушен пин 2 в 4-х пиновом разъеме.
On_Off = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];

//Подписи к пинам
signature = [
		//"A4 - SDA","A5 - SCL","","","5V","","GND","VIN","","","","","","","",
	 //"VIN","GND","","5V","","","A5 - SCL","A4 - SDA","","","","","","","",
		//"GND","5V","A4 - SDA","A5 - SCL","","","","","","","","","","",
		//"4 - SDA","5 - SCL","5V","GND","","","","","","","","","","",
	 //"","","","","","","","","","","","","","",""
		"EN - 10","DIR - 9","STEP - 8"
];

//Печатать подписи
p_sign=true;//[false,true]

//Выровнять подписи пинов по Низу = BOTTOM или по Верху = TOP. Положение Названия коннектора противоположно положению подписей пинов.
t_halign = "BOTTOM";//[TOP,BOTTOM]
//Варианты текста cut - вырезать на глубину "text_depth", print - текст не вырезается, а печатается слоем меньше толщины одного слоя.
var_Text = "print";//[cut,print]
//Высота текста
text_h = 0.15;//[0.1:0.01:0.2]
//Глубина выреза под текст
text_depth = 0.0;//[0.0:0.01:0.2]
//Размер шрифта подписей пинов
text_size = 2.0;//[1.6:0.1:2.6]

//Название коннектора. Если не заполнено, не выводится.
sign_Name = "STEP";
//Ориентация текста названия, "BOTTOM" - к ответной части разъема.
sign_Name_orient = "BOTTOM";//[TOP,BOTTOM]
//Размер шрифта Названия коннектора
sign_Name_font_size = 2.4;//[1.6:0.1:4.0]
//Корректировка положения Названия по оси Y
correct_pos_Name_Y=0.0;//[-12.0:0.1:12.0]

/*[ Основноые размеры / Basic dimensions ]*/
//Толщина верха
thickness_top = 1.0;//[0.6:0.1:2.4]
//Толщина низа
thickness_bot = 0.6;//[0.4:0.1:2.4]
//Высота заглушки, при 1.0 остается место для штыря.
h_plug = 1.0;//[0.4:0.2:2.6]
//Ширина выреза под провод
cutout_wires = 1.7;//[0.8:0.1:2.0]
/* [Hidden] */
Height_pin = 2.7;//Высота пина
Width_pin = 2.54;//Ширина пина
Length_Pin = 12.2;//Длина пина
pin_length = 6.5;//Длина выреза под дополнительный пин
Outer_Depth = 25;
ch=0.4;//фаска
pins_gap = 0.2;//Общий зазор пинов в ширину
wire_clamp_thickness = 1.2-cutout_wires/2; //Толщина прижима проводов
tX=0;

// Fixed values

//Толщина стенки
thickness_wall = 1.2;
mink_r = 0.4;//радиус закругления

// Calculated values
//ширина 2 стенки+ширина_пина*кол-во пинов
Outer_Width = thickness_wall+Width_pin*count+pins_gap+ thickness_wall;
//высота верх+низ +толщина_пина
Outer_Height = thickness_bot + Height_pin + thickness_top;
Cutout_width = 4.8;//Ширина выреза	под краба

arr_tr_1 = [[Outer_Width-0.4,25-(Cutout_width+1)/2-18.2,0],[Outer_Width-0.4,25-Cutout_width/2-3.15*2,0],[-0.01,25-(Cutout_width+1)/2-18.2,-0.01],[-0.01,25-Cutout_width/2-3.15*2,-0.01]];
gap_ar_2 = 0.8;
arr_tr_2 = [gap_ar_2-0.8,gap_ar_2-0.8,gap_ar_2-0.39,gap_ar_2-0.39];
h=Outer_Height;

/* [ View ] */
// Modeling - ориентация для просмотра вида модели, Export - ориентация для экспорта.
Export="Modeling"; //[Export,Modeling]
//Выбери что отображать на экране
Display = "Top&Bottom"; // [Top&Bottom,Bottom Only,Top Only,Text Only]
//Приподнять Top в режиме Моделирования
Lift_up = 3.4;//[0:0.2:5]
//Показать Разрез вдоль Y
View_Section=false; //[false,true]
//Глубина разреза
View_Section_Y  = 4.3;//[0.0:0.1:25]

//*********** Блокируем пины
		On_Off_s= [for(i=[1:count]) On_Off[i-1]==undef||On_Off[i-1]==1?1:0];

module BOTTOM(){
//bottom
		union(){
				difference(){
//***************Корпус
						translate([mink_r,mink_r,mink_r]){
								minkowski(){
										cube([Outer_Width-2*mink_r, Outer_Depth-2*mink_r, Height_pin-mink_r+thickness_bot],false);
										sphere(mink_r, $fn=50);
								};
						}
						  translate([0,0,Height_pin+thickness_bot]){cube([Outer_Width, Outer_Depth, Height_pin-2*mink_r+thickness_bot],false);}

								translate([0.01+thickness_wall,thickness_wall,-0.01+thickness_bot]){color("Teal")//Вырез под пины внутренний
										cube([Width_pin*count+0.02+pins_gap,25-thickness_wall*2-Length_Pin-pins_gap, Height_pin+thickness_top+0.02],false);//Вырез под пины
								}
								translate([0.01+thickness_wall,25-Length_Pin-pins_gap,-0.01+thickness_bot]){color("Teal")//Вырез под пины выходной
										cube([Width_pin*count+0.02+pins_gap,Length_Pin+pins_gap+0.02, Height_pin+thickness_top+0.02],false);//Вырез под пины
								}
								translate([0.01+thickness_wall,thickness_wall,1.0+thickness_bot]){color("Teal")//Вырез под пины выходной
										cube([Width_pin*count+0.02+pins_gap,25+pins_gap, Height_pin+thickness_top+0.02],false);//Вырез под пины
								}

				//******************Bottom вырезы под защелки
								for(i = [0:3]){
										union(){
												translate(arr_tr_1[i]){color("Teal")
														//color("red")
														cube([0.4+0.01, Cutout_width, Outer_Height+thickness_bot]);
														translate([arr_tr_2[i], Cutout_width,0]){//под "добавить зацепы"
																rotate([90,270,0]){
																		linear_extrude(height=Cutout_width){
																		polygon(points=[[0,-0.4], [0.4,0], [0,0.4]]);
																		}
																}
														}
												}
										}
								}
				//****************Bottom вырез под пины
								for(i = [0:count-1]){
										if(On_Off_s[i]!=0){
												offset_x=i*Width_pin;
												translate([offset_x+thickness_wall+pins_gap/2+(Width_pin-cutout_wires)/2,-0.01,1.5-cutout_wires/2+thickness_bot+cutout_wires/2]){//color("red")//Вырез под провода пинов
														cube([cutout_wires,25-Length_Pin+thickness_wall,5],false);//вырез для вставки проводов
												}
												//Вырез под провода пинов
												translate([(cutout_wires)/2+offset_x+thickness_wall+pins_gap/2+(Width_pin-cutout_wires)/2,-0.01,(cutout_wires)/2+1.5-cutout_wires/2+thickness_bot]){
														rotate([270,0,0]){
																cylinder(h=25-Length_Pin+thickness_wall,r = cutout_wires/2,center = false,$fn=30);
														}
												}
										}
								}

					//****************Bottom дополнительные вырезы под пины	сбоку
						if(cut_right==true){pin_cut(2.54*count);}
						if(cut_left==true){pin_cut(-2.54);}
					//****************Bottom End дополнительные вырезы под пины	сбоку

						  //**************Bottom вырез под щупы
								translate([0.5+thickness_wall+pins_gap/2,25-3.2-3,-0.01]){
								cube([Width_pin*count-thickness_wall+pins_gap,3,3],false);//
						}
				}
		}
		//**************Bottom Заглушки пинов
		for(i = [0:count-1]){
				if(On_Off_s[i]==0){//color("red")
						translate([thickness_wall+pins_gap+Width_pin*i,25-Length_Pin-pins_gap-h_plug,0]){//color("Teal")
								cube([Width_pin-pins_gap,Length_Pin+pins_gap, h_plug+thickness_bot],false);
								if(i==0||On_Off_s[i-1]==0){//заполнение
										translate([-pins_gap,0,0]){
												cube([pins_gap,Length_Pin+pins_gap, h_plug+thickness_bot],false);
										}
								}
								if(i==count-1){
										translate([Width_pin-pins_gap,0,0]){
												cube([pins_gap+0.04,Length_Pin+pins_gap, h_plug+thickness_bot],false);
										}
								}
						}
				}
		}
}

//Дополнительные вырезы под пины сбоку разъема
module pin_cut(offset_x=0){//
		translate([(cutout_wires)/2+offset_x+thickness_wall+pins_gap/2+(Width_pin-cutout_wires)/2,25-pin_length,(cutout_wires)/2+1.5-cutout_wires/2+thickness_bot]){
				rotate([270,0,0]){
						cylinder(h=pin_length,r = 0.65,center = false,$fn=30);
				}
		}
}

module TOP(){
		translate([0,0,Lift_up]){
				difference(){
						union(){
								translate([-0.5,0,Outer_Height-thickness_top]){//color("blue")
						//*************Top крышка тело
										minkowski(){
												minkowski(){
														translate([1.1,1,0]){
																cube([Outer_Width-4*ch, Outer_Depth-6*ch, thickness_top-ch/2]);
														}
														cylinder(0.001,0.2);//штука нужная, добавляет фаску на ТОПЕ, не удалять
												}
												translate([0.2,0.2,0]){
														polyhedron(
																points=[[0.2,0.2,0], [0.2,-0.2,0], [-0.2,-0.2,0], [-0.2,0.2,0], [0,0,0.2]],
																faces=[[0,1,4], [1,2,4], [2,3,4], [3,0,4], [1,0,3], [2,1,3]]
														);
												}
										}
								}
						//************Top Прижим проводов
						translate([thickness_wall+pins_gap,thickness_wall+2, Outer_Height-thickness_top-wire_clamp_thickness]){
									cube([Width_pin*count-pins_gap,1.2,thickness_top+wire_clamp_thickness],false);
							}
						//***************Top зацепы
						arr_tr_3 = [[-0.5,4.2,0],[-0.5,16.6,0],[-0.5-Outer_Width,-4.2*2,0],[-0.5-Outer_Width,-4.2-16.6,0]];
						x=2.0;//x ширина с зацепом
						y=Height_pin+thickness_top+thickness_bot+0.00;//высота
						zc=0.45;//ширина зацепа
						scos=1.04;//фаски на усилителе
						h_zc=0.8;//толщина усилителя
						l_zc=4.2;//ширина зацепа
						for(i = [0:3]){
								rotates = i>1 ? 180:0;
										rotate([0,0,rotates]){
										translate(arr_tr_3[i]){
												union(){//рычаг краба
/*******************Top Зацепы новые ****************************/
														translate([-0.741,l_zc,0]){
																rotate([90,0,0]){
																		difference(){
																		linear_extrude(height=l_zc){
																				polygon(points=[[0,scos],[scos,0]
																				,[x,0], [x-zc,zc],[x-zc,y-zc]
																				,[x,y],[scos,y]
																				,[0,y-scos]]
																		);}
																				translate([0-0.001,0,h_zc]){//Вырез под усилители
																					cube([x-scos-0.2,y,l_zc-2*h_zc]);
																				}
																		}
																}
														}
	/*******************end Зацепы новые ****************************/
											 }//end union
										}
								}
						}//end for
				}
						//***************Top Вырез под щупы
						translate([0.5+thickness_wall+pins_gap/2,25-3.2-3,2]){//
								cube([Width_pin*count-thickness_wall+pins_gap,3,1+thickness_top+thickness_bot],false);//
						}

					//****************Top дополнительные вырезы под пины	сбоку
						cut_gap=0.6;
						if(cut_right==true){pin_cut(2.54*count-cut_gap);}
						if(cut_left==true){pin_cut(-2.54+cut_gap);}
					//****************Top End дополнительные вырезы под пины	сбоку

				}//end differece
		}
}

/************************* Подписи пинов ****************************/
module Print_Number(move_h=0,ext_h=text_depth){
		move=0.35;
		if(p_sign){
				len_s=len(signature);
				tX=(Display=="Text Only")?5.08+(count-1)*2.54:0;
				for(i=[0:count-1]){
						if(signature[i]!=undef){
								translate([tX,0,0]){
										rotate([0,Display=="Text Only"?180:0,0]){
												translate([move,t_halign=="TOP"?1.7:17,move_h]) {
														//color("red")
														linear_extrude(height = ext_h)
														translate([3+i*2.54,0,0])
														rotate([0,Display=="Text Only"?0:0,90])
														text(str(signature[i]), size = text_size, font = "Liberation :style=Bold", halign = (t_halign=="TOP")?"left":"right", $fn = 18);
												}
										}
								}
						}//end undef
				}//end for
		}
}

/************************************ Название ******************************/

module Print_Name(move_h=0,ext_h=text_depth){

		len_sN=len(sign_Name);
		move_t_Name=-Outer_Width/2-sign_Name_font_size*0.02;//Центруем по длине надписи
		if(sign_Name != ""){
				tX=(Display=="Text Only")?0:5.08+(count-1)*2.54;
				translate([tX,0,0]){
						rotate([0,Display=="Text Only"?180:0,0]){
								translate([move_t_Name,t_halign=="TOP"?17:4.0-correct_pos_Name_Y,move_h]) {
										color("red")
										linear_extrude(height = ext_h)
										translate([0,(sign_Name_orient=="TOP")?-sign_Name_font_size*0.8:sign_Name_font_size*0.2,10])
										rotate([0,0,(sign_Name_orient=="TOP")?0:180])
										text(str(sign_Name), size = sign_Name_font_size, font = "Liberation :style=Bold", halign = "center", $fn = 18);
								}
						}
				}
		}//end if
}

/************************************ End Название ******************************/
assert(!(p_sign==true&&var_Text=="cut"&&Display=="Text Only"), "Нельзя отобразить только текст в варианте 'Вырезание текста', It is not possible to display only text in the 'Cut Text' option");

if(p_sign==true&&var_Text=="print"&&Display=="Text Only"){
		difference() {//Разрез
				union(){
						color("Red"){
								Print_Number(0,text_h);
						}
						Print_Name(0,text_h);
						translate([0,0,text_h]){//
								cube([Outer_Width,Outer_Depth,0.01],false);
						}
				}//end union
				if(View_Section==true&&Export=="Modeling"){//
						translate([-25,-50+View_Section_Y ,-10])
						cube([50,50,50],false);
				}
		}//end difference
}

//Распололжение в зависимости от выбранного варианта вида
x= Export=="Export" ? 1:0;
if (Display == "Bottom Only" ||
		Display == "Top&Bottom"){
		y= Display=="Bottom Only" ? 0:1;
		translate([0,0,(1-x)*y*.01]){
				difference() {//Разрез
						BOTTOM();
						if(View_Section==true&&Export=="Modeling"){
								translate([-25,-50+View_Section_Y ,-10])
								cube([50,50,50],false);
						}
				}
		}
}

if (Display == "Top Only" ||
		Display == "Top&Bottom") {
		y= Display=="Top Only" ? 0:1;
		translate([x*(y*(Outer_Width+3.6)+Outer_Width),0,x*(Lift_up+thickness_top+2.7+thickness_bot)]){
    rotate([x*180,0,x*180]){
						difference() {//Разрез
								union(){//Подписи
										TOP();
										if(Export=="Modeling"){
													color("Purple")
											 	if(var_Text=="print"){
															Print_Number(2.705-text_h+Lift_up+thickness_top+thickness_bot,text_h);//
															Print_Name(2.705-text_h+Lift_up+thickness_top+thickness_bot,text_h);
													}
										}
								}

//Всё что ниже вырезаем
								if(p_sign==true){color("red")
										Print_Number(2.705+Lift_up+thickness_top+thickness_bot-text_depth,text_depth);
										Print_Name(2.705+Lift_up+thickness_top+thickness_bot-text_depth,text_depth);
								}
								//Разрез
								if(View_Section==true&&Export=="Modeling"){
										translate([-25,-50+View_Section_Y ,-10])
										cube([50,50,50],false);
								}
						}
				}
		}
}

```


[User]
Hey, I'm looking to get some help creating an OpenSCAD design for a customizable vape tray. I have a basic idea and some of the code already, but it's not quite what I'm aiming for and frankly, it's a bit clunky.

Essentially, I need a rectangular tray with cylindrical holes for vape cartridges. I'd like this to be fully parametric so I can change the number of columns and rows of holes. Also important - I need a space to put labels under each column of holes, with vertical lines separating them and a horizontal line just above them. I'm thinking I can input some text as well and it'll apply them to the appropriate label areas.

I’m thinking the base should have rounded edges and the holes should have a little lip for easy insertion/removal of cartridges. The text should be extruded.  I'd like to use the "Comic Sans MS" font for the labels.

So, to be clear, I need a parametric OpenSCAD design that:

*   Creates a rectangular tray.
*   Has configurable number of rows and columns for the cartridge holes.
*   The tray base has rounded edges using a hull.
*   Each hole has a small lip/chamfer at the top
*   Has a space for labels below the holes for each column
*   Includes text labels and the spacing lines between them
*   Uses the "Comic Sans MS" font for the labels and lets the user specify the text.

I want the label height and size to also be parametric so they can be customized.

I don’t have a library in mind, so use whatever makes sense. I'd greatly appreciate a robust and easy to modify solution!

[Assistant]
VapeTray-parametric-withLabels.scad:
```
columns = 4;
rows = 5;
labels = "SHIC"; //Include same amout as (or fewer than) columns

module hole() {
    cylinder(h=25, r=6, $fn=60);
    translate([0,0,24]) {
        rotate_extrude($fn=60) {
            translate([-7,0,0]) {
                difference() {
                    square(1);
                    circle(r=1);
                }
            }
        }
    }
}

module box(width, length, height) {
    hull() {
        radius = 3;

        translate([radius, radius, 0])
        cylinder(h=height, r=radius, $fn=20);

        translate([width-radius,radius,0])
        cylinder(h=height, r=3, $fn=20);

        translate([radius,length-radius,0])
        cylinder(h=height, r=3, $fn=20);

        translate([width-radius,length-radius,0])
        cylinder(h=height, r=3, $fn=20);

        // Adding in label area below holes
        translate([width-radius,-10,0])
        cylinder(h=height, r=3, $fn=20);
        translate([radius,-10,0])
        cylinder(h=height, r=3, $fn=20);
    }

}

font = "Comic Sans MS";
letter_size = 8;
letter_height = 5;
module letter(l) {
	// Use linear_extrude() to make the letters 3D objects as they
	// are only 2D shapes when only using text()
	linear_extrude(height = letter_height) {
		text(l, size = letter_size, font = font, halign = "center", valign = "center", $fn = 16);
	}
}

width = columns * 18 ;
length = rows * 13 + 6;

difference() {
    box(width, length, 30);

    for(col = [0 : columns-1]) {
        for(row = [0 : rows-1]) {
            translate([col*18 + 9, row*13+9, 5])
            hole();
        }
    }

    // Add horizontal line above labels
    translate([3,0,29.5])
        linear_extrude(1) {square([width-6,1]); };

    // Add vertical lines between labels
    for(col = [1 : columns-1]) {
        translate([col*(width/columns)+.5,-12,29.5])
        rotate([0,0,90]){
            linear_extrude(1) { square([16+13*rows,1]); };
        };
    }

    // Add label text
    for(l = [0 : len(labels)-1]) {
    translate([9+18*l,-6,29.5])
        letter(labels[l]);
    }
}
```


[User]
I'd like to design a customizable wheel for 3D printing, something that could replace wheels on things like wagons or grills. I'm aiming for something that prints easily without supports, even on single-extruder printers like my Ender 3. The goal is to balance print speed, functionality, and strength, keeping in mind the limits of typical desktop 3D printers.

Here are the basic features I have in mind, with a range of flexibility:

**Overall Dimensions & Hub:**

*   **Overall Diameter (OD):** This should be easily adjustable, probably between 75mm (3") to 300mm (12"), but it should work for larger sizes too, if needed.
*   **Axel Hole Diameter:** This needs to be customizable, too, and should include a little clearance, likely 0.1-0.2mm extra, to avoid it being too tight.
*   **Hub Diameter:** The hub should be larger than the axel hole diameter.
*   **Wheel Height:** This is the thickness of the wheel, it should be customizable.

**Webs/Spokes:**

*   **Number of Webs/Spokes:**  I'd like to specify how many spokes the wheel has.
*   **Web/Spoke Thickness:** This should be adjustable.
*   **Semi-circle Cut Depth:** The spokes should have a semi-circle cutout to remove weight and reduce print time. The depth of this cut should be relative to the wheel's height and customizable.

**Rim:**

*   **Rim Thickness:**  The thickness of the material connecting the webs to the tread.
*   **Auto Rim Diameter:** Ideally, the rim diameter should automatically adjust based on the tread dimensions. It should be calculated as a function of the tread depth.
*   **Manual Rim Diameter Override:** It would be good if I could manually set the rim diameter, if needed.

**Tread:**

*   **Tread Shape:** The tread should be made of cones rather than a continuous layer, for easy printing.
*  **Tread Depth 1 & 2:** The "top" and "bottom" depth of each cone (the cone might be inverted). This should allow for a tread profile which varies with angle.
*  **Tread Wall:** The minimum distance between cone surfaces, this is for printability and should be adjustable
*   **Auto Tread Count:**  Ideally, the number of treads should automatically calculate based on the other tread parameters like tread depth and tread wall, and the wheel OD.
*   **Manual Tread Count Override:** I'd also need to be able to manually set the number of treads, if necessary.

I'm not looking for super-precise mechanical parts, just good functionality for something like a wagon or cart wheel. I'd like to avoid using libraries if possible, and keep the code concise.

I'd like the design to be fully parametric, so all the above parameters can be easily changed in the customizer. Could you generate the OpenSCAD file(s) for this?

[Assistant]
Wheel_Tire.scad:
```

//overall wheel diameter
OD=148;//[0:.01:500]
//Axel diameter (add clearance)
Axel_hole_D=9.00;//[0:.01:100]

//Hub diameter (must be larger than Axel diameter)
HubD=15;//[0:.1:130]
//thickness of wheel
wheel_height=30;
/*[Web/spokes]*/
//number of webs or spokes
WebNum=13;
WebAng=360/WebNum;
//thickness of webs
Webthick=1.75;
//depth of semicricle cut relative to wheel height
Cut_depth=0.33;
/*[Rim]*/
//inputs for rim
//Thickness of the rim connecting the webs to the tread
Rim_Thickness=3.5;//5.5;
//Checked automatically. Calculates the rim section based on the Treads
Auto_rim=true;
//Rim diameter if Auto is disabled
Rim_diameter_manual=117;

/*[Tread]*/
//for printablity the tread is made of cones. TreadDepth1 & TreadDepth2 are the top and bottom depth of the cones
TreadDepth1=15;
TreadDepth2=3;
//Minimum distance between any two cone surface. Lower values are faster prints but less strong
TreadWall=1.5;
//Checked automatically. Calculates the number of treads based on tread depth, tread wall and OD
Auto_Tread=true;
//Number of Treads of Auto is disabled, TreadWall may be ignored
Manual_Number_of_Treads=19;

$fa=4+.001; // simple "+" blocks customizer

tempR2=Stacked_R2(OD/2,TreadDepth1,TreadDepth2,TreadWall);
//echo("Treadnum");
echo("Treadnum LEVEL_0: ",floor(PI*OD/(2*TreadWall+TreadDepth1+TreadDepth2)));

Auto_Tread_parms=TParm2(tempR2,OD,TreadDepth1,TreadDepth2,TreadWall);

echo("Auto_Tread_Parms: ",Auto_Tread_parms);

Treadnum=Auto_Tread ? Auto_Tread_parms[0] :Manual_Number_of_Treads;
echo("Used Treadnum: ",Treadnum);
TreadAng=360/Treadnum;
T_R2=Tread_R2(OD/2,TreadDepth1,TreadDepth2,TreadWall,TreadAng/2);
echo("Tread_r2: ",T_R2);
//echo("new_Treadnum");
//echo(floor(2*PI*OD/(TreadWall+TreadDepth1+TreadDepth1)));

Rim_D=Auto_rim ? 2*T_R2-max(TreadDepth1,TreadDepth2)/2 :Rim_diameter_manual;
echo("Rim_D: ",Rim_D)

union(){

    Rim_ID=Rim_D-2*Rim_Thickness;
difference(){//Rim
    cylinder(wheel_height,d=Rim_D);
    DifTransX()cylinder(wheel_height+2,d=Rim_ID);
}
difference(){//Hub
    cylinder(wheel_height,d=HubD);
    DifTransX()cylinder(wheel_height+2,d=Axel_hole_D);
}
// spokes or webs
Web_Length=(Rim_ID-HubD)/2+1;
R_cut=R_cut_fun(Web_Length-1,wheel_height*Cut_depth);
for (i = [0:WebAng:(360.1-WebAng)]) {
    rotate([0,-90,i]){//roate it upritght and into Nth position
        translate([0,(HubD-.5)/2,-(Webthick/2)]){//Translate it in to position
            difference(){//build a web in the XY plane and cut a semicircle out of it
                cube([wheel_height,Web_Length,Webthick]);
                translate([R_cut+(1-Cut_depth)*wheel_height,Web_Length/2,-1])cylinder((Webthick+2),r=R_cut);
                //translate([45,22.5,-1])cylinder((Webthick+2),d=50);
            }
        }
    }
}

//Treads
difference(){
    cylinder(wheel_height,d=OD);
    DifTransX()cylinder(wheel_height+2,d=Rim_D-.2);
    for (i = [0:TreadAng:(360.1-TreadAng)]) {
        rotate([0,0,i]){
            translate([OD/2,0,-.5])cylinder(wheel_height+1,d1=TreadDepth1,d2=TreadDepth2);
            translate([T_R2,0,-.5])cylinder(wheel_height+1,d1=TreadDepth2,d2=TreadDepth1);
        }
        rotate([0,0,i+TreadAng/2]){
            translate([OD/2,0,-.5])cylinder(wheel_height+1,d1=TreadDepth2,d2=TreadDepth1);
            translate([T_R2,0,-.5])cylinder(wheel_height+1,d1=TreadDepth1,d2=TreadDepth2);
        }

    }
    }
}

module DifTransX()
      { translate([0,0,-1]) children(0);
      children([1:1:$children-1]);}

function R_cut_fun(Web_L,Cut_depth)=Web_L*Web_L/(8*Cut_depth)+(1/2)*Cut_depth;

function Tread_R2(OR,TD1,TD2,R2Wall,phi)=
      //echo(OR,TD1,TD2,R2Wall,phi)  //echo(min(Stacked_R2(OR,TD1,TD2,R2Wall),Diag_R2(OR,TD1,TD2,R2Wall,phi)))
      min(Stacked_R2(OR,TD1,TD2,R2Wall),Diag_R2(OR,TD1,TD2,R2Wall,phi));

function Stacked_R2(OR,TD1,TD2,SWall)=
      //echo(OR,TD1,TD2,SWall)
      //echo(OR-TD1/2-TD2/2-SWall)
      OR-TD1/2-TD2/2-SWall;

function Diag_R2(OR,TD1,TD2,DWall,phi)=
      //echo(OR,TD1,TD2,DWall,phi)
      let (Diag=max(TD1,TD2)+DWall)
      let (omga=-phi+asin(OR/Diag*sin(phi)))
      //echo(sqrt(OR*OR+Diag*Diag-2*OR*Diag*cos(omga)))
      sqrt(OR*OR+Diag*Diag-2*OR*Diag*cos(omga));

function Tread_parms(R2,OD,TD1,TD2,DWall) =
    let (Tnum=floor(2*PI*R2/(2*DWall+TD1+TD2)))
    let (TreadAng=360/Tnum)
    let (Parms1=Tread_parms(R2,OD,TD1,TD2,DWall))
    let (Tnum2=floor(2*PI*Parms1[2]/(2*DWall+TD1+TD2)))
    (Tnum==Tnum2)?Tread_parms(Parms1[2],OD,TD1,TD2,DWall):
    [Tnum,TreadAng,Tread_R2(OD/2,TD1,TD2,DWall,TreadAng/2)];

function TParm(R2,OD,TD1,TD2,DWall)=
    let (Tnum=floor(2*PI*R2/(2*DWall+TD1+TD2)))
    let (TreadAng=360/Tnum)
    [Tnum,TreadAng,Tread_R2(OD/2,TD1,TD2,DWall,TreadAng/2)];

//this could be done more cleanly with recursion
//but it always seems to stablize at ~level 2
//so 4 calls seems effective
function TParm2(R2,OD,TD1,TD2,DWall)=
    let (Parms1=TParm(R2,OD,TD1,TD2,DWall))
    echo("Treadnum LEVEL_1 :",Parms1[0])
    let (Parms2=TParm(Parms1[2],OD,TD1,TD2,DWall))
    echo("Treadnum LEVEL_2 :",Parms2[0])
    let (Parms3=TParm(Parms2[2],OD,TD1,TD2,DWall))
    echo("Treadnum LEVEL_3 :",Parms3[0])
    let (Parms4=TParm(Parms3[2],OD,TD1,TD2,DWall))
    echo("Treadnum LEVEL_4 :",Parms4[0])
    TParm(Parms4[2],OD,TD1,TD2,DWall);

```


[User]
Okay, I'm looking for an OpenSCAD design for a parametric clip or bracket, kind of like a C-clamp, but designed to hold cylindrical objects or flat bars. I've seen some nice designs on Thingiverse, particularly this one https://www.thingiverse.com/thing:3197218, and a remix here: https://www.thingiverse.com/thing:5635293 which does some of the things I need, but I'd really like something I can easily tweak for my specific projects.

Here’s what I’m envisioning:

**Core Features:**

*   **C-Clamp/Bracket Shape:**  The basic shape should be like a C-clamp, with a curved section forming a grip area, and straight arms extending out. The object will hold by pressure between the circular section of the "C" and the straight arms of the "C".
*   **Parametric Dimensions:**
    *   **Clip Diameter:**  This should control the diameter of the circular opening on the 'C' (the grip area).
    *   **Clip Thickness:**  The overall thickness of the bracket.
    *   **Arm Length:**  A parameter to control how long the "arms" of the C extend out. This should be able to scale in the x-direction, similar to what is in the remix, but this should be a configurable length.
    *   **Arm Thickness:** The thickness of the arms.
    *    **Arm Width:** The width of the arms, which should also scale.
    *   **Screw Hole:** A hole designed to accommodate a standard machine screw, with a countersunk area. I'd like to have a parameter for screw gauge (e.g., #4, #6, #8) that automatically sizes the hole appropriately and the countersink. There should also be an option to have these screw holes mirrored on the top and bottom.
    *   **Text Engraving:** A place to add a part ID. It would be great if I could specify the text, the size of the text (or it can calculate the size by a reasonable fraction of the clip diameter), and the placement (e.g. on the side or on the bottom).

**Additional Considerations:**

*   **Opening Calculation:**  The design should calculate and provide the actual opening size (distance between the arms) when the clip is fully open. The text output with `echo()` is perfect.
*   **Adjustability:** I should be able to adjust the arm length, arm width, and arm thickness, not just the entire object as the remix does with scaling, that's not what I'm after.
*   **Material Compensation:** While not critical, it would be nice to have a parameter to slightly adjust the clip diameter to account for material flex during printing (e.g. the need to compress the clip to use it). It should be an adjustment on the base `Clip_Diameter`.
*   **OpenSCAD Libraries:** Please try to avoid using any specific OpenSCAD libraries to keep things simple.

I’d like to control these parameters:
*   `clipDiameter`: The base diameter of the circular opening
*   `clipThickness`: The base thickness of the bracket
*   `armLength`: How far the "arms" of the clip extend out
*   `armThickness`: The thickness of the arms
*    `armWidth`: The width of the arms
*   `screwGauge`:  The screw gauge for the hole (e.g., 4 for #4 screw)
*   `screwHoles`:  A boolean to turn on or off top and bottom mirrored holes, single hole is fine when `false`
*   `engravingText`:  The text to engrave
*   `engravingSize`: The size of the engraving, or set to zero for auto size.
*    `materialCompensation`: How much to shrink the clip to account for material flex.
* `openingEcho`: Boolean, should `echo()` be included for opening dimension.

I think that should cover what I'm looking for. Let me know if you have any questions, and thanks in advance!

[Assistant]
bracket.scad:
```
// bracket.scad
// library for parametric Holder, Clip, Grip, Hook, Hanger, Bracket
// Author: Tony Z. Tonchev, Michigan (USA)
// last update: October 2018
// https://www.thingiverse.com/thing:3197218
//
// Remix https://www.thingiverse.com/thing:5635293
// Added opening calculation, scaling, and engraving
//

// (in mm)
Clip_Diameter = 30;//0.1

// (in mm)
Clip_Thickness = 15; //0.1

// #4-3mm, #6-3.5mm, #8-4mm, #9-4.5mm, #10-5mm (in mm)
Screw_Gauge = 4;
//scale[x,y,z]
part_scale = [1.01,1.0,1.0]; //[0.1:.1:5]
// Part ID (rwmix from 3197218)
engraving = "tv 5635293";
// 0 = auto
text_size = 0; //0.1

/* [Hidden] */

$fn=100;
TZT_Rad=Clip_Diameter/2;
TZT_Thk=Clip_Thickness;
TZT_Scr=Screw_Gauge;

text_size_v =text_size == 0 ? TZT_Rad/4 : text_size ;

scale(part_scale) translate([0*(-TZT_Rad+2),0,TZT_Thk/2])
stl();
module stl(){
difference () {
    TZT_CLIP();
    if ((TZT_Thk/8)<TZT_Scr) {
        TZT_SCREW();
    } else {
        translate ([0,0,TZT_Thk/4]) {
            TZT_SCREW();
        }
        translate ([0,0,-TZT_Thk/4]) {
            TZT_SCREW();
        }

    }
  //  translate([-TZT_Rad*(1.1+.3)+.2,0,TZT_Thk/4])rotate([90,0,-90])linear_extrude(100)text("thingiverse 3197218",size=1.5,halign="center");
    translate([-TZT_Rad*(1.1+.1),0,-TZT_Thk/2+0.2])rotate([180,0,-90])linear_extrude(1)text(engraving,size=text_size_v,halign="center",valign="center");
}
}

module TZT_SCREW () {
    translate ([-TZT_Rad*.98,,0]) {
        rotate ([0,-90,0]) {
            cylinder (TZT_Scr*1.1,TZT_Scr,0);
            cylinder (TZT_Rad*.6,TZT_Scr/2,TZT_Scr/2);
            translate ([0,0,-TZT_Scr]) {
                cylinder (TZT_Scr*1.1,TZT_Scr,TZT_Scr);
            }
        }
    }
}

module TZT_CLIP () {
    difference () {
        union () {
            translate ([-TZT_Rad*1.1,0,0]) {
                difference () {
                    cube ([TZT_Rad*.6,TZT_Rad*2.2,TZT_Thk],true) ;
                    translate ([TZT_Rad*.2,TZT_Rad*1.07,0]) {
                        cylinder (TZT_Thk*1.1,TZT_Rad*.2,TZT_Rad*.2,true);
                    }
                    translate ([TZT_Rad*.2,-TZT_Rad*1.07,0]) {
                        cylinder (TZT_Thk*1.1,TZT_Rad*.2,TZT_Rad*.2,true);
                    }

                }
            }
            cylinder (TZT_Thk,TZT_Rad*1.2,TZT_Rad*1.2,true);
        }
        cylinder (TZT_Thk*4,TZT_Rad,TZT_Rad,true);
        translate ([TZT_Rad*1.3,0,0]) {
            cylinder (TZT_Thk+1,TZT_Rad,TZT_Rad,true);
        }
    }
    echo(str("Opening: ",2*(sin(50)*TZT_Rad*1.1-(TZT_Rad*0.2)), " mm"));
    for (TZT_i=[-50:100:50]) {
        rotate ([0,0,TZT_i]) {
            translate ([TZT_Rad*1.1,0,0]) {
                cylinder (TZT_Thk,TZT_Rad*.2,TZT_Rad*.2,true);
            }
        }
    }
}
```


[User]
I'd like to design a parametric wall mount for a cylindrical object, specifically a Yandex Station Light but customizable for other similar objects. I'm envisioning a design that uses a circular base with several radial "handles" or support structures extending from it, with a top section that flares outwards to hold the object securely. I'd also like to have counterbores for screw mounting.

Here are the specific parameters I need to control:

*   **D**: Diameter of the cylinder the mount will hold (e.g., 90.5mm).
*   **H**: Height of the main cylinder-hugging part of the mount (e.g., 55mm).
*   **BOT_H**: Height of the bottom section of the mount (e.g., 4mm).
*   **TOP_H**: Height of the top flared section (e.g., 2mm).
*  **WALL_W**: Width of each "handle" or support structure (e.g., 3mm).
*  **WALL_N**: Number of "handles" or support structures (e.g., 5).
*  **WALL_RATIO**: Ratio that controls the curvature of the base, determining where the flat section of each handle transitions to a curve (e.g., 0.6).
*   **TOP_W**: Width of the top flare (e.g., 1.5mm).
*   **BOT_CUT_R**: Radius of the cut curve at the bottom of the handles (e.g., 25mm).
*   **SCREW_TOP_R**: Radius of the top of the screw counterbore (e.g., 4mm).
*   **SCREW_BOT_R**: Radius of the bottom of the screw counterbore (e.g., 1mm).
*   **SCREWS_DISTANCE**: Distance between the screw holes (e.g., 20mm).

The basic shape is a cylinder of diameter D and height H, sitting on a base of height BOT_H. The top section flares out by TOP_W over a height of TOP_H. There should be a number of equally spaced "handles" (WALL_N) around the base, with a width of WALL_W. The bottom of these handles should have a curved cut controlled by BOT_CUT_R. The screw counterbores should be located on the base, separated by SCREWS_DISTANCE, and each should transition from SCREW_TOP_R to SCREW_BOT_R over the depth of BOT_H.

I'd prefer the design be created using standard OpenSCAD functions; I don't need any specific libraries for this one. I'm thinking a `linear_extrude` combined with some clever `difference()` operations should get the job done, but I'm open to other suggestions.

[Assistant]
station_holder.scad:
```
D=90.5;
H=55;
BOT_H=4;
TOP_H=2;
WALL_W=3;
WALL_N=5;
WALL_RATIO=.6;
TOP_W=1.5;
BOT_CUT_R=25;
SCREW_TOP_R=4;
SCREW_BOT_R=1;
SCREWS_DISTANCE=20;

FULL_H=H+TOP_H+BOT_H;
R=D/2;

RENDER_DZ = 1;
RENDER_DA = .5;

EPSILON = 1;

// Uses plot function by Ryan A. Colyer.
// https://www.thingiverse.com/thing:2391851
module PlotAxialFunction(AxialFuncN, minz_stepz_maxz, num_circle_steps=360) {
  ang_step = 360 / num_circle_steps;
  minz = minz_stepz_maxz[0];
  stepz = minz_stepz_maxz[1];
  maxz = minz_stepz_maxz[2] + 0.001*stepz;
  minplot = 0.001*stepz;

  pointarrays = [
    for (z = [minz:stepz:maxz])
      [ for (ai = [0:num_circle_steps-1]) let(
            a = ai * ang_step,
            r = CallAxialFunc(z, a, AxialFuncN),
            rchecked = r < minplot ? minplot : r
          )
          [rchecked * cos(a), rchecked * sin(a), z]
      ]

  ];

  PlotClosePoints(pointarrays);
}

function CallAxialFunc(z, ang, n) =
  (n == 1) ? AxialFunc1(z, ang) :
  (n == 2) ? AxialFunc2(z, ang) :
  (n == 3) ? AxialFunc3(z, ang) :
  (n == 4) ? AxialFunc4(z, ang) :
  (n == 5) ? AxialFunc5(z, ang) :
  (n == 6) ? AxialFunc6(z, ang) :
  (n == 7) ? AxialFunc7(z, ang) :
  (n == 8) ? AxialFunc8(z, ang) :
  (n == 9) ? AxialFunc9(z, ang) :
  AxialFunctionNumberOutOfRange;

module PlotClosePoints(pointarrays) {
  function recurse_avg(arr, n=0, p=[0,0,0]) = (n>=len(arr)) ? p :
    recurse_avg(arr, n+1, p+(arr[n]-p)/(n+1));

  N = len(pointarrays);
  P = len(pointarrays[0]);
  NP = N*P;
  lastarr = pointarrays[N-1];
  midbot = recurse_avg(pointarrays[0]);
  midtop = recurse_avg(pointarrays[N-1]);

  faces_bot = [
    for (i=[0:P-1])
      [0,i+1,1+(i+1)%len(pointarrays[0])]
  ];

  loop_offset = 1;
  bot_len = loop_offset + P;

  faces_loop = [
    for (j=[0:N-2], i=[0:P-1], t=[0:1])
      [loop_offset, loop_offset, loop_offset] + (t==0 ?
      [j*P+i, (j+1)*P+i, (j+1)*P+(i+1)%P] :
      [j*P+i, (j+1)*P+(i+1)%P, j*P+(i+1)%P])
  ];

  top_offset = loop_offset + NP - P;
  midtop_offset = top_offset + P;

  faces_top = [
    for (i=[0:P-1])
      [midtop_offset,top_offset+(i+1)%P,top_offset+i]
  ];

  points = [
    for (i=[-1:NP])
      (i<0) ? midbot :
      ((i==NP) ? midtop :
      pointarrays[floor(i/P)][i%P])
  ];
  faces = concat(faces_bot, faces_loop, faces_top);

  polyhedron(points=points, faces=faces, convexity=8);
}

difference() {
    difference() {
        difference() {
            PlotAxialFunction(1, [0, RENDER_DZ, FULL_H], 360 / RENDER_DA);
            PlotAxialFunction(2, [BOT_H, RENDER_DZ, H+BOT_H], 360 / RENDER_DA);
        }
        PlotAxialFunction(3, [BOT_H+H - EPSILON, RENDER_DZ, FULL_H + EPSILON], 360 / RENDER_DA);
    }
    PlotAxialFunction(4, [- EPSILON, RENDER_DZ, BOT_H + EPSILON], 360 / RENDER_DA);
    translate([SCREWS_DISTANCE,0,0]) PlotAxialFunction(4, [- EPSILON, RENDER_DZ, BOT_H + EPSILON], 360 / RENDER_DA);
}

function base(z,a) =
    (cos(a*WALL_N) > WALL_RATIO)
        ? R+WALL_W
        : (R) - BOT_CUT_R * sqrt(sqrt(1 - (cos(a*WALL_N)+1) / (1+WALL_RATIO)));

function middle(a) =
    (cos(a*WALL_N) > WALL_RATIO)
        ? R+WALL_W
        : 0;

function AxialFunc1(z, a) =
    (z <= BOT_H)
        ? base(z, a)
        : middle(a);

function AxialFunc2(z, a) = R;

function AxialFunc3(z, a) = R + (z - FULL_H) / TOP_H * TOP_W;

function AxialFunc4(z, a) = SCREW_BOT_R + (SCREW_TOP_R - SCREW_BOT_R) / BOT_H * z;
```


[User]
I'd like to create a parametric OpenSCAD design for a filament swatch booklet. I've got some specific ideas in mind, and I'd like the design to be flexible so I can easily adjust things later.

Here's what I need:

**Core Design:**

*   The base of the design is a rectangular card, with adjustable width, height, and thickness.
*   I'd like to have rounded corners on the card, with a radius that I can control.
*   The card should have a spine along one edge, allowing me to assemble the swatches into a booklet. The spine should also have adjustable depth, height, and holes for binding, and the hole sizes should be adjustable.

**Text and Labeling:**

*   I want to be able to add multiple lines of text to the card. I'm thinking maybe 6 lines should be good.
*   Each line of text should have its own adjustable font, font size, and horizontal alignment (left or center).
*   I'll need adjustable margins for the text from the card edges, both for the sides and top.
*   I'd also like to control the vertical space between the text lines.
*  The text should be slightly raised off the surface of the card.

**Additional Features:**

*   A star icon should be included at the top, right of the card, and can be turned on or off (for favorite filaments).
*   It should also have a layer height preview along one side, with a set step height.
*    I'd like the option for a bridging test section on the edge of the card.
*   I'd like the ability to render just the card, just the text, or both the card and text together, which will help for multi-color prints.

**Specific Parameters:**

I'd like these parameters to be easily adjustable:

*   `Card_Width`
*   `Card_Height`
*   `Card_Thickness`
*   `Card_Corner_Radius`
*   `Card_Margin_Sides`
*   `Card_Margin_Top`
*   `Line_Margin`
*   `Text_Offset`
*   `Line_1_Position`, `Line_2_Position`, `Line_3_Position`, `Line_4_Position`, `Line_5_Position`, `Line_6_Position` (each can be "Left" or "Center")
*   `Line_1_Font`, `Line_2_Font`, `Line_3_Font`, `Line_4_Font`, `Line_5_Font`, `Line_6_Font`
*   `Line_1_Font_Size`, `Line_2_Font_Size`, `Line_3_Font_Size`, `Line_4_Font_Size`, `Line_5_Font_Size`, `Line_6_Font_Size`
*   `Line_1_Text`, `Line_2_Text`, `Line_3_Text`, `Line_4_Text`, `Line_5_Text`, `Line_6_Text`
*   `Star` (boolean)
*   `Layer_Heights_Preview` (boolean)
*   `Layer_Heights_Step_Height`
*   `Bridging_Test` (boolean)
*  `Render_Section` (can be "Card and Text", "Text Only", or "Card Only")
*   `Spine_Depth`
*   `Spine_Height`
*   `Spine_Hole_Width`
*   `Spine_Hole_Height`
*  `Spine_Hole_Depth`

I don't think you need any external libraries for this but let me know if you think otherwise.

[Assistant]
Booklet_Filament_Swatch.scad:
```
$fn = 30 + 0;

// How much margin separates the edge of the card from text?
Card_Margin_Sides = 6;

// How much margin separates the edge of the card from text?
Card_Margin_Top = 8;

// How rounded should the card/spacer corners be?
Card_Corner_Radius = 2;

// Indicate this is a favorite with a star icon.
Star = false;

// Include a layer heights preview.
Layer_Heights_Preview = true;

// How tall should the layer heights preview be?
Layer_Heights_Step_Height = 8;

// Include a bridging test.
Bridging_Test = false;

// How much space (mm) should be between each line of text?
Line_Margin = 2.5; // [0:1:4]

// How far should text be raised above the card surface?
Text_Offset = 0.6;

// Select a text position for line 1.
Line_1_Position = "Left"; //["Left", "Center"]
// Select a text position for line 2.
Line_2_Position = "Left"; //["Left", "Center"]
// Select a text position for line 3.
Line_3_Position = "Left"; //["Left", "Center"]
// Select a text position for line 4.
Line_4_Position = "Left"; //["Left", "Center"]
// Select a text position for line 5.
Line_5_Position = "Left"; //["Left", "Center"]
// Select a text position for line 6.
Line_6_Position = "Left"; //["Left", "Center"]

// Select a font for line 1.
Line_1_Font = "Liberation Sans:style=Bold";
// Select a font for line 2.
Line_2_Font = "Liberation Sans:style=Normal";
// Select a font for line 3.
Line_3_Font = "Liberation Sans:style=Italic";
// Select a font for line 4.
Line_4_Font = "Liberation Sans:style=Normal";
// Select a font for line 5.
Line_5_Font = "Liberation Sans:style=Normal";
// Select a font for line 6.
Line_6_Font = "Liberation Sans:style=Normal";

// Select a font size (mm) for line 1.
Line_1_Font_Size = 7; // [4:1:24]
// Select a font size (mm) for line 2.
Line_2_Font_Size = 7; // [4:1:24]
// Select a font size (mm) for line 3.
Line_3_Font_Size = 6; // [4:1:24]
// Select a font size (mm) for line 4.
Line_4_Font_Size = 6; // [4:1:24]
// Select a font size (mm) for line 5.
Line_5_Font_Size = 6; // [4:1:24]
// Select a font size (mm) for line 6.
Line_6_Font_Size = 6; // [4:1:24]

Line_1_Text = "Brand Name";
Line_2_Text = "Color";
Line_3_Text = "200-215 C";
Line_4_Text = "PLA";
Line_5_Text = "_";
Line_6_Text = "_";

// How wide is the card?
Card_Width = 90 + 0;

// How tall is the card?
Card_Height = 64 + 0;

// How thick is the card (mm)?
Card_Thickness = 1.2 + 0;

// Layer heights constants
layer_heights_step_width = Card_Width / 7;

// Use this setting to render text separately from card.
// Can be used for multicolor printing.
Render_Section = "Card and Text"; //["Card and Text", "Text Only", "Card Only"]

Spine_Depth = 1 + 0;
Spine_Height = 4 + 0;
Spine_Hole_Width = 2 + 0;
Spine_Hole_Height = 2 + 0;
Spine_Hole_Depth = 10 + 0;

difference() {

  union() {

    difference() {

      // Card body
      translate([-Card_Width/2, 0, 0]){
        cube([Card_Width, Card_Thickness, Card_Height], false);
      }

      // Cut out space for top left rounded corners
      translate ([-Card_Width / 2, 0, 0]) {
        cube([Card_Corner_Radius * 2, Card_Thickness * 3, Card_Corner_Radius * 2], true);
      }
      // Cut out space for top right rounded corners
      translate ([Card_Width / 2, 0, 0]) {
        cube([Card_Corner_Radius * 2, Card_Thickness * 3, Card_Corner_Radius * 2], true);
      }

    }

    // Line 1
    if (Line_1_Position == "Left") {
      translate ([-Card_Width / 2 + Card_Margin_Sides, 0, Card_Height - Card_Margin_Top]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_1_Font_Size, text = Line_1_Text, font = Line_1_Font, valign= "top");
          }
        }
      }
    }
    else if (Line_1_Position == "Center") {
      translate ([0, 0, Card_Height - Card_Margin_Top]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_1_Font_Size, text = Line_1_Text, font = Line_1_Font, valign= "top", halign="center");
          }
        }
      }
    }

    // Line 2
    if (Line_2_Position == "Left") {
      translate ([-Card_Width / 2 + Card_Margin_Sides, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_Margin]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_2_Font_Size, text = Line_2_Text, font = Line_2_Font, valign= "top");
          }
        }
      }
    }
    else if (Line_2_Position == "Center") {
      translate ([0, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_Margin]) {
          rotate([90, 0, 0]) {
            linear_extrude(height = Text_Offset, $fn = 100) {
              text(size = Line_2_Font_Size, text = Line_2_Text, font = Line_2_Font, valign= "top", halign="center");
            }
          }
      }
    }

    // Line 3
    if (Line_3_Position == "Left") {
      translate ([-Card_Width / 2 + Card_Margin_Sides, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_Margin * 2]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_3_Font_Size, text = Line_3_Text, font = Line_3_Font, valign= "top");
          }
        }
      }
    }
    else if (Line_3_Position == "Center") {
      translate ([0, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_Margin * 2]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_3_Font_Size, text = Line_3_Text, font = Line_3_Font, valign= "top", halign="center");
          }
        }
      }
    }

    // Line 4
    if (Line_4_Position == "Left") {
      translate ([-Card_Width / 2 + Card_Margin_Sides, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_3_Font_Size - Line_Margin * 3]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_4_Font_Size, text = Line_4_Text, font = Line_4_Font, valign= "top");
          }
        }
      }
    }
    else if (Line_4_Position == "Center") {
      translate ([0, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_3_Font_Size - Line_Margin * 3]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_4_Font_Size, text = Line_4_Text, font = Line_4_Font, valign= "top", halign="center");
          }
        }
      }
    }

    // Line 5
    if (Line_5_Position == "Left") {
      translate ([-Card_Width / 2 + Card_Margin_Sides, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_3_Font_Size - Line_4_Font_Size - Line_Margin * 4]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_5_Font_Size, text = Line_5_Text, font = Line_5_Font, valign= "top");
          }
        }
      }
    }
    else if (Line_5_Position == "Center") {
      translate ([0, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_3_Font_Size - Line_4_Font_Size - Line_Margin * 4]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_5_Font_Size, text = Line_5_Text, font = Line_5_Font, valign= "top", halign="center");
          }
        }
      }
    }

    // Line 6
    if (Line_6_Position == "Left") {
      translate ([-Card_Width / 2 + Card_Margin_Sides, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_3_Font_Size - Line_4_Font_Size - Line_5_Font_Size - Line_Margin * 5]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_6_Font_Size, text = Line_6_Text, font = Line_6_Font, valign= "top");
          }
        }
      }
    }
    else if (Line_6_Position == "Center") {
      translate ([0, 0, Card_Height - Card_Margin_Top - Line_1_Font_Size - Line_2_Font_Size - Line_3_Font_Size - Line_4_Font_Size - Line_5_Font_Size - Line_Margin * 5]) {
        rotate([90, 0, 0]) {
          linear_extrude(height = Text_Offset, $fn = 100) {
            text(size = Line_6_Font_Size, text = Line_6_Text, font = Line_6_Font, valign= "top", halign="center");
          }
        }
      }
    }

    // Add bottom left rounded corner
    translate ([-Card_Width / 2 + Card_Corner_Radius, Card_Thickness/2, Card_Corner_Radius]) {
      rotate([90, 0, 0]) {
        cylinder(Card_Thickness, Card_Corner_Radius, Card_Corner_Radius, true);
      }
    }
    // Add bottom right rounded corner
    translate ([Card_Width / 2 - Card_Corner_Radius, Card_Thickness/2, Card_Corner_Radius]) {
      rotate([90, 0, 0]) {
        cylinder(Card_Thickness, Card_Corner_Radius, Card_Corner_Radius, true);
      }
    }

    // Add spine
    translate([0, -Spine_Depth/2, Card_Height - Spine_Height/2]) {
      cube([Card_Width, Spine_Depth, Spine_Height], true);
    }

  }

  // Add spine holes
  Hole_Positions = [-35, -25, -5, 5, 25, 35];
  for (i = Hole_Positions){
    translate([i, 0, Card_Height - Spine_Height/2]) {
      cube([Spine_Hole_Width, Spine_Hole_Depth, Spine_Hole_Height], true);
    }
  }

  // Star
  if (Star) {
    translate([Card_Width / 2 - 7.5, Card_Thickness + 1, Card_Height - 10]) {
      rotate([90,180,0]) {
          linear_extrude(Card_Thickness + 2) {
            polygon([[0,-4],[1,-1.25],[3.75,-1.25],[1.5,0.425],[2.375,3],[0,1.5],[-2.375,3],[-1.5,0.425],[-3.75,-1.25],[-1,-1.25]]);
          }
      }
    }
  }

  // Layer heights preview
  if (Layer_Heights_Preview) {
    translate([0, 0, 0]) {
      translate([-layer_heights_step_width * 2, (Card_Thickness - 1.2)/2 - 0.4, 0]) {
        cube([layer_heights_step_width, Card_Thickness, Layer_Heights_Step_Height * 2], true);
      }
      translate([-layer_heights_step_width, (Card_Thickness - 1.2)/2 - 0.2, 0]) {
        cube([layer_heights_step_width, Card_Thickness, Layer_Heights_Step_Height * 2], true);
      }
      translate([0, (Card_Thickness - 1.2)/2, 0]) {
        cube([layer_heights_step_width, Card_Thickness, Layer_Heights_Step_Height * 2], true);
      }
      translate([layer_heights_step_width, (Card_Thickness - 1.2)/2 + 0.2, 0]) {
        cube([layer_heights_step_width, Card_Thickness, Layer_Heights_Step_Height * 2], true);
      }
      translate([layer_heights_step_width * 2, (Card_Thickness - 1.2)/2 + 0.4, 0]) {
        cube([layer_heights_step_width, Card_Thickness, Layer_Heights_Step_Height * 2], true);
      }
    }
  }

  // Bridging Test
  if (Bridging_Test) {
    translate([Card_Width/2, Card_Thickness, Card_Height/2]) {
      cube([Card_Margin_Sides * 2, Card_Thickness, Card_Height/2], true);
    }
  }

  if (Render_Section == "Card Only") {
    translate([-Card_Width/2 - 1, -Card_Thickness * 4, -1]) {
      cube([Card_Width + 2, Card_Thickness * 4, Card_Height + 2]);
    }
  }

  else if (Render_Section == "Text Only") {
    translate([-Card_Width/2 - 1, 0, -1]) {
      cube([Card_Width + 2, Card_Thickness * 4, Card_Height + 2]);
    }
  }

}

```


[User]
I need an OpenSCAD design for a base to support a vertical axis wind turbine, specifically a Savonius or Gorlov type. The base should house the generator, which will be a motor, and have gears to connect the main shaft to the motor. I need the design to be parametric so I can easily adjust the sizes of various components. I'm envisioning a box-like structure, with mounts for the bearings, motor, and gears.

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

*   **`rod_dia`**: Diameter of the main shaft rod.
*   **`case_screw_dia`**: Diameter of the screws used to hold the case together.
*  **`case_screw_indent`**: Depth of the counterbore for the case screws.
*   **`motor_screw_dia`**: Diameter of the screws used to mount the motor.
*   **`motor_dia`**: Diameter of the motor.
*   **`motor_screw_dist`**: Distance between the motor mounting screws.
*   **`motor_height`**: Height of the motor.
*   **`motor_frame_height`**: Height of the frame that holds the motor.
*   **`bearing_dia`**: Diameter of the bearings.
*   **`bearing_height`**: Height of the bearings.
*   **`gear_rod_dia`**: Diameter of the gear attached to the main shaft rod.
*   **`gear_motor_dia`**: Diameter of the gear attached to the motor shaft.
*   **`gear_height`**: Height of both gears.
*   **`gear_overlap`**: Overlap between the two gears.
*   **`wall`**: Thickness of the case walls.
*  **`render_part`**: Which part of the case to render - options should be 1 for top, 2 for bottom, 3 for both.
*   **`render_gears`**: Boolean (1/0) to render the gears or not.
*   **`render_motor`**: Boolean (1/0) to render the motor or not.

The design should include:

*   A main case with rounded corners.
*   Cutouts for the motor, main shaft, and motor screws.
*   A frame to hold the motor.
*   Bearing holders on the top and bottom of the case.
*   Gears that mesh together.
*   Screws and screw holes to secure the top and bottom parts of the case.
*   Optional connectors between top and bottom part of the case when printing.
* A cutout at the bottom of the motor housing for motor wires.
* Separate module for a main case with rounded corners using minkowski sums.
* Separate module for generating rounded corners.

I'd like to be able to render only the top part, the bottom part, both, or the full assembly, plus be able to toggle rendering of the gears and motor separately for easy inspection. I also need to be able to specify the wall thickness.

Please use the `minkowski()` function and a `cube_round` module, if needed to provide rounded corners for the main case.

I don't have any preference about libraries to use.

[Assistant]
wind_generator_base.scad:
```
$fn=32;

// Which part to render. Choose 1 for top, 2 for bottom, 3 for both
render_part = 2;

// Render gears - set to true to render gear shape
render_gears = 1;

// Render motor - set to true to render motor shape
render_motor = 1;

// rod diameter
rod_dia = 10;

// case screws
case_screw_dia = 5.2;

// case screw indents
case_screw_indent = 5;

// motor screw diameter
motor_screw_dia = 4;

// motor diameter
motor_dia = 30;

// motor screw distance
motor_screw_dist = 20;

// motor height
motor_height = 50;

// height of frame for motor
motor_frame_height = 10;

// bearing dia
bearing_dia = 20;

// bearing height
bearing_height = 4;

// rod gear diameter
gear_rod_dia = 60;

// motor gear diameter
gear_motor_dia = 10;

// gear height
gear_height = 5;

// gear overlap
gear_overlap = 2;

// wall thickness
wall = 4;

/***************
* Calculations *
***************/

case_height = gear_height + motor_height + bearing_height + 2*wall;
case_length = gear_rod_dia + gear_motor_dia + motor_dia + 2*wall;
case_width = max(motor_dia,gear_rod_dia+4*wall);

/*****************
* Render section *
*****************/

if (render_part == 1)
{
    part_top();
}

if (render_part == 2)
{
    part_bottom();
}

if (render_gears)
{
    gears();
}

if (render_motor)
{
    motor();
}

/************
* Functions *
************/

// top part
module part_top()
{
    difference()
    {
        case();
        translate([-case_length/2,-case_width/2,-case_height/2])
        {
            cube([case_length,case_width,case_height-(wall+gear_height)]);
        }
    }
    difference()
    {
        connectors(gap=0.8);
        case_screw_holes();
    }
}

// bottom part
module part_bottom()
{
    difference()
    {
        case();
        translate([-case_length/2,-case_width/2,+case_height/2-(wall+gear_height)])
        {
            cube([case_length,case_width,wall+gear_height]);
        }
        connectors();
    }
}

// case
module case()
{
    difference(){
        main_case();
        translate([gear_rod_dia/2+gear_motor_dia/2-gear_overlap,0,-case_height/2+wall]){
            motor_cutout();
        }
        cylinder(d=rod_dia,h=case_height,center=true);
        case_screw_holes();
    }

    // motor frame
    translate([gear_rod_dia/2+gear_motor_dia/2-gear_overlap,0,-case_height/2+wall])
    {
        motor_frame();
    }

    // bearing holder top
    translate([0,0,case_height/2-2*wall])
    bearing();

    // bearing holder bottom
    translate([0,0,-case_height/2+wall])
    bearing();
}

// gears
module gears()
{
    translate([0,0,-case_height/2+motor_height+wall]){
        cylinder(d=gear_rod_dia,h=gear_height);
        translate([gear_rod_dia/2+gear_motor_dia/2-gear_overlap,0,0]){
            cylinder(d=gear_motor_dia,h=gear_height);
        }
    }
}

// motor shape
module motor()
{
    translate([gear_rod_dia/2+gear_motor_dia/2-gear_overlap,0,-case_height/2+wall])
    {
        cylinder(d=motor_dia,h=motor_height);
    }
}

// motor cutout
module motor_cutout()
{
    // motor shape
    cylinder(d=motor_dia,h=motor_height);
    // motor screws
    for(i=[-1,1]) {
        translate([0,i*motor_screw_dist/2,-wall])
        {
            cylinder(d=motor_screw_dia,h=wall);
            cylinder(d2=motor_screw_dia,d1=2*motor_screw_dia,h=1);
        }
    }
    // lower cutout for cables
    translate([-(motor_dia+4)/2,-(motor_screw_dist-motor_screw_dia*2-2)/2,-wall/2])
    {
        cube([motor_dia+4,motor_screw_dist-motor_screw_dia*2-2,wall/2]);
    }
}

// motor frame
module motor_frame()
{
    difference()
    {
        cylinder(d=motor_dia+2*wall,h=motor_frame_height);
        translate([-(motor_dia+2*wall)/2,-(motor_dia-2*wall)/2,0])
        {
            cube([motor_dia+2*wall,motor_dia-2*wall,motor_frame_height]);
        }
        cylinder(d=motor_dia,h=motor_height);
    }
}

module bearing()
{
    difference(){
        cylinder(d=bearing_dia+2*wall,h=bearing_height);
        cylinder(d=bearing_dia,h=bearing_height);
    }
}

// main case
module main_case()
{
    difference()
    {
        translate([-case_length/2,-case_width/2,-case_height/2])
            cube_round([case_length,case_width,case_height],mki=2*case_screw_dia+wall);
        translate([-case_length/2,-case_width/2,-case_height/2+wall])
            cube_round([case_length,case_width,case_height-2*wall],mki=2*case_screw_dia+wall);
    }
    // case_screws
    case_screws();
}

// case screws
module case_screws()
{
    for(i = [1,-1])
    {
        for(j = [1,-1])
        {
            translate([0,j*(case_width/2-(2*case_screw_dia+wall)/2),0])
            {
                translate([i*(case_length/2-(2*case_screw_dia+wall)/2),0,-case_height/2])
                {
                    cylinder(h=case_height,d=2*case_screw_dia+wall);
                }
            }
        }
    }
}

// case screw holes
module case_screw_holes()
{
    for(i = [1,-1])
    {
        for(j = [1,-1])
        {
            translate([0,j*(case_width/2-(2*case_screw_dia+wall)/2),0])
            {
                translate([i*(case_length/2-(2*case_screw_dia+wall)/2),0,-case_height/2])
                {
                    cylinder(h=case_height,d=case_screw_dia);
                    cylinder(h=case_screw_indent,d=2*case_screw_dia,$fn=6);
                    translate([0,0,case_height-case_screw_indent]){
                        cylinder(h=case_screw_indent,d=2*case_screw_dia);
                    }
                }
            }
        }
    }
}

module connectors(gap=0)
{
    for(i = [1,-1])
    {
        for(j = [1,-1])
        {
            translate([0,j*(case_width/2-(2*case_screw_dia+wall)/2),0])
            {
                translate([i*(case_length/2-(2*case_screw_dia+wall)/2),0,case_height/2-(2*wall+gear_height)])
                {
                    cylinder(h=2*wall,d=2*case_screw_dia-gap);
                }
            }
        }
    }
}

module cube_round(dim,center=false,mki=5,plane="xy")
{
    if(mki<=0)
    {
        cube(dim);
    }
    else
    {
        if(plane=="xy")
        {
            translate([mki/2,mki/2,0])
            {
                linear_extrude(dim[2])
                {
                    minkowski()
                    {
                        square([dim[0]-mki,dim[1]-mki],center=center);
                        circle(d=mki);
                    }
                }
            }
        }
        if(plane=="yz")
        {
            translate([0,mki/2,dim[2]-mki/2])
            {
                rotate([0,90,0])
                {
                    linear_extrude(dim[0])
                    {
                        minkowski()
                        {
                            square([dim[2]-mki,dim[1]-mki],center=center);
                            circle(d=mki);
                        }
                    }
                }
            }
        }
        if(plane=="xz")
        {
            translate([mki/2,dim[1],mki/2])
            {
                rotate([90,0,0])
                {
                    linear_extrude(dim[1])
                    {
                        minkowski()
                        {
                            square([dim[0]-mki,dim[2]-mki],center=center);
                            circle(d=mki);
                        }
                    }
                }
            }
        }
    }
}
```


[User]
Hey, I'm looking to create a parametric snowflake ornament that can be assembled from three flat, interlocking pieces. It's kind of like a 3D puzzle, meant to be stored flat.

I need an OpenSCAD design where the snowflake is made up of three identical parts, rotated 120 degrees from each other, that slot together. The assembled snowflake should have a roughly circular profile, about 9 to 10 cm in diameter, or maybe slightly larger if there's room. The parts should have a main spine with decorative spikes coming off of it, and a central hub section. The outer edge of each part should have a slot that interlocks with the spine of the next part.

Each part should be flat, and the thickness should be adjustable, ideally in the range of 1.2 to 3.0 mm. I need to be able to control the overall radius of the snowflake, so I can make it smaller or larger. A parameter for the clearance in the interlocking slots would also be essential, maybe adjustable in increments of 0.01mm. It would be really useful if there's also a locking barb on one of the parts that holds the assembled snowflake together, though I'd like it to be optional.

I’d like to be able to easily generate STL files, and I think it would also be cool to have some animation options so I can view the assembly process. The parts should all be able to fit on a typical home 3D printer build plate. I'd prefer not to use any external libraries for this design, since it's relatively simple. Could you generate the necessary SCAD files?

[Assistant]
snowflake-slotted.scad:
```
// Snowflake Ornament, consisting of three parts that slot together
// By Curt McDowell <maker@fishlet.com> 2021-11-11
// Licensed under the Creative Commons - Attribution - ShareAlike.
// https://creativecommons.org/licenses/by-sa/4.0/

// OpenSCAD usage
// - Window -> Hide Customizer, make sure it's unchecked.
// - Select Object to generate in customizer.
// - To begin an animation, select View -> Animate, set Steps=3 and FPS=1.
// - To generate STL, select "All parts for printing", hit F6 to render,
//   and hit F7 to save STL.
// - If the print bed is not big enough for all three parts, generate them
//   one at a time by selecting "Part X for printing".

// Object to generate
Part = "flat123";   // [assembled: Assembled, flat1: Part 1 for printing, flat2: Part 2 for printing, flat3: Part 3 for printing, flat123: All parts for printing, animate_print: Animate printed, upright1: Part 1 upright, upright2: Part 2 upright, upright3: Part 3 upright, animate_upright: Animate upright, barb_check: Barb check = parts 1 and 3, intersection_check: Intersection check]

// Locking barb to keep parts from slipping apart. Not needed if superglued.
Barb = true;

// Render instead of preview (in case too many artifacts)
Render = false;

// Overall thickness
Thickness = 2.0; // [1.2 : 0.2 : 3.0]

// Overall radius; small=50, large=80
R = 60; // [40 : 1 : 200]

// Clearance for interlocking parts
Slot_Clearance = 0.16; // [0.10 : 0.01 : 0.40]

module _() {} // End of parameters

// Distance of slot midpoints from main axis
Slot_R = Thickness / 2.0 * 1.6;

Spine_W = R / 20;
Spike_L = R / 10;
Spike_W = R / 24;

$inf = 1000;
$extra_width = 0;

// A spike capped with a triangle
module spike(length, width) {
    translate([-width / 2 - $extra_width, 0])
        square([width + 2 * $extra_width, length]);

    translate([0, length + width * sqrt(3) / 6])
        rotate(-30)
            circle(width * sqrt(3) / 3, $fn = 3);
}

module spike_pair(r, scale)
    translate([0, r]) {
        rotate(-60)
            spike(Spike_L * scale, Spike_W);
        rotate(60)
            spike(Spike_L * scale, Spike_W);
    }

module hexagon(radial_pos, radius)
    translate([0, radial_pos])
        rotate(30)
            circle(radius + $extra_width, $fn = 6);

module arm(a, part)
    difference() {
        union() {
            // Main spine
            spike(R * 0.75, Spine_W);

            hexagon(R / 2, Spine_W * 2);

            spike_pair(R / 4, 1.5);
            spike_pair(R / 3, 1.75);
            spike_pair(R / 2, 2);

            if (!(part == 3 && a == 0) &&
                !(part == 1 && a == 3) &&
                !(part == 2 && (a == 0 || a == 3)))
                spike_pair(R * 15 / 24, 1.25);
        }

        hexagon(R / 2, Spine_W * 2 * 0.4);
    }

module flake(part) {
    difference() {
        // Central hexagon
        hexagon(0, R / 5);
        hexagon(0, R / 6.5);
    }

    // Arms
    for (a = [0 : 5])
        rotate(a * 60)
            arm(a, part);
}

// Generate the main 2D model for a wing
module pattern(part) {
    // Wider spine (flange) for slot
    flange_r = R * 1.1;
    flange_w = (Slot_R + Slot_Clearance + Thickness * 2.5) * 2;

    w = (part == 2) ? flange_w / 2 : flange_w;
    ext = R / 24;

    if (part == 1 || part == 2)
        translate([-flange_w / 2, -flange_r / 2])
            square([w, flange_r / 2 + ext]);

    if (part == 3 || part == 2)
        translate([(part == 2) ? 0 : -flange_w / 2, -ext])
            square([w, flange_r / 2 + ext]);

    flake(part);
}

// Turn 2D model into a wing
module wing(angle, r = Slot_R)
    rotate(angle)
        translate([0, r, 0])
            rotate([90, 0, 0])
                translate([0, 0, -Thickness / 2 - $extra_width])
                    linear_extrude(Thickness + 2 * $extra_width, convexity=8)
                        children();

// For a part that's been transformed and had slots cut out,
// undo the wing transformations to lay it back on the X-Y plane.
module wing_flatten(angle)
    translate([0, 0, Thickness / 2 + $extra_width])
        rotate([-90, 0, 0])
            translate([0, -Slot_R, 0])
                rotate([0, 0, -angle])
                    children();

module slot_mask(angle, upper)
    rotate(angle) {
        // Mask radius greater than largest radius of pattern (~R)
        mask_r = R * 1.25;
        mask_pos = (upper ?  mask_r - Slot_Clearance :
                            -mask_r + Slot_Clearance);
        translate([0, Slot_R, mask_pos])
            cube([$inf,
                  Thickness + Slot_Clearance * 2,
                  mask_r * 2],
                 center = true);
    }

module part3() {
    difference() {
        wing(240)
            pattern(3);
        slot_mask(120, upper = true);
        slot_mask(0, upper = true);
    }
}

module part2() {
    difference() {
        wing(120)
            pattern(2);
        slot_mask(0, upper = true);
        slot_mask(240, upper = false);
    }
}

module barb() {
    // Hack in a little barb on part1 to catch and retain part3.
    // Part2 is then held captive between part1 and part3.
    // Warning: this could stand more tweaking or a redesign.
    wing(0)
        let (r1 = R / 6, r2 = R / 3)
            polygon([[0, -r1],
                     [Slot_R * 1.8, -r1],
                     [0, -r2]]);
}

// The barb on part1 is trimmed by part 3 to remove overlap
// and provide a snug fit with Slot_Clearance.
module barb_trimmed()
    difference() {
        barb($extra_width = -0.001);
        part3($extra_width = Slot_Clearance);
    }

module part1() {
    difference() {
        wing(0)
            pattern(1);
        slot_mask(120, upper = false);
        slot_mask(240, upper = false);
    }

    if (Barb) {
        // For barb_check, the little barb is made visible (red)
        if (part == "barb_check")
            #barb_trimmed();
        else
            barb_trimmed();
    }
}

frame = 1 + round($t * 3);

part = (Part == "animate_upright" ? str("upright", frame) :
        Part == "animate_print" ? str("flat", frame) :
        Part);

echo(str("Displaying part: ", part));

module render_or_preview() {
    if (Render)
        render()
            children();
    else
        children();
}

render_or_preview() {
    if (part == "upright1" || part == "barb_check" || part == "assembled")
        part1();

    if (part == "flat1")
        wing_flatten(0)
            part1();

    if (part == "upright2" || part == "assembled")
        part2();

    if (part == "flat2")
        wing_flatten(120)
            part2();

    if (part == "upright3" || part == "barb_check" || part == "assembled")
       part3();

    if (part == "flat3")
        wing_flatten(240)
            part3();

    if (part == "flat123") {
        dx = R * 0.70;
        dy = R * 0.60;

        translate([0, dy, 0])
            wing_flatten(0)
                part1();
        translate([-dx, -dy, 0])
            wing_flatten(120)
                part2();
        translate([dx, -dy, 0])
            wing_flatten(240)
                part3();
    }

    // Parts should not intersect at all
    if (part == "intersection_check") {
        intersection() {
            part1();
            part2();
        }
        intersection() {
            part2();
            part3();
        }
        intersection() {
            part3();
            part1();
        }
        // Part should not go below print bed
        intersection() {
            wing_flatten(0)
                part1();
            translate([-$inf / 2, -$inf / 2, -$inf - 0.001])
                cube([$inf, $inf, $inf]);
        }
    }
}

```


[User]
I'd like to design a parametric collet for a Morse Taper 2 (MT2) fitting, suitable for light-duty stationary use, like on a woodworking lathe. I need to be able to customize the shaft diameter, the number of slits in the collet, the slit width, the "bridge" thickness between the slits, and a chamfer at the end. I need a user-friendly OpenSCAD file that's easily customizable using the OpenSCAD customizer.

Here are the specific details I'd like to be able to control:

*   **Shaft Diameter:**  This will be the diameter of the hole in the center of the collet where the tool or work piece will go. It should accept a value in either millimeters or inches.
*   **Shaft Units:** A toggle to set the shaft diameter to be either mm or inches.
*   **Number of Slits:**  The number of cuts or slits along the length of the collet.
*   **Slit Width:** The width of each slit.
*   **Bridge Percentage:** The thickness of the bridge between the slits as a percentage of the circle circumference at the inner diameter of the collet.
*   **Chamfer:** The size of the chamfer at the collet end.

The MT2 dimensions can be considered fixed. They are as follows:
*   Outer Diameter (d1) = 0.7 inches
*   Inner Diameter (d2) = 0.5720 inches
*   Taper = 0.0500 (taper amount per inch)
*   Length = 2.56 inches

I'd also like to use the `rotate_extrude` module, as it allows for fairly straightforward generation of the shape. Please use a high `$fn` value (like 128) for smooth curves. Finally, please ensure that the OpenSCAD file uses a reasonable set of comments to be easy to follow. I am not requesting any specific libraries for this design.

[Assistant]
MT2Collect.scad:
```
/* Morse Taper 2 (MT2) collet */

/* Customizer values */
shaft = 6.0; //0.01
shaftIsMM = true;
nSlits = 5;
slit = 0.5; //0.01
bridgePercent = 100.0;
chamfer = 1;

module __Customizer_Limit__ () {}

//constants
in2MM = 25.4;
ep = 0.001; //epsilon - for rounding

//MT2 data -https://uwoodcraft.com/morse-taper-dimensions/
d1 = 0.7 * in2MM; //outer diameter
d2 = 0.5720 * in2MM; //inner diameter
taper = 0.0500;
length = 2.56 * in2MM;

bridge = d2 * PI / nSlits * bridgePercent * 0.005;
shaftMM = shaft * (shaftIsMM ? 1 : in2MM);

module Body(shaft=shaftMM)
{
    pts = [[shaft, -length], [d2, -length],
        [d1, length], [shaftMM + chamfer, length], [shaft, length-chamfer]] / 2;
    rotate_extrude($fn=128)
        polygon(pts);
}

module Slits()
    for (up = [-1, 1])
        for (s = [0:(nSlits)])
            rotate([0, 0, (s * 2 + up * .5) * 180 / nSlits])
                translate([0, -slit/2, bridge*up - length/2])
                    cube([d2, slit, length]);

module Part()
    difference()
    {
        Body();
        Slits();
    }

Part();
```