[User]
Hey, I'm looking to design a threaded cylinder container, similar to a pill bottle but with a spiral thread, and I'd like a parametric OpenSCAD file for it. I already have an example but it's a bit clunky and I'd rather start fresh with something more adaptable.

Essentially, I need two cylinders that fit together with a spiral thread. The inner cylinder should slide into the outer cylinder and twist to lock.

Here are the key parameters I need:

*   `base_diameter`: The base diameter of the inner cylinder (and the starting point for calculating the outer cylinder).
*   `height`: The overall height of the assembled container.
*   `thread_depth`: The depth of the thread (half this value is the amount to offset from the base diameter for the thread peaks and valleys), with the thread depth being consistent between the inner and outer parts.
*   `thread_angle_degrees`: The angle of the spiral thread (expressed as degrees of rotation over the total height), which can affect how quickly the cylinders lock into each other.
*   `outer_clearance`: The amount of clearance between the inner cylinder thread peak and the outer cylinder thread valley; this value should affect the inner/outer cylinder fit. This parameter should default to 0 for the inner cylinder.
*   `base_thickness`: The thickness of a small flange at the bottom to make the inner cylinder easier to grip/remove.
*   `flare_height`: The height of a truncated cone flare at the top of the *outer* cylinder to make it easier to engage the threads
*   `flare_offset_distance`: The height of the flare above the top of the cylinder

The outer cylinder should be essentially the same as the inner cylinder, but with a different offset and a truncated cone "flare" at the top to make the threads easier to start.

For the *inner* cylinder, the `outer_clearance` parameter should be set to 0. For the *outer* cylinder, the `outer_clearance` should be > 0.

The spiral thread should be generated by linear extruding a circle with a twist. To generate the thread cutouts, I'd suggest a circular array of smaller circles being subtracted from a larger circle.

I need separate modules for generating the inner cylinder and outer cylinder.

I don't need any libraries. Could you provide the code?

[Assistant]
twist-cylinder.scad:
```

// Parameters used for uploaded objects:
// twistcyl(22,50,0) -> twist-cyl-2250-in.stl
// twistcyl(22,50,0.6) -> twist-cyl-2250-out6.stl
// twistcyl(80,50,0) -> twist-cyl-8050-in.stl
// twistcyl(80,50,0.6) -> twist-cyl-8050-out6.stl
// twistcyl(52,80,0) -> twist-cyl-5280-in.stl
// twistcyl(52,80,0.6) -> twist-cyl-5280-out6.stl

twistcyl(52,80,0.6);

module twistcyl(diam1,height1,outside){
// diam1 = "base" diameter (inside diam is 2 x "thread" + nozzle less, outside diam is 2 x "thread" + nozzle greater)
// height1 = height overall
// outside = difference between outside object of pair and inside object of pair (0 for inside object, >0 for outside object. For 0.4mm nozzle, 0.5 is tight, 0.7 is slip-fit.)
thread=0.5; // half thread depth

// note: negative "twist" = right-hand thread
linear_extrude(height = height1, center = false, convexity = 10, twist = -120*height1/diam1, slices = 100, scale = 1.0, $fn = 72){
difference(){
circle(r=diam1/2+thread+outside, $fn = 72);
for (i=[0:60:300])
    rotate([0,0,i])
translate([diam1*0.90+outside,0,0])
    circle(r=diam1/2,$fn=6);
}
circle(r=(diam1/2)-thread+outside, $fn = 72);
}
if (outside > 0)
    // truncated cone flare at top of "outside" cylinder to make it easier to engage the threads
translate([0,0,height1-2])
  cylinder(d2=diam1+2*(thread+outside), d1=diam1-2*thread+2*outside, h=2, center=false, $fn=72);

// base disc to prevent the inside cylinder going flush with the outside cylinder, making it difficult to twist/pull out. Assumes 0.2mm layers and 4 bottom layers in slicer.
cylinder(d=diam1+2*(thread+outside), h=0.8, center=false, $fn=72);
}

```


[User]
Hey, I'm looking to create a customizable box generator in OpenSCAD, kind of like the "Box Wizard" I saw. I need it to be fully parametric so I can adjust pretty much everything.

Here are the features I need it to have:

**Basic Box Dimensions:**
*   I need to be able to set the internal width, length, and height of the box.
*   I also need to be able to set the wall thickness of the box.

**Fillets and Chamfers:**
*   I want an option for an internal fillet on the top edge of the box opening. The radius of this fillet should be adjustable.
*   I need the ability to add chamfers on all the internal corners of the box. The size of the chamfer needs to be adjustable.
*   If internal chamfers are enabled, I'd like an option to *also* add matching chamfers to the outside edges of the box.

**Mounting Screws:**
*   It needs to have an option to add counter sunk screw holes for mounting.
*   I need to be able to pick from standard metric sizes (M1, M1.6, M2, M2.5, M3, M3.5, M4, M5, M6, M8, M10, M12, M16) *or* standard imperial sizes (0000, 000, 00, 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 1/4, 5/16, 3/8, 7/16, 1/2, 9/16, 5/8, 3/4). The size should be selectable in a drop down.

**Stacking Slots:**
*   I need an option to add stacking slots to the box. These can be on the sides, ends, or both.
*   The width, height, length, and chamfer of the slots should be adjustable parameters.
*   I need a small clearance parameter to ensure a snug fit when stacking boxes together.

I'd really prefer if you could generate the OpenSCAD file from scratch instead of trying to modify my example code. I don't think I'll need any external libraries like MCAD or BOSL for this, but let me know if you think it'd be beneficial.  I'm looking for just a single `.scad` file for all of this, by the way, so I can just open it up and start adjusting. Let me know if you need more information!

[Assistant]
BoxWizard_v2.scad:
```
/*
*  Copyright (c)..: 2018 janpetersen.com
*
*  Creation Date..:11/18/2018
*  Discription....: Box Wizard - OpenScad application to build fully parametric boxes
*
*  Rev 1: Developed Model
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  If used commercially attribution is required (janpetersen.com)
*
*
*   Thingiverse Customizer documentation:
*   http://customizer.makerbot.com/docs
*
*
*/

/* ----- customizer view ----- */
//preview[view:north, tilt:top]

/* ----- parameters ----- */

/* [Hidden] */
$fn=90; // recomended value for smooth circle is 60
debug = false;

// Standard screw dimensions arrays
//    [ screwDiameter (d), headDiameter (d1), headHeight (k) ]

CounterSunkScrewsMetric=[
// DIN963 slotted countersunk (flathead) head
        [0, 0, 0],            // "none"
        [1, 1.9, 0.6],      // "M1"
        [1.6, 3, 0.96],    // "M1.6"
        [2, 3.8, 1.2],      // "M2"
        [2.5, 4.7, 1.5],   // "M2.5"
        [3, 5.6, 1.65],    // "M3"
        [3.5, 6.5, 1.93], //"M3.5"
        [4, 7.5, 2.2],      // "M4"
        [5, 9.2, 2.5],      // "M5"
        [6, 11, 3],          // "M6"
        [8, 14.5, 4],       // "M8"
        [10, 18, 5],        // "M10"
        [12, 22, 6],        // "M12"
        [16, 29, 8]         // "M16"
  ];
  CounterSunkScrewsStandard=[
        [0, 0, 0],                        // "none"
        [0.021, 0.038, 0.019],   // "0000"
        [0.034, 0.059, 0.029],   // "000"
        [0.047, 0.082, 0.037],   // "00"
        [0.06, 0.096, 0.043],     // "0"
        [0.073, 0.118, 0.053],   // "1"
        [0.086, 0.140, 0.062],   // "2"
        [0.099, 0.161, 0.070],   // "3"
        [0.112, 0.183, 0.079],   //"4"
        [0.125, 0.205, 0.088],   // "5"
        [0.138, 0.226, 0.096],   // "6"
        [0.164, 0.270, 0.113],   // "8"
        [0.190, 0.313, 0.130],   // "10"
        [0.216, 0.357, 0.148],   // "12"
        [0.250, 0.414, 0.170],   // "1/4"
        [0.3125, 0.518, 0.211], // "5/16"
        [0.375, 0.622, 0.253],   // "3/8"
        [0.4375, 0.625, 0.265], // "7/16"
        [0.500, 0.750, 0.297],   // "1/2"
        [0.5625, 0.812, 0.336], // "9/16"
        [0.625, 0.875, 0.375],   // "5/8"
        [0.750, 1.000, 0.441]    // "3/4"
    ];

  // a fix to ensure the proper rendering of aligned surfaces, such as used for Difference operations
  openSCADFix = 0.01;

/* [Global] */

/* [Box] */
// internal width of box
width=50;
// internal length of box
length=120;
// internal height of box
height=60;
// wall thickness in mm
wallThickness=3;

/* [Fillets] */
// radius of inside fillet on box opening, set to 0 for no fillet
insideTopFillet=1.5;
// inside champer along all internal corners of the box, set to 0 for no chamfer
insideCornerChamfer=3;
// add outside chamfers as well
outsideChamfers=1; // [0:false, 1:true]
outsideCornerChamfer= outsideChamfers && insideCornerChamfer > 0 ? ( ( wallThickness + insideCornerChamfer / 2 ) / cos( 45 )  - wallThickness ) * cos( 45 ) * 2 : 0;

/* [Mounting Screws] */
Metric_Counter_Sunk=0; // [0:none, 1:M1, 2:M1.6, 3:M2, 4:M2.5, 5:M3, 6:M3.5, 7:M4, 8:M5, 9:M6, 10:M8, 11:M10, 12:M12, 13:M16]
Standard_Counter_Sunk=11; // [0:none, 1:0000, 2:000, 3:00, 4:0, 5:1, 6:2, 7:3, 8:4, 9:5, 10:6, 11:8, 12:10, 13:12, 14:1/4, 15:5/16, 16:3.8, 17:7/16, 18:1/2, 19:9/16, 20:5/8, 21:3/4]

/* [Stacking Slots] */
Add_Stacking_Slots="both"; // [none, side, end, both]
Slot_Width=5;
Slot_Height=2;
Slot_Chamfer=3;
Slot_Length=45;
// tolerance to allow for tight fit when mating the boxes
Slot_Clearance=0.2;

/* ----- execute ----- */

mainModule();

/* ----- functions ----- */
function inch_to_mm(inch) = inch / 0.039370;

/* ----- modules ----- */
module mainModule() {

    // if a mounting screw is selected, set the screw variables
    d = Metric_Counter_Sunk > 0 ? CounterSunkScrewsMetric[Metric_Counter_Sunk][0] : inch_to_mm( CounterSunkScrewsStandard[Standard_Counter_Sunk][0]);
    d1 = Metric_Counter_Sunk > 0 ? CounterSunkScrewsMetric[Metric_Counter_Sunk][1] : inch_to_mm( CounterSunkScrewsStandard[Standard_Counter_Sunk][1]);
    k = Metric_Counter_Sunk > 0 ? CounterSunkScrewsMetric[Metric_Counter_Sunk][2] : inch_to_mm( CounterSunkScrewsStandard[Standard_Counter_Sunk][2]);
    l = wallThickness;
    if ( debug ) {
        echo("Screw Diameter: " , d);
        echo("Screw Head Diameter: ", d1);
        echo("Screw Head Hight: ", k);
        echo("Screw Length: ", l);
    }

    difference() {
        union() {
            cube( size=[
                    ( length+2*wallThickness ),
                    ( width+2*wallThickness ),
                    ( height+wallThickness )
                 ], center=true );

            if ( ( Add_Stacking_Slots == "both" || Add_Stacking_Slots == "side" ) && ( Slot_Width * 4 < length )) {
                translate( [ -length / 3, width / 2 + wallThickness, - ( height + wallThickness ) / 2 + outsideCornerChamfer ] )
                    slot();
                translate( [ length / 3, width / 2 + wallThickness, - ( height + wallThickness ) / 2 + outsideCornerChamfer ] )
                    slot();
            }
            if ( ( Add_Stacking_Slots == "both" || Add_Stacking_Slots == "end" ) && ( Slot_Width * 4 < width ) ) {
                translate( [ -length / 2 - wallThickness, width / 3, - ( height + wallThickness ) / 2 + outsideCornerChamfer ] )
                    rotate( [ 0, 0, 90 ] )
                        slot();
                translate( [ -length / 2 - wallThickness, -width / 3, - ( height + wallThickness ) / 2 + outsideCornerChamfer ] )
                    rotate( [ 0, 0, 90 ] )
                        slot();
            }
        }
        if ( outsideCornerChamfer > 0 )
            outsideChamfers();
        translate([0, 0, wallThickness/2 + 0.01 ])
            if ( insideCornerChamfer == 0 )
                cube( size=[ length, width, height + 0.02 ], center=true );
            else
                difference() {
                    cube( size=[ length, width, height + 0.02 ], center=true );
                    insideChamfers();
                }
        if ( d > 0 ) {
            translate( [ -length/3, 0, - height  / 2  + 0.01 ] )
                screwHole(d, d1, k, l + 0.02);
            translate( [ length/3, 0, - height  / 2  + 0.01 ] )
                screwHole(d, d1, k, l + 0.02);
        }
        if ( insideTopFillet > 0 )
            translate( [ 0, 0, (height  + wallThickness - insideTopFillet ) / 2] )
                insideTopFillet();

        if ( ( Add_Stacking_Slots == "both" || Add_Stacking_Slots == "side" ) && ( Slot_Width * 4 < length ) ) {
            translate( [ -length / 3, - width / 2 - wallThickness, - (height + wallThickness + Slot_Clearance ) / 2 ] )
                groove();
            translate( [ length / 3, - width / 2 - wallThickness, - (height + wallThickness + Slot_Clearance ) / 2 ] )
                groove();
        }
        if ( ( Add_Stacking_Slots == "both" || Add_Stacking_Slots == "end" ) && ( Slot_Width * 4 < width ) ) {
            translate( [ length / 2 + wallThickness, width / 3, - ( height + wallThickness + Slot_Clearance ) / 2 ] )
                rotate( [ 0, 0, 90 ] )
                    groove();
            translate( [ length / 2 + wallThickness, -width / 3, - ( height + wallThickness + Slot_Clearance ) / 2 ] )
                rotate( [ 0, 0, 90 ] )
                    groove();
        }

    }
}

module screwHole(d, d1, k, l) {
    // d = screw diameter
    // d1 = head diameter
    // k = head height
    // l = screw length

    chamferHeight = min( k, l );
    if ( debug ) {
        echo("Adjusted screw chamfer height: ", chamferHeight);
    }
    union() {
        cylinder( d=d, h=l + 0.02, center=true );
        if ( d1> d ) {
            translate( [ 0, 0, ( l - chamferHeight ) / 2 ] )
                cylinder( d1=d, d2=d1, h= chamferHeight, center=true );
        }
    }
}

module insideChamfers() {
    // vertical chamfers
    // top left
    translate( [ -length / 2 , width / 2, 0 ] )
        cornerAngle( height + 2 * wallThickness, insideCornerChamfer, 0, 0 );

    // bottom left
    translate( [ -length / 2 , -width / 2, 0 ] )
        cornerAngle( height + 2 * wallThickness, insideCornerChamfer, 0, 90 );

    // bottom right
    translate( [ length / 2 , -width / 2, 0 ] )
        cornerAngle( height + 2 * wallThickness, insideCornerChamfer, 0, 180 );

    // top right
    translate( [ length / 2 , width / 2, 0 ] )
        cornerAngle( height + 2 * wallThickness, insideCornerChamfer, 0, 270 );

    // horizontal chamfers
    // top
    translate( [ 0 , -width / 2, -height / 2 ] )
        cornerAngle( length + 2 * wallThickness, insideCornerChamfer, -90, 90 );

    // bottom
    translate( [ 0 , width / 2, -height / 2 ] )
        cornerAngle( length + 2 * wallThickness, insideCornerChamfer, -90, -90 );

    // left
    translate( [ -length / 2, 0 , -height / 2 ] )
        cornerAngle( width + 2 * wallThickness, insideCornerChamfer, -90, -0 );

    // right
    translate( [ length / 2, 0 , -height / 2 ] )
        cornerAngle( width + 2 * wallThickness, insideCornerChamfer, -90, 180 );

}

module outsideChamfers() {
    d1 = max( 1, Slot_Width - Slot_Chamfer );
    // vertical chamfers
    // top left
    translate( [ -length / 2 - wallThickness - openSCADFix, width / 2 + wallThickness + openSCADFix, 0 ] )
        cornerAngle( height + 2 * wallThickness + 2, outsideCornerChamfer + openSCADFix, 0, 0 );

    // bottom left
    translate( [ -length / 2 - wallThickness - openSCADFix, -width / 2 - wallThickness - openSCADFix, 0 ] )
        cornerAngle( height + 2 * wallThickness + 2, outsideCornerChamfer + openSCADFix, 0, 90 );

    // bottom right
    translate( [ length / 2 + wallThickness + openSCADFix, -width / 2 - wallThickness - openSCADFix, 0 ] )
        cornerAngle( height + 2 * wallThickness + 2, outsideCornerChamfer + openSCADFix, 0, 180 );

   // top right
    translate( [ length / 2 + wallThickness + openSCADFix, width / 2 + wallThickness + openSCADFix, 0 ] )
        cornerAngle( height + 2 * wallThickness + 2, outsideCornerChamfer + openSCADFix, 0, 270 );

    // horizontal chamfers
    // top
    translate( [ 0 , -width / 2 - wallThickness - d1 - openSCADFix, ( -height - wallThickness ) / 2 - openSCADFix ] )
        cornerAngle( length + 2 * wallThickness, outsideCornerChamfer + d1 + openSCADFix, -90, 90 );

    // bottom
    translate( [ 0 , width / 2 + wallThickness + d1 + openSCADFix, ( -height - wallThickness ) / 2  - openSCADFix ] )
        cornerAngle( length + 2 * wallThickness, outsideCornerChamfer + d1 + openSCADFix, -90, -90 );

    // left
    translate( [ -length / 2 - wallThickness - d1 - openSCADFix, 0 , ( -height - wallThickness ) / 2  - openSCADFix] )
        cornerAngle( width + 2 * wallThickness, outsideCornerChamfer + d1 + openSCADFix, -90, 0 );

    // right
    translate( [ length / 2 + wallThickness + d1 + openSCADFix, 0 , ( -height  - wallThickness ) / 2 - openSCADFix ] )
        cornerAngle( width + 2 * wallThickness, outsideCornerChamfer + d1 + openSCADFix, -90, 180 );
}

module insideTopFillet() {
    difference() {
        cube(size=[ length + insideTopFillet * 2, width + insideTopFillet * 2, insideTopFillet + openSCADFix ], center=true);
        if ( insideCornerChamfer > 0 ) {
            cornerCylOffset = ( ( insideTopFillet + insideCornerChamfer / 2 ) / cos( 45 ) - insideTopFillet ) * cos( 45 );
            cornerSize = cornerCylOffset * 2;
            // top left
            translate( [ -length / 2 - insideTopFillet, width / 2 + insideTopFillet , 0 ] ) {
                cornerAngle( insideTopFillet, cornerSize, 0, 0 );
                translate( [  cornerCylOffset, -cornerCylOffset, -insideTopFillet / 2 ] )
                    rotate( [ -45, 90, 0 ] )
                        cylinder( r=insideTopFillet, h=insideCornerChamfer * 2, center = true );
            }

            // bottom left
            translate( [ -length / 2 - insideTopFillet, -width / 2 - insideTopFillet, 0 ] ) {
                cornerAngle( insideTopFillet, cornerSize, 0, 90 );
                translate( [  cornerCylOffset, cornerCylOffset, -insideTopFillet / 2 ] )
                    rotate( [ 45, 90, 0 ] )
                        cylinder( r=insideTopFillet, h=insideCornerChamfer * 2, center = true );
            }

            // bottom right
            translate( [ length / 2 + insideTopFillet, -width / 2 - insideTopFillet, 0 ] ) {
                cornerAngle( insideTopFillet, cornerSize, 0, 180 );
                translate( [  -cornerCylOffset, cornerCylOffset, -insideTopFillet / 2 ] )
                    rotate( [ -45, 90, 0 ] )
                        cylinder( r=insideTopFillet, h=insideCornerChamfer * 2, center = true );
            }

            // top right
            translate( [ length / 2 + insideTopFillet, width / 2 + insideTopFillet, 0 ] ) {
                cornerAngle( insideTopFillet, cornerSize, 0, 270 );
                translate( [  -cornerCylOffset, -cornerCylOffset, -insideTopFillet / 2 ] )
                    rotate( [ 45, 90, 0 ] )
                        cylinder( r=insideTopFillet, h=insideCornerChamfer * 2, center = true );
            }

        }
        translate( [ 0, width / 2 + insideTopFillet, -insideTopFillet / 2] )
            rotate( [ 0, 90, 0 ] )
                cylinder( r=insideTopFillet, h=length + insideTopFillet * 2, center=true );
        translate( [ 0, - width / 2 - insideTopFillet, -insideTopFillet / 2 ] )
            rotate( [ 0, 90, 0 ] )
                cylinder( r=insideTopFillet, h=length + insideTopFillet * 2, center=true );
        translate( [  length / 2 + insideTopFillet, 0, -insideTopFillet / 2 ] )
            rotate( [ 90, 0, 0 ] )
                cylinder( r=insideTopFillet, h=width + insideTopFillet * 2, center=true );
        translate( [  -length / 2 - insideTopFillet, 0, -insideTopFillet / 2 ] )
            rotate( [ 90, 0, 0 ] )
                cylinder( r=insideTopFillet, h=width + insideTopFillet * 2, center=true );
    }
}

module slot() {
    h = min( wallThickness - 1, Slot_Height );
    d1 = max( 1, Slot_Width - Slot_Chamfer );
    p = [
        [ Slot_Width / 2, h ],
        [ -Slot_Width / 2, h ],
        [ -d1 / 2, 0 ],
        [ d1 / 2, 0 ]
    ];
    if ( debug ) {
        echo( p );
    }
    linear_extrude( height=Slot_Length - outsideCornerChamfer )
        polygon( points=p );
}

module groove() {
    h = min( wallThickness - 1, Slot_Height );
    d1 = max( 1, Slot_Width - Slot_Chamfer );
    p = [
        [ Slot_Width / 2, h ],
        [ -Slot_Width / 2, h ],
        [ -d1 / 2, 0 ],
        [ d1 / 2, 0 ]
    ];
    if ( debug ) {
        echo( p );
    }
    linear_extrude( height=Slot_Length + Slot_Clearance * 2)
        offset(delta=Slot_Clearance) polygon( points=p );
}

module cornerAngle( length, width, rotateAngleX, rotateAngleZ ) {
    rotate( [ rotateAngleX, 0, rotateAngleZ ] )
        linear_extrude( height=length, center=true )
            polygon( points= [
                [ 0, 0 ],
                [ width, 0 ],
                [ 0, -width ]
            ]);
}

```


[User]
Okay, I'm looking to create a fully customizable pipe connector in OpenSCAD, and I'd like it to be parametric so I can easily adjust the dimensions. I've got a few different scenarios in mind, so it'd be great if we could design a few versions, or have one flexible design.

First, I need a basic T-shaped connector, with three pipes coming together at 90-degree angles. For this, I need to be able to specify the outer and inner diameter of each pipe, the length of each pipe, and how much of each pipe inserts into the main connector body. I'd also like to control the inner diameter of the junction where the pipes meet. It would also be ideal if the pipe ends were rounded. Finally, I'd like an option to see a cutaway view to help with the design process.

Second, it would be amazing to have a version where I can change the angles at which the left and right pipes connect. So, the central pipe is still straight, but I'd want to be able to specify the angle at which the other two pipes attach.

Third, I'd like a version that's similar to the basic tee, but with four pipes, so a cross shape. Again, the same kinds of parameters would be needed - outer/inner diameters, lengths, insertion depths and the inner junction diameter.

Fourth, I could imagine situations where I might want different internal diameters for the connectors than just the shared common one. I also can think of times when it'd be useful to have a sphere at the internal junction to help with flow or maybe just structurally. It would be fantastic if a version with the ability to control the internal diameters for each pipe bore separately was available.

Finally, I'd like one version to be pre-configured to handle pipes with 20mm inside diameters.

Ideally, I'd like these to be separate, but related files. I want to be able to make small tweaks in one place and have it reflected across all of the models, if possible.

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

//Test: 0: normal, 1:examine inside
test=1;

//Outer diameter of transverse pipe
DX1=14;
//Inner diameter of transverse pipe
BX1=11;
// Length of transverse pipe
LX1=40;
//Length to insert transverse
LX11=15;

//Outer diameter of right pipe
DX2=10;
//Inner diameter of right part
BX2=8;
//Length of right pipe
LX2=45;
//Length to insert at right
LX22=30;

//Outer diameter of left pipe
DX3=9;
//Inner diameter of left pipe
BX3=6;
// Left of left pipe
LX3=20;
//Length to insert at left
LX33=10;

//Common inner diameter of connection
CD=5;

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

difference() {

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

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

rotate([0,90,0])   // transverse pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=120);

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = 120);
  }   // union

rotate([-90,0,0])    // right pipe
  union(){
    cylinder(d=DX2,h=LX2,$fn=120);

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

rotate([90,0,0])     // left pipe
  union(){
   translate([0,0,0])
     cylinder(d=DX3,h=LX3,$fn=120);

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

 }   // union of pipes and central sphere

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

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

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

  }

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

 }

// Test option to look inside
if(test==1)  {
  translate([-50,-50,0])
    cube([150,100,50]);
}

 }   // difference
```
Tee4.scad:
```
// My tee  v4
// phildc 2020
// Use openscad customizer

//Test: 0: normal, 1:examine inside
test=1;

//Outer diameter of transverse pipe
DX1=14;
//Inner diameter of transverse pipe
BX1=11;
// Length of transverse pipe
LX1=40;
//Length to insert transverse
LX11=15;

//Outer diameter of right pipe
DX2=10;
//Angle1 of right pipe
AR1=-45;        //was -90   V4
//Angle2 of right pipe
AR2=-45;        //was 0   V4
//Inner diameter of right part
BX2=8;
//Length of right pipe
LX2=45;
//Length to insert at right
LX22=30;

//Outer diameter of left pipe
DX3=9;
//Angle1 of leftt pipe
AL1=45;        //was 90   V4
//Angle2 of left pipe
AL2=-30;        //was 0   V4
//Inner diameter of left pipe
BX3=6;
// Left of left pipe
LX3=20;
//Length to insert at left
LX33=10;

//Common inner diameter of connection
CD=5;

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

difference() {

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

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

rotate([0,90,0])   // transverse pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=120);

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = 120);
  }   // union

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

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

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

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

 }   // union of pipes and central sphere

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

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

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

  }

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

 }

// Test option to look inside
if(test==1)  {
  translate([-50,-50,0])
    cube([150,100,50]);
}

 }   // difference
```
Tee3cross.scad:
```
// My tee 3
// phildc 2020
// Use openscad customizer

//Test: 0: normal, 1:examine inside
test=0;
//Cross: 0: Tee(3), 1: Cross(4)
cross=1;

//Outer diameter of transverse pipe
DX1=14;
//Inner diameter of transverse pipe
BX1=11;
// Length of transverse pipe
LX1=40;
//Length to insert transverse
LX11=15;

//Outer diameter of right pipe
DX2=10;
//Inner diameter of right part
BX2=8;
//Length of right pipe
LX2=45;
//Length to insert at right
LX22=30;

//Outer diameter of left pipe
DX3=9;
//Inner diameter of left pipe
BX3=6;
//Length of left pipe
LX3=20;
//Length to insert at left
LX33=10;

//Common inner diameter of connection
CD=5;

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

difference() {

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

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

rotate([0,90,0])   // transverse pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=120);

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = 120);
  }   // union

// V3cross
if (cross==1) {
rotate([0,-90,0])   // cross pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=120);

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = 120);
  }   // union
}    // V3cross

rotate([-90,0,0])    // right pipe
  union(){
    cylinder(d=DX2,h=LX2,$fn=120);

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

rotate([90,0,0])     // left pipe
  union(){
   translate([0,0,0])
     cylinder(d=DX3,h=LX3,$fn=120);

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

 }   // union of pipes and central sphere

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

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

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

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

  }

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

 }

// Test option to look inside
if(test==1)  {
  translate([-50,-50,0])
    cube([150,100,50]);
}

 }   // difference
```
Tee3full.scad:
```
// T3full with separate diameters (FT parm)
// and possible sphere at junction (SP parm)
// phildc 2020
// Use openscad customizer

//Test: 0: normal, 1:examine inside, 2 or 3: cut more
test=0;
//Cross: 0: Tee(3), 1: Cross(4)
cross=1;

//Outer diameter of transverse pipe
DX1=14;
//Inner diameter of transverse pipe
BX1=11;
// Length of transverse pipe
LX1=20;
//Length to insert transverse
LX11=12;

//Outer diameter of right pipe
DX2=14;
//Inner diameter of right part
BX2=11;
//Length of right pipe
LX2=20;
//Length to insert at right
LX22=12;

//Outer diameter of left pipe
DX3=14;
//Inner diameter of left pipe
BX3=11;
//Length of left pipe
LX3=20;
//Length to insert at left
LX33=12;

//Common inner diameter of connection
CD=8;

//Force dia    0=same dia, 1= different dia
FT=1;
//Dia transverse forced
DT=9;
//Dia left if forced
DL=9;
//Dia right if forced
DR=9;

//Sphere at junction  0= no, 1= yes
SP=1;

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

difference() {

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

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

rotate([0,90,0])   // transverse pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=120);

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = 120);
  }   // union

// V3cross
if (cross==1) {
rotate([0,-90,0])   // cross pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=120);

    rotate_extrude(convexity = 10,$fn=120)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = 120);
  }   // union
}    // V3cross

rotate([-90,0,0])    // right pipe
  union(){
    cylinder(d=DX2,h=LX2,$fn=120);

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

rotate([90,0,0])     // left pipe
  union(){
   translate([0,0,0])
     cylinder(d=DX3,h=LX3,$fn=120);

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

 }   // union of pipes and central sphere

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

if (SP==1){    // sphere at junction ?
 //sphere(d=min(DX1,DX2,DX3),$fn=120);   // v3b
 sphere(d=max(BX1,BX2,BX3),$fn=120);   // v3b
}

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

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

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

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

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

  }   // union
  }
 else {
 rotate([-90,0,0])    // bore right
  union() {
  translate([0,0,LX2-LX22])
     cylinder(d=BX2,h=LX2+2,$fn=120);
  translate([0,0,-1])
     cylinder(d=DR,h=LX2+2,$fn=120);

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

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

 }   //union
 }

// Test options to look inside
if(test>=1)  {
  translate([-50,-50,0])
    cube([150,100,50]);
}
if(test>=2)  {
  rotate([0,90,0])
  translate([-50,-50,0])
    cube([150,100,50]);
}
if(test>=3)  {
  rotate([90,0,0])
  translate([-50,-50,0])
    cube([150,100,50]);
}
 }   // difference
```
Tee20.scad:
```
// T3full with separate diameters (FT parm)
// and possible sphere at junction (SP parm)
// phildc 2020
// Use openscad customizer

// This TEE configured for 20mm inside diameter

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

//Test: 0: normal, 1:examine inside, 2 or 3: cut more
test=0;
//Cross: 0: Tee(3), 1: Cross(4)
cross=0;

//Outer diameter of transverse pipe
DX1=24;
//Inner diameter of transverse pipe
BX1=20;
// Length of transverse pipe
LX1=25;
//Length to insert transverse
LX11=13;

//Outer diameter of right pipe
DX2=24;
//Inner diameter of right part
BX2=20;
//Length of right pipe
LX2=25;
//Length to insert at right
LX22=13;

//Outer diameter of left pipe
DX3=24;
//Inner diameter of left pipe
BX3=20;
//Length of left pipe
LX3=25;
//Length to insert at left
LX33=13;

//Common inner diameter of connection
CD=16;

//Force dia    0=same dia, 1= different dia
FT=0;
//Dia transverse forced
DT=8;
//Dia left if forced
DL=8;
//Dia right if forced
DR=8;

//Sphere at junction  0= no, 1= yes
SP=0;

// Resolution
R=360;

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

difference() {

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

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

rotate([0,90,0])   // transverse pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=R);

    rotate_extrude(convexity = 10,$fn=R)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = R);
  }   // union

// V3cross
if (cross==1) {
rotate([0,-90,0])   // cross pipe
  union() {
    cylinder(d=DX1,h=LX1,$fn=R);

    rotate_extrude(convexity = 10,$fn=R)
     translate([DX1/2-((DX1-BX1)/4), LX1, 0])
       circle(r = (DX1-BX1)/4, $fn = R);
  }   // union
}    // V3cross

rotate([-90,0,0])    // right pipe
  union(){
    cylinder(d=DX2,h=LX2,$fn=R);

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

rotate([90,0,0])     // left pipe
  union(){
   translate([0,0,0])
     cylinder(d=DX3,h=LX3,$fn=R);

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

 }   // union of pipes and central sphere

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

if (SP==1){    // sphere at junction ?
 sphere(d=max(BX1,BX2,BX3),$fn=R);   // v3b
}

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

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

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

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

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

  }   // union
  }
 else {
 rotate([-90,0,0])    // bore right
  union() {
  translate([0,0,LX2-LX22])
     cylinder(d=BX2,h=LX2+2,$fn=R);
  translate([0,0,-1])
     cylinder(d=DR,h=LX2+2,$fn=R);

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

 }   //union
 }
 else {
     rotate([90,0,0])    // bore left
  union() {
  translate([0,0,LX3-LX33])
     cylinder(d=BX3,h=LX33+2,$fn=R);
  translate([0,0,-1])
     cylinder(d=DL,h=LX3+2,$fn=R);

 }   //union
 }

// Test options to look inside
if(test>=1)  {
  translate([-500,-500,0])
    cube([1500,1000,500]);
}
if(test>=2)  {
  rotate([0,90,0])
  translate([-500,-500,0])
    cube([1500,1000,500]);
}
if(test>=3)  {
  rotate([90,0,0])
  translate([-500,-500,0])
    cube([1500,1000,500]);
}
 }   // difference
```


[User]
Hey, I'm looking to design a parametric funnel in OpenSCAD, and I'd like it to have a threaded bottom that fits the Mattmill Kompakt. I've already got a design in mind and some specific dimensions, but I need it to be fully parametric, so I can easily adjust sizes later.

Essentially, the funnel will have three main parts: a cylindrical top section, a conical mid-section, and then a threaded bottom section that's supposed to screw onto the Mattmill.

Here's the breakdown of the parameters I need:

*   **Cylindrical Section:**
    *   `Cyl_Height`: The height of the cylindrical portion.
    *   `Top_Inside_Diameter`: The inner diameter of the top of the funnel.
    *   `Ledge`: A small ledge on the top inner diameter of the cylinder (this should extrude inward).
*   **Conical Section:**
    *   `Cone_Height`: The height of the conical section.
*   **General Parameters:**
    *   `Wall_Thickness`: The thickness of the walls of the funnel.
    *   `Tube_Outside_Diameter`: The outer diameter of the threaded bottom section, which will need to match the Mattmill.
*   **Threading Parameters:**
     *  `thread_depth`: depth of threads
     *  `threads`: number of threads
     *  `thread_distance`: distance between each thread
     * `neck_height`: height of the threaded section.
     * `triangle_magic`: controls thread triangle shape.

I'd like the design to be robust, and easy to modify through parameters, so you should define all the variables I listed. The threaded section needs to be generated using a helical extrusion.

Also, I'd like to include two options for the threaded section, one to make a female thread which is used to join the MattMill, and one to make a male thread which would act as a lid. I'd like to implement this with a boolean.

I think that covers the main details.  I'm thinking something like a primary file for the overall structure of the funnel, and then a separate module or file (whichever is better practice) for generating the threaded part. I'd like to use MCAD for thread generation. Let me know if that makes sense or if I should clarify any other part of my request.

[Assistant]
parametric_funnel_mattmill.scad:
```
//Height of the cylindrical part
Cyl_Height = 160;

//Height of the conical part
Cone_Height = 70;

// Cylinder inner diameter
Top_Inside_Diameter=190;

// Top Ledge Width
Ledge=3;

// Wall Thickness
Wall_Thickness=1.2;

Tube_Outside_Diameter=25.0;

Height = Cyl_Height + Cone_Height;

$fn = 60;
Top_Height = Height;
Top_Outside_Diameter = Top_Inside_Diameter + Wall_Thickness*2;

rotate([180,0,0]) translate([0,0,-Height]) {

  // Funnel ledge
  translate ([0,0,Height-Wall_Thickness*2]) difference(){
    cylinder(d = Top_Inside_Diameter+Ledge*2, h = Wall_Thickness*2);
    translate ([0,0,-0.1]) cylinder(d = Top_Inside_Diameter, h = Wall_Thickness*2+0.2);
  }

  // Funnel top
  translate ([0,0,Height-Cyl_Height-Wall_Thickness])
  difference(){
    union(){
      cylinder(d1 = Top_Outside_Diameter, d2 = Top_Outside_Diameter, h = Cyl_Height-Wall_Thickness);
      cylinder(d = Wall_Thickness, h = Top_Height-Wall_Thickness);
    }
    translate ([0,0,+0.1])cylinder(d1 = Top_Outside_Diameter - Wall_Thickness*2, d2 = Top_Outside_Diameter - Wall_Thickness*2+0.2, h = Top_Height-Wall_Thickness);
    translate ([0,0,-0.1]) cylinder(d = Top_Outside_Diameter - Wall_Thickness*2, h = 0.3);
  }

  // Funnel cone
  difference(){
      cylinder(d1 = Tube_Outside_Diameter, d2 = Top_Outside_Diameter, h = Cone_Height-Wall_Thickness);
  translate ([0,0,+0.1])cylinder(d1 = Tube_Outside_Diameter - Wall_Thickness*2, d2 = Top_Outside_Diameter - Wall_Thickness*2+0.2, h = Cone_Height-Wall_Thickness);
  translate ([0,0,-0.1]) cylinder(d = Tube_Outside_Diameter - Wall_Thickness*2, h = 0.3);
  }
}

 translate ([0,0,Height])
if (true) {
  difference() {
    bubble_bottle();
    translate([0,0,-1]) {
      water_bottle();
    }
  }
} else {
  water_bottle();
}
module bubble_bottle() {
  bottle_neck(thread_depth=1.5,
	      neck_dia=Tube_Outside_Diameter,
	      threads=1,
	      thread_distance=3.96,
	      neck_height=11,
	      triangle_magic=0.01);
}

module water_bottle() {
  bottle_neck(thread_depth=0,
	      neck_dia=Tube_Outside_Diameter-2*Wall_Thickness,
	      threads=1,
	      thread_distance=1.86,
	      neck_height=13,
	      triangle_magic=11.5);
}

module bottle_neck(thread_depth,
		   neck_dia,
		   threads,
		   thread_distance,
		   neck_height,
		   triangle_magic) {
  linear_extrude(height=neck_height,
		 twist=-360*neck_height/thread_distance/threads,
		 slices=neck_height*10) {
    intersection() {
      union() {
	circle(
          r = neck_dia/2
//          , $fn=64
          );
	if (threads >= 3) {
	  circle(r = neck_dia/2 + triangle_magic, $fn = threads);
	} else if (threads == 1) {
	  translate([triangle_magic + thread_depth, 0,0]) {
	    circle(
          r = neck_dia/2 + triangle_magic
//          , $fn = 30
          );
	  }
	}
      }
      circle(
        r = neck_dia/2+thread_depth
//      , $fn=64
      );
    }
  }
}

```


[User]
I'd like to create a customizable flashlight diffuser for my Olight S1R Baton, and I'm looking for an OpenSCAD design. I've got a good idea of the basic shape and parameters, so here it is:

The diffuser will essentially be a hollow truncated cone with a ring at the base and a rounded cap. Here are the parameters I want to be able to adjust:

*   **Bottom Diameter (d1):** This should be the outer diameter of the flashlight head that the diffuser will sit on. For my Olight S1R, this is around 21.5mm.
*   **Top Diameter (d2):** This is the diameter of the top opening of the cone, which for now should be around 6mm, but I'd like to change it easily.
*   **Height of the Cone (h):** This is the height of the main cone part of the diffuser. The default value is 50mm for me.
*   **Height of the Ring (h_u):** This is the height of the ring that sits at the base, making it a tight fit around the flashlight head. I would like to start with a 5mm ring.
*   **Wall Thickness (wall):** This parameter should control the thickness of the diffuser wall. I would like to start at 1mm.

The design should be constructed using a difference of unions. First, the outer form is made with a cylinder, a translated cylinder, and a sphere. Next, we should use a difference to remove the inner space for the cone, base, and rounded cap. I'd like to use a small epsilon value to offset those subtracted shapes to reduce Z-fighting with the outer form, maybe 0.1mm for both. And I think $fn=64 would be good for a smooth cylinder.

I would prefer to have it as a single parametric OpenSCAD file, rather than multiple. I'm not familiar with any external libraries I'd like to use here, so the base OpenSCAD functions will be fine. Thanks!

[Assistant]
flashlight_diffuser.scad:
```
// Customizable Flashlight Diffusor
// by iggy@yggi.de

// default parameters fit a olight S1R baton

//Bottom Diameter / Outer Diameter of the head of the lamp
d1=21.5; //[10:0.1:80]

//Top Diameter
d2=6; //[5:0.1:50]

//Height of the cone
h=50; //[10:1:100]

//Height of the ring
h_u=5; //[0:1:20]

//Thickness of the wall
wall=1; //[0.1:0.1:3]

/* [Hidden] */

eps=0.1;
$fn=64;

difference(){
    union(){
        cylinder(d1=d1+2*wall,d2=d2+2*wall,h=h+wall);
        translate([0,0,-h_u]) cylinder(d=d1+2*wall,h=h_u);
        translate([0,0,h]) sphere(d=d2+2*wall+0.2);
    }
    translate([0,0,-eps]) cylinder(d1=d1,d2=d2,h=h);
    translate([0,0,-eps-h_u]) cylinder(d=d1,h=h_u+eps);
    translate([0,0,h]) sphere(d=d2);

}
```


[User]
Okay, so I'm looking to create a configurable PVC pipe connector in OpenSCAD, and I've seen some existing designs, but I'd like one that gives me a good amount of flexibility without being overly complicated. I need it to be parametric so I can change the size, wall thickness, segment lengths, and even the bore diameter. I’d also like control over how many segments are connected.

Here are the specifics:

*   **PVC Size Presets:** I need it to support standard PVC pipe sizes (both inch and metric). I have a table of sizes, outer diameters, socket diameters, socket depths, segment lengths and bore diameters. I will paste the table here to make sure you get it exactly correct. I would prefer that these constants be stored in a two-dimensional list so that I can perform lookups by name.

```
[
  ["3/8 inch",.9375,.687,.75,1.15625,.473],  // McMaster-Carr:  4880K109
  ["1/2 inch",1.09375,.848,.75,1.25,.602],  // McMaster-Carr:  4880K41
	["3/4 inch",1.28125,1.058,0.8125,1.375,.804],  // McMaster-Carr:  4880K42
	["1 inch",1.59375,1.325,1,1.6875,1.029],  // McMaster-Carr:  4880K43
	["1 1/4 inch",1.96875,1.67,1.25,2.125,1.36],  // McMaster-Carr:  4880K44
	["1 1/2 inch",2.25,1.912,1.3125,2.3125,1.59],  // McMaster-Carr:  4880K45
	["2 inch",2.6875,2.387,1.375,2.625,2.047],  // McMaster-Carr:  4880K46
	["2 1/2 inch",3.3125,2.889,2,3.5,2.445],  // McMaster-Carr:  4880K123
	["3 inch",3.96875,3.516,2,3.8125,3.042],  // McMaster-Carr:  4880K47
	["4 inch",5,4.518,2.03125,4.34375,3.998],  // McMaster-Carr:  4880K48
	["6 inch",7.3125,6.647,3.53125,7.03125,6.031],  // McMaster-Carr:  4880K121
  ["20 mm",25,20,16,27,18],  // McMaster-Carr:  4211N122
  ["25 mm",32,25,19,33,22.5],  // McMaster-Carr:  4211N123
  ["32 mm",38,32,22,39,28.4],  // McMaster-Carr:  4211N124
  ["50 mm",58,50,31,57,45.2],  // McMaster-Carr:  4211N125
  ["63 mm",73,63,38,71,57]  // McMaster-Carr:  4211N126
];
```

*   **Configurable Segments:** The connector should be able to have segments along the X, Y, and Z axes, each having 0, 1, or 2 segments.  A value of 0 should eliminate the segment entirely, 1 should produce a single cylinder aligned along the specified axis, and 2 should produce two cylinders aligned along the axis that connect to the central portion. Each segment should have a socket on the end (or ends if there are two).

*   **Parameter Control:** I need to be able to adjust these parameters:
    *   **Bore:**  The bore diameter through the center of the connector. This should allow me to reduce the diameter (but should never increase it beyond the socket's inner diameter).
    *   **Socket Inner Diameter:** I need to be able to adjust the socket's inner diameter by a small amount, as a percentage of the default value.
    *   **Segment Length:** The length of each segment can be adjusted by a positive amount relative to the default length.
    *   **Wall Thickness:**  I would like to be able to adjust the wall thickness of the pipe, again as a positive amount relative to the default.
    *   **Tolerance:** I should have an overall tolerance parameter as a percentage which uniformly increases or decreases all diameters.

*   **Units:** All units are in millimeters.  The inch-based measurements will need to be converted.  I'd like for you to define a conversion constant (e.g. `mm_per_inch = 25.4;`) for easy conversion.

*   **Output:** The OpenSCAD file should generate a preview of the connector, and output an `echo` statement to the console with the parameters used. Include both the preset dimensions and the "actual" dimensions used.

*   **No Libraries:** I'd like to avoid external libraries if possible.

I think that covers everything.  Let me know if anything is unclear, and please include comments in the code so I can understand how it works.

[Assistant]
PVC_Pipe_Coupler.scad:
```
/* [Hidden] */
mm_per_inch = 25.4;

// PVC - Size, OD, Socket Diameter, Socket Depth, Segment Length, Bore
PVCConnectorData =
[
  ["3/8 inch",.9375,.687,.75,1.15625,.473],  // McMaster-Carr:  4880K109
  ["1/2 inch",1.09375,.848,.75,1.25,.602],  // McMaster-Carr:  4880K41
	["3/4 inch",1.28125,1.058,0.8125,1.375,.804],  // McMaster-Carr:  4880K42
	["1 inch",1.59375,1.325,1,1.6875,1.029],  // McMaster-Carr:  4880K43
	["1 1/4 inch",1.96875,1.67,1.25,2.125,1.36],  // McMaster-Carr:  4880K44
	["1 1/2 inch",2.25,1.912,1.3125,2.3125,1.59],  // McMaster-Carr:  4880K45
	["2 inch",2.6875,2.387,1.375,2.625,2.047],  // McMaster-Carr:  4880K46
	["2 1/2 inch",3.3125,2.889,2,3.5,2.445],  // McMaster-Carr:  4880K123
	["3 inch",3.96875,3.516,2,3.8125,3.042],  // McMaster-Carr:  4880K47
	["4 inch",5,4.518,2.03125,4.34375,3.998],  // McMaster-Carr:  4880K48
	["6 inch",7.3125,6.647,3.53125,7.03125,6.031],  // McMaster-Carr:  4880K121
  ["20 mm",25,20,16,27,18],  // McMaster-Carr:  4211N122
  ["25 mm",32,25,19,33,22.5],  // McMaster-Carr:  4211N123
  ["32 mm",38,32,22,39,28.4],  // McMaster-Carr:  4211N124
  ["50 mm",58,50,31,57,45.2],  // McMaster-Carr:  4211N125
  ["63 mm",73,63,38,71,57]  // McMaster-Carr:  4211N126
];

/* [ 1 - Configuration ] */
// Resolution
resolution = 150; // [25:25:300]
$fn = resolution;

// Tolerance
tolerancePctPar = 0;
tolerancePct = tolerancePctPar / 100;

/* [ 2 - Presets] */
// Size
size = "3/4 inch"; // ["3/8 inch","1/2 inch","3/4 inch","1 inch","1 1/4 inch","1 1/2 inch","2 inch","2 1/2 inch","3 inch","4 inch","6 inch","20 mm","25 mm","32 mm","50 mm","63 mm"]

pSocketOD = search("i",size) ? PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][1] * mm_per_inch : PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][1];
pSocketID = search("i",size) ? PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][2] * mm_per_inch : PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][2];
pSocketDepth = search("i",size) ? PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][3] * mm_per_inch : PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][3];
pSegmentL = search("i",size) ? PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][4] * mm_per_inch : PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][4];
pBore = search("i",size) ? PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][5] * mm_per_inch : PVCConnectorData[(search([size],PVCConnectorData,1,0)[0])][5];
pWallT = (pSocketOD - pSocketID) / 2;

/* [ 3 - Custom] */
// Bore [mm]
borePar = 0; // [-150:150]
boreAdjust = borePar <= pBore ? borePar : pBore;
bore = (pBore + boreAdjust) <= pSocketID ? pBore + boreAdjust : pSocketID;

// Socket Inside Diameter [mm/100]
socketDPar = 0; // [-100:100]
socketD = (pSocketID + (socketDPar / 100)) * (1 + tolerancePct);

// Segment length from the origin [mm]
segmentLPar = 0; // [0:500]
segmentL = pSegmentL + segmentLPar;

socketDepth = pSocketDepth;

// Wall Thickness [mm/10]
wallTPar = 0; // [0:500]
wallT = pWallT + (wallTPar / 10);

/* [ 4 - Axis Segments] */
// X Segment Count
xSegments = 2; // [0,1,2]

// Y Segment Count
ySegments = 1; // [0,1,2]

// Z Segment Count
zSegments = 0; // [0,1,2]

segmentOD = socketD + (wallT * 2);
totalSegments = xSegments + ySegments + zSegments;

if (totalSegments)
	difference()
	{
		BuildSolid();
		CutOut();
	}
Report();

module BuildSolid()
{
	xOffset = xSegments == 1 ? segmentL / 2 : 0;
	yOffset = ySegments == 1 ? segmentL / 2 : 0;
	zOffset = zSegments == 1 ? segmentL / 2 : 0;

	if (xSegments < 2 && ySegments < 2 && zSegments < 2)
	{
		sphere(d=segmentOD,center=true);
	}
	if(xSegments > 0)
		translate([xOffset,0,0])
			rotate([0,90,0])
				BuildCylinder(xSegments,segmentL,segmentOD);
	if(ySegments > 0)
		translate([0,-yOffset,0])
			rotate([90,0,0])
				BuildCylinder(ySegments,segmentL,segmentOD);
	if(zSegments > 0)
		translate([0,0,zOffset])
			BuildCylinder(zSegments,segmentL,segmentOD);
}

module CutOut()
{
	xOffset = xSegments == 1 ? segmentL / 2 : 0;
	offset2 = (socketDepth / 2) + (segmentL - socketDepth) + .05;
	yOffset = ySegments == 1 ? segmentL / 2 : 0;
	zOffset = zSegments == 1 ? segmentL / 2 : 0;

	if (xSegments < 2 && ySegments < 2 && zSegments < 2)
	{
		sphere(d=bore,center=true);
	}

	if(xSegments > 0)
	{
		translate([xOffset,0,0])
			rotate([0,90,0])
				BuildCylinder(xSegments,segmentL,bore);
		translate([offset2,0,0])
			rotate([0,90,0])
				BuildCylinder(1,socketDepth+.1,socketD);
		if(xSegments == 2)
			mirror([1,0,0])
				translate([offset2,0,0])
					rotate([0,90,0])
						BuildCylinder(1,socketDepth+.1,socketD);
	}

	if(ySegments > 0)
	{
		translate([0,-yOffset,0])
			rotate([90,0,0])
				BuildCylinder(ySegments,segmentL,bore);
		translate([0,-offset2,0])
			rotate([90,0,0])
				BuildCylinder(1,socketDepth+.1,socketD);
		if(ySegments == 2)
			mirror([0,1,0])
				translate([0,-offset2,0])
					rotate([90,0,0])
						BuildCylinder(1,socketDepth+.1,socketD);
	}

	if(zSegments > 0)
	{
		translate([0,0,zOffset])
			BuildCylinder(zSegments,segmentL,bore);
		translate([0,0,offset2])
				BuildCylinder(1,socketDepth+.1,socketD);
		if(zSegments == 2)
			mirror([0,0,1])
				translate([0,0,offset2])
						BuildCylinder(1,socketDepth+.1,socketD);
	}
}

module BuildCylinder(segments,segmentL,segmentOD)
{
	length = segments * segmentL;

	cylinder(d=segmentOD,h=length,center=true);
}

module Report()
{
	sizeString = str(" - Size:  ",size);
	toleranceString = (tolerancePct) ? str("   Tolderance:  ",tolerancePct*100,"%") : "";

	echo();
	echo(str("Presets",sizeString));
	echo(str("Socket - OD:  ",pSocketOD, " mm   ID:  ",pSocketID," mm   Wall Thickness:  ",pWallT," mm   Depth:  ",pSocketDepth," mm"));
	echo(str("Segment - Length:  ",pSegmentL," mm   Bore:  ",pBore," mm"));

	if(borePar || segmentLPar || socketDPar || wallTPar || tolerancePct)
	{
		echo();
		echo(str("Actual Values",sizeString,toleranceString));
		echo(str("Socket - OD:  ",segmentOD, " mm   ID:  ",socketD," mm   Wall Thickness:  ",wallT," mm   Depth:  ",socketDepth," mm"));
		echo(str("Segment - Length:  ",segmentL," mm   Bore:  ",bore," mm"));
	}
		echo();
}
```


[User]
Hey, I'm looking to design a parametric SD card holder, something I can 3D print. I'd like to be able to easily adjust the number of cards it holds. I'd also like to make a few other tweaks like the overall size and style if I decide to later.

Specifically, I want it to hold standard full-size SD cards, and I'm also planning to use it for some mini-SD cards in adapters, so I need to make sure the slots are sized correctly. The design should be a simple box shape with slots cut out for each card. I think it's good to have a small bit of clearance to make it easier to get the cards in and out. Oh, and if possible, I like the idea of having a hollow core running lengthwise for a bit of extra style and using less material.

Could you generate an OpenSCAD file for this? I’m not sure if I need more than one file; you can decide if a library is necessary. I don't want to add dependencies unnecessarily, so if a simpler solution is possible without libraries that would be great. I need to control the number of slots, the length of the holder, and the width of the slots. Let me know if I’m missing anything.

[Assistant]
my_SD_card_holder.scad:
```
//Tell me how many cards you have:

/* [Slot Count] */
//The number of SD cards to accomodate
slotCount=10;

/* [Hidden] */
height=20;

shrinkFactor=1.01;
buffer=2*shrinkFactor;
cardWidth=25*shrinkFactor;
cardThickness=2.5*shrinkFactor;
length=slotCount*(cardThickness+buffer)+buffer;
width=cardWidth+buffer*2;

echo("holder is ",length,"x",width);

difference()
  {
  cube([length,width,height]);

  for (i=[0:slotCount-1])
    card(i);

  translate([-1,width/2,height/2])
    rotate([0,90,0])
      cylinder(d=height*0.9,h=length+2,$fs=.5,$fa=5);
  }

module card(position)
  {
  translate([buffer+position*(cardThickness+buffer),width/2-cardWidth/2,1])
    cube([cardThickness,cardWidth,height+1]);
  }

```


[User]
Hey, I'm looking to get an OpenSCAD design for a customizable wall mount for keystone jacks. I need it to be parametric so I can easily adjust things like the number of keystone sockets, the depth and height of the wall mount itself, and even the positions of screw holes.

I want to be able to specify:

*   **Number of keystone sockets:** This should be easily adjustable.
*   **Wall mount depth:** The distance the mount protrudes from the wall.
*   **Wall mount height:** The overall height of the mounting plate.
*   **Wall mount offset:** How much the wall mount sits proud of the keystone jack on the side.
*   **Screw holes:** I need the option to include screw holes, and to be able to define which specific keystone sockets on the mount have screw holes. I'd also like to be able to specify the dimensions for the screw head and the screw diameter and the offset of the hole from the top of the mount.
*   **Port numbering:** It would be super helpful to have an option to add numbers next to each keystone socket, with the ability to control the depth of the numbers.

The basic keystone jack dimensions are: Length of 16.5mm, a width of 15mm, and a wall thickness of 4mm all around it. It should include the clip catch features. The keystone itself is 10mm in overall height with a 4mm thick wall. The clip clearances should be 3mm and 6.5mm. The clip overhang is 2mm.

Essentially, I'd like the design to build off the basic keystone jack mounting plate geometry, and then add the customizable mounting part around it. I'd also like to be able to specify the text direction to make it readable left to right or right to left.

Can you help me create the necessary OpenSCAD file for this?

[Assistant]
keystonewallmount.scad:
```
//number of keystone sockets
number_of_sockets=3; //[1:1:20]

//depth of the wallmount (mm)
wallmount_depth=5; //[0:1:100]

//height of the wallmount (mm)
wallmount_height=40; //[10:1:100]

//offset of wallmount to the keystone
wallmount_offset=0; //[0:1:100]

//number the ports
text_enabled=true;

//depth of the numbers  (mm)
text_depth=0.4;

//screw holes in wallmount
screw_hole=true;

//positions that have screw holes
holes_at=[1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0];

//wallmount_screw_offset
wallmount_screw_offset=10;

//diameter of the screw  (mm)
screw_diameter=3.4;

//height of the screw head  (mm)
screw_head_length=4.2;

//diamter of the screw head  (mm)
screw_head_diameter=6.9;

/* Hidden */
front=false;
text_ltr=-1;

/* ************************************************************
 * Keystone code is from https://www.thingiverse.com/thing:6647
 * Mounting plate for keystone jack
 * by jsadusk https://www.thingiverse.com/jsadusk
 *
 * Changes
 * - catch_hangover from 3 to 2 (line 26)
 * - X translate of the clip_catch from 28.5 to 29 (line 76)
 */
/* Hidden */
jack_length =16.5;
jack_width = 15;

wall_height = 10;
wall_thickness = 4;

catch_overhang = 2;
small_clip_depth = catch_overhang;
big_clip_depth = catch_overhang + 2;
big_clip_clearance = 3; //4
small_clip_clearance = 6.5;

outer_length = jack_length + small_clip_depth + big_clip_depth +
                            (wall_thickness * 2);
outer_width = jack_width + (wall_thickness * 2);

module clip_catch() {
  rotate([90, 0, 0]) {
    linear_extrude(height = outer_width) {
      polygon(points = [[0,0],
                                       [catch_overhang,0],
                                       [wall_thickness,catch_overhang],
                                       [0,catch_overhang]],
                      paths = [[0,1,2,3]]);
    }
  }
}

module keystone() {
union() {

difference() {
  difference() {
    cube([outer_length, outer_width, wall_height]);
   translate([wall_thickness, wall_thickness, big_clip_clearance]) {
      cube([outer_length, jack_width, wall_height]);
    }
  }
  translate([wall_thickness + small_clip_depth, wall_thickness, 0]){
    cube([jack_length, jack_width, wall_height + 1]);
  }
}

cube([wall_thickness, outer_width, wall_height]);

cube([wall_thickness + small_clip_depth,
           outer_width, small_clip_clearance]);

translate([2, 23, 8]) {
  clip_catch();
}

translate([26.5,0,0]) {
  cube([4, 23, 10]);
}

translate([29, 0, 8]) { //28.5,0,8
  rotate([0, 0, -180]) {
    clip_catch();
  }
}

}
}
/* ************************************************************
 * end of https://www.thingiverse.com/thing:6647 code
 */

module screw()
{
	$fn=64;

	translate([screw_head_length,0,0])
	rotate([0,90,0])
		union()
		{
			cylinder(h=wallmount_depth,d=screw_diameter);
			translate([0,0,-screw_head_length])
				cylinder(r1=screw_head_diameter/2,r2=screw_diameter/2,screw_head_length);
			translate([0,0,-screw_head_length-100])
				cylinder(h=100,d=screw_head_diameter);
		}
}

module wallmount(hole=false)
{
	keystone();

	translate([outer_length-4+wallmount_offset,0,0])
	{
		difference()
		{
			union()
			{
				cube([wallmount_depth,outer_width,wallmount_height]);
				translate([-wallmount_offset+1,0,0])
					cube([wallmount_offset,outer_width,wall_height]);
			}
			union()
			{
				if(hole && screw_hole)
				{
					translate([0,outer_width/2,wallmount_height-wallmount_screw_offset])
						screw();
				}
			}
		}
	}

	if(front)
	{
		difference()
		{
			translate([0,0.2,0])
				cube([1,outer_width-0.4,wallmount_height]);

			union()
			{
				if(hole && screw_hole)
				{
					translate([0,outer_width/2,wallmount_height-wallmount_screw_offset])
						screw();
				}
			}
		}
	}
}

for(i=[1:1:number_of_sockets])
{
	translate([0,text_ltr*(i-1)*outer_width,0])
	{
		difference()
		{
			if(holes_at[i-1]==1)
			{
				wallmount(hole=true);
			}
			else
			{
				wallmount(hole=false);
			}

			if(text_enabled)
			{
				translate([text_depth,outer_width/2,1.5])
					rotate([90,0,0])
						rotate([0,-90,0])
							linear_extrude(text_depth)
								text(text=str(i),size=7,halign="center",font="DejaVu Sans:style=Regular");
			}
		}
	}
}

```


[User] I'm looking to design a bike clamp, and I've got some specific parameters I need it to be adjustable with. I want to be able to control the clamp's height, thickness, inner diameter, the space between the two clamp ends, the diameter of the screw used to fasten it, some extra space for the screw head, the diameter of the hinge screw, and finally a small space value for the printer. I also want to generate the clamp in either two halves or as a single piece, and have the option to add a GoPro mount on the second half of the clamp. Could you generate an OpenSCAD file, or files if needed, for me based on these specifications? I also want to use the included linear_extrude and circle primitives, and be able to customize polygon counts.

[Assistant]
bike_clamp.scad:
```
// clamp parameters
clamp_height = 20 * 1;
// thickness of the clamp - thicker means stronger
clamp_thickness = 8; // [5:0.1:10]
// inner diameter of the clamp
clamp_inner_diameter = 32; // [15:0.1:50]
// space between the two ends of the clamp
clamp_space = 5; // [3:0.1:10]
// diameter of the screw used to fasten the clamp
clamp_screw_diameter = 5; // [3:0.1:8]
// some extra space for the screw head and a washer
clamp_screw_head_space = 4.0; // [2:0.1:4]
// diameter of the screw for the hinge
clamp_hinge_screw_diameter = 5; // [3:0.1:8]

mount_height = clamp_height;
mount_base_width = clamp_thickness / 2 + clamp_inner_diameter / 4 + 10;
mount_base_height = 10 * 1;

// space between parts that should fit exactly - adjust this according to your printers accuracy (if the parts dont fit together increase the value slightly, if they are too loose reduce the value slightly)
s = 0.2; // [0.1:0.05:0.5]

mount_cut_thickness = 3 + 2 * s;
mount_2_height = 5 * mount_cut_thickness;

// which parts of the clamp to generate
clamp_part = "both"; //["both", "part_1", "part_2"]

// add a GoPro mount to part-2
add_go_pro_mount = 1; //[1:true,0:false]

// minimum value for difference operations to fully cut through body of the same size
e = 0.03 * 1;
// low values -> lower polygon count/size, higher values -> smoother, but bigger model
fn = 128 * 1;

module clamp_side_1() {
    hinge_diameter = clamp_hinge_screw_diameter + 4 / 3 * clamp_thickness;

    difference() {
        union() {
            difference() {
                linear_extrude(height = clamp_height, center = true)
                circle(r=clamp_thickness + (clamp_inner_diameter / 2), $fn=fn);
                union() {
                    linear_extrude(height = clamp_height + e, center=true)
                    circle(r=clamp_inner_diameter / 2, $fn = fn);
                    // remove other half (ensure we keep more at the hinge!)
                    cube_length = clamp_inner_diameter + clamp_thickness;
                    cube_width = clamp_inner_diameter + clamp_thickness + e;
                    translate([clamp_thickness, cube_width / 2 - clamp_space / 2, 0])
                    cube(size = [cube_length, cube_width, clamp_height + e], center=true);
                    translate([clamp_thickness, cube_width / 2, 0])
                    cube(size = [2 * cube_length, cube_width, clamp_height + e], center=true);
                }
            }
            // screwhole
            difference() {
                union() {
                    translate([clamp_inner_diameter / 2 + clamp_screw_diameter / 2 + clamp_screw_head_space + 9 / 10 * clamp_thickness - clamp_height / 4, -clamp_space / 2 - clamp_thickness / 2, 0])
                    cube(size = [clamp_screw_diameter + 2 * clamp_screw_head_space + clamp_thickness - clamp_height / 2, clamp_thickness, clamp_height], center=true);
                    translate([clamp_inner_diameter / 2 + clamp_screw_diameter / 2 + clamp_screw_head_space + 9 / 10 * clamp_thickness, -clamp_space / 2 - clamp_thickness / 2, 0])
                    rotate([90,0,0])
                    linear_extrude(height = clamp_thickness, center = true)
                    circle(r=clamp_height / 2, $fn=fn);
                }
                translate([clamp_inner_diameter / 2 + clamp_screw_diameter / 2 + clamp_screw_head_space + 9 / 10 * clamp_thickness, -clamp_space / 2 - clamp_thickness / 2, 0])
                rotate([90,0,0])
                linear_extrude(height = clamp_thickness + 0.01, center = true)
                circle(r=(clamp_screw_diameter + s) / 2, $fn=fn);
            }
            // hinge
            translate([-clamp_inner_diameter / 2 - hinge_diameter / 2, 0, 0])
            linear_extrude(height = clamp_height, center = true)
            circle(r=hinge_diameter / 2, $fn=fn);

        }
        // pin hole
        translate([-clamp_inner_diameter / 2 - hinge_diameter / 2, 0, 0])
        linear_extrude(height = clamp_height + e, center = true)
        circle(r=(clamp_hinge_screw_diameter + s) / 2, $fn=fn);
        // hinge part 2
        translate([-clamp_inner_diameter / 2 - hinge_diameter / 2, 0, 0])
        linear_extrude(height = (clamp_height + s) / 2, center = true)
        circle(r=hinge_diameter / 2 + s, $fn=fn);
    }
}

module clamp_side_2() {
    hinge_diameter = clamp_hinge_screw_diameter + 4 / 3 * clamp_thickness;

    difference() {
        union() {
            difference() {
                linear_extrude(height = clamp_height, center = true)
                circle(r=clamp_thickness + (clamp_inner_diameter / 2), $fn=fn);
                union() {
                    linear_extrude(height = clamp_height + e, center=true)
                    circle(r=clamp_inner_diameter / 2, $fn = fn);
                    // remove other half (ensure we keep more at the hinge!)
                    cube_length = clamp_inner_diameter + clamp_thickness;
                    cube_width = clamp_inner_diameter + clamp_thickness + e;
                    translate([clamp_thickness, -cube_width / 2 + clamp_space / 2, 0])
                    cube(size = [cube_length, cube_width, clamp_height + e], center=true);
                    translate([clamp_thickness, -cube_width / 2, 0])
                    cube(size = [2 * cube_length, cube_width, clamp_height + e], center=true);
                }
            }
            // screwhole
            difference() {
                union() {
                    translate([clamp_inner_diameter / 2 + clamp_screw_diameter / 2 + clamp_screw_head_space + 9 / 10 * clamp_thickness - clamp_height / 4, clamp_space / 2 + clamp_thickness / 2, 0])
                    cube(size = [clamp_screw_diameter + 2 * clamp_screw_head_space + clamp_thickness - clamp_height / 2, clamp_thickness, clamp_height], center=true);
                    translate([clamp_inner_diameter / 2 + clamp_screw_diameter / 2 + clamp_screw_head_space + 9 / 10 * clamp_thickness, clamp_space / 2 + clamp_thickness / 2, 0])
                    rotate([90,0,0])
                    linear_extrude(height = clamp_thickness, center = true)
                    circle(r=clamp_height / 2, $fn=fn);
                }
                translate([clamp_inner_diameter / 2 + clamp_screw_diameter / 2 + clamp_screw_head_space + 9 / 10 *clamp_thickness, clamp_space / 2 + clamp_thickness / 2, 0])
                rotate([90,0,0])
                linear_extrude(height = clamp_thickness + 0.01, center = true)
                circle(r=(clamp_screw_diameter + s) / 2, $fn=fn);
            }
            // hinge
            translate([-clamp_inner_diameter / 2 - hinge_diameter / 2, 0, 0])
            linear_extrude(height = clamp_height, center = true)
            circle(r=hinge_diameter / 2, $fn=fn);

        }
        // pin hole
        translate([-clamp_inner_diameter / 2 - hinge_diameter / 2, 0, 0])
        linear_extrude(height = clamp_height + e, center = true)
        circle(r=(clamp_hinge_screw_diameter + s) / 2, $fn=fn);
        // hinge part 2
        translate([-clamp_inner_diameter / 2 - hinge_diameter / 2, 0, (clamp_height - s) / 2])
        linear_extrude(height = (clamp_height + s) / 2, center = true)
        circle(r=hinge_diameter / 2 + s, $fn=fn);
        translate([-clamp_inner_diameter / 2 - hinge_diameter / 2, 0, -(clamp_height - s) / 2])
        linear_extrude(height = (clamp_height + s) / 2, center = true)
        circle(r=hinge_diameter / 2 + s, $fn=fn);
    }
}

module go_pro_mount() {
    difference() {
        union() {
            linear_extrude(mount_height, center = true)
            circle(r=15 / 2 + s, $fn=fn);
            linear_extrude(mount_height, center = true)
            polygon(points=[[-mount_base_width/2,-mount_base_height],[-7.1,3],[7.1,3], [mount_base_width/2,-mount_base_height]]);
        }
        linear_extrude(mount_height + e, center = true)
        circle(r=5/ 2 + s, $fn=fn);
        translate([0,0,(mount_height - 4) / 2])
        linear_extrude(4 + e, center = true)
        circle(r=8.7 / 2 + 2 * s, $fn=6);
        translate([0,0, (-mount_height + 3) / 2 + mount_cut_thickness])
        cube(size = [30, 16, mount_cut_thickness], center=true);
        translate([0,0, (-mount_height + 3) / 2 + 2 * mount_cut_thickness + 3])
        cube(size = [30, 16, mount_cut_thickness], center=true);
    }
}

module go_pro_mount_2() {
    rotate([0, 0, 180])
    translate([0,0, (mount_2_height-mount_height - 4 * s) / 2]) {
        difference() {
            union() {
                linear_extrude(mount_2_height, center = true)
                circle(r=15 / 2 + s, $fn=fn);
                linear_extrude(mount_2_height, center = true)
                polygon(points=[[-mount_base_width/2,-mount_base_height],[-7.1,3],[7.1,3], [mount_base_width/2,-mount_base_height]]);
            }
            linear_extrude(mount_2_height + e, center = true)
            circle(r=5/ 2 + s, $fn=fn);
            cube(size = [30, 16, mount_cut_thickness], center=true);

            translate([0,0, -(3/2 * mount_cut_thickness + 3)])
            cube(size = [50, 16, 2 * mount_cut_thickness], center=true);

            translate([0,0, 3/2 * mount_cut_thickness + 3])
            cube(size = [50, 16, 2 * mount_cut_thickness], center=true);
        }
    }
}

if((clamp_part=="both")||(clamp_part=="part_1")) {
    clamp_side_1();
}

if((clamp_part=="both")||(clamp_part=="part_2")) {
    if (add_go_pro_mount==1) {
        // special adjustments required because clamp intersects go-pro-mount -> need to cut here!
        difference() {
            clamp_y_translate = (clamp_inner_diameter + clamp_thickness) / 2 + mount_base_height;
            union() {
                clamp_side_2();
                translate([0, clamp_y_translate, 0])
                go_pro_mount();
            }
            translate([0, clamp_y_translate, (-mount_height + 3) / 2 + mount_cut_thickness])
            cube(size = [50, 16, mount_cut_thickness], center=true);
            translate([0, clamp_y_translate, (-mount_height + 3) / 2 + 2 * mount_cut_thickness + 3])
            cube(size = [50, 16, mount_cut_thickness], center=true);
        }
    } else {
        clamp_side_2();
    }
}

//clamp_side_1();
//clamp_side_2();
//go_pro_mount();
//go_pro_mount_2();

```


[User]
I'm looking to create a custom spacer for my GraviTrax setup, and I'd like to be able to adjust the height and some other parameters. I've seen some designs that use a hyperboloid shape for the spacer's structure, which looks pretty cool and strong.

Here's what I need:

*   **Overall Structure:** The main body should be a hyperboloid shape, created with intersecting rods, and end in a Gravitrax compatible male and female connector.
*   **Parametric Height:** The most important parameter is the height of the spacer. I need to be able to specify this and have the design adjust accordingly.
*   **Rod Thickness:** The thickness (diameter) of the rods forming the hyperboloid should be adjustable.
*   **End Plates:** There should be flat end plates (rings) at both the top and bottom of the hyperboloid structure. The height of these plates should be adjustable as well.
*   **Number of Rods:** I want to control the number of lines (rods) that make up the hyperboloid in each direction.
*   **Hyperboloid Shape:** I'd like some basic control over the shape of the hyperboloid itself, specifically the outer diameter at the ends, the bore diameter (inner diameter at the ends), and the minimum inner diameter in the middle.
*   **GraviTrax Connectors:** The end plates need to incorporate the standard GraviTrax male and female connectors, with specific widths, cutaways, and heights.
*   **Optional Support:** I would like the option to have the design generate its own support structure when the hyperboloid geometry may cause it to require support when 3D printing.
*   **Test Print Option:** Also, it would be nice to have a way to quickly generate a small test piece, say 10mm tall, with only the connectors, so I can check the tolerances and make sure they fit well before printing the full model.
*   **Print Settings:** Include a way to choose between generating the full hyperboloid spacer or the smaller test part, and include the support toggle.
*   **Rendering:** Make the rendering quality adjustable via $fn, and also have a separate sphere resolution parameter for rounded edges (if applicable).

I don’t have any specific code to share, but ideally the final result would be a single `.scad` file that allows me to adjust all these parameters. I'm hoping you can generate the complete code for this. I'm happy to try it out, adjust settings, and report back. It'd be great if it could be relatively optimized too, as I know complex models in OpenSCAD can take a while to render!

[Assistant]
Hyperboloid_Gravitrax.scad:
```
/* [ Main parameter: height of the spacer ] */
// desired height of the spacer
desired_height = 60;

/* [ Auxiliary parameters -- here you can play around ] */
// what is the diameter of the main tubes/sticks?
stick_diameter = 4;
// what is the height of one end plate/ring?
height_end_cap = 5;
// how many lines (in one direction) are in one hyperboloid?
lines_in_hyperboloid = 12;

// height of the cones at the end of the sticks
cone_height=14;
// diameter of the cone
cone_diameter=11.6;

/* [ parameters for the shape of the hyperboloid -- shouldn't be necessary to change ] */
// what outer diameter shall the object have at its ends?
outer_diameter_on_ends = 45.6;
// what is the bore diameter at the ends?
bore_diameter_at_ends = 40;
// what is the minimum inner diameter in the middle of the part?
minimum_inner_diameter = 25;

/* [ GraviTrax interface parameters -- shouldn't be necessary to change ] */
main_hexagon_width = 45.60;
male_connector_width = 29.78;
male_connector_cutaway_width = 25;
male_connector_height = 2.7;
female_connector_width = 29.80;
connector_radius = 0.8;

/* [ Print settings ] */
// How do you want to print it?
Print_setting="hyperboloid"; // [hyperboloid:print hyperboloid spacer, test: print only the connectors for checking clearances. Result is a 10mm spacer.]

// Do you want a part which doesn't need support (e.g. is self-supporting)?
Generate_support="no"; // [yes:generate support in the stl file, no: no support generated -- slicer needs to add support]

/* [ Rendering ] */
// how exact shall the rendering be?
$fn=80;
// how exact shall the sphere be which is used for rounding the edges? (Caution: too big values will cause very long run times!)
fn_sphere =12;

if (Print_setting == "hyperboloid") {
  get_hyperboloids(outer_diameter_on_ends,minimum_inner_diameter, desired_height, stick_diameter,lines_in_hyperboloid);
}

if (Print_setting == "test") {
  get_both_ends(10, 5, outer_diameter_on_ends);
}

module get_hyperboloids(outer_diameter_on_ends,minimum_inner_diameter, desired_height, stick_diameter,lines_in_hyperboloid) {
  // first calculate the necessary angle and stick length
angle = atan(sqrt((pow((outer_diameter_on_ends-stick_diameter)/2,2) - pow(minimum_inner_diameter/2,2)) / pow((desired_height-height_end_cap)/2,2)));
// daraus dann stick_length
stick_length=(desired_height-height_end_cap)/cos(angle);

union() {
hyperboloid (angle,lines_in_hyperboloid,stick_diameter/2,minimum_inner_diameter/2,stick_length);
hyperboloid (-angle,lines_in_hyperboloid,stick_diameter/2,minimum_inner_diameter/2,stick_length);

if(Generate_support=="yes") {
hyperboloid_selfsupport (angle,lines_in_hyperboloid,stick_diameter/2,minimum_inner_diameter/2,stick_length);
hyperboloid_selfsupport (-angle,lines_in_hyperboloid,stick_diameter/2,minimum_inner_diameter/2,stick_length);
}

get_both_ends(desired_height, height_end_cap, outer_diameter_on_ends);

}
}

module hyperboloid (angle,steps,radius, small_radius,stick_length) {
union() {
for (i = [0:steps-1]) {
  rotate([0,0,360/steps*i])
    rotate([angle,0,0])
      translate([small_radius,0,0]) {
        cylinder(r=radius,h=stick_length,center=true);
      }  // translate
} // for
} // union
} // module

module hyperboloid_selfsupport (angle,steps,radius, small_radius,stick_length) {
difference() {
intersection() {
union() {
for(j=[1,0])
mirror(v=[0,0,j])  // make this at the top and at the bottom
for (i = [0:steps-1]) {
  rotate([0,0,360/steps*i])
    rotate([angle,0,0])
      translate([small_radius,0,0])
       hull() {  // this is essentially the hull one needs to avoid additional support structures
        translate([0,0,stick_length/2-cone_height])
          sphere(r=radius);

        translate([0,0,stick_length/2])
          sphere(r=cone_diameter/2);
     }
} // for
} // union
translate([0,0,-desired_height/2])
  hexagon(main_hexagon_width-2*connector_radius,desired_height);
} // intersection
hull() {
// subtract the hexagon cone from the female end
translate([0,0,-desired_height/2-0.1])
  hexagon(female_connector_width,height_end_cap+0.1);
translate([0,0,0])
  sphere(r=0.01);
}
} // difference
} // module

module get_both_ends(desired_height, height_end_cap, outer_diameter_on_ends) {
difference() {
union() {
    translate([0,0,(desired_height/2-height_end_cap)])
        rounded_hexagon(main_hexagon_width,height_end_cap,connector_radius,true);
    translate([0,0,(desired_height/2)-0.1])
        rounded_hexagon(male_connector_width,male_connector_height+0.1,connector_radius,false);
} // union
    translate([0,0,(desired_height/2-2*height_end_cap)])
        hexagon(male_connector_cutaway_width,4*height_end_cap);

    // make some slopes at the male connector to end up with the female connector width
    hull() {
      translate([0,0,(desired_height/2)])
        hexagon(male_connector_cutaway_width,0.01);
      translate([0,0,(desired_height/2-height_end_cap)])
        hexagon(female_connector_width,0.01);
    }
}

difference() {

    translate([0,0,(-desired_height/2)])
        rounded_hexagon(main_hexagon_width,height_end_cap,1,true);
    translate([0,0,(-desired_height/2-2*height_end_cap)])
        hexagon(female_connector_width,4*height_end_cap);
}
}

module get_end(filled) {
    minkowski() {
    if(filled) {
      cylinder(r=outer_diameter_on_ends/2-radius_on_end_rings,h=height_end_cap,center=true);
    }
    else {
    difference() {
    cylinder(r=outer_diameter_on_ends/2-radius_on_end_rings,h=height_end_cap,center=true);
    cylinder(r=bore_diameter_at_ends/2+radius_on_end_rings,h=2*height_end_cap,center=true);
    }
    }
    sphere(r=radius_on_end_rings, $fn=fn_sphere);
    }

}

// will draw a hexagon with given width and height
module hexagon(width,height) {
/* the only calculation to be done is to get the second dimension of the base cube:
    - in the corner there's a 60° angle
    - r*sin(60)=width/2
    - r*cos(60)=depth/2
    therefore depth = 2*r*cos(60) = 2*width/(2*sin(60))*cos(60)
    depth = width*cos(60)/sin(60) = width/tan(60)
*/
depth = width/tan(60);
translate([0,0,height/2])
union() {
for (i=[0,120,240])
  rotate([0,0,i])
    cube([width,depth,height],center=true);
}
}

// will produce a hexagon with rounded edges
// parameter bottom_round will define if the bottom shall also be rounded or not
module rounded_hexagon(width, height, radius, bottom_round) {
  smaller_width = width - 2*radius;
  smaller_height = height - (bottom_round? 2*radius: radius);

  if(bottom_round) {
    translate([0,0,radius])
      round_edge(radius)hexagon(smaller_width, smaller_height);
  }
  else {
    difference() {
      round_edge(radius)hexagon(smaller_width, smaller_height);
      translate([0,0,-2*radius])
        cylinder(r=width,h=radius*2);
    }
  }

}

// will produce round edges by the minkowski function. But the object will grow in each dimension by "radius"
module round_edge(radius) {
  minkowski() {
    children(0);
    sphere(radius);
  }
}

/* calculation stuff -- just to remid me where the formulae came from
  desired_height=cos(angle)*stick_length;
  outer_diameter_on_ends = 2*sqrt((minimum_inner_diameter/2)^2 + (sin(angle)*stick_length/2)^2);
  = 2*sqrt((minimum_inner_diameter/2)^2 + (sin(angle)*desired_height/(cos(angle)*2))^2);
  = 2*sqrt((minimum_inner_diameter/2)^2 + (tan(angle)*desired_height/2)^2);

  two equations, two variables -- apply substitution

stick_length=desired_height/cos(angle)
outer_diameter_on_ends = 2*sqrt((minimum_inner_diameter/2)*(minimum_inner_diameter/2) + (tan(angle)*desired_height/2)*(tan(angle)*desired_height/2));
*/

// Johann Schuster, January 2025, v 0.6
```


[User]
I'm looking to design a small box with a latch and a hinged lid, and I'd like your help generating the OpenSCAD code. I've got some specific requirements, but I want to keep it parametric so I can easily adjust the size and other details later.

Here's the general idea:
- The box should be rectangular, and I need to be able to set the inside dimensions of the box, as well as the inside height of the lid. This should include the space between the lid and base when closed.
- There should be a latch mechanism on one side using bolts - this mechanism needs to be designed so the bolt head acts as a catch when the box is closed. I'll need to be able to define the bolt dimensions (diameter, head diameter, length) and the position of the axes for the latch.
- The lid should be hinged on the back of the box using a bolt. I will need to be able to control the size of this bolt as well.
- It would be great if the design incorporated rounded edges, with the radius configurable.
- I need to be able to specify a gap between mating parts, both in the XY plane and along the Z-axis.
- There should also be a "preassure" parameter that determines how much the lid will shift in the X direction to apply a small force when closed.
- I will need a parameter for the wall thickness.
- And finally, the latch mechanism needs to have a bit of an extra gap around the bolt to make space for the bolt head, and this needs to be configurable.

I'd like to be able to control the location of the latch axis points, and these should be relative to the position of the box itself.

I'm envisioning that the code would be structured in a way that I can render each of the parts separately for printing (the base, the lid, and possibly the latch components), so I'll need some variables to enable/disable rendering of each of these.

For libraries, I'd expect this would use the BOSL2 library, specifically for the rounded cube and rounded cylinder modules. I'm also expecting to need a module for producing rounded edges of the non-BOSL variety for those parts of the lid and the base where the rounded edge does not extend the full length of the part.

Thanks!

[Assistant]
box_with_latch.scad:
```
$fn=16;

showPart1=1;
showPart2=1;
showPart3=1;

//inside dimensions of the box including the lid space
box=[128,100,29];
//inside height of lid
lidH=9;

width=40;
gap=0.2;
gapZ=0.4;
layer=0.2;
wall=1;
///location of axes: x coord of 1st and 3rd axes, y coord. of 2nd axis
pos=[-6,3,10];
///shift of top to gain a preassure when closed
preassure=1;

//negative=20;
latchBoxGap=1;

bolt=3;
boltL=22;
boltHole=3.6;
boltHead=6.5;
boltHeadL=3;

r1=0.5;
r2=3;
r3=max(0,r2-wall);

cutThrough=0;
not=0.01;
inf=200;

widthIn=width-2*boltHeadL-2*gap;
axis=[bolt,boltHead-not];
sideW=pos[1]+axis[1]+wall+gap+latchBoxGap;

//////////////////////////////////////////////////////////////////////

//for visualisation and printing
angles=[90,-100];

difference(){
	u(){

		t([axis[1]/2,0,-width/2]){
			if(showPart3) axes(angles);
			if(showPart3) r(angles[0],[0,pos[1]+axis[0]/2,0]) r(angles[1],[pos[2],axis[1]/2,0]) cover();
			if(showPart2) t([-preassure,0,0]) catch();
			if(showPart1) bottom();
			if(showPart1) r(angles[0],[0,pos[1]+axis[0]/2,0]) connect();
		}

		if(showPart1) box();
		if(showPart2) lid();
	}

	if(cutThrough) t([-inf,-inf,0]) cube([2*inf,2*inf,box.x/2]);
}

//////////////////////////////////////////////////////////////////////

module space(h=10,sideHinge=true){
	difference(){
		u(){
			cubeRounded([h+wall,box.x+2*wall,box.y+2*wall],r=[r2,r2,r2],r4=[not,r2,r2],r6=[not,r2,r2],r0=[not,r2,r2],r2=[not,r2,r2],center=[0,2,1]);

			sideW=pos[1]+axis[1]+wall+gap+latchBoxGap;
			for(b=[0,1]) mirror([0,0,b]) t([0,sideW,width/2+2*gap]) cubeRounded([h+wall,box.x+2*wall+sideW,(box.y+2*wall-width)/2-2*gap],r=[r2,r2,r2],r4=[not,r2,r2],r6=[not,r2,r2],r0=[not,r2,r2],r2=[not,r2,r2],center=[0,2,0]);

		}

		t([h,-wall,0]) cubeRounded([2*box.z,box.x,box.y],r=[r3,r3,r3],center=[2,2,1]);

		for(b=[0,1]) mirror([0,0,b]) t([-wall-inf,sideW-wall,width/2+2*gap+wall]) cubeRounded([h+wall+inf,box.x+2*wall+sideW-2*wall,(box.y+2*wall-width)/2-2*gap-2*wall],r=[r3,r3,r3],center=[0,2,0]);
	}

	//hinge
	if(sideHinge){
		l=box.y+2*wall-2*r2-2*boltHeadL-boltL-2*gap;
		t([0,-box.x-2*wall-boltHead/2-gap,0]) difference(){
			u(){
				cylinder(d=boltHead,h=l,center=true);
				hull() intersection(){
					u(){
						cylinder(d=boltHead,h=l,center=true);
						t([0,gap+boltHead/2,0]) cubeRounded([boltHead+gap+wall/2,wall/2,l],r=[r1,r1,r1],center=[0,0,1]);
					}
					cubeRounded([inf,inf,l],center=[0,1,1]);
				}
			}

			cylinder(d=boltHole,h=inf,center=true);
		}
	} else {
		t([0,-box.x-2*wall-boltHead/2-gap,0]) difference(){
			for(b=[0,1]) mirror([0,0,b]) t([0,0,-box.y/2-wall+r2+boltHeadL]){
				cylinder(d=boltHead,h=boltL/2);
				cube([boltHead/2,wall+boltHead/2,boltL/2]);

				maxZ=min(h+wall,boltHead+gap+wall/2);
				shiftY=(boltHead+gap+wall/2)-maxZ;

				hull() intersection(){
					u(){
						cylinder(d=boltHead,h=boltL/2);
						t([0,gap+boltHead/2-shiftY,0]) cubeRounded([maxZ,wall+shiftY,boltL/2],r=[r1,r1,r1]);
					}
					cubeRounded([inf,inf,boltL],center=[0,1,1]);
				}
				if(shiftY>0) t([h,gap+boltHead/2-shiftY,0]) cubeRounded([wall,r2+r1+shiftY,boltL/2],r=[r1,r1,r1]);
			}

			cylinder(d=bolt,h=inf,center=true);
		}
	}
}

module lid(){
	mirror([1,0,0]) t([layer,-latchBoxGap,0]) space(lidH,false);
}

module box(){
	t([0,-latchBoxGap,0]) space(box.z-lidH);
}

module catch(){
	t([axis[0]/4,0,0]) difference(){
		t([pos[0],-(latchBoxGap+wall),boltHeadL+gap]) cubeRounded([axis[0]/2+wall,axis[0]+gap+wall+latchBoxGap+wall,widthIn],r=[r1,r1,r1]);

		hull(){
			t([pos[0]-axis[0]/4,axis[0]/2,0]) cylinder(d=axis[0]+2*gap,h=width);
			t([pos[0]-gap-inf,axis[0]/2,0]) cylinder(d=axis[0]+2*gap,h=width);
		}

	}
}

module connect(){
	t([0,0,boltHeadL+gap]){
		difference(){
			hull(){
				t([0,pos[1]+axis[0]/2,0]) cylinderRounded(d=[0,axis[1]],h=widthIn,r=[0,r1,0,r1]);
				t([pos[2],axis[1]/2,0]) cylinderRounded(d=[0,axis[1]],h=widthIn,r=[0,r1,0,r1]);
			}

			t([0,pos[1]+axis[0]/2,widthIn/4-gap/2]) cubeRounded([axis[1]+2*gap,2*axis[1],widthIn/2+gap],center=[1,1,0]);

			t([pos[2],axis[1]/2,-not]) cylinder(d=bolt,h=width);
		}
		t([-(gapZ-gap),pos[1]+axis[0]/2,-not]) cylinder(d=axis[0],h=widthIn);
	}
}

module bottom(){
	t([0,0,boltHeadL+gap]) difference(){
		hull(){
			cubeRounded([axis[1],wall+latchBoxGap,widthIn/2-gap],r=[r1,r1,1],center=[1,2,-1]);
			t([0,pos[1]+axis[0]/2,widthIn/4+gap/2]) cylinderRounded(d=[0,axis[1]],h=widthIn/2-gap,r=[0,r1,0,r1]);
		}

		t([0,pos[1]+axis[0]/2,0]) cylinder(d=axis[0]+gap+gapZ,h=inf,center=true);
	}
}

module axes(angles=[0,0]){
	r(angles[0],[0,pos[1]+axis[0]/2,0]) r(angles[1],[pos[2],axis[1]/2,0]) {
		t([pos[0]-axis[0]/2-wall/2,0,wall]) cube([axis[0]/2+wall/2,axis[0],width-2*wall]);
		t([pos[0],axis[0]/2,wall]) cylinder(d=axis[0],h=width-2*wall);
	}

	//t([0,pos[1]+axis[0]/2,wall+gap]) cylinder(d=axis[0],h=widthIn);
	//r(angles[0],[0,pos[1]+axis[0]/2,0]) t([pos[2],axis[1]/2,wall]) cylinder(d=axis[0],h=width-2*wall);
}

module cover(){
	difference(){
		t([pos[0]-axis[0]/2-wall,0,0]) difference(){
			cubeRounded([pos[2]-pos[0]+(axis[0]+axis[1])/2+gap+2*wall,pos[1]+axis[1]+wall+gap,width],r=[r1,r1,r1]);

			t([-not+wall,-not,boltHeadL]) cube([pos[2]-pos[0]+(axis[0]+axis[1])/2+gap,pos[1]+axis[1]+gap,width-2*boltHeadL]);

		}

		//r(angles[0],[0,pos[1]+axis[0]/2,0])
		t([pos[2],axis[1]/2,0]) cylinder(d=boltHead,h=width);
	}
}

//////////////////////////////////////////////////////////////////////

module cubeRounded(
	size=[3,3,3],
	r=[0,0,0],
	r0=[-1,-1,-1],
	r1=[-1,-1,-1],
	r2=[-1,-1,-1],
	r3=[-1,-1,-1],
	r4=[-1,-1,-1],
	r5=[-1,-1,-1],
	r6=[-1,-1,-1],
	r7=[-1,-1,-1],
	chamfer=[0,0,0,0],
	chamferAngle=60,
	center=[0,0,0],
	not=0.00001
){

	module halfTriangle3D(angle=45,h=10,z=10){
		linear_extrude(z) polygon([[0,0],[h*tan(angle),0],[0,h]]);
	}
	module tetrahedron(height=0) {
		length = 2 * height / sqrt(3);
		translate([-height / sqrt(3), -height / 3, -height / 3])
			polyhedron(
				points = [[0, 0, 0], [length, 0, 0], [length/2, height, 0], [length/2, height/3, height]],
				faces = [[0, 1, 2], [0, 2, 3], [1, 3, 2], [0, 3, 1]]
			);
	}

	function sat3(a) = [max(not,a.x),max(not,a.y),max(not,a.z)];
	function chg(a, b) = (a.x==-1 && a.y==-1 && a.z==-1) ? b : a;

	_r0 = sat3(chg(r0, r));
	_r1 = sat3(chg(r1, r));
	_r2 = sat3(chg(r2, r));
	_r3 = sat3(chg(r3, r));
	_r4 = sat3(chg(r4, r));
	_r5 = sat3(chg(r5, r));
	_r6 = sat3(chg(r6, r));
	_r7 = sat3(chg(r7, r));

	rot= $fn<8 ? 0 : 360/$fn/2;

	module cubeR(){
		hull(){
			t(_r0) if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r0) intersection(){
					r(rot) sphere(1);
					mirror([1,0,0]) mirror([0,1,1]) cube(1);
				}
			}

			t([size.x-_r1.x,_r1.y,_r1.z])  if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r1) intersection(){
					r(rot) sphere(1);
					mirror([0,1,1]) cube(1);
				}
			}

			t([_r2.x,size.y-_r2.y,_r2.z])  if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r2) intersection(){
					r(rot) sphere(1);
					mirror([1,0,1]) cube(1);
				}
			}

			t([size.x-_r3.x,size.y-_r3.y,_r3.z])  if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r3) intersection(){
					r(rot) sphere(1);
					mirror([0,0,1]) cube(1);
				}
			}

			t([_r4.x,_r4.y,size.z-_r4.z])  if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r4) intersection(){
					r(rot) sphere(1);
					mirror([1,1,0]) cube(1);
				}
			}

			t([size.x-_r5.x,_r5.y,size.z-_r5.z])  if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r5) intersection(){
					r(rot) sphere(1);
					mirror([0,1,0]) cube(1);
				}
			}

			t([_r6.x,size.y-_r6.y,size.z-_r6.z])  if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r6) intersection(){
					r(rot) sphere(1);
					mirror([1,0,0]) cube(1);
				}
			}

			t([size.x-_r7.x,size.y-_r7.y,size.z-_r7.z])  if(_r0==[not,not,not]){
				tetrahedron(not);
			} else {
				scale(_r7) intersection(){
					r(rot) sphere(1);
					mirror([0,0,0]) cube(1);
				}
			}
		}

		//chamfering
		if(chamfer[0]) r([0,90,0]) halfTriangle3D(90-chamferAngle,size.y/(chamfer[2]+1),size.x);

		if(chamfer[2]) tr([0,size.y,0],[0,90,0]) mirror([0,1,0]) halfTriangle3D(90-chamferAngle,size.y/(chamfer[0]+1),size.x);

		if(chamfer[1]) tr([0,size.y,0],[90,90,0]) halfTriangle3D(90-chamferAngle,size.x/(chamfer[3]+1),size.y);

		if(chamfer[3]) tr([size.x,size.y,0],[90,90,0]) mirror([0,1,0]) halfTriangle3D(90-chamferAngle,size.x/(chamfer[1]+1),size.y);
	}

	t([-size.x/2*center.x,-size.y/2*center.y,-size.z/2*center.z]) cubeR();
}

module cylinderRounded(d=[20,40],h=30,center=false,r=[1,-10,4,1]){
	translate([0,0,-h/2* (center?1:0)])
	rotate_extrude(convexity = 10)
	{
		//square
		difference(){
			translate([d[0]/2,0,0]) square([(d[1]-d[0])/2,h]);
			if(r[0]>0) translate([d[0]/2,0,0]) square([r[0],r[0]]);
			if(r[1]>0) translate([d[1]/2-r[1],0,0]) square([r[1],r[1]]);
			if(r[2]>0) translate([d[0]/2,h-r[2],0]) square([r[2],r[2]]);
			if(r[3]>0) translate([d[1]/2-r[3],h-r[3],0]) square([r[3],r[3]]);
		}

		//corners

		if(r[0]>0){
			intersection(){
				translate([d[0]/2+r[0], r[0], 0]) circle(r = r[0]);
				translate([d[0]/2,0,0]) square([r[0],r[0]]);
			}
		} else {
			difference(){
				translate([d[0]/2+r[0], 0, 0]) square([-r[0],-r[0]]);
				translate([d[0]/2+r[0], -r[0], 0]) circle(r = -r[0]);
			}
		}

		if(r[1]>0){
			intersection(){
				translate([d[1]/2-r[1], r[1], 0]) circle(r = r[1]);
				translate([d[1]/2-r[1],0,0]) square([r[1],r[1]]);
			}
		} else {
			difference(){
				translate([d[1]/2, 0, 0]) square([-r[1],-r[1]]);
				translate([d[1]/2-r[1], -r[1], 0]) circle(r = -r[1]);
			}
		}

		if(r[2]>0){
			intersection(){
				translate([d[0]/2+r[2], h-r[2], 0]) circle(r = r[2]);
				translate([d[0]/2,h-r[2],0]) square([r[2],r[2]]);
			}
		} else {
			difference(){
				translate([d[0]/2+r[2], h+r[2], 0]) square([-r[2],-r[2]]);
				translate([d[0]/2+r[2], h+r[2], 0]) circle(r = -r[2]);
			}
		}

		if(r[3]>0){
			intersection(){
				translate([d[1]/2-r[3], h-r[3], 0]) circle(r = r[3]);
				translate([d[1]/2-r[3],h-r[3],0]) square([r[3],r[3]]);
			}
		} else {
			difference(){
				translate([d[1]/2, h+r[3], 0]) square([-r[3],-r[3]]);
				translate([d[1]/2-r[3], h+r[3], 0]) circle(r = -r[3]);
			}
		}
	}
}

module t(v=[0,0,0]){translate(v) children();}
module r(a=[0,0,0],rv=[0,0,0]){translate(rv) rotate(a) translate(-rv) children();}
module tr(v=[0,0,0],a=[0,0,0]){t(v) r(a) children();}
module rt(a=[0,0,0],v=[0,0,0]){r(a) t(v) children();}
module u(){union() children();}

```