[User]
Okay, I'm looking to design a parametric OpenSCAD model for a Kwikset KW1 key, but I need it to also integrate a credit card-style backing. I want the final design to be something that can be easily carried in a wallet, like a plastic card, but still function as a key.

Here's what I'm envisioning:

1.  **Key Parameters:** The key part needs to be a Kwikset KW1 type key, with the standard five pin depths. These depths need to be fully customizable through parameters, not just hard-coded values. Specifically, I'd like to be able to set the depth of each pin cut independently via named variables: `first_measurement`, `second_measurement`, `third_measurement`, `fourth_measurement`, and `fifth_measurement`. These should all have values ranging from 1 to 7.

2.  **Key Dimensions:** The key dimensions should generally match a typical Kwikset KW1 key, including the key blade length, width, and thickness.  The shoulder position, pin spacing, and depth increment should all be parameterized to allow for slight adjustment if needed. The code I'm using has values like `thickness = mm(0.080);`, `length = mm(9/8);`, `width = mm(.337);` etc. and I'd like to keep the same basic dimensions and parameter names.

3.  **Credit Card Integration:** I want the key to be attached to a "credit card" backing. This backing should be roughly the size and shape of a standard credit card. I want to be able to make the card portion and the cutouts in the card customizable. I'm looking to have the card backing have dimensions of roughly 75mm wide and 45 mm high, with rounded corners. Also, there should be a rectangular cutout in the card, roughly 49mm by 21mm.

4.  **Rounded Elements:** The key handle, and the credit card backing should use rounded corners and edges for both aesthetics and practicality. I'd like to keep the ability to adjust the rounding radius of all rounded parts separately.

5.  **Keychain Hole:** There needs to be a small hole in the handle area of the key for attaching it to a keyring.

6.  **Printability:** It needs to be designed to print reliably, accounting for typical 3D printing characteristics. I’ve noticed a fudge factor in my current code for printing, but I would be happy to adjust that manually if necessary.

7. **Units:** All internal calculations should use inches, as the original code does, but final output dimensions should be in mm to match typical printer settings. This should mean the final key design is properly sized. I need a `mm()` function to convert from inches to millimeters.

Essentially, I want a robust, parametric key design that can be easily adapted to different pin settings and printer setups and that fits into a wallet. Can you generate the OpenSCAD code for this?

[Assistant]
kw1CardKey.scad:
```
// This is a customizer Remix of http://www.thingiverse.com/thing:8925 by nrp.
// I added the "credit card" and the ability to enter your numbers without using SCAD.  Everything else is original work from original design.

// Physical Keygen - by Nirav Patel <http://eclecti.cc>
//
// Generate a duplicate of a Kwikset KW1 key by editing the last line of the file
// and entering in the key code of the lock.  If you don't know the key code,
// you can measure the key and compare the numbers at:
// http://web.archive.org/web/20050217020917fw_/http://dlaco.com/spacing/tips.htm
//
// This work is licensed under a Creative Commons Attribution 3.0 Unported License.

// Since the keys and locks I have were all designed in imperial units, the
// constants in this file will be defined in inches.  The mm function
// allows us to retain the proper size for exporting a metric STL.

// First measurement
First_measurement = 1; //[1:7]
// First measurement
Second_measurement = 1; //[1:7]
// First measurement
Third_measurement = 1; //[1:7]
// First measurement
Fourth_measurement = 1; //[1:7]
// First measurement
Fifth_measurement = 1; //[1:7]

function mm(i) = i*25.4;

module rounded(size, r) {
    union() {
        translate([r, 0, 0]) cube([size[0]-2*r, size[1], size[2]]);
        translate([0, r, 0]) cube([size[0], size[1]-2*r, size[2]]);
        translate([r, r, 0]) cylinder(h=size[2], r=r);
        translate([size[0]-r, r, 0]) cylinder(h=size[2], r=r);
        translate([r, size[1]-r, 0]) cylinder(h=size[2], r=r);
        translate([size[0]-r, size[1]-r, 0]) cylinder(h=size[2], r=r);
    }
}

module bit() {
    w = mm(1/4);
    difference() {
        translate([-w/2, 0, 0]) cube([w, mm(1), w]);
        translate([-mm(5/128), 0, 0]) rotate([0, 0, 135]) cube([w, w, w]);
        translate([mm(5/128), 0, 0]) rotate([0, 0, -45]) cube([w, w, w]);
    }
}

// Kwikset KW1 5 pin key.  The measurements are mostly guesses based on reverse
// engineering some keys I have and some publicly available information.
module kw1(bits) {
    // You may need to adjust these to fit your specific printer settings
    thickness = mm(0.080);
    length = mm(9/8);
    width = mm(.337);

    shoulder = mm(.247);
    pin_spacing = mm(.15);
    depth_inc = mm(.023);

    // A fudge factor.  Printing with your average RepRap, the bottom layer is
    // going to be squeezed larger than you want. You can make the pins
    // go slighly deeper by a fudge amount to make up for it if you aren't
    // adjusting for it elsewhere like Skeinforge or in your firmware.
    fudge = 0.5;

    // Adding the credit card to the key
    difference(){

    // The Card
    translate([0,4]){
        linear_extrude(height=1)offset(r=5)
    square([75, 45], center=true);
                    }
    // The empty space in the card

    translate([-21.5,-6]){
        linear_extrude(height=1)offset(r=3)
    square([49, 21]);
                         }
                }

    // Handle size
    h_l = mm(1);
    h_w = mm(1);
    h_d = mm(1/16);
    difference() {
        // blade and key handle
        union() {
            translate([-h_l, -h_w/2 + width/2, 0]) rounded([h_l, h_w, thickness], mm(1/4));
            // cut a little off the tip to avoid going too long
            cube([length - mm(1/64), width, thickness]);
        }

        // chamfer the tip
        translate([length, mm(1/8), 0]) {
            rotate([0, 0, 45]) cube([10, 10, thickness]);
            rotate([0, 0, 225]) cube([10, 10, thickness]);
        }

        // put in a hole for keychain use
        translate([-h_l + mm(3/16), width/2, 0]) cylinder(h=thickness, r=mm(1/8));

        // cut the channels in the key.  designed more for printability than accuracy
        union() {
            translate([-h_d, mm(.105), mm(.025)]) rotate([225, 0, 0]) cube([length + h_d, width, width]);
            translate([-h_d, mm(.105), mm(.05)]) rotate([260, 0, 0]) cube([length + h_d, thickness/2, mm(1/32)]);
            translate([-h_d, mm(.105), 0]) cube([length + h_d, mm(7/128), mm(.05)]);
            translate([-h_d, mm(.105) + mm(7/128), mm(.05)]) rotate([225, 0, 0]) cube([length + h_d, mm(3/64), thickness]);
        }

        translate([-h_d, width - mm(9/64), mm(.043)]) {
            cube([length + h_d, width - (width - mm(10/64)), thickness]);
            rotate([50, 0, 0]) cube([length + h_d, width, thickness]);
        }

        union() {
            translate([-h_d, mm(0.015), mm(.03)]) cube([length + h_d, mm(15/256), thickness]);
            translate([-h_d, mm(0.015) + mm(13/256), thickness - mm(1/64)]) rotate([45, 0, 0]) cube([length + h_d, mm(1/16), mm(1/16)]);
        }

        // Do the actual bitting
        for (b = [0:4]) {
            // KW1 doesn't have a 0 value and instead has 1 start around .008" deep
            translate([shoulder + fudge + b*pin_spacing, width - mm(.008) - (bits[b] - 1)*depth_inc - fudge, 0]) bit();
        }
    }
}

// This sample key goes to a lock that is sitting disassembled on my desk
// Flip the key over for easy printing
kw1([First_measurement,Second_measurement,Third_measurement,Fourth_measurement,Fifth_measurement]);

```


[User]
I'd like to create a custom color palette generator in OpenSCAD, building off the work done in the `colors.scad` library I've been using. This library provides a `colorArray()` function that generates a 2D array of RGB values, and also some useful modules like `wheel()`, `grid()`, and `demoCyl()`. I need to create a more flexible system for generating different kinds of color palettes using this library.

Specifically, I'd like a new module, let's call it `palette_generator()`. This module should accept the following parameters:

*   `type`: A string indicating the type of palette to generate. It should accept at least "grid", "wheel", and "cylinder".
*   `columns`: An integer for the number of columns in a grid palette, segments in a wheel, or segments around a cylinder.
*   `rows`: An integer for the number of rows in a grid or rings in a wheel, or layers of a cylinder.
*   `scaled`: A boolean indicating whether to scale colors from white to full color.
*   `pixel_size`: A vector `[x, y, z]` for the size of each color representation (like a cube), used in the grid and wheel palettes.
*   `cylinder_radius`: A real value specifying the radius of a cylinder, only used when `type` is "cylinder".

Inside `palette_generator()`:

1.  It should use an `if`/`else if` block to determine which type of palette to generate.
2.  For "grid": it should call the `grid()` function from `colors.scad` and pass all relevant arguments.
3.  For "wheel": it should call the `wheel()` function from `colors.scad` and pass all relevant arguments.
4.  For "cylinder": it should call the `demoCyl()` function from `colors.scad` and pass all relevant arguments, using `cylinder_radius` to define the cylinder's size.
5.  It should not need to handle the `objDemo()` type, or any other logic not specifically related to this module.

I'd also like to include another top-level variable that specifies a default pixel size, something like `default_pixel = [20, 20, 20];`, so that the `pixel_size` parameter can be optional.

Also, I want to be able to test different palettes so I need some top-level variables to drive the `palette_generator()` function. These variables should be set to some reasonable values by default. I need:

*   `palette_type` (string, default "grid")
*   `palette_columns` (integer, default 16)
*   `palette_rows` (integer, default 16)
*   `palette_scaled` (boolean, default true)
*   `cylinder_radius` (real, default 100)

Finally, I need to call the `palette_generator()` function with my default values for the arguments. I want to make `pixel_size` optional and if it is not supplied then use the `default_pixel`.

Essentially, I want to consolidate the palette-generating functions into one module for easier access and a simpler interface while still using the `colors.scad` library, please.

I'm using the `colors.scad` library in this project.

[Assistant]
colors.scad:
```
/* [Demo] */
//Which Demo?
demo = "wheel"; //[wheel:Color Wheel, grid:Color Palette, cylinder:Color Cylinder, objDemo:Object Demo]
//Use color scaling?
ColorScaling = true; //[true:Use Color Scaling, false:Do Not Use Color Scaling]
//Segments to divide color wheel into
WheelSegments = 144; //[2:255]
// Iterations
WheelIterations = 144; // [1:255]
//Palette Columns
PaletteColumns = 144; //[1:255]
//Palette Rows
PaletteRows = 144; //[1:255]
//Demo Rows
DemoRows = 10; //[2:50]
//Demo Columns
DemoColumns = 5; //[2:50]

thingiverse();

/*
Rainbow Color Library
=====================
Aaron Ciuffo (Txoof)
Reach me at google mail: aaron dot ciuffo
https://www.thingiverse.com/txoof/about
released under GPL V3.0

Library to produce 2D array of RGB values scaled across an abitrary size

To use this library:
  * Move the library into your path
    eg: /Users/myHomeDirectory/OpenSCAD/libraries/
  * Add the following line to your OpenSCAD project:
    include </Users/myHomeDirectory/OpenSCAD/libraries/colors.scad>
  * Begin using the functions:
    myColorArray = colorArray(10, 10, scaled = true);

  * Functions are documented at each function
  * Examples can be found below
*/
// uncomment the following examples to test
//wheel(144, 90, true);
//grid(255, 255, true);

//echo(chord(10, 30));
//echo(RGB(30));
//echo(colorArray(11, 15));

/*
echo(RGBScale(55, 1));
echo(RGBScale(55, 0.5));
echo(RGBScale(55, 0));
*/

/*
myArray = colorArray(3, 3, scaled = false);
for (row=[0:len(myArray)-1]) { //three iterations
  echo(myArray[row]);
}
*/

//help_color();

module help_color(modName = false) {
  //edit content below this line
  modules =
            [["colorArray",
              "function: colorAray(columns = <integer>, rows = <integer, scaled = <boolean>)",
              "returns: <vector of vectors>",
              "Description: returns column X rows vector of interpolated RGB values",
              "Paramaters:",
              "     columns      <integer>   1-255 columns",
              "     rows         <integer>   1-255 rows",
	      "     scaled       <boolean>   scale the colors from white to full color"],

             ["chord",
              "function: chord(r = <real>, angle = <real>)",
              "returns: <real>",
              "Description: returns a chord length given a radius and angle",
              "     r           <real>      radius of circle",
              "     angle       <real>      0 < angle < 180 in degrees"],
             ["RGB",
              "function: RGB(angle = <real>)",
              "returns: <RGB vector>",
              "Description: returns vector of RGB around color wheel relative to angle",
              "Paramaters: ",
              "     angle     <real>         0 <= angle <= 360"],
             ["RGBScale",
              "function: RGBScale(angle = <real>, r = <real>",
              "returns: <RGB vector>",
              "Description: returns vector of RGB around color wheel relative to angle scaled with distance from center 0 = [255, 255, 255]",
              "Paramaters: ",
              "     angle    <real>         0 <= angle <= 360",
              "     r        <real>         0 <= radius <= 1"],
             ["red",
              "function: red(angle = <real>)",
              "returns: <real>",
              "Description: returns red value along a color wheel relative to angle",
              "Paramaters: ",
              "   angle   <real>          0 <= angle <= 360"],
              ["redScaled",
               "function: red(angle = <real>, r = <real>)",
               "returns: <real>",
               "Description: returns red value along a color wheel relative to angle scaled with distance from center 0 = [255, 255, 255]",
               "Paramaters: ",
               "    angle   <real>          0 <= angle <= 360",
               "    r       <real>          0 <= radius <= 1"],
              ["green",
               "function: red(angle = <real>)",
               "returns: <real>",
               "Description: returns greenvalue along a color wheel relative to angle",
               "Paramaters: ",
               "     angle   <real>          0 <= angle <= 360"],
              ["greenScaled",
               "function: red(angle = <real>, r = <real>)",
               "returns: <real>",
               "Description: returns green value along a color wheel relative to angle scaled with distance from center 0 = [255, 255, 255]",
               "Paramaters: ",
               "    angle   <real>          0 <= angle <= 360",
               "    r       <real>          0 <= radius <= 1"
              ],
              ["blue",
               "function: red(angle = <real>)",
               "returns: <real>",
               "Description: returns green value along a color wheel relative to angle",
               "Paramaters: ",
               "     angle   <real>          0 <= angle <= 360"],
              ["blueScaled",
               "function: red(angle = <real>, r = <real>)",
               "returns: <real>",
               "Description: returns blue value along a color wheel relative to angle scaled with distance from center 0 = [255, 255, 255]",
               "Paramaters: ",
               "    angle   <real>          0 <= angle <= 360",
               "    r       <real>          0 <= radius <= 1"],
              ["wCos",
               "function: wCos(angle = <real>, shift = <real>)",
               "returns: <real>",
               "Description: returns cos(angle) shifted out of phase by shift degrees - used to calculate colors around a color wheel",
               "Pramaters: ",
               "    angle   <real>          0 <= angle <= 360",
               "    shift   <real>          0 <= angle <= 360"],
              ["grid",
               "module: grid(x = <integer>, y = <integer>, scaled = <boolean>, pixel = [x, y, z])",
               "returns: none (module)",
               "Description: draws a grid of RGB pixels; pixles can be scaled from [255, 255, 255]",
               "Paramaters: ",
               "    x       <integer>       number of columns",
               "    y       <integer>       number of rows",
               "    scaled  <boolean>       scale the colors from white to full color",
               "    pixel   <vector>        [x, y, z] size of pixels in grid"],
              ["wheel",
               "module: wheel(segments = <integer>, rings = <integer>, scaled = <boolean>, pixel = [x, y, z])",
               "returns: none (module)",
               "Description: draws a color wheel of pixels; pixels can be scaled from [255, 255, 255]",
               "Paramaters: ",
               "    segments  <integer>     number of segments to divide wheel into",
               "    rings     <integer>     number of rings",
               "    scaled    <boolean>     scale the colors from white to full color",
               "    pixel     <vector>      [x, y, z] size of base pixel"],
              ["demoCyl",
               "module: demoCyl(segments = <integer>, layers = <integer>, r = <real>, scaled = <boolean>",
               "returns: none (module)",
               "Description: draws a grid wrapped into a cylinder",
               "Paramaters: ",
               "    segments  <integer>     number of segments to divide cylinder into",
               "    layers    <integer>     number of layers in cylinder",
               "    r         <real>        radius of cylinder",
               "    scaled    <boolean>     scale colors from white to full color"]
             ];
  //End editable content
  //DO NOT EDIT BELOW THIS POINT

  //convert string into a vector to make search work properly
  modVect = [modName];
  //use the vectorized string to search the modules vector
  index = search(modVect, modules)[0];

  //chcek if a name was passed
  if (modName==false || len(modules[index])==undef) {
      if (len(modules[index])==undef && modName != false) {
        echo(str("*****Module: ", modName, " not found*****"));
        echo("");
      }

      echo("Available Help Topics in this Library:");
      for (i=[0:len(modules)-1]) {
        echo(modules[i][0]);
      }
      echo("USE: help_colors(\"moduleName \")  ");
      //assert(modName);
    } else {
      //return the first matching entry
      //-possibly modify this to return all entries - allows partial match

      echo(str("Help for module/function: ", modName));
      //basic = modules[index[0]][1];
      //echo(basic);
      for (text=[1:len(modules[index])-1]) {
        echo(modules[index][text]);
      }
    }
}

module thingiverse() {
  if (demo == "wheel") {
    wheel(WheelSegments, WheelIterations, ColorScaling);
  }

  if (demo == "grid") {
    grid(PaletteColumns, PaletteRows, ColorScaling, pixel = [20, 20, 20]);
  }

  if (demo == "objDemo") {
    objDemo(DemoRows, DemoColumns, scaled = ColorScaling);
  }

  if (demo == "cylinder") {
    demoCyl(segments = WheelSegments, layers = WheelIterations, scaled = ColorScaling);
    // preview[view:south west, tçilt:bottom]
  }

}

/*
function wCos (wheel cosine)
  calculates the phase shifted cos function with a period of 8Pi/3 (cos(t*3/4))/0.7

  accepts:
    * angle (real): angle for which to calculate a the cos function (in degrees)
    * shift (real): angle by which to advance/retard the phase (in degrees)

  returns:
    * positive real number between 1.5, 0
*/
function wCos(angle = 0, shift = 0) = abs(cos((angle*3/4)-shift))/0.7;

/*
function chord(r = 1, angle = 30)
  calculates chord length given a radius and angle

  accepts:
    * r (real): radius of circle
    * angle (real): angle of segement
  returns:
    * length of chord (real)

  example:
    * echo(chord(r = 10, angle = 60));
    * ECHO: 5.17638

*/
function chord(r = 1, angle = 30) = 2*(r*sin(angle/2));

/*
function red
  provides red values for a color wheel with a radius of 1 radian where:
    red increases from 0 to 1 over interval 4Pi/3 to 5Pi/3
    red is 1 over interval  5Pi/3 to Pi/3
    red decreases from 1 to 0 over interval Pi/3 to 2Pi/3
    phase shift = 0 and 270

  accepts:
    * angle (real)
  returns:
    * positive real between 0 and 1
*/
function red(angle = 0) =
  angle <= 120  ?
    ((wCos(angle, 0) >= 1) ? 1 : wCos(angle, 0))
  : ((angle >=240) ? (wCos(angle, 270) >= 1 ? 1 : wCos(angle, 270))
  : 0);

/*
function redScale
  Scales red values between 5Pi/3 to Pi/3 for a color wheel as a percentage
  of 1 radian

  accepts:
    * angle (real)
    * r (real): radians
  returns:
    * positive real between 0 and 1
*/
function redScale(angle = 0, r = 1) =
  ((angle >= 0 && angle <= 60) || (angle >= 300 && angle <= 360)) ?
    red(angle) : red(angle) + ((1 - red(angle)) * (1 - r));

/*
function green
  provides green values for a color wheel with a radius of 1 radian where:
    green increases from 0 to 1 over interval 0 to Pi/3
    green is 1 over interval Pi/3 to Pi
    green decreases from 1 to 0 over interval Pi to 4Pi/3
    phase shift = 90

  accepts:
    * angle (real)
  returns:
    * positive real between 0 and 1
*/
function green(angle = 0) =
  angle <= 240 ?
    ((wCos(angle, 90) >= 1) ? 1 : wCos(angle, 90)) : 0;

/*
function greenScale
  Scales green values between Pi/3 to 3Pi/3 for a color wheel as a percentage
  of 1 radian

  accepts:
    * angle (real)
    * r (real): radians
  returns:
    * positive real between 0 and 1
*/
function greenScale(angle = 0, r = 1) =
  (angle >= 60 && angle <= 180) ?
    green(angle) : green(angle) + ((1 - green(angle)) * (1 - r));

/*
function blue
  provides green values for a color wheel with a radius of  radian where:
    blue increases from 0 to 1 over interval 2Pi/3 to Pi
    blue is 1 over interval Pi to 5Pi/3
    blue decreases from 1 to 0 over interval 5Pi/3 to 0
  accepts:
    * angle (real)
  returns:
    * positve real between 0 and 1
*/
function blue(angle = 0) =
  angle >= 120 ?
    ((wCos(angle, 180) >= 1) ? 1 : wCos(angle, 180)) : 0;

/*
function blueScale
  Scales blue values between 3Pi/3 to 5Pi/3 for a color wheel as a percentage
  of 1 radian

  accepts:
    * angle (real)
    * r (real): radians
  returns:
    * positive real between 0 and 1
*/
function blueScale(angle = 0, r = 1) =
  (angle >= 180 && angle <= 300) ?
    blue(angle) : blue(angle) + ((1 - blue(angle)) * (1 - r));

/*
function RGB(angle = 30)
  returns RGB value as 1D array of reals value between 0 an 1.

  accepts:
    * angle (real)
  returns:
    * RGB (array of real)

  example:
    * echo(RGB(30))
    * ECHO: [1, 0.546691, 0]

*/
function RGB(angle = 0) = [red(angle), green(angle), blue(angle)];

/*
funciton RGBScale
  returns RGB value as 1D array of reals scaled by distance from origin (0-1)

  accepts:
    * angle (real)
    * r (real): radius between 0-1
  returns:
    * RGB (array of real)

  example:
    * echo(RGBScale(55, 1));
    * ECHO: [1, 0.941923, 0]
    * echo(RGBScale(55, 0.5));
    * ECHO: [1, 0.970961, 0.5]
    * echo(RGBScale(55, 0));
    * ECHO: [1, 1, 1]
*/
function RGBScale(angle = 0, r = 1) =
  [redScale(angle, r), greenScale(angle, r), blueScale(angle, r)];

/*
function colorArray(columns = 10, rows = 10, scaled = false)
  create an X by Y array of RGB values

  accepts:
    * columns (integer): divisions of color wheel
    * rows (integer): number of itterations from origin
    * scaled (boolean): when true, return a value scaled by
      distance from origin
  returns:
    * X by Y array of RGB values

  example:
    * myArray = colorArray(3, 3, scaled = false);
      for (row=[0:len(myArray)-1]) { //five itterations
        echo(colorArray[row]);
      }

    * ECHO: [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
    * ECHO: [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
    * ECHO: [[0, 0, 1], [0, 0, 1], [0, 0, 1]]

*/
function colorArray(columns = 10, rows = 10, scaled = false) =
  scaled ?
    [
      for(i = [0:360/columns:359.999])
        [for (j = [rows:-1:1]) RGBScale(i, (j)/rows)]
    ] :
    [
      for(i = [0:360/columns:359.999])
        [for(j = [1:rows])  RGB(i)]
    ];

/*
module grid(x = 10, y = 10, scaled = true, pixel = [10, 10, 10]
  draws a color grid

  accepts:
    * x (integer): number of rows
    * y (integer): number of columns
    * scaled (boolean): true - use scaling
    * pixel ([l, w, h]): 1D array of length, width, height
*/
module grid(x = 10, y = 10, scaled = true, pixel = [10, 10, 10]) {
  myColor = colorArray(x, y, scaled);
  translate([(-(pixel[0])*x)/2, (-(pixel[1])*y)/2, 0]) {
    for (i = [0:x-1]) {
      for (j = [0:y-1]) {
        translate([pixel[0]*1*i, pixel[1]*1*j, 0])
        color(myColor[i][j])
        cube(pixel, center = true);
      }
    }
  }
}

/*
module wheel(segments = 72, rings = 72, pixel = [10, 10, 10]
  draws a color wheel

  accepts:
    * segments (integer): number of segments to divide the wheel into
    * rings (integer): number of itterations
    * scaled (boolean): true - use color scaling
    * pixel (1D array): [x dimension, y dimension, z dimension] of elements
*/
module wheel(segments = 72, rings = 72, scaled = true, pixel = [10, 10, 10]) {
  angle = 360/segments;
  for (i = [0 : 360/segments : 359.99999]) {
    segnum = i/360*segments;
    //echo(str("segnum: ", segnum, " angle: ", i, " RGB: ", [red(i ), green(i), blue(i)]));
    for (j = [1 : rings-0 ]) {
      if (scaled) {
        rotate([0, 0, i])
          translate([j*pixel[2], 0, 0])
          color(RGBScale(i, (j-1)/rings))
          //color(RGB(i))
          cube([pixel[0], chord(r = pixel[1]*(j+1), angle = angle),
                pixel[2]], center = true);
      } else {
         rotate([0, 0, i])
          translate([j*pixel[2], 0, 0])
          //color(RGBScale(i, (j-1)/rings))
          color(RGB(i))
          cube([pixel[0], chord(r = pixel[1]*(j+1), angle = angle),
                pixel[2]], center = true);

      }
    }

  }
}

/*
module objDemo
  draws an array of objects using the colorArray() function to provide colors

  accepts:
    * rows (integer): number of rows of objects
    * columns (integer): number of columns of objects
    * size (real): radius of objects
    * minQual (integer): minimum quality value to use
    * step (integer): steps to increase quality by
    * scaled (boolean): when true, use color scaling
*/
module objDemo(rows = 15, columns = 5, size = 150, minQual = 3,
                step = 1, scaled = true) {
  sep = 1; //seperation of elements
  //create an array of RGB values that matches the rows and columns
  myColors = colorArray(rows+1, columns+1, scaled = scaled);

  // loop through the rows
  for (i = [1:rows]) {
    // loop through the columns
    for (j = [1:columns]) {
      // make some interesting shapes by adjusting the quality variable
      $fn = ((minQual-1)+(i*step)) >= 36 ? 36 : (minQual-1)+(i*step);

      // pull the next color from the color array
      color(myColors[i-1][j-1])
        translate([size+(size*2+size*sep)*(i-1),
                  size+(size*2+size*sep)*(j-1), 0])
        // draw a sphere with the quality set above
        sphere(size, $fn);
    }
  }

}

/*
module demoCyl
  draw a hollow cylinder using the colorArray() function

  accepts:
    * segments (integer): number of segments to divde cylinder into
    * layers (integer): number of layers
    * r (real): radius of cylinder
    * scaled (boolean): when true use color scaling
*/

module demoCyl(segments = 12, layers = 10, r = 1000, scaled = true) {

  angle = 360/segments;
  cuUnit = chord(r = r, angle = 360/segments);

  // set the color array equal to the number of segments and layers
  myColor = colorArray(segments, layers, scaled = scaled);
  translate([0, 0, -(layers*cuUnit)/2]) {
    for (j=[0:layers-1]) {
      for (i=[0:segments-1]) {
          // draw a cube and rotate to make each segment of each layer
          // pull a color from the array
          color(myColor[i][j])
            rotate([0, 0, angle*i])
            translate([r, 0, j*cuUnit])
            cube(cuUnit, center = true);
      }
    }
  }
}

```


[User]
Hey, I'm looking for some help designing a parametric tube separator in OpenSCAD. I saw a design that was close to what I need but I’d like to have it made from scratch.

Basically, I need a design that will hold a number of parallel tubes (or cables, etc.) of a specified diameter, separated by a defined distance. Each tube will sit in a clip that is partially enclosed - not a complete circle, but more like a C-shape, allowing them to be easily inserted and removed but held securely.

Here's what I'm thinking for parameters:

*   **tubeCount:** The number of tubes to hold.
*   **tubeDiameter:** The diameter of the tubes.
*   **tubeSeparation:** The distance between the center of each tube.
*   **clipThickness:** The thickness of the material used for the clips.
*  **enclosedAngle**: The angular amount the clip is open, this should be specified in degrees.
*   **height:** The overall height of the separator, a constant value.

The base of the design should be relatively flat, for easy printing, and the C-shaped clips should smoothly transition into a common base, perhaps with some ribbing between clips to make it more sturdy. I would really like to have a rib that runs along the back of the clips to help with stability. The C-shape of the clip should be created using a Minkowski sum and rotation extrusion.

Could you generate an OpenSCAD file for this design? I'd really appreciate it!

[Assistant]
TubeSeparator.scad:
```

//Number of tubes to hold
tubeCount = 3;
//Diameter of tubes it will hold
tubeDiameter = 4.7625;
//Distance between each tube
tubeSeparation = 19;
//Wall thickness used for the clips
clipThickness = 2;
//Determines how easy it is to insert or remove the cable.  190-330 are pragmatic limits
EnclosedAngle = 260;
$fs=.5;
$fa=20;

//NOTE:Multiplying by 1 prevents values from being editable in Thingiverse customizer.
$in=25.4*1;
$demoMode = 0*1;
//Originally for 3/16 brake tube, parametric for any parrallel cables or tubes
if ($demoMode>0){
    vals=[
            [tubeCount, tubeDiameter, tubeSeparation, clipThickness],
            [2, 3/16*$in, 1.5*$in, 1.6],
            [2, 3/16*$in, 2*$in],
            [3, .19*$in, .5*$in],
            [4, 5/16*$in, 3/4*$in],
            [4, 5/16*$in, 3/4*$in],
            [4, 7/16*$in, 3/4*$in]
            ];
    for(i=[0:len(vals)-1])
        translate([0,30*i,0])
            TubeSeparator(vals[i][0], vals[i][1], vals[i][2], len(vals[i]>3)?vals[i][3]:2);
} else
    TubeSeparator(tubeCount, tubeDiameter, tubeSeparation, clipThickness);

module TubeSeparator(tubes, diameter, separation, wallThickness=2){
    height=6;
    intersection(){
        //NOTE: rounded with bit of flatness on base for printing without supports
        translate([-separation,-diameter,-.75] )
            cube([(separation+diameter)*tubes, diameter*2, height]);
        union(){
            //Clips
            for(i=[0:tubes-1])
                translate([separation*i,0,0])
                    TubeClip(diameter, height, wallThickness);
            //Center rib
            for(i=[1:tubes-1])
                translate([ (i-.5)*separation,0,height/2-.75])
                    cube([separation-diameter-wallThickness,wallThickness,height], center=true);

            //Back rib
            translate([0,-diameter/2-wallThickness,-.75])
                translate([0,1,wallThickness/2-.25])
                minkowski(){
                    cube([separation*(tubes-1), wallThickness<=2 ? .001: wallThickness-2, height-1.75]);
                    sphere(1);
                }
        }
    }

}

module TubeClip(diameter, height, wallThickness){
    rotate([0,0,(360-EnclosedAngle)/2 + 90])
        minkowski() {
            rotate_extrude(angle=EnclosedAngle, convexity=5)
                translate([diameter/2+ wallThickness/2,0,-.25])
                    square([.01, height-wallThickness+.25]);
            sphere(wallThickness/2);
        }
}
```


[User]
I'd like to generate some rock-like shapes using OpenSCAD, and I'm thinking of using a system where a base sphere is deformed to get that look. I have some parameters in mind, and I'd like to be able to control the overall size of the rock, how much the base sphere is deformed, and also the skew or stretching of the rock. I also think I want to control how detailed the shape is with the "resolution" parameter, and then the size of the small spheres used to make the base shape with "finest_point".

Specifically, I envision starting with a base sphere, then essentially applying a "noise" field to move its surface points around, giving the rock its bumpy texture. Then also I want to be able to scale and skew the base sphere a bit to make more varied shapes. I'd want to control the overall size, the scale of the noise deformations (how much points move), the "seed" for those deformation movements to get the same shape each time, a scale/skew for the overall shape, and a seed for that as well.

I think it would be cool to be able to adjust the resolution of the points the deformation is applied to as well, making the surface smoother or bumpier, and the base sphere size for each point on the "hull." I expect it will be a hull of spheres which is deformed in a complex way.

So, the parameters I would want to be able to customize are:

*   `size`: The overall size of the rock.
*   `deformation_scale`: How much the surface points are moved randomly.
*   `deform_seed`: Seed value to control the randomness of the surface point movements (for consistent shape generation).
*   `skew_scale`: The scale factor to skew/stretch the overall shape.
*   `skew_seed`: The random seed for the overall skew/stretch of the shape.
*   `resolution`: The number of points used to make the sphere to deform.
*    `finest_point`: The radius of the small spheres used in the hull generation.

I'd really appreciate it if you could write an OpenSCAD file (or files, if it makes sense) to do this. Let me know if you need any clarification. Thanks!

[Assistant]
asteroid.scad:
```
size = 150;
deformation_scale = 50;
deform_seed = 42;

skew_scale = .5;
skew_seed = 51;

resolution = 20;
finest_point = 6;

// Hidden
$fn = 4;

hull(){
  translate([.1*size, 0, 0]) {
    random_skew=rands(0, skew_scale, 4, skew_seed);
    echo ("random_skew", random_skew);
    scale([ 1 + random_skew[0], 1 + random_skew[1], 1 + random_skew[2]]) {
      for (ii=[0:resolution:360]) {
        for (i=[0:resolution:170]) {
          translate([size * sin(ii),  size * cos(ii) * cos(i), size * cos(ii) * sin(i)]) {
            random_offset=rands(0, deformation_scale, 4, deform_seed + i + ii);

            translate([ random_offset[0], random_offset[1], random_offset[2]]) {
              sphere(finest_point);
            }
          }
        }
      }
    }
  }
}

```


[User]
I'd like a parametric OpenSCAD design for a Raspberry Pi case, specifically for the B+ model but with some features I'd like to make configurable. I'd like to be able to specify if the case should have openings for a ribbon camera cable, a Unicorn HAT (an 8x8 LED matrix), an SD card, LEDs on the Pi board itself, and the 40-pin GPIO header. I'd also like to control whether the GPIO header opening is fully open, or just has a basic rectangular cut-out for a ribbon cable or similar.

The design should be in two parts - a top and a bottom - that snap together. I need to be able to adjust the overall height of the top piece, which will affect the internal clearance. It should also have mounting holes for the Pi board itself. The design should be based on the dimensions of the B+ model, but I'm happy for the placement of the external openings to be parameterized so that I could adjust the position and size of those holes. I would like to have control over the wall thickness and the radius of the corners.

The design should also have standoffs for the Raspberry Pi PCB, and I would like the ability to disable them so I could use some other standoffs, if necessary. The standoffs should be designed in a way to have a countersunk screw head so I could use flat head screws. I'd like there to be a way for the standoffs to have a "break away" peg for additional stability if the user chooses to leave it. Also the snap fit should be adjustable in size and length.

I need the design to use the built-in OpenSCAD libraries.

Basically, I want a highly customizable case, where I can toggle and adjust a bunch of features.

[Assistant]
case.scad:
```
//
// raspberry pi model B+
//
// design by Egil Kvaleberg, 8 Aug 2015
// Rasoberrypi 3 adapt by Guy Sheffer 28 Apr 2016
//
// drawing of the B+ model, for referrence:
// https://www.raspberrypi.org/documentation/hardware/raspberrypi/mechanical/Raspberry-Pi-B-Plus-V1.2-Mechanical-Drawing.pdf
//
// notes:
// design origin is middle of PCB
//

part = "all"; // [ top, bottom, all, demo ]

have_plugrow = 0; // [ 1:true, 0:false ]
have_open_plugrow = 0; // [ 1:true, 0:false ]
have_camera = 0; // [ 1:true, 0:false ]
have_unicorn = 1; // [ 1:true, 0:false ]
have_sdcard_support = 1; // [ 1:true, 0:false ]
have_leds = 0; // [ 1:true, 0:false ]

/* [Hidden] */
mil2mm = 0.0254;

pcb = [85.0, 56.0, 1.5]; // main board
pcb2floor = 4.0; // 3.5
pcb2roof = 17.0; // 15.7

pcbmntdia = 2.75; // mounting holes
pcbmnt1dx = pcb[0]/2 - 3.5;  //
pcbmnt1dy = 3.5 - pcb[1]/2;
pcbmnt2dx = pcb[0]/2 - 3.5 - 58.0; //
pcbmnt2dy = pcb[1]/2 - 3.5;
pcbmnthead = 6.2; // countersunk
pcbmntthreads = 2.2;
breakaway = 0.3; // have hidden hole for screw, 0 for no extra pegs

cardsy = 12.0; // card measures 11.0
cardsz = 3.1;
cardsx = 8.0; // size of internal platform
carddy = pcb[1]/2 - 28.0;
ethersy = 16.0; // ethernet contact width
ethersz = 13.5 + 0.5;
etherdy = pcb[1]/2 - 10.25;
usbsy = 13.5; // core
usbframe = 1.0; // frame
usbsz = 15.8 + 0.5;
usb1dy = pcb[1]/2 - 29.0;
usb2dy = pcb[1]/2 - 47.0;
powerpsx = 11.0; // power plug width
powerpsz = 4.5; // plug height
powerssx = 8.0; // power socket width
powerssz = 3.3; // socket height
powerdz = -1.7; // for plug
powerdx = pcb[0]/2 - 10.6; //
hdmisx = 15.2; // hdmi contact width
hdmisz = 6.2;
hdmipsx = 25.0; // typical plug
hdmipsz = 12.0;
hdmidx = pcb[0]/2 - 32.0;
audior = 7.0; // audio contact radius
audiodz = 6.0/2; // above pcb
audiodx = pcb[0]/2 -53.5;
leddx = pcb[0]/2 - 1.3;
led1dy = -8.9 + 2.4/2  + pcb[1]/2;
led2dy = -12.9 + 2.4/2 + pcb[1]/2 - 0.2;
leddia = 2.0;
// BUG: fine tune, maybe add slot out of box
plugrow1 = [pcb[0]/2 - 29.0 - mil2mm*100*11, 3.5 - pcb[1]/2, 0]; // coordinates D7 pin, mid

cam_box = 40;
unicorn_box = 51;

frame_w = 2.5; // width of lip for frame
snap_dia = 1.8; // snap lock ridge diameter
snap_len = 50.0; // snap lock length
tol = 0.5; // general tolerance

wall = 1.2; // general wall thickness
thinwall = 0.4;
corner_r = wall; // casing corner radius
corner2_r = wall+tol+wall; // corners of top casing
d = 0.01;

extra_y = 0; // extra space in y

module nut(af, h) { // af is 2*r
	cylinder(r=af/2/cos(30), h=h, $fn=6);
}

module c_cube(x, y, z) {
	translate([-x/2, -y/2, 0]) cube([x, y, z]);
}

module cr_cube(x, y, z, r) {
	hull() {
		for (dx=[-1,1]) for (dy=[-1,1]) translate([dx*(x/2-r), dy*(y/2-r), 0]) cylinder(r=r, h=z, $fn=20);
	}
}

module cr2_cube(x, y, z, r1, r2) {
	hull() {
		for (dx=[-1,1]) for (dy=[-1,1]) translate([dx*(x/2-r1), dy*(y/2-r1), 0]) cylinder(r1=r1, r2=r2, h=z, $fn=20);
	}
}

module bottom() {
	module snap2(ex, both) {
		if (both) translate([pcb[0]/2+tol+wall+ex, -snap_len/2, wall+pcb2floor+pcb[2]-frame_w/2]) rotate([-90, 0, 0]) cylinder(r=snap_dia/2, h=snap_len, $fs=0.3);
		if (both) translate([-snap_len/2, pcb[1]/2+tol+wall, wall+pcb2floor+pcb[2]-frame_w/2]) rotate([0, 90, 0]) cylinder(r=snap_dia/2, h=snap_len, $fs=0.3);
	}
    module plugs(extra) {
        z0 = wall+pcb2floor+pcb[2];
        // card slot
        translate([pcb[0]/2 - cardsx/2, carddy, wall+pcb2floor+pcb[2]-cardsz-extra])
			c_cube(cardsx + 19.9, cardsy+2*extra, cardsz+2*extra);
        // power plug (near side)
        translate([powerdx, pcb[1]/2+2.1*wall, z0+powerdz-extra])
			c_cube(powerpsx+2*extra, 2*wall, powerpsz+2*extra+frame_w);
        // hdmi socket (near side)
        translate([hdmidx, pcb[1]/2+19.9/2-8.25/2, -extra-frame_w])
			c_cube(hdmipsx+2*extra, 8.25, hdmipsz+2*extra+frame_w);
    }
    module plugs_add() {
        z0 = wall+pcb2floor+pcb[2];
        // audio plug (near side)
        difference() /*/ color("green")*/ {
            translate([audiodx, pcb[1]/2 + 1.4*wall, z0-snap_dia/2-tol])
                c_cube(audior, 1.6*wall, audiodz+snap_dia);
            translate([audiodx, pcb[1]/2 + wall/2 - d, z0+audiodz])
                rotate([-90, 0, 0]) cylinder(r=audior/2+tol, h=1.8*wall+2*d, $fn=20);
        }
        // card slot
        if (have_sdcard_support) difference () {
            translate([pcb[0]/2 + tol + d - cardsx/2, carddy, 0])
                c_cube(cardsx, cardsy+2*tol+2*wall, wall+frame_w-tol);
            plugs(tol);
        }
    }

	module add() {
		hull () for (x = [-1, 1]) for (y = [-1, 1])
			translate([x*(pcb[0]/2+tol+wall-corner_r) + (x>0 ? extra_y : 0), y*(pcb[1]/2+tol+wall-corner_r), corner_r]) {
				sphere(r = corner_r, $fs=0.3);
				cylinder(r = corner_r, h = wall+pcb2floor+pcb[2]-corner_r, $fs=0.3);
		}
		snap2(0, true);
        rotate([0, 0, 180]) snap2(0, false);
	}
	module sub() {
        module pedestal(dx, dy, hg, dia) {
            translate([dx, dy, wall]) {
				cylinder(r = dia/2+wall, h = hg, $fs=0.2);
                // pegs through pcb mount holes
                if (breakaway > 0) translate([0, 0, hg])
                        cylinder(r = dia/2 - tol, h = pcb[2]+d, $fs=0.2);
            }
        }
        module pedestal_hole(dx, dy, hg, dia) {
            translate([dx, dy, breakaway]) {
				cylinder(r = dia/2, h = wall+hg-2*breakaway, $fs=0.2);
				cylinder(r = 1.0/2, h = wall+hg+pcb[2]+d, $fs=0.2); // needed to 'expose' internal structure so it dows not get removed
				cylinder(r1 = pcbmnthead/2 - breakaway, r2 = 0, h = pcbmnthead/2 - breakaway, $fs=0.2); // countersunk head
		    }
        }
		difference () {
			// pcb itself
			translate([-(pcb[0]/2+tol), -(pcb[1]/2+tol), wall])
				cube([2*tol+pcb[0], 2*tol+pcb[1], pcb2floor+pcb[2]+d]);
			// less pcb mount pedestals
            for (dx = [pcbmnt1dx,pcbmnt2dx]) for (dy = [pcbmnt1dy,pcbmnt2dy])
                pedestal(dx, dy, pcb2floor, pcbmntdia);
		}
		// hole for countersunk pcb mounting screws, hidden (can be broken away)
        for (dx = [pcbmnt1dx,pcbmnt2dx]) for (dy = [pcbmnt1dy,pcbmnt2dy])
            pedestal_hole(dx, dy, pcb2floor, pcbmntdia);
        plugs(tol);
	}
	difference () {
		add();
		sub();
	}
    plugs_add();

    //color("red") plugs(0);
}

// Z base is top of pcb
module top() {
	module snap2(ex, both) {
		if (both) translate([pcb[0]/2+tol+wall+ex, -snap_len/2-tol, -frame_w/2]) rotate([-90, 0, 0]) cylinder(r=snap_dia/2, h=snap_len+2*tol, $fs=0.3);
		translate([-snap_len/2-tol, pcb[1]/2+tol+wall, -frame_w/2]) rotate([0, 90, 0]) cylinder(r=snap_dia/2, h=snap_len+2*tol, $fs=0.3);
	}
    module plugs(extra) {
        module usb_plug(dy) {
            translate([-pcb[0]/2, dy, -extra-frame_w])
				c_cube(19.9, usbsy+2*extra, usbsz+2*extra+frame_w);
            translate([-pcb[0]/2 -19.9/2, dy, -extra-frame_w])
				c_cube(19.9, usbsy+2*usbframe+2*extra, usbsz+2*extra+frame_w+2*usbframe/2);
        }
        // card slot
        translate([pcb[0]/2, carddy, -cardsz-extra - 1.2]) // fudge
			c_cube(19.9, cardsy+2*extra, cardsz+2*extra);
        // power socket (near side)
        translate([powerdx, pcb[1]/2, -extra-frame_w])
			c_cube(powerssx+2*extra, 19.9, powerssz+2*extra+frame_w);
        // room for power plug
        translate([powerdx, pcb[1]/2+9.9/2+wall*1.4, -extra-frame_w])
			c_cube(powerpsx+2*extra, 9.9, powerpsz+2*extra+frame_w);
       // ether plug
        translate([-pcb[0]/2, etherdy, -extra-frame_w])
			c_cube(19.9, ethersy+2*extra, ethersz+2*extra+frame_w); // use half tol horizontally, seems to be enough
       // usb plugs
       usb_plug(usb1dy);
       usb_plug(usb2dy);
        // hdmi socket (near side)
        translate([hdmidx, pcb[1]/2, -extra-frame_w])
			c_cube(hdmisx+2*extra, 19.9, hdmisz+2*extra+frame_w);
        translate([hdmidx, pcb[1]/2+19.9/2-7.8/2, -extra-frame_w])
			c_cube(hdmipsx+2*extra, 7.8, hdmipsz+2*extra+frame_w);
        // audio plug (near side)
        translate([audiodx, pcb[1]/2 + 19.9/2, audiodz])
			rotate([90, 0, 0]) cylinder(r=audior/2+extra, h=19.9, $fn=20);
        translate([audiodx, pcb[1]/2, -extra-frame_w])
			c_cube(audior+2*extra, 19.9, audiodz+2*extra+frame_w);
        // camera opening
        if (have_camera) translate([-8.0, 0, pcb2roof - extra])
			c_cube(cam_box, cam_box, wall+2*extra);
        // unicorn hat opening
        if (have_unicorn) translate([10, 0, pcb2roof - extra])
			c_cube(unicorn_box, unicorn_box, wall+2*extra);

    }

	module add() {
		hull () for (x = [-1, 1]) for (y = [-1, 1]) {
			translate([x*(pcb[0]/2+tol+wall-corner_r) + (x>0 ? extra_y : 0), y*(pcb[1]/2+tol+wall-corner_r), -frame_w])
				cylinder(r = corner_r+tol+wall, h = d, $fs=0.3); // include frame
			translate([x*(pcb[0]/2+tol+wall-corner2_r) + (x>0 ? extra_y : 0), y*(pcb[1]/2+tol+wall-corner2_r), pcb2roof+wall-corner2_r])
					sphere(r = corner2_r, $fs=0.3);
		}

	}

	module sub() {
		module plugrow_frame(xy, start, pins) {
			frmheight = 3.0;
            echo((pcb[1]/2+xy[1]));
			if (have_open_plugrow) translate([xy[0]+(start+(pins-1)/2)*2.56, xy[1]-(pcb[1]/2+xy[1]-2.56)/2, 1.0])
				c_cube(pins*2.56+2*tol+2*wall, (pcb[1]/2+xy[1]-2.56)+2*2.56+2*tol+2*wall, pcb2roof-1.0);
            else translate([xy[0]+(start+(pins-1)/2)*2.56, xy[1], frmheight])
				c_cube(pins*2.56+2*tol+2*wall, 2*2.56+2*tol+2*wall, pcb2roof-frmheight);

       }
		module plugrow_hole(xy, start, pins) {
            if (have_open_plugrow) translate([xy[0]+(start+(pins-1)/2)*2.56, xy[1]-9.9/2, -frame_w-d])
				c_cube(pins*2.56+2*tol, 2*2.56+2*tol+9.9, pcb2roof+wall+pcb[0]+frame_w);
			else translate([xy[0]+(start+(pins-1)/2)*2.56, xy[1], 0])
				c_cube(pins*2.56+2*tol, 2*2.56+2*tol, pcb2roof+wall);
        }
        module led_frame(dy) {
			frmheight = 2.0;
			translate([leddx, dy, frmheight])
				cylinder(r=leddia/2+wall, h=pcb2roof+d-frmheight, $fn=16);
        }
        module led_hole(dy) {
			translate([leddx, dy, 0])
				cylinder(r=leddia/2-d, h=pcb2roof+wall+d, $fn=16);

        }
		// room for bottom case within frame
		hull () for (x = [-1, 1]) for (y = [-1, 1])
			translate([x*(pcb[0]/2+tol+wall-corner_r) + (x>0 ? extra_y : 0), y*(pcb[1]/2+tol+wall-corner_r), -frame_w-d])
                cylinder(r = corner_r+tol, h = d+frame_w, $fs=0.3);
		// snap lock
		snap2(0, true);
		rotate([0, 0, 180]) snap2(0, false);
		difference() {
			// room for pcb
			translate([0, 0, -d]) cr_cube(2*tol+pcb[0], 2*tol+pcb[1], d+pcb2roof, 1.0);
			union () { // subtract from pcb:
				// plug rows
				if (have_plugrow || have_open_plugrow)
					plugrow_frame(plugrow1, 0, 20);
                // leds
				if (have_leds) {
					led_frame(led1dy);
                    led_frame(led2dy);
                }
                // strengthing frame around usb and ether
                translate([-pcb[0]/2, 0, tol])
				c_cube(19.9-2*d, pcb[1]+2*tol+2*d, usbsz+2*tol+frame_w);

                // pegs
                if (have_unicorn) {
                    translate([0, 0, 10]) {
                        for (dx = [pcbmnt1dx,pcbmnt2dx]) for (dy = [pcbmnt1dy,pcbmnt2dy]) translate([dx, dy, 0]) {
                            cylinder(r1 = pcbmntdia/2 +wall, r2 = pcbmntdia/2 +wall, h = pcb2roof-10, $fs=0.2);
                        }
                    }
                } else {
                     for (dx = [pcbmnt1dx,pcbmnt2dx]) for (dy = [pcbmnt1dy,pcbmnt2dy]) translate([dx, dy, 0]) {
                            cylinder(r1 = pcbmntdia/2 +wall, r2 = pcbmntdia/2 +2.5*wall, h = pcb2roof, $fs=0.2);
                        }
                }
			}

		}
        // hole for usb, ether and power
        plugs(tol);
		// plug rows
		if (have_plugrow || have_open_plugrow)
			plugrow_hole(plugrow1, 0, 20);
        // leds
		if (have_leds) {
			led_hole(led1dy);
            led_hole(led2dy);
        }
        // peg holes
        for (dx = [pcbmnt1dx,pcbmnt2dx]) for (dy = [pcbmnt1dy,pcbmnt2dy]) translate([dx, dy, 0]) {
            translate([0, 0, -d]) cylinder(r = pcbmntthreads/2, h = pcb2roof, $fs=0.2); // hole
        }
	}
	difference () {
		add();
		sub();
	}

    if (part == "demo")
        color("red") plugs(0);
}

//

if (part=="demo") { bottom(); translate([0, 0, wall+pcb2floor+pcb[2]]) top(); }
if (part=="bottom" || part=="all") translate([0, -35, 0]) bottom();
if (part=="top" || part=="all") translate([-0, 35, pcb2roof+tol+wall]) rotate([180,0,0]) top();

```


[User]
I need an OpenSCAD design for two interlocking, spiraling containers that slide into each other. They should be based on an ellipse that's extruded and twisted. I've got a basic description here that should get you started.

The outer container is created by taking an ellipse (defined by two side lengths, 'side1' and 'side2'), extruding it to a certain height, and twisting it by a specified number of degrees ('rotation') as it's extruded. Then, to create the space for the inner container, a slightly smaller, similarly twisted ellipse is subtracted from the solid. There is a base added to the outside container and the internal container as well. There's also a small gap or space, specified by a variable 'space', between the two containers, which needs to be adjustable, so they can slide smoothly into each other without friction (or with minimal friction.)

The inner container is generated in a very similar fashion as the outer one, but it’s translated from the origin and uses slightly different measurements. The inner container also has a twisted shape and base.

Here are the specific parameters that I'd like to be able to adjust:

*   **`$fn`**: The resolution of the circle used to make the ellipse (default to 100).
*   **`heightt`**: The height of the containers in millimeters (mm).
*   **`rotation`**: The total rotation in degrees as the shape is extruded, try 90, 180, 360.
*   **`thickness`**: The thickness of the walls of the container in mm.
*   **`side1`**: The length of the first side of the ellipse in mm.
*   **`side2`**: The length of the second side of the ellipse in mm.
*    **`base`**:  The base thickness in mm. This shouldn't need to be changed very often.
*   **`space`**: The empty space in mm between the two containers.

I’m hoping that I can easily adjust these variables to change the size and fit of the containers. This will let me experiment with a good fit. Let me know if you have questions.

[Assistant]
spizoelips.scad:
```
$fn=100;
heightt=40; //mm
rotation=90; //degree
thickness=0.5; //mm
side1=20; //elips side mm
side2=10; //elips side mm
base=2; // mm
space=0.8; //space between two shapes mm

// External shape
difference () {
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
resize([side1,side2])circle(d=20,center=true);

translate([0,0,base])
rotate([0,0,-1*rotation/heightt*base])
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
resize([side1-thickness,side2-thickness])circle(d=20,,center=true);
}

// Internal shape
translate([side2+side1,0,0])
difference () {
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
resize([side1-thickness-space,side2-thickness-space])circle(d=20,center=true);
translate([0,0,base])
rotate([0,0,-1*rotation/heightt*base])
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
resize([side1-thickness*2-space,side2-thickness*2-space])circle(d=20,center=true);
}
// Base
translate([side2+side1,0,0.5])
minkowski() {
linear_extrude(0.1, center = false)
resize([side1,side2])circle(d=20,center=true);
sphere (0.5);
}
```


[User]
Hey, I'm trying to design a parametric fidget spinner in OpenSCAD. I have some specific ideas in mind and would like your help creating the SCAD code for it.

Here's what I'm envisioning:

The spinner should be based around a central bearing and a variable number of outer bearings. The user should be able to specify:

*   **`bearingCount`:** The number of outer bearings (e.g., 1, 2, 3, etc.).
*   **`bearingRadius`:** The radius of the bearings being used.
*   **`bearingHeight`:** The height/thickness of the bearings.
*   **`bearingDistance`:** The distance from the center of the central bearing to the center of each outer bearing.
*  **`thickness`**: The amount of material between the bearings and the outer perimeter.
*   **`tolerance`:** A small value to add to the bearing diameter to make sure that there is a slightly loose fit for the bearings when printed.

I want the code to create a solid body by creating a hull for all the outer bearings and the center bearing using cylinders slightly larger than the bearing size, and then subtract a union of smaller cylinders representing the actual bearings, using the tolerance. So there should only be one solid part when it's all rendered. I guess I should also set `$fn=80` for smoother curves.

Could you generate the OpenSCAD file(s) for this? Thanks so much!

[Assistant]
Spinner.scad:
```
/* [Parameters] */
//Number of outer bearings
bearingCount = 3; //[1:8]
//Radius of the bearings being used
bearingRadius = 11;
//Height of the bearings being used
bearingHeight = 7;
//Distance from the center of the central bearing to the center of the outer bearings
bearingDistance = 40;
//Thickness between the bearing and the outer perimeters
thickness = 5;
//Tolerance compensation
tolerance = .3;

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

bearingDiameter = (bearingRadius*2) + tolerance;

difference() {
    for(i = [1:bearingCount]){
        hull() {
            rotate([0,0,(360/bearingCount)*i]) {
                translate([bearingDistance,0,0]) {
                    cylinder(bearingHeight, d = bearingDiameter + (thickness*2), true);
                }
            }
            cylinder(bearingHeight, d = bearingDiameter + (thickness*2), true);
        }
    }

    union() {
        for(i = [1:bearingCount]){
            rotate([0,0,(360/bearingCount)*i]) {
                translate([bearingDistance,0,0]) {
                    cylinder(bearingHeight, d = bearingDiameter, true);
                }
            }
        }
        cylinder(bearingHeight, d = bearingDiameter, true);
    }
}
```


[User]
I'm looking to create a customizable game token, something like a coin, but with a cut-out number or symbol in the middle. I want to be able to adjust various aspects of the coin, like its diameter, height, and the shape of its edges. I'd like to be able to specify the text cut out, its size and font, and choose if the edges are beveled on the top, the bottom, or both sides. Finally, I need the shape to be able to have different numbers of points (like a triangle, pentagon, etc. instead of a circle). I would like to use OpenSCAD for this, and I would need the code to be fully parametric.

Specifically, I want parameters for:

*   **outerDiameter**: The overall diameter of the coin.
*   **innerDiameter**: The diameter at the top of the bevel.
*   **bevelBothSides**: A yes/no toggle to control if both sides of the coin are beveled or only the top.
*   **shapeRotation**: Allows for the rotation of the whole shape to align it with the text.
*   **points**: The number of points on the coin shape - this will make the coin polygon, not a circle.
*   **totalHeight**: The total height of the coin.
*   **coinValue**: The text to cut out of the coin.
*   **textSize**: The size of the cut-out text.
*  **textFont**: The font for the cut-out text.

The cut-out text should go all the way through the coin. The token's overall design will consist of a beveled polygon (controlled by `points`) that is cut by the specified text. Can you generate an OpenSCAD file (or files) to achieve this? I don't need you to use any specific libraries.

[Assistant]
coin.scad:
```
// Created by Grant Emsley <grant@emsley.ca>

/* [Shape] */
// total diameter of the coin
outerDiameter = 20; // [1:50]
// diameter at the top of the bevel
innerDiameter = 18; // [1:50]
// bevel only top or both sides?
bevelBothSides = "yes"; // [yes,no]
// adjust to line up text the way you want
shapeRotation = 90; // [0:360]
// number of points the coin shape has
points = 5; // [3:20]
// total height of the coin
totalHeight = 1; // [0.5:0.1:20]

/* [Text] */

// Text to cut out of the coin
coinValue = "1";
textSize = 9;
textFont = "Open Sans";

module taperedShape() {
	if(bevelBothSides == "yes") {
		rotate(shapeRotation) translate([0,0,totalHeight/2]) cylinder(h = totalHeight/2, d1 = outerDiameter, d2 = innerDiameter, $fn = points);
		rotate(shapeRotation) cylinder(h = totalHeight/2, d1 = innerDiameter, d2 = outerDiameter, $fn = points);
	} else {
		cylinder(h = totalHeight, d1 = outerDiameter, d2 = innerDiameter, $fn = points);
	}
}

module createText() {
	translate([0,0,-1]) linear_extrude(height = totalHeight + 2) text(str(coinValue), size=textSize, halign="center", valign="center", font=textFont);
}

difference() {
	taperedShape();
	createText();
}

```


[User]
I'd like to create a custom text generator using OpenSCAD that can utilize any font from Google Fonts, similar to the example I found on Thingiverse (thing:2157178). The core idea is to have a module that extrudes text with adjustable font, size, and spacing, but I also want to include some features for previewing the design.

Here's a breakdown of what I need:

1.  **Text Input:** A customizable text field where the user can enter the desired text.
2.  **Font Selection:**
    *   A dropdown menu with some predefined popular Google Fonts (like "Gloria Hallelujah," "Pacifico," "Courgette," etc.).
    *   A "Manual" option that allows users to enter any Google Font name directly, including font styles like "Amatic SC:bold".
3.  **Font Size:** A parameter to control the font size (height above the baseline, in mm), with a default of 30.
4.  **Character Spacing:**  A parameter to adjust character spacing with a default of 0.7, where 1.0 is normal spacing.
5.  **Text Direction:** Option for 'left-to-right' (ltr) or 'top-to-bottom' (ttb) writing direction.
6.  **Extrusion Thickness:** A parameter to adjust the thickness of the extruded text (in mm), default to 35.
7.  **Color:** A parameter to select the color of the text for display purposes.
8.  **Build Plate Preview:**
    *   Option to display a build plate for reference.
    *   Ability to view the build plate in "Measure" mode (for sizing), or in a solid color.
    *   Manual control over the x and y dimensions of the build plate.
9.  **Build Plate Dimensions:**
    * Allow setting the build plate dimensions via x and y parameters, defaulting to x = 210 and y = 150

The OpenSCAD code should also have a "doEcho" option for debugging to be able to output to the console which values are being used, or any other debug messages.

I’d like the OpenSCAD code to use the `utils/build_plate.scad` library for the build plate module, which will also need to be created. Please do not include any code within the `utils/build_plate.scad` file in your response.

[Assistant]
Text_with_all_Google_Fonts.scad:
```
// Text Font Test by Robert Halter
// Just to Test the OpenSCAD text function in Thingiverse Customizer in Combination with Google Fonts.
//
// http://www.thingiverse.com/thing:2157178
//
// Licensed: Creative Commons - Attribution - Non-Commercial - Share Alike (by-nc-sa)
// see: http://en.wikipedia.org/wiki/Creative_Commons
//
// License for Font Courgette, Amatic SC (on Google Fonts)
// Open Font License : http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL_web
//
// author: Robert Halter - CH - 06.03.2017
//
// with:   OpenSCAD Version 2015.03
//
// History
// 21.03.2017
// - font_name options extended Font work with fontname:style, i.e. Corben:bold
// - function isBold() added.
// - function getFontNameWithStyle() added.
// - doEcho added, for debugging.
// 09.03.2017
// - text_color
// - build_plate_color
// - parameter descriptions with default values
// - Modules
// - show_build_plate yes, no
// 06.03.2017
// - optimizing some parameter values / defaults
// - build plate translate
// - font_name_manual
// - parameter writing_direction
// - build plate Robox x210 y150
// - preview
// - some predefined Fonts
// - Initial Version

use <utils/build_plate.scad>

/* [Customize] */

// your customizable text to generate.
text_to_generate = "Courgette 123";

// font size - dezimal - height above the baseline. - Default is '30'.
font_size = 30;

// font name - use predefined Names or 'Manual' - default is 'Courgette'
font_name = "Courgette"; // [Gloria Hallelujah, Pacifico, Courgette, Permanent Marker, Domine:Domine bold, Ultra, Bevan, Corben:Corben bold, Coustard:Coustard bold, Manual]

// use it if font name is 'Manual' - manual entered g o o g l e font name - can also include a style parameter - example is 'Amatic SC:bold'
font_name_manual = "Amatic SC:bold";

// factor to increase/decrease the character spacing. '1.0' is normal spacing. - default is '0.7'
character_spacing = 0.7; // [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

// direction of the text flow. - default is 'left-to-right'
writing_direction = "ltr"; // [ltr:left-to-right, ttb:top-to-bottom]

// thickness of letters [mm] - z direction - default is '35'
thickness_of_letters = 35; // [35, 30, 25, 20, 15, 10, 5, 1]

/* [View] */

// view text in selected color - default is 'Red'
text_color = "Red"; // [White, Silver, Gray, Black, Red, Maroon, Yellow, Olive, Lime, Green, Aqua, Teal, Blue, Navy, Fuchsia, Purple]

// to measure of text extent switch to 'yes' - default is 'no'
show_build_plate = "no"; // [yes, no]

// view build plate in Measure-Mode or in selected color - default is 'Measure'
build_plate_color = "Measure"; // [Measure, White, Silver, Gray, Black, Red, Maroon, Yellow, Olive, Lime, Green, Aqua, Teal, Blue, Navy, Fuchsia, Purple]

// the build plate x dimension - default is '210'
build_plate_manual_x = 210; // [100, 150, 200, 210, 246, 400]

// the build plate y dimension - default is '150'
build_plate_manual_y = 150; // [100, 150, 152, 200, 400]

/* [Hidden] */

// Show some Log Information for Debugging to Console [boolean]
doEcho = false; // [false,true]
if(doEcho) echo("WARNING: doEcho ist true!");

if(doEcho) echo(str("font_name: ", font_name));

// Font Names with Bold Style
function isBold() =
    "Domine" == font_name ?
        true
	: "Corben" == font_name ?
        true
	: "Coustard" == font_name ?
        true
	: false;

if(doEcho) echo(str("isBold(): ", isBold()));

function getFontNameWithStyle() =
    isBold() ?
	    str(font_name, ":bold")
	: font_name;
if(doEcho) echo(str("getFontNameWithStyle(): ", getFontNameWithStyle()));

xTranslate = build_plate_manual_x / 2;
yTranslate = build_plate_manual_y / 2;

// for display only, doesn't contribute to final object
build_plate_selector = 3; // [0:Replicator 2, 1: Replicator, 2:Thingomatic, 3:Manual]

function is_vertical() = writing_direction == "ttb" ? true : false;
function is_manual_entered_font_name() = font_name == "Manual" ? true : false;

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

// Model - Start

if ("yes" == show_build_plate) {
    if ("Measure" == build_plate_color) {
        myBuildPlate();
    } else {
        color(build_plate_color) {
            myBuildPlate();
        }
    }
}
color(text_color) {
    myText();
}

// Model - End

// Modules - Start

module myBuildPlate() {
    translate([xTranslate,yTranslate,0]) {
        build_plate(build_plate_selector, build_plate_manual_x, build_plate_manual_y);
    }
}

module myText() {
    linear_extrude(height = thickness_of_letters) {
        text(text_to_generate,
            size = font_size,
            font = is_manual_entered_font_name() ? font_name_manual : getFontNameWithStyle(),
            halign = is_vertical() ? "center" : "left",
            valign = "baseline",
            spacing = character_spacing,
            direction = writing_direction,
            language = "en",
            script = "latin",
            $fn = 50);
    }
}

// Modules - End

```


[User]
I'd like to create a customizable tray for holding a phone or other small object. I've been playing around with some existing code, but I'd like to start fresh and have something a bit more robustly designed and easier to customize.

Here are the features I need:

*   **Basic Shape:** The tray should be a rectangular box with rounded corners.
*   **Inner Dimensions:** The inner dimensions of the tray (length, width, and height) should be customizable using parameters. I'll call these `phone_length`, `phone_width`, and `phone_thickness`.
*   **Wall Thickness:** The thickness of the walls and base should also be adjustable via parameters named `wall_thickness` and `base_thickness` respectively.
*   **Corner Radius:** The radius of the rounded corners should be adjustable with a parameter called `corner_radius`.
*   **Fit Adjustment:** I need a parameter, `gap`, to add a bit of extra space for a looser fit. This should be added to the inner dimensions.
*   **Cutaways:** I'd like to include optional cutaways on the sides, top/bottom, and base.
    *   **Cutaway Control:** I need boolean parameters `side_cutaway`, `top_bottom_cutaway`, and `base_cutaway` to enable or disable each cutaway type (0 for no cutaway, 1 for cutaway).
    *   **Cutaway Percentage:** I also want to control the percentage of each cutaway (how much of each side/base is removed). These should be parameters named `side_percent`, `top_bottom_percent`, and `base_cutaway_percent`, with values between 0 and 1 (e.g. 0.75 for 75% removed).
    *   **Lip Height:** I need parameters called `side_lip` and `top_bottom_lip`, which are the heights of the remaining lip above the base on the respective cutaways.

I'd prefer to avoid any libraries unless you think they're crucial for a clean design. I'm happy to specify the output file naming conventions: I'd like just one .scad file called "parametric_phone_tray.scad".

Could you generate that for me? I think that covers everything. Thanks!

[Assistant]
parametric_phone_tray_customizer.scad:
```
// Length of phone or object
phone_length = 138;
// Width of phone or object
phone_width = 68;
// Thickness of phone. How high to make the walls above top of base.
phone_thickness = 10;
// Radius of corners
corner_radius = 8;
// Extra room/play/fudge-factor. Use this to adjust fit.
gap = 0;
// thickness of walls and base
wall_thickness = 1;
base_thickness = 1;

// 0 solid side walls, 1 for cutawayside cutaway side walls
side_cutaway = 1;
// 0 solid top/bottom walls, 1 for cutaway top/bottom walls
top_bottom_cutaway = 1; // 0=no top/bottom cutaway
// 0 for solid base, 1 for cutaway in base
base_cutaway = 1;       // 0=no base cutaway

// Height of Lip between top of base and lowest point of cutaway
side_lip = 1;
// Percentage of side's length to cut away (0-1)
side_percent = 0.75;
// Height of Lip between top of base and lowest point of cutaway
top_bottom_lip = 1;
// Percentage of length of top/bottom wall to cut away (0-1)
top_bottom_percent = 0.8;
// Percentage of base to cut away (0-1)
base_cutaway_percent = 0.75;

module cube_rect_rounded(sx,sy,sz,rad,ct)
{
    // remove radius so shape keeps outer dimensions
    sxp =sx-2*rad;
    syp =sy-2*rad;

    hull()
    {
        for(x=[-1,+1])
        for(y=[-1,+1])
            translate([x*sxp/2,y*syp/2,0])
                cylinder(r=rad,h=sz, center=ct);
     }

 }

module tray()
{
$fn=100;

// add margin
phone_length = phone_length + gap;
phone_width  = phone_width + gap;

// calculate outer dimensions based on inner size and wall thickness
tray_o_ln=phone_length + 2 * wall_thickness;
tray_o_wd=phone_width + 2 * wall_thickness;
tray_o_ht=phone_thickness + base_thickness;
tray_o_rad=corner_radius + wall_thickness;

    difference()
    {
        // stuff to add
        union()
        {
            difference ()
            {
                cube_rect_rounded(tray_o_ln, tray_o_wd, tray_o_ht, tray_o_rad,false);
                translate([0,0,base_thickness])cube_rect_rounded(phone_length, phone_width,phone_thickness+1,corner_radius,false);
            }
        }

        // stuff to subtract
        union()
        {
            if (top_bottom_cutaway != 0)
            {
                translate([-tray_o_ln,0,tray_o_ht])rotate([0,90,0])scale([2*(phone_thickness-top_bottom_lip),tray_o_wd*top_bottom_percent,1])cylinder(2*tray_o_ln,d=1,true);
            }

            if (side_cutaway != 0)
            {
              translate([0,tray_o_wd,tray_o_ht])rotate([90,0,0])scale([tray_o_ln*side_percent,2*(phone_thickness-side_lip),1])cylinder(2*tray_o_wd,d=1,true);
            }
            if (base_cutaway != 0)
            {
                cube([phone_length*base_cutaway_percent,phone_width*base_cutaway_percent,10*tray_o_ht],true);
            }

        }
     }

}

tray();
```


[User]
I'd like to design a parametric filament spool in OpenSCAD, and I'm hoping you can help me create the SCAD file(s).

The spool needs to have a few key customizable parameters:

*   **Spindle Inner Diameter (spindle\_id):** This is the diameter of the center hole that goes on the printer's spool holder. I'd like to be able to set this from 10mm to 100mm.
*   **Spindle Outer Diameter (spindle\_od):**  This is the diameter of the cylinder that the filament will actually wrap around. I need to be able to set this from 40mm to 200mm.
*   **Overall Spool Outer Diameter (spool\_od):** This is the outer diameter of the entire spool. The range should be from 100mm to 250mm.
*   **Overall Spool Width (spool\_width):** This sets the total width of the spool. I'll need to adjust this between 20mm and 75mm.
*   **Wall Style (wall\_style):** I want to choose between "solid" and "spoked" walls for the sides of the spool.
*   **Spoke Count (spoke\_count):** If "spoked" walls are selected, I need to control the number of spokes. This should range from 4 to 12, incrementing in steps of 2.
*   **Spoke Width (spoke\_width):**  If spoked, I need to control the width of each spoke, ranging from 5mm to 25mm.
*  **Label Text (label\_text):** I'd like to add a short text label to the side of the spool.
*   **Label Orientation (label\_orient):**  The label should orient either along the side of the spool, or along a spoke, if they exist. Options should be "side" or "spoke".
*   **Spool Wall Thickness (spool\_wall):**  This is the thickness of the walls of the spool, ranging from 2mm to 10mm.
*   **Filament Hole Diameter (filament\_diam):** I need to have a small hole to tuck the filament into. It should default to 1.75mm, but should be adjustable to 3.00mm.

The design will be a cylindrical spool with optional spokes. It should include small holes to tuck the filament into both on the inner wall of the spool (on the spindle) and on the outer wall. The spindle part should include small cutouts/indentations that make it easier to snap the two sides together. The label should be a simple extruded text on one of the sides.

I'd prefer the code to be well-structured, commented and using named parameters. Please make use of the `Belfry` library for convenience functions, but please don't suggest including the `Belfry` library itself; I have it already.

[Assistant]
Spool.scad:
```
//////////////////////////////////////////////////
// Customizable Filament Spool
//////////////////////////////////////////////////

/* [Size] */

// Diameter of the center spindle hole in mm.
spindle_id = 52; // [10:100]

// Diam of spindle that filament wraps around in mm.
spindle_od = 145; // [40:200]

// Overall outside diameter of spool in mm.
spool_od = 180; // [100:250]

// Overall width of spool in mm.
spool_width = 31; // [20:75]

/* [Spokes] */

// Style of spool walls.
wall_style = "spoked"; // [solid,spoked]

// Number of spokes, if enabled.
spoke_count = 6; // [4:2:12]

// Spoke width, if enabled, in mm.
spoke_width = 12; // [5:25]

/* [Label] */

// Label text to print on side of spool. (Keep it to just a few letters.)
label_text = "";

// Orient label along side or spoke.
label_orient = "side"; // [side,spoke]

/* [Misc] */

// Thickness of spool walls in mm.
spool_wall = 3; // [2:10]

// Diameter of filament holes in mm.
filament_diam = 1.75; // [1.75,3.00]

//////////////////////////////////////////////////
// Belfry SCAD Library

module right(x) {translate([+x,0,0]) children();}
module left(x)  {translate([-x,0,0]) children();}
module back(y)  {translate([0,+y,0]) children();}
module fwd(y)   {translate([0,-y,0]) children();}
module up(z)    {translate([0,0,+z]) children();}
module down(z)  {translate([0,0,-z]) children();}

module xrot(x) {rotate([x,0,0]) children();}
module yrot(y) {rotate([0,y,0]) children();}
module zrot(z) {rotate([0,0,z]) children();}

module xscale(x) {scale([x,1,1]) children();}
module yscale(y) {scale([1,y,1]) children();}
module zscale(z) {scale([1,1,z]) children();}

module xflip() {mirror([1,0,0]) children();}
module yflip() {mirror([0,1,0]) children();}
module zflip() {mirror([0,0,1]) children();}

module mirror_copy(v) {children(); mirror(v) children();}
module place_copies(a=[[0,0,0]]) {for (p = a) translate(p) children();}
module xrot_copies(rots=[0]) {for (r = rots) rotate([r, 0, 0]) children();}
module yrot_copies(rots=[0]) {for (r = rots) rotate([0, r, 0]) children();}
module zrot_copies(rots=[0]) {for (r = rots) rotate([0, 0, r]) children();}

module xspread(spacing=1,n=2) {off=spacing*(n-1)/2.0; for (x =[-off:spacing:off]) translate([x, 0, 0]) children();}
module yspread(spacing=1,n=2) {off=spacing*(n-1)/2.0; for (y =[-off:spacing:off]) translate([0, y, 0]) children();}
module zspread(spacing=1,n=2) {off=spacing*(n-1)/2.0; for (z =[-off:spacing:off]) translate([0, 0, z]) children();}
module spread(p1,p2,n=2) {d=(p2-p1)/(n-1); for (i=[0:n-1]) translate(p1+d*i) children();}

module xring(n=2,r=0) {d=360.0/n; for (a=[d:d:360]) rotate([a%360,0,0]) translate([0,r,0]) children();}
module yring(n=2,r=0) {d=360.0/n; for (a=[d:d:360]) rotate([0,a%360,0]) translate([r,0,0]) children();}
module zring(n=2,r=0) {d=360.0/n; for (a=[d:d:360]) rotate([0,0,a%360]) translate([r,0,0]) children();}

module grid_of(xa=[0],ya=[0],za=[0]) {for (x=xa,y=ya,z=za) translate([x,y,z]) children();}

module chain_hull() {for (i=[0:$children-2]) hull() {children(i); children(i+1);}}

module top_half   (s=100) difference() {children();  down(s/2) cube(s, center=true);}
module bottom_half(s=100) difference() {children();    up(s/2) cube(s, center=true);}
module left_half  (s=100) difference() {children(); right(s/2) cube(s, center=true);}
module right_half (s=100) difference() {children();  left(s/2) cube(s, center=true);}
module front_half (s=100) difference() {children();  back(s/2) cube(s, center=true);}
module back_half  (s=100) difference() {children();   fwd(s/2) cube(s, center=true);}

pi = 3.141592653589793236 + 0;

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

$fa = 2;
$fs = 2;

eps = 0.01 + 0;
spindle_slices = (spoke_count/2) * ceil(spindle_od*pi/spoke_count/50);
snap_indent = spool_wall/3;

module spindle_segments(excess=0) {
	render(4)
	zring(n=spindle_slices) {
		difference() {
			cylinder(h=spool_width, d=spindle_od+excess, center=true);
			cylinder(h=spool_width+2*eps, d=spindle_od-2*spool_wall, center=true);
			up(spool_width/2-spool_wall/4) cylinder(h=spool_wall/2+eps, d2=spindle_od-2*spool_wall+2*snap_indent, d1=spindle_od-2*spool_wall, center=true);
			up(spool_width/2-spool_wall*3/4) cylinder(h=spool_wall/2+eps, d1=spindle_od-2*spool_wall+2*snap_indent, d2=spindle_od-2*spool_wall, center=true);
			xrot(-5) fwd(spindle_od) cube(size=[spindle_od*2, spindle_od*2, spool_width*2], center=true);
			zrot(360/spindle_slices/2) xrot(5) back(spindle_od) cube(size=[spindle_od*2, spindle_od*2, spool_width*2], center=true);
		}
	}
}

module spool() {
	difference() {
		union() {
			up(spool_wall/2) {
				if (wall_style == "spoked") {
					// Spoked walls

					// Outer spool wall ring
					difference() {
						cylinder(h=spool_wall, d=spool_od, center=true);
						cylinder(h=spool_wall+eps*2, d=spool_od-spoke_width*2, center=true);
					}

					// Mid spool wall ring
					difference() {
						cylinder(h=spool_wall, d=spindle_od+spoke_width, center=true);
						cylinder(h=spool_wall+eps*2, d=spindle_od-spool_wall*2-spoke_width, center=true);
					}

					// Inner spool wall ring
					difference() {
						cylinder(h=spool_wall, d=spindle_id+20, center=true);
						cylinder(h=spool_wall+eps*2, d=spindle_id, center=true);
					}

					// Spool wall spokes
					for (ang =[0:(360/spoke_count):359.99]) {
						zrot(ang) {
							right(spool_od/4) {
								cube(size=[spool_od/2-spoke_width/2, spoke_width, spool_wall], center=true);
							}
						}
					}

				} else {
					// Solid walls.

					// Outer spool wall ring
					cylinder(h=spool_wall, d=spool_od, center=true);
				}
			}

			// Spindle wall that filament wraps around
			up(spool_width/2) {
				spindle_segments();
			}
		}

		// Clear spindle hole.
		up(spool_wall/2) {
			cylinder(h=spool_wall+2*eps, d=spindle_id, center=true);
		}

		// Clear out snap holes.
		up(spool_width/2-eps) {
			zflip() {
				zrot(360/(spindle_slices*2)) {
					slop_ang = 0.25 * 360.0 / (spindle_od * pi);
					zrot(slop_ang/2) spindle_segments(excess=snap_indent);
					zrot(-slop_ang/2) spindle_segments(excess=snap_indent);
				}
			}
		}

		// Label text.
		if (label_text != "") {
			linear_extrude(height=1, center=true, convexity=10) {
				xflip() {
					right(spool_od/2-5) {
						if (label_orient == "side") {
							zrot(-90) text(text=label_text, size=spoke_width*0.67, valign="top", halign="center");
						} else {
							right(3) zrot(180) text(text=label_text, size=spoke_width*0.4, valign="bottom");
						}
					}
				}
			}
		}

		// Inner filament holding holes.
		up(spool_wall+filament_diam*1.6/2) {
			zrot(360/spindle_slices/4) {
				right(spindle_od/2) {
					yrot(90) cylinder(h=spindle_od/2, d=filament_diam*1.5, center=true, $fn=12);
				}
			}
		}

		// Outer filament holding holes.
		zrot(45) {
			zring(n=4) {
				right(spool_od/2-5) {
					back(5) xrot(-45) cylinder(h=spool_wall*5, d=filament_diam*1.5, center=true, $fn=8);
					fwd(5) xrot(45) cylinder(h=spool_wall*5, d=filament_diam*1.5, center=true, $fn=8);
				}
			}
		}
	}
}

spool();

// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

```


[User]
I'd like a customizable OpenSCAD design for bottle nameplates, similar to the ones I've been making, but more flexible. I use these with rope to attach labels to IKEA bottles.

The basic structure is a rectangular plate with rounded corners, and I'd like it to have the option for 1, 2 or 3 lines of text. Each line of text should be centered on its respective row.  The text should be extruded and pass through the plate. I need to be able to control the text content of each line. The plate itself needs to have a hole for attaching the rope to the bottle - centered at the top of the plate.

I need the following parameters:
*   **line1**: Text for the first line.
*   **line2**: Text for the second line. This should be optional and if left empty, should not be rendered.
*   **line3**: Text for the third line, also optional.
*   **plateHeight**: The thickness of the plate, in mm.
*  **border**: The space between the edge of the text and the edge of the plate (before rounding), in mm.
*   **fontThresh**: A value used to calculate text width, as described in the existing code. Should have the same behaviour as it does there.
*   **fontSize**: The size of the text.
*   **distLines**: The distance between each line of text, vertically.
*   **rounding**: The radius of the rounded corners.
*   **ropeHoleRadius**: The radius of the hole for the rope.

I'm looking for a clean, parametric design where the overall plate size adjusts automatically depending on the lengths of the strings entered and the provided `fontSize`, `fontThresh` and `border` parameters. I think you will probably use the `text` and `linear_extrude` functions and then `difference()` to cut the text out of the plate. Also need a `minkowski()` for those round edges! I plan to use the Sirin Stencil font for the text.

I'm not using any external libraries, so just base OpenSCAD is enough. Thanks!

[Assistant]
Ikea_Bottle_Name_Plate.scad:
```
//Text in first line
line1=" kg";
//Text in second line
line2=" L";
//Text in third line
line3=" €";
//Thickness(Height in mm) of the plate
plateHeight=0.75*2.5;
//Size of the border in mm
border=2;
//fontSize/fontThresh=LetterWidth (this is an approximated value. Depending on the font and size of individual letters in the used words you might have to raise or lower the value. E.g. When having broader characters like M,W or m or many slim characters like i or !
fontThresh=0.625;
//Font Size
fontSize=11;
//Distance in mm between the line
distLines=1.6;
//Radius in mm of the rounded corners
rounding=3;

difference() {
    $fn=32;

    difference() {
        union(){
            minkowski() {
                union() {
                    cube([max(len(line1),len(line2),len(line3))*(fontSize*fontThresh)+border,(fontSize*distLines),plateHeight], center=true);
                     if(line2) {
                         translate([0,-(fontSize*distLines),0]) cube([max(len(line1),len(line2),len(line3))*(fontSize*fontThresh)+border,(fontSize*distLines),plateHeight], center=true);
                     }
                     if(line3) {
                         if(!line2) {
                         translate([0,-(fontSize*distLines),0]) cube([max(len(line1),len(line2),len(line3))*(fontSize*fontThresh)+border,(fontSize*distLines),plateHeight], center=true);
                         }
                         translate([0,-(fontSize*distLines)*2,0]) cube([max(len(line1),len(line2),len(line3))*(fontSize*fontThresh)+border,(fontSize*distLines),plateHeight], center=true);
                     }
                }
                cylinder(r=rounding,h=plateHeight); //Border Roundings
            }
            translate([(max(len(line1),len(line2),len(line3))*(fontSize*fontThresh)+border)/2,(fontSize*distLines)/2,-plateHeight/2]) cylinder(r=5,h=2*plateHeight);
        }
        translate([(max(len(line1),len(line2),len(line3))*(fontSize*fontThresh)+border)/2,(fontSize*distLines)/2,-plateHeight*4]) cylinder(r=2.5,h=plateHeight*2*4);
    }

    union() {
            linear_extrude(height = plateHeight*4, center=true) text(line1, font = "Sirin Stencil", "center", size = fontSize, valign="center", halign="center");
        if(line2) {
            translate([0,-(fontSize*distLines),0]) linear_extrude(height = plateHeight*4, center=true) text(line2, font = "Sirin Stencil", "center", size = fontSize, valign="center", halign="center");
        }
        if(line3) {
            translate([0,-(fontSize*distLines)*2,0]) linear_extrude(height = plateHeight*4, center=true) text(line3, font = "Sirin Stencil", "center", size = fontSize, valign="center", halign="center");
        }
    }
}
```


[User]
I'm looking to create a pen holder clip that I can attach to my tablet case. The idea is to have a clip that holds a pen against the side of the case. I want to be able to customize it for different pen diameters and clip thicknesses.

Specifically, I need the design to:

1.  Have a cylindrical pen holder part with a customizable diameter.
2.  Include a clip that extends from the pen holder. The clip should have two arms, separated by a customizable distance.
3.  Have customizable wall thicknesses for both the pen holder and the clip.
4.  Include an option to add text on the clip, with customizable text, text size, and font (using a font from a standard font library).
5. Be able to set the length of the clip.
6. I would also like the pen holder part to have a cut out where it mounts to the tablet that reduces its contact area (and material usage) with the case. I'd like this cut out to extend back from the edge and be angled on both sides. I'd also like to have small support cylinders added on either side of this cut out.
7.  I would prefer a parametric design in OpenSCAD, so I can easily adjust all these variables. I'm not familiar with the libraries, so it would be great if it's just pure OpenSCAD.

I think those are the main points - let me know if you have any questions.

[Assistant]
clipPen.scad:
```
// Diameter of the pen
diameter=10;

// Width of the piece
width = 10;

// Lenght of the clip
length=30;

//Separation of the clip arms
sep = 4;

// Wall thickness of the pen holder
wt = 1;

// Wall thickness of the clip
wt2 = 0.8;

// Text
t = "NAME";

// Text Size
textSize = 6;

// Font type (you can use any Google Font but consider any stencil font for better results)
font = "Stencil";
/* [Hidden] */

$fn=60;
ang=40;

height=width;

penHolder();
difference() {
    clip();
    textOnClip();
}

module textOnClip() {
    translate ([diameter/2+length/2, -sep/2+1, height/2])
    rotate([90, 0, 0])
    linear_extrude(height=wt2+2)
    text(t, font=font, size=textSize, valign="center", halign="center", style="bold");
}

module penHolder() {
    difference() {
        cylinder (d=diameter+wt*2, h=height);
        translate([0, 0, -1]) cylinder (d=diameter, h=height+2);
        translate([0, 0, -1]) linear_extrude(height=height+2)
        polygon(points=[[0,0], [-cos(ang/2)*(diameter), sin(ang/2)*(diameter+1)], [-cos(ang/2)*(diameter), -sin(ang/2)*(diameter+1)]]);
    }
    translate([-cos(ang/2)*(diameter/2+wt/2), sin(ang/2)*(diameter/2+wt/2), 0]) cylinder(d=wt, h=height);
translate([-cos(ang/2)*(diameter/2+wt/2), -sin(ang/2)*(diameter/2+wt/2), 0]) cylinder(d=wt, h=height);
}

module clip() {

    r1 = wt2 * 0.5;

    difference() {
        translate([diameter/2, -sep/2-wt2, 0]) cube([length, sep+wt2*2, height]);
        translate([diameter/2+wt+sep/2, 0, -1])cylinder(d=sep, h=height+2);
        translate([diameter/2+wt+sep/2, -sep/2, -1]) cube([length, sep, height+2]);
    }
    translate([diameter/2+length-r1, sep/2, 0]) cylinder(r=r1, h=height);
    translate([diameter/2+length-r1, -sep/2, 0]) cylinder(r=r1, h=height);

}
```


[User]
I'd like to create a customizable BFF necklace using OpenSCAD. It should be designed as two interlocking halves, each with a space for a name and a hole for a jump ring or chain.

Here are the specifics I'm thinking of:

*   **Shape:** The overall shape should be a sort of rounded, almost heart-like shape, split vertically down the middle. Instead of a perfectly symmetrical heart, I was thinking of having the halves interlock with a sort of curved cut-out section on one side. I've provided a rough outline of the shape as a list of 2D coordinates in the attached .scad file.

*   **Dimensions:** I want to control the overall width, height, and thickness of the necklace pieces. Let's call these `width`, `height`, and `thickness`.

*   **Text:** Each half should have space for a name. So, we'll have two text variables, `name1` and `name2`. I also need to control the text size (`textSize`), the font (`font`), and the text's angle (`textAng`). There should also be horizontal and vertical translation variables (`horzTrans`, `vertTrans`) to allow text placement fine-tuning.

*   **Drill Holes:** Each half needs a hole for attaching a chain or jump ring. The diameter of the hole should be adjustable (`drillDiam`), the angle of the hole's position (`angDrill`) and the position of the hole along the height, using a separation ratio (`sepDrill`).

*   **Modularity:** The design should be modular, with separate modules for the left and right halves, the text, the cut-out shape, and the drill hole.

I want all the dimensions and text settings, like font, size, etc. to be parameters I can easily adjust. Could you create an OpenSCAD file for this design that includes all of these parameters? I don't need any dependencies besides the core OpenSCAD libraries.

[Assistant]
bffNecklace.scad:
```
/* [Dimensions] */
// Width
width = 25;

// Height
height = 30;

// Plate thickness
thickness = 2;

/* [Texts configuration] */
name1 = "Name1";
name2 = "Name2";

// Text Size
textSize = 4; // [0:20]

// Text font (choose from Google Fonts)
font = "Arial";

// Text angle
textAng = 60; // [0:180]

// Horizontal translation
horzTrans = -1; // [-20:20]

// Vertical translation
vertTrans = 1; // [-20:20]

/* [Drills configuration] */
// Drill diameter
drillDiam = 4;

// Drill position angle
angDrill = 0; // [0:180]

// Drill separation ratio
sepDrill = 9; // [0:10]

/* [Hidden] */
$fn=60;
cut=[[-width/2,-height/2], [-width/6,-height/3], [-width/6.5,-height/5],[-width/7,-height/12],[0, 0],[width/7,height/12],[width/6.5,height/5], [width/2,height/2], [width/2+1, height/2+1], [width/2+1, -height/2-1], [-width/2-1, -height/2-1]];

a = atan((width/6.5)/(height/5));
b = textAng-a;
ra = height/5 / sin(a);

left_part();
translate([1, -1, 0]) right_part();

module left_part() {
    difference() {
        scale([width, height, thickness]) cylinder(d=1, h=1);
        translate([0, 0, -0.1]) cut();
        rotate([0, 0, angDrill]) drill();
    }
    textName(name1);

}

module textName(name) {
    translate([0, 0, 2]) linear_extrude(height=2) rotate([0, 0, textAng]) translate([horzTrans, sin(b)*ra+vertTrans, 0]) text(name, size=textSize, valign="center", halign="center", font = font);
}

module right_part() {
    difference(){
        intersection() {
        scale([width, height, thickness]) cylinder(d=1, h=1);
        translate([0, 0, -0.1]) cut();
    }
    rotate([0, 0, angDrill+180]) drill();
    }
    rotate([0, 0, 180]) textName(name2);

}

module cut() {
    linear_extrude(height=thickness+1) polygon(points=cut);
}

module drill() {
    translate([0, height/2*sepDrill/10-drillDiam/2, -0.1]) cylinder(d=drillDiam, h=thickness+1);
}

```


[User]
Hey, I'm trying to design a set of interlocking, twisting prism containers that slide into each other. I've seen some examples online (links in the prompt if you need them) and I'd like to create my own, parametrically, using OpenSCAD.

Here's the basic idea: I want two prisms, one slightly smaller than the other, so they can slide together. They should twist as they slide, creating a kind of helical or spiral movement. Think of a screw and a nut, but instead of a circular cross-section, I want the cross-section to be a polygon. So they'll be like two twisty polygonal containers.

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

*   **Polygon Sides (`poly`):** I need to be able to choose how many sides the polygon has. For example, 3 for a triangle, 4 for a square, etc.
*   **Height (`heightt`):** The overall height of the prisms.
*   **Rotation (`rotation`):** The total degrees of rotation as the prism goes from top to bottom.
*   **Thickness (`thickness`):** The wall thickness of the prism.
*   **Diameter (`diameter`):** The overall diameter of the outer prism from point to point (or corner to corner).
*   **Base Thickness (`base`):** How thick the bottom of the prisms is. This one I will probably leave at a small default, not so important to adjust it dynamically.
*   **Space (`space`):** This parameter is crucial; it determines the gap between the inner and outer prisms. This controls how easily the pieces slide together, and might need tweaking for different polygon counts and print tolerances.

I would like the outer container to be a hollow prism, and the inner container to be a hollow prism that can fit inside the outer container. I need to be able to generate both from the parameters.

I'd like an OpenSCAD file (or multiple files if that makes more sense) that can do all this. I don't have any specific requirements on which libraries to use, but if you can recommend a standard approach it would be useful for me.

[Assistant]
Slidomatic_polygonal_prism.scad:
```
//© 2017 Nevit Dilmen CC4.0 Attribution, Share-alike

$fn=300;
heightt=50; //mm
rotation=180; //degree
thickness=0.5; //mm
diameter=30; //Polygon diameter mm
base=2; // mm
space=2.6; //space between two shapes mm larger for low poly 2.6 for poly=3
poly =3; // Number of polygon sides

// External shape
difference () {
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
circle(d=diameter,center=true,$fn=poly);

translate([0,0,base])
rotate([0,0,-1*rotation/heightt*base])
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
circle(d=diameter-thickness,center=true,$fn=poly);
}

// Internal shape
translate([diameter*2,0,0])
difference () {
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
circle(d=diameter-thickness-space,center=true,$fn=poly);
translate([0,0,base])
rotate([0,0,-1*rotation/heightt*base])
linear_extrude(height = heightt, center = false, convexity = 10, twist = rotation)
circle(d=diameter-thickness*2-space,center=true, $fn=poly);
}
// Base
translate([diameter*2,0,0.5])
minkowski() {
linear_extrude(0.1, center = false)
circle(d=diameter,center=true, $fn=poly);
sphere (0.5);
}

```


[User]
Hey, I'm looking to design a clip for holding an LED strip to the frame of my printer, similar to the one I made but a bit more sophisticated and adjustable. I want it to be fully parametric so I can tweak the dimensions as needed.

Here's the basic idea:

It'll have a looped part at one end that acts as a spring, then two arms that extend out, one of which is angled slightly towards the other.  The idea is that you can slide a strip of material (like an LED strip) into the gap between the arms, and the angled arm will apply pressure to hold it in place. The arms should be flat.

I’m thinking a few key parameters are needed:

*   **`pin_radius`**: The radius of the loop part of the clip, which controls the springiness.
*   **`pin_length`**: The length of the straight sections of the arms.
*   **`pin_width`**: The width of the clip.
*   **`pin_thickness`**: The thickness of the clip arms.
*   **`pin_gap`**: The gap between the arms at the end where the material is inserted. This should be the dimension when the arms are parallel so the LED strip is held firmly.
*   **`tooth_width`** and **`tooth_height`**:  These control the size of little teeth I want along one of the arms to provide better grip.
*   **`teeth_on_spring_side`**: A boolean that defines whether the teeth are on the inside of the spring arm or on the other arm.

I'd like the design to use linear extrude on a 2D shape.  The arms should be parallel at the set `pin_gap` when pressure is applied. I'd like to avoid any sharp corners that are difficult to print where the arms extend from the loop.

Could you generate a parametric OpenSCAD file for this?

[Assistant]
hairpin_clip.scad:
```
fn = 64;
// Radius of the loop part of the pin
pin_radius = 5;
// Pin length
pin_length = 40;
// Pin width
pin_width = 10;
// Thickness of the arms
pin_thickness = 2;
// Gap between the arms. When you put something of this thickness in the pin, the arms should be parallel.
pin_gap = 1.34;

// Tooth width
tooth_width = 2.2;
// Tooth height
tooth_height = 1;
// Which side to put the teeth on. Set to false to put teeth on flat side.
teeth_on_spring_side = true;

linear_extrude(pin_width) pin_section();

module pin_section() {
  difference() {
    translate([0, pin_radius], 0) {
      difference() {
        circle(pin_radius, $fn=fn);
        circle(pin_radius - pin_thickness, $fn=fn);
      }
    }
    translate([0, pin_thickness]) polygon([
      [0, 0],
      [0, pin_gap + tooth_height],
      [(pin_gap+tooth_height)/pin_gap*pin_length, 0]
    ]);
  }

  square([pin_length, pin_thickness]);
  if (!teeth_on_spring_side) {
    translate([0, pin_thickness]) teeth();
  }

  difference() {
    rotate([0, 0, atan(pin_gap/pin_length)*-1])
    translate([0, pin_gap + tooth_height + pin_thickness]) {
      square([pin_length, pin_thickness]);
      if (teeth_on_spring_side) mirror([0, 1, 0]) teeth();
    }

    translate([0, pin_radius], 0) circle(pin_radius - pin_thickness, $fn=fn);
  }
}

module teeth() {
  translate([(pin_length-pin_radius)%tooth_width, 0])
  for (i = [pin_radius : tooth_width : pin_length-tooth_width]) {
    polygon([[i, 0], [i + tooth_width/2, tooth_height], [i + tooth_width, 0]]);
  }
}

```