[User]
I'm looking to design a customizable clamp, kind of like a G-clamp but fully 3D printable and with some specific features. I've seen some existing designs, but I want something that I can really tweak to fit different needs, and I'd like to use OpenSCAD to do it.

Here's what I'm after:

**Overall Structure:**

*   The clamp should have a main body (the "base"), a screw-type bolt, and a press piece at the end of the bolt that makes contact with the object being clamped.
*   I want control over the length, width, and depth of the base, as well as its thickness.
*   It should be possible to choose between rounded or square corners for the base. If rounded, I'd like to control the radius and smoothness of the rounding.
*   There needs to be a threaded hole in the base for the bolt.

**Bolt:**

*   The bolt needs to have a knob for easy turning, and I'd like to control the knob's length and diameter.
*   I need a way to adjust the knurled finish on the knob, specifically the fineness of the points.
*   The screw part of the bolt needs to have a diameter and length that I can specify. I also need to be able to reduce the screw length to keep it from getting in the way for small objects being clamped.
*   I'd like to be able to preview the assembled clamp with the screw partially tightened, so I need a parameter to control the bolt's position.
*   The end of the screw should have a ball on it.

**Press Piece:**

*   The press piece should have a diameter and thickness that I can set.
*   It should have a ball-and-socket type joint with the ball on the end of the screw so it can rotate.
*   I'd like to be able to set the clearance for the ball-and-socket joint to get the fit right for my printer.

**Other Considerations:**

*   I want to be able to turn different parts of the clamp on/off for previewing purposes (e.g. just the base, just the screw, etc).
*   Also, I don't want it to generate an assembled model if I'm generating files for printing.
*   I'd like to use the `polyScrewThread_r1.h` library for the screw thread and `knurledFinishLib.h` for the knurling on the knob.

I think that covers all the main points for my initial design.  I'd like to be able to adjust all the dimensions, and have the code be parametric so I can test out different ideas. I'm mainly concerned with printability and functionality.

[Assistant]
clampV3.4.8.scad:
```
// clamp customizable akaJes 2016 (c)
// tweaked for increasing thickness not affecting other parts, renamed dimensions, adjustable knurled finish, made more adjustments available and updated screw library and fixed warnings created by it. - 247generator 2018

//include <polyScrewThread_r1.h>
//include <knurledFinishLib.h>

// preview[view:east]

/*[visibility]*/
//
show_clamp=1; //[0:No,1:Yes]
show_bolt=1; //[0:No,1:Yes]
show_press=1; //[0:No,1:Yes]
// do not create assembled for printing :)
show_assembled=1; //[0:No,1:Yes]
/*[clamp]*/
clamp_length=60;
clamp_width=35;
clamp_depth=60;
// minimum 5, do not exceed clamp length
clamp_thickness=15;
// round is stronger
corner_shape=0;//[0:Round,1:Square]
// radius of ball that makes round edges
rounding=2;
// how smoothly rounded or polygonal the rounding is (24 is plenty)
roundedness=8; //[4:4:64]
// adjusts how tight the threads mesh - don't change unless it's too sloppy or tight. (4.3% x 16mm is a 0.688mm gap)
bolt_hole_space_factor=4.3;
/*[bolt]*/
knob_length=15;
knob_diameter=24;
// a lower number makes finer points: 180 felt good, 250 is sharp but better grip, 400 previews quickly.
knob_knurled_finish=400;
screw_diameter=16;
// keeps the bolt from getting in the way but limits it to clamping objects bigger this amount
screw_length_reduction=0;
// shows the preview opened by this much:
assembled_screw_position=0;
/*[press]*/
press_diameter=24;
// 2 is zero thickness for press diameter: can help with clearance on shorter clamps with a wide enough screw.
press_thickness=5;//[2:5]
// 0.3mm fit snug and 0.6mm was too loose on my prints
ball_and_socket_clearance=0.3;

/*[hidden]*/
// resets the zoom every time, comment out or change as needed
//$vpd = 300;
radius=1.8; //rounded corners
diameter_ball=8; //and socket
screw_length=clamp_length+clamp_thickness-13-(rounding*4)-screw_length_reduction;
length=clamp_length-rounding*2;
width=clamp_width-rounding*2;
depth=clamp_depth-rounding*2;
thickness=clamp_thickness-rounding*2;
hole_shift=depth-min(clamp_width/2,depth/2);
screw_pos=assembled_screw_position;
knurled_finish=(knob_knurled_finish/100);

 if (show_assembled)rotate([0,0,-90]){
  base();
  translate([-knob_length-radius*thickness-rounding-screw_pos,hole_shift,clamp_width/2]){
    rotate([0,90,0])bolt();
    translate([knob_length+screw_length+5+8,0,0])rotate([0,-90,0])press();
  }
}else{
  if(show_clamp)
    base();
  if(show_press)
    translate([max(30,knob_diameter),depth+20,0])press();
  if(show_bolt)
    translate([0,depth+max(20,knob_diameter*0.75),0])bolt();
}

module corner(tr){
  intersection(){
    difference(){
        // outside corner
      cylinder(r=tr,h=width,$fn=64);
      translate([0,0,-.1])
        // inside corner
        cylinder(r=thickness*(radius-1),h=width+.2,$fn=32);
    }
    translate([-tr,-tr,0])cube([tr,tr,width]);
  }
}

module s_corner(tr){
    ti=thickness*(radius-1);
  intersection(){
    difference(){
        // outside corner
      cylinder(r=tr,h=width,$fn=64);
      translate([0,0,-.1])
        // inside corner
        cylinder(r=thickness*(radius-1),h=width+.2,$fn=32);
        // square corner option
              translate([0,0*(-rounding-thickness/2),(width/2)-.1])
        cube([2*ti*corner_shape,2*ti*corner_shape,corner_shape*(width+.2)],center=true);

    }
    translate([-tr,-tr,0])cube([tr,tr,width]);
  }
}

module acorner(tr){
    ti=thickness*(radius-1);
    echo (tr);
    echo (ti);
  intersection(){
    difference(){
        // outside corner
      cylinder(r=tr,h=width,$fn=64);
        // inside corner
      translate([0,-rounding-thickness/2,-.1])
        cylinder(r=thickness*(radius-1),h=width+.2,$fn=32);
        // remove the remaining inside corner for asym corner
        translate([-ti,(-ti/2)-rounding,0])cube([ti,ti,width]);
    }
    translate([-tr,-tr,0])cube([tr,tr,width]);
  }
}
module base(){
  translate([0,0,rounding])
    difference(){
    minkowski(){
        //union(){
      tr=thickness*radius;
      ti=thickness*(radius-1);
        union(){
            // top corner
        corner(tr);
            // top flat
        translate([-tr,0,0])cube([thickness,depth,width]);
            // side flat
        translate([0,-tr,0])cube([length-ti*2,thickness,width]);
            // bottom corner
        translate([length-ti*2,0,0])rotate([0,0,90])s_corner(tr);
            // bottom flat
        translate([length-ti,0,0])cube([thickness,depth,width]);
                }
      sphere(r=rounding,$fn=roundedness);
    }
    translate([0.1-clamp_thickness*0.8+rounding*2+(rounding*0.6),hole_shift,width/2])rotate([0,-90,0])
   {
       screw_thread(screw_diameter+((bolt_hole_space_factor/100)*screw_diameter),4,55,clamp_thickness+0.2,PI/2,2);
       translate ([0,0,0.2]) hex_countersink_ends(1.5,screw_diameter,55,2,clamp_thickness-0.2);
   }
  }
}
module bolt(){
    knurled_cyl(knob_length, knob_diameter, knurled_finish, knurled_finish, 1, 2, 10);
    translate([0,0,knob_length]) screw_thread(screw_diameter,4,55,screw_length,PI/2,2);
    translate([0,0,knob_length+screw_length])cylinder(h=6,d=6,$fn=30);
    translate([0,0,knob_length+screw_length+5])sphere(d=diameter_ball,$fn=32);
}
module rounded(h=5,d=20,r=2){
  translate([0,0,r])
    minkowski(){
      cylinder(d=d-r*2,h=h-r*2, $fn=64);
      sphere(r=r, $fn=roundedness);
    }
}
module press(){
    $fn=32;
    difference(){
      union(){
        rounded(h=press_thickness,d=press_diameter,r=1);
        rounded(h=10,d=14,r=1);
        }
      translate([0,0,10-2])sphere(d=diameter_ball+ball_and_socket_clearance);
      for (i=[0,120,240])
        translate([0,0,6.5])rotate([90,0,90+i])hull(){
          cylinder(d=3,h=10);
          translate([0,5,0])cylinder(d=3,h=10);
        }
    }
}

/*
 * knurledFinishLib.scad
 *
 * Written by aubenc @ Thingiverse
 *
 * This script is licensed under the Public Domain license.
 *
 * http://www.thingiverse.com/thing:9095
 *
 * Usage:
 *
 *    knurled_cyl( Knurled cylinder height,
 *                 Knurled cylinder outer diameter,
 *                 Knurl polyhedron width,
 *                 Knurl polyhedron height,
 *                 Knurl polyhedron depth,
 *                 Cylinder ends smoothed height,
 *                 Knurled surface smoothing amount );
 */
module knurled_cyl(chg, cod, cwd, csh, cdp, fsh, smt)
{
    cord=(cod+cdp+cdp*smt/100)/2;
    cird=cord-cdp;
    cfn=round(2*cird*PI/cwd);
    clf=360/cfn;
    crn=ceil(chg/csh);

    intersection()
    {
        shape(fsh, cird, cord-cdp*smt/100, cfn*4, chg);

        translate([0,0,-(crn*csh-chg)/2])
          knurled_finish(cord, cird, clf, csh, cfn, crn);
    }
}

module shape(hsh, ird, ord, fn4, hg)
{
        union()
        {
            cylinder(h=hsh, r1=ird, r2=ord, $fn=fn4, center=false);

            translate([0,0,hsh-0.002])
              cylinder(h=hg-2*hsh+0.004, r=ord, $fn=fn4, center=false);

            translate([0,0,hg-hsh])
              cylinder(h=hsh, r1=ord, r2=ird, $fn=fn4, center=false);
        }

}

module knurled_finish(ord, ird, lf, sh, fn, rn)
{
    for(j=[0:rn-1])
    {
    h0=sh*j;
    h1=sh*(j+1/2);
    h2=sh*(j+1);
        for(i=[0:fn-1])
        {
            lf0=lf*i;
            lf1=lf*(i+1/2);
            lf2=lf*(i+1);
            polyhedron(
                points=[
                     [ 0,0,h0],
                     [ ord*cos(lf0), ord*sin(lf0), h0],
                     [ ird*cos(lf1), ird*sin(lf1), h0],
                     [ ord*cos(lf2), ord*sin(lf2), h0],

                     [ ird*cos(lf0), ird*sin(lf0), h1],
                     [ ord*cos(lf1), ord*sin(lf1), h1],
                     [ ird*cos(lf2), ird*sin(lf2), h1],

                     [ 0,0,h2],
                     [ ord*cos(lf0), ord*sin(lf0), h2],
                     [ ird*cos(lf1), ird*sin(lf1), h2],
                     [ ord*cos(lf2), ord*sin(lf2), h2]
                    ],
                faces=[
                     [0,1,2],[2,3,0],
                     [1,0,4],[4,0,7],[7,8,4],
                     [8,7,9],[10,9,7],
                     [10,7,6],[6,7,0],[3,6,0],
                     [2,1,4],[3,2,6],[10,6,9],[8,9,4],
                     [4,5,2],[2,5,6],[6,5,9],[9,5,4]
                    ],
                convexity=5);
         }
    }
}

/*
 *    polyScrewThread_r1.scad    by aubenc @ Thingiverse
 *
 * This script contains the library modules that can be used to generate
 * threaded rods, screws and nuts.
 *
 * http://www.thingiverse.com/thing:8796
 *
 * CC Public Domain
 */

module screw_thread(od,st,lf0,lt,rs,cs)
{
    or=od/2;
    ir=or-st/2*cos(lf0)/sin(lf0);
    pf=2*PI*or;
    sn=floor(pf/rs);
    lfxy=360/sn;
    ttn=round(lt/st+1)+1;
    zt=st/sn;

    intersection()
    {
        if (cs >= -1)
        {
           thread_shape(cs,lt,or,ir,sn,st);
        }

        full_thread(ttn,st,sn,zt,lfxy,or,ir);
    }
}

module hex_nut(df,hg,sth,clf,cod,crs)
{

    difference()
    {
        hex_head(hg,df);

        hex_countersink_ends(sth/2,cod,clf,crs,hg);

        screw_thread(cod,sth,clf,hg,crs,-2);
    }
}

module hex_screw(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);

    union()
    {
        hex_head(hg,df);

        translate([0,0,hg])
        if ( ntl == 0 )
        {
            cylinder(h=0.01, r=ntr, center=true);
        }
        else
        {
            if ( ntd == -1 )
            {
                cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            }
            else if ( ntd == 0 )
            {
                union()
                {
                    cylinder(h=ntl-st/2,
                             r=od/2, $fn=floor(od*PI/rs), center=false);

                    translate([0,0,ntl-st/2])
                    cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                }
            }
            else
            {
                cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            }
        }

        translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    }
}

module hex_screw_0(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd)
{
    ntr=od/2-(st/2)*cos(lf0)/sin(lf0);

    union()
    {
        hex_head_0(hg,df);

        translate([0,0,hg])
        if ( ntl == 0 )
        {
            cylinder(h=0.01, r=ntr, center=true);
        }
        else
        {
            if ( ntd == -1 )
            {
                cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false);
            }
            else if ( ntd == 0 )
            {
                union()
                {
                    cylinder(h=ntl-st/2,
                             r=od/2, $fn=floor(od*PI/rs), center=false);

                    translate([0,0,ntl-st/2])
                    cylinder(h=st/2,
                             r1=od/2, r2=ntr,
                             $fn=floor(od*PI/rs), center=false);
                }
            }
            else
            {
                cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false);
            }
        }

        translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs);
    }
}

module thread_shape(cs,lt,or,ir,sn,st)
{
    if ( cs == 0 )
    {
        cylinder(h=lt, r=or, $fn=sn, center=false);
    }
    else
    {
        union()
        {
            translate([0,0,st/2])
              cylinder(h=lt-st+0.005, r=or, $fn=sn, center=false);

            if ( cs == -1 || cs == 2 )
            {
                cylinder(h=st/2, r1=ir, r2=or, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }

            translate([0,0,lt-st/2])
            if ( cs == 1 || cs == 2 )
            {
                  cylinder(h=st/2, r1=or, r2=ir, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }
        }
    }
}

module full_thread(ttn,st,sn,zt,lfxy,or,ir)
{
  if(ir >= 0.2)
  {
    for(i=[0:ttn-1])
    {
        for(j=[0:sn-1])
        {
			 pt =  [	[0,                  0,                  i*st-st            ],
                        [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt-st       ],
                        [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt-st   ],
								[0,0,i*st],
                        [or*cos(j*lfxy),     or*sin(j*lfxy),     i*st+j*zt-st/2     ],
                        [or*cos((j+1)*lfxy), or*sin((j+1)*lfxy), i*st+(j+1)*zt-st/2 ],
                        [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt          ],
                        [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt      ],
                        [0,                  0,                  i*st+st            ]	] ;

            polyhedron(points=pt,
              		  faces=[	[1,0,3],[1,3,6],[6,3,8],[1,6,4],
											[0,1,2],[1,4,2],[2,4,5],[5,4,6],[5,6,7],[7,6,8],
											[7,8,3],[0,2,3],[3,2,7],[7,2,5]	]);
        }
    }
  }
  else
  {
    echo("Step Degrees too agresive, the thread will not be made!!");
    echo("Try to increase de value for the degrees and/or...");
    echo(" decrease the pitch value and/or...");
    echo(" increase the outer diameter value.");
  }
}

module hex_head(hg,df)
{
	rd0=df/2/sin(60);
	x0=0;	x1=df/2;	x2=x1+hg/2;
	y0=0;	y1=hg/2;	y2=hg;

	intersection()
	{
	   cylinder(h=hg, r=rd0, $fn=6, center=false);

		rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5))
		polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]);
	}
}

module hex_head_0(hg,df)
{
    cylinder(h=hg, r=df/2/sin(60), $fn=6, center=false);
}

module hex_countersink_ends(chg,cod,clf,crs,hg)
{
    translate([0,0,-0.1])
    cylinder(h=chg+0.01,
             r1=cod/2,
             r2=cod/2-(chg+0.1)*cos(clf)/sin(clf),
             $fn=floor(cod*PI/crs), center=false);

    translate([0,0,hg-chg+0.1])
    cylinder(h=chg+0.01,
             r1=cod/2-(chg+0.1)*cos(clf)/sin(clf),
             r2=cod/2,
             $fn=floor(cod*PI/crs), center=false);
}

```


[User]
Hey, I'm looking to design a mount for ball bearings that can be attached to the side of a piece of wood, like a sideways roller. I've got some basic dimensions in mind, but I'd like the design to be parametric so I can easily adjust it for different bearing and board sizes.

Specifically, I need:

*   A mount that clamps to the edge of a board. The board thickness will be a variable.
*   The design should accommodate a cylindrical ball bearing, where the bearing's inner diameter, outer diameter, and thickness are all parameters.
*   There should be a hole for an axle that goes through the center of the bearing. I plan to use an aluminum pipe for the axle.
*   The mount should have screw holes to attach it to the board.
*   The design should account for the fact that the axle will rest against the edge of the board, taking a lot of the load off of the plastic mount.

I'd like the parameters to include:

*   `board_thickness`: The thickness of the wood board.
*   `side_height`: The vertical length of the mount's side that covers the board's edge.
*  `bearing_inner_diameter`: The inner diameter of the bearing.
*   `bearing_outer_diameter`: The outer diameter of the bearing.
*   `bearing_height`: The thickness/height of the bearing.
*   `axle_diameter`: The diameter of the axle.
*   `screw_hole_diameter`: The diameter of the screw holes.
*   `screw_hole_offset`: How far in from the edges of the main mounting body the centers of the screw holes are located.

I'm envisioning the mount looking like a U shape when viewed from the side, with the board sandwiched inside the U. One side will have a circular cutout to fit the bearing, with an axle hole going through the center. The sides of the U will also have holes for screws.

It'd be great if the design were done in OpenSCAD, and I'd prefer the code to be organized into a single file, if possible. I'm not using any external libraries besides the built-in ones, but I would be open to using libraries like BOSL2 or MCAD if they made it easier to achieve this design.

Do you think you can handle that? Let me know if I can clarify any points.

[Assistant]
lateralroller.scad:
```
// thickness of wood plate, mm
thick = 16;

// vertical length of plate side to cover, mm
sides = 20;

// inner diameter of bearing axle, mm
b_inner = 8;

// outer diameter of bearing, mm
b_outer = 22;

// height/thickness of bearing, mm
b_height = 7;

rotate( [ 90, 0, 0 ] ) {

translate( [0,0,10.5]) {
//	cylinder( d=b_outer, b_height ); // bearing
}

difference() {
union() {
translate( [ - 3 - 0.5 * thick , b_inner / 2, 0 ] )  {
	cube( [ thick + 3+3, sides, 21 + b_height ] ); // main body
	translate( [ 0, -2 -1 * b_inner, 0 ] )
		cube( [ thick + 3+ 3, sides, 21 + b_height ] ); // front round
	}
	}
translate( [ - 0.5 * thick, 0.5 * b_inner, -0.5 ] )
	cube( [ thick, sides + 1, b_height + 22 ] ); //  wood plate
translate( [ -0.5 * (thick + 6 + 1), -0.5 * (b_outer + 1), 10 ] )
	cube( [ thick + 6 + 1, b_outer + 1, b_height + 1 ] ); // opening for bearing
translate( [0,0, -0.5] )
	cylinder( 22 + b_height, d=b_inner ); // axle

translate( [- 0.5 * (thick + 6.1 ),sides - 1, 5 ] )
	rotate( [ 0, 90, 0 ] )
	cylinder( d = 3, thick + 6.1 ); // screw hole

translate( [- 0.5 * (thick + 6.1 ),sides - 1, 21 + b_height - 5  ] )
	rotate( [ 0, 90, 0 ] )
	cylinder( d = 3, thick + 6.1 ); // screw hole

}

}
```


[User]
Hey, I'm looking for an OpenSCAD design for a jig that'll hold circular objects for my Neje DK-8-KZ laser engraver. I've got a simple design in mind, and I'd like it to be parametric so I can adjust it easily.

The jig should be a block with a circular cutout.  The jig needs to have the same footprint as the engraver's bed, with a little extra around the sides, and then it needs to be twice as tall.  I want it to sit on the laser bed. The circular cutout should go all the way through the block. The cutout diameter should be a parameter so I can easily change it for different sized coins or other circular objects. I will have a need to flip the object in the cutout, so the cutout should pass all the way through the jig.

Could you generate that for me?  It's a pretty simple design. I don't need anything too fancy, just something that works and is easy to customize. And make sure it's parametric!

[Assistant]
basic-neje-disk-jig.scad-inlined.scad:
```

cutoutDiameter = 40; // [5:100]

zTranslate = nejeLaserEngraverDiskJig_zLength();

translate([0,0,zTranslate])
rotate([180, 0, 0])
nejeLaserEngraverDiskJig(diameter = cutoutDiameter);

module nejeLaserEngraverDiskJig(diameter = 40)
{
	bed_xLength = nejeDk8KzBed_xLength();
	bed_yLength = nejeDk8KzBed_yLength();
	bed_zLength = nejeDk8KzBed_zLength();

	xyIncrease = 3 * 2;

	xLength = nejeDk8KzBed_xLength() + xyIncrease;
	yLength = nejeDk8KzBed_yLength() + xyIncrease;
	zLength = nejeLaserEngraverDiskJig_zLength();

	difference()
	{
		nejeLaserEngraverDiskJig_block(bed_xLength, bed_yLength, bed_zLength,
						 			   xLength, yLength, zLength);

		nejeLaserEngraverDiskJig_cutout(diameter, xLength, yLength, zLength);
	}
}

/** Support functions and modules follow. **/

function nejeLaserEngraverDiskJig_zLength() = nejeDk8KzBed_zLength() * 2;

module nejeLaserEngraverDiskJig_block(bed_xLength, bed_yLength, bed_zLength,
				 					  xLength, yLength, zLength)
{
	laserEngraverJig(bed_xLength, bed_yLength, bed_zLength,
					 xLength, yLength, zLength);
}

module nejeLaserEngraverDiskJig_cutout(diameter, xLength, yLength, zLength)
{
	radius = diameter / 2.0;
	xTranslate = xLength / 2.0;
	yTranslate = yLength / 2.0;
	translate([xTranslate, yTranslate, -0.01])
	cylinder(r=radius, h=10);
}

module laserEngraverJig(bed_xLength, bed_yLength, bed_zLength,
						xLength, yLength, zLength)
{
	laserEngraverJig_base(bed_xLength, bed_yLength, bed_zLength,
						  xLength, yLength, zLength);
}

/** Support functions and modules follow. **/

module laserEngraverJig_base(bed_xLength, bed_yLength, bed_zLength,
							 xLength, yLength, zLength)
{
	difference()
	{
		// main block
		cube([xLength, yLength, zLength]);

		// bed cutout
		xTranslate = (xLength - bed_xLength) / 2.0;
		yTranslate = (yLength - bed_yLength) / 2.0;
		color("green")
		translate([xTranslate, yTranslate, -0.01])
		cube([bed_xLength, bed_yLength, bed_zLength + 0.01]);
	}
}

module nejeDk8KzBed()
{
	xLength = nejeDk8KzBed_xLength();
	yLength = nejeDk8KzBed_yLength();
	zLength = nejeDk8KzBed_zLength();

	cube([xLength, yLength, zLength]);
}

/** Support functions and modules follow. **/

function nejeDk8KzBed_xLength() = 78;

function nejeDk8KzBed_yLength() = 90;

function nejeDk8KzBed_zLength() = 3;
```


[User]
I need a parametric cable clip design, something I can use to join two cables together, like a little clamp. I want to be able to adjust the diameter of the cable it's meant to hold and how tall the clip itself is. It should have a simple design, nothing too fancy. I want it to be printable without supports.

Specifically, the clip should be made by starting with two cylinders that are slightly larger than the cable diameter (like, maybe 2mm bigger on the radius). Then cut out from each of these larger cylinders a smaller cylinder equal to the cable's radius. Finally, cut out a triangle shape from each of these cut out cylinders. This triangle would extend from the small cylinder cutout to the larger outer cylinder, and should "point" toward the center of the clip. When I put this together in my head, it should look like two open-ended circles, both with a slit cut into them. When I rotate one of those circles by 180 degrees, it should be able to overlap with the other, completing the clamping motion.

I'd like the OpenSCAD design to have at least two parameters that I can adjust: `cable_diameter` and `clip_height`. I think those are the most important. I want to be able to easily modify both. I'm not sure what else I would need to change, really. It seems pretty simple, and I’m not trying to reinvent the wheel here. I'd like to see this in a single .scad file, and please don't use any external libraries if you can avoid it, it's just a simple shape.

[Assistant]
cable_connect.scad:
```
// cable_connect
// version 0.0.1
// by pixelhaufen
// is licensed under CC BY-SA 3.0 -> https://creativecommons.org/licenses/by-sa/3.0/

//------------------------------------------------------------------
// User Inputs

cable_diameter = 8; // [4:0.1:15]
clip_height = 10; // [5:25]

/*[Hidden]*/
// speed vs. resolution
$fn = 60; // looks like this can't be modified on thingiverse

cable_radius = cable_diameter/2;

//------------------------------------------------------------------
// Main
rotate([0, 0, 180])
{
    translate([(cable_radius+1)*-1,0,0])
    {
        ring();
    }
}

translate([(cable_radius+1)*-1,0,0])
{
    ring();
}

//------------------------------------------------------------------
// Module

module ring()
{
    difference()
    {
        cylinder(h=clip_height, r1=cable_radius+2, r2=cable_radius+2, center=false);

        union()
        {
            cylinder(h=clip_height, r1=cable_radius, r2=cable_radius, center=false);
            linear_extrude(height = clip_height, twist = 0)
            {
                polygon(points=[[cable_radius+0,0], [(cable_radius+2)*-1,cable_radius+2], [(cable_radius+2)*-1,(cable_radius+2)*-1]]);
            }
        }
    }
}

```


[User]
I'm looking to create some 3D printable Lissajous knots, and I'd like a parametric OpenSCAD design that lets me control all the key features. I've seen some cool examples, but I need something customizable.

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

*   **Overall Scale:** A general scaling factor for the entire knot.
*   **Rope Sides:** The number of sides for the rope's cross-section (like a polygon). This should be a divisor of 360 to get a consistent shape.
*   **Path Step:** The step size for generating the path, which affects the detail level. Smaller steps will result in smoother curves and longer computation time.
*   **Axis Scaling:** Separate scaling factors for the X, Y, and Z axes of the knot, which will stretch the knot in the 3 dimensions.
*   **Rope Diameter:** The diameter or radius of the rope itself.
*   **Open/Closed Knot:** An option to create an open or partial knot by cutting off the loop.
*   **Partial Path Start/End:** If the knot is open, options to specify the start and end points of the path loop in degrees from 0 to 360.
*   **X, Y, Z Angle Multipliers:** The multipliers used in the Lissajous formula for the X, Y, and Z axes - these will change the number of loops, effectively.
*   **X, Y, Z Phase Angles:** Phase shifts for the X, Y, and Z axes, in radians. This shifts the starting points on the sine curve. It would be nice if this was adjustable in degrees instead of radians.
*   **Profile Phase:** The starting angle of the rope profile, which really only matters with low values of "Sides."

The design should use a polygonal tube along a path, and it should be able to handle an open knot (meaning the faces at the start and end will not connect, if `Open=true`). I think that it's probably best to generate this as a polyhedron, but I'm not certain of that. I'd really appreciate the ability to render the path itself using `hull()` with spheres as well.

I don't need any libraries to be included in this file. I'd prefer it to be written as a single `.scad` file, if that's at all possible.

[Assistant]
Lissajous_Knot.scad:
```
/*
    remix of "Celtic knots as Lissajous knots"
    by Kit Wallace
    https://www.thingiverse.com/thing:2257185

    Lissajous Knot:
    https://en.wikipedia.org/wiki/Lissajous_knot
*/

Scale=20; // [1.0:0.5:60]
// Sides of rope - must be a divisor of 360
Sides=4; // [3,4,5,6,8,9,10,12,15,30,45,60]
// decrease for finer details
Step = 2; // [0.2:0.2:5]
// x scaling
vx = 1.0; // [0.0:0.1:4]
// y scaling
vy = 1.0; // [0.0:0.1:4]
// z scaling
vz = 1.0; // [0.0:0.1:4]
// Rope diameter
Rope_Diameter=2.0; // [0.5:0.1:6.0]
// true if knot is open or partial
Open = false; // [false,true]
// change for partial path
Start=0; // [0:360]
// change for partial path
End=360; // [0:360]

// x_angle_multiple
nx = 3.0; // [0.5:0.5:30]
// y_angle_multiple
ny = 2.0; // [0.5:0.5:30]
// z_angle_multiple
nz = 7.0; // [0.5:0.5:30]
// (radians)
px_RAD = 0.7; // [0.0:0.1:4.0]
// (radians)
py_RAD = 0.2; // [0.0:0.1:4.0]
// (radians)
pz_RAD = 0.0; // [0.0:0.1:4.0]
// phase angle for profile (matters for low Sides)
Phase = 45; // [0:5:180]

/* [Hidden] */

// create a tube as a polyhedron
// tube can be open or closed

// polyhedron constructor

function poly(name,vertices,faces,debug=[],partial=false) =
    [name,vertices,faces,debug,partial];

function p_name(obj) = obj[0];
function p_vertices(obj) = obj[1];
function p_faces(obj) = obj[2];

module show_solid(obj) {
    polyhedron(p_vertices(obj),p_faces(obj),convexity=10);
};

// utility functions
function m_translate(v) = [ [1, 0, 0, 0],
                            [0, 1, 0, 0],
                            [0, 0, 1, 0],
                            [v.x, v.y, v.z, 1  ] ];

function m_rotate(v) =  [ [1,  0,         0,        0],
                          [0,  cos(v.x),  sin(v.x), 0],
                          [0, -sin(v.x),  cos(v.x), 0],
                          [0,  0,         0,        1] ]
                      * [ [ cos(v.y), 0,  -sin(v.y), 0],
                          [0,         1,  0,        0],
                          [ sin(v.y), 0,  cos(v.y), 0],
                          [0,         0,  0,        1] ]
                      * [ [ cos(v.z),  sin(v.z), 0, 0],
                          [-sin(v.z),  cos(v.z), 0, 0],
                          [ 0,         0,        1, 0],
                          [ 0,         0,        0, 1] ];

function vec3(v) = [v.x, v.y, v.z];
function transform(v, m)  = vec3([v.x, v.y, v.z, 1] * m);

function orient_to(centre,normal, p) = m_rotate([0, atan2(sqrt(pow(normal.x, 2) + pow(normal.y, 2)), normal.z), 0])
                     * m_rotate([0, 0, atan2(normal[1], normal[0])])
                     * m_translate(centre);

// solid from path

function circle_points(r, sides,phase=45) =
    let (delta = 360/sides)
    [for (i=[0:sides-1]) [r * sin(i*delta + phase), r *  cos(i*delta+phase), 0]];

function loop_points(step,min=0,max=360) =
    [for (t=[min:step:max-step]) f(t)];

function transform_points(list, matrix, i = 0) =
    i < len(list)
       ? concat([ transform(list[i], matrix) ], transform_points(list, matrix, i + 1))
       : [];

function tube_points(loop, circle_points,  i = 0) =
    (i < len(loop) - 1)
     ?  concat(transform_points(circle_points, orient_to(loop[i], loop[i + 1] - loop[i] )),
               tube_points(loop, circle_points, i + 1))
     : transform_points(circle_points, orient_to(loop[i], loop[0] - loop[i] )) ;

function loop_faces(segs, sides, open=false) =
   open
     ?  concat(
         [[for (j=[sides - 1:-1:0]) j ]],
         [for (i=[0:segs-3])
          for (j=[0:sides -1])
             [ i * sides + j,
               i * sides + (j + 1) % sides,
              (i + 1) * sides + (j + 1) % sides,
              (i + 1) * sides + j
             ]
        ] ,
        [[for (j=[0:1:sides - 1]) (segs-2)*sides  + j]]
        )
     : [for (i=[0:segs])
        for (j=[0:sides -1])
         [ i * sides + j,
          i * sides + (j + 1) % sides,
          ((i + 1) % segs) * sides + (j + 1) % sides,
          ((i + 1) % segs) * sides + j
         ]
       ]
     ;

//  path with hulls

module hulled_path(path,r) {
    for (i = [0 : 1 : len(path) - 1 ]) {
        hull() {
            translate(path[i]) sphere(r);
            translate(path[(i + 1) % len(path)]) sphere(r);
        }
    }
};

// smoothed path by interpolate between points

weight = [-1, 9, 9, -1] / 16;

function interpolate(path,n,i) =
        path[(i + n - 1) %n] * weight[0] +
        path[i]             * weight[1] +
        path[(i + 1) %n]    * weight[2] +
        path[(i + 2) %n]    * weight[3] ;

function subdivide(path,i=0) =
    i < len(path)
     ? concat([path[i]],
              [interpolate(path,len(path),i)],
              subdivide(path, i+1))
     : [];

function smooth(path,n) =
    n == 0
     ?  path
     :  smooth(subdivide(path),n-1);

function path_segment(path,start,end) =
    let (l = len(path))
    let (s = max(floor(start * 360 / l),0),
         e = min(ceil(end * 360 / l),l - 1))
    [for (i=[s:e]) path[i]];

function scale(path,scale,i=0) =
    i < len(path)
      ?  concat(
           [[path[i][0]*scale[0],path[i][1]*scale[1],path[i][2]*scale[2]]],
           scale(path,scale,i+1)
         )
      :  [];

function curve_length(step,t=0) =
    t < 360
      ?  norm(f(t+step) - f(t)) + curve_length(step,t+step)
      :  0;

function map(t, min, max) =
      min + t* (max-min)/360;

//  create a knot from a path

function path_knot(path,r,sides,kscale,phase=45,open=false)  =
  let(loop_points = scale(path,kscale))
  let(circle_points = circle_points(r,sides,phase))
  let(tube_points = tube_points(loop_points,circle_points))
  let(loop_faces = loop_faces(len(loop_points),sides,open))
  poly(name="Knot",
         vertices = tube_points,
         faces = loop_faces);

 // render_type function-1

R=Rope_Diameter/Scale;
RAD=180/PI;
px=px_RAD * RAD;
py=py_RAD * RAD;
pz=pz_RAD * RAD;

function f(t) =
    [  cos(nx * t + px) ,
       cos(ny * t + py),
       cos(nz * t + pz)
    ];

Kscale=[vx,vy,vz];
Paths = [loop_points(Step,Start,End)];
scale(Scale)
for (k =[0:len(Paths) - 1]) {
   path = Paths[k];
 knot= path_knot(path,R,Sides,Kscale,Phase,Open);

   solid1=knot;  // apply transformations here
      show_solid(solid1);
};

```


[User]
I need an OpenSCAD design for a modular HDD stand, primarily for 2.5" external drives. I want it to be parametric so I can easily adjust it for different numbers of drives and sizes.

Here are the basic parameters I think I need:

*   `base_thickness`: The thickness of the base of the stand.
*   `pocket_width`: The width of the pocket that the HDD sits in (should match the HDD width).
*   `pocket_length`: The length of the pocket.
*   `wall_thickness`: The thickness of the walls surrounding each pocket.
*   `wall_height`: The height of the walls.
*  `quantity_of_racks`: How many pockets/HDDs to accommodate.
*   `skirt_on`:  A boolean or string to control whether a skirt is added at the base of the stand, extending outwards from the pockets.
*   `skirt_width`: The width of that skirt.

The basic design should consist of a series of pockets, each with a base, and surrounding walls.  Each pocket should be joined to the next.  If the skirt is enabled, it needs to go around the entire base of the joined pockets.

I would like the design to be such that I can change the `quantity_of_racks` parameter and the stand will generate the correct amount of pockets for the drives. It should also be able to be 3d printed, so nothing crazy in terms of angles.

I'm not sure if I need to use specific libraries, but let me know if it makes the design easier to create.

[Assistant]
HddStandModularCustomizableV2.scad:
```

//How thick should the base be (in mm)?
base_thickness=2;
//How wide should the pocket be / is your HDD?
pocket_width=21;
//How long should the pocket be / is your HDD?
pocket_lenght=130;
//How thick should the wall_thickness be (in mm)?
wall_thickness=2;
//How high should the wall_thickness be (in mm)?
wall_height=15;
//How much pockets do you want?
quantity_of_racks=3;
//Do you want to add a skirt?
skirt_on = "no";// [yes,no]
skirt_width=5;//[1:20]

difference(){
    for(i=[1:quantity_of_racks]){
        xOffset=(wall_thickness+pocket_width)*i;
        if (skirt_on=="yes"){
            translate([xOffset-skirt_width,-skirt_width,0]){
                cube([pocket_width+2*wall_thickness+2*skirt_width,pocket_lenght+2*wall_thickness+2*skirt_width,            base_thickness]);
            }
        }
        translate([xOffset,0,0]){
            difference(){

            //außen
            cube([2*wall_thickness+pocket_width,2*wall_thickness+pocket_lenght,base_thickness+  wall_height]);

            //innen
            translate([wall_thickness,wall_thickness,base_thickness]){
                cube([pocket_width,pocket_lenght,base_thickness+wall_height]);
                }
            }
        }
    }
}

```


[User]
Hey! I'm looking for some help creating a parametric OpenSCAD design for a 6-cylinder Kwikset house key. I've got some details and I'd love to see if you can create the SCAD files needed to make this work.

Basically, I need a design that allows me to customize a few key aspects of the key. Here's a breakdown of what I'm after:

**Key Shape and Size:**

*   **Blade:** The blade needs to be 29.3 mm long, and about 8.5 mm wide (that's 0.335 inches converted to mm). The blade thickness should be adjustable, but I'd like it to default to 1.9 mm.
*   **Key Head:** I'd like a key head that's essentially a polygon, and the number of sides needs to be adjustable, allowing for 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 20, 24, 28, or 32 sides. I'd like the diameter to be about 26mm.
*   **Overall:** The design should consider the "shoulder" of the key where the blade connects to the head. The shoulder should be sized to fit the blade and key head appropriately.
*   **Key Ring Hole:** I need a hole for a key ring in the key head. It should be around 9mm in diameter. I need the distance of this hole from the center of the key head to be about the same radius of the key ring hole plus one.

**Key Cutting:**

*   **Cylinders:** The key needs to have 6 cuts for 6 cylinders. Each of these cuts will need to be at specific locations along the blade. The spacing needs to follow Kwikset standards using the following dimensions:
    *   The first cut is 2.46mm (0.097 inches) from the shoulder.
    *   The other cuts are each 3.81mm (0.150 inches) further.
*   **Cut Depths:** Each cut can have 7 different depths. The depths are as follows:
    *   Depth 1: 8.36mm (0.329 inches)
    *   Depth 2: 7.78mm (depth1 - 0.5842mm)
    *   Depth 3: 7.20mm (depth2 - 0.5842mm)
    *   Depth 4: 6.62mm (depth3 - 0.5842mm)
    *   Depth 5: 6.04mm (depth4 - 0.5842mm)
    *   Depth 6: 5.46mm (depth5 - 0.5842mm)
    *   Depth 7: 4.88mm (depth6 - 0.5842mm)
    *   These depth values need to be used to determine the distance from the bottom of the key blade to each cylinder cut.
*   **Key Code:** There should be a way to specify a 6-digit key code (using digits 1-7) to generate the proper cuts. If no code is provided, it should randomly generate a 6-digit code.

**Key Slots/Grooves:**

*   **Front Slot:**  There's a slot on the front of the key that needs to be 1.60 mm wide, and offset from the center of the blade by 0.60mm on the Y axis and also 0.60 mm on the Z axis. The length of this groove should be the same length as the blade plus a small amount for clipping.
*   **Back Slot:** A similar slot on the back, but this one is 1.35 mm wide, offset from the center of the blade by 0.60mm + 1.60mm + 0.60mm on the Y axis, and offset by -0.60mm on the Z axis, and needs to have an angled cut for a portion of the blade. The angle for this cut needs to be 210 degrees. The length of this groove should be the same length as the blade plus a small amount for clipping.
*   **Top Slot:** There's also a slot that runs along the top of the key with a width of 3.25 mm, and an offset of 1.0mm on the Z axis with a cut angled at 30 degrees relative to the blade's top surface. The length of this groove should be the same length as the blade plus a small amount for clipping.

**Additional Features:**

*   **Text:** It would be great if I could print text on the key head using the "Write.scad" library. The default text should be the key code or a random key code if no key code is specified. There should be an option to use custom text, as well. There needs to be a conditional that prevents the text from being added if the "polygon count" is set to 3, 4, or 5. The text size should be adjustable, defaulting to 5.0 mm.
*   **Belfry Logo:** On the back of the key head I'd like to have the letter 'B' in a nice size. It should be near the key ring hole.
*    **Build Plate:** There should be a way to select a build plate using the "build\_plate.scad" library.
*   **Unit Conversion:** Make sure to use a variable for converting inches to millimeters.

I'm thinking it will require a few SCAD files - maybe a main file, a utility library file for things like conversions, and then a library file for writing text. I'd love it if the design was well-commented and easy to modify!

Let me know if you have questions and thanks for the help!

[Assistant]
Six_Cylinder_House_Key.scad:
```
// Thingiverse File Name: Six_Cylinder_House_Key.scad
// Author: Belfry ( John Gardner )
// Date: 09/02/2018
// Phone: 720-980-6802
// Email: belfry6050@jgpc.net
// Web: www.thingiverse.com/belfry
// Last Update: 09/02/2018
// Version: 004

// Create a customized 6 cylinder (Kwikset) house key

// external files
use <utils/build_plate.scad>
use <write/Write.scad>
//use <Write.scad>

// preview[view:south,tilt:top]

// let's create a way to check things out
//debug_flag = true;
debug_flag = false;

// ----- start of 'customizer' user options -----

//: Determines the shape of the key header
polygon_count = 6;	// [3,4,5,6,7,8,9,10,11,12,16,20,24,28,32]
echo( "Polygon Count" , polygon_count );

//: Key Cutting Code (1-7 for each cylinder) leave blank for random key
cut_depth = "";
//cut_depth = "777777";
echo( "cut_depth" , cut_depth );

//: Custom text to print in place of the key code
custom_key_text ="";
//custom_key_text ="WWWWWW";
echo( "custom_key_text" , custom_key_text );

//: Thickness of the Blade (z-dimension)
blade_thickness = 1.9;  // [1.7,1.8,1.9,2.0,2.1]
echo( "blade_thickness" , blade_thickness );

//---[ Build Plate ]-----------------------------------

//: for display only, doesn't contribute to final object
build_plate_selector = 3; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
//: when Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 100 + 0;
//: when Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 100 + 0;

build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

//-------------------------------------------------

module dummy_call()
{
    echo( "module: dummy_call()" );
}
dummy_call();  //  end of customizer user options???

//----- start of program 'constant' declarations -----

text_height = 0.9;

in2mm = 25.4;
echo( "in2mm" , in2mm );

// Set the Number of Cutting Steps
cutting_steps = 7; //Kwikset standard - 0.023inch:0.5842mm;
echo( "cutting_steps" , cutting_steps );

// --- cylinder definitions -----------------------

// Diameter of individual cylinder pins
key_cyl_diameter = ( 0.115 * in2mm );
echo( "key_cyl_diameter" , key_cyl_diameter );

// cylinder pin bottom diameter
cyl_pin_bot_diameter = ( 0.084 * in2mm );
echo( "cyl_pin_bot_diameter" , cyl_pin_bot_diameter );

// spacing between cylinders
cyl_spacing = ( 0.150 * in2mm );
echo( "cyl_spacing" , cyl_spacing );

// distance from the "shoulder" to the "first cut"
first_cut = ( 0.097 * in2mm );
echo( "first_cut" , first_cut );

cylinder_spacing = [ first_cut , first_cut + cyl_spacing , first_cut + ( cyl_spacing * 2 ) , first_cut + ( cyl_spacing * 3 ) , first_cut + ( cyl_spacing * 4 ) , first_cut + ( cyl_spacing * 5 ) ];
echo( "cylinder_spacing" , cylinder_spacing );

// Key Cutting Codes (1-7 for each cylinder)
step_size = ( 0.023 * in2mm );
echo( "step_size" , step_size );

depth1 = ( 0.329 * in2mm );
echo( "depth1" , depth1 );

depth2 = depth1 - step_size;
echo( "depth2" , depth2 );

depth3 = depth2 - step_size;
echo( "depth3" , depth3 );

depth4 = depth3 - step_size;
echo( "depth4" , depth4 );

depth5 = depth4 - step_size;
echo( "depth5" , depth5 );

depth6 = depth5 - step_size;
echo( "depth6" , depth6 );

depth7 = depth6 - step_size;
echo( "depth7" , depth7 );

depth_array = [ depth1 , depth2 , depth3 , depth4 , depth5 , depth6 , depth7 ];
echo( "depth_array" , depth_array );

size1 = 15;
echo( "size1" , size1 );

flat_tip = ( cyl_pin_bot_diameter / 2 );
echo( "flat_tip" , flat_tip );

// define a value for 'pi'
pi_value = 3.14159 *1;	// pi
echo( "Pi" , pi_value );

//define the 'belfry B' logo
belfry_b_logo = str( "B", "" );
echo( "belfry_b_logo" , belfry_b_logo );

// Number of Cylinders
number_of_cylinders =  6;
echo( "number_of_cylinders" , number_of_cylinders );

// Length of the Blade ( shoulder to tip ) (x-dimension)
blade_length = 29.3;
echo( "blade_length" , blade_length );

// Width of the Blade (y-dimension)
blade_width = ( 0.335 * in2mm );
echo( "blade_width" , blade_width );

// Height/Size of printed text ( 'write.scad' value )
text_h_size = 5.0;
echo( "text_h_size" , text_h_size );

// --- front slot definitions -------------------

// Key Front Side Slot Width
key_fslot_width = 1.60;
echo( "key_fslot_width" , key_fslot_width );

// Key Front Side Slot Y Offset
key_fslot_yoffset = 0.60;
echo( "key_fslot_yoffset" , key_fslot_yoffset );

// Key Front Side Slot Z Offset
key_fslot_zoffset = 0.60;
echo( "key_fslot_zoffset" , key_fslot_zoffset );

// --- back slot definitions --------------------

// Key Back Side Slot Width
key_bslot_width = 1.35;
echo( "key_bslot_width" , key_bslot_width );

// Key Back Side Slot Y Offset
key_bslot_yoffset = key_fslot_yoffset+key_fslot_width+0.60;
echo( "key_bslot_yoffset" , key_bslot_yoffset );

// Key Back Side Slot Z Offset
key_bslot_zoffset = -0.6;
echo( "key_bslot_zoffset" , key_bslot_zoffset );

// Key Back Side Angle Value
key_bslot_angle = 210;
echo( "key_bslot_angle" , key_bslot_angle );

// --- top slot definitions -----------------

// Key Top Slot Width
key_tslot_width = 3.25;
echo( "key_tslot_width" , key_tslot_width );

// Key Top Slot Z Offset
key_tslot_zoffset = 1.0;
echo( "key_tslot_zoffset" , key_tslot_zoffset );

// Key Top Slot Angle Value
key_tslot_angle = 30;
echo( "key_tslot_angle" , key_tslot_angle );

// --- tip of key definitions - top -----------

// key tip - top angle rotation value
key_tip_topangle = 45;
echo( "key_tip_topangle" , key_tip_topangle );

// key tip - top y offset value
key_tip_topyoffset = key_fslot_yoffset+key_fslot_width;
echo( "key_tip_topyoffset" , key_tip_topyoffset );

// --- tip of key definitions - bottom --------

// key tip - bottom angle rotation value
key_tip_botangle = 225;
echo( "key_tip_botangle" , key_tip_botangle );

// key tip - bottom y offset value
key_tip_botyoffset = key_bslot_yoffset;
echo( "key_tip_botyoffset" , key_tip_botyoffset );

// set the 'long' side of the top slot triangle value
tslot_long_side = (blade_thickness - key_tslot_zoffset);
echo( "tslot_long_side" , tslot_long_side );

// calculate the short side of the top slot triangle
tslot_short_side = tslot_long_side * tan(30);
echo( "tslot_short_side" , tslot_short_side );

// calculate the hypotenuse side of the top slot triangle
tslot_hypotenuse_side = sqrt(pow(tslot_long_side,2) + pow(tslot_short_side,2));
echo( "tslot_hypotenuse_side" , tslot_hypotenuse_side );

// Number Lookup Array
number_lookup = str( "0123456789" , "" );
echo( "number_lookup" ,  number_lookup );

// Array of random key pin STEP values - 1 to (cutting_steps+1) for each cylinder
random_key_array = rands( 1 , (cutting_steps+1) , number_of_cylinders );
echo( "random_key_array" , random_key_array );

// 6 'character' random number
random_key = str( floor( random_key_array[0] ) , floor( random_key_array[1] ) , floor( random_key_array[2] ) , floor( random_key_array[3] ) , floor( random_key_array[4] ) , floor( random_key_array[5] ) );
echo( "random_key: " , random_key );

// Key Cutting Code (1-7 for each cylinder)
key_cut_code = str(cut_depth,random_key);	// random_key used if cut_depth is blank
// random_key used if cut_depth is blank
echo( "key_cut_code" , key_cut_code );

// distance between characters (mm)
text_separation = 3.5;
echo( "text_separation" , text_separation );

// radius for the key ring hole
key_ring_radius = 4.5;
echo( "key_ring_radius" , key_ring_radius );

// diameter for the key head
key_head_diameter = 26;
echo( "key_head_diameter" , key_head_diameter );

// ---[ Start of program ]--------------------------------
echo( "start of the program ===============" );
{
    create_a_key_blank();

    translate(([-(key_head_diameter/2),(blade_width/2),0]))
    rotate([0,0,90])
    difference()
    {
        union()
        {
            // create a key header
            create_key_header( header_diameter=key_head_diameter );
            create_key_shoulder();
        }	//	end of union
        create_key_ringhole();
        create_belfry_B();
    } // end of difference
} // ---[ end of program ] --------------------------------
echo( "end of program ===============" );

// ---[ End of program ]-------------------------------

// ---[ Start of module definitions ] ---

// ----- create a key blank object
module create_a_key_blank()
{
    echo( "module: create_a_key_blank()" );
    difference()
    {
        // ----- main blade setup -----
        echo( "main blade setup" );
        cube( [ blade_length , blade_width , blade_thickness ] );	// main key blade

       // ----- cut the main key slots -----
        echo( "cut the main key slots" );
        cut_slots()

         // ----- cut off the tip of the blade -----
        echo( "cut off the tip of the blade" );
        cut_the_tip();

        // ----- cylinder cutting loop -------------
        echo( "start cutting" , number_of_cylinders , "cylinders" );
        cut_cylinders( total_cylinders = number_of_cylinders );

    } // end of difference
}

// ----- cylinder cutting loop ---------------------------------------------------
module cut_cylinders( total_cylinders = 1 )
{
    echo( "module: cut_cylinders()" );
    echo( "start of cylinder cutting loop for" , total_cylinders , "cylinders" );
    for ( cyl_loop = [1 : total_cylinders] )
    {
        echo(str("cylinder:" , cyl_loop , " - key_cut_code:" , key_cut_code[cyl_loop-1]));
        cut_a_cylinder( cylinder_number = cyl_loop , cutter_depth = key_cut_code[cyl_loop-1] );
    }  // end of for loop
}	// end of module

// ----- cut a cylinder
module cut_a_cylinder( cylinder_number = 1 , cutter_depth = "1" )
{
    echo( "module: cut_a_cylinder()" );
    echo( "cylinder_number" , cylinder_number );
    echo( "cutter_depth" , cutter_depth );

    if ( ( cylinder_number >= 1 )  && ( cylinder_number <= number_of_cylinders ) )
    {
        if ( cutter_depth == "1" )
        {
            translate([cylinder_spacing[cylinder_number-1],0,0])
            create_depth_cut(1);
        }

        if ( cutter_depth == "2" )
        {
            translate([cylinder_spacing[cylinder_number-1],0,0])
            create_depth_cut(2);
        }

        if ( cutter_depth == "3" )
        {
            translate([cylinder_spacing[cylinder_number-1],0,0])
            create_depth_cut(3);
        }

        if ( cutter_depth == "4" )
        {
            translate([cylinder_spacing[cylinder_number-1],0,0])
            create_depth_cut(4);
        }

        if ( cutter_depth == "5" )
        {
            translate([cylinder_spacing[cylinder_number-1],0,0])
            create_depth_cut(5);
        }

        if ( cutter_depth == "6" )
        {
            translate([cylinder_spacing[cylinder_number-1],0,0])
            create_depth_cut(6);
        }

        if ( cutter_depth == "7" )
        {
            translate([cylinder_spacing[cylinder_number-1],0,0])
            create_depth_cut(7);
        }
    }
}

// ----- create a cutter for the key
module create_depth_cut( cut_depth = 1 )
{
    echo( "module: create_depth_cut()" );
    echo( "cut_depth" , cut_depth );

    if ( ( cut_depth >= 1 ) && ( cut_depth <=7 ) )
    {
        translate([0,depth_array[cut_depth-1],0])
        {
            translate([0,-flat_tip,0])
            difference()
            {
                translate([0,0,-1])
                rotate([0,0,45])
                cube([size1,size1,size1/3]);

                translate([-size1/2,-(size1-flat_tip),-size1/2])
                cube(size1);
            }  // end of difference
        }  // end of translate
    };  // end of if
}

// ----- cut the main key slots (millings/grooves) module ---------------------
module cut_slots()
{
    echo( "module: cut_slots()" );

    // ----- front slot setup -----
    echo( "front slot setup" );
    front_slot();

    // ----- back slot setup -----
    echo( "back slot setup" );
    back_slot();

    // ----- top slot setup -----
    echo( "top slot setup" );
    top_slot();

}	//	end of module

// ----- front slot setup module ---------------------------------------------
module front_slot()
{
    echo( "module: front_slot()" );
    translate([-1,key_fslot_yoffset,key_fslot_zoffset])
    cube( [ blade_length + 2 , key_fslot_width , blade_thickness ] );
}	//	end of module

// ----- back slot setup module ----------------------------------------------
module back_slot()
{
    echo( "module: back_slot()" );

    //	set length of short cube
    short_cube = ( blade_thickness *2 );
    echo( "short_cube" , short_cube );

    // calculate the short side
    short_side_offset = ( short_cube * sin(30) );
    echo( "short_side_offset" , short_side_offset );

    // calculate the long side
    long_side_offset = ( short_cube * cos(30) );
    echo( "long_side_offset" , long_side_offset );

    translate([-1,key_bslot_yoffset,key_bslot_zoffset])
    {
        union()
        {
            cube( [ blade_length + 2 , key_bslot_width , blade_thickness ] );
            translate([0,key_bslot_width,blade_thickness])
            {
                rotate( [key_bslot_angle,0,0] )
                cube( [ blade_length + 2 , key_bslot_width , short_cube ] );
            }  // end of translate
        }	// end of union
    }	// end of translate
}	//	end of module

// ----- top slot setup module ------------------------------------------------
module top_slot()
{
    echo( "module: top_slot()" );
    translate( [-1,blade_width-key_tslot_width,key_tslot_zoffset] )
    {
        union()
        {
            rotate( [key_tslot_angle,0,0] ) cube( [ blade_length + 2 , blade_width , blade_thickness * 2 ] );
            cube( [ blade_length + 2 , blade_width  , blade_thickness * 2 ] );
        }	// end of union
    }	// end of translate
}	// end of module

// ----- cut off the tip of the blade -------------------------------------------
module cut_the_tip()
{
    echo( "module: cut_the_tip()" );
    echo( "cut off the tip of the blade module" );
    union()
    {
        translate( [blade_length,key_tip_topyoffset,-1] )
            rotate( [0,0,key_tip_topangle] )
            cube( blade_width *1.5 );

        translate( [blade_length+0,key_tip_botyoffset+1,-1] )
            rotate( [0,0,key_tip_botangle] )
            cube( blade_width );
    }	// end of union
}	//	end of module

// ----- key header -------------------
module create_key_header( header_diameter = 25 )
{
    echo( "module: create_key_header()" );
    echo( "header_diameter" , header_diameter );

    rotate([0,0,90])
    cylinder( h=blade_thickness , r=header_diameter/2 , center = false , $fn=polygon_count);	// header for key

    translate([-( (number_of_cylinders * text_separation ) / 2 ),-(text_h_size),blade_thickness])
    if (len(custom_key_text)==0)
    {
        key_header_text( header_text = key_cut_code );
    }
    else
    {
        key_header_text( header_text = custom_key_text );
    }
}  //  end of module

// ----- key header text ---------------
module key_header_text( header_text = "0123456789" )
{
    echo( "module: key_header_text()" );
    echo( "header_text" , header_text );

    if ( polygon_count >= 6 )
    {
        // ----- add the 'key code' -----
        for ( rdigit_loop = [0:number_of_cylinders-1] )
        {
            echo( "rdigit_loop" , rdigit_loop , "text" , header_text[rdigit_loop] );
            translate([(text_separation*rdigit_loop),0,0])
            write(header_text[rdigit_loop],h=text_h_size,t=text_height);
        }  //  end of for loop
    }
}  //  end of module

//----- create a 'shoulder' for the key --------------
module create_key_shoulder()
{
    echo( "module: create_key_shoulder()" );

    translate([-((blade_width*1.5)/2),-(key_head_diameter/2),0])
    cube([(blade_width*1.5),blade_width,blade_thickness]);
}	//	end of module

//------- put a hole in the key header for a key chain ring ----------------
module create_key_ringhole()
{
    echo( "module: create_key_ringhole()" );

    //create_key_ring_hole();
    translate([0,key_ring_radius+1,0])
    rotate([0,0,90])
    cylinder( h=blade_thickness*3 , r=key_ring_radius , center = true , $fn=polygon_count);
}	//	end of module

//----- create the Belfry 'B' on the back of the key ----------------------
module create_belfry_B()
{
    echo( "module: create_belfry_b()" );

    // print the Belfry 'B' on the back
    translate([2.5,-10,text_height-0.001])
    rotate([0,180,0])
    write(belfry_b_logo,h=10,t=text_height);
}	//	end of module

//-------------------------------------------------------

```


[User]
Hi, I'm looking to create a custom Lego attachment for 3D-printed models. I've seen a few designs out there, but I'd like something more flexible and parametric that I can use with different sized models.

Essentially, I want to be able to import an STL file of my model, then add a Lego-compatible base to it. The base should be configurable so I can control how many Lego studs are included (e.g., a 2x2 base, 4x4 base, etc), and how far those studs extend from the bottom of the imported model. I'd also like to be able to control the diameter and height of the studs themselves to ensure good fit with real Lego bricks.

Ideally, I'd like to specify:
*   The file path to the STL model I want to use
*   The X, Y, and Z offset of the imported model (if any)
*   Scaling for the model
*   The number of Lego studs to use in both the X and Y directions (e.g., base\_size\_x = 2, base\_size\_y = 4)
*   The height of the connector (stud)
*   The outer and inner diameter of the connector (stud)
*   The thickness of the connecting base underneath the model.
*   A margin (or gap) between the connecting base and the model's edge.
*   The ability to choose between adding a "hole" to an object for existing studs to fit in, or "extending" the object with the lego stud attachment.

It would be great if the scad file used a relatively high number of facets for the circles (cylinders) -- maybe allow that value to be variable too -- to keep the studs smooth. I'd also like to have options for both creating "negative space" for existing lego studs on my models (kind of like a female lego connection) as well as "positive" extensions (the lego studs themselves).

I think a key thing would be being able to visually test this quickly - the imported model and attachment should be visible within openscad. Could you generate that for me?

[Assistant]
Lego_Attachment_for_Thingiverse_Models.scad:
```
// Lego Attachment for Thingiverse Models
// https://www.thingiverse.com/thing:3082386

// .stl file name/path
filename="";
x=0; // [-100:0.5:100]
y=0; // [-100:0.5:100]
z=0; // [-100:0.5:100]
enlarge=1.0; //[1:0.1:20]
shrink=1.0; //[1:0.1:20]
connector_height=2.2;// [2.0:0.1:5.0]
connector_diameter=6.51; //
// extend only
connector_thickness=5; // [4:0.1:12]
connector_margin=1; // [0:0.5:4]
// base size x,0 is entire surface
base_size_x=4; // [1:25]
// base size y,0 is entire surface
base_size_y=4; // [1:25]
fn=30; // [10,30,60,90]

choose(i=object);
//Choose method
object=0;//[0:test,1:indent,2:extend]

/* [Hidden] */
MAX_SIZE=200;
rf1=40;
DELTA_KNOB=8;
connector_inner_diameter=4.8;

module draw_model() {
    if (filename == "") {
        translate([0,0,2.5])
	     cube([40,40,5], center=true);
    } else {
        import(filename, center=true);
    }
}

module main_shape() {
    scale_amount = enlarge / shrink;
    scale(scale_amount)
    translate([x,y,z])
	 draw_model();
}

module build(isExtend) {
    x_range=DELTA_KNOB*(1+(base_size_x-1)/2);
    y_range=DELTA_KNOB*(1+(base_size_y-1)/2);
    difference() {
        union() {
            main_shape();
            if (isExtend) {
                translate([0,0,-connector_thickness/2])
	             cube([DELTA_KNOB*(base_size_x+2*connector_margin),DELTA_KNOB*(base_size_y+2*connector_margin),connector_thickness], center=true);
            }
        }
        union() {
            difference() {
                translate([0,0,isExtend ? -((MAX_SIZE/2+connector_height)/2+(connector_thickness-connector_height)) : (-MAX_SIZE/4)+connector_height/2])
                cube([2*x_range-DELTA_KNOB,2*y_range-DELTA_KNOB,MAX_SIZE/2+connector_height],center=true);
                union() {
                   for (x_pos=[-x_range:DELTA_KNOB:x_range]) {
                       for (y_pos=[-y_range:DELTA_KNOB:y_range]) {
                           translate([x_pos+DELTA_KNOB/2,y_pos+DELTA_KNOB/2,isExtend?-(connector_thickness-connector_height/2):(delta_connector)])
                           difference() {
                               cylinder(h=connector_height,r=connector_diameter/2,$fn=fn,center=true);
                               cylinder(h=2*connector_height,r=connector_inner_diameter/2,$fn=fn,center=true);
                           }
                       }
                    }
                }
            }
        }
    }
}

module test() {
    main_shape();
}

module choose(i=0){
   if(i==0)test();
   if(i==1)build(false);
   if(i==2)build(true);
}
```


[User]
I'd like a customizer for a Koch snowflake-inspired vase. I want to be able to control the number of sides of the base polygon (like a triangle, square, pentagon, hexagon, etc., up to a dodecagon), the depth of the Koch curve recursion, the ellipse axes for the base shape, the overall height of the vase, the number of slices used for rendering, the scaling of the top surface, a twist angle, and the wall thickness of the vase. I've got some basic parameters in mind, like polygon sides between 3 and 12, recursion depth from 1 to 5, axis dimensions from 5-100, vase height from 10-200, slices for rendering between 4-100, scaling from .1 to 3 (with steps of 0.1), twist angle from 0-360, and wall thickness from 0.4-3 (with steps of 0.1). Can you generate the OpenSCAD code for that?

I'd like to use BOSL2 if possible for any helpful functions that are available.

[Assistant]
koch_vase_customizer.scad:
```
// Base polygon
POLYGON = 3; // [3,4,5,6,7,8,9,10,11,12]
// Recursion depth
DEPTH = 2; // [1,2,3,4,5]
// Ellipse axis A
RADIUS_A = 40; // [5:100]
// Ellipse axis B
RADIUS_B = 50; // [5:100]
// Height of Vase
HEIGHT = 100; // [10:200]
// Slices (Decrease for faster render speed)
SLICES = 50; // [4:100]
// Scale of Top Surface
SCALE = 1.5; // [0.1:0.1:3]
// Rotation angle
TWIST = 90; // [0:360]
// Wall Width
WIDTH = 0.8; // [0.4:0.1:3]
// [0.4,0.5,0.6,0.7,0.8,1,1.2,1.5,2]
points = [for (i=[0:ceil(360/POLYGON):ceil(360/POLYGON)*(POLYGON-1)])
            [cos(i)*RADIUS_A, sin(i)*RADIUS_B]];
points_koch = [for (i=[0:len(points)-1], p=koch(DEPTH, [points[i], points[(i+1)%len(points)]])) p];

koch_vase();
//koch_vase_test();

module koch_vase_test(){
    linear_extrude(height=HEIGHT, center=true, slices=SLICES, twist=TWIST, scale=SCALE)
                polygon(points_koch);

}

module koch_vase(){
    union(){
        difference(){
            linear_extrude(height=HEIGHT, center=true, slices=SLICES, twist=TWIST, scale=SCALE)
                polygon(points_koch);
            scale([1,1,1.01])
                linear_extrude(height=HEIGHT, center=true, slices=SLICES, twist=TWIST, scale=SCALE)
                    offset(r = -WIDTH)
                        polygon(points_koch);
        }
        translate([0,0,-HEIGHT/2]) linear_extrude(height=WIDTH, center=false, slices=1)
            polygon(points_koch);
    }
}

function koch(N, points, first = false) = N==0 ? (first ? points : [points[1]]) :
    let (
        m = (points[0]+points[1])/2,
        v = points[1]-points[0],
        l = norm(v),
        n = [v[1], -v[0]]/l,
        a = l/3,
        h = a * sqrt(3) / 2,
        p1 = points[0] + v / 3,
        p2 = m + n*h,
        p3 = points[0] +2*v/3
    )
    concat(
        first ? [points[0]] : [],
        koch(N-1, [points[0], p1]),
        koch(N-1, [p1, p2]),
        koch(N-1, [p2, p3]),
        koch(N-1, [p3, points[1]])
        );
```


[User]
I'm looking to create a parametric snowman generator in OpenSCAD. I'd like to be able to specify the overall size of the snowman by setting the diameter of the bottom snowball. All other dimensions should be derived from this single parameter. I also want to be able to output a complete snowman model or individual parts of the snowman separately.

Here are the details:

**General Requirements**

*   **Parametric Design:** Everything should be driven by a single parameter: `bottom_diameter` (diameter of the bottom snowball). All other dimensions (radii, heights, offsets, diameters of other parts) should be calculated based on this value.
*   **Modular Design:** The snowman should be built from modules to allow easy selection of a single part or the whole snowman.
*   **Printer Friendly:** I need all vertical dimensions to be rounded to multiples of a configurable printer layer height (`layer_height`) and all horizontal dimensions to be rounded to multiples of a configurable printer nozzle diameter (`nozzle_diameter`).
*   **Part Selection:** I need a parameter, probably a string, to select which part(s) to generate from the modules. Possible values for this parameter should be:
    *   `"man"`: The complete snowman, including the body and hat.
    *   `"body"`: The complete body of the snowman (all three snowballs).
    *   `"hat"`: Just the hat.
    *   `"botmid"`: The bottom and middle snowballs combined.
    *   `"midtop"`: The middle and top snowballs combined.
    *   `"bot"`: The bottom snowball only.
    *   `"mid"`: The middle snowball only.
    *   `"top"`: The top snowball only.
    *   `"none"`: Generate nothing (useful for importing into other projects).
*   **Customization:**
    *   Include or exclude buttons on the middle snowball (boolean parameter `include_buttons`).
    *   Include or exclude a hanger eyelet on the hat (boolean parameter `include_eyelet`).
    *   Option to cut off the back half of any generated part to create a flat surface (boolean parameter `flat_back`).

**Specific Dimensions (relative to the bottom snowball diameter)**

*   **Bottom Snowball:**
    *   Radius: `bottom_diameter / 2`
*   **Middle Snowball:**
    *   Radius: 0.75 times the bottom snowball radius
    *   Flattened top (cut off to make a flat surface for the top snowball): 0.13 times the middle snowball radius (before any rounding)
    *   Cut off from the bottom to match the flat top of the bottom snowball, based on the calculated flat_top value of the bottom snowball.
*   **Top Snowball:**
    *   Radius: 0.5 times the bottom snowball radius
    *   Flattened top: 0.11 times the top snowball diameter (before any rounding).
    *   Cut off from the bottom to match the flat top of the middle snowball, based on the calculated flat_top value of the middle snowball.
*   **Base:**
    *   Base Radius equal to the top snowball radius.
    *   Base height is 0.15 times the bottom snowball diameter.
*   **Hat:**
    *   Hat Diameter: 0.6 times the top snowball diameter
    *   Hat Height: 0.7 times the top snowball diameter.
    *   Brim Diameter: equal to top snowball diameter.
    *   Brim Height: 0.2 times the hat height.
    *   Hat Peg Diameter: 0.2 times the top snowball diameter
    *   Hat Peg Height: a fixed 6mm
*   **Buttons:**
    *   Button Diameter: 0.12 times the middle snowball diameter
    *   Button Gap (spacing between buttons): 0.2 times the middle snowball diameter
    *   Button Offset (from the center plane): 1/3 of the Button Gap
*   **Face:**
    *   Eye Diameter: 0.12 times the top snowball diameter
    *   Eye Gap: 0.16 times the top snowball diameter (distance from the center of the eye to the center of the snowman)
    *   Eye Separation: 0.36 times the top snowball radius
    *   Nose Diameter: 0.14 times the top snowball diameter
    *   Nose Length: 0.13 times the top snowball diameter
    *   Nose Gap: 0.3 times the eye gap
    *   Mouth Diameter: 0.09 times the top snowball diameter
    *   Mouth Offset: 0.08 times the top snowball diameter
    *   Mouth Gap: 0.2 times the mouth diameter
    *   Mouth Separation: calculated so that the mouth is roughly centered with the eye separation.
*  **Hanger Eyelet:** (when `include_eyelet` is enabled)
     * Eyelet Diameter: 6.4mm
     * Eyelet Thickness: 2.0mm
     * Eyelet Wall Thickness: 1.8mm
*  **Pegs and Holes:**
     * Peg diameter is calculated as described above.
     * Peg height is a fixed 6mm.
     * Hole diameter for pegs is peg diameter + a small tolerance value set by parameter `peg_tolerance`
     * Hole height is equal to the peg height plus `peg_tolerance`*2

**Printer Parameters:**

*   `layer_height`: The printer layer height.
*   `nozzle_diameter`: The printer nozzle diameter.
*   `peg_tolerance`: The tolerance to add to peg holes.

**Additional Notes**

*   Use functions to convert dimensions to an exact multiple of `layer_height` (for vertical dimensions) and `nozzle_diameter` (for horizontal dimensions)
*   Use the `adornSphere` module (as defined in my existing SCAD code) to correctly position the eye, nose, mouth and buttons.
*   Use a `difference` to create the flat bottoms for each sphere.

It seems like I should be able to create an all-in-one SCAD file, but if you think it would be clearer to break things out into separate files, that would work too. I'm not using any libraries like MCAD, BOSL, etc., so there's no need to include any specific file paths or library requests, just the modules and functions in the OpenSCAD file(s). I hope this is detailed enough. Let me know if you have any questions.

[Assistant]
snowman.scad:
```
/*  PARAMETERIZED AND MODULAR SNOWMAN - OPENSCAD
    Copyright (C) 2018 by David L. March
    Last Revised: September 4, 2018

  This program is free software licensed under the Creative Commons
  Attribution-NonCommercial-ShareAlike license. You may:
    1) Copy and redistribute this program in any medium or format.
    2) Remix, transform, and build upon this program.
  as long as you:
    1) Give appropriate comment, provide a link to the license,
       and indicate any changes made.
    2) Do not use this program or the models it produces for
       commercial purposes.
    3) If you remix, transform, or build upon this material, you
       must redistribute the contributions under the same license
       as this original.

  The complete license can be found at:
    https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode

  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
  Section 5 of the legal code for further details.
*/
//==============================================================

//Roundness (default 120)
  $fn = 120;

//==============================================================
//  REQUIRED PARAMETERS
//--------------------------------------------------------------
/* [Snowman] */

//Part to be created by snowman module (default = none)
  part = "none";    //[man,body,hat,botmid,midtop,bot,mid,top,none]

//Diameter of bottom snowball (default = 32)
  diam = 32;        //[24:250]

//Include buttons on middle snowball? (default = 1:true)
  wantButtons = 1;    //[1:true,0:false]

//Include hanger eyelet on hat? (default = 0:false)
  wantEyelet = 0;    //[1:true,0:false]

//Cut back half to make it flat? (default = 0:false)
  wantHalf = 0;      //[1:true,0:false]

/* [Printer] */

//Printer layer height (default = 0.2)
  layer = 0.2;      //[0.10,0.15,0.20,0.25,0.30]

//Printer nozzle diameter (default = 0.4)
  nozzle = 0.4;     //[0.2,0.3,0.4,0.6]

//Printer tolerance for inserting pegs into holes (default = 0.8)
  toler = 0.8;      //[0.20:1.00]

//==============================================================
//  MAIN EXECUTION
//--------------------------------------------------------------

  snowman();

  module snowman()
  {
      difference()
      {
          snowmanPart();
          if (wantHalf != 0)
            translate([-botRadius-1,0,-1])
    	  cube([botDiam+2,botRadius+1,hatTop+2]);
      }
  }

  module snowmanPart()
  {
      echo("SNOWMAN",Part=part,Diam=diam,Round=$fn);
      echo("-----",Buttons=wantButtons,Eyelet=wantEyelet,Half=wantHalf);
      echo("PRINTER",Layer=layer,Nozzle=nozzle,Toler=toler);
  //Snowman
      if (part == "man")
        man(true,false,false);
      else if (part == "body")
        body(true,false,true);
      else if (part == "hat")
        hat(true,wantEyelet);
  //Compound parts
      else if (part == "botmid")
        bodyBotmid();
      else if (part == "midtop")
        bodyMidtop();
  //Separate parts
      else if (part == "bottom" || part == "bot")
        translate([0,0,botRadius])
          bodyBot(true);
      else if (part == "middle" || part == "mid")
        translate([0,0,midRadius-midCut])
          bodyMid(true,true);
      else if (part == "top")
        translate([0,0,topRadius-topCut])
          bodyTop(true,true,true);
  //  else
  //    do nothing to allow for include into custom programs
  }

//==============================================================
//  DERIVED PARAMETERS
//--------------------------------------------------------------

//Convert dimensions to exact multiples of printer specs
    function printXY(length) = round(length / nozzle) * nozzle;
    function printZ(height) = round(height / layer) * layer;

//Bottom snowball
      botRadius = printZ(diam / 2.0);
      botDiam = botRadius * 2.0;
  //Amount to flatten top of bottom sphere
      botFlat = printZ(botRadius * 0.15);

//Middle snowball
      midRadius = printZ(botRadius * 0.75);
      midDiam = midRadius * 2.0;
  //Amount to flatten top of middle sphere
      midFlat = printZ(midRadius * 0.13);
  //Amount to cut off bottom of middle sphere tp match flat of bottom sphere
      midCut = printZ(smallCut(botRadius,midRadius,botFlat));

//Top snowball
      topRadius = printZ(botRadius * 0.50);
      topDiam = topRadius * 2.0;
  //Amount to flatten top of top sphere
      topFlat = printZ(topDiam * 0.11);
  //Amount to cut off bottom of top sphere to match flat of middle sphere
      topCut = printZ(smallCut(midRadius,topRadius,midFlat));

//Snowman base
    baseRadius = topRadius;
    baseHeight = printZ(botDiam * 0.15);

//Hat
    hatDiam = printXY(topDiam * 0.60);
    hatHeight = printZ(topDiam * 0.70);
    brimDiam = printXY(topDiam);
    brimHeight = printZ(hatHeight * 0.20);
//Hat peg
    pegDiam = printXY(topDiam * 0.20);
    pegHeight = printZ(6.0);
    pegHole = pegHeight + toler * 2;

//Body buttons
    buttonDiam = printZ(midDiam * 0.12);
    buttonGap = printZ(midDiam * 0.20);
    buttonOff = printZ(buttonGap / 3.0);

//Face eyes
    eyeDiam = printZ(topDiam * 0.12);
    eyeGap = printZ(topDiam * 0.16);
    eyeSep = printXY(topRadius * 0.36);

//Face nose
    noseDiam = printZ(topDiam * 0.14);
    noseLength = printXY(topDiam * 0.13);
    noseGap = printZ(eyeGap * 0.30);

//Face mouth
    mouthDiam = printZ(topDiam * 0.09);
    mouthOff = printZ(topDiam * 0.08);
    mouthGap = printZ(mouthDiam * 0.20);
    mouthSep = (eyeSep + mouthDiam*2) / 4.0;

//Hanger eyelet (add 0.0 to hide from customizer)
    eyeletDiam = 6.4 + 0.0;
    eyeletRadius = eyeletDiam / 2.0;
    eyeletThick = 2.0 + 0.0;
    eyeletWall = 1.8 + 0.0;
    eyeletHole = eyeletDiam - eyeletWall * 2;

//Z-axis offsets to move bottom of centered upper part to top of lower part
    midBot = botDiam - botFlat - midCut + midRadius;
    topBot = midBot + midRadius - midFlat - topCut + topRadius;
    hatBot = topBot + topRadius - topFlat;
    hatTop = hatBot + brimHeight + hatHeight;

//--------------------------------------------------------------
    echo("SNOW BALLS",Bot=botDiam,Mid=midDiam,Top=topDiam);
    echo("BALL FLATS",Bot=botFlat,Mid=midFlat,Top=topFlat);
    echo("HAT TOP",Diam=hatDiam,Height=hatHeight);
    echo("HAT BRIM",Diam=brimDiam,Height=brimHeight);
    echo("HAT PEG",Diam=pegDiam,Height=pegHeight);
    inches = hatTop / 25.4;
    echo("TOTAL HEIGHT",METRIC=hatTop,IMPERIAL=inches);
//--------------------------------------------------------------
/*  Calculate the radius of the X-Y plane that is offset by
    zOff above or below the sphere center.
*/
    function planeRadius(sphereRadius,zOff) =
      sqrt(sphereRadius*sphereRadius - zOff*zOff);

/*  Calculate the Y-axis offset to a point on the sphere's
    surface that corresponds to the specified X-axis and
    Y-axis offset from a point on the front surface of
    the sphere that with a center at [0,0,0].  Note that
    if xOff=0 and zOff=0 the result is the radius of
    the sphere.
*/
  function yOffset(sphereRadius,xOff,zOff) =
    sqrt(sphereRadius*sphereRadius - xOff*xOff - zOff*zOff);

/*  Rotate and translate child to put it on the surface of a
    sphere located at [0,0,0].  The child is added at a point
    on the sufface that is offset to the left or right of the
    Z-axis (the xOff) and above or below the X-axis (the zOff).
    Note that the rotation is an approximation but it works
    well enough for children placed on the front of the sphere.
*/
  module adornSphere(sphereRadius,xOff,zOff)
  {
      yOff = yOffset(sphereRadius,xOff,zOff);
  //Compute radius of circular plane at zOff above X-axis
      radius = sqrt(sphereRadius*sphereRadius - zOff*zOff);
  //Compute angles to rotate so dot is tangent to sphere
      xAng = asin(zOff / sphereRadius);
      zAng = asin(xOff / radius);
  //Add children to sphere
      translate([xOff,-yOff+nozzle*2,zOff])
        rotate([90-xAng,0,zAng])
          children();
  }

/*  Calculate the amount to cut off the bottom of the small
    radius sphere so that the radius of the flat surface on
    the small sphere will exactly match the radius of the
    flat surface of the large radius sphere that was cut.
*/
  function smallCut(large,small,largeCut) =
    small - sqrt(small*small - large*large + (large-largeCut)*(large-largeCut));

//--------------------------------------------------------------

  module bodyDot(dotDiam,sphereRadius,xOff,zOff)
  {
      adornSphere(sphereRadius,xOff,zOff)
      {
        cylinder(d=dotDiam,h=nozzle*2.0);
        color("black")
          cylinder(d=dotDiam-layer*2.0,h=nozzle*3.0);
      }

  }

  module bodyCone(diam,length,sphereRadius,xOff,zOff)
  {
      adornSphere(sphereRadius,xOff,zOff)
        color("red")
          cylinder(d1=diam,,d2=diam/2.0,h=length+nozzle*2);
  }

  module bodyPeg(zOff)
  {
      translate([-pegDiam/2,-pegDiam/2,zOff-layer])
      {
        cube([pegDiam,pegDiam,pegHeight+layer]);
	translate([pegDiam/2.0,pegDiam,0])
	  cylinder(d=pegDiam,h=pegHeight+layer);
      }
  }

  module bodyHole(zOff)
  {
      holeDiam = pegDiam + toler;
      translate([-holeDiam/2,-holeDiam/2,zOff-layer])
      {
        cube([holeDiam,holeDiam,pegHole+layer]);
	translate([holeDiam/2.0,holeDiam,0])
	  cylinder(d=holeDiam,h=pegHole+layer);
      }
  }

  module eyelet()
  {
      difference()
      {
        union()
        {
          cylinder(d=eyeletDiam,h=eyeletThick);
          translate([-eyeletRadius,-eyeletRadius,0])
            cube([eyeletDiam,eyeletRadius,eyeletThick]);
        }
        translate([0,0,-1])
          cylinder(d=eyeletHole,h=eyeletThick+2);
      }
  }

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

  module bodyBot(peg=false)
  {
      difference()
      {
        sphere(r=botRadius);
    //Flatten top if want a peg
        if (peg == true)
          translate([0,0,botRadius-botFlat])
            cylinder(r=botRadius,h=botFlat+1);
      }
  //Add conical base
      baseOff = botRadius - baseHeight;
      baseTop = sqrt(botRadius*botRadius - baseOff*baseOff);
      translate([0,0,-botRadius])
        cylinder(r1=baseRadius,r2=baseTop,h=baseHeight);
  //Add peg
      if (peg == true)
        bodyPeg(botRadius-botFlat);
  }

  module bodyMid(hole=false,peg=false)
  {
      difference()
      {
        sphere(r=midRadius);
    //Flatten top if want a peg
        if (peg == true)
          translate([0,0,midRadius-midFlat])
            cylinder(r=midRadius,h=midFlat+1);
    //Flatten bottom and cut hole if want a peg hole
        if (hole == true)
        {
          translate([0,0,-midRadius-1])
            cylinder(r=midRadius,h=midCut+1);
          bodyHole(-midRadius+midCut);
        }
      }
  //Add peg
      if (peg == true)
        bodyPeg(midRadius-midFlat);
  //Add buttons
      if (wantButtons != 0)
      {
        bodyDot(buttonDiam,midRadius,0,buttonGap+buttonOff);
        bodyDot(buttonDiam,midRadius,0,buttonOff);
        bodyDot(buttonDiam,midRadius,0,-buttonGap+buttonOff);
      }
  }

  module bodyTop(face=true,hole=false,peg=true)
  {
      topOff = topRadius - topFlat;
      difference()
      {
        sphere(r=topRadius);
    //Flatten top for hat
        translate([0,0,topOff])
          cylinder(r=topRadius,h=topFlat+1);
    //Flatten bottom and cut hole if want hole
        if (hole == true)
        {
          translate([0,0,-topRadius-1])
            cylinder(r=topRadius,h=topCut+1);
          bodyHole(-topRadius+topCut);
        }
      }
  //Add peg for hat
      if (peg == true)
        translate([0,0,topOff-layer])
          cylinder(d=pegDiam,h=pegHeight);
  //Adorn face
      if (face == true)
      {
    //Eyes
        bodyDot(eyeDiam,topRadius,+eyeSep,eyeGap);
        bodyDot(eyeDiam,topRadius,-eyeSep,eyeGap);
    //Nose
        bodyCone(noseDiam,noseLength,topRadius,0,noseGap);
    //Mouth
        bodyDot(mouthDiam,topRadius,-mouthSep*2,-mouthOff);
        bodyDot(mouthDiam,topRadius,-mouthSep,  -mouthOff-mouthGap*2);
        bodyDot(mouthDiam,topRadius, 0,         -mouthOff-mouthGap*3);
        bodyDot(mouthDiam,topRadius,+mouthSep,  -mouthOff-mouthGap*2);
        bodyDot(mouthDiam,topRadius,+mouthSep*2,-mouthOff);
      }
  }

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

  module body(face,join,peg,offset=0)
  {
      translate([0,0,botRadius])
        bodyBot(join);
      translate([0,0,midBot+offset])
        bodyMid(join,join);
      translate([0,0,topBot+offset*2])
        bodyTop(face,join,peg);
  }

  module bodyBotmid()
  {
      translate([0,0,botRadius])
        bodyBot(false);
      translate([0,0,midBot])
        bodyMid(false,true);
  }

  module bodyMidtop()
  {
      translate([0,0,midRadius-midCut])
        bodyMid(true,false);
      translate([0,0,topBot-midBot + midRadius - midCut])
        bodyTop(true,false,true);
  }

  module hat(hole=true,eyelet=true)
  {
      difference()
      {
    //Create hat
        union()
        {
          cylinder(d=brimDiam,h=brimHeight);
          translate([0,0,brimHeight-layer])
            cylinder(d=hatDiam,h=hatHeight+layer);
        }
    //Cut peg hole
        if (hole == true)
          translate([0,0,-1])
            cylinder(d=pegDiam+toler,h=pegHole+1);
       }
  //Add eyelet
      if (wantEyelet != 1 || eyelet == true)
      {
        translate([0,eyeletThick/2.0,brimHeight+hatHeight+eyeletWall])
          rotate([90,0,0])
            color("black")
              eyelet();
      }
  }

  module man(face,join,peg,offset=0)
  {
      body(face,join,peg,offset);
      translate([0,0,hatBot+offset*3])
        color("black")
          hat(true);
  }

```


[User]
I need an OpenSCAD design for a bottle holder, specifically for Pico Squeeze squonk bottles. I'd like it to be parametric so I can easily adjust the number of bottles it holds and other dimensions.

Here are the specifics:

1.  **Bottle Dimensions:** The bottles are 19.25mm in diameter at their widest point. The cap length is 15mm, and the total bottle length is 33.75mm + the cap length, so 48.75mm in total.

2.  **Bottle Count:** I'd like to be able to specify the number of bottles to hold. Start with a default of 4 bottles but allow me to easily change it to any whole number of bottles (say up to 8, for now).

3.  **Bottle Spacing:** There needs to be a small space between each bottle. I'd like to set this to 0.8mm.

4.  **Container:** The bottles should sit inside a container with a rounded rectangular shape. The container dimensions need to accommodate the bottles, spacing, and an additional 0.8mm of spacing on all sides. The total width of the container should be calculated as `bottle_spacing + (bottle_diameter+bottle_spacing)*bottle_count`. The height will be the bottle diameter.  The depth will be `bottle_length + (bottle_spacing*2)`.  The container should have a rounding radius applied to its edges, about 2mm, but I would like to be able to adjust that.

5.  **Rounded Rectangle:** I'd like to use a custom module, possibly called `rounded_rectangle`, for creating the container with rounded edges.

6.  **Juice Viewing Window:** Include a small, rectangular window on the back of the holder for each bottle so I can easily check the juice levels. These windows should be (bottle_diameter/4) wide, 10mm high, and (bottle_length - bottle_cap_length) deep. The windows should be centered on the rear of each bottle space.  This should be able to be toggled on or off as well.

I'm hoping you can generate an OpenSCAD file using the parameters above. The `hull()` and `linear_extrude()` functions are helpful for generating the rounded container, so be sure to make use of those. I don't need any special libraries or anything.  Thanks.

[Assistant]
pico_squeeze_bottle_holder.scad:
```
//Diameter, at the widest, for a standard Pico Squeeze squonk bottle
bottle_diameter = 19.25;

//Length of the bottle cap
bottle_cap_length = 15;

bottle_length = 33.75 + bottle_cap_length;

//How many bottles?
bottle_count = 4; //[1:8]

//The space between each bottle
bottle_spacing = 0.8;

container = [
     bottle_spacing + (bottle_diameter+bottle_spacing)*bottle_count,
     bottle_diameter,
     bottle_length + (bottle_spacing*2)
];

container_rounding_radius = 2 * 1; // * 1 hides this from thingiverse's customizer

//Include rear juice level viewing window?
include_window = 1; //[1:true, 0:false]

module rounded_rectangle(size, radius) {
     x = size[0];
     y = size[1];
     z = size[2];

     linear_extrude(height = z, center = true) hull() {
          translate([(-x/2)+(radius/2), (-y/2)+(radius/2), 0])
               circle(r=radius);
          translate([(x/2)+(radius/2), (-y/2)+(radius/2), 0])
               circle(r=radius);
          translate([(-x/2)+(radius/2), (y/2)-(radius/2), 0])
               circle(r=radius);
          translate([(x/2)+(radius/2), (y/2)-(radius/2), 0])
               circle(r=radius);
     }
}

translate([0,0,0]) {
    difference() {
      rounded_rectangle(container, container_rounding_radius, $fn=100);
      translate([
        -(container.x/2)+(container_rounding_radius/2)+(bottle_spacing+(bottle_diameter/2)),
        -(container_rounding_radius),
        0
      ]) {
          for (bottle = [0:(bottle_count-1)]) {
            translate_vector = [
              (bottle)*(bottle_diameter+bottle_spacing),
              0,
              (bottle_spacing*2)
            ];

            translate(translate_vector) {
              cylinder(
                h = bottle_length,
                d = bottle_diameter,
                $fn = 100,
                center = true
              );
            }

            if (include_window) {
              translate([translate_vector.x, +(bottle_diameter/2), translate_vector.z - (bottle_cap_length/2)])
                cube([(bottle_diameter/4), 10, bottle_length - bottle_cap_length], center = true);
            }
          }
      }
    }
}

```


[User]
Hey, I'm looking for some help designing a parametric tool organizer, specifically for screwdrivers. I've got a basic idea in mind based on an existing design, but I'd like it to be more customizable. I'd like to be able to specify the number of tools it holds, the width and depth of the cutouts, the spacing between the cutouts, and the overall height and width of the organizer.

Essentially, the organizer should be a long rectangular prism with a series of cylindrical cutouts along its length, with flat sections leading up to each cylindrical cutout to support the tool. The cutouts should all be the same size and spacing, with the flat sections leading to each cylindrical cutout. I'm envisioning it as being generated with a difference between a rectangular prism and some number of cutouts.

I'd like the cylindrical portion of the cutout to extend a bit beyond the bottom of the rectangular base, to create more of a "cup" that holds the screwdriver in place.

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

*   `organizerHeight`: The overall height of the base.
*   `organizerWidth`: The overall width of the base.
*   `numberOfTools`: The number of screwdriver cutouts.
*   `toolCutoutWidth`: The width/diameter of the cylindrical cutout.
*   `toolCutoutDepth`: The depth of the cylindrical cutout, including the portion extending below the main base.
*   `spaceBetweenToolCutouts`: The space between each cutout.

I'd like a single .scad file with all of this, if possible. Thanks!

[Assistant]
Tool_organizer.scad:
```
//perameters
organizerHeight = 15;
organizerWidth = 25;

numberOfTools = 10;
toolCutoutWidth = 10;
toolCutoutDepth = 10;
spaceBetweenToolCutouts = 10;

//Find the initial length of the organizer
organizerLength = (numberOfTools*toolCutoutWidth)+(numberOfTools*spaceBetweenToolCutouts);
difference () {
//Generate the base shape
cube(size=[organizerLength,organizerWidth,organizerHeight], center = false);
    for(i = [0:numberOfTools-1]) {
        //Generate a cutout for each number of tools
        cutoutSpot = (spaceBetweenToolCutouts/2) + (i * (toolCutoutWidth+spaceBetweenToolCutouts));

        //Cube for the flat sides
        translate([cutoutSpot,-5,organizerHeight-toolCutoutDepth+toolCutoutWidth/2])
        cube([toolCutoutWidth,organizerWidth+10, toolCutoutDepth]);

        //Cylinder for the rounded bottom
        rotate([-90,0,0])
        translate([toolCutoutWidth/2+cutoutSpot,-(organizerHeight)+toolCutoutDepth-toolCutoutWidth/2,-10])
        cylinder(h=organizerWidth+20, r=toolCutoutWidth/2, center=false, $fn=100);
    }
}

```


[User]
Hey, I'm looking to design a customizable pill case using OpenSCAD and I found this design on Thingiverse (https://www.thingiverse.com/thing:4866368). It's a pretty good starting point, but I'd like to make it fully parametric and flexible and have a design that's a bit more robust as well as easier to understand and modify.

The basic idea is a cylindrical pill case with a number of equally-sized segments around the circumference. Each segment should have a small, printed-in-place label on the outer wall. There's a lid that fits over the top. The critical dimensions are the overall diameter, the height of the main body, and the wall thickness.

Here's what I want it to do:
*   **Number of Segments:** Fully adjustable, so I can specify how many compartments the case has.
*   **Outer Diameter:** I need to be able to set the overall diameter of the case.
*   **Inside Height:**  The usable height of the pill compartment space needs to be a variable.
*   **Wall Thickness:** A customizable wall thickness for the case body and lid is essential.
*   **Lid Fit:** The lid needs to fit snugly but still be easily removable. The original design had a notch and some extra material on the lid. I'd like a design that's a bit more robust and easier to print, but I do want a tight fit.
*   **Printed Labels:** Each segment should have a printed-in-place label (number) on the outside. I want to adjust the size and position of these.
*   **Material Volume:** I'd like an echo to the console of the approximate material volume of a single segment in cubic cm.

I don't need to use an existing library, unless it helps with the parametric design, in which case, I'd prefer BOSL or BOSL2 for any added modules you might find handy. I'd also really like you to include some comments in the code to explain how each of the parameters works, since it can get a bit hard to keep track of it all. I would like the entire design to be generated as if from scratch.

Thanks!

[Assistant]
Pill_Box.scad:
```
// - Number of segments
N = 9;

// - Outer diameter of body
D = 70;

// - inside height of body
H = 25;

// - wall thickness
t = 1.2;

// - print quality
$fn=60;

/* [hidden] */
c = 0.8;
R = D/2;
NotchZ = max(H-10, H/2);
unitVol = (3.14159*pow(R-t,2) - (R-t)*t )*H /(1000*(N+1));
echo(unitVol, "cm^3");
echo(str("FileName: PillCase2 ",N," ",D," ",H," ",t));

//Body();
//Lid();
//PrintConfig();
FitConfig();

module Body() {
    //body polygon starting at origin and going CCW
    pBody = [
        [0,         0],         //origin
        [R,       0],           //Bottom outer edge
        [R,       NotchZ-t/2],  //Bottom of notch
        [R-t/2,   NotchZ],      //Center of notch
        [R,       NotchZ+t/2],  //Top of notch
        [R,       H],           //Top outer edge
        [R-t,     H],           //Top inner edge
        [R-t,     t],           //Bottom inner edge
        [0,         t]];        //I/S Center
    rotate_extrude() {
        polygon(pBody);
    }
    //walls
    for(a=[0:360/(N+1):360]) {
        rotate(a) translate([0, -t/2, 0]) {
            cube([R-t, t, H]);
        }
    }
    //filled segment. N-NE
    intersection() {
        linear_extrude(H)
        polygon([
            [0,0],
            [D,0],
            [D,D*10],
            [R*cos(360/(N+1)), R*sin(360/(N+1))]]);
        cylinder(d=D-t,H);
    }
    //locating notches
    rotate(0) translate([R-0.3*t,0,NotchZ]) scale([1,1,2]) {
        sphere(d=1.5*c);
    }
    rotate(360/(N+1)) translate([R-0.3*t,0,NotchZ]) scale([1,1,2]) {
        sphere(d=1.5*c);
    }

    //labels going CW
    textWd = min(3.14159*D/(N+1)*0.8, D/10);
    textHt = min(textWd*1.4, H/2-2);
    textTk = 0.7;
    for(n = [1:N]) {
        ang = -360/(N+1)*(n-0.5);
        rotate(ang)
        translate([R-textTk*1.5, 0, 0.5*NotchZ])
        rotate([90,0,90])
        linear_extrude(2*textTk)
        resize([textWd, textHt])
        text(str(n), halign="center",, valign="center", font="Arial");
    }
}

module Lid() {
    //lid polygon starting inside center and going CCW
    pLid = [
        [0,       H+c],             //inside center
        [R+c,     H+c],             //top inside ring
        [R+c,     NotchZ+t/2+c],    //top of notch
        [R-t/2,   NotchZ],          //point of notch
        [R+t+c,     NotchZ],          //back of notch
        [R+t+c,     H+c+t],           //top outer edge
        [0,       H+c+t]];          //top center

    difference() {
        //body
        rotate_extrude() {
            polygon(pLid);
        }
        //splits
        splitLen = min(R/2, 15, 0);
        for(a=[0:360/(N+1):360]) {
            rotate(a) translate([R-splitLen, -c/2]) {
                cube([R,c,H*2]);
            }
        }
        //opening
        difference() {
            //triangular wedge
            linear_extrude(H*2)
            polygon([
                [0,0],
                [D,0],
                [D,D*10],
                [R*2*cos(360/(N+1)), R*2*sin(360/(N+1))]]);
            //central cylinder
            cylinder(d=R,H*2);
        }
    }
}

module PrintConfig() {
    Body();
    rotate([180,0,0]) translate([D+t*2, 0, -H-t-c]) {
        Lid();
    }
}

module FitConfig() {
    intersection() {
        union() {
            Body();
            Lid();
        }
        rotate(-45/2) cube(D);
    }
}
```


[User]
I'm looking to create a small, cross-shaped support piece for drawer dividers. I want to be able to slot in pieces of cardboard or other thin material to create the dividers. I need this to be customizable so I can adjust it to various material thicknesses and drawer sizes.

Here's what I'm picturing:

*   The support will be essentially two rectangular prisms intersecting at their centers to form a cross.
*   There will be a slot in each arm of the cross to hold the divider material. This slot needs to have a configurable thickness to match the divider material.
*   There needs to be a bit of wall thickness around the slot to provide support.
*   The height of the slot/support should also be configurable, as should the overall width of the cross.

Specifically, I need these parameters to be customizable:

*   `divider_thickness`: The thickness of the cardboard or other material I'll be using for the dividers.
*   `support_wall_thickness`: The thickness of the outer wall around the slot.
*   `support_height`: How far the divider material can be pushed into the support.
*   `support_width`: The overall size/length of the cross arms.

Could you generate a parametric OpenSCAD file that allows me to configure these parameters to generate the cross-shaped support? I'd also like the script to define the `support_length` to be equal to `support_width`. The total thickness of each arm (`total_thickness`) should be the `divider_thickness` + 2*`support_wall_thickness`.

[Assistant]
Drawer_divider_supports.scad:
```
// configurable stuff

divider_thickness = 1; 			// [0.1:0.1:100]
support_wall_thickness = 2;		// [0.1:0.1:100]
support_height = 15;			// [0.1:0.1:100]
support_width = 30;				// [0.1:0.1:100]

/* [Hidden] */

support_length = support_width;
total_thickness = divider_thickness + (2 * support_wall_thickness);

module makebox ( xbox, ybox, zbox ) {

    translate([-(xbox/2), -(ybox/2), 0])
    cube([xbox, ybox, zbox]);

}

difference() {

union() {
    makebox(total_thickness, support_width, support_height);
    makebox(support_length, total_thickness, support_height);

}

translate([0,0,support_wall_thickness]) makebox(divider_thickness,support_width+1, support_height);
translate([0,0,support_wall_thickness]) makebox(support_length+1, divider_thickness, support_height);

}
```