[User]
I need help creating a parametric drill guide for round pipes, specifically for a project like the Mostly Printed CNC. I've been using a design I found but want to rebuild it from scratch so I can understand it better, and I’d like it to be more robust.

Essentially, the guide needs to be a clamp that holds a round pipe and allows drilling perpendicular holes at specific, configurable locations. Here's a breakdown of the parameters I need to control:

*   **Nut Spacing:** The distance between the centers of the drill holes. I need to be able to set the primary spacing between the first and third holes (`NutSpacing`) and the spacing between the first and second holes (`NutSpacing2`). If only two holes are needed, the second spacing can effectively be ignored.
*   **Drill Hole Diameter:**  The diameter of the drill holes, which will ultimately be for bolts, but I don’t want to assume the size of the bolt - just give me the hole size (`DrSize`).
*   **Pipe Outer Diameter:**  The outer diameter of the pipe to clamp (`OD`).  The guide should fit the pipe snugly.
*   **Base Thickness:** The thickness of the guide's base, which sits under the pipe (`Thickness`).
*   **Wall Thickness:** The thickness of the walls on either side of the pipe holding it in place (`Wall_Thk`).
*   **Nut Trap Offsets:** The offset from the left edge of the clamp to the center of the first hole (`LtNutOffset`) and the offset from the right edge of the clamp to the last hole (`RtNutOffset`).
*   **Number of Holes:** This should be configurable - 1, 2, or 3 holes (`Num_Holes`).
*   **Number of Guide Halves:** I need to be able to print either one or two halves of the clamp (`Qty`).

The design should have a cutout to accommodate the pipe and holes for the drill bit at the specified locations. It also needs to have a gap in the center where the clamping force is applied. I'd prefer a clean, solid model without any unnecessary complexity. If this can also include a center clamp width that can be adjusted, that would also be great.

I am not requesting code from any particular library. Can you help me generate this?

[Assistant]
Zpipe_DrillGuide_rev7.scad:
```
//Drill guide for Mostly Printed CNC Z Pipe
//7/3/2015  By: David Bunch
//Revised center cuts for larger Diameter holes to work better
//
//Revised 10/4/2015
//Designed for Bosch Toolholder with Dust cover using 2 parts
//76.25mm 1st & 3rd hole
//28.01mm 1st & 2nd hole
//48.24mm 2nd & 3rd hole

//Revised 8/5/2017 to make Wall thickness & Base Thickness separate variables & fixed one bug

//CUSTOMIZER VARIABLES

//1. Spacing between Nuts (Standard is 76.2mm)
NutSpacing = 76.2;      //(I used 53mm for the rotary tool I am using)

//2. Spacing between 1st & 2nd Nut
NutSpacing2 = 28;    //Use 0 if only using 2 nuts

//3. Dia of Drill Hole (9/64" Drill bit, Added .2mm for slip fit)
//M3 = 3.8;
DrSize = 3.8;

//4. Pipe Dia. to Use (Added 0.8 for slight tight fit)
OD = 23.8;              //23.7mm was a little tight & 24.0 was a little loose for me

//5. Thickness at bottom of Pipe
Thickness = 5;

//6. Thickness of Walls on each side of pipe
Wall_Thk = 5;

//7. Offset from Left for 1st Nut Trap
LtNutOffset = 19.05;      //Original Design is 19.05mm

//8. Offset from Right of 2nd Nut Trap (Original is 6.35mm)
RtNutOffset = 8.35;       //(I decided to add 2mm to this gap)

//9. Number of Holes (1, 2 or 3)
Num_Holes = 2;        //I needed 3 for the Bosch quick disconnect mount

//10. Print 1 or 2 Nut Traps
Qty = 2;

//CUSTOMIZER VARIABLES END
//----------------------------------------
//Begin calculations based on input variables

//The 0.7 gives about 0.7mm length segments on arcs.
//The 0.5 gives about 0.5mm length segments on arcs for the smaller hole.
Pipe_res = (round(((OD * 3.14) / 4) / 0.7) * 4);
Hole_res = (round(((DrSize * 3.14) / 4) / 0.5) * 4);
Cl_Wid = 6;             //Center Clamp width, not really used

MidNutOffset = LtNutOffset + NutSpacing2;
echo("MidNutOffset = ", MidNutOffset);
TotCenGap=NutSpacing - DrSize - 12;         //I use 6mm plastic around holes
echo("TotCenGap = ",TotCenGap);

DrRad = DrSize / 2;
echo("DrRad = ", DrRad);
THK = OD /2 + Thickness;

DrLen = OD + Thickness + Thickness+2;

echo("THK = ",THK);

Width = OD + Wall_Thk + Wall_Thk;
echo("Width = ",Width);

WidSlice = Width + 2;
Rad = OD / 2;
echo("Rad = ", Rad);

Ht = LtNutOffset + NutSpacing + RtNutOffset;
echo("Ht = ", Ht);

CenCub_Z2 = LtNutOffset + DrRad + 3;         //6mm of plastic each side of hole

CenCub_Z1 = MidNutOffset + DrRad + 3;

CenCubLen1 = NutSpacing - NutSpacing2 - (6 + DrSize);                     //Use this if Cl_Wid = 0;
//CenCubLen2 = (TotCenGap - Cl_Wid) / 2;
CenCubLen2 = NutSpacing2 - (6 + DrSize);

echo("CenCubLen1 = ", CenCubLen1);
echo("CenCubLen2 = ", CenCubLen2);
//translate([LtNutOffset,0,0])
//%cube([DrRad + 3,4,10]);
echo("Pipe_res = ", Pipe_res);
echo("Hole_res = ", Hole_res);
module Base()
{
    translate([0,0,THK])
    rotate([0,90,0])
    difference()
    {
        translate([0,-Width/2,0])
        cube([THK,Width,Ht]);                   //Basic starting cube
        translate([0,0,-1])
        cylinder(d=OD,h=Ht+2,$fn=Pipe_res);     //Cut for where pipe is laid
        if (Cl_Wid > 0)
        {
            translate([-1,-Rad,CenCub_Z2])
            cube([THK+2,OD,CenCubLen2]);           //cube cut out of 1st part of center
            translate([-1,-Rad,CenCub_Z1])
            cube([THK+2,OD,CenCubLen1]);           //cube cut out of 2nd part of center
        } else {
            translate([-1,-Rad,CenCub_Z])
            cube([OD,OD,CenCubLen1]);          //cube cut out of center
        }
        translate([-1,-Rad,-1])
        cube([THK+2,OD,(LtNutOffset - DrRad - 6) + 1]);         //Slice a cube cut at left side
        translate([-(Thickness+2),-(WidSlice/2),LtNutOffset])
        cube([THK,WidSlice,NutSpacing]);        //Slice across Top for less plastic

        translate([0,0,LtNutOffset])
        rotate([0,90,0])
        cylinder(d=DrSize,h=DrLen,center=true,$fn=Hole_res);       //Drill Bottom Hole
        if (Num_Holes == 3)
        {
            translate([0,0,LtNutOffset+NutSpacing2])
            rotate([0,90,0])
            cylinder(d=DrSize,h=DrLen,center=true,$fn=Hole_res);       //Drill Middle Hole
        }
        if (Num_Holes > 1)
        {
                translate([0,0,LtNutOffset+NutSpacing])
                rotate([0,90,0])
                cylinder(d=DrSize,h=DrLen,center=true,$fn=Hole_res);    //Drill Top Hole
        }
    }
}
            //translate([-1,-Rad,CenCub_Z])
            //cube([OD,OD,CenCubLen2]);           //cube cut out of 1st part of center
Base();
if (Qty == 2)
{
    translate([0,Width+6,0])
    Base();
}
```


[User]
I'm looking to generate a design for a drying rack, specifically one that I can use with acetone vapor smoothing of 3D printed parts. I've got a basic idea but need help turning it into a parametric OpenSCAD design.

The rack should be circular with a raised rim around the outside and a grid pattern inside. It should also have three supporting pillars on the bottom.

Here are the specifics:

*   **Overall shape:** Circular
*   **Outer Diameter:** Should be a parameter that defaults to 130mm.
*   **Rim:** The rim should be a raised ring around the circumference of the rack, with its width and height being parameters. Let's default to 3mm width and 2mm height, but these should be adjustable.
*   **Grid:** The interior should be a grid of intersecting lines. The lines should have a thickness (parameter, default is 0.37 * 4 = 1.48mm ) and the space between grid lines should also be a parameter (default to 10mm). The grid should extend close to the edge of the rim but should be shortened enough to avoid going under the rim. There should also be central orthogonal grid lines. The grid height should match the rim height.
*   **Pillars:** There should be three pillars underneath the rack to raise it up off a surface. These should be placed evenly around the circumference of the rack, a set distance from the outside of the rim. They should have a cylindrical base on the bottom and be flat on top, and a specified width, height, and the base cylinder radius should be slightly less than the base width, and a specified thickness. Let's start with a pillar width of 5mm, pillar height of 25mm, and a base cylinder radius of (5 -1.3)/2 = 1.85 mm, and a base thickness of 2mm. They should be a set distance from the outside of the rim; they should be flush with the inside edge of the rim.
*   **Parametric:** Everything above should be definable as a parameter, including the number of pillars if it's not too much trouble.

I'd also like to ensure the outer ring (the rim) is smooth. I was having some issues with aliasing there in a previous design. I'm thinking a difference of two cylinders, with one being slightly larger in diameter, but perhaps there's a more standard way of doing this.

I am not worried about the material properties.

I think that covers it. Can you provide a parametric OpenSCAD file, or files, that accomplish this?

[Assistant]
DryingRack.scad:
```
//Diameter of rack (mm)
outerDiam = 130;
rimRad = outerDiam/2;
// mm
rimWidth = 3;
// mm
rackHeight = 2;
// mm
gridSpacing = 10;
// mm
extrusionWidth = 0.37;
// Grid line thickness is grid width * extrusion width
gridWidth = 4;
// mm
pillarWidth = 5;
//mm
pillarHeight = 25;
numBars = floor(outerDiam/2/gridSpacing);
$fn = 50;

difference()
{
    union()
    {
        translate([0,0,rackHeight/2])
        {
            // Rim
            difference()
            {
                cylinder(d=outerDiam,h=rackHeight,center=true);
                cylinder(d=outerDiam-rimWidth,h=rackHeight,center=true);
            }

            // Central cross

            cube([gridWidth*extrusionWidth,outerDiam,rackHeight],center=true);
            cube([outerDiam,gridWidth*extrusionWidth,rackHeight],center=true);

            // Grid
            for(i=[1:numBars])
            {
                chord = 2*sqrt(pow(rimRad,2)-pow(i*gridSpacing,2))-1.5;
                translate([i*gridSpacing,0,0])
                cube([gridWidth*extrusionWidth,chord,rackHeight],center=true);
                translate([-i*gridSpacing,0,0])
                cube([gridWidth*extrusionWidth,chord,rackHeight],center=true);
                translate([0,i*gridSpacing,0])
                cube([chord,gridWidth*extrusionWidth,rackHeight],center=true);
                translate([0,-i*gridSpacing,0])
                cube([chord,gridWidth*extrusionWidth,rackHeight],center=true);

            }
        }  // translate
    // Pillars

    translate([0,rimRad-pillarWidth/2,pillarHeight/2])
    {
        cube([pillarWidth,pillarWidth,pillarHeight],center=true);
        translate([0,0,pillarHeight/2])
        cylinder(d=pillarWidth-1.3,h=2);

    }
    translate([(rimRad-pillarWidth/2)*cos(30),-(rimRad-pillarWidth/2)*sin(30),pillarHeight/2])
    {
        rotate([0,0,-30])
        {
            cube([pillarWidth,pillarWidth,pillarHeight],center=true);
        }
        translate([0,0,pillarHeight/2])
        cylinder(d=pillarWidth-1,h=2);
    }

    translate([-(rimRad-pillarWidth/2)*cos(30),-(rimRad-pillarWidth/2)*sin(30),pillarHeight/2])
    {
        rotate([0,0,210])
        {
            cube([pillarWidth,pillarWidth,pillarHeight],center=true);
        }
        translate([0,0,pillarHeight/2])
        cylinder(d=pillarWidth-1,h=2);
    }
    }  // Union
    translate([0,rimRad-pillarWidth/2,0])
    cylinder(d=pillarWidth-1,h=2);
    translate([-(rimRad-pillarWidth/2)*cos(30),-(rimRad-pillarWidth/2)*sin(30),0])
    cylinder(d=pillarWidth-1,h=2);
    translate([(rimRad-pillarWidth/2)*cos(30),-(rimRad-pillarWidth/2)*sin(30),0])
    cylinder(d=pillarWidth-1,h=2);
    difference()
    {
        cylinder(d=outerDiam+rimWidth,h=rackHeight);
        cylinder(d=outerDiam,h=rackHeight);
    }

} // Difference
```


[User]
Hey there, I'm looking to create a clam gauge using OpenSCAD. I've seen a few designs online but they're not quite what I need, and I'd like something more flexible.

Essentially, the gauge needs to have a hole of a specified diameter for checking if a clam is a legal size, and I'd like to support both metric (mm) and imperial (inches) measurements. The design should be a solid, printable object, probably cylindrical with a cutout in the center.

Here's a breakdown of the parameters I'd like to control:

*   **`measurementSystem`**: A variable that selects between metric (mm, value of 1) or imperial (inches, value of 0).
*   **`legalSizeDiameter`**: The diameter of the hole. It should accept a floating-point number, and its interpretation depends on the value of `measurementSystem`. If `measurementSystem` is 1, then this is in mm; if `measurementSystem` is 0, then this is in inches.
*   **`nozzleDiameter`**: This is also a floating-point number in mm and used to adjust the hole size to compensate for printing tolerances. This value should _always_ be in mm, regardless of the measurement system. So, a positive `nozzleDiameter` makes the hole larger, while negative values make it smaller.
*   **Text Labels**: Text labels should be printed onto the design which indicate the diameter of the hole and the units. If the `measurementSystem` is 1, then the text should say `[legalSizeDiameter] mm`. If `measurementSystem` is 0, then the text should say `[legalSizeDiameter] IN`.

I'd like the gauge to have a kind of handle or a second, smaller cylinder attached to the main cylinder via a hull operation, just to make it easier to hold. This smaller cylinder should be off to one side. I'd like the text labels to appear near this handle on the top surface. The base object should have a base height of 4mm, and the hole should extend through the part. The text labels should be printed on the top surface. I think it'd be helpful if the text labels were also red in the final product.

Also, I'd want the text to use the `Write` library from Thingiverse thing:16193, so please use that to create the text. I don't need to submit the text library since it's already published. I will make sure to include the correct `use` statement in my top-level SCAD file to reference the library.

Can you put this into parametric OpenSCAD files for me, please?

[Assistant]
clamGauge.scad:
```
// clam gauge
// Insane measurements, i.e, inches
use <write/Write.scad>
// preview[view:south, tilt:bottom]

measurementSystem=0; //[0:Insane (inches), 1:Metric (mm)]

//The legal shell diameter -- enter mm if using metric, inches if using insane (AKA imperial) measurements.
legalSizeDiameter=1.5;

//THIS MEASUREMENT IN MM ONLY!  You can also use this setting to adjust the hole diameter to account for printing artifacts -- increasing the number makes the hole larger, decreasing (even negative) makes the diameter smaller.
nozzleDiameter=0.5;

/* Private */
sizeText=str(legalSizeDiameter);
module metricGauge()
{

	difference()
	{
		hull()
		{
			translate([0,0,0]) cylinder(r=((legalSizeDiameter/2)+10), h=4);
			translate([(legalSizeDiameter*1.4),0,0]) cylinder(r=10, h=4);
		}

		union()
		{
			translate([0,0,-1]) cylinder(r=((legalSizeDiameter/2)+nozzleDiameter), h=6);
			translate([(legalSizeDiameter*1.4),0,-1]) cylinder(r=5, h=6);

			if (legalSizeDiameter<=80)
			{
				color("red") translate([(legalSizeDiameter*0.95),0,4.1]) write(sizeText,t=2,h=(6*legalSizeDiameter/25),rotate=90,center=true);
				color("red") translate([((legalSizeDiameter*0.95)-(5*legalSizeDiameter/25)),0,4.1]) write("mm",t=2,h=(3*legalSizeDiameter/25),rotate=90,center=true);
			} else {
				color("red") translate([(legalSizeDiameter*0.95),0,4.1]) write(sizeText,t=2,h=20,rotate=90,center=true);
				color("red") translate([((legalSizeDiameter*0.95)-21),0,4.1]) write("mm",t=2,h=16,rotate=90,center=true);
			}
		}
	}
}

module insaneGauge()
{

	difference()
	{
		hull()
		{
			translate([0,0,0]) cylinder(r=((legalSizeDiameter/2/0.039)+10), h=4);
			translate([(legalSizeDiameter/0.039*1.4),0,0]) cylinder(r=10, h=4);
		}

		union()
		{
			translate([0,0,-1]) cylinder(r=(legalSizeDiameter/2/0.039), h=6);
			translate([(legalSizeDiameter/0.039*1.4),0,-1]) cylinder(r=5, h=6);
			if (legalSizeDiameter<=3)
			{
				color("red") translate([(legalSizeDiameter/0.039*0.95),0,4.1]) write(sizeText,t=2,h=(6.6*legalSizeDiameter),rotate=90,center=true);
				color("red") translate([((legalSizeDiameter/0.039*0.95)-(7*legalSizeDiameter)),0,4.1]) write("IN",t=2,h=(4*legalSizeDiameter),rotate=90,center=true);
			} else {
				color("red") translate([(legalSizeDiameter/0.039*0.95),0,4.1]) write(sizeText,t=2,h=20,rotate=90,center=true);
				color("red") translate([((legalSizeDiameter/0.039*0.95)-21),0,4.1]) write("IN",t=2,h=16,rotate=90,center=true);
			}
		}
	}
}

module finalAssembly()
{

	//translate and rotate to place words on printbed for nice printing.
	translate([0,0,4])
	{
		rotate([180,0,0])
		{
			if (measurementSystem==1)
			{
				metricGauge();
			} else {
				insaneGauge();
			}
		}
	}
}

finalAssembly();

```


[User]
I need help designing some parametric t-nuts for OpenBuilds V-slot, similar to the ones I found online but with a few modifications and a more robust parameterization. The original design was for plastic nuts that could be tapped or screws could be forced into, but I'd like mine to accept standard metal nuts and also have the option to use them without a nut. I’m not interested in using any kind of press-fit method - I'd prefer to fully enclose the metal nut.

Here are the specifics:

1.  **Metal Nut Accommodation:** The design should have a cavity to fully enclose a standard hex nut. I need to be able to specify the size of the hex nut using parameters (e.g., "hex\_nut\_size" which would be the size of the nut as specified by the distance between parallel faces). I also need to parameterize the nut's thickness, perhaps with a variable named "hex\_nut\_thickness". The cavity should include a small amount of clearance around the nut on all sides and the top/bottom - perhaps 0.2mm or so of clearance everywhere, which could be specified using a "clearance" parameter.

2.  **Plastic-only Option:** The design should also have an option to create the same nut profile without a nut cavity, essentially like the original design.  There should be a parameter, perhaps "use\_metal\_nut", which is a boolean to control whether a hex nut cavity is included or not. If this is false, there should still be a hole, also parameterizable by diameter with "hole\_diameter" so that I can tap it or just drive a screw into it.

3.  **T-Slot Profile:** The outer profile of the nut should be designed to fit standard OpenBuilds V-Slot rails, with dimensions similar to the original design linked below, specifically the T shape that fits into the slot.

4.  **Length and Array:** I need parameters to control the length of the nut ("nut\_length") and parameters for creating arrays of nuts (similar to the original design), like "rows" and "columns", along with "row\_spacing" and "column\_spacing". The "spacing" parameters should specify the distance between the center of one nut and the next.

5.  **Optional Counterbore:** For some applications, I might want to have a counterbore on the top side of the nut for a flat-head screw to sit flush or slightly recessed. There should be a parameter, like "counterbore", which could be a boolean to enable a counterbore, along with "counterbore\_diameter" and "counterbore\_depth" parameters. The counterbore should only be generated if the metal nut option is turned on or if the hole diameter is present.

6.  **File Naming:**  I'd like the primary file to be named something like "parametric\_vslot\_nut.scad".

I am not sure whether you have existing libraries for these, but I'm hoping that you can generate a suitable file from scratch that fulfills these requirements. It would be great if the code was well-commented and easy to modify later.

Here's the original code for reference:

```
// parametric T-Slot nuts for OpenBuilds V-Slot

holeDiameter = 4.8; // diameter of nut hole
nutLength = 10; // overall length of nut
rows = 4; // number of rows of nuts to make
columns = 3; // number of columns of nuts to make

module nut()
{
	rotate([180,0,0])
	difference()
	{
		translate([-5.25, -nutLength/2, 0]) rotate([270,0,0]) linear_extrude(height=nutLength) polygon([ [0,0], [10.5, 0], [10.5, 1.4], [7.75, 3.9], [2.75, 3.9], [0, 1.4] ]);
		translate([0,0,-1.2]) cylinder(r=holeDiameter/2, h=10, $fn=100, center=true
       );

	}
}

for(x=[0:(rows-1)]) for(y=[0:(columns-1)])
{
	translate([x*13, y*(nutLength + 2), 0]) nut();
}

*translate([-6, -7.5, 0]) cube([120, 120, 1]);
```

[Assistant]
V-Slot_no_nut.scad:
```
// parametric T-Slot nuts for OpenBuilds V-Slot

holeDiameter = 4.8; // diameter of nut hole
nutLength = 10; // overall length of nut
rows = 4; // number of rows of nuts to make
columns = 3; // number of columns of nuts to make

module nut()
{
	rotate([180,0,0])
	difference()
	{
		translate([-5.25, -nutLength/2, 0]) rotate([270,0,0]) linear_extrude(height=nutLength) polygon([ [0,0], [10.5, 0], [10.5, 1.4], [7.75, 3.9], [2.75, 3.9], [0, 1.4] ]);
		translate([0,0,-1.2]) cylinder(r=holeDiameter/2, h=10, $fn=100, center=true
       );

	}
}

for(x=[0:(rows-1)]) for(y=[0:(columns-1)])
{
	translate([x*13, y*(nutLength + 2), 0]) nut();
}

*translate([-6, -7.5, 0]) cube([120, 120, 1]);
```


[User]
Hey there, I'm looking for some help designing a bike mount for my phone using OpenSCAD. I've got an iPhone 6, but I'd like the design to be easily customizable for other phones and cases. Basically, I want a clamp that attaches to the handlebars and holds the phone securely.

Here's what I'm envisioning:

**General Structure:**

*   The mount should consist of two mirrored halves that clamp around the handlebars.
*   There should be a "bridge" connecting the clamp to the phone holder section.
*   The phone holder should have a box-like shape with a cutout in the left side for easier phone removal.

**Key Dimensions (These should be parameters):**

*   `phoneWidth`: Width of the phone (and its case) in millimeters.
*   `phoneLength`: Length of the phone (and its case) in millimeters.
*  `phoneHeight`: Thickness of the phone (and its case) in millimeters.
*   `phoneCornerRad`: Radius of the rounded corners on the phone and case, in millimeters
*   `handlebarDiameter`: Diameter of the bike's handlebars, in millimeters.
*   `bridgeHeight`: Vertical distance between the handlebar clamp and the bottom of the phone holder, in millimeters.
*   `rimWidth`: Thickness of the material around the phone, in millimeters.
*  `barWidth`: Width of the bar on the side of the phone cutout, in millimeters.
*  `barTop`: Fraction of phone length that the top of the bar should be placed, starting from the top of the phone (e.g 0.6 would make it slightly below center)

**Specific Design Elements:**

*   **Phone Holder:**
    *   Rounded rectangular box with rounded corners on the inside and outside.
    *   Cutout on the left side to make it easier to remove the phone.
    *   A supporting bar across the cutout to provide stability
    *   A small lip at the base of the holder to keep the phone from sliding out.
*   **Handlebar Clamp:**
    *   Two mirrored semi-circular clamps that grip the handlebars using an angled linear extrusion.
    *   There should be a flat section where the bridge attaches.

**Customization:**

*   All the dimensions I've listed above should be parameters that can be easily changed in the OpenSCAD file.
*   I would like to be able to adjust the vertical position of the "bar" across the cutout.

**Important Notes:**

*   I want the design to use `difference()` operations extensively to subtract shapes and create the desired form.
*  I would like to use the same rounding method as in the attached code.

I believe I've attached the code for an earlier version as a starting point, along with the description, but I would like the new design to be entirely parametric and more customizable. I'm looking for a clean, well-structured design, but the main goal is that it's functional and easy to adapt to different phones. I'd also like for the design to be efficient in terms of rendering.

I think you'd be able to handle this for me, right?

[Assistant]
iphone6BikeMountRevised.scad:
```

// mm
phoneWidth = 71;

// mm
phoneLength = 142;

// mm
phoneHeight = 10;

// mm
phoneCornerRad = 10;

// mm
handlebarDiameter = 25;

// Measured from feet to bottom of holder (mm)
bridgeHeight = 20;

leftHalf();
mirror([1,0,0]) leftHalf();
module leftHalf()
{

rimWidth = 5;
barWidth = 20;
barTop = .6;  // fraction of length for y position of top of bar

$fn=100;
handlebarRad = handlebarDiameter/2;
cutoutRad = (phoneWidth-barWidth)/2;

y1 = handlebarRad+rimWidth;
y2 = y1+bridgeHeight*tan(20);

crossBarTop = barTop*phoneLength-phoneLength/2;
crossBarBottom = crossBarTop-barWidth;
cutoutLength = phoneLength/2 - phoneCornerRad - cutoutRad + crossBarBottom;

difference()
{
// outer box
roundedRect(phoneWidth+2*rimWidth,phoneLength+2*rimWidth,phoneHeight+rimWidth,phoneCornerRad+rimWidth);

//inner box
translate([0,0,rimWidth/2])
roundedRect(phoneWidth,phoneLength,phoneHeight,phoneCornerRad);
// left coutout
translate([-phoneWidth/2,-(phoneLength/2-cutoutRad-phoneCornerRad),-(phoneHeight+rimWidth)/2])
difference()
{
    cylinder(r=cutoutRad,h=rimWidth+phoneHeight);
    translate([-cutoutRad,0,0])
    cube([2*cutoutRad,cutoutRad,rimWidth+phoneHeight]);
}
translate([-phoneWidth/2-rimWidth,crossBarBottom-cutoutLength,-phoneHeight])
cube([cutoutRad+rimWidth,cutoutLength,2*phoneHeight]);

// Cutout entire right half
translate([-phoneWidth-barWidth/2,-phoneLength/2+barTop*phoneLength,-phoneHeight])
cube([phoneWidth,phoneLength,2*phoneHeight]);
translate([phoneWidth/2,0,0])cube([phoneWidth,phoneLength*2,phoneHeight*2],center=true);
// Cut front to just over bar height
translate([0,phoneLength/2,rimWidth/2+2])cube([phoneWidth/2,2*rimWidth,phoneHeight],center=true);
// Cut back
translate([0,-rimWidth/2-phoneLength/2,0])
    cube([phoneWidth-2*phoneCornerRad,rimWidth,2*phoneHeight],center=true);
}

// Gripper
translate([-phoneWidth/2-rimWidth,crossBarBottom+barWidth,(phoneHeight+rimWidth)/2])
rotate([90,0,0])
linear_extrude(height=barWidth)
polygon([[0,0],[rimWidth,0],[3*rimWidth/2,rimWidth],[0,rimWidth]],convexity=1);

translate([-barWidth/2,0,-bridgeHeight-(rimWidth+phoneHeight)/2])
rotate([90,0,90])
difference()
{
    union()
    {
        linear_extrude(height=rimWidth)
        polygon([[-y1,0],[y1,0],[y2,bridgeHeight],[-y2,bridgeHeight]],1);
        cylinder(r=handlebarRad+rimWidth,h=barWidth/2);
    }
    cylinder(r=handlebarRad,h=barWidth/2);
    translate([-y1,-handlebarRad-rimWidth,0])cube([2*y1,handlebarRad+rimWidth,20]);
}
}

// This module creates the shape that needs to be subtracted from a cube to make its corners rounded.

module createMeniscus(height, radius){

rrhide = 0.001;

//This shape is basicly the difference between a quarter of cylinder and a cube

difference(){

translate([radius/2 + rrhide, radius/2 + rrhide]){

cube([radius + rrhide, radius + rrhide, height + rrhide], center=true);

}

cylinder(h = height, r = radius, $fn = 25, center=true);

}

}

// Now we just substract the shape we have created in the four corners

module roundedRect(xu, yu, zu, ru) {

rotation = 0;

difference(){

cube([xu, yu, zu], center=true);

translate([xu/2-ru, yu/2-ru]){

rotate(0){

createMeniscus(zu, ru);

}

}

translate([-xu/2+ru, yu/2-ru]){

rotate(90){

createMeniscus(zu, ru);

}

}

translate([-xu/2+ru, -yu/2+ru]){

rotate(180){

createMeniscus(zu, ru);

}

}

translate([xu/2-ru, -yu/2+ru]){

rotate(270){

createMeniscus(zu, ru);

}

}

}

}

module roundedRectX(x, y, z, r){

rotate([90,0,90]){

roundedRect(y, z, x, r);

}

}

module roundedRectY(x, y, z, r){

rotate([0,90,90]){

roundedRect(z, x, y, r);

}

}

module roundedRectZ(x, y, z, r){

roundedRect(x, y, z, r);

}
```


[User]
Hi there, I'm looking to create a parametric test object for 3D printer bridging capabilities, and I want to include a spiral pattern for the bridges. I've been playing around with a modified version of a design I found, but I need a fully parametric version that I can more easily tweak.

Here's the general idea:

**Overall Structure:** The object should have a central hub from which a series of bridges spiral outwards. Instead of a full spiral, I only want a half-round spiral, meaning the bridges will fan out in a 180-degree arc. These bridges will increase in length as they spiral outwards. The whole structure needs to be constructed from a set of stacked bridges that step up in Z.

**Parameters:**
*   **`min_bridge_length`**: The length of the shortest bridge. I'd like to set this from 10 to 180mm in increments of 5mm.
*   **`max_bridge_length`**: The length of the longest bridge, variable from 20 to 200mm, also in increments of 5.
*   **`number_of_bridges`**: The total number of bridges, from 4 to 20 in increments of 1.
*   **`hub_diameter`**: The diameter of the central hub, from 18 to 30mm, in steps of 1mm.
*   **`bridge_width`**: The width of each bridge, adjustable from 1 to 8mm, also in increments of 1.
*   **`wall_thickness`**: The thickness of the vertical walls supporting the bridges, from 1 to 10mm in 1mm steps.
*   **`bridge_height`**: The layer height of each bridge, from 0.1 to 2mm in steps of 0.1mm.
*   **`bridge_z_gap`**: The vertical distance between successive bridge layers, ranging from 0.2 to 1mm, incrementing by 0.1mm.
*  **`show_XY_test`**: A boolean variable that controls the presence of an additional bridge perpendicular to the longest bridge for testing XY cooling. Options are "yes" or "no".

**Specific Details:**

1.  The bridge lengths should increment linearly between `min_bridge_length` and `max_bridge_length` as they progress around the spiral arc, with the bridge at the start being `min_bridge_length` and the last bridge `max_bridge_length`

2. There will be a cylindrical central hub with a thickness of `wall_thickness`. The bridges extend outwards from the edge of the central hub.
3.  Each bridge should be a rectangular extrusion that's placed above the previous one at a height of `bridge_z_gap`. The walls connecting the bridges will form a smooth spiral.
4. At the end of the spiral there should be a long vertical wall segment at each end of the longest bridge.
5.  If `show_XY_test` is set to "yes", add an additional bridge that is the same length as the longest bridge and perpendicular to the last bridge. Also, at the end of this bridge, an additional supporting wall, similar to those added to the longest bridge, should also be present.

**OpenSCAD Notes:**

I don't think I need any specific libraries for this, just standard OpenSCAD primitives and operations.
I need all the parameters to be easily customizable. I want it to be completely parametric so I can change any of the values and regenerate the object.

Thanks a lot for your help!

[Assistant]
Customizable_BridgingSpiral.scad:
```
// OpenSCAD  test object for bridges
// Also tests for on-extruder fan cooling unevenness in X,Y axes.

// Parametrised to use as much space on a print bed as required.
// this design uses only half the spiral of the original.

// Customiser enabled

// preview[view:north east, tilt:top diagonal]

/*[Bridges]*/
// length of the shortest bridge
min_bridge_length = 10; //[10:5:180]

// length of the longest bridge
max_bridge_length = 100; //[20:5:200]

// Total number
number_of_bridges = 10; //[4:1:20]

// difference between subsequent bridges
bridge_increment = (max_bridge_length - min_bridge_length) / (number_of_bridges - 1) ; //5;

// Test the XY cooling efficiency
show_XY_test = "yes"; // [yes,no]

/*[Shape]*/
// diameter of the central hub
hub_diameter = 24; //[18:1:30]

// width of each bridge
bridge_width = 3; //[1:1:8]

// thickness of the vertical walls
wall_thickness = 1; //[1:10]

// layer height of each bridge
bridge_height = 0.8; //[0.1:0.1:2]

// height of each bridge above the previous bridge
bridge_z_gap = 0.2; //[0.2:0.1:1]

/* [Hidden] */
// angle between each bridge
bridge_angle = 180.0 / (number_of_bridges -1);

spiralResolution = 12*1;
spiralIncrement = 1/spiralResolution;
maxAngle = bridge_angle * (number_of_bridges-1);
eps = 0.1 * 1;
$fn = 90 * 1;

function spiralOffset(angle) = min_bridge_length + angle/maxAngle * (max_bridge_length - min_bridge_length);
function extensionAngle(length) = asin((bridge_width*0.5)/length);

module bridgeTest() {
	tubeHeight = number_of_bridges * (bridge_height + bridge_z_gap);
	heightStride = bridge_height + bridge_z_gap;

	difference() {
		union() {
			// Central hub
			cylinder(r = hub_diameter/2, h = tubeHeight);

			// The spokes (bridges)
			for (i=[0:number_of_bridges-1]) {
				length = spiralOffset(i*bridge_angle) + wall_thickness * 2;
				start_height = i * heightStride  + bridge_z_gap;
				hull() {
					rotate([0, 0, i*bridge_angle]) {
						translate([hub_diameter/2 - wall_thickness, 0, start_height]) {
							translate([0, -bridge_width/2, 0])
							cube([length , bridge_width, bridge_height], center=false);
							if (i < number_of_bridges-1) {
								translate([0, bridge_width/2 - eps, 0]) {
									cube([spiralOffset(i*bridge_angle + extensionAngle(length)) + wall_thickness, eps, bridge_height], center=false); // only an approximation
								}
							}
						}
					}
				}
				// Outer Spiral uprights
				if (i < number_of_bridges-1) {
					for (f=[0 : spiralResolution-1]) {
						frac  = f / spiralResolution;
						top	  = start_height + bridge_height;
						//end	= i == number_of_bridges-1 && f == spiralResolution-1;
						angle = i*bridge_angle + bridge_angle * f / spiralResolution;
						hull()  { // This hull sweeps through all the supports making a nice spiral
							rotate([0, 0, angle]) {
								translate([hub_diameter/2 + spiralOffset(angle), -bridge_width/2, 0]) {
									cube([wall_thickness, wall_thickness, top + heightStride * frac], center=false);
								}
								rotate([0, 0, bridge_angle * spiralIncrement])
								translate([hub_diameter/2 + spiralOffset(angle + bridge_angle * spiralIncrement),
										   -bridge_width/2, 0]) {
									cube([wall_thickness, wall_thickness, top + heightStride * (frac + spiralIncrement)], center=false);
								}
							}
						}
					}
				} else { // last upright of outer spiral
					rotate([0, 0, i*bridge_angle]) {
						translate([length + hub_diameter/2 - wall_thickness*2, -bridge_width/2, 0]) {
							cube([wall_thickness, bridge_width, start_height + heightStride-bridge_z_gap], center=false);
						}
					}
					if (show_XY_test == "yes") {
						// extra leg
						rotate([0, 0, i*bridge_angle -90]) {
							translate([length + hub_diameter/2 - wall_thickness*2, -bridge_width/2, 0]) {
								cube([wall_thickness, bridge_width, start_height + heightStride-bridge_z_gap], center=false);
							}
						}
						// extra bridge
						rotate([0, 0, i*bridge_angle - 90]) {
							translate([hub_diameter/2 - wall_thickness, 0, start_height]) {
								translate([0, -bridge_width/2, 0])
								cube([length , bridge_width, bridge_height], center=false);
							}
						}
					}
				}
			}
		}
		// core out the cylinder and remove the un-needed side
		cylinder(r=hub_diameter/2 - wall_thickness, h=tubeHeight*3, center=true);
		translate([-hub_diameter/2, -hub_diameter-bridge_width/2, -1]) {
			cube([hub_diameter, hub_diameter, tubeHeight+2], center=false);
		}
	}

}

bridgeTest();
```


[User]
Hey there, I'm looking to design a 3D printable part to help support the clothes rod in my closet, since it sags a bit in the middle. I'd like to design something that's basically a linking bracket that hangs from the closet shelf above and then supports the rod itself from below, to keep it from dipping. I've got some general dimensions in mind, but it would be great if the design could be parametric so I can adjust the sizes to fit different closet setups.

Here's what I'm thinking for the basic design:

There should be a top section that fits around the closet shelf. This should be a cylinder with an adjustable inner diameter and thickness, and a specified length.

Then, there's a bottom section that holds the clothes rod. This also should be a cylinder with an adjustable inner diameter, thickness, and length, similar to the top part.

Between those two sections is a "stem" to connect them. I think it should be formed by multiple cable-like structures running parallel to each other and then connected to the top and bottom sections with "interface" sections which are slightly thicker. I also would like to be able to adjust the stem length and cable diameter. The interface should gradually blend the multiple cables in the stem to the top and bottom cylinders. I'd also like to be able to set the number of cables (I'm thinking 5 to start).

For all the cylindrical parts, I’d like to be able to adjust the resolution using $fn. Also, I'd like the ability to add some weight-reducing holes to both the top and bottom sections, which are basically cylinders cutting through the cylindrical parts.

So, to recap the adjustable parameters:

*   **Rod Hanger (Bottom):**
    *   Inner Diameter
    *   Thickness
    *   Length
*   **Shelf Hanger (Top):**
    *   Inner Diameter
    *   Thickness
    *   Length
*   **Stem Assembly:**
    *   Cable Diameter
    *   Stem Length
    *   Interface Length
*   **Other:**
    *   `$fn` for all cylinders
	*    Number of cables in the stem (default to 5)
    *   Weight Reduction (boolean)

I think it would be easiest to define everything in inches and then convert to mm when needed, and use the MCAD library for its convenience functions. Could you create the OpenSCAD file(s) for this?

[Assistant]
hanger_v5.scad:
```

/*
 * Customizable Closet Clothes Linking Bracket v5
 *
 *
 * Works well when printed in Nylon 618 (or similar) for tensile
 * strength.
 *
 * Author: Jordan Husney <jordan.husney@gmail.com>
 * License: https://creativecommons.org/licenses/by-sa/4.0/
 *
 */

/* [Global] */

/* [Rod Hanger] */
// Rod Hanger Inner Diameter
rod_id = 1.5;                       // [0.25:0.125:2]
// Rod Hanger Thickness
rod_thick = 0.25;                   // [0.125:0.125:1]
// Rod Sleeve Length
rod_sleeve = 1.5;                   // [0.25:0.25:4]

/* [Shelf Hanger] */

// Shelf Hanger Inner Diameter
shelf_id = 1;                       // [0.25:0.125:2]
// Shelf Hanger Thickness
shelf_thick = 0.25;                 // [0.125:0.125:1]
// Shelf Sleeve Length
shelf_sleeve = 0.5;                 // [0.25:0.25:4]

/* [Stem Assembly] */

// Stem Cable Diameter
cable_diameter = 0.125;             // [0.0625:0.0625:0.5]
// Stem Length
stem_length = 2.75;                 // [0.5:0.25:12]
// Stem Interface Length
interface_length = 0.5;             // [0.25:0.25:2]

/* [Hidden] */

IN=25.4;

ROD_ID=rod_id*IN;
ROD_OD=ROD_ID+rod_thick*IN;
ROD_H=rod_sleeve*IN;

HANGER_ID=shelf_id*IN;
HANGER_OD=HANGER_ID+shelf_thick*IN;
HANGER_H=shelf_sleeve*IN;

STEM_L=stem_length*IN;
STEM_CABLE_D=cable_diameter*IN;
STEM_INTERFACE_L=interface_length*IN;
STEM_CABLE_L=STEM_L-2*STEM_INTERFACE_L;

WEIGHT_REDUCTION=0;

STEM_CABLE_XLATIONS = [
 		[ -1, 0, 0],
 		[ 0, 0, 0 ],
 		[ 1, 0, 0 ],
 		[ 0, -1, 0 ],
 		[ 0, 1, 0 ]
];

module stem_interface() {
    translate([0, -STEM_INTERFACE_L/8, 0])
    rotate([90, 0, 0])
    hull() {
        for (i = [0:1:4]) {
          // TODO: something isn't quite right here:
          translate(STEM_CABLE_D*STEM_CABLE_XLATIONS[i])
          cylinder(r=STEM_CABLE_D/2, h=STEM_INTERFACE_L/4, $fn=60, center=true);
        }
    }
}

union() {
    translate([0, STEM_INTERFACE_L, 0])
    union() {
        translate([0, STEM_CABLE_L, 0])
        union() {
            translate([0, HANGER_OD/2+STEM_INTERFACE_L-(HANGER_OD-HANGER_ID)/2, 0])
            difference()
            {
                cylinder(r=HANGER_OD/2, h=HANGER_H, center=true, $fn=60);

                scale([1, 1, 1.1])
                cylinder(r=HANGER_ID/2, h=HANGER_H, center=true, $fn=60);

                if (WEIGHT_REDUCTION) {
                    rotate([0, 0, 60])
                    translate([0, -HANGER_OD/2, 0])
                    rotate([90, 0, 0])
                    cylinder(r=HANGER_H/3, h=HANGER_OD, center=true, $fn=60);

                    rotate([0, 0, -60])
                    translate([0, -HANGER_OD/2, 0])
                    rotate([90, 0, 0])
                    cylinder(r=HANGER_H/3, h=HANGER_OD, center=true, $fn=60);
                }
            }

            translate([0, STEM_INTERFACE_L, 0])
            rotate([0, 0, 180])
            hull() {
                translate([0, STEM_INTERFACE_L, 0])
                stem_interface();

                translate([0, STEM_INTERFACE_L/4, 0])
                resize([sqrt(pow(HANGER_OD,2) - pow(HANGER_ID, 2)), STEM_INTERFACE_L/4, HANGER_H])
                stem_interface();
            }
        }

        translate([0, STEM_CABLE_L/2, 0])
        rotate([90,0,0])
        for (i = [0:1:4]) {
          translate(STEM_CABLE_D*STEM_CABLE_XLATIONS[i])
          cylinder(r=STEM_CABLE_D/2, h=STEM_CABLE_L, $fn=60, center=true);
        }
    }

    hull() {
        translate([0, STEM_INTERFACE_L, 0])
        stem_interface();

        translate([0, STEM_INTERFACE_L/4, 0])
        resize([sqrt(pow(ROD_OD,2) - pow(ROD_ID, 2)), STEM_INTERFACE_L/4, ROD_H])
        stem_interface();
    }

    translate([0, ROD_OD/-2+(ROD_OD-ROD_ID)/2, 0])
    difference()
    {
        cylinder(r=ROD_OD/2, h=ROD_H, center=true, $fn=60);

        scale([1, 1, 1.1])
        cylinder(r=ROD_ID/2, h=ROD_H, center=true, $fn=60);

        if (WEIGHT_REDUCTION) {
            rotate([0, 0, 120])
            translate([0, -ROD_OD/2, 0])
            rotate([90, 0, 0])
            cylinder(r=ROD_H/8, h=ROD_OD, center=true, $fn=60);

            rotate([0, 0, -120])
            translate([0, -ROD_OD/2, 0])
            rotate([90, 0, 0])
            cylinder(r=ROD_H/8, h=ROD_OD, center=true, $fn=60);
        }
    }
}

```


[User]
Hi there, I'm looking to get a custom case designed for my Cheerson CX10A mini quadcopter. I've seen some designs out there, but I'd like something that's fully parametric so I can tweak it for different props or if I get a slightly different sized quad down the road. I've got some of the basic dimensions I'm using in my current design, but I'd like you to start from scratch.

Essentially, it needs to be a box with rounded corners that snugly fits the quad, plus a little room for spare props and a USB charging cable. I think something with the quad's prop hubs resting on small posts would work well, like little pedestals. The top should be a separate lid that fits snugly but can be easily removed, maybe with a slight lip that keys into the main body.

Here's the basic layout I'm working with for the quad itself:
*   **Prop Diameter:** Around 34mm (I've included some extra clearance)
*   **Foot Diameter:** 9.4mm (This is the base of the landing gear)
*   **Prop Spacing:** 32mm between the centers of the props.
*   **Base Width:** 18.6mm (Rough width of the quad body)
*   **Base Height:** 4.5mm (Thickness of the main body)
*   **Quad Height:** 20mm (Total height from base to the top of the props)

For the case itself:
*   **Lid Height:** 3mm
*   **Wall Thickness:** I'd like to make this a parameter, but let's start with a wall thickness multiplier of 4.
*   **Nozzle Diameter:** I'm using a 0.4mm nozzle, so let's keep that as a parameter so the wall thickness will adjust correctly.
*   **Wiggle Room (Lid) X:** I'd like a bit of play in the lid. Let's start with 0.5mm on each side of the X dimension.
*   **Wiggle Room (Lid) Z:** I'd also like a little space vertically. Maybe start with 0.2mm.

I'd like all of the measurements listed here to be settable via variables. I'd also like you to use `$fn` for smoothing the circles, so that's also a variable. I'm not sure about which modules I'll need, but I can say that I would like the `lid()` and `box()` modules to be separate, and the lid to have a support that nests inside the box, as well as rests for the quad's feet.

I'd like you to use OpenSCAD and generate all the code for me, and make sure it prints without needing support. It would be great if the code was well-commented and easy to read. I'd like you to include the `hull()` function to create the rounded corners of the box.

[Assistant]
CX10Case.scad:
```
/* [Quad size] */
//Prop clearance
PropDia=34;//30+4
FootDia=9.4;//8.8+0.6
PropSpacing=32;
BaseWidth=18.6;//18+0.6
BaseHeight=4.5;
QuadHeight=20;
/* [Box size] */
LidHeight=3;
//Walls
w=4;
//Nozzle Dia
n=0.4;
//Wiggle Room (Lid) X
WiggleRoomX=0.5;
//Wiggle Room (Lid) Z
WiggleRoomZ=0.2;

//-----------
wt=w*n;//Wall Thickness
$fn=128;

box();
quadRests();
lidSupport();
rotate([180,0,0])translate([0,PropSpacing+PropDia+wt*6,-QuadHeight-wt-LidHeight])lid();
%lid();

module quadRests(){
    translate([-PropSpacing/2,-PropSpacing/2,wt])
    for (x=[0:1]){for (y=[0:1]){
    translate([x*PropSpacing,y*PropSpacing,0])
        difference(){
            cylinder(d=FootDia+wt*2,h=BaseHeight);
            cylinder(d=FootDia,h=BaseHeight+1);
        }
    }}
    difference(){
        translate([-(BaseWidth+wt*2)/2,-(BaseWidth+wt*2)/2,wt])
        cube([BaseWidth+wt*2,BaseWidth+wt*2,BaseHeight]);
        translate([-BaseWidth/2,-BaseWidth/2,wt])
        cube([BaseWidth,BaseWidth,BaseHeight+1]);
    }
    /*for (s=[0:4]){
        rotate([0,0,45+90*s])translate([13,-wt/2,wt])cube([5,wt,BaseHeight]);
    }*/
}

module box(){
    difference(){
        //Outer
    hull()translate([-PropSpacing/2,-PropSpacing/2,0]){
    for (x=[0:1]){for (y=[0:1]){
    translate([x*PropSpacing,y*PropSpacing,0])
        cylinder(d=PropDia+wt*2,h=QuadHeight+wt);
    }}}
        //Inner
    hull()translate([-PropSpacing/2,-PropSpacing/2,wt]){
    for (x=[0:1]){for (y=[0:1]){
    translate([x*PropSpacing,y*PropSpacing,0])
        cylinder(d=PropDia,h=QuadHeight+wt);
    }}}

    }
}

module lid(wr=WiggleRoomX){
    outerD=PropDia+(wt*6);
    lipHeight=wt*2;
    totalLidHeight=lipHeight-WiggleRoomZ+LidHeight;

    difference(){
        //top
    hull()translate([-PropSpacing/2,-PropSpacing/2,QuadHeight+wt]){
    for (x=[0:1]){for (y=[0:1]){
    translate([x*PropSpacing,y*PropSpacing,-lipHeight+WiggleRoomZ])
        cylinder(d=outerD,h=totalLidHeight);
    }}}
        //support
        lidSupport(hollow=false,wr=wr);
        translate([PropSpacing,0,0])lidSupport(hollow=false,wr=wr);
    }
}

module lidSupport(hollow=true,wr=0){
    bevelD=PropDia+(wt*4)+wr;
    difference(){
    //Lip
    hull()translate([-PropSpacing/2,-PropSpacing/2,QuadHeight-wt]){
    for (x=[0:1]){for (y=[0:1]){
    translate([x*PropSpacing,y*PropSpacing,0]){
        cylinder(d1=PropDia+wt*2+wr,d2=bevelD,h=wt);
        translate([0,0,wt])cylinder(d1=bevelD,d2=PropDia+wt*2+wr,h=wt);}
    }}}
    //Inner
    if (hollow){
    hull()translate([-PropSpacing/2,-PropSpacing/2,QuadHeight-wt-1]){
    for (x=[0:1]){for (y=[0:1]){
    translate([x*PropSpacing,y*PropSpacing,0])
        cylinder(d=PropDia,h=(wt*2)+2);
    }}}
    }
    }
}
```


[User]
I'd like to have a customizable box designed in OpenSCAD. It needs to have a base, a lid, and a latching mechanism, and I'd like to be able to control the internal dimensions of the box (length, width, and height) using parameters. It'd also be good to control the number of latches on the lid, and have a way to adjust whether or not the lid is open or closed, as well as a setting to lock or unlock the latches.

I'd like the design to be parametric, so I can easily change the size and number of latches. Please make sure all units are in millimeters.

Specifically:

*   **Inner Dimensions:** I need parameters for `length`, `width`, and `height` to control the box's inner dimensions.

*   **Latches:** I'd like a `latches` parameter that lets me specify how many latches the box has, evenly spaced along the length.

*   **Display Options:**
    *   `print`: A boolean toggle to affect the display of the latches (i.e., if `print` is set to `0`, it should hide the moving latch parts.)
    *   `close`: An angle (0-90) that controls how much the lid is open. 0 would mean fully closed and 90 would mean fully open.
    *   `lock`: A boolean toggle for if the latches are locked (1) or not locked (0).

The lid should be hinged and able to open, with the angle controlled by the `close` parameter. The latch should be a simple hook design. The hinge should be a cylindrical type hinge with a matching pin. I don't need it to be super fancy.

I'm not using any custom libraries at the moment, so please make sure it doesn't require anything outside standard OpenSCAD functionalities.

[Assistant]
Boxparametric.scad:
```
//parametric box by Lars Baronat 2015 Creative Commons - Attribution - Non-Commercial

/* [Basic Settings] */
//inner dimensions in mm
length=50;
width=50;
height=50;

//number of latches
latches=1;

/* [Display Options] */

print=1;    //[0:Off,1:On]

close=0;    //[0:90]
lock=0;     //[0:unlocked,1:locked]

//===========================================================================
//code for the box

//base
difference(){
	translate([0,0,height/4-1.25])cube([length,width+6,height/2+2.5],center=true);
	translate([0,0,height/4+0.5])cube([length+1,width,height/2+1],center=true);
	translate([length/2,(width+6)/2,height/4+2.5])cube([3,3,height/2],center=true);
	translate([length/2,-(width+6)/2,height/4+2.5])cube([3,3,height/2],center=true);
	translate([-length/2,(width+6)/2,height/4+2.5])cube([3,3,height/2],center=true);
	translate([-length/2,-(width+6)/2,height/4+2.5])cube([3,3,height/2],center=true);
    translate([0,(width+6)/2,height/2])cube([length,3,3],center=true);
    translate([0,-(width+6)/2,height/2])cube([length,3,3],center=true);
    translate([length/2,0,-1.5])cube([0.5,width,4],center=true);
    translate([-length/2,0,-1.5])cube([0.5,width,4],center=true);
}

//hinge(base part)
for(x=[0,180]){
	rotate(x)for(y=[-width/2-1.5,width/2+1.5]){
		difference(){
			union(){
				translate([length/2+1.25,y,0])cube([2.5,3,5],center=true);
				translate([length/2+2.5,y,0])rotate(90,[1,0,0])cylinder(h=3,r=2.5,center=true,$fn=50);
			}
			translate([length/2+2.5,y,0])rotate(90,[1,0,0])cylinder(h=38,r=1.75,center=true,$fn=50);
		}
	}
}

//lid
for(side=[0,180])rotate(side)translate([length/2+2.5,0,0])rotate(90-(close-close*print),[0,1,0]){
	rotate(90,[1,0,0])cylinder(h=width+6,r=1.25,center=true,$fn=50);
	difference(){
		union(){
			translate([0,0,height/2])cube([5,width+6,height],center=true);
			rotate(90,[1,0,0])cylinder(h=width+6,r=2.5,center=true,$fn=50);
		}
		for(y=[-width/2-1.5,width/2+1.5]){
			translate([0,y,0])rotate(90,[1,0,0])cylinder(h=3.5,r=3,center=true,$fn=50);
			translate([-1.5,y,1.5])cube([3,3.5,3],center=true);
		}
	}
	for(y=[-width/2-1.5,width/2+1.5]){
		difference(){
			translate([-length/4-2.5,y,3*height/4])cube([length/2,3,height/2],center=true);
			translate([-length/2-2.5,y+1.5,3*height/4])cube([1.5,3,height/2+1],center=true);
		}
		translate([-length/2-2.5,y-0.75,3*height/4])cube([1.5,1.5,height/2],center=true);
	}

	translate([-(length/2-0.75)/2-2.5,width/2+2.25,height/2])cube([length/2-0.75,1.5,3],center=true);
    translate([-(length/2+0.75)/2-2.5,-width/2-2.25,height/2])cube([length/2+0.75,1.5,3],center=true);
	translate([-3.25,width/2+2.25,height/4+3])cube([1.5,1.5,height/2],center=true);
	translate([-3.25,-width/2-2.25,height/4+3])cube([1.5,1.5,height/2],center=true);

	translate([-(length/2+5)/2+2.5,0,height+1.25])cube([length/2+5,width+6,2.5],center=true);

	if(side==0){    //hook
        for(k=[1:latches]){
            translate([-length/2+7.5,-width/2+k*width/(latches+1),height+2.5])difference(){
                rotate(90,[1,0,0])cylinder(h=9.5,r=6,center=true,$fn=50);
                translate([0,0,2.5])rotate(90,[1,0,0])cylinder(h=10,r=2.5,center=true,$fn=50);
                translate([3.5,0,0])cube([7,10,13],center=true);
                translate([0,0,-3.5])cube([12,10,6],center=true);
            }
        }
	}
	else{           //hinge for latch
        for(k=[1:latches]){
            translate([-length/2+2.5,-width/2+k*width/(latches+1),height+5]){
                difference(){
                    union(){
                        rotate(90,[1,0,0])cylinder(h=5,r=2.5,center=true,$fn=50);
                        translate([0,0,-2.5])cube([5,5,5],center=true);
                    }
                    rotate(90,[1,0,0])cylinder(h=6,r=1.25,center=true,$fn=50);
                }
                if(print==0){
                    latch(lock,0);
                }
            }
        }
	}
}

module latch(lock,seperate){
    rotate(lock*90,[0,1,0]){
        translate([-2.5,2.75,0])cube([5,2,20]);
        translate([0,4.75,0])rotate(90,[1,0,0])cylinder(h=2,r=2.5,$fn=50);
        translate([-2.5,-4.75,0])cube([5,2,20]);
        translate([0,-2.75,0])rotate(90,[1,0,0])cylinder(h=2,r=2.5,$fn=50);
        translate([0,0,20])rotate(90,[1,0,0])cylinder(h=9.5,r=2.5,center=true,$fn=50);
        translate([0,2.75,0])rotate(90,[1,0,0])cylinder(h=2,r=1,$fn=50);
        translate([0,-0.75,0])rotate(90,[1,0,0])cylinder(h=2,r=1,$fn=50);
        translate([0,7,5])rotate(90,[1,0,0])cylinder(h=2.25,r=1,$fn=50);
        translate([0,-4.75,5])rotate(90,[1,0,0])cylinder(h=2.25,r=1,$fn=50);

        translate([0,0,5-seperate*10])rotate(-lock*90,[0,1,0]){
            difference(){
                union(){
                    translate([-20,5,-2.5])cube([20,2,5]);
                    translate([0,7,0])rotate(90,[1,0,0])cylinder(h=2,r=2.5,$fn=50);
                    translate([-20,-7,-2.5])cube([20,2,5]);
                    translate([0,-5,0])rotate(90,[1,0,0])cylinder(h=2,r=2.5,$fn=50);
                    translate([-20,0,0])rotate(90,[1,0,0])cylinder(h=14,r=2.5,center=true,$fn=50);
                }
                rotate(90,[1,0,0])cylinder(h=40,r=1.25,center=true,$fn=50);
            }

        }
    }
}
```


[User]
I'm looking to create a temperature calibration tower for my 3D printer, and I really like the designs I've seen in `tour_de_chauffe_v3.scad` and `tour_de_chauffe_v2a.scad`.  I want to combine some of the features and make it more customizable.

Here's what I'm thinking:

**Overall Structure:**

*   It should be a tower made of stacked blocks.
*   The number of blocks should be a parameter.
*   Each block should represent a different temperature, and the temperature values should be displayed as labels on the block.
*   I'd like to be able to reverse the order of the labels if needed.
*   There should be a base at the bottom for stability, which should be a parameter (size and height).

**Block Design:**

*   Each block should have a basic cube shape with parametric sizing.
*   I need to be able to set the wall thickness.
*   Each block needs to have a few features:
    *   A central cutout (parameterizable size)
    *   A temperature label on the front face (using customizable fonts)
    *   A circular hole on one side (parameterizable diameter)
    *   An arch/slant feature on one side (similar to the existing designs)
    *   A triangle cutout on another side
*   I need to be able to control the thickness of the top of the triangle cutout.

**Font Customization:**

*   I want to be able to choose from different fonts (like Arial Bold, Liberation Sans, Open Sans, Montserrat, Dosis, and Orbitron).
*   I need to be able to adjust the depth of the font.

**General Parameters:**

*   All dimensions and sizes should be parameters.
*   I would like a parameter to control the `$fn` variable to increase detail for curves, if desired, using the `getCustomFn` helper function pattern.
*   I'm aware I will need to modify my slicer G-code to change the temperature at each level.

**Specific Features To add from V3:**

*   I like the idea of a separate base, so the base should be a parameter and not just added to the size of the lowest cube, as in V2.

Essentially, I'd like an OpenSCAD file (or multiple files if it makes sense) that allows me to generate a calibration tower with all of these options.  I'm not sure about what libraries you might find helpful - but please use them if it makes things easier. I expect there will be some custom modules as well.

[Assistant]
tour_de_chauffe_v3.scad:
```
//NOT TESTED for non cubes... should work for variable height (X=Y), but no guarantee.

// You must modify the gcode you generate! (Cura has TweakAtX plugin, not sure about other slicers)
DoYouUnderstandYouMustModifyTheGCodeThisMakes=123; // [123:What?, 7:Yes I understand]

NumberOfBlocks = 7;
Labels=["206","208","210","212","214","216","218"];
ReverseLabelOrder=0; //[0:False, 1:True]
Reverse=ReverseLabelOrder == 1;

// NOT guaranteed to work for all combinations (simply have not tested it)
BlockSize=[10,10,10];
WallThickness=1.2;
TopOfTriangleThickness=2;

TowerBase=[30,30,3];

FontFace=2;// [0:Arial Bold,1:Liberation Sans,2:Open Sans,3:Montserrat,4:Dosis,5:Orbitron]
FontFacesInCustomizer=["Arial:style=Bold","Liberation Sans",
    "Open Sans:style=Bold",
    "Montserrat:style=Bold","Dosis:style=Bold","Orbitron:style=Bold"]; //TODO add more
Font=FontFacesInCustomizer[FontFace];

FontDepth=0.5;

//Increase the default facet number to produce smoother curves
fn_override = 0; //[0:Default, 24:Better (24), 50:High quality (50), 100:Super HQ (100)]
$fn = fn_override;

/* [Hidden] */

TWEAK=0.01;
TWEAK2=TWEAK*2;

//My disclamer strategy...
if (DoYouUnderstandYouMustModifyTheGCodeThisMakes == 7)
difference()
{
    union()
    {
        for (i = [1:NumberOfBlocks])
        {
            translate([0,0,BlockSize[2] * (i-1)])
                block(Reverse ? Labels[NumberOfBlocks-i-1] : Labels[i-1], BlockSize);
            echo(Labels[i-1]);
        }

        //Base
        towerXOffset = (TowerBase[0]-BlockSize[0])/2;
        towerYOffset = (TowerBase[1]-BlockSize[1])/2;
        translate([-towerXOffset,-towerYOffset,-TowerBase[2]+TWEAK])
            cube(TowerBase);
    }

    for (i = [1:NumberOfBlocks-1])
    {
        translate([-TWEAK,0,i*BlockSize[2]]) rotate([0,90,0])
            cylinder(d=1,h=BlockSize[0]+TWEAK2, $fn=getCustomFn(30));
    }
}
else
{
    rotate([90,-90,0])
    linear_extrude(2)
        union(){
        text("Learn/confirm you know how to modify", 10);
            translate([0,-12,0])
        text("the temp when generating the gcode", 10);}
}

$fn_override = $fn;
function getCustomFn(base) = max(base, $fn_override);

module block(label, size)
{
    blockHeight=BlockSize[2];
    fontSizePct=0.4;
    fontXOffsetPct=0.05;
    fontZOffsetPct=0.3;

    difference()
    {
        union()
        {
            cube(size);
        }

        union()
        {
            centerXYOffset = WallThickness;
            centerCutX=size[0]-WallThickness*2;
            centerCutY=size[1]-WallThickness*2;
            centerCutZ=size[2]-TopOfTriangleThickness; //Not used for center cut...

            //Center cutout
            translate([centerXYOffset, centerXYOffset, -TWEAK])
                cube([centerCutX, centerCutY, size[2]+TWEAK2]);
            //Temp label
            translate([blockHeight * fontXOffsetPct, FontDepth, blockHeight * fontZOffsetPct]) rotate([90,0,0])
                linear_extrude(FontDepth+TWEAK)
                //text(label, font=Font, size=blockHeight * fontSizePct);
                text(str(label), blockHeight * fontSizePct, Font);
                //text(label, size=font_size, halign="center");

            //Side 1: Hole
            //  Same size as centerCutoutPct
            translate([size[0]-(centerXYOffset)-TWEAK, size[1]*.5, size[2]*.5]) rotate([0,90,0])
                cylinder(d=min(centerCutX, centerCutY), h=size[0], $fn=getCustomFn(24));

            //Side2: Arch/Slant
            radius=9;
            paddingSize=(size[0]-radius)/2;
            translate([radius+paddingSize,size[1] - (centerXYOffset)-TWEAK,paddingSize])
                rotate([90,0,180])
                archslantThing(radius, size[1]);

            //Size 3: Triangle
            //  Same size as centerCutoutPct
            //  NOT equilateral, h=6, w=6
            translate([(centerXYOffset)+TWEAK,size[1]/2,size[2]-(size[2]-centerCutZ)/2]) rotate([-90,0,90])
                triangle(centerCutZ, centerCutX, size[0]);
        }
    }
}

module triangle(height, base, depth)
{
    linear_extrude(height)
        //polygon(points=[[-base/2,-height/2],[base/2,-height/2],[0,height/2]]);
        polygon(points=[[-base/2,0],[base/2,0],[0,height]]);
}
module archslantThing(radius, height)
{
    //bottom flat=4.5
    //28 degree slope toward top
    yOffset=-0.5;
    difference()
    {
        translate([0,yOffset,0])
        cylinder(r=radius, h=height);

        translate([-radius,-radius+yOffset,-TWEAK])
            cube([radius*2+TWEAK2,radius-yOffset,height+TWEAK2]);
        translate([radius/2,0,0])
        rotate([0,0,28])
        translate([-radius*1.5,0,-TWEAK])
            cube([radius*1.5,radius*1.5,height+TWEAK2]);
    }
}
```
tour_de_chauffe_v2a.scad:
```
//NOT TESTED for non cubes... should work for variable height (X=Y), but no guarantee.

// You must modify the gcode you generate! (Cura has TweakAtX plugin, not sure about other slicers)
DoYouUnderstandYouMustModifyTheGCodeThisMakes=123; // [123:What?, 7:Yes I understand]

NumberOfBlocks = 7;
Labels=["206","208","210","212","214","216","218"];
ReverseLabelOrder=0; //[0:False, 1:True]
Reverse=ReverseLabelOrder == 1;

// NOT guaranteed to work for all combinations (simply have not tested it)
BlockSize=[10,10,10];

FontFace=2;// [0:Arial Bold,1:Liberation Sans,2:Open Sans,3:Montserrat,4:Dosis,5:Orbitron]
FontFacesInCustomizer=["Arial:style=Bold","Liberation Sans",
    "Open Sans:style=Bold",
    "Montserrat:style=Bold","Dosis:style=Bold","Orbitron:style=Bold"]; //TODO add more
Font=FontFacesInCustomizer[FontFace];

FontDepth=0.5;

//Increase the default facet number to produce smoother curves
fn_override = 0; //[0:Default, 24:Better (24), 50:High quality (50), 100:Super HQ (100)]
$fn = fn_override;

/* [Hidden] */

TWEAK=0.01;
TWEAK2=TWEAK*2;

//My disclamer strategy...
if (DoYouUnderstandYouMustModifyTheGCodeThisMakes == 7)
difference()
{
    for (i = [1:NumberOfBlocks])
    {
        translate([0,0,BlockSize[2] * (i-1)])
            block(Reverse ? Labels[NumberOfBlocks-i] : Labels[i-1], BlockSize);
        echo(Labels[i-1]);
    }

    for (i = [1:NumberOfBlocks-1])
    {
        translate([-TWEAK,0,i*BlockSize[2]]) rotate([0,90,0])
            cylinder(d=1,h=BlockSize[0]+TWEAK2, $fn=getCustomFn(30));
    }
}
else
{
    rotate([90,-90,0])
    linear_extrude(2)
        union(){
        text("Learn/confirm you know how to modify", 10);
            translate([0,-12,0])
        text("the temp when generating the gcode", 10);}
}

$fn_override = $fn;
function getCustomFn(base) = max(base, $fn_override);

module block(label, size)
{
    blockHeight=BlockSize[2];
    fontSizePct=0.4;
    fontXOffsetPct=0.05;
    fontZOffsetPct=0.3;

    centerCutoutPct=0.6;

    difference()
    {
        union()
        {
            cube(size);
        }

        union()
        {
            centerXYOffset = (1-centerCutoutPct) / 2;
            centerCutX=size[0]*centerCutoutPct;
            centerCutY=size[1]*centerCutoutPct;
            centerCutZ=size[2]*centerCutoutPct; //Not used for center cut...

            //Center cutout
            translate([size[0]*centerXYOffset, size[1]*centerXYOffset, -TWEAK])
                cube([centerCutX, centerCutY, size[2]+TWEAK2]);
            //Temp label
            translate([blockHeight * fontXOffsetPct, FontDepth, blockHeight * fontZOffsetPct]) rotate([90,0,0])
                linear_extrude(FontDepth+TWEAK)
                //text(label, font=Font, size=blockHeight * fontSizePct);
                text(str(label), blockHeight * fontSizePct, Font);
                //text(label, size=font_size, halign="center");

            //Side 1: Hole
            //  Same size as centerCutoutPct
            translate([size[0]-(size[0]*centerXYOffset)-TWEAK, size[1]*.5, size[2]*.5]) rotate([0,90,0])
                cylinder(d=min(centerCutX, centerCutY), h=size[0], $fn=getCustomFn(24));

            //Side2: Arch/Slant
            radius=9;
            paddingSize=(size[0]-radius)/2;
            translate([radius+paddingSize,size[1] - (size[1]*centerXYOffset)-TWEAK,paddingSize])
                rotate([90,0,180])
                archslantThing(radius, size[1]);

            //Size 3: Triangle
            //  Same size as centerCutoutPct
            //  NOT equilateral, h=6, w=6
            translate([(size[0]*centerXYOffset)+TWEAK,size[1]/2,size[2]-(size[2]-centerCutZ)/2]) rotate([-90,0,90])
                triangle(centerCutZ, centerCutX, size[0]);
        }
    }
}

module triangle(height, base, depth)
{
    linear_extrude(height)
        //polygon(points=[[-base/2,-height/2],[base/2,-height/2],[0,height/2]]);
        polygon(points=[[-base/2,0],[base/2,0],[0,height]]);
}
module archslantThing(radius, height)
{
    //bottom flat=4.5
    //28 degree slope toward top
    yOffset=-0.5;
    difference()
    {
        translate([0,yOffset,0])
        cylinder(r=radius, h=height);

        translate([-radius,-radius+yOffset,-TWEAK])
            cube([radius*2+TWEAK2,radius-yOffset,height+TWEAK2]);
        translate([radius/2,0,0])
        rotate([0,0,28])
        translate([-radius*1.5,0,-TWEAK])
            cube([radius*1.5,radius*1.5,height+TWEAK2]);
    }
}
```


[User]
I'd like a parametric drag chain generator in OpenSCAD, please. I want to be able to control the internal height and width of the chain, as well as the thickness of the material. It should be designed to be printed as a single assembled piece if possible, and the number of links should be a parameter as well. It would be great if there were options to add a small anti-stick gap, extend the links lengthwise, and to chamfer the pins to ease assembly. Finally, I want the ability to generate male and female mount points on the ends of the chain, or to print the chain only.

Could you also ensure it uses named modules and reasonable variable names? I'm trying to keep things readable.

I'm not sure if it's helpful, but I've seen a few designs that use the hull function for the rounded ends of the links, which seems efficient. And ideally I'd like the mounting holes to be an option, and placed to work with M3 screws if they are included.

I think that's everything for the basic functionality, but please let me know if you have any questions. I don't need a specific library beyond the built-in OpenSCAD ones.

[Assistant]
DragChain.scad:
```
// Drag chain, best printed together as much as possible
// Common sizes 8x8, 10x10, 10x20
// Copyright (c) R. Linder
// This work is licensed under a Creative Commons Attribution 4.0 International License.

// Internal dimension (min 5)
link_height=8;
// Internal dimension (min 5)
link_width=10;
// Thickness of part body (Typical 1.8 min 1.4)
shell_thickness=1.8;
// Print as many as your bed can fit
number_of_links = 5;
// Add anti stick if your chain is fused solid.
anti_stick = 0.15;		// [0.0:0.01:0.35]
// Select what to print
part_selection = 3;		// [0:Chain only, 1:Male mount, 2:Female mount, 3:Chain with mounts]

linkHeight = link_height + 2 * shell_thickness;
linkWidth = link_width + 4 * shell_thickness;

linkLength = link_height * 2.8;
pinRad = linkHeight / 6;
pinSpace = ((linkLength / 2) - (linkHeight / 2)) * 2;
tabLen = linkHeight / 2;

$fn=140;

module femaleMount ()
{
	difference ()
	{
		union ()
		{
			difference ()
			{
				translate ([linkHeight/2, 0, 0]) hull ()
				{
					translate ([0, pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
					translate ([-linkHeight/2,-pinSpace/6, 0]) cube ([linkHeight, 1, linkWidth+0.6]);
					translate ([0,-pinSpace*1.5, 0]) cube ([linkHeight/2, 5, linkWidth+0.6]);
				}
				femaleEnd (mount=1);
			}
		}
		translate ([-shell_thickness,-pinSpace*1.5-0.6, shell_thickness*2]) cube (size=[linkHeight, pinSpace*2.5, linkWidth- (shell_thickness*4)+0.6]);

		// M3 Holes
		if (link_width >= 10)
		{
			for (off = [0:2])
			{
				rotate ([0, 90, 0]) translate ([-linkWidth/2-off- (link_width) /6+0.6,-linkLength/2.5- (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);

				rotate ([0, 90, 0]) translate ([-linkWidth/2+off+ (link_width) /6-1.2,-linkLength/2.5+ (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);

			}
		}
		else
		{
			rotate ([0, 90, 0]) translate ([-linkWidth/2-0.3,-linkLength/2.5, link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);
		}

	}
}
module maleMount ()
{
	difference ()
	{
		union ()
		{
			difference ()
			{
				union ()
				{
					translate ([linkHeight/2, 0, 0]) hull ()
					{
						translate ([0, pinSpace, 0]) cube ([linkHeight/2, 5, linkWidth+0.6]);
						translate ([0, -pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
						translate ([-linkHeight/2,-pinSpace/6, 0]) cube ([linkHeight, 1, linkWidth+0.6]);
					}
					translate ([0,- (linkLength+tabLen) /2, shell_thickness+0.3]) cube (size=[(linkHeight+shell_thickness) /2, tabLen*2, linkWidth-shell_thickness*2+0.2]);
				}
				maleEnd ();
			}
			// Locking pins (0.2mm narrower)
			translate ([linkHeight/2, (-pinSpace/2)-1.5, 0.4]) cylinder (h=linkWidth-0.2, r=pinRad-0.25);

		}
		// Top Champher on stopper
		translate ([linkHeight/2+1,-linkLength/2-sqrt (tabLen*tabLen*2), 0]) rotate ([0, 0, 45]) cube ([tabLen, tabLen, linkWidth]);
		// Front Champher on stopper
		translate ([linkHeight/2-0.6,-linkLength/2-tabLen+0.6, 0]) rotate ([0, 0, 70]) cube (size=[2.5, 5, linkWidth+5]);
		// Hole for wires
		translate ([0-shell_thickness,-pinSpace*1.5, shell_thickness*2]) cube (size=[linkHeight, pinSpace*3.5, linkWidth- (shell_thickness*4)+0.6]);

		if (link_width >= 10)
		{
			for (off = [0:2])
			{
				rotate ([0, 90, 0]) translate ([-linkWidth/2-off- (link_width) /6+0.6, linkLength/3+ (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);

				rotate ([0, 90, 0]) translate ([-linkWidth/2+off+ (link_width) /6-1.2, linkLength/3- (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);
			}
		}
		else
		{
			rotate ([0, 90, 0]) translate ([-linkWidth/2-0.3, linkLength/2, link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);
		}
	}
}

module link ()
{
	difference ()
	{
		union ()
		{
			difference ()
			{
				union ()
				{
					translate ([linkHeight/2, 0, 0]) hull ()
					{
						translate ([0, pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
						translate ([0,-pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
					}
					translate ([0,- (linkLength+tabLen) /2, shell_thickness+anti_stick/2]) cube (size=[(linkHeight+shell_thickness) /2, tabLen*2, linkWidth-shell_thickness*2+0.2]);
				}

				maleEnd ();
				femaleEnd ();
			}

			// Locking pins (0.2mm narrower)
			translate ([linkHeight/2, (-pinSpace/2)-1.5, 0.4]) cylinder (h=linkWidth-0.2, r=pinRad-0.25);
		}
		// Top Champher on stopper
		translate ([linkHeight/2+1,-linkLength/2-sqrt (tabLen*tabLen*2), 0]) rotate ([0, 0, 45]) cube ([tabLen, tabLen, linkWidth]);

		// Front Champher on stopper
		translate ([linkHeight/2-0.6,-linkLength/2-tabLen+0.6, 0]) rotate ([0, 0, 70]) cube (size=[2.5, 5, linkWidth+5]);

		// Hole for cables
		translate ([0+shell_thickness+0.2,-linkLength/2-tabLen, shell_thickness*2]) cube (size=[linkHeight-shell_thickness*2, linkLength+tabLen+1, linkWidth- (shell_thickness*4)+0.6]);

		// trim top brace
		translate ([0,-pinSpace-pinRad*2, shell_thickness*2]) cube (size=[linkHeight+3, linkLength/2, linkWidth- (shell_thickness*4)+0.6]);
	}
}

// The Male end with cutouts

module maleEnd ()
{
	//Cutouts for opposite side
	translate ([-0.6,-linkLength/2, (linkWidth+0.4)-shell_thickness])  cube (size=[linkHeight+1, pinRad*6-shell_thickness, shell_thickness+0.7]);

	translate ([-0.6,-linkLength/2, -0.6]) cube (size=[linkHeight+1, pinRad*6-shell_thickness, shell_thickness+0.8]);
	// Cut out on rounds to fit on base
	translate ([-0.6, (- (linkLength+tabLen) /2)-0.6, 0]) cube (size=[shell_thickness+1.2+anti_stick, pinRad*2.5+tabLen+0.6, linkWidth+0.6]);

}

module femaleEnd (mount=0)
{
	translate ([linkHeight/2, (pinSpace/2)+1.5,-1]) cylinder (h=linkWidth+3, r=pinRad);
	// Bottom brace
	translate ([-0.6, pinRad*2.5, shell_thickness-anti_stick]) cube (size=[linkHeight+2, pinRad*3+1.2, linkWidth- (shell_thickness*2)+0.6+ (2*anti_stick)]);
	// Top brace
	translate ([linkHeight-shell_thickness-0.6, pinRad*2, shell_thickness-anti_stick]) cube (size=[shell_thickness*2, pinRad*4, linkWidth- (shell_thickness*2)+0.6+anti_stick*2]);
	if (mount)
	{
		translate ([0, 0, shell_thickness]) cube (size=[linkHeight- (shell_thickness), (linkLength/2), linkWidth- (shell_thickness*2)+0.6]);
	}
	else
	{
		translate ([shell_thickness+0.2, -0.6, shell_thickness-anti_stick]) cube (size=[linkHeight- (shell_thickness*2), (linkLength/2)+0.6, linkWidth- (shell_thickness*2)+0.6+ (2*anti_stick)]);
	}
}

rotate ([0, 90, 0])
{
	if (part_selection == 1)
	{
		maleMount ();
	}
	else if (part_selection == 2)
	{
		femaleMount ();
	}
	else if (part_selection <= 3)
	{
		if (part_selection == 3)
		{
			femaleMount ();
			translate ([0, (number_of_links+1) *(pinSpace+3), 0]) maleMount ();
		}

		for (cnt = [0:number_of_links-1])
		{
			translate ([0, (cnt+1) *(pinSpace+3), 0]) link ();
		}
	}
	else
	{
		echo ("Invalid Part Selection");
	}
}

```
DragChain.scad:
```
// Drag chain, best printed together as much as possible
// Common sizes 8x8, 10x10, 10x20
//
// If your chain is fused it is usualy cause by extruder calibration errors
// The hole in your nozzle wears out
// Copyright (c) R. Linder
// This work is licensed under a Creative Commons Attribution 4.0 International License.

// Internal dimension (min 5)
link_height=8;
// Internal dimension (min 5)
link_width=10;
// Thickness of part body (Typical 1.8 min 1.4)
shell_thickness=1.8;
// Extend the links by a number of mm (Eg. 10)
link_xtend = 5;
// Pin release, enables a champher on the pins to help insertion [0:off, 1:On]
pin_release = 1;	// [0:off, 1:On]
// Print as many as your bed can fit
number_of_links = 9;
// Add anti stick if your chain is fused solid, first try reduce feedrate Eg. 85%
anti_stick = 0.15;		// [0.0:0.01:0.35]
// Select what to print
part_selection = 3;		// [0:Chain only, 1:Male mount, 2:Female mount, 3:Chain with mounts]

linkHeight = link_height + 2 * shell_thickness;
linkWidth = link_width + 4 * shell_thickness;

linkLength = link_height * 2.8;
pinRad = linkHeight / 6;
pinSpace = ((linkLength / 2) - (linkHeight / 2)) * 2;
tabLen = linkHeight / 2;

$fn=140;

module femaleMount (part_only=0)
{
	difference ()
	{
		union ()
		{
			difference ()
			{
				translate ([linkHeight/2, 0, 0]) hull ()
				{
					translate ([0, pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
					translate ([-linkHeight/2,-pinSpace/6, 0]) cube ([linkHeight, 1, linkWidth+0.6]);
					translate ([0,-pinSpace*1.5, 0]) cube ([linkHeight/2, 5, linkWidth+0.6]);
				}
				femaleEnd (1,part_only);
			}
		}
		translate ([-shell_thickness,-pinSpace*1.5-0.6, shell_thickness*2]) cube (size=[linkHeight, pinSpace*2.5, linkWidth- (shell_thickness*4)+0.6]);

		// M3 Holes
		if (link_width >= 10)
		{
			for (off = [0:2])
			{
				rotate ([0, 90, 0]) translate ([-linkWidth/2-off- (link_width) /6+0.6,-linkLength/2.5- (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);

				rotate ([0, 90, 0]) translate ([-linkWidth/2+off+ (link_width) /6-1.2,-linkLength/2.5+ (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);

			}
		}
		else
		{
			rotate ([0, 90, 0]) translate ([-linkWidth/2-0.3,-linkLength/2.5, link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);
		}

	}
}
module maleMount ()
{
	difference ()
	{
		union ()
		{
			difference ()
			{
				union ()
				{
					translate ([linkHeight/2, 0, 0]) hull ()
					{
						translate ([0, pinSpace, 0]) cube ([linkHeight/2, 5, linkWidth+0.6]);
						translate ([0, -pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
						translate ([-linkHeight/2,-pinSpace/6, 0]) cube ([linkHeight, 1, linkWidth+0.6]);
					}
					translate ([0,- (linkLength+tabLen) /2, shell_thickness+0.3]) cube (size=[(linkHeight+shell_thickness) /2, tabLen*2, linkWidth-shell_thickness*2+0.2]);
				}
				maleEnd ();
			}
			// Locking pins (0.2mm narrower)
			translate ([linkHeight/2, (-pinSpace/2)-1.5, 0.4]) cylinder (h=linkWidth-0.2, r=pinRad-0.25);

		}
		// Top Champher on stopper
		translate ([linkHeight/2+1,-linkLength/2-sqrt (tabLen*tabLen*2), 0]) rotate ([0, 0, 45]) cube ([tabLen, tabLen, linkWidth]);
		// Front Champher on stopper
		translate ([linkHeight/2-0.6,-linkLength/2-tabLen+0.6, 0]) rotate ([0, 0, 70]) cube (size=[2.5, 5, linkWidth+5]);
		// Hole for wires
		translate ([0-shell_thickness,-pinSpace*1.5, shell_thickness*2]) cube (size=[linkHeight, pinSpace*3.5, linkWidth- (shell_thickness*4)+0.6]);

		if (link_width >= 10)
		{
			for (off = [0:2])
			{
				rotate ([0, 90, 0]) translate ([-linkWidth/2-off- (link_width) /6+0.6, linkLength/3+ (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);

				rotate ([0, 90, 0]) translate ([-linkWidth/2+off+ (link_width) /6-1.2, linkLength/3- (linkLength/6), link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);
			}
		}
		else
		{
			rotate ([0, 90, 0]) translate ([-linkWidth/2-0.3, linkLength/2, link_height+0.6]) cylinder (h=shell_thickness*2, r=1.6);
		}
	}
}

module link ()
{
	difference ()
	{
		union ()
		{
			difference ()
			{
				union ()
				{
					translate ([linkHeight/2, 0, 0]) hull ()
					{
						translate ([0, link_xtend + pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
						translate ([0,-pinSpace/2, 0]) cylinder (h=linkWidth+0.6, r=linkHeight/2);
					}
					translate ([0,- (linkLength+tabLen) /2, shell_thickness+anti_stick/2]) cube (size=[(linkHeight+shell_thickness) /2, tabLen*2, linkWidth-shell_thickness*2+0.2]);
				}

				maleEnd ();
				femaleEnd (part_only=1);
			}

			// Locking pins (0.2mm narrower)
			translate ([linkHeight/2, (-pinSpace/2)-1.5, 0.4]) cylinder (h=linkWidth-0.2, r=pinRad-0.25);
		}
		// Top Champher on stopper
		translate ([linkHeight/2+1,-linkLength/2-sqrt (tabLen*tabLen*2), 0]) rotate ([0, 0, 45]) cube ([tabLen, tabLen, linkWidth]);

		// Front Champher on stopper
		translate ([linkHeight/2-0.6,-linkLength/2-tabLen+0.6, 0]) rotate ([0, 0, 70]) cube (size=[2.5, 5, linkWidth+5]);

		// Hole for cables
		translate ([0+shell_thickness+0.2,-linkLength/2-tabLen, shell_thickness*2]) cube (size=[linkHeight-shell_thickness*2, link_xtend+linkLength+tabLen+1, linkWidth- (shell_thickness*4)+0.6]);

		// trim top brace
		translate ([0,-pinSpace-pinRad*2, shell_thickness*2]) cube (size=[linkHeight+3, linkLength/2, linkWidth- (shell_thickness*4)+0.6]);

		if (pin_release == 1)
		{
			// Champher pins to ease joining
			translate ([linkHeight/2-pinRad, (-pinSpace/2)-1.5, -pinRad*2-1.5])  rotate ([60,0,0]) cube (pinRad*2+1); //cylinder (h=linkWidth-0.2, r=pinRad-0.25);

			translate ([linkHeight/2-pinRad, (-pinSpace/2)-1.5-pinRad*2, linkWidth+pinRad+1.5])  rotate ([300,0,0]) cube (pinRad*2+1); //cylinder (h=linkWidth-0.2, r=pinRad-0.25);
		}
	}
}

// The Male end with cutouts

module maleEnd ()
{
	//Cutouts for opposite side
	translate ([-0.6,-linkLength/2, (linkWidth+0.4)-shell_thickness])  cube (size=[linkHeight+1, pinRad*6-shell_thickness, shell_thickness+0.7]);

	translate ([-0.6,-linkLength/2, -0.6]) cube (size=[linkHeight+1, pinRad*6-shell_thickness, shell_thickness+0.8]);
	// Cut out on rounds to fit on base
	translate ([-0.6, (- (linkLength+tabLen) /2)-0.6, 0]) cube (size=[shell_thickness+1.2+anti_stick, pinRad*2.5+tabLen+0.6, linkWidth+0.6]);

}

module femaleEnd (mount, part_only)
{
	xtend = (part_only != 0) ? link_xtend : 0;

	translate ([linkHeight/2, (xtend+pinSpace/2)+1.5,-1]) cylinder (h=linkWidth+3, r=pinRad);

	//  brace cut away
	translate ([-0.6, xtend+pinRad*2.5, shell_thickness-anti_stick]) cube (size=[linkHeight+2, pinRad*3+1.2, linkWidth- (shell_thickness*2)+0.6+ (2*anti_stick)]);

	// Top brace trim
    translate ([linkHeight-shell_thickness-0.6, pinRad*2, shell_thickness-anti_stick]) cube (size=[shell_thickness*2, pinRad*4, linkWidth- (shell_thickness*2)+0.6+anti_stick*2]);

	// Cut another slot if extended
	if (xtend > 0)
	{
		translate ([-0.6, pinRad*2.5, shell_thickness-anti_stick]) cube (size=[linkHeight+2, xtend-pinRad*3, linkWidth- (shell_thickness*2)+0.6+ (2*anti_stick)]);
	}

	if (mount)
	{
		translate ([0, 0, shell_thickness]) cube (size=[linkHeight- (shell_thickness), (linkLength/2), linkWidth- (shell_thickness*2)+0.6]);
	}
	else
	{
		translate ([shell_thickness+0.2, xtend-0.6, shell_thickness-anti_stick]) cube (size=[linkHeight- (shell_thickness*2), (linkLength/2)+0.6, linkWidth- (shell_thickness*2)+0.6+ (2*anti_stick)]);
	}
}

rotate ([0, 90, 0])
{
	if (part_selection == 1)
	{
		maleMount ();
	}
	else if (part_selection == 2)
	{
		femaleMount (part_only=0);
	}
	else if (part_selection <= 3)
	{
		if (part_selection == 3)
		{
			translate ([0,link_xtend,0]) femaleMount ();
			translate ([0, (number_of_links+1) *(link_xtend+pinSpace+3), 0]) maleMount ();
		}

		for (cnt = [0:number_of_links-1])
		{
			translate ([0, (cnt+1) *(link_xtend+pinSpace+3), 0]) link ();
		}
	}
	else
	{
		echo ("Invalid Part Selection");
	}
}

```