[User]
I'm looking for a design for a compact, strong, easy-to-clean, and pocket-safe utility blade holder. I want it to be made of two parts that clip together. The blade should have two positions secured by a frame lock catch, and the default configuration should be for right-handed users. It should be designed for a standard utility blade with a default holder thickness of 5mm.

**Safety is crucial.** The safest way to hold the blade should involve gripping the holder at the frame lock with the thumb and index finger to prevent slippage. An alternative grip would be to use the thumb and middle finger on the frame lock while placing the index finger on the jimping for precise control.

I need the design to be adjustable for blade thickness and width to ensure a snug fit. There should also be an option for left-handed users, though this doesn't need to be tested initially. I'd like you to create a parametric OpenSCAD file or files to achieve this.

**Specific requirements:**

*   Two-part design that clips together.
*   Two blade positions secured by a frame lock.
*   Default configuration for right-handed users.
*   Designed for a standard utility blade.
*   Default holder thickness of 5mm.
*   Adjustable blade thickness and width for a snug fit.
*   Option for left-handed users.
*   Safe grip design to prevent blade slippage.
*   Jimping for precise control.
*   Parametric OpenSCAD file(s).

Please use the MCAD library for the OpenSCAD file(s).

[Assistant]
yaubh.scad:
```
//Yet another utility blade holder (YAUBH)
//by Khairulmizam Samsudin, Sept 2016
//xource@gmail.com
//"yaubh" is licenced under Creative Commons :
//Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
//http://creativecommons.org/licenses/by-nc-sa/4.0/

//UPDATES:
//05/09/2016 - Released

/* [Options] */
// preview[view:south, tilt:top]
// Blade thickness. Adjust to obtain smooth deployment of blade
Blade_thickness = 0.6; // [0.4:1.2]
// Tolerance for blade width. Adjust to reduce cutout width and obtain snug fit
Blade_width_tolerance = 1; // [0:5]
// Option for left handed user
Left_handed_user = 0; // [0:No,1:Yes]

/* [Hidden] */
bladeT=Blade_thickness;
left=Left_handed_user;

epsilon=0.1;
holderT=bladeT+5;
//blade
bL1=33;
bL2=64;
bW=20-Blade_width_tolerance;
notchL=3;
notchW=3;
tabL=3.5;

diff=(bL2-bL1)/2;
holderL1=bL1+diff;
holderL2=bL2+diff;
hnotchX=holderL1/2-diff+notchL/2+notchL;
hedgeR=3;//5;//4; - remove pin
hpinD=2.5;//2; //1.75mm filament
hpinR=hpinD/2;
hdepth=holderT/4; //frame cutout depth
hbottomT=holderT/2+hdepth;
htopT=holderT-hbottomT;
bopenX=tabL/2+notchL/2+hnotchX;
bcloseX=-diff/2;
bZ=hbottomT/2-bladeT/2-hdepth;
//frame cut
fcW=1;
fcL=18; //fixme. need to minus -notchL-tabL
//holder clip
hclipL=10;
hclipW=hedgeR;
hclipT=htopT;
hclipRotX=30;//20;
hclipY1=-bW/2;
hclipX1=holderL1/3;
hclipX2=-hclipX1-hedgeR;
hclipX3=-fcL/1.5;
hclipX4=+hclipX1+diff-hedgeR/1.5;
//jimping
jcR=1;
jcH=10;
jcY=bW/2+hedgeR;
jcX1=bL1+jcR*1;
jcX2=bL1-jcR*2;
jcX3=bL1-jcR*2-jcR*3;
jcX4=bL1-jcR*2-jcR*3-jcR*3;

module trapezoid(L1,L2,W,T,type=1,R=0) {
    //used for blade
    if (type==1) {
        p=[[L2/2,-W/2],[-L2/2,-W/2],[-L1/2,W/2],[L1/2,W/2]];
        translate([0,0,-T/2]) linear_extrude(height=T) polygon(p);
    //used for holder
    } else if (type==2) {
        cover_tip=13;
        p=[[L1/2-diff+cover_tip,-W/2],[-L2/2,-W/2],[-L1/2,W/2],[L1/2+cover_tip,W/2]];
        if (R !=0) {
        hull() {
                for (loc = [0:3]) {
                    translate([p[loc][0],p[loc][1],-T/2]) cylinder(r=R,h=T,$fn=20);
                }
            }
            //loca=0;
            //translate([p[loca][0],p[loca][1],-T/2]) #cylinder(r=R,h=T*2,$fn=20);
        } else {
            translate([0,0,-T/2]) linear_extrude(height=T) polygon(p);
        }
    }
}

module blade(T=bladeT,notch=0) {
    difference() {
        trapezoid(bL1,bL2,bW,T);
        //notch
        if (notch) {
            translate([notchL/2+tabL/2,bW/2-notchW/2,0]) cube([notchL,notchW+epsilon,T+epsilon],center=true);
            translate([-notchL/2-tabL/2,bW/2-notchW/2,0]) cube([notchL,notchW+epsilon,T+epsilon],center=true);
        }
    }
}

module handlec(T,e=0) {
    translate ([-bL1/6,-notchW/4,0]) scale([0.4,0.4,1]) trapezoid(bL1,bL2,bW-e,T+epsilon,type=2,R=hedgeR-e);
    translate ([-bL1/2,-notchW/4,0]) scale([0.4,0.4,1]) trapezoid(bL1,bL2,bW-e,T+epsilon,type=2,R=hedgeR-e);
}

module jimpc() {
    translate([jcX1,jcY,0]) rotate([0,0,90]) cylinder(r=jcR,h=jcH,$fn=20,center=true);
    translate([jcX2,jcY,0]) rotate([0,0,90]) cylinder(r=jcR,h=jcH,$fn=20,center=true);
    translate([jcX3,jcY,0]) rotate([0,0,90]) cylinder(r=jcR,h=jcH,$fn=20,center=true);
    translate([jcX4,jcY,0]) rotate([0,0,90]) cylinder(r=jcR,h=jcH,$fn=20,center=true);
}
//holder clip
module clip(L=hclipL,e=0) {
    difference() {
        translate([-L/2,-hclipW-epsilon/2,-hclipT/2-epsilon/2]) cube([L,hclipW*2,hclipT+e]);
        rotate([-hclipRotX,0,0]) translate([-L/2-epsilon/2,0,-hclipT]) cube([L+epsilon,hclipW*2,hclipT*3]);
    }
}

module hclip(Z=0,e=0) {
    translate([hclipX1,hclipY1,Z]) clip(e=e);
    translate([hclipX2,hclipY1,Z]) clip(e=e);
    mirror([0,1,0]) translate([hclipX3,hclipY1,Z]) clip(e=e);
    mirror([0,1,0]) translate([hclipX4,hclipY1,Z]) clip(L=hclipL*1.5, e=e);
}

module holder(T=holderT/2,e=0,jimping=1,clip=0) {
    difference() {
        //trapezoid(holderL1,holderL2,bW,bladeT,type=2);
        trapezoid(holderL1,holderL2,bW,T,type=2,R=hedgeR+e);
        //holder clip cutout
        if (clip==2) {hclip(Z=0,e=epsilon); }

        //handle cutout
        handlec(T,e=e);

        //jimping
        if (jimping) jimpc();
    }
    //holder clip
    if (clip==1) {
        difference() {
            hclipZ=hbottomT/2+htopT/2;
            hclip(Z=hclipZ);
            //jimping
            jimpc();
        }
    }
}

module bhframe(opennotch=1,bladecut=1,e=0,jimping=1,clip=0) {
    difference() {
        holder(T=hbottomT+e,e=e,jimping=jimping,clip=clip);
        //blade cutout
        bcT=bladeT+hdepth+epsilon;
        bcZ=hbottomT/2-bcT/2+epsilon/2;
        if (bladecut) {
            //blade close and open position
            translate([bcloseX,0,bcZ]) blade(notch=0,T=bcT+epsilon);
            translate([bopenX,0,bcZ]) blade(notch=0,T=bcT+epsilon);
            //blade access
            translate([bopenX+bL1,0,bcZ]) blade(notch=1,T=bcT+epsilon);
        }

    }
}
//bottom holder - additional hdepth recessed
module bholder() {
    difference() {
        bhframe(clip=1);

        //frame cut
        translate([hnotchX,bW/2-notchW/2,holderT/2-hbottomT/2]) union() {
            translate([-fcL/2-notchL/2,-fcW/2+notchW/2,0]) cube([fcL,fcW,10],center=true);
            //two catch
            //c1L=notchL*2+tabL+fcW*2;
            //only catch1 i.e. one notch
            c1L=notchL+fcW;
            c1W=notchW+fcW;
            //two catch
            //c1X=c1L/2-notchL/2-fcW;
            c1X=c1L/2-notchL-tabL+fcW;
            c1Y=-fcW/2;
            //only catch2 i.e. one notch
            c3L=notchL+tabL;
            c3W=notchW;
            c3X=c1X-c1L/2-c3L/2+notchW+tabL;
            c3Y=-c3W/2+fcW/2;
            difference() {
                //catch1 plate
                // translate([c1X,c1Y,0]) cube([c1L,c1W,10],center=true);
                //longer plate
                translate([c1X,c1Y,0]) cube([c1L+notchL+tabL,c1W,10],center=true);
            //    translate([c3X,c3Y,0]) cube([c3L,c3W,11], center=true);
            }

            c2W=notchW+hedgeR+fcW+epsilon;
            c2L=fcW;
            c2X=-c2L/2+c1L/2+c1L/2-notchL/2;
            c2Y=c2W/2+c1Y-c1W/2;
            translate([c2X,c2Y,0]) cube([c2L,c2W,10], center=true);
        }
        //frame lock stress reliever
        hsrR=1;
        hsrY=bW/2+hedgeR/2;
        hsrX=hnotchX-fcL-hsrR;
        hsrZ=hbottomT/2;
        translate([hsrX,hsrY,hsrZ]) rotate([90,0,0]) cylinder(r=hsrR,h=10,center=true,$fn=20);

    }
    //catch
    translate([hnotchX,bW/2-notchW/2,-hdepth/2]) union() {
        //catch1
        cube([notchL,notchW,hbottomT-hdepth],center=true);
        //catch2
        //translate([tabL+notchL,0,0]) cube([notchL,notchW,hbottomT-hdepth],center=true);

        //bottom index finger plate
        tpL=tabL+notchL*2;
        translate([-tabL/2-notchL/2,0,-bladeT/2]) cube([tpL,notchW,hbottomT-hdepth-bladeT],center=true);
        //top index finger plate
        tpL=tabL+notchL*2;
        translate([-tabL/2-notchL/2,notchW/2+hedgeR/2,holderT/2-(hbottomT-hdepth)/2]) difference() {
            cube([tpL,hedgeR,holderT],center=true);
            //translate([0,0,hedgeR/2]) rotate([90,0,0]) cylinder(r=1,h=hedgeR*2,$fn=20,center=true);
            //translate([tpL/2,0,hedgeR/2]) rotate([90,0,0]) cylinder(r=1,h=hedgeR*2,$fn=20,center=true);
            //translate([-tpL/2,0,hedgeR/2]) rotate([90,0,0]) cylinder(r=1,h=hedgeR*2,$fn=20,center=true);
        }
    }

}
module bg() {
    difference() {
        bhframe(opennotch=0,bladecut=0);
        bhframe(opennotch=0,bladecut=1,e=epsilon);
    }
}

//top holder - additional hdepth raised
module tholder() {
    difference() {
        holder(T=htopT,clip=2);
        //index finger plate cut
        c1L=notchL*2+tabL+fcW*2;
        translate([notchL+tabL-fcW,bW/2-notchW/2,holderT/2-hbottomT/2])
        translate([c1L/2-notchL/2,hedgeR/2+notchW/2,0]) cube([c1L,hedgeR+epsilon,10],center=true);

    }

    //blade clamp
    bgT=hdepth;
    bgZ=-bgT/2-bgT;
    translate([0,0,-htopT/2]) difference() {
        translate([0,0,-hbottomT/2]) bhframe(opennotch=0,bladecut=0,jimping=0);
        translate([0,0,-hbottomT/2]) bhframe(opennotch=0,bladecut=1,e=epsilon,jimping=0);
        translate([0,0,bgZ]) cube([bL2*2,bL2,bgT],center=true);
    }
}

debug=0;
if (debug) {
        tholder();
        translate([0,0,-10]) bholder();
} else {
    tholderZ = (left==0) ? htopT/2-hbottomT/2 : -htopT/2+hbottomT/2;
    rotX = (left==0) ? 0 : 180;
    rotate ([rotX,0,0]) {
        translate([0,40,tholderZ]) mirror([0,0,left]) rotate([180,0,0]) tholder();
        translate([0,0,0]) mirror([0,0,left]) bholder();
    //translate([bcloseX,0,bZ]) %blade(notch=1);
    translate([bopenX,0,bZ]) %blade(notch=1);
}
}

```


[User]
Okay, I'm looking to design a highly customizable funnel in OpenSCAD. I have a basic idea and some specific parameters I want to control. I've also got a good idea about how the funnel should look visually, and I've noted the different dimensions in a drawing, but I'm not going to include the picture in this request since I am trying to simulate a user.

Here's a breakdown of the adjustable parameters I need:

1.  **Big Outside Diameter (Big_OD):**  This is the diameter of the wide opening of the funnel. I need to be able to adjust this.

2.  **Big Height (Big_Ht):** The height of the straight cylindrical portion of the large opening before it starts to flare inwards. This should be adjustable, even to zero.

3.  **Funnel Thickness (Thk):** The overall wall thickness of the funnel, which should be consistent throughout, except for the small end, which should be configurable separately.

4.  **Big Flare Angle (Big_Ang):** The angle of the flare of the main funnel body, relative to the vertical, between the big and small diameters. This should be adjustable.

5.  **Small Outside Diameter (Small_OD):**  The diameter of the small end opening of the funnel, also an adjustable parameter.

6.  **Small Height (Small_Ht):** The length of the straight cylindrical section at the small end, prior to any flaring.

7.  **Small Flare Angle (Small_Ang):** The flare angle of the small end of the funnel. This should be adjustable, ideally constrained between 75 and 90 degrees.

8.  **Resolution (Ires):**  The resolution of the curves in the funnel's cylinders and flares. This should affect how smooth the funnel appears. I'd like to control the number of facets using this.

9.  **Draw Eyelet (Draw_Eye):**  A yes/no switch to include an eyelet (a small loop) for hanging the funnel.

10. **Eyelet Outside Diameter (Eye_OD):** The diameter of the outer surface of the eyelet.

11. **Eyelet Inside Diameter (Eye_ID):** The diameter of the hole in the eyelet.

12. **Eyelet Height (Eye_Ht):** The overall height of the eyelet.

13. **Lip Thickness (Lip_Thk):** The thickness of a lip around the base of the wide end of the funnel that could act as a support.

14. **Lip Height (Lip_Ht):**  The vertical height of the aforementioned lip.

There should also be a hidden parameter:

15.  **Small Thickness (Small_Thk):** The wall thickness of the small end of the funnel.  This should default to the same value as Thk, but it should be customizable separately.

I need to calculate several other dimensions internally, such as:

*   Lip Outside Diameter (**Lip_OD**)
*   Lip Radius (**Lip_Rad**)
*   Big Inside Diameter (**Big_ID**)
*   Small Radius (**Small_Rad**)
*   Inside Diameter of Smallest Part of Funnel (**Small_ID**)
*   Height of Funnel Top to Center Line (**Fun_TopHt**)
*   Funnel Thickness Based on Angle Used (**Fun_Thk**)
*   Inside Diameter of the big flared portion of the funnel (**Fun_Top_ID**)
*   Lower Flared Portion of the Funnel Inside Diameter (**Bot_Fun_ID**)
*   Total Height of the Funnel (**Tot_Ht**)

I need to make sure that all of the dimensions are calculated correctly based on the user's input.

I'm hoping you can generate all of the necessary OpenSCAD code, not just a modification of an existing file. Also, I'm *not* looking for libraries like MCAD or BOSL, so please don't use them. I'd prefer if the code was completely self-contained. Thanks!

[Assistant]
Funnel_Rev11.scad:
```
//Customizable Funnel
//8/31/2016     By: David Bunch
//
//1. Outside Diameter of Big Part of Funnel
Big_OD = 70;    //[12:1:200]

//2. Height of Big Part of Funnel before Flare
Big_Ht = 5;     //[0:1:50]

//3. Thickness of Funnel
Thk = 1.65;         //[1:.1:5]

//4. Flare Angle of Big Part of Funnel
Big_Ang = 45;   //[30:1:80]

//5. Outside Diameter of Small Part of Funnel
Small_OD = 22;    //[6:1:100]

//6. Height of Small Part of Funnel
Small_Ht = 26;     //[6:1:50]

//7. Small Flared Angle (75 to 90 degress)
Small_Ang = 85;   //[75:1:90]

//8. Resolution of Funnel
Ires = 32;           //[4:2:72]

//9. Draw Eyelet to hang Funnel from (yes, or no)
Draw_Eye = "yes";  //[yes,no]

//10. Outside Diameter of Eyelet
Eye_OD = 11;       //[7:1:20]

//11. Inside Diameter of Eyelet
Eye_ID = 7;        //[1:1:18]

//12. Height of Eyelet Hanger
Eye_Ht = 2;        //[.1:.1:3]

//13. How Thick to draw a Lip around Print Base of Funnel
Lip_Thk = 2;        //[0:.5:20]

//14. How High is the Lip
Lip_Ht = 2;         //[.5:.5:6]

/* [Hidden] */

Small_Thk = Thk;       //Thickness at Bottom smaller part can be different from Top
                                        //Generally the same as Thk, but you can change it
Lip_OD = Big_OD + (2 * Lip_Thk);        //Diameter including the Lip Width
Lip_Rad = Lip_OD / 2;                   //Radius of Lip
Big_OD1 = Big_OD - (Thk * 2);       //Only used if Big_Ht > 0
Big_Rad = Big_OD / 2;               //Radius of Top Part of Funnel
Small_Rad = Small_OD / 2;           //Radius of Bottom Part of Funnel
Small_ID = Small_OD - (Small_Thk * 2);    //Inside Diameter of Smallest part of Funnel
Fun_TopHt = (Big_Rad - Small_Rad) * tan(Big_Ang);     //Height of Funnel Top to Center Line
Fun_Thk = Thk / sin(Big_Ang);       //Funnel Thickness based on Angle used
                                    //Need this to calculate actual Inside Diameters of flared part
Fun_Top_ID = Big_OD - (Fun_Thk * 2);        //Inside Diameter of Big part of Funnel
Fun_Top_ID1 = Small_OD - (Fun_Thk * 2);
Bot_Fun_90Ang = 90 - Small_Ang;             //Need smaller Angle
Bot_Fun_Thk = Small_Thk / cos(Bot_Fun_90Ang);
Bot_Fun_ID = Small_OD - (Bot_Fun_Thk * 2);      //Lower flared portion of Funnel Inside Diameter

Tot_Ht = Fun_TopHt + Small_Ht + Big_Ht;     //Total Height of Funnel
//            cylinder(d1=Fun_Top_ID,d2=Bot_Fun_ID,h=Fun_TopHt+.01);
Bot_Flare_Dist = Small_Ht * tan(Bot_Fun_90Ang);
Bot_Fun_OD1 = Small_OD - (Bot_Flare_Dist * 2);
Bot_Fun_ID1 = Bot_Fun_OD1 - (2 * Bot_Fun_Thk);

echo(Bot_Fun_Thk = Bot_Fun_Thk);
echo(Fun_TopHt = Fun_TopHt);
echo(Fun_Thk = Fun_Thk);
echo(Fun_Top_ID = Fun_Top_ID);

echo(Tot_Ht = Tot_Ht);
echo(Bot_Fun_90Ang = Bot_Fun_90Ang);
echo(Bot_Flare_Dist = Bot_Flare_Dist);
echo(Bot_Fun_OD1 = Bot_Fun_OD1);
echo(Bot_Fun_ID = Bot_Fun_ID);
echo(Bot_Fun_ID1 = Bot_Fun_ID1);
Big_OD_Res = (round(((Big_OD * 3.14)/4) / 1.0)*4);  //about 1.0mm segments
Eye_OD_Res = (round(((Eye_OD * 3.14)/4) / .7)*4);
echo(Big_OD_Res = Big_OD_Res);
$fn=Ires;               //Set Resolution so we do not need to add it to cylinder()'s
//$fn = Big_OD_Res;       //If you prefer about 1mm resolution on segments, uncomment this

module DrawFunnel()
{
    difference()
    {
        union()
        {
            if (Big_Ht > 0)
            {
                cylinder(d=Big_OD,h=Big_Ht);
            }
//Flared Outside Big part of Funnel
            translate([0,0,Big_Ht])
            cylinder(d1=Big_OD,d2=Small_OD,h=Fun_TopHt);
//Flared or Straight Small part of Funnel
            translate([0,0,Fun_TopHt+Big_Ht])
            cylinder(d1=Small_OD,d2=Bot_Fun_OD1,h=Small_Ht);
            if (Draw_Eye == "yes")
            {
                translate([(Lip_Rad + (Eye_ID/2)),0,0])
                difference()
                {
                    cylinder(d=Eye_OD,h=Eye_Ht,$fn=Eye_OD_Res);
                    translate([0,0,-1])
                    cylinder(d=Eye_ID,h=Eye_Ht+2,$fn=Eye_OD_Res);
                }
            }
//See if we need to draw a lip around the Funnel
            if (Lip_Thk > 0)
            {
                cylinder(d=Lip_OD,h=Lip_Ht);
            }
        }
        if (Big_Ht > 0)
        {
            translate([0,0,-1])
            cylinder(d1=Big_OD1,d2=Fun_Top_ID,h=Big_Ht+1.01);
            translate([0,0,Big_Ht])
            cylinder(d1=Fun_Top_ID,d2=Fun_Top_ID1,h=Fun_TopHt+.01);
        } else
        {
            translate([0,0,Big_Ht-.01])
            cylinder(d1=Fun_Top_ID,d2=Bot_Fun_ID,h=Fun_TopHt+.01);
        }
        if (Small_Ang > 0)
        {
            translate([0,0,Big_Ht+Fun_TopHt-10])
            cylinder(d=Bot_Fun_ID,h=10);     //Flared cut small part of Funnel
            translate([0,0,Big_Ht+Fun_TopHt-.01])
            cylinder(d1=Bot_Fun_ID,d2=Bot_Fun_ID1,h=Small_Ht);     //Flared cut small part of Funnel
            translate([0,0,-1])
            cylinder(d=Bot_Fun_ID1,h=Tot_Ht+2);
        } else
        {
            translate([0,0,-1])
            cylinder(d=Small_ID,h=Tot_Ht+2);
        }
//        translate([-Big_OD,0,-1])
//        cube([Big_OD*2,Big_OD,Tot_Ht+2]);       //Cross section to see Thickness width
    }
}
DrawFunnel();
//%cylinder(d=Big_OD,h=Tot_Ht);     //Check Total Height
```


[User]
I need a customizable thumbscrew design for hex bolts, kind of like the one I saw online. It should be parametric so I can adjust it for different bolt sizes, protrusion amounts and shaft length, and other things that will allow for 3D printing and use with various hardware.

Specifically, I'd like to be able to specify:

*   **Hex Bolt Dimensions:**
    *   The width across parallel sides of the bolt's hex head (let's call this `hex_w`).
    *   The thickness/height of the bolt's hex head (let's call this `hex_h`).
    *   The diameter of the bolt's shaft (let's call this `bolt_dia`).
*   **Thumbscrew Parameters:**
    *   An extra space factor for the bolt's hex head opening and shaft hole, to allow for easier fitting, (call it `slack`).  This should be a multiplier, like 1.05.
    *   The overall size of the thumbscrew in relation to the hex bolt head (e.g., a factor of 2 means the thumbscrew is roughly twice as big as `hex_w` - call this `t_size`).
    *   The number of protrusions/bumps around the circumference (call this `bumps`).
    *   The diameter of each bump relative to the hex head size - again, a multiplier (like 2 means bumps twice as large) (call this `bump_dia`).
    *   The height of the shaft as a ratio of the hex head height, (call it `shaft_h`).
*   **Resolution:** The number of facets used to render round parts, via the `$fn` variable.

The design should:

1.  Create a central base with a cylindrical or rounded shape that is large enough to surround the hex head, with a hole that precisely fits the hex head and an attached section to fit the bolt shaft, with a taper to allow for 3D printing.
2.  Have a series of protrusions/bumps equally spaced around the circumference, with a cylindrical or rounded profile, placed around the widest part of the base.
3.  Include a short cylindrical shaft section that extends from the base with a hole that allows the bolt shaft to pass through. The hole should use `slack` to give some extra play.
4.  Use a small amount of taper internally and externally to avoid bridging issues during 3D printing and improve strength.
5.  The design should avoid internal bridging and ensure the model prints well and it is reasonably easy to assemble with the intended hardware. The idea is that the user will press fit, or use glue if the tolerance is loose, in order to fix the hardware into the part.

It'd be great if this used the `MCAD` library since it contains a nice `hexagon` function I'm familiar with.  I’m happy to use other libraries if necessary though.

[Assistant]
thumbscrew_customizer.scad:
```
// configurable thumbscrew - 2016 Tim Deagan
// Default values are for an M6 bolt

/* [Bolt Dimensions] */
// = the width across parallel sides of the bolt hex head
hex_w = 10;
// = the thickness of the bolt hex head
hex_h = 4.24;
// = the diameter of the bolt's shaft
bolt_dia = 6;

/* [Thumbscrew parameters] */
// = the extra space factor for the holes
slack = 1.05;
// = the size of the thumbscrew in relation to the hex bolt head (hex_w)
t_size = 2;
//= the number of protrusions
bumps = 6;
// = ratio of the diameter of the bump to the hex head size (2 = x2)
bump_dia = 1;
// = how tall the shaft is as a ratio of the hex head height
shaft_h = 1.25;
// = polygon resolution of the rendering (100 = high, 20 = low)
$fn = 100;

//utility params
bump_rad = (hex_w * bump_dia)/2;
bolt_rad = bolt_dia/2;

difference(){
    union(){
        cylinder(h=hex_h+2,r=(hex_w * t_size)*.85 );

        translate([0,0,hex_h+2])
            cylinder(h=(hex_h*shaft_h)+2,r1=hex_w *.8 , r2=bolt_rad +3);

        for(turn=[0:360/bumps:360]){
            rotate([0,0,turn])
                union(){
                    translate([0,(hex_w * t_size)/2,(hex_h+2)/2])
                        cube([bump_rad*2,hex_w*t_size,hex_h + 2],true);

                    translate([0,(hex_w * t_size),0])
                        cylinder(r1=bump_rad,r2=bump_rad,h=hex_h+2);
                }
        }
    }

    translate([0,0,hex_h-.02])
        cylinder(r1=(hex_w/2)*1.05,r2=bolt_rad,h=hex_h+.02);

    translate([0,0,-.01])
        scale([slack,slack,1])
            target_bolt(100);

    }

module target_bolt(len){
    union(){
        translate([0,0,hex_h/2])
            hexagon(hex_w,hex_h);

        translate([0,0,hex_h])
            cylinder(h=len,r=bolt_rad * slack);
    }
}

// from MCAD
// size is the XY plane size, height in Z
module hexagon(size, height) {
  boxWidth = size/1.75;
  for (r = [-60, 0, 60]) rotate([0,0,r]) cube([boxWidth, size, height], true);
}

```


[User]
Okay, I need an OpenSCAD design for a monitor stand. The stand should be composed of two crossing bars, each shaped sort of like a wide, flat popsicle stick.

Here are the parameters I want to be able to control:

*   **`thickness`**: The thickness of the bars (default 4mm).
*   **`length`**: The length of the bars (default 190mm).
*   **`height`**: The height of the bars (default 20mm).
*   **`angle`**: The angle at which the bars cross each other (default 60 degrees).
*   **`tiltangle`**: The angle at which the monitor will be tilted back (default 30 degrees).
*   **`monitorthickness`**: The thickness of the monitor (default 15.875mm).
*   **`crossingPointDistance`**: The distance from the end of each bar to the point where they cross (default 25mm).
*   **`sittingPosition`**: The distance from the front edge of the monitor to the front of the bars (default 20mm).

The two bars should cross at the specified `angle`, forming an X shape when viewed from the side. The monitor will rest on top of this X, tilted back at the `tiltangle`. There should be a cutout in the top of each bar to accommodate the monitor's thickness and prevent it from sliding forward. The `sittingPosition` parameter determines how far back this cutout is placed.

I also want a way to select which parts to print:

*   **`part`**: A string parameter to control which parts are generated. It should accept the following values:
    *   `"lbar"`: Only the left bar.
    *   `"rbar"`: Only the right bar.
    *   `"both"`: Both left and right bars (default).

The ends of the bars should be rounded, I would like the roundings to be accomplished using `minkowski` operations. The bars should intersect with each other in a way that makes them flush, so one bar will need a cutout on the top, and one will need a cutout on the bottom so that they overlap nicely. I'd like these cutouts to be generated using `difference` operations.

Oh, one more thing. The design uses the MCAD library, specifically `parts/polyhole.scad` for some of the rounded end stuff. Please make sure to include that in the generated files.

[Assistant]
monitorstand.scad:
```
// Thickness of the bars
thickness=4;
// Length of the bars
length=190;
// Height of the bars
height=20;
// Crossing angle of the bars
angle=60;
// Tilt angle of the monitor
tiltangle=30;
// Thickness of the monitor
monitorthickness=15.875;
// Distance from the end of the bar to the crossing point
crossingPointDistance = 25;
// Sitting position from the monitor to the front of the bars
sittingPosition=20;
// What to print
part = "both"; //[lbar:Left Bar Only, rbar:Right Bar Only,both:Both left and right bars]

module foo() {}
$fn=40;
inch=25.4;
barangle=angle/2;
separation=2*sin(barangle)*(length-crossingPointDistance);

difference() {
    union() {
        if (part == "lbar" || part == "both") {
             difference() {
                rotate([0,0,-barangle]) bar();
                #translate([separation-thickness/2-2,0,0]) rotate([0,0,barangle]) translate([0,0,height/2-.5]) cube([thickness+2.5,length+1,height/2+1.5]);
            }
        }
        if (part == "rbar" || part == "both") {
            difference() {
                translate([separation-.25,0,0]) rotate([0,0,barangle]) bar();
                #rotate([0,0,-barangle]) translate([-thickness/2-1.25,0,-1]) cube([thickness+2.5,length+1,height/2+1.5]);
            }
        }
    }
    #translate([-40,sittingPosition,1.5+monitorthickness*sin(tiltangle)]) rotate([-tiltangle,0,0]) cube([separation+80, monitorthickness+1, height*3]);
}

module bar() {
    minkowski() {
        union() {
            translate([-thickness/2+1,1,1]) cube([thickness-2,length-2,height-2]);
            translate([thickness/2,2,0]) rotate([0,-90,0]) difference() {
                translate([height/2,-1,1]) cylinder(h=thickness-2, d=height-2);
                translate([0,-1,0]) cube([height+.01,height/2+.01,thickness+.01]);
            }
        }
        sphere(2);
    }
}
```


[User]
I'm looking for a parametric OpenSCAD design for an SD card holder. I want it to be able to hold both standard SDHC cards and MicroSD cards. I'd like the ability to easily change the number of slots for each type of card.

The overall design should have a rectangular base, with slots cut out for each card. I want the SDHC slots on one side and the MicroSD slots on the other. I'd like the slots to be oriented so that the cards are inserted vertically, with the longer dimension of the card going into the slot.

Here's a breakdown of the dimensions I have in mind, but I'd like these to be easily adjustable as parameters:

*   **SDHC:**
    *   Width: 24mm (so maybe 26mm wide slots?)
    *   Thickness: 2.1mm (so maybe 3mm slots?)
    *   Depth: 32mm (but only half of the card should stick into the holder, so 16mm deep slots?)
*   **MicroSD:**
    *   Width: 11mm (so maybe 12mm wide slots?)
    *   Thickness: 1mm (so maybe 2mm slots?)
    *   Depth: 15mm (again, only half should go in, so maybe 8mm slots?)

I'd also like to be able to adjust the border thickness around the slots. I'm thinking around 3mm would be good, but it would be nice to be able to tweak that. It would also be nice if the design incorporated rounded corners instead of sharp, squared-off edges.

For libraries, please use the BOSL2 library.

Please generate the OpenSCAD file or files necessary to create this parametric SD card holder. I'm looking for a well-commented, clean design that I can easily modify to fit my specific needs.

[Assistant]
sdcard_holder.scad:
```
slotBorder = 3; // border around slots

microsdSlots = 4;
microsdWidth = 12; // 11 wide
microsdThickness = 2; // 1 thick
microsdDepth = 8; // half of 15 high
microsdHolderDepth = slotBorder + microsdThickness*microsdSlots + slotBorder*microsdSlots;

sdhcSlots = 5;
sdhcWidth = 26; // 24
sdhcThickness = 3; // 2.1
sdhcDepth = 16; // half of 32
sdhcHolderDepth = slotBorder + sdhcThickness*sdhcSlots + slotBorder*sdhcSlots;

holderWidth = sdhcWidth + 2*slotBorder;
holderHeight = sdhcDepth + slotBorder;

// makes solid holder block
module basicBlock(holderDepth) {
    linear_extrude(height=holderWidth, convexity=2)
        polygon(points = [ [0, 0],
            [holderDepth, 0],
            [holderDepth, holderHeight],
            [0, holderHeight] ]);
}

// makes slot holes
module slots(width,thickness,numslots) {
    for (i = [0 : numslots-1]) {
        translate([i*(slotBorder+thickness), 0])
            translate([slotBorder, slotBorder,0])
                translate([0,0,slotBorder])
                    cube(size=[thickness, holderHeight, width]);
    }
}

// generate both parts
module SDCardHolder() {
    // sdhc
    translate([-sdhcHolderDepth/2,holderWidth/2,0])
        rotate([90,0,0])
            difference() {
                basicBlock(sdhcHolderDepth);
                slots(sdhcWidth, sdhcThickness, sdhcSlots);
            }

    // microsd
    translate([sdhcHolderDepth, 0, 0])
        translate([-sdhcHolderDepth/2,holderWidth/2,0])
            rotate([90,0,0])
            difference() {
                basicBlock(microsdHolderDepth);
                slots(microsdWidth, microsdThickness, microsdSlots);
            }
}

SDCardHolder();

```


[User]
I need a parametric OpenSCAD design for a bottle holder. I'd like to be able to customize it for different bottle sizes and quantities.

Here's what I'm looking for:

*   **Shape Options:** It should support both round and square bottle shapes.
*   **Customizable Dimensions:**
    *   Number of bottles along the X and Y axes.
    *   Inside diameter (or dimensions for square bottles) - I want to be able to add a little clearance.
    *   The diameter of a push hole at the bottom of each holder to remove the object - this should be optional and default to 0 (disabled).
    *   Overall height of the holder.
    *   Base thickness - ideally divisible by the layer height for easier printing.
    *   Wall thickness - ideally divisible by the nozzle diameter for easier printing, and the walls should connect. Don't make this any less than 2mm.
*   **Calculated Variables:** The code should automatically calculate things like the starting position of the first bottle and the spacing between bottles.
*   **Printing Considerations:** I'll likely be using a 0.5mm nozzle with 0.3mm layer height, and want to be able to print with 3 solid top and bottom layers, 2-3 perimeters, and 20% infill.

Can you design a parametric OpenSCAD file that meets these requirements? I don't need any external libraries for this project.

[Assistant]
Parametric_botte_holder.scad:
```
// Code written by "deckingman" Feb 2016
// Use and enjoy

// User Defined Variables
// Change any of the following to suit

// Bottle hole shape. round/square
Bottle_Shape="round"; // [round, square]

// The number of bottles along the x axis
Number_Of_Bottles_Long=4;

// The number of bottles along the y axis
Number_Of_Bottles_Wide=6;

// The inside diameter of the cylinders which hold the bottles.
// Measure your bottle then add a milimeter or 2 for clearance
Inside_Diameter=25.5;

//Diameter of a push hole at the bottom of each cylinder
Push_Hole_Diameter=0;

// The overall height in mm. Note that the depth of the hole will be this number minus the base thickness
Overall_Height=22;

// The base thickness in mm. For ease of printing, make this divisible by the layer height of you slicer/printer.
// So for example if the layer height is 0.3 mm use 1.8 or 2.1 but not 2.0.
Base_Thickness=1.8;

// The wall thickness of the cylinders. Note that the cylinders are joined together by whatever this thickness is
// so DO NOT MAKE THIS ANY LESS THAN 2mm. For ease of printing, make this number divisible by your nozzle diameter.
Wall_Thickness=2.25;

// Suggested Slicer settings. 3 solid top and bottom layers, 2 or 3 perimiters. 20% infill.
// Using a 0.5mm nozzle with 0.3mm layer height.

/* [Hidden] */
// Changing any of the following will likely screw things up !!
First_Bottle_Pos = Bottle_Shape == "square" ? 0 : (Inside_Diameter/2)+Wall_Thickness;
Step = Inside_Diameter+Wall_Thickness;
iMax=First_Bottle_Pos+(Number_Of_Bottles_Long*(Step-1));
jMax=First_Bottle_Pos+(Number_Of_Bottles_Wide*(Step-1));

$fn = 150;

for (i = [First_Bottle_Pos:Step:iMax]){
  for (j = [First_Bottle_Pos:Step:jMax]){
    if(Bottle_Shape == "round"){
      difference(){
        translate ([i,j,0]) cylinder (d=Inside_Diameter+(2*Wall_Thickness),h=Overall_Height);
        translate ([i,j,Base_Thickness]) cylinder (d=Inside_Diameter, h=Overall_Height);
        if(Push_Hole_Diameter > 0)translate ([i,j,-0.1]) cylinder (d=Push_Hole_Diameter, h=Base_Thickness+0.2);
      }
    }
    if(Bottle_Shape == "square"){
      difference(){
        translate ([i,j,0]) cube ([Inside_Diameter+(2*Wall_Thickness),Inside_Diameter+(2*Wall_Thickness),Overall_Height]);
        translate ([i+Wall_Thickness,j+Wall_Thickness,Base_Thickness]) cube ([Inside_Diameter,Inside_Diameter,Overall_Height]);
        if(Push_Hole_Diameter > 0)translate ([i+(Inside_Diameter/2)+Wall_Thickness,j+(Inside_Diameter/2)+Wall_Thickness,-0.1]) cylinder (d=Push_Hole_Diameter, h=Base_Thickness+0.2);
      }
    }
  }
}
```


[User]
I need an OpenSCAD file that can generate a customized square plate with a two-digit anniversary number prominently displayed on it. This plate is meant to be used as a part of a personalized candle holder design, specifically the one found at this link: [http://www.thingiverse.com/thing:1755787](http://www.thingiverse.com/thing:1755787).

The plate itself should have dimensions of approximately 85mm x 85mm x 2mm. There should be a circular hole in the center of the plate, with a diameter of around 70mm, and a 2mm wide bar at the top part, to allow for it to be hung by another part of the candle holder design. The main feature of the design is a two-digit number, representing the anniversary year (e.g., "47"), which should be extruded from the plate's surface, and should use the "Archivo Black" font. The numbers should be centered on the plate, have a height of approximately 34, and be extruded by 2 units. The entire plate should also have an overall thickness of 2 units.

Make the design parametric so that I can easily change the anniversary year. I'd also like to be able to adjust the position of the horizontal bar as well as the font and size of the text, if needed. I think the `MCAD` library should be used for this project, but I'm open to suggestions if another library would be better.


[User]
I'd like to create a modular maze game using OpenSCAD. The idea is to have square tiles that can be connected to form a larger maze. Each tile will have four "quart" sections, and each section can have different features that create paths or walls. I also need the tiles to have the option of having walls on the perimeter, and some connecting posts on the corners.

Here's a breakdown of what I'm imagining:

**Core Tile Design:**

*   **Grid:** The maze is built from square tiles, arranged in a grid. The tile size is a parameter, let's start with 100mm.
*   **Quart Sections:** Each tile should be divided into four quadrants or "quart" sections. These sections are rotated by 90 degrees.
*   **Path/Wall Generation:** Within each quart, I need some parameters that determine if there is a wall. I want both a major wall section that acts as a "dice" or random cut-out feature and a wall blocking the passage from the side.
*   **Corner Connection:** I'd like the corners of the tiles to have posts/connectors so they can fit together.
*   **Perimeter Walls:** Each tile needs the option to have walls on its outside edges (north, east, south, west). These would be for the maze edge and for dividing the maze.
*  **Randomization:** I need a way to randomize the wall generation inside each section (using a dice function) and whether the section has a cut-out or not (again using a dice function). A random color option would be a nice addition.

**Quart Section Detail:**

*   **Base Shape:**  Each section will have a base shape created using `linear_extrude` with some offsets to create a pathway effect, with the walls and edges on each side. There should be a cut-out or wall option on one side.
*  **Cut-out Feature:** I need a way to produce a cut-out or a wall feature in one part of the main passage within the quart section, a sort of half triangle. I would like it to have a cylindrical wall as well. It should be parametrized so the shape is similar, but with a different size.
*   **Blocker Feature:** I'd like to be able to have a cylinder and or a cube block a passage, and again use a "dice" value to determine if they are present or not.

**Parameters:**

*   `Xsize` and `Ysize`: The dimensions of the maze in terms of number of tiles.
*   `Open`: A parameter to control the dice function, perhaps to allow for the maze to be more or less open with different wall probabilities.
*   Tile size: Size of one side of the square tile.
*   Wall thickness.
*   Cut-out size and position parameters.
*  Blocker radius and height parameters.
*   Perimeter wall thickness.

**Output:**

I need an OpenSCAD file that generates a full maze based on the given parameters.  It should display a grid of tiles, each with its own random internal features. I would also like an option to have corner connectors to make the tiles stay in place.

I think that's everything, let me know if you need more info. I don't have any external libraries, so please try to avoid needing them.

[Assistant]
amazecustm.scad:
```
Xsize=5;
Ysize=5;
Open=0;//[-1:0.01:1]
/*[hidden]*/

for(y=[1:Ysize]){
for(x=[1:Xsize]){
translate([x*100,y*100,0])make(x,y);
}}

module make(x,y){

W=[y==1?true:false,x==Xsize?true:false,y==Ysize?true:false,x==1?true:false];
G=[dice(),dice(),dice(),dice()];
I=[dice(),dice(),dice(),dice()];

color(rndc()) piece(W,G,I);

}

module piece(W,G,I){
rotate([0,0,0]) quart(G[0],I[0],W[1],W[2]);
rotate([0,0,90]) quart(G[1 ],I[1],W[2],W[3]);
rotate([0,0,180]) quart(G[2],I[2],W[3],W[0]);
rotate([0,0,270]) quart(G[3],I[3],W[0],W[1]);
cylinder(45,5,5);
translate([0,0,44])sphere(6,center=true);
if(W[0]==true){
rotate([0,0,-90])
translate([41.5,-47.225,0])cube([5,89.5,45]);
}
if(W[1]==true){
rotate([0,0,0])
translate([41.5,-47.225 ,0])cube([5,89.5,45]);
}
if(W[2]==true){
rotate([0,0,90])
translate([41.5,-47.225,0])cube([5,89.5,45]);
}
if(W[3]==true){
rotate([0,0,180])
translate([41.5,-47.225,0])cube([5,89.5,45]);
}
if(W[0]==true&&W[1]==true){
rotate([0,0,0])
{
translate([40,-40,0])translate([4,-4,0]){cylinder(51,5,5);
translate([0,0,51])sphere(7,center=true);}
}}
if(W[1]==true&&W[2]==true){
rotate([0,0,90])
{
translate([40,-40,0])translate([4,-4,0]){cylinder(51,5,5);
translate([0,0,51])sphere(7,center=true);}
}}
if(W[2]==true&&W[3]==true){
rotate([0,0,180])
{
translate([40,-40,0])translate([4,-4,0]){cylinder(51,5,5);
translate([0,0,51])sphere(7,center=true);}
}}
if(W[3]==true&&W[0]==true){
rotate([0,0,270])
{
translate([40,-40,0])translate([4,-4,0]){cylinder(51,5,5);
translate([0,0,51])sphere(7,center=true);}
}}

}

module quart(g,i,w,w2){
$fn=15;
translate([22.5,22.5]){
linear_extrude(5, convexity = 5){
offset(r=1.9)offset(r=-6)offset(r=4){ difference(){
translate([1,-1.25,-0])square([42.5,42],center=true);
if(w2==false){rotate([0,0,90])translate([15,0,0]) circle(5);}

}translate([-1,-22.5,-0])square([42.5,5],center=true);

if(w==false){hull(){
translate([30,0,0]) circle(4.65);
*translate([20,0,0]) circle(1);}}
}}
translate([-0.25,0.25,0]){

if(w==false){
if(g==false){intersection(){
linear_extrude(50, convexity = 5){
polygon([[20,-20],[20,19.5],[22.5,22],[25,19.5],[25,-20],[22.5,-22.5]]); }
 union() {
translate([10,-25,0])cube([25+20,25+25,25+5]);
translate([25,0,25+5])scale([1,1.5,1])rotate([0,90,0])cylinder(30,12.5+3,12.5+3,center=true,$fn=40);}
}}
else {difference(){

intersection(){linear_extrude(50, convexity = 5){
polygon([[20,-20],[20,19.5],[22.5,22],[25,19.5],[25,-20],[22.5,-22.5]]); }
 union() {
translate([10,-25,0])cube([25+20,25+25,25+5]);
translate([25,0,25+5])scale([1,1.5,1])rotate([0,90,0])cylinder(30,12.5+3,12.5+3,center=true,$fn=40);}}

union() {
translate([10,-12.5,5])cube([25,25,25]);
translate([25,0,25])rotate([0,90,0])cylinder(30,12.5,12.5,center=true);
}
}}}

if(i==false){
intersection(){
linear_extrude(50, convexity = 5)rotate([0,0,-90])
polygon([[20,-20],[20,20],[22.5,22.5],[25,20],[25,-20],[22.5,-22.5]]); union() {
 translate([-25,-30,0])cube([50,25,35]);
translate([0,-25,25+5])scale([1.5,1,1])rotate([90,0,0])cylinder(30,12.5+3,12.5+3,center=true,$fn=40);}

}}
else {difference(){

intersection(){
linear_extrude(50, convexity = 5)rotate([0,0,-90])
polygon([[20,-20],[20,20],[22.5,22.5],[25,20],[25,-20],[22.5,-22.5]]); union() {
 translate([-25,-30,0])cube([50,25,35]);
translate([0,-25,25+5])scale([1.5,1,1])rotate([90,0,0])cylinder(30,12.5+3,12.5+3,center=true,$fn=40);}

}
rotate([0,0,-90])union(){
translate([10,-12.5,5])cube([25,25,25]);
translate([25,0,25])rotate([0,90,0])cylinder(30,12.5,12.5,center=true);}
}}
}}}
function rnd(a = 0, b = 1) = (rands(min(a, b), max(a, b), 1)[0]);
function dice()=round (rnd(2+Open))==1?false:true;
function rndc() = [rands(0, 1, 1)[0], rands(0, 1, 1)[0], rands(0, 1, 1)[0]];
```


[User]
I'm looking to design a custom enclosure for a rocker power switch for my MakerFront i3Pro 3D printer. The printer originally didn't have a power switch, so I'm adding one. I've got a specific switch in mind, and I need the enclosure to fit flush against it, so I can't accidentally engage the switch's self-locking mechanism after it's installed. Also, I want the enclosure to mount to the printer's left z-axis motor, sort of wrapping around it a bit.

Here are the key dimensions and design requirements I'm working with:

*   **Switch Cavity:**
    *   Inner width (ix): 28.5 mm
    *   Inner depth (iy): 13.9 mm
*   **Wall Thickness (wt):** 1.3 mm
*   **Motor and Bracket Height (mbz):** 43.14 mm
*   **Front and Side Clearance Below Motor (mz):** 5 mm. This means the enclosure should sit 5mm lower than the motor in front and on the sides, to cover the wires and such.
*   **Back Clearance Below Motor (mzb):** 1 mm. The back should be a little less low than the front and sides.
*   **Screw Hole Spacing (hx):** 31 mm (center to center).
*   **Overhang into motor mount bracket (mby):** 10 mm. The box extends into the motor mount bracket for stability.
*  **Overhang width on left side (mbx):** 40 mm. The box goes around the motor for attachment.

The enclosure should have:

*   A top plate extending into the motor mount area on the left, flush with the z motor mounting bracket.
*   An opening for the switch.
*   Screw holes to mount to the printer frame.
*   A cut out at the bottom so it can be slid down the wire.

I'd like the entire design to be parametric in OpenSCAD, so I can easily adjust these dimensions later if needed. I would also like the OpenSCAD code to be well commented and well organized. I'd like a module for the top plate, the left triangular cut out, the right triangular cut out, the screw holes, the bottom cut out and the main body itself.

I'm imagining the final design being created using a `difference()` with a `union()` and several `cube()` and `linear_extrude()` commands, as well as using a `translate()` and a `rotate()` to help with placement.

Could you please generate the necessary OpenSCAD code for this? Thanks!

[Assistant]
MakerFrontPowerButtonBox.scad:
```
//by Eric Hughes specifically for a MakerFront i3Pro
//circle precision
$fn=20;
//inner switch width
ix=28.5;//28.3
//inner switch depth
iy=13.9;
//wall thickness
wt=1.3;//1.2
//motor + bracket height
mbz= 43.14;
//customize height lower than motor in front and sides (covers wires, zero if flush with motor)
mz = 5;
//customize height lower than motor in back (zero if flush)
mzb = 1;
//distance between both scres holes
hx=31;
//overhang into motermount bracket
mby=10;
//overhang into motermount bracket
mbx=40;

rotate([0,180,0]){
difference(){
    union(){
        TopPlate();
        cube([2*wt+ix, 2*wt+iy, mbz+mz], center=true);
    }
    cube([ix, iy, 100], center=true);
    LeftTriangleCut();
    RightTriangleCut();
    screwholes();
    BottomCut();
}}

module BottomCut(){
    translate([0,wt+.01,-50-(mbz+mz)/2+(mz-mzb)])
     cube([ix, iy, 100], center=true);
}

module TopPlate(){
translate([0,mby/2,(mbz+mz+wt)/2]){
cube([mbx,mby+2*wt+iy, wt],center=true);}}

module RightTriangleCut(){
linear_extrude(height=100, center=true){
 polygon(points=[[-(2*wt+ix)/2,-(2*wt+iy)/2-.001],[-(mbx)/2-.001,-(2*wt+iy)/2-.001],[-(mbx)/2,(2*wt+iy)/2]]);}}

module LeftTriangleCut(){
    mirror([1,0,0]) RightTriangleCut();
}

module screwholes(){
translate([0,(2*wt+iy)/2+5.25,]){
translate([hx/2,0,0]){
 cylinder(100, d=3.2, center=true);
}
translate([-hx/2,0,0]){
 cylinder(100, d=3.2, center=true);
}}}
```


[User]
I need a parametric OpenSCAD design for an oscilloscope probe holder that's compatible with the PCB Workstation with Articulated Arms (the one from Thingiverse). Basically, I want to be able to measure my probes and input those measurements to generate a custom holder.

The holder needs to have two grippers, one on the bottom and one on the top, to secure the probe. I need to be able to define the diameter and height of each gripper individually, as well as the gap between them. There should also be an offset from the central shaft and an angle for the gripper opening. The gripper walls also need to be parametric.

The holder also needs a shaft that the grippers attach to and a ball cap that connects to the articulated arm. The shaft diameter needs to be parametric, as well as the ball diameter and wall thickness. The ball cap also has slots around it for flexibility and these should also be parametric (slot length, number of slots).

I don't have any prior code to share, so you'll be creating everything from scratch. Please provide the design in OpenSCAD, and let me know if you have any questions. It looks like the original design didn't use any libraries, but feel free to use one if it makes sense for this.

[Assistant]
holder_oscilloscope.scad:
```
/*
 * Oscilloscope probe holder for PCB Workstation with Articulated Arms
 *   (http://www.thingiverse.com/thing:801279)
 *
 * Author: Scott Shumate <scott@shumatech.com>
 */

// Diameter of the top gripper (mm)
top_gripper_diameter = 8.5;

// Height of the top gripper (mm)
top_gripper_height = 4;

// Diameter of the bottom gripper (mm)
bottom_gripper_diameter = 6.7;

// Height of the bottom gripper (mm)
bottom_gripper_height = 2;

// Gap between the two grippers (mm)
gripper_gap = 7;

// Offset of the grippers from the shaft (mm)
gripper_offset = 13;

// Angle of the gripper opening (degrees)
gripper_angle = 30;

// Thickness of the gripper walls (mm)
gripper_wall_thickness = 2;

// Diameter of the gripper shaft
gripper_shaft_diameter = 8.5;

// Diameter of the ball (mm)
ball_diameter = 10.5;

// Thickness of the wall around the ball (mm)
ball_wall_thickness = 1.6;

// Length of the slots in the ball cap (mm)
slot_length = 6.5;

// Number of slots around the ball cap
number_of_slots = 3;

$fa=1;
$fs=0.5;

create();

module create() {
    total_gripper_height=bottom_gripper_height+gripper_gap+top_gripper_height;

    // Ball cap
    translate([0, 0, total_gripper_height+0.75*ball_diameter])
        rotate([180, 0, 0])
            ball_cap(ball_diameter, ball_wall_thickness, slot_length, number_of_slots);

    // Griper Shaft
    cylinder(d=gripper_shaft_diameter, h=total_gripper_height);

    //Bottom Gripper
    translate([gripper_offset, 0, 0])
        difference() {
            union() {
                cylinder(d=bottom_gripper_diameter+2*gripper_wall_thickness, h=bottom_gripper_height);
                translate([-gripper_offset, -gripper_shaft_diameter/2, 0])
                    cube([gripper_offset, gripper_shaft_diameter, bottom_gripper_height]);
            }
            union() {
                translate([0, 0, -bottom_gripper_height/2]) {
                    cylinder(d=bottom_gripper_diameter, h=2*bottom_gripper_height);
                    translate([-bottom_gripper_diameter/2,0,0])
                        triangle(2*bottom_gripper_diameter, 2*bottom_gripper_height, gripper_angle);
                }
            }
        }

    //Top Gripper
    translate([gripper_offset, 0, bottom_gripper_height+gripper_gap])
        difference() {
            union() {
                cylinder(d=top_gripper_diameter+2*gripper_wall_thickness, h=top_gripper_height);
                translate([-gripper_offset, -gripper_shaft_diameter/2, 0])
                    cube([gripper_offset, gripper_shaft_diameter, top_gripper_height]);
            }
            union() {
                translate([0, 0, -top_gripper_height/2]) {
                    cylinder(d=top_gripper_diameter, h=2*top_gripper_height);
                    translate([-top_gripper_diameter/2,0,0])
                        triangle(2*top_gripper_diameter, 2*top_gripper_height, gripper_angle);
                }
            }
        }

}

module triangle(diameter, height, angle)
{
    cd = diameter * cos(angle);
    sd = diameter * sin(angle);

    linear_extrude(height=height)
        polygon([[0,0],[cd,sd],[cd,-sd],[0,0]]);
}

module ball_cap(ball_diameter, wall_thickness, slot_length, number_of_slots)
{
    difference() {
        // Cap body
        translate([0, 0, ball_diameter/4])
            sphere(d=ball_diameter+2*wall_thickness);

        union() {
            // Bottom ball
            translate([0, 0, ball_diameter/4])
                sphere(d=ball_diameter);

            // Bottom bevel
            translate([0, 0, -1.7])
                cylinder(r1=(ball_diameter+wall_thickness)/2+1, r2=0, h=(ball_diameter+wall_thickness)/2+1);

            // Bottom trim
            translate([0, 0, -ball_diameter/2])
                cylinder(d=2*wall_thickness+ball_diameter, h=ball_diameter/2);

            // Slots
            for (i = [1:number_of_slots])
                ball_slot(slot_length, ball_diameter, i*360/number_of_slots);
        }
    }
}

module ball_slot(slot_length, slot_height, angle)
{
    rotate([-90, 0, angle]) {
        translate([0, -slot_length, 0])
            ball_teardrop(1.25*slot_length, slot_height);
    }
}

module ball_teardrop(length,height)
{
    r1=0.15*length;
    x1=0;
    y1=r1;

    r2=0.45*length;
    x2=0.5*length;
    y2=length-r2;

    t12 = tangent(x1, y1, r1, x2, y2, r2, -1, -1);
    t23 = tangent(x2, y2, r2, -x2, y2, r2, 1, -1);
    t31 = tangent(-x2, y2, r2, x1, y1, r1, -1, 1);

    linear_extrude(height=height) {
        difference() {
            union() {
                translate([x1,y1]) circle(r=r1);
                polygon([t12[0], t12[1], t23[0], t23[1], t31[0], t31[1]]);
            }
            union() {
                translate([x2,y2]) circle(r=r2);
                translate([-x2,y2]) circle(r=r2);
            }
        }
    }
}

// Calculate the tangent points between two circles
function tangent (x1, y1, r1, x2, y2, r2, s1, s2) =
    let (
    d = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)),
    vx = (x2 - x1) / d,
    vy = (y2 - y1) / d,

    c = (r1 - s1 * r2) / d,
    h = sqrt(1.0 - c*c),

    nx = vx * c - s2 * h * vy,
    ny = vy * c + s2 * h * vx,

    xt1 = x1 + r1 * nx,
    yt1 = y1 + r1 * ny,
    xt2 = x2 + s1 * r2 * nx,
    yt2 = y2 + s1 * r2 * ny
    )
    [[xt1,yt1],[xt2,yt2]];

```


[User]
Hi there! I'm looking to create a customizable box with a sliding lid, and I'd like to be able to specify dimensions and text on all faces. I'd like the design to be parametric so I can easily adjust things.

Here are the specific features I'd like in the OpenSCAD design:

*   **Box Dimensions:** I need to be able to control the width, height, and depth of the box. I'd also need to specify the wall thickness of the box, and the thickness of the sliding cover.
*   **Sliding Cover:** The cover should slide into the box, and I'd like a small margin to account for printer error.
*   **Text Customization:** I want to be able to add text to each face of the box (front, back, two sides), and also to the cover. I need to be able to control the size of the text, and I'd like the text on the sides to automatically adjust depending on whether I have one or two lines of text.
*   **Error Handling:** It'd be great to have the option to switch between a mode that adds an error margin in the cover's dimensions for printing, and a mode where no error is added for rendering purposes.
*   **Ease of Use:** I'd like to have the text aligned as well as possible, such as centering on the faces as much as possible.
*   **Units:** I'm working in millimeters.

I don't need any complex features, just a solid, functional, and parametric box. I'm not using any external libraries and I'm not looking for the code to interface with external files or other programs, just pure OpenSCAD. Could you create the necessary .scad file(s) for me?

[Assistant]
ScrewBoxV1.scad:
```
/* [Geral] */
Print = "True"; // [True,False]
// Printer XY Dimensional Error
printerErrorMargin = 0.5;
/* [Dimensions] */
// Box side size (Y)
boxSizeWidth  = 55;
boxSizeHeight = 20;
// Face of tthe box (where cover slides in) (X)
boxSizeDepth  = 35;
boxWallThickness = 1.5;
coverThickness = 2;
/* [Text] */
TextLateral1 = "Allen Screw";
TextLateral2 = "Metric 3x10";
// Zero to AUTO
LateralFontSize=7;
TextFront = "";
TextBack = "";

TextCover1 = " A M3x10";
TextCover2 = "";
TextHeight = 0.4;

/* [HIDDEN] */
$fn = 50;
diffMargin = 0.01;

if(Print == "True"){
    box();
    translate([-boxSizeDepth-1,0,0])
        cover();
}else{
    box();
    translate([printerErrorMargin/2,0,
               boxSizeHeight-coverThickness])
        cover();
}

module box(){
    bwt = boxWallThickness;
    difference(){
        minkowski(){
            translate([0.5,0.5]) cube([boxSizeDepth-1,boxSizeWidth-1,boxSizeHeight-1]);
            cylinder(d=1,h=1);
        }

        translate([bwt,bwt,bwt])
            cube([boxSizeDepth-bwt*2,
                  boxSizeWidth-bwt*2,
                  boxSizeHeight+bwt*2]);
        translate([bwt,
                   -2*diffMargin,
                   boxSizeHeight-coverThickness
                   +diffMargin]){
            cube([boxSizeDepth-bwt*2,bwt*2,
                  coverThickness]);
        }
        d=coverThickness*2/3;
        h=boxSizeWidth+2*diffMargin-boxWallThickness;
        translate([boxWallThickness,-2*diffMargin,
                   boxSizeHeight-coverThickness+d/2])
            coverCutCylinder(d,h);
        translate([boxSizeDepth-boxWallThickness,
                   -2*diffMargin,
                   boxSizeHeight-coverThickness+d/2])
            coverCutCylinder(d,h);
    }
    boxText();
}
module boxText(){
    // Frente e Fundo
    if(len(TextFront) > 0) translate([1,0,5]) CreateText(TextFront,boxSizeHeight/3,90,0,0);
    if(len(TextBack) > 0)
        translate([boxSizeDepth,boxSizeWidth,6])
            CreateText(TextBack,boxSizeHeight/3,90,0,180);
    // Lateral
    yOffset = 1;
    lateralFont = LateralFontSize > 0? LateralFontSize:(boxSizeHeight/2)-2;
    if(len(TextLateral1) > 0 && len(TextLateral2) > 0) {
        translate([0,boxSizeWidth-yOffset,boxSizeHeight/2])
            CreateText(TextLateral1,lateralFont,90,0,-90);
        translate([boxSizeDepth,yOffset,boxSizeHeight/2])
            CreateText(TextLateral1,lateralFont,90,0,90);
    }
    if(len(TextLateral1) > 0 && len(TextLateral2) == 0) {
        translate([0,boxSizeWidth-yOffset,boxSizeHeight/3])
            CreateText(TextLateral1,lateralFont,90,0,-90);
        translate([boxSizeDepth,0,boxSizeHeight/3])
            CreateText(TextLateral1,lateralFont,90,0,90);
    }
    if(len(TextLateral2) > 0){
        translate([0,boxSizeWidth-yOffset,1])
            CreateText(TextLateral2,lateralFont,90,0,-90);
        translate([boxSizeDepth,yOffset,1])
            CreateText(TextLateral2,lateralFont,90,0,90);
    }

}
module cover(){
    bwt = boxWallThickness;
    h=boxSizeWidth-bwt-printerErrorMargin;
    x=boxSizeDepth-bwt*2-printerErrorMargin;
    translate([bwt+0,0,0]){
        cube([x,
              h,
              coverThickness]);
        d=coverThickness*2/3;
        translate([0,-2*diffMargin,d/2])
            coverCutCylinder(d,h);
        translate([boxSizeDepth-bwt*2-printerErrorMargin,
                   -2*diffMargin,d/2])
            coverCutCylinder(d,h);
    }
    size = x/2;
    if(len(TextCover2) > 0){
        translate([x-2,1,coverThickness])
            CreateText(TextCover2,(size/2),0,0,90);
        translate([x/2-2,1,coverThickness])
            CreateText(TextCover1,(size/2),0,0,90);
    }else{
         translate([2*x/3,1,coverThickness])
            CreateText(TextCover1,(size/2),0,0,90);
    }
}
/* Aux Functions */
module coverCutCylinder(d,h){
    rotate(a=[-90,0,0])
        cylinder(d=d,
                 h=h);
}
module CreateText(Text, Size, RotX, RoY, RotZ){
    color("blue")
    rotate(a=[RotX, RoY, RotZ])
        linear_extrude(height=TextHeight)
            text(Text,Size);
}

```


[User]
I'd like to create a customizable OpenSCAD design for making plugs, caps, or couplers for square and rectangular tubes. I've seen some existing designs but they don't quite have all the features I need and I want to start from scratch to really understand how it's put together. Here are the key requirements:

**Core Functionality:**

*   **Shape:** The design should handle both square and rectangular tubes. The user should be able to specify the outside dimensions of the tube (X and Y dimensions). If the Y dimension is zero, the tube should be treated as square.
*   **Wall Thickness:** The user should be able to specify the wall thickness of the tube.
*   **Corner Rounding:** I need an option to round the corners of the cap/plug/coupler. The user should be able to control the amount of rounding with a parameter, from none to maximum rounding.
*   **Cap/Plug Length:** The user should be able to control the overall length of the exposed portion of the cap/plug or the length of the coupler in the Z dimension.
*   **Cap Taper:** For caps/plugs, I'd like to have the option to taper the exposed portion of the plug. The user should be able to specify what percentage of the cap will have straight sides versus the tapered portion, and the angle of the taper should remain constant. This feature would not apply to couplers.
*   **Recess/Hollow:** The cap or plug should have an option to be recessed from the underside. The user should be able to specify the percentage of the cap that is recessed, meaning what percentage of the cap is hollow underneath. For a coupler, the hollow should go all the way through, with the percentage controlling the size of the through-hole.
*   **Build Orientation:** There should be an option to build in a vertical or horizontal orientation. The default orientation should be vertical (tabs facing up) as this is typically easiest to print without supports. The horizontal orientation should orient the piece on a corner, as this minimizes supports for square pieces.
*   **Units:** Ideally the input units should be in mm, and the design should always output in mm.

**Retaining Tabs:**

*   **Tab Size:** The retaining tabs should have a size based on the tube size. Something like 2/3 the square root of the average of x and y dimensions should work well.
*   **Tab Grooves:** There should be an option to add cross-grooves to the retaining tabs.
*   **Tab Ribs:** I also need an option to add lengthwise ribs to the retaining tabs, perhaps as an alternative or in addition to grooves.
*   **Tip Bevel:** The tips of the retaining tabs should have a slight bevel to ease insertion into the tube.
*   **Tab Angle:** The angle of the tabs might need to vary based on the size of the tube (perhaps tilted out slightly), but this isn't critical.
*   **Coupler Tabs:** For couplers, the retaining tabs should effectively be on both sides. These tabs should also have some extra base/reinforcement.

**Coupler Specifics:**

*   **Hollow:** Coupler should have the "hollow" completely pierce the through the part.
*   **Tabs:** Couplers should have tabs on both sides, with reinforcement.
*   **No Taper:** Couplers shouldn't have the taper option enabled.

**Additional Needs:**

*   **Error Handling:** I would like some basic error handling, specifically for things like internal dimensions being too small (when desired tab size is too large for the opening) and bad or missing data in the Customizer.
*   **Build Plate:** An optional build plate can be displayed, with options for common printer sizes like the Replicator 2 and Thingomatic. Also a custom size should be supported.

I'd also like to minimize the use of linear extrude as it is somewhat inefficient.

I do not need any custom libraries, so all code should be self contained. I would like to have a single file generated if possible but multiple files will be fine.

Essentially, I'm trying to recreate similar functionality to the code I've attached in the square\_plug\_generator\_v2.scad file, but from the ground up. Let me know if you need any clarification!

[Assistant]
square_plug_generator_v2.scad:
```
// OpenSCAD file to create custom sized plugs/caps or couplers for square and rectangular tubes.
// Setup for use with Thingiverse.com Customizer.
//
// preview[view:south west, tilt:bottom diagonal]
//
// Original code created Aug 14, 2016 by Allan M. Estes
// Rewritten to clean up, add options, and to allow production of joiners
// Rewrite "completed" Sep 6, 2016, other changes:
//      revised tab grooves and implemented bottom groove/relief differently
//      thicker and wider tabs
//      tab angle varies with tab length,
//      optional vertical/horizontal build positions
//      option to put "ribs" on tabs (& combine with grooves)
//      revision on recess calculation
//      clean up typos and small error where tubeX was used instead of tubeY
//      some differences tweaked to reduce false surfaces in preview
//      more meaningful message for bad_ratio error: interior dimensions too small
//          (generated because desired tab size is too large for opening)
//
// to do:
//   [x]keep constant angle on cap taper
//   (allow control of taper angle?)
//   [x]display on build surface w/on-off option
//   [x]control view
//   [x]more sanity checks?
//   [x]possibly angle retaining tabs out a degree or two
//   [x]adjust tilt on long tabs
//   (allow control of tab angle?)
//   [x]base tab size on tube size (like 2/3*sqrt((x+y)/2) )
//
//   [x]adapt to optionally create couplers:
//      taper does not apply
//      hollow(recess) goes through (hpctz = 1.01 or so)
//      (future: hollow could always be rounded - possibly in both cap and coupler)
//      tabs are effectively generated on both sides of cap
//      reinforcement/base for tabs (future: optional/adjustable?)
//
//   future: may want option to enter tube inside dimension and "add" wall thickness
//
// Note: the +0 added to some assignments is to keep item from showing up in Thingiverse
// Customizer.

$fn=36+0;

// Following are parameter which are configurable through the customizer

// input_units exists to allow mm or inch values to be entered through customizer
// it is currently disabled and set to mm input;
// Units for customizer input (output is always mm)
input_units = 1+0; // [1:mm, 25.4:inch]

// What to produce?
build_item = 0; // [1:cap/plug, 0:coupler]

// Outside measurement of tube end (X direction)
tube_x = 25.54;
// Outside measurement of tube end (Y direction) 0 = same as X (square tube)
tube_y = 0;
// How thick is tube wall
tube_wall = 1.67;
// Amount of rounding to apply to corners (0=no rounding, 50=default rounding, 100=maximum rounding)
corner_rounding = 50; // [0:100]

// Length of external portion of cap/coupler
cap_length = 3;
// Portion of plug with straight sides (percentage), does not affect coupler
straight_pct = 50; // [0:100]
// Add cross grooves to retaining tabs?
groove_tabs = 1; // [0:no, 1:yes]
// Add length-wise ribs to retaining tabs?
ribs = 0; // [0:no, 1:yes]
// Percent cap will be recessed underneath (none to maximum), size of through hole in coupler
recess_pct = 100; // [0:100]
// Build orientation, vertical cap won't need supports, horizontal might make stronger piece
orientation = 1; // [0:vertical, 1:horizontal]

// user control of viewable build plate in Customizer:
// Turn on viewable build plate?
display_build_plate = 0; // [0:no, 1:yes]
use <utils/build_plate.scad>
// For display only, doesn't contribute to final object
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
// When Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 100; //[100:400]
// When Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 100; //[100:400]

// sanitize/convert parameters:
cap = build_item;
horz = orientation;

tubeY = abs((tube_y==0) ? tube_x : tube_y) * input_units;
tubeX = abs(tube_x) * input_units;
tubeWall = abs(tube_wall) * input_units;
capLen = abs(cap_length) * input_units;

fillet = tubeWall * (corner_rounding/25); // 0:4, default=2
taperpct = cap ? 1 - (straight_pct/100) : 0 ;

shorter = min(tubeX,tubeY);
tabThck = .7*sqrt((tubeX+tubeY)/2);
tabLen = (tubeX+tubeY)/2/3;

// flag problems for bad data, used later to generate error "messages"
bad_cap = capLen<2 || capLen>shorter;	// too short or long (long is a bit arbitrary)
bad_wall = tubeWall<1;				// too thin
bad_ratio = shorter<2*tubeWall+3*tabThck;	// internal dimensions too small
bad_over = tubeX>800 || tubeY>800;	// really determined by build plate
									// it won't break the program but why bother
bad_input = tubeX==undef || tubeY==undef || tubeWall==undef || capLen==undef;

// Module definitions start here

// generic modules to copy and move (translate) an object or to copy and rotate
module duplicate_t(v) {
    children();
    translate(v) children();
}
module duplicate_r(v1,v2) {
    children();
    rotate(v1,v2) children();
}
// prism routine straight out of OpenSCAD online manual
module prism(l, w, h) {
   polyhedron(
           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]]
           );
}

// Modules to deal with exposed/visible portion of the cap/plug -
// Cap is generated from a flat square/rectangle with optionally rounded corners which is
// extruded into the final shape. A portion (from none to all) of the cap can be tapered
// by extruding with scaling (the angle is currently fixed at 45 degrees). By default
// the underside is recessed, but the amount is controllable and can be reduced down to
// zero.

// generate 2d outline used to build cap
module cap_outline() {
    if (2*fillet>shorter) {
        circle(fillet);
    } else if (fillet>0) {
        minkowski() {
            square([tubeX-(2*fillet),tubeY-(2*fillet)], true);
            circle(fillet);
        }
    } else
        square([tubeX,tubeY], true);
}

// hollow/recess cap by difference with a scaled down clone - hollow should fit within
// tabs and not make the cap too thin to support itself (though if doing a coupler the
// hollow goes all the way through and percentage controls size of opening).
module hollow() {
    // keep recess within tabs (2.0* would be flush with tabs, 2.1* adds a tiny edge)
    hpctx = (tubeX-2.1*(tubeWall+tabThck))/tubeX;
    hpcty = (tubeY-2.1*(tubeWall+tabThck))/tubeY;
    // recess along Z axis - maximum should be 85% or whatever percent keeps the thickness
    // greater than or equal to 1.25 mm
    hpct = (capLen-max(.15*capLen,1.25))/capLen*(recess_pct/100);
    hpctz = cap ? hpct : 1.01;
    xyreduce =  cap ? .75 + .25*recess_pct/100 : recess_pct/100;
    difference() {
        children();
        translate([0,0,-.001]) scale(v=[hpctx*xyreduce,hpcty*xyreduce,hpctz]) children();
    }
}

// create cap
module build_cap() {
    hollow()
    union() {
        if (taperpct>0) {
            translate([0,0,capLen*(1-taperpct)-.001])
            // scale is based on length to maintain a 45 deg angle - could be adjustable
            linear_extrude(height=capLen*taperpct, center=false,
                           convexity=10, scale=1-(capLen*taperpct/(shorter/2)))
            children();
        }
        if (taperpct!=1) {
            linear_extrude(height=capLen*(1-taperpct), center=false, convexity=10)
            children();
        }
    }
}

// Modules to deal with retaining tabs -
// Grooved tabs are created by building a 2D profile and extruding to "width". Ribbed tabs
// are created from a cube + cylinders. Ribs and grooves are optional. The tab tip is
// beveled to ease insertion into the tube. The tip bevel is only generated in the tab_grooves
// module, but an intersection of the two tab design preserves the tip bevel while allowing
// the groove and rib options to be combined. The resulting combined tab has the ends beveled
// for clearance. Tabs for X and Y directions are created, duplicated and moved into position

// tab w/tip bevel and (optional) cross grooves
module tab_grooves(size) {
//    sp = (tabLen-.75*tabThck-tabThck/2.5 )/3; // spacing for grooves
    sp = (tabLen-.75*tabThck-tabThck/5 )/3; // spacing for grooves

    linear_extrude(height=size,center=false)
    difference() {
        polygon([ [0,0], [tabLen-.75*tabThck,0], [tabLen,.6*tabThck],
                  [tabLen,tabThck], [0,tabThck] ]);
        if (groove_tabs) {
            for (i=[0:2]) {
                translate([tabLen-.75*tabThck-sp/2-i*sp,-sp/3.0])
                circle(sp*.6);
            }
        }
    }
}

// tab w/(optional) vertical ribs
module tab_ribs(size) {
    cnt=floor(size/tabThck/2) + 1;  // how many ribs to produce
    sp = (size-tabThck*2/3)/cnt; // spacing for ribs

    translate([tabLen,tabThck/3,0])
    rotate([0,-90,0]) {
        cube([size,tabThck*2/3,tabLen]);
        if (ribs) {
            for (i=[0:cnt]) {
                translate([tabThck/3+i*sp,0,0])
                cylinder(h=tabLen,r=tabThck/3);
            }
        } else {
            translate([0,-tabThck*2/3,0])
            cube([size,tabThck*2/3,tabLen], center=false);
        }
    }
}

// Module to produce base/reinforcement for tabs (currently only used for couplers).
// Base is simplification of a ribbed tab which is not beveled at ends/edges allowing the
// bases to join in the corners for extra strength
module tab_base(baseLen,size) {
    cnt=floor(size/tabThck/2) + 1;  // how many ribs to produce
    sp = (size-tabThck*2/3)/cnt; // spacing for ribs

    translate([baseLen,tabThck/3+.01,0]) // .01 offset for looser fit
    rotate([0,-90,0]) {
        cube([size,tabThck*2/3,baseLen]);
        for (i=[0:cnt]) { // columns/ribs
            translate([tabThck/3+i*sp,0,0])
            cylinder(h=baseLen,r=tabThck/3);
        }
        translate([0,-tabThck/3,0])
        prism(size, tabThck/3, tabThck/3);
    }
}

// create tab by combining optional styles, beveling ends, and tilting outward a little
module build_tab(size) {
    tabTilt  = min(tubeWall/tabLen/3, .04); // used to be sin(1.5)
    multmatrix([ [1,0,0,0], [-tabTilt,1,0,0], [0,0,1,0], [0,0,0,1] ]) // tilt
    // bevel ends by difference with rotated cubes
    difference() {
        intersection() { // combine the optional tab styles
            tab_grooves(size);
            tab_ribs(size);
        }
        duplicate_t([0,0,size])
        translate([tabLen/2,tabThck,0])
        rotate([45,0,0])
        cube([tabLen*1.1,tabThck,tabThck], true);
    }
}

// generates and places the four retaining tabs
module place_tabs(base) {
    tabWidX = tubeX-2*tubeWall-(tabThck*1.25);
    duplicate_r(180)
    translate([tabWidX/-2,tubeY/-2+tubeWall,-base])
    rotate([0,90,0]) {
        if (base) {
            translate([-base,0,0])
            tab_base(base,tabWidX);
        }
        build_tab(tabWidX);
    }

    tabWidY = tubeY-2*tubeWall-(tabThck*1.25);
    duplicate_r(180)
    translate([tubeX/-2+tubeWall,tabWidY/2,-base])
    rotate([90,90,0]) {
        if (base) {
            translate([-base,0,0])
            tab_base(base,tabWidY);
        }
        build_tab(tabWidY);
    }
}

// Create cylinders to "subtract" from corner formed by cap and base/tab. The intent here
// is to allow the cap/coupler to sit better against tube even if there is some irregularity
// in the cap printing and/or tube end. More relief should probably be done in the corners,
// but this is a start.
module corner_relief(zOffset) {
    sideAdj = sign(-zOffset); // if positive offset adjust for top of cap
    reliefX = tubeX-2*tubeWall-(tabThck*1.25)+.01;
    duplicate_r(180)
    translate([reliefX/-2-tabThck/16,tubeY/-2+tubeWall,sideAdj*-tabThck/16+zOffset])
    rotate([0,90,0])
        cylinder(h=reliefX,d=tabThck/4);

    reliefY = tubeY-2*tubeWall-(tabThck*1.25)+.01;
    duplicate_r(180)
    translate([tubeX/-2+tubeWall-tabThck/16,reliefY/2,sideAdj*-tabThck/16+zOffset])
    rotate([90,90,0])
        cylinder(h=reliefY,d=tabThck/4);
}

// Position object for build plate - vertical vs horizontal
// horizontal orientation has piece rotated to be on "corner" to minimize support needs.
module build_position(zShift) {
    if (horz) {
        translate([0,0,sin(45)*(tubeX+tubeY-4*fillet)/2+fillet]) rotate([90,45,0])
        children();
    } else {
        translate([0,0,zShift])
        children();
    }
}

// End of Modules

// Main "code" starts here, first portion deals with customizer errors. To be a little
// more user friendly, generate a "stop sign" with text describing error. In the event
// of multiple errors, just give a single most significant error.

s = 10 + 0; r=s*1.414+s; // points to create octagon
if (bad_input || bad_cap || bad_wall || bad_ratio || bad_over) {
    rotate([90,0,0]) {
        color("red")
        translate([0,0,-.01])
        polygon([[r,s],[s,r],[-s,r],[-r,s],[-r,-s],[-s,-r],[s,-r],[r,-s]]);
        text("Bad Parameters",halign="center",valign="bottom");
        if (bad_input) {
            text("Missing or non-numeric data",halign="center",valign="top");
            //echo("Missing or non-numeric data");
        } else if (bad_ratio) {
            text("Internal dimensions too small",halign="center",valign="top");
            //echo("Internal dimensions too small");
        } else if (bad_wall) {
            text("Wall too thin",halign="center",valign="top");
            //echo("Wall too thin");
        } else if (bad_cap) {
            text("Bad Cap Length",halign="center",valign="top");
            //echo("Bad Cap Length");
        } else if (bad_over) {
            text("Overly large Tube dimensions",halign="center",valign="top");
            //echo("Overly large Tube dimensions");
        }
    }
} else {

    // If all is well, build the cap/plug or joiner ...

    if (display_build_plate)
        build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

    if (cap) { // building a cap/plug
        build_position(capLen) // position object for build plate
        rotate([0,180,0]) // flip over so item will be oriented cap down in vertical orientation
        difference() {
            union() {
                build_cap() cap_outline();
                place_tabs(0);
            }
           corner_relief(0);
        }

    } else { // building a joiner
        // joiner is effectively cap with retaining tabs on both sides - taper does not apply,
        // "hollow" goes all the through "cap" portion, and tabs will have a "base" adding
        // length and strength.
        build_position(capLen/2+tabLen*2) // position object for build plate
        difference() {
            union() {
                translate([0,0,-capLen/2])
                build_cap() cap_outline();
                duplicate_r([0,180,0])
                translate([0,0,-capLen/2])
                place_tabs(tabLen);
            }
           corner_relief(capLen/2);
           corner_relief(-capLen/2);
        }
    }
}
```


[User]
Hi! I've got a Nalgene wide-mouth water bottle and the handle broke. I'm looking for a replacement and I'd like to be able to customize a few things, so I was hoping you could help me design a parametric OpenSCAD file for it.

Here's what I'm thinking:

*   **Handle Design:** It needs to clip onto the bottle's cap. I'd like the handle itself to be a rounded shape, like a half-circle or a thick ring.

*   **Key Parameters:**
    *   **Gap:** The distance between the clip and the cap should be adjustable. Let's start with a default of 1mm and make it adjustable between 0 and 4 mm.
    *   **Mount Thickness:** The thickness of the part of the clip that touches the cap (the mounting plate) needs to be adjustable too.  A default of 5mm would be good, and an adjustment range of 5 to 10mm.
    *   **Clip Cover:** I'd also like the option to add a cover to the clip, to make it more of a permanent attachment. It should be a simple on/off option.

*  **Dimensions:**
    *   **Cap Diameter:** The cap's diameter without the ridges is about 56mm (so a radius of 28mm). The main cap height is 16.7mm.
     * **Cap Detail:** There are three sections to the cap (a smaller, flat cylinder, a larger, flat cylinder and a final, smaller flat cylinder at the end). Respectively, their diameters are 6.6 + 2.3\*2 mm (radius of 5.6mm), 19 mm (radius of 9.5mm) and 6.6+2.3\*2 mm (radius of 5.6mm) with heights of 2mm, 2mm and 1mm. The first cylinder is recessed and is contained entirely within the main cylindrical cap shape (of height 16.7mm).
    *   **Clip Dimensions:** The clip needs to fit snugly around the cap, so maybe make it a bit larger than the cap diameter. The clip is essentially made up of three sections, a first section 2mm high, a second section 2 mm high, and a final section 1mm high, with a total height of 5mm.
    *    **Handle Dimensions**: The handle should be sized to be comfortable to hold, a default diameter of 52mm or so would be fine. The thickness of the handle should default to about 4mm.

*  **Libraries:**
    * I'd really like to make use of the BOSL2 library for more efficient and powerful development.

So, basically I need a customizable handle that clips onto the Nalgene cap and lets me adjust the fit, thickness, and add a cover. Can you help me with that? Thanks!

[Assistant]
nalgene_wide_mouth_handle.scad:
```
//Handle Replacement for Nalgene Wide Mouth
//by Khairulmizam Samsudin, Sept 2016
//xource@gmail.com
//"nalgene_wide_mouth_handle" is licenced under Creative Commons :
//Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
//http://creativecommons.org/licenses/by-nc-sa/4.0/

//UPDATES:
//7/9/2016 - Released
//8/9/2016
//-Added a wedge to reduce gap between clip and cap
//-Added an option for a clip cover
//-Customizable version

/* [Options] */
// preview[view:north east, tilt:side]
// Gap between clip and cap.
Gap=1.0; // [0:4]
// Mount plate thickness
Mount_thickness=5; // [5:10]
// Generate cover for the handle clip? Advisable for permanent replacement
Cover=0; // [0:No,1:Yes]

/* [Hidden] */
$fn=60;
epsilon=0.1;
//cap
capD=57-1;//exclude ridges
capR=capD/2;
capH=16.7;
capD1=6.6+2.3*2;
capR1=capD1/2;
capH1=2;
capD2=19;
capR2=capD2/2;
capH2=2;
capD3=capD1;
capR3=capD3/2;
capH3=1;
//clip
tight=0;
clipgap=Gap;
clipcover=Cover;
cliptotalH=capH1+capH2+capH3;
clipH=cliptotalH+2;
clipL=capD-5;
clipW=capD2+4;
clipCH1=capH1;
clipCL1=capD1-tight;
clipCW1=capD2;
clipCZ1=clipCH1/2;
clipCH2=capH2;
clipCL2=capD2-tight;
clipCW2=capD2;
clipCZ2=clipCH1/2+clipCH2;
clipCH3=capH3;
clipCL3=capD3-tight;
clipCW3=capD2;
clipCZ3=clipCH1+clipCH2+clipCH3/2;

module cap(e=0,bottom=1) {
    if (bottom) {
        translate([0,0,-capH]) cylinder(r=capR,h=capH);
    }
    translate([0,0,-e/2]) cylinder(r=capR1+e,h=capH1+e);
    translate([0,0,capH1-e/2]) cylinder(r=capR2+e,h=capH2+e);
    translate([0,0,capH1+capH2-e/2]) cylinder(r=capR3+e,h=capH3+e);
}

module clip(L=clipL,e=epsilon) {
    difference() {
        translate([0,0,clipH/2]) union() {
            cube([L,clipW,clipH],center=true);
            translate([0,0,clipH/2]) rotate([90,0,0]) hgap(L=clipL-4);
        }
        cap(bottom=0);
        translate([0,0,-2]) cylinder(r=capR1,h=capH1+2);
        translate([0,clipCW1/2,clipCZ1]) cube([clipCL1,clipCW1,clipCH1+2+e],center=true);
        translate([0,clipCW2/2,clipCZ2]) cube([clipCL2,clipCW2,clipCH2+e],center=true);
        translate([0,clipCW3/2,clipCZ3]) cube([clipCL3,clipCW3,clipCH3+e],center=true);
        if (clipcover) {
            translate([0,clipcoverW/2+capD2/2,-(clipH-cliptotalH)/2-2/2]) clipcover(e=epsilon,H=cliptotalH+2);
        }
    }
}

module clipcover2() {
    difference() {
        translate([0,0,clipH/2]) cube([clipL-epsilon,clipW-epsilon,clipH-epsilon],center=true);
        union() {
            clip(e=0);
            cap(e=epsilon);
        }
    }
}
clipcoverW=(clipW-capD2)/2;
module clipcover(e=0,H=cliptotalH) {
    translate([0,epsilon/2,clipH/2]) cube([clipCW2,clipcoverW+epsilon,H],center=true);
}

handleD=clipL;
handleR=handleD/2;
handleH=clipW;
handleT=4;
module h1(gap=clipgap) {
difference() {
        cylinder(r=handleR,h=handleH,center=true);
        cylinder(r=handleR-handleT,h=handleH+epsilon,center=true);
        translate([0,-handleR-clipH-gap,0]) cube([handleD,handleD,handleH+epsilon],center=true);
    }
}

module hgap(L=clipL){
    rotate([0,0,atan(-clipgap*2/L)]) translate([0,-clipH,-clipW/2]) cube([L/2,clipgap,clipW]);
    mirror([1,0,0]) rotate([0,0,atan(-clipgap*2/L)]) translate([0,-clipH,-clipW/2]) cube([L/2,clipgap,clipW]);
}
module hgap1(intersect=1){
    difference() {
        intersection() {
            h1();
            hgap();
        }
        hdull();
    }
}
module hdull() {
//dull the edge
     translate([0,0,handleH/2]) cylinder(r2=handleR-handleT+2,r1=handleR-handleT,h=2,center=true);
        mirror([0,0,1]) translate([0,0,handleH/2]) cylinder(r2=handleR-handleT+2,r1=handleR-handleT,h=2,center=true);
        translate([0,0,-handleH/2]) difference() {
            cylinder(r=handleR+epsilon,h=2,center=true);
            cylinder(r2=handleR,r1=handleR-2,h=2,center=true);
        }
        mirror([0,0,1]) translate([0,0,-handleH/2]) difference() {
            cylinder(r=handleR+epsilon,h=2,center=true);
            cylinder(r2=handleR,r1=handleR-2,h=2,center=true);
        }
}
module h2() {
    difference() {
        h1(gap=clipgap/2);
        hdull();
    }
    hgap1();
    //difference() {
        //hgap(L=clipL-4);
        //rotate([]) cap(bottom=0);
    //}

}
module handle() {
    translate([0,0,clipH]) rotate([90,0,0]) h2();
    clip(L=clipL-4);
}

handle();
%cap();
if (clipcover) {
    translate([0,capD,0]) clipcover();
}
```


[User]
I'd like a parametric OpenSCAD design for a foldable drill bit box. It should have two main parts: a larger box that acts as the main container and a smaller, insertable box that holds the drill bits.

Here are the key parameters I need:

*   **General:**
    *   `PrinterXYErrorMargin`:  A small value (e.g., 0.4mm) to account for printer inaccuracies, applied to the drill bit holes.
    *   `PrintMode`: A boolean toggle to control the layout; `True` for a layout intended for printing (with the small box translated and offset) and `False` for a combined view (for editing/visualization).
    *   `PrintBigBox`: A boolean toggle to control whether the main box is rendered.
    *   `PrintBitBox`: A boolean toggle to control whether the drill bit insert box is rendered.

*   **Dimensions:**
    *   `BoxWidth`: The width of the main container box.
    *   `BoxHeight`: The height of the main container box.
    *  `BoxLength`: The length of the main container box
    *   `WallThickness`: The thickness of the walls for both boxes.

*   **Drill Bit Configuration:**
    *   `NumberOfBits`: The number of drill bits the insert should hold.
    *   `FirstSize`: The diameter of the smallest drill bit hole.
    *   `IncrementInMillimeters`: The increment in diameter between successive drill bit holes.

*   **Hidden:**
    *   `$fn`: The number of faces for circles/spheres (e.g., 25).
    *   `fit`: A small clearance value to allow the boxes to fit together.
    *   `lockDiameter`: The diameter of the locking mechanism, should be derived from BoxHeight.

Here's the intended design for the main box (`BigBox`):

*   A simple rectangular box with a lid opening on the top.
*   The lid has two "locks" that consist of a sphere and cylinder shape on the big box, and a cut-out (lockCut) on the insertable box.
*   The lock "cut" shape is made from 2 stacked hulls of a cylinder and a translated cube.

And here's the design for the insertable drill bit box (`SmallBox`):

*   A rectangular "insert" that fits into the main box. The dimensions should account for the `WallThickness` and `fit` parameters when determining how it will fit inside the `BigBox`.
*   The drill bit holes should be placed along the width of the insert with spacing that equally separates the provided number of holes.
*   Drill bit holes should have diameter text labels using the provided `FirstSize` and `IncrementInMillimeters` variables.
*   The insert should have two locks/pins that will fit in with the `BigBox` locks.

The insertable box also includes a half cylinder running along its length for easier handling and aesthetics. The `fit` parameter should account for the necessary clearances to insert the smaller box inside of the main box. When a `PrintMode` is set to `True`, the `SmallBox` should be translated relative to the `BigBox` in order to be a printable file, otherwise it should be a combined visualization.

I don't want to use any libraries for this; everything should be implemented with basic OpenSCAD primitives.

[Assistant]
DrillBit.scad:
```
/* [General] */
PrinterXYErrorMargin = 0.4;

PrintMode = "True"; // [True;False]
PrintBigBox = "True"; // [True;False]
PrintBitBox = "True"; // [True;False]

/* [Dimensions] */
BoxWidth = 35;
BoxHeight = 10;
BoxLength = 75;
WallThickness = 1.5;

/* [Drill Bit] */
NumberOfBits = 4;
FirstSize = 1;
IncrementInMillimeters = 1;

/* [HIDDEN] */
$fn = 25;
fit = 0.5;
lockDiameter = BoxHeight/4;

//DrillBits();

if(PrintMode == "True"){
    if(PrintBigBox=="True") BigBox();
    translate([WallThickness+fit,-BoxWidth,0])
        if(PrintBitBox=="True") SmallBox();
}
else{
    BigBox();
    translate([WallThickness+fit,WallThickness+fit,WallThickness+fit])
        SmallBox();
}

module BigBox(){
    difference(){
        cube([BoxLength,BoxWidth,BoxHeight]);
        translate([WallThickness,WallThickness,WallThickness])
            cube([BoxLength-2*WallThickness,BoxWidth-2*WallThickness,BoxHeight]);

        cutHeight = BoxHeight/2+lockDiameter/2;
        translate([WallThickness+BoxHeight/2,WallThickness/2+0.5,cutHeight])
            rotate(a=[-90]) LockCut();
        translate([WallThickness+BoxHeight/2,BoxWidth-WallThickness/2-0.5,cutHeight])
            rotate(a=[-90]) LockCut();
    }

    translate([WallThickness+BoxHeight*2,WallThickness,BoxHeight/2+fit])
        sphere(d=lockDiameter*0.75);
    translate([WallThickness+BoxHeight*2,BoxWidth-WallThickness,BoxHeight/2+fit])
        sphere(d=lockDiameter*0.75);

    translate([WallThickness+BoxHeight/2,lockDiameter,BoxHeight/2+fit])
        rotate(a=[90,0]) cylinder(d2=lockDiameter,d1=lockDiameter*0.8,h=lockDiameter);
    translate([WallThickness+BoxHeight/2,BoxWidth,BoxHeight/2+fit])
        rotate(a=[90,0]) cylinder(d1=lockDiameter,d2=lockDiameter*0.8,h=lockDiameter);
}
module SmallBox(){
    bH = BoxHeight-WallThickness-fit*2;
    bW = BoxWidth-2*WallThickness-2*fit;

    difference(){
        union(){
            translate([BoxHeight/2,0])
               cube([BoxHeight*2,bW, bH]);
            translate([BoxHeight/2,0,bH/2])
                rotate(a=[-90])cylinder(d=bH,h=bW);
        }
        translate([bH/2,BoxWidth-WallThickness,bH/2])
            rotate(a=[0,-90,180]) DrillBits();
        translate([BoxHeight/2,-0.1,bH/2]){
            rotate(a=[-90]){
                cylinder(d=lockDiameter,h=lockDiameter);
                translate([0,0,bW-lockDiameter+0.2])
                    cylinder(d=lockDiameter,h=lockDiameter);
            }
        }
        translate([BoxHeight*2,0,bH/2]){
            sphere(d=lockDiameter+0.1);
            translate([0,bW])sphere(d=lockDiameter+0.1);
        }
    }

}
/* Aux */
module DrillBits(){
    bW = BoxWidth-4*WallThickness-2*fit;
    distIncrement = bW / NumberOfBits;
    for(i = [1:NumberOfBits]){
        translate([0,i*distIncrement,0]){
            size =FirstSize+(i-1)*IncrementInMillimeters;
            cylinder(d=size+PrinterXYErrorMargin,h=BoxLength);
            translate([BoxHeight/3,-BoxHeight/4])
                rotate(a=[90,0,90]) linear_extrude(height=5) text(str(size),BoxHeight/2);
        }
    }
}
module LockCut(){
    difference(){
        LockCutPattern(2*lockDiameter,WallThickness*2,BoxHeight);
        LockCutPattern(1.4*lockDiameter,WallThickness*2+1,BoxHeight+1);
    }

}
module LockCutPattern(d,h,w){
    hull(){
        cylinder(d=d,h=h,center=true);
        translate([0,w]) cube([d,w,h],center=true);
    }
}

```


[User]
Hey, I need help designing a parametric spring for 3D printing. I have an existing script that kind of does what I want but it's really rigid and not very flexible, and it isn't parameterized in a way I like. I'm looking for something I can easily tweak, especially the spring shape.

Here's what I'm thinking:

*   **General Shape:** I want a helical spring made from a series of connected segments. Each segment will be a polygon that's extruded into a 3D shape.
*   **Parameters:** I need to be able to control these things:
    *   `inner_radius`: The inner radius of the spring loops.
    *   `outer_radius`: The outer radius of the spring loops. This should be able to change independently of the inner radius to give me control over the thickness of the spring material.
    *   `loops`: The number of spring loops.
    *   `loop_height`: The vertical distance between each loop. This needs to be variable, so I can make compressed or stretched springs.
    *   `polygon_sides`: The number of sides on the polygon that forms each section of the spring, this should allow for springs with rectangular, pentagonal, hexagonal, etc shapes
    *   `segment_angle`: The angle that a single spring section makes before going to the next vertical position. It defaults to 360/`polygon_sides`. This needs to be a variable so we can do some more advanced spring designs.
    *    `extrusion_height`: The thickness/height of the polygon sections that form the spring.
    *   `flat_top`: A boolean value that determines if the top of the spring is flat or not. This makes printing much easier.
*   **Design:** Each loop should be a series of these extruded polygons, rotated around the center of the spring at an angle relative to the height of the current position, which will form a continuous helix.
*   **Output:** I'd prefer a single SCAD file that can be parameterized.

I'm printing this in ABS, so I need to be mindful of overhangs, which is why the flat top is useful.

I want to be able to adjust each of the parameters, including the sides of each segment of the spring, the angle that each segment travels before going to the next vertical position, the extrude thickness, and the flat top boolean.

Can you help me with that?

[Assistant]
Spring.scad:
```
/*
  3D Printed Spring
  Copyright Nir Dobovizki 2016
  Provided under the Creative Commons Attribution-NonCommercial 4.0 International (CC-BY-NC)
  Contact me at nir@nbdtech.com
*/

// Number of sides
sides = 20;
// Inner radius of the loops
radius = 5;
// Number of loops
loops = 7;
// Loops width
width = 2;
// Loop thickness
height = 1.6;
// distance between loops
loopHeight = 5;

ptOuter = ngon(sides,radius+width);
ptInner = ngon(sides,radius);

rotate([0,270,0])
union(){

for(i=[0:loops-1])
{
    base = loopHeight*i;

    for(j=[1:sides])
    {
        polyhedron(
            points = [
                [ptOuter[(j-1)%sides][0],ptOuter[((j-1)%sides)%sides][1],base+(j*(loopHeight/sides))],
                [ptOuter[j%sides][0],ptOuter[j%sides][1],base+((j+1)*(loopHeight/sides))],
                [ptOuter[(j-1)%sides][0],ptOuter[(j-1)%sides][1],base+height+(j*(loopHeight/sides))],
                [ptOuter[j%sides][0],ptOuter[j%sides][1],base+height+((j+1)*(loopHeight/sides))],
                [ptInner[(j-1)%sides][0],ptInner[(j-1)%sides][1],base+(j*(loopHeight/sides))],
                [ptInner[j%sides][0],ptInner[j%sides][1],base+((j+1)*(loopHeight/sides))],
                [ptInner[(j-1)%sides][0],ptInner[(j-1)%sides][1],base+height+(j*(loopHeight/sides))],
                [ptInner[j%sides][0],ptInner[j%sides][1],base+height+((j+1)*(loopHeight/sides))]
                ],
            faces = [
                [0,1,2],[1,3,2], //right
                [1,0,4],[4,5,1], //bottom
                [2,4,0],[2,6,4], //back
                [6,5,4],[6,7,5], //left
                [1,5,7],[7,3,1], //front
                [3,7,6],[6,2,3]  //top
        ]);
    }
    for(j=[sides/4+1:(sides/4)*3-1])
    {
        polyhedron(
            points = [
                [-(radius+width),ptOuter[((j-1)%sides)%sides][1],base+(j*(loopHeight/sides))],
                [-(radius+width),ptOuter[j%sides][1],base+((j+1)*(loopHeight/sides))],
                [-(radius+width),ptOuter[(j-1)%sides][1],base+height+(j*(loopHeight/sides))],
                [-(radius+width),ptOuter[j%sides][1],base+height+((j+1)*(loopHeight/sides))],
                [ptOuter[(j-1)%sides][0],ptOuter[((j-1)%sides)%sides][1],base+(j*(loopHeight/sides))],
                [ptOuter[j%sides][0],ptOuter[j%sides][1],base+((j+1)*(loopHeight/sides))],
                [ptOuter[(j-1)%sides][0],ptOuter[(j-1)%sides][1],base+height+((j)*(loopHeight/sides))],
                [ptOuter[j%sides][0],ptOuter[j%sides][1],base+height+((j+1)*(loopHeight/sides))]
                ],
            faces = [
                [0,1,2],[1,3,2], //right
                [1,0,4],[4,5,1], //bottom
                [2,4,0],[2,6,4], //back
                [6,5,4],[6,7,5], //left
                [1,5,7],[7,3,1], //front
                [3,7,6],[6,2,3]  //top
        ]);
    }
}

 }
// Simple list comprehension for creating N-gon vertices
function ngon(num, r) =
  [for (i=[0:num-1], a=i*360/num) [ r*cos(a), r*sin(a) ]];
```