[User]
Okay, I'm looking to create a design inspired by Mary Martin's "Inversions" artwork, specifically the version that has a grid of square tiles, each with a triangular prism extending from the base. I've found a neat example of this as OpenSCAD code already, but I need to generate my own from scratch and I'm hoping you can help with that.

Here's what I'm thinking:

**Overall Structure:**

*   The design will be a rectangular base with a grid of tiles on top.
*   Each tile will have a triangular prism that extends upwards.
*   Each prism will have a random rotation around the z-axis, in multiples of 90 degrees.

**Parameters:**

I'd like the following parameters to control the design:
*   `random_seed`:  A seed to control the randomness of the tile rotation. It should use the `rands()` function.
*   `width`: The overall length of the base along the x-axis.
*   `cols`: The number of square tiles across the length (x-axis).
*   `rows`: The number of square tiles vertically (y-axis).
*   `height`: The height of each triangular prism.
*   `baseheight`: The height of the rectangular base

**Specifics for the SCAD design:**

*   The tiles must be square; the side of the square tile can be derived from `width` and `rows` as  `side = width/rows`.
*   The overall length of the design should be a derived value, so `length = cols * side;`.
*   The rotation of each tile should be controlled by `floor(rands(0,3.999,rows*cols,random_seed))` and rotated in multiples of 90 degrees around the z-axis.
*   Each tile should have its base at [0,0,0], with the point at height located directly above one of the edges.
*   I want to use a `grid()` module to organize the layout, `tile()` module to create the prisms, and `base()` to build the base.

**Libraries:**

*   I am *not* using any external libraries, everything should be in a single file.

**Output:**
The output should be a single scad file.

Can you provide the OpenSCAD code for this design? I'm excited to see what you come up with!

[Assistant]
mary_martin.scad:
```
// inspired by Mary Martin's work "Inversions"
// https://www.tate.org.uk/art/artists/mary-martin-1586
// https://en.wikipedia.org/wiki/Mary_Martin_(artist)
//
// by Laura Taalman and Edmund Harriss
// http://mathgrrl.com http://maxwelldemon.com
// Made at the #0things Hackathon at Construct3d 2018
//
// Inspired by British constructivist Artist Mary Martin
// https://www.tate.org.uk/art/artists/mary-martin-1586
// https://en.wikipedia.org/wiki/Mary_Martin_(artist)
// and her artwork Inversions
// https://www.tate.org.uk/art/artworks/martin-inversions-t01198
// Now in the Tate Gallery in London. We use random numbers here, but the original
// work uses the mathematical idea of permutations. Can you work out the structure hidden
// in her beautiful work?

/////////////////////////////////////////////
// parameters

// random seed
random_seed = 1729; // [1:1:9999]
//random_seed = floor(rands(1,9999,1)[0]);  // use for openscad
//random_seed = 1;                          // use for fixed seed

// Width of the canvas (long side)
width = 100;

// Number of pieces across the long side
cols = 12;

// Number of pieces vertically
rows = 4;

// Height of the pieces (out from the canvas)
height = 20;

// computed
side = width/rows;
length = cols*side;
baseheight = 2*1;

// random turn for each cell of grid
turn = rands(0,3.999,rows*cols,random_seed);

/////////////////////////////////////////////
// render

color("lightgray")
grid(side,height);

/////////////////////////////////////////////
// modules

module grid(side,height){
    translate([0,0,baseheight])
    for(j=[0:cols-1])
        for(i=[0:rows-1]){
            translate([j*side,i*side,0])
            translate([side/2,side/2,0])
            rotate(floor(turn[j+i*(cols-1)])*90,[0,0,1])
            translate([-side/2,-side/2,0])
            tile(side,height);
        }
    base(length,width,baseheight);
}

module tile(side,height){
    polyhedron(
        points=[[0,0,0],[0,side,0],[side,side,0],[side,0,0],
                [0,0,height],[0,side,height]],
        faces=[[0,1,2,3],[3,2,5,4],[0,3,4],[2,1,5],[0,4,5,1]]
    );
}

module base(length,width,baseheight){
    cube([length,width,baseheight]);
}

```


[User]
Hi! I'm looking to get a customizable camera mount designed for a BetaFPV 75x drone. The stock mount has a 35-degree angle, but I want to be able to adjust that. I'd like the design to be parametric so I can easily change the camera tilt angle and have it generate a new STL file.

I've seen some similar designs, and I think the basics should be relatively simple. The mount should have:

*   A main frame that attaches to the drone. This frame should have two mounting feet/holes on the sides and one on top.
*   A camera cradle that is connected to the main frame via a tilted support.
*   The camera cradle needs to have cutouts for the camera body, lens, and necessary wiring.
*   The whole thing should be printable, so I need to consider support material.

I'd like to be able to specify the following parameters:

*   `degrees`: The tilt angle of the camera in degrees, with the default being 25.
*   `outerDiameter`: The diameter of the mounting holes/feet. Should default to 6.5.
*   `holeDiameter`: The diameter of the actual screw holes within the mounting feet. Should default to 1.5.
*   `mountHeight`: The height/thickness of the mounting feet. Should default to 1.8.

And then a couple "hidden" parameters that shouldn't require changing:

*   `roundnessSteps`: Number of steps used for rounding curves of the camera cradle, should default to 15.
*   `$fn`: which I think is for segments of circles, should default to 64.

I'm not sure on specifics, but I imagine that we'll need to make use of basic shapes like cubes, cylinders, spheres, and then use `difference()` and `hull()` to get the correct shapes. I don't think I need to import any external libraries either.

I'm aiming for something like the attached file but starting from scratch would be preferred. Could you generate the OpenSCAD files needed?

[Assistant]
z02_mount.scad:
```
//#import("Z02_camera_mount.stl");

/* [General] */
// Amount of degrees the camera should have (35° is the BetaFPV default)
degrees = 25;

/* [HIdden] */
outerDiameter = 6.5;
holeDiameter = 1.5;
mountHeight = 1.8;

roundnessSteps = 15;

/* [Hidden] */
$fn = 64;
outerRadius = outerDiameter / 2;
holeRadius = holeDiameter / 2;

mountingFeet();
difference() {
  frame();

  translate([0, 12.91, 13.42])
    rotate([degrees, 0, 0])
      translate([0, -6.3, -3.6])
        camMount();
}
camMountSlots();

module camMountSlots() {
  translate([0, 12.91, 13.42]) {
    rotate([degrees, 0, 0])
      // Translate point of rotation
      translate([0, -6.3, -3.6]) {
        group() {
          difference() {
            group() {
              camMount();

              /*
              // Rotation Helper
              translate([-10, 6.3, 3.6])
                cube([20, 0.5, 0.5]);
              */
            }

            translate([0, -12 ,0]) {
              translate([-1.6, 10, -2])
                cube([3.2, 3.02, 20]);

              translate([-5.6, 13, -3])
                cube([11.2, 2.62, 20]);

              // Cutout lens
              group() {
                translate([0, 14.5, 4.7])
                  rotate([270, 0, 0])
                    cylinder(r=4.1, h = 5);

                translate([-4.1, 14.5, 4.7])
                  cube([8.2, 5, 5]);
              }

              hull() {
                translate([-3.6, 13.5, -0.513])
                  rotate([270,0,0])
                    cylinder(r=2, h = 3.53);

                translate([3.6, 13.5, -0.513])
                  rotate([270,0,0])
                    cylinder(r=2, h = 3.53);

                translate([-5.6, 13.5, 5])
                  cube([11.2, 3.53, 4]);
              }

              translate([-7, 13, 7.5]) {
                difference() {
                  cube([14, 6, 4]);
                  translate([0, 0, 0.92])
                  rotate([270 - degrees, 0, 0])
                    cube([14, 6, 4]);
                }
              }
            }
          }
        }
      }
  }
}

module camMount() {
  difference() {
    group() {
      hull() {
        translate([-5.55, 5.57, 3.50])
          rotate([0,0,0])
            cylinder(r=1.25, h = 4);

        translate([5.55, 5.57, 3.50])
          rotate([0,0,0])
            cylinder(r=1.25, h = 4);

        for(a = [0:roundnessSteps:90]) {
          translate([-2.8, 5.57, -1.3])
            rotate([0, -a, 0])
              translate([-3 + 0.25, 0, 0])
                rotate([270,0,0])
                  sphere(r=1.25, h = 5.57);
        }

        for(a = [0:roundnessSteps:90]) {
          translate([2.8, 5.57, -1.3])
            rotate([0, a, 0])
              translate([3 - 0.25, 0, 0])
                rotate([270,0,0])
                  sphere(r=1.25, h = 5.57);
        }
      }

      hull() {
        translate([-5.81, 0, -4.3])
          rotate([270,0,0])
            cylinder(r=1, h = 4.03);

        translate([5.81, 0, -4.3])
          rotate([270,0,0])
            cylinder(r=1, h = 4.03);

        translate([-6.81, 0, 4.4])
          cube([13.62, 4.03, 4.1]);
      }

      hull() {
        for(a = [0:roundnessSteps:90]) {
          translate([-2.8, 0, -1.3])
            rotate([0, -a, 0])
              translate([-2.5, 0, 0])
                rotate([270,0,0])
                  cylinder(r=1.5, h = 5.6);
        }

        for(a = [0:roundnessSteps:90]) {
          translate([2.8, 0, -1.3])
            rotate([0, a, 0])
              translate([3, 0, 0])
                rotate([270,0,0])
                  cylinder(r=1, h = 5.6);
        }

        translate([-6.81, 0, 4.4])
          cube([13.62, 5.6, 4]);
      }
    }

    translate([0, -12 ,0]) {
      translate([-6.8 -1, 10, -6])
        cube([13.6 + 2, 3.02, 5.91]);
    }

  }
}

module frame() {
  frameHalf();

  mirror([1, 0, 0])
    frameHalf();
}

module frameHalf() {
  difference() {
    group(){
      translate([16.31, 0, 2.11]) {
        rotate([0, 0, 37.55]) {
          cube([1, 20, 4.457]);
          translate([-1, 0, 3.45])
            cube([2, 20, 1.0]);
        }
      }

      translate([13.5, -outerRadius, 0])
        cube([1.5, outerDiameter + 2, 2.11 + 3.45 + 1]);

      difference() {
        translate([0, 13.5, 0])
          cube([8, 1.5, 2.11 + 3.45 + 1]);

        translate([2, 13.4, -0.73])
          rotate([0, 59.5, 0])
            cube([4, 1.5 + 0.2, 5 + 2]);
      }

      translate([0, 10.1, 2.11])
        cube([9, 4.5, 1]);

      // Connector feet to mount
      hull() {
        translate([13.5, -3.25, 6.5])
            cube([1.5, 3, 0.1]);

        translate([0, 12.91, 13.42]) {
          rotate([degrees, 0, 0]) {
            translate([5.31, -6.3, 1.91]) {
              cube([1.5, 0.1, 3]);
            }
          }
        }
      }
    }

    translate([15, -outerRadius - 1, 0])
      cube([10, outerDiameter + 4, 2.11 + 3.45 + 1 + 1]);

    translate([17.57, 0, -1])
      rotate([0, 0, 37.55])
        cube([10, 20, 10]);

    translate([12.5, outerRadius, -1])
      cube([3.5, outerDiameter + 2, 4.1]);

    hull() {
      translate([8, 9.1, 2.1]){
        rotate([0, 90, 0])
          cylinder(r = 1, h = 5);

        translate([0, -6, -3.1])
          cube([5, 1, 4.1]);
      }
    }

    translate([-1, 15, -1]) {
      cube([8 + 2, 1.5, 8]);
    }
  }
}

module mountingFeet() {
  translate([-18, 0, 0])
    mountHole();

  translate([18, 0, 0])
    rotate([0, 0, 180])
      mountHole();

  translate([0, 18, 0])
    rotate([0, 0, 270])
      mountHole();
}

module mountHole() {
  difference() {
    hull() {
      cylinder(h = mountHeight, r = outerRadius);
      translate([3.5, -outerRadius, 0])
        cube([1, outerDiameter, mountHeight]);
    }

    translate([0, 0, -1])
      cylinder(h = mountHeight + 2, r = holeRadius);
  }

  translate([3.5 - 1.5, -outerRadius, 0])
    difference() {
      cube([2.5, outerDiameter, mountHeight + 1.3]);
      translate([0, -1, 2.8]) {
        hull(){
          rotate([-90,0,0])
            cylinder(r = 1, h=outerDiameter + 2);
            translate([-1, 0, 0])
              cube([2, outerDiameter + 2, mountHeight]);
        }
      }
    }
}
```


[User]
I need an OpenSCAD design for a key seal, like a small container that can be 3D printed. The idea is you pause the print midway, insert a key, and then continue printing to seal it in.

Here are the design parameters I want to be able to adjust:

*   **Bow Shape:** The "grip" part of the key can either be circular or rectangular. I need a variable for this, likely a 0/1 toggle or an enum.

*   **Circular Bow Diameter:** If the bow is circular, I need a parameter for its diameter.

*   **Rectangular Bow Width/Length:** If the bow is rectangular, I need parameters for its width and length.

*   **Key Thickness:**  The overall thickness of the key itself, essentially the height of the container.

*   **Blade Length:**  The length of the key's blade (the part that goes into the lock) from tip to where the bow begins.

*   **Blade Width:** The width of the key's blade.

*   **Shoulder Width:** The width of the "shoulder" that stops the key from going too far into the lock (the wider part at the base of the blade).

*   **Shoulder Length:**  The length of the shoulder from the bow.

*  **Ring Hole Diameter:** I want a hole for a keyring, controlled by this diameter.

*   **Ring Hole Land:** Distance from the edge of the bow to the ring hole.

*   **Ring Hole Offset:** A way to adjust the ring hole's position, moving it towards the blade or away, perpendicular to the main axis of the key.

*   **Preview/Render Toggle:** A way to switch between low-res preview and high-res render.

I also need some internal parameters for the 3D printing that I think should be defined within the scad file:

*   **Wall Width:** The thickness of the outer wall.

*   **Bottom Plate Height:** The height of the base of the container.

*  **Clearance:**  A bit of extra space around the key for fit.

*  **Layer Height:** The 3D printer's layer height

*  **Nozzle Width:** The printer's nozzle width.

*   **Strake Height:**  The height of internal "strakes" or stringers - these will essentially be the "grid" that I put the key on to. They should be two layers of material.

I think the overall design should consist of these parts:
*   A main container body that matches the key dimensions, plus space for walls.
*   A void inside the container for the key itself
*   Internal strakes that act as a grid or base.

I imagine I'll have to pause the print just before the "grid" strakes are printed, put in the key, and resume.
I'm not providing any libraries but I think it'll need trigonometric functions and likely a function that would let me generate the strakes or grid easily.

[Assistant]
key_seal.scad:
```
// -*- mode: SCAD ; c-file-style: "ellemtel" ; coding: utf-8 -*-
//
// Key seal.
//
// Copyright 2018 Roland Sieker <ospalh@gmail.com>
// Licence: CC-BY-SA 4.0

// The bow is the "grip" of the key
bow_shape = 0; // [0: circular, 1: rectangular]

// Diameter of the key's circular bow.
bow_diameter = 16.5;  // [5:0.1:40]

// Size of the rectangular bow normal to the insertion direction
bow_width = 19;  // [5:0.1:40]

// Size of the rectangular bow in insertion direction
bow_length = 12;  // [5:0.1:40]

// Thickness of the key.
thickness = 2;  // [0.5:0.1:5]

// The length of the bit you insert into the lock from tip to bow
blade_length = 18.5;  // [5:0.1:40]

// The width of the blade
blade_width = 5.3;  // [1:0.1:20]

// The shoulder is the bit that stops you from pushing in the key too far. This is the extra width of the protrusion.
shoulder_width = 2.5;  // [1:0.1:10]

// Distance from the bow to the shoulder.
shoulder_length = 6.5;  // [1:0.1:15]

// Key ring hole diameter
ring_hole_diameter = 4;  // [0.5:0.1:5]

// Distance of the ring hole from the edge of the key
ring_hole_land = 2.5;  // [0.5:0.1:5]

// How much the ring hole is offset towards the cuts and shoulder side. Manual correction of the land width needed for circular bows.
ring_hole_offset = 0;  // [-20:0.1:20]

// Set this to "render" and click on "Create Thing" when done with the setup.
preview = 1; // [0:render, 1:preview]

/* [Hidden] */

// Done with the customizer.

// *******************************************************
// Extra parameters. These can be changed reasonably safely.

w = 0.8;  // external wall width
p = 0.4;  // height of the bottomt plate
c = 0.4;  // clearance

lh = 0.2; // layer height
nw = 0.4;  // nozzle width

sh = lh * 2;  // We do two layers of strakes or stringers

// *******************************************************
// Some shortcuts. These shouldn't be changed

angle = 60; // Overhangs much below 60 degrees are a problem for me
xy_factor = 1/tan(angle);  // To get from a height to a horizontal width
                           // inclined correctly
z_factor = tan(angle);  // the other way around

rb = bow_shape ? bow_length/2 : bow_diameter / 2;
kw = bow_shape ? bow_width/2 : bow_diameter / 2;
h = thickness;

some_distance = 50;
ms = 0.01;  // Muggeseggele.

// fn for differently sized objects and fs, fa,  all for preview or rendering.
pna = 40;
pnb = 15;
pa = 5;
ps = 1;
rna = 180;
rnb = 30;
ra = 1;
rs = 0.1;
function na() = (preview) ? pna : rna;
function nb() = (preview) ? pnb : rnb;
$fs = (preview) ? ps : rs;
$fa = (preview) ? pa : ra;

// *******************************************************
// End setup

// *******************************************************
// Generate the part

key_seal();

// Test
// %solid_key_seal();
// %key_hollow();
// #strakes();

// *******************************************************
// Code for the parts themselves

module key_seal()
{
   difference()
   {
      solid_key_seal();
      key_hollow();
   }
   intersection()
   {
      solid_key_seal();
      strakes();
   }
}

module solid_key_seal()
{

   if (bow_shape)
   {
      translate([-bow_width/2-c-w, -bow_length/2-c-w, 0])
      {
         cube([bow_width+2*c+2*w, bow_length+2*c+2*w, h+2*p+c+sh]);
      }
   }
   else
   {
      cylinder(r=rb+w+c, h=h+2*p+c+sh);
   }
   translate([-blade_width/2-c-w, 0, 0])
      {
         cube([blade_width+2*c+2*w, blade_length+w+rb+w+c, h+2*p+c+sh]);
      }
   cube([blade_width/2+shoulder_width+c+w, rb+shoulder_length+c+w, h+2*p+c+sh]);
}

module key_hollow()
{
   translate([0,0,p])
   {
      if (bow_shape)
      {
         translate([-bow_width/2-c, -bow_length/2-c, 0])
         {
            cube([bow_width+2*c, bow_length+2*c, h+c+sh]);
         }
      }
      else
      {
         cylinder(r=rb+c, h=h+c+sh);
      }
      translate([-blade_width/2-c, 0, 0])
      {
         cube([blade_width+2*c, some_distance, h+c+sh]);
      }
      cube([blade_width/2+shoulder_width+c, rb+shoulder_length+c, h+c+sh]);
   }
   translate([ring_hole_offset, -rb + ring_hole_land + ring_hole_diameter/2,-ms])
   {
      cylinder(d=ring_hole_diameter, h=h+2*p+c+sh+2*ms);
   }
}

module strakes()
{

   translate([-rb-c-w,-kw-c-w,h+p+c])
   {
      for (so=[0:5*nw: blade_length+w+2*rb+2*w+2*c])
      {
         translate([0,so,0])
         {
            cube([2*rb+2*c+2*w, nw, lh]);
         }
      }

      translate([0,0, lh])
      {
         for (st=[0:5*nw: 2*rb+2*w+2*c])
         {
            translate([st, 0, 0])
            {
               cube([nw, blade_length+w+2*rb+2*w+2*c, lh]);
            }
         }
      }
   }
}

```


[User]
Okay, I'm looking to design a customizable toy disk shooter, and I'm going to need a few different OpenSCAD files to do it. I want the main design to be a slide that you pull back to launch disks, and I need to make it parametric so users can customize a few key aspects.

Specifically, I need a file, which I'm calling `customizable_slide.scad`, that creates the slide itself. This slide needs a finger ring on one end for pulling, and a cutout in the middle and at the other end. The finger ring needs to be sized based on the user's finger, and there should be user-configurable options for the quality of the curves on the finger ring and cutouts. Think of quality as how many sides a circle will have. There should also be stoppers on either side of the end cutout to stop the slide from moving further than it should.

The `customizable_slide.scad` file should have the following parameters:

*   `finger_size`: (float) The diameter of the user's finger in millimeters, which will determine the size of the finger ring.
*   `finger_ring_face_count`: (integer) The number of faces used to generate the circular part of the finger ring. A higher number is a smoother circle, but a more complex model.
*   `center_cut_out_quality`: (integer) The number of faces for the cylinder cut out in the center of the slide.
*   `end_cut_out_quality`: (integer) The number of faces for the cylinder cutout at the end of the slide.

The slide has a few specific features to note.  There's the finger ring as described above, a main body with a roughly rectangular cut-out at each end, a cylinder cutout in the center, and small rectangular stoppers on both sides of the end cutout. It should have top stabilizers, and a connection point to the main body. The connection point has an area that connects directly to the side and a set of triangular arms to increase the connecting area. There also needs to be a rubber band retaining feature on the bottom, which includes a shallow ring for the rubber band and two triangle ears at each side of the ring.

I want this file to be a complete module, so it will include all of the necessary shapes to build this slide.  I'd like the rendered object to be blue, too; that's why it has `color("cyan")` at the bottom of the code.

I'd like a separate file, `customizable_disk.scad`, for generating the disks that are launched, but I'll leave the details of that file for a later time. I'm hoping to eventually add more files to this project for things like the main handle, the ammo holder, and maybe even a mechanism to cock the shooter without firing. I might need help with that as well, so I'll let you know when I have more details on those files, but the key thing right now is getting the slide working.

[Assistant]
customizable_slide_V1.5.scad:
```
/*			CHANGELOG

	V1.1 fixes the stopers at the end, and makes it better for customizer on thingiverse.

	V1,5 has some tweaks for the size so you don't have to scale it, and i added in a color command because i NEED all my designs to be blue.

			INTRODUCTION
This is my first scad file, but i think i did a decent job, although i still have alot to learn. Please excuse all, if any, typoes, and feel free to share with me all the ways i messed this up, if any. and if you have any problems, please leave a comment with what scad editor your using, and what exactly went wrong (screen shots are helpfull too) and i'll do my best to help you... or look up stuff on google till i can:)

Among other things, i plan on updating this file with options to customize the grip, the disks (ammo) , and maybe the ammo holder. i don't think there is anything to do with the barrel.

			INSTRUCTIONS
This should be fairly straight foward for those of you who are familiar with these types of files. All instructions given are with openscad's default editor in mind. when i say left and right, thats with the finger ring as the closest part, and your looking down the lentgh of it. If you still not sure of orientation, just play with a variable to see which piece moves or changes.

For those who aren't familiar, while i do encourage you to read through my code and try to understand what i did, that's not actually needed to tweak this file. All you need to do is measure the diameter of your finger, and input the measurements in milimeters, and end the line with a semi colon ( ; ). You can round a bit, so 25 mm can be called a inch, if you only have imperial measuring devices.

You can also adjust the quality of the finger ring and cut outs. the latter may be slightly pointless, but why not?

After you change the values and end the line with a semicolon ( ; ), you can press F5 to to preview it, this is quick. when your ready to save it as a stl file, press F6 to render the file, this can take a while depending on what you told it for the various quality settings, and than save as stl. Easy peasy.

Please note that i'm not responsible if your computer explodes, or becomes a giant fire breathing duck that yodels.
*/

/*           CUSTOMIZABLES               */

finger_size = 25;

finger_ring_face_count = 50;

center_cut_out_quality = 50;

end_cut_out_quality = 50;

/*						CODE					 feel free to play with and modify it
*/

/* i redifned these to make it easier for me*/
r = finger_size;
frq = finger_ring_face_count;
ccq = center_cut_out_quality;
ecq = end_cut_out_quality;

module slide() {
	union() {

//finger ring
		translate([116/2+r/2,0,2.5]) {
			difference() {
				cylinder(r=r/2+2, h = 8, center = true,$fn = frq);
				cylinder(r=r/2, h = 9, center = true,$fn = frq);
			}
		}
//circular cutout at the end of the slide and in the center of the slide, and the stoppers.

//this is the circular cut out at the end
		difference() {
			translate([-5,0,0]) {
			cube([92,28,3], center = true);
			}
			translate([-56.6,0,0]) {
				cylinder(r=33.5/2, h=4, center = true, $fn = ecq);
			}
//this is the oval cut out in the center.
			translate([0,0,0]) {
				scale([5,1,1]) cylinder (r = 7, h = 5, center = true, $fn = ccq);
			}
		}

//these are the stoppers at the end on either side of the cutout

		//right side
		scale([1,0.8,1]) {
			rotate([0,0,-90]) {
				translate([-17.5,-42.944,-1.5]) {
					difference() {
						linear_extrude(3) {
							hull() {
						translate([0,3.5,0]) square (9, center = true);
				circle (r=4.5, $fn = 4 );
								}
							}
							translate([5,2,0]) cube([10,20,10],center = true);
						}
					}
				}
			}

		//left side
		scale([1,0.8,1]) {
			rotate([0,180,-90]) {
				translate([-17.5,-42.9,-1.5]) {
					difference() {
						linear_extrude(3) {
							hull() {
								translate([0,3.5,0]) square (9, center = true);
				circle (r=4.5, $fn = 4 );
								}
							}
							translate([5,2,0]) cube([10,20,10],center = true);
						}
					}
				}
			}

//stablizers for the top sides

		//right side
		translate([2,12,3]) {
			cube([74,2,4], center = true);
		}
		translate([-43,13,1]) {
			rotate ([90,0,0]) {
				scale ([4,2,1]) {
					difference() {
						cube (2);
						rotate([0,0,45]) cube(10);
					}
				}
			}
		}

		//left side
		translate([2,-12,3]) {
			cube([74,2,4], center = true);
		}
		translate([-43,-11,1]) {
			rotate ([90,0,0]) {
				scale ([4,2,1]) {
					difference() {
						cube (2);
						rotate([0,0,45]) cube(10);
					}
				}
			}
		}

//conection for the finger ring
		translate([51,0,0]) {
			cube([14,13,3], center = true);
		}
		translate([53.5,0,4]) {
			cube([9,13,5], center = true);
		}
		translate([48,10,0]) {
			rotate([0,0,-45]) cube([12,2,3], center = true);
		}
			translate([48,-10,0]) {
				rotate([0,0,45]) cube([12,2,3], center = true);
		}
			translate([47.5,11.915,1.45]) {
				rotate ([90,0,-45]) {
					scale ([4,2.4,1]) {
					difference() {
						cube (2);
						rotate([0,0,45]) cube(10);
					}
				}
			}
		}
		translate([46.2,-10.385,1.45]) {
			rotate ([90,0,45]) {
				scale ([4,2.4,1]) {
					difference() {
						cube (2);
						rotate([0,0,45]) cube(10);
					}
				}
			}
		}

//rubberband retainer, has a large ring for the indent the rubber band goes into, and little triangle ears.
		difference() {
		translate([41,0,2.75]) {
			cube([8.5,34,8.5], center = true);
			}
			//this is the ring indent
			translate([-21,0,3.51]) {
				difference() {
					cylinder(r=70, h = 4, $fn = 90, center = true);
					cylinder(r= 65.5, h = 5, $fn = 250, center = true);
				}
			}
		}
		//left triangle stop ear
		translate([36.75,20,7]) {
				rotate ([-90,0,-90]) {
					scale ([1,1.5,1]) {
					difference() {
						cube (3);
						rotate([0,0,45]) cube(10);
					}
				}
			}
		}
		//right triangle stop ear
		translate([39.75,-20,7]) {
			rotate ([-90,0,90]) {
				scale ([1,1.5,1]) {
					difference() {
						cube (3);
						rotate([0,0,45]) cube(10);
					}
				}
			}
		}
	}
}

color("cyan") {
	slide();
}

//still no firebreathing duck? i must've done somthing wrong...
```


[User]
I'd like a parametric OpenSCAD design for a cable organizer that can be screwed into a wall or other surface. It should have three different shape options: round, oval, and rectangular.

For all shapes, I'd like to be able to specify the main structure's thickness and height, plus the length of the "foot" that extends from the main structure and contains the screw holes. The screw holes themselves should also be parameterized with radius, recess radius, and recess depth.

For the round and oval shapes, I need to specify a radius, which will be the base for those shapes (modified for the oval shape). The rectangular shape should have separate parameters for its two side lengths.

I also would like to ask if you could use a good library for this, like BOSL2.

[Assistant]
Gancio_cavi_viti.scad:
```
$fn=72;

//0= round     1=Oval      2=rectangular
Type=0;
//Structure measures
Foot_length=15;
Structure_thickness=6;
Structure_height=10;
//Rounded structure measures
Radius=40;
//Rectangular structure measures
Side_1=20;
Side_2=75;
//Screws measures
Screw_hole_radius=1.75;
Screw_hole_recess_radius=3.2;
Screw_hole_recess_depth=3;

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

if(Type==0)
difference()
    {
    union()
        {
        translate([Radius/1.25,0,0])
            cylinder(r=Radius+Structure_thickness,h=Structure_height);
        translate([0,-Radius-Foot_length,0])
            cube([Structure_thickness,Radius*2+Foot_length*2,Structure_height]);
        }

        translate([Radius/1.25,0,-1])
            cylinder(r=Radius,h=Structure_height+2);
        translate([-Radius*4,-Radius*2,-Structure_height/2])
            cube([Radius*4,Radius*4,Structure_height*2]);

//Screw holes
        translate([-2,Radius+Foot_length-Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_radius,h=Structure_height*2);
        translate([-2,-Radius-Foot_length+Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_radius,h=Structure_height*2);
//Screw holes recesses
        translate([Structure_thickness-Screw_hole_recess_depth,Radius+Foot_length-Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_recess_radius,h=Structure_height);
        translate([Structure_thickness-Screw_hole_recess_depth,-Radius-Foot_length+Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_recess_radius,h=Structure_height);

    }

if(Type==1)
difference()
    {
    union()
        {
        scale([0.5,1,1])
        translate([Radius/1.25,0,0])
            cylinder(r=Radius+Structure_thickness,h=Structure_height);
        translate([0,-Radius-Foot_length,0])
            cube([Structure_thickness,Radius*2+Foot_length*2,Structure_height]);
        }

        scale([0.5,1,1])
        translate([Radius/1.25,0,-1])
            cylinder(r=Radius,h=Structure_height+2);
        translate([-Radius*4,-Radius-Foot_length*2,-Structure_height/2])
            cube([Radius*4,Radius*2+Foot_length*4,Structure_height*2]);

//Screw holes
        translate([-2,Radius+Foot_length-Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_radius,h=Structure_height*2);
        translate([-2,-Radius-Foot_length+Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_radius,h=Structure_height*2);
//Screw holes recesses
        translate([Structure_thickness-Screw_hole_recess_depth,Radius+Foot_length-Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_recess_radius,h=Structure_height);
        translate([Structure_thickness-Screw_hole_recess_depth,-Radius-Foot_length+Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_recess_radius,h=Structure_height);

    }

if(Type==2)
difference()
    {
    union()
        {
        translate([0,-Side_2/2-Structure_thickness,0])
            cube([Side_1+Structure_thickness,Side_2+Structure_thickness*2,Structure_height]);
        translate([0,-Side_2/2-Structure_thickness-Foot_length,0])
            cube([Structure_thickness,Side_2+Structure_thickness*2+Foot_length*2,Structure_height]);
        }

        translate([-0.1,-Side_2/2,-1])
            cube([Side_1,Side_2,Structure_height+2]);

//Screw holes
        translate([-2,Side_2/2+Structure_thickness+Foot_length-Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_radius,h=Structure_height*2);
        translate([-2,-Side_2/2-Structure_thickness-Foot_length+Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_radius,h=Structure_height*2);
//Screw holes recesses
        translate([Structure_thickness-Screw_hole_recess_depth,Side_2/2+Structure_thickness+Foot_length-Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_recess_radius,h=Structure_height);
        translate([Structure_thickness-Screw_hole_recess_depth,-Side_2/2-Structure_thickness-Foot_length+Screw_hole_recess_radius*1.5,Structure_height/2])
            rotate([0,90,0])
            cylinder(r=Screw_hole_recess_radius,h=Structure_height);

    }
```


[User]
I'm looking to create a parametric design for a small, rectangular sorting box using OpenSCAD. I'd like the box to be composed of a base, and four side pieces that are meant to be glued together. It should have a sort of hexagonal pattern cut into the walls, not all the way through, just creating indentations. I'm thinking a modular design would be best, where individual parts can be generated as well as the assembled box.

Here are the key parameters I have in mind:

*   **`part`**: An integer parameter to select which part to render. `0` would be the assembled box, `1` the base, `2` would be the north side, `3` the south side, `4` the east side, and `5` the west side.
*   **`xi`**: The inner width of the box (x-dimension). Should have a range of 20 to 200mm.
*   **`yi`**: The inner length of the box (y-dimension). Also with a range of 20 to 200mm.
*   **`zi`**: The inner height of the box (z-dimension). With a range of 20 to 150mm.
*   **`wt`**: The wall thickness of the box. This should range from 0 to 4mm, but should allow for increments of 0.05mm.
*   **`rs`**: The radius of the hexagonal indentations. Range from 1 to 40mm with an increment of 0.1mm
*   **`bl`**: Backlash or a little extra space to take into account tolerances for printing and fit. A range of 0 to 1mm with increments of 0.05mm

The hexagonal pattern should have a wall thickness as well and that should be half of `wt`. I think the Hexagonal pattern function should have a parameter to invert it if I would like to use it in other projects. The walls should be flat, except for the hexagonal pattern.

Also, it would be nice if the top edges of the side walls had some small tabs to aid in the glueing. They should only protrude a little bit.

I don't need any libraries for this project. Can you generate the OpenSCAD files for this?

[Assistant]
Tueten-Lager_V2.scad:
```
// -----------------------------------------------------
// Tueten-Lager V2
//
//  Detlev Ahlgrimm, 10.2018
// -----------------------------------------------------

/* [main parameters] */

part=0;       // [0:assembled, 1:floor, 2:side north, 3:side south, 4:side east, 5:side west]

// inner width
xi=170;      // [20:200]

// inner length
yi=65;     // [20:200]

// inner height
zi=40;      // [20:150]

// wall thinckess
wt=2.0;     // [0:0.05:4]

// radius Hex
rs=11;      // [1:0.1:40]

// backlash
bl=0.3;     //  [0:0.05:1]

/* [Hidden] */
$fn=100;

// -----------------------------------------------------
//    x : Breite
//    y : Laenge
//    z : Hoehe
//    r : Radius der Aussparungen
//    w : Wandstaerke zwischen den Aussparungen
//    i : invertieren (true:Sechseck-Umrandungen, false:Sechsecke)
//    a : anschraegen (bei true)
// -----------------------------------------------------
module HexPattern(x, y, z, r=3, w=1, i=true, a=false) {
  h=r-(r/2*1.732);  // 1.732=sqrt(3)
  dx=3*r+2*w;
  dy=(r-h)+w;
  r1=(a) ? r-(z+0.2) : r;
  intersection() {
    difference() {
      if(i) cube([x, y, z]);
      for(yi=[0:1:y/dy+1]) {
        for(xi=[0:1:x/dx+1]) {
          if((yi%2)==1) {
            translate([xi*dx, yi*dy, -0.1]) cylinder(r1=r1, r2=r, h=z+0.2, $fn=6);
          } else {
            translate([xi*dx+dx/2, yi*dy, -0.1]) cylinder(r1=r1, r2=r, h=z+0.2, $fn=6);
          }
        }
      }
    }
    if(!i) cube([x, y, z]);
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module Wand(v, wt, rs=10) {
  difference() {
    cube([v.x+2*wt, v.y+2*wt, v.z]);
    translate([wt, wt, -0.1]) cube([v.x, v.y, v.z+0.2]);
  }
  translate([wt, wt, 0]) HexPattern(v.x, v.y, v.z, rs, wt/2);
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module Boden() {
  difference() {
    union() {
      cube([xi+4*wt+2*bl, yi+4*wt+2*bl, wt]);
      translate([wt+bl, wt+bl, 0]) cube([xi+2*wt, yi+2*wt, 2*wt]);
    }
    translate([2*wt+bl, 2*wt+bl, -0.1]) cube([xi, yi, 2*wt+0.2]);
  }
  Wand([xi+2*wt+2*bl, yi+2*wt+2*bl, wt], wt, rs);
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module SeiteX() {
  translate([wt+bl, wt, wt+bl]) rotate([90, 0, 0]) Wand([xi, zi, wt], wt, rs);
  translate([xi+3*wt+bl, wt, wt+bl+zi+wt]) linear_extrude(wt) polygon([[0,0], [0,2], [-0.5,2], [-2.5,0]]);
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module SeiteY() {
  translate([wt, 0, wt+bl]) rotate([0, -90, 0]) Wand([zi, yi+2*wt+2*bl, wt], wt, rs);
  translate([wt, wt+bl, wt+bl+zi+wt]) linear_extrude(wt) polygon([[0,0], [2,0], [2,0.5], [0,2.5]]);
}

if(part==0) {
  Boden();
  SeiteX();
  translate([xi+4*wt+2*bl, yi+4*wt+2*bl, 0]) mirror([1, 0, 0]) mirror([0, 1, 0]) SeiteX();
  SeiteY();
  translate([xi+4*wt+2*bl, yi+4*wt+2*bl, 0]) mirror([1, 0, 0]) mirror([0, 1, 0]) SeiteY();
} else if(part==1) {
  Boden();
} else if(part==2) {
  rotate([-90, 0, 0]) mirror([1, 0, 0]) mirror([0, 1, 0]) SeiteX();
} else if(part==3) {
  rotate([90, 0, 0]) SeiteX();
} else if(part==4) {
  rotate([0, -90, 0]) SeiteY();
} else if(part==5) {
  rotate([0, 90, 0]) mirror([1, 0, 0]) mirror([0, 1, 0]) SeiteY();
}

```


[User]
Hey, I'm looking to create a customizable can koozie and I need some help generating the OpenSCAD code. I'd like it to be parametric so I can easily adjust the dimensions for different can sizes. Here's what I'm envisioning:

The koozie should consist of two main parts: a bottom cup-like section that holds the base of the can and a top lid-like section that fits over the top of the can. The two sections should connect in a way that allows them to be removable from each other.

Here's the breakdown of the important dimensions:

*   `h`: This is the total height of the can that the koozie is designed to fit, and the default should be around 122.682 mm.
*   `d`: This is the uniform diameter of the can, a default of around 66.04 mm.
*   `dt`: This is the diameter of the top lip of the can, defaulting to approximately 54.102 mm.
*   `hl`: The height of the lip on the can that the top of the koozie sits on, around 10 mm.
*   `hu`: The height of the upper lid section of the koozie, approximately 45 mm.
*   `hc`: This is a calculated value that is the height of the koozie's bottom cup. It should be `h` minus `hl` minus `hu`.
*   `$fn`: Use a resolution of 500 for smooth curves.

The bottom cup section should be a cylinder with a slightly larger diameter than `d`, to allow for the can to fit inside. The top lid should also be a cylinder with a slightly larger diameter than `dt` at the base (where it sits on the lip) and then should transition to be slightly larger than `d` at the top. The top lid should also have a lip that fits inside the bottom cup.

The connection between the two sections should be a joint that will allow the two halves to slide into place when put together. I'm picturing something like a cylinder that extends from each piece that fit together.

Please generate the necessary OpenSCAD code for this, making sure all dimensions are easily customizable through variables. I think this is doable using only standard OpenSCAD functions, so no need to use any specific libraries.

[Assistant]
customizablekoozie.scad:
```
//actual can hegiht
h=122.682;

//uniform diameter of can
d=66.04;

//diameter of top of can
dt=54.102;

//height of lip
hl=10;

//upper lid height
hu=45;

//height of cup
hc=h-hl-hu;

//resolution
$fn=500;

module can() {
    difference () {
    cylinder (hc,d+3,d+3, false);
    translate([0,0,3])
    cylinder(hc,d,d, false);
    }
}

module cap() {
    difference(){
    translate ([0,0,h-hl])
    cylinder(hl,d+3,dt+3,false);
    translate([0,0,h-hl-.1])
    cylinder(hl+.2,d,dt,false);
    }
    difference(){
    translate([0,0,hc+hl])
    cylinder(hu-hl,d+3,d+3,false);
    translate([0,0,hc+hl-.1])
    cylinder(hu-hl+.5,d,d,false);
    }
}

module lower_joint() {
    difference(){
    translate([0,0,hc])
    cylinder(15,5+d,5+d,false);
    translate([0,0,hc-.5])
    cylinder(15+1,d,d,false);
    translate([0,0,hc+10])
    cylinder(4+1.01,2.5+d,2.5+d,false);
}
}

module upper_joint() {
    difference(){
    translate([0,0,hc+15/2])
    cylinder(15/2,5+d,5+d,false);
    translate([0,0,hc+15/2-.5])
    cylinder(15/2+1,d,d,false);
    }
}

module connector() {
    union(){
    upper_joint();
    difference(){
    translate([0,0,hc+15/2-4])
    cylinder(4,2.5+d,2.5+d,false);
    translate([0,0,hc+15/2-4.5])
    cylinder(4+1,d,d,false);
    }
    }
}
module bottom() {
    union(){
        can();
        lower_joint();
    }
}
module top(){
    rotate([180,0,0])
    translate([175,0,-h])
    union(){
        cap();
        upper_joint();
        connector();
    }
}
bottom();
top();
```


[User]
I'd like to design a custom coaster, and I'm hoping you can help me create the OpenSCAD code. The basic shape should be a cylinder. I want to put a cutout of the Upper Peninsula of Michigan on it, as well as some customizable text.

Specifically, the coaster should start as a cylinder with a height of 3mm and a radius of 30mm. Then, I'd like to cut out the shape of the Upper Peninsula from the top. I have the coordinates for the outline in a polygon, and the polygon should be extruded to a height of 5mm, and positioned correctly. Finally, I want to add text to the center of the coaster that I can customize. This text should be extruded to a height of 10mm. The text should start with some negative x-translation to move it to the left, and the text itself can have a size parameter and the ability to change the text.

It would be great if the text used the "Liberation Sans:style=Bold Italic" font. The final design should have a rotated text element.

Could you generate the OpenSCAD file for me?

[Assistant]
Custom_Coaster.scad:
```
//Enter text
texty="The Kaspriak's"; //

//Enter text size
sizey=5; //

//Enter Left/Right Adjustment
movey=-24;

difference(){

cylinder(h=3,r=30,center=false);

scale([0.1,0.1])
translate([-160,-75,-1])
linear_extrude(height = 5, center = false, convexity = 15)
polygon(points=[[0,58],[85,25],[133,0],[142,9],[109,29],[101,47],[117,34],[146,45],[149,50],[193,63],[215,48],[250,47],[274,38],[283,57],[321,52],[336,82],[294,95],[254,82],[242,92],[222,91],[201,114],[176,105],[146,149],[115,101],[14,73]],  paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]],convexity=10);
rotate([0,0,180])
translate([movey,-15,-5])
linear_extrude(height=10){
text(texty, size=sizey,font = "Liberation Sans:style=Bold Italic");}}
```


[User]
I need a customizable sorting box, something like a small parts organizer, and I've got a pretty clear idea of what I want. I've even got a design I like, but I need something more flexible.

The box should have adjustable internal dimensions - width, length, and height, so I can get the exact size I need. Let's call those `xi`, `yi`, and `zi` respectively. I'll also need to control the radius of the bracing `rs`, and the wall thickness `wt`.  For the wall profile `wp`, I want options for "none" (just solid walls), "tubes" (with diagonal bracing tubes) or "hex" (with a hexagonal bracing pattern).

The box should have dividers within the box, parallel to the x-axis, that can be positioned at configurable y-coordinates. Let's call those `y1`, `y2`, `y3`, `y4`, and `y5`.  I need to be able to choose whether or not to include a divider at each position.

For the floor of the box, I need to be able to choose between "none," "lines," "grid," and "hex" patterns using `typ`. For solid floors, we'll use `fhs` for the floor height. If the floor isn't solid (lines, grid, hex), then I need control over `fh` for the line/hex height, `fw` for the line/hex width, `fcx` for the number of lines (or hex rows) along the x-axis, and `fcy` for the number of grid lines along the y-axis.

Finally, I need to be able to add a label holder to the front of the box. I should be able to control whether the label holder is included or not with `ilh`, the width of the label holder with `lx`, and the height of the label holder with `lz`.

I think that covers the basics. Oh - and it'd be nice if the main box used rounded corners where the top edges meet. Let's make sure it is all parametric, so it can be adjusted easily. I don't want to have to do any coding, though. I want to be able to adjust these settings via a customizer/parameter interface.

I don't need you to use any special libraries, just a basic OpenSCAD file. Could you write that for me?

[Assistant]
Tueten-Lager_V3.scad:
```
// -----------------------------------------------------
// Tueten-Lager V3
//
//  Detlev Ahlgrimm, 10.2018
// -----------------------------------------------------

/* [Main Parameters] */

// inner width
xi=65;      // [20:100]

// inner length
yi=150;     // [20:200]

// inner height
zi=40;      // [20:150]

// bracing radius
rs=2.0;     // [1:0.1:5]

// wall thinckess
wt=0.90;    // [0:0.05:4]

// wall profile
wp=2;       // [0:none, 1:tubes, 2:hex]

/* [Divider Position] */
y1=50;      // [0:200]
y2=0;       // [0:200]
y3=0;       // [0:200]
y4=0;       // [0:200]
y5=0;       // [0:200]

/* [Floor] */
// type
typ=3;      // [0:none, 1:lines, 2:grid, 3:hex]

// floor height (solid)
fhs=0.25;   // [0:0.05:5]

// floor height (lines, grid and hex)
fh=1.0;     // [0.5:0.5:5]

// floor line width (lines, grid and hex)
fw=3;       // [0.5:0.1:5]

// floor line count X (lines, grid and hex)
fcx=3;      // [1:20]

// floor line count Y (grid only)
fcy=8;      // [1:20]

/* [Label Holder] */
// include label holder
ilh=0;      // [0:false, 1:true]

// label holder width
lx=65;      // [20:100]

// label holder height
lz=40;      // [20:150]

/* [Hidden] */
$fn=100;
fh2=(typ>0) ? max(fh, fhs) : fhs;   // die Hoehe des Bodens
//echo("fh2=", fh2);

// -----------------------------------------------------
//  v   : Vektor mit den inneren Abmessungen
//  r   : Radius der Rundungen
//  fh  : Hoehe des Bodens
//  wt  : Wandstaerke
//
//  bei wp==0 gibts keine oberen Rundungen
//  bei r<=wt gibt es keinerlei Rundungen
//
//  "wp", "fh2", "fcx" und "fw" kommen von "ausserhalb"
// -----------------------------------------------------
module base(v, r, fh, wt) {
  rh=(wp==0 || r<=wt) ? 0 : r;    // bei (wp==0 || r<=wt) gibts keine obere Rundung -> rh=0
  //echo("rh=", rh);
  d=2*r;
  difference() {
    union() {
      translate([-wt, -wt, 0]) cube([v.x+2*wt, v.y+2*wt, v.z+fh-rh]);

      if(wp>0 && r>wt) {  // bedingte Ausfuehrung bringt ggf. mehr Render-Geschwindigkeit
        translate([  0,   0, v.z+fh-rh]) sphere(r=r);  // die oberen Ecken
        translate([v.x,   0, v.z+fh-rh]) sphere(r=r);
        translate([  0, v.y, v.z+fh-rh]) sphere(r=r);
        translate([v.x, v.y, v.z+fh-rh]) sphere(r=r);
      }

      if(r>wt) {
        translate([  0,   0, 0]) cylinder(r=r, h=v.z+fh-rh);  // die senkrechten Eck-Rundungen
        translate([v.x,   0, 0]) cylinder(r=r, h=v.z+fh-rh);
        translate([  0, v.y, 0]) cylinder(r=r, h=v.z+fh-rh);
        translate([v.x, v.y, 0]) cylinder(r=r, h=v.z+fh-rh);

        translate([-0.1,    0, 0]) rotate([  0, 90, 0]) cylinder(r=r, h=v.x+0.2);  // die unteren waagerechten Eck-Rundungen
        translate([-0.1,  v.y, 0]) rotate([  0, 90, 0]) cylinder(r=r, h=v.x+0.2);
        translate([   0, -0.1, 0]) rotate([-90,  0, 0]) cylinder(r=r, h=v.y+0.2);
        translate([ v.x, -0.1, 0]) rotate([-90,  0, 0]) cylinder(r=r, h=v.y+0.2);
      }

      if(wp>0 && r>wt) {
        translate([-0.1,    0, v.z+fh-rh]) rotate([  0, 90, 0]) cylinder(r=r, h=v.x+0.2); // die oberen waagerechten Eck-Rundungen
        translate([-0.1,  v.y, v.z+fh-rh]) rotate([  0, 90, 0]) cylinder(r=r, h=v.x+0.2);
        translate([   0, -0.1, v.z+fh-rh]) rotate([-90,  0, 0]) cylinder(r=r, h=v.y+0.2);
        translate([ v.x, -0.1, v.z+fh-rh]) rotate([-90,  0, 0]) cylinder(r=r, h=v.y+0.2);
      }

      if(wp==1) {
        kx=sqrt(v.x*v.x + (v.z+fh-r)*(v.z+fh-r));
        wx=atan(v.x/(v.z+fh-r));
        translate([  0,   0, 0]) rotate([0,  wx, 0]) cylinder(r=r, h=kx);   // Querstreben vorne/hinten
        translate([v.x,   0, 0]) rotate([0, -wx, 0]) cylinder(r=r, h=kx);
        translate([  0, v.y, 0]) rotate([0,  wx, 0]) cylinder(r=r, h=kx);
        translate([v.x, v.y, 0]) rotate([0, -wx, 0]) cylinder(r=r, h=kx);
        ky=sqrt(v.y*v.y + (v.z+fh-r)*(v.z+fh-r));
        wy=atan(v.y/(v.z+fh-r));
        translate([  0,   0, 0]) rotate([-wy, 0, 0]) cylinder(r=r, h=ky);   // Querstreben links/rechts
        translate([  0, v.y, 0]) rotate([ wy, 0, 0]) cylinder(r=r, h=ky);
        translate([v.x,   0, 0]) rotate([-wy, 0, 0]) cylinder(r=r, h=ky);
        translate([v.x, v.y, 0]) rotate([ wy, 0, 0]) cylinder(r=r, h=ky);
      } else if(wp==2) {
        translate([  0,   0,         0]) rotate([  0, -90, 0]) HexPattern(v.z+fh2-r,       v.y, r, v.x/(fcx*2), fw/2, true, true);   // Hex-Pattern rundrum
        translate([v.x,   0, v.z+fh2-r]) rotate([  0,  90, 0]) HexPattern(v.z+fh2-r,       v.y, r, v.x/(fcx*2), fw/2, true, true);
        translate([  0,   0,         0]) rotate([ 90,   0, 0]) HexPattern(      v.x, v.z+fh2-r, r, v.x/(fcx*2), fw/2, true, true);
        translate([  0, v.y, v.z+fh2-r]) rotate([-90,   0, 0]) HexPattern(      v.x, v.z+fh2-r, r, v.x/(fcx*2), fw/2, true, true);
      }
    }
    translate([0, 0, -0.1]) cube([v.x, v.y, v.z+fh+0.2]);   // Kiste aushoehlen
    translate([-d, -d, -d]) cube([v.x+2*d, v.y+2*d, d]);    // Boden plan machen
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module AllTogether() {
  base([xi, yi, zi], rs, fh2, wt);

  // den Boden zufuegen
  if(typ==0) {                    // kein Boden bzw. keine Bodenverstaerkung
    // nix
  } else if(typ==1) {             // Linien
    for(x=[1:fcx]) {
      translate([xi/(fcx+1)*x-fw/2, 0, 0]) cube([fw, yi, fh]);
    }
  } else if(typ==2) {           // Raster
    for(x=[1:fcx]) {
      translate([xi/(fcx+1)*x-fw/2, 0, 0]) cube([fw, yi, fh]);
    }
    for(y=[1:fcy]) {
      translate([0, yi/(fcy+1)*y-fw/2, 0]) cube([xi, fw, fh]);
    }
  } else if(typ==3) {           // Hex-Raster
    HexPattern(xi, yi, fh, xi/(fcx*2), fw/2);
  }

  if(fhs>0) {                   // massiver Boden (ggf. zusaetzlich zur Boden-Verstrebung)
    cube([xi, yi, fhs]);
  }

  // die Trennelemente
  kx=sqrt(xi*xi + (zi+fh2-rs)*(zi+fh2-rs));
  wx=atan(xi/(zi+fh2-rs));
  difference() {
    union() {
      for(y=[y1, y2, y3, y4, y5]) {   // Trennelemente
        if(y>0 && y<yi) {
          if(wp>0 && rs>wt) {
            translate([ 0, y-wt/2,         0]) cube([xi, wt, zi+fh2-rs]);
            translate([ 0,      y,         0]) rotate([0,  90, 0]) cylinder(r=rs, h=xi);
            translate([ 0,      y, zi+fh2-rs]) rotate([0,  90, 0]) cylinder(r=rs, h=xi);
          } else {
            translate([ 0, y-wt/2,         0]) cube([xi, wt, zi+fh2]);
          }
          if(wp==1) {
            translate([ 0, y, 0]) rotate([0,  wx, 0]) cylinder(r=rs, h=kx);
            translate([xi, y, 0]) rotate([0, -wx, 0]) cylinder(r=rs, h=kx);
          } else if(wp==2) {
            translate([0, y+wt/2, rs]) rotate([90, 0, 0]) HexPattern(xi, zi+fh2-2*rs, rs, xi/(fcx*2), fw, true, true);
          }
        }
      }
    }
    if(valid_y()) {   // nur ausfuehren, wenn mindestens eine Trennwand eingebaut wurde
      translate([-2*rs, -2*rs, -2*rs]) cube([xi+4*rs, yi+4*rs, 2*rs]);    // Boden plan machen
    }
  }
  if(ilh==1) {
    translate([xi/2-lx/2, -rs-4, 0]) BeschriftungsHalter([lx, rs+4, lz]);
  }
}
//rotate([0, 0, 90])
AllTogether();

// -----------------------------------------------------
//    x : Breite
//    y : Laenge
//    z : Hoehe
//    r : Radius der Aussparungen
//    w : Wandstaerke zwischen den Aussparungen
//    i : invertieren (true:Sechseck-Umrandungen, false:Sechsecke)
//    a : anschraegen (bei true)
// -----------------------------------------------------
module HexPattern(x, y, z, r=3, w=1, i=true, a=false) {
  h=r-(r/2*1.732);  // 1.732=sqrt(3)
  dx=3*r+2*w;
  dy=(r-h)+w;
  r1=(a) ? r-(z+0.2) : r;
  intersection() {
    difference() {
      if(i) cube([x, y, z]);
      for(yi=[0:1:y/dy+1]) {
        for(xi=[0:1:x/dx+1]) {
          if((yi%2)==1) {
            translate([xi*dx, yi*dy, -0.1]) cylinder(r1=r1, r2=r, h=z+0.2, $fn=6);
          } else {
            translate([xi*dx+dx/2, yi*dy, -0.1]) cylinder(r1=r1, r2=r, h=z+0.2, $fn=6);
          }
        }
      }
    }
    if(!i) cube([x, y, z]);
  }
}

// -----------------------------------------------------
//  wie cube() - nur abgerundet
// -----------------------------------------------------
module BoxMitAbgerundetenEcken(x, y, z, r=1) {
  hull() {
    translate([  r,   r,   r]) sphere(r=r);
    translate([x-r,   r,   r]) sphere(r=r);
    translate([  r, y-r,   r]) sphere(r=r);
    translate([x-r, y-r,   r]) sphere(r=r);

    translate([  r,   r, z-r]) sphere(r=r);
    translate([x-r,   r, z-r]) sphere(r=r);
    translate([  r, y-r, z-r]) sphere(r=r);
    translate([x-r, y-r, z-r]) sphere(r=r);
  }
}

// -----------------------------------------------------
//
// -----------------------------------------------------
module BeschriftungsHalter(v, sr=5, wt=1) {
  difference() {
    BoxMitAbgerundetenEcken(v.x, v.y, v.z);
    translate([sr, -0.1, sr]) cube([v.x-2*sr, v.y+0.2, v.z]);
    translate([wt, wt, wt]) cube([v.x-2*wt, v.y, v.z]);
  }
}

// -----------------------------------------------------
// Liefert einen Vektor mit den Werten von [y1 - y5],
// die groesser 0 und kleiner yi sind.
// -----------------------------------------------------
function valid_y(v=[y1, y2, y3, y4, y5], c=4) =
    (c<0) ? [] : concat((v[c]<=0 || v[c]>=yi) ? [] : v[c], valid_y(c=c-1));

```


[User]
Hey, I'm looking to create a customizable switch enclosure for assistive technology, similar to the DIYAT V4 but using a beefier microswitch. I need it to be parametric in OpenSCAD so I can adjust the size and style easily. I've got some specific requirements:

1.  **Multiple Parts:** The enclosure should be made of three main parts that fit together: a base, a retainer, and a button. I'd like to be able to export each part separately.

2.  **Parametric Diameter:** I need to be able to specify the outside diameter of the enclosure, probably somewhere between 55 and 65mm.

3.  **Base Options:** There should be two base options: a standard base and one that includes a mounting plate for a strap. The strap slots should be designed for a 1-inch strap.

4.  **Button Options:** I'd also like two button styles, one that's recessed (concave) and one that's protruding (convex).

5.  **Threaded Latch:** The retainer needs to screw onto the base using a threaded connection. It would be great if the threads could also be parametric for size, pitch, and length.

6.  **Flexure Activation:** The button should activate using radial flexures, similar to how it works in the video link I can provide (https://youtu.be/2EqkSfG4u5U), for smooth and durable button action.

7.  **Microswitch Compatibility:** The enclosure must fit a standard mini-microswitch. (I can specify the exact dimensions if needed).

8.  **PCB Stand Offs:** I need to mount a PCB inside the base so the design needs some standoffs/posts for mounting the PCB.

9.  **Cable Cutout:** There also needs to be a cutout for a cable, preferably on the side of the base.

10. **No Fasteners:** The three parts should fit together without screws, and just the threads should hold them together.

For libraries, could you please use the `metric_thread` module from the `MCAD` library, and the `rounded_cube` module from somewhere as well (either create one or if it exists in one of the other libraries, like `BOSL` or something. If there's a library for rounded cylinders that's cool too. I need to make some customizations to the flexure mechanism and those might be useful.

Could you provide the OpenSCAD code for this, or, if it's too complicated for one file, for each individual part in its own `.scad` file? It would also be cool to see a few of the main parameters exposed as customizer options. Let me know if you have any questions.

[Assistant]
DIYAT_V5_MS_FLXR_pub04a.scad:
```
// Drew Browning 2021
// DIYAT_V5_MS_FLXR_pub04a

// part to build
_part = "base"; // [base,base-for-strap,retainer,button-cc,button-cv]
// outside diameter in mm (55 - 65)
_outsideDiameter = 65;// [55:65]

/*[Hidden]*/
_outsideHeight = 22;
_partGap = 0.2;
_switchHeight = 15.2;
_interiorHeight = _switchHeight+1;
_topBottomThickness = 2;
_lipHeight = 3;
_resolution = 100; // [30:Low, 60:Medium, 120:High]

////////////////////////////////////////////////////////////////////////
make();

module make($fn=_resolution) {
    outsideDiameter = _outsideDiameter;
	baseHeight = _interiorHeight - _topBottomThickness - _lipHeight;
	retainerHeight = _outsideHeight-baseHeight-_lipHeight+3;

    // call respective modules
	if (_part == "base") {
		makeBase( outsideDiameter, baseHeight,_topBottomThickness,_lipHeight,_partGap);
	} else if (_part == "base-for-strap") {
		makeBaseForStrap( outsideDiameter, baseHeight,_topBottomThickness,_lipHeight,_partGap);
	} else if (_part == "retainer") {
		makeRetainer(outsideDiameter,retainerHeight,_topBottomThickness,_lipHeight,_partGap);
	} else if (_part == "button-cc") {
        makeButton_cc(outsideDiameter,retainerHeight,_topBottomThickness,_lipHeight, _partGap);
	} else if (_part == "button-cv") {
        makeButton_cv(outsideDiameter,retainerHeight,_topBottomThickness,_lipHeight, _partGap);
	}
}
module makeBase(diameter,height, base,lipHeight,partGap) {
	height = max(height, base+lipHeight);
	radius = diameter/2;
	innerRadius = radius - 5.6 - partGap;
	innerRadiusB = radius - 5.6;
	fullHeight = height + lipHeight;
	rounding = 2.0;
	eps = 0.1;
    standoff = height+3;//standoff height
    buttonstop = height+3.25;
    translate([0,0,0]){union(){
        difference(){
            union(){
                // body
                roundCylinder(radius, 11.1, rounding);
                // lower clamp stop
                translate([0,0,11.1+lipHeight]){cylinder(r=innerRadius+3-partGap,h=height/3+.4);}
                //threads
                translate([0,0,11.1]){
                    metric_thread (diameter=(diameter-2.5-partGap), pitch=2, length=lipHeight);}
            }
            color("Purple"){union() {
                // inner cutout
                translate([0,0,base]){cylinder(r=innerRadius, h=fullHeight*2);}//innermost cutout
                //cable cutout
                translate([1.5,0,height-1.5]){rotate([0,0,180]){cube([3,diameter*.55,12],false);}}
                translate([0,0,height-1.5]){rotate([90,0,0]){cylinder(d=3,h=diameter*.55,center=false);}}
                //translate([0,-radius-1,0]){cube(diameter+2,true);}//cross-section cube
            }}
        }
        // lower button stop
        rotate([0,0,0]){translate([-.5,innerRadius-3.75,2]){cube([1,2.9,buttonstop],false);}}
        rotate([0,0,120]){translate([-.5,innerRadius-3.75,2]){cube([1,2.9,buttonstop],false);}}
        rotate([0,0,240]){translate([-.5,innerRadius-3.75,2]){cube([1,2.9,buttonstop],false);}}
        difference(){
            // button stop
            difference(){
                translate([0,0,2]){cylinder(r=innerRadius-3.6, h=buttonstop);}//OD of cutter (23.1x15))
                translate([0,0,1]){cylinder(r=innerRadius-4.6, h=height+8);}//ID of cutter
            }
            union() {
                //cable cutout
                translate([1.5,0,height-1.5]){rotate([0,0,180]){cube([3,diameter*.55,12],false);}}
                translate([0,0,height-1.5]){rotate([90,0,0]){cylinder(d=3,h=diameter*.55,center=false);}}
            }
        }
        // PCB standoff/posts
        color("Pink"){translate([3.1,0,-2]){union(){
            translate([0,4.3,13/2+3.4]){cube([20.5,2,13],true);}//standoff box back
            translate([-9.5,-4.3,13/2+3.4]){cube([1.5,2,13],true);}//standoff box front left
            translate([9.5,-4.3,13/2+3.4]){cube([1.5,2,13],true);}//standoff box front right
            translate([11.25,0,13/2+3.4]){cube([2,10.6,13],true);}//standoff box right
            translate([-11.25,0,13/2+3.4]){cube([2,10.6,13],true);}//standoff box left
            translate([-3.7,2,13/2-.5]){cube([1,3,13-8],true);}//standoff left
            translate([4.4,2,13/2-.5]){cube([1,3,13-8],true);}//standoff right
            translate([-4.8,4.1,13-1.15]){sphere(r=1);}//bumps
            translate([4.75,4.1,13-1.15]){sphere(r=1);}//bumps
        }}}
    }}
}
module makeBaseForStrap(diameter,height,base,lipHeight,partGap){
	height = max(height, base+lipHeight);
	radius = diameter/2;
	innerRadius = radius - 5.6 - partGap;
	fullHeight = height + lipHeight;
	rounding = 2.0;
	eps = 0.1;
    standoff = height+3;//standoff height
    buttonstop = height+3.25;
    union(){
        difference(){
            union(){
                // body
                roundCylinder(radius, 11.1, rounding);
                // lower clamp stop
                translate([0,0,11.1+lipHeight]){cylinder(r=innerRadius+3-partGap,h=height/3+.4);}
                //threads
                translate([0,0,11.1]){
                    metric_thread (diameter=(diameter-2.5-partGap), pitch=2, length=lipHeight);}
            }
            color("Purple"){union() {
                // inner cutout
                translate([0,0,base+.5]){cylinder(r=innerRadius, h=fullHeight*2);}//innermost cutout
                //cable cutout
                translate([1.5,0,height-1.5]){rotate([0,0,180]){cube([3,diameter*.55,12],false);}}
                translate([0,0,height-1.5]){rotate([90,0,0]){cylinder(d=3,h=diameter*.55,center=false);}}
                //translate([0,-radius-1,0]){cube(diameter+2,true);}//cross-section cube
            }}
        }
        // lower button stop
        rotate([0,0,0]){translate([-.5,innerRadius-3.75,2]){cube([1,2.9,buttonstop],false);}}
        rotate([0,0,120]){translate([-.5,innerRadius-3.75,2]){cube([1,2.9,buttonstop],false);}}
        rotate([0,0,240]){translate([-.5,innerRadius-3.75,2]){cube([1,2.9,buttonstop],false);}}
        difference(){
            // button stop
            difference(){
                translate([0,0,2]){cylinder(r=innerRadius-3.6, h=buttonstop);}//OD of cutter (23.1x15))
                translate([0,0,1]){cylinder(r=innerRadius-4.6, h=height+8);}//ID of cutter
            }
            union() {
                //cable cutout
                translate([1.5,0,height-1.5]){rotate([0,0,180]){cube([3,diameter*.55,12],false);}}
                translate([0,0,height-1.5]){rotate([90,0,0]){cylinder(d=3,h=diameter*.55,center=false);}}
            }
        }
        // PCB standoff/posts
        color("Pink"){translate([3.1,0,-2]){union(){
            translate([0,4.3,13/2+3.4]){cube([20.5,2,13],true);}//13bx back
            translate([-9.5,-4.3,13/2+3.4]){cube([1.5,2,13],true);}//13 box front left
            translate([9.5,-4.3,13/2+3.4]){cube([1.5,2,13],true);}//standoff box front right
            translate([11.25,0,13/2+3.4]){cube([2,10.6,13],true);}//standoff box right
            translate([-11.25,0,13/2+3.4]){cube([2,10.6,13],true);}//standoff box right
            translate([-4,0,13/2-.5]){cube([3,7,13-8],true);}//standoff
            translate([4.5,0,13/2-.5]){cube([3,7,13-8],true);}//standoff
            translate([-4.8,4,13-1.15]){sphere(r=1);}//bumps
            translate([4.75,4,13-1.15]){sphere(r=1);}//bumps
        }}}
        // strap/mounting plate
        rotate([0,0,90]){
            difference(){
                //cylinder(r=innerRadius+10,h=2);//circular base plate
                translate([-(diameter+12)/2,-18,0]){
                    rounded_cube([diameter+12,36,2.5],1);}//rect. base plate (rounded)
                    //rounded_cubeNilsR(diameter+12,36,3,60,1);}
                union(){//1" strap slots
                    translate([(radius+2)*-1,0,1]){//front slot
                        translate([0,-13,0]){cylinder(r=1.5,h=6,center=true);}//right end
                        translate([0,13,0]){cylinder(r=1.5,h=6,center=true);}//left end
                        translate([0,0,0]){cube([3,26,6],true);}//slot
                        //top bevels
                        translate([0,0,2.5]){rotate([0,45,0]){cube([4,26,4],true);}}//slot bevel
                        translate([0,-13,1.17]){cylinder(r1=1,r2=2,h=1,center=true);}//right end bevel
                        translate([0,13,1.17]){cylinder(r1=1,r2=2,h=1,center=true);}//left end bevel
                        //bottom bevels
                        translate([0,0,-2]){rotate([0,45,0]){cube([4,26,4],true);}}//slot bevel
                        translate([0,-13,-.67]){cylinder(r1=2,r2=1,h=1,center=true);}//right end bevel
                        translate([0,13,-.67]){cylinder(r1=2,r2=1,h=1,center=true);}//left end bevel
                    }
                    translate([(radius+2),0,1]){//back slot
                        translate([0,-13,0]){cylinder(r=1.5,h=6,center=true);}//right end
                        translate([0,13,0]){cylinder(r=1.5,h=6,center=true);}//left end
                        translate([0,0,0]){cube([3,26,6],true);}//slot
                        //top bevels
                        translate([0,0,2.5]){rotate([0,45,0]){cube([4,26,4],true);}}//slot bevel
                        translate([0,-13,1.17]){cylinder(r1=1,r2=2,h=1,center=true);}//right end bevel
                        translate([0,13,1.17]){cylinder(r1=1,r2=2,h=1,center=true);}//left end bevel
                        //bottom bevels
                        translate([0,0,-2]){rotate([0,45,0]){cube([4,26,4],true);}}//slot bevel
                        translate([0,-13,-.67]){cylinder(r1=2,r2=1,h=1,center=true);}//right end bevel
                        translate([0,13,-.67]){cylinder(r1=2,r2=1,h=1,center=true);}//left end bevel
                    }
                }
            }
        }
    }
}
module makeRetainer(diameter,height,base,lipHeight,partGap) {
	height = max(height, base+lipHeight);
	radius = diameter/2;
	innerRadius = radius - 2.5;
	innerRadiusB = radius - 2.5 - partGap;
	rounding = 2.0;
	eps = 0.1;
    difference(){
        // body
        roundCylinder(radius,height,rounding);
        // inner cutout
        translate([0,0,height-lipHeight-5.1]){cylinder(r=innerRadius,h=height+1,center=false);}    // button hole through top
        translate([0,0,base-2]) {cylinder(r=innerRadius-9.5, h=height*2, center = true);}
        // threads
        translate([0,0,height-lipHeight]){rotate([0,0,-120]){
            metric_thread (internal=true,diameter=diameter-2.5+partGap+.4,pitch=2,length=lipHeight+.1);}}
    }
}
module makeButton_cv(diameter, height, base, lipHeight, partGap){
	height = max(height, base+lipHeight);
	radius = diameter/2;
	innerRadius = radius-3.1-partGap;
	rounding = 1.5;
    translate([0,0,6]){rotate([0,180,0]){
        for (i = [0:45:337.5]){
            rotate([0, 0, i])
            makeArcDB(_outsideDiameter-2.75);
        }
        difference(){
            union(){
                // convex button
                translate([0,0,-height+9]){roundCylinder(innerRadius-9.3,height-3.5,rounding);}
                // rim for clamping
                translate([0,0,5]){cylinder(r=innerRadius+.55, h=1.1);}//rim
            }
            //rim-slot-cutter
            difference(){
                translate([0,0,4]){cylinder(r=innerRadius-2.5, h=3);}//OD of cutter
                translate([0,0,4]){cylinder(r=innerRadius-6.1, h=5);}//ID of cutter
            }
        }
    }}
}
module makeButton_cc(diameter, height, base, lipHeight, partGap){
	height = max(height, base+lipHeight);
	radius = diameter/2;
	innerRadius = radius-3.1-partGap;
	rounding = 1.5;
    translate([0,0,6]){rotate([0,180,0]){
        for (i = [0:45:337.5]){
            rotate([0, 0, i])
            makeArcDB(_outsideDiameter-2.75);
        }
        difference(){
            union(){
                // concave recessed button
                difference(){
                    // body (roundCylinder has artifacts if height is greater than 3 (height-7.8))
                    translate([0,0,-height+13.03]){cylinder(r=innerRadius-9.3,h=height-7.8);}
                    // concavity
                    translate([0,0,-diameter/(cos(diameter)*2)+4.75]){sphere(r=diameter/(cos(diameter)*2));}
                }
                // rim for clamping
                translate([0,0,5]){cylinder(r=innerRadius+.55, h=1.1);}//rim
            }
            //rim-slot-cutter
            difference(){
                translate([0,0,4]){cylinder(r=innerRadius-2.5, h=3);}//OD of cutter
                translate([0,0,4]){cylinder(r=innerRadius-6.1, h=5);}//ID of cutter
            }
        }
    }}
}
////////////// Utility Functions ///////////////////

module makeArcDB(diameter){
	radius = diameter/2;
    translate([4.1,0,3.2]){
            difference(){
                translate([0,0,2.5]){cylinder(r=radius-4, h=.4);}//OD of cutter
                translate([0,0,0]){cylinder(r=radius-4.6, h=5);}//ID of cutter
                translate([0,-radius,0]){cube([diameter,diameter,10]);}//rectangular cutter
                translate([-radius,-diameter,0]){cube([diameter,diameter,10]);}//rectangular cutter
            }
    }
}
module torus(r1, r2) {
	rotate_extrude(convexity = 4)
	translate([r1, 0, 0])
	circle(r = r2);
}

module roundCylinder(radius, height, rounding) {
	if (rounding == 0) {
		cylinder(r=radius, h=height);
	} else {
		hull() {
			translate([0,0,height-rounding]) {
				cylinder(r=radius, h=rounding);
			}
			translate([0,0,rounding]) {
				torus(radius-rounding, rounding);
			}
		}
	}
}
module rounded_cube(d,r) {
    hull() for(p=[[r,r,r], [r,r,d[2]-r], [r,d[1]-r,r], [r,d[1]-r,d[2]-r],
                  [d[0]-r,r,r], [d[0]-r,r,d[2]-r], [d[0]-r,d[1]-r,r], [d[0]-r,d[1]-r,d[2]-r]])
        translate(p) sphere(r);
}
// ----------------------------------------------------------------------------
function segments (diameter) = min (50, ceil (diameter*6));
// ----------------------------------------------------------------------------
module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1,
                      thread_size=-1, groove=false, square=false, rectangle=0,
                      angle=30, taper=0, leadin=0, leadfac=1.0)
{
   // thread_size: size of thread "V" different than travel per turn (pitch).
   // Default: same as pitch.
   local_thread_size = thread_size == -1 ? pitch : thread_size;
   local_rectangle = rectangle ? rectangle : 1;

   n_segments = segments (diameter);
   h = (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size / (2 * tan(angle));

   h_fac1 = (square || rectangle) ? 0.90 : 0.625;

   // External thread includes additional relief.
   h_fac2 = (square || rectangle) ? 0.95 : 5.3/8;

   tapered_diameter = diameter - length*taper;

   difference () {
      union () {
         if (! groove) {
            metric_thread_turns (diameter, pitch, length, internal, n_starts,
                                 local_thread_size, groove, square, rectangle, angle,
                                 taper);
         }

         difference () {

            // Solid center, including Dmin truncation.
            if (groove) {
               cylinder (r1=diameter/2, r2=tapered_diameter/2,
                         h=length, $fn=n_segments);
            } else if (internal) {
               cylinder (r1=diameter/2 - h*h_fac1, r2=tapered_diameter/2 - h*h_fac1,
                         h=length, $fn=n_segments);
            } else {

               // External thread.
               cylinder (r1=diameter/2 - h*h_fac2, r2=tapered_diameter/2 - h*h_fac2,
                         h=length, $fn=n_segments);
            }

            if (groove) {
               metric_thread_turns (diameter, pitch, length, internal, n_starts,
                                    local_thread_size, groove, square, rectangle,
                                    angle, taper);
            }
         }
      }

      // chamfer z=0 end if leadin is 2 or 3
      if (leadin == 2 || leadin == 3) {
         difference () {
            cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);

            cylinder (r2=diameter/2, r1=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
                      $fn=n_segments);
         }
      }

      // chamfer z-max end if leadin is 1 or 2.
      if (leadin == 1 || leadin == 2) {
         translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) {
            difference () {
               cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);
               cylinder (r1=tapered_diameter/2, r2=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
                         $fn=n_segments);
            }
         }
      }
   }
}
// ----------------------------------------------------------------------------
module metric_thread_turns (diameter, pitch, length, internal, n_starts,
                            thread_size, groove, square, rectangle, angle,
                            taper)
{
   // Number of turns needed.
   n_turns = floor (length/pitch);

   intersection () {

      // Start one below z = 0.  Gives an extra turn at each end.
      for (i=[-1*n_starts : n_turns+1]) {
         translate ([0, 0, i*pitch]) {
            metric_thread_turn (diameter, pitch, internal, n_starts,
                                thread_size, groove, square, rectangle, angle,
                                taper, i*pitch);
         }
      }

      // Cut to length.
      translate ([0, 0, length/2]) {
         cube ([diameter*3, diameter*3, length], center=true);
      }
   }
}
// ----------------------------------------------------------------------------
module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size,
                           groove, square, rectangle, angle, taper, z)
{
   n_segments = segments (diameter);
   fraction_circle = 1.0/n_segments;
   for (i=[0 : n_segments-1]) {
      rotate ([0, 0, i*360*fraction_circle]) {
         translate ([0, 0, i*n_starts*pitch*fraction_circle]) {
            //current_diameter = diameter - taper*(z + i*n_starts*pitch*fraction_circle);
            thread_polyhedron ((diameter - taper*(z + i*n_starts*pitch*fraction_circle))/2,
                               pitch, internal, n_starts, thread_size, groove,
                               square, rectangle, angle);
         }
      }
   }
}
// ----------------------------------------------------------------------------
module thread_polyhedron (radius, pitch, internal, n_starts, thread_size,
                          groove, square, rectangle, angle)
{
   n_segments = segments (radius*2);
   fraction_circle = 1.0/n_segments;

   local_rectangle = rectangle ? rectangle : 1;

   h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size / (2 * tan(angle));
   outer_r = radius + (internal ? h/20 : 0); // Adds internal relief.
   //echo (str ("outer_r: ", outer_r));

   // A little extra on square thread -- make sure overlaps cylinder.
   h_fac1 = (square || rectangle) ? 1.1 : 0.875;
   inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with
                                // cylinder.

   translate_y = groove ? outer_r + inner_r : 0;
   reflect_x   = groove ? 1 : 0;

   // Make these just slightly bigger (keep in proportion) so polyhedra will
   // overlap.
   x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02;
   x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02;
   z_incr = n_starts * pitch * fraction_circle * 1.005;

   /*
    (angles x0 and x3 inner are actually 60 deg)

                          /\  (x2_inner, z2_inner) [2]
                         /  \
   (x3_inner, z3_inner) /    \
                  [3]   \     \
                        |\     \ (x2_outer, z2_outer) [6]
                        | \    /
                        |  \  /|
             z          |[7]\/ / (x1_outer, z1_outer) [5]
             |          |   | /
             |   x      |   |/
             |  /       |   / (x0_outer, z0_outer) [4]
             | /        |  /     (behind: (x1_inner, z1_inner) [1]
             |/         | /
    y________|          |/
   (r)                  / (x0_inner, z0_inner) [0]

   */

   x1_outer = outer_r * fraction_circle * 2 * PI;

   z0_outer = (outer_r - inner_r) * tan(angle);
   //echo (str ("z0_outer: ", z0_outer));

   //polygon ([[inner_r, 0], [outer_r, z0_outer],
   //        [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]);
   z1_outer = z0_outer + z_incr;

   // Give internal square threads some clearance in the z direction, too.
   bottom = internal ? 0.235 : 0.25;
   top    = internal ? 0.765 : 0.75;

   translate ([0, translate_y, 0]) {
      mirror ([reflect_x, 0, 0]) {

         if (square || rectangle) {

            // Rule for face ordering: look at polyhedron from outside: points must
            // be in clockwise order.
            polyhedron (
               points = [
                         [-x_incr_inner/2, -inner_r, bottom*thread_size],         // [0]
                         [x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1]
                         [x_incr_inner/2, -inner_r, top*thread_size + z_incr],    // [2]
                         [-x_incr_inner/2, -inner_r, top*thread_size],            // [3]

                         [-x_incr_outer/2, -outer_r, bottom*thread_size],         // [4]
                         [x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5]
                         [x_incr_outer/2, -outer_r, top*thread_size + z_incr],    // [6]
                         [-x_incr_outer/2, -outer_r, top*thread_size]             // [7]
                        ],

               faces = [
                         [0, 3, 7, 4],  // This-side trapezoid

                         [1, 5, 6, 2],  // Back-side trapezoid

                         [0, 1, 2, 3],  // Inner rectangle

                         [4, 7, 6, 5],  // Outer rectangle

                         // These are not planar, so do with separate triangles.
                         [7, 2, 6],     // Upper rectangle, bottom
                         [7, 3, 2],     // Upper rectangle, top

                         [0, 5, 1],     // Lower rectangle, bottom
                         [0, 4, 5]      // Lower rectangle, top
                        ]
            );
         } else {

            // Rule for face ordering: look at polyhedron from outside: points must
            // be in clockwise order.
            polyhedron (
               points = [
                         [-x_incr_inner/2, -inner_r, 0],                        // [0]
                         [x_incr_inner/2, -inner_r, z_incr],                    // [1]
                         [x_incr_inner/2, -inner_r, thread_size + z_incr],      // [2]
                         [-x_incr_inner/2, -inner_r, thread_size],              // [3]

                         [-x_incr_outer/2, -outer_r, z0_outer],                 // [4]
                         [x_incr_outer/2, -outer_r, z0_outer + z_incr],         // [5]
                         [x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6]
                         [-x_incr_outer/2, -outer_r, thread_size - z0_outer]    // [7]
                        ],

               faces = [
                         [0, 3, 7, 4],  // This-side trapezoid

                         [1, 5, 6, 2],  // Back-side trapezoid

                         [0, 1, 2, 3],  // Inner rectangle

                         [4, 7, 6, 5],  // Outer rectangle

                         // These are not planar, so do with separate triangles.
                         [7, 2, 6],     // Upper rectangle, bottom
                         [7, 3, 2],     // Upper rectangle, top

                         [0, 5, 1],     // Lower rectangle, bottom
                         [0, 4, 5]      // Lower rectangle, top
                        ]
            );
         }
      }
   }
}

```


[User]
I need a parametric design for a test tube rack, something that can be 3D printed without supports. I'm thinking it should have a few shelves, maybe 2 or 3, that slide into the sides of the stand. The stand should have a small 'foot' and the option for 'shoes' to keep it stable.

Here are the parameters I want to control:

**Tubes:**

*   `tube_x_separation`: Distance between tubes along a row (X-axis).
*   `tube_y_separation`: Distance between rows of tubes (Y-axis).
*   `tube_y_count`: Number of rows of tubes.
*   `tube_x_count`: An array defining how many tubes are in each row.
*   `tube_x_offset`: An array to offset each row by a fraction of a tube, to stagger rows.
*   `tube_size`: An array specifying the diameter of the tube hole on each shelf (top to bottom).

**Shelves:**

*   `shelf_size`: A 3D vector for the base size of each shelf (x, y, z).
*   `shelf_count`: Number of shelves (2 or 3).
*   `shelf_height`: Vertical distance between shelves.
*   `reinforcement_rail`: Thickness of the reinforcement rails on the shelves.
*   `reinforcement_edge_inset`: Distance of the reinforcement rails from the shelf edge.
*  `registration_bump`: Size of the bump used to lock the shelves into the stand.
*  `slide_tolerance`: Amount of space to allow for sliding shelves into the stand.

**Stand:**

*   `stand_height`: Vertical height of the bottom shelf.
*   `stand_thick`: Thickness of the stand walls.
*   `stand_foot`: Width of the stand foot.
*   `stand_shoes`: Boolean, whether to include 'shoes' on the stand.
*   `shoe_size`: Size of the stand shoes (x, y, z).

**Hidden:**

*   `registration_inset`: Inset for the registration bump on the stand.
*   `reinforcement_rail_inset`: Inset for the reinforcement rails.

I'd also like a way to generate individual parts for printing, like a "platter" of all the stand parts, and one for all the shelves. The "assembly" view should show the assembled rack.

I'm thinking I need to use `hull()` to create rounded edges and the slide rail on the stand. I also need to use `mirror()` to create the stand sides and the registration bump on both sides of the shelf.

I think that's it - can you generate the OpenSCAD file or files, including the `mirror()` and `hull()` implementations, along with the requested parameters? It should use the standard OpenSCAD library only; no external libraries are required.

[Assistant]
tube_rack.scad:
```
/* [Parts] */
// select parts to display or print
Print_Part = "assembly"; // [assembly,platter_stand,platter_shelves,stand,shoe,shelf_1,shelf_2,shelf_3]

/* [Tubes] */
// X separation between tubes (along row)
tube_x_separation = 22; // [5:0.5:50]
// Y separation between tubes (between rows)
tube_y_separation = 22; // [5:0.5:50]
// How many rows of tubes
tube_y_count = 1; // [1:6]
// How many tubes on each row
tube_x_count = [6,6,6,6,0,0]; // 1:20
// Offset the row by a quarter of a tube. Alternate +1 and -1 to stagger rows.
tube_x_offset = [0,0,0,0,0,0]; // -1:1:1
// How large is the hole on each shelf (top..bottom)
tube_size = [17,17,8];

/* [Shelves] */
// Base size of each shelf  (x,y,z)
shelf_size = [140,30,2]; // 1:0.5:200
// How many shelves
shelf_count = 3; // [2:3]
// Vertical distance between shelves
shelf_height = 35; // [0:60]
// Thickness of reinforcement rails
reinforcement_rail = 2.0; // [0:0.5:5]
// Distance of rail from shelf edge
reinforcement_edge_inset = 1.5; // [0:0.5:5]
// Size of slide registration bump
registration_bump = 0.6; // [0:0.1:1.5]
// Tolerance distance for slide fit
slide_tolerance = 0.2; // [0:0.05:0.5]

/* [Stand] */
// Vertical height of the bottom shelf
stand_height = 10; // [10:1:40]
// Thickness of stand walls
stand_thick = 2; // [1:0.5:4]
// Width of stand foot
stand_foot = 10; // [5:1:20]
// Add 'shoes' to prevent rack tipping over
stand_shoes = true;
// Base size of stand shoes (x,y,z)
shoe_size = [15,70,2];

/* [Hidden] */
// Inset from slide edge for registration bump
registration_inset = 3; // [0:1:10]
// Distance of rail from stand
reinforcement_rail_inset = 6; // [0:1:10]

// combine options into arrays

// How much shelf space to allocate to each tube (x,y)
tube_separation = [tube_x_separation,tube_y_separation];

/*
// 1 x 10ml test
tube_y_count = 1;
tube_x_count = [1];
tube_x_offset = [0];
tube_size = [17,17,8];
tube_separation = [22,20,0];
shelf_size = [30,30,2];
shelf_height = 30;
shelf_count = 3;
stand_height = 10;
stand_thick = 2;
stand_foot = 10;
shoe_size = [15,70,2];
reinforcement_edge_inset = 2;
reinforcement_rail = 2.5;

*/

/*
// 14 x 2ml
tube_y_count = 2;
tube_x_count = [7,7];
tube_x_offset = [0,0];
tube_size = [11,11,7];
tube_separation = [15.5,20,0];
shelf_size = [120,40,1.5];
shelf_height = 16;
shelf_count = 3;
stand_height = 10;
stand_thick = 2;
stand_foot = 10;
*/

// pill box markings
/*
upper_markings = ["S","M","T","W","T","F","S"];
upper_markings_size = 4;
upper_markings_y = 40;
*/

if(Print_Part=="shelf_1") {
	shelf(0);
} else if(Print_Part=="shelf_2") {
	shelf(1);
} else if(Print_Part=="shelf_3") {
	shelf(2);
} else if(Print_Part=="stand") {
	translate([0,0,shelf_size[1]/2])
		rotate([-90,0,0]) stand();
} else if(Print_Part=="shoe") {
	translate([0,0,shoe_size[0]])
		rotate([0,90,0]) shoe();
} else if(Print_Part=="platter_stand") {
	// stand and shoes
	mirror_x() {
		translate([5,-(stand_height+shelf_height*(shelf_count-1))/2,shelf_size[1]/2])
			rotate([-90,0,0]) stand();
		if(stand_shoes) translate([-stand_foot-13,0,shoe_size[0]])
			rotate([0,90,0]) shoe();
	}
} else if(Print_Part=="platter_shelves") {
	// shelves
	for(i=[0:shelf_count-1]) translate([0,(i-(shelf_count-1)/2)*(shelf_size[1]+5),0])
		shelf(i);
} else {
	rack_assembly();
}

module rack_assembly() {
	color("grey",0.6) for(i=[0:shelf_count-1]) translate([0,0,(shelf_count-i-1)*shelf_height+stand_height]) {
		rotate([180,0,0]) shelf(i);
	}
	color("blue",0.5) mirror_x() translate(-[shelf_size[0]/2,0,0]) {
		stand();
		if(stand_shoes) {
			translate([-2,0,-shoe_size[2]]) shoe();
		}
	}
}

module shoe() {
	translate([0,-shoe_size[1]/2,0])
		cube(shoe_size);
	clip_offset = 5; // stand_thick*2 + 1;
	clip_size = [shoe_size[0]-clip_offset,2,2];
	p = [clip_offset,-shelf_size[1]/2-2-slide_tolerance,shoe_size[2]];
	mirror_y() {
		translate(p) cube(clip_size+[0,0,2.1]);
		hull() {
			translate(p+[0,0,stand_thick+0.51]) cube(clip_size);
			translate(p+[0,4,stand_thick-slide_tolerance]) cube(clip_size);
		}
		//translate(p+[0,stand_thick*2,stand_thick-slide_tolerance]) cube(clip_size+[0,3,0]);
	}
}

module stand() {
	cut_z = 0; // shelf_size[2];
	translate(-[stand_thick,shelf_size[1]/2,0]) {
		cube([stand_thick,shelf_size[1],(shelf_count-1) * shelf_height + stand_height]);
		for(i=[0:shelf_count-1]) translate([0,0,(i)*shelf_height+ stand_height]) {
			// slide rail
			translate([0,0,0]) cube([stand_thick+5,shelf_size[1],2]);
			translate([0,0,-4-shelf_size[2]-slide_tolerance]) {
				cube([stand_thick+1,shelf_size[1],2]);
				translate([stand_thick,0,0]) difference() {
					hull() {
						translate([1,0,0]) cube([1,shelf_size[1],2]);
						translate([4,0,2]) cube([1,shelf_size[1],2]);
					}
					// registration bumps
					for(y=[
						registration_inset,
						shelf_size[1]-registration_inset-registration_bump*2-1
					]) {
						translate([1,y,4]) hull() {
							cube([5,registration_bump*2+1,0.1+slide_tolerance]);
							translate([0,registration_bump,-registration_bump]) cube([5,1,0.1+registration_bump+slide_tolerance]);
						}
					}
				}
			}
		}
		// foot
		cube([stand_foot+stand_thick,shelf_size[1],stand_thick]);
		// foot chamfer
		hull() {
			cube([stand_thick,shelf_size[1],stand_thick*2]);
			cube([stand_thick*2,shelf_size[1],stand_thick]);
		}
	}
}

module shelf(index=0) {
	translate(-[shelf_size[0],shelf_size[1],0]/2) {
		difference() {
			cube(shelf_size);
			// tube holes
			for(y=[0:tube_y_count-1]) {
				x_count = tube_x_count[y];
				tube_origin = [
					shelf_size[0] - tube_separation[0]*(x_count-1 - tube_x_offset[y]*0.5),
					shelf_size[1] - tube_separation[1]*(tube_y_count-1),
					-1
				]/2;
				for(x=[0:x_count-1]) {
					translate(tube_origin + [x*tube_separation[0],y*tube_separation[1],0])
						cylinder(d=tube_size[index], h=shelf_size[2]+1, $fn=30);
				}
			}
			// markings
			/*
			if(upper_markings) {
				x_count = tube_x_count[0];
				label_origin = [
					shelf_size[0] - tube_separation[0]*(x_count-1 - tube_x_offset[y]*0.5),
					upper_markings_y,
					1
				]/2;
				for(x=[0:x_count-1]) {
					translate(label_origin + [x*tube_separation[0],0,0])
						rotate([180,0,0]) linear_extrude(height=1)
							text(upper_markings[x],font="Liberation Sans:style=Bold",size=upper_markings_size,halign="center",valign="center");

				}
			}
			*/
		}
		// reinforcement
		if(reinforcement_rail) {
			r = reinforcement_rail;
			if(tube_y_count>1) {
				// reinforcement rails between rows
				for(y=[0:tube_y_count-2]) {
					tube_y = (shelf_size[1] - tube_separation[1]*(tube_y_count-1))/2;
					rail_origin = [
						reinforcement_rail_inset,
						tube_y + tube_separation[1]*(y+0.5) - r/2, //tube_separation[1]*(y+1) - r/2,
						shelf_size[2]-1
					];
					rail_size = [ shelf_size[0]-reinforcement_rail_inset*2, r, 1 ];
					hull() {
						translate(rail_origin) cube(rail_size);
						translate(rail_origin+[r,0,r]) cube(rail_size-[2*r,0,0]);
					}
				}
			}
			// reinforcement rails at sides
			for(y=[reinforcement_edge_inset, shelf_size[1]-reinforcement_edge_inset-r]) {
				rail_origin = [
					reinforcement_rail_inset,
					y,
					shelf_size[2]-1
				];
				rail_size = [ shelf_size[0]-reinforcement_rail_inset*2, r, 1 ];
				hull() {
					translate(rail_origin) cube(rail_size);
					translate(rail_origin+[r,0,r]) cube(rail_size-[2*r,0,0]);
				}
			}
		}
	}
	mirror_x() translate(-[shelf_size[0],shelf_size[1],0]/2) {
		// slide rails
		difference() {
			hull() {
				cube([4,shelf_size[1],shelf_size[2]]);
				cube([1,shelf_size[1],shelf_size[2]+2]);
			}
		}
		// registration bump
		translate([0,registration_inset,shelf_size[2]-0.1]) hull() {
			cube([5.5,registration_bump*2+1,0.1]);
			translate([0,registration_bump,0]) cube([5,1,0.1+registration_bump]);
		}
	}

}

// utilities

module cube_centered(size=[10,10,10]) {
	translate(-[size[0],size[1],0]/2) cube(size);
}

module mirror_x() {
	children();
	mirror([1,0,0]) children();
}

module mirror_y() {
	children();
	mirror([0,1,0]) children();
}

module mirror_z() {
	children();
	mirror([0,0,1]) children();
}

module mirror_xy() {
	mirror_x() mirror_y() children();
}

module explode(distance=100, direction=[0,0,1]) {
	nd = direction / norm(direction);
	translate(nd * distance * animate_explode) children();
}

```


[User]
Hi, I'd like to design a customizable planter and plate set using OpenSCAD. I need it to be parametric so I can easily change the dimensions and some features.

Here are the main things I need:

**Planter:**

*   **Shape:** I want the base shape of the planter to be controllable, using the `$fn` variable in OpenSCAD, but it needs to still work with the rest of the script to be a planter. I'm thinking options for `$fn` like 3 for a triangle, 4 for a square, 5 for a pentagon, 6 for a hexagon, 10 for a slightly rounded "circle" and 100 for a smooth circle.
*   **Base Diameter:**  I need to be able to set the diameter of the base of the planter.
*   **Mouth Diameter:**  I need to set the diameter of the top opening of the planter. This should allow for a cone shape in most cases.
*   **Height:**  I need to set the overall height of the planter.
*   **Thickness:**  I need to be able to set the wall thickness of the planter. This should be an inward thickness from the outer diameters.
*   **Drain Hole:** I need an option to include a drain hole at the bottom of the planter. This should have a settable diameter. I'd like a simple yes/no option for the drain hole.

**Plate:**

*   **Plate Diameter Difference:** I need to set how much larger the plate's diameter is than the base diameter of the planter.
*   **Plate Depth:** I need to set the depth of the plate (the distance from the bottom of the plate to the lip).
*   **Plate Thickness:**  I need to set the thickness of the plate. This should be inward thickness from the outer diameter.

**Combining Planter and Plate:**

*   I need options for different arrangements of the planter and the plate:
    *   A separate planter and plate.
    *   A combined planter and plate where they touch.
    *   A combined planter and plate with a set gap between them.

**Units:**

*   I also need a way to easily switch between millimeters and inches for all the dimensions.

It'd be great if all of this was easy to configure. I'd like to have the code be as clear and understandable as possible.

Could you generate the OpenSCAD file(s) for this? I'm not using any libraries.

[Assistant]
Customizable_Planter.scad:
```
// Custom Planter
// Creator: Levi Oxner
// Created:October 10, 2018

/* [Part] */
Units="mm"; //[mm,inches]

Part="3";//[1:Planter,2:Plate,3:Joined Planter & Plate,4:Spaced Planter & Plate]

/* [Planter] */

//Shape (Only Smooth Circle is visible on the preview, check my uploaded STL files for examples of other shapes)
$fn=100; //[3:Triangle,4:Square,5:Pentagon,6:Hexagon,10:Rough "Circle",100:Smooth Circle]

// Base Diameter
B_Diameter=100;

// Mouth Diameter
M_Diameter=50;

// Planter Height
Height=100;

//Thickness inward from outerdiameter (Keep less than ~1/2 the Diameters)
Thickness=5;

/* [Drain Hole] */
//Do you want a drain hole?
Drain= "No"; //[Yes,No]

//Drain Hole Diameter
D_Diameter=5;

/* [Plate] */
//How much larger the plate is from the base diameter (recommend greater than Plate Thickness)
Plate_Diameter_Difference=10;

//Plate Rim Depth
Plate_Depth=10;

//Plate Thicness inward from outerdiameter
Plate_Thickness=5;

//Spaced Planter & Plate Gape (Mind if you're printing with or without supports)
Gape=2;

if (Part=="4"){
    if (Units=="mm"){
        Custom_Planter();
        translate([0,0,(Plate_Depth+Plate_Thickness)/2-Plate_Thickness-Gape])
            Plate();
    }if(Units=="inches"){
        scale([25.4,25.4,25.4])
            Custom_Planter();
        scale([25.4,25.4,25.4])
        translate([0,0,(Plate_Depth+Plate_Thickness)/2-Plate_Thickness-Gape])
            Plate();
    }
}else if(Part=="3"){
    if (Units=="mm"){
        Custom_Planter();
        translate([0,0,(Plate_Depth+Plate_Thickness)/2-Plate_Thickness])
            Plate();
    }if(Units=="inches"){
        scale([25.4,25.4,25.4])
            Custom_Planter();
        scale([25.4,25.4,25.4])
        translate([0,0,(Plate_Depth+Plate_Thickness)/2-Plate_Thickness])
            Plate();
    }
}else if(Part=="2"){
    if(Units=="mm"){
        Plate();
    }if(Units=="inches"){
        scale([25.4,25.4,25.4])
            Plate();
    }
}else{
    if(Units=="mm"){
        Custom_Planter();
    }if(Units=="inches"){
        scale([25.4,25.4,25.4])
            Custom_Planter();
    }
}

/////////////////////////////////Planter////////////////////////////////////////////

module Custom_Planter(){

    difference(){
        translate([0,0,Height/2])
            cylinder(h=Height,d1=B_Diameter,d2=M_Diameter,center=true);
        translate([0,0,Height/2+Thickness])
            cylinder(h=Height,d1=B_Diameter-2*Thickness,d2=M_Diameter-2*Thickness,center=true);

        //Drain
        if(Drain=="Yes"){
            translate([0,0,Thickness/2])
                cylinder(h=Thickness,d=D_Diameter,center=true);
        }//if drain

    }//difference
}//module planter

///////////////////////////////////Plate////////////////////////////////////////////

module Plate(){

    difference(){
            cylinder(h=Plate_Depth+Plate_Thickness,d=Plate_Diameter_Difference+B_Diameter,center=true);
        translate([0,0,Plate_Thickness])
            cylinder(h=Plate_Depth+Plate_Thickness,d=Plate_Diameter_Difference+B_Diameter-2*Plate_Thickness,center=true);
    }//difference

}//module plate
```